[RFC PATCH 3/5] gfs2: Add a dynamic buffer backed by a vector of pages

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

 



This patch adds a new buffer called 'vbuf' that is backed by a
vector of pages. It is dynamic and can be expanded as needed with
low overhead.

Signed-off-by: Abhi Das <adas@xxxxxxxxxx>
---
 fs/gfs2/util.c | 299 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/gfs2/util.h |  43 +++++++++
 2 files changed, 342 insertions(+)

diff --git a/fs/gfs2/util.c b/fs/gfs2/util.c
index 86d2035..7345489 100644
--- a/fs/gfs2/util.c
+++ b/fs/gfs2/util.c
@@ -263,3 +263,302 @@ int gfs2_io_error_bh_i(struct gfs2_sbd *sdp, struct buffer_head *bh,
 	return rv;
 }
 
+/*
+ * Fast vector-of-pages backed buffer
+ */
+
+static int vp_alloc_pages(struct vp_ctx *vpx, int start, int end)
+{
+	int i;
+
+	for (i = start; i < end; i++) {
+		vpx->vp_pages[i] = alloc_page(GFP_KERNEL | GFP_NOFS);
+		if (vpx->vp_pages[i] == NULL)
+			goto free;
+	}
+	return 0;
+free:
+	for (i = start; i < end; i++)
+		if (vpx->vp_pages[i]) {
+			__free_page(vpx->vp_pages[i]);
+			vpx->vp_pages[i] = NULL;
+		}
+	return -ENOMEM;
+}
+
+static void vp_free_pages(struct vp_ctx *vpx)
+{
+	int i;
+
+	for (i = 0; i < vpx->vp_size; i++)
+		if (vpx->vp_pages[i]) {
+			__free_page(vpx->vp_pages[i]);
+			vpx->vp_pages[i] = NULL;
+		}
+}
+
+static int vp_extend(struct vp_ctx *vpx, int size)
+{
+	struct gfs2_sbd *sdp = vpx->vp_sdp;
+
+	/* first make room for more pointers */
+	if (size <= 0)
+		return -EINVAL;
+
+	vpx->vp_pages = krealloc(vpx->vp_pages,
+				 sizeof(struct page *) * (vpx->vp_size + size),
+				 GFP_KERNEL);
+	if (vpx->vp_pages == NULL)
+		goto out;
+
+	/* Zero out the new pointers and allocate pages*/
+	memset(&vpx->vp_pages[vpx->vp_size], 0, sizeof(struct page *) * size);
+	if (vp_alloc_pages(vpx, vpx->vp_size, vpx->vp_size + size))
+		goto out;
+
+	vpx->vp_size += size;
+	return 0;
+out:
+	return -ENOMEM;
+}
+
+int vp_init(struct gfs2_sbd *sdp, struct vbuf *vb, int init_cap)
+{
+	int cap, err = -ENOMEM;
+	struct vp_ctx *vpx;
+
+	cap = DIV_ROUND_UP(init_cap, PAGE_SIZE);
+
+	vpx = kmalloc(sizeof(struct vp_ctx), GFP_KERNEL);
+	if (vpx == NULL)
+		goto out;
+
+	vpx->vp_magic = VP_MAGIC;
+	vpx->vp_size = cap;
+	vpx->vp_pages = kzalloc(sizeof(struct page *) * cap, GFP_KERNEL);
+	if (vpx->vp_pages == NULL)
+		goto free;
+
+	if (vp_alloc_pages(vpx, 0, cap))
+		goto free_all;
+
+	vpx->vp_baseptr = vpx->vp_top = page_address(vpx->vp_pages[0]);
+	vpx->vp_sdp = sdp;
+	vb->v_ptr = vpx->vp_baseptr;
+	vb->v_opaque = vpx;
+
+	err = 0;
+	goto out;
+
+free_all:
+	vp_free_pages(vpx);
+	kfree(vpx->vp_pages);
+free:
+	kfree(vpx);
+	vpx = NULL;
+out:
+	return err;
+}
+
+void vp_uninit(struct vbuf *vb)
+{
+	struct vp_ctx *vpx;
+
+	if (!vb || !vb->v_opaque)
+		return;
+
+	vpx = vb->v_opaque;
+	if (vpx->vp_magic != VP_MAGIC)
+		return;
+
+	vp_free_pages(vpx);
+	kfree(vpx->vp_pages);
+	kfree(vpx);
+	vb->v_ptr = vb->v_opaque = NULL;
+}
+
+static int vp_rw_pages(struct vp_ctx *vpx, void *to, const void *from,
+		       size_t count, int what)
+{
+	int pg_ind, pg_off, bytes, rw = 0;
+
+	while (count > 0) {
+		pg_ind = what == VP_READ ? VP_PAGE_INDEX(vpx, from)
+			: VP_PAGE_INDEX(vpx, to);
+		pg_off = what == VP_READ ? VP_PAGE_OFFSET(vpx, from)
+			: VP_PAGE_OFFSET(vpx, to);
+		bytes = what == VP_READ ? VP_PAGE_BYTES_LEFT(vpx, from)
+			: VP_PAGE_BYTES_LEFT(vpx, to);
+		bytes = min(count, (size_t) bytes);
+
+		if (what == VP_READ)
+			memcpy(to, VP_PAGE_PTR(vpx, pg_ind, pg_off), bytes);
+		else {
+			if (what == VP_WRITE)
+				memcpy(VP_PAGE_PTR(vpx, pg_ind, pg_off),
+				       from, bytes);
+			else if (what == VP_MEMSET)
+				memset(VP_PAGE_PTR(vpx, pg_ind, pg_off),
+				       (*(const int*)from), bytes);
+			if ((to + count) > vpx->vp_top)
+				vpx->vp_top = to + count;
+		}
+		to += bytes;
+		if (what != VP_MEMSET)
+			from += bytes;
+		rw += bytes;
+		count -= bytes;
+	}
+	return rw;
+}
+
+struct vp_ctx* vp_get_vpx(struct vbuf *vb)
+{
+	struct vp_ctx *vpx = NULL;
+
+	if (!vb || !vb->v_opaque)
+		goto out;
+
+	vpx = vb->v_opaque;
+	if (vpx->vp_magic != VP_MAGIC) {
+		vpx = NULL;
+		goto out;
+	}
+out:
+	return vpx;
+}
+
+int vp_read(struct vbuf *vb, void *to, const void *from, size_t count)
+{
+	struct vp_ctx *vpx;
+	void *buf_end = NULL;
+	int err = -EINVAL;
+
+	vpx = vp_get_vpx(vb);
+	if (!vpx)
+		return err;
+
+	buf_end = vpx->vp_baseptr + PAGE_SIZE * vpx->vp_size;
+	if (count <= 0 || (from + count > buf_end))
+		return err;
+
+	return vp_rw_pages(vpx, to, from, count, VP_READ);
+}
+
+int vp_write_i(struct vp_ctx *vpx, void *to, const void *from,
+	       size_t count, int what)
+{
+	int err = -EINVAL;
+	void *buf_end = NULL;
+
+	/* Check to see if the write will fall within limits */
+	buf_end = vpx->vp_baseptr + PAGE_SIZE * vpx->vp_size;
+	if (to < vpx->vp_baseptr ||
+	    (to + count) > (buf_end + PAGE_SIZE * VP_MAX_ALLOC_STEP))
+		return err;
+
+	if ((to + count) > buf_end) {
+		err = vp_extend(vpx, DIV_ROUND_UP(to + count - buf_end,
+						  PAGE_SIZE));
+		if (err)
+			return err;
+	}
+	return vp_rw_pages(vpx, to, from, count, what);
+}
+
+int vp_write(struct vbuf *vb, void *to, const void *from, size_t count)
+{
+	struct vp_ctx *vpx;
+	int err = -EINVAL;
+
+	if (count <= 0 || count > VP_MAX_WRITE)
+		return err;
+
+	vpx = vp_get_vpx(vb);
+	if (!vpx)
+		return err;
+
+	return vp_write_i(vpx, to, from, count, VP_WRITE);
+}
+
+int vp_append(struct vbuf *vb, const void *from, size_t count)
+{
+	struct vp_ctx *vpx;
+	int err = -EINVAL;
+
+	if (count <= 0 || count > VP_MAX_WRITE)
+		return err;
+
+	vpx = vp_get_vpx(vb);
+	if (!vpx)
+		return err;
+	
+	return vp_write_i(vpx, vpx->vp_top, from, count, VP_WRITE);
+}
+
+int vp_memset(struct vbuf *vb, void *to, int c, size_t count)
+{
+	struct vp_ctx *vpx;
+	int err = -EINVAL;
+
+	if (count <= 0 || count > VP_MAX_WRITE)
+		return err;
+
+	vpx = vp_get_vpx(vb);
+	if (!vpx)
+		return err;
+
+	err = vp_write_i(vpx, to, &c, count, VP_MEMSET);
+
+	return err;
+}
+
+/*
+ * Returns the linear ptr to the top of this buffer
+ */
+void* vp_get_top(struct vbuf *vb)
+{
+	struct vp_ctx *vpx;
+	vpx = vp_get_vpx(vb);
+	if (!vpx)
+		return NULL;
+	else
+		return vpx->vp_top;
+}
+
+/*
+ * vp_get_page_count: Returns # of pages allocated to this buffer
+ */
+size_t vp_get_page_count(struct vbuf *vb)
+{
+	struct vp_ctx *vpx;
+	vpx = vp_get_vpx(vb);
+	if (!vpx)
+		return -EINVAL;
+	return vpx->vp_size;
+}
+
+/*
+ * vp_get_size: Returns number of bytes used in this buffer
+ */
+size_t vp_get_size(struct vbuf *vb)
+{
+	struct vp_ctx *vpx;
+	vpx = vp_get_vpx(vb);
+	if (!vpx)
+		return -EINVAL;
+	return vpx->vp_top - vpx->vp_baseptr;
+}
+
+/*
+ * vp_reset: Doesn't deallocate, simply moves
+ * vp_top to vp_baseptr, making the entire buffer
+ * available for writing.
+ */
+void vp_reset(struct vbuf *vb)
+{
+	struct vp_ctx *vpx;
+	vpx = vp_get_vpx(vb);
+	if (vpx)
+		vpx->vp_top = vpx->vp_baseptr;
+}
diff --git a/fs/gfs2/util.h b/fs/gfs2/util.h
index cbdcbdf..40fb692 100644
--- a/fs/gfs2/util.h
+++ b/fs/gfs2/util.h
@@ -16,6 +16,7 @@
 #endif
 
 #include <linux/mempool.h>
