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