linux/arch/xtensa/platforms/iss/simdisk.c
<<
>>
Prefs
   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                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
 105static 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 = __bio_kmap_atomic(bio, iter);
 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                __bio_kunmap_atomic(buffer);
 120        }
 121
 122        bio_endio(bio);
 123        return BLK_QC_T_NONE;
 124}
 125
 126static 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
 138static 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
 146static const struct block_device_operations simdisk_ops = {
 147        .owner          = THIS_MODULE,
 148        .open           = simdisk_open,
 149        .release        = simdisk_release,
 150};
 151
 152static struct simdisk *sddev;
 153static struct proc_dir_entry *simdisk_procdir;
 154
 155static 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);
 179out:
 180        if (err)
 181                kfree(filename);
 182        spin_unlock(&dev->lock);
 183
 184        return err;
 185}
 186
 187static 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
 212static 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
 228static 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;
 250out_free:
 251        kfree(tmp);
 252        return err;
 253}
 254
 255static const struct file_operations fops = {
 256        .read = proc_read_simdisk,
 257        .write = proc_write_simdisk,
 258        .llseek = default_llseek,
 259};
 260
 261static 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
 297out_alloc_disk:
 298        blk_cleanup_queue(dev->queue);
 299        dev->queue = NULL;
 300out_alloc_queue:
 301        simc_close(dev->fd);
 302        return -EIO;
 303}
 304
 305static 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(simdisk_count * sizeof(struct simdisk),
 321                        GFP_KERNEL);
 322        if (sddev == NULL)
 323                goto out_unregister;
 324
 325        simdisk_procdir = proc_mkdir("simdisk", 0);
 326        if (simdisk_procdir == NULL)
 327                goto out_free_unregister;
 328
 329        for (i = 0; i < simdisk_count; ++i) {
 330                if (simdisk_setup(sddev + i, i, simdisk_procdir) == 0) {
 331                        if (filename[i] != NULL && filename[i][0] != 0 &&
 332                                        (n_files == 0 || i < n_files))
 333                                simdisk_attach(sddev + i, filename[i]);
 334                }
 335        }
 336
 337        return 0;
 338
 339out_free_unregister:
 340        kfree(sddev);
 341out_unregister:
 342        unregister_blkdev(simdisk_major, "simdisk");
 343        return -ENOMEM;
 344}
 345module_init(simdisk_init);
 346
 347static void simdisk_teardown(struct simdisk *dev, int which,
 348                struct proc_dir_entry *procdir)
 349{
 350        char tmp[2] = { '0' + which, 0 };
 351
 352        simdisk_detach(dev);
 353        if (dev->gd)
 354                del_gendisk(dev->gd);
 355        if (dev->queue)
 356                blk_cleanup_queue(dev->queue);
 357        remove_proc_entry(tmp, procdir);
 358}
 359
 360static void __exit simdisk_exit(void)
 361{
 362        int i;
 363
 364        for (i = 0; i < simdisk_count; ++i)
 365                simdisk_teardown(sddev + i, i, simdisk_procdir);
 366        remove_proc_entry("simdisk", 0);
 367        kfree(sddev);
 368        unregister_blkdev(simdisk_major, "simdisk");
 369}
 370module_exit(simdisk_exit);
 371
 372MODULE_ALIAS_BLOCKDEV_MAJOR(SIMDISK_MAJOR);
 373
 374MODULE_LICENSE("GPL");
 375