Commit 2fba46a0 authored by Bob Peterson's avatar Bob Peterson
Browse files

gfs2: Change inode qa_data to allow multiple users



Before this patch, multiple users called gfs2_qa_alloc which allocated
a qadata structure to the inode, if quotas are turned on. Later, in
file close or evict, the structure was deleted with gfs2_qa_delete.
But there can be several competing processes who need access to the
structure. There were races between file close (release) and the others.
Thus, a release could delete the structure out from under a process
that relied upon its existence. For example, chown.

This patch changes the management of the qadata structures to be
a get/put scheme. Function gfs2_qa_alloc has been changed to gfs2_qa_get
and if the structure is allocated, the count essentially starts out at
1. Function gfs2_qa_delete has been renamed to gfs2_qa_put, and the
last guy to decrement the count to 0 frees the memory.

Signed-off-by: default avatarBob Peterson <rpeterso@redhat.com>
parent d580712a
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -117,14 +117,14 @@ int gfs2_set_acl(struct inode *inode, struct posix_acl *acl, int type)
	if (acl && acl->a_count > GFS2_ACL_MAX_ENTRIES(GFS2_SB(inode)))
		return -E2BIG;

	ret = gfs2_qa_alloc(ip);
	ret = gfs2_qa_get(ip);
	if (ret)
		return ret;

	if (!gfs2_glock_is_locked_by_me(ip->i_gl)) {
		ret = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &gh);
		if (ret)
			return ret;
			goto out;
		need_unlock = true;
	}

@@ -144,5 +144,7 @@ int gfs2_set_acl(struct inode *inode, struct posix_acl *acl, int type)
unlock:
	if (need_unlock)
		gfs2_glock_dq_uninit(&gh);
out:
	gfs2_qa_put(ip);
	return ret;
}
+1 −1
Original line number Diff line number Diff line
@@ -2183,7 +2183,7 @@ int gfs2_setattr_size(struct inode *inode, u64 newsize)

	inode_dio_wait(inode);

	ret = gfs2_qa_alloc(ip);
	ret = gfs2_qa_get(ip);
	if (ret)
		goto out;

+25 −10
Original line number Diff line number Diff line
@@ -458,7 +458,7 @@ static vm_fault_t gfs2_page_mkwrite(struct vm_fault *vmf)

	sb_start_pagefault(inode->i_sb);

	ret = gfs2_qa_alloc(ip);
	ret = gfs2_qa_get(ip);
	if (ret)
		goto out;

@@ -553,6 +553,7 @@ out_quota_unlock:
out_unlock:
	gfs2_glock_dq(&gh);
out_uninit:
	gfs2_qa_put(ip);
	gfs2_holder_uninit(&gh);
	if (ret == 0) {
		set_page_dirty(page);
@@ -635,7 +636,17 @@ int gfs2_open_common(struct inode *inode, struct file *file)

	gfs2_assert_warn(GFS2_SB(inode), !file->private_data);
	file->private_data = fp;
	if (file->f_mode & FMODE_WRITE) {
		ret = gfs2_qa_get(GFS2_I(inode));
		if (ret)
			goto fail;
	}
	return 0;

fail:
	kfree(file->private_data);
	file->private_data = NULL;
	return ret;
}

/**
@@ -690,9 +701,7 @@ static int gfs2_release(struct inode *inode, struct file *file)
	kfree(file->private_data);
	file->private_data = NULL;

	if (!(file->f_mode & FMODE_WRITE))
		return 0;

	if (file->f_mode & FMODE_WRITE)
		gfs2_rsqa_delete(ip, &inode->i_writecount);
	return 0;
}
@@ -849,7 +858,7 @@ static ssize_t gfs2_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
	struct gfs2_inode *ip = GFS2_I(inode);
	ssize_t ret;

	ret = gfs2_qa_alloc(ip);
	ret = gfs2_qa_get(ip);
	if (ret)
		return ret;

@@ -860,7 +869,7 @@ static ssize_t gfs2_file_write_iter(struct kiocb *iocb, struct iov_iter *from)

		ret = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, 0, &gh);
		if (ret)
			return ret;
			goto out;
		gfs2_glock_dq_uninit(&gh);
	}

@@ -918,6 +927,8 @@ static ssize_t gfs2_file_write_iter(struct kiocb *iocb, struct iov_iter *from)

out_unlock:
	inode_unlock(inode);
out:
	gfs2_qa_put(ip);
	return ret;
}

@@ -1149,7 +1160,7 @@ static long gfs2_fallocate(struct file *file, int mode, loff_t offset, loff_t le
	if (mode & FALLOC_FL_PUNCH_HOLE) {
		ret = __gfs2_punch_hole(file, offset, len);
	} else {
		ret = gfs2_qa_alloc(ip);
		ret = gfs2_qa_get(ip);
		if (ret)
			goto out_putw;

@@ -1157,6 +1168,7 @@ static long gfs2_fallocate(struct file *file, int mode, loff_t offset, loff_t le

		if (ret)
			gfs2_rs_deltree(&ip->i_res);
		gfs2_qa_put(ip);
	}

out_putw:
@@ -1175,14 +1187,17 @@ static ssize_t gfs2_file_splice_write(struct pipe_inode_info *pipe,
{
	int error;
	struct gfs2_inode *ip = GFS2_I(out->f_mapping->host);
	ssize_t ret;

	error = gfs2_qa_alloc(ip);
	error = gfs2_qa_get(ip);
	if (error)
		return (ssize_t)error;

	gfs2_size_hint(out, *ppos, len);

	return iter_file_splice_write(pipe, out, ppos, len, flags);
	ret = iter_file_splice_write(pipe, out, ppos, len, flags);
	gfs2_qa_put(ip);
	return ret;
}

#ifdef CONFIG_GFS2_FS_LOCKING_DLM
+1 −0
Original line number Diff line number Diff line
@@ -295,6 +295,7 @@ struct gfs2_qadata { /* quota allocation data */
	struct gfs2_quota_data *qa_qd[2 * GFS2_MAXQUOTAS];
	struct gfs2_holder qa_qd_ghs[2 * GFS2_MAXQUOTAS];
	unsigned int qa_qd_num;
	int qa_ref;
};

