[PATCH 05/10] smb: client: compress: enable Pattern_V1 + chained compression
Enzo Matsumiya <[email protected]>
| Newsgroups | org.kernel.vger.linux-cifs |
|---|---|
| Message-ID | <[email protected]> |
Negotiate Pattern_V1 and LZ77 compression algorithms by default (if 'compress' mount option is set). Changes: - drop TCP_Server_Info::compression::requested, rely on ::enabled only - add smb_compress_alloc_size() helper to compute compressed buffer size, to account for Pattern_V1 payloads as well - call smb_compression_compress() with SMB2 header as @uncomp Signed-off-by: Enzo Matsumiya <[email protected]> --- fs/smb/client/cifs_debug.c | 18 ++++++++++------ fs/smb/client/cifsglob.h | 1 - fs/smb/client/compress.c | 36 ++++++++++++++----------------- fs/smb/client/connect.c | 2 +- fs/smb/client/sess.c | 2 +- fs/smb/client/smb2pdu.c | 25 ++++++++++++++------- fs/smb/common/compress/compress.h | 28 ++++++++++++++++++++++++ fs/smb/server/compress.c | 5 +---- 8 files changed, 75 insertions(+), 42 deletions(-) diff --git a/fs/smb/client/cifs_debug.c b/fs/smb/client/cifs_debug.c index 4ed4f55a0bb7..36b4b7345c04 100644 --- a/fs/smb/client/cifs_debug.c +++ b/fs/smb/client/cifs_debug.c @@ -552,14 +552,18 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) } seq_puts(m, "\nCompression: "); - if (!IS_ENABLED(CONFIG_CIFS_COMPRESSION)) + if (!IS_ENABLED(CONFIG_CIFS_COMPRESSION)) { seq_puts(m, "no built-in support"); - else if (!server->compression.requested) - seq_puts(m, "disabled on mount"); - else if (server->compression.enabled) - seq_printf(m, "enabled (%s)", compression_alg_str(server->compression.alg)); - else - seq_puts(m, "disabled (not supported by this server)"); + } else if (!server->compression.enabled) { + seq_puts(m, "disabled"); /* XXX: reason? */ + } else { + seq_printf(m, "enabled, chained: %s, algs: ", + str_yes_no(server->compression.chained)); + + seq_printf(m, "%s ", compression_alg_str(server->compression.alg)); + if (server->compression.pattern) + seq_printf(m, "%s ", compression_alg_str(SMB3_COMPRESS_PATTERN)); + } /* Show negotiated encryption cipher, even if not required */ seq_puts(m, "\nEncryption: "); diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h index 99f9e6dca62b..a08aa7fc84f4 100644 --- a/fs/smb/client/cifsglob.h +++ b/fs/smb/client/cifsglob.h @@ -787,7 +787,6 @@ struct TCP_Server_Info { unsigned int rdma_readwrite_threshold; unsigned int retrans; struct { - bool requested; /* "compress" mount option set*/ bool enabled; /* actually negotiated with server */ bool chained; /* chained transforms were negotiated */ bool pattern; /* Pattern_V1 chained payloads were negotiated */ diff --git a/fs/smb/client/compress.c b/fs/smb/client/compress.c index 7c08e2b843c2..72ac727250f6 100644 --- a/fs/smb/client/compress.c +++ b/fs/smb/client/compress.c @@ -338,8 +338,9 @@ bool should_compress(const struct cifs_tcon *tcon, const struct smb_rqst *rq) int smb_compress(struct TCP_Server_Info *server, struct smb_rqst *rq, compress_send_fn send_fn) { struct iov_iter iter; - u32 slen, dlen; + u32 slen, dlen, shdr_len; void *src, *dst = NULL; + bool use_pattern; int ret; if (!server || !rq || !rq->rq_iov || !rq->rq_iov->iov_base) @@ -380,32 +381,27 @@ int smb_compress(struct TCP_Server_Info *server, struct smb_rqst *rq, compress_s goto err_free; } - dlen = smb_lz77_compressed_alloc_size(slen); + use_pattern = server->compression.pattern; + shdr_len = rq->rq_iov[0].iov_len; + dlen = smb_compress_alloc_size(slen, use_pattern) + shdr_len; dst = kvzalloc(dlen, GFP_KERNEL); if (!dst) { ret = -ENOMEM; goto err_free; } - ret = smb_lz77_compress(src, slen, dst, &dlen); + ret = smb_compression_compress(SMB3_COMPRESS_LZ77, server->compression.chained, use_pattern, + src, slen, dst, &dlen, rq->rq_iov[0].iov_base, shdr_len); if (!ret) { - struct smb2_compression_hdr hdr = { 0 }; - struct smb_rqst comp_rq = { .rq_nvec = 3, }; - struct kvec iov[3]; - - hdr.ProtocolId = SMB2_COMPRESSION_TRANSFORM_ID; - hdr.OriginalCompressedSegmentSize = cpu_to_le32(slen); - hdr.CompressionAlgorithm = SMB3_COMPRESS_LZ77; - hdr.Flags = SMB2_COMPRESSION_FLAG_NONE; - hdr.Offset = cpu_to_le32(rq->rq_iov[0].iov_len); - - iov[0].iov_base = &hdr; - iov[0].iov_len = sizeof(hdr); - iov[1] = rq->rq_iov[0]; - iov[2].iov_base = dst; - iov[2].iov_len = dlen; - - comp_rq.rq_iov = iov; + struct smb_rqst comp_rq = { .rq_nvec = 1, }; + struct kvec iov = { + .iov_base = dst, + .iov_len = dlen, + }; + + iov.iov_base = dst; + iov.iov_len = dlen; + comp_rq.rq_iov = &iov; ret = send_fn(server, 1, &comp_rq); } else if (ret == -EMSGSIZE || dlen >= slen) { diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c index 324452bc0437..dd35abdac334 100644 --- a/fs/smb/client/connect.c +++ b/fs/smb/client/connect.c @@ -1830,7 +1830,7 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx, tcp_ses->channel_sequence_num = 0; /* only tracked for primary channel */ tcp_ses->reconnect_instance = 1; tcp_ses->lstrp = jiffies; - tcp_ses->compression.requested = ctx->compress; + tcp_ses->compression.enabled = ctx->compress; spin_lock_init(&tcp_ses->req_lock); spin_lock_init(&tcp_ses->srv_lock); spin_lock_init(&tcp_ses->mid_queue_lock); diff --git a/fs/smb/client/sess.c b/fs/smb/client/sess.c index de2012cc9cf3..83f4b5d107f6 100644 --- a/fs/smb/client/sess.c +++ b/fs/smb/client/sess.c @@ -552,7 +552,7 @@ cifs_ses_add_channel(struct cifs_ses *ses, ctx->echo_interval = ses->server->echo_interval / HZ; ctx->max_credits = ses->server->max_credits; ctx->min_offload = ses->server->min_offload; - ctx->compress = ses->server->compression.requested; + ctx->compress = ses->server->compression.enabled; ctx->dfs_conn = ses->server->dfs_conn; ctx->ignore_signature = ses->server->ignore_signature; ctx->leaf_fullpath = ses->server->leaf_fullpath; diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index d058584b8f05..41cdc9f5e692 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -785,7 +785,7 @@ assemble_neg_contexts(struct smb2_negotiate_req *req, pneg_ctxt += sizeof(struct smb2_posix_neg_context); neg_context_count++; - if (server->compression.requested) { + if (server->compression.enabled) { build_compression_ctxt((struct smb2_compression_capabilities_context *) pneg_ctxt); ctxt_len = ALIGN(sizeof(struct smb2_compression_capabilities_context), 8); @@ -835,11 +835,6 @@ static void decode_compress_ctx(struct TCP_Server_Info *server, unsigned int len = le16_to_cpu(ctxt->DataLength); unsigned int count, i; - server->compression.enabled = false; - server->compression.chained = false; - server->compression.pattern = false; - server->compression.alg = SMB3_COMPRESS_NONE; - /* * Caller checked that DataLength remains within SMB boundary. We still * need to confirm that one CompressionAlgorithms member is accounted @@ -876,6 +871,12 @@ static void decode_compress_ctx(struct TCP_Server_Info *server, /* * Pattern_V1 cannot appear in an unchained transform even if a broken * peer lists it in the algorithm array. + * + * OTOH, the chained flag might have been negotiated without either/both peers having not + * advertised Pattern_V1 algorithm support. + * + * (which doesn't make much sense, but we'll still have to send compressed payloads with + * the chained flag set in "chained && !pattern_v1" cases) */ server->compression.chained = ctxt->Flags == SMB2_COMPRESSION_CAPABILITIES_FLAG_CHAINED; @@ -1336,11 +1337,19 @@ SMB2_negotiate(const unsigned int xid, } if (server->dialect == SMB311_PROT_ID) { - if (rsp->NegotiateContextCount) + bool compression_enabled = server->compression.enabled; + + /* Reset compression settings in case of server not supporting it */ + memset(&server->compression, 0, sizeof(server->compression)); + + if (rsp->NegotiateContextCount) { rc = smb311_decode_neg_context(rsp, server, rsp_iov.iov_len); - else + if (!rc && compression_enabled && !server->compression.enabled) + cifs_server_dbg(VFS, "Server doesn't support compression, disabling\n"); + } else { cifs_server_dbg(VFS, "Missing expected negotiate contexts\n"); + } } if (server->cipher_type && !rc) diff --git a/fs/smb/common/compress/compress.h b/fs/smb/common/compress/compress.h index 6760862de04c..e1fad498d567 100644 --- a/fs/smb/common/compress/compress.h +++ b/fs/smb/common/compress/compress.h @@ -145,6 +145,34 @@ static __always_inline bool smb_compress_alg_valid(__le16 alg, bool valid_none) return alg == SMB3_COMPRESS_LZ77 || alg == SMB3_COMPRESS_PATTERN; } +/** + * smb_compress_alloc_size() - Compute total allocation size required for compressed (dst) buffer. + * @size: uncompressed size + * @use_pattern: if Pattern_V1 is enabled + * + * For any case: + * - SMB2 compression hdr + * - LZ* payload + * + * If @use_pattern, also account for: + * - 2x payload hdr for Pattern_V1 + * - 2x Pattern_V1 payloads + * - 1x NONE payload hdr + * + * (possible uncompressed leftovers are included in LZ alloc size) + */ +static __always_inline u32 smb_compress_alloc_size(const u32 size, const bool use_pattern) +{ + u32 alloc_size; + + alloc_size = sizeof(struct smb2_compression_hdr) + smb_lz77_compressed_alloc_size(size); + if (use_pattern) + alloc_size += (SMB2_COMPRESSION_PAYLOAD_BASE_LEN * 3) + + (sizeof(struct smb2_compression_pattern_v1) * 2); + + return alloc_size; +} + int smb_compression_decompress(__le16 alg, bool allow_chained, const void *src, u32 slen, void *dst, u32 dlen); int smb_compression_compress(__le16 alg, bool chained, bool allow_pattern, diff --git a/fs/smb/server/compress.c b/fs/smb/server/compress.c index 32027a15f7ab..e5851d603469 100644 --- a/fs/smb/server/compress.c +++ b/fs/smb/server/compress.c @@ -132,10 +132,7 @@ int ksmbd_compress_response(struct ksmbd_work *work) goto out; } - max_dst_len = smb_lz77_compressed_alloc_size(src_len) + - sizeof(struct smb2_compression_hdr) + - 3 * sizeof(struct smb2_compression_payload_hdr) + - 2 * sizeof(struct smb2_compression_pattern_v1); + max_dst_len = smb_compress_alloc_size(src_len, work->conn->compress_pattern); out = kvzalloc(sizeof(__be32) + max_dst_len, KSMBD_DEFAULT_GFP); if (!out) { -- 2.54.0