diff options
Diffstat (limited to 'fs')
| -rw-r--r-- | fs/ext3/balloc.c | 5 | ||||
| -rw-r--r-- | fs/file.c | 1 | ||||
| -rw-r--r-- | fs/jffs2/file.c | 39 | ||||
| -rw-r--r-- | fs/notify/fanotify/fanotify_user.c | 3 | ||||
| -rw-r--r-- | fs/proc/base.c | 109 | ||||
| -rw-r--r-- | fs/pstore/platform.c | 3 | ||||
| -rw-r--r-- | fs/reiserfs/inode.c | 10 | ||||
| -rw-r--r-- | fs/reiserfs/stree.c | 4 | ||||
| -rw-r--r-- | fs/reiserfs/super.c | 60 | ||||
| -rw-r--r-- | fs/ubifs/find.c | 12 | ||||
| -rw-r--r-- | fs/ubifs/lprops.c | 6 | ||||
| -rw-r--r-- | fs/ubifs/ubifs.h | 3 | ||||
| -rw-r--r-- | fs/xfs/xfs_aops.c | 54 | ||||
| -rw-r--r-- | fs/xfs/xfs_attr_leaf.c | 20 | ||||
| -rw-r--r-- | fs/xfs/xfs_buf.c | 14 | 
15 files changed, 282 insertions, 61 deletions
diff --git a/fs/ext3/balloc.c b/fs/ext3/balloc.c index 7320a66e958..22548f56197 100644 --- a/fs/ext3/balloc.c +++ b/fs/ext3/balloc.c @@ -2101,8 +2101,9 @@ int ext3_trim_fs(struct super_block *sb, struct fstrim_range *range)  	end = start + (range->len >> sb->s_blocksize_bits) - 1;  	minlen = range->minlen >> sb->s_blocksize_bits; -	if (unlikely(minlen > EXT3_BLOCKS_PER_GROUP(sb)) || -	    unlikely(start >= max_blks)) +	if (minlen > EXT3_BLOCKS_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/file.c b/fs/file.c index 708d997a774..7cb71b99260 100644 --- a/fs/file.c +++ b/fs/file.c @@ -685,7 +685,6 @@ void do_close_on_exec(struct files_struct *files)  	struct fdtable *fdt;  	/* exec unshares first */ -	BUG_ON(atomic_read(&files->count) != 1);  	spin_lock(&files->file_lock);  	for (i = 0; ; i++) {  		unsigned long set; diff --git a/fs/jffs2/file.c b/fs/jffs2/file.c index 60ef3fb707f..1506673c087 100644 --- a/fs/jffs2/file.c +++ b/fs/jffs2/file.c @@ -138,33 +138,39 @@ static int jffs2_write_begin(struct file *filp, struct address_space *mapping,  	struct page *pg;  	struct inode *inode = mapping->host;  	struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); +	struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); +	struct jffs2_raw_inode ri; +	uint32_t alloc_len = 0;  	pgoff_t index = pos >> PAGE_CACHE_SHIFT;  	uint32_t pageofs = index << PAGE_CACHE_SHIFT;  	int ret = 0; +	jffs2_dbg(1, "%s()\n", __func__); + +	if (pageofs > inode->i_size) { +		ret = jffs2_reserve_space(c, sizeof(ri), &alloc_len, +					  ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE); +		if (ret) +			return ret; +	} + +	mutex_lock(&f->sem);  	pg = grab_cache_page_write_begin(mapping, index, flags); -	if (!pg) +	if (!pg) { +		if (alloc_len) +			jffs2_complete_reservation(c); +		mutex_unlock(&f->sem);  		return -ENOMEM; +	}  	*pagep = pg; -	jffs2_dbg(1, "%s()\n", __func__); - -	if (pageofs > inode->i_size) { +	if (alloc_len) {  		/* Make new hole frag from old EOF to new page */ -		struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); -		struct jffs2_raw_inode ri;  		struct jffs2_full_dnode *fn; -		uint32_t alloc_len;  		jffs2_dbg(1, "Writing new hole frag 0x%x-0x%x between current EOF and new page\n",  			  (unsigned int)inode->i_size, pageofs); -		ret = jffs2_reserve_space(c, sizeof(ri), &alloc_len, -					  ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE); -		if (ret) -			goto out_page; - -		mutex_lock(&f->sem);  		memset(&ri, 0, sizeof(ri));  		ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); @@ -191,7 +197,6 @@ static int jffs2_write_begin(struct file *filp, struct address_space *mapping,  		if (IS_ERR(fn)) {  			ret = PTR_ERR(fn);  			jffs2_complete_reservation(c); -			mutex_unlock(&f->sem);  			goto out_page;  		}  		ret = jffs2_add_full_dnode_to_inode(c, f, fn); @@ -206,12 +211,10 @@ static int jffs2_write_begin(struct file *filp, struct address_space *mapping,  			jffs2_mark_node_obsolete(c, fn->raw);  			jffs2_free_full_dnode(fn);  			jffs2_complete_reservation(c); -			mutex_unlock(&f->sem);  			goto out_page;  		}  		jffs2_complete_reservation(c);  		inode->i_size = pageofs; -		mutex_unlock(&f->sem);  	}  	/* @@ -220,18 +223,18 @@ static int jffs2_write_begin(struct file *filp, struct address_space *mapping,  	 * case of a short-copy.  	 */  	if (!PageUptodate(pg)) { -		mutex_lock(&f->sem);  		ret = jffs2_do_readpage_nolock(inode, pg); -		mutex_unlock(&f->sem);  		if (ret)  			goto out_page;  	} +	mutex_unlock(&f->sem);  	jffs2_dbg(1, "end write_begin(). pg->flags %lx\n", pg->flags);  	return ret;  out_page:  	unlock_page(pg);  	page_cache_release(pg); +	mutex_unlock(&f->sem);  	return ret;  } diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 721d692fa8d..6fcaeb8c902 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -258,7 +258,8 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,  	if (ret)  		goto out_close_fd; -	fd_install(fd, f); +	if (fd != FAN_NOFD) +		fd_install(fd, f);  	return fanotify_event_metadata.event_len;  out_close_fd: diff --git a/fs/proc/base.c b/fs/proc/base.c index 144a96732dd..3c231adf845 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -873,6 +873,113 @@ static const struct file_operations proc_environ_operations = {  	.release	= mem_release,  }; +static ssize_t oom_adj_read(struct file *file, char __user *buf, size_t count, +			    loff_t *ppos) +{ +	struct task_struct *task = get_proc_task(file->f_path.dentry->d_inode); +	char buffer[PROC_NUMBUF]; +	int oom_adj = OOM_ADJUST_MIN; +	size_t len; +	unsigned long flags; + +	if (!task) +		return -ESRCH; +	if (lock_task_sighand(task, &flags)) { +		if (task->signal->oom_score_adj == OOM_SCORE_ADJ_MAX) +			oom_adj = OOM_ADJUST_MAX; +		else +			oom_adj = (task->signal->oom_score_adj * -OOM_DISABLE) / +				  OOM_SCORE_ADJ_MAX; +		unlock_task_sighand(task, &flags); +	} +	put_task_struct(task); +	len = snprintf(buffer, sizeof(buffer), "%d\n", oom_adj); +	return simple_read_from_buffer(buf, count, ppos, buffer, len); +} + +static ssize_t oom_adj_write(struct file *file, const char __user *buf, +			     size_t count, loff_t *ppos) +{ +	struct task_struct *task; +	char buffer[PROC_NUMBUF]; +	int oom_adj; +	unsigned long flags; +	int err; + +	memset(buffer, 0, sizeof(buffer)); +	if (count > sizeof(buffer) - 1) +		count = sizeof(buffer) - 1; +	if (copy_from_user(buffer, buf, count)) { +		err = -EFAULT; +		goto out; +	} + +	err = kstrtoint(strstrip(buffer), 0, &oom_adj); +	if (err) +		goto out; +	if ((oom_adj < OOM_ADJUST_MIN || oom_adj > OOM_ADJUST_MAX) && +	     oom_adj != OOM_DISABLE) { +		err = -EINVAL; +		goto out; +	} + +	task = get_proc_task(file->f_path.dentry->d_inode); +	if (!task) { +		err = -ESRCH; +		goto out; +	} + +	task_lock(task); +	if (!task->mm) { +		err = -EINVAL; +		goto err_task_lock; +	} + +	if (!lock_task_sighand(task, &flags)) { +		err = -ESRCH; +		goto err_task_lock; +	} + +	/* +	 * Scale /proc/pid/oom_score_adj appropriately ensuring that a maximum +	 * value is always attainable. +	 */ +	if (oom_adj == OOM_ADJUST_MAX) +		oom_adj = OOM_SCORE_ADJ_MAX; +	else +		oom_adj = (oom_adj * OOM_SCORE_ADJ_MAX) / -OOM_DISABLE; + +	if (oom_adj < task->signal->oom_score_adj && +	    !capable(CAP_SYS_RESOURCE)) { +		err = -EACCES; +		goto err_sighand; +	} + +	/* +	 * /proc/pid/oom_adj is provided for legacy purposes, ask users to use +	 * /proc/pid/oom_score_adj instead. +	 */ +	printk_once(KERN_WARNING "%s (%d): /proc/%d/oom_adj is deprecated, please use /proc/%d/oom_score_adj instead.\n", +		  current->comm, task_pid_nr(current), task_pid_nr(task), +		  task_pid_nr(task)); + +	task->signal->oom_score_adj = oom_adj; +	trace_oom_score_adj_update(task); +err_sighand: +	unlock_task_sighand(task, &flags); +err_task_lock: +	task_unlock(task); +	put_task_struct(task); +out: +	return err < 0 ? err : count; +} + +static const struct file_operations proc_oom_adj_operations = { +	.read		= oom_adj_read, +	.write		= oom_adj_write, +	.llseek		= generic_file_llseek, +}; +  static ssize_t oom_score_adj_read(struct file *file, char __user *buf,  					size_t count, loff_t *ppos)  { @@ -2598,6 +2705,7 @@ static const struct pid_entry tgid_base_stuff[] = {  	REG("cgroup",  S_IRUGO, proc_cgroup_operations),  #endif  	INF("oom_score",  S_IRUGO, proc_oom_score), +	REG("oom_adj",    S_IRUGO|S_IWUSR, proc_oom_adj_operations),  	REG("oom_score_adj", S_IRUGO|S_IWUSR, proc_oom_score_adj_operations),  #ifdef CONFIG_AUDITSYSCALL  	REG("loginuid",   S_IWUSR|S_IRUGO, proc_loginuid_operations), @@ -2964,6 +3072,7 @@ static const struct pid_entry tid_base_stuff[] = {  	REG("cgroup",  S_IRUGO, proc_cgroup_operations),  #endif  	INF("oom_score", S_IRUGO, proc_oom_score), +	REG("oom_adj",   S_IRUGO|S_IWUSR, proc_oom_adj_operations),  	REG("oom_score_adj", S_IRUGO|S_IWUSR, proc_oom_score_adj_operations),  #ifdef CONFIG_AUDITSYSCALL  	REG("loginuid",  S_IWUSR|S_IRUGO, proc_loginuid_operations), diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c index a40da07e93d..947fbe06c3b 100644 --- a/fs/pstore/platform.c +++ b/fs/pstore/platform.c @@ -161,6 +161,7 @@ static void pstore_console_write(struct console *con, const char *s, unsigned c)  	while (s < e) {  		unsigned long flags; +		u64 id;  		if (c > psinfo->bufsize)  			c = psinfo->bufsize; @@ -172,7 +173,7 @@ static void pstore_console_write(struct console *con, const char *s, unsigned c)  			spin_lock_irqsave(&psinfo->buf_lock, flags);  		}  		memcpy(psinfo->buf, s, c); -		psinfo->write(PSTORE_TYPE_CONSOLE, 0, NULL, 0, c, psinfo); +		psinfo->write(PSTORE_TYPE_CONSOLE, 0, &id, 0, c, psinfo);  		spin_unlock_irqrestore(&psinfo->buf_lock, flags);  		s += c;  		c = e - s; diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c index f27f01a98aa..d83736fbc26 100644 --- a/fs/reiserfs/inode.c +++ b/fs/reiserfs/inode.c @@ -1782,8 +1782,9 @@ int reiserfs_new_inode(struct reiserfs_transaction_handle *th,  	BUG_ON(!th->t_trans_id); -	dquot_initialize(inode); +	reiserfs_write_unlock(inode->i_sb);  	err = dquot_alloc_inode(inode); +	reiserfs_write_lock(inode->i_sb);  	if (err)  		goto out_end_trans;  	if (!dir->i_nlink) { @@ -1979,8 +1980,10 @@ int reiserfs_new_inode(struct reiserfs_transaction_handle *th,        out_end_trans:  	journal_end(th, th->t_super, th->t_blocks_allocated); +	reiserfs_write_unlock(inode->i_sb);  	/* Drop can be outside and it needs more credits so it's better to have it outside */  	dquot_drop(inode); +	reiserfs_write_lock(inode->i_sb);  	inode->i_flags |= S_NOQUOTA;  	make_bad_inode(inode); @@ -3103,10 +3106,9 @@ int reiserfs_setattr(struct dentry *dentry, struct iattr *attr)  	/* must be turned off for recursive notify_change calls */  	ia_valid = attr->ia_valid &= ~(ATTR_KILL_SUID|ATTR_KILL_SGID); -	depth = reiserfs_write_lock_once(inode->i_sb);  	if (is_quota_modification(inode, attr))  		dquot_initialize(inode); - +	depth = reiserfs_write_lock_once(inode->i_sb);  	if (attr->ia_valid & ATTR_SIZE) {  		/* version 2 items will be caught by the s_maxbytes check  		 ** done for us in vmtruncate @@ -3170,7 +3172,9 @@ int reiserfs_setattr(struct dentry *dentry, struct iattr *attr)  		error = journal_begin(&th, inode->i_sb, jbegin_count);  		if (error)  			goto out; +		reiserfs_write_unlock_once(inode->i_sb, depth);  		error = dquot_transfer(inode, attr); +		depth = reiserfs_write_lock_once(inode->i_sb);  		if (error) {  			journal_end(&th, inode->i_sb, jbegin_count);  			goto out; diff --git a/fs/reiserfs/stree.c b/fs/reiserfs/stree.c index f8afa4b162b..2f40a4c70a4 100644 --- a/fs/reiserfs/stree.c +++ b/fs/reiserfs/stree.c @@ -1968,7 +1968,9 @@ int reiserfs_paste_into_item(struct reiserfs_transaction_handle *th, struct tree  		       key2type(&(key->on_disk_key)));  #endif +	reiserfs_write_unlock(inode->i_sb);  	retval = dquot_alloc_space_nodirty(inode, pasted_size); +	reiserfs_write_lock(inode->i_sb);  	if (retval) {  		pathrelse(search_path);  		return retval; @@ -2061,9 +2063,11 @@ int reiserfs_insert_item(struct reiserfs_transaction_handle *th,  			       "reiserquota insert_item(): allocating %u id=%u type=%c",  			       quota_bytes, inode->i_uid, head2type(ih));  #endif +		reiserfs_write_unlock(inode->i_sb);  		/* We can't dirty inode here. It would be immediately written but  		 * appropriate stat item isn't inserted yet... */  		retval = dquot_alloc_space_nodirty(inode, quota_bytes); +		reiserfs_write_lock(inode->i_sb);  		if (retval) {  			pathrelse(path);  			return retval; diff --git a/fs/reiserfs/super.c b/fs/reiserfs/super.c index 1078ae17999..418bdc3a57d 100644 --- a/fs/reiserfs/super.c +++ b/fs/reiserfs/super.c @@ -298,7 +298,9 @@ static int finish_unfinished(struct super_block *s)  			retval = remove_save_link_only(s, &save_link_key, 0);  			continue;  		} +		reiserfs_write_unlock(s);  		dquot_initialize(inode); +		reiserfs_write_lock(s);  		if (truncate && S_ISDIR(inode->i_mode)) {  			/* We got a truncate request for a dir which is impossible. @@ -1335,7 +1337,7 @@ static int reiserfs_remount(struct super_block *s, int *mount_flags, char *arg)  				kfree(qf_names[i]);  #endif  		err = -EINVAL; -		goto out_err; +		goto out_unlock;  	}  #ifdef CONFIG_QUOTA  	handle_quota_files(s, qf_names, &qfmt); @@ -1379,7 +1381,7 @@ static int reiserfs_remount(struct super_block *s, int *mount_flags, char *arg)  	if (blocks) {  		err = reiserfs_resize(s, blocks);  		if (err != 0) -			goto out_err; +			goto out_unlock;  	}  	if (*mount_flags & MS_RDONLY) { @@ -1389,9 +1391,15 @@ static int reiserfs_remount(struct super_block *s, int *mount_flags, char *arg)  			/* it is read-only already */  			goto out_ok; +		/* +		 * Drop write lock. Quota will retake it when needed and lock +		 * ordering requires calling dquot_suspend() without it. +		 */ +		reiserfs_write_unlock(s);  		err = dquot_suspend(s, -1);  		if (err < 0)  			goto out_err; +		reiserfs_write_lock(s);  		/* try to remount file system with read-only permissions */  		if (sb_umount_state(rs) == REISERFS_VALID_FS @@ -1401,7 +1409,7 @@ static int reiserfs_remount(struct super_block *s, int *mount_flags, char *arg)  		err = journal_begin(&th, s, 10);  		if (err) -			goto out_err; +			goto out_unlock;  		/* Mounting a rw partition read-only. */  		reiserfs_prepare_for_journal(s, SB_BUFFER_WITH_SB(s), 1); @@ -1416,7 +1424,7 @@ static int reiserfs_remount(struct super_block *s, int *mount_flags, char *arg)  		if (reiserfs_is_journal_aborted(journal)) {  			err = journal->j_errno; -			goto out_err; +			goto out_unlock;  		}  		handle_data_mode(s, mount_options); @@ -1425,7 +1433,7 @@ static int reiserfs_remount(struct super_block *s, int *mount_flags, char *arg)  		s->s_flags &= ~MS_RDONLY;	/* now it is safe to call journal_begin */  		err = journal_begin(&th, s, 10);  		if (err) -			goto out_err; +			goto out_unlock;  		/* Mount a partition which is read-only, read-write */  		reiserfs_prepare_for_journal(s, SB_BUFFER_WITH_SB(s), 1); @@ -1442,10 +1450,16 @@ static int reiserfs_remount(struct super_block *s, int *mount_flags, char *arg)  	SB_JOURNAL(s)->j_must_wait = 1;  	err = journal_end(&th, s, 10);  	if (err) -		goto out_err; +		goto out_unlock;  	if (!(*mount_flags & MS_RDONLY)) { +		/* +		 * Drop write lock. Quota will retake it when needed and lock +		 * ordering requires calling dquot_resume() without it. +		 */ +		reiserfs_write_unlock(s);  		dquot_resume(s, -1); +		reiserfs_write_lock(s);  		finish_unfinished(s);  		reiserfs_xattr_init(s, *mount_flags);  	} @@ -1455,9 +1469,10 @@ out_ok:  	reiserfs_write_unlock(s);  	return 0; +out_unlock: +	reiserfs_write_unlock(s);  out_err:  	kfree(new_opts); -	reiserfs_write_unlock(s);  	return err;  } @@ -2095,13 +2110,15 @@ static int reiserfs_write_dquot(struct dquot *dquot)  			  REISERFS_QUOTA_TRANS_BLOCKS(dquot->dq_sb));  	if (ret)  		goto out; +	reiserfs_write_unlock(dquot->dq_sb);  	ret = dquot_commit(dquot); +	reiserfs_write_lock(dquot->dq_sb);  	err =  	    journal_end(&th, dquot->dq_sb,  			REISERFS_QUOTA_TRANS_BLOCKS(dquot->dq_sb));  	if (!ret && err)  		ret = err; -      out: +out:  	reiserfs_write_unlock(dquot->dq_sb);  	return ret;  } @@ -2117,13 +2134,15 @@ static int reiserfs_acquire_dquot(struct dquot *dquot)  			  REISERFS_QUOTA_INIT_BLOCKS(dquot->dq_sb));  	if (ret)  		goto out; +	reiserfs_write_unlock(dquot->dq_sb);  	ret = dquot_acquire(dquot); +	reiserfs_write_lock(dquot->dq_sb);  	err =  	    journal_end(&th, dquot->dq_sb,  			REISERFS_QUOTA_INIT_BLOCKS(dquot->dq_sb));  	if (!ret && err)  		ret = err; -      out: +out:  	reiserfs_write_unlock(dquot->dq_sb);  	return ret;  } @@ -2137,19 +2156,21 @@ static int reiserfs_release_dquot(struct dquot *dquot)  	ret =  	    journal_begin(&th, dquot->dq_sb,  			  REISERFS_QUOTA_DEL_BLOCKS(dquot->dq_sb)); +	reiserfs_write_unlock(dquot->dq_sb);  	if (ret) {  		/* Release dquot anyway to avoid endless cycle in dqput() */  		dquot_release(dquot);  		goto out;  	}  	ret = dquot_release(dquot); +	reiserfs_write_lock(dquot->dq_sb);  	err =  	    journal_end(&th, dquot->dq_sb,  			REISERFS_QUOTA_DEL_BLOCKS(dquot->dq_sb));  	if (!ret && err)  		ret = err; -      out:  	reiserfs_write_unlock(dquot->dq_sb); +out:  	return ret;  } @@ -2174,11 +2195,13 @@ static int reiserfs_write_info(struct super_block *sb, int type)  	ret = journal_begin(&th, sb, 2);  	if (ret)  		goto out; +	reiserfs_write_unlock(sb);  	ret = dquot_commit_info(sb, type); +	reiserfs_write_lock(sb);  	err = journal_end(&th, sb, 2);  	if (!ret && err)  		ret = err; -      out: +out:  	reiserfs_write_unlock(sb);  	return ret;  } @@ -2203,8 +2226,11 @@ static int reiserfs_quota_on(struct super_block *sb, int type, int format_id,  	struct reiserfs_transaction_handle th;  	int opt = type == USRQUOTA ? REISERFS_USRQUOTA : REISERFS_GRPQUOTA; -	if (!(REISERFS_SB(sb)->s_mount_opt & (1 << opt))) -		return -EINVAL; +	reiserfs_write_lock(sb); +	if (!(REISERFS_SB(sb)->s_mount_opt & (1 << opt))) { +		err = -EINVAL; +		goto out; +	}  	/* Quotafile not on the same filesystem? */  	if (path->dentry->d_sb != sb) { @@ -2246,8 +2272,10 @@ static int reiserfs_quota_on(struct super_block *sb, int type, int format_id,  		if (err)  			goto out;  	} -	err = dquot_quota_on(sb, type, format_id, path); +	reiserfs_write_unlock(sb); +	return dquot_quota_on(sb, type, format_id, path);  out: +	reiserfs_write_unlock(sb);  	return err;  } @@ -2320,7 +2348,9 @@ static ssize_t reiserfs_quota_write(struct super_block *sb, int type,  		tocopy = sb->s_blocksize - offset < towrite ?  		    sb->s_blocksize - offset : towrite;  		tmp_bh.b_state = 0; +		reiserfs_write_lock(sb);  		err = reiserfs_get_block(inode, blk, &tmp_bh, GET_BLOCK_CREATE); +		reiserfs_write_unlock(sb);  		if (err)  			goto out;  		if (offset || tocopy != sb->s_blocksize) @@ -2336,10 +2366,12 @@ static ssize_t reiserfs_quota_write(struct super_block *sb, int type,  		flush_dcache_page(bh->b_page);  		set_buffer_uptodate(bh);  		unlock_buffer(bh); +		reiserfs_write_lock(sb);  		reiserfs_prepare_for_journal(sb, bh, 1);  		journal_mark_dirty(current->journal_info, sb, bh);  		if (!journal_quota)  			reiserfs_add_ordered_list(inode, bh); +		reiserfs_write_unlock(sb);  		brelse(bh);  		offset = 0;  		towrite -= tocopy; diff --git a/fs/ubifs/find.c b/fs/ubifs/find.c index 28ec13af28d..2dcf3d473fe 100644 --- a/fs/ubifs/find.c +++ b/fs/ubifs/find.c @@ -681,8 +681,16 @@ int ubifs_find_free_leb_for_idx(struct ubifs_info *c)  	if (!lprops) {  		lprops = ubifs_fast_find_freeable(c);  		if (!lprops) { -			ubifs_assert(c->freeable_cnt == 0); -			if (c->lst.empty_lebs - c->lst.taken_empty_lebs > 0) { +			/* +			 * The first condition means the following: go scan the +			 * LPT if there are uncategorized lprops, which means +			 * there may be freeable LEBs there (UBIFS does not +			 * store the information about freeable LEBs in the +			 * master node). +			 */ +			if (c->in_a_category_cnt != c->main_lebs || +			    c->lst.empty_lebs - c->lst.taken_empty_lebs > 0) { +				ubifs_assert(c->freeable_cnt == 0);  				lprops = scan_for_leb_for_idx(c);  				if (IS_ERR(lprops)) {  					err = PTR_ERR(lprops); diff --git a/fs/ubifs/lprops.c b/fs/ubifs/lprops.c index e5a2a35a46d..46190a7c42a 100644 --- a/fs/ubifs/lprops.c +++ b/fs/ubifs/lprops.c @@ -300,8 +300,11 @@ void ubifs_add_to_cat(struct ubifs_info *c, struct ubifs_lprops *lprops,  	default:  		ubifs_assert(0);  	} +  	lprops->flags &= ~LPROPS_CAT_MASK;  	lprops->flags |= cat; +	c->in_a_category_cnt += 1; +	ubifs_assert(c->in_a_category_cnt <= c->main_lebs);  }  /** @@ -334,6 +337,9 @@ static void ubifs_remove_from_cat(struct ubifs_info *c,  	default:  		ubifs_assert(0);  	} + +	c->in_a_category_cnt -= 1; +	ubifs_assert(c->in_a_category_cnt >= 0);  }  /** diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h index 5486346d0a3..d133c276fe0 100644 --- a/fs/ubifs/ubifs.h +++ b/fs/ubifs/ubifs.h @@ -1183,6 +1183,8 @@ struct ubifs_debug_info;   * @freeable_list: list of freeable non-index LEBs (free + dirty == @leb_size)   * @frdi_idx_list: list of freeable index LEBs (free + dirty == @leb_size)   * @freeable_cnt: number of freeable LEBs in @freeable_list + * @in_a_category_cnt: count of lprops which are in a certain category, which + *                     basically meants that they were loaded from the flash   *   * @ltab_lnum: LEB number of LPT's own lprops table   * @ltab_offs: offset of LPT's own lprops table @@ -1412,6 +1414,7 @@ struct ubifs_info {  	struct list_head freeable_list;  	struct list_head frdi_idx_list;  	int freeable_cnt; +	int in_a_category_cnt;  	int ltab_lnum;  	int ltab_offs; diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c index e562dd43f41..e57e2daa357 100644 --- a/fs/xfs/xfs_aops.c +++ b/fs/xfs/xfs_aops.c @@ -481,11 +481,17 @@ static inline int bio_add_buffer(struct bio *bio, struct buffer_head *bh)   *   * The fix is two passes across the ioend list - one to start writeback on the   * buffer_heads, and then submit them for I/O on the second pass. + * + * If @fail is non-zero, it means that we have a situation where some part of + * the submission process has failed after we have marked paged for writeback + * and unlocked them. In this situation, we need to fail the ioend chain rather + * than submit it to IO. This typically only happens on a filesystem shutdown.   */  STATIC void  xfs_submit_ioend(  	struct writeback_control *wbc, -	xfs_ioend_t		*ioend) +	xfs_ioend_t		*ioend, +	int			fail)  {  	xfs_ioend_t		*head = ioend;  	xfs_ioend_t		*next; @@ -506,6 +512,18 @@ xfs_submit_ioend(  		next = ioend->io_list;  		bio = NULL; +		/* +		 * If we are failing the IO now, just mark the ioend with an +		 * error and finish it. This will run IO completion immediately +		 * as there is only one reference to the ioend at this point in +		 * time. +		 */ +		if (fail) { +			ioend->io_error = -fail; +			xfs_finish_ioend(ioend); +			continue; +		} +  		for (bh = ioend->io_buffer_head; bh; bh = bh->b_private) {  			if (!bio) { @@ -1060,7 +1078,18 @@ xfs_vm_writepage(  	xfs_start_page_writeback(page, 1, count); -	if (ioend && imap_valid) { +	/* if there is no IO to be submitted for this page, we are done */ +	if (!ioend) +		return 0; + +	ASSERT(iohead); + +	/* +	 * Any errors from this point onwards need tobe reported through the IO +	 * completion path as we have marked the initial page as under writeback +	 * and unlocked it. +	 */ +	if (imap_valid) {  		xfs_off_t		end_index;  		end_index = imap.br_startoff + imap.br_blockcount; @@ -1079,20 +1108,15 @@ xfs_vm_writepage(  				  wbc, end_index);  	} -	if (iohead) { -		/* -		 * Reserve log space if we might write beyond the on-disk -		 * inode size. -		 */ -		if (ioend->io_type != XFS_IO_UNWRITTEN && -		    xfs_ioend_is_append(ioend)) { -			err = xfs_setfilesize_trans_alloc(ioend); -			if (err) -				goto error; -		} -		xfs_submit_ioend(wbc, iohead); -	} +	/* +	 * Reserve log space if we might write beyond the on-disk inode size. +	 */ +	err = 0; +	if (ioend->io_type != XFS_IO_UNWRITTEN && xfs_ioend_is_append(ioend)) +		err = xfs_setfilesize_trans_alloc(ioend); + +	xfs_submit_ioend(wbc, iohead, err);  	return 0; diff --git a/fs/xfs/xfs_attr_leaf.c b/fs/xfs/xfs_attr_leaf.c index d330111ca73..70eec182977 100644 --- a/fs/xfs/xfs_attr_leaf.c +++ b/fs/xfs/xfs_attr_leaf.c @@ -1291,6 +1291,7 @@ xfs_attr_leaf_rebalance(xfs_da_state_t *state, xfs_da_state_blk_t *blk1,  	leaf2 = blk2->bp->b_addr;  	ASSERT(leaf1->hdr.info.magic == cpu_to_be16(XFS_ATTR_LEAF_MAGIC));  	ASSERT(leaf2->hdr.info.magic == cpu_to_be16(XFS_ATTR_LEAF_MAGIC)); +	ASSERT(leaf2->hdr.count == 0);  	args = state->args;  	trace_xfs_attr_leaf_rebalance(args); @@ -1361,6 +1362,7 @@ xfs_attr_leaf_rebalance(xfs_da_state_t *state, xfs_da_state_blk_t *blk1,  		 * I assert that since all callers pass in an empty  		 * second buffer, this code should never execute.  		 */ +		ASSERT(0);  		/*  		 * Figure the total bytes to be added to the destination leaf. @@ -1422,10 +1424,24 @@ xfs_attr_leaf_rebalance(xfs_da_state_t *state, xfs_da_state_blk_t *blk1,  			args->index2 = 0;  			args->blkno2 = blk2->blkno;  		} else { +			/* +			 * On a double leaf split, the original attr location +			 * is already stored in blkno2/index2, so don't +			 * overwrite it overwise we corrupt the tree. +			 */  			blk2->index = blk1->index  				    - be16_to_cpu(leaf1->hdr.count); -			args->index = args->index2 = blk2->index; -			args->blkno = args->blkno2 = blk2->blkno; +			args->index = blk2->index; +			args->blkno = blk2->blkno; +			if (!state->extravalid) { +				/* +				 * set the new attr location to match the old +				 * one and let the higher level split code +				 * decide where in the leaf to place it. +				 */ +				args->index2 = blk2->index; +				args->blkno2 = blk2->blkno; +			}  		}  	} else {  		ASSERT(state->inleaf == 1); diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c index 933b7930b86..4b0b8dd1b7b 100644 --- a/fs/xfs/xfs_buf.c +++ b/fs/xfs/xfs_buf.c @@ -1197,9 +1197,14 @@ xfs_buf_bio_end_io(  {  	xfs_buf_t		*bp = (xfs_buf_t *)bio->bi_private; -	xfs_buf_ioerror(bp, -error); +	/* +	 * don't overwrite existing errors - otherwise we can lose errors on +	 * buffers that require multiple bios to complete. +	 */ +	if (!bp->b_error) +		xfs_buf_ioerror(bp, -error); -	if (!error && xfs_buf_is_vmapped(bp) && (bp->b_flags & XBF_READ)) +	if (!bp->b_error && xfs_buf_is_vmapped(bp) && (bp->b_flags & XBF_READ))  		invalidate_kernel_vmap_range(bp->b_addr, xfs_buf_vmap_len(bp));  	_xfs_buf_ioend(bp, 1); @@ -1279,6 +1284,11 @@ next_chunk:  		if (size)  			goto next_chunk;  	} else { +		/* +		 * This is guaranteed not to be the last io reference count +		 * because the caller (xfs_buf_iorequest) holds a count itself. +		 */ +		atomic_dec(&bp->b_io_remaining);  		xfs_buf_ioerror(bp, EIO);  		bio_put(bio);  	}  |