From 1ad479a1af8fb8bcf2d3120f8972915bd31ba185 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Wed, 1 Feb 2017 10:02:27 -0800 Subject: [PATCH] Add ephemeral items Ephemeral items exist to reference external values. They're going to be used by the page cache to reference dirty pages for writeback. Signed-off-by: Zach Brown --- kmod/src/counters.h | 2 ++ kmod/src/item.c | 84 +++++++++++++++++++++++++++++++++++++++++---- kmod/src/item.h | 6 ++++ 3 files changed, 85 insertions(+), 7 deletions(-) diff --git a/kmod/src/counters.h b/kmod/src/counters.h index 186a57d7..1ccb08d4 100644 --- a/kmod/src/counters.h +++ b/kmod/src/counters.h @@ -25,6 +25,8 @@ EXPAND_COUNTER(compact_segment_read) \ EXPAND_COUNTER(compact_segment_written) \ EXPAND_COUNTER(item_create) \ + EXPAND_COUNTER(item_create_ephemeral) \ + EXPAND_COUNTER(item_update_ephemeral) \ EXPAND_COUNTER(item_lookup_hit) \ EXPAND_COUNTER(item_lookup_miss) \ EXPAND_COUNTER(item_delete) \ diff --git a/kmod/src/item.c b/kmod/src/item.c index 23516168..e5b5970f 100644 --- a/kmod/src/item.c +++ b/kmod/src/item.c @@ -64,7 +64,8 @@ struct cached_item { }; long dirty; - unsigned deletion:1; + unsigned deletion:1, + ephemeral:1; struct scoutfs_key_buf *key; @@ -87,7 +88,8 @@ static void free_item(struct super_block *sb, struct cached_item *item) { if (!IS_ERR_OR_NULL(item)) { scoutfs_key_free(sb, item->key); - scoutfs_kvec_kfree(item->val); + if (!item->ephemeral) + scoutfs_kvec_kfree(item->val); kfree(item); } } @@ -344,7 +346,7 @@ static void erase_item(struct super_block *sb, struct item_cache *cac, * The caller is responsible for marking the newly inserted item dirty. */ static int insert_item(struct super_block *sb, struct item_cache *cac, - struct cached_item *ins) + struct cached_item *ins, bool overwrite) { struct rb_root *root = &cac->items; struct cached_item *item; @@ -369,7 +371,7 @@ restart: item->dirty |= RIGHT_DIRTY; node = &(*node)->rb_right; } else { - if (!item->deletion) + if (!item->deletion && !overwrite) return -EEXIST; /* sadly there's no augmented replace */ @@ -767,7 +769,7 @@ int scoutfs_item_create(struct super_block *sb, struct scoutfs_key_buf *key, return -ENOMEM; spin_lock_irqsave(&cac->lock, flags); - ret = insert_item(sb, cac, item); + ret = insert_item(sb, cac, item, false); if (!ret) { scoutfs_inc_counter(sb, item_create); mark_item_dirty(cac, item); @@ -780,6 +782,74 @@ int scoutfs_item_create(struct super_block *sb, struct scoutfs_key_buf *key, return ret; } +/* + * Ephemeral items are slightly magical and used to track file contents + * without copying the data into an allocated value. + * + * Their value kvec clones the callers which means they reference + * external data. They're freed after items are copied into segments so + * that callers can know that no items reference their structures after + * a commit finishes. + * + * They forcefully clobber any existing item at their key without + * reading the existing item. + */ +int scoutfs_item_create_ephemeral(struct super_block *sb, + struct scoutfs_key_buf *key, + struct kvec *val) +{ + struct scoutfs_sb_info *sbi = SCOUTFS_SB(sb); + struct item_cache *cac = sbi->item_cache; + struct cached_item *item; + unsigned long flags; + int ret; + + item = alloc_item(sb, key, NULL); + if (!item) + return -ENOMEM; + + scoutfs_kvec_clone(item->val, val); + item->ephemeral = 1; + + spin_lock_irqsave(&cac->lock, flags); + + ret = insert_item(sb, cac, item, true); + BUG_ON(ret); + + scoutfs_inc_counter(sb, item_create_ephemeral); + mark_item_dirty(cac, item); + + spin_unlock_irqrestore(&cac->lock, flags); + + return ret; +} + +/* + * Update the value for an ephemeral item if it exists. + */ +void scoutfs_item_update_ephemeral(struct super_block *sb, + struct scoutfs_key_buf *key, + struct kvec *val) +{ + struct scoutfs_sb_info *sbi = SCOUTFS_SB(sb); + struct item_cache *cac = sbi->item_cache; + struct cached_item *item; + unsigned long flags; + + spin_lock_irqsave(&cac->lock, flags); + + item = find_item(sb, &cac->items, key); + if (item && item->ephemeral) { + trace_printk("updating ephemeral item %p\n", item); + scoutfs_inc_counter(sb, item_update_ephemeral); + clear_item_dirty(cac, item); + scoutfs_kvec_clone(item->val, val); + mark_item_dirty(cac, item); + } + + spin_unlock_irqrestore(&cac->lock, flags); +} + /* * Allocate an item with the key and value and add it to the list of * items to be inserted as a batch later. The caller adds in sort order @@ -852,7 +922,7 @@ int scoutfs_item_insert_batch(struct super_block *sb, struct list_head *list, list_for_each_entry_safe(item, tmp, list, entry) { list_del(&item->entry); - if (insert_item(sb, cac, item)) + if (insert_item(sb, cac, item, false)) list_add(&item->entry, list); } @@ -1314,7 +1384,7 @@ int scoutfs_item_dirty_seg(struct super_block *sb, struct scoutfs_segment *seg) del = item; item = next_dirty(item); - if (del->deletion) + if (del->deletion || del->ephemeral) erase_item(sb, cac, del); nr_items--; diff --git a/kmod/src/item.h b/kmod/src/item.h index 4ca4d088..6021e3c3 100644 --- a/kmod/src/item.h +++ b/kmod/src/item.h @@ -23,9 +23,15 @@ int scoutfs_item_insert(struct super_block *sb, struct scoutfs_key_buf *key, struct kvec *val); int scoutfs_item_create(struct super_block *sb, struct scoutfs_key_buf *key, struct kvec *val); +int scoutfs_item_create_ephemeral(struct super_block *sb, + struct scoutfs_key_buf *key, + struct kvec *val); int scoutfs_item_dirty(struct super_block *sb, struct scoutfs_key_buf *key); int scoutfs_item_update(struct super_block *sb, struct scoutfs_key_buf *key, struct kvec *val); +void scoutfs_item_update_ephemeral(struct super_block *sb, + struct scoutfs_key_buf *key, + struct kvec *val); void scoutfs_item_delete_dirty(struct super_block *sb, struct scoutfs_key_buf *key); int scoutfs_item_delete_many(struct super_block *sb,