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 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_lseek(dev->fd, offset, SEEK_SET); 89 READ_ONCE(*buffer); 90 if (write) 91 io = simc_write(dev->fd, buffer, nbytes); 92 else 93 io = simc_read(dev->fd, buffer, nbytes); 94 if (io == -1) { 95 pr_err("SIMDISK: IO error %d\n", errno); 96 break; 97 } 98 buffer += io; 99 offset += io; 100 nbytes -= io; 101 } 102 spin_unlock(&dev->lock); 103 } 104 105 static blk_qc_t simdisk_make_request(struct request_queue *q, struct bio *bio) 106 { 107 struct simdisk *dev = q->queuedata; 108 struct bio_vec bvec; 109 struct bvec_iter iter; 110 sector_t sector = bio->bi_iter.bi_sector; 111 112 bio_for_each_segment(bvec, bio, iter) { 113 char *buffer = kmap_atomic(bvec.bv_page) + bvec.bv_offset; 114 unsigned len = bvec.bv_len >> SECTOR_SHIFT; 115 116 simdisk_transfer(dev, sector, len, buffer, 117 bio_data_dir(bio) == WRITE); 118 sector += len; 119 kunmap_atomic(buffer); 120 } 121 122 bio_endio(bio); 123 return BLK_QC_T_NONE; 124 } 125 126 static int simdisk_open(struct block_device *bdev, fmode_t mode) 127 { 128 struct simdisk *dev = bdev->bd_disk->private_data; 129 130 spin_lock(&dev->lock); 131 if (!dev->users) 132 check_disk_change(bdev); 133 ++dev->users; 134 spin_unlock(&dev->lock); 135 return 0; 136 } 137 138 static void simdisk_release(struct gendisk *disk, fmode_t mode) 139 { 140 struct simdisk *dev = disk->private_data; 141 spin_lock(&dev->lock); 142 --dev->users; 143 spin_unlock(&dev->lock); 144 } 145 146 static const struct block_device_operations simdisk_ops = { 147 .owner = THIS_MODULE, 148 .open = simdisk_open, 149 .release = simdisk_release, 150 }; 151 152 static struct simdisk *sddev; 153 static struct proc_dir_entry *simdisk_procdir; 154 155 static int simdisk_attach(struct simdisk *dev, const char *filename) 156 { 157 int err = 0; 158 159 filename = kstrdup(filename, GFP_KERNEL); 160 if (filename == NULL) 161 return -ENOMEM; 162 163 spin_lock(&dev->lock); 164 165 if (dev->fd != -1) { 166 err = -EBUSY; 167 goto out; 168 } 169 dev->fd = simc_open(filename, O_RDWR, 0); 170 if (dev->fd == -1) { 171 pr_err("SIMDISK: Can't open %s: %d\n", filename, errno); 172 err = -ENODEV; 173 goto out; 174 } 175 dev->size = simc_lseek(dev->fd, 0, SEEK_END); 176 set_capacity(dev->gd, dev->size >> SECTOR_SHIFT); 177 dev->filename = filename; 178 pr_info("SIMDISK: %s=%s\n", dev->gd->disk_name, dev->filename); 179 out: 180 if (err) 181 kfree(filename); 182 spin_unlock(&dev->lock); 183 184 return err; 185 } 186 187 static int simdisk_detach(struct simdisk *dev) 188 { 189 int err = 0; 190 191 spin_lock(&dev->lock); 192 193 if (dev->users != 0) { 194 err = -EBUSY; 195 } else if (dev->fd != -1) { 196 if (simc_close(dev->fd)) { 197 pr_err("SIMDISK: error closing %s: %d\n", 198 dev->filename, errno); 199 err = -EIO; 200 } else { 201 pr_info("SIMDISK: %s detached from %s\n", 202 dev->gd->disk_name, dev->filename); 203 dev->fd = -1; 204 kfree(dev->filename); 205 dev->filename = NULL; 206 } 207 } 208 spin_unlock(&dev->lock); 209 return err; 210 } 211 212 static ssize_t proc_read_simdisk(struct file *file, char __user *buf, 213 size_t size, loff_t *ppos) 214 { 215 struct simdisk *dev = PDE_DATA(file_inode(file)); 216 const char *s = dev->filename; 217 if (s) { 218 ssize_t n = simple_read_from_buffer(buf, size, ppos, 219 s, strlen(s)); 220 if (n < 0) 221 return n; 222 buf += n; 223 size -= n; 224 } 225 return simple_read_from_buffer(buf, size, ppos, "\n", 1); 226 } 227 228 static ssize_t proc_write_simdisk(struct file *file, const char __user *buf, 229 size_t count, loff_t *ppos) 230 { 231 char *tmp = memdup_user_nul(buf, count); 232 struct simdisk *dev = PDE_DATA(file_inode(file)); 233 int err; 234 235 if (IS_ERR(tmp)) 236 return PTR_ERR(tmp); 237 238 err = simdisk_detach(dev); 239 if (err != 0) 240 goto out_free; 241 242 if (count > 0 && tmp[count - 1] == '\n') 243 tmp[count - 1] = 0; 244 245 if (tmp[0]) 246 err = simdisk_attach(dev, tmp); 247 248 if (err == 0) 249 err = count; 250 out_free: 251 kfree(tmp); 252 return err; 253 } 254 255 static const struct file_operations fops = { 256 .read = proc_read_simdisk, 257 .write = proc_write_simdisk, 258 .llseek = default_llseek, 259 }; 260 261 static int __init simdisk_setup(struct simdisk *dev, int which, 262 struct proc_dir_entry *procdir) 263 { 264 char tmp[2] = { '0' + which, 0 }; 265 266 dev->fd = -1; 267 dev->filename = NULL; 268 spin_lock_init(&dev->lock); 269 dev->users = 0; 270 271 dev->queue = blk_alloc_queue(GFP_KERNEL); 272 if (dev->queue == NULL) { 273 pr_err("blk_alloc_queue failed\n"); 274 goto out_alloc_queue; 275 } 276 277 blk_queue_make_request(dev->queue, simdisk_make_request); 278 dev->queue->queuedata = dev; 279 280 dev->gd = alloc_disk(SIMDISK_MINORS); 281 if (dev->gd == NULL) { 282 pr_err("alloc_disk failed\n"); 283 goto out_alloc_disk; 284 } 285 dev->gd->major = simdisk_major; 286 dev->gd->first_minor = which; 287 dev->gd->fops = &simdisk_ops; 288 dev->gd->queue = dev->queue; 289 dev->gd->private_data = dev; 290 snprintf(dev->gd->disk_name, 32, "simdisk%d", which); 291 set_capacity(dev->gd, 0); 292 add_disk(dev->gd); 293 294 dev->procfile = proc_create_data(tmp, 0644, procdir, &fops, dev); 295 return 0; 296 297 out_alloc_disk: 298 blk_cleanup_queue(dev->queue); 299 dev->queue = NULL; 300 out_alloc_queue: 301 simc_close(dev->fd); 302 return -EIO; 303 } 304 305 static int __init simdisk_init(void) 306 { 307 int i; 308 309 if (register_blkdev(simdisk_major, "simdisk") < 0) { 310 pr_err("SIMDISK: register_blkdev: %d\n", simdisk_major); 311 return -EIO; 312 } 313 pr_info("SIMDISK: major: %d\n", simdisk_major); 314 315 if (n_files > simdisk_count) 316 simdisk_count = n_files; 317 if (simdisk_count > MAX_SIMDISK_COUNT) 318 simdisk_count = MAX_SIMDISK_COUNT; 319 320 sddev = kmalloc_array(simdisk_count, sizeof(*sddev), GFP_KERNEL); 321 if (sddev == NULL) 322 goto out_unregister; 323 324 simdisk_procdir = proc_mkdir("simdisk", 0); 325 if (simdisk_procdir == NULL) 326 goto out_free_unregister; 327 328 for (i = 0; i < simdisk_count; ++i) { 329 if (simdisk_setup(sddev + i, i, simdisk_procdir) == 0) { 330 if (filename[i] != NULL && filename[i][0] != 0 && 331 (n_files == 0 || i < n_files)) 332 simdisk_attach(sddev + i, filename[i]); 333 } 334 } 335 336 return 0; 337 338 out_free_unregister: 339 kfree(sddev); 340 out_unregister: 341 unregister_blkdev(simdisk_major, "simdisk"); 342 return -ENOMEM; 343 } 344 module_init(simdisk_init); 345 346 static void simdisk_teardown(struct simdisk *dev, int which, 347 struct proc_dir_entry *procdir) 348 { 349 char tmp[2] = { '0' + which, 0 }; 350 351 simdisk_detach(dev); 352 if (dev->gd) 353 del_gendisk(dev->gd); 354 if (dev->queue) 355 blk_cleanup_queue(dev->queue); 356 remove_proc_entry(tmp, procdir); 357 } 358 359 static void __exit simdisk_exit(void) 360 { 361 int i; 362 363 for (i = 0; i < simdisk_count; ++i) 364 simdisk_teardown(sddev + i, i, simdisk_procdir); 365 remove_proc_entry("simdisk", 0); 366 kfree(sddev); 367 unregister_blkdev(simdisk_major, "simdisk"); 368 } 369 module_exit(simdisk_exit); 370 371 MODULE_ALIAS_BLOCKDEV_MAJOR(SIMDISK_MAJOR); 372 373 MODULE_LICENSE("GPL"); 374