[PATCH 2/9] nilfs2: add simple cache for modifications to SUFILE

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

 



This patch adds a simple, small cache that can be used to accumulate
modifications to SUFILE entries. This is for example useful for
keeping track of reclaimable blocks, because most of the
modifications consist of small increments or decrements. By adding
these up and temporarily storing them in a small cache, the
performance can be improved. Additionally lock contention is
reduced.

Signed-off-by: Andreas Rohner <andreas.rohner@xxxxxxx>
---
 fs/nilfs2/sufile.c | 178 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/nilfs2/sufile.h |  44 +++++++++++++
 2 files changed, 222 insertions(+)

diff --git a/fs/nilfs2/sufile.c b/fs/nilfs2/sufile.c
index 1e8cac6..a369c30 100644
--- a/fs/nilfs2/sufile.c
+++ b/fs/nilfs2/sufile.c
@@ -1168,6 +1168,184 @@ out_sem:
 }
 
 /**
+ * nilfs_sufile_mc_init - inits segusg modification cache
+ * @mc: modification cache
+ * @capacity: maximum capacity of the mod cache
+ *
+ * Description: Allocates memory for an array of nilfs_sufile_mod structures
+ * according to @capacity. This memory must be freed with
+ * nilfs_sufile_mc_destroy().
+ *
+ * Return Value: On success, 0 is returned. On error, one of the following
+ * negative error codes is returned.
+ *
+ * %-ENOMEM - Insufficient amount of memory available.
+ *
+ * %-EINVAL - Invalid capacity.
+ */
+int nilfs_sufile_mc_init(struct nilfs_sufile_mod_cache *mc, size_t capacity)
+{
+	mc->mc_capacity = capacity;
+	if (!capacity)
+		return -EINVAL;
+
+	mc->mc_mods = kmalloc(capacity * sizeof(struct nilfs_sufile_mod),
+			      GFP_KERNEL);
+	if (!mc->mc_mods)
+		return -ENOMEM;
+
+	mc->mc_size = 0;
+
+	return 0;
+}
+
+/**
+ * nilfs_sufile_mc_add - add signed value to segusg modification cache
+ * @mc: modification cache
+ * @segnum: segment number
+ * @value: signed value (can be positive and negative)
+ *
+ * Description: nilfs_sufile_mc_add() tries to add a pair of @segnum and
+ * @value to the modification cache. If the cache already contains a
+ * segment number equal to @segnum, then @value is simply added to the
+ * existing value. This way thousands of small modifications can be
+ * accumulated into one value. If @segnum cannot be found and the
+ * capacity allows it, a new element is added to the cache. If the
+ * capacity is reached an error value is returned.
+ *
+ * Return Value: On success, 0 is returned. On error, one of the following
+ * negative error codes is returned.
+ *
+ * %-ENOSPC - The mod cache has reached its capacity and must be flushed.
+ */
+static inline int nilfs_sufile_mc_add(struct nilfs_sufile_mod_cache *mc,
+				      __u64 segnum, __s64 value)
+{
+	struct nilfs_sufile_mod *mods = mc->mc_mods;
+	int i;
+
+	for (i = 0; i < mc->mc_size; ++i, ++mods) {
+		if (mods->m_segnum == segnum) {
+			mods->m_value += value;
+			return 0;
+		}
+	}
+
+	if (mc->mc_size < mc->mc_capacity) {
+		mods->m_segnum = segnum;
+		mods->m_value = value;
+		mc->mc_size++;
+		return 0;
+	}
+
+	return -ENOSPC;
+}
+
+/**
+ * nilfs_sufile_mc_clear - set mc_size to 0
+ * @mc: modification cache
+ *
+ * Description: nilfs_sufile_mc_clear() sets mc_size to 0, which enables
+ * nilfs_sufile_mc_add() to overwrite the elements in @mc.
+ */
+static inline void nilfs_sufile_mc_clear(struct nilfs_sufile_mod_cache *mc)
+{
+	mc->mc_size = 0;
+}
+
+/**
+ * nilfs_sufile_mc_reset - clear cache and add one element
+ * @mc: modification cache
+ * @segnum: segment number
+ * @value: signed value (can be positive and negative)
+ *
+ * Description: Clears the modification cache in @mc and adds a new pair of
+ * @segnum and @value to it at the same time.
+ */
+static inline void nilfs_sufile_mc_reset(struct nilfs_sufile_mod_cache *mc,
+					 __u64 segnum, __s64 value)
+{
+	struct nilfs_sufile_mod *mods = mc->mc_mods;
+
+	mods->m_segnum = segnum;
+	mods->m_value = value;
+	mc->mc_size = 1;
+}
+
+/**
+ * nilfs_sufile_mc_flush - flush modification cache
+ * @sufile: inode of segment usage file
+ * @mc: modification cache
+ * @dofunc: primitive operation for the update
+ *
+ * Description: nilfs_sufile_mc_flush() flushes the cached modifications
+ * and applies them to the segment usages on disk. It persists the cached
+ * changes, by calling @dofunc for every element in the cache. @dofunc also
+ * determines the interpretation of the cached values and how they should
+ * be applied to the corresponding segment usage entries.
+ *
+ * Return Value: On success, zero is returned.  On error, one of the
+ * following negative error codes is returned.
+ *
+ * %-EIO - I/O error.
+ *
+ * %-ENOMEM - Insufficient amount of memory available.
+ *
+ * %-ENOENT - Given segment usage is in hole block
+ *
+ * %-EINVAL - Invalid segment usage number
+ */
+static inline int nilfs_sufile_mc_flush(struct inode *sufile,
+					struct nilfs_sufile_mod_cache *mc,
+					void (*dofunc)(struct inode *,
+						struct nilfs_sufile_mod *,
+						struct buffer_head *,
+						struct buffer_head *))
+{
+	return nilfs_sufile_updatev(sufile, mc->mc_mods,
+				    sizeof(struct nilfs_sufile_mod),
+				    offsetof(struct nilfs_sufile_mod, m_segnum),
+				    mc->mc_size, 0, NULL, (void *)dofunc);
+}
+
+/**
+ * nilfs_sufile_mc_update - immediately applies modification
+ * @sufile: inode of segment usage file
+ * @segnum: segment number
+ * @value: signed value (can be positive and negative)
+ * @dofunc: primitive operation for the update
+ *
+ * Description: nilfs_sufile_mc_update() is a helper function, that
+ * creates a temporary nilfs_sufile_mod structure out of @segnum and @value
+ * and immediately flushes it using @dofunc, without the use of a
+ * modification cache.
+ *
+ * Return Value: On success, zero is returned.  On error, one of the
+ * following negative error codes is returned.
+ *
+ * %-EIO - I/O error.
+ *
+ * %-ENOMEM - Insufficient amount of memory available.
+ *
+ * %-ENOENT - Given segment usage is in hole block
+ *
+ * %-EINVAL - Invalid segment usage number
+ */
+static inline int nilfs_sufile_mc_update(struct inode *sufile,
+					 __u64 segnum, __s64 value,
+					 void (*dofunc)(struct inode *,
+						struct nilfs_sufile_mod *,
+						struct buffer_head *,
+						struct buffer_head *))
+{
+	struct nilfs_sufile_mod m = {.m_segnum = segnum, .m_value = value};
+
+	return nilfs_sufile_update(sufile, &m,
+				   offsetof(struct nilfs_sufile_mod, m_segnum),
+				   0, (void *)dofunc);
+}
+
+/**
  * nilfs_sufile_read - read or get sufile inode
  * @sb: super block instance
  * @susize: size of a segment usage entry
diff --git a/fs/nilfs2/sufile.h b/fs/nilfs2/sufile.h
index 2df6c71..c446325 100644
--- a/fs/nilfs2/sufile.h
+++ b/fs/nilfs2/sufile.h
@@ -146,4 +146,48 @@ static inline int nilfs_sufile_set_error(struct inode *sufile, __u64 segnum)
 				   (void *)nilfs_sufile_do_set_error);
 }
 
+#define NILFS_SUFILE_MC_SIZE_DEFAULT	5
+#define NILFS_SUFILE_MC_SIZE_EXT	10
+
+/**
+ * struct nilfs_sufile_mod - segment usage modification
+ * @m_segnum: segment number
+ * @m_value: signed value that gets added to respective segusg field
+ */
+struct nilfs_sufile_mod {
+	__u64 m_segnum;
+	__s64 m_value;
+};
+
+/**
+ * struct nilfs_sufile_mod_cache - segment usage modification cache
+ * @mc_mods: array of modifications to segments
+ * @mc_capacity: maximum number of elements that fit in @mc_mods
+ * @mc_size: number of elements currently filled with valid data
+ */
+struct nilfs_sufile_mod_cache {
+	struct nilfs_sufile_mod *mc_mods;
+	size_t mc_capacity;
+	size_t mc_size;
+};
+
+int nilfs_sufile_mc_init(struct nilfs_sufile_mod_cache *, size_t);
+
+/**
+ * nilfs_sufile_mc_destroy - destroy segusg modification cache
+ * @mc: modification cache
+ *
+ * Description: Releases the memory allocated by nilfs_sufile_mc_init and
+ * sets the size and capacity to 0. @mc should not be used after a call to
+ * this function.
+ */
+static inline void nilfs_sufile_mc_destroy(struct nilfs_sufile_mod_cache *mc)
+{
+	if (mc) {
+		kfree(mc->mc_mods);
+		mc->mc_capacity = 0;
+		mc->mc_size = 0;
+	}
+}
+
 #endif	/* _NILFS_SUFILE_H */
-- 
2.3.0

--
To unsubscribe from this list: send the line "unsubscribe linux-nilfs" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Filesystem Development]     [Linux BTRFS]     [Linux CIFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux SCSI]

  Powered by Linux