1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * arch/xtensa/platforms/iss/simdisk.c
3*4882a593Smuzhiyun *
4*4882a593Smuzhiyun * This file is subject to the terms and conditions of the GNU General Public
5*4882a593Smuzhiyun * License. See the file "COPYING" in the main directory of this archive
6*4882a593Smuzhiyun * for more details.
7*4882a593Smuzhiyun *
8*4882a593Smuzhiyun * Copyright (C) 2001-2013 Tensilica Inc.
9*4882a593Smuzhiyun * Authors Victor Prupis
10*4882a593Smuzhiyun */
11*4882a593Smuzhiyun
12*4882a593Smuzhiyun #include <linux/module.h>
13*4882a593Smuzhiyun #include <linux/moduleparam.h>
14*4882a593Smuzhiyun #include <linux/kernel.h>
15*4882a593Smuzhiyun #include <linux/init.h>
16*4882a593Smuzhiyun #include <linux/string.h>
17*4882a593Smuzhiyun #include <linux/blkdev.h>
18*4882a593Smuzhiyun #include <linux/bio.h>
19*4882a593Smuzhiyun #include <linux/proc_fs.h>
20*4882a593Smuzhiyun #include <linux/uaccess.h>
21*4882a593Smuzhiyun #include <platform/simcall.h>
22*4882a593Smuzhiyun
23*4882a593Smuzhiyun #define SIMDISK_MAJOR 240
24*4882a593Smuzhiyun #define SIMDISK_MINORS 1
25*4882a593Smuzhiyun #define MAX_SIMDISK_COUNT 10
26*4882a593Smuzhiyun
27*4882a593Smuzhiyun struct simdisk {
28*4882a593Smuzhiyun const char *filename;
29*4882a593Smuzhiyun spinlock_t lock;
30*4882a593Smuzhiyun struct request_queue *queue;
31*4882a593Smuzhiyun struct gendisk *gd;
32*4882a593Smuzhiyun struct proc_dir_entry *procfile;
33*4882a593Smuzhiyun int users;
34*4882a593Smuzhiyun unsigned long size;
35*4882a593Smuzhiyun int fd;
36*4882a593Smuzhiyun };
37*4882a593Smuzhiyun
38*4882a593Smuzhiyun
39*4882a593Smuzhiyun static int simdisk_count = CONFIG_BLK_DEV_SIMDISK_COUNT;
40*4882a593Smuzhiyun module_param(simdisk_count, int, S_IRUGO);
41*4882a593Smuzhiyun MODULE_PARM_DESC(simdisk_count, "Number of simdisk units.");
42*4882a593Smuzhiyun
43*4882a593Smuzhiyun static int n_files;
44*4882a593Smuzhiyun static const char *filename[MAX_SIMDISK_COUNT] = {
45*4882a593Smuzhiyun #ifdef CONFIG_SIMDISK0_FILENAME
46*4882a593Smuzhiyun CONFIG_SIMDISK0_FILENAME,
47*4882a593Smuzhiyun #ifdef CONFIG_SIMDISK1_FILENAME
48*4882a593Smuzhiyun CONFIG_SIMDISK1_FILENAME,
49*4882a593Smuzhiyun #endif
50*4882a593Smuzhiyun #endif
51*4882a593Smuzhiyun };
52*4882a593Smuzhiyun
simdisk_param_set_filename(const char * val,const struct kernel_param * kp)53*4882a593Smuzhiyun static int simdisk_param_set_filename(const char *val,
54*4882a593Smuzhiyun const struct kernel_param *kp)
55*4882a593Smuzhiyun {
56*4882a593Smuzhiyun if (n_files < ARRAY_SIZE(filename))
57*4882a593Smuzhiyun filename[n_files++] = val;
58*4882a593Smuzhiyun else
59*4882a593Smuzhiyun return -EINVAL;
60*4882a593Smuzhiyun return 0;
61*4882a593Smuzhiyun }
62*4882a593Smuzhiyun
63*4882a593Smuzhiyun static const struct kernel_param_ops simdisk_param_ops_filename = {
64*4882a593Smuzhiyun .set = simdisk_param_set_filename,
65*4882a593Smuzhiyun };
66*4882a593Smuzhiyun module_param_cb(filename, &simdisk_param_ops_filename, &n_files, 0);
67*4882a593Smuzhiyun MODULE_PARM_DESC(filename, "Backing storage filename.");
68*4882a593Smuzhiyun
69*4882a593Smuzhiyun static int simdisk_major = SIMDISK_MAJOR;
70*4882a593Smuzhiyun
simdisk_transfer(struct simdisk * dev,unsigned long sector,unsigned long nsect,char * buffer,int write)71*4882a593Smuzhiyun static void simdisk_transfer(struct simdisk *dev, unsigned long sector,
72*4882a593Smuzhiyun unsigned long nsect, char *buffer, int write)
73*4882a593Smuzhiyun {
74*4882a593Smuzhiyun unsigned long offset = sector << SECTOR_SHIFT;
75*4882a593Smuzhiyun unsigned long nbytes = nsect << SECTOR_SHIFT;
76*4882a593Smuzhiyun
77*4882a593Smuzhiyun if (offset > dev->size || dev->size - offset < nbytes) {
78*4882a593Smuzhiyun pr_notice("Beyond-end %s (%ld %ld)\n",
79*4882a593Smuzhiyun write ? "write" : "read", offset, nbytes);
80*4882a593Smuzhiyun return;
81*4882a593Smuzhiyun }
82*4882a593Smuzhiyun
83*4882a593Smuzhiyun spin_lock(&dev->lock);
84*4882a593Smuzhiyun while (nbytes > 0) {
85*4882a593Smuzhiyun unsigned long io;
86*4882a593Smuzhiyun
87*4882a593Smuzhiyun simc_lseek(dev->fd, offset, SEEK_SET);
88*4882a593Smuzhiyun READ_ONCE(*buffer);
89*4882a593Smuzhiyun if (write)
90*4882a593Smuzhiyun io = simc_write(dev->fd, buffer, nbytes);
91*4882a593Smuzhiyun else
92*4882a593Smuzhiyun io = simc_read(dev->fd, buffer, nbytes);
93*4882a593Smuzhiyun if (io == -1) {
94*4882a593Smuzhiyun pr_err("SIMDISK: IO error %d\n", errno);
95*4882a593Smuzhiyun break;
96*4882a593Smuzhiyun }
97*4882a593Smuzhiyun buffer += io;
98*4882a593Smuzhiyun offset += io;
99*4882a593Smuzhiyun nbytes -= io;
100*4882a593Smuzhiyun }
101*4882a593Smuzhiyun spin_unlock(&dev->lock);
102*4882a593Smuzhiyun }
103*4882a593Smuzhiyun
simdisk_submit_bio(struct bio * bio)104*4882a593Smuzhiyun static blk_qc_t simdisk_submit_bio(struct bio *bio)
105*4882a593Smuzhiyun {
106*4882a593Smuzhiyun struct simdisk *dev = bio->bi_disk->private_data;
107*4882a593Smuzhiyun struct bio_vec bvec;
108*4882a593Smuzhiyun struct bvec_iter iter;
109*4882a593Smuzhiyun sector_t sector = bio->bi_iter.bi_sector;
110*4882a593Smuzhiyun
111*4882a593Smuzhiyun bio_for_each_segment(bvec, bio, iter) {
112*4882a593Smuzhiyun char *buffer = kmap_atomic(bvec.bv_page) + bvec.bv_offset;
113*4882a593Smuzhiyun unsigned len = bvec.bv_len >> SECTOR_SHIFT;
114*4882a593Smuzhiyun
115*4882a593Smuzhiyun simdisk_transfer(dev, sector, len, buffer,
116*4882a593Smuzhiyun bio_data_dir(bio) == WRITE);
117*4882a593Smuzhiyun sector += len;
118*4882a593Smuzhiyun kunmap_atomic(buffer);
119*4882a593Smuzhiyun }
120*4882a593Smuzhiyun
121*4882a593Smuzhiyun bio_endio(bio);
122*4882a593Smuzhiyun return BLK_QC_T_NONE;
123*4882a593Smuzhiyun }
124*4882a593Smuzhiyun
simdisk_open(struct block_device * bdev,fmode_t mode)125*4882a593Smuzhiyun static int simdisk_open(struct block_device *bdev, fmode_t mode)
126*4882a593Smuzhiyun {
127*4882a593Smuzhiyun struct simdisk *dev = bdev->bd_disk->private_data;
128*4882a593Smuzhiyun
129*4882a593Smuzhiyun spin_lock(&dev->lock);
130*4882a593Smuzhiyun ++dev->users;
131*4882a593Smuzhiyun spin_unlock(&dev->lock);
132*4882a593Smuzhiyun return 0;
133*4882a593Smuzhiyun }
134*4882a593Smuzhiyun
simdisk_release(struct gendisk * disk,fmode_t mode)135*4882a593Smuzhiyun static void simdisk_release(struct gendisk *disk, fmode_t mode)
136*4882a593Smuzhiyun {
137*4882a593Smuzhiyun struct simdisk *dev = disk->private_data;
138*4882a593Smuzhiyun spin_lock(&dev->lock);
139*4882a593Smuzhiyun --dev->users;
140*4882a593Smuzhiyun spin_unlock(&dev->lock);
141*4882a593Smuzhiyun }
142*4882a593Smuzhiyun
143*4882a593Smuzhiyun static const struct block_device_operations simdisk_ops = {
144*4882a593Smuzhiyun .owner = THIS_MODULE,
145*4882a593Smuzhiyun .submit_bio = simdisk_submit_bio,
146*4882a593Smuzhiyun .open = simdisk_open,
147*4882a593Smuzhiyun .release = simdisk_release,
148*4882a593Smuzhiyun };
149*4882a593Smuzhiyun
150*4882a593Smuzhiyun static struct simdisk *sddev;
151*4882a593Smuzhiyun static struct proc_dir_entry *simdisk_procdir;
152*4882a593Smuzhiyun
simdisk_attach(struct simdisk * dev,const char * filename)153*4882a593Smuzhiyun static int simdisk_attach(struct simdisk *dev, const char *filename)
154*4882a593Smuzhiyun {
155*4882a593Smuzhiyun int err = 0;
156*4882a593Smuzhiyun
157*4882a593Smuzhiyun filename = kstrdup(filename, GFP_KERNEL);
158*4882a593Smuzhiyun if (filename == NULL)
159*4882a593Smuzhiyun return -ENOMEM;
160*4882a593Smuzhiyun
161*4882a593Smuzhiyun spin_lock(&dev->lock);
162*4882a593Smuzhiyun
163*4882a593Smuzhiyun if (dev->fd != -1) {
164*4882a593Smuzhiyun err = -EBUSY;
165*4882a593Smuzhiyun goto out;
166*4882a593Smuzhiyun }
167*4882a593Smuzhiyun dev->fd = simc_open(filename, O_RDWR, 0);
168*4882a593Smuzhiyun if (dev->fd == -1) {
169*4882a593Smuzhiyun pr_err("SIMDISK: Can't open %s: %d\n", filename, errno);
170*4882a593Smuzhiyun err = -ENODEV;
171*4882a593Smuzhiyun goto out;
172*4882a593Smuzhiyun }
173*4882a593Smuzhiyun dev->size = simc_lseek(dev->fd, 0, SEEK_END);
174*4882a593Smuzhiyun set_capacity(dev->gd, dev->size >> SECTOR_SHIFT);
175*4882a593Smuzhiyun dev->filename = filename;
176*4882a593Smuzhiyun pr_info("SIMDISK: %s=%s\n", dev->gd->disk_name, dev->filename);
177*4882a593Smuzhiyun out:
178*4882a593Smuzhiyun if (err)
179*4882a593Smuzhiyun kfree(filename);
180*4882a593Smuzhiyun spin_unlock(&dev->lock);
181*4882a593Smuzhiyun
182*4882a593Smuzhiyun return err;
183*4882a593Smuzhiyun }
184*4882a593Smuzhiyun
simdisk_detach(struct simdisk * dev)185*4882a593Smuzhiyun static int simdisk_detach(struct simdisk *dev)
186*4882a593Smuzhiyun {
187*4882a593Smuzhiyun int err = 0;
188*4882a593Smuzhiyun
189*4882a593Smuzhiyun spin_lock(&dev->lock);
190*4882a593Smuzhiyun
191*4882a593Smuzhiyun if (dev->users != 0) {
192*4882a593Smuzhiyun err = -EBUSY;
193*4882a593Smuzhiyun } else if (dev->fd != -1) {
194*4882a593Smuzhiyun if (simc_close(dev->fd)) {
195*4882a593Smuzhiyun pr_err("SIMDISK: error closing %s: %d\n",
196*4882a593Smuzhiyun dev->filename, errno);
197*4882a593Smuzhiyun err = -EIO;
198*4882a593Smuzhiyun } else {
199*4882a593Smuzhiyun pr_info("SIMDISK: %s detached from %s\n",
200*4882a593Smuzhiyun dev->gd->disk_name, dev->filename);
201*4882a593Smuzhiyun dev->fd = -1;
202*4882a593Smuzhiyun kfree(dev->filename);
203*4882a593Smuzhiyun dev->filename = NULL;
204*4882a593Smuzhiyun }
205*4882a593Smuzhiyun }
206*4882a593Smuzhiyun spin_unlock(&dev->lock);
207*4882a593Smuzhiyun return err;
208*4882a593Smuzhiyun }
209*4882a593Smuzhiyun
proc_read_simdisk(struct file * file,char __user * buf,size_t size,loff_t * ppos)210*4882a593Smuzhiyun static ssize_t proc_read_simdisk(struct file *file, char __user *buf,
211*4882a593Smuzhiyun size_t size, loff_t *ppos)
212*4882a593Smuzhiyun {
213*4882a593Smuzhiyun struct simdisk *dev = PDE_DATA(file_inode(file));
214*4882a593Smuzhiyun const char *s = dev->filename;
215*4882a593Smuzhiyun if (s) {
216*4882a593Smuzhiyun ssize_t len = strlen(s);
217*4882a593Smuzhiyun char *temp = kmalloc(len + 2, GFP_KERNEL);
218*4882a593Smuzhiyun
219*4882a593Smuzhiyun if (!temp)
220*4882a593Smuzhiyun return -ENOMEM;
221*4882a593Smuzhiyun
222*4882a593Smuzhiyun len = scnprintf(temp, len + 2, "%s\n", s);
223*4882a593Smuzhiyun len = simple_read_from_buffer(buf, size, ppos,
224*4882a593Smuzhiyun temp, len);
225*4882a593Smuzhiyun
226*4882a593Smuzhiyun kfree(temp);
227*4882a593Smuzhiyun return len;
228*4882a593Smuzhiyun }
229*4882a593Smuzhiyun return simple_read_from_buffer(buf, size, ppos, "\n", 1);
230*4882a593Smuzhiyun }
231*4882a593Smuzhiyun
proc_write_simdisk(struct file * file,const char __user * buf,size_t count,loff_t * ppos)232*4882a593Smuzhiyun static ssize_t proc_write_simdisk(struct file *file, const char __user *buf,
233*4882a593Smuzhiyun size_t count, loff_t *ppos)
234*4882a593Smuzhiyun {
235*4882a593Smuzhiyun char *tmp = memdup_user_nul(buf, count);
236*4882a593Smuzhiyun struct simdisk *dev = PDE_DATA(file_inode(file));
237*4882a593Smuzhiyun int err;
238*4882a593Smuzhiyun
239*4882a593Smuzhiyun if (IS_ERR(tmp))
240*4882a593Smuzhiyun return PTR_ERR(tmp);
241*4882a593Smuzhiyun
242*4882a593Smuzhiyun err = simdisk_detach(dev);
243*4882a593Smuzhiyun if (err != 0)
244*4882a593Smuzhiyun goto out_free;
245*4882a593Smuzhiyun
246*4882a593Smuzhiyun if (count > 0 && tmp[count - 1] == '\n')
247*4882a593Smuzhiyun tmp[count - 1] = 0;
248*4882a593Smuzhiyun
249*4882a593Smuzhiyun if (tmp[0])
250*4882a593Smuzhiyun err = simdisk_attach(dev, tmp);
251*4882a593Smuzhiyun
252*4882a593Smuzhiyun if (err == 0)
253*4882a593Smuzhiyun err = count;
254*4882a593Smuzhiyun out_free:
255*4882a593Smuzhiyun kfree(tmp);
256*4882a593Smuzhiyun return err;
257*4882a593Smuzhiyun }
258*4882a593Smuzhiyun
259*4882a593Smuzhiyun static const struct proc_ops simdisk_proc_ops = {
260*4882a593Smuzhiyun .proc_read = proc_read_simdisk,
261*4882a593Smuzhiyun .proc_write = proc_write_simdisk,
262*4882a593Smuzhiyun .proc_lseek = default_llseek,
263*4882a593Smuzhiyun };
264*4882a593Smuzhiyun
simdisk_setup(struct simdisk * dev,int which,struct proc_dir_entry * procdir)265*4882a593Smuzhiyun static int __init simdisk_setup(struct simdisk *dev, int which,
266*4882a593Smuzhiyun struct proc_dir_entry *procdir)
267*4882a593Smuzhiyun {
268*4882a593Smuzhiyun char tmp[2] = { '0' + which, 0 };
269*4882a593Smuzhiyun
270*4882a593Smuzhiyun dev->fd = -1;
271*4882a593Smuzhiyun dev->filename = NULL;
272*4882a593Smuzhiyun spin_lock_init(&dev->lock);
273*4882a593Smuzhiyun dev->users = 0;
274*4882a593Smuzhiyun
275*4882a593Smuzhiyun dev->queue = blk_alloc_queue(NUMA_NO_NODE);
276*4882a593Smuzhiyun if (dev->queue == NULL) {
277*4882a593Smuzhiyun pr_err("blk_alloc_queue failed\n");
278*4882a593Smuzhiyun goto out_alloc_queue;
279*4882a593Smuzhiyun }
280*4882a593Smuzhiyun
281*4882a593Smuzhiyun dev->gd = alloc_disk(SIMDISK_MINORS);
282*4882a593Smuzhiyun if (dev->gd == NULL) {
283*4882a593Smuzhiyun pr_err("alloc_disk failed\n");
284*4882a593Smuzhiyun goto out_alloc_disk;
285*4882a593Smuzhiyun }
286*4882a593Smuzhiyun dev->gd->major = simdisk_major;
287*4882a593Smuzhiyun dev->gd->first_minor = which;
288*4882a593Smuzhiyun dev->gd->fops = &simdisk_ops;
289*4882a593Smuzhiyun dev->gd->queue = dev->queue;
290*4882a593Smuzhiyun dev->gd->private_data = dev;
291*4882a593Smuzhiyun snprintf(dev->gd->disk_name, 32, "simdisk%d", which);
292*4882a593Smuzhiyun set_capacity(dev->gd, 0);
293*4882a593Smuzhiyun add_disk(dev->gd);
294*4882a593Smuzhiyun
295*4882a593Smuzhiyun dev->procfile = proc_create_data(tmp, 0644, procdir, &simdisk_proc_ops, dev);
296*4882a593Smuzhiyun return 0;
297*4882a593Smuzhiyun
298*4882a593Smuzhiyun out_alloc_disk:
299*4882a593Smuzhiyun blk_cleanup_queue(dev->queue);
300*4882a593Smuzhiyun dev->queue = NULL;
301*4882a593Smuzhiyun out_alloc_queue:
302*4882a593Smuzhiyun return -ENOMEM;
303*4882a593Smuzhiyun }
304*4882a593Smuzhiyun
simdisk_init(void)305*4882a593Smuzhiyun static int __init simdisk_init(void)
306*4882a593Smuzhiyun {
307*4882a593Smuzhiyun int i;
308*4882a593Smuzhiyun
309*4882a593Smuzhiyun if (register_blkdev(simdisk_major, "simdisk") < 0) {
310*4882a593Smuzhiyun pr_err("SIMDISK: register_blkdev: %d\n", simdisk_major);
311*4882a593Smuzhiyun return -EIO;
312*4882a593Smuzhiyun }
313*4882a593Smuzhiyun pr_info("SIMDISK: major: %d\n", simdisk_major);
314*4882a593Smuzhiyun
315*4882a593Smuzhiyun if (n_files > simdisk_count)
316*4882a593Smuzhiyun simdisk_count = n_files;
317*4882a593Smuzhiyun if (simdisk_count > MAX_SIMDISK_COUNT)
318*4882a593Smuzhiyun simdisk_count = MAX_SIMDISK_COUNT;
319*4882a593Smuzhiyun
320*4882a593Smuzhiyun sddev = kmalloc_array(simdisk_count, sizeof(*sddev), GFP_KERNEL);
321*4882a593Smuzhiyun if (sddev == NULL)
322*4882a593Smuzhiyun goto out_unregister;
323*4882a593Smuzhiyun
324*4882a593Smuzhiyun simdisk_procdir = proc_mkdir("simdisk", 0);
325*4882a593Smuzhiyun if (simdisk_procdir == NULL)
326*4882a593Smuzhiyun goto out_free_unregister;
327*4882a593Smuzhiyun
328*4882a593Smuzhiyun for (i = 0; i < simdisk_count; ++i) {
329*4882a593Smuzhiyun if (simdisk_setup(sddev + i, i, simdisk_procdir) == 0) {
330*4882a593Smuzhiyun if (filename[i] != NULL && filename[i][0] != 0 &&
331*4882a593Smuzhiyun (n_files == 0 || i < n_files))
332*4882a593Smuzhiyun simdisk_attach(sddev + i, filename[i]);
333*4882a593Smuzhiyun }
334*4882a593Smuzhiyun }
335*4882a593Smuzhiyun
336*4882a593Smuzhiyun return 0;
337*4882a593Smuzhiyun
338*4882a593Smuzhiyun out_free_unregister:
339*4882a593Smuzhiyun kfree(sddev);
340*4882a593Smuzhiyun out_unregister:
341*4882a593Smuzhiyun unregister_blkdev(simdisk_major, "simdisk");
342*4882a593Smuzhiyun return -ENOMEM;
343*4882a593Smuzhiyun }
344*4882a593Smuzhiyun module_init(simdisk_init);
345*4882a593Smuzhiyun
simdisk_teardown(struct simdisk * dev,int which,struct proc_dir_entry * procdir)346*4882a593Smuzhiyun static void simdisk_teardown(struct simdisk *dev, int which,
347*4882a593Smuzhiyun struct proc_dir_entry *procdir)
348*4882a593Smuzhiyun {
349*4882a593Smuzhiyun char tmp[2] = { '0' + which, 0 };
350*4882a593Smuzhiyun
351*4882a593Smuzhiyun simdisk_detach(dev);
352*4882a593Smuzhiyun if (dev->gd)
353*4882a593Smuzhiyun del_gendisk(dev->gd);
354*4882a593Smuzhiyun if (dev->queue)
355*4882a593Smuzhiyun blk_cleanup_queue(dev->queue);
356*4882a593Smuzhiyun remove_proc_entry(tmp, procdir);
357*4882a593Smuzhiyun }
358*4882a593Smuzhiyun
simdisk_exit(void)359*4882a593Smuzhiyun static void __exit simdisk_exit(void)
360*4882a593Smuzhiyun {
361*4882a593Smuzhiyun int i;
362*4882a593Smuzhiyun
363*4882a593Smuzhiyun for (i = 0; i < simdisk_count; ++i)
364*4882a593Smuzhiyun simdisk_teardown(sddev + i, i, simdisk_procdir);
365*4882a593Smuzhiyun remove_proc_entry("simdisk", 0);
366*4882a593Smuzhiyun kfree(sddev);
367*4882a593Smuzhiyun unregister_blkdev(simdisk_major, "simdisk");
368*4882a593Smuzhiyun }
369*4882a593Smuzhiyun module_exit(simdisk_exit);
370*4882a593Smuzhiyun
371*4882a593Smuzhiyun MODULE_ALIAS_BLOCKDEV_MAJOR(SIMDISK_MAJOR);
372*4882a593Smuzhiyun
373*4882a593Smuzhiyun MODULE_LICENSE("GPL");
374