Skip to content

Commit d14bbff

Browse files
committed
smb3: mark compression as CONFIG_EXPERIMENTAL and fix missing compression operation
Move SMB3.1.1 compression code into experimental config option, and fix the compress mount option. Implement unchained LZ77 "plain" compression algorithm as per MS-XCA specification section "2.3 Plain LZ77 Compression Algorithm Details". Signed-off-by: Enzo Matsumiya <[email protected]> Signed-off-by: Steve French <[email protected]>
1 parent 6795dab commit d14bbff

File tree

9 files changed

+685
-4
lines changed

9 files changed

+685
-4
lines changed

fs/smb/client/Kconfig

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,4 +204,18 @@ config CIFS_ROOT
204204

205205
Most people say N here.
206206

207+
config CIFS_COMPRESSION
208+
bool "SMB message compression (Experimental)"
209+
depends on CIFS
210+
default n
211+
help
212+
Enables over-the-wire message compression for SMB 3.1.1
213+
mounts when negotiated with the server.
214+
215+
Only write requests with data size >= PAGE_SIZE will be
216+
compressed to avoid wasting resources.
217+
218+
Say Y here if you want SMB traffic to be compressed.
219+
If unsure, say N.
220+
207221
endif

fs/smb/client/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,5 @@ cifs-$(CONFIG_CIFS_SMB_DIRECT) += smbdirect.o
3333
cifs-$(CONFIG_CIFS_ROOT) += cifsroot.o
3434

3535
cifs-$(CONFIG_CIFS_ALLOW_INSECURE_LEGACY) += smb1ops.o cifssmb.o
36+
37+
cifs-$(CONFIG_CIFS_COMPRESSION) += compress.o compress/lz77.o

fs/smb/client/cifs_debug.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,9 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
349349
seq_printf(m, ",ACL");
350350
#ifdef CONFIG_CIFS_SWN_UPCALL
351351
seq_puts(m, ",WITNESS");
352+
#endif
353+
#ifdef CONFIG_CIFS_COMPRESSION
354+
seq_puts(m, ",COMPRESSION");
352355
#endif
353356
seq_putc(m, '\n');
354357
seq_printf(m, "CIFSMaxBufSize: %d\n", CIFSMaxBufSize);
@@ -475,7 +478,9 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
475478
}
476479

477480
seq_puts(m, "\nCompression: ");
478-
if (!server->compression.requested)
481+
if (!IS_ENABLED(CONFIG_CIFS_COMPRESSION))
482+
seq_puts(m, "no built-in support");
483+
else if (!server->compression.requested)
479484
seq_puts(m, "disabled on mount");
480485
else if (server->compression.enabled)
481486
seq_printf(m, "enabled (%s)", compression_alg_str(server->compression.alg));

