subr_disk.c revision 85624
1/* 2 * ---------------------------------------------------------------------------- 3 * "THE BEER-WARE LICENSE" (Revision 42): 4 * <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you 5 * can do whatever you want with this stuff. If we meet some day, and you think 6 * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp 7 * ---------------------------------------------------------------------------- 8 * 9 * $FreeBSD: head/sys/kern/subr_disk.c 85624 2001-10-28 09:39:28Z phk $ 10 * 11 */ 12 13#include <sys/param.h> 14#include <sys/systm.h> 15#include <sys/kernel.h> 16#include <sys/sysctl.h> 17#include <sys/bio.h> 18#include <sys/conf.h> 19#include <sys/disk.h> 20#include <sys/malloc.h> 21#include <sys/sysctl.h> 22#include <machine/md_var.h> 23#include <sys/ctype.h> 24 25static MALLOC_DEFINE(M_DISK, "disk", "disk data"); 26 27static d_strategy_t diskstrategy; 28static d_open_t diskopen; 29static d_close_t diskclose; 30static d_ioctl_t diskioctl; 31static d_psize_t diskpsize; 32 33static LIST_HEAD(, disk) disklist = LIST_HEAD_INITIALIZER(&disklist); 34 35void disk_dev_synth(dev_t dev); 36 37void 38disk_dev_synth(dev_t dev) 39{ 40 struct disk *dp; 41 int u, s, p; 42 dev_t pdev; 43 44 LIST_FOREACH(dp, &disklist, d_list) { 45 if (major(dev) != dp->d_devsw->d_maj) 46 continue; 47 u = dkunit(dev); 48 p = RAW_PART; 49 s = WHOLE_DISK_SLICE; 50 pdev = makedev(dp->d_devsw->d_maj, dkmakeminor(u, s, p)); 51 if (pdev->si_devsw == NULL) 52 return; /* Probably a unit we don't have */ 53 s = dkslice(dev); 54 p = dkpart(dev); 55 if (s == WHOLE_DISK_SLICE && p == RAW_PART) { 56 /* XXX: actually should not happen */ 57 dev = make_dev(pdev->si_devsw, dkmakeminor(u, s, p), 58 UID_ROOT, GID_OPERATOR, 0640, "%s%d", 59 dp->d_devsw->d_name, u); 60 dev_depends(pdev, dev); 61 return; 62 } 63 if (s == COMPATIBILITY_SLICE) { 64 dev = make_dev(pdev->si_devsw, dkmakeminor(u, s, p), 65 UID_ROOT, GID_OPERATOR, 0640, "%s%d%c", 66 dp->d_devsw->d_name, u, 'a' + p); 67 dev_depends(pdev, dev); 68 return; 69 } 70 dev = make_dev(pdev->si_devsw, dkmakeminor(u, s, p), 71 UID_ROOT, GID_OPERATOR, 0640, "%s%ds%d%c", 72 dp->d_devsw->d_name, u, s - BASE_SLICE + 1, 'a' + p); 73 dev_depends(pdev, dev); 74 if (p == RAW_PART) 75 make_dev_alias(dev, "%s%ds%d", 76 dp->d_devsw->d_name, u, s - BASE_SLICE + 1); 77 return; 78 } 79} 80 81static void 82disk_clone(void *arg, char *name, int namelen, dev_t *dev) 83{ 84 struct disk *dp; 85 char const *d; 86 int i, u, s, p; 87 dev_t pdev; 88 89 if (*dev != NODEV) 90 return; 91 92 LIST_FOREACH(dp, &disklist, d_list) { 93 d = dp->d_devsw->d_name; 94 i = strlen(d); 95 if (bcmp(d, name, i) != 0) 96 continue; 97 u = 0; 98 if (!isdigit(name[i])) 99 continue; 100 while (isdigit(name[i])) { 101 u *= 10; 102 u += name[i++] - '0'; 103 } 104 if (u > DKMAXUNIT) 105 continue; 106 p = RAW_PART; 107 s = WHOLE_DISK_SLICE; 108 pdev = makedev(dp->d_devsw->d_maj, dkmakeminor(u, s, p)); 109 if (pdev->si_disk == NULL) 110 continue; 111 if (name[i] != '\0') { 112 if (name[i] == 's') { 113 s = 0; 114 i++; 115 if (!isdigit(name[i])) 116 continue; 117 while (isdigit(name[i])) { 118 s *= 10; 119 s += name[i++] - '0'; 120 } 121 s += BASE_SLICE - 1; 122 } else { 123 s = COMPATIBILITY_SLICE; 124 } 125 if (name[i] == '\0') 126 ; 127 else if (name[i + 1] != '\0') 128 return; 129 else if (name[i] < 'a' || name[i] > 'h') 130 continue; 131 else 132 p = name[i] - 'a'; 133 } 134 135 if (s >= BASE_SLICE) 136 *dev = make_dev(pdev->si_devsw, dkmakeminor(u, s, p), 137 UID_ROOT, GID_OPERATOR, 0640, "%s%ds%d%c", 138 pdev->si_devsw->d_name, u, s - BASE_SLICE + 1, p + 'a'); 139 else 140 *dev = make_dev(pdev->si_devsw, dkmakeminor(u, s, p), 141 UID_ROOT, GID_OPERATOR, 0640, name); 142 dev_depends(pdev, *dev); 143 if (s >= BASE_SLICE && p == RAW_PART) { 144 make_dev_alias(*dev, "%s%ds%d", 145 pdev->si_devsw->d_name, u, s - BASE_SLICE + 1); 146 } 147 return; 148 } 149} 150 151static void 152inherit_raw(dev_t pdev, dev_t dev) 153{ 154 dev->si_disk = pdev->si_disk; 155 dev->si_drv1 = pdev->si_drv1; 156 dev->si_drv2 = pdev->si_drv2; 157 dev->si_iosize_max = pdev->si_iosize_max; 158 dev->si_bsize_phys = pdev->si_bsize_phys; 159 dev->si_bsize_best = pdev->si_bsize_best; 160} 161 162dev_t 163disk_create(int unit, struct disk *dp, int flags, struct cdevsw *cdevsw, struct cdevsw *proto) 164{ 165 static int once; 166 dev_t dev; 167 168 if (!once) { 169 EVENTHANDLER_REGISTER(dev_clone, disk_clone, 0, 1000); 170 once++; 171 } 172 173 bzero(dp, sizeof(*dp)); 174 175 if (proto->d_open != diskopen) { 176 *proto = *cdevsw; 177 proto->d_open = diskopen; 178 proto->d_close = diskclose; 179 proto->d_ioctl = diskioctl; 180 proto->d_strategy = diskstrategy; 181 proto->d_psize = diskpsize; 182 cdevsw_add(proto); 183 } 184 185 if (bootverbose) 186 printf("Creating DISK %s%d\n", cdevsw->d_name, unit); 187 dev = make_dev(proto, dkmakeminor(unit, WHOLE_DISK_SLICE, RAW_PART), 188 UID_ROOT, GID_OPERATOR, 0640, "%s%d", cdevsw->d_name, unit); 189 190 dev->si_disk = dp; 191 dp->d_dev = dev; 192 dp->d_dsflags = flags; 193 dp->d_devsw = cdevsw; 194 LIST_INSERT_HEAD(&disklist, dp, d_list); 195 196 return (dev); 197} 198 199int 200disk_dumpcheck(dev_t dev, u_int *count, u_int *blkno, u_int *secsize) 201{ 202 struct disk *dp; 203 struct disklabel *dl; 204 u_int boff; 205 206 dp = dev->si_disk; 207 if (!dp) 208 return (ENXIO); 209 if (!dp->d_slice) 210 return (ENXIO); 211 dl = dsgetlabel(dev, dp->d_slice); 212 if (!dl) 213 return (ENXIO); 214 *count = Maxmem * (PAGE_SIZE / dl->d_secsize); 215 if (dumplo <= LABELSECTOR || 216 (dumplo + *count > dl->d_partitions[dkpart(dev)].p_size)) 217 return (EINVAL); 218 boff = dl->d_partitions[dkpart(dev)].p_offset + 219 dp->d_slice->dss_slices[dkslice(dev)].ds_offset; 220 *blkno = boff + dumplo; 221 *secsize = dl->d_secsize; 222 return (0); 223 224} 225 226void 227disk_invalidate (struct disk *disk) 228{ 229 if (disk->d_slice) 230 dsgone(&disk->d_slice); 231} 232 233void 234disk_destroy(dev_t dev) 235{ 236 LIST_REMOVE(dev->si_disk, d_list); 237 bzero(dev->si_disk, sizeof(*dev->si_disk)); 238 dev->si_disk = NULL; 239 destroy_dev(dev); 240 return; 241} 242 243struct disk * 244disk_enumerate(struct disk *disk) 245{ 246 if (!disk) 247 return (LIST_FIRST(&disklist)); 248 else 249 return (LIST_NEXT(disk, d_list)); 250} 251 252static int 253sysctl_disks(SYSCTL_HANDLER_ARGS) 254{ 255 struct disk *disk; 256 int error, first; 257 258 disk = NULL; 259 first = 1; 260 261 while ((disk = disk_enumerate(disk))) { 262 if (!first) { 263 error = SYSCTL_OUT(req, " ", 1); 264 if (error) 265 return error; 266 } else { 267 first = 0; 268 } 269 error = SYSCTL_OUT(req, disk->d_dev->si_name, strlen(disk->d_dev->si_name)); 270 if (error) 271 return error; 272 } 273 error = SYSCTL_OUT(req, "", 1); 274 return error; 275} 276 277SYSCTL_PROC(_kern, OID_AUTO, disks, CTLTYPE_STRING | CTLFLAG_RD, 0, NULL, 278 sysctl_disks, "A", "names of available disks"); 279 280/* 281 * The cdevsw functions 282 */ 283 284static int 285diskopen(dev_t dev, int oflags, int devtype, struct thread *td) 286{ 287 dev_t pdev; 288 struct disk *dp; 289 int error; 290 291 error = 0; 292 pdev = dkmodpart(dkmodslice(dev, WHOLE_DISK_SLICE), RAW_PART); 293 294 dp = pdev->si_disk; 295 if (!dp) 296 return (ENXIO); 297 298 while (dp->d_flags & DISKFLAG_LOCK) { 299 dp->d_flags |= DISKFLAG_WANTED; 300 error = tsleep(dp, PRIBIO | PCATCH, "diskopen", hz); 301 if (error) 302 return (error); 303 } 304 dp->d_flags |= DISKFLAG_LOCK; 305 306 if (!dsisopen(dp->d_slice)) { 307 if (!pdev->si_iosize_max) 308 pdev->si_iosize_max = dev->si_iosize_max; 309 error = dp->d_devsw->d_open(pdev, oflags, devtype, td); 310 } 311 312 /* Inherit properties from the whole/raw dev_t */ 313 inherit_raw(pdev, dev); 314 315 if (error) 316 goto out; 317 318 error = dsopen(dev, devtype, dp->d_dsflags, &dp->d_slice, &dp->d_label); 319 320 if (!dsisopen(dp->d_slice)) 321 dp->d_devsw->d_close(pdev, oflags, devtype, td); 322out: 323 dp->d_flags &= ~DISKFLAG_LOCK; 324 if (dp->d_flags & DISKFLAG_WANTED) { 325 dp->d_flags &= ~DISKFLAG_WANTED; 326 wakeup(dp); 327 } 328 329 return(error); 330} 331 332static int 333diskclose(dev_t dev, int fflag, int devtype, struct thread *td) 334{ 335 struct disk *dp; 336 int error; 337 dev_t pdev; 338 339 error = 0; 340 pdev = dkmodpart(dkmodslice(dev, WHOLE_DISK_SLICE), RAW_PART); 341 dp = pdev->si_disk; 342 if (!dp) 343 return (ENXIO); 344 dsclose(dev, devtype, dp->d_slice); 345 if (!dsisopen(dp->d_slice)) 346 error = dp->d_devsw->d_close(dp->d_dev, fflag, devtype, td); 347 return (error); 348} 349 350static void 351diskstrategy(struct bio *bp) 352{ 353 dev_t pdev; 354 struct disk *dp; 355 356 pdev = dkmodpart(dkmodslice(bp->bio_dev, WHOLE_DISK_SLICE), RAW_PART); 357 dp = pdev->si_disk; 358 bp->bio_resid = bp->bio_bcount; 359 if (dp != bp->bio_dev->si_disk) 360 inherit_raw(pdev, bp->bio_dev); 361 362 if (!dp) { 363 biofinish(bp, NULL, ENXIO); 364 return; 365 } 366 367 if (dscheck(bp, dp->d_slice) <= 0) { 368 biodone(bp); 369 return; 370 } 371 372 if (bp->bio_bcount == 0) { 373 biodone(bp); 374 return; 375 } 376 377 KASSERT(dp->d_devsw != NULL, ("NULL devsw")); 378 KASSERT(dp->d_devsw->d_strategy != NULL, ("NULL d_strategy")); 379 dp->d_devsw->d_strategy(bp); 380 return; 381 382} 383 384static int 385diskioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td) 386{ 387 struct disk *dp; 388 int error; 389 dev_t pdev; 390 391 pdev = dkmodpart(dkmodslice(dev, WHOLE_DISK_SLICE), RAW_PART); 392 dp = pdev->si_disk; 393 if (!dp) 394 return (ENXIO); 395 error = dsioctl(dev, cmd, data, fflag, &dp->d_slice); 396 if (error == ENOIOCTL) 397 error = dp->d_devsw->d_ioctl(dev, cmd, data, fflag, td); 398 return (error); 399} 400 401static int 402diskpsize(dev_t dev) 403{ 404 struct disk *dp; 405 dev_t pdev; 406 407 pdev = dkmodpart(dkmodslice(dev, WHOLE_DISK_SLICE), RAW_PART); 408 dp = pdev->si_disk; 409 if (!dp) 410 return (-1); 411 if (dp != dev->si_disk) { 412 dev->si_drv1 = pdev->si_drv1; 413 dev->si_drv2 = pdev->si_drv2; 414 /* XXX: don't set bp->b_dev->si_disk (?) */ 415 } 416 return (dssize(dev, &dp->d_slice)); 417} 418 419SYSCTL_DECL(_debug_sizeof); 420 421SYSCTL_INT(_debug_sizeof, OID_AUTO, disklabel, CTLFLAG_RD, 422 0, sizeof(struct disklabel), "sizeof(struct disklabel)"); 423 424SYSCTL_INT(_debug_sizeof, OID_AUTO, diskslices, CTLFLAG_RD, 425 0, sizeof(struct diskslices), "sizeof(struct diskslices)"); 426 427SYSCTL_INT(_debug_sizeof, OID_AUTO, disk, CTLFLAG_RD, 428 0, sizeof(struct disk), "sizeof(struct disk)"); 429