1 /* 2 * arch/xtensa/platforms/iss/simdisk.c 3 * 4 * This file is subject to the terms and conditions of the GNU General Public 5 * License. See the file "COPYING" in the main directory of this archive 6 * for more details. 7 * 8 * Copyright (C) 2001-2013 Tensilica Inc. 9 * Authors Victor Prupis 10 */ 11 12 #include <linux/module.h> 13 #include <linux/moduleparam.h> 14 #include <linux/kernel.h> 15 #include <linux/init.h> 16 #include <linux/string.h> 17 #include <linux/blkdev.h> 18 #include <linux/bio.h> 19 #include <linux/proc_fs.h> 20 #include <asm/uaccess.h> 21 #include <platform/simcall.h> 22 23 #define SIMDISK_MAJOR 240 24 #define SECTOR_SHIFT 9 25 #define SIMDISK_MINORS 1 26 #define MAX_SIMDISK_COUNT 10 27 28 struct simdisk { 29 const char *filename; 30 spinlock_t lock; 31 struct request_queue *queue; 32 struct gendisk *gd; 33 struct proc_dir_entry *procfile; 34 int users; 35 unsigned long size; 36 int fd; 37 }; 38 39 40 static int simdisk_count = CONFIG_BLK_DEV_SIMDISK_COUNT; 41 module_param(simdisk_count, int, S_IRUGO); 42 MODULE_PARM_DESC(simdisk_count, "Number of simdisk units."); 43 44 static int n_files; 45 static const char *filename[MAX_SIMDISK_COUNT] = { 46 #ifdef CONFIG_SIMDISK0_FILENAME 47 CONFIG_SIMDISK0_FILENAME, 48 #ifdef CONFIG_SIMDISK1_FILENAME 49 CONFIG_SIMDISK1_FILENAME, 50 #endif 51 #endif 52 }; 53 54 static int simdisk_param_set_filename(const char *val, 55 const struct kernel_param *kp) 56 { 57 if (n_files < ARRAY_SIZE(filename)) 58 filename[n_files++] = val; 59 else 60 return -EINVAL; 61 return 0; 62 } 63 64 static const struct kernel_param_ops simdisk_param_ops_filename = { 65 .set = simdisk_param_set_filename, 66 }; 67 module_param_cb(filename, &simdisk_param_ops_filename, &n_files, 0); 68 MODULE_PARM_DESC(filename, "Backing storage filename."); 69 70 static int simdisk_major = SIMDISK_MAJOR; 71 72 static void simdisk_transfer(struct simdisk *dev, unsigned long sector, 73 unsigned long nsect, char *buffer, int write) 74 { 75 unsigned long offset = sector << SECTOR_SHIFT; 76 unsigned long nbytes = nsect << SECTOR_SHIFT; 77 78 if (offset > dev->size || dev->size - offset < nbytes) { 79 pr_notice("Beyond-end %s (%ld %ld)\n", 80 write ? "write" : "read", offset, nbytes); 81 return; 82 } 83 84 spin_lock(&dev->lock); 85 while (nbytes > 0) { 86 unsigned long io; 87 88 __simc(SYS_lseek, dev->fd, offset, SEEK_SET, 0, 0); 89 if (write) 90 io = simc_write(dev->fd, buffer, nbytes); 91 else 92 io = simc_read(dev->fd, buffer, nbytes); 93 if (io == -1) { 94 pr_err("SIMDISK: IO error %d\n", errno); 95 break; 96 } 97 buffer += io; 98 offset += io; 99 nbytes -= io; 100 } 101 spin_unlock(&dev->lock); 102 } 103 104 static int simdisk_xfer_bio(struct simdisk *dev, struct bio *bio) 105 { 106 int i; 107 struct bio_vec *bvec; 108 sector_t sector = bio->bi_sector; 109 110 bio_for_each_segment(bvec, bio, i) { 111 char *buffer = __bio_kmap_atomic(bio, i, KM_USER0); 112 unsigned len = bvec->bv_len >> SECTOR_SHIFT; 113 114 simdisk_transfer(dev, sector, len, buffer, 115 bio_data_dir(bio) == WRITE); 116 sector += len; 117 __bio_kunmap_atomic(bio, KM_USER0); 118 } 119 return 0; 120 } 121 122 static void simdisk_make_request(struct request_queue *q, struct bio *bio) 123 { 124 struct simdisk *dev = q->queuedata; 125 int status = simdisk_xfer_bio(dev, bio); 126 bio_endio(bio, status); 127 } 128 129 130 static int simdisk_open(struct block_device *bdev, fmode_t mode) 131 { 132 struct simdisk *dev = bdev->bd_disk->private_data; 133 134 spin_lock(&dev->lock); 135 if (!dev->users) 136 check_disk_change(bdev); 137 ++dev->users; 138 spin_unlock(&dev->lock); 139 return 0; 140 } 141 142 static int simdisk_release(struct gendisk *disk, fmode_t mode) 143 { 144 struct simdisk *dev = disk->private_data; 145 spin_lock(&dev->lock); 146 --dev->users; 147 spin_unlock(&dev->lock); 148 return 0; 149 } 150 151 static const struct block_device_operations simdisk_ops = { 152 .owner = THIS_MODULE, 153 .open = simdisk_open, 154 .release = simdisk_release, 155 }; 156 157 static struct simdisk *sddev; 158 static struct proc_dir_entry *simdisk_procdir; 159 160 static int simdisk_attach(struct simdisk *dev, const char *filename) 161 { 162 int err = 0; 163 164 filename = kstrdup(filename, GFP_KERNEL); 165 if (filename == NULL) 166 return -ENOMEM; 167 168 spin_lock(&dev->lock); 169 170 if (dev->fd != -1) { 171 err = -EBUSY; 172 goto out; 173 } 174 dev->fd = simc_open(filename, O_RDWR, 0); 175 if (dev->fd == -1) { 176 pr_err("SIMDISK: Can't open %s: %d\n", filename, errno); 177 err = -ENODEV; 178 goto out; 179 } 180 dev->size = __simc(SYS_lseek, dev->fd, 0, SEEK_END, 0, 0); 181 set_capacity(dev->gd, dev->size >> SECTOR_SHIFT); 182 dev->filename = filename; 183 pr_info("SIMDISK: %s=%s\n", dev->gd->disk_name, dev->filename); 184 out: 185 if (err) 186 kfree(filename); 187 spin_unlock(&dev->lock); 188 189 return err; 190 } 191 192 static int simdisk_detach(struct simdisk *dev) 193 { 194 int err = 0; 195 196 spin_lock(&dev->lock); 197 198 if (dev->users != 0) { 199 err = -EBUSY; 200 } else if (dev->fd != -1) { 201 if (simc_close(dev->fd)) { 202 pr_err("SIMDISK: error closing %s: %d\n", 203 dev->filename, errno); 204 err = -EIO; 205 } else { 206 pr_info("SIMDISK: %s detached from %s\n", 207 dev->gd->disk_name, dev->filename); 208 dev->fd = -1; 209 kfree(dev->filename); 210 dev->filename = NULL; 211 } 212 } 213 spin_unlock(&dev->lock); 214 return err; 215 } 216 217 static int proc_read_simdisk(char *page, char **start, off_t off, 218 int count, int *eof, void *data) 219 { 220 int len; 221 struct simdisk *dev = (struct simdisk *) data; 222 len = sprintf(page, "%s\n", dev->filename ? dev->filename : ""); 223 return len; 224 } 225 226 static int proc_write_simdisk(struct file *file, const char *buffer, 227 unsigned long count, void *data) 228 { 229 char *tmp = kmalloc(count + 1, GFP_KERNEL); 230 struct simdisk *dev = (struct simdisk *) data; 231 int err; 232 233 if (tmp == NULL) 234 return -ENOMEM; 235 if (copy_from_user(tmp, buffer, count)) { 236 err = -EFAULT; 237 goto out_free; 238 } 239 240 err = simdisk_detach(dev); 241 if (err != 0) 242 goto out_free; 243 244 if (count > 0 && tmp[count - 1] == '\n') 245 tmp[count - 1] = 0; 246 else 247 tmp[count] = 0; 248 249 if (tmp[0]) 250 err = simdisk_attach(dev, tmp); 251 252 if (err == 0) 253 err = count; 254 out_free: 255 kfree(tmp); 256 return err; 257 } 258 259 static int __init simdisk_setup(struct simdisk *dev, int which, 260 struct proc_dir_entry *procdir) 261 { 262 char tmp[2] = { '0' + which, 0 }; 263 264 dev->fd = -1; 265 dev->filename = NULL; 266 spin_lock_init(&dev->lock); 267 dev->users = 0; 268 269 dev->queue = blk_alloc_queue(GFP_KERNEL); 270 if (dev->queue == NULL) { 271 pr_err("blk_alloc_queue failed\n"); 272 goto out_alloc_queue; 273 } 274 275 blk_queue_make_request(dev->queue, simdisk_make_request); 276 dev->queue->queuedata = dev; 277 278 dev->gd = alloc_disk(SIMDISK_MINORS); 279 if (dev->gd == NULL) { 280 pr_err("alloc_disk failed\n"); 281 goto out_alloc_disk; 282 } 283 dev->gd->major = simdisk_major; 284 dev->gd->first_minor = which; 285 dev->gd->fops = &simdisk_ops; 286 dev->gd->queue = dev->queue; 287 dev->gd->private_data = dev; 288 snprintf(dev->gd->disk_name, 32, "simdisk%d", which); 289 set_capacity(dev->gd, 0); 290 add_disk(dev->gd); 291 292 dev->procfile = create_proc_entry(tmp, 0644, procdir); 293 dev->procfile->data = dev; 294 dev->procfile->read_proc = proc_read_simdisk; 295 dev->procfile->write_proc = proc_write_simdisk; 296 return 0; 297 298 out_alloc_disk: 299 blk_cleanup_queue(dev->queue); 300 dev->queue = NULL; 301 out_alloc_queue: 302 simc_close(dev->fd); 303 return -EIO; 304 } 305 306 static int __init simdisk_init(void) 307 { 308 int i; 309 310 if (register_blkdev(simdisk_major, "simdisk") < 0) { 311 pr_err("SIMDISK: register_blkdev: %d\n", simdisk_major); 312 return -EIO; 313 } 314 pr_info("SIMDISK: major: %d\n", simdisk_major); 315 316 if (n_files > simdisk_count) 317 simdisk_count = n_files; 318 if (simdisk_count > MAX_SIMDISK_COUNT) 319 simdisk_count = MAX_SIMDISK_COUNT; 320 321 sddev = kmalloc(simdisk_count * sizeof(struct simdisk), 322 GFP_KERNEL); 323 if (sddev == NULL) 324 goto out_unregister; 325 326 simdisk_procdir = proc_mkdir("simdisk", 0); 327 if (simdisk_procdir == NULL) 328 goto out_free_unregister; 329 330 for (i = 0; i < simdisk_count; ++i) { 331 if (simdisk_setup(sddev + i, i, simdisk_procdir) == 0) { 332 if (filename[i] != NULL && filename[i][0] != 0 && 333 (n_files == 0 || i < n_files)) 334 simdisk_attach(sddev + i, filename[i]); 335 } 336 } 337 338 return 0; 339 340 out_free_unregister: 341 kfree(sddev); 342 out_unregister: 343 unregister_blkdev(simdisk_major, "simdisk"); 344 return -ENOMEM; 345 } 346 module_init(simdisk_init); 347 348 static void simdisk_teardown(struct simdisk *dev, int which, 349 struct proc_dir_entry *procdir) 350 { 351 char tmp[2] = { '0' + which, 0 }; 352 353 simdisk_detach(dev); 354 if (dev->gd) 355 del_gendisk(dev->gd); 356 if (dev->queue) 357 blk_cleanup_queue(dev->queue); 358 remove_proc_entry(tmp, procdir); 359 } 360 361 static void __exit simdisk_exit(void) 362 { 363 int i; 364 365 for (i = 0; i < simdisk_count; ++i) 366 simdisk_teardown(sddev + i, i, simdisk_procdir); 367 remove_proc_entry("simdisk", 0); 368 kfree(sddev); 369 unregister_blkdev(simdisk_major, "simdisk"); 370 } 371 module_exit(simdisk_exit); 372 373 MODULE_ALIAS_BLOCKDEV_MAJOR(SIMDISK_MAJOR); 374 375 MODULE_LICENSE("GPL"); 376