geom_disk.c revision 219970
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: head/sys/geom/geom_disk.c 219970 2011-03-24 19:23:42Z 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>
46196823Spjd#include <sys/ctype.h>
47110119Sphk#include <sys/fcntl.h>
4892108Sphk#include <sys/malloc.h>
4992108Sphk#include <sys/sysctl.h>
50111979Sphk#include <sys/devicestat.h>
5192108Sphk#include <machine/md_var.h>
5292108Sphk
5392108Sphk#include <sys/lock.h>
5492108Sphk#include <sys/mutex.h>
5592108Sphk#include <geom/geom.h>
56112952Sphk#include <geom/geom_disk.h>
57112476Sphk#include <geom/geom_int.h>
5892108Sphk
59219970Smav#include <dev/led/led.h>
60219970Smav
61219970Smavstruct g_disk_softc {
62219970Smav	struct disk		*dp;
63219970Smav	struct sysctl_ctx_list	sysctl_ctx;
64219970Smav	struct sysctl_oid	*sysctl_tree;
65219970Smav	char			led[64];
66219970Smav	uint32_t		state;
67219970Smav};
68219970Smav
69110720Sphkstatic struct mtx g_disk_done_mtx;
70110720Sphk
7192108Sphkstatic g_access_t g_disk_access;
72115473Sphkstatic g_init_t g_disk_init;
73115473Sphkstatic g_fini_t g_disk_fini;
74133314Sphkstatic g_start_t g_disk_start;
75133314Sphkstatic g_ioctl_t g_disk_ioctl;
76133314Sphkstatic g_dumpconf_t g_disk_dumpconf;
7792108Sphk
78141624Sphkstatic struct g_class g_disk_class = {
79112552Sphk	.name = "DISK",
80133318Sphk	.version = G_VERSION,
81115473Sphk	.init = g_disk_init,
82115473Sphk	.fini = g_disk_fini,
83133314Sphk	.start = g_disk_start,
84133314Sphk	.access = g_disk_access,
85133314Sphk	.ioctl = g_disk_ioctl,
86133314Sphk	.dumpconf = g_disk_dumpconf,
8792108Sphk};
8892108Sphk
89219970SmavSYSCTL_DECL(_kern_geom);
90219970SmavSYSCTL_NODE(_kern_geom, OID_AUTO, disk, CTLFLAG_RW, 0, "GEOM_DISK stuff");
91219970Smav
92110720Sphkstatic void
93115473Sphkg_disk_init(struct g_class *mp __unused)
94110720Sphk{
95115473Sphk
96123271Struckman	mtx_init(&g_disk_done_mtx, "g_disk_done", NULL, MTX_DEF);
97110720Sphk}
98104936Sphk
99115473Sphkstatic void
100115473Sphkg_disk_fini(struct g_class *mp __unused)
101115473Sphk{
102110720Sphk
103115473Sphk	mtx_destroy(&g_disk_done_mtx);
104115473Sphk}
105115473Sphk
106115473SphkDECLARE_GEOM_CLASS(g_disk_class, g_disk);
107115473Sphk
108110052Sphkstatic void __inline
109110052Sphkg_disk_lock_giant(struct disk *dp)
110110052Sphk{
111125975Sphk	if (dp->d_flags & DISKFLAG_NEEDSGIANT)
112125975Sphk		mtx_lock(&Giant);
113110052Sphk}
114110052Sphk
115110052Sphkstatic void __inline
116110052Sphkg_disk_unlock_giant(struct disk *dp)
117110052Sphk{
118125975Sphk	if (dp->d_flags & DISKFLAG_NEEDSGIANT)
119125975Sphk		mtx_unlock(&Giant);
120110052Sphk}
121110052Sphk
12292108Sphkstatic int
12392108Sphkg_disk_access(struct g_provider *pp, int r, int w, int e)
12492108Sphk{
12592108Sphk	struct disk *dp;
126219970Smav	struct g_disk_softc *sc;
12792108Sphk	int error;
12892108Sphk
12992108Sphk	g_trace(G_T_ACCESS, "g_disk_access(%s, %d, %d, %d)",
13092108Sphk	    pp->name, r, w, e);
13192108Sphk	g_topology_assert();
132219970Smav	sc = pp->geom->softc;
133219970Smav	if (sc == NULL || (dp = sc->dp) == NULL || dp->d_destroyed) {
134125539Spjd		/*
135125539Spjd		 * Allow decreasing access count even if disk is not
136125539Spjd		 * avaliable anymore.
137125539Spjd		 */
138125539Spjd		if (r <= 0 && w <= 0 && e <= 0)
139125539Spjd			return (0);
140125975Sphk		return (ENXIO);
141125539Spjd	}
14292108Sphk	r += pp->acr;
14392108Sphk	w += pp->acw;
14492108Sphk	e += pp->ace;
145110119Sphk	error = 0;
14692108Sphk	if ((pp->acr + pp->acw + pp->ace) == 0 && (r + w + e) > 0) {
147111668Sphk		if (dp->d_open != NULL) {
148110119Sphk			g_disk_lock_giant(dp);
149111668Sphk			error = dp->d_open(dp);
150166861Sn_hibma			if (bootverbose && error != 0)
151110119Sphk				printf("Opened disk %s -> %d\n",
152110119Sphk				    pp->name, error);
153110119Sphk			g_disk_unlock_giant(dp);
154110119Sphk		}
155105551Sphk		pp->mediasize = dp->d_mediasize;
156105551Sphk		pp->sectorsize = dp->d_sectorsize;
157110119Sphk		dp->d_flags |= DISKFLAG_OPEN;
158110727Sphk		if (dp->d_maxsize == 0) {
159110727Sphk			printf("WARNING: Disk drive %s%d has no d_maxsize\n",
160110727Sphk			    dp->d_name, dp->d_unit);
161110727Sphk			dp->d_maxsize = DFLTPHYS;
162110727Sphk		}
16392108Sphk	} else if ((pp->acr + pp->acw + pp->ace) > 0 && (r + w + e) == 0) {
164111668Sphk		if (dp->d_close != NULL) {
165110119Sphk			g_disk_lock_giant(dp);
166111668Sphk			error = dp->d_close(dp);
167110119Sphk			if (error != 0)
168110119Sphk				printf("Closed disk %s -> %d\n",
169110119Sphk				    pp->name, error);
170110119Sphk			g_disk_unlock_giant(dp);
171110119Sphk		}
172219970Smav		sc->state = G_STATE_ACTIVE;
173219970Smav		if (sc->led[0] != 0)
174219970Smav			led_set(sc->led, "0");
175110119Sphk		dp->d_flags &= ~DISKFLAG_OPEN;
17692108Sphk	}
17792108Sphk	return (error);
17892108Sphk}
17992108Sphk
18092108Sphkstatic void
18195038Sphkg_disk_kerneldump(struct bio *bp, struct disk *dp)
182219950Smav{
18395038Sphk	struct g_kerneldump *gkd;
184104450Sphk	struct g_geom *gp;
18595038Sphk
18695038Sphk	gkd = (struct g_kerneldump*)bp->bio_data;
187104450Sphk	gp = bp->bio_to->geom;
188104450Sphk	g_trace(G_T_TOPOLOGY, "g_disk_kernedump(%s, %jd, %jd)",
189104450Sphk		gp->name, (intmax_t)gkd->offset, (intmax_t)gkd->length);
190120572Sphk	if (dp->d_dump == NULL) {
191120572Sphk		g_io_deliver(bp, ENODEV);
192120572Sphk		return;
193120572Sphk	}
194219950Smav	gkd->di.dumper = dp->d_dump;
195219950Smav	gkd->di.priv = dp;
196219950Smav	gkd->di.blocksize = dp->d_sectorsize;
197219950Smav	gkd->di.maxiosize = dp->d_maxsize;
198219950Smav	gkd->di.mediaoffset = gkd->offset;
199140968Sphk	if ((gkd->offset + gkd->length) > dp->d_mediasize)
200140968Sphk		gkd->length = dp->d_mediasize - gkd->offset;
201219950Smav	gkd->di.mediasize = gkd->length;
202219950Smav	g_io_deliver(bp, 0);
20395038Sphk}
20495038Sphk
20595038Sphkstatic void
206219970Smavg_disk_setstate(struct bio *bp, struct g_disk_softc *sc)
207219970Smav{
208219970Smav	const char *cmd;
209219970Smav
210219970Smav	memcpy(&sc->state, bp->bio_data, sizeof(sc->state));
211219970Smav	if (sc->led[0] != 0) {
212219970Smav		switch (sc->state) {
213219970Smav		case G_STATE_FAILED:
214219970Smav			cmd = "1";
215219970Smav			break;
216219970Smav		case G_STATE_REBUILD:
217219970Smav			cmd = "f5";
218219970Smav			break;
219219970Smav		case G_STATE_RESYNC:
220219970Smav			cmd = "f1";
221219970Smav			break;
222219970Smav		default:
223219970Smav			cmd = "0";
224219970Smav			break;
225219970Smav		}
226219970Smav		led_set(sc->led, cmd);
227219970Smav	}
228219970Smav	g_io_deliver(bp, 0);
229219970Smav}
230219970Smav
231219970Smavstatic void
23292108Sphkg_disk_done(struct bio *bp)
23392108Sphk{
234111979Sphk	struct bio *bp2;
235111979Sphk	struct disk *dp;
236219970Smav	struct g_disk_softc *sc;
23792108Sphk
238110720Sphk	/* See "notes" for why we need a mutex here */
239110720Sphk	/* XXX: will witness accept a mix of Giant/unGiant drivers here ? */
240110720Sphk	mtx_lock(&g_disk_done_mtx);
24192108Sphk	bp->bio_completed = bp->bio_length - bp->bio_resid;
242111979Sphk
243111979Sphk	bp2 = bp->bio_parent;
244111979Sphk	if (bp2->bio_error == 0)
245111979Sphk		bp2->bio_error = bp->bio_error;
246111979Sphk	bp2->bio_completed += bp->bio_completed;
247163833Spjd	if ((bp->bio_cmd & (BIO_READ|BIO_WRITE|BIO_DELETE)) &&
248219970Smav	    (sc = bp2->bio_to->geom->softc) &&
249219970Smav	    (dp = sc->dp)) {
250150759Stegge		devstat_end_transaction_bio(dp->d_devstat, bp);
251163833Spjd	}
252111979Sphk	g_destroy_bio(bp);
253111979Sphk	bp2->bio_inbed++;
254111979Sphk	if (bp2->bio_children == bp2->bio_inbed) {
255112259Sphk		bp2->bio_resid = bp2->bio_bcount - bp2->bio_completed;
256111979Sphk		g_io_deliver(bp2, bp2->bio_error);
257111979Sphk	}
258110720Sphk	mtx_unlock(&g_disk_done_mtx);
25992108Sphk}
26092108Sphk
261119660Sphkstatic int
262138732Sphkg_disk_ioctl(struct g_provider *pp, u_long cmd, void * data, int fflag, struct thread *td)
263119660Sphk{
264119660Sphk	struct g_geom *gp;
265119660Sphk	struct disk *dp;
266219970Smav	struct g_disk_softc *sc;
267119660Sphk	int error;
268119660Sphk
269119660Sphk	gp = pp->geom;
270219970Smav	sc = gp->softc;
271219970Smav	dp = sc->dp;
272119660Sphk
273119660Sphk	if (dp->d_ioctl == NULL)
274119660Sphk		return (ENOIOCTL);
275119660Sphk	g_disk_lock_giant(dp);
276138732Sphk	error = dp->d_ioctl(dp, cmd, data, fflag, td);
277119660Sphk	g_disk_unlock_giant(dp);
278119660Sphk	return(error);
279119660Sphk}
280119660Sphk
28192108Sphkstatic void
28292108Sphkg_disk_start(struct bio *bp)
28392108Sphk{
284110720Sphk	struct bio *bp2, *bp3;
28592108Sphk	struct disk *dp;
286219970Smav	struct g_disk_softc *sc;
28792403Sphk	int error;
288110720Sphk	off_t off;
28992108Sphk
290219970Smav	sc = bp->bio_to->geom->softc;
291219970Smav	if (sc == NULL || (dp = sc->dp) == NULL || dp->d_destroyed) {
292115214Sphk		g_io_deliver(bp, ENXIO);
293143791Sphk		return;
294143791Sphk	}
295104609Sphk	error = EJUSTRETURN;
29692108Sphk	switch(bp->bio_cmd) {
297104609Sphk	case BIO_DELETE:
298110119Sphk		if (!(dp->d_flags & DISKFLAG_CANDELETE)) {
299104609Sphk			error = 0;
300104609Sphk			break;
301104609Sphk		}
302104609Sphk		/* fall-through */
30392108Sphk	case BIO_READ:
30492108Sphk	case BIO_WRITE:
305110720Sphk		off = 0;
306110720Sphk		bp3 = NULL;
30792108Sphk		bp2 = g_clone_bio(bp);
308110477Sphk		if (bp2 == NULL) {
309110477Sphk			error = ENOMEM;
310110477Sphk			break;
311110477Sphk		}
312110720Sphk		do {
313110720Sphk			bp2->bio_offset += off;
314110720Sphk			bp2->bio_length -= off;
315110766Stegge			bp2->bio_data += off;
316110720Sphk			if (bp2->bio_length > dp->d_maxsize) {
317110720Sphk				/*
318110720Sphk				 * XXX: If we have a stripesize we should really
319110720Sphk				 * use it here.
320110720Sphk				 */
321110720Sphk				bp2->bio_length = dp->d_maxsize;
322110720Sphk				off += dp->d_maxsize;
323110720Sphk				/*
324110720Sphk				 * To avoid a race, we need to grab the next bio
325110720Sphk				 * before we schedule this one.  See "notes".
326110720Sphk				 */
327110720Sphk				bp3 = g_clone_bio(bp);
328110720Sphk				if (bp3 == NULL)
329110720Sphk					bp->bio_error = ENOMEM;
330110720Sphk			}
331110720Sphk			bp2->bio_done = g_disk_done;
332110720Sphk			bp2->bio_pblkno = bp2->bio_offset / dp->d_sectorsize;
333110720Sphk			bp2->bio_bcount = bp2->bio_length;
334110720Sphk			bp2->bio_disk = dp;
335150759Stegge			devstat_start_transaction_bio(dp->d_devstat, bp2);
336110720Sphk			g_disk_lock_giant(dp);
337110720Sphk			dp->d_strategy(bp2);
338110720Sphk			g_disk_unlock_giant(dp);
339110720Sphk			bp2 = bp3;
340110720Sphk			bp3 = NULL;
341110720Sphk		} while (bp2 != NULL);
34292108Sphk		break;
34392108Sphk	case BIO_GETATTR:
344216794Skib		if (g_handleattr_int(bp, "GEOM::candelete",
345216794Skib		    (dp->d_flags & DISKFLAG_CANDELETE) != 0))
34692403Sphk			break;
347216794Skib		else if (g_handleattr_int(bp, "GEOM::fwsectors",
348216794Skib		    dp->d_fwsectors))
349216794Skib			break;
350103714Sphk		else if (g_handleattr_int(bp, "GEOM::fwheads", dp->d_fwheads))
35192403Sphk			break;
35298066Sphk		else if (g_handleattr_off_t(bp, "GEOM::frontstuff", 0))
35394287Sphk			break;
354169285Spjd		else if (g_handleattr_str(bp, "GEOM::ident", dp->d_ident))
355169285Spjd			break;
356210471Smav		else if (g_handleattr(bp, "GEOM::hba_vendor",
357210471Smav		    &dp->d_hba_vendor, 2))
358210471Smav			break;
359210471Smav		else if (g_handleattr(bp, "GEOM::hba_device",
360210471Smav		    &dp->d_hba_device, 2))
361210471Smav			break;
362210471Smav		else if (g_handleattr(bp, "GEOM::hba_subvendor",
363210471Smav		    &dp->d_hba_subvendor, 2))
364210471Smav			break;
365210471Smav		else if (g_handleattr(bp, "GEOM::hba_subdevice",
366210471Smav		    &dp->d_hba_subdevice, 2))
367210471Smav			break;
36895038Sphk		else if (!strcmp(bp->bio_attribute, "GEOM::kerneldump"))
36995038Sphk			g_disk_kerneldump(bp, dp);
370219970Smav		else if (!strcmp(bp->bio_attribute, "GEOM::setstate"))
371219970Smav			g_disk_setstate(bp, sc);
372119660Sphk		else
37392403Sphk			error = ENOIOCTL;
37492403Sphk		break;
375163833Spjd	case BIO_FLUSH:
376163833Spjd		g_trace(G_T_TOPOLOGY, "g_disk_flushcache(%s)",
377163833Spjd		    bp->bio_to->name);
378163833Spjd		if (!(dp->d_flags & DISKFLAG_CANFLUSHCACHE)) {
379163833Spjd			g_io_deliver(bp, ENODEV);
380163833Spjd			return;
381163833Spjd		}
382163833Spjd		bp2 = g_clone_bio(bp);
383163833Spjd		if (bp2 == NULL) {
384163833Spjd			g_io_deliver(bp, ENOMEM);
385163833Spjd			return;
386163833Spjd		}
387163833Spjd		bp2->bio_done = g_disk_done;
388163833Spjd		bp2->bio_disk = dp;
389163833Spjd		g_disk_lock_giant(dp);
390163833Spjd		dp->d_strategy(bp2);
391163833Spjd		g_disk_unlock_giant(dp);
392163833Spjd		break;
39392108Sphk	default:
39492403Sphk		error = EOPNOTSUPP;
39592403Sphk		break;
39692403Sphk	}
397104609Sphk	if (error != EJUSTRETURN)
398104195Sphk		g_io_deliver(bp, error);
39992403Sphk	return;
40092108Sphk}
40192108Sphk
402104936Sphkstatic void
403107953Sphkg_disk_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, struct g_consumer *cp, struct g_provider *pp)
404105537Sphk{
405105537Sphk	struct disk *dp;
406219970Smav	struct g_disk_softc *sc;
407105537Sphk
408219970Smav	sc = gp->softc;
409219970Smav	if (sc == NULL || (dp = sc->dp) == NULL)
410120374Sphk		return;
411106101Sphk	if (indent == NULL) {
412106101Sphk		sbuf_printf(sb, " hd %u", dp->d_fwheads);
413106101Sphk		sbuf_printf(sb, " sc %u", dp->d_fwsectors);
414106101Sphk		return;
415106101Sphk	}
416105539Sphk	if (pp != NULL) {
417105537Sphk		sbuf_printf(sb, "%s<fwheads>%u</fwheads>\n",
418105537Sphk		    indent, dp->d_fwheads);
419105537Sphk		sbuf_printf(sb, "%s<fwsectors>%u</fwsectors>\n",
420105537Sphk		    indent, dp->d_fwsectors);
421219056Snwhitehorn		sbuf_printf(sb, "%s<ident>%s</ident>\n", indent, dp->d_ident);
422219056Snwhitehorn		sbuf_printf(sb, "%s<descr>%s</descr>\n", indent, dp->d_descr);
423105537Sphk	}
424105537Sphk}
425105537Sphk
426105537Sphkstatic void
427115309Sphkg_disk_create(void *arg, int flag)
42892108Sphk{
42992108Sphk	struct g_geom *gp;
43092108Sphk	struct g_provider *pp;
431110708Sphk	struct disk *dp;
432219970Smav	struct g_disk_softc *sc;
433219970Smav	char tmpstr[80];
43492108Sphk
435115309Sphk	if (flag == EV_CANCEL)
436115309Sphk		return;
437104936Sphk	g_topology_assert();
438110708Sphk	dp = arg;
439219970Smav	sc = g_malloc(sizeof(*sc), M_WAITOK | M_ZERO);
440219970Smav	sc->dp = dp;
441110708Sphk	gp = g_new_geomf(&g_disk_class, "%s%d", dp->d_name, dp->d_unit);
442219970Smav	gp->softc = sc;
443104936Sphk	pp = g_new_providerf(gp, "%s", gp->name);
444110708Sphk	pp->mediasize = dp->d_mediasize;
445110708Sphk	pp->sectorsize = dp->d_sectorsize;
446110708Sphk	if (dp->d_flags & DISKFLAG_CANDELETE)
447110708Sphk		pp->flags |= G_PF_CANDELETE;
448110710Sphk	pp->stripeoffset = dp->d_stripeoffset;
449110710Sphk	pp->stripesize = dp->d_stripesize;
450105957Sphk	if (bootverbose)
451105957Sphk		printf("GEOM: new disk %s\n", gp->name);
452219970Smav	sysctl_ctx_init(&sc->sysctl_ctx);
453219970Smav	snprintf(tmpstr, sizeof(tmpstr), "GEOM disk %s", gp->name);
454219970Smav	sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
455219970Smav		SYSCTL_STATIC_CHILDREN(_kern_geom_disk), OID_AUTO, gp->name,
456219970Smav		CTLFLAG_RD, 0, tmpstr);
457219970Smav	if (sc->sysctl_tree != NULL) {
458219970Smav		snprintf(tmpstr, sizeof(tmpstr),
459219970Smav		    "kern.geom.disk.%s.led", gp->name);
460219970Smav		TUNABLE_STR_FETCH(tmpstr, sc->led, sizeof(sc->led));
461219970Smav		SYSCTL_ADD_STRING(&sc->sysctl_ctx,
462219970Smav		    SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "led",
463219970Smav		    CTLFLAG_RW | CTLFLAG_TUN, sc->led, sizeof(sc->led),
464219970Smav		    "LED name");
465219970Smav	}
466219970Smav	pp->private = sc;
467112989Sphk	dp->d_geom = gp;
468112989Sphk	g_error_provider(pp, 0);
469104936Sphk}
470104936Sphk
471119652Sphkstatic void
472119652Sphkg_disk_destroy(void *ptr, int flag)
473119652Sphk{
474125975Sphk	struct disk *dp;
475119652Sphk	struct g_geom *gp;
476219970Smav	struct g_disk_softc *sc;
477104936Sphk
478119652Sphk	g_topology_assert();
479125975Sphk	dp = ptr;
480125975Sphk	gp = dp->d_geom;
481140261Sphk	if (gp != NULL) {
482219970Smav		sc = gp->softc;
483219970Smav		if (sc->sysctl_tree != NULL) {
484219970Smav			sysctl_ctx_free(&sc->sysctl_ctx);
485219970Smav			sc->sysctl_tree = NULL;
486219970Smav		}
487219970Smav		if (sc->led[0] != 0) {
488219970Smav			led_set(sc->led, "0");
489219970Smav			sc->led[0] = 0;
490219970Smav		}
491219970Smav		g_free(sc);
492140261Sphk		gp->softc = NULL;
493140261Sphk		g_wither_geom(gp, ENXIO);
494140261Sphk	}
495125975Sphk	g_free(dp);
496119652Sphk}
497104936Sphk
498169287Spjd/*
499196823Spjd * We only allow printable characters in disk ident,
500196823Spjd * the rest is converted to 'x<HH>'.
501169287Spjd */
502169287Spjdstatic void
503169287Spjdg_disk_ident_adjust(char *ident, size_t size)
504169287Spjd{
505196823Spjd	char *p, tmp[4], newid[DISK_IDENT_SIZE];
506169287Spjd
507196823Spjd	newid[0] = '\0';
508196823Spjd	for (p = ident; *p != '\0'; p++) {
509196823Spjd		if (isprint(*p)) {
510196823Spjd			tmp[0] = *p;
511196823Spjd			tmp[1] = '\0';
512196823Spjd		} else {
513196823Spjd			snprintf(tmp, sizeof(tmp), "x%02hhx",
514196823Spjd			    *(unsigned char *)p);
515196823Spjd		}
516196823Spjd		if (strlcat(newid, tmp, sizeof(newid)) >= sizeof(newid))
517169287Spjd			break;
518169287Spjd	}
519169287Spjd	bzero(ident, size);
520169287Spjd	strlcpy(ident, newid, size);
521169287Spjd}
522169287Spjd
523125975Sphkstruct disk *
524125975Sphkdisk_alloc()
525125975Sphk{
526125975Sphk	struct disk *dp;
527125975Sphk
528125975Sphk	dp = g_malloc(sizeof *dp, M_WAITOK | M_ZERO);
529125975Sphk	return (dp);
530125975Sphk}
531125975Sphk
532111668Sphkvoid
533125975Sphkdisk_create(struct disk *dp, int version)
534104936Sphk{
535169285Spjd	if (version != DISK_VERSION_00 && version != DISK_VERSION_01) {
536125975Sphk		printf("WARNING: Attempt to add disk %s%d %s",
537125975Sphk		    dp->d_name, dp->d_unit,
538125975Sphk		    " using incompatible ABI version of disk(9)\n");
539125975Sphk		printf("WARNING: Ignoring disk %s%d\n",
540125975Sphk		    dp->d_name, dp->d_unit);
541125975Sphk		return;
542125975Sphk	}
543110119Sphk	KASSERT(dp->d_strategy != NULL, ("disk_create need d_strategy"));
544110119Sphk	KASSERT(dp->d_name != NULL, ("disk_create need d_name"));
545110119Sphk	KASSERT(*dp->d_name != 0, ("disk_create need d_name"));
546110317Sphk	KASSERT(strlen(dp->d_name) < SPECNAMELEN - 4, ("disk name too long"));
547120374Sphk	if (dp->d_devstat == NULL)
548120374Sphk		dp->d_devstat = devstat_new_entry(dp->d_name, dp->d_unit,
549120374Sphk		    dp->d_sectorsize, DEVSTAT_ALL_SUPPORTED,
550120374Sphk		    DEVSTAT_TYPE_DIRECT, DEVSTAT_PRIORITY_MAX);
551112989Sphk	dp->d_geom = NULL;
552169287Spjd	g_disk_ident_adjust(dp->d_ident, sizeof(dp->d_ident));
553113937Sphk	g_post_event(g_disk_create, dp, M_WAITOK, dp, NULL);
55492108Sphk}
55592108Sphk
55692108Sphkvoid
557111216Sphkdisk_destroy(struct disk *dp)
55892108Sphk{
55992108Sphk
560112989Sphk	g_cancel_event(dp);
561125975Sphk	dp->d_destroyed = 1;
562131207Sphk	if (dp->d_devstat != NULL)
563131207Sphk		devstat_remove_entry(dp->d_devstat);
564131267Sphk	g_post_event(g_disk_destroy, dp, M_WAITOK, NULL);
56592108Sphk}
56692108Sphk
567152565Sjdpvoid
568152565Sjdpdisk_gone(struct disk *dp)
569152565Sjdp{
570152565Sjdp	struct g_geom *gp;
571152565Sjdp	struct g_provider *pp;
572152565Sjdp
573152565Sjdp	gp = dp->d_geom;
574152565Sjdp	if (gp != NULL)
575152565Sjdp		LIST_FOREACH(pp, &gp->provider, provider)
576157619Smarcel			g_wither_provider(pp, ENXIO);
577152565Sjdp}
578152565Sjdp
579104451Sphkstatic void
580112988Sphkg_kern_disks(void *p, int flag __unused)
581104451Sphk{
582104451Sphk	struct sbuf *sb;
583104451Sphk	struct g_geom *gp;
584104451Sphk	char *sp;
585104451Sphk
586104451Sphk	sb = p;
587104451Sphk	sp = "";
588104451Sphk	g_topology_assert();
589104451Sphk	LIST_FOREACH(gp, &g_disk_class.geom, geom) {
590104451Sphk		sbuf_printf(sb, "%s%s", sp, gp->name);
591104451Sphk		sp = " ";
592104451Sphk	}
593104451Sphk	sbuf_finish(sb);
594104451Sphk}
595104451Sphk
596104451Sphkstatic int
597104451Sphksysctl_disks(SYSCTL_HANDLER_ARGS)
598104451Sphk{
599104451Sphk	int error;
600104451Sphk	struct sbuf *sb;
601104451Sphk
602181463Sdes	sb = sbuf_new_auto();
603113940Sphk	g_waitfor_event(g_kern_disks, sb, M_WAITOK, NULL);
604113937Sphk	error = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb) + 1);
605104451Sphk	sbuf_delete(sb);
606104451Sphk	return error;
607104451Sphk}
608104451Sphk
609217915SmdfSYSCTL_PROC(_kern, OID_AUTO, disks,
610217915Smdf    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 0,
611104451Sphk    sysctl_disks, "A", "names of available disks");
612104519Sphk
613