kernel - Improve physio performance
[dragonfly.git] / sys / kern / subr_diskslice.c
blobdb9def8f07df14a4fe46239611cb8674e3f607e0
1 /*-
2 * Copyright (c) 1994 Bruce D. Evans.
3 * All rights reserved.
5 * Copyright (c) 1990 The Regents of the University of California.
6 * All rights reserved.
8 * This code is derived from software contributed to Berkeley by
9 * William Jolitz.
11 * Copyright (c) 1982, 1986, 1988 Regents of the University of California.
12 * All rights reserved.
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions
16 * are met:
17 * 1. Redistributions of source code must retain the above copyright
18 * notice, this list of conditions and the following disclaimer.
19 * 2. Redistributions in binary form must reproduce the above copyright
20 * notice, this list of conditions and the following disclaimer in the
21 * documentation and/or other materials provided with the distribution.
22 * 3. Neither the name of the University nor the names of its contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
38 * from: @(#)wd.c 7.2 (Berkeley) 5/9/91
39 * from: wd.c,v 1.55 1994/10/22 01:57:12 phk Exp $
40 * from: @(#)ufs_disksubr.c 7.16 (Berkeley) 5/4/91
41 * from: ufs_disksubr.c,v 1.8 1994/06/07 01:21:39 phk Exp $
42 * $FreeBSD: src/sys/kern/subr_diskslice.c,v 1.82.2.6 2001/07/24 09:49:41 dd Exp $
45 #include <sys/param.h>
46 #include <sys/systm.h>
47 #include <sys/buf.h>
48 #include <sys/conf.h>
49 #include <sys/disklabel.h>
50 #include <sys/disklabel32.h>
51 #include <sys/disklabel64.h>
52 #include <sys/diskslice.h>
53 #include <sys/disk.h>
54 #include <sys/diskmbr.h>
55 #include <sys/fcntl.h>
56 #include <sys/malloc.h>
57 #include <sys/stat.h>
58 #include <sys/syslog.h>
59 #include <sys/proc.h>
60 #include <sys/vnode.h>
61 #include <sys/device.h>
62 #include <sys/thread2.h>
64 #include <vfs/ufs/dinode.h> /* XXX used only for fs.h */
65 #include <vfs/ufs/fs.h> /* XXX used only to get BBSIZE/SBSIZE */
66 #include <sys/devfs.h>
68 static int dsreadandsetlabel(cdev_t dev, u_int flags,
69 struct diskslices *ssp, struct diskslice *sp,
70 struct disk_info *info);
71 static void free_ds_label (struct diskslices *ssp, int slice);
72 static void set_ds_label (struct diskslices *ssp, int slice, disklabel_t lp,
73 disklabel_ops_t ops);
74 static void set_ds_wlabel (struct diskslices *ssp, int slice, int wlabel);
77 * Determine the size of the transfer, and make sure it is
78 * within the boundaries of the partition. Adjust transfer
79 * if needed, and signal errors or early completion.
81 * XXX TODO:
82 * o Split buffers that are too big for the device.
83 * o Check for overflow.
84 * o Finish cleaning this up.
86 * This function returns 1 on success, 0 if transfer equates
87 * to EOF (end of disk) or -1 on failure. The appropriate
88 * 'errno' value is also set in bp->b_error and bp->b_flags
89 * is marked with B_ERROR.
91 struct bio *
92 dscheck(cdev_t dev, struct bio *bio, struct diskslices *ssp)
94 struct buf *bp = bio->bio_buf;
95 struct bio *nbio;
96 disklabel_t lp;
97 disklabel_ops_t ops;
98 long nsec;
99 u_int64_t secno;
100 u_int64_t endsecno;
101 u_int64_t slicerel_secno;
102 struct diskslice *sp;
103 u_int32_t part;
104 u_int32_t slice;
105 int shift;
106 int mask;
108 slice = dkslice(dev);
109 part = dkpart(dev);
111 if (bio->bio_offset < 0) {
112 kprintf("dscheck(%s): negative bio_offset %lld\n",
113 devtoname(dev), (long long)bio->bio_offset);
114 goto bad;
116 if (slice >= ssp->dss_nslices) {
117 kprintf("dscheck(%s): slice too large %d/%d\n",
118 devtoname(dev), slice, ssp->dss_nslices);
119 goto bad;
121 sp = &ssp->dss_slices[slice];
123 * Calculate secno and nsec
125 if (ssp->dss_secmult == 1) {
126 shift = DEV_BSHIFT;
127 goto doshift;
128 } else if (ssp->dss_secshift != -1) {
129 shift = DEV_BSHIFT + ssp->dss_secshift;
130 doshift:
131 mask = (1 << shift) - 1;
132 if ((int)bp->b_bcount & mask)
133 goto bad_bcount;
134 if ((int)bio->bio_offset & mask)
135 goto bad_blkno;
136 secno = bio->bio_offset >> shift;
137 nsec = bp->b_bcount >> shift;
138 } else {
139 if (bp->b_bcount % ssp->dss_secsize)
140 goto bad_bcount;
141 if (bio->bio_offset % ssp->dss_secsize)
142 goto bad_blkno;
143 secno = bio->bio_offset / ssp->dss_secsize;
144 nsec = bp->b_bcount / ssp->dss_secsize;
148 * Calculate slice-relative sector number end slice-relative
149 * limit.
151 if (slice == WHOLE_DISK_SLICE) {
153 * Labels have not been allowed on whole-disks for a while.
154 * This really puts the nail in the coffin.
156 * Accesses to the WHOLE_DISK_SLICE do not use a disklabel
157 * and partition numbers are special-cased. Currently numbers
158 * less then 128 are not allowed. Partition numbers >= 128
159 * are encoded in the high 8 bits of the 64 bit buffer offset
160 * and are fed directly through to the device with no
161 * further interpretation. In particular, no sector
162 * translation interpretation should occur because the
163 * sector size for the special raw access may not be the
164 * same as the nominal sector size for the device.
166 lp.opaque = NULL;
167 if (part < 128) {
168 kprintf("dscheck(%s): illegal partition number (%d) "
169 "for WHOLE_DISK_SLICE access\n",
170 devtoname(dev), part);
171 goto bad;
172 } else if (part != WHOLE_SLICE_PART) {
173 nbio = push_bio(bio);
174 nbio->bio_offset = bio->bio_offset |
175 (u_int64_t)part << 56;
176 return(nbio);
177 } else {
179 * If writing to the raw disk request a
180 * reprobe on the last close.
182 if (bp->b_cmd == BUF_CMD_WRITE)
183 sp->ds_flags |= DSF_REPROBE;
187 * sp->ds_size is for the whole disk in the WHOLE_DISK_SLICE,
188 * there are no reserved areas.
190 endsecno = sp->ds_size;
191 slicerel_secno = secno;
192 } else if (part == WHOLE_SLICE_PART) {
194 * NOTE! opens on a whole-slice partition will not attempt
195 * to read a disklabel in, so there may not be an in-core
196 * disklabel even if there is one on the disk.
198 endsecno = sp->ds_size;
199 slicerel_secno = secno;
200 } else if ((lp = sp->ds_label).opaque != NULL) {
202 * A label is present, extract the partition. Snooping of
203 * the disklabel is not supported even if accessible. Of
204 * course, the reserved area is still write protected.
206 ops = sp->ds_ops;
207 if (ops->op_getpartbounds(ssp, lp, part,
208 &slicerel_secno, &endsecno)) {
209 kprintf("dscheck(%s): partition %d out of bounds\n",
210 devtoname(dev), part);
211 goto bad;
213 slicerel_secno += secno;
214 } else {
216 * Attempt to access partition when no disklabel present
218 kprintf("dscheck(%s): attempt to access non-existent partition\n",
219 devtoname(dev));
220 goto bad;
224 * Disallow writes to reserved areas unless ds_wlabel allows it.
225 * If the reserved area is written to request a reprobe of the
226 * disklabel when the slice is closed.
228 if (slicerel_secno < sp->ds_reserved && nsec &&
229 bp->b_cmd == BUF_CMD_WRITE) {
230 if (sp->ds_wlabel == 0) {
231 bp->b_error = EROFS;
232 goto error;
234 sp->ds_flags |= DSF_REPROBE;
238 * If we get here, bio_offset must be on a block boundary and
239 * the sector size must be a power of 2.
241 if ((bio->bio_offset & (ssp->dss_secsize - 1)) ||
242 (ssp->dss_secsize ^ (ssp->dss_secsize - 1)) !=
243 ((ssp->dss_secsize << 1) - 1)) {
244 kprintf("%s: invalid BIO offset, not sector aligned or"
245 " invalid sector size (not power of 2) %08llx %d\n",
246 devtoname(dev), (long long)bio->bio_offset,
247 ssp->dss_secsize);
248 goto bad;
252 * EOF handling
254 if (secno + nsec > endsecno) {
256 * Return an error if beyond the end of the disk, or
257 * if B_BNOCLIP is set. Tell the system that we do not
258 * need to keep the buffer around.
260 if (secno > endsecno || (bp->b_flags & B_BNOCLIP))
261 goto bad;
264 * If exactly at end of disk, return an EOF. Throw away
265 * the buffer contents, if any, by setting B_INVAL.
267 if (secno == endsecno) {
268 bp->b_resid = bp->b_bcount;
269 bp->b_flags |= B_INVAL;
270 goto done;
274 * Else truncate
276 nsec = endsecno - secno;
277 bp->b_bcount = nsec * ssp->dss_secsize;
280 nbio = push_bio(bio);
281 nbio->bio_offset = (off_t)(sp->ds_offset + slicerel_secno) *
282 ssp->dss_secsize;
283 return (nbio);
285 bad_bcount:
286 kprintf(
287 "dscheck(%s): b_bcount %d is not on a sector boundary (ssize %d)\n",
288 devtoname(dev), bp->b_bcount, ssp->dss_secsize);
289 goto bad;
291 bad_blkno:
292 kprintf(
293 "dscheck(%s): bio_offset %lld is not on a sector boundary (ssize %d)\n",
294 devtoname(dev), (long long)bio->bio_offset, ssp->dss_secsize);
295 bad:
296 bp->b_error = EINVAL;
297 /* fall through */
298 error:
300 * Terminate the I/O with a ranging error. Since the buffer is
301 * either illegal or beyond the file EOF, mark it B_INVAL as well.
303 bp->b_resid = bp->b_bcount;
304 bp->b_flags |= B_ERROR | B_INVAL;
305 done:
307 * Caller must biodone() the originally passed bio if NULL is
308 * returned.
310 return (NULL);
314 * dsclose() - close a cooked disk slice.
316 * WARNING! The passed diskslices and related diskslice structures may
317 * be invalidated or replaced by this function, callers must
318 * reload from the disk structure for continued access.
320 void
321 dsclose(cdev_t dev, int mode, struct diskslices *ssp)
323 u_int32_t part;
324 u_int32_t slice;
325 struct diskslice *sp;
327 slice = dkslice(dev);
328 part = dkpart(dev);
329 if (slice < ssp->dss_nslices) {
330 sp = &ssp->dss_slices[slice];
331 dsclrmask(sp, part);
332 if (sp->ds_flags & DSF_REPROBE) {
333 sp->ds_flags &= ~DSF_REPROBE;
334 if (slice == WHOLE_DISK_SLICE) {
335 disk_msg_send_sync(DISK_DISK_REPROBE,
336 dev->si_disk, NULL);
337 devfs_config();
338 } else {
339 disk_msg_send_sync(DISK_SLICE_REPROBE,
340 dev->si_disk, sp);
341 devfs_config();
343 /* ssp and sp may both be invalid after reprobe */
348 void
349 dsgone(struct diskslices **sspp)
351 int slice;
352 struct diskslices *ssp;
354 if ((ssp = *sspp) != NULL) {
355 for (slice = 0; slice < ssp->dss_nslices; slice++)
356 free_ds_label(ssp, slice);
357 kfree(ssp, M_DEVBUF);
358 *sspp = NULL;
363 * For the "write" commands (DIOCSDINFO and DIOCWDINFO), this
364 * is subject to the same restriction as dsopen().
367 dsioctl(cdev_t dev, u_long cmd, caddr_t data, int flags,
368 struct diskslices **sspp, struct disk_info *info)
370 int error;
371 disklabel_t lp;
372 disklabel_t lptmp;
373 disklabel_ops_t ops;
374 int old_wlabel;
375 u_int32_t openmask[DKMAXPARTITIONS/(sizeof(u_int32_t)*8)];
376 int part;
377 int slice;
378 struct diskslice *sp;
379 struct diskslices *ssp;
381 slice = dkslice(dev);
382 part = dkpart(dev);
383 ssp = *sspp;
384 if (ssp == NULL)
385 return (EINVAL);
386 if (slice >= ssp->dss_nslices)
387 return (EINVAL);
388 sp = &ssp->dss_slices[slice];
389 lp = sp->ds_label;
390 ops = sp->ds_ops; /* may be NULL if no label */
392 switch (cmd) {
393 case DIOCGDVIRGIN32:
394 ops = &disklabel32_ops;
395 /* fall through */
396 case DIOCGDVIRGIN64:
397 if (cmd != DIOCGDVIRGIN32)
398 ops = &disklabel64_ops;
400 * You can only retrieve a virgin disklabel on the whole
401 * disk slice or whole-slice partition.
403 if (slice != WHOLE_DISK_SLICE &&
404 part != WHOLE_SLICE_PART) {
405 return(EINVAL);
408 lp.opaque = data;
409 ops->op_makevirginlabel(lp, ssp, sp, info);
410 return (0);
412 case DIOCGDINFO32:
413 case DIOCGDINFO64:
415 * You can only retrieve a disklabel on the whole
416 * slice partition.
418 * We do not support labels directly on whole-disks
419 * any more (that is, disks without slices), unless the
420 * device driver has asked for a compatible label (e.g.
421 * for a CD) to allow booting off of storage that is
422 * otherwise unlabeled.
424 error = 0;
425 if (part != WHOLE_SLICE_PART)
426 return(EINVAL);
427 if (slice == WHOLE_DISK_SLICE &&
428 (info->d_dsflags & DSO_COMPATLABEL) == 0) {
429 return (ENODEV);
431 if (sp->ds_label.opaque == NULL) {
432 error = dsreadandsetlabel(dev, info->d_dsflags,
433 ssp, sp, info);
434 ops = sp->ds_ops; /* may be NULL */
438 * The type of label we found must match the type of
439 * label requested.
441 if (error == 0 && IOCPARM_LEN(cmd) != ops->labelsize)
442 error = ENOATTR;
443 if (error == 0)
444 bcopy(sp->ds_label.opaque, data, ops->labelsize);
445 return (error);
447 case DIOCGPART:
449 struct partinfo *dpart = (void *)data;
452 * The disk management layer may not have read the
453 * disklabel yet because simply opening a slice no
454 * longer 'probes' the disk that way. Be sure we
455 * have tried.
457 * We ignore any error.
459 if (sp->ds_label.opaque == NULL &&
460 part == WHOLE_SLICE_PART &&
461 slice != WHOLE_DISK_SLICE) {
462 dsreadandsetlabel(dev, info->d_dsflags,
463 ssp, sp, info);
464 ops = sp->ds_ops; /* may be NULL */
467 bzero(dpart, sizeof(*dpart));
468 dpart->media_offset = (u_int64_t)sp->ds_offset *
469 info->d_media_blksize;
470 dpart->media_size = (u_int64_t)sp->ds_size *
471 info->d_media_blksize;
472 dpart->media_blocks = sp->ds_size;
473 dpart->media_blksize = info->d_media_blksize;
474 dpart->reserved_blocks= sp->ds_reserved;
475 dpart->fstype_uuid = sp->ds_type_uuid;
476 dpart->storage_uuid = sp->ds_stor_uuid;
478 if (slice != WHOLE_DISK_SLICE &&
479 part != WHOLE_SLICE_PART) {
480 u_int64_t start;
481 u_int64_t blocks;
482 if (lp.opaque == NULL)
483 return(EINVAL);
484 if (ops->op_getpartbounds(ssp, lp, part,
485 &start, &blocks)) {
486 return(EINVAL);
488 ops->op_loadpartinfo(lp, part, dpart);
489 dpart->media_offset += start *
490 info->d_media_blksize;
491 dpart->media_size = blocks *
492 info->d_media_blksize;
493 dpart->media_blocks = blocks;
496 * partition starting sector (p_offset)
497 * requires slice's reserved areas to be
498 * adjusted.
500 if (dpart->reserved_blocks > start)
501 dpart->reserved_blocks -= start;
502 else
503 dpart->reserved_blocks = 0;
507 * Load remaining fields from the info structure
509 dpart->d_nheads = info->d_nheads;
510 dpart->d_ncylinders = info->d_ncylinders;
511 dpart->d_secpertrack = info->d_secpertrack;
512 dpart->d_secpercyl = info->d_secpercyl;
514 return (0);
516 case DIOCGSLICEINFO:
517 bcopy(ssp, data, (char *)&ssp->dss_slices[ssp->dss_nslices] -
518 (char *)ssp);
519 return (0);
521 case DIOCSDINFO32:
522 ops = &disklabel32_ops;
523 /* fall through */
524 case DIOCSDINFO64:
525 if (cmd != DIOCSDINFO32)
526 ops = &disklabel64_ops;
528 * You can write a disklabel on the whole disk slice or
529 * whole-slice partition.
531 if (slice != WHOLE_DISK_SLICE &&
532 part != WHOLE_SLICE_PART) {
533 return(EINVAL);
537 * We no longer support writing disklabels directly to media
538 * without there being a slice. Keep this as a separate
539 * conditional.
541 if (slice == WHOLE_DISK_SLICE)
542 return (ENODEV);
543 if (!(flags & FWRITE))
544 return (EBADF);
547 * If an existing label is present it must be the same
548 * type as the label being passed by the ioctl.
550 if (sp->ds_label.opaque && sp->ds_ops != ops)
551 return (ENOATTR);
554 * Create a temporary copy of the existing label
555 * (if present) so setdisklabel can compare it against
556 * the new label.
558 lp.opaque = kmalloc(ops->labelsize, M_DEVBUF, M_WAITOK);
559 if (sp->ds_label.opaque == NULL)
560 bzero(lp.opaque, ops->labelsize);
561 else
562 bcopy(sp->ds_label.opaque, lp.opaque, ops->labelsize);
563 if (sp->ds_label.opaque == NULL) {
564 bzero(openmask, sizeof(openmask));
565 } else {
566 bcopy(sp->ds_openmask, openmask, sizeof(openmask));
568 lptmp.opaque = data;
569 error = ops->op_setdisklabel(lp, lptmp, ssp, sp, openmask);
570 disk_msg_send_sync(DISK_SLICE_REPROBE, dev->si_disk, sp);
571 devfs_config();
572 if (error != 0) {
573 kfree(lp.opaque, M_DEVBUF);
574 return (error);
576 free_ds_label(ssp, slice);
577 set_ds_label(ssp, slice, lp, ops);
578 return (0);
580 case DIOCSYNCSLICEINFO:
582 * This ioctl can only be done on the whole disk
584 if (slice != WHOLE_DISK_SLICE || part != WHOLE_SLICE_PART)
585 return (EINVAL);
587 if (*(int *)data == 0) {
588 for (slice = 0; slice < ssp->dss_nslices; slice++) {
589 struct diskslice *ds = &ssp->dss_slices[slice];
591 switch(dscountmask(ds)) {
592 case 0:
593 break;
594 case 1:
595 if (slice != WHOLE_DISK_SLICE)
596 return (EBUSY);
597 if (!dschkmask(ds, RAW_PART))
598 return (EBUSY);
599 break;
600 default:
601 return (EBUSY);
606 disk_msg_send_sync(DISK_DISK_REPROBE, dev->si_disk, NULL);
607 devfs_config();
608 return 0;
610 case DIOCWDINFO32:
611 case DIOCWDINFO64:
612 error = dsioctl(dev, ((cmd == DIOCWDINFO32) ?
613 DIOCSDINFO32 : DIOCSDINFO64),
614 data, flags, &ssp, info);
615 if (error == 0 && sp->ds_label.opaque == NULL)
616 error = EINVAL;
617 if (part != WHOLE_SLICE_PART)
618 error = EINVAL;
619 if (error != 0)
620 return (error);
623 * Allow the reserved area to be written, reload ops
624 * because the DIOCSDINFO op above may have installed
625 * a new label type.
627 ops = sp->ds_ops;
628 old_wlabel = sp->ds_wlabel;
629 set_ds_wlabel(ssp, slice, TRUE);
630 error = ops->op_writedisklabel(dev, ssp, sp, sp->ds_label);
631 disk_msg_send_sync(DISK_SLICE_REPROBE, dev->si_disk, sp);
632 devfs_config();
633 set_ds_wlabel(ssp, slice, old_wlabel);
634 /* XXX should invalidate in-core label if write failed. */
635 return (error);
637 case DIOCWLABEL:
638 if (slice == WHOLE_DISK_SLICE)
639 return (ENODEV);
640 if (!(flags & FWRITE))
641 return (EBADF);
642 set_ds_wlabel(ssp, slice, *(int *)data != 0);
643 return (0);
645 default:
646 return (ENOIOCTL);
651 dsisopen(struct diskslices *ssp)
653 int slice;
655 if (ssp == NULL)
656 return (0);
657 for (slice = 0; slice < ssp->dss_nslices; slice++) {
658 if (dscountmask(&ssp->dss_slices[slice]))
659 return (1);
661 return (0);
665 * Allocate a slices "struct" and initialize it to contain only an empty
666 * compatibility slice (pointing to itself), a whole disk slice (covering
667 * the disk as described by the label), and (nslices - BASE_SLICES) empty
668 * slices beginning at BASE_SLICE.
670 * Note that the compatibility slice is no longer really a compatibility
671 * slice. It is slice 0 if a GPT label is present, and the dangerously
672 * dedicated slice if no slice table otherwise exists. Else it is 0-sized.
674 struct diskslices *
675 dsmakeslicestruct(int nslices, struct disk_info *info)
677 struct diskslice *sp;
678 struct diskslices *ssp;
680 ssp = kmalloc(offsetof(struct diskslices, dss_slices) +
681 nslices * sizeof *sp, M_DEVBUF, M_WAITOK);
682 ssp->dss_first_bsd_slice = COMPATIBILITY_SLICE;
683 ssp->dss_nslices = nslices;
684 ssp->dss_oflags = 0;
687 * Figure out if we can use shifts or whether we have to
688 * use mod/multply to translate byte offsets into sector numbers.
690 if ((info->d_media_blksize ^ (info->d_media_blksize - 1)) ==
691 (info->d_media_blksize << 1) - 1) {
692 ssp->dss_secmult = info->d_media_blksize / DEV_BSIZE;
693 if (ssp->dss_secmult & (ssp->dss_secmult - 1))
694 ssp->dss_secshift = -1;
695 else
696 ssp->dss_secshift = ffs(ssp->dss_secmult) - 1;
697 } else {
698 ssp->dss_secmult = 0;
699 ssp->dss_secshift = -1;
701 ssp->dss_secsize = info->d_media_blksize;
702 sp = &ssp->dss_slices[0];
703 bzero(sp, nslices * sizeof *sp);
704 sp[WHOLE_DISK_SLICE].ds_size = info->d_media_blocks;
705 return (ssp);
708 char *
709 dsname(cdev_t dev, int unit, int slice, int part, char *partname)
711 return dev->si_name;
715 * This should only be called when the unit is inactive and the strategy
716 * routine should not allow it to become active unless we call it. Our
717 * strategy routine must be special to allow activity.
720 dsopen(cdev_t dev, int mode, u_int flags,
721 struct diskslices **sspp, struct disk_info *info)
723 struct diskslice *sp;
724 struct diskslices *ssp;
725 int slice;
726 int part;
728 ssp = *sspp;
729 dev->si_bsize_phys = info->d_media_blksize;
730 slice = dkslice(dev);
731 part = dkpart(dev);
732 sp = &ssp->dss_slices[slice];
733 dssetmask(sp, part);
735 return 0;
739 * Attempt to read the disklabel. If successful, store it in sp->ds_label.
741 * If we cannot read the disklabel and DSO_COMPATLABEL is set, we construct
742 * a fake label covering the whole disk.
744 static
746 dsreadandsetlabel(cdev_t dev, u_int flags,
747 struct diskslices *ssp, struct diskslice *sp,
748 struct disk_info *info)
750 disklabel_t lp;
751 disklabel_ops_t ops;
752 const char *msg;
753 const char *sname;
754 char partname[2];
755 int slice = dkslice(dev);
758 * Probe the disklabel
760 lp.opaque = NULL;
761 sname = dsname(dev, dkunit(dev), slice, WHOLE_SLICE_PART, partname);
762 ops = &disklabel32_ops;
763 msg = ops->op_readdisklabel(dev, sp, &lp, info);
764 if (msg && strcmp(msg, "no disk label") == 0) {
765 ops = &disklabel64_ops;
766 msg = disklabel64_ops.op_readdisklabel(dev, sp, &lp, info);
770 * If we failed and COMPATLABEL is set, create a dummy disklabel.
772 if (msg != NULL && (flags & DSO_COMPATLABEL)) {
773 msg = NULL;
774 if (sp->ds_size >= 0x100000000ULL)
775 ops = &disklabel64_ops;
776 else
777 ops = &disklabel32_ops;
778 lp = ops->op_clone_label(info, sp);
780 if (msg != NULL) {
781 if (sp->ds_type == DOSPTYP_386BSD /* XXX */)
782 log(LOG_WARNING, "%s: cannot find label (%s)\n",
783 sname, msg);
784 if (lp.opaque)
785 kfree(lp.opaque, M_DEVBUF);
786 } else {
787 set_ds_label(ssp, slice, lp, ops);
788 set_ds_wlabel(ssp, slice, FALSE);
790 return (msg ? EINVAL : 0);
793 int64_t
794 dssize(cdev_t dev, struct diskslices **sspp)
796 disklabel_t lp;
797 disklabel_ops_t ops;
798 int part;
799 int slice;
800 struct diskslices *ssp;
801 u_int64_t start;
802 u_int64_t blocks;
804 slice = dkslice(dev);
805 part = dkpart(dev);
806 ssp = *sspp;
807 if (ssp == NULL || slice >= ssp->dss_nslices
808 || !dschkmask(&ssp->dss_slices[slice], part)) {
809 if (dev_dopen(dev, FREAD, S_IFCHR, proc0.p_ucred, NULL) != 0)
810 return (-1);
811 dev_dclose(dev, FREAD, S_IFCHR, NULL);
812 ssp = *sspp;
814 lp = ssp->dss_slices[slice].ds_label;
815 if (part == WHOLE_SLICE_PART) {
816 blocks = ssp->dss_slices[slice].ds_size;
817 } else if (lp.opaque == NULL) {
818 blocks = (u_int64_t)-1;
819 } else {
820 ops = ssp->dss_slices[slice].ds_ops;
821 if (ops->op_getpartbounds(ssp, lp, part, &start, &blocks))
822 return (-1);
824 return ((int64_t)blocks);
827 static void
828 free_ds_label(struct diskslices *ssp, int slice)
830 struct diskslice *sp;
831 disklabel_t lp;
833 sp = &ssp->dss_slices[slice];
834 lp = sp->ds_label;
835 if (lp.opaque != NULL) {
836 kfree(lp.opaque, M_DEVBUF);
837 lp.opaque = NULL;
838 set_ds_label(ssp, slice, lp, NULL);
842 static void
843 set_ds_label(struct diskslices *ssp, int slice,
844 disklabel_t lp, disklabel_ops_t ops)
846 struct diskslice *sp = &ssp->dss_slices[slice];
848 sp->ds_label = lp;
849 sp->ds_ops = ops;
850 if (lp.opaque && slice != WHOLE_DISK_SLICE)
851 ops->op_adjust_label_reserved(ssp, slice, sp);
852 else
853 sp->ds_reserved = 0;
856 static void
857 set_ds_wlabel(struct diskslices *ssp, int slice, int wlabel)
859 ssp->dss_slices[slice].ds_wlabel = wlabel;