Received: by 2002:a25:ad19:0:0:0:0:0 with SMTP id y25csp3053744ybi; Tue, 2 Jul 2019 01:13:18 -0700 (PDT) X-Google-Smtp-Source: APXvYqyebd99aI2qmFULLRFwRe08Cc1y4BkIx3BGGu8C/pgNa8zBFVUWUh1hSWzw5HVr+gqpxMtw X-Received: by 2002:a17:90b:d8a:: with SMTP id bg10mr4165154pjb.92.1562055198294; Tue, 02 Jul 2019 01:13:18 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1562055198; cv=none; d=google.com; s=arc-20160816; b=CIX9IaMpuNGP5LRfU1We79MdvxSFcg2T3JiLBpY9vJWHYShpVKSe6jYUSE+SJXgcTX LAkgtQ61h2RfgfUwU6bhGBwTJcns4XJv5+SjNXkvtSTV+UvQ6G/t3HAr/0/llKO0meVg U2c6oPutMyMJ5oKKLspxjyQkM33vJ136pSPrEGbwdqsTXA+VWvAdbEgRI7GJZQXrNewr g5Dgy8X6S3KjHJconv7TJh1l8AYSZERUFB2FaURYNdrcobAjkE4i/oyK1o6pt0dYSOhg R1frEwtAn9D9439xKpbQ9CQm/3yCzZqZLX+pjxTTADnsTCLPJx2zFC/2kYdJxzCPR/O2 1LkA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :user-agent:references:in-reply-to:message-id:date:subject:cc:to :from:dkim-signature; bh=zOUCvbd69gKqlLqfhMhrfLKukpJAV2p448Nu2EUgeeU=; b=y3gVc9DCHLYIPUr8wXCszRkyp1sDMQgR8hydDyhgyq3v6P4zeZ93XYGGDpeTHkEdtW U0cgyrHXPzUjrT9GBNXuGbTzkYABKGOJnU/LjsQWu4e34rbSaibJanqxsQDrpv1VGezx HKlgKw4VcBCUyY31adEra7zeHCa/ajHCKdO3Qims4scbOyVaW7rFCfXgE2wD+GtUVCHz OTaQpCpsvpw+2CbwpKnCwYBv8/CVpzPWfVcM7W3xlKcUtTOF/y1TAiYxTr1KjfXAB0rr pUdMf7ARNpEWRpEHSey2JPO91mpzdh3PnSfbRGO4oCMCEnBebpwoP9/EgLvrFgpcxIKx GqJQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@kernel.org header.s=default header.b="f5oZ2/G5"; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id y63si3157957pgd.403.2019.07.02.01.13.03; Tue, 02 Jul 2019 01:13:18 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@kernel.org header.s=default header.b="f5oZ2/G5"; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728899AbfGBIKu (ORCPT + 99 others); Tue, 2 Jul 2019 04:10:50 -0400 Received: from mail.kernel.org ([198.145.29.99]:59616 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728895AbfGBIKq (ORCPT ); Tue, 2 Jul 2019 04:10:46 -0400 Received: from localhost (83-86-89-107.cable.dynamic.v4.ziggo.nl [83.86.89.107]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPSA id 04286206A2; Tue, 2 Jul 2019 08:10:44 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=default; t=1562055045; bh=Vx9DkmCnEUQSOhGDZdt1CgFSoxs+WL6oyvc7wV8s+vs=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=f5oZ2/G5ZU9hI06PnLs3Ksb7M1kS7d0Px34NUtJUPg6R2HLA/MP2Gk4m0gmHEciML YMTk3qr0rePtM7WkzmgOlnTG3/zuT0QFMeTF/Lft4r0Ce+q5HjusSGGQONeaX1bWn9 xNwul39M9G4Y+QA74sB9ERAehj3uSy+QM0xXcSio= From: Greg Kroah-Hartman To: linux-kernel@vger.kernel.org Cc: Greg Kroah-Hartman , stable@vger.kernel.org, Wang Xin , Mark Jonas , Bartosz Golaszewski Subject: [PATCH 4.14 26/43] eeprom: at24: fix unexpected timeout under high load Date: Tue, 2 Jul 2019 10:02:06 +0200 Message-Id: <20190702080125.196152902@linuxfoundation.org> X-Mailer: git-send-email 2.22.0 In-Reply-To: <20190702080123.904399496@linuxfoundation.org> References: <20190702080123.904399496@linuxfoundation.org> User-Agent: quilt/0.66 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org 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 | 107 ++++++++++++++++++++++++++++++++------------- 1 file changed, 77 insertions(+), 30 deletions(-) --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c @@ -113,22 +113,6 @@ MODULE_PARM_DESC(write_timeout, "Time (i ((1 << AT24_SIZE_FLAGS | (_flags)) \ << AT24_SIZE_BYTELEN | ilog2(_len)) -/* - * 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 loop_until_timeout(tout, op_time) \ - for (tout = jiffies + msecs_to_jiffies(write_timeout), op_time = 0; \ - op_time ? time_before(op_time, tout) : true; \ - usleep_range(1000, 1500), op_time = jiffies) - static const struct i2c_device_id at24_ids[] = { /* needs 8 addresses as A0-A2 are ignored */ { "24c00", AT24_DEVICE_MAGIC(128 / 8, AT24_FLAG_TAKE8ADDR) }, @@ -234,7 +218,14 @@ static ssize_t at24_eeprom_read_smbus(st if (count > I2C_SMBUS_BLOCK_MAX) count = I2C_SMBUS_BLOCK_MAX; - loop_until_timeout(timeout, read_time) { + timeout = jiffies + msecs_to_jiffies(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; + status = i2c_smbus_read_i2c_block_data_or_emulated(client, offset, count, buf); @@ -244,7 +235,9 @@ static ssize_t at24_eeprom_read_smbus(st if (status == count) return count; - } + + usleep_range(1000, 1500); + } while (time_before(read_time, timeout)); return -ETIMEDOUT; } @@ -284,7 +277,14 @@ static ssize_t at24_eeprom_read_i2c(stru msg[1].buf = buf; msg[1].len = count; - loop_until_timeout(timeout, read_time) { + timeout = jiffies + msecs_to_jiffies(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; + status = i2c_transfer(client->adapter, msg, 2); if (status == 2) status = count; @@ -294,7 +294,9 @@ static ssize_t at24_eeprom_read_i2c(stru if (status == count) return count; - } + + usleep_range(1000, 1500); + } while (time_before(read_time, timeout)); return -ETIMEDOUT; } @@ -343,11 +345,20 @@ static ssize_t at24_eeprom_read_serial(s msg[1].buf = buf; msg[1].len = count; - loop_until_timeout(timeout, read_time) { + timeout = jiffies + msecs_to_jiffies(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; + status = i2c_transfer(client->adapter, msg, 2); if (status == 2) return count; - } + + usleep_range(1000, 1500); + } while (time_before(read_time, timeout)); return -ETIMEDOUT; } @@ -374,11 +385,20 @@ static ssize_t at24_eeprom_read_mac(stru msg[1].buf = buf; msg[1].len = count; - loop_until_timeout(timeout, read_time) { + timeout = jiffies + msecs_to_jiffies(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; + status = i2c_transfer(client->adapter, msg, 2); if (status == 2) return count; - } + + usleep_range(1000, 1500); + } while (time_before(read_time, timeout)); return -ETIMEDOUT; } @@ -420,7 +440,14 @@ static ssize_t at24_eeprom_write_smbus_b client = at24_translate_offset(at24, &offset); count = at24_adjust_write_count(at24, offset, count); - loop_until_timeout(timeout, write_time) { + timeout = jiffies + msecs_to_jiffies(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; + status = i2c_smbus_write_i2c_block_data(client, offset, count, buf); if (status == 0) @@ -431,7 +458,9 @@ static ssize_t at24_eeprom_write_smbus_b if (status == count) return count; - } + + usleep_range(1000, 1500); + } while (time_before(write_time, timeout)); return -ETIMEDOUT; } @@ -446,7 +475,14 @@ static ssize_t at24_eeprom_write_smbus_b client = at24_translate_offset(at24, &offset); - loop_until_timeout(timeout, write_time) { + timeout = jiffies + msecs_to_jiffies(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; + status = i2c_smbus_write_byte_data(client, offset, buf[0]); if (status == 0) status = count; @@ -456,7 +492,9 @@ static ssize_t at24_eeprom_write_smbus_b if (status == count) return count; - } + + usleep_range(1000, 1500); + } while (time_before(write_time, timeout)); return -ETIMEDOUT; } @@ -485,7 +523,14 @@ static ssize_t at24_eeprom_write_i2c(str memcpy(&msg.buf[i], buf, count); msg.len = i + count; - loop_until_timeout(timeout, write_time) { + timeout = jiffies + msecs_to_jiffies(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; + status = i2c_transfer(client->adapter, &msg, 1); if (status == 1) status = count; @@ -495,7 +540,9 @@ static ssize_t at24_eeprom_write_i2c(str if (status == count) return count; - } + + usleep_range(1000, 1500); + } while (time_before(write_time, timeout)); return -ETIMEDOUT; }