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/11/sys/geom/geom_disk.c 356589 2020-01-10 00:55:37Z 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>
46298420Sasomers#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
62291716Sken#include <machine/bus.h>
63291716Sken
64219970Smavstruct g_disk_softc {
65248694Smav	struct mtx		 done_mtx;
66219970Smav	struct disk		*dp;
67356589Smav	struct devstat		*d_devstat;
68219970Smav	struct sysctl_ctx_list	sysctl_ctx;
69219970Smav	struct sysctl_oid	*sysctl_tree;
70219970Smav	char			led[64];
71219970Smav	uint32_t		state;
72256880Smav	struct mtx		 start_mtx;
73219970Smav};
74219970Smav
7592108Sphkstatic g_access_t g_disk_access;
76133314Sphkstatic g_start_t g_disk_start;
77133314Sphkstatic g_ioctl_t g_disk_ioctl;
78133314Sphkstatic g_dumpconf_t g_disk_dumpconf;
79237518Skenstatic g_provgone_t g_disk_providergone;
8092108Sphk
81141624Sphkstatic struct g_class g_disk_class = {
82249507Sivoras	.name = G_DISK_CLASS_NAME,
83133318Sphk	.version = G_VERSION,
84133314Sphk	.start = g_disk_start,
85133314Sphk	.access = g_disk_access,
86133314Sphk	.ioctl = g_disk_ioctl,
87237518Sken	.providergone = g_disk_providergone,
88133314Sphk	.dumpconf = g_disk_dumpconf,
8992108Sphk};
9092108Sphk
91219970SmavSYSCTL_DECL(_kern_geom);
92227309Sedstatic SYSCTL_NODE(_kern_geom, OID_AUTO, disk, CTLFLAG_RW, 0,
93227309Sed    "GEOM_DISK stuff");
94219970Smav
95115473SphkDECLARE_GEOM_CLASS(g_disk_class, g_disk);
96115473Sphk
9792108Sphkstatic int
9892108Sphkg_disk_access(struct g_provider *pp, int r, int w, int e)
9992108Sphk{
10092108Sphk	struct disk *dp;
101219970Smav	struct g_disk_softc *sc;
10292108Sphk	int error;
10392108Sphk
10492108Sphk	g_trace(G_T_ACCESS, "g_disk_access(%s, %d, %d, %d)",
10592108Sphk	    pp->name, r, w, e);
10692108Sphk	g_topology_assert();
107248694Smav	sc = pp->private;
108219970Smav	if (sc == NULL || (dp = sc->dp) == NULL || dp->d_destroyed) {
109125539Spjd		/*
110125539Spjd		 * Allow decreasing access count even if disk is not
111298808Spfg		 * available anymore.
112125539Spjd		 */
113125539Spjd		if (r <= 0 && w <= 0 && e <= 0)
114125539Spjd			return (0);
115125975Sphk		return (ENXIO);
116125539Spjd	}
11792108Sphk	r += pp->acr;
11892108Sphk	w += pp->acw;
11992108Sphk	e += pp->ace;
120110119Sphk	error = 0;
12192108Sphk	if ((pp->acr + pp->acw + pp->ace) == 0 && (r + w + e) > 0) {
122329316Savg		/*
123329316Savg		 * It would be better to defer this decision to d_open if
124329316Savg		 * it was able to take flags.
125329316Savg		 */
126329316Savg		if (w > 0 && (dp->d_flags & DISKFLAG_WRITE_PROTECT) != 0)
127329316Savg			error = EROFS;
128329316Savg		if (error == 0 && dp->d_open != NULL)
129111668Sphk			error = dp->d_open(dp);
130329316Savg		if (bootverbose && error != 0)
131329316Savg			printf("Opened disk %s -> %d\n", pp->name, error);
132329316Savg		if (error != 0)
133329316Savg			return (error);
134105551Sphk		pp->sectorsize = dp->d_sectorsize;
135110727Sphk		if (dp->d_maxsize == 0) {
136110727Sphk			printf("WARNING: Disk drive %s%d has no d_maxsize\n",
137110727Sphk			    dp->d_name, dp->d_unit);
138110727Sphk			dp->d_maxsize = DFLTPHYS;
139110727Sphk		}
140252657Ssmh		if (dp->d_delmaxsize == 0) {
141252657Ssmh			if (bootverbose && dp->d_flags & DISKFLAG_CANDELETE) {
142252657Ssmh				printf("WARNING: Disk drive %s%d has no "
143252657Ssmh				    "d_delmaxsize\n", dp->d_name, dp->d_unit);
144249940Ssmh			}
145252657Ssmh			dp->d_delmaxsize = dp->d_maxsize;
146249940Ssmh		}
147249940Ssmh		pp->stripeoffset = dp->d_stripeoffset;
148249940Ssmh		pp->stripesize = dp->d_stripesize;
149249940Ssmh		dp->d_flags |= DISKFLAG_OPEN;
150306476Sae		/*
151306476Sae		 * Do not invoke resize event when initial size was zero.
152306476Sae		 * Some disks report its size only after first opening.
153306476Sae		 */
154306476Sae		if (pp->mediasize == 0)
155306476Sae			pp->mediasize = dp->d_mediasize;
156306476Sae		else
157306476Sae			g_resize_provider(pp, dp->d_mediasize);
15892108Sphk	} else if ((pp->acr + pp->acw + pp->ace) > 0 && (r + w + e) == 0) {
159111668Sphk		if (dp->d_close != NULL) {
160111668Sphk			error = dp->d_close(dp);
161110119Sphk			if (error != 0)
162110119Sphk				printf("Closed disk %s -> %d\n",
163110119Sphk				    pp->name, error);
164110119Sphk		}
165219970Smav		sc->state = G_STATE_ACTIVE;
166219970Smav		if (sc->led[0] != 0)
167219970Smav			led_set(sc->led, "0");
168110119Sphk		dp->d_flags &= ~DISKFLAG_OPEN;
16992108Sphk	}
17092108Sphk	return (error);
17192108Sphk}
17292108Sphk
17392108Sphkstatic void
17495038Sphkg_disk_kerneldump(struct bio *bp, struct disk *dp)
175219950Smav{
17695038Sphk	struct g_kerneldump *gkd;
177104450Sphk	struct g_geom *gp;
17895038Sphk
17995038Sphk	gkd = (struct g_kerneldump*)bp->bio_data;
180104450Sphk	gp = bp->bio_to->geom;
181264320Sbdrewery	g_trace(G_T_TOPOLOGY, "g_disk_kerneldump(%s, %jd, %jd)",
182104450Sphk		gp->name, (intmax_t)gkd->offset, (intmax_t)gkd->length);
183120572Sphk	if (dp->d_dump == NULL) {
184120572Sphk		g_io_deliver(bp, ENODEV);
185120572Sphk		return;
186120572Sphk	}
187219950Smav	gkd->di.dumper = dp->d_dump;
188219950Smav	gkd->di.priv = dp;
189219950Smav	gkd->di.blocksize = dp->d_sectorsize;
190219950Smav	gkd->di.maxiosize = dp->d_maxsize;
191219950Smav	gkd->di.mediaoffset = gkd->offset;
192140968Sphk	if ((gkd->offset + gkd->length) > dp->d_mediasize)
193140968Sphk		gkd->length = dp->d_mediasize - gkd->offset;
194219950Smav	gkd->di.mediasize = gkd->length;
195219950Smav	g_io_deliver(bp, 0);
19695038Sphk}
19795038Sphk
19895038Sphkstatic void
199219970Smavg_disk_setstate(struct bio *bp, struct g_disk_softc *sc)
200219970Smav{
201219970Smav	const char *cmd;
202219970Smav
203219970Smav	memcpy(&sc->state, bp->bio_data, sizeof(sc->state));
204219970Smav	if (sc->led[0] != 0) {
205219970Smav		switch (sc->state) {
206219970Smav		case G_STATE_FAILED:
207219970Smav			cmd = "1";
208219970Smav			break;
209219970Smav		case G_STATE_REBUILD:
210219970Smav			cmd = "f5";
211219970Smav			break;
212219970Smav		case G_STATE_RESYNC:
213219970Smav			cmd = "f1";
214219970Smav			break;
215219970Smav		default:
216219970Smav			cmd = "0";
217219970Smav			break;
218219970Smav		}
219219970Smav		led_set(sc->led, cmd);
220219970Smav	}
221219970Smav	g_io_deliver(bp, 0);
222219970Smav}
223219970Smav
224219970Smavstatic void
22592108Sphkg_disk_done(struct bio *bp)
22692108Sphk{
227256603Smav	struct bintime now;
228111979Sphk	struct bio *bp2;
229219970Smav	struct g_disk_softc *sc;
23092108Sphk
231110720Sphk	/* See "notes" for why we need a mutex here */
232356589Smav	sc = bp->bio_caller1;
233248694Smav	bp2 = bp->bio_parent;
234256603Smav	binuptime(&now);
235248694Smav	mtx_lock(&sc->done_mtx);
236111979Sphk	if (bp2->bio_error == 0)
237111979Sphk		bp2->bio_error = bp->bio_error;
238356589Smav	bp2->bio_completed += bp->bio_length - bp->bio_resid;
239300207Sken
240296605Simp	switch (bp->bio_cmd) {
241300207Sken	case BIO_ZONE:
242300207Sken		bcopy(&bp->bio_zone, &bp2->bio_zone, sizeof(bp->bio_zone));
243300207Sken		/*FALLTHROUGH*/
244296605Simp	case BIO_READ:
245296605Simp	case BIO_WRITE:
246296605Simp	case BIO_DELETE:
247296605Simp	case BIO_FLUSH:
248356589Smav		devstat_end_transaction_bio_bt(sc->d_devstat, bp, &now);
249296605Simp		break;
250296605Simp	default:
251296605Simp		break;
252296605Simp	}
253111979Sphk	bp2->bio_inbed++;
254111979Sphk	if (bp2->bio_children == bp2->bio_inbed) {
255256606Smav		mtx_unlock(&sc->done_mtx);
256112259Sphk		bp2->bio_resid = bp2->bio_bcount - bp2->bio_completed;
257111979Sphk		g_io_deliver(bp2, bp2->bio_error);
258256606Smav	} else
259256606Smav		mtx_unlock(&sc->done_mtx);
260256606Smav	g_destroy_bio(bp);
26192108Sphk}
26292108Sphk
263119660Sphkstatic int
264138732Sphkg_disk_ioctl(struct g_provider *pp, u_long cmd, void * data, int fflag, struct thread *td)
265119660Sphk{
266119660Sphk	struct disk *dp;
267219970Smav	struct g_disk_softc *sc;
268119660Sphk	int error;
269119660Sphk
270248694Smav	sc = pp->private;
271219970Smav	dp = sc->dp;
272119660Sphk
273119660Sphk	if (dp->d_ioctl == NULL)
274119660Sphk		return (ENOIOCTL);
275138732Sphk	error = dp->d_ioctl(dp, cmd, data, fflag, td);
276226736Spjd	return (error);
277119660Sphk}
278119660Sphk
279291741Skenstatic off_t
280291741Skeng_disk_maxsize(struct disk *dp, struct bio *bp)
281291741Sken{
282291741Sken	if (bp->bio_cmd == BIO_DELETE)
283291741Sken		return (dp->d_delmaxsize);
284291741Sken	return (dp->d_maxsize);
285291741Sken}
286291741Sken
287291716Skenstatic int
288291741Skeng_disk_maxsegs(struct disk *dp, struct bio *bp)
289291716Sken{
290291741Sken	return ((g_disk_maxsize(dp, bp) / PAGE_SIZE) + 1);
291291716Sken}
292291716Sken
29392108Sphkstatic void
294291716Skeng_disk_advance(struct disk *dp, struct bio *bp, off_t off)
295291716Sken{
296291716Sken
297291716Sken	bp->bio_offset += off;
298291716Sken	bp->bio_length -= off;
299291716Sken
300291716Sken	if ((bp->bio_flags & BIO_VLIST) != 0) {
301291716Sken		bus_dma_segment_t *seg, *end;
302291716Sken
303291716Sken		seg = (bus_dma_segment_t *)bp->bio_data;
304291716Sken		end = (bus_dma_segment_t *)bp->bio_data + bp->bio_ma_n;
305291716Sken		off += bp->bio_ma_offset;
306291716Sken		while (off >= seg->ds_len) {
307291716Sken			KASSERT((seg != end),
308291716Sken			    ("vlist request runs off the end"));
309291716Sken			off -= seg->ds_len;
310291716Sken			seg++;
311291716Sken		}
312291716Sken		bp->bio_ma_offset = off;
313291716Sken		bp->bio_ma_n = end - seg;
314291716Sken		bp->bio_data = (void *)seg;
315291716Sken	} else if ((bp->bio_flags & BIO_UNMAPPED) != 0) {
316291716Sken		bp->bio_ma += off / PAGE_SIZE;
317291716Sken		bp->bio_ma_offset += off;
318291716Sken		bp->bio_ma_offset %= PAGE_SIZE;
319291716Sken		bp->bio_ma_n -= off / PAGE_SIZE;
320291716Sken	} else {
321291716Sken		bp->bio_data += off;
322291716Sken	}
323291716Sken}
324291716Sken
325291716Skenstatic void
326291716Skeng_disk_seg_limit(bus_dma_segment_t *seg, off_t *poffset,
327291716Sken    off_t *plength, int *ppages)
328291716Sken{
329291716Sken	uintptr_t seg_page_base;
330291716Sken	uintptr_t seg_page_end;
331291716Sken	off_t offset;
332291716Sken	off_t length;
333291716Sken	int seg_pages;
334291716Sken
335291716Sken	offset = *poffset;
336291716Sken	length = *plength;
337291716Sken
338291716Sken	if (length > seg->ds_len - offset)
339291716Sken		length = seg->ds_len - offset;
340291716Sken
341291716Sken	seg_page_base = trunc_page(seg->ds_addr + offset);
342291716Sken	seg_page_end  = round_page(seg->ds_addr + offset + length);
343291716Sken	seg_pages = (seg_page_end - seg_page_base) >> PAGE_SHIFT;
344291716Sken
345291716Sken	if (seg_pages > *ppages) {
346291716Sken		seg_pages = *ppages;
347291716Sken		length = (seg_page_base + (seg_pages << PAGE_SHIFT)) -
348291716Sken		    (seg->ds_addr + offset);
349291716Sken	}
350291716Sken
351291716Sken	*poffset = 0;
352291716Sken	*plength -= length;
353291716Sken	*ppages -= seg_pages;
354291716Sken}
355291716Sken
356291716Skenstatic off_t
357291716Skeng_disk_vlist_limit(struct disk *dp, struct bio *bp, bus_dma_segment_t **pendseg)
358291716Sken{
359291716Sken	bus_dma_segment_t *seg, *end;
360291716Sken	off_t residual;
361291716Sken	off_t offset;
362291716Sken	int pages;
363291716Sken
364291716Sken	seg = (bus_dma_segment_t *)bp->bio_data;
365291716Sken	end = (bus_dma_segment_t *)bp->bio_data + bp->bio_ma_n;
366291716Sken	residual = bp->bio_length;
367291716Sken	offset = bp->bio_ma_offset;
368291741Sken	pages = g_disk_maxsegs(dp, bp);
369291716Sken	while (residual != 0 && pages != 0) {
370291716Sken		KASSERT((seg != end),
371291716Sken		    ("vlist limit runs off the end"));
372291716Sken		g_disk_seg_limit(seg, &offset, &residual, &pages);
373291716Sken		seg++;
374291716Sken	}
375291716Sken	if (pendseg != NULL)
376291716Sken		*pendseg = seg;
377291716Sken	return (residual);
378291716Sken}
379291716Sken
380291716Skenstatic bool
381291716Skeng_disk_limit(struct disk *dp, struct bio *bp)
382291716Sken{
383291716Sken	bool limited = false;
384291742Sken	off_t maxsz;
385291716Sken
386291742Sken	maxsz = g_disk_maxsize(dp, bp);
387291742Sken
388291716Sken	/*
389291716Sken	 * XXX: If we have a stripesize we should really use it here.
390291716Sken	 *      Care should be taken in the delete case if this is done
391291716Sken	 *      as deletes can be very sensitive to size given how they
392291716Sken	 *      are processed.
393291716Sken	 */
394291741Sken	if (bp->bio_length > maxsz) {
395291741Sken		bp->bio_length = maxsz;
396291716Sken		limited = true;
397291716Sken	}
398291716Sken
399291716Sken	if ((bp->bio_flags & BIO_VLIST) != 0) {
400291716Sken		bus_dma_segment_t *firstseg, *endseg;
401291716Sken		off_t residual;
402291716Sken
403291716Sken		firstseg = (bus_dma_segment_t*)bp->bio_data;
404291716Sken		residual = g_disk_vlist_limit(dp, bp, &endseg);
405291716Sken		if (residual != 0) {
406291716Sken			bp->bio_ma_n = endseg - firstseg;
407291716Sken			bp->bio_length -= residual;
408291716Sken			limited = true;
409291716Sken		}
410291716Sken	} else if ((bp->bio_flags & BIO_UNMAPPED) != 0) {
411291716Sken		bp->bio_ma_n =
412291716Sken		    howmany(bp->bio_ma_offset + bp->bio_length, PAGE_SIZE);
413291716Sken	}
414291716Sken
415291716Sken	return (limited);
416291716Sken}
417291716Sken
418291716Skenstatic void
41992108Sphkg_disk_start(struct bio *bp)
42092108Sphk{
421110720Sphk	struct bio *bp2, *bp3;
42292108Sphk	struct disk *dp;
423219970Smav	struct g_disk_softc *sc;
42492403Sphk	int error;
425273638Smav	off_t off;
42692108Sphk
427248694Smav	sc = bp->bio_to->private;
428219970Smav	if (sc == NULL || (dp = sc->dp) == NULL || dp->d_destroyed) {
429115214Sphk		g_io_deliver(bp, ENXIO);
430143791Sphk		return;
431143791Sphk	}
432104609Sphk	error = EJUSTRETURN;
43392108Sphk	switch(bp->bio_cmd) {
434104609Sphk	case BIO_DELETE:
435110119Sphk		if (!(dp->d_flags & DISKFLAG_CANDELETE)) {
436226737Spjd			error = EOPNOTSUPP;
437104609Sphk			break;
438104609Sphk		}
439104609Sphk		/* fall-through */
44092108Sphk	case BIO_READ:
44192108Sphk	case BIO_WRITE:
442291716Sken		KASSERT((dp->d_flags & DISKFLAG_UNMAPPED_BIO) != 0 ||
443291716Sken		    (bp->bio_flags & BIO_UNMAPPED) == 0,
444291716Sken		    ("unmapped bio not supported by disk %s", dp->d_name));
445110720Sphk		off = 0;
446110720Sphk		bp3 = NULL;
44792108Sphk		bp2 = g_clone_bio(bp);
448110477Sphk		if (bp2 == NULL) {
449110477Sphk			error = ENOMEM;
450110477Sphk			break;
451110477Sphk		}
452291716Sken		for (;;) {
453291716Sken			if (g_disk_limit(dp, bp2)) {
454291716Sken				off += bp2->bio_length;
455273638Smav
456110720Sphk				/*
457110720Sphk				 * To avoid a race, we need to grab the next bio
458110720Sphk				 * before we schedule this one.  See "notes".
459110720Sphk				 */
460110720Sphk				bp3 = g_clone_bio(bp);
461110720Sphk				if (bp3 == NULL)
462110720Sphk					bp->bio_error = ENOMEM;
463110720Sphk			}
464110720Sphk			bp2->bio_done = g_disk_done;
465356589Smav			bp2->bio_caller1 = sc;
466110720Sphk			bp2->bio_pblkno = bp2->bio_offset / dp->d_sectorsize;
467110720Sphk			bp2->bio_bcount = bp2->bio_length;
468110720Sphk			bp2->bio_disk = dp;
469256880Smav			mtx_lock(&sc->start_mtx);
470150759Stegge			devstat_start_transaction_bio(dp->d_devstat, bp2);
471256880Smav			mtx_unlock(&sc->start_mtx);
472110720Sphk			dp->d_strategy(bp2);
473291716Sken
474291716Sken			if (bp3 == NULL)
475291716Sken				break;
476291716Sken
477110720Sphk			bp2 = bp3;
478110720Sphk			bp3 = NULL;
479291716Sken			g_disk_advance(dp, bp2, off);
480291716Sken		}
48192108Sphk		break;
48292108Sphk	case BIO_GETATTR:
483223089Sgibbs		/* Give the driver a chance to override */
484223089Sgibbs		if (dp->d_getattr != NULL) {
485223089Sgibbs			if (bp->bio_disk == NULL)
486223089Sgibbs				bp->bio_disk = dp;
487223089Sgibbs			error = dp->d_getattr(bp);
488223089Sgibbs			if (error != -1)
489223089Sgibbs				break;
490223089Sgibbs			error = EJUSTRETURN;
491223089Sgibbs		}
492216794Skib		if (g_handleattr_int(bp, "GEOM::candelete",
493216794Skib		    (dp->d_flags & DISKFLAG_CANDELETE) != 0))
49492403Sphk			break;
495216794Skib		else if (g_handleattr_int(bp, "GEOM::fwsectors",
496216794Skib		    dp->d_fwsectors))
497216794Skib			break;
498103714Sphk		else if (g_handleattr_int(bp, "GEOM::fwheads", dp->d_fwheads))
49992403Sphk			break;
50098066Sphk		else if (g_handleattr_off_t(bp, "GEOM::frontstuff", 0))
50194287Sphk			break;
502169285Spjd		else if (g_handleattr_str(bp, "GEOM::ident", dp->d_ident))
503169285Spjd			break;
504321290Smav		else if (g_handleattr_str(bp, "GEOM::descr", dp->d_descr))
505321290Smav			break;
506256956Ssmh		else if (g_handleattr_uint16_t(bp, "GEOM::hba_vendor",
507256956Ssmh		    dp->d_hba_vendor))
508210471Smav			break;
509256956Ssmh		else if (g_handleattr_uint16_t(bp, "GEOM::hba_device",
510256956Ssmh		    dp->d_hba_device))
511210471Smav			break;
512256956Ssmh		else if (g_handleattr_uint16_t(bp, "GEOM::hba_subvendor",
513256956Ssmh		    dp->d_hba_subvendor))
514210471Smav			break;
515256956Ssmh		else if (g_handleattr_uint16_t(bp, "GEOM::hba_subdevice",
516256956Ssmh		    dp->d_hba_subdevice))
517210471Smav			break;
51895038Sphk		else if (!strcmp(bp->bio_attribute, "GEOM::kerneldump"))
51995038Sphk			g_disk_kerneldump(bp, dp);
520219970Smav		else if (!strcmp(bp->bio_attribute, "GEOM::setstate"))
521219970Smav			g_disk_setstate(bp, sc);
522256956Ssmh		else if (g_handleattr_uint16_t(bp, "GEOM::rotation_rate",
523256956Ssmh		    dp->d_rotation_rate))
524256956Ssmh			break;
525119660Sphk		else
52692403Sphk			error = ENOIOCTL;
52792403Sphk		break;
528163833Spjd	case BIO_FLUSH:
529240629Savg		g_trace(G_T_BIO, "g_disk_flushcache(%s)",
530163833Spjd		    bp->bio_to->name);
531163833Spjd		if (!(dp->d_flags & DISKFLAG_CANFLUSHCACHE)) {
532226737Spjd			error = EOPNOTSUPP;
533226737Spjd			break;
534163833Spjd		}
535300207Sken		/*FALLTHROUGH*/
536300207Sken	case BIO_ZONE:
537300207Sken		if (bp->bio_cmd == BIO_ZONE) {
538300207Sken			if (!(dp->d_flags & DISKFLAG_CANZONE)) {
539300207Sken				error = EOPNOTSUPP;
540300207Sken				break;
541300207Sken			}
542300207Sken			g_trace(G_T_BIO, "g_disk_zone(%s)",
543300207Sken			    bp->bio_to->name);
544300207Sken		}
545273638Smav		bp2 = g_clone_bio(bp);
546273638Smav		if (bp2 == NULL) {
547273638Smav			g_io_deliver(bp, ENOMEM);
548273638Smav			return;
549273638Smav		}
550273638Smav		bp2->bio_done = g_disk_done;
551356589Smav		bp2->bio_caller1 = sc;
552273638Smav		bp2->bio_disk = dp;
553273638Smav		mtx_lock(&sc->start_mtx);
554273638Smav		devstat_start_transaction_bio(dp->d_devstat, bp2);
555273638Smav		mtx_unlock(&sc->start_mtx);
556273638Smav		dp->d_strategy(bp2);
557163833Spjd		break;
55892108Sphk	default:
55992403Sphk		error = EOPNOTSUPP;
56092403Sphk		break;
56192403Sphk	}
562104609Sphk	if (error != EJUSTRETURN)
563104195Sphk		g_io_deliver(bp, error);
56492403Sphk	return;
56592108Sphk}
56692108Sphk
567104936Sphkstatic void
568107953Sphkg_disk_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, struct g_consumer *cp, struct g_provider *pp)
569105537Sphk{
570251654Smav	struct bio *bp;
571105537Sphk	struct disk *dp;
572219970Smav	struct g_disk_softc *sc;
573251654Smav	char *buf;
574251654Smav	int res = 0;
575105537Sphk
576219970Smav	sc = gp->softc;
577219970Smav	if (sc == NULL || (dp = sc->dp) == NULL)
578120374Sphk		return;
579106101Sphk	if (indent == NULL) {
580106101Sphk		sbuf_printf(sb, " hd %u", dp->d_fwheads);
581106101Sphk		sbuf_printf(sb, " sc %u", dp->d_fwsectors);
582106101Sphk		return;
583106101Sphk	}
584105539Sphk	if (pp != NULL) {
585105537Sphk		sbuf_printf(sb, "%s<fwheads>%u</fwheads>\n",
586105537Sphk		    indent, dp->d_fwheads);
587105537Sphk		sbuf_printf(sb, "%s<fwsectors>%u</fwsectors>\n",
588105537Sphk		    indent, dp->d_fwsectors);
589294042Srpokala
590294042Srpokala		/*
591294042Srpokala		 * "rotationrate" is a little complicated, because the value
592294042Srpokala		 * returned by the drive might not be the RPM; 0 and 1 are
593294042Srpokala		 * special cases, and there's also a valid range.
594294042Srpokala		 */
595294042Srpokala		sbuf_printf(sb, "%s<rotationrate>", indent);
596312405Smav		if (dp->d_rotation_rate == DISK_RR_UNKNOWN) /* Old drives */
597312405Smav			sbuf_printf(sb, "unknown");	/* don't report RPM. */
598312405Smav		else if (dp->d_rotation_rate == DISK_RR_NON_ROTATING)
599312405Smav			sbuf_printf(sb, "0");
600312405Smav		else if ((dp->d_rotation_rate >= DISK_RR_MIN) &&
601312405Smav		    (dp->d_rotation_rate <= DISK_RR_MAX))
602294042Srpokala			sbuf_printf(sb, "%u", dp->d_rotation_rate);
603294042Srpokala		else
604294042Srpokala			sbuf_printf(sb, "invalid");
605294042Srpokala		sbuf_printf(sb, "</rotationrate>\n");
606251654Smav		if (dp->d_getattr != NULL) {
607251654Smav			buf = g_malloc(DISK_IDENT_SIZE, M_WAITOK);
608251654Smav			bp = g_alloc_bio();
609251654Smav			bp->bio_disk = dp;
610251654Smav			bp->bio_attribute = "GEOM::ident";
611251654Smav			bp->bio_length = DISK_IDENT_SIZE;
612251654Smav			bp->bio_data = buf;
613251654Smav			res = dp->d_getattr(bp);
614258683Smav			sbuf_printf(sb, "%s<ident>", indent);
615258683Smav			g_conf_printf_escaped(sb, "%s",
616251654Smav			    res == 0 ? buf: dp->d_ident);
617258683Smav			sbuf_printf(sb, "</ident>\n");
618251654Smav			bp->bio_attribute = "GEOM::lunid";
619251654Smav			bp->bio_length = DISK_IDENT_SIZE;
620251654Smav			bp->bio_data = buf;
621258683Smav			if (dp->d_getattr(bp) == 0) {
622258683Smav				sbuf_printf(sb, "%s<lunid>", indent);
623258683Smav				g_conf_printf_escaped(sb, "%s", buf);
624258683Smav				sbuf_printf(sb, "</lunid>\n");
625258683Smav			}
626254766Smav			bp->bio_attribute = "GEOM::lunname";
627254766Smav			bp->bio_length = DISK_IDENT_SIZE;
628254766Smav			bp->bio_data = buf;
629258683Smav			if (dp->d_getattr(bp) == 0) {
630258683Smav				sbuf_printf(sb, "%s<lunname>", indent);
631258683Smav				g_conf_printf_escaped(sb, "%s", buf);
632258683Smav				sbuf_printf(sb, "</lunname>\n");
633258683Smav			}
634251654Smav			g_destroy_bio(bp);
635251654Smav			g_free(buf);
636258683Smav		} else {
637258683Smav			sbuf_printf(sb, "%s<ident>", indent);
638258683Smav			g_conf_printf_escaped(sb, "%s", dp->d_ident);
639258683Smav			sbuf_printf(sb, "</ident>\n");
640258683Smav		}
641258683Smav		sbuf_printf(sb, "%s<descr>", indent);
642258683Smav		g_conf_printf_escaped(sb, "%s", dp->d_descr);
643258683Smav		sbuf_printf(sb, "</descr>\n");
644105537Sphk	}
645105537Sphk}
646105537Sphk
647105537Sphkstatic void
648242322Straszg_disk_resize(void *ptr, int flag)
649238216Strasz{
650242322Strasz	struct disk *dp;
651238216Strasz	struct g_geom *gp;
652238216Strasz	struct g_provider *pp;
653238216Strasz
654242322Strasz	if (flag == EV_CANCEL)
655242322Strasz		return;
656242322Strasz	g_topology_assert();
657242322Strasz
658242322Strasz	dp = ptr;
659238216Strasz	gp = dp->d_geom;
660238216Strasz
661242322Strasz	if (dp->d_destroyed || gp == NULL)
662242322Strasz		return;
663242322Strasz
664238216Strasz	LIST_FOREACH(pp, &gp->provider, provider) {
665238216Strasz		if (pp->sectorsize != 0 &&
666238216Strasz		    pp->sectorsize != dp->d_sectorsize)
667238216Strasz			g_wither_provider(pp, ENXIO);
668238216Strasz		else
669238216Strasz			g_resize_provider(pp, dp->d_mediasize);
670238216Strasz	}
671238216Strasz}
672238216Strasz
673238216Straszstatic void
674115309Sphkg_disk_create(void *arg, int flag)
67592108Sphk{
67692108Sphk	struct g_geom *gp;
67792108Sphk	struct g_provider *pp;
678110708Sphk	struct disk *dp;
679219970Smav	struct g_disk_softc *sc;
680219970Smav	char tmpstr[80];
68192108Sphk
682115309Sphk	if (flag == EV_CANCEL)
683115309Sphk		return;
684104936Sphk	g_topology_assert();
685110708Sphk	dp = arg;
686302069Sken
687302150Sken	mtx_pool_lock(mtxpool_sleep, dp);
688302069Sken	dp->d_init_level = DISK_INIT_START;
689302069Sken
690302069Sken	/*
691302069Sken	 * If the disk has already gone away, we can just stop here and
692302069Sken	 * call the user's callback to tell him we've cleaned things up.
693302069Sken	 */
694302069Sken	if (dp->d_goneflag != 0) {
695302150Sken		mtx_pool_unlock(mtxpool_sleep, dp);
696302069Sken		if (dp->d_gone != NULL)
697302069Sken			dp->d_gone(dp);
698302069Sken		return;
699302069Sken	}
700302150Sken	mtx_pool_unlock(mtxpool_sleep, dp);
701302069Sken
702219970Smav	sc = g_malloc(sizeof(*sc), M_WAITOK | M_ZERO);
703256880Smav	mtx_init(&sc->start_mtx, "g_disk_start", NULL, MTX_DEF);
704248694Smav	mtx_init(&sc->done_mtx, "g_disk_done", NULL, MTX_DEF);
705219970Smav	sc->dp = dp;
706356589Smav	sc->d_devstat = dp->d_devstat;
707110708Sphk	gp = g_new_geomf(&g_disk_class, "%s%d", dp->d_name, dp->d_unit);
708219970Smav	gp->softc = sc;
709104936Sphk	pp = g_new_providerf(gp, "%s", gp->name);
710256880Smav	devstat_remove_entry(pp->stat);
711256880Smav	pp->stat = NULL;
712256880Smav	dp->d_devstat->id = pp;
713110708Sphk	pp->mediasize = dp->d_mediasize;
714110708Sphk	pp->sectorsize = dp->d_sectorsize;
715110710Sphk	pp->stripeoffset = dp->d_stripeoffset;
716110710Sphk	pp->stripesize = dp->d_stripesize;
717248516Skib	if ((dp->d_flags & DISKFLAG_UNMAPPED_BIO) != 0)
718248516Skib		pp->flags |= G_PF_ACCEPT_UNMAPPED;
719256880Smav	if ((dp->d_flags & DISKFLAG_DIRECT_COMPLETION) != 0)
720256880Smav		pp->flags |= G_PF_DIRECT_SEND;
721256880Smav	pp->flags |= G_PF_DIRECT_RECEIVE;
722105957Sphk	if (bootverbose)
723105957Sphk		printf("GEOM: new disk %s\n", gp->name);
724219970Smav	sysctl_ctx_init(&sc->sysctl_ctx);
725219970Smav	snprintf(tmpstr, sizeof(tmpstr), "GEOM disk %s", gp->name);
726219970Smav	sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
727219970Smav		SYSCTL_STATIC_CHILDREN(_kern_geom_disk), OID_AUTO, gp->name,
728219970Smav		CTLFLAG_RD, 0, tmpstr);
729219970Smav	if (sc->sysctl_tree != NULL) {
730219970Smav		SYSCTL_ADD_STRING(&sc->sysctl_ctx,
731219970Smav		    SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "led",
732267992Shselasky		    CTLFLAG_RWTUN, sc->led, sizeof(sc->led),
733219970Smav		    "LED name");
734219970Smav	}
735219970Smav	pp->private = sc;
736112989Sphk	dp->d_geom = gp;
737112989Sphk	g_error_provider(pp, 0);
738302069Sken
739302150Sken	mtx_pool_lock(mtxpool_sleep, dp);
740302069Sken	dp->d_init_level = DISK_INIT_DONE;
741302069Sken
742302069Sken	/*
743302069Sken	 * If the disk has gone away at this stage, start the withering
744302069Sken	 * process for it.
745302069Sken	 */
746302069Sken	if (dp->d_goneflag != 0) {
747302150Sken		mtx_pool_unlock(mtxpool_sleep, dp);
748302069Sken		g_wither_provider(pp, ENXIO);
749302069Sken		return;
750302069Sken	}
751302150Sken	mtx_pool_unlock(mtxpool_sleep, dp);
752302069Sken
753104936Sphk}
754104936Sphk
755237518Sken/*
756237518Sken * We get this callback after all of the consumers have gone away, and just
757237518Sken * before the provider is freed.  If the disk driver provided a d_gone
758237518Sken * callback, let them know that it is okay to free resources -- they won't
759237518Sken * be getting any more accesses from GEOM.
760237518Sken */
761119652Sphkstatic void
762237518Skeng_disk_providergone(struct g_provider *pp)
763237518Sken{
764237518Sken	struct disk *dp;
765237518Sken	struct g_disk_softc *sc;
766237518Sken
767248694Smav	sc = (struct g_disk_softc *)pp->private;
768237518Sken	dp = sc->dp;
769248694Smav	if (dp != NULL && dp->d_gone != NULL)
770237518Sken		dp->d_gone(dp);
771248694Smav	if (sc->sysctl_tree != NULL) {
772248694Smav		sysctl_ctx_free(&sc->sysctl_ctx);
773248694Smav		sc->sysctl_tree = NULL;
774248694Smav	}
775248694Smav	if (sc->led[0] != 0) {
776248694Smav		led_set(sc->led, "0");
777248694Smav		sc->led[0] = 0;
778248694Smav	}
779248694Smav	pp->private = NULL;
780248694Smav	pp->geom->softc = NULL;
781248694Smav	mtx_destroy(&sc->done_mtx);
782256880Smav	mtx_destroy(&sc->start_mtx);
783248694Smav	g_free(sc);
784237518Sken}
785237518Sken
786237518Skenstatic void
787119652Sphkg_disk_destroy(void *ptr, int flag)
788119652Sphk{
789125975Sphk	struct disk *dp;
790119652Sphk	struct g_geom *gp;
791219970Smav	struct g_disk_softc *sc;
792104936Sphk
793119652Sphk	g_topology_assert();
794125975Sphk	dp = ptr;
795125975Sphk	gp = dp->d_geom;
796140261Sphk	if (gp != NULL) {
797219970Smav		sc = gp->softc;
798248694Smav		if (sc != NULL)
799248694Smav			sc->dp = NULL;
800248694Smav		dp->d_geom = NULL;
801140261Sphk		g_wither_geom(gp, ENXIO);
802140261Sphk	}
803302069Sken
804125975Sphk	g_free(dp);
805119652Sphk}
806104936Sphk
807169287Spjd/*
808196823Spjd * We only allow printable characters in disk ident,
809196823Spjd * the rest is converted to 'x<HH>'.
810169287Spjd */
811169287Spjdstatic void
812169287Spjdg_disk_ident_adjust(char *ident, size_t size)
813169287Spjd{
814196823Spjd	char *p, tmp[4], newid[DISK_IDENT_SIZE];
815169287Spjd
816196823Spjd	newid[0] = '\0';
817196823Spjd	for (p = ident; *p != '\0'; p++) {
818196823Spjd		if (isprint(*p)) {
819196823Spjd			tmp[0] = *p;
820196823Spjd			tmp[1] = '\0';
821196823Spjd		} else {
822196823Spjd			snprintf(tmp, sizeof(tmp), "x%02hhx",
823196823Spjd			    *(unsigned char *)p);
824196823Spjd		}
825196823Spjd		if (strlcat(newid, tmp, sizeof(newid)) >= sizeof(newid))
826169287Spjd			break;
827169287Spjd	}
828169287Spjd	bzero(ident, size);
829169287Spjd	strlcpy(ident, newid, size);
830169287Spjd}
831169287Spjd
832125975Sphkstruct disk *
833226735Spjddisk_alloc(void)
834125975Sphk{
835125975Sphk
836226735Spjd	return (g_malloc(sizeof(struct disk), M_WAITOK | M_ZERO));
837125975Sphk}
838125975Sphk
839111668Sphkvoid
840125975Sphkdisk_create(struct disk *dp, int version)
841104936Sphk{
842226736Spjd
843252657Ssmh	if (version != DISK_VERSION) {
844125975Sphk		printf("WARNING: Attempt to add disk %s%d %s",
845125975Sphk		    dp->d_name, dp->d_unit,
846125975Sphk		    " using incompatible ABI version of disk(9)\n");
847125975Sphk		printf("WARNING: Ignoring disk %s%d\n",
848125975Sphk		    dp->d_name, dp->d_unit);
849125975Sphk		return;
850125975Sphk	}
851257131Sjhb	if (dp->d_flags & DISKFLAG_RESERVED) {
852257131Sjhb		printf("WARNING: Attempt to add non-MPSAFE disk %s%d\n",
853257131Sjhb		    dp->d_name, dp->d_unit);
854257131Sjhb		printf("WARNING: Ignoring disk %s%d\n",
855257131Sjhb		    dp->d_name, dp->d_unit);
856257131Sjhb		return;
857257131Sjhb	}
858110119Sphk	KASSERT(dp->d_strategy != NULL, ("disk_create need d_strategy"));
859110119Sphk	KASSERT(dp->d_name != NULL, ("disk_create need d_name"));
860110119Sphk	KASSERT(*dp->d_name != 0, ("disk_create need d_name"));
861110317Sphk	KASSERT(strlen(dp->d_name) < SPECNAMELEN - 4, ("disk name too long"));
862120374Sphk	if (dp->d_devstat == NULL)
863120374Sphk		dp->d_devstat = devstat_new_entry(dp->d_name, dp->d_unit,
864120374Sphk		    dp->d_sectorsize, DEVSTAT_ALL_SUPPORTED,
865120374Sphk		    DEVSTAT_TYPE_DIRECT, DEVSTAT_PRIORITY_MAX);
866112989Sphk	dp->d_geom = NULL;
867302069Sken
868302069Sken	dp->d_init_level = DISK_INIT_NONE;
869302069Sken
870169287Spjd	g_disk_ident_adjust(dp->d_ident, sizeof(dp->d_ident));
871113937Sphk	g_post_event(g_disk_create, dp, M_WAITOK, dp, NULL);
87292108Sphk}
87392108Sphk
87492108Sphkvoid
875111216Sphkdisk_destroy(struct disk *dp)
87692108Sphk{
87792108Sphk
878112989Sphk	g_cancel_event(dp);
879125975Sphk	dp->d_destroyed = 1;
880131207Sphk	if (dp->d_devstat != NULL)
881131207Sphk		devstat_remove_entry(dp->d_devstat);
882131267Sphk	g_post_event(g_disk_destroy, dp, M_WAITOK, NULL);
88392108Sphk}
88492108Sphk
885152565Sjdpvoid
886152565Sjdpdisk_gone(struct disk *dp)
887152565Sjdp{
888152565Sjdp	struct g_geom *gp;
889152565Sjdp	struct g_provider *pp;
890152565Sjdp
891302150Sken	mtx_pool_lock(mtxpool_sleep, dp);
892302069Sken	dp->d_goneflag = 1;
893302069Sken
894302069Sken	/*
895302069Sken	 * If we're still in the process of creating this disk (the
896302069Sken	 * g_disk_create() function is still queued, or is in
897302069Sken	 * progress), the init level will not yet be DISK_INIT_DONE.
898302069Sken	 *
899302069Sken	 * If that is the case, g_disk_create() will see d_goneflag
900302069Sken	 * and take care of cleaning things up.
901302069Sken	 *
902302069Sken	 * If the disk has already been created, we default to
903302069Sken	 * withering the provider as usual below.
904302069Sken	 *
905302069Sken	 * If the caller has not set a d_gone() callback, he will
906302069Sken	 * not be any worse off by returning here, because the geom
907302069Sken	 * has not been fully setup in any case.
908302069Sken	 */
909302069Sken	if (dp->d_init_level < DISK_INIT_DONE) {
910302150Sken		mtx_pool_unlock(mtxpool_sleep, dp);
911302069Sken		return;
912302069Sken	}
913302150Sken	mtx_pool_unlock(mtxpool_sleep, dp);
914302069Sken
915152565Sjdp	gp = dp->d_geom;
916240822Spjd	if (gp != NULL) {
917241022Spjd		pp = LIST_FIRST(&gp->provider);
918241022Spjd		if (pp != NULL) {
919241022Spjd			KASSERT(LIST_NEXT(pp, provider) == NULL,
920241022Spjd			    ("geom %p has more than one provider", gp));
921157619Smarcel			g_wither_provider(pp, ENXIO);
922241022Spjd		}
923240822Spjd	}
924152565Sjdp}
925152565Sjdp
926223089Sgibbsvoid
927223089Sgibbsdisk_attr_changed(struct disk *dp, const char *attr, int flag)
928223089Sgibbs{
929223089Sgibbs	struct g_geom *gp;
930223089Sgibbs	struct g_provider *pp;
931298420Sasomers	char devnamebuf[128];
932223089Sgibbs
933223089Sgibbs	gp = dp->d_geom;
934223089Sgibbs	if (gp != NULL)
935223089Sgibbs		LIST_FOREACH(pp, &gp->provider, provider)
936223089Sgibbs			(void)g_attr_changed(pp, attr, flag);
937298439Sasomers	snprintf(devnamebuf, sizeof(devnamebuf), "devname=%s%d", dp->d_name,
938298439Sasomers	    dp->d_unit);
939298420Sasomers	devctl_notify("GEOM", "disk", attr, devnamebuf);
940223089Sgibbs}
941223089Sgibbs
942238216Straszvoid
943238886Smavdisk_media_changed(struct disk *dp, int flag)
944238886Smav{
945238886Smav	struct g_geom *gp;
946238886Smav	struct g_provider *pp;
947238886Smav
948238886Smav	gp = dp->d_geom;
949238886Smav	if (gp != NULL) {
950249161Smav		pp = LIST_FIRST(&gp->provider);
951249161Smav		if (pp != NULL) {
952249161Smav			KASSERT(LIST_NEXT(pp, provider) == NULL,
953249161Smav			    ("geom %p has more than one provider", gp));
954238886Smav			g_media_changed(pp, flag);
955249161Smav		}
956238886Smav	}
957238886Smav}
958238886Smav
959238886Smavvoid
960238886Smavdisk_media_gone(struct disk *dp, int flag)
961238886Smav{
962238886Smav	struct g_geom *gp;
963238886Smav	struct g_provider *pp;
964238886Smav
965238886Smav	gp = dp->d_geom;
966238886Smav	if (gp != NULL) {
967249161Smav		pp = LIST_FIRST(&gp->provider);
968249161Smav		if (pp != NULL) {
969249161Smav			KASSERT(LIST_NEXT(pp, provider) == NULL,
970249161Smav			    ("geom %p has more than one provider", gp));
971238886Smav			g_media_gone(pp, flag);
972249161Smav		}
973238886Smav	}
974238886Smav}
975238886Smav
976242322Straszint
977242322Straszdisk_resize(struct disk *dp, int flag)
978238216Strasz{
979238216Strasz
980242322Strasz	if (dp->d_destroyed || dp->d_geom == NULL)
981242322Strasz		return (0);
982238216Strasz
983242322Strasz	return (g_post_event(g_disk_resize, dp, flag, NULL));
984238216Strasz}
985238216Strasz
986104451Sphkstatic void
987112988Sphkg_kern_disks(void *p, int flag __unused)
988104451Sphk{
989104451Sphk	struct sbuf *sb;
990104451Sphk	struct g_geom *gp;
991104451Sphk	char *sp;
992104451Sphk
993104451Sphk	sb = p;
994104451Sphk	sp = "";
995104451Sphk	g_topology_assert();
996104451Sphk	LIST_FOREACH(gp, &g_disk_class.geom, geom) {
997104451Sphk		sbuf_printf(sb, "%s%s", sp, gp->name);
998104451Sphk		sp = " ";
999104451Sphk	}
1000104451Sphk	sbuf_finish(sb);
1001104451Sphk}
1002104451Sphk
1003104451Sphkstatic int
1004104451Sphksysctl_disks(SYSCTL_HANDLER_ARGS)
1005104451Sphk{
1006104451Sphk	int error;
1007104451Sphk	struct sbuf *sb;
1008104451Sphk
1009181463Sdes	sb = sbuf_new_auto();
1010113940Sphk	g_waitfor_event(g_kern_disks, sb, M_WAITOK, NULL);
1011113937Sphk	error = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb) + 1);
1012104451Sphk	sbuf_delete(sb);
1013104451Sphk	return error;
1014104451Sphk}
1015104451Sphk
1016217915SmdfSYSCTL_PROC(_kern, OID_AUTO, disks,
1017217915Smdf    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 0,
1018104451Sphk    sysctl_disks, "A", "names of available disks");
1019