Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 25641c0c authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull NFS client updates from Trond Myklebust:
 "Highlights include:

  Stable fixes:
   - fix an NFSv4.1 state renewal regression
   - fix open/lock state recovery error handling
   - fix lock recovery when CREATE_SESSION/SETCLIENTID_CONFIRM fails
   - fix statd when reconnection fails
   - don't wake tasks during connection abort
   - don't start reboot recovery if lease check fails
   - fix duplicate proc entries

  Features:
  - pNFS block driver fixes and clean ups from Christoph
  - More code cleanups from Anna
  - Improve mmap() writeback performance
  - Replace use of PF_TRANS with a more generic mechanism for avoiding
    deadlocks in nfs_release_page"

* tag 'nfs-for-3.18-1' of git://git.linux-nfs.org/projects/trondmy/linux-nfs: (66 commits)
  NFSv4.1: Fix an NFSv4.1 state renewal regression
  NFSv4: fix open/lock state recovery error handling
  NFSv4: Fix lock recovery when CREATE_SESSION/SETCLIENTID_CONFIRM fails
  NFS: Fabricate fscache server index key correctly
  SUNRPC: Add missing support for RPC_CLNT_CREATE_NO_RETRANS_TIMEOUT
  NFSv3: Fix missing includes of nfs3_fs.h
  NFS/SUNRPC: Remove other deadlock-avoidance mechanisms in nfs_release_page()
  NFS: avoid waiting at all in nfs_release_page when congested.
  NFS: avoid deadlocks with loop-back mounted NFS filesystems.
  MM: export page_wakeup functions
  SCHED: add some "wait..on_bit...timeout()" interfaces.
  NFS: don't use STABLE writes during writeback.
  NFSv4: use exponential retry on NFS4ERR_DELAY for async requests.
  rpc: Add -EPERM processing for xs_udp_send_request()
  rpc: return sent and err from xs_sendpages()
  lockd: Try to reconnect if statd has moved
  SUNRPC: Don't wake tasks during connection abort
  Fixing lease renewal
  nfs: fix duplicate proc entries
  pnfs/blocklayout: Fix a 64-bit division/remainder issue in bl_map_stripe
  ...
