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