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: src/sys/dev/md/md.c,v 1.8.2.2 2002/08/19 17:43:34 jdp Exp $ 10 * 11 */ 12 13 #include "opt_md.h" /* We have adopted some tasks from MFS */ 14 15 #include <sys/param.h> 16 #include <sys/systm.h> 17 #include <sys/buf.h> 18 #include <sys/conf.h> 19 #include <sys/devicestat.h> 20 #include <sys/disk.h> 21 #include <sys/kernel.h> 22 #include <sys/malloc.h> 23 #include <sys/sysctl.h> 24 #include <sys/linker.h> 25 #include <sys/proc.h> 26 #include <sys/buf2.h> 27 #include <sys/thread2.h> 28 #include <sys/queue.h> 29 #include <sys/udev.h> 30 31 #ifndef MD_NSECT 32 #define MD_NSECT (10000 * 2) 33 #endif 34 35 MALLOC_DEFINE(M_MD, "MD disk", "Memory Disk"); 36 MALLOC_DEFINE(M_MDSECT, "MD sectors", "Memory Disk Sectors"); 37 38 static int md_debug; 39 SYSCTL_INT(_debug, OID_AUTO, mddebug, CTLFLAG_RW, &md_debug, 0, 40 "Enable debug output for memory disk devices"); 41 42 #if defined(MD_ROOT) && defined(MD_ROOT_SIZE) 43 /* Image gets put here: */ 44 static u_char mfs_root[MD_ROOT_SIZE*1024] = "MFS Filesystem goes here"; 45 static u_char end_mfs_root[] __unused = "MFS Filesystem had better STOP here"; 46 #endif 47 48 static int mdrootready; 49 50 static d_strategy_t mdstrategy; 51 static d_strategy_t mdstrategy_preload; 52 static d_strategy_t mdstrategy_malloc; 53 static d_open_t mdopen; 54 static d_close_t mdclose; 55 static d_ioctl_t mdioctl; 56 57 static struct dev_ops md_ops = { 58 { "md", 0, D_DISK | D_CANFREE | D_MEMDISK | D_TRACKCLOSE | D_MPSAFE}, 59 .d_open = mdopen, 60 .d_close = mdclose, 61 .d_read = physread, 62 .d_write = physwrite, 63 .d_ioctl = mdioctl, 64 .d_strategy = mdstrategy, 65 }; 66 67 struct md_s { 68 struct lwkt_token tok; 69 int unit; 70 struct devstat stats; 71 struct bio_queue_head bio_queue; 72 struct disk disk; 73 cdev_t dev; 74 int busy; 75 enum { /* Memory disk type */ 76 MD_MALLOC, 77 MD_PRELOAD 78 } type; 79 unsigned nsect; 80 81 /* MD_MALLOC related fields */ 82 unsigned nsecp; 83 u_char **secp; 84 85 /* MD_PRELOAD related fields */ 86 u_char *pl_ptr; 87 unsigned pl_len; 88 TAILQ_ENTRY(md_s) link; 89 }; 90 TAILQ_HEAD(mdshead, md_s) mdlist = TAILQ_HEAD_INITIALIZER(mdlist); 91 92 static int mdunits; 93 static int refcnt; 94 95 static struct md_s *mdcreate(unsigned); 96 static void mdcreate_malloc(void); 97 static int mdinit(module_t, int, void *); 98 static void md_drvinit(void *); 99 static int md_drvcleanup(void); 100 101 static int 102 mdinit(module_t mod, int cmd, void *arg) 103 { 104 int ret = 0; 105 106 switch(cmd) { 107 case MOD_LOAD: 108 TAILQ_INIT(&mdlist); 109 md_drvinit(NULL); 110 break; 111 case MOD_UNLOAD: 112 ret = md_drvcleanup(); 113 break; 114 default: 115 ret = EINVAL; 116 break; 117 } 118 119 return (ret); 120 } 121 122 static int 123 mdopen(struct dev_open_args *ap) 124 { 125 cdev_t dev = ap->a_head.a_dev; 126 struct md_s *sc; 127 128 if (md_debug) { 129 kprintf("mdopen(%s %x %x)\n", 130 devtoname(dev), ap->a_oflags, ap->a_devtype); 131 } 132 133 sc = dev->si_drv1; 134 lwkt_gettoken(&sc->tok); 135 if (sc->unit + 1 == mdunits) 136 mdcreate_malloc(); 137 atomic_add_int(&refcnt, 1); 138 lwkt_reltoken(&sc->tok); 139 140 return (0); 141 } 142 143 static int 144 mdclose(struct dev_close_args *ap) 145 { 146 cdev_t dev = ap->a_head.a_dev; 147 struct md_s *sc; 148 149 if (md_debug) { 150 kprintf("mdclose(%s %x %x)\n", 151 devtoname(dev), ap->a_fflag, ap->a_devtype); 152 } 153 sc = dev->si_drv1; 154 lwkt_gettoken(&sc->tok); 155 atomic_add_int(&refcnt, -1); 156 lwkt_reltoken(&sc->tok); 157 158 return (0); 159 } 160 161 static int 162 mdioctl(struct dev_ioctl_args *ap) 163 { 164 cdev_t dev = ap->a_head.a_dev; 165 166 if (md_debug) { 167 kprintf("mdioctl(%s %lx %p %x)\n", 168 devtoname(dev), ap->a_cmd, ap->a_data, ap->a_fflag); 169 } 170 171 return (ENOIOCTL); 172 } 173 174 static int 175 mdstrategy(struct dev_strategy_args *ap) 176 { 177 cdev_t dev = ap->a_head.a_dev; 178 struct bio *bio = ap->a_bio; 179 struct buf *bp = bio->bio_buf; 180 struct md_s *sc; 181 182 if (md_debug > 1) { 183 kprintf("mdstrategy(%p) %s %08x, %lld, %d, %p)\n", 184 bp, devtoname(dev), bp->b_flags, 185 (long long)bio->bio_offset, 186 bp->b_bcount, bp->b_data); 187 } 188 bio->bio_driver_info = dev; 189 sc = dev->si_drv1; 190 lwkt_gettoken(&sc->tok); 191 if (sc->type == MD_MALLOC) { 192 mdstrategy_malloc(ap); 193 } else { 194 mdstrategy_preload(ap); 195 } 196 lwkt_reltoken(&sc->tok); 197 return(0); 198 } 199 200 201 static int 202 mdstrategy_malloc(struct dev_strategy_args *ap) 203 { 204 cdev_t dev = ap->a_head.a_dev; 205 struct bio *bio = ap->a_bio; 206 struct buf *bp = bio->bio_buf; 207 unsigned secno, nsec, secval, uc; 208 u_char *secp, **secpp, *dst; 209 struct md_s *sc; 210 int i; 211 212 if (md_debug > 1) 213 kprintf("mdstrategy_malloc(%p) %s %08xx, %lld, %d, %p)\n", 214 bp, devtoname(dev), bp->b_flags, 215 (long long)bio->bio_offset, 216 bp->b_bcount, bp->b_data); 217 218 sc = dev->si_drv1; 219 220 crit_enter(); 221 222 bioqdisksort(&sc->bio_queue, bio); 223 224 if (sc->busy) { 225 crit_exit(); 226 return(0); 227 } 228 229 sc->busy++; 230 231 while (1) { 232 bio = bioq_first(&sc->bio_queue); 233 if (bio == NULL) { 234 crit_exit(); 235 break; 236 } 237 crit_exit(); 238 bioq_remove(&sc->bio_queue, bio); 239 bp = bio->bio_buf; 240 241 devstat_start_transaction(&sc->stats); 242 243 switch (bp->b_cmd) { 244 case BUF_CMD_FREEBLKS: 245 case BUF_CMD_READ: 246 case BUF_CMD_WRITE: 247 break; 248 default: 249 panic("md: bad b_cmd %d", bp->b_cmd); 250 } 251 252 nsec = bp->b_bcount >> DEV_BSHIFT; 253 secno = (unsigned)(bio->bio_offset >> DEV_BSHIFT); 254 dst = bp->b_data; 255 while (nsec--) { 256 if (secno < sc->nsecp) { 257 secpp = &sc->secp[secno]; 258 if ((u_int)(uintptr_t)*secpp > 255) { 259 secp = *secpp; 260 secval = 0; 261 } else { 262 secp = NULL; 263 secval = (u_int)(uintptr_t)*secpp; 264 } 265 } else { 266 secpp = NULL; 267 secp = NULL; 268 secval = 0; 269 } 270 if (md_debug > 2) 271 kprintf("%08x %p %p %d\n", bp->b_flags, secpp, secp, secval); 272 273 switch (bp->b_cmd) { 274 case BUF_CMD_FREEBLKS: 275 if (secpp) { 276 if (secp) 277 kfree(secp, M_MDSECT); 278 *secpp = NULL; 279 } 280 break; 281 case BUF_CMD_READ: 282 if (secp) { 283 bcopy(secp, dst, DEV_BSIZE); 284 } else if (secval) { 285 for (i = 0; i < DEV_BSIZE; i++) 286 dst[i] = secval; 287 } else { 288 bzero(dst, DEV_BSIZE); 289 } 290 break; 291 case BUF_CMD_WRITE: 292 uc = dst[0]; 293 for (i = 1; i < DEV_BSIZE; i++) 294 if (dst[i] != uc) 295 break; 296 if (i == DEV_BSIZE && !uc) { 297 if (secp) 298 kfree(secp, M_MDSECT); 299 if (secpp) 300 *secpp = (u_char *)(uintptr_t)uc; 301 } else { 302 if (!secpp) { 303 secpp = kmalloc((secno + nsec + 1) * sizeof(u_char *), 304 M_MD, 305 M_WAITOK | M_ZERO); 306 bcopy(sc->secp, secpp, sc->nsecp * sizeof(u_char *)); 307 kfree(sc->secp, M_MD); 308 sc->secp = secpp; 309 sc->nsecp = secno + nsec + 1; 310 secpp = &sc->secp[secno]; 311 } 312 if (i == DEV_BSIZE) { 313 if (secp) 314 kfree(secp, M_MDSECT); 315 *secpp = (u_char *)(uintptr_t)uc; 316 } else { 317 if (!secp) 318 secp = kmalloc(DEV_BSIZE, 319 M_MDSECT, 320 M_WAITOK); 321 bcopy(dst, secp, DEV_BSIZE); 322 323 *secpp = secp; 324 } 325 } 326 break; 327 default: 328 panic("md: bad b_cmd %d", bp->b_cmd); 329 330 } 331 secno++; 332 dst += DEV_BSIZE; 333 } 334 bp->b_resid = 0; 335 devstat_end_transaction_buf(&sc->stats, bp); 336 biodone(bio); 337 crit_enter(); 338 } 339 sc->busy = 0; 340 return(0); 341 } 342 343 344 static int 345 mdstrategy_preload(struct dev_strategy_args *ap) 346 { 347 cdev_t dev = ap->a_head.a_dev; 348 struct bio *bio = ap->a_bio; 349 struct buf *bp = bio->bio_buf; 350 struct md_s *sc; 351 352 if (md_debug > 1) 353 kprintf("mdstrategy_preload(%p) %s %08x, %lld, %d, %p)\n", 354 bp, devtoname(dev), bp->b_flags, 355 (long long)bio->bio_offset, 356 bp->b_bcount, bp->b_data); 357 358 sc = dev->si_drv1; 359 360 crit_enter(); 361 362 bioqdisksort(&sc->bio_queue, bio); 363 364 if (sc->busy) { 365 crit_exit(); 366 return(0); 367 } 368 369 sc->busy++; 370 371 while (1) { 372 bio = bioq_takefirst(&sc->bio_queue); 373 crit_exit(); 374 if (bio == NULL) 375 break; 376 377 devstat_start_transaction(&sc->stats); 378 379 switch (bp->b_cmd) { 380 case BUF_CMD_FREEBLKS: 381 break; 382 case BUF_CMD_READ: 383 bcopy(sc->pl_ptr + bio->bio_offset, 384 bp->b_data, bp->b_bcount); 385 break; 386 case BUF_CMD_WRITE: 387 bcopy(bp->b_data, sc->pl_ptr + bio->bio_offset, 388 bp->b_bcount); 389 break; 390 default: 391 panic("md: bad cmd %d", bp->b_cmd); 392 } 393 bp->b_resid = 0; 394 devstat_end_transaction_buf(&sc->stats, bp); 395 biodone(bio); 396 crit_enter(); 397 } 398 sc->busy = 0; 399 return(0); 400 } 401 402 static struct md_s * 403 mdcreate(unsigned length) 404 { 405 struct md_s *sc; 406 struct disk_info info; 407 408 sc = kmalloc(sizeof(*sc), M_MD, M_WAITOK | M_ZERO); 409 lwkt_token_init(&sc->tok, "md"); 410 sc->unit = mdunits++; 411 bioq_init(&sc->bio_queue); 412 devstat_add_entry(&sc->stats, "md", sc->unit, DEV_BSIZE, 413 DEVSTAT_NO_ORDERED_TAGS, 414 DEVSTAT_TYPE_DIRECT | DEVSTAT_TYPE_IF_OTHER, 415 DEVSTAT_PRIORITY_OTHER); 416 sc->dev = disk_create(sc->unit, &sc->disk, &md_ops); 417 sc->dev->si_drv1 = sc; 418 sc->dev->si_iosize_max = MAXPHYS; 419 disk_setdisktype(&sc->disk, "memory"); 420 421 bzero(&info, sizeof(info)); 422 info.d_media_blksize = DEV_BSIZE; /* mandatory */ 423 info.d_media_blocks = length / DEV_BSIZE; 424 425 info.d_secpertrack = 1024; /* optional */ 426 info.d_nheads = 1; 427 info.d_secpercyl = info.d_secpertrack * info.d_nheads; 428 info.d_ncylinders = (u_int)(info.d_media_blocks / info.d_secpercyl); 429 disk_setdiskinfo(&sc->disk, &info); 430 TAILQ_INSERT_HEAD(&mdlist, sc, link); 431 432 return (sc); 433 } 434 435 436 static void 437 mdcreate_preload(u_char *image, unsigned length) 438 { 439 struct md_s *sc; 440 441 sc = mdcreate(length); 442 sc->type = MD_PRELOAD; 443 sc->nsect = length / DEV_BSIZE; 444 sc->pl_ptr = image; 445 sc->pl_len = length; 446 447 if (sc->unit == 0) 448 mdrootready = 1; 449 } 450 451 static void 452 mdcreate_malloc(void) 453 { 454 struct md_s *sc; 455 456 sc = mdcreate(MD_NSECT*DEV_BSIZE); 457 sc->type = MD_MALLOC; 458 459 sc->nsect = MD_NSECT; /* for now */ 460 sc->secp = kmalloc(sizeof(u_char *), M_MD, M_WAITOK | M_ZERO); 461 sc->nsecp = 1; 462 kprintf("md%d: Malloc disk\n", sc->unit); 463 } 464 465 static int 466 md_drvcleanup(void) 467 { 468 469 int secno; 470 struct md_s *sc, *sc_temp; 471 472 if (atomic_fetchadd_int(&refcnt, 0) != 0) 473 return EBUSY; 474 475 /* 476 * Go through all the md devices, freeing up all the 477 * memory allocated for sectors, and the md_s struct 478 * itself. 479 */ 480 TAILQ_FOREACH_MUTABLE(sc, &mdlist, link, sc_temp) { 481 for (secno = 0; secno < sc->nsecp; secno++) { 482 if ((u_int)(uintptr_t)sc->secp[secno] > 255) 483 kfree(sc->secp[secno], M_MDSECT); 484 } 485 486 if (sc->dev != NULL) 487 disk_destroy(&sc->disk); 488 489 devstat_remove_entry(&sc->stats); 490 TAILQ_REMOVE(&mdlist, sc, link); 491 492 kfree(sc->secp, M_MD); 493 kfree(sc, M_MD); 494 } 495 496 return 0; 497 498 } 499 500 static void 501 md_drvinit(void *unused) 502 { 503 504 caddr_t mod; 505 caddr_t c; 506 u_char *ptr, *name, *type; 507 unsigned len; 508 509 #ifdef MD_ROOT_SIZE 510 mdcreate_preload(mfs_root, MD_ROOT_SIZE*1024); 511 #endif 512 mod = NULL; 513 while ((mod = preload_search_next_name(mod)) != NULL) { 514 name = (char *)preload_search_info(mod, MODINFO_NAME); 515 type = (char *)preload_search_info(mod, MODINFO_TYPE); 516 if (name == NULL) 517 continue; 518 if (type == NULL) 519 continue; 520 if (strcmp(type, "md_image") && strcmp(type, "mfs_root")) 521 continue; 522 c = preload_search_info(mod, MODINFO_ADDR); 523 ptr = *(u_char **)c; 524 c = preload_search_info(mod, MODINFO_SIZE); 525 len = *(unsigned *)c; 526 kprintf("md%d: Preloaded image <%s> %d bytes at %p\n", 527 mdunits, name, len, ptr); 528 mdcreate_preload(ptr, len); 529 } 530 mdcreate_malloc(); 531 } 532 533 DEV_MODULE(md, mdinit, NULL); 534 535 #ifdef MD_ROOT 536 static void 537 md_takeroot(void *junk) 538 { 539 if (mdrootready) 540 rootdevnames[0] = "ufs:/dev/md0s0"; 541 } 542 543 SYSINIT(md_root, SI_SUB_MOUNT_ROOT, SI_ORDER_FIRST, md_takeroot, NULL); 544 #endif 545