parents ef0625b7 72c23f08
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -159,6 +159,12 @@ static int nsm_mon_unmon(struct nsm_handle *nsm, u32 proc, struct nsm_res *res,

	msg.rpc_proc = &clnt->cl_procinfo[proc];
	status = rpc_call_sync(clnt, &msg, RPC_TASK_SOFTCONN);
	if (status == -ECONNREFUSED) {
		dprintk("lockd:	NSM upcall RPC failed, status=%d, forcing rebind\n",
				status);
		rpc_force_rebind(clnt);
		status = rpc_call_sync(clnt, &msg, RPC_TASK_SOFTCONN);
	}
	if (status < 0)
		dprintk("lockd: NSM upcall RPC failed, status=%d\n",
				status);
+2 −1
Original line number Diff line number Diff line
@@ -2,4 +2,5 @@
# Makefile for the pNFS block layout driver kernel module
#
obj-$(CONFIG_PNFS_BLOCK) += blocklayoutdriver.o
blocklayoutdriver-objs := blocklayout.o extents.o blocklayoutdev.o blocklayoutdm.o

blocklayoutdriver-y += blocklayout.o dev.o extent_tree.o rpc_pipefs.o
+427 −959

File changed.

Preview size limit exceeded, changes collapsed.

+103 −110
Original line number Diff line number Diff line
@@ -44,105 +44,112 @@
#define PAGE_CACHE_SECTOR_SHIFT (PAGE_CACHE_SHIFT - SECTOR_SHIFT)
#define SECTOR_SIZE (1 << SECTOR_SHIFT)

struct block_mount_id {
	spinlock_t			bm_lock;    /* protects list */
	struct list_head		bm_devlist; /* holds pnfs_block_dev */
};
struct pnfs_block_dev;

struct pnfs_block_dev {
	struct list_head		bm_node;
	struct nfs4_deviceid		bm_mdevid;    /* associated devid */
	struct block_device		*bm_mdev;     /* meta device itself */
	struct net			*net;
enum pnfs_block_volume_type {
	PNFS_BLOCK_VOLUME_SIMPLE	= 0,
	PNFS_BLOCK_VOLUME_SLICE		= 1,
	PNFS_BLOCK_VOLUME_CONCAT	= 2,
	PNFS_BLOCK_VOLUME_STRIPE	= 3,
};

enum exstate4 {
	PNFS_BLOCK_READWRITE_DATA	= 0,
	PNFS_BLOCK_READ_DATA		= 1,
	PNFS_BLOCK_INVALID_DATA		= 2, /* mapped, but data is invalid */
	PNFS_BLOCK_NONE_DATA		= 3  /* unmapped, it's a hole */
#define PNFS_BLOCK_MAX_UUIDS	4
#define PNFS_BLOCK_MAX_DEVICES	64

/*
 * Random upper cap for the uuid length to avoid unbounded allocation.
 * Not actually limited by the protocol.
 */
#define PNFS_BLOCK_UUID_LEN	128


struct pnfs_block_volume {
	enum pnfs_block_volume_type	type;
	union {
		struct {
			int		len;
			int		nr_sigs;
			struct {
				u64		offset;
				u32		sig_len;
				u8		sig[PNFS_BLOCK_UUID_LEN];
			} sigs[PNFS_BLOCK_MAX_UUIDS];
		} simple;
		struct {
			u64		start;
			u64		len;
			u32		volume;
		} slice;
		struct {
			u32		volumes_count;
			u32		volumes[PNFS_BLOCK_MAX_DEVICES];
		} concat;
		struct {
			u64		chunk_size;
			u32		volumes_count;
			u32		volumes[PNFS_BLOCK_MAX_DEVICES];
		} stripe;
	};
};

#define MY_MAX_TAGS (15) /* tag bitnums used must be less than this */
struct pnfs_block_dev_map {
	sector_t			start;
	sector_t			len;

struct my_tree {
	sector_t		mtt_step_size;	/* Internal sector alignment */
	struct list_head	mtt_stub; /* Should be a radix tree */
	sector_t			disk_offset;
	struct block_device		*bdev;
};

struct pnfs_inval_markings {
	spinlock_t	im_lock;
	struct my_tree	im_tree;	/* Sectors that need LAYOUTCOMMIT */
	sector_t	im_block_size;	/* Server blocksize in sectors */
	struct list_head im_extents;	/* Short extents for INVAL->RW conversion */
struct pnfs_block_dev {
	struct nfs4_deviceid_node	node;

	u64				start;
	u64				len;

	u32				nr_children;
	struct pnfs_block_dev		*children;
	u64				chunk_size;

	struct block_device		*bdev;
	u64				disk_offset;

	bool (*map)(struct pnfs_block_dev *dev, u64 offset,
			struct pnfs_block_dev_map *map);
};

struct pnfs_inval_tracking {
	struct list_head it_link;
	int		 it_sector;
	int		 it_tags;
enum exstate4 {
	PNFS_BLOCK_READWRITE_DATA	= 0,
	PNFS_BLOCK_READ_DATA		= 1,
	PNFS_BLOCK_INVALID_DATA		= 2, /* mapped, but data is invalid */
	PNFS_BLOCK_NONE_DATA		= 3  /* unmapped, it's a hole */
};

/* sector_t fields are all in 512-byte sectors */
struct pnfs_block_extent {
	struct kref	be_refcnt;
	struct list_head be_node;	/* link into lseg list */
	struct nfs4_deviceid be_devid;  /* FIXME: could use device cache instead */
	struct block_device *be_mdev;
	union {
		struct rb_node	be_node;
		struct list_head be_list;
	};
	struct nfs4_deviceid_node *be_device;
	sector_t	be_f_offset;	/* the starting offset in the file */
	sector_t	be_length;	/* the size of the extent */
	sector_t	be_v_offset;	/* the starting offset in the volume */
	enum exstate4	be_state;	/* the state of this extent */
	struct pnfs_inval_markings *be_inval; /* tracks INVAL->RW transition */
};

/* Shortened extent used by LAYOUTCOMMIT */
struct pnfs_block_short_extent {
	struct list_head bse_node;
	struct nfs4_deviceid bse_devid;
	struct block_device *bse_mdev;
	sector_t	bse_f_offset;	/* the starting offset in the file */
	sector_t	bse_length;	/* the size of the extent */
};

static inline void
BL_INIT_INVAL_MARKS(struct pnfs_inval_markings *marks, sector_t blocksize)
{
	spin_lock_init(&marks->im_lock);
	INIT_LIST_HEAD(&marks->im_tree.mtt_stub);
	INIT_LIST_HEAD(&marks->im_extents);
	marks->im_block_size = blocksize;
	marks->im_tree.mtt_step_size = min((sector_t)PAGE_CACHE_SECTORS,
					   blocksize);
}

enum extentclass4 {
	RW_EXTENT       = 0, /* READWRTE and INVAL */
	RO_EXTENT       = 1, /* READ and NONE */
	EXTENT_LISTS    = 2,
#define EXTENT_WRITTEN		1
#define EXTENT_COMMITTING	2
	unsigned int	be_tag;
};

static inline int bl_choose_list(enum exstate4 state)
{
	if (state == PNFS_BLOCK_READ_DATA || state == PNFS_BLOCK_NONE_DATA)
		return RO_EXTENT;
	else
		return RW_EXTENT;
}
/* on the wire size of the extent */
#define BL_EXTENT_SIZE	(7 * sizeof(__be32) + NFS4_DEVICEID4_SIZE)

struct pnfs_block_layout {
	struct pnfs_layout_hdr	bl_layout;
	struct pnfs_inval_markings bl_inval; /* tracks INVAL->RW transition */
	struct rb_root		bl_ext_rw;
	struct rb_root		bl_ext_ro;
	spinlock_t		bl_ext_lock;   /* Protects list manipulation */
	struct list_head	bl_extents[EXTENT_LISTS]; /* R and RW extents */
	struct list_head	bl_commit;	/* Needs layout commit */
	struct list_head	bl_committing;	/* Layout committing */
	unsigned int		bl_count;	/* entries in bl_commit */
	sector_t		bl_blocksize;  /* Server blocksize in sectors */
};

#define BLK_ID(lo) ((struct block_mount_id *)(NFS_SERVER(lo->plh_inode)->pnfs_ld_data))

static inline struct pnfs_block_layout *
BLK_LO2EXT(struct pnfs_layout_hdr *lo)
{
@@ -171,41 +178,27 @@ struct bl_msg_hdr {
#define BL_DEVICE_REQUEST_PROC         0x1 /* User level process succeeds */
#define BL_DEVICE_REQUEST_ERR          0x2 /* User level process fails */

/* blocklayoutdev.c */
ssize_t bl_pipe_downcall(struct file *, const char __user *, size_t);
void bl_pipe_destroy_msg(struct rpc_pipe_msg *);
void nfs4_blkdev_put(struct block_device *bdev);
struct pnfs_block_dev *nfs4_blk_decode_device(struct nfs_server *server,
						struct pnfs_device *dev);
int nfs4_blk_process_layoutget(struct pnfs_layout_hdr *lo,
				struct nfs4_layoutget_res *lgr, gfp_t gfp_flags);

/* blocklayoutdm.c */
void bl_free_block_dev(struct pnfs_block_dev *bdev);

/* extents.c */
struct pnfs_block_extent *
bl_find_get_extent(struct pnfs_block_layout *bl, sector_t isect,
		struct pnfs_block_extent **cow_read);
int bl_mark_sectors_init(struct pnfs_inval_markings *marks,
			     sector_t offset, sector_t length);
void bl_put_extent(struct pnfs_block_extent *be);
struct pnfs_block_extent *bl_alloc_extent(void);
int bl_is_sector_init(struct pnfs_inval_markings *marks, sector_t isect);
int encode_pnfs_block_layoutupdate(struct pnfs_block_layout *bl,
				   struct xdr_stream *xdr,
				   const struct nfs4_layoutcommit_args *arg);
void clean_pnfs_block_layoutupdate(struct pnfs_block_layout *bl,
				   const struct nfs4_layoutcommit_args *arg,
				   int status);
int bl_add_merge_extent(struct pnfs_block_layout *bl,
/* dev.c */
struct nfs4_deviceid_node *bl_alloc_deviceid_node(struct nfs_server *server,
		struct pnfs_device *pdev, gfp_t gfp_mask);
void bl_free_deviceid_node(struct nfs4_deviceid_node *d);

/* extent_tree.c */
int ext_tree_insert(struct pnfs_block_layout *bl,
		struct pnfs_block_extent *new);
int bl_mark_for_commit(struct pnfs_block_extent *be,
			sector_t offset, sector_t length,
			struct pnfs_block_short_extent *new);
int bl_push_one_short_extent(struct pnfs_inval_markings *marks);
struct pnfs_block_short_extent *
bl_pop_one_short_extent(struct pnfs_inval_markings *marks);
void bl_free_short_extents(struct pnfs_inval_markings *marks, int num_to_free);
int ext_tree_remove(struct pnfs_block_layout *bl, bool rw, sector_t start,
		sector_t end);
int ext_tree_mark_written(struct pnfs_block_layout *bl, sector_t start,
		sector_t len);
bool ext_tree_lookup(struct pnfs_block_layout *bl, sector_t isect,
		struct pnfs_block_extent *ret, bool rw);
int ext_tree_prepare_commit(struct nfs4_layoutcommit_args *arg);
void ext_tree_mark_committed(struct nfs4_layoutcommit_args *arg, int status);

/* rpc_pipefs.c */
dev_t bl_resolve_deviceid(struct nfs_server *server,
		struct pnfs_block_volume *b, gfp_t gfp_mask);
int __init bl_init_pipefs(void);
void __exit bl_cleanup_pipefs(void);

#endif /* FS_NFS_NFS4BLOCKLAYOUT_H */
+0 −384
Original line number Diff line number Diff line
/*
 *  linux/fs/nfs/blocklayout/blocklayoutdev.c
 *
 *  Device operations for the pnfs nfs4 file layout driver.
 *
 *  Copyright (c) 2006 The Regents of the University of Michigan.
 *  All rights reserved.
 *
 *  Andy Adamson <andros@citi.umich.edu>
 *  Fred Isaman <iisaman@umich.edu>
 *
 * permission is granted to use, copy, create derivative works and
 * redistribute this software and such derivative works for any purpose,
 * so long as the name of the university of michigan is not used in
 * any advertising or publicity pertaining to the use or distribution
 * of this software without specific, written prior authorization.  if
 * the above copyright notice or any other identification of the
 * university of michigan is included in any copy of any portion of
 * this software, then the disclaimer below must also be included.
 *
 * this software is provided as is, without representation from the
 * university of michigan as to its fitness for any purpose, and without
 * warranty by the university of michigan of any kind, either express
 * or implied, including without limitation the implied warranties of
 * merchantability and fitness for a particular purpose.  the regents
 * of the university of michigan shall not be liable for any damages,
 * including special, indirect, incidental, or consequential damages,
 * with respect to any claim arising out or in connection with the use
 * of the software, even if it has been or is hereafter advised of the
 * possibility of such damages.
 */
#include <linux/module.h>
#include <linux/buffer_head.h> /* __bread */

#include <linux/genhd.h>
#include <linux/blkdev.h>
#include <linux/hash.h>

#include "blocklayout.h"

#define NFSDBG_FACILITY         NFSDBG_PNFS_LD

static int decode_sector_number(__be32 **rp, sector_t *sp)
{
	uint64_t s;

	*rp = xdr_decode_hyper(*rp, &s);
	if (s & 0x1ff) {
		printk(KERN_WARNING "NFS: %s: sector not aligned\n", __func__);
		return -1;
	}
	*sp = s >> SECTOR_SHIFT;
	return 0;
}

/*
 * Release the block device
 */
void nfs4_blkdev_put(struct block_device *bdev)
{
	dprintk("%s for device %d:%d\n", __func__, MAJOR(bdev->bd_dev),
			MINOR(bdev->bd_dev));
	blkdev_put(bdev, FMODE_READ);
}

ssize_t bl_pipe_downcall(struct file *filp, const char __user *src,
			 size_t mlen)
{
	struct nfs_net *nn = net_generic(filp->f_dentry->d_sb->s_fs_info,
					 nfs_net_id);

	if (mlen != sizeof (struct bl_dev_msg))
		return -EINVAL;

	if (copy_from_user(&nn->bl_mount_reply, src, mlen) != 0)
		return -EFAULT;

	wake_up(&nn->bl_wq);

	return mlen;
}

void bl_pipe_destroy_msg(struct rpc_pipe_msg *msg)
{
	struct bl_pipe_msg *bl_pipe_msg = container_of(msg, struct bl_pipe_msg, msg);

	if (msg->errno >= 0)
		return;
	wake_up(bl_pipe_msg->bl_wq);
}

/*
 * Decodes pnfs_block_deviceaddr4 which is XDR encoded in dev->dev_addr_buf.
 */
struct pnfs_block_dev *
nfs4_blk_decode_device(struct nfs_server *server,
		       struct pnfs_device *dev)
{
	struct pnfs_block_dev *rv;
	struct block_device *bd = NULL;
	struct bl_pipe_msg bl_pipe_msg;
	struct rpc_pipe_msg *msg = &bl_pipe_msg.msg;
	struct bl_msg_hdr bl_msg = {
		.type = BL_DEVICE_MOUNT,
		.totallen = dev->mincount,
	};
	uint8_t *dataptr;
	DECLARE_WAITQUEUE(wq, current);
	int offset, len, i, rc;
	struct net *net = server->nfs_client->cl_net;
	struct nfs_net *nn = net_generic(net, nfs_net_id);
	struct bl_dev_msg *reply = &nn->bl_mount_reply;

	dprintk("%s CREATING PIPEFS MESSAGE\n", __func__);
	dprintk("%s: deviceid: %s, mincount: %d\n", __func__, dev->dev_id.data,
		dev->mincount);

	bl_pipe_msg.bl_wq = &nn->bl_wq;
	memset(msg, 0, sizeof(*msg));
	msg->data = kzalloc(sizeof(bl_msg) + dev->mincount, GFP_NOFS);
	if (!msg->data) {
		rv = ERR_PTR(-ENOMEM);
		goto out;
	}

	memcpy(msg->data, &bl_msg, sizeof(bl_msg));
	dataptr = (uint8_t *) msg->data;
	len = dev->mincount;
	offset = sizeof(bl_msg);
	for (i = 0; len > 0; i++) {
		memcpy(&dataptr[offset], page_address(dev->pages[i]),
				len < PAGE_CACHE_SIZE ? len : PAGE_CACHE_SIZE);
		len -= PAGE_CACHE_SIZE;
		offset += PAGE_CACHE_SIZE;
	}
	msg->len = sizeof(bl_msg) + dev->mincount;

	dprintk("%s CALLING USERSPACE DAEMON\n", __func__);
	add_wait_queue(&nn->bl_wq, &wq);
	rc = rpc_queue_upcall(nn->bl_device_pipe, msg);
	if (rc < 0) {
		remove_wait_queue(&nn->bl_wq, &wq);
		rv = ERR_PTR(rc);
		goto out;
	}

	set_current_state(TASK_UNINTERRUPTIBLE);
	schedule();
	__set_current_state(TASK_RUNNING);
	remove_wait_queue(&nn->bl_wq, &wq);

	if (reply->status != BL_DEVICE_REQUEST_PROC) {
		dprintk("%s failed to open device: %d\n",
			__func__, reply->status);
		rv = ERR_PTR(-EINVAL);
		goto out;
	}

	bd = blkdev_get_by_dev(MKDEV(reply->major, reply->minor),
			       FMODE_READ, NULL);
	if (IS_ERR(bd)) {
		dprintk("%s failed to open device : %ld\n", __func__,
			PTR_ERR(bd));
		rv = ERR_CAST(bd);
		goto out;
	}

	rv = kzalloc(sizeof(*rv), GFP_NOFS);
	if (!rv) {
		rv = ERR_PTR(-ENOMEM);
		goto out;
	}

	rv->bm_mdev = bd;
	memcpy(&rv->bm_mdevid, &dev->dev_id, sizeof(struct nfs4_deviceid));
	rv->net = net;
	dprintk("%s Created device %s with bd_block_size %u\n",
		__func__,
		bd->bd_disk->disk_name,
		bd->bd_block_size);

out:
	kfree(msg->data);
	return rv;
}

/* Map deviceid returned by the server to constructed block_device */
static struct block_device *translate_devid(struct pnfs_layout_hdr *lo,
					    struct nfs4_deviceid *id)
{
	struct block_device *rv = NULL;
	struct block_mount_id *mid;
	struct pnfs_block_dev *dev;

	dprintk("%s enter, lo=%p, id=%p\n", __func__, lo, id);
	mid = BLK_ID(lo);
	spin_lock(&mid->bm_lock);
	list_for_each_entry(dev, &mid->bm_devlist, bm_node) {
		if (memcmp(id->data, dev->bm_mdevid.data,
			   NFS4_DEVICEID4_SIZE) == 0) {
			rv = dev->bm_mdev;
			goto out;
		}
	}
 out:
	spin_unlock(&mid->bm_lock);
	dprintk("%s returning %p\n", __func__, rv);
	return rv;
}

/* Tracks info needed to ensure extents in layout obey constraints of spec */
struct layout_verification {
	u32 mode;	/* R or RW */
	u64 start;	/* Expected start of next non-COW extent */
	u64 inval;	/* Start of INVAL coverage */
	u64 cowread;	/* End of COW read coverage */
};

/* Verify the extent meets the layout requirements of the pnfs-block draft,
 * section 2.3.1.
 */
static int verify_extent(struct pnfs_block_extent *be,
			 struct layout_verification *lv)
{
	if (lv->mode == IOMODE_READ) {
		if (be->be_state == PNFS_BLOCK_READWRITE_DATA ||
		    be->be_state == PNFS_BLOCK_INVALID_DATA)
			return -EIO;
		if (be->be_f_offset != lv->start)
			return -EIO;
		lv->start += be->be_length;
		return 0;
	}
	/* lv->mode == IOMODE_RW */
	if (be->be_state == PNFS_BLOCK_READWRITE_DATA) {
		if (be->be_f_offset != lv->start)
			return -EIO;
		if (lv->cowread > lv->start)
			return -EIO;
		lv->start += be->be_length;
		lv->inval = lv->start;
		return 0;
	} else if (be->be_state == PNFS_BLOCK_INVALID_DATA) {
		if (be->be_f_offset != lv->start)
			return -EIO;
		lv->start += be->be_length;
		return 0;
	} else if (be->be_state == PNFS_BLOCK_READ_DATA) {
		if (be->be_f_offset > lv->start)
			return -EIO;
		if (be->be_f_offset < lv->inval)
			return -EIO;
		if (be->be_f_offset < lv->cowread)
			return -EIO;
		/* It looks like you might want to min this with lv->start,
		 * but you really don't.
		 */
		lv->inval = lv->inval + be->be_length;
		lv->cowread = be->be_f_offset + be->be_length;
		return 0;
	} else
		return -EIO;
}

/* XDR decode pnfs_block_layout4 structure */
int
nfs4_blk_process_layoutget(struct pnfs_layout_hdr *lo,
			   struct nfs4_layoutget_res *lgr, gfp_t gfp_flags)
{
	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;
	__be32 *p;
	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,
	};
	LIST_HEAD(extents);

	dprintk("---> %s\n", __func__);

	scratch = alloc_page(gfp_flags);
	if (!scratch)
		return -ENOMEM;

	xdr_init_decode_pages(&stream, &buf, lgr->layoutp->pages, lgr->layoutp->len);
	xdr_set_scratch_buffer(&stream, page_address(scratch), PAGE_SIZE);

	p = xdr_inline_decode(&stream, 4);
	if (unlikely(!p))
		goto out_err;

	count = be32_to_cpup(p++);

	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.
	 */
	for (i = 0; i < count; i++) {
		be = bl_alloc_extent();
		if (!be) {
			status = -ENOMEM;
			goto out_err;
		}
		memcpy(&be->be_devid, p, NFS4_DEVICEID4_SIZE);
		p += XDR_QUADLEN(NFS4_DEVICEID4_SIZE);
		be->be_mdev = translate_devid(lo, &be->be_devid);
		if (!be->be_mdev)
			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 (be->be_state == PNFS_BLOCK_INVALID_DATA)
			be->be_inval = &bl->bl_inval;
		if (verify_extent(be, &lv)) {
			dprintk("%s verify failed\n", __func__);
			goto out_err;
		}
		list_add_tail(&be->be_node, &extents);
	}
	if (lgr->range.offset + lgr->range.length !=
			lv.start << SECTOR_SHIFT) {
		dprintk("%s Final length mismatch\n", __func__);
		be = NULL;
		goto out_err;
	}
	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.
	 */
	spin_lock(&bl->bl_ext_lock);
	list_for_each_entry_safe(be, save, &extents, be_node) {
		list_del(&be->be_node);
		status = bl_add_merge_extent(bl, be);
		if (status) {
			spin_unlock(&bl->bl_ext_lock);
			/* This is a fairly catastrophic error, as the
			 * entire layout extent lists are now corrupted.
			 * We should have some way to distinguish this.
			 */
			be = NULL;
			goto out_err;
		}
	}
	spin_unlock(&bl->bl_ext_lock);
	status = 0;
 out:
	__free_page(scratch);
	dprintk("%s returns %i\n", __func__, status);
	return status;

 out_err:
	bl_put_extent(be);
	while (!list_empty(&extents)) {
		be = list_first_entry(&extents, struct pnfs_block_extent,
				      be_node);
		list_del(&be->be_node);
		bl_put_extent(be);
	}
	goto out;
}
Loading