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 329319 2018-02-15 16:31:35Z avg $"); 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> 46300214Sasomers#include <sys/bus.h> 47196823Spjd#include <sys/ctype.h> 48110119Sphk#include <sys/fcntl.h> 4992108Sphk#include <sys/malloc.h> 50223921Sae#include <sys/sbuf.h> 51111979Sphk#include <sys/devicestat.h> 5292108Sphk#include <machine/md_var.h> 5392108Sphk 5492108Sphk#include <sys/lock.h> 5592108Sphk#include <sys/mutex.h> 5692108Sphk#include <geom/geom.h> 57112952Sphk#include <geom/geom_disk.h> 58112476Sphk#include <geom/geom_int.h> 5992108Sphk 60219970Smav#include <dev/led/led.h> 61219970Smav 62292348Sken#include <machine/bus.h> 63292348Sken 64219970Smavstruct g_disk_softc { 65248694Smav struct mtx done_mtx; 66219970Smav struct disk *dp; 67219970Smav struct sysctl_ctx_list sysctl_ctx; 68219970Smav struct sysctl_oid *sysctl_tree; 69219970Smav char led[64]; 70219970Smav uint32_t state; 71260385Sscottl struct mtx start_mtx; 72219970Smav}; 73219970Smav 7492108Sphkstatic g_access_t g_disk_access; 75133314Sphkstatic g_start_t g_disk_start; 76133314Sphkstatic g_ioctl_t g_disk_ioctl; 77133314Sphkstatic g_dumpconf_t g_disk_dumpconf; 78237518Skenstatic g_provgone_t g_disk_providergone; 7992108Sphk 80141624Sphkstatic struct g_class g_disk_class = { 81249507Sivoras .name = G_DISK_CLASS_NAME, 82133318Sphk .version = G_VERSION, 83133314Sphk .start = g_disk_start, 84133314Sphk .access = g_disk_access, 85133314Sphk .ioctl = g_disk_ioctl, 86237518Sken .providergone = g_disk_providergone, 87133314Sphk .dumpconf = g_disk_dumpconf, 8892108Sphk}; 8992108Sphk 90219970SmavSYSCTL_DECL(_kern_geom); 91227309Sedstatic SYSCTL_NODE(_kern_geom, OID_AUTO, disk, CTLFLAG_RW, 0, 92227309Sed "GEOM_DISK stuff"); 93219970Smav 94115473SphkDECLARE_GEOM_CLASS(g_disk_class, g_disk); 95115473Sphk 96110052Sphkstatic void __inline 97110052Sphkg_disk_lock_giant(struct disk *dp) 98110052Sphk{ 99226736Spjd 100125975Sphk if (dp->d_flags & DISKFLAG_NEEDSGIANT) 101125975Sphk mtx_lock(&Giant); 102110052Sphk} 103110052Sphk 104110052Sphkstatic void __inline 105110052Sphkg_disk_unlock_giant(struct disk *dp) 106110052Sphk{ 107226736Spjd 108125975Sphk if (dp->d_flags & DISKFLAG_NEEDSGIANT) 109125975Sphk mtx_unlock(&Giant); 110110052Sphk} 111110052Sphk 11292108Sphkstatic int 11392108Sphkg_disk_access(struct g_provider *pp, int r, int w, int e) 11492108Sphk{ 11592108Sphk struct disk *dp; 116219970Smav struct g_disk_softc *sc; 11792108Sphk int error; 11892108Sphk 11992108Sphk g_trace(G_T_ACCESS, "g_disk_access(%s, %d, %d, %d)", 12092108Sphk pp->name, r, w, e); 12192108Sphk g_topology_assert(); 122248694Smav sc = pp->private; 123219970Smav if (sc == NULL || (dp = sc->dp) == NULL || dp->d_destroyed) { 124125539Spjd /* 125125539Spjd * Allow decreasing access count even if disk is not 126125539Spjd * avaliable anymore. 127125539Spjd */ 128125539Spjd if (r <= 0 && w <= 0 && e <= 0) 129125539Spjd return (0); 130125975Sphk return (ENXIO); 131125539Spjd } 13292108Sphk r += pp->acr; 13392108Sphk w += pp->acw; 13492108Sphk e += pp->ace; 135110119Sphk error = 0; 13692108Sphk if ((pp->acr + pp->acw + pp->ace) == 0 && (r + w + e) > 0) { 137329319Savg /* 138329319Savg * It would be better to defer this decision to d_open if 139329319Savg * it was able to take flags. 140329319Savg */ 141329319Savg if (w > 0 && (dp->d_flags & DISKFLAG_WRITE_PROTECT) != 0) 142329319Savg error = EROFS; 143329319Savg if (error == 0 && dp->d_open != NULL) { 144110119Sphk g_disk_lock_giant(dp); 145111668Sphk error = dp->d_open(dp); 146110119Sphk g_disk_unlock_giant(dp); 147110119Sphk } 148329319Savg if (bootverbose && error != 0) 149329319Savg printf("Opened disk %s -> %d\n", pp->name, error); 150329319Savg if (error != 0) 151329319Savg return (error); 152105551Sphk pp->mediasize = dp->d_mediasize; 153105551Sphk pp->sectorsize = dp->d_sectorsize; 154110727Sphk if (dp->d_maxsize == 0) { 155110727Sphk printf("WARNING: Disk drive %s%d has no d_maxsize\n", 156110727Sphk dp->d_name, dp->d_unit); 157110727Sphk dp->d_maxsize = DFLTPHYS; 158110727Sphk } 159252657Ssmh if (dp->d_delmaxsize == 0) { 160252657Ssmh if (bootverbose && dp->d_flags & DISKFLAG_CANDELETE) { 161252657Ssmh printf("WARNING: Disk drive %s%d has no " 162252657Ssmh "d_delmaxsize\n", dp->d_name, dp->d_unit); 163249940Ssmh } 164252657Ssmh dp->d_delmaxsize = dp->d_maxsize; 165249940Ssmh } 166249940Ssmh pp->stripeoffset = dp->d_stripeoffset; 167249940Ssmh pp->stripesize = dp->d_stripesize; 168249940Ssmh dp->d_flags |= DISKFLAG_OPEN; 16992108Sphk } else if ((pp->acr + pp->acw + pp->ace) > 0 && (r + w + e) == 0) { 170111668Sphk if (dp->d_close != NULL) { 171110119Sphk g_disk_lock_giant(dp); 172111668Sphk error = dp->d_close(dp); 173110119Sphk if (error != 0) 174110119Sphk printf("Closed disk %s -> %d\n", 175110119Sphk pp->name, error); 176110119Sphk g_disk_unlock_giant(dp); 177110119Sphk } 178219970Smav sc->state = G_STATE_ACTIVE; 179219970Smav if (sc->led[0] != 0) 180219970Smav led_set(sc->led, "0"); 181110119Sphk dp->d_flags &= ~DISKFLAG_OPEN; 18292108Sphk } 18392108Sphk return (error); 18492108Sphk} 18592108Sphk 18692108Sphkstatic void 18795038Sphkg_disk_kerneldump(struct bio *bp, struct disk *dp) 188219950Smav{ 18995038Sphk struct g_kerneldump *gkd; 190104450Sphk struct g_geom *gp; 19195038Sphk 19295038Sphk gkd = (struct g_kerneldump*)bp->bio_data; 193104450Sphk gp = bp->bio_to->geom; 194264712Sbdrewery g_trace(G_T_TOPOLOGY, "g_disk_kerneldump(%s, %jd, %jd)", 195104450Sphk gp->name, (intmax_t)gkd->offset, (intmax_t)gkd->length); 196120572Sphk if (dp->d_dump == NULL) { 197120572Sphk g_io_deliver(bp, ENODEV); 198120572Sphk return; 199120572Sphk } 200219950Smav gkd->di.dumper = dp->d_dump; 201219950Smav gkd->di.priv = dp; 202219950Smav gkd->di.blocksize = dp->d_sectorsize; 203219950Smav gkd->di.maxiosize = dp->d_maxsize; 204219950Smav gkd->di.mediaoffset = gkd->offset; 205140968Sphk if ((gkd->offset + gkd->length) > dp->d_mediasize) 206140968Sphk gkd->length = dp->d_mediasize - gkd->offset; 207219950Smav gkd->di.mediasize = gkd->length; 208219950Smav g_io_deliver(bp, 0); 20995038Sphk} 21095038Sphk 21195038Sphkstatic void 212219970Smavg_disk_setstate(struct bio *bp, struct g_disk_softc *sc) 213219970Smav{ 214219970Smav const char *cmd; 215219970Smav 216219970Smav memcpy(&sc->state, bp->bio_data, sizeof(sc->state)); 217219970Smav if (sc->led[0] != 0) { 218219970Smav switch (sc->state) { 219219970Smav case G_STATE_FAILED: 220219970Smav cmd = "1"; 221219970Smav break; 222219970Smav case G_STATE_REBUILD: 223219970Smav cmd = "f5"; 224219970Smav break; 225219970Smav case G_STATE_RESYNC: 226219970Smav cmd = "f1"; 227219970Smav break; 228219970Smav default: 229219970Smav cmd = "0"; 230219970Smav break; 231219970Smav } 232219970Smav led_set(sc->led, cmd); 233219970Smav } 234219970Smav g_io_deliver(bp, 0); 235219970Smav} 236219970Smav 237219970Smavstatic void 23892108Sphkg_disk_done(struct bio *bp) 23992108Sphk{ 240260385Sscottl struct bintime now; 241111979Sphk struct bio *bp2; 242219970Smav struct g_disk_softc *sc; 24392108Sphk 244110720Sphk /* See "notes" for why we need a mutex here */ 245110720Sphk /* XXX: will witness accept a mix of Giant/unGiant drivers here ? */ 246248694Smav bp2 = bp->bio_parent; 247248694Smav sc = bp2->bio_to->private; 24892108Sphk bp->bio_completed = bp->bio_length - bp->bio_resid; 249260385Sscottl binuptime(&now); 250248694Smav mtx_lock(&sc->done_mtx); 251111979Sphk if (bp2->bio_error == 0) 252111979Sphk bp2->bio_error = bp->bio_error; 253111979Sphk bp2->bio_completed += bp->bio_completed; 254266608Smav if ((bp->bio_cmd & (BIO_READ|BIO_WRITE|BIO_DELETE|BIO_FLUSH)) != 0) 255260385Sscottl devstat_end_transaction_bio_bt(sc->dp->d_devstat, bp, &now); 256111979Sphk bp2->bio_inbed++; 257111979Sphk if (bp2->bio_children == bp2->bio_inbed) { 258260385Sscottl mtx_unlock(&sc->done_mtx); 259112259Sphk bp2->bio_resid = bp2->bio_bcount - bp2->bio_completed; 260111979Sphk g_io_deliver(bp2, bp2->bio_error); 261260385Sscottl } else 262260385Sscottl mtx_unlock(&sc->done_mtx); 263260385Sscottl g_destroy_bio(bp); 264260385Sscottl} 265260385Sscottl 266119660Sphkstatic int 267138732Sphkg_disk_ioctl(struct g_provider *pp, u_long cmd, void * data, int fflag, struct thread *td) 268119660Sphk{ 269119660Sphk struct disk *dp; 270219970Smav struct g_disk_softc *sc; 271119660Sphk int error; 272119660Sphk 273248694Smav sc = pp->private; 274219970Smav dp = sc->dp; 275119660Sphk 276119660Sphk if (dp->d_ioctl == NULL) 277119660Sphk return (ENOIOCTL); 278119660Sphk g_disk_lock_giant(dp); 279138732Sphk error = dp->d_ioctl(dp, cmd, data, fflag, td); 280119660Sphk g_disk_unlock_giant(dp); 281226736Spjd return (error); 282119660Sphk} 283119660Sphk 284292348Skenstatic off_t 285292348Skeng_disk_maxsize(struct disk *dp, struct bio *bp) 286292348Sken{ 287292348Sken if (bp->bio_cmd == BIO_DELETE) 288292348Sken return (dp->d_delmaxsize); 289292348Sken return (dp->d_maxsize); 290292348Sken} 291292348Sken 292292348Skenstatic int 293292348Skeng_disk_maxsegs(struct disk *dp, struct bio *bp) 294292348Sken{ 295292348Sken return ((g_disk_maxsize(dp, bp) / PAGE_SIZE) + 1); 296292348Sken} 297292348Sken 29892108Sphkstatic void 299292348Skeng_disk_advance(struct disk *dp, struct bio *bp, off_t off) 300292348Sken{ 301292348Sken 302292348Sken bp->bio_offset += off; 303292348Sken bp->bio_length -= off; 304292348Sken 305292348Sken if ((bp->bio_flags & BIO_VLIST) != 0) { 306292348Sken bus_dma_segment_t *seg, *end; 307292348Sken 308292348Sken seg = (bus_dma_segment_t *)bp->bio_data; 309292348Sken end = (bus_dma_segment_t *)bp->bio_data + bp->bio_ma_n; 310292348Sken off += bp->bio_ma_offset; 311292348Sken while (off >= seg->ds_len) { 312292348Sken KASSERT((seg != end), 313292348Sken ("vlist request runs off the end")); 314292348Sken off -= seg->ds_len; 315292348Sken seg++; 316292348Sken } 317292348Sken bp->bio_ma_offset = off; 318292348Sken bp->bio_ma_n = end - seg; 319292348Sken bp->bio_data = (void *)seg; 320292348Sken } else if ((bp->bio_flags & BIO_UNMAPPED) != 0) { 321292348Sken bp->bio_ma += off / PAGE_SIZE; 322292348Sken bp->bio_ma_offset += off; 323292348Sken bp->bio_ma_offset %= PAGE_SIZE; 324292348Sken bp->bio_ma_n -= off / PAGE_SIZE; 325292348Sken } else { 326292348Sken bp->bio_data += off; 327292348Sken } 328292348Sken} 329292348Sken 330292348Skenstatic void 331292348Skeng_disk_seg_limit(bus_dma_segment_t *seg, off_t *poffset, 332292348Sken off_t *plength, int *ppages) 333292348Sken{ 334292348Sken uintptr_t seg_page_base; 335292348Sken uintptr_t seg_page_end; 336292348Sken off_t offset; 337292348Sken off_t length; 338292348Sken int seg_pages; 339292348Sken 340292348Sken offset = *poffset; 341292348Sken length = *plength; 342292348Sken 343292348Sken if (length > seg->ds_len - offset) 344292348Sken length = seg->ds_len - offset; 345292348Sken 346292348Sken seg_page_base = trunc_page(seg->ds_addr + offset); 347292348Sken seg_page_end = round_page(seg->ds_addr + offset + length); 348292348Sken seg_pages = (seg_page_end - seg_page_base) >> PAGE_SHIFT; 349292348Sken 350292348Sken if (seg_pages > *ppages) { 351292348Sken seg_pages = *ppages; 352292348Sken length = (seg_page_base + (seg_pages << PAGE_SHIFT)) - 353292348Sken (seg->ds_addr + offset); 354292348Sken } 355292348Sken 356292348Sken *poffset = 0; 357292348Sken *plength -= length; 358292348Sken *ppages -= seg_pages; 359292348Sken} 360292348Sken 361292348Skenstatic off_t 362292348Skeng_disk_vlist_limit(struct disk *dp, struct bio *bp, bus_dma_segment_t **pendseg) 363292348Sken{ 364292348Sken bus_dma_segment_t *seg, *end; 365292348Sken off_t residual; 366292348Sken off_t offset; 367292348Sken int pages; 368292348Sken 369292348Sken seg = (bus_dma_segment_t *)bp->bio_data; 370292348Sken end = (bus_dma_segment_t *)bp->bio_data + bp->bio_ma_n; 371292348Sken residual = bp->bio_length; 372292348Sken offset = bp->bio_ma_offset; 373292348Sken pages = g_disk_maxsegs(dp, bp); 374292348Sken while (residual != 0 && pages != 0) { 375292348Sken KASSERT((seg != end), 376292348Sken ("vlist limit runs off the end")); 377292348Sken g_disk_seg_limit(seg, &offset, &residual, &pages); 378292348Sken seg++; 379292348Sken } 380292348Sken if (pendseg != NULL) 381292348Sken *pendseg = seg; 382292348Sken return (residual); 383292348Sken} 384292348Sken 385292348Skenstatic bool 386292348Skeng_disk_limit(struct disk *dp, struct bio *bp) 387292348Sken{ 388292348Sken bool limited = false; 389292348Sken off_t maxsz; 390292348Sken 391292348Sken maxsz = g_disk_maxsize(dp, bp); 392292348Sken 393292348Sken /* 394292348Sken * XXX: If we have a stripesize we should really use it here. 395292348Sken * Care should be taken in the delete case if this is done 396292348Sken * as deletes can be very sensitive to size given how they 397292348Sken * are processed. 398292348Sken */ 399292348Sken if (bp->bio_length > maxsz) { 400292348Sken bp->bio_length = maxsz; 401292348Sken limited = true; 402292348Sken } 403292348Sken 404292348Sken if ((bp->bio_flags & BIO_VLIST) != 0) { 405292348Sken bus_dma_segment_t *firstseg, *endseg; 406292348Sken off_t residual; 407292348Sken 408292348Sken firstseg = (bus_dma_segment_t*)bp->bio_data; 409292348Sken residual = g_disk_vlist_limit(dp, bp, &endseg); 410292348Sken if (residual != 0) { 411292348Sken bp->bio_ma_n = endseg - firstseg; 412292348Sken bp->bio_length -= residual; 413292348Sken limited = true; 414292348Sken } 415292348Sken } else if ((bp->bio_flags & BIO_UNMAPPED) != 0) { 416292348Sken bp->bio_ma_n = 417292348Sken howmany(bp->bio_ma_offset + bp->bio_length, PAGE_SIZE); 418292348Sken } 419292348Sken 420292348Sken return (limited); 421292348Sken} 422292348Sken 423292348Skenstatic void 42492108Sphkg_disk_start(struct bio *bp) 42592108Sphk{ 426110720Sphk struct bio *bp2, *bp3; 42792108Sphk struct disk *dp; 428219970Smav struct g_disk_softc *sc; 42992403Sphk int error; 430273767Smav off_t off; 43192108Sphk 432248694Smav sc = bp->bio_to->private; 433219970Smav if (sc == NULL || (dp = sc->dp) == NULL || dp->d_destroyed) { 434115214Sphk g_io_deliver(bp, ENXIO); 435143791Sphk return; 436143791Sphk } 437104609Sphk error = EJUSTRETURN; 43892108Sphk switch(bp->bio_cmd) { 439104609Sphk case BIO_DELETE: 440110119Sphk if (!(dp->d_flags & DISKFLAG_CANDELETE)) { 441226737Spjd error = EOPNOTSUPP; 442104609Sphk break; 443104609Sphk } 444104609Sphk /* fall-through */ 44592108Sphk case BIO_READ: 44692108Sphk case BIO_WRITE: 447292348Sken KASSERT((dp->d_flags & DISKFLAG_UNMAPPED_BIO) != 0 || 448292348Sken (bp->bio_flags & BIO_UNMAPPED) == 0, 449292348Sken ("unmapped bio not supported by disk %s", dp->d_name)); 450110720Sphk off = 0; 451110720Sphk bp3 = NULL; 45292108Sphk bp2 = g_clone_bio(bp); 453110477Sphk if (bp2 == NULL) { 454110477Sphk error = ENOMEM; 455110477Sphk break; 456110477Sphk } 457292348Sken for (;;) { 458292348Sken if (g_disk_limit(dp, bp2)) { 459292348Sken off += bp2->bio_length; 460273767Smav 461110720Sphk /* 462110720Sphk * To avoid a race, we need to grab the next bio 463110720Sphk * before we schedule this one. See "notes". 464110720Sphk */ 465110720Sphk bp3 = g_clone_bio(bp); 466110720Sphk if (bp3 == NULL) 467110720Sphk bp->bio_error = ENOMEM; 468110720Sphk } 469110720Sphk bp2->bio_done = g_disk_done; 470110720Sphk bp2->bio_pblkno = bp2->bio_offset / dp->d_sectorsize; 471110720Sphk bp2->bio_bcount = bp2->bio_length; 472110720Sphk bp2->bio_disk = dp; 473260385Sscottl mtx_lock(&sc->start_mtx); 474150759Stegge devstat_start_transaction_bio(dp->d_devstat, bp2); 475260385Sscottl mtx_unlock(&sc->start_mtx); 476110720Sphk g_disk_lock_giant(dp); 477110720Sphk dp->d_strategy(bp2); 478110720Sphk g_disk_unlock_giant(dp); 479292348Sken 480292348Sken if (bp3 == NULL) 481292348Sken break; 482292348Sken 483110720Sphk bp2 = bp3; 484110720Sphk bp3 = NULL; 485292348Sken g_disk_advance(dp, bp2, off); 486292348Sken } 48792108Sphk break; 48892108Sphk case BIO_GETATTR: 489223089Sgibbs /* Give the driver a chance to override */ 490223089Sgibbs if (dp->d_getattr != NULL) { 491223089Sgibbs if (bp->bio_disk == NULL) 492223089Sgibbs bp->bio_disk = dp; 493223089Sgibbs error = dp->d_getattr(bp); 494223089Sgibbs if (error != -1) 495223089Sgibbs break; 496223089Sgibbs error = EJUSTRETURN; 497223089Sgibbs } 498216794Skib if (g_handleattr_int(bp, "GEOM::candelete", 499216794Skib (dp->d_flags & DISKFLAG_CANDELETE) != 0)) 50092403Sphk break; 501216794Skib else if (g_handleattr_int(bp, "GEOM::fwsectors", 502216794Skib dp->d_fwsectors)) 503216794Skib break; 504103714Sphk else if (g_handleattr_int(bp, "GEOM::fwheads", dp->d_fwheads)) 50592403Sphk break; 50698066Sphk else if (g_handleattr_off_t(bp, "GEOM::frontstuff", 0)) 50794287Sphk break; 508169285Spjd else if (g_handleattr_str(bp, "GEOM::ident", dp->d_ident)) 509169285Spjd break; 510321291Smav else if (g_handleattr_str(bp, "GEOM::descr", dp->d_descr)) 511321291Smav break; 512271238Ssmh else if (g_handleattr_uint16_t(bp, "GEOM::hba_vendor", 513271238Ssmh dp->d_hba_vendor)) 514210471Smav break; 515271238Ssmh else if (g_handleattr_uint16_t(bp, "GEOM::hba_device", 516271238Ssmh dp->d_hba_device)) 517210471Smav break; 518271238Ssmh else if (g_handleattr_uint16_t(bp, "GEOM::hba_subvendor", 519271238Ssmh dp->d_hba_subvendor)) 520210471Smav break; 521271238Ssmh else if (g_handleattr_uint16_t(bp, "GEOM::hba_subdevice", 522271238Ssmh dp->d_hba_subdevice)) 523210471Smav break; 52495038Sphk else if (!strcmp(bp->bio_attribute, "GEOM::kerneldump")) 52595038Sphk g_disk_kerneldump(bp, dp); 526219970Smav else if (!strcmp(bp->bio_attribute, "GEOM::setstate")) 527219970Smav g_disk_setstate(bp, sc); 528271238Ssmh else if (!strcmp(bp->bio_attribute, "GEOM::rotation_rate")) { 529271238Ssmh uint64_t v; 530271238Ssmh 531271238Ssmh if ((dp->d_flags & DISKFLAG_LACKS_ROTRATE) == 0) 532271238Ssmh v = dp->d_rotation_rate; 533271238Ssmh else 534271238Ssmh v = 0; /* rate unknown */ 535271238Ssmh g_handleattr_uint16_t(bp, "GEOM::rotation_rate", v); 536271238Ssmh break; 537271238Ssmh } else 53892403Sphk error = ENOIOCTL; 53992403Sphk break; 540163833Spjd case BIO_FLUSH: 541240629Savg g_trace(G_T_BIO, "g_disk_flushcache(%s)", 542163833Spjd bp->bio_to->name); 543163833Spjd if (!(dp->d_flags & DISKFLAG_CANFLUSHCACHE)) { 544226737Spjd error = EOPNOTSUPP; 545226737Spjd break; 546163833Spjd } 547273767Smav bp2 = g_clone_bio(bp); 548273767Smav if (bp2 == NULL) { 549273767Smav g_io_deliver(bp, ENOMEM); 550273767Smav return; 551273767Smav } 552273767Smav bp2->bio_done = g_disk_done; 553273767Smav bp2->bio_disk = dp; 554273767Smav mtx_lock(&sc->start_mtx); 555273767Smav devstat_start_transaction_bio(dp->d_devstat, bp2); 556273767Smav mtx_unlock(&sc->start_mtx); 557163833Spjd g_disk_lock_giant(dp); 558273767Smav dp->d_strategy(bp2); 559163833Spjd g_disk_unlock_giant(dp); 560163833Spjd break; 56192108Sphk default: 56292403Sphk error = EOPNOTSUPP; 56392403Sphk break; 56492403Sphk } 565104609Sphk if (error != EJUSTRETURN) 566104195Sphk g_io_deliver(bp, error); 56792403Sphk return; 56892108Sphk} 56992108Sphk 570104936Sphkstatic void 571107953Sphkg_disk_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, struct g_consumer *cp, struct g_provider *pp) 572105537Sphk{ 573251654Smav struct bio *bp; 574105537Sphk struct disk *dp; 575219970Smav struct g_disk_softc *sc; 576251654Smav char *buf; 577251654Smav int res = 0; 578105537Sphk 579219970Smav sc = gp->softc; 580219970Smav if (sc == NULL || (dp = sc->dp) == NULL) 581120374Sphk return; 582106101Sphk if (indent == NULL) { 583106101Sphk sbuf_printf(sb, " hd %u", dp->d_fwheads); 584106101Sphk sbuf_printf(sb, " sc %u", dp->d_fwsectors); 585106101Sphk return; 586106101Sphk } 587105539Sphk if (pp != NULL) { 588105537Sphk sbuf_printf(sb, "%s<fwheads>%u</fwheads>\n", 589105537Sphk indent, dp->d_fwheads); 590105537Sphk sbuf_printf(sb, "%s<fwsectors>%u</fwsectors>\n", 591105537Sphk indent, dp->d_fwsectors); 592294483Srpokala 593294483Srpokala /* 594294483Srpokala * "rotationrate" is a little complicated, because the value 595294483Srpokala * returned by the drive might not be the RPM; 0 and 1 are 596294483Srpokala * special cases, and there's also a valid range. 597294483Srpokala */ 598294483Srpokala sbuf_printf(sb, "%s<rotationrate>", indent); 599312406Smav if (dp->d_rotation_rate == DISK_RR_UNKNOWN) /* Old drives */ 600312406Smav sbuf_printf(sb, "unknown"); /* don't report RPM. */ 601312406Smav else if (dp->d_rotation_rate == DISK_RR_NON_ROTATING) 602312406Smav sbuf_printf(sb, "0"); 603312406Smav else if ((dp->d_rotation_rate >= DISK_RR_MIN) && 604312406Smav (dp->d_rotation_rate <= DISK_RR_MAX)) 605294483Srpokala sbuf_printf(sb, "%u", dp->d_rotation_rate); 606294483Srpokala else 607294483Srpokala sbuf_printf(sb, "invalid"); 608294483Srpokala sbuf_printf(sb, "</rotationrate>\n"); 609251654Smav if (dp->d_getattr != NULL) { 610251654Smav buf = g_malloc(DISK_IDENT_SIZE, M_WAITOK); 611251654Smav bp = g_alloc_bio(); 612251654Smav bp->bio_disk = dp; 613251654Smav bp->bio_attribute = "GEOM::ident"; 614251654Smav bp->bio_length = DISK_IDENT_SIZE; 615251654Smav bp->bio_data = buf; 616251654Smav res = dp->d_getattr(bp); 617260479Smav sbuf_printf(sb, "%s<ident>", indent); 618260479Smav g_conf_printf_escaped(sb, "%s", 619251654Smav res == 0 ? buf: dp->d_ident); 620260479Smav sbuf_printf(sb, "</ident>\n"); 621251654Smav bp->bio_attribute = "GEOM::lunid"; 622251654Smav bp->bio_length = DISK_IDENT_SIZE; 623251654Smav bp->bio_data = buf; 624260479Smav if (dp->d_getattr(bp) == 0) { 625260479Smav sbuf_printf(sb, "%s<lunid>", indent); 626260479Smav g_conf_printf_escaped(sb, "%s", buf); 627260479Smav sbuf_printf(sb, "</lunid>\n"); 628260479Smav } 629254766Smav bp->bio_attribute = "GEOM::lunname"; 630254766Smav bp->bio_length = DISK_IDENT_SIZE; 631254766Smav bp->bio_data = buf; 632260479Smav if (dp->d_getattr(bp) == 0) { 633260479Smav sbuf_printf(sb, "%s<lunname>", indent); 634260479Smav g_conf_printf_escaped(sb, "%s", buf); 635260479Smav sbuf_printf(sb, "</lunname>\n"); 636260479Smav } 637251654Smav g_destroy_bio(bp); 638251654Smav g_free(buf); 639260479Smav } else { 640260479Smav sbuf_printf(sb, "%s<ident>", indent); 641260479Smav g_conf_printf_escaped(sb, "%s", dp->d_ident); 642260479Smav sbuf_printf(sb, "</ident>\n"); 643260479Smav } 644260479Smav sbuf_printf(sb, "%s<descr>", indent); 645260479Smav g_conf_printf_escaped(sb, "%s", dp->d_descr); 646260479Smav sbuf_printf(sb, "</descr>\n"); 647105537Sphk } 648105537Sphk} 649105537Sphk 650105537Sphkstatic void 651242322Straszg_disk_resize(void *ptr, int flag) 652238216Strasz{ 653242322Strasz struct disk *dp; 654238216Strasz struct g_geom *gp; 655238216Strasz struct g_provider *pp; 656238216Strasz 657242322Strasz if (flag == EV_CANCEL) 658242322Strasz return; 659242322Strasz g_topology_assert(); 660242322Strasz 661242322Strasz dp = ptr; 662238216Strasz gp = dp->d_geom; 663238216Strasz 664242322Strasz if (dp->d_destroyed || gp == NULL) 665242322Strasz return; 666242322Strasz 667238216Strasz LIST_FOREACH(pp, &gp->provider, provider) { 668238216Strasz if (pp->sectorsize != 0 && 669238216Strasz pp->sectorsize != dp->d_sectorsize) 670238216Strasz g_wither_provider(pp, ENXIO); 671238216Strasz else 672238216Strasz g_resize_provider(pp, dp->d_mediasize); 673238216Strasz } 674238216Strasz} 675238216Strasz 676238216Straszstatic void 677115309Sphkg_disk_create(void *arg, int flag) 67892108Sphk{ 67992108Sphk struct g_geom *gp; 68092108Sphk struct g_provider *pp; 681110708Sphk struct disk *dp; 682219970Smav struct g_disk_softc *sc; 683219970Smav char tmpstr[80]; 68492108Sphk 685115309Sphk if (flag == EV_CANCEL) 686115309Sphk return; 687104936Sphk g_topology_assert(); 688110708Sphk dp = arg; 689219970Smav sc = g_malloc(sizeof(*sc), M_WAITOK | M_ZERO); 690260385Sscottl mtx_init(&sc->start_mtx, "g_disk_start", NULL, MTX_DEF); 691248694Smav mtx_init(&sc->done_mtx, "g_disk_done", NULL, MTX_DEF); 692219970Smav sc->dp = dp; 693110708Sphk gp = g_new_geomf(&g_disk_class, "%s%d", dp->d_name, dp->d_unit); 694219970Smav gp->softc = sc; 695104936Sphk pp = g_new_providerf(gp, "%s", gp->name); 696260385Sscottl devstat_remove_entry(pp->stat); 697260385Sscottl pp->stat = NULL; 698260385Sscottl dp->d_devstat->id = pp; 699110708Sphk pp->mediasize = dp->d_mediasize; 700110708Sphk pp->sectorsize = dp->d_sectorsize; 701110710Sphk pp->stripeoffset = dp->d_stripeoffset; 702110710Sphk pp->stripesize = dp->d_stripesize; 703248516Skib if ((dp->d_flags & DISKFLAG_UNMAPPED_BIO) != 0) 704248516Skib pp->flags |= G_PF_ACCEPT_UNMAPPED; 705260385Sscottl if ((dp->d_flags & DISKFLAG_DIRECT_COMPLETION) != 0) 706260385Sscottl pp->flags |= G_PF_DIRECT_SEND; 707260385Sscottl pp->flags |= G_PF_DIRECT_RECEIVE; 708105957Sphk if (bootverbose) 709105957Sphk printf("GEOM: new disk %s\n", gp->name); 710219970Smav sysctl_ctx_init(&sc->sysctl_ctx); 711219970Smav snprintf(tmpstr, sizeof(tmpstr), "GEOM disk %s", gp->name); 712219970Smav sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx, 713219970Smav SYSCTL_STATIC_CHILDREN(_kern_geom_disk), OID_AUTO, gp->name, 714219970Smav CTLFLAG_RD, 0, tmpstr); 715219970Smav if (sc->sysctl_tree != NULL) { 716219970Smav snprintf(tmpstr, sizeof(tmpstr), 717219970Smav "kern.geom.disk.%s.led", gp->name); 718219970Smav TUNABLE_STR_FETCH(tmpstr, sc->led, sizeof(sc->led)); 719219970Smav SYSCTL_ADD_STRING(&sc->sysctl_ctx, 720219970Smav SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "led", 721219970Smav CTLFLAG_RW | CTLFLAG_TUN, sc->led, sizeof(sc->led), 722219970Smav "LED name"); 723219970Smav } 724219970Smav pp->private = sc; 725112989Sphk dp->d_geom = gp; 726112989Sphk g_error_provider(pp, 0); 727104936Sphk} 728104936Sphk 729237518Sken/* 730237518Sken * We get this callback after all of the consumers have gone away, and just 731237518Sken * before the provider is freed. If the disk driver provided a d_gone 732237518Sken * callback, let them know that it is okay to free resources -- they won't 733237518Sken * be getting any more accesses from GEOM. 734237518Sken */ 735119652Sphkstatic void 736237518Skeng_disk_providergone(struct g_provider *pp) 737237518Sken{ 738237518Sken struct disk *dp; 739237518Sken struct g_disk_softc *sc; 740237518Sken 741248694Smav sc = (struct g_disk_softc *)pp->private; 742237518Sken dp = sc->dp; 743248694Smav if (dp != NULL && dp->d_gone != NULL) 744237518Sken dp->d_gone(dp); 745248694Smav if (sc->sysctl_tree != NULL) { 746248694Smav sysctl_ctx_free(&sc->sysctl_ctx); 747248694Smav sc->sysctl_tree = NULL; 748248694Smav } 749248694Smav if (sc->led[0] != 0) { 750248694Smav led_set(sc->led, "0"); 751248694Smav sc->led[0] = 0; 752248694Smav } 753248694Smav pp->private = NULL; 754248694Smav pp->geom->softc = NULL; 755248694Smav mtx_destroy(&sc->done_mtx); 756260385Sscottl mtx_destroy(&sc->start_mtx); 757248694Smav g_free(sc); 758237518Sken} 759237518Sken 760237518Skenstatic void 761119652Sphkg_disk_destroy(void *ptr, int flag) 762119652Sphk{ 763125975Sphk struct disk *dp; 764119652Sphk struct g_geom *gp; 765219970Smav struct g_disk_softc *sc; 766104936Sphk 767119652Sphk g_topology_assert(); 768125975Sphk dp = ptr; 769125975Sphk gp = dp->d_geom; 770140261Sphk if (gp != NULL) { 771219970Smav sc = gp->softc; 772248694Smav if (sc != NULL) 773248694Smav sc->dp = NULL; 774248694Smav dp->d_geom = NULL; 775140261Sphk g_wither_geom(gp, ENXIO); 776140261Sphk } 777125975Sphk g_free(dp); 778119652Sphk} 779104936Sphk 780169287Spjd/* 781196823Spjd * We only allow printable characters in disk ident, 782196823Spjd * the rest is converted to 'x<HH>'. 783169287Spjd */ 784169287Spjdstatic void 785169287Spjdg_disk_ident_adjust(char *ident, size_t size) 786169287Spjd{ 787196823Spjd char *p, tmp[4], newid[DISK_IDENT_SIZE]; 788169287Spjd 789196823Spjd newid[0] = '\0'; 790196823Spjd for (p = ident; *p != '\0'; p++) { 791196823Spjd if (isprint(*p)) { 792196823Spjd tmp[0] = *p; 793196823Spjd tmp[1] = '\0'; 794196823Spjd } else { 795196823Spjd snprintf(tmp, sizeof(tmp), "x%02hhx", 796196823Spjd *(unsigned char *)p); 797196823Spjd } 798196823Spjd if (strlcat(newid, tmp, sizeof(newid)) >= sizeof(newid)) 799169287Spjd break; 800169287Spjd } 801169287Spjd bzero(ident, size); 802169287Spjd strlcpy(ident, newid, size); 803169287Spjd} 804169287Spjd 805125975Sphkstruct disk * 806226735Spjddisk_alloc(void) 807125975Sphk{ 808125975Sphk 809226735Spjd return (g_malloc(sizeof(struct disk), M_WAITOK | M_ZERO)); 810125975Sphk} 811125975Sphk 812111668Sphkvoid 813125975Sphkdisk_create(struct disk *dp, int version) 814104936Sphk{ 815226736Spjd 816252657Ssmh if (version != DISK_VERSION) { 817125975Sphk printf("WARNING: Attempt to add disk %s%d %s", 818125975Sphk dp->d_name, dp->d_unit, 819125975Sphk " using incompatible ABI version of disk(9)\n"); 820125975Sphk printf("WARNING: Ignoring disk %s%d\n", 821125975Sphk dp->d_name, dp->d_unit); 822125975Sphk return; 823125975Sphk } 824271238Ssmh if (version < DISK_VERSION_04) 825271238Ssmh dp->d_flags |= DISKFLAG_LACKS_ROTRATE; 826110119Sphk KASSERT(dp->d_strategy != NULL, ("disk_create need d_strategy")); 827110119Sphk KASSERT(dp->d_name != NULL, ("disk_create need d_name")); 828110119Sphk KASSERT(*dp->d_name != 0, ("disk_create need d_name")); 829110317Sphk KASSERT(strlen(dp->d_name) < SPECNAMELEN - 4, ("disk name too long")); 830120374Sphk if (dp->d_devstat == NULL) 831120374Sphk dp->d_devstat = devstat_new_entry(dp->d_name, dp->d_unit, 832120374Sphk dp->d_sectorsize, DEVSTAT_ALL_SUPPORTED, 833120374Sphk DEVSTAT_TYPE_DIRECT, DEVSTAT_PRIORITY_MAX); 834112989Sphk dp->d_geom = NULL; 835169287Spjd g_disk_ident_adjust(dp->d_ident, sizeof(dp->d_ident)); 836113937Sphk g_post_event(g_disk_create, dp, M_WAITOK, dp, NULL); 83792108Sphk} 83892108Sphk 83992108Sphkvoid 840111216Sphkdisk_destroy(struct disk *dp) 84192108Sphk{ 84292108Sphk 843112989Sphk g_cancel_event(dp); 844125975Sphk dp->d_destroyed = 1; 845131207Sphk if (dp->d_devstat != NULL) 846131207Sphk devstat_remove_entry(dp->d_devstat); 847131267Sphk g_post_event(g_disk_destroy, dp, M_WAITOK, NULL); 84892108Sphk} 84992108Sphk 850152565Sjdpvoid 851152565Sjdpdisk_gone(struct disk *dp) 852152565Sjdp{ 853152565Sjdp struct g_geom *gp; 854152565Sjdp struct g_provider *pp; 855152565Sjdp 856152565Sjdp gp = dp->d_geom; 857240822Spjd if (gp != NULL) { 858241022Spjd pp = LIST_FIRST(&gp->provider); 859241022Spjd if (pp != NULL) { 860241022Spjd KASSERT(LIST_NEXT(pp, provider) == NULL, 861241022Spjd ("geom %p has more than one provider", gp)); 862157619Smarcel g_wither_provider(pp, ENXIO); 863241022Spjd } 864240822Spjd } 865152565Sjdp} 866152565Sjdp 867223089Sgibbsvoid 868223089Sgibbsdisk_attr_changed(struct disk *dp, const char *attr, int flag) 869223089Sgibbs{ 870223089Sgibbs struct g_geom *gp; 871223089Sgibbs struct g_provider *pp; 872300214Sasomers char devnamebuf[128]; 873223089Sgibbs 874223089Sgibbs gp = dp->d_geom; 875223089Sgibbs if (gp != NULL) 876223089Sgibbs LIST_FOREACH(pp, &gp->provider, provider) 877223089Sgibbs (void)g_attr_changed(pp, attr, flag); 878300214Sasomers snprintf(devnamebuf, sizeof(devnamebuf), "devname=%s%d", dp->d_name, 879300214Sasomers dp->d_unit); 880300214Sasomers devctl_notify("GEOM", "disk", attr, devnamebuf); 881223089Sgibbs} 882223089Sgibbs 883238216Straszvoid 884238886Smavdisk_media_changed(struct disk *dp, int flag) 885238886Smav{ 886238886Smav struct g_geom *gp; 887238886Smav struct g_provider *pp; 888238886Smav 889238886Smav gp = dp->d_geom; 890238886Smav if (gp != NULL) { 891249161Smav pp = LIST_FIRST(&gp->provider); 892249161Smav if (pp != NULL) { 893249161Smav KASSERT(LIST_NEXT(pp, provider) == NULL, 894249161Smav ("geom %p has more than one provider", gp)); 895238886Smav g_media_changed(pp, flag); 896249161Smav } 897238886Smav } 898238886Smav} 899238886Smav 900238886Smavvoid 901238886Smavdisk_media_gone(struct disk *dp, int flag) 902238886Smav{ 903238886Smav struct g_geom *gp; 904238886Smav struct g_provider *pp; 905238886Smav 906238886Smav gp = dp->d_geom; 907238886Smav if (gp != NULL) { 908249161Smav pp = LIST_FIRST(&gp->provider); 909249161Smav if (pp != NULL) { 910249161Smav KASSERT(LIST_NEXT(pp, provider) == NULL, 911249161Smav ("geom %p has more than one provider", gp)); 912238886Smav g_media_gone(pp, flag); 913249161Smav } 914238886Smav } 915238886Smav} 916238886Smav 917242322Straszint 918242322Straszdisk_resize(struct disk *dp, int flag) 919238216Strasz{ 920238216Strasz 921242322Strasz if (dp->d_destroyed || dp->d_geom == NULL) 922242322Strasz return (0); 923238216Strasz 924242322Strasz return (g_post_event(g_disk_resize, dp, flag, NULL)); 925238216Strasz} 926238216Strasz 927104451Sphkstatic void 928112988Sphkg_kern_disks(void *p, int flag __unused) 929104451Sphk{ 930104451Sphk struct sbuf *sb; 931104451Sphk struct g_geom *gp; 932104451Sphk char *sp; 933104451Sphk 934104451Sphk sb = p; 935104451Sphk sp = ""; 936104451Sphk g_topology_assert(); 937104451Sphk LIST_FOREACH(gp, &g_disk_class.geom, geom) { 938104451Sphk sbuf_printf(sb, "%s%s", sp, gp->name); 939104451Sphk sp = " "; 940104451Sphk } 941104451Sphk sbuf_finish(sb); 942104451Sphk} 943104451Sphk 944104451Sphkstatic int 945104451Sphksysctl_disks(SYSCTL_HANDLER_ARGS) 946104451Sphk{ 947104451Sphk int error; 948104451Sphk struct sbuf *sb; 949104451Sphk 950181463Sdes sb = sbuf_new_auto(); 951113940Sphk g_waitfor_event(g_kern_disks, sb, M_WAITOK, NULL); 952113937Sphk error = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb) + 1); 953104451Sphk sbuf_delete(sb); 954104451Sphk return error; 955104451Sphk} 956104451Sphk 957217915SmdfSYSCTL_PROC(_kern, OID_AUTO, disks, 958217915Smdf CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 0, 959104451Sphk sysctl_disks, "A", "names of available disks"); 960