/* Resource group multi-block reservation, in order of appearance:
+19 −13
Original line number Diff line number Diff line
@@ -588,13 +588,13 @@ static int gfs2_create_inode(struct inode *dir, struct dentry *dentry,
	if (!name->len || name->len > GFS2_FNAMESIZE)
		return -ENAMETOOLONG;

	error = gfs2_qa_alloc(dip);
	error = gfs2_qa_get(dip);
	if (error)
		return error;

	error = gfs2_rindex_update(sdp);
	if (error)
		return error;
		goto fail;

	error = gfs2_glock_nq_init(dip->i_gl, LM_ST_EXCLUSIVE, 0, ghs);
	if (error)
@@ -641,7 +641,7 @@ static int gfs2_create_inode(struct inode *dir, struct dentry *dentry,
		goto fail_gunlock;

	ip = GFS2_I(inode);
	error = gfs2_qa_alloc(ip);
	error = gfs2_qa_get(ip);
	if (error)
		goto fail_free_acls;

@@ -776,6 +776,7 @@ fail_gunlock2:
	clear_bit(GLF_INODE_CREATING, &io_gl->gl_flags);
	gfs2_glock_put(io_gl);
fail_free_inode:
	gfs2_qa_put(ip);
	if (ip->i_gl) {
		glock_clear_object(ip->i_gl, ip);
		gfs2_glock_put(ip->i_gl);
@@ -798,6 +799,7 @@ fail_gunlock:
	if (gfs2_holder_initialized(ghs + 1))
		gfs2_glock_dq_uninit(ghs + 1);
fail:
	gfs2_qa_put(dip);
	return error;
}

@@ -899,7 +901,7 @@ static int gfs2_link(struct dentry *old_dentry, struct inode *dir,
	if (S_ISDIR(inode->i_mode))
		return -EPERM;

	error = gfs2_qa_alloc(dip);
	error = gfs2_qa_get(dip);
	if (error)
		return error;

@@ -1002,6 +1004,7 @@ out_gunlock:
out_child:
	gfs2_glock_dq(ghs);
out_parent:
	gfs2_qa_put(ip);
	gfs2_holder_uninit(ghs);
	gfs2_holder_uninit(ghs + 1);
	return error;
@@ -1362,7 +1365,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry,
	if (error)
		return error;

	error = gfs2_qa_alloc(ndip);
	error = gfs2_qa_get(ndip);
	if (error)
		return error;

@@ -1562,6 +1565,7 @@ out_gunlock_r:
	if (gfs2_holder_initialized(&r_gh))
		gfs2_glock_dq_uninit(&r_gh);
out:
	gfs2_qa_put(ndip);
	return error;
}

@@ -1873,10 +1877,9 @@ static int setattr_chown(struct inode *inode, struct iattr *attr)
		ouid = nuid = NO_UID_QUOTA_CHANGE;
	if (!(attr->ia_valid & ATTR_GID) || gid_eq(ogid, ngid))
		ogid = ngid = NO_GID_QUOTA_CHANGE;

	error = gfs2_qa_alloc(ip);
	error = gfs2_qa_get(ip);
	if (error)
		goto out;
		return error;

	error = gfs2_rindex_update(sdp);
	if (error)
@@ -1914,6 +1917,7 @@ out_end_trans:
out_gunlock_q:
	gfs2_quota_unlock(ip);
out:
	gfs2_qa_put(ip);
	return error;
}

@@ -1935,21 +1939,21 @@ static int gfs2_setattr(struct dentry *dentry, struct iattr *attr)
	struct gfs2_holder i_gh;
	int error;

	error = gfs2_qa_alloc(ip);
	error = gfs2_qa_get(ip);
	if (error)
		return error;

	error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &i_gh);
	if (error)
		return error;
		goto out;

	error = -EPERM;
	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
		goto out;
		goto error;

	error = setattr_prepare(dentry, attr);
	if (error)
		goto out;
		goto error;

	if (attr->ia_valid & ATTR_SIZE)
		error = gfs2_setattr_size(inode, attr->ia_size);
@@ -1961,10 +1965,12 @@ static int gfs2_setattr(struct dentry *dentry, struct iattr *attr)
			error = posix_acl_chmod(inode, inode->i_mode);
	}

out:
error:
	if (!error)
		mark_inode_dirty(inode);
	gfs2_glock_dq_uninit(&i_gh);
out:
	gfs2_qa_put(ip);
	return error;
}

Loading