[PATCH 1/2] mbcache: Add functions to invalidate entries and wait for entry users

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Add functions which allow for invalidating entry in the cache but
keeping the last reference and for waiting on remaining users of the
cache entry. These functions will be used by ext4 to fix races with
xattr block sharing.

CC: stable@xxxxxxxxxxxxxxx
Fixes: 82939d7999df ("ext4: convert to mbcache2")
Signed-off-by: Jan Kara <jack@xxxxxxx>
---
 fs/mbcache.c            | 47 ++++++++++++++++++++++++++++++++++++-----
 include/linux/mbcache.h | 11 +++++++++-
 2 files changed, 52 insertions(+), 6 deletions(-)

diff --git a/fs/mbcache.c b/fs/mbcache.c
index 97c54d3a2227..b81ea7c6fa21 100644
--- a/fs/mbcache.c
+++ b/fs/mbcache.c
@@ -125,6 +125,21 @@ void __mb_cache_entry_free(struct mb_cache_entry *entry)
 }
 EXPORT_SYMBOL(__mb_cache_entry_free);
 
+/*
+ * mb_cache_entry_wait_unused - wait to be the last user of the entry
+ *
+ * @entry - entry to work on
+ *
+ * Wait to be the last user of the entry.
+ */
+void mb_cache_entry_wait_unused(struct mb_cache_entry *entry)
+{
+	WARN_ON_ONCE(!list_empty(&entry->e_list));
+	WARN_ON_ONCE(!hlist_bl_unhashed(&entry->e_hash_list));
+	wait_var_event(&entry->e_refcnt, atomic_read(&entry->e_refcnt) == 1);
+}
+EXPORT_SYMBOL(mb_cache_entry_wait_unused);
+
 static struct mb_cache_entry *__entry_find(struct mb_cache *cache,
 					   struct mb_cache_entry *entry,
 					   u32 key)
@@ -217,14 +232,18 @@ struct mb_cache_entry *mb_cache_entry_get(struct mb_cache *cache, u32 key,
 }
 EXPORT_SYMBOL(mb_cache_entry_get);
 
-/* mb_cache_entry_delete - remove a cache entry
+/* mb_cache_entry_invalidate - invalidate mbcache entry
  * @cache - cache we work with
  * @key - key
  * @value - value
  *
- * Remove entry from cache @cache with key @key and value @value.
+ * Invalidate entry in cache @cache with key @key and value @value. The entry
+ * is removed from the hash and LRU so there can be no new users of it. The
+ * invalidated entry is returned so that the caller can drop the last
+ * reference (perhaps after waiting for remaining users).
  */
-void mb_cache_entry_delete(struct mb_cache *cache, u32 key, u64 value)
+struct mb_cache_entry *mb_cache_entry_invalidate(struct mb_cache *cache,
+						 u32 key, u64 value)
 {
 	struct hlist_bl_node *node;
 	struct hlist_bl_head *head;
@@ -246,11 +265,29 @@ void mb_cache_entry_delete(struct mb_cache *cache, u32 key, u64 value)
 				atomic_dec(&entry->e_refcnt);
 			}
 			spin_unlock(&cache->c_list_lock);
-			mb_cache_entry_put(cache, entry);
-			return;
+			return entry;
 		}
 	}
 	hlist_bl_unlock(head);
+
+	return NULL;
+}
+EXPORT_SYMBOL(mb_cache_entry_invalidate);
+
+/* mb_cache_entry_delete - delete mbcache entry
+ * @cache - cache we work with
+ * @key - key
+ * @value - value
+ *
+ * Delete entry in cache @cache with key @key and value @value.
+ */
+void mb_cache_entry_delete(struct mb_cache *cache, u32 key, u64 value)
+{
+	struct mb_cache_entry *entry;
+
+	entry = mb_cache_entry_invalidate(cache, key, value);
+	if (entry)
+		mb_cache_entry_put(cache, entry);
 }
 EXPORT_SYMBOL(mb_cache_entry_delete);
 
diff --git a/include/linux/mbcache.h b/include/linux/mbcache.h
index 20f1e3ff6013..4e6a5e05e78b 100644
--- a/include/linux/mbcache.h
+++ b/include/linux/mbcache.h
@@ -30,15 +30,24 @@ void mb_cache_destroy(struct mb_cache *cache);
 int mb_cache_entry_create(struct mb_cache *cache, gfp_t mask, u32 key,
 			  u64 value, bool reusable);
 void __mb_cache_entry_free(struct mb_cache_entry *entry);
+void mb_cache_entry_wait_unused(struct mb_cache_entry *entry);
 static inline int mb_cache_entry_put(struct mb_cache *cache,
 				     struct mb_cache_entry *entry)
 {
-	if (!atomic_dec_and_test(&entry->e_refcnt))
+	unsigned int cnt;
+
+	cnt = atomic_dec_return(&entry->e_refcnt);
+	if (cnt > 0) {
+		if (cnt == 1)
+			wake_up_var(&entry->e_refcnt);
 		return 0;
+	}
 	__mb_cache_entry_free(entry);
 	return 1;
 }
 
+struct mb_cache_entry *mb_cache_entry_invalidate(struct mb_cache *cache,
+						 u32 key, u64 value);
 void mb_cache_entry_delete(struct mb_cache *cache, u32 key, u64 value);
 struct mb_cache_entry *mb_cache_entry_get(struct mb_cache *cache, u32 key,
 					  u64 value);
-- 
2.35.3




[Index of Archives]     [Linux Kernel]     [Kernel Development Newbies]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite Hiking]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux