| Commit | Line | Data |
|---|---|---|
| 984263bc | 1 | /* |
| 8c10bfcf MD |
2 | * Copyright (c) 2003,2004 The DragonFly Project. All rights reserved. |
| 3 | * | |
| 4 | * This code is derived from software contributed to The DragonFly Project | |
| 5 | * by Matthew Dillon <dillon@backplane.com> | |
| 6 | * | |
| 7 | * Redistribution and use in source and binary forms, with or without | |
| 8 | * modification, are permitted provided that the following conditions | |
| 9 | * are met: | |
| 10 | * | |
| 11 | * 1. Redistributions of source code must retain the above copyright | |
| 12 | * notice, this list of conditions and the following disclaimer. | |
| 13 | * 2. Redistributions in binary form must reproduce the above copyright | |
| 14 | * notice, this list of conditions and the following disclaimer in | |
| 15 | * the documentation and/or other materials provided with the | |
| 16 | * distribution. | |
| 17 | * 3. Neither the name of The DragonFly Project nor the names of its | |
| 18 | * contributors may be used to endorse or promote products derived | |
| 19 | * from this software without specific, prior written permission. | |
| 20 | * | |
| 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| 22 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| 23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | |
| 24 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | |
| 25 | * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | |
| 26 | * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, | |
| 27 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
| 28 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED | |
| 29 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |
| 30 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |
| 31 | * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| 32 | * SUCH DAMAGE. | |
| 33 | * | |
| 984263bc MD |
34 | * ---------------------------------------------------------------------------- |
| 35 | * "THE BEER-WARE LICENSE" (Revision 42): | |
| 36 | * <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you | |
| 37 | * can do whatever you want with this stuff. If we meet some day, and you think | |
| 38 | * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp | |
| 39 | * ---------------------------------------------------------------------------- | |
| 40 | * | |
| 7a9e53ad MD |
41 | * Copyright (c) 1982, 1986, 1988, 1993 |
| 42 | * The Regents of the University of California. All rights reserved. | |
| 43 | * (c) UNIX System Laboratories, Inc. | |
| 44 | * All or some portions of this file are derived from material licensed | |
| 45 | * to the University of California by American Telephone and Telegraph | |
| 46 | * Co. or Unix System Laboratories, Inc. and are reproduced herein with | |
| 47 | * the permission of UNIX System Laboratories, Inc. | |
| 48 | * | |
| 49 | * Redistribution and use in source and binary forms, with or without | |
| 50 | * modification, are permitted provided that the following conditions | |
| 51 | * are met: | |
| 52 | * 1. Redistributions of source code must retain the above copyright | |
| 53 | * notice, this list of conditions and the following disclaimer. | |
| 54 | * 2. Redistributions in binary form must reproduce the above copyright | |
| 55 | * notice, this list of conditions and the following disclaimer in the | |
| 56 | * documentation and/or other materials provided with the distribution. | |
| 57 | * 3. All advertising materials mentioning features or use of this software | |
| 58 | * must display the following acknowledgement: | |
| 59 | * This product includes software developed by the University of | |
| 60 | * California, Berkeley and its contributors. | |
| 61 | * 4. Neither the name of the University nor the names of its contributors | |
| 62 | * may be used to endorse or promote products derived from this software | |
| 63 | * without specific prior written permission. | |
| 64 | * | |
| 65 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
| 66 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 67 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| 68 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
| 69 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| 70 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
| 71 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
| 72 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
| 73 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
| 74 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| 75 | * SUCH DAMAGE. | |
| 984263bc | 76 | * |
| 7a9e53ad MD |
77 | * @(#)ufs_disksubr.c 8.5 (Berkeley) 1/21/94 |
| 78 | * $FreeBSD: src/sys/kern/subr_disk.c,v 1.20.2.6 2001/10/05 07:14:57 peter Exp $ | |
| 79 | * $FreeBSD: src/sys/ufs/ufs/ufs_disksubr.c,v 1.44.2.3 2001/03/05 05:42:19 obrien Exp $ | |
| 3641b7ca | 80 | * $DragonFly: src/sys/kern/subr_disk.c,v 1.40 2008/06/05 18:06:32 swildner Exp $ |
| 984263bc MD |
81 | */ |
| 82 | ||
| 83 | #include <sys/param.h> | |
| 84 | #include <sys/systm.h> | |
| 85 | #include <sys/kernel.h> | |
| 7a9e53ad | 86 | #include <sys/proc.h> |
| 984263bc MD |
87 | #include <sys/sysctl.h> |
| 88 | #include <sys/buf.h> | |
| 89 | #include <sys/conf.h> | |
| 7a9e53ad | 90 | #include <sys/diskslice.h> |
| 984263bc MD |
91 | #include <sys/disk.h> |
| 92 | #include <sys/malloc.h> | |
| 93 | #include <sys/sysctl.h> | |
| 94 | #include <machine/md_var.h> | |
| 95 | #include <sys/ctype.h> | |
| 7a9e53ad MD |
96 | #include <sys/syslog.h> |
| 97 | #include <sys/device.h> | |
| 335dda38 MD |
98 | #include <sys/msgport.h> |
| 99 | #include <sys/msgport2.h> | |
| 7a9e53ad | 100 | #include <sys/buf2.h> |
| 984263bc MD |
101 | |
| 102 | static MALLOC_DEFINE(M_DISK, "disk", "disk data"); | |
| 103 | ||
| 984263bc MD |
104 | static d_open_t diskopen; |
| 105 | static d_close_t diskclose; | |
| 106 | static d_ioctl_t diskioctl; | |
| fef8985e | 107 | static d_strategy_t diskstrategy; |
| 984263bc | 108 | static d_psize_t diskpsize; |
| e4c9c0c8 | 109 | static d_clone_t diskclone; |
| fef8985e | 110 | static d_dump_t diskdump; |
| 984263bc MD |
111 | |
| 112 | static LIST_HEAD(, disk) disklist = LIST_HEAD_INITIALIZER(&disklist); | |
| 113 | ||
| fef8985e | 114 | static struct dev_ops disk_ops = { |
| daf0c2f6 | 115 | { "disk", 0, D_DISK }, |
| fef8985e MD |
116 | .d_open = diskopen, |
| 117 | .d_close = diskclose, | |
| 118 | .d_read = physread, | |
| 119 | .d_write = physwrite, | |
| 120 | .d_ioctl = diskioctl, | |
| 121 | .d_strategy = diskstrategy, | |
| 122 | .d_dump = diskdump, | |
| 123 | .d_psize = diskpsize, | |
| 124 | .d_clone = diskclone | |
| 125 | }; | |
| 126 | ||
| 335dda38 | 127 | /* |
| fef8985e MD |
128 | * Create a raw device for the dev_ops template (which is returned). Also |
| 129 | * create a slice and unit managed disk and overload the user visible | |
| 130 | * device space with it. | |
| e4c9c0c8 | 131 | * |
| fef8985e MD |
132 | * NOTE: The returned raw device is NOT a slice and unit managed device. |
| 133 | * It is an actual raw device representing the raw disk as specified by | |
| 134 | * the passed dev_ops. The disk layer not only returns such a raw device, | |
| 135 | * it also uses it internally when passing (modified) commands through. | |
| 335dda38 | 136 | */ |
| b13267a5 | 137 | cdev_t |
| a688b15c | 138 | disk_create(int unit, struct disk *dp, struct dev_ops *raw_ops) |
| 984263bc | 139 | { |
| b13267a5 | 140 | cdev_t rawdev; |
| fef8985e | 141 | struct dev_ops *dev_ops; |
| e4c9c0c8 MD |
142 | |
| 143 | /* | |
| 144 | * Create the raw backing device | |
| 145 | */ | |
| fef8985e | 146 | compile_dev_ops(raw_ops); |
| 5350e1e9 | 147 | rawdev = make_dev(raw_ops, dkmakewholedisk(unit), |
| e4c9c0c8 | 148 | UID_ROOT, GID_OPERATOR, 0640, |
| fef8985e | 149 | "%s%d", raw_ops->head.name, unit); |
| 984263bc MD |
150 | |
| 151 | bzero(dp, sizeof(*dp)); | |
| 152 | ||
| e4c9c0c8 MD |
153 | /* |
| 154 | * We install a custom cdevsw rather then the passed cdevsw, | |
| 155 | * and save our disk structure in d_data so we can get at it easily | |
| 156 | * without any complex cloning code. | |
| 335dda38 | 157 | */ |
| fef8985e MD |
158 | dev_ops = dev_ops_add_override(rawdev, &disk_ops, |
| 159 | dkunitmask(), dkmakeunit(unit)); | |
| 160 | dev_ops->head.data = dp; | |
| 161 | ||
| e4c9c0c8 | 162 | dp->d_rawdev = rawdev; |
| fef8985e MD |
163 | dp->d_raw_ops = raw_ops; |
| 164 | dp->d_dev_ops = dev_ops; | |
| 165 | dp->d_cdev = make_dev(dev_ops, | |
| 5350e1e9 | 166 | dkmakewholedisk(unit), |
| e4c9c0c8 | 167 | UID_ROOT, GID_OPERATOR, 0640, |
| fef8985e | 168 | "%s%d", dev_ops->head.name, unit); |
| e4c9c0c8 | 169 | |
| 984263bc | 170 | LIST_INSERT_HEAD(&disklist, dp, d_list); |
| e4c9c0c8 | 171 | return (dp->d_rawdev); |
| 984263bc MD |
172 | } |
| 173 | ||
| e4c9c0c8 | 174 | /* |
| a688b15c MD |
175 | * Disk drivers must call this routine when media parameters are available |
| 176 | * or have changed. | |
| 177 | */ | |
| 178 | void | |
| 179 | disk_setdiskinfo(struct disk *disk, struct disk_info *info) | |
| 180 | { | |
| a688b15c MD |
181 | bcopy(info, &disk->d_info, sizeof(disk->d_info)); |
| 182 | info = &disk->d_info; | |
| 183 | ||
| 184 | KKASSERT(info->d_media_size == 0 || info->d_media_blksize == 0); | |
| 185 | if (info->d_media_size == 0 && info->d_media_blocks) { | |
| 186 | info->d_media_size = (u_int64_t)info->d_media_blocks * | |
| 187 | info->d_media_blksize; | |
| 188 | } else if (info->d_media_size && info->d_media_blocks == 0 && | |
| 189 | info->d_media_blksize) { | |
| 190 | info->d_media_blocks = info->d_media_size / | |
| 191 | info->d_media_blksize; | |
| 192 | } | |
| 5d6c6885 MD |
193 | |
| 194 | /* | |
| 195 | * The si_* fields for rawdev are not set until after the | |
| 196 | * disk_create() call, so someone using the cooked version | |
| 197 | * of the raw device (i.e. da0s0) will not get the right | |
| 198 | * si_iosize_max unless we fix it up here. | |
| 199 | */ | |
| 200 | if (disk->d_cdev && disk->d_rawdev && | |
| 201 | disk->d_cdev->si_iosize_max == 0) { | |
| 202 | disk->d_cdev->si_iosize_max = disk->d_rawdev->si_iosize_max; | |
| 203 | disk->d_cdev->si_bsize_phys = disk->d_rawdev->si_bsize_phys; | |
| 204 | disk->d_cdev->si_bsize_best = disk->d_rawdev->si_bsize_best; | |
| 205 | } | |
| a688b15c MD |
206 | } |
| 207 | ||
| 208 | /* | |
| e4c9c0c8 MD |
209 | * This routine is called when an adapter detaches. The higher level |
| 210 | * managed disk device is destroyed while the lower level raw device is | |
| 211 | * released. | |
| 212 | */ | |
| 335dda38 MD |
213 | void |
| 214 | disk_destroy(struct disk *disk) | |
| 215 | { | |
| 493f17de MD |
216 | u_int match; |
| 217 | ||
| fef8985e | 218 | if (disk->d_dev_ops) { |
| 493f17de MD |
219 | match = dkmakeunit(dkunit(disk->d_cdev)); |
| 220 | dev_ops_remove_override(disk->d_dev_ops, dkunitmask(), match); | |
| e4c9c0c8 MD |
221 | LIST_REMOVE(disk, d_list); |
| 222 | } | |
| fef8985e | 223 | if (disk->d_raw_ops) { |
| 493f17de MD |
224 | match = dkmakeunit(dkunit(disk->d_rawdev)); |
| 225 | destroy_all_devs(disk->d_raw_ops, dkunitmask(), match); | |
| 0015ee8d | 226 | } |
| 335dda38 | 227 | bzero(disk, sizeof(*disk)); |
| 335dda38 MD |
228 | } |
| 229 | ||
| 984263bc | 230 | int |
| e0fc5693 | 231 | disk_dumpcheck(cdev_t dev, u_int64_t *count, u_int64_t *blkno, u_int *secsize) |
| 984263bc | 232 | { |
| a6c0f342 MD |
233 | struct partinfo pinfo; |
| 234 | int error; | |
| 984263bc | 235 | |
| a6c0f342 MD |
236 | bzero(&pinfo, sizeof(pinfo)); |
| 237 | error = dev_dioctl(dev, DIOCGPART, (void *)&pinfo, 0, proc0.p_ucred); | |
| 238 | if (error) | |
| 239 | return (error); | |
| 240 | if (pinfo.media_blksize == 0) | |
| 984263bc | 241 | return (ENXIO); |
| a6c0f342 | 242 | *count = (u_int64_t)Maxmem * PAGE_SIZE / pinfo.media_blksize; |
| 1c3c151b | 243 | if (dumplo64 < pinfo.reserved_blocks || |
| a6c0f342 MD |
244 | dumplo64 + *count > pinfo.media_blocks) { |
| 245 | return (ENOSPC); | |
| 246 | } | |
| 247 | *blkno = dumplo64 + pinfo.media_offset / pinfo.media_blksize; | |
| 248 | *secsize = pinfo.media_blksize; | |
| 984263bc | 249 | return (0); |
| 984263bc MD |
250 | } |
| 251 | ||
| 252 | void | |
| 253 | disk_invalidate (struct disk *disk) | |
| 254 | { | |
| 255 | if (disk->d_slice) | |
| 256 | dsgone(&disk->d_slice); | |
| 257 | } | |
| 258 | ||
| 984263bc MD |
259 | struct disk * |
| 260 | disk_enumerate(struct disk *disk) | |
| 261 | { | |
| 262 | if (!disk) | |
| 263 | return (LIST_FIRST(&disklist)); | |
| 264 | else | |
| 265 | return (LIST_NEXT(disk, d_list)); | |
| 266 | } | |
| 267 | ||
| fbda7fa6 MD |
268 | static |
| 269 | int | |
| 984263bc MD |
270 | sysctl_disks(SYSCTL_HANDLER_ARGS) |
| 271 | { | |
| 272 | struct disk *disk; | |
| 273 | int error, first; | |
| 274 | ||
| 275 | disk = NULL; | |
| 276 | first = 1; | |
| 277 | ||
| 278 | while ((disk = disk_enumerate(disk))) { | |
| 279 | if (!first) { | |
| 280 | error = SYSCTL_OUT(req, " ", 1); | |
| 281 | if (error) | |
| 282 | return error; | |
| 283 | } else { | |
| 284 | first = 0; | |
| 285 | } | |
| 95ce4036 HP |
286 | error = SYSCTL_OUT(req, disk->d_rawdev->si_name, |
| 287 | strlen(disk->d_rawdev->si_name)); | |
| 984263bc MD |
288 | if (error) |
| 289 | return error; | |
| 290 | } | |
| 291 | error = SYSCTL_OUT(req, "", 1); | |
| 292 | return error; | |
| 293 | } | |
| 294 | ||
| 3641b7ca | 295 | SYSCTL_PROC(_kern, OID_AUTO, disks, CTLTYPE_STRING | CTLFLAG_RD, NULL, 0, |
| 984263bc MD |
296 | sysctl_disks, "A", "names of available disks"); |
| 297 | ||
| 298 | /* | |
| e4c9c0c8 MD |
299 | * Open a disk device or partition. |
| 300 | */ | |
| fbda7fa6 MD |
301 | static |
| 302 | int | |
| fef8985e | 303 | diskopen(struct dev_open_args *ap) |
| 984263bc | 304 | { |
| b13267a5 | 305 | cdev_t dev = ap->a_head.a_dev; |
| 984263bc MD |
306 | struct disk *dp; |
| 307 | int error; | |
| 308 | ||
| e4c9c0c8 MD |
309 | /* |
| 310 | * dp can't be NULL here XXX. | |
| 311 | */ | |
| e4c9c0c8 | 312 | dp = dev->si_disk; |
| 335dda38 | 313 | if (dp == NULL) |
| 984263bc | 314 | return (ENXIO); |
| fef8985e | 315 | error = 0; |
| 984263bc | 316 | |
| e4c9c0c8 MD |
317 | /* |
| 318 | * Deal with open races | |
| 319 | */ | |
| 984263bc MD |
320 | while (dp->d_flags & DISKFLAG_LOCK) { |
| 321 | dp->d_flags |= DISKFLAG_WANTED; | |
| 377d4740 | 322 | error = tsleep(dp, PCATCH, "diskopen", hz); |
| 984263bc MD |
323 | if (error) |
| 324 | return (error); | |
| 325 | } | |
| 326 | dp->d_flags |= DISKFLAG_LOCK; | |
| 327 | ||
| e4c9c0c8 MD |
328 | /* |
| 329 | * Open the underlying raw device. | |
| 330 | */ | |
| 984263bc | 331 | if (!dsisopen(dp->d_slice)) { |
| e4c9c0c8 | 332 | #if 0 |
| 984263bc MD |
333 | if (!pdev->si_iosize_max) |
| 334 | pdev->si_iosize_max = dev->si_iosize_max; | |
| e4c9c0c8 | 335 | #endif |
| fef8985e MD |
336 | error = dev_dopen(dp->d_rawdev, ap->a_oflags, |
| 337 | ap->a_devtype, ap->a_cred); | |
| 984263bc MD |
338 | } |
| 339 | ||
| e4c9c0c8 MD |
340 | /* |
| 341 | * Inherit properties from the underlying device now that it is | |
| 342 | * open. | |
| 343 | */ | |
| fef8985e | 344 | dev_dclone(dev); |
| 984263bc MD |
345 | |
| 346 | if (error) | |
| 347 | goto out; | |
| 348 | ||
| a688b15c | 349 | error = dsopen(dev, ap->a_devtype, dp->d_info.d_dsflags, |
| 84f8b009 | 350 | &dp->d_slice, &dp->d_info); |
| 984263bc MD |
351 | |
| 352 | if (!dsisopen(dp->d_slice)) | |
| fef8985e | 353 | dev_dclose(dp->d_rawdev, ap->a_oflags, ap->a_devtype); |
| 984263bc MD |
354 | out: |
| 355 | dp->d_flags &= ~DISKFLAG_LOCK; | |
| 356 | if (dp->d_flags & DISKFLAG_WANTED) { | |
| 357 | dp->d_flags &= ~DISKFLAG_WANTED; | |
| 358 | wakeup(dp); | |
| 359 | } | |
| 360 | ||
| 361 | return(error); | |
| 362 | } | |
| 363 | ||
| e4c9c0c8 MD |
364 | /* |
| 365 | * Close a disk device or partition | |
| 366 | */ | |
| fbda7fa6 MD |
367 | static |
| 368 | int | |
| fef8985e | 369 | diskclose(struct dev_close_args *ap) |
| 984263bc | 370 | { |
| b13267a5 | 371 | cdev_t dev = ap->a_head.a_dev; |
| 984263bc MD |
372 | struct disk *dp; |
| 373 | int error; | |
| 984263bc MD |
374 | |
| 375 | error = 0; | |
| e4c9c0c8 MD |
376 | dp = dev->si_disk; |
| 377 | ||
| fef8985e | 378 | dsclose(dev, ap->a_devtype, dp->d_slice); |
| 984263bc | 379 | if (!dsisopen(dp->d_slice)) |
| fef8985e MD |
380 | error = dev_dclose(dp->d_rawdev, ap->a_fflag, ap->a_devtype); |
| 381 | return (error); | |
| 382 | } | |
| 383 | ||
| 384 | /* | |
| 385 | * First execute the ioctl on the disk device, and if it isn't supported | |
| 386 | * try running it on the backing device. | |
| 387 | */ | |
| 388 | static | |
| 389 | int | |
| 390 | diskioctl(struct dev_ioctl_args *ap) | |
| 391 | { | |
| b13267a5 | 392 | cdev_t dev = ap->a_head.a_dev; |
| fef8985e MD |
393 | struct disk *dp; |
| 394 | int error; | |
| 395 | ||
| 396 | dp = dev->si_disk; | |
| 397 | if (dp == NULL) | |
| 398 | return (ENXIO); | |
| 84f8b009 MD |
399 | error = dsioctl(dev, ap->a_cmd, ap->a_data, ap->a_fflag, |
| 400 | &dp->d_slice, &dp->d_info); | |
| fef8985e MD |
401 | if (error == ENOIOCTL) { |
| 402 | error = dev_dioctl(dp->d_rawdev, ap->a_cmd, ap->a_data, | |
| 403 | ap->a_fflag, ap->a_cred); | |
| 404 | } | |
| 984263bc MD |
405 | return (error); |
| 406 | } | |
| 407 | ||
| e4c9c0c8 MD |
408 | /* |
| 409 | * Execute strategy routine | |
| 410 | */ | |
| fbda7fa6 | 411 | static |
| fef8985e MD |
412 | int |
| 413 | diskstrategy(struct dev_strategy_args *ap) | |
| 984263bc | 414 | { |
| b13267a5 | 415 | cdev_t dev = ap->a_head.a_dev; |
| fef8985e | 416 | struct bio *bio = ap->a_bio; |
| 81b5c339 | 417 | struct bio *nbio; |
| 984263bc MD |
418 | struct disk *dp; |
| 419 | ||
| 81b5c339 | 420 | dp = dev->si_disk; |
| 984263bc | 421 | |
| e4c9c0c8 | 422 | if (dp == NULL) { |
| 81b5c339 MD |
423 | bio->bio_buf->b_error = ENXIO; |
| 424 | bio->bio_buf->b_flags |= B_ERROR; | |
| 425 | biodone(bio); | |
| fef8985e | 426 | return(0); |
| 984263bc | 427 | } |
| 81b5c339 | 428 | KKASSERT(dev->si_disk == dp); |
| 984263bc | 429 | |
| 6f76c57e HP |
430 | /* |
| 431 | * The dscheck() function will also transform the slice relative | |
| 54078292 | 432 | * block number i.e. bio->bio_offset into a block number that can be |
| 9a71d53f MD |
433 | * passed directly to the underlying raw device. If dscheck() |
| 434 | * returns NULL it will have handled the bio for us (e.g. EOF | |
| 435 | * or error due to being beyond the device size). | |
| 6f76c57e | 436 | */ |
| 9a71d53f MD |
437 | if ((nbio = dscheck(dev, bio, dp->d_slice)) != NULL) |
| 438 | dev_dstrategy(dp->d_rawdev, nbio); | |
| 439 | else | |
| 81b5c339 | 440 | biodone(bio); |
| fef8985e | 441 | return(0); |
| 984263bc MD |
442 | } |
| 443 | ||
| 335dda38 | 444 | /* |
| fef8985e | 445 | * Return the partition size in ?blocks? |
| 335dda38 | 446 | */ |
| fbda7fa6 MD |
447 | static |
| 448 | int | |
| fef8985e | 449 | diskpsize(struct dev_psize_args *ap) |
| 984263bc | 450 | { |
| b13267a5 | 451 | cdev_t dev = ap->a_head.a_dev; |
| 984263bc | 452 | struct disk *dp; |
| 984263bc | 453 | |
| e4c9c0c8 MD |
454 | dp = dev->si_disk; |
| 455 | if (dp == NULL) | |
| fef8985e MD |
456 | return(ENODEV); |
| 457 | ap->a_result = dssize(dev, &dp->d_slice); | |
| 458 | return(0); | |
| 984263bc MD |
459 | } |
| 460 | ||
| e4c9c0c8 | 461 | /* |
| fef8985e MD |
462 | * When new device entries are instantiated, make sure they inherit our |
| 463 | * si_disk structure and block and iosize limits from the raw device. | |
| e4c9c0c8 | 464 | * |
| fef8985e MD |
465 | * This routine is always called synchronously in the context of the |
| 466 | * client. | |
| 467 | * | |
| 468 | * XXX The various io and block size constraints are not always initialized | |
| 469 | * properly by devices. | |
| e4c9c0c8 | 470 | */ |
| fbda7fa6 MD |
471 | static |
| 472 | int | |
| fef8985e | 473 | diskclone(struct dev_clone_args *ap) |
| 984263bc | 474 | { |
| b13267a5 | 475 | cdev_t dev = ap->a_head.a_dev; |
| 984263bc | 476 | struct disk *dp; |
| 984263bc | 477 | |
| fef8985e MD |
478 | dp = dev->si_ops->head.data; |
| 479 | KKASSERT(dp != NULL); | |
| 480 | dev->si_disk = dp; | |
| 481 | dev->si_iosize_max = dp->d_rawdev->si_iosize_max; | |
| 482 | dev->si_bsize_phys = dp->d_rawdev->si_bsize_phys; | |
| 483 | dev->si_bsize_best = dp->d_rawdev->si_bsize_best; | |
| 484 | return(0); | |
| 485 | } | |
| 486 | ||
| 487 | int | |
| 488 | diskdump(struct dev_dump_args *ap) | |
| 489 | { | |
| b13267a5 | 490 | cdev_t dev = ap->a_head.a_dev; |
| fef8985e MD |
491 | struct disk *dp = dev->si_ops->head.data; |
| 492 | int error; | |
| 493 | ||
| 494 | error = disk_dumpcheck(dev, &ap->a_count, &ap->a_blkno, &ap->a_secsize); | |
| 495 | if (error == 0) { | |
| 496 | ap->a_head.a_dev = dp->d_rawdev; | |
| 497 | error = dev_doperate(&ap->a_head); | |
| 984263bc | 498 | } |
| fef8985e MD |
499 | |
| 500 | return(error); | |
| 984263bc MD |
501 | } |
| 502 | ||
| fef8985e | 503 | |
| 984263bc MD |
504 | SYSCTL_INT(_debug_sizeof, OID_AUTO, diskslices, CTLFLAG_RD, |
| 505 | 0, sizeof(struct diskslices), "sizeof(struct diskslices)"); | |
| 506 | ||
| 507 | SYSCTL_INT(_debug_sizeof, OID_AUTO, disk, CTLFLAG_RD, | |
| 508 | 0, sizeof(struct disk), "sizeof(struct disk)"); | |
| 7a9e53ad MD |
509 | |
| 510 | ||
| 511 | /* | |
| 512 | * Seek sort for disks. | |
| 513 | * | |
| 81b5c339 | 514 | * The bio_queue keep two queues, sorted in ascending block order. The first |
| 7a9e53ad MD |
515 | * queue holds those requests which are positioned after the current block |
| 516 | * (in the first request); the second, which starts at queue->switch_point, | |
| 517 | * holds requests which came in after their block number was passed. Thus | |
| 518 | * we implement a one way scan, retracting after reaching the end of the drive | |
| 519 | * to the first request on the second queue, at which time it becomes the | |
| 520 | * first queue. | |
| 521 | * | |
| 522 | * A one-way scan is natural because of the way UNIX read-ahead blocks are | |
| 523 | * allocated. | |
| 524 | */ | |
| 525 | void | |
| 81b5c339 | 526 | bioqdisksort(struct bio_queue_head *bioq, struct bio *bio) |
| 7a9e53ad | 527 | { |
| 81b5c339 MD |
528 | struct bio *bq; |
| 529 | struct bio *bn; | |
| 530 | struct bio *be; | |
| 7a9e53ad | 531 | |
| 81b5c339 | 532 | be = TAILQ_LAST(&bioq->queue, bio_queue); |
| 7a9e53ad MD |
533 | /* |
| 534 | * If the queue is empty or we are an | |
| 535 | * ordered transaction, then it's easy. | |
| 536 | */ | |
| 81b5c339 MD |
537 | if ((bq = bioq_first(bioq)) == NULL || |
| 538 | (bio->bio_buf->b_flags & B_ORDERED) != 0) { | |
| 539 | bioq_insert_tail(bioq, bio); | |
| 7a9e53ad | 540 | return; |
| 81b5c339 | 541 | } else if (bioq->insert_point != NULL) { |
| 7a9e53ad MD |
542 | |
| 543 | /* | |
| 544 | * A certain portion of the list is | |
| 545 | * "locked" to preserve ordering, so | |
| 546 | * we can only insert after the insert | |
| 547 | * point. | |
| 548 | */ | |
| 81b5c339 | 549 | bq = bioq->insert_point; |
| 7a9e53ad MD |
550 | } else { |
| 551 | ||
| 552 | /* | |
| 553 | * If we lie before the last removed (currently active) | |
| 554 | * request, and are not inserting ourselves into the | |
| 555 | * "locked" portion of the list, then we must add ourselves | |
| 556 | * to the second request list. | |
| 557 | */ | |
| 54078292 | 558 | if (bio->bio_offset < bioq->last_offset) { |
| 81b5c339 | 559 | bq = bioq->switch_point; |
| 7a9e53ad MD |
560 | /* |
| 561 | * If we are starting a new secondary list, | |
| 562 | * then it's easy. | |
| 563 | */ | |
| 564 | if (bq == NULL) { | |
| 81b5c339 MD |
565 | bioq->switch_point = bio; |
| 566 | bioq_insert_tail(bioq, bio); | |
| 7a9e53ad MD |
567 | return; |
| 568 | } | |
| 569 | /* | |
| 570 | * If we lie ahead of the current switch point, | |
| 571 | * insert us before the switch point and move | |
| 572 | * the switch point. | |
| 573 | */ | |
| 54078292 | 574 | if (bio->bio_offset < bq->bio_offset) { |
| 81b5c339 MD |
575 | bioq->switch_point = bio; |
| 576 | TAILQ_INSERT_BEFORE(bq, bio, bio_act); | |
| 7a9e53ad MD |
577 | return; |
| 578 | } | |
| 579 | } else { | |
| 81b5c339 MD |
580 | if (bioq->switch_point != NULL) |
| 581 | be = TAILQ_PREV(bioq->switch_point, | |
| 582 | bio_queue, bio_act); | |
| 7a9e53ad | 583 | /* |
| 54078292 | 584 | * If we lie between last_offset and bq, |
| 7a9e53ad MD |
585 | * insert before bq. |
| 586 | */ | |
| 54078292 | 587 | if (bio->bio_offset < bq->bio_offset) { |
| 81b5c339 | 588 | TAILQ_INSERT_BEFORE(bq, bio, bio_act); |
| 7a9e53ad MD |
589 | return; |
| 590 | } | |
| 591 | } | |
| 592 | } | |
| 593 | ||
| 594 | /* | |
| 595 | * Request is at/after our current position in the list. | |
| 596 | * Optimize for sequential I/O by seeing if we go at the tail. | |
| 597 | */ | |
| 54078292 | 598 | if (bio->bio_offset > be->bio_offset) { |
| 81b5c339 | 599 | TAILQ_INSERT_AFTER(&bioq->queue, be, bio, bio_act); |
| 7a9e53ad MD |
600 | return; |
| 601 | } | |
| 602 | ||
| 603 | /* Otherwise, insertion sort */ | |
| 81b5c339 | 604 | while ((bn = TAILQ_NEXT(bq, bio_act)) != NULL) { |
| 7a9e53ad MD |
605 | |
| 606 | /* | |
| 607 | * We want to go after the current request if it is the end | |
| 608 | * of the first request list, or if the next request is a | |
| 609 | * larger cylinder than our request. | |
| 610 | */ | |
| 81b5c339 | 611 | if (bn == bioq->switch_point |
| 54078292 | 612 | || bio->bio_offset < bn->bio_offset) |
| 7a9e53ad MD |
613 | break; |
| 614 | bq = bn; | |
| 615 | } | |
| 81b5c339 | 616 | TAILQ_INSERT_AFTER(&bioq->queue, bq, bio, bio_act); |
| 7a9e53ad MD |
617 | } |
| 618 | ||
| 7a9e53ad MD |
619 | /* |
| 620 | * Disk error is the preface to plaintive error messages | |
| 621 | * about failing disk transfers. It prints messages of the form | |
| 622 | ||
| 623 | hp0g: hard error reading fsbn 12345 of 12344-12347 (hp0 bn %d cn %d tn %d sn %d) | |
| 624 | ||
| 625 | * if the offset of the error in the transfer and a disk label | |
| 626 | * are both available. blkdone should be -1 if the position of the error | |
| 627 | * is unknown; the disklabel pointer may be null from drivers that have not | |
| 6ea70f76 | 628 | * been converted to use them. The message is printed with kprintf |
| 7a9e53ad | 629 | * if pri is LOG_PRINTF, otherwise it uses log at the specified priority. |
| 6ea70f76 | 630 | * The message should be completed (with at least a newline) with kprintf |
| a0a36cfd | 631 | * or log(-1, ...), respectively. There is no trailing space. |
| 7a9e53ad MD |
632 | */ |
| 633 | void | |
| a688b15c | 634 | diskerr(struct bio *bio, cdev_t dev, const char *what, int pri, int donecnt) |
| 7a9e53ad | 635 | { |
| 81b5c339 | 636 | struct buf *bp = bio->bio_buf; |
| ee3ad2c9 MD |
637 | int unit = dkunit(dev); |
| 638 | int slice = dkslice(dev); | |
| 639 | int part = dkpart(dev); | |
| 7a9e53ad MD |
640 | char partname[2]; |
| 641 | char *sname; | |
| c6f49b01 MD |
642 | const char *term; |
| 643 | ||
| 644 | switch(bp->b_cmd) { | |
| 645 | case BUF_CMD_READ: | |
| 646 | term = "read"; | |
| 647 | break; | |
| 648 | case BUF_CMD_WRITE: | |
| 649 | term = "write"; | |
| 650 | break; | |
| 651 | default: | |
| 652 | term = "access"; | |
| 653 | break; | |
| 654 | } | |
| ee3ad2c9 | 655 | sname = dsname(dev, unit, slice, part, partname); |
| c6f49b01 | 656 | kprintf("%s%s: %s %sing ", sname, partname, what, term); |
| 973c11b9 MD |
657 | kprintf("offset %012llx for %d", |
| 658 | (long long)bio->bio_offset, | |
| 659 | bp->b_bcount); | |
| 54078292 | 660 | if (donecnt) |
| 6ea70f76 | 661 | kprintf(" (%d bytes completed)", donecnt); |
| 7a9e53ad | 662 | } |
| 81b5c339 | 663 | |
| a8873631 MD |
664 | /* |
| 665 | * Locate a disk device | |
| 666 | */ | |
| 667 | cdev_t | |
| 668 | disk_locate(const char *devname) | |
| 669 | { | |
| 670 | struct disk *dp; | |
| 671 | cdev_t dev; | |
| 672 | char *ptr; | |
| 673 | int i; | |
| 674 | int prefix; | |
| 675 | int slice; | |
| 676 | int part; | |
| 677 | ||
| 678 | /* | |
| 679 | * Device and unit | |
| 680 | */ | |
| 681 | for (i = 0; devname[i]; ++i) { | |
| 682 | if (devname[i] >= '0' && devname[i] <= '9') | |
| 683 | break; | |
| 684 | } | |
| 685 | while (devname[i] >= '0' && devname[i] <= '9') | |
| 686 | ++i; | |
| 687 | prefix = i; | |
| 688 | ||
| 689 | /* | |
| 690 | * Slice and partition. s1 starts at slice #2. s0 is slice #0. | |
| 691 | * slice #1 is the WHOLE_DISK_SLICE. | |
| 692 | */ | |
| 693 | if (devname[i] == 's') { | |
| 694 | slice = strtol(devname + i + 1, &ptr, 10); | |
| 695 | i = (const char *)ptr - devname; | |
| 696 | if (slice > 0) | |
| 697 | ++slice; | |
| 698 | } else { | |
| 699 | slice = WHOLE_DISK_SLICE; | |
| 700 | } | |
| 701 | if (devname[i] >= 'a' && devname[i] <= 'z') { | |
| 702 | part = devname[i] - 'a'; | |
| 703 | } else { | |
| 704 | part = WHOLE_SLICE_PART; | |
| 705 | } | |
| 706 | ||
| 707 | /* | |
| 708 | * Find the device | |
| 709 | */ | |
| 710 | LIST_FOREACH(dp, &disklist, d_list) { | |
| 711 | dev = dp->d_cdev; | |
| 712 | if (strlen(dev->si_name) == prefix && | |
| 713 | strncmp(devname, dev->si_name, prefix) == 0 | |
| 714 | ) { | |
| 715 | return(dkmodpart(dkmodslice(dev, slice), part)); | |
| 716 | } | |
| 717 | } | |
| 718 | return(NULL); | |
| 719 | } | |
| 720 |