geom_disk.c revision 133314
1285809Sscottl/*- 2285809Sscottl * Copyright (c) 2002 Poul-Henning Kamp 3285809Sscottl * Copyright (c) 2002 Networks Associates Technology, Inc. 4285809Sscottl * All rights reserved. 5285809Sscottl * 6285809Sscottl * This software was developed for the FreeBSD Project by Poul-Henning Kamp 7285809Sscottl * and NAI Labs, the Security Research Division of Network Associates, Inc. 8285809Sscottl * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the 9285809Sscottl * DARPA CHATS research program. 10285809Sscottl * 11285809Sscottl * Redistribution and use in source and binary forms, with or without 12285809Sscottl * modification, are permitted provided that the following conditions 13285809Sscottl * are met: 14285809Sscottl * 1. Redistributions of source code must retain the above copyright 15285809Sscottl * notice, this list of conditions and the following disclaimer. 16285809Sscottl * 2. Redistributions in binary form must reproduce the above copyright 17285809Sscottl * notice, this list of conditions and the following disclaimer in the 18285809Sscottl * documentation and/or other materials provided with the distribution. 19285809Sscottl * 3. The names of the authors may not be used to endorse or promote 20285809Sscottl * products derived from this software without specific prior written 21285809Sscottl * permission. 22285809Sscottl * 23285809Sscottl * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 24285809Sscottl * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25285809Sscottl * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26285809Sscottl * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 27285809Sscottl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28285809Sscottl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29285809Sscottl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30285809Sscottl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31285809Sscottl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32285809Sscottl * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33285809Sscottl * SUCH DAMAGE. 34285809Sscottl */ 35285809Sscottl 36285809Sscottl#include <sys/cdefs.h> 37285809Sscottl__FBSDID("$FreeBSD: head/sys/geom/geom_disk.c 133314 2004-08-08 06:49:07Z phk $"); 38285809Sscottl 39285809Sscottl#include "opt_geom.h" 40285809Sscottl 41285809Sscottl#include <sys/param.h> 42285809Sscottl#include <sys/systm.h> 43285809Sscottl#include <sys/kernel.h> 44285809Sscottl#include <sys/sysctl.h> 45285809Sscottl#include <sys/bio.h> 46285809Sscottl#include <sys/conf.h> 47285809Sscottl#include <sys/fcntl.h> 48285809Sscottl#include <sys/malloc.h> 49285809Sscottl#include <sys/sysctl.h> 50285809Sscottl#include <sys/devicestat.h> 51285809Sscottl#include <machine/md_var.h> 52285809Sscottl 53285809Sscottl#include <sys/lock.h> 54285809Sscottl#include <sys/mutex.h> 55285809Sscottl#include <geom/geom.h> 56285809Sscottl#include <geom/geom_disk.h> 57285809Sscottl#include <geom/geom_int.h> 58285809Sscottl 59285809Sscottlstatic struct mtx g_disk_done_mtx; 60285809Sscottl 61285809Sscottlstatic g_access_t g_disk_access; 62285809Sscottlstatic g_init_t g_disk_init; 63285809Sscottlstatic g_fini_t g_disk_fini; 64285809Sscottlstatic g_start_t g_disk_start; 65285809Sscottlstatic g_ioctl_t g_disk_ioctl; 66285809Sscottlstatic g_dumpconf_t g_disk_dumpconf; 67285809Sscottl 68285809Sscottlstruct g_class g_disk_class = { 69285809Sscottl .name = "DISK", 70285809Sscottl .init = g_disk_init, 71285809Sscottl .fini = g_disk_fini, 72285809Sscottl .start = g_disk_start, 73285809Sscottl .access = g_disk_access, 74285809Sscottl .ioctl = g_disk_ioctl, 75285809Sscottl .dumpconf = g_disk_dumpconf, 76285809Sscottl}; 77285809Sscottl 78285809Sscottlstatic void 79285809Sscottlg_disk_init(struct g_class *mp __unused) 80285809Sscottl{ 81285809Sscottl 82285809Sscottl mtx_init(&g_disk_done_mtx, "g_disk_done", NULL, MTX_DEF); 83285809Sscottl} 84285809Sscottl 85285809Sscottlstatic void 86285809Sscottlg_disk_fini(struct g_class *mp __unused) 87285809Sscottl{ 88285809Sscottl 89285809Sscottl mtx_destroy(&g_disk_done_mtx); 90285809Sscottl} 91285809Sscottl 92285809SscottlDECLARE_GEOM_CLASS(g_disk_class, g_disk); 93285809Sscottl 94285809Sscottlstatic void __inline 95285809Sscottlg_disk_lock_giant(struct disk *dp) 96285809Sscottl{ 97285809Sscottl if (dp->d_flags & DISKFLAG_NEEDSGIANT) 98285809Sscottl mtx_lock(&Giant); 99285809Sscottl} 100285809Sscottl 101285809Sscottlstatic void __inline 102285809Sscottlg_disk_unlock_giant(struct disk *dp) 103285809Sscottl{ 104285809Sscottl if (dp->d_flags & DISKFLAG_NEEDSGIANT) 105285809Sscottl mtx_unlock(&Giant); 106285809Sscottl} 107285809Sscottl 108285809Sscottlstatic int 109285809Sscottlg_disk_access(struct g_provider *pp, int r, int w, int e) 110285809Sscottl{ 111285809Sscottl struct disk *dp; 112285809Sscottl int error; 113285809Sscottl 114285809Sscottl g_trace(G_T_ACCESS, "g_disk_access(%s, %d, %d, %d)", 115285809Sscottl pp->name, r, w, e); 116285809Sscottl g_topology_assert(); 117285809Sscottl dp = pp->geom->softc; 118285809Sscottl if (dp == NULL || dp->d_destroyed) { 119285809Sscottl /* 120285809Sscottl * Allow decreasing access count even if disk is not 121285809Sscottl * avaliable anymore. 122285809Sscottl */ 123285809Sscottl if (r <= 0 && w <= 0 && e <= 0) 124285809Sscottl return (0); 125285809Sscottl return (ENXIO); 126285809Sscottl } 127285809Sscottl r += pp->acr; 128285809Sscottl w += pp->acw; 129285809Sscottl e += pp->ace; 130285809Sscottl error = 0; 131285809Sscottl if ((pp->acr + pp->acw + pp->ace) == 0 && (r + w + e) > 0) { 132285809Sscottl if (dp->d_open != NULL) { 133285809Sscottl g_disk_lock_giant(dp); 134285809Sscottl error = dp->d_open(dp); 135285809Sscottl if (error != 0) 136285809Sscottl printf("Opened disk %s -> %d\n", 137285809Sscottl pp->name, error); 138285809Sscottl g_disk_unlock_giant(dp); 139285809Sscottl } 140285809Sscottl pp->mediasize = dp->d_mediasize; 141285809Sscottl pp->sectorsize = dp->d_sectorsize; 142285809Sscottl dp->d_flags |= DISKFLAG_OPEN; 143285809Sscottl if (dp->d_maxsize == 0) { 144285809Sscottl printf("WARNING: Disk drive %s%d has no d_maxsize\n", 145285809Sscottl dp->d_name, dp->d_unit); 146285809Sscottl dp->d_maxsize = DFLTPHYS; 147285809Sscottl } 148285809Sscottl } else if ((pp->acr + pp->acw + pp->ace) > 0 && (r + w + e) == 0) { 149285809Sscottl if (dp->d_close != NULL) { 150285809Sscottl g_disk_lock_giant(dp); 151285809Sscottl error = dp->d_close(dp); 152285809Sscottl if (error != 0) 153285809Sscottl printf("Closed disk %s -> %d\n", 154285809Sscottl pp->name, error); 155285809Sscottl g_disk_unlock_giant(dp); 156285809Sscottl } 157285809Sscottl dp->d_flags &= ~DISKFLAG_OPEN; 158285809Sscottl } 159285809Sscottl return (error); 160285809Sscottl} 161285809Sscottl 162285809Sscottlstatic void 163285809Sscottlg_disk_kerneldump(struct bio *bp, struct disk *dp) 164285809Sscottl{ 165285809Sscottl int error; 166285809Sscottl struct g_kerneldump *gkd; 167285809Sscottl struct dumperinfo di; 168285809Sscottl struct g_geom *gp; 169285809Sscottl 170285809Sscottl gkd = (struct g_kerneldump*)bp->bio_data; 171285809Sscottl gp = bp->bio_to->geom; 172285809Sscottl g_trace(G_T_TOPOLOGY, "g_disk_kernedump(%s, %jd, %jd)", 173285809Sscottl gp->name, (intmax_t)gkd->offset, (intmax_t)gkd->length); 174285809Sscottl if (dp->d_dump == NULL) { 175285809Sscottl g_io_deliver(bp, ENODEV); 176285809Sscottl return; 177285809Sscottl } 178285809Sscottl di.dumper = dp->d_dump; 179285809Sscottl di.priv = dp; 180285809Sscottl di.blocksize = dp->d_sectorsize; 181285809Sscottl di.mediaoffset = gkd->offset; 182285809Sscottl di.mediasize = gkd->length; 183285809Sscottl error = set_dumper(&di); 184285809Sscottl g_io_deliver(bp, error); 185285809Sscottl} 186285809Sscottl 187285809Sscottlstatic void 188285809Sscottlg_disk_done(struct bio *bp) 189285809Sscottl{ 190285809Sscottl struct bio *bp2; 191285809Sscottl struct disk *dp; 192285809Sscottl 193285809Sscottl /* See "notes" for why we need a mutex here */ 194285809Sscottl /* XXX: will witness accept a mix of Giant/unGiant drivers here ? */ 195285809Sscottl mtx_lock(&g_disk_done_mtx); 196285809Sscottl bp->bio_completed = bp->bio_length - bp->bio_resid; 197285809Sscottl 198285809Sscottl bp2 = bp->bio_parent; 199285809Sscottl if (bp2->bio_error == 0) 200285809Sscottl bp2->bio_error = bp->bio_error; 201285809Sscottl bp2->bio_completed += bp->bio_completed; 202285809Sscottl g_destroy_bio(bp); 203285809Sscottl bp2->bio_inbed++; 204285809Sscottl if (bp2->bio_children == bp2->bio_inbed) { 205285809Sscottl bp2->bio_resid = bp2->bio_bcount - bp2->bio_completed; 206285809Sscottl if ((dp = bp2->bio_to->geom->softc)) 207285809Sscottl devstat_end_transaction_bio(dp->d_devstat, bp2); 208285809Sscottl g_io_deliver(bp2, bp2->bio_error); 209285809Sscottl } 210285809Sscottl mtx_unlock(&g_disk_done_mtx); 211285809Sscottl} 212285809Sscottl 213285809Sscottlstatic int 214285809Sscottlg_disk_ioctl(struct g_provider *pp, u_long cmd, void * data, struct thread *td) 215285809Sscottl{ 216285809Sscottl struct g_geom *gp; 217285809Sscottl struct disk *dp; 218285809Sscottl int error; 219285809Sscottl 220285809Sscottl gp = pp->geom; 221285809Sscottl dp = gp->softc; 222285809Sscottl 223285809Sscottl if (dp->d_ioctl == NULL) 224285809Sscottl return (ENOIOCTL); 225285809Sscottl g_disk_lock_giant(dp); 226285809Sscottl error = dp->d_ioctl(dp, cmd, data, 0, td); 227285809Sscottl g_disk_unlock_giant(dp); 228285809Sscottl return(error); 229285809Sscottl} 230285809Sscottl 231285809Sscottlstatic void 232285809Sscottlg_disk_start(struct bio *bp) 233285809Sscottl{ 234285809Sscottl struct bio *bp2, *bp3; 235285809Sscottl struct disk *dp; 236285809Sscottl int error; 237285809Sscottl off_t off; 238285809Sscottl 239285809Sscottl dp = bp->bio_to->geom->softc; 240285809Sscottl if (dp == NULL || dp->d_destroyed) 241285809Sscottl g_io_deliver(bp, ENXIO); 242285809Sscottl error = EJUSTRETURN; 243285809Sscottl switch(bp->bio_cmd) { 244285809Sscottl case BIO_DELETE: 245285809Sscottl if (!(dp->d_flags & DISKFLAG_CANDELETE)) { 246285809Sscottl error = 0; 247285809Sscottl break; 248285809Sscottl } 249285809Sscottl /* fall-through */ 250285809Sscottl case BIO_READ: 251285809Sscottl case BIO_WRITE: 252285809Sscottl off = 0; 253285809Sscottl bp3 = NULL; 254285809Sscottl bp2 = g_clone_bio(bp); 255285809Sscottl if (bp2 == NULL) { 256285809Sscottl error = ENOMEM; 257285809Sscottl break; 258285809Sscottl } 259285809Sscottl devstat_start_transaction_bio(dp->d_devstat, bp); 260285809Sscottl do { 261285809Sscottl bp2->bio_offset += off; 262285809Sscottl bp2->bio_length -= off; 263285809Sscottl bp2->bio_data += off; 264285809Sscottl if (bp2->bio_length > dp->d_maxsize) { 265285809Sscottl /* 266285809Sscottl * XXX: If we have a stripesize we should really 267285809Sscottl * use it here. 268285809Sscottl */ 269285809Sscottl bp2->bio_length = dp->d_maxsize; 270285809Sscottl off += dp->d_maxsize; 271285809Sscottl /* 272285809Sscottl * To avoid a race, we need to grab the next bio 273285809Sscottl * before we schedule this one. See "notes". 274285809Sscottl */ 275285809Sscottl bp3 = g_clone_bio(bp); 276285809Sscottl if (bp3 == NULL) 277285809Sscottl bp->bio_error = ENOMEM; 278285809Sscottl } 279285809Sscottl bp2->bio_done = g_disk_done; 280285809Sscottl bp2->bio_pblkno = bp2->bio_offset / dp->d_sectorsize; 281285809Sscottl bp2->bio_bcount = bp2->bio_length; 282285809Sscottl bp2->bio_disk = dp; 283285809Sscottl g_disk_lock_giant(dp); 284285809Sscottl dp->d_strategy(bp2); 285285809Sscottl g_disk_unlock_giant(dp); 286285809Sscottl bp2 = bp3; 287285809Sscottl bp3 = NULL; 288285809Sscottl } while (bp2 != NULL); 289285809Sscottl break; 290285809Sscottl case BIO_GETATTR: 291285809Sscottl if (g_handleattr_int(bp, "GEOM::fwsectors", dp->d_fwsectors)) 292285809Sscottl break; 293285809Sscottl else if (g_handleattr_int(bp, "GEOM::fwheads", dp->d_fwheads)) 294285809Sscottl break; 295285809Sscottl else if (g_handleattr_off_t(bp, "GEOM::frontstuff", 0)) 296285809Sscottl break; 297285809Sscottl else if (!strcmp(bp->bio_attribute, "GEOM::kerneldump")) 298285809Sscottl g_disk_kerneldump(bp, dp); 299285809Sscottl else 300285809Sscottl error = ENOIOCTL; 301285809Sscottl break; 302285809Sscottl default: 303285809Sscottl error = EOPNOTSUPP; 304285809Sscottl break; 305285809Sscottl } 306285809Sscottl if (error != EJUSTRETURN) 307285809Sscottl g_io_deliver(bp, error); 308285809Sscottl return; 309285809Sscottl} 310285809Sscottl 311285809Sscottlstatic void 312285809Sscottlg_disk_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, struct g_consumer *cp, struct g_provider *pp) 313285809Sscottl{ 314285809Sscottl struct disk *dp; 315285809Sscottl 316285809Sscottl dp = gp->softc; 317285809Sscottl if (dp == NULL) 318285809Sscottl return; 319285809Sscottl if (indent == NULL) { 320285809Sscottl sbuf_printf(sb, " hd %u", dp->d_fwheads); 321285809Sscottl sbuf_printf(sb, " sc %u", dp->d_fwsectors); 322285809Sscottl return; 323285809Sscottl } 324285809Sscottl if (pp != NULL) { 325285809Sscottl sbuf_printf(sb, "%s<fwheads>%u</fwheads>\n", 326285809Sscottl indent, dp->d_fwheads); 327285809Sscottl sbuf_printf(sb, "%s<fwsectors>%u</fwsectors>\n", 328285809Sscottl indent, dp->d_fwsectors); 329285809Sscottl } 330285809Sscottl} 331285809Sscottl 332285809Sscottlstatic void 333285809Sscottlg_disk_create(void *arg, int flag) 334285809Sscottl{ 335285809Sscottl struct g_geom *gp; 336285809Sscottl struct g_provider *pp; 337285809Sscottl struct disk *dp; 338285809Sscottl 339285809Sscottl if (flag == EV_CANCEL) 340285809Sscottl return; 341285809Sscottl g_topology_assert(); 342285809Sscottl dp = arg; 343285809Sscottl gp = g_new_geomf(&g_disk_class, "%s%d", dp->d_name, dp->d_unit); 344285809Sscottl gp->softc = dp; 345285809Sscottl pp = g_new_providerf(gp, "%s", gp->name); 346285809Sscottl pp->mediasize = dp->d_mediasize; 347285809Sscottl pp->sectorsize = dp->d_sectorsize; 348285809Sscottl if (dp->d_flags & DISKFLAG_CANDELETE) 349285809Sscottl pp->flags |= G_PF_CANDELETE; 350285809Sscottl pp->stripeoffset = dp->d_stripeoffset; 351285809Sscottl pp->stripesize = dp->d_stripesize; 352285809Sscottl if (bootverbose) 353285809Sscottl printf("GEOM: new disk %s\n", gp->name); 354285809Sscottl dp->d_geom = gp; 355285809Sscottl g_error_provider(pp, 0); 356285809Sscottl} 357285809Sscottl 358285809Sscottlstatic void 359285809Sscottlg_disk_destroy(void *ptr, int flag) 360285809Sscottl{ 361285809Sscottl struct disk *dp; 362285809Sscottl struct g_geom *gp; 363285809Sscottl 364285809Sscottl g_topology_assert(); 365285809Sscottl dp = ptr; 366285809Sscottl gp = dp->d_geom; 367285809Sscottl gp->softc = NULL; 368285809Sscottl g_wither_geom(gp, ENXIO); 369285809Sscottl g_free(dp); 370285809Sscottl} 371285809Sscottl 372285809Sscottlstruct disk * 373285809Sscottldisk_alloc() 374285809Sscottl{ 375285809Sscottl struct disk *dp; 376285809Sscottl 377285809Sscottl dp = g_malloc(sizeof *dp, M_WAITOK | M_ZERO); 378285809Sscottl return (dp); 379285809Sscottl} 380285809Sscottl 381285809Sscottlvoid 382285809Sscottldisk_create(struct disk *dp, int version) 383285809Sscottl{ 384285809Sscottl if (version != DISK_VERSION_00) { 385285809Sscottl printf("WARNING: Attempt to add disk %s%d %s", 386285809Sscottl dp->d_name, dp->d_unit, 387285809Sscottl " using incompatible ABI version of disk(9)\n"); 388285809Sscottl printf("WARNING: Ignoring disk %s%d\n", 389285809Sscottl dp->d_name, dp->d_unit); 390285809Sscottl return; 391285809Sscottl } 392285809Sscottl KASSERT(dp->d_strategy != NULL, ("disk_create need d_strategy")); 393285809Sscottl KASSERT(dp->d_name != NULL, ("disk_create need d_name")); 394285809Sscottl KASSERT(*dp->d_name != 0, ("disk_create need d_name")); 395285809Sscottl KASSERT(strlen(dp->d_name) < SPECNAMELEN - 4, ("disk name too long")); 396285809Sscottl if (dp->d_devstat == NULL) 397285809Sscottl dp->d_devstat = devstat_new_entry(dp->d_name, dp->d_unit, 398285809Sscottl dp->d_sectorsize, DEVSTAT_ALL_SUPPORTED, 399285809Sscottl DEVSTAT_TYPE_DIRECT, DEVSTAT_PRIORITY_MAX); 400285809Sscottl dp->d_geom = NULL; 401285809Sscottl g_post_event(g_disk_create, dp, M_WAITOK, dp, NULL); 402285809Sscottl} 403285809Sscottl 404285809Sscottlvoid 405285809Sscottldisk_destroy(struct disk *dp) 406285809Sscottl{ 407285809Sscottl 408285809Sscottl g_cancel_event(dp); 409285809Sscottl dp->d_destroyed = 1; 410285809Sscottl if (dp->d_devstat != NULL) 411285809Sscottl devstat_remove_entry(dp->d_devstat); 412285809Sscottl g_post_event(g_disk_destroy, dp, M_WAITOK, NULL); 413285809Sscottl} 414285809Sscottl 415285809Sscottlstatic void 416285809Sscottlg_kern_disks(void *p, int flag __unused) 417285809Sscottl{ 418285809Sscottl struct sbuf *sb; 419285809Sscottl struct g_geom *gp; 420285809Sscottl char *sp; 421285809Sscottl 422285809Sscottl sb = p; 423285809Sscottl sp = ""; 424285809Sscottl g_topology_assert(); 425285809Sscottl LIST_FOREACH(gp, &g_disk_class.geom, geom) { 426285809Sscottl sbuf_printf(sb, "%s%s", sp, gp->name); 427285809Sscottl sp = " "; 428285809Sscottl } 429285809Sscottl sbuf_finish(sb); 430285809Sscottl} 431285809Sscottl 432285809Sscottlstatic int 433285809Sscottlsysctl_disks(SYSCTL_HANDLER_ARGS) 434285809Sscottl{ 435285809Sscottl int error; 436285809Sscottl struct sbuf *sb; 437285809Sscottl 438285809Sscottl sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND); 439285809Sscottl g_waitfor_event(g_kern_disks, sb, M_WAITOK, NULL); 440285809Sscottl error = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb) + 1); 441285809Sscottl sbuf_delete(sb); 442285809Sscottl return error; 443285809Sscottl} 444285809Sscottl 445285809SscottlSYSCTL_PROC(_kern, OID_AUTO, disks, CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_NOLOCK, 0, 0, 446285809Sscottl sysctl_disks, "A", "names of available disks"); 447285809Sscottl 448285809Sscottl