| Commit | Line | Data |
|---|---|---|
| 984263bc MD |
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 $ | |
| 8b6f36f5 | 10 | * $DragonFly: src/sys/dev/disk/md/md.c,v 1.20 2008/09/07 08:09:39 swildner Exp $ |
| 984263bc MD |
11 | * |
| 12 | */ | |
| 13 | ||
| 984263bc MD |
14 | #include "opt_md.h" /* We have adopted some tasks from MFS */ |
| 15 | ||
| 16 | #include <sys/param.h> | |
| 17 | #include <sys/systm.h> | |
| 18 | #include <sys/buf.h> | |
| 19 | #include <sys/conf.h> | |
| 20 | #include <sys/devicestat.h> | |
| 21 | #include <sys/disk.h> | |
| 22 | #include <sys/kernel.h> | |
| 23 | #include <sys/malloc.h> | |
| 24 | #include <sys/sysctl.h> | |
| 25 | #include <sys/linker.h> | |
| 3020e3be MD |
26 | #include <sys/proc.h> |
| 27 | #include <sys/buf2.h> | |
| fbe276c7 | 28 | #include <sys/thread2.h> |
| 984263bc MD |
29 | |
| 30 | #ifndef MD_NSECT | |
| 31 | #define MD_NSECT (10000 * 2) | |
| 32 | #endif | |
| 33 | ||
| 34 | MALLOC_DEFINE(M_MD, "MD disk", "Memory Disk"); | |
| 35 | MALLOC_DEFINE(M_MDSECT, "MD sectors", "Memory Disk Sectors"); | |
| 36 | ||
| 37 | static int md_debug; | |
| 38 | SYSCTL_INT(_debug, OID_AUTO, mddebug, CTLFLAG_RW, &md_debug, 0, ""); | |
| 39 | ||
| 984263bc MD |
40 | #if defined(MD_ROOT) && defined(MD_ROOT_SIZE) |
| 41 | /* Image gets put here: */ | |
| 42 | static u_char mfs_root[MD_ROOT_SIZE*1024] = "MFS Filesystem goes here"; | |
| 43 | static u_char end_mfs_root[] __unused = "MFS Filesystem had better STOP here"; | |
| 44 | #endif | |
| 45 | ||
| 46 | static int mdrootready; | |
| 47 | ||
| 48 | static void mdcreate_malloc(void); | |
| 49 | ||
| 50 | #define CDEV_MAJOR 95 | |
| 984263bc MD |
51 | |
| 52 | static d_strategy_t mdstrategy; | |
| 53 | static d_strategy_t mdstrategy_preload; | |
| 54 | static d_strategy_t mdstrategy_malloc; | |
| 55 | static d_open_t mdopen; | |
| 56 | static d_ioctl_t mdioctl; | |
| 57 | ||
| fef8985e MD |
58 | static struct dev_ops md_ops = { |
| 59 | { "md", CDEV_MAJOR, D_DISK | D_CANFREE | D_MEMDISK }, | |
| 60 | .d_open = mdopen, | |
| 61 | .d_close = nullclose, | |
| 62 | .d_read = physread, | |
| 63 | .d_write = physwrite, | |
| 64 | .d_ioctl = mdioctl, | |
| 65 | .d_strategy = mdstrategy, | |
| 984263bc MD |
66 | }; |
| 67 | ||
| 984263bc MD |
68 | struct md_s { |
| 69 | int unit; | |
| 70 | struct devstat stats; | |
| 81b5c339 | 71 | struct bio_queue_head bio_queue; |
| 984263bc | 72 | struct disk disk; |
| b13267a5 | 73 | cdev_t dev; |
| 984263bc MD |
74 | int busy; |
| 75 | enum {MD_MALLOC, MD_PRELOAD} type; | |
| 76 | unsigned nsect; | |
| 77 | ||
| 78 | /* MD_MALLOC related fields */ | |
| 79 | unsigned nsecp; | |
| 80 | u_char **secp; | |
| 81 | ||
| 82 | /* MD_PRELOAD related fields */ | |
| 83 | u_char *pl_ptr; | |
| 84 | unsigned pl_len; | |
| 85 | }; | |
| 86 | ||
| 87 | static int mdunits; | |
| 88 | ||
| 89 | static int | |
| fef8985e | 90 | mdopen(struct dev_open_args *ap) |
| 984263bc | 91 | { |
| b13267a5 | 92 | cdev_t dev = ap->a_head.a_dev; |
| 984263bc | 93 | struct md_s *sc; |
| ec92fd9a | 94 | #if 0 |
| a688b15c | 95 | struct disk_info info; |
| ec92fd9a | 96 | #endif |
| 984263bc MD |
97 | |
| 98 | if (md_debug) | |
| e3869ec7 | 99 | kprintf("mdopen(%s %x %x)\n", |
| fef8985e | 100 | devtoname(dev), ap->a_oflags, ap->a_devtype); |
| 984263bc MD |
101 | |
| 102 | sc = dev->si_drv1; | |
| 103 | if (sc->unit + 1 == mdunits) | |
| 104 | mdcreate_malloc(); | |
| ec92fd9a | 105 | #if 0 |
| a688b15c MD |
106 | bzero(&info, sizeof(info)); |
| 107 | info.d_media_blksize = DEV_BSIZE; /* mandatory */ | |
| 108 | info.d_media_blocks = sc->nsect; | |
| 109 | ||
| 110 | info.d_secpertrack = 1024; /* optional */ | |
| 111 | info.d_nheads = 1; | |
| 112 | info.d_secpercyl = info.d_secpertrack * info.d_nheads; | |
| 113 | info.d_ncylinders = (u_int)(info.d_media_blocks / info.d_secpercyl); | |
| 114 | disk_setdiskinfo(&sc->disk, &info); | |
| ec92fd9a | 115 | #endif |
| 984263bc MD |
116 | return (0); |
| 117 | } | |
| 118 | ||
| 119 | static int | |
| fef8985e | 120 | mdioctl(struct dev_ioctl_args *ap) |
| 984263bc | 121 | { |
| b13267a5 | 122 | cdev_t dev = ap->a_head.a_dev; |
| 984263bc MD |
123 | |
| 124 | if (md_debug) | |
| e3869ec7 | 125 | kprintf("mdioctl(%s %lx %p %x)\n", |
| fef8985e | 126 | devtoname(dev), ap->a_cmd, ap->a_data, ap->a_fflag); |
| 984263bc MD |
127 | |
| 128 | return (ENOIOCTL); | |
| 129 | } | |
| 130 | ||
| fef8985e MD |
131 | static int |
| 132 | mdstrategy(struct dev_strategy_args *ap) | |
| 984263bc | 133 | { |
| b13267a5 | 134 | cdev_t dev = ap->a_head.a_dev; |
| fef8985e | 135 | struct bio *bio = ap->a_bio; |
| 81b5c339 | 136 | struct buf *bp = bio->bio_buf; |
| 984263bc MD |
137 | struct md_s *sc; |
| 138 | ||
| 81b5c339 | 139 | if (md_debug > 1) { |
| e3869ec7 | 140 | kprintf("mdstrategy(%p) %s %08x, %lld, %d, %p)\n", |
| 973c11b9 MD |
141 | bp, devtoname(dev), bp->b_flags, |
| 142 | (long long)bio->bio_offset, | |
| 54078292 | 143 | bp->b_bcount, bp->b_data); |
| 81b5c339 MD |
144 | } |
| 145 | bio->bio_driver_info = dev; | |
| 146 | sc = dev->si_drv1; | |
| 984263bc | 147 | if (sc->type == MD_MALLOC) { |
| fef8985e | 148 | mdstrategy_malloc(ap); |
| 984263bc | 149 | } else { |
| fef8985e | 150 | mdstrategy_preload(ap); |
| 984263bc | 151 | } |
| fef8985e | 152 | return(0); |
| 984263bc MD |
153 | } |
| 154 | ||
| 155 | ||
| fef8985e MD |
156 | static int |
| 157 | mdstrategy_malloc(struct dev_strategy_args *ap) | |
| 984263bc | 158 | { |
| b13267a5 | 159 | cdev_t dev = ap->a_head.a_dev; |
| fef8985e | 160 | struct bio *bio = ap->a_bio; |
| 81b5c339 | 161 | struct buf *bp = bio->bio_buf; |
| 984263bc | 162 | unsigned secno, nsec, secval, uc; |
| 81b5c339 | 163 | u_char *secp, **secpp, *dst; |
| 81b5c339 MD |
164 | struct md_s *sc; |
| 165 | int i; | |
| 984263bc MD |
166 | |
| 167 | if (md_debug > 1) | |
| e3869ec7 | 168 | kprintf("mdstrategy_malloc(%p) %s %08xx, %lld, %d, %p)\n", |
| 973c11b9 MD |
169 | bp, devtoname(dev), bp->b_flags, |
| 170 | (long long)bio->bio_offset, | |
| 54078292 | 171 | bp->b_bcount, bp->b_data); |
| 984263bc | 172 | |
| 81b5c339 | 173 | sc = dev->si_drv1; |
| 984263bc | 174 | |
| fbe276c7 | 175 | crit_enter(); |
| 984263bc | 176 | |
| 81b5c339 | 177 | bioqdisksort(&sc->bio_queue, bio); |
| 984263bc MD |
178 | |
| 179 | if (sc->busy) { | |
| fbe276c7 | 180 | crit_exit(); |
| fef8985e | 181 | return(0); |
| 984263bc MD |
182 | } |
| 183 | ||
| 184 | sc->busy++; | |
| 185 | ||
| 186 | while (1) { | |
| 81b5c339 | 187 | bio = bioq_first(&sc->bio_queue); |
| 31a163d2 MD |
188 | if (bio == NULL) { |
| 189 | crit_exit(); | |
| 984263bc | 190 | break; |
| 31a163d2 MD |
191 | } |
| 192 | crit_exit(); | |
| 193 | bioq_remove(&sc->bio_queue, bio); | |
| 194 | bp = bio->bio_buf; | |
| 984263bc MD |
195 | |
| 196 | devstat_start_transaction(&sc->stats); | |
| 197 | ||
| 8b6f36f5 | 198 | switch (bp->b_cmd) { |
| 10f3fee5 | 199 | case BUF_CMD_FREEBLKS: |
| 10f3fee5 | 200 | case BUF_CMD_READ: |
| 10f3fee5 | 201 | case BUF_CMD_WRITE: |
| 10f3fee5 MD |
202 | break; |
| 203 | default: | |
| 204 | panic("md: bad b_cmd %d", bp->b_cmd); | |
| 205 | } | |
| 984263bc | 206 | |
| 54078292 MD |
207 | nsec = bp->b_bcount >> DEV_BSHIFT; |
| 208 | secno = (unsigned)(bio->bio_offset >> DEV_BSHIFT); | |
| 984263bc MD |
209 | dst = bp->b_data; |
| 210 | while (nsec--) { | |
| 984263bc MD |
211 | if (secno < sc->nsecp) { |
| 212 | secpp = &sc->secp[secno]; | |
| 973c11b9 | 213 | if ((u_int)(uintptr_t)*secpp > 255) { |
| 984263bc MD |
214 | secp = *secpp; |
| 215 | secval = 0; | |
| 216 | } else { | |
| 217 | secp = 0; | |
| 973c11b9 | 218 | secval = (u_int)(uintptr_t)*secpp; |
| 984263bc MD |
219 | } |
| 220 | } else { | |
| 221 | secpp = 0; | |
| 222 | secp = 0; | |
| 223 | secval = 0; | |
| 224 | } | |
| 225 | if (md_debug > 2) | |
| e3869ec7 | 226 | kprintf("%08x %p %p %d\n", bp->b_flags, secpp, secp, secval); |
| 984263bc | 227 | |
| 8b6f36f5 | 228 | switch (bp->b_cmd) { |
| 10f3fee5 | 229 | case BUF_CMD_FREEBLKS: |
| 984263bc MD |
230 | if (secpp) { |
| 231 | if (secp) | |
| 232 | FREE(secp, M_MDSECT); | |
| 233 | *secpp = 0; | |
| 234 | } | |
| 10f3fee5 MD |
235 | break; |
| 236 | case BUF_CMD_READ: | |
| 984263bc MD |
237 | if (secp) { |
| 238 | bcopy(secp, dst, DEV_BSIZE); | |
| 239 | } else if (secval) { | |
| 240 | for (i = 0; i < DEV_BSIZE; i++) | |
| 241 | dst[i] = secval; | |
| 242 | } else { | |
| 243 | bzero(dst, DEV_BSIZE); | |
| 244 | } | |
| 10f3fee5 MD |
245 | break; |
| 246 | case BUF_CMD_WRITE: | |
| 984263bc MD |
247 | uc = dst[0]; |
| 248 | for (i = 1; i < DEV_BSIZE; i++) | |
| 249 | if (dst[i] != uc) | |
| 250 | break; | |
| 251 | if (i == DEV_BSIZE && !uc) { | |
| 252 | if (secp) | |
| 253 | FREE(secp, M_MDSECT); | |
| 254 | if (secpp) | |
| 973c11b9 | 255 | *secpp = (u_char *)(uintptr_t)uc; |
| 984263bc MD |
256 | } else { |
| 257 | if (!secpp) { | |
| e7b4468c | 258 | MALLOC(secpp, u_char **, (secno + nsec + 1) * sizeof(u_char *), M_MD, M_WAITOK | M_ZERO); |
| 984263bc MD |
259 | bcopy(sc->secp, secpp, sc->nsecp * sizeof(u_char *)); |
| 260 | FREE(sc->secp, M_MD); | |
| 261 | sc->secp = secpp; | |
| 262 | sc->nsecp = secno + nsec + 1; | |
| 263 | secpp = &sc->secp[secno]; | |
| 264 | } | |
| 265 | if (i == DEV_BSIZE) { | |
| 266 | if (secp) | |
| 267 | FREE(secp, M_MDSECT); | |
| 973c11b9 | 268 | *secpp = (u_char *)(uintptr_t)uc; |
| 984263bc MD |
269 | } else { |
| 270 | if (!secp) | |
| 271 | MALLOC(secp, u_char *, DEV_BSIZE, M_MDSECT, M_WAITOK); | |
| 272 | bcopy(dst, secp, DEV_BSIZE); | |
| 273 | ||
| 274 | *secpp = secp; | |
| 275 | } | |
| 276 | } | |
| 10f3fee5 MD |
277 | break; |
| 278 | default: | |
| 279 | panic("md: bad b_cmd %d", bp->b_cmd); | |
| 280 | ||
| 984263bc MD |
281 | } |
| 282 | secno++; | |
| 283 | dst += DEV_BSIZE; | |
| 284 | } | |
| 285 | bp->b_resid = 0; | |
| 286 | devstat_end_transaction_buf(&sc->stats, bp); | |
| 81b5c339 | 287 | biodone(bio); |
| fbe276c7 | 288 | crit_enter(); |
| 984263bc MD |
289 | } |
| 290 | sc->busy = 0; | |
| fef8985e | 291 | return(0); |
| 984263bc MD |
292 | } |
| 293 | ||
| 294 | ||
| fef8985e MD |
295 | static int |
| 296 | mdstrategy_preload(struct dev_strategy_args *ap) | |
| 984263bc | 297 | { |
| b13267a5 | 298 | cdev_t dev = ap->a_head.a_dev; |
| fef8985e | 299 | struct bio *bio = ap->a_bio; |
| 81b5c339 | 300 | struct buf *bp = bio->bio_buf; |
| 81b5c339 | 301 | struct md_s *sc; |
| 984263bc MD |
302 | |
| 303 | if (md_debug > 1) | |
| e3869ec7 | 304 | kprintf("mdstrategy_preload(%p) %s %08x, %lld, %d, %p)\n", |
| 973c11b9 MD |
305 | bp, devtoname(dev), bp->b_flags, |
| 306 | (long long)bio->bio_offset, | |
| 54078292 | 307 | bp->b_bcount, bp->b_data); |
| 984263bc | 308 | |
| 81b5c339 | 309 | sc = dev->si_drv1; |
| 984263bc | 310 | |
| fbe276c7 | 311 | crit_enter(); |
| 984263bc | 312 | |
| 81b5c339 | 313 | bioqdisksort(&sc->bio_queue, bio); |
| 984263bc MD |
314 | |
| 315 | if (sc->busy) { | |
| fbe276c7 | 316 | crit_exit(); |
| fef8985e | 317 | return(0); |
| 984263bc MD |
318 | } |
| 319 | ||
| 320 | sc->busy++; | |
| 321 | ||
| 322 | while (1) { | |
| 81b5c339 MD |
323 | bio = bioq_first(&sc->bio_queue); |
| 324 | if (bio) | |
| 325 | bioq_remove(&sc->bio_queue, bio); | |
| fbe276c7 | 326 | crit_exit(); |
| 81b5c339 | 327 | if (bio == NULL) |
| 984263bc MD |
328 | break; |
| 329 | ||
| 330 | devstat_start_transaction(&sc->stats); | |
| 331 | ||
| 8b6f36f5 | 332 | switch (bp->b_cmd) { |
| 10f3fee5 | 333 | case BUF_CMD_FREEBLKS: |
| 10f3fee5 MD |
334 | break; |
| 335 | case BUF_CMD_READ: | |
| 54078292 MD |
336 | bcopy(sc->pl_ptr + bio->bio_offset, |
| 337 | bp->b_data, bp->b_bcount); | |
| 10f3fee5 MD |
338 | break; |
| 339 | case BUF_CMD_WRITE: | |
| 54078292 MD |
340 | bcopy(bp->b_data, sc->pl_ptr + bio->bio_offset, |
| 341 | bp->b_bcount); | |
| 10f3fee5 MD |
342 | break; |
| 343 | default: | |
| 344 | panic("md: bad cmd %d\n", bp->b_cmd); | |
| 984263bc MD |
345 | } |
| 346 | bp->b_resid = 0; | |
| 347 | devstat_end_transaction_buf(&sc->stats, bp); | |
| 81b5c339 | 348 | biodone(bio); |
| fbe276c7 | 349 | crit_enter(); |
| 984263bc MD |
350 | } |
| 351 | sc->busy = 0; | |
| fef8985e | 352 | return(0); |
| 984263bc MD |
353 | } |
| 354 | ||
| 355 | static struct md_s * | |
| ec92fd9a | 356 | mdcreate(unsigned length) |
| 984263bc MD |
357 | { |
| 358 | struct md_s *sc; | |
| cd29885a | 359 | struct disk_info info; |
| 984263bc | 360 | |
| e7b4468c | 361 | MALLOC(sc, struct md_s *,sizeof(*sc), M_MD, M_WAITOK | M_ZERO); |
| 984263bc | 362 | sc->unit = mdunits++; |
| 81b5c339 | 363 | bioq_init(&sc->bio_queue); |
| 984263bc MD |
364 | devstat_add_entry(&sc->stats, "md", sc->unit, DEV_BSIZE, |
| 365 | DEVSTAT_NO_ORDERED_TAGS, | |
| 366 | DEVSTAT_TYPE_DIRECT | DEVSTAT_TYPE_IF_OTHER, | |
| 367 | DEVSTAT_PRIORITY_OTHER); | |
| a688b15c | 368 | sc->dev = disk_create(sc->unit, &sc->disk, &md_ops); |
| 984263bc | 369 | sc->dev->si_drv1 = sc; |
| a260c869 MD |
370 | sc->dev->si_iosize_max = DFLTPHYS; |
| 371 | ||
| cd29885a MD |
372 | bzero(&info, sizeof(info)); |
| 373 | info.d_media_blksize = DEV_BSIZE; /* mandatory */ | |
| ec92fd9a | 374 | info.d_media_blocks = length / DEV_BSIZE; |
| cd29885a MD |
375 | |
| 376 | info.d_secpertrack = 1024; /* optional */ | |
| 377 | info.d_nheads = 1; | |
| 378 | info.d_secpercyl = info.d_secpertrack * info.d_nheads; | |
| 379 | info.d_ncylinders = (u_int)(info.d_media_blocks / info.d_secpercyl); | |
| 380 | disk_setdiskinfo(&sc->disk, &info); | |
| 381 | ||
| 984263bc MD |
382 | return (sc); |
| 383 | } | |
| 384 | ||
| 385 | static void | |
| 386 | mdcreate_preload(u_char *image, unsigned length) | |
| 387 | { | |
| 388 | struct md_s *sc; | |
| 389 | ||
| ec92fd9a | 390 | sc = mdcreate(length); |
| 984263bc MD |
391 | sc->type = MD_PRELOAD; |
| 392 | sc->nsect = length / DEV_BSIZE; | |
| 393 | sc->pl_ptr = image; | |
| 394 | sc->pl_len = length; | |
| 395 | ||
| 396 | if (sc->unit == 0) | |
| 397 | mdrootready = 1; | |
| 398 | } | |
| 399 | ||
| 400 | static void | |
| 401 | mdcreate_malloc(void) | |
| 402 | { | |
| 403 | struct md_s *sc; | |
| 404 | ||
| ec92fd9a | 405 | sc = mdcreate(0); |
| 984263bc MD |
406 | sc->type = MD_MALLOC; |
| 407 | ||
| 408 | sc->nsect = MD_NSECT; /* for now */ | |
| e7b4468c | 409 | MALLOC(sc->secp, u_char **, sizeof(u_char *), M_MD, M_WAITOK | M_ZERO); |
| 984263bc | 410 | sc->nsecp = 1; |
| e3869ec7 | 411 | kprintf("md%d: Malloc disk\n", sc->unit); |
| 984263bc MD |
412 | } |
| 413 | ||
| 414 | static void | |
| 415 | md_drvinit(void *unused) | |
| 416 | { | |
| 417 | ||
| 418 | caddr_t mod; | |
| 419 | caddr_t c; | |
| 420 | u_char *ptr, *name, *type; | |
| 421 | unsigned len; | |
| 422 | ||
| 423 | #ifdef MD_ROOT_SIZE | |
| 424 | mdcreate_preload(mfs_root, MD_ROOT_SIZE*1024); | |
| 425 | #endif | |
| 426 | mod = NULL; | |
| 427 | while ((mod = preload_search_next_name(mod)) != NULL) { | |
| 428 | name = (char *)preload_search_info(mod, MODINFO_NAME); | |
| 429 | type = (char *)preload_search_info(mod, MODINFO_TYPE); | |
| 430 | if (name == NULL) | |
| 431 | continue; | |
| 432 | if (type == NULL) | |
| 433 | continue; | |
| 434 | if (strcmp(type, "md_image") && strcmp(type, "mfs_root")) | |
| 435 | continue; | |
| 436 | c = preload_search_info(mod, MODINFO_ADDR); | |
| 437 | ptr = *(u_char **)c; | |
| 438 | c = preload_search_info(mod, MODINFO_SIZE); | |
| 439 | len = *(unsigned *)c; | |
| e3869ec7 | 440 | kprintf("md%d: Preloaded image <%s> %d bytes at %p\n", |
| 984263bc MD |
441 | mdunits, name, len, ptr); |
| 442 | mdcreate_preload(ptr, len); | |
| 443 | } | |
| 444 | mdcreate_malloc(); | |
| 445 | } | |
| 446 | ||
| 447 | SYSINIT(mddev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR, md_drvinit,NULL) | |
| 448 | ||
| 449 | #ifdef MD_ROOT | |
| 450 | static void | |
| 451 | md_takeroot(void *junk) | |
| 452 | { | |
| 453 | if (mdrootready) | |
| ec92fd9a | 454 | rootdevnames[0] = "ufs:/dev/md0s0"; |
| 984263bc MD |
455 | } |
| 456 | ||
| 457 | SYSINIT(md_root, SI_SUB_MOUNT_ROOT, SI_ORDER_FIRST, md_takeroot, NULL); | |
| 458 | #endif |