scoutfs: add item cache key ioctls

These ioctls let userspace see the items and ranges that are cached.

Signed-off-by: Zach Brown <zab@versity.com>
This commit is contained in:
Zach Brown
2017-06-27 14:04:38 -07:00
parent 5f5729b2a4
commit 2eecbbe78a
4 changed files with 215 additions and 0 deletions
+69
View File
@@ -433,6 +433,73 @@ static long scoutfs_ioc_stat_more(struct file *file, unsigned long arg)
return 0;
}
static long scoutfs_ioc_item_cache_keys(struct file *file, unsigned long arg)
{
struct super_block *sb = file_inode(file)->i_sb;
struct scoutfs_ioctl_item_cache_keys ick;
struct scoutfs_key_buf *key;
struct page *page;
unsigned bytes;
void *buf;
int total;
int ret;
if (copy_from_user(&ick, (void __user *)arg, sizeof(ick)))
return -EFAULT;
if ((!!ick.key_ptr != !!ick.key_len) ||
ick.key_len > SCOUTFS_MAX_KEY_SIZE ||
ick.which > SCOUTFS_IOC_ITEM_CACHE_KEYS_RANGES)
return -EINVAL;
/* don't overflow signed 32bit syscall return longs */
ick.buf_len = min_t(u64, ick.buf_len, S32_MAX);
key = scoutfs_key_alloc(sb, SCOUTFS_MAX_KEY_SIZE);
page = alloc_page(GFP_KERNEL);
if (!key || !page) {
ret = -ENOMEM;
goto out;
}
if (copy_from_user(key->data, (void __user *)ick.key_ptr, ick.key_len)) {
ret = -EFAULT;
goto out;
}
scoutfs_key_init_buf_len(key, key->data, ick.key_len,
SCOUTFS_MAX_KEY_SIZE);
scoutfs_key_inc(key);
buf = page_address(page);
total = 0;
ret = 0;
while (ick.buf_len) {
bytes = min_t(u64, ick.buf_len, PAGE_SIZE);
if (ick.which == SCOUTFS_IOC_ITEM_CACHE_KEYS_ITEMS)
ret = scoutfs_item_copy_keys(sb, key, buf, bytes);
else
ret = scoutfs_item_copy_range_keys(sb, key, buf, bytes);
if (ret > 0 && copy_to_user((void __user *)ick.buf_ptr, buf, ret))
ret = -EFAULT;
if (ret <= 0)
break;
ick.buf_len -= ret;
ick.buf_ptr += ret;
total += ret;
ret = 0;
}
out:
scoutfs_key_free(sb, key);
if (page)
__free_page(page);
return ret ?: total;
}
long scoutfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
switch (cmd) {
@@ -446,6 +513,8 @@ long scoutfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
return scoutfs_ioc_stage(file, arg);
case SCOUTFS_IOC_STAT_MORE:
return scoutfs_ioc_stat_more(file, arg);
case SCOUTFS_IOC_ITEM_CACHE_KEYS:
return scoutfs_ioc_item_cache_keys(file, arg);
}
return -ENOTTY;
+16
View File
@@ -166,4 +166,20 @@ struct scoutfs_ioctl_stat_more {
#define SCOUTFS_IOC_STAT_MORE _IOW(SCOUTFS_IOCTL_MAGIC, 7, \
struct scoutfs_ioctl_stat_more)
struct scoutfs_ioctl_item_cache_keys {
__u64 key_ptr;
__u64 key_len;
__u64 buf_ptr;
__u64 buf_len;
__u8 which;
} __packed;
enum {
SCOUTFS_IOC_ITEM_CACHE_KEYS_ITEMS = 0,
SCOUTFS_IOC_ITEM_CACHE_KEYS_RANGES,
};
#define SCOUTFS_IOC_ITEM_CACHE_KEYS _IOW(SCOUTFS_IOCTL_MAGIC, 8, \
struct scoutfs_ioctl_item_cache_keys)
#endif
+124
View File
@@ -457,6 +457,16 @@ static struct cached_range *rb_first_rng(struct rb_root *root)
return NULL;
}
static struct cached_range *rb_next_rng(struct cached_range *rng)
{
struct rb_node *node;
if (rng && (node = rb_next(&rng->node)))
return container_of(node, struct cached_range, node);
return NULL;
}
static struct cached_range *walk_ranges(struct rb_root *root,
struct scoutfs_key_buf *key,
struct cached_range **prev,
@@ -1849,6 +1859,120 @@ out:
return min_t(unsigned long, cac->lru_nr, INT_MAX);
}
static void *copy_key_with_len(void *data, struct scoutfs_key_buf *key)
{
u16 len = key->key_len;
memcpy(data, &len, sizeof(len));
data += sizeof(len);
memcpy(data, key->data, len);
return data + len;
}
/*
* Copy the next cached ranges starting with the key into the caller's
* buffer. Each range copied by storing each keys size in a u16
* followed by the binary key data. The number of bytes of full copied
* ranges is returned. The caller's key is incremented past the last
* key returned so that they can iterate without worrying about
* examining the returned keys.
*/
int scoutfs_item_copy_range_keys(struct super_block *sb,
struct scoutfs_key_buf *key, void *data,
unsigned len)
{
struct scoutfs_sb_info *sbi = SCOUTFS_SB(sb);
struct item_cache *cac = sbi->item_cache;
struct rb_node *node = cac->ranges.rb_node;
struct cached_range *next = NULL;
struct scoutfs_key_buf *last = NULL;
struct cached_range *rng;
unsigned long flags;
unsigned bytes;
int ret = 0;
int cmp;
spin_lock_irqsave(&cac->lock, flags);
while (node) {
rng = container_of(node, struct cached_range, node);
cmp = scoutfs_key_compare_ranges(key, key,
rng->start, rng->end);
if (cmp < 0) {
next = rng;
node = node->rb_left;
} else if (cmp > 0) {
node = node->rb_right;
} else {
next = rng;
break;
}
}
for (rng = next; rng; rng = rb_next_rng(rng)) {
bytes = 2 + rng->start->key_len + 2 + rng->end->key_len;
if (len < bytes)
break;
data = copy_key_with_len(data, rng->start);
data = copy_key_with_len(data, rng->end);
len -= bytes;
ret += bytes;
last = rng->end;
}
if (last) {
scoutfs_key_copy(key, last);
scoutfs_key_inc(key);
}
spin_unlock_irqrestore(&cac->lock, flags);
return ret;
}
/* like copy_range_keys, but for present items */
int scoutfs_item_copy_keys(struct super_block *sb, struct scoutfs_key_buf *key,
void *data, unsigned len)
{
struct scoutfs_sb_info *sbi = SCOUTFS_SB(sb);
struct item_cache *cac = sbi->item_cache;
struct scoutfs_key_buf *last = NULL;
struct cached_item *item = NULL;
unsigned long flags;
unsigned bytes;
int ret = 0;
spin_lock_irqsave(&cac->lock, flags);
for (item = next_item(&cac->items, key); item; item = rb_next_item(item)) {
if (item->deletion)
continue;
bytes = 2 + item->key->key_len;
if (len < bytes)
break;
data = copy_key_with_len(data, item->key);
len -= bytes;
ret += bytes;
last = item->key;
}
if (last) {
scoutfs_key_copy(key, last);
scoutfs_key_inc(key);
}
spin_unlock_irqrestore(&cac->lock, flags);
return ret;
}
int scoutfs_item_setup(struct super_block *sb)
{
struct scoutfs_sb_info *sbi = SCOUTFS_SB(sb);
+6
View File
@@ -57,6 +57,12 @@ int scoutfs_item_invalidate(struct super_block *sb,
struct scoutfs_key_buf *start,
struct scoutfs_key_buf *end);
int scoutfs_item_copy_range_keys(struct super_block *sb,
struct scoutfs_key_buf *key, void *data,
unsigned len);
int scoutfs_item_copy_keys(struct super_block *sb, struct scoutfs_key_buf *key,
void *data, unsigned len);
int scoutfs_item_setup(struct super_block *sb);
void scoutfs_item_destroy(struct super_block *sb);