2018-11-27 16:24:04

by Dave Rodgman

[permalink] [raw]
Subject: [PATCH v2 0/7] lib/lzo: performance improvements

This patch series introduces performance improvements for lzo.

The previous version of this patchset is here:
https://lkml.org/lkml/2018/11/21/625

This version tidies up the ifdefs as per Christoph's comment (although
certainly more could be done, this is at least a bit more consistent
with normal kernel coding style).

On 23/11/2018 2:12 am, Sergey Senozhatsky wrote:

>> The graph below shows the weighted round-trip throughput of lzo, lz4 and
>> lzo-rle, for randomly generated 4k chunks of data with varying levels of
>> entropy. (To calculate weighted round-trip throughput, compression performance
>> is emphasised to reflect the fact that zram does around 2.25x more compression
>> than decompression.
>
> Right. The number is data dependent. Not all swapped out pages can be
> compressed; compressed pages that end up being >= zs_huge_class_size() are
> considered incompressible and stored as it.
>
> I'd say that on my setups around 50-60% of pages are incompressible.

So, just to give a bit more detail: the test setup was a Samsung
Chromebook Pro, cycling through 80 tabs in Chrome. With lzo-rle, only
5% of pages increased in size, and 90% of pages compress to 75% of
original size (or better). Mean compression ratio was 41%. Importantly
for lzo-rle, there are a lot of low-entropy pages where it can do well:
in total about 20% of the data is zeros forming part of a run of 4 or
more bytes.

As a quick summary of the impact of these patches on bigger chunks of
data, I've compared the performance of four different variants of lzo
on two large (~40 MB) files. The numbers show round-trip throughput
in MB/s:

Variant | Low-entropy | High-entropy
Current lzo | 242 | 157
Arm opts | 290 | 159
RLE | 876 | 151
Arm opts + RLE | 1150 | 181

So both the Arm optimisations (8,16-byte copy & CTZ patches), and the
RLE implementation make a significant contribution to the overall
performance uplift.



2018-11-27 16:21:56

by Dave Rodgman

[permalink] [raw]
Subject: [PATCH 7/7] lib/lzo: separate lzo-rle from lzo

To prevent any issues with persistent data, separate lzo-rle
from lzo so that it is treated as a separate algorithm, and
lzo is still available.

Use lzo-rle as the default algorithm for
zram.

Signed-off-by: Dave Rodgman <[email protected]>
---
Documentation/lzo.txt | 12 ++-
crypto/Makefile | 2 +-
crypto/lzo-rle.c | 175 ++++++++++++++++++++++++++++++++++++++++++
crypto/tcrypt.c | 4 +-
drivers/block/zram/zcomp.c | 1 +
drivers/block/zram/zram_drv.c | 2 +-
include/linux/lzo.h | 4 +
lib/lzo/lzo1x_compress.c | 42 +++++++---
lib/lzo/lzodefs.h | 3 +-
9 files changed, 227 insertions(+), 18 deletions(-)
create mode 100644 crypto/lzo-rle.c

diff --git a/Documentation/lzo.txt b/Documentation/lzo.txt
index 306c60344ca7..f79934225d8d 100644
--- a/Documentation/lzo.txt
+++ b/Documentation/lzo.txt
@@ -88,6 +88,10 @@ length encoding. This improves speed for data with many zeros, which is a
common case for zram. This modifies the bitstream in a backwards compatible way
(v1 can correctly decompress v0 compressed data, but v0 cannot read v1 data).

+For maximum compatibility, both versions are available under different names
+(lzo and lzo-rle). Differences in the encoding are noted in this document with
+e.g.: version 1 only.
+
Byte sequences
==============

@@ -99,8 +103,8 @@ Byte sequences
invalid at this place.

17 : bitstream version. If the first byte is 17, the next byte
- gives the bitstream version. If the first byte is not 17,
- the bitstream version is 0.
+ gives the bitstream version (version 1 only). If the first byte
+ is not 17, the bitstream version is 0.

18..21 : copy 0..3 literals
state = (byte - 17) = 0..3 [ copy <state> literals ]
@@ -154,8 +158,8 @@ Byte sequences
state = S (copy S literals after this block)
End of stream is reached if distance == 16384

- In version 1, this instruction is also used to encode a run of zeros if
- distance = 0xbfff, i.e. H = 1 and the D bits are all 1.
+ In version 1 only, this instruction is also used to encode a run of
+ zeros if distance = 0xbfff, i.e. H = 1 and the D bits are all 1.
In this case, it is followed by a fourth byte, X.
run length = ((X << 3) | (0 0 0 0 0 L L L)) + 4.

diff --git a/crypto/Makefile b/crypto/Makefile
index 5e789dc2d4fd..23491b70e601 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -127,7 +127,7 @@ obj-$(CONFIG_CRYPTO_CRC32C) += crc32c_generic.o
obj-$(CONFIG_CRYPTO_CRC32) += crc32_generic.o
obj-$(CONFIG_CRYPTO_CRCT10DIF) += crct10dif_common.o crct10dif_generic.o
obj-$(CONFIG_CRYPTO_AUTHENC) += authenc.o authencesn.o
-obj-$(CONFIG_CRYPTO_LZO) += lzo.o
+obj-$(CONFIG_CRYPTO_LZO) += lzo.o lzo-rle.o
obj-$(CONFIG_CRYPTO_LZ4) += lz4.o
obj-$(CONFIG_CRYPTO_LZ4HC) += lz4hc.o
obj-$(CONFIG_CRYPTO_842) += 842.o
diff --git a/crypto/lzo-rle.c b/crypto/lzo-rle.c
new file mode 100644
index 000000000000..ea9c75b1db49
--- /dev/null
+++ b/crypto/lzo-rle.c
@@ -0,0 +1,175 @@
+/*
+ * Cryptographic API.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/crypto.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
+#include <linux/lzo.h>
+#include <crypto/internal/scompress.h>
+
+struct lzorle_ctx {
+ void *lzorle_comp_mem;
+};
+
+static void *lzorle_alloc_ctx(struct crypto_scomp *tfm)
+{
+ void *ctx;
+
+ ctx = kvmalloc(LZO1X_MEM_COMPRESS, GFP_KERNEL);
+ if (!ctx)
+ return ERR_PTR(-ENOMEM);
+
+ return ctx;
+}
+
+static int lzorle_init(struct crypto_tfm *tfm)
+{
+ struct lzorle_ctx *ctx = crypto_tfm_ctx(tfm);
+
+ ctx->lzorle_comp_mem = lzorle_alloc_ctx(NULL);
+ if (IS_ERR(ctx->lzorle_comp_mem))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void lzorle_free_ctx(struct crypto_scomp *tfm, void *ctx)
+{
+ kvfree(ctx);
+}
+
+static void lzorle_exit(struct crypto_tfm *tfm)
+{
+ struct lzorle_ctx *ctx = crypto_tfm_ctx(tfm);
+
+ lzorle_free_ctx(NULL, ctx->lzorle_comp_mem);
+}
+
+static int __lzorle_compress(const u8 *src, unsigned int slen,
+ u8 *dst, unsigned int *dlen, void *ctx)
+{
+ size_t tmp_len = *dlen; /* size_t(ulong) <-> uint on 64 bit */
+ int err;
+
+ err = lzorle1x_1_compress(src, slen, dst, &tmp_len, ctx);
+
+ if (err != LZO_E_OK)
+ return -EINVAL;
+
+ *dlen = tmp_len;
+ return 0;
+}
+
+static int lzorle_compress(struct crypto_tfm *tfm, const u8 *src,
+ unsigned int slen, u8 *dst, unsigned int *dlen)
+{
+ struct lzorle_ctx *ctx = crypto_tfm_ctx(tfm);
+
+ return __lzorle_compress(src, slen, dst, dlen, ctx->lzorle_comp_mem);
+}
+
+static int lzorle_scompress(struct crypto_scomp *tfm, const u8 *src,
+ unsigned int slen, u8 *dst, unsigned int *dlen,
+ void *ctx)
+{
+ return __lzorle_compress(src, slen, dst, dlen, ctx);
+}
+
+static int __lzorle_decompress(const u8 *src, unsigned int slen,
+ u8 *dst, unsigned int *dlen)
+{
+ int err;
+ size_t tmp_len = *dlen; /* size_t(ulong) <-> uint on 64 bit */
+
+ err = lzo1x_decompress_safe(src, slen, dst, &tmp_len);
+
+ if (err != LZO_E_OK)
+ return -EINVAL;
+
+ *dlen = tmp_len;
+ return 0;
+}
+
+static int lzorle_decompress(struct crypto_tfm *tfm, const u8 *src,
+ unsigned int slen, u8 *dst, unsigned int *dlen)
+{
+ return __lzorle_decompress(src, slen, dst, dlen);
+}
+
+static int lzorle_sdecompress(struct crypto_scomp *tfm, const u8 *src,
+ unsigned int slen, u8 *dst, unsigned int *dlen,
+ void *ctx)
+{
+ return __lzorle_decompress(src, slen, dst, dlen);
+}
+
+static struct crypto_alg alg = {
+ .cra_name = "lzo-rle",
+ .cra_flags = CRYPTO_ALG_TYPE_COMPRESS,
+ .cra_ctxsize = sizeof(struct lzorle_ctx),
+ .cra_module = THIS_MODULE,
+ .cra_init = lzorle_init,
+ .cra_exit = lzorle_exit,
+ .cra_u = { .compress = {
+ .coa_compress = lzorle_compress,
+ .coa_decompress = lzorle_decompress } }
+};
+
+static struct scomp_alg scomp = {
+ .alloc_ctx = lzorle_alloc_ctx,
+ .free_ctx = lzorle_free_ctx,
+ .compress = lzorle_scompress,
+ .decompress = lzorle_sdecompress,
+ .base = {
+ .cra_name = "lzo-rle",
+ .cra_driver_name = "lzo-rle-scomp",
+ .cra_module = THIS_MODULE,
+ }
+};
+
+static int __init lzorle_mod_init(void)
+{
+ int ret;
+
+ ret = crypto_register_alg(&alg);
+ if (ret)
+ return ret;
+
+ ret = crypto_register_scomp(&scomp);
+ if (ret) {
+ crypto_unregister_alg(&alg);
+ return ret;
+ }
+
+ return ret;
+}
+
+static void __exit lzorle_mod_fini(void)
+{
+ crypto_unregister_alg(&alg);
+ crypto_unregister_scomp(&scomp);
+}
+
+module_init(lzorle_mod_init);
+module_exit(lzorle_mod_fini);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("LZO-RLE Compression Algorithm");
+MODULE_ALIAS_CRYPTO("lzo-rle");
diff --git a/crypto/tcrypt.c b/crypto/tcrypt.c
index 0590a9204562..c1c56c9771cf 100644
--- a/crypto/tcrypt.c
+++ b/crypto/tcrypt.c
@@ -76,8 +76,8 @@ static char *check[] = {
"cast6", "arc4", "michael_mic", "deflate", "crc32c", "tea", "xtea",
"khazad", "wp512", "wp384", "wp256", "tnepres", "xeta", "fcrypt",
"camellia", "seed", "salsa20", "rmd128", "rmd160", "rmd256", "rmd320",
- "lzo", "cts", "sha3-224", "sha3-256", "sha3-384", "sha3-512",
- "streebog256", "streebog512",
+ "lzo", "lzo-rle", "cts", "sha3-224", "sha3-256", "sha3-384",
+ "sha3-512", "streebog256", "streebog512",
NULL
};

