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/string_choices.h> 18 #include <linux/blkdev.h> 19 #include <linux/bio.h> 20 #include <linux/proc_fs.h> 21 #include <linux/uaccess.h> 22 #include <platform/simcall.h> 23 24 #define SIMDISK_MAJOR 240 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 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 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 /* 54 * The simdisk code can be built either into the kernel or as a loadable module. 55 * When built-in, CONFIG_SIMDISK{0,1}_FILENAME can be used to specify the 56 * initial simdisk filenames and additional filenames can be provided on the 57 * kernel command line. These arguments are parsed during early boot when slab 58 * is not yet available, but the command line itself is preserved for the 59 * lifetime of the kernel, so the incoming pointer is stored directly. 60 * When built as a loadable module, each value is copied with kstrdup() and all 61 * allocated memory is freed in simdisk_param_free_filename() when the module is 62 * unloaded. 63 */ 64 static int simdisk_param_set_filename(const char *val, 65 const struct kernel_param *kp) 66 { 67 char *str; 68 69 if (n_files >= ARRAY_SIZE(filename)) 70 return -EINVAL; 71 72 #ifdef MODULE 73 str = kstrdup(val, GFP_KERNEL); 74 if (!str) 75 return -ENOMEM; 76 #else 77 str = (char *)val; 78 #endif 79 80 filename[n_files++] = str; 81 return 0; 82 } 83 84 static void simdisk_param_free_filename(void *arg) 85 { 86 for (int i = 0; i < n_files; i++) 87 kfree(filename[i]); 88 } 89 90 static const struct kernel_param_ops simdisk_param_ops_filename = { 91 .set = simdisk_param_set_filename, 92 .free = simdisk_param_free_filename, 93 }; 94 module_param_cb(filename, &simdisk_param_ops_filename, NULL, 0); 95 MODULE_PARM_DESC(filename, "Backing storage filename."); 96 97 static int simdisk_major = SIMDISK_MAJOR; 98 99 static void simdisk_transfer(struct simdisk *dev, unsigned long sector, 100 unsigned long nsect, char *buffer, int write) 101 { 102 unsigned long offset = sector << SECTOR_SHIFT; 103 unsigned long nbytes = nsect << SECTOR_SHIFT; 104 105 if (offset > dev->size || dev->size - offset < nbytes) { 106 pr_notice("Beyond-end %s (%ld %ld)\n", 107 str_write_read(write), offset, nbytes); 108 return; 109 } 110 111 spin_lock(&dev->lock); 112 while (nbytes > 0) { 113 unsigned long io; 114 115 simc_lseek(dev->fd, offset, SEEK_SET); 116 READ_ONCE(*buffer); 117 if (write) 118 io = simc_write(dev->fd, buffer, nbytes); 119 else 120 io = simc_read(dev->fd, buffer, nbytes); 121 if (io == -1) { 122 pr_err("SIMDISK: IO error %d\n", errno); 123 break; 124 } 125 buffer += io; 126 offset += io; 127 nbytes -= io; 128 } 129 spin_unlock(&dev->lock); 130 } 131 132 static void simdisk_submit_bio(struct bio *bio) 133 { 134 struct simdisk *dev = bio->bi_bdev->bd_disk->private_data; 135 struct bio_vec bvec; 136 struct bvec_iter iter; 137 sector_t sector = bio->bi_iter.bi_sector; 138 139 bio_for_each_segment(bvec, bio, iter) { 140 char *buffer = bvec_kmap_local(&bvec); 141 unsigned len = bvec.bv_len >> SECTOR_SHIFT; 142 143 simdisk_transfer(dev, sector, len, buffer, 144 bio_data_dir(bio) == WRITE); 145 sector += len; 146 kunmap_local(buffer); 147 } 148 149 bio_endio(bio); 150 } 151 152 static int simdisk_open(struct gendisk *disk, blk_mode_t mode) 153 { 154 struct simdisk *dev = disk->private_data; 155 156 spin_lock(&dev->lock); 157 ++dev->users; 158 spin_unlock(&dev->lock); 159 return 0; 160 } 161 162 static void simdisk_release(struct gendisk *disk) 163 { 164 struct simdisk *dev = disk->private_data; 165 spin_lock(&dev->lock); 166 --dev->users; 167 spin_unlock(&dev->lock); 168 } 169 170 static const struct block_device_operations simdisk_ops = { 171 .owner = THIS_MODULE, 172 .submit_bio = simdisk_submit_bio, 173 .open = simdisk_open, 174 .release = simdisk_release, 175 }; 176 177 static struct simdisk *sddev; 178 static struct proc_dir_entry *simdisk_procdir; 179 180 static int simdisk_attach(struct simdisk *dev, const char *filename) 181 { 182 int err = 0; 183 184 filename = kstrdup(filename, GFP_KERNEL); 185 if (filename == NULL) 186 return -ENOMEM; 187 188 spin_lock(&dev->lock); 189 190 if (dev->fd != -1) { 191 err = -EBUSY; 192 goto out; 193 } 194 dev->fd = simc_open(filename, O_RDWR, 0); 195 if (dev->fd == -1) { 196 pr_err("SIMDISK: Can't open %s: %d\n", filename, errno); 197 err = -ENODEV; 198 goto out; 199 } 200 dev->size = simc_lseek(dev->fd, 0, SEEK_END); 201 set_capacity(dev->gd, dev->size >> SECTOR_SHIFT); 202 dev->filename = filename; 203 pr_info("SIMDISK: %s=%s\n", dev->gd->disk_name, dev->filename); 204 out: 205 if (err) 206 kfree(filename); 207 spin_unlock(&dev->lock); 208 209 return err; 210 } 211 212 static int simdisk_detach(struct simdisk *dev) 213 { 214 int err = 0; 215 216 spin_lock(&dev->lock); 217 218 if (dev->users != 0) { 219 err = -EBUSY; 220 } else if (dev->fd != -1) { 221 if (simc_close(dev->fd)) { 222 pr_err("SIMDISK: error closing %s: %d\n", 223 dev->filename, errno); 224 err = -EIO; 225 } else { 226 pr_info("SIMDISK: %s detached from %s\n", 227 dev->gd->disk_name, dev->filename); 228 dev->fd = -1; 229 kfree(dev->filename); 230 dev->filename = NULL; 231 } 232 } 233 spin_unlock(&dev->lock); 234 return err; 235 } 236 237 static ssize_t proc_read_simdisk(struct file *file, char __user *buf, 238 size_t size, loff_t *ppos) 239 { 240 struct simdisk *dev = pde_data(file_inode(file)); 241 const char *s = dev->filename; 242 if (s) { 243 ssize_t len = strlen(s); 244 char *temp = kmalloc(len + 2, GFP_KERNEL); 245 246 if (!temp) 247 return -ENOMEM; 248 249 len = scnprintf(temp, len + 2, "%s\n", s); 250 len = simple_read_from_buffer(buf, size, ppos, 251 temp, len); 252 253 kfree(temp); 254 return len; 255 } 256 return simple_read_from_buffer(buf, size, ppos, "\n", 1); 257 } 258 259 static ssize_t proc_write_simdisk(struct file *file, const char __user *buf, 260 size_t count, loff_t *ppos) 261 { 262 char *tmp; 263 struct simdisk *dev = pde_data(file_inode(file)); 264 int err; 265 266 if (count == 0 || count > PAGE_SIZE) 267 return -EINVAL; 268 269 tmp = memdup_user_nul(buf, count); 270 if (IS_ERR(tmp)) 271 return PTR_ERR(tmp); 272 273 err = simdisk_detach(dev); 274 if (err != 0) 275 goto out_free; 276 277 if (count > 0 && tmp[count - 1] == '\n') 278 tmp[count - 1] = 0; 279 280 if (tmp[0]) 281 err = simdisk_attach(dev, tmp); 282 283 if (err == 0) 284 err = count; 285 out_free: 286 kfree(tmp); 287 return err; 288 } 289 290 static const struct proc_ops simdisk_proc_ops = { 291 .proc_read = proc_read_simdisk, 292 .proc_write = proc_write_simdisk, 293 .proc_lseek = default_llseek, 294 }; 295 296 static int __init simdisk_setup(struct simdisk *dev, int which, 297 struct proc_dir_entry *procdir) 298 { 299 struct queue_limits lim = { 300 .features = BLK_FEAT_ROTATIONAL, 301 }; 302 char tmp[2] = { '0' + which, 0 }; 303 int err; 304 305 dev->fd = -1; 306 dev->filename = NULL; 307 spin_lock_init(&dev->lock); 308 dev->users = 0; 309 310 dev->gd = blk_alloc_disk(&lim, NUMA_NO_NODE); 311 if (IS_ERR(dev->gd)) { 312 err = PTR_ERR(dev->gd); 313 goto out; 314 } 315 dev->gd->major = simdisk_major; 316 dev->gd->first_minor = which; 317 dev->gd->minors = SIMDISK_MINORS; 318 dev->gd->fops = &simdisk_ops; 319 dev->gd->private_data = dev; 320 snprintf(dev->gd->disk_name, 32, "simdisk%d", which); 321 set_capacity(dev->gd, 0); 322 err = add_disk(dev->gd); 323 if (err) 324 goto out_cleanup_disk; 325 326 dev->procfile = proc_create_data(tmp, 0644, procdir, &simdisk_proc_ops, dev); 327 328 return 0; 329 330 out_cleanup_disk: 331 put_disk(dev->gd); 332 out: 333 return err; 334 } 335 336 static int __init simdisk_init(void) 337 { 338 int i; 339 340 if (register_blkdev(simdisk_major, "simdisk") < 0) { 341 pr_err("SIMDISK: register_blkdev: %d\n", simdisk_major); 342 return -EIO; 343 } 344 pr_info("SIMDISK: major: %d\n", simdisk_major); 345 346 if (n_files > simdisk_count) 347 simdisk_count = n_files; 348 if (simdisk_count > MAX_SIMDISK_COUNT) 349 simdisk_count = MAX_SIMDISK_COUNT; 350 351 sddev = kmalloc_objs(*sddev, simdisk_count); 352 if (sddev == NULL) 353 goto out_unregister; 354 355 simdisk_procdir = proc_mkdir("simdisk", 0); 356 if (simdisk_procdir == NULL) 357 goto out_free_unregister; 358 359 for (i = 0; i < simdisk_count; ++i) { 360 if (simdisk_setup(sddev + i, i, simdisk_procdir) == 0) { 361 if (filename[i] != NULL && filename[i][0] != 0 && 362 (n_files == 0 || i < n_files)) 363 simdisk_attach(sddev + i, filename[i]); 364 } 365 } 366 367 return 0; 368 369 out_free_unregister: 370 kfree(sddev); 371 out_unregister: 372 unregister_blkdev(simdisk_major, "simdisk"); 373 return -ENOMEM; 374 } 375 module_init(simdisk_init); 376 377 static void simdisk_teardown(struct simdisk *dev, int which, 378 struct proc_dir_entry *procdir) 379 { 380 char tmp[2] = { '0' + which, 0 }; 381 382 simdisk_detach(dev); 383 if (dev->gd) { 384 del_gendisk(dev->gd); 385 put_disk(dev->gd); 386 } 387 remove_proc_entry(tmp, procdir); 388 } 389 390 static void __exit simdisk_exit(void) 391 { 392 int i; 393 394 for (i = 0; i < simdisk_count; ++i) 395 simdisk_teardown(sddev + i, i, simdisk_procdir); 396 remove_proc_entry("simdisk", 0); 397 kfree(sddev); 398 unregister_blkdev(simdisk_major, "simdisk"); 399 } 400 module_exit(simdisk_exit); 401 402 MODULE_ALIAS_BLOCKDEV_MAJOR(SIMDISK_MAJOR); 403 404 MODULE_LICENSE("GPL"); 405