diff options
author | Ming Lin <mlin@kernel.org> | 2015-09-02 11:22:49 -0700 |
---|---|---|
committer | Ming Lin <ming.l@ssi.samsung.com> | 2016-11-05 09:17:56 -0700 |
commit | 13a9e5307d39145c6fcf5e9555bb04efc94fd7a1 (patch) | |
tree | 4e74d0a8d35bcf4925926ee514f279960a5a4b5d | |
parent | c13dcf9f2d6f5f06ef1bf79ec456df614c5e058b (diff) | |
download | linux-kvfs.tar.gz |
kvfs: draft prototypekvfs
mount -t kvfs /dev/kv0 /mnt
-rw-r--r-- | fs/Makefile | 2 | ||||
-rw-r--r-- | fs/kvfs.c | 391 | ||||
-rw-r--r-- | fs/kvstore.c | 202 | ||||
-rw-r--r-- | fs/kvstore.h | 41 |
4 files changed, 635 insertions, 1 deletions
diff --git a/fs/Makefile b/fs/Makefile index cb20e4bf230398..0d5fdcbd92d327 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -11,7 +11,7 @@ obj-y := open.o read_write.o file_table.o super.o \ attr.o bad_inode.o file.o filesystems.o namespace.o \ seq_file.o xattr.o libfs.o fs-writeback.o \ pnode.o splice.o sync.o utimes.o \ - stack.o fs_struct.o statfs.o fs_pin.o nsfs.o + stack.o fs_struct.o statfs.o fs_pin.o nsfs.o kvfs.o kvstore.o ifeq ($(CONFIG_BLOCK),y) obj-y += buffer.o block_dev.o direct-io.o mpage.o diff --git a/fs/kvfs.c b/fs/kvfs.c new file mode 100644 index 00000000000000..bf6105e8e9117f --- /dev/null +++ b/fs/kvfs.c @@ -0,0 +1,391 @@ +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/mount.h> +#include <linux/backing-dev.h> +#include "kvstore.h" + +#define KVFS_MAGIC_NUMBER 0x4B564653 +#define KVFS_DIRENT_SIZE 20 + +#define KVSTORE(inode) ((struct kvfs_sb_info *)inode->i_sb->s_fs_info)->kvs + +struct kvfs_sb_info { + struct kvstore *kvs; + struct backing_dev_info backing_dev_info; +}; + +static struct inode *kvfs_get_inode(struct super_block *sb, const struct inode *dir, + umode_t mode, dev_t dev, unsigned long flags); + +static int kvfs_readpage(struct file *file, struct page *page) +{ + struct kvstore *kvs; + struct address_space *mapping; + struct inode *inode; + char *key; + char *value; + int len = 4096; + int ret; + + //printk("DEBUG %s called\n", __func__); + + mapping = page->mapping; + inode = mapping->host; + kvs = KVSTORE(inode); + key = inode->i_private; + /* TODO: this is ugly */ + value = __va(page_to_phys(page)); + + ret = kvs->get(kvs, key, value, len); + //printk("value %s, ret %d\n", value, ret); + if (ret == KV_SUCCESS) + SetPageUptodate(page); + + unlock_page(page); + return 0; +} + +static int __kvfs_writepage(struct page *page, struct writeback_control *wbc, void *data) +{ + struct kvstore *kvs; + struct address_space *mapping; + struct inode *inode; + char *key; + char *value; + int ret; + + mapping = page->mapping; + inode = mapping->host; + kvs = KVSTORE(inode); + key = inode->i_private; + /* TODO: this is ugly */ + value = __va(page_to_phys(page)); + + printk("DEBUG: %s called, kvs %p, key %s, value %s\n", __func__, kvs, key, value); + + ret = kvs->put(kvs, key, strlen(key)+1, value, strlen(key)+1); + if (ret) + goto err; + + SetPageUptodate(page); +err: + unlock_page(page); + return ret; +} + +static int kvfs_writepage(struct page *page, struct writeback_control *wbc) +{ + printk("DEBUG: %s called\n", __func__); + return 0; +} + +static int kvfs_writepages(struct address_space *mapping, struct writeback_control *wbc) +{ + struct kvstore *kvs; + struct inode *inode; + + printk("DEBUG: %s called\n", __func__); + + inode = mapping->host; + kvs = KVSTORE(inode); + BUG_ON(!kvs); + + write_cache_pages(mapping, wbc, __kvfs_writepage, NULL); + + return 0; +} + +static const struct address_space_operations kvfs_aops = { + .readpage = kvfs_readpage, + .writepage = kvfs_writepage, + .writepages = kvfs_writepages, + .write_begin = simple_write_begin, + .write_end = simple_write_end, +}; + +ssize_t +kvfs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter) +{ + //printk("DEBUG: %s called\n", __func__); + return generic_file_read_iter(iocb, iter); +} + +static int kvfs_fsync(struct file *file, loff_t start, loff_t end, int datasync) +{ + struct inode *inode = file_inode(file); + int ret; + + //printk("DEBUG: %s called, mapping nrpages %lu, dirty %d\n", + // __func__, inode->i_mapping->nrpages, mapping_cap_writeback_dirty(inode->i_mapping)); + + ret = filemap_write_and_wait_range(inode->i_mapping, start, end); + return ret; +} + +static const struct file_operations kvfs_file_operations = { + .read_iter = kvfs_file_read_iter, + .write_iter = generic_file_write_iter, + .fsync = kvfs_fsync, +}; + +static const struct inode_operations kvfs_inode_operations = { +}; + +static int +kvfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev) +{ + struct inode *inode; + int error = -ENOSPC; + struct kvstore *kvs; + struct kvstore_entry *entry; + + kvs = KVSTORE(dir); + BUG_ON(!kvs); + + /* TODO: now we assume kvfs is a flat filesystem, means no directory is supported */ + if (kvs->put(kvs, dentry->d_iname, strlen(dentry->d_iname)+1, NULL, 0)) + return -ENOSPC; + + inode = kvfs_get_inode(dir->i_sb, dir, mode, dev, VM_NORESERVE); + entry = kvs->search(kvs, dentry->d_iname); + BUG_ON(!entry); + entry->private = (void*)inode; + BUG_ON(!inode); + if (inode) { + char *key = kmalloc(strlen(dentry->d_iname)+1, GFP_KERNEL); + + BUG_ON(!key); + strcpy(key, dentry->d_iname); + /* TODO: free i_private */ + inode->i_private = key; + + error = 0; + dir->i_size += KVFS_DIRENT_SIZE; + dir->i_ctime = dir->i_mtime = CURRENT_TIME; + d_instantiate(dentry, inode); + dget(dentry); /* Extra count - pin the dentry in core */ + } + return error; + +#if 0 +out_iput: + printk("DEBUG 2 in %s: error=%d\n", __func__, error); + iput(inode); + return error; +#endif +} + +static int kvfs_create(struct inode *dir, struct dentry *dentry, umode_t mode, + bool excl) +{ + return kvfs_mknod(dir, dentry, mode | S_IFREG, 0); +} + +static struct dentry *kvfs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) +{ + struct kvstore *kvs = KVSTORE(dir); + struct kvstore_entry *entry; + struct inode *inode; + umode_t mode = S_IWUSR|S_IRUSR|S_IWGRP|S_IRGRP|S_IROTH|S_IFREG; + + //printk("DEBUG: %s called: dentry %s, flags 0x%x\n", __func__, dentry->d_iname, flags); + //dump_stack(); + + inode = NULL; + entry = kvs->search(kvs, dentry->d_iname); + if (entry) { + inode = entry->private; + if (!inode) { + char *key; + + key = kmalloc(strlen(dentry->d_iname)+1, GFP_KERNEL); + inode = kvfs_get_inode(dir->i_sb, dir, mode, 0, VM_NORESERVE); + + BUG_ON(!key || !inode); + strcpy(key, dentry->d_iname); + inode->i_size = entry->len; + /* TODO: free i_private */ + inode->i_private = key; + + entry->private = (void*)inode; + } + } + + //d_instantiate(dentry, inode); + return d_splice_alias(inode, dentry); +} + +static const struct inode_operations kvfs_dir_inode_operations = { + .create = kvfs_create, + .lookup = kvfs_lookup, +}; + +static int kvfs_readdir(struct file *file, struct dir_context *ctx) +{ + //loff_t pos = ctx->pos; + struct inode *inode = file_inode(file); + struct kvstore *kvs = KVSTORE(inode); + struct kvstore_entry *entry; + int i; + int fake_ino; + + //printk("DEBUG: %s called\n", __func__); + + /* FIXME */ + if (ctx->pos != 0) + return 0; + + /* FIXME: any way to store inode number in k/v device? */ + fake_ino = 10; + + for (i = 0; i < MAX_KV_ENTRIES; i++) { + entry = &kvs->entries[i]; + if (!entry->valid) + continue; + + if (!dir_emit(ctx, entry->key, strlen(entry->key)+1, fake_ino++, DT_REG)) + return 0; + ctx->pos++; + } + + return 0; +} + +static const struct file_operations kvfs_dir_operations = { + .open = dcache_dir_open, + .release = dcache_dir_close, + .llseek = dcache_dir_lseek, + .read = generic_read_dir, + .iterate = kvfs_readdir, + .fsync = noop_fsync, +}; + +static struct inode *kvfs_get_inode(struct super_block *sb, const struct inode *dir, + umode_t mode, dev_t dev, unsigned long flags) +{ + struct inode *inode; + + inode = new_inode(sb); + if (inode) { + inode->i_ino = get_next_ino(); + inode_init_owner(inode, dir, mode); + inode->i_blocks = 0; + inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; + inode->i_generation = get_seconds(); + + switch (mode & S_IFMT) { + default: + printk("DEBUG: not implemnted yet\n"); + break; + case S_IFREG: + inode->i_mapping->a_ops = &kvfs_aops; + inode->i_op = &kvfs_inode_operations; + inode->i_fop = &kvfs_file_operations; + break; + case S_IFDIR: + inc_nlink(inode); + /* Some things misbehave if size == 0 on a directory */ + inode->i_size = 2 * KVFS_DIRENT_SIZE; + inode->i_op = &kvfs_dir_inode_operations; + inode->i_fop = &kvfs_dir_operations; + break; + } + } + return inode; +} + +static void kvfs_put_super(struct super_block *sb) +{ + pr_debug("kvfs super block destroyed\n"); + + dput(sb->s_root); + kfree(sb->s_fs_info); + sb->s_fs_info = NULL; +} + +static struct super_operations const kvfs_super_ops = { + .put_super = kvfs_put_super, +}; + +static int kvfs_fill_super(struct super_block *sb, void *data, int silent) +{ + struct inode *root = NULL; + struct kvfs_sb_info *sbinfo; + + sbinfo = kmalloc(sizeof(*sbinfo), GFP_KERNEL); + if (!sbinfo) + return -ENOMEM; + sbinfo->kvs = data; + sbinfo->backing_dev_info.name = "kvfs"; + + sb->s_magic = KVFS_MAGIC_NUMBER; + sb->s_fs_info = sbinfo; + sb->s_op = &kvfs_super_ops; + sb->s_bdi = &sbinfo->backing_dev_info; + bdi_init(sb->s_bdi); + + root = kvfs_get_inode(sb, NULL, S_IFDIR | S_IRWXUGO | S_ISVTX, 0, VM_NORESERVE); + if (!root) + { + pr_err("inode allocation failed\n"); + kfree(sbinfo); + return -ENOMEM; + } + + root->i_ino = 0; + root->i_sb = sb; + root->i_atime = root->i_mtime = root->i_ctime = CURRENT_TIME; + inode_init_owner(root, NULL, S_IFDIR); + + sb->s_root = d_make_root(root); + if (!sb->s_root) + { + pr_err("root creation failed\n"); + iput(root); + kfree(sbinfo); + return -ENOMEM; + } + + return 0; +} + +static struct dentry *kvfs_mount(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data) +{ + struct kvstore *kvs; + + /* TODO: other elegant way to find kvstore by dev_name? */ + kvs = kvs_lookup(dev_name); + if (!kvs) + return ERR_PTR(-ENODEV); + return mount_nodev(fs_type, flags, kvs, kvfs_fill_super); +} + +static struct file_system_type kv_fs_type = { + .owner = THIS_MODULE, + .name = "kvfs", + .mount = kvfs_mount, + .kill_sb = kill_litter_super, + .fs_flags = FS_USERNS_MOUNT, +}; +MODULE_ALIAS_FS("kv"); + +static int __init init_kv_fs(void) +{ + int err; + + err = register_filesystem(&kv_fs_type); + return err; +} + +static void __exit exit_kv_fs(void) +{ + unregister_filesystem(&kv_fs_type); +} + +MODULE_AUTHOR("Ming Lin"); +MODULE_DESCRIPTION("key/value fs"); +MODULE_LICENSE("GPL"); +module_init(init_kv_fs) +module_exit(exit_kv_fs) + diff --git a/fs/kvstore.c b/fs/kvstore.c new file mode 100644 index 00000000000000..b33ca4f688bc81 --- /dev/null +++ b/fs/kvstore.c @@ -0,0 +1,202 @@ +/* A super simple K/V device simulation */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/miscdevice.h> +#include <linux/fs.h> +#include <linux/spinlock.h> +#include <linux/uaccess.h> +#include "kvstore.h" + +static DEFINE_SPINLOCK(kvs_list_lock); +static LIST_HEAD(kvs_list); +static struct kvstore simulate_kv_store; + +/* TODO: some way to find kvstore by dev_name? */ +struct kvstore *kvs_lookup(const char *dev_name) +{ + return &simulate_kv_store; +} +EXPORT_SYMBOL(kvs_lookup); + +static struct kvstore_entry *kv_find_key(struct kvstore *kvs, char *key) +{ + struct kvstore_entry *entry; + int i; + + for (i = 0; i < MAX_KV_ENTRIES; i++) { + entry = &kvs->entries[i]; + if (!entry->valid) + continue; + if (!strcmp(key, entry->key)) + return entry; + } + + return NULL; +} + +static struct kvstore_entry *kv_find_empty(struct kvstore *kvs) +{ + struct kvstore_entry *entry; + int i; + + for (i = 0; i < MAX_KV_ENTRIES; i++) { + entry = &kvs->entries[i]; + if (!entry->valid) + return entry; + } + + return NULL; +} + +static int simulate_get(struct kvstore *kvs, char *key, char *value, int len) +{ + struct kvstore_entry *entry; + + entry = kv_find_key(kvs, key); + if (!entry) + return KV_NOT_FOUND; + + //if (len != entry->len) + // return KV_WRONG_SIZE; + + printk("KVSTORE: GET() called: len %d\n", entry->len); + + memcpy(value, entry->value, entry->len); + return KV_SUCCESS; +} + +static int simulate_put(struct kvstore *kvs, char *key, int key_len, char *value, int value_len) +{ + struct kvstore_entry *entry; + + //printk("LINMING: %s called: key %s, value_len %d\n", __func__, key, value_len); + + if (key_len > MAX_KEY_LEN || value_len > MAX_VALUE_LEN) + return KV_WRONG_SIZE; + + BUG_ON(!value && value_len); + + entry = kv_find_key(kvs, key); + if (entry) { /* Just update it */ + if (value) + memcpy(entry->value, value, value_len); + entry->len = value_len; + return KV_SUCCESS; + } + + if (kvs->num == MAX_KV_ENTRIES) + return KV_FULL; + + entry = kv_find_empty(kvs); + BUG_ON(!entry); + memcpy(entry->key, key, key_len); + if (value) + memcpy(entry->value, value, value_len); + entry->len = value_len; + entry->valid = 1; + + kvs->num++; + + return KV_SUCCESS; +} + +static int simulate_del(struct kvstore *kvs, char *key) +{ + struct kvstore_entry *entry; + entry = kv_find_key(kvs, key); + + if (!entry) + return KV_NOT_FOUND; + + entry->valid = 0; + kvs->num--; + return KV_SUCCESS; +} + +static struct kvstore_entry *simulate_search(struct kvstore *kvs, char *key) +{ + return kv_find_key(kvs, key); +} + +static int simulate_kv_open(struct inode *inode, struct file *f) +{ + struct kvstore *kvs; + int instance = iminor(inode); + int ret = -ENODEV; + + spin_lock(&kvs_list_lock); + list_for_each_entry(kvs, &kvs_list, node) { + if (kvs->instance == instance) { + f->private_data = kvs; + ret = 0; + break; + } + } + spin_unlock(&kvs_list_lock); + + return ret; +} + +static long simulate_kv_ioctl(struct file *f, unsigned int cmd, unsigned long arg) +{ + //struct kvstore *kvs = f->private_data; + int is_kv = 1; /* report to userspace: yes, I'm a valid k/v device */ + + switch (cmd) { + case KV_IOCTL_IS_KV: + copy_to_user((void __user *)arg, &is_kv, sizeof(int)); + return 0; + /* TODO: k/v ioctl GET/PUT/DEL */ + default: + return -ENODEV; + } +} + +static const struct file_operations simulate_kv_fops = { + .open = simulate_kv_open, + .unlocked_ioctl = simulate_kv_ioctl, + .compat_ioctl = simulate_kv_ioctl, + .owner = THIS_MODULE, +}; + +static struct miscdevice simulate_kv_device = { + .minor = 0, + .name = "kv0", + .fops = &simulate_kv_fops, +}; + +static void add_debug_kv(struct kvstore *kvs) +{ + char key[] = "key1"; + char value[] = "blablabla\n"; + + kvs->put(kvs, key, strlen(key)+1, value, strlen(value)+1); +} + +static int __init kv_store_init(void) +{ + simulate_kv_store.num = 0; + simulate_kv_store.instance = 0; + INIT_LIST_HEAD(&simulate_kv_store.node); + simulate_kv_store.get = simulate_get; + simulate_kv_store.put = simulate_put; + simulate_kv_store.del = simulate_del; + simulate_kv_store.search = simulate_search; + list_add(&simulate_kv_store.node, &kvs_list); + + add_debug_kv(&simulate_kv_store); + + return misc_register(&simulate_kv_device); +} + +static void __exit kv_store_exit(void) +{ + misc_deregister(&simulate_kv_device); +} + +MODULE_AUTHOR("Ming Lin"); +MODULE_DESCRIPTION("key/value device simulation"); +MODULE_LICENSE("GPL"); +module_init(kv_store_init); +module_exit(kv_store_exit); diff --git a/fs/kvstore.h b/fs/kvstore.h new file mode 100644 index 00000000000000..26fa6f02ab5393 --- /dev/null +++ b/fs/kvstore.h @@ -0,0 +1,41 @@ +#ifndef __KV_STORE_H_ +#define __KV_STORE_H_ + +#define MAX_KEY_LEN 64 +#define MAX_VALUE_LEN (8*1024) +#define MAX_KV_ENTRIES 10 + +#define KV_SUCCESS 0 +#define KV_FULL 1 +#define KV_WRONG_SIZE 2 +#define KV_NOT_FOUND 3 + +#define KV_IOCTL_IS_KV 1 + +#include <linux/list.h> + +struct kvstore_entry { + char key[MAX_KEY_LEN]; + char value[MAX_VALUE_LEN]; + int len; /* value length */ + int valid; + void *private; +}; + +struct kvstore { + /*TODO: add spinlock */ + + struct kvstore_entry entries[MAX_KV_ENTRIES]; + int num; + int (*get)(struct kvstore *kvs, char *key, char *value, int len); + int (*put)(struct kvstore *kvs, char *key, int key_len, char *value, int len); + int (*del)(struct kvstore *kvs, char *key); + struct kvstore_entry *(*search)(struct kvstore *kvs, char *key); + + struct list_head node; + int instance; +}; + +extern struct kvstore *kvs_lookup(const char *dev_name); + +#endif |