Skip to content

Commit 173217b

Browse files
ritbudhirajaSteve French
authored andcommitted
smb3: retrying on failed server close
In the current implementation, CIFS close sends a close to the server and does not check for the success of the server close. This patch adds functionality to check for server close return status and retries in case of an EBUSY or EAGAIN error. This can help avoid handle leaks Cc: [email protected] Signed-off-by: Ritvik Budhiraja <[email protected]> Signed-off-by: Steve French <[email protected]>
1 parent 93cee45 commit 173217b

File tree

7 files changed

+85
-17
lines changed

7 files changed

+85
-17
lines changed

fs/smb/client/cached_dir.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,7 @@ smb2_close_cached_fid(struct kref *ref)
417417
{
418418
struct cached_fid *cfid = container_of(ref, struct cached_fid,
419419
refcount);
420+
int rc;
420421

421422
spin_lock(&cfid->cfids->cfid_list_lock);
422423
if (cfid->on_list) {
@@ -430,9 +431,10 @@ smb2_close_cached_fid(struct kref *ref)
430431
cfid->dentry = NULL;
431432

432433
if (cfid->is_open) {
433-
SMB2_close(0, cfid->tcon, cfid->fid.persistent_fid,
434+
rc = SMB2_close(0, cfid->tcon, cfid->fid.persistent_fid,
434435
cfid->fid.volatile_fid);
435-
atomic_dec(&cfid->tcon->num_remote_opens);
436+
if (rc != -EBUSY && rc != -EAGAIN)
437+
atomic_dec(&cfid->tcon->num_remote_opens);
436438
}
437439

438440
free_cached_dir(cfid);

fs/smb/client/cifsfs.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ struct workqueue_struct *decrypt_wq;
156156
struct workqueue_struct *fileinfo_put_wq;
157157
struct workqueue_struct *cifsoplockd_wq;
158158
struct workqueue_struct *deferredclose_wq;
159+
struct workqueue_struct *serverclose_wq;
159160
__u32 cifs_lock_secret;
160161

161162
/*
@@ -1888,6 +1889,13 @@ init_cifs(void)
18881889
goto out_destroy_cifsoplockd_wq;
18891890
}
18901891

1892+
serverclose_wq = alloc_workqueue("serverclose",
1893+
WQ_FREEZABLE|WQ_MEM_RECLAIM, 0);
1894+
if (!serverclose_wq) {
1895+
rc = -ENOMEM;
1896+
goto out_destroy_serverclose_wq;
1897+
}
1898+
18911899
rc = cifs_init_inodecache();
18921900
if (rc)
18931901
goto out_destroy_deferredclose_wq;
@@ -1962,6 +1970,8 @@ init_cifs(void)
19621970
destroy_workqueue(decrypt_wq);
19631971
out_destroy_cifsiod_wq:
19641972
destroy_workqueue(cifsiod_wq);
1973+
out_destroy_serverclose_wq:
1974+
destroy_workqueue(serverclose_wq);
19651975
out_clean_proc:
19661976
cifs_proc_clean();
19671977
return rc;
@@ -1991,6 +2001,7 @@ exit_cifs(void)
19912001
destroy_workqueue(cifsoplockd_wq);
19922002
destroy_workqueue(decrypt_wq);
19932003
destroy_workqueue(fileinfo_put_wq);
2004+
destroy_workqueue(serverclose_wq);
19942005
destroy_workqueue(cifsiod_wq);
19952006
cifs_proc_clean();
19962007
}

fs/smb/client/cifsglob.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -442,10 +442,10 @@ struct smb_version_operations {
442442
/* set fid protocol-specific info */
443443
void (*set_fid)(struct cifsFileInfo *, struct cifs_fid *, __u32);
444444
/* close a file */
445-
void (*close)(const unsigned int, struct cifs_tcon *,
445+
int (*close)(const unsigned int, struct cifs_tcon *,
446446
struct cifs_fid *);
447447
/* close a file, returning file attributes and timestamps */
448-
void (*close_getattr)(const unsigned int xid, struct cifs_tcon *tcon,
448+
int (*close_getattr)(const unsigned int xid, struct cifs_tcon *tcon,
449449
struct cifsFileInfo *pfile_info);
450450
/* send a flush request to the server */
451451
int (*flush)(const unsigned int, struct cifs_tcon *, struct cifs_fid *);
@@ -1439,6 +1439,7 @@ struct cifsFileInfo {
14391439
bool swapfile:1;
14401440
bool oplock_break_cancelled:1;
14411441
bool status_file_deleted:1; /* file has been deleted */
1442+
bool offload:1; /* offload final part of _put to a wq */
14421443
unsigned int oplock_epoch; /* epoch from the lease break */
14431444
__u32 oplock_level; /* oplock/lease level from the lease break */
14441445
int count;
@@ -1447,6 +1448,7 @@ struct cifsFileInfo {
14471448
struct cifs_search_info srch_inf;
14481449
struct work_struct oplock_break; /* work for oplock breaks */
14491450
struct work_struct put; /* work for the final part of _put */
1451+
struct work_struct serverclose; /* work for serverclose */
14501452
struct delayed_work deferred;
14511453
bool deferred_close_scheduled; /* Flag to indicate close is scheduled */
14521454
char *symlink_target;
@@ -2103,6 +2105,7 @@ extern struct workqueue_struct *decrypt_wq;
21032105
extern struct workqueue_struct *fileinfo_put_wq;
21042106
extern struct workqueue_struct *cifsoplockd_wq;
21052107
extern struct workqueue_struct *deferredclose_wq;
2108+
extern struct workqueue_struct *serverclose_wq;
21062109
extern __u32 cifs_lock_secret;
21072110

21082111
extern mempool_t *cifs_sm_req_poolp;

fs/smb/client/file.c

Lines changed: 57 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,7 @@ cifs_down_write(struct rw_semaphore *sem)
459459
}
460460

461461
static void cifsFileInfo_put_work(struct work_struct *work);
462+
void serverclose_work(struct work_struct *work);
462463

463464
struct cifsFileInfo *cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
464465
struct tcon_link *tlink, __u32 oplock,
@@ -505,6 +506,7 @@ struct cifsFileInfo *cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
505506
cfile->tlink = cifs_get_tlink(tlink);
506507
INIT_WORK(&cfile->oplock_break, cifs_oplock_break);
507508
INIT_WORK(&cfile->put, cifsFileInfo_put_work);
509+
INIT_WORK(&cfile->serverclose, serverclose_work);
508510
INIT_DELAYED_WORK(&cfile->deferred, smb2_deferred_work_close);
509511
mutex_init(&cfile->fh_mutex);
510512
spin_lock_init(&cfile->file_info_lock);
@@ -596,6 +598,40 @@ static void cifsFileInfo_put_work(struct work_struct *work)
596598
cifsFileInfo_put_final(cifs_file);
597599
}
598600

601+
void serverclose_work(struct work_struct *work)
602+
{
603+
struct cifsFileInfo *cifs_file = container_of(work,
604+
struct cifsFileInfo, serverclose);
605+
606+
struct cifs_tcon *tcon = tlink_tcon(cifs_file->tlink);
607+
608+
struct TCP_Server_Info *server = tcon->ses->server;
609+
int rc = 0;
610+
int retries = 0;
611+
int MAX_RETRIES = 4;
612+
613+
do {
614+
if (server->ops->close_getattr)
615+
rc = server->ops->close_getattr(0, tcon, cifs_file);
616+
else if (server->ops->close)
617+
rc = server->ops->close(0, tcon, &cifs_file->fid);
618+
619+
if (rc == -EBUSY || rc == -EAGAIN) {
620+
retries++;
621+
msleep(250);
622+
}
623+
} while ((rc == -EBUSY || rc == -EAGAIN) && (retries < MAX_RETRIES)
624+
);
625+
626+
if (retries == MAX_RETRIES)
627+
pr_warn("Serverclose failed %d times, giving up\n", MAX_RETRIES);
628+
629+
if (cifs_file->offload)
630+
queue_work(fileinfo_put_wq, &cifs_file->put);
631+
else
632+
cifsFileInfo_put_final(cifs_file);
633+
}
634+
599635
/**
600636
* cifsFileInfo_put - release a reference of file priv data
601637
*
@@ -636,10 +672,13 @@ void _cifsFileInfo_put(struct cifsFileInfo *cifs_file,
636672
struct cifs_fid fid = {};
637673
struct cifs_pending_open open;
638674
bool oplock_break_cancelled;
675+
bool serverclose_offloaded = false;
639676

640677
spin_lock(&tcon->open_file_lock);
641678
spin_lock(&cifsi->open_file_lock);
642679
spin_lock(&cifs_file->file_info_lock);
680+
681+
cifs_file->offload = offload;
643682
if (--cifs_file->count > 0) {
644683
spin_unlock(&cifs_file->file_info_lock);
645684
spin_unlock(&cifsi->open_file_lock);
@@ -681,24 +720,36 @@ void _cifsFileInfo_put(struct cifsFileInfo *cifs_file,
681720
if (!tcon->need_reconnect && !cifs_file->invalidHandle) {
682721
struct TCP_Server_Info *server = tcon->ses->server;
683722
unsigned int xid;
723+
int rc = 0;
684724

685725
xid = get_xid();
686726
if (server->ops->close_getattr)
687-
server->ops->close_getattr(xid, tcon, cifs_file);
727+
rc = server->ops->close_getattr(xid, tcon, cifs_file);
688728
else if (server->ops->close)
689-
server->ops->close(xid, tcon, &cifs_file->fid);
729+
rc = server->ops->close(xid, tcon, &cifs_file->fid);
690730
_free_xid(xid);
731+
732+
if (rc == -EBUSY || rc == -EAGAIN) {
733+
// Server close failed, hence offloading it as an async op
734+
queue_work(serverclose_wq, &cifs_file->serverclose);
735+
serverclose_offloaded = true;
736+
}
691737
}
692738

693739
if (oplock_break_cancelled)
694740
cifs_done_oplock_break(cifsi);
695741

696742
cifs_del_pending_open(&open);
697743

698-
if (offload)
699-
queue_work(fileinfo_put_wq, &cifs_file->put);
700-
else
701-
cifsFileInfo_put_final(cifs_file);
744+
// if serverclose has been offloaded to wq (on failure), it will
745+
// handle offloading put as well. If serverclose not offloaded,
746+
// we need to handle offloading put here.
747+
if (!serverclose_offloaded) {
748+
if (offload)
749+
queue_work(fileinfo_put_wq, &cifs_file->put);
750+
else
751+
cifsFileInfo_put_final(cifs_file);
752+
}
702753
}
703754

704755
int cifs_open(struct inode *inode, struct file *file)

fs/smb/client/smb1ops.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -753,11 +753,11 @@ cifs_set_fid(struct cifsFileInfo *cfile, struct cifs_fid *fid, __u32 oplock)
753753
cinode->can_cache_brlcks = CIFS_CACHE_WRITE(cinode);
754754
}
755755

756-
static void
756+
static int
757757
cifs_close_file(const unsigned int xid, struct cifs_tcon *tcon,
758758
struct cifs_fid *fid)
759759
{
760-
CIFSSMBClose(xid, tcon, fid->netfid);
760+
return CIFSSMBClose(xid, tcon, fid->netfid);
761761
}
762762

763763
static int

fs/smb/client/smb2ops.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1412,14 +1412,14 @@ smb2_set_fid(struct cifsFileInfo *cfile, struct cifs_fid *fid, __u32 oplock)
14121412
memcpy(cfile->fid.create_guid, fid->create_guid, 16);
14131413
}
14141414

1415-
static void
1415+
static int
14161416
smb2_close_file(const unsigned int xid, struct cifs_tcon *tcon,
14171417
struct cifs_fid *fid)
14181418
{
1419-
SMB2_close(xid, tcon, fid->persistent_fid, fid->volatile_fid);
1419+
return SMB2_close(xid, tcon, fid->persistent_fid, fid->volatile_fid);
14201420
}
14211421

1422-
static void
1422+
static int
14231423
smb2_close_getattr(const unsigned int xid, struct cifs_tcon *tcon,
14241424
struct cifsFileInfo *cfile)
14251425
{
@@ -1430,7 +1430,7 @@ smb2_close_getattr(const unsigned int xid, struct cifs_tcon *tcon,
14301430
rc = __SMB2_close(xid, tcon, cfile->fid.persistent_fid,
14311431
cfile->fid.volatile_fid, &file_inf);
14321432
if (rc)
1433-
return;
1433+
return rc;
14341434

14351435
inode = d_inode(cfile->dentry);
14361436

@@ -1459,6 +1459,7 @@ smb2_close_getattr(const unsigned int xid, struct cifs_tcon *tcon,
14591459

14601460
/* End of file and Attributes should not have to be updated on close */
14611461
spin_unlock(&inode->i_lock);
1462+
return rc;
14621463
}
14631464

14641465
static int

fs/smb/client/smb2pdu.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3628,9 +3628,9 @@ __SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
36283628
memcpy(&pbuf->network_open_info,
36293629
&rsp->network_open_info,
36303630
sizeof(pbuf->network_open_info));
3631+
atomic_dec(&tcon->num_remote_opens);
36313632
}
36323633

3633-
atomic_dec(&tcon->num_remote_opens);
36343634
close_exit:
36353635
SMB2_close_free(&rqst);
36363636
free_rsp_buf(resp_buftype, rsp);

0 commit comments

Comments
 (0)