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 <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
  27struct 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
  39static int simdisk_count = CONFIG_BLK_DEV_SIMDISK_COUNT;
  40module_param(simdisk_count, int, S_IRUGO);
  41MODULE_PARM_DESC(simdisk_count, "Number of simdisk units.");
  42
  43static int n_files;
  44static 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
  53static 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
  63static const struct kernel_param_ops simdisk_param_ops_filename = {
  64        .set = simdisk_param_set_filename,
  65};
  66module_param_cb(filename, &simdisk_param_ops_filename, &n_files, 0);
  67MODULE_PARM_DESC(filename, "Backing storage filename.");
  68
  69static int simdisk_major = SIMDISK_MAJOR;
  70
  71static 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
 104static blk_qc_t simdisk_submit_bio(struct bio *bio)
 105{
 106        struct simdisk *dev = bio->bi_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
 125static 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
 135static 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
 143static 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
 150static struct simdisk *sddev;
 151static struct proc_dir_entry *simdisk_procdir;
 152
 153static 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);
 177out:
 178        if (err)
 179                kfree(filename);
 180        spin_unlock(&dev->lock);
 181
 182        return err;
 183}
 184
 185static 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
 210static 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
 226static 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;
 248out_free:
 249        kfree(tmp);
 250        return err;
 251}
 252
 253static 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
 259static 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
 292out_alloc_disk:
 293        blk_cleanup_queue(dev->queue);
 294        dev->queue = NULL;
 295out_alloc_queue:
 296        return -ENOMEM;
 297}
 298
 299static 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
 332out_free_unregister:
 333        kfree(sddev);
 334out_unregister:
 335        unregister_blkdev(simdisk_major, "simdisk");
 336        return -ENOMEM;
 337}
 338module_init(simdisk_init);
 339
 340static 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
 353static 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}
 363module_exit(simdisk_exit);
 364
 365MODULE_ALIAS_BLOCKDEV_MAJOR(SIMDISK_MAJOR);
 366
 367MODULE_LICENSE("GPL");
 368