diff options
author | Rohith Surabattula <rohiths@microsoft.com> | 2021-04-19 21:02:03 +0200 |
---|---|---|
committer | Steve French <stfrench@microsoft.com> | 2021-05-04 18:53:15 +0200 |
commit | 78c09634f7dc061a3bd09704cdbebb3762a45cdf (patch) | |
tree | 54a99294ff733dbf99bad6110c78e2aeb9f33151 /fs/cifs | |
parent | cifs: fix regression when mounting shares with prefix paths (diff) | |
download | linux-78c09634f7dc061a3bd09704cdbebb3762a45cdf.tar.xz linux-78c09634f7dc061a3bd09704cdbebb3762a45cdf.zip |
Cifs: Fix kernel oops caused by deferred close for files.
Fix regression issue caused by deferred close for files.
Signed-off-by: Rohith Surabattula <rohiths@microsoft.com>
Reviewed-by: Shyam Prasad N <sprasad@microsoft.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
Diffstat (limited to 'fs/cifs')
-rw-r--r-- | fs/cifs/cifsproto.h | 2 | ||||
-rw-r--r-- | fs/cifs/file.c | 16 | ||||
-rw-r--r-- | fs/cifs/inode.c | 3 | ||||
-rw-r--r-- | fs/cifs/misc.c | 17 |
4 files changed, 33 insertions, 5 deletions
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index c6dacce87d3a..3c6b97ef39d3 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -278,6 +278,8 @@ extern void cifs_del_deferred_close(struct cifsFileInfo *cfile); extern void cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode); +extern void cifs_close_all_deferred_files(struct cifs_tcon *cifs_tcon); + extern struct TCP_Server_Info *cifs_get_tcp_session(struct smb3_fs_context *ctx); extern void cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect); diff --git a/fs/cifs/file.c b/fs/cifs/file.c index c95893351b6c..919c82d4713d 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -878,6 +878,10 @@ void smb2_deferred_work_close(struct work_struct *work) struct cifsFileInfo, deferred.work); spin_lock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock); + if (!cfile->deferred_scheduled) { + spin_unlock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock); + return; + } cifs_del_deferred_close(cfile); cfile->deferred_scheduled = false; spin_unlock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock); @@ -1987,8 +1991,10 @@ cifs_write(struct cifsFileInfo *open_file, __u32 pid, const char *write_data, if (total_written > 0) { spin_lock(&d_inode(dentry)->i_lock); - if (*offset > d_inode(dentry)->i_size) + if (*offset > d_inode(dentry)->i_size) { i_size_write(d_inode(dentry), *offset); + d_inode(dentry)->i_blocks = (512 - 1 + *offset) >> 9; + } spin_unlock(&d_inode(dentry)->i_lock); } mark_inode_dirty_sync(d_inode(dentry)); @@ -2647,8 +2653,10 @@ static int cifs_write_end(struct file *file, struct address_space *mapping, if (rc > 0) { spin_lock(&inode->i_lock); - if (pos > inode->i_size) + if (pos > inode->i_size) { i_size_write(inode, pos); + inode->i_blocks = (512 - 1 + pos) >> 9; + } spin_unlock(&inode->i_lock); } @@ -4864,7 +4872,6 @@ oplock_break_ack: cinode); cifs_dbg(FYI, "Oplock release rc = %d\n", rc); } - _cifsFileInfo_put(cfile, false /* do not wait for ourself */, false); /* * When oplock break is received and there are no active * file handles but cached, then set the flag oplock_break_received. @@ -4872,11 +4879,12 @@ oplock_break_ack: */ spin_lock(&CIFS_I(inode)->deferred_lock); is_deferred = cifs_is_deferred_close(cfile, &dclose); - if (is_deferred) { + if (is_deferred && cfile->deferred_scheduled) { cfile->oplock_break_received = true; mod_delayed_work(deferredclose_wq, &cfile->deferred, 0); } spin_unlock(&CIFS_I(inode)->deferred_lock); + _cifsFileInfo_put(cfile, false /* do not wait for ourself */, false); cifs_done_oplock_break(cinode); } diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 728ff45b6667..591f18e3e933 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -1647,7 +1647,7 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry) goto unlink_out; } - cifs_close_deferred_file(CIFS_I(inode)); + cifs_close_all_deferred_files(tcon); if (cap_unix(tcon->ses) && (CIFS_UNIX_POSIX_PATH_OPS_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability))) { rc = CIFSPOSIXDelFile(xid, tcon, full_path, @@ -2125,6 +2125,7 @@ cifs_rename2(struct user_namespace *mnt_userns, struct inode *source_dir, goto cifs_rename_exit; } + cifs_close_all_deferred_files(tcon); rc = cifs_do_rename(xid, source_dentry, from_name, target_dentry, to_name); diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index e63fbd4a6bfe..524dbdfb7184 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -734,6 +734,23 @@ cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode) } } +void +cifs_close_all_deferred_files(struct cifs_tcon *tcon) +{ + struct cifsFileInfo *cfile; + struct cifsInodeInfo *cinode; + struct list_head *tmp; + + spin_lock(&tcon->open_file_lock); + list_for_each(tmp, &tcon->openFileList) { + cfile = list_entry(tmp, struct cifsFileInfo, tlist); + cinode = CIFS_I(d_inode(cfile->dentry)); + if (delayed_work_pending(&cfile->deferred)) + mod_delayed_work(deferredclose_wq, &cfile->deferred, 0); + } + spin_unlock(&tcon->open_file_lock); +} + /* parses DFS refferal V3 structure * caller is responsible for freeing target_nodes * returns: |