Skip to content

Commit c3f207a

Browse files
rohiths-msftsmfrench
authored andcommitted
cifs: Deferred close for files
When file is closed, SMB2 close request is not sent to server immediately and is deferred for acregmax defined interval. When file is reopened by same process for read or write, the file handle is reused if an oplock is held. When client receives a oplock/lease break, file is closed immediately if reference count is zero, else oplock is downgraded. Signed-off-by: Rohith Surabattula <rohiths@microsoft.com> Reviewed-by: Shyam Prasad N <sprasad@microsoft.com> Signed-off-by: Steve French <stfrench@microsoft.com>
1 parent fee742b commit c3f207a

6 files changed

Lines changed: 187 additions & 3 deletions

File tree

fs/cifs/cifsfs.c

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ struct workqueue_struct *cifsiod_wq;
133133
struct workqueue_struct *decrypt_wq;
134134
struct workqueue_struct *fileinfo_put_wq;
135135
struct workqueue_struct *cifsoplockd_wq;
136+
struct workqueue_struct *deferredclose_wq;
136137
__u32 cifs_lock_secret;
137138

138139
/*
@@ -390,6 +391,8 @@ cifs_alloc_inode(struct super_block *sb)
390391
/* cifs_inode->vfs_inode.i_flags = S_NOATIME | S_NOCMTIME; */
391392
INIT_LIST_HEAD(&cifs_inode->openFileList);
392393
INIT_LIST_HEAD(&cifs_inode->llist);
394+
INIT_LIST_HEAD(&cifs_inode->deferred_closes);
395+
spin_lock_init(&cifs_inode->deferred_lock);
393396
return &cifs_inode->vfs_inode;
394397
}
395398

@@ -1637,9 +1640,16 @@ init_cifs(void)
16371640
goto out_destroy_fileinfo_put_wq;
16381641
}
16391642

1643+
deferredclose_wq = alloc_workqueue("deferredclose",
1644+
WQ_FREEZABLE|WQ_MEM_RECLAIM, 0);
1645+
if (!deferredclose_wq) {
1646+
rc = -ENOMEM;
1647+
goto out_destroy_cifsoplockd_wq;
1648+
}
1649+
16401650
rc = cifs_fscache_register();
16411651
if (rc)
1642-
goto out_destroy_cifsoplockd_wq;
1652+
goto out_destroy_deferredclose_wq;
16431653

16441654
rc = cifs_init_inodecache();
16451655
if (rc)
@@ -1707,6 +1717,8 @@ init_cifs(void)
17071717
cifs_destroy_inodecache();
17081718
out_unreg_fscache:
17091719
cifs_fscache_unregister();
1720+
out_destroy_deferredclose_wq:
1721+
destroy_workqueue(deferredclose_wq);
17101722
out_destroy_cifsoplockd_wq:
17111723
destroy_workqueue(cifsoplockd_wq);
17121724
out_destroy_fileinfo_put_wq:
@@ -1741,6 +1753,7 @@ exit_cifs(void)
17411753
cifs_destroy_mids();
17421754
cifs_destroy_inodecache();
17431755
cifs_fscache_unregister();
1756+
destroy_workqueue(deferredclose_wq);
17441757
destroy_workqueue(cifsoplockd_wq);
17451758
destroy_workqueue(decrypt_wq);
17461759
destroy_workqueue(fileinfo_put_wq);

fs/cifs/cifsglob.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1154,6 +1154,14 @@ struct cifs_pending_open {
11541154
__u32 oplock;
11551155
};
11561156

1157+
struct cifs_deferred_close {
1158+
struct list_head dlist;
1159+
struct tcon_link *tlink;
1160+
__u16 netfid;
1161+
__u64 persistent_fid;
1162+
__u64 volatile_fid;
1163+
};
1164+
11571165
/*
11581166
* This info hangs off the cifsFileInfo structure, pointed to by llist.
11591167
* This is used to track byte stream locks on the file
@@ -1248,6 +1256,9 @@ struct cifsFileInfo {
12481256
struct cifs_search_info srch_inf;
12491257
struct work_struct oplock_break; /* work for oplock breaks */
12501258
struct work_struct put; /* work for the final part of _put */
1259+
struct delayed_work deferred;
1260+
bool oplock_break_received; /* Flag to indicate oplock break */
1261+
bool deferred_scheduled;
12511262
};
12521263

