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