I really want to write it
parent
c0aa4d19cb
commit
19750efd6b
5
STL
5
STL
|
@ -49,9 +49,12 @@ N = index block size / 16 = phys block size / 16 = 32
|
|||
<skip cost for raw NAND> = WRITE*(int(O*S/E) > int(L*S/E) ? ((O*S)%E) : 0)
|
||||
|
||||
Data structures:
|
||||
* Mapping/version array: 8b * block count = 8MB for 4GB flash and 4096/512 map/phys sizes
|
||||
* Mapping/version array: 8b * block count ~~ 8MB for 4GB flash and 4096/512 map/phys sizes
|
||||
* Next block pointer: exactly 1 integer because STL flash is a ring buffer
|
||||
filled with number of first free block followed or included in an empty sequence
|
||||
* Free segment counter
|
||||
* Free block counter
|
||||
* (MAYBE) R-B tree / list of segments having at least one free block
|
||||
|
||||
USB flash read/write cost:
|
||||
* Write <8Kb cost = 4 * (Random read <8Kb cost)
|
||||
|
|
249
sftl.c
249
sftl.c
|
@ -1,9 +1,9 @@
|
|||
/*
|
||||
* A sample, extra-simple block driver. Updated for kernel 2.6.31.
|
||||
* Simple log-structured translation layer for FTLed flash drives
|
||||
* like memory cards or USB sticks.
|
||||
*
|
||||
* (C) 2003 Eklektix, Inc.
|
||||
* (C) 2010 Pat Patterson <pat at superpat dot com>
|
||||
* Redistributable under the terms of the GNU GPL.
|
||||
* (C) 2013 Vitaliy Filippov <vitalif at mail dot ru>
|
||||
* Redistributable under the terms of the GNU GPL 3.0+.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
|
@ -19,36 +19,39 @@
|
|||
#include <linux/blkdev.h>
|
||||
#include <linux/hdreg.h>
|
||||
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
static char *Version = "1.4";
|
||||
|
||||
static int major_num = 0;
|
||||
module_param(major_num, int, 0);
|
||||
static int logical_block_size = 512;
|
||||
module_param(logical_block_size, int, 0);
|
||||
static int nsectors = 1024; /* How big the drive is */
|
||||
module_param(nsectors, int, 0);
|
||||
|
||||
/*
|
||||
* We can tweak our hardware sector size, but the kernel talks to us
|
||||
* in terms of small sectors, always.
|
||||
*/
|
||||
#define KERNEL_SECTOR_SIZE 512
|
||||
|
||||
/*
|
||||
* Our request queue.
|
||||
*/
|
||||
static struct request_queue *Queue;
|
||||
#define ERROR(fmt, args...) printk(KERN_ERR "sftl: " fmt "\n" , ## args)
|
||||
#define INFO(fmt, args...) printk(KERN_INFO "sftl: " fmt "\n" , ## args)
|
||||
|
||||
/*
|
||||
* The internal representation of our device.
|
||||
*/
|
||||
static struct sbd_device {
|
||||
unsigned long size;
|
||||
spinlock_t lock;
|
||||
u8 *data;
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Vitaliy Filippov <vitalif@mail.ru>");
|
||||
MODULE_DESCRIPTION("Log-structured translation layer for USB sticks and memory cards");
|
||||
|
||||
static char *Version = "0.1";
|
||||
|
||||
static int major_num = 0;
|
||||
static int phy_sz = 512;
|
||||
static int clust_sz = 4096;
|
||||
static int clust_blocks = clust_sz/phy_sz;
|
||||
static int seg_sz = phy_sz/16; /* in blocks */
|
||||
|
||||
/* The internal representation of our device */
|
||||
struct sftl_dev {
|
||||
u32 size; // device size in blocks
|
||||
u32 *map, *ver; // block mappings and versions
|
||||
u32 nextblk; // next free block pointer
|
||||
u32 freeblks, freesegs; // free block count, free segment count
|
||||
struct gendisk *gd;
|
||||
} Device;
|
||||
struct block_device *blkdev;
|
||||
struct request_queue *queue;
|
||||
spinlock_t spinlock;
|
||||
struct mutex write_mutex;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
/* Our block device list, used in cleanup_module */
|
||||
static LIST_HEAD(blkmtd_device_list);
|
||||
|
||||
/*
|
||||
* Handle an I/O request.
|
||||
|
@ -109,67 +112,157 @@ int sbd_getgeo(struct block_device * block_device, struct hd_geometry * geo) {
|
|||
/*
|
||||
* The device operations structure.
|
||||
*/
|
||||
static struct block_device_operations sbd_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.getgeo = sbd_getgeo
|
||||
static struct block_device_operations sftl_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.getgeo = sbd_getgeo
|
||||
};
|
||||
|
||||
static int __init sbd_init(void) {
|
||||
static int __init sftl_init(void)
|
||||
{
|
||||
/*
|
||||
* Set up our internal device.
|
||||
* Register major number
|
||||
*/
|
||||
Device.size = nsectors * logical_block_size;
|
||||
spin_lock_init(&Device.lock);
|
||||
Device.data = vmalloc(Device.size);
|
||||
if (Device.data == NULL)
|
||||
return -ENOMEM;
|
||||
/*
|
||||
* Get a request queue.
|
||||
*/
|
||||
Queue = blk_init_queue(sbd_request, &Device.lock);
|
||||
if (Queue == NULL)
|
||||
goto out;
|
||||
blk_queue_logical_block_size(Queue, logical_block_size);
|
||||
/*
|
||||
* Get registered.
|
||||
*/
|
||||
major_num = register_blkdev(major_num, "sbd");
|
||||
if (major_num < 0) {
|
||||
printk(KERN_WARNING "sbd: unable to get major number\n");
|
||||
major_num = register_blkdev(major_num, "sftl");
|
||||
if (major_num < 0)
|
||||
{
|
||||
printk(KERN_WARNING "sftl: unable to get major number\n");
|
||||
goto out;
|
||||
}
|
||||
/*
|
||||
* And the gendisk structure.
|
||||
*/
|
||||
Device.gd = alloc_disk(16);
|
||||
if (!Device.gd)
|
||||
goto out_unregister;
|
||||
Device.gd->major = major_num;
|
||||
Device.gd->first_minor = 0;
|
||||
Device.gd->fops = &sbd_ops;
|
||||
Device.gd->private_data = &Device;
|
||||
strcpy(Device.gd->disk_name, "sbd0");
|
||||
set_capacity(Device.gd, nsectors);
|
||||
Device.gd->queue = Queue;
|
||||
add_disk(Device.gd);
|
||||
|
||||
return 0;
|
||||
|
||||
out_unregister:
|
||||
unregister_blkdev(major_num, "sbd");
|
||||
out:
|
||||
vfree(Device.data);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static void __exit sbd_exit(void)
|
||||
static void __exit sftl_exit(void)
|
||||
{
|
||||
del_gendisk(Device.gd);
|
||||
put_disk(Device.gd);
|
||||
unregister_blkdev(major_num, "sbd");
|
||||
unregister_blkdev(major_num, "sftl");
|
||||
blk_cleanup_queue(Queue);
|
||||
vfree(Device.data);
|
||||
}
|
||||
|
||||
module_init(sbd_init);
|
||||
module_exit(sbd_exit);
|
||||
module_init(sftl_init);
|
||||
module_exit(sftl_exit);
|
||||
|
||||
static struct sftl_dev *add_device(char *devname)
|
||||
{
|
||||
const fmode_t mode = FMODE_READ | FMODE_WRITE | FMODE_EXCL;
|
||||
struct block_device *bdev;
|
||||
struct sftl_dev *dev;
|
||||
char *name;
|
||||
|
||||
if (!devname)
|
||||
return NULL;
|
||||
|
||||
dev = kzalloc(sizeof(struct sftl_dev), GFP_KERNEL);
|
||||
if (!dev)
|
||||
return NULL;
|
||||
|
||||
/**
|
||||
* Get a handle on the device
|
||||
*/
|
||||
bdev = blkdev_get_by_path(devname, mode, dev);
|
||||
if (IS_ERR(bdev))
|
||||
{
|
||||
ERROR("cannot open device %s", devname);
|
||||
goto devinit_err;
|
||||
}
|
||||
dev->blkdev = bdev;
|
||||
|
||||
/* if (MAJOR(bdev->bd_dev) == major_num)
|
||||
{
|
||||
ERROR("attempting to use an SFTL device into another SFTL device");
|
||||
goto devinit_err;
|
||||
}*/
|
||||
|
||||
mutex_init(&dev->write_mutex);
|
||||
|
||||
dev->segs = bdev->bd_block_size / clust_blocks / (seg_sz+1);
|
||||
dev->size = dev->segs * seg_sz * clust_blocks;
|
||||
dev->reserved_segs = seg_sz * (seg_sz+1);
|
||||
|
||||
/*
|
||||
* Get a request queue
|
||||
*/
|
||||
spin_lock_init(&dev->spinlock);
|
||||
dev->queue = blk_init_queue(sftl_request, &dev->spinlock);
|
||||
if (dev->queue == NULL)
|
||||
goto devinit_err;
|
||||
blk_queue_logical_block_size(dev->queue, clust_sz);
|
||||
|
||||
/*
|
||||
* And the gendisk structure
|
||||
*/
|
||||
dev->gd = alloc_disk(16);
|
||||
if (!dev->gd)
|
||||
goto devinit_err;
|
||||
dev->gd->major = major_num;
|
||||
dev->gd->first_minor = 0;//!!!
|
||||
dev->gd->fops = &sftl_ops;
|
||||
dev->gd->private_data = dev;
|
||||
strcpy(dev->gd->disk_name, "sftl0");//!!!
|
||||
set_capacity(dev->gd, dev->size);
|
||||
dev->gd->queue = dev->queue;
|
||||
add_disk(dev->gd);
|
||||
|
||||
list_add(&dev->list, &sftl_device_list);
|
||||
INFO("/dev/sftl%d: [%s]", !!!, devname);
|
||||
return dev;
|
||||
|
||||
devinit_err:
|
||||
sftl_free_device(dev);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void kill_final_newline(char *str)
|
||||
{
|
||||
char *newline = strrchr(str, '\n');
|
||||
if (newline && !newline[1])
|
||||
*newline = 0;
|
||||
}
|
||||
|
||||
static int sftl_setup(const char *val, struct kernel_param *kp)
|
||||
{
|
||||
char buf[80 + 12];
|
||||
char *str = buf;
|
||||
char *token[2];
|
||||
char *name;
|
||||
int i, ret;
|
||||
|
||||
if (strnlen(val, sizeof(buf)) >= sizeof(buf))
|
||||
{
|
||||
ERROR("parameter too long");
|
||||
return 0;
|
||||
}
|
||||
|
||||
strcpy(str, val);
|
||||
kill_final_newline(str);
|
||||
|
||||
for (i = 0; i < 1; i++)
|
||||
token[i] = strsep(&str, ",");
|
||||
|
||||
if (str)
|
||||
{
|
||||
ERROR("too much parameters");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!token[0])
|
||||
{
|
||||
ERROR("underlying block device name missing");
|
||||
return 0;
|
||||
}
|
||||
|
||||
name = token[0];
|
||||
if (strlen(name) + 1 > 80)
|
||||
{
|
||||
ERROR("device name too long");
|
||||
return 0;
|
||||
}
|
||||
|
||||
add_device(name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
module_param_call(dev, sftl_setup, NULL, NULL, 0200);
|
||||
MODULE_PARM_DESC(dev, "Underlying device to use. \"dev=<dev>\"");
|
||||
|
|
Loading…
Reference in New Issue