diff options
Diffstat (limited to 'fs')
49 files changed, 539 insertions, 279 deletions
@@ -75,6 +75,7 @@ static struct kmem_cache *bio_find_or_create_slab(unsigned int extra_size)  	unsigned int sz = sizeof(struct bio) + extra_size;  	struct kmem_cache *slab = NULL;  	struct bio_slab *bslab, *new_bio_slabs; +	unsigned int new_bio_slab_max;  	unsigned int i, entry = -1;  	mutex_lock(&bio_slab_lock); @@ -97,12 +98,13 @@ static struct kmem_cache *bio_find_or_create_slab(unsigned int extra_size)  		goto out_unlock;  	if (bio_slab_nr == bio_slab_max && entry == -1) { -		bio_slab_max <<= 1; +		new_bio_slab_max = bio_slab_max << 1;  		new_bio_slabs = krealloc(bio_slabs, -					 bio_slab_max * sizeof(struct bio_slab), +					 new_bio_slab_max * sizeof(struct bio_slab),  					 GFP_KERNEL);  		if (!new_bio_slabs)  			goto out_unlock; +		bio_slab_max = new_bio_slab_max;  		bio_slabs = new_bio_slabs;  	}  	if (entry == -1) diff --git a/fs/block_dev.c b/fs/block_dev.c index b3c1d3dae77..1a1e5e3b1ea 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -1661,6 +1661,39 @@ static int blkdev_mmap(struct file *file, struct vm_area_struct *vma)  	return ret;  } +static ssize_t blkdev_splice_read(struct file *file, loff_t *ppos, +				  struct pipe_inode_info *pipe, size_t len, +				  unsigned int flags) +{ +	ssize_t ret; +	struct block_device *bdev = I_BDEV(file->f_mapping->host); + +	percpu_down_read(&bdev->bd_block_size_semaphore); + +	ret = generic_file_splice_read(file, ppos, pipe, len, flags); + +	percpu_up_read(&bdev->bd_block_size_semaphore); + +	return ret; +} + +static ssize_t blkdev_splice_write(struct pipe_inode_info *pipe, +				   struct file *file, loff_t *ppos, size_t len, +				   unsigned int flags) +{ +	ssize_t ret; +	struct block_device *bdev = I_BDEV(file->f_mapping->host); + +	percpu_down_read(&bdev->bd_block_size_semaphore); + +	ret = generic_file_splice_write(pipe, file, ppos, len, flags); + +	percpu_up_read(&bdev->bd_block_size_semaphore); + +	return ret; +} + +  /*   * Try to release a page associated with block device when the system   * is under memory pressure. @@ -1699,8 +1732,8 @@ const struct file_operations def_blk_fops = {  #ifdef CONFIG_COMPAT  	.compat_ioctl	= compat_blkdev_ioctl,  #endif -	.splice_read	= generic_file_splice_read, -	.splice_write	= generic_file_splice_write, +	.splice_read	= blkdev_splice_read, +	.splice_write	= blkdev_splice_write,  };  int ioctl_by_bdev(struct block_device *bdev, unsigned cmd, unsigned long arg) diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c index f3187938e08..208d8aa5b07 100644 --- a/fs/btrfs/backref.c +++ b/fs/btrfs/backref.c @@ -283,9 +283,7 @@ static int __resolve_indirect_ref(struct btrfs_fs_info *fs_info,  		goto out;  	} -	rcu_read_lock(); -	root_level = btrfs_header_level(root->node); -	rcu_read_unlock(); +	root_level = btrfs_old_root_level(root, time_seq);  	if (root_level + 1 == level)  		goto out; @@ -1177,16 +1175,15 @@ int btrfs_find_one_extref(struct btrfs_root *root, u64 inode_objectid,  	return ret;  } -static char *ref_to_path(struct btrfs_root *fs_root, -			 struct btrfs_path *path, -			 u32 name_len, unsigned long name_off, -			 struct extent_buffer *eb_in, u64 parent, -			 char *dest, u32 size) +char *btrfs_ref_to_path(struct btrfs_root *fs_root, struct btrfs_path *path, +			u32 name_len, unsigned long name_off, +			struct extent_buffer *eb_in, u64 parent, +			char *dest, u32 size)  {  	int slot;  	u64 next_inum;  	int ret; -	s64 bytes_left = size - 1; +	s64 bytes_left = ((s64)size) - 1;  	struct extent_buffer *eb = eb_in;  	struct btrfs_key found_key;  	int leave_spinning = path->leave_spinning; @@ -1266,10 +1263,10 @@ char *btrfs_iref_to_path(struct btrfs_root *fs_root,  			 struct extent_buffer *eb_in, u64 parent,  			 char *dest, u32 size)  { -	return ref_to_path(fs_root, path, -			   btrfs_inode_ref_name_len(eb_in, iref), -			   (unsigned long)(iref + 1), -			   eb_in, parent, dest, size); +	return btrfs_ref_to_path(fs_root, path, +				 btrfs_inode_ref_name_len(eb_in, iref), +				 (unsigned long)(iref + 1), +				 eb_in, parent, dest, size);  }  /* @@ -1715,9 +1712,8 @@ static int inode_to_path(u64 inum, u32 name_len, unsigned long name_off,  					ipath->fspath->bytes_left - s_ptr : 0;  	fspath_min = (char *)ipath->fspath->val + (i + 1) * s_ptr; -	fspath = ref_to_path(ipath->fs_root, ipath->btrfs_path, name_len, -			     name_off, eb, inum, fspath_min, -			     bytes_left); +	fspath = btrfs_ref_to_path(ipath->fs_root, ipath->btrfs_path, name_len, +				   name_off, eb, inum, fspath_min, bytes_left);  	if (IS_ERR(fspath))  		return PTR_ERR(fspath); diff --git a/fs/btrfs/backref.h b/fs/btrfs/backref.h index e75533043a5..d61feca7945 100644 --- a/fs/btrfs/backref.h +++ b/fs/btrfs/backref.h @@ -62,6 +62,10 @@ int btrfs_find_all_roots(struct btrfs_trans_handle *trans,  char *btrfs_iref_to_path(struct btrfs_root *fs_root, struct btrfs_path *path,  			 struct btrfs_inode_ref *iref, struct extent_buffer *eb,  			 u64 parent, char *dest, u32 size); +char *btrfs_ref_to_path(struct btrfs_root *fs_root, struct btrfs_path *path, +			u32 name_len, unsigned long name_off, +			struct extent_buffer *eb_in, u64 parent, +			char *dest, u32 size);  struct btrfs_data_container *init_data_container(u32 total_bytes);  struct inode_fs_paths *init_ipath(s32 total_bytes, struct btrfs_root *fs_root, diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index b3343621100..cdfb4c49a80 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c @@ -596,6 +596,11 @@ tree_mod_log_insert_move(struct btrfs_fs_info *fs_info,  	if (tree_mod_dont_log(fs_info, eb))  		return 0; +	/* +	 * When we override something during the move, we log these removals. +	 * This can only happen when we move towards the beginning of the +	 * buffer, i.e. dst_slot < src_slot. +	 */  	for (i = 0; i + dst_slot < src_slot && i < nr_items; i++) {  		ret = tree_mod_log_insert_key_locked(fs_info, eb, i + dst_slot,  					      MOD_LOG_KEY_REMOVE_WHILE_MOVING); @@ -647,8 +652,6 @@ tree_mod_log_insert_root(struct btrfs_fs_info *fs_info,  	if (tree_mod_dont_log(fs_info, NULL))  		return 0; -	__tree_mod_log_free_eb(fs_info, old_root); -  	ret = tree_mod_alloc(fs_info, flags, &tm);  	if (ret < 0)  		goto out; @@ -926,12 +929,7 @@ static noinline int update_ref_for_cow(struct btrfs_trans_handle *trans,  			ret = btrfs_dec_ref(trans, root, buf, 1, 1);  			BUG_ON(ret); /* -ENOMEM */  		} -		/* -		 * don't log freeing in case we're freeing the root node, this -		 * is done by tree_mod_log_set_root_pointer later -		 */ -		if (buf != root->node && btrfs_header_level(buf) != 0) -			tree_mod_log_free_eb(root->fs_info, buf); +		tree_mod_log_free_eb(root->fs_info, buf);  		clean_tree_block(trans, root, buf);  		*last_ref = 1;  	} @@ -1225,6 +1223,8 @@ tree_mod_log_rewind(struct btrfs_fs_info *fs_info, struct extent_buffer *eb,  	free_extent_buffer(eb);  	__tree_mod_log_rewind(eb_rewin, time_seq, tm); +	WARN_ON(btrfs_header_nritems(eb_rewin) > +		BTRFS_NODEPTRS_PER_BLOCK(fs_info->fs_root));  	return eb_rewin;  } @@ -1241,9 +1241,11 @@ get_old_root(struct btrfs_root *root, u64 time_seq)  {  	struct tree_mod_elem *tm;  	struct extent_buffer *eb; +	struct extent_buffer *old;  	struct tree_mod_root *old_root = NULL;  	u64 old_generation = 0;  	u64 logical; +	u32 blocksize;  	eb = btrfs_read_lock_root_node(root);  	tm = __tree_mod_log_oldest_root(root->fs_info, root, time_seq); @@ -1259,14 +1261,32 @@ get_old_root(struct btrfs_root *root, u64 time_seq)  	}  	tm = tree_mod_log_search(root->fs_info, logical, time_seq); -	if (old_root) +	if (old_root && tm && tm->op != MOD_LOG_KEY_REMOVE_WHILE_FREEING) { +		btrfs_tree_read_unlock(root->node); +		free_extent_buffer(root->node); +		blocksize = btrfs_level_size(root, old_root->level); +		old = read_tree_block(root, logical, blocksize, 0); +		if (!old) { +			pr_warn("btrfs: failed to read tree block %llu from get_old_root\n", +				logical); +			WARN_ON(1); +		} else { +			eb = btrfs_clone_extent_buffer(old); +			free_extent_buffer(old); +		} +	} else if (old_root) { +		btrfs_tree_read_unlock(root->node); +		free_extent_buffer(root->node);  		eb = alloc_dummy_extent_buffer(logical, root->nodesize); -	else +	} else {  		eb = btrfs_clone_extent_buffer(root->node); -	btrfs_tree_read_unlock(root->node); -	free_extent_buffer(root->node); +		btrfs_tree_read_unlock(root->node); +		free_extent_buffer(root->node); +	} +  	if (!eb)  		return NULL; +	extent_buffer_get(eb);  	btrfs_tree_read_lock(eb);  	if (old_root) {  		btrfs_set_header_bytenr(eb, eb->start); @@ -1279,11 +1299,28 @@ get_old_root(struct btrfs_root *root, u64 time_seq)  		__tree_mod_log_rewind(eb, time_seq, tm);  	else  		WARN_ON(btrfs_header_level(eb) != 0); -	extent_buffer_get(eb); +	WARN_ON(btrfs_header_nritems(eb) > BTRFS_NODEPTRS_PER_BLOCK(root));  	return eb;  } +int btrfs_old_root_level(struct btrfs_root *root, u64 time_seq) +{ +	struct tree_mod_elem *tm; +	int level; + +	tm = __tree_mod_log_oldest_root(root->fs_info, root, time_seq); +	if (tm && tm->op == MOD_LOG_ROOT_REPLACE) { +		level = tm->old_root.level; +	} else { +		rcu_read_lock(); +		level = btrfs_header_level(root->node); +		rcu_read_unlock(); +	} + +	return level; +} +  static inline int should_cow_block(struct btrfs_trans_handle *trans,  				   struct btrfs_root *root,  				   struct extent_buffer *buf) @@ -1725,6 +1762,7 @@ static noinline int balance_level(struct btrfs_trans_handle *trans,  			goto enospc;  		} +		tree_mod_log_free_eb(root->fs_info, root->node);  		tree_mod_log_set_root_pointer(root, child);  		rcu_assign_pointer(root->node, child); @@ -2970,8 +3008,10 @@ static int push_node_left(struct btrfs_trans_handle *trans,  			   push_items * sizeof(struct btrfs_key_ptr));  	if (push_items < src_nritems) { -		tree_mod_log_eb_move(root->fs_info, src, 0, push_items, -				     src_nritems - push_items); +		/* +		 * don't call tree_mod_log_eb_move here, key removal was already +		 * fully logged by tree_mod_log_eb_copy above. +		 */  		memmove_extent_buffer(src, btrfs_node_key_ptr_offset(0),  				      btrfs_node_key_ptr_offset(push_items),  				      (src_nritems - push_items) * diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 926c9ffc66d..c72ead86950 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -3120,6 +3120,7 @@ static inline u64 btrfs_inc_tree_mod_seq(struct btrfs_fs_info *fs_info)  {  	return atomic_inc_return(&fs_info->tree_mod_seq);  } +int btrfs_old_root_level(struct btrfs_root *root, u64 time_seq);  /* root-item.c */  int btrfs_find_root_ref(struct btrfs_root *tree_root, @@ -3338,6 +3339,8 @@ struct extent_map *btrfs_get_extent(struct inode *inode, struct page *page,  int btrfs_update_inode(struct btrfs_trans_handle *trans,  			      struct btrfs_root *root,  			      struct inode *inode); +int btrfs_update_inode_fallback(struct btrfs_trans_handle *trans, +				struct btrfs_root *root, struct inode *inode);  int btrfs_orphan_add(struct btrfs_trans_handle *trans, struct inode *inode);  int btrfs_orphan_del(struct btrfs_trans_handle *trans, struct inode *inode);  int btrfs_orphan_cleanup(struct btrfs_root *root); diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 8036d3a8485..472873a94d9 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -4110,8 +4110,8 @@ struct extent_buffer *alloc_dummy_extent_buffer(u64 start, unsigned long len)  	return eb;  err: -	for (i--; i >= 0; i--) -		__free_page(eb->pages[i]); +	for (; i > 0; i--) +		__free_page(eb->pages[i - 1]);  	__free_extent_buffer(eb);  	return NULL;  } diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 85a1e5053fe..95542a1b3df 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -94,8 +94,6 @@ static noinline int cow_file_range(struct inode *inode,  				   struct page *locked_page,  				   u64 start, u64 end, int *page_started,  				   unsigned long *nr_written, int unlock); -static noinline int btrfs_update_inode_fallback(struct btrfs_trans_handle *trans, -				struct btrfs_root *root, struct inode *inode);  static int btrfs_init_inode_security(struct btrfs_trans_handle *trans,  				     struct inode *inode,  struct inode *dir, @@ -2746,8 +2744,9 @@ noinline int btrfs_update_inode(struct btrfs_trans_handle *trans,  	return btrfs_update_inode_item(trans, root, inode);  } -static noinline int btrfs_update_inode_fallback(struct btrfs_trans_handle *trans, -				struct btrfs_root *root, struct inode *inode) +noinline int btrfs_update_inode_fallback(struct btrfs_trans_handle *trans, +					 struct btrfs_root *root, +					 struct inode *inode)  {  	int ret; diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 61168805f17..8fcf9a59c28 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -343,7 +343,8 @@ static noinline int btrfs_ioctl_fitrim(struct file *file, void __user *arg)  		return -EOPNOTSUPP;  	if (copy_from_user(&range, arg, sizeof(range)))  		return -EFAULT; -	if (range.start > total_bytes) +	if (range.start > total_bytes || +	    range.len < fs_info->sb->s_blocksize)  		return -EINVAL;  	range.len = min(range.len, total_bytes - range.start); @@ -570,7 +571,8 @@ static int create_snapshot(struct btrfs_root *root, struct dentry *dentry,  		ret = btrfs_commit_transaction(trans,  					       root->fs_info->extent_root);  	} -	BUG_ON(ret); +	if (ret) +		goto fail;  	ret = pending_snapshot->error;  	if (ret) diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c index 5039686df6a..fe9d02c45f8 100644 --- a/fs/btrfs/qgroup.c +++ b/fs/btrfs/qgroup.c @@ -790,8 +790,10 @@ int btrfs_quota_enable(struct btrfs_trans_handle *trans,  	}  	path = btrfs_alloc_path(); -	if (!path) -		return -ENOMEM; +	if (!path) { +		ret = -ENOMEM; +		goto out_free_root; +	}  	key.objectid = 0;  	key.type = BTRFS_QGROUP_STATUS_KEY; @@ -800,7 +802,7 @@ int btrfs_quota_enable(struct btrfs_trans_handle *trans,  	ret = btrfs_insert_empty_item(trans, quota_root, path, &key,  				      sizeof(*ptr));  	if (ret) -		goto out; +		goto out_free_path;  	leaf = path->nodes[0];  	ptr = btrfs_item_ptr(leaf, path->slots[0], @@ -818,8 +820,15 @@ int btrfs_quota_enable(struct btrfs_trans_handle *trans,  	fs_info->quota_root = quota_root;  	fs_info->pending_quota_state = 1;  	spin_unlock(&fs_info->qgroup_lock); -out: +out_free_path:  	btrfs_free_path(path); +out_free_root: +	if (ret) { +		free_extent_buffer(quota_root->node); +		free_extent_buffer(quota_root->commit_root); +		kfree(quota_root); +	} +out:  	return ret;  } diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c index c7beb543a4a..e78b297b0b0 100644 --- a/fs/btrfs/send.c +++ b/fs/btrfs/send.c @@ -745,31 +745,36 @@ typedef int (*iterate_inode_ref_t)(int num, u64 dir, int index,  				   void *ctx);  /* - * Helper function to iterate the entries in ONE btrfs_inode_ref. + * Helper function to iterate the entries in ONE btrfs_inode_ref or + * btrfs_inode_extref.   * The iterate callback may return a non zero value to stop iteration. This can   * be a negative value for error codes or 1 to simply stop it.   * - * path must point to the INODE_REF when called. + * path must point to the INODE_REF or INODE_EXTREF when called.   */  static int iterate_inode_ref(struct send_ctx *sctx,  			     struct btrfs_root *root, struct btrfs_path *path,  			     struct btrfs_key *found_key, int resolve,  			     iterate_inode_ref_t iterate, void *ctx)  { -	struct extent_buffer *eb; +	struct extent_buffer *eb = path->nodes[0];  	struct btrfs_item *item;  	struct btrfs_inode_ref *iref; +	struct btrfs_inode_extref *extref;  	struct btrfs_path *tmp_path;  	struct fs_path *p; -	u32 cur; -	u32 len; +	u32 cur = 0;  	u32 total; -	int slot; +	int slot = path->slots[0];  	u32 name_len;  	char *start;  	int ret = 0; -	int num; +	int num = 0;  	int index; +	u64 dir; +	unsigned long name_off; +	unsigned long elem_size; +	unsigned long ptr;  	p = fs_path_alloc_reversed(sctx);  	if (!p) @@ -781,24 +786,40 @@ static int iterate_inode_ref(struct send_ctx *sctx,  		return -ENOMEM;  	} -	eb = path->nodes[0]; -	slot = path->slots[0]; -	item = btrfs_item_nr(eb, slot); -	iref = btrfs_item_ptr(eb, slot, struct btrfs_inode_ref); -	cur = 0; -	len = 0; -	total = btrfs_item_size(eb, item); -	num = 0; +	if (found_key->type == BTRFS_INODE_REF_KEY) { +		ptr = (unsigned long)btrfs_item_ptr(eb, slot, +						    struct btrfs_inode_ref); +		item = btrfs_item_nr(eb, slot); +		total = btrfs_item_size(eb, item); +		elem_size = sizeof(*iref); +	} else { +		ptr = btrfs_item_ptr_offset(eb, slot); +		total = btrfs_item_size_nr(eb, slot); +		elem_size = sizeof(*extref); +	} +  	while (cur < total) {  		fs_path_reset(p); -		name_len = btrfs_inode_ref_name_len(eb, iref); -		index = btrfs_inode_ref_index(eb, iref); +		if (found_key->type == BTRFS_INODE_REF_KEY) { +			iref = (struct btrfs_inode_ref *)(ptr + cur); +			name_len = btrfs_inode_ref_name_len(eb, iref); +			name_off = (unsigned long)(iref + 1); +			index = btrfs_inode_ref_index(eb, iref); +			dir = found_key->offset; +		} else { +			extref = (struct btrfs_inode_extref *)(ptr + cur); +			name_len = btrfs_inode_extref_name_len(eb, extref); +			name_off = (unsigned long)&extref->name; +			index = btrfs_inode_extref_index(eb, extref); +			dir = btrfs_inode_extref_parent(eb, extref); +		} +  		if (resolve) { -			start = btrfs_iref_to_path(root, tmp_path, iref, eb, -						found_key->offset, p->buf, -						p->buf_len); +			start = btrfs_ref_to_path(root, tmp_path, name_len, +						  name_off, eb, dir, +						  p->buf, p->buf_len);  			if (IS_ERR(start)) {  				ret = PTR_ERR(start);  				goto out; @@ -809,9 +830,10 @@ static int iterate_inode_ref(struct send_ctx *sctx,  						p->buf_len + p->buf - start);  				if (ret < 0)  					goto out; -				start = btrfs_iref_to_path(root, tmp_path, iref, -						eb, found_key->offset, p->buf, -						p->buf_len); +				start = btrfs_ref_to_path(root, tmp_path, +							  name_len, name_off, +							  eb, dir, +							  p->buf, p->buf_len);  				if (IS_ERR(start)) {  					ret = PTR_ERR(start);  					goto out; @@ -820,21 +842,16 @@ static int iterate_inode_ref(struct send_ctx *sctx,  			}  			p->start = start;  		} else { -			ret = fs_path_add_from_extent_buffer(p, eb, -					(unsigned long)(iref + 1), name_len); +			ret = fs_path_add_from_extent_buffer(p, eb, name_off, +							     name_len);  			if (ret < 0)  				goto out;  		} - -		len = sizeof(*iref) + name_len; -		iref = (struct btrfs_inode_ref *)((char *)iref + len); -		cur += len; - -		ret = iterate(num, found_key->offset, index, p, ctx); +		cur += elem_size + name_len; +		ret = iterate(num, dir, index, p, ctx);  		if (ret)  			goto out; -  		num++;  	} @@ -998,7 +1015,8 @@ static int get_inode_path(struct send_ctx *sctx, struct btrfs_root *root,  	}  	btrfs_item_key_to_cpu(p->nodes[0], &found_key, p->slots[0]);  	if (found_key.objectid != ino || -		found_key.type != BTRFS_INODE_REF_KEY) { +	    (found_key.type != BTRFS_INODE_REF_KEY && +	     found_key.type != BTRFS_INODE_EXTREF_KEY)) {  		ret = -ENOENT;  		goto out;  	} @@ -1551,8 +1569,8 @@ static int get_first_ref(struct send_ctx *sctx,  	struct btrfs_key key;  	struct btrfs_key found_key;  	struct btrfs_path *path; -	struct btrfs_inode_ref *iref;  	int len; +	u64 parent_dir;  	path = alloc_path_for_send();  	if (!path) @@ -1568,27 +1586,41 @@ static int get_first_ref(struct send_ctx *sctx,  	if (!ret)  		btrfs_item_key_to_cpu(path->nodes[0], &found_key,  				path->slots[0]); -	if (ret || found_key.objectid != key.objectid || -	    found_key.type != key.type) { +	if (ret || found_key.objectid != ino || +	    (found_key.type != BTRFS_INODE_REF_KEY && +	     found_key.type != BTRFS_INODE_EXTREF_KEY)) {  		ret = -ENOENT;  		goto out;  	} -	iref = btrfs_item_ptr(path->nodes[0], path->slots[0], -			struct btrfs_inode_ref); -	len = btrfs_inode_ref_name_len(path->nodes[0], iref); -	ret = fs_path_add_from_extent_buffer(name, path->nodes[0], -			(unsigned long)(iref + 1), len); +	if (key.type == BTRFS_INODE_REF_KEY) { +		struct btrfs_inode_ref *iref; +		iref = btrfs_item_ptr(path->nodes[0], path->slots[0], +				      struct btrfs_inode_ref); +		len = btrfs_inode_ref_name_len(path->nodes[0], iref); +		ret = fs_path_add_from_extent_buffer(name, path->nodes[0], +						     (unsigned long)(iref + 1), +						     len); +		parent_dir = found_key.offset; +	} else { +		struct btrfs_inode_extref *extref; +		extref = btrfs_item_ptr(path->nodes[0], path->slots[0], +					struct btrfs_inode_extref); +		len = btrfs_inode_extref_name_len(path->nodes[0], extref); +		ret = fs_path_add_from_extent_buffer(name, path->nodes[0], +					(unsigned long)&extref->name, len); +		parent_dir = btrfs_inode_extref_parent(path->nodes[0], extref); +	}  	if (ret < 0)  		goto out;  	btrfs_release_path(path); -	ret = get_inode_info(root, found_key.offset, NULL, dir_gen, NULL, NULL, +	ret = get_inode_info(root, parent_dir, NULL, dir_gen, NULL, NULL,  			NULL, NULL);  	if (ret < 0)  		goto out; -	*dir = found_key.offset; +	*dir = parent_dir;  out:  	btrfs_free_path(path); @@ -2430,7 +2462,8 @@ verbose_printk("btrfs: send_create_inode %llu\n", ino);  		TLV_PUT_PATH(sctx, BTRFS_SEND_A_PATH_LINK, p);  	} else if (S_ISCHR(mode) || S_ISBLK(mode) ||  		   S_ISFIFO(mode) || S_ISSOCK(mode)) { -		TLV_PUT_U64(sctx, BTRFS_SEND_A_RDEV, rdev); +		TLV_PUT_U64(sctx, BTRFS_SEND_A_RDEV, new_encode_dev(rdev)); +		TLV_PUT_U64(sctx, BTRFS_SEND_A_MODE, mode);  	}  	ret = send_cmd(sctx); @@ -3226,7 +3259,8 @@ static int process_all_refs(struct send_ctx *sctx,  		btrfs_item_key_to_cpu(eb, &found_key, slot);  		if (found_key.objectid != key.objectid || -		    found_key.type != key.type) +		    (found_key.type != BTRFS_INODE_REF_KEY && +		     found_key.type != BTRFS_INODE_EXTREF_KEY))  			break;  		ret = iterate_inode_ref(sctx, root, path, &found_key, 0, cb, @@ -3987,7 +4021,7 @@ static int process_recorded_refs_if_needed(struct send_ctx *sctx, int at_end)  	if (sctx->cur_ino == 0)  		goto out;  	if (!at_end && sctx->cur_ino == sctx->cmp_key->objectid && -	    sctx->cmp_key->type <= BTRFS_INODE_REF_KEY) +	    sctx->cmp_key->type <= BTRFS_INODE_EXTREF_KEY)  		goto out;  	if (list_empty(&sctx->new_refs) && list_empty(&sctx->deleted_refs))  		goto out; @@ -4033,22 +4067,21 @@ static int finish_inode_if_needed(struct send_ctx *sctx, int at_end)  	if (ret < 0)  		goto out; -	if (!S_ISLNK(sctx->cur_inode_mode)) { -		if (!sctx->parent_root || sctx->cur_inode_new) { +	if (!sctx->parent_root || sctx->cur_inode_new) { +		need_chown = 1; +		if (!S_ISLNK(sctx->cur_inode_mode))  			need_chmod = 1; -			need_chown = 1; -		} else { -			ret = get_inode_info(sctx->parent_root, sctx->cur_ino, -					NULL, NULL, &right_mode, &right_uid, -					&right_gid, NULL); -			if (ret < 0) -				goto out; +	} else { +		ret = get_inode_info(sctx->parent_root, sctx->cur_ino, +				NULL, NULL, &right_mode, &right_uid, +				&right_gid, NULL); +		if (ret < 0) +			goto out; -			if (left_uid != right_uid || left_gid != right_gid) -				need_chown = 1; -			if (left_mode != right_mode) -				need_chmod = 1; -		} +		if (left_uid != right_uid || left_gid != right_gid) +			need_chown = 1; +		if (!S_ISLNK(sctx->cur_inode_mode) && left_mode != right_mode) +			need_chmod = 1;  	}  	if (S_ISREG(sctx->cur_inode_mode)) { @@ -4335,7 +4368,8 @@ static int changed_cb(struct btrfs_root *left_root,  	if (key->type == BTRFS_INODE_ITEM_KEY)  		ret = changed_inode(sctx, result); -	else if (key->type == BTRFS_INODE_REF_KEY) +	else if (key->type == BTRFS_INODE_REF_KEY || +		 key->type == BTRFS_INODE_EXTREF_KEY)  		ret = changed_ref(sctx, result);  	else if (key->type == BTRFS_XATTR_ITEM_KEY)  		ret = changed_xattr(sctx, result); diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index 77db875b511..04bbfb1052e 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c @@ -1200,7 +1200,7 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,  	btrfs_i_size_write(parent_inode, parent_inode->i_size +  					 dentry->d_name.len * 2);  	parent_inode->i_mtime = parent_inode->i_ctime = CURRENT_TIME; -	ret = btrfs_update_inode(trans, parent_root, parent_inode); +	ret = btrfs_update_inode_fallback(trans, parent_root, parent_inode);  	if (ret)  		btrfs_abort_transaction(trans, root, ret);  fail: diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 029b903a4ae..0f5ebb72a5e 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -1819,6 +1819,13 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path)  				    "Failed to relocate sys chunks after "  				    "device initialization. This can be fixed "  				    "using the \"btrfs balance\" command."); +		trans = btrfs_attach_transaction(root); +		if (IS_ERR(trans)) { +			if (PTR_ERR(trans) == -ENOENT) +				return 0; +			return PTR_ERR(trans); +		} +		ret = btrfs_commit_transaction(trans, root);  	}  	return ret; diff --git a/fs/ceph/export.c b/fs/ceph/export.c index 02ce90972d8..9349bb37a2f 100644 --- a/fs/ceph/export.c +++ b/fs/ceph/export.c @@ -90,6 +90,8 @@ static int ceph_encode_fh(struct inode *inode, u32 *rawfh, int *max_len,  		*max_len = handle_length;  		type = 255;  	} +	if (dentry) +		dput(dentry);  	return type;  } diff --git a/fs/char_dev.c b/fs/char_dev.c index 3f152b92a94..afc2bb69178 100644 --- a/fs/char_dev.c +++ b/fs/char_dev.c @@ -471,9 +471,19 @@ static int exact_lock(dev_t dev, void *data)   */  int cdev_add(struct cdev *p, dev_t dev, unsigned count)  { +	int error; +  	p->dev = dev;  	p->count = count; -	return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p); + +	error = kobj_map(cdev_map, dev, count, NULL, +			 exact_match, exact_lock, p); +	if (error) +		return error; + +	kobject_get(p->kobj.parent); + +	return 0;  }  static void cdev_unmap(dev_t dev, unsigned count) @@ -498,14 +508,20 @@ void cdev_del(struct cdev *p)  static void cdev_default_release(struct kobject *kobj)  {  	struct cdev *p = container_of(kobj, struct cdev, kobj); +	struct kobject *parent = kobj->parent; +  	cdev_purge(p); +	kobject_put(parent);  }  static void cdev_dynamic_release(struct kobject *kobj)  {  	struct cdev *p = container_of(kobj, struct cdev, kobj); +	struct kobject *parent = kobj->parent; +  	cdev_purge(p);  	kfree(p); +	kobject_put(parent);  }  static struct kobj_type ktype_cdev_default = { diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c index f5054025f9d..4c6285fff59 100644 --- a/fs/compat_ioctl.c +++ b/fs/compat_ioctl.c @@ -210,6 +210,8 @@ static int do_video_set_spu_palette(unsigned int fd, unsigned int cmd,  	err  = get_user(palp, &up->palette);  	err |= get_user(length, &up->length); +	if (err) +		return -EFAULT;  	up_native = compat_alloc_user_space(sizeof(struct video_spu_palette));  	err  = put_user(compat_ptr(palp), &up_native->palette); diff --git a/fs/exec.c b/fs/exec.c index 8b9011b6704..0039055b1fc 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1083,7 +1083,8 @@ int flush_old_exec(struct linux_binprm * bprm)  	bprm->mm = NULL;		/* We're using it now */  	set_fs(USER_DS); -	current->flags &= ~(PF_RANDOMIZE | PF_FORKNOEXEC | PF_KTHREAD); +	current->flags &= +		~(PF_RANDOMIZE | PF_FORKNOEXEC | PF_KTHREAD | PF_NOFREEZE);  	flush_thread();  	current->personality &= ~bprm->per_clear; diff --git a/fs/ext4/balloc.c b/fs/ext4/balloc.c index 1b5089067d0..cf1821784a1 100644 --- a/fs/ext4/balloc.c +++ b/fs/ext4/balloc.c @@ -174,8 +174,7 @@ void ext4_init_block_bitmap(struct super_block *sb, struct buffer_head *bh,  		ext4_free_inodes_set(sb, gdp, 0);  		ext4_itable_unused_set(sb, gdp, 0);  		memset(bh->b_data, 0xff, sb->s_blocksize); -		ext4_block_bitmap_csum_set(sb, block_group, gdp, bh, -					   EXT4_BLOCKS_PER_GROUP(sb) / 8); +		ext4_block_bitmap_csum_set(sb, block_group, gdp, bh);  		return;  	}  	memset(bh->b_data, 0, sb->s_blocksize); @@ -212,8 +211,7 @@ void ext4_init_block_bitmap(struct super_block *sb, struct buffer_head *bh,  	 */  	ext4_mark_bitmap_end(num_clusters_in_group(sb, block_group),  			     sb->s_blocksize * 8, bh->b_data); -	ext4_block_bitmap_csum_set(sb, block_group, gdp, bh, -				   EXT4_BLOCKS_PER_GROUP(sb) / 8); +	ext4_block_bitmap_csum_set(sb, block_group, gdp, bh);  	ext4_group_desc_csum_set(sb, block_group, gdp);  } @@ -350,7 +348,7 @@ void ext4_validate_block_bitmap(struct super_block *sb,  		return;  	}  	if (unlikely(!ext4_block_bitmap_csum_verify(sb, block_group, -			desc, bh, EXT4_BLOCKS_PER_GROUP(sb) / 8))) { +			desc, bh))) {  		ext4_unlock_group(sb, block_group);  		ext4_error(sb, "bg %u: bad block bitmap checksum", block_group);  		return; diff --git a/fs/ext4/bitmap.c b/fs/ext4/bitmap.c index 5c2d1813ebe..3285aa5a706 100644 --- a/fs/ext4/bitmap.c +++ b/fs/ext4/bitmap.c @@ -58,11 +58,12 @@ void ext4_inode_bitmap_csum_set(struct super_block *sb, ext4_group_t group,  int ext4_block_bitmap_csum_verify(struct super_block *sb, ext4_group_t group,  				  struct ext4_group_desc *gdp, -				  struct buffer_head *bh, int sz) +				  struct buffer_head *bh)  {  	__u32 hi;  	__u32 provided, calculated;  	struct ext4_sb_info *sbi = EXT4_SB(sb); +	int sz = EXT4_CLUSTERS_PER_GROUP(sb) / 8;  	if (!EXT4_HAS_RO_COMPAT_FEATURE(sb,  					EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) @@ -84,8 +85,9 @@ int ext4_block_bitmap_csum_verify(struct super_block *sb, ext4_group_t group,  void ext4_block_bitmap_csum_set(struct super_block *sb, ext4_group_t group,  				struct ext4_group_desc *gdp, -				struct buffer_head *bh, int sz) +				struct buffer_head *bh)  { +	int sz = EXT4_CLUSTERS_PER_GROUP(sb) / 8;  	__u32 csum;  	struct ext4_sb_info *sbi = EXT4_SB(sb); diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 3ab2539b7b2..3c20de1d59d 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -1882,10 +1882,10 @@ int ext4_inode_bitmap_csum_verify(struct super_block *sb, ext4_group_t group,  				  struct buffer_head *bh, int sz);  void ext4_block_bitmap_csum_set(struct super_block *sb, ext4_group_t group,  				struct ext4_group_desc *gdp, -				struct buffer_head *bh, int sz); +				struct buffer_head *bh);  int ext4_block_bitmap_csum_verify(struct super_block *sb, ext4_group_t group,  				  struct ext4_group_desc *gdp, -				  struct buffer_head *bh, int sz); +				  struct buffer_head *bh);  /* balloc.c */  extern void ext4_validate_block_bitmap(struct super_block *sb, @@ -2063,8 +2063,7 @@ extern int ext4_resize_fs(struct super_block *sb, ext4_fsblk_t n_blocks_count);  extern int ext4_calculate_overhead(struct super_block *sb);  extern int ext4_superblock_csum_verify(struct super_block *sb,  				       struct ext4_super_block *es); -extern void ext4_superblock_csum_set(struct super_block *sb, -				     struct ext4_super_block *es); +extern void ext4_superblock_csum_set(struct super_block *sb);  extern void *ext4_kvmalloc(size_t size, gfp_t flags);  extern void *ext4_kvzalloc(size_t size, gfp_t flags);  extern void ext4_kvfree(void *ptr); diff --git a/fs/ext4/ext4_jbd2.c b/fs/ext4/ext4_jbd2.c index bfa65b49d42..b4323ba846b 100644 --- a/fs/ext4/ext4_jbd2.c +++ b/fs/ext4/ext4_jbd2.c @@ -143,17 +143,13 @@ int __ext4_handle_dirty_super(const char *where, unsigned int line,  	struct buffer_head *bh = EXT4_SB(sb)->s_sbh;  	int err = 0; +	ext4_superblock_csum_set(sb);  	if (ext4_handle_valid(handle)) { -		ext4_superblock_csum_set(sb, -				(struct ext4_super_block *)bh->b_data);  		err = jbd2_journal_dirty_metadata(handle, bh);  		if (err)  			ext4_journal_abort_handle(where, line, __func__,  						  bh, handle, err); -	} else { -		ext4_superblock_csum_set(sb, -				(struct ext4_super_block *)bh->b_data); +	} else  		mark_buffer_dirty(bh); -	}  	return err;  } diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index 1c94cca35ed..7011ac96720 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -52,6 +52,9 @@  #define EXT4_EXT_MARK_UNINIT1	0x2  /* mark first half uninitialized */  #define EXT4_EXT_MARK_UNINIT2	0x4  /* mark second half uninitialized */ +#define EXT4_EXT_DATA_VALID1	0x8  /* first half contains valid data */ +#define EXT4_EXT_DATA_VALID2	0x10 /* second half contains valid data */ +  static __le32 ext4_extent_block_csum(struct inode *inode,  				     struct ext4_extent_header *eh)  { @@ -2914,6 +2917,9 @@ static int ext4_split_extent_at(handle_t *handle,  	unsigned int ee_len, depth;  	int err = 0; +	BUG_ON((split_flag & (EXT4_EXT_DATA_VALID1 | EXT4_EXT_DATA_VALID2)) == +	       (EXT4_EXT_DATA_VALID1 | EXT4_EXT_DATA_VALID2)); +  	ext_debug("ext4_split_extents_at: inode %lu, logical"  		"block %llu\n", inode->i_ino, (unsigned long long)split); @@ -2972,7 +2978,14 @@ static int ext4_split_extent_at(handle_t *handle,  	err = ext4_ext_insert_extent(handle, inode, path, &newex, flags);  	if (err == -ENOSPC && (EXT4_EXT_MAY_ZEROOUT & split_flag)) { -		err = ext4_ext_zeroout(inode, &orig_ex); +		if (split_flag & (EXT4_EXT_DATA_VALID1|EXT4_EXT_DATA_VALID2)) { +			if (split_flag & EXT4_EXT_DATA_VALID1) +				err = ext4_ext_zeroout(inode, ex2); +			else +				err = ext4_ext_zeroout(inode, ex); +		} else +			err = ext4_ext_zeroout(inode, &orig_ex); +  		if (err)  			goto fix_extent_len;  		/* update the extent length and mark as initialized */ @@ -3025,12 +3038,13 @@ static int ext4_split_extent(handle_t *handle,  	uninitialized = ext4_ext_is_uninitialized(ex);  	if (map->m_lblk + map->m_len < ee_block + ee_len) { -		split_flag1 = split_flag & EXT4_EXT_MAY_ZEROOUT ? -			      EXT4_EXT_MAY_ZEROOUT : 0; +		split_flag1 = split_flag & EXT4_EXT_MAY_ZEROOUT;  		flags1 = flags | EXT4_GET_BLOCKS_PRE_IO;  		if (uninitialized)  			split_flag1 |= EXT4_EXT_MARK_UNINIT1 |  				       EXT4_EXT_MARK_UNINIT2; +		if (split_flag & EXT4_EXT_DATA_VALID2) +			split_flag1 |= EXT4_EXT_DATA_VALID1;  		err = ext4_split_extent_at(handle, inode, path,  				map->m_lblk + map->m_len, split_flag1, flags1);  		if (err) @@ -3043,8 +3057,8 @@ static int ext4_split_extent(handle_t *handle,  		return PTR_ERR(path);  	if (map->m_lblk >= ee_block) { -		split_flag1 = split_flag & EXT4_EXT_MAY_ZEROOUT ? -			      EXT4_EXT_MAY_ZEROOUT : 0; +		split_flag1 = split_flag & (EXT4_EXT_MAY_ZEROOUT | +					    EXT4_EXT_DATA_VALID2);  		if (uninitialized)  			split_flag1 |= EXT4_EXT_MARK_UNINIT1;  		if (split_flag & EXT4_EXT_MARK_UNINIT2) @@ -3323,26 +3337,47 @@ static int ext4_split_unwritten_extents(handle_t *handle,  	split_flag |= ee_block + ee_len <= eof_block ? EXT4_EXT_MAY_ZEROOUT : 0;  	split_flag |= EXT4_EXT_MARK_UNINIT2; - +	if (flags & EXT4_GET_BLOCKS_CONVERT) +		split_flag |= EXT4_EXT_DATA_VALID2;  	flags |= EXT4_GET_BLOCKS_PRE_IO;  	return ext4_split_extent(handle, inode, path, map, split_flag, flags);  }  static int ext4_convert_unwritten_extents_endio(handle_t *handle, -					      struct inode *inode, -					      struct ext4_ext_path *path) +						struct inode *inode, +						struct ext4_map_blocks *map, +						struct ext4_ext_path *path)  {  	struct ext4_extent *ex; +	ext4_lblk_t ee_block; +	unsigned int ee_len;  	int depth;  	int err = 0;  	depth = ext_depth(inode);  	ex = path[depth].p_ext; +	ee_block = le32_to_cpu(ex->ee_block); +	ee_len = ext4_ext_get_actual_len(ex);  	ext_debug("ext4_convert_unwritten_extents_endio: inode %lu, logical"  		"block %llu, max_blocks %u\n", inode->i_ino, -		(unsigned long long)le32_to_cpu(ex->ee_block), -		ext4_ext_get_actual_len(ex)); +		  (unsigned long long)ee_block, ee_len); + +	/* If extent is larger than requested then split is required */ +	if (ee_block != map->m_lblk || ee_len > map->m_len) { +		err = ext4_split_unwritten_extents(handle, inode, map, path, +						   EXT4_GET_BLOCKS_CONVERT); +		if (err < 0) +			goto out; +		ext4_ext_drop_refs(path); +		path = ext4_ext_find_extent(inode, map->m_lblk, path); +		if (IS_ERR(path)) { +			err = PTR_ERR(path); +			goto out; +		} +		depth = ext_depth(inode); +		ex = path[depth].p_ext; +	}  	err = ext4_ext_get_access(handle, inode, path + depth);  	if (err) @@ -3652,7 +3687,7 @@ ext4_ext_handle_uninitialized_extents(handle_t *handle, struct inode *inode,  	}  	/* IO end_io complete, convert the filled extent to written */  	if ((flags & EXT4_GET_BLOCKS_CONVERT)) { -		ret = ext4_convert_unwritten_extents_endio(handle, inode, +		ret = ext4_convert_unwritten_extents_endio(handle, inode, map,  							path);  		if (ret >= 0) {  			ext4_update_inode_fsync_trans(handle, inode, 1); @@ -4428,6 +4463,9 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len)  	 */  	if (len <= EXT_UNINIT_MAX_LEN << blkbits)  		flags |= EXT4_GET_BLOCKS_NO_NORMALIZE; + +	/* Prevent race condition between unwritten */ +	ext4_flush_unwritten_io(inode);  retry:  	while (ret >= 0 && ret < max_blocks) {  		map.m_lblk = map.m_lblk + ret; diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c index fa36372f3fd..3a100e7a62a 100644 --- a/fs/ext4/ialloc.c +++ b/fs/ext4/ialloc.c @@ -725,6 +725,10 @@ repeat_in_this_group:  				   "inode=%lu", ino + 1);  			continue;  		} +		BUFFER_TRACE(inode_bitmap_bh, "get_write_access"); +		err = ext4_journal_get_write_access(handle, inode_bitmap_bh); +		if (err) +			goto fail;  		ext4_lock_group(sb, group);  		ret2 = ext4_test_and_set_bit(ino, inode_bitmap_bh->b_data);  		ext4_unlock_group(sb, group); @@ -738,6 +742,11 @@ repeat_in_this_group:  	goto out;  got: +	BUFFER_TRACE(inode_bitmap_bh, "call ext4_handle_dirty_metadata"); +	err = ext4_handle_dirty_metadata(handle, NULL, inode_bitmap_bh); +	if (err) +		goto fail; +  	/* We may have to initialize the block bitmap if it isn't already */  	if (ext4_has_group_desc_csum(sb) &&  	    gdp->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT)) { @@ -762,9 +771,7 @@ got:  			ext4_free_group_clusters_set(sb, gdp,  				ext4_free_clusters_after_init(sb, group, gdp));  			ext4_block_bitmap_csum_set(sb, group, gdp, -						   block_bitmap_bh, -						   EXT4_BLOCKS_PER_GROUP(sb) / -						   8); +						   block_bitmap_bh);  			ext4_group_desc_csum_set(sb, group, gdp);  		}  		ext4_unlock_group(sb, group); @@ -773,11 +780,6 @@ got:  			goto fail;  	} -	BUFFER_TRACE(inode_bitmap_bh, "get_write_access"); -	err = ext4_journal_get_write_access(handle, inode_bitmap_bh); -	if (err) -		goto fail; -  	BUFFER_TRACE(group_desc_bh, "get_write_access");  	err = ext4_journal_get_write_access(handle, group_desc_bh);  	if (err) @@ -825,11 +827,6 @@ got:  	}  	ext4_unlock_group(sb, group); -	BUFFER_TRACE(inode_bitmap_bh, "call ext4_handle_dirty_metadata"); -	err = ext4_handle_dirty_metadata(handle, NULL, inode_bitmap_bh); -	if (err) -		goto fail; -  	BUFFER_TRACE(group_desc_bh, "call ext4_handle_dirty_metadata");  	err = ext4_handle_dirty_metadata(handle, NULL, group_desc_bh);  	if (err) diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index f8b27bf80ac..526e5535860 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -2805,8 +2805,7 @@ ext4_mb_mark_diskspace_used(struct ext4_allocation_context *ac,  	}  	len = ext4_free_group_clusters(sb, gdp) - ac->ac_b_ex.fe_len;  	ext4_free_group_clusters_set(sb, gdp, len); -	ext4_block_bitmap_csum_set(sb, ac->ac_b_ex.fe_group, gdp, bitmap_bh, -				   EXT4_BLOCKS_PER_GROUP(sb) / 8); +	ext4_block_bitmap_csum_set(sb, ac->ac_b_ex.fe_group, gdp, bitmap_bh);  	ext4_group_desc_csum_set(sb, ac->ac_b_ex.fe_group, gdp);  	ext4_unlock_group(sb, ac->ac_b_ex.fe_group); @@ -4666,8 +4665,7 @@ do_more:  	ret = ext4_free_group_clusters(sb, gdp) + count_clusters;  	ext4_free_group_clusters_set(sb, gdp, ret); -	ext4_block_bitmap_csum_set(sb, block_group, gdp, bitmap_bh, -				   EXT4_BLOCKS_PER_GROUP(sb) / 8); +	ext4_block_bitmap_csum_set(sb, block_group, gdp, bitmap_bh);  	ext4_group_desc_csum_set(sb, block_group, gdp);  	ext4_unlock_group(sb, block_group);  	percpu_counter_add(&sbi->s_freeclusters_counter, count_clusters); @@ -4811,8 +4809,7 @@ int ext4_group_add_blocks(handle_t *handle, struct super_block *sb,  	mb_free_blocks(NULL, &e4b, bit, count);  	blk_free_count = blocks_freed + ext4_free_group_clusters(sb, desc);  	ext4_free_group_clusters_set(sb, desc, blk_free_count); -	ext4_block_bitmap_csum_set(sb, block_group, desc, bitmap_bh, -				   EXT4_BLOCKS_PER_GROUP(sb) / 8); +	ext4_block_bitmap_csum_set(sb, block_group, desc, bitmap_bh);  	ext4_group_desc_csum_set(sb, block_group, desc);  	ext4_unlock_group(sb, block_group);  	percpu_counter_add(&sbi->s_freeclusters_counter, @@ -4993,8 +4990,9 @@ int ext4_trim_fs(struct super_block *sb, struct fstrim_range *range)  	minlen = EXT4_NUM_B2C(EXT4_SB(sb),  			      range->minlen >> sb->s_blocksize_bits); -	if (unlikely(minlen > EXT4_CLUSTERS_PER_GROUP(sb)) || -	    unlikely(start >= max_blks)) +	if (minlen > EXT4_CLUSTERS_PER_GROUP(sb) || +	    start >= max_blks || +	    range->len < sb->s_blocksize)  		return -EINVAL;  	if (end >= max_blks)  		end = max_blks - 1; diff --git a/fs/ext4/resize.c b/fs/ext4/resize.c index 7a75e108696..47bf06a2765 100644 --- a/fs/ext4/resize.c +++ b/fs/ext4/resize.c @@ -1212,8 +1212,7 @@ static int ext4_set_bitmap_checksums(struct super_block *sb,  	bh = ext4_get_bitmap(sb, group_data->block_bitmap);  	if (!bh)  		return -EIO; -	ext4_block_bitmap_csum_set(sb, group, gdp, bh, -				   EXT4_BLOCKS_PER_GROUP(sb) / 8); +	ext4_block_bitmap_csum_set(sb, group, gdp, bh);  	brelse(bh);  	return 0; diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 7265a036747..80928f71685 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -143,9 +143,10 @@ int ext4_superblock_csum_verify(struct super_block *sb,  	return es->s_checksum == ext4_superblock_csum(sb, es);  } -void ext4_superblock_csum_set(struct super_block *sb, -			      struct ext4_super_block *es) +void ext4_superblock_csum_set(struct super_block *sb)  { +	struct ext4_super_block *es = EXT4_SB(sb)->s_es; +  	if (!EXT4_HAS_RO_COMPAT_FEATURE(sb,  		EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))  		return; @@ -1963,7 +1964,7 @@ static int ext4_fill_flex_info(struct super_block *sb)  		sbi->s_log_groups_per_flex = 0;  		return 1;  	} -	groups_per_flex = 1 << sbi->s_log_groups_per_flex; +	groups_per_flex = 1U << sbi->s_log_groups_per_flex;  	err = ext4_alloc_flex_bg_array(sb, sbi->s_groups_count);  	if (err) @@ -4381,7 +4382,7 @@ static int ext4_commit_super(struct super_block *sb, int sync)  		cpu_to_le32(percpu_counter_sum_positive(  				&EXT4_SB(sb)->s_freeinodes_counter));  	BUFFER_TRACE(sbh, "marking dirty"); -	ext4_superblock_csum_set(sb, es); +	ext4_superblock_csum_set(sb);  	mark_buffer_dirty(sbh);  	if (sync) {  		error = sync_dirty_buffer(sbh); diff --git a/fs/file.c b/fs/file.c index d3b5fa80b71..708d997a774 100644 --- a/fs/file.c +++ b/fs/file.c @@ -900,7 +900,7 @@ int replace_fd(unsigned fd, struct file *file, unsigned flags)  		return __close_fd(files, fd);  	if (fd >= rlimit(RLIMIT_NOFILE)) -		return -EMFILE; +		return -EBADF;  	spin_lock(&files->file_lock);  	err = expand_files(files, fd); @@ -926,7 +926,7 @@ SYSCALL_DEFINE3(dup3, unsigned int, oldfd, unsigned int, newfd, int, flags)  		return -EINVAL;  	if (newfd >= rlimit(RLIMIT_NOFILE)) -		return -EMFILE; +		return -EBADF;  	spin_lock(&files->file_lock);  	err = expand_files(files, newfd); diff --git a/fs/jfs/jfs_discard.c b/fs/jfs/jfs_discard.c index 9947563e417..dfcd5030455 100644 --- a/fs/jfs/jfs_discard.c +++ b/fs/jfs/jfs_discard.c @@ -83,7 +83,7 @@ int jfs_ioc_trim(struct inode *ip, struct fstrim_range *range)  	struct bmap *bmp = JFS_SBI(ip->i_sb)->bmap;  	struct super_block *sb = ipbmap->i_sb;  	int agno, agno_end; -	s64 start, end, minlen; +	u64 start, end, minlen;  	u64 trimmed = 0;  	/** @@ -93,15 +93,19 @@ int jfs_ioc_trim(struct inode *ip, struct fstrim_range *range)  	 * minlen:	minimum extent length in Bytes  	 */  	start = range->start >> sb->s_blocksize_bits; -	if (start < 0) -		start = 0;  	end = start + (range->len >> sb->s_blocksize_bits) - 1; -	if (end >= bmp->db_mapsize) -		end = bmp->db_mapsize - 1;  	minlen = range->minlen >> sb->s_blocksize_bits; -	if (minlen <= 0) +	if (minlen == 0)  		minlen = 1; +	if (minlen > bmp->db_agsize || +	    start >= bmp->db_mapsize || +	    range->len < sb->s_blocksize) +		return -EINVAL; + +	if (end >= bmp->db_mapsize) +		end = bmp->db_mapsize - 1; +  	/**  	 * we trim all ag's within the range  	 */ diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c index e4fb3ba5a58..3d7e09bcc0e 100644 --- a/fs/lockd/mon.c +++ b/fs/lockd/mon.c @@ -85,29 +85,38 @@ static struct rpc_clnt *nsm_create(struct net *net)  	return rpc_create(&args);  } +static struct rpc_clnt *nsm_client_set(struct lockd_net *ln, +		struct rpc_clnt *clnt) +{ +	spin_lock(&ln->nsm_clnt_lock); +	if (ln->nsm_users == 0) { +		if (clnt == NULL) +			goto out; +		ln->nsm_clnt = clnt; +	} +	clnt = ln->nsm_clnt; +	ln->nsm_users++; +out: +	spin_unlock(&ln->nsm_clnt_lock); +	return clnt; +} +  static struct rpc_clnt *nsm_client_get(struct net *net)  { -	static DEFINE_MUTEX(nsm_create_mutex); -	struct rpc_clnt	*clnt; +	struct rpc_clnt	*clnt, *new;  	struct lockd_net *ln = net_generic(net, lockd_net_id); -	spin_lock(&ln->nsm_clnt_lock); -	if (ln->nsm_users) { -		ln->nsm_users++; -		clnt = ln->nsm_clnt; -		spin_unlock(&ln->nsm_clnt_lock); +	clnt = nsm_client_set(ln, NULL); +	if (clnt != NULL)  		goto out; -	} -	spin_unlock(&ln->nsm_clnt_lock); -	mutex_lock(&nsm_create_mutex); -	clnt = nsm_create(net); -	if (!IS_ERR(clnt)) { -		ln->nsm_clnt = clnt; -		smp_wmb(); -		ln->nsm_users = 1; -	} -	mutex_unlock(&nsm_create_mutex); +	clnt = new = nsm_create(net); +	if (IS_ERR(clnt)) +		goto out; + +	clnt = nsm_client_set(ln, new); +	if (clnt != new) +		rpc_shutdown_client(new);  out:  	return clnt;  } @@ -115,18 +124,16 @@ out:  static void nsm_client_put(struct net *net)  {  	struct lockd_net *ln = net_generic(net, lockd_net_id); -	struct rpc_clnt	*clnt = ln->nsm_clnt; -	int shutdown = 0; +	struct rpc_clnt	*clnt = NULL;  	spin_lock(&ln->nsm_clnt_lock); -	if (ln->nsm_users) { -		if (--ln->nsm_users) -			ln->nsm_clnt = NULL; -		shutdown = !ln->nsm_users; +	ln->nsm_users--; +	if (ln->nsm_users == 0) { +		clnt = ln->nsm_clnt; +		ln->nsm_clnt = NULL;  	}  	spin_unlock(&ln->nsm_clnt_lock); - -	if (shutdown) +	if (clnt != NULL)  		rpc_shutdown_client(clnt);  } diff --git a/fs/namei.c b/fs/namei.c index d1895f30815..937f9d50c84 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -705,8 +705,8 @@ static inline void put_link(struct nameidata *nd, struct path *link, void *cooki  	path_put(link);  } -int sysctl_protected_symlinks __read_mostly = 1; -int sysctl_protected_hardlinks __read_mostly = 1; +int sysctl_protected_symlinks __read_mostly = 0; +int sysctl_protected_hardlinks __read_mostly = 0;  /**   * may_follow_link - Check symlink following for unsafe situations diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c index 9a521fb3986..5088b57b078 100644 --- a/fs/nfs/callback.c +++ b/fs/nfs/callback.c @@ -241,7 +241,7 @@ static int nfs_callback_start_svc(int minorversion, struct rpc_xprt *xprt,  		svc_exit_thread(cb_info->rqst);  		cb_info->rqst = NULL;  		cb_info->task = NULL; -		return PTR_ERR(cb_info->task); +		return ret;  	}  	dprintk("nfs_callback_up: service started\n");  	return 0; diff --git a/fs/nfs/dns_resolve.c b/fs/nfs/dns_resolve.c index 31c26c4dcc2..ca4b11ec87a 100644 --- a/fs/nfs/dns_resolve.c +++ b/fs/nfs/dns_resolve.c @@ -217,7 +217,7 @@ static int nfs_dns_parse(struct cache_detail *cd, char *buf, int buflen)  {  	char buf1[NFS_DNS_HOSTNAME_MAXLEN+1];  	struct nfs_dns_ent key, *item; -	unsigned long ttl; +	unsigned int ttl;  	ssize_t len;  	int ret = -EINVAL; @@ -240,7 +240,8 @@ static int nfs_dns_parse(struct cache_detail *cd, char *buf, int buflen)  	key.namelen = len;  	memset(&key.h, 0, sizeof(key.h)); -	ttl = get_expiry(&buf); +	if (get_uint(&buf, &ttl) < 0) +		goto out;  	if (ttl == 0)  		goto out;  	key.h.expiry_time = ttl + seconds_since_boot(); diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 5c7325c5c5e..6fa01aea248 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -685,7 +685,10 @@ static void __put_nfs_open_context(struct nfs_open_context *ctx, int is_sync)  	if (ctx->cred != NULL)  		put_rpccred(ctx->cred);  	dput(ctx->dentry); -	nfs_sb_deactive(sb); +	if (is_sync) +		nfs_sb_deactive(sb); +	else +		nfs_sb_deactive_async(sb);  	kfree(ctx->mdsthreshold);  	kfree(ctx);  } diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 59b133c5d65..05521cadac2 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -351,10 +351,12 @@ extern int __init register_nfs_fs(void);  extern void __exit unregister_nfs_fs(void);  extern void nfs_sb_active(struct super_block *sb);  extern void nfs_sb_deactive(struct super_block *sb); +extern void nfs_sb_deactive_async(struct super_block *sb);  /* namespace.c */ +#define NFS_PATH_CANONICAL 1  extern char *nfs_path(char **p, struct dentry *dentry, -		      char *buffer, ssize_t buflen); +		      char *buffer, ssize_t buflen, unsigned flags);  extern struct vfsmount *nfs_d_automount(struct path *path);  struct vfsmount *nfs_submount(struct nfs_server *, struct dentry *,  			      struct nfs_fh *, struct nfs_fattr *); @@ -498,7 +500,7 @@ static inline char *nfs_devname(struct dentry *dentry,  				char *buffer, ssize_t buflen)  {  	char *dummy; -	return nfs_path(&dummy, dentry, buffer, buflen); +	return nfs_path(&dummy, dentry, buffer, buflen, NFS_PATH_CANONICAL);  }  /* diff --git a/fs/nfs/mount_clnt.c b/fs/nfs/mount_clnt.c index 8e65c7f1f87..015f71f8f62 100644 --- a/fs/nfs/mount_clnt.c +++ b/fs/nfs/mount_clnt.c @@ -181,7 +181,7 @@ int nfs_mount(struct nfs_mount_request *info)  	else  		msg.rpc_proc = &mnt_clnt->cl_procinfo[MOUNTPROC_MNT]; -	status = rpc_call_sync(mnt_clnt, &msg, 0); +	status = rpc_call_sync(mnt_clnt, &msg, RPC_TASK_SOFT|RPC_TASK_TIMEOUT);  	rpc_shutdown_client(mnt_clnt);  	if (status < 0) diff --git a/fs/nfs/namespace.c b/fs/nfs/namespace.c index 655925373b9..dd057bc6b65 100644 --- a/fs/nfs/namespace.c +++ b/fs/nfs/namespace.c @@ -33,6 +33,7 @@ int nfs_mountpoint_expiry_timeout = 500 * HZ;   * @dentry - pointer to dentry   * @buffer - result buffer   * @buflen - length of buffer + * @flags - options (see below)   *   * Helper function for constructing the server pathname   * by arbitrary hashed dentry. @@ -40,8 +41,14 @@ int nfs_mountpoint_expiry_timeout = 500 * HZ;   * This is mainly for use in figuring out the path on the   * server side when automounting on top of an existing partition   * and in generating /proc/mounts and friends. + * + * Supported flags: + * NFS_PATH_CANONICAL: ensure there is exactly one slash after + *		       the original device (export) name + *		       (if unset, the original name is returned verbatim)   */ -char *nfs_path(char **p, struct dentry *dentry, char *buffer, ssize_t buflen) +char *nfs_path(char **p, struct dentry *dentry, char *buffer, ssize_t buflen, +	       unsigned flags)  {  	char *end;  	int namelen; @@ -74,7 +81,7 @@ rename_retry:  		rcu_read_unlock();  		goto rename_retry;  	} -	if (*end != '/') { +	if ((flags & NFS_PATH_CANONICAL) && *end != '/') {  		if (--buflen < 0) {  			spin_unlock(&dentry->d_lock);  			rcu_read_unlock(); @@ -91,9 +98,11 @@ rename_retry:  		return end;  	}  	namelen = strlen(base); -	/* Strip off excess slashes in base string */ -	while (namelen > 0 && base[namelen - 1] == '/') -		namelen--; +	if (flags & NFS_PATH_CANONICAL) { +		/* Strip off excess slashes in base string */ +		while (namelen > 0 && base[namelen - 1] == '/') +			namelen--; +	}  	buflen -= namelen;  	if (buflen < 0) {  		spin_unlock(&dentry->d_lock); diff --git a/fs/nfs/nfs4filelayout.c b/fs/nfs/nfs4filelayout.c index 52d84721206..2e45fd9c02a 100644 --- a/fs/nfs/nfs4filelayout.c +++ b/fs/nfs/nfs4filelayout.c @@ -122,12 +122,21 @@ static void filelayout_reset_read(struct nfs_read_data *data)  	}  } +static void filelayout_fenceme(struct inode *inode, struct pnfs_layout_hdr *lo) +{ +	if (!test_and_clear_bit(NFS_LAYOUT_RETURN, &lo->plh_flags)) +		return; +	clear_bit(NFS_INO_LAYOUTCOMMIT, &NFS_I(inode)->flags); +	pnfs_return_layout(inode); +} +  static int filelayout_async_handle_error(struct rpc_task *task,  					 struct nfs4_state *state,  					 struct nfs_client *clp,  					 struct pnfs_layout_segment *lseg)  { -	struct inode *inode = lseg->pls_layout->plh_inode; +	struct pnfs_layout_hdr *lo = lseg->pls_layout; +	struct inode *inode = lo->plh_inode;  	struct nfs_server *mds_server = NFS_SERVER(inode);  	struct nfs4_deviceid_node *devid = FILELAYOUT_DEVID_NODE(lseg);  	struct nfs_client *mds_client = mds_server->nfs_client; @@ -204,10 +213,8 @@ static int filelayout_async_handle_error(struct rpc_task *task,  		dprintk("%s DS connection error %d\n", __func__,  			task->tk_status);  		nfs4_mark_deviceid_unavailable(devid); -		clear_bit(NFS_INO_LAYOUTCOMMIT, &NFS_I(inode)->flags); -		_pnfs_return_layout(inode); +		set_bit(NFS_LAYOUT_RETURN, &lo->plh_flags);  		rpc_wake_up(&tbl->slot_tbl_waitq); -		nfs4_ds_disconnect(clp);  		/* fall through */  	default:  reset: @@ -331,7 +338,9 @@ static void filelayout_read_count_stats(struct rpc_task *task, void *data)  static void filelayout_read_release(void *data)  {  	struct nfs_read_data *rdata = data; +	struct pnfs_layout_hdr *lo = rdata->header->lseg->pls_layout; +	filelayout_fenceme(lo->plh_inode, lo);  	nfs_put_client(rdata->ds_clp);  	rdata->header->mds_ops->rpc_release(data);  } @@ -429,7 +438,9 @@ static void filelayout_write_count_stats(struct rpc_task *task, void *data)  static void filelayout_write_release(void *data)  {  	struct nfs_write_data *wdata = data; +	struct pnfs_layout_hdr *lo = wdata->header->lseg->pls_layout; +	filelayout_fenceme(lo->plh_inode, lo);  	nfs_put_client(wdata->ds_clp);  	wdata->header->mds_ops->rpc_release(data);  } @@ -739,7 +750,7 @@ filelayout_decode_layout(struct pnfs_layout_hdr *flo,  		goto out_err;  	if (fl->num_fh > 0) { -		fl->fh_array = kzalloc(fl->num_fh * sizeof(struct nfs_fh *), +		fl->fh_array = kcalloc(fl->num_fh, sizeof(fl->fh_array[0]),  				       gfp_flags);  		if (!fl->fh_array)  			goto out_err; diff --git a/fs/nfs/nfs4filelayout.h b/fs/nfs/nfs4filelayout.h index dca47d78671..8c07241fe52 100644 --- a/fs/nfs/nfs4filelayout.h +++ b/fs/nfs/nfs4filelayout.h @@ -149,6 +149,5 @@ extern void nfs4_fl_put_deviceid(struct nfs4_file_layout_dsaddr *dsaddr);  extern void nfs4_fl_free_deviceid(struct nfs4_file_layout_dsaddr *dsaddr);  struct nfs4_file_layout_dsaddr *  filelayout_get_device_info(struct inode *inode, struct nfs4_deviceid *dev_id, gfp_t gfp_flags); -void nfs4_ds_disconnect(struct nfs_client *clp);  #endif /* FS_NFS_NFS4FILELAYOUT_H */ diff --git a/fs/nfs/nfs4filelayoutdev.c b/fs/nfs/nfs4filelayoutdev.c index 3336d5eaf87..a8eaa9b7bb0 100644 --- a/fs/nfs/nfs4filelayoutdev.c +++ b/fs/nfs/nfs4filelayoutdev.c @@ -149,28 +149,6 @@ _data_server_lookup_locked(const struct list_head *dsaddrs)  }  /* - * Lookup DS by nfs_client pointer. Zero data server client pointer - */ -void nfs4_ds_disconnect(struct nfs_client *clp) -{ -	struct nfs4_pnfs_ds *ds; -	struct nfs_client *found = NULL; - -	dprintk("%s clp %p\n", __func__, clp); -	spin_lock(&nfs4_ds_cache_lock); -	list_for_each_entry(ds, &nfs4_data_server_cache, ds_node) -		if (ds->ds_clp && ds->ds_clp == clp) { -			found = ds->ds_clp; -			ds->ds_clp = NULL; -		} -	spin_unlock(&nfs4_ds_cache_lock); -	if (found) { -		set_bit(NFS_CS_STOP_RENEW, &clp->cl_res_state); -		nfs_put_client(clp); -	} -} - -/*   * Create an rpc connection to the nfs4_pnfs_ds data server   * Currently only supports IPv4 and IPv6 addresses   */ diff --git a/fs/nfs/nfs4getroot.c b/fs/nfs/nfs4getroot.c index 6a83780e0ce..549462e5b9b 100644 --- a/fs/nfs/nfs4getroot.c +++ b/fs/nfs/nfs4getroot.c @@ -5,6 +5,7 @@  #include <linux/nfs_fs.h>  #include "nfs4_fs.h" +#include "internal.h"  #define NFSDBG_FACILITY		NFSDBG_CLIENT diff --git a/fs/nfs/nfs4namespace.c b/fs/nfs/nfs4namespace.c index 79fbb61ce20..1e09eb78543 100644 --- a/fs/nfs/nfs4namespace.c +++ b/fs/nfs/nfs4namespace.c @@ -81,7 +81,8 @@ static char *nfs_path_component(const char *nfspath, const char *end)  static char *nfs4_path(struct dentry *dentry, char *buffer, ssize_t buflen)  {  	char *limit; -	char *path = nfs_path(&limit, dentry, buffer, buflen); +	char *path = nfs_path(&limit, dentry, buffer, buflen, +			      NFS_PATH_CANONICAL);  	if (!IS_ERR(path)) {  		char *path_component = nfs_path_component(path, limit);  		if (path_component) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 68b21d81b7a..5eec4429970 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -339,8 +339,7 @@ static int nfs4_handle_exception(struct nfs_server *server, int errorcode, struc  			dprintk("%s ERROR: %d Reset session\n", __func__,  				errorcode);  			nfs4_schedule_session_recovery(clp->cl_session, errorcode); -			exception->retry = 1; -			break; +			goto wait_on_recovery;  #endif /* defined(CONFIG_NFS_V4_1) */  		case -NFS4ERR_FILE_OPEN:  			if (exception->timeout > HZ) { @@ -1572,9 +1571,11 @@ static void nfs4_open_prepare(struct rpc_task *task, void *calldata)  	data->timestamp = jiffies;  	if (nfs4_setup_sequence(data->o_arg.server,  				&data->o_arg.seq_args, -				&data->o_res.seq_res, task)) -		return; -	rpc_call_start(task); +				&data->o_res.seq_res, +				task) != 0) +		nfs_release_seqid(data->o_arg.seqid); +	else +		rpc_call_start(task);  	return;  unlock_no_action:  	rcu_read_unlock(); @@ -1748,7 +1749,7 @@ static int nfs4_opendata_access(struct rpc_cred *cred,  	/* even though OPEN succeeded, access is denied. Close the file */  	nfs4_close_state(state, fmode); -	return -NFS4ERR_ACCESS; +	return -EACCES;  }  /* @@ -2196,7 +2197,7 @@ static void nfs4_free_closedata(void *data)  	nfs4_put_open_state(calldata->state);  	nfs_free_seqid(calldata->arg.seqid);  	nfs4_put_state_owner(sp); -	nfs_sb_deactive(sb); +	nfs_sb_deactive_async(sb);  	kfree(calldata);  } @@ -2296,9 +2297,10 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data)  	if (nfs4_setup_sequence(NFS_SERVER(inode),  				&calldata->arg.seq_args,  				&calldata->res.seq_res, -				task)) -		goto out; -	rpc_call_start(task); +				task) != 0) +		nfs_release_seqid(calldata->arg.seqid); +	else +		rpc_call_start(task);  out:  	dprintk("%s: done!\n", __func__);  } @@ -4529,6 +4531,7 @@ static void nfs4_locku_done(struct rpc_task *task, void *data)  			if (nfs4_async_handle_error(task, calldata->server, NULL) == -EAGAIN)  				rpc_restart_call_prepare(task);  	} +	nfs_release_seqid(calldata->arg.seqid);  }  static void nfs4_locku_prepare(struct rpc_task *task, void *data) @@ -4545,9 +4548,11 @@ static void nfs4_locku_prepare(struct rpc_task *task, void *data)  	calldata->timestamp = jiffies;  	if (nfs4_setup_sequence(calldata->server,  				&calldata->arg.seq_args, -				&calldata->res.seq_res, task)) -		return; -	rpc_call_start(task); +				&calldata->res.seq_res, +				task) != 0) +		nfs_release_seqid(calldata->arg.seqid); +	else +		rpc_call_start(task);  }  static const struct rpc_call_ops nfs4_locku_ops = { @@ -4692,7 +4697,7 @@ static void nfs4_lock_prepare(struct rpc_task *task, void *calldata)  	/* Do we need to do an open_to_lock_owner? */  	if (!(data->arg.lock_seqid->sequence->flags & NFS_SEQID_CONFIRMED)) {  		if (nfs_wait_on_sequence(data->arg.open_seqid, task) != 0) -			return; +			goto out_release_lock_seqid;  		data->arg.open_stateid = &state->stateid;  		data->arg.new_lock_owner = 1;  		data->res.open_seqid = data->arg.open_seqid; @@ -4701,10 +4706,15 @@ static void nfs4_lock_prepare(struct rpc_task *task, void *calldata)  	data->timestamp = jiffies;  	if (nfs4_setup_sequence(data->server,  				&data->arg.seq_args, -				&data->res.seq_res, task)) +				&data->res.seq_res, +				task) == 0) { +		rpc_call_start(task);  		return; -	rpc_call_start(task); -	dprintk("%s: done!, ret = %d\n", __func__, data->rpc_status); +	} +	nfs_release_seqid(data->arg.open_seqid); +out_release_lock_seqid: +	nfs_release_seqid(data->arg.lock_seqid); +	dprintk("%s: done!, ret = %d\n", __func__, task->tk_status);  }  static void nfs4_recover_lock_prepare(struct rpc_task *task, void *calldata) @@ -5667,7 +5677,7 @@ static void nfs4_add_and_init_slots(struct nfs4_slot_table *tbl,  		tbl->slots = new;  		tbl->max_slots = max_slots;  	} -	tbl->highest_used_slotid = -1;	/* no slot is currently used */ +	tbl->highest_used_slotid = NFS4_NO_SLOT;  	for (i = 0; i < tbl->max_slots; i++)  		tbl->slots[i].seq_nr = ivalue;  	spin_unlock(&tbl->slot_tbl_lock); diff --git a/fs/nfs/objlayout/objio_osd.c b/fs/nfs/objlayout/objio_osd.c index be731e6b7b9..c6f990656f8 100644 --- a/fs/nfs/objlayout/objio_osd.c +++ b/fs/nfs/objlayout/objio_osd.c @@ -369,7 +369,7 @@ void objio_free_result(struct objlayout_io_res *oir)  	kfree(objios);  } -enum pnfs_osd_errno osd_pri_2_pnfs_err(enum osd_err_priority oep) +static enum pnfs_osd_errno osd_pri_2_pnfs_err(enum osd_err_priority oep)  {  	switch (oep) {  	case OSD_ERR_PRI_NO_ERROR: @@ -574,7 +574,7 @@ static bool objio_pg_test(struct nfs_pageio_descriptor *pgio,  			(unsigned long)pgio->pg_layout_private;  } -void objio_init_read(struct nfs_pageio_descriptor *pgio, struct nfs_page *req) +static void objio_init_read(struct nfs_pageio_descriptor *pgio, struct nfs_page *req)  {  	pnfs_generic_pg_init_read(pgio, req);  	if (unlikely(pgio->pg_lseg == NULL)) @@ -604,7 +604,7 @@ static bool aligned_on_raid_stripe(u64 offset, struct ore_layout *layout,  	return false;  } -void objio_init_write(struct nfs_pageio_descriptor *pgio, struct nfs_page *req) +static void objio_init_write(struct nfs_pageio_descriptor *pgio, struct nfs_page *req)  {  	unsigned long stripe_end = 0;  	u64 wb_size; diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index fe624c91bd0..2878f97bd78 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c @@ -925,8 +925,8 @@ pnfs_find_alloc_layout(struct inode *ino,  	if (likely(nfsi->layout == NULL)) {	/* Won the race? */  		nfsi->layout = new;  		return new; -	} -	pnfs_free_layout_hdr(new); +	} else if (new != NULL) +		pnfs_free_layout_hdr(new);  out_existing:  	pnfs_get_layout_hdr(nfsi->layout);  	return nfsi->layout; diff --git a/fs/nfs/pnfs.h b/fs/nfs/pnfs.h index 2d722dba111..dbf7bba52da 100644 --- a/fs/nfs/pnfs.h +++ b/fs/nfs/pnfs.h @@ -62,6 +62,7 @@ enum {  	NFS_LAYOUT_RW_FAILED,		/* get rw layout failed stop trying */  	NFS_LAYOUT_BULK_RECALL,		/* bulk recall affecting layout */  	NFS_LAYOUT_ROC,			/* some lseg had roc bit set */ +	NFS_LAYOUT_RETURN,		/* Return this layout ASAP */  };  enum layoutdriver_policy_flags { diff --git a/fs/nfs/super.c b/fs/nfs/super.c index e831bce4976..652d3f7176a 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -54,6 +54,7 @@  #include <linux/parser.h>  #include <linux/nsproxy.h>  #include <linux/rcupdate.h> +#include <linux/kthread.h>  #include <asm/uaccess.h> @@ -415,6 +416,54 @@ void nfs_sb_deactive(struct super_block *sb)  }  EXPORT_SYMBOL_GPL(nfs_sb_deactive); +static int nfs_deactivate_super_async_work(void *ptr) +{ +	struct super_block *sb = ptr; + +	deactivate_super(sb); +	module_put_and_exit(0); +	return 0; +} + +/* + * same effect as deactivate_super, but will do final unmount in kthread + * context + */ +static void nfs_deactivate_super_async(struct super_block *sb) +{ +	struct task_struct *task; +	char buf[INET6_ADDRSTRLEN + 1]; +	struct nfs_server *server = NFS_SB(sb); +	struct nfs_client *clp = server->nfs_client; + +	if (!atomic_add_unless(&sb->s_active, -1, 1)) { +		rcu_read_lock(); +		snprintf(buf, sizeof(buf), +			rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_ADDR)); +		rcu_read_unlock(); + +		__module_get(THIS_MODULE); +		task = kthread_run(nfs_deactivate_super_async_work, sb, +				"%s-deactivate-super", buf); +		if (IS_ERR(task)) { +			pr_err("%s: kthread_run: %ld\n", +				__func__, PTR_ERR(task)); +			/* make synchronous call and hope for the best */ +			deactivate_super(sb); +			module_put(THIS_MODULE); +		} +	} +} + +void nfs_sb_deactive_async(struct super_block *sb) +{ +	struct nfs_server *server = NFS_SB(sb); + +	if (atomic_dec_and_test(&server->active)) +		nfs_deactivate_super_async(sb); +} +EXPORT_SYMBOL_GPL(nfs_sb_deactive_async); +  /*   * Deliver file system statistics to userspace   */ @@ -771,7 +820,7 @@ int nfs_show_devname(struct seq_file *m, struct dentry *root)  	int err = 0;  	if (!page)  		return -ENOMEM; -	devname = nfs_path(&dummy, root, page, PAGE_SIZE); +	devname = nfs_path(&dummy, root, page, PAGE_SIZE, 0);  	if (IS_ERR(devname))  		err = PTR_ERR(devname);  	else diff --git a/fs/nfs/unlink.c b/fs/nfs/unlink.c index 13cea637eff..3f79c77153b 100644 --- a/fs/nfs/unlink.c +++ b/fs/nfs/unlink.c @@ -95,7 +95,7 @@ static void nfs_async_unlink_release(void *calldata)  	nfs_dec_sillycount(data->dir);  	nfs_free_unlinkdata(data); -	nfs_sb_deactive(sb); +	nfs_sb_deactive_async(sb);  }  static void nfs_unlink_prepare(struct rpc_task *task, void *calldata) diff --git a/fs/proc/stat.c b/fs/proc/stat.c index 64c3b317236..e296572c73e 100644 --- a/fs/proc/stat.c +++ b/fs/proc/stat.c @@ -45,10 +45,13 @@ static cputime64_t get_iowait_time(int cpu)  static u64 get_idle_time(int cpu)  { -	u64 idle, idle_time = get_cpu_idle_time_us(cpu, NULL); +	u64 idle, idle_time = -1ULL; + +	if (cpu_online(cpu)) +		idle_time = get_cpu_idle_time_us(cpu, NULL);  	if (idle_time == -1ULL) -		/* !NO_HZ so we can rely on cpustat.idle */ +		/* !NO_HZ or cpu offline so we can rely on cpustat.idle */  		idle = kcpustat_cpu(cpu).cpustat[CPUTIME_IDLE];  	else  		idle = usecs_to_cputime64(idle_time); @@ -58,10 +61,13 @@ static u64 get_idle_time(int cpu)  static u64 get_iowait_time(int cpu)  { -	u64 iowait, iowait_time = get_cpu_iowait_time_us(cpu, NULL); +	u64 iowait, iowait_time = -1ULL; + +	if (cpu_online(cpu)) +		iowait_time = get_cpu_iowait_time_us(cpu, NULL);  	if (iowait_time == -1ULL) -		/* !NO_HZ so we can rely on cpustat.iowait */ +		/* !NO_HZ or cpu offline so we can rely on cpustat.iowait */  		iowait = kcpustat_cpu(cpu).cpustat[CPUTIME_IOWAIT];  	else  		iowait = usecs_to_cputime64(iowait_time); diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 6b0bb00d4d2..2fbdff6be25 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -485,20 +485,18 @@ int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)  /**   *	sysfs_pathname - return full path to sysfs dirent   *	@sd: sysfs_dirent whose path we want - *	@path: caller allocated buffer + *	@path: caller allocated buffer of size PATH_MAX   *   *	Gives the name "/" to the sysfs_root entry; any path returned   *	is relative to wherever sysfs is mounted. - * - *	XXX: does no error checking on @path size   */  static char *sysfs_pathname(struct sysfs_dirent *sd, char *path)  {  	if (sd->s_parent) {  		sysfs_pathname(sd->s_parent, path); -		strcat(path, "/"); +		strlcat(path, "/", PATH_MAX);  	} -	strcat(path, sd->s_name); +	strlcat(path, sd->s_name, PATH_MAX);  	return path;  } @@ -531,9 +529,11 @@ int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)  		char *path = kzalloc(PATH_MAX, GFP_KERNEL);  		WARN(1, KERN_WARNING  		     "sysfs: cannot create duplicate filename '%s'\n", -		     (path == NULL) ? sd->s_name : -		     strcat(strcat(sysfs_pathname(acxt->parent_sd, path), "/"), -		            sd->s_name)); +		     (path == NULL) ? sd->s_name +				    : (sysfs_pathname(acxt->parent_sd, path), +				       strlcat(path, "/", PATH_MAX), +				       strlcat(path, sd->s_name, PATH_MAX), +				       path));  		kfree(path);  	}  |