diff options
author | Steve French <stfrench@microsoft.com> | 2019-09-07 02:09:49 -0400 |
---|---|---|
committer | Steve French <stfrench@microsoft.com> | 2019-09-16 12:43:38 -0400 |
commit | 35cf94a397280b9e27576ac1480f631bdd3e7b70 (patch) | |
tree | eed59267fe903c092e9ec7e7e6e710f861c295de /fs/cifs | |
parent | 3175eb9b577e82b44a25ad2d515ec9418ae06c04 (diff) |
smb3: allow parallelizing decryption of reads
decrypting large reads on encrypted shares can be slow (e.g. adding
multiple milliseconds per-read on non-GCM capable servers or
when mounting with dialects prior to SMB3.1.1) - allow parallelizing
of read decryption by launching worker threads.
Testing to Samba on localhost showed 25% improvement.
Testing to remote server showed very large improvement when
doing more than one 'cp' command was called at one time.
Signed-off-by: Steve French <stfrench@microsoft.com>
Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
Diffstat (limited to 'fs/cifs')
-rw-r--r-- | fs/cifs/cifsfs.c | 17 | ||||
-rw-r--r-- | fs/cifs/cifsglob.h | 1 | ||||
-rw-r--r-- | fs/cifs/smb2ops.c | 83 |
3 files changed, 97 insertions, 4 deletions
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index de90e665ef11..b0ea332af35c 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c | |||
@@ -109,6 +109,7 @@ extern mempool_t *cifs_req_poolp; | |||
109 | extern mempool_t *cifs_mid_poolp; | 109 | extern mempool_t *cifs_mid_poolp; |
110 | 110 | ||
111 | struct workqueue_struct *cifsiod_wq; | 111 | struct workqueue_struct *cifsiod_wq; |
112 | struct workqueue_struct *decrypt_wq; | ||
112 | struct workqueue_struct *cifsoplockd_wq; | 113 | struct workqueue_struct *cifsoplockd_wq; |
113 | __u32 cifs_lock_secret; | 114 | __u32 cifs_lock_secret; |
114 | 115 | ||
@@ -1499,11 +1500,22 @@ init_cifs(void) | |||
1499 | goto out_clean_proc; | 1500 | goto out_clean_proc; |
1500 | } | 1501 | } |
1501 | 1502 | ||
1503 | /* | ||
1504 | * BB Consider setting limit!=0 maybe to min(num_of_cores - 1, 3) so we | ||
1505 | * don't launch too many worker threads | ||
1506 | */ | ||
1507 | decrypt_wq = alloc_workqueue("smb3decryptd", | ||
1508 | WQ_FREEZABLE|WQ_MEM_RECLAIM, 0); | ||
1509 | if (!decrypt_wq) { | ||
1510 | rc = -ENOMEM; | ||
1511 | goto out_destroy_cifsiod_wq; | ||
1512 | } | ||
1513 | |||
1502 | cifsoplockd_wq = alloc_workqueue("cifsoplockd", | 1514 | cifsoplockd_wq = alloc_workqueue("cifsoplockd", |
1503 | WQ_FREEZABLE|WQ_MEM_RECLAIM, 0); | 1515 | WQ_FREEZABLE|WQ_MEM_RECLAIM, 0); |
1504 | if (!cifsoplockd_wq) { | 1516 | if (!cifsoplockd_wq) { |
1505 | rc = -ENOMEM; | 1517 | rc = -ENOMEM; |
1506 | goto out_destroy_cifsiod_wq; | 1518 | goto out_destroy_decrypt_wq; |
1507 | } | 1519 | } |
1508 | 1520 | ||
1509 | rc = cifs_fscache_register(); | 1521 | rc = cifs_fscache_register(); |
@@ -1569,6 +1581,8 @@ out_unreg_fscache: | |||
1569 | cifs_fscache_unregister(); | 1581 | cifs_fscache_unregister(); |
1570 | out_destroy_cifsoplockd_wq: | 1582 | out_destroy_cifsoplockd_wq: |
1571 | destroy_workqueue(cifsoplockd_wq); | 1583 | destroy_workqueue(cifsoplockd_wq); |
1584 | out_destroy_decrypt_wq: | ||
1585 | destroy_workqueue(decrypt_wq); | ||
1572 | out_destroy_cifsiod_wq: | 1586 | out_destroy_cifsiod_wq: |
1573 | destroy_workqueue(cifsiod_wq); | 1587 | destroy_workqueue(cifsiod_wq); |
1574 | out_clean_proc: | 1588 | out_clean_proc: |
@@ -1595,6 +1609,7 @@ exit_cifs(void) | |||
1595 | cifs_destroy_inodecache(); | 1609 | cifs_destroy_inodecache(); |
1596 | cifs_fscache_unregister(); | 1610 | cifs_fscache_unregister(); |
1597 | destroy_workqueue(cifsoplockd_wq); | 1611 | destroy_workqueue(cifsoplockd_wq); |
1612 | destroy_workqueue(decrypt_wq); | ||
1598 | destroy_workqueue(cifsiod_wq); | 1613 | destroy_workqueue(cifsiod_wq); |
1599 | cifs_proc_clean(); | 1614 | cifs_proc_clean(); |
1600 | } | 1615 | } |
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 1f53dee211d8..d66106ac031a 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h | |||
@@ -1892,6 +1892,7 @@ void cifs_queue_oplock_break(struct cifsFileInfo *cfile); | |||
1892 | 1892 | ||
1893 | extern const struct slow_work_ops cifs_oplock_break_ops; | 1893 | extern const struct slow_work_ops cifs_oplock_break_ops; |
1894 | extern struct workqueue_struct *cifsiod_wq; | 1894 | extern struct workqueue_struct *cifsiod_wq; |
1895 | extern struct workqueue_struct *decrypt_wq; | ||
1895 | extern struct workqueue_struct *cifsoplockd_wq; | 1896 | extern struct workqueue_struct *cifsoplockd_wq; |
1896 | extern __u32 cifs_lock_secret; | 1897 | extern __u32 cifs_lock_secret; |
1897 | 1898 | ||
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 83b02d74d48e..c74284484947 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c | |||
@@ -4017,8 +4017,58 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid, | |||
4017 | return length; | 4017 | return length; |
4018 | } | 4018 | } |
4019 | 4019 | ||
4020 | struct smb2_decrypt_work { | ||
4021 | struct work_struct decrypt; | ||
4022 | struct TCP_Server_Info *server; | ||
4023 | struct page **ppages; | ||
4024 | char *buf; | ||
4025 | unsigned int npages; | ||
4026 | unsigned int len; | ||
4027 | }; | ||
4028 | |||
4029 | |||
4030 | static void smb2_decrypt_offload(struct work_struct *work) | ||
4031 | { | ||
4032 | struct smb2_decrypt_work *dw = container_of(work, | ||
4033 | struct smb2_decrypt_work, decrypt); | ||
4034 | int i, rc; | ||
4035 | struct mid_q_entry *mid; | ||
4036 | |||
4037 | rc = decrypt_raw_data(dw->server, dw->buf, dw->server->vals->read_rsp_size, | ||
4038 | dw->ppages, dw->npages, dw->len); | ||
4039 | if (rc) { | ||
4040 | cifs_dbg(VFS, "error decrypting rc=%d\n", rc); | ||
4041 | goto free_pages; | ||
4042 | } | ||
4043 | |||
4044 | mid = smb2_find_mid(dw->server, dw->buf); | ||
4045 | if (mid == NULL) | ||
4046 | cifs_dbg(FYI, "mid not found\n"); | ||
4047 | else { | ||
4048 | mid->decrypted = true; | ||
4049 | rc = handle_read_data(dw->server, mid, dw->buf, | ||
4050 | dw->server->vals->read_rsp_size, | ||
4051 | dw->ppages, dw->npages, dw->len); | ||
4052 | } | ||
4053 | |||
4054 | dw->server->lstrp = jiffies; | ||
4055 | |||
4056 | mid->callback(mid); | ||
4057 | |||
4058 | cifs_mid_q_entry_release(mid); | ||
4059 | |||
4060 | free_pages: | ||
4061 | for (i = dw->npages-1; i >= 0; i--) | ||
4062 | put_page(dw->ppages[i]); | ||
4063 | |||
4064 | kfree(dw->ppages); | ||
4065 | cifs_small_buf_release(dw->buf); | ||
4066 | } | ||
4067 | |||
4068 | |||
4020 | static int | 4069 | static int |
4021 | receive_encrypted_read(struct TCP_Server_Info *server, struct mid_q_entry **mid) | 4070 | receive_encrypted_read(struct TCP_Server_Info *server, struct mid_q_entry **mid, |
4071 | int *num_mids) | ||
4022 | { | 4072 | { |
4023 | char *buf = server->smallbuf; | 4073 | char *buf = server->smallbuf; |
4024 | struct smb2_transform_hdr *tr_hdr = (struct smb2_transform_hdr *)buf; | 4074 | struct smb2_transform_hdr *tr_hdr = (struct smb2_transform_hdr *)buf; |
@@ -4028,7 +4078,9 @@ receive_encrypted_read(struct TCP_Server_Info *server, struct mid_q_entry **mid) | |||
4028 | unsigned int buflen = server->pdu_size; | 4078 | unsigned int buflen = server->pdu_size; |
4029 | int rc; | 4079 | int rc; |
4030 | int i = 0; | 4080 | int i = 0; |
4081 | struct smb2_decrypt_work *dw; | ||
4031 | 4082 | ||
4083 | *num_mids = 1; | ||
4032 | len = min_t(unsigned int, buflen, server->vals->read_rsp_size + | 4084 | len = min_t(unsigned int, buflen, server->vals->read_rsp_size + |
4033 | sizeof(struct smb2_transform_hdr)) - HEADER_SIZE(server) + 1; | 4085 | sizeof(struct smb2_transform_hdr)) - HEADER_SIZE(server) + 1; |
4034 | 4086 | ||
@@ -4064,6 +4116,32 @@ receive_encrypted_read(struct TCP_Server_Info *server, struct mid_q_entry **mid) | |||
4064 | if (rc) | 4116 | if (rc) |
4065 | goto free_pages; | 4117 | goto free_pages; |
4066 | 4118 | ||
4119 | /* | ||
4120 | * For large reads, offload to different thread for better performance, | ||
4121 | * use more cores decrypting which can be expensive | ||
4122 | */ | ||
4123 | |||
4124 | /* TODO: make the size limit to enable decrypt offload configurable */ | ||
4125 | if (server->pdu_size > (512 * 1024)) { | ||
4126 | dw = kmalloc(sizeof(struct smb2_decrypt_work), GFP_KERNEL); | ||
4127 | if (dw == NULL) | ||
4128 | goto non_offloaded_decrypt; | ||
4129 | |||
4130 | dw->buf = server->smallbuf; | ||
4131 | server->smallbuf = (char *)cifs_small_buf_get(); | ||
4132 | |||
4133 | INIT_WORK(&dw->decrypt, smb2_decrypt_offload); | ||
4134 | |||
4135 | dw->npages = npages; | ||
4136 | dw->server = server; | ||
4137 | dw->ppages = pages; | ||
4138 | dw->len = len; | ||
4139 | queue_work(cifsiod_wq, &dw->decrypt); | ||
4140 | *num_mids = 0; /* worker thread takes care of finding mid */ | ||
4141 | return -1; | ||
4142 | } | ||
4143 | |||
4144 | non_offloaded_decrypt: | ||
4067 | rc = decrypt_raw_data(server, buf, server->vals->read_rsp_size, | 4145 | rc = decrypt_raw_data(server, buf, server->vals->read_rsp_size, |
4068 | pages, npages, len); | 4146 | pages, npages, len); |
4069 | if (rc) | 4147 | if (rc) |
@@ -4210,8 +4288,7 @@ smb3_receive_transform(struct TCP_Server_Info *server, | |||
4210 | 4288 | ||
4211 | /* TODO: add support for compounds containing READ. */ | 4289 | /* TODO: add support for compounds containing READ. */ |
4212 | if (pdu_length > CIFSMaxBufSize + MAX_HEADER_SIZE(server)) { | 4290 | if (pdu_length > CIFSMaxBufSize + MAX_HEADER_SIZE(server)) { |
4213 | *num_mids = 1; | 4291 | return receive_encrypted_read(server, &mids[0], num_mids); |
4214 | return receive_encrypted_read(server, &mids[0]); | ||
4215 | } | 4292 | } |
4216 | 4293 | ||
4217 | return receive_encrypted_standard(server, mids, bufs, num_mids); | 4294 | return receive_encrypted_standard(server, mids, bufs, num_mids); |