12531264
struct cifs_io_parms {
@@ -1392,6 +1403,7 @@ struct cifsInodeInfo {
13921403
#define CIFS_INO_DELETE_PENDING (3) /* delete pending on server */
13931404
#define CIFS_INO_INVALID_MAPPING (4) /* pagecache is invalid */
13941405
#define CIFS_INO_LOCK (5) /* lock bit for synchronization */
1406+
#define CIFS_INO_MODIFIED_ATTR (6) /* Indicate change in mtime/ctime */
13951407
unsigned long flags;
13961408
spinlock_t writers_lock;
13971409
unsigned int writers; /* Number of writers on this inode */
@@ -1404,6 +1416,8 @@ struct cifsInodeInfo {
14041416
struct fscache_cookie *fscache;
14051417
#endif
14061418
struct inode vfs_inode;
1419+
struct list_head deferred_closes; /* list of deferred closes */
1420+
spinlock_t deferred_lock; /* protection on deferred list */
14071421
};
14081422

14091423
static inline struct cifsInodeInfo *
@@ -1871,11 +1885,14 @@ extern bool disable_legacy_dialects; /* forbid vers=1.0 and vers=2.0 mounts */
18711885

18721886
void cifs_oplock_break(struct work_struct *work);
18731887
void cifs_queue_oplock_break(struct cifsFileInfo *cfile);
1888+
void smb2_deferred_work_close(struct work_struct *work);
18741889

1890+
extern const struct slow_work_ops cifs_oplock_break_ops;
18751891
extern struct workqueue_struct *cifsiod_wq;
18761892
extern struct workqueue_struct *decrypt_wq;
18771893
extern struct workqueue_struct *fileinfo_put_wq;
18781894
extern struct workqueue_struct *cifsoplockd_wq;
1895+
extern struct workqueue_struct *deferredclose_wq;
18791896
extern __u32 cifs_lock_secret;
18801897

18811898
extern mempool_t *cifs_mid_poolp;

fs/cifs/cifsproto.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,17 @@ extern void cifs_add_pending_open_locked(struct cifs_fid *fid,
267267
struct tcon_link *tlink,
268268
struct cifs_pending_open *open);
269269
extern void cifs_del_pending_open(struct cifs_pending_open *open);
270+
271+
extern bool cifs_is_deferred_close(struct cifsFileInfo *cfile,
272+
struct cifs_deferred_close **dclose);
273+
274+
extern void cifs_add_deferred_close(struct cifsFileInfo *cfile,
275+
struct cifs_deferred_close *dclose);
276+
277+
extern void cifs_del_deferred_close(struct cifsFileInfo *cfile);
278+
279+
extern void cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode);
280+
270281
extern struct TCP_Server_Info *cifs_get_tcp_session(struct smb3_fs_context *ctx);
271282
extern void cifs_put_tcp_session(struct TCP_Server_Info *server,
272283
int from_reconnect);

fs/cifs/file.c

Lines changed: 82 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -322,9 +322,12 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
322322
cfile->dentry = dget(dentry);
323323
cfile->f_flags = file->f_flags;
324324
cfile->invalidHandle = false;
325+
cfile->oplock_break_received = false;
326+
cfile->deferred_scheduled = false;
325327
cfile->tlink = cifs_get_tlink(tlink);
326328
INIT_WORK(&cfile->oplock_break, cifs_oplock_break);
327329
INIT_WORK(&cfile->put, cifsFileInfo_put_work);
330+
INIT_DELAYED_WORK(&cfile->deferred, smb2_deferred_work_close);
328331
mutex_init(&cfile->fh_mutex);
329332
spin_lock_init(&cfile->file_info_lock);
330333

@@ -565,6 +568,23 @@ int cifs_open(struct inode *inode, struct file *file)
565568
file->f_op = &cifs_file_direct_ops;
566569
}
567570

571+
spin_lock(&CIFS_I(inode)->deferred_lock);
572+
/* Get the cached handle as SMB2 close is deferred */
573+
rc = cifs_get_readable_path(tcon, full_path, &cfile);
574+
if (rc == 0) {
575+
if (file->f_flags == cfile->f_flags) {
576+
file->private_data = cfile;
577+
cifs_del_deferred_close(cfile);
578+
spin_unlock(&CIFS_I(inode)->deferred_lock);
579+
goto out;
580+
} else {
581+
spin_unlock(&CIFS_I(inode)->deferred_lock);
582+
_cifsFileInfo_put(cfile, true, false);
583+
}
584+
} else {
585+
spin_unlock(&CIFS_I(inode)->deferred_lock);
586+
}
587+
568588
if (server->oplocks)
569589
oplock = REQ_OPLOCK;
570590
else
@@ -846,11 +866,52 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush)
846866
return rc;
847867
}
848868

