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 <linux/uaccess.h> 21 #include <platform/simcall.h> 22 23 #define SIMDISK_MAJOR 240 24 #define SIMDISK_MINORS 1 25 #define MAX_SIMDISK_COUNT 10 26 27 struct simdisk { 28 const char *filename; 29 spinlock_t lock; 30 struct request_queue *queue; 31 struct gendisk *gd; 32 struct proc_dir_entry *procfile; 33 int users; 34 unsigned long size; 35 int fd; 36 }; 37 38 39 static int simdisk_count = CONFIG_BLK_DEV_SIMDISK_COUNT; 40 module_param(simdisk_count, int, S_IRUGO); 41 MODULE_PARM_DESC(simdisk_count, "Number of simdisk units."); 42 43 static int n_files; 44 static const char *filename[MAX_SIMDISK_COUNT] = { 45 #ifdef CONFIG_SIMDISK0_FILENAME 46 CONFIG_SIMDISK0_FILENAME, 47 #ifdef CONFIG_SIMDISK1_FILENAME 48 CONFIG_SIMDISK1_FILENAME, 49 #endif 50 #endif 51 }; 52 53 static int simdisk_param_set_filename(const char *val, 54 const struct kernel_param *kp) 55 { 56 if (n_files < ARRAY_SIZE(filename)) 57 filename[n_files++] = val; 58 else 59 return -EINVAL; 60 return 0; 61 } 62 63 static const struct kernel_param_ops simdisk_param_ops_filename = { 64 .set = simdisk_param_set_filename, 65 }; 66 module_param_cb(filename, &simdisk_param_ops_filename, &n_files, 0); 67 MODULE_PARM_DESC(filename, "Backing storage filename."); 68 69 static int simdisk_major = SIMDISK_MAJOR; 70 71 static void simdisk_transfer(struct simdisk *dev, unsigned long sector, 72 unsigned long nsect, char *buffer, int write) 73 { 74 unsigned long offset = sector << SECTOR_SHIFT; 75 unsigned long nbytes = nsect << SECTOR_SHIFT; 76 77 if (offset > dev->size || dev->size - offset < nbytes) { 78 pr_notice("Beyond-end %s (%ld %ld)\n", 79 write ? "write" : "read", offset, nbytes); 80 return; 81 } 82 83 spin_lock(&dev->lock); 84 while (nbytes > 0) { 85 unsigned long io; 86 87 simc_lseek(dev->fd, offset, SEEK_SET); 88 READ_ONCE(*buffer); 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 blk_qc_t simdisk_submit_bio(struct bio *bio) 105 { 106 struct simdisk *dev = bio->bi_bdev->bd_disk->private_data; 107 struct bio_vec bvec; 108 struct bvec_iter iter; 109 sector_t sector = bio->bi_iter.bi_sector; 110 111 bio_for_each_segment(bvec, bio, iter) { 112 char *buffer = kmap_atomic(bvec.bv_page) + bvec.bv_offset; 113 unsigned len = bvec.bv_len >> SECTOR_SHIFT; 114 115 simdisk_transfer(dev, sector, len, buffer, 116 bio_data_dir(bio) == WRITE); 117 sector += len; 118 kunmap_atomic(buffer); 119 } 120 121 bio_endio(bio); 122 return BLK_QC_T_NONE; 123 } 124 125 static int simdisk_open(struct block_device *bdev, fmode_t mode) 126 { 127 struct simdisk *dev = bdev->bd_disk->private_data; 128 129 spin_lock(&dev->lock); 130 ++dev->users; 131 spin_unlock(&dev->lock); 132 return 0; 133 } 134 135 static void simdisk_release(struct gendisk *disk, fmode_t mode) 136 { 137 struct simdisk *dev = disk->private_data; 138 spin_lock(&dev->lock); 139 --dev->users; 140 spin_unlock(&dev->lock); 141 } 142 143 static const struct block_device_operations simdisk_ops = { 144 .owner = THIS_MODULE, 145 .submit_bio = simdisk_submit_bio, 146 .open = simdisk_open, 147 .release = simdisk_release, 148 }; 149 150 static struct simdisk *sddev; 151 static struct proc_dir_entry *simdisk_procdir; 152 153 static int simdisk_attach(struct simdisk *dev, const char *filename) 154 { 155 int err = 0; 156 157 filename = kstrdup(filename, GFP_KERNEL); 158 if (filename == NULL) 159 return -ENOMEM; 160 161 spin_lock(&dev->lock); 162 163 if (dev->fd != -1) { 164 err = -EBUSY; 165 goto out; 166 } 167 dev->fd = simc_open(filename, O_RDWR, 0); 168 if (dev->fd == -1) { 169 pr_err("SIMDISK: Can't open %s: %d\n", filename, errno); 170 err = -ENODEV; 171 goto out; 172 } 173 dev->size = simc_lseek(dev->fd, 0, SEEK_END); 174 set_capacity(dev->gd, dev->size >> SECTOR_SHIFT); 175 dev->filename = filename; 176 pr_info("SIMDISK: %s=%s\n", dev->gd->disk_name, dev->filename); 177 out: 178 if (err) 179 kfree(filename); 180 spin_unlock(&dev->lock); 181 182 return err; 183 } 184 185 static int simdisk_detach(struct simdisk *dev) 186 { 187 int err = 0; 188 189 spin_lock(&dev->lock); 190 191 if (dev->users != 0) { 192 err = -EBUSY; 193 } else if (dev->fd != -1) { 194 if (simc_close(dev->fd)) { 195 pr_err("SIMDISK: error closing %s: %d\n", 196 dev->filename, errno); 197 err = -EIO; 198 } else { 199 pr_info("SIMDISK: %s detached from %s\n", 200 dev->gd->disk_name, dev->filename); 201 dev->fd = -1; 202 kfree(dev->filename); 203 dev->filename = NULL; 204 } 205 } 206 spin_unlock(&dev->lock); 207 return err; 208 } 209 210 static ssize_t proc_read_simdisk(struct file *file, char __user *buf, 211 size_t size, loff_t *ppos) 212 { 213 struct simdisk *dev = PDE_DATA(file_inode(file)); 214 const char *s = dev->filename; 215 if (s) { 216 ssize_t n = simple_read_from_buffer(buf, size, ppos, 217 s, strlen(s)); 218 if (n < 0) 219 return n; 220 buf += n; 221 size -= n; 222 } 223 return simple_read_from_buffer(buf, size, ppos, "\n", 1); 224 } 225 226 static ssize_t proc_write_simdisk(struct file *file, const char __user *buf, 227 size_t count, loff_t *ppos) 228 { 229 char *tmp = memdup_user_nul(buf, count); 230 struct simdisk *dev = PDE_DATA(file_inode(file)); 231 int err; 232 233 if (IS_ERR(tmp)) 234 return PTR_ERR(tmp); 235 236 err = simdisk_detach(dev); 237 if (err != 0) 238 goto out_free; 239 240 if (count > 0 && tmp[count - 1] == '\n') 241 tmp[count - 1] = 0; 242 243 if (tmp[0]) 244 err = simdisk_attach(dev, tmp); 245 246 if (err == 0) 247 err = count; 248 out_free: 249 kfree(tmp); 250 return err; 251 } 252 253 static const struct proc_ops simdisk_proc_ops = { 254 .proc_read = proc_read_simdisk, 255 .proc_write = proc_write_simdisk, 256 .proc_lseek = default_llseek, 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(NUMA_NO_NODE); 270 if (dev->queue == NULL) { 271 pr_err("blk_alloc_queue failed\n"); 272 goto out_alloc_queue; 273 } 274 275 dev->gd = alloc_disk(SIMDISK_MINORS); 276 if (dev->gd == NULL) { 277 pr_err("alloc_disk failed\n"); 278 goto out_alloc_disk; 279 } 280 dev->gd->major = simdisk_major; 281 dev->gd->first_minor = which; 282 dev->gd->fops = &simdisk_ops; 283 dev->gd->queue = dev->queue; 284 dev->gd->private_data = dev; 285 snprintf(dev->gd->disk_name, 32, "simdisk%d", which); 286 set_capacity(dev->gd, 0); 287 add_disk(dev->gd); 288 289 dev->procfile = proc_create_data(tmp, 0644, procdir, &simdisk_proc_ops, dev); 290 return 0; 291 292 out_alloc_disk: 293 blk_cleanup_queue(dev->queue); 294 dev->queue = NULL; 295 out_alloc_queue: 296 return -ENOMEM; 297 } 298 299 static int __init simdisk_init(void) 300 { 301 int i; 302 303 if (register_blkdev(simdisk_major, "simdisk") < 0) { 304 pr_err("SIMDISK: register_blkdev: %d\n", simdisk_major); 305 return -EIO; 306 } 307 pr_info("SIMDISK: major: %d\n", simdisk_major); 308 309 if (n_files > simdisk_count) 310 simdisk_count = n_files; 311 if (simdisk_count > MAX_SIMDISK_COUNT) 312 simdisk_count = MAX_SIMDISK_COUNT; 313 314 sddev = kmalloc_array(simdisk_count, sizeof(*sddev), GFP_KERNEL); 315 if (sddev == NULL) 316 goto out_unregister; 317 318 simdisk_procdir = proc_mkdir("simdisk", 0); 319 if (simdisk_procdir == NULL) 320 goto out_free_unregister; 321 322 for (i = 0; i < simdisk_count; ++i) { 323 if (simdisk_setup(sddev + i, i, simdisk_procdir) == 0) { 324 if (filename[i] != NULL && filename[i][0] != 0 && 325 (n_files == 0 || i < n_files)) 326 simdisk_attach(sddev + i, filename[i]); 327 } 328 } 329 330 return 0; 331 332 out_free_unregister: 333 kfree(sddev); 334 out_unregister: 335 unregister_blkdev(simdisk_major, "simdisk"); 336 return -ENOMEM; 337 } 338 module_init(simdisk_init); 339 340 static void simdisk_teardown(struct simdisk *dev, int which, 341 struct proc_dir_entry *procdir) 342 { 343 char tmp[2] = { '0' + which, 0 }; 344 345 simdisk_detach(dev); 346 if (dev->gd) 347 del_gendisk(dev->gd); 348 if (dev->queue) 349 blk_cleanup_queue(dev->queue); 350 remove_proc_entry(tmp, procdir); 351 } 352 353 static void __exit simdisk_exit(void) 354 { 355 int i; 356 357 for (i = 0; i < simdisk_count; ++i) 358 simdisk_teardown(sddev + i, i, simdisk_procdir); 359 remove_proc_entry("simdisk", 0); 360 kfree(sddev); 361 unregister_blkdev(simdisk_major, "simdisk"); 362 } 363 module_exit(simdisk_exit); 364 365 MODULE_ALIAS_BLOCKDEV_MAJOR(SIMDISK_MAJOR); 366 367 MODULE_LICENSE("GPL"); 368