geom_disk.c revision 251654
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 251654 2013-06-12 13:36:20Z mav $");
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			if (error != 0)
142				return (error);
143		}
144		pp->mediasize = dp->d_mediasize;
145		pp->sectorsize = dp->d_sectorsize;
146		if (dp->d_maxsize == 0) {
147			printf("WARNING: Disk drive %s%d has no d_maxsize\n",
148			    dp->d_name, dp->d_unit);
149			dp->d_maxsize = DFLTPHYS;
150		}
151		if (dp->d_flags & DISKFLAG_CANDELETE) {
152			if (bootverbose && dp->d_delmaxsize == 0) {
153				printf("WARNING: Disk drive %s%d has no d_delmaxsize\n",
154				    dp->d_name, dp->d_unit);
155				dp->d_delmaxsize = dp->d_maxsize;
156			}
157		} else {
158			dp->d_delmaxsize = 0;
159		}
160		pp->stripeoffset = dp->d_stripeoffset;
161		pp->stripesize = dp->d_stripesize;
162		dp->d_flags |= DISKFLAG_OPEN;
163	} else if ((pp->acr + pp->acw + pp->ace) > 0 && (r + w + e) == 0) {
164		if (dp->d_close != NULL) {
165			g_disk_lock_giant(dp);
166			error = dp->d_close(dp);
167			if (error != 0)
168				printf("Closed disk %s -> %d\n",
169				    pp->name, error);
170			g_disk_unlock_giant(dp);
171		}
172		sc->state = G_STATE_ACTIVE;
173		if (sc->led[0] != 0)
174			led_set(sc->led, "0");
175		dp->d_flags &= ~DISKFLAG_OPEN;
176	}
177	return (error);
178}
179
180static void
181g_disk_kerneldump(struct bio *bp, struct disk *dp)
182{
183	struct g_kerneldump *gkd;
184	struct g_geom *gp;
185
186	gkd = (struct g_kerneldump*)bp->bio_data;
187	gp = bp->bio_to->geom;
188	g_trace(G_T_TOPOLOGY, "g_disk_kernedump(%s, %jd, %jd)",
189		gp->name, (intmax_t)gkd->offset, (intmax_t)gkd->length);
190	if (dp->d_dump == NULL) {
191		g_io_deliver(bp, ENODEV);
192		return;
193	}
194	gkd->di.dumper = dp->d_dump;
195	gkd->di.priv = dp;
196	gkd->di.blocksize = dp->d_sectorsize;
197	gkd->di.maxiosize = dp->d_maxsize;
198	gkd->di.mediaoffset = gkd->offset;
199	if ((gkd->offset + gkd->length) > dp->d_mediasize)
200		gkd->length = dp->d_mediasize - gkd->offset;
201	gkd->di.mediasize = gkd->length;
202	g_io_deliver(bp, 0);
203}
204
205static void
206g_disk_setstate(struct bio *bp, struct g_disk_softc *sc)
207{
208	const char *cmd;
209
210	memcpy(&sc->state, bp->bio_data, sizeof(sc->state));
211	if (sc->led[0] != 0) {
212		switch (sc->state) {
213		case G_STATE_FAILED:
214			cmd = "1";
215			break;
216		case G_STATE_REBUILD:
217			cmd = "f5";
218			break;
219		case G_STATE_RESYNC:
220			cmd = "f1";
221			break;
222		default:
223			cmd = "0";
224			break;
225		}
226		led_set(sc->led, cmd);
227	}
228	g_io_deliver(bp, 0);
229}
230
231static void
232g_disk_done(struct bio *bp)
233{
234	struct bio *bp2;
235	struct g_disk_softc *sc;
236
237	/* See "notes" for why we need a mutex here */
238	/* XXX: will witness accept a mix of Giant/unGiant drivers here ? */
239	bp2 = bp->bio_parent;
240	sc = bp2->bio_to->private;
241	bp->bio_completed = bp->bio_length - bp->bio_resid;
242	mtx_lock(&sc->done_mtx);
243	if (bp2->bio_error == 0)
244		bp2->bio_error = bp->bio_error;
245	bp2->bio_completed += bp->bio_completed;
246	if ((bp->bio_cmd & (BIO_READ|BIO_WRITE|BIO_DELETE)) != 0)
247		devstat_end_transaction_bio(sc->dp->d_devstat, bp);
248	g_destroy_bio(bp);
249	bp2->bio_inbed++;
250	if (bp2->bio_children == bp2->bio_inbed) {
251		bp2->bio_resid = bp2->bio_bcount - bp2->bio_completed;
252		g_io_deliver(bp2, bp2->bio_error);
253	}
254	mtx_unlock(&sc->done_mtx);
255}
256
257static int
258g_disk_ioctl(struct g_provider *pp, u_long cmd, void * data, int fflag, struct thread *td)
259{
260	struct disk *dp;
261	struct g_disk_softc *sc;
262	int error;
263
264	sc = pp->private;
265	dp = sc->dp;
266
267	if (dp->d_ioctl == NULL)
268		return (ENOIOCTL);
269	g_disk_lock_giant(dp);
270	error = dp->d_ioctl(dp, cmd, data, fflag, td);
271	g_disk_unlock_giant(dp);
272	return (error);
273}
274
275static void
276g_disk_start(struct bio *bp)
277{
278	struct bio *bp2, *bp3;
279	struct disk *dp;
280	struct g_disk_softc *sc;
281	int error;
282	off_t off;
283
284	sc = bp->bio_to->private;
285	if (sc == NULL || (dp = sc->dp) == NULL || dp->d_destroyed) {
286		g_io_deliver(bp, ENXIO);
287		return;
288	}
289	error = EJUSTRETURN;
290	switch(bp->bio_cmd) {
291	case BIO_DELETE:
292		if (!(dp->d_flags & DISKFLAG_CANDELETE)) {
293			error = EOPNOTSUPP;
294			break;
295		}
296		/* fall-through */
297	case BIO_READ:
298	case BIO_WRITE:
299		off = 0;
300		bp3 = NULL;
301		bp2 = g_clone_bio(bp);
302		if (bp2 == NULL) {
303			error = ENOMEM;
304			break;
305		}
306		do {
307			off_t d_maxsize;
308
309			d_maxsize = (bp->bio_cmd == BIO_DELETE) ?
310			    dp->d_delmaxsize : dp->d_maxsize;
311			bp2->bio_offset += off;
312			bp2->bio_length -= off;
313			if ((bp->bio_flags & BIO_UNMAPPED) == 0) {
314				bp2->bio_data += off;
315			} else {
316				KASSERT((dp->d_flags & DISKFLAG_UNMAPPED_BIO)
317				    != 0,
318				    ("unmapped bio not supported by disk %s",
319				    dp->d_name));
320				bp2->bio_ma += off / PAGE_SIZE;
321				bp2->bio_ma_offset += off;
322				bp2->bio_ma_offset %= PAGE_SIZE;
323				bp2->bio_ma_n -= off / PAGE_SIZE;
324			}
325			if (bp2->bio_length > d_maxsize) {
326				/*
327				 * XXX: If we have a stripesize we should really
328				 * use it here. Care should be taken in the delete
329				 * case if this is done as deletes can be very
330				 * sensitive to size given how they are processed.
331				 */
332				bp2->bio_length = d_maxsize;
333				if ((bp->bio_flags & BIO_UNMAPPED) != 0) {
334					bp2->bio_ma_n = howmany(
335					    bp2->bio_ma_offset +
336					    bp2->bio_length, PAGE_SIZE);
337				}
338				off += d_maxsize;
339				/*
340				 * To avoid a race, we need to grab the next bio
341				 * before we schedule this one.  See "notes".
342				 */
343				bp3 = g_clone_bio(bp);
344				if (bp3 == NULL)
345					bp->bio_error = ENOMEM;
346			}
347			bp2->bio_done = g_disk_done;
348			bp2->bio_pblkno = bp2->bio_offset / dp->d_sectorsize;
349			bp2->bio_bcount = bp2->bio_length;
350			bp2->bio_disk = dp;
351			devstat_start_transaction_bio(dp->d_devstat, bp2);
352			g_disk_lock_giant(dp);
353			dp->d_strategy(bp2);
354			g_disk_unlock_giant(dp);
355			bp2 = bp3;
356			bp3 = NULL;
357		} while (bp2 != NULL);
358		break;
359	case BIO_GETATTR:
360		/* Give the driver a chance to override */
361		if (dp->d_getattr != NULL) {
362			if (bp->bio_disk == NULL)
363				bp->bio_disk = dp;
364			error = dp->d_getattr(bp);
365			if (error != -1)
366				break;
367			error = EJUSTRETURN;
368		}
369		if (g_handleattr_int(bp, "GEOM::candelete",
370		    (dp->d_flags & DISKFLAG_CANDELETE) != 0))
371			break;
372		else if (g_handleattr_int(bp, "GEOM::fwsectors",
373		    dp->d_fwsectors))
374			break;
375		else if (g_handleattr_int(bp, "GEOM::fwheads", dp->d_fwheads))
376			break;
377		else if (g_handleattr_off_t(bp, "GEOM::frontstuff", 0))
378			break;
379		else if (g_handleattr_str(bp, "GEOM::ident", dp->d_ident))
380			break;
381		else if (g_handleattr(bp, "GEOM::hba_vendor",
382		    &dp->d_hba_vendor, 2))
383			break;
384		else if (g_handleattr(bp, "GEOM::hba_device",
385		    &dp->d_hba_device, 2))
386			break;
387		else if (g_handleattr(bp, "GEOM::hba_subvendor",
388		    &dp->d_hba_subvendor, 2))
389			break;
390		else if (g_handleattr(bp, "GEOM::hba_subdevice",
391		    &dp->d_hba_subdevice, 2))
392			break;
393		else if (!strcmp(bp->bio_attribute, "GEOM::kerneldump"))
394			g_disk_kerneldump(bp, dp);
395		else if (!strcmp(bp->bio_attribute, "GEOM::setstate"))
396			g_disk_setstate(bp, sc);
397		else
398			error = ENOIOCTL;
399		break;
400	case BIO_FLUSH:
401		g_trace(G_T_BIO, "g_disk_flushcache(%s)",
402		    bp->bio_to->name);
403		if (!(dp->d_flags & DISKFLAG_CANFLUSHCACHE)) {
404			error = EOPNOTSUPP;
405			break;
406		}
407		bp2 = g_clone_bio(bp);
408		if (bp2 == NULL) {
409			g_io_deliver(bp, ENOMEM);
410			return;
411		}
412		bp2->bio_done = g_disk_done;
413		bp2->bio_disk = dp;
414		g_disk_lock_giant(dp);
415		dp->d_strategy(bp2);
416		g_disk_unlock_giant(dp);
417		break;
418	default:
419		error = EOPNOTSUPP;
420		break;
421	}
422	if (error != EJUSTRETURN)
423		g_io_deliver(bp, error);
424	return;
425}
426
427static void
428g_disk_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, struct g_consumer *cp, struct g_provider *pp)
429{
430	struct bio *bp;
431	struct disk *dp;
432	struct g_disk_softc *sc;
433	char *buf;
434	int res = 0;
435
436	sc = gp->softc;
437	if (sc == NULL || (dp = sc->dp) == NULL)
438		return;
439	if (indent == NULL) {
440		sbuf_printf(sb, " hd %u", dp->d_fwheads);
441		sbuf_printf(sb, " sc %u", dp->d_fwsectors);
442		return;
443	}
444	if (pp != NULL) {
445		sbuf_printf(sb, "%s<fwheads>%u</fwheads>\n",
446		    indent, dp->d_fwheads);
447		sbuf_printf(sb, "%s<fwsectors>%u</fwsectors>\n",
448		    indent, dp->d_fwsectors);
449		if (dp->d_getattr != NULL) {
450			buf = g_malloc(DISK_IDENT_SIZE, M_WAITOK);
451			bp = g_alloc_bio();
452			bp->bio_disk = dp;
453			bp->bio_attribute = "GEOM::ident";
454			bp->bio_length = DISK_IDENT_SIZE;
455			bp->bio_data = buf;
456			res = dp->d_getattr(bp);
457			sbuf_printf(sb, "%s<ident>%s</ident>\n", indent,
458			    res == 0 ? buf: dp->d_ident);
459			bp->bio_attribute = "GEOM::lunid";
460			bp->bio_length = DISK_IDENT_SIZE;
461			bp->bio_data = buf;
462			if (dp->d_getattr(bp) == 0)
463				sbuf_printf(sb, "%s<lunid>%s</lunid>\n",
464				    indent, buf);
465			g_destroy_bio(bp);
466			g_free(buf);
467		} else
468			sbuf_printf(sb, "%s<ident>%s</ident>\n", indent,
469			    dp->d_ident);
470		sbuf_printf(sb, "%s<descr>%s</descr>\n", indent, dp->d_descr);
471	}
472}
473
474static void
475g_disk_resize(void *ptr, int flag)
476{
477	struct disk *dp;
478	struct g_geom *gp;
479	struct g_provider *pp;
480
481	if (flag == EV_CANCEL)
482		return;
483	g_topology_assert();
484
485	dp = ptr;
486	gp = dp->d_geom;
487
488	if (dp->d_destroyed || gp == NULL)
489		return;
490
491	LIST_FOREACH(pp, &gp->provider, provider) {
492		if (pp->sectorsize != 0 &&
493		    pp->sectorsize != dp->d_sectorsize)
494			g_wither_provider(pp, ENXIO);
495		else
496			g_resize_provider(pp, dp->d_mediasize);
497	}
498}
499
500static void
501g_disk_create(void *arg, int flag)
502{
503	struct g_geom *gp;
504	struct g_provider *pp;
505	struct disk *dp;
506	struct g_disk_softc *sc;
507	char tmpstr[80];
508
509	if (flag == EV_CANCEL)
510		return;
511	g_topology_assert();
512	dp = arg;
513	sc = g_malloc(sizeof(*sc), M_WAITOK | M_ZERO);
514	mtx_init(&sc->done_mtx, "g_disk_done", NULL, MTX_DEF);
515	sc->dp = dp;
516	gp = g_new_geomf(&g_disk_class, "%s%d", dp->d_name, dp->d_unit);
517	gp->softc = sc;
518	pp = g_new_providerf(gp, "%s", gp->name);
519	pp->mediasize = dp->d_mediasize;
520	pp->sectorsize = dp->d_sectorsize;
521	pp->stripeoffset = dp->d_stripeoffset;
522	pp->stripesize = dp->d_stripesize;
523	if ((dp->d_flags & DISKFLAG_UNMAPPED_BIO) != 0)
524		pp->flags |= G_PF_ACCEPT_UNMAPPED;
525	if (bootverbose)
526		printf("GEOM: new disk %s\n", gp->name);
527	sysctl_ctx_init(&sc->sysctl_ctx);
528	snprintf(tmpstr, sizeof(tmpstr), "GEOM disk %s", gp->name);
529	sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
530		SYSCTL_STATIC_CHILDREN(_kern_geom_disk), OID_AUTO, gp->name,
531		CTLFLAG_RD, 0, tmpstr);
532	if (sc->sysctl_tree != NULL) {
533		snprintf(tmpstr, sizeof(tmpstr),
534		    "kern.geom.disk.%s.led", gp->name);
535		TUNABLE_STR_FETCH(tmpstr, sc->led, sizeof(sc->led));
536		SYSCTL_ADD_STRING(&sc->sysctl_ctx,
537		    SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "led",
538		    CTLFLAG_RW | CTLFLAG_TUN, sc->led, sizeof(sc->led),
539		    "LED name");
540	}
541	pp->private = sc;
542	dp->d_geom = gp;
543	g_error_provider(pp, 0);
544}
545
546/*
547 * We get this callback after all of the consumers have gone away, and just
548 * before the provider is freed.  If the disk driver provided a d_gone
549 * callback, let them know that it is okay to free resources -- they won't
550 * be getting any more accesses from GEOM.
551 */
552static void
553g_disk_providergone(struct g_provider *pp)
554{
555	struct disk *dp;
556	struct g_disk_softc *sc;
557
558	sc = (struct g_disk_softc *)pp->private;
559	dp = sc->dp;
560	if (dp != NULL && dp->d_gone != NULL)
561		dp->d_gone(dp);
562	if (sc->sysctl_tree != NULL) {
563		sysctl_ctx_free(&sc->sysctl_ctx);
564		sc->sysctl_tree = NULL;
565	}
566	if (sc->led[0] != 0) {
567		led_set(sc->led, "0");
568		sc->led[0] = 0;
569	}
570	pp->private = NULL;
571	pp->geom->softc = NULL;
572	mtx_destroy(&sc->done_mtx);
573	g_free(sc);
574}
575
576static void
577g_disk_destroy(void *ptr, int flag)
578{
579	struct disk *dp;
580	struct g_geom *gp;
581	struct g_disk_softc *sc;
582
583	g_topology_assert();
584	dp = ptr;
585	gp = dp->d_geom;
586	if (gp != NULL) {
587		sc = gp->softc;
588		if (sc != NULL)
589			sc->dp = NULL;
590		dp->d_geom = NULL;
591		g_wither_geom(gp, ENXIO);
592	}
593	g_free(dp);
594}
595
596/*
597 * We only allow printable characters in disk ident,
598 * the rest is converted to 'x<HH>'.
599 */
600static void
601g_disk_ident_adjust(char *ident, size_t size)
602{
603	char *p, tmp[4], newid[DISK_IDENT_SIZE];
604
605	newid[0] = '\0';
606	for (p = ident; *p != '\0'; p++) {
607		if (isprint(*p)) {
608			tmp[0] = *p;
609			tmp[1] = '\0';
610		} else {
611			snprintf(tmp, sizeof(tmp), "x%02hhx",
612			    *(unsigned char *)p);
613		}
614		if (strlcat(newid, tmp, sizeof(newid)) >= sizeof(newid))
615			break;
616	}
617	bzero(ident, size);
618	strlcpy(ident, newid, size);
619}
620
621struct disk *
622disk_alloc(void)
623{
624
625	return (g_malloc(sizeof(struct disk), M_WAITOK | M_ZERO));
626}
627
628void
629disk_create(struct disk *dp, int version)
630{
631
632	if (version != DISK_VERSION_02) {
633		printf("WARNING: Attempt to add disk %s%d %s",
634		    dp->d_name, dp->d_unit,
635		    " using incompatible ABI version of disk(9)\n");
636		printf("WARNING: Ignoring disk %s%d\n",
637		    dp->d_name, dp->d_unit);
638		return;
639	}
640	KASSERT(dp->d_strategy != NULL, ("disk_create need d_strategy"));
641	KASSERT(dp->d_name != NULL, ("disk_create need d_name"));
642	KASSERT(*dp->d_name != 0, ("disk_create need d_name"));
643	KASSERT(strlen(dp->d_name) < SPECNAMELEN - 4, ("disk name too long"));
644	if (dp->d_devstat == NULL)
645		dp->d_devstat = devstat_new_entry(dp->d_name, dp->d_unit,
646		    dp->d_sectorsize, DEVSTAT_ALL_SUPPORTED,
647		    DEVSTAT_TYPE_DIRECT, DEVSTAT_PRIORITY_MAX);
648	dp->d_geom = NULL;
649	g_disk_ident_adjust(dp->d_ident, sizeof(dp->d_ident));
650	g_post_event(g_disk_create, dp, M_WAITOK, dp, NULL);
651}
652
653void
654disk_destroy(struct disk *dp)
655{
656
657	g_cancel_event(dp);
658	dp->d_destroyed = 1;
659	if (dp->d_devstat != NULL)
660		devstat_remove_entry(dp->d_devstat);
661	g_post_event(g_disk_destroy, dp, M_WAITOK, NULL);
662}
663
664void
665disk_gone(struct disk *dp)
666{
667	struct g_geom *gp;
668	struct g_provider *pp;
669
670	gp = dp->d_geom;
671	if (gp != NULL) {
672		pp = LIST_FIRST(&gp->provider);
673		if (pp != NULL) {
674			KASSERT(LIST_NEXT(pp, provider) == NULL,
675			    ("geom %p has more than one provider", gp));
676			g_wither_provider(pp, ENXIO);
677		}
678	}
679}
680
681void
682disk_attr_changed(struct disk *dp, const char *attr, int flag)
683{
684	struct g_geom *gp;
685	struct g_provider *pp;
686
687	gp = dp->d_geom;
688	if (gp != NULL)
689		LIST_FOREACH(pp, &gp->provider, provider)
690			(void)g_attr_changed(pp, attr, flag);
691}
692
693void
694disk_media_changed(struct disk *dp, int flag)
695{
696	struct g_geom *gp;
697	struct g_provider *pp;
698
699	gp = dp->d_geom;
700	if (gp != NULL) {
701		pp = LIST_FIRST(&gp->provider);
702		if (pp != NULL) {
703			KASSERT(LIST_NEXT(pp, provider) == NULL,
704			    ("geom %p has more than one provider", gp));
705			g_media_changed(pp, flag);
706		}
707	}
708}
709
710void
711disk_media_gone(struct disk *dp, int flag)
712{
713	struct g_geom *gp;
714	struct g_provider *pp;
715
716	gp = dp->d_geom;
717	if (gp != NULL) {
718		pp = LIST_FIRST(&gp->provider);
719		if (pp != NULL) {
720			KASSERT(LIST_NEXT(pp, provider) == NULL,
721			    ("geom %p has more than one provider", gp));
722			g_media_gone(pp, flag);
723		}
724	}
725}
726
727int
728disk_resize(struct disk *dp, int flag)
729{
730
731	if (dp->d_destroyed || dp->d_geom == NULL)
732		return (0);
733
734	return (g_post_event(g_disk_resize, dp, flag, NULL));
735}
736
737static void
738g_kern_disks(void *p, int flag __unused)
739{
740	struct sbuf *sb;
741	struct g_geom *gp;
742	char *sp;
743
744	sb = p;
745	sp = "";
746	g_topology_assert();
747	LIST_FOREACH(gp, &g_disk_class.geom, geom) {
748		sbuf_printf(sb, "%s%s", sp, gp->name);
749		sp = " ";
750	}
751	sbuf_finish(sb);
752}
753
754static int
755sysctl_disks(SYSCTL_HANDLER_ARGS)
756{
757	int error;
758	struct sbuf *sb;
759
760	sb = sbuf_new_auto();
761	g_waitfor_event(g_kern_disks, sb, M_WAITOK, NULL);
762	error = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb) + 1);
763	sbuf_delete(sb);
764	return error;
765}
766
767SYSCTL_PROC(_kern, OID_AUTO, disks,
768    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 0,
769    sysctl_disks, "A", "names of available disks");
770