diff options
author | Bob Peterson <rpeterso@redhat.com> | 2020-02-27 21:47:53 +0300 |
---|---|---|
committer | Bob Peterson <rpeterso@redhat.com> | 2020-03-27 22:08:04 +0300 |
commit | 2fba46a04c383f91e7fe837d43bf1ab33ce32b6a (patch) | |
tree | 3137e791a08473e956a2a6ac477cb20b8677081b /fs/gfs2/file.c | |
parent | d580712a37272182cb63002878f3bb7bcebbb8bd (diff) | |
download | linux-2fba46a04c383f91e7fe837d43bf1ab33ce32b6a.tar.xz |
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.c | 35 |
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 |