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