fs/smb/client/cifsglob.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -555,7 +555,7 @@ struct smb_version_operations {
555555
bool (*dir_needs_close)(struct cifsFileInfo *);
556556
long (*fallocate)(struct file *, struct cifs_tcon *, int, loff_t,
557557
loff_t);
558-
/* init transform request - used for encryption for now */
558+
/* init transform (compress/encrypt) request */
559559
int (*init_transform_rq)(struct TCP_Server_Info *, int num_rqst,
560560
struct smb_rqst *, struct smb_rqst *);
561561
int (*is_transform_hdr)(void *buf);
@@ -1876,6 +1876,7 @@ static inline bool is_replayable_error(int error)
18761876
#define CIFS_HAS_CREDITS 0x0400 /* already has credits */
18771877
#define CIFS_TRANSFORM_REQ 0x0800 /* transform request before sending */
18781878
#define CIFS_NO_SRV_RSP 0x1000 /* there is no server response */
1879+
#define CIFS_COMPRESS_REQ 0x4000 /* compress request before sending */
18791880

18801881
/* Security Flags: indicate type of session setup needed */
18811882
#define CIFSSEC_MAY_SIGN 0x00001

fs/smb/client/compress.c

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/*
3+
* Copyright (C) 2024, SUSE LLC
4+
*
5+
* Authors: Enzo Matsumiya <[email protected]>
6+
*
7+
* This file implements I/O compression support for SMB2 messages (SMB 3.1.1 only).
8+
* See compress/ for implementation details of each algorithm.
9+
*
10+
* References:
11+
* MS-SMB2 "3.1.4.4 Compressing the Message"
12+
* MS-SMB2 "3.1.5.3 Decompressing the Chained Message"
13+
* MS-XCA - for details of the supported algorithms
14+
*/
15+
#include <linux/slab.h>
16+
#include <linux/kernel.h>
17+
#include <linux/uio.h>
18+
19+
#include "cifsglob.h"
20+
#include "../common/smb2pdu.h"
21+
#include "cifsproto.h"
22+
#include "smb2proto.h"
23+
24+
#include "compress/lz77.h"
25+
#include "compress.h"
26+
27+
int smb_compress(void *buf, const void *data, size_t *len)
28+
{
29+
struct smb2_compression_hdr *hdr;
30+
size_t buf_len, data_len;
31+
int ret;
32+
33+
buf_len = sizeof(struct smb2_write_req);
34+
data_len = *len;
35+
*len = 0;
36+
37+
hdr = buf;
38+
hdr->ProtocolId = SMB2_COMPRESSION_TRANSFORM_ID;
39+
hdr->OriginalCompressedSegmentSize = cpu_to_le32(data_len);
40+
hdr->Offset = cpu_to_le32(buf_len);
41+
hdr->Flags = SMB2_COMPRESSION_FLAG_NONE;
42+
hdr->CompressionAlgorithm = SMB3_COMPRESS_LZ77;
43+
44+
/* XXX: add other algs here as they're implemented */
45+
ret = lz77_compress(data, data_len, buf + SMB_COMPRESS_HDR_LEN + buf_len, &data_len);
46+
if (!ret)
47+
*len = SMB_COMPRESS_HDR_LEN + buf_len + data_len;
48+
49+
return ret;
50+
}

fs/smb/client/compress.h

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/* SPDX-License-Identifier: GPL-2.0-only */
2+
/*
3+
* Copyright (C) 2024, SUSE LLC
4+
*
5+
* Authors: Enzo Matsumiya <[email protected]>
6+
*
7+
* This file implements I/O compression support for SMB2 messages (SMB 3.1.1 only).
8+
* See compress/ for implementation details of each algorithm.
9+
*
10+
* References:
11+
* MS-SMB2 "3.1.4.4 Compressing the Message" - for compression details
12+
* MS-SMB2 "3.1.5.3 Decompressing the Chained Message" - for decompression details
13+
* MS-XCA - for details of the supported algorithms
14+
*/
15+
#ifndef _SMB_COMPRESS_H
16+
#define _SMB_COMPRESS_H
17+
18+
#include <linux/uio.h>
19+
#include <linux/kernel.h>
20+
#include "../common/smb2pdu.h"
21+
#include "cifsglob.h"
22+
23+
/* sizeof(smb2_compression_hdr) - sizeof(OriginalPayloadSize) */
24+
#define SMB_COMPRESS_HDR_LEN 16
25+
/* sizeof(smb2_compression_payload_hdr) - sizeof(OriginalPayloadSize) */
26+
#define SMB_COMPRESS_PAYLOAD_HDR_LEN 8
27+
#define SMB_COMPRESS_MIN_LEN PAGE_SIZE
28+
29+
struct smb_compress_ctx {
30+
struct TCP_Server_Info *server;
31+
struct work_struct work;
32+
struct mid_q_entry *mid;
33+
34+
void *buf; /* compressed data */
35+
void *data; /* uncompressed data */
36+
size_t len;
37+
};
38+
39+
#ifdef CONFIG_CIFS_COMPRESSION
40+
int smb_compress(void *buf, const void *data, size_t *len);
41+
42+
/**
43+
* smb_compress_alg_valid() - Validate a compression algorithm.
44+
* @alg: Compression algorithm to check.
45+
* @valid_none: Conditional check whether NONE algorithm should be
46+
* considered valid or not.
47+
*
48+
* If @alg is SMB3_COMPRESS_NONE, this function returns @valid_none.
49+
*
50+
* Note that 'NONE' (0) compressor type is considered invalid in protocol
51+
* negotiation, as it's never requested to/returned from the server.
52+
*
53+
* Return: true if @alg is valid/supported, false otherwise.
54+
*/
55+
static __always_inline int smb_compress_alg_valid(__le16 alg, bool valid_none)
56+
{
57+
if (alg == SMB3_COMPRESS_NONE)
58+
return valid_none;
59+
60+
if (alg == SMB3_COMPRESS_LZ77 || alg == SMB3_COMPRESS_PATTERN)
61+
return true;
62+
63+
return false;
64+
}
65+
66+
/**
67+
* should_compress() - Determines if a request (write) or the response to a
68+
* request (read) should be compressed.
69+
* @tcon: tcon of the request is being sent to
70+
* @buf: buffer with an SMB2 READ/WRITE request
71+
*
72+
* Return: true iff:
73+
* - compression was successfully negotiated with server
74+
* - server has enabled compression for the share
75+
* - it's a read or write request
76+
* - if write, request length is >= SMB_COMPRESS_MIN_LEN
77+
*
78+
* Return false otherwise.
79+
*/
80+
static __always_inline bool should_compress(const struct cifs_tcon *tcon, const void *buf)
81+
{
82+
const struct smb2_hdr *shdr = buf;
83+
84+
if (!tcon || !tcon->ses || !tcon->ses->server)
85+
return false;
86+
87+
if (!tcon->ses->server->compression.enabled)
88+
return false;
89+
90+
if (!(tcon->share_flags & SMB2_SHAREFLAG_COMPRESS_DATA))
91+
return false;
92+
93+
if (shdr->Command == SMB2_WRITE) {
94+
const struct smb2_write_req *req = buf;
95+
96+
return (req->Length >= SMB_COMPRESS_MIN_LEN);
97+
}
98+
99+
return (shdr->Command == SMB2_READ);
100+
}
101+
/*
102+
* #else !CONFIG_CIFS_COMPRESSION ...
103+
* These routines should not be called when CONFIG_CIFS_COMPRESSION disabled
104+
* #define smb_compress(arg1, arg2, arg3) (-EOPNOTSUPP)
105+
* #define smb_compress_alg_valid(arg1, arg2) (-EOPNOTSUPP)
106+
* #define should_compress(arg1, arg2) (false)
107+
*/
108+
#endif /* !CONFIG_CIFS_COMPRESSION */
109+
#endif /* _SMB_COMPRESS_H */

0 commit comments

Comments
 (0)