dk.c revision 1.64.2.5 1 /* $NetBSD: dk.c,v 1.64.2.5 2017/12/03 11:37:00 jdolecek Exp $ */
2
3 /*-
4 * Copyright (c) 2004, 2005, 2006, 2007 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Jason R. Thorpe.
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 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: dk.c,v 1.64.2.5 2017/12/03 11:37:00 jdolecek Exp $");
34
35 #ifdef _KERNEL_OPT
36 #include "opt_dkwedge.h"
37 #endif
38
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/proc.h>
42 #include <sys/errno.h>
43 #include <sys/pool.h>
44 #include <sys/ioctl.h>
45 #include <sys/disklabel.h>
46 #include <sys/disk.h>
47 #include <sys/fcntl.h>
48 #include <sys/buf.h>
49 #include <sys/bufq.h>
50 #include <sys/vnode.h>
51 #include <sys/stat.h>
52 #include <sys/conf.h>
53 #include <sys/callout.h>
54 #include <sys/kernel.h>
55 #include <sys/malloc.h>
56 #include <sys/device.h>
57 #include <sys/kauth.h>
58
59 #include <miscfs/specfs/specdev.h>
60
61 MALLOC_DEFINE(M_DKWEDGE, "dkwedge", "Disk wedge structures");
62
63 typedef enum {
64 DKW_STATE_LARVAL = 0,
65 DKW_STATE_RUNNING = 1,
66 DKW_STATE_DYING = 2,
67 DKW_STATE_DEAD = 666
68 } dkwedge_state_t;
69
70 struct dkwedge_softc {
71 device_t sc_dev; /* pointer to our pseudo-device */
72 struct cfdata sc_cfdata; /* our cfdata structure */
73 uint8_t sc_wname[128]; /* wedge name (Unicode, UTF-8) */
74
75 dkwedge_state_t sc_state; /* state this wedge is in */
76
77 struct disk *sc_parent; /* parent disk */
78 daddr_t sc_offset; /* LBA offset of wedge in parent */
79 uint64_t sc_size; /* size of wedge in blocks */
80 char sc_ptype[32]; /* partition type */
81 dev_t sc_pdev; /* cached parent's dev_t */
82 /* link on parent's wedge list */
83 LIST_ENTRY(dkwedge_softc) sc_plink;
84
85 struct disk sc_dk; /* our own disk structure */
86 struct bufq_state *sc_bufq; /* buffer queue */
87 struct callout sc_restart_ch; /* callout to restart I/O */
88
89 kmutex_t sc_iolock;
90 kcondvar_t sc_dkdrn;
91 u_int sc_iopend; /* I/Os pending */
92 int sc_flags; /* flags (sc_iolock) */
93 };
94
95 #define DK_F_WAIT_DRAIN 0x0001 /* waiting for I/O to drain */
96
97 static void dkstart(struct dkwedge_softc *);
98 static void dkiodone(struct buf *);
99 static void dkrestart(void *);
100 static void dkminphys(struct buf *);
101
102 static int dklastclose(struct dkwedge_softc *);
103 static int dkwedge_cleanup_parent(struct dkwedge_softc *, int);
104 static int dkwedge_detach(device_t, int);
105 static void dkwedge_delall1(struct disk *, bool);
106 static int dkwedge_del1(struct dkwedge_info *, int);
107 static int dk_open_parent(dev_t, int, struct vnode **);
108 static int dk_close_parent(struct vnode *, int);
109
110 static dev_type_open(dkopen);
111 static dev_type_close(dkclose);
112 static dev_type_read(dkread);
113 static dev_type_write(dkwrite);
114 static dev_type_ioctl(dkioctl);
115 static dev_type_strategy(dkstrategy);
116 static dev_type_dump(dkdump);
117 static dev_type_size(dksize);
118 static dev_type_discard(dkdiscard);
119
120 const struct bdevsw dk_bdevsw = {
121 .d_open = dkopen,
122 .d_close = dkclose,
123 .d_strategy = dkstrategy,
124 .d_ioctl = dkioctl,
125 .d_dump = dkdump,
126 .d_psize = dksize,
127 .d_discard = dkdiscard,
128 .d_flag = D_DISK | D_MPSAFE
129 };
130
131 const struct cdevsw dk_cdevsw = {
132 .d_open = dkopen,
133 .d_close = dkclose,
134 .d_read = dkread,
135 .d_write = dkwrite,
136 .d_ioctl = dkioctl,
137 .d_stop = nostop,
138 .d_tty = notty,
139 .d_poll = nopoll,
140 .d_mmap = nommap,
141 .d_kqfilter = nokqfilter,
142 .d_discard = dkdiscard,
143 .d_flag = D_DISK | D_MPSAFE
144 };
145
146 const struct dkdriver dk_dkdriver = {
147 .d_strategy = dkstrategy,
148 .d_minphys = dkminphys,
149 };
150
151 static struct dkwedge_softc **dkwedges;
152 static u_int ndkwedges;
153 static krwlock_t dkwedges_lock;
154
155 static LIST_HEAD(, dkwedge_discovery_method) dkwedge_discovery_methods;
156 static krwlock_t dkwedge_discovery_methods_lock;
157
158 /*
159 * dkwedge_match:
160 *
161 * Autoconfiguration match function for pseudo-device glue.
162 */
163 static int
164 dkwedge_match(device_t parent, cfdata_t match,
165 void *aux)
166 {
167
168 /* Pseudo-device; always present. */
169 return (1);
170 }
171
172 /*
173 * dkwedge_attach:
174 *
175 * Autoconfiguration attach function for pseudo-device glue.
176 */
177 static void
178 dkwedge_attach(device_t parent, device_t self,
179 void *aux)
180 {
181
182 if (!pmf_device_register(self, NULL, NULL))
183 aprint_error_dev(self, "couldn't establish power handler\n");
184 }
185
186 CFDRIVER_DECL(dk, DV_DISK, NULL);
187 CFATTACH_DECL3_NEW(dk, 0,
188 dkwedge_match, dkwedge_attach, dkwedge_detach, NULL, NULL, NULL,
189 DVF_DETACH_SHUTDOWN);
190
191 /*
192 * dkwedge_wait_drain:
193 *
194 * Wait for I/O on the wedge to drain.
195 */
196 static void
197 dkwedge_wait_drain(struct dkwedge_softc *sc)
198 {
199
200 mutex_enter(&sc->sc_iolock);
201 while (sc->sc_iopend != 0) {
202 sc->sc_flags |= DK_F_WAIT_DRAIN;
203 cv_wait(&sc->sc_dkdrn, &sc->sc_iolock);
204 }
205 mutex_exit(&sc->sc_iolock);
206 }
207
208 /*
209 * dkwedge_compute_pdev:
210 *
211 * Compute the parent disk's dev_t.
212 */
213 static int
214 dkwedge_compute_pdev(const char *pname, dev_t *pdevp, enum vtype type)
215 {
216 const char *name, *cp;
217 devmajor_t pmaj;
218 int punit;
219 char devname[16];
220
221 name = pname;
222 switch (type) {
223 case VBLK:
224 pmaj = devsw_name2blk(name, devname, sizeof(devname));
225 break;
226 case VCHR:
227 pmaj = devsw_name2chr(name, devname, sizeof(devname));
228 break;
229 default:
230 pmaj = NODEVMAJOR;
231 break;
232 }
233 if (pmaj == NODEVMAJOR)
234 return (ENODEV);
235
236 name += strlen(devname);
237 for (cp = name, punit = 0; *cp >= '0' && *cp <= '9'; cp++)
238 punit = (punit * 10) + (*cp - '0');
239 if (cp == name) {
240 /* Invalid parent disk name. */
241 return (ENODEV);
242 }
243
244 *pdevp = MAKEDISKDEV(pmaj, punit, RAW_PART);
245
246 return (0);
247 }
248
249 /*
250 * dkwedge_array_expand:
251 *
252 * Expand the dkwedges array.
253 */
254 static void
255 dkwedge_array_expand(void)
256 {
257 int newcnt = ndkwedges + 16;
258 struct dkwedge_softc **newarray, **oldarray;
259
260 newarray = malloc(newcnt * sizeof(*newarray), M_DKWEDGE,
261 M_WAITOK|M_ZERO);
262 if ((oldarray = dkwedges) != NULL)
263 memcpy(newarray, dkwedges, ndkwedges * sizeof(*newarray));
264 dkwedges = newarray;
265 ndkwedges = newcnt;
266 if (oldarray != NULL)
267 free(oldarray, M_DKWEDGE);
268 }
269
270 static void
271 dk_set_geometry(struct dkwedge_softc *sc, struct disk *pdk)
272 {
273 struct disk *dk = &sc->sc_dk;
274 struct disk_geom *dg = &dk->dk_geom;
275
276 memset(dg, 0, sizeof(*dg));
277
278 dg->dg_secperunit = sc->sc_size;
279 dg->dg_secsize = DEV_BSIZE << pdk->dk_blkshift;
280
281 /* fake numbers, 1 cylinder is 1 MB with default sector size */
282 dg->dg_nsectors = 32;
283 dg->dg_ntracks = 64;
284 dg->dg_ncylinders = dg->dg_secperunit / (dg->dg_nsectors * dg->dg_ntracks);
285
286 disk_set_info(sc->sc_dev, dk, NULL);
287 }
288
289 /*
290 * dkwedge_add: [exported function]
291 *
292 * Add a disk wedge based on the provided information.
293 *
294 * The incoming dkw_devname[] is ignored, instead being
295 * filled in and returned to the caller.
296 */
297 int
298 dkwedge_add(struct dkwedge_info *dkw)
299 {
300 struct dkwedge_softc *sc, *lsc;
301 struct disk *pdk;
302 u_int unit;
303 int error;
304 dev_t pdev;
305
306 dkw->dkw_parent[sizeof(dkw->dkw_parent) - 1] = '\0';
307 pdk = disk_find(dkw->dkw_parent);
308 if (pdk == NULL)
309 return (ENODEV);
310
311 error = dkwedge_compute_pdev(pdk->dk_name, &pdev, VBLK);
312 if (error)
313 return (error);
314
315 if (dkw->dkw_offset < 0)
316 return (EINVAL);
317
318 sc = malloc(sizeof(*sc), M_DKWEDGE, M_WAITOK|M_ZERO);
319 sc->sc_state = DKW_STATE_LARVAL;
320 sc->sc_parent = pdk;
321 sc->sc_pdev = pdev;
322 sc->sc_offset = dkw->dkw_offset;
323 sc->sc_size = dkw->dkw_size;
324
325 memcpy(sc->sc_wname, dkw->dkw_wname, sizeof(sc->sc_wname));
326 sc->sc_wname[sizeof(sc->sc_wname) - 1] = '\0';
327
328 memcpy(sc->sc_ptype, dkw->dkw_ptype, sizeof(sc->sc_ptype));
329 sc->sc_ptype[sizeof(sc->sc_ptype) - 1] = '\0';
330
331 bufq_alloc(&sc->sc_bufq, "fcfs", 0);
332
333 callout_init(&sc->sc_restart_ch, 0);
334 callout_setfunc(&sc->sc_restart_ch, dkrestart, sc);
335
336 mutex_init(&sc->sc_iolock, MUTEX_DEFAULT, IPL_BIO);
337 cv_init(&sc->sc_dkdrn, "dkdrn");
338
339 /*
340 * Wedge will be added; increment the wedge count for the parent.
341 * Only allow this to happend if RAW_PART is the only thing open.
342 */
343 mutex_enter(&pdk->dk_openlock);
344 if (pdk->dk_openmask & ~(1 << RAW_PART))
345 error = EBUSY;
346 else {
347 /* Check for wedge overlap. */
348 LIST_FOREACH(lsc, &pdk->dk_wedges, sc_plink) {
349 daddr_t lastblk = sc->sc_offset + sc->sc_size - 1;
350 daddr_t llastblk = lsc->sc_offset + lsc->sc_size - 1;
351
352 if (sc->sc_offset >= lsc->sc_offset &&
353 sc->sc_offset <= llastblk) {
354 /* Overlaps the tail of the existing wedge. */
355 break;
356 }
357 if (lastblk >= lsc->sc_offset &&
358 lastblk <= llastblk) {
359 /* Overlaps the head of the existing wedge. */
360 break;
361 }
362 }
363 if (lsc != NULL) {
364 if (sc->sc_offset == lsc->sc_offset &&
365 sc->sc_size == lsc->sc_size &&
366 strcmp(sc->sc_wname, lsc->sc_wname) == 0)
367 error = EEXIST;
368 else
369 error = EINVAL;
370 } else {
371 pdk->dk_nwedges++;
372 LIST_INSERT_HEAD(&pdk->dk_wedges, sc, sc_plink);
373 }
374 }
375 mutex_exit(&pdk->dk_openlock);
376 if (error) {
377 cv_destroy(&sc->sc_dkdrn);
378 mutex_destroy(&sc->sc_iolock);
379 bufq_free(sc->sc_bufq);
380 free(sc, M_DKWEDGE);
381 return (error);
382 }
383
384 /* Fill in our cfdata for the pseudo-device glue. */
385 sc->sc_cfdata.cf_name = dk_cd.cd_name;
386 sc->sc_cfdata.cf_atname = dk_ca.ca_name;
387 /* sc->sc_cfdata.cf_unit set below */
388 sc->sc_cfdata.cf_fstate = FSTATE_STAR;
389
390 /* Insert the larval wedge into the array. */
391 rw_enter(&dkwedges_lock, RW_WRITER);
392 for (error = 0;;) {
393 struct dkwedge_softc **scpp;
394
395 /*
396 * Check for a duplicate wname while searching for
397 * a slot.
398 */
399 for (scpp = NULL, unit = 0; unit < ndkwedges; unit++) {
400 if (dkwedges[unit] == NULL) {
401 if (scpp == NULL) {
402 scpp = &dkwedges[unit];
403 sc->sc_cfdata.cf_unit = unit;
404 }
405 } else {
406 /* XXX Unicode. */
407 if (strcmp(dkwedges[unit]->sc_wname,
408 sc->sc_wname) == 0) {
409 error = EEXIST;
410 break;
411 }
412 }
413 }
414 if (error)
415 break;
416 KASSERT(unit == ndkwedges);
417 if (scpp == NULL)
418 dkwedge_array_expand();
419 else {
420 KASSERT(scpp == &dkwedges[sc->sc_cfdata.cf_unit]);
421 *scpp = sc;
422 break;
423 }
424 }
425 rw_exit(&dkwedges_lock);
426 if (error) {
427 mutex_enter(&pdk->dk_openlock);
428 pdk->dk_nwedges--;
429 LIST_REMOVE(sc, sc_plink);
430 mutex_exit(&pdk->dk_openlock);
431
432 cv_destroy(&sc->sc_dkdrn);
433 mutex_destroy(&sc->sc_iolock);
434 bufq_free(sc->sc_bufq);
435 free(sc, M_DKWEDGE);
436 return (error);
437 }
438
439 /*
440 * Now that we know the unit #, attach a pseudo-device for
441 * this wedge instance. This will provide us with the
442 * device_t necessary for glue to other parts of the system.
443 *
444 * This should never fail, unless we're almost totally out of
445 * memory.
446 */
447 if ((sc->sc_dev = config_attach_pseudo(&sc->sc_cfdata)) == NULL) {
448 aprint_error("%s%u: unable to attach pseudo-device\n",
449 sc->sc_cfdata.cf_name, sc->sc_cfdata.cf_unit);
450
451 rw_enter(&dkwedges_lock, RW_WRITER);
452 dkwedges[sc->sc_cfdata.cf_unit] = NULL;
453 rw_exit(&dkwedges_lock);
454
455 mutex_enter(&pdk->dk_openlock);
456 pdk->dk_nwedges--;
457 LIST_REMOVE(sc, sc_plink);
458 mutex_exit(&pdk->dk_openlock);
459
460 cv_destroy(&sc->sc_dkdrn);
461 mutex_destroy(&sc->sc_iolock);
462 bufq_free(sc->sc_bufq);
463 free(sc, M_DKWEDGE);
464 return (ENOMEM);
465 }
466
467 /* Return the devname to the caller. */
468 strlcpy(dkw->dkw_devname, device_xname(sc->sc_dev),
469 sizeof(dkw->dkw_devname));
470
471 /*
472 * XXX Really ought to make the disk_attach() and the changing
473 * of state to RUNNING atomic.
474 */
475
476 disk_init(&sc->sc_dk, device_xname(sc->sc_dev), &dk_dkdriver);
477 dk_set_geometry(sc, pdk);
478 disk_attach(&sc->sc_dk);
479
480 /* Disk wedge is ready for use! */
481 sc->sc_state = DKW_STATE_RUNNING;
482
483 /* Announce our arrival. */
484 aprint_normal(
485 "%s at %s: \"%s\", %"PRIu64" blocks at %"PRId64", type: %s\n",
486 device_xname(sc->sc_dev), pdk->dk_name,
487 sc->sc_wname, /* XXX Unicode */
488 sc->sc_size, sc->sc_offset,
489 sc->sc_ptype[0] == '\0' ? "<unknown>" : sc->sc_ptype);
490
491 return (0);
492 }
493
494 /*
495 * dkwedge_find:
496 *
497 * Lookup a disk wedge based on the provided information.
498 * NOTE: We look up the wedge based on the wedge devname,
499 * not wname.
500 *
501 * Return NULL if the wedge is not found, otherwise return
502 * the wedge's softc. Assign the wedge's unit number to unitp
503 * if unitp is not NULL.
504 */
505 static struct dkwedge_softc *
506 dkwedge_find(struct dkwedge_info *dkw, u_int *unitp)
507 {
508 struct dkwedge_softc *sc = NULL;
509 u_int unit;
510
511 /* Find our softc. */
512 dkw->dkw_devname[sizeof(dkw->dkw_devname) - 1] = '\0';
513 rw_enter(&dkwedges_lock, RW_READER);
514 for (unit = 0; unit < ndkwedges; unit++) {
515 if ((sc = dkwedges[unit]) != NULL &&
516 strcmp(device_xname(sc->sc_dev), dkw->dkw_devname) == 0 &&
517 strcmp(sc->sc_parent->dk_name, dkw->dkw_parent) == 0) {
518 break;
519 }
520 }
521 rw_exit(&dkwedges_lock);
522 if (unit == ndkwedges)
523 return NULL;
524
525 if (unitp != NULL)
526 *unitp = unit;
527
528 return sc;
529 }
530
531 /*
532 * dkwedge_del: [exported function]
533 *
534 * Delete a disk wedge based on the provided information.
535 * NOTE: We look up the wedge based on the wedge devname,
536 * not wname.
537 */
538 int
539 dkwedge_del(struct dkwedge_info *dkw)
540 {
541 return dkwedge_del1(dkw, 0);
542 }
543
544 int
545 dkwedge_del1(struct dkwedge_info *dkw, int flags)
546 {
547 struct dkwedge_softc *sc = NULL;
548
549 /* Find our softc. */
550 if ((sc = dkwedge_find(dkw, NULL)) == NULL)
551 return (ESRCH);
552
553 return config_detach(sc->sc_dev, flags);
554 }
555
556 static int
557 dkwedge_cleanup_parent(struct dkwedge_softc *sc, int flags)
558 {
559 struct disk *dk = &sc->sc_dk;
560 int rc;
561
562 rc = 0;
563 mutex_enter(&dk->dk_openlock);
564 if (dk->dk_openmask == 0)
565 /* nothing to do */
566 mutex_exit(&dk->dk_openlock);
567 else if ((flags & DETACH_FORCE) == 0) {
568 rc = EBUSY;
569 mutex_exit(&dk->dk_openlock);
570 } else {
571 mutex_enter(&sc->sc_parent->dk_rawlock);
572 rc = dklastclose(sc); /* releases locks */
573 }
574
575 return rc;
576 }
577
578 /*
579 * dkwedge_detach:
580 *
581 * Autoconfiguration detach function for pseudo-device glue.
582 */
583 static int
584 dkwedge_detach(device_t self, int flags)
585 {
586 struct dkwedge_softc *sc = NULL;
587 u_int unit;
588 int bmaj, cmaj, rc;
589
590 rw_enter(&dkwedges_lock, RW_WRITER);
591 for (unit = 0; unit < ndkwedges; unit++) {
592 if ((sc = dkwedges[unit]) != NULL && sc->sc_dev == self)
593 break;
594 }
595 if (unit == ndkwedges)
596 rc = ENXIO;
597 else if ((rc = dkwedge_cleanup_parent(sc, flags)) == 0) {
598 /* Mark the wedge as dying. */
599 sc->sc_state = DKW_STATE_DYING;
600 }
601 rw_exit(&dkwedges_lock);
602
603 if (rc != 0)
604 return rc;
605
606 pmf_device_deregister(self);
607
608 /* Locate the wedge major numbers. */
609 bmaj = bdevsw_lookup_major(&dk_bdevsw);
610 cmaj = cdevsw_lookup_major(&dk_cdevsw);
611
612 /* Kill any pending restart. */
613 callout_stop(&sc->sc_restart_ch);
614
615 /*
616 * dkstart() will kill any queued buffers now that the
617 * state of the wedge is not RUNNING. Once we've done
618 * that, wait for any other pending I/O to complete.
619 */
620 dkstart(sc);
621 dkwedge_wait_drain(sc);
622
623 /* Nuke the vnodes for any open instances. */
624 vdevgone(bmaj, unit, unit, VBLK);
625 vdevgone(cmaj, unit, unit, VCHR);
626
627 /* Clean up the parent. */
628 dkwedge_cleanup_parent(sc, flags | DETACH_FORCE);
629
630 /* Announce our departure. */
631 aprint_normal("%s at %s (%s) deleted\n", device_xname(sc->sc_dev),
632 sc->sc_parent->dk_name,
633 sc->sc_wname); /* XXX Unicode */
634
635 mutex_enter(&sc->sc_parent->dk_openlock);
636 sc->sc_parent->dk_nwedges--;
637 LIST_REMOVE(sc, sc_plink);
638 mutex_exit(&sc->sc_parent->dk_openlock);
639
640 /* Delete our buffer queue. */
641 bufq_free(sc->sc_bufq);
642
643 /* Detach from the disk list. */
644 disk_detach(&sc->sc_dk);
645 disk_destroy(&sc->sc_dk);
646
647 /* Poof. */
648 rw_enter(&dkwedges_lock, RW_WRITER);
649 dkwedges[unit] = NULL;
650 sc->sc_state = DKW_STATE_DEAD;
651 rw_exit(&dkwedges_lock);
652
653 mutex_destroy(&sc->sc_iolock);
654 cv_destroy(&sc->sc_dkdrn);
655
656 free(sc, M_DKWEDGE);
657
658 return 0;
659 }
660
661 /*
662 * dkwedge_delall: [exported function]
663 *
664 * Delete all of the wedges on the specified disk. Used when
665 * a disk is being detached.
666 */
667 void
668 dkwedge_delall(struct disk *pdk)
669 {
670 dkwedge_delall1(pdk, false);
671 }
672
673 static void
674 dkwedge_delall1(struct disk *pdk, bool idleonly)
675 {
676 struct dkwedge_info dkw;
677 struct dkwedge_softc *sc;
678 int flags;
679
680 flags = DETACH_QUIET;
681 if (!idleonly) flags |= DETACH_FORCE;
682
683 for (;;) {
684 mutex_enter(&pdk->dk_openlock);
685 LIST_FOREACH(sc, &pdk->dk_wedges, sc_plink) {
686 if (!idleonly || sc->sc_dk.dk_openmask == 0)
687 break;
688 }
689 if (sc == NULL) {
690 KASSERT(idleonly || pdk->dk_nwedges == 0);
691 mutex_exit(&pdk->dk_openlock);
692 return;
693 }
694 strlcpy(dkw.dkw_parent, pdk->dk_name, sizeof(dkw.dkw_parent));
695 strlcpy(dkw.dkw_devname, device_xname(sc->sc_dev),
696 sizeof(dkw.dkw_devname));
697 mutex_exit(&pdk->dk_openlock);
698 (void) dkwedge_del1(&dkw, flags);
699 }
700 }
701
702 /*
703 * dkwedge_list: [exported function]
704 *
705 * List all of the wedges on a particular disk.
706 */
707 int
708 dkwedge_list(struct disk *pdk, struct dkwedge_list *dkwl, struct lwp *l)
709 {
710 struct uio uio;
711 struct iovec iov;
712 struct dkwedge_softc *sc;
713 struct dkwedge_info dkw;
714 int error = 0;
715
716 iov.iov_base = dkwl->dkwl_buf;
717 iov.iov_len = dkwl->dkwl_bufsize;
718
719 uio.uio_iov = &iov;
720 uio.uio_iovcnt = 1;
721 uio.uio_offset = 0;
722 uio.uio_resid = dkwl->dkwl_bufsize;
723 uio.uio_rw = UIO_READ;
724 KASSERT(l == curlwp);
725 uio.uio_vmspace = l->l_proc->p_vmspace;
726
727 dkwl->dkwl_ncopied = 0;
728
729 mutex_enter(&pdk->dk_openlock);
730 LIST_FOREACH(sc, &pdk->dk_wedges, sc_plink) {
731 if (uio.uio_resid < sizeof(dkw))
732 break;
733
734 if (sc->sc_state != DKW_STATE_RUNNING)
735 continue;
736
737 strlcpy(dkw.dkw_devname, device_xname(sc->sc_dev),
738 sizeof(dkw.dkw_devname));
739 memcpy(dkw.dkw_wname, sc->sc_wname, sizeof(dkw.dkw_wname));
740 dkw.dkw_wname[sizeof(dkw.dkw_wname) - 1] = '\0';
741 strlcpy(dkw.dkw_parent, sc->sc_parent->dk_name,
742 sizeof(dkw.dkw_parent));
743 dkw.dkw_offset = sc->sc_offset;
744 dkw.dkw_size = sc->sc_size;
745 strlcpy(dkw.dkw_ptype, sc->sc_ptype, sizeof(dkw.dkw_ptype));
746
747 error = uiomove(&dkw, sizeof(dkw), &uio);
748 if (error)
749 break;
750 dkwl->dkwl_ncopied++;
751 }
752 dkwl->dkwl_nwedges = pdk->dk_nwedges;
753 mutex_exit(&pdk->dk_openlock);
754
755 return (error);
756 }
757
758 device_t
759 dkwedge_find_by_wname(const char *wname)
760 {
761 device_t dv = NULL;
762 struct dkwedge_softc *sc;
763 int i;
764
765 rw_enter(&dkwedges_lock, RW_WRITER);
766 for (i = 0; i < ndkwedges; i++) {
767 if ((sc = dkwedges[i]) == NULL)
768 continue;
769 if (strcmp(sc->sc_wname, wname) == 0) {
770 if (dv != NULL) {
771 printf(
772 "WARNING: double match for wedge name %s "
773 "(%s, %s)\n", wname, device_xname(dv),
774 device_xname(sc->sc_dev));
775 continue;
776 }
777 dv = sc->sc_dev;
778 }
779 }
780 rw_exit(&dkwedges_lock);
781 return dv;
782 }
783
784 device_t
785 dkwedge_find_by_parent(const char *name, size_t *i)
786 {
787 rw_enter(&dkwedges_lock, RW_WRITER);
788 for (; *i < (size_t)ndkwedges; (*i)++) {
789 struct dkwedge_softc *sc;
790 if ((sc = dkwedges[*i]) == NULL)
791 continue;
792 if (strcmp(sc->sc_parent->dk_name, name) != 0)
793 continue;
794 rw_exit(&dkwedges_lock);
795 return sc->sc_dev;
796 }
797 rw_exit(&dkwedges_lock);
798 return NULL;
799 }
800
801 void
802 dkwedge_print_wnames(void)
803 {
804 struct dkwedge_softc *sc;
805 int i;
806
807 rw_enter(&dkwedges_lock, RW_WRITER);
808 for (i = 0; i < ndkwedges; i++) {
809 if ((sc = dkwedges[i]) == NULL)
810 continue;
811 printf(" wedge:%s", sc->sc_wname);
812 }
813 rw_exit(&dkwedges_lock);
814 }
815
816 /*
817 * We need a dummy object to stuff into the dkwedge discovery method link
818 * set to ensure that there is always at least one object in the set.
819 */
820 static struct dkwedge_discovery_method dummy_discovery_method;
821 __link_set_add_bss(dkwedge_methods, dummy_discovery_method);
822
823 /*
824 * dkwedge_init:
825 *
826 * Initialize the disk wedge subsystem.
827 */
828 void
829 dkwedge_init(void)
830 {
831 __link_set_decl(dkwedge_methods, struct dkwedge_discovery_method);
832 struct dkwedge_discovery_method * const *ddmp;
833 struct dkwedge_discovery_method *lddm, *ddm;
834
835 rw_init(&dkwedges_lock);
836 rw_init(&dkwedge_discovery_methods_lock);
837
838 if (config_cfdriver_attach(&dk_cd) != 0)
839 panic("dkwedge: unable to attach cfdriver");
840 if (config_cfattach_attach(dk_cd.cd_name, &dk_ca) != 0)
841 panic("dkwedge: unable to attach cfattach");
842
843 rw_enter(&dkwedge_discovery_methods_lock, RW_WRITER);
844
845 LIST_INIT(&dkwedge_discovery_methods);
846
847 __link_set_foreach(ddmp, dkwedge_methods) {
848 ddm = *ddmp;
849 if (ddm == &dummy_discovery_method)
850 continue;
851 if (LIST_EMPTY(&dkwedge_discovery_methods)) {
852 LIST_INSERT_HEAD(&dkwedge_discovery_methods,
853 ddm, ddm_list);
854 continue;
855 }
856 LIST_FOREACH(lddm, &dkwedge_discovery_methods, ddm_list) {
857 if (ddm->ddm_priority == lddm->ddm_priority) {
858 aprint_error("dk-method-%s: method \"%s\" "
859 "already exists at priority %d\n",
860 ddm->ddm_name, lddm->ddm_name,
861 lddm->ddm_priority);
862 /* Not inserted. */
863 break;
864 }
865 if (ddm->ddm_priority < lddm->ddm_priority) {
866 /* Higher priority; insert before. */
867 LIST_INSERT_BEFORE(lddm, ddm, ddm_list);
868 break;
869 }
870 if (LIST_NEXT(lddm, ddm_list) == NULL) {
871 /* Last one; insert after. */
872 KASSERT(lddm->ddm_priority < ddm->ddm_priority);
873 LIST_INSERT_AFTER(lddm, ddm, ddm_list);
874 break;
875 }
876 }
877 }
878
879 rw_exit(&dkwedge_discovery_methods_lock);
880 }
881
882 #ifdef DKWEDGE_AUTODISCOVER
883 int dkwedge_autodiscover = 1;
884 #else
885 int dkwedge_autodiscover = 0;
886 #endif
887
888 /*
889 * dkwedge_discover: [exported function]
890 *
891 * Discover the wedges on a newly attached disk.
892 * Remove all unused wedges on the disk first.
893 */
894 void
895 dkwedge_discover(struct disk *pdk)
896 {
897 struct dkwedge_discovery_method *ddm;
898 struct vnode *vp;
899 int error;
900 dev_t pdev;
901
902 /*
903 * Require people playing with wedges to enable this explicitly.
904 */
905 if (dkwedge_autodiscover == 0)
906 return;
907
908 rw_enter(&dkwedge_discovery_methods_lock, RW_READER);
909
910 /*
911 * Use the character device for scanning, the block device
912 * is busy if there are already wedges attached.
913 */
914 error = dkwedge_compute_pdev(pdk->dk_name, &pdev, VCHR);
915 if (error) {
916 aprint_error("%s: unable to compute pdev, error = %d\n",
917 pdk->dk_name, error);
918 goto out;
919 }
920
921 error = cdevvp(pdev, &vp);
922 if (error) {
923 aprint_error("%s: unable to find vnode for pdev, error = %d\n",
924 pdk->dk_name, error);
925 goto out;
926 }
927
928 error = vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
929 if (error) {
930 aprint_error("%s: unable to lock vnode for pdev, error = %d\n",
931 pdk->dk_name, error);
932 vrele(vp);
933 goto out;
934 }
935
936 error = VOP_OPEN(vp, FREAD | FSILENT, NOCRED);
937 if (error) {
938 if (error != ENODEV)
939 aprint_error("%s: unable to open device, error = %d\n",
940 pdk->dk_name, error);
941 vput(vp);
942 goto out;
943 }
944 VOP_UNLOCK(vp);
945
946 /*
947 * Remove unused wedges
948 */
949 dkwedge_delall1(pdk, true);
950
951 /*
952 * For each supported partition map type, look to see if
953 * this map type exists. If so, parse it and add the
954 * corresponding wedges.
955 */
956 LIST_FOREACH(ddm, &dkwedge_discovery_methods, ddm_list) {
957 error = (*ddm->ddm_discover)(pdk, vp);
958 if (error == 0) {
959 /* Successfully created wedges; we're done. */
960 break;
961 }
962 }
963
964 error = vn_close(vp, FREAD, NOCRED);
965 if (error) {
966 aprint_error("%s: unable to close device, error = %d\n",
967 pdk->dk_name, error);
968 /* We'll just assume the vnode has been cleaned up. */
969 }
970
971 out:
972 rw_exit(&dkwedge_discovery_methods_lock);
973 }
974
975 /*
976 * dkwedge_read:
977 *
978 * Read some data from the specified disk, used for
979 * partition discovery.
980 */
981 int
982 dkwedge_read(struct disk *pdk, struct vnode *vp, daddr_t blkno,
983 void *tbuf, size_t len)
984 {
985 buf_t *bp;
986 int error;
987 bool isopen;
988 dev_t bdev;
989 struct vnode *bdvp;
990
991 /*
992 * The kernel cannot read from a character device vnode
993 * as physio() only handles user memory.
994 *
995 * If the block device has already been opened by a wedge
996 * use that vnode and temporarily bump the open counter.
997 *
998 * Otherwise try to open the block device.
999 */
1000
1001 bdev = devsw_chr2blk(vp->v_rdev);
1002
1003 mutex_enter(&pdk->dk_rawlock);
1004 if (pdk->dk_rawopens != 0) {
1005 KASSERT(pdk->dk_rawvp != NULL);
1006 isopen = true;
1007 ++pdk->dk_rawopens;
1008 bdvp = pdk->dk_rawvp;
1009 error = 0;
1010 } else {
1011 isopen = false;
1012 error = dk_open_parent(bdev, FREAD, &bdvp);
1013 }
1014 mutex_exit(&pdk->dk_rawlock);
1015
1016 if (error)
1017 return error;
1018
1019 bp = getiobuf(bdvp, true);
1020 bp->b_flags = B_READ;
1021 bp->b_cflags = BC_BUSY;
1022 bp->b_dev = bdev;
1023 bp->b_data = tbuf;
1024 bp->b_bufsize = bp->b_bcount = len;
1025 bp->b_blkno = blkno;
1026 bp->b_cylinder = 0;
1027 bp->b_error = 0;
1028
1029 VOP_STRATEGY(bdvp, bp);
1030 error = biowait(bp);
1031 putiobuf(bp);
1032
1033 mutex_enter(&pdk->dk_rawlock);
1034 if (isopen) {
1035 --pdk->dk_rawopens;
1036 } else {
1037 dk_close_parent(bdvp, FREAD);
1038 }
1039 mutex_exit(&pdk->dk_rawlock);
1040
1041 return error;
1042 }
1043
1044 /*
1045 * dkwedge_lookup:
1046 *
1047 * Look up a dkwedge_softc based on the provided dev_t.
1048 */
1049 static struct dkwedge_softc *
1050 dkwedge_lookup(dev_t dev)
1051 {
1052 int unit = minor(dev);
1053
1054 if (unit >= ndkwedges)
1055 return (NULL);
1056
1057 KASSERT(dkwedges != NULL);
1058
1059 return (dkwedges[unit]);
1060 }
1061
1062 static int
1063 dk_open_parent(dev_t dev, int mode, struct vnode **vpp)
1064 {
1065 struct vnode *vp;
1066 int error;
1067
1068 error = bdevvp(dev, &vp);
1069 if (error)
1070 return error;
1071
1072 error = vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
1073 if (error) {
1074 vrele(vp);
1075 return error;
1076 }
1077 error = VOP_OPEN(vp, mode, NOCRED);
1078 if (error) {
1079 vput(vp);
1080 return error;
1081 }
1082
1083 /* VOP_OPEN() doesn't do this for us. */
1084 if (mode & FWRITE) {
1085 mutex_enter(vp->v_interlock);
1086 vp->v_writecount++;
1087 mutex_exit(vp->v_interlock);
1088 }
1089
1090 VOP_UNLOCK(vp);
1091
1092 *vpp = vp;
1093
1094 return 0;
1095 }
1096
1097 static int
1098 dk_close_parent(struct vnode *vp, int mode)
1099 {
1100 int error;
1101
1102 error = vn_close(vp, mode, NOCRED);
1103 return error;
1104 }
1105
1106 /*
1107 * dkopen: [devsw entry point]
1108 *
1109 * Open a wedge.
1110 */
1111 static int
1112 dkopen(dev_t dev, int flags, int fmt, struct lwp *l)
1113 {
1114 struct dkwedge_softc *sc = dkwedge_lookup(dev);
1115 struct vnode *vp;
1116 int error = 0;
1117
1118 if (sc == NULL)
1119 return (ENODEV);
1120 if (sc->sc_state != DKW_STATE_RUNNING)
1121 return (ENXIO);
1122
1123 /*
1124 * We go through a complicated little dance to only open the parent
1125 * vnode once per wedge, no matter how many times the wedge is
1126 * opened. The reason? We see one dkopen() per open call, but
1127 * only dkclose() on the last close.
1128 */
1129 mutex_enter(&sc->sc_dk.dk_openlock);
1130 mutex_enter(&sc->sc_parent->dk_rawlock);
1131 if (sc->sc_dk.dk_openmask == 0) {
1132 if (sc->sc_parent->dk_rawopens == 0) {
1133 KASSERT(sc->sc_parent->dk_rawvp == NULL);
1134 error = dk_open_parent(sc->sc_pdev, FREAD | FWRITE, &vp);
1135 if (error)
1136 goto popen_fail;
1137 sc->sc_parent->dk_rawvp = vp;
1138 }
1139 sc->sc_parent->dk_rawopens++;
1140 }
1141 if (fmt == S_IFCHR)
1142 sc->sc_dk.dk_copenmask |= 1;
1143 else
1144 sc->sc_dk.dk_bopenmask |= 1;
1145 sc->sc_dk.dk_openmask =
1146 sc->sc_dk.dk_copenmask | sc->sc_dk.dk_bopenmask;
1147
1148 popen_fail:
1149 mutex_exit(&sc->sc_parent->dk_rawlock);
1150 mutex_exit(&sc->sc_dk.dk_openlock);
1151 return (error);
1152 }
1153
1154 /*
1155 * Caller must hold sc->sc_dk.dk_openlock and sc->sc_parent->dk_rawlock.
1156 */
1157 static int
1158 dklastclose(struct dkwedge_softc *sc)
1159 {
1160 int error = 0, doclose;
1161
1162 doclose = 0;
1163 if (sc->sc_parent->dk_rawopens > 0) {
1164 if (--sc->sc_parent->dk_rawopens == 0)
1165 doclose = 1;
1166 }
1167
1168 mutex_exit(&sc->sc_parent->dk_rawlock);
1169 mutex_exit(&sc->sc_dk.dk_openlock);
1170
1171 if (doclose) {
1172 KASSERT(sc->sc_parent->dk_rawvp != NULL);
1173 dk_close_parent(sc->sc_parent->dk_rawvp, FREAD | FWRITE);
1174 sc->sc_parent->dk_rawvp = NULL;
1175 }
1176
1177 return error;
1178 }
1179
1180 /*
1181 * dkclose: [devsw entry point]
1182 *
1183 * Close a wedge.
1184 */
1185 static int
1186 dkclose(dev_t dev, int flags, int fmt, struct lwp *l)
1187 {
1188 struct dkwedge_softc *sc = dkwedge_lookup(dev);
1189 int error = 0;
1190
1191 if (sc == NULL)
1192 return (ENODEV);
1193 if (sc->sc_state != DKW_STATE_RUNNING)
1194 return (ENXIO);
1195
1196 KASSERT(sc->sc_dk.dk_openmask != 0);
1197
1198 mutex_enter(&sc->sc_dk.dk_openlock);
1199 mutex_enter(&sc->sc_parent->dk_rawlock);
1200
1201 if (fmt == S_IFCHR)
1202 sc->sc_dk.dk_copenmask &= ~1;
1203 else
1204 sc->sc_dk.dk_bopenmask &= ~1;
1205 sc->sc_dk.dk_openmask =
1206 sc->sc_dk.dk_copenmask | sc->sc_dk.dk_bopenmask;
1207
1208 if (sc->sc_dk.dk_openmask == 0)
1209 error = dklastclose(sc); /* releases locks */
1210 else {
1211 mutex_exit(&sc->sc_parent->dk_rawlock);
1212 mutex_exit(&sc->sc_dk.dk_openlock);
1213 }
1214
1215 return (error);
1216 }
1217
1218 /*
1219 * dkstragegy: [devsw entry point]
1220 *
1221 * Perform I/O based on the wedge I/O strategy.
1222 */
1223 static void
1224 dkstrategy(struct buf *bp)
1225 {
1226 struct dkwedge_softc *sc = dkwedge_lookup(bp->b_dev);
1227 uint64_t p_size, p_offset;
1228
1229 if (sc == NULL) {
1230 bp->b_error = ENODEV;
1231 goto done;
1232 }
1233
1234 if (sc->sc_state != DKW_STATE_RUNNING ||
1235 sc->sc_parent->dk_rawvp == NULL) {
1236 bp->b_error = ENXIO;
1237 goto done;
1238 }
1239
1240 /* If it's an empty transfer, wake up the top half now. */
1241 if (bp->b_bcount == 0)
1242 goto done;
1243
1244 p_offset = sc->sc_offset << sc->sc_parent->dk_blkshift;
1245 p_size = sc->sc_size << sc->sc_parent->dk_blkshift;
1246
1247 /* Make sure it's in-range. */
1248 if (bounds_check_with_mediasize(bp, DEV_BSIZE, p_size) <= 0)
1249 goto done;
1250
1251 /* Translate it to the parent's raw LBA. */
1252 bp->b_rawblkno = bp->b_blkno + p_offset;
1253
1254 /* Place it in the queue and start I/O on the unit. */
1255 mutex_enter(&sc->sc_iolock);
1256 sc->sc_iopend++;
1257 disk_wait(&sc->sc_dk);
1258 bufq_put(sc->sc_bufq, bp);
1259 mutex_exit(&sc->sc_iolock);
1260
1261 dkstart(sc);
1262 return;
1263
1264 done:
1265 bp->b_resid = bp->b_bcount;
1266 biodone(bp);
1267 }
1268
1269 /*
1270 * dkstart:
1271 *
1272 * Start I/O that has been enqueued on the wedge.
1273 */
1274 static void
1275 dkstart(struct dkwedge_softc *sc)
1276 {
1277 struct vnode *vp;
1278 struct buf *bp, *nbp;
1279
1280 mutex_enter(&sc->sc_iolock);
1281
1282 /* Do as much work as has been enqueued. */
1283 while ((bp = bufq_peek(sc->sc_bufq)) != NULL) {
1284
1285 if (sc->sc_state != DKW_STATE_RUNNING) {
1286 (void) bufq_get(sc->sc_bufq);
1287 if (sc->sc_iopend-- == 1 &&
1288 (sc->sc_flags & DK_F_WAIT_DRAIN) != 0) {
1289 sc->sc_flags &= ~DK_F_WAIT_DRAIN;
1290 cv_broadcast(&sc->sc_dkdrn);
1291 }
1292 mutex_exit(&sc->sc_iolock);
1293 bp->b_error = ENXIO;
1294 bp->b_resid = bp->b_bcount;
1295 biodone(bp);
1296 mutex_enter(&sc->sc_iolock);
1297 continue;
1298 }
1299
1300 /* fetch an I/O buf with sc_iolock dropped */
1301 mutex_exit(&sc->sc_iolock);
1302 nbp = getiobuf(sc->sc_parent->dk_rawvp, false);
1303 mutex_enter(&sc->sc_iolock);
1304 if (nbp == NULL) {
1305 /*
1306 * No resources to run this request; leave the
1307 * buffer queued up, and schedule a timer to
1308 * restart the queue in 1/2 a second.
1309 */
1310 callout_schedule(&sc->sc_restart_ch, hz / 2);
1311 break;
1312 }
1313
1314 /*
1315 * fetch buf, this can fail if another thread
1316 * has already processed the queue, it can also
1317 * return a completely different buf.
1318 */
1319 bp = bufq_get(sc->sc_bufq);
1320 if (bp == NULL) {
1321 mutex_exit(&sc->sc_iolock);
1322 putiobuf(nbp);
1323 mutex_enter(&sc->sc_iolock);
1324 continue;
1325 }
1326
1327 /* Instrumentation. */
1328 disk_busy(&sc->sc_dk);
1329
1330 /* release lock for VOP_STRATEGY */
1331 mutex_exit(&sc->sc_iolock);
1332
1333 nbp->b_data = bp->b_data;
1334 nbp->b_flags = bp->b_flags;
1335 nbp->b_oflags = bp->b_oflags;
1336 nbp->b_cflags = bp->b_cflags;
1337 nbp->b_iodone = dkiodone;
1338 nbp->b_proc = bp->b_proc;
1339 nbp->b_blkno = bp->b_rawblkno;
1340 nbp->b_dev = sc->sc_parent->dk_rawvp->v_rdev;
1341 nbp->b_bcount = bp->b_bcount;
1342 nbp->b_private = bp;
1343 BIO_COPYPRIO(nbp, bp);
1344
1345 vp = nbp->b_vp;
1346 if ((nbp->b_flags & B_READ) == 0) {
1347 mutex_enter(vp->v_interlock);
1348 vp->v_numoutput++;
1349 mutex_exit(vp->v_interlock);
1350 }
1351 VOP_STRATEGY(vp, nbp);
1352
1353 mutex_enter(&sc->sc_iolock);
1354 }
1355
1356 mutex_exit(&sc->sc_iolock);
1357 }
1358
1359 /*
1360 * dkiodone:
1361 *
1362 * I/O to a wedge has completed; alert the top half.
1363 */
1364 static void
1365 dkiodone(struct buf *bp)
1366 {
1367 struct buf *obp = bp->b_private;
1368 struct dkwedge_softc *sc = dkwedge_lookup(obp->b_dev);
1369
1370 if (bp->b_error != 0)
1371 obp->b_error = bp->b_error;
1372 obp->b_resid = bp->b_resid;
1373 putiobuf(bp);
1374
1375 mutex_enter(&sc->sc_iolock);
1376 if (sc->sc_iopend-- == 1 && (sc->sc_flags & DK_F_WAIT_DRAIN) != 0) {
1377 sc->sc_flags &= ~DK_F_WAIT_DRAIN;
1378 cv_broadcast(&sc->sc_dkdrn);
1379 }
1380
1381 disk_unbusy(&sc->sc_dk, obp->b_bcount - obp->b_resid,
1382 obp->b_flags & B_READ);
1383 mutex_exit(&sc->sc_iolock);
1384
1385 biodone(obp);
1386
1387 /* Kick the queue in case there is more work we can do. */
1388 dkstart(sc);
1389 }
1390
1391 /*
1392 * dkrestart:
1393 *
1394 * Restart the work queue after it was stalled due to
1395 * a resource shortage. Invoked via a callout.
1396 */
1397 static void
1398 dkrestart(void *v)
1399 {
1400 struct dkwedge_softc *sc = v;
1401
1402 dkstart(sc);
1403 }
1404
1405 /*
1406 * dkminphys:
1407 *
1408 * Call parent's minphys function.
1409 */
1410 static void
1411 dkminphys(struct buf *bp)
1412 {
1413 struct dkwedge_softc *sc = dkwedge_lookup(bp->b_dev);
1414 dev_t dev;
1415
1416 dev = bp->b_dev;
1417 bp->b_dev = sc->sc_pdev;
1418 (*sc->sc_parent->dk_driver->d_minphys)(bp);
1419 bp->b_dev = dev;
1420 }
1421
1422 /*
1423 * dkread: [devsw entry point]
1424 *
1425 * Read from a wedge.
1426 */
1427 static int
1428 dkread(dev_t dev, struct uio *uio, int flags)
1429 {
1430 struct dkwedge_softc *sc = dkwedge_lookup(dev);
1431
1432 if (sc == NULL)
1433 return (ENODEV);
1434 if (sc->sc_state != DKW_STATE_RUNNING)
1435 return (ENXIO);
1436
1437 return (physio(dkstrategy, NULL, dev, B_READ, dkminphys, uio));
1438 }
1439
1440 /*
1441 * dkwrite: [devsw entry point]
1442 *
1443 * Write to a wedge.
1444 */
1445 static int
1446 dkwrite(dev_t dev, struct uio *uio, int flags)
1447 {
1448 struct dkwedge_softc *sc = dkwedge_lookup(dev);
1449
1450 if (sc == NULL)
1451 return (ENODEV);
1452 if (sc->sc_state != DKW_STATE_RUNNING)
1453 return (ENXIO);
1454
1455 return (physio(dkstrategy, NULL, dev, B_WRITE, dkminphys, uio));
1456 }
1457
1458 /*
1459 * dkioctl: [devsw entry point]
1460 *
1461 * Perform an ioctl request on a wedge.
1462 */
1463 static int
1464 dkioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
1465 {
1466 struct dkwedge_softc *sc = dkwedge_lookup(dev);
1467 int error = 0;
1468
1469 if (sc == NULL)
1470 return (ENODEV);
1471 if (sc->sc_state != DKW_STATE_RUNNING)
1472 return (ENXIO);
1473 if (sc->sc_parent->dk_rawvp == NULL)
1474 return (ENXIO);
1475
1476 /*
1477 * We pass NODEV instead of our device to indicate we don't
1478 * want to handle disklabel ioctls
1479 */
1480 error = disk_ioctl(&sc->sc_dk, NODEV, cmd, data, flag, l);
1481 if (error != EPASSTHROUGH)
1482 return (error);
1483
1484 error = 0;
1485
1486 switch (cmd) {
1487 case DIOCGSTRATEGY:
1488 case DIOCGCACHE:
1489 case DIOCCACHESYNC:
1490 error = VOP_IOCTL(sc->sc_parent->dk_rawvp, cmd, data, flag,
1491 l != NULL ? l->l_cred : NOCRED);
1492 break;
1493 case DIOCGWEDGEINFO:
1494 {
1495 struct dkwedge_info *dkw = (void *) data;
1496
1497 strlcpy(dkw->dkw_devname, device_xname(sc->sc_dev),
1498 sizeof(dkw->dkw_devname));
1499 memcpy(dkw->dkw_wname, sc->sc_wname, sizeof(dkw->dkw_wname));
1500 dkw->dkw_wname[sizeof(dkw->dkw_wname) - 1] = '\0';
1501 strlcpy(dkw->dkw_parent, sc->sc_parent->dk_name,
1502 sizeof(dkw->dkw_parent));
1503 dkw->dkw_offset = sc->sc_offset;
1504 dkw->dkw_size = sc->sc_size;
1505 strlcpy(dkw->dkw_ptype, sc->sc_ptype, sizeof(dkw->dkw_ptype));
1506
1507 break;
1508 }
1509
1510 default:
1511 error = ENOTTY;
1512 }
1513
1514 return (error);
1515 }
1516
1517 /*
1518 * dkdiscard: [devsw entry point]
1519 *
1520 * Perform a discard-range request on a wedge.
1521 */
1522 static int
1523 dkdiscard(dev_t dev, off_t pos, off_t len)
1524 {
1525 struct dkwedge_softc *sc = dkwedge_lookup(dev);
1526 unsigned shift;
1527 off_t offset, maxlen;
1528
1529 if (sc == NULL)
1530 return (ENODEV);
1531 if (sc->sc_state != DKW_STATE_RUNNING)
1532 return (ENXIO);
1533 if (sc->sc_parent->dk_rawvp == NULL)
1534 return (ENXIO);
1535
1536 shift = (sc->sc_parent->dk_blkshift + DEV_BSHIFT);
1537 KASSERT(__type_fit(off_t, sc->sc_size));
1538 KASSERT(__type_fit(off_t, sc->sc_offset));
1539 KASSERT(0 <= sc->sc_offset);
1540 KASSERT(sc->sc_size <= (__type_max(off_t) >> shift));
1541 KASSERT(sc->sc_offset <= ((__type_max(off_t) >> shift) - sc->sc_size));
1542 offset = ((off_t)sc->sc_offset << shift);
1543 maxlen = ((off_t)sc->sc_size << shift);
1544
1545 if (len > maxlen)
1546 return (EINVAL);
1547 if (pos > (maxlen - len))
1548 return (EINVAL);
1549
1550 pos += offset;
1551 return VOP_FDISCARD(sc->sc_parent->dk_rawvp, pos, len);
1552 }
1553
1554 /*
1555 * dksize: [devsw entry point]
1556 *
1557 * Query the size of a wedge for the purpose of performing a dump
1558 * or for swapping to.
1559 */
1560 static int
1561 dksize(dev_t dev)
1562 {
1563 struct dkwedge_softc *sc = dkwedge_lookup(dev);
1564 int rv = -1;
1565
1566 if (sc == NULL)
1567 return (-1);
1568 if (sc->sc_state != DKW_STATE_RUNNING)
1569 return (-1);
1570
1571 mutex_enter(&sc->sc_dk.dk_openlock);
1572 mutex_enter(&sc->sc_parent->dk_rawlock);
1573
1574 /* Our content type is static, no need to open the device. */
1575
1576 if (strcmp(sc->sc_ptype, DKW_PTYPE_SWAP) == 0) {
1577 /* Saturate if we are larger than INT_MAX. */
1578 if (sc->sc_size > INT_MAX)
1579 rv = INT_MAX;
1580 else
1581 rv = (int) sc->sc_size;
1582 }
1583
1584 mutex_exit(&sc->sc_parent->dk_rawlock);
1585 mutex_exit(&sc->sc_dk.dk_openlock);
1586
1587 return (rv);
1588 }
1589
1590 /*
1591 * dkdump: [devsw entry point]
1592 *
1593 * Perform a crash dump to a wedge.
1594 */
1595 static int
1596 dkdump(dev_t dev, daddr_t blkno, void *va, size_t size)
1597 {
1598 struct dkwedge_softc *sc = dkwedge_lookup(dev);
1599 const struct bdevsw *bdev;
1600 int rv = 0;
1601
1602 if (sc == NULL)
1603 return (ENODEV);
1604 if (sc->sc_state != DKW_STATE_RUNNING)
1605 return (ENXIO);
1606
1607 mutex_enter(&sc->sc_dk.dk_openlock);
1608 mutex_enter(&sc->sc_parent->dk_rawlock);
1609
1610 /* Our content type is static, no need to open the device. */
1611
1612 if (strcmp(sc->sc_ptype, DKW_PTYPE_SWAP) != 0 &&
1613 strcmp(sc->sc_ptype, DKW_PTYPE_RAID) != 0) {
1614 rv = ENXIO;
1615 goto out;
1616 }
1617 if (size % DEV_BSIZE != 0) {
1618 rv = EINVAL;
1619 goto out;
1620 }
1621 if (blkno + size / DEV_BSIZE > sc->sc_size) {
1622 printf("%s: blkno (%" PRIu64 ") + size / DEV_BSIZE (%zu) > "
1623 "sc->sc_size (%" PRIu64 ")\n", __func__, blkno,
1624 size / DEV_BSIZE, sc->sc_size);
1625 rv = EINVAL;
1626 goto out;
1627 }
1628
1629 bdev = bdevsw_lookup(sc->sc_pdev);
1630 rv = (*bdev->d_dump)(sc->sc_pdev, blkno + sc->sc_offset, va, size);
1631
1632 out:
1633 mutex_exit(&sc->sc_parent->dk_rawlock);
1634 mutex_exit(&sc->sc_dk.dk_openlock);
1635
1636 return rv;
1637 }
1638
1639 /*
1640 * config glue
1641 */
1642
1643 /*
1644 * dkwedge_find_partition
1645 *
1646 * Find wedge corresponding to the specified parent name
1647 * and offset/length.
1648 */
1649 device_t
1650 dkwedge_find_partition(device_t parent, daddr_t startblk, uint64_t nblks)
1651 {
1652 struct dkwedge_softc *sc;
1653 int i;
1654 device_t wedge = NULL;
1655
1656 rw_enter(&dkwedges_lock, RW_READER);
1657 for (i = 0; i < ndkwedges; i++) {
1658 if ((sc = dkwedges[i]) == NULL)
1659 continue;
1660 if (strcmp(sc->sc_parent->dk_name, device_xname(parent)) == 0 &&
1661 sc->sc_offset == startblk &&
1662 sc->sc_size == nblks) {
1663 if (wedge) {
1664 printf("WARNING: double match for boot wedge "
1665 "(%s, %s)\n",
1666 device_xname(wedge),
1667 device_xname(sc->sc_dev));
1668 continue;
1669 }
1670 wedge = sc->sc_dev;
1671 }
1672 }
1673 rw_exit(&dkwedges_lock);
1674
1675 return wedge;
1676 }
1677
1678 const char *
1679 dkwedge_get_parent_name(dev_t dev)
1680 {
1681 /* XXX: perhaps do this in lookup? */
1682 int bmaj = bdevsw_lookup_major(&dk_bdevsw);
1683 int cmaj = cdevsw_lookup_major(&dk_cdevsw);
1684 if (major(dev) != bmaj && major(dev) != cmaj)
1685 return NULL;
1686 struct dkwedge_softc *sc = dkwedge_lookup(dev);
1687 if (sc == NULL)
1688 return NULL;
1689 return sc->sc_parent->dk_name;
1690 }
1691
1692