6.1.191-rc1 review
Subsystems (12)
Greg Kroah-Hartman
maintainerPATCH 523/1887 net: team: fix NULL pointer dereference in team_xmit during mode change
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Weiming Shi <[email protected]>
[ Upstream commit 25fe708bbc59289d3d1ea4b126fbc1b460a072a5 ]
__team_change_mode() clears team->ops with memset() before restoring safe dummy handlers via team_adjust_ops(). A concurrent team_xmit() running under RCU on another CPU can read team->ops.transmit during this window and call a NULL function pointer, crashing the kernel.
The race requires a mode change (CAP_NET_ADMIN) concurrent with transmit on the team device.
BUG: kernel NULL pointer dereference, address: 0000000000000000 Oops: 0010 [#1] SMP KASAN NOPTI RIP: 0010:0x0 Call Trace: team_xmit (drivers/net/team/team_core.c:1853) dev_hard_start_xmit (net/core/dev.c:3904) __dev_queue_xmit (net/core/dev.c:4871) packet_sendmsg (net/packet/af_packet.c:3109) __sys_sendto (net/socket.c:2265)
The original code assumed that no ports means no traffic, so mode changes could freely memset()/memcpy() the ops. AF_PACKET with forced carrier breaks that assumption.
Prevent the race instead of making it safe: replace memset()/memcpy() with per-field updates that never touch transmit or receive. Those two handlers are managed solely by team_adjust_ops(), which already installs dummies when tx_en_port_count == 0 (always true during mode change since no ports are present). WRITE_ONCE/READ_ONCE prevent store/load tearing on the handler pointers.
synchronize_net() before exit_op() drains in-flight readers that may still reference old mode state from before port removal switched the handlers to dummies.
Stable-dep-of: b7454f1940c5 ("net: team: stop reusing skb after queue override")
--- drivers/net/team/team.c | 45 +++++++++++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 13 deletions(-)
drivers/net/team/team.c +32 -13
@@ -535,21 +535,23 @@ static void team_adjust_ops(struct team *team)if (!team->en_port_count || !team_is_mode_set (team) || !team->mode->ops->transmit) - team->ops.transmit = team_dummy_transmit; +WRITE_ONCE (team->ops.transmit, team_dummy_transmit);else - team->ops.transmit = team->mode->ops->transmit; +WRITE_ONCE (team->ops.transmit, team->mode->ops->transmit);if (!team->en_port_count || !team_is_mode_set (team) || !team->mode->ops->receive) - team->ops.receive = team_dummy_receive; +WRITE_ONCE (team->ops.receive, team_dummy_receive);else - team->ops.receive = team->mode->ops->receive; +WRITE_ONCE (team->ops.receive, team->mode->ops->receive); }/* -* We can benefit from the fact that it's ensured no port is present -* at the time of mode change. Therefore no packets are in fly so there's no -* need to set mode operations in any special way. +* team_change_mode() ensures no ports are present during mode change, +* but lockless readers can still reach team_xmit(). Avoid touching +* transmit/receive -- they are already set to dummies by +* team_adjust_ops() since no ports are enabled. synchronize_net() +* drains in-flight readers before destroying old mode state. */ static int __team_change_mode (struct team *team,const struct team_mode *new_mode)
@@ -558,9 +560,21 @@ static int __team_change_mode(struct team *team,if (team_is_mode_set (team)) {void (*exit_op)(struct team *team) = team->ops.exit; -/* Clear ops area so no callback is called any longer */ -memset (&team->ops,0 ,sizeof (struct team_mode_ops )); -team_adjust_ops (team); +/* Clear cold-path ops used only under RTNL. transmit and +* receive are already dummies (no ports) so leave them +* alone -- overwriting them is the source of the race. +*/ + team->ops.init =NULL ; + team->ops.exit =NULL ; + team->ops.port_enter =NULL ; + team->ops.port_leave =NULL ; + team->ops.port_change_dev_addr =NULL ; + team->ops.port_tx_disabled =NULL ; + +/* Wait for in-flight readers before tearing down mode +* state they may reference. +*/ +synchronize_net ();if (exit_op)exit_op (team);
@@ -583,7 +597,12 @@ static int __team_change_mode(struct team *team, } team->mode = new_mode; -memcpy (&team->ops, new_mode->ops,sizeof (struct team_mode_ops )); + team->ops.init = new_mode->ops->init; + team->ops.exit = new_mode->ops->exit; + team->ops.port_enter = new_mode->ops->port_enter; + team->ops.port_leave = new_mode->ops->port_leave; + team->ops.port_change_dev_addr = new_mode->ops->port_change_dev_addr; + team->ops.port_tx_disabled = new_mode->ops->port_tx_disabled;team_adjust_ops (team);return 0 ;
@@ -744,7 +763,7 @@ static rx_handler_result_t team_handle_frame(struct sk_buff **pskb)/* allow exact match delivery for disabled ports */ res =RX_HANDLER_EXACT ; }else { - res = team->ops.receive (team, port, skb); + res =READ_ONCE (team->ops.receive)(team, port, skb); }if (res ==RX_HANDLER_ANOTHER ) {struct team_pcpu_stats *pcpu_stats;
@@ -1742,7 +1761,7 @@ static netdev_tx_t team_xmit(struct sk_buff *skb, struct net_device *dev) tx_success =team_queue_override_transmit (team, skb);if (!tx_success) - tx_success = team->ops.transmit (team, skb); + tx_success =READ_ONCE (team->ops.transmit)(team, skb);if (tx_success) {struct team_pcpu_stats *pcpu_stats;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 524/1887 net: team: stop reusing skb after queue override
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Weiming Shi <[email protected]>
[ Upstream commit b7454f1940c5e6d92e753853c476a769e452f30a ]
A nonzero dev_queue_xmit() result does not return ownership of the skb. When an override port's qdisc drops it, the queue override helper used to try another port or the mode transmit op with the freed skb.
Return the handoff decision separately from transmit success. Select only the first override port under RCU and stop after handing it the skb, regardless of the lower transmit result. Keep the existing success and drop accounting.
BUG: KASAN: slab-use-after-free in sk_skb_reason_drop Write of size 4 by task poc/5040 sk_skb_reason_drop (net/core/skbuff.c:1240) dev_kfree_skb_any_reason (net/core/dev.c:3470) ab_transmit (drivers/net/team/team_mode_activebackup.c:49) team_xmit (drivers/net/team/team_core.c:1849) __dev_direct_xmit (net/core/dev.c:4934) Freed by task 5040: __tcf_kfree_skb_list (net/sched/sch_generic.c:43) __dev_queue_xmit (net/core/dev.c:4831) team_xmit (drivers/net/team/team_core.c:1846) Kernel panic - not syncing: KASAN: panic_on_warn set ...
--- drivers/net/team/team.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-)
drivers/net/team/team.c +8 -8
@@ -822,7 +822,8 @@ static struct list_head *__team_get_qom_list(struct team *team, u16 queue_id)/* * note: already called with rcu_read_lock */ -static bool team_queue_override_transmit (struct team *team,struct sk_buff *skb) +static bool team_queue_override_transmit (struct team *team,struct sk_buff *skb, +bool *tx_success) {struct list_head *qom_list;struct team_port *port;
@@ -830,11 +831,11 @@ static bool team_queue_override_transmit(struct team *team, struct sk_buff *skb)if (!team->queue_override_enabled || !skb->queue_mapping)return false; qom_list =__team_get_qom_list (team, skb->queue_mapping); -list_for_each_entry_rcu (port, qom_list, qom_list) { -if (!team_dev_queue_xmit (team, port, skb)) -return true; - } -return false; + port = list_first_or_null_rcu(qom_list, struct team_port, qom_list); +if (!port) +return false; + *tx_success = !team_dev_queue_xmit (team, port, skb); +return true; } static void __team_queue_override_port_del(struct team *team,
@@ -1759,8 +1760,7 @@ static netdev_tx_t team_xmit(struct sk_buff *skb, struct net_device *dev)bool tx_success;unsigned int len = skb->len; - tx_success =team_queue_override_transmit (team, skb); -if (!tx_success) +if (!team_queue_override_transmit (team, skb, &tx_success)) tx_success =READ_ONCE (team->ops.transmit)(team, skb);if (tx_success) {struct team_pcpu_stats *pcpu_stats;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 525/1887 Bluetooth: RFCOMM: connect the session socket without rfcomm_mutex
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mikhail Gavrilov <[email protected]>
[ Upstream commit ac022970e9fb0a5f7e7d1f6c8e104c1ed924dd1c ]
An RFCOMM connect() issued while a BR/EDR link is being authenticated makes lockdep report a circular dependency, and the reported cycle is a real AB/BA between rfcomm_mutex and hdev->lock.
rfcomm_security_cfm() is called from the HCI event path, which already holds hdev->lock:
hci_rx_work()
hci_event_packet()
hci_cc_read_enc_key_size() [hdev->lock]
hci_encrypt_cfm() [hci_cb_list_lock]
rfcomm_security_cfm() [rfcomm_mutex]while an RFCOMM connect() from userspace takes the same two locks the other way round:
rfcomm_sock_connect()
rfcomm_dlc_open() [rfcomm_mutex]
__rfcomm_dlc_open()
rfcomm_session_create()
kernel_connect()
l2cap_sock_connect()
l2cap_chan_connect() [hdev->lock] WARNING: possible circular locking dependency detected kworker/u131:1/1128 is trying to acquire lock:
rfcomm_mutex, at: rfcomm_security_cfm+0x31/0x3e0 [rfcomm] but task is already holding lock:
hci_cb_list_lock, at: hci_cc_read_enc_key_size+0x1d2/0xcc0
Chain exists of:
rfcomm_mutex --> &hdev->lock --> hci_cb_list_lockhci_auth_complete_evt() and hci_encrypt_change_evt() reach the callback the same way.
Both orders have to be seen in the same boot, which is why a BR/EDR connection alone is not enough to show it: a session set up by the remote side is created by rfcomm_accept_connection() in krfcommd, which calls kernel_accept() and never takes hdev->lock under rfcomm_mutex. Connecting a device that authenticates and encrypts the link and then calling connect() on an RFCOMM socket towards any address - the connect does not have to succeed, the order is recorded before the page timeout
- reports it every time.
Only the session socket has to be connected with the lock held, and it does not: nothing else can see the socket before it is put on the session list. So connect it first and take rfcomm_mutex afterwards, which removes the rfcomm_mutex -> hdev->lock order for good, rather than keeping the HCI event path out of rfcomm_mutex.
rfcomm_session_create() becomes rfcomm_session_connect(), which returns the connected socket without touching the session list, and rfcomm_dlc_open() adds the session once it holds the lock again. If another opener added a session for the same pair while this socket was connecting, that session is used and this socket is dropped. __rfcomm_dlc_open() now takes the session it should use, and its state check runs after the lock is re-acquired, so a DLC that was opened or closed in the meantime is still handled.
Over an existing ACL link the connection can complete before the session reaches the list, and the wakeup from the socket callback is then lost, so krfcommd is woken once the session is visible.
Connecting without the lock opens a window in which the socket can be closed. The DLC is not on a session yet, so rfcomm_dlc_close() finds nothing to do and returns without touching it, and rfcomm_dlc_open() would then attach a DLC whose owner is gone. rfcomm_dlc_close() now marks such a DLC with RFCOMM_CLOSED, and rfcomm_dlc_open() checks the mark once it holds rfcomm_mutex again and drops the socket instead of attaching the DLC. The mark is cleared when an open starts, so a DLC that is reused is not affected. This covers both callers of rfcomm_dlc_open(), the socket and the tty layer.
--- include/net/bluetooth/rfcomm.h | 1 + net/bluetooth/rfcomm/core.c | 134 +++++++++++++++++++++++---------- 2 files changed, 96 insertions(+), 39 deletions(-)
include/net/bluetooth/rfcomm.h +1 -0
@@ -210,6 +210,7 @@ struct rfcomm_dlc {#define RFCOMM_AUTH_REJECT 7#define RFCOMM_DEFER_SETUP 8#define RFCOMM_ENC_DROP 9 +#define RFCOMM_CLOSED 10/* Scheduling flags and events */ #define RFCOMM_SCHED_WAKEUP 31
net/bluetooth/rfcomm/core.c +95 -39
191 lines not on this page · raw message
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 526/1887 drm/vmwgfx: validate pitch coming from userspace
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zack Rusin <[email protected]>
[ Upstream commit 21bc51cb89ced74d6645a138cc2f21184bc75094 ]
Validate the pitch, alongside the box dimensions before trying to copy data from the underlying surface. Fixes possible out of bounds reads with cursor snooping.
--- drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-)
drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +18 -4
@@ -277,6 +277,7 @@ void vmw_kms_cursor_snoop(struct vmw_surface *srf,unsigned long kmap_num;SVGA3dCopyBox *box;unsigned box_count; +u64 src_extent;void *virtual;bool dummy;struct vmw_dma_cmd {
@@ -285,6 +286,8 @@ void vmw_kms_cursor_snoop(struct vmw_surface *srf, } *cmd;int i, ret; +BUILD_BUG_ON (sizeof (*srf->snooper.image) !=4 ); + cmd = container_of(header, struct vmw_dma_cmd, header);/* No snooper installed */
@@ -322,8 +325,19 @@ void vmw_kms_cursor_snoop(struct vmw_surface *srf,return ; } +if (box->w ==0 || box->h ==0 ) +return ; + + src_extent = (u64)(box->h -1 ) * cmd->dma.guest.pitch + + (u64 )box->w *4 ; +if (src_extent > bo->base.size) { +DRM_ERROR ("Cursor snoop source of %llu bytes exceeds the %zu byte buffer\n" , + src_extent, bo->base.size); +return ; + } + kmap_offset = cmd->dma.guest.ptr.offset >>PAGE_SHIFT ; - kmap_num = (64 *64 *4 ) >>PAGE_SHIFT ; + kmap_num =PFN_UP (src_extent); ret =ttm_bo_reserve (bo, true, false,NULL );if (unlikely (ret !=0 )) {
@@ -337,13 +351,13 @@ void vmw_kms_cursor_snoop(struct vmw_surface *srf, virtual =ttm_kmap_obj_virtual (&map, &dummy); -if (box->w ==64 && cmd->dma.guest.pitch ==64 *4 ) { +if (box->w ==64 && box->h ==64 && cmd->dma.guest.pitch ==64 *4 ) {memcpy (srf->snooper.image, virtual,64 *64 *4 ); }else { -/* Image is unsigned pointer. */ +/* The destination stride is in pixels. */ for (i =0 ; i < box->h; i++)memcpy (srf->snooper.image + i *64 , - virtual + i * cmd->dma.guest.pitch, + virtual + (size_t )i * cmd->dma.guest.pitch, box->w *4 ); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 527/1887 Bluetooth: ISO: Serialize concurrent connect calls
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chengfeng Ye <[email protected]>
[ Upstream commit e32d80293a0e1a160d5e5101daa8c82148c07510 ]
iso_sock_connect() checks the socket state before taking the socket lock and drops the lock again before setting up a connection. Two callers can both pass the admission check while the socket is open or bound.
Caller A can copy its destination for route selection, then caller B can replace the socket destination before A binds or connects the CIS. A then uses B's destination with the route selected for its own request. B can also proceed after A attaches a connection and sets BT_CONNECT. For deferred BIS setup, B can bind a second BIS and overwrite iso_pi(sk)->conn, leaving the first connection's reference and conn->sk back-pointer stranded. Concurrent CIS connects can likewise replace the socket's connection. Later teardown only detaches the current connection, so a callback on the old connection can race with socket release and access a freed socket.
KASAN reported:
BUG: KASAN: slab-use-after-free in iso_sock_hold+0xf7/0x1b0 Call Trace: iso_sock_hold+0xf7/0x1b0 iso_conn_del+0x7b/0x1d0 iso_connect_cfm+0x186/0x16a0 hci_conn_failed+0x154/0x280 hci_abort_conn_sync+0x3dc/0x7d0 hci_cmd_sync_work+0x173/0x300 Allocated by task 121: sk_alloc+0x2b/0x6d0 bt_sock_alloc+0x29/0x370 iso_sock_alloc.constprop.0+0x19/0x300 iso_sock_create+0x94/0x100 Freed by task 121: kfree+0x121/0x3c0 __sk_destruct+0x42b/0x540 iso_sock_release+0x29d/0x340 __sock_release+0xa1/0x260
Check admission under the socket lock and mark the connect operation in progress before publishing its destination. Keep that flag set across route lookup and connection setup, rejecting another connect with -EBADFD before it can change the destination or attach a connection. Clear the flag after either helper returns, including on failure, so a failed setup can be retried.
A separate flag is needed because the socket lock must be released before acquiring the HCI device lock, while BT_CONNECT requires an attached connection. Keep the existing state transitions and the deferred CIS completion through iso_sock_recvmsg().
Reject an existing socket attachment in __iso_chan_add() before taking a new reference or publishing either pointer, so every caller preserves the connection association. Keep the same-socket, same-connection success case for deferred CIS setup. Reject an attached socket before BIS setup so a reusable BIS is not claimed before the attachment is rejected.
In CIS setup, reject a different HCI connection before iso_conn_add() and drop the per-attempt HCI hold directly. An unowned iso_conn may still carry another reference after detachment, so its destruction cannot be relied on to release that hold.
iso_listen_bis() creates a fresh PA HCI/ISO pair under the device lock, so the temporary iso_conn_put() frees the ISO candidate and drops its HCI hold on rejection. iso_conn_ready() uses a freshly allocated, unpublished child with no connection, making the socket-side rejection unreachable.
--- net/bluetooth/iso.c | 73 +++++++++++++++++++++++++++++---------------- 1 file changed, 48 insertions(+), 25 deletions(-)
net/bluetooth/iso.c +48 -25
@@ -49,6 +49,11 @@ static void iso_sock_kill(struct sock *sk);#define BASE_MAX_LENGTH (HCI_MAX_PER_AD_LENGTH - EIR_SERVICE_DATA_LENGTH)#define EIR_BAA_SERVICE_UUID 0x1851 +/* iso_pinfo flags values */ +enum { +BT_SK_CONNECTING , +}; +struct iso_pinfo {struct bt_sock bt;bdaddr_t src;
@@ -59,7 +64,7 @@ struct iso_pinfo {__u8 bc_num_bis;__u8 bc_bis[ISO_MAX_NUM_BIS ];__u16 sync_handle; -__u32 flags; +unsigned long flags;struct bt_iso_qos qos;__u8 base_len;__u8 base[BASE_MAX_LENGTH ];
@@ -225,6 +230,9 @@ static int __iso_chan_add(struct iso_conn *conn, struct sock *sk,return -EBUSY ; } +if (iso_pi (sk)->conn) +return -EISCONN ; +iso_pi (sk)->conn = conn; conn->sk = sk;
@@ -269,6 +277,12 @@ static int iso_connect_bis(struct sock *sk)return -EHOSTUNREACH ;hci_dev_lock (hdev); +lock_sock (sk); + +if (iso_pi (sk)->conn) { + err = -EISCONN ; + goto unlock; + }if (!bis_capable (hdev)) { err = -EOPNOTSUPP ;
@@ -296,13 +310,9 @@ static int iso_connect_bis(struct sock *sk) goto unlock; } -lock_sock (sk); - err =iso_chan_add (conn, sk,NULL ); -if (err) { -release_sock (sk); +if (err) goto unlock; - }/* Update source addr of the socket */ bacpy (&iso_pi (sk)->src, &hcon->src);
@@ -315,9 +325,8 @@ static int iso_connect_bis(struct sock *sk)iso_sock_set_timer (sk, sk->sk_sndtimeo); } -release_sock (sk); - unlock: +release_sock (sk);hci_dev_unlock (hdev);hci_dev_put (hdev);return err;
@@ -338,6 +347,7 @@ static int iso_connect_cis(struct sock *sk)return -EHOSTUNREACH ;hci_dev_lock (hdev); +lock_sock (sk);if (!cis_central_capable (hdev)) { err = -EOPNOTSUPP ;
@@ -369,6 +379,13 @@ static int iso_connect_cis(struct sock *sk) } } +/* The socket lock keeps the current attachment and its hcon stable. */ +if (iso_pi (sk)->conn &&iso_pi (sk)->conn->hcon != hcon) { +hci_conn_drop (hcon); + err = -EISCONN ; + goto unlock; + } + conn =iso_conn_add (hcon);if (!conn) {hci_conn_drop (hcon);
@@ -376,13 +393,9 @@ static int iso_connect_cis(struct sock *sk) goto unlock; } -lock_sock (sk); - err =iso_chan_add (conn, sk,NULL ); -if (err) { -release_sock (sk); +if (err) goto unlock; - }/* Update source addr of the socket */ bacpy (&iso_pi (sk)->src, &hcon->src);
@@ -398,9 +411,8 @@ static int iso_connect_cis(struct sock *sk)iso_sock_set_timer (sk, sk->sk_sndtimeo); } -release_sock (sk); - unlock: +release_sock (sk);hci_dev_unlock (hdev);hci_dev_put (hdev);return err;
@@ -847,17 +859,26 @@ static int iso_sock_connect(struct socket *sock, struct sockaddr *addr, addr->sa_family != AF_BLUETOOTH)return -EINVAL ; -if (sk->sk_state !=BT_OPEN && sk->sk_state !=BT_BOUND ) -return -EBADFD ; +lock_sock (sk); -if (sk->sk_type !=SOCK_SEQPACKET ) -return -EINVAL ; +if ((sk->sk_state !=BT_OPEN && sk->sk_state !=BT_BOUND ) || +test_bit (BT_SK_CONNECTING , &iso_pi (sk)->flags)) { + err = -EBADFD ; + goto done; + } + +if (sk->sk_type !=SOCK_SEQPACKET ) { + err = -EINVAL ; + goto done; + }/* Check if the address type is of LE type */ -if (!bdaddr_type_is_le (sa->iso_bdaddr_type)) -return -EINVAL ; +if (!bdaddr_type_is_le (sa->iso_bdaddr_type)) { + err = -EINVAL ; + goto done; + } -lock_sock (sk); +set_bit (BT_SK_CONNECTING , &iso_pi (sk)->flags);bacpy (&iso_pi (sk)->dst, &sa->iso_bdaddr);iso_pi (sk)->dst_type = sa->iso_bdaddr_type;
@@ -869,16 +890,18 @@ static int iso_sock_connect(struct socket *sock, struct sockaddr *addr,else err =iso_connect_bis (sk); -if (err) -return err; -lock_sock (sk); +clear_bit (BT_SK_CONNECTING , &iso_pi (sk)->flags); +if (err) + goto done; +if (!test_bit (BT_SK_DEFER_SETUP , &bt_sk (sk)->flags)) { err =bt_sock_wait_state (sk,BT_CONNECTED ,sock_sndtimeo (sk, flags &O_NONBLOCK )); } +done:release_sock (sk);return err; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 528/1887 namespace: dont inherit MNT_UMOUNT in clone_mnt()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
[ Upstream commit e2c6d326216642011da318b9edfd5d57ebd5a56f ]
MNT_UMOUNT gets raised on a mount that umount_tree() or umount_one() has taken out of its namespace. It's never cleared. __detach_mounts() unhooks a mount that carries it instead of unmounting it.
disconnect_mount() lets an unmounted child stay attached only if its parent carries it. propagate_umount() takes it as the sign that a mount is going to be unmounted when it decides what else can be unmounted, where a chain of locked mounts ends and where a for reparenting an overmount.
clone_mnt() copies the flags of the source and only masks MNT_INTERNAL_FLAGS which doesn't include MNT_UMOUNT. A mount that was lazily unmounted but is kept alive by a file descriptor can still be copied when its root is an nsfs or pidfs file because may_copy_tree() accepts those before checking whether the source is mounted at all:
mount --bind /proc/self/ns/net /tmp/x exec 3< /tmp/x umount -l /tmp/x mount --bind /proc/self/fd/3 /tmp/y
The mount on /tmp/y is attached to the caller's namespace with MNT_UMOUNT already set and every copy of it inherits the flag again.
That causes a number of bugs:
- Unlink its mountpoint from a mount namespace where it isn't one. __detach_mounts() takes it for a mount that was unmounted earlier, unhooks it from its parent and drops the reference the namespace holds on it. It stays in the namespace with one reference too few and as its own parent, so it can't be unmounted through the file descriptor anymore and the namespace never finds it again. When the namespace goes away it's leaked and points to a freed struct mnt_namespace that may_change_propagation() and anon_ns_root() read on the next propagation change or mount_setattr() through the file descriptor.
- Make it shared and unmount a peer. trace_transfers() follows the flag from the peer into it and strips it from its peer group.
- Let a propagated umount reach it. gather_candidates() marks it with T_UMOUNT_CANDIDATE but never puts it on the candidate list, so the mark is never cleared and every later propagated umount skips it. If it's the parent of a locked mount handle_locked() takes it as the cutoff and commits the locked child that should have stayed and disconnect_mount() leaves that child attached to it, unmounted under a mounted parent, which is what disconnect_mount() exists to prevent. When the namespace goes away umount_tree() visits the child a second time. And once __detach_mounts() has made it its own parent reparent() never finds the end of the chain of unmounted parents.
A clone is a new mount that has never been unmounted. Add MNT_UMOUNT to MNT_INTERNAL_FLAGS so clone_mnt() never copies it. And warn when umount_tree() reaches a mount that already has it set.
--- fs/namespace.c | 4 +++- include/linux/mount.h | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-)
fs/namespace.c +3 -1
@@ -1122,7 +1122,7 @@ static struct mount *clone_mnt(struct mount *old, struct dentry *root, } mnt->mnt.mnt_flags = old->mnt.mnt_flags; - mnt->mnt.mnt_flags &= ~(MNT_WRITE_HOLD |MNT_MARKED |MNT_INTERNAL ); + mnt->mnt.mnt_flags &= ~(MNT_WRITE_HOLD |MNT_MARKED |MNT_INTERNAL |MNT_UMOUNT );atomic_inc (&sb->s_active); mnt->mnt.mnt_userns =mnt_user_ns (&old->mnt);
@@ -1561,6 +1561,8 @@ static void umount_tree(struct mount *mnt, enum umount_tree_flags how)/* Gather the mounts to umount */ for (p = mnt; p; p =next_mnt (p, mnt)) { +/* A mount is unmounted once. */ +WARN_ON_ONCE (p->mnt.mnt_flags &MNT_UMOUNT ); p->mnt.mnt_flags |=MNT_UMOUNT ;list_move (&p->mnt_list, &tmp_list); }
include/linux/mount.h +1 -1
@@ -50,7 +50,7 @@ struct path;#define MNT_INTERNAL_FLAGS (MNT_SHARED | MNT_WRITE_HOLD | MNT_INTERNAL | \ MNT_DOOMED | MNT_SYNC_UMOUNT | MNT_MARKED | \ - MNT_CURSOR) + MNT_CURSOR | MNT_UMOUNT)#define MNT_INTERNAL 0x4000
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 529/1887 namespace: check the mounts before reading their parents in pivot_root()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
[ Upstream commit 6b7c001eb5857fa09041f47673666060a2e5fc1c ]
path_pivot_root() reads the parents of new_root and of the caller's root and checks whether they are shared before it checks that either mount is in the caller's mount namespace. Only namespace_sem is held. That's fine for a mount that is in the caller's mount namespace.
But new_root can be a file descriptor to a mount that has been unmounted and that only the file descriptor keeps alive. If that mount stayed attached to its parent when it was unmounted nothing pins the parent for it. The final mntput() of the parent unhooks the children under mount_lock alone and frees the parent afterwards:
pivot_root() close(fd), last ref on the parent
------------ ---------------------------------
ex_parent = new_mnt->mnt_parent
mntput_no_expire_slowpath()
__umount_mnt(new_mnt)
cleanup_mnt()
call_rcu() IS_MNT_SHARED(ex_parent)
BUG: KASAN: slab-use-after-free in path_pivot_root+0xf1a/0x1840 Read of size 4 at addr ffff8881047382f8 by task pivot_widen/157 path_pivot_root+0xf1a/0x1840 __x64_sys_pivot_root+0x165/0x190
The same goes for the caller's root via chroot(). Both outcomes of the check end in EINVAL so nothing but the read itself goes wrong. It's the same thing commit bb4609405752 ("statmount: read the parent of an unmounted mount under mount_lock") fixed for statmount().
Check that both mounts are in the caller's namespace before their parents are read. Every path returns EINVAL either way.
--- fs/namespace.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
fs/namespace.c +3 -2
@@ -3973,14 +3973,15 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root, new_mnt =real_mount (new.mnt); root_mnt =real_mount (root.mnt); old_mnt =real_mount (old.mnt); +/* only a mounted mount has a parent that namespace_sem pins */ +if (!check_mnt (root_mnt) || !check_mnt (new_mnt)) + goto out4; ex_parent = new_mnt->mnt_parent; root_parent = root_mnt->mnt_parent;if (IS_MNT_SHARED (old_mnt) ||IS_MNT_SHARED (ex_parent) ||IS_MNT_SHARED (root_parent)) goto out4; -if (!check_mnt (root_mnt) || !check_mnt (new_mnt)) - goto out4;if (new_mnt->mnt.mnt_flags &MNT_LOCKED ) goto out4; error = -ENOENT ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 530/1887 dm: fix reading free memory in do_resume
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mikulas Patocka <[email protected]>
[ Upstream commit c9417c1c5269e2bd0570fbe53e4ec03a8d31f008 ]
When do_resume calls dm_table_get_mode(new_map), the call is done without holding any locks (it only holds a reference to the md). It may be possible that another concurrent ioctl on the same device will swap table after dm_swap_table and before dm_table_get_mode. In this case, dm_table_get_mode(new_map) reads freed memory.
This race condition was triggered by syzbot.
This commit fixes the race by reading mode of the new table before dm_swap_table. Note that new_map is also passed to dm_ima_need_measure, but this function doesn't attempt to dereference it, so this call should be safe.
Note that this is not a security bug because only root can trigger it and the commonly used tools such as lvm or cryptsetup do not call the ioctls concurrently, so that they can't trigger it acceidentally.
--- drivers/md/dm-ioctl.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/md/dm-ioctl.c +3 -1
@@ -1166,6 +1166,7 @@ static int do_resume(struct dm_ioctl *param)/* Do we need to load a new map ? */ if (new_map) {sector_t old_size, new_size; +fmode_t new_map_mode;/* Suspend if it isn't already suspended */ if (param->flags &DM_SKIP_LOCKFS_FLAG )
@@ -1193,6 +1194,7 @@ static int do_resume(struct dm_ioctl *param) } } + new_map_mode =dm_table_get_mode (new_map); old_size =dm_get_size (md); old_map =dm_swap_table (md, new_map);if (IS_ERR (old_map)) {
@@ -1205,7 +1207,7 @@ static int do_resume(struct dm_ioctl *param)if (old_size && new_size && old_size != new_size) need_resize_uevent = true; -if (dm_table_get_mode (new_map) &FMODE_WRITE ) +if (new_map_mode &FMODE_WRITE )set_disk_ro (dm_disk (md),0 );else set_disk_ro (dm_disk (md),1 );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 531/1887 pmdomain: rockchip: fix clock leak on domain probe failure
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Shawn Lin <[email protected]>
[ Upstream commit d8b3847770661b7109146e6479eeadcdab9cecf0 ]
When of_clk_get() failed in the middle of the loop, the references of the clocks acquired so far were leaked instead of being released by the error path.
--- drivers/soc/rockchip/pm_domains.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/soc/rockchip/pm_domains.c +1 -1
@@ -589,7 +589,7 @@ static int rockchip_pm_add_one_domain(struct rockchip_pmu *pmu,dev_err (pmu->dev,"%pOFn: failed to get clk at index %d: %d\n" , node, i, error); -return error; + goto err_put_clocks; } }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 532/1887 pmdomain: imx8m-blk-ctrl: Serialize power on/off across sibling domains
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ming Qian <[email protected]>
[ Upstream commit 51c2db3ef0cb89a49ab7f47a40efd5d5b074ce8d ]
On i.MX8MP the VPU blk-ctrl exposes G1, G2 and VC8000E as three separate genpds, each serialized only by its own genpd lock, so their power_on and power_off callbacks can run concurrently on SMP.
The sequences are not independent: they share the VPUMIX bus domain, the VPU_NOC and the ADB400 handshake. On power up the GPC cannot ack-verify the ADB400 handshake - the ack only completes once blk-ctrl sets the bus clk-en bit - so it just waits a fixed delay instead of polling hskack. A sibling transition landing inside another domain's reset window disturbs that shared clock and handshake timing, the victim's reset does not take effect, and its block registers read back all zeros: the decoder times out or the encoder fails its format check.
Serialize the blk-ctrl reset sequence with a per-blk-ctrl mutex; the driver deliberately avoids a genpd hierarchy, so the genpd core gives no cross-sibling serialization.
--- drivers/soc/imx/imx8m-blk-ctrl.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
drivers/soc/imx/imx8m-blk-ctrl.c +15 -0
@@ -13,6 +13,7 @@#include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/clk.h> +#include <linux/mutex.h> #include <dt-bindings/power/imx8mm-power.h> #include <dt-bindings/power/imx8mn-power.h>
@@ -32,6 +33,12 @@ struct imx8m_blk_ctrl {struct regmap *regmap;struct imx8m_blk_ctrl_domain *domains;struct genpd_onecell_data onecell_data; +/* +* Serializes the blk-ctrl reset/clock sequence across sibling domains; +* their transitions interact through the shared VPUMIX bus domain, +* VPU_NOC and the not-ack-verified ADB400 handshake (ERR050531). +*/ +struct mutex power_lock; };struct imx8m_blk_ctrl_domain_data {
@@ -87,6 +94,8 @@ static int imx8m_blk_ctrl_power_on(struct generic_pm_domain *genpd)struct imx8m_blk_ctrl *bc = domain->bc;int ret; +guard (mutex)(&bc->power_lock); +/* make sure bus domain is awake */ ret =pm_runtime_get_sync (bc->bus_power_dev);if (ret <0 ) {
@@ -146,6 +155,8 @@ static int imx8m_blk_ctrl_power_off(struct generic_pm_domain *genpd)const struct imx8m_blk_ctrl_domain_data *data = domain->data;struct imx8m_blk_ctrl *bc = domain->bc; +guard (mutex)(&bc->power_lock); +/* put devices into reset and disable clocks */ if (data->mipi_phy_rst_mask)regmap_clear_bits (bc->regmap,BLK_MIPI_RESET_DIV , data->mipi_phy_rst_mask);
@@ -184,6 +195,10 @@ static int imx8m_blk_ctrl_probe(struct platform_device *pdev) bc->dev = dev; + ret =devm_mutex_init (dev, &bc->power_lock); +if (ret) +return ret; + bc_data =of_device_get_match_data (dev); base =devm_platform_ioremap_resource (pdev,0 );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 533/1887 pmdomain: rockchip: dont ignore clock lookup errors on attach
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Shawn Lin <[email protected]>
[ Upstream commit 4c66c423593e26273ea0d644906ed007630c6f6d ]
rockchip_pd_attach_dev() treated every of_clk_get() error as the end of the clock list, so a clock that exists but is not available yet (e.g. -EPROBE_DEFER) was silently skipped and the device ended up without pm_clk management. Only -ENOENT means there are no more clocks.
--- drivers/soc/rockchip/pm_domains.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-)
drivers/soc/rockchip/pm_domains.c +13 -1
@@ -507,7 +507,19 @@ static int rockchip_pd_attach_dev(struct generic_pm_domain *genpd, } i =0 ; -while ((clk =of_clk_get (dev->of_node, i++)) && !IS_ERR (clk)) { +while (1 ) { + clk =of_clk_get (dev->of_node, i++); +if (IS_ERR (clk)) { + error =PTR_ERR (clk); +if (error == -ENOENT ) +break ; + +dev_err (dev,"failed to get clock %d: %d\n" , i -1 , + error); +pm_clk_destroy (dev); +return error; + } +dev_dbg (dev,"adding clock '%pC' to list of PM clocks\n" , clk); error =pm_clk_add_clk (dev, clk);if (error) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 534/1887 pmdomain: rockchip: propagate subdomain add errors
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Shawn Lin <[email protected]>
[ Upstream commit 4c0d69d677cf5b0065ebbb3ceb8ba66fddbec630 ]
rockchip_pm_add_subdomain() ignored the return value of its recursive call, so a failure deeper in the hierarchy was silently dropped and probe still succeeded with an incomplete domain tree.
--- drivers/soc/rockchip/pm_domains.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/soc/rockchip/pm_domains.c +3 -1
@@ -767,7 +767,9 @@ static int rockchip_pm_add_subdomain(struct rockchip_pmu *pmu, parent_domain->name, child_domain->name); } -rockchip_pm_add_subdomain (pmu, np); + error =rockchip_pm_add_subdomain (pmu, np); +if (error) + goto err_out; }return 0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 535/1887 optee: register TEE devices only once fully initialized
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Shao-Fu Chen <[email protected]>
[ Upstream commit c108feca0a096f8db25d1b69130035025e9e936d ]
optee_probe() called tee_device_register() on both the client and the supplicant device before the rest of struct optee had been initialized.
tee_device_register() calls cdev_device_add(), which does two things at
once: it creates /dev/tee0 and /dev/teepriv0, and it links the device into the tee class so that class_find_device() can find it. From that moment on the device is reachable both from user space via tee_open() and from kernel space via tee_client_open_context(). The only gate in teedev_open() is tee_device_get(), which merely checks that teedev->desc is non-NULL, which was already set by tee_device_alloc(). Therefore, there is effectively no gate at all.
A context opened in that window can run against a struct optee where
- optee->call_queue.mutex is not initialized by optee_cq_init()
- optee->supp mutex and completions is not initialized by optee_supp_init()
- optee->rpmb_dev_mutex is not initialized yet,
- the message argument cache is not initialized by optee_shm_arg_cache_init()
- optee->ctx is still NULL
Any open session or invoke during this window takes uninitialized mutexes and dereferences a NULL pointer.
Fix it by moving both tee_device_register() calls down to the point where all of struct optee is set up.
--- drivers/tee/optee/ffa_abi.c | 16 ++++++++-------- drivers/tee/optee/smc_abi.c | 17 ++++++++--------- 2 files changed, 16 insertions(+), 17 deletions(-)
drivers/tee/optee/ffa_abi.c +8 -8
@@ -844,14 +844,6 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev) } optee->supp_teedev = teedev; - rc =tee_device_register (optee->teedev); -if (rc) - goto err_unreg_supp_teedev; - - rc =tee_device_register (optee->supp_teedev); -if (rc) - goto err_unreg_supp_teedev; - rc =rhashtable_init (&optee->ffa.global_ids, &shm_rhash_params);if (rc) goto err_unreg_supp_teedev;
@@ -871,6 +863,14 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)if (rc) goto err_close_ctx; + rc =tee_device_register (optee->teedev); +if (rc) + goto err_unregister_devices; + + rc =tee_device_register (optee->supp_teedev); +if (rc) + goto err_unregister_devices; + rc =optee_enumerate_devices (PTA_CMD_GET_DEVICES );if (rc) goto err_unregister_devices;
drivers/tee/optee/smc_abi.c +8 -9
@@ -1467,14 +1467,6 @@ static int optee_probe(struct platform_device *pdev) } optee->supp_teedev = teedev; - rc =tee_device_register (optee->teedev); -if (rc) - goto err_unreg_supp_teedev; - - rc =tee_device_register (optee->supp_teedev); -if (rc) - goto err_unreg_supp_teedev; -mutex_init (&optee->call_queue.mutex);INIT_LIST_HEAD (&optee->call_queue.waiters);optee_supp_init (&optee->supp);
@@ -1531,6 +1523,14 @@ static int optee_probe(struct platform_device *pdev)if (optee->smc.sec_caps &OPTEE_SMC_SEC_CAP_DYNAMIC_SHM )pr_info ("dynamic shared memory is enabled\n" ); + rc =tee_device_register (optee->teedev); +if (rc) + goto err_disable_shm_cache; + + rc =tee_device_register (optee->supp_teedev); +if (rc) + goto err_disable_shm_cache; + rc =optee_enumerate_devices (PTA_CMD_GET_DEVICES );if (rc) goto err_disable_shm_cache;
@@ -1551,7 +1551,6 @@ static int optee_probe(struct platform_device *pdev)optee_shm_arg_cache_uninit (optee);optee_supp_uninit (&optee->supp);mutex_destroy (&optee->call_queue.mutex); -err_unreg_supp_teedev:tee_device_unregister (optee->supp_teedev); err_unreg_teedev:tee_device_unregister (optee->teedev);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 536/1887 scsi: ch: Do not keep references to data transfer element devices
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ahmed Abdelhaleem Ahmed <[email protected]>
[ Upstream commit 27c0f718d3b3c99e6d315308e95d987d0382a8a8 ]
ch_readconfig() looks up the scsi_device of every data transfer element whose SCSI id the changer reports in READ ELEMENT STATUS, and stores it in ch->dt[]. scsi_device_lookup() takes a reference, and nothing ever drops
it: ch_destroy() frees the array with kfree(). ch->dt[] is read nowhere else - it only supplies the vendor, model and revision printed in the same loop.
Once such a drive is removed, its scsi_device can never be released. It stays on the host's device list at its address, so a new device there is refused by anything that walks the list - target_core_pscsi reports "scsi_device_get() failed for H:C:T:L" - and the low-level driver's module can no longer be unloaded. Only a reboot recovers.
It shows with any changer that reports its drives' ids; with the mhvtl virtual library (IBM 3573-TL personality), each create and remove of a library with four drives leaves four references behind, counted by the module's use count in lsmod. With ch not bound the count is unchanged, and with this patch applied it is unchanged too.
Drop the reference as soon as the name has been printed, and remove the now unused dt[] array. That also removes the array leaked when ch_probe() fails after ch_readconfig().
The same leak was reported with an RFC patch in 2022, which was not merged.
--- drivers/scsi/ch.c | 30 +++++++++--------------------- 1 file changed, 9 insertions(+), 21 deletions(-)
drivers/scsi/ch.c +9 -21
@@ -110,7 +110,6 @@ typedef struct {int minor;char name[8 ];struct scsi_device *device; -struct scsi_device **dt;/* ptrs to data transfer elements */ u_int firsts[CH_TYPES ];u_int counts[CH_TYPES ];u_int unit_attention;
@@ -357,16 +356,10 @@ ch_readconfig(scsi_changer *ch) vendor_labels[i]); } -/* look up the devices of the data transfer elements */ - ch->dt =kcalloc (ch->counts[CHET_DT ],sizeof (*ch->dt), -GFP_KERNEL ); - -if (!ch->dt) { -kfree (buffer); -return -ENOMEM ; - } - +/* report the devices of the data transfer elements */ for (elem =0 ; elem < ch->counts[CHET_DT ]; elem++) { +struct scsi_device *sdev; + id =-1 ; lun =0 ;if (elem <CH_DT_MAX &&-1 != dt_id[elem]) {
@@ -382,10 +375,8 @@ ch_readconfig(scsi_changer *ch)VPRINTK (KERN_INFO ,"dt 0x%x: " ,elem+ch->firsts[CHET_DT ]);if (data[6 ] &0x80 ) {VPRINTK (KERN_CONT ,"not this SCSI bus\n" ); - ch->dt[elem] =NULL ; }else if (0 == (data[6 ] &0x30 )) {VPRINTK (KERN_CONT ,"ID/LUN unknown\n" ); - ch->dt[elem] =NULL ; }else { id = ch->device->id; lun =0 ;
@@ -395,18 +386,16 @@ ch_readconfig(scsi_changer *ch) }if (-1 != id) {VPRINTK (KERN_CONT ,"ID %i, LUN %i, " ,id,lun); - ch->dt[elem] = -scsi_device_lookup (ch->device->host, - ch->device->channel, - id,lun); -if (!ch->dt[elem]) { + sdev =scsi_device_lookup (ch->device->host, + ch->device->channel, + id, lun); +if (!sdev) {/* should not happen */ VPRINTK (KERN_CONT ,"Huh? device not found!\n" ); }else {VPRINTK (KERN_CONT ,"name: %8.8s %16.16s %4.4s\n" , - ch->dt[elem]->vendor, - ch->dt[elem]->model, - ch->dt[elem]->rev); + sdev->vendor, sdev->model, sdev->rev); +scsi_device_put (sdev); } } }
@@ -570,7 +559,6 @@ static void ch_destroy(struct kref *ref)scsi_changer *ch =container_of (ref, scsi_changer, ref); ch->device =NULL ; -kfree (ch->dt);kfree (ch); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 537/1887 drm/vc4: Fix firmware reference leak in vc4_drm_bind()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Wentao Liang <[email protected]>
[ Upstream commit b816da5d5df1461446a0a5f3fe2b4948a659a790 ]
The firmware reference obtained via rpi_firmware_get() is leaked when aperture_remove_all_conflicting_devices() fails, as the error label returns without putting it. This happens before the firmware is consumed and put further down in the function.
Put the firmware reference before jumping to err.
--- drivers/gpu/drm/vc4/vc4_drv.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
drivers/gpu/drm/vc4/vc4_drv.c +4 -1
@@ -352,8 +352,11 @@ static int vc4_drm_bind(struct device *dev) } ret =drm_aperture_remove_framebuffers (driver); -if (ret) +if (ret) { +if (firmware) +rpi_firmware_put (firmware);return ret; + }if (firmware) { ret = rpi_firmware_property(firmware,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 538/1887 drm/amdgpu: copy debugfs register data outside the GRBM/SRBM locks
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mikhail Gavrilov <[email protected]>
[ Upstream commit 16c64495859f13f75602f439588883f2a4a4d6a6 ]
Reading or writing a register through the amdgpu_regs2 debugfs file with GRBM bank selection, which umr does for every banked access (a single "umr -b x x x -r <reg>" is enough, as is the wave dump with "-O halt_waves"), makes lockdep report a circular locking dependency, after which lockdep stays off until reboot:
WARNING: possible circular locking dependency detected
umr/63306 is trying to acquire lock:
ffff8882cf4b8350 (&mm->mmap_lock){++++}-{4:4}, at: __might_fault+0x9b/0x140
but task is already holding lock:
ffff8881aec010d0 (&amdgpu_grbm_lock_key){+.+.}-{4:4}, at: amdgpu_debugfs_regs2_op.isra.0+0x20d/0xd00 [amdgpu]
...
Chain exists of:
&mm->mmap_lock --> &amdgpu_srbm_lock_key --> &amdgpu_grbm_lock_key
...
__might_fault+0xc5/0x140
amdgpu_debugfs_regs2_op.isra.0+0x441/0xd00 [amdgpu]
full_proxy_write+0x10e/0x1d0
vfs_write+0x1d0/0xf50
ksys_write+0x116/0x250amdgpu_debugfs_regs2_op() and amdgpu_debugfs_process_reg_op() call get_user()/put_user() for every dword while holding grbm_idx_mutex or srbm_mutex, and pm.mutex if requested, so a fault there takes mmap_lock inside those locks. The lock hierarchy in amdgpu_lockdep.c puts srbm_mutex and grbm_idx_mutex below notifier_lock, which amdgpu_cs_submit() takes with the BO reservations held, and dma_resv_lockdep() orders the reservations inside mmap_lock. So mmap_lock is outside grbm_idx_mutex and srbm_mutex, and these two debugfs paths nest it the other way round. The copies under the locks are as old as the bank selection in both files; commit 1d0f5838b126 ("drm/amdgpu: Add lockdep annotations for lock ordering validation") only made them visible to lockdep.
Copy the user buffer in before any of the locks is taken and the result out after all of them are dropped, as amdgpu_debugfs_wave_read(), amdgpu_debugfs_gpr_read() and amdgpu_debugfs_gprwave_read() already do. The buffer is sized by the count passed to read() or write(), so an access larger than PAGE_SIZE is now rejected with -EINVAL; umr reads and writes one register at a time. A fault on the user buffer of a write now fails with -EFAULT before any register is written, instead of after a partial write.
--- drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c | 76 ++++++++++++--------- 1 file changed, 45 insertions(+), 31 deletions(-)
drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c +45 -31
@@ -114,6 +114,20 @@ static int amdgpu_debugfs_process_reg_op(bool read, struct file *f, *pos &= (1UL <<22 ) -1 ; +if (size >PAGE_SIZE ) +return -EINVAL ; + +/* +* Access the user buffer only while none of the locks below are held: +* a fault takes mmap_lock, and grbm_idx_mutex and srbm_mutex nest +* inside it. +*/ + u32 *data __free(kfree) = kmalloc(size, GFP_KERNEL); +if (!data) +return -ENOMEM ; +if (!read &©_from_user (data, buf, size)) +return -EFAULT ; + r =pm_runtime_get_sync (adev_to_drm (adev)->dev);if (r <0 ) {pm_runtime_put_autosuspend (adev_to_drm (adev)->dev);
@@ -146,28 +160,16 @@ static int amdgpu_debugfs_process_reg_op(bool read, struct file *f,mutex_lock (&adev->pm.mutex);while (size) { -uint32_t value; - -if (read) { - value =RREG32 (*pos >>2 ); - r =put_user (value, (uint32_t *)buf); - }else { - r =get_user (value, (uint32_t *)buf); -if (!r) -amdgpu_mm_wreg_mmio_rlc (adev, *pos >>2 , value); - } -if (r) { - result = r; - goto end; - } +if (read) + data[result >>2 ] =RREG32 (*pos >>2 ); +else +amdgpu_mm_wreg_mmio_rlc (adev, *pos >>2 , data[result >>2 ]); result +=4 ; - buf +=4 ; *pos +=4 ; size -=4 ; } -end:if (use_bank) {amdgpu_gfx_select_se_sh (adev,0xffffffff ,0xffffffff ,0xffffffff );mutex_unlock (&adev->grbm_idx_mutex);
@@ -183,6 +185,10 @@ static int amdgpu_debugfs_process_reg_op(bool read, struct file *f,pm_runtime_put_autosuspend (adev_to_drm (adev)->dev);amdgpu_virt_disable_access_debugfs (adev); + +if (read &©_to_user (buf, data, result)) +return -EFAULT ; +return result; }
@@ -232,11 +238,24 @@ static ssize_t amdgpu_debugfs_regs2_op(struct file *f, char __user *buf, u32 offstruct amdgpu_device *adev = rd->adev;ssize_t result =0 ;int r; -uint32_t value;if (size &0x3 || offset &0x3 )return -EINVAL ; +if (size >PAGE_SIZE ) +return -EINVAL ; + +/* +* Access the user buffer only while none of the locks below are held: +* a fault takes mmap_lock, and grbm_idx_mutex and srbm_mutex nest +* inside it. +*/ + u32 *data __free(kfree) = kmalloc(size, GFP_KERNEL); +if (!data) +return -ENOMEM ; +if (write_en &©_from_user (data, buf, size)) +return -EFAULT ; + r =pm_runtime_get_sync (adev_to_drm (adev)->dev);if (r <0 ) {pm_runtime_put_autosuspend (adev_to_drm (adev)->dev);
@@ -276,24 +295,15 @@ static ssize_t amdgpu_debugfs_regs2_op(struct file *f, char __user *buf, u32 offmutex_lock (&adev->pm.mutex);while (size) { -if (!write_en) { - value =RREG32 (offset >>2 ); - r =put_user (value, (uint32_t *)buf); - }else { - r =get_user (value, (uint32_t *)buf); -if (!r) -amdgpu_mm_wreg_mmio_rlc (adev, offset >>2 , value); - } -if (r) { - result = r; - goto end; - } +if (!write_en) + data[result >>2 ] =RREG32 (offset >>2 ); +else +amdgpu_mm_wreg_mmio_rlc (adev, offset >>2 , data[result >>2 ]); offset +=4 ; size -=4 ; result +=4 ; - buf +=4 ; } -end: +if (rd->id.use_grbm) {amdgpu_gfx_select_se_sh (adev,0xffffffff ,0xffffffff ,0xffffffff );mutex_unlock (&adev->grbm_idx_mutex);
@@ -313,6 +323,10 @@ static ssize_t amdgpu_debugfs_regs2_op(struct file *f, char __user *buf, u32 offpm_runtime_put_autosuspend (adev_to_drm (adev)->dev);amdgpu_virt_disable_access_debugfs (adev); + +if (!write_en &©_to_user (buf, data, result)) +return -EFAULT ; +return result; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 539/1887 drm/fb-helper: defer setup when fbdev_probe() fails, not just on -EAGAIN
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nguyen Ngoc Thang <[email protected]>
[ Upstream commit 755c94d178b7e972bb766199d34e4ba4f63a89eb ]
__drm_fb_helper_initial_config_and_unlock() calls drm_client_modeset_probe() first, which already fills in each mode_set->mode/num_connectors for every connected output, and only afterwards calls drm_fb_helper_single_fb_probe() to create fb_helper->fb and wire it into those mode_sets via drm_setup_crtcs_fb().
If drm_fb_helper_single_fb_probe() fails with anything other than -EAGAIN (e.g. dev->driver->fbdev_probe() itself fails), the function bails out before drm_setup_crtcs_fb() runs and before setting fb_helper->deferred_setup. The client is left with mode_sets that have a mode and connectors but mode_set->fb == NULL, and nothing marks this fb_helper as needing a retry.
__drm_fb_helper_restore_fbdev_mode_unlocked() only skips committing when fb_helper->deferred_setup is set, so a later .restore() call (drm_lastclose() on process exit, in this report) commits the stale mode_set through drm_client_modeset_commit() -> drm_client_modeset_commit_atomic() -> __drm_atomic_helper_set_config(), which hits WARN_ON(!set->fb).
Mark fb_helper->deferred_setup on any drm_fb_helper_single_fb_probe() failure, not just -EAGAIN, so restore keeps skipping the commit until a later hotplug event successfully re-probes and re-populates mode_set->fb. The real error code is still returned to the caller unchanged.
--- drivers/gpu/drm/drm_fb_helper.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
drivers/gpu/drm/drm_fb_helper.c +4 -4
@@ -1942,11 +1942,11 @@ __drm_fb_helper_initial_config_and_unlock(struct drm_fb_helper *fb_helper,drm_client_modeset_probe (&fb_helper->client, width, height); ret =drm_fb_helper_single_fb_probe (fb_helper, bpp_sel);if (ret <0 ) { -if (ret == -EAGAIN ) { - fb_helper->preferred_bpp = bpp_sel; - fb_helper->deferred_setup = true; + fb_helper->preferred_bpp = bpp_sel; +/* modesets are probed but fb_helper->fb isn't; defer restore too */ + fb_helper->deferred_setup = true; +if (ret == -EAGAIN ) ret =0 ; - }mutex_unlock (&fb_helper->lock);return ret;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 540/1887 namespace: unhash a dentry before detaching the mounts on it
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
[ Upstream commit 528b383dc35d1e5caf64ddf5d35f983ae543b0d0 ]
unlink(), rmdir() and rename() remove the entry from the filesystem, call detach_mounts() on the entry with the inode locked and then call d_delete() once the inode is unlocked.
The thing is that between detach_mounts() and d_delete() the dentry is still hashed and positive. But after detach_mounts() nothing covers the dentry anymore. A lookup that finds this dentry in the dcache can uncover what the mounts hid.
That's a problem when unlinking files or directories that are mountpoints in other mount namespaces. Everybody who had the underlying entry covered can race the detach_mount() call until d_delete() has run.
The race window isn't all that small. It encompasses namespace_unlock() with a full synchronize_rcu_expedited() grace period and the inode_unlock() of the entry.
In my experiments three walkers that kept trying read an overmounted file in 11938 of the 16140 unlinks that removed its mountpoint from a bind mount of the filesystem within a minute.
Fun fact, d_invalidate() has the same ordering problem but gets it right. It unhashes the dentry first and detaches the mounts afterwards. Let's do the same in __detach_mounts():
- A lookup that hasn't found the dentry yet misses it in the dcache and waits for the directory lock that the caller holds until the name is gone for good.
- A lockless lookup that found it already fails the mount_lock check while the dentry still counts as a mountpoint and the d_seq check once it doesn't, and retries.
This fixes the lockless path. We still need to fix the reference count lookup in a follow-up patch.
So d_drop() the dentry. The dentry stays positive and held but can't be found anymore. Then proceed with the detach and unlink.
Reproducer:
The reads were counted with the program below, three walkers for 60 s in a VM with 4 CPUs. The race window is stretched with the debug patch pasted here. fs.detach_race_walk_us sleeps in lookup_fast() once it found a mountpoint dentry and before its mounts are crossed. fs.detach_race_unlink_us sleeps in vfs_unlink() between detach_mounts() and d_delete().
// SPDX-License-Identifier: GPL-2.0 /* * unlink_covered: unlink a file that is a mountpoint in a detached copy of * its mount, against lookups that open it through that copy. * * A is a tmpfs with the file f1 (content SECRET_F) and the plain file * MARK_A. A' is an open_tree(OPEN_TREE_CLONE) copy of A with MARK_A bound * on A'/f1, held through an O_PATH fd on its root once the tree fd is * closed. Walkers open f1 through that fd while the driver unlinks A/f1, * where nothing is mounted on it. A walker may read MARK_A or get ENOENT. * A read of SECRET_F is a hit: the name was found after its mount was * gone. The lockless walks use openat2(RESOLVE_CACHED). * * usage: unlink_covered [-t seconds] [-w walkers] */ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include <errno.h> #include <fcntl.h> #include <pthread.h> #include <stdatomic.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/mount.h> #include <sys/stat.h> #include <sys/syscall.h> #include <linux/openat2.h> #ifndef __NR_open_tree #define __NR_open_tree 428 #endif #ifndef __NR_move_mount #define __NR_move_mount 429 #endif #ifndef __NR_openat2 #define __NR_openat2 437 #endif #ifndef OPEN_TREE_CLONE #define OPEN_TREE_CLONE 1 #endif #ifndef OPEN_TREE_CLOEXEC #define OPEN_TREE_CLOEXEC O_CLOEXEC #endif #ifndef MOVE_MOUNT_F_EMPTY_PATH #define MOVE_MOUNT_F_EMPTY_PATH 0x00000004 #endif #define WORK "/tmp/uc" #define ADIR WORK "/A"
static int duration = 60, nwalkers = 3;
static atomic_int stop, writer_waiting;
static pthread_rwlock_t cur_lock = PTHREAD_RWLOCK_INITIALIZER;
static int cur_fd = -1; /* the root of A', -1 while there is none */
static atomic_long n_unlink, n_walk, n_mark, n_enoent, n_other, n_secret,
n_secret_cached;
static void die(const char *what)
{
fprintf(stderr, "FATAL %s: %s\n", what, strerror(errno));
exit(2);
} static void put_file(int dfd, const char *name, const char *content) {
int fd = openat(dfd, name, O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC,
0644);
if (fd < 0 || write(fd, content, strlen(content)) < 0)
die(name);
close(fd);
}
/* "plain/../" @n times, then f1: a longer walk that checks nothing on its way */
static char *longpath(int n)
{
char *p = malloc(n * 9 + 3), *q = p;
for (int i = 0; i < n; i++, q += 9)
memcpy(q, "plain/../", 9);
strcpy(q, "f1");
return p;
}
static void try_read(int dfd, const char *path, int cached)
{
struct open_how how = { .flags = O_RDONLY | O_CLOEXEC,
.resolve = RESOLVE_CACHED };
char buf[32] = "";
long n;
int fd;
if (cached)
fd = syscall(__NR_openat2, dfd, path, &how, sizeof(how));
else
fd = openat(dfd, path, O_RDONLY | O_CLOEXEC);
atomic_fetch_add(&n_walk, 1);
if (fd < 0) {
if (errno == ENOENT)
atomic_fetch_add(&n_enoent, 1);
else if (!cached || errno != EAGAIN)
atomic_fetch_add(&n_other, 1);
return;
}
n = read(fd, buf, sizeof(buf) - 1);
close(fd);
if (n >= 6 && !strncmp(buf, "SECRET", 6)) {
if (cached)
atomic_fetch_add(&n_secret_cached, 1);
if (!atomic_fetch_add(&n_secret, 1))
printf("HIT: read \"%s\" through %s (%s walk)\n", buf,
path, cached ? "lockless" : "any");
} else {
atomic_fetch_add(&n_mark, 1);
}
}
static void *walker(void *arg)
{
unsigned int r = (long)arg * 2654435761u;
while (!atomic_load(&stop)) {
char *p_long, *p_short;
int dfd;
while (atomic_load(&writer_waiting) && !atomic_load(&stop))
usleep(20);
pthread_rwlock_rdlock(&cur_lock);
dfd = cur_fd;
if (dfd < 0) {
pthread_rwlock_unlock(&cur_lock);
usleep(100);
continue;
}
r = r * 1103515245u + 12345u;
p_long = longpath(1 + (r >> 8) % 400);
p_short = longpath(0);
try_read(dfd, p_long, 0);
try_read(dfd, p_short, 0);
try_read(dfd, p_long, 1);
pthread_rwlock_unlock(&cur_lock);
free(p_long);
free(p_short);
}
return NULL;
} /* hand the walkers a new A' (or none), close the old one */ static void publish(int fd)
{
int old;
atomic_fetch_add(&writer_waiting, 1);
pthread_rwlock_wrlock(&cur_lock);
old = cur_fd;
cur_fd = fd;
pthread_rwlock_unlock(&cur_lock);
atomic_fetch_sub(&writer_waiting, 1);
if (old >= 0)
close(old);
}
static void *driver(void *arg __attribute__((unused)))
{
int a;
if (mkdir(ADIR, 0755) && errno != EEXIST)
die("mkdir A");
if (mount("A", ADIR, "tmpfs", 0, "size=4M"))
die("mount A");
a = open(ADIR, O_PATH | O_DIRECTORY | O_CLOEXEC);
if (a < 0 || mkdirat(a, "plain", 0755))
die("A/plain");
put_file(a, "MARK_A", "MARK_A");
while (!atomic_load(&stop)) {
int t, m, fd_a;
put_file(a, "f1", "SECRET_F");
t = syscall(__NR_open_tree, a, "",
OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC | AT_EMPTY_PATH);
if (t < 0)
die("open_tree A");
m = syscall(__NR_open_tree, a, "MARK_A",
OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC);
if (m < 0)
die("open_tree MARK_A");
if (syscall(__NR_move_mount, m, "", t, "f1",
MOVE_MOUNT_F_EMPTY_PATH))
die("move_mount");
close(m);
fd_a = openat(t, ".", O_PATH | O_DIRECTORY | O_CLOEXEC);
if (fd_a < 0)
die("open A'");
publish(fd_a);
usleep(200 + rand() % 1000);
close(t); /* A' is unmounted, fd_a holds it */
usleep(200 + rand() % 1000);
if (unlinkat(a, "f1", 0)) /* through A, a plain file there */
die("unlink f1");
atomic_fetch_add(&n_unlink, 1);
usleep(rand() % 300);
publish(-1);
}
close(a);
umount2(ADIR, MNT_DETACH);
return NULL;
}
int main(int argc, char **argv)
{
pthread_t d, *w;
int c, i;
setvbuf(stdout, NULL, _IOLBF, 0);
while ((c = getopt(argc, argv, "t:w:")) != -1) {
switch (c) {
case 't':
duration = atoi(optarg);
break;
case 'w':
nwalkers = atoi(optarg);
break;
default:
fprintf(stderr, "usage: unlink_covered [-t seconds] [-w walkers]\n");
return 2;
}
}
if (unshare(CLONE_NEWNS) ||
mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL))
die("unshare");
if (mkdir(WORK, 0755) && errno != EEXIST)
die("mkdir");
w = calloc(nwalkers, sizeof(*w));
for (i = 0; i < nwalkers; i++)
if (pthread_create(&w[i], NULL, walker, (void *)(long)i))
die("pthread_create");
if (pthread_create(&d, NULL, driver, NULL))
die("pthread_create");
sleep(duration);
atomic_store(&stop, 1);
pthread_join(d, NULL);
for (i = 0; i < nwalkers; i++)
pthread_join(w[i], NULL);
printf("unlink_covered: %d s, %d walkers: unlinks %ld walks %ld mark %ld enoent %ld other %ld SECRET %ld (lockless %ld)\n",
duration, nwalkers, atomic_load(&n_unlink), atomic_load(&n_walk),
atomic_load(&n_mark), atomic_load(&n_enoent),
atomic_load(&n_other), atomic_load(&n_secret),
atomic_load(&n_secret_cached));
return atomic_load(&n_secret) ? 1 : 0;
}# diff --git a/fs/namei.c b/fs/namei.c
# --- a/fs/namei.c
# +++ b/fs/namei.c
# @@ -35,6 +35,7 @@
# #include <linux/fcntl.h>
# #include <linux/device_cgroup.h>
# #include <linux/fs_struct.h>
# +#include <linux/delay.h>
# #include <linux/posix_acl.h>
# #include <linux/hash.h>
# #include <linux/bitops.h>
# @@ -1205,9 +1206,26 @@ static int sysctl_protected_symlinks __read_mostly;
# static int sysctl_protected_hardlinks __read_mostly;
# static int sysctl_protected_fifos __read_mostly;
# static int sysctl_protected_regular __read_mostly;
# +/* debug: widen the two windows of the detach_mounts() race */
# +static int sysctl_detach_race_walk_us __read_mostly;
# +static int sysctl_detach_race_unlink_us __read_mostly;
#
# #ifdef CONFIG_SYSCTL
# static const struct ctl_table namei_sysctls[] = {
# + {
# + .procname = "detach_race_walk_us",
# + .data = &sysctl_detach_race_walk_us,
# + .maxlen = sizeof(int),
# + .mode = 0644,
# + .proc_handler = proc_dointvec,
# + },
# + {
# + .procname = "detach_race_unlink_us",
# + .data = &sysctl_detach_race_unlink_us,
# + .maxlen = sizeof(int),
# + .mode = 0644,
# + .proc_handler = proc_dointvec,
# + },
# {
# .procname = "protected_symlinks",
# .data = &sysctl_protected_symlinks,
# @@ -1878,6 +1896,10 @@ static struct dentry *lookup_fast(struct nameidata *nd)
# dentry = __d_lookup(parent, &nd->last);
# if (unlikely(!dentry))
# return NULL;
# + /* debug: between finding a mountpoint and crossing its mounts */
# + if (unlikely(sysctl_detach_race_walk_us) && d_mountpoint(dentry))
# + usleep_range(sysctl_detach_race_walk_us,
# + sysctl_detach_race_walk_us + 10);
# status = d_revalidate(nd->inode, &nd->last, dentry, nd->flags);
# }
# if (unlikely(status <= 0)) {
# @@ -5693,6 +5715,10 @@ int vfs_unlink(struct mnt_idmap *idmap, struct inode *dir,
# if (!error) {
# dont_mount(dentry);
# detach_mounts(dentry);
# + /* debug: the mounts are gone, d_delete() is still to come */
# + if (unlikely(sysctl_detach_race_unlink_us))
# + usleep_range(sysctl_detach_race_unlink_us,
# + sysctl_detach_race_unlink_us + 10);
# }
# }
# }--- fs/namespace.c | 5 +++++ 1 file changed, 5 insertions(+)
fs/namespace.c +5 -0
@@ -1737,6 +1737,9 @@ static int do_umount(struct mount *mnt, int flags)* detach_mounts allows lazily unmounting those mounts instead of * leaking them. * +* The dentry is unhashed before the mounts go so that no lookup finds +* what they covered. The caller removes it for good afterwards. +* * The caller may hold dentry->d_inode->i_mutex. */ void __detach_mounts (struct dentry *dentry)
@@ -1750,6 +1753,8 @@ void __detach_mounts(struct dentry *dentry)if (!mp) goto out_unlock; +/* the name goes first, what covered it goes second */ +d_drop (dentry); event++;while (!hlist_empty (&mp->m_list)) { mnt = hlist_entry(mp->m_list.first, struct mount, mnt_mp_list);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 541/1887 namespace: dont copy an unmounted mount tree
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
[ Upstream commit 8bacbf6be959973b28b691713a872c12eae69eec ]
may_copy_tree() accepts a mount whose root is an nsfs or pidfs file before it checks whether that mount is mounted. This exception exists for nsfs and pidfs mounts. They are part of no mount namespace so neither check_mnt() nor is_mounted() would let them through and bind-mounting a nsfsfd or a pidfd wouldn't work.
Problem is that exception also admits a bind mount of such a file when it was lazily unmounted and is only kept alive by a file descriptor:
mount --bind /proc/self/ns/net /tmp/x exec 3< /tmp/x umount -l /tmp/x mount --rbind /proc/self/fd/3 /tmp/y
With MS_REC or AT_RECURSIVE copy_tree() doesn't stop at the unmounted mount. It copies every mount that stayed attached to it as well. So if you have a bunch of overmounts over the nsfsfd or the pidfd the whole unmounted stack is copied. That's a bug.
So refuse to walk an unmounted tree. Copying its root alone stays possible. So a bind mount of a namespace that is gone can still be recreated from a file descriptor on the old one. The file descriptor pins that mount and its root and __has_locked_children() only reads the direct children which don't leave the parent's list without namespace_sem. The internal mounts carry MNT_NS_INTERNAL instead of NULL and are still walked. Copying an unmounted tree recursively fails with EINVAL.
--- fs/namespace.c | 3 +++ 1 file changed, 3 insertions(+)
fs/namespace.c +3 -0
@@ -2435,6 +2435,9 @@ static struct mount *__do_loopback(struct path *old_path, int recurse)if (!check_mnt (old) && old_path->dentry->d_op != &ns_dentry_operations)return mnt; +if (recurse && !old->mnt_ns) +return ERR_PTR (-EINVAL ); +if (!recurse &&has_locked_children (old, old_path->dentry))return mnt;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 542/1887 namespace: check the target before walking it in do_mount_setattr()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
[ Upstream commit 08f4288a37ef4b3d19b4a9599ec3048219cac828 ]
When mount_setattr() changes the propagation type to MS_SHARED do_mount_setattr() takes namespace_sem and calls invent_group_ids() which walks the whole target tree with next_mnt() and allocates a peer group id for every mount that doesn't have one.
Whether the caller may touch the target at all is only checked afterwards under mount_lock. If it may not, cleanup_group_ids() walks the tree a second time on the way out to free the ids again.
A detached tree that a file descriptor keeps alive, a mount in another mount namespace or a mount below the root of an anonymous mount namespace does a bunch of allocation work that is completely useless because it will fail and revert right after.
Do the right thing and check the target right after taking namespace_sem instead. A mount leaves its namespace only under namespace_sem so the mount stays stable. The check under mount_lock stays for the case where no propagation change is requested and namespace_sem isn't taken. Callers get the same EINVAL as before.
--- fs/namespace.c | 8 ++++++++ 1 file changed, 8 insertions(+)
fs/namespace.c +8 -0
@@ -4224,6 +4224,13 @@ static int do_mount_setattr(struct path *path, struct mount_kattr *kattr)* propagation. */ namespace_lock (); +/* invent_group_ids() walks the tree, only walk a mounted one */ +if (!is_mounted (&mnt->mnt) || + ((mnt_has_parent (mnt) || !is_anon_ns (mnt->mnt_ns)) && + !check_mnt (mnt))) { +namespace_unlock (); +return -EINVAL ; + }if (kattr->propagation ==MS_SHARED ) { err =invent_group_ids (mnt, kattr->recurse);if (err) {
@@ -4236,6 +4243,7 @@ static int do_mount_setattr(struct path *path, struct mount_kattr *kattr) err = -EINVAL ;lock_mount_hash (); +/* Checked under namespace_sem already if the propagation changes. */ /* Ensure that this isn't anything purely vfs internal. */ if (!is_mounted (&mnt->mnt)) goto out;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 543/1887 fcntl: refuse F_SET_RW_HINT on an immutable inode
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
[ Upstream commit b0d1022b9bdb1e98aa7fb3244fdd50a58c61766c ]
An immutable inode is never written to so write hints are pointless. Refuse the hint on an immutable inode the way setattr(), fallocate() and etxattr() refuse their changes.
--- fs/fcntl.c | 3 +++ 1 file changed, 3 insertions(+)
fs/fcntl.c +3 -0
@@ -290,6 +290,9 @@ static long fcntl_rw_hint(struct file *file, unsigned int cmd,return -EFAULT ;return 0 ;case F_SET_RW_HINT : +/* nothing is ever written to it */ +if (IS_IMMUTABLE (inode)) +return -EPERM ;if (copy_from_user (&hint, argp,sizeof (hint)))return -EFAULT ;if (!rw_hint_valid (hint))
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 544/1887 namespace: handle mount locking for automounts correctly
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
[ Upstream commit 33aadc7c72055fca93cb5a3691d836ebf8b31c9b ]
If mounts are propagated across user namespaces, attach_recursive_mnt() locks every copy of the source mount to protect overmounts from vanishing and revealing the underlying files or directories.
The user namespace is taken from the caller's mount namespaces since this is where the mounts end up. Except, that's not always true. Automounts may legitimately get popped in by tasks located in a different mount and user namespace during path lookup.
Then check doesn't make sense at that point. The copy in the namespace of the parent - which may be the host's - is now locked and the host cannot change the flags of its own mounts anymore.
Here's the reproducer:
- host has debugfs mounted nosuid,nodev,noexec and shared
- tracefs automount below it is not yet active
- hand a child process a directory descriptor on that mount
- child process enters auser and mount namespace
- child process' namespace now holds a locked copy of the host's debugfs mount that receives propagation from it
- child process stats "tracing/." through the descriptor
- lookup runs on the host's mount so the automount lands below the host's mount
- propagation puts a copy below the child's copy
- both try to clear the flags on the mount they got, with a bind remount:
host, on its own automount: MS_REMOUNT|MS_BIND = EPERM
child, on the copy in its namespace: MS_REMOUNT|MS_BIND = 0So the lock landed on the host's mount instead of the child's copy. Congrats. So we need to compare with the owner of the namespace the mount actually gets mounted on. For all regular cases that is the caller's mount namespace and so nothing changes.
Detached trees in anonymous mount namespaces by be handed over via SCM_RIGHTS or inherited in other ways on purpose so the attaching task's mount namespace is authoritative, not the creator of the detached tree.
--- fs/namespace.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-)
fs/namespace.c +17 -1
@@ -2219,14 +2219,30 @@ static int attach_recursive_mnt(struct mount *source_mnt,struct mountpoint *dest_mp, bool moving) { -struct user_namespace *user_ns = current->nsproxy->mnt_ns->user_ns;HLIST_HEAD (tree_list );struct mnt_namespace *ns = dest_mnt->mnt_ns; +struct user_namespace *user_ns = ns->user_ns;struct mountpoint *smp;struct mount *child, *p;struct hlist_node *n;int err; +/* +* A caller in an unprivileged mount namespaces may trigger an +* automount and propagate locked mounts into privileged mount +* namespaces. Take ownership from the target mount namespace. +* It's equivalent for everything but the automount case. +* +* Detached trees in anonymous mount namespaces by be handed +* over via SCM_RIGHTS or inherited in other ways on purpose +* the attaching task's mount namespace is authoritative, not +* the creator of the detached tree. +*/ +if (is_anon_ns (ns)) + user_ns = current->nsproxy->mnt_ns->user_ns; +else + user_ns = ns->user_ns; +/* Preallocate a mountpoint in case the new mounts need * to be tucked under other mounts. */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 545/1887 tty: add break_wait kernel-doc
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
[ Upstream commit 5dad87615c9861cfa366ca984b52f581e861df20 ]
Add the missing kernel-doc description for the new break_wait field in struct tty_struct.
--- include/linux/tty.h | 1 + 1 file changed, 1 insertion(+)
include/linux/tty.h +1 -0
@@ -173,6 +173,7 @@ struct tty_operations;* @write_wait: concurrent writers are waiting in this queue until they are * allowed to write * @read_wait: readers wait for data in this queue +* @break_wait: wait queue for timed breaks * @hangup_work: normally a work to perform a hangup (do_tty_hangup()); while * freeing the tty, (re)used to release_one_tty() * @disc_data: pointer to @ldisc's private data (e.g. to &struct n_tty_data)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 546/1887 xfs: xfs_ifree_cluster vs xfs_iflush_shutdown_abort deadlock
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dave Chinner <[email protected]>
[ Upstream commit 09234a632be42573d9743ac5ff6773622d233ad0 ]
Lock order of xfs_ifree_cluster() is cluster buffer -> try ILOCK -> IFLUSHING, except for the last inode in the cluster that is triggering the free. In that case, the lock order is ILOCK -> cluster buffer -> IFLUSHING.
xfs_iflush_cluster() uses cluster buffer -> try ILOCK -> IFLUSHING, so this can safely run concurrently with xfs_ifree_cluster().
xfs_inode_item_precommit() uses ILOCK -> cluster buffer, but this cannot race with xfs_ifree_cluster() so being in a different order will not trigger a deadlock.
xfs_reclaim_inode() during a filesystem shutdown uses ILOCK -> IFLUSHING -> cluster buffer via xfs_iflush_shutdown_abort(), and this deadlocks against xfs_ifree_cluster() like so:
sysrq: Show Blocked State
task:kworker/10:37 state:D stack:12560 pid:276182 tgid:276182 ppid:2 flags:0x00004000
Workqueue: xfs-inodegc/dm-3 xfs_inodegc_worker Call Trace: <TASK> __schedule+0x650/0xb10 schedule+0x6d/0xf0 schedule_timeout+0x8b/0x180 schedule_timeout_uninterruptible+0x1e/0x30 xfs_ifree+0x326/0x730 xfs_inactive_ifree+0xcb/0x230 xfs_inactive+0x2c8/0x380 xfs_inodegc_worker+0xaa/0x180 process_scheduled_works+0x1d4/0x400 worker_thread+0x234/0x2e0 kthread+0x147/0x170 ret_from_fork+0x3e/0x50 ret_from_fork_asm+0x1a/0x30 </TASK> task:fsync-tester state:D stack:12160 pid:2255943 tgid:2255943 ppid:3988702 flags:0x00004006 Call Trace: <TASK> __schedule+0x650/0xb10 schedule+0x6d/0xf0 schedule_timeout+0x31/0x180 __down_common+0xbe/0x1f0 __down+0x1d/0x30 down+0x48/0x50 xfs_buf_lock+0x3d/0xe0 xfs_iflush_shutdown_abort+0x51/0x1e0 xfs_icwalk_ag+0x386/0x690 xfs_reclaim_inodes_nr+0x114/0x160 xfs_fs_free_cached_objects+0x19/0x20 super_cache_scan+0x17b/0x1a0 do_shrink_slab+0x180/0x350 shrink_slab+0xf8/0x430 drop_slab+0x97/0xf0 drop_caches_sysctl_handler+0x59/0xc0 proc_sys_call_handler+0x189/0x280 proc_sys_write+0x13/0x20 vfs_write+0x33d/0x3f0 ksys_write+0x7c/0xf0 __x64_sys_write+0x1b/0x30 x64_sys_call+0x271d/0x2ee0 do_syscall_64+0x68/0x130 entry_SYSCALL_64_after_hwframe+0x76/0x7e
We can't change the lock order of xfs_ifree_cluster() - XFS_ISTALE and XFS_IFLUSHING are serialised through to journal IO completion by the cluster buffer lock being held.
There's quite a few asserts in the code that check that XFS_ISTALE does not occur out of sync with buffer locking (e.g. in xfs_iflush_cluster). There's also a dependency on the inode log item being removed from the buffer before XFS_IFLUSHING is cleared, also with asserts that trigger on this.
Further, we don't have a requirement for the inode to be locked when completing or aborting inode flushing because all the inode state updates are serialised by holding the cluster buffer lock across the IO to completion.
We can't check for XFS_IRECLAIM in xfs_ifree_mark_inode_stale() and skip the inode, because there is no guarantee that the inode will be reclaimed. Hence it *must* be marked XFS_ISTALE regardless of whether reclaim is preparing to free that inode. Similarly, we can't check for IFLUSHING before locking the inode because that would result in dirty inodes not being marked with ISTALE in the event of racing with XFS_IRECLAIM.
Hence we have to address this issue from the xfs_reclaim_inode() side. It is clear that we cannot hold the inode locked here when calling xfs_iflush_shutdown_abort() because it is the inode->buffer lock order that causes the deadlock against xfs_ifree_cluster().
Hence we need to drop the ILOCK before aborting the inode in the shutdown case. Once we've aborted the inode, we can grab the ILOCK again and then immediately reclaim it as it is now guaranteed to be clean.
Note that dropping the ILOCK in xfs_reclaim_inode() means that it can now be locked by xfs_ifree_mark_inode_stale() and seen whilst in this state. This is safe because we have left the XFS_IFLUSHING flag on the inode and so xfs_ifree_mark_inode_stale() will simply set XFS_ISTALE and move to the next inode. An ASSERT check in this path needs to be tweaked to take into account this new shutdown interaction.
--- fs/xfs/xfs_icache.c | 8 ++++++++ fs/xfs/xfs_inode.c | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-)
fs/xfs/xfs_icache.c +8 -0
@@ -916,7 +916,15 @@ xfs_reclaim_inode(*/ if (xlog_is_shutdown (ip->i_mount->m_log)) {xfs_iunpin_wait (ip); +/* +* Avoid a ABBA deadlock on the inode cluster buffer vs +* concurrent xfs_ifree_cluster() trying to mark the inode +* stale. We don't need the inode locked to run the flush abort +* code, but the flush abort needs to lock the cluster buffer. +*/ +xfs_iunlock (ip,XFS_ILOCK_EXCL );xfs_iflush_shutdown_abort (ip); +xfs_ilock (ip,XFS_ILOCK_EXCL ); goto reclaim; }if (xfs_ipincount (ip))
fs/xfs/xfs_inode.c +1 -1
@@ -2225,7 +2225,7 @@ xfs_ifree_mark_inode_stale( iip = ip->i_itemp;if (__xfs_iflags_test (ip,XFS_IFLUSHING )) {ASSERT (!list_empty (&iip->ili_item.li_bio_list)); -ASSERT (iip->ili_last_fields); +ASSERT (iip->ili_last_fields ||xlog_is_shutdown (mp->m_log)); goto out_iunlock; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 547/1887 xfs: catch stale AGF/AGF metadata
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dave Chinner <[email protected]>
[ Upstream commit db6a2274162de615ff74b927d38942fe3134d298 ]
There is a race condition that can trigger in dmflakey fstests that can result in asserts in xfs_ialloc_read_agi() and xfs_alloc_read_agf() firing. The asserts look like this:
XFS: Assertion failed: pag->pagf_freeblks == be32_to_cpu(agf->agf_freeblks), file: fs/xfs/libxfs/xfs_alloc.c, line: 3440
.....
Call Trace: <TASK> xfs_alloc_read_agf+0x2ad/0x3a0 xfs_alloc_fix_freelist+0x280/0x720 xfs_alloc_vextent_prepare_ag+0x42/0x120 xfs_alloc_vextent_iterate_ags+0x67/0x260 xfs_alloc_vextent_start_ag+0xe4/0x1c0 xfs_bmapi_allocate+0x6fe/0xc90 xfs_bmapi_convert_delalloc+0x338/0x560 xfs_map_blocks+0x354/0x580 iomap_writepages+0x52b/0xa70 xfs_vm_writepages+0xd7/0x100 do_writepages+0xe1/0x2c0 __writeback_single_inode+0x44/0x340 writeback_sb_inodes+0x2d0/0x570 __writeback_inodes_wb+0x9c/0xf0 wb_writeback+0x139/0x2d0 wb_workfn+0x23e/0x4c0 process_scheduled_works+0x1d4/0x400 worker_thread+0x234/0x2e0 kthread+0x147/0x170 ret_from_fork+0x3e/0x50 ret_from_fork_asm+0x1a/0x30
I've seen the AGI variant from scrub running on the filesysetm after unmount failed due to systemd interference:
XFS: Assertion failed: pag->pagi_freecount == be32_to_cpu(agi->agi_freecount) || xfs_is_shutdown(pag->pag_mount), file: fs/xfs/libxfs/xfs_ialloc.c, line: 2804
.....
Call Trace: <TASK> xfs_ialloc_read_agi+0xee/0x150 xchk_perag_drain_and_lock+0x7d/0x240 xchk_ag_init+0x34/0x90 xchk_inode_xref+0x7b/0x220 xchk_inode+0x14d/0x180 xfs_scrub_metadata+0x2e2/0x510 xfs_ioc_scrub_metadata+0x62/0xb0 xfs_file_ioctl+0x446/0xbf0 __se_sys_ioctl+0x6f/0xc0 __x64_sys_ioctl+0x1d/0x30 x64_sys_call+0x1879/0x2ee0 do_syscall_64+0x68/0x130 ? exc_page_fault+0x62/0xc0 entry_SYSCALL_64_after_hwframe+0x76/0x7e
Essentially, it is the same problem. When _flakey_drop_and_remount() loads the drop-writes table, it makes all writes silently fail. Writes are reported to the fs as completed successfully, but they are not issued to the backing store. The filesystem sees the successful write completion and marks the metadata buffer clean and removes it from the AIL.
If this happens at the same time as memory pressure is occuring, the now-clean AGF and/or AGI buffers can be reclaimed from memory.
Shortly afterwards, but before _flakey_drop_and_remount() runs unmount, background writeback is kicked and it tries to allocate blocks for the dirty pages in memory. This then tries to access the AGF buffer we just turfed out of memory. It's not found, so it gets read in from disk.
This is all fine, except for the fact that the last writeback of the AGF did not actually reach disk. The AGF on disk is stale compared to the in-memory state held by the perag, and so they don't match and the assert fires.
Then other operations on that inode hang because the task was killed whilst holding inode locks. e.g:
Workqueue: xfs-conv/dm-12 xfs_end_io Call Trace: <TASK> __schedule+0x650/0xb10 schedule+0x6d/0xf0 schedule_preempt_disabled+0x15/0x30 rwsem_down_write_slowpath+0x31a/0x5f0 down_write+0x43/0x60 xfs_ilock+0x1a8/0x210 xfs_trans_alloc_inode+0x9c/0x240 xfs_iomap_write_unwritten+0xe3/0x300 xfs_end_ioend+0x90/0x130 xfs_end_io+0xce/0x100 process_scheduled_works+0x1d4/0x400 worker_thread+0x234/0x2e0 kthread+0x147/0x170 ret_from_fork+0x3e/0x50 ret_from_fork_asm+0x1a/0x30 </TASK>
and it's all down hill from there.
Memory pressure is one way to trigger this, another is to run "echo 3 > /proc/sys/vm/drop_caches" randomly while tests are running.
Regardless of how it is triggered, this effectively takes down the system once umount hangs because it's holding a sb->s_umount lock exclusive and now every sync(1) call gets stuck on it.
Fix this by replacing the asserts with a corruption detection check and a shutdown.
6.12:
Fixed conflict due to the following missing commit:
e9c4d8bfb26c ("xfs: factor out a generic xfs_group structure")
6.6:
Fixed conflict due to the following missing commit:
e45ea3645178 ("xfs: split the agf_roots and agf_levels arrays")--- fs/xfs/libxfs/xfs_alloc.c | 46 +++++++++++++++++++++++++++++--------- fs/xfs/libxfs/xfs_ialloc.c | 32 ++++++++++++++++++++++---- 2 files changed, 64 insertions(+), 14 deletions(-)
fs/xfs/libxfs/xfs_alloc.c +36 -10
@@ -26,6 +26,7 @@#include "xfs_ag.h" #include "xfs_ag_resv.h" #include "xfs_bmap.h" +#include "xfs_health.h" struct kmem_cache *xfs_extfree_item_cache;
@@ -3167,18 +3168,43 @@ xfs_alloc_read_agf(atomic64_add (allocbt_blks, &pag->pag_mount->m_allocbt_blks); } +#ifdef DEBUG - else if (!xfs_is_shutdown(pag->pag_mount)) { -ASSERT (pag->pagf_freeblks ==be32_to_cpu (agf->agf_freeblks)); -ASSERT (pag->pagf_btreeblks ==be32_to_cpu (agf->agf_btreeblks)); -ASSERT (pag->pagf_flcount ==be32_to_cpu (agf->agf_flcount)); -ASSERT (pag->pagf_longest ==be32_to_cpu (agf->agf_longest)); -ASSERT (pag->pagf_levels[XFS_BTNUM_BNOi] == -be32_to_cpu (agf->agf_levels[XFS_BTNUM_BNOi])); -ASSERT (pag->pagf_levels[XFS_BTNUM_CNTi] == -be32_to_cpu (agf->agf_levels[XFS_BTNUM_CNTi])); +/* +* It's possible for the AGF to be out of sync if the block device is +* silently dropping writes. This can happen in fstests with dmflakey +* enabled, which allows the buffer to be cleaned and reclaimed by +* memory pressure and then re-read from disk here. We will get a +* stale version of the AGF from disk, and nothing good can happen from +* here. Hence if we detect this situation, immediately shut down the +* filesystem. +* +* This can also happen if we are already in the middle of a forced +* shutdown, so don't bother checking if we are already shut down. +*/ +if (!xfs_is_shutdown (pag->pag_mount)) { +bool ok = true; + + ok &= pag->pagf_freeblks ==be32_to_cpu (agf->agf_freeblks); + ok &= pag->pagf_freeblks ==be32_to_cpu (agf->agf_freeblks); + ok &= pag->pagf_btreeblks ==be32_to_cpu (agf->agf_btreeblks); + ok &= pag->pagf_flcount ==be32_to_cpu (agf->agf_flcount); + ok &= pag->pagf_longest ==be32_to_cpu (agf->agf_longest); + ok &= pag->pagf_levels[XFS_BTNUM_BNOi] == +be32_to_cpu (agf->agf_levels[XFS_BTNUM_BNOi]); + ok &= pag->pagf_levels[XFS_BTNUM_CNTi] == +be32_to_cpu (agf->agf_levels[XFS_BTNUM_CNTi]); + +if (XFS_IS_CORRUPT (pag->pag_mount, !ok)) { +xfs_ag_mark_sick (pag,XFS_SICK_AG_AGF ); +xfs_trans_brelse (tp, agfbp); +xfs_force_shutdown (pag->pag_mount, +SHUTDOWN_CORRUPT_ONDISK ); +return -EFSCORRUPTED ; + } } -#endif +#endif /* DEBUG */ +if (agfbpp) *agfbpp = agfbp;else
fs/xfs/libxfs/xfs_ialloc.c +28 -4
@@ -27,6 +27,7 @@#include "xfs_log.h" #include "xfs_rmap.h" #include "xfs_ag.h" +#include "xfs_health.h" /* * Lookup a record by ino in the btree given by cur.
@@ -2645,12 +2646,35 @@ xfs_ialloc_read_agi( pag->pagi_init =1 ; } +#ifdef DEBUG /* -* It's possible for these to be out of sync if -* we are in the middle of a forced shutdown. +* It's possible for the AGF to be out of sync if the block device is +* silently dropping writes. This can happen in fstests with dmflakey +* enabled, which allows the buffer to be cleaned and reclaimed by +* memory pressure and then re-read from disk here. We will get a +* stale version of the AGF from disk, and nothing good can happen from +* here. Hence if we detect this situation, immediately shut down the +* filesystem. +* +* This can also happen if we are already in the middle of a forced +* shutdown, so don't bother checking if we are already shut down. */ -ASSERT (pag->pagi_freecount ==be32_to_cpu (agi->agi_freecount) || -xfs_is_shutdown (pag->pag_mount)); +if (!xfs_is_shutdown (pag->pag_mount)) { +bool ok = true; + + ok &= pag->pagi_freecount ==be32_to_cpu (agi->agi_freecount); + ok &= pag->pagi_count ==be32_to_cpu (agi->agi_count); + +if (XFS_IS_CORRUPT (pag->pag_mount, !ok)) { +xfs_ag_mark_sick (pag,XFS_SICK_AG_AGI ); +xfs_trans_brelse (tp, agibp); +xfs_force_shutdown (pag->pag_mount, +SHUTDOWN_CORRUPT_ONDISK ); +return -EFSCORRUPTED ; + } + } +#endif /* DEBUG */ +if (agibpp) *agibpp = agibp;else
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 548/1887 xfs: rearrange code in xfs_buf_item.c
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dave Chinner <[email protected]>
[ Upstream commit d2fe5c4c8d25999862d615f616aea7befdd62799 ]
The code to initialise, release and free items is all the way down the bottom of the file. Upcoming fixes need to these functions earlier in the file, so move them to the top.
There is one code change in this move - the parameter to xfs_buf_item_relse() is changed from the xfs_buf to the xfs_buf_log_item - the thing that the function is releasing.
6.6:
Fixed conflict due to the following missing commits:
[Continuous range: 10634530f7ba^..d4c75a1b40cd]
d4c75a1b40cd ("xfs: convert remaining kmem_free() to kfree()")
49292576136f ("xfs: convert kmem_free() for kvmalloc users to kvfree()")
afdc115559c5 ("xfs: move kmem_to_page()")
f078d4ea8276 ("xfs: convert kmem_alloc() to kmalloc()")
10634530f7ba ("xfs: convert kmem_zalloc() to kzalloc()")Removed ASSERT in favour of existing atomic_read-and-return in xfs_buf_item_relse() due to the following missing commit:
3762113b597f ("xfs: don't lose solo superblock counter update transactions")
--- fs/xfs/xfs_buf_item.c | 118 +++++++++++++++++++++--------------------- fs/xfs/xfs_buf_item.h | 1 - 2 files changed, 60 insertions(+), 59 deletions(-)
fs/xfs/xfs_buf_item.c +60 -58
@@ -32,6 +32,63 @@ static inline struct xfs_buf_log_item *BUF_ITEM(struct xfs_log_item *lip) return container_of(lip, struct xfs_buf_log_item, bli_item); } +static void +xfs_buf_item_get_format ( +struct xfs_buf_log_item *bip, +int count) +{ +ASSERT (bip->bli_formats ==NULL ); + bip->bli_format_count = count; + +if (count ==1 ) { + bip->bli_formats = &bip->__bli_format; +return ; + } + + bip->bli_formats =kmem_zalloc (count *sizeof (struct xfs_buf_log_format ), +0 ); +} + +static void +xfs_buf_item_free_format ( +struct xfs_buf_log_item *bip) +{ +if (bip->bli_formats != &bip->__bli_format) { +kmem_free (bip->bli_formats); + bip->bli_formats =NULL ; + } +} + +static void +xfs_buf_item_free ( +struct xfs_buf_log_item *bip) +{ +xfs_buf_item_free_format (bip); +kmem_free (bip->bli_item.li_lv_shadow); +kmem_cache_free (xfs_buf_item_cache, bip); +} + +/* +* xfs_buf_item_relse() is called when the buf log item is no longer needed. +*/ +static void +xfs_buf_item_relse ( +struct xfs_buf_log_item *bip) +{ +struct xfs_buf *bp = bip->bli_buf; + +trace_xfs_buf_item_relse (bp, _RET_IP_); + +ASSERT (!test_bit (XFS_LI_IN_AIL , &bip->bli_item.li_flags)); + +if (atomic_read (&bip->bli_refcount)) +return ; + + bp->b_log_item =NULL ; +xfs_buf_rele (bp); +xfs_buf_item_free (bip); +} +/* Is this log iovec plausibly large enough to contain the buffer log format? */ bool xfs_buf_log_check_iovec(
@@ -563,7 +620,7 @@ xfs_buf_item_unpin(ASSERT (list_empty (&bp->b_li_list)); }else {xfs_trans_ail_delete (lip,SHUTDOWN_LOG_IO_ERROR ); -xfs_buf_item_relse (bp); +xfs_buf_item_relse (bip);ASSERT (bp->b_log_item ==NULL ); }xfs_buf_relse (bp);
@@ -673,7 +730,7 @@ xfs_buf_item_put(*/ if (aborted)xfs_trans_ail_delete (lip,0 ); -xfs_buf_item_relse (bip->bli_buf); +xfs_buf_item_relse (bip);return true; }
@@ -824,33 +881,6 @@ static const struct xfs_item_ops xfs_buf_item_ops = { .iop_push = xfs_buf_item_push, }; -STATIC void -xfs_buf_item_get_format ( -struct xfs_buf_log_item *bip, -int count) -{ -ASSERT (bip->bli_formats ==NULL ); - bip->bli_format_count = count; - -if (count ==1 ) { - bip->bli_formats = &bip->__bli_format; -return ; - } - - bip->bli_formats =kmem_zalloc (count *sizeof (struct xfs_buf_log_format ), -0 ); -} - -STATIC void -xfs_buf_item_free_format ( -struct xfs_buf_log_item *bip) -{ -if (bip->bli_formats != &bip->__bli_format) { -kmem_free (bip->bli_formats); - bip->bli_formats =NULL ; - } -} -/* * Allocate a new buf log item to go with the given buffer. * Set the buffer's b_log_item field to point to the new
@@ -1072,34 +1102,6 @@ xfs_buf_item_dirty_format(return false; } -STATIC void -xfs_buf_item_free ( -struct xfs_buf_log_item *bip) -{ -xfs_buf_item_free_format (bip); -kmem_free (bip->bli_item.li_lv_shadow); -kmem_cache_free (xfs_buf_item_cache, bip); -} - -/* -* xfs_buf_item_relse() is called when the buf log item is no longer needed. -*/ -void -xfs_buf_item_relse ( -struct xfs_buf *bp) -{ -struct xfs_buf_log_item *bip = bp->b_log_item; - -trace_xfs_buf_item_relse (bp, _RET_IP_); -ASSERT (!test_bit (XFS_LI_IN_AIL , &bip->bli_item.li_flags)); - -if (atomic_read (&bip->bli_refcount)) -return ; - bp->b_log_item =NULL ; -xfs_buf_rele (bp); -xfs_buf_item_free (bip); -} -void xfs_buf_item_done (struct xfs_buf *bp)
@@ -1119,5 +1121,5 @@ xfs_buf_item_done(xfs_trans_ail_delete (&bp->b_log_item->bli_item, (bp->b_flags & _XBF_LOGRECOVERY) ?0 :SHUTDOWN_CORRUPT_INCORE ); -xfs_buf_item_relse (bp); +xfs_buf_item_relse (bp->b_log_item); }
fs/xfs/xfs_buf_item.h +0 -1
@@ -49,7 +49,6 @@ struct xfs_buf_log_item {int xfs_buf_item_init (struct xfs_buf *,struct xfs_mount *);void xfs_buf_item_done (struct xfs_buf *bp); -void xfs_buf_item_relse (struct xfs_buf *);bool xfs_buf_item_put (struct xfs_buf_log_item *);void xfs_buf_item_log (struct xfs_buf_log_item *,uint ,uint );bool xfs_buf_item_dirty_format (struct xfs_buf_log_item *);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 549/1887 xfs: factor out stale buffer item completion
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dave Chinner <[email protected]>
[ Upstream commit 816c330b605c3f4813c0dc0ab5af5cce17ff06b3 ]
The stale buffer item completion handling is currently only done from BLI unpinning. We need to perform this function from where-ever the last reference to the BLI is dropped, so first we need to factor this code out into a helper.
--- fs/xfs/xfs_buf_item.c | 60 ++++++++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 23 deletions(-)
fs/xfs/xfs_buf_item.c +37 -23
@@ -541,6 +541,42 @@ xfs_buf_item_pin(atomic_inc (&bip->bli_buf->b_pin_count); } +/* +* For a stale BLI, process all the necessary completions that must be +* performed when the final BLI reference goes away. The buffer will be +* referenced and locked here - we return to the caller with the buffer still +* referenced and locked for them to finalise processing of the buffer. +*/ +static void +xfs_buf_item_finish_stale ( +struct xfs_buf_log_item *bip) +{ +struct xfs_buf *bp = bip->bli_buf; +struct xfs_log_item *lip = &bip->bli_item; + +ASSERT (bip->bli_flags &XFS_BLI_STALE ); +ASSERT (xfs_buf_islocked (bp)); +ASSERT (bp->b_flags &XBF_STALE ); +ASSERT (bip->__bli_format.blf_flags &XFS_BLF_CANCEL ); +ASSERT (list_empty (&lip->li_trans)); +ASSERT (!bp->b_transp); + +if (bip->bli_flags &XFS_BLI_STALE_INODE ) { +xfs_buf_item_done (bp); +xfs_buf_inode_iodone (bp); +ASSERT (list_empty (&bp->b_li_list)); +return ; + } + +/* +* We may or may not be on the AIL here, xfs_trans_ail_delete() will do +* the right thing regardless of the situation in which we are called. +*/ +xfs_trans_ail_delete (lip,SHUTDOWN_LOG_IO_ERROR ); +xfs_buf_item_relse (bip); +ASSERT (bp->b_log_item ==NULL ); +} +/* * This is called to unpin the buffer associated with the buf log item which was * previously pinned with a call to xfs_buf_item_pin(). We enter this function
@@ -590,13 +626,6 @@ xfs_buf_item_unpin( }if (stale) { -ASSERT (bip->bli_flags &XFS_BLI_STALE ); -ASSERT (xfs_buf_islocked (bp)); -ASSERT (bp->b_flags &XBF_STALE ); -ASSERT (bip->__bli_format.blf_flags &XFS_BLF_CANCEL ); -ASSERT (list_empty (&lip->li_trans)); -ASSERT (!bp->b_transp); -trace_xfs_buf_item_unpin_stale (bip);/*
@@ -607,22 +636,7 @@ xfs_buf_item_unpin(* processing is complete. */ xfs_buf_rele (bp); - -/* -* If we get called here because of an IO error, we may or may -* not have the item on the AIL. xfs_trans_ail_delete() will -* take care of that situation. xfs_trans_ail_delete() drops -* the AIL lock. -*/ -if (bip->bli_flags &XFS_BLI_STALE_INODE ) { -xfs_buf_item_done (bp); -xfs_buf_inode_iodone (bp); -ASSERT (list_empty (&bp->b_li_list)); - }else { -xfs_trans_ail_delete (lip,SHUTDOWN_LOG_IO_ERROR ); -xfs_buf_item_relse (bip); -ASSERT (bp->b_log_item ==NULL ); - } +xfs_buf_item_finish_stale (bip);xfs_buf_relse (bp);return ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 550/1887 xfs: fix unmount hang with unflushable inodes stuck in the AIL
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dave Chinner <[email protected]>
[ Upstream commit 7b5f775be14ac1532c049022feadcfe44769566d ]
Unmount of a shutdown filesystem can hang with stale inode cluster buffers in the AIL like so:
[95964.140623] Call Trace: [95964.144641] __schedule+0x699/0xb70 [95964.154003] schedule+0x64/0xd0 [95964.156851] xfs_ail_push_all_sync+0x9b/0xf0 [95964.164816] xfs_unmount_flush_inodes+0x41/0x70 [95964.168698] xfs_unmountfs+0x7f/0x170 [95964.171846] xfs_fs_put_super+0x3b/0x90 [95964.175216] generic_shutdown_super+0x77/0x160 [95964.178060] kill_block_super+0x1b/0x40 [95964.180553] xfs_kill_sb+0x12/0x30 [95964.182796] deactivate_locked_super+0x38/0x100 [95964.185735] deactivate_super+0x41/0x50 [95964.188245] cleanup_mnt+0x9f/0x160 [95964.190519] __cleanup_mnt+0x12/0x20 [95964.192899] task_work_run+0x89/0xb0 [95964.195221] resume_user_mode_work+0x4f/0x60 [95964.197931] syscall_exit_to_user_mode+0x76/0xb0 [95964.201003] do_syscall_64+0x74/0x130
$ pstree -N mnt |grep umount |-check-parallel---nsexec---run_test.sh---753---umount
It always seems to be generic/753 that triggers this, and repeating a quick group test run triggers it every 10-15 iterations. Hence it generally triggers once up every 30-40 minutes of test time. just running generic/753 by itself or concurrently with a limited group of tests doesn't reproduce this issue at all.
Tracing on a hung system shows the AIL repeating every 50ms a log force followed by an attempt to push pinned, aborted inodes from the AIL (trimmed for brevity):
xfs_log_force: lsn 0x1c caller xfsaild+0x18e xfs_log_force: lsn 0x0 caller xlog_cil_flush+0xbd xfs_log_force: lsn 0x1c caller xfs_log_force+0x77 xfs_ail_pinned: lip 0xffff88826014afa0 lsn 1/37472 type XFS_LI_INODE flags IN_AIL|ABORTED xfs_ail_pinned: lip 0xffff88814000a708 lsn 1/37472 type XFS_LI_INODE flags IN_AIL|ABORTED xfs_ail_pinned: lip 0xffff88810b850c80 lsn 1/37472 type XFS_LI_INODE flags IN_AIL|ABORTED xfs_ail_pinned: lip 0xffff88810b850af0 lsn 1/37472 type XFS_LI_INODE flags IN_AIL|ABORTED xfs_ail_pinned: lip 0xffff888165cf0a28 lsn 1/37472 type XFS_LI_INODE flags IN_AIL|ABORTED xfs_ail_pinned: lip 0xffff88810b850bb8 lsn 1/37472 type XFS_LI_INODE flags IN_AIL|ABORTED
....
The inode log items are marked as aborted, which means that either:
a) a transaction commit has occurred, seen an error or shutdown, and called xfs_trans_free_items() to abort the items. This should happen before any pinning of log items occurs.
or
b) a dirty transaction has been cancelled. This should also happen before any pinning of log items occurs.
or
c) AIL insertion at journal IO completion is marked as aborted. In this case, the log item is pinned by the CIL until journal IO completes and hence needs to be unpinned. This is then done after the ->iop_committed() callback is run, so the pin count should be balanced correctly.
Yet none of these seemed to be occurring. Further tracing indicated this:
d) Shutdown during CIL pushing resulting in log item completion being called from checkpoint abort processing. Items are unpinned and released without serialisation against each other, journal IO completion or transaction commit completion.
In this case, we may still have a transaction commit in flight that holds a reference to a xfs_buf_log_item (BLI) after CIL insertion. e.g. a synchronous transaction will flush the CIL before the transaction is torn down. The concurrent CIL push then aborts insertion it and drops the commit/AIL reference to the BLI. This can leave the transaction commit context with the last reference to the BLI which is dropped here:
xfs_trans_free_items()
->iop_release
xfs_buf_item_release
xfs_buf_item_put
if (XFS_LI_ABORTED)
xfs_trans_ail_delete
xfs_buf_item_relse()
Unlike the journal completion ->iop_unpin path, this path does not run stale buffer completion process when it drops the last reference, hence leaving the stale inodes attached to the buffer sitting the AIL. There are no other references to those inodes, so there is no other mechanism to remove them from the AIL. Hence unmount hangs.
The buffer lock context for stale buffers is passed to the last BLI reference. This is normally the last BLI unpin on journal IO completion. The unpin then processes the stale buffer completion and releases the buffer lock. However, if the final unpin from journal IO completion (or CIL push abort) does not hold the last reference to the BLI, there -must- still be a transaction context that references the BLI, and so that context must perform the stale buffer completion processing before the buffer is unlocked and the BLI torn down.
The fix for this is to rework the xfs_buf_item_relse() path to run stale buffer completion processing if it drops the last reference to the BLI. We still hold the buffer locked, so the buffer owner and lock context is the same as if we passed the BLI and buffer to the ->iop_unpin() context to finish stale process on journal commit.
However, we have to be careful here. In a shutdown state, we can be freeing dirty BLIs from xfs_buf_item_put() via xfs_trans_brelse() and xfs_trans_bdetach(). The existing code handles this case by considering shutdown state as "aborted", but in doing so largely masks the failure to clean up stale BLI state from the xfs_buf_item_relse() path. i.e regardless of the shutdown state and whether the item is in the AIL, we must finish the stale buffer cleanup if we are are dropping the last BLI reference from the ->iop_relse path in transaction commit context.
--- fs/xfs/xfs_buf_item.c | 121 +++++++++++++++++++++++++++++------------- fs/xfs/xfs_buf_item.h | 2 +- 2 files changed, 86 insertions(+), 37 deletions(-)
fs/xfs/xfs_buf_item.c +85 -36
@@ -709,43 +709,42 @@ xfs_buf_item_push(* Drop the buffer log item refcount and take appropriate action. This helper * determines whether the bli must be freed or not, since a decrement to zero * does not necessarily mean the bli is unused. -* -* Return true if the bli is freed, false otherwise. */ -bool +void xfs_buf_item_put (struct xfs_buf_log_item *bip) { -struct xfs_log_item *lip = &bip->bli_item; -bool aborted; -bool dirty; + +ASSERT (xfs_buf_islocked (bip->bli_buf));/* drop the bli ref and return if it wasn't the last one */ if (!atomic_dec_and_test (&bip->bli_refcount)) -return false; +return ; -/* -* We dropped the last ref and must free the item if clean or aborted. -* If the bli is dirty and non-aborted, the buffer was clean in the -* transaction but still awaiting writeback from previous changes. In -* that case, the bli is freed on buffer writeback completion. -*/ - aborted =test_bit (XFS_LI_ABORTED , &lip->li_flags) || -xlog_is_shutdown (lip->li_log); - dirty = bip->bli_flags &XFS_BLI_DIRTY ; -if (dirty && !aborted) -return false; +/* If the BLI is in the AIL, then it is still dirty and in use */ +if (test_bit (XFS_LI_IN_AIL , &bip->bli_item.li_flags)) { +ASSERT (bip->bli_flags &XFS_BLI_DIRTY ); +return ; + }/* -* The bli is aborted or clean. An aborted item may be in the AIL -* regardless of dirty state. For example, consider an aborted -* transaction that invalidated a dirty bli and cleared the dirty -* state. +* In shutdown conditions, we can be asked to free a dirty BLI that +* isn't in the AIL. This can occur due to a checkpoint aborting a BLI +* instead of inserting it into the AIL at checkpoint IO completion. If +* there's another bli reference (e.g. a btree cursor holds a clean +* reference) and it is released via xfs_trans_brelse(), we can get here +* with that aborted, dirty BLI. In this case, it is safe to free the +* dirty BLI immediately, as it is not in the AIL and there are no +* other references to it. +* +* We should never get here with a stale BLI via that path as +* xfs_trans_brelse() specifically holds onto stale buffers rather than +* releasing them. */ -if (aborted) -xfs_trans_ail_delete (lip,0 ); +ASSERT (!(bip->bli_flags &XFS_BLI_DIRTY ) || +test_bit (XFS_LI_ABORTED , &bip->bli_item.li_flags)); +ASSERT (!(bip->bli_flags &XFS_BLI_STALE ));xfs_buf_item_relse (bip); -return true; }/*
@@ -766,6 +765,15 @@ xfs_buf_item_put(* if necessary but do not unlock the buffer. This is for support of * xfs_trans_bhold(). Make sure the XFS_BLI_HOLD field is cleared if we don't * free the item. +* +* If the XFS_BLI_STALE flag is set, the last reference to the BLI *must* +* perform a completion abort of any objects attached to the buffer for IO +* tracking purposes. This generally only happens in shutdown situations, +* normally xfs_buf_item_unpin() will drop the last BLI reference and perform +* completion processing. However, because transaction completion can race with +* checkpoint completion during a shutdown, this release context may end up +* being the last active reference to the BLI and so needs to perform this +* cleanup. */ STATIC void xfs_buf_item_release(
@@ -773,18 +781,19 @@ xfs_buf_item_release( {struct xfs_buf_log_item *bip =BUF_ITEM (lip);struct xfs_buf *bp = bip->bli_buf; -bool released;bool hold = bip->bli_flags &XFS_BLI_HOLD ;bool stale = bip->bli_flags &XFS_BLI_STALE ; -#if defined(DEBUG ) || defined(XFS_WARN ) -bool ordered = bip->bli_flags &XFS_BLI_ORDERED ; -bool dirty = bip->bli_flags &XFS_BLI_DIRTY ;bool aborted =test_bit (XFS_LI_ABORTED , &lip->li_flags); +bool dirty = bip->bli_flags &XFS_BLI_DIRTY ; +#if defined(DEBUG ) || defined(XFS_WARN ) +bool ordered = bip->bli_flags &XFS_BLI_ORDERED ;#endif trace_xfs_buf_item_release (bip); +ASSERT (xfs_buf_islocked (bp)); +/* * The bli dirty state should match whether the blf has logged segments * except for ordered buffers, where only the bli should be dirty.
@@ -800,16 +809,56 @@ xfs_buf_item_release( bp->b_transp =NULL ; bip->bli_flags &= ~(XFS_BLI_LOGGED |XFS_BLI_HOLD |XFS_BLI_ORDERED ); +/* If there are other references, then we have nothing to do. */ +if (!atomic_dec_and_test (&bip->bli_refcount)) + goto out_release; + +/* +* Stale buffer completion frees the BLI, unlocks and releases the +* buffer. Neither the BLI or buffer are safe to reference after this +* call, so there's nothing more we need to do here. +* +* If we get here with a stale buffer and references to the BLI remain, +* we must not unlock the buffer as the last BLI reference owns lock +* context, not us. +*/ +if (stale) { +xfs_buf_item_finish_stale (bip); +xfs_buf_relse (bp); +ASSERT (!hold); +return ; + } + +/* +* Dirty or clean, aborted items are done and need to be removed from +* the AIL and released. This frees the BLI, but leaves the buffer +* locked and referenced. +*/ +if (aborted ||xlog_is_shutdown (lip->li_log)) { +ASSERT (list_empty (&bip->bli_buf->b_li_list)); +xfs_buf_item_done (bp); + goto out_release; + } + +/* +* Clean, unreferenced BLIs can be immediately freed, leaving the buffer +* locked and referenced. +* +* Dirty, unreferenced BLIs *must* be in the AIL awaiting writeback. +*/ +if (!dirty) +xfs_buf_item_relse (bip); +else +ASSERT (test_bit (XFS_LI_IN_AIL , &lip->li_flags)); + +/* Not safe to reference the BLI from here */ +out_release:/* -* Unref the item and unlock the buffer unless held or stale. Stale -* buffers remain locked until final unpin unless the bli is freed by -* the unref call. The latter implies shutdown because buffer -* invalidation dirties the bli and transaction. +* If we get here with a stale buffer, we must not unlock the +* buffer as the last BLI reference owns lock context, not us. */ - released =xfs_buf_item_put (bip); -if (hold || (stale && !released)) +if (stale || hold)return ; -ASSERT (!stale || aborted);xfs_buf_relse (bp); }
fs/xfs/xfs_buf_item.h +1 -1
@@ -49,7 +49,7 @@ struct xfs_buf_log_item {int xfs_buf_item_init (struct xfs_buf *,struct xfs_mount *);void xfs_buf_item_done (struct xfs_buf *bp); -bool xfs_buf_item_put (struct xfs_buf_log_item *); +void xfs_buf_item_put (struct xfs_buf_log_item *bip);void xfs_buf_item_log (struct xfs_buf_log_item *,uint ,uint );bool xfs_buf_item_dirty_format (struct xfs_buf_log_item *);void xfs_buf_inode_iodone (struct xfs_buf *);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 551/1887 mm: migrate: fix getting incorrect page mapping during page migration
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Baolin Wang <[email protected]>
[ Upstream commit d1adb25df7111de83b64655a80b5a135adbded61 ]
When running stress-ng testing, we found below kernel crash after a few hours:
Unable to handle kernel NULL pointer dereference at virtual address 0000000000000000 pc : dentry_name+0xd8/0x224 lr : pointer+0x22c/0x370 sp : ffff800025f134c0 ...... Call trace: dentry_name+0xd8/0x224 pointer+0x22c/0x370 vsnprintf+0x1ec/0x730 vscnprintf+0x2c/0x60 vprintk_store+0x70/0x234 vprintk_emit+0xe0/0x24c vprintk_default+0x3c/0x44 vprintk_func+0x84/0x2d0 printk+0x64/0x88 __dump_page+0x52c/0x530 dump_page+0x14/0x20 set_migratetype_isolate+0x110/0x224 start_isolate_page_range+0xc4/0x20c offline_pages+0x124/0x474 memory_block_offline+0x44/0xf4 memory_subsys_offline+0x3c/0x70 device_offline+0xf0/0x120 ......
After analyzing the vmcore, I found this issue is caused by page migration. The scenario is that, one thread is doing page migration, and we will use the target page's ->mapping field to save 'anon_vma' pointer between page unmap and page move, and now the target page is locked and refcount is 1.
Currently, there is another stress-ng thread performing memory hotplug, attempting to offline the target page that is being migrated. It discovers that the refcount of this target page is 1, preventing the offline operation, thus proceeding to dump the page. However, page_mapping() of the target page may return an incorrect file mapping to crash the system in dump_mapping(), since the target page->mapping only saves 'anon_vma' pointer without setting PAGE_MAPPING_ANON flag.
There are seveval ways to fix this issue:
(1) Setting the PAGE_MAPPING_ANON flag for target page's ->mapping when saving 'anon_vma', but this can confuse PageAnon() for PFN walkers, since the target page has not built mappings yet.
(2) Getting the page lock to call page_mapping() in __dump_page() to avoid crashing the system, however, there are still some PFN walkers that call page_mapping() without holding the page lock, such as compaction.
(3) Using target page->private field to save the 'anon_vma' pointer and 2 bits page state, just as page->mapping records an anonymous page, which can remove the page_mapping() impact for PFN walkers and also seems a simple way.
So I choose option 3 to fix this issue, and this can also fix other potential issues for PFN walkers, such as compaction.
--- mm/migrate.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-)
mm/migrate.c +12 -15
@@ -1010,32 +1010,29 @@ static int move_to_new_folio(struct folio *dst, struct folio *src, }/* -* To record some information during migration, we use some unused -* fields (mapping and private) of struct folio of the newly allocated -* destination folio. This is safe because nobody is using them -* except us. +* To record some information during migration, we use unused private +* field of struct folio of the newly allocated destination folio. +* This is safe because nobody is using it except us. */ -union migration_ptr { -struct anon_vma *anon_vma; -struct address_space *mapping; +enum { +PAGE_WAS_MAPPED =BIT (0 ), }; +static void __migrate_folio_record (struct folio *dst, -unsigned long page_was_mapped, +int page_was_mapped,struct anon_vma *anon_vma) { -union migration_ptr ptr = { .anon_vma = anon_vma }; - dst->mapping = ptr.mapping; - dst->private = (void *)page_was_mapped; + dst->private = (void *)anon_vma + page_was_mapped; }static void __migrate_folio_extract (struct folio *dst,int *page_was_mappedp,struct anon_vma **anon_vmap) { -union migration_ptr ptr = { .mapping = dst->mapping }; - *anon_vmap = ptr.anon_vma; - *page_was_mappedp = (unsigned long )dst->private; - dst->mapping =NULL ; +unsigned long private = (unsigned long )dst->private; + + *anon_vmap = (struct anon_vma *)(private & ~PAGE_WAS_MAPPED ); + *page_was_mappedp = private &PAGE_WAS_MAPPED ; dst->private =NULL ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 552/1887 drm/msm/dpu: Add mutex lock in control vblank irq
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Paloma Arellano <[email protected]>
[ Upstream commit 45284ff733e4caf6c118aae5131eb7e7cf3eea5a ]
Add a mutex lock to control vblank irq to synchronize vblank enable/disable operations happening from different threads to prevent race conditions while registering/unregistering the vblank irq callback.
v4: -Removed vblank_ctl_lock from dpu_encoder_virt, so it is only a
parameter of dpu_encoder_phys.
-Switch from atomic refcnt to a simple int counter as mutex has now been added
v3: Mistakenly did not change wording in last version. It is done now.
v2: Slightly changed wording of commit message
Patchwork: https://patchwork.freedesktop.org/patch/571854/
--- .../gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h | 4 ++- .../drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c | 33 +++++++++++------ .../drm/msm/disp/dpu1/dpu_encoder_phys_vid.c | 35 ++++++++++++------- .../drm/msm/disp/dpu1/dpu_encoder_phys_wb.c | 1 - 4 files changed, 48 insertions(+), 25 deletions(-)
drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h +3 -1
@@ -177,6 +177,7 @@ enum dpu_intr_idx {* @hw_wb: Hardware interface to the wb registers * @dpu_kms: Pointer to the dpu_kms top level * @cached_mode: DRM mode cached at mode_set time, acted on in enable +* @vblank_ctl_lock: Vblank ctl mutex lock to protect vblank_refcount * @enabled: Whether the encoder has enabled and running a mode * @split_role: Role to play in a split-panel configuration * @intf_mode: Interface mode
@@ -207,13 +208,14 @@ struct dpu_encoder_phys {struct dpu_hw_wb *hw_wb;struct dpu_kms *dpu_kms;struct drm_display_mode cached_mode; +struct mutex vblank_ctl_lock;enum dpu_enc_split_role split_role;enum dpu_intf_mode intf_mode;enum dpu_intf intf_idx;enum dpu_wb wb_idx;spinlock_t *enc_spinlock;enum dpu_enc_enable_state enable_state; -atomic_t vblank_refcount; +int vblank_refcount;atomic_t vsync_cnt;atomic_t underrun_cnt;atomic_t pending_ctlstart_cnt;
drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c +22 -11
@@ -246,7 +246,8 @@ static int dpu_encoder_phys_cmd_control_vblank_irq(return -EINVAL ; } - refcount =atomic_read (&phys_enc->vblank_refcount); +mutex_lock (&phys_enc->vblank_ctl_lock); + refcount = phys_enc->vblank_refcount;/* Slave encoders don't report vblank */ if (!dpu_encoder_phys_cmd_is_master (phys_enc))
@@ -262,16 +263,24 @@ static int dpu_encoder_phys_cmd_control_vblank_irq( phys_enc->hw_pp->idx -PINGPONG_0 , enable ? "true" : "false", refcount); -if (enable &&atomic_inc_return (&phys_enc->vblank_refcount) ==1 ) - ret =dpu_core_irq_register_callback (phys_enc->dpu_kms, - phys_enc->irq[INTR_IDX_RDPTR ], - dpu_encoder_phys_cmd_pp_rd_ptr_irq, - phys_enc); -else if (!enable &&atomic_dec_return (&phys_enc->vblank_refcount) ==0 ) - ret =dpu_core_irq_unregister_callback (phys_enc->dpu_kms, - phys_enc->irq[INTR_IDX_RDPTR ]); +if (enable) { +if (phys_enc->vblank_refcount ==0 ) + ret =dpu_core_irq_register_callback (phys_enc->dpu_kms, + phys_enc->irq[INTR_IDX_RDPTR ], + dpu_encoder_phys_cmd_pp_rd_ptr_irq, + phys_enc); +if (!ret) + phys_enc->vblank_refcount++; + }else if (!enable) { +if (phys_enc->vblank_refcount ==1 ) + ret =dpu_core_irq_unregister_callback (phys_enc->dpu_kms, + phys_enc->irq[INTR_IDX_RDPTR ]); +if (!ret) + phys_enc->vblank_refcount--; + } end: +mutex_unlock (&phys_enc->vblank_ctl_lock);if (ret) { DRM_ERROR("vblank irq err id:%u pp:%d ret:%d, enable %s/%d\n", DRMID(phys_enc->parent),
@@ -287,7 +296,7 @@ static void dpu_encoder_phys_cmd_irq_control(struct dpu_encoder_phys *phys_enc, {trace_dpu_enc_phys_cmd_irq_ctrl (DRMID (phys_enc->parent), phys_enc->hw_pp->idx -PINGPONG_0 , - enable,atomic_read (&phys_enc->vblank_refcount)); + enable, phys_enc->vblank_refcount);if (enable) { dpu_core_irq_register_callback(phys_enc->dpu_kms,
@@ -777,6 +786,9 @@ struct dpu_encoder_phys *dpu_encoder_phys_cmd_init( phys_enc->hw_mdptop = p->dpu_kms->hw_mdp; phys_enc->intf_idx = p->intf_idx; +mutex_init (&phys_enc->vblank_ctl_lock); + phys_enc->vblank_refcount =0 ; +dpu_encoder_phys_cmd_init_ops (&phys_enc->ops); phys_enc->parent = p->parent; phys_enc->parent_ops = p->parent_ops;
@@ -789,7 +801,6 @@ struct dpu_encoder_phys *dpu_encoder_phys_cmd_init(for (i =0 ; i <ARRAY_SIZE (phys_enc->irq); i++) phys_enc->irq[i] = -EINVAL ; -atomic_set (&phys_enc->vblank_refcount,0 );atomic_set (&phys_enc->pending_kickoff_cnt,0 );atomic_set (&phys_enc->pending_ctlstart_cnt,0 );atomic_set (&cmd_enc->pending_vblank_cnt,0 );
drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c +23 -12
@@ -375,7 +375,8 @@ static int dpu_encoder_phys_vid_control_vblank_irq(int ret =0 ;int refcount; - refcount =atomic_read (&phys_enc->vblank_refcount); +mutex_lock (&phys_enc->vblank_ctl_lock); + refcount = phys_enc->vblank_refcount;/* Slave encoders don't report vblank */ if (!dpu_encoder_phys_vid_is_master (phys_enc))
@@ -388,18 +389,26 @@ static int dpu_encoder_phys_vid_control_vblank_irq( }DRM_DEBUG_VBL ("id:%u enable=%d/%d\n" ,DRMID (phys_enc->parent), enable, -atomic_read (&phys_enc->vblank_refcount)); + refcount); -if (enable &&atomic_inc_return (&phys_enc->vblank_refcount) ==1 ) - ret =dpu_core_irq_register_callback (phys_enc->dpu_kms, - phys_enc->irq[INTR_IDX_VSYNC ], - dpu_encoder_phys_vid_vblank_irq, - phys_enc); -else if (!enable &&atomic_dec_return (&phys_enc->vblank_refcount) ==0 ) - ret =dpu_core_irq_unregister_callback (phys_enc->dpu_kms, - phys_enc->irq[INTR_IDX_VSYNC ]); +if (enable) { +if (phys_enc->vblank_refcount ==0 ) + ret =dpu_core_irq_register_callback (phys_enc->dpu_kms, + phys_enc->irq[INTR_IDX_VSYNC ], + dpu_encoder_phys_vid_vblank_irq, + phys_enc); +if (!ret) + phys_enc->vblank_refcount++; + }else if (!enable) { +if (phys_enc->vblank_refcount ==1 ) + ret =dpu_core_irq_unregister_callback (phys_enc->dpu_kms, + phys_enc->irq[INTR_IDX_VSYNC ]); +if (!ret) + phys_enc->vblank_refcount--; + } end: +mutex_unlock (&phys_enc->vblank_ctl_lock);if (ret) { DRM_ERROR("failed: id:%u intf:%d ret:%d enable:%d refcnt:%d\n", DRMID(phys_enc->parent),
@@ -607,7 +616,7 @@ static void dpu_encoder_phys_vid_irq_control(struct dpu_encoder_phys *phys_enc,trace_dpu_enc_phys_vid_irq_ctrl (DRMID (phys_enc->parent), phys_enc->hw_intf->idx -INTF_0 , enable, -atomic_read (&phys_enc->vblank_refcount)); + phys_enc->vblank_refcount);if (enable) { ret =dpu_encoder_phys_vid_control_vblank_irq (phys_enc, true);
@@ -703,6 +712,9 @@ struct dpu_encoder_phys *dpu_encoder_phys_vid_init(DPU_DEBUG_VIDENC (phys_enc,"\n" ); +mutex_init (&phys_enc->vblank_ctl_lock); + phys_enc->vblank_refcount =0 ; +dpu_encoder_phys_vid_init_ops (&phys_enc->ops); phys_enc->parent = p->parent; phys_enc->parent_ops = p->parent_ops;
@@ -713,7 +725,6 @@ struct dpu_encoder_phys *dpu_encoder_phys_vid_init(for (i =0 ; i <ARRAY_SIZE (phys_enc->irq); i++) phys_enc->irq[i] = -EINVAL ; -atomic_set (&phys_enc->vblank_refcount,0 );atomic_set (&phys_enc->pending_kickoff_cnt,0 );init_waitqueue_head (&phys_enc->pending_kickoff_wq); phys_enc->enable_state =DPU_ENC_DISABLED ;
drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c +0 -1
@@ -735,7 +735,6 @@ struct dpu_encoder_phys *dpu_encoder_phys_wb_init( phys_enc->irq[i] = -EINVAL ;atomic_set (&phys_enc->pending_kickoff_cnt,0 ); -atomic_set (&phys_enc->vblank_refcount,0 ); wb_enc->wb_done_timeout_cnt =0 ;init_waitqueue_head (&phys_enc->pending_kickoff_wq);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 553/1887 fs: new helper: simple_rename_timestamp
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jeff Layton <[email protected]>
[ Upstream commit 0c4767923ed6964d279309744cdb248890e95ec2 ]
A rename potentially involves updating 4 different inode timestamps. Add a function that handles the details sanely, and convert the libfs.c callers to use it.
Message-Id: <[email protected]>
Stable-dep-of: 49db9b1b86a8 ("reiserfs: Avoid touching renamed directory if parent does not change")
--- fs/libfs.c | 36 +++++++++++++++++++++++++++--------- include/linux/fs.h | 2 ++ 2 files changed, 29 insertions(+), 9 deletions(-)
fs/libfs.c +27 -9
@@ -449,6 +449,31 @@ int simple_rmdir(struct inode *dir, struct dentry *dentry) }EXPORT_SYMBOL (simple_rmdir); +/** +* simple_rename_timestamp - update the various inode timestamps for rename +* @old_dir: old parent directory +* @old_dentry: dentry that is being renamed +* @new_dir: new parent directory +* @new_dentry: target for rename +* +* POSIX mandates that the old and new parent directories have their ctime and +* mtime updated, and that inodes of @old_dentry and @new_dentry (if any), have +* their ctime updated. +*/ +void simple_rename_timestamp (struct inode *old_dir,struct dentry *old_dentry, +struct inode *new_dir,struct dentry *new_dentry) +{ +struct inode *newino =d_inode (new_dentry); + + old_dir->i_mtime =inode_set_ctime_current (old_dir); +if (new_dir != old_dir) + new_dir->i_mtime =inode_set_ctime_current (new_dir); +inode_set_ctime_current (d_inode (old_dentry)); +if (newino) +inode_set_ctime_current (newino); +} +EXPORT_SYMBOL_GPL (simple_rename_timestamp); +int simple_rename_exchange (struct inode *old_dir,struct dentry *old_dentry,struct inode *new_dir,struct dentry *new_dentry) {
@@ -464,11 +489,7 @@ int simple_rename_exchange(struct inode *old_dir, struct dentry *old_dentry,inc_nlink (old_dir); } } - old_dir->i_ctime = old_dir->i_mtime = - new_dir->i_ctime = new_dir->i_mtime = -d_inode (old_dentry)->i_ctime = -d_inode (new_dentry)->i_ctime =current_time (old_dir); - +simple_rename_timestamp (old_dir, old_dentry, new_dir, new_dentry);return 0 ; }EXPORT_SYMBOL_GPL (simple_rename_exchange);
@@ -477,7 +498,6 @@ int simple_rename(struct user_namespace *mnt_userns, struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry, unsigned int flags) { - struct inode *inode = d_inode(old_dentry); int they_are_dirs = d_is_dir(old_dentry);if (flags & ~(RENAME_NOREPLACE |RENAME_EXCHANGE ))
@@ -500,9 +520,7 @@ int simple_rename(struct user_namespace *mnt_userns, struct inode *old_dir,inc_nlink (new_dir); } - old_dir->i_ctime = old_dir->i_mtime = new_dir->i_ctime = - new_dir->i_mtime = inode->i_ctime =current_time (old_dir); - +simple_rename_timestamp (old_dir, old_dentry, new_dir, new_dentry);return 0 ; }EXPORT_SYMBOL (simple_rename);
include/linux/fs.h +2 -0
@@ -3483,6 +3483,8 @@ extern int simple_open(struct inode *inode, struct file *file);extern int simple_link (struct dentry *,struct inode *,struct dentry *);extern int simple_unlink (struct inode *,struct dentry *);extern int simple_rmdir (struct inode *,struct dentry *); +void simple_rename_timestamp (struct inode *old_dir,struct dentry *old_dentry, +struct inode *new_dir,struct dentry *new_dentry);extern int simple_rename_exchange (struct inode *old_dir,struct dentry *old_dentry,struct inode *new_dir,struct dentry *new_dentry); extern int simple_rename(struct user_namespace *, struct inode *,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 554/1887 reiserfs: convert to simple_rename_timestamp
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jeff Layton <[email protected]>
[ Upstream commit 298d35ef763de3393bd1f60d35ff4203cbb4f26a ]
Message-Id: <[email protected]>
Stable-dep-of: 49db9b1b86a8 ("reiserfs: Avoid touching renamed directory if parent does not change")
--- fs/reiserfs/namei.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-)
fs/reiserfs/namei.c +1 -6
@@ -1327,7 +1327,6 @@ static int reiserfs_rename(struct user_namespace *mnt_userns,int jbegin_count;umode_t old_inode_mode;unsigned long savelink =1 ; -struct timespec64 ctime;if (flags & ~RENAME_NOREPLACE )return -EINVAL ;
@@ -1578,14 +1577,11 @@ static int reiserfs_rename(struct user_namespace *mnt_userns,mark_de_hidden (old_de.de_deh + old_de.de_entry_num);journal_mark_dirty (&th, old_de.de_bh); - ctime =current_time (old_dir); - old_dir->i_ctime = old_dir->i_mtime = ctime; - new_dir->i_ctime = new_dir->i_mtime = ctime;/* * thanks to Alex Adriaanse <[email protected]> for patch * which adds ctime update of renamed object */ - old_inode->i_ctime = ctime; +simple_rename_timestamp (old_dir, old_dentry, new_dir, new_dentry);if (new_dentry_inode) {/* adjust link number of the victim */
@@ -1594,7 +1590,6 @@ static int reiserfs_rename(struct user_namespace *mnt_userns, }else {drop_nlink (new_dentry_inode); } - new_dentry_inode->i_ctime = ctime; savelink = new_dentry_inode->i_nlink; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 555/1887 reiserfs: Avoid touching renamed directory if parent does not change
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jan Kara <[email protected]>
[ Upstream commit 49db9b1b86a82448dfaf3fcfefcf678dee56c8ed ]
The VFS will not be locking moved directory if its parent does not change. Change reiserfs rename code to avoid touching renamed directory if its parent does not change as without locking that can corrupt the filesystem.
--- fs/reiserfs/namei.c | 54 ++++++++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 25 deletions(-)
fs/reiserfs/namei.c +29 -25
@@ -1325,8 +1325,8 @@ static int reiserfs_rename(struct user_namespace *mnt_userns,struct inode *old_inode, *new_dentry_inode;struct reiserfs_transaction_handle th;int jbegin_count; -umode_t old_inode_mode;unsigned long savelink =1 ; +bool update_dir_parent = false;if (flags & ~RENAME_NOREPLACE )return -EINVAL ;
@@ -1376,8 +1376,7 @@ static int reiserfs_rename(struct user_namespace *mnt_userns,return -ENOENT ; } - old_inode_mode = old_inode->i_mode; -if (S_ISDIR (old_inode_mode)) { +if (S_ISDIR (old_inode->i_mode)) {/* * make sure that directory being renamed has correct ".." * and that its new parent directory has not too many links
@@ -1390,24 +1389,28 @@ static int reiserfs_rename(struct user_namespace *mnt_userns, } } -/* -* directory is renamed, its parent directory will be changed, -* so find ".." entry -*/ - dot_dot_de.de_gen_number_bit_string =NULL ; - retval = -reiserfs_find_entry (old_inode,".." ,2 , &dot_dot_entry_path, +if (old_dir != new_dir) { +/* +* directory is renamed, its parent directory will be +* changed, so find ".." entry +*/ + dot_dot_de.de_gen_number_bit_string =NULL ; + retval = +reiserfs_find_entry (old_inode,".." ,2 , + &dot_dot_entry_path, &dot_dot_de); -pathrelse (&dot_dot_entry_path); -if (retval !=NAME_FOUND ) { -reiserfs_write_unlock (old_dir->i_sb); -return -EIO ; - } +pathrelse (&dot_dot_entry_path); +if (retval !=NAME_FOUND ) { +reiserfs_write_unlock (old_dir->i_sb); +return -EIO ; + } -/* inode number of .. must equal old_dir->i_ino */ -if (dot_dot_de.de_objectid != old_dir->i_ino) { -reiserfs_write_unlock (old_dir->i_sb); -return -EIO ; +/* inode number of .. must equal old_dir->i_ino */ +if (dot_dot_de.de_objectid != old_dir->i_ino) { +reiserfs_write_unlock (old_dir->i_sb); +return -EIO ; + } + update_dir_parent = true; } }
@@ -1487,7 +1490,7 @@ static int reiserfs_rename(struct user_namespace *mnt_userns,reiserfs_prepare_for_journal (old_inode->i_sb, new_de.de_bh,1 ); - if (S_ISDIR(old_inode->i_mode)) { + if (update_dir_parent) { if ((retval = search_by_entry_key(new_dir->i_sb, &dot_dot_de.de_entry_key,
@@ -1535,14 +1538,14 @@ static int reiserfs_rename(struct user_namespace *mnt_userns, new_de.de_bh);reiserfs_restore_prepared_buffer (old_inode->i_sb, old_de.de_bh); -if (S_ISDIR (old_inode_mode)) +if (update_dir_parent)reiserfs_restore_prepared_buffer (old_inode-> i_sb, dot_dot_de. de_bh);continue ; } -if (S_ISDIR (old_inode_mode)) { +if (update_dir_parent) {if (item_moved (&dot_dot_ih, &dot_dot_entry_path) || !entry_points_to_object (".." ,2 , &dot_dot_de, old_dir)) {
@@ -1560,7 +1563,7 @@ static int reiserfs_rename(struct user_namespace *mnt_userns, } } -RFALSE (S_ISDIR (old_inode_mode) && +RFALSE (update_dir_parent && !buffer_journal_prepared (dot_dot_de.de_bh),"" );break ;
@@ -1593,11 +1596,12 @@ static int reiserfs_rename(struct user_namespace *mnt_userns, savelink = new_dentry_inode->i_nlink; } -if (S_ISDIR (old_inode_mode)) { +if (update_dir_parent) {/* adjust ".." of renamed directory */ set_ino_in_dir_entry (&dot_dot_de,INODE_PKEY (new_dir));journal_mark_dirty (&th, dot_dot_de.de_bh); - + } +if (S_ISDIR (old_inode->i_mode)) {/* * there (in new_dir) was no directory, so it got new link * (".." of renamed directory)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 556/1887 SUNRPC: fix a memleak in gss_import_v2_context
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhipeng Lu <[email protected]>
[ Upstream commit e67b652d8e8591d3b1e569dbcdfcee15993e91fa ]
The ctx->mech_used.data allocated by kmemdup is not freed in neither gss_import_v2_context nor it only caller gss_krb5_import_sec_context, which frees ctx on error.
Thus, this patch reform the last call of gss_import_v2_context to the gss_krb5_import_ctx_v2, preventing the memleak while keepping the return formation.
--- net/sunrpc/auth_gss/gss_krb5_mech.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-)
net/sunrpc/auth_gss/gss_krb5_mech.c +14 -3
@@ -471,6 +471,7 @@ gss_import_v2_context(const void *p, const void *end, struct krb5_ctx *ctx,u64 seq_send64;int keylen;u32 time32; +int ret; p =simple_get_bytes (p, end, &ctx->flags,sizeof (ctx->flags));if (IS_ERR (p))
@@ -528,14 +529,24 @@ gss_import_v2_context(const void *p, const void *end, struct krb5_ctx *ctx,switch (ctx->enctype) {case ENCTYPE_DES3_CBC_RAW : -return context_derive_keys_des3 (ctx, gfp_mask); + ret =context_derive_keys_des3 (ctx, gfp_mask); +break ;case ENCTYPE_AES128_CTS_HMAC_SHA1_96 :case ENCTYPE_AES256_CTS_HMAC_SHA1_96 : -return context_derive_keys_new (ctx, gfp_mask); + ret =context_derive_keys_new (ctx, gfp_mask); +break ;default : -return -EINVAL ; + ret = -EINVAL ; + } +if (ret) { + p =ERR_PTR (ret); + goto out_free; } +return 0 ; + +out_free: +kfree (ctx->mech_used.data); out_err:return PTR_ERR (p); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 557/1887 drm/amd/display: Fix hang/underflow when transitioning to ODM4:1
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ilya Bakoulin <[email protected]>
[ Upstream commit e7b2b108cdeab76a7e7324459e50b0c1214c0386 ]
[Why]
Under some circumstances, disabling an OPTC and attempting to reclaim its OPP(s) for a different OPTC could cause a hang/underflow due to OPPs not being properly disconnected from the disabled OPTC.
[How]
Ensure that all OPPs are unassigned from an OPTC when it gets disabled.
--- drivers/gpu/drm/amd/display/dc/dcn314/dcn314_optc.c | 7 +++++++ drivers/gpu/drm/amd/display/dc/dcn32/dcn32_optc.c | 7 +++++++ 2 files changed, 14 insertions(+)
drivers/gpu/drm/amd/display/dc/dcn314/dcn314_optc.c +7 -0
@@ -139,6 +139,13 @@ static bool optc314_disable_crtc(struct timing_generator *optc)REG_UPDATE (OTG_CONTROL ,OTG_MASTER_EN ,0 ); +REG_UPDATE_5 (OPTC_DATA_SOURCE_SELECT , +OPTC_SEG0_SRC_SEL ,0xf , +OPTC_SEG1_SRC_SEL ,0xf , +OPTC_SEG2_SRC_SEL ,0xf , +OPTC_SEG3_SRC_SEL ,0xf , +OPTC_NUM_OF_INPUT_SEGMENT ,0 ); +REG_UPDATE (CONTROL ,VTG0_ENABLE ,0 );
drivers/gpu/drm/amd/display/dc/dcn32/dcn32_optc.c +7 -0
@@ -145,6 +145,13 @@ static bool optc32_disable_crtc(struct timing_generator *optc)REG_UPDATE (OTG_CONTROL ,OTG_MASTER_EN ,0 ); +REG_UPDATE_5 (OPTC_DATA_SOURCE_SELECT , +OPTC_SEG0_SRC_SEL ,0xf , +OPTC_SEG1_SRC_SEL ,0xf , +OPTC_SEG2_SRC_SEL ,0xf , +OPTC_SEG3_SRC_SEL ,0xf , +OPTC_NUM_OF_INPUT_SEGMENT ,0 ); +REG_UPDATE (CONTROL ,VTG0_ENABLE ,0 );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 558/1887 smb: client: fix use-after-free in smb2_query_info_compound()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Paulo Alcantara <[email protected]>
[ Upstream commit 5c86919455c1edec99ebd3338ad213b59271a71b ]
The following UAF was triggered when running fstests generic/072 with KASAN enabled against Windows Server 2022 and mount options 'multichannel,max_channels=2,vers=3.1.1,mfsymlinks,noperm'
BUG: KASAN: slab-use-after-free in smb2_query_info_compound+0x423/0x6d0 [cifs] Read of size 8 at addr ffff888014941048 by task xfs_io/27534 CPU: 0 PID: 27534 Comm: xfs_io Not tainted 6.6.0-rc7 #1 Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS rel-1.16.2-3-gd478f380-rebuilt.opensuse.org 04/01/2014 Call Trace: dump_stack_lvl+0x4a/0x80 print_report+0xcf/0x650 ? srso_alias_return_thunk+0x5/0x7f ? srso_alias_return_thunk+0x5/0x7f ? __phys_addr+0x46/0x90 kasan_report+0xda/0x110 ? smb2_query_info_compound+0x423/0x6d0 [cifs] ? smb2_query_info_compound+0x423/0x6d0 [cifs] smb2_query_info_compound+0x423/0x6d0 [cifs] ? __pfx_smb2_query_info_compound+0x10/0x10 [cifs] ? srso_alias_return_thunk+0x5/0x7f ? __stack_depot_save+0x39/0x480 ? kasan_save_stack+0x33/0x60 ? kasan_set_track+0x25/0x30 ? ____kasan_slab_free+0x126/0x170 smb2_queryfs+0xc2/0x2c0 [cifs] ? __pfx_smb2_queryfs+0x10/0x10 [cifs] ? __pfx___lock_acquire+0x10/0x10 smb311_queryfs+0x210/0x220 [cifs] ? __pfx_smb311_queryfs+0x10/0x10 [cifs] ? srso_alias_return_thunk+0x5/0x7f ? __lock_acquire+0x480/0x26c0 ? lock_release+0x1ed/0x640 ? srso_alias_return_thunk+0x5/0x7f ? do_raw_spin_unlock+0x9b/0x100 cifs_statfs+0x18c/0x4b0 [cifs] statfs_by_dentry+0x9b/0xf0 fd_statfs+0x4e/0xb0 __do_sys_fstatfs+0x7f/0xe0 ? __pfx___do_sys_fstatfs+0x10/0x10 ? srso_alias_return_thunk+0x5/0x7f ? lockdep_hardirqs_on_prepare+0x136/0x200 ? srso_alias_return_thunk+0x5/0x7f do_syscall_64+0x3f/0x90 entry_SYSCALL_64_after_hwframe+0x6e/0xd8 Allocated by task 27534: kasan_save_stack+0x33/0x60 kasan_set_track+0x25/0x30 __kasan_kmalloc+0x8f/0xa0 open_cached_dir+0x71b/0x1240 [cifs] smb2_query_info_compound+0x5c3/0x6d0 [cifs] smb2_queryfs+0xc2/0x2c0 [cifs] smb311_queryfs+0x210/0x220 [cifs] cifs_statfs+0x18c/0x4b0 [cifs] statfs_by_dentry+0x9b/0xf0 fd_statfs+0x4e/0xb0 __do_sys_fstatfs+0x7f/0xe0 do_syscall_64+0x3f/0x90 entry_SYSCALL_64_after_hwframe+0x6e/0xd8 Freed by task 27534: kasan_save_stack+0x33/0x60 kasan_set_track+0x25/0x30 kasan_save_free_info+0x2b/0x50 ____kasan_slab_free+0x126/0x170 slab_free_freelist_hook+0xd0/0x1e0 __kmem_cache_free+0x9d/0x1b0 open_cached_dir+0xff5/0x1240 [cifs] smb2_query_info_compound+0x5c3/0x6d0 [cifs] smb2_queryfs+0xc2/0x2c0 [cifs]
This is a race between open_cached_dir() and cached_dir_lease_break() where the cache entry for the open directory handle receives a lease break while creating it. And before returning from open_cached_dir(), we put the last reference of the new @cfid because of !@cfid->has_lease.
Besides the UAF, while running xfstests a lot of missed lease breaks have been noticed in tests that run several concurrent statfs(2) calls on those cached fids
CIFS: VFS: \\w22-root1.gandalf.test No task to wake, unknown frame...
CIFS: VFS: \\w22-root1.gandalf.test Cmd: 18 Err: 0x0 Flags: 0x1...
CIFS: VFS: \\w22-root1.gandalf.test smb buf 00000000715bfe83 len 108
CIFS: VFS: Dump pending requests:
CIFS: VFS: \\w22-root1.gandalf.test No task to wake, unknown frame...
CIFS: VFS: \\w22-root1.gandalf.test Cmd: 18 Err: 0x0 Flags: 0x1...
CIFS: VFS: \\w22-root1.gandalf.test smb buf 000000005aa7316e len 108
...
To fix both, in open_cached_dir() ensure that @cfid->has_lease is set right before sending out compounded request so that any potential lease break will be get processed by demultiplex thread while we're still caching @cfid. And, if open failed for some reason, re-check @cfid->has_lease to decide whether or not put lease reference.
--- fs/smb/client/cached_dir.c | 97 +++++++++++++++++++++++--------------- 1 file changed, 60 insertions(+), 37 deletions(-)
fs/smb/client/cached_dir.c +60 -37
@@ -30,7 +30,7 @@ static struct cached_fid *find_or_create_cached_dir(struct cached_fids *cfids,* fully cached or it may be in the process of * being deleted due to a lease break. */ -if (!cfid->has_lease) { +if (!cfid->time || !cfid->has_lease) {spin_unlock (&cfids->cfid_list_lock);return NULL ; }
@@ -188,10 +188,20 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, npath =path_no_prefix (cifs_sb, path);if (IS_ERR (npath)) { rc =PTR_ERR (npath); -kfree (utf16_path); -return rc; + goto out; } +if (!npath[0 ]) { + dentry =dget (cifs_sb->root); + }else { + dentry =path_to_dentry (cifs_sb, npath); +if (IS_ERR (dentry)) { + rc = -ENOENT ; + goto out; + } + } + cfid->dentry = dentry; +/* * We do not hold the lock for the open because in case * SMB2_open needs to reconnect.
@@ -253,6 +263,15 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,smb2_set_related (&rqst[1 ]); +/* +* Set @cfid->has_lease to true before sending out compounded request so +* its lease reference can be put in cached_dir_lease_break() due to a +* potential lease break right after the request is sent or while @cfid +* is still being cached. Concurrent processes won't be to use it yet +* due to @cfid->time being zero. +*/ + cfid->has_lease = true; + rc =compound_send_recv (xid, ses, server, flags,2 , rqst, resp_buftype, rsp_iov);
@@ -267,6 +286,8 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, cfid->tcon = tcon; cfid->is_open = true; +spin_lock (&cfids->cfid_list_lock); + o_rsp = (struct smb2_create_rsp *)rsp_iov[0 ].iov_base; oparms.fid->persistent_fid = o_rsp->PersistentFileId; oparms.fid->volatile_fid = o_rsp->VolatileFileId;
@@ -274,20 +295,30 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, oparms.fid->mid =le64_to_cpu (o_rsp->hdr.MessageId);#endif /* CIFS_DEBUG2 */ -if (o_rsp->OplockLevel !=SMB2_OPLOCK_LEVEL_LEASE ) + rc = -EINVAL ; +if (o_rsp->OplockLevel !=SMB2_OPLOCK_LEVEL_LEASE ) { +spin_unlock (&cfids->cfid_list_lock); goto oshr_free; + } rc =smb2_parse_contexts (server, rsp_iov, &oparms.fid->epoch, oparms.fid->lease_key, &oplock,NULL ,NULL ); -if (rc) +if (rc) { +spin_unlock (&cfids->cfid_list_lock); goto oshr_free; -if (!(oplock &SMB2_LEASE_READ_CACHING_HE )) + } + rc = -EINVAL ; +if (!(oplock &SMB2_LEASE_READ_CACHING_HE )) { +spin_unlock (&cfids->cfid_list_lock); goto oshr_free; + } qi_rsp = (struct smb2_query_info_rsp *)rsp_iov[1 ].iov_base; -if (le32_to_cpu (qi_rsp->OutputBufferLength) <sizeof (struct smb2_file_all_info )) +if (le32_to_cpu (qi_rsp->OutputBufferLength) <sizeof (struct smb2_file_all_info )) { +spin_unlock (&cfids->cfid_list_lock); goto oshr_free; + } if (!smb2_validate_and_copy_iov( le16_to_cpu(qi_rsp->OutputBufferOffset), sizeof(struct smb2_file_all_info),
@@ -295,57 +326,49 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, (char *)&cfid->file_all_info)) cfid->file_all_info_is_valid = true; -if (!npath[0 ]) - dentry =dget (cifs_sb->root); -else { - dentry =path_to_dentry (cifs_sb, npath); -if (IS_ERR (dentry)) { - rc = -ENOENT ; - goto oshr_free; - } - } - cfid->dentry = dentry; cfid->time = jiffies; - cfid->has_lease = true; +spin_unlock (&cfids->cfid_list_lock); +/* At this point the directory handle is fully cached */ + rc =0 ; oshr_free: -kfree (utf16_path);SMB2_open_free (&rqst[0 ]);SMB2_query_info_free (&rqst[1 ]);free_rsp_buf (resp_buftype[0 ], rsp_iov[0 ].iov_base);free_rsp_buf (resp_buftype[1 ], rsp_iov[1 ].iov_base); -spin_lock (&cfids->cfid_list_lock); -if (rc && !cfid->has_lease) { +if (rc) { +spin_lock (&cfids->cfid_list_lock);if (cfid->on_list) {list_del (&cfid->entry); cfid->on_list = false; cfids->num_entries--; } - rc = -ENOENT ; - } -spin_unlock (&cfids->cfid_list_lock); -if (!rc && !cfid->has_lease) { -/* -* We are guaranteed to have two references at this point. -* One for the caller and one for a potential lease. -* Release the Lease-ref so that the directory will be closed -* when the caller closes the cached handle. -*/ -kref_put (&cfid->refcount, smb2_close_cached_fid); +if (cfid->has_lease) { +/* +* We are guaranteed to have two references at this +* point. One for the caller and one for a potential +* lease. Release the Lease-ref so that the directory +* will be closed when the caller closes the cached +* handle. +*/ + cfid->has_lease = false; +spin_unlock (&cfids->cfid_list_lock); +kref_put (&cfid->refcount, smb2_close_cached_fid); + goto out; + } +spin_unlock (&cfids->cfid_list_lock); } +out:if (rc) {if (cfid->is_open)SMB2_close (0 , cfid->tcon, cfid->fid.persistent_fid, cfid->fid.volatile_fid);free_cached_dir (cfid); - cfid =NULL ; - } - -if (rc ==0 ) { + }else { *ret_cfid = cfid;atomic_inc (&tcon->num_remote_opens); } - +kfree (utf16_path);return rc; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 559/1887 riscv: VMAP_STACK overflow detection thread-safe
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Deepak Gupta <[email protected]>
[ Upstream commit be97d0db5f44c0674480cb79ac6f5b0529b84c76 ]
commit 31da94c25aea ("riscv: add VMAP_STACK overflow detection") added support for CONFIG_VMAP_STACK. If overflow is detected, CPU switches to shadow_stack temporarily before switching finally to per-cpu overflow_stack.
If two CPUs/harts are racing and end up in over flowing kernel stack, one or both will end up corrupting each other state because shadow_stack is not per-cpu. This patch optimizes per-cpu overflow stack switch by directly picking per-cpu overflow_stack and gets rid of shadow_stack.
Following are the changes in this patch
- Defines an asm macro to obtain per-cpu symbols in destination register.
- In entry.S, when overflow is detected, per-cpu overflow stack is located using per-cpu asm macro. Computing per-cpu symbol requires a temporary register. x31 is saved away into CSR_SCRATCH (CSR_SCRATCH is anyways zero since we're in kernel).
Please see Links for additional relevant disccussion and alternative solution.
Tested by echo EXHAUST_STACK > /sys/kernel/debug/provoke-crash/DIRECT Kernel crash log below
Insufficient stack space to handle exception!/debug/provoke-crash/DIRECT
Task stack: [0xff20000010a98000..0xff20000010a9c000]
Overflow stack: [0xff600001f7d98370..0xff600001f7d99370]
CPU: 1 PID: 205 Comm: bash Not tainted 6.1.0-rc2-00001-g328a1f96f7b9 #34 Hardware name: riscv-virtio,qemu (DT) epc : __memset+0x60/0xfc ra : recursive_loop+0x48/0xc6 [lkdtm] epc : ffffffff808de0e4 ra : ffffffff0163a752 sp : ff20000010a97e80 gp : ffffffff815c0330 tp : ff600000820ea280 t0 : ff20000010a97e88 t1 : 000000000000002e t2 : 3233206874706564 s0 : ff20000010a982b0 s1 : 0000000000000012 a0 : ff20000010a97e88 a1 : 0000000000000000 a2 : 0000000000000400 a3 : ff20000010a98288 a4 : 0000000000000000 a5 : 0000000000000000 a6 : fffffffffffe43f0 a7 : 00007fffffffffff s2 : ff20000010a97e88 s3 : ffffffff01644680 s4 : ff20000010a9be90 s5 : ff600000842ba6c0 s6 : 00aaaaaac29e42b0 s7 : 00fffffff0aa3684 s8 : 00aaaaaac2978040 s9 : 0000000000000065 s10: 00ffffff8a7cad10 s11: 00ffffff8a76a4e0 t3 : ffffffff815dbaf4 t4 : ffffffff815dbaf4 t5 : ffffffff815dbab8 t6 : ff20000010a9bb48 status: 0000000200000120 badaddr: ff20000010a97e88 cause: 000000000000000f Kernel panic - not syncing: Kernel stack overflow CPU: 1 PID: 205 Comm: bash Not tainted 6.1.0-rc2-00001-g328a1f96f7b9 #34 Hardware name: riscv-virtio,qemu (DT) Call Trace: [<ffffffff80006754>] dump_backtrace+0x30/0x38 [<ffffffff808de798>] show_stack+0x40/0x4c [<ffffffff808ea2a8>] dump_stack_lvl+0x44/0x5c [<ffffffff808ea2d8>] dump_stack+0x18/0x20 [<ffffffff808dec06>] panic+0x126/0x2fe [<ffffffff800065ea>] walk_stackframe+0x0/0xf0 [<ffffffff0163a752>] recursive_loop+0x48/0xc6 [lkdtm] SMP: stopping secondary CPUs ---[ end Kernel panic - not syncing: Kernel stack overflow ]---
--- arch/riscv/include/asm/asm-prototypes.h | 1 - arch/riscv/include/asm/asm.h | 23 ++++++++ arch/riscv/include/asm/thread_info.h | 3 -- arch/riscv/kernel/asm-offsets.c | 1 + arch/riscv/kernel/entry.S | 70 ++++--------------------- arch/riscv/kernel/traps.c | 30 +---------- 6 files changed, 35 insertions(+), 93 deletions(-)
arch/riscv/include/asm/asm-prototypes.h +0 -1
@@ -25,7 +25,6 @@ DECLARE_DO_ERROR_INFO(do_trap_ecall_s);DECLARE_DO_ERROR_INFO (do_trap_ecall_m);DECLARE_DO_ERROR_INFO (do_trap_break); -asmlinkage unsigned long get_overflow_stack (void ); asmlinkage void handle_bad_stack(struct pt_regs *regs);#endif /* _ASM_RISCV_PROTOTYPES_H */
arch/riscv/include/asm/asm.h +23 -0
@@ -69,6 +69,7 @@#endif #ifdef __ASSEMBLY__ +#include <asm/asm-offsets.h> /* Common assembly source macros */
@@ -81,6 +82,28 @@ .endr .endm +#ifdef CONFIG_SMP +#ifdef CONFIG_32BIT +#define PER_CPU_OFFSET_SHIFT 2 +#else +#define PER_CPU_OFFSET_SHIFT 3 +#endif + +.macro asm_per_cpu dst sym tmp + REG_L \tmp, TASK_TI_CPU_NUM(tp) + slli \tmp, \tmp, PER_CPU_OFFSET_SHIFT + la \dst, __per_cpu_offset + add \dst, \dst, \tmp + REG_L \tmp, 0(\dst) + la \dst, \sym + add \dst, \dst, \tmp +.endm +#else /* CONFIG_SMP */ +.macro asm_per_cpu dst sym tmp + la \dst, \sym +.endm +#endif /* CONFIG_SMP */ + #endif /* __ASSEMBLY__ */ #endif /* _ASM_RISCV_ASM_H */
arch/riscv/include/asm/thread_info.h +0 -3
@@ -42,9 +42,6 @@#ifndef __ASSEMBLY__ -extern long shadow_stack[SHADOW_OVERFLOW_STACK_SIZE /sizeof (long )]; -extern unsigned long spin_shadow_stack; -#include <asm/processor.h> #include <asm/csr.h>
arch/riscv/kernel/asm-offsets.c +1 -0
@@ -37,6 +37,7 @@ void asm_offsets(void)OFFSET (TASK_TI_KERNEL_SP , task_struct, thread_info.kernel_sp);OFFSET (TASK_TI_USER_SP , task_struct, thread_info.user_sp); +OFFSET (TASK_TI_CPU_NUM , task_struct, thread_info.cpu);OFFSET (TASK_THREAD_F0 , task_struct, thread.fstate.f[0 ]);OFFSET (TASK_THREAD_F1 , task_struct, thread.fstate.f[1 ]);OFFSET (TASK_THREAD_F2 , task_struct, thread.fstate.f[2 ]);
arch/riscv/kernel/entry.S +10 -60
86 lines not on this page · raw message
arch/riscv/kernel/traps.c +1 -29
@@ -209,42 +209,14 @@ int is_valid_bugaddr(unsigned long pc)#endif /* CONFIG_GENERIC_BUG */ #ifdef CONFIG_VMAP_STACK -static DEFINE_PER_CPU(unsigned long [OVERFLOW_STACK_SIZE/sizeof(long)], +DEFINE_PER_CPU(unsigned long [OVERFLOW_STACK_SIZE/sizeof(long)], overflow_stack)__aligned(16); -/* -* shadow stack, handled_ kernel_ stack_ overflow(in kernel/entry.S) is used -* to get per-cpu overflow stack(get_overflow_stack). -*/ -long shadow_stack[SHADOW_OVERFLOW_STACK_SIZE /sizeof (long )]__aligned(16) ; -asmlinkage unsigned long get_overflow_stack (void ) -{ -return (unsigned long )this_cpu_ptr (overflow_stack) + -OVERFLOW_STACK_SIZE ; -} - -/* -* A pseudo spinlock to protect the shadow stack from being used by multiple -* harts concurrently. This isn't a real spinlock because the lock side must -* be taken without a valid stack and only a single register, it's only taken -* while in the process of panicing anyway so the performance and error -* checking a proper spinlock gives us doesn't matter. -*/ -unsigned long spin_shadow_stack; asmlinkage void handle_bad_stack(struct pt_regs *regs) {unsigned long tsk_stk = (unsigned long )current->stack;unsigned long ovf_stk = (unsigned long )this_cpu_ptr (overflow_stack); -/* -* We're done with the shadow stack by this point, as we're on the -* overflow stack. Tell any other concurrent overflowing harts that -* they can proceed with panicing by releasing the pseudo-spinlock. -* -* This pairs with an amoswap.aq in handle_kernel_stack_overflow. -*/ -smp_store_release (&spin_shadow_stack,0 ); -console_verbose ();pr_emerg ("Insufficient stack space to handle exception!\n" );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 560/1887 f2fs: split initial and dynamic conditions for extent_cache
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jaegeuk Kim <[email protected]>
[ Upstream commit f803982190f0265fd36cf84670aa6daefc2b0768 ]
Let's allocate the extent_cache tree without dynamic conditions to avoid a missing condition causing a panic as below.
# create a file w/ a compressed flag # disable the compression # panic while updating extent_cache
F2FS-fs (dm-64): Swapfile: last extent is not aligned to section
F2FS-fs (dm-64): Swapfile (3) is not align to section: 1) creat(), 2) ioctl(F2FS_IOC_SET_PIN_FILE), 3) fallocate(2097152 * N) Adding 124996k swap on ./swap-file. Priority:0 extents:2 across:17179494468k
==================================================================
BUG: KASAN: null-ptr-deref in instrument_atomic_read_write out/common/include/linux/instrumented.h:101 [inline] BUG: KASAN: null-ptr-deref in atomic_try_cmpxchg_acquire out/common/include/asm-generic/atomic-instrumented.h:705 [inline] BUG: KASAN: null-ptr-deref in queued_write_lock out/common/include/asm-generic/qrwlock.h:92 [inline] BUG: KASAN: null-ptr-deref in __raw_write_lock out/common/include/linux/rwlock_api_smp.h:211 [inline] BUG: KASAN: null-ptr-deref in _raw_write_lock+0x5a/0x110 out/common/kernel/locking/spinlock.c:295 Write of size 4 at addr 0000000000000030 by task syz-executor154/3327 CPU: 0 PID: 3327 Comm: syz-executor154 Tainted: G O 5.10.185 #1 Hardware name: emulation qemu-x86/qemu-x86, BIOS 2023.01-21885-gb3cc1cd24d 01/01/2023 Call Trace: __dump_stack out/common/lib/dump_stack.c:77 [inline] dump_stack_lvl+0x17e/0x1c4 out/common/lib/dump_stack.c:118 __kasan_report+0x16c/0x260 out/common/mm/kasan/report.c:415 kasan_report+0x51/0x70 out/common/mm/kasan/report.c:428 kasan_check_range+0x2f3/0x340 out/common/mm/kasan/generic.c:186 __kasan_check_write+0x14/0x20 out/common/mm/kasan/shadow.c:37 instrument_atomic_read_write out/common/include/linux/instrumented.h:101 [inline] atomic_try_cmpxchg_acquire out/common/include/asm-generic/atomic-instrumented.h:705 [inline] queued_write_lock out/common/include/asm-generic/qrwlock.h:92 [inline] __raw_write_lock out/common/include/linux/rwlock_api_smp.h:211 [inline] _raw_write_lock+0x5a/0x110 out/common/kernel/locking/spinlock.c:295 __drop_extent_tree+0xdf/0x2f0 out/common/fs/f2fs/extent_cache.c:1155 f2fs_drop_extent_tree+0x17/0x30 out/common/fs/f2fs/extent_cache.c:1172 f2fs_insert_range out/common/fs/f2fs/file.c:1600 [inline] f2fs_fallocate+0x19fd/0x1f40 out/common/fs/f2fs/file.c:1764 vfs_fallocate+0x514/0x9b0 out/common/fs/open.c:310 ksys_fallocate out/common/fs/open.c:333 [inline] __do_sys_fallocate out/common/fs/open.c:341 [inline] __se_sys_fallocate out/common/fs/open.c:339 [inline] __x64_sys_fallocate+0xb8/0x100 out/common/fs/open.c:339 do_syscall_64+0x35/0x50 out/common/arch/x86/entry/common.c:46
--- fs/f2fs/extent_cache.c | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-)
fs/f2fs/extent_cache.c +13 -16
@@ -58,24 +58,11 @@ static void __set_extent_info(struct extent_info *ei, } } -static bool __may_read_extent_tree (struct inode *inode) -{ -struct f2fs_sb_info *sbi =F2FS_I_SB (inode); - -if (!test_opt (sbi,READ_EXTENT_CACHE )) -return false; -if (is_inode_flag_set (inode,FI_NO_EXTENT )) -return false; -if (is_inode_flag_set (inode,FI_COMPRESSED_FILE ) && - !f2fs_sb_has_readonly (sbi)) -return false; -return S_ISREG (inode->i_mode); -} -static bool __init_may_extent_tree (struct inode *inode,enum extent_type type) {if (type ==EX_READ ) -return __may_read_extent_tree (inode); +return test_opt (F2FS_I_SB (inode),READ_EXTENT_CACHE ) && +S_ISREG (inode->i_mode);return false; }
@@ -88,7 +75,17 @@ static bool __may_extent_tree(struct inode *inode, enum extent_type type)if (list_empty (&F2FS_I_SB (inode)->s_list))return false; -return __init_may_extent_tree (inode, type); +if (!__init_may_extent_tree (inode, type)) +return false; + +if (type ==EX_READ ) { +if (is_inode_flag_set (inode,FI_NO_EXTENT )) +return false; +if (is_inode_flag_set (inode,FI_COMPRESSED_FILE ) && + !f2fs_sb_has_readonly (F2FS_I_SB (inode))) +return false; + } +return true; } static void __try_update_largest_extent(struct extent_tree *et,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 561/1887 cxl/port: Fix delete_endpoint() vs parent unregistration race
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dan Williams <[email protected]>
[ Upstream commit 8d2ad999ca3c64cb08cf6a58d227b9d9e746d708 ]
The CXL subsystem, at cxl_mem ->probe() time, establishes a lineage of ports (struct cxl_port objects) between an endpoint and the root of a CXL topology. Each port including the endpoint port is attached to the cxl_port driver.
Given that setup, it follows that when either any port in that lineage goes through a cxl_port ->remove() event, or the memdev goes through a cxl_mem ->remove() event. The hierarchy below the removed port, or the entire hierarchy if the memdev is removed needs to come down.
The delete_endpoint() callback is careful to check whether it is being called to tear down the hierarchy, or if it is only being called to teardown the memdev because an ancestor port is going through ->remove().
That care needs to take the device_lock() of the endpoint's parent. Which requires 2 bugs to be fixed:
1/ A reference on the parent is needed to prevent use-after-free
scenarios like this signature:
BUG: spinlock bad magic on CPU#0, kworker/u56:0/11
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS edk2-20230524-3.fc38 05/24/2023
Workqueue: cxl_port detach_memdev [cxl_core]
RIP: 0010:spin_bug+0x65/0xa0
Call Trace:
do_raw_spin_lock+0x69/0xa0
__mutex_lock+0x695/0xb80
delete_endpoint+0xad/0x150 [cxl_core]
devres_release_all+0xb8/0x110
device_unbind_cleanup+0xe/0x70
device_release_driver_internal+0x1d2/0x210
detach_memdev+0x15/0x20 [cxl_core]
process_one_work+0x1e3/0x4c0
worker_thread+0x1dd/0x3d02/ In the case of RCH topologies, the parent device that needs to be
locked is not always @port->dev as returned by cxl_mem_find_port(), use endpoint->dev.parent instead.
--- drivers/cxl/core/port.c | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-)
drivers/cxl/core/port.c +9 -14
@@ -1094,29 +1094,24 @@ static void delete_endpoint(void *data) {struct cxl_memdev *cxlmd = data;struct cxl_port *endpoint =dev_get_drvdata (&cxlmd->dev); -struct cxl_port *parent_port; -struct device *parent; +struct device *host = endpoint->dev.parent; - parent_port =cxl_mem_find_port (cxlmd,NULL ); -if (!parent_port) - goto out; - parent = &parent_port->dev; - -device_lock (parent); -if (parent->driver && !endpoint->dead) { -devm_release_action (parent, cxl_unlink_uport, endpoint); -devm_release_action (parent, unregister_port, endpoint); +device_lock (host); +if (host->driver && !endpoint->dead) { +devm_release_action (host, cxl_unlink_uport, endpoint); +devm_release_action (host, unregister_port, endpoint); } -device_unlock (parent); -put_device (parent); -out: +device_unlock (host);put_device (&endpoint->dev); +put_device (host); }int cxl_endpoint_autoremove (struct cxl_memdev *cxlmd,struct cxl_port *endpoint) { +struct device *host = endpoint->dev.parent;struct device *dev = &cxlmd->dev; +get_device (host);get_device (&endpoint->dev);dev_set_drvdata (dev, endpoint);return devm_add_action_or_reset (dev, delete_endpoint, cxlmd);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 562/1887 media: mediatek: vcodec: Only free buffer VA that is not NULL
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fei Shao <[email protected]>
[ Upstream commit eb005c801ec70ff4307727bd3bd6e8280169ef32 ]
In the MediaTek vcodec driver, while mtk_vcodec_mem_free() is mostly called only when the buffer to free exists, there are some instances that didn't do the check and triggered warnings in practice.
We believe those checks were forgotten unintentionally. Add the checks back to fix the warnings.
--- drivers/media/platform/mediatek/vcodec/venc/venc_h264_if.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
drivers/media/platform/mediatek/vcodec/venc/venc_h264_if.c +3 -2
@@ -304,11 +304,12 @@ static void h264_enc_free_work_buf(struct venc_h264_inst *inst)* other buffers need to be freed by AP. */ for (i =0 ; i <VENC_H264_VPU_WORK_BUF_MAX ; i++) { -if (i !=VENC_H264_VPU_WORK_BUF_SKIP_FRAME ) +if (i !=VENC_H264_VPU_WORK_BUF_SKIP_FRAME && inst->work_bufs[i].va)mtk_vcodec_mem_free (inst->ctx, &inst->work_bufs[i]); } -mtk_vcodec_mem_free (inst->ctx, &inst->pps_buf); +if (inst->pps_buf.va) +mtk_vcodec_mem_free (inst->ctx, &inst->pps_buf);mtk_vcodec_debug_leave (inst); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 563/1887 ext4: avoid deadlock in fs reclaim with page writeback
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jan Kara <[email protected]>
[ Upstream commit 00d873c17e29cc32d90ca852b82685f1673acaa5 ]
Ext4 has a filesystem wide lock protecting ext4_writepages() calls to avoid races with switching of journalled data flag or inode format. This lock can however cause a deadlock like:
CPU0 CPU1
ext4_writepages()
percpu_down_read(sbi->s_writepages_rwsem);
ext4_change_inode_journal_flag()
percpu_down_write(sbi->s_writepages_rwsem);
- blocks, all readers block from now on
ext4_do_writepages()
ext4_init_io_end()
kmem_cache_zalloc(io_end_cachep, GFP_KERNEL)
fs_reclaim frees dentry...
dentry_unlink_inode()
iput() - last ref =>
iput_final() - inode dirty =>
write_inode_now()...
ext4_writepages() tries to acquire sbi->s_writepages_rwsem
and blocks forever
Make sure we cannot recurse into filesystem reclaim from writeback code to avoid the deadlock.
--- fs/ext4/ext4.h | 24 ++++++++++++++++++++++++ fs/ext4/inode.c | 18 ++++++++++-------- fs/ext4/migrate.c | 11 ++++++----- 3 files changed, 40 insertions(+), 13 deletions(-)
fs/ext4/ext4.h +24 -0
@@ -1778,6 +1778,30 @@ static inline struct ext4_inode_info *EXT4_I(struct inode *inode) return container_of(inode, struct ext4_inode_info, vfs_inode); } +static inline int ext4_writepages_down_read (struct super_block *sb) +{ +percpu_down_read (&EXT4_SB (sb)->s_writepages_rwsem); +return memalloc_nofs_save (); +} + +static inline void ext4_writepages_up_read (struct super_block *sb,int ctx) +{ +memalloc_nofs_restore (ctx); +percpu_up_read (&EXT4_SB (sb)->s_writepages_rwsem); +} + +static inline int ext4_writepages_down_write (struct super_block *sb) +{ +percpu_down_write (&EXT4_SB (sb)->s_writepages_rwsem); +return memalloc_nofs_save (); +} + +static inline void ext4_writepages_up_write (struct super_block *sb,int ctx) +{ +memalloc_nofs_restore (ctx); +percpu_up_write (&EXT4_SB (sb)->s_writepages_rwsem); +} +static inline int ext4_valid_inum (struct super_block *sb,unsigned long ino) { return ino == EXT4_ROOT_INO ||
fs/ext4/inode.c +10 -8
@@ -2710,11 +2710,12 @@ static int ext4_writepages(struct address_space *mapping,struct ext4_sb_info *sbi =EXT4_SB (mapping->host->i_sb);struct blk_plug plug;bool give_up_on_write = false; +int alloc_ctx;if (unlikely (ext4_forced_shutdown (EXT4_SB (inode->i_sb))))return -EIO ; -percpu_down_read (&sbi->s_writepages_rwsem); + alloc_ctx =ext4_writepages_down_read (inode->i_sb);trace_ext4_writepages (inode, wbc);/*
@@ -2924,7 +2925,7 @@ static int ext4_writepages(struct address_space *mapping, out_writepages:trace_ext4_writepages_result (inode, wbc, ret, nr_to_write - wbc->nr_to_write); -percpu_up_read (&sbi->s_writepages_rwsem); +ext4_writepages_up_read (inode->i_sb, alloc_ctx);return ret; }
@@ -2935,17 +2936,18 @@ static int ext4_dax_writepages(struct address_space *mapping,long nr_to_write = wbc->nr_to_write;struct inode *inode = mapping->host;struct ext4_sb_info *sbi =EXT4_SB (mapping->host->i_sb); +int alloc_ctx;if (unlikely (ext4_forced_shutdown (EXT4_SB (inode->i_sb))))return -EIO ; -percpu_down_read (&sbi->s_writepages_rwsem); + alloc_ctx =ext4_writepages_down_read (inode->i_sb);trace_ext4_writepages (inode, wbc); ret =dax_writeback_mapping_range (mapping, sbi->s_daxdev, wbc);trace_ext4_writepages_result (inode, wbc, ret, nr_to_write - wbc->nr_to_write); -percpu_up_read (&sbi->s_writepages_rwsem); +ext4_writepages_up_read (inode->i_sb, alloc_ctx);return ret; }
@@ -6172,7 +6174,7 @@ int ext4_change_inode_journal_flag(struct inode *inode, int val)journal_t *journal;handle_t *handle;int err; -struct ext4_sb_info *sbi =EXT4_SB (inode->i_sb); +int alloc_ctx;/* * We have to be very careful here: changing a data block's
@@ -6210,7 +6212,7 @@ int ext4_change_inode_journal_flag(struct inode *inode, int val) } } -percpu_down_write (&sbi->s_writepages_rwsem); + alloc_ctx =ext4_writepages_down_write (inode->i_sb);jbd2_journal_lock_updates (journal);/*
@@ -6227,7 +6229,7 @@ int ext4_change_inode_journal_flag(struct inode *inode, int val) err =jbd2_journal_flush (journal,0 );if (err <0 ) {jbd2_journal_unlock_updates (journal); -percpu_up_write (&sbi->s_writepages_rwsem); +ext4_writepages_up_write (inode->i_sb, alloc_ctx);return err; }ext4_clear_inode_flag (inode,EXT4_INODE_JOURNAL_DATA );
@@ -6235,7 +6237,7 @@ int ext4_change_inode_journal_flag(struct inode *inode, int val)ext4_set_aops (inode);jbd2_journal_unlock_updates (journal); -percpu_up_write (&sbi->s_writepages_rwsem); +ext4_writepages_up_write (inode->i_sb, alloc_ctx);if (val)filemap_invalidate_unlock (inode->i_mapping);
fs/ext4/migrate.c +6 -5
@@ -408,7 +408,6 @@ static int free_ext_block(handle_t *handle, struct inode *inode)int ext4_ext_migrate (struct inode *inode) { -struct ext4_sb_info *sbi =EXT4_SB (inode->i_sb);handle_t *handle;int retval =0 , i;__le32 *i_data;
@@ -418,6 +417,7 @@ int ext4_ext_migrate(struct inode *inode)unsigned long max_entries;__u32 goal, tmp_csum_seed;uid_t owner[2 ]; +int alloc_ctx;/* * If the filesystem does not support extents, or the inode
@@ -434,7 +434,7 @@ int ext4_ext_migrate(struct inode *inode)*/ return retval; -percpu_down_write (&sbi->s_writepages_rwsem); + alloc_ctx =ext4_writepages_down_write (inode->i_sb);/* * Worst case we can touch the allocation bitmaps and a block
@@ -586,7 +586,7 @@ int ext4_ext_migrate(struct inode *inode)unlock_new_inode (tmp_inode);iput (tmp_inode); out_unlock: -percpu_up_write (&sbi->s_writepages_rwsem); +ext4_writepages_up_write (inode->i_sb, alloc_ctx);return retval; }
@@ -605,6 +605,7 @@ int ext4_ind_migrate(struct inode *inode)ext4_fsblk_t blk;handle_t *handle;int ret, ret2 =0 ; +int alloc_ctx;if (!ext4_has_feature_extents (inode->i_sb) || (!ext4_test_inode_flag (inode,EXT4_INODE_EXTENTS )))
@@ -621,7 +622,7 @@ int ext4_ind_migrate(struct inode *inode)if (test_opt (inode->i_sb,DELALLOC ))ext4_alloc_da_blocks (inode); -percpu_down_write (&sbi->s_writepages_rwsem); + alloc_ctx =ext4_writepages_down_write (inode->i_sb); handle =ext4_journal_start (inode,EXT4_HT_MIGRATE ,1 );if (IS_ERR (handle)) {
@@ -665,6 +666,6 @@ int ext4_ind_migrate(struct inode *inode)up_write (&EXT4_I (inode)->i_data_sem);ext4_journal_stop (handle); out_unlock: -percpu_up_write (&sbi->s_writepages_rwsem); +ext4_writepages_up_write (inode->i_sb, alloc_ctx);return ret; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 564/1887 coresight: Fix memory leak in acpi_buffer->pointer
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Junhao He <[email protected]>
[ Upstream commit 1a9e02673e2550f5612099e64e8761f0c8fc0f50 ]
There are memory leaks reported by kmemleak:
...
unreferenced object 0xffff00213c141000 (size 1024):
comm "systemd-udevd", pid 2123, jiffies 4294909467 (age 6062.160s) hex dump (first 32 bytes):
04 00 00 00 02 00 00 00 18 10 14 3c 21 00 ff ff ...........<!...
00 00 00 00 00 00 00 00 03 00 00 00 10 00 00 00 ................
backtrace:
[<000000004b7c9001>] __kmem_cache_alloc_node+0x2f8/0x348
[<00000000b0fc7ceb>] __kmalloc+0x58/0x108
[<0000000064ff4695>] acpi_os_allocate+0x2c/0x68
[<000000007d57d116>] acpi_ut_initialize_buffer+0x54/0xe0
[<0000000024583908>] acpi_evaluate_object+0x388/0x438
[<0000000017b2e72b>] acpi_evaluate_object_typed+0xe8/0x240
[<000000005df0eac2>] coresight_get_platform_data+0x1b4/0x988 [coresight]...
The ACPI buffer memory (buf.pointer) should be freed. But the buffer is also used after returning from acpi_get_dsd_graph(). Move the temporary variables buf to acpi_coresight_parse_graph(), and free it before the function return to prevent memory leak.
--- .../hwtracing/coresight/coresight-platform.c | 48 ++++++++++++------- 1 file changed, 31 insertions(+), 17 deletions(-)
drivers/hwtracing/coresight/coresight-platform.c +31 -17
@@ -520,19 +520,18 @@ static inline bool acpi_validate_dsd_graph(const union acpi_object *graph)/* acpi_get_dsd_graph - Find the _DSD Graph property for the given device. */ static const union acpi_object * -acpi_get_dsd_graph (struct acpi_device *adev) +acpi_get_dsd_graph (struct acpi_device *adev,struct acpi_buffer *buf) {int i; -struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER };acpi_status status;const union acpi_object *dsd; status =acpi_evaluate_object_typed (adev->handle,"_DSD" ,NULL , - &buf,ACPI_TYPE_PACKAGE ); + buf,ACPI_TYPE_PACKAGE );if (ACPI_FAILURE (status))return NULL ; - dsd = buf.pointer; + dsd = buf->pointer;/* * _DSD property consists tuples { Prop_UUID, Package() }
@@ -583,12 +582,12 @@ acpi_validate_coresight_graph(const union acpi_object *cs_graph)* returns NULL. */ static const union acpi_object * -acpi_get_coresight_graph (struct acpi_device *adev) +acpi_get_coresight_graph (struct acpi_device *adev,struct acpi_buffer *buf) {const union acpi_object *graph_list, *graph;int i, nr_graphs; - graph_list =acpi_get_dsd_graph (adev); + graph_list =acpi_get_dsd_graph (adev, buf);if (!graph_list)return graph_list;
@@ -688,18 +687,22 @@ static int acpi_coresight_parse_link(struct acpi_device *adev,static int acpi_coresight_parse_graph (struct acpi_device *adev,struct coresight_platform_data *pdata) { -int rc, i, nlinks; +int rc =0 ; +int i, nlinks;const union acpi_object *graph;struct coresight_connection *conns, *ptr; +struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER ,NULL }; pdata->nr_inport = pdata->nr_outport =0 ; - graph =acpi_get_coresight_graph (adev); -if (!graph) -return -ENOENT ; + graph =acpi_get_coresight_graph (adev, &buf); +if (!graph) { + rc = -ENOENT ; + goto free; + } nlinks = graph->package.elements[2 ].integer.value;if (!nlinks) -return 0 ; + goto free;/* * To avoid scanning the table twice (once for finding the number of
@@ -708,16 +711,20 @@ static int acpi_coresight_parse_graph(struct acpi_device *adev,* it to the pdata. */ conns =devm_kcalloc (&adev->dev, nlinks,sizeof (*conns),GFP_KERNEL ); -if (!conns) -return -ENOMEM ; +if (!conns) { + rc = -ENOMEM ; + goto free; + } ptr = conns;for (i =0 ; i < nlinks; i++) {const union acpi_object *link = &graph->package.elements[3 + i];int dir; dir =acpi_coresight_parse_link (adev, link, ptr); -if (dir <0 ) -return dir; +if (dir <0 ) { + rc = dir; + goto free; + }if (dir ==ACPI_CORESIGHT_LINK_MASTER ) {if (ptr->outport >= pdata->nr_outport)
@@ -739,7 +746,7 @@ static int acpi_coresight_parse_graph(struct acpi_device *adev, rc =coresight_alloc_conns (&adev->dev, pdata);if (rc) -return rc; + goto free;/* Copy the connection information to the final location */ for (i =0 ; conns + i < ptr; i++) {
@@ -751,7 +758,14 @@ static int acpi_coresight_parse_graph(struct acpi_device *adev, }devm_kfree (&adev->dev, conns); -return 0 ; +free: +/* +* When ACPI fails to alloc a buffer, it will free the buffer +* created via ACPI_ALLOCATE_BUFFER and set to NULL. +* ACPI_FREE can handle NULL pointers, so free it directly. +*/ +ACPI_FREE (buf.pointer); +return rc; }/*
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 565/1887 net/mlx5e: xsk: Fix crash on regular rq reactivation
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dragos Tatulea <[email protected]>
[ Upstream commit 39646d9bcd1a65d2396328026626859a1dab59d7 ]
When the regular rq is reactivated after the XSK socket is closed it could be reading stale cqes which eventually corrupts the rq. This leads to no more traffic being received on the regular rq and a crash on the next close or deactivation of the rq.
Kal Cuttler Conely reported this issue as a crash on the release path when the xdpsock sample program is stopped (killed) and restarted in sequence while traffic is running.
This patch flushes all cqes when during the rq flush. The cqe flushing is done in the reset state of the rq. mlx5e_rq_to_ready code is moved into the flush function to allow for this.
--- .../net/ethernet/mellanox/mlx5/core/en_main.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-)
drivers/net/ethernet/mellanox/mlx5/core/en_main.c +11 -8
@@ -989,9 +989,11 @@ static int mlx5e_modify_rq_state(struct mlx5e_rq *rq, int curr_state, int next_sreturn err; } -static int mlx5e_rq_to_ready (struct mlx5e_rq *rq,int curr_state) +int mlx5e_flush_rq (struct mlx5e_rq *rq,int curr_state) { +struct mlx5_cqwq *cqwq = &rq->cq.wq;struct net_device *dev = rq->netdev; +struct mlx5_cqe64 *cqe;int err; err =mlx5e_modify_rq_state (rq, curr_state,MLX5_RQC_STATE_RST );
@@ -999,6 +1001,14 @@ static int mlx5e_rq_to_ready(struct mlx5e_rq *rq, int curr_state)netdev_err (dev,"Failed to move rq 0x%x to reset\n" , rq->rqn);return err; } + +mlx5e_free_rx_descs (rq); + +while ((cqe =mlx5_cqwq_get_cqe (cqwq))) +mlx5_cqwq_pop (cqwq); + +mlx5_cqwq_update_db_record (cqwq); + err =mlx5e_modify_rq_state (rq,MLX5_RQC_STATE_RST ,MLX5_RQC_STATE_RDY );if (err) {netdev_err (dev,"Failed to move rq 0x%x to ready\n" , rq->rqn);
@@ -1008,13 +1018,6 @@ static int mlx5e_rq_to_ready(struct mlx5e_rq *rq, int curr_state)return 0 ; } -int mlx5e_flush_rq (struct mlx5e_rq *rq,int curr_state) -{ -mlx5e_free_rx_descs (rq); - -return mlx5e_rq_to_ready (rq, curr_state); -} -static int mlx5e_modify_rq_vsd (struct mlx5e_rq *rq,bool vsd) {struct mlx5_core_dev *mdev = rq->mdev;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 566/1887 f2fs: fix to set flush_merge opt and show noflush_merge
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yangtao Li <[email protected]>
[ Upstream commit 967eaad1fed5f6335ea97a47d45214744dc57925 ]
Some minor modifications to flush_merge and related parameters:
1.The FLUSH_MERGE opt is set by default only in non-ro mode. 2.When ro and merge are set at the same time, an error is reported. 3.Display noflush_merge mount opt.
Stable-dep-of: 458c15dfbce6 ("f2fs: don't reset unchangable mount option in f2fs_remount()")
--- fs/f2fs/super.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-)
fs/f2fs/super.c +11 -2
@@ -1400,6 +1400,12 @@ static int parse_options(struct super_block *sb, char *options, bool is_remount)return -EINVAL ; } +if ((f2fs_sb_has_readonly (sbi) ||f2fs_readonly (sbi->sb)) && +test_opt (sbi,FLUSH_MERGE )) { +f2fs_err (sbi,"FLUSH_MERGE not compatible with readonly mode" ); +return -EINVAL ; + } +if (f2fs_sb_has_readonly (sbi) && !f2fs_readonly (sbi->sb)) {f2fs_err (sbi,"Allow to mount readonly mode only" );return -EROFS ;
@@ -1997,8 +2003,10 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)seq_puts (seq,",inline_dentry" ); else seq_puts(seq, ",noinline_dentry"); -if (!f2fs_readonly (sbi->sb) &&test_opt (sbi,FLUSH_MERGE )) + if (test_opt(sbi, FLUSH_MERGE)) seq_puts(seq, ",flush_merge"); + else + seq_puts(seq, ",noflush_merge"); if (test_opt(sbi, NOBARRIER)) seq_puts(seq, ",nobarrier"); if (test_opt(sbi, FASTBOOT))
@@ -2125,7 +2133,8 @@ static void default_options(struct f2fs_sb_info *sbi)set_opt (sbi,MERGE_CHECKPOINT );F2FS_OPTION (sbi).unusable_cap =0 ; sbi->sb->s_flags |=SB_LAZYTIME ; -set_opt (sbi,FLUSH_MERGE ); +if (!f2fs_sb_has_readonly (sbi) && !f2fs_readonly (sbi->sb)) +set_opt (sbi,FLUSH_MERGE );if (f2fs_hw_support_discard (sbi) ||f2fs_hw_should_discard (sbi))set_opt (sbi,DISCARD );if (f2fs_sb_has_blkzoned (sbi)) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 567/1887 f2fs: dont reset unchangable mount option in f2fs_remount()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chao Yu <[email protected]>
[ Upstream commit 458c15dfbce62c35fefd9ca637b20a051309c9f1 ]
syzbot reports a bug as below:
general protection fault, probably for non-canonical address 0xdffffc0000000009: 0000 [#1] PREEMPT SMP KASAN RIP: 0010:__lock_acquire+0x69/0x2000 kernel/locking/lockdep.c:4942 Call Trace: lock_acquire+0x1e3/0x520 kernel/locking/lockdep.c:5691 __raw_write_lock include/linux/rwlock_api_smp.h:209 [inline] _raw_write_lock+0x2e/0x40 kernel/locking/spinlock.c:300 __drop_extent_tree+0x3ac/0x660 fs/f2fs/extent_cache.c:1100 f2fs_drop_extent_tree+0x17/0x30 fs/f2fs/extent_cache.c:1116 f2fs_insert_range+0x2d5/0x3c0 fs/f2fs/file.c:1664 f2fs_fallocate+0x4e4/0x6d0 fs/f2fs/file.c:1838 vfs_fallocate+0x54b/0x6b0 fs/open.c:324 ksys_fallocate fs/open.c:347 [inline] __do_sys_fallocate fs/open.c:355 [inline] __se_sys_fallocate fs/open.c:353 [inline] __x64_sys_fallocate+0xbd/0x100 fs/open.c:353 do_syscall_x64 arch/x86/entry/common.c:50 [inline] do_syscall_64+0x41/0xc0 arch/x86/entry/common.c:80 entry_SYSCALL_64_after_hwframe+0x63/0xcd
The root cause is race condition as below:
- since it tries to remount rw filesystem, so that do_remount won't call sb_prepare_remount_readonly to block fallocate, there may be race condition in between remount and fallocate.
- in f2fs_remount(), default_options() will reset mount option to default one, and then update it based on result of parse_options(), so there is a hole which race condition can happen.
Thread A Thread B
- f2fs_fill_super
- parse_options
- clear_opt(READ_EXTENT_CACHE)
- f2fs_remount
- default_options
- set_opt(READ_EXTENT_CACHE)
- f2fs_fallocate
- f2fs_insert_range
- f2fs_drop_extent_tree
- __drop_extent_tree
- __may_extent_tree
- test_opt(READ_EXTENT_CACHE) return true
- write_lock(&et->lock) access NULL pointer
- parse_options
- clear_opt(READ_EXTENT_CACHE)
--- fs/f2fs/super.c | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-)
fs/f2fs/super.c +18 -12
@@ -2104,9 +2104,22 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)return 0 ; } -static void default_options (struct f2fs_sb_info *sbi) +static void default_options (struct f2fs_sb_info *sbi,bool remount) {/* init some FS parameters */ +if (!remount) { +set_opt (sbi,READ_EXTENT_CACHE ); +clear_opt (sbi,DISABLE_CHECKPOINT ); + +if (f2fs_hw_support_discard (sbi) ||f2fs_hw_should_discard (sbi)) +set_opt (sbi,DISCARD ); + +if (f2fs_sb_has_blkzoned (sbi)) +F2FS_OPTION (sbi).discard_unit =DISCARD_UNIT_SECTION ; +else +F2FS_OPTION (sbi).discard_unit =DISCARD_UNIT_BLOCK ; + } +if (f2fs_sb_has_readonly (sbi))F2FS_OPTION (sbi).active_logs =NR_CURSEG_RO_TYPE ;else
@@ -2127,23 +2140,16 @@ static void default_options(struct f2fs_sb_info *sbi)set_opt (sbi,INLINE_XATTR );set_opt (sbi,INLINE_DATA );set_opt (sbi,INLINE_DENTRY ); -set_opt (sbi,READ_EXTENT_CACHE );set_opt (sbi,NOHEAP ); -clear_opt (sbi,DISABLE_CHECKPOINT );set_opt (sbi,MERGE_CHECKPOINT );F2FS_OPTION (sbi).unusable_cap =0 ; sbi->sb->s_flags |=SB_LAZYTIME ;if (!f2fs_sb_has_readonly (sbi) && !f2fs_readonly (sbi->sb))set_opt (sbi,FLUSH_MERGE ); -if (f2fs_hw_support_discard (sbi) ||f2fs_hw_should_discard (sbi)) -set_opt (sbi,DISCARD ); -if (f2fs_sb_has_blkzoned (sbi)) { +if (f2fs_sb_has_blkzoned (sbi))F2FS_OPTION (sbi).fs_mode =FS_MODE_LFS ; -F2FS_OPTION (sbi).discard_unit =DISCARD_UNIT_SECTION ; - }else { +else F2FS_OPTION (sbi).fs_mode =FS_MODE_ADAPTIVE ; -F2FS_OPTION (sbi).discard_unit =DISCARD_UNIT_BLOCK ; - }#ifdef CONFIG_F2FS_FS_XATTR set_opt (sbi,XATTR_USER );
@@ -2320,7 +2326,7 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)clear_sbi_flag (sbi,SBI_NEED_SB_WRITE ); } -default_options (sbi); +default_options (sbi, true);/* parse mount options */ err =parse_options (sb, data, true);
@@ -4252,7 +4258,7 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent) sbi->s_chksum_seed =f2fs_chksum (sbi, ~0 , raw_super->uuid,sizeof (raw_super->uuid)); -default_options (sbi); +default_options (sbi, false);/* parse mount options */ options =kstrdup ((const char *)data,GFP_KERNEL );if (data && !options) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 568/1887 can: gs_usb: fix time stamp counter initialization
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Marc Kleine-Budde <[email protected]>
[ Upstream commit 5886e4d5ecec3e22844efed90b2dd383ef804b3a ]
If the gs_usb device driver is unloaded (or unbound) before the interface is shut down, the USB stack first calls the struct usb_driver::disconnect and then the struct net_device_ops::ndo_stop callback.
In gs_usb_disconnect() all pending bulk URBs are killed, i.e. no more RX'ed CAN frames are send from the USB device to the host. Later in gs_can_close() a reset control message is send to each CAN channel to remove the controller from the CAN bus. In this race window the USB device can still receive CAN frames from the bus and internally queue them to be send to the host.
At least in the current version of the candlelight firmware, the queue of received CAN frames is not emptied during the reset command. After loading (or binding) the gs_usb driver, new URBs are submitted during the struct net_device_ops::ndo_open callback and the candlelight firmware starts sending its already queued CAN frames to the host.
However, this scenario was not considered when implementing the hardware timestamp function. The cycle counter/time counter infrastructure is set up (gs_usb_timestamp_init()) after the USBs are submitted, resulting in a NULL pointer dereference if timecounter_cyc2time() (via the call chain:gs_usb_receive_bulk_callback() -> gs_usb_set_timestamp() -> gs_usb_skb_set_timestamp()) is called too early.
Move the gs_usb_timestamp_init() function before the URBs are submitted to fix this problem.
For a comprehensive solution, we need to consider gs_usb devices with more than 1 channel. The cycle counter/time counter infrastructure is setup per channel, but the RX URBs are per device. Once gs_can_open() of _a_ channel has been called, and URBs have been submitted, the gs_usb_receive_bulk_callback() can be called for _all_ available channels, even for channels that are not running, yet. As cycle counter/time counter has not set up, this will again lead to a NULL pointer dereference.
Convert the cycle counter/time counter from a "per channel" to a "per device" functionality. Also set it up, before submitting any URBs to the device.
Further in gs_usb_receive_bulk_callback(), don't process any URBs for not started CAN channels, only resubmit the URB.
--- drivers/net/can/usb/gs_usb.c | 100 ++++++++++++++++++----------------- 1 file changed, 52 insertions(+), 48 deletions(-)
drivers/net/can/usb/gs_usb.c +52 -48
@@ -297,12 +297,6 @@ struct gs_can {struct can_bittiming_const bt_const, data_bt_const;unsigned int channel;/* channel number */ -/* time counter for hardware timestamps */ -struct cyclecounter cc; -struct timecounter tc; -spinlock_t tc_lock;/* spinlock to guard access tc->cycle_last */ -struct delayed_work timestamp; -u32 feature;unsigned int hf_size_tx;
@@ -318,6 +312,13 @@ struct gs_can {struct gs_usb {struct usb_anchor rx_submitted;struct usb_device *udev; + +/* time counter for hardware timestamps */ +struct cyclecounter cc; +struct timecounter tc; +spinlock_t tc_lock;/* spinlock to guard access tc->cycle_last */ +struct delayed_work timestamp; +unsigned int hf_size_rx;u8 active_channels;u8 channel_cnt;
@@ -387,16 +388,15 @@ static int gs_cmd_reset(struct gs_can *dev) GFP_KERNEL); } -static inline int gs_usb_get_timestamp (const struct gs_can *dev, +static inline int gs_usb_get_timestamp (const struct gs_usb *parent,u32 *timestamp_p) {__le32 timestamp;int rc; - rc =usb_control_msg_recv (interface_to_usbdev (dev->iface),0 , -GS_USB_BREQ_TIMESTAMP , + rc =usb_control_msg_recv (parent->udev,0 ,GS_USB_BREQ_TIMESTAMP ,USB_DIR_IN |USB_TYPE_VENDOR |USB_RECIP_INTERFACE , - dev->channel,0 , +0 ,0 , ×tamp,sizeof (timestamp),USB_CTRL_GET_TIMEOUT ,GFP_KERNEL );
@@ -410,20 +410,20 @@ static inline int gs_usb_get_timestamp(const struct gs_can *dev, static u64 gs_usb_timestamp_read(const struct cyclecounter *cc) __must_hold(&dev->tc_lock) { - struct gs_can *dev = container_of(cc, struct gs_can, cc); + struct gs_usb *parent = container_of(cc, struct gs_usb, cc);u32 timestamp =0 ;int err; -lockdep_assert_held (&dev->tc_lock); +lockdep_assert_held (&parent->tc_lock);/* drop lock for synchronous USB transfer */ -spin_unlock_bh (&dev->tc_lock); - err =gs_usb_get_timestamp (dev, ×tamp); -spin_lock_bh (&dev->tc_lock); +spin_unlock_bh (&parent->tc_lock); + err =gs_usb_get_timestamp (parent, ×tamp); +spin_lock_bh (&parent->tc_lock);if (err) -netdev_err (dev->netdev, -"Error %d while reading timestamp. HW timestamps may be inaccurate." , - err); +dev_err (&parent->udev->dev, +"Error %d while reading timestamp. HW timestamps may be inaccurate." , + err);return timestamp; }
@@ -431,14 +431,14 @@ static u64 gs_usb_timestamp_read(const struct cyclecounter *cc) __must_hold(&devstatic void gs_usb_timestamp_work (struct work_struct *work) {struct delayed_work *delayed_work =to_delayed_work (work); -struct gs_can *dev; +struct gs_usb *parent; - dev = container_of(delayed_work, struct gs_can, timestamp); -spin_lock_bh (&dev->tc_lock); -timecounter_read (&dev->tc); -spin_unlock_bh (&dev->tc_lock); + parent = container_of(delayed_work, struct gs_usb, timestamp); +spin_lock_bh (&parent->tc_lock); +timecounter_read (&parent->tc); +spin_unlock_bh (&parent->tc_lock); -schedule_delayed_work (&dev->timestamp, +schedule_delayed_work (&parent->timestamp,GS_USB_TIMESTAMP_WORK_DELAY_SEC *HZ ); }
@@ -446,37 +446,38 @@ static void gs_usb_skb_set_timestamp(struct gs_can *dev, struct sk_buff *skb, u32 timestamp) {struct skb_shared_hwtstamps *hwtstamps =skb_hwtstamps (skb); +struct gs_usb *parent = dev->parent;u64 ns; -spin_lock_bh (&dev->tc_lock); - ns =timecounter_cyc2time (&dev->tc, timestamp); -spin_unlock_bh (&dev->tc_lock); +spin_lock_bh (&parent->tc_lock); + ns =timecounter_cyc2time (&parent->tc, timestamp); +spin_unlock_bh (&parent->tc_lock); hwtstamps->hwtstamp =ns_to_ktime (ns); } -static void gs_usb_timestamp_init (struct gs_can *dev) +static void gs_usb_timestamp_init (struct gs_usb *parent) { -struct cyclecounter *cc = &dev->cc; +struct cyclecounter *cc = &parent->cc; cc->read = gs_usb_timestamp_read; cc->mask =CYCLECOUNTER_MASK (32 ); cc->shift =32 -bits_per (NSEC_PER_SEC /GS_USB_TIMESTAMP_TIMER_HZ ); cc->mult =clocksource_hz2mult (GS_USB_TIMESTAMP_TIMER_HZ , cc->shift); -spin_lock_init (&dev->tc_lock); -spin_lock_bh (&dev->tc_lock); -timecounter_init (&dev->tc, &dev->cc,ktime_get_real_ns ()); -spin_unlock_bh (&dev->tc_lock); +spin_lock_init (&parent->tc_lock); +spin_lock_bh (&parent->tc_lock); +timecounter_init (&parent->tc, &parent->cc,ktime_get_real_ns ()); +spin_unlock_bh (&parent->tc_lock); -INIT_DELAYED_WORK (&dev->timestamp, gs_usb_timestamp_work); -schedule_delayed_work (&dev->timestamp, +INIT_DELAYED_WORK (&parent->timestamp, gs_usb_timestamp_work); +schedule_delayed_work (&parent->timestamp,GS_USB_TIMESTAMP_WORK_DELAY_SEC *HZ ); } -static void gs_usb_timestamp_stop (struct gs_can *dev) +static void gs_usb_timestamp_stop (struct gs_usb *parent) { -cancel_delayed_work_sync (&dev->timestamp); +cancel_delayed_work_sync (&parent->timestamp); }static void gs_update_state (struct gs_can *dev,struct can_frame *cf)
@@ -570,6 +571,9 @@ static void gs_usb_receive_bulk_callback(struct urb *urb)if (!netif_device_present (netdev))return ; +if (!netif_running (netdev)) + goto resubmit_urb; +if (hf->echo_id ==-1 ) {/* normal rx */ if (hf->flags &GS_CAN_FLAG_FD ) { skb =alloc_canfd_skb (dev->netdev, &cfd);
@@ -891,6 +895,9 @@ static int gs_can_open(struct net_device *netdev) }if (!parent->active_channels) { +if (dev->feature &GS_CAN_FEATURE_HW_TIMESTAMP ) +gs_usb_timestamp_init (parent); +for (i =0 ; i <GS_MAX_RX_URBS ; i++) {u8 *buf;
@@ -973,10 +980,6 @@ static int gs_can_open(struct net_device *netdev) } } -/* start polling timestamp */ -if (dev->feature &GS_CAN_FEATURE_HW_TIMESTAMP ) -gs_usb_timestamp_init (dev); -/* finally start device */ dev->can.state =CAN_STATE_ERROR_ACTIVE ; dm.flags =cpu_to_le32 (flags);
@@ -987,8 +990,6 @@ static int gs_can_open(struct net_device *netdev) GFP_KERNEL);if (rc) {netdev_err (netdev,"Couldn't start device (err=%d)\n" , rc); -if (dev->feature &GS_CAN_FEATURE_HW_TIMESTAMP ) -gs_usb_timestamp_stop (dev); dev->can.state =CAN_STATE_STOPPED ; goto out_usb_kill_anchored_urbs;
@@ -1005,9 +1006,13 @@ static int gs_can_open(struct net_device *netdev) out_usb_free_urb:usb_free_urb (urb); out_usb_kill_anchored_urbs: -if (!parent->active_channels) +if (!parent->active_channels) {usb_kill_anchored_urbs (&parent->rx_submitted); +if (dev->feature &GS_CAN_FEATURE_HW_TIMESTAMP ) +gs_usb_timestamp_stop (parent); + } +close_candev (netdev);return rc;
@@ -1021,14 +1026,13 @@ static int gs_can_close(struct net_device *netdev)netif_stop_queue (netdev); -/* stop polling timestamp */ -if (dev->feature &GS_CAN_FEATURE_HW_TIMESTAMP ) -gs_usb_timestamp_stop (dev); -/* Stop polling */ parent->active_channels--;if (!parent->active_channels) {usb_kill_anchored_urbs (&parent->rx_submitted); + +if (dev->feature &GS_CAN_FEATURE_HW_TIMESTAMP ) +gs_usb_timestamp_stop (parent); }/* Stop sending URBs */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 569/1887 smc: Fix use-after-free in tcp_write_timer_handler().
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kuniyuki Iwashima <[email protected]>
[ Upstream commit 9744d2bf19762703704ecba885b7ac282c02eacf ]
With Eric's ref tracker, syzbot finally found a repro for use-after-free in tcp_write_timer_handler() by kernel TCP sockets. [0]
If SMC creates a kernel socket in __smc_create(), the kernel socket is supposed to be freed in smc_clcsock_release() by calling sock_release() when we close() the parent SMC socket.
However, at the end of smc_clcsock_release(), the kernel socket's sk_state might not be TCP_CLOSE. This means that we have not called inet_csk_destroy_sock() in __tcp_close() and have not stopped the TCP timers.
The kernel socket's TCP timers can be fired later, so we need to hold a refcnt for net as we do for MPTCP subflows in mptcp_subflow_create_socket().
[0]:
leaked reference.
sk_alloc (./include/net/net_namespace.h:335 net/core/sock.c:2108) inet_create (net/ipv4/af_inet.c:319 net/ipv4/af_inet.c:244) __sock_create (net/socket.c:1546) smc_create (net/smc/af_smc.c:3269 net/smc/af_smc.c:3284) __sock_create (net/socket.c:1546) __sys_socket (net/socket.c:1634 net/socket.c:1618 net/socket.c:1661) __x64_sys_socket (net/socket.c:1672) do_syscall_64 (arch/x86/entry/common.c:50 arch/x86/entry/common.c:80) entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:120) ================================================================== BUG: KASAN: slab-use-after-free in tcp_write_timer_handler (net/ipv4/tcp_timer.c:378 net/ipv4/tcp_timer.c:624 net/ipv4/tcp_timer.c:594) Read of size 1 at addr ffff888052b65e0d by task syzrepro/18091 CPU: 0 PID: 18091 Comm: syzrepro Tainted: G W 6.3.0-rc4-01174-gb5d54eb5899a #7 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.16.0-1.amzn2022.0.1 04/01/2014 Call Trace: <IRQ> dump_stack_lvl (lib/dump_stack.c:107) print_report (mm/kasan/report.c:320 mm/kasan/report.c:430) kasan_report (mm/kasan/report.c:538) tcp_write_timer_handler (net/ipv4/tcp_timer.c:378 net/ipv4/tcp_timer.c:624 net/ipv4/tcp_timer.c:594) tcp_write_timer (./include/linux/spinlock.h:390 net/ipv4/tcp_timer.c:643) call_timer_fn (./arch/x86/include/asm/jump_label.h:27 ./include/linux/jump_label.h:207 ./include/trace/events/timer.h:127 kernel/time/timer.c:1701) __run_timers.part.0 (kernel/time/timer.c:1752 kernel/time/timer.c:2022) run_timer_softirq (kernel/time/timer.c:2037) __do_softirq (./arch/x86/include/asm/jump_label.h:27 ./include/linux/jump_label.h:207 ./include/trace/events/irq.h:142 kernel/softirq.c:572) __irq_exit_rcu (kernel/softirq.c:445 kernel/softirq.c:650) irq_exit_rcu (kernel/softirq.c:664) sysvec_apic_timer_interrupt (arch/x86/kernel/apic/apic.c:1107 (discriminator 14)) </IRQ>
--- net/smc/af_smc.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
net/smc/af_smc.c +10 -0
@@ -3315,6 +3315,16 @@ static int __smc_create(struct net *net, struct socket *sock, int protocol,sk_common_release (sk); goto out; } + +/* smc_clcsock_release() does not wait smc->clcsock->sk's +* destruction; its sk_state might not be TCP_CLOSE after +* smc->sk is close()d, and TCP timers can be fired later, +* which need net ref. +*/ + sk = smc->clcsock->sk; + sk->sk_net_refcnt =1 ; +get_net_track (net, &sk->ns_tracker,GFP_KERNEL ); +sock_inuse_add (net,1 ); }else { smc->clcsock = clcsock; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 570/1887 block/rq_qos: protect rq_qos apis with a new lock
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yu Kuai <[email protected]>
[ Upstream commit a13bd91be22318768d55470cbc0b0f4488ef9edf ]
commit 50e34d78815e ("block: disable the elevator int del_gendisk") move rq_qos_exit() from disk_release() to del_gendisk(), this will introduce some problems:
1) If rq_qos_add() is triggered by enabling iocost/iolatency through cgroupfs, then it can concurrent with del_gendisk(), it's not safe to write 'q->rq_qos' concurrently.
2) Activate cgroup policy that is relied on rq_qos will call rq_qos_add() and blkcg_activate_policy(), and if rq_qos_exit() is called in the middle, null-ptr-dereference will be triggered in blkcg_activate_policy().
3) blkg_conf_open_bdev() can call blkdev_get_no_open() first to find the disk, then if rq_qos_exit() from del_gendisk() is done before rq_qos_add(), then memory will be leaked.
This patch add a new disk level mutex 'rq_qos_mutex':
1) The lock will protect rq_qos_exit() directly.
2) For wbt that doesn't relied on blk-cgroup, rq_qos_add() can only be called from disk initialization for now because wbt can't be destructed until rq_qos_exit(), so it's safe not to protect wbt for now. Hoever, in case that rq_qos dynamically destruction is supported in the furture, this patch also protect rq_qos_add() from wbt_init() directly, this is enough because blk-sysfs already synchronize writers with disk removal.
3) For iocost and iolatency, in order to synchronize disk removal and cgroup configuration, the lock is held after blkdev_get_no_open() from blkg_conf_open_bdev(), and is released in blkg_conf_exit(). In order to fix the above memory leak, disk_live() is checked after holding the new lock.
--- block/blk-cgroup.c | 12 ++++++++++++ block/blk-core.c | 1 + block/blk-iocost.c | 4 ++++ block/blk-rq-qos.c | 2 ++ block/blk-rq-qos.h | 18 ++++-------------- block/blk-wbt.c | 2 ++ include/linux/blkdev.h | 1 + 7 files changed, 26 insertions(+), 14 deletions(-)
block/blk-cgroup.c +12 -0
@@ -656,6 +656,13 @@ struct block_device *blkcg_conf_open_bdev(char **inputp)return ERR_PTR (-ENODEV ); } +mutex_lock (&bdev->bd_queue->rq_qos_mutex); +if (!disk_live (bdev->bd_disk)) { +mutex_unlock (&bdev->bd_queue->rq_qos_mutex); +blkdev_put_no_open (bdev); +return ERR_PTR (-ENODEV ); + } + *inputp = input;return bdev; }
@@ -779,6 +786,7 @@ int blkg_conf_prep(struct blkcg *blkcg, const struct blkcg_policy *pol, fail_exit_queue:blk_queue_exit (q); fail: +mutex_unlock (&q->rq_qos_mutex);blkdev_put_no_open (bdev);/* * If queue was bypassing, we should retry. Do so after a
@@ -803,9 +811,11 @@ EXPORT_SYMBOL_GPL(blkg_conf_prep);*/ void blkg_conf_finish (struct blkg_conf_ctx *ctx) __releases(&ctx->bdev->bd_queue->queue_lock) __releases(rcu) + __releases(&ctx->bdev->bd_queue->rq_qos_mutex) {spin_unlock_irq (&bdev_get_queue (ctx->bdev)->queue_lock);rcu_read_unlock (); +mutex_unlock (&ctx->bdev->bd_queue->rq_qos_mutex);blkdev_put_no_open (ctx->bdev); }EXPORT_SYMBOL_GPL (blkg_conf_finish);
@@ -1297,7 +1307,9 @@ int blkcg_init_disk(struct gendisk *disk)if (ret) goto err_ioprio_exit; +mutex_lock (&q->rq_qos_mutex); ret =blk_iolatency_init (disk); +mutex_unlock (&q->rq_qos_mutex);if (ret) goto err_throtl_exit;
block/blk-core.c +1 -0
@@ -412,6 +412,7 @@ struct request_queue *blk_alloc_queue(int node_id, bool alloc_srcu)mutex_init (&q->debugfs_mutex);mutex_init (&q->sysfs_lock);mutex_init (&q->sysfs_dir_lock); +mutex_init (&q->rq_qos_mutex);spin_lock_init (&q->queue_lock);init_waitqueue_head (&q->mq_freeze_wq);
block/blk-iocost.c +4 -0
@@ -3309,11 +3309,13 @@ static ssize_t ioc_qos_write(struct kernfs_open_file *of, char *input,ioc_refresh_params (ioc, true);spin_unlock_irq (&ioc->lock); +mutex_unlock (&bdev->bd_queue->rq_qos_mutex);blkdev_put_no_open (bdev);return nbytes; einval: ret = -EINVAL ; err: +mutex_unlock (&bdev->bd_queue->rq_qos_mutex);blkdev_put_no_open (bdev);return ret; }
@@ -3434,12 +3436,14 @@ static ssize_t ioc_cost_model_write(struct kernfs_open_file *of, char *input,ioc_refresh_params (ioc, true);spin_unlock_irq (&ioc->lock); +mutex_unlock (&bdev->bd_queue->rq_qos_mutex);blkdev_put_no_open (bdev);return nbytes; einval: ret = -EINVAL ; err: +mutex_unlock (&bdev->bd_queue->rq_qos_mutex);blkdev_put_no_open (bdev);return ret; }
block/blk-rq-qos.c +2 -0
@@ -288,9 +288,11 @@ void rq_qos_wait(struct rq_wait *rqw, void *private_data,void rq_qos_exit (struct request_queue *q) { +mutex_lock (&q->rq_qos_mutex);while (q->rq_qos) {struct rq_qos *rqos = q->rq_qos; q->rq_qos = rqos->next; rqos->ops->exit (rqos); } +mutex_unlock (&q->rq_qos_mutex); }
block/blk-rq-qos.h +4 -14
@@ -87,21 +87,18 @@ static inline void rq_wait_init(struct rq_wait *rq_wait)static inline int rq_qos_add (struct request_queue *q,struct rq_qos *rqos) { +lockdep_assert_held (&q->rq_qos_mutex); +/* * No IO can be in-flight when adding rqos, so freeze queue, which * is fine since we only support rq_qos for blk-mq queue. -* -* Reuse ->queue_lock for protecting against other concurrent -* rq_qos adding/deleting */ blk_mq_freeze_queue (q); -spin_lock_irq (&q->queue_lock);if (rq_qos_id (q, rqos->id)) goto ebusy; rqos->next = q->rq_qos; q->rq_qos = rqos; -spin_unlock_irq (&q->queue_lock);blk_mq_unfreeze_queue (q);
@@ -113,7 +110,6 @@ static inline int rq_qos_add(struct request_queue *q, struct rq_qos *rqos)return 0 ; ebusy: -spin_unlock_irq (&q->queue_lock);blk_mq_unfreeze_queue (q);return -EBUSY ;
@@ -123,21 +119,15 @@ static inline void rq_qos_del(struct request_queue *q, struct rq_qos *rqos) {struct rq_qos **cur; -/* -* See comment in rq_qos_add() about freezing queue & using -* ->queue_lock. -*/ -blk_mq_freeze_queue (q); +lockdep_assert_held (&q->rq_qos_mutex); -spin_lock_irq (&q->queue_lock); +blk_mq_freeze_queue (q);for (cur = &q->rq_qos; *cur; cur = &(*cur)->next) {if (*cur == rqos) { *cur = rqos->next;break ; } } -spin_unlock_irq (&q->queue_lock); -blk_mq_unfreeze_queue (q);mutex_lock (&q->debugfs_mutex);
block/blk-wbt.c +2 -0
@@ -850,7 +850,9 @@ int wbt_init(struct request_queue *q)/* * Assign rwb and add the stats callback. */ +mutex_lock (&q->rq_qos_mutex); ret =rq_qos_add (q, &rwb->rqos); +mutex_unlock (&q->rq_qos_mutex);if (ret) goto err_free;
include/linux/blkdev.h +1 -0
@@ -397,6 +397,7 @@ struct request_queue {struct blk_queue_stats *stats;struct rq_qos *rq_qos; +struct mutex rq_qos_mutex;const struct blk_mq_ops *mq_ops;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 571/1887 f2fs: fix to do sanity check on direct node in truncate_dnode()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chao Yu <[email protected]>
[ Upstream commit a6ec83786ab9f13f25fb18166dee908845713a95 ]
syzbot reports below bug:
BUG: KASAN: slab-use-after-free in f2fs_truncate_data_blocks_range+0x122a/0x14c0 fs/f2fs/file.c:574 Read of size 4 at addr ffff88802a25c000 by task syz-executor148/5000 CPU: 1 PID: 5000 Comm: syz-executor148 Not tainted 6.4.0-rc7-syzkaller-00041-ge660abd551f1 #0 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 05/27/2023 Call Trace: <TASK> __dump_stack lib/dump_stack.c:88 [inline] dump_stack_lvl+0xd9/0x150 lib/dump_stack.c:106 print_address_description.constprop.0+0x2c/0x3c0 mm/kasan/report.c:351 print_report mm/kasan/report.c:462 [inline] kasan_report+0x11c/0x130 mm/kasan/report.c:572 f2fs_truncate_data_blocks_range+0x122a/0x14c0 fs/f2fs/file.c:574 truncate_dnode+0x229/0x2e0 fs/f2fs/node.c:944 f2fs_truncate_inode_blocks+0x64b/0xde0 fs/f2fs/node.c:1154 f2fs_do_truncate_blocks+0x4ac/0xf30 fs/f2fs/file.c:721 f2fs_truncate_blocks+0x7b/0x300 fs/f2fs/file.c:749 f2fs_truncate.part.0+0x4a5/0x630 fs/f2fs/file.c:799 f2fs_truncate include/linux/fs.h:825 [inline] f2fs_setattr+0x1738/0x2090 fs/f2fs/file.c:1006 notify_change+0xb2c/0x1180 fs/attr.c:483 do_truncate+0x143/0x200 fs/open.c:66 handle_truncate fs/namei.c:3295 [inline] do_open fs/namei.c:3640 [inline] path_openat+0x2083/0x2750 fs/namei.c:3791 do_filp_open+0x1ba/0x410 fs/namei.c:3818 do_sys_openat2+0x16d/0x4c0 fs/open.c:1356 do_sys_open fs/open.c:1372 [inline] __do_sys_creat fs/open.c:1448 [inline] __se_sys_creat fs/open.c:1442 [inline] __x64_sys_creat+0xcd/0x120 fs/open.c:1442 do_syscall_x64 arch/x86/entry/common.c:50 [inline] do_syscall_64+0x39/0xb0 arch/x86/entry/common.c:80 entry_SYSCALL_64_after_hwframe+0x63/0xcd
The root cause is, inodeA references inodeB via inodeB's ino, once inodeA is truncated, it calls truncate_dnode() to truncate data blocks in inodeB's node page, it traverse mapping data from node->i.i_addr[0] to node->i.i_addr[ADDRS_PER_BLOCK() - 1], result in out-of-boundary access.
This patch fixes to add sanity check on dnode page in truncate_dnode(), so that, it can help to avoid triggering such issue, and once it encounters such issue, it will record newly introduced ERROR_INVALID_NODE_REFERENCE error into superblock, later fsck can detect such issue and try repairing.
Also, it removes f2fs_truncate_data_blocks() for cleanup due to the function has only one caller, and uses f2fs_truncate_data_blocks_range() instead.
--- fs/f2fs/f2fs.h | 1 - fs/f2fs/file.c | 5 ----- fs/f2fs/node.c | 14 ++++++++++++-- 3 files changed, 12 insertions(+), 8 deletions(-)
fs/f2fs/f2fs.h +0 -1
@@ -3466,7 +3466,6 @@ static inline bool __is_valid_data_blkaddr(block_t blkaddr)* file.c */ int f2fs_sync_file (struct file *file,loff_t start,loff_t end,int datasync); -void f2fs_truncate_data_blocks (struct dnode_of_data *dn);int f2fs_do_truncate_blocks (struct inode *inode,u64 from,bool lock);int f2fs_truncate_blocks (struct inode *inode,u64 from,bool lock);int f2fs_truncate (struct inode *inode);
fs/f2fs/file.c +0 -5
@@ -666,11 +666,6 @@ void f2fs_truncate_data_blocks_range(struct dnode_of_data *dn, int count) dn->ofs_in_node, nr_free); } -void f2fs_truncate_data_blocks (struct dnode_of_data *dn) -{ -f2fs_truncate_data_blocks_range (dn,ADDRS_PER_BLOCK (dn->inode)); -} -static int truncate_partial_data_page (struct inode *inode,u64 from,bool cache_only) {
fs/f2fs/node.c +12 -2
@@ -946,6 +946,7 @@ static int truncate_node(struct dnode_of_data *dn)static int truncate_dnode (struct dnode_of_data *dn) { +struct f2fs_sb_info *sbi =F2FS_I_SB (dn->inode);struct page *page;int err;
@@ -953,16 +954,25 @@ static int truncate_dnode(struct dnode_of_data *dn)return 1 ;/* get direct node */ - page =f2fs_get_node_page (F2FS_I_SB (dn->inode), dn->nid); + page =f2fs_get_node_page (sbi, dn->nid);if (PTR_ERR (page) == -ENOENT )return 1 ;else if (IS_ERR (page))return PTR_ERR (page); +if (IS_INODE (page) ||ino_of_node (page) != dn->inode->i_ino) { +f2fs_err (sbi,"incorrect node reference, ino: %lu, nid: %u, ino_of_node: %u" , + dn->inode->i_ino, dn->nid,ino_of_node (page)); +set_sbi_flag (sbi,SBI_NEED_FSCK ); +f2fs_handle_error (sbi,ERROR_INVALID_NODE_REFERENCE ); +f2fs_put_page (page,1 ); +return -EFSCORRUPTED ; + } +/* Make dnode_of_data for parameter */ dn->node_page = page; dn->ofs_in_node =0 ; -f2fs_truncate_data_blocks (dn); +f2fs_truncate_data_blocks_range (dn,ADDRS_PER_BLOCK (dn->inode)); err =truncate_node (dn);if (err) {f2fs_put_page (page,1 );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 572/1887 interconnect: Fix locking for runpm vs reclaim
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Rob Clark <[email protected]>
[ Upstream commit af42269c3523492d71ebbe11fefae2653e9cdc78 ]
For cases where icc_bw_set() can be called in callbaths that could deadlock against shrinker/reclaim, such as runpm resume, we need to decouple the icc locking. Introduce a new icc_bw_lock for cases where we need to serialize bw aggregation and update to decouple that from paths that require memory allocation such as node/link creation/ destruction.
Fixes this lockdep splat:
======================================================
WARNING: possible circular locking dependency detected 6.2.0-rc8-debug+ #554 Not tainted
------------------------------------------------------
ring0/132 is trying to acquire lock:
ffffff80871916d0 (&gmu->lock){+.+.}-{3:3}, at: a6xx_pm_resume+0xf0/0x234
but task is already holding lock:
ffffffdb5aee57e8 (dma_fence_map){++++}-{0:0}, at: msm_job_run+0x68/0x150
which lock already depends on the new lock.
the existing dependency chain (in reverse order) is:
-> #4 (dma_fence_map){++++}-{0:0}:
__dma_fence_might_wait+0x74/0xc0
dma_resv_lockdep+0x1f4/0x2f4
do_one_initcall+0x104/0x2bc
kernel_init_freeable+0x344/0x34c
kernel_init+0x30/0x134
ret_from_fork+0x10/0x20-> #3 (mmu_notifier_invalidate_range_start){+.+.}-{0:0}:
fs_reclaim_acquire+0x80/0xa8
slab_pre_alloc_hook.constprop.0+0x40/0x25c
__kmem_cache_alloc_node+0x60/0x1cc
__kmalloc+0xd8/0x100
topology_parse_cpu_capacity+0x8c/0x178
get_cpu_for_node+0x88/0xc4
parse_cluster+0x1b0/0x28c
parse_cluster+0x8c/0x28c
init_cpu_topology+0x168/0x188
smp_prepare_cpus+0x24/0xf8
kernel_init_freeable+0x18c/0x34c
kernel_init+0x30/0x134
ret_from_fork+0x10/0x20-> #2 (fs_reclaim){+.+.}-{0:0}:
__fs_reclaim_acquire+0x3c/0x48
fs_reclaim_acquire+0x54/0xa8
slab_pre_alloc_hook.constprop.0+0x40/0x25c
__kmem_cache_alloc_node+0x60/0x1cc
__kmalloc+0xd8/0x100
kzalloc.constprop.0+0x14/0x20
icc_node_create_nolock+0x4c/0xc4
icc_node_create+0x38/0x58
qcom_icc_rpmh_probe+0x1b8/0x248
platform_probe+0x70/0xc4
really_probe+0x158/0x290
__driver_probe_device+0xc8/0xe0
driver_probe_device+0x44/0x100
__driver_attach+0xf8/0x108
bus_for_each_dev+0x78/0xc4
driver_attach+0x2c/0x38
bus_add_driver+0xd0/0x1d8
driver_register+0xbc/0xf8
__platform_driver_register+0x30/0x3c
qnoc_driver_init+0x24/0x30
do_one_initcall+0x104/0x2bc
kernel_init_freeable+0x344/0x34c
kernel_init+0x30/0x134
ret_from_fork+0x10/0x20-> #1 (icc_lock){+.+.}-{3:3}:
__mutex_lock+0xcc/0x3c8
mutex_lock_nested+0x30/0x44
icc_set_bw+0x88/0x2b4
_set_opp_bw+0x8c/0xd8
_set_opp+0x19c/0x300
dev_pm_opp_set_opp+0x84/0x94
a6xx_gmu_resume+0x18c/0x804
a6xx_pm_resume+0xf8/0x234
adreno_runtime_resume+0x2c/0x38
pm_generic_runtime_resume+0x30/0x44
__rpm_callback+0x15c/0x174
rpm_callback+0x78/0x7c
rpm_resume+0x318/0x524
__pm_runtime_resume+0x78/0xbc
adreno_load_gpu+0xc4/0x17c
msm_open+0x50/0x120
drm_file_alloc+0x17c/0x228
drm_open_helper+0x74/0x118
drm_open+0xa0/0x144
drm_stub_open+0xd4/0xe4
chrdev_open+0x1b8/0x1e4
do_dentry_open+0x2f8/0x38c
vfs_open+0x34/0x40
path_openat+0x64c/0x7b4
do_filp_open+0x54/0xc4
do_sys_openat2+0x9c/0x100
do_sys_open+0x50/0x7c
__arm64_sys_openat+0x28/0x34
invoke_syscall+0x8c/0x128
el0_svc_common.constprop.0+0xa0/0x11c
do_el0_svc+0xac/0xbc
el0_svc+0x48/0xa0
el0t_64_sync_handler+0xac/0x13c
el0t_64_sync+0x190/0x194-> #0 (&gmu->lock){+.+.}-{3:3}:
__lock_acquire+0xe00/0x1060
lock_acquire+0x1e0/0x2f8
__mutex_lock+0xcc/0x3c8
mutex_lock_nested+0x30/0x44
a6xx_pm_resume+0xf0/0x234
adreno_runtime_resume+0x2c/0x38
pm_generic_runtime_resume+0x30/0x44
__rpm_callback+0x15c/0x174
rpm_callback+0x78/0x7c
rpm_resume+0x318/0x524
__pm_runtime_resume+0x78/0xbc
pm_runtime_get_sync.isra.0+0x14/0x20
msm_gpu_submit+0x58/0x178
msm_job_run+0x78/0x150
drm_sched_main+0x290/0x370
kthread+0xf0/0x100
ret_from_fork+0x10/0x20other info that might help us debug this:
Chain exists of:
&gmu->lock --> mmu_notifier_invalidate_range_start --> dma_fence_map
Possible unsafe locking scenario: CPU0 CPU1
---- ----
lock(dma_fence_map);
lock(mmu_notifier_invalidate_range_start);
lock(dma_fence_map);
lock(&gmu->lock);
*** DEADLOCK ***2 locks held by ring0/132:
#0: ffffff8087191170 (&gpu->lock){+.+.}-{3:3}, at: msm_job_run+0x64/0x150
#1: ffffffdb5aee57e8 (dma_fence_map){++++}-{0:0}, at: msm_job_run+0x68/0x150 stack backtrace:
CPU: 7 PID: 132 Comm: ring0 Not tainted 6.2.0-rc8-debug+ #554
Hardware name: Google Lazor (rev1 - 2) with LTE (DT)
Call trace:
dump_backtrace.part.0+0xb4/0xf8
show_stack+0x20/0x38
dump_stack_lvl+0x9c/0xd0
dump_stack+0x18/0x34
print_circular_bug+0x1b4/0x1f0
check_noncircular+0x78/0xac
__lock_acquire+0xe00/0x1060
lock_acquire+0x1e0/0x2f8
__mutex_lock+0xcc/0x3c8
mutex_lock_nested+0x30/0x44
a6xx_pm_resume+0xf0/0x234
adreno_runtime_resume+0x2c/0x38
pm_generic_runtime_resume+0x30/0x44
__rpm_callback+0x15c/0x174
rpm_callback+0x78/0x7c
rpm_resume+0x318/0x524
__pm_runtime_resume+0x78/0xbc
pm_runtime_get_sync.isra.0+0x14/0x20
msm_gpu_submit+0x58/0x178
msm_job_run+0x78/0x150
drm_sched_main+0x290/0x370
kthread+0xf0/0x100
ret_from_fork+0x10/0x20--- drivers/interconnect/core.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
drivers/interconnect/core.c +6 -2
@@ -29,6 +29,7 @@ static LIST_HEAD(icc_providers);static int providers_count;static bool synced_state;static DEFINE_MUTEX (icc_lock); +static DEFINE_MUTEX (icc_bw_lock);static struct dentry *icc_debugfs_dir;static void icc_summary_show_one (struct seq_file *s,struct icc_node *n)
@@ -635,7 +636,7 @@ int icc_set_bw(struct icc_path *path, u32 avg_bw, u32 peak_bw)if (WARN_ON (IS_ERR (path) || !path->num_nodes))return -EINVAL ; -mutex_lock (&icc_lock); +mutex_lock (&icc_bw_lock); old_avg = path->reqs[0 ].avg_bw; old_peak = path->reqs[0 ].peak_bw;
@@ -667,7 +668,7 @@ int icc_set_bw(struct icc_path *path, u32 avg_bw, u32 peak_bw)apply_constraints (path); } -mutex_unlock (&icc_lock); +mutex_unlock (&icc_bw_lock);trace_icc_set_bw_end (path, ret);
@@ -971,6 +972,7 @@ void icc_node_add(struct icc_node *node, struct icc_provider *provider)return ;mutex_lock (&icc_lock); +mutex_lock (&icc_bw_lock); node->provider = provider;list_add_tail (&node->node_list, &provider->nodes);
@@ -996,6 +998,7 @@ void icc_node_add(struct icc_node *node, struct icc_provider *provider) node->avg_bw =0 ; node->peak_bw =0 ; +mutex_unlock (&icc_bw_lock);mutex_unlock (&icc_lock); }EXPORT_SYMBOL_GPL (icc_node_add);
@@ -1135,6 +1138,7 @@ void icc_sync_state(struct device *dev)return ;mutex_lock (&icc_lock); +mutex_lock (&icc_bw_lock); synced_state = true;list_for_each_entry (p, &icc_providers, provider_list) {dev_dbg (p->dev,"interconnect provider is in synced state\n" );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 573/1887 blk-cgroup: dropping parent refcount after pd_free_fn() is done
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yu Kuai <[email protected]>
[ Upstream commit c7241babf0855d8a6180cd1743ff0ec34de40b4e ]
Some cgroup policies will access parent pd through child pd even after pd_offline_fn() is done. If pd_free_fn() for parent is called before child, then UAF can be triggered. Hence it's better to guarantee the order of pd_free_fn().
Currently refcount of parent blkg is dropped in __blkg_release(), which is before pd_free_fn() is called in blkg_free_work_fn() while blkg_free_work_fn() is called asynchronously.
This patch make sure pd_free_fn() called from removing cgroup is ordered by delaying dropping parent refcount after calling pd_free_fn() for child.
BTW, pd_free_fn() will also be called from blkcg_deactivate_policy() from deleting device, and following patches will guarantee the order.
--- block/blk-cgroup.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
block/blk-cgroup.c +2 -2
@@ -94,6 +94,8 @@ static void blkg_free_workfn(struct work_struct *work)if (blkg->pd[i]) blkcg_policy[i]->pd_free_fn (blkg->pd[i]); +if (blkg->parent) +blkg_put (blkg->parent);if (blkg->q)blk_put_queue (blkg->q);free_percpu (blkg->iostat_cpu);
@@ -128,8 +130,6 @@ static void __blkg_release(struct rcu_head *rcu)/* release the blkcg and parent blkg refs this blkg has been holding */ css_put (&blkg->blkcg->css); -if (blkg->parent) -blkg_put (blkg->parent);blkg_free (blkg); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 574/1887 nexthop: Factor out hash threshold fdb nexthop selection
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Benjamin Poirier <[email protected]>
[ Upstream commit eedd47a6ec9f683f0b8d931aacca81985be55eec ]
The loop in nexthop_select_path_hthr() includes code to check for neighbor validity. Since this does not apply to fdb nexthops, simplify the loop by moving the fdb nexthop selection to its own function.
Stable-dep-of: b72a6a7ab957 ("net: nexthop: Increase weight to u16")
--- net/ipv4/nexthop.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-)
net/ipv4/nexthop.c +20 -2
@@ -1153,11 +1153,31 @@ static bool ipv4_good_nh(const struct fib_nh *nh)return !!(state &NUD_VALID ); } +static struct nexthop *nexthop_select_path_fdb (struct nh_group *nhg,int hash) +{ +int i; + +for (i =0 ; i < nhg->num_nh; i++) { +struct nh_grp_entry *nhge = &nhg->nh_entries[i]; + +if (hash >atomic_read (&nhge->hthr.upper_bound)) +continue ; + +return nhge->nh; + } + +WARN_ON_ONCE (1 ); +return NULL ; +} +static struct nexthop *nexthop_select_path_hthr (struct nh_group *nhg,int hash) {struct nexthop *rc =NULL ;int i; +if (nhg->fdb_nh) +return nexthop_select_path_fdb (nhg, hash); +for (i =0 ; i < nhg->num_nh; ++i) {struct nh_grp_entry *nhge = &nhg->nh_entries[i];struct nh_info *nhi;
@@ -1166,8 +1186,6 @@ static struct nexthop *nexthop_select_path_hthr(struct nh_group *nhg, int hash)continue ; nhi =rcu_dereference (nhge->nh->nh_info); -if (nhi->fdb_nh) -return nhge->nh;/* nexthops always check if it is good and does * not rely on a sysctl for this behavior
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 575/1887 nexthop: Factor out neighbor validity check
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Benjamin Poirier <[email protected]>
[ Upstream commit 4bb5239b43348f4a1cd73f44be3dc57ac653ae9c ]
For legacy nexthops, there is fib_good_nh() to check the neighbor validity. In order to make the nexthop object code more similar to the legacy nexthop code, factor out the nexthop object neighbor validity check into its own function.
Stable-dep-of: b72a6a7ab957 ("net: nexthop: Increase weight to u16")
--- net/ipv4/nexthop.c | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-)
net/ipv4/nexthop.c +16 -13
@@ -1153,6 +1153,20 @@ static bool ipv4_good_nh(const struct fib_nh *nh)return !!(state &NUD_VALID ); } +static bool nexthop_is_good_nh (const struct nexthop *nh) +{ +struct nh_info *nhi =rcu_dereference (nh->nh_info); + +switch (nhi->family) { +case AF_INET : +return ipv4_good_nh (&nhi->fib_nh); +case AF_INET6 : +return ipv6_good_nh (&nhi->fib6_nh); + } + +return false; +} +static struct nexthop *nexthop_select_path_fdb (struct nh_group *nhg,int hash) {int i;
@@ -1180,26 +1194,15 @@ static struct nexthop *nexthop_select_path_hthr(struct nh_group *nhg, int hash)for (i =0 ; i < nhg->num_nh; ++i) {struct nh_grp_entry *nhge = &nhg->nh_entries[i]; -struct nh_info *nhi;if (hash >atomic_read (&nhge->hthr.upper_bound))continue ; - nhi =rcu_dereference (nhge->nh->nh_info); -/* nexthops always check if it is good and does * not rely on a sysctl for this behavior */ -switch (nhi->family) { -case AF_INET : -if (ipv4_good_nh (&nhi->fib_nh)) -return nhge->nh; -break ; -case AF_INET6 : -if (ipv6_good_nh (&nhi->fib6_nh)) -return nhge->nh; -break ; - } +if (nexthop_is_good_nh (nhge->nh)) +return nhge->nh;if (!rc) rc = nhge->nh;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 576/1887 nexthop: Do not return invalid nexthop object during multipath selection
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Benjamin Poirier <[email protected]>
[ Upstream commit 75f5f04c7bd2163044f80d5f7bea6b79edc288fa ]
With legacy nexthops, when net.ipv4.fib_multipath_use_neigh is set, fib_select_multipath() will never set res->nhc to a nexthop that is not good (as per fib_good_nh()). OTOH, with nexthop objects, nexthop_select_path_hthr() may return a nexthop that failed the nexthop_is_good_nh() test even if there was one that passed. Refactor nexthop_select_path_hthr() to follow a selection logic more similar to fib_select_multipath().
The issue can be demonstrated with the following sequence of commands. The first block shows that things work as expected with legacy nexthops. The last sequence of ip rou get in the second block shows the problem case - some routes still use the .2 nexthop.
sysctl net.ipv4.fib_multipath_use_neigh=1
ip link add dummy1 up type dummy
ip rou add 198.51.100.0/24 nexthop via 192.0.2.1 dev dummy1 onlink nexthop via 192.0.2.2 dev dummy1 onlink for i in {10..19}; do ip -o rou get 198.51.100.$i; done
ip neigh add 192.0.2.1 dev dummy1 nud failed
echo ".1 failed:" # results should not use .1
for i in {10..19}; do ip -o rou get 198.51.100.$i; done
ip neigh del 192.0.2.1 dev dummy1
ip neigh add 192.0.2.2 dev dummy1 nud failed
echo ".2 failed:" # results should not use .2
for i in {10..19}; do ip -o rou get 198.51.100.$i; done
ip link del dummy1
ip link add dummy1 up type dummy
ip nexthop add id 1 via 192.0.2.1 dev dummy1 onlink ip nexthop add id 2 via 192.0.2.2 dev dummy1 onlink ip nexthop add id 1001 group 1/2
ip rou add 198.51.100.0/24 nhid 1001
for i in {10..19}; do ip -o rou get 198.51.100.$i; done ip neigh add 192.0.2.1 dev dummy1 nud failed echo ".1 failed:" # results should not use .1 for i in {10..19}; do ip -o rou get 198.51.100.$i; done ip neigh del 192.0.2.1 dev dummy1
ip neigh add 192.0.2.2 dev dummy1 nud failed echo ".2 failed:" # results should not use .2 for i in {10..19}; do ip -o rou get 198.51.100.$i; done ip link del dummy1
Stable-dep-of: b72a6a7ab957 ("net: nexthop: Increase weight to u16")
--- net/ipv4/nexthop.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-)
net/ipv4/nexthop.c +8 -6
@@ -1195,20 +1195,22 @@ static struct nexthop *nexthop_select_path_hthr(struct nh_group *nhg, int hash)for (i =0 ; i < nhg->num_nh; ++i) {struct nh_grp_entry *nhge = &nhg->nh_entries[i]; -if (hash >atomic_read (&nhge->hthr.upper_bound)) -continue ; -/* nexthops always check if it is good and does * not rely on a sysctl for this behavior */ -if (nexthop_is_good_nh (nhge->nh)) -return nhge->nh; +if (!nexthop_is_good_nh (nhge->nh)) +continue ;if (!rc) rc = nhge->nh; + +if (hash >atomic_read (&nhge->hthr.upper_bound)) +continue ; + +return nhge->nh; } -return rc; +return rc ? : nhg->nh_entries[0 ].nh; }static struct nexthop *nexthop_select_path_res (struct nh_group *nhg,int hash)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 577/1887 net: nexthop: Have all NH notifiers carry NH ID
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Petr Machata <[email protected]>
[ Upstream commit e99eb57e9b14d830a571c5255248d4d7eb08b27e ]
When sending the notifications to collect NH statistics for resilient groups, the driver will need to know the nexthop IDs in individual buckets to look up the right counter. To that end, move the nexthop ID from struct nh_notifier_grp_entry_info to nh_notifier_single_info.
Stable-dep-of: b72a6a7ab957 ("net: nexthop: Increase weight to u16")
--- include/net/nexthop.h | 2 +- net/ipv4/nexthop.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
include/net/nexthop.h +1 -1
@@ -173,6 +173,7 @@ struct nh_notifier_single_info {__be32 ipv4;struct in6_addr ipv6; }; +u32 id; u8 is_reject:1, is_fdb:1, has_encap:1;
@@ -180,7 +181,6 @@ struct nh_notifier_single_info {struct nh_notifier_grp_entry_info {u8 weight; -u32 id;struct nh_notifier_single_info nh; };
net/ipv4/nexthop.c +1 -1
@@ -92,6 +92,7 @@ __nh_notifier_single_info_init(struct nh_notifier_single_info *nh_info,else if (nh_info->gw_family ==AF_INET6 ) nh_info->ipv6 = nhi->fib_nhc.nhc_gw.ipv6; + nh_info->id = nhi->nh_parent->id; nh_info->is_reject = nhi->reject_nh; nh_info->is_fdb = nhi->fdb_nh; nh_info->has_encap = !!nhi->fib_nhc.nhc_lwtstate;
@@ -137,7 +138,6 @@ static int nh_notifier_mpath_info_init(struct nh_notifier_info *info,struct nh_info *nhi; nhi =rtnl_dereference (nhge->nh->nh_info); - info->nh_grp->nh_entries[i].id = nhge->nh->id; info->nh_grp->nh_entries[i].weight = nhge->weight;__nh_notifier_single_info_init (&info->nh_grp->nh_entries[i].nh, nhi);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 578/1887 net: nexthop: Increase weight to u16
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Petr Machata <[email protected]>
[ Upstream commit b72a6a7ab9573e06d5c2fcb92eaa28614a735bfd ]
In CLOS networks, as link failures occur at various points in the network, ECMP weights of the involved nodes are adjusted to compensate. With high fan-out of the involved nodes, and overall high number of nodes, a (non-)ECMP weight ratio that we would like to configure does not fit into 8 bits. Instead of, say, 255:254, we might like to configure something like 1000:999. For these deployments, the 8-bit weight may not be enough.
To that end, in this patch increase the next hop weight from u8 to u16.
Increasing the width of an integral type can be tricky, because while the code still compiles, the types may not check out anymore, and numerical errors come up. To prevent this, the conversion was done in two steps. First the type was changed from u8 to a single-member structure, which invalidated all uses of the field. This allowed going through them one by one and audit for type correctness. Then the structure was replaced with a vanilla u16 again. This should ensure that no place was missed.
The UAPI for configuring nexthop group members is that an attribute NHA_GROUP carries an array of struct nexthop_grp entries:
struct nexthop_grp {
__u32 id; /* nexthop id - must exist */
__u8 weight; /* weight of this nexthop */
__u8 resvd1;
__u16 resvd2;
};The field resvd1 is currently validated and required to be zero. We can lift this requirement and carry high-order bits of the weight in the reserved field:
struct nexthop_grp {
__u32 id; /* nexthop id - must exist */
__u8 weight; /* weight of this nexthop */
__u8 weight_high;
__u16 resvd2;
};Keeping the fields split this way was chosen in case an existing userspace makes assumptions about the width of the weight field, and to sidestep any endianness issues.
The weight field is currently encoded as the weight value minus one, because weight of 0 is invalid. This same trick is impossible for the new weight_high field, because zero must mean actual zero. With this in place:
- Old userspace is guaranteed to carry weight_high of 0, therefore configuring 8-bit weights as appropriate. When dumping nexthops with 16-bit weight, it would only show the lower 8 bits. But configuring such nexthops implies existence of userspace aware of the extension in the first place.
- New userspace talking to an old kernel will work as long as it only attempts to configure 8-bit weights, where the high-order bits are zero. Old kernel will bounce attempts at configuring >8-bit weights.
Renaming reserved fields as they are allocated for some purpose is commonly done in Linux. Whoever touches a reserved field is doing so at their own risk. nexthop_grp::resvd1 in particular is currently used by at least strace, however they carry an own copy of UAPI headers, and the conversion should be trivial. A helper is provided for decoding the weight out of the two fields. Forcing a conversion seems preferable to bending backwards and introducing anonymous unions or whatever.
--- include/net/nexthop.h | 4 ++-- include/uapi/linux/nexthop.h | 7 ++++++- net/ipv4/nexthop.c | 37 ++++++++++++++++++++++-------------- 3 files changed, 31 insertions(+), 17 deletions(-)
include/net/nexthop.h +2 -2
@@ -97,7 +97,7 @@ struct nh_res_table { struct nh_grp_entry { struct nexthop *nh; - u8 weight; + u16 weight; union { struct {
@@ -180,7 +180,7 @@ struct nh_notifier_single_info { };struct nh_notifier_grp_entry_info { -u8 weight; +u16 weight;struct nh_notifier_single_info nh; };
include/uapi/linux/nexthop.h +6 -1
@@ -16,10 +16,15 @@ struct nhmsg {struct nexthop_grp {__u32 id;/* nexthop id - must exist */ __u8 weight;/* weight of this nexthop */ -__u8 resvd1; +__u8 weight_high;/* high order bits of weight */ __u16 resvd2; }; +static inline __u16 nexthop_grp_weight (const struct nexthop_grp *entry) +{ +return ((entry->weight_high <<8 ) | entry->weight) +1 ; +} +enum {NEXTHOP_GRP_TYPE_MPATH ,/* hash-threshold nexthop group * default type if not specified
net/ipv4/nexthop.c +23 -14
@@ -660,6 +660,7 @@ static int nla_put_nh_group(struct sk_buff *skb, struct nh_group *nhg)size_t len = nhg->num_nh *sizeof (*p);struct nlattr *nla;u16 group_type =0 ; +u16 weight;int i;if (nhg->hash_threshold)
@@ -676,9 +677,12 @@ static int nla_put_nh_group(struct sk_buff *skb, struct nh_group *nhg) p =nla_data (nla);for (i =0 ; i < nhg->num_nh; ++i) { + weight = nhg->nh_entries[i].weight -1 ; + *p++ = (struct nexthop_grp ) { .id = nhg->nh_entries[i].nh->id, - .weight = nhg->nh_entries[i].weight -1 , + .weight = weight, + .weight_high = weight >>8 , }; }
@@ -1062,11 +1066,14 @@ static int nh_check_attr_group(struct net *net, nhg =nla_data (tb[NHA_GROUP ]);for (i =0 ; i < len; ++i) { -if (nhg[i].resvd1 || nhg[i].resvd2) { -NL_SET_ERR_MSG (extack,"Reserved fields in nexthop_grp must be 0" ); +if (nhg[i].resvd2) { +NL_SET_ERR_MSG (extack,"Reserved field in nexthop_grp must be 0" );return -EINVAL ; } -if (nhg[i].weight >254 ) { +if (nexthop_grp_weight (&nhg[i]) ==0 ) { +/* 0xffff got passed in, representing weight of 0x10000, +* which is too heavy. +*/ NL_SET_ERR_MSG (extack,"Invalid value for weight" );return -EINVAL ; }
@@ -1655,9 +1662,9 @@ static void nh_res_table_cancel_upkeep(struct nh_res_table *res_table)static void nh_res_group_rebalance (struct nh_group *nhg,struct nh_res_table *res_table) { -int prev_upper_bound =0 ; -int total =0 ; -int w =0 ; +u16 prev_upper_bound =0 ; +u32 total =0 ; +u32 w =0 ;int i;INIT_LIST_HEAD (&res_table->uw_nh_entries);
@@ -1667,11 +1674,12 @@ static void nh_res_group_rebalance(struct nh_group *nhg,for (i =0 ; i < nhg->num_nh; ++i) {struct nh_grp_entry *nhge = &nhg->nh_entries[i]; -int upper_bound; +u16 upper_bound; +u64 btw; w += nhge->weight; - upper_bound =DIV_ROUND_CLOSEST (res_table->num_nh_buckets * w, - total); + btw = ((u64 )res_table->num_nh_buckets) * w; + upper_bound =DIV_ROUND_CLOSEST_ULL (btw, total); nhge->res.wants_buckets = upper_bound - prev_upper_bound; prev_upper_bound = upper_bound;
@@ -1737,8 +1745,8 @@ static void replace_nexthop_grp_res(struct nh_group *oldg,static void nh_hthr_group_rebalance (struct nh_group *nhg) { -int total =0 ; -int w =0 ; +u32 total =0 ; +u32 w =0 ;int i;for (i =0 ; i < nhg->num_nh; ++i)
@@ -1746,7 +1754,7 @@ static void nh_hthr_group_rebalance(struct nh_group *nhg)for (i =0 ; i < nhg->num_nh; ++i) {struct nh_grp_entry *nhge = &nhg->nh_entries[i]; -int upper_bound; +u32 upper_bound; w += nhge->weight; upper_bound =DIV_ROUND_CLOSEST_ULL ((u64 )w <<31 , total) -1 ;
@@ -2492,7 +2500,8 @@ static struct nexthop *nexthop_create_group(struct net *net, nhg->has_v4 = true; nhg->nh_entries[i].nh = nhe; - nhg->nh_entries[i].weight = entry[i].weight +1 ; + nhg->nh_entries[i].weight =nexthop_grp_weight (&entry[i]); +list_add (&nhg->nh_entries[i].nh_list, &nhe->grp_list); nhg->nh_entries[i].nh_parent = nh; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 579/1887 drm/amd/display: Add NULL test for timing generator in dcn21_set_pipe()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Srinivasan Shanmugam <[email protected]>
[ Upstream commit 66951d98d9bf45ba25acf37fe0747253fafdf298 ]
In "u32 otg_inst = pipe_ctx->stream_res.tg->inst;" pipe_ctx->stream_res.tg could be NULL, it is relying on the caller to ensure the tg is not NULL.
--- drivers/gpu/drm/amd/display/dc/dcn21/dcn21_hwseq.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-)
drivers/gpu/drm/amd/display/dc/dcn21/dcn21_hwseq.c +8 -3
@@ -182,17 +182,22 @@ void dcn21_set_abm_immediate_disable(struct pipe_ctx *pipe_ctx)void dcn21_set_pipe (struct pipe_ctx *pipe_ctx) {struct abm *abm = pipe_ctx->stream_res.abm; -uint32_t otg_inst = pipe_ctx->stream_res.tg->inst; +struct timing_generator *tg = pipe_ctx->stream_res.tg;struct panel_cntl *panel_cntl = pipe_ctx->stream->link->panel_cntl;struct dmcu *dmcu = pipe_ctx->stream->ctx->dc->res_pool->dmcu; +uint32_t otg_inst; + +if (!abm || !tg || !panel_cntl) +return ; + + otg_inst = tg->inst;if (dmcu) {dce110_set_pipe (pipe_ctx);return ; } -if (abm && panel_cntl) -dmub_abm_set_pipe (abm, otg_inst,SET_ABM_PIPE_NORMAL , panel_cntl->inst); +dmub_abm_set_pipe (abm, otg_inst,SET_ABM_PIPE_NORMAL , panel_cntl->inst); } bool dcn21_set_backlight_level(struct pipe_ctx *pipe_ctx,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 580/1887 drm/amd/display: Fix panel_cntl could be null in dcn21_set_backlight_level()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Srinivasan Shanmugam <[email protected]>
[ Upstream commit e96fddb32931d007db12b1fce9b5e8e4c080401b ]
'panel_cntl' structure used to control the display panel could be null, dereferencing it could lead to a null pointer access.
Fixes the below:drivers/gpu/drm/amd/amdgpu/../display/dc/hwss/dcn21/dcn21_hwseq.c:269 dcn21_set_backlight_level() error: we previously assumed 'panel_cntl' could be null (see line 250)
--- drivers/gpu/drm/amd/display/dc/dcn21/dcn21_hwseq.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-)
drivers/gpu/drm/amd/display/dc/dcn21/dcn21_hwseq.c +8 -3
@@ -207,16 +207,21 @@ bool dcn21_set_backlight_level(struct pipe_ctx *pipe_ctx,union dmub_rb_cmd cmd;struct dc_context *dc = pipe_ctx->stream->ctx;struct abm *abm = pipe_ctx->stream_res.abm; -uint32_t otg_inst = pipe_ctx->stream_res.tg->inst; +struct timing_generator *tg = pipe_ctx->stream_res.tg;struct panel_cntl *panel_cntl = pipe_ctx->stream->link->panel_cntl; +uint32_t otg_inst; + +if (!abm || !tg || !panel_cntl) +return false; + + otg_inst = tg->inst;if (dc->dc->res_pool->dmcu) {dce110_set_backlight_level (pipe_ctx, backlight_pwm_u16_16, frame_ramp);return true; } -if (abm && panel_cntl) -dmub_abm_set_pipe (abm, otg_inst,SET_ABM_PIPE_NORMAL , panel_cntl->inst); +dmub_abm_set_pipe (abm, otg_inst,SET_ABM_PIPE_NORMAL , panel_cntl->inst);memset (&cmd,0 ,sizeof (cmd)); cmd.abm_set_backlight.header.type =DMUB_CMD__ABM ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 581/1887 net/sched: flower: Fix chain template offload
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ido Schimmel <[email protected]>
[ Upstream commit 32f2a0afa95fae0d1ceec2ff06e0e816939964b8 ]
When a qdisc is deleted from a net device the stack instructs the underlying driver to remove its flow offload callback from the associated filter block using the 'FLOW_BLOCK_UNBIND' command. The stack then continues to replay the removal of the filters in the block for this driver by iterating over the chains in the block and invoking the 'reoffload' operation of the classifier being used. In turn, the classifier in its 'reoffload' operation prepares and emits a 'FLOW_CLS_DESTROY' command for each filter.
However, the stack does not do the same for chain templates and the underlying driver never receives a 'FLOW_CLS_TMPLT_DESTROY' command when a qdisc is deleted. This results in a memory leak [1] which can be reproduced using [2].
Fix by introducing a 'tmplt_reoffload' operation and have the stack invoke it with the appropriate arguments as part of the replay. Implement the operation in the sole classifier that supports chain templates (flower) by emitting the 'FLOW_CLS_TMPLT_{CREATE,DESTROY}' command based on whether a flow offload callback is being bound to a filter block or being unbound from one.
As far as I can tell, the issue happens since cited commit which reordered tcf_block_offload_unbind() before tcf_block_flush_all_chains() in __tcf_block_put(). The order cannot be reversed as the filter block is expected to be freed after flushing all the chains.
[1]
unreferenced object 0xffff888107e28800 (size 2048):
comm "tc", pid 1079, jiffies 4294958525 (age 3074.287s) hex dump (first 32 bytes):
b1 a6 7c 11 81 88 ff ff e0 5b b3 10 81 88 ff ff ..|......[......
01 00 00 00 00 00 00 00 e0 aa b0 84 ff ff ff ff ................
backtrace:
[<ffffffff81c06a68>] __kmem_cache_alloc_node+0x1e8/0x320
[<ffffffff81ab374e>] __kmalloc+0x4e/0x90
[<ffffffff832aec6d>] mlxsw_sp_acl_ruleset_get+0x34d/0x7a0
[<ffffffff832bc195>] mlxsw_sp_flower_tmplt_create+0x145/0x180
[<ffffffff832b2e1a>] mlxsw_sp_flow_block_cb+0x1ea/0x280
[<ffffffff83a10613>] tc_setup_cb_call+0x183/0x340
[<ffffffff83a9f85a>] fl_tmplt_create+0x3da/0x4c0
[<ffffffff83a22435>] tc_ctl_chain+0xa15/0x1170
[<ffffffff838a863c>] rtnetlink_rcv_msg+0x3cc/0xed0
[<ffffffff83ac87f0>] netlink_rcv_skb+0x170/0x440
[<ffffffff83ac6270>] netlink_unicast+0x540/0x820
[<ffffffff83ac6e28>] netlink_sendmsg+0x8d8/0xda0
[<ffffffff83793def>] ____sys_sendmsg+0x30f/0xa80
[<ffffffff8379d29a>] ___sys_sendmsg+0x13a/0x1e0
[<ffffffff8379d50c>] __sys_sendmsg+0x11c/0x1f0
[<ffffffff843b9ce0>] do_syscall_64+0x40/0xe0
unreferenced object 0xffff88816d2c0400 (size 1024):
comm "tc", pid 1079, jiffies 4294958525 (age 3074.287s)
hex dump (first 32 bytes):
40 00 00 00 00 00 00 00 57 f6 38 be 00 00 00 00 @.......W.8.....
10 04 2c 6d 81 88 ff ff 10 04 2c 6d 81 88 ff ff ..,m......,m....
backtrace:
[<ffffffff81c06a68>] __kmem_cache_alloc_node+0x1e8/0x320
[<ffffffff81ab36c1>] __kmalloc_node+0x51/0x90
[<ffffffff81a8ed96>] kvmalloc_node+0xa6/0x1f0
[<ffffffff82827d03>] bucket_table_alloc.isra.0+0x83/0x460
[<ffffffff82828d2b>] rhashtable_init+0x43b/0x7c0
[<ffffffff832aed48>] mlxsw_sp_acl_ruleset_get+0x428/0x7a0
[<ffffffff832bc195>] mlxsw_sp_flower_tmplt_create+0x145/0x180
[<ffffffff832b2e1a>] mlxsw_sp_flow_block_cb+0x1ea/0x280
[<ffffffff83a10613>] tc_setup_cb_call+0x183/0x340
[<ffffffff83a9f85a>] fl_tmplt_create+0x3da/0x4c0
[<ffffffff83a22435>] tc_ctl_chain+0xa15/0x1170
[<ffffffff838a863c>] rtnetlink_rcv_msg+0x3cc/0xed0
[<ffffffff83ac87f0>] netlink_rcv_skb+0x170/0x440
[<ffffffff83ac6270>] netlink_unicast+0x540/0x820
[<ffffffff83ac6e28>] netlink_sendmsg+0x8d8/0xda0
[<ffffffff83793def>] ____sys_sendmsg+0x30f/0xa80[2]
# tc qdisc add dev swp1 clsact # tc chain add dev swp1 ingress proto ip chain 1 flower dst_ip 0.0.0.0/32 # tc qdisc del dev swp1 clsact # devlink dev reload pci/0000:06:00.0
--- include/net/sch_generic.h | 4 ++++ net/sched/cls_api.c | 9 ++++++++- net/sched/cls_flower.c | 23 +++++++++++++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-)
include/net/sch_generic.h +4 -0
@@ -378,6 +378,10 @@ struct tcf_proto_ops {struct nlattr **tca, struct netlink_ext_ack *extack);void (*tmplt_destroy)(void *tmplt_priv); +void (*tmplt_reoffload)(struct tcf_chain *chain, +bool add, +flow_setup_cb_t *cb, +void *cb_priv);/* rtnetlink specific */ int (*dump)(struct net*, struct tcf_proto*, void *,
net/sched/cls_api.c +8 -1
@@ -1431,6 +1431,9 @@ tcf_block_playback_offloads(struct tcf_block *block, flow_setup_cb_t *cb, chain_prev = chain, chain = __tcf_get_next_chain(block, chain), tcf_chain_put(chain_prev)) { +if (chain->tmplt_ops && add) + chain->tmplt_ops->tmplt_reoffload (chain, true, cb, + cb_priv); for (tp = __tcf_get_next_proto(chain, NULL); tp; tp_prev = tp, tp = __tcf_get_next_proto(chain, tp),
@@ -1446,6 +1449,9 @@ tcf_block_playback_offloads(struct tcf_block *block, flow_setup_cb_t *cb, goto err_playback_remove; } } +if (chain->tmplt_ops && !add) + chain->tmplt_ops->tmplt_reoffload (chain, false, cb, + cb_priv); }return 0 ;
@@ -2861,7 +2867,8 @@ static int tc_chain_tmplt_add(struct tcf_chain *chain, struct net *net, ops =tcf_proto_lookup_ops (name, true, extack);if (IS_ERR (ops))return PTR_ERR (ops); -if (!ops->tmplt_create || !ops->tmplt_destroy || !ops->tmplt_dump) { +if (!ops->tmplt_create || !ops->tmplt_destroy || !ops->tmplt_dump || + !ops->tmplt_reoffload) {NL_SET_ERR_MSG (extack,"Chain templates are not supported with specified classifier" );module_put (ops->owner);return -EOPNOTSUPP ;
net/sched/cls_flower.c +23 -0
@@ -2596,6 +2596,28 @@ static void fl_tmplt_destroy(void *tmplt_priv)kfree (tmplt); } +static void fl_tmplt_reoffload (struct tcf_chain *chain,bool add, +flow_setup_cb_t *cb,void *cb_priv) +{ +struct fl_flow_tmplt *tmplt = chain->tmplt_priv; +struct flow_cls_offload cls_flower = {}; + + cls_flower.rule =flow_rule_alloc (0 ); +if (!cls_flower.rule) +return ; + + cls_flower.common.chain_index = chain->index; + cls_flower.command = add ?FLOW_CLS_TMPLT_CREATE : +FLOW_CLS_TMPLT_DESTROY ; + cls_flower.cookie = (unsigned long ) tmplt; + cls_flower.rule->match.dissector = &tmplt->dissector; + cls_flower.rule->match.mask = &tmplt->mask; + cls_flower.rule->match.key = &tmplt->dummy_key; + +cb (TC_SETUP_CLSFLOWER , &cls_flower, cb_priv); +kfree (cls_flower.rule); +} +static int fl_dump_key_val (struct sk_buff *skb,void *val,int val_type,void *mask,int mask_type,int len)
@@ -3452,6 +3474,7 @@ static struct tcf_proto_ops cls_fl_ops __read_mostly = { .bind_class = fl_bind_class, .tmplt_create = fl_tmplt_create, .tmplt_destroy = fl_tmplt_destroy, + .tmplt_reoffload = fl_tmplt_reoffload, .tmplt_dump = fl_tmplt_dump, .owner = THIS_MODULE, .flags = TCF_PROTO_OPS_DOIT_UNLOCKED,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 582/1887 arm64: entry: fix ARM64_WORKAROUND_SPECULATIVE_UNPRIV_LOAD
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mark Rutland <[email protected]>
[ Upstream commit 832dd634bd1b4e3bbe9f10b9c9ba5db6f6f2b97f ]
Currently the ARM64_WORKAROUND_SPECULATIVE_UNPRIV_LOAD workaround isn't quite right, as it is supposed to be applied after the last explicit memory access, but is immediately followed by an LDR.
The ARM64_WORKAROUND_SPECULATIVE_UNPRIV_LOAD workaround is used to handle Cortex-A520 erratum 2966298 and Cortex-A510 erratum 3117295, which are described in:
* https://developer.arm.com/documentation/SDEN2444153/0600/?lang=en
* https://developer.arm.com/documentation/SDEN1873361/1600/?lang=en
In both cases the workaround is described as:
| If pagetable isolation is disabled, the context switch logic in the | kernel can be updated to execute the following sequence on affected | cores before exiting to EL0, and after all explicit memory accesses: | | 1. A non-shareable TLBI to any context and/or address, including | unused contexts or addresses, such as a `TLBI VALE1 Xzr`. | | 2. A DSB NSH to guarantee completion of the TLBI.
The important part being that the TLBI+DSB must be placed "after all explicit memory accesses".
Unfortunately, as-implemented, the TLBI+DSB is immediately followed by an LDR, as we have:
| alternative_if ARM64_WORKAROUND_SPECULATIVE_UNPRIV_LOAD | tlbi vale1, xzr | dsb nsh | alternative_else_nop_endif | alternative_if_not ARM64_UNMAP_KERNEL_AT_EL0 | ldr lr, [sp, #S_LR] | add sp, sp, #PT_REGS_SIZE // restore sp | eret | alternative_else_nop_endif | | [ ... KPTI exception return path ... ]
This patch fixes this by reworking the logic to place the TLBI+DSB immediately before the ERET, after all explicit memory accesses.
The ERET is currently in a separate alternative block, and alternatives cannot be nested. To account for this, the alternative block for ARM64_UNMAP_KERNEL_AT_EL0 is replaced with a single alternative branch to skip the KPTI logic, with the new shape of the logic being:
| alternative_insn "b .L_skip_tramp_exit_\@", nop, ARM64_UNMAP_KERNEL_AT_EL0 | [ ... KPTI exception return path ... ] | .L_skip_tramp_exit_\@: | | ldr lr, [sp, #S_LR] | add sp, sp, #PT_REGS_SIZE // restore sp | | alternative_if ARM64_WORKAROUND_SPECULATIVE_UNPRIV_LOAD | tlbi vale1, xzr | dsb nsh | alternative_else_nop_endif | eret
The new structure means that the workaround is only applied when KPTI is not in use; this is fine as noted in the documented implications of the erratum:
| Pagetable isolation between EL0 and higher level ELs prevents the | issue from occurring.
... and as per the workaround description quoted above, the workaround is only necessary "If pagetable isolation is disabled".
--- arch/arm64/kernel/entry.S | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-)
arch/arm64/kernel/entry.S +13 -9
@@ -419,16 +419,9 @@ alternative_else_nop_endif ldp x28, x29, [sp, #16 * 14] .if \el == 0 -alternative_if ARM64_WORKAROUND_SPECULATIVE_UNPRIV_LOAD - tlbi vale1, xzr - dsb nsh -alternative_else_nop_endif -alternative_if_not ARM64_UNMAP_KERNEL_AT_EL0 - ldr lr, [sp, #S_LR] - add sp, sp, #PT_REGS_SIZE // restore sp - eret -alternative_else_nop_endif #ifdef CONFIG_UNMAP_KERNEL_AT_EL0 + alternative_insn "b .L_skip_tramp_exit_\@", nop, ARM64_UNMAP_KERNEL_AT_EL0 + bne 4f msr far_el1, x29 tramp_alias x30, tramp_exit_native, x29
@@ -436,7 +429,18 @@ alternative_else_nop_endif 4: tramp_alias x30, tramp_exit_compat, x29 br x30 + +.L_skip_tramp_exit_\@: #endif + ldr lr, [sp, #S_LR] + add sp, sp, #PT_REGS_SIZE // restore sp + + /* This must be after the last explicit memory access */ +alternative_if ARM64_WORKAROUND_SPECULATIVE_UNPRIV_LOAD + tlbi vale1, xzr + dsb nsh +alternative_else_nop_endif + eret .else ldr lr, [sp, #S_LR] add sp, sp, #PT_REGS_SIZE // restore sp
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 583/1887 rxrpc: Fix delayed ACKs to not set the reference serial number
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Howells <[email protected]>
[ Upstream commit e7870cf13d20f56bfc19f9c3e89707c69cf104ef ]
Fix the construction of delayed ACKs to not set the reference serial number as they can't be used as an RTT reference.
--- net/rxrpc/output.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/rxrpc/output.c +1 -1
@@ -87,7 +87,7 @@ static size_t rxrpc_fill_out_ack(struct rxrpc_connection *conn,return 0 ;/* Barrier against rxrpc_input_data(). */ - serial = call->ackr_serial; + serial = reason ==RXRPC_ACK_DELAY ?0 : call->ackr_serial; hard_ack =READ_ONCE (call->rx_hard_ack); top =smp_load_acquire (&call->rx_top); *_hard_ack = hard_ack;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 584/1887 net: stmmac: protect updates of 64-bit statistics counters
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Petr Tesarik <[email protected]>
[ Upstream commit 38cc3c6dcc09dc3a1800b5ec22aef643ca11eab8 ]
As explained by a comment in <linux/u64_stats_sync.h>, write side of struct u64_stats_sync must ensure mutual exclusion, or one seqcount update could be lost on 32-bit platforms, thus blocking readers forever. Such lockups have been observed in real world after stmmac_xmit() on one CPU raced with stmmac_napi_poll_tx() on another CPU.
To fix the issue without introducing a new lock, split the statics into three parts:
1. fields updated only under the tx queue lock,
2. fields updated only during NAPI poll,
3. fields updated only from interrupt context,
Updates to fields in the first two groups are already serialized through other locks. It is sufficient to split the existing struct u64_stats_sync so that each group has its own.
Note that tx_set_ic_bit is updated from both contexts. Split this counter so that each context gets its own, and calculate their sum to get the total value in stmmac_get_ethtool_stats().
For the third group, multiple interrupts may be processed by different CPUs at the same time, but interrupts on the same CPU will not nest. Move fields from this group to a newly created per-cpu struct stmmac_pcpu_stats.
--- drivers/net/ethernet/stmicro/stmmac/common.h | 56 +++++--- .../net/ethernet/stmicro/stmmac/dwmac-sun8i.c | 15 +- .../net/ethernet/stmicro/stmmac/dwmac4_lib.c | 15 +- .../net/ethernet/stmicro/stmmac/dwmac_lib.c | 15 +- .../ethernet/stmicro/stmmac/dwxgmac2_dma.c | 15 +- .../ethernet/stmicro/stmmac/stmmac_ethtool.c | 129 +++++++++++------ .../net/ethernet/stmicro/stmmac/stmmac_main.c | 133 +++++++++--------- 7 files changed, 221 insertions(+), 157 deletions(-)
drivers/net/ethernet/stmicro/stmmac/common.h +40 -16
@@ -58,28 +58,51 @@#undef FRAME_FILTER_DEBUG/* #define FRAME_FILTER_DEBUG */ +struct stmmac_q_tx_stats { +u64_stats_t tx_bytes; +u64_stats_t tx_set_ic_bit; +u64_stats_t tx_tso_frames; +u64_stats_t tx_tso_nfrags; +}; + +struct stmmac_napi_tx_stats { +u64_stats_t tx_packets; +u64_stats_t tx_pkt_n; +u64_stats_t poll; +u64_stats_t tx_clean; +u64_stats_t tx_set_ic_bit; +}; +struct stmmac_txq_stats { -u64 tx_bytes; -u64 tx_packets; -u64 tx_pkt_n; -u64 tx_normal_irq_n; -u64 napi_poll; -u64 tx_clean; -u64 tx_set_ic_bit; -u64 tx_tso_frames; -u64 tx_tso_nfrags; -struct u64_stats_sync syncp; +/* Updates protected by tx queue lock. */ +struct u64_stats_sync q_syncp; +struct stmmac_q_tx_stats q; + +/* Updates protected by NAPI poll logic. */ +struct u64_stats_sync napi_syncp; +struct stmmac_napi_tx_stats napi; }____cacheline_aligned_in_smp ; +struct stmmac_napi_rx_stats { +u64_stats_t rx_bytes; +u64_stats_t rx_packets; +u64_stats_t rx_pkt_n; +u64_stats_t poll; +}; +struct stmmac_rxq_stats { -u64 rx_bytes; -u64 rx_packets; -u64 rx_pkt_n; -u64 rx_normal_irq_n; -u64 napi_poll; -struct u64_stats_sync syncp; +/* Updates protected by NAPI poll logic. */ +struct u64_stats_sync napi_syncp; +struct stmmac_napi_rx_stats napi; }____cacheline_aligned_in_smp ; +/* Updates on each CPU protected by not allowing nested irqs. */ +struct stmmac_pcpu_stats { +struct u64_stats_sync syncp; +u64_stats_t rx_normal_irq_n[MTL_MAX_TX_QUEUES ]; +u64_stats_t tx_normal_irq_n[MTL_MAX_RX_QUEUES ]; +}; +/* Extra statistic and debug information exposed by ethtool */ struct stmmac_extra_stats {/* Transmit errors */
@@ -204,6 +227,7 @@ struct stmmac_extra_stats {/* per queue statistics */ struct stmmac_txq_stats txq_stats[MTL_MAX_TX_QUEUES ];struct stmmac_rxq_stats rxq_stats[MTL_MAX_RX_QUEUES ]; +struct stmmac_pcpu_stats __percpu *pcpu_stats;unsigned long rx_dropped;unsigned long rx_errors;unsigned long tx_dropped;
drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c +7 -8
@@ -440,8 +440,7 @@ static int sun8i_dwmac_dma_interrupt(struct stmmac_priv *priv, struct stmmac_extra_stats *x, u32 chan, u32 dir) { - struct stmmac_rxq_stats *rxq_stats = &priv->xstats.rxq_stats[chan]; -struct stmmac_txq_stats *txq_stats = &priv->xstats.txq_stats[chan]; + struct stmmac_pcpu_stats *stats = this_cpu_ptr(priv->xstats.pcpu_stats);int ret =0 ;u32 v;
@@ -454,9 +453,9 @@ static int sun8i_dwmac_dma_interrupt(struct stmmac_priv *priv,if (v &EMAC_TX_INT ) { ret |= handle_tx; -u64_stats_update_begin (&txq_stats->syncp); - txq_stats->tx_normal_irq_n++; -u64_stats_update_end (&txq_stats->syncp); +u64_stats_update_begin (&stats->syncp); +u64_stats_inc (&stats->tx_normal_irq_n[chan]); +u64_stats_update_end (&stats->syncp); }if (v &EMAC_TX_DMA_STOP_INT )
@@ -478,9 +477,9 @@ static int sun8i_dwmac_dma_interrupt(struct stmmac_priv *priv,if (v &EMAC_RX_INT ) { ret |= handle_rx; -u64_stats_update_begin (&rxq_stats->syncp); - rxq_stats->rx_normal_irq_n++; -u64_stats_update_end (&rxq_stats->syncp); +u64_stats_update_begin (&stats->syncp); +u64_stats_inc (&stats->rx_normal_irq_n[chan]); +u64_stats_update_end (&stats->syncp); }if (v &EMAC_RX_BUF_UA_INT )
drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c +7 -8
@@ -171,8 +171,7 @@ int dwmac4_dma_interrupt(struct stmmac_priv *priv, void __iomem *ioaddr,const struct dwmac4_addrs *dwmac4_addrs = priv->plat->dwmac4_addrs;u32 intr_status =readl (ioaddr +DMA_CHAN_STATUS (dwmac4_addrs, chan));u32 intr_en =readl (ioaddr +DMA_CHAN_INTR_ENA (dwmac4_addrs, chan)); -struct stmmac_rxq_stats *rxq_stats = &priv->xstats.rxq_stats[chan]; -struct stmmac_txq_stats *txq_stats = &priv->xstats.txq_stats[chan]; +struct stmmac_pcpu_stats *stats =this_cpu_ptr (priv->xstats.pcpu_stats);int ret =0 ;if (dir ==DMA_DIR_RX )
@@ -201,15 +200,15 @@ int dwmac4_dma_interrupt(struct stmmac_priv *priv, void __iomem *ioaddr, }/* TX/RX NORMAL interrupts */ if (likely (intr_status &DMA_CHAN_STATUS_RI )) { -u64_stats_update_begin (&rxq_stats->syncp); - rxq_stats->rx_normal_irq_n++; -u64_stats_update_end (&rxq_stats->syncp); +u64_stats_update_begin (&stats->syncp); +u64_stats_inc (&stats->rx_normal_irq_n[chan]); +u64_stats_update_end (&stats->syncp); ret |= handle_rx; }if (likely (intr_status &DMA_CHAN_STATUS_TI )) { -u64_stats_update_begin (&txq_stats->syncp); - txq_stats->tx_normal_irq_n++; -u64_stats_update_end (&txq_stats->syncp); +u64_stats_update_begin (&stats->syncp); +u64_stats_inc (&stats->tx_normal_irq_n[chan]); +u64_stats_update_end (&stats->syncp); ret |= handle_tx; }
drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c +7 -8
@@ -162,8 +162,7 @@ static void show_rx_process_state(unsigned int status)int dwmac_dma_interrupt (struct stmmac_priv *priv,void __iomem *ioaddr,struct stmmac_extra_stats *x,u32 chan,u32 dir) { -struct stmmac_rxq_stats *rxq_stats = &priv->xstats.rxq_stats[chan]; -struct stmmac_txq_stats *txq_stats = &priv->xstats.txq_stats[chan]; +struct stmmac_pcpu_stats *stats =this_cpu_ptr (priv->xstats.pcpu_stats);int ret =0 ;/* read the status register (CSR5) */ u32 intr_status =readl (ioaddr +DMA_STATUS );
@@ -215,16 +214,16 @@ int dwmac_dma_interrupt(struct stmmac_priv *priv, void __iomem *ioaddr,u32 value =readl (ioaddr +DMA_INTR_ENA );/* to schedule NAPI on real RIE event. */ if (likely (value &DMA_INTR_ENA_RIE )) { -u64_stats_update_begin (&rxq_stats->syncp); - rxq_stats->rx_normal_irq_n++; -u64_stats_update_end (&rxq_stats->syncp); +u64_stats_update_begin (&stats->syncp); +u64_stats_inc (&stats->rx_normal_irq_n[chan]); +u64_stats_update_end (&stats->syncp); ret |= handle_rx; } }if (likely (intr_status &DMA_STATUS_TI )) { -u64_stats_update_begin (&txq_stats->syncp); - txq_stats->tx_normal_irq_n++; -u64_stats_update_end (&txq_stats->syncp); +u64_stats_update_begin (&stats->syncp); +u64_stats_inc (&stats->tx_normal_irq_n[chan]); +u64_stats_update_end (&stats->syncp); ret |= handle_tx; }if (unlikely (intr_status &DMA_STATUS_ERI ))
drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c +7 -8
@@ -333,8 +333,7 @@ static int dwxgmac2_dma_interrupt(struct stmmac_priv *priv, struct stmmac_extra_stats *x, u32 chan, u32 dir) { - struct stmmac_rxq_stats *rxq_stats = &priv->xstats.rxq_stats[chan]; -struct stmmac_txq_stats *txq_stats = &priv->xstats.txq_stats[chan]; + struct stmmac_pcpu_stats *stats = this_cpu_ptr(priv->xstats.pcpu_stats);u32 intr_status =readl (ioaddr +XGMAC_DMA_CH_STATUS (chan));u32 intr_en =readl (ioaddr +XGMAC_DMA_CH_INT_EN (chan));int ret =0 ;
@@ -363,15 +362,15 @@ static int dwxgmac2_dma_interrupt(struct stmmac_priv *priv,/* TX/RX NORMAL interrupts */ if (likely (intr_status &XGMAC_NIS )) {if (likely (intr_status &XGMAC_RI )) { -u64_stats_update_begin (&rxq_stats->syncp); - rxq_stats->rx_normal_irq_n++; -u64_stats_update_end (&rxq_stats->syncp); +u64_stats_update_begin (&stats->syncp); +u64_stats_inc (&stats->rx_normal_irq_n[chan]); +u64_stats_update_end (&stats->syncp); ret |= handle_rx; }if (likely (intr_status & (XGMAC_TI |XGMAC_TBU ))) { -u64_stats_update_begin (&txq_stats->syncp); - txq_stats->tx_normal_irq_n++; -u64_stats_update_end (&txq_stats->syncp); +u64_stats_update_begin (&stats->syncp); +u64_stats_inc (&stats->tx_normal_irq_n[chan]); +u64_stats_update_end (&stats->syncp); ret |= handle_tx; } }
drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +87 -42
170 lines not on this page · raw message
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +66 -67
273 lines not on this page · raw message
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 585/1887 net/sched: act_mirred: use the backlog for mirred ingress
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jakub Kicinski <[email protected]>
[ Upstream commit 52f671db18823089a02f07efc04efdb2272ddc17 ]
The test Davide added in commit ca22da2fbd69 ("act_mirred: use the backlog for nested calls to mirred ingress") hangs our testing VMs every 10 or so runs, with the familiar tcp_v4_rcv -> tcp_v4_rcv deadlock reported by lockdep.
The problem as previously described by Davide (see Link) is that if we reverse flow of traffic with the redirect (egress -> ingress) we may reach the same socket which generated the packet. And we may still be holding its socket lock. The common solution to such deadlocks is to put the packet in the Rx backlog, rather than run the Rx path inline. Do that for all egress -> ingress reversals, not just once we started to nest mirred calls.
In the past there was a concern that the backlog indirection will lead to loss of error reporting / less accurate stats. But the current workaround does not seem to address the issue.
--- net/sched/act_mirred.c | 14 +++++--------- .../testing/selftests/net/forwarding/tc_actions.sh | 3 --- 2 files changed, 5 insertions(+), 12 deletions(-)
net/sched/act_mirred.c +5 -9
@@ -205,18 +205,14 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,return err; } -static bool is_mirred_nested (void ) -{ -return unlikely (__this_cpu_read (mirred_nest_level) >1 ); -} - -static int tcf_mirred_forward (bool want_ingress,struct sk_buff *skb) +static int +tcf_mirred_forward (bool at_ingress,bool want_ingress,struct sk_buff *skb) {int err;if (!want_ingress) err =tcf_dev_queue_xmit (skb, dev_queue_xmit); -else if (is_mirred_nested ()) +else if (!at_ingress) err =netif_rx (skb);else err =netif_receive_skb (skb);
@@ -312,7 +308,7 @@ static int tcf_mirred_act(struct sk_buff *skb, const struct tc_action *a,/* let's the caller reinsert the packet, if possible */ if (use_reinsert) { - err =tcf_mirred_forward (want_ingress, skb); + err =tcf_mirred_forward (at_ingress, want_ingress, skb);if (err)tcf_action_inc_overlimit_qstats (&m->common);__this_cpu_dec (mirred_nest_level);
@@ -320,7 +316,7 @@ static int tcf_mirred_act(struct sk_buff *skb, const struct tc_action *a, } } - err =tcf_mirred_forward (want_ingress, skb2); + err =tcf_mirred_forward (at_ingress, want_ingress, skb2);if (err)tcf_action_inc_overlimit_qstats (&m->common);__this_cpu_dec (mirred_nest_level);
tools/testing/selftests/net/forwarding/tc_actions.sh +0 -3
@@ -235,9 +235,6 @@ mirred_egress_to_ingress_tcp_test() check_err $? "didn't mirred redirect ICMP" tc_check_packets "dev $h1 ingress" 102 10 check_err $? "didn't drop mirred ICMP" - local overlimits=$(tc_rule_stats_get ${h1} 101 egress .overlimits) - test ${overlimits} = 10 - check_err $? "wrong overlimits, expected 10 got ${overlimits}" tc filter del dev $h1 egress protocol ip pref 100 handle 100 flower tc filter del dev $h1 egress protocol ip pref 101 handle 101 flower
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 586/1887 ASoC: qcom: Fix uninitialized pointer dmactl
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Colin Ian King <[email protected]>
[ Upstream commit 1382d8b55129875b2e07c4d2a7ebc790183769ee ]
In the case where __lpass_get_dmactl_handle is called and the driver id dai_id is invalid the pointer dmactl is not being assigned a value, and dmactl contains a garbage value since it has not been initialized and so the null check may not work. Fix this to initialize dmactl to NULL. One could argue that modern compilers will set this to zero, but it is useful to keep this initialized as per the same way in functions __lpass_platform_codec_intf_init and lpass_cdc_dma_daiops_hw_params.
Cleans up clang scan build warning:sound/soc/qcom/lpass-cdc-dma.c:275:7: warning: Branch condition evaluates to a garbage value [core.uninitialized.Branch]
--- sound/soc/qcom/lpass-cdc-dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
sound/soc/qcom/lpass-cdc-dma.c +1 -1
@@ -258,7 +258,7 @@ static int lpass_cdc_dma_daiops_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream); -struct lpaif_dmactl *dmactl; +struct lpaif_dmactl *dmactl =NULL ;int ret =0 , id;switch (cmd) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 587/1887 spi: lpspi: Avoid potential use-after-free in probe()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alexander Sverdlin <[email protected]>
[ Upstream commit 2ae0ab0143fcc06190713ed81a6486ed0ad3c861 ]
fsl_lpspi_probe() is allocating/disposing memory manually with spi_alloc_host()/spi_alloc_target(), but uses devm_spi_register_controller(). In case of error after the latter call the memory will be explicitly freed in the probe function by spi_controller_put() call, but used afterwards by "devm" management outside probe() (spi_unregister_controller() <- devm_spi_unregister() below).
Unable to handle kernel NULL pointer dereference at virtual address 0000000000000070 ... Call trace: kernfs_find_ns kernfs_find_and_get_ns sysfs_remove_group sysfs_remove_groups device_remove_attrs device_del spi_unregister_controller devm_spi_unregister release_nodes devres_release_all really_probe driver_probe_device __device_attach_driver bus_for_each_drv __device_attach device_initial_probe bus_probe_device deferred_probe_work_func process_one_work worker_thread kthread ret_from_fork
--- drivers/spi/spi-fsl-lpspi.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-)
drivers/spi/spi-fsl-lpspi.c +4 -5
@@ -827,11 +827,11 @@ static int fsl_lpspi_probe(struct platform_device *pdev) is_slave =of_property_read_bool ((&pdev->dev)->of_node,"spi-slave" );if (is_slave) - controller =spi_alloc_slave (&pdev->dev, -sizeof (struct fsl_lpspi_data )); + controller =devm_spi_alloc_target (&pdev->dev, +sizeof (struct fsl_lpspi_data ));else - controller =spi_alloc_master (&pdev->dev, -sizeof (struct fsl_lpspi_data )); + controller =devm_spi_alloc_host (&pdev->dev, +sizeof (struct fsl_lpspi_data ));if (!controller)return -ENOMEM ;
@@ -931,7 +931,6 @@ static int fsl_lpspi_probe(struct platform_device *pdev)pm_runtime_put_sync (fsl_lpspi->dev);pm_runtime_disable (fsl_lpspi->dev); out_controller_put: -spi_controller_put (controller);return ret; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 588/1887 f2fs: introduce f2fs_is_readonly() for readability
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yangtao Li <[email protected]>
[ Upstream commit ed8ac22b6b75804743f1dae6563d75f85cfd1483 ]
Introduce f2fs_is_readonly() and use it to simplify code.
Stable-dep-of: 9f0c4a46be1f ("f2fs: fix to truncate meta inode pages forcely")
--- fs/f2fs/f2fs.h | 5 +++++ fs/f2fs/super.c | 5 ++--- 2 files changed, 7 insertions(+), 3 deletions(-)
fs/f2fs/f2fs.h +5 -0
@@ -4596,6 +4596,11 @@ static inline void f2fs_handle_page_eio(struct f2fs_sb_info *sbi, pgoff_t ofs, } } +static inline bool f2fs_is_readonly (struct f2fs_sb_info *sbi) +{ +return f2fs_sb_has_readonly (sbi) ||f2fs_readonly (sbi->sb); +} +#define EFSBADCRC EBADMSG/* Bad CRC detected */ #define EFSCORRUPTED EUCLEAN/* Filesystem is corrupted */
fs/f2fs/super.c +2 -3
@@ -1400,8 +1400,7 @@ static int parse_options(struct super_block *sb, char *options, bool is_remount)return -EINVAL ; } -if ((f2fs_sb_has_readonly (sbi) ||f2fs_readonly (sbi->sb)) && -test_opt (sbi,FLUSH_MERGE )) { +if (f2fs_is_readonly (sbi) &&test_opt (sbi,FLUSH_MERGE )) {f2fs_err (sbi,"FLUSH_MERGE not compatible with readonly mode" );return -EINVAL ; }
@@ -2144,7 +2143,7 @@ static void default_options(struct f2fs_sb_info *sbi, bool remount)set_opt (sbi,MERGE_CHECKPOINT );F2FS_OPTION (sbi).unusable_cap =0 ; sbi->sb->s_flags |=SB_LAZYTIME ; -if (!f2fs_sb_has_readonly (sbi) && !f2fs_readonly (sbi->sb)) +if (!f2fs_is_readonly (sbi))set_opt (sbi,FLUSH_MERGE );if (f2fs_sb_has_blkzoned (sbi))F2FS_OPTION (sbi).fs_mode =FS_MODE_LFS ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 589/1887 f2fs: introduce f2fs_invalidate_internal_cache() for cleanup
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chao Yu <[email protected]>
[ Upstream commit 4e4f1eb9949b10cb7d76370fd27d41f20ef2b32b ]
Just cleanup, no logic changes.
Stable-dep-of: 9f0c4a46be1f ("f2fs: fix to truncate meta inode pages forcely")
--- fs/f2fs/data.c | 8 +++----- fs/f2fs/f2fs.h | 7 +++++++ fs/f2fs/gc.c | 5 ++--- fs/f2fs/segment.c | 14 ++++---------- 4 files changed, 16 insertions(+), 18 deletions(-)
fs/f2fs/data.c +3 -5
@@ -1458,11 +1458,9 @@ static int __allocate_data_block(struct dnode_of_data *dn, int seg_type) old_blkaddr = dn->data_blkaddr;f2fs_allocate_data_block (sbi,NULL , old_blkaddr, &dn->data_blkaddr, &sum, seg_type,NULL ); -if (GET_SEGNO (sbi, old_blkaddr) !=NULL_SEGNO ) { -invalidate_mapping_pages (META_MAPPING (sbi), - old_blkaddr, old_blkaddr); -f2fs_invalidate_compress_page (sbi, old_blkaddr); - } +if (GET_SEGNO (sbi, old_blkaddr) !=NULL_SEGNO ) +f2fs_invalidate_internal_cache (sbi, old_blkaddr); +f2fs_update_data_blkaddr (dn, dn->data_blkaddr);return 0 ; }
fs/f2fs/f2fs.h +7 -0
@@ -4601,6 +4601,13 @@ static inline bool f2fs_is_readonly(struct f2fs_sb_info *sbi)return f2fs_sb_has_readonly (sbi) ||f2fs_readonly (sbi->sb); } +static inline void f2fs_invalidate_internal_cache (struct f2fs_sb_info *sbi, +block_t blkaddr) +{ +invalidate_mapping_pages (META_MAPPING (sbi), blkaddr, blkaddr); +f2fs_invalidate_compress_page (sbi, blkaddr); +} +#define EFSBADCRC EBADMSG/* Bad CRC detected */ #define EFSCORRUPTED EUCLEAN/* Filesystem is corrupted */
fs/f2fs/gc.c +2 -3
@@ -1377,9 +1377,8 @@ static int move_data_block(struct inode *inode, block_t bidx,memcpy (page_address (fio.encrypted_page),page_address (mpage),PAGE_SIZE );f2fs_put_page (mpage,1 ); -invalidate_mapping_pages (META_MAPPING (fio.sbi), - fio.old_blkaddr, fio.old_blkaddr); -f2fs_invalidate_compress_page (fio.sbi, fio.old_blkaddr); + +f2fs_invalidate_internal_cache (fio.sbi, fio.old_blkaddr);set_page_dirty (fio.encrypted_page);if (clear_page_dirty_for_io (fio.encrypted_page))
fs/f2fs/segment.c +4 -10
@@ -2298,8 +2298,7 @@ void f2fs_invalidate_blocks(struct f2fs_sb_info *sbi, block_t addr)if (addr ==NEW_ADDR || addr ==COMPRESS_ADDR )return ; -invalidate_mapping_pages (META_MAPPING (sbi), addr, addr); -f2fs_invalidate_compress_page (sbi, addr); +f2fs_invalidate_internal_cache (sbi, addr);/* add it into sit main buffer */ down_write (&sit_i->sentry_lock);
@@ -3369,11 +3368,8 @@ static void do_write_page(struct f2fs_summary *sum, struct f2fs_io_info *fio) reallocate:f2fs_allocate_data_block (fio->sbi, fio->page, fio->old_blkaddr, &fio->new_blkaddr, sum, type, fio); -if (GET_SEGNO (fio->sbi, fio->old_blkaddr) !=NULL_SEGNO ) { -invalidate_mapping_pages (META_MAPPING (fio->sbi), - fio->old_blkaddr, fio->old_blkaddr); -f2fs_invalidate_compress_page (fio->sbi, fio->old_blkaddr); - } +if (GET_SEGNO (fio->sbi, fio->old_blkaddr) !=NULL_SEGNO ) +f2fs_invalidate_internal_cache (fio->sbi, fio->old_blkaddr);/* writeout dirty page into bdev */ f2fs_submit_page_write (fio);
@@ -3568,9 +3564,7 @@ void f2fs_do_replace_block(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,update_sit_entry (sbi, new_blkaddr,1 ); }if (GET_SEGNO (sbi, old_blkaddr) !=NULL_SEGNO ) { -invalidate_mapping_pages (META_MAPPING (sbi), - old_blkaddr, old_blkaddr); -f2fs_invalidate_compress_page (sbi, old_blkaddr); +f2fs_invalidate_internal_cache (sbi, old_blkaddr);if (!from_gc)update_segment_mtime (sbi, old_blkaddr,0 );update_sit_entry (sbi, old_blkaddr,-1 );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 590/1887 f2fs: fix to truncate meta inode pages forcely
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chao Yu <[email protected]>
[ Upstream commit 9f0c4a46be1fe9b97dbe66d49204c1371e3ece65 ]
Below race case can cause data corruption:
Thread A GC thread
- gc_data_segment
- ra_data_block
- locked meta_inode page
- f2fs_inplace_write_data
- invalidate_mapping_pages
: fail to invalidate meta_inode page
due to lock failure or dirty|writeback
status
- f2fs_submit_page_bio
: write last dirty data to old blkaddr
- move_data_block
- load old data from meta_inode page
- f2fs_submit_page_write : write old data to new blkaddr
Because invalidate_mapping_pages() will skip invalidating page which has unclear status including locked, dirty, writeback and so on, so we need to use truncate_inode_pages_range() instead of invalidate_mapping_pages() to make sure meta_inode page will be dropped.
--- fs/f2fs/checkpoint.c | 5 +++-- fs/f2fs/f2fs.h | 28 +++++++++++++++++++++++++++- fs/f2fs/segment.c | 5 ++--- include/linux/f2fs_fs.h | 1 + 4 files changed, 33 insertions(+), 6 deletions(-)
fs/f2fs/checkpoint.c +3 -2
@@ -1609,8 +1609,9 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)*/ if (f2fs_sb_has_encrypt (sbi) ||f2fs_sb_has_verity (sbi) ||f2fs_sb_has_compression (sbi)) -invalidate_mapping_pages (META_MAPPING (sbi), -MAIN_BLKADDR (sbi),MAX_BLKADDR (sbi) -1 ); +f2fs_bug_on (sbi, +invalidate_inode_pages2_range (META_MAPPING (sbi), +MAIN_BLKADDR (sbi),MAX_BLKADDR (sbi) -1 ));f2fs_release_ino_entry (sbi, false);
fs/f2fs/f2fs.h +27 -1
@@ -4601,10 +4601,36 @@ static inline bool f2fs_is_readonly(struct f2fs_sb_info *sbi)return f2fs_sb_has_readonly (sbi) ||f2fs_readonly (sbi->sb); } +static inline void f2fs_truncate_meta_inode_pages (struct f2fs_sb_info *sbi, +block_t blkaddr,unsigned int cnt) +{ +bool need_submit = false; +int i =0 ; + +do { +struct page *page; + + page =find_get_page (META_MAPPING (sbi), blkaddr + i); +if (page) { +if (PageWriteback (page)) + need_submit = true; +f2fs_put_page (page,0 ); + } + }while (++i < cnt && !need_submit); + +if (need_submit) +f2fs_submit_merged_write_cond (sbi, sbi->meta_inode, +NULL ,0 ,DATA ); + +truncate_inode_pages_range (META_MAPPING (sbi), +F2FS_BLK_TO_BYTES ((loff_t )blkaddr), +F2FS_BLK_END_BYTES ((loff_t)(blkaddr + cnt -1 ))); +} +static inline void f2fs_invalidate_internal_cache (struct f2fs_sb_info *sbi,block_t blkaddr) { -invalidate_mapping_pages (META_MAPPING (sbi), blkaddr, blkaddr); +f2fs_truncate_meta_inode_pages (sbi, blkaddr,1 );f2fs_invalidate_compress_page (sbi, blkaddr); }
fs/f2fs/segment.c +2 -3
@@ -3462,8 +3462,7 @@ int f2fs_inplace_write_data(struct f2fs_io_info *fio) }if (fio->post_read) -invalidate_mapping_pages (META_MAPPING (sbi), - fio->new_blkaddr, fio->new_blkaddr); +f2fs_truncate_meta_inode_pages (sbi, fio->new_blkaddr,1 );stat_inc_inplace_blocks (fio->sbi);
@@ -3653,7 +3652,7 @@ void f2fs_wait_on_block_writeback_range(struct inode *inode, block_t blkaddr,for (i =0 ; i < len; i++)f2fs_wait_on_block_writeback (inode, blkaddr + i); -invalidate_mapping_pages (META_MAPPING (sbi), blkaddr, blkaddr + len -1 ); +f2fs_truncate_meta_inode_pages (sbi, blkaddr, len); }static int read_compacted_summaries (struct f2fs_sb_info *sbi)
include/linux/f2fs_fs.h +1 -0
@@ -27,6 +27,7 @@#define F2FS_BYTES_TO_BLK (bytes) ((bytes) >> F2FS_BLKSIZE_BITS)#define F2FS_BLK_TO_BYTES (blk) ((blk) << F2FS_BLKSIZE_BITS) +#define F2FS_BLK_END_BYTES (blk) (F2FS_BLK_TO_BYTES(blk + 1) - 1)/* 0, 1(node nid), 2(meta nid) are reserved node id */ #define F2FS_RESERVED_NODE_NUM 3
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 591/1887 drm/bridge: adv7511: fix crash on irq during probe
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mads Bligaard Nielsen <[email protected]>
[ Upstream commit aeedaee5ef5468caf59e2bb1265c2116e0c9a924 ]
Moved IRQ registration down to end of adv7511_probe().
If an IRQ already is pending during adv7511_probe (before adv7511_cec_init) then cec_received_msg_ts could crash using uninitialized data:
Unable to handle kernel read from unreadable memory at virtual address 00000000000003d5
Internal error: Oops: 96000004 [#1] PREEMPT_RT SMP
Call trace:
cec_received_msg_ts+0x48/0x990 [cec]
adv7511_cec_irq_process+0x1cc/0x308 [adv7511]
adv7511_irq_process+0xd8/0x120 [adv7511]
adv7511_irq_handler+0x1c/0x30 [adv7511]
irq_thread_fn+0x30/0xa0
irq_thread+0x14c/0x238
kthread+0x190/0x1a8--- drivers/gpu/drm/bridge/adv7511/adv7511_drv.c | 22 ++++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-)
drivers/gpu/drm/bridge/adv7511/adv7511_drv.c +11 -11
@@ -1292,17 +1292,6 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)INIT_WORK (&adv7511->hpd_work, adv7511_hpd_work); -if (i2c->irq) { -init_waitqueue_head (&adv7511->wq); - - ret =devm_request_threaded_irq (dev, i2c->irq,NULL , - adv7511_irq_handler, -IRQF_ONESHOT ,dev_name (dev), - adv7511); -if (ret) - goto err_unregister_cec; - } -adv7511_power_off (adv7511);i2c_set_clientdata (i2c, adv7511);
@@ -1326,6 +1315,17 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)adv7511_audio_init (dev, adv7511); +if (i2c->irq) { +init_waitqueue_head (&adv7511->wq); + + ret =devm_request_threaded_irq (dev, i2c->irq,NULL , + adv7511_irq_handler, +IRQF_ONESHOT ,dev_name (dev), + adv7511); +if (ret) + goto err_unregister_audio; + } +if (adv7511->type ==ADV7533 || adv7511->type ==ADV7535 ) { ret =adv7533_attach_dsi (adv7511);if (ret)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 592/1887 Bluetooth: af_bluetooth: Fix deadlock
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Luiz Augusto von Dentz <[email protected]>
[ Upstream commit f7b94bdc1ec107c92262716b073b3e816d4784fb ]
Attemting to do sock_lock on .recvmsg may cause a deadlock as shown bellow, so instead of using sock_sock this uses sk_receive_queue.lock on bt_sock_ioctl to avoid the UAF:
INFO: task kworker/u9:1:121 blocked for more than 30 seconds.
Not tainted 6.7.6-lemon #183
Workqueue: hci0 hci_rx_work
Call Trace:
<TASK>
__schedule+0x37d/0xa00
schedule+0x32/0xe0
__lock_sock+0x68/0xa0
? __pfx_autoremove_wake_function+0x10/0x10
lock_sock_nested+0x43/0x50
l2cap_sock_recv_cb+0x21/0xa0
l2cap_recv_frame+0x55b/0x30a0
? psi_task_switch+0xeb/0x270
? finish_task_switch.isra.0+0x93/0x2a0
hci_rx_work+0x33a/0x3f0
process_one_work+0x13a/0x2f0
worker_thread+0x2f0/0x410
? __pfx_worker_thread+0x10/0x10
kthread+0xe0/0x110
? __pfx_kthread+0x10/0x10
ret_from_fork+0x2c/0x50
? __pfx_kthread+0x10/0x10
ret_from_fork_asm+0x1b/0x30
</TASK>--- net/bluetooth/af_bluetooth.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-)
net/bluetooth/af_bluetooth.c +3 -7
@@ -363,14 +363,11 @@ int bt_sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,if (flags &MSG_OOB )return -EOPNOTSUPP ; -lock_sock (sk); - skb =skb_recv_datagram (sk, flags, &err);if (!skb) {if (sk->sk_shutdown &RCV_SHUTDOWN ) err =0 ; -release_sock (sk);return err; }
@@ -396,8 +393,6 @@ int bt_sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,skb_free_datagram (sk, skb); -release_sock (sk); -if (flags &MSG_TRUNC ) copied = skblen;
@@ -630,10 +625,11 @@ int bt_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)if (sk->sk_state ==BT_LISTEN )return -EINVAL ; -lock_sock (sk); +spin_lock (&sk->sk_receive_queue.lock); skb =skb_peek (&sk->sk_receive_queue); amount = skb ? skb->len :0 ; -release_sock (sk); +spin_unlock (&sk->sk_receive_queue.lock); + err =put_user (amount, (int __user *)arg);break ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 593/1887 Bluetooth: hci_core: Fix possible buffer overflow
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Luiz Augusto von Dentz <[email protected]>
[ Upstream commit 81137162bfaa7278785b24c1fd2e9e74f082e8e4 ]
struct hci_dev_info has a fixed size name[8] field so in the event that hdev->name is bigger than that strcpy would attempt to write past its size, so this fixes this problem by switching to use strscpy.
--- net/bluetooth/hci_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/bluetooth/hci_core.c +1 -1
@@ -877,7 +877,7 @@ int hci_get_dev_info(void __user *arg)else flags = hdev->flags; -strcpy (di.name, hdev->name); +strscpy (di.name, hdev->name,sizeof (di.name)); di.bdaddr = hdev->bdaddr; di.type = (hdev->bus &0x0f ); di.flags = flags;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 594/1887 perf: RISC-V: Define helper functions expose hpm counter width and count
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Atish Patra <[email protected]>
[ Upstream commit 585e351ff359c032ea7ab48d999b252ba09f8051 ]
KVM module needs to know how many hardware counters and the counter width that the platform supports. Otherwise, it will not be able to show optimal value of virtual counters to the guest. The virtual hardware counters also need to have the same width as the logical hardware counters for simplicity. However, there shouldn't be mapping between virtual hardware counters and logical hardware counters. As we don't support hetergeneous harts or counters with different width as of now, the implementation relies on the counter width of the first available programmable counter.
Stable-dep-of: 34b567868777 ("perf: RISCV: Fix panic on pmu overflow handler")
--- drivers/perf/riscv_pmu_sbi.c | 37 ++++++++++++++++++++++++++++++++-- include/linux/perf/riscv_pmu.h | 3 +++ 2 files changed, 38 insertions(+), 2 deletions(-)
drivers/perf/riscv_pmu_sbi.c +35 -2
@@ -43,12 +43,15 @@ static const struct attribute_group *riscv_pmu_attr_groups[] = { };/* -* RISC-V doesn't have hetergenous harts yet. This need to be part of +* RISC-V doesn't have heterogeneous harts yet. This need to be part of * per_cpu in case of harts with different pmu counters */ static union sbi_pmu_ctr_info *pmu_ctr_list;static unsigned int riscv_pmu_irq; +/* Cache the available counters in a bitmask */ +static unsigned long cmask; + struct sbi_pmu_event_data { union { union {
@@ -264,6 +267,37 @@ static bool pmu_sbi_ctr_is_fw(int cidx)return (info->type ==SBI_PMU_CTR_TYPE_FW ) ? true : false; } +/* +* Returns the counter width of a programmable counter and number of hardware +* counters. As we don't support heterogeneous CPUs yet, it is okay to just +* return the counter width of the first programmable counter. +*/ +int riscv_pmu_get_hpm_info (u32 *hw_ctr_width,u32 *num_hw_ctr) +{ +int i; +union sbi_pmu_ctr_info *info; +u32 hpm_width =0 , hpm_count =0 ; + +if (!cmask) +return -EINVAL ; + +for_each_set_bit (i, &cmask,RISCV_MAX_COUNTERS ) { + info = &pmu_ctr_list[i]; +if (!info) +continue ; +if (!hpm_width && info->csr !=CSR_CYCLE && info->csr !=CSR_INSTRET ) + hpm_width = info->width; +if (info->type ==SBI_PMU_CTR_TYPE_HW ) + hpm_count++; + } + + *hw_ctr_width = hpm_width; + *num_hw_ctr = hpm_count; + +return 0 ; +} +EXPORT_SYMBOL_GPL (riscv_pmu_get_hpm_info); +static int pmu_sbi_ctr_get_idx (struct perf_event *event) {struct hw_perf_event *hwc = &event->hw;
@@ -798,7 +832,6 @@ static void riscv_pmu_destroy(struct riscv_pmu *pmu)static int pmu_sbi_device_probe (struct platform_device *pdev) {struct riscv_pmu *pmu =NULL ; -unsigned long cmask =0 ;int ret = -ENODEV ;int num_counters;
include/linux/perf/riscv_pmu.h +3 -0
@@ -73,6 +73,9 @@ void riscv_pmu_legacy_skip_init(void);static inline void riscv_pmu_legacy_skip_init (void ) {};#endif struct riscv_pmu *riscv_pmu_alloc (void ); +#ifdef CONFIG_RISCV_PMU_SBI +int riscv_pmu_get_hpm_info (u32 *hw_ctr_width,u32 *num_hw_ctr); +#endif #endif /* CONFIG_RISCV_PMU */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 595/1887 perf: RISCV: Fix panic on pmu overflow handler
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fei Wu <[email protected]>
[ Upstream commit 34b567868777e9fd39ec5333969728a7f0cf179c ]
(1 << idx) of int is not desired when setting bits in unsigned long overflowed_ctrs, use BIT() instead. This panic happens when running 'perf record -e branches' on sophgo sg2042.
[ 273.311852] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000098 [ 273.320851] Oops [#1] [ 273.323179] Modules linked in: [ 273.326303] CPU: 0 PID: 1475 Comm: perf Not tainted 6.6.0-rc3+ #9 [ 273.332521] Hardware name: Sophgo Mango (DT) [ 273.336878] epc : riscv_pmu_ctr_get_width_mask+0x8/0x62 [ 273.342291] ra : pmu_sbi_ovf_handler+0x2e0/0x34e [ 273.347091] epc : ffffffff80aecd98 ra : ffffffff80aee056 sp : fffffff6e36928b0 [ 273.354454] gp : ffffffff821f82d0 tp : ffffffd90c353200 t0 : 0000002ade4f9978 [ 273.361815] t1 : 0000000000504d55 t2 : ffffffff8016cd8c s0 : fffffff6e3692a70 [ 273.369180] s1 : 0000000000000020 a0 : 0000000000000000 a1 : 00001a8e81800000 [ 273.376540] a2 : 0000003c00070198 a3 : 0000003c00db75a4 a4 : 0000000000000015 [ 273.383901] a5 : ffffffd7ff8804b0 a6 : 0000000000000015 a7 : 000000000000002a [ 273.391327] s2 : 000000000000ffff s3 : 0000000000000000 s4 : ffffffd7ff8803b0 [ 273.398773] s5 : 0000000000504d55 s6 : ffffffd905069800 s7 : ffffffff821fe210 [ 273.406139] s8 : 000000007fffffff s9 : ffffffd7ff8803b0 s10: ffffffd903f29098 [ 273.413660] s11: 0000000080000000 t3 : 0000000000000003 t4 : ffffffff8017a0ca [ 273.421022] t5 : ffffffff8023cfc2 t6 : ffffffd9040780e8 [ 273.426437] status: 0000000200000100 badaddr: 0000000000000098 cause: 000000000000000d [ 273.434512] [<ffffffff80aecd98>] riscv_pmu_ctr_get_width_mask+0x8/0x62 [ 273.441169] [<ffffffff80076bd8>] handle_percpu_devid_irq+0x98/0x1ee [ 273.447562] [<ffffffff80071158>] generic_handle_domain_irq+0x28/0x36 [ 273.454151] [<ffffffff8047a99a>] riscv_intc_irq+0x36/0x4e [ 273.459659] [<ffffffff80c944de>] handle_riscv_irq+0x4a/0x74 [ 273.465442] [<ffffffff80c94c48>] do_irq+0x62/0x92 [ 273.470360] Code: 0420 60a2 6402 5529 0141 8082 0013 0000 0013 0000 (6d5c) b783 [ 273.477921] ---[ end trace 0000000000000000 ]--- [ 273.482630] Kernel panic - not syncing: Fatal exception in interrupt
--- drivers/perf/riscv_pmu_sbi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/perf/riscv_pmu_sbi.c +2 -2
@@ -651,14 +651,14 @@ static irqreturn_t pmu_sbi_ovf_handler(int irq, void *dev)/* compute hardware counter index */ hidx = info->csr -CSR_CYCLE ;/* check if the corresponding bit is set in sscountovf */ -if (!(overflow & (1 << hidx))) +if (!(overflow &BIT (hidx)))continue ;/* * Keep a track of overflowed counters so that they can be started * with updated initial value. */ - overflowed_ctrs |=1 << lidx; + overflowed_ctrs |=BIT (lidx); hw_evt = &event->hw;riscv_pmu_event_update (event);perf_sample_data_init (&data,0 , hw_evt->last_period);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 596/1887 ARM: 9359/1: flush: check if the folio is reserved for no-mapping addresses
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yongqiang Liu <[email protected]>
[ Upstream commit 0c66c6f4e21cb22220cbd8821c5c73fc157d20dc ]
Since commit a4d5613c4dc6 ("arm: extend pfn_valid to take into account freed memory map alignment") changes the semantics of pfn_valid() to check presence of the memory map for a PFN. A valid page for an address which is reserved but not mapped by the kernel[1], the system crashed during some uio test with the following memory layout:
node 0: [mem 0x00000000c0a00000-0x00000000cc8fffff]
node 0: [mem 0x00000000d0000000-0x00000000da1fffff]
the uio layout is:0xc0900000, 0x100000
the crash backtrace like:
Unable to handle kernel paging request at virtual address bff00000 [...] CPU: 1 PID: 465 Comm: startapp.bin Tainted: G O 5.10.0 #1 Hardware name: Generic DT based system PC is at b15_flush_kern_dcache_area+0x24/0x3c LR is at __sync_icache_dcache+0x6c/0x98 [...] (b15_flush_kern_dcache_area) from (__sync_icache_dcache+0x6c/0x98) (__sync_icache_dcache) from (set_pte_at+0x28/0x54) (set_pte_at) from (remap_pfn_range+0x1a0/0x274) (remap_pfn_range) from (uio_mmap+0x184/0x1b8 [uio]) (uio_mmap [uio]) from (__mmap_region+0x264/0x5f4) (__mmap_region) from (__do_mmap_mm+0x3ec/0x440) (__do_mmap_mm) from (do_mmap+0x50/0x58) (do_mmap) from (vm_mmap_pgoff+0xfc/0x188) (vm_mmap_pgoff) from (ksys_mmap_pgoff+0xac/0xc4) (ksys_mmap_pgoff) from (ret_fast_syscall+0x0/0x5c) Code: e0801001 e2423001 e1c00003 f57ff04f (ee070f3e) ---[ end trace 09cf0734c3805d52 ]--- Kernel panic - not syncing: Fatal exception
So check if PG_reserved was set to solve this issue.
[1]: https://lore.kernel.org/lkml/[email protected]/
--- arch/arm/mm/flush.c | 2 ++ 1 file changed, 2 insertions(+)
arch/arm/mm/flush.c +2 -0
@@ -280,6 +280,8 @@ void __sync_icache_dcache(pte_t pteval)return ; page =pfn_to_page (pfn); +if (PageReserved (page)) +return ;if (cache_is_vipt_aliasing ()) mapping =page_mapping_file (page);else
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 597/1887 drm/amd/display: Add a dc_state NULL check in dc_state_release
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Allen Pan <[email protected]>
[ Upstream commit 334b56cea5d9df5989be6cf1a5898114fa70ad98 ]
[How]
Check wheather state is NULL before releasing it.
--- drivers/gpu/drm/amd/display/dc/core/dc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/gpu/drm/amd/display/dc/core/dc.c +2 -1
@@ -2283,7 +2283,8 @@ static void dc_state_free(struct kref *kref)void dc_release_state (struct dc_state *context) { -kref_put (&context->refcount, dc_state_free); +if (context !=NULL ) +kref_put (&context->refcount, dc_state_free); } bool dc_set_generic_gpio_for_stereo(bool enable,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 598/1887 net: esp: fix bad handling of pages from page_pool
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dragos Tatulea <[email protected]>
[ Upstream commit c3198822c6cb9fb588e446540485669cc81c5d34 ]
When the skb is reorganized during esp_output (!esp->inline), the pages coming from the original skb fragments are supposed to be released back to the system through put_page. But if the skb fragment pages are originating from a page_pool, calling put_page on them will trigger a page_pool leak which will eventually result in a crash.
This leak can be easily observed when using CONFIG_DEBUG_VM and doing ipsec + gre (non offloaded) forwarding:
BUG: Bad page state in process ksoftirqd/16 pfn:1451b6 page:00000000de2b8d32 refcount:0 mapcount:0 mapping:0000000000000000 index:0x1451b6000 pfn:0x1451b6 flags: 0x200000000000000(node=0|zone=2) page_type: 0xffffffff() raw: 0200000000000000 dead000000000040 ffff88810d23c000 0000000000000000 raw: 00000001451b6000 0000000000000001 00000000ffffffff 0000000000000000 page dumped because: page_pool leak Modules linked in: ip_gre gre mlx5_ib mlx5_core xt_conntrack xt_MASQUERADE nf_conntrack_netlink nfnetlink iptable_nat nf_nat xt_addrtype br_netfilter rpcrdma rdma_ucm ib_iser libiscsi scsi_transport_iscsi ib_umad rdma_cm ib_ipoib iw_cm ib_cm ib_uverbs ib_core overlay zram zsmalloc fuse [last unloaded: mlx5_core] CPU: 16 PID: 96 Comm: ksoftirqd/16 Not tainted 6.8.0-rc4+ #22 Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS rel-1.13.0-0-gf21b5a4aeb02-prebuilt.qemu.org 04/01/2014 Call Trace: <TASK> dump_stack_lvl+0x36/0x50 bad_page+0x70/0xf0 free_unref_page_prepare+0x27a/0x460 free_unref_page+0x38/0x120 esp_ssg_unref.isra.0+0x15f/0x200 esp_output_tail+0x66d/0x780 esp_xmit+0x2c5/0x360 validate_xmit_xfrm+0x313/0x370 ? validate_xmit_skb+0x1d/0x330 validate_xmit_skb_list+0x4c/0x70 sch_direct_xmit+0x23e/0x350 __dev_queue_xmit+0x337/0xba0 ? nf_hook_slow+0x3f/0xd0 ip_finish_output2+0x25e/0x580 iptunnel_xmit+0x19b/0x240 ip_tunnel_xmit+0x5fb/0xb60 ipgre_xmit+0x14d/0x280 [ip_gre] dev_hard_start_xmit+0xc3/0x1c0 __dev_queue_xmit+0x208/0xba0 ? nf_hook_slow+0x3f/0xd0 ip_finish_output2+0x1ca/0x580 ip_sublist_rcv_finish+0x32/0x40 ip_sublist_rcv+0x1b2/0x1f0 ? ip_rcv_finish_core.constprop.0+0x460/0x460 ip_list_rcv+0x103/0x130 __netif_receive_skb_list_core+0x181/0x1e0 netif_receive_skb_list_internal+0x1b3/0x2c0 napi_gro_receive+0xc8/0x200 gro_cell_poll+0x52/0x90 __napi_poll+0x25/0x1a0 net_rx_action+0x28e/0x300 __do_softirq+0xc3/0x276 ? sort_range+0x20/0x20 run_ksoftirqd+0x1e/0x30 smpboot_thread_fn+0xa6/0x130 kthread+0xcd/0x100 ? kthread_complete_and_exit+0x20/0x20 ret_from_fork+0x31/0x50 ? kthread_complete_and_exit+0x20/0x20 ret_from_fork_asm+0x11/0x20 </TASK>
The suggested fix is to introduce a new wrapper (skb_page_unref) that covers page refcounting for page_pool pages as well.
--- include/linux/skbuff.h | 10 ++++++++++ net/ipv4/esp4.c | 8 ++++---- net/ipv6/esp6.c | 8 ++++---- 3 files changed, 18 insertions(+), 8 deletions(-)
include/linux/skbuff.h +10 -0
@@ -3480,6 +3480,16 @@ static inline void skb_frag_ref(struct sk_buff *skb, int f)__skb_frag_ref (&skb_shinfo (skb)->frags[f]); } +static inline void +skb_page_unref (const struct sk_buff *skb,struct page *page,bool napi_safe) +{ +#ifdef CONFIG_PAGE_POOL +if (skb->pp_recycle &&page_pool_return_skb_page (page)) +return ; +#endif +put_page (page); +} +/** * __skb_frag_unref - release a reference on a paged fragment. * @frag: the paged fragment
net/ipv4/esp4.c +4 -4
@@ -95,7 +95,7 @@ static inline struct scatterlist *esp_req_sg(struct crypto_aead *aead, __alignof__(struct scatterlist)); } -static void esp_ssg_unref (struct xfrm_state *x,void *tmp) +static void esp_ssg_unref (struct xfrm_state *x,void *tmp,struct sk_buff *skb) {struct crypto_aead *aead = x->data;int extralen =0 ;
@@ -114,7 +114,7 @@ static void esp_ssg_unref(struct xfrm_state *x, void *tmp)*/ if (req->src != req->dst)for (sg =sg_next (req->src); sg; sg =sg_next (sg)) -put_page (sg_page (sg)); +skb_page_unref (skb,sg_page (sg), false); }#ifdef CONFIG_INET_ESPINTCP
@@ -218,7 +218,7 @@ static void esp_output_done(struct crypto_async_request *base, int err) } tmp =ESP_SKB_CB (skb)->tmp; -esp_ssg_unref (x, tmp); +esp_ssg_unref (x, tmp, skb);kfree (tmp);if (xo && (xo->flags &XFRM_DEV_RESUME )) {
@@ -608,7 +608,7 @@ int esp_output_tail(struct xfrm_state *x, struct sk_buff *skb, struct esp_info * }if (sg != dsg) -esp_ssg_unref (x, tmp); +esp_ssg_unref (x, tmp, skb);if (!err && x->encap && x->encap->encap_type ==TCP_ENCAP_ESPINTCP ) err =esp_output_tail_tcp (x, skb);
net/ipv6/esp6.c +4 -4
@@ -112,7 +112,7 @@ static inline struct scatterlist *esp_req_sg(struct crypto_aead *aead, __alignof__(struct scatterlist)); } -static void esp_ssg_unref (struct xfrm_state *x,void *tmp) +static void esp_ssg_unref (struct xfrm_state *x,void *tmp,struct sk_buff *skb) {struct crypto_aead *aead = x->data;int extralen =0 ;
@@ -131,7 +131,7 @@ static void esp_ssg_unref(struct xfrm_state *x, void *tmp)*/ if (req->src != req->dst)for (sg =sg_next (req->src); sg; sg =sg_next (sg)) -put_page (sg_page (sg)); +skb_page_unref (skb,sg_page (sg), false); }#ifdef CONFIG_INET6_ESPINTCP
@@ -252,7 +252,7 @@ static void esp_output_done(struct crypto_async_request *base, int err) } tmp =ESP_SKB_CB (skb)->tmp; -esp_ssg_unref (x, tmp); +esp_ssg_unref (x, tmp, skb);kfree (tmp);esp_output_encap_csum (skb);
@@ -646,7 +646,7 @@ int esp6_output_tail(struct xfrm_state *x, struct sk_buff *skb, struct esp_info }if (sg != dsg) -esp_ssg_unref (x, tmp); +esp_ssg_unref (x, tmp, skb);if (!err && x->encap && x->encap->encap_type ==TCP_ENCAP_ESPINTCP ) err =esp_output_tail_tcp (x, skb);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 599/1887 net/sched: Fix mirred deadlock on device recursion
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 0f022d32c3eca477fbf79a205243a6123ed0fe11 ]
When the mirred action is used on a classful egress qdisc and a packet is mirrored or redirected to self we hit a qdisc lock deadlock. See trace below.
[..... other info removed for brevity....]
[ 82.890906]
[ 82.890906] ============================================
[ 82.890906] WARNING: possible recursive locking detected
[ 82.890906] 6.8.0-05205-g77fadd89fe2d-dirty #213 Tainted: G W
[ 82.890906] --------------------------------------------
[ 82.890906] ping/418 is trying to acquire lock:
[ 82.890906] ffff888006994110 (&sch->q.lock){+.-.}-{3:3}, at:
__dev_queue_xmit+0x1778/0x3550
[ 82.890906]
[ 82.890906] but task is already holding lock:
[ 82.890906] ffff888006994110 (&sch->q.lock){+.-.}-{3:3}, at:
__dev_queue_xmit+0x1778/0x3550
[ 82.890906]
[ 82.890906] other info that might help us debug this:
[ 82.890906] Possible unsafe locking scenario:
[ 82.890906]
[ 82.890906] CPU0
[ 82.890906] ----
[ 82.890906] lock(&sch->q.lock);
[ 82.890906] lock(&sch->q.lock);
[ 82.890906]
[ 82.890906] *** DEADLOCK ***
[ 82.890906][..... other info removed for brevity....]
Example setup (eth0->eth0) to recreate
tc qdisc add dev eth0 root handle 1: htb default 30 tc filter add dev eth0 handle 1: protocol ip prio 2 matchall \
action mirred egress redirect dev eth0
Another example(eth0->eth1->eth0) to recreate
tc qdisc add dev eth0 root handle 1: htb default 30 tc filter add dev eth0 handle 1: protocol ip prio 2 matchall \
action mirred egress redirect dev eth1
tc qdisc add dev eth1 root handle 1: htb default 30 tc filter add dev eth1 handle 1: protocol ip prio 2 matchall \
action mirred egress redirect dev eth0
We fix this by adding an owner field (CPU id) to struct Qdisc set after root qdisc is entered. When the softirq enters it a second time, if the qdisc owner is the same CPU, the packet is dropped to break the loop.
--- include/net/sch_generic.h | 1 + net/core/dev.c | 6 ++++++ net/sched/sch_generic.c | 1 + 3 files changed, 8 insertions(+)
include/net/sch_generic.h +1 -0
@@ -117,6 +117,7 @@ struct Qdisc {struct qdisc_skb_head q;struct gnet_stats_basic_sync bstats;struct gnet_stats_queue qstats; +int owner;unsigned long state;unsigned long state2;/* must be written under qdisc spinlock */ struct Qdisc *next_sched;
net/core/dev.c +6 -0
@@ -3948,6 +3948,10 @@ static inline int __dev_xmit_skb(struct sk_buff *skb, struct Qdisc *q,return rc; } +if (unlikely (READ_ONCE (q->owner) ==smp_processor_id ())) { +kfree_skb_reason (skb,SKB_DROP_REASON_TC_EGRESS ); +return NET_XMIT_DROP ; + }/* * Heuristic to force contended enqueues to serialize on a * separate lock before trying to get qdisc main lock.
@@ -3987,7 +3991,9 @@ static inline int __dev_xmit_skb(struct sk_buff *skb, struct Qdisc *q,qdisc_run_end (q); rc =NET_XMIT_SUCCESS ; }else { +WRITE_ONCE (q->owner,smp_processor_id ()); rc =dev_qdisc_enqueue (skb, q, &to_free, txq); +WRITE_ONCE (q->owner,-1 );if (qdisc_run_begin (q)) {if (unlikely (contended)) {spin_unlock (&q->busylock);
net/sched/sch_generic.c +1 -0
@@ -986,6 +986,7 @@ struct Qdisc *qdisc_alloc(struct netdev_queue *dev_queue, sch->enqueue = ops->enqueue; sch->dequeue = ops->dequeue; sch->dev_queue = dev_queue; + sch->owner =-1 ;netdev_hold (dev, &sch->dev_tracker,GFP_KERNEL );refcount_set (&sch->refcnt,1 );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 600/1887 netfilter: nf_tables: fix memleak in map from abort path
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pablo Neira Ayuso <[email protected]>
[ Upstream commit 86a1471d7cde792941109b93b558b5dc078b9ee9 ]
The delete set command does not rely on the transaction object for element removal, therefore, a combination of delete element + delete set from the abort path could result in restoring twice the refcount of the mapping.
Check for inactive element in the next generation for the delete element command in the abort path, skip restoring state if next generation bit has been already cleared. This is similar to the activate logic using the set walk iterator.
[ 6170.286929] ------------[ cut here ]------------ [ 6170.286939] WARNING: CPU: 6 PID: 790302 at net/netfilter/nf_tables_api.c:2086 nf_tables_chain_destroy+0x1f7/0x220 [nf_tables] [ 6170.287071] Modules linked in: [...] [ 6170.287633] CPU: 6 PID: 790302 Comm: kworker/6:2 Not tainted 6.9.0-rc3+ #365 [ 6170.287768] RIP: 0010:nf_tables_chain_destroy+0x1f7/0x220 [nf_tables] [ 6170.287886] Code: df 48 8d 7d 58 e8 69 2e 3b df 48 8b 7d 58 e8 80 1b 37 df 48 8d 7d 68 e8 57 2e 3b df 48 8b 7d 68 e8 6e 1b 37 df 48 89 ef eb c4 <0f> 0b 48 83 c4 08 5b 5d 41 5c 41 5d 41 5e 41 5f c3 cc cc cc cc 0f [ 6170.287895] RSP: 0018:ffff888134b8fd08 EFLAGS: 00010202 [ 6170.287904] RAX: 0000000000000001 RBX: ffff888125bffb28 RCX: dffffc0000000000 [ 6170.287912] RDX: 0000000000000003 RSI: ffffffffa20298ab RDI: ffff88811ebe4750 [ 6170.287919] RBP: ffff88811ebe4700 R08: ffff88838e812650 R09: fffffbfff0623a55 [ 6170.287926] R10: ffffffff8311d2af R11: 0000000000000001 R12: ffff888125bffb10 [ 6170.287933] R13: ffff888125bffb10 R14: dead000000000122 R15: dead000000000100 [ 6170.287940] FS: 0000000000000000(0000) GS:ffff888390b00000(0000) knlGS:0000000000000000 [ 6170.287948] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 6170.287955] CR2: 00007fd31fc00710 CR3: 0000000133f60004 CR4: 00000000001706f0 [ 6170.287962] Call Trace: [ 6170.287967] <TASK> [ 6170.287973] ? __warn+0x9f/0x1a0 [ 6170.287986] ? nf_tables_chain_destroy+0x1f7/0x220 [nf_tables] [ 6170.288092] ? report_bug+0x1b1/0x1e0 [ 6170.287986] ? nf_tables_chain_destroy+0x1f7/0x220 [nf_tables] [ 6170.288092] ? report_bug+0x1b1/0x1e0 [ 6170.288104] ? handle_bug+0x3c/0x70 [ 6170.288112] ? exc_invalid_op+0x17/0x40 [ 6170.288120] ? asm_exc_invalid_op+0x1a/0x20 [ 6170.288132] ? nf_tables_chain_destroy+0x2b/0x220 [nf_tables] [ 6170.288243] ? nf_tables_chain_destroy+0x1f7/0x220 [nf_tables] [ 6170.288366] ? nf_tables_chain_destroy+0x2b/0x220 [nf_tables] [ 6170.288483] nf_tables_trans_destroy_work+0x588/0x590 [nf_tables]
--- net/netfilter/nf_tables_api.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-)
net/netfilter/nf_tables_api.c +14 -2
@@ -6869,6 +6869,16 @@ void nft_data_hold(const struct nft_data *data, enum nft_data_types type) } } +static int nft_setelem_active_next (const struct net *net, +const struct nft_set *set, +struct nft_set_elem *elem) +{ +const struct nft_set_ext *ext =nft_set_elem_ext (set, elem->priv); +u8 genmask =nft_genmask_next (net); + +return nft_set_elem_active (ext, genmask); +} +static void nft_setelem_data_activate (const struct net *net,const struct nft_set *set,struct nft_set_elem *elem)
@@ -10233,8 +10243,10 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)case NFT_MSG_DELSETELEM : te = (struct nft_trans_elem *)trans->data; -nft_setelem_data_activate (net, te->set, &te->elem); -nft_setelem_activate (net, te->set, &te->elem); +if (!nft_setelem_active_next (net, te->set, &te->elem)) { +nft_setelem_data_activate (net, te->set, &te->elem); +nft_setelem_activate (net, te->set, &te->elem); + }if (!nft_setelem_is_catchall (te->set, &te->elem)) te->set->ndeact--;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 601/1887 drm/amd/display: fix NULL checks for adev->dm.dc in amdgpu_dm_fini()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nikita Zhandarovich <[email protected]>
[ Upstream commit 2a3cfb9a24a28da9cc13d2c525a76548865e182c ]
Since 'adev->dm.dc' in amdgpu_dm_fini() might turn out to be NULL before the call to dc_enable_dmub_notifications(), check beforehand to ensure there will not be a possible NULL-ptr-deref there.
Also, since commit 1e88eb1b2c25 ("drm/amd/display: Drop CONFIG_DRM_AMD_DC_HDCP") there are two separate checks for NULL in 'adev->dm.dc' before dc_deinit_callbacks() and dc_dmub_srv_destroy(). Clean up by combining them all under one 'if'.
Found by Linux Verification Center (linuxtesting.org) with static analysis tool SVACE.
--- .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-)
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +9 -9
@@ -1881,19 +1881,19 @@ static void amdgpu_dm_fini(struct amdgpu_device *adev)hdcp_destroy (&adev->dev->kobj, adev->dm.hdcp_workqueue); adev->dm.hdcp_workqueue = NULL; } +#endif -if (adev->dm.dc) +if (adev->dm.dc) { +#ifdef CONFIG_DRM_AMD_DC_HDCP dc_deinit_callbacks (adev->dm.dc);#endif - -if (adev->dm.dc)dc_dmub_srv_destroy (&adev->dm.dc->ctx->dmub_srv); - -if (dc_enable_dmub_notifications (adev->dm.dc)) { -kfree (adev->dm.dmub_notify); - adev->dm.dmub_notify =NULL ; -destroy_workqueue (adev->dm.delayed_hpd_wq); - adev->dm.delayed_hpd_wq =NULL ; +if (dc_enable_dmub_notifications (adev->dm.dc)) { +kfree (adev->dm.dmub_notify); + adev->dm.dmub_notify =NULL ; +destroy_workqueue (adev->dm.delayed_hpd_wq); + adev->dm.delayed_hpd_wq =NULL ; + } }if (adev->dm.dmub_bo)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 602/1887 ASoC: SOF: ipc4-pcm: Workaround for crashed firmware on system suspend
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Peter Ujfalusi <[email protected]>
[ Upstream commit c40aad7c81e5fba34b70123ed7ce3397fa62a4d2 ]
When the system is suspended while audio is active, the sof_ipc4_pcm_hw_free() is invoked to reset the pipelines since during suspend the DSP is turned off, streams will be re-started after resume.
If the firmware crashes during while audio is running (or when we reset the stream before suspend) then the sof_ipc4_set_multi_pipeline_state() will fail with IPC error and the state change is interrupted. This will cause misalignment between the kernel and firmware state on next DSP boot resulting errors returned by firmware for IPC messages, eventually failing the audio resume.
On stream close the errors are ignored so the kernel state will be corrected on the next DSP boot, so the second boot after the DSP panic.
If sof_ipc4_trigger_pipelines() is called from sof_ipc4_pcm_hw_free() then state parameter is SOF_IPC4_PIPE_RESET and only in this case.
Treat a forced pipeline reset similarly to how we treat a pcm_free by ignoring error on state sending to allow the kernel's state to be consistent with the state the firmware will have after the next boot.
--- sound/soc/sof/ipc4-pcm.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-)
sound/soc/sof/ipc4-pcm.c +15 -2
@@ -86,7 +86,9 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,if (ret <0 ) {dev_err (sdev->dev,"failed to pause pipeline %d\n" , swidget->pipeline_id); -return ret; +if (sdev->fw_state !=SOF_FW_CRASHED || + state !=SOF_IPC4_PIPE_RESET ) +return ret; } }
@@ -100,7 +102,18 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,if (ret <0 ) {dev_err (sdev->dev,"failed to set state %d for pipeline %d\n" , state, swidget->pipeline_id); -break ; +/* +* workaround: if the firmware is crashed while setting the +* pipelines to reset state we must ignore the error code and +* reset it to 0. +* Since the firmware is crashed we will not send IPC messages +* and we are going to see errors printed, but the state of the +* widgets will be correct for the next boot. +*/ +if (sdev->fw_state !=SOF_FW_CRASHED || state !=SOF_IPC4_PIPE_RESET ) +break ; + + ret =0 ; } pipeline->state = state;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 603/1887 nouveau: lock the client object tree.
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dave Airlie <[email protected]>
[ Upstream commit b7cc4ff787a572edf2c55caeffaa88cd801eb135 ]
It appears the client object tree has no locking unless I've missed something else. Fix races around adding/removing client objects, mostly vram bar mappings.
4562.099306] general protection fault, probably for non-canonical address 0x6677ed422bceb80c: 0000 [#1] PREEMPT SMP PTI
[ 4562.099314] CPU: 2 PID: 23171 Comm: deqp-vk Not tainted 6.8.0-rc6+ #27 [ 4562.099324] Hardware name: Gigabyte Technology Co., Ltd. Z390 I AORUS PRO WIFI/Z390 I AORUS PRO WIFI-CF, BIOS F8 11/05/2021 [ 4562.099330] RIP: 0010:nvkm_object_search+0x1d/0x70 [nouveau] [ 4562.099503] Code: 90 90 90 90 90 90 90 90 90 90 90 90 90 66 0f 1f 00 0f 1f 44 00 00 48 89 f8 48 85 f6 74 39 48 8b 87 a0 00 00 00 48 85 c0 74 12 <48> 8b 48 f8 48 39 ce 73 15 48 8b 40 10 48 85 c0 75 ee 48 c7 c0 fe [ 4562.099506] RSP: 0000:ffffa94cc420bbf8 EFLAGS: 00010206 [ 4562.099512] RAX: 6677ed422bceb814 RBX: ffff98108791f400 RCX: ffff9810f26b8f58 [ 4562.099517] RDX: 0000000000000000 RSI: ffff9810f26b9158 RDI: ffff98108791f400 [ 4562.099519] RBP: ffff9810f26b9158 R08: 0000000000000000 R09: 0000000000000000 [ 4562.099521] R10: ffffa94cc420bc48 R11: 0000000000000001 R12: ffff9810f02a7cc0 [ 4562.099526] R13: 0000000000000000 R14: 00000000000000ff R15: 0000000000000007 [ 4562.099528] FS: 00007f629c5017c0(0000) GS:ffff98142c700000(0000) knlGS:0000000000000000 [ 4562.099534] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 4562.099536] CR2: 00007f629a882000 CR3: 000000017019e004 CR4: 00000000003706f0 [ 4562.099541] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 4562.099542] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 [ 4562.099544] Call Trace: [ 4562.099555] <TASK> [ 4562.099573] ? die_addr+0x36/0x90 [ 4562.099583] ? exc_general_protection+0x246/0x4a0 [ 4562.099593] ? asm_exc_general_protection+0x26/0x30 [ 4562.099600] ? nvkm_object_search+0x1d/0x70 [nouveau] [ 4562.099730] nvkm_ioctl+0xa1/0x250 [nouveau] [ 4562.099861] nvif_object_map_handle+0xc8/0x180 [nouveau] [ 4562.099986] nouveau_ttm_io_mem_reserve+0x122/0x270 [nouveau] [ 4562.100156] ? dma_resv_test_signaled+0x26/0xb0 [ 4562.100163] ttm_bo_vm_fault_reserved+0x97/0x3c0 [ttm] [ 4562.100182] ? __mutex_unlock_slowpath+0x2a/0x270 [ 4562.100189] nouveau_ttm_fault+0x69/0xb0 [nouveau] [ 4562.100356] __do_fault+0x32/0x150 [ 4562.100362] do_fault+0x7c/0x560 [ 4562.100369] __handle_mm_fault+0x800/0xc10 [ 4562.100382] handle_mm_fault+0x17c/0x3e0 [ 4562.100388] do_user_addr_fault+0x208/0x860 [ 4562.100395] exc_page_fault+0x7f/0x200 [ 4562.100402] asm_exc_page_fault+0x26/0x30 [ 4562.100412] RIP: 0033:0x9b9870 [ 4562.100419] Code: 85 a8 f7 ff ff 8b 8d 80 f7 ff ff 89 08 e9 18 f2 ff ff 0f 1f 84 00 00 00 00 00 44 89 32 e9 90 fa ff ff 0f 1f 84 00 00 00 00 00 <44> 89 32 e9 f8 f1 ff ff 0f 1f 84 00 00 00 00 00 66 44 89 32 e9 e7 [ 4562.100422] RSP: 002b:00007fff9ba2dc70 EFLAGS: 00010246 [ 4562.100426] RAX: 0000000000000004 RBX: 000000000dd65e10 RCX: 000000fff0000000 [ 4562.100428] RDX: 00007f629a882000 RSI: 00007f629a882000 RDI: 0000000000000066 [ 4562.100432] RBP: 00007fff9ba2e570 R08: 0000000000000000 R09: 0000000123ddf000 [ 4562.100434] R10: 0000000000000001 R11: 0000000000000246 R12: 000000007fffffff [ 4562.100436] R13: 0000000000000000 R14: 0000000000000000 R15: 0000000000000000 [ 4562.100446] </TASK> [ 4562.100448] Modules linked in: nf_conntrack_netbios_ns nf_conntrack_broadcast nft_fib_inet nft_fib_ipv4 nft_fib_ipv6 nft_fib nft_reject_inet nf_reject_ipv4 nf_reject_ipv6 nft_reject nft_ct nft_chain_nat nf_nat nf_conntrack nf_defrag_ipv6 nf_defrag_ipv4 ip_set nf_tables libcrc32c nfnetlink cmac bnep sunrpc iwlmvm intel_rapl_msr intel_rapl_common snd_sof_pci_intel_cnl x86_pkg_temp_thermal intel_powerclamp snd_sof_intel_hda_common mac80211 coretemp snd_soc_acpi_intel_match kvm_intel snd_soc_acpi snd_soc_hdac_hda snd_sof_pci snd_sof_xtensa_dsp snd_sof_intel_hda_mlink snd_sof_intel_hda snd_sof kvm snd_sof_utils snd_soc_core snd_hda_codec_realtek libarc4 snd_hda_codec_generic snd_compress snd_hda_ext_core vfat fat snd_hda_intel snd_intel_dspcfg irqbypass iwlwifi snd_hda_codec snd_hwdep snd_hda_core btusb btrtl mei_hdcp iTCO_wdt rapl mei_pxp btintel snd_seq iTCO_vendor_support btbcm snd_seq_device intel_cstate bluetooth snd_pcm cfg80211 intel_wmi_thunderbolt wmi_bmof intel_uncore snd_timer mei_me snd ecdh_generic i2c_i801 [ 4562.100541] ecc mei i2c_smbus soundcore rfkill intel_pch_thermal acpi_pad zram nouveau drm_ttm_helper ttm gpu_sched i2c_algo_bit drm_gpuvm drm_exec mxm_wmi drm_display_helper drm_kms_helper drm crct10dif_pclmul crc32_pclmul nvme e1000e crc32c_intel nvme_core ghash_clmulni_intel video wmi pinctrl_cannonlake ip6_tables ip_tables fuse [ 4562.100616] ---[ end trace 0000000000000000 ]---
--- .../drm/nouveau/include/nvkm/core/client.h | 1 + drivers/gpu/drm/nouveau/nvkm/core/client.c | 1 + drivers/gpu/drm/nouveau/nvkm/core/object.c | 26 ++++++++++++++----- 3 files changed, 22 insertions(+), 6 deletions(-)
drivers/gpu/drm/nouveau/include/nvkm/core/client.h +1 -0
@@ -12,6 +12,7 @@ struct nvkm_client {struct nvkm_client_notify *notify[32 ];struct rb_root objroot; +spinlock_t obj_lock;void *data;int (*ntfy)(const void *,u32 ,const void *,u32 );
drivers/gpu/drm/nouveau/nvkm/core/client.c +1 -0
@@ -300,6 +300,7 @@ nvkm_client_new(const char *name, u64 device, const char *cfg, client->device = device; client->debug =nvkm_dbgopt (dbg,"CLIENT" ); client->objroot =RB_ROOT ; +spin_lock_init (&client->obj_lock); client->ntfy = ntfy;INIT_LIST_HEAD (&client->umem);spin_lock_init (&client->lock);
drivers/gpu/drm/nouveau/nvkm/core/object.c +20 -6
@@ -30,8 +30,10 @@ nvkm_object_search(struct nvkm_client *client, u64 handle, const struct nvkm_object_func *func) {struct nvkm_object *object; +unsigned long flags;if (handle) { +spin_lock_irqsave (&client->obj_lock, flags);struct rb_node *node = client->objroot.rb_node;while (node) { object =rb_entry (node,typeof (*object), node);
@@ -40,9 +42,12 @@ nvkm_object_search(struct nvkm_client *client, u64 handle,else if (handle > object->object) node = node->rb_right; -else +else { +spin_unlock_irqrestore (&client->obj_lock, flags); goto done; + } } +spin_unlock_irqrestore (&client->obj_lock, flags);return ERR_PTR (-ENOENT ); }else { object = &client->object;
@@ -57,30 +62,39 @@ nvkm_object_search(struct nvkm_client *client, u64 handle,void nvkm_object_remove (struct nvkm_object *object) { +unsigned long flags; + +spin_lock_irqsave (&object->client->obj_lock, flags);if (!RB_EMPTY_NODE (&object->node))rb_erase (&object->node, &object->client->objroot); +spin_unlock_irqrestore (&object->client->obj_lock, flags); }bool nvkm_object_insert (struct nvkm_object *object) { -struct rb_node **ptr = &object->client->objroot.rb_node; +struct rb_node **ptr;struct rb_node *parent =NULL ; +unsigned long flags; +spin_lock_irqsave (&object->client->obj_lock, flags); + ptr = &object->client->objroot.rb_node;while (*ptr) {struct nvkm_object *this =rb_entry (*ptr,typeof (*this), node); parent = *ptr; -if (object->object < this->object) +if (object->object < this->object) { ptr = &parent->rb_left; -else -if (object->object > this->object) + }else if (object->object > this->object) { ptr = &parent->rb_right; -else + }else { +spin_unlock_irqrestore (&object->client->obj_lock, flags);return false; + } }rb_link_node (&object->node, parent, ptr);rb_insert_color (&object->node, &object->client->objroot); +spin_unlock_irqrestore (&object->client->obj_lock, flags);return true; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 604/1887 dmaengine: dw-edma: eDMA: Add sync read before starting the DMA transfer in remote setup
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kory Maincent <[email protected]>
[ Upstream commit bbcc1c83f343e580c3aa1f2a8593343bf7b55bba ]
The Linked list element and pointer are not stored in the same memory as the eDMA controller register. If the doorbell register is toggled before the full write of the linked list a race condition error will occur. In remote setup we can only use a readl to the memory to assure the full write has occurred.
--- drivers/dma/dw-edma/dw-edma-v0-core.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
drivers/dma/dw-edma/dw-edma-v0-core.c +17 -0
@@ -357,6 +357,20 @@ static void dw_edma_v0_core_write_chunk(struct dw_edma_chunk *chunk)#endif /* CONFIG_64BIT */ } +static void dw_edma_v0_sync_ll_data (struct dw_edma_chunk *chunk) +{ +/* +* In case of remote eDMA engine setup, the DW PCIe RP/EP internal +* configuration registers and application memory are normally accessed +* over different buses. Ensure LL-data reaches the memory before the +* doorbell register is toggled by issuing the dummy-read from the remote +* LL memory in a hope that the MRd TLP will return only after the +* last MWr TLP is completed +*/ +if (!(chunk->chan->dw->chip->flags &DW_EDMA_CHIP_LOCAL )) +readl (chunk->ll_region.vaddr); +} +void dw_edma_v0_core_start (struct dw_edma_chunk *chunk,bool first) {struct dw_edma_chan *chan = chunk->chan;
@@ -429,6 +443,9 @@ void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)SET_CH_32 (dw, chan->dir, chan->id, llp.msb,upper_32_bits (chunk->ll_region.paddr)); } + +dw_edma_v0_sync_ll_data (chunk); +/* Doorbell */ SET_RW_32 (dw, chan->dir, doorbell,FIELD_PREP (EDMA_V0_DOORBELL_CH_MASK , chan->id));
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 605/1887 btrfs: fix deadlock with fiemap and extent locking
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Josef Bacik <[email protected]>
[ Upstream commit b0ad381fa7690244802aed119b478b4bdafc31dd ]
While working on the patchset to remove extent locking I got a lockdep splat with fiemap and pagefaulting with my new extent lock replacement lock.
This deadlock exists with our normal code, we just don't have lockdep annotations with the extent locking so we've never noticed it.
Since we're copying the fiemap extent to user space on every iteration we have the chance of pagefaulting. Because we hold the extent lock for the entire range we could mkwrite into a range in the file that we have mmap'ed. This would deadlock with the following stack trace
[<0>] lock_extent+0x28d/0x2f0 [<0>] btrfs_page_mkwrite+0x273/0x8a0 [<0>] do_page_mkwrite+0x50/0xb0 [<0>] do_fault+0xc1/0x7b0 [<0>] __handle_mm_fault+0x2fa/0x460 [<0>] handle_mm_fault+0xa4/0x330 [<0>] do_user_addr_fault+0x1f4/0x800 [<0>] exc_page_fault+0x7c/0x1e0 [<0>] asm_exc_page_fault+0x26/0x30 [<0>] rep_movs_alternative+0x33/0x70 [<0>] _copy_to_user+0x49/0x70 [<0>] fiemap_fill_next_extent+0xc8/0x120 [<0>] emit_fiemap_extent+0x4d/0xa0 [<0>] extent_fiemap+0x7f8/0xad0 [<0>] btrfs_fiemap+0x49/0x80 [<0>] __x64_sys_ioctl+0x3e1/0xb50 [<0>] do_syscall_64+0x94/0x1a0 [<0>] entry_SYSCALL_64_after_hwframe+0x6e/0x76
I wrote an fstest to reproduce this deadlock without my replacement lock and verified that the deadlock exists with our existing locking.
To fix this simply don't take the extent lock for the entire duration of the fiemap. This is safe in general because we keep track of where we are when we're searching the tree, so if an ordered extent updates in the middle of our fiemap call we'll still emit the correct extents because we know what offset we were on before.
The only place we maintain the lock is searching delalloc. Since the delalloc stuff can change during writeback we want to lock the extent range so we have a consistent view of delalloc at the time we're checking to see if we need to set the delalloc flag.
With this patch applied we no longer deadlock with my testcase.
--- fs/btrfs/extent_io.c | 62 ++++++++++++++++++++++++++++++++------------ 1 file changed, 45 insertions(+), 17 deletions(-)
fs/btrfs/extent_io.c +45 -17
@@ -3706,15 +3706,33 @@ static int fiemap_process_hole(struct btrfs_inode *inode,* it beyond i_size. */ while (cur_offset < end && cur_offset < i_size) { +struct extent_state *cached_state =NULL ;u64 delalloc_start;u64 delalloc_end;u64 prealloc_start; +u64 lockstart; +u64 lockend;u64 prealloc_len =0 ;bool delalloc; + lockstart =round_down (cur_offset, inode->root->fs_info->sectorsize); + lockend =round_up (end, inode->root->fs_info->sectorsize); + +/* +* We are only locking for the delalloc range because that's the +* only thing that can change here. With fiemap we have a lock +* on the inode, so no buffered or direct writes can happen. +* +* However mmaps and normal page writeback will cause this to +* change arbitrarily. We have to lock the extent lock here to +* make sure that nobody messes with the tree while we're doing +* btrfs_find_delalloc_in_range. +*/ +lock_extent (&inode->io_tree, lockstart, lockend, &cached_state); delalloc =btrfs_find_delalloc_in_range (inode, cur_offset, end, &delalloc_start, &delalloc_end); +unlock_extent (&inode->io_tree, lockstart, lockend, &cached_state);if (!delalloc)break ;
@@ -3884,7 +3902,6 @@ int extent_fiemap(struct btrfs_inode *inode, struct fiemap_extent_info *fieinfo, u64 start, u64 len) {const u64 ino =btrfs_ino (inode); -struct extent_state *cached_state =NULL ;struct btrfs_path *path;struct btrfs_root *root = inode->root;struct fiemap_cache cache = {0 };
@@ -3893,8 +3910,9 @@ int extent_fiemap(struct btrfs_inode *inode, struct fiemap_extent_info *fieinfo,struct ulist *tmp_ulist;u64 last_extent_end;u64 prev_extent_end; -u64 lockstart; -u64 lockend; +u64 range_start; +u64 range_end; +const u64 sectorsize = inode->root->fs_info->sectorsize;bool stopped = false;int ret;
@@ -3907,12 +3925,11 @@ int extent_fiemap(struct btrfs_inode *inode, struct fiemap_extent_info *fieinfo, goto out; } - lockstart =round_down (start, root->fs_info->sectorsize); - lockend =round_up (start + len, root->fs_info->sectorsize); - prev_extent_end = lockstart; + range_start =round_down (start, sectorsize); + range_end =round_up (start + len, sectorsize); + prev_extent_end = range_start;btrfs_inode_lock (&inode->vfs_inode,BTRFS_ILOCK_SHARED ); -lock_extent (&inode->io_tree, lockstart, lockend, &cached_state); ret =fiemap_find_last_extent_offset (inode, path, &last_extent_end);if (ret <0 )
@@ -3920,7 +3937,7 @@ int extent_fiemap(struct btrfs_inode *inode, struct fiemap_extent_info *fieinfo,btrfs_release_path (path); path->reada =READA_FORWARD ; - ret =fiemap_search_slot (inode, path, lockstart); + ret =fiemap_search_slot (inode, path, range_start);if (ret <0 ) { goto out_unlock; }else if (ret >0 ) {
@@ -3932,7 +3949,7 @@ int extent_fiemap(struct btrfs_inode *inode, struct fiemap_extent_info *fieinfo, goto check_eof_delalloc; } -while (prev_extent_end < lockend) { +while (prev_extent_end < range_end) {struct extent_buffer *leaf = path->nodes[0 ];struct btrfs_file_extent_item *ei;struct btrfs_key key;
@@ -3955,17 +3972,17 @@ int extent_fiemap(struct btrfs_inode *inode, struct fiemap_extent_info *fieinfo,* The first iteration can leave us at an extent item that ends * before our range's start. Move to the next item. */ -if (extent_end <= lockstart) +if (extent_end <= range_start) goto next_item;/* We have in implicit hole (NO_HOLES feature enabled). */ if (prev_extent_end < key.offset) { -const u64 range_end =min (key.offset, lockend) -1 ; +const u64 hole_end =min (key.offset, range_end) -1 ; ret =fiemap_process_hole (inode, fieinfo, &cache, backref_cache,0 ,0 ,0 , roots, tmp_ulist, - prev_extent_end, range_end); + prev_extent_end, hole_end);if (ret <0 ) { goto out_unlock; }else if (ret >0 ) {
@@ -3975,7 +3992,7 @@ int extent_fiemap(struct btrfs_inode *inode, struct fiemap_extent_info *fieinfo, }/* We've reached the end of the fiemap range, stop. */ -if (key.offset >= lockend) { +if (key.offset >= range_end) { stopped = true;break ; }
@@ -4070,28 +4087,40 @@ int extent_fiemap(struct btrfs_inode *inode, struct fiemap_extent_info *fieinfo,btrfs_free_path (path); path =NULL ; -if (!stopped && prev_extent_end < lockend) { +if (!stopped && prev_extent_end < range_end) { ret =fiemap_process_hole (inode, fieinfo, &cache, backref_cache,0 ,0 ,0 , roots, tmp_ulist, - prev_extent_end, lockend -1 ); + prev_extent_end, range_end -1 );if (ret <0 ) goto out_unlock; - prev_extent_end = lockend; + prev_extent_end = range_end; }if (cache.cached && cache.offset + cache.len >= last_extent_end) {const u64 i_size =i_size_read (&inode->vfs_inode);if (prev_extent_end < i_size) { +struct extent_state *cached_state =NULL ;u64 delalloc_start;u64 delalloc_end; +u64 lockstart; +u64 lockend;bool delalloc; + lockstart =round_down (prev_extent_end, sectorsize); + lockend =round_up (i_size, sectorsize); + +/* +* See the comment in fiemap_process_hole as to why +* we're doing the locking here. +*/ +lock_extent (&inode->io_tree, lockstart, lockend, &cached_state); delalloc =btrfs_find_delalloc_in_range (inode, prev_extent_end, i_size -1 , &delalloc_start, &delalloc_end); +unlock_extent (&inode->io_tree, lockstart, lockend, &cached_state);if (!delalloc) cache.flags |=FIEMAP_EXTENT_LAST ; }else {
@@ -4102,7 +4131,6 @@ int extent_fiemap(struct btrfs_inode *inode, struct fiemap_extent_info *fieinfo, ret =emit_last_fiemap_cache (fieinfo, &cache); out_unlock: -unlock_extent (&inode->io_tree, lockstart, lockend, &cached_state);btrfs_inode_unlock (&inode->vfs_inode,BTRFS_ILOCK_SHARED ); out:kfree (backref_cache);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 606/1887 drm/amd/display: Prevent crash when disable stream
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chris Park <[email protected]>
[ Upstream commit 72d72e8fddbcd6c98e1b02d32cf6f2b04e10bd1c ]
[Why]
Disabling stream encoder invokes a function that no longer exists.
[How]
Check if the function declaration is NULL in disable stream encoder.
--- drivers/gpu/drm/amd/display/dc/link/link_hwss_hpo_dp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/gpu/drm/amd/display/dc/link/link_hwss_hpo_dp.c +2 -1
@@ -137,7 +137,8 @@ static void reset_hpo_dp_stream_encoder(struct pipe_ctx *pipe_ctx) dto_params.timing = &pipe_ctx->stream->timing; stream_enc->funcs->disable (stream_enc); - dccg->funcs->set_dtbclk_dto (dccg, &dto_params); +if (dccg && dccg->funcs->set_dtbclk_dto) + dccg->funcs->set_dtbclk_dto (dccg, &dto_params); dccg->funcs->disable_symclk32_se (dccg, stream_enc->inst); dccg->funcs->set_dpstreamclk (dccg,REFCLK , tg->inst, stream_enc->inst); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 607/1887 bpf: support deferring bpf_link dealloc to after RCU grace period
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Andrii Nakryiko <[email protected]>
[ Upstream commit 1a80dbcb2dbaf6e4c216e62e30fa7d3daa8001ce ]
BPF link for some program types is passed as a "context" which can be used by those BPF programs to look up additional information. E.g., for multi-kprobes and multi-uprobes, link is used to fetch BPF cookie values.
Because of this runtime dependency, when bpf_link refcnt drops to zero there could still be active BPF programs running accessing link data.
This patch adds generic support to defer bpf_link dealloc callback to after RCU GP, if requested. This is done by exposing two different deallocation callbacks, one synchronous and one deferred. If deferred one is provided, bpf_link_free() will schedule dealloc_deferred() callback to happen after RCU GP.
BPF is using two flavors of RCU: "classic" non-sleepable one and RCU tasks trace one. The latter is used when sleepable BPF programs are used. bpf_link_free() accommodates that by checking underlying BPF program's sleepable flag, and goes either through normal RCU GP only for non-sleepable, or through RCU tasks trace GP *and* then normal RCU GP (taking into account rcu_trace_implies_rcu_gp() optimization), if BPF program is sleepable.
We use this for multi-kprobe and multi-uprobe links, which dereference link during program run. We also preventively switch raw_tp link to use deferred dealloc callback, as upcoming changes in bpf-next tree expose raw_tp link data (specifically, cookie value) to BPF program at runtime as well.
--- include/linux/bpf.h | 16 +++++++++++++++- kernel/bpf/syscall.c | 35 ++++++++++++++++++++++++++++++++--- kernel/trace/bpf_trace.c | 2 +- 3 files changed, 48 insertions(+), 5 deletions(-)
include/linux/bpf.h +15 -1
@@ -1305,12 +1305,26 @@ struct bpf_link {enum bpf_link_type type;const struct bpf_link_ops *ops;struct bpf_prog *prog; -struct work_struct work; +/* rcu is used before freeing, work can be used to schedule that +* RCU-based freeing before that, so they never overlap +*/ +union { +struct rcu_head rcu; +struct work_struct work; + }; };struct bpf_link_ops {void (*release)(struct bpf_link *link); +/* deallocate link resources callback, called without RCU grace period +* waiting +*/ void (*dealloc)(struct bpf_link *link); +/* deallocate link resources callback, called after RCU grace period; +* if underlying BPF program is sleepable we go through tasks trace +* RCU GP and then "classic" RCU GP +*/ +void (*dealloc_deferred)(struct bpf_link *link);int (*detach)(struct bpf_link *link);int (*update_prog)(struct bpf_link *link,struct bpf_prog *new_prog,struct bpf_prog *old_prog);
kernel/bpf/syscall.c +32 -3
@@ -2760,17 +2760,46 @@ void bpf_link_inc(struct bpf_link *link)atomic64_inc (&link->refcnt); } +static void bpf_link_defer_dealloc_rcu_gp (struct rcu_head *rcu) +{ + struct bpf_link *link = container_of(rcu, struct bpf_link, rcu); + +/* free bpf_link and its containing memory */ + link->ops->dealloc_deferred (link); +} + +static void bpf_link_defer_dealloc_mult_rcu_gp (struct rcu_head *rcu) +{ +if (rcu_trace_implies_rcu_gp ()) +bpf_link_defer_dealloc_rcu_gp (rcu); +else +call_rcu (rcu, bpf_link_defer_dealloc_rcu_gp); +} +/* bpf_link_free is guaranteed to be called from process context */ static void bpf_link_free (struct bpf_link *link) { +bool sleepable = false; +bpf_link_free_id (link->id);if (link->prog) { + sleepable = link->prog->aux->sleepable;/* detach BPF program, clean up used resources */ link->ops->release (link);bpf_prog_put (link->prog); } -/* free bpf_link and its containing memory */ - link->ops->dealloc (link); +if (link->ops->dealloc_deferred) { +/* schedule BPF link deallocation; if underlying BPF program +* is sleepable, we need to first wait for RCU tasks trace +* sync, then go through "classic" RCU grace period +*/ +if (sleepable) +call_rcu_tasks_trace (&link->rcu, bpf_link_defer_dealloc_mult_rcu_gp); +else +call_rcu (&link->rcu, bpf_link_defer_dealloc_rcu_gp); + } +if (link->ops->dealloc) + link->ops->dealloc (link); }static void bpf_link_put_deferred (struct work_struct *work)
@@ -3256,7 +3285,7 @@ static int bpf_raw_tp_link_fill_link_info(const struct bpf_link *link,static const struct bpf_link_ops bpf_raw_tp_link_lops = { .release = bpf_raw_tp_link_release, - .dealloc = bpf_raw_tp_link_dealloc, + .dealloc_deferred = bpf_raw_tp_link_dealloc, .show_fdinfo = bpf_raw_tp_link_show_fdinfo, .fill_link_info = bpf_raw_tp_link_fill_link_info, };
kernel/trace/bpf_trace.c +1 -1
@@ -2565,7 +2565,7 @@ static void bpf_kprobe_multi_link_dealloc(struct bpf_link *link)static const struct bpf_link_ops bpf_kprobe_multi_link_lops = { .release = bpf_kprobe_multi_link_release, - .dealloc = bpf_kprobe_multi_link_dealloc, + .dealloc_deferred = bpf_kprobe_multi_link_dealloc, };static void bpf_kprobe_multi_cookie_swap (void *a,void *b,int size,const void *priv)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 608/1887 selinux: avoid dereference of garbage after mount failure
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Göttsche <[email protected]>
[ Upstream commit 37801a36b4d68892ce807264f784d818f8d0d39b ]
In case kern_mount() fails and returns an error pointer return in the error branch instead of continuing and dereferencing the error pointer.
While on it drop the never read static variable selinuxfs_mount.
--- security/selinux/selinuxfs.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-)
security/selinux/selinuxfs.c +8 -6
@@ -2211,7 +2211,6 @@ static struct file_system_type sel_fs_type = { .kill_sb = sel_kill_sb, }; -static struct vfsmount *selinuxfs_mount__ro_after_init ;struct path selinux_null__ro_after_init ;static int __init init_sel_fs (void )
@@ -2233,18 +2232,21 @@ static int __init init_sel_fs(void)return err; } - selinux_null.mnt = selinuxfs_mount =kern_mount (&sel_fs_type); -if (IS_ERR (selinuxfs_mount)) { + selinux_null.mnt =kern_mount (&sel_fs_type); +if (IS_ERR (selinux_null.mnt)) {pr_err ("selinuxfs: could not mount!\n" ); - err =PTR_ERR (selinuxfs_mount); - selinuxfs_mount =NULL ; + err =PTR_ERR (selinux_null.mnt); + selinux_null.mnt =NULL ; +return err; } + selinux_null.dentry =d_hash_and_lookup (selinux_null.mnt->mnt_root, &null_name);if (IS_ERR (selinux_null.dentry)) {pr_err ("selinuxfs: could not lookup null!\n" ); err =PTR_ERR (selinux_null.dentry); selinux_null.dentry =NULL ; +return err; }return err;
@@ -2257,7 +2259,7 @@ void exit_sel_fs(void) {sysfs_remove_mount_point (fs_kobj,"selinux" );dput (selinux_null.dentry); -kern_unmount (selinuxfs_mount); +kern_unmount (selinux_null.mnt);unregister_filesystem (&sel_fs_type); }#endif
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 609/1887 usb: typec: ucsi: Limit read size on v1.2
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Abhishek Pandit-Subedi <[email protected]>
[ Upstream commit b3db266fb031fba88c423d4bb8983a73a3db6527 ]
Between UCSI 1.2 and UCSI 2.0, the size of the MESSAGE_IN region was increased from 16 to 256. In order to avoid overflowing reads for older systems, add a mechanism to use the read UCSI version to truncate read sizes on UCSI v1.2.
--- drivers/usb/typec/ucsi/ucsi.c | 26 ++++++++++++++++++++++++-- drivers/usb/typec/ucsi/ucsi.h | 11 +++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-)
drivers/usb/typec/ucsi/ucsi.c +24 -2
@@ -36,6 +36,19 @@*/ #define UCSI_SWAP_TIMEOUT_MS 5000 +static int ucsi_read_message_in (struct ucsi *ucsi,void *buf, +size_t buf_size) +{ +/* +* Below UCSI 2.0, MESSAGE_IN was limited to 16 bytes. Truncate the +* reads here. +*/ +if (ucsi->version <=UCSI_VERSION_1_2 ) + buf_size =clamp (buf_size,0 ,16 ); + +return ucsi->ops->read (ucsi,UCSI_MESSAGE_IN , buf, buf_size); +} +static int ucsi_acknowledge (struct ucsi *ucsi,bool conn_ack) {u64 ctrl;
@@ -66,7 +79,7 @@ static int ucsi_read_error(struct ucsi *ucsi)if (ret <0 )return ret; - ret = ucsi->ops->read (ucsi,UCSI_MESSAGE_IN , &error,sizeof (error)); + ret =ucsi_read_message_in (ucsi, &error,sizeof (error));if (ret)return ret;
@@ -173,7 +186,7 @@ static int ucsi_send_command_common(struct ucsi *ucsi, u64 command, length = ret;if (data) { - ret = ucsi->ops->read (ucsi,UCSI_MESSAGE_IN , data, size); + ret =ucsi_read_message_in (ucsi, data, size);if (ret) goto out; }
@@ -1589,6 +1602,15 @@ int ucsi_register(struct ucsi *ucsi)if (!ucsi->version)return -ENODEV ; +/* +* Version format is JJ.M.N (JJ = Major version, M = Minor version, +* N = sub-minor version). +*/ +dev_dbg (ucsi->dev,"Registered UCSI interface with version %x.%x.%x" , +UCSI_BCD_GET_MAJOR (ucsi->version), +UCSI_BCD_GET_MINOR (ucsi->version), +UCSI_BCD_GET_SUBMINOR (ucsi->version)); +queue_delayed_work (system_long_wq, &ucsi->work,0 );return 0 ;
drivers/usb/typec/ucsi/ucsi.h +11 -0
@@ -23,6 +23,17 @@ struct ucsi_connector;#define UCSI_CONTROL 8#define UCSI_MESSAGE_IN 16#define UCSI_MESSAGE_OUT 32 +#define UCSIv2_MESSAGE_OUT 272 + +/* UCSI versions */ +#define UCSI_VERSION_1_2 0x0120 +#define UCSI_VERSION_2_0 0x0200 +#define UCSI_VERSION_2_1 0x0210 +#define UCSI_VERSION_3_0 0x0300 + +#define UCSI_BCD_GET_MAJOR (_v_) (((_v_) >> 8) & 0xFF) +#define UCSI_BCD_GET_MINOR (_v_) (((_v_) >> 4) & 0x0F) +#define UCSI_BCD_GET_SUBMINOR (_v_) ((_v_) & 0x0F)/* Command Status and Connector Change Indication (CCI) bits */ #define UCSI_CCI_CONNECTOR (_c_) (((_c_) & GENMASK(7, 1)) >> 1)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 610/1887 pmdomain: imx8mp-blk-ctrl: imx8mp_blk: Add fdcc clock to hdmimix domain
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Adam Ford <[email protected]>
[ Upstream commit 697624ee8ad557ab5417f985d2c804241a7ad30d ]
According to i.MX8MP RM and HDMI ADD, the fdcc clock is part of hdmi rx verification IP that should not enable for HDMI TX. But actually if the clock is disabled before HDMI/LCDIF probe, LCDIF will not get pixel clock from HDMI PHY and print the error logs:
[CRTC:39:crtc-2] vblank wait timed out
WARNING: CPU: 2 PID: 9 at drivers/gpu/drm/drm_atomic_helper.c:1634 drm_atomic_helper_wait_for_vblanks.part.0+0x23c/0x260
Add fdcc clock to LCDIF and HDMI TX power domains to fix the issue.
--- drivers/soc/imx/imx8mp-blk-ctrl.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
drivers/soc/imx/imx8mp-blk-ctrl.c +5 -5
@@ -45,7 +45,7 @@ struct imx8mp_blk_ctrl_domain_data {const unsigned int flags; }; -#define DOMAIN_MAX_CLKS 2 +#define DOMAIN_MAX_CLKS 3#define DOMAIN_MAX_PATHS 3struct imx8mp_blk_ctrl_domain {
@@ -359,8 +359,8 @@ static const struct imx8mp_blk_ctrl_domain_data imx8mp_hdmi_domain_data[] = { }, [IMX8MP_HDMIBLK_PD_LCDIF] = { .name = "hdmiblk-lcdif", - .clk_names = (const char *[]){ "axi", "apb" }, - .num_clks = 2, + .clk_names = (const char *[]){ "axi", "apb", "fdcc" }, + .num_clks = 3, .gpc_name = "lcdif", .path_names = (const char *[]){"lcdif-hdmi"}, .num_paths = 1,
@@ -385,8 +385,8 @@ static const struct imx8mp_blk_ctrl_domain_data imx8mp_hdmi_domain_data[] = { }, [IMX8MP_HDMIBLK_PD_HDMI_TX] = { .name = "hdmiblk-hdmi-tx", - .clk_names = (const char *[]){ "apb", "ref_266m" }, - .num_clks = 2, + .clk_names = (const char *[]){ "apb", "ref_266m", "fdcc" }, + .num_clks = 3, .gpc_name = "hdmi-tx", }, [IMX8MP_HDMIBLK_PD_HDMI_TX_PHY] = {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 611/1887 net: phy: phy_device: Prevent nullptr exceptions on ISR
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Andre Werner <[email protected]>
[ Upstream commit 61c81872815f46006982bb80460c0c80a949b35b ]
If phydev->irq is set unconditionally, check
for valid interrupt handler or fall back to polling mode to prevent nullptr exceptions in interrupt service routine.
--- drivers/net/phy/phy_device.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-)
drivers/net/phy/phy_device.c +8 -5
@@ -1404,6 +1404,11 @@ int phy_sfp_probe(struct phy_device *phydev, }EXPORT_SYMBOL (phy_sfp_probe); +static bool phy_drv_supports_irq (struct phy_driver *phydrv) +{ +return phydrv->config_intr && phydrv->handle_interrupt; +} +/** * phy_attach_direct - attach a network device to a given PHY device pointer * @dev: network device to attach
@@ -1513,6 +1518,9 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, phydev->interrupts =PHY_INTERRUPT_DISABLED ; +if (!phy_drv_supports_irq (phydev->drv) &&phy_interrupt_is_valid (phydev)) + phydev->irq =PHY_POLL ; +/* Port is set to PORT_TP by default and the actual PHY driver will set * it to different value depending on the PHY configuration. If we have * the generic PHY driver we can't figure it out, thus set the old
@@ -2951,11 +2959,6 @@ s32 phy_get_internal_delay(struct phy_device *phydev, struct device *dev, }EXPORT_SYMBOL (phy_get_internal_delay); -static bool phy_drv_supports_irq (struct phy_driver *phydrv) -{ -return phydrv->config_intr && phydrv->handle_interrupt; -} -/** * fwnode_mdio_find_device - Given a fwnode, find the mdio_device * @fwnode: pointer to the mdio_device's fwnode
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 612/1887 wifi: rtw89: fix null pointer access when abort scan
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Po-Hao Huang <[email protected]>
[ Upstream commit 7e11a2966f51695c0af0b1f976a32d64dee243b2 ]
During cancel scan we might use vif that weren't scanning. Fix this by using the actual scanning vif.
--- drivers/net/wireless/realtek/rtw89/mac80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/wireless/realtek/rtw89/mac80211.c +1 -1
@@ -383,7 +383,7 @@ static void rtw89_ops_bss_info_changed(struct ieee80211_hw *hw,* when disconnected by peer */ if (rtwdev->scanning) -rtw89_hw_scan_abort (rtwdev, vif); +rtw89_hw_scan_abort (rtwdev, rtwdev->scan_info.scanning_vif); } }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 613/1887 drm/panfrost: Fix the error path in panfrost_mmu_map_fault_addr()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Boris Brezillon <[email protected]>
[ Upstream commit 1fc9af813b25e146d3607669247d0f970f5a87c3 ]
Subject: [PATCH 6.1 0613/1887] [PATCH] drm/panfrost: Fix the error path in
panfrost_mmu_map_fault_addr()
If some the pages or sgt allocation failed, we shouldn't release the pages ref we got earlier, otherwise we will end up with unbalanced get/put_pages() calls. We should instead leave everything in place and let the BO release function deal with extra cleanup when the object is destroyed, or let the fault handler try again next time it's called.
--- drivers/gpu/drm/panfrost/panfrost_mmu.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-)
drivers/gpu/drm/panfrost/panfrost_mmu.c +9 -4
@@ -500,12 +500,19 @@ static int panfrost_mmu_map_fault_addr(struct panfrost_device *pfdev, int as,mapping_set_unevictable (mapping);for (i = page_offset; i < page_offset +NUM_FAULT_PAGES ; i++) { +/* Can happen if the last fault only partially filled this +* section of the pages array before failing. In that case +* we skip already filled pages. +*/ +if (pages[i]) +continue ; + pages[i] =shmem_read_mapping_page (mapping, i);if (IS_ERR (pages[i])) {mutex_unlock (&bo->base.pages_lock); ret =PTR_ERR (pages[i]); pages[i] =NULL ; - goto err_pages; + goto err_bo; } }
@@ -515,7 +522,7 @@ static int panfrost_mmu_map_fault_addr(struct panfrost_device *pfdev, int as, ret =sg_alloc_table_from_pages (sgt, pages + page_offset,NUM_FAULT_PAGES ,0 ,SZ_2M ,GFP_KERNEL );if (ret) - goto err_pages; + goto err_bo; ret =dma_map_sgtable (pfdev->dev, sgt,DMA_BIDIRECTIONAL ,0 );if (ret)
@@ -535,8 +542,6 @@ static int panfrost_mmu_map_fault_addr(struct panfrost_device *pfdev, int as, err_map:sg_free_table (sgt); -err_pages: -drm_gem_shmem_put_pages (&bo->base); err_bo:panfrost_gem_mapping_put (bomapping);return ret;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 614/1887 mm/userfaultfd: reset ptes when close() for wr-protected ones
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Peter Xu <[email protected]>
[ Upstream commit c88033efe9a391e72ba6b5df4b01d6e628f4e734 ]
Userfaultfd unregister includes a step to remove wr-protect bits from all the relevant pgtable entries, but that only covered an explicit UFFDIO_UNREGISTER ioctl, not a close() on the userfaultfd itself. Cover that too. This fixes a WARN trace.
The only user visible side effect is the user can observe leftover wr-protect bits even if the user close()ed on an userfaultfd when releasing the last reference of it. However hopefully that should be harmless, and nothing bad should happen even if so.
This change is now more important after the recent page-table-check patch we merged in mm-unstable (446dd9ad37d0 ("mm/page_table_check:
support userfault wr-protect entries")), as we'll do sanity check on uffd-wp bits without vma context. So it's better if we can 100% guarantee no uffd-wp bit leftovers, to make sure each report will be valid.
--- fs/userfaultfd.c | 4 ++++ 1 file changed, 4 insertions(+)
fs/userfaultfd.c +4 -0
@@ -908,6 +908,10 @@ static int userfaultfd_release(struct inode *inode, struct file *file) prev = vma;continue ; } +/* Reset ptes for the whole vma range if wr-protected */ +if (userfaultfd_wp (vma)) +uffd_wp_range (mm, vma, vma->vm_start, + vma->vm_end - vma->vm_start, false); new_flags = vma->vm_flags & ~__VM_UFFD_FLAGS; prev = vma_merge(mm, prev, vma->vm_start, vma->vm_end, new_flags, vma->anon_vma,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 615/1887 hv_netvsc: Dont free decrypted memory
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Rick Edgecombe <[email protected]>
[ Upstream commit bbf9ac34677b57506a13682b31a2a718934c0e31 ]
In CoCo VMs it is possible for the untrusted host to cause set_memory_encrypted() or set_memory_decrypted() to fail such that an error is returned and the resulting memory is shared. Callers need to take care to handle these errors to avoid returning decrypted (shared) memory to the page allocator, which could lead to functional or security issues.
The netvsc driver could free decrypted/shared pages if set_memory_decrypted() fails. Check the decrypted field in the gpadl to decide whether to free the memory.
Message-ID: <[email protected]>
--- drivers/net/hyperv/netvsc.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-)
drivers/net/hyperv/netvsc.c +12 -8
@@ -156,15 +156,19 @@ static void free_netvsc_device(struct rcu_head *head)kfree (nvdev->extension); -if (nvdev->recv_original_buf) -vfree (nvdev->recv_original_buf); -else -vfree (nvdev->recv_buf); +if (!nvdev->recv_buf_gpadl_handle.decrypted) { +if (nvdev->recv_original_buf) +vfree (nvdev->recv_original_buf); +else +vfree (nvdev->recv_buf); + } -if (nvdev->send_original_buf) -vfree (nvdev->send_original_buf); -else -vfree (nvdev->send_buf); +if (!nvdev->send_buf_gpadl_handle.decrypted) { +if (nvdev->send_original_buf) +vfree (nvdev->send_original_buf); +else +vfree (nvdev->send_buf); + }bitmap_free (nvdev->send_section_map);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 616/1887 Bluetooth: L2CAP: Fix div-by-zero in l2cap_le_flowctl_init()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sungwoo Kim <[email protected]>
[ Upstream commit a5b862c6a221459d54e494e88965b48dcfa6cc44 ]
l2cap_le_flowctl_init() can cause both div-by-zero and an integer overflow since hdev->le_mtu may not fall in the valid range.
Move MTU from hci_dev to hci_conn to validate MTU and stop the connection process earlier if MTU is invalid.
Also, add a missing validation in read_buffer_size() and make it return an error value if the validation fails.
Now hci_conn_add() returns ERR_PTR() as it can fail due to the both a kzalloc failure and invalid MTU value.
divide error: 0000 [#1] PREEMPT SMP KASAN NOPTI
CPU: 0 PID: 67 Comm: kworker/u5:0 Tainted: G W 6.9.0-rc5+ #20 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.15.0-1 04/01/2014 Workqueue: hci0 hci_rx_work RIP: 0010:l2cap_le_flowctl_init+0x19e/0x3f0 net/bluetooth/l2cap_core.c:547 Code: e8 17 17 0c 00 66 41 89 9f 84 00 00 00 bf 01 00 00 00 41 b8 02 00 00 00 4c 89 fe 4c 89 e2 89 d9 e8 27 17 0c 00 44 89 f0 31 d2 <66> f7 f3 89 c3 ff c3 4d 8d b7 88 00 00 00 4c 89 f0 48 c1 e8 03 42 RSP: 0018:ffff88810bc0f858 EFLAGS: 00010246 RAX: 00000000000002a0 RBX: 0000000000000000 RCX: dffffc0000000000 RDX: 0000000000000000 RSI: ffff88810bc0f7c0 RDI: ffffc90002dcb66f RBP: ffff88810bc0f880 R08: aa69db2dda70ff01 R09: 0000ffaaaaaaaaaa R10: 0084000000ffaaaa R11: 0000000000000000 R12: ffff88810d65a084 R13: dffffc0000000000 R14: 00000000000002a0 R15: ffff88810d65a000 FS: 0000000000000000(0000) GS:ffff88811ac00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000020000100 CR3: 0000000103268003 CR4: 0000000000770ef0 PKRU: 55555554 Call Trace: <TASK> l2cap_le_connect_req net/bluetooth/l2cap_core.c:4902 [inline] l2cap_le_sig_cmd net/bluetooth/l2cap_core.c:5420 [inline] l2cap_le_sig_channel net/bluetooth/l2cap_core.c:5486 [inline] l2cap_recv_frame+0xe59d/0x11710 net/bluetooth/l2cap_core.c:6809 l2cap_recv_acldata+0x544/0x10a0 net/bluetooth/l2cap_core.c:7506 hci_acldata_packet net/bluetooth/hci_core.c:3939 [inline] hci_rx_work+0x5e5/0xb20 net/bluetooth/hci_core.c:4176 process_one_work kernel/workqueue.c:3254 [inline] process_scheduled_works+0x90f/0x1530 kernel/workqueue.c:3335 worker_thread+0x926/0xe70 kernel/workqueue.c:3416 kthread+0x2e3/0x380 kernel/kthread.c:388 ret_from_fork+0x5c/0x90 arch/x86/kernel/process.c:147 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:244 </TASK> Modules linked in: ---[ end trace 0000000000000000 ]---
--- include/net/bluetooth/hci.h | 9 ++++ include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_conn.c | 71 ++++++++++++++++++++++---------- net/bluetooth/hci_event.c | 29 ++++++++----- net/bluetooth/iso.c | 2 +- net/bluetooth/l2cap_core.c | 17 ++------ net/bluetooth/sco.c | 6 +-- 7 files changed, 85 insertions(+), 50 deletions(-)
include/net/bluetooth/hci.h +9 -0
@@ -1532,6 +1532,15 @@ struct hci_cp_le_set_event_mask {__u8 mask[8 ]; } __packed; +/* BLUETOOTH CORE SPECIFICATION Version 5.4 | Vol 4, Part E +* 7.8.2 LE Read Buffer Size command +* MAX_LE_MTU is 0xffff. +* 0 is also valid. It means that no dedicated LE Buffer exists. +* It should use the HCI_Read_Buffer_Size command and mtu is shared +* between BR/EDR and LE. +*/ +#define HCI_MIN_LE_MTU 0x001b +#define HCI_OP_LE_READ_BUFFER_SIZE 0x2002struct hci_rp_le_read_buffer_size {__u8 status;
include/net/bluetooth/hci_core.h +1 -0
@@ -675,6 +675,7 @@ struct hci_conn {__u16 handle;__u16 sync_handle;__u16 state; +__u16 mtu;__u8 mode;__u8 type;__u8 role;
net/bluetooth/hci_conn.c +49 -22
155 lines not on this page · raw message
net/bluetooth/hci_event.c +19 -10
@@ -939,6 +939,9 @@ static u8 hci_cc_read_buffer_size(struct hci_dev *hdev, void *data,BT_DBG ("%s acl mtu %d:%d sco mtu %d:%d" , hdev->name, hdev->acl_mtu, hdev->acl_pkts, hdev->sco_mtu, hdev->sco_pkts); +if (!hdev->acl_mtu || !hdev->acl_pkts) +return HCI_ERROR_INVALID_PARAMETERS ; +return rp->status; }
@@ -1202,6 +1205,9 @@ static u8 hci_cc_le_read_buffer_size(struct hci_dev *hdev, void *data,BT_DBG ("%s le mtu %d:%d" , hdev->name, hdev->le_mtu, hdev->le_pkts); +if (hdev->le_mtu && hdev->le_mtu <HCI_MIN_LE_MTU ) +return HCI_ERROR_INVALID_PARAMETERS ; +return rp->status; }
@@ -2263,8 +2269,8 @@ static void hci_cs_create_conn(struct hci_dev *hdev, __u8 status)if (!conn) { conn =hci_conn_add_unset (hdev,ACL_LINK , &cp->bdaddr,HCI_ROLE_MASTER ); -if (!conn) -bt_dev_err (hdev,"no memory for new connection" ); +if (IS_ERR (conn)) +bt_dev_err (hdev,"connection err: %ld" ,PTR_ERR (conn)); } }
@@ -3106,8 +3112,8 @@ static void hci_conn_complete_evt(struct hci_dev *hdev, void *data, BDADDR_BREDR)) { conn =hci_conn_add_unset (hdev, ev->link_type, &ev->bdaddr,HCI_ROLE_SLAVE ); -if (!conn) { -bt_dev_err (hdev,"no memory for new conn" ); +if (IS_ERR (conn)) { +bt_dev_err (hdev,"connection err: %ld" ,PTR_ERR (conn)); goto unlock; } } else {
@@ -3305,8 +3311,8 @@ static void hci_conn_request_evt(struct hci_dev *hdev, void *data,if (!conn) { conn =hci_conn_add_unset (hdev, ev->link_type, &ev->bdaddr,HCI_ROLE_SLAVE ); -if (!conn) { -bt_dev_err (hdev,"no memory for new connection" ); +if (IS_ERR (conn)) { +bt_dev_err (hdev,"connection err: %ld" ,PTR_ERR (conn)); goto unlock; } }
@@ -3785,6 +3791,9 @@ static u8 hci_cc_le_read_buffer_size_v2(struct hci_dev *hdev, void *data,BT_DBG ("%s acl mtu %d:%d iso mtu %d:%d" , hdev->name, hdev->acl_mtu, hdev->acl_pkts, hdev->iso_mtu, hdev->iso_pkts); +if (hdev->le_mtu && hdev->le_mtu <HCI_MIN_LE_MTU ) +return HCI_ERROR_INVALID_PARAMETERS ; +return rp->status; }
@@ -5640,8 +5649,8 @@ static void le_conn_complete_evt(struct hci_dev *hdev, u8 status, goto unlock; conn =hci_conn_add_unset (hdev,LE_LINK , bdaddr, role); -if (!conn) { -bt_dev_err (hdev,"no memory for new connection" ); +if (IS_ERR (conn)) { +bt_dev_err (hdev,"connection err: %ld" ,PTR_ERR (conn)); goto unlock; }
@@ -6740,7 +6749,7 @@ static void hci_le_cis_req_evt(struct hci_dev *hdev, void *data,if (!cis) { cis =hci_conn_add (hdev,ISO_LINK , &acl->dst,HCI_ROLE_SLAVE , cis_handle); -if (!cis) { +if (IS_ERR (cis)) {hci_le_reject_cis (hdev, ev->cis_handle); goto unlock; }
@@ -6836,7 +6845,7 @@ static void hci_le_big_sync_established_evt(struct hci_dev *hdev, void *data, } bis =hci_conn_add (hdev,ISO_LINK ,BDADDR_ANY ,HCI_ROLE_SLAVE , handle); -if (!bis) +if (IS_ERR (bis))continue ; }
net/bluetooth/iso.c +1 -1
@@ -1103,7 +1103,7 @@ static int iso_sock_sendmsg(struct socket *sock, struct msghdr *msg,return -ENOTCONN ; } - mtu =iso_pi (sk)->conn->hcon->hdev->iso_mtu; + mtu =iso_pi (sk)->conn->hcon->mtu;release_sock (sk);
net/bluetooth/l2cap_core.c +3 -14
@@ -6547,7 +6547,7 @@ static int l2cap_finish_move(struct l2cap_chan *chan)BT_DBG ("chan %p" , chan); chan->rx_state =L2CAP_RX_STATE_RECV ; - chan->conn->mtu = chan->conn->hcon->hdev->acl_mtu; + chan->conn->mtu = chan->conn->hcon->mtu;return l2cap_resegment (chan); }
@@ -6614,7 +6614,7 @@ static int l2cap_rx_state_wait_f(struct l2cap_chan *chan,*/ chan->next_tx_seq = control->reqseq; chan->unacked_frames =0 ; - chan->conn->mtu = chan->conn->hcon->hdev->acl_mtu; + chan->conn->mtu = chan->conn->hcon->mtu; err =l2cap_resegment (chan);
@@ -7213,18 +7213,7 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon)BT_DBG ("hcon %p conn %p hchan %p" , hcon, conn, hchan); -switch (hcon->type) { -case LE_LINK : -if (hcon->hdev->le_mtu) { - conn->mtu = hcon->hdev->le_mtu; -break ; - } - fallthrough; -default : - conn->mtu = hcon->hdev->acl_mtu; -break ; - } - + conn->mtu = hcon->mtu; conn->feat_mask =0 ; conn->local_fixed_chan =L2CAP_FC_SIG_BREDR |L2CAP_FC_CONNLESS ;
net/bluetooth/sco.c +3 -3
@@ -135,7 +135,6 @@ static void sco_sock_clear_timer(struct sock *sk)/* ---- SCO connections ---- */ static struct sco_conn *sco_conn_add (struct hci_conn *hcon) { -struct hci_dev *hdev = hcon->hdev;struct sco_conn *conn = hcon->sco_data;if (conn) {
@@ -153,9 +152,10 @@ static struct sco_conn *sco_conn_add(struct hci_conn *hcon) hcon->sco_data = conn; conn->hcon = hcon; + conn->mtu = hcon->mtu; -if (hdev->sco_mtu >0 ) - conn->mtu = hdev->sco_mtu; +if (hcon->mtu >0 ) + conn->mtu = hcon->mtu;else conn->mtu =60 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 617/1887 net/mlx5: Reload only IB representors upon lag disable/enable
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Maher Sanalla <[email protected]>
[ Upstream commit 0f06228d4a2dcc1fca5b3ddb0eefa09c05b102c4 ]
On lag disable, the bond IB device along with all of its representors are destroyed, and then the slaves' representors get reloaded.
In case the slave IB representor load fails, the eswitch error flow unloads all representors, including ethernet representors, where the netdevs get detached and removed from lag bond. Such flow is inaccurate as the lag driver is not responsible for loading/unloading ethernet representors. Furthermore, the flow described above begins by holding lag lock to prevent bond changes during disable flow. However, when reaching the ethernet representors detachment from lag, the lag lock is required again, triggering the following deadlock:
Call trace: __switch_to+0xf4/0x148 __schedule+0x2c8/0x7d0 schedule+0x50/0xe0 schedule_preempt_disabled+0x18/0x28 __mutex_lock.isra.13+0x2b8/0x570 __mutex_lock_slowpath+0x1c/0x28 mutex_lock+0x4c/0x68 mlx5_lag_remove_netdev+0x3c/0x1a0 [mlx5_core] mlx5e_uplink_rep_disable+0x70/0xa0 [mlx5_core] mlx5e_detach_netdev+0x6c/0xb0 [mlx5_core] mlx5e_netdev_change_profile+0x44/0x138 [mlx5_core] mlx5e_netdev_attach_nic_profile+0x28/0x38 [mlx5_core] mlx5e_vport_rep_unload+0x184/0x1b8 [mlx5_core] mlx5_esw_offloads_rep_load+0xd8/0xe0 [mlx5_core] mlx5_eswitch_reload_reps+0x74/0xd0 [mlx5_core] mlx5_disable_lag+0x130/0x138 [mlx5_core] mlx5_lag_disable_change+0x6c/0x70 [mlx5_core] // hold ldev->lock mlx5_devlink_eswitch_mode_set+0xc0/0x410 [mlx5_core] devlink_nl_cmd_eswitch_set_doit+0xdc/0x180 genl_family_rcv_msg_doit.isra.17+0xe8/0x138 genl_rcv_msg+0xe4/0x220 netlink_rcv_skb+0x44/0x108 genl_rcv+0x40/0x58 netlink_unicast+0x198/0x268 netlink_sendmsg+0x1d4/0x418 sock_sendmsg+0x54/0x60 __sys_sendto+0xf4/0x120 __arm64_sys_sendto+0x30/0x40 el0_svc_common+0x8c/0x120 do_el0_svc+0x30/0xa0 el0_svc+0x20/0x30 el0_sync_handler+0x90/0xb8 el0_sync+0x160/0x180
Thus, upon lag enable/disable, load and unload only the IB representors of the slaves preventing the deadlock mentioned above.
While at it, refactor the mlx5_esw_offloads_rep_load() function to have a static helper method for its internal logic, in symmetry with the representor unload design.
--- .../net/ethernet/mellanox/mlx5/core/eswitch.h | 4 +-- .../mellanox/mlx5/core/eswitch_offloads.c | 28 ++++++++++++------- .../net/ethernet/mellanox/mlx5/core/lag/lag.c | 12 ++++---- 3 files changed, 26 insertions(+), 18 deletions(-)
drivers/net/ethernet/mellanox/mlx5/core/eswitch.h +2 -2
@@ -746,7 +746,7 @@ int mlx5_eswitch_offloads_config_single_fdb(struct mlx5_eswitch *master_esw, struct mlx5_eswitch *slave_esw);void mlx5_eswitch_offloads_destroy_single_fdb (struct mlx5_eswitch *master_esw,struct mlx5_eswitch *slave_esw); -int mlx5_eswitch_reload_reps (struct mlx5_eswitch *esw); +int mlx5_eswitch_reload_ib_reps (struct mlx5_eswitch *esw);static inline int mlx5_eswitch_num_vfs (struct mlx5_eswitch *esw) {
@@ -798,7 +798,7 @@ mlx5_eswitch_offloads_destroy_single_fdb(struct mlx5_eswitch *master_esw, struct mlx5_eswitch *slave_esw) {}static inline int -mlx5_eswitch_reload_reps (struct mlx5_eswitch *esw) +mlx5_eswitch_reload_ib_reps (struct mlx5_eswitch *esw) {return 0 ; }
drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +18 -10
@@ -2405,6 +2405,16 @@ int esw_offloads_init_reps(struct mlx5_eswitch *esw)return err; } +static int __esw_offloads_load_rep (struct mlx5_eswitch *esw, +struct mlx5_eswitch_rep *rep,u8 rep_type) +{ +if (atomic_cmpxchg (&rep->rep_data[rep_type].state, +REP_REGISTERED ,REP_LOADED ) ==REP_REGISTERED ) +return esw->offloads.rep_ops[rep_type]->load (esw->dev, rep); + +return 0 ; +} +static void __esw_offloads_unload_rep (struct mlx5_eswitch *esw,struct mlx5_eswitch_rep *rep,u8 rep_type) {
@@ -2453,13 +2463,11 @@ int mlx5_esw_offloads_rep_load(struct mlx5_eswitch *esw, u16 vport_num)int err; rep =mlx5_eswitch_get_rep (esw, vport_num); -for (rep_type =0 ; rep_type <NUM_REP_TYPES ; rep_type++) -if (atomic_cmpxchg (&rep->rep_data[rep_type].state, -REP_REGISTERED ,REP_LOADED ) ==REP_REGISTERED ) { - err = esw->offloads.rep_ops[rep_type]->load (esw->dev, rep); -if (err) - goto err_reps; - } +for (rep_type =0 ; rep_type <NUM_REP_TYPES ; rep_type++) { + err =__esw_offloads_load_rep (esw, rep, rep_type); +if (err) + goto err_reps; + }return 0 ;
@@ -3102,7 +3110,7 @@ static void esw_destroy_uplink_offloads_acl_tables(struct mlx5_eswitch *esw)esw_vport_destroy_offloads_acl_tables (esw, vport); } -int mlx5_eswitch_reload_reps (struct mlx5_eswitch *esw) +int mlx5_eswitch_reload_ib_reps (struct mlx5_eswitch *esw) {struct mlx5_eswitch_rep *rep;unsigned long i;
@@ -3115,13 +3123,13 @@ int mlx5_eswitch_reload_reps(struct mlx5_eswitch *esw)if (atomic_read (&rep->rep_data[REP_ETH ].state) !=REP_LOADED )return 0 ; - ret =mlx5_esw_offloads_rep_load (esw,MLX5_VPORT_UPLINK ); + ret =__esw_offloads_load_rep (esw, rep,REP_IB );if (ret)return ret;mlx5_esw_for_each_rep (esw, i, rep) {if (atomic_read (&rep->rep_data[REP_ETH ].state) ==REP_LOADED ) -mlx5_esw_offloads_rep_load (esw, rep->vport); +__esw_offloads_load_rep (esw, rep,REP_IB ); }return 0 ;
drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c +6 -6
@@ -796,9 +796,9 @@ void mlx5_disable_lag(struct mlx5_lag *ldev)if (shared_fdb) {if (!(dev0->priv.flags &MLX5_PRIV_FLAGS_DISABLE_ALL_ADEV )) -mlx5_eswitch_reload_reps (dev0->priv.eswitch); +mlx5_eswitch_reload_ib_reps (dev0->priv.eswitch);if (!(dev1->priv.flags &MLX5_PRIV_FLAGS_DISABLE_ALL_ADEV )) -mlx5_eswitch_reload_reps (dev1->priv.eswitch); +mlx5_eswitch_reload_ib_reps (dev1->priv.eswitch); } }
@@ -898,17 +898,17 @@ static void mlx5_do_bond(struct mlx5_lag *ldev) dev0->priv.flags &= ~MLX5_PRIV_FLAGS_DISABLE_IB_ADEV ;mlx5_rescan_drivers_locked (dev0); - err =mlx5_eswitch_reload_reps (dev0->priv.eswitch); + err =mlx5_eswitch_reload_ib_reps (dev0->priv.eswitch);if (!err) - err =mlx5_eswitch_reload_reps (dev1->priv.eswitch); + err =mlx5_eswitch_reload_ib_reps (dev1->priv.eswitch);if (err) { dev0->priv.flags |=MLX5_PRIV_FLAGS_DISABLE_IB_ADEV ;mlx5_rescan_drivers_locked (dev0);mlx5_deactivate_lag (ldev);mlx5_lag_add_devices (ldev); -mlx5_eswitch_reload_reps (dev0->priv.eswitch); -mlx5_eswitch_reload_reps (dev1->priv.eswitch); +mlx5_eswitch_reload_ib_reps (dev0->priv.eswitch); +mlx5_eswitch_reload_ib_reps (dev1->priv.eswitch);mlx5_core_err (dev0,"Failed to enable lag\n" );return ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 618/1887 bpf: Add BPF_PROG_TYPE_CGROUP_SKB attach type enforcement in BPF_LINK_CREATE
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Stanislav Fomichev <[email protected]>
[ Upstream commit 543576ec15b17c0c93301ac8297333c7b6e84ac7 ]
bpf_prog_attach uses attach_type_to_prog_type to enforce proper attach type for BPF_PROG_TYPE_CGROUP_SKB. link_create uses bpf_prog_get and relies on bpf_prog_attach_check_attach_type to properly verify prog_type <> attach_type association.
Add missing attach_type enforcement for the link_create case. Otherwise, it's currently possible to attach cgroup_skb prog types to other cgroup hooks.
--- kernel/bpf/syscall.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
kernel/bpf/syscall.c +10 -0
@@ -3456,9 +3456,14 @@ static int bpf_raw_tracepoint_open(const union bpf_attr *attr)return fd; } +static enum bpf_prog_type +attach_type_to_prog_type (enum bpf_attach_type attach_type); +static int bpf_prog_attach_check_attach_type (const struct bpf_prog *prog,enum bpf_attach_type attach_type) { +enum bpf_prog_type ptype; +switch (prog->type) {case BPF_PROG_TYPE_CGROUP_SOCK :case BPF_PROG_TYPE_CGROUP_SOCK_ADDR :
@@ -3471,6 +3476,11 @@ static int bpf_prog_attach_check_attach_type(const struct bpf_prog *prog,* check permissions at attach time. */ return -EPERM ; + + ptype =attach_type_to_prog_type (attach_type); +if (prog->type != ptype) +return -EINVAL ; +return prog->enforce_expected_attach_type && prog->expected_attach_type != attach_type ? -EINVAL :0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 619/1887 gfs2: Fix potential glock use-after-free on unmount
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Andreas Gruenbacher <[email protected]>
[ Upstream commit d98779e687726d8f8860f1c54b5687eec5f63a73 ]
When a DLM lockspace is released and there ares still locks in that lockspace, DLM will unlock those locks automatically. Commit fb6791d100d1b started exploiting this behavior to speed up filesystem
unmount: gfs2 would simply free glocks it didn't want to unlock and then release the lockspace. This didn't take the bast callbacks for asynchronous lock contention notifications into account, which remain active until until a lock is unlocked or its lockspace is released.
To prevent those callbacks from accessing deallocated objects, put the glocks that should not be unlocked on the sd_dead_glocks list, release the lockspace, and only then free those glocks.
As an additional measure, ignore unexpected ast and bast callbacks if the receiving glock is dead.
--- fs/gfs2/glock.c | 35 ++++++++++++++++++++++++++++++++--- fs/gfs2/glock.h | 1 + fs/gfs2/incore.h | 1 + fs/gfs2/lock_dlm.c | 12 +++++++++++- fs/gfs2/ops_fstype.c | 1 + fs/gfs2/super.c | 3 --- 6 files changed, 46 insertions(+), 7 deletions(-)
fs/gfs2/glock.c +32 -3
@@ -167,19 +167,46 @@ static bool glock_blocked_by_withdraw(struct gfs2_glock *gl)return true; } -void gfs2_glock_free (struct gfs2_glock *gl) +static void __gfs2_glock_free (struct gfs2_glock *gl) { -struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; -gfs2_glock_assert_withdraw (gl,atomic_read (&gl->gl_revokes) ==0 );rhashtable_remove_fast (&gl_hash_table, &gl->gl_node, ht_parms);smp_mb ();wake_up_glock (gl);call_rcu (&gl->gl_rcu, gfs2_glock_dealloc); +} + +void gfs2_glock_free (struct gfs2_glock *gl) { +struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; + +__gfs2_glock_free (gl);if (atomic_dec_and_test (&sdp->sd_glock_disposal))wake_up (&sdp->sd_glock_wait); } +void gfs2_glock_free_later (struct gfs2_glock *gl) { +struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; + +spin_lock (&lru_lock); +list_add (&gl->gl_lru, &sdp->sd_dead_glocks); +spin_unlock (&lru_lock); +if (atomic_dec_and_test (&sdp->sd_glock_disposal)) +wake_up (&sdp->sd_glock_wait); +} + +static void gfs2_free_dead_glocks (struct gfs2_sbd *sdp) +{ +struct list_head *list = &sdp->sd_dead_glocks; + +while (!list_empty (list)) { +struct gfs2_glock *gl; + + gl = list_first_entry(list, struct gfs2_glock, gl_lru); +list_del_init (&gl->gl_lru); +__gfs2_glock_free (gl); + } +} +/** * gfs2_glock_hold() - increment reference count on glock * @gl: The glock to hold
@@ -2263,6 +2290,8 @@ void gfs2_gl_hash_clear(struct gfs2_sbd *sdp)wait_event_timeout (sdp->sd_glock_wait,atomic_read (&sdp->sd_glock_disposal) ==0 ,HZ *600 ); +gfs2_lm_unmount (sdp); +gfs2_free_dead_glocks (sdp);glock_hash_walk (dump_glock_func, sdp); }
fs/gfs2/glock.h +1 -0
@@ -270,6 +270,7 @@ extern void gfs2_gl_dq_holders(struct gfs2_sbd *sdp);extern void gfs2_glock_thaw (struct gfs2_sbd *sdp);extern void gfs2_glock_add_to_lru (struct gfs2_glock *gl);extern void gfs2_glock_free (struct gfs2_glock *gl); +extern void gfs2_glock_free_later (struct gfs2_glock *gl);extern int __init gfs2_glock_init (void );extern void gfs2_glock_exit (void );
fs/gfs2/incore.h +1 -0
@@ -837,6 +837,7 @@ struct gfs2_sbd {struct gfs2_holder sd_freeze_gh;atomic_t sd_freeze_state;struct mutex sd_freeze_mutex; +struct list_head sd_dead_glocks;char sd_fsname[GFS2_FSNAME_LEN +3 *sizeof (int ) +2 ];char sd_table_name[GFS2_FSNAME_LEN ];
fs/gfs2/lock_dlm.c +11 -1
@@ -121,6 +121,11 @@ static void gdlm_ast(void *arg)struct gfs2_glock *gl = arg;unsigned ret = gl->gl_state; +/* If the glock is dead, we only react to a dlm_unlock() reply. */ +if (__lockref_is_dead (&gl->gl_lockref) && + gl->gl_lksb.sb_status != -DLM_EUNLOCK ) +return ; +gfs2_update_reply_times (gl);BUG_ON (gl->gl_lksb.sb_flags &DLM_SBF_DEMOTED );
@@ -171,6 +176,9 @@ static void gdlm_bast(void *arg, int mode) {struct gfs2_glock *gl = arg; +if (__lockref_is_dead (&gl->gl_lockref)) +return ; +switch (mode) {case DLM_LOCK_EX :gfs2_glock_cb (gl,LM_ST_UNLOCKED );
@@ -291,6 +299,8 @@ static void gdlm_put_lock(struct gfs2_glock *gl)struct lm_lockstruct *ls = &sdp->sd_lockstruct;int error; +BUG_ON (!__lockref_is_dead (&gl->gl_lockref)); +if (gl->gl_lksb.sb_lkid ==0 ) {gfs2_glock_free (gl);return ;
@@ -305,7 +315,7 @@ static void gdlm_put_lock(struct gfs2_glock *gl)if (test_bit (SDF_SKIP_DLM_UNLOCK , &sdp->sd_flags) && !gl->gl_lksb.sb_lvbptr) { -gfs2_glock_free (gl); +gfs2_glock_free_later (gl);return ; }
fs/gfs2/ops_fstype.c +1 -0
@@ -137,6 +137,7 @@ static struct gfs2_sbd *init_sbd(struct super_block *sb)init_waitqueue_head (&sdp->sd_log_flush_wait);atomic_set (&sdp->sd_freeze_state,SFS_UNFROZEN );mutex_init (&sdp->sd_freeze_mutex); +INIT_LIST_HEAD (&sdp->sd_dead_glocks);return sdp;
fs/gfs2/super.c +0 -3
@@ -641,10 +641,7 @@ static void gfs2_put_super(struct super_block *sb)gfs2_gl_hash_clear (sdp);truncate_inode_pages_final (&sdp->sd_aspace);gfs2_delete_debugfs_file (sdp); -/* Unmount the locking protocol */ -gfs2_lm_unmount (sdp); -/* At this point, we're through participating in the lockspace */ gfs2_sys_fs_del (sdp);free_sbd (sdp); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 620/1887 net: stmmac: move the EST lock to struct stmmac_priv
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xiaolei Wang <[email protected]>
[ Upstream commit 36ac9e7f2e5786bd37c5cd91132e1f39c29b8197 ]
Reinitialize the whole EST structure would also reset the mutex lock which is embedded in the EST structure, and then trigger the following warning. To address this, move the lock to struct stmmac_priv. We also need to reacquire the mutex lock when doing this initialization.
DEBUG_LOCKS_WARN_ON(lock->magic != lock)
WARNING: CPU: 3 PID: 505 at kernel/locking/mutex.c:587 __mutex_lock+0xd84/0x1068 Modules linked in: CPU: 3 PID: 505 Comm: tc Not tainted 6.9.0-rc6-00053-g0106679839f7-dirty #29 Hardware name: NXP i.MX8MPlus EVK board (DT) pstate: 60000005 (nZCv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--) pc : __mutex_lock+0xd84/0x1068 lr : __mutex_lock+0xd84/0x1068 sp : ffffffc0864e3570 x29: ffffffc0864e3570 x28: ffffffc0817bdc78 x27: 0000000000000003 x26: ffffff80c54f1808 x25: ffffff80c9164080 x24: ffffffc080d723ac x23: 0000000000000000 x22: 0000000000000002 x21: 0000000000000000 x20: 0000000000000000 x19: ffffffc083bc3000 x18: ffffffffffffffff x17: ffffffc08117b080 x16: 0000000000000002 x15: ffffff80d2d40000 x14: 00000000000002da x13: ffffff80d2d404b8 x12: ffffffc082b5a5c8 x11: ffffffc082bca680 x10: ffffffc082bb2640 x9 : ffffffc082bb2698 x8 : 0000000000017fe8 x7 : c0000000ffffefff x6 : 0000000000000001 x5 : ffffff8178fe0d48 x4 : 0000000000000000 x3 : 0000000000000027 x2 : ffffff8178fe0d50 x1 : 0000000000000000 x0 : 0000000000000000 Call trace: __mutex_lock+0xd84/0x1068 mutex_lock_nested+0x28/0x34 tc_setup_taprio+0x118/0x68c stmmac_setup_tc+0x50/0xf0 taprio_change+0x868/0xc9c
--- drivers/net/ethernet/stmicro/stmmac/stmmac.h | 2 ++ .../net/ethernet/stmicro/stmmac/stmmac_ptp.c | 8 ++++---- .../net/ethernet/stmicro/stmmac/stmmac_tc.c | 18 ++++++++++-------- include/linux/stmmac.h | 1 - 4 files changed, 16 insertions(+), 13 deletions(-)
drivers/net/ethernet/stmicro/stmmac/stmmac.h +2 -0
@@ -243,6 +243,8 @@ struct stmmac_priv {struct stmmac_extra_stats xstats____cacheline_aligned_in_smp ;struct stmmac_safety_stats sstats;struct plat_stmmacenet_data *plat; +/* Protect est parameters */ +struct mutex est_lock;struct dma_features dma_cap;struct stmmac_counters mmc;int hw_cap_support;
drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c +4 -4
@@ -79,11 +79,11 @@ static int stmmac_adjust_time(struct ptp_clock_info *ptp, s64 delta)/* If EST is enabled, disabled it before adjust ptp time. */ if (priv->plat->est && priv->plat->est->enable) { est_rst = true; -mutex_lock (&priv->plat->est->lock); +mutex_lock (&priv->est_lock); priv->plat->est->enable = false;stmmac_est_configure (priv, priv->ioaddr, priv->plat->est, priv->plat->clk_ptp_rate); -mutex_unlock (&priv->plat->est->lock); +mutex_unlock (&priv->est_lock); }write_lock_irqsave (&priv->ptp_lock, flags);
@@ -96,7 +96,7 @@ static int stmmac_adjust_time(struct ptp_clock_info *ptp, s64 delta)ktime_t current_time_ns, basetime;u64 cycle_time; -mutex_lock (&priv->plat->est->lock); +mutex_lock (&priv->est_lock); priv->ptp_clock_ops.gettime64 (&priv->ptp_clock_ops, ¤t_time); current_time_ns =timespec64_to_ktime (current_time); time.tv_nsec = priv->plat->est->btr_reserve[0 ];
@@ -113,7 +113,7 @@ static int stmmac_adjust_time(struct ptp_clock_info *ptp, s64 delta) priv->plat->est->enable = true; ret =stmmac_est_configure (priv, priv->ioaddr, priv->plat->est, priv->plat->clk_ptp_rate); -mutex_unlock (&priv->plat->est->lock); +mutex_unlock (&priv->est_lock);if (ret)netdev_err (priv->dev,"failed to configure EST\n" ); }
drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c +10 -8
@@ -1013,17 +1013,19 @@ static int tc_setup_taprio(struct stmmac_priv *priv,if (!plat->est)return -ENOMEM ; -mutex_init (&priv->plat->est->lock); +mutex_init (&priv->est_lock); }else { +mutex_lock (&priv->est_lock);memset (plat->est,0 ,sizeof (*plat->est)); +mutex_unlock (&priv->est_lock); } size = qopt->num_entries; -mutex_lock (&priv->plat->est->lock); +mutex_lock (&priv->est_lock); priv->plat->est->gcl_size = size; priv->plat->est->enable = qopt->enable; -mutex_unlock (&priv->plat->est->lock); +mutex_unlock (&priv->est_lock);for (i =0 ; i < size; i++) {s64 delta_ns = qopt->entries[i].interval;
@@ -1054,7 +1056,7 @@ static int tc_setup_taprio(struct stmmac_priv *priv, priv->plat->est->gcl[i] = delta_ns | (gates << wid); } -mutex_lock (&priv->plat->est->lock); +mutex_lock (&priv->est_lock);/* Adjust for real system time */ priv->ptp_clock_ops.gettime64 (&priv->ptp_clock_ops, ¤t_time); current_time_ns =timespec64_to_ktime (current_time);
@@ -1073,7 +1075,7 @@ static int tc_setup_taprio(struct stmmac_priv *priv, priv->plat->est->ctr[1 ] = (u32 )ctr;if (fpe && !priv->dma_cap.fpesel) { -mutex_unlock (&priv->plat->est->lock); +mutex_unlock (&priv->est_lock);return -EOPNOTSUPP ; }
@@ -1084,7 +1086,7 @@ static int tc_setup_taprio(struct stmmac_priv *priv, ret =stmmac_est_configure (priv, priv->ioaddr, priv->plat->est, priv->plat->clk_ptp_rate); -mutex_unlock (&priv->plat->est->lock); +mutex_unlock (&priv->est_lock);if (ret) {netdev_err (priv->dev,"failed to configure EST\n" ); goto disable;
@@ -1101,11 +1103,11 @@ static int tc_setup_taprio(struct stmmac_priv *priv, disable:if (priv->plat->est) { -mutex_lock (&priv->plat->est->lock); +mutex_lock (&priv->est_lock); priv->plat->est->enable = false;stmmac_est_configure (priv, priv->ioaddr, priv->plat->est, priv->plat->clk_ptp_rate); -mutex_unlock (&priv->plat->est->lock); +mutex_unlock (&priv->est_lock); } priv->plat->fpe_cfg->enable = false;
include/linux/stmmac.h +0 -1
@@ -115,7 +115,6 @@ struct stmmac_axi {#define EST_GCL 1024struct stmmac_est { -struct mutex lock;int enable;u32 btr_reserve[2 ];u32 btr_offset[2 ];
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 621/1887 drm/msm/dpu: Add callback function pointer check before its call
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Aleksandr Mishin <[email protected]>
[ Upstream commit 530f272053a5e72243a9cb07bb1296af6c346002 ]
In dpu_core_irq_callback_handler() callback function pointer is compared to NULL, but then callback function is unconditionally called by this pointer. Fix this bug by adding conditional return.
Found by Linux Verification Center (linuxtesting.org) with SVACE.
Patchwork: https://patchwork.freedesktop.org/patch/588237/
--- drivers/gpu/drm/msm/disp/dpu1/dpu_hw_interrupts.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/gpu/drm/msm/disp/dpu1/dpu_hw_interrupts.c +3 -1
@@ -156,8 +156,10 @@ static void dpu_core_irq_callback_handler(struct dpu_kms *dpu_kms, int irq_idx) {VERB ("irq_idx=%d\n" , irq_idx); -if (!dpu_kms->hw_intr->irq_tbl[irq_idx].cb) +if (!dpu_kms->hw_intr->irq_tbl[irq_idx].cb) {DRM_ERROR ("no registered cb, idx:%d\n" , irq_idx); +return ; + }atomic_inc (&dpu_kms->hw_intr->irq_tbl[irq_idx].count);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 622/1887 fs/ntfs3: Check folio pointer for NULL
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Konstantin Komarov <[email protected]>
[ Upstream commit 1cd6c96219c429ebcfa8e79a865277376c563803 ]
It can be NULL if bmap is called.
--- fs/ntfs3/inode.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-)
fs/ntfs3/inode.c +11 -6
@@ -580,13 +580,18 @@ static noinline int ntfs_get_block_vbo(struct inode *inode, u64 vbo, }if (is_resident (ni)) { -ni_lock (ni); - err =attr_data_read_resident (ni, page); -ni_unlock (ni); - -if (!err) -set_buffer_uptodate (bh); + bh->b_blocknr =RESIDENT_LCN ; bh->b_size = block_size; +if (!page) { + err =0 ; + }else { +ni_lock (ni); + err =attr_data_read_resident (ni, page); +ni_unlock (ni); + +if (!err) +set_buffer_uptodate (bh); + }return err; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 623/1887 platform/x86: x86-android-tablets: Unregister devices in reverse order
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hans de Goede <[email protected]>
[ Upstream commit 3de0f2627ef849735f155c1818247f58404dddfe ]
Not all subsystems support a device getting removed while there are still consumers of the device with a reference to the device.
One example of this is the regulator subsystem. If a regulator gets unregistered while there are still drivers holding a reference a WARN() at drivers/regulator/core.c:5829 triggers, e.g.:
WARNING: CPU: 1 PID: 1587 at drivers/regulator/core.c:5829 regulator_unregister Hardware name: Intel Corp. VALLEYVIEW C0 PLATFORM/BYT-T FFD8, BIOS BLADE_21.X64.0005.R00.1504101516 FFD8_X64_R_2015_04_10_1516 04/10/2015 RIP: 0010:regulator_unregister Call Trace: <TASK> regulator_unregister devres_release_group i2c_device_remove device_release_driver_internal bus_remove_device device_del device_unregister x86_android_tablet_remove
On the Lenovo Yoga Tablet 2 series the bq24190 charger chip also provides a 5V boost converter output for powering USB devices connected to the micro USB port, the bq24190-charger driver exports this as a Vbus regulator.
On the 830 (8") and 1050 ("10") models this regulator is controlled by a platform_device and x86_android_tablet_remove() removes platform_device-s before i2c_clients so the consumer gets removed first.
But on the 1380 (13") model there is a lc824206xa micro-USB switch connected over I2C and the extcon driver for that controls the regulator. The bq24190 i2c-client *must* be registered first, because that creates the regulator with the lc824206xa listed as its consumer. If the regulator has not been registered yet the lc824206xa driver will end up getting a dummy regulator.
Since in this case both the regulator provider and consumer are I2C devices, the only way to ensure that the consumer is unregistered first is to unregister the I2C devices in reverse order of in which they were created.
For consistency and to avoid similar problems in the future change x86_android_tablet_remove() to unregister all device types in reverse order.
--- drivers/platform/x86/x86-android-tablets.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/platform/x86/x86-android-tablets.c +3 -3
@@ -1748,19 +1748,19 @@ static void x86_android_tablet_cleanup(void) {int i; -for (i =0 ; i < serdev_count; i++) { +for (i = serdev_count -1 ; i >=0 ; i--) {if (serdevs[i])serdev_device_remove (serdevs[i]); }kfree (serdevs); -for (i =0 ; i < pdev_count; i++) +for (i = pdev_count -1 ; i >=0 ; i--)platform_device_unregister (pdevs[i]);kfree (pdevs); -for (i =0 ; i < i2c_client_count; i++) +for (i = i2c_client_count -1 ; i >=0 ; i--)i2c_unregister_device (i2c_clients[i]);kfree (i2c_clients);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 624/1887 ext4: refactoring to use the unified helper ext4_quotas_off()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Baokun Li <[email protected]>
[ Upstream commit f3c1c42e0c40656e73f12ab5dcb1110f83ef8e65 ]
Rename ext4_quota_off_umount() to ext4_quotas_off(), and add type parameter to replace open code in ext4_enable_quotas().
Stable-dep-of: b4b4fda34e53 ("ext4: fix uninitialized ratelimit_state->lock access in __ext4_fill_super()")
--- fs/ext4/super.c | 26 +++++++------------------- 1 file changed, 7 insertions(+), 19 deletions(-)
fs/ext4/super.c +7 -19
@@ -1167,12 +1167,12 @@ static void dump_orphan_list(struct super_block *sb, struct ext4_sb_info *sbi)#ifdef CONFIG_QUOTA static int ext4_quota_off (struct super_block *sb,int type); -static inline void ext4_quota_off_umount (struct super_block *sb) +static inline void ext4_quotas_off (struct super_block *sb,int type) { -int type; +BUG_ON (type >EXT4_MAXQUOTAS );/* Use our quota_off function to clear inode flags etc. */ -for (type =0 ; type <EXT4_MAXQUOTAS ; type++) +for (type--; type >=0 ; type--)ext4_quota_off (sb, type); }
@@ -1188,7 +1188,7 @@ static inline char *get_qf_name(struct super_block *sb, lockdep_is_held(&sb->s_umount)); }#else -static inline void ext4_quota_off_umount (struct super_block *sb) +static inline void ext4_quotas_off (struct super_block *sb,int type) { }#endif
@@ -1283,7 +1283,7 @@ static void ext4_put_super(struct super_block *sb)ext4_msg (sb,KERN_INFO ,"unmounting filesystem." );ext4_unregister_li_request (sb); -ext4_quota_off_umount (sb); +ext4_quotas_off (sb,EXT4_MAXQUOTAS );flush_work (&sbi->s_error_work);destroy_workqueue (sbi->rsv_conversion_wq);
@@ -5605,7 +5605,7 @@ static int __ext4_fill_super(struct fs_context *fc, struct super_block *sb)return 0 ; failed_mount10: -ext4_quota_off_umount (sb); +ext4_quotas_off (sb,EXT4_MAXQUOTAS ); failed_mount9:__maybe_unused ext4_release_orphan_info (sb); failed_mount8:
@@ -7091,20 +7091,8 @@ int ext4_enable_quotas(struct super_block *sb)"(type=%d, err=%d, ino=%lu). " "Please run e2fsck to fix." , type, err, qf_inums[type]); -for (type--; type >=0 ; type--) { -struct inode *inode; - - inode =sb_dqopt (sb)->files[type]; -if (inode) - inode =igrab (inode); -dquot_quota_off (sb, type); -if (inode) { -lockdep_set_quota_inode (inode, -I_DATA_SEM_NORMAL ); -iput (inode); - } - } +ext4_quotas_off (sb, type);return err; } }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 625/1887 ext4: fix uninitialized ratelimit_state->lock access in __ext4_fill_super()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Baokun Li <[email protected]>
[ Upstream commit b4b4fda34e535756f9e774fb2d09c4537b7dfd1c ]
In the following concurrency we will access the uninitialized rs->lock:
ext4_fill_super
ext4_register_sysfs
// sysfs registered msg_ratelimit_interval_ms
// Other processes modify rs->interval to
// non-zero via msg_ratelimit_interval_ms
ext4_orphan_cleanup
ext4_msg(sb, KERN_INFO, "Errors on filesystem, "
__ext4_msg
___ratelimit(&(EXT4_SB(sb)->s_msg_ratelimit_state)
if (!rs->interval) // do nothing if interval is 0
return 1;
raw_spin_trylock_irqsave(&rs->lock, flags)
raw_spin_trylock(lock)
_raw_spin_trylock
__raw_spin_trylock
spin_acquire(&lock->dep_map, 0, 1, _RET_IP_)
lock_acquire
__lock_acquire
register_lock_class
assign_lock_key
dump_stack();
ratelimit_state_init(&sbi->s_msg_ratelimit_state, 5 * HZ, 10);
raw_spin_lock_init(&rs->lock);
// init rs->lock hereand get the following dump_stack:
=========================================================
INFO: trying to register non-static key.
The code is fine but needs lockdep annotation, or maybe you didn't initialize this object before use? turning off the locking correctness validator.
CPU: 12 PID: 753 Comm: mount Tainted: G E 6.7.0-rc6-next-20231222 #504 [...] Call Trace: dump_stack_lvl+0xc5/0x170 dump_stack+0x18/0x30 register_lock_class+0x740/0x7c0 __lock_acquire+0x69/0x13a0 lock_acquire+0x120/0x450 _raw_spin_trylock+0x98/0xd0 ___ratelimit+0xf6/0x220 __ext4_msg+0x7f/0x160 [ext4] ext4_orphan_cleanup+0x665/0x740 [ext4] __ext4_fill_super+0x21ea/0x2b10 [ext4] ext4_fill_super+0x14d/0x360 [ext4]
[...]
=========================================================
Normally interval is 0 until s_msg_ratelimit_state is initialized, so ___ratelimit() does nothing. But registering sysfs precedes initializing rs->lock, so it is possible to change rs->interval to a non-zero value via the msg_ratelimit_interval_ms interface of sysfs while rs->lock is uninitialized, and then a call to ext4_msg triggers the problem by accessing an uninitialized rs->lock. Therefore register sysfs after all initializations are complete to avoid such problems.
--- fs/ext4/super.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-)
fs/ext4/super.c +10 -12
@@ -5547,19 +5547,15 @@ static int __ext4_fill_super(struct fs_context *fc, struct super_block *sb)if (err) goto failed_mount6; - err =ext4_register_sysfs (sb); -if (err) - goto failed_mount7; - err =ext4_init_orphan_info (sb);if (err) - goto failed_mount8; + goto failed_mount7;#ifdef CONFIG_QUOTA /* Enable quota usage during mount. */ if (ext4_has_feature_quota (sb) && !sb_rdonly (sb)) { err =ext4_enable_quotas (sb);if (err) - goto failed_mount9; + goto failed_mount8; }#endif /* CONFIG_QUOTA */
@@ -5585,7 +5581,7 @@ static int __ext4_fill_super(struct fs_context *fc, struct super_block *sb)ext4_msg (sb,KERN_INFO ,"recovery complete" ); err =ext4_mark_recovery_complete (sb, es);if (err) - goto failed_mount10; + goto failed_mount9; }if (test_opt (sb,DISCARD ) && !bdev_max_discard_sectors (sb->s_bdev))
@@ -5602,15 +5598,17 @@ static int __ext4_fill_super(struct fs_context *fc, struct super_block *sb)atomic_set (&sbi->s_warning_count,0 );atomic_set (&sbi->s_msg_count,0 ); +/* Register sysfs after all initializations are complete. */ + err =ext4_register_sysfs (sb); +if (err) + goto failed_mount9; +return 0 ; -failed_mount10: +failed_mount9:ext4_quotas_off (sb,EXT4_MAXQUOTAS ); -failed_mount9:__maybe_unused +failed_mount8:__maybe_unused ext4_release_orphan_info (sb); -failed_mount8: -ext4_unregister_sysfs (sb); -kobject_put (&sbi->s_kobj); failed_mount7:ext4_unregister_li_request (sb); failed_mount6:
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 626/1887 drm/amdgpu: change vm->task_info handling
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Shashank Sharma <[email protected]>
[ Upstream commit b8f67b9ddf4f8fe6dd536590712b5912ad78f99c ]
This patch changes the handling and lifecycle of vm->task_info object. The major changes are:
- vm->task_info is a dynamically allocated ptr now, and its uasge is reference counted.
- introducing two new helper funcs for task_info lifecycle management
- amdgpu_vm_get_task_info: reference counts up task_info before returning this info
- amdgpu_vm_put_task_info: reference counts down task_info
- last put to task_info() frees task_info from the vm.
This patch also does logistical changes required for existing usage of vm->task_info.
V2: Do not block all the prints when task_info not found (Felix)
V3: Fixed review comments from Felix
- Fix wrong indentation
- No debug message for -ENOMEM
- Add NULL check for task_info
- Do not duplicate the debug messages (ti vs no ti)
- Get first reference of task_info in vm_init(), put last in vm_fini()
V4: Fixed review comments from Felix
- fix double reference increment in create_task_info
- change amdgpu_vm_get_task_info_pasid
- additional changes in amdgpu_gem.c while porting
--- drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c | 9 +- drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 13 +- drivers/gpu/drm/amd/amdgpu/amdgpu_job.c | 18 ++- drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c | 149 ++++++++++++++------ drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h | 21 ++- drivers/gpu/drm/amd/amdgpu/amdgpu_vm_pt.c | 2 +- drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c | 25 ++-- drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c | 24 ++-- drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c | 20 ++- drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c | 24 ++-- drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c | 23 +-- drivers/gpu/drm/amd/amdkfd/kfd_smi_events.c | 20 +-- 12 files changed, 229 insertions(+), 119 deletions(-)
drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c +7 -2
@@ -1621,9 +1621,14 @@ static int amdgpu_debugfs_vm_info_show(struct seq_file *m, void *unused)list_for_each_entry (file, &dev->filelist, lhead) {struct amdgpu_fpriv *fpriv = file->driver_priv;struct amdgpu_vm *vm = &fpriv->vm; +struct amdgpu_task_info *ti; + + ti =amdgpu_vm_get_task_info_vm (vm); +if (ti) { +seq_printf (m,"pid:%d\tProcess:%s ----------\n" , ti->pid, ti->process_name); +amdgpu_vm_put_task_info (ti); + } -seq_printf (m,"pid:%d\tProcess:%s ----------\n" , - vm->task_info.pid, vm->task_info.process_name); r =amdgpu_bo_reserve (vm->root.bo, true);if (r)break ;
drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +10 -3
@@ -5170,9 +5170,16 @@ int amdgpu_do_asic_reset(struct list_head *device_list_handle, tmp_adev->reset_vram_lost = vram_lost;memset (&tmp_adev->reset_task_info,0 ,sizeof (tmp_adev->reset_task_info)); -if (reset_context->job && reset_context->job->vm) - tmp_adev->reset_task_info = - reset_context->job->vm->task_info; +if (reset_context->job && reset_context->job->vm) { +struct amdgpu_task_info *ti; +struct amdgpu_vm *vm = reset_context->job->vm; + + ti =amdgpu_vm_get_task_info_vm (vm); +if (ti) { + tmp_adev->reset_task_info = *ti; +amdgpu_vm_put_task_info (ti); + } + }amdgpu_reset_capture_coredumpm (tmp_adev);#endif if (vram_lost) {
drivers/gpu/drm/amd/amdgpu/amdgpu_job.c +11 -7
@@ -35,7 +35,7 @@ static enum drm_gpu_sched_stat amdgpu_job_timedout(struct drm_sched_job *s_job) {struct amdgpu_ring *ring =to_amdgpu_ring (s_job->sched);struct amdgpu_job *job =to_amdgpu_job (s_job); -struct amdgpu_task_info ti; +struct amdgpu_task_info *ti;struct amdgpu_device *adev = ring->adev;int idx;int r;
@@ -48,7 +48,7 @@ static enum drm_gpu_sched_stat amdgpu_job_timedout(struct drm_sched_job *s_job)return DRM_GPU_SCHED_STAT_ENODEV ; } -memset (&ti,0 ,sizeof (struct amdgpu_task_info )); + adev->job_hang = true; if (amdgpu_gpu_recovery &&
@@ -58,12 +58,16 @@ static enum drm_gpu_sched_stat amdgpu_job_timedout(struct drm_sched_job *s_job) goto exit; } -amdgpu_vm_get_task_info (ring->adev, job->pasid, &ti);DRM_ERROR ("ring %s timeout, signaled seq=%u, emitted seq=%u\n" , - job->base.sched->name,atomic_read (&ring->fence_drv.last_seq), - ring->fence_drv.sync_seq); -DRM_ERROR ("Process information: process %s pid %d thread %s pid %d\n" , - ti.process_name, ti.tgid, ti.task_name, ti.pid); + job->base.sched->name,atomic_read (&ring->fence_drv.last_seq), + ring->fence_drv.sync_seq); + + ti =amdgpu_vm_get_task_info_pasid (ring->adev, job->pasid); +if (ti) { +DRM_ERROR ("Process information: process %s pid %d thread %s pid %d\n" , + ti->process_name, ti->tgid, ti->task_name, ti->pid); +amdgpu_vm_put_task_info (ti); + }if (amdgpu_device_should_recover_gpu (ring->adev)) {struct amdgpu_reset_context reset_context;
drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +107 -42
173 lines not on this page · raw message
drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h +14 -7
@@ -174,10 +174,11 @@ struct amdgpu_vm_pte_funcs { };struct amdgpu_task_info { -char process_name[TASK_COMM_LEN ]; -char task_name[TASK_COMM_LEN ]; -pid_t pid; -pid_t tgid; +char process_name[TASK_COMM_LEN ]; +char task_name[TASK_COMM_LEN ]; +pid_t pid; +pid_t tgid; +struct kref refcount; };/**
@@ -323,7 +324,7 @@ struct amdgpu_vm {uint64_t pd_phys_addr;/* Some basic info about the task */ -struct amdgpu_task_info task_info; +struct amdgpu_task_info *task_info;/* Store positions of group of BOs */ struct ttm_lru_bulk_move lru_bulk_move;
@@ -452,8 +453,14 @@ bool amdgpu_vm_need_pipeline_sync(struct amdgpu_ring *ring, struct amdgpu_job *job);void amdgpu_vm_check_compute_bug (struct amdgpu_device *adev); -void amdgpu_vm_get_task_info (struct amdgpu_device *adev,u32 pasid, -struct amdgpu_task_info *task_info); +struct amdgpu_task_info * +amdgpu_vm_get_task_info_pasid (struct amdgpu_device *adev,u32 pasid); + +struct amdgpu_task_info * +amdgpu_vm_get_task_info_vm (struct amdgpu_vm *vm); + +void amdgpu_vm_put_task_info (struct amdgpu_task_info *task_info); +bool amdgpu_vm_handle_fault (struct amdgpu_device *adev,u32 pasid,uint64_t addr,bool write_fault);
drivers/gpu/drm/amd/amdgpu/amdgpu_vm_pt.c +1 -1
@@ -978,7 +978,7 @@ int amdgpu_vm_ptes_update(struct amdgpu_vm_update_params *params,trace_amdgpu_vm_update_ptes (params, frag_start, upd_end,min (nptes,32u ), dst, incr, upd_flags, - vm->task_info.tgid, + vm->task_info ? vm->task_info->tgid :0 , vm->immediate.fence_context); amdgpu_vm_pte_update_flags(params, to_amdgpu_bo_vm(pt), cursor.level, pe_start, dst,
drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c +14 -11
@@ -100,7 +100,7 @@ static int gmc_v10_0_process_interrupt(struct amdgpu_device *adev,bool retry_fault = !!(entry->src_data[1 ] &0x80 );bool write_fault = !!(entry->src_data[1 ] &0x20 );struct amdgpu_vmhub *hub = &adev->vmhub[entry->vmid_src]; -struct amdgpu_task_info task_info; +struct amdgpu_task_info *task_info;uint32_t status =0 ;u64 addr;
@@ -148,19 +148,22 @@ static int gmc_v10_0_process_interrupt(struct amdgpu_device *adev,if (!printk_ratelimit ())return 0 ; -memset (&task_info,0 ,sizeof (struct amdgpu_task_info )); -amdgpu_vm_get_task_info (adev, entry->pasid, &task_info); -dev_err (adev->dev, -"[%s] page fault (src_id:%u ring:%u vmid:%u pasid:%u, " -"for process %s pid %d thread %s pid %d)\n" , +"[%s] page fault (src_id:%u ring:%u vmid:%u pasid:%u)\n" , entry->vmid_src ?"mmhub" :"gfxhub" , - entry->src_id, entry->ring_id, entry->vmid, - entry->pasid, task_info.process_name, task_info.tgid, - task_info.task_name, task_info.pid); + entry->src_id, entry->ring_id, entry->vmid, entry->pasid); + task_info =amdgpu_vm_get_task_info_pasid (adev, entry->pasid); +if (task_info) { +dev_err (adev->dev, +" in process %s pid %d thread %s pid %d\n" , + task_info->process_name, task_info->tgid, + task_info->task_name, task_info->pid); +amdgpu_vm_put_task_info (task_info); + } +dev_err (adev->dev," in page starting at address 0x%016llx from client 0x%x (%s)\n" , - addr, entry->client_id, - soc15_ih_clientid_name[entry->client_id]); + addr, entry->client_id, + soc15_ih_clientid_name[entry->client_id]);if (!amdgpu_sriov_vf (adev)) hub->vmhub_funcs->print_l2_protection_fault_status(adev,
drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c +14 -10
@@ -106,20 +106,24 @@ static int gmc_v11_0_process_interrupt(struct amdgpu_device *adev, }if (printk_ratelimit ()) { -struct amdgpu_task_info task_info; - -memset (&task_info,0 ,sizeof (struct amdgpu_task_info )); -amdgpu_vm_get_task_info (adev, entry->pasid, &task_info); +struct amdgpu_task_info *task_info;dev_err (adev->dev, -"[%s] page fault (src_id:%u ring:%u vmid:%u pasid:%u, " -"for process %s pid %d thread %s pid %d)\n" , +"[%s] page fault (src_id:%u ring:%u vmid:%u pasid:%u)\n" , entry->vmid_src ?"mmhub" :"gfxhub" , - entry->src_id, entry->ring_id, entry->vmid, - entry->pasid, task_info.process_name, task_info.tgid, - task_info.task_name, task_info.pid); + entry->src_id, entry->ring_id, entry->vmid, entry->pasid); + task_info =amdgpu_vm_get_task_info_pasid (adev, entry->pasid); +if (task_info) { +dev_err (adev->dev, +" in process %s pid %d thread %s pid %d)\n" , + task_info->process_name, task_info->tgid, + task_info->task_name, task_info->pid); +amdgpu_vm_put_task_info (task_info); + } +dev_err (adev->dev," in page starting at address 0x%016llx from client %d\n" , - addr, entry->client_id); + addr, entry->client_id); +if (!amdgpu_sriov_vf (adev)) hub->vmhub_funcs->print_l2_protection_fault_status (adev, status); }
drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c +13 -7
@@ -1461,18 +1461,24 @@ static int gmc_v8_0_process_interrupt(struct amdgpu_device *adev,gmc_v8_0_set_fault_enable_default (adev, false);if (printk_ratelimit ()) { -struct amdgpu_task_info task_info; +struct amdgpu_task_info *task_info; -memset (&task_info,0 ,sizeof (struct amdgpu_task_info )); -amdgpu_vm_get_task_info (adev, entry->pasid, &task_info); +dev_err (adev->dev,"GPU fault detected: %d 0x%08x\n" , + entry->src_id, entry->src_data[0 ]); + + task_info =amdgpu_vm_get_task_info_pasid (adev, entry->pasid); +if (task_info) { +dev_err (adev->dev," for process %s pid %d thread %s pid %d\n" , + task_info->process_name, task_info->tgid, + task_info->task_name, task_info->pid); +amdgpu_vm_put_task_info (task_info); + } -dev_err (adev->dev,"GPU fault detected: %d 0x%08x for process %s pid %d thread %s pid %d\n" , - entry->src_id, entry->src_data[0 ], task_info.process_name, - task_info.tgid, task_info.task_name, task_info.pid);dev_err (adev->dev," VM_CONTEXT1_PROTECTION_FAULT_ADDR 0x%08X\n" , - addr); + addr);dev_err (adev->dev," VM_CONTEXT1_PROTECTION_FAULT_STATUS 0x%08X\n" , status); +gmc_v8_0_vm_decode_fault (adev, status, addr, mc_client, entry->pasid); }
drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c +14 -10
@@ -532,7 +532,7 @@ static int gmc_v9_0_process_interrupt(struct amdgpu_device *adev,bool retry_fault = !!(entry->src_data[1 ] &0x80 );bool write_fault = !!(entry->src_data[1 ] &0x20 );uint32_t status =0 , cid =0 , rw =0 ; -struct amdgpu_task_info task_info; +struct amdgpu_task_info *task_info;struct amdgpu_vmhub *hub;const char *mmhub_cid;const char *hub_name;
@@ -579,16 +579,20 @@ static int gmc_v9_0_process_interrupt(struct amdgpu_device *adev, hub = &adev->vmhub[AMDGPU_GFXHUB_0 ]; } -memset (&task_info,0 ,sizeof (struct amdgpu_task_info )); -amdgpu_vm_get_task_info (adev, entry->pasid, &task_info); -dev_err (adev->dev, -"[%s] %s page fault (src_id:%u ring:%u vmid:%u " -"pasid:%u, for process %s pid %d thread %s pid %d)\n" , - hub_name, retry_fault ?"retry" :"no-retry" , - entry->src_id, entry->ring_id, entry->vmid, - entry->pasid, task_info.process_name, task_info.tgid, - task_info.task_name, task_info.pid); +"[%s] %s page fault (src_id:%u ring:%u vmid:%u pasid:%u)\n" , hub_name, + retry_fault ?"retry" :"no-retry" , + entry->src_id, entry->ring_id, entry->vmid, entry->pasid); + + task_info =amdgpu_vm_get_task_info_pasid (adev, entry->pasid); +if (task_info) { +dev_err (adev->dev, +" for process %s pid %d thread %s pid %d)\n" , + task_info->process_name, task_info->tgid, + task_info->task_name, task_info->pid); +amdgpu_vm_put_task_info (task_info); + } +dev_err (adev->dev," in page starting at address 0x%016llx from IH client 0x%x (%s)\n" , addr, entry->client_id, soc15_ih_clientid_name[entry->client_id]);
drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c +14 -9
@@ -2133,7 +2133,7 @@ static int sdma_v4_0_print_iv_entry(struct amdgpu_device *adev, struct amdgpu_iv_entry *entry) {int instance; -struct amdgpu_task_info task_info; +struct amdgpu_task_info *task_info;u64 addr; instance =sdma_v4_0_irq_id_to_seq (entry->client_id);
@@ -2145,15 +2145,20 @@ static int sdma_v4_0_print_iv_entry(struct amdgpu_device *adev, addr = (u64 )entry->src_data[0 ] <<12 ; addr |= ((u64 )entry->src_data[1 ] &0xf ) <<44 ; -memset (&task_info,0 ,sizeof (struct amdgpu_task_info )); -amdgpu_vm_get_task_info (adev, entry->pasid, &task_info); -dev_dbg_ratelimited (adev->dev, -"[sdma%d] address:0x%016llx src_id:%u ring:%u vmid:%u " -"pasid:%u, for process %s pid %d thread %s pid %d\n" , - instance, addr, entry->src_id, entry->ring_id, entry->vmid, - entry->pasid, task_info.process_name, task_info.tgid, - task_info.task_name, task_info.pid); +"[sdma%d] address:0x%016llx src_id:%u ring:%u vmid:%u pasid:%u\n" , + instance, addr, entry->src_id, entry->ring_id, entry->vmid, + entry->pasid); + + task_info =amdgpu_vm_get_task_info_pasid (adev, entry->pasid); +if (task_info) { +dev_dbg_ratelimited (adev->dev, +" for process %s pid %d thread %s pid %d\n" , + task_info->process_name, task_info->tgid, + task_info->task_name, task_info->pid); +amdgpu_vm_put_task_info (task_info); + } +return 0 ; }
drivers/gpu/drm/amd/amdkfd/kfd_smi_events.c +10 -10
@@ -238,16 +238,16 @@ void kfd_smi_event_update_thermal_throttling(struct kfd_dev *dev,void kfd_smi_event_update_vmfault (struct kfd_dev *dev,uint16_t pasid) { -struct amdgpu_task_info task_info; - -memset (&task_info,0 ,sizeof (struct amdgpu_task_info )); -amdgpu_vm_get_task_info (dev->adev, pasid, &task_info); -/* Report VM faults from user applications, not retry from kernel */ -if (!task_info.pid) -return ; - -kfd_smi_event_add (0 , dev,KFD_SMI_EVENT_VMFAULT ,"%x:%s\n" , - task_info.pid, task_info.task_name); +struct amdgpu_task_info *task_info; + + task_info =amdgpu_vm_get_task_info_pasid (dev->adev, pasid); +if (task_info) { +/* Report VM faults from user applications, not retry from kernel */ +if (task_info->pid) +kfd_smi_event_add (0 , dev,KFD_SMI_EVENT_VMFAULT ,"%x:%s\n" , + task_info->pid, task_info->task_name); +amdgpu_vm_put_task_info (task_info); + } } void kfd_smi_event_page_fault_start(struct kfd_dev *dev, pid_t pid,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 627/1887 LoongArch: Update cpu_sibling_map when disabling nonboot CPUs
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Huacai Chen <[email protected]>
[ Upstream commit 752cd08da320a667a833803a8fd6bb266114cce5 ]
Update cpu_sibling_map when disabling nonboot CPUs by defining & calling clear_cpu_sibling_map(), otherwise we get such errors on SMT systems:
jump label: negative count!
WARNING: CPU: 6 PID: 45 at kernel/jump_label.c:263 __static_key_slow_dec_cpuslocked+0xec/0x100 CPU: 6 PID: 45 Comm: cpuhp/6 Not tainted 6.8.0-rc5+ #1340
pc 90000000004c302c ra 90000000004c302c tp 90000001005bc000 sp 90000001005bfd20 a0 000000000000001b a1 900000000224c278 a2 90000001005bfb58 a3 900000000224c280 a4 900000000224c278 a5 90000001005bfb50 a6 0000000000000001 a7 0000000000000001 t0 ce87a4763eb5234a t1 ce87a4763eb5234a t2 0000000000000000 t3 0000000000000000 t4 0000000000000006 t5 0000000000000000 t6 0000000000000064 t7 0000000000001964 t8 000000000009ebf6 u0 9000000001f2a068 s9 0000000000000000 s0 900000000246a2d8 s1 ffffffffffffffff s2 ffffffffffffffff s3 90000000021518c0 s4 0000000000000040 s5 9000000002151058 s6 9000000009828e40 s7 00000000000000b4 s8 0000000000000006
ra: 90000000004c302c __static_key_slow_dec_cpuslocked+0xec/0x100
ERA: 90000000004c302c __static_key_slow_dec_cpuslocked+0xec/0x100
CRMD: 000000b0 (PLV0 -IE -DA +PG DACF=CC DACM=CC -WE)
PRMD: 00000004 (PPLV0 +PIE -PWE)
EUEN: 00000000 (-FPE -SXE -ASXE -BTE)
ECFG: 00071c1c (LIE=2-4,10-12 VS=7)
ESTAT: 000c0000 [BRK] (IS= ECode=12 EsubCode=0)
PRID: 0014d000 (Loongson-64bit, Loongson-3A6000-HV)
CPU: 6 PID: 45 Comm: cpuhp/6 Not tainted 6.8.0-rc5+ #1340
Stack : 0000000000000000 900000000203f258 900000000179afc8 90000001005bc000
90000001005bf980 0000000000000000 90000001005bf988 9000000001fe0be0
900000000224c280 900000000224c278 90000001005bf8c0 0000000000000001
0000000000000001 ce87a4763eb5234a 0000000007f38000 90000001003f8cc0
0000000000000000 0000000000000006 0000000000000000 4c206e6f73676e6f
6f4c203a656d616e 000000000009ec99 0000000007f38000 0000000000000000
900000000214b000 9000000001fe0be0 0000000000000004 0000000000000000
0000000000000107 0000000000000009 ffffffffffafdabe 00000000000000b4
0000000000000006 90000000004c302c 9000000000224528 00005555939a0c7c
00000000000000b0 0000000000000004 0000000000000000 0000000000071c1c
...
Call Trace:
[<9000000000224528>] show_stack+0x48/0x1a0
[<900000000179afc8>] dump_stack_lvl+0x78/0xa0
[<9000000000263ed0>] __warn+0x90/0x1a0
[<90000000017419b8>] report_bug+0x1b8/0x280
[<900000000179c564>] do_bp+0x264/0x420
[<90000000004c302c>] __static_key_slow_dec_cpuslocked+0xec/0x100
[<90000000002b4d7c>] sched_cpu_deactivate+0x2fc/0x300
[<9000000000266498>] cpuhp_invoke_callback+0x178/0x8a0
[<9000000000267f70>] cpuhp_thread_fun+0xf0/0x240
[<90000000002a117c>] smpboot_thread_fn+0x1dc/0x2e0
[<900000000029a720>] kthread+0x140/0x160
[<9000000000222288>] ret_from_kernel_thread+0xc/0xa4--- arch/loongarch/kernel/smp.c | 129 ++++++++++++++++++++---------------- 1 file changed, 72 insertions(+), 57 deletions(-)
arch/loongarch/kernel/smp.c +72 -57
@@ -89,6 +89,77 @@ void show_ipi_list(struct seq_file *p, int prec) } } +static inline void set_cpu_core_map (int cpu) +{ +int i; + +cpumask_set_cpu (cpu, &cpu_core_setup_map); + +for_each_cpu (i, &cpu_core_setup_map) { +if (cpu_data[cpu].package == cpu_data[i].package) { +cpumask_set_cpu (i, &cpu_core_map[cpu]); +cpumask_set_cpu (cpu, &cpu_core_map[i]); + } + } +} + +static inline void set_cpu_sibling_map (int cpu) +{ +int i; + +cpumask_set_cpu (cpu, &cpu_sibling_setup_map); + +if (smp_num_siblings <=1 ) +cpumask_set_cpu (cpu, &cpu_sibling_map[cpu]); +else { +for_each_cpu (i, &cpu_sibling_setup_map) { +if (cpus_are_siblings (cpu, i)) { +cpumask_set_cpu (i, &cpu_sibling_map[cpu]); +cpumask_set_cpu (cpu, &cpu_sibling_map[i]); + } + } + } +} + +static inline void clear_cpu_sibling_map (int cpu) +{ +int i; + +for_each_cpu (i, &cpu_sibling_setup_map) { +if (cpus_are_siblings (cpu, i)) { +cpumask_clear_cpu (i, &cpu_sibling_map[cpu]); +cpumask_clear_cpu (cpu, &cpu_sibling_map[i]); + } + } + +cpumask_clear_cpu (cpu, &cpu_sibling_setup_map); +} + +/* +* Calculate a new cpu_foreign_map mask whenever a +* new cpu appears or disappears. +*/ +void calculate_cpu_foreign_map (void ) +{ +int i, k, core_present; +cpumask_t temp_foreign_map; + +/* Re-calculate the mask */ +cpumask_clear (&temp_foreign_map); +for_each_online_cpu (i) { + core_present =0 ; +for_each_cpu (k, &temp_foreign_map) +if (cpus_are_siblings (i, k)) + core_present =1 ; +if (!core_present) +cpumask_set_cpu (i, &temp_foreign_map); + } + +for_each_online_cpu (i) +cpumask_andnot (&cpu_foreign_map[i], + &temp_foreign_map, &cpu_sibling_map[i]); +} +/* Send mailbox buffer via Mail_Send */ static void csr_mail_send (uint64_t data,int cpu,int mailbox) {
@@ -263,6 +334,7 @@ int loongson_cpu_disable(void)numa_remove_cpu (cpu);#endif set_cpu_online (cpu, false); +clear_cpu_sibling_map (cpu);calculate_cpu_foreign_map ();local_irq_save (flags);irq_migrate_all_off_this_cpu ();
@@ -340,63 +412,6 @@ static int __init ipi_pm_init(void)core_initcall (ipi_pm_init);#endif -static inline void set_cpu_sibling_map (int cpu) -{ -int i; - -cpumask_set_cpu (cpu, &cpu_sibling_setup_map); - -if (smp_num_siblings <=1 ) -cpumask_set_cpu (cpu, &cpu_sibling_map[cpu]); -else { -for_each_cpu (i, &cpu_sibling_setup_map) { -if (cpus_are_siblings (cpu, i)) { -cpumask_set_cpu (i, &cpu_sibling_map[cpu]); -cpumask_set_cpu (cpu, &cpu_sibling_map[i]); - } - } - } -} - -static inline void set_cpu_core_map (int cpu) -{ -int i; - -cpumask_set_cpu (cpu, &cpu_core_setup_map); - -for_each_cpu (i, &cpu_core_setup_map) { -if (cpu_data[cpu].package == cpu_data[i].package) { -cpumask_set_cpu (i, &cpu_core_map[cpu]); -cpumask_set_cpu (cpu, &cpu_core_map[i]); - } - } -} - -/* -* Calculate a new cpu_foreign_map mask whenever a -* new cpu appears or disappears. -*/ -void calculate_cpu_foreign_map (void ) -{ -int i, k, core_present; -cpumask_t temp_foreign_map; - -/* Re-calculate the mask */ -cpumask_clear (&temp_foreign_map); -for_each_online_cpu (i) { - core_present =0 ; -for_each_cpu (k, &temp_foreign_map) -if (cpus_are_siblings (i, k)) - core_present =1 ; -if (!core_present) -cpumask_set_cpu (i, &temp_foreign_map); - } - -for_each_online_cpu (i) -cpumask_andnot (&cpu_foreign_map[i], - &temp_foreign_map, &cpu_sibling_map[i]); -} -/* Preload SMP state for boot cpu */ void smp_prepare_boot_cpu (void ) {
Signature · 1 line
2.53.0