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 28struct 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 40static int simdisk_count = CONFIG_BLK_DEV_SIMDISK_COUNT; 41module_param(simdisk_count, int, S_IRUGO); 42MODULE_PARM_DESC(simdisk_count, "Number of simdisk units."); 43 44static int n_files; 45static 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 54static 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 64static const struct kernel_param_ops simdisk_param_ops_filename = { 65 .set = simdisk_param_set_filename, 66}; 67module_param_cb(filename, &simdisk_param_ops_filename, &n_files, 0); 68MODULE_PARM_DESC(filename, "Backing storage filename."); 69 70static int simdisk_major = SIMDISK_MAJOR; 71 72static 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_lseek(dev->fd, offset, SEEK_SET); 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 104static int simdisk_xfer_bio(struct simdisk *dev, struct bio *bio) 105{ 106 struct bio_vec bvec; 107 struct bvec_iter iter; 108 sector_t sector = bio->bi_iter.bi_sector; 109 110 bio_for_each_segment(bvec, bio, iter) { 111 char *buffer = __bio_kmap_atomic(bio, iter); 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(buffer); 118 } 119 return 0; 120} 121 122static 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 130static 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 142static void 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} 149 150static const struct block_device_operations simdisk_ops = { 151 .owner = THIS_MODULE, 152 .open = simdisk_open, 153 .release = simdisk_release, 154}; 155 156static struct simdisk *sddev; 157static struct proc_dir_entry *simdisk_procdir; 158 159static int simdisk_attach(struct simdisk *dev, const char *filename) 160{ 161 int err = 0; 162 163 filename = kstrdup(filename, GFP_KERNEL); 164 if (filename == NULL) 165 return -ENOMEM; 166 167 spin_lock(&dev->lock); 168 169 if (dev->fd != -1) { 170 err = -EBUSY; 171 goto out; 172 } 173 dev->fd = simc_open(filename, O_RDWR, 0); 174 if (dev->fd == -1) { 175 pr_err("SIMDISK: Can't open %s: %d\n", filename, errno); 176 err = -ENODEV; 177 goto out; 178 } 179 dev->size = simc_lseek(dev->fd, 0, SEEK_END); 180 set_capacity(dev->gd, dev->size >> SECTOR_SHIFT); 181 dev->filename = filename; 182 pr_info("SIMDISK: %s=%s\n", dev->gd->disk_name, dev->filename); 183out: 184 if (err) 185 kfree(filename); 186 spin_unlock(&dev->lock); 187 188 return err; 189} 190 191static int simdisk_detach(struct simdisk *dev) 192{ 193 int err = 0; 194 195 spin_lock(&dev->lock); 196 197 if (dev->users != 0) { 198 err = -EBUSY; 199 } else if (dev->fd != -1) { 200 if (simc_close(dev->fd)) { 201 pr_err("SIMDISK: error closing %s: %d\n", 202 dev->filename, errno); 203 err = -EIO; 204 } else { 205 pr_info("SIMDISK: %s detached from %s\n", 206 dev->gd->disk_name, dev->filename); 207 dev->fd = -1; 208 kfree(dev->filename); 209 dev->filename = NULL; 210 } 211 } 212 spin_unlock(&dev->lock); 213 return err; 214} 215 216static ssize_t proc_read_simdisk(struct file *file, char __user *buf, 217 size_t size, loff_t *ppos) 218{ 219 struct simdisk *dev = PDE_DATA(file_inode(file)); 220 const char *s = dev->filename; 221 if (s) { 222 ssize_t n = simple_read_from_buffer(buf, size, ppos, 223 s, strlen(s)); 224 if (n < 0) 225 return n; 226 buf += n; 227 size -= n; 228 } 229 return simple_read_from_buffer(buf, size, ppos, "\n", 1); 230} 231 232static ssize_t proc_write_simdisk(struct file *file, const char __user *buf, 233 size_t count, loff_t *ppos) 234{ 235 char *tmp = kmalloc(count + 1, GFP_KERNEL); 236 struct simdisk *dev = PDE_DATA(file_inode(file)); 237 int err; 238 239 if (tmp == NULL) 240 return -ENOMEM; 241 if (copy_from_user(tmp, buf, count)) { 242 err = -EFAULT; 243 goto out_free; 244 } 245 246 err = simdisk_detach(dev); 247 if (err != 0) 248 goto out_free; 249 250 if (count > 0 && tmp[count - 1] == '\n') 251 tmp[count - 1] = 0; 252 else 253 tmp[count] = 0; 254 255 if (tmp[0]) 256 err = simdisk_attach(dev, tmp); 257 258 if (err == 0) 259 err = count; 260out_free: 261 kfree(tmp); 262 return err; 263} 264 265static const struct file_operations fops = { 266 .read = proc_read_simdisk, 267 .write = proc_write_simdisk, 268 .llseek = default_llseek, 269}; 270 271static int __init simdisk_setup(struct simdisk *dev, int which, 272 struct proc_dir_entry *procdir) 273{ 274 char tmp[2] = { '0' + which, 0 }; 275 276 dev->fd = -1; 277 dev->filename = NULL; 278 spin_lock_init(&dev->lock); 279 dev->users = 0; 280 281 dev->queue = blk_alloc_queue(GFP_KERNEL); 282 if (dev->queue == NULL) { 283 pr_err("blk_alloc_queue failed\n"); 284 goto out_alloc_queue; 285 } 286 287 blk_queue_make_request(dev->queue, simdisk_make_request); 288 dev->queue->queuedata = dev; 289 290 dev->gd = alloc_disk(SIMDISK_MINORS); 291 if (dev->gd == NULL) { 292 pr_err("alloc_disk failed\n"); 293 goto out_alloc_disk; 294 } 295 dev->gd->major = simdisk_major; 296 dev->gd->first_minor = which; 297 dev->gd->fops = &simdisk_ops; 298 dev->gd->queue = dev->queue; 299 dev->gd->private_data = dev; 300 snprintf(dev->gd->disk_name, 32, "simdisk%d", which); 301 set_capacity(dev->gd, 0); 302 add_disk(dev->gd); 303 304 dev->procfile = proc_create_data(tmp, 0644, procdir, &fops, dev); 305 return 0; 306 307out_alloc_disk: 308 blk_cleanup_queue(dev->queue); 309 dev->queue = NULL; 310out_alloc_queue: 311 simc_close(dev->fd); 312 return -EIO; 313} 314 315static int __init simdisk_init(void) 316{ 317 int i; 318 319 if (register_blkdev(simdisk_major, "simdisk") < 0) { 320 pr_err("SIMDISK: register_blkdev: %d\n", simdisk_major); 321 return -EIO; 322 } 323 pr_info("SIMDISK: major: %d\n", simdisk_major); 324 325 if (n_files > simdisk_count) 326 simdisk_count = n_files; 327 if (simdisk_count > MAX_SIMDISK_COUNT) 328 simdisk_count = MAX_SIMDISK_COUNT; 329 330 sddev = kmalloc(simdisk_count * sizeof(struct simdisk), 331 GFP_KERNEL); 332 if (sddev == NULL) 333 goto out_unregister; 334 335 simdisk_procdir = proc_mkdir("simdisk", 0); 336 if (simdisk_procdir == NULL) 337 goto out_free_unregister; 338 339 for (i = 0; i < simdisk_count; ++i) { 340 if (simdisk_setup(sddev + i, i, simdisk_procdir) == 0) { 341 if (filename[i] != NULL && filename[i][0] != 0 && 342 (n_files == 0 || i < n_files)) 343 simdisk_attach(sddev + i, filename[i]); 344 } 345 } 346 347 return 0; 348 349out_free_unregister: 350 kfree(sddev); 351out_unregister: 352 unregister_blkdev(simdisk_major, "simdisk"); 353 return -ENOMEM; 354} 355module_init(simdisk_init); 356 357static void simdisk_teardown(struct simdisk *dev, int which, 358 struct proc_dir_entry *procdir) 359{ 360 char tmp[2] = { '0' + which, 0 }; 361 362 simdisk_detach(dev); 363 if (dev->gd) 364 del_gendisk(dev->gd); 365 if (dev->queue) 366 blk_cleanup_queue(dev->queue); 367 remove_proc_entry(tmp, procdir); 368} 369 370static void __exit simdisk_exit(void) 371{ 372 int i; 373 374 for (i = 0; i < simdisk_count; ++i) 375 simdisk_teardown(sddev + i, i, simdisk_procdir); 376 remove_proc_entry("simdisk", 0); 377 kfree(sddev); 378 unregister_blkdev(simdisk_major, "simdisk"); 379} 380module_exit(simdisk_exit); 381 382MODULE_ALIAS_BLOCKDEV_MAJOR(SIMDISK_MAJOR); 383 384MODULE_LICENSE("GPL"); 385