From: Hugo Villeneuve <hvilleneuve(a)dimonoff.com>
When enabling access to the special register set, Receiver time-out and
RHR interrupts can happen. In this case, the IRQ handler will try to read
from the FIFO thru the RHR register at address 0x00, but address 0x00 is
mapped to DLL register, resulting in erroneous FIFO reading.
Call graph example:
sc16is7xx_startup(): entry
sc16is7xx_ms_proc(): entry
sc16is7xx_set_termios(): entry
sc16is7xx_set_baud(): DLH/DLL = $009C --> access special register set
sc16is7xx_port_irq() entry --> IIR is 0x0C
sc16is7xx_handle_rx() entry
sc16is7xx_fifo_read(): --> unable to access FIFO (RHR) because it is
mapped to DLL (LCR=LCR_CONF_MODE_A)
sc16is7xx_set_baud(): exit --> Restore access to general register set
Fix the problem by claiming the efr_lock mutex when accessing the Special
register set.
Fixes: dfeae619d781 ("serial: sc16is7xx")
Cc: <stable(a)vger.kernel.org>
Signed-off-by: Hugo Villeneuve <hvilleneuve(a)dimonoff.com>
---
drivers/tty/serial/sc16is7xx.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c
index 58696e05492c..b4c1798a1df2 100644
--- a/drivers/tty/serial/sc16is7xx.c
+++ b/drivers/tty/serial/sc16is7xx.c
@@ -592,6 +592,8 @@ static int sc16is7xx_set_baud(struct uart_port *port, int baud)
SC16IS7XX_MCR_CLKSEL_BIT,
prescaler == 1 ? 0 : SC16IS7XX_MCR_CLKSEL_BIT);
+ mutex_lock(&one->efr_lock);
+
/* Backup LCR and access special register set (DLL/DLH) */
lcr = sc16is7xx_port_read(port, SC16IS7XX_LCR_REG);
sc16is7xx_port_write(port, SC16IS7XX_LCR_REG,
@@ -606,6 +608,8 @@ static int sc16is7xx_set_baud(struct uart_port *port, int baud)
/* Restore LCR and access to general register set */
sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, lcr);
+ mutex_unlock(&one->efr_lock);
+
return DIV_ROUND_CLOSEST((clk / prescaler) / 16, div);
}
--
2.39.2
From: Hugo Villeneuve <hvilleneuve(a)dimonoff.com>
Sometimes, when a packet is received on channel A at almost the same time
as a packet is about to be transmitted on channel B, we observe with a
logic analyzer that the received packet on channel A is transmitted on
channel B. In other words, the Tx buffer data on channel B is corrupted
with data from channel A.
The problem appeared since commit 4409df5866b7 ("serial: sc16is7xx: change
EFR lock to operate on each channels"), which changed the EFR locking to
operate on each channel instead of chip-wise.
This commit has introduced a regression, because the EFR lock is used not
only to protect the EFR registers access, but also, in a very obscure and
undocumented way, to protect access to the data buffer, which is shared by
the Tx and Rx handlers, but also by each channel of the IC.
Fix this regression first by switching to kfifo_out_linear_ptr() in
sc16is7xx_handle_tx() to eliminate the need for a shared Rx/Tx buffer.
Secondly, replace the chip-wise Rx buffer with a separate Rx buffer for
each channel.
Fixes: 4409df5866b7 ("serial: sc16is7xx: change EFR lock to operate on each channels")
Cc: <stable(a)vger.kernel.org>
Signed-off-by: Hugo Villeneuve <hvilleneuve(a)dimonoff.com>
---
drivers/tty/serial/sc16is7xx.c | 21 +++++++++++----------
1 file changed, 11 insertions(+), 10 deletions(-)
diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c
index c79dcd7c8d1a..58696e05492c 100644
--- a/drivers/tty/serial/sc16is7xx.c
+++ b/drivers/tty/serial/sc16is7xx.c
@@ -327,6 +327,7 @@ struct sc16is7xx_one {
struct kthread_work reg_work;
struct kthread_delayed_work ms_work;
struct sc16is7xx_one_config config;
+ unsigned char buf[SC16IS7XX_FIFO_SIZE]; /* Rx buffer. */
unsigned int old_mctrl;
u8 old_lcr; /* Value before EFR access. */
bool irda_mode;
@@ -340,7 +341,6 @@ struct sc16is7xx_port {
unsigned long gpio_valid_mask;
#endif
u8 mctrl_mask;
- unsigned char buf[SC16IS7XX_FIFO_SIZE];
struct kthread_worker kworker;
struct task_struct *kworker_task;
struct sc16is7xx_one p[];
@@ -612,18 +612,18 @@ static int sc16is7xx_set_baud(struct uart_port *port, int baud)
static void sc16is7xx_handle_rx(struct uart_port *port, unsigned int rxlen,
unsigned int iir)
{
- struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
+ struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
unsigned int lsr = 0, bytes_read, i;
bool read_lsr = (iir == SC16IS7XX_IIR_RLSE_SRC) ? true : false;
u8 ch, flag;
- if (unlikely(rxlen >= sizeof(s->buf))) {
+ if (unlikely(rxlen >= sizeof(one->buf))) {
dev_warn_ratelimited(port->dev,
"ttySC%i: Possible RX FIFO overrun: %d\n",
port->line, rxlen);
port->icount.buf_overrun++;
/* Ensure sanity of RX level */
- rxlen = sizeof(s->buf);
+ rxlen = sizeof(one->buf);
}
while (rxlen) {
@@ -636,10 +636,10 @@ static void sc16is7xx_handle_rx(struct uart_port *port, unsigned int rxlen,
lsr = 0;
if (read_lsr) {
- s->buf[0] = sc16is7xx_port_read(port, SC16IS7XX_RHR_REG);
+ one->buf[0] = sc16is7xx_port_read(port, SC16IS7XX_RHR_REG);
bytes_read = 1;
} else {
- sc16is7xx_fifo_read(port, s->buf, rxlen);
+ sc16is7xx_fifo_read(port, one->buf, rxlen);
bytes_read = rxlen;
}
@@ -672,7 +672,7 @@ static void sc16is7xx_handle_rx(struct uart_port *port, unsigned int rxlen,
}
for (i = 0; i < bytes_read; ++i) {
- ch = s->buf[i];
+ ch = one->buf[i];
if (uart_handle_sysrq_char(port, ch))
continue;
@@ -690,10 +690,10 @@ static void sc16is7xx_handle_rx(struct uart_port *port, unsigned int rxlen,
static void sc16is7xx_handle_tx(struct uart_port *port)
{
- struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
struct tty_port *tport = &port->state->port;
unsigned long flags;
unsigned int txlen;
+ unsigned char *tail;
if (unlikely(port->x_char)) {
sc16is7xx_port_write(port, SC16IS7XX_THR_REG, port->x_char);
@@ -718,8 +718,9 @@ static void sc16is7xx_handle_tx(struct uart_port *port)
txlen = 0;
}
- txlen = uart_fifo_out(port, s->buf, txlen);
- sc16is7xx_fifo_write(port, s->buf, txlen);
+ txlen = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail, txlen);
+ sc16is7xx_fifo_write(port, tail, txlen);
+ uart_xmit_advance(port, txlen);
uart_port_lock_irqsave(port, &flags);
if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
--
2.39.2
The patch below does not apply to the 6.10-stable tree.
If someone wants it applied there, or to any other stable or longterm
tree, then please email the backport, including the original git commit
id to <stable(a)vger.kernel.org>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.10.y
git checkout FETCH_HEAD
git cherry-pick -x e528be3c87be953b73e7826a2d7e4b837cbad39d
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<stable(a)vger.kernel.org>' --in-reply-to '2024072302-policy-spleen-3156@gregkh' --subject-prefix 'PATCH 6.10.y' HEAD^..
Possible dependencies:
e528be3c87be ("thermal: core: Allow thermal zones to tell the core to ignore them")
7c8267275de6 ("Merge git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net")
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From e528be3c87be953b73e7826a2d7e4b837cbad39d Mon Sep 17 00:00:00 2001
From: "Rafael J. Wysocki" <rafael.j.wysocki(a)intel.com>
Date: Wed, 17 Jul 2024 21:45:02 +0200
Subject: [PATCH] thermal: core: Allow thermal zones to tell the core to ignore
them
The iwlwifi wireless driver registers a thermal zone that is only needed
when the network interface handled by it is up and it wants that thermal
zone to be effectively ignored by the core otherwise.
Before commit a8a261774466 ("thermal: core: Call monitor_thermal_zone()
if zone temperature is invalid") that could be achieved by returning
an error code from the thermal zone's .get_temp() callback because the
core did not really handle errors returned by it almost at all.
However, commit a8a261774466 made the core attempt to recover from the
situation in which the temperature of a thermal zone cannot be
determined due to errors returned by its .get_temp() and is always
invalid from the core's perspective.
That was done because there are thermal zones in which .get_temp()
returns errors to start with due to some difficulties related to the
initialization ordering, but then it will start to produce valid
temperature values at one point.
Unfortunately, the simple approach taken by commit a8a261774466,
which is to poll the thermal zone periodically until its .get_temp()
callback starts to return valid temperature values, is at odds with
the special thermal zone in iwlwifi in which .get_temp() may always
return an error because its network interface may always be down. If
that happens, every attempt to invoke the thermal zone's .get_temp()
callback resulting in an error causes the thermal core to print a
dev_warn() message to the kernel log which is super-noisy.
To address this problem, make the core handle the case in which
.get_temp() returns 0, but the temperature value returned by it
is not actually valid, in a special way. Namely, make the core
completely ignore the invalid temperature value coming from
.get_temp() in that case, which requires folding in
update_temperature() into its caller and a few related changes.
On the iwlwifi side, modify iwl_mvm_tzone_get_temp() to return 0
and put THERMAL_TEMP_INVALID into the temperature return memory
location instead of returning an error when the firmware is not
running or it is not of the right type.
Also, to clearly separate the handling of invalid temperature
values from the thermal zone initialization, introduce a special
THERMAL_TEMP_INIT value specifically for the latter purpose.
Fixes: a8a261774466 ("thermal: core: Call monitor_thermal_zone() if zone temperature is invalid")
Closes: https://lore.kernel.org/linux-pm/20240715044527.GA1544@sol.localdomain/
Reported-by: Eric Biggers <ebiggers(a)kernel.org>
Reported-by: Stefan Lippers-Hollmann <s.l-h(a)gmx.de>
Link: https://bugzilla.kernel.org/show_bug.cgi?id=201761
Tested-by: Oleksandr Natalenko <oleksandr(a)natalenko.name>
Tested-by: Stefan Lippers-Hollmann <s.l-h(a)gmx.de>
Cc: 6.10+ <stable(a)vger.kernel.org> # 6.10+
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki(a)intel.com>
Link: https://patch.msgid.link/4950004.31r3eYUQgx@rjwysocki.net
[ rjw: Rebased on top of the current mainline ]
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki(a)intel.com>
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c
index ed0796aff722..d92470960b38 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c
@@ -621,8 +621,14 @@ static int iwl_mvm_tzone_get_temp(struct thermal_zone_device *device,
guard(mvm)(mvm);
if (!iwl_mvm_firmware_running(mvm) ||
- mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR)
- return -ENODATA;
+ mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR) {
+ /*
+ * Tell the core that there is no valid temperature value to
+ * return, but it need not worry about this.
+ */
+ *temperature = THERMAL_TEMP_INVALID;
+ return 0;
+ }
ret = iwl_mvm_get_temp(mvm, &temp);
if (ret)
diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
index 8795187fbc52..f6e700e48aad 100644
--- a/drivers/thermal/thermal_core.c
+++ b/drivers/thermal/thermal_core.c
@@ -300,8 +300,6 @@ static void monitor_thermal_zone(struct thermal_zone_device *tz)
thermal_zone_device_set_polling(tz, tz->passive_delay_jiffies);
else if (tz->polling_delay_jiffies)
thermal_zone_device_set_polling(tz, tz->polling_delay_jiffies);
- else if (tz->temperature == THERMAL_TEMP_INVALID)
- thermal_zone_device_set_polling(tz, msecs_to_jiffies(THERMAL_RECHECK_DELAY_MS));
}
static struct thermal_governor *thermal_get_tz_governor(struct thermal_zone_device *tz)
@@ -382,7 +380,7 @@ static void handle_thermal_trip(struct thermal_zone_device *tz,
td->threshold = trip->temperature;
if (tz->last_temperature >= old_threshold &&
- tz->last_temperature != THERMAL_TEMP_INVALID) {
+ tz->last_temperature != THERMAL_TEMP_INIT) {
/*
* Mitigation is under way, so it needs to stop if the zone
* temperature falls below the low temperature of the trip.
@@ -417,27 +415,6 @@ static void handle_thermal_trip(struct thermal_zone_device *tz,
}
}
-static void update_temperature(struct thermal_zone_device *tz)
-{
- int temp, ret;
-
- ret = __thermal_zone_get_temp(tz, &temp);
- if (ret) {
- if (ret != -EAGAIN)
- dev_warn(&tz->device,
- "failed to read out thermal zone (%d)\n",
- ret);
- return;
- }
-
- tz->last_temperature = tz->temperature;
- tz->temperature = temp;
-
- trace_thermal_temperature(tz);
-
- thermal_genl_sampling_temp(tz->id, temp);
-}
-
static void thermal_zone_device_check(struct work_struct *work)
{
struct thermal_zone_device *tz = container_of(work, struct
@@ -452,7 +429,7 @@ static void thermal_zone_device_init(struct thermal_zone_device *tz)
INIT_DELAYED_WORK(&tz->poll_queue, thermal_zone_device_check);
- tz->temperature = THERMAL_TEMP_INVALID;
+ tz->temperature = THERMAL_TEMP_INIT;
tz->passive = 0;
tz->prev_low_trip = -INT_MAX;
tz->prev_high_trip = INT_MAX;
@@ -504,6 +481,7 @@ void __thermal_zone_device_update(struct thermal_zone_device *tz,
struct thermal_trip_desc *td;
LIST_HEAD(way_down_list);
LIST_HEAD(way_up_list);
+ int temp, ret;
if (tz->suspended)
return;
@@ -511,10 +489,29 @@ void __thermal_zone_device_update(struct thermal_zone_device *tz,
if (!thermal_zone_device_is_enabled(tz))
return;
- update_temperature(tz);
+ ret = __thermal_zone_get_temp(tz, &temp);
+ if (ret) {
+ if (ret != -EAGAIN)
+ dev_info(&tz->device, "Temperature check failed (%d)\n", ret);
- if (tz->temperature == THERMAL_TEMP_INVALID)
+ thermal_zone_device_set_polling(tz, msecs_to_jiffies(THERMAL_RECHECK_DELAY_MS));
+ return;
+ } else if (temp <= THERMAL_TEMP_INVALID) {
+ /*
+ * Special case: No valid temperature value is available, but
+ * the zone owner does not want the core to do anything about
+ * it. Continue regular zone polling if needed, so that this
+ * function can be called again, but skip everything else.
+ */
goto monitor;
+ }
+
+ tz->last_temperature = tz->temperature;
+ tz->temperature = temp;
+
+ trace_thermal_temperature(tz);
+
+ thermal_genl_sampling_temp(tz->id, temp);
tz->notify_event = event;
diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h
index 30c0e78859a7..ba8e6fc807ca 100644
--- a/drivers/thermal/thermal_core.h
+++ b/drivers/thermal/thermal_core.h
@@ -133,6 +133,9 @@ struct thermal_zone_device {
struct thermal_trip_desc trips[] __counted_by(num_trips);
};
+/* Initial thermal zone temperature. */
+#define THERMAL_TEMP_INIT INT_MIN
+
/*
* Default delay after a failing thermal zone temperature check before
* attempting to check it again.
diff --git a/drivers/thermal/thermal_helpers.c b/drivers/thermal/thermal_helpers.c
index 81e019493557..aedb8369e2aa 100644
--- a/drivers/thermal/thermal_helpers.c
+++ b/drivers/thermal/thermal_helpers.c
@@ -163,6 +163,8 @@ int thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp)
}
ret = __thermal_zone_get_temp(tz, temp);
+ if (!ret && *temp <= THERMAL_TEMP_INVALID)
+ ret = -ENODATA;
unlock:
mutex_unlock(&tz->lock);
Commit 310d6c15e910 ("mm/damon/core: merge regions aggressively when
max_nr_regions") causes a build warning and a build failure [1] on
5.15.y. Those are due to
1) unnecessarily strict type check from max(), and
2) use of not-yet-introduced damon_ctx->attrs field, respectively.
Fix the warning by backporting a minmax.h upstream commit that made the
type check less strict for unnecessary case, and upstream commits that
it depends on.
Note that all patches except the fourth one ("minmax: fix header
inclusions") are clean cherry-picks of upstream commit. For the fourth
one, minor conflict resolving was needed.
Also, the last patch, which is the backport of the DAMON fix, was
cleanly cherry-picked, but added manual fix for the build failure.
[1] https://lore.kernel.org/2024071532-pebble-jailhouse-48b2@gregkh
Andy Shevchenko (1):
minmax: fix header inclusions
Bart Van Assche (1):
tracing: Define the is_signed_type() macro once
David Laight (3):
minmax: allow min()/max()/clamp() if the arguments have the same
signedness.
minmax: allow comparisons of 'int' against 'unsigned char/short'
minmax: relax check to allow comparison between unsigned arguments and
signed constants
Jason A. Donenfeld (2):
minmax: sanity check constant bounds when clamping
minmax: clamp more efficiently by avoiding extra comparison
SeongJae Park (1):
mm/damon/core: merge regions aggressively when max_nr_regions is unmet
include/linux/compiler.h | 6 +++
include/linux/minmax.h | 89 ++++++++++++++++++++++++++----------
include/linux/overflow.h | 1 -
include/linux/trace_events.h | 2 -
mm/damon/core.c | 23 ++++++++--
5 files changed, 90 insertions(+), 31 deletions(-)
base-commit: 4d1b7f1bf3858ed48a98c004bda5fdff2cdf13c8
--
2.39.2
Commit 310d6c15e910 ("mm/damon/core: merge regions aggressively when
max_nr_regions") causes a build warning [1] on 6.1.y. That was due to
unnecessarily strict type check from max().
Fix the warning by backporting a minmax.h upstream commit that made the
type check less strict for unnecessary case, and upstream commits that
it depends on.
Note that all patches except the third one ("minmax: fix header
inclusions") are clean cherry-picks of upstream commit. For the third
one, a minor conflict fix was needed.
[1] https://lore.kernel.org/2024071519-janitor-robe-779f@gregkh
Andy Shevchenko (1):
minmax: fix header inclusions
David Laight (3):
minmax: allow min()/max()/clamp() if the arguments have the same
signedness.
minmax: allow comparisons of 'int' against 'unsigned char/short'
minmax: relax check to allow comparison between unsigned arguments and
signed constants
Jason A. Donenfeld (2):
minmax: sanity check constant bounds when clamping
minmax: clamp more efficiently by avoiding extra comparison
SeongJae Park (1):
mm/damon/core: merge regions aggressively when max_nr_regions is unmet
include/linux/minmax.h | 89 ++++++++++++++++++++++++++++++------------
mm/damon/core.c | 21 +++++++++-
2 files changed, 83 insertions(+), 27 deletions(-)
base-commit: 291e563ecab1ea89c70172ecf0d6bff7b725d3cb
--
2.39.2
This is the start of the stable review cycle for the 6.10.1 release.
There are 9 patches in this series, all will be posted as a response
to this one. If anyone has any issues with these being applied, please
let me know.
Responses should be made by Thu, 25 Jul 2024 11:40:39 +0000.
Anything received after that time might be too late.
The whole patch series can be found in one patch at:
https://www.kernel.org/pub/linux/kernel/v6.x/stable-review/patch-6.10.1-rc1…
or in the git tree and branch at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.git linux-6.10.y
and the diffstat can be found below.
thanks,
greg k-h
-------------
Pseudo-Shortlog of commits:
Greg Kroah-Hartman <gregkh(a)linuxfoundation.org>
Linux 6.10.1-rc1
Richard Fitzgerald <rf(a)opensource.cirrus.com>
ASoC: cs35l56: Limit Speaker Volume to +12dB maximum
Richard Fitzgerald <rf(a)opensource.cirrus.com>
ASoC: cs35l56: Use header defines for Speaker Volume control definition
Hao Ge <gehao(a)kylinos.cn>
tpm: Use auth only after NULL check in tpm_buf_check_hmac_response()
David Howells <dhowells(a)redhat.com>
cifs: Fix setting of zero_point after DIO write
David Howells <dhowells(a)redhat.com>
cifs: Fix server re-repick on subrequest retry
Steve French <stfrench(a)microsoft.com>
cifs: fix noisy message on copy_file_range
David Howells <dhowells(a)redhat.com>
cifs: Fix missing fscache invalidation
David Howells <dhowells(a)redhat.com>
cifs: Fix missing error code set
Kees Cook <kees(a)kernel.org>
ext4: use memtostr_pad() for s_volume_name
-------------
Diffstat:
Makefile | 4 ++--
drivers/char/tpm/tpm2-sessions.c | 5 +++--
fs/ext4/ext4.h | 2 +-
fs/ext4/ioctl.c | 2 +-
fs/smb/client/cifsfs.c | 2 +-
fs/smb/client/file.c | 21 +++++++++++++++++----
fs/smb/client/smb2pdu.c | 3 ---
include/sound/cs35l56.h | 2 +-
sound/soc/codecs/cs35l56.c | 6 +++++-
9 files changed, 31 insertions(+), 16 deletions(-)
Patch series to limit the upper range of the CS35L56 volume control to
+12 dB.
These commits were not marked 'Fixes' because they were thought to be only
a cosmetic issue. The user could reduce the volume to a usable value.
But for some complex audio topologies with SOF Audio DSP + CS42L43 +
multiple CS35L56 it has turned out to be not obvious to the user what the
problem actually is and what to do to fix it. As support for these
topologies went into 6.10 we would like this series to be applied to 6.10.
Richard Fitzgerald (2):
ASoC: cs35l56: Use header defines for Speaker Volume control
definition
ASoC: cs35l56: Limit Speaker Volume to +12dB maximum
include/sound/cs35l56.h | 2 +-
sound/soc/codecs/cs35l56.c | 6 +++++-
2 files changed, 6 insertions(+), 2 deletions(-)
--
2.39.2
On 23.07.24 06:11, Theodore Ts'o wrote:
> On Mon, Jul 22, 2024 at 12:06:59AM -0700, Kees Cook wrote:
>>> Is strscpy_pad appropriate if the @src parameter itself is a fixed
>>> length char[16] which isn't null terminated when the label itself is 16
>>> chars long?
>>
>> Nope; it needed memtostr_pad(). I sent the fix back at the end of May, but it only just recently landed:
>> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?…
>
> Yeah, sorry, I was on vacation for 3.5 weeks starting just before
> Memorial day, and it took me a while to get caught up. Unfortunately,
> I missed the bug in the strncpy extirpation patch, and it was't
> something that our regression tests caught. (Sometimes, the
> old/deprecated ways are just more reliable; all of ext4's strncpy()
> calls were working and had been correct for decades. :-P )
>
> Anyway, Kees's bugfix is in Linus's tree, and it should be shortly be
> making its way to -stable.
Adding Greg and the stable list to the list of recipients: given that we
already have two reports about trouble due to this[1] he might want to
fast-track the fix (be27cd64461c45 ("ext4: use memtostr_pad() for
s_volume_name")) to 6.10.y, as it's not queued yet -- at least afaics
from looking at
https://git.kernel.org/pub/scm/linux/kernel/git/stable/stable-queue.git/tre…
Ciao, Thorsten
[1] https://bugzilla.kernel.org/show_bug.cgi?id=219072 and
https://bugzilla.kernel.org/show_bug.cgi?id=219078
From: Bailey Forrest <bcf(a)google.com>
The NIC requires each TSO segment to not span more than 10
descriptors. gve_can_send_tso() performs this check. However,
the current code misses an edge case when a TSO skb has a large
frag that needs to be split into multiple descriptors, causing
the 10 descriptor limit per TSO-segment to be exceeded. This
change fixes the edge case.
Fixes: a57e5de476be ("gve: DQO: Add TX path")
Signed-off-by: Praveen Kaligineedi <pkaligineedi(a)google.com>
Signed-off-by: Bailey Forrest <bcf(a)google.com>
Reviewed-by: Jeroen de Borst <jeroendb(a)google.com>
---
drivers/net/ethernet/google/gve/gve_tx_dqo.c | 22 +++++++++++++++++++-
1 file changed, 21 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/google/gve/gve_tx_dqo.c b/drivers/net/ethernet/google/gve/gve_tx_dqo.c
index 0b3cca3fc792..dc39dc481f21 100644
--- a/drivers/net/ethernet/google/gve/gve_tx_dqo.c
+++ b/drivers/net/ethernet/google/gve/gve_tx_dqo.c
@@ -866,22 +866,42 @@ static bool gve_can_send_tso(const struct sk_buff *skb)
const int header_len = skb_tcp_all_headers(skb);
const int gso_size = shinfo->gso_size;
int cur_seg_num_bufs;
+ int last_frag_size;
int cur_seg_size;
int i;
cur_seg_size = skb_headlen(skb) - header_len;
+ last_frag_size = skb_headlen(skb);
cur_seg_num_bufs = cur_seg_size > 0;
for (i = 0; i < shinfo->nr_frags; i++) {
if (cur_seg_size >= gso_size) {
cur_seg_size %= gso_size;
cur_seg_num_bufs = cur_seg_size > 0;
+
+ /* If the last buffer is split in the middle of a TSO
+ * segment, then it will count as two descriptors.
+ */
+ if (last_frag_size > GVE_TX_MAX_BUF_SIZE_DQO) {
+ int last_frag_remain = last_frag_size %
+ GVE_TX_MAX_BUF_SIZE_DQO;
+
+ /* If the last frag was evenly divisible by
+ * GVE_TX_MAX_BUF_SIZE_DQO, then it will not be
+ * split in the current segment.
+ */
+ if (last_frag_remain &&
+ cur_seg_size > last_frag_remain) {
+ cur_seg_num_bufs++;
+ }
+ }
}
if (unlikely(++cur_seg_num_bufs > max_bufs_per_seg))
return false;
- cur_seg_size += skb_frag_size(&shinfo->frags[i]);
+ last_frag_size = skb_frag_size(&shinfo->frags[i]);
+ cur_seg_size += last_frag_size;
}
return true;
--
2.45.2.993.g49e7a77208-goog