I've written a module that acts as a cache for fixed size objects but I
get a soft lockup trying to use the buffer cache.

I've attached the module that reproduces the error. You need to supply the
module with a block device, i.e.
insmod disk_cache.ko devname="/dev/hda2".
/*
 * An object oriented disk cache.
 */

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/vmalloc.h>
#include <linux/genhd.h>
#include <linux/blkdev.h>
#include <linux/hdreg.h>
#include <linux/workqueue.h>
#include <linux/bio.h>
#include <linux/buffer_head.h>
//#include "assert.h"
//#include "debug.h"
#define assert(s) if(!(s)) { printk(KERN_EMERG "assertion failed: %d: ", 
__LINE__); panic(#s);}

#define KERNEL_SECTOR_SIZE 512

MODULE_LICENSE("Dual BSD/GPL");

struct disk_cache {
        int object_size;                        /* object size */
        sector_t cache_size;                    /* number of objects to be held 
in cache */
        sector_t disk_size;                     /* maximum number of objects */
        int objects_per_sector;                 /* number of objects per sector 
*/
        sector_t start;                         /* start sector for data 
storage */
};

static int major_num = 0;

struct block_device *bdev = NULL;

sector_t start = 0;

static char *devname = NULL;
module_param(devname, charp, 0);

/*
 * Create and initialize a disk cache.
 * @disk_cache - an allocated disk_cache structure
 * @object_size - the object size to use (in bytes)
 * @cache_size - total memory size for cache (in objects)
 * @disk_size - total disk size for objects (in objects)
 */
int disk_cache_create(struct disk_cache* const disk_cache, const int 
object_size, const sector_t disk_size) {

        assert(disk_cache);
        assert(disk_size > 0);
        
        disk_cache->object_size = object_size;
        disk_cache->disk_size = disk_size;
        disk_cache->objects_per_sector = bdev_get_queue(bdev)->hardsect_size / 
object_size;
        disk_cache->start = start;      
        start += ((unsigned long)disk_size / (unsigned 
long)disk_cache->objects_per_sector) + 1;
        return 1;
}
EXPORT_SYMBOL(disk_cache_create);

/*
 * Returns an address where the requested object is found.
 */
void * get_object(const struct disk_cache* disk_cache, const sector_t 
object_number) {

        int offset = 0;
        sector_t sector = 0;
        struct buffer_head *bh = NULL;

        assert(disk_cache);
        assert(object_number < disk_cache->disk_size);

        /*
         * Compute page number in which requested object resides.
         */
        sector = disk_cache->start + (unsigned long)object_number / (unsigned 
long)disk_cache->objects_per_sector;
        //assert(sector < disk_cache->disk_size);
        //assert(sector < get_capacity(bdev->bd_disk));
        offset = ((unsigned long)object_number % (unsigned 
long)disk_cache->objects_per_sector) * disk_cache->object_size;

        bh = __bread(bdev, sector, bdev_get_queue(bdev)->hardsect_size);
        //return bh->b_data + offset;
        return NULL;
}
EXPORT_SYMBOL(get_object);

/*
 * Puts given object back to the buffer cache. Flag 'modified' must be set if 
the object was modified.
 */
void put_object(const struct disk_cache* const disk_cache, const sector_t 
object_number, const int modified) {

        struct buffer_head *bh = NULL;
        sector_t sector = disk_cache->start + (unsigned long)object_number / 
(unsigned long)disk_cache->objects_per_sector;
        
        bh = __bread(bdev, sector, bdev_get_queue(bdev)->hardsect_size);
        //if(modified) {                
        //      lock_buffer(bh);
        //      set_buffer_uptodate(bh);
        //      mark_buffer_dirty(bh);
        //      unlock_buffer(bh);
        //}
        brelse(bh);
        /*
         * an additional release, we didn't released it in get_object
         */
        brelse(bh);             
}
EXPORT_SYMBOL(put_object);

static void test(void) {

        struct disk_cache dk;
        int i;
        void *p;

        if(!disk_cache_create(&dk, 6, 100)) {
                printk(KERN_ERR "create cache error\n");
                return;
        }
        for(i = 0; i < 100; i++) {
                get_object(&dk, i);
                put_object(&dk, i, 0);
        }
}

static int __init disk_cache_init(void) {

        major_num = register_blkdev(major_num, "disk_cache");
        if (major_num <= 0) {
                printk(KERN_ERR "disk_cache: unable to get major number\n");
                return -EINVAL;
        }

        if(!devname) {
                printk(KERN_ERR "disk_cache: must supply a valid block 
device\n");
                return -EINVAL;
        }

        bdev = open_bdev_excl(devname, 0, NULL);
        if(IS_ERR(bdev)) {
                printk(KERN_ERR "disk_cache: cannot open device %s.\n", 
devname);
                return -EINVAL;
        }

        printk(KERN_INFO "disk_cache: using device %s\n", devname);
        printk(KERN_INFO "disk_cache: %llu sectors\n", 
get_capacity(bdev->bd_disk));

        test();
        
        return 0;
}

static void __exit disk_cache_exit(void) {
        close_bdev_excl(bdev);
}

module_init(disk_cache_init);
module_exit(disk_cache_exit);

Reply via email to