From: Joanne Koong <joannelkoong(a)gmail.com>
[ Upstream commit 7aa6bc3e8766990824f66ca76c19596ce10daf3e ]
iomap_adjust_read_range() assumes that the position and length passed in
are block-aligned. This is not always the case however, as shown in the
syzbot generated case for erofs. This causes too many bytes to be
skipped for uptodate blocks, which results in returning the incorrect
position and length to read in. If all the blocks are uptodate, this
underflows length and returns a position beyond the folio.
Fix the calculation to also take into account the block offset when
calculating how many bytes can be skipped for uptodate blocks.
Signed-off-by: Joanne Koong <joannelkoong(a)gmail.com>
Tested-by: syzbot(a)syzkaller.appspotmail.com
Reviewed-by: Brian Foster <bfoster(a)redhat.com>
Reviewed-by: Christoph Hellwig <hch(a)lst.de>
Signed-off-by: Christian Brauner <brauner(a)kernel.org>
Signed-off-by: Sasha Levin <sashal(a)kernel.org>
---
LLM Generated explanations, may be completely bogus:
Now I have all the information needed for a comprehensive analysis. Let
me compile my findings.
---
## COMPREHENSIVE ANALYSIS: iomap_adjust_read_range() Bug Fix
### 1. COMMIT MESSAGE ANALYSIS
**Subject:** `iomap: adjust read range correctly for non-block-aligned
positions`
**Key Points from Message:**
- The function `iomap_adjust_read_range()` **assumes block-aligned
positions**, but this assumption is violated
- Bug was discovered via **syzbot testing with erofs** filesystem
- Causes "too many bytes to be skipped for uptodate blocks"
- Results in **incorrect position and length to read**
- If all blocks are uptodate: **underflows length** and **returns
position beyond the folio**
**Tags Analysis:**
- ✅ `Tested-by: syzbot(a)syzkaller.appspotmail.com` - Automated testing
confirmed
- ✅ `Reviewed-by: Brian Foster <bfoster(a)redhat.com>` - Red Hat
filesystem expert
- ✅ `Reviewed-by: Christoph Hellwig <hch(a)lst.de>` - Major iomap author
- ✅ `Signed-off-by: Christian Brauner <brauner(a)kernel.org>` - VFS
maintainer
- ❌ **No `Cc: stable(a)vger.kernel.org`** tag
- ❌ **No `Fixes:` tag** pointing to original buggy commit
### 2. CODE CHANGE ANALYSIS
**Bug Location:** `fs/iomap/buffered-io.c`, function
`iomap_adjust_read_range()`
**The Buggy Code (lines 246-253 before fix):**
```c
for (i = first; i <= last; i++) {
if (!ifs_block_is_uptodate(ifs, i))
break;
*pos += block_size; // Bug: assumes pos is block-aligned
poff += block_size;
plen -= block_size;
first++;
}
```
**Technical Root Cause:**
When `*pos` has a sub-block offset (e.g., `pos = 512` in a 1024-byte
block):
- `first = poff >> block_bits` computes the block index correctly (block
0)
- But the loop skips a full `block_size` per block, ignoring the offset
**Example demonstrating the bug:**
- Block size: 1024 bytes
- Position: 512 (half-way into block 0)
- Block 0 is uptodate
**Old buggy calculation:**
- Skip full 1024 bytes → `pos = 1536`, overshoots by 512 bytes
- `plen -= 1024` → underflows if `plen < 1024`
**Fixed calculation:**
```c
blocks_skipped = i - first;
if (blocks_skipped) {
unsigned long block_offset = *pos & (block_size - 1); // = 512
unsigned bytes_skipped =
(blocks_skipped << block_bits) - block_offset; // = 1024 -
512 = 512
*pos += bytes_skipped; // Correct: pos = 1024
poff += bytes_skipped;
plen -= bytes_skipped;
}
```
**Consequences of the Bug:**
1. **Integer underflow**: `plen` becomes huge (wraps around as unsigned)
2. **Position beyond folio**: `*pos` can point past the folio boundary
3. **Incorrect read ranges**: Wrong data read, potential corruption
4. **Potential crashes**: Invalid memory access from bad ranges
### 3. CLASSIFICATION
- **Type:** BUG FIX (arithmetic/logic error causing incorrect
calculations)
- **NOT:** Feature addition, cleanup, or optimization
- **Severity:** HIGH - affects filesystem data integrity
- **Scope:** Core iomap buffered I/O infrastructure
### 4. SCOPE AND RISK ASSESSMENT
**Change Size:**
- 13 insertions, 6 deletions
- Single file: `fs/iomap/buffered-io.c`
- Localized to one function
**Risk Level:** LOW
- Fix is mathematically straightforward
- Does not change control flow significantly
- Well-reviewed by iomap experts
- Tested by syzbot
**Affected Subsystem:** `fs/iomap/` - mature, widely-used infrastructure
**Affected Filesystems:** Multiple major filesystems use iomap:
- **XFS** - Primary enterprise Linux filesystem
- **GFS2** - Cluster filesystem
- **erofs** - Read-only filesystem (Android)
- **zonefs** - Zoned storage filesystem
- **fuse** - User-space filesystems
### 5. USER IMPACT
**Who is affected?**
- ALL users of XFS, GFS2, erofs, zonefs, and fuse filesystems
- Particularly affects systems with:
- Block sizes smaller than page size
- Partial folio reads/writes at non-block-aligned offsets
**How severe?**
- Can cause **incorrect data reads**
- Potential **data corruption** in read paths
- Possible **kernel crashes** from invalid memory access
- **Data integrity** issues for critical filesystems
**Triggered by:** syzbot (automated fuzzing) - confirms this is a real
triggerable bug
### 6. STABILITY INDICATORS
- ✅ `Tested-by: syzbot` - Confirmed fix works
- ✅ Two expert reviewers (Brian Foster, Christoph Hellwig)
- ✅ Merged by VFS maintainer (Christian Brauner)
- ⚠️ Relatively new commit (September 2025)
### 7. DEPENDENCY CHECK
**Bug Origin:** Commit `9dc55f1389f95` ("iomap: add support for sub-
pagesize buffered I/O without buffer heads") from **v4.19-rc1** (2018)
**Dependencies for backport:**
- The fix is self-contained
- May need minor adaptation for older kernels:
- v5.x: Uses `struct iomap_page` and pages (not folios)
- v6.x: Uses `struct iomap_folio_state` and folios
- Core logic remains the same across versions
**Related commits:**
- `8e3c15ee0d292` - Minor loop syntax change (`for` → `while`), NOT a
dependency
- `9d875e0eef8ec` - Related fix for unaligned END offsets (separate
issue)
### 8. HISTORICAL CONTEXT
The bug has existed for **~6 years** (since v4.19). It was latent
because:
- Most callers pass block-aligned positions
- erofs triggered an edge case with non-aligned positions
- syzbot's extensive testing exposed it
The original code assumed callers would always pass block-aligned
positions, which is a reasonable assumption violated in specific edge
cases.
---
## BACKPORT RECOMMENDATION
### Arguments FOR backporting:
1. **Fixes a real bug** discovered by automated testing (syzbot)
2. **Affects critical filesystems** (XFS, GFS2) used in production
3. **Small, surgical fix** - 13 lines added, 6 removed
4. **Low regression risk** - mathematically correct fix
5. **Expert-reviewed** by iomap maintainers
6. **Bug existed since v4.19** - all stable trees are affected
7. **Can cause data integrity issues** - serious for a filesystem bug
### Arguments AGAINST backporting:
1. **No `Cc: stable` tag** - maintainers didn't explicitly request
backport
2. **No `Fixes:` tag** - doesn't identify the buggy commit
3. **Relatively new commit** - hasn't had extensive mainline testing yet
4. **Needs adaptation** for older kernels (page vs folio APIs)
### Conclusion:
Despite the missing stable tags, this commit should be backported
because:
- It fixes a **real, triggerable bug** found by syzbot
- The bug can cause **incorrect data reads and potential corruption**
- It affects **major filesystems** (XFS, GFS2) used in production
environments
- The fix is **small, contained, and well-reviewed**
- The **risk of NOT fixing** (data corruption) outweighs the risk of the
fix (minimal)
The absence of `Cc: stable` may be an oversight, as this clearly meets
stable criteria.
**YES**
fs/iomap/buffered-io.c | 19 +++++++++++++------
1 file changed, 13 insertions(+), 6 deletions(-)
diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c
index 8b847a1e27f13..1c95a0a7b302d 100644
--- a/fs/iomap/buffered-io.c
+++ b/fs/iomap/buffered-io.c
@@ -240,17 +240,24 @@ static void iomap_adjust_read_range(struct inode *inode, struct folio *folio,
* to avoid reading in already uptodate ranges.
*/
if (ifs) {
- unsigned int i;
+ unsigned int i, blocks_skipped;
/* move forward for each leading block marked uptodate */
- for (i = first; i <= last; i++) {
+ for (i = first; i <= last; i++)
if (!ifs_block_is_uptodate(ifs, i))
break;
- *pos += block_size;
- poff += block_size;
- plen -= block_size;
- first++;
+
+ blocks_skipped = i - first;
+ if (blocks_skipped) {
+ unsigned long block_offset = *pos & (block_size - 1);
+ unsigned bytes_skipped =
+ (blocks_skipped << block_bits) - block_offset;
+
+ *pos += bytes_skipped;
+ poff += bytes_skipped;
+ plen -= bytes_skipped;
}
+ first = i;
/* truncate len if we find any trailing uptodate block(s) */
while (++i <= last) {
--
2.51.0
Hi,
This series intends to fix the race between the MHI stack and the MHI client
drivers due to the MHI 'auto_queue' feature. As it turns out often, the best
way to fix an issue in a feature is to drop the feature itself and this series
does exactly that.
There is no real benefit in having the 'auto_queue' feature in the MHI stack,
other than saving a few lines of code in the client drivers. Since the QRTR is
the only client driver which makes use of this feature, this series reworks the
QRTR driver to manage the buffer on its own.
Testing
=======
Tested on Qcom X1E based Lenovo Thinkpad T14s laptop with WLAN device.
Merge Strategy
==============
Since this series modifies many subsystem drivers, I'd like to get acks from
relevant subsystem maintainers and take the series through MHI tree.
Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam(a)oss.qualcomm.com>
---
Manivannan Sadhasivam (2):
net: qrtr: Drop the MHI auto_queue feature for IPCR DL channels
bus: mhi: host: Drop the auto_queue support
drivers/accel/qaic/mhi_controller.c | 44 -------------------
drivers/bus/mhi/host/init.c | 10 -----
drivers/bus/mhi/host/internal.h | 3 --
drivers/bus/mhi/host/main.c | 81 +----------------------------------
drivers/bus/mhi/host/pci_generic.c | 20 +--------
drivers/net/wireless/ath/ath11k/mhi.c | 4 --
drivers/net/wireless/ath/ath12k/mhi.c | 4 --
include/linux/mhi.h | 14 ------
net/qrtr/mhi.c | 67 ++++++++++++++++++++++++-----
9 files changed, 60 insertions(+), 187 deletions(-)
---
base-commit: 8f0b4cce4481fb22653697cced8d0d04027cb1e8
change-id: 20251217-qrtr-fix-c058251d8d1a
Best regards,
--
Manivannan Sadhasivam <manivannan.sadhasivam(a)oss.qualcomm.com>
In the alloc_ep_skb_list() failure path, the c4iw_put_ep() is
incorrectly used instead of the kfree(). Since the child_ep's
reference count hasn't been properly established at this point,
the c4iw_put_ep() won't actually free the memory, resulting in
permanent memory leak.
Fix by releasing child_ep correctly in the fail path.
Fixes: 4a740838bf44 ("RDMA/iw_cxgb4: Low resource fixes for connection manager")
Cc: stable(a)vger.kernel.org
Signed-off-by: Wentao Liang <vulab(a)iscas.ac.cn>
---
drivers/infiniband/hw/cxgb4/cm.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c
index b3b45c49077d..a09eeb48775f 100644
--- a/drivers/infiniband/hw/cxgb4/cm.c
+++ b/drivers/infiniband/hw/cxgb4/cm.c
@@ -2665,7 +2665,7 @@ static int pass_accept_req(struct c4iw_dev *dev, struct sk_buff *skb)
}
goto out;
fail:
- c4iw_put_ep(&child_ep->com);
+ kfree(child_ep);
reject:
reject_cr(dev, hwtid, skb);
out:
--
2.34.1
The sysfs buffer passed to alarms_store() is allocated with 'size + 1'
bytes and a NUL terminator is appended. However, the 'size' argument
does not account for this extra byte. The original code then allocated
'size' bytes and used strcpy() to copy 'buf', which always writes one
byte past the allocated buffer since strcpy() copies until the NUL
terminator at index 'size'.
Fix this by parsing the 'buf' parameter directly using simple_strtoll()
without allocating any intermediate memory or string copying. This
removes the overflow while simplifying the code.
Cc: stable(a)vger.kernel.org
Fixes: e2c94d6f5720 ("w1_therm: adding alarm sysfs entry")
Signed-off-by: Thorsten Blum <thorsten.blum(a)linux.dev>
---
Compile-tested only.
Changes in v5:
- Replace gotos with return size (Krzysztof)
- Link to v4: https://lore.kernel.org/lkml/20251111204422.41993-2-thorsten.blum@linux.dev/
Changes in v4:
- Use simple_strtoll because kstrtoint also parses long long internally
- Return -ERANGE in addition to -EINVAL to match kstrtoint's behavior
- Remove any changes unrelated to fixing the buffer overflow (Krzysztof)
while maintaining the same behavior and return values as before
- Link to v3: https://lore.kernel.org/lkml/20251030155614.447905-1-thorsten.blum@linux.de…
Changes in v3:
- Add integer range check for 'temp' to match kstrtoint() behavior
- Explicitly cast 'temp' to int when calling int_to_short()
- Link to v2: https://lore.kernel.org/lkml/20251029130045.70127-2-thorsten.blum@linux.dev/
Changes in v2:
- Fix buffer overflow instead of truncating the copy using strscpy()
- Parse buffer directly using simple_strtol() as suggested by David
- Update patch subject and description
- Link to v1: https://lore.kernel.org/lkml/20251017170047.114224-2-thorsten.blum@linux.de…
---
drivers/w1/slaves/w1_therm.c | 63 ++++++++++++------------------------
1 file changed, 20 insertions(+), 43 deletions(-)
diff --git a/drivers/w1/slaves/w1_therm.c b/drivers/w1/slaves/w1_therm.c
index 9ccedb3264fb..5c4e40883400 100644
--- a/drivers/w1/slaves/w1_therm.c
+++ b/drivers/w1/slaves/w1_therm.c
@@ -1836,55 +1836,36 @@ static ssize_t alarms_store(struct device *device,
struct w1_slave *sl = dev_to_w1_slave(device);
struct therm_info info;
u8 new_config_register[3]; /* array of data to be written */
- int temp, ret;
- char *token = NULL;
+ long long temp;
+ int ret = 0;
s8 tl, th; /* 1 byte per value + temp ring order */
- char *p_args, *orig;
-
- p_args = orig = kmalloc(size, GFP_KERNEL);
- /* Safe string copys as buf is const */
- if (!p_args) {
- dev_warn(device,
- "%s: error unable to allocate memory %d\n",
- __func__, -ENOMEM);
- return size;
- }
- strcpy(p_args, buf);
-
- /* Split string using space char */
- token = strsep(&p_args, " ");
-
- if (!token) {
- dev_info(device,
- "%s: error parsing args %d\n", __func__, -EINVAL);
- goto free_m;
- }
-
- /* Convert 1st entry to int */
- ret = kstrtoint (token, 10, &temp);
+ const char *p = buf;
+ char *endp;
+
+ temp = simple_strtoll(p, &endp, 10);
+ if (p == endp || *endp != ' ')
+ ret = -EINVAL;
+ else if (temp < INT_MIN || temp > INT_MAX)
+ ret = -ERANGE;
if (ret) {
dev_info(device,
"%s: error parsing args %d\n", __func__, ret);
- goto free_m;
+ return size;
}
tl = int_to_short(temp);
- /* Split string using space char */
- token = strsep(&p_args, " ");
- if (!token) {
- dev_info(device,
- "%s: error parsing args %d\n", __func__, -EINVAL);
- goto free_m;
- }
- /* Convert 2nd entry to int */
- ret = kstrtoint (token, 10, &temp);
+ p = endp + 1;
+ temp = simple_strtoll(p, &endp, 10);
+ if (p == endp)
+ ret = -EINVAL;
+ else if (temp < INT_MIN || temp > INT_MAX)
+ ret = -ERANGE;
if (ret) {
dev_info(device,
"%s: error parsing args %d\n", __func__, ret);
- goto free_m;
+ return size;
}
-
/* Prepare to cast to short by eliminating out of range values */
th = int_to_short(temp);
@@ -1905,7 +1886,7 @@ static ssize_t alarms_store(struct device *device,
dev_info(device,
"%s: error reading from the slave device %d\n",
__func__, ret);
- goto free_m;
+ return size;
}
/* Write data in the device RAM */
@@ -1913,7 +1894,7 @@ static ssize_t alarms_store(struct device *device,
dev_info(device,
"%s: Device not supported by the driver %d\n",
__func__, -ENODEV);
- goto free_m;
+ return size;
}
ret = SLAVE_SPECIFIC_FUNC(sl)->write_data(sl, new_config_register);
@@ -1922,10 +1903,6 @@ static ssize_t alarms_store(struct device *device,
"%s: error writing to the slave device %d\n",
__func__, ret);
-free_m:
- /* free allocated memory */
- kfree(orig);
-
return size;
}
--
Thorsten Blum <thorsten.blum(a)linux.dev>
GPG: 1D60 735E 8AEF 3BE4 73B6 9D84 7336 78FD 8DFE EAD4
A deadlock can occur between nfc_unregister_device() and rfkill_fop_write()
due to lock ordering inversion between device_lock and rfkill_global_mutex.
The problematic lock order is:
Thread A (rfkill_fop_write):
rfkill_fop_write()
mutex_lock(&rfkill_global_mutex)
rfkill_set_block()
nfc_rfkill_set_block()
nfc_dev_down()
device_lock(&dev->dev) <- waits for device_lock
Thread B (nfc_unregister_device):
nfc_unregister_device()
device_lock(&dev->dev)
rfkill_unregister()
mutex_lock(&rfkill_global_mutex) <- waits for rfkill_global_mutex
This creates a classic ABBA deadlock scenario.
Fix this by moving rfkill_unregister() and rfkill_destroy() outside the
device_lock critical section. Store the rfkill pointer in a local variable
before releasing the lock, then call rfkill_unregister() after releasing
device_lock.
This change is safe because rfkill_fop_write() holds rfkill_global_mutex
while calling the rfkill callbacks, and rfkill_unregister() also acquires
rfkill_global_mutex before cleanup. Therefore, rfkill_unregister() will
wait for any ongoing callback to complete before proceeding, and
device_del() is only called after rfkill_unregister() returns, preventing
any use-after-free.
The similar lock ordering in nfc_register_device() (device_lock ->
rfkill_global_mutex via rfkill_register) is safe because during
registration the device is not yet in rfkill_list, so no concurrent
rfkill operations can occur on this device.
Fixes: 3e3b5dfcd16a ("NFC: reorder the logic in nfc_{un,}register_device")
Cc: stable(a)vger.kernel.org
Reported-by: syzbot+4ef89409a235d804c6c2(a)syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=4ef89409a235d804c6c2
Link: https://lore.kernel.org/all/20251217054908.178907-1-kartikey406@gmail.com/T/ [v1]
Signed-off-by: Deepanshu Kartikey <kartikey406(a)gmail.com>
---
v2:
- Added explanation of why UAF is not possible
- Added explanation of why nfc_register_device() is safe
- Added Fixes and Cc: stable tags
- Fixed blank line after variable declaration (kept it)
---
net/nfc/core.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/net/nfc/core.c b/net/nfc/core.c
index ae1c842f9c64..82f023f37754 100644
--- a/net/nfc/core.c
+++ b/net/nfc/core.c
@@ -1154,6 +1154,7 @@ EXPORT_SYMBOL(nfc_register_device);
void nfc_unregister_device(struct nfc_dev *dev)
{
int rc;
+ struct rfkill *rfk = NULL;
pr_debug("dev_name=%s\n", dev_name(&dev->dev));
@@ -1164,13 +1165,17 @@ void nfc_unregister_device(struct nfc_dev *dev)
device_lock(&dev->dev);
if (dev->rfkill) {
- rfkill_unregister(dev->rfkill);
- rfkill_destroy(dev->rfkill);
+ rfk = dev->rfkill;
dev->rfkill = NULL;
}
dev->shutting_down = true;
device_unlock(&dev->dev);
+ if (rfk) {
+ rfkill_unregister(rfk);
+ rfkill_destroy(rfk);
+ }
+
if (dev->ops->check_presence) {
timer_delete_sync(&dev->check_pres_timer);
cancel_work_sync(&dev->check_pres_work);
--
2.43.0
syzbot reported a KASAN out-of-bounds Read in ext4_xattr_set_entry()[1].
When xattr_find_entry() returns -ENODATA, search.here still points to the
position after the last valid entry. ext4_xattr_block_set() clones the xattr
block because the original block maybe shared and must not be modified in
place.
In the clone_block, search.here is recomputed unconditionally from the old
offset, which may place it past search.first. This results in a negative
reset size and an out-of-bounds memmove() in ext4_xattr_set_entry().
Fix this by initializing search.here correctly when search.not_found is set.
[1] https://syzkaller.appspot.com/bug?extid=f792df426ff0f5ceb8d1
Fixes: fd48e9acdf2 (ext4: Unindent codeblock in ext4_xattr_block_set)
Cc: stable(a)vger.kernel.org
Reported-by: syzbot+f792df426ff0f5ceb8d1(a)syzkaller.appspotmail.com
Signed-off-by: Jinchao Wang <wangjinchao600(a)gmail.com>
---
fs/ext4/xattr.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
index 2e02efbddaac..cc30abeb7f30 100644
--- a/fs/ext4/xattr.c
+++ b/fs/ext4/xattr.c
@@ -1980,7 +1980,10 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode,
goto cleanup;
s->first = ENTRY(header(s->base)+1);
header(s->base)->h_refcount = cpu_to_le32(1);
- s->here = ENTRY(s->base + offset);
+ if (s->not_found)
+ s->here = s->first;
+ else
+ s->here = ENTRY(s->base + offset);
s->end = s->base + bs->bh->b_size;
/*
--
2.43.0
Since commit 9c328f54741b ("net: nfc: nci: Add parameter validation for
packet data") communication with nci nfc chips is not working any more.
The mentioned commit tries to fix access of uninitialized data, but
failed to understand that in some cases the data packet is of variable
length and can therefore not be compared to the maximum packet length
given by the sizeof(struct).
For these cases it is only possible to check for minimum packet length.
Fixes: 9c328f54741b ("net: nfc: nci: Add parameter validation for packet data")
Cc: stable(a)vger.kernel.org
Signed-off-by: Michael Thalmeier <michael.thalmeier(a)hale.at>
---
Changes in v2:
- Reference correct commit hash
---
net/nfc/nci/ntf.c | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/net/nfc/nci/ntf.c b/net/nfc/nci/ntf.c
index 418b84e2b260..5161e94f067f 100644
--- a/net/nfc/nci/ntf.c
+++ b/net/nfc/nci/ntf.c
@@ -58,7 +58,8 @@ static int nci_core_conn_credits_ntf_packet(struct nci_dev *ndev,
struct nci_conn_info *conn_info;
int i;
- if (skb->len < sizeof(struct nci_core_conn_credit_ntf))
+ /* Minimal packet size for num_entries=1 is 1 x __u8 + 1 x conn_credit_entry */
+ if (skb->len < (sizeof(__u8) + sizeof(struct conn_credit_entry)))
return -EINVAL;
ntf = (struct nci_core_conn_credit_ntf *)skb->data;
@@ -364,7 +365,8 @@ static int nci_rf_discover_ntf_packet(struct nci_dev *ndev,
const __u8 *data;
bool add_target = true;
- if (skb->len < sizeof(struct nci_rf_discover_ntf))
+ /* Minimal packet size is 5 if rf_tech_specific_params_len=0 */
+ if (skb->len < (5 * sizeof(__u8)))
return -EINVAL;
data = skb->data;
@@ -596,7 +598,10 @@ static int nci_rf_intf_activated_ntf_packet(struct nci_dev *ndev,
const __u8 *data;
int err = NCI_STATUS_OK;
- if (skb->len < sizeof(struct nci_rf_intf_activated_ntf))
+ /* Minimal packet size is 11 if
+ * f_tech_specific_params_len=0 and activation_params_len=0
+ */
+ if (skb->len < (11 * sizeof(__u8)))
return -EINVAL;
data = skb->data;
--
2.52.0
Commit 53326135d0e0 ("i2c: riic: Add suspend/resume support") added
suspend support for the Renesas I2C driver and following this change
on RZ/G3E the following WARNING is seen on entering suspend ...
[ 134.275704] Freezing remaining freezable tasks completed (elapsed 0.001 seconds)
[ 134.285536] ------------[ cut here ]------------
[ 134.290298] i2c i2c-2: Transfer while suspended
[ 134.295174] WARNING: drivers/i2c/i2c-core.h:56 at __i2c_smbus_xfer+0x1e4/0x214, CPU#0: systemd-sleep/388
[ 134.365507] Tainted: [W]=WARN
[ 134.368485] Hardware name: Renesas SMARC EVK version 2 based on r9a09g047e57 (DT)
[ 134.375961] pstate: 60400005 (nZCv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--)
[ 134.382935] pc : __i2c_smbus_xfer+0x1e4/0x214
[ 134.387329] lr : __i2c_smbus_xfer+0x1e4/0x214
[ 134.391717] sp : ffff800083f23860
[ 134.395040] x29: ffff800083f23860 x28: 0000000000000000 x27: ffff800082ed5d60
[ 134.402226] x26: 0000001f4395fd74 x25: 0000000000000007 x24: 0000000000000001
[ 134.409408] x23: 0000000000000000 x22: 000000000000006f x21: ffff800083f23936
[ 134.416589] x20: ffff0000c090e140 x19: ffff0000c090e0d0 x18: 0000000000000006
[ 134.423771] x17: 6f63657320313030 x16: 2e30206465737061 x15: ffff800083f23280
[ 134.430953] x14: 0000000000000000 x13: ffff800082b16ce8 x12: 0000000000000f09
[ 134.438134] x11: 0000000000000503 x10: ffff800082b6ece8 x9 : ffff800082b16ce8
[ 134.445315] x8 : 00000000ffffefff x7 : ffff800082b6ece8 x6 : 80000000fffff000
[ 134.452495] x5 : 0000000000000504 x4 : 0000000000000000 x3 : 0000000000000000
[ 134.459672] x2 : 0000000000000000 x1 : 0000000000000000 x0 : ffff0000c9ee9e80
[ 134.466851] Call trace:
[ 134.469311] __i2c_smbus_xfer+0x1e4/0x214 (P)
[ 134.473715] i2c_smbus_xfer+0xbc/0x120
[ 134.477507] i2c_smbus_read_byte_data+0x4c/0x84
[ 134.482077] isl1208_i2c_read_time+0x44/0x178 [rtc_isl1208]
[ 134.487703] isl1208_rtc_read_time+0x14/0x20 [rtc_isl1208]
[ 134.493226] __rtc_read_time+0x44/0x88
[ 134.497012] rtc_read_time+0x3c/0x68
[ 134.500622] rtc_suspend+0x9c/0x170
The warning is triggered because I2C transfers can still be attempted
while the controller is already suspended, due to inappropriate ordering
of the system sleep callbacks.
If the controller is autosuspended, there is no way to wake it up once
runtime PM disabled (in suspend_late()). During system resume, the I2C
controller will be available only after runtime PM is re-enabled
(in resume_early()). However, this may be too late for some devices.
Wake up the controller in the suspend() callback while runtime PM is
still enabled. The I2C controller will remain available until the
suspend_noirq() callback (pm_runtime_force_suspend()) is called. During
resume, the I2C controller can be restored by the resume_noirq() callback
(pm_runtime_force_resume()). Finally, the resume() callback re-enables
autosuspend. As a result, the I2C controller can remain available until
the system enters suspend_noirq() and from resume_noirq().
Cc: stable(a)vger.kernel.org
Fixes: 53326135d0e0 ("i2c: riic: Add suspend/resume support")
Signed-off-by: Tommaso Merciai <tommaso.merciai.xr(a)bp.renesas.com>
---
v1->v2:
- Taking as reference commit:
4262df2a69c3 ("i2c: imx-lpi2c: make controller available until the system
enters suspend_noirq() and from resume_noirq().") reworked the patch with
a similar approach. Updated commit body accordingly.
drivers/i2c/busses/i2c-riic.c | 46 +++++++++++++++++++++++++++++------
1 file changed, 39 insertions(+), 7 deletions(-)
diff --git a/drivers/i2c/busses/i2c-riic.c b/drivers/i2c/busses/i2c-riic.c
index 3e8f126cb7f7..9e3595b3623e 100644
--- a/drivers/i2c/busses/i2c-riic.c
+++ b/drivers/i2c/busses/i2c-riic.c
@@ -670,12 +670,39 @@ static const struct riic_of_data riic_rz_t2h_info = {
static int riic_i2c_suspend(struct device *dev)
{
- struct riic_dev *riic = dev_get_drvdata(dev);
- int ret;
+ /*
+ * Some I2C devices may need the I2C controller to remain active
+ * during resume_noirq() or suspend_noirq(). If the controller is
+ * autosuspended, there is no way to wake it up once runtime PM is
+ * disabled (in suspend_late()).
+ *
+ * During system resume, the I2C controller will be available only
+ * after runtime PM is re-enabled (in resume_early()). However, this
+ * may be too late for some devices.
+ *
+ * Wake up the controller in the suspend() callback while runtime PM
+ * is still enabled. The I2C controller will remain available until
+ * the suspend_noirq() callback (pm_runtime_force_suspend()) is
+ * called. During resume, the I2C controller can be restored by the
+ * resume_noirq() callback (pm_runtime_force_resume()).
+ *
+ * Finally, the resume() callback re-enables autosuspend, ensuring
+ * the I2C controller remains available until the system enters
+ * suspend_noirq() and from resume_noirq().
+ */
+ return pm_runtime_resume_and_get(dev);
+}
- ret = pm_runtime_resume_and_get(dev);
- if (ret)
- return ret;
+static int riic_i2c_resume(struct device *dev)
+{
+ pm_runtime_put_autosuspend(dev);
+
+ return 0;
+}
+
+static int riic_i2c_suspend_noirq(struct device *dev)
+{
+ struct riic_dev *riic = dev_get_drvdata(dev);
i2c_mark_adapter_suspended(&riic->adapter);
@@ -683,12 +710,12 @@ static int riic_i2c_suspend(struct device *dev)
riic_clear_set_bit(riic, ICCR1_ICE, 0, RIIC_ICCR1);
pm_runtime_mark_last_busy(dev);
- pm_runtime_put_sync(dev);
+ pm_runtime_force_suspend(dev);
return reset_control_assert(riic->rstc);
}
-static int riic_i2c_resume(struct device *dev)
+static int riic_i2c_resume_noirq(struct device *dev)
{
struct riic_dev *riic = dev_get_drvdata(dev);
int ret;
@@ -697,6 +724,10 @@ static int riic_i2c_resume(struct device *dev)
if (ret)
return ret;
+ ret = pm_runtime_force_resume(dev);
+ if (ret)
+ return ret;
+
ret = riic_init_hw(riic);
if (ret) {
/*
@@ -714,6 +745,7 @@ static int riic_i2c_resume(struct device *dev)
}
static const struct dev_pm_ops riic_i2c_pm_ops = {
+ NOIRQ_SYSTEM_SLEEP_PM_OPS(riic_i2c_suspend_noirq, riic_i2c_resume_noirq)
SYSTEM_SLEEP_PM_OPS(riic_i2c_suspend, riic_i2c_resume)
};
--
2.43.0