aboutsummaryrefslogtreecommitdiffstats
path: root/arch/xtensa/platforms/iss
diff options
context:
space:
mode:
authorVictor Prupis <vnp@vnp_fc3.hq.tensilica.com>2008-05-19 17:50:38 -0400
committerChris Zankel <chris@zankel.net>2013-02-23 22:22:41 -0500
commitb6c7e873daf765e41233b9752083b66442703b7a (patch)
treef073ce1cf3f0ce32e4f4d177348d8aece9d8040b /arch/xtensa/platforms/iss
parentc5a285bb1b54e8b636cb522a9eb9c9ad232a23f8 (diff)
xtensa: ISS: add host file-based simulated disk
Simdisk is a block device that maps to a file in the host file system. It is usable for testing in the simulated environment, like xt-sim or QEMU. Device binding to host file may be changed at runtime via proc interface provided the device is not in use. Number of block devices and initial binding to host files is controlled via kernel/module parameters, with defaults specified in the kernel configuration. Signed-off-by: Victor Prupis <vnp@tensilica.com> Signed-off-by: Max Filippov <jcmvbkbc@gmail.com> Signed-off-by: Chris Zankel <chris@zankel.net>
Diffstat (limited to 'arch/xtensa/platforms/iss')
-rw-r--r--arch/xtensa/platforms/iss/Makefile1
-rw-r--r--arch/xtensa/platforms/iss/simdisk.c375
2 files changed, 376 insertions, 0 deletions
diff --git a/arch/xtensa/platforms/iss/Makefile b/arch/xtensa/platforms/iss/Makefile
index b7d1a5c0ff7f..d2369b799c50 100644
--- a/arch/xtensa/platforms/iss/Makefile
+++ b/arch/xtensa/platforms/iss/Makefile
@@ -6,3 +6,4 @@
6 6
7obj-y = console.o setup.o 7obj-y = console.o setup.o
8obj-$(CONFIG_NET) += network.o 8obj-$(CONFIG_NET) += network.o
9obj-$(CONFIG_BLK_DEV_SIMDISK) += simdisk.o
diff --git a/arch/xtensa/platforms/iss/simdisk.c b/arch/xtensa/platforms/iss/simdisk.c
new file mode 100644
index 000000000000..f58ffc3b68a8
--- /dev/null
+++ b/arch/xtensa/platforms/iss/simdisk.c
@@ -0,0 +1,375 @@
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(SYS_lseek, dev->fd, offset, SEEK_SET, 0, 0);
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 int i;
107 struct bio_vec *bvec;
108 sector_t sector = bio->bi_sector;
109
110 bio_for_each_segment(bvec, bio, i) {
111 char *buffer = __bio_kmap_atomic(bio, i, KM_USER0);
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(bio, KM_USER0);
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 int 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 return 0;
149}
150
151static const struct block_device_operations simdisk_ops = {
152 .owner = THIS_MODULE,
153 .open = simdisk_open,
154 .release = simdisk_release,
155};
156
157static struct simdisk *sddev;
158static struct proc_dir_entry *simdisk_procdir;
159
160static int simdisk_attach(struct simdisk *dev, const char *filename)
161{
162 int err = 0;
163
164 filename = kstrdup(filename, GFP_KERNEL);
165 if (filename == NULL)
166 return -ENOMEM;
167
168 spin_lock(&dev->lock);
169
170 if (dev->fd != -1) {
171 err = -EBUSY;
172 goto out;
173 }
174 dev->fd = simc_open(filename, O_RDWR, 0);
175 if (dev->fd == -1) {
176 pr_err("SIMDISK: Can't open %s: %d\n", filename, errno);
177 err = -ENODEV;
178 goto out;
179 }
180 dev->size = __simc(SYS_lseek, dev->fd, 0, SEEK_END, 0, 0);
181 set_capacity(dev->gd, dev->size >> SECTOR_SHIFT);
182 dev->filename = filename;
183 pr_info("SIMDISK: %s=%s\n", dev->gd->disk_name, dev->filename);
184out:
185 if (err)
186 kfree(filename);
187 spin_unlock(&dev->lock);
188
189 return err;
190}
191
192static int simdisk_detach(struct simdisk *dev)
193{
194 int err = 0;
195
196 spin_lock(&dev->lock);
197
198 if (dev->users != 0) {
199 err = -EBUSY;
200 } else if (dev->fd != -1) {
201 if (simc_close(dev->fd)) {
202 pr_err("SIMDISK: error closing %s: %d\n",
203 dev->filename, errno);
204 err = -EIO;
205 } else {
206 pr_info("SIMDISK: %s detached from %s\n",
207 dev->gd->disk_name, dev->filename);
208 dev->fd = -1;
209 kfree(dev->filename);
210 dev->filename = NULL;
211 }
212 }
213 spin_unlock(&dev->lock);
214 return err;
215}
216
217static int proc_read_simdisk(char *page, char **start, off_t off,
218 int count, int *eof, void *data)
219{
220 int len;
221 struct simdisk *dev = (struct simdisk *) data;
222 len = sprintf(page, "%s\n", dev->filename ? dev->filename : "");
223 return len;
224}
225
226static int proc_write_simdisk(struct file *file, const char *buffer,
227 unsigned long count, void *data)
228{
229 char *tmp = kmalloc(count + 1, GFP_KERNEL);
230 struct simdisk *dev = (struct simdisk *) data;
231 int err;
232
233 if (tmp == NULL)
234 return -ENOMEM;
235 if (copy_from_user(tmp, buffer, count)) {
236 err = -EFAULT;
237 goto out_free;
238 }
239
240 err = simdisk_detach(dev);
241 if (err != 0)
242 goto out_free;
243
244 if (count > 0 && tmp[count - 1] == '\n')
245 tmp[count - 1] = 0;
246 else
247 tmp[count] = 0;
248
249 if (tmp[0])
250 err = simdisk_attach(dev, tmp);
251
252 if (err == 0)
253 err = count;
254out_free:
255 kfree(tmp);
256 return err;
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(GFP_KERNEL);
270 if (dev->queue == NULL) {
271 pr_err("blk_alloc_queue failed\n");
272 goto out_alloc_queue;
273 }
274
275 blk_queue_make_request(dev->queue, simdisk_make_request);
276 dev->queue->queuedata = dev;
277
278 dev->gd = alloc_disk(SIMDISK_MINORS);
279 if (dev->gd == NULL) {
280 pr_err("alloc_disk failed\n");
281 goto out_alloc_disk;
282 }
283 dev->gd->major = simdisk_major;
284 dev->gd->first_minor = which;
285 dev->gd->fops = &simdisk_ops;
286 dev->gd->queue = dev->queue;
287 dev->gd->private_data = dev;
288 snprintf(dev->gd->disk_name, 32, "simdisk%d", which);
289 set_capacity(dev->gd, 0);
290 add_disk(dev->gd);
291
292 dev->procfile = create_proc_entry(tmp, 0644, procdir);
293 dev->procfile->data = dev;
294 dev->procfile->read_proc = proc_read_simdisk;
295 dev->procfile->write_proc = proc_write_simdisk;
296 return 0;
297
298out_alloc_disk:
299 blk_cleanup_queue(dev->queue);
300 dev->queue = NULL;
301out_alloc_queue:
302 simc_close(dev->fd);
303 return -EIO;
304}
305
306static int __init simdisk_init(void)
307{
308 int i;
309
310 if (register_blkdev(simdisk_major, "simdisk") < 0) {
311 pr_err("SIMDISK: register_blkdev: %d\n", simdisk_major);
312 return -EIO;
313 }
314 pr_info("SIMDISK: major: %d\n", simdisk_major);
315
316 if (n_files > simdisk_count)
317 simdisk_count = n_files;
318 if (simdisk_count > MAX_SIMDISK_COUNT)
319 simdisk_count = MAX_SIMDISK_COUNT;
320
321 sddev = kmalloc(simdisk_count * sizeof(struct simdisk),
322 GFP_KERNEL);
323 if (sddev == NULL)
324 goto out_unregister;
325
326 simdisk_procdir = proc_mkdir("simdisk", 0);
327 if (simdisk_procdir == NULL)
328 goto out_free_unregister;
329
330 for (i = 0; i < simdisk_count; ++i) {
331 if (simdisk_setup(sddev + i, i, simdisk_procdir) == 0) {
332 if (filename[i] != NULL && filename[i][0] != 0 &&
333 (n_files == 0 || i < n_files))
334 simdisk_attach(sddev + i, filename[i]);
335 }
336 }
337
338 return 0;
339
340out_free_unregister:
341 kfree(sddev);
342out_unregister:
343 unregister_blkdev(simdisk_major, "simdisk");
344 return -ENOMEM;
345}
346module_init(simdisk_init);
347
348static void simdisk_teardown(struct simdisk *dev, int which,
349 struct proc_dir_entry *procdir)
350{
351 char tmp[2] = { '0' + which, 0 };
352
353 simdisk_detach(dev);
354 if (dev->gd)
355 del_gendisk(dev->gd);
356 if (dev->queue)
357 blk_cleanup_queue(dev->queue);
358 remove_proc_entry(tmp, procdir);
359}
360
361static void __exit simdisk_exit(void)
362{
363 int i;
364
365 for (i = 0; i < simdisk_count; ++i)
366 simdisk_teardown(sddev + i, i, simdisk_procdir);
367 remove_proc_entry("simdisk", 0);
368 kfree(sddev);
369 unregister_blkdev(simdisk_major, "simdisk");
370}
371module_exit(simdisk_exit);
372
373MODULE_ALIAS_BLOCKDEV_MAJOR(SIMDISK_MAJOR);
374
375MODULE_LICENSE("GPL");