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