* Copyright (c) 2014 Christoph Hellwig.
*/
+#include <linux/vmalloc.h>
+
#include "blocklayout.h"
#define NFSDBG_FACILITY NFSDBG_PNFS_LD
{
if (be1->be_state != be2->be_state)
return false;
- if (be1->be_mdev != be2->be_mdev)
+ if (be1->be_device != be2->be_device)
return false;
if (be1->be_f_offset + be1->be_length != be2->be_f_offset)
if (left && ext_can_merge(left, be)) {
left->be_length += be->be_length;
rb_erase(&be->be_node, root);
+ nfs4_put_deviceid_node(be->be_device);
kfree(be);
return left;
}
if (right && ext_can_merge(be, right)) {
be->be_length += right->be_length;
rb_erase(&right->be_node, root);
+ nfs4_put_deviceid_node(right->be_device);
kfree(right);
}
be->be_v_offset = new->be_v_offset;
be->be_length += new->be_length;
be = ext_try_to_merge_left(root, be);
- kfree(new);
- return;
+ goto free_new;
}
p = &(*p)->rb_left;
} else if (new->be_f_offset >= ext_f_end(be)) {
if (merge_ok && ext_can_merge(be, new)) {
be->be_length += new->be_length;
be = ext_try_to_merge_right(root, be);
- kfree(new);
- return;
+ goto free_new;
}
p = &(*p)->rb_right;
} else {
rb_link_node(&new->be_node, parent, p);
rb_insert_color(&new->be_node, root);
+ return;
+free_new:
+ nfs4_put_deviceid_node(new->be_device);
+ kfree(new);
}
static int
{
struct pnfs_block_extent *be;
sector_t len1 = 0, len2 = 0;
- sector_t orig_f_offset;
sector_t orig_v_offset;
sector_t orig_len;
if (be->be_f_offset >= end)
return 0;
- orig_f_offset = be->be_f_offset;
orig_v_offset = be->be_v_offset;
orig_len = be->be_length;
new->be_length = len2;
new->be_state = be->be_state;
new->be_tag = be->be_tag;
- new->be_mdev = be->be_mdev;
- memcpy(&new->be_devid, &be->be_devid,
- sizeof(struct nfs4_deviceid));
+ new->be_device = nfs4_get_deviceid(be->be_device);
__ext_tree_insert(root, new, true);
} else {
struct pnfs_block_extent *next = ext_tree_next(be);
rb_erase(&be->be_node, root);
+ nfs4_put_deviceid_node(be->be_device);
kfree(be);
be = next;
}
__ext_tree_insert(root, new, true);
} else if (new->be_f_offset >= be->be_f_offset) {
if (ext_f_end(new) <= ext_f_end(be)) {
+ nfs4_put_deviceid_node(new->be_device);
kfree(new);
} else {
sector_t new_len = ext_f_end(new) - ext_f_end(be);
}
split->be_length = be->be_f_offset - split->be_f_offset;
+ split->be_device = nfs4_get_deviceid(new->be_device);
__ext_tree_insert(root, split, true);
new->be_f_offset += diff;
struct pnfs_block_extent *new;
sector_t orig_len = be->be_length;
- dprintk("%s: need split for 0x%lx:0x%lx at 0x%lx\n",
- __func__, be->be_f_offset, ext_f_end(be), split);
-
new = kzalloc(sizeof(*new), GFP_ATOMIC);
if (!new)
return -ENOMEM;
new->be_length = orig_len - be->be_length;
new->be_state = be->be_state;
new->be_tag = be->be_tag;
-
- new->be_mdev = be->be_mdev;
- memcpy(&new->be_devid, &be->be_devid, sizeof(struct nfs4_deviceid));
-
- dprintk("%s: got 0x%lx:0x%lx!\n",
- __func__, be->be_f_offset, ext_f_end(be));
- dprintk("%s: got 0x%lx:0x%lx!\n",
- __func__, new->be_f_offset, ext_f_end(new));
+ new->be_device = nfs4_get_deviceid(be->be_device);
__ext_tree_insert(root, new, false);
return 0;
return err;
}
-int
-ext_tree_encode_commit(struct pnfs_block_layout *bl, struct xdr_stream *xdr)
+static void ext_tree_free_commitdata(struct nfs4_layoutcommit_args *arg,
+ size_t buffer_size)
{
- struct pnfs_block_extent *be;
- unsigned int count = 0;
- __be32 *p, *xdr_start;
- int ret = 0;
+ if (arg->layoutupdate_pages != &arg->layoutupdate_page) {
+ int nr_pages = DIV_ROUND_UP(buffer_size, PAGE_SIZE), i;
- dprintk("%s enter\n", __func__);
+ for (i = 0; i < nr_pages; i++)
+ put_page(arg->layoutupdate_pages[i]);
+ kfree(arg->layoutupdate_pages);
+ } else {
+ put_page(arg->layoutupdate_page);
+ }
+}
- xdr_start = xdr_reserve_space(xdr, 8);
- if (!xdr_start)
- return -ENOSPC;
+static int ext_tree_encode_commit(struct pnfs_block_layout *bl, __be32 *p,
+ size_t buffer_size, size_t *count)
+{
+ struct pnfs_block_extent *be;
+ int ret = 0;
spin_lock(&bl->bl_ext_lock);
for (be = ext_tree_first(&bl->bl_ext_rw); be; be = ext_tree_next(be)) {
be->be_tag != EXTENT_WRITTEN)
continue;
- p = xdr_reserve_space(xdr, 7 * sizeof(__be32) +
- NFS4_DEVICEID4_SIZE);
- if (!p) {
- printk("%s: out of space for extent list\n", __func__);
+ (*count)++;
+ if (*count * BL_EXTENT_SIZE > buffer_size) {
+ /* keep counting.. */
ret = -ENOSPC;
- break;
+ continue;
}
- p = xdr_encode_opaque_fixed(p, be->be_devid.data,
+ p = xdr_encode_opaque_fixed(p, be->be_device->deviceid.data,
NFS4_DEVICEID4_SIZE);
p = xdr_encode_hyper(p, be->be_f_offset << SECTOR_SHIFT);
p = xdr_encode_hyper(p, be->be_length << SECTOR_SHIFT);
*p++ = cpu_to_be32(PNFS_BLOCK_READWRITE_DATA);
be->be_tag = EXTENT_COMMITTING;
- count++;
}
spin_unlock(&bl->bl_ext_lock);
- xdr_start[0] = cpu_to_be32((xdr->p - xdr_start - 1) * 4);
- xdr_start[1] = cpu_to_be32(count);
-
- dprintk("%s found %i ranges\n", __func__, count);
return ret;
}
+int
+ext_tree_prepare_commit(struct nfs4_layoutcommit_args *arg)
+{
+ struct pnfs_block_layout *bl = BLK_LO2EXT(NFS_I(arg->inode)->layout);
+ size_t count = 0, buffer_size = PAGE_SIZE;
+ __be32 *start_p;
+ int ret;
+
+ dprintk("%s enter\n", __func__);
+
+ arg->layoutupdate_page = alloc_page(GFP_NOFS);
+ if (!arg->layoutupdate_page)
+ return -ENOMEM;
+ start_p = page_address(arg->layoutupdate_page);
+ arg->layoutupdate_pages = &arg->layoutupdate_page;
+
+retry:
+ ret = ext_tree_encode_commit(bl, start_p + 1, buffer_size, &count);
+ if (unlikely(ret)) {
+ ext_tree_free_commitdata(arg, buffer_size);
+
+ buffer_size = sizeof(__be32) + BL_EXTENT_SIZE * count;
+ count = 0;
+
+ arg->layoutupdate_pages =
+ kcalloc(DIV_ROUND_UP(buffer_size, PAGE_SIZE),
+ sizeof(struct page *), GFP_NOFS);
+ if (!arg->layoutupdate_pages)
+ return -ENOMEM;
+
+ start_p = __vmalloc(buffer_size, GFP_NOFS, PAGE_KERNEL);
+ if (!start_p) {
+ kfree(arg->layoutupdate_pages);
+ return -ENOMEM;
+ }
+
+ goto retry;
+ }
+
+ *start_p = cpu_to_be32(count);
+ arg->layoutupdate_len = sizeof(__be32) + BL_EXTENT_SIZE * count;
+
+ if (unlikely(arg->layoutupdate_pages != &arg->layoutupdate_page)) {
+ __be32 *p = start_p;
+ int i = 0;
+
+ for (p = start_p;
+ p < start_p + arg->layoutupdate_len;
+ p += PAGE_SIZE) {
+ arg->layoutupdate_pages[i++] = vmalloc_to_page(p);
+ }
+ }
+
+ dprintk("%s found %zu ranges\n", __func__, count);
+ return 0;
+}
+
void
-ext_tree_mark_committed(struct pnfs_block_layout *bl, int status)
+ext_tree_mark_committed(struct nfs4_layoutcommit_args *arg, int status)
{
+ struct pnfs_block_layout *bl = BLK_LO2EXT(NFS_I(arg->inode)->layout);
struct rb_root *root = &bl->bl_ext_rw;
struct pnfs_block_extent *be;
dprintk("%s status %d\n", __func__, status);
+ ext_tree_free_commitdata(arg, arg->layoutupdate_len);
+
spin_lock(&bl->bl_ext_lock);
for (be = ext_tree_first(root); be; be = ext_tree_next(be)) {
if (be->be_state != PNFS_BLOCK_INVALID_DATA ||