summaryrefslogtreecommitdiffstats
path: root/fs/gfs2/file.c
diff options
context:
space:
mode:
authorBob Peterson <rpeterso@redhat.com>2020-02-27 19:47:53 +0100
committerBob Peterson <rpeterso@redhat.com>2020-03-27 20:08:04 +0100
commit2fba46a04c383f91e7fe837d43bf1ab33ce32b6a (patch)
tree3137e791a08473e956a2a6ac477cb20b8677081b /fs/gfs2/file.c
parentgfs2: eliminate gfs2_rsqa_alloc in favor of gfs2_qa_alloc (diff)
downloadlinux-2fba46a04c383f91e7fe837d43bf1ab33ce32b6a.tar.xz
linux-2fba46a04c383f91e7fe837d43bf1ab33ce32b6a.zip
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: Bob Peterson <rpeterso@redhat.com>
Diffstat (limited to 'fs/gfs2/file.c')
-rw-r--r--fs/gfs2/file.c35
1 files changed, 25 insertions, 10 deletions
diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c
index 54b0708e6d35..f18876cdfb0f 100644
--- a/fs/gfs2/file.c
+++ b/fs/gfs2/file.c
@@ -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,10 +701,8 @@ 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;
-
- gfs2_rsqa_delete(ip, &inode->i_writecount);
+ 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