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