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/kern/subr_disk.c,v 1.20.2.6 2001/10/05 07:14:57 peter Exp $ 10 * $DragonFly: src/sys/kern/subr_disk.c,v 1.5 2003/07/22 17:03:33 dillon Exp $ 11 * 12 */ 13 14 #include <sys/param.h> 15 #include <sys/systm.h> 16 #include <sys/kernel.h> 17 #include <sys/sysctl.h> 18 #include <sys/buf.h> 19 #include <sys/conf.h> 20 #include <sys/disk.h> 21 #include <sys/malloc.h> 22 #include <sys/sysctl.h> 23 #include <machine/md_var.h> 24 #include <sys/ctype.h> 25 #include <sys/msgport.h> 26 #include <sys/msgport2.h> 27 28 static MALLOC_DEFINE(M_DISK, "disk", "disk data"); 29 30 static d_strategy_t diskstrategy; 31 static d_open_t diskopen; 32 static d_close_t diskclose; 33 static d_ioctl_t diskioctl; 34 static d_psize_t diskpsize; 35 static int disk_putport(lwkt_port_t port, lwkt_msg_t msg); 36 37 static LIST_HEAD(, disk) disklist = LIST_HEAD_INITIALIZER(&disklist); 38 39 static void 40 inherit_raw(dev_t pdev, dev_t dev) 41 { 42 dev->si_disk = pdev->si_disk; 43 dev->si_drv1 = pdev->si_drv1; 44 dev->si_drv2 = pdev->si_drv2; 45 dev->si_iosize_max = pdev->si_iosize_max; 46 dev->si_bsize_phys = pdev->si_bsize_phys; 47 dev->si_bsize_best = pdev->si_bsize_best; 48 } 49 50 /* 51 * Create a slice and unit managed disk. The underlying raw disk device 52 * is specified by cdevsw. We create the device as a managed device by 53 * first creating it normally then overriding the message port with our 54 * own frontend (which will be responsible for assigning pblkno). 55 */ 56 dev_t 57 disk_create(int unit, struct disk *dp, int flags, struct cdevsw *cdevsw) 58 { 59 dev_t dev; 60 61 bzero(dp, sizeof(*dp)); 62 lwkt_init_port(&dp->d_port, NULL); /* intercept port */ 63 dp->d_port.mp_beginmsg = disk_putport; 64 65 dev = makedev(cdevsw->d_maj, 0); /* base device */ 66 dev->si_disk = dp; 67 /* forwarding port */ 68 dp->d_fwdport = cdevsw_add_override(cdevsw, &dp->d_port); 69 70 if (bootverbose) 71 printf("Creating DISK %s%d\n", cdevsw->d_name, unit); 72 73 /* 74 * The whole disk placemarker holds the disk structure. 75 */ 76 dev = make_dev(cdevsw, dkmakeminor(unit, WHOLE_DISK_SLICE, RAW_PART), 77 UID_ROOT, GID_OPERATOR, 0640, "%s%d", cdevsw->d_name, unit); 78 dev->si_disk = dp; 79 dp->d_dev = dev; 80 dp->d_dsflags = flags; 81 LIST_INSERT_HEAD(&disklist, dp, d_list); 82 return (dev); 83 } 84 85 void 86 disk_destroy(struct disk *disk) 87 { 88 dev_t dev = disk->d_dev; 89 90 LIST_REMOVE(disk, d_list); 91 bzero(disk, sizeof(*disk)); 92 dev->si_disk = NULL; 93 destroy_dev(dev); 94 /* YYY remove cdevsw entries? */ 95 return; 96 } 97 98 int 99 disk_dumpcheck(dev_t dev, u_int *count, u_int *blkno, u_int *secsize) 100 { 101 struct disk *dp; 102 struct disklabel *dl; 103 u_int boff; 104 105 dp = dev->si_disk; 106 if (!dp) 107 return (ENXIO); 108 if (!dp->d_slice) 109 return (ENXIO); 110 dl = dsgetlabel(dev, dp->d_slice); 111 if (!dl) 112 return (ENXIO); 113 *count = Maxmem * (PAGE_SIZE / dl->d_secsize); 114 if (dumplo <= LABELSECTOR || 115 (dumplo + *count > dl->d_partitions[dkpart(dev)].p_size)) 116 return (EINVAL); 117 boff = dl->d_partitions[dkpart(dev)].p_offset + 118 dp->d_slice->dss_slices[dkslice(dev)].ds_offset; 119 *blkno = boff + dumplo; 120 *secsize = dl->d_secsize; 121 return (0); 122 123 } 124 125 void 126 disk_invalidate (struct disk *disk) 127 { 128 if (disk->d_slice) 129 dsgone(&disk->d_slice); 130 } 131 132 struct disk * 133 disk_enumerate(struct disk *disk) 134 { 135 if (!disk) 136 return (LIST_FIRST(&disklist)); 137 else 138 return (LIST_NEXT(disk, d_list)); 139 } 140 141 static int 142 sysctl_disks(SYSCTL_HANDLER_ARGS) 143 { 144 struct disk *disk; 145 int error, first; 146 147 disk = NULL; 148 first = 1; 149 150 while ((disk = disk_enumerate(disk))) { 151 if (!first) { 152 error = SYSCTL_OUT(req, " ", 1); 153 if (error) 154 return error; 155 } else { 156 first = 0; 157 } 158 error = SYSCTL_OUT(req, disk->d_dev->si_name, strlen(disk->d_dev->si_name)); 159 if (error) 160 return error; 161 } 162 error = SYSCTL_OUT(req, "", 1); 163 return error; 164 } 165 166 SYSCTL_PROC(_kern, OID_AUTO, disks, CTLTYPE_STRING | CTLFLAG_RD, 0, NULL, 167 sysctl_disks, "A", "names of available disks"); 168 169 /* 170 * The port intercept functions 171 */ 172 static 173 int 174 disk_putport(lwkt_port_t port, lwkt_msg_t lmsg) 175 { 176 struct disk *disk = (struct disk *)port; 177 cdevallmsg_t msg = (cdevallmsg_t)lmsg; 178 int error; 179 180 switch(msg->am_lmsg.ms_cmd) { 181 case CDEV_CMD_OPEN: 182 error = diskopen( 183 msg->am_open.msg.dev, 184 msg->am_open.oflags, 185 msg->am_open.devtype, 186 msg->am_open.td); 187 break; 188 case CDEV_CMD_CLOSE: 189 error = diskclose( 190 msg->am_close.msg.dev, 191 msg->am_close.fflag, 192 msg->am_close.devtype, 193 msg->am_close.td); 194 break; 195 case CDEV_CMD_IOCTL: 196 error = diskioctl( 197 msg->am_ioctl.msg.dev, 198 msg->am_ioctl.cmd, 199 msg->am_ioctl.data, 200 msg->am_ioctl.fflag, 201 msg->am_ioctl.td); 202 break; 203 case CDEV_CMD_STRATEGY: 204 diskstrategy(msg->am_strategy.bp); 205 error = 0; 206 break; 207 case CDEV_CMD_PSIZE: 208 msg->am_psize.result = diskpsize(msg->am_psize.msg.dev); 209 error = 0; /* XXX */ 210 break; 211 default: 212 error = lwkt_forwardmsg(disk->d_fwdport, &msg->am_lmsg); 213 break; 214 } 215 return(error); 216 } 217 218 static int 219 diskopen(dev_t dev, int oflags, int devtype, struct thread *td) 220 { 221 dev_t pdev; 222 struct disk *dp; 223 int error; 224 225 error = 0; 226 pdev = dkmodpart(dkmodslice(dev, WHOLE_DISK_SLICE), RAW_PART); 227 228 dp = pdev->si_disk; 229 if (dp == NULL) 230 return (ENXIO); 231 232 while (dp->d_flags & DISKFLAG_LOCK) { 233 dp->d_flags |= DISKFLAG_WANTED; 234 error = tsleep(dp, PCATCH, "diskopen", hz); 235 if (error) 236 return (error); 237 } 238 dp->d_flags |= DISKFLAG_LOCK; 239 240 if (!dsisopen(dp->d_slice)) { 241 if (!pdev->si_iosize_max) 242 pdev->si_iosize_max = dev->si_iosize_max; 243 error = dev_port_dopen(dp->d_fwdport, pdev, oflags, devtype, td); 244 } 245 246 /* Inherit properties from the whole/raw dev_t */ 247 inherit_raw(pdev, dev); 248 249 if (error) 250 goto out; 251 252 error = dsopen(dev, devtype, dp->d_dsflags, &dp->d_slice, &dp->d_label); 253 254 if (!dsisopen(dp->d_slice)) 255 dev_port_dclose(dp->d_fwdport, pdev, oflags, devtype, td); 256 out: 257 dp->d_flags &= ~DISKFLAG_LOCK; 258 if (dp->d_flags & DISKFLAG_WANTED) { 259 dp->d_flags &= ~DISKFLAG_WANTED; 260 wakeup(dp); 261 } 262 263 return(error); 264 } 265 266 static int 267 diskclose(dev_t dev, int fflag, int devtype, struct thread *td) 268 { 269 struct disk *dp; 270 int error; 271 dev_t pdev; 272 273 error = 0; 274 pdev = dkmodpart(dkmodslice(dev, WHOLE_DISK_SLICE), RAW_PART); 275 dp = pdev->si_disk; 276 if (!dp) 277 return (ENXIO); 278 dsclose(dev, devtype, dp->d_slice); 279 if (!dsisopen(dp->d_slice)) 280 error = dev_port_dclose(dp->d_fwdport, pdev, fflag, devtype, td); 281 return (error); 282 } 283 284 static void 285 diskstrategy(struct buf *bp) 286 { 287 dev_t pdev; 288 struct disk *dp; 289 290 pdev = dkmodpart(dkmodslice(bp->b_dev, WHOLE_DISK_SLICE), RAW_PART); 291 dp = pdev->si_disk; 292 if (dp != bp->b_dev->si_disk) 293 inherit_raw(pdev, bp->b_dev); 294 295 if (!dp) { 296 bp->b_error = ENXIO; 297 bp->b_flags |= B_ERROR; 298 biodone(bp); 299 return; 300 } 301 302 if (dscheck(bp, dp->d_slice) <= 0) { 303 biodone(bp); 304 return; 305 } 306 dev_port_dstrategy(dp->d_fwdport, dp->d_dev, bp); 307 } 308 309 /* 310 * note: when forwarding the ioctl we use the original device rather then 311 * the whole disk slice. 312 */ 313 static int 314 diskioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td) 315 { 316 struct disk *dp; 317 int error; 318 dev_t pdev; 319 320 pdev = dkmodpart(dkmodslice(dev, WHOLE_DISK_SLICE), RAW_PART); 321 dp = pdev->si_disk; 322 if (!dp) 323 return (ENXIO); 324 error = dsioctl(dev, cmd, data, fflag, &dp->d_slice); 325 if (error == ENOIOCTL) 326 error = dev_port_dioctl(dp->d_fwdport, dev, cmd, data, fflag, td); 327 return (error); 328 } 329 330 static int 331 diskpsize(dev_t dev) 332 { 333 struct disk *dp; 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 (-1); 340 if (dp != dev->si_disk) { 341 dev->si_drv1 = pdev->si_drv1; 342 dev->si_drv2 = pdev->si_drv2; 343 /* XXX: don't set bp->b_dev->si_disk (?) */ 344 } 345 return (dssize(dev, &dp->d_slice)); 346 } 347 348 SYSCTL_DECL(_debug_sizeof); 349 350 SYSCTL_INT(_debug_sizeof, OID_AUTO, disklabel, CTLFLAG_RD, 351 0, sizeof(struct disklabel), "sizeof(struct disklabel)"); 352 353 SYSCTL_INT(_debug_sizeof, OID_AUTO, diskslices, CTLFLAG_RD, 354 0, sizeof(struct diskslices), "sizeof(struct diskslices)"); 355 356 SYSCTL_INT(_debug_sizeof, OID_AUTO, disk, CTLFLAG_RD, 357 0, sizeof(struct disk), "sizeof(struct disk)"); 358