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