geom_disk.c revision 238886
1296417Sdim/*-
2254721Semaste * Copyright (c) 2002 Poul-Henning Kamp
3254721Semaste * Copyright (c) 2002 Networks Associates Technology, Inc.
4254721Semaste * All rights reserved.
5254721Semaste *
6254721Semaste * This software was developed for the FreeBSD Project by Poul-Henning Kamp
7254721Semaste * and NAI Labs, the Security Research Division of Network Associates, Inc.
8254721Semaste * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
9254721Semaste * DARPA CHATS research program.
10254721Semaste *
11254721Semaste * Redistribution and use in source and binary forms, with or without
12254721Semaste * modification, are permitted provided that the following conditions
13254721Semaste * are met:
14254721Semaste * 1. Redistributions of source code must retain the above copyright
15254721Semaste *    notice, this list of conditions and the following disclaimer.
16254721Semaste * 2. Redistributions in binary form must reproduce the above copyright
17254721Semaste *    notice, this list of conditions and the following disclaimer in the
18254721Semaste *    documentation and/or other materials provided with the distribution.
19254721Semaste * 3. The names of the authors may not be used to endorse or promote
20254721Semaste *    products derived from this software without specific prior written
21254721Semaste *    permission.
22254721Semaste *
23254721Semaste * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
24254721Semaste * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25254721Semaste * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26254721Semaste * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
27296417Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28296417Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29254721Semaste * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30254721Semaste * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31254721Semaste * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32296417Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33254721Semaste * SUCH DAMAGE.
34296417Sdim */
35296417Sdim
36254721Semaste#include <sys/cdefs.h>
37296417Sdim__FBSDID("$FreeBSD: head/sys/geom/geom_disk.c 238886 2012-07-29 11:51:48Z mav $");
38296417Sdim
39254721Semaste#include "opt_geom.h"
40296417Sdim
41296417Sdim#include <sys/param.h>
42254721Semaste#include <sys/systm.h>
43296417Sdim#include <sys/kernel.h>
44296417Sdim#include <sys/sysctl.h>
45254721Semaste#include <sys/bio.h>
46296417Sdim#include <sys/ctype.h>
47296417Sdim#include <sys/fcntl.h>
48254721Semaste#include <sys/malloc.h>
49296417Sdim#include <sys/sbuf.h>
50254721Semaste#include <sys/sysctl.h>
51254721Semaste#include <sys/taskqueue.h>
52296417Sdim#include <sys/devicestat.h>
53296417Sdim#include <machine/md_var.h>
54254721Semaste
55296417Sdim#include <sys/lock.h>
56254721Semaste#include <sys/mutex.h>
57254721Semaste#include <geom/geom.h>
58254721Semaste#include <geom/geom_disk.h>
59296417Sdim#include <geom/geom_int.h>
60296417Sdim
61296417Sdim#include <dev/led/led.h>
62296417Sdim
63296417Sdimstruct g_disk_softc {
64296417Sdim	struct disk		*dp;
65254721Semaste	struct sysctl_ctx_list	sysctl_ctx;
66296417Sdim	struct sysctl_oid	*sysctl_tree;
67296417Sdim	char			led[64];
68254721Semaste	uint32_t		state;
69296417Sdim	struct task		resize_task;
70254721Semaste};
71254721Semaste
72254721Semastestatic struct mtx g_disk_done_mtx;
73254721Semaste
74254721Semastestatic g_access_t g_disk_access;
75254721Semastestatic g_init_t g_disk_init;
76254721Semastestatic g_fini_t g_disk_fini;
77296417Sdimstatic g_start_t g_disk_start;
78static g_ioctl_t g_disk_ioctl;
79static g_dumpconf_t g_disk_dumpconf;
80static g_provgone_t g_disk_providergone;
81
82static struct g_class g_disk_class = {
83	.name = "DISK",
84	.version = G_VERSION,
85	.init = g_disk_init,
86	.fini = g_disk_fini,
87	.start = g_disk_start,
88	.access = g_disk_access,
89	.ioctl = g_disk_ioctl,
90	.providergone = g_disk_providergone,
91	.dumpconf = g_disk_dumpconf,
92};
93
94SYSCTL_DECL(_kern_geom);
95static SYSCTL_NODE(_kern_geom, OID_AUTO, disk, CTLFLAG_RW, 0,
96    "GEOM_DISK stuff");
97
98static void
99g_disk_init(struct g_class *mp __unused)
100{
101
102	mtx_init(&g_disk_done_mtx, "g_disk_done", NULL, MTX_DEF);
103}
104
105static void
106g_disk_fini(struct g_class *mp __unused)
107{
108
109	mtx_destroy(&g_disk_done_mtx);
110}
111
112DECLARE_GEOM_CLASS(g_disk_class, g_disk);
113
114static void __inline
115g_disk_lock_giant(struct disk *dp)
116{
117
118	if (dp->d_flags & DISKFLAG_NEEDSGIANT)
119		mtx_lock(&Giant);
120}
121
122static void __inline
123g_disk_unlock_giant(struct disk *dp)
124{
125
126	if (dp->d_flags & DISKFLAG_NEEDSGIANT)
127		mtx_unlock(&Giant);
128}
129
130static int
131g_disk_access(struct g_provider *pp, int r, int w, int e)
132{
133	struct disk *dp;
134	struct g_disk_softc *sc;
135	int error;
136
137	g_trace(G_T_ACCESS, "g_disk_access(%s, %d, %d, %d)",
138	    pp->name, r, w, e);
139	g_topology_assert();
140	sc = pp->geom->softc;
141	if (sc == NULL || (dp = sc->dp) == NULL || dp->d_destroyed) {
142		/*
143		 * Allow decreasing access count even if disk is not
144		 * avaliable anymore.
145		 */
146		if (r <= 0 && w <= 0 && e <= 0)
147			return (0);
148		return (ENXIO);
149	}
150	r += pp->acr;
151	w += pp->acw;
152	e += pp->ace;
153	error = 0;
154	if ((pp->acr + pp->acw + pp->ace) == 0 && (r + w + e) > 0) {
155		if (dp->d_open != NULL) {
156			g_disk_lock_giant(dp);
157			error = dp->d_open(dp);
158			if (bootverbose && error != 0)
159				printf("Opened disk %s -> %d\n",
160				    pp->name, error);
161			g_disk_unlock_giant(dp);
162		}
163		pp->mediasize = dp->d_mediasize;
164		pp->sectorsize = dp->d_sectorsize;
165		if (dp->d_flags & DISKFLAG_CANDELETE)
166			pp->flags |= G_PF_CANDELETE;
167		else
168			pp->flags &= ~G_PF_CANDELETE;
169		pp->stripeoffset = dp->d_stripeoffset;
170		pp->stripesize = dp->d_stripesize;
171		dp->d_flags |= DISKFLAG_OPEN;
172		if (dp->d_maxsize == 0) {
173			printf("WARNING: Disk drive %s%d has no d_maxsize\n",
174			    dp->d_name, dp->d_unit);
175			dp->d_maxsize = DFLTPHYS;
176		}
177	} else if ((pp->acr + pp->acw + pp->ace) > 0 && (r + w + e) == 0) {
178		if (dp->d_close != NULL) {
179			g_disk_lock_giant(dp);
180			error = dp->d_close(dp);
181			if (error != 0)
182				printf("Closed disk %s -> %d\n",
183				    pp->name, error);
184			g_disk_unlock_giant(dp);
185		}
186		sc->state = G_STATE_ACTIVE;
187		if (sc->led[0] != 0)
188			led_set(sc->led, "0");
189		dp->d_flags &= ~DISKFLAG_OPEN;
190	}
191	return (error);
192}
193
194static void
195g_disk_kerneldump(struct bio *bp, struct disk *dp)
196{
197	struct g_kerneldump *gkd;
198	struct g_geom *gp;
199
200	gkd = (struct g_kerneldump*)bp->bio_data;
201	gp = bp->bio_to->geom;
202	g_trace(G_T_TOPOLOGY, "g_disk_kernedump(%s, %jd, %jd)",
203		gp->name, (intmax_t)gkd->offset, (intmax_t)gkd->length);
204	if (dp->d_dump == NULL) {
205		g_io_deliver(bp, ENODEV);
206		return;
207	}
208	gkd->di.dumper = dp->d_dump;
209	gkd->di.priv = dp;
210	gkd->di.blocksize = dp->d_sectorsize;
211	gkd->di.maxiosize = dp->d_maxsize;
212	gkd->di.mediaoffset = gkd->offset;
213	if ((gkd->offset + gkd->length) > dp->d_mediasize)
214		gkd->length = dp->d_mediasize - gkd->offset;
215	gkd->di.mediasize = gkd->length;
216	g_io_deliver(bp, 0);
217}
218
219static void
220g_disk_setstate(struct bio *bp, struct g_disk_softc *sc)
221{
222	const char *cmd;
223
224	memcpy(&sc->state, bp->bio_data, sizeof(sc->state));
225	if (sc->led[0] != 0) {
226		switch (sc->state) {
227		case G_STATE_FAILED:
228			cmd = "1";
229			break;
230		case G_STATE_REBUILD:
231			cmd = "f5";
232			break;
233		case G_STATE_RESYNC:
234			cmd = "f1";
235			break;
236		default:
237			cmd = "0";
238			break;
239		}
240		led_set(sc->led, cmd);
241	}
242	g_io_deliver(bp, 0);
243}
244
245static void
246g_disk_done(struct bio *bp)
247{
248	struct bio *bp2;
249	struct disk *dp;
250	struct g_disk_softc *sc;
251
252	/* See "notes" for why we need a mutex here */
253	/* XXX: will witness accept a mix of Giant/unGiant drivers here ? */
254	mtx_lock(&g_disk_done_mtx);
255	bp->bio_completed = bp->bio_length - bp->bio_resid;
256
257	bp2 = bp->bio_parent;
258	if (bp2->bio_error == 0)
259		bp2->bio_error = bp->bio_error;
260	bp2->bio_completed += bp->bio_completed;
261	if ((bp->bio_cmd & (BIO_READ|BIO_WRITE|BIO_DELETE)) != 0 &&
262	    (sc = bp2->bio_to->geom->softc) != NULL &&
263	    (dp = sc->dp) != NULL) {
264		devstat_end_transaction_bio(dp->d_devstat, bp);
265	}
266	g_destroy_bio(bp);
267	bp2->bio_inbed++;
268	if (bp2->bio_children == bp2->bio_inbed) {
269		bp2->bio_resid = bp2->bio_bcount - bp2->bio_completed;
270		g_io_deliver(bp2, bp2->bio_error);
271	}
272	mtx_unlock(&g_disk_done_mtx);
273}
274
275static int
276g_disk_ioctl(struct g_provider *pp, u_long cmd, void * data, int fflag, struct thread *td)
277{
278	struct g_geom *gp;
279	struct disk *dp;
280	struct g_disk_softc *sc;
281	int error;
282
283	gp = pp->geom;
284	sc = gp->softc;
285	dp = sc->dp;
286
287	if (dp->d_ioctl == NULL)
288		return (ENOIOCTL);
289	g_disk_lock_giant(dp);
290	error = dp->d_ioctl(dp, cmd, data, fflag, td);
291	g_disk_unlock_giant(dp);
292	return (error);
293}
294
295static void
296g_disk_start(struct bio *bp)
297{
298	struct bio *bp2, *bp3;
299	struct disk *dp;
300	struct g_disk_softc *sc;
301	int error;
302	off_t off;
303
304	sc = bp->bio_to->geom->softc;
305	if (sc == NULL || (dp = sc->dp) == NULL || dp->d_destroyed) {
306		g_io_deliver(bp, ENXIO);
307		return;
308	}
309	error = EJUSTRETURN;
310	switch(bp->bio_cmd) {
311	case BIO_DELETE:
312		if (!(dp->d_flags & DISKFLAG_CANDELETE)) {
313			error = EOPNOTSUPP;
314			break;
315		}
316		/* fall-through */
317	case BIO_READ:
318	case BIO_WRITE:
319		off = 0;
320		bp3 = NULL;
321		bp2 = g_clone_bio(bp);
322		if (bp2 == NULL) {
323			error = ENOMEM;
324			break;
325		}
326		do {
327			bp2->bio_offset += off;
328			bp2->bio_length -= off;
329			bp2->bio_data += off;
330			if (bp2->bio_length > dp->d_maxsize) {
331				/*
332				 * XXX: If we have a stripesize we should really
333				 * use it here.
334				 */
335				bp2->bio_length = dp->d_maxsize;
336				off += dp->d_maxsize;
337				/*
338				 * To avoid a race, we need to grab the next bio
339				 * before we schedule this one.  See "notes".
340				 */
341				bp3 = g_clone_bio(bp);
342				if (bp3 == NULL)
343					bp->bio_error = ENOMEM;
344			}
345			bp2->bio_done = g_disk_done;
346			bp2->bio_pblkno = bp2->bio_offset / dp->d_sectorsize;
347			bp2->bio_bcount = bp2->bio_length;
348			bp2->bio_disk = dp;
349			devstat_start_transaction_bio(dp->d_devstat, bp2);
350			g_disk_lock_giant(dp);
351			dp->d_strategy(bp2);
352			g_disk_unlock_giant(dp);
353			bp2 = bp3;
354			bp3 = NULL;
355		} while (bp2 != NULL);
356		break;
357	case BIO_GETATTR:
358		/* Give the driver a chance to override */
359		if (dp->d_getattr != NULL) {
360			if (bp->bio_disk == NULL)
361				bp->bio_disk = dp;
362			error = dp->d_getattr(bp);
363			if (error != -1)
364				break;
365			error = EJUSTRETURN;
366		}
367		if (g_handleattr_int(bp, "GEOM::candelete",
368		    (dp->d_flags & DISKFLAG_CANDELETE) != 0))
369			break;
370		else if (g_handleattr_int(bp, "GEOM::fwsectors",
371		    dp->d_fwsectors))
372			break;
373		else if (g_handleattr_int(bp, "GEOM::fwheads", dp->d_fwheads))
374			break;
375		else if (g_handleattr_off_t(bp, "GEOM::frontstuff", 0))
376			break;
377		else if (g_handleattr_str(bp, "GEOM::ident", dp->d_ident))
378			break;
379		else if (g_handleattr(bp, "GEOM::hba_vendor",
380		    &dp->d_hba_vendor, 2))
381			break;
382		else if (g_handleattr(bp, "GEOM::hba_device",
383		    &dp->d_hba_device, 2))
384			break;
385		else if (g_handleattr(bp, "GEOM::hba_subvendor",
386		    &dp->d_hba_subvendor, 2))
387			break;
388		else if (g_handleattr(bp, "GEOM::hba_subdevice",
389		    &dp->d_hba_subdevice, 2))
390			break;
391		else if (!strcmp(bp->bio_attribute, "GEOM::kerneldump"))
392			g_disk_kerneldump(bp, dp);
393		else if (!strcmp(bp->bio_attribute, "GEOM::setstate"))
394			g_disk_setstate(bp, sc);
395		else
396			error = ENOIOCTL;
397		break;
398	case BIO_FLUSH:
399		g_trace(G_T_TOPOLOGY, "g_disk_flushcache(%s)",
400		    bp->bio_to->name);
401		if (!(dp->d_flags & DISKFLAG_CANFLUSHCACHE)) {
402			error = EOPNOTSUPP;
403			break;
404		}
405		bp2 = g_clone_bio(bp);
406		if (bp2 == NULL) {
407			g_io_deliver(bp, ENOMEM);
408			return;
409		}
410		bp2->bio_done = g_disk_done;
411		bp2->bio_disk = dp;
412		g_disk_lock_giant(dp);
413		dp->d_strategy(bp2);
414		g_disk_unlock_giant(dp);
415		break;
416	default:
417		error = EOPNOTSUPP;
418		break;
419	}
420	if (error != EJUSTRETURN)
421		g_io_deliver(bp, error);
422	return;
423}
424
425static void
426g_disk_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, struct g_consumer *cp, struct g_provider *pp)
427{
428	struct disk *dp;
429	struct g_disk_softc *sc;
430
431	sc = gp->softc;
432	if (sc == NULL || (dp = sc->dp) == NULL)
433		return;
434	if (indent == NULL) {
435		sbuf_printf(sb, " hd %u", dp->d_fwheads);
436		sbuf_printf(sb, " sc %u", dp->d_fwsectors);
437		return;
438	}
439	if (pp != NULL) {
440		sbuf_printf(sb, "%s<fwheads>%u</fwheads>\n",
441		    indent, dp->d_fwheads);
442		sbuf_printf(sb, "%s<fwsectors>%u</fwsectors>\n",
443		    indent, dp->d_fwsectors);
444		sbuf_printf(sb, "%s<ident>%s</ident>\n", indent, dp->d_ident);
445		sbuf_printf(sb, "%s<descr>%s</descr>\n", indent, dp->d_descr);
446	}
447}
448
449static void
450g_disk_resize_task(void *context, int pending)
451{
452	struct g_geom *gp;
453	struct g_provider *pp;
454	struct disk *dp;
455	struct g_disk_softc *sc;
456
457	sc = (struct g_disk_softc *)context;
458	dp = sc->dp;
459	gp = dp->d_geom;
460
461	LIST_FOREACH(pp, &gp->provider, provider) {
462		if (pp->sectorsize != 0 &&
463		    pp->sectorsize != dp->d_sectorsize)
464			g_wither_provider(pp, ENXIO);
465		else
466			g_resize_provider(pp, dp->d_mediasize);
467	}
468}
469
470static void
471g_disk_create(void *arg, int flag)
472{
473	struct g_geom *gp;
474	struct g_provider *pp;
475	struct disk *dp;
476	struct g_disk_softc *sc;
477	char tmpstr[80];
478
479	if (flag == EV_CANCEL)
480		return;
481	g_topology_assert();
482	dp = arg;
483	sc = g_malloc(sizeof(*sc), M_WAITOK | M_ZERO);
484	sc->dp = dp;
485	gp = g_new_geomf(&g_disk_class, "%s%d", dp->d_name, dp->d_unit);
486	gp->softc = sc;
487	pp = g_new_providerf(gp, "%s", gp->name);
488	pp->mediasize = dp->d_mediasize;
489	pp->sectorsize = dp->d_sectorsize;
490	if (dp->d_flags & DISKFLAG_CANDELETE)
491		pp->flags |= G_PF_CANDELETE;
492	pp->stripeoffset = dp->d_stripeoffset;
493	pp->stripesize = dp->d_stripesize;
494	if (bootverbose)
495		printf("GEOM: new disk %s\n", gp->name);
496	sysctl_ctx_init(&sc->sysctl_ctx);
497	snprintf(tmpstr, sizeof(tmpstr), "GEOM disk %s", gp->name);
498	sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
499		SYSCTL_STATIC_CHILDREN(_kern_geom_disk), OID_AUTO, gp->name,
500		CTLFLAG_RD, 0, tmpstr);
501	if (sc->sysctl_tree != NULL) {
502		snprintf(tmpstr, sizeof(tmpstr),
503		    "kern.geom.disk.%s.led", gp->name);
504		TUNABLE_STR_FETCH(tmpstr, sc->led, sizeof(sc->led));
505		SYSCTL_ADD_STRING(&sc->sysctl_ctx,
506		    SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "led",
507		    CTLFLAG_RW | CTLFLAG_TUN, sc->led, sizeof(sc->led),
508		    "LED name");
509	}
510	TASK_INIT(&sc->resize_task, 0, g_disk_resize_task, sc);
511	pp->private = sc;
512	dp->d_geom = gp;
513	g_error_provider(pp, 0);
514}
515
516/*
517 * We get this callback after all of the consumers have gone away, and just
518 * before the provider is freed.  If the disk driver provided a d_gone
519 * callback, let them know that it is okay to free resources -- they won't
520 * be getting any more accesses from GEOM.
521 */
522static void
523g_disk_providergone(struct g_provider *pp)
524{
525	struct disk *dp;
526	struct g_disk_softc *sc;
527
528	sc = (struct g_disk_softc *)pp->geom->softc;
529
530	/*
531	 * If the softc is already NULL, then we've probably been through
532	 * g_disk_destroy already; there is nothing for us to do anyway.
533	 */
534	if (sc == NULL)
535		return;
536
537	dp = sc->dp;
538
539	if (dp->d_gone != NULL)
540		dp->d_gone(dp);
541}
542
543static void
544g_disk_destroy(void *ptr, int flag)
545{
546	struct disk *dp;
547	struct g_geom *gp;
548	struct g_disk_softc *sc;
549
550	g_topology_assert();
551	dp = ptr;
552	gp = dp->d_geom;
553	if (gp != NULL) {
554		sc = gp->softc;
555		if (sc->sysctl_tree != NULL) {
556			sysctl_ctx_free(&sc->sysctl_ctx);
557			sc->sysctl_tree = NULL;
558		}
559		if (sc->led[0] != 0) {
560			led_set(sc->led, "0");
561			sc->led[0] = 0;
562		}
563		g_free(sc);
564		gp->softc = NULL;
565		g_wither_geom(gp, ENXIO);
566	}
567	g_free(dp);
568}
569
570/*
571 * We only allow printable characters in disk ident,
572 * the rest is converted to 'x<HH>'.
573 */
574static void
575g_disk_ident_adjust(char *ident, size_t size)
576{
577	char *p, tmp[4], newid[DISK_IDENT_SIZE];
578
579	newid[0] = '\0';
580	for (p = ident; *p != '\0'; p++) {
581		if (isprint(*p)) {
582			tmp[0] = *p;
583			tmp[1] = '\0';
584		} else {
585			snprintf(tmp, sizeof(tmp), "x%02hhx",
586			    *(unsigned char *)p);
587		}
588		if (strlcat(newid, tmp, sizeof(newid)) >= sizeof(newid))
589			break;
590	}
591	bzero(ident, size);
592	strlcpy(ident, newid, size);
593}
594
595struct disk *
596disk_alloc(void)
597{
598
599	return (g_malloc(sizeof(struct disk), M_WAITOK | M_ZERO));
600}
601
602void
603disk_create(struct disk *dp, int version)
604{
605
606	if (version != DISK_VERSION_02) {
607		printf("WARNING: Attempt to add disk %s%d %s",
608		    dp->d_name, dp->d_unit,
609		    " using incompatible ABI version of disk(9)\n");
610		printf("WARNING: Ignoring disk %s%d\n",
611		    dp->d_name, dp->d_unit);
612		return;
613	}
614	KASSERT(dp->d_strategy != NULL, ("disk_create need d_strategy"));
615	KASSERT(dp->d_name != NULL, ("disk_create need d_name"));
616	KASSERT(*dp->d_name != 0, ("disk_create need d_name"));
617	KASSERT(strlen(dp->d_name) < SPECNAMELEN - 4, ("disk name too long"));
618	if (dp->d_devstat == NULL)
619		dp->d_devstat = devstat_new_entry(dp->d_name, dp->d_unit,
620		    dp->d_sectorsize, DEVSTAT_ALL_SUPPORTED,
621		    DEVSTAT_TYPE_DIRECT, DEVSTAT_PRIORITY_MAX);
622	dp->d_geom = NULL;
623	g_disk_ident_adjust(dp->d_ident, sizeof(dp->d_ident));
624	g_post_event(g_disk_create, dp, M_WAITOK, dp, NULL);
625}
626
627void
628disk_destroy(struct disk *dp)
629{
630
631	g_cancel_event(dp);
632	dp->d_destroyed = 1;
633	if (dp->d_devstat != NULL)
634		devstat_remove_entry(dp->d_devstat);
635	g_post_event(g_disk_destroy, dp, M_WAITOK, NULL);
636}
637
638void
639disk_gone(struct disk *dp)
640{
641	struct g_geom *gp;
642	struct g_provider *pp;
643
644	gp = dp->d_geom;
645	if (gp != NULL)
646		LIST_FOREACH(pp, &gp->provider, provider)
647			g_wither_provider(pp, ENXIO);
648}
649
650void
651disk_attr_changed(struct disk *dp, const char *attr, int flag)
652{
653	struct g_geom *gp;
654	struct g_provider *pp;
655
656	gp = dp->d_geom;
657	if (gp != NULL)
658		LIST_FOREACH(pp, &gp->provider, provider)
659			(void)g_attr_changed(pp, attr, flag);
660}
661
662void
663disk_media_changed(struct disk *dp, int flag)
664{
665	struct g_geom *gp;
666	struct g_provider *pp;
667
668	gp = dp->d_geom;
669	if (gp != NULL) {
670		LIST_FOREACH(pp, &gp->provider, provider)
671			g_media_changed(pp, flag);
672	}
673}
674
675void
676disk_media_gone(struct disk *dp, int flag)
677{
678	struct g_geom *gp;
679	struct g_provider *pp;
680
681	gp = dp->d_geom;
682	if (gp != NULL) {
683		LIST_FOREACH(pp, &gp->provider, provider)
684			g_media_gone(pp, flag);
685	}
686}
687
688void
689disk_resize(struct disk *dp)
690{
691	struct g_geom *gp;
692	struct g_disk_softc *sc;
693	int error;
694
695	gp = dp->d_geom;
696
697	if (gp == NULL)
698		return;
699
700	sc = gp->softc;
701
702	error = taskqueue_enqueue(taskqueue_thread, &sc->resize_task);
703	KASSERT(error == 0, ("taskqueue_enqueue(9) failed."));
704}
705
706static void
707g_kern_disks(void *p, int flag __unused)
708{
709	struct sbuf *sb;
710	struct g_geom *gp;
711	char *sp;
712
713	sb = p;
714	sp = "";
715	g_topology_assert();
716	LIST_FOREACH(gp, &g_disk_class.geom, geom) {
717		sbuf_printf(sb, "%s%s", sp, gp->name);
718		sp = " ";
719	}
720	sbuf_finish(sb);
721}
722
723static int
724sysctl_disks(SYSCTL_HANDLER_ARGS)
725{
726	int error;
727	struct sbuf *sb;
728
729	sb = sbuf_new_auto();
730	g_waitfor_event(g_kern_disks, sb, M_WAITOK, NULL);
731	error = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb) + 1);
732	sbuf_delete(sb);
733	return error;
734}
735
736SYSCTL_PROC(_kern, OID_AUTO, disks,
737    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 0,
738    sysctl_disks, "A", "names of available disks");
739