+#include <linux/slab.h>
 
 #include "incore.h"
 
@@ -168,4 +169,46 @@ gfs2_tune_get_i(&(sdp)->sd_tune, &(sdp)->sd_tune.field)
 __printf(2, 3)
 int gfs2_lm_withdraw(struct gfs2_sbd *sdp, const char *fmt, ...);
 
+/*
+ * Fast buffer backed by vector of pages.
+ * Always allocate one page at a time
+ */
+struct vbuf {
+	void    *v_ptr;
+	void    *v_opaque;
+};
+
+struct vp_ctx {
+	struct gfs2_sbd *vp_sdp;
+	unsigned int     vp_magic;
+	void            *vp_baseptr;
+	void            *vp_top;
+	unsigned long    vp_size; /* size in pages (for bytes, * PAGE_SIZE) */
+	struct page    **vp_pages;
+};
+
+#define VP_MAGIC           0xfabd1cc5
+#define VP_MAX_WRITE       (PAGE_SIZE * 1024)
+#define VP_MAX_ALLOC_STEP  128 /* Can only alloc these many pages at a time */
+
+#define VP_READ    1
+#define VP_WRITE   2
+#define VP_MEMSET  3
+
+#define VP_PAGE_INDEX(vpx, a)      (((char*)a - (char*)vpx->vp_baseptr) / PAGE_SIZE)
+#define VP_PAGE_OFFSET(vpx, a)     (((char*)a - (char*)vpx->vp_baseptr) % PAGE_SIZE)
+#define VP_PAGE_BYTES_LEFT(vpx, a) (PAGE_SIZE - VP_PAGE_OFFSET(vpx, a))
+#define VP_PAGE_PTR(vpx, ind, off) (page_address(vpx->vp_pages[ind]) + off)
+
+int vp_init(struct gfs2_sbd *sdp, struct vbuf *vb, int init_cap);
+void vp_uninit(struct vbuf *vb);
+void* vp_get_top(struct vbuf *vb);
+size_t vp_get_size(struct vbuf *vb);
+size_t vp_get_page_count(struct vbuf *vb);
+void vp_reset(struct vbuf *vb);
+int vp_read(struct vbuf *vb, void *to, const void *from, size_t count);
+int vp_write(struct vbuf *vb, void *to, const void *from, size_t count);
+int vp_append(struct vbuf *vb, const void *from, size_t count);
+int vp_memset(struct vbuf *vb, void *to, int c, size_t count);
+ 
 #endif /* __UTIL_DOT_H__ */
-- 
1.8.1.4

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




[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [Samba]     [Device Mapper]     [CEPH Development]
  Powered by Linux