869+
void smb2_deferred_work_close(struct work_struct *work)
870+
{
871+
struct cifsFileInfo *cfile = container_of(work,
872+
struct cifsFileInfo, deferred.work);
873+
874+
spin_lock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock);
875+
cifs_del_deferred_close(cfile);
876+
cfile->deferred_scheduled = false;
877+
spin_unlock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock);
878+
_cifsFileInfo_put(cfile, true, false);
879+
}
880+
849881
int cifs_close(struct inode *inode, struct file *file)
850882
{
883+
struct cifsFileInfo *cfile;
884+
struct cifsInodeInfo *cinode = CIFS_I(inode);
885+
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
886+
struct cifs_deferred_close *dclose;
887+
851888
if (file->private_data != NULL) {
852-
_cifsFileInfo_put(file->private_data, true, false);
889+
cfile = file->private_data;
853890
file->private_data = NULL;
891+
dclose = kmalloc(sizeof(struct cifs_deferred_close), GFP_KERNEL);
892+
if ((cinode->oplock == CIFS_CACHE_RHW_FLG) &&
893+
dclose) {
894+
if (test_bit(CIFS_INO_MODIFIED_ATTR, &cinode->flags))
895+
inode->i_ctime = inode->i_mtime = current_time(inode);
896+
spin_lock(&cinode->deferred_lock);
897+
cifs_add_deferred_close(cfile, dclose);
898+
if (cfile->deferred_scheduled) {
899+
mod_delayed_work(deferredclose_wq,
900+
&cfile->deferred, cifs_sb->ctx->acregmax);
901+
} else {
902+
/* Deferred close for files */
903+
queue_delayed_work(deferredclose_wq,
904+
&cfile->deferred, cifs_sb->ctx->acregmax);
905+
cfile->deferred_scheduled = true;
906+
spin_unlock(&cinode->deferred_lock);
907+
return 0;
908+
}
909+
spin_unlock(&cinode->deferred_lock);
910+
_cifsFileInfo_put(cfile, true, false);
911+
} else {
912+
_cifsFileInfo_put(cfile, true, false);
913+
kfree(dclose);
914+
}
854915
}
855916

856917
/* return code from the ->release op is always ignored */
@@ -1947,7 +2008,8 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode,
19472008
if (fsuid_only && !uid_eq(open_file->uid, current_fsuid()))
19482009
continue;
19492010
if (OPEN_FMODE(open_file->f_flags) & FMODE_READ) {
1950-
if (!open_file->invalidHandle) {
2011+
if ((!open_file->invalidHandle) &&
2012+
(!open_file->oplock_break_received)) {
19512013
/* found a good file */
19522014
/* lock it so it will not be closed on us */
19532015
cifsFileInfo_get(open_file);
@@ -2476,6 +2538,8 @@ static int cifs_writepages(struct address_space *mapping,
24762538
if (cfile)
24772539
cifsFileInfo_put(cfile);
24782540
free_xid(xid);
2541+
/* Indication to update ctime and mtime as close is deferred */
2542+
set_bit(CIFS_INO_MODIFIED_ATTR, &CIFS_I(inode)->flags);
24792543
return rc;
24802544
}
24812545

@@ -2584,6 +2648,8 @@ static int cifs_write_end(struct file *file, struct address_space *mapping,
25842648

25852649
unlock_page(page);
25862650
put_page(page);
2651+
/* Indication to update ctime and mtime as close is deferred */
2652+
set_bit(CIFS_INO_MODIFIED_ATTR, &CIFS_I(inode)->flags);
25872653

25882654
return rc;
25892655
}
@@ -4744,6 +4810,8 @@ void cifs_oplock_break(struct work_struct *work)
47444810
struct TCP_Server_Info *server = tcon->ses->server;
47454811
int rc = 0;
47464812
bool purge_cache = false;
4813+
bool is_deferred = false;
4814+
struct cifs_deferred_close *dclose;
47474815

47484816
wait_on_bit(&cinode->flags, CIFS_INODE_PENDING_WRITERS,
47494817
TASK_UNINTERRUPTIBLE);
@@ -4791,6 +4859,18 @@ void cifs_oplock_break(struct work_struct *work)
47914859
cifs_dbg(FYI, "Oplock release rc = %d\n", rc);
47924860
}
47934861
_cifsFileInfo_put(cfile, false /* do not wait for ourself */, false);
4862+
/*
4863+
* When oplock break is received and there are no active
4864+
* file handles but cached, then set the flag oplock_break_received.
4865+
* So, new open will not use cached handle.
4866+
*/
4867+
spin_lock(&CIFS_I(inode)->deferred_lock);
4868+
is_deferred = cifs_is_deferred_close(cfile, &dclose);
4869+
if (is_deferred) {
4870+
cfile->oplock_break_received = true;
4871+
mod_delayed_work(deferredclose_wq, &cfile->deferred, 0);
4872+
}
4873+
spin_unlock(&CIFS_I(inode)->deferred_lock);
47944874
cifs_done_oplock_break(cinode);
47954875
}
47964876

fs/cifs/inode.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1645,6 +1645,7 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
16451645
goto unlink_out;
16461646
}
16471647

1648+
cifs_close_deferred_file(CIFS_I(inode));
16481649
if (cap_unix(tcon->ses) && (CIFS_UNIX_POSIX_PATH_OPS_CAP &
16491650
le64_to_cpu(tcon->fsUnixInfo.Capability))) {
16501651
rc = CIFSPOSIXDelFile(xid, tcon, full_path,

fs/cifs/misc.c

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -672,6 +672,68 @@ cifs_add_pending_open(struct cifs_fid *fid, struct tcon_link *tlink,
672672
spin_unlock(&tlink_tcon(open->tlink)->open_file_lock);
673673
}
674674

675+
bool
676+
cifs_is_deferred_close(struct cifsFileInfo *cfile, struct cifs_deferred_close **pdclose)
677+
{
678+
struct cifs_deferred_close *dclose;
679+
680+
list_for_each_entry(dclose, &CIFS_I(d_inode(cfile->dentry))->deferred_closes, dlist) {
681+
if ((dclose->netfid == cfile->fid.netfid) &&
682+
(dclose->persistent_fid == cfile->fid.persistent_fid) &&
683+
(dclose->volatile_fid == cfile->fid.volatile_fid)) {
684+
*pdclose = dclose;
685+
return true;
686+
}
687+
}
688+
return false;
689+
}
690+
691+
void
692+
cifs_add_deferred_close(struct cifsFileInfo *cfile, struct cifs_deferred_close *dclose)
693+
{
694+
bool is_deferred = false;
695+
struct cifs_deferred_close *pdclose;
696+
697+
is_deferred = cifs_is_deferred_close(cfile, &pdclose);
698+
if (is_deferred) {
699+
kfree(dclose);
700+
return;
701+
}
702+
703+
dclose->tlink = cfile->tlink;
704+
dclose->netfid = cfile->fid.netfid;
705+
dclose->persistent_fid = cfile->fid.persistent_fid;
706+
dclose->volatile_fid = cfile->fid.volatile_fid;
707+
list_add_tail(&dclose->dlist, &CIFS_I(d_inode(cfile->dentry))->deferred_closes);
708+
}
709+
710+
void
711+
cifs_del_deferred_close(struct cifsFileInfo *cfile)
712+
{
713+
bool is_deferred = false;
714+
struct cifs_deferred_close *dclose;
715+
716+
is_deferred = cifs_is_deferred_close(cfile, &dclose);
717+
if (!is_deferred)
718+
return;
719+
list_del(&dclose->dlist);
720+
kfree(dclose);
721+
}
722+
723+
void
724+
cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode)
725+
{
726+
struct cifsFileInfo *cfile = NULL;
727+
struct cifs_deferred_close *dclose;
728+
729+
list_for_each_entry(cfile, &cifs_inode->openFileList, flist) {
730+
spin_lock(&cifs_inode->deferred_lock);
731+
if (cifs_is_deferred_close(cfile, &dclose))
732+
mod_delayed_work(deferredclose_wq, &cfile->deferred, 0);
733+
spin_unlock(&cifs_inode->deferred_lock);
734+
}
735+
}
736+
675737
/* parses DFS refferal V3 structure
676738
* caller is responsible for freeing target_nodes
677739
* returns:

0 commit comments

Comments
 (0)