diff options
author | Jeff Layton <jlayton@redhat.com> | 2010-10-06 19:51:12 -0400 |
---|---|---|
committer | Steve French <sfrench@us.ibm.com> | 2010-10-07 23:31:21 -0400 |
commit | 2de970ff69bbcc5a4b7440df669a595b2b1acd73 (patch) | |
tree | ec710893fcd1aa6313a3bbe2eafb3f463b364afc /fs/cifs | |
parent | 3aa1c8c2900065a51268430ab48a1b42fdfe5b45 (diff) |
cifs: implement recurring workqueue job to prune old tcons
Create a workqueue job that cleans out unused tlinks. For now, it uses
a hardcoded expire time of 10 minutes. When it's done, the work rearms
itself. On umount, the work is cancelled before tearing down the tlink
tree.
Signed-off-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>
Diffstat (limited to 'fs/cifs')
-rw-r--r-- | fs/cifs/cifs_fs_sb.h | 1 | ||||
-rw-r--r-- | fs/cifs/connect.c | 58 |
2 files changed, 58 insertions, 1 deletions
diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h index 5ce57bdf1865..586ee3d527d2 100644 --- a/fs/cifs/cifs_fs_sb.h +++ b/fs/cifs/cifs_fs_sb.h | |||
@@ -59,5 +59,6 @@ struct cifs_sb_info { | |||
59 | char *mountdata; /* mount options received at mount time */ | 59 | char *mountdata; /* mount options received at mount time */ |
60 | #endif | 60 | #endif |
61 | struct backing_dev_info bdi; | 61 | struct backing_dev_info bdi; |
62 | struct delayed_work prune_tlinks; | ||
62 | }; | 63 | }; |
63 | #endif /* _CIFS_FS_SB_H */ | 64 | #endif /* _CIFS_FS_SB_H */ |
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index e65f72d1f23b..1092e9e839c2 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c | |||
@@ -110,11 +110,13 @@ struct smb_vol { | |||
110 | struct nls_table *local_nls; | 110 | struct nls_table *local_nls; |
111 | }; | 111 | }; |
112 | 112 | ||
113 | /* FIXME: should these be tunable? */ | ||
113 | #define TLINK_ERROR_EXPIRE (1 * HZ) | 114 | #define TLINK_ERROR_EXPIRE (1 * HZ) |
114 | 115 | #define TLINK_IDLE_EXPIRE (600 * HZ) | |
115 | 116 | ||
116 | static int ipv4_connect(struct TCP_Server_Info *server); | 117 | static int ipv4_connect(struct TCP_Server_Info *server); |
117 | static int ipv6_connect(struct TCP_Server_Info *server); | 118 | static int ipv6_connect(struct TCP_Server_Info *server); |
119 | static void cifs_prune_tlinks(struct work_struct *work); | ||
118 | 120 | ||
119 | /* | 121 | /* |
120 | * cifs tcp session reconnection | 122 | * cifs tcp session reconnection |
@@ -2494,6 +2496,8 @@ convert_delimiter(char *path, char delim) | |||
2494 | static void setup_cifs_sb(struct smb_vol *pvolume_info, | 2496 | static void setup_cifs_sb(struct smb_vol *pvolume_info, |
2495 | struct cifs_sb_info *cifs_sb) | 2497 | struct cifs_sb_info *cifs_sb) |
2496 | { | 2498 | { |
2499 | INIT_DELAYED_WORK(&cifs_sb->prune_tlinks, cifs_prune_tlinks); | ||
2500 | |||
2497 | if (pvolume_info->rsize > CIFSMaxBufSize) { | 2501 | if (pvolume_info->rsize > CIFSMaxBufSize) { |
2498 | cERROR(1, "rsize %d too large, using MaxBufSize", | 2502 | cERROR(1, "rsize %d too large, using MaxBufSize", |
2499 | pvolume_info->rsize); | 2503 | pvolume_info->rsize); |
@@ -2899,6 +2903,9 @@ remote_path_check: | |||
2899 | spin_unlock(&cifs_sb->tlink_tree_lock); | 2903 | spin_unlock(&cifs_sb->tlink_tree_lock); |
2900 | radix_tree_preload_end(); | 2904 | radix_tree_preload_end(); |
2901 | 2905 | ||
2906 | queue_delayed_work(system_nrt_wq, &cifs_sb->prune_tlinks, | ||
2907 | TLINK_IDLE_EXPIRE); | ||
2908 | |||
2902 | mount_fail_check: | 2909 | mount_fail_check: |
2903 | /* on error free sesinfo and tcon struct if needed */ | 2910 | /* on error free sesinfo and tcon struct if needed */ |
2904 | if (rc) { | 2911 | if (rc) { |
@@ -3090,6 +3097,8 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb) | |||
3090 | struct tcon_link *tlink[8]; | 3097 | struct tcon_link *tlink[8]; |
3091 | unsigned long index = 0; | 3098 | unsigned long index = 0; |
3092 | 3099 | ||
3100 | cancel_delayed_work_sync(&cifs_sb->prune_tlinks); | ||
3101 | |||
3093 | do { | 3102 | do { |
3094 | spin_lock(&cifs_sb->tlink_tree_lock); | 3103 | spin_lock(&cifs_sb->tlink_tree_lock); |
3095 | ret = radix_tree_gang_lookup(&cifs_sb->tlink_tree, | 3104 | ret = radix_tree_gang_lookup(&cifs_sb->tlink_tree, |
@@ -3363,3 +3372,50 @@ wait_for_construction: | |||
3363 | 3372 | ||
3364 | return tlink; | 3373 | return tlink; |
3365 | } | 3374 | } |
3375 | |||
3376 | /* | ||
3377 | * periodic workqueue job that scans tcon_tree for a superblock and closes | ||
3378 | * out tcons. | ||
3379 | */ | ||
3380 | static void | ||
3381 | cifs_prune_tlinks(struct work_struct *work) | ||
3382 | { | ||
3383 | struct cifs_sb_info *cifs_sb = container_of(work, struct cifs_sb_info, | ||
3384 | prune_tlinks.work); | ||
3385 | struct tcon_link *tlink[8]; | ||
3386 | unsigned long now = jiffies; | ||
3387 | unsigned long index = 0; | ||
3388 | int i, ret; | ||
3389 | |||
3390 | do { | ||
3391 | spin_lock(&cifs_sb->tlink_tree_lock); | ||
3392 | ret = radix_tree_gang_lookup(&cifs_sb->tlink_tree, | ||
3393 | (void **)tlink, index, | ||
3394 | ARRAY_SIZE(tlink)); | ||
3395 | /* increment index for next pass */ | ||
3396 | if (ret > 0) | ||
3397 | index = tlink[ret - 1]->tl_index + 1; | ||
3398 | for (i = 0; i < ret; i++) { | ||
3399 | if (test_bit(TCON_LINK_MASTER, &tlink[i]->tl_flags) || | ||
3400 | atomic_read(&tlink[i]->tl_count) != 0 || | ||
3401 | time_after(tlink[i]->tl_time + TLINK_IDLE_EXPIRE, | ||
3402 | now)) { | ||
3403 | tlink[i] = NULL; | ||
3404 | continue; | ||
3405 | } | ||
3406 | cifs_get_tlink(tlink[i]); | ||
3407 | clear_bit(TCON_LINK_IN_TREE, &tlink[i]->tl_flags); | ||
3408 | radix_tree_delete(&cifs_sb->tlink_tree, | ||
3409 | tlink[i]->tl_index); | ||
3410 | } | ||
3411 | spin_unlock(&cifs_sb->tlink_tree_lock); | ||
3412 | |||
3413 | for (i = 0; i < ret; i++) { | ||
3414 | if (tlink[i] != NULL) | ||
3415 | cifs_put_tlink(tlink[i]); | ||
3416 | } | ||
3417 | } while (ret != 0); | ||
3418 | |||
3419 | queue_delayed_work(system_nrt_wq, &cifs_sb->prune_tlinks, | ||
3420 | TLINK_IDLE_EXPIRE); | ||
3421 | } | ||