geom_disk.c revision 273767
192108Sphk/*- 292108Sphk * Copyright (c) 2002 Poul-Henning Kamp 392108Sphk * Copyright (c) 2002 Networks Associates Technology, Inc. 492108Sphk * All rights reserved. 592108Sphk * 692108Sphk * This software was developed for the FreeBSD Project by Poul-Henning Kamp 792108Sphk * and NAI Labs, the Security Research Division of Network Associates, Inc. 892108Sphk * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the 992108Sphk * DARPA CHATS research program. 1092108Sphk * 1192108Sphk * Redistribution and use in source and binary forms, with or without 1292108Sphk * modification, are permitted provided that the following conditions 1392108Sphk * are met: 1492108Sphk * 1. Redistributions of source code must retain the above copyright 1592108Sphk * notice, this list of conditions and the following disclaimer. 1692108Sphk * 2. Redistributions in binary form must reproduce the above copyright 1792108Sphk * notice, this list of conditions and the following disclaimer in the 1892108Sphk * documentation and/or other materials provided with the distribution. 1992108Sphk * 3. The names of the authors may not be used to endorse or promote 2092108Sphk * products derived from this software without specific prior written 2192108Sphk * permission. 2292108Sphk * 2392108Sphk * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 2492108Sphk * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2592108Sphk * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2692108Sphk * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2792108Sphk * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2892108Sphk * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2992108Sphk * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3092108Sphk * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3192108Sphk * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3292108Sphk * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3392108Sphk * SUCH DAMAGE. 3492108Sphk */ 3592108Sphk 36116196Sobrien#include <sys/cdefs.h> 37116196Sobrien__FBSDID("$FreeBSD: stable/10/sys/geom/geom_disk.c 273767 2014-10-28 08:00:28Z mav $"); 38116196Sobrien 39104519Sphk#include "opt_geom.h" 40104519Sphk 4192108Sphk#include <sys/param.h> 4292108Sphk#include <sys/systm.h> 4392108Sphk#include <sys/kernel.h> 4492108Sphk#include <sys/sysctl.h> 4592108Sphk#include <sys/bio.h> 46196823Spjd#include <sys/ctype.h> 47110119Sphk#include <sys/fcntl.h> 4892108Sphk#include <sys/malloc.h> 49223921Sae#include <sys/sbuf.h> 50111979Sphk#include <sys/devicestat.h> 5192108Sphk#include <machine/md_var.h> 5292108Sphk 5392108Sphk#include <sys/lock.h> 5492108Sphk#include <sys/mutex.h> 5592108Sphk#include <geom/geom.h> 56112952Sphk#include <geom/geom_disk.h> 57112476Sphk#include <geom/geom_int.h> 5892108Sphk 59219970Smav#include <dev/led/led.h> 60219970Smav 61219970Smavstruct g_disk_softc { 62248694Smav struct mtx done_mtx; 63219970Smav struct disk *dp; 64219970Smav struct sysctl_ctx_list sysctl_ctx; 65219970Smav struct sysctl_oid *sysctl_tree; 66219970Smav char led[64]; 67219970Smav uint32_t state; 68260385Sscottl struct mtx start_mtx; 69219970Smav}; 70219970Smav 7192108Sphkstatic g_access_t g_disk_access; 72133314Sphkstatic g_start_t g_disk_start; 73133314Sphkstatic g_ioctl_t g_disk_ioctl; 74133314Sphkstatic g_dumpconf_t g_disk_dumpconf; 75237518Skenstatic g_provgone_t g_disk_providergone; 7692108Sphk 77141624Sphkstatic struct g_class g_disk_class = { 78249507Sivoras .name = G_DISK_CLASS_NAME, 79133318Sphk .version = G_VERSION, 80133314Sphk .start = g_disk_start, 81133314Sphk .access = g_disk_access, 82133314Sphk .ioctl = g_disk_ioctl, 83237518Sken .providergone = g_disk_providergone, 84133314Sphk .dumpconf = g_disk_dumpconf, 8592108Sphk}; 8692108Sphk 87219970SmavSYSCTL_DECL(_kern_geom); 88227309Sedstatic SYSCTL_NODE(_kern_geom, OID_AUTO, disk, CTLFLAG_RW, 0, 89227309Sed "GEOM_DISK stuff"); 90219970Smav 91115473SphkDECLARE_GEOM_CLASS(g_disk_class, g_disk); 92115473Sphk 93110052Sphkstatic void __inline 94110052Sphkg_disk_lock_giant(struct disk *dp) 95110052Sphk{ 96226736Spjd 97125975Sphk if (dp->d_flags & DISKFLAG_NEEDSGIANT) 98125975Sphk mtx_lock(&Giant); 99110052Sphk} 100110052Sphk 101110052Sphkstatic void __inline 102110052Sphkg_disk_unlock_giant(struct disk *dp) 103110052Sphk{ 104226736Spjd 105125975Sphk if (dp->d_flags & DISKFLAG_NEEDSGIANT) 106125975Sphk mtx_unlock(&Giant); 107110052Sphk} 108110052Sphk 10992108Sphkstatic int 11092108Sphkg_disk_access(struct g_provider *pp, int r, int w, int e) 11192108Sphk{ 11292108Sphk struct disk *dp; 113219970Smav struct g_disk_softc *sc; 11492108Sphk int error; 11592108Sphk 11692108Sphk g_trace(G_T_ACCESS, "g_disk_access(%s, %d, %d, %d)", 11792108Sphk pp->name, r, w, e); 11892108Sphk g_topology_assert(); 119248694Smav sc = pp->private; 120219970Smav if (sc == NULL || (dp = sc->dp) == NULL || dp->d_destroyed) { 121125539Spjd /* 122125539Spjd * Allow decreasing access count even if disk is not 123125539Spjd * avaliable anymore. 124125539Spjd */ 125125539Spjd if (r <= 0 && w <= 0 && e <= 0) 126125539Spjd return (0); 127125975Sphk return (ENXIO); 128125539Spjd } 12992108Sphk r += pp->acr; 13092108Sphk w += pp->acw; 13192108Sphk e += pp->ace; 132110119Sphk error = 0; 13392108Sphk if ((pp->acr + pp->acw + pp->ace) == 0 && (r + w + e) > 0) { 134111668Sphk if (dp->d_open != NULL) { 135110119Sphk g_disk_lock_giant(dp); 136111668Sphk error = dp->d_open(dp); 137166861Sn_hibma if (bootverbose && error != 0) 138110119Sphk printf("Opened disk %s -> %d\n", 139110119Sphk pp->name, error); 140110119Sphk g_disk_unlock_giant(dp); 141251616Smav if (error != 0) 142251616Smav return (error); 143110119Sphk } 144105551Sphk pp->mediasize = dp->d_mediasize; 145105551Sphk pp->sectorsize = dp->d_sectorsize; 146110727Sphk if (dp->d_maxsize == 0) { 147110727Sphk printf("WARNING: Disk drive %s%d has no d_maxsize\n", 148110727Sphk dp->d_name, dp->d_unit); 149110727Sphk dp->d_maxsize = DFLTPHYS; 150110727Sphk } 151252657Ssmh if (dp->d_delmaxsize == 0) { 152252657Ssmh if (bootverbose && dp->d_flags & DISKFLAG_CANDELETE) { 153252657Ssmh printf("WARNING: Disk drive %s%d has no " 154252657Ssmh "d_delmaxsize\n", dp->d_name, dp->d_unit); 155249940Ssmh } 156252657Ssmh dp->d_delmaxsize = dp->d_maxsize; 157249940Ssmh } 158249940Ssmh pp->stripeoffset = dp->d_stripeoffset; 159249940Ssmh pp->stripesize = dp->d_stripesize; 160249940Ssmh dp->d_flags |= DISKFLAG_OPEN; 16192108Sphk } else if ((pp->acr + pp->acw + pp->ace) > 0 && (r + w + e) == 0) { 162111668Sphk if (dp->d_close != NULL) { 163110119Sphk g_disk_lock_giant(dp); 164111668Sphk error = dp->d_close(dp); 165110119Sphk if (error != 0) 166110119Sphk printf("Closed disk %s -> %d\n", 167110119Sphk pp->name, error); 168110119Sphk g_disk_unlock_giant(dp); 169110119Sphk } 170219970Smav sc->state = G_STATE_ACTIVE; 171219970Smav if (sc->led[0] != 0) 172219970Smav led_set(sc->led, "0"); 173110119Sphk dp->d_flags &= ~DISKFLAG_OPEN; 17492108Sphk } 17592108Sphk return (error); 17692108Sphk} 17792108Sphk 17892108Sphkstatic void 17995038Sphkg_disk_kerneldump(struct bio *bp, struct disk *dp) 180219950Smav{ 18195038Sphk struct g_kerneldump *gkd; 182104450Sphk struct g_geom *gp; 18395038Sphk 18495038Sphk gkd = (struct g_kerneldump*)bp->bio_data; 185104450Sphk gp = bp->bio_to->geom; 186264712Sbdrewery g_trace(G_T_TOPOLOGY, "g_disk_kerneldump(%s, %jd, %jd)", 187104450Sphk gp->name, (intmax_t)gkd->offset, (intmax_t)gkd->length); 188120572Sphk if (dp->d_dump == NULL) { 189120572Sphk g_io_deliver(bp, ENODEV); 190120572Sphk return; 191120572Sphk } 192219950Smav gkd->di.dumper = dp->d_dump; 193219950Smav gkd->di.priv = dp; 194219950Smav gkd->di.blocksize = dp->d_sectorsize; 195219950Smav gkd->di.maxiosize = dp->d_maxsize; 196219950Smav gkd->di.mediaoffset = gkd->offset; 197140968Sphk if ((gkd->offset + gkd->length) > dp->d_mediasize) 198140968Sphk gkd->length = dp->d_mediasize - gkd->offset; 199219950Smav gkd->di.mediasize = gkd->length; 200219950Smav g_io_deliver(bp, 0); 20195038Sphk} 20295038Sphk 20395038Sphkstatic void 204219970Smavg_disk_setstate(struct bio *bp, struct g_disk_softc *sc) 205219970Smav{ 206219970Smav const char *cmd; 207219970Smav 208219970Smav memcpy(&sc->state, bp->bio_data, sizeof(sc->state)); 209219970Smav if (sc->led[0] != 0) { 210219970Smav switch (sc->state) { 211219970Smav case G_STATE_FAILED: 212219970Smav cmd = "1"; 213219970Smav break; 214219970Smav case G_STATE_REBUILD: 215219970Smav cmd = "f5"; 216219970Smav break; 217219970Smav case G_STATE_RESYNC: 218219970Smav cmd = "f1"; 219219970Smav break; 220219970Smav default: 221219970Smav cmd = "0"; 222219970Smav break; 223219970Smav } 224219970Smav led_set(sc->led, cmd); 225219970Smav } 226219970Smav g_io_deliver(bp, 0); 227219970Smav} 228219970Smav 229219970Smavstatic void 23092108Sphkg_disk_done(struct bio *bp) 23192108Sphk{ 232260385Sscottl struct bintime now; 233111979Sphk struct bio *bp2; 234219970Smav struct g_disk_softc *sc; 23592108Sphk 236110720Sphk /* See "notes" for why we need a mutex here */ 237110720Sphk /* XXX: will witness accept a mix of Giant/unGiant drivers here ? */ 238248694Smav bp2 = bp->bio_parent; 239248694Smav sc = bp2->bio_to->private; 24092108Sphk bp->bio_completed = bp->bio_length - bp->bio_resid; 241260385Sscottl binuptime(&now); 242248694Smav mtx_lock(&sc->done_mtx); 243111979Sphk if (bp2->bio_error == 0) 244111979Sphk bp2->bio_error = bp->bio_error; 245111979Sphk bp2->bio_completed += bp->bio_completed; 246266608Smav if ((bp->bio_cmd & (BIO_READ|BIO_WRITE|BIO_DELETE|BIO_FLUSH)) != 0) 247260385Sscottl devstat_end_transaction_bio_bt(sc->dp->d_devstat, bp, &now); 248111979Sphk bp2->bio_inbed++; 249111979Sphk if (bp2->bio_children == bp2->bio_inbed) { 250260385Sscottl mtx_unlock(&sc->done_mtx); 251112259Sphk bp2->bio_resid = bp2->bio_bcount - bp2->bio_completed; 252111979Sphk g_io_deliver(bp2, bp2->bio_error); 253260385Sscottl } else 254260385Sscottl mtx_unlock(&sc->done_mtx); 255260385Sscottl g_destroy_bio(bp); 256260385Sscottl} 257260385Sscottl 258119660Sphkstatic int 259138732Sphkg_disk_ioctl(struct g_provider *pp, u_long cmd, void * data, int fflag, struct thread *td) 260119660Sphk{ 261119660Sphk struct disk *dp; 262219970Smav struct g_disk_softc *sc; 263119660Sphk int error; 264119660Sphk 265248694Smav sc = pp->private; 266219970Smav dp = sc->dp; 267119660Sphk 268119660Sphk if (dp->d_ioctl == NULL) 269119660Sphk return (ENOIOCTL); 270119660Sphk g_disk_lock_giant(dp); 271138732Sphk error = dp->d_ioctl(dp, cmd, data, fflag, td); 272119660Sphk g_disk_unlock_giant(dp); 273226736Spjd return (error); 274119660Sphk} 275119660Sphk 27692108Sphkstatic void 27792108Sphkg_disk_start(struct bio *bp) 27892108Sphk{ 279110720Sphk struct bio *bp2, *bp3; 28092108Sphk struct disk *dp; 281219970Smav struct g_disk_softc *sc; 28292403Sphk int error; 283273767Smav off_t off; 28492108Sphk 285248694Smav sc = bp->bio_to->private; 286219970Smav if (sc == NULL || (dp = sc->dp) == NULL || dp->d_destroyed) { 287115214Sphk g_io_deliver(bp, ENXIO); 288143791Sphk return; 289143791Sphk } 290104609Sphk error = EJUSTRETURN; 29192108Sphk switch(bp->bio_cmd) { 292104609Sphk case BIO_DELETE: 293110119Sphk if (!(dp->d_flags & DISKFLAG_CANDELETE)) { 294226737Spjd error = EOPNOTSUPP; 295104609Sphk break; 296104609Sphk } 297104609Sphk /* fall-through */ 29892108Sphk case BIO_READ: 29992108Sphk case BIO_WRITE: 300110720Sphk off = 0; 301110720Sphk bp3 = NULL; 30292108Sphk bp2 = g_clone_bio(bp); 303110477Sphk if (bp2 == NULL) { 304110477Sphk error = ENOMEM; 305110477Sphk break; 306110477Sphk } 307110720Sphk do { 308273767Smav off_t d_maxsize; 309273767Smav 310273767Smav d_maxsize = (bp->bio_cmd == BIO_DELETE) ? 311273767Smav dp->d_delmaxsize : dp->d_maxsize; 312110720Sphk bp2->bio_offset += off; 313110720Sphk bp2->bio_length -= off; 314248516Skib if ((bp->bio_flags & BIO_UNMAPPED) == 0) { 315248516Skib bp2->bio_data += off; 316248516Skib } else { 317248516Skib KASSERT((dp->d_flags & DISKFLAG_UNMAPPED_BIO) 318248516Skib != 0, 319248516Skib ("unmapped bio not supported by disk %s", 320248516Skib dp->d_name)); 321248516Skib bp2->bio_ma += off / PAGE_SIZE; 322248516Skib bp2->bio_ma_offset += off; 323248516Skib bp2->bio_ma_offset %= PAGE_SIZE; 324248516Skib bp2->bio_ma_n -= off / PAGE_SIZE; 325248516Skib } 326249940Ssmh if (bp2->bio_length > d_maxsize) { 327110720Sphk /* 328110720Sphk * XXX: If we have a stripesize we should really 329249940Ssmh * use it here. Care should be taken in the delete 330249940Ssmh * case if this is done as deletes can be very 331249940Ssmh * sensitive to size given how they are processed. 332110720Sphk */ 333249940Ssmh bp2->bio_length = d_maxsize; 334248516Skib if ((bp->bio_flags & BIO_UNMAPPED) != 0) { 335248516Skib bp2->bio_ma_n = howmany( 336248516Skib bp2->bio_ma_offset + 337248516Skib bp2->bio_length, PAGE_SIZE); 338248516Skib } 339249940Ssmh off += d_maxsize; 340110720Sphk /* 341110720Sphk * To avoid a race, we need to grab the next bio 342110720Sphk * before we schedule this one. See "notes". 343110720Sphk */ 344110720Sphk bp3 = g_clone_bio(bp); 345110720Sphk if (bp3 == NULL) 346110720Sphk bp->bio_error = ENOMEM; 347110720Sphk } 348110720Sphk bp2->bio_done = g_disk_done; 349110720Sphk bp2->bio_pblkno = bp2->bio_offset / dp->d_sectorsize; 350110720Sphk bp2->bio_bcount = bp2->bio_length; 351110720Sphk bp2->bio_disk = dp; 352260385Sscottl mtx_lock(&sc->start_mtx); 353150759Stegge devstat_start_transaction_bio(dp->d_devstat, bp2); 354260385Sscottl mtx_unlock(&sc->start_mtx); 355110720Sphk g_disk_lock_giant(dp); 356110720Sphk dp->d_strategy(bp2); 357110720Sphk g_disk_unlock_giant(dp); 358110720Sphk bp2 = bp3; 359110720Sphk bp3 = NULL; 360110720Sphk } while (bp2 != NULL); 36192108Sphk break; 36292108Sphk case BIO_GETATTR: 363223089Sgibbs /* Give the driver a chance to override */ 364223089Sgibbs if (dp->d_getattr != NULL) { 365223089Sgibbs if (bp->bio_disk == NULL) 366223089Sgibbs bp->bio_disk = dp; 367223089Sgibbs error = dp->d_getattr(bp); 368223089Sgibbs if (error != -1) 369223089Sgibbs break; 370223089Sgibbs error = EJUSTRETURN; 371223089Sgibbs } 372216794Skib if (g_handleattr_int(bp, "GEOM::candelete", 373216794Skib (dp->d_flags & DISKFLAG_CANDELETE) != 0)) 37492403Sphk break; 375216794Skib else if (g_handleattr_int(bp, "GEOM::fwsectors", 376216794Skib dp->d_fwsectors)) 377216794Skib break; 378103714Sphk else if (g_handleattr_int(bp, "GEOM::fwheads", dp->d_fwheads)) 37992403Sphk break; 38098066Sphk else if (g_handleattr_off_t(bp, "GEOM::frontstuff", 0)) 38194287Sphk break; 382169285Spjd else if (g_handleattr_str(bp, "GEOM::ident", dp->d_ident)) 383169285Spjd break; 384271238Ssmh else if (g_handleattr_uint16_t(bp, "GEOM::hba_vendor", 385271238Ssmh dp->d_hba_vendor)) 386210471Smav break; 387271238Ssmh else if (g_handleattr_uint16_t(bp, "GEOM::hba_device", 388271238Ssmh dp->d_hba_device)) 389210471Smav break; 390271238Ssmh else if (g_handleattr_uint16_t(bp, "GEOM::hba_subvendor", 391271238Ssmh dp->d_hba_subvendor)) 392210471Smav break; 393271238Ssmh else if (g_handleattr_uint16_t(bp, "GEOM::hba_subdevice", 394271238Ssmh dp->d_hba_subdevice)) 395210471Smav break; 39695038Sphk else if (!strcmp(bp->bio_attribute, "GEOM::kerneldump")) 39795038Sphk g_disk_kerneldump(bp, dp); 398219970Smav else if (!strcmp(bp->bio_attribute, "GEOM::setstate")) 399219970Smav g_disk_setstate(bp, sc); 400271238Ssmh else if (!strcmp(bp->bio_attribute, "GEOM::rotation_rate")) { 401271238Ssmh uint64_t v; 402271238Ssmh 403271238Ssmh if ((dp->d_flags & DISKFLAG_LACKS_ROTRATE) == 0) 404271238Ssmh v = dp->d_rotation_rate; 405271238Ssmh else 406271238Ssmh v = 0; /* rate unknown */ 407271238Ssmh g_handleattr_uint16_t(bp, "GEOM::rotation_rate", v); 408271238Ssmh break; 409271238Ssmh } else 41092403Sphk error = ENOIOCTL; 41192403Sphk break; 412163833Spjd case BIO_FLUSH: 413240629Savg g_trace(G_T_BIO, "g_disk_flushcache(%s)", 414163833Spjd bp->bio_to->name); 415163833Spjd if (!(dp->d_flags & DISKFLAG_CANFLUSHCACHE)) { 416226737Spjd error = EOPNOTSUPP; 417226737Spjd break; 418163833Spjd } 419273767Smav bp2 = g_clone_bio(bp); 420273767Smav if (bp2 == NULL) { 421273767Smav g_io_deliver(bp, ENOMEM); 422273767Smav return; 423273767Smav } 424273767Smav bp2->bio_done = g_disk_done; 425273767Smav bp2->bio_disk = dp; 426273767Smav mtx_lock(&sc->start_mtx); 427273767Smav devstat_start_transaction_bio(dp->d_devstat, bp2); 428273767Smav mtx_unlock(&sc->start_mtx); 429163833Spjd g_disk_lock_giant(dp); 430273767Smav dp->d_strategy(bp2); 431163833Spjd g_disk_unlock_giant(dp); 432163833Spjd break; 43392108Sphk default: 43492403Sphk error = EOPNOTSUPP; 43592403Sphk break; 43692403Sphk } 437104609Sphk if (error != EJUSTRETURN) 438104195Sphk g_io_deliver(bp, error); 43992403Sphk return; 44092108Sphk} 44192108Sphk 442104936Sphkstatic void 443107953Sphkg_disk_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, struct g_consumer *cp, struct g_provider *pp) 444105537Sphk{ 445251654Smav struct bio *bp; 446105537Sphk struct disk *dp; 447219970Smav struct g_disk_softc *sc; 448251654Smav char *buf; 449251654Smav int res = 0; 450105537Sphk 451219970Smav sc = gp->softc; 452219970Smav if (sc == NULL || (dp = sc->dp) == NULL) 453120374Sphk return; 454106101Sphk if (indent == NULL) { 455106101Sphk sbuf_printf(sb, " hd %u", dp->d_fwheads); 456106101Sphk sbuf_printf(sb, " sc %u", dp->d_fwsectors); 457106101Sphk return; 458106101Sphk } 459105539Sphk if (pp != NULL) { 460105537Sphk sbuf_printf(sb, "%s<fwheads>%u</fwheads>\n", 461105537Sphk indent, dp->d_fwheads); 462105537Sphk sbuf_printf(sb, "%s<fwsectors>%u</fwsectors>\n", 463105537Sphk indent, dp->d_fwsectors); 464251654Smav if (dp->d_getattr != NULL) { 465251654Smav buf = g_malloc(DISK_IDENT_SIZE, M_WAITOK); 466251654Smav bp = g_alloc_bio(); 467251654Smav bp->bio_disk = dp; 468251654Smav bp->bio_attribute = "GEOM::ident"; 469251654Smav bp->bio_length = DISK_IDENT_SIZE; 470251654Smav bp->bio_data = buf; 471251654Smav res = dp->d_getattr(bp); 472260479Smav sbuf_printf(sb, "%s<ident>", indent); 473260479Smav g_conf_printf_escaped(sb, "%s", 474251654Smav res == 0 ? buf: dp->d_ident); 475260479Smav sbuf_printf(sb, "</ident>\n"); 476251654Smav bp->bio_attribute = "GEOM::lunid"; 477251654Smav bp->bio_length = DISK_IDENT_SIZE; 478251654Smav bp->bio_data = buf; 479260479Smav if (dp->d_getattr(bp) == 0) { 480260479Smav sbuf_printf(sb, "%s<lunid>", indent); 481260479Smav g_conf_printf_escaped(sb, "%s", buf); 482260479Smav sbuf_printf(sb, "</lunid>\n"); 483260479Smav } 484254766Smav bp->bio_attribute = "GEOM::lunname"; 485254766Smav bp->bio_length = DISK_IDENT_SIZE; 486254766Smav bp->bio_data = buf; 487260479Smav if (dp->d_getattr(bp) == 0) { 488260479Smav sbuf_printf(sb, "%s<lunname>", indent); 489260479Smav g_conf_printf_escaped(sb, "%s", buf); 490260479Smav sbuf_printf(sb, "</lunname>\n"); 491260479Smav } 492251654Smav g_destroy_bio(bp); 493251654Smav g_free(buf); 494260479Smav } else { 495260479Smav sbuf_printf(sb, "%s<ident>", indent); 496260479Smav g_conf_printf_escaped(sb, "%s", dp->d_ident); 497260479Smav sbuf_printf(sb, "</ident>\n"); 498260479Smav } 499260479Smav sbuf_printf(sb, "%s<descr>", indent); 500260479Smav g_conf_printf_escaped(sb, "%s", dp->d_descr); 501260479Smav sbuf_printf(sb, "</descr>\n"); 502105537Sphk } 503105537Sphk} 504105537Sphk 505105537Sphkstatic void 506242322Straszg_disk_resize(void *ptr, int flag) 507238216Strasz{ 508242322Strasz struct disk *dp; 509238216Strasz struct g_geom *gp; 510238216Strasz struct g_provider *pp; 511238216Strasz 512242322Strasz if (flag == EV_CANCEL) 513242322Strasz return; 514242322Strasz g_topology_assert(); 515242322Strasz 516242322Strasz dp = ptr; 517238216Strasz gp = dp->d_geom; 518238216Strasz 519242322Strasz if (dp->d_destroyed || gp == NULL) 520242322Strasz return; 521242322Strasz 522238216Strasz LIST_FOREACH(pp, &gp->provider, provider) { 523238216Strasz if (pp->sectorsize != 0 && 524238216Strasz pp->sectorsize != dp->d_sectorsize) 525238216Strasz g_wither_provider(pp, ENXIO); 526238216Strasz else 527238216Strasz g_resize_provider(pp, dp->d_mediasize); 528238216Strasz } 529238216Strasz} 530238216Strasz 531238216Straszstatic void 532115309Sphkg_disk_create(void *arg, int flag) 53392108Sphk{ 53492108Sphk struct g_geom *gp; 53592108Sphk struct g_provider *pp; 536110708Sphk struct disk *dp; 537219970Smav struct g_disk_softc *sc; 538219970Smav char tmpstr[80]; 53992108Sphk 540115309Sphk if (flag == EV_CANCEL) 541115309Sphk return; 542104936Sphk g_topology_assert(); 543110708Sphk dp = arg; 544219970Smav sc = g_malloc(sizeof(*sc), M_WAITOK | M_ZERO); 545260385Sscottl mtx_init(&sc->start_mtx, "g_disk_start", NULL, MTX_DEF); 546248694Smav mtx_init(&sc->done_mtx, "g_disk_done", NULL, MTX_DEF); 547219970Smav sc->dp = dp; 548110708Sphk gp = g_new_geomf(&g_disk_class, "%s%d", dp->d_name, dp->d_unit); 549219970Smav gp->softc = sc; 550104936Sphk pp = g_new_providerf(gp, "%s", gp->name); 551260385Sscottl devstat_remove_entry(pp->stat); 552260385Sscottl pp->stat = NULL; 553260385Sscottl dp->d_devstat->id = pp; 554110708Sphk pp->mediasize = dp->d_mediasize; 555110708Sphk pp->sectorsize = dp->d_sectorsize; 556110710Sphk pp->stripeoffset = dp->d_stripeoffset; 557110710Sphk pp->stripesize = dp->d_stripesize; 558248516Skib if ((dp->d_flags & DISKFLAG_UNMAPPED_BIO) != 0) 559248516Skib pp->flags |= G_PF_ACCEPT_UNMAPPED; 560260385Sscottl if ((dp->d_flags & DISKFLAG_DIRECT_COMPLETION) != 0) 561260385Sscottl pp->flags |= G_PF_DIRECT_SEND; 562260385Sscottl pp->flags |= G_PF_DIRECT_RECEIVE; 563105957Sphk if (bootverbose) 564105957Sphk printf("GEOM: new disk %s\n", gp->name); 565219970Smav sysctl_ctx_init(&sc->sysctl_ctx); 566219970Smav snprintf(tmpstr, sizeof(tmpstr), "GEOM disk %s", gp->name); 567219970Smav sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx, 568219970Smav SYSCTL_STATIC_CHILDREN(_kern_geom_disk), OID_AUTO, gp->name, 569219970Smav CTLFLAG_RD, 0, tmpstr); 570219970Smav if (sc->sysctl_tree != NULL) { 571219970Smav snprintf(tmpstr, sizeof(tmpstr), 572219970Smav "kern.geom.disk.%s.led", gp->name); 573219970Smav TUNABLE_STR_FETCH(tmpstr, sc->led, sizeof(sc->led)); 574219970Smav SYSCTL_ADD_STRING(&sc->sysctl_ctx, 575219970Smav SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "led", 576219970Smav CTLFLAG_RW | CTLFLAG_TUN, sc->led, sizeof(sc->led), 577219970Smav "LED name"); 578219970Smav } 579219970Smav pp->private = sc; 580112989Sphk dp->d_geom = gp; 581112989Sphk g_error_provider(pp, 0); 582104936Sphk} 583104936Sphk 584237518Sken/* 585237518Sken * We get this callback after all of the consumers have gone away, and just 586237518Sken * before the provider is freed. If the disk driver provided a d_gone 587237518Sken * callback, let them know that it is okay to free resources -- they won't 588237518Sken * be getting any more accesses from GEOM. 589237518Sken */ 590119652Sphkstatic void 591237518Skeng_disk_providergone(struct g_provider *pp) 592237518Sken{ 593237518Sken struct disk *dp; 594237518Sken struct g_disk_softc *sc; 595237518Sken 596248694Smav sc = (struct g_disk_softc *)pp->private; 597237518Sken dp = sc->dp; 598248694Smav if (dp != NULL && dp->d_gone != NULL) 599237518Sken dp->d_gone(dp); 600248694Smav if (sc->sysctl_tree != NULL) { 601248694Smav sysctl_ctx_free(&sc->sysctl_ctx); 602248694Smav sc->sysctl_tree = NULL; 603248694Smav } 604248694Smav if (sc->led[0] != 0) { 605248694Smav led_set(sc->led, "0"); 606248694Smav sc->led[0] = 0; 607248694Smav } 608248694Smav pp->private = NULL; 609248694Smav pp->geom->softc = NULL; 610248694Smav mtx_destroy(&sc->done_mtx); 611260385Sscottl mtx_destroy(&sc->start_mtx); 612248694Smav g_free(sc); 613237518Sken} 614237518Sken 615237518Skenstatic void 616119652Sphkg_disk_destroy(void *ptr, int flag) 617119652Sphk{ 618125975Sphk struct disk *dp; 619119652Sphk struct g_geom *gp; 620219970Smav struct g_disk_softc *sc; 621104936Sphk 622119652Sphk g_topology_assert(); 623125975Sphk dp = ptr; 624125975Sphk gp = dp->d_geom; 625140261Sphk if (gp != NULL) { 626219970Smav sc = gp->softc; 627248694Smav if (sc != NULL) 628248694Smav sc->dp = NULL; 629248694Smav dp->d_geom = NULL; 630140261Sphk g_wither_geom(gp, ENXIO); 631140261Sphk } 632125975Sphk g_free(dp); 633119652Sphk} 634104936Sphk 635169287Spjd/* 636196823Spjd * We only allow printable characters in disk ident, 637196823Spjd * the rest is converted to 'x<HH>'. 638169287Spjd */ 639169287Spjdstatic void 640169287Spjdg_disk_ident_adjust(char *ident, size_t size) 641169287Spjd{ 642196823Spjd char *p, tmp[4], newid[DISK_IDENT_SIZE]; 643169287Spjd 644196823Spjd newid[0] = '\0'; 645196823Spjd for (p = ident; *p != '\0'; p++) { 646196823Spjd if (isprint(*p)) { 647196823Spjd tmp[0] = *p; 648196823Spjd tmp[1] = '\0'; 649196823Spjd } else { 650196823Spjd snprintf(tmp, sizeof(tmp), "x%02hhx", 651196823Spjd *(unsigned char *)p); 652196823Spjd } 653196823Spjd if (strlcat(newid, tmp, sizeof(newid)) >= sizeof(newid)) 654169287Spjd break; 655169287Spjd } 656169287Spjd bzero(ident, size); 657169287Spjd strlcpy(ident, newid, size); 658169287Spjd} 659169287Spjd 660125975Sphkstruct disk * 661226735Spjddisk_alloc(void) 662125975Sphk{ 663125975Sphk 664226735Spjd return (g_malloc(sizeof(struct disk), M_WAITOK | M_ZERO)); 665125975Sphk} 666125975Sphk 667111668Sphkvoid 668125975Sphkdisk_create(struct disk *dp, int version) 669104936Sphk{ 670226736Spjd 671252657Ssmh if (version != DISK_VERSION) { 672125975Sphk printf("WARNING: Attempt to add disk %s%d %s", 673125975Sphk dp->d_name, dp->d_unit, 674125975Sphk " using incompatible ABI version of disk(9)\n"); 675125975Sphk printf("WARNING: Ignoring disk %s%d\n", 676125975Sphk dp->d_name, dp->d_unit); 677125975Sphk return; 678125975Sphk } 679271238Ssmh if (version < DISK_VERSION_04) 680271238Ssmh dp->d_flags |= DISKFLAG_LACKS_ROTRATE; 681110119Sphk KASSERT(dp->d_strategy != NULL, ("disk_create need d_strategy")); 682110119Sphk KASSERT(dp->d_name != NULL, ("disk_create need d_name")); 683110119Sphk KASSERT(*dp->d_name != 0, ("disk_create need d_name")); 684110317Sphk KASSERT(strlen(dp->d_name) < SPECNAMELEN - 4, ("disk name too long")); 685120374Sphk if (dp->d_devstat == NULL) 686120374Sphk dp->d_devstat = devstat_new_entry(dp->d_name, dp->d_unit, 687120374Sphk dp->d_sectorsize, DEVSTAT_ALL_SUPPORTED, 688120374Sphk DEVSTAT_TYPE_DIRECT, DEVSTAT_PRIORITY_MAX); 689112989Sphk dp->d_geom = NULL; 690169287Spjd g_disk_ident_adjust(dp->d_ident, sizeof(dp->d_ident)); 691113937Sphk g_post_event(g_disk_create, dp, M_WAITOK, dp, NULL); 69292108Sphk} 69392108Sphk 69492108Sphkvoid 695111216Sphkdisk_destroy(struct disk *dp) 69692108Sphk{ 69792108Sphk 698112989Sphk g_cancel_event(dp); 699125975Sphk dp->d_destroyed = 1; 700131207Sphk if (dp->d_devstat != NULL) 701131207Sphk devstat_remove_entry(dp->d_devstat); 702131267Sphk g_post_event(g_disk_destroy, dp, M_WAITOK, NULL); 70392108Sphk} 70492108Sphk 705152565Sjdpvoid 706152565Sjdpdisk_gone(struct disk *dp) 707152565Sjdp{ 708152565Sjdp struct g_geom *gp; 709152565Sjdp struct g_provider *pp; 710152565Sjdp 711152565Sjdp gp = dp->d_geom; 712240822Spjd if (gp != NULL) { 713241022Spjd pp = LIST_FIRST(&gp->provider); 714241022Spjd if (pp != NULL) { 715241022Spjd KASSERT(LIST_NEXT(pp, provider) == NULL, 716241022Spjd ("geom %p has more than one provider", gp)); 717157619Smarcel g_wither_provider(pp, ENXIO); 718241022Spjd } 719240822Spjd } 720152565Sjdp} 721152565Sjdp 722223089Sgibbsvoid 723223089Sgibbsdisk_attr_changed(struct disk *dp, const char *attr, int flag) 724223089Sgibbs{ 725223089Sgibbs struct g_geom *gp; 726223089Sgibbs struct g_provider *pp; 727223089Sgibbs 728223089Sgibbs gp = dp->d_geom; 729223089Sgibbs if (gp != NULL) 730223089Sgibbs LIST_FOREACH(pp, &gp->provider, provider) 731223089Sgibbs (void)g_attr_changed(pp, attr, flag); 732223089Sgibbs} 733223089Sgibbs 734238216Straszvoid 735238886Smavdisk_media_changed(struct disk *dp, int flag) 736238886Smav{ 737238886Smav struct g_geom *gp; 738238886Smav struct g_provider *pp; 739238886Smav 740238886Smav gp = dp->d_geom; 741238886Smav if (gp != NULL) { 742249161Smav pp = LIST_FIRST(&gp->provider); 743249161Smav if (pp != NULL) { 744249161Smav KASSERT(LIST_NEXT(pp, provider) == NULL, 745249161Smav ("geom %p has more than one provider", gp)); 746238886Smav g_media_changed(pp, flag); 747249161Smav } 748238886Smav } 749238886Smav} 750238886Smav 751238886Smavvoid 752238886Smavdisk_media_gone(struct disk *dp, int flag) 753238886Smav{ 754238886Smav struct g_geom *gp; 755238886Smav struct g_provider *pp; 756238886Smav 757238886Smav gp = dp->d_geom; 758238886Smav if (gp != NULL) { 759249161Smav pp = LIST_FIRST(&gp->provider); 760249161Smav if (pp != NULL) { 761249161Smav KASSERT(LIST_NEXT(pp, provider) == NULL, 762249161Smav ("geom %p has more than one provider", gp)); 763238886Smav g_media_gone(pp, flag); 764249161Smav } 765238886Smav } 766238886Smav} 767238886Smav 768242322Straszint 769242322Straszdisk_resize(struct disk *dp, int flag) 770238216Strasz{ 771238216Strasz 772242322Strasz if (dp->d_destroyed || dp->d_geom == NULL) 773242322Strasz return (0); 774238216Strasz 775242322Strasz return (g_post_event(g_disk_resize, dp, flag, NULL)); 776238216Strasz} 777238216Strasz 778104451Sphkstatic void 779112988Sphkg_kern_disks(void *p, int flag __unused) 780104451Sphk{ 781104451Sphk struct sbuf *sb; 782104451Sphk struct g_geom *gp; 783104451Sphk char *sp; 784104451Sphk 785104451Sphk sb = p; 786104451Sphk sp = ""; 787104451Sphk g_topology_assert(); 788104451Sphk LIST_FOREACH(gp, &g_disk_class.geom, geom) { 789104451Sphk sbuf_printf(sb, "%s%s", sp, gp->name); 790104451Sphk sp = " "; 791104451Sphk } 792104451Sphk sbuf_finish(sb); 793104451Sphk} 794104451Sphk 795104451Sphkstatic int 796104451Sphksysctl_disks(SYSCTL_HANDLER_ARGS) 797104451Sphk{ 798104451Sphk int error; 799104451Sphk struct sbuf *sb; 800104451Sphk 801181463Sdes sb = sbuf_new_auto(); 802113940Sphk g_waitfor_event(g_kern_disks, sb, M_WAITOK, NULL); 803113937Sphk error = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb) + 1); 804104451Sphk sbuf_delete(sb); 805104451Sphk return error; 806104451Sphk} 807104451Sphk 808217915SmdfSYSCTL_PROC(_kern, OID_AUTO, disks, 809217915Smdf CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 0, 810104451Sphk sysctl_disks, "A", "names of available disks"); 811