ceph: use pagelist to present MDS request data
authorYan, Zheng <zyan@redhat.com>
Tue, 16 Sep 2014 11:15:28 +0000 (19:15 +0800)
committerSage Weil <sage@redhat.com>
Tue, 14 Oct 2014 19:56:49 +0000 (12:56 -0700)
Current code uses page array to present MDS request data. Pages in the
array are allocated/freed by caller of ceph_mdsc_do_request(). If request
is interrupted, the pages can be freed while they are still being used by
the request message.

The fix is use pagelist to present MDS request data. Pagelist is
reference counted.

Signed-off-by: Yan, Zheng <zyan@redhat.com>
Reviewed-by: Sage Weil <sage@redhat.com>
fs/ceph/mds_client.c
fs/ceph/mds_client.h
fs/ceph/xattr.c

index b4430ce1b3f6f7b1a913ee2d49e632b35b38c6ee..f8f774e6f86833c884bb6d0652cb96ca3d959bdb 100644 (file)
@@ -543,6 +543,8 @@ void ceph_mdsc_release_request(struct kref *kref)
        }
        kfree(req->r_path1);
        kfree(req->r_path2);
+       if (req->r_pagelist)
+               ceph_pagelist_release(req->r_pagelist);
        put_request_session(req);
        ceph_unreserve_caps(req->r_mdsc, &req->r_caps_reservation);
        kfree(req);
@@ -1916,13 +1918,15 @@ static struct ceph_msg *create_request_message(struct ceph_mds_client *mdsc,
        msg->front.iov_len = p - msg->front.iov_base;
        msg->hdr.front_len = cpu_to_le32(msg->front.iov_len);
 
-       if (req->r_data_len) {
-               /* outbound data set only by ceph_sync_setxattr() */
-               BUG_ON(!req->r_pages);
-               ceph_msg_data_add_pages(msg, req->r_pages, req->r_data_len, 0);
+       if (req->r_pagelist) {
+               struct ceph_pagelist *pagelist = req->r_pagelist;
+               atomic_inc(&pagelist->refcnt);
+               ceph_msg_data_add_pagelist(msg, pagelist);
+               msg->hdr.data_len = cpu_to_le32(pagelist->length);
+       } else {
+               msg->hdr.data_len = 0;
        }
 
-       msg->hdr.data_len = cpu_to_le32(req->r_data_len);
        msg->hdr.data_off = cpu_to_le16(0);
 
 out_free2:
index e00737cf523c0ae237121fe0fdcef0d147b2d9f2..23015f747061b86098b71a201b226946c2800f52 100644 (file)
@@ -202,9 +202,7 @@ struct ceph_mds_request {
        bool r_direct_is_hash;  /* true if r_direct_hash is valid */
 
        /* data payload is used for xattr ops */
-       struct page **r_pages;
-       int r_num_pages;
-       int r_data_len;
+       struct ceph_pagelist *r_pagelist;
 
        /* what caps shall we drop? */
        int r_inode_drop, r_inode_unless;
index 19da5026c38f565fa138b5c43d68e9577ff23acc..678b0d2bbbc4d6d8eaaa4ee64a310b212aeda655 100644 (file)
@@ -1,4 +1,5 @@
 #include <linux/ceph/ceph_debug.h>
+#include <linux/ceph/pagelist.h>
 
 #include "super.h"
 #include "mds_client.h"
@@ -850,35 +851,25 @@ static int ceph_sync_setxattr(struct dentry *dentry, const char *name,
        struct ceph_inode_info *ci = ceph_inode(inode);
        struct ceph_mds_request *req;
        struct ceph_mds_client *mdsc = fsc->mdsc;
+       struct ceph_pagelist *pagelist = NULL;
        int err;
-       int i, nr_pages;
-       struct page **pages = NULL;
-       void *kaddr;
-
-       /* copy value into some pages */
-       nr_pages = calc_pages_for(0, size);
-       if (nr_pages) {
-               pages = kmalloc(sizeof(pages[0])*nr_pages, GFP_NOFS);
-               if (!pages)
+
+       if (value) {
+               /* copy value into pagelist */
+               pagelist = kmalloc(sizeof(*pagelist), GFP_NOFS);
+               if (!pagelist)
                        return -ENOMEM;
-               err = -ENOMEM;
-               for (i = 0; i < nr_pages; i++) {
-                       pages[i] = __page_cache_alloc(GFP_NOFS);
-                       if (!pages[i]) {
-                               nr_pages = i;
-                               goto out;
-                       }
-                       kaddr = kmap(pages[i]);
-                       memcpy(kaddr, value + i*PAGE_CACHE_SIZE,
-                              min(PAGE_CACHE_SIZE, size-i*PAGE_CACHE_SIZE));
-               }
+
+               ceph_pagelist_init(pagelist);
+               err = ceph_pagelist_append(pagelist, value, size);
+               if (err)
+                       goto out;
+       } else {
+               flags |= CEPH_XATTR_REMOVE;
        }
 
        dout("setxattr value=%.*s\n", (int)size, value);
 
-       if (!value)
-               flags |= CEPH_XATTR_REMOVE;
-
        /* do request */
        req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_SETXATTR,
                                       USE_AUTH_MDS);
@@ -893,9 +884,8 @@ static int ceph_sync_setxattr(struct dentry *dentry, const char *name,
        req->r_args.setxattr.flags = cpu_to_le32(flags);
        req->r_path2 = kstrdup(name, GFP_NOFS);
 
-       req->r_pages = pages;
-       req->r_num_pages = nr_pages;
-       req->r_data_len = size;
+       req->r_pagelist = pagelist;
+       pagelist = NULL;
 
        dout("xattr.ver (before): %lld\n", ci->i_xattrs.version);
        err = ceph_mdsc_do_request(mdsc, NULL, req);
@@ -903,11 +893,8 @@ static int ceph_sync_setxattr(struct dentry *dentry, const char *name,
        dout("xattr.ver (after): %lld\n", ci->i_xattrs.version);
 
 out:
-       if (pages) {
-               for (i = 0; i < nr_pages; i++)
-                       __free_page(pages[i]);
-               kfree(pages);
-       }
+       if (pagelist)
+               ceph_pagelist_release(pagelist);
        return err;
 }