diff options
Diffstat (limited to 'fs/nfs')
-rw-r--r-- | fs/nfs/blocklayout/blocklayout.c | 207 |
1 files changed, 105 insertions, 102 deletions
diff --git a/fs/nfs/blocklayout/blocklayout.c b/fs/nfs/blocklayout/blocklayout.c index 61a858cd54fe..76ec017a6f0a 100644 --- a/fs/nfs/blocklayout/blocklayout.c +++ b/fs/nfs/blocklayout/blocklayout.c @@ -513,144 +513,147 @@ static int decode_sector_number(__be32 **rp, sector_t *sp) return 0; } -/* XDR decode pnfs_block_layout4 structure */ static int -nfs4_blk_process_layoutget(struct pnfs_layout_hdr *lo, - struct nfs4_layoutget_res *lgr, gfp_t gfp_flags) +bl_alloc_extent(struct xdr_stream *xdr, struct pnfs_layout_hdr *lo, + struct layout_verification *lv, struct list_head *extents, + gfp_t gfp_mask) { - struct pnfs_block_layout *bl = BLK_LO2EXT(lo); - int i, status = -EIO; - uint32_t count; - struct pnfs_block_extent *be = NULL, *save; - struct xdr_stream stream; - struct xdr_buf buf; - struct page *scratch; + struct pnfs_block_extent *be; + struct nfs4_deviceid id; + int error; __be32 *p; + + p = xdr_inline_decode(xdr, 28 + NFS4_DEVICEID4_SIZE); + if (!p) + return -EIO; + + be = kzalloc(sizeof(*be), GFP_NOFS); + if (!be) + return -ENOMEM; + + memcpy(&id, p, NFS4_DEVICEID4_SIZE); + p += XDR_QUADLEN(NFS4_DEVICEID4_SIZE); + + error = -EIO; + be->be_device = nfs4_find_get_deviceid(NFS_SERVER(lo->plh_inode), &id, + lo->plh_lc_cred, gfp_mask); + if (!be->be_device) + goto out_free_be; + + /* + * The next three values are read in as bytes, but stored in the + * extent structure in 512-byte granularity. + */ + if (decode_sector_number(&p, &be->be_f_offset) < 0) + goto out_put_deviceid; + if (decode_sector_number(&p, &be->be_length) < 0) + goto out_put_deviceid; + if (decode_sector_number(&p, &be->be_v_offset) < 0) + goto out_put_deviceid; + be->be_state = be32_to_cpup(p++); + + error = verify_extent(be, lv); + if (error) { + dprintk("%s: extent verification failed\n", __func__); + goto out_put_deviceid; + } + + list_add_tail(&be->be_list, extents); + return 0; + +out_put_deviceid: + nfs4_put_deviceid_node(be->be_device); +out_free_be: + kfree(be); + return error; +} + +static struct pnfs_layout_segment * +bl_alloc_lseg(struct pnfs_layout_hdr *lo, struct nfs4_layoutget_res *lgr, + gfp_t gfp_mask) +{ struct layout_verification lv = { .mode = lgr->range.iomode, .start = lgr->range.offset >> SECTOR_SHIFT, .inval = lgr->range.offset >> SECTOR_SHIFT, .cowread = lgr->range.offset >> SECTOR_SHIFT, }; + struct pnfs_block_layout *bl = BLK_LO2EXT(lo); + struct pnfs_layout_segment *lseg; + struct xdr_buf buf; + struct xdr_stream xdr; + struct page *scratch; + int status, i; + uint32_t count; + __be32 *p; LIST_HEAD(extents); dprintk("---> %s\n", __func__); - scratch = alloc_page(gfp_flags); + lseg = kzalloc(sizeof(*lseg), gfp_mask); + if (!lseg) + return ERR_PTR(-ENOMEM); + + status = -ENOMEM; + scratch = alloc_page(gfp_mask); if (!scratch) - return -ENOMEM; + goto out; - xdr_init_decode_pages(&stream, &buf, lgr->layoutp->pages, lgr->layoutp->len); - xdr_set_scratch_buffer(&stream, page_address(scratch), PAGE_SIZE); + xdr_init_decode_pages(&xdr, &buf, + lgr->layoutp->pages, lgr->layoutp->len); + xdr_set_scratch_buffer(&xdr, page_address(scratch), PAGE_SIZE); - p = xdr_inline_decode(&stream, 4); + status = -EIO; + p = xdr_inline_decode(&xdr, 4); if (unlikely(!p)) - goto out_err; + goto out_free_scratch; count = be32_to_cpup(p++); + dprintk("%s: number of extents %d\n", __func__, count); - dprintk("%s enter, number of extents %i\n", __func__, count); - p = xdr_inline_decode(&stream, (28 + NFS4_DEVICEID4_SIZE) * count); - if (unlikely(!p)) - goto out_err; - - /* Decode individual extents, putting them in temporary - * staging area until whole layout is decoded to make error - * recovery easier. + /* + * Decode individual extents, putting them in temporary staging area + * until whole layout is decoded to make error recovery easier. */ for (i = 0; i < count; i++) { - struct nfs4_deviceid id; - - be = kzalloc(sizeof(struct pnfs_block_extent), GFP_NOFS); - if (!be) { - status = -ENOMEM; - goto out_err; - } - memcpy(&id, p, NFS4_DEVICEID4_SIZE); - p += XDR_QUADLEN(NFS4_DEVICEID4_SIZE); - - be->be_device = - nfs4_find_get_deviceid(NFS_SERVER(lo->plh_inode), &id, - lo->plh_lc_cred, gfp_flags); - if (!be->be_device) - goto out_err; - - /* The next three values are read in as bytes, - * but stored as 512-byte sector lengths - */ - if (decode_sector_number(&p, &be->be_f_offset) < 0) - goto out_err; - if (decode_sector_number(&p, &be->be_length) < 0) - goto out_err; - if (decode_sector_number(&p, &be->be_v_offset) < 0) - goto out_err; - be->be_state = be32_to_cpup(p++); - if (verify_extent(be, &lv)) { - dprintk("%s verify failed\n", __func__); - goto out_err; - } - list_add_tail(&be->be_list, &extents); + status = bl_alloc_extent(&xdr, lo, &lv, &extents, gfp_mask); + if (status) + goto process_extents; } + if (lgr->range.offset + lgr->range.length != lv.start << SECTOR_SHIFT) { dprintk("%s Final length mismatch\n", __func__); - be = NULL; - goto out_err; + status = -EIO; + goto process_extents; } + if (lv.start < lv.cowread) { dprintk("%s Final uncovered COW extent\n", __func__); - be = NULL; - goto out_err; - } - /* Extents decoded properly, now try to merge them in to - * existing layout extents. - */ - list_for_each_entry_safe(be, save, &extents, be_list) { - list_del(&be->be_list); - - status = ext_tree_insert(bl, be); - if (status) - goto out_free_list; + status = -EIO; } - status = 0; - out: - __free_page(scratch); - dprintk("%s returns %i\n", __func__, status); - return status; - out_err: - nfs4_put_deviceid_node(be->be_device); - kfree(be); - out_free_list: +process_extents: while (!list_empty(&extents)) { - be = list_first_entry(&extents, struct pnfs_block_extent, - be_list); + struct pnfs_block_extent *be = + list_first_entry(&extents, struct pnfs_block_extent, + be_list); list_del(&be->be_list); - nfs4_put_deviceid_node(be->be_device); - kfree(be); - } - goto out; -} -/* We pretty much ignore lseg, and store all data layout wide, so we - * can correctly merge. - */ -static struct pnfs_layout_segment *bl_alloc_lseg(struct pnfs_layout_hdr *lo, - struct nfs4_layoutget_res *lgr, - gfp_t gfp_flags) -{ - struct pnfs_layout_segment *lseg; - int status; + if (!status) + status = ext_tree_insert(bl, be); - dprintk("%s enter\n", __func__); - lseg = kzalloc(sizeof(*lseg), gfp_flags); - if (!lseg) - return ERR_PTR(-ENOMEM); - status = nfs4_blk_process_layoutget(lo, lgr, gfp_flags); + if (status) { + nfs4_put_deviceid_node(be->be_device); + kfree(be); + } + } + +out_free_scratch: + __free_page(scratch); +out: + dprintk("%s returns %d\n", __func__, status); if (status) { - /* We don't want to call the full-blown bl_free_lseg, - * since on error extents were not touched. - */ kfree(lseg); return ERR_PTR(status); } |