From: Sasha Levin Date: Sat, 6 Jun 2026 13:31:36 +0000 (-0400) Subject: Fixes for all trees X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=3bae52dba9d7ae0caf3166726fac6aa30d7b6370;p=thirdparty%2Fkernel%2Fstable-queue.git Fixes for all trees Signed-off-by: Sasha Levin --- diff --git a/queue-5.10/page_pool-fix-use-after-free-in-page_pool_recycle_in.patch b/queue-5.10/page_pool-fix-use-after-free-in-page_pool_recycle_in.patch new file mode 100644 index 0000000000..43cf99eb3e --- /dev/null +++ b/queue-5.10/page_pool-fix-use-after-free-in-page_pool_recycle_in.patch @@ -0,0 +1,131 @@ +From f7d240f727b67cffaa0a1c5becfe5f224f1fdc5c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 4 Jun 2026 20:41:10 +0000 +Subject: page_pool: Fix use-after-free in page_pool_recycle_in_ring +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Dong Chenchen + +[ Upstream commit 271683bb2cf32e5126c592b5d5e6a756fa374fd9 ] + +syzbot reported a uaf in page_pool_recycle_in_ring: + +BUG: KASAN: slab-use-after-free in lock_release+0x151/0xa30 kernel/locking/lockdep.c:5862 +Read of size 8 at addr ffff8880286045a0 by task syz.0.284/6943 + +root cause is: + +page_pool_recycle_in_ring + ptr_ring_produce + spin_lock(&r->producer_lock); + WRITE_ONCE(r->queue[r->producer++], ptr) + //recycle last page to pool + page_pool_release + page_pool_scrub + page_pool_empty_ring + ptr_ring_consume + page_pool_return_page //release all page + __page_pool_destroy + free_percpu(pool->recycle_stats); + free(pool) //free + + spin_unlock(&r->producer_lock); //pool->ring uaf read + recycle_stat_inc(pool, ring); + +page_pool can be free while page pool recycle the last page in ring. +Add producer-lock barrier to page_pool_release to prevent the page +pool from being free before all pages have been recycled. + +Suggested-by: Jakub Kicinski +Link: https://lore.kernel.org/netdev/20250513083123.3514193-1-dongchenchen2@huawei.com +Fixes: ff7d6b27f894 ("page_pool: refurbish version of page_pool code") +Reported-by: syzbot+204a4382fcb3311f3858@syzkaller.appspotmail.com +Closes: https://syzkaller.appspot.com/bug?extid=204a4382fcb3311f3858 +Signed-off-by: Dong Chenchen +Reviewed-by: Toke Høiland-Jørgensen +Reviewed-by: Mina Almasry +Link: https://patch.msgid.link/20250527114152.3119109-1-dongchenchen2@huawei.com +Signed-off-by: Jakub Kicinski +[v5.10: introduced page_pool_producer_lock/unlock helpers inline since + prerequisite commit 368d3cb406cd ("page_pool: fix inconsistency for + page_pool_ring_[un]lock()") depends on page_pool_put_page_bulk which + does not exist in 5.10; used in_serving_softirq() per 5.10 convention; + kept struct page * API (no netmem_ref); dropped recycle_stat_inc change + as page pool stats do not exist in this tree] +Signed-off-by: Bjoern Doebel +Assisted-by: Claude:claude-opus-4-6-v1 +Signed-off-by: Sasha Levin +--- + net/core/page_pool.c | 39 +++++++++++++++++++++++++++++++++------ + 1 file changed, 33 insertions(+), 6 deletions(-) + +diff --git a/net/core/page_pool.c b/net/core/page_pool.c +index 15ad99330bb9b1..09d98fcf669f2c 100644 +--- a/net/core/page_pool.c ++++ b/net/core/page_pool.c +@@ -318,16 +318,39 @@ static void page_pool_return_page(struct page_pool *pool, struct page *page) + */ + } + ++static bool page_pool_producer_lock(struct page_pool *pool) ++ __acquires(&pool->ring.producer_lock) ++{ ++ bool in_softirq = in_serving_softirq(); ++ ++ if (in_softirq) ++ spin_lock(&pool->ring.producer_lock); ++ else ++ spin_lock_bh(&pool->ring.producer_lock); ++ ++ return in_softirq; ++} ++ ++static void page_pool_producer_unlock(struct page_pool *pool, ++ bool in_softirq) ++ __releases(&pool->ring.producer_lock) ++{ ++ if (in_softirq) ++ spin_unlock(&pool->ring.producer_lock); ++ else ++ spin_unlock_bh(&pool->ring.producer_lock); ++} ++ + static bool page_pool_recycle_in_ring(struct page_pool *pool, struct page *page) + { +- int ret; ++ bool in_softirq, ret; ++ + /* BH protection not needed if current is serving softirq */ +- if (in_serving_softirq()) +- ret = ptr_ring_produce(&pool->ring, page); +- else +- ret = ptr_ring_produce_bh(&pool->ring, page); ++ in_softirq = page_pool_producer_lock(pool); ++ ret = !__ptr_ring_produce(&pool->ring, page); ++ page_pool_producer_unlock(pool, in_softirq); + +- return (ret == 0) ? true : false; ++ return ret; + } + + /* Only allow direct recycling in special circumstances, into the +@@ -464,10 +487,14 @@ static void page_pool_scrub(struct page_pool *pool) + + static int page_pool_release(struct page_pool *pool) + { ++ bool in_softirq; + int inflight; + + page_pool_scrub(pool); + inflight = page_pool_inflight(pool); ++ /* Acquire producer lock to make sure producers have exited. */ ++ in_softirq = page_pool_producer_lock(pool); ++ page_pool_producer_unlock(pool, in_softirq); + if (!inflight) + page_pool_free(pool); + +-- +2.53.0 + diff --git a/queue-5.10/serial-dz-fix-bootconsole-handover-lockup.patch b/queue-5.10/serial-dz-fix-bootconsole-handover-lockup.patch new file mode 100644 index 0000000000..d9622b7ba0 --- /dev/null +++ b/queue-5.10/serial-dz-fix-bootconsole-handover-lockup.patch @@ -0,0 +1,114 @@ +From 95b067a4c8d4af9c34a5b260f2f24a1e732fe538 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 5 Jun 2026 04:10:42 +0100 +Subject: serial: dz: Fix bootconsole handover lockup + +From: Maciej W. Rozycki + +commit 7f127b2208e5e2b817243cad41fe4211a6d5a7a3 upstream. + +Calling dz_reset() in the course of setting up the serial device causes +line parameters to be reset and the transmitter disabled. We've been +lucky in that no message is usually produced to the kernel log between +this call and the later call to uart_set_options() in the course of +console setup done by dz_serial_console_init(), or the system would hang +as the console output handler in the firmware tried to access a port the +transmitter of which has been disabled and line parameters messed up. + +This will change with the next change to the driver, so fix dz_reset() +such that line parameters are set for 9600n8 console operation as with +the system firmware and the transmitter re-enabled after reset. This +also means dz_pm() serves no purpose anymore, so drop it. + +Fixes: e6ee512f5a77 ("dz.c: Resource management") +Signed-off-by: Maciej W. Rozycki +Cc: stable@vger.kernel.org # v2.6.25+ +Link: https://patch.msgid.link/alpine.DEB.2.21.2605062302010.46195@angie.orcam.me.uk +Signed-off-by: Greg Kroah-Hartman +[ Avoid C99+ 'for' loop initial declaration for 5.10.y. ] +Signed-off-by: Maciej W. Rozycki +Signed-off-by: Sasha Levin +--- + drivers/tty/serial/dz.c | 37 +++++++++++++------------------------ + 1 file changed, 13 insertions(+), 24 deletions(-) + +diff --git a/drivers/tty/serial/dz.c b/drivers/tty/serial/dz.c +index fdd025c1b5e163..a4c7b5413100a3 100644 +--- a/drivers/tty/serial/dz.c ++++ b/drivers/tty/serial/dz.c +@@ -546,6 +546,7 @@ static void dz_reset(struct dz_port *dport) + struct dz_mux *mux = dport->mux; + unsigned short tcr; + int loops = 10000; ++ int line; + + if (mux->initialised) + return; +@@ -573,6 +574,18 @@ static void dz_reset(struct dz_port *dport) + while (dz_in(dport, DZ_CSR) & DZ_CLR); + iob(); + ++ /* ++ * Set parameters across all lines such as not to interfere ++ * with the initial PROM-based console. Otherwise any output ++ * produced before the console handover would cause the system ++ * firmware to produce rubbish. ++ */ ++ for (line = 0; line < DZ_NB_PORT; line++) ++ dz_out(dport, DZ_LPR, DZ_B9600 | DZ_CS8 | line); ++ ++ /* Re-enable transmission for the initial PROM-based console. */ ++ dz_out(dport, DZ_TCR, tcr); ++ + /* Enable scanning. */ + dz_out(dport, DZ_CSR, DZ_MSE); + +@@ -653,26 +666,6 @@ static void dz_set_termios(struct uart_port *uport, struct ktermios *termios, + spin_unlock_irqrestore(&dport->port.lock, flags); + } + +-/* +- * Hack alert! +- * Required solely so that the initial PROM-based console +- * works undisturbed in parallel with this one. +- */ +-static void dz_pm(struct uart_port *uport, unsigned int state, +- unsigned int oldstate) +-{ +- struct dz_port *dport = to_dport(uport); +- unsigned long flags; +- +- spin_lock_irqsave(&dport->port.lock, flags); +- if (state < 3) +- dz_start_tx(&dport->port); +- else +- dz_stop_tx(&dport->port); +- spin_unlock_irqrestore(&dport->port.lock, flags); +-} +- +- + static const char *dz_type(struct uart_port *uport) + { + return "DZ"; +@@ -768,7 +761,6 @@ static const struct uart_ops dz_ops = { + .startup = dz_startup, + .shutdown = dz_shutdown, + .set_termios = dz_set_termios, +- .pm = dz_pm, + .type = dz_type, + .release_port = dz_release_port, + .request_port = dz_request_port, +@@ -893,10 +885,7 @@ static int __init dz_console_setup(struct console *co, char *options) + if (ret) + return ret; + +- spin_lock_init(&dport->port.lock); /* For dz_pm(). */ +- + dz_reset(dport); +- dz_pm(uport, 0, -1); + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); +-- +2.53.0 + diff --git a/queue-5.10/series b/queue-5.10/series index fba3779f74..118a7b988a 100644 --- a/queue-5.10/series +++ b/queue-5.10/series @@ -117,3 +117,6 @@ bluetooth-hci_core-fix-use-after-free-in-vhci_flush.patch usb-serial-cypress_m8-fix-memory-corruption-with-sma.patch usb-serial-digi_acceleport-fix-memory-corruption-wit.patch xhci-tegra-fix-ghost-usb-device-on-dual-role-port-un.patch +serial-dz-fix-bootconsole-handover-lockup.patch +page_pool-fix-use-after-free-in-page_pool_recycle_in.patch +team-move-team-device-type-change-at-the-end-of-team.patch diff --git a/queue-5.10/team-move-team-device-type-change-at-the-end-of-team.patch b/queue-5.10/team-move-team-device-type-change-at-the-end-of-team.patch new file mode 100644 index 0000000000..5592203de9 --- /dev/null +++ b/queue-5.10/team-move-team-device-type-change-at-the-end-of-team.patch @@ -0,0 +1,121 @@ +From f08f92fbc8e5a65bcedc98f8ad914aae4fa6a53f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 5 Jun 2026 16:47:00 +0300 +Subject: team: Move team device type change at the end of team_port_add + +From: Nikola Z. Ivanov + +commit 0ae9cfc454ea5ead5f3ddbdfe2e70270d8e2c8ef upstream. + +Attempting to add a port device that is already up will expectedly fail, +but not before modifying the team device header_ops. + +In the case of the syzbot reproducer the gre0 device is +already in state UP when it attempts to add it as a +port device of team0, this fails but before that +header_ops->create of team0 is changed from eth_header to ipgre_header +in the call to team_dev_type_check_change. + +Later when we end up in ipgre_header() struct ip_tunnel* points to nonsense +as the private data of the device still holds a struct team. + +Example sequence of iproute2 commands to reproduce the hang/BUG(): +ip link add dev team0 type team +ip link add dev gre0 type gre +ip link set dev gre0 up +ip link set dev gre0 master team0 +ip link set dev team0 up +ping -I team0 1.1.1.1 + +Move team_dev_type_check_change down where all other checks have passed +as it changes the dev type with no way to restore it in case +one of the checks that follow it fail. + +Also make sure to preserve the origial mtu assignment: + - If port_dev is not the same type as dev, dev takes mtu from port_dev + - If port_dev is the same type as dev, port_dev takes mtu from dev + +This is done by adding a conditional before the call to dev_set_mtu +to prevent it from assigning port_dev->mtu = dev->mtu and instead +letting team_dev_type_check_change assign dev->mtu = port_dev->mtu. +The conditional is needed because the patch moves the call to +team_dev_type_check_change past dev_set_mtu. + +Testing: + - team device driver in-tree selftests + - Add/remove various devices as slaves of team device + - syzbot + +Reported-by: syzbot+a2a3b519de727b0f7903@syzkaller.appspotmail.com +Closes: https://syzkaller.appspot.com/bug?extid=a2a3b519de727b0f7903 +Fixes: 1d76efe1577b ("team: add support for non-ethernet devices") +Signed-off-by: Nikola Z. Ivanov +Reviewed-by: Jiri Pirko +Link: https://patch.msgid.link/20251122002027.695151-1-zlatistiv@gmail.com +Signed-off-by: Jakub Kicinski +[ Alexey: backport to 5.10: adjust path from +drivers/net/team/team_core.c to drivers/net/team/team.c ] +Signed-off-by: Alexey Panov +Signed-off-by: Sasha Levin +--- + drivers/net/team/team.c | 23 +++++++++++++++-------- + 1 file changed, 15 insertions(+), 8 deletions(-) + +diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c +index 03cc3da8c3c119..0b62e204c7bb1c 100644 +--- a/drivers/net/team/team.c ++++ b/drivers/net/team/team.c +@@ -1180,10 +1180,6 @@ static int team_port_add(struct team *team, struct net_device *port_dev, + return -EPERM; + } + +- err = team_dev_type_check_change(dev, port_dev); +- if (err) +- return err; +- + if (port_dev->flags & IFF_UP) { + NL_SET_ERR_MSG(extack, "Device is up. Set it down before adding it as a team port"); + netdev_err(dev, "Device %s is up. Set it down before adding it as a team port\n", +@@ -1201,10 +1197,16 @@ static int team_port_add(struct team *team, struct net_device *port_dev, + INIT_LIST_HEAD(&port->qom_list); + + port->orig.mtu = port_dev->mtu; +- err = dev_set_mtu(port_dev, dev->mtu); +- if (err) { +- netdev_dbg(dev, "Error %d calling dev_set_mtu\n", err); +- goto err_set_mtu; ++ /* ++ * MTU assignment will be handled in team_dev_type_check_change ++ * if dev and port_dev are of different types ++ */ ++ if (dev->type == port_dev->type) { ++ err = dev_set_mtu(port_dev, dev->mtu); ++ if (err) { ++ netdev_dbg(dev, "Error %d calling dev_set_mtu\n", err); ++ goto err_set_mtu; ++ } + } + + memcpy(port->orig.dev_addr, port_dev->dev_addr, port_dev->addr_len); +@@ -1279,6 +1281,10 @@ static int team_port_add(struct team *team, struct net_device *port_dev, + } + } + ++ err = team_dev_type_check_change(dev, port_dev); ++ if (err) ++ goto err_set_dev_type; ++ + if (dev->flags & IFF_UP) { + netif_addr_lock_bh(dev); + dev_uc_sync_multiple(port_dev, dev); +@@ -1297,6 +1303,7 @@ static int team_port_add(struct team *team, struct net_device *port_dev, + + return 0; + ++err_set_dev_type: + err_set_slave_promisc: + __team_option_inst_del_port(team, port); + +-- +2.53.0 + diff --git a/queue-5.15/serial-dz-fix-bootconsole-handover-lockup.patch b/queue-5.15/serial-dz-fix-bootconsole-handover-lockup.patch new file mode 100644 index 0000000000..d38154692b --- /dev/null +++ b/queue-5.15/serial-dz-fix-bootconsole-handover-lockup.patch @@ -0,0 +1,114 @@ +From 2ee7e0cfd03da3ced85cc863d45e3c60ac8d5bb6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 5 Jun 2026 04:09:15 +0100 +Subject: serial: dz: Fix bootconsole handover lockup + +From: Maciej W. Rozycki + +commit 7f127b2208e5e2b817243cad41fe4211a6d5a7a3 upstream. + +Calling dz_reset() in the course of setting up the serial device causes +line parameters to be reset and the transmitter disabled. We've been +lucky in that no message is usually produced to the kernel log between +this call and the later call to uart_set_options() in the course of +console setup done by dz_serial_console_init(), or the system would hang +as the console output handler in the firmware tried to access a port the +transmitter of which has been disabled and line parameters messed up. + +This will change with the next change to the driver, so fix dz_reset() +such that line parameters are set for 9600n8 console operation as with +the system firmware and the transmitter re-enabled after reset. This +also means dz_pm() serves no purpose anymore, so drop it. + +Fixes: e6ee512f5a77 ("dz.c: Resource management") +Signed-off-by: Maciej W. Rozycki +Cc: stable@vger.kernel.org # v2.6.25+ +Link: https://patch.msgid.link/alpine.DEB.2.21.2605062302010.46195@angie.orcam.me.uk +Signed-off-by: Greg Kroah-Hartman +[ Avoid C99+ 'for' loop initial declaration for 5.15.y. ] +Signed-off-by: Maciej W. Rozycki +Signed-off-by: Sasha Levin +--- + drivers/tty/serial/dz.c | 37 +++++++++++++------------------------ + 1 file changed, 13 insertions(+), 24 deletions(-) + +diff --git a/drivers/tty/serial/dz.c b/drivers/tty/serial/dz.c +index 6d74ba5a5a431c..05f22e76a50893 100644 +--- a/drivers/tty/serial/dz.c ++++ b/drivers/tty/serial/dz.c +@@ -546,6 +546,7 @@ static void dz_reset(struct dz_port *dport) + struct dz_mux *mux = dport->mux; + unsigned short tcr; + int loops = 10000; ++ int line; + + if (mux->initialised) + return; +@@ -573,6 +574,18 @@ static void dz_reset(struct dz_port *dport) + while (dz_in(dport, DZ_CSR) & DZ_CLR); + iob(); + ++ /* ++ * Set parameters across all lines such as not to interfere ++ * with the initial PROM-based console. Otherwise any output ++ * produced before the console handover would cause the system ++ * firmware to produce rubbish. ++ */ ++ for (line = 0; line < DZ_NB_PORT; line++) ++ dz_out(dport, DZ_LPR, DZ_B9600 | DZ_CS8 | line); ++ ++ /* Re-enable transmission for the initial PROM-based console. */ ++ dz_out(dport, DZ_TCR, tcr); ++ + /* Enable scanning. */ + dz_out(dport, DZ_CSR, DZ_MSE); + +@@ -653,26 +666,6 @@ static void dz_set_termios(struct uart_port *uport, struct ktermios *termios, + spin_unlock_irqrestore(&dport->port.lock, flags); + } + +-/* +- * Hack alert! +- * Required solely so that the initial PROM-based console +- * works undisturbed in parallel with this one. +- */ +-static void dz_pm(struct uart_port *uport, unsigned int state, +- unsigned int oldstate) +-{ +- struct dz_port *dport = to_dport(uport); +- unsigned long flags; +- +- spin_lock_irqsave(&dport->port.lock, flags); +- if (state < 3) +- dz_start_tx(&dport->port); +- else +- dz_stop_tx(&dport->port); +- spin_unlock_irqrestore(&dport->port.lock, flags); +-} +- +- + static const char *dz_type(struct uart_port *uport) + { + return "DZ"; +@@ -768,7 +761,6 @@ static const struct uart_ops dz_ops = { + .startup = dz_startup, + .shutdown = dz_shutdown, + .set_termios = dz_set_termios, +- .pm = dz_pm, + .type = dz_type, + .release_port = dz_release_port, + .request_port = dz_request_port, +@@ -893,10 +885,7 @@ static int __init dz_console_setup(struct console *co, char *options) + if (ret) + return ret; + +- spin_lock_init(&dport->port.lock); /* For dz_pm(). */ +- + dz_reset(dport); +- dz_pm(uport, 0, -1); + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); +-- +2.53.0 + diff --git a/queue-5.15/series b/queue-5.15/series index 5bc0113b3d..21131c60d9 100644 --- a/queue-5.15/series +++ b/queue-5.15/series @@ -139,3 +139,4 @@ hid-pass-the-buffer-size-to-hid_report_raw_event.patch hid-core-fix-size_t-specifier-in-hid_report_raw_even.patch usb-serial-digi_acceleport-fix-memory-corruption-wit.patch xhci-tegra-fix-ghost-usb-device-on-dual-role-port-un.patch +serial-dz-fix-bootconsole-handover-lockup.patch diff --git a/queue-6.1/bpf-bonding-reject-vlan-srcmac-xmit_hash_policy-chan.patch b/queue-6.1/bpf-bonding-reject-vlan-srcmac-xmit_hash_policy-chan.patch new file mode 100644 index 0000000000..a3eca319ae --- /dev/null +++ b/queue-6.1/bpf-bonding-reject-vlan-srcmac-xmit_hash_policy-chan.patch @@ -0,0 +1,121 @@ +From 7dfd9db0a06c94ebba4a089803fb88d620feff5b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 5 Jun 2026 14:07:44 +0800 +Subject: bpf/bonding: reject vlan+srcmac xmit_hash_policy change when XDP is + loaded + +From: Jiayuan Chen + +[ Upstream commit 479d589b40b836442bbdadc3fdb37f001bb67f26 ] + +bond_option_mode_set() already rejects mode changes that would make a +loaded XDP program incompatible via bond_xdp_check(). However, +bond_option_xmit_hash_policy_set() has no such guard. + +For 802.3ad and balance-xor modes, bond_xdp_check() returns false when +xmit_hash_policy is vlan+srcmac, because the 802.1q payload is usually +absent due to hardware offload. This means a user can: + +1. Attach a native XDP program to a bond in 802.3ad/balance-xor mode + with a compatible xmit_hash_policy (e.g. layer2+3). +2. Change xmit_hash_policy to vlan+srcmac while XDP remains loaded. + +This leaves bond->xdp_prog set but bond_xdp_check() now returning false +for the same device. When the bond is later destroyed, dev_xdp_uninstall() +calls bond_xdp_set(dev, NULL, NULL) to remove the program, which hits +the bond_xdp_check() guard and returns -EOPNOTSUPP, triggering: + +WARN_ON(dev_xdp_install(dev, mode, bpf_op, NULL, 0, NULL)) + +Fix this by rejecting xmit_hash_policy changes to vlan+srcmac when an +XDP program is loaded on a bond in 802.3ad or balance-xor mode. + +commit 39a0876d595b ("net, bonding: Disallow vlan+srcmac with XDP") +introduced bond_xdp_check() which returns false for 802.3ad/balance-xor +modes when xmit_hash_policy is vlan+srcmac. The check was wired into +bond_xdp_set() to reject XDP attachment with an incompatible policy, but +the symmetric path -- preventing xmit_hash_policy from being changed to an +incompatible value after XDP is already loaded -- was left unguarded in +bond_option_xmit_hash_policy_set(). + +Note: +commit 094ee6017ea0 ("bonding: check xdp prog when set bond mode") +later added a similar guard to bond_option_mode_set(), but +bond_option_xmit_hash_policy_set() remained unprotected. + +Reported-by: syzbot+5a287bcdc08104bc3132@syzkaller.appspotmail.com +Closes: https://lore.kernel.org/all/6995aff6.050a0220.2eeac1.014e.GAE@google.com/T/ +Fixes: 39a0876d595b ("net, bonding: Disallow vlan+srcmac with XDP") +Signed-off-by: Jiayuan Chen +Link: https://patch.msgid.link/20260226080306.98766-2-jiayuan.chen@linux.dev +Signed-off-by: Paolo Abeni +Signed-off-by: Rajani Kantha <681739313@139.com> +Signed-off-by: Sasha Levin +--- + drivers/net/bonding/bond_main.c | 9 +++++++-- + drivers/net/bonding/bond_options.c | 2 ++ + include/net/bonding.h | 1 + + 3 files changed, 10 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c +index 0e078252b52a98..f2e97f640467de 100644 +--- a/drivers/net/bonding/bond_main.c ++++ b/drivers/net/bonding/bond_main.c +@@ -322,7 +322,7 @@ bool bond_sk_check(struct bonding *bond) + } + } + +-bool bond_xdp_check(struct bonding *bond, int mode) ++bool __bond_xdp_check(int mode, int xmit_policy) + { + switch (mode) { + case BOND_MODE_ROUNDROBIN: +@@ -333,7 +333,7 @@ bool bond_xdp_check(struct bonding *bond, int mode) + /* vlan+srcmac is not supported with XDP as in most cases the 802.1q + * payload is not in the packet due to hardware offload. + */ +- if (bond->params.xmit_policy != BOND_XMIT_POLICY_VLAN_SRCMAC) ++ if (xmit_policy != BOND_XMIT_POLICY_VLAN_SRCMAC) + return true; + fallthrough; + default: +@@ -341,6 +341,11 @@ bool bond_xdp_check(struct bonding *bond, int mode) + } + } + ++bool bond_xdp_check(struct bonding *bond, int mode) ++{ ++ return __bond_xdp_check(mode, bond->params.xmit_policy); ++} ++ + /*---------------------------------- VLAN -----------------------------------*/ + + /* In the following 2 functions, bond_vlan_rx_add_vid and bond_vlan_rx_kill_vid, +diff --git a/drivers/net/bonding/bond_options.c b/drivers/net/bonding/bond_options.c +index 40731d180bb505..3d15b340b5c939 100644 +--- a/drivers/net/bonding/bond_options.c ++++ b/drivers/net/bonding/bond_options.c +@@ -1590,6 +1590,8 @@ static int bond_option_fail_over_mac_set(struct bonding *bond, + static int bond_option_xmit_hash_policy_set(struct bonding *bond, + const struct bond_opt_value *newval) + { ++ if (bond->xdp_prog && !__bond_xdp_check(BOND_MODE(bond), newval->value)) ++ return -EOPNOTSUPP; + netdev_dbg(bond->dev, "Setting xmit hash policy to %s (%llu)\n", + newval->string, newval->value); + bond->params.xmit_policy = newval->value; +diff --git a/include/net/bonding.h b/include/net/bonding.h +index 06a048d716b19f..f2dee8cefa1963 100644 +--- a/include/net/bonding.h ++++ b/include/net/bonding.h +@@ -704,6 +704,7 @@ void bond_debug_register(struct bonding *bond); + void bond_debug_unregister(struct bonding *bond); + void bond_debug_reregister(struct bonding *bond); + const char *bond_mode_name(int mode); ++bool __bond_xdp_check(int mode, int xmit_policy); + bool bond_xdp_check(struct bonding *bond, int mode); + void bond_setup(struct net_device *bond_dev); + unsigned int bond_get_num_tx_queues(void); +-- +2.53.0 + diff --git a/queue-6.1/serial-dz-fix-bootconsole-handover-lockup.patch b/queue-6.1/serial-dz-fix-bootconsole-handover-lockup.patch new file mode 100644 index 0000000000..c725c6e452 --- /dev/null +++ b/queue-6.1/serial-dz-fix-bootconsole-handover-lockup.patch @@ -0,0 +1,104 @@ +From 2da11212366aba0c1bf79f16b66427d269b95a77 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 5 Jun 2026 04:07:17 +0100 +Subject: serial: dz: Fix bootconsole handover lockup + +From: Maciej W. Rozycki + +commit 7f127b2208e5e2b817243cad41fe4211a6d5a7a3 upstream. + +Calling dz_reset() in the course of setting up the serial device causes +line parameters to be reset and the transmitter disabled. We've been +lucky in that no message is usually produced to the kernel log between +this call and the later call to uart_set_options() in the course of +console setup done by dz_serial_console_init(), or the system would hang +as the console output handler in the firmware tried to access a port the +transmitter of which has been disabled and line parameters messed up. + +This will change with the next change to the driver, so fix dz_reset() +such that line parameters are set for 9600n8 console operation as with +the system firmware and the transmitter re-enabled after reset. This +also means dz_pm() serves no purpose anymore, so drop it. + +Fixes: e6ee512f5a77 ("dz.c: Resource management") +Signed-off-by: Maciej W. Rozycki +Cc: stable@vger.kernel.org # v2.6.25+ +Link: https://patch.msgid.link/alpine.DEB.2.21.2605062302010.46195@angie.orcam.me.uk +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Sasha Levin +--- + drivers/tty/serial/dz.c | 36 ++++++++++++------------------------ + 1 file changed, 12 insertions(+), 24 deletions(-) + +diff --git a/drivers/tty/serial/dz.c b/drivers/tty/serial/dz.c +index a16738799418ca..d80bbd95a7f786 100644 +--- a/drivers/tty/serial/dz.c ++++ b/drivers/tty/serial/dz.c +@@ -573,6 +573,18 @@ static void dz_reset(struct dz_port *dport) + while (dz_in(dport, DZ_CSR) & DZ_CLR); + iob(); + ++ /* ++ * Set parameters across all lines such as not to interfere ++ * with the initial PROM-based console. Otherwise any output ++ * produced before the console handover would cause the system ++ * firmware to produce rubbish. ++ */ ++ for (int line = 0; line < DZ_NB_PORT; line++) ++ dz_out(dport, DZ_LPR, DZ_B9600 | DZ_CS8 | line); ++ ++ /* Re-enable transmission for the initial PROM-based console. */ ++ dz_out(dport, DZ_TCR, tcr); ++ + /* Enable scanning. */ + dz_out(dport, DZ_CSR, DZ_MSE); + +@@ -656,26 +668,6 @@ static void dz_set_termios(struct uart_port *uport, struct ktermios *termios, + spin_unlock_irqrestore(&dport->port.lock, flags); + } + +-/* +- * Hack alert! +- * Required solely so that the initial PROM-based console +- * works undisturbed in parallel with this one. +- */ +-static void dz_pm(struct uart_port *uport, unsigned int state, +- unsigned int oldstate) +-{ +- struct dz_port *dport = to_dport(uport); +- unsigned long flags; +- +- spin_lock_irqsave(&dport->port.lock, flags); +- if (state < 3) +- dz_start_tx(&dport->port); +- else +- dz_stop_tx(&dport->port); +- spin_unlock_irqrestore(&dport->port.lock, flags); +-} +- +- + static const char *dz_type(struct uart_port *uport) + { + return "DZ"; +@@ -771,7 +763,6 @@ static const struct uart_ops dz_ops = { + .startup = dz_startup, + .shutdown = dz_shutdown, + .set_termios = dz_set_termios, +- .pm = dz_pm, + .type = dz_type, + .release_port = dz_release_port, + .request_port = dz_request_port, +@@ -896,10 +887,7 @@ static int __init dz_console_setup(struct console *co, char *options) + if (ret) + return ret; + +- spin_lock_init(&dport->port.lock); /* For dz_pm(). */ +- + dz_reset(dport); +- dz_pm(uport, 0, -1); + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); +-- +2.53.0 + diff --git a/queue-6.1/series b/queue-6.1/series index 8a3815650b..ea14ac5190 100644 --- a/queue-6.1/series +++ b/queue-6.1/series @@ -185,3 +185,5 @@ usb-serial-digi_acceleport-fix-memory-corruption-wit.patch xhci-tegra-fix-ghost-usb-device-on-dual-role-port-un.patch netfilter-nf_tables-restore-set-elements-when-delete.patch usb-serial-cypress_m8-fix-memory-corruption-with-sma.patch +serial-dz-fix-bootconsole-handover-lockup.patch +bpf-bonding-reject-vlan-srcmac-xmit_hash_policy-chan.patch diff --git a/queue-6.12/iommu-skip-pasid-validation-for-devices-without-pasi.patch b/queue-6.12/iommu-skip-pasid-validation-for-devices-without-pasi.patch new file mode 100644 index 0000000000..e2ba014d5d --- /dev/null +++ b/queue-6.12/iommu-skip-pasid-validation-for-devices-without-pasi.patch @@ -0,0 +1,105 @@ +From ee1b7c23dd4b3a823f24bd0514852e5255b8fa63 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 4 Jun 2026 16:47:53 +0300 +Subject: iommu: Skip PASID validation for devices without PASID capability + +From: Tushar Dave + +[ Upstream commit b3f6fcd8404f9f92262303369bb877ec5d188a81 ] + +Generally PASID support requires ACS settings that usually create +single device groups, but there are some niche cases where we can get +multi-device groups and still have working PASID support. The primary +issue is that PCI switches are not required to treat PASID tagged TLPs +specially so appropriate ACS settings are required to route all TLPs to +the host bridge if PASID is going to work properly. + +pci_enable_pasid() does check that each device that will use PASID has +the proper ACS settings to achieve this routing. + +However, no-PASID devices can be combined with PASID capable devices +within the same topology using non-uniform ACS settings. In this case +the no-PASID devices may not have strict route to host ACS flags and +end up being grouped with the PASID devices. + +This configuration fails to allow use of the PASID within the iommu +core code which wrongly checks if the no-PASID device supports PASID. + +Fix this by ignoring no-PASID devices during the PASID validation. They +will never issue a PASID TLP anyhow so they can be ignored. + +Fixes: c404f55c26fc ("iommu: Validate the PASID in iommu_attach_device_pasid()") +Cc: stable@vger.kernel.org +Signed-off-by: Tushar Dave +Reviewed-by: Lu Baolu +Reviewed-by: Vasant Hegde +Link: https://lore.kernel.org/r/20250520011937.3230557-1-tdave@nvidia.com +Signed-off-by: Joerg Roedel + +[ Refactored to apply cleanly without support attaching PASID to the blocked domain ] +Signed-off-by: Dmitrii Chervov +Signed-off-by: Sasha Levin +--- + drivers/iommu/iommu.c | 25 ++++++++++++++++++------- + 1 file changed, 18 insertions(+), 7 deletions(-) + +diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c +index 0ad55649e2d007..62e1d637250318 100644 +--- a/drivers/iommu/iommu.c ++++ b/drivers/iommu/iommu.c +@@ -3341,9 +3341,11 @@ static int __iommu_set_group_pasid(struct iommu_domain *domain, + int ret; + + for_each_group_device(group, device) { +- ret = domain->ops->set_dev_pasid(domain, device->dev, pasid); +- if (ret) +- goto err_revert; ++ if (device->dev->iommu->max_pasids > 0) { ++ ret = domain->ops->set_dev_pasid(domain, device->dev, pasid); ++ if (ret) ++ goto err_revert; ++ } + } + + return 0; +@@ -3355,7 +3357,8 @@ static int __iommu_set_group_pasid(struct iommu_domain *domain, + + if (device == last_gdev) + break; +- ops->remove_dev_pasid(device->dev, pasid, domain); ++ if (device->dev->iommu->max_pasids > 0) ++ ops->remove_dev_pasid(device->dev, pasid, domain); + } + return ret; + } +@@ -3368,8 +3371,10 @@ static void __iommu_remove_group_pasid(struct iommu_group *group, + const struct iommu_ops *ops; + + for_each_group_device(group, device) { +- ops = dev_iommu_ops(device->dev); +- ops->remove_dev_pasid(device->dev, pasid, domain); ++ if (device->dev->iommu->max_pasids > 0) { ++ ops = dev_iommu_ops(device->dev); ++ ops->remove_dev_pasid(device->dev, pasid, domain); ++ } + } + } + +@@ -3403,7 +3408,13 @@ int iommu_attach_device_pasid(struct iommu_domain *domain, + + mutex_lock(&group->mutex); + for_each_group_device(group, device) { +- if (pasid >= device->dev->iommu->max_pasids) { ++ /* ++ * Skip PASID validation for devices without PASID support ++ * (max_pasids = 0). These devices cannot issue transactions ++ * with PASID, so they don't affect group's PASID usage. ++ */ ++ if ((device->dev->iommu->max_pasids > 0) && ++ (pasid >= device->dev->iommu->max_pasids)) { + ret = -EINVAL; + goto out_unlock; + } +-- +2.53.0 + diff --git a/queue-6.12/series b/queue-6.12/series index 223bcd70f9..8b5d6dfcdd 100644 --- a/queue-6.12/series +++ b/queue-6.12/series @@ -262,3 +262,7 @@ serial-zs-convert-to-use-a-platform-device.patch usb-serial-cypress_m8-fix-memory-corruption-with-sma.patch usb-serial-digi_acceleport-fix-memory-corruption-wit.patch xhci-tegra-fix-ghost-usb-device-on-dual-role-port-un.patch +iommu-skip-pasid-validation-for-devices-without-pasi.patch +xfrm-hold-dev-ref-until-after-transport_finish-nf_ho.patch +x86-boot-disable-stack-protector-for-early-boot-code.patch +x86-kexec-disable-kcov-instrumentation-after-load_se.patch diff --git a/queue-6.12/x86-boot-disable-stack-protector-for-early-boot-code.patch b/queue-6.12/x86-boot-disable-stack-protector-for-early-boot-code.patch new file mode 100644 index 0000000000..4794b0378c --- /dev/null +++ b/queue-6.12/x86-boot-disable-stack-protector-for-early-boot-code.patch @@ -0,0 +1,45 @@ +From 4c4d3ac497d18b33be30ed8e17938dc5e2f7f7cd Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Jan 2025 14:07:35 -0500 +Subject: x86/boot: Disable stack protector for early boot code + +From: Brian Gerst + +[ Upstream commit a9a76b38aaf577887103e3ebb41d70e6aa5a4b19 ] + +On 64-bit, this will prevent crashes when the canary access is changed +from %gs:40 to %gs:__stack_chk_guard(%rip). RIP-relative addresses from +the identity-mapped early boot code will target the wrong address with +zero-based percpu. KASLR could then shift that address to an unmapped +page causing a crash on boot. + +This early boot code runs well before user-space is active and does not +need stack protector enabled. + +Signed-off-by: Brian Gerst +Signed-off-by: Ingo Molnar +Reviewed-by: Ard Biesheuvel +Cc: Linus Torvalds +Link: https://lore.kernel.org/r/20250123190747.745588-4-brgerst@gmail.com +Stable-dep-of: 917e3ad3321e ("x86/kexec: Disable KCOV instrumentation after load_segments()") +Signed-off-by: Sasha Levin +--- + arch/x86/kernel/Makefile | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile +index f7918980667a33..f42c0903ef86d4 100644 +--- a/arch/x86/kernel/Makefile ++++ b/arch/x86/kernel/Makefile +@@ -44,6 +44,8 @@ KCOV_INSTRUMENT_unwind_orc.o := n + KCOV_INSTRUMENT_unwind_frame.o := n + KCOV_INSTRUMENT_unwind_guess.o := n + ++CFLAGS_head32.o := -fno-stack-protector ++CFLAGS_head64.o := -fno-stack-protector + CFLAGS_irq.o := -I $(src)/../include/asm/trace + + obj-y += head_$(BITS).o +-- +2.53.0 + diff --git a/queue-6.12/x86-kexec-disable-kcov-instrumentation-after-load_se.patch b/queue-6.12/x86-kexec-disable-kcov-instrumentation-after-load_se.patch new file mode 100644 index 0000000000..3d25037948 --- /dev/null +++ b/queue-6.12/x86-kexec-disable-kcov-instrumentation-after-load_se.patch @@ -0,0 +1,91 @@ +From 9c5ef28faa6a652a4adb8983626db2bf6776af82 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 25 Mar 2026 16:48:24 +0100 +Subject: x86/kexec: Disable KCOV instrumentation after load_segments() + +From: Aleksandr Nogikh + +[ Upstream commit 917e3ad3321e75ca0223d5ccf26ceda116aa51e1 ] + +The load_segments() function changes segment registers, invalidating GS base +(which KCOV relies on for per-cpu data). When CONFIG_KCOV is enabled, any +subsequent instrumented C code call (e.g. native_gdt_invalidate()) begins +crashing the kernel in an endless loop. + +To reproduce the problem, it's sufficient to do kexec on a KCOV-instrumented +kernel: + + $ kexec -l /boot/otherKernel + $ kexec -e + +The real-world context for this problem is enabling crash dump collection in +syzkaller. For this, the tool loads a panic kernel before fuzzing and then +calls makedumpfile after the panic. This workflow requires both CONFIG_KEXEC +and CONFIG_KCOV to be enabled simultaneously. + +Adding safeguards directly to the KCOV fast-path (__sanitizer_cov_trace_pc()) +is also undesirable as it would introduce an extra performance overhead. + +Disabling instrumentation for the individual functions would be too fragile, +so disable KCOV instrumentation for the entire machine_kexec_64.c and +physaddr.c. If coverage-guided fuzzing ever needs these components in the +future, other approaches should be considered. + +The problem is not relevant for 32 bit kernels as CONFIG_KCOV is not supported +there. + + [ bp: Space out comment for better readability. ] + +Fixes: 0d345996e4cb ("x86/kernel: increase kcov coverage under arch/x86/kernel folder") +Signed-off-by: Aleksandr Nogikh +Signed-off-by: Borislav Petkov (AMD) +Reviewed-by: Dmitry Vyukov +Cc: stable@vger.kernel.org +Link: https://patch.msgid.link/20260325154825.551191-1-nogikh@google.com +Signed-off-by: Sasha Levin +--- + arch/x86/kernel/Makefile | 14 ++++++++++++++ + arch/x86/mm/Makefile | 2 ++ + 2 files changed, 16 insertions(+) + +diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile +index f42c0903ef86d4..5f58b5a38cac17 100644 +--- a/arch/x86/kernel/Makefile ++++ b/arch/x86/kernel/Makefile +@@ -44,6 +44,20 @@ KCOV_INSTRUMENT_unwind_orc.o := n + KCOV_INSTRUMENT_unwind_frame.o := n + KCOV_INSTRUMENT_unwind_guess.o := n + ++# Disable KCOV to prevent crashes during kexec: load_segments() invalidates ++# the GS base, which KCOV relies on for per-CPU data. ++# ++# As KCOV and KEXEC compatibility should be preserved (e.g. syzkaller is ++# using it to collect crash dumps during kernel fuzzing), disabling ++# KCOV for KEXEC kernels is not an option. Selectively disabling KCOV ++# instrumentation for individual affected functions can be fragile, while ++# adding more checks to KCOV would slow it down. ++# ++# As a compromise solution, disable KCOV instrumentation for the whole ++# source code file. If its coverage is ever needed, other approaches ++# should be considered. ++KCOV_INSTRUMENT_machine_kexec_64.o := n ++ + CFLAGS_head32.o := -fno-stack-protector + CFLAGS_head64.o := -fno-stack-protector + CFLAGS_irq.o := -I $(src)/../include/asm/trace +diff --git a/arch/x86/mm/Makefile b/arch/x86/mm/Makefile +index 690fbf48e8538b..60a53baa042793 100644 +--- a/arch/x86/mm/Makefile ++++ b/arch/x86/mm/Makefile +@@ -5,6 +5,8 @@ KCOV_INSTRUMENT_mem_encrypt.o := n + KCOV_INSTRUMENT_mem_encrypt_amd.o := n + KCOV_INSTRUMENT_mem_encrypt_identity.o := n + KCOV_INSTRUMENT_pgprot.o := n ++# See the "Disable KCOV" comment in arch/x86/kernel/Makefile. ++KCOV_INSTRUMENT_physaddr.o := n + + KASAN_SANITIZE_mem_encrypt.o := n + KASAN_SANITIZE_mem_encrypt_amd.o := n +-- +2.53.0 + diff --git a/queue-6.12/xfrm-hold-dev-ref-until-after-transport_finish-nf_ho.patch b/queue-6.12/xfrm-hold-dev-ref-until-after-transport_finish-nf_ho.patch new file mode 100644 index 0000000000..c09c387dd2 --- /dev/null +++ b/queue-6.12/xfrm-hold-dev-ref-until-after-transport_finish-nf_ho.patch @@ -0,0 +1,141 @@ +From c23bf6bc628df4f85442f73c1e190b74dae30031 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 5 Jun 2026 14:12:54 +0000 +Subject: xfrm: hold dev ref until after transport_finish NF_HOOK + +From: Qi Tang + +[ Upstream commit 1c428b03840094410c5fb6a5db30640486bbbfcb ] + +After async crypto completes, xfrm_input_resume() calls dev_put() +immediately on re-entry before the skb reaches transport_finish. +The skb->dev pointer is then used inside NF_HOOK and its okfn, +which can race with device teardown. + +Remove the dev_put from the async resumption entry and instead +drop the reference after the NF_HOOK call in transport_finish, +using a saved device pointer since NF_HOOK may consume the skb. +This covers NF_DROP, NF_QUEUE and NF_STOLEN paths that skip +the okfn. + +For non-transport exits (decaps, gro, drop) and secondary +async return points, release the reference inline when +async is set. + +Suggested-by: Florian Westphal +Fixes: acf568ee859f ("xfrm: Reinject transport-mode packets through tasklet") +Cc: stable@vger.kernel.org +Signed-off-by: Qi Tang +Signed-off-by: Steffen Klassert +[ net/xfrm/xfrm_input.c: dev_hold/dev_put are unconditional here rather +than inside !crypto_done as in mainline, and the dev_put in the +encap_type == -1 async-resumption block does not exist; adapted by +gating dev_put at resume: with if (!async), adding if (async) dev_put +at -EINPROGRESS return, gro_cells_receive paths, and drop label. ] +Signed-off-by: Simon Liebold +Signed-off-by: Sasha Levin +--- + net/ipv4/xfrm4_input.c | 5 ++++- + net/ipv6/xfrm6_input.c | 5 ++++- + net/xfrm/xfrm_input.c | 14 ++++++++++++-- + 3 files changed, 20 insertions(+), 4 deletions(-) + +diff --git a/net/ipv4/xfrm4_input.c b/net/ipv4/xfrm4_input.c +index 12a1a0f421956c..adf21d6b6076c1 100644 +--- a/net/ipv4/xfrm4_input.c ++++ b/net/ipv4/xfrm4_input.c +@@ -50,6 +50,7 @@ int xfrm4_transport_finish(struct sk_buff *skb, int async) + { + struct xfrm_offload *xo = xfrm_offload(skb); + struct iphdr *iph = ip_hdr(skb); ++ struct net_device *dev = skb->dev; + + iph->protocol = XFRM_MODE_SKB_CB(skb)->protocol; + +@@ -73,8 +74,10 @@ int xfrm4_transport_finish(struct sk_buff *skb, int async) + } + + NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, +- dev_net(skb->dev), NULL, skb, skb->dev, NULL, ++ dev_net(dev), NULL, skb, dev, NULL, + xfrm4_rcv_encap_finish); ++ if (async) ++ dev_put(dev); + return 0; + } + +diff --git a/net/ipv6/xfrm6_input.c b/net/ipv6/xfrm6_input.c +index 9005fc156a20e6..699a001ac16629 100644 +--- a/net/ipv6/xfrm6_input.c ++++ b/net/ipv6/xfrm6_input.c +@@ -43,6 +43,7 @@ static int xfrm6_transport_finish2(struct net *net, struct sock *sk, + int xfrm6_transport_finish(struct sk_buff *skb, int async) + { + struct xfrm_offload *xo = xfrm_offload(skb); ++ struct net_device *dev = skb->dev; + int nhlen = -skb_network_offset(skb); + + skb_network_header(skb)[IP6CB(skb)->nhoff] = +@@ -68,8 +69,10 @@ int xfrm6_transport_finish(struct sk_buff *skb, int async) + } + + NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING, +- dev_net(skb->dev), NULL, skb, skb->dev, NULL, ++ dev_net(dev), NULL, skb, dev, NULL, + xfrm6_transport_finish2); ++ if (async) ++ dev_put(dev); + return 0; + } + +diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c +index 8edcb32735e595..a0a04b87a0c2ab 100644 +--- a/net/xfrm/xfrm_input.c ++++ b/net/xfrm/xfrm_input.c +@@ -645,10 +645,14 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) + else + nexthdr = x->type->input(x, skb); + +- if (nexthdr == -EINPROGRESS) ++ if (nexthdr == -EINPROGRESS) { ++ if (async) ++ dev_put(skb->dev); + return 0; ++ } + resume: +- dev_put(skb->dev); ++ if (!async) ++ dev_put(skb->dev); + + spin_lock(&x->lock); + if (nexthdr < 0) { +@@ -716,6 +720,8 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) + sp->olen = 0; + if (skb_valid_dst(skb)) + skb_dst_drop(skb); ++ if (async) ++ dev_put(skb->dev); + gro_cells_receive(&gro_cells, skb); + return 0; + } else { +@@ -735,6 +741,8 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) + sp->olen = 0; + if (skb_valid_dst(skb)) + skb_dst_drop(skb); ++ if (async) ++ dev_put(skb->dev); + gro_cells_receive(&gro_cells, skb); + return err; + } +@@ -745,6 +753,8 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) + drop_unlock: + spin_unlock(&x->lock); + drop: ++ if (async) ++ dev_put(skb->dev); + xfrm_rcv_cb(skb, family, x && x->type ? x->type->proto : nexthdr, -1); + kfree_skb(skb); + return 0; +-- +2.53.0 + diff --git a/queue-6.6/serial-dz-convert-to-use-a-platform-device.patch b/queue-6.6/serial-dz-convert-to-use-a-platform-device.patch new file mode 100644 index 0000000000..95bf035c80 --- /dev/null +++ b/queue-6.6/serial-dz-convert-to-use-a-platform-device.patch @@ -0,0 +1,416 @@ +From a3b4c1c90c4b845072e7378f644db16db3df589f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 5 Jun 2026 04:04:20 +0100 +Subject: serial: dz: Convert to use a platform device + +From: Maciej W. Rozycki + +commit 5d7a49d60b8fda66da60e240fd7315232fa1754f upstream. + +Prevent a crash from happening as the first serial port is initialised: + + Console: switching to colour frame buffer device 160x64 + tgafb: SFB+ detected, rev=0x02 + fb0: Digital ZLX-E1 frame buffer device at 0x1e000000 + DECstation DZ serial driver version 1.04 + CPU 0 Unable to handle kernel paging request at virtual address 000000bc, epc == 8048b3a4, ra == 80470a78 + Oops[#1]: + CPU: 0 UID: 0 PID: 1 Comm: swapper/0 Not tainted 6.19.0-dirty #35 NONE + $ 0 : 00000000 1000ac00 00000004 804707ac + $ 4 : 00000000 80e20850 80e20858 81000030 + $ 8 : 00000000 8072c81c 00000008 fefefeff + $12 : 6c616972 00000006 80c5917f 69726420 + $16 : 80e20800 00000000 808f8968 80e20800 + $20 : 00000000 807f5a90 808b0094 808d3bc8 + $24 : 00000018 80479030 + $28 : 80c2e000 80c2fd70 00000069 80470a78 + Hi : 00000004 + Lo : 00000000 + epc : 8048b3a4 __dev_fwnode+0x0/0xc + ra : 80470a78 serial_base_ctrl_add+0xa0/0x168 + Status: 1000ac04 IEp + Cause : 30000008 (ExcCode 02) + BadVA : 000000bc + PrId : 00000220 (R3000) + Modules linked in: + Process swapper/0 (pid: 1, threadinfo=(ptrval), task=(ptrval), tls=00000000) + Stack : 00400044 00400040 8046f4cc 00000000 808a6148 808a0000 808f8968 8086983c + 808e0000 8046fc84 1000ac01 00000028 80e20700 802ba3f8 80e20700 80d34a94 + 80c1b900 80e20700 80e20700 80e20700 80e20700 80444650 00000000 00000000 + 00000000 807f5a90 808b0094 80447080 00400040 808e0000 80d34a94 808a6148 + 80d34a94 00000004 80e20700 00000000 8076974c 80469810 80c2fe3c 1000ac01 + ... + Call Trace: + [<8048b3a4>] __dev_fwnode+0x0/0xc + [<80470a78>] serial_base_ctrl_add+0xa0/0x168 + [<8046fc84>] serial_core_register_port+0x1c8/0x974 + [<808c6af0>] dz_init+0x74/0xc8 + [<800470e0>] do_one_initcall+0x44/0x2d4 + [<808b111c>] kernel_init_freeable+0x258/0x308 + [<8072e434>] kernel_init+0x20/0x114 + [<80049cd0>] ret_from_kernel_thread+0x14/0x1c + + Code: 27bd0018 03e00008 2402ffea <8c8200bc> 03e00008 00000000 27bdffc0 afbe0038 afb30024 + + ---[ end trace 0000000000000000 ]--- + +-- where a pointer is dereferenced that has been derived from a null +pointer to the port's parent device. + +Since no device is available with legacy probing and it's not anymore a +preferable way to discover devices anyway, switch the driver to using a +platform device and use it as the port's parent device. Update resource +handling accordingly and only request the actual span of addresses used +within the slot, which will have had its resource already requested by +generic platform device code. + +Use platform_driver_probe() not just because the DZ device is fixed with +solder on board and not straightforward to remove, but foremost because +the associated TTY's major device number is the same as used by the zs +driver and the first driver to claim it will prevent the other one from +using it. Either one DZ device or some SCC devices will be present in a +given system but never both at a time, and therefore we want the major +device number to be claimed by the first driver to actually successfully +bind to its device and platform_driver_probe() is a way to fulfil that. + +An unfortunate consequence of the switch to a platform device is we now +hand the console over from the bootconsole much later in the bootstrap. +The firmware console handler appears good enough though to work so late +and in particular with interrupts enabled. + +Conversely only starting the console port so late lets the reset code +fully utilise our delay handlers, so switch from udelay() to fsleep() +for transmitter draining so as to avoid busy-waiting for an excessive +amount of time. + +Fixes: 84a9582fd203 ("serial: core: Start managing serial controllers to enable runtime PM") +Signed-off-by: Maciej W. Rozycki +Cc: stable@vger.kernel.org # needs to use .remove_new for <= 6.10 +Link: https://patch.msgid.link/alpine.DEB.2.21.2605062326540.46195@angie.orcam.me.uk +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Sasha Levin +--- + arch/mips/dec/platform.c | 55 ++++++++++++++++++- + drivers/tty/serial/dz.c | 116 +++++++++++++++++++-------------------- + 2 files changed, 110 insertions(+), 61 deletions(-) + +diff --git a/arch/mips/dec/platform.c b/arch/mips/dec/platform.c +index c4fcb8c58e01ce..fdecc91ee22abb 100644 +--- a/arch/mips/dec/platform.c ++++ b/arch/mips/dec/platform.c +@@ -10,6 +10,13 @@ + #include + #include + ++#include ++ ++#include ++#include ++#include ++#include ++ + static struct resource dec_rtc_resources[] = { + { + .name = "rtc", +@@ -30,11 +37,57 @@ static struct platform_device dec_rtc_device = { + .num_resources = ARRAY_SIZE(dec_rtc_resources), + }; + ++static struct resource dec_dz_resources[] = { ++ { .name = "dz", .flags = IORESOURCE_MEM, }, ++ { .name = "dz", .flags = IORESOURCE_IRQ, }, ++}; ++ ++static struct platform_device dec_dz_device = { ++ .name = "dz", ++ .id = PLATFORM_DEVID_NONE, ++ .resource = dec_dz_resources, ++ .num_resources = ARRAY_SIZE(dec_dz_resources), ++}; ++ ++static struct platform_device *dec_dz_devices[] __initdata = { ++ &dec_dz_device, ++}; ++ + static int __init dec_add_devices(void) + { ++ int ret1, ret2; ++ int num_dz; ++ int irq, i; ++ + dec_rtc_resources[0].start = RTC_PORT(0); + dec_rtc_resources[0].end = RTC_PORT(0) + dec_kn_slot_size - 1; +- return platform_device_register(&dec_rtc_device); ++ ++ i = 0; ++ irq = dec_interrupt[DEC_IRQ_DZ11]; ++ if (IS_ENABLED(CONFIG_32BIT) && irq >= 0) { ++ resource_size_t base; ++ ++ switch (mips_machtype) { ++ case MACH_DS23100: ++ case MACH_DS5100: ++ base = dec_kn_slot_base + KN01_DZ11; ++ break; ++ default: ++ base = dec_kn_slot_base + KN02_DZ11; ++ break; ++ } ++ dec_dz_device.resource[0].start = base; ++ dec_dz_device.resource[0].end = base + dec_kn_slot_size - 1; ++ dec_dz_device.resource[1].start = irq; ++ dec_dz_device.resource[1].end = irq; ++ i++; ++ } ++ num_dz = i; ++ ++ ret1 = platform_device_register(&dec_rtc_device); ++ ret2 = IS_ENABLED(CONFIG_32BIT) ? ++ platform_add_devices(dec_dz_devices, num_dz) : 0; ++ return ret1 ? ret1 : ret2; + } + + device_initcall(dec_add_devices); +diff --git a/drivers/tty/serial/dz.c b/drivers/tty/serial/dz.c +index 90231ec00ff070..08f2414bf1d92e 100644 +--- a/drivers/tty/serial/dz.c ++++ b/drivers/tty/serial/dz.c +@@ -40,6 +40,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -48,14 +49,6 @@ + + #include + #include +-#include +- +-#include +-#include +-#include +-#include +-#include +-#include + + #include "dz.h" + +@@ -65,7 +58,9 @@ MODULE_LICENSE("GPL"); + + + static char dz_name[] __initdata = "DECstation DZ serial driver version "; +-static char dz_version[] __initdata = "1.04"; ++static char dz_version[] __initdata = "1.05"; ++ ++#define DZ_IO_SIZE 0x20 /* IOMEM space size. */ + + struct dz_port { + struct dz_mux *mux; +@@ -81,6 +76,7 @@ struct dz_mux { + }; + + static struct dz_mux dz_mux; ++static struct uart_driver dz_reg; + + static inline struct dz_port *to_dport(struct uart_port *uport) + { +@@ -565,7 +561,7 @@ static void dz_reset(struct dz_port *dport) + iob(); + udelay(2); /* 1.4us TRDY recovery. */ + } +- udelay(1200); /* Transmitter drain. */ ++ fsleep(1200); /* Transmitter drain. */ + } + + dz_out(dport, DZ_CSR, DZ_CLR); +@@ -682,14 +678,13 @@ static void dz_release_port(struct uart_port *uport) + + map_guard = atomic_add_return(-1, &mux->map_guard); + if (!map_guard) +- release_mem_region(uport->mapbase, dec_kn_slot_size); ++ release_mem_region(uport->mapbase, DZ_IO_SIZE); + } + + static int dz_map_port(struct uart_port *uport) + { + if (!uport->membase) +- uport->membase = ioremap(uport->mapbase, +- dec_kn_slot_size); ++ uport->membase = ioremap(uport->mapbase, DZ_IO_SIZE); + if (!uport->membase) { + printk(KERN_ERR "dz: Cannot map MMIO\n"); + return -ENOMEM; +@@ -705,8 +700,7 @@ static int dz_request_port(struct uart_port *uport) + + map_guard = atomic_add_return(1, &mux->map_guard); + if (map_guard == 1) { +- if (!request_mem_region(uport->mapbase, dec_kn_slot_size, +- "dz")) { ++ if (!request_mem_region(uport->mapbase, DZ_IO_SIZE, "dz")) { + atomic_add(-1, &mux->map_guard); + printk(KERN_ERR + "dz: Unable to reserve MMIO resource\n"); +@@ -717,7 +711,7 @@ static int dz_request_port(struct uart_port *uport) + if (ret) { + map_guard = atomic_add_return(-1, &mux->map_guard); + if (!map_guard) +- release_mem_region(uport->mapbase, dec_kn_slot_size); ++ release_mem_region(uport->mapbase, DZ_IO_SIZE); + return ret; + } + return 0; +@@ -769,20 +763,15 @@ static const struct uart_ops dz_ops = { + .verify_port = dz_verify_port, + }; + +-static void __init dz_init_ports(void) ++static int __init dz_probe(struct platform_device *pdev) + { +- static int first = 1; +- unsigned long base; ++ struct resource *mem_resource, *irq_resource; + int line; + +- if (!first) +- return; +- first = 0; +- +- if (mips_machtype == MACH_DS23100 || mips_machtype == MACH_DS5100) +- base = dec_kn_slot_base + KN01_DZ11; +- else +- base = dec_kn_slot_base + KN02_DZ11; ++ mem_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ irq_resource = platform_get_resource(pdev, IORESOURCE_IRQ, 0); ++ if (!mem_resource || !irq_resource) ++ return -ENODEV; + + for (line = 0; line < DZ_NB_PORT; line++) { + struct dz_port *dport = &dz_mux.dport[line]; +@@ -790,14 +779,33 @@ static void __init dz_init_ports(void) + + dport->mux = &dz_mux; + +- uport->irq = dec_interrupt[DEC_IRQ_DZ11]; ++ uport->dev = &pdev->dev; ++ uport->irq = irq_resource->start; + uport->fifosize = 1; + uport->iotype = UPIO_MEM; + uport->flags = UPF_BOOT_AUTOCONF; + uport->ops = &dz_ops; + uport->line = line; +- uport->mapbase = base; ++ uport->mapbase = mem_resource->start; + uport->has_sysrq = IS_ENABLED(CONFIG_SERIAL_DZ_CONSOLE); ++ ++ if (uart_add_one_port(&dz_reg, uport)) ++ uport->dev = NULL; ++ } ++ ++ return 0; ++} ++ ++static void __exit dz_remove(struct platform_device *pdev) ++{ ++ int line; ++ ++ for (line = DZ_NB_PORT - 1; line >= 0; line--) { ++ struct dz_port *dport = &dz_mux.dport[line]; ++ struct uart_port *uport = &dport->port; ++ ++ if (uport->dev) ++ uart_remove_one_port(&dz_reg, uport); + } + } + +@@ -880,21 +888,14 @@ static int __init dz_console_setup(struct console *co, char *options) + int bits = 8; + int parity = 'n'; + int flow = 'n'; +- int ret; +- +- ret = dz_map_port(uport); +- if (ret) +- return ret; +- +- dz_reset(dport); + ++ if (!dport->mux) ++ return -ENODEV; + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); +- +- return uart_set_options(&dport->port, co, baud, parity, bits, flow); ++ return uart_set_options(uport, co, baud, parity, bits, flow); + } + +-static struct uart_driver dz_reg; + static struct console dz_console = { + .name = "ttyS", + .write = dz_console_print, +@@ -905,18 +906,6 @@ static struct console dz_console = { + .data = &dz_reg, + }; + +-static int __init dz_serial_console_init(void) +-{ +- if (!IOASIC) { +- dz_init_ports(); +- register_console(&dz_console); +- return 0; +- } else +- return -ENXIO; +-} +- +-console_initcall(dz_serial_console_init); +- + #define SERIAL_DZ_CONSOLE &dz_console + #else + #define SERIAL_DZ_CONSOLE NULL +@@ -932,25 +921,32 @@ static struct uart_driver dz_reg = { + .cons = SERIAL_DZ_CONSOLE, + }; + ++static struct platform_driver dz_driver = { ++ .remove_new = __exit_p(dz_remove), ++ .driver = { .name = "dz" }, ++}; ++ + static int __init dz_init(void) + { +- int ret, i; +- +- if (IOASIC) +- return -ENXIO; ++ int ret; + + printk("%s%s\n", dz_name, dz_version); + +- dz_init_ports(); +- + ret = uart_register_driver(&dz_reg); + if (ret) + return ret; ++ ret = platform_driver_probe(&dz_driver, dz_probe); ++ if (ret) ++ uart_unregister_driver(&dz_reg); + +- for (i = 0; i < DZ_NB_PORT; i++) +- uart_add_one_port(&dz_reg, &dz_mux.dport[i].port); ++ return ret; ++} + +- return 0; ++static void __exit dz_exit(void) ++{ ++ platform_driver_unregister(&dz_driver); ++ uart_unregister_driver(&dz_reg); + } + + module_init(dz_init); ++module_exit(dz_exit); +-- +2.53.0 + diff --git a/queue-6.6/serial-dz-fix-bootconsole-handover-lockup.patch b/queue-6.6/serial-dz-fix-bootconsole-handover-lockup.patch new file mode 100644 index 0000000000..528794c93d --- /dev/null +++ b/queue-6.6/serial-dz-fix-bootconsole-handover-lockup.patch @@ -0,0 +1,104 @@ +From 102c7bfd9a6514c2bd42cc4b7c8bcd740a6d3ead Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 5 Jun 2026 04:02:50 +0100 +Subject: serial: dz: Fix bootconsole handover lockup + +From: Maciej W. Rozycki + +commit 7f127b2208e5e2b817243cad41fe4211a6d5a7a3 upstream. + +Calling dz_reset() in the course of setting up the serial device causes +line parameters to be reset and the transmitter disabled. We've been +lucky in that no message is usually produced to the kernel log between +this call and the later call to uart_set_options() in the course of +console setup done by dz_serial_console_init(), or the system would hang +as the console output handler in the firmware tried to access a port the +transmitter of which has been disabled and line parameters messed up. + +This will change with the next change to the driver, so fix dz_reset() +such that line parameters are set for 9600n8 console operation as with +the system firmware and the transmitter re-enabled after reset. This +also means dz_pm() serves no purpose anymore, so drop it. + +Fixes: e6ee512f5a77 ("dz.c: Resource management") +Signed-off-by: Maciej W. Rozycki +Cc: stable@vger.kernel.org # v2.6.25+ +Link: https://patch.msgid.link/alpine.DEB.2.21.2605062302010.46195@angie.orcam.me.uk +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Sasha Levin +--- + drivers/tty/serial/dz.c | 36 ++++++++++++------------------------ + 1 file changed, 12 insertions(+), 24 deletions(-) + +diff --git a/drivers/tty/serial/dz.c b/drivers/tty/serial/dz.c +index 3cfb68165334e2..90231ec00ff070 100644 +--- a/drivers/tty/serial/dz.c ++++ b/drivers/tty/serial/dz.c +@@ -572,6 +572,18 @@ static void dz_reset(struct dz_port *dport) + while (dz_in(dport, DZ_CSR) & DZ_CLR); + iob(); + ++ /* ++ * Set parameters across all lines such as not to interfere ++ * with the initial PROM-based console. Otherwise any output ++ * produced before the console handover would cause the system ++ * firmware to produce rubbish. ++ */ ++ for (int line = 0; line < DZ_NB_PORT; line++) ++ dz_out(dport, DZ_LPR, DZ_B9600 | DZ_CS8 | line); ++ ++ /* Re-enable transmission for the initial PROM-based console. */ ++ dz_out(dport, DZ_TCR, tcr); ++ + /* Enable scanning. */ + dz_out(dport, DZ_CSR, DZ_MSE); + +@@ -655,26 +667,6 @@ static void dz_set_termios(struct uart_port *uport, struct ktermios *termios, + spin_unlock_irqrestore(&dport->port.lock, flags); + } + +-/* +- * Hack alert! +- * Required solely so that the initial PROM-based console +- * works undisturbed in parallel with this one. +- */ +-static void dz_pm(struct uart_port *uport, unsigned int state, +- unsigned int oldstate) +-{ +- struct dz_port *dport = to_dport(uport); +- unsigned long flags; +- +- spin_lock_irqsave(&dport->port.lock, flags); +- if (state < 3) +- dz_start_tx(&dport->port); +- else +- dz_stop_tx(&dport->port); +- spin_unlock_irqrestore(&dport->port.lock, flags); +-} +- +- + static const char *dz_type(struct uart_port *uport) + { + return "DZ"; +@@ -770,7 +762,6 @@ static const struct uart_ops dz_ops = { + .startup = dz_startup, + .shutdown = dz_shutdown, + .set_termios = dz_set_termios, +- .pm = dz_pm, + .type = dz_type, + .release_port = dz_release_port, + .request_port = dz_request_port, +@@ -895,10 +886,7 @@ static int __init dz_console_setup(struct console *co, char *options) + if (ret) + return ret; + +- spin_lock_init(&dport->port.lock); /* For dz_pm(). */ +- + dz_reset(dport); +- dz_pm(uport, 0, -1); + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); +-- +2.53.0 + diff --git a/queue-6.6/serial-zs-convert-to-use-a-platform-device.patch b/queue-6.6/serial-zs-convert-to-use-a-platform-device.patch new file mode 100644 index 0000000000..3880b7f287 --- /dev/null +++ b/queue-6.6/serial-zs-convert-to-use-a-platform-device.patch @@ -0,0 +1,488 @@ +From 4cb0b06a0f5ebe3721a8898c3e2412b918815b6d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 5 Jun 2026 04:05:05 +0100 +Subject: serial: zs: Convert to use a platform device + +From: Maciej W. Rozycki + +commit 7cac59d08a73cb866ec51a483a6f3fe0f531947c upstream. + +Prevent a crash from happening as the first serial port is initialised: + + Console: switching to mono frame buffer device 160x64 + fb0: PMAG-AA frame buffer device at tc0 + DECstation Z85C30 serial driver version 0.10 + CPU 0 Unable to handle kernel paging request at virtual address 0000002c, epc == 803ab00c, ra == 803aafe0 + Oops[#1]: + CPU: 0 PID: 1 Comm: swapper Not tainted 6.4.0-rc3-00031-g84a9582fd203-dirty #57 + $ 0 : 00000000 10012c00 803aaeb0 00000000 + $ 4 : 80e12f60 80e12f50 80e12f58 81000030 + $ 8 : 00000000 805ff37c 00000000 33433538 + $12 : 65732030 00000006 80c2915d 6c616972 + $16 : 80e12f00 807b7630 00000000 00000000 + $20 : 00000004 00000348 000001a0 807623b8 + $24 : 00000018 00000000 + $28 : 80c24000 80c25d60 8078b148 803aafe0 + Hi : 00000000 + Lo : 00000000 + epc : 803ab00c serial_base_ctrl_add+0x78/0xf4 + ra : 803aafe0 serial_base_ctrl_add+0x4c/0xf4 + Status: 10012c03 KERNEL EXL IE + Cause : 00000008 (ExcCode 02) + BadVA : 0000002c + PrId : 00000440 (R4400SC) + Modules linked in: + Process swapper (pid: 1, threadinfo=(ptrval), task=(ptrval), tls=00000000) + Stack : 80760000 00000cc0 00400044 00400040 803aa02c 80d61ab8 00000000 807b7630 + 80760000 807623b8 807b7628 803aa644 80386998 00000000 80e17780 80220f68 + 80e17780 80d61ab8 80c17d80 80e17780 80e17780 8063c798 80e17780 80383fa0 + 00000010 80e17780 00000000 80386998 807a0000 00000000 00400040 8038f848 + 807623b8 80d61ab8 00000004 80e17780 00000000 803a68e4 80c25e2c 803bb884 + ... + Call Trace: + [<803ab00c>] serial_base_ctrl_add+0x78/0xf4 + [<803aa644>] serial_core_register_port+0x174/0x69c + [<8077e9ac>] zs_init+0xc8/0xfc + [<800404d4>] do_one_initcall+0x40/0x2ac + [<8076cecc>] kernel_init_freeable+0x1e4/0x270 + [<80605bec>] kernel_init+0x20/0x108 + [<800431e8>] ret_from_kernel_thread+0x14/0x1c + + Code: 2442aeb0 ae120024 ae0200d0 <8c67002c> 50e00001 8c670000 3c06806e 3c05806e afb30010 + + ---[ end trace 0000000000000000 ]--- + +(report at the offending commit) -- where a pointer is dereferenced that +has been derived from a null pointer to the port's parent device. + +Since no device is available with legacy probing and it's not anymore a +preferable way to discover devices anyway, switch the driver to using a +platform device and use it as the port's parent device. Update resource +handling accordingly and only request the actual span of addresses used +within the slot, which will have had its resource already requested by +generic platform device code. + +Use platform_driver_probe() not just because SCC devices are fixed with +solder on board and not straightforward to remove, but foremost because +the associated TTY's major device number is the same as used by the dz +driver and the first driver to claim it will prevent the other one from +using it. Either one DZ device or some SCC devices will be present in a +given system but never both at a time, and therefore we want the major +device number to be claimed by the first driver to actually successfully +bind to its device and platform_driver_probe() is a way to fulfil that. + +An unfortunate consequence of the switch to a platform device is we now +hand the console over from the bootconsole much later in the bootstrap. +The firmware console handler appears good enough though to work so late +and in particular with interrupts enabled. + +Since there is one way only remaining to reach zs_reset() now, remove +the port initialisation marker as no longer needed and go through the +channel reset unconditionally. + +Fixes: 84a9582fd203 ("serial: core: Start managing serial controllers to enable runtime PM") +Signed-off-by: Maciej W. Rozycki +Cc: stable@vger.kernel.org # needs to use .remove_new for <= 6.10 +Link: https://patch.msgid.link/alpine.DEB.2.21.2605062328480.46195@angie.orcam.me.uk +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Sasha Levin +--- + arch/mips/dec/platform.c | 60 +++++++++++- + drivers/tty/serial/zs.c | 192 +++++++++++++++------------------------ + drivers/tty/serial/zs.h | 1 - + 3 files changed, 129 insertions(+), 124 deletions(-) + +diff --git a/arch/mips/dec/platform.c b/arch/mips/dec/platform.c +index fdecc91ee22abb..723ce16cbfc0cc 100644 +--- a/arch/mips/dec/platform.c ++++ b/arch/mips/dec/platform.c +@@ -13,6 +13,7 @@ + #include + + #include ++#include + #include + #include + #include +@@ -53,10 +54,37 @@ static struct platform_device *dec_dz_devices[] __initdata = { + &dec_dz_device, + }; + ++static struct resource dec_zs_resources[][2] = { ++ { ++ { .name = "scc0", .flags = IORESOURCE_MEM, }, ++ { .name = "scc0", .flags = IORESOURCE_IRQ, }, ++ }, ++ { ++ { .name = "scc1", .flags = IORESOURCE_MEM, }, ++ { .name = "scc1", .flags = IORESOURCE_IRQ, }, ++ }, ++}; ++ ++static struct platform_device dec_zs_device[] = { ++ { ++ .name = "zs", ++ .id = 0, ++ .resource = dec_zs_resources[0], ++ .num_resources = ARRAY_SIZE(dec_zs_resources[0]), ++ }, ++ { ++ .name = "zs", ++ .id = 1, ++ .resource = dec_zs_resources[1], ++ .num_resources = ARRAY_SIZE(dec_zs_resources[1]), ++ }, ++}; ++ + static int __init dec_add_devices(void) + { +- int ret1, ret2; +- int num_dz; ++ struct platform_device *dec_zs_devices[ARRAY_SIZE(dec_zs_device)]; ++ int ret1, ret2, ret3; ++ int num_dz, num_zs; + int irq, i; + + dec_rtc_resources[0].start = RTC_PORT(0); +@@ -84,10 +112,36 @@ static int __init dec_add_devices(void) + } + num_dz = i; + ++ i = 0; ++ irq = dec_interrupt[DEC_IRQ_SCC0]; ++ if (irq >= 0) { ++ resource_size_t base = dec_kn_slot_base + IOASIC_SCC0; ++ ++ dec_zs_device[i].resource[0].start = base; ++ dec_zs_device[i].resource[0].end = base + dec_kn_slot_size - 1; ++ dec_zs_device[i].resource[1].start = irq; ++ dec_zs_device[i].resource[1].end = irq; ++ dec_zs_devices[i] = &dec_zs_device[i]; ++ i++; ++ } ++ irq = dec_interrupt[DEC_IRQ_SCC1]; ++ if (irq >= 0) { ++ resource_size_t base = dec_kn_slot_base + IOASIC_SCC1; ++ ++ dec_zs_device[i].resource[0].start = base; ++ dec_zs_device[i].resource[0].end = base + dec_kn_slot_size - 1; ++ dec_zs_device[i].resource[1].start = irq; ++ dec_zs_device[i].resource[1].end = irq; ++ dec_zs_devices[i] = &dec_zs_device[i]; ++ i++; ++ } ++ num_zs = i; ++ + ret1 = platform_device_register(&dec_rtc_device); + ret2 = IS_ENABLED(CONFIG_32BIT) ? + platform_add_devices(dec_dz_devices, num_dz) : 0; +- return ret1 ? ret1 : ret2; ++ ret3 = platform_add_devices(dec_zs_devices, num_zs); ++ return ret1 ? ret1 : ret2 ? ret2 : ret3; + } + + device_initcall(dec_add_devices); +diff --git a/drivers/tty/serial/zs.c b/drivers/tty/serial/zs.c +index b58d5af0fdc35c..0e4471af1e9bd5 100644 +--- a/drivers/tty/serial/zs.c ++++ b/drivers/tty/serial/zs.c +@@ -56,6 +56,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -66,10 +67,6 @@ + + #include + +-#include +-#include +-#include +- + #include "zs.h" + + +@@ -79,7 +76,7 @@ MODULE_LICENSE("GPL"); + + + static char zs_name[] __initdata = "DECstation Z85C30 serial driver version "; +-static char zs_version[] __initdata = "0.10"; ++static char zs_version[] __initdata = "0.11"; + + /* + * It would be nice to dynamically allocate everything that +@@ -98,12 +95,8 @@ static char zs_version[] __initdata = "0.10"; + + #define to_zport(uport) container_of(uport, struct zs_port, port) + +-struct zs_parms { +- resource_size_t scc[ZS_NUM_SCCS]; +- int irq[ZS_NUM_SCCS]; +-}; +- + static struct zs_scc zs_sccs[ZS_NUM_SCCS]; ++static struct uart_driver zs_reg; + + /* + * Set parameters in WR5, WR12, WR13 such as not to interfere +@@ -838,16 +831,15 @@ static void zs_reset(struct zs_port *zport) + + spin_lock_irqsave(&scc->zlock, flags); + irq = !irqs_disabled_flags(flags); +- if (!zport->initialised) { +- /* Reset the pointer first, just in case... */ +- read_zsreg(zport, R0); +- /* And let the current transmission finish. */ +- zs_line_drain(zport, irq); +- write_zsreg(zport, R9, zport == zport_a ? CHRA : CHRB); +- udelay(10); +- write_zsreg(zport, R9, 0); +- zport->initialised = 1; +- } ++ ++ /* Reset the pointer first, just in case... */ ++ read_zsreg(zport, R0); ++ /* And let the current transmission finish. */ ++ zs_line_drain(zport, irq); ++ write_zsreg(zport, R9, zport == zport_a ? CHRA : CHRB); ++ udelay(10); ++ write_zsreg(zport, R9, 0); ++ + load_zsregs(zport, zport->regs, irq); + spin_unlock_irqrestore(&scc->zlock, flags); + } +@@ -1054,63 +1046,62 @@ static const struct uart_ops zs_ops = { + /* + * Initialize Z85C30 port structures. + */ +-static int __init zs_probe_sccs(void) ++static int __init zs_probe(struct platform_device *pdev) + { +- static int probed; +- struct zs_parms zs_parms; +- int chip, side, irq; +- int n_chips = 0; ++ struct resource *mem_resource, *irq_resource; ++ int chip, side; + int i; + +- if (probed) +- return 0; ++ mem_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ irq_resource = platform_get_resource(pdev, IORESOURCE_IRQ, 0); ++ if (!mem_resource || !irq_resource) ++ return -ENODEV; + +- irq = dec_interrupt[DEC_IRQ_SCC0]; +- if (irq >= 0) { +- zs_parms.scc[n_chips] = IOASIC_SCC0; +- zs_parms.irq[n_chips] = dec_interrupt[DEC_IRQ_SCC0]; +- n_chips++; +- } +- irq = dec_interrupt[DEC_IRQ_SCC1]; +- if (irq >= 0) { +- zs_parms.scc[n_chips] = IOASIC_SCC1; +- zs_parms.irq[n_chips] = dec_interrupt[DEC_IRQ_SCC1]; +- n_chips++; +- } +- if (!n_chips) +- return -ENXIO; +- +- probed = 1; +- +- for (chip = 0; chip < n_chips; chip++) { +- spin_lock_init(&zs_sccs[chip].zlock); +- for (side = 0; side < ZS_NUM_CHAN; side++) { +- struct zs_port *zport = &zs_sccs[chip].zport[side]; +- struct uart_port *uport = &zport->port; +- +- zport->scc = &zs_sccs[chip]; +- zport->clk_mode = 16; +- +- uport->has_sysrq = IS_ENABLED(CONFIG_SERIAL_ZS_CONSOLE); +- uport->irq = zs_parms.irq[chip]; +- uport->uartclk = ZS_CLOCK; +- uport->fifosize = 1; +- uport->iotype = UPIO_MEM; +- uport->flags = UPF_BOOT_AUTOCONF; +- uport->ops = &zs_ops; +- uport->line = chip * ZS_NUM_CHAN + side; +- uport->mapbase = dec_kn_slot_base + +- zs_parms.scc[chip] + +- (side ^ ZS_CHAN_B) * ZS_CHAN_IO_SIZE; +- +- for (i = 0; i < ZS_NUM_REGS; i++) +- zport->regs[i] = zs_init_regs[i]; +- } ++ chip = pdev->id; ++ spin_lock_init(&zs_sccs[chip].zlock); ++ for (side = 0; side < ZS_NUM_CHAN; side++) { ++ struct zs_port *zport = &zs_sccs[chip].zport[side]; ++ struct uart_port *uport = &zport->port; ++ ++ zport->scc = &zs_sccs[chip]; ++ zport->clk_mode = 16; ++ ++ uport->dev = &pdev->dev; ++ uport->has_sysrq = IS_ENABLED(CONFIG_SERIAL_ZS_CONSOLE); ++ uport->irq = irq_resource->start; ++ uport->uartclk = ZS_CLOCK; ++ uport->fifosize = 1; ++ uport->iotype = UPIO_MEM; ++ uport->flags = UPF_BOOT_AUTOCONF; ++ uport->ops = &zs_ops; ++ uport->line = chip * ZS_NUM_CHAN + side; ++ uport->mapbase = mem_resource->start + ++ (side ^ ZS_CHAN_B) * ZS_CHAN_IO_SIZE; ++ ++ for (i = 0; i < ZS_NUM_REGS; i++) ++ zport->regs[i] = zs_init_regs[i]; ++ ++ if (uart_add_one_port(&zs_reg, uport)) ++ uport->dev = NULL; + } + + return 0; + } + ++static void __exit zs_remove(struct platform_device *pdev) ++{ ++ int chip, side; ++ ++ chip = pdev->id; ++ for (side = ZS_NUM_CHAN - 1; side >= 0; side--) { ++ struct zs_port *zport = &zs_sccs[chip].zport[side]; ++ struct uart_port *uport = &zport->port; ++ ++ if (uport->dev) ++ uart_remove_one_port(&zs_reg, uport); ++ } ++} ++ + + #ifdef CONFIG_SERIAL_ZS_CONSOLE + static void zs_console_putchar(struct uart_port *uport, unsigned char ch) +@@ -1191,20 +1182,14 @@ static int __init zs_console_setup(struct console *co, char *options) + int bits = 8; + int parity = 'n'; + int flow = 'n'; +- int ret; +- +- ret = zs_map_port(uport); +- if (ret) +- return ret; +- +- zs_reset(zport); + ++ if (!zport->scc) ++ return -ENODEV; + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + return uart_set_options(uport, co, baud, parity, bits, flow); + } + +-static struct uart_driver zs_reg; + static struct console zs_console = { + .name = "ttyS", + .write = zs_console_write, +@@ -1215,23 +1200,6 @@ static struct console zs_console = { + .data = &zs_reg, + }; + +-/* +- * Register console. +- */ +-static int __init zs_serial_console_init(void) +-{ +- int ret; +- +- ret = zs_probe_sccs(); +- if (ret) +- return ret; +- register_console(&zs_console); +- +- return 0; +-} +- +-console_initcall(zs_serial_console_init); +- + #define SERIAL_ZS_CONSOLE &zs_console + #else + #define SERIAL_ZS_CONSOLE NULL +@@ -1247,47 +1215,31 @@ static struct uart_driver zs_reg = { + .cons = SERIAL_ZS_CONSOLE, + }; + ++static struct platform_driver zs_driver = { ++ .remove_new = __exit_p(zs_remove), ++ .driver = { .name = "zs" }, ++}; ++ + /* zs_init inits the driver. */ + static int __init zs_init(void) + { +- int i, ret; ++ int ret; + + pr_info("%s%s\n", zs_name, zs_version); + +- /* Find out how many Z85C30 SCCs we have. */ +- ret = zs_probe_sccs(); +- if (ret) +- return ret; +- + ret = uart_register_driver(&zs_reg); + if (ret) + return ret; ++ ret = platform_driver_probe(&zs_driver, zs_probe); ++ if (ret) ++ uart_unregister_driver(&zs_reg); + +- for (i = 0; i < ZS_NUM_SCCS * ZS_NUM_CHAN; i++) { +- struct zs_scc *scc = &zs_sccs[i / ZS_NUM_CHAN]; +- struct zs_port *zport = &scc->zport[i % ZS_NUM_CHAN]; +- struct uart_port *uport = &zport->port; +- +- if (zport->scc) +- uart_add_one_port(&zs_reg, uport); +- } +- +- return 0; ++ return ret; + } + + static void __exit zs_exit(void) + { +- int i; +- +- for (i = ZS_NUM_SCCS * ZS_NUM_CHAN - 1; i >= 0; i--) { +- struct zs_scc *scc = &zs_sccs[i / ZS_NUM_CHAN]; +- struct zs_port *zport = &scc->zport[i % ZS_NUM_CHAN]; +- struct uart_port *uport = &zport->port; +- +- if (zport->scc) +- uart_remove_one_port(&zs_reg, uport); +- } +- ++ platform_driver_unregister(&zs_driver); + uart_unregister_driver(&zs_reg); + } + +diff --git a/drivers/tty/serial/zs.h b/drivers/tty/serial/zs.h +index 8e51f847bc03f9..e0d3c189b33f66 100644 +--- a/drivers/tty/serial/zs.h ++++ b/drivers/tty/serial/zs.h +@@ -22,7 +22,6 @@ + struct zs_port { + struct zs_scc *scc; /* Containing SCC. */ + struct uart_port port; /* Underlying UART. */ +- int initialised; /* For the console port. */ + + int clk_mode; /* May be 1, 16, 32, or 64. */ + +-- +2.53.0 + diff --git a/queue-6.6/series b/queue-6.6/series index 726695120e..8d0ca44883 100644 --- a/queue-6.6/series +++ b/queue-6.6/series @@ -195,3 +195,6 @@ x86-kexec-disable-kcov-instrumentation-after-load_se.patch landlock-fix-handling-of-disconnected-directories.patch usb-serial-digi_acceleport-fix-memory-corruption-wit.patch xhci-tegra-fix-ghost-usb-device-on-dual-role-port-un.patch +serial-dz-fix-bootconsole-handover-lockup.patch +serial-dz-convert-to-use-a-platform-device.patch +serial-zs-convert-to-use-a-platform-device.patch