spull.c
上传用户:wudi5211
上传日期:2010-01-21
资源大小:607k
文件大小:20k
源码类别:

嵌入式Linux

开发平台:

C/C++

  1. /*
  2.  * spull.c -- the Simple Partitionable Utility
  3.  *
  4.  * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet
  5.  * Copyright (C) 2001 O'Reilly & Associates
  6.  *
  7.  * The source code in this file can be freely used, adapted,
  8.  * and redistributed in source or binary form, so long as an
  9.  * acknowledgment appears in derived source files.  The citation
  10.  * should list that the code comes from the book "Linux Device
  11.  * Drivers" by Alessandro Rubini and Jonathan Corbet, published
  12.  * by O'Reilly & Associates.   No warranty is attached;
  13.  * we cannot take responsibility for errors or fitness for use.
  14.  *
  15.  * $Id: spull.c,v 1.17 2001/07/18 22:28:18 rubini Exp $
  16.  */
  17. #ifndef __KERNEL__
  18. #  define __KERNEL__
  19. #endif
  20. #ifndef MODULE
  21. #  define MODULE
  22. #endif
  23. #include <linux/config.h>
  24. #include <linux/module.h>
  25. #include <linux/sched.h>
  26. #include <linux/kernel.h> /* printk() */
  27. #include <linux/malloc.h> /* kmalloc() */
  28. #include <linux/fs.h>     /* everything... */
  29. #include <linux/errno.h>  /* error codes */
  30. #include <linux/timer.h>
  31. #include <linux/types.h>  /* size_t */
  32. #include <linux/fcntl.h>        /* O_ACCMODE */
  33. #include <linux/hdreg.h>  /* HDIO_GETGEO */
  34. #include <asm/system.h>   /* cli(), *_flags */
  35. #define MAJOR_NR spull_major /* force definitions on in blk.h */
  36. int spull_major; /* must be declared before including blk.h */
  37. #define SPULL_SHIFT 4                         /* max 16 partitions  */
  38. #define SPULL_MAXNRDEV 4                      /* max 4 device units */
  39. #define DEVICE_NR(device) (MINOR(device)>>SPULL_SHIFT)
  40. #define DEVICE_NAME "pd"                      /* name for messaging */
  41. #define DEVICE_INTR spull_intrptr         /* pointer to the bottom half */
  42. #define DEVICE_NO_RANDOM                  /* no entropy to contribute */
  43. #define DEVICE_REQUEST spull_request
  44. #define DEVICE_OFF(d) /* do-nothing */
  45. #include <linux/blk.h>
  46. #include "spull.h"        /* local definitions */
  47. #ifdef HAVE_BLKPG_H
  48. #include <linux/blkpg.h>  /* blk_ioctl() */
  49. #endif
  50. /*
  51.  * Non-prefixed symbols are static. They are meant to be assigned at
  52.  * load time. Prefixed symbols are not static, so they can be used in
  53.  * debugging. They are hidden anyways by register_symtab() unless
  54.  * SPULL_DEBUG is defined.
  55.  */
  56. static int major    = SPULL_MAJOR;
  57. static int devs     = SPULL_DEVS;
  58. static int rahead   = SPULL_RAHEAD;
  59. static int size     = SPULL_SIZE;
  60. static int irq      = 0;
  61. static int blksize  = SPULL_BLKSIZE;
  62. MODULE_PARM(major, "i");
  63. MODULE_PARM(devs, "i");
  64. MODULE_PARM(rahead, "i");
  65. MODULE_PARM(size, "i");
  66. MODULE_PARM(blksize, "i");
  67. MODULE_PARM(irq, "i");
  68. MODULE_AUTHOR("Alessandro Rubini");
  69. int spull_devs, spull_rahead, spull_size;
  70. int spull_blksize, spull_irq;
  71. /* The following items are obtained through kmalloc() in spull_init() */
  72. Spull_Dev *spull_devices = NULL;
  73. int *spull_sizes = NULL;
  74. /* Forwards */
  75. int spull_revalidate(kdev_t i_rdev);
  76. #ifdef LINUX_24
  77. struct block_device_operations spull_bdops;
  78. #endif
  79. /*
  80.  * Time for our genhd structure.
  81.  */
  82. struct gendisk spull_gendisk = {
  83.     major:              0,              /* Major number assigned later */
  84.     major_name:         "pd",           /* Name of the major device */
  85.     minor_shift:        SPULL_SHIFT,    /* Shift to get device number */
  86.     max_p:              1 << SPULL_SHIFT, /* Number of partitions */
  87. #ifdef LINUX_24                         
  88.     fops:               &spull_bdops,   /* Block dev operations */
  89. #endif                                  
  90. /* everything else is dynamic */
  91. };
  92. struct hd_struct *spull_partitions = NULL;
  93. /*
  94.  * Flag used in "irq driven" mode to mark when we have operations
  95.  * outstanding.
  96.  */
  97. volatile int spull_busy = 0;
  98. /*
  99.  * Open and close
  100.  */
  101. int spull_open (struct inode *inode, struct file *filp)
  102. {
  103.     Spull_Dev *dev; /* device information */
  104.     int num = DEVICE_NR(inode->i_rdev);
  105.     if (num >= spull_devs) return -ENODEV;
  106.     dev = spull_devices + num;
  107.     /* kill the timer associated to the device: it might be active */
  108.     del_timer(&dev->timer);
  109.     spin_lock(&dev->lock);
  110.     /*
  111.      * If no data area is there, allocate it. Clear its head as
  112.      * well to prevent memory corruption due to bad partition info.
  113.      */
  114.     if (!dev->data) {
  115.         dev->data = vmalloc(dev->size);
  116.         memset(dev->data,0,2048);
  117.     }
  118.     if (!dev->data)
  119.     {
  120.         spin_unlock(&dev->lock);
  121.         return -ENOMEM;
  122.     }
  123.     
  124.     dev->usage++;
  125.     MOD_INC_USE_COUNT;
  126.     spin_unlock(&dev->lock);
  127.     return 0;          /* success */
  128. }
  129. int spull_release (struct inode *inode, struct file *filp)
  130. {
  131.     Spull_Dev *dev = spull_devices + DEVICE_NR(inode->i_rdev);
  132.     spin_lock(&dev->lock);
  133.     dev->usage--;
  134.     /*
  135.      * If the device is closed for the last time, start a timer
  136.      * to release RAM in half a minute. The function and argument
  137.      * for the timer have been setup in spull_init()
  138.      */
  139.     if (!dev->usage) {
  140.         dev->timer.expires = jiffies + 60 * HZ;
  141.         add_timer(&dev->timer);
  142.         /* but flush it right now */
  143.         fsync_dev(inode->i_rdev);
  144.         invalidate_buffers(inode->i_rdev);
  145.     }
  146.     MOD_DEC_USE_COUNT;
  147.     spin_unlock(&dev->lock);
  148.     return 0;
  149. }
  150. /*
  151.  * The timer function. As argument it receives the device
  152.  */
  153. void spull_expires(unsigned long data)
  154. {
  155.     Spull_Dev *dev = (Spull_Dev *)data;
  156.     spin_lock(&dev->lock);
  157.     if (dev->usage || !dev->data) {
  158.         spin_unlock(&dev->lock);
  159.         printk(KERN_WARNING "spull: timer mismatch for device %in",
  160.                dev - spull_devices);
  161.         return;
  162.     }
  163.     PDEBUG("freeing device %in",dev - spull_devices);
  164.     vfree(dev->data);
  165.     dev->data=0;
  166.     spin_unlock(&dev->lock);
  167.     return;
  168. }    
  169. /*
  170.  * The ioctl() implementation
  171.  */
  172. int spull_ioctl (struct inode *inode, struct file *filp,
  173.                  unsigned int cmd, unsigned long arg)
  174. {
  175.     int err, size;
  176.     struct hd_geometry geo;
  177.     PDEBUG("ioctl 0x%x 0x%lxn", cmd, arg);
  178.     switch(cmd) {
  179.       case BLKGETSIZE:
  180.         /* Return the device size, expressed in sectors */
  181.         err = ! access_ok (VERIFY_WRITE, arg, sizeof(long));
  182.         if (err) return -EFAULT;
  183.         size = spull_gendisk.part[MINOR(inode->i_rdev)].nr_sects;
  184. if (copy_to_user((long *) arg, &size, sizeof (long)))
  185.     return -EFAULT;
  186.         return 0;
  187.       case BLKFLSBUF: /* flush */
  188.         if (! capable(CAP_SYS_RAWIO)) return -EACCES; /* only root */
  189.         fsync_dev(inode->i_rdev);
  190.         invalidate_buffers(inode->i_rdev);
  191.         return 0;
  192.       case BLKRAGET: /* return the readahead value */
  193.         err = ! access_ok(VERIFY_WRITE, arg, sizeof(long));
  194.         if (err) return -EFAULT;
  195.         PUT_USER(read_ahead[MAJOR(inode->i_rdev)],(long *) arg);
  196.         return 0;
  197.       case BLKRASET: /* set the readahead value */
  198.         if (!capable(CAP_SYS_RAWIO)) return -EACCES;
  199.         if (arg > 0xff) return -EINVAL; /* limit it */
  200.         read_ahead[MAJOR(inode->i_rdev)] = arg;
  201.         return 0;
  202.       case BLKRRPART: /* re-read partition table */
  203.         return spull_revalidate(inode->i_rdev);
  204.       case HDIO_GETGEO:
  205.         /*
  206.          * get geometry: we have to fake one...  trim the size to a
  207.          * multiple of 64 (32k): tell we have 16 sectors, 4 heads,
  208.          * whatever cylinders. Tell also that data starts at sector. 4.
  209.          */
  210.         err = ! access_ok(VERIFY_WRITE, arg, sizeof(geo));
  211.         if (err) return -EFAULT;
  212.         size = spull_size * blksize / SPULL_HARDSECT;
  213.         geo.cylinders = (size & ~0x3f) >> 6;
  214. geo.heads = 4;
  215. geo.sectors = 16;
  216. geo.start = 4;
  217. if (copy_to_user((void *) arg, &geo, sizeof(geo)))
  218.     return -EFAULT;
  219.         return 0;
  220.       default:
  221.         /*
  222.          * For ioctls we don't understand, let the block layer handle them.
  223.          */
  224.         return blk_ioctl(inode->i_rdev, cmd, arg);
  225.     }
  226.     return -ENOTTY; /* unknown command */
  227. }
  228. /*
  229.  * Support for removable devices
  230.  */
  231. int spull_check_change(kdev_t i_rdev)
  232. {
  233.     int minor = DEVICE_NR(i_rdev);
  234.     Spull_Dev *dev = spull_devices + minor;
  235.     if (minor >= spull_devs) /* paranoid */
  236.         return 0;
  237.     PDEBUG("check_change for dev %in",minor);
  238.     if (dev->data)
  239.         return 0; /* still valid */
  240.     return 1; /* expired */
  241. }
  242. /*
  243.  * The file operations
  244.  */
  245. #ifdef LINUX_24
  246. struct block_device_operations spull_bdops = {
  247.     open:       spull_open,
  248.     release:    spull_release,
  249.     ioctl:      spull_ioctl,
  250.     revalidate: spull_revalidate,
  251.     check_media_change: spull_check_change,
  252. };
  253. #else
  254. #ifdef LINUX_20
  255. void spull_release_20 (struct inode *inode, struct file *filp)
  256. {
  257.         (void) spull_release (inode, filp);
  258. }
  259. #define spull_release spull_release_20
  260. #endif
  261. struct file_operations spull_bdops = {
  262.     read:       block_read,
  263.     write:      block_write,
  264.     ioctl:      spull_ioctl,
  265.     open:       spull_open,
  266.     release:    spull_release,
  267.     fsync:      block_fsync,
  268.     check_media_change: spull_check_change,
  269.     revalidate: spull_revalidate
  270. };
  271. # endif /* LINUX_24 */
  272. /*
  273.  * Note no locks taken out here.  In a worst case scenario, we could drop
  274.  * a chunk of system memory.  But that should never happen, since validation
  275.  * happens at open or mount time, when locks are held.
  276.  */
  277. int spull_revalidate(kdev_t i_rdev)
  278. {
  279.     /* first partition, # of partitions */
  280.     int part1 = (DEVICE_NR(i_rdev) << SPULL_SHIFT) + 1;
  281.     int npart = (1 << SPULL_SHIFT) -1;
  282.     /* first clear old partition information */
  283.     memset(spull_gendisk.sizes+part1, 0, npart*sizeof(int));
  284.     memset(spull_gendisk.part +part1, 0, npart*sizeof(struct hd_struct));
  285.     spull_gendisk.part[DEVICE_NR(i_rdev) << SPULL_SHIFT].nr_sects =
  286.             spull_size << 1;
  287.     /* then fill new info */
  288.     printk(KERN_INFO "Spull partition check: (%d) ", DEVICE_NR(i_rdev));
  289.     register_disk(&spull_gendisk, i_rdev, SPULL_MAXNRDEV, &spull_bdops,
  290.                     spull_size << 1);
  291.     return 0;
  292. }
  293. /*
  294.  * Block-driver specific functions
  295.  */
  296. /*
  297.  * Find the device for this request.
  298.  */
  299. static Spull_Dev *spull_locate_device(const struct request *req)
  300. {
  301.     int devno;
  302.     Spull_Dev *device;
  303.     /* Check if the minor number is in range */
  304.     devno = DEVICE_NR(req->rq_dev);
  305.     if (devno >= spull_devs) {
  306.         static int count = 0;
  307.         if (count++ < 5) /* print the message at most five times */
  308.             printk(KERN_WARNING "spull: request for unknown devicen");
  309.         return NULL;
  310.     }
  311.     device = spull_devices + devno;
  312.     return device;
  313. }
  314. /*
  315.  * Perform an actual transfer.
  316.  */
  317. static int spull_transfer(Spull_Dev *device, const struct request *req)
  318. {
  319.     int size, minor = MINOR(req->rq_dev);
  320.     u8 *ptr;
  321.     
  322.     ptr = device->data +
  323.             (spull_partitions[minor].start_sect + req->sector)*SPULL_HARDSECT;
  324.     size = req->current_nr_sectors*SPULL_HARDSECT;
  325.     /*
  326.      * Make sure that the transfer fits within the device.
  327.      */
  328.     if (req->sector + req->current_nr_sectors >
  329.                     spull_partitions[minor].nr_sects) {
  330.         static int count = 0;
  331.         if (count++ < 5)
  332.             printk(KERN_WARNING "spull: request past end of partitionn");
  333.         return 0;
  334.     }
  335.     /*
  336.      * Looks good, do the transfer.
  337.      */
  338.     switch(req->cmd) {
  339.         case READ:
  340.             memcpy(req->buffer, ptr, size); /* from spull to buffer */
  341.             return 1;
  342.         case WRITE:
  343.             memcpy(ptr, req->buffer, size); /* from buffer to spull */
  344.             return 1;
  345.         default:
  346.             /* can't happen */
  347.             return 0;
  348.         }
  349. }
  350. #ifdef LINUX_24
  351. void spull_request(request_queue_t *q)
  352. #else           
  353. void spull_request()
  354. #endif  
  355. {
  356.     Spull_Dev *device;
  357.     int status;
  358.     long flags;
  359.     while(1) {
  360.         INIT_REQUEST;  /* returns when queue is empty */
  361.         /* Which "device" are we using?  (Is returned locked) */
  362.         device = spull_locate_device (CURRENT);
  363.         if (device == NULL) {
  364.             end_request(0);
  365.             continue;
  366.         }
  367. spin_lock_irqsave(&device->lock, flags);
  368.         /* Perform the transfer and clean up. */
  369.         status = spull_transfer(device, CURRENT);
  370.         spin_unlock_irqrestore(&device->lock, flags);
  371.         end_request(status); /* success */
  372.     }
  373. }
  374. /*
  375.  * The fake interrupt-driven request
  376.  */
  377. struct timer_list spull_timer; /* the engine for async invocation */
  378. #ifdef LINUX_24
  379. void spull_irqdriven_request(request_queue_t *q)
  380. #else                                           
  381. void spull_irqdriven_request()                  
  382. #endif                                          
  383. {
  384.     Spull_Dev *device;
  385.     int status;
  386.     long flags;
  387.     /* If we are already processing requests, don't do any more now. */
  388.     if (spull_busy)
  389.             return;
  390.     while(1) {
  391.         INIT_REQUEST;  /* returns when queue is empty */
  392.         /* Which "device" are we using? */
  393.         device = spull_locate_device (CURRENT);
  394.         if (device == NULL) {
  395.             end_request(0);
  396.             continue;
  397.         }
  398. spin_lock_irqsave(&device->lock, flags);
  399.         /* Perform the transfer and clean up. */
  400.         status = spull_transfer(device, CURRENT);
  401.         spin_unlock_irqrestore(&device->lock, flags);
  402.         /* ... and wait for the timer to expire -- no end_request(1) */
  403.         spull_timer.expires = jiffies + spull_irq;
  404.         add_timer(&spull_timer);
  405.         spull_busy = 1;
  406.         return;
  407.     }
  408. }
  409. /* this is invoked when the timer expires */
  410. void spull_interrupt(unsigned long unused)
  411. {
  412.     unsigned long flags;
  413.     spin_lock_irqsave(&io_request_lock, flags);
  414.     end_request(1);    /* This request is done - we always succeed */
  415.     spull_busy = 0;  /* We have io_request_lock, no conflict with request */
  416.     if (! QUEUE_EMPTY) /* more of them? */
  417. #ifdef LINUX_24                         
  418.         spull_irqdriven_request(NULL);  /* Start the next transfer */
  419. #else                                   
  420.         spull_irqdriven_request();      
  421. #endif                                  
  422.     spin_unlock_irqrestore(&io_request_lock, flags);
  423. }
  424. /*
  425.  * Finally, the module stuff
  426.  */
  427. int spull_init(void)
  428. {
  429.     int result, i;
  430.     /*
  431.      * Copy the (static) cfg variables to public prefixed ones to allow
  432.      * snoozing with a debugger.
  433.      */
  434.     spull_major    = major;
  435.     spull_devs     = devs;
  436.     spull_rahead   = rahead;
  437.     spull_size     = size;
  438.     spull_blksize  = blksize;
  439.     /*
  440.      * Register your major, and accept a dynamic number
  441.      */
  442.     result = register_blkdev(spull_major, "spull", &spull_bdops);
  443.     if (result < 0) {
  444.         printk(KERN_WARNING "spull: can't get major %dn",spull_major);
  445.         return result;
  446.     }
  447.     if (spull_major == 0) spull_major = result; /* dynamic */
  448.     major = spull_major; /* Use `major' later on to save typing */
  449.     spull_gendisk.major = major; /* was unknown at load time */
  450.     /* 
  451.      * allocate the devices -- we can't have them static, as the number
  452.      * can be specified at load time
  453.      */
  454.     spull_devices = kmalloc(spull_devs * sizeof (Spull_Dev), GFP_KERNEL);
  455.     if (!spull_devices)
  456.         goto fail_malloc;
  457.     memset(spull_devices, 0, spull_devs * sizeof (Spull_Dev));
  458.     for (i=0; i < spull_devs; i++) {
  459.         /* data and usage remain zeroed */
  460.         spull_devices[i].size = blksize * spull_size;
  461.         init_timer(&(spull_devices[i].timer));
  462.         spull_devices[i].timer.data = (unsigned long)(spull_devices+i);
  463.         spull_devices[i].timer.function = spull_expires;
  464.         spin_lock_init(&spull_devices[i].lock);
  465.     }
  466.     /*
  467.      * Assign the other needed values: request, rahead, size, blksize,
  468.      * hardsect. All the minor devices feature the same value.
  469.      * Note that `spull' defines all of them to allow testing non-default
  470.      * values. A real device could well avoid setting values in global
  471.      * arrays if it uses the default values.
  472.      */
  473.     read_ahead[major] = spull_rahead;
  474.     result = -ENOMEM; /* for the possible errors */
  475.     spull_sizes = kmalloc( (spull_devs << SPULL_SHIFT) * sizeof(int),
  476.                           GFP_KERNEL);
  477.     if (!spull_sizes)
  478.         goto fail_malloc;
  479.     /* Start with zero-sized partitions, and correctly sized units */
  480.     memset(spull_sizes, 0, (spull_devs << SPULL_SHIFT) * sizeof(int));
  481.     for (i=0; i< spull_devs; i++)
  482.         spull_sizes[i<<SPULL_SHIFT] = spull_size;
  483.     blk_size[MAJOR_NR] = spull_gendisk.sizes = spull_sizes;
  484.     /* Allocate the partitions array. */
  485.     spull_partitions = kmalloc( (spull_devs << SPULL_SHIFT) *
  486.                                sizeof(struct hd_struct), GFP_KERNEL);
  487.     if (!spull_partitions)
  488.         goto fail_malloc;
  489.     memset(spull_partitions, 0, (spull_devs << SPULL_SHIFT) *
  490.            sizeof(struct hd_struct));
  491.     /* fill in whole-disk entries */
  492.     for (i=0; i < spull_devs; i++) 
  493.         spull_partitions[i << SPULL_SHIFT].nr_sects =
  494. spull_size*(blksize/SPULL_HARDSECT);
  495.     spull_gendisk.part = spull_partitions;
  496.     spull_gendisk.nr_real = spull_devs;
  497. #ifndef LINUX_24
  498.     spull_gendisk.max_nr = spull_devs;
  499. #endif
  500.     /*
  501.      * Put our gendisk structure on the list.
  502.      */
  503.     spull_gendisk.next = gendisk_head;
  504.     gendisk_head = &spull_gendisk; 
  505.     /* dump the partition table to see it */
  506.     for (i=0; i < spull_devs << SPULL_SHIFT; i++)
  507.         PDEBUGG("part %i: beg %lx, size %lxn", i,
  508.                spull_partitions[i].start_sect,
  509.                spull_partitions[i].nr_sects);
  510.     /*
  511.      * Allow interrupt-driven operation, if "irq=" has been specified
  512.      */
  513.     spull_irq = irq; /* copy the static variable to the visible one */
  514.     if (spull_irq) {
  515.         PDEBUG("setting timern");
  516.         spull_timer.function = spull_interrupt;
  517.         blk_init_queue(BLK_DEFAULT_QUEUE(major), spull_irqdriven_request);
  518.     }
  519.     else
  520.         blk_init_queue(BLK_DEFAULT_QUEUE(major), spull_request);
  521. #ifdef NOTNOW
  522.     for (i = 0; i < spull_devs; i++)
  523.             register_disk(NULL, MKDEV(major, i), 1, &spull_bdops,
  524.                             spull_size << 1);
  525. #endif
  526. #ifndef SPULL_DEBUG
  527.     EXPORT_NO_SYMBOLS; /* otherwise, leave global symbols visible */
  528. #endif
  529.     printk ("<1>spull: init complete, %d devs, size %d blks %dn",
  530.                     spull_devs, spull_size, spull_blksize);
  531.     return 0; /* succeed */
  532.   fail_malloc:
  533.     read_ahead[major] = 0;
  534.     if (spull_sizes) kfree(spull_sizes);
  535.     if (spull_partitions) kfree(spull_partitions);
  536.     blk_size[major] = NULL;
  537.     if (spull_devices) kfree(spull_devices);
  538.     unregister_blkdev(major, "spull");
  539.     return result;
  540. }
  541. void spull_cleanup(void)
  542. {
  543.     int i;
  544.     struct gendisk **gdp;
  545. /*
  546.  * Before anything else, get rid of the timer functions.  Set the "usage"
  547.  * flag on each device as well, under lock, so that if the timer fires up
  548.  * just before we delete it, it will either complete or abort.  Otherwise
  549.  * we have nasty race conditions to worry about.
  550.  */
  551.     for (i = 0; i < spull_devs; i++) {
  552.         Spull_Dev *dev = spull_devices + i;
  553.         del_timer(&dev->timer);
  554.         spin_lock(&dev->lock);
  555.         dev->usage++;
  556.         spin_unlock(&dev->lock);
  557.     }
  558.     /* flush it all and reset all the data structures */
  559. /*
  560.  * Unregister the device now to avoid further operations during cleanup.
  561.  */
  562.     unregister_blkdev(major, "spull");
  563.     for (i = 0; i < (spull_devs << SPULL_SHIFT); i++)
  564.         fsync_dev(MKDEV(spull_major, i)); /* flush the devices */
  565.     blk_cleanup_queue(BLK_DEFAULT_QUEUE(major));
  566.     read_ahead[major] = 0;
  567.     kfree(blk_size[major]); /* which is gendisk->sizes as well */
  568.     blk_size[major] = NULL;
  569.     kfree(spull_gendisk.part);
  570.     kfree(blksize_size[major]);
  571.     blksize_size[major] = NULL;
  572.     /*
  573.      * Get our gendisk structure off the list.
  574.      */
  575.     for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next))
  576.         if (*gdp == &spull_gendisk) {
  577.             *gdp = (*gdp)->next;
  578.             break;
  579.         }
  580.     /* finally, the usual cleanup */
  581.     for (i=0; i < spull_devs; i++) {
  582.         if (spull_devices[i].data)
  583.             vfree(spull_devices[i].data);
  584.     }
  585.     kfree(spull_devices);
  586. }
  587. module_init(spull_init);
  588. module_exit(spull_cleanup);