1/*	$NetBSD: ld.c,v 1.68 2010/09/20 06:54:06 kiyohara Exp $	*/
2
3/*-
4 * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Andrew Doran and Charles M. Hannum.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32/*
33 * Disk driver for use by RAID controllers.
34 */
35
36#include <sys/cdefs.h>
37__KERNEL_RCSID(0, "$NetBSD: ld.c,v 1.68 2010/09/20 06:54:06 kiyohara Exp $");
38
39#include <sys/param.h>
40#include <sys/systm.h>
41#include <sys/kernel.h>
42#include <sys/device.h>
43#include <sys/queue.h>
44#include <sys/proc.h>
45#include <sys/buf.h>
46#include <sys/bufq.h>
47#include <sys/endian.h>
48#include <sys/disklabel.h>
49#include <sys/disk.h>
50#include <sys/dkio.h>
51#include <sys/stat.h>
52#include <sys/conf.h>
53#include <sys/fcntl.h>
54#include <sys/vnode.h>
55#include <sys/syslog.h>
56#include <sys/mutex.h>
57#include <sys/rnd.h>
58
59#include <dev/ldvar.h>
60
61#include <prop/proplib.h>
62
63static void	ldgetdefaultlabel(struct ld_softc *, struct disklabel *);
64static void	ldgetdisklabel(struct ld_softc *);
65static void	ldminphys(struct buf *bp);
66static bool	ld_suspend(device_t, const pmf_qual_t *);
67static bool	ld_shutdown(device_t, int);
68static void	ldstart(struct ld_softc *, struct buf *);
69static void	ld_set_properties(struct ld_softc *);
70static void	ld_config_interrupts (device_t);
71static int	ldlastclose(device_t);
72
73extern struct	cfdriver ld_cd;
74
75static dev_type_open(ldopen);
76static dev_type_close(ldclose);
77static dev_type_read(ldread);
78static dev_type_write(ldwrite);
79static dev_type_ioctl(ldioctl);
80static dev_type_strategy(ldstrategy);
81static dev_type_dump(lddump);
82static dev_type_size(ldsize);
83
84const struct bdevsw ld_bdevsw = {
85	ldopen, ldclose, ldstrategy, ldioctl, lddump, ldsize, D_DISK
86};
87
88const struct cdevsw ld_cdevsw = {
89	ldopen, ldclose, ldread, ldwrite, ldioctl,
90	nostop, notty, nopoll, nommap, nokqfilter, D_DISK
91};
92
93static struct	dkdriver lddkdriver = { ldstrategy, ldminphys };
94
95void
96ldattach(struct ld_softc *sc)
97{
98	char tbuf[9];
99
100	mutex_init(&sc->sc_mutex, MUTEX_DEFAULT, IPL_VM);
101
102	if ((sc->sc_flags & LDF_ENABLED) == 0) {
103		aprint_normal_dev(sc->sc_dv, "disabled\n");
104		return;
105	}
106
107	/* Initialise and attach the disk structure. */
108	disk_init(&sc->sc_dk, device_xname(sc->sc_dv), &lddkdriver);
109	disk_attach(&sc->sc_dk);
110
111	if (sc->sc_maxxfer > MAXPHYS)
112		sc->sc_maxxfer = MAXPHYS;
113
114	/* Build synthetic geometry if necessary. */
115	if (sc->sc_nheads == 0 || sc->sc_nsectors == 0 ||
116	    sc->sc_ncylinders == 0) {
117		uint64_t ncyl;
118
119		if (sc->sc_secperunit <= 528 * 2048)		/* 528MB */
120			sc->sc_nheads = 16;
121		else if (sc->sc_secperunit <= 1024 * 2048)	/* 1GB */
122			sc->sc_nheads = 32;
123		else if (sc->sc_secperunit <= 21504 * 2048)	/* 21GB */
124			sc->sc_nheads = 64;
125		else if (sc->sc_secperunit <= 43008 * 2048)	/* 42GB */
126			sc->sc_nheads = 128;
127		else
128			sc->sc_nheads = 255;
129
130		sc->sc_nsectors = 63;
131		sc->sc_ncylinders = INT_MAX;
132		ncyl = sc->sc_secperunit /
133		    (sc->sc_nheads * sc->sc_nsectors);
134		if (ncyl < INT_MAX)
135			sc->sc_ncylinders = (int)ncyl;
136	}
137
138	format_bytes(tbuf, sizeof(tbuf), sc->sc_secperunit *
139	    sc->sc_secsize);
140	aprint_normal_dev(sc->sc_dv, "%s, %d cyl, %d head, %d sec, "
141	    "%d bytes/sect x %"PRIu64" sectors\n",
142	    tbuf, sc->sc_ncylinders, sc->sc_nheads,
143	    sc->sc_nsectors, sc->sc_secsize, sc->sc_secperunit);
144	sc->sc_disksize512 = sc->sc_secperunit * sc->sc_secsize / DEV_BSIZE;
145
146	ld_set_properties(sc);
147
148	/* Attach the device into the rnd source list. */
149	rnd_attach_source(&sc->sc_rnd_source, device_xname(sc->sc_dv),
150	    RND_TYPE_DISK, 0);
151
152	/* Register with PMF */
153	if (!pmf_device_register1(sc->sc_dv, ld_suspend, NULL, ld_shutdown))
154		aprint_error_dev(sc->sc_dv,
155		    "couldn't establish power handler\n");
156
157	bufq_alloc(&sc->sc_bufq, BUFQ_DISK_DEFAULT_STRAT, BUFQ_SORT_RAWBLOCK);
158
159	/* Discover wedges on this disk. */
160	config_interrupts(sc->sc_dv, ld_config_interrupts);
161}
162
163int
164ldadjqparam(struct ld_softc *sc, int xmax)
165{
166	int s;
167
168	s = splbio();
169	sc->sc_maxqueuecnt = xmax;
170	splx(s);
171
172	return (0);
173}
174
175int
176ldbegindetach(struct ld_softc *sc, int flags)
177{
178	int s, rv = 0;
179
180	if ((sc->sc_flags & LDF_ENABLED) == 0)
181		return (0);
182
183	rv = disk_begindetach(&sc->sc_dk, ldlastclose, sc->sc_dv, flags);
184
185	if (rv != 0)
186		return rv;
187
188	s = splbio();
189	sc->sc_maxqueuecnt = 0;
190	sc->sc_flags |= LDF_DETACH;
191	while (sc->sc_queuecnt > 0) {
192		sc->sc_flags |= LDF_DRAIN;
193		rv = tsleep(&sc->sc_queuecnt, PRIBIO, "lddrn", 0);
194		if (rv)
195			break;
196	}
197	splx(s);
198
199	return (rv);
200}
201
202void
203ldenddetach(struct ld_softc *sc)
204{
205	int s, bmaj, cmaj, i, mn;
206
207	if ((sc->sc_flags & LDF_ENABLED) == 0)
208		return;
209
210	/* Wait for commands queued with the hardware to complete. */
211	if (sc->sc_queuecnt != 0)
212		if (tsleep(&sc->sc_queuecnt, PRIBIO, "lddtch", 30 * hz))
213			printf("%s: not drained\n", device_xname(sc->sc_dv));
214
215	/* Locate the major numbers. */
216	bmaj = bdevsw_lookup_major(&ld_bdevsw);
217	cmaj = cdevsw_lookup_major(&ld_cdevsw);
218
219	/* Kill off any queued buffers. */
220	s = splbio();
221	bufq_drain(sc->sc_bufq);
222	splx(s);
223
224	bufq_free(sc->sc_bufq);
225
226	/* Nuke the vnodes for any open instances. */
227	for (i = 0; i < MAXPARTITIONS; i++) {
228		mn = DISKMINOR(device_unit(sc->sc_dv), i);
229		vdevgone(bmaj, mn, mn, VBLK);
230		vdevgone(cmaj, mn, mn, VCHR);
231	}
232
233	/* Delete all of our wedges. */
234	dkwedge_delall(&sc->sc_dk);
235
236	/* Detach from the disk list. */
237	disk_detach(&sc->sc_dk);
238	disk_destroy(&sc->sc_dk);
239
240	/* Unhook the entropy source. */
241	rnd_detach_source(&sc->sc_rnd_source);
242
243	/* Deregister with PMF */
244	pmf_device_deregister(sc->sc_dv);
245
246	/*
247	 * XXX We can't really flush the cache here, beceause the
248	 * XXX device may already be non-existent from the controller's
249	 * XXX perspective.
250	 */
251#if 0
252	/* Flush the device's cache. */
253	if (sc->sc_flush != NULL)
254		if ((*sc->sc_flush)(sc, 0) != 0)
255			aprint_error_dev(&sc->sc_dv, "unable to flush cache\n");
256#endif
257	mutex_destroy(&sc->sc_mutex);
258}
259
260/* ARGSUSED */
261static bool
262ld_suspend(device_t dev, const pmf_qual_t *qual)
263{
264	return ld_shutdown(dev, 0);
265}
266
267/* ARGSUSED */
268static bool
269ld_shutdown(device_t dev, int flags)
270{
271	struct ld_softc *sc = device_private(dev);
272
273	if (sc->sc_flush != NULL && (*sc->sc_flush)(sc, LDFL_POLL) != 0) {
274		printf("%s: unable to flush cache\n", device_xname(dev));
275		return false;
276	}
277
278	return true;
279}
280
281/* ARGSUSED */
282static int
283ldopen(dev_t dev, int flags, int fmt, struct lwp *l)
284{
285	struct ld_softc *sc;
286	int error, unit, part;
287
288	unit = DISKUNIT(dev);
289	if ((sc = device_lookup_private(&ld_cd, unit)) == NULL)
290		return (ENXIO);
291	if ((sc->sc_flags & LDF_ENABLED) == 0)
292		return (ENODEV);
293	part = DISKPART(dev);
294
295	mutex_enter(&sc->sc_dk.dk_openlock);
296
297	if (sc->sc_dk.dk_openmask == 0) {
298		/* Load the partition info if not already loaded. */
299		if ((sc->sc_flags & LDF_VLABEL) == 0)
300			ldgetdisklabel(sc);
301	}
302
303	/* Check that the partition exists. */
304	if (part != RAW_PART && (part >= sc->sc_dk.dk_label->d_npartitions ||
305	    sc->sc_dk.dk_label->d_partitions[part].p_fstype == FS_UNUSED)) {
306		error = ENXIO;
307		goto bad1;
308	}
309
310	/* Ensure only one open at a time. */
311	switch (fmt) {
312	case S_IFCHR:
313		sc->sc_dk.dk_copenmask |= (1 << part);
314		break;
315	case S_IFBLK:
316		sc->sc_dk.dk_bopenmask |= (1 << part);
317		break;
318	}
319	sc->sc_dk.dk_openmask =
320	    sc->sc_dk.dk_copenmask | sc->sc_dk.dk_bopenmask;
321
322	error = 0;
323 bad1:
324	mutex_exit(&sc->sc_dk.dk_openlock);
325	return (error);
326}
327
328static int
329ldlastclose(device_t self)
330{
331	struct ld_softc *sc = device_private(self);
332
333	if (sc->sc_flush != NULL && (*sc->sc_flush)(sc, 0) != 0)
334		aprint_error_dev(self, "unable to flush cache\n");
335	if ((sc->sc_flags & LDF_KLABEL) == 0)
336		sc->sc_flags &= ~LDF_VLABEL;
337
338	return 0;
339}
340
341/* ARGSUSED */
342static int
343ldclose(dev_t dev, int flags, int fmt, struct lwp *l)
344{
345	struct ld_softc *sc;
346	int part, unit;
347
348	unit = DISKUNIT(dev);
349	part = DISKPART(dev);
350	sc = device_lookup_private(&ld_cd, unit);
351
352	mutex_enter(&sc->sc_dk.dk_openlock);
353
354	switch (fmt) {
355	case S_IFCHR:
356		sc->sc_dk.dk_copenmask &= ~(1 << part);
357		break;
358	case S_IFBLK:
359		sc->sc_dk.dk_bopenmask &= ~(1 << part);
360		break;
361	}
362	sc->sc_dk.dk_openmask =
363	    sc->sc_dk.dk_copenmask | sc->sc_dk.dk_bopenmask;
364
365	if (sc->sc_dk.dk_openmask == 0)
366		ldlastclose(sc->sc_dv);
367
368	mutex_exit(&sc->sc_dk.dk_openlock);
369	return (0);
370}
371
372/* ARGSUSED */
373static int
374ldread(dev_t dev, struct uio *uio, int ioflag)
375{
376
377	return (physio(ldstrategy, NULL, dev, B_READ, ldminphys, uio));
378}
379
380/* ARGSUSED */
381static int
382ldwrite(dev_t dev, struct uio *uio, int ioflag)
383{
384
385	return (physio(ldstrategy, NULL, dev, B_WRITE, ldminphys, uio));
386}
387
388/* ARGSUSED */
389static int
390ldioctl(dev_t dev, u_long cmd, void *addr, int32_t flag, struct lwp *l)
391{
392	struct ld_softc *sc;
393	int part, unit, error;
394#ifdef __HAVE_OLD_DISKLABEL
395	struct disklabel newlabel;
396#endif
397	struct disklabel *lp;
398
399	unit = DISKUNIT(dev);
400	part = DISKPART(dev);
401	sc = device_lookup_private(&ld_cd, unit);
402
403	error = disk_ioctl(&sc->sc_dk, cmd, addr, flag, l);
404	if (error != EPASSTHROUGH)
405		return (error);
406
407	error = 0;
408	switch (cmd) {
409	case DIOCGDINFO:
410		memcpy(addr, sc->sc_dk.dk_label, sizeof(struct disklabel));
411		return (0);
412
413#ifdef __HAVE_OLD_DISKLABEL
414	case ODIOCGDINFO:
415		newlabel = *(sc->sc_dk.dk_label);
416		if (newlabel.d_npartitions > OLDMAXPARTITIONS)
417			return ENOTTY;
418		memcpy(addr, &newlabel, sizeof(struct olddisklabel));
419		return (0);
420#endif
421
422	case DIOCGPART:
423		((struct partinfo *)addr)->disklab = sc->sc_dk.dk_label;
424		((struct partinfo *)addr)->part =
425		    &sc->sc_dk.dk_label->d_partitions[part];
426		break;
427
428	case DIOCWDINFO:
429	case DIOCSDINFO:
430#ifdef __HAVE_OLD_DISKLABEL
431	case ODIOCWDINFO:
432	case ODIOCSDINFO:
433
434		if (cmd == ODIOCSDINFO || cmd == ODIOCWDINFO) {
435			memset(&newlabel, 0, sizeof newlabel);
436			memcpy(&newlabel, addr, sizeof (struct olddisklabel));
437			lp = &newlabel;
438		} else
439#endif
440		lp = (struct disklabel *)addr;
441
442		if ((flag & FWRITE) == 0)
443			return (EBADF);
444
445		mutex_enter(&sc->sc_dk.dk_openlock);
446		sc->sc_flags |= LDF_LABELLING;
447
448		error = setdisklabel(sc->sc_dk.dk_label,
449		    lp, /*sc->sc_dk.dk_openmask : */0,
450		    sc->sc_dk.dk_cpulabel);
451		if (error == 0 && (cmd == DIOCWDINFO
452#ifdef __HAVE_OLD_DISKLABEL
453		    || cmd == ODIOCWDINFO
454#endif
455		    ))
456			error = writedisklabel(
457			    MAKEDISKDEV(major(dev), DISKUNIT(dev), RAW_PART),
458			    ldstrategy, sc->sc_dk.dk_label,
459			    sc->sc_dk.dk_cpulabel);
460
461		sc->sc_flags &= ~LDF_LABELLING;
462		mutex_exit(&sc->sc_dk.dk_openlock);
463		break;
464
465	case DIOCKLABEL:
466		if ((flag & FWRITE) == 0)
467			return (EBADF);
468		if (*(int *)addr)
469			sc->sc_flags |= LDF_KLABEL;
470		else
471			sc->sc_flags &= ~LDF_KLABEL;
472		break;
473
474	case DIOCWLABEL:
475		if ((flag & FWRITE) == 0)
476			return (EBADF);
477		if (*(int *)addr)
478			sc->sc_flags |= LDF_WLABEL;
479		else
480			sc->sc_flags &= ~LDF_WLABEL;
481		break;
482
483	case DIOCGDEFLABEL:
484		ldgetdefaultlabel(sc, (struct disklabel *)addr);
485		break;
486
487#ifdef __HAVE_OLD_DISKLABEL
488	case ODIOCGDEFLABEL:
489		ldgetdefaultlabel(sc, &newlabel);
490		if (newlabel.d_npartitions > OLDMAXPARTITIONS)
491			return ENOTTY;
492		memcpy(addr, &newlabel, sizeof (struct olddisklabel));
493		break;
494#endif
495
496	case DIOCCACHESYNC:
497		/*
498		 * XXX Do we really need to care about having a writable
499		 * file descriptor here?
500		 */
501		if ((flag & FWRITE) == 0)
502			error = EBADF;
503		else if (sc->sc_flush)
504			error = (*sc->sc_flush)(sc, 0);
505		else
506			error = 0;	/* XXX Error out instead? */
507		break;
508
509	case DIOCAWEDGE:
510	    {
511	    	struct dkwedge_info *dkw = (void *) addr;
512
513		if ((flag & FWRITE) == 0)
514			return (EBADF);
515
516		/* If the ioctl happens here, the parent is us. */
517		strlcpy(dkw->dkw_parent, device_xname(sc->sc_dv),
518			sizeof(dkw->dkw_parent));
519		return (dkwedge_add(dkw));
520	    }
521
522	case DIOCDWEDGE:
523	    {
524	    	struct dkwedge_info *dkw = (void *) addr;
525
526		if ((flag & FWRITE) == 0)
527			return (EBADF);
528
529		/* If the ioctl happens here, the parent is us. */
530		strlcpy(dkw->dkw_parent, device_xname(sc->sc_dv),
531			sizeof(dkw->dkw_parent));
532		return (dkwedge_del(dkw));
533	    }
534
535	case DIOCLWEDGES:
536	    {
537	    	struct dkwedge_list *dkwl = (void *) addr;
538
539		return (dkwedge_list(&sc->sc_dk, dkwl, l));
540	    }
541	case DIOCGSTRATEGY:
542	    {
543		struct disk_strategy *dks = (void *)addr;
544
545		mutex_enter(&sc->sc_mutex);
546		strlcpy(dks->dks_name, bufq_getstrategyname(sc->sc_bufq),
547		    sizeof(dks->dks_name));
548		mutex_exit(&sc->sc_mutex);
549		dks->dks_paramlen = 0;
550
551		return 0;
552	    }
553	case DIOCSSTRATEGY:
554	    {
555		struct disk_strategy *dks = (void *)addr;
556		struct bufq_state *new, *old;
557
558		if ((flag & FWRITE) == 0)
559			return EPERM;
560
561		if (dks->dks_param != NULL)
562			return EINVAL;
563
564		dks->dks_name[sizeof(dks->dks_name) - 1] = 0; /* ensure term */
565		error = bufq_alloc(&new, dks->dks_name,
566		    BUFQ_EXACT|BUFQ_SORT_RAWBLOCK);
567		if (error)
568			return error;
569
570		mutex_enter(&sc->sc_mutex);
571		old = sc->sc_bufq;
572		bufq_move(new, old);
573		sc->sc_bufq = new;
574		mutex_exit(&sc->sc_mutex);
575		bufq_free(old);
576
577		return 0;
578	    }
579	default:
580		error = ENOTTY;
581		break;
582	}
583
584	return (error);
585}
586
587static void
588ldstrategy(struct buf *bp)
589{
590	struct ld_softc *sc;
591	struct disklabel *lp;
592	daddr_t blkno;
593	int s, part;
594
595	sc = device_lookup_private(&ld_cd, DISKUNIT(bp->b_dev));
596	part = DISKPART(bp->b_dev);
597
598	if ((sc->sc_flags & LDF_DETACH) != 0) {
599		bp->b_error = EIO;
600		goto done;
601	}
602
603	lp = sc->sc_dk.dk_label;
604
605	/*
606	 * The transfer must be a whole number of blocks and the offset must
607	 * not be negative.
608	 */
609	if ((bp->b_bcount % lp->d_secsize) != 0 || bp->b_blkno < 0) {
610		bp->b_error = EINVAL;
611		goto done;
612	}
613
614	/* If it's a null transfer, return immediately. */
615	if (bp->b_bcount == 0)
616		goto done;
617
618	/*
619	 * Do bounds checking and adjust the transfer.  If error, process.
620	 * If past the end of partition, just return.
621	 */
622	if (part == RAW_PART) {
623		if (bounds_check_with_mediasize(bp, DEV_BSIZE,
624		    sc->sc_disksize512) <= 0)
625			goto done;
626	} else {
627		if (bounds_check_with_label(&sc->sc_dk, bp,
628		    (sc->sc_flags & (LDF_WLABEL | LDF_LABELLING)) != 0) <= 0)
629			goto done;
630	}
631
632	/*
633	 * Convert the block number to absolute and put it in terms
634	 * of the device's logical block size.
635	 */
636	if (lp->d_secsize == DEV_BSIZE)
637		blkno = bp->b_blkno;
638	else if (lp->d_secsize > DEV_BSIZE)
639		blkno = bp->b_blkno / (lp->d_secsize / DEV_BSIZE);
640	else
641		blkno = bp->b_blkno * (DEV_BSIZE / lp->d_secsize);
642
643	if (part != RAW_PART)
644		blkno += lp->d_partitions[part].p_offset;
645
646	bp->b_rawblkno = blkno;
647
648	s = splbio();
649	ldstart(sc, bp);
650	splx(s);
651	return;
652
653 done:
654	bp->b_resid = bp->b_bcount;
655	biodone(bp);
656}
657
658static void
659ldstart(struct ld_softc *sc, struct buf *bp)
660{
661	int error;
662
663	mutex_enter(&sc->sc_mutex);
664
665	if (bp != NULL)
666		bufq_put(sc->sc_bufq, bp);
667
668	while (sc->sc_queuecnt < sc->sc_maxqueuecnt) {
669		/* See if there is work to do. */
670		if ((bp = bufq_peek(sc->sc_bufq)) == NULL)
671			break;
672
673		disk_busy(&sc->sc_dk);
674		sc->sc_queuecnt++;
675
676		if (__predict_true((error = (*sc->sc_start)(sc, bp)) == 0)) {
677			/*
678			 * The back-end is running the job; remove it from
679			 * the queue.
680			 */
681			(void) bufq_get(sc->sc_bufq);
682		} else  {
683			disk_unbusy(&sc->sc_dk, 0, (bp->b_flags & B_READ));
684			sc->sc_queuecnt--;
685			if (error == EAGAIN) {
686				/*
687				 * Temporary resource shortage in the
688				 * back-end; just defer the job until
689				 * later.
690				 *
691				 * XXX We might consider a watchdog timer
692				 * XXX to make sure we are kicked into action.
693				 */
694				break;
695			} else {
696				(void) bufq_get(sc->sc_bufq);
697				bp->b_error = error;
698				bp->b_resid = bp->b_bcount;
699				mutex_exit(&sc->sc_mutex);
700				biodone(bp);
701				mutex_enter(&sc->sc_mutex);
702			}
703		}
704	}
705
706	mutex_exit(&sc->sc_mutex);
707}
708
709void
710lddone(struct ld_softc *sc, struct buf *bp)
711{
712
713	if (bp->b_error != 0) {
714		diskerr(bp, "ld", "error", LOG_PRINTF, 0, sc->sc_dk.dk_label);
715		printf("\n");
716	}
717
718	disk_unbusy(&sc->sc_dk, bp->b_bcount - bp->b_resid,
719	    (bp->b_flags & B_READ));
720	rnd_add_uint32(&sc->sc_rnd_source, bp->b_rawblkno);
721	biodone(bp);
722
723	mutex_enter(&sc->sc_mutex);
724	if (--sc->sc_queuecnt <= sc->sc_maxqueuecnt) {
725		if ((sc->sc_flags & LDF_DRAIN) != 0) {
726			sc->sc_flags &= ~LDF_DRAIN;
727			wakeup(&sc->sc_queuecnt);
728		}
729		mutex_exit(&sc->sc_mutex);
730		ldstart(sc, NULL);
731	} else
732		mutex_exit(&sc->sc_mutex);
733}
734
735static int
736ldsize(dev_t dev)
737{
738	struct ld_softc *sc;
739	int part, unit, omask, size;
740
741	unit = DISKUNIT(dev);
742	if ((sc = device_lookup_private(&ld_cd, unit)) == NULL)
743		return (ENODEV);
744	if ((sc->sc_flags & LDF_ENABLED) == 0)
745		return (ENODEV);
746	part = DISKPART(dev);
747
748	omask = sc->sc_dk.dk_openmask & (1 << part);
749
750	if (omask == 0 && ldopen(dev, 0, S_IFBLK, NULL) != 0)
751		return (-1);
752	else if (sc->sc_dk.dk_label->d_partitions[part].p_fstype != FS_SWAP)
753		size = -1;
754	else
755		size = sc->sc_dk.dk_label->d_partitions[part].p_size *
756		    (sc->sc_dk.dk_label->d_secsize / DEV_BSIZE);
757	if (omask == 0 && ldclose(dev, 0, S_IFBLK, NULL) != 0)
758		return (-1);
759
760	return (size);
761}
762
763/*
764 * Load the label information from the specified device.
765 */
766static void
767ldgetdisklabel(struct ld_softc *sc)
768{
769	const char *errstring;
770
771	ldgetdefaultlabel(sc, sc->sc_dk.dk_label);
772
773	/* Call the generic disklabel extraction routine. */
774	errstring = readdisklabel(MAKEDISKDEV(0, device_unit(sc->sc_dv),
775	    RAW_PART), ldstrategy, sc->sc_dk.dk_label, sc->sc_dk.dk_cpulabel);
776	if (errstring != NULL)
777		printf("%s: %s\n", device_xname(sc->sc_dv), errstring);
778
779	/* In-core label now valid. */
780	sc->sc_flags |= LDF_VLABEL;
781}
782
783/*
784 * Construct a ficticious label.
785 */
786static void
787ldgetdefaultlabel(struct ld_softc *sc, struct disklabel *lp)
788{
789
790	memset(lp, 0, sizeof(struct disklabel));
791
792	lp->d_secsize = sc->sc_secsize;
793	lp->d_ntracks = sc->sc_nheads;
794	lp->d_nsectors = sc->sc_nsectors;
795	lp->d_ncylinders = sc->sc_ncylinders;
796	lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors;
797	lp->d_type = DTYPE_LD;
798	strlcpy(lp->d_typename, "unknown", sizeof(lp->d_typename));
799	strlcpy(lp->d_packname, "fictitious", sizeof(lp->d_packname));
800	lp->d_secperunit = sc->sc_secperunit;
801	lp->d_rpm = 7200;
802	lp->d_interleave = 1;
803	lp->d_flags = 0;
804
805	lp->d_partitions[RAW_PART].p_offset = 0;
806	lp->d_partitions[RAW_PART].p_size =
807	    lp->d_secperunit * (lp->d_secsize / DEV_BSIZE);
808	lp->d_partitions[RAW_PART].p_fstype = FS_UNUSED;
809	lp->d_npartitions = RAW_PART + 1;
810
811	lp->d_magic = DISKMAGIC;
812	lp->d_magic2 = DISKMAGIC;
813	lp->d_checksum = dkcksum(lp);
814}
815
816/*
817 * Take a dump.
818 */
819static int
820lddump(dev_t dev, daddr_t blkno, void *vav, size_t size)
821{
822	char *va = vav;
823	struct ld_softc *sc;
824	struct disklabel *lp;
825	int unit, part, nsects, sectoff, towrt, nblk, maxblkcnt, rv;
826	static int dumping;
827
828	unit = DISKUNIT(dev);
829	if ((sc = device_lookup_private(&ld_cd, unit)) == NULL)
830		return (ENXIO);
831	if ((sc->sc_flags & LDF_ENABLED) == 0)
832		return (ENODEV);
833	if (sc->sc_dump == NULL)
834		return (ENXIO);
835
836	/* Check if recursive dump; if so, punt. */
837	if (dumping)
838		return (EFAULT);
839	dumping = 1;
840
841	/* Convert to disk sectors.  Request must be a multiple of size. */
842	part = DISKPART(dev);
843	lp = sc->sc_dk.dk_label;
844	if ((size % lp->d_secsize) != 0)
845		return (EFAULT);
846	towrt = size / lp->d_secsize;
847	blkno = dbtob(blkno) / lp->d_secsize;	/* blkno in DEV_BSIZE units */
848
849	nsects = lp->d_partitions[part].p_size;
850	sectoff = lp->d_partitions[part].p_offset;
851
852	/* Check transfer bounds against partition size. */
853	if ((blkno < 0) || ((blkno + towrt) > nsects))
854		return (EINVAL);
855
856	/* Offset block number to start of partition. */
857	blkno += sectoff;
858
859	/* Start dumping and return when done. */
860	maxblkcnt = sc->sc_maxxfer / sc->sc_secsize - 1;
861	while (towrt > 0) {
862		nblk = min(maxblkcnt, towrt);
863
864		if ((rv = (*sc->sc_dump)(sc, va, blkno, nblk)) != 0)
865			return (rv);
866
867		towrt -= nblk;
868		blkno += nblk;
869		va += nblk * sc->sc_secsize;
870	}
871
872	dumping = 0;
873	return (0);
874}
875
876/*
877 * Adjust the size of a transfer.
878 */
879static void
880ldminphys(struct buf *bp)
881{
882	struct ld_softc *sc;
883
884	sc = device_lookup_private(&ld_cd, DISKUNIT(bp->b_dev));
885
886	if (bp->b_bcount > sc->sc_maxxfer)
887		bp->b_bcount = sc->sc_maxxfer;
888	minphys(bp);
889}
890
891static void
892ld_set_properties(struct ld_softc *ld)
893{
894	prop_dictionary_t disk_info, odisk_info, geom;
895
896	disk_info = prop_dictionary_create();
897
898	geom = prop_dictionary_create();
899
900	prop_dictionary_set_uint64(geom, "sectors-per-unit",
901	    ld->sc_secperunit);
902
903	prop_dictionary_set_uint32(geom, "sector-size",
904	    ld->sc_secsize);
905
906	prop_dictionary_set_uint16(geom, "sectors-per-track",
907	    ld->sc_nsectors);
908
909	prop_dictionary_set_uint16(geom, "tracks-per-cylinder",
910	    ld->sc_nheads);
911
912	prop_dictionary_set_uint64(geom, "cylinders-per-unit",
913	    ld->sc_ncylinders);
914
915	prop_dictionary_set(disk_info, "geometry", geom);
916	prop_object_release(geom);
917
918	prop_dictionary_set(device_properties(ld->sc_dv),
919	    "disk-info", disk_info);
920
921	/*
922	 * Don't release disk_info here; we keep a reference to it.
923	 * disk_detach() will release it when we go away.
924	 */
925
926	odisk_info = ld->sc_dk.dk_info;
927	ld->sc_dk.dk_info = disk_info;
928	if (odisk_info)
929		prop_object_release(odisk_info);
930}
931
932static void
933ld_config_interrupts(device_t d)
934{
935	struct ld_softc *sc = device_private(d);
936	dkwedge_discover(&sc->sc_dk);
937}
938