From: Sasha Levin Date: Tue, 8 Jul 2025 00:37:06 +0000 (-0400) Subject: Fixes for 5.15 X-Git-Tag: v5.15.187~46 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=8801928a16c4025af37a0eba254abddabae92310;p=thirdparty%2Fkernel%2Fstable-queue.git Fixes for 5.15 Signed-off-by: Sasha Levin --- diff --git a/queue-5.15/btrfs-fix-inode-lookup-error-handling-during-log-rep.patch b/queue-5.15/btrfs-fix-inode-lookup-error-handling-during-log-rep.patch new file mode 100644 index 0000000000..8868aae0d7 --- /dev/null +++ b/queue-5.15/btrfs-fix-inode-lookup-error-handling-during-log-rep.patch @@ -0,0 +1,451 @@ +From 66b39b200cc42b3e4d9962311739f75d8ce20c45 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 18 Jun 2025 15:58:31 +0100 +Subject: btrfs: fix inode lookup error handling during log replay + +From: Filipe Manana + +[ Upstream commit 5f61b961599acbd2bed028d3089105a1f7d224b8 ] + +When replaying log trees we use read_one_inode() to get an inode, which is +just a wrapper around btrfs_iget_logging(), which in turn is a wrapper for +btrfs_iget(). But read_one_inode() always returns NULL for any error +that btrfs_iget_logging() / btrfs_iget() may return and this is a problem +because: + +1) In many callers of read_one_inode() we convert the NULL into -EIO, + which is not accurate since btrfs_iget() may return -ENOMEM and -ENOENT + for example, besides -EIO and other errors. So during log replay we + may end up reporting a false -EIO, which is confusing since we may + not have had any IO error at all; + +2) When replaying directory deletes, at replay_dir_deletes(), we assume + the NULL returned from read_one_inode() means that the inode doesn't + exist and then proceed as if no error had happened. This is wrong + because unless btrfs_iget() returned ERR_PTR(-ENOENT), we had an + actual error and the target inode may exist in the target subvolume + root - this may later result in the log replay code failing at a + later stage (if we are "lucky") or succeed but leaving some + inconsistency in the filesystem. + +So fix this by not ignoring errors from btrfs_iget_logging() and as +a consequence remove the read_one_inode() wrapper and just use +btrfs_iget_logging() directly. Also since btrfs_iget_logging() is +supposed to be called only against subvolume roots, just like +read_one_inode() which had a comment about it, add an assertion to +btrfs_iget_logging() to check that the target root corresponds to a +subvolume root. + +Fixes: 5d4f98a28c7d ("Btrfs: Mixed back reference (FORWARD ROLLING FORMAT CHANGE)") +Reviewed-by: Johannes Thumshirn +Reviewed-by: Qu Wenruo +Signed-off-by: Filipe Manana +Signed-off-by: David Sterba +Signed-off-by: Sasha Levin +--- + fs/btrfs/tree-log.c | 206 +++++++++++++++++++++++++++++--------------- + 1 file changed, 138 insertions(+), 68 deletions(-) + +diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c +index 10db10544c96c..459b85268bc6b 100644 +--- a/fs/btrfs/tree-log.c ++++ b/fs/btrfs/tree-log.c +@@ -130,6 +130,31 @@ static void wait_log_commit(struct btrfs_root *root, int transid); + * and once to do all the other items. + */ + ++static struct btrfs_inode *btrfs_iget_logging(u64 objectid, struct btrfs_root *root) ++{ ++ unsigned int nofs_flag; ++ struct inode *inode; ++ ++ /* Only meant to be called for subvolume roots and not for log roots. */ ++ ASSERT(root->root_key.objectid != BTRFS_TREE_LOG_OBJECTID && ++ root->root_key.objectid != BTRFS_TREE_LOG_FIXUP_OBJECTID); ++ ++ /* ++ * We're holding a transaction handle whether we are logging or ++ * replaying a log tree, so we must make sure NOFS semantics apply ++ * because btrfs_alloc_inode() may be triggered and it uses GFP_KERNEL ++ * to allocate an inode, which can recurse back into the filesystem and ++ * attempt a transaction commit, resulting in a deadlock. ++ */ ++ nofs_flag = memalloc_nofs_save(); ++ inode = btrfs_iget(root->fs_info->sb, objectid, root); ++ memalloc_nofs_restore(nofs_flag); ++ ++ if (IS_ERR(inode)) ++ return ERR_CAST(inode); ++ ++ return BTRFS_I(inode); ++} + /* + * start a sub transaction and setup the log tree + * this increments the log tree writer count to make the people +@@ -584,19 +609,18 @@ static noinline int overwrite_item(struct btrfs_trans_handle *trans, + return 0; + } + +-/* +- * simple helper to read an inode off the disk from a given root +- * This can only be called for subvolume roots and not for the log +- */ +-static noinline struct inode *read_one_inode(struct btrfs_root *root, +- u64 objectid) ++static int read_alloc_one_name(struct extent_buffer *eb, void *start, int len, ++ char **name) + { +- struct inode *inode; ++ char *buf; + +- inode = btrfs_iget(root->fs_info->sb, objectid, root); +- if (IS_ERR(inode)) +- inode = NULL; +- return inode; ++ buf = kmalloc(len, GFP_NOFS); ++ if (!buf) ++ return -ENOMEM; ++ ++ read_extent_buffer(eb, buf, (unsigned long)start, len); ++ *name = buf; ++ return 0; + } + + /* replays a single extent in 'eb' at 'slot' with 'key' into the +@@ -652,10 +676,14 @@ static noinline int replay_one_extent(struct btrfs_trans_handle *trans, + goto out; + } + +- inode = read_one_inode(root, key->objectid); +- if (!inode) { +- ret = -EIO; +- goto out; ++ { ++ struct btrfs_inode *btrfs_inode; ++ btrfs_inode = btrfs_iget_logging(key->objectid, root); ++ if (IS_ERR(btrfs_inode)) { ++ ret = PTR_ERR(btrfs_inode); ++ goto out; ++ } ++ inode = &btrfs_inode->vfs_inode; + } + + /* +@@ -918,6 +946,7 @@ static noinline int drop_one_dir_item(struct btrfs_trans_handle *trans, + struct btrfs_inode *dir, + struct btrfs_dir_item *di) + { ++ struct btrfs_inode *btrfs_inode; + struct inode *inode; + char *name; + int name_len; +@@ -936,11 +965,13 @@ static noinline int drop_one_dir_item(struct btrfs_trans_handle *trans, + read_extent_buffer(leaf, name, (unsigned long)(di + 1), name_len); + btrfs_release_path(path); + +- inode = read_one_inode(root, location.objectid); +- if (!inode) { +- ret = -EIO; ++ btrfs_inode = btrfs_iget_logging(location.objectid, root); ++ if (IS_ERR(btrfs_inode)) { ++ ret = PTR_ERR(btrfs_inode); ++ inode = NULL; + goto out; + } ++ inode = &btrfs_inode->vfs_inode; + + ret = link_to_fixup_dir(trans, root, path, location.objectid); + if (ret) +@@ -1140,6 +1171,7 @@ static inline int __add_inode_ref(struct btrfs_trans_handle *trans, + u32 item_size; + u32 cur_offset = 0; + unsigned long base; ++ struct btrfs_inode *victim_parent_btrfs; + struct inode *victim_parent; + + leaf = path->nodes[0]; +@@ -1173,10 +1205,11 @@ static inline int __add_inode_ref(struct btrfs_trans_handle *trans, + kfree(victim_name); + return ret; + } else if (!ret) { +- ret = -ENOENT; +- victim_parent = read_one_inode(root, +- parent_objectid); +- if (victim_parent) { ++ victim_parent_btrfs = btrfs_iget_logging(parent_objectid, root); ++ if (IS_ERR(victim_parent_btrfs)) { ++ ret = PTR_ERR(victim_parent_btrfs); ++ } else { ++ victim_parent = &victim_parent_btrfs->vfs_inode; + inc_nlink(&inode->vfs_inode); + btrfs_release_path(path); + +@@ -1333,11 +1366,15 @@ static int unlink_old_inode_refs(struct btrfs_trans_handle *trans, + struct inode *dir; + + btrfs_release_path(path); +- dir = read_one_inode(root, parent_id); +- if (!dir) { +- ret = -ENOENT; +- kfree(name); +- goto out; ++ { ++ struct btrfs_inode *btrfs_dir; ++ btrfs_dir = btrfs_iget_logging(parent_id, root); ++ if (IS_ERR(btrfs_dir)) { ++ ret = PTR_ERR(btrfs_dir); ++ kfree(name); ++ goto out; ++ } ++ dir = &btrfs_dir->vfs_inode; + } + ret = unlink_inode_for_log_replay(trans, BTRFS_I(dir), + inode, name, namelen); +@@ -1432,10 +1469,14 @@ static int add_link(struct btrfs_trans_handle *trans, struct btrfs_root *root, + */ + btrfs_dir_item_key_to_cpu(path->nodes[0], dir_item, &key); + btrfs_release_path(path); +- other_inode = read_one_inode(root, key.objectid); +- if (!other_inode) { +- ret = -ENOENT; +- goto out; ++ { ++ struct btrfs_inode *btrfs_other_inode; ++ btrfs_other_inode = btrfs_iget_logging(key.objectid, root); ++ if (IS_ERR(btrfs_other_inode)) { ++ ret = PTR_ERR(btrfs_other_inode); ++ goto out; ++ } ++ other_inode = &btrfs_other_inode->vfs_inode; + } + ret = unlink_inode_for_log_replay(trans, BTRFS_I(dir), BTRFS_I(other_inode), + name, namelen); +@@ -1470,6 +1511,8 @@ static noinline int add_inode_ref(struct btrfs_trans_handle *trans, + struct extent_buffer *eb, int slot, + struct btrfs_key *key) + { ++ struct btrfs_inode *dir_btrfs = NULL; ++ struct btrfs_inode *inode_btrfs = NULL; + struct inode *dir = NULL; + struct inode *inode = NULL; + unsigned long ref_ptr; +@@ -1506,17 +1549,21 @@ static noinline int add_inode_ref(struct btrfs_trans_handle *trans, + * copy the back ref in. The link count fixup code will take + * care of the rest + */ +- dir = read_one_inode(root, parent_objectid); +- if (!dir) { +- ret = -ENOENT; ++ dir_btrfs = btrfs_iget_logging(parent_objectid, root); ++ if (IS_ERR(dir_btrfs)) { ++ ret = PTR_ERR(dir_btrfs); ++ dir = NULL; + goto out; + } ++ dir = &dir_btrfs->vfs_inode; + +- inode = read_one_inode(root, inode_objectid); +- if (!inode) { +- ret = -EIO; ++ inode_btrfs = btrfs_iget_logging(inode_objectid, root); ++ if (IS_ERR(inode_btrfs)) { ++ ret = PTR_ERR(inode_btrfs); ++ inode = NULL; + goto out; + } ++ inode = &inode_btrfs->vfs_inode; + + while (ref_ptr < ref_end) { + if (log_ref_ver) { +@@ -1526,11 +1573,14 @@ static noinline int add_inode_ref(struct btrfs_trans_handle *trans, + * parent object can change from one array + * item to another. + */ +- if (!dir) +- dir = read_one_inode(root, parent_objectid); + if (!dir) { +- ret = -ENOENT; +- goto out; ++ dir_btrfs = btrfs_iget_logging(parent_objectid, root); ++ if (IS_ERR(dir_btrfs)) { ++ ret = PTR_ERR(dir_btrfs); ++ dir = NULL; ++ goto out; ++ } ++ dir = &dir_btrfs->vfs_inode; + } + } else { + ret = ref_get_fields(eb, ref_ptr, &namelen, &name, +@@ -1805,6 +1855,7 @@ static noinline int fixup_inode_link_counts(struct btrfs_trans_handle *trans, + { + int ret; + struct btrfs_key key; ++ struct btrfs_inode *btrfs_inode; + struct inode *inode; + + key.objectid = BTRFS_TREE_LOG_FIXUP_OBJECTID; +@@ -1832,11 +1883,12 @@ static noinline int fixup_inode_link_counts(struct btrfs_trans_handle *trans, + break; + + btrfs_release_path(path); +- inode = read_one_inode(root, key.offset); +- if (!inode) { +- ret = -EIO; ++ btrfs_inode = btrfs_iget_logging(key.offset, root); ++ if (IS_ERR(btrfs_inode)) { ++ ret = PTR_ERR(btrfs_inode); + break; + } ++ inode = &btrfs_inode->vfs_inode; + + ret = fixup_inode_link_count(trans, inode); + iput(inode); +@@ -1867,11 +1919,13 @@ static noinline int link_to_fixup_dir(struct btrfs_trans_handle *trans, + { + struct btrfs_key key; + int ret = 0; ++ struct btrfs_inode *btrfs_inode; + struct inode *inode; + +- inode = read_one_inode(root, objectid); +- if (!inode) +- return -EIO; ++ btrfs_inode = btrfs_iget_logging(objectid, root); ++ if (IS_ERR(btrfs_inode)) ++ return PTR_ERR(btrfs_inode); ++ inode = &btrfs_inode->vfs_inode; + + key.objectid = BTRFS_TREE_LOG_FIXUP_OBJECTID; + key.type = BTRFS_ORPHAN_ITEM_KEY; +@@ -1907,17 +1961,21 @@ static noinline int insert_one_name(struct btrfs_trans_handle *trans, + { + struct inode *inode; + struct inode *dir; ++ struct btrfs_inode *btrfs_inode; ++ struct btrfs_inode *btrfs_dir; + int ret; + +- inode = read_one_inode(root, location->objectid); +- if (!inode) +- return -ENOENT; ++ btrfs_inode = btrfs_iget_logging(location->objectid, root); ++ if (IS_ERR(btrfs_inode)) ++ return PTR_ERR(btrfs_inode); ++ inode = &btrfs_inode->vfs_inode; + +- dir = read_one_inode(root, dirid); +- if (!dir) { ++ btrfs_dir = btrfs_iget_logging(dirid, root); ++ if (IS_ERR(btrfs_dir)) { + iput(inode); +- return -EIO; ++ return PTR_ERR(btrfs_dir); + } ++ dir = &btrfs_dir->vfs_inode; + + ret = btrfs_add_link(trans, BTRFS_I(dir), BTRFS_I(inode), name, + name_len, 1, index); +@@ -1957,6 +2015,7 @@ static noinline int replay_one_name(struct btrfs_trans_handle *trans, + struct btrfs_dir_item *dst_di; + struct btrfs_key found_key; + struct btrfs_key log_key; ++ struct btrfs_inode *dir_btrfs; + struct inode *dir; + u8 log_type; + bool exists; +@@ -1964,9 +2023,10 @@ static noinline int replay_one_name(struct btrfs_trans_handle *trans, + bool update_size = (key->type == BTRFS_DIR_INDEX_KEY); + bool name_added = false; + +- dir = read_one_inode(root, key->objectid); +- if (!dir) +- return -EIO; ++ dir_btrfs = btrfs_iget_logging(key->objectid, root); ++ if (IS_ERR(dir_btrfs)) ++ return PTR_ERR(dir_btrfs); ++ dir = &dir_btrfs->vfs_inode; + + name_len = btrfs_dir_name_len(eb, di); + name = kmalloc(name_len, GFP_NOFS); +@@ -2273,6 +2333,7 @@ static noinline int check_item_in_log(struct btrfs_trans_handle *trans, + struct btrfs_dir_item *di; + int name_len; + char *name; ++ struct btrfs_inode *btrfs_inode = NULL; + struct inode *inode = NULL; + struct btrfs_key location; + +@@ -2316,11 +2377,13 @@ static noinline int check_item_in_log(struct btrfs_trans_handle *trans, + btrfs_dir_item_key_to_cpu(eb, di, &location); + btrfs_release_path(path); + btrfs_release_path(log_path); +- inode = read_one_inode(root, location.objectid); +- if (!inode) { +- ret = -EIO; ++ btrfs_inode = btrfs_iget_logging(location.objectid, root); ++ if (IS_ERR(btrfs_inode)) { ++ ret = PTR_ERR(btrfs_inode); ++ inode = NULL; + goto out; + } ++ inode = &btrfs_inode->vfs_inode; + + ret = link_to_fixup_dir(trans, root, path, location.objectid); + if (ret) +@@ -2462,6 +2525,7 @@ static noinline int replay_dir_deletes(struct btrfs_trans_handle *trans, + struct btrfs_key dir_key; + struct btrfs_key found_key; + struct btrfs_path *log_path; ++ struct btrfs_inode *dir_btrfs; + struct inode *dir; + + dir_key.objectid = dirid; +@@ -2470,15 +2534,19 @@ static noinline int replay_dir_deletes(struct btrfs_trans_handle *trans, + if (!log_path) + return -ENOMEM; + +- dir = read_one_inode(root, dirid); +- /* it isn't an error if the inode isn't there, that can happen +- * because we replay the deletes before we copy in the inode item +- * from the log ++ dir_btrfs = btrfs_iget_logging(dirid, root); ++ /* ++ * It isn't an error if the inode isn't there, that can happen because ++ * we replay the deletes before we copy in the inode item from the log. + */ +- if (!dir) { ++ if (IS_ERR(dir_btrfs)) { + btrfs_free_path(log_path); +- return 0; ++ ret = PTR_ERR(dir_btrfs); ++ if (ret == -ENOENT) ++ ret = 0; ++ return ret; + } ++ dir = &dir_btrfs->vfs_inode; + + range_start = 0; + range_end = 0; +@@ -2629,14 +2697,16 @@ static int replay_one_buffer(struct btrfs_root *log, struct extent_buffer *eb, + */ + if (S_ISREG(mode)) { + struct btrfs_drop_extents_args drop_args = { 0 }; ++ struct btrfs_inode *btrfs_inode; + struct inode *inode; + u64 from; + +- inode = read_one_inode(root, key.objectid); +- if (!inode) { +- ret = -EIO; ++ btrfs_inode = btrfs_iget_logging(key.objectid, root); ++ if (IS_ERR(btrfs_inode)) { ++ ret = PTR_ERR(btrfs_inode); + break; + } ++ inode = &btrfs_inode->vfs_inode; + from = ALIGN(i_size_read(inode), + root->fs_info->sectorsize); + drop_args.start = from; +-- +2.39.5 + diff --git a/queue-5.15/btrfs-propagate-last_unlink_trans-earlier-when-doing.patch b/queue-5.15/btrfs-propagate-last_unlink_trans-earlier-when-doing.patch new file mode 100644 index 0000000000..ee6d955121 --- /dev/null +++ b/queue-5.15/btrfs-propagate-last_unlink_trans-earlier-when-doing.patch @@ -0,0 +1,101 @@ +From cd133f62cfbbbee8a2bbca746aaefc6e52f9e2a8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 20 Jun 2025 15:54:05 +0100 +Subject: btrfs: propagate last_unlink_trans earlier when doing a rmdir + +From: Filipe Manana + +[ Upstream commit c466e33e729a0ee017d10d919cba18f503853c60 ] + +In case the removed directory had a snapshot that was deleted, we are +propagating its inode's last_unlink_trans to the parent directory after +we removed the entry from the parent directory. This leaves a small race +window where someone can log the parent directory after we removed the +entry and before we updated last_unlink_trans, and as a result if we ever +try to replay such a log tree, we will fail since we will attempt to +remove a snapshot during log replay, which is currently not possible and +results in the log replay (and mount) to fail. This is the type of failure +described in commit 1ec9a1ae1e30 ("Btrfs: fix unreplayable log after +snapshot delete + parent dir fsync"). + +So fix this by propagating the last_unlink_trans to the parent directory +before we remove the entry from it. + +Fixes: 44f714dae50a ("Btrfs: improve performance on fsync against new inode after rename/unlink") +Reviewed-by: Johannes Thumshirn +Signed-off-by: Filipe Manana +Signed-off-by: David Sterba +Signed-off-by: Sasha Levin +--- + fs/btrfs/inode.c | 36 ++++++++++++++++++------------------ + 1 file changed, 18 insertions(+), 18 deletions(-) + +diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c +index 32ae349b6abfb..76a59f95e7613 100644 +--- a/fs/btrfs/inode.c ++++ b/fs/btrfs/inode.c +@@ -4655,7 +4655,6 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry) + struct inode *inode = d_inode(dentry); + int err = 0; + struct btrfs_trans_handle *trans; +- u64 last_unlink_trans; + + if (inode->i_size > BTRFS_EMPTY_DIR_SIZE) + return -ENOTEMPTY; +@@ -4666,6 +4665,23 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry) + if (IS_ERR(trans)) + return PTR_ERR(trans); + ++ /* ++ * Propagate the last_unlink_trans value of the deleted dir to its ++ * parent directory. This is to prevent an unrecoverable log tree in the ++ * case we do something like this: ++ * 1) create dir foo ++ * 2) create snapshot under dir foo ++ * 3) delete the snapshot ++ * 4) rmdir foo ++ * 5) mkdir foo ++ * 6) fsync foo or some file inside foo ++ * ++ * This is because we can't unlink other roots when replaying the dir ++ * deletes for directory foo. ++ */ ++ if (BTRFS_I(inode)->last_unlink_trans >= trans->transid) ++ BTRFS_I(dir)->last_unlink_trans = BTRFS_I(inode)->last_unlink_trans; ++ + if (unlikely(btrfs_ino(BTRFS_I(inode)) == BTRFS_EMPTY_SUBVOL_DIR_OBJECTID)) { + err = btrfs_unlink_subvol(trans, dir, dentry); + goto out; +@@ -4675,28 +4691,12 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry) + if (err) + goto out; + +- last_unlink_trans = BTRFS_I(inode)->last_unlink_trans; +- + /* now the directory is empty */ + err = btrfs_unlink_inode(trans, BTRFS_I(dir), + BTRFS_I(d_inode(dentry)), dentry->d_name.name, + dentry->d_name.len); +- if (!err) { ++ if (!err) + btrfs_i_size_write(BTRFS_I(inode), 0); +- /* +- * Propagate the last_unlink_trans value of the deleted dir to +- * its parent directory. This is to prevent an unrecoverable +- * log tree in the case we do something like this: +- * 1) create dir foo +- * 2) create snapshot under dir foo +- * 3) delete the snapshot +- * 4) rmdir foo +- * 5) mkdir foo +- * 6) fsync foo or some file inside foo +- */ +- if (last_unlink_trans >= trans->transid) +- BTRFS_I(dir)->last_unlink_trans = last_unlink_trans; +- } + out: + btrfs_end_transaction(trans); + btrfs_btree_balance_dirty(BTRFS_I(dir)->root->fs_info); +-- +2.39.5 + diff --git a/queue-5.15/btrfs-remove-noinline-from-btrfs_update_inode.patch b/queue-5.15/btrfs-remove-noinline-from-btrfs_update_inode.patch new file mode 100644 index 0000000000..c4415103f2 --- /dev/null +++ b/queue-5.15/btrfs-remove-noinline-from-btrfs_update_inode.patch @@ -0,0 +1,42 @@ +From 749a04213654801d0644d81baf712cb3f7daba35 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 22 Sep 2023 11:37:20 +0100 +Subject: btrfs: remove noinline from btrfs_update_inode() + +From: Filipe Manana + +[ Upstream commit cddaaacca9339d2f13599a822dc2f68be71d2e0d ] + +The noinline attribute of btrfs_update_inode() is pointless as the +function is exported and widely used, so remove it. + +Reviewed-by: Qu Wenruo +Signed-off-by: Filipe Manana +Reviewed-by: David Sterba +Signed-off-by: David Sterba +Stable-dep-of: 5f61b961599a ("btrfs: fix inode lookup error handling during log replay") +Signed-off-by: Sasha Levin +--- + fs/btrfs/inode.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c +index e3e5bd4fb477a..32ae349b6abfb 100644 +--- a/fs/btrfs/inode.c ++++ b/fs/btrfs/inode.c +@@ -4074,9 +4074,9 @@ static noinline int btrfs_update_inode_item(struct btrfs_trans_handle *trans, + /* + * copy everything in the in-memory inode into the btree. + */ +-noinline int btrfs_update_inode(struct btrfs_trans_handle *trans, +- struct btrfs_root *root, +- struct btrfs_inode *inode) ++int btrfs_update_inode(struct btrfs_trans_handle *trans, ++ struct btrfs_root *root, ++ struct btrfs_inode *inode) + { + struct btrfs_fs_info *fs_info = root->fs_info; + int ret; +-- +2.39.5 + diff --git a/queue-5.15/btrfs-remove-redundant-root-argument-from-fixup_inod.patch b/queue-5.15/btrfs-remove-redundant-root-argument-from-fixup_inod.patch new file mode 100644 index 0000000000..84a67eeca8 --- /dev/null +++ b/queue-5.15/btrfs-remove-redundant-root-argument-from-fixup_inod.patch @@ -0,0 +1,108 @@ +From 0a86a37de55a083ad6ddb17ee782640f238fdb6e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 22 Sep 2023 11:37:26 +0100 +Subject: btrfs: remove redundant root argument from fixup_inode_link_count() + +From: Filipe Manana + +[ Upstream commit 8befc61cbba2d4567122d400542da8900a352971 ] + +The root argument for fixup_inode_link_count() always matches the root of +the given inode, so remove the root argument and get it from the inode +argument. This also applies to the helpers count_inode_extrefs() and +count_inode_refs() used by fixup_inode_link_count() - they don't need the +root argument, as it always matches the root of the inode passed to them. + +Reviewed-by: Qu Wenruo +Signed-off-by: Filipe Manana +Reviewed-by: David Sterba +Signed-off-by: David Sterba +Stable-dep-of: 5f61b961599a ("btrfs: fix inode lookup error handling during log replay") +Signed-off-by: Sasha Levin +--- + fs/btrfs/tree-log.c | 20 +++++++++----------- + 1 file changed, 9 insertions(+), 11 deletions(-) + +diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c +index 7049a19e07baf..10db10544c96c 100644 +--- a/fs/btrfs/tree-log.c ++++ b/fs/btrfs/tree-log.c +@@ -1638,8 +1638,7 @@ static noinline int add_inode_ref(struct btrfs_trans_handle *trans, + return ret; + } + +-static int count_inode_extrefs(struct btrfs_root *root, +- struct btrfs_inode *inode, struct btrfs_path *path) ++static int count_inode_extrefs(struct btrfs_inode *inode, struct btrfs_path *path) + { + int ret = 0; + int name_len; +@@ -1653,8 +1652,8 @@ static int count_inode_extrefs(struct btrfs_root *root, + struct extent_buffer *leaf; + + while (1) { +- ret = btrfs_find_one_extref(root, inode_objectid, offset, path, +- &extref, &offset); ++ ret = btrfs_find_one_extref(inode->root, inode_objectid, offset, ++ path, &extref, &offset); + if (ret) + break; + +@@ -1682,8 +1681,7 @@ static int count_inode_extrefs(struct btrfs_root *root, + return nlink; + } + +-static int count_inode_refs(struct btrfs_root *root, +- struct btrfs_inode *inode, struct btrfs_path *path) ++static int count_inode_refs(struct btrfs_inode *inode, struct btrfs_path *path) + { + int ret; + struct btrfs_key key; +@@ -1698,7 +1696,7 @@ static int count_inode_refs(struct btrfs_root *root, + key.offset = (u64)-1; + + while (1) { +- ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); ++ ret = btrfs_search_slot(NULL, inode->root, &key, path, 0, 0); + if (ret < 0) + break; + if (ret > 0) { +@@ -1750,9 +1748,9 @@ static int count_inode_refs(struct btrfs_root *root, + * will free the inode. + */ + static noinline int fixup_inode_link_count(struct btrfs_trans_handle *trans, +- struct btrfs_root *root, + struct inode *inode) + { ++ struct btrfs_root *root = BTRFS_I(inode)->root; + struct btrfs_path *path; + int ret; + u64 nlink = 0; +@@ -1762,13 +1760,13 @@ static noinline int fixup_inode_link_count(struct btrfs_trans_handle *trans, + if (!path) + return -ENOMEM; + +- ret = count_inode_refs(root, BTRFS_I(inode), path); ++ ret = count_inode_refs(BTRFS_I(inode), path); + if (ret < 0) + goto out; + + nlink = ret; + +- ret = count_inode_extrefs(root, BTRFS_I(inode), path); ++ ret = count_inode_extrefs(BTRFS_I(inode), path); + if (ret < 0) + goto out; + +@@ -1840,7 +1838,7 @@ static noinline int fixup_inode_link_counts(struct btrfs_trans_handle *trans, + break; + } + +- ret = fixup_inode_link_count(trans, root, inode); ++ ret = fixup_inode_link_count(trans, inode); + iput(inode); + if (ret) + break; +-- +2.39.5 + diff --git a/queue-5.15/btrfs-use-btrfs_record_snapshot_destroy-during-rmdir.patch b/queue-5.15/btrfs-use-btrfs_record_snapshot_destroy-during-rmdir.patch new file mode 100644 index 0000000000..bb904462d5 --- /dev/null +++ b/queue-5.15/btrfs-use-btrfs_record_snapshot_destroy-during-rmdir.patch @@ -0,0 +1,46 @@ +From c9d4e50e114d31aab2c9842c0498b032dfa441d2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 20 Jun 2025 16:37:01 +0100 +Subject: btrfs: use btrfs_record_snapshot_destroy() during rmdir + +From: Filipe Manana + +[ Upstream commit 157501b0469969fc1ba53add5049575aadd79d80 ] + +We are setting the parent directory's last_unlink_trans directly which +may result in a concurrent task starting to log the directory not see the +update and therefore can log the directory after we removed a child +directory which had a snapshot within instead of falling back to a +transaction commit. Replaying such a log tree would result in a mount +failure since we can't currently delete snapshots (and subvolumes) during +log replay. This is the type of failure described in commit 1ec9a1ae1e30 +("Btrfs: fix unreplayable log after snapshot delete + parent dir fsync"). + +Fix this by using btrfs_record_snapshot_destroy() which updates the +last_unlink_trans field while holding the inode's log_mutex lock. + +Fixes: 44f714dae50a ("Btrfs: improve performance on fsync against new inode after rename/unlink") +Reviewed-by: Johannes Thumshirn +Signed-off-by: Filipe Manana +Signed-off-by: David Sterba +Signed-off-by: Sasha Levin +--- + fs/btrfs/inode.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c +index 76a59f95e7613..92a4c1a9d639b 100644 +--- a/fs/btrfs/inode.c ++++ b/fs/btrfs/inode.c +@@ -4680,7 +4680,7 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry) + * deletes for directory foo. + */ + if (BTRFS_I(inode)->last_unlink_trans >= trans->transid) +- BTRFS_I(dir)->last_unlink_trans = BTRFS_I(inode)->last_unlink_trans; ++ btrfs_record_snapshot_destroy(trans, BTRFS_I(dir)); + + if (unlikely(btrfs_ino(BTRFS_I(inode)) == BTRFS_EMPTY_SUBVOL_DIR_OBJECTID)) { + err = btrfs_unlink_subvol(trans, dir, dentry); +-- +2.39.5 + diff --git a/queue-5.15/dpaa2-eth-fix-xdp_rxq_info-leak.patch b/queue-5.15/dpaa2-eth-fix-xdp_rxq_info-leak.patch new file mode 100644 index 0000000000..7c548cfa60 --- /dev/null +++ b/queue-5.15/dpaa2-eth-fix-xdp_rxq_info-leak.patch @@ -0,0 +1,101 @@ +From b5b52ec4e00e310c0a9a08bcab0c68082c4cd703 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 7 Jul 2025 16:06:22 -0400 +Subject: dpaa2-eth: fix xdp_rxq_info leak + +From: Fushuai Wang + +[ Upstream commit 2def09ead4ad5907988b655d1e1454003aaf8297 ] + +The driver registered xdp_rxq_info structures via xdp_rxq_info_reg() +but failed to properly unregister them in error paths and during +removal. + +Fixes: d678be1dc1ec ("dpaa2-eth: add XDP_REDIRECT support") +Signed-off-by: Fushuai Wang +Reviewed-by: Simon Horman +Reviewed-by: Ioana Ciornei +Link: https://patch.msgid.link/20250626133003.80136-1-wangfushuai@baidu.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + .../net/ethernet/freescale/dpaa2/dpaa2-eth.c | 26 +++++++++++++++++-- + 1 file changed, 24 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c +index acbb26cb2177e..07d8c9e41328e 100644 +--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c ++++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c +@@ -3512,6 +3512,7 @@ static int dpaa2_eth_setup_rx_flow(struct dpaa2_eth_priv *priv, + MEM_TYPE_PAGE_ORDER0, NULL); + if (err) { + dev_err(dev, "xdp_rxq_info_reg_mem_model failed\n"); ++ xdp_rxq_info_unreg(&fq->channel->xdp_rxq); + return err; + } + +@@ -4004,17 +4005,25 @@ static int dpaa2_eth_bind_dpni(struct dpaa2_eth_priv *priv) + return -EINVAL; + } + if (err) +- return err; ++ goto out; + } + + err = dpni_get_qdid(priv->mc_io, 0, priv->mc_token, + DPNI_QUEUE_TX, &priv->tx_qdid); + if (err) { + dev_err(dev, "dpni_get_qdid() failed\n"); +- return err; ++ goto out; + } + + return 0; ++ ++out: ++ while (i--) { ++ if (priv->fq[i].type == DPAA2_RX_FQ && ++ xdp_rxq_info_is_reg(&priv->fq[i].channel->xdp_rxq)) ++ xdp_rxq_info_unreg(&priv->fq[i].channel->xdp_rxq); ++ } ++ return err; + } + + /* Allocate rings for storing incoming frame descriptors */ +@@ -4371,6 +4380,17 @@ static void dpaa2_eth_del_ch_napi(struct dpaa2_eth_priv *priv) + } + } + ++static void dpaa2_eth_free_rx_xdp_rxq(struct dpaa2_eth_priv *priv) ++{ ++ int i; ++ ++ for (i = 0; i < priv->num_fqs; i++) { ++ if (priv->fq[i].type == DPAA2_RX_FQ && ++ xdp_rxq_info_is_reg(&priv->fq[i].channel->xdp_rxq)) ++ xdp_rxq_info_unreg(&priv->fq[i].channel->xdp_rxq); ++ } ++} ++ + static int dpaa2_eth_probe(struct fsl_mc_device *dpni_dev) + { + struct device *dev; +@@ -4559,6 +4579,7 @@ static int dpaa2_eth_probe(struct fsl_mc_device *dpni_dev) + free_percpu(priv->percpu_stats); + err_alloc_percpu_stats: + dpaa2_eth_del_ch_napi(priv); ++ dpaa2_eth_free_rx_xdp_rxq(priv); + err_bind: + dpaa2_eth_free_dpbp(priv); + err_dpbp_setup: +@@ -4610,6 +4631,7 @@ static int dpaa2_eth_remove(struct fsl_mc_device *ls_dev) + free_percpu(priv->percpu_extras); + + dpaa2_eth_del_ch_napi(priv); ++ dpaa2_eth_free_rx_xdp_rxq(priv); + dpaa2_eth_free_dpbp(priv); + dpaa2_eth_free_dpio(priv); + dpaa2_eth_free_dpni(priv); +-- +2.39.5 + diff --git a/queue-5.15/dpaa2-eth-update-dpni_get_single_step_cfg-command.patch b/queue-5.15/dpaa2-eth-update-dpni_get_single_step_cfg-command.patch new file mode 100644 index 0000000000..b2f68ed2ef --- /dev/null +++ b/queue-5.15/dpaa2-eth-update-dpni_get_single_step_cfg-command.patch @@ -0,0 +1,100 @@ +From 80073cdedb72f40e403091ce23a8e95d53193c39 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 18 Feb 2022 22:22:00 +0200 +Subject: dpaa2-eth: Update dpni_get_single_step_cfg command + +From: Radu Bulie + +[ Upstream commit 9572594ecf027a2b1828e42c26fb55cbd3219708 ] + +dpni_get_single_step_cfg is an MC firmware command used for +retrieving the contents of SINGLE_STEP 1588 register available +in a DPMAC. + +This patch adds a new version of this command that returns as an extra +argument the physical base address of the aforementioned register. +The address will be used to directly modify the contents of the +SINGLE_STEP register instead of invoking the MC command +dpni_set_single_step_cgf. The former approach introduced huge delays on +the TX datapath when one step PTP events were transmitted. This led to low +throughput and high latencies observed in the PTP correction field. + +Signed-off-by: Radu Bulie +Signed-off-by: David S. Miller +Stable-dep-of: 2def09ead4ad ("dpaa2-eth: fix xdp_rxq_info leak") +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/freescale/dpaa2/dpni-cmd.h | 6 +++++- + drivers/net/ethernet/freescale/dpaa2/dpni.c | 2 ++ + drivers/net/ethernet/freescale/dpaa2/dpni.h | 6 ++++++ + 3 files changed, 13 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/freescale/dpaa2/dpni-cmd.h b/drivers/net/ethernet/freescale/dpaa2/dpni-cmd.h +index 9f80bdfeedece..828f538097af8 100644 +--- a/drivers/net/ethernet/freescale/dpaa2/dpni-cmd.h ++++ b/drivers/net/ethernet/freescale/dpaa2/dpni-cmd.h +@@ -98,7 +98,7 @@ + #define DPNI_CMDID_GET_LINK_CFG DPNI_CMD(0x278) + + #define DPNI_CMDID_SET_SINGLE_STEP_CFG DPNI_CMD(0x279) +-#define DPNI_CMDID_GET_SINGLE_STEP_CFG DPNI_CMD(0x27a) ++#define DPNI_CMDID_GET_SINGLE_STEP_CFG DPNI_CMD_V2(0x27a) + + /* Macros for accessing command fields smaller than 1byte */ + #define DPNI_MASK(field) \ +@@ -658,12 +658,16 @@ struct dpni_cmd_single_step_cfg { + __le16 flags; + __le16 offset; + __le32 peer_delay; ++ __le32 ptp_onestep_reg_base; ++ __le32 pad0; + }; + + struct dpni_rsp_single_step_cfg { + __le16 flags; + __le16 offset; + __le32 peer_delay; ++ __le32 ptp_onestep_reg_base; ++ __le32 pad0; + }; + + struct dpni_cmd_enable_vlan_filter { +diff --git a/drivers/net/ethernet/freescale/dpaa2/dpni.c b/drivers/net/ethernet/freescale/dpaa2/dpni.c +index d6afada99fb66..6c3b36f20fb80 100644 +--- a/drivers/net/ethernet/freescale/dpaa2/dpni.c ++++ b/drivers/net/ethernet/freescale/dpaa2/dpni.c +@@ -2136,6 +2136,8 @@ int dpni_get_single_step_cfg(struct fsl_mc_io *mc_io, + ptp_cfg->ch_update = dpni_get_field(le16_to_cpu(rsp_params->flags), + PTP_CH_UPDATE) ? 1 : 0; + ptp_cfg->peer_delay = le32_to_cpu(rsp_params->peer_delay); ++ ptp_cfg->ptp_onestep_reg_base = ++ le32_to_cpu(rsp_params->ptp_onestep_reg_base); + + return err; + } +diff --git a/drivers/net/ethernet/freescale/dpaa2/dpni.h b/drivers/net/ethernet/freescale/dpaa2/dpni.h +index 7de0562bbf59c..6fffd519aa00e 100644 +--- a/drivers/net/ethernet/freescale/dpaa2/dpni.h ++++ b/drivers/net/ethernet/freescale/dpaa2/dpni.h +@@ -1074,12 +1074,18 @@ int dpni_set_tx_shaping(struct fsl_mc_io *mc_io, + * @peer_delay: For peer-to-peer transparent clocks add this value to the + * correction field in addition to the transient time update. + * The value expresses nanoseconds. ++ * @ptp_onestep_reg_base: 1588 SINGLE_STEP register base address. This address ++ * is used to update directly the register contents. ++ * User has to create an address mapping for it. ++ * ++ * + */ + struct dpni_single_step_cfg { + u8 en; + u8 ch_update; + u16 offset; + u32 peer_delay; ++ u32 ptp_onestep_reg_base; + }; + + int dpni_set_single_step_cfg(struct fsl_mc_io *mc_io, +-- +2.39.5 + diff --git a/queue-5.15/dpaa2-eth-update-single_step-register-access.patch b/queue-5.15/dpaa2-eth-update-single_step-register-access.patch new file mode 100644 index 0000000000..25cbc10358 --- /dev/null +++ b/queue-5.15/dpaa2-eth-update-single_step-register-access.patch @@ -0,0 +1,234 @@ +From c3a34eba2e422582e2af976fa660b2bc2c184736 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 18 Feb 2022 22:22:01 +0200 +Subject: dpaa2-eth: Update SINGLE_STEP register access + +From: Radu Bulie + +[ Upstream commit c4680c978567328a696fd2400bbf58a36cff95d1 ] + +DPAA2 MAC supports 1588 one step timestamping. +If this option is enabled then for each transmitted PTP event packet, +the 1588 SINGLE_STEP register is accessed to modify the following fields: + +-offset of the correction field inside the PTP packet +-UDP checksum update bit, in case the PTP event packet has + UDP encapsulation + +These values can change any time, because there may be multiple +PTP clients connected, that receive various 1588 frame types: +- L2 only frame +- UDP / Ipv4 +- UDP / Ipv6 +- other + +The current implementation uses dpni_set_single_step_cfg to update the +SINLGE_STEP register. +Using an MC command on the Tx datapath for each transmitted 1588 message +introduces high delays, leading to low throughput and consequently to a +small number of supported PTP clients. Besides these, the nanosecond +correction field from the PTP packet will contain the high delay from the +driver which together with the originTimestamp will render timestamp +values that are unacceptable in a GM clock implementation. + +This patch updates the Tx datapath for 1588 messages when single step +timestamp is enabled and provides direct access to SINGLE_STEP register, +eliminating the overhead caused by the dpni_set_single_step_cfg +MC command. MC version >= 10.32 implements this functionality. +If the MC version does not have support for returning the +single step register base address, the driver will use +dpni_set_single_step_cfg command for updates operations. + +All the delay introduced by dpni_set_single_step_cfg +function will be eliminated (if MC version has support for returning the +base address of the single step register), improving the egress driver +performance for PTP packets when single step timestamping is enabled. + +Before these changes the maximum throughput for 1588 messages with +single step hardware timestamp enabled was around 2000pps. +After the updates the throughput increased up to 32.82 Mbps / 46631.02 pps. + +Signed-off-by: Radu Bulie +Signed-off-by: David S. Miller +Stable-dep-of: 2def09ead4ad ("dpaa2-eth: fix xdp_rxq_info leak") +Signed-off-by: Sasha Levin +--- + .../net/ethernet/freescale/dpaa2/dpaa2-eth.c | 89 +++++++++++++++++-- + .../net/ethernet/freescale/dpaa2/dpaa2-eth.h | 14 ++- + 2 files changed, 93 insertions(+), 10 deletions(-) + +diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c +index fa1b1b7dd8a06..acbb26cb2177e 100644 +--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c ++++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c +@@ -34,6 +34,75 @@ MODULE_DESCRIPTION("Freescale DPAA2 Ethernet Driver"); + struct ptp_qoriq *dpaa2_ptp; + EXPORT_SYMBOL(dpaa2_ptp); + ++static void dpaa2_eth_detect_features(struct dpaa2_eth_priv *priv) ++{ ++ priv->features = 0; ++ ++ if (dpaa2_eth_cmp_dpni_ver(priv, DPNI_PTP_ONESTEP_VER_MAJOR, ++ DPNI_PTP_ONESTEP_VER_MINOR) >= 0) ++ priv->features |= DPAA2_ETH_FEATURE_ONESTEP_CFG_DIRECT; ++} ++ ++static void dpaa2_update_ptp_onestep_indirect(struct dpaa2_eth_priv *priv, ++ u32 offset, u8 udp) ++{ ++ struct dpni_single_step_cfg cfg; ++ ++ cfg.en = 1; ++ cfg.ch_update = udp; ++ cfg.offset = offset; ++ cfg.peer_delay = 0; ++ ++ if (dpni_set_single_step_cfg(priv->mc_io, 0, priv->mc_token, &cfg)) ++ WARN_ONCE(1, "Failed to set single step register"); ++} ++ ++static void dpaa2_update_ptp_onestep_direct(struct dpaa2_eth_priv *priv, ++ u32 offset, u8 udp) ++{ ++ u32 val = 0; ++ ++ val = DPAA2_PTP_SINGLE_STEP_ENABLE | ++ DPAA2_PTP_SINGLE_CORRECTION_OFF(offset); ++ ++ if (udp) ++ val |= DPAA2_PTP_SINGLE_STEP_CH; ++ ++ if (priv->onestep_reg_base) ++ writel(val, priv->onestep_reg_base); ++} ++ ++static void dpaa2_ptp_onestep_reg_update_method(struct dpaa2_eth_priv *priv) ++{ ++ struct device *dev = priv->net_dev->dev.parent; ++ struct dpni_single_step_cfg ptp_cfg; ++ ++ priv->dpaa2_set_onestep_params_cb = dpaa2_update_ptp_onestep_indirect; ++ ++ if (!(priv->features & DPAA2_ETH_FEATURE_ONESTEP_CFG_DIRECT)) ++ return; ++ ++ if (dpni_get_single_step_cfg(priv->mc_io, 0, ++ priv->mc_token, &ptp_cfg)) { ++ dev_err(dev, "dpni_get_single_step_cfg cannot retrieve onestep reg, falling back to indirect update\n"); ++ return; ++ } ++ ++ if (!ptp_cfg.ptp_onestep_reg_base) { ++ dev_err(dev, "1588 onestep reg not available, falling back to indirect update\n"); ++ return; ++ } ++ ++ priv->onestep_reg_base = ioremap(ptp_cfg.ptp_onestep_reg_base, ++ sizeof(u32)); ++ if (!priv->onestep_reg_base) { ++ dev_err(dev, "1588 onestep reg cannot be mapped, falling back to indirect update\n"); ++ return; ++ } ++ ++ priv->dpaa2_set_onestep_params_cb = dpaa2_update_ptp_onestep_direct; ++} ++ + static void *dpaa2_iova_to_virt(struct iommu_domain *domain, + dma_addr_t iova_addr) + { +@@ -693,7 +762,6 @@ static void dpaa2_eth_enable_tx_tstamp(struct dpaa2_eth_priv *priv, + struct sk_buff *skb) + { + struct ptp_tstamp origin_timestamp; +- struct dpni_single_step_cfg cfg; + u8 msgtype, twostep, udp; + struct dpaa2_faead *faead; + struct dpaa2_fas *fas; +@@ -747,14 +815,12 @@ static void dpaa2_eth_enable_tx_tstamp(struct dpaa2_eth_priv *priv, + htonl(origin_timestamp.sec_lsb); + *(__be32 *)(data + offset2 + 6) = htonl(origin_timestamp.nsec); + +- cfg.en = 1; +- cfg.ch_update = udp; +- cfg.offset = offset1; +- cfg.peer_delay = 0; ++ if (priv->ptp_correction_off == offset1) ++ return; ++ ++ priv->dpaa2_set_onestep_params_cb(priv, offset1, udp); ++ priv->ptp_correction_off = offset1; + +- if (dpni_set_single_step_cfg(priv->mc_io, 0, priv->mc_token, +- &cfg)) +- WARN_ONCE(1, "Failed to set single step register"); + } + } + +@@ -2199,6 +2265,9 @@ static int dpaa2_eth_ts_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) + config.rx_filter = HWTSTAMP_FILTER_ALL; + } + ++ if (priv->tx_tstamp_type == HWTSTAMP_TX_ONESTEP_SYNC) ++ dpaa2_ptp_onestep_reg_update_method(priv); ++ + return copy_to_user(rq->ifr_data, &config, sizeof(config)) ? + -EFAULT : 0; + } +@@ -4096,6 +4165,8 @@ static int dpaa2_eth_netdev_init(struct net_device *net_dev) + return err; + } + ++ dpaa2_eth_detect_features(priv); ++ + /* Capabilities listing */ + supported |= IFF_LIVE_ADDR_CHANGE; + +@@ -4542,6 +4613,8 @@ static int dpaa2_eth_remove(struct fsl_mc_device *ls_dev) + dpaa2_eth_free_dpbp(priv); + dpaa2_eth_free_dpio(priv); + dpaa2_eth_free_dpni(priv); ++ if (priv->onestep_reg_base) ++ iounmap(priv->onestep_reg_base); + + fsl_mc_portal_free(priv->mc_io); + +diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h +index 67fd926331fed..805e5619e1e63 100644 +--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h ++++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h +@@ -506,12 +506,15 @@ struct dpaa2_eth_priv { + u8 num_channels; + struct dpaa2_eth_channel *channel[DPAA2_ETH_MAX_DPCONS]; + struct dpaa2_eth_sgt_cache __percpu *sgt_cache; +- ++ unsigned long features; + struct dpni_attr dpni_attrs; + u16 dpni_ver_major; + u16 dpni_ver_minor; + u16 tx_data_offset; +- ++ void __iomem *onestep_reg_base; ++ u8 ptp_correction_off; ++ void (*dpaa2_set_onestep_params_cb)(struct dpaa2_eth_priv *priv, ++ u32 offset, u8 udp); + struct fsl_mc_device *dpbp_dev; + u16 rx_buf_size; + u16 bpid; +@@ -651,6 +654,13 @@ enum dpaa2_eth_rx_dist { + #define DPAA2_ETH_DIST_L4DST BIT(8) + #define DPAA2_ETH_DIST_ALL (~0ULL) + ++#define DPNI_PTP_ONESTEP_VER_MAJOR 8 ++#define DPNI_PTP_ONESTEP_VER_MINOR 2 ++#define DPAA2_ETH_FEATURE_ONESTEP_CFG_DIRECT BIT(0) ++#define DPAA2_PTP_SINGLE_STEP_ENABLE BIT(31) ++#define DPAA2_PTP_SINGLE_STEP_CH BIT(7) ++#define DPAA2_PTP_SINGLE_CORRECTION_OFF(v) ((v) << 8) ++ + #define DPNI_PAUSE_VER_MAJOR 7 + #define DPNI_PAUSE_VER_MINOR 13 + #define dpaa2_eth_has_pause_support(priv) \ +-- +2.39.5 + diff --git a/queue-5.15/drm-v3d-disable-interrupts-before-resetting-the-gpu.patch b/queue-5.15/drm-v3d-disable-interrupts-before-resetting-the-gpu.patch new file mode 100644 index 0000000000..622e1d6110 --- /dev/null +++ b/queue-5.15/drm-v3d-disable-interrupts-before-resetting-the-gpu.patch @@ -0,0 +1,217 @@ +From 3c2b6a858023e8bc31477619ad8269976f9dad61 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 28 Jun 2025 19:42:42 -0300 +Subject: drm/v3d: Disable interrupts before resetting the GPU +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Maíra Canal + +[ Upstream commit 226862f50a7a88e4e4de9abbf36c64d19acd6fd0 ] + +Currently, an interrupt can be triggered during a GPU reset, which can +lead to GPU hangs and NULL pointer dereference in an interrupt context +as shown in the following trace: + + [ 314.035040] Unable to handle kernel NULL pointer dereference at virtual address 00000000000000c0 + [ 314.043822] Mem abort info: + [ 314.046606] ESR = 0x0000000096000005 + [ 314.050347] EC = 0x25: DABT (current EL), IL = 32 bits + [ 314.055651] SET = 0, FnV = 0 + [ 314.058695] EA = 0, S1PTW = 0 + [ 314.061826] FSC = 0x05: level 1 translation fault + [ 314.066694] Data abort info: + [ 314.069564] ISV = 0, ISS = 0x00000005, ISS2 = 0x00000000 + [ 314.075039] CM = 0, WnR = 0, TnD = 0, TagAccess = 0 + [ 314.080080] GCS = 0, Overlay = 0, DirtyBit = 0, Xs = 0 + [ 314.085382] user pgtable: 4k pages, 39-bit VAs, pgdp=0000000102728000 + [ 314.091814] [00000000000000c0] pgd=0000000000000000, p4d=0000000000000000, pud=0000000000000000 + [ 314.100511] Internal error: Oops: 0000000096000005 [#1] PREEMPT SMP + [ 314.106770] Modules linked in: v3d i2c_brcmstb vc4 snd_soc_hdmi_codec gpu_sched drm_shmem_helper drm_display_helper cec drm_dma_helper drm_kms_helper drm drm_panel_orientation_quirks snd_soc_core snd_compress snd_pcm_dmaengine snd_pcm snd_timer snd backlight + [ 314.129654] CPU: 0 UID: 0 PID: 0 Comm: swapper/0 Not tainted 6.12.25+rpt-rpi-v8 #1 Debian 1:6.12.25-1+rpt1 + [ 314.139388] Hardware name: Raspberry Pi 4 Model B Rev 1.4 (DT) + [ 314.145211] pstate: 600000c5 (nZCv daIF -PAN -UAO -TCO -DIT -SSBS BTYPE=--) + [ 314.152165] pc : v3d_irq+0xec/0x2e0 [v3d] + [ 314.156187] lr : v3d_irq+0xe0/0x2e0 [v3d] + [ 314.160198] sp : ffffffc080003ea0 + [ 314.163502] x29: ffffffc080003ea0 x28: ffffffec1f184980 x27: 021202b000000000 + [ 314.170633] x26: ffffffec1f17f630 x25: ffffff8101372000 x24: ffffffec1f17d9f0 + [ 314.177764] x23: 000000000000002a x22: 000000000000002a x21: ffffff8103252000 + [ 314.184895] x20: 0000000000000001 x19: 00000000deadbeef x18: 0000000000000000 + [ 314.192026] x17: ffffff94e51d2000 x16: ffffffec1dac3cb0 x15: c306000000000000 + [ 314.199156] x14: 0000000000000000 x13: b2fc982e03cc5168 x12: 0000000000000001 + [ 314.206286] x11: ffffff8103f8bcc0 x10: ffffffec1f196868 x9 : ffffffec1dac3874 + [ 314.213416] x8 : 0000000000000000 x7 : 0000000000042a3a x6 : ffffff810017a180 + [ 314.220547] x5 : ffffffec1ebad400 x4 : ffffffec1ebad320 x3 : 00000000000bebeb + [ 314.227677] x2 : 0000000000000000 x1 : 0000000000000000 x0 : 0000000000000000 + [ 314.234807] Call trace: + [ 314.237243] v3d_irq+0xec/0x2e0 [v3d] + [ 314.240906] __handle_irq_event_percpu+0x58/0x218 + [ 314.245609] handle_irq_event+0x54/0xb8 + [ 314.249439] handle_fasteoi_irq+0xac/0x240 + [ 314.253527] handle_irq_desc+0x48/0x68 + [ 314.257269] generic_handle_domain_irq+0x24/0x38 + [ 314.261879] gic_handle_irq+0x48/0xd8 + [ 314.265533] call_on_irq_stack+0x24/0x58 + [ 314.269448] do_interrupt_handler+0x88/0x98 + [ 314.273624] el1_interrupt+0x34/0x68 + [ 314.277193] el1h_64_irq_handler+0x18/0x28 + [ 314.281281] el1h_64_irq+0x64/0x68 + [ 314.284673] default_idle_call+0x3c/0x168 + [ 314.288675] do_idle+0x1fc/0x230 + [ 314.291895] cpu_startup_entry+0x3c/0x50 + [ 314.295810] rest_init+0xe4/0xf0 + [ 314.299030] start_kernel+0x5e8/0x790 + [ 314.302684] __primary_switched+0x80/0x90 + [ 314.306691] Code: 940029eb 360ffc13 f9442ea0 52800001 (f9406017) + [ 314.312775] ---[ end trace 0000000000000000 ]--- + [ 314.317384] Kernel panic - not syncing: Oops: Fatal exception in interrupt + [ 314.324249] SMP: stopping secondary CPUs + [ 314.328167] Kernel Offset: 0x2b9da00000 from 0xffffffc080000000 + [ 314.334076] PHYS_OFFSET: 0x0 + [ 314.336946] CPU features: 0x08,00002013,c0200000,0200421b + [ 314.342337] Memory Limit: none + [ 314.345382] ---[ end Kernel panic - not syncing: Oops: Fatal exception in interrupt ]--- + +Before resetting the GPU, it's necessary to disable all interrupts and +deal with any interrupt handler still in-flight. Otherwise, the GPU might +reset with jobs still running, or yet, an interrupt could be handled +during the reset. + +Cc: stable@vger.kernel.org +Fixes: 57692c94dcbe ("drm/v3d: Introduce a new DRM driver for Broadcom V3D V3.x+") +Reviewed-by: Juan A. Suarez +Reviewed-by: Iago Toral Quiroga +Link: https://lore.kernel.org/r/20250628224243.47599-1-mcanal@igalia.com +Signed-off-by: Maíra Canal +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/v3d/v3d_drv.h | 8 +++++++ + drivers/gpu/drm/v3d/v3d_gem.c | 2 ++ + drivers/gpu/drm/v3d/v3d_irq.c | 39 +++++++++++++++++++++++++---------- + 3 files changed, 38 insertions(+), 11 deletions(-) + +diff --git a/drivers/gpu/drm/v3d/v3d_drv.h b/drivers/gpu/drm/v3d/v3d_drv.h +index 2701347790730..0d551b1d9b05d 100644 +--- a/drivers/gpu/drm/v3d/v3d_drv.h ++++ b/drivers/gpu/drm/v3d/v3d_drv.h +@@ -71,6 +71,12 @@ struct v3d_perfmon { + u64 values[]; + }; + ++enum v3d_irq { ++ V3D_CORE_IRQ, ++ V3D_HUB_IRQ, ++ V3D_MAX_IRQS, ++}; ++ + struct v3d_dev { + struct drm_device drm; + +@@ -80,6 +86,8 @@ struct v3d_dev { + int ver; + bool single_irq_line; + ++ int irq[V3D_MAX_IRQS]; ++ + void __iomem *hub_regs; + void __iomem *core_regs[3]; + void __iomem *bridge_regs; +diff --git a/drivers/gpu/drm/v3d/v3d_gem.c b/drivers/gpu/drm/v3d/v3d_gem.c +index 805d6f6cba0e2..ecd03ad9699a0 100644 +--- a/drivers/gpu/drm/v3d/v3d_gem.c ++++ b/drivers/gpu/drm/v3d/v3d_gem.c +@@ -120,6 +120,8 @@ v3d_reset(struct v3d_dev *v3d) + if (false) + v3d_idle_axi(v3d, 0); + ++ v3d_irq_disable(v3d); ++ + v3d_idle_gca(v3d); + v3d_reset_v3d(v3d); + +diff --git a/drivers/gpu/drm/v3d/v3d_irq.c b/drivers/gpu/drm/v3d/v3d_irq.c +index b2d59a1686972..9aba78e6d7a5a 100644 +--- a/drivers/gpu/drm/v3d/v3d_irq.c ++++ b/drivers/gpu/drm/v3d/v3d_irq.c +@@ -215,7 +215,7 @@ v3d_hub_irq(int irq, void *arg) + int + v3d_irq_init(struct v3d_dev *v3d) + { +- int irq1, ret, core; ++ int irq, ret, core; + + INIT_WORK(&v3d->overflow_mem_work, v3d_overflow_mem_work); + +@@ -226,17 +226,24 @@ v3d_irq_init(struct v3d_dev *v3d) + V3D_CORE_WRITE(core, V3D_CTL_INT_CLR, V3D_CORE_IRQS); + V3D_WRITE(V3D_HUB_INT_CLR, V3D_HUB_IRQS); + +- irq1 = platform_get_irq_optional(v3d_to_pdev(v3d), 1); +- if (irq1 == -EPROBE_DEFER) +- return irq1; +- if (irq1 > 0) { +- ret = devm_request_irq(v3d->drm.dev, irq1, ++ irq = platform_get_irq_optional(v3d_to_pdev(v3d), 1); ++ if (irq == -EPROBE_DEFER) ++ return irq; ++ if (irq > 0) { ++ v3d->irq[V3D_CORE_IRQ] = irq; ++ ++ ret = devm_request_irq(v3d->drm.dev, v3d->irq[V3D_CORE_IRQ], + v3d_irq, IRQF_SHARED, + "v3d_core0", v3d); + if (ret) + goto fail; +- ret = devm_request_irq(v3d->drm.dev, +- platform_get_irq(v3d_to_pdev(v3d), 0), ++ ++ irq = platform_get_irq(v3d_to_pdev(v3d), 0); ++ if (irq < 0) ++ return irq; ++ v3d->irq[V3D_HUB_IRQ] = irq; ++ ++ ret = devm_request_irq(v3d->drm.dev, v3d->irq[V3D_HUB_IRQ], + v3d_hub_irq, IRQF_SHARED, + "v3d_hub", v3d); + if (ret) +@@ -244,8 +251,12 @@ v3d_irq_init(struct v3d_dev *v3d) + } else { + v3d->single_irq_line = true; + +- ret = devm_request_irq(v3d->drm.dev, +- platform_get_irq(v3d_to_pdev(v3d), 0), ++ irq = platform_get_irq(v3d_to_pdev(v3d), 0); ++ if (irq < 0) ++ return irq; ++ v3d->irq[V3D_CORE_IRQ] = irq; ++ ++ ret = devm_request_irq(v3d->drm.dev, v3d->irq[V3D_CORE_IRQ], + v3d_irq, IRQF_SHARED, + "v3d", v3d); + if (ret) +@@ -279,13 +290,19 @@ v3d_irq_enable(struct v3d_dev *v3d) + void + v3d_irq_disable(struct v3d_dev *v3d) + { +- int core; ++ int core, i; + + /* Disable all interrupts. */ + for (core = 0; core < v3d->cores; core++) + V3D_CORE_WRITE(core, V3D_CTL_INT_MSK_SET, ~0); + V3D_WRITE(V3D_HUB_INT_MSK_SET, ~0); + ++ /* Finish any interrupt handler still in flight. */ ++ for (i = 0; i < V3D_MAX_IRQS; i++) { ++ if (v3d->irq[i]) ++ synchronize_irq(v3d->irq[i]); ++ } ++ + /* Clear any pending interrupts we might have left. */ + for (core = 0; core < v3d->cores; core++) + V3D_CORE_WRITE(core, V3D_CTL_INT_CLR, V3D_CORE_IRQS); +-- +2.39.5 + diff --git a/queue-5.15/ethernet-atl1-add-missing-dma-mapping-error-checks-a.patch b/queue-5.15/ethernet-atl1-add-missing-dma-mapping-error-checks-a.patch new file mode 100644 index 0000000000..d3823e9513 --- /dev/null +++ b/queue-5.15/ethernet-atl1-add-missing-dma-mapping-error-checks-a.patch @@ -0,0 +1,212 @@ +From 120ac71b5a511a9d1a5c2fec0560a43d7fd8e17f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 25 Jun 2025 16:16:24 +0200 +Subject: ethernet: atl1: Add missing DMA mapping error checks and count errors + +From: Thomas Fourier + +[ Upstream commit d72411d20905180cdc452c553be17481b24463d2 ] + +The `dma_map_XXX()` functions can fail and must be checked using +`dma_mapping_error()`. This patch adds proper error handling for all +DMA mapping calls. + +In `atl1_alloc_rx_buffers()`, if DMA mapping fails, the buffer is +deallocated and marked accordingly. + +In `atl1_tx_map()`, previously mapped buffers are unmapped and the +packet is dropped on failure. + +If `atl1_xmit_frame()` drops the packet, increment the tx_error counter. + +Fixes: f3cc28c79760 ("Add Attansic L1 ethernet driver.") +Signed-off-by: Thomas Fourier +Link: https://patch.msgid.link/20250625141629.114984-2-fourier.thomas@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/atheros/atlx/atl1.c | 78 +++++++++++++++++------- + 1 file changed, 56 insertions(+), 22 deletions(-) + +diff --git a/drivers/net/ethernet/atheros/atlx/atl1.c b/drivers/net/ethernet/atheros/atlx/atl1.c +index 68f6c0bbd945c..ca8d7a82407b6 100644 +--- a/drivers/net/ethernet/atheros/atlx/atl1.c ++++ b/drivers/net/ethernet/atheros/atlx/atl1.c +@@ -1861,14 +1861,21 @@ static u16 atl1_alloc_rx_buffers(struct atl1_adapter *adapter) + break; + } + +- buffer_info->alloced = 1; +- buffer_info->skb = skb; +- buffer_info->length = (u16) adapter->rx_buffer_len; + page = virt_to_page(skb->data); + offset = offset_in_page(skb->data); + buffer_info->dma = dma_map_page(&pdev->dev, page, offset, + adapter->rx_buffer_len, + DMA_FROM_DEVICE); ++ if (dma_mapping_error(&pdev->dev, buffer_info->dma)) { ++ kfree_skb(skb); ++ adapter->soft_stats.rx_dropped++; ++ break; ++ } ++ ++ buffer_info->alloced = 1; ++ buffer_info->skb = skb; ++ buffer_info->length = (u16)adapter->rx_buffer_len; ++ + rfd_desc->buffer_addr = cpu_to_le64(buffer_info->dma); + rfd_desc->buf_len = cpu_to_le16(adapter->rx_buffer_len); + rfd_desc->coalese = 0; +@@ -2180,8 +2187,8 @@ static int atl1_tx_csum(struct atl1_adapter *adapter, struct sk_buff *skb, + return 0; + } + +-static void atl1_tx_map(struct atl1_adapter *adapter, struct sk_buff *skb, +- struct tx_packet_desc *ptpd) ++static bool atl1_tx_map(struct atl1_adapter *adapter, struct sk_buff *skb, ++ struct tx_packet_desc *ptpd) + { + struct atl1_tpd_ring *tpd_ring = &adapter->tpd_ring; + struct atl1_buffer *buffer_info; +@@ -2191,6 +2198,7 @@ static void atl1_tx_map(struct atl1_adapter *adapter, struct sk_buff *skb, + unsigned int nr_frags; + unsigned int f; + int retval; ++ u16 first_mapped; + u16 next_to_use; + u16 data_len; + u8 hdr_len; +@@ -2198,6 +2206,7 @@ static void atl1_tx_map(struct atl1_adapter *adapter, struct sk_buff *skb, + buf_len -= skb->data_len; + nr_frags = skb_shinfo(skb)->nr_frags; + next_to_use = atomic_read(&tpd_ring->next_to_use); ++ first_mapped = next_to_use; + buffer_info = &tpd_ring->buffer_info[next_to_use]; + BUG_ON(buffer_info->skb); + /* put skb in last TPD */ +@@ -2213,6 +2222,8 @@ static void atl1_tx_map(struct atl1_adapter *adapter, struct sk_buff *skb, + buffer_info->dma = dma_map_page(&adapter->pdev->dev, page, + offset, hdr_len, + DMA_TO_DEVICE); ++ if (dma_mapping_error(&adapter->pdev->dev, buffer_info->dma)) ++ goto dma_err; + + if (++next_to_use == tpd_ring->count) + next_to_use = 0; +@@ -2239,6 +2250,9 @@ static void atl1_tx_map(struct atl1_adapter *adapter, struct sk_buff *skb, + page, offset, + buffer_info->length, + DMA_TO_DEVICE); ++ if (dma_mapping_error(&adapter->pdev->dev, ++ buffer_info->dma)) ++ goto dma_err; + if (++next_to_use == tpd_ring->count) + next_to_use = 0; + } +@@ -2251,6 +2265,8 @@ static void atl1_tx_map(struct atl1_adapter *adapter, struct sk_buff *skb, + buffer_info->dma = dma_map_page(&adapter->pdev->dev, page, + offset, buf_len, + DMA_TO_DEVICE); ++ if (dma_mapping_error(&adapter->pdev->dev, buffer_info->dma)) ++ goto dma_err; + if (++next_to_use == tpd_ring->count) + next_to_use = 0; + } +@@ -2274,6 +2290,9 @@ static void atl1_tx_map(struct atl1_adapter *adapter, struct sk_buff *skb, + buffer_info->dma = skb_frag_dma_map(&adapter->pdev->dev, + frag, i * ATL1_MAX_TX_BUF_LEN, + buffer_info->length, DMA_TO_DEVICE); ++ if (dma_mapping_error(&adapter->pdev->dev, ++ buffer_info->dma)) ++ goto dma_err; + + if (++next_to_use == tpd_ring->count) + next_to_use = 0; +@@ -2282,6 +2301,22 @@ static void atl1_tx_map(struct atl1_adapter *adapter, struct sk_buff *skb, + + /* last tpd's buffer-info */ + buffer_info->skb = skb; ++ ++ return true; ++ ++ dma_err: ++ while (first_mapped != next_to_use) { ++ buffer_info = &tpd_ring->buffer_info[first_mapped]; ++ dma_unmap_page(&adapter->pdev->dev, ++ buffer_info->dma, ++ buffer_info->length, ++ DMA_TO_DEVICE); ++ buffer_info->dma = 0; ++ ++ if (++first_mapped == tpd_ring->count) ++ first_mapped = 0; ++ } ++ return false; + } + + static void atl1_tx_queue(struct atl1_adapter *adapter, u16 count, +@@ -2352,10 +2387,8 @@ static netdev_tx_t atl1_xmit_frame(struct sk_buff *skb, + + len = skb_headlen(skb); + +- if (unlikely(skb->len <= 0)) { +- dev_kfree_skb_any(skb); +- return NETDEV_TX_OK; +- } ++ if (unlikely(skb->len <= 0)) ++ goto drop_packet; + + nr_frags = skb_shinfo(skb)->nr_frags; + for (f = 0; f < nr_frags; f++) { +@@ -2369,10 +2402,8 @@ static netdev_tx_t atl1_xmit_frame(struct sk_buff *skb, + if (skb->protocol == htons(ETH_P_IP)) { + proto_hdr_len = (skb_transport_offset(skb) + + tcp_hdrlen(skb)); +- if (unlikely(proto_hdr_len > len)) { +- dev_kfree_skb_any(skb); +- return NETDEV_TX_OK; +- } ++ if (unlikely(proto_hdr_len > len)) ++ goto drop_packet; + /* need additional TPD ? */ + if (proto_hdr_len != len) + count += (len - proto_hdr_len + +@@ -2404,23 +2435,26 @@ static netdev_tx_t atl1_xmit_frame(struct sk_buff *skb, + } + + tso = atl1_tso(adapter, skb, ptpd); +- if (tso < 0) { +- dev_kfree_skb_any(skb); +- return NETDEV_TX_OK; +- } ++ if (tso < 0) ++ goto drop_packet; + + if (!tso) { + ret_val = atl1_tx_csum(adapter, skb, ptpd); +- if (ret_val < 0) { +- dev_kfree_skb_any(skb); +- return NETDEV_TX_OK; +- } ++ if (ret_val < 0) ++ goto drop_packet; + } + +- atl1_tx_map(adapter, skb, ptpd); ++ if (!atl1_tx_map(adapter, skb, ptpd)) ++ goto drop_packet; ++ + atl1_tx_queue(adapter, count, ptpd); + atl1_update_mailbox(adapter); + return NETDEV_TX_OK; ++ ++drop_packet: ++ adapter->soft_stats.tx_errors++; ++ dev_kfree_skb_any(skb); ++ return NETDEV_TX_OK; + } + + static int atl1_rings_clean(struct napi_struct *napi, int budget) +-- +2.39.5 + diff --git a/queue-5.15/mmc-core-sd-apply-broken_sd_discard-quirk-earlier.patch b/queue-5.15/mmc-core-sd-apply-broken_sd_discard-quirk-earlier.patch new file mode 100644 index 0000000000..71dc6a7735 --- /dev/null +++ b/queue-5.15/mmc-core-sd-apply-broken_sd_discard-quirk-earlier.patch @@ -0,0 +1,65 @@ +From 25a4800edcd012c6b239cbc557907a9a46a5be30 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 26 May 2025 14:44:45 +0300 +Subject: mmc: core: sd: Apply BROKEN_SD_DISCARD quirk earlier + +From: Avri Altman + +[ Upstream commit 009c3a4bc41e855fd76f92727f9fbae4e5917d7f ] + +Move the BROKEN_SD_DISCARD quirk for certain SanDisk SD cards from the +`mmc_blk_fixups[]` to `mmc_sd_fixups[]`. This ensures the quirk is +applied earlier in the device initialization process, aligning with the +reasoning in [1]. Applying the quirk sooner prevents the kernel from +incorrectly enabling discard support on affected cards during initial +setup. + +[1] https://lore.kernel.org/all/20240820230631.GA436523@sony.com + +Fixes: 07d2872bf4c8 ("mmc: core: Add SD card quirk for broken discard") +Signed-off-by: Avri Altman +Cc: stable@vger.kernel.org +Link: https://lore.kernel.org/r/20250526114445.675548-1-avri.altman@sandisk.com +Signed-off-by: Ulf Hansson +Signed-off-by: Sasha Levin +--- + drivers/mmc/core/quirks.h | 16 +++++++++------- + 1 file changed, 9 insertions(+), 7 deletions(-) + +diff --git a/drivers/mmc/core/quirks.h b/drivers/mmc/core/quirks.h +index afe8d8c5fa8a2..2920633e2ae1e 100644 +--- a/drivers/mmc/core/quirks.h ++++ b/drivers/mmc/core/quirks.h +@@ -14,6 +14,15 @@ + + #include "card.h" + ++static const struct mmc_fixup __maybe_unused mmc_sd_fixups[] = { ++ /* ++ * Some SD cards reports discard support while they don't ++ */ ++ MMC_FIXUP(CID_NAME_ANY, CID_MANFID_SANDISK_SD, 0x5344, add_quirk_sd, ++ MMC_QUIRK_BROKEN_SD_DISCARD), ++ ++ END_FIXUP ++}; + static const struct mmc_fixup __maybe_unused mmc_blk_fixups[] = { + #define INAND_CMD38_ARG_EXT_CSD 113 + #define INAND_CMD38_ARG_ERASE 0x00 +@@ -112,13 +121,6 @@ static const struct mmc_fixup __maybe_unused mmc_blk_fixups[] = { + */ + MMC_FIXUP("Q2J54A", CID_MANFID_MICRON, 0x014e, add_quirk_mmc, + MMC_QUIRK_TRIM_BROKEN), +- +- /* +- * Some SD cards reports discard support while they don't +- */ +- MMC_FIXUP(CID_NAME_ANY, CID_MANFID_SANDISK_SD, 0x5344, add_quirk_sd, +- MMC_QUIRK_BROKEN_SD_DISCARD), +- + END_FIXUP + }; + +-- +2.39.5 + diff --git a/queue-5.15/net-dpaa2-eth-rearrange-variable-in-dpaa2_eth_get_et.patch b/queue-5.15/net-dpaa2-eth-rearrange-variable-in-dpaa2_eth_get_et.patch new file mode 100644 index 0000000000..fab35708df --- /dev/null +++ b/queue-5.15/net-dpaa2-eth-rearrange-variable-in-dpaa2_eth_get_et.patch @@ -0,0 +1,62 @@ +From fd2f7d2736b04cbda92a15cbda7d6e6c738f4c3d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 18 Oct 2022 17:18:51 +0300 +Subject: net: dpaa2-eth: rearrange variable in dpaa2_eth_get_ethtool_stats + +From: Ioana Ciornei + +[ Upstream commit 3313206827678f6f036eca601a51f6c4524b559a ] + +Rearrange the variables in the dpaa2_eth_get_ethtool_stats() function so +that we adhere to the reverse Christmas tree rule. +Also, in the next patch we are adding more variables and I didn't know +where to place them with the current ordering. + +Signed-off-by: Ioana Ciornei +Signed-off-by: David S. Miller +Stable-dep-of: 2def09ead4ad ("dpaa2-eth: fix xdp_rxq_info leak") +Signed-off-by: Sasha Levin +--- + .../ethernet/freescale/dpaa2/dpaa2-ethtool.c | 18 ++++++++---------- + 1 file changed, 8 insertions(+), 10 deletions(-) + +diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c +index 2da5f881f6302..3a310d92ef2f7 100644 +--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c ++++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c +@@ -225,17 +225,8 @@ static void dpaa2_eth_get_ethtool_stats(struct net_device *net_dev, + struct ethtool_stats *stats, + u64 *data) + { +- int i = 0; +- int j, k, err; +- int num_cnt; +- union dpni_statistics dpni_stats; +- u32 fcnt, bcnt; +- u32 fcnt_rx_total = 0, fcnt_tx_total = 0; +- u32 bcnt_rx_total = 0, bcnt_tx_total = 0; +- u32 buf_cnt; + struct dpaa2_eth_priv *priv = netdev_priv(net_dev); +- struct dpaa2_eth_drv_stats *extras; +- struct dpaa2_eth_ch_stats *ch_stats; ++ union dpni_statistics dpni_stats; + int dpni_stats_page_size[DPNI_STATISTICS_CNT] = { + sizeof(dpni_stats.page_0), + sizeof(dpni_stats.page_1), +@@ -245,6 +236,13 @@ static void dpaa2_eth_get_ethtool_stats(struct net_device *net_dev, + sizeof(dpni_stats.page_5), + sizeof(dpni_stats.page_6), + }; ++ u32 fcnt_rx_total = 0, fcnt_tx_total = 0; ++ u32 bcnt_rx_total = 0, bcnt_tx_total = 0; ++ struct dpaa2_eth_ch_stats *ch_stats; ++ struct dpaa2_eth_drv_stats *extras; ++ int j, k, err, num_cnt, i = 0; ++ u32 fcnt, bcnt; ++ u32 buf_cnt; + + memset(data, 0, + sizeof(u64) * (DPAA2_ETH_NUM_STATS + DPAA2_ETH_NUM_EXTRA_STATS)); +-- +2.39.5 + diff --git a/queue-5.15/nfsv4-flexfiles-fix-handling-of-nfs-level-errors-in-.patch b/queue-5.15/nfsv4-flexfiles-fix-handling-of-nfs-level-errors-in-.patch new file mode 100644 index 0000000000..d19e2cc07a --- /dev/null +++ b/queue-5.15/nfsv4-flexfiles-fix-handling-of-nfs-level-errors-in-.patch @@ -0,0 +1,250 @@ +From d4f86625444c5b1a84cf044a8da842b25bd6e2de Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 19 Jun 2025 15:16:11 -0400 +Subject: NFSv4/flexfiles: Fix handling of NFS level errors in I/O + +From: Trond Myklebust + +[ Upstream commit 38074de35b015df5623f524d6f2b49a0cd395c40 ] + +Allow the flexfiles error handling to recognise NFS level errors (as +opposed to RPC level errors) and handle them separately. The main +motivator is the NFSERR_PERM errors that get returned if the NFS client +connects to the data server through a port number that is lower than +1024. In that case, the client should disconnect and retry a READ on a +different data server, or it should retry a WRITE after reconnecting. + +Reviewed-by: Tigran Mkrtchyan +Fixes: d67ae825a59d ("pnfs/flexfiles: Add the FlexFile Layout Driver") +Signed-off-by: Trond Myklebust +Signed-off-by: Anna Schumaker +Signed-off-by: Sasha Levin +--- + fs/nfs/flexfilelayout/flexfilelayout.c | 121 ++++++++++++++++++------- + 1 file changed, 87 insertions(+), 34 deletions(-) + +diff --git a/fs/nfs/flexfilelayout/flexfilelayout.c b/fs/nfs/flexfilelayout/flexfilelayout.c +index a55eec241657d..7a568d2de472e 100644 +--- a/fs/nfs/flexfilelayout/flexfilelayout.c ++++ b/fs/nfs/flexfilelayout/flexfilelayout.c +@@ -1099,6 +1099,7 @@ static void ff_layout_reset_read(struct nfs_pgio_header *hdr) + } + + static int ff_layout_async_handle_error_v4(struct rpc_task *task, ++ u32 op_status, + struct nfs4_state *state, + struct nfs_client *clp, + struct pnfs_layout_segment *lseg, +@@ -1109,32 +1110,42 @@ static int ff_layout_async_handle_error_v4(struct rpc_task *task, + struct nfs4_deviceid_node *devid = FF_LAYOUT_DEVID_NODE(lseg, idx); + struct nfs4_slot_table *tbl = &clp->cl_session->fc_slot_table; + +- switch (task->tk_status) { +- case -NFS4ERR_BADSESSION: +- case -NFS4ERR_BADSLOT: +- case -NFS4ERR_BAD_HIGH_SLOT: +- case -NFS4ERR_DEADSESSION: +- case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION: +- case -NFS4ERR_SEQ_FALSE_RETRY: +- case -NFS4ERR_SEQ_MISORDERED: ++ switch (op_status) { ++ case NFS4_OK: ++ case NFS4ERR_NXIO: ++ break; ++ case NFSERR_PERM: ++ if (!task->tk_xprt) ++ break; ++ xprt_force_disconnect(task->tk_xprt); ++ goto out_retry; ++ case NFS4ERR_BADSESSION: ++ case NFS4ERR_BADSLOT: ++ case NFS4ERR_BAD_HIGH_SLOT: ++ case NFS4ERR_DEADSESSION: ++ case NFS4ERR_CONN_NOT_BOUND_TO_SESSION: ++ case NFS4ERR_SEQ_FALSE_RETRY: ++ case NFS4ERR_SEQ_MISORDERED: + dprintk("%s ERROR %d, Reset session. Exchangeid " + "flags 0x%x\n", __func__, task->tk_status, + clp->cl_exchange_flags); + nfs4_schedule_session_recovery(clp->cl_session, task->tk_status); +- break; +- case -NFS4ERR_DELAY: +- case -NFS4ERR_GRACE: ++ goto out_retry; ++ case NFS4ERR_DELAY: ++ nfs_inc_stats(lseg->pls_layout->plh_inode, NFSIOS_DELAY); ++ fallthrough; ++ case NFS4ERR_GRACE: + rpc_delay(task, FF_LAYOUT_POLL_RETRY_MAX); +- break; +- case -NFS4ERR_RETRY_UNCACHED_REP: +- break; ++ goto out_retry; ++ case NFS4ERR_RETRY_UNCACHED_REP: ++ goto out_retry; + /* Invalidate Layout errors */ +- case -NFS4ERR_PNFS_NO_LAYOUT: +- case -ESTALE: /* mapped NFS4ERR_STALE */ +- case -EBADHANDLE: /* mapped NFS4ERR_BADHANDLE */ +- case -EISDIR: /* mapped NFS4ERR_ISDIR */ +- case -NFS4ERR_FHEXPIRED: +- case -NFS4ERR_WRONG_TYPE: ++ case NFS4ERR_PNFS_NO_LAYOUT: ++ case NFS4ERR_STALE: ++ case NFS4ERR_BADHANDLE: ++ case NFS4ERR_ISDIR: ++ case NFS4ERR_FHEXPIRED: ++ case NFS4ERR_WRONG_TYPE: + dprintk("%s Invalid layout error %d\n", __func__, + task->tk_status); + /* +@@ -1147,6 +1158,11 @@ static int ff_layout_async_handle_error_v4(struct rpc_task *task, + pnfs_destroy_layout(NFS_I(inode)); + rpc_wake_up(&tbl->slot_tbl_waitq); + goto reset; ++ default: ++ break; ++ } ++ ++ switch (task->tk_status) { + /* RPC connection errors */ + case -ECONNREFUSED: + case -EHOSTDOWN: +@@ -1162,26 +1178,56 @@ static int ff_layout_async_handle_error_v4(struct rpc_task *task, + nfs4_delete_deviceid(devid->ld, devid->nfs_client, + &devid->deviceid); + rpc_wake_up(&tbl->slot_tbl_waitq); +- fallthrough; ++ break; + default: +- if (ff_layout_avoid_mds_available_ds(lseg)) +- return -NFS4ERR_RESET_TO_PNFS; +-reset: +- dprintk("%s Retry through MDS. Error %d\n", __func__, +- task->tk_status); +- return -NFS4ERR_RESET_TO_MDS; ++ break; + } ++ ++ if (ff_layout_avoid_mds_available_ds(lseg)) ++ return -NFS4ERR_RESET_TO_PNFS; ++reset: ++ dprintk("%s Retry through MDS. Error %d\n", __func__, ++ task->tk_status); ++ return -NFS4ERR_RESET_TO_MDS; ++ ++out_retry: + task->tk_status = 0; + return -EAGAIN; + } + + /* Retry all errors through either pNFS or MDS except for -EJUKEBOX */ + static int ff_layout_async_handle_error_v3(struct rpc_task *task, ++ u32 op_status, ++ struct nfs_client *clp, + struct pnfs_layout_segment *lseg, + u32 idx) + { + struct nfs4_deviceid_node *devid = FF_LAYOUT_DEVID_NODE(lseg, idx); + ++ switch (op_status) { ++ case NFS_OK: ++ case NFSERR_NXIO: ++ break; ++ case NFSERR_PERM: ++ if (!task->tk_xprt) ++ break; ++ xprt_force_disconnect(task->tk_xprt); ++ goto out_retry; ++ case NFSERR_ACCES: ++ case NFSERR_BADHANDLE: ++ case NFSERR_FBIG: ++ case NFSERR_IO: ++ case NFSERR_NOSPC: ++ case NFSERR_ROFS: ++ case NFSERR_STALE: ++ goto out_reset_to_pnfs; ++ case NFSERR_JUKEBOX: ++ nfs_inc_stats(lseg->pls_layout->plh_inode, NFSIOS_DELAY); ++ goto out_retry; ++ default: ++ break; ++ } ++ + switch (task->tk_status) { + /* File access problems. Don't mark the device as unavailable */ + case -EACCES: +@@ -1200,6 +1246,7 @@ static int ff_layout_async_handle_error_v3(struct rpc_task *task, + nfs4_delete_deviceid(devid->ld, devid->nfs_client, + &devid->deviceid); + } ++out_reset_to_pnfs: + /* FIXME: Need to prevent infinite looping here. */ + return -NFS4ERR_RESET_TO_PNFS; + out_retry: +@@ -1210,6 +1257,7 @@ static int ff_layout_async_handle_error_v3(struct rpc_task *task, + } + + static int ff_layout_async_handle_error(struct rpc_task *task, ++ u32 op_status, + struct nfs4_state *state, + struct nfs_client *clp, + struct pnfs_layout_segment *lseg, +@@ -1228,10 +1276,11 @@ static int ff_layout_async_handle_error(struct rpc_task *task, + + switch (vers) { + case 3: +- return ff_layout_async_handle_error_v3(task, lseg, idx); +- case 4: +- return ff_layout_async_handle_error_v4(task, state, clp, ++ return ff_layout_async_handle_error_v3(task, op_status, clp, + lseg, idx); ++ case 4: ++ return ff_layout_async_handle_error_v4(task, op_status, state, ++ clp, lseg, idx); + default: + /* should never happen */ + WARN_ON_ONCE(1); +@@ -1284,6 +1333,7 @@ static void ff_layout_io_track_ds_error(struct pnfs_layout_segment *lseg, + switch (status) { + case NFS4ERR_DELAY: + case NFS4ERR_GRACE: ++ case NFS4ERR_PERM: + break; + case NFS4ERR_NXIO: + ff_layout_mark_ds_unreachable(lseg, idx); +@@ -1316,7 +1366,8 @@ static int ff_layout_read_done_cb(struct rpc_task *task, + trace_ff_layout_read_error(hdr); + } + +- err = ff_layout_async_handle_error(task, hdr->args.context->state, ++ err = ff_layout_async_handle_error(task, hdr->res.op_status, ++ hdr->args.context->state, + hdr->ds_clp, hdr->lseg, + hdr->pgio_mirror_idx); + +@@ -1483,7 +1534,8 @@ static int ff_layout_write_done_cb(struct rpc_task *task, + trace_ff_layout_write_error(hdr); + } + +- err = ff_layout_async_handle_error(task, hdr->args.context->state, ++ err = ff_layout_async_handle_error(task, hdr->res.op_status, ++ hdr->args.context->state, + hdr->ds_clp, hdr->lseg, + hdr->pgio_mirror_idx); + +@@ -1529,8 +1581,9 @@ static int ff_layout_commit_done_cb(struct rpc_task *task, + trace_ff_layout_commit_error(data); + } + +- err = ff_layout_async_handle_error(task, NULL, data->ds_clp, +- data->lseg, data->ds_commit_index); ++ err = ff_layout_async_handle_error(task, data->res.op_status, ++ NULL, data->ds_clp, data->lseg, ++ data->ds_commit_index); + + trace_nfs4_pnfs_commit_ds(data, err); + switch (err) { +-- +2.39.5 + diff --git a/queue-5.15/platform-x86-dell-wmi-sysman-fix-class-device-unregi.patch b/queue-5.15/platform-x86-dell-wmi-sysman-fix-class-device-unregi.patch new file mode 100644 index 0000000000..3d7d0a96bc --- /dev/null +++ b/queue-5.15/platform-x86-dell-wmi-sysman-fix-class-device-unregi.patch @@ -0,0 +1,52 @@ +From c0db36591cf2117f334863bb6166b503207c9b8f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 7 Jul 2025 15:14:51 -0400 +Subject: platform/x86: dell-wmi-sysman: Fix class device unregistration +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Kurt Borja + +[ Upstream commit 314e5ad4782d08858b3abc325c0487bd2abc23a1 ] + +Devices under the firmware_attributes_class do not have unique a dev_t. +Therefore, device_unregister() should be used instead of +device_destroy(), since the latter may match any device with a given +dev_t. + +Fixes: e8a60aa7404b ("platform/x86: Introduce support for Systems Management Driver over WMI for Dell Systems") +Signed-off-by: Kurt Borja +Link: https://lore.kernel.org/r/20250625-dest-fix-v1-3-3a0f342312bb@gmail.com +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +Signed-off-by: Sasha Levin +--- + drivers/platform/x86/dell/dell-wmi-sysman/sysman.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c b/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c +index ab639dc8a8072..ddde6e41d8f36 100644 +--- a/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c ++++ b/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c +@@ -605,7 +605,7 @@ static int __init sysman_init(void) + release_attributes_data(); + + err_destroy_classdev: +- device_destroy(fw_attr_class, MKDEV(0, 0)); ++ device_unregister(wmi_priv.class_dev); + + err_unregister_class: + fw_attributes_class_put(); +@@ -622,7 +622,7 @@ static int __init sysman_init(void) + static void __exit sysman_exit(void) + { + release_attributes_data(); +- device_destroy(fw_attr_class, MKDEV(0, 0)); ++ device_unregister(wmi_priv.class_dev); + fw_attributes_class_put(); + exit_bios_attr_set_interface(); + exit_bios_attr_pass_interface(); +-- +2.39.5 + diff --git a/queue-5.15/platform-x86-think-lmi-fix-class-device-unregistrati.patch b/queue-5.15/platform-x86-think-lmi-fix-class-device-unregistrati.patch new file mode 100644 index 0000000000..adadb4bd41 --- /dev/null +++ b/queue-5.15/platform-x86-think-lmi-fix-class-device-unregistrati.patch @@ -0,0 +1,52 @@ +From 5136321525127637927ca27a27b9cea1c19e0b1a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 25 Jun 2025 22:17:36 -0300 +Subject: platform/x86: think-lmi: Fix class device unregistration +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Kurt Borja + +[ Upstream commit 5ff1fbb3059730700b4823f43999fc1315984632 ] + +Devices under the firmware_attributes_class do not have unique a dev_t. +Therefore, device_unregister() should be used instead of +device_destroy(), since the latter may match any device with a given +dev_t. + +Fixes: a40cd7ef22fb ("platform/x86: think-lmi: Add WMI interface support on Lenovo platforms") +Signed-off-by: Kurt Borja +Link: https://lore.kernel.org/r/20250625-dest-fix-v1-2-3a0f342312bb@gmail.com +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +Signed-off-by: Sasha Levin +--- + drivers/platform/x86/think-lmi.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/platform/x86/think-lmi.c b/drivers/platform/x86/think-lmi.c +index 154b1b26d03f8..462f7779daaa6 100644 +--- a/drivers/platform/x86/think-lmi.c ++++ b/drivers/platform/x86/think-lmi.c +@@ -892,7 +892,7 @@ static int tlmi_sysfs_init(void) + fail_create_attr: + tlmi_release_attr(); + fail_device_created: +- device_destroy(fw_attr_class, MKDEV(0, 0)); ++ device_unregister(tlmi_priv.class_dev); + fail_class_created: + fw_attributes_class_put(); + return ret; +@@ -1055,7 +1055,7 @@ static int tlmi_analyze(void) + static void tlmi_remove(struct wmi_device *wdev) + { + tlmi_release_attr(); +- device_destroy(fw_attr_class, MKDEV(0, 0)); ++ device_unregister(tlmi_priv.class_dev); + fw_attributes_class_put(); + } + +-- +2.39.5 + diff --git a/queue-5.15/regulator-gpio-add-input_supply-support-in-gpio_regu.patch b/queue-5.15/regulator-gpio-add-input_supply-support-in-gpio_regu.patch new file mode 100644 index 0000000000..ea5f7b9687 --- /dev/null +++ b/queue-5.15/regulator-gpio-add-input_supply-support-in-gpio_regu.patch @@ -0,0 +1,82 @@ +From 29a5fa7b2f96ea122d1802dd2a1fae7460d95bf0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 29 Sep 2022 15:25:25 +0200 +Subject: regulator: gpio: Add input_supply support in gpio_regulator_config + +From: Jerome Neanne + +[ Upstream commit adfdfcbdbd32b356323a3db6d3a683270051a7e6 ] + +This is simillar as fixed-regulator. +Used to extract regulator parent from the device tree. + +Without that property used, the parent regulator can be shut down (if not an always on). +Thus leading to inappropriate behavior: +On am62-SP-SK this fix is required to avoid tps65219 ldo1 (SDMMC rail) to be shut down after boot completion. + +Signed-off-by: Jerome Neanne +Link: https://lore.kernel.org/r/20220929132526.29427-2-jneanne@baylibre.com +Signed-off-by: Mark Brown +Stable-dep-of: c9764fd88bc7 ("regulator: gpio: Fix the out-of-bounds access to drvdata::gpiods") +Signed-off-by: Sasha Levin +--- + drivers/regulator/gpio-regulator.c | 15 +++++++++++++++ + include/linux/regulator/gpio-regulator.h | 2 ++ + 2 files changed, 17 insertions(+) + +diff --git a/drivers/regulator/gpio-regulator.c b/drivers/regulator/gpio-regulator.c +index 5927d4f3eabd7..95e61a2f43f5d 100644 +--- a/drivers/regulator/gpio-regulator.c ++++ b/drivers/regulator/gpio-regulator.c +@@ -220,6 +220,9 @@ of_get_gpio_regulator_config(struct device *dev, struct device_node *np, + regtype); + } + ++ if (of_find_property(np, "vin-supply", NULL)) ++ config->input_supply = "vin"; ++ + return config; + } + +@@ -259,6 +262,18 @@ static int gpio_regulator_probe(struct platform_device *pdev) + + drvdata->gpiods = devm_kzalloc(dev, sizeof(struct gpio_desc *), + GFP_KERNEL); ++ ++ if (config->input_supply) { ++ drvdata->desc.supply_name = devm_kstrdup(&pdev->dev, ++ config->input_supply, ++ GFP_KERNEL); ++ if (!drvdata->desc.supply_name) { ++ dev_err(&pdev->dev, ++ "Failed to allocate input supply\n"); ++ return -ENOMEM; ++ } ++ } ++ + if (!drvdata->gpiods) + return -ENOMEM; + for (i = 0; i < config->ngpios; i++) { +diff --git a/include/linux/regulator/gpio-regulator.h b/include/linux/regulator/gpio-regulator.h +index fdeb312cdabdf..c223e50ff9f78 100644 +--- a/include/linux/regulator/gpio-regulator.h ++++ b/include/linux/regulator/gpio-regulator.h +@@ -42,6 +42,7 @@ struct gpio_regulator_state { + /** + * struct gpio_regulator_config - config structure + * @supply_name: Name of the regulator supply ++ * @input_supply: Name of the input regulator supply + * @enabled_at_boot: Whether regulator has been enabled at + * boot or not. 1 = Yes, 0 = No + * This is used to keep the regulator at +@@ -62,6 +63,7 @@ struct gpio_regulator_state { + */ + struct gpio_regulator_config { + const char *supply_name; ++ const char *input_supply; + + unsigned enabled_at_boot:1; + unsigned startup_delay; +-- +2.39.5 + diff --git a/queue-5.15/regulator-gpio-fix-the-out-of-bounds-access-to-drvda.patch b/queue-5.15/regulator-gpio-fix-the-out-of-bounds-access-to-drvda.patch new file mode 100644 index 0000000000..518c8f37e1 --- /dev/null +++ b/queue-5.15/regulator-gpio-fix-the-out-of-bounds-access-to-drvda.patch @@ -0,0 +1,57 @@ +From bcabbcdc1991b3d14e2827cb2647ae62275bf2d1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 3 Jul 2025 16:05:49 +0530 +Subject: regulator: gpio: Fix the out-of-bounds access to drvdata::gpiods + +From: Manivannan Sadhasivam + +[ Upstream commit c9764fd88bc744592b0604ccb6b6fc1a5f76b4e3 ] + +drvdata::gpiods is supposed to hold an array of 'gpio_desc' pointers. But +the memory is allocated for only one pointer. This will lead to +out-of-bounds access later in the code if 'config::ngpios' is > 1. So +fix the code to allocate enough memory to hold 'config::ngpios' of GPIO +descriptors. + +While at it, also move the check for memory allocation failure to be below +the allocation to make it more readable. + +Cc: stable@vger.kernel.org # 5.0 +Fixes: d6cd33ad7102 ("regulator: gpio: Convert to use descriptors") +Signed-off-by: Manivannan Sadhasivam +Link: https://patch.msgid.link/20250703103549.16558-1-mani@kernel.org +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + drivers/regulator/gpio-regulator.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/drivers/regulator/gpio-regulator.c b/drivers/regulator/gpio-regulator.c +index 95e61a2f43f5d..b34671eb49b52 100644 +--- a/drivers/regulator/gpio-regulator.c ++++ b/drivers/regulator/gpio-regulator.c +@@ -260,8 +260,10 @@ static int gpio_regulator_probe(struct platform_device *pdev) + return -ENOMEM; + } + +- drvdata->gpiods = devm_kzalloc(dev, sizeof(struct gpio_desc *), +- GFP_KERNEL); ++ drvdata->gpiods = devm_kcalloc(dev, config->ngpios, ++ sizeof(struct gpio_desc *), GFP_KERNEL); ++ if (!drvdata->gpiods) ++ return -ENOMEM; + + if (config->input_supply) { + drvdata->desc.supply_name = devm_kstrdup(&pdev->dev, +@@ -274,8 +276,6 @@ static int gpio_regulator_probe(struct platform_device *pdev) + } + } + +- if (!drvdata->gpiods) +- return -ENOMEM; + for (i = 0; i < config->ngpios; i++) { + drvdata->gpiods[i] = devm_gpiod_get_index(dev, + NULL, +-- +2.39.5 + diff --git a/queue-5.15/series b/queue-5.15/series index 8008a6d48f..cf49673e0a 100644 --- a/queue-5.15/series +++ b/queue-5.15/series @@ -134,3 +134,20 @@ wifi-ath6kl-remove-warn-on-bad-firmware-input.patch acpica-refuse-to-evaluate-a-method-if-arguments-are-.patch mtd-spinand-fix-memory-leak-of-ecc-engine-conf.patch rcu-return-early-if-callback-is-not-specified.patch +mmc-core-sd-apply-broken_sd_discard-quirk-earlier.patch +regulator-gpio-add-input_supply-support-in-gpio_regu.patch +regulator-gpio-fix-the-out-of-bounds-access-to-drvda.patch +drm-v3d-disable-interrupts-before-resetting-the-gpu.patch +nfsv4-flexfiles-fix-handling-of-nfs-level-errors-in-.patch +btrfs-remove-noinline-from-btrfs_update_inode.patch +btrfs-remove-redundant-root-argument-from-fixup_inod.patch +btrfs-fix-inode-lookup-error-handling-during-log-rep.patch +btrfs-propagate-last_unlink_trans-earlier-when-doing.patch +btrfs-use-btrfs_record_snapshot_destroy-during-rmdir.patch +ethernet-atl1-add-missing-dma-mapping-error-checks-a.patch +dpaa2-eth-update-dpni_get_single_step_cfg-command.patch +dpaa2-eth-update-single_step-register-access.patch +net-dpaa2-eth-rearrange-variable-in-dpaa2_eth_get_et.patch +dpaa2-eth-fix-xdp_rxq_info-leak.patch +platform-x86-think-lmi-fix-class-device-unregistrati.patch +platform-x86-dell-wmi-sysman-fix-class-device-unregi.patch