diff --git a/drivers/block/zram/zcomp.c b/drivers/block/zram/zcomp.c
index 4ed0a78fdc09..4d9a38890965 100644
--- a/drivers/block/zram/zcomp.c
+++ b/drivers/block/zram/zcomp.c
@@ -20,6 +20,7 @@

static const char * const backends[] = {
"lzo",
+ "lzo-rle",
#if IS_ENABLED(CONFIG_CRYPTO_LZ4)
"lz4",
#endif
diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c
index 4879595200e1..984072868422 100644
--- a/drivers/block/zram/zram_drv.c
+++ b/drivers/block/zram/zram_drv.c
@@ -41,7 +41,7 @@ static DEFINE_IDR(zram_index_idr);
static DEFINE_MUTEX(zram_index_mutex);

static int zram_major;
-static const char *default_compressor = "lzo";
+static const char *default_compressor = "lzo-rle";

/* Module params (documentation at end) */
static unsigned int num_devices = 1;
diff --git a/include/linux/lzo.h b/include/linux/lzo.h
index 547a86c71e1b..e95c7d1092b2 100644
--- a/include/linux/lzo.h
+++ b/include/linux/lzo.h
@@ -24,6 +24,10 @@
int lzo1x_1_compress(const unsigned char *src, size_t src_len,
unsigned char *dst, size_t *dst_len, void *wrkmem);

+/* This requires 'wrkmem' of size LZO1X_1_MEM_COMPRESS */
+int lzorle1x_1_compress(const unsigned char *src, size_t src_len,
+ unsigned char *dst, size_t *dst_len, void *wrkmem);
+
/* safe decompression with overrun testing */
int lzo1x_decompress_safe(const unsigned char *src, size_t src_len,
unsigned char *dst, size_t *dst_len);
diff --git a/lib/lzo/lzo1x_compress.c b/lib/lzo/lzo1x_compress.c
index fa8d4ff38531..1a1cd5e84391 100644
--- a/lib/lzo/lzo1x_compress.c
+++ b/lib/lzo/lzo1x_compress.c
@@ -20,7 +20,8 @@
static noinline size_t
lzo1x_1_do_compress(const unsigned char *in, size_t in_len,
unsigned char *out, size_t *out_len,
- size_t ti, void *wrkmem, signed char *state_offset)
+ size_t ti, void *wrkmem, signed char *state_offset,
+ const unsigned char bitstream_version)
{
const unsigned char *ip;
unsigned char *op;
@@ -46,7 +47,7 @@ lzo1x_1_do_compress(const unsigned char *in, size_t in_len,
break;
dv = get_unaligned_le32(ip);

- if (dv == 0) {
+ if (dv == 0 && bitstream_version) {
const unsigned char *ir = ip + 4;
const unsigned char *limit = ip_end
< (ip + MAX_ZERO_RUN_LENGTH + 1)
@@ -282,30 +283,36 @@ lzo1x_1_do_compress(const unsigned char *in, size_t in_len,
return in_end - (ii - ti);
}

-int lzo1x_1_compress(const unsigned char *in, size_t in_len,
+int lzogeneric1x_1_compress(const unsigned char *in, size_t in_len,
unsigned char *out, size_t *out_len,
- void *wrkmem)
+ void *wrkmem, const unsigned char bitstream_version)
{
const unsigned char *ip = in;
unsigned char *op = out;
size_t l = in_len;
size_t t = 0;
signed char state_offset = -2;
+ unsigned int m4_max_offset;

// LZO v0 will never write 17 as first byte,
// so this is used to version the bitstream
- *op++ = 17;
- *op++ = LZO_VERSION;
+ if (bitstream_version > 0) {
+ *op++ = 17;
+ *op++ = bitstream_version;
+ m4_max_offset = M4_MAX_OFFSET_V1;
+ } else {
+ m4_max_offset = M4_MAX_OFFSET_V0;
+ }

while (l > 20) {
- size_t ll = l <= (M4_MAX_OFFSET + 1) ? l : (M4_MAX_OFFSET + 1);
+ size_t ll = l <= (m4_max_offset + 1) ? l : (m4_max_offset + 1);
uintptr_t ll_end = (uintptr_t) ip + ll;
if ((ll_end + ((t + ll) >> 5)) <= ll_end)
break;
BUILD_BUG_ON(D_SIZE * sizeof(lzo_dict_t) > LZO1X_1_MEM_COMPRESS);
memset(wrkmem, 0, D_SIZE * sizeof(lzo_dict_t));
- t = lzo1x_1_do_compress(ip, ll, op, out_len,
- t, wrkmem, &state_offset);
+ t = lzo1x_1_do_compress(ip, ll, op, out_len, t, wrkmem,
+ &state_offset, bitstream_version);
ip += ll;
op += *out_len;
l -= ll;
@@ -348,7 +355,24 @@ int lzo1x_1_compress(const unsigned char *in, size_t in_len,
*out_len = op - out;
return LZO_E_OK;
}
+
+int lzo1x_1_compress(const unsigned char *in, size_t in_len,
+ unsigned char *out, size_t *out_len,
+ void *wrkmem)
+{
+ return lzogeneric1x_1_compress(in, in_len, out, out_len, wrkmem, 0);
+}
+
+int lzorle1x_1_compress(const unsigned char *in, size_t in_len,
+ unsigned char *out, size_t *out_len,
+ void *wrkmem)
+{
+ return lzogeneric1x_1_compress(in, in_len, out, out_len,
+ wrkmem, LZO_VERSION);
+}
+
EXPORT_SYMBOL_GPL(lzo1x_1_compress);
+EXPORT_SYMBOL_GPL(lzorle1x_1_compress);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("LZO1X-1 Compressor");
diff --git a/lib/lzo/lzodefs.h b/lib/lzo/lzodefs.h
index 682359058b3c..c0657441a35d 100644
--- a/lib/lzo/lzodefs.h
+++ b/lib/lzo/lzodefs.h
@@ -52,7 +52,8 @@
#define M1_MAX_OFFSET 0x0400
#define M2_MAX_OFFSET 0x0800
#define M3_MAX_OFFSET 0x4000
-#define M4_MAX_OFFSET 0xbffe
+#define M4_MAX_OFFSET_V0 0xbfff
+#define M4_MAX_OFFSET_V1 0xbffe

#define M1_MIN_LEN 2
#define M1_MAX_LEN 2
--
2.16.4


2018-11-27 16:21:59

by Dave Rodgman

[permalink] [raw]
Subject: [PATCH 4/7] lib/lzo: 64-bit CTZ on arm64

From: Matt Sealey <[email protected]>

LZO leaves some performance on the table by not realising that arm64 can
optimize count-trailing-zeros bit operations.

Add CONFIG_ARM64 to the checked definitions alongside CONFIG_X86_64 to
enable the use of rbit/clz instructions on full 64-bit quantities.

Signed-off-by: Matt Sealey <[email protected]>
Signed-off-by: Dave Rodgman <[email protected]>
---
lib/lzo/lzodefs.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/lzo/lzodefs.h b/lib/lzo/lzodefs.h
index c0193f726db0..c8965dc181df 100644
--- a/lib/lzo/lzodefs.h
+++ b/lib/lzo/lzodefs.h
@@ -28,7 +28,7 @@

#if defined(__BIG_ENDIAN) && defined(__LITTLE_ENDIAN)
#error "conflicting endian definitions"
-#elif defined(CONFIG_X86_64)
+#elif defined(CONFIG_X86_64) || defined(CONFIG_ARM64)
#define LZO_USE_CTZ64 1
#define LZO_USE_CTZ32 1
#elif defined(CONFIG_X86) || defined(CONFIG_PPC)
--
2.16.4


2018-11-27 16:21:59

by Dave Rodgman

[permalink] [raw]
Subject: [PATCH 3/7] lib/lzo: enable 64-bit CTZ on Arm

From: Matt Sealey <[email protected]>

ARMv6 Thumb state introduced an RBIT instruction which, combined with CLZ
as present in ARMv5, introduces an extremely fast path for counting
trailing zeroes.

Enable the use of the GCC builtin for this on ARMv6+ with
CONFIG_THUMB2_KERNEL to ensure we get the 'new' instruction usage.

We do not bother enabling LZO_USE_CTZ64 support for ARMv5 as the builtin
code path does the same thing as the LZO_USE_CTZ32 code, only with more
register pressure.

Signed-off-by: Matt Sealey <[email protected]>
---
lib/lzo/lzodefs.h | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/lib/lzo/lzodefs.h b/lib/lzo/lzodefs.h
index e1b3cf6459a9..c0193f726db0 100644
--- a/lib/lzo/lzodefs.h
+++ b/lib/lzo/lzodefs.h
@@ -33,9 +33,14 @@
#define LZO_USE_CTZ32 1
#elif defined(CONFIG_X86) || defined(CONFIG_PPC)
#define LZO_USE_CTZ32 1
-#elif defined(CONFIG_ARM) && (__LINUX_ARM_ARCH__ >= 5)
+#elif defined(CONFIG_ARM)
+#if (__LINUX_ARM_ARCH__ >= 5)
#define LZO_USE_CTZ32 1
#endif
+#if (__LINUX_ARM_ARCH__ >= 6) && defined(CONFIG_THUMB2_KERNEL)
+#define LZO_USE_CTZ64 1
+#endif
+#endif

#define M1_MAX_OFFSET 0x0400
#define M2_MAX_OFFSET 0x0800
--
2.16.4


2018-11-27 16:22:05

by Dave Rodgman

[permalink] [raw]
Subject: [PATCH 2/7] lib/lzo: clean-up by introducing COPY16

From: Matt Sealey <[email protected]>

Most compilers should be able to merge adjacent loads/stores of sizes
which are less than but effect a multiple of a machine word size (in
effect a memcpy() of a constant amount). However the semantics of the
macro are that it just does the copy, the pointer increment is in the
code, hence we see

*a = *b
a += 8
b += 8
*a = *b
a += 8
b += 8

This introduces a dependency between the two groups of statements which
seems to defeat said compiler optimizers and generate some very strange
sequences of addition and subtraction of address offsets (i.e. it is
overcomplicated).

Since COPY8 is only ever used to copy amounts of 16 bytes (in pairs),
just define COPY16 as COPY8,COPY8. We leave the definition to preserve
the need to do unaligned accesses to machine-sized words per the
original code intent, we just don't use it in the code proper.

COPY16 then gives us code like:

*a = *b
*(a+8) = *(b+8)
a += 16
b += 16

This seems to allow compilers to generate much better code by using
base register writeback or simply positively incrementing offsets which
seems to positively affect performance. It is, at least, fewer
instructions to do the same job.

Signed-off-by: Matt Sealey <[email protected]>
---
lib/lzo/lzo1x_compress.c | 9 +++------
lib/lzo/lzo1x_decompress_safe.c | 18 ++++++------------
lib/lzo/lzodefs.h | 3 +++
3 files changed, 12 insertions(+), 18 deletions(-)

diff --git a/lib/lzo/lzo1x_compress.c b/lib/lzo/lzo1x_compress.c
index 236eb21167b5..82fb5571ce5e 100644
--- a/lib/lzo/lzo1x_compress.c
+++ b/lib/lzo/lzo1x_compress.c
@@ -60,8 +60,7 @@ lzo1x_1_do_compress(const unsigned char *in, size_t in_len,
op += t;
} else if (t <= 16) {
*op++ = (t - 3);
- COPY8(op, ii);
- COPY8(op + 8, ii + 8);
+ COPY16(op, ii);
op += t;
} else {
if (t <= 18) {
@@ -76,8 +75,7 @@ lzo1x_1_do_compress(const unsigned char *in, size_t in_len,
*op++ = tt;
}
do {
- COPY8(op, ii);
- COPY8(op + 8, ii + 8);
+ COPY16(op, ii);
op += 16;
ii += 16;
t -= 16;
@@ -255,8 +253,7 @@ int lzo1x_1_compress(const unsigned char *in, size_t in_len,
*op++ = tt;
}
if (t >= 16) do {
- COPY8(op, ii);
- COPY8(op + 8, ii + 8);
+ COPY16(op, ii);
op += 16;
ii += 16;
t -= 16;
diff --git a/lib/lzo/lzo1x_decompress_safe.c b/lib/lzo/lzo1x_decompress_safe.c
index a1c387f6afba..aa95d3066b7d 100644
--- a/lib/lzo/lzo1x_decompress_safe.c
+++ b/lib/lzo/lzo1x_decompress_safe.c
@@ -86,12 +86,9 @@ int lzo1x_decompress_safe(const unsigned char *in, size_t in_len,
const unsigned char *ie = ip + t;
unsigned char *oe = op + t;
do {
- COPY8(op, ip);
- op += 8;
- ip += 8;
- COPY8(op, ip);
- op += 8;
- ip += 8;
+ COPY16(op, ip);
+ op += 16;
+ ip += 16;
} while (ip < ie);
ip = ie;
op = oe;
@@ -187,12 +184,9 @@ int lzo1x_decompress_safe(const unsigned char *in, size_t in_len,
unsigned char *oe = op + t;
if (likely(HAVE_OP(t + 15))) {
do {
- COPY8(op, m_pos);
- op += 8;
- m_pos += 8;
- COPY8(op, m_pos);
- op += 8;
- m_pos += 8;
+ COPY16(op, m_pos);
+ op += 16;
+ m_pos += 16;
} while (op < oe);
op = oe;
if (HAVE_IP(6)) {
diff --git a/lib/lzo/lzodefs.h b/lib/lzo/lzodefs.h
index 497f9c9f03a8..e1b3cf6459a9 100644
--- a/lib/lzo/lzodefs.h
+++ b/lib/lzo/lzodefs.h
@@ -23,6 +23,9 @@
COPY4(dst, src); COPY4((dst) + 4, (src) + 4)
#endif

+#define COPY16(dst, src) \
+ do { COPY8(dst, src); COPY8((dst) + 8, (src) + 8); } while (0)
+
#if defined(__BIG_ENDIAN) && defined(__LITTLE_ENDIAN)
#error "conflicting endian definitions"
#elif defined(CONFIG_X86_64)
--
2.16.4


2018-11-27 19:26:06

by Dave Rodgman

[permalink] [raw]
Subject: [PATCH 5/7] lib/lzo: fast 8-byte copy on arm64

From: Matt Sealey <[email protected]>

Enable faster 8-byte copies on arm64.

Signed-off-by: Dave Rodgman <[email protected]>
Signed-off-by: Matt Sealey <[email protected]>
---
lib/lzo/lzodefs.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/lzo/lzodefs.h b/lib/lzo/lzodefs.h
index c8965dc181df..06fa83a38e0a 100644
--- a/lib/lzo/lzodefs.h
+++ b/lib/lzo/lzodefs.h
@@ -15,7 +15,7 @@

#define COPY4(dst, src) \
put_unaligned(get_unaligned((const u32 *)(src)), (u32 *)(dst))
-#if defined(CONFIG_X86_64)
+#if defined(CONFIG_X86_64) || defined(CONFIG_ARM64)
#define COPY8(dst, src) \
put_unaligned(get_unaligned((const u64 *)(src)), (u64 *)(dst))
#else
--
2.16.4


2018-11-27 19:26:09

by Dave Rodgman

[permalink] [raw]
Subject: [PATCH 1/7] lib/lzo: tidy-up ifdefs

Modify the ifdefs in lzodefs.h to be more consistent with normal kernel
macros (e.g., change __aarch64__ to CONFIG_ARM64).

Signed-off-by: Dave Rodgman <[email protected]>
---
lib/lzo/lzodefs.h | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/lib/lzo/lzodefs.h b/lib/lzo/lzodefs.h
index 4edefd2f540c..497f9c9f03a8 100644
--- a/lib/lzo/lzodefs.h
+++ b/lib/lzo/lzodefs.h
@@ -15,7 +15,7 @@

#define COPY4(dst, src) \
put_unaligned(get_unaligned((const u32 *)(src)), (u32 *)(dst))
-#if defined(__x86_64__)
+#if defined(CONFIG_X86_64)
#define COPY8(dst, src) \
put_unaligned(get_unaligned((const u64 *)(src)), (u64 *)(dst))
#else
@@ -25,12 +25,12 @@

#if defined(__BIG_ENDIAN) && defined(__LITTLE_ENDIAN)
#error "conflicting endian definitions"
-#elif defined(__x86_64__)
+#elif defined(CONFIG_X86_64)
#define LZO_USE_CTZ64 1
#define LZO_USE_CTZ32 1
-#elif defined(__i386__) || defined(__powerpc__)
+#elif defined(CONFIG_X86) || defined(CONFIG_PPC)
#define LZO_USE_CTZ32 1
-#elif defined(__arm__) && (__LINUX_ARM_ARCH__ >= 5)
+#elif defined(CONFIG_ARM) && (__LINUX_ARM_ARCH__ >= 5)
#define LZO_USE_CTZ32 1
#endif

--
2.16.4


2018-11-27 19:26:10

by Dave Rodgman

[permalink] [raw]
Subject: [PATCH 6/7] lib/lzo: implement run-length encoding

When using zram, we frequently encounter long runs of zero bytes.
This adds a special case which identifies runs of zeros and encodes
them using run-length encoding.

This is faster for both compression and decompresion. For
high-entropy data which doesn't hit this case, impact is minimal.

Compression ratio is within a few percent in all cases.

This modifies the bitstream in a way which is backwards compatible
(i.e., we can decompress old bitstreams, but old versions of lzo
cannot decompress new bitstreams).

Signed-off-by: Dave Rodgman <[email protected]>
---
Documentation/lzo.txt | 35 ++++++++++++---
include/linux/lzo.h | 2 +-
lib/lzo/lzo1x_compress.c | 98 ++++++++++++++++++++++++++++++++++++-----
lib/lzo/lzo1x_decompress_safe.c | 75 +++++++++++++++++++++----------
lib/lzo/lzodefs.h | 12 ++++-
5 files changed, 180 insertions(+), 42 deletions(-)

diff --git a/Documentation/lzo.txt b/Documentation/lzo.txt
index 6fa6a93d0949..306c60344ca7 100644
--- a/Documentation/lzo.txt
+++ b/Documentation/lzo.txt
@@ -78,16 +78,30 @@ Description
is an implementation design choice independent on the algorithm or
encoding.

+Versions
+
+0: Original version
+1: LZO-RLE
+
+Version 1 of LZO implements an extension to encode runs of zeros using run
+length encoding. This improves speed for data with many zeros, which is a
+common case for zram. This modifies the bitstream in a backwards compatible way
+(v1 can correctly decompress v0 compressed data, but v0 cannot read v1 data).
+
Byte sequences
==============

First byte encoding::

- 0..17 : follow regular instruction encoding, see below. It is worth
- noting that codes 16 and 17 will represent a block copy from
- the dictionary which is empty, and that they will always be
+ 0..16 : follow regular instruction encoding, see below. It is worth
+ noting that code 16 will represent a block copy from the
+ dictionary which is empty, and that it will always be
invalid at this place.

+ 17 : bitstream version. If the first byte is 17, the next byte
+ gives the bitstream version. If the first byte is not 17,
+ the bitstream version is 0.
+
18..21 : copy 0..3 literals
state = (byte - 17) = 0..3 [ copy <state> literals ]
skip byte
@@ -140,6 +154,11 @@ Byte sequences
state = S (copy S literals after this block)
End of stream is reached if distance == 16384

+ In version 1, this instruction is also used to encode a run of zeros if
+ distance = 0xbfff, i.e. H = 1 and the D bits are all 1.
+ In this case, it is followed by a fourth byte, X.
+ run length = ((X << 3) | (0 0 0 0 0 L L L)) + 4.
+
0 0 1 L L L L L (32..63)
Copy of small block within 16kB distance (preferably less than 34B)
length = 2 + (L ?: 31 + (zero_bytes * 255) + non_zero_byte)
@@ -165,7 +184,9 @@ Authors
=======

This document was written by Willy Tarreau <[email protected]> on 2014/07/19 during an
- analysis of the decompression code available in Linux 3.16-rc5. The code is
- tricky, it is possible that this document contains mistakes or that a few
- corner cases were overlooked. In any case, please report any doubt, fix, or
- proposed updates to the author(s) so that the document can be updated.
+ analysis of the decompression code available in Linux 3.16-rc5, and updated
+ by Dave Rodgman <[email protected]> on 2018/10/30 to introduce run-length
+ encoding. The code is tricky, it is possible that this document contains
+ mistakes or that a few corner cases were overlooked. In any case, please
+ report any doubt, fix, or proposed updates to the author(s) so that the
+ document can be updated.
diff --git a/include/linux/lzo.h b/include/linux/lzo.h
index 2ae27cb89927..547a86c71e1b 100644
--- a/include/linux/lzo.h
+++ b/include/linux/lzo.h
@@ -18,7 +18,7 @@
#define LZO1X_1_MEM_COMPRESS (8192 * sizeof(unsigned short))
#define LZO1X_MEM_COMPRESS LZO1X_1_MEM_COMPRESS

-#define lzo1x_worst_compress(x) ((x) + ((x) / 16) + 64 + 3)
+#define lzo1x_worst_compress(x) ((x) + ((x) / 16) + 64 + 3 + 2)

/* This requires 'wrkmem' of size LZO1X_1_MEM_COMPRESS */
int lzo1x_1_compress(const unsigned char *src, size_t src_len,
diff --git a/lib/lzo/lzo1x_compress.c b/lib/lzo/lzo1x_compress.c
index 82fb5571ce5e..fa8d4ff38531 100644
--- a/lib/lzo/lzo1x_compress.c
+++ b/lib/lzo/lzo1x_compress.c
@@ -20,7 +20,7 @@
static noinline size_t
lzo1x_1_do_compress(const unsigned char *in, size_t in_len,
unsigned char *out, size_t *out_len,
- size_t ti, void *wrkmem)
+ size_t ti, void *wrkmem, signed char *state_offset)
{
const unsigned char *ip;
unsigned char *op;
@@ -38,24 +38,82 @@ lzo1x_1_do_compress(const unsigned char *in, size_t in_len,
const unsigned char *m_pos;
size_t t, m_len, m_off;
u32 dv;
+ u32 run_length = 0;
literal:
ip += 1 + ((ip - ii) >> 5);
next:
if (unlikely(ip >= ip_end))
break;
dv = get_unaligned_le32(ip);
- t = ((dv * 0x1824429d) >> (32 - D_BITS)) & D_MASK;
- m_pos = in + dict[t];
- dict[t] = (lzo_dict_t) (ip - in);
- if (unlikely(dv != get_unaligned_le32(m_pos)))
- goto literal;
+
+ if (dv == 0) {
+ const unsigned char *ir = ip + 4;
+ const unsigned char *limit = ip_end
+ < (ip + MAX_ZERO_RUN_LENGTH + 1)
+ ? ip_end : ip + MAX_ZERO_RUN_LENGTH + 1;
+#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && \
+ defined(LZO_FAST_64BIT_MEMORY_ACCESS)
+ u64 dv64;
+
+ for (; (ir + 32) <= limit; ir += 32) {
+ dv64 = get_unaligned((u64 *)ir);
+ dv64 |= get_unaligned((u64 *)ir + 1);
+ dv64 |= get_unaligned((u64 *)ir + 2);
+ dv64 |= get_unaligned((u64 *)ir + 3);
+ if (dv64)
+ break;
+ }
+ for (; (ir + 8) <= limit; ir += 8) {
+ dv64 = get_unaligned((u64 *)ir);
+ if (dv64) {
+# if defined(__LITTLE_ENDIAN)
+ ir += __builtin_ctzll(dv64) >> 3;
+# elif defined(__BIG_ENDIAN)
+ ir += __builtin_clzll(dv64) >> 3;
+# else
+# error "missing endian definition"
+# endif
+ break;
+ }
+ }
+#else
+ while ((ir < (const unsigned char *)
+ ALIGN((uintptr_t)ir, 4)) &&
+ (ir < limit) && (*ir == 0))
+ ir++;
+ for (; (ir + 4) <= limit; ir += 4) {
+ dv = *((u32 *)ir);
+ if (dv) {
+# if defined(__LITTLE_ENDIAN)
+ ir += __builtin_ctz(dv) >> 3;
+# elif defined(__BIG_ENDIAN)
+ ir += __builtin_clz(dv) >> 3;
+# else
+# error "missing endian definition"
+# endif
+ break;
+ }
+ }
+#endif
+ while (likely(ir < limit) && unlikely(*ir == 0))
+ ir++;
+ run_length = ir - ip;
+ if (run_length > MAX_ZERO_RUN_LENGTH)
+ run_length = MAX_ZERO_RUN_LENGTH;
+ } else {
+ t = ((dv * 0x1824429d) >> (32 - D_BITS)) & D_MASK;
+ m_pos = in + dict[t];
+ dict[t] = (lzo_dict_t) (ip - in);
+ if (unlikely(dv != get_unaligned_le32(m_pos)))
+ goto literal;
+ }

ii -= ti;
ti = 0;
t = ip - ii;
if (t != 0) {
if (t <= 3) {
- op[-2] |= t;
+ op[*state_offset] |= t;
COPY4(op, ii);
op += t;
} else if (t <= 16) {
@@ -86,6 +144,17 @@ lzo1x_1_do_compress(const unsigned char *in, size_t in_len,
}
}

+ if (unlikely(run_length)) {
+ ip += run_length;
+ run_length -= MIN_ZERO_RUN_LENGTH;
+ put_unaligned_le32((run_length << 21) | 0xfffc18
+ | (run_length & 0x7), op);
+ op += 4;
+ run_length = 0;
+ *state_offset = -3;
+ goto finished_writing_instruction;
+ }
+
m_len = 4;
{
#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && defined(LZO_USE_CTZ64)
@@ -168,7 +237,6 @@ lzo1x_1_do_compress(const unsigned char *in, size_t in_len,

m_off = ip - m_pos;
ip += m_len;
- ii = ip;
if (m_len <= M2_MAX_LEN && m_off <= M2_MAX_OFFSET) {
m_off -= 1;
*op++ = (((m_len - 1) << 5) | ((m_off & 7) << 2));
@@ -205,6 +273,9 @@ lzo1x_1_do_compress(const unsigned char *in, size_t in_len,
*op++ = (m_off << 2);
*op++ = (m_off >> 6);
}
+ *state_offset = -2;
+finished_writing_instruction:
+ ii = ip;
goto next;
}
*out_len = op - out;
@@ -219,6 +290,12 @@ int lzo1x_1_compress(const unsigned char *in, size_t in_len,
unsigned char *op = out;
size_t l = in_len;
size_t t = 0;
+ signed char state_offset = -2;
+
+ // LZO v0 will never write 17 as first byte,
+ // so this is used to version the bitstream
+ *op++ = 17;
+ *op++ = LZO_VERSION;

while (l > 20) {
size_t ll = l <= (M4_MAX_OFFSET + 1) ? l : (M4_MAX_OFFSET + 1);
@@ -227,7 +304,8 @@ int lzo1x_1_compress(const unsigned char *in, size_t in_len,
break;
BUILD_BUG_ON(D_SIZE * sizeof(lzo_dict_t) > LZO1X_1_MEM_COMPRESS);
memset(wrkmem, 0, D_SIZE * sizeof(lzo_dict_t));
- t = lzo1x_1_do_compress(ip, ll, op, out_len, t, wrkmem);
+ t = lzo1x_1_do_compress(ip, ll, op, out_len,
+ t, wrkmem, &state_offset);
ip += ll;
op += *out_len;
l -= ll;
@@ -240,7 +318,7 @@ int lzo1x_1_compress(const unsigned char *in, size_t in_len,
if (op == out && t <= 238) {
*op++ = (17 + t);
} else if (t <= 3) {
- op[-2] |= t;
+ op[state_offset] |= t;
} else if (t <= 18) {
*op++ = (t - 3);
} else {
diff --git a/lib/lzo/lzo1x_decompress_safe.c b/lib/lzo/lzo1x_decompress_safe.c
index aa95d3066b7d..b8f88d5ea3ff 100644
--- a/lib/lzo/lzo1x_decompress_safe.c
+++ b/lib/lzo/lzo1x_decompress_safe.c
@@ -46,11 +46,23 @@ int lzo1x_decompress_safe(const unsigned char *in, size_t in_len,
const unsigned char * const ip_end = in + in_len;
unsigned char * const op_end = out + *out_len;

+ unsigned char bitstream_version;
+
op = out;
ip = in;

if (unlikely(in_len < 3))
goto input_overrun;
+
+ if (likely(*ip == 17)) {
+ bitstream_version = ip[1];
+ ip += 2;
+ if (unlikely(in_len < 5))
+ goto input_overrun;
+ } else {
+ bitstream_version = 0;
+ }
+
if (*ip > 17) {
t = *ip++ - 17;
if (t < 4) {
@@ -151,32 +163,49 @@ int lzo1x_decompress_safe(const unsigned char *in, size_t in_len,
m_pos -= next >> 2;
next &= 3;
} else {
- m_pos = op;
- m_pos -= (t & 8) << 11;
- t = (t & 7) + (3 - 1);
- if (unlikely(t == 2)) {
- size_t offset;
- const unsigned char *ip_last = ip;
+ NEED_IP(2);
+ next = get_unaligned_le16(ip);
+ if (((next & 0xfffc) == 0xfffc) &&
+ ((t & 0xf8) == 0x18) &&
+ likely(bitstream_version)) {
+ NEED_IP(3);
+ t &= 7;
+ t |= ip[2] << 3;
+ t += MIN_ZERO_RUN_LENGTH;
+ NEED_OP(t);
+ memset(op, 0, t);
+ op += t;
+ next &= 3;
+ ip += 3;
+ goto match_next;
+ } else {
+ m_pos = op;
+ m_pos -= (t & 8) << 11;
+ t = (t & 7) + (3 - 1);
+ if (unlikely(t == 2)) {
+ size_t offset;
+ const unsigned char *ip_last = ip;

- while (unlikely(*ip == 0)) {
- ip++;
- NEED_IP(1);
- }
- offset = ip - ip_last;
- if (unlikely(offset > MAX_255_COUNT))
- return LZO_E_ERROR;
+ while (unlikely(*ip == 0)) {
+ ip++;
+ NEED_IP(1);
+ }
+ offset = ip - ip_last;
+ if (unlikely(offset > MAX_255_COUNT))
+ return LZO_E_ERROR;

- offset = (offset << 8) - offset;
- t += offset + 7 + *ip++;
- NEED_IP(2);
+ offset = (offset << 8) - offset;
+ t += offset + 7 + *ip++;
+ NEED_IP(2);
+ next = get_unaligned_le16(ip);
+ }
+ ip += 2;
+ m_pos -= next >> 2;
+ next &= 3;
+ if (m_pos == op)
+ goto eof_found;
+ m_pos -= 0x4000;
}
- next = get_unaligned_le16(ip);
- ip += 2;
- m_pos -= next >> 2;
- next &= 3;
- if (m_pos == op)
- goto eof_found;
- m_pos -= 0x4000;
}
TEST_LB(m_pos);
#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
diff --git a/lib/lzo/lzodefs.h b/lib/lzo/lzodefs.h
index 06fa83a38e0a..682359058b3c 100644
--- a/lib/lzo/lzodefs.h
+++ b/lib/lzo/lzodefs.h
@@ -13,6 +13,12 @@
*/


+/* Version
+ * 0: original lzo version
+ * 1: lzo with support for RLE
+ */
+#define LZO_VERSION 1
+
#define COPY4(dst, src) \
put_unaligned(get_unaligned((const u32 *)(src)), (u32 *)(dst))
#if defined(CONFIG_X86_64) || defined(CONFIG_ARM64)
@@ -31,6 +37,7 @@
#elif defined(CONFIG_X86_64) || defined(CONFIG_ARM64)
#define LZO_USE_CTZ64 1
#define LZO_USE_CTZ32 1
+#define LZO_FAST_64BIT_MEMORY_ACCESS
#elif defined(CONFIG_X86) || defined(CONFIG_PPC)
#define LZO_USE_CTZ32 1
#elif defined(CONFIG_ARM)
@@ -45,7 +52,7 @@
#define M1_MAX_OFFSET 0x0400
#define M2_MAX_OFFSET 0x0800
#define M3_MAX_OFFSET 0x4000
-#define M4_MAX_OFFSET 0xbfff
+#define M4_MAX_OFFSET 0xbffe

#define M1_MIN_LEN 2
#define M1_MAX_LEN 2
@@ -61,6 +68,9 @@
#define M3_MARKER 32
#define M4_MARKER 16

+#define MIN_ZERO_RUN_LENGTH 4
+#define MAX_ZERO_RUN_LENGTH (2047 + MIN_ZERO_RUN_LENGTH)
+
#define lzo_dict_t unsigned short
#define D_BITS 13
#define D_SIZE (1u << D_BITS)
--
2.16.4


2018-11-27 22:52:14

by Andrew Morton

[permalink] [raw]
Subject: Re: [PATCH 2/7] lib/lzo: clean-up by introducing COPY16

On Tue, 27 Nov 2018 16:19:31 +0000 Dave Rodgman <[email protected]> wrote:

> From: Matt Sealey <[email protected]>
>
> ...
>
> Signed-off-by: Matt Sealey <[email protected]>

Several of the From:Matt patches were missing your Signed-off-by:. I
added it.

[patch 5/7] had the signoffs in an inappropriate order - I switched
them.


2018-11-29 03:10:59

by Sergey Senozhatsky

[permalink] [raw]
Subject: Re: [PATCH 6/7] lib/lzo: implement run-length encoding

On (11/27/18 16:19), Dave Rodgman wrote:
>
> This modifies the bitstream in a way which is backwards compatible
> (i.e., we can decompress old bitstreams, but old versions of lzo
> cannot decompress new bitstreams).
>

Hmmm... Whoa. Help me understand this:

So a btrfs filesystem, compressed with the new lzo, say I run 4.21,
won't be readable at all once I reboot under 4.19?

What about compressed net traffic between servers running different
kernel version (one with new lzo, the other one with old lzo)?
XFRM can be compressed with lzo, can't it?

-ss

2018-11-29 03:12:48

by Sergey Senozhatsky

[permalink] [raw]
Subject: Re: [PATCH 6/7] lib/lzo: implement run-length encoding

On (11/29/18 12:08), Sergey Senozhatsky wrote:
> On (11/27/18 16:19), Dave Rodgman wrote:
> >
> > This modifies the bitstream in a way which is backwards compatible
> > (i.e., we can decompress old bitstreams, but old versions of lzo
> > cannot decompress new bitstreams).
> >
>
> Hmmm... Whoa. Help me understand this:
>
> So a btrfs filesystem, compressed with the new lzo, say I run 4.21,
> won't be readable at all once I reboot under 4.19?
>
> What about compressed net traffic between servers running different
> kernel version (one with new lzo, the other one with old lzo)?
> XFRM can be compressed with lzo, can't it?

Ah, false alarm. Sorry for the noise!

You create a new lzo version in the next patch.

-ss

2018-11-29 04:44:35

by Sergey Senozhatsky

[permalink] [raw]
Subject: Re: [PATCH 7/7] lib/lzo: separate lzo-rle from lzo

On (11/27/18 16:19), Dave Rodgman wrote:
> Documentation/lzo.txt | 12 ++-
> crypto/Makefile | 2 +-
> crypto/lzo-rle.c | 175 ++++++++++++++++++++++++++++++++++++++++++
> crypto/tcrypt.c | 4 +-
> drivers/block/zram/zcomp.c | 1 +
> drivers/block/zram/zram_drv.c | 2 +-
> include/linux/lzo.h | 4 +
> lib/lzo/lzo1x_compress.c | 42 +++++++---
> lib/lzo/lzodefs.h | 3 +-
> 9 files changed, 227 insertions(+), 18 deletions(-)
> create mode 100644 crypto/lzo-rle.c

[..]

> +static struct crypto_alg alg = {
> + .cra_name = "lzo-rle",
> + .cra_flags = CRYPTO_ALG_TYPE_COMPRESS,
> + .cra_ctxsize = sizeof(struct lzorle_ctx),
> + .cra_module = THIS_MODULE,
> + .cra_init = lzorle_init,
> + .cra_exit = lzorle_exit,
> + .cra_u = { .compress = {
> + .coa_compress = lzorle_compress,
> + .coa_decompress = lzorle_decompress } }
> +};

A nitpick:
indentation for .compress assignment is a bit confusing, maybe.


[..]
> +++ b/drivers/block/zram/zcomp.c
> @@ -20,6 +20,7 @@
>
> static const char * const backends[] = {
> "lzo",
> + "lzo-rle",
> #if IS_ENABLED(CONFIG_CRYPTO_LZ4)
> "lz4",
> #endif

[..]

> +++ b/drivers/block/zram/zram_drv.c
> @@ -41,7 +41,7 @@ static DEFINE_IDR(zram_index_idr);
> static DEFINE_MUTEX(zram_index_mutex);
>
> static int zram_major;
> -static const char *default_compressor = "lzo";
> +static const char *default_compressor = "lzo-rle";

OK, so it's not just "separate lzo-rle", it's also "switch zram to
a new compression algorithm by default". I'd say that usually I'd
expect this to be separate patches.

-ss

2018-11-29 04:47:01

by Sergey Senozhatsky

[permalink] [raw]
Subject: Re: [PATCH v2 0/7] lib/lzo: performance improvements

On (11/27/18 16:19), Dave Rodgman wrote:
> > Right. The number is data dependent. Not all swapped out pages can be
> > compressed; compressed pages that end up being >= zs_huge_class_size() are
> > considered incompressible and stored as it.
> >
> > I'd say that on my setups around 50-60% of pages are incompressible.
>
> So, just to give a bit more detail: the test setup was a Samsung
> Chromebook Pro, cycling through 80 tabs in Chrome. With lzo-rle, only
> 5% of pages increased in size, and 90% of pages compress to 75% of
> original size (or better). Mean compression ratio was 41%. Importantly
> for lzo-rle, there are a lot of low-entropy pages where it can do well:
> in total about 20% of the data is zeros forming part of a run of 4 or
> more bytes.
>
> As a quick summary of the impact of these patches on bigger chunks of
> data, I've compared the performance of four different variants of lzo
> on two large (~40 MB) files. The numbers show round-trip throughput
> in MB/s:
>
> Variant | Low-entropy | High-entropy
> Current lzo | 242 | 157
> Arm opts | 290 | 159
> RLE | 876 | 151
> Arm opts + RLE | 1150 | 181
>
> So both the Arm optimisations (8,16-byte copy & CTZ patches), and the
> RLE implementation make a significant contribution to the overall
> performance uplift.

Cool!

-ss

2018-11-29 10:22:55

by Dave Rodgman

[permalink] [raw]
Subject: Re: [PATCH 7/7] lib/lzo: separate lzo-rle from lzo



On 29/11/2018 4:43 am, Sergey Senozhatsky wrote:
> On (11/27/18 16:19), Dave Rodgman wrote:
>> Documentation/lzo.txt | 12 ++-
>> crypto/Makefile | 2 +-
>> crypto/lzo-rle.c | 175 ++++++++++++++++++++++++++++++++++++++++++
>> crypto/tcrypt.c | 4 +-
>> drivers/block/zram/zcomp.c | 1 +
>> drivers/block/zram/zram_drv.c | 2 +-
>> include/linux/lzo.h | 4 +
>> lib/lzo/lzo1x_compress.c | 42 +++++++---
>> lib/lzo/lzodefs.h | 3 +-
>> 9 files changed, 227 insertions(+), 18 deletions(-)
>> create mode 100644 crypto/lzo-rle.c
>
> [..]
>
>> +static struct crypto_alg alg = {
>> + .cra_name = "lzo-rle",
>> + .cra_flags = CRYPTO_ALG_TYPE_COMPRESS,
>> + .cra_ctxsize = sizeof(struct lzorle_ctx),
>> + .cra_module = THIS_MODULE,
>> + .cra_init = lzorle_init,
>> + .cra_exit = lzorle_exit,
>> + .cra_u = { .compress = {
>> + .coa_compress = lzorle_compress,
>> + .coa_decompress = lzorle_decompress } }
>> +};
>
> A nitpick:
> indentation for .compress assignment is a bit confusing, maybe.

Agreed - I've copied this directly from crypto/lzo.c though, so I
retained the same style. Cleanup could be a separate patch.

> [..]
>> +++ b/drivers/block/zram/zcomp.c
>> @@ -20,6 +20,7 @@
>>
>> static const char * const backends[] = {
>> "lzo",
>> + "lzo-rle",
>> #if IS_ENABLED(CONFIG_CRYPTO_LZ4)
>> "lz4",
>> #endif
>
> [..]
>
>> +++ b/drivers/block/zram/zram_drv.c
>> @@ -41,7 +41,7 @@ static DEFINE_IDR(zram_index_idr);
>> static DEFINE_MUTEX(zram_index_mutex);
>>
>> static int zram_major;
>> -static const char *default_compressor = "lzo";
>> +static const char *default_compressor = "lzo-rle";
>
> OK, so it's not just "separate lzo-rle", it's also "switch zram to
> a new compression algorithm by default". I'd say that usually I'd
> expect this to be separate patches.

Yes, fair point. akpm has picked this up now though, so probably a bit
late to break it out into a separate patch?

Dave

2018-11-29 20:33:36

by Andrew Morton

[permalink] [raw]
Subject: Re: [PATCH 7/7] lib/lzo: separate lzo-rle from lzo

On Thu, 29 Nov 2018 10:21:53 +0000 Dave Rodgman <[email protected]> wrote:

> >> @@ -41,7 +41,7 @@ static DEFINE_IDR(zram_index_idr);
> >> static DEFINE_MUTEX(zram_index_mutex);
> >>
> >> static int zram_major;
> >> -static const char *default_compressor = "lzo";
> >> +static const char *default_compressor = "lzo-rle";
> >
> > OK, so it's not just "separate lzo-rle", it's also "switch zram to
> > a new compression algorithm by default". I'd say that usually I'd
> > expect this to be separate patches.
>
> Yes, fair point. akpm has picked this up now though, so probably a bit
> late to break it out into a separate patch?

That's fine. akpm is flexible ;) Whatever produces the best overall
result in the permanent record, please.


2018-11-30 03:05:53

by Sergey Senozhatsky

[permalink] [raw]
Subject: Re: [PATCH 7/7] lib/lzo: separate lzo-rle from lzo

On (11/29/18 10:21), Dave Rodgman wrote:
> > [..]
> >> +++ b/drivers/block/zram/zcomp.c
> >> @@ -20,6 +20,7 @@
> >>
> >> static const char * const backends[] = {
> >> "lzo",
> >> + "lzo-rle",
> >> #if IS_ENABLED(CONFIG_CRYPTO_LZ4)
> >> "lz4",
> >> #endif
> >
> > [..]
> >
> >> +++ b/drivers/block/zram/zram_drv.c
> >> @@ -41,7 +41,7 @@ static DEFINE_IDR(zram_index_idr);
> >> static DEFINE_MUTEX(zram_index_mutex);
> >>
> >> static int zram_major;
> >> -static const char *default_compressor = "lzo";
> >> +static const char *default_compressor = "lzo-rle";
> >
> > OK, so it's not just "separate lzo-rle", it's also "switch zram to
> > a new compression algorithm by default". I'd say that usually I'd
> > expect this to be separate patches.
>
> Yes, fair point. akpm has picked this up now though, so probably a bit
> late to break it out into a separate patch?

Andrew accepts patches to patches, and patches to patches to patches, etc.
He is very flexible :)

I don't have a very strong opinion. Would probably be better to split
it, tho. Let's hear from Minchan.

-ss

2018-11-30 10:47:13

by Dave Rodgman

[permalink] [raw]
Subject: Re: [PATCH 7/7] lib/lzo: separate lzo-rle from lzo

On 29/11/2018 4:43 am, Sergey Senozhatsky wrote:
> On (11/27/18 16:19), Dave Rodgman wrote:>
>> +static struct crypto_alg alg = {
>> + .cra_name = "lzo-rle",
>> + .cra_flags = CRYPTO_ALG_TYPE_COMPRESS,
>> + .cra_ctxsize = sizeof(struct lzorle_ctx),
>> + .cra_module = THIS_MODULE,
>> + .cra_init = lzorle_init,
>> + .cra_exit = lzorle_exit,
>> + .cra_u = { .compress = {
>> + .coa_compress = lzorle_compress,
>> + .coa_decompress = lzorle_decompress } }
>> +};
>
> A nitpick:
> indentation for .compress assignment is a bit confusing, maybe.

Looking a bit more closely, these structs are formatted fairly
inconsistently in the crypto directory. So, lzo-rle is consistent with
what lzo does... but various other files do it differently.

I'm happy to submit a whitespace cleanup patch if people would like, and
get everything in that directory consistent (i.e. adopt a style similar
to the example below)?

static struct scomp_alg scomp = {
.alloc_ctx = lzorle_alloc_ctx,
.free_ctx = lzorle_free_ctx,
.compress = lzorle_scompress,
.decompress = lzorle_sdecompress,
.base = {
.cra_name = "lzo-rle",
.cra_driver_name = "lzo-rle-scomp",
.cra_module = THIS_MODULE,
}
};

Dave

2018-12-03 02:41:19

by Sergey Senozhatsky

[permalink] [raw]
Subject: Re: [PATCH 7/7] lib/lzo: separate lzo-rle from lzo

On (11/30/18 10:45), Dave Rodgman wrote:
> Looking a bit more closely, these structs are formatted fairly
> inconsistently in the crypto directory. So, lzo-rle is consistent with
> what lzo does... but various other files do it differently.
>
> I'm happy to submit a whitespace cleanup patch if people would like, and
> get everything in that directory consistent (i.e. adopt a style similar
> to the example below)?

I'm not in any position to ask you to do this; white-space clean ups
are not very popular (it's OK for staging tree; not so much otherwise).
So we better ask David and Herbert.

> static struct scomp_alg scomp = {
> .alloc_ctx = lzorle_alloc_ctx,
> .free_ctx = lzorle_free_ctx,
> .compress = lzorle_scompress,
> .decompress = lzorle_sdecompress,
> .base = {
> .cra_name = "lzo-rle",
> .cra_driver_name = "lzo-rle-scomp",
> .cra_module = THIS_MODULE,
> }
> };

Looks nice.

-ss

2018-12-03 02:54:21

by Herbert Xu

[permalink] [raw]
Subject: Re: [PATCH 7/7] lib/lzo: separate lzo-rle from lzo

On Mon, Dec 03, 2018 at 11:40:19AM +0900, Sergey Senozhatsky wrote:
>
> I'm not in any position to ask you to do this; white-space clean ups
> are not very popular (it's OK for staging tree; not so much otherwise).
> So we better ask David and Herbert.

I'm fine with white space cleanups.

Thanks,
--
Email: Herbert Xu <[email protected]>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt