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