1 /*
2 * Sample disk driver, from the beginning.
3 */
4
5 #include <linux/module.h>
6 #include <linux/moduleparam.h>
7 #include <linux/init.h>
8
9 #include <linux/sched.h>
10 #include <linux/kernel.h> /* printk() */
11 #include <linux/slab.h> /* kmalloc() */
12 #include <linux/fs.h> /* everything... */
13 #include <linux/errno.h> /* error codes */
14 #include <linux/timer.h>
15 #include <linux/types.h> /* size_t */
16 #include <linux/fcntl.h> /* O_ACCMODE */
17 #include <linux/hdreg.h> /* HDIO_GETGEO */
18 #include <linux/kdev_t.h>
19 #include <linux/vmalloc.h>
20 #include <linux/genhd.h>
21 #include <linux/blkdev.h>
22 #include <linux/buffer_head.h> /* invalidate_bdev */
23 #include <linux/bio.h>
24
25 MODULE_LICENSE("Dual BSD/GPL");
26
27 static int sbull_major = 0;
28 module_param(sbull_major, int, 0);
29 static int hardsect_size = 512;
30 module_param(hardsect_size, int, 0);
31 static int nsectors = 1024; /* How big the drive is */
32 module_param(nsectors, int, 0);
33 static int ndevices = 4;
34 module_param(ndevices, int, 0);
35
36 /*
37 * The different "request modes" we can use.
38 */
39 enum {
40 RM_SIMPLE = 0, /* The extra-simple request function */
41 RM_FULL = 1, /* The full-blown version */
42 RM_NOQUEUE = 2, /* Use make_request */
43 };
44 static int request_mode = RM_SIMPLE;
45 module_param(request_mode, int, 0);
46
47 /*
48 * Minor number and partition management.
49 */
50 #define SBULL_MINORS 16
51 #define MINOR_SHIFT 4
52 #define DEVNUM(kdevnum) (MINOR(kdev_t_to_nr(kdevnum)) >> MINOR_SHIFT
53
54 /*
55 * We can tweak our hardware sector size, but the kernel talks to us
56 * in terms of small sectors, always.
57 */
58 #define KERNEL_SECTOR_SIZE 512
59
60 /*
61 * After this much idle time, the driver will simulate a media change.
62 */
63 #define INVALIDATE_DELAY 30*HZ
64
65 /*
66 * The internal representation of our device.
67 */
68 struct sbull_dev {
69 int size; /* Device size in sectors */
70 u8 *data; /* The data array */
71 short users; /* How many users */
72 short media_change; /* Flag a media change? */
73 spinlock_t lock; /* For mutual exclusion */
74 struct request_queue *queue; /* The device request queue */
75 struct gendisk *gd; /* The gendisk structure */
76 struct timer_list timer; /* For simulated media changes */
77 };
78
79 static struct sbull_dev *Devices = NULL;
80
81 /*
82 * Handle an I/O request.
83 */
84 static void sbull_transfer(struct sbull_dev *dev, unsigned long sector,
85 unsigned long nsect, char *buffer, int write)
86 {
87 unsigned long offset = sector*KERNEL_SECTOR_SIZE;
88 unsigned long nbytes = nsect*KERNEL_SECTOR_SIZE;
89
90 if ((offset + nbytes) > dev->size) {
91 printk (KERN_NOTICE "Beyond-end write (%ld %ld)\n", offset, nbytes);
92 return;
93 }
94 if (write)
95 memcpy(dev->data + offset, buffer, nbytes);
96 else
97 memcpy(buffer, dev->data + offset, nbytes);
98 }
99
100 /*
101 * The simple form of the request function.
102 */
103 static void sbull_request(struct request_queue *q)
104 {
105 struct request *req;
106
107 while ((req = elv_next_request(q)) != NULL) {
108 struct sbull_dev *dev = req->rq_disk->private_data;
109 if (! blk_fs_request(req)) {
110 printk (KERN_NOTICE "Skip non-fs request\n");
111 blk_end_request(req, -EIO, req->current_nr_sectors << 9);
112 continue;
113 }
114 // printk (KERN_NOTICE "Req dev %d dir %ld sec %ld, nr %d f %lx\n",
115 // dev - Devices, rq_data_dir(req),
116 // req->sector, req->current_nr_sectors,
117 // req->flags);
118 sbull_transfer(dev, req->sector, req->current_nr_sectors,
119 req->buffer, rq_data_dir(req));
120 blk_end_request(req, 1, req->current_nr_sectors << 9);
121 }
122 }
123
124
125 /*
126 * Transfer a single BIO.
127 */
128 static int sbull_xfer_bio(struct sbull_dev *dev, struct bio *bio)
129 {
130 int i;
131 struct bio_vec *bvec;
132 sector_t sector = bio->bi_sector;
133
134 /* Do each segment independently. */
135 bio_for_each_segment(bvec, bio, i) {
136 char *buffer = __bio_kmap_atomic(bio, i, KM_USER0);
137 sbull_transfer(dev, sector, bio_cur_sectors(bio),
138 buffer, bio_data_dir(bio) == WRITE);
139 sector += bio_cur_sectors(bio);
140 __bio_kunmap_atomic(bio, KM_USER0);
141 }
142 return 0; /* Always "succeed" */
143 }
144
145 /*
146 * Transfer a full request.
147 */
148 static int sbull_xfer_request(struct sbull_dev *dev, struct request *req)
149 {
150
151 struct req_iterator iter;
152 int nsect = 0;
153 struct bio_vec *bvec;
154
155 /* Macro rq_for_each_bio is gone.
156 * In most cases one should use rq_for_each_segment.
157 */
158 rq_for_each_segment(bvec, req, iter) {
159 char *buffer = __bio_kmap_atomic(iter.bio, iter.i, KM_USER0);
160 sector_t sector = iter.bio->bi_sector;
161 sbull_transfer(dev, sector, bio_cur_sectors(iter.bio),
162 buffer, bio_data_dir(iter.bio) == WRITE);
163 sector += bio_cur_sectors(iter.bio);
164 __bio_kunmap_atomic(iter.bio, KM_USER0);
165 nsect += iter.bio->bi_size/KERNEL_SECTOR_SIZE;
166 }
167 return nsect;
168 }
169
170
171 /*
172 * Smarter request function that "handles clustering".
173 */
174 static void sbull_full_request(struct request_queue *q)
175 {
176 struct request *req;
177 int sectors_xferred;
178 struct sbull_dev *dev = q->queuedata;
179
180 while ((req = elv_next_request(q)) != NULL) {
181 if (! blk_fs_request(req)) {
182 printk (KERN_NOTICE "Skip non-fs request\n");
183 end_request(req, 0);
184 continue;
185 }
186 sectors_xferred = sbull_xfer_request(dev, req);
187 __blk_end_request (req, 1, sectors_xferred << 9);
188 /* The above includes a call to add_disk_randomness(). */
189 }
190 }
191
192 /*
193 * The direct make request version.
194 */
195 static int sbull_make_request(struct request_queue *q, struct bio *bio)
196 {
197 struct sbull_dev *dev = q->queuedata;
198 int status;
199
200 status = sbull_xfer_bio(dev, bio);
201 bio_endio(bio, status);
202 return 0;
203 }
204
205
206 /*
207 * Open and close.
208 */
209
210 static int sbull_open(struct inode *inode, struct file *filp)
211 {
212 struct sbull_dev *dev = inode->i_bdev->bd_disk->private_data;
213
214 del_timer_sync(&dev->timer);
215 filp->private_data = dev;
216 spin_lock(&dev->lock);
217 if (! dev->users)
218 check_disk_change(inode->i_bdev);
219 dev->users++;
220 spin_unlock(&dev->lock);
221 return 0;
222 }
223
224 static int sbull_release(struct inode *inode, struct file *filp)
225 {
226 struct sbull_dev *dev = inode->i_bdev->bd_disk->private_data;
227
228 spin_lock(&dev->lock);
229 dev->users--;
230
231 if (!dev->users) {
232 dev->timer.expires = jiffies + INVALIDATE_DELAY;
233 add_timer(&dev->timer);
234 }
235 spin_unlock(&dev->lock);
236
237 return 0;
238 }
239
240 /*
241 * Look for a (simulated) media change.
242 */
243 int sbull_media_changed(struct gendisk *gd)
244 {
245 struct sbull_dev *dev = gd->private_data;
246
247 return dev->media_change;
248 }
249
250 /*
251 * Revalidate. WE DO NOT TAKE THE LOCK HERE, for fear of deadlocking
252 * with open. That needs to be reevaluated.
253 */
254 int sbull_revalidate(struct gendisk *gd)
255 {
256 struct sbull_dev *dev = gd->private_data;
257
258 if (dev->media_change) {
259 dev->media_change = 0;
260 memset (dev->data, 0, dev->size);
261 }
262 return 0;
263 }
264
265 /*
266 * The "invalidate" function runs out of the device timer; it sets
267 * a flag to simulate the removal of the media.
268 */
269 void sbull_invalidate(unsigned long ldev)
270 {
271 struct sbull_dev *dev = (struct sbull_dev *) ldev;
272
273 spin_lock(&dev->lock);
274 if (dev->users || !dev->data)
275 printk (KERN_WARNING "sbull: timer sanity check failed\n");
276 else
277 dev->media_change = 1;
278 spin_unlock(&dev->lock);
279 }
280
281 /*
282 * The ioctl() implementation
283 */
284
285 int sbull_ioctl (struct inode *inode, struct file *filp,
286 unsigned int cmd, unsigned long arg)
287 {
288 long size;
289 struct hd_geometry geo;
290 struct sbull_dev *dev = filp->private_data;
291
292 switch(cmd) {
293 case HDIO_GETGEO:
294 /*
295 * Get geometry: since we are a virtual device, we have to make
296 * up something plausible. So we claim 16 sectors, four heads,
297 * and calculate the corresponding number of cylinders. We set the
298 * start of data at sector four.
299 */
300 size = dev->size*(hardsect_size/KERNEL_SECTOR_SIZE);
301 geo.cylinders = (size & ~0x3f) >> 6;
302 geo.heads = 4;
303 geo.sectors = 16;
304 geo.start = 4;
305 if (copy_to_user((void __user *) arg, &geo, sizeof(geo)))
306 return -EFAULT;
307 return 0;
308 }
309
310 return -ENOTTY; /* unknown command */
311 }
312
313
314
315 /*
316 * The device operations structure.
317 */
318 static struct block_device_operations sbull_ops = {
319 .owner = THIS_MODULE,
320 .open = sbull_open,
321 .release = sbull_release,
322 .media_changed = sbull_media_changed,
323 .revalidate_disk = sbull_revalidate,
324 .ioctl = sbull_ioctl
325 };
326
327
328 /*
329 * Set up our internal device.
330 */
331 static void setup_device(struct sbull_dev *dev, int which)
332 {
333 /*
334 * Get some memory.
335 */
336 memset (dev, 0, sizeof (struct sbull_dev));
337 dev->size = nsectors*hardsect_size;
338 dev->data = vmalloc(dev->size);
339 if (dev->data == NULL) {
340 printk (KERN_NOTICE "vmalloc failure.\n");
341 return;
342 }
343 spin_lock_init(&dev->lock);
344
345 /*
346 * The timer which "invalidates" the device.
347 */
348 init_timer(&dev->timer);
349 dev->timer.data = (unsigned long) dev;
350 dev->timer.function = sbull_invalidate;
351
352 /*
353 * The I/O queue, depending on whether we are using our own
354 * make_request function or not.
355 */
356 switch (request_mode) {
357 case RM_NOQUEUE:
358 dev->queue = blk_alloc_queue(GFP_KERNEL);
359 if (dev->queue == NULL)
360 goto out_vfree;
361 blk_queue_make_request(dev->queue, sbull_make_request);
362 break;
363
364 case RM_FULL:
365 dev->queue = blk_init_queue(sbull_full_request, &dev->lock);
366 if (dev->queue == NULL)
367 goto out_vfree;
368 break;
369
370 default:
371 printk(KERN_NOTICE "Bad request mode %d, using simple\n", request_mode);
372 /* fall into.. */
373
374 case RM_SIMPLE:
375 dev->queue = blk_init_queue(sbull_request, &dev->lock);
376 if (dev->queue == NULL)
377 goto out_vfree;
378 break;
379 }
380 blk_queue_hardsect_size(dev->queue, hardsect_size);
381 dev->queue->queuedata = dev;
382 /*
383 * And the gendisk structure.
384 */
385 dev->gd = alloc_disk(SBULL_MINORS);
386 if (! dev->gd) {
387 printk (KERN_NOTICE "alloc_disk failure\n");
388 goto out_vfree;
389 }
390 dev->gd->major = sbull_major;
391 dev->gd->first_minor = which*SBULL_MINORS;
392 dev->gd->fops = &sbull_ops;
393 dev->gd->queue = dev->queue;
394 dev->gd->private_data = dev;
395 snprintf (dev->gd->disk_name, 32, "sbull%c", which + 'a');
396 set_capacity(dev->gd, nsectors*(hardsect_size/KERNEL_SECTOR_SIZE));
397 add_disk(dev->gd);
398 return;
399
400 out_vfree:
401 if (dev->data)
402 vfree(dev->data);
403 }
404
405
406
407 static int __init sbull_init(void)
408 {
409 int i;
410 /*
411 * Get registered.
412 */
413 sbull_major = register_blkdev(sbull_major, "sbull");
414 if (sbull_major <= 0) {
415 printk(KERN_WARNING "sbull: unable to get major number\n");
416 return -EBUSY;
417 }
418 /*
419 * Allocate the device array, and initialize each one.
420 */
421 Devices = kmalloc(ndevices*sizeof (struct sbull_dev), GFP_KERNEL);
422 if (Devices == NULL)
423 goto out_unregister;
424 for (i = 0; i < ndevices; i++)
425 setup_device(Devices + i, i);
426
427 return 0;
428
429 out_unregister:
430 unregister_blkdev(sbull_major, "sbd");
431 return -ENOMEM;
432 }
433
434 static void sbull_exit(void)
435 {
436 int i;
437
438 for (i = 0; i < ndevices; i++) {
439 struct sbull_dev *dev = Devices + i;
440
441 del_timer_sync(&dev->timer);
442 if (dev->gd) {
443 del_gendisk(dev->gd);
444 put_disk(dev->gd);
445 }
446 if (dev->queue) {
447 if (request_mode == RM_NOQUEUE)
448 kobject_put (&dev->queue->kobj);
449 /* blk_put_queue() is no longer an exported symbol */
450 else
451 blk_cleanup_queue(dev->queue);
452 }
453 if (dev->data)
454 vfree(dev->data);
455 }
456 unregister_blkdev(sbull_major, "sbull");
457 kfree(Devices);
458 }
459
460 module_init(sbull_init);
461 module_exit(sbull_exit);
462
|
This page was automatically generated by the
LXR engine.
|