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