I have been reading how to implement a block device driver in Chaper 12
from the <<Linux device driver>> 2nd, which is available on
http://www.xml.com/ldd/chapter/book/ . The block driver also simulate a
harddisk using physical memory. There is a "struct Sbull_Dev"
associated with each device.
There are things that I can't figure out by reading the source code. My question are: (1)
By default, each device has two megbytes. The 'Sbull_Dev.size' is used
to represent the size of the device. what I can't understand is these
two megbytes has never been allocated, how can the device holds data. (2)
the pointer variable 'Sbull_dev.data' is used to hold the data been
transferred from the buffer cache. But the pointer has never been
assigned to pointer to any memory, and has never been allocated. How
can the pointer hold the tranferred data???
(3) This is related to the
second question. At the clean_up function, the pointer variable
'Sbull_Dev.data' is freed using 'vfree'. If a pointer has never been
allocated, how the pointer can be freed.
I just list the necessary code which is related to my questions: (1)
the struct for each device. (2) the tranfer function from the request
queue to the device memory. (3) the init module function. (4) the clean
up function.
(1) the struct associated with each device
typedef struct Sbull_Dev {
int size;
int usage;
struct timer_list timer;
spinlock_t lock;
u8 *data;
} Sbull_Dev;
(2) The transfer function from the request queue to the device memory.
The pointer variable 'ptr' is used to hold the transferred data. But
the variable is never allocated or assigned to other variable. How can
the pointer holds the transferred data.
/*
* Perform an actual transfer.
*/
static int sbull_transfer(Sbull_Dev *device, const struct request *req)
{
int size;
u8 *ptr;
ptr = device->data + req->sector * sbull_hardsect;
size = req->current_nr_sectors * sbull_hardsect;
/* Make sure that the transfer fits within the device. */
if (ptr + size > device->data + sbull_blksize*sbull_size) {
static int count = 0;
if (count++ < 5)
printk(KERN_WARNING "sbull: request past end of device\n");
return 0;
}
/* Looks good, do the transfer. */
switch(req->cmd) {
case READ:
memcpy(req->buffer, ptr, size); /* from sbull to buffer */
return 1;
case WRITE:
memcpy(ptr, req->buffer, size); /* from buffer to sbull */
return 1;
default:
/* can't happen */
return 0;
}
}
(3) The init module function. The two megbytes is never been allocated.
int sbull_init(void)
{
int result, i;
/*
* Copy the (static) cfg variables to public prefixed ones to allow
* snoozing with a debugger.
*/
sbull_major = major;
sbull_devs = devs;
sbull_rahead = rahead;
sbull_size = size;
sbull_blksize = blksize;
sbull_hardsect = hardsect;
/*
* Register your major, and accept a dynamic number
*/
result = register_blkdev(sbull_major, "sbull", &sbull_bdops);
if (result < 0) {
printk(KERN_WARNING "sbull: can't get major %d\n",sbull_major);
return result;
}
if (sbull_major == 0) sbull_major = result; /* dynamic */
major = sbull_major; /* Use `major' later on to save typing */
/*
* Assign the other needed values: request, rahead, size, blksize,
* hardsect. All the minor devices feature the same value.
* Note that `sbull' defines all of them to allow testing non-default
* values. A real device could well avoid setting values in global
* arrays if it uses the default values.
*/
read_ahead[major] = sbull_rahead;
result = -ENOMEM; /* for the possible errors */
sbull_sizes = kmalloc(sbull_devs * sizeof(int), GFP_KERNEL);
if (!sbull_sizes)
goto fail_malloc;
for (i=0; i < sbull_devs; i++) /* all the same size */
sbull_sizes[i] = sbull_size;
blk_size[major]=sbull_sizes;
sbull_blksizes = kmalloc(sbull_devs * sizeof(int), GFP_KERNEL);
if (!sbull_blksizes)
goto fail_malloc;
for (i=0; i < sbull_devs; i++) /* all the same blocksize */
sbull_blksizes[i] = sbull_blksize;
blksize_size[major]=sbull_blksizes;
sbull_hardsects = kmalloc(sbull_devs * sizeof(int), GFP_KERNEL);
if (!sbull_hardsects)
goto fail_malloc;
for (i=0; i < sbull_devs; i++) /* all the same hardsect */
sbull_hardsects[i] = sbull_hardsect;
hardsect_size[major]=sbull_hardsects;
/* FIXME: max_readahead and max_sectors */
/*
* allocate the devices -- we can't have them static, as the number
* can be specified at load time
*/
sbull_devices = kmalloc(sbull_devs * sizeof (Sbull_Dev), GFP_KERNEL);
if (!sbull_devices)
goto fail_malloc;
memset(sbull_devices, 0, sbull_devs * sizeof (Sbull_Dev));
for (i=0; i < sbull_devs; i++) {
/* data and usage remain zeroed */
sbull_devices[i].size = 1024 * sbull_size;
init_timer(&(sbull_devices[i].timer));
sbull_devices[i].timer.data = "" long)(sbull_devices+i);
sbull_devices[i].timer.function = sbull_expires;
spin_lock_init(&sbull_devices[i].lock);
}
/*
* Get the queue set up, and register our (nonexistent) partitions.
*/
if (noqueue)
blk_queue_make_request(BLK_DEFAULT_QUEUE(major), sbull_make_request);
else
blk_init_queue(BLK_DEFAULT_QUEUE(major), sbull_request);
/* A no-op in 2.4.0, but all drivers seem to do it anyway */
for (i = 0; i < sbull_devs; i++)
register_disk(NULL, MKDEV(major, i), 1, &sbull_bdops,
sbull_size << 1);
#ifndef SBULL_DEBUG
EXPORT_NO_SYMBOLS; /* otherwise, leave global symbols visible */
#endif
printk ("<1>sbull: init complete, %d devs, size %d blks %d hs %d\n",
sbull_devs, sbull_size, sbull_blksize, sbull_hardsect);
if (noqueue)
printk (KERN_INFO "sbull: using direct make_request\n");
return 0; /* succeed */
fail_malloc:
read_ahead[major] = 0;
if (sbull_sizes) kfree(sbull_sizes);
blk_size[major] = NULL;
if (sbull_blksizes) kfree(sbull_blksizes);
blksize_size[major] = NULL;
if (sbull_hardsects) kfree(sbull_hardsects);
hardsect_size[major] = NULL;
if (sbull_devices) kfree(sbull_devices);
unregister_blkdev(major, "sbull");
return result;
}
(4) The clean up function. Where the 'Sbull_Dev.data' is been vfreed, but which is never been allocated.
void sbull_cleanup(void)
{
int i;
/*
* Before anything else, get rid of the timer functions. Set the "usage"
* flag on each device as well, under lock, so that if the timer fires up
* just before we delete it, it will either complete or abort. Otherwise
* we have nasty race conditions to worry about.
*/
for (i = 0; i < sbull_devs; i++) {
Sbull_Dev *dev = sbull_devices + i;
del_timer(&dev->timer);
spin_lock(&dev->lock);
dev->usage++;
spin_unlock(&dev->lock);
}
/* flush it all and reset all the data structures */
for (i=0; i<sbull_devs; i++)
fsync_dev(MKDEV(sbull_major, i)); /* flush the devices */
unregister_blkdev(major, "sbull");
blk_cleanup_queue(BLK_DEFAULT_QUEUE(major));
/* Clean up the global arrays */
read_ahead[major] = 0;
kfree(blk_size[major]);
blk_size[major] = NULL;
kfree(blksize_size[major]);
blksize_size[major] = NULL;
kfree(hardsect_size[major]);
hardsect_size[major] = NULL;
/* FIXME: max_readahead and max_sectors */
/* finally, the usual cleanup */
for (i=0; i < sbull_devs; i++) {
if (sbull_devices[i].data)
vfree(sbull_devices[i].data);
}
kfree(sbull_devices);
}