ld.c revision 1.2 1 /* $NetBSD: ld.c,v 1.2 2000/12/03 13:03:30 ad 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 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 /*
40 * Disk driver for use by RAID controllers.
41 */
42
43 #include "rnd.h"
44
45 #include <sys/param.h>
46 #include <sys/systm.h>
47 #include <sys/kernel.h>
48 #include <sys/device.h>
49 #include <sys/queue.h>
50 #include <sys/proc.h>
51 #include <sys/buf.h>
52 #include <sys/endian.h>
53 #include <sys/disklabel.h>
54 #include <sys/disk.h>
55 #include <sys/dkio.h>
56 #include <sys/stat.h>
57 #include <sys/lock.h>
58 #include <sys/conf.h>
59 #include <sys/fcntl.h>
60 #include <sys/vnode.h>
61 #include <sys/syslog.h>
62 #if NRND > 0
63 #include <sys/rnd.h>
64 #endif
65
66 #include <dev/ldvar.h>
67
68 static void ldgetdefaultlabel(struct ld_softc *, struct disklabel *);
69 static void ldgetdisklabel(struct ld_softc *);
70 static int ldlock(struct ld_softc *);
71 static void ldminphys(struct buf *bp);
72 static void ldshutdown(void *);
73 static int ldstart(struct ld_softc *, struct buf *);
74 static void ldunlock(struct ld_softc *);
75
76 extern struct cfdriver ld_cd;
77
78 static struct dkdriver lddkdriver = { ldstrategy };
79 static void *ld_sdh;
80
81 void
82 ldattach(struct ld_softc *sc)
83 {
84 char buf[9];
85
86 /* Initialise and attach the disk structure. */
87 sc->sc_dk.dk_driver = &lddkdriver;
88 sc->sc_dk.dk_name = sc->sc_dv.dv_xname;
89 disk_attach(&sc->sc_dk);
90
91 if ((sc->sc_flags & LDF_ENABLED) == 0) {
92 printf("%s: disabled\n", sc->sc_dv.dv_xname);
93 return;
94 }
95 if (sc->sc_maxxfer > MAXPHYS)
96 sc->sc_maxxfer = MAXPHYS;
97
98 format_bytes(buf, sizeof(buf), (u_int64_t)sc->sc_secperunit *
99 sc->sc_secsize);
100 printf("%s: %s, %d cyl, %d head, %d sec, %d bytes/sect x %d sectors\n",
101 sc->sc_dv.dv_xname, buf, sc->sc_ncylinders, sc->sc_nheads,
102 sc->sc_nsectors, sc->sc_secsize, sc->sc_secperunit);
103
104 #if NRND > 0
105 /* Attach the device into the rnd source list. */
106 rnd_attach_source(&sc->sc_rnd_source, sc->sc_dv.dv_xname,
107 RND_TYPE_DISK, 0);
108 #endif
109
110 /* Set the `shutdownhook'. */
111 if (ld_sdh == NULL)
112 ld_sdh = shutdownhook_establish(ldshutdown, NULL);
113 BUFQ_INIT(&sc->sc_bufq);
114 }
115
116 void
117 lddetach(struct ld_softc *sc)
118 {
119 struct buf *bp;
120 int s, bmaj, cmaj, mn;
121
122 /* Wait for commands queued with the hardware to complete. */
123 if (sc->sc_queuecnt != 0)
124 tsleep(&sc->sc_queuecnt, PRIBIO, "lddrn", 0);
125
126 /* Locate the major numbers. */
127 for (bmaj = 0; bmaj <= nblkdev; bmaj++)
128 if (bdevsw[bmaj].d_open == sdopen)
129 break;
130 for (cmaj = 0; cmaj <= nchrdev; cmaj++)
131 if (cdevsw[cmaj].d_open == sdopen)
132 break;
133
134 /* Kill off any queued buffers. */
135 s = splbio();
136 while ((bp = BUFQ_FIRST(&sc->sc_bufq)) != NULL) {
137 BUFQ_REMOVE(&sc->sc_bufq, bp);
138 bp->b_error = EIO;
139 bp->b_flags |= B_ERROR;
140 bp->b_resid = bp->b_bcount;
141 biodone(bp);
142 }
143 splx(s);
144
145 /* Nuke the vnodes for any open instances. */
146 mn = DISKUNIT(sc->sc_dv.dv_unit);
147 vdevgone(bmaj, mn, mn + (MAXPARTITIONS - 1), VBLK);
148 vdevgone(cmaj, mn, mn + (MAXPARTITIONS - 1), VCHR);
149
150 /* Detach from the disk list. */
151 disk_detach(&sc->sc_dk);
152
153 #if NRND > 0
154 /* Unhook the entropy source. */
155 rnd_detach_source(&sc->sc_rnd_source);
156 #endif
157
158 /* Flush the device's cache. */
159 if (sc->sc_flush != NULL)
160 if ((*sc->sc_flush)(sc) != 0)
161 printf("%s: unable to flush cache\n",
162 sc->sc_dv.dv_xname);
163 }
164
165 static void
166 ldshutdown(void *cookie)
167 {
168 struct ld_softc *sc;
169 int i;
170
171 for (i = 0; i < ld_cd.cd_ndevs; i++) {
172 if ((sc = device_lookup(&ld_cd, i)) == NULL)
173 continue;
174 if (sc->sc_flush != NULL && (*sc->sc_flush)(sc) != 0)
175 printf("%s: unable to flush cache\n",
176 sc->sc_dv.dv_xname);
177 }
178 }
179
180 int
181 ldopen(dev_t dev, int flags, int fmt, struct proc *p)
182 {
183 struct ld_softc *sc;
184 int unit, part;
185
186 unit = DISKUNIT(dev);
187 if ((sc = device_lookup(&ld_cd, unit))== NULL)
188 return (ENXIO);
189 if ((sc->sc_flags & LDF_ENABLED) == 0)
190 return (ENODEV);
191 part = DISKPART(dev);
192 ldlock(sc);
193
194 if (sc->sc_dk.dk_openmask == 0)
195 ldgetdisklabel(sc);
196
197 /* Check that the partition exists. */
198 if (part != RAW_PART && (part >= sc->sc_dk.dk_label->d_npartitions ||
199 sc->sc_dk.dk_label->d_partitions[part].p_fstype == FS_UNUSED)) {
200 ldunlock(sc);
201 return (ENXIO);
202 }
203
204 /* Ensure only one open at a time. */
205 switch (fmt) {
206 case S_IFCHR:
207 sc->sc_dk.dk_copenmask |= (1 << part);
208 break;
209 case S_IFBLK:
210 sc->sc_dk.dk_bopenmask |= (1 << part);
211 break;
212 }
213 sc->sc_dk.dk_openmask =
214 sc->sc_dk.dk_copenmask | sc->sc_dk.dk_bopenmask;
215
216 ldunlock(sc);
217 return (0);
218 }
219
220 int
221 ldclose(dev_t dev, int flags, int fmt, struct proc *p)
222 {
223 struct ld_softc *sc;
224 int part, unit;
225
226 unit = DISKUNIT(dev);
227 part = DISKPART(dev);
228 sc = device_lookup(&ld_cd, unit);
229 ldlock(sc);
230
231 switch (fmt) {
232 case S_IFCHR:
233 sc->sc_dk.dk_copenmask &= ~(1 << part);
234 break;
235 case S_IFBLK:
236 sc->sc_dk.dk_bopenmask &= ~(1 << part);
237 break;
238 }
239 sc->sc_dk.dk_openmask =
240 sc->sc_dk.dk_copenmask | sc->sc_dk.dk_bopenmask;
241
242 if (sc->sc_dk.dk_openmask == 0 && sc->sc_flush != NULL)
243 if ((*sc->sc_flush)(sc) != 0)
244 printf("%s: unable to flush cache\n",
245 sc->sc_dv.dv_xname);
246
247 ldunlock(sc);
248 return (0);
249 }
250
251 int
252 ldread(dev_t dev, struct uio *uio, int ioflag)
253 {
254
255 return (physio(ldstrategy, NULL, dev, B_READ, ldminphys, uio));
256 }
257
258 int
259 ldwrite(dev_t dev, struct uio *uio, int ioflag)
260 {
261
262 return (physio(ldstrategy, NULL, dev, B_WRITE, ldminphys, uio));
263 }
264
265 int
266 ldioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, struct proc *p)
267 {
268 struct ld_softc *sc;
269 int part, unit, error;
270
271 unit = DISKUNIT(dev);
272 part = DISKPART(dev);
273 sc = device_lookup(&ld_cd, unit);
274 error = 0;
275
276 switch (cmd) {
277 case DIOCGDINFO:
278 memcpy(addr, sc->sc_dk.dk_label, sizeof(struct disklabel));
279 return (0);
280
281 case DIOCGPART:
282 ((struct partinfo *)addr)->disklab = sc->sc_dk.dk_label;
283 ((struct partinfo *)addr)->part =
284 &sc->sc_dk.dk_label->d_partitions[part];
285 break;
286
287 case DIOCWDINFO:
288 case DIOCSDINFO:
289 if ((flag & FWRITE) == 0)
290 return (EBADF);
291
292 if ((error = ldlock(sc)) != 0)
293 return (error);
294 sc->sc_flags |= LDF_LABELLING;
295
296 error = setdisklabel(sc->sc_dk.dk_label,
297 (struct disklabel *)addr, /*sc->sc_dk.dk_openmask : */0,
298 sc->sc_dk.dk_cpulabel);
299 if (error == 0 && cmd == DIOCWDINFO)
300 error = writedisklabel(
301 MAKEDISKDEV(major(dev), DISKUNIT(dev), RAW_PART),
302 ldstrategy, sc->sc_dk.dk_label,
303 sc->sc_dk.dk_cpulabel);
304
305 sc->sc_flags &= ~LDF_LABELLING;
306 ldunlock(sc);
307 break;
308
309 case DIOCWLABEL:
310 if ((flag & FWRITE) == 0)
311 return (EBADF);
312 if (*(int *)addr)
313 sc->sc_flags |= LDF_WLABEL;
314 else
315 sc->sc_flags &= ~LDF_WLABEL;
316 break;
317
318 case DIOCGDEFLABEL:
319 ldgetdefaultlabel(sc, (struct disklabel *)addr);
320 break;
321
322 default:
323 error = ENOTTY;
324 break;
325 }
326
327 return (error);
328 }
329
330 void
331 ldstrategy(struct buf *bp)
332 {
333 struct ld_softc *sc;
334 int s;
335
336 sc = device_lookup(&ld_cd, DISKUNIT(bp->b_dev));
337
338 s = splbio();
339 if (sc->sc_queuecnt == sc->sc_maxqueuecnt) {
340 BUFQ_INSERT_TAIL(&sc->sc_bufq, bp);
341 splx(s);
342 return;
343 }
344 splx(s);
345 ldstart(sc, bp);
346 }
347
348 static int
349 ldstart(struct ld_softc *sc, struct buf *bp)
350 {
351 struct disklabel *lp;
352 int part, s, rv;
353
354 if ((sc->sc_flags & LDF_DRAIN) != 0) {
355 bp->b_error = EIO;
356 bp->b_flags |= B_ERROR;
357 bp->b_resid = bp->b_bcount;
358 biodone(bp);
359 return (-1);
360 }
361
362 part = DISKPART(bp->b_dev);
363 lp = sc->sc_dk.dk_label;
364
365 /*
366 * The transfer must be a whole number of blocks and the offset must
367 * not be negative.
368 */
369 if ((bp->b_bcount % lp->d_secsize) != 0 || bp->b_blkno < 0) {
370 bp->b_flags |= B_ERROR;
371 biodone(bp);
372 return (-1);
373 }
374
375 /*
376 * If it's a null transfer, return.
377 */
378 if (bp->b_bcount == 0) {
379 bp->b_resid = bp->b_bcount;
380 biodone(bp);
381 return (-1);
382 }
383
384 /*
385 * Do bounds checking and adjust the transfer. If error, process.
386 * If past the end of partition, just return.
387 */
388 if (part != RAW_PART &&
389 bounds_check_with_label(bp, lp,
390 (sc->sc_flags & (LDF_WLABEL | LDF_LABELLING)) != 0) <= 0) {
391 bp->b_resid = bp->b_bcount;
392 biodone(bp);
393 return (-1);
394 }
395
396 /*
397 * Convert the logical block number to a physical one and put it in
398 * terms of the device's logical block size.
399 */
400 if (lp->d_secsize >= DEV_BSIZE)
401 bp->b_rawblkno = bp->b_blkno / (lp->d_secsize / DEV_BSIZE);
402 else
403 bp->b_rawblkno = bp->b_blkno * (DEV_BSIZE / lp->d_secsize);
404
405 if (bp->b_dev != RAW_PART)
406 bp->b_rawblkno += lp->d_partitions[part].p_offset;
407
408 s = splbio();
409 disk_busy(&sc->sc_dk);
410 sc->sc_queuecnt++;
411 splx(s);
412
413 if ((rv = (*sc->sc_start)(sc, bp)) != 0) {
414 bp->b_error = rv;
415 bp->b_flags |= B_ERROR;
416 bp->b_resid = bp->b_bcount;
417 s = splbio();
418 lddone(sc, bp);
419 splx(s);
420 }
421
422 return (0);
423 }
424
425 void
426 lddone(struct ld_softc *sc, struct buf *bp)
427 {
428
429 if ((bp->b_flags & B_ERROR) != 0) {
430 diskerr(bp, "ld", "error", LOG_PRINTF, 0, sc->sc_dk.dk_label);
431 printf("\n");
432 }
433
434 disk_unbusy(&sc->sc_dk, bp->b_bcount - bp->b_resid);
435 #if NRND > 0
436 rnd_add_uint32(&sc->sc_rnd_source, bp->b_rawblkno);
437 #endif
438 biodone(bp);
439 if (--sc->sc_queuecnt == 0 && (sc->sc_flags & LDF_DRAIN) != 0)
440 wakeup(&sc->sc_queuecnt);
441
442 while ((bp = BUFQ_FIRST(&sc->sc_bufq)) != NULL) {
443 BUFQ_REMOVE(&sc->sc_bufq, bp);
444 if (!ldstart(sc, bp))
445 break;
446 }
447 }
448
449 int
450 ldsize(dev_t dev)
451 {
452 struct ld_softc *sc;
453 int part, unit, omask, size;
454
455 unit = DISKUNIT(dev);
456 if ((sc = device_lookup(&ld_cd, unit)) == NULL)
457 return (ENODEV);
458 if ((sc->sc_flags & LDF_ENABLED) == 0)
459 return (ENODEV);
460 part = DISKPART(dev);
461
462 omask = sc->sc_dk.dk_openmask & (1 << part);
463
464 if (omask == 0 && ldopen(dev, 0, S_IFBLK, NULL) != 0)
465 return (-1);
466 else if (sc->sc_dk.dk_label->d_partitions[part].p_fstype != FS_SWAP)
467 size = -1;
468 else
469 size = sc->sc_dk.dk_label->d_partitions[part].p_size *
470 (sc->sc_dk.dk_label->d_secsize / DEV_BSIZE);
471 if (omask == 0 && ldclose(dev, 0, S_IFBLK, NULL) != 0)
472 return (-1);
473
474 return (size);
475 }
476
477 /*
478 * Load the label information from the specified device.
479 */
480 static void
481 ldgetdisklabel(struct ld_softc *sc)
482 {
483 const char *errstring;
484
485 ldgetdefaultlabel(sc, sc->sc_dk.dk_label);
486
487 /* Call the generic disklabel extraction routine. */
488 errstring = readdisklabel(MAKEDISKDEV(0, sc->sc_dv.dv_unit, RAW_PART),
489 ldstrategy, sc->sc_dk.dk_label, sc->sc_dk.dk_cpulabel);
490 if (errstring != NULL)
491 printf("%s: %s\n", sc->sc_dv.dv_xname, errstring);
492 }
493
494 /*
495 * Construct a ficticious label.
496 */
497 static void
498 ldgetdefaultlabel(struct ld_softc *sc, struct disklabel *lp)
499 {
500
501 memset(lp, 0, sizeof(struct disklabel));
502
503 lp->d_secsize = sc->sc_secsize;
504 lp->d_ntracks = sc->sc_nheads;
505 lp->d_nsectors = sc->sc_nsectors;
506 lp->d_ncylinders = sc->sc_ncylinders;
507 lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors;
508 lp->d_type = DTYPE_LD;
509 strcpy(lp->d_typename, "unknown");
510 strcpy(lp->d_packname, "fictitious");
511 lp->d_secperunit = sc->sc_secperunit;
512 lp->d_rpm = 7200;
513 lp->d_interleave = 1;
514 lp->d_flags = 0;
515
516 lp->d_partitions[RAW_PART].p_offset = 0;
517 lp->d_partitions[RAW_PART].p_size =
518 lp->d_secperunit * (lp->d_secsize / DEV_BSIZE);
519 lp->d_partitions[RAW_PART].p_fstype = FS_UNUSED;
520 lp->d_npartitions = RAW_PART + 1;
521
522 lp->d_magic = DISKMAGIC;
523 lp->d_magic2 = DISKMAGIC;
524 lp->d_checksum = dkcksum(lp);
525 }
526
527 /*
528 * Wait interruptibly for an exclusive lock.
529 *
530 * XXX Several drivers do this; it should be abstracted and made MP-safe.
531 */
532 static int
533 ldlock(struct ld_softc *sc)
534 {
535 int error;
536
537 while ((sc->sc_flags & LDF_LKHELD) != 0) {
538 sc->sc_flags |= LDF_LKWANTED;
539 if ((error = tsleep(sc, PRIBIO | PCATCH, "ldlck", 0)) != 0)
540 return (error);
541 }
542 sc->sc_flags |= LDF_LKHELD;
543 return (0);
544 }
545
546 /*
547 * Unlock and wake up any waiters.
548 */
549 static void
550 ldunlock(struct ld_softc *sc)
551 {
552
553 sc->sc_flags &= ~LDF_LKHELD;
554 if ((sc->sc_flags & LDF_LKWANTED) != 0) {
555 sc->sc_flags &= ~LDF_LKWANTED;
556 wakeup(sc);
557 }
558 }
559
560 /*
561 * Take a dump.
562 */
563 int
564 lddump(dev_t dev, daddr_t blkno, caddr_t va, size_t size)
565 {
566 struct ld_softc *sc;
567 struct disklabel *lp;
568 int unit, part, nsects, sectoff, towrt, nblk, maxblkcnt, rv;
569 static int dumping;
570
571 /* Check if recursive dump; if so, punt. */
572 if (dumping)
573 return (EFAULT);
574 dumping = 1;
575 if (sc->sc_dump == NULL)
576 return (ENXIO);
577
578 unit = DISKUNIT(dev);
579 if ((sc = device_lookup(&ld_cd, unit)) == NULL)
580 return (ENXIO);
581 if ((sc->sc_flags & LDF_ENABLED) == 0)
582 return (ENODEV);
583 part = DISKPART(dev);
584
585 /* Convert to disk sectors. Request must be a multiple of size. */
586 lp = sc->sc_dk.dk_label;
587 if ((size % lp->d_secsize) != 0)
588 return (EFAULT);
589 towrt = size / lp->d_secsize;
590 blkno = dbtob(blkno) / lp->d_secsize; /* blkno in DEV_BSIZE units */
591
592 nsects = lp->d_partitions[part].p_size;
593 sectoff = lp->d_partitions[part].p_offset;
594
595 /* Check transfer bounds against partition size. */
596 if ((blkno < 0) || ((blkno + towrt) > nsects))
597 return (EINVAL);
598
599 /* Offset block number to start of partition. */
600 blkno += sectoff;
601
602 /* Start dumping and return when done. */
603 maxblkcnt = sc->sc_maxxfer / sc->sc_secsize;
604 while (towrt > 0) {
605 nblk = max(maxblkcnt, towrt);
606
607 if ((rv = (*sc->sc_dump)(sc, va, blkno, nblk)) != 0)
608 return (rv);
609
610 towrt -= nblk;
611 blkno += nblk;
612 va += nblk * sc->sc_secsize;
613 }
614
615 dumping = 0;
616 return (0);
617 }
618
619 /*
620 * Adjust the size of a transfer.
621 */
622 static void
623 ldminphys(struct buf *bp)
624 {
625 struct ld_softc *sc;
626
627 sc = device_lookup(&ld_cd, DISKUNIT(bp->b_dev));
628
629 if (bp->b_bcount > sc->sc_maxxfer)
630 bp->b_bcount = sc->sc_maxxfer;
631 minphys(bp);
632 }
633