geom_disk.c revision 133314
1285809Sscottl/*-
2285809Sscottl * Copyright (c) 2002 Poul-Henning Kamp
3285809Sscottl * Copyright (c) 2002 Networks Associates Technology, Inc.
4285809Sscottl * All rights reserved.
5285809Sscottl *
6285809Sscottl * This software was developed for the FreeBSD Project by Poul-Henning Kamp
7285809Sscottl * and NAI Labs, the Security Research Division of Network Associates, Inc.
8285809Sscottl * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
9285809Sscottl * DARPA CHATS research program.
10285809Sscottl *
11285809Sscottl * Redistribution and use in source and binary forms, with or without
12285809Sscottl * modification, are permitted provided that the following conditions
13285809Sscottl * are met:
14285809Sscottl * 1. Redistributions of source code must retain the above copyright
15285809Sscottl *    notice, this list of conditions and the following disclaimer.
16285809Sscottl * 2. Redistributions in binary form must reproduce the above copyright
17285809Sscottl *    notice, this list of conditions and the following disclaimer in the
18285809Sscottl *    documentation and/or other materials provided with the distribution.
19285809Sscottl * 3. The names of the authors may not be used to endorse or promote
20285809Sscottl *    products derived from this software without specific prior written
21285809Sscottl *    permission.
22285809Sscottl *
23285809Sscottl * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
24285809Sscottl * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25285809Sscottl * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26285809Sscottl * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
27285809Sscottl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28285809Sscottl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29285809Sscottl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30285809Sscottl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31285809Sscottl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32285809Sscottl * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33285809Sscottl * SUCH DAMAGE.
34285809Sscottl */
35285809Sscottl
36285809Sscottl#include <sys/cdefs.h>
37285809Sscottl__FBSDID("$FreeBSD: head/sys/geom/geom_disk.c 133314 2004-08-08 06:49:07Z phk $");
38285809Sscottl
39285809Sscottl#include "opt_geom.h"
40285809Sscottl
41285809Sscottl#include <sys/param.h>
42285809Sscottl#include <sys/systm.h>
43285809Sscottl#include <sys/kernel.h>
44285809Sscottl#include <sys/sysctl.h>
45285809Sscottl#include <sys/bio.h>
46285809Sscottl#include <sys/conf.h>
47285809Sscottl#include <sys/fcntl.h>
48285809Sscottl#include <sys/malloc.h>
49285809Sscottl#include <sys/sysctl.h>
50285809Sscottl#include <sys/devicestat.h>
51285809Sscottl#include <machine/md_var.h>
52285809Sscottl
53285809Sscottl#include <sys/lock.h>
54285809Sscottl#include <sys/mutex.h>
55285809Sscottl#include <geom/geom.h>
56285809Sscottl#include <geom/geom_disk.h>
57285809Sscottl#include <geom/geom_int.h>
58285809Sscottl
59285809Sscottlstatic struct mtx g_disk_done_mtx;
60285809Sscottl
61285809Sscottlstatic g_access_t g_disk_access;
62285809Sscottlstatic g_init_t g_disk_init;
63285809Sscottlstatic g_fini_t g_disk_fini;
64285809Sscottlstatic g_start_t g_disk_start;
65285809Sscottlstatic g_ioctl_t g_disk_ioctl;
66285809Sscottlstatic g_dumpconf_t g_disk_dumpconf;
67285809Sscottl
68285809Sscottlstruct g_class g_disk_class = {
69285809Sscottl	.name = "DISK",
70285809Sscottl	.init = g_disk_init,
71285809Sscottl	.fini = g_disk_fini,
72285809Sscottl	.start = g_disk_start,
73285809Sscottl	.access = g_disk_access,
74285809Sscottl	.ioctl = g_disk_ioctl,
75285809Sscottl	.dumpconf = g_disk_dumpconf,
76285809Sscottl};
77285809Sscottl
78285809Sscottlstatic void
79285809Sscottlg_disk_init(struct g_class *mp __unused)
80285809Sscottl{
81285809Sscottl
82285809Sscottl	mtx_init(&g_disk_done_mtx, "g_disk_done", NULL, MTX_DEF);
83285809Sscottl}
84285809Sscottl
85285809Sscottlstatic void
86285809Sscottlg_disk_fini(struct g_class *mp __unused)
87285809Sscottl{
88285809Sscottl
89285809Sscottl	mtx_destroy(&g_disk_done_mtx);
90285809Sscottl}
91285809Sscottl
92285809SscottlDECLARE_GEOM_CLASS(g_disk_class, g_disk);
93285809Sscottl
94285809Sscottlstatic void __inline
95285809Sscottlg_disk_lock_giant(struct disk *dp)
96285809Sscottl{
97285809Sscottl	if (dp->d_flags & DISKFLAG_NEEDSGIANT)
98285809Sscottl		mtx_lock(&Giant);
99285809Sscottl}
100285809Sscottl
101285809Sscottlstatic void __inline
102285809Sscottlg_disk_unlock_giant(struct disk *dp)
103285809Sscottl{
104285809Sscottl	if (dp->d_flags & DISKFLAG_NEEDSGIANT)
105285809Sscottl		mtx_unlock(&Giant);
106285809Sscottl}
107285809Sscottl
108285809Sscottlstatic int
109285809Sscottlg_disk_access(struct g_provider *pp, int r, int w, int e)
110285809Sscottl{
111285809Sscottl	struct disk *dp;
112285809Sscottl	int error;
113285809Sscottl
114285809Sscottl	g_trace(G_T_ACCESS, "g_disk_access(%s, %d, %d, %d)",
115285809Sscottl	    pp->name, r, w, e);
116285809Sscottl	g_topology_assert();
117285809Sscottl	dp = pp->geom->softc;
118285809Sscottl	if (dp == NULL || dp->d_destroyed) {
119285809Sscottl		/*
120285809Sscottl		 * Allow decreasing access count even if disk is not
121285809Sscottl		 * avaliable anymore.
122285809Sscottl		 */
123285809Sscottl		if (r <= 0 && w <= 0 && e <= 0)
124285809Sscottl			return (0);
125285809Sscottl		return (ENXIO);
126285809Sscottl	}
127285809Sscottl	r += pp->acr;
128285809Sscottl	w += pp->acw;
129285809Sscottl	e += pp->ace;
130285809Sscottl	error = 0;
131285809Sscottl	if ((pp->acr + pp->acw + pp->ace) == 0 && (r + w + e) > 0) {
132285809Sscottl		if (dp->d_open != NULL) {
133285809Sscottl			g_disk_lock_giant(dp);
134285809Sscottl			error = dp->d_open(dp);
135285809Sscottl			if (error != 0)
136285809Sscottl				printf("Opened disk %s -> %d\n",
137285809Sscottl				    pp->name, error);
138285809Sscottl			g_disk_unlock_giant(dp);
139285809Sscottl		}
140285809Sscottl		pp->mediasize = dp->d_mediasize;
141285809Sscottl		pp->sectorsize = dp->d_sectorsize;
142285809Sscottl		dp->d_flags |= DISKFLAG_OPEN;
143285809Sscottl		if (dp->d_maxsize == 0) {
144285809Sscottl			printf("WARNING: Disk drive %s%d has no d_maxsize\n",
145285809Sscottl			    dp->d_name, dp->d_unit);
146285809Sscottl			dp->d_maxsize = DFLTPHYS;
147285809Sscottl		}
148285809Sscottl	} else if ((pp->acr + pp->acw + pp->ace) > 0 && (r + w + e) == 0) {
149285809Sscottl		if (dp->d_close != NULL) {
150285809Sscottl			g_disk_lock_giant(dp);
151285809Sscottl			error = dp->d_close(dp);
152285809Sscottl			if (error != 0)
153285809Sscottl				printf("Closed disk %s -> %d\n",
154285809Sscottl				    pp->name, error);
155285809Sscottl			g_disk_unlock_giant(dp);
156285809Sscottl		}
157285809Sscottl		dp->d_flags &= ~DISKFLAG_OPEN;
158285809Sscottl	}
159285809Sscottl	return (error);
160285809Sscottl}
161285809Sscottl
162285809Sscottlstatic void
163285809Sscottlg_disk_kerneldump(struct bio *bp, struct disk *dp)
164285809Sscottl{
165285809Sscottl	int error;
166285809Sscottl	struct g_kerneldump *gkd;
167285809Sscottl	struct dumperinfo di;
168285809Sscottl	struct g_geom *gp;
169285809Sscottl
170285809Sscottl	gkd = (struct g_kerneldump*)bp->bio_data;
171285809Sscottl	gp = bp->bio_to->geom;
172285809Sscottl	g_trace(G_T_TOPOLOGY, "g_disk_kernedump(%s, %jd, %jd)",
173285809Sscottl		gp->name, (intmax_t)gkd->offset, (intmax_t)gkd->length);
174285809Sscottl	if (dp->d_dump == NULL) {
175285809Sscottl		g_io_deliver(bp, ENODEV);
176285809Sscottl		return;
177285809Sscottl	}
178285809Sscottl	di.dumper = dp->d_dump;
179285809Sscottl	di.priv = dp;
180285809Sscottl	di.blocksize = dp->d_sectorsize;
181285809Sscottl	di.mediaoffset = gkd->offset;
182285809Sscottl	di.mediasize = gkd->length;
183285809Sscottl	error = set_dumper(&di);
184285809Sscottl	g_io_deliver(bp, error);
185285809Sscottl}
186285809Sscottl
187285809Sscottlstatic void
188285809Sscottlg_disk_done(struct bio *bp)
189285809Sscottl{
190285809Sscottl	struct bio *bp2;
191285809Sscottl	struct disk *dp;
192285809Sscottl
193285809Sscottl	/* See "notes" for why we need a mutex here */
194285809Sscottl	/* XXX: will witness accept a mix of Giant/unGiant drivers here ? */
195285809Sscottl	mtx_lock(&g_disk_done_mtx);
196285809Sscottl	bp->bio_completed = bp->bio_length - bp->bio_resid;
197285809Sscottl
198285809Sscottl	bp2 = bp->bio_parent;
199285809Sscottl	if (bp2->bio_error == 0)
200285809Sscottl		bp2->bio_error = bp->bio_error;
201285809Sscottl	bp2->bio_completed += bp->bio_completed;
202285809Sscottl	g_destroy_bio(bp);
203285809Sscottl	bp2->bio_inbed++;
204285809Sscottl	if (bp2->bio_children == bp2->bio_inbed) {
205285809Sscottl		bp2->bio_resid = bp2->bio_bcount - bp2->bio_completed;
206285809Sscottl		if ((dp = bp2->bio_to->geom->softc))
207285809Sscottl			devstat_end_transaction_bio(dp->d_devstat, bp2);
208285809Sscottl		g_io_deliver(bp2, bp2->bio_error);
209285809Sscottl	}
210285809Sscottl	mtx_unlock(&g_disk_done_mtx);
211285809Sscottl}
212285809Sscottl
213285809Sscottlstatic int
214285809Sscottlg_disk_ioctl(struct g_provider *pp, u_long cmd, void * data, struct thread *td)
215285809Sscottl{
216285809Sscottl	struct g_geom *gp;
217285809Sscottl	struct disk *dp;
218285809Sscottl	int error;
219285809Sscottl
220285809Sscottl	gp = pp->geom;
221285809Sscottl	dp = gp->softc;
222285809Sscottl
223285809Sscottl	if (dp->d_ioctl == NULL)
224285809Sscottl		return (ENOIOCTL);
225285809Sscottl	g_disk_lock_giant(dp);
226285809Sscottl	error = dp->d_ioctl(dp, cmd, data, 0, td);
227285809Sscottl	g_disk_unlock_giant(dp);
228285809Sscottl	return(error);
229285809Sscottl}
230285809Sscottl
231285809Sscottlstatic void
232285809Sscottlg_disk_start(struct bio *bp)
233285809Sscottl{
234285809Sscottl	struct bio *bp2, *bp3;
235285809Sscottl	struct disk *dp;
236285809Sscottl	int error;
237285809Sscottl	off_t off;
238285809Sscottl
239285809Sscottl	dp = bp->bio_to->geom->softc;
240285809Sscottl	if (dp == NULL || dp->d_destroyed)
241285809Sscottl		g_io_deliver(bp, ENXIO);
242285809Sscottl	error = EJUSTRETURN;
243285809Sscottl	switch(bp->bio_cmd) {
244285809Sscottl	case BIO_DELETE:
245285809Sscottl		if (!(dp->d_flags & DISKFLAG_CANDELETE)) {
246285809Sscottl			error = 0;
247285809Sscottl			break;
248285809Sscottl		}
249285809Sscottl		/* fall-through */
250285809Sscottl	case BIO_READ:
251285809Sscottl	case BIO_WRITE:
252285809Sscottl		off = 0;
253285809Sscottl		bp3 = NULL;
254285809Sscottl		bp2 = g_clone_bio(bp);
255285809Sscottl		if (bp2 == NULL) {
256285809Sscottl			error = ENOMEM;
257285809Sscottl			break;
258285809Sscottl		}
259285809Sscottl		devstat_start_transaction_bio(dp->d_devstat, bp);
260285809Sscottl		do {
261285809Sscottl			bp2->bio_offset += off;
262285809Sscottl			bp2->bio_length -= off;
263285809Sscottl			bp2->bio_data += off;
264285809Sscottl			if (bp2->bio_length > dp->d_maxsize) {
265285809Sscottl				/*
266285809Sscottl				 * XXX: If we have a stripesize we should really
267285809Sscottl				 * use it here.
268285809Sscottl				 */
269285809Sscottl				bp2->bio_length = dp->d_maxsize;
270285809Sscottl				off += dp->d_maxsize;
271285809Sscottl				/*
272285809Sscottl				 * To avoid a race, we need to grab the next bio
273285809Sscottl				 * before we schedule this one.  See "notes".
274285809Sscottl				 */
275285809Sscottl				bp3 = g_clone_bio(bp);
276285809Sscottl				if (bp3 == NULL)
277285809Sscottl					bp->bio_error = ENOMEM;
278285809Sscottl			}
279285809Sscottl			bp2->bio_done = g_disk_done;
280285809Sscottl			bp2->bio_pblkno = bp2->bio_offset / dp->d_sectorsize;
281285809Sscottl			bp2->bio_bcount = bp2->bio_length;
282285809Sscottl			bp2->bio_disk = dp;
283285809Sscottl			g_disk_lock_giant(dp);
284285809Sscottl			dp->d_strategy(bp2);
285285809Sscottl			g_disk_unlock_giant(dp);
286285809Sscottl			bp2 = bp3;
287285809Sscottl			bp3 = NULL;
288285809Sscottl		} while (bp2 != NULL);
289285809Sscottl		break;
290285809Sscottl	case BIO_GETATTR:
291285809Sscottl		if (g_handleattr_int(bp, "GEOM::fwsectors", dp->d_fwsectors))
292285809Sscottl			break;
293285809Sscottl		else if (g_handleattr_int(bp, "GEOM::fwheads", dp->d_fwheads))
294285809Sscottl			break;
295285809Sscottl		else if (g_handleattr_off_t(bp, "GEOM::frontstuff", 0))
296285809Sscottl			break;
297285809Sscottl		else if (!strcmp(bp->bio_attribute, "GEOM::kerneldump"))
298285809Sscottl			g_disk_kerneldump(bp, dp);
299285809Sscottl		else
300285809Sscottl			error = ENOIOCTL;
301285809Sscottl		break;
302285809Sscottl	default:
303285809Sscottl		error = EOPNOTSUPP;
304285809Sscottl		break;
305285809Sscottl	}
306285809Sscottl	if (error != EJUSTRETURN)
307285809Sscottl		g_io_deliver(bp, error);
308285809Sscottl	return;
309285809Sscottl}
310285809Sscottl
311285809Sscottlstatic void
312285809Sscottlg_disk_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, struct g_consumer *cp, struct g_provider *pp)
313285809Sscottl{
314285809Sscottl	struct disk *dp;
315285809Sscottl
316285809Sscottl	dp = gp->softc;
317285809Sscottl	if (dp == NULL)
318285809Sscottl		return;
319285809Sscottl	if (indent == NULL) {
320285809Sscottl		sbuf_printf(sb, " hd %u", dp->d_fwheads);
321285809Sscottl		sbuf_printf(sb, " sc %u", dp->d_fwsectors);
322285809Sscottl		return;
323285809Sscottl	}
324285809Sscottl	if (pp != NULL) {
325285809Sscottl		sbuf_printf(sb, "%s<fwheads>%u</fwheads>\n",
326285809Sscottl		    indent, dp->d_fwheads);
327285809Sscottl		sbuf_printf(sb, "%s<fwsectors>%u</fwsectors>\n",
328285809Sscottl		    indent, dp->d_fwsectors);
329285809Sscottl	}
330285809Sscottl}
331285809Sscottl
332285809Sscottlstatic void
333285809Sscottlg_disk_create(void *arg, int flag)
334285809Sscottl{
335285809Sscottl	struct g_geom *gp;
336285809Sscottl	struct g_provider *pp;
337285809Sscottl	struct disk *dp;
338285809Sscottl
339285809Sscottl	if (flag == EV_CANCEL)
340285809Sscottl		return;
341285809Sscottl	g_topology_assert();
342285809Sscottl	dp = arg;
343285809Sscottl	gp = g_new_geomf(&g_disk_class, "%s%d", dp->d_name, dp->d_unit);
344285809Sscottl	gp->softc = dp;
345285809Sscottl	pp = g_new_providerf(gp, "%s", gp->name);
346285809Sscottl	pp->mediasize = dp->d_mediasize;
347285809Sscottl	pp->sectorsize = dp->d_sectorsize;
348285809Sscottl	if (dp->d_flags & DISKFLAG_CANDELETE)
349285809Sscottl		pp->flags |= G_PF_CANDELETE;
350285809Sscottl	pp->stripeoffset = dp->d_stripeoffset;
351285809Sscottl	pp->stripesize = dp->d_stripesize;
352285809Sscottl	if (bootverbose)
353285809Sscottl		printf("GEOM: new disk %s\n", gp->name);
354285809Sscottl	dp->d_geom = gp;
355285809Sscottl	g_error_provider(pp, 0);
356285809Sscottl}
357285809Sscottl
358285809Sscottlstatic void
359285809Sscottlg_disk_destroy(void *ptr, int flag)
360285809Sscottl{
361285809Sscottl	struct disk *dp;
362285809Sscottl	struct g_geom *gp;
363285809Sscottl
364285809Sscottl	g_topology_assert();
365285809Sscottl	dp = ptr;
366285809Sscottl	gp = dp->d_geom;
367285809Sscottl	gp->softc = NULL;
368285809Sscottl	g_wither_geom(gp, ENXIO);
369285809Sscottl	g_free(dp);
370285809Sscottl}
371285809Sscottl
372285809Sscottlstruct disk *
373285809Sscottldisk_alloc()
374285809Sscottl{
375285809Sscottl	struct disk *dp;
376285809Sscottl
377285809Sscottl	dp = g_malloc(sizeof *dp, M_WAITOK | M_ZERO);
378285809Sscottl	return (dp);
379285809Sscottl}
380285809Sscottl
381285809Sscottlvoid
382285809Sscottldisk_create(struct disk *dp, int version)
383285809Sscottl{
384285809Sscottl	if (version != DISK_VERSION_00) {
385285809Sscottl		printf("WARNING: Attempt to add disk %s%d %s",
386285809Sscottl		    dp->d_name, dp->d_unit,
387285809Sscottl		    " using incompatible ABI version of disk(9)\n");
388285809Sscottl		printf("WARNING: Ignoring disk %s%d\n",
389285809Sscottl		    dp->d_name, dp->d_unit);
390285809Sscottl		return;
391285809Sscottl	}
392285809Sscottl	KASSERT(dp->d_strategy != NULL, ("disk_create need d_strategy"));
393285809Sscottl	KASSERT(dp->d_name != NULL, ("disk_create need d_name"));
394285809Sscottl	KASSERT(*dp->d_name != 0, ("disk_create need d_name"));
395285809Sscottl	KASSERT(strlen(dp->d_name) < SPECNAMELEN - 4, ("disk name too long"));
396285809Sscottl	if (dp->d_devstat == NULL)
397285809Sscottl		dp->d_devstat = devstat_new_entry(dp->d_name, dp->d_unit,
398285809Sscottl		    dp->d_sectorsize, DEVSTAT_ALL_SUPPORTED,
399285809Sscottl		    DEVSTAT_TYPE_DIRECT, DEVSTAT_PRIORITY_MAX);
400285809Sscottl	dp->d_geom = NULL;
401285809Sscottl	g_post_event(g_disk_create, dp, M_WAITOK, dp, NULL);
402285809Sscottl}
403285809Sscottl
404285809Sscottlvoid
405285809Sscottldisk_destroy(struct disk *dp)
406285809Sscottl{
407285809Sscottl
408285809Sscottl	g_cancel_event(dp);
409285809Sscottl	dp->d_destroyed = 1;
410285809Sscottl	if (dp->d_devstat != NULL)
411285809Sscottl		devstat_remove_entry(dp->d_devstat);
412285809Sscottl	g_post_event(g_disk_destroy, dp, M_WAITOK, NULL);
413285809Sscottl}
414285809Sscottl
415285809Sscottlstatic void
416285809Sscottlg_kern_disks(void *p, int flag __unused)
417285809Sscottl{
418285809Sscottl	struct sbuf *sb;
419285809Sscottl	struct g_geom *gp;
420285809Sscottl	char *sp;
421285809Sscottl
422285809Sscottl	sb = p;
423285809Sscottl	sp = "";
424285809Sscottl	g_topology_assert();
425285809Sscottl	LIST_FOREACH(gp, &g_disk_class.geom, geom) {
426285809Sscottl		sbuf_printf(sb, "%s%s", sp, gp->name);
427285809Sscottl		sp = " ";
428285809Sscottl	}
429285809Sscottl	sbuf_finish(sb);
430285809Sscottl}
431285809Sscottl
432285809Sscottlstatic int
433285809Sscottlsysctl_disks(SYSCTL_HANDLER_ARGS)
434285809Sscottl{
435285809Sscottl	int error;
436285809Sscottl	struct sbuf *sb;
437285809Sscottl
438285809Sscottl	sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND);
439285809Sscottl	g_waitfor_event(g_kern_disks, sb, M_WAITOK, NULL);
440285809Sscottl	error = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb) + 1);
441285809Sscottl	sbuf_delete(sb);
442285809Sscottl	return error;
443285809Sscottl}
444285809Sscottl
445285809SscottlSYSCTL_PROC(_kern, OID_AUTO, disks, CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_NOLOCK, 0, 0,
446285809Sscottl    sysctl_disks, "A", "names of available disks");
447285809Sscottl
448285809Sscottl