mirror of
https://github.com/torvalds/linux
synced 2024-09-06 09:51:23 +00:00
![Mikhail Zaslonko](/assets/img/avatar_default.png)
Since hardware inflate does not support Z_PACKET_FLUSH option (used exclusively by kernel PPP driver), always switch to software like we already do for Z_BLOCK flush option. Without this patch, PPP might get Z_DATA_ERROR return code from zlib_inflate() and disable zlib compression for the packets. Link: https://lkml.kernel.org/r/20230126131428.1222214-9-zaslonko@linux.ibm.com Signed-off-by: Mikhail Zaslonko <zaslonko@linux.ibm.com> Acked-by: Ilya Leoshkevich <iii@linux.ibm.com> Cc: Heiko Carstens <hca@linux.ibm.com> Cc: Vasily Gorbik <gor@linux.ibm.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
155 lines
4.5 KiB
C
155 lines
4.5 KiB
C
// SPDX-License-Identifier: Zlib
|
|
|
|
#include "../zlib_inflate/inflate.h"
|
|
#include "dfltcc_util.h"
|
|
#include "dfltcc_inflate.h"
|
|
#include <asm/setup.h>
|
|
#include <linux/export.h>
|
|
#include <linux/zutil.h>
|
|
|
|
/*
|
|
* Expand.
|
|
*/
|
|
int dfltcc_can_inflate(
|
|
z_streamp strm
|
|
)
|
|
{
|
|
struct inflate_state *state = (struct inflate_state *)strm->state;
|
|
struct dfltcc_state *dfltcc_state = GET_DFLTCC_STATE(state);
|
|
|
|
/* Check for kernel dfltcc command line parameter */
|
|
if (zlib_dfltcc_support == ZLIB_DFLTCC_DISABLED ||
|
|
zlib_dfltcc_support == ZLIB_DFLTCC_DEFLATE_ONLY)
|
|
return 0;
|
|
|
|
/* Unsupported hardware */
|
|
return is_bit_set(dfltcc_state->af.fns, DFLTCC_XPND) &&
|
|
is_bit_set(dfltcc_state->af.fmts, DFLTCC_FMT0);
|
|
}
|
|
EXPORT_SYMBOL(dfltcc_can_inflate);
|
|
|
|
void dfltcc_reset_inflate_state(z_streamp strm) {
|
|
struct inflate_state *state = (struct inflate_state *)strm->state;
|
|
struct dfltcc_state *dfltcc_state = GET_DFLTCC_STATE(state);
|
|
|
|
dfltcc_reset_state(dfltcc_state);
|
|
}
|
|
EXPORT_SYMBOL(dfltcc_reset_inflate_state);
|
|
|
|
static int dfltcc_was_inflate_used(
|
|
z_streamp strm
|
|
)
|
|
{
|
|
struct inflate_state *state = (struct inflate_state *)strm->state;
|
|
struct dfltcc_param_v0 *param = &GET_DFLTCC_STATE(state)->param;
|
|
|
|
return !param->nt;
|
|
}
|
|
|
|
static int dfltcc_inflate_disable(
|
|
z_streamp strm
|
|
)
|
|
{
|
|
struct inflate_state *state = (struct inflate_state *)strm->state;
|
|
struct dfltcc_state *dfltcc_state = GET_DFLTCC_STATE(state);
|
|
|
|
if (!dfltcc_can_inflate(strm))
|
|
return 0;
|
|
if (dfltcc_was_inflate_used(strm))
|
|
/* DFLTCC has already decompressed some data. Since there is not
|
|
* enough information to resume decompression in software, the call
|
|
* must fail.
|
|
*/
|
|
return 1;
|
|
/* DFLTCC was not used yet - decompress in software */
|
|
memset(&dfltcc_state->af, 0, sizeof(dfltcc_state->af));
|
|
return 0;
|
|
}
|
|
|
|
static dfltcc_cc dfltcc_xpnd(
|
|
z_streamp strm
|
|
)
|
|
{
|
|
struct inflate_state *state = (struct inflate_state *)strm->state;
|
|
struct dfltcc_param_v0 *param = &GET_DFLTCC_STATE(state)->param;
|
|
size_t avail_in = strm->avail_in;
|
|
size_t avail_out = strm->avail_out;
|
|
dfltcc_cc cc;
|
|
|
|
cc = dfltcc(DFLTCC_XPND | HBT_CIRCULAR,
|
|
param, &strm->next_out, &avail_out,
|
|
&strm->next_in, &avail_in, state->window);
|
|
strm->avail_in = avail_in;
|
|
strm->avail_out = avail_out;
|
|
return cc;
|
|
}
|
|
|
|
dfltcc_inflate_action dfltcc_inflate(
|
|
z_streamp strm,
|
|
int flush,
|
|
int *ret
|
|
)
|
|
{
|
|
struct inflate_state *state = (struct inflate_state *)strm->state;
|
|
struct dfltcc_state *dfltcc_state = GET_DFLTCC_STATE(state);
|
|
struct dfltcc_param_v0 *param = &dfltcc_state->param;
|
|
dfltcc_cc cc;
|
|
|
|
if (flush == Z_BLOCK || flush == Z_PACKET_FLUSH) {
|
|
/* DFLTCC does not support stopping on block boundaries (Z_BLOCK flush option)
|
|
* as well as the use of Z_PACKET_FLUSH option (used exclusively by PPP driver)
|
|
*/
|
|
if (dfltcc_inflate_disable(strm)) {
|
|
*ret = Z_STREAM_ERROR;
|
|
return DFLTCC_INFLATE_BREAK;
|
|
} else
|
|
return DFLTCC_INFLATE_SOFTWARE;
|
|
}
|
|
|
|
if (state->last) {
|
|
if (state->bits != 0) {
|
|
strm->next_in++;
|
|
strm->avail_in--;
|
|
state->bits = 0;
|
|
}
|
|
state->mode = CHECK;
|
|
return DFLTCC_INFLATE_CONTINUE;
|
|
}
|
|
|
|
if (strm->avail_in == 0 && !param->cf)
|
|
return DFLTCC_INFLATE_BREAK;
|
|
|
|
if (!state->window || state->wsize == 0) {
|
|
state->mode = MEM;
|
|
return DFLTCC_INFLATE_CONTINUE;
|
|
}
|
|
|
|
/* Translate stream to parameter block */
|
|
param->cvt = CVT_ADLER32;
|
|
param->sbb = state->bits;
|
|
if (param->hl)
|
|
param->nt = 0; /* Honor history for the first block */
|
|
param->cv = state->check;
|
|
|
|
/* Inflate */
|
|
do {
|
|
cc = dfltcc_xpnd(strm);
|
|
} while (cc == DFLTCC_CC_AGAIN);
|
|
|
|
/* Translate parameter block to stream */
|
|
strm->msg = oesc_msg(dfltcc_state->msg, param->oesc);
|
|
state->last = cc == DFLTCC_CC_OK;
|
|
state->bits = param->sbb;
|
|
state->check = param->cv;
|
|
if (cc == DFLTCC_CC_OP2_CORRUPT && param->oesc != 0) {
|
|
/* Report an error if stream is corrupted */
|
|
state->mode = BAD;
|
|
return DFLTCC_INFLATE_CONTINUE;
|
|
}
|
|
state->mode = TYPEDO;
|
|
/* Break if operands are exhausted, otherwise continue looping */
|
|
return (cc == DFLTCC_CC_OP1_TOO_SHORT || cc == DFLTCC_CC_OP2_TOO_SHORT) ?
|
|
DFLTCC_INFLATE_BREAK : DFLTCC_INFLATE_CONTINUE;
|
|
}
|
|
EXPORT_SYMBOL(dfltcc_inflate);
|