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