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