mirror of
https://github.com/versity/scoutfs.git
synced 2026-07-29 19:43:13 +00:00
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:
@@ -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;
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user