From 1bbfce1e30faf028ec7186eeb3984f685ec609df Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 1 Jul 2019 17:46:51 +0200 Subject: [PATCH] 4.19-stable patches added patches: eeprom-at24-fix-unexpected-timeout-under-high-load.patch --- ...x-unexpected-timeout-under-high-load.patch | 122 ++++++++++++++++++ queue-4.19/series | 1 + 2 files changed, 123 insertions(+) create mode 100644 queue-4.19/eeprom-at24-fix-unexpected-timeout-under-high-load.patch diff --git a/queue-4.19/eeprom-at24-fix-unexpected-timeout-under-high-load.patch b/queue-4.19/eeprom-at24-fix-unexpected-timeout-under-high-load.patch new file mode 100644 index 00000000000..4c9f3572287 --- /dev/null +++ b/queue-4.19/eeprom-at24-fix-unexpected-timeout-under-high-load.patch @@ -0,0 +1,122 @@ +From 9a9e295e7c5c0409c020088b0ae017e6c2b7df6e Mon Sep 17 00:00:00 2001 +From: Wang Xin +Date: Thu, 16 Aug 2018 19:45:34 +0200 +Subject: eeprom: at24: fix unexpected timeout under high load + +From: Wang Xin + +commit 9a9e295e7c5c0409c020088b0ae017e6c2b7df6e upstream. + +Within at24_loop_until_timeout the timestamp used for timeout checking +is recorded after the I2C transfer and sleep_range(). Under high CPU +load either the execution time for I2C transfer or sleep_range() could +actually be larger than the timeout value. Worst case the I2C transfer +is only tried once because the loop will exit due to the timeout +although the EEPROM is now ready. + +To fix this issue the timestamp is recorded at the beginning of each +iteration. That is, before I2C transfer and sleep. Then the timeout +is actually checked against the timestamp of the previous iteration. +This makes sure that even if the timeout is reached, there is still one +more chance to try the I2C transfer in case the EEPROM is ready. + +Example: + +If you have a system which combines high CPU load with repeated EEPROM +writes you will run into the following scenario. + + - System makes a successful regmap_bulk_write() to EEPROM. + - System wants to perform another write to EEPROM but EEPROM is still + busy with the last write. + - Because of high CPU load the usleep_range() will sleep more than + 25 ms (at24_write_timeout). + - Within the over-long sleeping the EEPROM finished the previous write + operation and is ready again. + - at24_loop_until_timeout() will detect timeout and won't try to write. + +Signed-off-by: Wang Xin +Signed-off-by: Mark Jonas +Signed-off-by: Bartosz Golaszewski +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/misc/eeprom/at24.c | 43 ++++++++++++++++++++++--------------------- + 1 file changed, 22 insertions(+), 21 deletions(-) + +--- a/drivers/misc/eeprom/at24.c ++++ b/drivers/misc/eeprom/at24.c +@@ -106,23 +106,6 @@ static unsigned int at24_write_timeout = + module_param_named(write_timeout, at24_write_timeout, uint, 0); + MODULE_PARM_DESC(at24_write_timeout, "Time (in ms) to try writes (default 25)"); + +-/* +- * Both reads and writes fail if the previous write didn't complete yet. This +- * macro loops a few times waiting at least long enough for one entire page +- * write to work while making sure that at least one iteration is run before +- * checking the break condition. +- * +- * It takes two parameters: a variable in which the future timeout in jiffies +- * will be stored and a temporary variable holding the time of the last +- * iteration of processing the request. Both should be unsigned integers +- * holding at least 32 bits. +- */ +-#define at24_loop_until_timeout(tout, op_time) \ +- for (tout = jiffies + msecs_to_jiffies(at24_write_timeout), \ +- op_time = 0; \ +- op_time ? time_before(op_time, tout) : true; \ +- usleep_range(1000, 1500), op_time = jiffies) +- + struct at24_chip_data { + /* + * these fields mirror their equivalents in +@@ -311,13 +294,22 @@ static ssize_t at24_regmap_read(struct a + /* adjust offset for mac and serial read ops */ + offset += at24->offset_adj; + +- at24_loop_until_timeout(timeout, read_time) { ++ timeout = jiffies + msecs_to_jiffies(at24_write_timeout); ++ do { ++ /* ++ * The timestamp shall be taken before the actual operation ++ * to avoid a premature timeout in case of high CPU load. ++ */ ++ read_time = jiffies; ++ + ret = regmap_bulk_read(regmap, offset, buf, count); + dev_dbg(&client->dev, "read %zu@%d --> %d (%ld)\n", + count, offset, ret, jiffies); + if (!ret) + return count; +- } ++ ++ usleep_range(1000, 1500); ++ } while (time_before(read_time, timeout)); + + return -ETIMEDOUT; + } +@@ -361,14 +353,23 @@ static ssize_t at24_regmap_write(struct + regmap = at24_client->regmap; + client = at24_client->client; + count = at24_adjust_write_count(at24, offset, count); ++ timeout = jiffies + msecs_to_jiffies(at24_write_timeout); ++ ++ do { ++ /* ++ * The timestamp shall be taken before the actual operation ++ * to avoid a premature timeout in case of high CPU load. ++ */ ++ write_time = jiffies; + +- at24_loop_until_timeout(timeout, write_time) { + ret = regmap_bulk_write(regmap, offset, buf, count); + dev_dbg(&client->dev, "write %zu@%d --> %d (%ld)\n", + count, offset, ret, jiffies); + if (!ret) + return count; +- } ++ ++ usleep_range(1000, 1500); ++ } while (time_before(write_time, timeout)); + + return -ETIMEDOUT; + } diff --git a/queue-4.19/series b/queue-4.19/series index 3cf6b7629a1..e3bdc90ea41 100644 --- a/queue-4.19/series +++ b/queue-4.19/series @@ -46,3 +46,4 @@ nfs-flexfiles-use-the-correct-tcp-timeout-for-flexfiles-i-o.patch cpu-speculation-warn-on-unsupported-mitigations-parameter.patch sunrpc-clean-up-initialisation-of-the-struct-rpc_rqst.patch irqchip-mips-gic-use-the-correct-local-interrupt-map-registers.patch +eeprom-at24-fix-unexpected-timeout-under-high-load.patch -- 2.47.2