6.18.32-rc1 review
Subsystems (7)
Greg Kroah-Hartman
maintainerPATCH 628/957 ksmbd: fix use-after-free in smb2_open during durable reconnect
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Akif <[email protected]>
[ Upstream commit 1baff47b81f94f9231c91236aa511420d0e266b9 ]
In smb2_open, the call to ksmbd_put_durable_fd(fp) drops the reference to the durable file descriptor early during the durable reconnect process. If an error occurs subsequently (eg, ksmbd_iov_pin_rsp fails) or a scavenger accesses the file, it leads to a use-after-free when accessing fp properties (eg fp->create_time).
Move the single put to the end of the function below err_out2 so fp stays valid until smb2_open returns.
--- fs/smb/server/smb2pdu.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-)
fs/smb/server/smb2pdu.c +5 -8
@@ -3024,29 +3024,23 @@ int smb2_open(struct ksmbd_work *work)if (dh_info.reconnected == true) { rc =smb2_check_durable_oplock (conn, share, dh_info.fp, lc, sess->user, name); -if (rc) { -ksmbd_put_durable_fd (dh_info.fp); +if (rc) goto err_out2; - } rc =ksmbd_reopen_durable_fd (work, dh_info.fp); -if (rc) { -ksmbd_put_durable_fd (dh_info.fp); +if (rc) goto err_out2; - } fp = dh_info.fp;if (ksmbd_override_fsids (work)) { rc = -ENOMEM ; -ksmbd_put_durable_fd (dh_info.fp); goto err_out2; } file_info =FILE_OPENED ; rc =ksmbd_vfs_getattr (&fp->filp->f_path, &stat); -ksmbd_put_durable_fd (fp);if (rc) goto err_out2;
@@ -3816,6 +3810,9 @@ int smb2_open(struct ksmbd_work *work)ksmbd_debug (SMB ,"Error response: %x\n" , rsp->hdr.Status); } +if (dh_info.reconnected) +ksmbd_put_durable_fd (dh_info.fp); +kfree (name);kfree (lc);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 629/957 tcp: move tp->chrono_type next tp->chrono_stat[]
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 4b78c9cbd8f1fbb9517aee48b372646f4cf05442 ]
chrono_type is currently in tcp_sock_read_txrx group, which is supposed to hold read-mostly fields.
But chrono_type is mostly written in tx path, it should be moved to tcp_sock_write_tx group, close to other chrono fields (chrono_stat[], chrono_start).
Note this adds holes, but data locality is far more important.
Use a full u8 for the time being, compiler can generate more efficient code.
Stable-dep-of: 267bf3cf9a6f ("tcp: annotate data-races in tcp_get_info_chrono_stats()")
--- include/linux/tcp.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
include/linux/tcp.h +2 -2
@@ -231,8 +231,7 @@ struct tcp_sock {u32 sacked_out;/* SACK'd packets */ u16 tcp_header_len;/* Bytes of tcp header to send */ u8 scaling_ratio;/* see tcp_win_from_space() */ - u8 chrono_type : 2, /* current chronograph type */ - repair : 1, + u8 repair : 1, tcp_usec_ts : 1, /* TSval values in usec */ is_sack_reneg:1, /* in recovery from loss with SACK reneg? */ is_cwnd_limited:1,/* forward progress limited by snd_cwnd? */
@@ -267,6 +266,7 @@ struct tcp_sock {* total number of data bytes sent. */ u32 snd_sml;/* Last byte of the most recently transmitted small packet */ +u8 chrono_type;/* current chronograph type */ u32 chrono_start;/* Start time in jiffies of a TCP chrono */ u32 chrono_stat[3 ];/* Time in jiffies for chrono_stat stats */ u32 write_seq;/* Tail(+1) of data held in tcp send buffer */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 630/957 tcp: inline tcp_chrono_start()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit d6d4ff335db2d9242937ca474d292010acd35c38 ]
tcp_chrono_start() is small enough, and used in TCP sendmsg() fast path (from tcp_skb_entail()).
Note clang is already inlining it from functions in tcp_output.c.
Inlining it improves performance and reduces bloat :
$ scripts/bloat-o-meter -t vmlinux.old vmlinux.new add/remove: 0/2 grow/shrink: 1/0 up/down: 1/-84 (-83) Function old new delta tcp_skb_entail 280 281 +1 __pfx_tcp_chrono_start 16 - -16 tcp_chrono_start 68 - -68 Total: Before=25192434, After=25192351, chg -0.00%
Note that tcp_chrono_stop() is too big.
Stable-dep-of: 267bf3cf9a6f ("tcp: annotate data-races in tcp_get_info_chrono_stats()")
--- include/net/tcp.h | 25 ++++++++++++++++++++++++- net/ipv4/tcp_output.c | 24 ------------------------ 2 files changed, 24 insertions(+), 25 deletions(-)
include/net/tcp.h +24 -1
@@ -2124,7 +2124,30 @@ enum tcp_chrono { __TCP_CHRONO_MAX, }; -void tcp_chrono_start (struct sock *sk,const enum tcp_chrono type); +static inline void tcp_chrono_set (struct tcp_sock *tp,const enum tcp_chrono new) +{ +const u32 now = tcp_jiffies32; +enum tcp_chrono old = tp->chrono_type; + +if (old >TCP_CHRONO_UNSPEC ) + tp->chrono_stat[old -1 ] += now - tp->chrono_start; + tp->chrono_start = now; + tp->chrono_type = new; +} + +static inline void tcp_chrono_start (struct sock *sk,const enum tcp_chrono type) +{ +struct tcp_sock *tp =tcp_sk (sk); + +/* If there are multiple conditions worthy of tracking in a +* chronograph then the highest priority enum takes precedence +* over the other conditions. So that if something "more interesting" +* starts happening, stop the previous chrono and start a new one. +*/ +if (type > tp->chrono_type) +tcp_chrono_set (tp, type); +} +void tcp_chrono_stop (struct sock *sk,const enum tcp_chrono type);/* This helper is needed, because skb->tcp_tsorted_anchor uses
net/ipv4/tcp_output.c +0 -24
@@ -2813,30 +2813,6 @@ static bool tcp_small_queue_check(struct sock *sk, const struct sk_buff *skb,return false; } -static void tcp_chrono_set (struct tcp_sock *tp,const enum tcp_chrono new) -{ -const u32 now = tcp_jiffies32; -enum tcp_chrono old = tp->chrono_type; - -if (old >TCP_CHRONO_UNSPEC ) - tp->chrono_stat[old -1 ] += now - tp->chrono_start; - tp->chrono_start = now; - tp->chrono_type = new; -} - -void tcp_chrono_start (struct sock *sk,const enum tcp_chrono type) -{ -struct tcp_sock *tp =tcp_sk (sk); - -/* If there are multiple conditions worthy of tracking in a -* chronograph then the highest priority enum takes precedence -* over the other conditions. So that if something "more interesting" -* starts happening, stop the previous chrono and start a new one. -*/ -if (type > tp->chrono_type) -tcp_chrono_set (tp, type); -} -void tcp_chrono_stop (struct sock *sk,const enum tcp_chrono type) {struct tcp_sock *tp =tcp_sk (sk);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 631/957 tcp: annotate data-races in tcp_get_info_chrono_stats()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 267bf3cf9a6f0ffb98b8afd983c1950e835f07c9 ]
tcp_get_timestamping_opt_stats() does not own the socket lock, this is intentional.
It calls tcp_get_info_chrono_stats() while other threads could change chrono fields in tcp_chrono_set().
I do not think we need coherent TCP socket state snapshot in tcp_get_timestamping_opt_stats(), I chose to only add annotations to keep KCSAN happy.
--- include/net/tcp.h | 10 +++++++--- net/ipv4/tcp.c | 14 ++++++++++---- 2 files changed, 17 insertions(+), 7 deletions(-)
include/net/tcp.h +7 -3
@@ -2129,10 +2129,14 @@ static inline void tcp_chrono_set(struct tcp_sock *tp, const enum tcp_chrono newconst u32 now = tcp_jiffies32;enum tcp_chrono old = tp->chrono_type; +/* Following WRITE_ONCE()s pair with READ_ONCE()s in +* tcp_get_info_chrono_stats(). +*/ if (old >TCP_CHRONO_UNSPEC ) - tp->chrono_stat[old -1 ] += now - tp->chrono_start; - tp->chrono_start = now; - tp->chrono_type = new; +WRITE_ONCE (tp->chrono_stat[old -1 ], + tp->chrono_stat[old -1 ] + now - tp->chrono_start); +WRITE_ONCE (tp->chrono_start, now); +WRITE_ONCE (tp->chrono_type, new); }static inline void tcp_chrono_start (struct sock *sk,const enum tcp_chrono type)
net/ipv4/tcp.c +10 -4
@@ -4151,12 +4151,18 @@ static void tcp_get_info_chrono_stats(const struct tcp_sock *tp, struct tcp_info *info) {u64 stats[__TCP_CHRONO_MAX], total =0 ; -enum tcp_chrono i; +enum tcp_chrono i, cur; +/* Following READ_ONCE()s pair with WRITE_ONCE()s in tcp_chrono_set(). +* This is because socket lock might not be owned by us at this point. +* This is best effort, tcp_get_timestamping_opt_stats() can +* see wrong values. A real fix would be too costly for TCP fast path. +*/ + cur =READ_ONCE (tp->chrono_type);for (i =TCP_CHRONO_BUSY ; i < __TCP_CHRONO_MAX; ++i) { - stats[i] = tp->chrono_stat[i -1 ]; -if (i == tp->chrono_type) - stats[i] += tcp_jiffies32 - tp->chrono_start; + stats[i] =READ_ONCE (tp->chrono_stat[i -1 ]); +if (i == cur) + stats[i] += tcp_jiffies32 -READ_ONCE (tp->chrono_start); stats[i] *=USEC_PER_SEC /HZ ; total += stats[i]; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 632/957 tcp: add data-race annotations around tp->data_segs_out and tp->total_retrans
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 21e92a38cfd891538598ba8f805e0165a820d532 ]
tcp_get_timestamping_opt_stats() intentionally runs lockless, we must add READ_ONCE() and WRITE_ONCE() annotations to keep KCSAN happy.
--- net/ipv4/tcp.c | 4 ++-- net/ipv4/tcp_output.c | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-)
net/ipv4/tcp.c +2 -2
@@ -4386,9 +4386,9 @@ struct sk_buff *tcp_get_timestamping_opt_stats(const struct sock *sk,nla_put_u64_64bit (stats,TCP_NLA_SNDBUF_LIMITED , info.tcpi_sndbuf_limited,TCP_NLA_PAD );nla_put_u64_64bit (stats,TCP_NLA_DATA_SEGS_OUT , - tp->data_segs_out,TCP_NLA_PAD ); +READ_ONCE (tp->data_segs_out),TCP_NLA_PAD );nla_put_u64_64bit (stats,TCP_NLA_TOTAL_RETRANS , - tp->total_retrans,TCP_NLA_PAD ); +READ_ONCE (tp->total_retrans),TCP_NLA_PAD ); rate =READ_ONCE (sk->sk_pacing_rate); rate64 = (rate != ~0UL ) ? rate : ~0ULL ;
net/ipv4/tcp_output.c +5 -3
@@ -1603,7 +1603,8 @@ static int __tcp_transmit_skb(struct sock *sk, struct sk_buff *skb,if (skb->len != tcp_header_size) {tcp_event_data_sent (tp, sk); - tp->data_segs_out +=tcp_skb_pcount (skb); +WRITE_ONCE (tp->data_segs_out, + tp->data_segs_out +tcp_skb_pcount (skb)); tp->bytes_sent += skb->len - tcp_header_size; }
@@ -3556,7 +3557,7 @@ int __tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb, int segs)TCP_ADD_STATS (sock_net (sk),TCP_MIB_RETRANSSEGS , segs);if (TCP_SKB_CB (skb)->tcp_flags &TCPHDR_SYN )__NET_INC_STATS (sock_net (sk),LINUX_MIB_TCPSYNRETRANS ); - tp->total_retrans += segs; +WRITE_ONCE (tp->total_retrans, tp->total_retrans + segs); tp->bytes_retrans += skb->len;/* make sure skb->data is aligned on arches that require it
@@ -4581,7 +4582,8 @@ int tcp_rtx_synack(const struct sock *sk, struct request_sock *req)* However in this case, we are dealing with a passive fastopen * socket thus we can change total_retrans value. */ -tcp_sk_rw (sk)->total_retrans++; +WRITE_ONCE (tcp_sk_rw (sk)->total_retrans, +tcp_sk_rw (sk)->total_retrans +1 ); }trace_tcp_retransmit_synack (sk, req);WRITE_ONCE (req->num_retrans, req->num_retrans +1 );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 633/957 tcp: add data-races annotations around tp->reordering, tp->snd_cwnd
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 829ba1f329cb7cbd56d599a6d225997fba66dc32 ]
tcp_get_timestamping_opt_stats() intentionally runs lockless, we must add READ_ONCE(), WRITE_ONCE() data_race() annotations to keep KCSAN happy.
--- include/net/tcp.h | 2 +- net/ipv4/tcp.c | 8 ++++---- net/ipv4/tcp_input.c | 14 ++++++++------ net/ipv4/tcp_metrics.c | 2 +- 4 files changed, 14 insertions(+), 12 deletions(-)
include/net/tcp.h +1 -1
@@ -1429,7 +1429,7 @@ static inline u32 tcp_snd_cwnd(const struct tcp_sock *tp)static inline void tcp_snd_cwnd_set (struct tcp_sock *tp,u32 val) {WARN_ON_ONCE ((int )val <=0 ); - tp->snd_cwnd = val; +WRITE_ONCE (tp->snd_cwnd, val); }static inline bool tcp_in_slow_start (const struct tcp_sock *tp)
net/ipv4/tcp.c +4 -4
@@ -4397,13 +4397,13 @@ struct sk_buff *tcp_get_timestamping_opt_stats(const struct sock *sk, rate64 =tcp_compute_delivery_rate (tp);nla_put_u64_64bit (stats,TCP_NLA_DELIVERY_RATE , rate64,TCP_NLA_PAD ); -nla_put_u32 (stats,TCP_NLA_SND_CWND ,tcp_snd_cwnd (tp)); -nla_put_u32 (stats,TCP_NLA_REORDERING , tp->reordering); -nla_put_u32 (stats,TCP_NLA_MIN_RTT ,tcp_min_rtt (tp)); +nla_put_u32 (stats,TCP_NLA_SND_CWND ,READ_ONCE (tp->snd_cwnd)); +nla_put_u32 (stats,TCP_NLA_REORDERING ,READ_ONCE (tp->reordering)); +nla_put_u32 (stats,TCP_NLA_MIN_RTT ,data_race (tcp_min_rtt (tp)));nla_put_u8 (stats,TCP_NLA_RECUR_RETRANS ,READ_ONCE (inet_csk (sk)->icsk_retransmits)); -nla_put_u8 (stats,TCP_NLA_DELIVERY_RATE_APP_LMT , !!tp->rate_app_limited); +nla_put_u8 (stats,TCP_NLA_DELIVERY_RATE_APP_LMT ,data_race (!!tp->rate_app_limited));nla_put_u32 (stats,TCP_NLA_SND_SSTHRESH , tp->snd_ssthresh);nla_put_u32 (stats,TCP_NLA_DELIVERED , tp->delivered);nla_put_u32 (stats,TCP_NLA_DELIVERED_CE , tp->delivered_ce);
net/ipv4/tcp_input.c +8 -6
@@ -1260,8 +1260,9 @@ static void tcp_check_sack_reordering(struct sock *sk, const u32 low_seq, tp->sacked_out, tp->undo_marker ? tp->undo_retrans : 0);#endif - tp->reordering =min_t (u32, (metric + mss -1 ) / mss, -READ_ONCE (sock_net (sk)->ipv4.sysctl_tcp_max_reordering)); +WRITE_ONCE (tp->reordering, +min_t (u32, (metric + mss -1 ) / mss, +READ_ONCE (sock_net (sk)->ipv4.sysctl_tcp_max_reordering))); }/* This exciting event is worth to be remembered. 8) */
@@ -2220,8 +2221,9 @@ static void tcp_check_reno_reordering(struct sock *sk, const int addend)if (!tcp_limit_reno_sacked (tp))return ; - tp->reordering =min_t (u32, tp->packets_out + addend, -READ_ONCE (sock_net (sk)->ipv4.sysctl_tcp_max_reordering)); +WRITE_ONCE (tp->reordering, +min_t (u32, tp->packets_out + addend, +READ_ONCE (sock_net (sk)->ipv4.sysctl_tcp_max_reordering))); tp->reord_seen++;NET_INC_STATS (sock_net (sk),LINUX_MIB_TCPRENOREORDER ); }
@@ -2360,8 +2362,8 @@ void tcp_enter_loss(struct sock *sk) reordering =READ_ONCE (net->ipv4.sysctl_tcp_reordering);if (icsk->icsk_ca_state <= TCP_CA_Disorder && tp->sacked_out >= reordering) - tp->reordering = min_t(unsigned int, tp->reordering, - reordering); +WRITE_ONCE (tp->reordering, + min_t(unsigned int, tp->reordering, reordering));tcp_set_ca_state (sk, TCP_CA_Loss); tp->high_seq = tp->snd_nxt;
net/ipv4/tcp_metrics.c +1 -1
@@ -496,7 +496,7 @@ void tcp_init_metrics(struct sock *sk) } val =tcp_metric_get (tm,TCP_METRIC_REORDERING );if (val && tp->reordering != val) - tp->reordering = val; +WRITE_ONCE (tp->reordering, val); crtt =tcp_metric_get (tm,TCP_METRIC_RTT );rcu_read_unlock ();
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 634/957 tcp: annotate data-races around tp->snd_ssthresh
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit fd571afb05ebaeac5d8f09460a0640d4cf6755f8 ]
tcp_get_timestamping_opt_stats() intentionally runs lockless, we must add READ_ONCE() and WRITE_ONCE() annotations to keep KCSAN happy.
--- net/core/filter.c | 2 +- net/ipv4/tcp.c | 4 ++-- net/ipv4/tcp_bbr.c | 6 +++--- net/ipv4/tcp_bic.c | 2 +- net/ipv4/tcp_cdg.c | 4 ++-- net/ipv4/tcp_cubic.c | 6 +++--- net/ipv4/tcp_dctcp.c | 2 +- net/ipv4/tcp_input.c | 8 ++++---- net/ipv4/tcp_metrics.c | 4 ++-- net/ipv4/tcp_nv.c | 4 ++-- net/ipv4/tcp_output.c | 4 ++-- net/ipv4/tcp_vegas.c | 9 +++++---- net/ipv4/tcp_westwood.c | 4 ++-- net/ipv4/tcp_yeah.c | 3 ++- 14 files changed, 32 insertions(+), 30 deletions(-)
net/core/filter.c +1 -1
@@ -5387,7 +5387,7 @@ static int bpf_sol_tcp_setsockopt(struct sock *sk, int optname,if (val <=0 )return -EINVAL ; tp->snd_cwnd_clamp = val; - tp->snd_ssthresh = val; +WRITE_ONCE (tp->snd_ssthresh, val);break ;case TCP_BPF_DELACK_MAX : timeout =usecs_to_jiffies (val);
net/ipv4/tcp.c +2 -2
@@ -3408,7 +3408,7 @@ int tcp_disconnect(struct sock *sk, int flags) icsk->icsk_rto =TCP_TIMEOUT_INIT ;WRITE_ONCE (icsk->icsk_rto_min,TCP_RTO_MIN );WRITE_ONCE (icsk->icsk_delack_max,TCP_DELACK_MAX ); - tp->snd_ssthresh =TCP_INFINITE_SSTHRESH ; +WRITE_ONCE (tp->snd_ssthresh,TCP_INFINITE_SSTHRESH );tcp_snd_cwnd_set (tp,TCP_INIT_CWND ); tp->snd_cwnd_cnt =0 ; tp->is_cwnd_limited =0 ;
@@ -4404,7 +4404,7 @@ struct sk_buff *tcp_get_timestamping_opt_stats(const struct sock *sk,nla_put_u8 (stats,TCP_NLA_RECUR_RETRANS ,READ_ONCE (inet_csk (sk)->icsk_retransmits));nla_put_u8 (stats,TCP_NLA_DELIVERY_RATE_APP_LMT ,data_race (!!tp->rate_app_limited)); -nla_put_u32 (stats,TCP_NLA_SND_SSTHRESH , tp->snd_ssthresh); +nla_put_u32 (stats,TCP_NLA_SND_SSTHRESH ,READ_ONCE (tp->snd_ssthresh));nla_put_u32 (stats,TCP_NLA_DELIVERED , tp->delivered);nla_put_u32 (stats,TCP_NLA_DELIVERED_CE , tp->delivered_ce);
net/ipv4/tcp_bbr.c +3 -3
@@ -896,8 +896,8 @@ static void bbr_check_drain(struct sock *sk, const struct rate_sample *rs)if (bbr->mode ==BBR_STARTUP &&bbr_full_bw_reached (sk)) { bbr->mode =BBR_DRAIN ;/* drain queue we created */ -tcp_sk (sk)->snd_ssthresh = -bbr_inflight (sk,bbr_max_bw (sk),BBR_UNIT ); +WRITE_ONCE (tcp_sk (sk)->snd_ssthresh, +bbr_inflight (sk,bbr_max_bw (sk),BBR_UNIT )); }/* fall through to check if in-flight is already small: */ if (bbr->mode ==BBR_DRAIN && bbr_packets_in_net_at_edt(sk, tcp_packets_in_flight(tcp_sk(sk))) <=
@@ -1042,7 +1042,7 @@ __bpf_kfunc static void bbr_init(struct sock *sk)struct bbr *bbr =inet_csk_ca (sk); bbr->prior_cwnd =0 ; - tp->snd_ssthresh =TCP_INFINITE_SSTHRESH ; +WRITE_ONCE (tp->snd_ssthresh,TCP_INFINITE_SSTHRESH ); bbr->rtt_cnt =0 ; bbr->next_rtt_delivered = tp->delivered; bbr->prev_ca_state = TCP_CA_Open;
net/ipv4/tcp_bic.c +1 -1
@@ -74,7 +74,7 @@ static void bictcp_init(struct sock *sk)bictcp_reset (ca);if (initial_ssthresh) -tcp_sk (sk)->snd_ssthresh = initial_ssthresh; +WRITE_ONCE (tcp_sk (sk)->snd_ssthresh, initial_ssthresh); }/*
net/ipv4/tcp_cdg.c +2 -2
@@ -162,7 +162,7 @@ static void tcp_cdg_hystart_update(struct sock *sk)NET_ADD_STATS (sock_net (sk),LINUX_MIB_TCPHYSTARTTRAINCWND ,tcp_snd_cwnd (tp)); - tp->snd_ssthresh =tcp_snd_cwnd (tp); +WRITE_ONCE (tp->snd_ssthresh,tcp_snd_cwnd (tp));return ; } }
@@ -181,7 +181,7 @@ static void tcp_cdg_hystart_update(struct sock *sk)NET_ADD_STATS (sock_net (sk),LINUX_MIB_TCPHYSTARTDELAYCWND ,tcp_snd_cwnd (tp)); - tp->snd_ssthresh =tcp_snd_cwnd (tp); +WRITE_ONCE (tp->snd_ssthresh,tcp_snd_cwnd (tp)); } } }
net/ipv4/tcp_cubic.c +3 -3
@@ -136,7 +136,7 @@ __bpf_kfunc static void cubictcp_init(struct sock *sk)bictcp_hystart_reset (sk);if (!hystart && initial_ssthresh) -tcp_sk (sk)->snd_ssthresh = initial_ssthresh; +WRITE_ONCE (tcp_sk (sk)->snd_ssthresh, initial_ssthresh); } __bpf_kfunc static void cubictcp_cwnd_event(struct sock *sk, enum tcp_ca_event event)
@@ -423,7 +423,7 @@ static void hystart_update(struct sock *sk, u32 delay)NET_ADD_STATS (sock_net (sk),LINUX_MIB_TCPHYSTARTTRAINCWND ,tcp_snd_cwnd (tp)); - tp->snd_ssthresh =tcp_snd_cwnd (tp); +WRITE_ONCE (tp->snd_ssthresh,tcp_snd_cwnd (tp)); } } }
@@ -443,7 +443,7 @@ static void hystart_update(struct sock *sk, u32 delay)NET_ADD_STATS (sock_net (sk),LINUX_MIB_TCPHYSTARTDELAYCWND ,tcp_snd_cwnd (tp)); - tp->snd_ssthresh =tcp_snd_cwnd (tp); +WRITE_ONCE (tp->snd_ssthresh,tcp_snd_cwnd (tp)); } } }
net/ipv4/tcp_dctcp.c +1 -1
@@ -177,7 +177,7 @@ static void dctcp_react_to_loss(struct sock *sk)struct tcp_sock *tp =tcp_sk (sk); ca->loss_cwnd =tcp_snd_cwnd (tp); - tp->snd_ssthresh =max (tcp_snd_cwnd (tp) >>1U ,2U ); +WRITE_ONCE (tp->snd_ssthresh,max (tcp_snd_cwnd (tp) >>1U ,2U )); } __bpf_kfunc static void dctcp_state(struct sock *sk, u8 new_state)
net/ipv4/tcp_input.c +4 -4
@@ -2348,7 +2348,7 @@ void tcp_enter_loss(struct sock *sk) (icsk->icsk_ca_state == TCP_CA_Loss && !icsk->icsk_retransmits)) { tp->prior_ssthresh =tcp_current_ssthresh (sk); tp->prior_cwnd =tcp_snd_cwnd (tp); - tp->snd_ssthresh = icsk->icsk_ca_ops->ssthresh (sk); +WRITE_ONCE (tp->snd_ssthresh, icsk->icsk_ca_ops->ssthresh (sk));tcp_ca_event (sk,CA_EVENT_LOSS );tcp_init_undo (tp); }
@@ -2641,7 +2641,7 @@ static void tcp_undo_cwnd_reduction(struct sock *sk, bool unmark_loss)tcp_snd_cwnd_set (tp, icsk->icsk_ca_ops->undo_cwnd (sk));if (tp->prior_ssthresh > tp->snd_ssthresh) { - tp->snd_ssthresh = tp->prior_ssthresh; +WRITE_ONCE (tp->snd_ssthresh, tp->prior_ssthresh);tcp_ecn_withdraw_cwr (tp); } }
@@ -2759,7 +2759,7 @@ static void tcp_init_cwnd_reduction(struct sock *sk) tp->prior_cwnd =tcp_snd_cwnd (tp); tp->prr_delivered =0 ; tp->prr_out =0 ; - tp->snd_ssthresh =inet_csk (sk)->icsk_ca_ops->ssthresh (sk); +WRITE_ONCE (tp->snd_ssthresh,inet_csk (sk)->icsk_ca_ops->ssthresh (sk));tcp_ecn_queue_cwr (tp); }
@@ -2901,7 +2901,7 @@ static void tcp_non_congestion_loss_retransmit(struct sock *sk)if (icsk->icsk_ca_state != TCP_CA_Loss) { tp->high_seq = tp->snd_nxt; - tp->snd_ssthresh =tcp_current_ssthresh (sk); +WRITE_ONCE (tp->snd_ssthresh,tcp_current_ssthresh (sk)); tp->prior_ssthresh =0 ; tp->undo_marker =0 ;tcp_set_ca_state (sk, TCP_CA_Loss);
net/ipv4/tcp_metrics.c +2 -2
@@ -490,9 +490,9 @@ void tcp_init_metrics(struct sock *sk) val =READ_ONCE (net->ipv4.sysctl_tcp_no_ssthresh_metrics_save) ?0 :tcp_metric_get (tm,TCP_METRIC_SSTHRESH );if (val) { - tp->snd_ssthresh = val; +WRITE_ONCE (tp->snd_ssthresh, val);if (tp->snd_ssthresh > tp->snd_cwnd_clamp) - tp->snd_ssthresh = tp->snd_cwnd_clamp; +WRITE_ONCE (tp->snd_ssthresh, tp->snd_cwnd_clamp); } val =tcp_metric_get (tm,TCP_METRIC_REORDERING );if (val && tp->reordering != val)
net/ipv4/tcp_nv.c +2 -2
@@ -396,8 +396,8 @@ static void tcpnv_acked(struct sock *sk, const struct ack_sample *sample)/* We have enough data to determine we are congested */ ca->nv_allow_cwnd_growth =0 ; - tp->snd_ssthresh = - (nv_ssthresh_factor * max_win) >>3 ; +WRITE_ONCE (tp->snd_ssthresh, + (nv_ssthresh_factor * max_win) >>3 );if (tcp_snd_cwnd (tp) - max_win >2 ) {/* gap > 2, we do exponential cwnd decrease */ int dec;
net/ipv4/tcp_output.c +2 -2
@@ -151,7 +151,7 @@ void tcp_cwnd_restart(struct sock *sk, s32 delta)tcp_ca_event (sk,CA_EVENT_CWND_RESTART ); - tp->snd_ssthresh =tcp_current_ssthresh (sk); +WRITE_ONCE (tp->snd_ssthresh,tcp_current_ssthresh (sk)); restart_cwnd =min (restart_cwnd, cwnd);while ((delta -=inet_csk (sk)->icsk_rto) >0 && cwnd > restart_cwnd)
@@ -2060,7 +2060,7 @@ static void tcp_cwnd_application_limited(struct sock *sk)u32 init_win =tcp_init_cwnd (tp,__sk_dst_get (sk));u32 win_used =max (tp->snd_cwnd_used, init_win);if (win_used <tcp_snd_cwnd (tp)) { - tp->snd_ssthresh =tcp_current_ssthresh (sk); +WRITE_ONCE (tp->snd_ssthresh,tcp_current_ssthresh (sk));tcp_snd_cwnd_set (tp, (tcp_snd_cwnd (tp) + win_used) >>1 ); } tp->snd_cwnd_used =0 ;
net/ipv4/tcp_vegas.c +5 -4
@@ -240,7 +240,8 @@ static void tcp_vegas_cong_avoid(struct sock *sk, u32 ack, u32 acked)*/ tcp_snd_cwnd_set (tp,min (tcp_snd_cwnd (tp), (u32 )target_cwnd +1 )); - tp->snd_ssthresh =tcp_vegas_ssthresh (tp); +WRITE_ONCE (tp->snd_ssthresh, +tcp_vegas_ssthresh (tp)); }else if (tcp_in_slow_start (tp)) {/* Slow start. */
@@ -256,8 +257,8 @@ static void tcp_vegas_cong_avoid(struct sock *sk, u32 ack, u32 acked)* we slow down. */ tcp_snd_cwnd_set (tp,tcp_snd_cwnd (tp) -1 ); - tp->snd_ssthresh - =tcp_vegas_ssthresh (tp); +WRITE_ONCE (tp->snd_ssthresh, +tcp_vegas_ssthresh (tp)); }else if (diff < alpha) {/* We don't have enough extra packets * in the network, so speed up.
@@ -275,7 +276,7 @@ static void tcp_vegas_cong_avoid(struct sock *sk, u32 ack, u32 acked)else if (tcp_snd_cwnd (tp) > tp->snd_cwnd_clamp)tcp_snd_cwnd_set (tp, tp->snd_cwnd_clamp); - tp->snd_ssthresh =tcp_current_ssthresh (sk); +WRITE_ONCE (tp->snd_ssthresh,tcp_current_ssthresh (sk)); }/* Wipe the slate clean for the next RTT. */
net/ipv4/tcp_westwood.c +2 -2
@@ -244,11 +244,11 @@ static void tcp_westwood_event(struct sock *sk, enum tcp_ca_event event)switch (event) {case CA_EVENT_COMPLETE_CWR : - tp->snd_ssthresh =tcp_westwood_bw_rttmin (sk); +WRITE_ONCE (tp->snd_ssthresh,tcp_westwood_bw_rttmin (sk));tcp_snd_cwnd_set (tp, tp->snd_ssthresh);break ;case CA_EVENT_LOSS : - tp->snd_ssthresh =tcp_westwood_bw_rttmin (sk); +WRITE_ONCE (tp->snd_ssthresh,tcp_westwood_bw_rttmin (sk));/* Update RTT_min when next ack arrives */ w->reset_rtt_min =1 ;break ;
net/ipv4/tcp_yeah.c +2 -1
@@ -147,7 +147,8 @@ static void tcp_yeah_cong_avoid(struct sock *sk, u32 ack, u32 acked)tcp_snd_cwnd_set (tp,max (tcp_snd_cwnd (tp), yeah->reno_count)); - tp->snd_ssthresh =tcp_snd_cwnd (tp); +WRITE_ONCE (tp->snd_ssthresh, +tcp_snd_cwnd (tp)); }if (yeah->reno_count <=2 )
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 635/957 tcp: annotate data-races around tp->delivered and tp->delivered_ce
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit faa886ad3ce5fc8f5156493491fe189b2b726bc9 ]
tcp_get_timestamping_opt_stats() intentionally runs lockless, we must add READ_ONCE() and WRITE_ONCE() annotations to keep KCSAN happy.
--- include/net/tcp_ecn.h | 2 +- net/ipv4/tcp.c | 4 ++-- net/ipv4/tcp_input.c | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-)
include/net/tcp_ecn.h +1 -1
@@ -189,7 +189,7 @@ static inline void tcp_accecn_third_ack(struct sock *sk, tcp_accecn_validate_syn_feedback(sk, ace, sent_ect)) {if ((tcp_accecn_extract_syn_ect (ace) ==INET_ECN_CE ) && !tp->delivered_ce) - tp->delivered_ce++; +WRITE_ONCE (tp->delivered_ce,1 ); }break ; }
net/ipv4/tcp.c +2 -2
@@ -4405,8 +4405,8 @@ struct sk_buff *tcp_get_timestamping_opt_stats(const struct sock *sk, READ_ONCE(inet_csk(sk)->icsk_retransmits));nla_put_u8 (stats,TCP_NLA_DELIVERY_RATE_APP_LMT ,data_race (!!tp->rate_app_limited));nla_put_u32 (stats,TCP_NLA_SND_SSTHRESH ,READ_ONCE (tp->snd_ssthresh)); -nla_put_u32 (stats,TCP_NLA_DELIVERED , tp->delivered); -nla_put_u32 (stats,TCP_NLA_DELIVERED_CE , tp->delivered_ce); +nla_put_u32 (stats,TCP_NLA_DELIVERED ,READ_ONCE (tp->delivered)); +nla_put_u32 (stats,TCP_NLA_DELIVERED_CE ,READ_ONCE (tp->delivered_ce));nla_put_u32 (stats,TCP_NLA_SNDQ_SIZE , tp->write_seq - tp->snd_una);nla_put_u8 (stats,TCP_NLA_CA_STATE ,inet_csk (sk)->icsk_ca_state);
net/ipv4/tcp_input.c +4 -4
@@ -476,14 +476,14 @@ static bool tcp_accecn_process_option(struct tcp_sock *tp,static void tcp_count_delivered_ce (struct tcp_sock *tp,u32 ecn_count) { - tp->delivered_ce += ecn_count; +WRITE_ONCE (tp->delivered_ce, tp->delivered_ce + ecn_count); }/* Updates the delivered and delivered_ce counts */ static void tcp_count_delivered (struct tcp_sock *tp,u32 delivered,bool ece_ack) { - tp->delivered += delivered; +WRITE_ONCE (tp->delivered, tp->delivered + delivered);if (tcp_ecn_mode_rfc3168 (tp) && ece_ack)tcp_count_delivered_ce (tp, delivered); }
@@ -6576,7 +6576,7 @@ static bool tcp_rcv_fastopen_synack(struct sock *sk, struct sk_buff *synack,NET_INC_STATS (sock_net (sk),LINUX_MIB_TCPFASTOPENACTIVE );/* SYN-data is counted as two separate packets in tcp_ack() */ if (tp->delivered >1 ) - --tp->delivered; +WRITE_ONCE (tp->delivered, tp->delivered -1 ); }tcp_fastopen_add_skb (sk, synack);
@@ -7007,7 +7007,7 @@ tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb)SKB_DR_SET (reason,NOT_SPECIFIED );switch (sk->sk_state) {case TCP_SYN_RECV : - tp->delivered++;/* SYN-ACK delivery isn't tracked in tcp_ack */ +WRITE_ONCE (tp->delivered, tp->delivered +1 );/* SYN-ACK delivery isn't tracked in tcp_ack */ if (!tp->srtt_us)tcp_synack_rtt_meas (sk, req);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 636/957 tcp: add data-race annotations for TCP_NLA_SNDQ_SIZE
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 124199444de467767175a9004e1574dc42523e62 ]
tcp_get_timestamping_opt_stats() intentionally runs lockless, we must add READ_ONCE() and WRITE_ONCE() annotations to keep KCSAN happy.
--- net/ipv4/tcp.c | 4 +++- net/ipv4/tcp_input.c | 4 ++-- net/ipv4/tcp_output.c | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-)
net/ipv4/tcp.c +3 -1
@@ -4408,7 +4408,9 @@ struct sk_buff *tcp_get_timestamping_opt_stats(const struct sock *sk,nla_put_u32 (stats,TCP_NLA_DELIVERED ,READ_ONCE (tp->delivered));nla_put_u32 (stats,TCP_NLA_DELIVERED_CE ,READ_ONCE (tp->delivered_ce)); -nla_put_u32 (stats,TCP_NLA_SNDQ_SIZE , tp->write_seq - tp->snd_una); +nla_put_u32 (stats,TCP_NLA_SNDQ_SIZE , +max_t (int,0 , +READ_ONCE (tp->write_seq) -READ_ONCE (tp->snd_una)));nla_put_u8 (stats,TCP_NLA_CA_STATE ,inet_csk (sk)->icsk_ca_state); nla_put_u64_64bit(stats, TCP_NLA_BYTES_SENT, tp->bytes_sent,
net/ipv4/tcp_input.c +2 -2
@@ -3694,7 +3694,7 @@ static void tcp_snd_una_update(struct tcp_sock *tp, u32 ack)sock_owned_by_me ((struct sock *)tp); tp->bytes_acked += delta;tcp_snd_sne_update (tp, ack); - tp->snd_una = ack; +WRITE_ONCE (tp->snd_una, ack); }static void tcp_rcv_sne_update (struct tcp_sock *tp,u32 seq)
@@ -7035,7 +7035,7 @@ tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb)if (sk->sk_socket)sk_wake_async (sk,SOCK_WAKE_IO ,POLL_OUT ); - tp->snd_una =TCP_SKB_CB (skb)->ack_seq; +WRITE_ONCE (tp->snd_una,TCP_SKB_CB (skb)->ack_seq); tp->snd_wnd =ntohs (th->window) << tp->rx_opt.snd_wscale;tcp_init_wl (tp,TCP_SKB_CB (skb)->seq);
net/ipv4/tcp_output.c +1 -1
@@ -4089,7 +4089,7 @@ static void tcp_connect_init(struct sock *sk) tp->snd_wnd =0 ;tcp_init_wl (tp,0 );tcp_write_queue_purge (sk); - tp->snd_una = tp->write_seq; +WRITE_ONCE (tp->snd_una, tp->write_seq); tp->snd_sml = tp->write_seq; tp->snd_up = tp->write_seq;WRITE_ONCE (tp->snd_nxt, tp->write_seq);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 637/957 tcp: annotate data-races around tp->bytes_sent
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit ee43e957ce2ec77b2ec47fef28f3c0df6ab01a31 ]
tcp_get_timestamping_opt_stats() intentionally runs lockless, we must add READ_ONCE() and WRITE_ONCE() annotations to keep KCSAN happy.
--- net/ipv4/tcp.c | 2 +- net/ipv4/tcp_output.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-)
net/ipv4/tcp.c +1 -1
@@ -4413,7 +4413,7 @@ struct sk_buff *tcp_get_timestamping_opt_stats(const struct sock *sk, READ_ONCE(tp->write_seq) - READ_ONCE(tp->snd_una)));nla_put_u8 (stats,TCP_NLA_CA_STATE ,inet_csk (sk)->icsk_ca_state); -nla_put_u64_64bit (stats,TCP_NLA_BYTES_SENT , tp->bytes_sent, +nla_put_u64_64bit (stats,TCP_NLA_BYTES_SENT ,READ_ONCE (tp->bytes_sent),TCP_NLA_PAD );nla_put_u64_64bit (stats,TCP_NLA_BYTES_RETRANS , tp->bytes_retrans,TCP_NLA_PAD );
net/ipv4/tcp_output.c +2 -1
@@ -1605,7 +1605,8 @@ static int __tcp_transmit_skb(struct sock *sk, struct sk_buff *skb,tcp_event_data_sent (tp, sk);WRITE_ONCE (tp->data_segs_out, tp->data_segs_out +tcp_skb_pcount (skb)); - tp->bytes_sent += skb->len - tcp_header_size; +WRITE_ONCE (tp->bytes_sent, + tp->bytes_sent + skb->len - tcp_header_size); }if (after (tcb->end_seq, tp->snd_nxt) || tcb->seq == tcb->end_seq)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 638/957 tcp: annotate data-races around tp->bytes_retrans
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 5efc7b9f7cbd43401f1af81d3d7f2be00f93390d ]
tcp_get_timestamping_opt_stats() intentionally runs lockless, we must add READ_ONCE() and WRITE_ONCE() annotations to keep KCSAN happy.
--- net/ipv4/tcp.c | 4 ++-- net/ipv4/tcp_output.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-)
net/ipv4/tcp.c +2 -2
@@ -4415,8 +4415,8 @@ struct sk_buff *tcp_get_timestamping_opt_stats(const struct sock *sk,nla_put_u64_64bit (stats,TCP_NLA_BYTES_SENT ,READ_ONCE (tp->bytes_sent),TCP_NLA_PAD ); -nla_put_u64_64bit (stats,TCP_NLA_BYTES_RETRANS , tp->bytes_retrans, -TCP_NLA_PAD ); +nla_put_u64_64bit (stats,TCP_NLA_BYTES_RETRANS , +READ_ONCE (tp->bytes_retrans),TCP_NLA_PAD );nla_put_u32 (stats,TCP_NLA_DSACK_DUPS , tp->dsack_dups);nla_put_u32 (stats,TCP_NLA_REORD_SEEN , tp->reord_seen);nla_put_u32 (stats,TCP_NLA_SRTT , tp->srtt_us >>3 );
net/ipv4/tcp_output.c +1 -1
@@ -3559,7 +3559,7 @@ int __tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb, int segs)if (TCP_SKB_CB (skb)->tcp_flags &TCPHDR_SYN )__NET_INC_STATS (sock_net (sk),LINUX_MIB_TCPSYNRETRANS );WRITE_ONCE (tp->total_retrans, tp->total_retrans + segs); - tp->bytes_retrans += skb->len; +WRITE_ONCE (tp->bytes_retrans, tp->bytes_retrans + skb->len);/* make sure skb->data is aligned on arches that require it * and check if ack-trimming & collapsing extended the headroom
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 639/957 tcp: annotate data-races around tp->dsack_dups
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit a984705ca88b976bf1087978fd98b7f3993da88c ]
tcp_get_timestamping_opt_stats() intentionally runs lockless, we must add READ_ONCE() and WRITE_ONCE() annotations to keep KCSAN happy.
--- net/ipv4/tcp.c | 2 +- net/ipv4/tcp_input.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
net/ipv4/tcp.c +1 -1
@@ -4417,7 +4417,7 @@ struct sk_buff *tcp_get_timestamping_opt_stats(const struct sock *sk, TCP_NLA_PAD);nla_put_u64_64bit (stats,TCP_NLA_BYTES_RETRANS ,READ_ONCE (tp->bytes_retrans),TCP_NLA_PAD ); -nla_put_u32 (stats,TCP_NLA_DSACK_DUPS , tp->dsack_dups); +nla_put_u32 (stats,TCP_NLA_DSACK_DUPS ,READ_ONCE (tp->dsack_dups));nla_put_u32 (stats,TCP_NLA_REORD_SEEN , tp->reord_seen);nla_put_u32 (stats,TCP_NLA_SRTT , tp->srtt_us >>3 );nla_put_u16 (stats,TCP_NLA_TIMEOUT_REHASH , tp->timeout_rehash);
net/ipv4/tcp_input.c +1 -1
@@ -1213,7 +1213,7 @@ static u32 tcp_dsack_seen(struct tcp_sock *tp, u32 start_seq,else if (tp->tlp_high_seq && tp->tlp_high_seq == end_seq) state->flag |=FLAG_DSACK_TLP ; - tp->dsack_dups += dup_segs; +WRITE_ONCE (tp->dsack_dups, tp->dsack_dups + dup_segs);/* Skip the DSACK if dup segs weren't retransmitted by sender */ if (tp->dsack_dups > tp->total_retrans)return 0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 640/957 tcp: annotate data-races around tp->reord_seen
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 62585690e6b2a112c408fe25f142b246ac833c42 ]
tcp_get_timestamping_opt_stats() intentionally runs lockless, we must add READ_ONCE() and WRITE_ONCE() annotations to keep KCSAN happy.
--- net/ipv4/tcp.c | 2 +- net/ipv4/tcp_input.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-)
net/ipv4/tcp.c +1 -1
@@ -4418,7 +4418,7 @@ struct sk_buff *tcp_get_timestamping_opt_stats(const struct sock *sk,nla_put_u64_64bit (stats,TCP_NLA_BYTES_RETRANS ,READ_ONCE (tp->bytes_retrans),TCP_NLA_PAD );nla_put_u32 (stats,TCP_NLA_DSACK_DUPS ,READ_ONCE (tp->dsack_dups)); -nla_put_u32 (stats,TCP_NLA_REORD_SEEN , tp->reord_seen); +nla_put_u32 (stats,TCP_NLA_REORD_SEEN ,READ_ONCE (tp->reord_seen));nla_put_u32 (stats,TCP_NLA_SRTT , tp->srtt_us >>3 );nla_put_u16 (stats,TCP_NLA_TIMEOUT_REHASH , tp->timeout_rehash); nla_put_u32(stats, TCP_NLA_BYTES_NOTSENT,
net/ipv4/tcp_input.c +2 -2
@@ -1266,7 +1266,7 @@ static void tcp_check_sack_reordering(struct sock *sk, const u32 low_seq, }/* This exciting event is worth to be remembered. 8) */ - tp->reord_seen++; +WRITE_ONCE (tp->reord_seen, tp->reord_seen +1 );NET_INC_STATS (sock_net (sk), ts ?LINUX_MIB_TCPTSREORDER :LINUX_MIB_TCPSACKREORDER ); }
@@ -2224,7 +2224,7 @@ static void tcp_check_reno_reordering(struct sock *sk, const int addend)WRITE_ONCE (tp->reordering,min_t (u32, tp->packets_out + addend,READ_ONCE (sock_net (sk)->ipv4.sysctl_tcp_max_reordering))); - tp->reord_seen++; +WRITE_ONCE (tp->reord_seen, tp->reord_seen +1 );NET_INC_STATS (sock_net (sk),LINUX_MIB_TCPRENOREORDER ); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 641/957 tcp: better handle TCP_TX_DELAY on established flows
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 1c51450f1afff1e7419797720df3fbd9ccbf610c ]
Some applications uses TCP_TX_DELAY socket option after TCP flow is established.
Some metrics need to be updated, otherwise TCP might take time to adapt to the new (emulated) RTT.
This patch adjusts tp->srtt_us, tp->rtt_min, icsk_rto and sk->sk_pacing_rate.
This is best effort, and for instance icsk_rto is reset without taking backoff into account.
Stable-dep-of: 290b693ce7c9 ("tcp: annotate data-races around tp->srtt_us")
--- include/net/tcp.h | 2 ++ net/ipv4/tcp.c | 31 +++++++++++++++++++++++++++---- net/ipv4/tcp_input.c | 4 ++-- 3 files changed, 31 insertions(+), 6 deletions(-)
include/net/tcp.h +2 -0
@@ -462,6 +462,8 @@ enum skb_drop_reason tcp_child_process(struct sock *parent, struct sock *child,void tcp_enter_loss (struct sock *sk);void tcp_cwnd_reduction (struct sock *sk,int newly_acked_sacked,int newly_lost,int flag);void tcp_clear_retrans (struct tcp_sock *tp); +void tcp_update_pacing_rate (struct sock *sk); +void tcp_set_rto (struct sock *sk);void tcp_update_metrics (struct sock *sk);void tcp_init_metrics (struct sock *sk);void tcp_metrics_init (void );
net/ipv4/tcp.c +27 -4
@@ -3585,9 +3585,12 @@ static int tcp_repair_options_est(struct sock *sk, sockptr_t optbuf,DEFINE_STATIC_KEY_FALSE (tcp_tx_delay_enabled);EXPORT_IPV6_MOD (tcp_tx_delay_enabled); -static void tcp_enable_tx_delay (void ) +static void tcp_enable_tx_delay (struct sock *sk,int val) { -if (!static_branch_unlikely (&tcp_tx_delay_enabled)) { +struct tcp_sock *tp =tcp_sk (sk); +s32 delta = (val - tp->tcp_tx_delay) <<3 ; + +if (val && !static_branch_unlikely (&tcp_tx_delay_enabled)) {static int __tcp_tx_delay_enabled =0 ;if (cmpxchg (&__tcp_tx_delay_enabled,0 ,1 ) ==0 ) {
@@ -3595,6 +3598,22 @@ static void tcp_enable_tx_delay(void)pr_info ("TCP_TX_DELAY enabled\n" ); } } +/* If we change tcp_tx_delay on a live flow, adjust tp->srtt_us, +* tp->rtt_min, icsk_rto and sk->sk_pacing_rate. +* This is best effort. +*/ +if (delta && sk->sk_state ==TCP_ESTABLISHED ) { +s64 srtt = (s64 )tp->srtt_us + delta; + + tp->srtt_us =clamp_t (s64, srtt,1 , ~0U ); + +/* Note: does not deal with non zero icsk_backoff */ +tcp_set_rto (sk); + +minmax_reset (&tp->rtt_min, tcp_jiffies32, ~0U ); + +tcp_update_pacing_rate (sk); + } }/* When set indicates to always queue non-full frames. Later the user clears
@@ -4121,8 +4140,12 @@ int do_tcp_setsockopt(struct sock *sk, int level, int optname, tp->recvmsg_inq = val;break ;case TCP_TX_DELAY : -if (val) -tcp_enable_tx_delay (); +/* tp->srtt_us is u32, and is shifted by 3 */ +if (val <0 || val >= (1U << (31 -3 ))) { + err = -EINVAL ; +break ; + } +tcp_enable_tx_delay (sk, val);WRITE_ONCE (tp->tcp_tx_delay, val);break ;default :
net/ipv4/tcp_input.c +2 -2
@@ -1102,7 +1102,7 @@ static void tcp_rtt_estimator(struct sock *sk, long mrtt_us) tp->srtt_us =max (1U , srtt); } -static void tcp_update_pacing_rate (struct sock *sk) +void tcp_update_pacing_rate (struct sock *sk) {const struct tcp_sock *tp =tcp_sk (sk);u64 rate;
@@ -1139,7 +1139,7 @@ static void tcp_update_pacing_rate(struct sock *sk)/* Calculate rto without backoff. This is the second half of Van Jacobson's * routine referred to above. */ -static void tcp_set_rto (struct sock *sk) +void tcp_set_rto (struct sock *sk) {const struct tcp_sock *tp =tcp_sk (sk);/* Old crap is replaced with new one. 8)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 642/957 tcp: annotate data-races around tp->srtt_us
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 290b693ce7c9d48588d88b15a782a3efc6fa036b ]
tcp_get_timestamping_opt_stats() intentionally runs lockless, we must add READ_ONCE() and WRITE_ONCE() annotations to keep KCSAN happy.
--- net/ipv4/tcp.c | 5 +++-- net/ipv4/tcp_input.c | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-)
net/ipv4/tcp.c +3 -2
@@ -3605,7 +3605,8 @@ static void tcp_enable_tx_delay(struct sock *sk, int val)if (delta && sk->sk_state ==TCP_ESTABLISHED ) {s64 srtt = (s64 )tp->srtt_us + delta; - tp->srtt_us =clamp_t (s64, srtt,1 , ~0U ); +WRITE_ONCE (tp->srtt_us, +clamp_t (s64, srtt,1 , ~0U ));/* Note: does not deal with non zero icsk_backoff */ tcp_set_rto (sk);
@@ -4442,7 +4443,7 @@ struct sk_buff *tcp_get_timestamping_opt_stats(const struct sock *sk, READ_ONCE(tp->bytes_retrans), TCP_NLA_PAD);nla_put_u32 (stats,TCP_NLA_DSACK_DUPS ,READ_ONCE (tp->dsack_dups));nla_put_u32 (stats,TCP_NLA_REORD_SEEN ,READ_ONCE (tp->reord_seen)); -nla_put_u32 (stats,TCP_NLA_SRTT , tp->srtt_us >>3 ); +nla_put_u32 (stats,TCP_NLA_SRTT ,READ_ONCE (tp->srtt_us) >>3 );nla_put_u16 (stats,TCP_NLA_TIMEOUT_REHASH , tp->timeout_rehash);nla_put_u32 (stats,TCP_NLA_BYTES_NOTSENT ,max_t (int,0 , tp->write_seq - tp->snd_nxt));
net/ipv4/tcp_input.c +1 -1
@@ -1099,7 +1099,7 @@ static void tcp_rtt_estimator(struct sock *sk, long mrtt_us)tcp_bpf_rtt (sk, mrtt_us, srtt); } - tp->srtt_us =max (1U , srtt); +WRITE_ONCE (tp->srtt_us,max (1U , srtt)); }void tcp_update_pacing_rate (struct sock *sk)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 643/957 tcp: annotate data-races around tp->timeout_rehash
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 71c675358b711bbfd8528949249419dc2dfa4ce1 ]
tcp_get_timestamping_opt_stats() intentionally runs lockless, we must add READ_ONCE() and WRITE_ONCE() annotations to keep KCSAN happy.
--- net/ipv4/tcp.c | 3 ++- net/ipv4/tcp_timer.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-)
net/ipv4/tcp.c +2 -1
@@ -4444,7 +4444,8 @@ struct sk_buff *tcp_get_timestamping_opt_stats(const struct sock *sk,nla_put_u32 (stats,TCP_NLA_DSACK_DUPS ,READ_ONCE (tp->dsack_dups));nla_put_u32 (stats,TCP_NLA_REORD_SEEN ,READ_ONCE (tp->reord_seen));nla_put_u32 (stats,TCP_NLA_SRTT ,READ_ONCE (tp->srtt_us) >>3 ); -nla_put_u16 (stats,TCP_NLA_TIMEOUT_REHASH , tp->timeout_rehash); +nla_put_u16 (stats,TCP_NLA_TIMEOUT_REHASH , +READ_ONCE (tp->timeout_rehash));nla_put_u32 (stats,TCP_NLA_BYTES_NOTSENT ,max_t (int,0 , tp->write_seq - tp->snd_nxt)); nla_put_u64_64bit(stats, TCP_NLA_EDT, orig_skb->skb_mstamp_ns,
net/ipv4/tcp_timer.c +1 -1
@@ -296,7 +296,7 @@ static int tcp_write_timeout(struct sock *sk) }if (sk_rethink_txhash (sk)) { - tp->timeout_rehash++; +WRITE_ONCE (tp->timeout_rehash, tp->timeout_rehash +1 );__NET_INC_STATS (sock_net (sk),LINUX_MIB_TCPTIMEOUTREHASH ); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 644/957 tcp: annotate data-races around (tp->write_seq - tp->snd_nxt)
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 3a63b3d160560ef51e43fb4c880a5cde8078053c ]
tcp_get_timestamping_opt_stats() intentionally runs lockless, we must add READ_ONCE() annotations to keep KCSAN happy.
WRITE_ONCE() annotations are already present.
--- net/ipv4/tcp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
net/ipv4/tcp.c +2 -1
@@ -4447,7 +4447,8 @@ struct sk_buff *tcp_get_timestamping_opt_stats(const struct sock *sk,nla_put_u16 (stats,TCP_NLA_TIMEOUT_REHASH ,READ_ONCE (tp->timeout_rehash));nla_put_u32 (stats,TCP_NLA_BYTES_NOTSENT , -max_t (int,0 , tp->write_seq - tp->snd_nxt)); +max_t (int,0 , +READ_ONCE (tp->write_seq) -READ_ONCE (tp->snd_nxt)));nla_put_u64_64bit (stats,TCP_NLA_EDT , orig_skb->skb_mstamp_ns,TCP_NLA_PAD );if (ack_skb)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 645/957 tcp: annotate data-races around tp->plb_rehash
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 9e89b9d03a2d2e30dcca166d5af52f9a8eceab25 ]
tcp_get_timestamping_opt_stats() intentionally runs lockless, we must add READ_ONCE() and WRITE_ONCE() annotations to keep KCSAN happy.
--- net/ipv4/tcp.c | 3 ++- net/ipv4/tcp_plb.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-)
net/ipv4/tcp.c +2 -1
@@ -4455,7 +4455,8 @@ struct sk_buff *tcp_get_timestamping_opt_stats(const struct sock *sk,nla_put_u8 (stats,TCP_NLA_TTL ,tcp_skb_ttl_or_hop_limit (ack_skb)); -nla_put_u32 (stats,TCP_NLA_REHASH , tp->plb_rehash + tp->timeout_rehash); +nla_put_u32 (stats,TCP_NLA_REHASH , +READ_ONCE (tp->plb_rehash) +READ_ONCE (tp->timeout_rehash));return stats; }
net/ipv4/tcp_plb.c +1 -1
@@ -79,7 +79,7 @@ void tcp_plb_check_rehash(struct sock *sk, struct tcp_plb_state *plb)sk_rethink_txhash (sk); plb->consec_cong_rounds =0 ; -tcp_sk (sk)->plb_rehash++; +WRITE_ONCE (tcp_sk (sk)->plb_rehash,tcp_sk (sk)->plb_rehash +1 );NET_INC_STATS (sock_net (sk),LINUX_MIB_TCPPLBREHASH ); }EXPORT_SYMBOL_GPL (tcp_plb_check_rehash);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 646/957 ice: fix adjust timer programming for E830 devices
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Grzegorz Nitka <[email protected]>
[ Upstream commit 885c5e57924dc040b23d0ad0d8388f0e35772159 ]
Fix incorrect 'adjust the timer' programming sequence for E830 devices series. Only shadow registers GLTSYN_SHADJ were programmed in the current implementation. According to the specification [1], write to command GLTSYN_CMD register is also required with CMD field set to "Adjust the Time" value, for the timer adjustment to take the effect.
The flow was broken for the adjustment less than S32_MAX/MIN range (around +/- 2 seconds). For bigger adjustment, non-atomic programming flow is used, involving set timer programming. Non-atomic flow is implemented correctly.
Testing hints:
Run command:
phc_ctl /dev/ptpX get adj 2 get
Expected result:
Returned timestamps differ at least by 2 seconds
[1] Intel® Ethernet Controller E830 Datasheet rev 1.3, chapter 9.7.5.4
https://cdrdv2.intel.com/v1/dl/getContent/787353?explicitVersion=true
--- drivers/net/ethernet/intel/ice/ice_ptp_hw.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/net/ethernet/intel/ice/ice_ptp_hw.c +3 -3
@@ -5381,8 +5381,8 @@ int ice_ptp_write_incval_locked(struct ice_hw *hw, u64 incval)*/ int ice_ptp_adj_clock (struct ice_hw *hw,s32 adj) { +int err =0 ;u8 tmr_idx; -int err; tmr_idx = hw->func_caps.ts_func_info.tmr_index_owned;
@@ -5399,8 +5399,8 @@ int ice_ptp_adj_clock(struct ice_hw *hw, s32 adj) err =ice_ptp_prep_phy_adj_e810 (hw, adj);break ;case ICE_MAC_E830 : -/* E830 sync PHYs automatically after setting GLTSYN_SHADJ */ -return 0 ; +/* E830 sync PHYs automatically after setting cmd register */ +break ;case ICE_MAC_GENERIC : err =ice_ptp_prep_phy_adj_e82x (hw, adj);break ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 647/957 ice: update PCS latency settings for E825 10G/25Gb modes
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Grzegorz Nitka <[email protected]>
[ Upstream commit 05567e4052732d70c7ff9655217b3d14d25f639a ]
Update MAC Rx/Tx offset registers settings (PHY_MAC_[RX|TX]_OFFSET registers) with the data obtained with the latest research. It applies to PCS latency settings for the following speeds/modes:
* 10Gb NO-FEC
- TX latency changed from 71.25 ns to 73 ns
- RX latency changed from -25.6 ns to -28 ns
* 25Gb NO-FEC
- TX latency changed from 28.17 ns to 33 ns
- RX latency changed from -12.45 ns to -12 ns
* 25Gb RS-FEC
- TX latency changed from 64.5 ns to 69 ns
- RX latency changed from -3.6 ns to -3 ns
The original data came from simulation and pre-production hardware. The new data measures the actual delays and as such is more accurate.
--- drivers/net/ethernet/intel/ice/ice_ptp_consts.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
drivers/net/ethernet/intel/ice/ice_ptp_consts.h +6 -6
@@ -78,14 +78,14 @@ struct ice_eth56g_mac_reg_cfg eth56g_mac_cfg[NUM_ICE_ETH56G_LNK_SPD] = { .blktime = 0x666, /* 3.2 */ .tx_offset = { .serdes = 0x234c, /* 17.6484848 */ - .no_fec = 0x8e80, /* 71.25 */ + .no_fec = 0x93d9, /* 73 */ .fc = 0xb4a4, /* 90.32 */ .sfd = 0x4a4, /* 2.32 */ .onestep =0x4ccd /* 38.4 */ }, .rx_offset = { .serdes = 0xffffeb27, /* -10.42424 */ - .no_fec = 0xffffcccd, /* -25.6 */ + .no_fec = 0xffffc7b6, /* -28 */ .fc = 0xfffc557b, /* -469.26 */ .sfd =0x4a4 ,/* 2.32 */ .bs_ds = 0x32 /* 0.0969697 */
@@ -118,17 +118,17 @@ struct ice_eth56g_mac_reg_cfg eth56g_mac_cfg[NUM_ICE_ETH56G_LNK_SPD] = { .mktime = 0x147b, /* 10.24, only if RS-FEC enabled */ .tx_offset = { .serdes = 0xe1e, /* 7.0593939 */ - .no_fec = 0x3857, /* 28.17 */ + .no_fec = 0x4266, /* 33 */ .fc = 0x48c3, /* 36.38 */ - .rs = 0x8100, /* 64.5 */ + .rs = 0x8a00, /* 69 */ .sfd = 0x1dc, /* 0.93 */ .onestep =0x1eb8 /* 15.36 */ }, .rx_offset = { .serdes = 0xfffff7a9, /* -4.1697 */ - .no_fec = 0xffffe71a, /* -12.45 */ + .no_fec = 0xffffe700, /* -12 */ .fc = 0xfffe894d, /* -187.35 */ - .rs = 0xfffff8cd, /* -3.6 */ + .rs = 0xfffff8cc, /* -3 */ .sfd = 0x1dc, /* 0.93 */ .bs_ds =0x14 /* 0.0387879, RS-FEC 0 */ }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 648/957 ice: Remove jumbo_remove step from TX path
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alice Mikityanska <[email protected]>
[ Upstream commit 8b76102c5e00d1f090e0c31d17b060c76d8fa859 ]
Now that the kernel doesn't insert HBH for BIG TCP IPv6 packets, remove unnecessary steps from the ice TX path, that used to check and remove HBH.
Stable-dep-of: 1a303baa715e ("ice: fix double-free of tx_buf skb")
--- drivers/net/ethernet/intel/ice/ice_txrx.c | 3 --- 1 file changed, 3 deletions(-)
drivers/net/ethernet/intel/ice/ice_txrx.c +0 -3
@@ -2594,9 +2594,6 @@ ice_xmit_frame_ring(struct sk_buff *skb, struct ice_tx_ring *tx_ring)ice_trace (xmit_frame_ring, tx_ring, skb); -if (unlikely (ipv6_hopopt_jumbo_remove (skb))) - goto out_drop; - count =ice_xmit_desc_count (skb);if (ice_chk_linearize (skb, count)) {if (__skb_linearize (skb))
Signature · 1 line
2.53.0
Re: [PATCH 6.18 648/957] ice: Remove jumbo_remove step from TX path
Quoted 2 lines · Greg Kroah-Hartman
6.18-stable review patch. If anyone has any objections, please let me know.
Sorry for the late reply, I see it's already applied. This commit depends on "net/ipv6: Drop HBH for BIG TCP on TX side" (part of the same series [1]). As far as I see, this dependency was not applied to stable trees, so removing this step from the drivers is not correct. Either this commit should not be backported (reverted), or the entire series can be backported, if eligible. This consideration applies to all stable trees.
Thanks!
[1]: https://lore.kernel.org/netdev/[email protected]/
Quoted 40 lines
------------------
From: Alice Mikityanska <[email protected]>
[ Upstream commit
8b76102c5e00d1f090e0c31d17b060c76d8fa859]Now that the kernel doesn't insert HBH for BIG TCP IPv6 packets, remove unnecessary steps from the ice TX path, that used to check and remove HBH.
Signed-off-by Alice Mikityanska <[email protected]>Acked-by Paolo Abeni <[email protected]>Reviewed-by Eric Dumazet <[email protected]>Signed-off-by Jakub Kicinski <[email protected]>Stable-dep-of:
1a303baa715e("ice: fix double-free oftx_bufskb")Signed-off-by Sasha Levin <[email protected]>--- drivers/net/ethernet/intel/ice/ice_txrx.c | 3 --- 1 file changed, 3 deletions(-)drivers/net/ethernet/intel/ice/ice_txrx.c +0
-4@@ -2594,9 +2594,6 @@ ice_xmit_frame_ring(struct sk_buff *skb, struct ice_tx_ring *tx_ring)ice_trace (xmit_frame_ring, tx_ring, skb); -if (unlikely (ipv6_hopopt_jumbo_remove (skb))) - goto out_drop; - count =ice_xmit_desc_count (skb);if (ice_chk_linearize (skb, count)) {if (__skb_linearize (skb)) --2.53.0
Sasha Levin
maintainerRe: [PATCH 6.18 648/957] ice: Remove jumbo_remove step from TX path
Quoted 7 lines
Sorry for the late reply, I see it's already applied. This commit depends on "net/ipv6: Drop HBH for BIG TCP on TX side" (part of the same series [1]). As far as I see, this dependency was not applied to stable trees, so removing this step from the drivers is not correct. Either this commit should not be backported (reverted), or the entire series can be backported, if eligible. This consideration applies to all stable trees.
Thanks for catching this. Reverted 8b76102c5e00 on 6.18, 6.12 and 6.6.
Because 8b76102c5e00 was pulled in as a Stable-dep-of for 1a303baa715e ("ice: fix double-free of tx_buf skb"), the double-free fix was reverted on these branches as well (it would otherwise conflict). The branches now build cleanly, and 1a303baa715e can be re-backported once a version that does not depend on the jumbo_remove change is prepared.
--
Thanks,
Sasha
Greg Kroah-Hartman
maintainerPATCH 649/957 ice: fix double-free of tx_buf skb
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michal Schmidt <[email protected]>
[ Upstream commit 1a303baa715e6b78d6a406aaf335f87ff35acfcd ]
If ice_tso() or ice_tx_csum() fail, the error path in ice_xmit_frame_ring() frees the skb, but the 'first' tx_buf still points to it and is marked as valid (ICE_TX_BUF_SKB).
'next_to_use' remains unchanged, so the potential problem will likely fix itself when the next packet is transmitted and the tx_buf gets overwritten. But if there is no next packet and the interface is brought down instead, ice_clean_tx_ring() -> ice_unmap_and_free_tx_buf() will find the tx_buf and free the skb for the second time.
The fix is to reset the tx_buf type to ICE_TX_BUF_EMPTY in the error path, so that ice_unmap_and_free_tx_buf().
Move the initialization of 'first' up, to ensure it's already valid in case we hit the linearization error path.
The bug was spotted by AI while I had it looking for something else. It also proposed an initial version of the patch.
I reproduced the bug and tested the fix by adding code to inject failures, on a build with KASAN.
I looked for similar bugs in related Intel drivers and did not find any.
--- drivers/net/ethernet/intel/ice/ice_txrx.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/net/ethernet/intel/ice/ice_txrx.c +4 -2
@@ -2594,6 +2594,9 @@ ice_xmit_frame_ring(struct sk_buff *skb, struct ice_tx_ring *tx_ring)ice_trace (xmit_frame_ring, tx_ring, skb); +/* record the location of the first descriptor for this packet */ + first = &tx_ring->tx_buf[tx_ring->next_to_use]; + count =ice_xmit_desc_count (skb);if (ice_chk_linearize (skb, count)) {if (__skb_linearize (skb))
@@ -2619,8 +2622,6 @@ ice_xmit_frame_ring(struct sk_buff *skb, struct ice_tx_ring *tx_ring) offload.tx_ring = tx_ring; -/* record the location of the first descriptor for this packet */ - first = &tx_ring->tx_buf[tx_ring->next_to_use]; first->skb = skb; first->type =ICE_TX_BUF_SKB ; first->bytecount = max_t(unsigned int, skb->len, ETH_ZLEN);
@@ -2685,6 +2686,7 @@ ice_xmit_frame_ring(struct sk_buff *skb, struct ice_tx_ring *tx_ring) out_drop:ice_trace (xmit_frame_ring_drop, tx_ring, skb);dev_kfree_skb_any (skb); + first->type =ICE_TX_BUF_EMPTY ;return NETDEV_TX_OK ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 650/957 ice: fix ICE_AQ_LINK_SPEED_M for 200G
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Paul Greenwalt <[email protected]>
[ Upstream commit 4a3a940059e98539de293a6e36e464094c2e875b ]
When setting PHY configuration during driver initialization, 200G link speed is not being advertised even when the PHY is capable. This is because the get PHY capabilities link speed response is being masked by ICE_AQ_LINK_SPEED_M, which does not include the 200G link speed bit.
ICE_AQ_LINK_SPEED_200GB is defined as BIT(11), but the mask 0x7FF only covers bits 0-10. Fix ICE_AQ_LINK_SPEED_M to use GENMASK(11, 0) so that it covers all defined link speed bits including 200G.
--- drivers/net/ethernet/intel/ice/ice_adminq_cmd.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/ethernet/intel/ice/ice_adminq_cmd.h +1 -1
@@ -1252,7 +1252,7 @@ struct ice_aqc_get_link_status_data {#define ICE_AQ_LINK_PWR_QSFP_CLASS_3 2#define ICE_AQ_LINK_PWR_QSFP_CLASS_4 3__le16 link_speed; -#define ICE_AQ_LINK_SPEED_M 0x7FF +#define ICE_AQ_LINK_SPEED_M GENMASK(11, 0)#define ICE_AQ_LINK_SPEED_10MB BIT(0)#define ICE_AQ_LINK_SPEED_100MB BIT(1)#define ICE_AQ_LINK_SPEED_1000MB BIT(2)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 651/957 i40e: dont advertise IFF_SUPP_NOFCS
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kohei Enju <[email protected]>
[ Upstream commit a24162f18825684ad04e3a5d0531f8a50d679347 ]
i40e advertises IFF_SUPP_NOFCS, allowing users to use the SO_NOFCS socket option. However, this option is silently ignored, as the driver does not check skb->no_fcs, and always enables FCS insertion offload.
Fix this by removing the advertisement of IFF_SUPP_NOFCS.
This behavior can be reproduced with a simple AF_PACKET socket:
import socket
s = socket.socket(socket.AF_PACKET, socket.SOCK_RAW) s.setsockopt(socket.SOL_SOCKET, 43, 1) # SO_NOFCS s.bind(("eth0", 0))
s.send(b'\xff' * 64)
Previously, send() succeeds but the driver ignores SO_NOFCS. With this change, send() fails with -EPROTONOSUPPORT, as expected.
--- drivers/net/ethernet/intel/i40e/i40e_main.c | 1 - 1 file changed, 1 deletion(-)
drivers/net/ethernet/intel/i40e/i40e_main.c +0 -1
@@ -13784,7 +13784,6 @@ static int i40e_config_netdev(struct i40e_vsi *vsi) netdev->neigh_priv_len =sizeof (u32) *4 ; netdev->priv_flags |=IFF_UNICAST_FLT ; - netdev->priv_flags |=IFF_SUPP_NOFCS ;/* Setup netdev TC information */ i40e_vsi_config_netdev_tc (vsi, vsi->tc_config.enabled_tc);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 652/957 iavf: fix wrong VLAN mask for legacy Rx descriptors L2TAG2
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Petr Oros <[email protected]>
[ Upstream commit 496d9f91062fa07956702e0f234c5203f03a974d ]
The IAVF_RXD_LEGACY_L2TAG2_M mask was incorrectly defined as GENMASK_ULL(63, 32), extracting 32 bits from qw2 instead of the 16-bit VLAN tag. In the legacy Rx descriptor layout, the 2nd L2TAG2 (VLAN tag) occupies bits 63:48 of qw2, not 63:32.
The oversized mask causes FIELD_GET to return a 32-bit value where the actual VLAN tag sits in bits 31:16. When this value is passed to iavf_receive_skb() as a u16 parameter, it gets truncated to the lower 16 bits (which contain the 1st L2TAG2, typically zero). As a result, __vlan_hwaccel_put_tag() is never called and software VLAN interfaces on VFs receive no traffic.
This affects VFs behind ice PF (VIRTCHNL VLAN v2) when the PF advertises VLAN stripping into L2TAG2_2 and legacy descriptors are used.
The flex descriptor path already uses the correct mask (IAVF_RXD_FLEX_L2TAG2_2_M = GENMASK_ULL(63, 48)).
Reproducer:
1. Create 2 VFs on ice PF (echo 2 > sriov_numvfs)
2. Disable spoofchk on both VFs
3. Move each VF into a separate network namespace
4. On each VF: create VLAN interface (e.g. vlan 198), assign IP, bring up
5. Set rx-vlan-offload OFF on both VFs
6. Ping between VLAN interfaces -> expect PASS
(VLAN tag stays in packet data, kernel matches in-band)
7. Set rx-vlan-offload ON on both VFs
8. Ping between VLAN interfaces -> expect FAIL if bug present (HW strips VLAN tag into descriptor L2TAG2 field, wrong mask extracts bits 47:32 instead of 63:48, truncated to u16 -> zero, __vlan_hwaccel_put_tag() never called, packet delivered to parent interface, not VLAN interface)
The reproducer requires legacy Rx descriptors. On modern ice + iavf with full PTP support, flex descriptors are always negotiated and the buggy legacy path is never reached. Flex descriptors require all of:
- CONFIG_PTP_1588_CLOCK enabled
- VIRTCHNL_VF_OFFLOAD_RX_FLEX_DESC granted by PF
- PTP capabilities negotiated (VIRTCHNL_VF_CAP_PTP)
- VIRTCHNL_1588_PTP_CAP_RX_TSTAMP supported
- VIRTCHNL_RXDID_2_FLEX_SQ_NIC present in DDP profile
If any condition is not met, iavf_select_rx_desc_format() falls back to legacy descriptors (RXDID=1) and the wrong L2TAG2 mask is hit.
--- drivers/net/ethernet/intel/iavf/iavf_type.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/ethernet/intel/iavf/iavf_type.h +1 -1
@@ -277,7 +277,7 @@ struct iavf_rx_desc {/* L2 Tag 2 Presence */ #define IAVF_RXD_LEGACY_L2TAG2P_M BIT(0)/* Stripped S-TAG VLAN from the receive packet */ -#define IAVF_RXD_LEGACY_L2TAG2_M GENMASK_ULL(63, 32) +#define IAVF_RXD_LEGACY_L2TAG2_M GENMASK_ULL(63, 48)/* Stripped S-TAG VLAN from the receive packet */ #define IAVF_RXD_FLEX_L2TAG2_2_M GENMASK_ULL(63, 48)/* The packet is a UDP tunneled packet */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 653/957 e1000e: Unroll PTP in probe error handling
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Matt Vollrath <[email protected]>
[ Upstream commit aa3f7fe409350857c25d050482a2eef2cfd69b58 ]
If probe fails after registering the PTP clock and its delayed work, these resources must be released.
This was not an issue until a 2016 fix moved the e1000e_ptp_init() call before the jump to err_register.
--- drivers/net/ethernet/intel/e1000e/netdev.c | 1 + 1 file changed, 1 insertion(+)
drivers/net/ethernet/intel/e1000e/netdev.c +1 -0
@@ -7705,6 +7705,7 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent) err_register:if (!(adapter->flags &FLAG_HAS_AMT ))e1000e_release_hw_control (adapter); +e1000e_ptp_remove (adapter); err_eeprom:if (hw->phy.ops.check_reset_block && !hw->phy.ops.check_reset_block (hw))e1000_phy_hw_reset (&adapter->hw);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 654/957 ipv6: fix possible UAF in icmpv6_rcv()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit f996edd7615e686ada141b7f3395025729ff8ccb ]
Caching saddr and daddr before pskb_pull() is problematic since skb->head can change.
Remove these temporary variables:
- We only access &ipv6_hdr(skb)->saddr and &ipv6_hdr(skb)->daddr when net_dbg_ratelimited() is called in the slow path.
- Avoid potential future misuse after pskb_pull() call.
--- net/ipv6/icmp.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-)
net/ipv6/icmp.c +4 -6
@@ -904,7 +904,6 @@ static int icmpv6_rcv(struct sk_buff *skb)struct net *net =dev_net_rcu (skb->dev);struct net_device *dev =icmp6_dev (skb);struct inet6_dev *idev =__in6_dev_get (dev); -const struct in6_addr *saddr, *daddr;struct icmp6hdr *hdr;u8 type;
@@ -935,12 +934,10 @@ static int icmpv6_rcv(struct sk_buff *skb)__ICMP6_INC_STATS (dev_net_rcu (dev), idev,ICMP6_MIB_INMSGS ); - saddr = &ipv6_hdr (skb)->saddr; - daddr = &ipv6_hdr (skb)->daddr; -if (skb_checksum_validate (skb,IPPROTO_ICMPV6 , ip6_compute_pseudo)) {net_dbg_ratelimited ("ICMPv6 checksum failed [%pI6c > %pI6c]\n" , - saddr, daddr); + &ipv6_hdr (skb)->saddr, + &ipv6_hdr (skb)->daddr); goto csum_error; }
@@ -1020,7 +1017,8 @@ static int icmpv6_rcv(struct sk_buff *skb)break ;net_dbg_ratelimited ("icmpv6: msg of unknown type [%pI6c > %pI6c]\n" , - saddr, daddr); + &ipv6_hdr (skb)->saddr, + &ipv6_hdr (skb)->daddr);/* * error of unknown type.
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 655/957 sctp: fix OOB write to userspace in sctp_getsockopt_peer_auth_chunks
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
[ Upstream commit 0cf004ffb61cd32d140531c3a84afe975f9fc7ea ]
sctp_getsockopt_peer_auth_chunks() checks that the caller's optval buffer is large enough for the peer AUTH chunk list with
if (len < num_chunks)
return -EINVAL;
but then writes num_chunks bytes to p->gauth_chunks, which lives at offset offsetof(struct sctp_authchunks, gauth_chunks) == 8 inside optval. The check is missing the sizeof(struct sctp_authchunks) = 8-byte header. When the caller supplies len == num_chunks (for any num_chunks > 0) the test passes but copy_to_user() writes sizeof(struct sctp_authchunks) = 8 bytes past the declared buffer.
The sibling function sctp_getsockopt_local_auth_chunks() at the next line already has the correct check:
if (len < sizeof(struct sctp_authchunks) + num_chunks)
return -EINVAL;
Align the peer variant with its sibling.
Reproducer confirms on v7.0-13-generic: an unprivileged userspace caller that opens a loopback SCTP association with AUTH enabled, queries num_chunks with a short optval, then issues the real getsockopt with len == num_chunks and sentinel bytes painted past the buffer observes those sentinel bytes overwritten with the peer's AUTH chunk type. The bytes written are under the peer's control but land in the caller's own userspace; this is not a kernel memory corruption, but it is a kernel-side contract violation that can silently corrupt adjacent userspace data.
--- net/sctp/socket.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/sctp/socket.c +1 -1
@@ -6990,7 +6990,7 @@ static int sctp_getsockopt_peer_auth_chunks(struct sock *sk, int len,/* See if the user provided enough room for all the data */ num_chunks =ntohs (ch->param_hdr.length) -sizeof (struct sctp_paramhdr ); -if (len < num_chunks) +if (len <sizeof (struct sctp_authchunks ) + num_chunks)return -EINVAL ;if (copy_to_user (to, ch->chunks, num_chunks))
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 656/957 pppoe: drop PFC frames
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Qingfang Deng <[email protected]>
[ Upstream commit cc1ff87bce1ccd38410ab10960f576dcd17db679 ]
RFC 2516 Section 7 states that Protocol Field Compression (PFC) is NOT RECOMMENDED for PPPoE. In practice, pppd does not support negotiating PFC for PPPoE sessions, and the current PPPoE driver assumes an uncompressed (2-byte) protocol field. However, the generic PPP layer function ppp_input() is not aware of the negotiation result, and still accepts PFC frames.
If a peer with a broken implementation or an attacker sends a frame with a compressed (1-byte) protocol field, the subsequent PPP payload is shifted by one byte. This causes the network header to be 4-byte misaligned, which may trigger unaligned access exceptions on some architectures.
To reduce the attack surface, drop PPPoE PFC frames. Introduce ppp_skb_is_compressed_proto() helper function to be used in both ppp_generic.c and pppoe.c to avoid open-coding.
--- drivers/net/ppp/ppp_generic.c | 2 +- drivers/net/ppp/pppoe.c | 8 +++++++- include/linux/ppp_defs.h | 16 ++++++++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-)
drivers/net/ppp/ppp_generic.c +1 -1
@@ -2240,7 +2240,7 @@ ppp_do_recv(struct ppp *ppp, struct sk_buff *skb, struct channel *pch)*/ static void __ppp_decompress_proto (struct sk_buff *skb) { -if (skb->data[0 ] &0x01 ) +if (ppp_skb_is_compressed_proto (skb)) *(u8 *)skb_push (skb,1 ) =0x00 ; }
drivers/net/ppp/pppoe.c +7 -1
@@ -424,7 +424,7 @@ static int pppoe_rcv(struct sk_buff *skb, struct net_device *dev,if (skb_mac_header_len (skb) <ETH_HLEN ) goto drop; -if (!pskb_may_pull (skb,sizeof (struct pppoe_hdr ))) +if (!pskb_may_pull (skb,PPPOE_SES_HLEN )) goto drop; ph =pppoe_hdr (skb);
@@ -434,6 +434,12 @@ static int pppoe_rcv(struct sk_buff *skb, struct net_device *dev,if (skb->len < len) goto drop; +/* skb->data points to the PPP protocol header after skb_pull_rcsum. +* Drop PFC frames. +*/ +if (ppp_skb_is_compressed_proto (skb)) + goto drop; +if (pskb_trim_rcsum (skb, len)) goto drop;
include/linux/ppp_defs.h +16 -0
@@ -8,6 +8,7 @@#define _PPP_DEFS_H_#include <linux/crc-ccitt.h> +#include <linux/skbuff.h> #include <uapi/linux/ppp_defs.h> #define PPP_FCS (fcs, c) crc_ccitt_byte(fcs, c)
@@ -25,4 +26,19 @@ static inline bool ppp_proto_is_valid(u16 proto) return !!((proto & 0x0101) == 0x0001); } +/** +* ppp_skb_is_compressed_proto - checks if PPP protocol in a skb is compressed +* @skb: skb to check +* +* Check if the PPP protocol field is compressed (the least significant +* bit of the most significant octet is 1). skb->data must point to the PPP +* protocol header. +* +* Return: Whether the PPP protocol field is compressed. +*/ +static inline bool ppp_skb_is_compressed_proto (const struct sk_buff *skb) +{ +return unlikely (skb->data[0 ] &0x01 ); +} +#endif /* _PPP_DEFS_H_ */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 657/957 net/mlx5: Fix HCA caps leak on notifier init failure
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Prathamesh Deshpande <[email protected]>
[ Upstream commit d03fc81a57956248383efec99967d0ae627390a8 ]
mlx5_mdev_init() allocates HCA caps via mlx5_hca_caps_alloc() before calling mlx5_notifiers_init(). If notifier initialization fails, the error path jumps to err_hca_caps and skips mlx5_hca_caps_free(), leaking allocated caps.
Add a dedicated unwind label for notifier-init failure that frees HCA caps before continuing the existing cleanup sequence.
--- drivers/net/ethernet/mellanox/mlx5/core/main.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/net/ethernet/mellanox/mlx5/core/main.c +3 -1
@@ -1878,7 +1878,7 @@ int mlx5_mdev_init(struct mlx5_core_dev *dev, int profile_idx) err =mlx5_notifiers_init (dev);if (err) - goto err_hca_caps; + goto err_notifiers_init;/* The conjunction of sw_vhca_id with sw_owner_id will be a global * unique id per function which uses mlx5_core.
@@ -1894,6 +1894,8 @@ int mlx5_mdev_init(struct mlx5_core_dev *dev, int profile_idx)return 0 ; +err_notifiers_init: +mlx5_hca_caps_free (dev); err_hca_caps:mlx5_adev_cleanup (dev); err_adev_init:
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 658/957 openvswitch: cap upcall PID array size and pre-size vport replies
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Weiming Shi <[email protected]>
[ Upstream commit 2091c6aa0df6aba47deb5c8ab232b1cb60af3519 ]
The vport netlink reply helpers allocate a fixed-size skb with nlmsg_new(NLMSG_DEFAULT_SIZE, ...) but serialize the full upcall PID array via ovs_vport_get_upcall_portids(). Since ovs_vport_set_upcall_portids() accepts any non-zero multiple of sizeof(u32) with no upper bound, a CAP_NET_ADMIN user can install a PID array large enough to overflow the reply buffer, causing nla_put() to fail with -EMSGSIZE and hitting BUG_ON(err < 0). On systems with unprivileged user namespaces enabled (e.g., Ubuntu default), this is reachable via unshare -Urn since OVS vport mutation operations use GENL_UNS_ADMIN_PERM.
kernel BUG at net/openvswitch/datapath.c:2414!
Oops: invalid opcode: 0000 [#1] SMP KASAN NOPTI CPU: 1 UID: 0 PID: 65 Comm: poc Not tainted 7.0.0-rc7-00195-geb216e422044 #1 RIP: 0010:ovs_vport_cmd_set+0x34c/0x400 Call Trace: <TASK> genl_family_rcv_msg_doit (net/netlink/genetlink.c:1116) genl_rcv_msg (net/netlink/genetlink.c:1194) netlink_rcv_skb (net/netlink/af_netlink.c:2550) genl_rcv (net/netlink/genetlink.c:1219) netlink_unicast (net/netlink/af_netlink.c:1344) netlink_sendmsg (net/netlink/af_netlink.c:1894) __sys_sendto (net/socket.c:2206) __x64_sys_sendto (net/socket.c:2209) do_syscall_64 (arch/x86/entry/syscall_64.c:63) entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:130) </TASK> Kernel panic - not syncing: Fatal exception
Reject attempts to set more PIDs than nr_cpu_ids in ovs_vport_set_upcall_portids(), and pre-compute the worst-case reply size in ovs_vport_cmd_msg_size() based on that bound, similar to the existing ovs_dp_cmd_msg_size(). nr_cpu_ids matches the cap already used by the per-CPU dispatch configuration on the datapath side (ovs_dp_cmd_fill_info() serialises at most nr_cpu_ids PIDs), so the two sides stay consistent.
--- net/openvswitch/datapath.c | 35 +++++++++++++++++++++++++++++++++-- net/openvswitch/vport.c | 3 +++ 2 files changed, 36 insertions(+), 2 deletions(-)
net/openvswitch/datapath.c +33 -2
@@ -2186,9 +2186,40 @@ static int ovs_vport_cmd_fill_info(struct vport *vport, struct sk_buff *skb,return err; } +static size_t ovs_vport_cmd_msg_size (void ) +{ +size_t msgsize =NLMSG_ALIGN (sizeof (struct ovs_header )); + + msgsize +=nla_total_size (sizeof (u32));/* OVS_VPORT_ATTR_PORT_NO */ + msgsize +=nla_total_size (sizeof (u32));/* OVS_VPORT_ATTR_TYPE */ + msgsize +=nla_total_size (IFNAMSIZ );/* OVS_VPORT_ATTR_NAME */ + msgsize +=nla_total_size (sizeof (u32));/* OVS_VPORT_ATTR_IFINDEX */ + msgsize +=nla_total_size (sizeof (s32));/* OVS_VPORT_ATTR_NETNSID */ + +/* OVS_VPORT_ATTR_STATS */ + msgsize +=nla_total_size_64bit (sizeof (struct ovs_vport_stats )); + +/* OVS_VPORT_ATTR_UPCALL_STATS(OVS_VPORT_UPCALL_ATTR_SUCCESS + +* OVS_VPORT_UPCALL_ATTR_FAIL) +*/ + msgsize +=nla_total_size (nla_total_size_64bit (sizeof (u64)) + +nla_total_size_64bit (sizeof (u64))); + +/* OVS_VPORT_ATTR_UPCALL_PID */ + msgsize +=nla_total_size (nr_cpu_ids *sizeof (u32)); + +/* OVS_VPORT_ATTR_OPTIONS(OVS_TUNNEL_ATTR_DST_PORT + +* OVS_TUNNEL_ATTR_EXTENSION(OVS_VXLAN_EXT_GBP)) +*/ + msgsize +=nla_total_size (nla_total_size (sizeof (u16)) + +nla_total_size (nla_total_size (0 ))); + +return msgsize; +} +static struct sk_buff *ovs_vport_cmd_alloc_info (void ) { -return nlmsg_new (NLMSG_DEFAULT_SIZE ,GFP_KERNEL ); +return genlmsg_new (ovs_vport_cmd_msg_size (),GFP_KERNEL ); }/* Called with ovs_mutex, only via ovs_dp_notify_wq(). */
@@ -2198,7 +2229,7 @@ struct sk_buff *ovs_vport_cmd_build_info(struct vport *vport, struct net *net,struct sk_buff *skb;int retval; - skb =nlmsg_new (NLMSG_DEFAULT_SIZE ,GFP_KERNEL ); + skb =ovs_vport_cmd_alloc_info ();if (!skb)return ERR_PTR (-ENOMEM );
net/openvswitch/vport.c +3 -0
@@ -407,6 +407,9 @@ int ovs_vport_set_upcall_portids(struct vport *vport, const struct nlattr *ids)if (!nla_len (ids) ||nla_len (ids) %sizeof (u32))return -EINVAL ; +if (nla_len (ids) /sizeof (u32) > nr_cpu_ids) +return -EINVAL ; + old =ovsl_dereference (vport->upcall_portids); vport_portids = kmalloc(sizeof(*vport_portids) + nla_len(ids),
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 659/957 net: airoha: Fix possible TX queue stall in airoha_qdma_tx_napi_poll()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lorenzo Bianconi <[email protected]>
[ Upstream commit b94769eb2f30e61e86cd8551c084c34134290d89 ]
Since multiple net_device TX queues can share the same hw QDMA TX queue, there is no guarantee we have inflight packets queued in hw belonging to a net_device TX queue stopped in the xmit path because hw QDMA TX queue can be full. In this corner case the net_device TX queue will never be re-activated. In order to avoid any potential net_device TX queue stall, we need to wake all the net_device TX queues feeding the same hw QDMA TX queue in airoha_qdma_tx_napi_poll routine.
--- drivers/net/ethernet/airoha/airoha_eth.c | 37 ++++++++++++++++++++---- drivers/net/ethernet/airoha/airoha_eth.h | 1 + 2 files changed, 33 insertions(+), 5 deletions(-)
drivers/net/ethernet/airoha/airoha_eth.c +32 -5
@@ -874,6 +874,21 @@ static int airoha_qdma_init_rx(struct airoha_qdma *qdma)return 0 ; } +static void airoha_qdma_wake_netdev_txqs (struct airoha_queue *q) +{ +struct airoha_qdma *qdma = q->qdma; +struct airoha_eth *eth = qdma->eth; +int i; + +for (i =0 ; i <ARRAY_SIZE (eth->ports); i++) { +struct airoha_gdm_port *port = eth->ports[i]; + +if (port && port->qdma == qdma) +netif_tx_wake_all_queues (port->dev); + } + q->txq_stopped = false; +} +static int airoha_qdma_tx_napi_poll (struct napi_struct *napi,int budget) {struct airoha_tx_irq_queue *irq_q;
@@ -956,12 +971,21 @@ static int airoha_qdma_tx_napi_poll(struct napi_struct *napi, int budget) txq =netdev_get_tx_queue (skb->dev, queue);netdev_tx_completed_queue (txq,1 , skb->len); -if (netif_tx_queue_stopped (txq) && - q->ndesc - q->queued >= q->free_thr) -netif_tx_wake_queue (txq); -dev_kfree_skb_any (skb); } + +if (q->txq_stopped && q->ndesc - q->queued >= q->free_thr) { +/* Since multiple net_device TX queues can share the +* same hw QDMA TX queue, there is no guarantee we have +* inflight packets queued in hw belonging to a +* net_device TX queue stopped in the xmit path. +* In order to avoid any potential net_device TX queue +* stall, we need to wake all the net_device TX queues +* feeding the same hw QDMA TX queue. +*/ +airoha_qdma_wake_netdev_txqs (q); + } + unlock:spin_unlock_bh (&q->lock); }
@@ -1978,6 +2002,7 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,if (airoha_dev_tx_queue_busy (q, nr_frags)) {/* not enough space in the queue */ netif_tx_stop_queue (txq); + q->txq_stopped = true;spin_unlock_bh (&q->lock);return NETDEV_TX_BUSY ; }
@@ -2030,8 +2055,10 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,TX_RING_CPU_IDX_MASK , FIELD_PREP(TX_RING_CPU_IDX_MASK, q->head)); -if (q->ndesc - q->queued < q->free_thr) +if (q->ndesc - q->queued < q->free_thr) {netif_tx_stop_queue (txq); + q->txq_stopped = true; + }spin_unlock_bh (&q->lock);
drivers/net/ethernet/airoha/airoha_eth.h +1 -0
@@ -181,6 +181,7 @@ struct airoha_queue {int ndesc;int free_thr;int buf_size; +bool txq_stopped;struct napi_struct napi;struct page_pool *page_pool;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 660/957 netfilter: nft_osf: restrict it to ipv4
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pablo Neira Ayuso <[email protected]>
[ Upstream commit b336fdbb7103fb1484e1dcb6741151d4b5a41e35 ]
This expression only supports for ipv4, restrict it.
--- net/netfilter/nft_osf.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
net/netfilter/nft_osf.c +5 -1
@@ -28,6 +28,11 @@ static void nft_osf_eval(const struct nft_expr *expr, struct nft_regs *regs,struct nf_osf_data data;struct tcphdr _tcph; +if (nft_pf (pkt) !=NFPROTO_IPV4 ) { + regs->verdict.code =NFT_BREAK ; +return ; + } +if (pkt->tprot !=IPPROTO_TCP ) { regs->verdict.code =NFT_BREAK ;return ;
@@ -114,7 +119,6 @@ static int nft_osf_validate(const struct nft_ctx *ctx,switch (ctx->family) {case NFPROTO_IPV4 : -case NFPROTO_IPV6 :case NFPROTO_INET : hooks = (1 <<NF_INET_LOCAL_IN ) | (1 << NF_INET_PRE_ROUTING) |
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 661/957 netfilter: nfnetlink_osf: fix divide-by-zero in OSF_WSS_MODULO
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xiang Mei <[email protected]>
[ Upstream commit 2195574dc6d9017d32ac346987e12659f931d932 ]
nf_osf_match_one() computes ctx->window % f->wss.val in the OSF_WSS_MODULO branch with no guard for f->wss.val == 0. A CAP_NET_ADMIN user can add such a fingerprint via nfnetlink; a subsequent matching TCP SYN divides by zero and panics the kernel.
Reject the bogus fingerprint in nfnl_osf_add_callback() above the per-option for-loop. f->wss is per-fingerprint, not per-option, so the check must run regardless of f->opt_num (including 0). Also reject wss.wc >= OSF_WSS_MAX; nf_osf_match_one() already treats that as "should not happen".
Crash:
Oops: divide error: 0000 [#1] SMP KASAN NOPTI RIP: 0010:nf_osf_match_one (net/netfilter/nfnetlink_osf.c:98) Call Trace: <IRQ> nf_osf_match (net/netfilter/nfnetlink_osf.c:220) xt_osf_match_packet (net/netfilter/xt_osf.c:32) ipt_do_table (net/ipv4/netfilter/ip_tables.c:348) nf_hook_slow (net/netfilter/core.c:622) ip_local_deliver (net/ipv4/ip_input.c:265) ip_rcv (include/linux/skbuff.h:1162) __netif_receive_skb_one_core (net/core/dev.c:6181) process_backlog (net/core/dev.c:6642) __napi_poll (net/core/dev.c:7710) net_rx_action (net/core/dev.c:7945) handle_softirqs (kernel/softirq.c:622)
--- net/netfilter/nfnetlink_osf.c | 4 ++++ 1 file changed, 4 insertions(+)
net/netfilter/nfnetlink_osf.c +4 -0
@@ -320,6 +320,10 @@ static int nfnl_osf_add_callback(struct sk_buff *skb,if (f->opt_num >ARRAY_SIZE (f->opt))return -EINVAL ; +if (f->wss.wc >=OSF_WSS_MAX || + (f->wss.wc ==OSF_WSS_MODULO && f->wss.val ==0 )) +return -EINVAL ; +for (i =0 ; i < f->opt_num; i++) {if (!f->opt[i].length || f->opt[i].length >MAX_IPOPTLEN )return -EINVAL ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 662/957 netfilter: conntrack: remove sprintf usage
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Florian Westphal <[email protected]>
[ Upstream commit 6e7066bdb481a87fe88c4fa563e348c03b2d373d ]
Replace it with scnprintf, the buffer sizes are expected to be large enough to hold the result, no need for snprintf+overflow check.
Increase buffer size in mangle_content_len() while at it.
BUG: KASAN: stack-out-of-bounds in vsnprintf+0xea5/0x1270 Write of size 1 at addr [..] vsnprintf+0xea5/0x1270 sprintf+0xb1/0xe0 mangle_content_len+0x1ac/0x280 nf_nat_sdp_session+0x1cc/0x240 process_sdp+0x8f8/0xb80 process_invite_request+0x108/0x2b0 process_sip_msg+0x5da/0xf50 sip_help_tcp+0x45e/0x780 nf_confirm+0x34d/0x990 [..]
--- net/netfilter/nf_nat_amanda.c | 2 +- net/netfilter/nf_nat_sip.c | 33 ++++++++++++++++++--------------- 2 files changed, 19 insertions(+), 16 deletions(-)
net/netfilter/nf_nat_amanda.c +1 -1
@@ -50,7 +50,7 @@ static unsigned int help(struct sk_buff *skb,return NF_DROP ; } -sprintf (buffer,"%u" , port); +snprintf (buffer,sizeof (buffer),"%u" , port);if (!nf_nat_mangle_udp_packet (skb, exp->master, ctinfo, protoff, matchoff, matchlen, buffer,strlen (buffer))) {
net/netfilter/nf_nat_sip.c +18 -15
@@ -68,25 +68,27 @@ static unsigned int mangle_packet(struct sk_buff *skb, unsigned int protoff, }static int sip_sprintf_addr (const struct nf_conn *ct,char *buffer, +size_t size,const union nf_inet_addr *addr,bool delim) {if (nf_ct_l3num (ct) ==NFPROTO_IPV4 ) -return sprintf (buffer,"%pI4" , &addr->ip); +return scnprintf (buffer, size,"%pI4" , &addr->ip);else {if (delim) -return sprintf (buffer,"[%pI6c]" , &addr->ip6); +return scnprintf (buffer, size,"[%pI6c]" , &addr->ip6);else -return sprintf (buffer,"%pI6c" , &addr->ip6); +return scnprintf (buffer, size,"%pI6c" , &addr->ip6); } }static int sip_sprintf_addr_port (const struct nf_conn *ct,char *buffer, +size_t size,const union nf_inet_addr *addr,u16 port) {if (nf_ct_l3num (ct) ==NFPROTO_IPV4 ) -return sprintf (buffer,"%pI4:%u" , &addr->ip, port); +return scnprintf (buffer, size,"%pI4:%u" , &addr->ip, port);else -return sprintf (buffer,"[%pI6c]:%u" , &addr->ip6, port); +return scnprintf (buffer, size,"[%pI6c]:%u" , &addr->ip6, port); } static int map_addr(struct sk_buff *skb, unsigned int protoff,
@@ -119,7 +121,7 @@ static int map_addr(struct sk_buff *skb, unsigned int protoff,if (nf_inet_addr_cmp (&newaddr, addr) && newport == port)return 1 ; - buflen =sip_sprintf_addr_port (ct, buffer, &newaddr,ntohs (newport)); + buflen =sip_sprintf_addr_port (ct, buffer,sizeof (buffer), &newaddr,ntohs (newport));return mangle_packet (skb, protoff, dataoff, dptr, datalen, matchoff, matchlen, buffer, buflen); }
@@ -212,7 +214,7 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff, &addr, true) > 0 && nf_inet_addr_cmp(&addr, &ct->tuplehash[dir].tuple.src.u3) && !nf_inet_addr_cmp(&addr, &ct->tuplehash[!dir].tuple.dst.u3)) { - buflen = sip_sprintf_addr(ct, buffer, + buflen = sip_sprintf_addr(ct, buffer, sizeof(buffer), &ct->tuplehash[!dir].tuple.dst.u3, true); if (!mangle_packet(skb, protoff, dataoff, dptr, datalen,
@@ -229,7 +231,7 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff, &addr, false) > 0 && nf_inet_addr_cmp(&addr, &ct->tuplehash[dir].tuple.dst.u3) && !nf_inet_addr_cmp(&addr, &ct->tuplehash[!dir].tuple.src.u3)) { - buflen = sip_sprintf_addr(ct, buffer, + buflen = sip_sprintf_addr(ct, buffer, sizeof(buffer), &ct->tuplehash[!dir].tuple.src.u3, false); if (!mangle_packet(skb, protoff, dataoff, dptr, datalen,
@@ -247,7 +249,7 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff, htons(n) == ct->tuplehash[dir].tuple.dst.u.udp.port && htons(n) != ct->tuplehash[!dir].tuple.src.u.udp.port) {__be16 p = ct->tuplehash[!dir].tuple.src.u.udp.port; - buflen =sprintf (buffer,"%u" ,ntohs (p)); + buflen =scnprintf (buffer,sizeof (buffer),"%u" ,ntohs (p));if (!mangle_packet (skb, protoff, dataoff, dptr, datalen, poff, plen, buffer, buflen)) {nf_ct_helper_log (skb, ct,"cannot mangle rport" );
@@ -418,7 +420,8 @@ static unsigned int nf_nat_sip_expect(struct sk_buff *skb, unsigned int protoff,if (!nf_inet_addr_cmp (&exp->tuple.dst.u3, &exp->saved_addr) || exp->tuple.dst.u.udp.port != exp->saved_proto.udp.port) { - buflen =sip_sprintf_addr_port (ct, buffer, &newaddr, port); + buflen =sip_sprintf_addr_port (ct, buffer,sizeof (buffer), + &newaddr, port);if (!mangle_packet (skb, protoff, dataoff, dptr, datalen, matchoff, matchlen, buffer, buflen)) {nf_ct_helper_log (skb, ct,"cannot mangle packet" );
@@ -438,8 +441,8 @@ static int mangle_content_len(struct sk_buff *skb, unsigned int protoff, {enum ip_conntrack_info ctinfo;struct nf_conn *ct =nf_ct_get (skb, &ctinfo); +char buffer[sizeof ("4294967295" )];unsigned int matchoff, matchlen; -char buffer[sizeof ("65536" )];int buflen, c_len;/* Get actual SDP length */
@@ -454,7 +457,7 @@ static int mangle_content_len(struct sk_buff *skb, unsigned int protoff, &matchoff, &matchlen) <= 0)return 0 ; - buflen =sprintf (buffer,"%u" , c_len); + buflen =scnprintf (buffer,sizeof (buffer),"%u" , c_len);return mangle_packet (skb, protoff, dataoff, dptr, datalen, matchoff, matchlen, buffer, buflen); }
@@ -491,7 +494,7 @@ static unsigned int nf_nat_sdp_addr(struct sk_buff *skb, unsigned int protoff,char buffer[INET6_ADDRSTRLEN ];unsigned int buflen; - buflen =sip_sprintf_addr (ct, buffer, addr, false); + buflen =sip_sprintf_addr (ct, buffer,sizeof (buffer), addr, false);if (mangle_sdp_packet (skb, protoff, dataoff, dptr, datalen, sdpoff, type, term, buffer, buflen))return 0 ;
@@ -509,7 +512,7 @@ static unsigned int nf_nat_sdp_port(struct sk_buff *skb, unsigned int protoff,char buffer[sizeof ("nnnnn" )];unsigned int buflen; - buflen =sprintf (buffer,"%u" , port); + buflen =scnprintf (buffer,sizeof (buffer),"%u" , port);if (!mangle_packet (skb, protoff, dataoff, dptr, datalen, matchoff, matchlen, buffer, buflen))return 0 ;
@@ -529,7 +532,7 @@ static unsigned int nf_nat_sdp_session(struct sk_buff *skb, unsigned int protoffunsigned int buflen;/* Mangle session description owner and contact addresses */ - buflen =sip_sprintf_addr (ct, buffer, addr, false); + buflen =sip_sprintf_addr (ct, buffer,sizeof (buffer), addr, false);if (mangle_sdp_packet (skb, protoff, dataoff, dptr, datalen, sdpoff,SDP_HDR_OWNER ,SDP_HDR_MEDIA , buffer, buflen))return 0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 663/957 netfilter: xtables: restrict several matches to inet family
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pablo Neira Ayuso <[email protected]>
[ Upstream commit b6fe26f86a1649f84e057f3f15605b08eda15497 ]
This is a partial revert of:
commit ab4f21e6fb1c ("netfilter: xtables: use NFPROTO_UNSPEC in more extensions")
to allow ipv4 and ipv6 only.
- xt_mac
- xt_owner
- xt_physdev
These extensions are not used by ebtables in userspace.
Moreover, xt_realm is only for ipv4, since dst->tclassid is ipv4 specific.
--- net/netfilter/xt_mac.c | 34 +++++++++++++++++++++++----------- net/netfilter/xt_owner.c | 37 +++++++++++++++++++++++++------------ net/netfilter/xt_physdev.c | 29 +++++++++++++++++++---------- net/netfilter/xt_realm.c | 2 +- 4 files changed, 68 insertions(+), 34 deletions(-)
net/netfilter/xt_mac.c +23 -11
@@ -38,25 +38,37 @@ static bool mac_mt(const struct sk_buff *skb, struct xt_action_param *par)return ret; } -static struct xt_match mac_mt_reg__read_mostly = { - .name ="mac" , - .revision =0 , - .family =NFPROTO_UNSPEC , - .match = mac_mt, - .matchsize =sizeof (struct xt_mac_info ), - .hooks = (1 <<NF_INET_PRE_ROUTING ) | (1 <<NF_INET_LOCAL_IN ) | - (1 <<NF_INET_FORWARD ), - .me =THIS_MODULE , +static struct xt_match mac_mt_reg[]__read_mostly = { + { + .name ="mac" , + .family =NFPROTO_IPV4 , + .match = mac_mt, + .matchsize =sizeof (struct xt_mac_info ), + .hooks = (1 <<NF_INET_PRE_ROUTING ) | + (1 <<NF_INET_LOCAL_IN ) | + (1 <<NF_INET_FORWARD ), + .me =THIS_MODULE , + }, + { + .name ="mac" , + .family =NFPROTO_IPV6 , + .match = mac_mt, + .matchsize =sizeof (struct xt_mac_info ), + .hooks = (1 <<NF_INET_PRE_ROUTING ) | + (1 <<NF_INET_LOCAL_IN ) | + (1 <<NF_INET_FORWARD ), + .me =THIS_MODULE , + }, };static int __init mac_mt_init (void ) { -return xt_register_match (&mac_mt_reg); +return xt_register_matches (mac_mt_reg,ARRAY_SIZE (mac_mt_reg)); } static void __exit mac_mt_exit(void) { -xt_unregister_match (&mac_mt_reg); +xt_unregister_matches (mac_mt_reg,ARRAY_SIZE (mac_mt_reg)); }module_init (mac_mt_init);
net/netfilter/xt_owner.c +25 -12
@@ -127,26 +127,39 @@ owner_mt(const struct sk_buff *skb, struct xt_action_param *par)return true; } -static struct xt_match owner_mt_reg__read_mostly = { - .name ="owner" , - .revision =1 , - .family =NFPROTO_UNSPEC , - .checkentry = owner_check, - .match = owner_mt, - .matchsize =sizeof (struct xt_owner_match_info ), - .hooks = (1 <<NF_INET_LOCAL_OUT ) | - (1 <<NF_INET_POST_ROUTING ), - .me =THIS_MODULE , +static struct xt_match owner_mt_reg[]__read_mostly = { + { + .name ="owner" , + .revision =1 , + .family =NFPROTO_IPV4 , + .checkentry = owner_check, + .match = owner_mt, + .matchsize =sizeof (struct xt_owner_match_info ), + .hooks = (1 <<NF_INET_LOCAL_OUT ) | + (1 <<NF_INET_POST_ROUTING ), + .me =THIS_MODULE , + }, + { + .name ="owner" , + .revision =1 , + .family =NFPROTO_IPV6 , + .checkentry = owner_check, + .match = owner_mt, + .matchsize =sizeof (struct xt_owner_match_info ), + .hooks = (1 <<NF_INET_LOCAL_OUT ) | + (1 <<NF_INET_POST_ROUTING ), + .me =THIS_MODULE , + } };static int __init owner_mt_init (void ) { -return xt_register_match (&owner_mt_reg); +return xt_register_matches (owner_mt_reg,ARRAY_SIZE (owner_mt_reg)); } static void __exit owner_mt_exit(void) { -xt_unregister_match (&owner_mt_reg); +xt_unregister_matches (owner_mt_reg,ARRAY_SIZE (owner_mt_reg)); }module_init (owner_mt_init);
net/netfilter/xt_physdev.c +19 -10
@@ -115,24 +115,33 @@ static int physdev_mt_check(const struct xt_mtchk_param *par)return 0 ; } -static struct xt_match physdev_mt_reg__read_mostly = { - .name ="physdev" , - .revision =0 , - .family =NFPROTO_UNSPEC , - .checkentry = physdev_mt_check, - .match = physdev_mt, - .matchsize =sizeof (struct xt_physdev_info ), - .me =THIS_MODULE , +static struct xt_match physdev_mt_reg[]__read_mostly = { + { + .name ="physdev" , + .family =NFPROTO_IPV4 , + .checkentry = physdev_mt_check, + .match = physdev_mt, + .matchsize =sizeof (struct xt_physdev_info ), + .me =THIS_MODULE , + }, + { + .name ="physdev" , + .family =NFPROTO_IPV6 , + .checkentry = physdev_mt_check, + .match = physdev_mt, + .matchsize =sizeof (struct xt_physdev_info ), + .me =THIS_MODULE , + }, };static int __init physdev_mt_init (void ) { -return xt_register_match (&physdev_mt_reg); +return xt_register_matches (physdev_mt_reg,ARRAY_SIZE (physdev_mt_reg)); } static void __exit physdev_mt_exit(void) { -xt_unregister_match (&physdev_mt_reg); +xt_unregister_matches (physdev_mt_reg,ARRAY_SIZE (physdev_mt_reg)); }module_init (physdev_mt_init);
net/netfilter/xt_realm.c +1 -1
@@ -33,7 +33,7 @@ static struct xt_match realm_mt_reg __read_mostly = { .matchsize = sizeof(struct xt_realm_info), .hooks = (1 <<NF_INET_POST_ROUTING ) | (1 <<NF_INET_FORWARD ) | (1 << NF_INET_LOCAL_OUT) | (1 << NF_INET_LOCAL_IN), - .family = NFPROTO_UNSPEC, + .family = NFPROTO_IPV4, .me =THIS_MODULE };
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 664/957 netfilter: nat: use kfree_rcu to release ops
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pablo Neira Ayuso <[email protected]>
[ Upstream commit 6eda0d771f94267f73f57c94630aa47e90957915 ]
Florian Westphal says:
"Historically this is not an issue, even for normal base hooks: the data path doesn't use the original nf_hook_ops that are used to register the callbacks.
However, in v5.14 I added the ability to dump the active netfilter hooks from userspace.
This code will peek back into the nf_hook_ops that are available at the tail of the pointer-array blob used by the datapath.
The nat hooks are special, because they are called indirectly from the central nat dispatcher hook. They are currently invisible to the nfnl hook dump subsystem though.
But once that changes the nat ops structures have to be deferred too."
Update nf_nat_register_fn() to deal with partial exposition of the hooks from error path which can be also an issue for nfnetlink_hook.
--- net/ipv4/netfilter/iptable_nat.c | 4 ++-- net/ipv6/netfilter/ip6table_nat.c | 4 ++-- net/netfilter/nf_nat_core.c | 10 ++++++---- 3 files changed, 10 insertions(+), 8 deletions(-)
net/ipv4/netfilter/iptable_nat.c +2 -2
@@ -79,7 +79,7 @@ static int ipt_nat_register_lookups(struct net *net)while (i)nf_nat_ipv4_unregister_fn (net, &ops[--i]); -kfree (ops); +kfree_rcu (ops, rcu);return ret; } }
@@ -100,7 +100,7 @@ static void ipt_nat_unregister_lookups(struct net *net)for (i =0 ; i <ARRAY_SIZE (nf_nat_ipv4_ops); i++)nf_nat_ipv4_unregister_fn (net, &ops[i]); -kfree (ops); +kfree_rcu (ops, rcu); }static int iptable_nat_table_init (struct net *net)
net/ipv6/netfilter/ip6table_nat.c +2 -2
@@ -81,7 +81,7 @@ static int ip6t_nat_register_lookups(struct net *net)while (i)nf_nat_ipv6_unregister_fn (net, &ops[--i]); -kfree (ops); +kfree_rcu (ops, rcu);return ret; } }
@@ -102,7 +102,7 @@ static void ip6t_nat_unregister_lookups(struct net *net)for (i =0 ; i <ARRAY_SIZE (nf_nat_ipv6_ops); i++)nf_nat_ipv6_unregister_fn (net, &ops[i]); -kfree (ops); +kfree_rcu (ops, rcu); }static int ip6table_nat_table_init (struct net *net)
net/netfilter/nf_nat_core.c +6 -4
@@ -1228,9 +1228,11 @@ int nf_nat_register_fn(struct net *net, u8 pf, const struct nf_hook_ops *ops, ret =nf_register_net_hooks (net, nat_ops, ops_count);if (ret <0 ) {mutex_unlock (&nf_nat_proto_mutex); -for (i =0 ; i < ops_count; i++) -kfree (nat_ops[i].priv); -kfree (nat_ops); +for (i =0 ; i < ops_count; i++) { + priv = nat_ops[i].priv; +kfree_rcu (priv, rcu_head); + } +kfree_rcu (nat_ops, rcu);return ret; }
@@ -1294,7 +1296,7 @@ void nf_nat_unregister_fn(struct net *net, u8 pf, const struct nf_hook_ops *ops, } nat_proto_net->nat_hook_ops =NULL ; -kfree (nat_ops); +kfree_rcu (nat_ops, rcu); } unlock:mutex_unlock (&nf_nat_proto_mutex);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 665/957 ipvs: fix MTU check for GSO packets in tunnel mode
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yingnan Zhang <[email protected]>
[ Upstream commit 67bf42cae41d847fd6e5749eb68278ca5d748b25 ]
Currently, IPVS skips MTU checks for GSO packets by excluding them with the !skb_is_gso(skb) condition. This creates problems when IPVS tunnel mode encapsulates GSO packets with IPIP headers.
The issue manifests in two ways:
1. MTU violation after encapsulation:
When a GSO packet passes through IPVS tunnel mode, the original MTU check is bypassed. After adding the IPIP tunnel header, the packet size may exceed the outgoing interface MTU, leading to unexpected fragmentation at the IP layer.
2. Fragmentation with problematic IP IDs:
When net.ipv4.vs.pmtu_disc=1 and a GSO packet with multiple segments is fragmented after encapsulation, each segment gets a sequentially incremented IP ID (0, 1, 2, ...). This happens because:
a) The GSO packet bypasses MTU check and gets encapsulated
b) At __ip_finish_output, the oversized GSO packet is split into separate SKBs (one per segment), with IP IDs incrementing
c) Each SKB is then fragmented again based on the actual MTU
This sequential IP ID allocation differs from the expected behavior and can cause issues with fragment reassembly and packet tracking.
Fix this by properly validating GSO packets using skb_gso_validate_network_len(). This function correctly validates whether the GSO segments will fit within the MTU after segmentation. If validation fails, send an ICMP Fragmentation Needed message to enable proper PMTU discovery.
--- net/netfilter/ipvs/ip_vs_xmit.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-)
net/netfilter/ipvs/ip_vs_xmit.c +15 -4
@@ -103,6 +103,18 @@ __ip_vs_dst_check(struct ip_vs_dest *dest)return dest_dst; } +/* Based on ip_exceeds_mtu(). */ +static bool ip_vs_exceeds_mtu (const struct sk_buff *skb,unsigned int mtu) +{ +if (skb->len <= mtu) +return false; + +if (skb_is_gso (skb) &&skb_gso_validate_network_len (skb, mtu)) +return false; + +return true; +} +static inline bool __mtu_check_toobig_v6 (const struct sk_buff *skb,u32 mtu) {
@@ -112,10 +124,9 @@ __mtu_check_toobig_v6(const struct sk_buff *skb, u32 mtu)*/ if (IP6CB (skb)->frag_max_size > mtu)return true;/* largest fragment violate MTU */ - } -else if (skb->len > mtu && !skb_is_gso (skb)) { + }else if (ip_vs_exceeds_mtu (skb, mtu))return true;/* Packet size violate MTU size */ - } +return false; }
@@ -233,7 +244,7 @@ static inline bool ensure_mtu_is_adequate(struct netns_ipvs *ipvs, int skb_af,return true;if (unlikely (ip_hdr (skb)->frag_off &htons (IP_DF ) && - skb->len > mtu && !skb_is_gso (skb) && +ip_vs_exceeds_mtu (skb, mtu) && !ip_vs_iph_icmp (ipvsh))) {icmp_send (skb,ICMP_DEST_UNREACH ,ICMP_FRAG_NEEDED ,htonl (mtu));
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 666/957 netfilter: nfnetlink_osf: fix out-of-bounds read on option matching
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fernando Fernandez Mancera <[email protected]>
[ Upstream commit f5ca450087c3baf3651055e7a6de92600f827af3 ]
In nf_osf_match(), the nf_osf_hdr_ctx structure is initialized once and passed by reference to nf_osf_match_one() for each fingerprint checked. During TCP option parsing, nf_osf_match_one() advances the shared ctx->optp pointer.
If a fingerprint perfectly matches, the function returns early without restoring ctx->optp to its initial state. If the user has configured NF_OSF_LOGLEVEL_ALL, the loop continues to the next fingerprint. However, because ctx->optp was not restored, the next call to nf_osf_match_one() starts parsing from the end of the options buffer. This causes subsequent matches to read garbage data and fail immediately, making it impossible to log more than one match or logging incorrect matches.
Instead of using a shared ctx->optp pointer, pass the context as a constant pointer and use a local pointer (optp) for TCP option traversal. This makes nf_osf_match_one() strictly stateless from the caller's perspective, ensuring every fingerprint check starts at the correct option offset.
--- net/netfilter/nfnetlink_osf.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-)
net/netfilter/nfnetlink_osf.c +8 -11
@@ -64,9 +64,9 @@ struct nf_osf_hdr_ctx {static bool nf_osf_match_one (const struct sk_buff *skb,const struct nf_osf_user_finger *f,int ttl_check, -struct nf_osf_hdr_ctx *ctx) +const struct nf_osf_hdr_ctx *ctx) { -const __u8 *optpinit = ctx->optp; +const __u8 *optp = ctx->optp;unsigned int check_WSS =0 ;int fmatch =FMATCH_WRONG ;int foptsize, optnum;
@@ -95,17 +95,17 @@ static bool nf_osf_match_one(const struct sk_buff *skb, check_WSS = f->wss.wc;for (optnum =0 ; optnum < f->opt_num; ++optnum) { -if (f->opt[optnum].kind == *ctx->optp) { +if (f->opt[optnum].kind == *optp) {__u32 len = f->opt[optnum].length; -const __u8 *optend = ctx->optp + len; +const __u8 *optend = optp + len; fmatch =FMATCH_OK ; -switch (*ctx->optp) { +switch (*optp) {case OSFOPT_MSS : - mss = ctx->optp[3 ]; + mss = optp[3 ]; mss <<=8 ; - mss |= ctx->optp[2 ]; + mss |= optp[2 ]; mss =ntohs ((__force __be16 )mss);break ;
@@ -113,7 +113,7 @@ static bool nf_osf_match_one(const struct sk_buff *skb,break ; } - ctx->optp = optend; + optp = optend; }else fmatch =FMATCH_OPT_WRONG ;
@@ -156,9 +156,6 @@ static bool nf_osf_match_one(const struct sk_buff *skb, } } -if (fmatch !=FMATCH_OK ) - ctx->optp = optpinit; -return fmatch ==FMATCH_OK ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 667/957 netfilter: nfnetlink_osf: fix potential NULL dereference in ttl check
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fernando Fernandez Mancera <[email protected]>
[ Upstream commit 711987ba281fd806322a7cd244e98e2a81903114 ]
The nf_osf_ttl() function accessed skb->dev to perform a local interface address lookup without verifying that the device pointer was valid.
Additionally, the implementation utilized an in_dev_for_each_ifa_rcu loop to match the packet source address against local interface addresses. It assumed that packets from the same subnet should not see a decrement on the initial TTL. A packet might appear it is from the same subnet but it actually isn't especially in modern environments with containers and virtual switching.
Remove the device dereference and interface loop. Replace the logic with a switch statement that evaluates the TTL according to the ttl_check.
--- net/netfilter/nfnetlink_osf.c | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-)
net/netfilter/nfnetlink_osf.c +7 -15
@@ -31,26 +31,18 @@ EXPORT_SYMBOL_GPL(nf_osf_fingers);static inline int nf_osf_ttl (const struct sk_buff *skb,int ttl_check,unsigned char f_ttl) { -struct in_device *in_dev =__in_dev_get_rcu (skb->dev);const struct iphdr *ip =ip_hdr (skb); -const struct in_ifaddr *ifa; -int ret =0 ; -if (ttl_check ==NF_OSF_TTL_TRUE ) +switch (ttl_check) { +case NF_OSF_TTL_TRUE :return ip->ttl == f_ttl; -if (ttl_check ==NF_OSF_TTL_NOCHECK ) -return 1 ; -else if (ip->ttl <= f_ttl) +break ; +case NF_OSF_TTL_NOCHECK :return 1 ; - -in_dev_for_each_ifa_rcu (ifa, in_dev) { -if (inet_ifa_match (ip->saddr, ifa)) { - ret = (ip->ttl == f_ttl); -break ; - } +case NF_OSF_TTL_LESS : +default : +return ip->ttl <= f_ttl; } - -return ret; }struct nf_osf_hdr_ctx {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 668/957 slip: reject VJ receive packets on instances with no rstate array
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Weiming Shi <[email protected]>
[ Upstream commit e76607442d5b73e1ba6768f501ef815bb58c2c0e ]
slhc_init() accepts rslots == 0 as a valid configuration, with the documented meaning of 'no receive compression'. In that case the allocation loop in slhc_init() is skipped, so comp->rstate stays NULL and comp->rslot_limit stays 0 (from the kzalloc of struct slcompress).
The receive helpers do not defend against that configuration. slhc_uncompress() dereferences comp->rstate[x] when the VJ header carries an explicit connection ID, and slhc_remember() later assigns cs = &comp->rstate[...] after only comparing the packet's slot number to comp->rslot_limit. Because rslot_limit is 0, slot 0 passes the range check, and the code dereferences a NULL rstate.
The configuration is reachable in-tree through PPP. PPPIOCSMAXCID stores its argument in a signed int, and (val >> 16) uses arithmetic shift. Passing 0xffff0000 therefore sign-extends to -1, so val2 + 1 is 0 and ppp_generic.c ends up calling slhc_init(0, 1). Because /dev/ppp open is gated by ns_capable(CAP_NET_ADMIN), the whole path is reachable from an unprivileged user namespace. Once the malformed VJ state is installed, any inbound VJ-compressed or VJ-uncompressed frame that selects slot 0 crashes the kernel in softirq context:
Oops: general protection fault, probably for non-canonical
address 0xdffffc0000000000: 0000 [#1] SMP KASAN NOPTI
KASAN: null-ptr-deref in range [0x0000000000000000-0x0000000000000007]
RIP: 0010:slhc_uncompress (drivers/net/slip/slhc.c:519)
Call Trace:
<TASK>
ppp_receive_nonmp_frame (drivers/net/ppp/ppp_generic.c:2466)
ppp_input (drivers/net/ppp/ppp_generic.c:2359)
ppp_async_process (drivers/net/ppp/ppp_async.c:492)
tasklet_action_common (kernel/softirq.c:926)
handle_softirqs (kernel/softirq.c:623)
run_ksoftirqd (kernel/softirq.c:1055)
smpboot_thread_fn (kernel/smpboot.c:160)
kthread (kernel/kthread.c:436)
ret_from_fork (arch/x86/kernel/process.c:164)
</TASK>Reject the receive side on such instances instead of touching rstate. slhc_uncompress() falls through to its existing 'bad' label, which bumps sls_i_error and enters the toss state. slhc_remember() mirrors that with an explicit sls_i_error increment followed by slhc_toss(); the sls_i_runt counter is not used here because a missing rstate is an internal configuration state, not a runt packet.
The transmit path is unaffected: the only in-tree caller that picks rslots from userspace (ppp_generic.c) still supplies tslots >= 1, and slip.c always calls slhc_init(16, 16), so comp->tstate remains valid and slhc_compress() continues to work.
--- drivers/net/slip/slhc.c | 6 ++++++ 1 file changed, 6 insertions(+)
drivers/net/slip/slhc.c +6 -0
@@ -506,6 +506,8 @@ slhc_uncompress(struct slcompress *comp, unsigned char *icp, int isize) comp->sls_i_error++;return 0 ; } +if (!comp->rstate) + goto bad; changes = *cp++;if (changes &NEW_C ){/* Make sure the state index is in range, then grab the state.
@@ -649,6 +651,10 @@ slhc_remember(struct slcompress *comp, unsigned char *icp, int isize)struct cstate *cs;unsigned int ihl; +if (!comp->rstate) { + comp->sls_i_error++; +return slhc_toss (comp); + }/* The packet is shorter than a legal IP header. * Also make sure isize is positive. */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 669/957 slip: bound decode() reads against the compressed packet length
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Weiming Shi <[email protected]>
[ Upstream commit 4c1367a2d7aad643a6f87c6931b13cc1a25e8ca7 ]
slhc_uncompress() parses a VJ-compressed TCP header by advancing a pointer through the packet via decode() and pull16(). Neither helper bounds-checks against isize, and decode() masks its return with & 0xffff so it can never return the -1 that callers test for -- those error paths are dead code.
A short compressed frame whose change byte requests optional fields lets decode() read past the end of the packet. The over-read bytes are folded into the cached cstate and reflected into subsequent reconstructed packets.
Make decode() and pull16() take the packet end pointer and return -1 when exhausted. Add a bounds check before the TCP-checksum read. The existing == -1 tests now do what they were always meant to.
--- drivers/net/slip/slhc.c | 43 ++++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 18 deletions(-)
drivers/net/slip/slhc.c +25 -18
@@ -80,9 +80,9 @@#include <linux/unaligned.h> static unsigned char *encode (unsigned char *cp,unsigned short n); -static long decode (unsigned char **cpp); +static long decode (unsigned char **cpp,const unsigned char *end);static unsigned char *put16 (unsigned char *cp,unsigned short x); -static unsigned short pull16 (unsigned char **cpp); +static long pull16 (unsigned char **cpp,const unsigned char *end);/* Allocate compression data structure * slots must be in range 0 to 255 (zero meaning no compression)
@@ -190,30 +190,34 @@ encode(unsigned char *cp, unsigned short n)return cp; } -/* Pull a 16-bit integer in host order from buffer in network byte order */ -static unsigned short -pull16 (unsigned char **cpp) +/* Pull a 16-bit integer in host order from buffer in network byte order. +* Returns -1 if the buffer is exhausted, otherwise the 16-bit value. +*/ +static long +pull16 (unsigned char **cpp,const unsigned char *end) { -short rval; +long rval; +if (*cpp +2 > end) +return -1 ; rval = *(*cpp)++; rval <<=8 ; rval |= *(*cpp)++;return rval; } -/* Decode a number */ +/* Decode a number. Returns -1 if the buffer is exhausted. */ static long -decode (unsigned char **cpp) +decode (unsigned char **cpp,const unsigned char *end) {int x; +if (*cpp >= end) +return -1 ; x = *(*cpp)++; -if (x ==0 ){ -return pull16 (cpp) &0xffff ;/* pull16 returns -1 on error */ - }else { -return x &0xff ;/* -1 if PULLCHAR returned error */ - } +if (x ==0 ) +return pull16 (cpp, end); +return x &0xff ; }/*
@@ -499,6 +503,7 @@ slhc_uncompress(struct slcompress *comp, unsigned char *icp, int isize)struct cstate *cs;int len, hdrlen;unsigned char *cp = icp; +const unsigned char *end = icp + isize;/* We've got a compressed packet; read the change byte */ comp->sls_i_compressed++;
@@ -536,6 +541,8 @@ slhc_uncompress(struct slcompress *comp, unsigned char *icp, int isize) thp = &cs->cs_tcp; ip = &cs->cs_ip; +if (cp +2 > end) + goto bad; thp->check = *(__sum16 *)cp; cp +=2 ;
@@ -566,26 +573,26 @@ slhc_uncompress(struct slcompress *comp, unsigned char *icp, int isize)default :if (changes &NEW_U ){ thp->urg =1 ; -if ((x =decode (&cp)) ==-1 ) { +if ((x =decode (&cp, end)) ==-1 ) { goto bad; } thp->urg_ptr =htons (x); }else thp->urg =0 ;if (changes &NEW_W ){ -if ((x =decode (&cp)) ==-1 ) { +if ((x =decode (&cp, end)) ==-1 ) { goto bad; } thp->window =htons (ntohs (thp->window) + x); }if (changes &NEW_A ){ -if ((x =decode (&cp)) ==-1 ) { +if ((x =decode (&cp, end)) ==-1 ) { goto bad; } thp->ack_seq =htonl (ntohl (thp->ack_seq) + x); }if (changes &NEW_S ){ -if ((x =decode (&cp)) ==-1 ) { +if ((x =decode (&cp, end)) ==-1 ) { goto bad; } thp->seq =htonl (ntohl (thp->seq) + x);
@@ -593,7 +600,7 @@ slhc_uncompress(struct slcompress *comp, unsigned char *icp, int isize)break ; }if (changes &NEW_I ){ -if ((x =decode (&cp)) ==-1 ) { +if ((x =decode (&cp, end)) ==-1 ) { goto bad; } ip->id =htons (ntohs (ip->id) + x);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 670/957 net/sched: sch_dualpi2: drain both C-queue and L-queue in dualpi2_change()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chia-Yu Chang <[email protected]>
[ Upstream commit 478ed6b7d2577439c610f91fa8759a4c878a4264 ]
Fix dualpi2_change() to correctly enforce updated limit and memlimit values after a configuration change of the dualpi2 qdisc.
Before this patch, dualpi2_change() always attempted to dequeue packets via the root qdisc (C-queue) when reducing backlog or memory usage, and unconditionally assumed that a valid skb will be returned. When traffic classification results in packets being queued in the L-queue while the C-queue is empty, this leads to a NULL skb dereference during limit or memlimit enforcement.
This is fixed by first dequeuing from the C-queue path if it is non-empty. Once the C-queue is empty, packets are dequeued directly from the L-queue. Return values from qdisc_dequeue_internal() are checked for both queues. When dequeuing from the L-queue, the parent qdisc qlen and backlog counters are updated explicitly to keep overall qdisc statistics consistent.
--- net/sched/sch_dualpi2.c | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-)
net/sched/sch_dualpi2.c +28 -4
@@ -871,11 +871,35 @@ static int dualpi2_change(struct Qdisc *sch, struct nlattr *opt, old_backlog = sch->qstats.backlog;while (qdisc_qlen (sch) > sch->limit || q->memory_used > q->memory_limit) { -struct sk_buff *skb =qdisc_dequeue_internal (sch, true); +struct sk_buff *skb =NULL ; - q->memory_used -= skb->truesize; -qdisc_qstats_backlog_dec (sch, skb); -rtnl_qdisc_drop (skb, sch); +if (qdisc_qlen (sch) >qdisc_qlen (q->l_queue)) { + skb =qdisc_dequeue_internal (sch, true); +if (unlikely (!skb)) { +WARN_ON_ONCE (1 ); +break ; + } + q->memory_used -= skb->truesize; +rtnl_qdisc_drop (skb, sch); + }else if (qdisc_qlen (q->l_queue)) { + skb =qdisc_dequeue_internal (q->l_queue, true); +if (unlikely (!skb)) { +WARN_ON_ONCE (1 ); +break ; + } +/* L-queue packets are counted in both sch and +* l_queue on enqueue; qdisc_dequeue_internal() +* handled l_queue, so we further account for sch. +*/ + --sch->q.qlen; +qdisc_qstats_backlog_dec (sch, skb); + q->memory_used -= skb->truesize; +rtnl_qdisc_drop (skb, q->l_queue); +qdisc_qstats_drop (sch); + }else { +WARN_ON_ONCE (1 ); +break ; + } }qdisc_tree_reduce_backlog (sch, old_qlen -qdisc_qlen (sch), old_backlog - sch->qstats.backlog);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 671/957 arm64: dts: amlogic: meson-axg: Add missing cache information to cpu0
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Anand Moon <[email protected]>
[ Upstream commit 918273be0885362a9a00615b46e03f15f8b55667 ]
Add missing L1 data and instruction cache parameters to the CPU node 0 for the Cortex-A53 caches on the Meson AXG SoC.
--- arch/arm64/boot/dts/amlogic/meson-axg.dtsi | 6 ++++++ 1 file changed, 6 insertions(+)
arch/arm64/boot/dts/amlogic/meson-axg.dtsi +6 -0
@@ -72,6 +72,12 @@ cpu0: cpu@0 { compatible = "arm,cortex-a53"; reg = <0x0 0x0>; enable-method = "psci"; + d-cache-line-size = <32>; + d-cache-size = <0x8000>; + d-cache-sets = <32>; + i-cache-line-size = <32>; + i-cache-size = <0x8000>; + i-cache-sets = <32>; next-level-cache = <&l2>; clocks = <&scpi_dvfs 0>; dynamic-power-coefficient = <140>;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 672/957 arm64: dts: meson-gxl-p230: fix ethernet PHY interrupt number
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jun Yan <[email protected]>
[ Upstream commit 174a0ef3b33434f475c87e66f37980e39b73805a ]
Correct the interrupt number assigned to the Realtek PHY in the p230
following the same logic as commit 3106507e1004 ("ARM64: dts: meson-gxm:
fix q200 interrupt number"),as reported in [PATCH 0/2] Ethernet PHY interrupt improvements [1].
[1] https://lore.kernel.org/all/[email protected]/
--- arch/arm64/boot/dts/amlogic/meson-gxl-s905d-p230.dts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
arch/arm64/boot/dts/amlogic/meson-gxl-s905d-p230.dts +2 -1
@@ -84,7 +84,8 @@ external_phy: ethernet-phy@0 { reset-gpios = <&gpio GPIOZ_14 GPIO_ACTIVE_LOW>; interrupt-parent = <&gpio_intc>; - interrupts = <29 IRQ_TYPE_LEVEL_LOW>; + /* MAC_INTR on GPIOZ_15 */ + interrupts = <25 IRQ_TYPE_LEVEL_LOW>; eee-broken-1000t; }; };
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 673/957 pwm: atmel-tcb: Cache clock rates and mark chip as atomic
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sangyun Kim <[email protected]>
[ Upstream commit 68637b68afcc3cb4d56aca14a3a1d1b47b879369 ]
atmel_tcb_pwm_apply() holds tcbpwmc->lock as a spinlock via guard(spinlock)() and then calls atmel_tcb_pwm_config(), which calls clk_get_rate() twice. clk_get_rate() acquires clk_prepare_lock (a mutex), so this is a sleep-in-atomic-context violation.
On CONFIG_DEBUG_ATOMIC_SLEEP kernels every pwm_apply_state() that enables or reconfigures the PWM triggers a "BUG: sleeping function called from invalid context" warning.
Acquire exclusive control over the clock rates with clk_rate_exclusive_get() at probe time and cache the rates in struct atmel_tcb_pwm_chip, then read the cached rates from atmel_tcb_pwm_config(). This keeps the spinlock-based mutual exclusion introduced in commit 37f7707077f5 ("pwm: atmel-tcb: Fix race condition and convert to guards") and removes the sleeping calls from the atomic section.
With no sleeping calls left in .apply() and the regmap-mmio bus already running with fast_io=true, also mark the chip as atomic so consumers can use pwm_apply_atomic() from atomic context.
--- drivers/pwm/pwm-atmel-tcb.c | 38 +++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-)
drivers/pwm/pwm-atmel-tcb.c +34 -4
@@ -50,6 +50,8 @@ struct atmel_tcb_pwm_chip {spinlock_t lock;u8 channel;u8 width; +unsigned long rate; +unsigned long slow_rate;struct regmap *regmap;struct clk *clk;struct clk *gclk;
@@ -266,7 +268,7 @@ static int atmel_tcb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,int slowclk =0 ;unsigned period;unsigned duty; -unsigned rate =clk_get_rate (tcbpwmc->clk); +unsigned long rate = tcbpwmc->rate;unsigned long long min;unsigned long long max;
@@ -294,7 +296,7 @@ static int atmel_tcb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,*/ if (i ==ARRAY_SIZE (atmel_tcb_divisors)) { i = slowclk; - rate =clk_get_rate (tcbpwmc->slow_clk); + rate = tcbpwmc->slow_rate; min =div_u64 (NSEC_PER_SEC , rate); max = min << tcbpwmc->width;
@@ -431,24 +433,49 @@ static int atmel_tcb_pwm_probe(struct platform_device *pdev) } chip->ops = &atmel_tcb_pwm_ops; + chip->atomic = true; tcbpwmc->channel = channel; tcbpwmc->width = config->counter_width; - err =clk_prepare_enable (tcbpwmc->slow_clk); + err =clk_prepare_enable (tcbpwmc->clk);if (err) goto err_gclk; + err =clk_prepare_enable (tcbpwmc->slow_clk); +if (err) + goto err_disable_clk;; + + err =clk_rate_exclusive_get (tcbpwmc->clk); +if (err) + goto err_disable_slow_clk; + + err =clk_rate_exclusive_get (tcbpwmc->slow_clk); +if (err) + goto err_clk_unlock; + + tcbpwmc->rate =clk_get_rate (tcbpwmc->clk); + tcbpwmc->slow_rate =clk_get_rate (tcbpwmc->slow_clk); +spin_lock_init (&tcbpwmc->lock); err =pwmchip_add (chip);if (err <0 ) - goto err_disable_clk; + goto err_slow_clk_unlock;platform_set_drvdata (pdev, chip);return 0 ; +err_slow_clk_unlock: +clk_rate_exclusive_put (tcbpwmc->slow_clk); + +err_clk_unlock: +clk_rate_exclusive_put (tcbpwmc->clk); + err_disable_clk: +clk_disable_unprepare (tcbpwmc->clk); + +err_disable_slow_clk:clk_disable_unprepare (tcbpwmc->slow_clk); err_gclk:
@@ -470,6 +497,9 @@ static void atmel_tcb_pwm_remove(struct platform_device *pdev)pwmchip_remove (chip); +clk_rate_exclusive_put (tcbpwmc->slow_clk); +clk_rate_exclusive_put (tcbpwmc->clk); +clk_disable_unprepare (tcbpwmc->clk);clk_disable_unprepare (tcbpwmc->slow_clk);clk_put (tcbpwmc->gclk);clk_put (tcbpwmc->clk);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 674/957 ksmbd: destroy tree_conn_ida in ksmbd_session_destroy()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: DaeMyung Kang <[email protected]>
[ Upstream commit c049ee14eb4343b69b6f7755563f961f5e153423 ]
When per-session tree_conn_ida was converted from a dynamically allocated ksmbd_ida to an embedded struct ida, ksmbd_ida_free() was removed from ksmbd_session_destroy() but no matching ida_destroy() was added. The session is therefore freed with the IDA's backing xarray still intact.
The kernel IDA API expects ida_init() and ida_destroy() to be paired over an object's lifetime, so add the missing cleanup before the enclosing session is freed.
Also move ida_init() to right after the session is allocated so that it is always paired with the destroy call even on the early error paths of __session_create() (ksmbd_init_file_table() or __init_smb2_session() failures), both of which jump to the error label and invoke ksmbd_session_destroy() on a partially initialised session.
No leak has been observed in testing; this is a pairing fix to match the IDA lifetime rules, not a response to a reproduced regression.
--- fs/smb/server/mgmt/user_session.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
fs/smb/server/mgmt/user_session.c +3 -2
@@ -170,6 +170,7 @@ void ksmbd_session_destroy(struct ksmbd_session *sess)free_channel_list (sess);kfree (sess->Preauth_HashValue);ksmbd_release_id (&session_ida, sess->id); +ida_destroy (&sess->tree_conn_ida);kfree (sess); }
@@ -444,6 +445,8 @@ static struct ksmbd_session *__session_create(int protocol)if (!sess)return NULL ; +ida_init (&sess->tree_conn_ida); +if (ksmbd_init_file_table (&sess->file_table)) goto error;
@@ -463,8 +466,6 @@ static struct ksmbd_session *__session_create(int protocol)if (ret) goto error; -ida_init (&sess->tree_conn_ida); -down_write (&sessions_table_lock);hash_add (sessions_table, &sess->hlist, sess->id);up_write (&sessions_table_lock);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 675/957 ksmbd: destroy async_ida in ksmbd_conn_free()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: DaeMyung Kang <[email protected]>
[ Upstream commit b32c8db48212a34998c36d0bbc05b29d5c407ef5 ]
When per-connection async_ida was converted from a dynamically allocated ksmbd_ida to an embedded struct ida, ksmbd_ida_free() was removed from the connection teardown path but no matching ida_destroy() was added. The connection is therefore freed with the IDA's backing xarray still intact.
The kernel IDA API expects ida_init() and ida_destroy() to be paired over an object's lifetime, so add the missing cleanup before the connection is freed.
No leak has been observed in testing; this is a pairing fix to match the IDA lifetime rules, not a response to a reproduced regression.
--- fs/smb/server/connection.c | 9 +++++++++ 1 file changed, 9 insertions(+)
fs/smb/server/connection.c +9 -0
@@ -41,6 +41,15 @@ void ksmbd_conn_free(struct ksmbd_conn *conn)kfree (conn->preauth_info);kfree (conn->mechToken);if (atomic_dec_and_test (&conn->refcnt)) { +/* +* async_ida is embedded in struct ksmbd_conn, so pair +* ida_destroy() with the final kfree() rather than with +* the unconditional field teardown above. This keeps +* the IDA valid for the entire lifetime of the struct, +* even while other refcount holders (oplock / vfs +* durable handles) still reference the connection. +*/ +ida_destroy (&conn->async_ida); conn->transport->ops->free_transport (conn->transport);kfree (conn); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 676/957 ksmbd: fix durable fd leak on ClientGUID mismatch in durable v2 open
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: DaeMyung Kang <[email protected]>
[ Upstream commit 804054d19886ac6628883d82410f6ee42a818664 ]
ksmbd_lookup_fd_cguid() returns a ksmbd_file with its refcount incremented via ksmbd_fp_get(). parse_durable_handle_context() in the DURABLE_REQ_V2 case properly releases this reference on every path inside the ClientGUID-match branch, either by calling ksmbd_put_durable_fd() or by transferring ownership to dh_info->fp for a successful reconnect. However, when an entry exists in the global file table with the same CreateGuid but a different ClientGUID, the code simply falls through to the new-open path without dropping the reference obtained from ksmbd_lookup_fd_cguid().
Per MS-SMB2 section 3.3.5.9.10 ("Handling the SMB2_CREATE_DURABLE_HANDLE_REQUEST_V2 Create Context"), the server MUST locate an Open whose Open.CreateGuid matches the request's CreateGuid AND whose Open.ClientGuid matches the ClientGuid of the connection that received the request. If no such Open is found, the server MUST continue with the normal open execution phase. A CreateGuid hit with a ClientGUID mismatch is therefore the "Open not found" case: proceeding with a new open is correct, but the reference obtained purely as a side effect of the lookup must not be leaked.
Repeated requests that hit this mismatch pin global_ft entries, prevent __ksmbd_close_fd() from ever running for the corresponding files, and defeat the durable scavenger, leading to long-lived resource leaks.
Release the reference in the mismatch path and clear dh_info->fp so subsequent logic does not mistake a non-matching lookup result for a reconnect target.
--- fs/smb/server/smb2pdu.c | 2 ++ 1 file changed, 2 insertions(+)
fs/smb/server/smb2pdu.c +2 -0
@@ -2854,6 +2854,8 @@ static int parse_durable_handle_context(struct ksmbd_work *work, dh_info->reconnected = true; goto out; } +ksmbd_put_durable_fd (dh_info->fp); + dh_info->fp =NULL ; } if ((lc && (lc->req_state & SMB2_LEASE_HANDLE_CACHING_LE)) ||
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 677/957 ksmbd: scope conn->binding slowpath to bound sessions only
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hyunwoo Kim <[email protected]>
[ Upstream commit b0da97c034b6107d14e537e212d4ce8b22109a58 ]
When the binding SESSION_SETUP sets conn->binding = true, the flag stays set after the call so that the global session lookup in ksmbd_session_lookup_all() can find the session, which was not added to conn->sessions. Because the flag is connection-wide, the global lookup path will also resolve any other session by id if asked.
Tighten the global lookup so that the returned session must have this connection registered in its channel xarray (sess->ksmbd_chann_list). The channel entry is installed by the existing binding_session path in ntlm_authenticate()/krb5_authenticate() when a SESSION_SETUP completes successfully, so this condition is a strict equivalent of "this connection has been accepted as a channel of this session". Connections that have not bound to a given session cannot reach it via the global table.
The existing conn->binding gate for entering the slowpath is preserved so that non-binding connections keep the fast-path-only behavior, and the session->state check is unchanged.
--- fs/smb/server/mgmt/user_session.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
fs/smb/server/mgmt/user_session.c +6 -1
@@ -327,8 +327,13 @@ struct ksmbd_session *ksmbd_session_lookup_all(struct ksmbd_conn *conn,struct ksmbd_session *sess; sess =ksmbd_session_lookup (conn, id); -if (!sess && conn->binding) +if (!sess && conn->binding) { sess =ksmbd_session_lookup_slowpath (id); +if (sess && !xa_load (&sess->ksmbd_chann_list, (long )conn)) { +ksmbd_user_session_put (sess); + sess =NULL ; + } + }if (sess && sess->state !=SMB2_SESSION_VALID ) {ksmbd_user_session_put (sess); sess =NULL ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 678/957 net: validate skb->napi_id in RX tracepoints
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kohei Enju <[email protected]>
[ Upstream commit 3bfcf396081ace536733b454ff128d53116581e5 ]
Since commit 2bd82484bb4c ("xps: fix xps for stacked devices"), skb->napi_id shares storage with sender_cpu. RX tracepoints using net_dev_rx_verbose_template read skb->napi_id directly and can therefore report sender_cpu values as if they were NAPI IDs.
For example, on the loopback path this can report 1 as napi_id, where 1 comes from raw_smp_processor_id() + 1 in the XPS path:
# bpftrace -e 'tracepoint:net:netif_rx_entry{ print(args->napi_id); }'
# taskset -c 0 ping -c 1 ::1Report only valid NAPI IDs in these tracepoints and use 0 otherwise.
--- include/trace/events/net.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
include/trace/events/net.h +3 -1
@@ -10,6 +10,7 @@#include <linux/if_vlan.h> #include <linux/ip.h> #include <linux/tracepoint.h> +#include <net/busy_poll.h> TRACE_EVENT(net_dev_start_xmit,
@@ -193,7 +194,8 @@ DECLARE_EVENT_CLASS(net_dev_rx_verbose_template,TP_fast_assign ( __assign_str(name);#ifdef CONFIG_NET_RX_BUSY_POLL - __entry->napi_id = skb->napi_id; + __entry->napi_id =napi_id_valid (skb->napi_id) ? + skb->napi_id :0 ;#else __entry->napi_id =0 ;#endif
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 679/957 bnge: fix initial HWRM sequence
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vikas Gupta <[email protected]>
[ Upstream commit 70d7c905a07ae8415b955569620bf2bf77423553 ]
Firmware may not advertize correct resources if backing store is not enabled before resource information is queried.
Fix the initial sequence of HWRMs so that driver gets capabilities and resource information correctly.
--- .../net/ethernet/broadcom/bnge/bnge_core.c | 30 ++++++++++++++----- 1 file changed, 22 insertions(+), 8 deletions(-)
drivers/net/ethernet/broadcom/bnge/bnge_core.c +22 -8
@@ -68,6 +68,13 @@ static int bnge_func_qcaps(struct bnge_dev *bd)return rc; } +return 0 ; +} + +static int bnge_func_qrcaps_qcfg (struct bnge_dev *bd) +{ +int rc; + rc =bnge_hwrm_func_resc_qcaps (bd);if (rc) {dev_err (bd->dev,"query resc caps failure rc: %d\n" , rc);
@@ -127,23 +134,28 @@ static int bnge_fw_register_dev(struct bnge_dev *bd)bnge_hwrm_fw_set_time (bd); - rc =bnge_hwrm_func_drv_rgtr (bd); +/* Get the resources and configuration from firmware */ + rc =bnge_func_qcaps (bd);if (rc) { -dev_err (bd->dev,"Failed to rgtr with firmware rc: %d\n" , rc); +dev_err (bd->dev,"Failed querying caps rc: %d\n" , rc);return rc; } rc =bnge_alloc_ctx_mem (bd);if (rc) {dev_err (bd->dev,"Failed to allocate ctx mem rc: %d\n" , rc); - goto err_func_unrgtr; + goto err_free_ctx_mem; } -/* Get the resources and configuration from firmware */ - rc =bnge_func_qcaps (bd); + rc =bnge_hwrm_func_drv_rgtr (bd);if (rc) { -dev_err (bd->dev,"Failed initial configuration rc: %d\n" , rc); - rc = -ENODEV ; +dev_err (bd->dev,"Failed to rgtr with firmware rc: %d\n" , rc); + goto err_free_ctx_mem; + } + + rc =bnge_func_qrcaps_qcfg (bd); +if (rc) { +dev_err (bd->dev,"Failed querying resources rc: %d\n" , rc); goto err_func_unrgtr; }
@@ -152,7 +164,9 @@ static int bnge_fw_register_dev(struct bnge_dev *bd)return 0 ; err_func_unrgtr: -bnge_fw_unregister_dev (bd); +bnge_hwrm_func_drv_unrgtr (bd); +err_free_ctx_mem: +bnge_free_ctx_mem (bd);return rc; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 680/957 bnge: remove unsupported backing store type
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vikas Gupta <[email protected]>
[ Upstream commit c6b34add67a5402f53359580956b5c318965a893 ]
The backing store type, BNGE_CTX_MRAV, is not applicable in Thor Ultra devices. Remove it from the backing store configuration, as the firmware will not populate entities in this backing store type, due to which the driver load fails.
--- drivers/net/ethernet/broadcom/bnge/bnge_rmem.c | 16 ---------------- 1 file changed, 16 deletions(-)
drivers/net/ethernet/broadcom/bnge/bnge_rmem.c +0 -16
@@ -325,7 +325,6 @@ int bnge_alloc_ctx_mem(struct bnge_dev *bd)u32 l2_qps, qp1_qps, max_qps;u32 ena, entries_sp, entries;u32 srqs, max_srqs, min; -u32 num_mr, num_ah;u32 extra_srqs =0 ;u32 extra_qps =0 ;u32 fast_qpmd_qps;
@@ -391,21 +390,6 @@ int bnge_alloc_ctx_mem(struct bnge_dev *bd)if (!bnge_is_roce_en (bd)) goto skip_rdma; - ctxm = &ctx->ctx_arr[BNGE_CTX_MRAV ]; -/* 128K extra is needed to accommodate static AH context -* allocation by f/w. -*/ - num_mr =min_t (u32, ctxm->max_entries /2 ,1024 *256 ); - num_ah =min_t (u32, num_mr,1024 *128 ); - ctxm->split_entry_cnt =BNGE_CTX_MRAV_AV_SPLIT_ENTRY +1 ; -if (!ctxm->mrav_av_entries || ctxm->mrav_av_entries > num_ah) - ctxm->mrav_av_entries = num_ah; - - rc =bnge_setup_ctxm_pg_tbls (bd, ctxm, num_mr + num_ah,2 ); -if (rc) -return rc; - ena |=FUNC_BACKING_STORE_CFG_REQ_ENABLES_MRAV ; - ctxm = &ctx->ctx_arr[BNGE_CTX_TIM ]; rc =bnge_setup_ctxm_pg_tbls (bd, ctxm, l2_qps + qp1_qps + extra_qps,1 );if (rc)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 681/957 net/rds: zero per-item info buffer before handing it to visitors
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
[ Upstream commit c88eb7e8d8397a8c1db59c425332c5a30b2a1682 ]
rds_for_each_conn_info() and rds_walk_conn_path_info() both hand a caller-allocated on-stack u64 buffer to a per-connection visitor and then copy the full item_len bytes back to user space via rds_info_copy() regardless of how much of the buffer the visitor actually wrote.
rds_ib_conn_info_visitor() and rds6_ib_conn_info_visitor() only write a subset of their output struct when the underlying rds_connection is not in state RDS_CONN_UP (src/dst addr, tos, sl and the two GIDs via explicit memsets). Several u32 fields (max_send_wr, max_recv_wr, max_send_sge, rdma_mr_max, rdma_mr_size, cache_allocs) and the 2-byte alignment hole between sl and cache_allocs remain as whatever stack contents preceded the visitor call and are then memcpy_to_user()'d out to user space.
struct rds_info_rdma_connection and struct rds6_info_rdma_connection are the only rds_info_* structs in include/uapi/linux/rds.h that are not marked __attribute__((packed)), so they have a real alignment hole. The other info visitors (rds_conn_info_visitor, rds6_conn_info_visitor, rds_tcp_tc_info, ...) write all fields of their packed output struct today and are not known to be vulnerable, but a future visitor that adds a conditional write-path would have the same bug.
Reproduction on a kernel built without CONFIG_INIT_STACK_ALL_ZERO=y:
a local unprivileged user opens AF_RDS, sets SO_RDS_TRANSPORT=IB, binds to a local address on an RDMA-capable netdev (rxe soft-RoCE on any netdev is sufficient), sendto()'s any peer on the same subnet (fails cleanly but installs an rds_connection in the global hash in RDS_CONN_CONNECTING), then calls getsockopt(SOL_RDS, RDS_INFO_IB_CONNECTIONS). The returned 68-byte item contains 26 bytes of stack garbage including kernel text/data pointers:
0..7 0a 63 00 01 0a 63 00 02 src=10.99.0.1 dst=10.99.0.2
8..39 00 ... gids (memset-zeroed)
40..47 e0 92 a3 81 ff ff ff ff kernel pointer (max_send_wr)
48..55 7f 37 b5 81 ff ff ff ff kernel pointer (rdma_mr_max)
56..59 01 00 08 00 rdma_mr_size (garbage)
60..61 00 00 tos, sl
62..63 00 00 alignment padding
64..67 18 00 00 00 cache_allocs (garbage)Fix by zeroing the per-item buffer in both rds_for_each_conn_info() and rds_walk_conn_path_info() before invoking the visitor. This covers the IPv4/IPv6 IB visitors and hardens all current and future visitors against the same class of bug.
No functional change for visitors that fully populate their output.
Changes in v2:
- retarget at the net tree (subject prefix "[PATCH net v2]", net/rds: prefix in the title)
- pick up Reviewed-by tags from Sharath Srinivasan and Allison Henderson
--- net/rds/connection.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+)
net/rds/connection.c +14 -0
@@ -674,6 +674,13 @@ void rds_for_each_conn_info(struct socket *sock, unsigned int len, i++, head++) {hlist_for_each_entry_rcu (conn, head, c_hash_node) { +/* Zero the per-item buffer before handing it to the +* visitor so any field the visitor does not write - +* including implicit alignment padding - cannot leak +* stack contents to user space via rds_info_copy(). +*/ +memset (buffer,0 , item_len); +/* XXX no c_lock usage.. */ if (!visitor (conn, buffer))continue ;
@@ -723,6 +730,13 @@ static void rds_walk_conn_path_info(struct socket *sock, unsigned int len,*/ cp = conn->c_path; +/* Zero the per-item buffer for the same reason as +* rds_for_each_conn_info(): any byte the visitor +* does not write (including alignment padding) must +* not leak stack contents via rds_info_copy(). +*/ +memset (buffer,0 , item_len); +/* XXX no cp_lock usage.. */ if (!visitor (cp, buffer))continue ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 682/957 ice: fix timestamp interrupt configuration for E825C
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Grzegorz Nitka <[email protected]>
[ Upstream commit c0a575a801a2040eb1e0db54b488f8c548c8458a ]
The E825C ice_phy_cfg_intr_eth56g() function is responsible for programming the PHY interrupt for a given port. This function writes to the PHY_REG_TS_INT_CONFIG register of the port. The register is responsible for configuring whether the port interrupt logic is enabled, as well as programming the threshold of waiting timestamps that will trigger an interrupt from this port.
This threshold value must not be programmed to zero while the interrupt is enabled. Doing so puts the port in a misconfigured state where the PHY timestamp interrupt for the quad of connected ports will become stuck.
This occurs, because a threshold of zero results in the timestamp interrupt status for the port becoming stuck high. The four ports in the connected quad have their timestamp status indicators muxed together. A new interrupt cannot be generated until the timestamp status indicators return low for all four ports.
Normally, the timestamp status for a port will clear once there are fewer timestamps in that ports timestamp memory bank than the threshold. A threshold of zero makes this impossible, so the timestamp status for the port does not clear.
The ice driver never intentionally programs the threshold to zero, indeed the driver always programs it to a value of 1, intending to get an interrupt immediately as soon as even a single packet is waiting for a timestamp.
However, there is a subtle flaw in the programming logic in the ice_phy_cfg_intr_eth56g() function. Due to the way that the hardware handles enabling the PHY interrupt. If the threshold value is modified at the same time as the interrupt is enabled, the HW PHY state machine might enable the interrupt before the new threshold value is actually updated. This leaves a potential race condition caused by the hardware logic where a PHY timestamp interrupt might be triggered before the non-zero threshold is written, resulting in the PHY timestamp logic becoming stuck.
Once the PHY timestamp status is stuck high, it will remain stuck even after attempting to reprogram the PHY block by changing its threshold or disabling the interrupt. Even a typical PF or CORE reset will not reset the particular block of the PHY that becomes stuck. Even a warm power cycle is not guaranteed to cause the PHY block to reset, and a cold power cycle is required.
Prevent this by always writing the PHY_REG_TS_INT_CONFIG in two stages. First write the threshold value with the interrupt disabled, and only write the enable bit after the threshold has been programmed. When disabling the interrupt, leave the threshold unchanged. Additionally, re-read the register after writing it to guarantee that the write to the PHY has been flushed upon exit of the function.
While we're modifying this function implementation, explicitly reject programming a threshold of 0 when enabling the interrupt. No caller does this today, but the consequences of doing so are significant. An explicit rejection in the code makes this clear.
--- drivers/net/ethernet/intel/ice/ice_ptp_hw.c | 36 ++++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-)
drivers/net/ethernet/intel/ice/ice_ptp_hw.c +32 -4
@@ -1847,6 +1847,8 @@ static int ice_phy_cfg_mac_eth56g(struct ice_hw *hw, u8 port)* @ena: enable or disable interrupt * @threshold: interrupt threshold * +* The threshold cannot be 0 while the interrupt is enabled. +* * Configure TX timestamp interrupt for the specified port * * Return:
@@ -1858,19 +1860,45 @@ int ice_phy_cfg_intr_eth56g(struct ice_hw *hw, u8 port, bool ena, u8 threshold)int err;u32 val; +if (ena && !threshold) +return -EINVAL ; + err =ice_read_ptp_reg_eth56g (hw, port,PHY_REG_TS_INT_CONFIG , &val);if (err)return err; + val &= ~PHY_TS_INT_CONFIG_ENA_M ;if (ena) { - val |=PHY_TS_INT_CONFIG_ENA_M ; val &= ~PHY_TS_INT_CONFIG_THRESHOLD_M ; val |=FIELD_PREP (PHY_TS_INT_CONFIG_THRESHOLD_M , threshold); - }else { - val &= ~PHY_TS_INT_CONFIG_ENA_M ; + err =ice_write_ptp_reg_eth56g (hw, port,PHY_REG_TS_INT_CONFIG , + val); +if (err) { +ice_debug (hw,ICE_DBG_PTP , +"Failed to update 'threshold' PHY_REG_TS_INT_CONFIG port=%u ena=%u threshold=%u\n" , + port, !!ena, threshold); +return err; + } + val |=PHY_TS_INT_CONFIG_ENA_M ; } -return ice_write_ptp_reg_eth56g (hw, port,PHY_REG_TS_INT_CONFIG , val); + err =ice_write_ptp_reg_eth56g (hw, port,PHY_REG_TS_INT_CONFIG , val); +if (err) { +ice_debug (hw,ICE_DBG_PTP , +"Failed to update 'ena' PHY_REG_TS_INT_CONFIG port=%u ena=%u threshold=%u\n" , + port, !!ena, threshold); +return err; + } + + err =ice_read_ptp_reg_eth56g (hw, port,PHY_REG_TS_INT_CONFIG , &val); +if (err) { +ice_debug (hw,ICE_DBG_PTP , +"Failed to read PHY_REG_TS_INT_CONFIG port=%u ena=%u threshold=%u\n" , + port, !!ena, threshold); +return err; + } + +return 0 ; }/**
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 683/957 ice: perform PHY soft reset for E825C ports at initialization
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Grzegorz Nitka <[email protected]>
[ Upstream commit 3ec46e157c7fa420c77dfc23f7030e61f2f3fd55 ]
In some cases the PHY timestamp block of the E825C can become stuck. This is known to occur if the software writes 0 to the Tx timestamp threshold, and with older versions of the ice driver the threshold configuration is buggy and can race in such that hardware briefly operates with a zero threshold enabled. There are no other known ways to trigger this behavior, but once it occurs, the hardware is not recovered by normal reset, a driver reload, or even a warm power cycle of the system. A cold power cycle is sufficient to recover hardware, but this is extremely invasive and can result in significant downtime on customer deployments.
The PHY for each port has a timestamping block which has its own reset functionality accessible by programming the PHY_REG_GLOBAL register. Writing to the PHY_REG_GLOBAL_SOFT_RESET_BIT triggers the hardware to perform a complete reset of the timestamping block of the PHY. This includes clearing the timestamp status for the port, clearing all outstanding timestamps in the memory bank, and resetting the PHY timer.
The new ice_ptp_phy_soft_reset_eth56g() function toggles the PHY_REG_GLOBAL soft reset bit with the required delays, ensuring the PHY is properly reinitialized without requiring a full device reset. The sequence clears the reset bit, asserts it, then clears it again, with short waits between transitions to allow hardware stabilization.
Call this function in the new ice_ptp_init_phc_e825c(), implementing the E825C device specific variant of the ice_ptp_init_phc(). Note that if ice_ptp_init_phc() fails, PTP functionality may be disabled, but the driver will still load to allow basic functionality to continue.
This causes the clock owning PF driver to perform a PHY soft reset for every port during initialization. This ensures the driver begins life in a known functional state regardless of how it was previously programmed.
This ensures that we properly reconfigure the hardware after a device reset or when loading the driver, even if it was previously misconfigured with an out-of-date or modified driver.
--- drivers/net/ethernet/intel/ice/ice_ptp_hw.c | 90 ++++++++++++++++++++- drivers/net/ethernet/intel/ice/ice_ptp_hw.h | 4 + 2 files changed, 93 insertions(+), 1 deletion(-)
drivers/net/ethernet/intel/ice/ice_ptp_hw.c +89 -1
@@ -377,6 +377,31 @@ static void ice_ptp_cfg_sync_delay(const struct ice_hw *hw, u32 delay)* The following functions operate on devices with the ETH 56G PHY. */ +/** +* ice_ptp_init_phc_e825c - Perform E825C specific PHC initialization +* @hw: pointer to HW struct +* +* Perform E825C-specific PTP hardware clock initialization steps. +* +* Return: 0 on success, or a negative error value on failure. +*/ +static int ice_ptp_init_phc_e825c (struct ice_hw *hw) +{ +int err; + +/* Soft reset all ports, to ensure everything is at a clean state */ +for (int port =0 ; port < hw->ptp.num_lports; port++) { + err =ice_ptp_phy_soft_reset_eth56g (hw, port); +if (err) { +ice_debug (hw,ICE_DBG_PTP ,"Failed to soft reset port %d, err %d\n" , + port, err); +return err; + } + } + +return 0 ; +} +/** * ice_ptp_get_dest_dev_e825 - get destination PHY for given port number * @hw: pointer to the HW struct
@@ -2179,6 +2204,69 @@ int ice_ptp_read_tx_hwtstamp_status_eth56g(struct ice_hw *hw, u32 *ts_status)return 0 ; } +/** +* ice_ptp_phy_soft_reset_eth56g - Perform a PHY soft reset on ETH56G +* @hw: pointer to the HW structure +* @port: PHY port number +* +* Trigger a soft reset of the ETH56G PHY by toggling the soft reset +* bit in the PHY global register. The reset sequence consists of: +* 1. Clearing the soft reset bit +* 2. Asserting the soft reset bit +* 3. Clearing the soft reset bit again +* +* Short delays are inserted between each step to allow the hardware +* to settle. This provides a controlled way to reinitialize the PHY +* without requiring a full device reset. +* +* Return: 0 on success, or a negative error code on failure when +* reading or writing the PHY register. +*/ +int ice_ptp_phy_soft_reset_eth56g (struct ice_hw *hw,u8 port) +{ +u32 global_val; +int err; + + err =ice_read_ptp_reg_eth56g (hw, port,PHY_REG_GLOBAL , &global_val); +if (err) { +ice_debug (hw,ICE_DBG_PTP ,"Failed to read PHY_REG_GLOBAL for port %d, err %d\n" , + port, err); +return err; + } + + global_val &= ~PHY_REG_GLOBAL_SOFT_RESET_M ; +ice_debug (hw,ICE_DBG_PTP ,"Clearing soft reset bit for port %d, val: 0x%x\n" , + port, global_val); + err =ice_write_ptp_reg_eth56g (hw, port,PHY_REG_GLOBAL , global_val); +if (err) { +ice_debug (hw,ICE_DBG_PTP ,"Failed to write PHY_REG_GLOBAL for port %d, err %d\n" , + port, err); +return err; + } + +usleep_range (5000 ,6000 ); + + global_val |=PHY_REG_GLOBAL_SOFT_RESET_M ; +ice_debug (hw,ICE_DBG_PTP ,"Set soft reset bit for port %d, val: 0x%x\n" , + port, global_val); + err =ice_write_ptp_reg_eth56g (hw, port,PHY_REG_GLOBAL , global_val); +if (err) { +ice_debug (hw,ICE_DBG_PTP ,"Failed to write PHY_REG_GLOBAL for port %d, err %d\n" , + port, err); +return err; + } +usleep_range (5000 ,6000 ); + + global_val &= ~PHY_REG_GLOBAL_SOFT_RESET_M ; +ice_debug (hw,ICE_DBG_PTP ,"Clear soft reset bit for port %d, val: 0x%x\n" , + port, global_val); + err =ice_write_ptp_reg_eth56g (hw, port,PHY_REG_GLOBAL , global_val); +if (err) +ice_debug (hw,ICE_DBG_PTP ,"Failed to write PHY_REG_GLOBAL for port %d, err %d\n" , + port, err); +return err; +} +/** * ice_get_phy_tx_tstamp_ready_eth56g - Read the Tx memory status register * @hw: pointer to the HW struct
@@ -5592,7 +5680,7 @@ int ice_ptp_init_phc(struct ice_hw *hw)case ICE_MAC_GENERIC :return ice_ptp_init_phc_e82x (hw);case ICE_MAC_GENERIC_3K_E825 : -return 0 ; +return ice_ptp_init_phc_e825c (hw);default :return -EOPNOTSUPP ; }
drivers/net/ethernet/intel/ice/ice_ptp_hw.h +4 -0
@@ -374,6 +374,7 @@ int ice_stop_phy_timer_eth56g(struct ice_hw *hw, u8 port, bool soft_reset);int ice_start_phy_timer_eth56g (struct ice_hw *hw,u8 port);int ice_phy_cfg_intr_eth56g (struct ice_hw *hw,u8 port,bool ena,u8 threshold);int ice_phy_cfg_ptp_1step_eth56g (struct ice_hw *hw,u8 port); +int ice_ptp_phy_soft_reset_eth56g (struct ice_hw *hw,u8 port);#define ICE_ETH56G_NOMINAL_INCVAL 0x140000000ULL#define ICE_ETH56G_NOMINAL_PCS_REF_TUS 0x100000000ULL
@@ -676,6 +677,9 @@ static inline u64 ice_get_base_incval(struct ice_hw *hw)#define ICE_P0_GNSS_PRSNT_N BIT(4)/* ETH56G PHY register addresses */ +#define PHY_REG_GLOBAL 0x0 +#define PHY_REG_GLOBAL_SOFT_RESET_M BIT(11) +/* Timestamp PHY incval registers */ #define PHY_REG_TIMETUS_L 0x8#define PHY_REG_TIMETUS_U 0xC
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 684/957 ice: fix ready bitmap check for non-E822 devices
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jacob Keller <[email protected]>
[ Upstream commit 359dc1d41358c88955eeff1b75aee55da7a415d3 ]
The E800 hardware (apart from E810) has a ready bitmap for the PHY indicating which timestamp slots currently have an outstanding timestamp waiting to be read by software.
This bitmap is checked in multiple places using the ice_get_phy_tx_tstamp_ready():
* ice_ptp_process_tx_tstamp() calls it to determine which timestamps to attempt reading from the PHY
* ice_ptp_tx_tstamps_pending() calls it in a loop at the end of the miscellaneous IRQ to check if new timestamps came in while the interrupt handler was executing.
* ice_ptp_maybe_trigger_tx_interrupt() calls it in the auxiliary work task to trigger a software interrupt in the event that the hardware logic gets stuck.
For E82X devices, multiple PHYs share the same block, and the parameter passed to the ready bitmap is a block number associated with the given port. For E825-C devices, the PHYs have their own independent blocks and do not share, so the parameter passed needs to be the port number. For E810 devices, the ice_get_phy_tx_tstamp_ready() always returns all 1s regardless of what port, since this hardware does not have a ready bitmap. Finally, for E830 devices, each PF has its own ready bitmap accessible via register, and the block parameter is unused.
The first call correctly uses the Tx timestamp tracker block parameter to check the appropriate timestamp block. This works because the tracker is setup correctly for each timestamp device type.
The second two callers behave incorrectly for all device types other than the older E822 devices. They both iterate in a loop using ICE_GET_QUAD_NUM() which is a macro only used by E822 devices. This logic is incorrect for devices other than the E822 devices.
For E810 the calls would always return true, causing E810 devices to always attempt to trigger a software interrupt even when they have no reason to. For E830, this results in duplicate work as the ready bitmap is checked once per number of quads. Finally, for E825-C, this results in the pending checks failing to detect timestamps on ports other than the first two.
Fix this by introducing a new hardware API function to ice_ptp_hw.c, ice_check_phy_tx_tstamp_ready(). This function will check if any timestamps are available and returns a positive value if any timestamps are pending. For E810, the function always returns false, so that the re-trigger checks never happen. For E830, check the ready bitmap just once. For E82x hardware, check each quad. Finally, for E825-C, check every port.
The interface function returns an integer to enable reporting of error code if the driver is unable read the ready bitmap. This enables callers to handle this case properly. The previous implementation assumed that timestamps are available if they failed to read the bitmap. This is problematic as it could lead to continuous software IRQ triggering if the PHY timestamp registers somehow become inaccessible.
This change is especially important for E825-C devices, as the missing checks could leave a window open where a new timestamp could arrive while the existing timestamps aren't completed. As a result, the hardware threshold logic would not trigger a new interrupt. Without the check, the timestamp is left unhandled, and new timestamps will not cause an interrupt again until the timestamp is handled. Since both the interrupt check and the backup check in the auxiliary task do not function properly, the device may have Tx timestamps permanently stuck failing on a given port.
The faulty checks originate from commit d938a8cca88a ("ice: Auxbus devices & driver for E822 TS") and commit 712e876371f8 ("ice: periodically kick Tx timestamp interrupt"), however at the time of the original coding, both functions only operated on E822 hardware. This is no longer the case, and hasn't been since the introduction of the ETH56G PHY model in commit 7cab44f1c35f ("ice: Introduce ETH56G PHY model for E825C products")
--- drivers/net/ethernet/intel/ice/ice_ptp.c | 44 +++----- drivers/net/ethernet/intel/ice/ice_ptp_hw.c | 117 ++++++++++++++++++++ drivers/net/ethernet/intel/ice/ice_ptp_hw.h | 1 + 3 files changed, 136 insertions(+), 26 deletions(-)
drivers/net/ethernet/intel/ice/ice_ptp.c +18 -26
@@ -2669,7 +2669,7 @@ static bool ice_any_port_has_timestamps(struct ice_pf *pf)bool ice_ptp_tx_tstamps_pending (struct ice_pf *pf) {struct ice_hw *hw = &pf->hw; -unsigned int i; +int ret;/* Check software indicator */ switch (pf->ptp.tx_interrupt_mode) {
@@ -2690,16 +2690,19 @@ bool ice_ptp_tx_tstamps_pending(struct ice_pf *pf) }/* Check hardware indicator */ -for (i =0 ; i <ICE_GET_QUAD_NUM (hw->ptp.num_lports); i++) { -u64 tstamp_ready =0 ; -int err; - - err =ice_get_phy_tx_tstamp_ready (&pf->hw, i, &tstamp_ready); -if (err || tstamp_ready) -return true; + ret =ice_check_phy_tx_tstamp_ready (hw); +if (ret <0 ) { +dev_dbg (ice_pf_to_dev (pf),"Unable to read PHY Tx timestamp ready bitmap, err %d\n" , + ret); +/* Stop triggering IRQs if we're unable to read PHY */ +return false; } -return false; +/* ice_check_phy_tx_tstamp_ready() returns 1 if there are timestamps +* available, 0 if there are no waiting timestamps, and a negative +* value if there was an error (which we checked for above). +*/ +return ret >0 ; }/**
@@ -2783,8 +2786,7 @@ static void ice_ptp_maybe_trigger_tx_interrupt(struct ice_pf *pf) {struct device *dev =ice_pf_to_dev (pf);struct ice_hw *hw = &pf->hw; -bool trigger_oicr = false; -unsigned int i; +int ret;if (!pf->ptp.port.tx.has_ready_bitmap)return ;
@@ -2792,21 +2794,11 @@ static void ice_ptp_maybe_trigger_tx_interrupt(struct ice_pf *pf)if (!ice_pf_src_tmr_owned (pf))return ; -for (i =0 ; i <ICE_GET_QUAD_NUM (hw->ptp.num_lports); i++) { -u64 tstamp_ready; -int err; - - err =ice_get_phy_tx_tstamp_ready (&pf->hw, i, &tstamp_ready); -if (!err && tstamp_ready) { - trigger_oicr = true; -break ; - } - } - -if (trigger_oicr) { -/* Trigger a software interrupt, to ensure this data -* gets processed. -*/ + ret =ice_check_phy_tx_tstamp_ready (hw); +if (ret <0 ) { +dev_dbg (dev,"PTP periodic task unable to read PHY timestamp ready bitmap, err %d\n" , + ret); + }else if (ret) {dev_dbg (dev,"PTP periodic task detected waiting timestamps. Triggering Tx timestamp interrupt now.\n" );wr32 (hw,PFINT_OICR ,PFINT_OICR_TSYN_TX_M );
drivers/net/ethernet/intel/ice/ice_ptp_hw.c +117 -0
147 lines not on this page · raw message
drivers/net/ethernet/intel/ice/ice_ptp_hw.h +1 -0
@@ -300,6 +300,7 @@ void ice_ptp_reset_ts_memory(struct ice_hw *hw);int ice_ptp_init_phc (struct ice_hw *hw);void ice_ptp_init_hw (struct ice_hw *hw);int ice_get_phy_tx_tstamp_ready (struct ice_hw *hw,u8 block,u64 *tstamp_ready); +int ice_check_phy_tx_tstamp_ready (struct ice_hw *hw);int ice_ptp_one_port_cmd (struct ice_hw *hw,u8 configured_port,enum ice_ptp_tmr_cmd configured_cmd);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 685/957 ice: fix ice_ptp_read_tx_hwtstamp_status_eth56g
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jacob Keller <[email protected]>
[ Upstream commit 1f75dbc53f68f0fb2acd99f92315e426a3d0b446 ]
The ice_ptp_read_tx_hwtstamp_status_eth56g function calls ice_read_phy_eth56g with a PHY index. However the function actually expects a port index. This causes the function to read the wrong PHY_PTP_INT_STATUS registers, and effectively makes the status wrong for the second set of ports from 4 to 7.
The ice_read_phy_eth56g function uses the provided port index to determine which PHY device to read. We could refactor the entire chain to take a PHY index, but this would impact many code sites. Instead, multiply the PHY index by the number of ports, so that we read from the first port of each PHY.
--- drivers/net/ethernet/intel/ice/ice_ptp_hw.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-)
drivers/net/ethernet/intel/ice/ice_ptp_hw.c +8 -2
@@ -2219,13 +2219,19 @@ int ice_ptp_read_tx_hwtstamp_status_eth56g(struct ice_hw *hw, u32 *ts_status) *ts_status =0 ;for (phy =0 ; phy < params->num_phys; phy++) { +u8 port;int err; - err =ice_read_phy_eth56g (hw, phy,PHY_PTP_INT_STATUS , &status); +/* ice_read_phy_eth56g expects a port index, so use the first +* port of the PHY +*/ + port = phy * hw->ptp.ports_per_phy; + + err =ice_read_phy_eth56g (hw, port,PHY_PTP_INT_STATUS , &status);if (err)return err; - *ts_status |= (status & mask) << (phy * hw->ptp.ports_per_phy); + *ts_status |= (status & mask) << port; }ice_debug (hw,ICE_DBG_PTP ,"PHY interrupt err: %x\n" , *ts_status);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 686/957 net_sched: sch_hhf: annotate data-races in hhf_dump_stats()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit a6edf2cd4156b71e07258876b7626692e158f7e8 ]
hhf_dump_stats() only runs with RTNL held, reading fields that can be changed in qdisc fast path.
Add READ_ONCE()/WRITE_ONCE() annotations.
--- net/sched/sch_hhf.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-)
net/sched/sch_hhf.c +10 -9
@@ -198,7 +198,8 @@ static struct hh_flow_state *seek_list(const u32 hash,return NULL ;list_del (&flow->flowchain);kfree (flow); - q->hh_flows_current_cnt--; +WRITE_ONCE (q->hh_flows_current_cnt, + q->hh_flows_current_cnt -1 ); }else if (flow->hash_id == hash) {return flow; }
@@ -226,7 +227,7 @@ static struct hh_flow_state *alloc_new_hh(struct list_head *head, }if (q->hh_flows_current_cnt >= q->hh_flows_limit) { - q->hh_flows_overlimit++; +WRITE_ONCE (q->hh_flows_overlimit, q->hh_flows_overlimit +1 );return NULL ; }/* Create new entry. */
@@ -234,7 +235,7 @@ static struct hh_flow_state *alloc_new_hh(struct list_head *head,if (!flow)return NULL ; - q->hh_flows_current_cnt++; +WRITE_ONCE (q->hh_flows_current_cnt, q->hh_flows_current_cnt +1 );INIT_LIST_HEAD (&flow->flowchain);list_add_tail (&flow->flowchain, head);
@@ -309,7 +310,7 @@ static enum wdrr_bucket_idx hhf_classify(struct sk_buff *skb, struct Qdisc *sch)return WDRR_BUCKET_FOR_NON_HH ; flow->hash_id = hash; flow->hit_timestamp = now; - q->hh_flows_total_cnt++; +WRITE_ONCE (q->hh_flows_total_cnt, q->hh_flows_total_cnt +1 );/* By returning without updating counters in q->hhf_arrays, * we implicitly implement "shielding" (see Optimization O1).
@@ -403,7 +404,7 @@ static int hhf_enqueue(struct sk_buff *skb, struct Qdisc *sch,return NET_XMIT_SUCCESS ; prev_backlog = sch->qstats.backlog; - q->drop_overlimit++; +WRITE_ONCE (q->drop_overlimit, q->drop_overlimit +1 );/* Return Congestion Notification only if we dropped a packet from this * bucket. */
@@ -687,10 +688,10 @@ static int hhf_dump_stats(struct Qdisc *sch, struct gnet_dump *d) {struct hhf_sched_data *q =qdisc_priv (sch);struct tc_hhf_xstats st = { - .drop_overlimit = q->drop_overlimit, - .hh_overlimit = q->hh_flows_overlimit, - .hh_tot_count = q->hh_flows_total_cnt, - .hh_cur_count = q->hh_flows_current_cnt, + .drop_overlimit =READ_ONCE (q->drop_overlimit), + .hh_overlimit =READ_ONCE (q->hh_flows_overlimit), + .hh_tot_count =READ_ONCE (q->hh_flows_total_cnt), + .hh_cur_count =READ_ONCE (q->hh_flows_current_cnt), };return gnet_stats_copy_app (d, &st,sizeof (st));
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 687/957 net/sched: sch_pie: annotate data-races in pie_dump_stats()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 5154561d9b119f781249f8e845fecf059b38b483 ]
pie_dump_stats() only runs with RTNL held, reading fields that can be changed in qdisc fast path.
Add READ_ONCE()/WRITE_ONCE() annotations.
Alternative would be to acquire the qdisc spinlock, but our long-term goal is to make qdisc dump operations lockless as much as we can.
tc_pie_xstats fields don't need to be latched atomically, otherwise this bug would have been caught earlier.
--- include/net/pie.h | 2 +- net/sched/sch_pie.c | 38 +++++++++++++++++++------------------- 2 files changed, 20 insertions(+), 20 deletions(-)
include/net/pie.h +1 -1
@@ -104,7 +104,7 @@ static inline void pie_vars_init(struct pie_vars *vars) vars->dq_tstamp =DTIME_INVALID ; vars->accu_prob =0 ; vars->dq_count =DQCOUNT_INVALID ; - vars->avg_dq_rate =0 ; +WRITE_ONCE (vars->avg_dq_rate,0 ); }static inline struct pie_skb_cb *get_pie_cb (const struct sk_buff *skb)
net/sched/sch_pie.c +19 -19
@@ -90,7 +90,7 @@ static int pie_qdisc_enqueue(struct sk_buff *skb, struct Qdisc *sch,bool enqueue = false;if (unlikely (qdisc_qlen (sch) >= sch->limit)) { - q->stats.overlimit++; +WRITE_ONCE (q->stats.overlimit, q->stats.overlimit +1 ); goto out; }
@@ -104,7 +104,7 @@ static int pie_qdisc_enqueue(struct sk_buff *skb, struct Qdisc *sch,/* If packet is ecn capable, mark it if drop probability * is lower than 10%, else drop it. */ - q->stats.ecn_mark++; +WRITE_ONCE (q->stats.ecn_mark, q->stats.ecn_mark +1 ); enqueue = true; }
@@ -114,15 +114,15 @@ static int pie_qdisc_enqueue(struct sk_buff *skb, struct Qdisc *sch,if (!q->params.dq_rate_estimator)pie_set_enqueue_time (skb); - q->stats.packets_in++; +WRITE_ONCE (q->stats.packets_in, q->stats.packets_in +1 );if (qdisc_qlen (sch) > q->stats.maxq) - q->stats.maxq =qdisc_qlen (sch); +WRITE_ONCE (q->stats.maxq,qdisc_qlen (sch));return qdisc_enqueue_tail (skb, sch); } out: - q->stats.dropped++; +WRITE_ONCE (q->stats.dropped, q->stats.dropped +1 ); q->vars.accu_prob =0 ;return qdisc_drop_reason (skb, sch, to_free, reason); }
@@ -267,11 +267,11 @@ void pie_process_dequeue(struct sk_buff *skb, struct pie_params *params, count = count / dtime;if (vars->avg_dq_rate ==0 ) - vars->avg_dq_rate = count; +WRITE_ONCE (vars->avg_dq_rate, count);else - vars->avg_dq_rate = +WRITE_ONCE (vars->avg_dq_rate, (vars->avg_dq_rate - - (vars->avg_dq_rate >>3 )) + (count >>3 ); + (vars->avg_dq_rate >>3 )) + (count >>3 ));/* If the queue has receded below the threshold, we hold * on to the last drain rate calculated, else we reset
@@ -381,7 +381,7 @@ void pie_calculate_probability(struct pie_params *params, struct pie_vars *vars,if (delta >0 ) {/* prevent overflow */ if (vars->prob < oldprob) { - vars->prob =MAX_PROB ; +WRITE_ONCE (vars->prob,MAX_PROB );/* Prevent normalization error. If probability is at * maximum value already, we normalize it here, and * skip the check to do a non-linear drop in the next
@@ -392,7 +392,7 @@ void pie_calculate_probability(struct pie_params *params, struct pie_vars *vars, }else {/* prevent underflow */ if (vars->prob > oldprob) - vars->prob =0 ; +WRITE_ONCE (vars->prob,0 ); }/* Non-linear drop in probability: Reduce drop probability quickly if
@@ -403,7 +403,7 @@ void pie_calculate_probability(struct pie_params *params, struct pie_vars *vars,/* Reduce drop probability to 98.4% */ vars->prob -= vars->prob /64 ; - vars->qdelay = qdelay; +WRITE_ONCE (vars->qdelay, qdelay); vars->backlog_old = backlog;/* We restart the measurement cycle if the following conditions are met
@@ -502,21 +502,21 @@ static int pie_dump_stats(struct Qdisc *sch, struct gnet_dump *d)struct pie_sched_data *q =qdisc_priv (sch);struct tc_pie_xstats st = { .prob = q->vars.prob <<BITS_PER_BYTE , - .delay = ((u32 )PSCHED_TICKS2NS (q->vars.qdelay)) / + .delay = ((u32 )PSCHED_TICKS2NS (READ_ONCE (q->vars.qdelay))) /NSEC_PER_USEC , - .packets_in = q->stats.packets_in, - .overlimit = q->stats.overlimit, - .maxq = q->stats.maxq, - .dropped = q->stats.dropped, - .ecn_mark = q->stats.ecn_mark, + .packets_in =READ_ONCE (q->stats.packets_in), + .overlimit =READ_ONCE (q->stats.overlimit), + .maxq =READ_ONCE (q->stats.maxq), + .dropped =READ_ONCE (q->stats.dropped), + .ecn_mark =READ_ONCE (q->stats.ecn_mark), };/* avg_dq_rate is only valid if dq_rate_estimator is enabled */ st.dq_rate_estimating = q->params.dq_rate_estimator;/* unscale and return dq_rate in bytes per sec */ -if (q->params.dq_rate_estimator) - st.avg_dq_rate = q->vars.avg_dq_rate * +if (st.dq_rate_estimating) + st.avg_dq_rate =READ_ONCE (q->vars.avg_dq_rate) * (PSCHED_TICKS_PER_SEC ) >>PIE_SCALE ;return gnet_stats_copy_app (d, &st,sizeof (st));
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 688/957 net/sched: sch_fq_codel: remove data-races from fq_codel_dump_stats()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit bbfaa73ea6871db03dc05d7f05f00557a8981f25 ]
fq_codel_dump_stats() acquires the qdisc spinlock a bit too late.
Move this acquisition before we fill st.qdisc_stats with live data.
--- net/sched/sch_fq_codel.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
net/sched/sch_fq_codel.c +2 -1
@@ -584,6 +584,8 @@ static int fq_codel_dump_stats(struct Qdisc *sch, struct gnet_dump *d) };struct list_head *pos; +sch_tree_lock (sch); + st.qdisc_stats.maxpacket = q->cstats.maxpacket; st.qdisc_stats.drop_overlimit = q->drop_overlimit; st.qdisc_stats.ecn_mark = q->cstats.ecn_mark;
@@ -592,7 +594,6 @@ static int fq_codel_dump_stats(struct Qdisc *sch, struct gnet_dump *d) st.qdisc_stats.memory_usage = q->memory_usage; st.qdisc_stats.drop_overmemory = q->drop_overmemory; -sch_tree_lock (sch);list_for_each (pos, &q->new_flows) st.qdisc_stats.new_flows_len++;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 689/957 net/sched: sch_red: annotate data-races in red_dump_stats()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit a8f5192809caf636d05ba47c144f282cfd0e3839 ]
red_dump_stats() only runs with RTNL held, reading fields that can be changed in qdisc fast path.
Add READ_ONCE()/WRITE_ONCE() annotations.
Alternative would be to acquire the qdisc spinlock, but our long-term goal is to make qdisc dump operations lockless as much as we can.
tc_red_xstats fields don't need to be latched atomically, otherwise this bug would have been caught earlier.
--- net/sched/sch_red.c | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-)
net/sched/sch_red.c +21 -10
@@ -90,17 +90,20 @@ static int red_enqueue(struct sk_buff *skb, struct Qdisc *sch,case RED_PROB_MARK :qdisc_qstats_overlimit (sch);if (!red_use_ecn (q)) { - q->stats.prob_drop++; +WRITE_ONCE (q->stats.prob_drop, + q->stats.prob_drop +1 ); goto congestion_drop; }if (INET_ECN_set_ce (skb)) { - q->stats.prob_mark++; +WRITE_ONCE (q->stats.prob_mark, + q->stats.prob_mark +1 ); skb =tcf_qevent_handle (&q->qe_mark, sch, skb, to_free, &ret);if (!skb)return NET_XMIT_CN | ret; }else if (!red_use_nodrop (q)) { - q->stats.prob_drop++; +WRITE_ONCE (q->stats.prob_drop, + q->stats.prob_drop +1 ); goto congestion_drop; }
@@ -111,17 +114,20 @@ static int red_enqueue(struct sk_buff *skb, struct Qdisc *sch, reason =SKB_DROP_REASON_QDISC_OVERLIMIT ;qdisc_qstats_overlimit (sch);if (red_use_harddrop (q) || !red_use_ecn (q)) { - q->stats.forced_drop++; +WRITE_ONCE (q->stats.forced_drop, + q->stats.forced_drop +1 ); goto congestion_drop; }if (INET_ECN_set_ce (skb)) { - q->stats.forced_mark++; +WRITE_ONCE (q->stats.forced_mark, + q->stats.forced_mark +1 ); skb =tcf_qevent_handle (&q->qe_mark, sch, skb, to_free, &ret);if (!skb)return NET_XMIT_CN | ret; }else if (!red_use_nodrop (q)) { - q->stats.forced_drop++; +WRITE_ONCE (q->stats.forced_drop, + q->stats.forced_drop +1 ); goto congestion_drop; }
@@ -135,7 +141,8 @@ static int red_enqueue(struct sk_buff *skb, struct Qdisc *sch, sch->qstats.backlog += len; sch->q.qlen++; }else if (net_xmit_drop_count (ret)) { - q->stats.pdrop++; +WRITE_ONCE (q->stats.pdrop, + q->stats.pdrop +1 );qdisc_qstats_drop (sch); }return ret;
@@ -463,9 +470,13 @@ static int red_dump_stats(struct Qdisc *sch, struct gnet_dump *d) dev->netdev_ops->ndo_setup_tc (dev,TC_SETUP_QDISC_RED , &hw_stats_request); } - st.early = q->stats.prob_drop + q->stats.forced_drop; - st.pdrop = q->stats.pdrop; - st.marked = q->stats.prob_mark + q->stats.forced_mark; + st.early =READ_ONCE (q->stats.prob_drop) + +READ_ONCE (q->stats.forced_drop); + + st.pdrop =READ_ONCE (q->stats.pdrop); + + st.marked =READ_ONCE (q->stats.prob_mark) + +READ_ONCE (q->stats.forced_mark);return gnet_stats_copy_app (d, &st,sizeof (st)); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 690/957 net/sched: sch_sfb: annotate data-races in sfb_dump_stats()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 1ada03fdef82d3d7d2edb9dcd3acc91917675e48 ]
sfb_dump_stats() only runs with RTNL held, reading fields that can be changed in qdisc fast path.
Add READ_ONCE()/WRITE_ONCE() annotations.
Alternative would be to acquire the qdisc spinlock, but our long-term goal is to make qdisc dump operations lockless as much as we can.
tc_sfb_xstats fields don't need to be latched atomically, otherwise this bug would have been caught earlier.
--- net/sched/sch_sfb.c | 54 +++++++++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 22 deletions(-)
net/sched/sch_sfb.c +32 -22
@@ -130,7 +130,7 @@ static void increment_one_qlen(u32 sfbhash, u32 slot, struct sfb_sched_data *q) sfbhash >>=SFB_BUCKET_SHIFT ;if (b[hash].qlen <0xFFFF ) - b[hash].qlen++; +WRITE_ONCE (b[hash].qlen, b[hash].qlen +1 ); b +=SFB_NUMBUCKETS ;/* next level */ } }
@@ -159,7 +159,7 @@ static void decrement_one_qlen(u32 sfbhash, u32 slot, sfbhash >>=SFB_BUCKET_SHIFT ;if (b[hash].qlen >0 ) - b[hash].qlen--; +WRITE_ONCE (b[hash].qlen, b[hash].qlen -1 ); b +=SFB_NUMBUCKETS ;/* next level */ } }
@@ -179,12 +179,12 @@ static void decrement_qlen(const struct sk_buff *skb, struct sfb_sched_data *q)static void decrement_prob (struct sfb_bucket *b,struct sfb_sched_data *q) { - b->p_mark =prob_minus (b->p_mark, q->decrement); +WRITE_ONCE (b->p_mark,prob_minus (b->p_mark, q->decrement)); }static void increment_prob (struct sfb_bucket *b,struct sfb_sched_data *q) { - b->p_mark =prob_plus (b->p_mark, q->increment); +WRITE_ONCE (b->p_mark,prob_plus (b->p_mark, q->increment)); }static void sfb_zero_all_buckets (struct sfb_sched_data *q)
@@ -202,11 +202,14 @@ static u32 sfb_compute_qlen(u32 *prob_r, u32 *avgpm_r, const struct sfb_sched_daconst struct sfb_bucket *b = &q->bins[q->slot].bins[0 ][0 ];for (i =0 ; i <SFB_LEVELS *SFB_NUMBUCKETS ; i++) { -if (qlen < b->qlen) - qlen = b->qlen; - totalpm += b->p_mark; -if (prob < b->p_mark) - prob = b->p_mark; +u32 b_qlen =READ_ONCE (b->qlen); +u32 b_mark =READ_ONCE (b->p_mark); + +if (qlen < b_qlen) + qlen = b_qlen; + totalpm += b_mark; +if (prob < b_mark) + prob = b_mark; b++; } *prob_r = prob;
@@ -295,7 +298,8 @@ static int sfb_enqueue(struct sk_buff *skb, struct Qdisc *sch,if (unlikely (sch->q.qlen >= q->limit)) {qdisc_qstats_overlimit (sch); - q->stats.queuedrop++; +WRITE_ONCE (q->stats.queuedrop, + q->stats.queuedrop +1 ); goto drop; }
@@ -348,7 +352,8 @@ static int sfb_enqueue(struct sk_buff *skb, struct Qdisc *sch,if (unlikely (minqlen >= q->max)) {qdisc_qstats_overlimit (sch); - q->stats.bucketdrop++; +WRITE_ONCE (q->stats.bucketdrop, + q->stats.bucketdrop +1 ); goto drop; }
@@ -374,7 +379,8 @@ static int sfb_enqueue(struct sk_buff *skb, struct Qdisc *sch, }if (sfb_rate_limit (skb, q)) {qdisc_qstats_overlimit (sch); - q->stats.penaltydrop++; +WRITE_ONCE (q->stats.penaltydrop, + q->stats.penaltydrop +1 ); goto drop; } goto enqueue;
@@ -390,14 +396,17 @@ static int sfb_enqueue(struct sk_buff *skb, struct Qdisc *sch,* In either case, we want to start dropping packets. */ if (r < (p_min -SFB_MAX_PROB /2 ) *2 ) { - q->stats.earlydrop++; +WRITE_ONCE (q->stats.earlydrop, + q->stats.earlydrop +1 ); goto drop; } }if (INET_ECN_set_ce (skb)) { - q->stats.marked++; +WRITE_ONCE (q->stats.marked, + q->stats.marked +1 ); }else { - q->stats.earlydrop++; +WRITE_ONCE (q->stats.earlydrop, + q->stats.earlydrop +1 ); goto drop; } }
@@ -410,7 +419,8 @@ static int sfb_enqueue(struct sk_buff *skb, struct Qdisc *sch, sch->q.qlen++;increment_qlen (&cb, q); }else if (net_xmit_drop_count (ret)) { - q->stats.childdrop++; +WRITE_ONCE (q->stats.childdrop, + q->stats.childdrop +1 );qdisc_qstats_drop (sch); }return ret;
@@ -599,12 +609,12 @@ static int sfb_dump_stats(struct Qdisc *sch, struct gnet_dump *d) {struct sfb_sched_data *q =qdisc_priv (sch);struct tc_sfb_xstats st = { - .earlydrop = q->stats.earlydrop, - .penaltydrop = q->stats.penaltydrop, - .bucketdrop = q->stats.bucketdrop, - .queuedrop = q->stats.queuedrop, - .childdrop = q->stats.childdrop, - .marked = q->stats.marked, + .earlydrop =READ_ONCE (q->stats.earlydrop), + .penaltydrop =READ_ONCE (q->stats.penaltydrop), + .bucketdrop =READ_ONCE (q->stats.bucketdrop), + .queuedrop =READ_ONCE (q->stats.queuedrop), + .childdrop =READ_ONCE (q->stats.childdrop), + .marked =READ_ONCE (q->stats.marked), }; st.maxqlen =sfb_compute_qlen (&st.maxprob, &st.avgprob, q);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 691/957 net: airoha: ppe: Dynamically allocate foe_check_time array in airoha_ppe struct
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lorenzo Bianconi <[email protected]>
[ Upstream commit 6d5b601d52a27aafff555b480e538507901c672c ]
This is a preliminary patch to properly enable PPE support for AN7583 SoC.
Stable-dep-of: 3309965fe44c ("net: airoha: Add missing bits in airoha_qdma_cleanup_tx_queue()")
--- drivers/net/ethernet/airoha/airoha_eth.h | 2 +- drivers/net/ethernet/airoha/airoha_ppe.c | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-)
drivers/net/ethernet/airoha/airoha_eth.h +1 -1
@@ -548,7 +548,7 @@ struct airoha_ppe {struct rhashtable l2_flows;struct hlist_head *foe_flow; -u16 foe_check_time[PPE_NUM_ENTRIES ]; +u16 *foe_check_time;struct airoha_foe_stats *foe_stats;dma_addr_t foe_stats_dma;
drivers/net/ethernet/airoha/airoha_ppe.c +5 -0
@@ -1538,6 +1538,11 @@ int airoha_ppe_init(struct airoha_eth *eth)return -ENOMEM ; } + ppe->foe_check_time =devm_kzalloc (eth->dev,PPE_NUM_ENTRIES , +GFP_KERNEL ); +if (!ppe->foe_check_time) +return -ENOMEM ; + err =rhashtable_init (ð->flow_table, &airoha_flow_table_params);if (err)return err;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 692/957 net: airoha: ppe: Move PPE memory info in airoha_eth_soc_data struct
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lorenzo Bianconi <[email protected]>
[ Upstream commit 5bd1d1fd48ea9f8300b211540d946899c7f96480 ]
AN7583 SoC runs a single PPE device while EN7581 runs two of them. Moreover PPE SRAM in AN7583 SoC is reduced to 8K (while SRAM is 16K on EN7581). Take into account PPE memory layout during PPE configuration.
Stable-dep-of: 3309965fe44c ("net: airoha: Add missing bits in airoha_qdma_cleanup_tx_queue()")
--- drivers/net/ethernet/airoha/airoha_eth.h | 10 +- drivers/net/ethernet/airoha/airoha_ppe.c | 133 +++++++++--------- .../net/ethernet/airoha/airoha_ppe_debugfs.c | 3 +- 3 files changed, 70 insertions(+), 76 deletions(-)
drivers/net/ethernet/airoha/airoha_eth.h +3 -7
@@ -47,14 +47,9 @@#define QDMA_METER_IDX (_n) ((_n) & 0xff)#define QDMA_METER_GROUP (_n) (((_n) >> 8) & 0x3) -#define PPE_NUM 2 -#define PPE1_SRAM_NUM_ENTRIES (8 * 1024) -#define PPE_SRAM_NUM_ENTRIES (PPE_NUM * PPE1_SRAM_NUM_ENTRIES) -#define PPE1_STATS_NUM_ENTRIES (4 * 1024) -#define PPE_STATS_NUM_ENTRIES (PPE_NUM * PPE1_STATS_NUM_ENTRIES) +#define PPE_SRAM_NUM_ENTRIES (8 * 1024) +#define PPE_STATS_NUM_ENTRIES (4 * 1024)#define PPE_DRAM_NUM_ENTRIES (16 * 1024) -#define PPE_NUM_ENTRIES (PPE_SRAM_NUM_ENTRIES + PPE_DRAM_NUM_ENTRIES) -#define PPE_HASH_MASK (PPE_NUM_ENTRIES - 1)#define PPE_ENTRY_SIZE 80#define PPE_RAM_NUM_ENTRIES_SHIFT (_n) (__ffs((_n) >> 10))
@@ -635,6 +630,7 @@ int airoha_ppe_setup_tc_block_cb(struct airoha_ppe_dev *dev, void *type_data);int airoha_ppe_init (struct airoha_eth *eth);void airoha_ppe_deinit (struct airoha_eth *eth);void airoha_ppe_init_upd_mem (struct airoha_gdm_port *port); +u32 airoha_ppe_get_total_num_entries (struct airoha_ppe *ppe);struct airoha_foe_entry *airoha_ppe_foe_get_entry (struct airoha_ppe *ppe,u32 hash); void airoha_ppe_foe_entry_get_stats(struct airoha_ppe *ppe, u32 hash,
drivers/net/ethernet/airoha/airoha_ppe.c +65 -68
279 lines not on this page · raw message
drivers/net/ethernet/airoha/airoha_ppe_debugfs.c +2 -1
@@ -53,9 +53,10 @@ static int airoha_ppe_debugfs_foe_show(struct seq_file *m, void *private, [AIROHA_FOE_STATE_FIN] = "FIN", };struct airoha_ppe *ppe = m->private; +u32 ppe_num_entries =airoha_ppe_get_total_num_entries (ppe);int i; -for (i =0 ; i <PPE_NUM_ENTRIES ; i++) { +for (i =0 ; i < ppe_num_entries; i++) {const char *state_str, *type_str ="UNKNOWN" ;void *src_addr =NULL , *dest_addr =NULL ;u16 *src_port =NULL , *dest_port =NULL ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 693/957 net: airoha: Refactor src port configuration in airhoha_set_gdm2_loopback
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lorenzo Bianconi <[email protected]>
[ Upstream commit 9d5b5219f672c80bed4d4e15f0068e648cdca43b ]
AN7583 chipset relies on different definitions for source-port identifier used for hw offloading. In order to support hw offloading in AN7583 controller, refactor src port configuration in airhoha_set_gdm2_loopback routine and introduce get_src_port_id callback.
Stable-dep-of: 3309965fe44c ("net: airoha: Add missing bits in airoha_qdma_cleanup_tx_queue()")
--- drivers/net/ethernet/airoha/airoha_eth.c | 82 ++++++++++++++++------- drivers/net/ethernet/airoha/airoha_eth.h | 18 +++-- drivers/net/ethernet/airoha/airoha_regs.h | 6 +- 3 files changed, 73 insertions(+), 33 deletions(-)
drivers/net/ethernet/airoha/airoha_eth.c +57 -25
@@ -1751,13 +1751,17 @@ static int airoha_dev_set_macaddr(struct net_device *dev, void *p)return 0 ; } -static void airhoha_set_gdm2_loopback (struct airoha_gdm_port *port) +static int airhoha_set_gdm2_loopback (struct airoha_gdm_port *port) { -u32 pse_port = port->id ==3 ?FE_PSE_PORT_GDM3 :FE_PSE_PORT_GDM4 ; +u32 val, pse_port, chan = port->id ==AIROHA_GDM3_IDX ?4 :0 ;struct airoha_eth *eth = port->qdma->eth; -u32 chan = port->id ==3 ?4 :0 ; +/* XXX: handle XSI_USB_PORT and XSI_PCE1_PORT */ +u32 nbq = port->id ==AIROHA_GDM3_IDX ?4 :0 ; +int src_port;/* Forward the traffic to the proper GDM port */ + pse_port = port->id ==AIROHA_GDM3_IDX ?FE_PSE_PORT_GDM3 + :FE_PSE_PORT_GDM4 ;airoha_set_gdm_port_fwd_cfg (eth,REG_GDM_FWD_CFG (2 ), pse_port);airoha_fe_clear (eth,REG_GDM_FWD_CFG (2 ),GDM_STRIP_CRC );
@@ -1778,29 +1782,25 @@ static void airhoha_set_gdm2_loopback(struct airoha_gdm_port *port)airoha_fe_clear (eth,REG_FE_VIP_PORT_EN ,BIT (2 ));airoha_fe_clear (eth,REG_FE_IFC_PORT_EN ,BIT (2 )); -if (port->id ==3 ) { -/* FIXME: handle XSI_PCE1_PORT */ -airoha_fe_rmw (eth,REG_FE_WAN_PORT , -WAN1_EN_MASK |WAN1_MASK |WAN0_MASK , -FIELD_PREP (WAN0_MASK ,HSGMII_LAN_PCIE0_SRCPORT )); -airoha_fe_rmw (eth, -REG_SP_DFT_CPORT (HSGMII_LAN_PCIE0_SRCPORT >>3 ), -SP_CPORT_PCIE0_MASK , -FIELD_PREP (SP_CPORT_PCIE0_MASK , -FE_PSE_PORT_CDM2 )); - }else { -/* FIXME: handle XSI_USB_PORT */ + src_port = eth->soc->ops.get_src_port_id (port, nbq); +if (src_port <0 ) +return src_port; + +airoha_fe_rmw (eth,REG_FE_WAN_PORT , +WAN1_EN_MASK |WAN1_MASK |WAN0_MASK , +FIELD_PREP (WAN0_MASK , src_port)); + val = src_port &SP_CPORT_DFT_MASK ; +airoha_fe_rmw (eth, +REG_SP_DFT_CPORT (src_port >>fls (SP_CPORT_DFT_MASK )), +SP_CPORT_MASK (val), +FE_PSE_PORT_CDM2 <<__ffs (SP_CPORT_MASK (val))); + +if (port->id !=AIROHA_GDM3_IDX )airoha_fe_rmw (eth,REG_SRC_PORT_FC_MAP6 ,FC_ID_OF_SRC_PORT24_MASK ,FIELD_PREP (FC_ID_OF_SRC_PORT24_MASK ,2 )); -airoha_fe_rmw (eth,REG_FE_WAN_PORT , -WAN1_EN_MASK |WAN1_MASK |WAN0_MASK , -FIELD_PREP (WAN0_MASK ,HSGMII_LAN_ETH_SRCPORT )); -airoha_fe_rmw (eth, -REG_SP_DFT_CPORT (HSGMII_LAN_ETH_SRCPORT >>3 ), -SP_CPORT_ETH_MASK , -FIELD_PREP (SP_CPORT_ETH_MASK ,FE_PSE_PORT_CDM2 )); - } + +return 0 ; }static int airoha_dev_init (struct net_device *dev)
@@ -1815,8 +1815,13 @@ static int airoha_dev_init(struct net_device *dev)case 3 :case 4 :/* If GDM2 is active we can't enable loopback */ -if (!eth->ports[1 ]) -airhoha_set_gdm2_loopback (port); +if (!eth->ports[1 ]) { +int err; + + err =airhoha_set_gdm2_loopback (port); +if (err) +return err; + } fallthrough;case 2 :if (airoha_ppe_is_enabled (eth,1 )) {
@@ -3135,11 +3140,38 @@ static const char * const en7581_xsi_rsts_names[] = { "xfp-mac", }; +static int airoha_en7581_get_src_port_id (struct airoha_gdm_port *port,int nbq) +{ +switch (port->id) { +case 3 : +/* 7581 SoC supports PCIe serdes on GDM3 port */ +if (nbq ==4 ) +return HSGMII_LAN_7581_PCIE0_SRCPORT ; +if (nbq ==5 ) +return HSGMII_LAN_7581_PCIE1_SRCPORT ; +break ; +case 4 : +/* 7581 SoC supports eth and usb serdes on GDM4 port */ +if (!nbq) +return HSGMII_LAN_7581_ETH_SRCPORT ; +if (nbq ==1 ) +return HSGMII_LAN_7581_USB_SRCPORT ; +break ; +default : +break ; + } + +return -EINVAL ; +} +static const struct airoha_eth_soc_data en7581_soc_data = { .version =0x7581 , .xsi_rsts_names = en7581_xsi_rsts_names, .num_xsi_rsts =ARRAY_SIZE (en7581_xsi_rsts_names), .num_ppe =2 , + .ops = { + .get_src_port_id = airoha_en7581_get_src_port_id, + }, };static const struct of_device_id of_airoha_match[] = {
drivers/net/ethernet/airoha/airoha_eth.h +14 -4
@@ -67,10 +67,10 @@ enum { };enum { -HSGMII_LAN_PCIE0_SRCPORT =0x16 , -HSGMII_LAN_PCIE1_SRCPORT , -HSGMII_LAN_ETH_SRCPORT , -HSGMII_LAN_USB_SRCPORT , +HSGMII_LAN_7581_PCIE0_SRCPORT =0x16 , +HSGMII_LAN_7581_PCIE1_SRCPORT , +HSGMII_LAN_7581_ETH_SRCPORT , +HSGMII_LAN_7581_USB_SRCPORT , };enum {
@@ -99,6 +99,13 @@ enum { CRSN_25 = 0x19, }; +enum airoha_gdm_index { +AIROHA_GDM1_IDX =1 , +AIROHA_GDM2_IDX =2 , +AIROHA_GDM3_IDX =3 , +AIROHA_GDM4_IDX =4 , +}; +enum {FE_PSE_PORT_CDM1 ,FE_PSE_PORT_GDM1 ,
@@ -556,6 +563,9 @@ struct airoha_eth_soc_data {const char *const *xsi_rsts_names;int num_xsi_rsts;int num_ppe; +struct { +int (*get_src_port_id)(struct airoha_gdm_port *port,int nbq); + } ops; };struct airoha_eth {
drivers/net/ethernet/airoha/airoha_regs.h +2 -4
@@ -383,10 +383,8 @@#define REG_MC_VLAN_DATA 0x2108#define REG_SP_DFT_CPORT (_n) (0x20e0 + ((_n) << 2)) -#define SP_CPORT_PCIE1_MASK GENMASK(31, 28) -#define SP_CPORT_PCIE0_MASK GENMASK(27, 24) -#define SP_CPORT_USB_MASK GENMASK(7, 4) -#define SP_CPORT_ETH_MASK GENMASK(7, 4) +#define SP_CPORT_DFT_MASK GENMASK(2, 0) +#define SP_CPORT_MASK (_n) GENMASK(3 + ((_n) << 2), ((_n) << 2))#define REG_SRC_PORT_FC_MAP6 0x2298#define FC_ID_OF_SRC_PORT27_MASK GENMASK(28, 24)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 694/957 net: airoha: Add AN7583 SoC support
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lorenzo Bianconi <[email protected]>
[ Upstream commit e4e5ce823bdd4601bd75ae7c206ae35e7c2fa60b ]
Introduce support for AN7583 ethernet controller to airoha-eth dirver.
Stable-dep-of: 3309965fe44c ("net: airoha: Add missing bits in airoha_qdma_cleanup_tx_queue()")
--- drivers/net/ethernet/airoha/airoha_eth.c | 68 ++++++++++++++++++++++-- drivers/net/ethernet/airoha/airoha_eth.h | 11 ++++ drivers/net/ethernet/airoha/airoha_ppe.c | 3 ++ 3 files changed, 77 insertions(+), 5 deletions(-)
drivers/net/ethernet/airoha/airoha_eth.c +63 -5
@@ -1753,10 +1753,8 @@ static int airoha_dev_set_macaddr(struct net_device *dev, void *p)static int airhoha_set_gdm2_loopback (struct airoha_gdm_port *port) { -u32 val, pse_port, chan = port->id ==AIROHA_GDM3_IDX ?4 :0 ;struct airoha_eth *eth = port->qdma->eth; -/* XXX: handle XSI_USB_PORT and XSI_PCE1_PORT */ -u32 nbq = port->id ==AIROHA_GDM3_IDX ?4 :0 ; +u32 val, pse_port, chan, nbq;int src_port;/* Forward the traffic to the proper GDM port */
@@ -1768,6 +1766,8 @@ static int airhoha_set_gdm2_loopback(struct airoha_gdm_port *port)/* Enable GDM2 loopback */ airoha_fe_wr (eth,REG_GDM_TXCHN_EN (2 ),0xffffffff );airoha_fe_wr (eth,REG_GDM_RXCHN_EN (2 ),0xffff ); + + chan = port->id ==AIROHA_GDM3_IDX ?airoha_is_7581 (eth) ?4 :3 :0 ; airoha_fe_rmw(eth, REG_GDM_LPBK_CFG(2), LPBK_CHAN_MASK | LPBK_MODE_MASK | LPBK_EN_MASK, FIELD_PREP(LPBK_CHAN_MASK, chan) |
@@ -1782,6 +1782,8 @@ static int airhoha_set_gdm2_loopback(struct airoha_gdm_port *port)airoha_fe_clear (eth,REG_FE_VIP_PORT_EN ,BIT (2 ));airoha_fe_clear (eth,REG_FE_IFC_PORT_EN ,BIT (2 )); +/* XXX: handle XSI_USB_PORT and XSI_PCE1_PORT */ + nbq = port->id ==AIROHA_GDM3_IDX &&airoha_is_7581 (eth) ?4 :0 ; src_port = eth->soc->ops.get_src_port_id (port, nbq);if (src_port <0 )return src_port;
@@ -1795,7 +1797,7 @@ static int airhoha_set_gdm2_loopback(struct airoha_gdm_port *port)SP_CPORT_MASK (val), FE_PSE_PORT_CDM2 << __ffs(SP_CPORT_MASK(val))); -if (port->id !=AIROHA_GDM3_IDX ) +if (port->id !=AIROHA_GDM3_IDX &&airoha_is_7581 (eth))airoha_fe_rmw (eth,REG_SRC_PORT_FC_MAP6 ,FC_ID_OF_SRC_PORT24_MASK ,FIELD_PREP (FC_ID_OF_SRC_PORT24_MASK ,2 ));
@@ -1951,6 +1953,22 @@ static bool airoha_dev_tx_queue_busy(struct airoha_queue *q, u32 nr_frags)return index >= tail; } +static int airoha_get_fe_port (struct airoha_gdm_port *port) +{ +struct airoha_qdma *qdma = port->qdma; +struct airoha_eth *eth = qdma->eth; + +switch (eth->soc->version) { +case 0x7583 : +return port->id ==AIROHA_GDM3_IDX ?FE_PSE_PORT_GDM3 + : port->id; +case 0x7581 : +default : +return port->id ==AIROHA_GDM4_IDX ?FE_PSE_PORT_GDM4 + : port->id; + } +} +static netdev_tx_t airoha_dev_xmit (struct sk_buff *skb,struct net_device *dev) {
@@ -1991,7 +2009,7 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb, } } - fport = port->id ==4 ?FE_PSE_PORT_GDM4 : port->id; + fport =airoha_get_fe_port (port); msg1 =FIELD_PREP (QDMA_ETH_TXMSG_FPORT_MASK , fport) |FIELD_PREP (QDMA_ETH_TXMSG_METER_MASK ,0x7f );
@@ -3164,6 +3182,35 @@ static int airoha_en7581_get_src_port_id(struct airoha_gdm_port *port, int nbq)return -EINVAL ; } +static const char *const an7583_xsi_rsts_names[] = { +"xsi-mac" , +"hsi0-mac" , +"hsi1-mac" , +"xfp-mac" , +}; + +static int airoha_an7583_get_src_port_id (struct airoha_gdm_port *port,int nbq) +{ +switch (port->id) { +case 3 : +/* 7583 SoC supports eth serdes on GDM3 port */ +if (!nbq) +return HSGMII_LAN_7583_ETH_SRCPORT ; +break ; +case 4 : +/* 7583 SoC supports PCIe and USB serdes on GDM4 port */ +if (!nbq) +return HSGMII_LAN_7583_PCIE_SRCPORT ; +if (nbq ==1 ) +return HSGMII_LAN_7583_USB_SRCPORT ; +break ; +default : +break ; + } + +return -EINVAL ; +} +static const struct airoha_eth_soc_data en7581_soc_data = { .version =0x7581 , .xsi_rsts_names = en7581_xsi_rsts_names,
@@ -3174,8 +3221,19 @@ static const struct airoha_eth_soc_data en7581_soc_data = { }, }; +static const struct airoha_eth_soc_data an7583_soc_data = { + .version =0x7583 , + .xsi_rsts_names = an7583_xsi_rsts_names, + .num_xsi_rsts =ARRAY_SIZE (an7583_xsi_rsts_names), + .num_ppe =1 , + .ops = { + .get_src_port_id = airoha_an7583_get_src_port_id, + }, +}; +static const struct of_device_id of_airoha_match[] = { { .compatible ="airoha,en7581-eth" , .data = &en7581_soc_data }, + { .compatible ="airoha,an7583-eth" , .data = &an7583_soc_data }, {/* sentinel */ } };MODULE_DEVICE_TABLE (of, of_airoha_match);
drivers/net/ethernet/airoha/airoha_eth.h +11 -0
@@ -73,6 +73,12 @@ enum { HSGMII_LAN_7581_USB_SRCPORT, }; +enum { +HSGMII_LAN_7583_ETH_SRCPORT =0x16 , +HSGMII_LAN_7583_PCIE_SRCPORT =0x18 , +HSGMII_LAN_7583_USB_SRCPORT , +}; +enum {XSI_PCIE0_VIP_PORT_MASK =BIT (22 ),XSI_PCIE1_VIP_PORT_MASK =BIT (23 ),
@@ -630,6 +636,11 @@ static inline bool airoha_is_7581(struct airoha_eth *eth)return eth->soc->version ==0x7581 ; } +static inline bool airoha_is_7583 (struct airoha_eth *eth) +{ +return eth->soc->version ==0x7583 ; +} +bool airoha_is_valid_gdm_port (struct airoha_eth *eth,struct airoha_gdm_port *port);
drivers/net/ethernet/airoha/airoha_ppe.c +3 -0
@@ -37,6 +37,9 @@ static int airoha_ppe_get_num_stats_entries(struct airoha_ppe *ppe)if (!IS_ENABLED (CONFIG_NET_AIROHA_FLOW_STATS ))return -EOPNOTSUPP ; +if (airoha_is_7583 (ppe->eth)) +return -EOPNOTSUPP ; +return PPE_STATS_NUM_ENTRIES ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 695/957 net: airoha: Add the capability to consume out-of-order DMA tx descriptors
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lorenzo Bianconi <[email protected]>
[ Upstream commit 3f47e67dff1f7266e112c50313d63824f6f17102 ]
EN7581 and AN7583 SoCs are capable of DMA mapping non-linear tx skbs on non-consecutive DMA descriptors. This feature is useful when multiple flows are queued on the same hw tx queue since it allows to fully utilize the available tx DMA descriptors and to avoid the starvation of high-priority flow we have in the current codebase due to head-of-line blocking introduced by low-priority flows.
Stable-dep-of: 3309965fe44c ("net: airoha: Add missing bits in airoha_qdma_cleanup_tx_queue()")
--- drivers/net/ethernet/airoha/airoha_eth.c | 85 +++++++++++------------- drivers/net/ethernet/airoha/airoha_eth.h | 7 +- 2 files changed, 45 insertions(+), 47 deletions(-)
drivers/net/ethernet/airoha/airoha_eth.c +39 -46
@@ -952,19 +952,13 @@ static int airoha_qdma_tx_napi_poll(struct napi_struct *napi, int budget)dma_unmap_single (eth->dev, e->dma_addr, e->dma_len,DMA_TO_DEVICE ); -memset (e,0 ,sizeof (*e)); + e->dma_addr =0 ; +list_add_tail (&e->list, &q->tx_list); +WRITE_ONCE (desc->msg0,0 );WRITE_ONCE (desc->msg1,0 ); q->queued--; -/* completion ring can report out-of-order indexes if hw QoS -* is enabled and packets with different priority are queued -* to same DMA ring. Take into account possible out-of-order -* reports incrementing DMA ring tail pointer -*/ -while (q->tail != q->head && !q->entry[q->tail].dma_addr) - q->tail = (q->tail +1 ) % q->ndesc; -if (skb) {u16 queue =skb_get_queue_mapping (skb);struct netdev_queue *txq;
@@ -1018,6 +1012,7 @@ static int airoha_qdma_init_tx_queue(struct airoha_queue *q, q->ndesc = size; q->qdma = qdma; q->free_thr =1 +MAX_SKB_FRAGS ; +INIT_LIST_HEAD (&q->tx_list); q->entry =devm_kzalloc (eth->dev, q->ndesc *sizeof (*q->entry),GFP_KERNEL );
@@ -1030,9 +1025,9 @@ static int airoha_qdma_init_tx_queue(struct airoha_queue *q,return -ENOMEM ;for (i =0 ; i < q->ndesc; i++) { -u32 val; +u32 val =FIELD_PREP (QDMA_DESC_DONE_MASK ,1 ); - val =FIELD_PREP (QDMA_DESC_DONE_MASK ,1 ); +list_add_tail (&q->entry[i].list, &q->tx_list);WRITE_ONCE (q->desc[i].ctrl,cpu_to_le32 (val)); }
@@ -1042,9 +1037,9 @@ static int airoha_qdma_init_tx_queue(struct airoha_queue *q,airoha_qdma_wr (qdma,REG_TX_RING_BASE (qid), dma_addr);airoha_qdma_rmw (qdma,REG_TX_CPU_IDX (qid),TX_RING_CPU_IDX_MASK , -FIELD_PREP (TX_RING_CPU_IDX_MASK , q->head)); +FIELD_PREP (TX_RING_CPU_IDX_MASK ,0 ));airoha_qdma_rmw (qdma,REG_TX_DMA_IDX (qid),TX_RING_DMA_IDX_MASK , -FIELD_PREP (TX_RING_DMA_IDX_MASK , q->head)); +FIELD_PREP (TX_RING_DMA_IDX_MASK ,0 ));return 0 ; }
@@ -1100,17 +1095,21 @@ static int airoha_qdma_init_tx(struct airoha_qdma *qdma)static void airoha_qdma_cleanup_tx_queue (struct airoha_queue *q) {struct airoha_eth *eth = q->qdma->eth; +int i;spin_lock_bh (&q->lock); -while (q->queued) { -struct airoha_queue_entry *e = &q->entry[q->tail]; +for (i =0 ; i < q->ndesc; i++) { +struct airoha_queue_entry *e = &q->entry[i]; + +if (!e->dma_addr) +continue ;dma_unmap_single (eth->dev, e->dma_addr, e->dma_len,DMA_TO_DEVICE );dev_kfree_skb_any (e->skb); + e->dma_addr =0 ; e->skb =NULL ; - - q->tail = (q->tail +1 ) % q->ndesc; +list_add_tail (&e->list, &q->tx_list); q->queued--; }spin_unlock_bh (&q->lock);
@@ -1939,20 +1938,6 @@ static u32 airoha_get_dsa_tag(struct sk_buff *skb, struct net_device *dev)#endif } -static bool airoha_dev_tx_queue_busy (struct airoha_queue *q,u32 nr_frags) -{ -u32 tail = q->tail <= q->head ? q->tail + q->ndesc : q->tail; -u32 index = q->head + nr_frags; - -/* completion napi can free out-of-order tx descriptors if hw QoS is -* enabled and packets with different priorities are queued to the same -* DMA ring. Take into account possible out-of-order reports checking -* if the tx queue is full using circular buffer head/tail pointers -* instead of the number of queued packets. -*/ -return index >= tail; -} -static int airoha_get_fe_port (struct airoha_gdm_port *port) {struct airoha_qdma *qdma = port->qdma;
@@ -1975,8 +1960,10 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,struct airoha_gdm_port *port =netdev_priv (dev);struct airoha_qdma *qdma = port->qdma;u32 nr_frags, tag, msg0, msg1, len; +struct airoha_queue_entry *e;struct netdev_queue *txq;struct airoha_queue *q; +LIST_HEAD (tx_list);void *data;int i, qid;u16 index;
@@ -2022,7 +2009,7 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb, txq =netdev_get_tx_queue (dev, qid); nr_frags =1 +skb_shinfo (skb)->nr_frags; -if (airoha_dev_tx_queue_busy (q, nr_frags)) { +if (q->queued + nr_frags >= q->ndesc) {/* not enough space in the queue */ netif_tx_stop_queue (txq); q->txq_stopped = true;
@@ -2032,11 +2019,13 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb, len =skb_headlen (skb); data = skb->data; - index = q->head; + + e = list_first_entry(&q->tx_list, struct airoha_queue_entry, + list); + index = e - q->entry;for (i =0 ; i < nr_frags; i++) {struct airoha_qdma_desc *desc = &q->desc[index]; -struct airoha_queue_entry *e = &q->entry[index];skb_frag_t *frag = &skb_shinfo (skb)->frags[i];dma_addr_t addr;u32 val;
@@ -2046,7 +2035,14 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,if (unlikely (dma_mapping_error (dev->dev.parent, addr))) goto error_unmap; - index = (index +1 ) % q->ndesc; +list_move_tail (&e->list, &tx_list); + e->skb = i ?NULL : skb; + e->dma_addr = addr; + e->dma_len = len; + + e = list_first_entry(&q->tx_list, struct airoha_queue_entry, + list); + index = e - q->entry; val =FIELD_PREP (QDMA_DESC_LEN_MASK , len);if (i < nr_frags -1 )
@@ -2059,15 +2055,9 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,WRITE_ONCE (desc->msg1,cpu_to_le32 (msg1));WRITE_ONCE (desc->msg2,cpu_to_le32 (0xffff )); - e->skb = i ?NULL : skb; - e->dma_addr = addr; - e->dma_len = len; - data =skb_frag_address (frag); len =skb_frag_size (frag); } - - q->head = index; q->queued += i;skb_tx_timestamp (skb);
@@ -2076,7 +2066,7 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,if (netif_xmit_stopped (txq) || !netdev_xmit_more ())airoha_qdma_rmw (qdma,REG_TX_CPU_IDX (qid),TX_RING_CPU_IDX_MASK , -FIELD_PREP (TX_RING_CPU_IDX_MASK , q->head)); +FIELD_PREP (TX_RING_CPU_IDX_MASK , index));if (q->ndesc - q->queued < q->free_thr) {netif_tx_stop_queue (txq);
@@ -2088,10 +2078,13 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,return NETDEV_TX_OK ; error_unmap: -for (i--; i >=0 ; i--) { - index = (q->head + i) % q->ndesc; -dma_unmap_single (dev->dev.parent, q->entry[index].dma_addr, - q->entry[index].dma_len,DMA_TO_DEVICE ); +while (!list_empty (&tx_list)) { + e = list_first_entry(&tx_list, struct airoha_queue_entry, + list); +dma_unmap_single (dev->dev.parent, e->dma_addr, e->dma_len, +DMA_TO_DEVICE ); + e->dma_addr =0 ; +list_move_tail (&e->list, &q->tx_list); }spin_unlock_bh (&q->lock);
drivers/net/ethernet/airoha/airoha_eth.h +6 -1
@@ -169,7 +169,10 @@ enum trtcm_param {struct airoha_queue_entry {union {void *buf; -struct sk_buff *skb; +struct { +struct list_head list; +struct sk_buff *skb; + }; };dma_addr_t dma_addr;u16 dma_len;
@@ -194,6 +197,8 @@ struct airoha_queue {struct napi_struct napi;struct page_pool *page_pool;struct sk_buff *skb; + +struct list_head tx_list; };struct airoha_tx_irq_queue {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 696/957 net: airoha: Add missing bits in airoha_qdma_cleanup_tx_queue()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lorenzo Bianconi <[email protected]>
[ Upstream commit 3309965fe44c00fd65af7cef5016e9e782c021a7 ]
Similar to airoha_qdma_cleanup_rx_queue(), reset DMA TX descriptors in airoha_qdma_cleanup_tx_queue routine. Moreover, reset TX_DMA_IDX to TX_CPU_IDX to notify the NIC the QDMA TX ring is empty.
--- drivers/net/ethernet/airoha/airoha_eth.c | 32 ++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-)
drivers/net/ethernet/airoha/airoha_eth.c +30 -2
@@ -1094,12 +1094,15 @@ static int airoha_qdma_init_tx(struct airoha_qdma *qdma)static void airoha_qdma_cleanup_tx_queue (struct airoha_queue *q) { -struct airoha_eth *eth = q->qdma->eth; -int i; +struct airoha_qdma *qdma = q->qdma; +struct airoha_eth *eth = qdma->eth; +int i, qid = q - &qdma->q_tx[0 ]; +u16 index =0 ;spin_lock_bh (&q->lock);for (i =0 ; i < q->ndesc; i++) {struct airoha_queue_entry *e = &q->entry[i]; +struct airoha_qdma_desc *desc = &q->desc[i];if (!e->dma_addr)continue ;
@@ -1110,8 +1113,33 @@ static void airoha_qdma_cleanup_tx_queue(struct airoha_queue *q) e->dma_addr =0 ; e->skb =NULL ;list_add_tail (&e->list, &q->tx_list); + +/* Reset DMA descriptor */ +WRITE_ONCE (desc->ctrl,0 ); +WRITE_ONCE (desc->addr,0 ); +WRITE_ONCE (desc->data,0 ); +WRITE_ONCE (desc->msg0,0 ); +WRITE_ONCE (desc->msg1,0 ); +WRITE_ONCE (desc->msg2,0 ); + q->queued--; } + +if (!list_empty (&q->tx_list)) { +struct airoha_queue_entry *e; + + e = list_first_entry(&q->tx_list, struct airoha_queue_entry, + list); + index = e - q->entry; + } +/* Set TX_DMA_IDX to TX_CPU_IDX to notify the hw the QDMA TX ring is +* empty. +*/ +airoha_qdma_rmw (qdma,REG_TX_CPU_IDX (qid),TX_RING_CPU_IDX_MASK , +FIELD_PREP (TX_RING_CPU_IDX_MASK , index)); +airoha_qdma_rmw (qdma,REG_TX_DMA_IDX (qid),TX_RING_DMA_IDX_MASK , +FIELD_PREP (TX_RING_DMA_IDX_MASK , index)); +spin_unlock_bh (&q->lock); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 697/957 net: dsa: realtek: rtl8365mb: fix mode mask calculation
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mieczyslaw Nalewaj <[email protected]>
[ Upstream commit 0c078021d3861966614d5e594ee03587f0c9e74d ]
The RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_MASK macro was shifting the 4-bit mask (0xF) by only (_extint % 2) bits instead of (_extint % 2) * 4. This caused the mask to overlap with the adjacent nibble when configuring odd-numbered external interfaces, selecting the wrong bits entirely.
Align the shift calculation with the existing ...MODE_OFFSET macro.
--- drivers/net/dsa/realtek/rtl8365mb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/dsa/realtek/rtl8365mb.c +1 -1
@@ -216,7 +216,7 @@ (_extint) == 2 ? RTL8365MB_DIGITAL_INTERFACE_SELECT_REG1 : \ 0x0)#define RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_MASK (_extint) \ - (0xF << (((_extint) % 2))) + (0xF << (((_extint) % 2) * 4))#define RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_OFFSET (_extint) \ (((_extint) % 2) * 4)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 698/957 net: airoha: Move ndesc initialization at end of airoha_qdma_init_rx_queue()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lorenzo Bianconi <[email protected]>
[ Upstream commit 379050947a1828826ad7ea50c95245a56929b35a ]
If queue entry or DMA descriptor list allocation fails in airoha_qdma_init_rx_queue routine, airoha_qdma_cleanup() will trigger a NULL pointer dereference running netif_napi_del() for RX queue NAPIs since netif_napi_add() has never been executed to this particular RX NAPI. The issue is due to the early ndesc initialization in airoha_qdma_init_rx_queue() since airoha_qdma_cleanup() relies on ndesc value to check if the queue is properly initialized. Fix the issue moving ndesc initialization at end of airoha_qdma_init_tx routine. Move page_pool allocation after descriptor list allocation in order to avoid memory leaks if desc allocation fails.
--- drivers/net/ethernet/airoha/airoha_eth.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)
drivers/net/ethernet/airoha/airoha_eth.c +7 -7
@@ -776,14 +776,18 @@ static int airoha_qdma_init_rx_queue(struct airoha_queue *q,dma_addr_t dma_addr; q->buf_size =PAGE_SIZE /2 ; - q->ndesc = ndesc; q->qdma = qdma; - q->entry =devm_kzalloc (eth->dev, q->ndesc *sizeof (*q->entry), + q->entry =devm_kzalloc (eth->dev, ndesc *sizeof (*q->entry),GFP_KERNEL );if (!q->entry)return -ENOMEM ; + q->desc =dmam_alloc_coherent (eth->dev, ndesc *sizeof (*q->desc), + &dma_addr,GFP_KERNEL ); +if (!q->desc) +return -ENOMEM ; + q->page_pool =page_pool_create (&pp_params);if (IS_ERR (q->page_pool)) {int err =PTR_ERR (q->page_pool);
@@ -792,11 +796,7 @@ static int airoha_qdma_init_rx_queue(struct airoha_queue *q,return err; } - q->desc =dmam_alloc_coherent (eth->dev, q->ndesc *sizeof (*q->desc), - &dma_addr,GFP_KERNEL ); -if (!q->desc) -return -ENOMEM ; - + q->ndesc = ndesc;netif_napi_add (eth->napi_dev, &q->napi, airoha_qdma_rx_napi_poll);airoha_qdma_wr (qdma,REG_RX_RING_BASE (qid), dma_addr);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 699/957 net: airoha: Rework the code flow in airoha_remove() and in airoha_probe() error path
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lorenzo Bianconi <[email protected]>
[ Upstream commit b1c803d5c8167026791abfaed96fd3e6a1fcd750 ]
As suggested by Simon in [0], rework the code flow in airoha_remove() and in the airoha_probe() error path in order to rely on a more common approach un-registering configured net-devices first and destroying the hw resources at the end of the code.
Introduce airoha_qdma_cleanup routine to release QDMA resources.
[0] https://lore.kernel.org/netdev/20251214-airoha-fix-dev-registration-v1-1-860e027ad4c6@kernel.org/
Stable-dep-of: 4b91cb65789b ("net: airoha: Add size check for TX NAPIs in airoha_qdma_cleanup()")
--- drivers/net/ethernet/airoha/airoha_eth.c | 76 ++++++++++++++---------- 1 file changed, 44 insertions(+), 32 deletions(-)
drivers/net/ethernet/airoha/airoha_eth.c +44 -32
@@ -1465,6 +1465,33 @@ static int airoha_qdma_init(struct platform_device *pdev,return airoha_qdma_hw_init (qdma); } +static void airoha_qdma_cleanup (struct airoha_qdma *qdma) +{ +int i; + +for (i =0 ; i <ARRAY_SIZE (qdma->q_rx); i++) { +if (!qdma->q_rx[i].ndesc) +continue ; + +netif_napi_del (&qdma->q_rx[i].napi); +airoha_qdma_cleanup_rx_queue (&qdma->q_rx[i]); +if (qdma->q_rx[i].page_pool) { +page_pool_destroy (qdma->q_rx[i].page_pool); + qdma->q_rx[i].page_pool =NULL ; + } + } + +for (i =0 ; i <ARRAY_SIZE (qdma->q_tx_irq); i++) +netif_napi_del (&qdma->q_tx_irq[i].napi); + +for (i =0 ; i <ARRAY_SIZE (qdma->q_tx); i++) { +if (!qdma->q_tx[i].ndesc) +continue ; + +airoha_qdma_cleanup_tx_queue (&qdma->q_tx[i]); + } +} +static int airoha_hw_init (struct platform_device *pdev,struct airoha_eth *eth) {
@@ -1492,41 +1519,30 @@ static int airoha_hw_init(struct platform_device *pdev,for (i =0 ; i <ARRAY_SIZE (eth->qdma); i++) { err =airoha_qdma_init (pdev, eth, ð->qdma[i]);if (err) -return err; + goto error; } err =airoha_ppe_init (eth);if (err) -return err; + goto error;set_bit (DEV_STATE_INITIALIZED , ð->state);return 0 ; +error: +for (i =0 ; i <ARRAY_SIZE (eth->qdma); i++) +airoha_qdma_cleanup (ð->qdma[i]); + +return err; } -static void airoha_hw_cleanup (struct airoha_qdma *qdma) +static void airoha_hw_cleanup (struct airoha_eth *eth) {int i; -for (i =0 ; i <ARRAY_SIZE (qdma->q_rx); i++) { -if (!qdma->q_rx[i].ndesc) -continue ; - -netif_napi_del (&qdma->q_rx[i].napi); -airoha_qdma_cleanup_rx_queue (&qdma->q_rx[i]); -if (qdma->q_rx[i].page_pool) -page_pool_destroy (qdma->q_rx[i].page_pool); - } - -for (i =0 ; i <ARRAY_SIZE (qdma->q_tx_irq); i++) -netif_napi_del (&qdma->q_tx_irq[i].napi); - -for (i =0 ; i <ARRAY_SIZE (qdma->q_tx); i++) { -if (!qdma->q_tx[i].ndesc) -continue ; - -airoha_qdma_cleanup_tx_queue (&qdma->q_tx[i]); - } +for (i =0 ; i <ARRAY_SIZE (eth->qdma); i++) +airoha_qdma_cleanup (ð->qdma[i]); +airoha_ppe_deinit (eth); }static void airoha_qdma_start_napi (struct airoha_qdma *qdma)
@@ -3096,7 +3112,7 @@ static int airoha_probe(struct platform_device *pdev) err =airoha_hw_init (pdev, eth);if (err) - goto error_hw_cleanup; + goto error_netdev_free;for (i =0 ; i <ARRAY_SIZE (eth->qdma); i++)airoha_qdma_start_napi (ð->qdma[i]);
@@ -3125,10 +3141,6 @@ static int airoha_probe(struct platform_device *pdev) error_napi_stop:for (i =0 ; i <ARRAY_SIZE (eth->qdma); i++)airoha_qdma_stop_napi (ð->qdma[i]); -airoha_ppe_deinit (eth); -error_hw_cleanup: -for (i =0 ; i <ARRAY_SIZE (eth->qdma); i++) -airoha_hw_cleanup (ð->qdma[i]);for (i =0 ; i <ARRAY_SIZE (eth->ports); i++) {struct airoha_gdm_port *port = eth->ports[i];
@@ -3140,6 +3152,8 @@ static int airoha_probe(struct platform_device *pdev)unregister_netdev (port->dev);airoha_metadata_dst_free (port); } +airoha_hw_cleanup (eth); +error_netdev_free:free_netdev (eth->napi_dev);platform_set_drvdata (pdev,NULL );
@@ -3151,10 +3165,8 @@ static void airoha_remove(struct platform_device *pdev)struct airoha_eth *eth =platform_get_drvdata (pdev);int i; -for (i =0 ; i <ARRAY_SIZE (eth->qdma); i++) { +for (i =0 ; i <ARRAY_SIZE (eth->qdma); i++)airoha_qdma_stop_napi (ð->qdma[i]); -airoha_hw_cleanup (ð->qdma[i]); - }for (i =0 ; i <ARRAY_SIZE (eth->ports); i++) {struct airoha_gdm_port *port = eth->ports[i];
@@ -3165,9 +3177,9 @@ static void airoha_remove(struct platform_device *pdev)unregister_netdev (port->dev);airoha_metadata_dst_free (port); } -free_netdev (eth->napi_dev); +airoha_hw_cleanup (eth); -airoha_ppe_deinit (eth); +free_netdev (eth->napi_dev);platform_set_drvdata (pdev,NULL ); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 700/957 net: airoha: Add size check for TX NAPIs in airoha_qdma_cleanup()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lorenzo Bianconi <[email protected]>
[ Upstream commit 4b91cb65789b794bfc8d50554b8994f8e0f16309 ]
If airoha_qdma_init routine fails before airoha_qdma_tx_irq_init() runs successfully for all TX NAPIs, airoha_qdma_cleanup() will unconditionally runs netif_napi_del() on TX NAPIs, triggering a NULL pointer dereference. Fix the issue relying on q_tx_irq size value to check if the TX NAPIs is properly initialized in airoha_qdma_cleanup(). Moreover, run netif_napi_add_tx() just if irq_q queue is properly allocated.
--- drivers/net/ethernet/airoha/airoha_eth.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-)
drivers/net/ethernet/airoha/airoha_eth.c +8 -3
@@ -1051,8 +1051,6 @@ static int airoha_qdma_tx_irq_init(struct airoha_tx_irq_queue *irq_q,struct airoha_eth *eth = qdma->eth;dma_addr_t dma_addr; -netif_napi_add_tx (eth->napi_dev, &irq_q->napi, - airoha_qdma_tx_napi_poll); irq_q->q =dmam_alloc_coherent (eth->dev, size *sizeof (u32), &dma_addr,GFP_KERNEL );if (!irq_q->q)
@@ -1062,6 +1060,9 @@ static int airoha_qdma_tx_irq_init(struct airoha_tx_irq_queue *irq_q, irq_q->size = size; irq_q->qdma = qdma; +netif_napi_add_tx (eth->napi_dev, &irq_q->napi, + airoha_qdma_tx_napi_poll); +airoha_qdma_wr (qdma,REG_TX_IRQ_BASE (id), dma_addr);airoha_qdma_rmw (qdma,REG_TX_IRQ_CFG (id),TX_IRQ_DEPTH_MASK ,FIELD_PREP (TX_IRQ_DEPTH_MASK , size));
@@ -1481,8 +1482,12 @@ static void airoha_qdma_cleanup(struct airoha_qdma *qdma) } } -for (i =0 ; i <ARRAY_SIZE (qdma->q_tx_irq); i++) +for (i =0 ; i <ARRAY_SIZE (qdma->q_tx_irq); i++) { +if (!qdma->q_tx_irq[i].size) +continue ; +netif_napi_del (&qdma->q_tx_irq[i].napi); + }for (i =0 ; i <ARRAY_SIZE (qdma->q_tx); i++) {if (!qdma->q_tx[i].ndesc)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 701/957 net: mana: Init link_change_work before potential error paths in probe
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Erni Sri Satya Vennela <[email protected]>
[ Upstream commit cb4a90744bcd1adf12f0d0c7c4f0dd2647444ec5 ]
Move INIT_WORK(link_change_work) to right after the mana_context allocation, before any error path that could reach mana_remove().
Previously, if mana_create_eq() or mana_query_device_cfg() failed, mana_probe() would jump to the error path which calls mana_remove(). mana_remove() unconditionally calls disable_work_sync(link_change_work), but the work struct had not been initialized yet. This can trigger CONFIG_DEBUG_OBJECTS_WORK enabled.
--- drivers/net/ethernet/microsoft/mana/mana_en.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/net/ethernet/microsoft/mana/mana_en.c +2 -2
@@ -3483,6 +3483,8 @@ int mana_probe(struct gdma_dev *gd, bool resuming) ac->gdma_dev = gd; gd->driver_data = ac; + +INIT_WORK (&ac->link_change_work, mana_link_state_handle); } err =mana_create_eq (ac);
@@ -3500,8 +3502,6 @@ int mana_probe(struct gdma_dev *gd, bool resuming)if (!resuming) { ac->num_ports = num_ports; - -INIT_WORK (&ac->link_change_work, mana_link_state_handle); }else {if (ac->num_ports != num_ports) { dev_err(dev, "The number of vPorts changed: %d->%d\n",
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 702/957 net: mana: Guard mana_remove against double invocation
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Erni Sri Satya Vennela <[email protected]>
[ Upstream commit 50271d7ec95144d26808025b508f463780517d3c ]
If PM resume fails (e.g., mana_attach() returns an error), mana_probe() calls mana_remove(), which tears down the device and sets gd->gdma_context = NULL and gd->driver_data = NULL.
However, a failed resume callback does not automatically unbind the driver. When the device is eventually unbound, mana_remove() is invoked a second time. Without a NULL check, it dereferences gc->dev with gc == NULL, causing a kernel panic.
Add an early return if gdma_context or driver_data is NULL so the second invocation is harmless. Move the dev = gc->dev assignment after the guard so it cannot dereference NULL.
--- drivers/net/ethernet/microsoft/mana/mana_en.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
drivers/net/ethernet/microsoft/mana/mana_en.c +6 -1
@@ -3568,11 +3568,16 @@ void mana_remove(struct gdma_dev *gd, bool suspending)struct gdma_context *gc = gd->gdma_context;struct mana_context *ac = gd->driver_data;struct mana_port_context *apc; -struct device *dev = gc->dev; +struct device *dev;struct net_device *ndev;int err;int i; +if (!gc || !ac) +return ; + + dev = gc->dev; +disable_work_sync (&ac->link_change_work);/* adev currently doesn't support suspending, always remove it */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 703/957 net: mana: Move hardware counter stats from per-port to per-VF context
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Erni Sri Satya Vennela <[email protected]>
[ Upstream commit e275d9091c01b3b46f3ec534ce4ac77cffc9e3ae ]
Move hardware counter (HC) statistics from mana_port_context to mana_context to enable sharing stats across multiple network ports on the same MANA VF. Previously, each network port queried hardware counters independently using MANA_QUERY_GF_STAT command (GF = Generic Function stats from GDMA hardware), resulting in redundant queries when multiple ports existed on the same device.
Isolate hardware counter stats by introducing mana_ethtool_hc_stats in mana_context and update the code to ensure all stats are properly reported via ethtool -S <interface>, maintaining consistency with previous behavior.
Stable-dep-of: a7fdaf069bd0 ("net: mana: Don't overwrite port probe error with add_adev result")
--- drivers/net/ethernet/microsoft/mana/mana_en.c | 67 ++++++++------- .../ethernet/microsoft/mana/mana_ethtool.c | 85 ++++++++++--------- include/net/mana/mana.h | 14 +-- 3 files changed, 90 insertions(+), 76 deletions(-)
drivers/net/ethernet/microsoft/mana/mana_en.c +34 -33
@@ -2836,11 +2836,12 @@ int mana_config_rss(struct mana_port_context *apc, enum TRI_STATE rx,return 0 ; } -void mana_query_gf_stats (struct mana_port_context *apc) +void mana_query_gf_stats (struct mana_context *ac) { +struct gdma_context *gc = ac->gdma_dev->gdma_context;struct mana_query_gf_stat_resp resp = {};struct mana_query_gf_stat_req req = {}; -struct net_device *ndev = apc->ndev; +struct device *dev = gc->dev;int err; mana_gd_init_req_hdr(&req.hdr, MANA_QUERY_GF_STAT,
@@ -2874,52 +2875,52 @@ void mana_query_gf_stats(struct mana_port_context *apc)STATISTICS_FLAGS_HC_TX_BCAST_BYTES |STATISTICS_FLAGS_TX_ERRORS_GDMA_ERROR ; - err =mana_send_request (apc->ac, &req,sizeof (req), &resp, + err =mana_send_request (ac, &req,sizeof (req), &resp,sizeof (resp));if (err) { -netdev_err (ndev,"Failed to query GF stats: %d\n" , err); +dev_err (dev,"Failed to query GF stats: %d\n" , err);return ; } err =mana_verify_resp_hdr (&resp.hdr,MANA_QUERY_GF_STAT ,sizeof (resp));if (err || resp.hdr.status) { -netdev_err (ndev,"Failed to query GF stats: %d, 0x%x\n" , err, - resp.hdr.status); +dev_err (dev,"Failed to query GF stats: %d, 0x%x\n" , err, + resp.hdr.status);return ; } - apc->eth_stats.hc_rx_discards_no_wqe = resp.rx_discards_nowqe; - apc->eth_stats.hc_rx_err_vport_disabled = resp.rx_err_vport_disabled; - apc->eth_stats.hc_rx_bytes = resp.hc_rx_bytes; - apc->eth_stats.hc_rx_ucast_pkts = resp.hc_rx_ucast_pkts; - apc->eth_stats.hc_rx_ucast_bytes = resp.hc_rx_ucast_bytes; - apc->eth_stats.hc_rx_bcast_pkts = resp.hc_rx_bcast_pkts; - apc->eth_stats.hc_rx_bcast_bytes = resp.hc_rx_bcast_bytes; - apc->eth_stats.hc_rx_mcast_pkts = resp.hc_rx_mcast_pkts; - apc->eth_stats.hc_rx_mcast_bytes = resp.hc_rx_mcast_bytes; - apc->eth_stats.hc_tx_err_gf_disabled = resp.tx_err_gf_disabled; - apc->eth_stats.hc_tx_err_vport_disabled = resp.tx_err_vport_disabled; - apc->eth_stats.hc_tx_err_inval_vportoffset_pkt = + ac->hc_stats.hc_rx_discards_no_wqe = resp.rx_discards_nowqe; + ac->hc_stats.hc_rx_err_vport_disabled = resp.rx_err_vport_disabled; + ac->hc_stats.hc_rx_bytes = resp.hc_rx_bytes; + ac->hc_stats.hc_rx_ucast_pkts = resp.hc_rx_ucast_pkts; + ac->hc_stats.hc_rx_ucast_bytes = resp.hc_rx_ucast_bytes; + ac->hc_stats.hc_rx_bcast_pkts = resp.hc_rx_bcast_pkts; + ac->hc_stats.hc_rx_bcast_bytes = resp.hc_rx_bcast_bytes; + ac->hc_stats.hc_rx_mcast_pkts = resp.hc_rx_mcast_pkts; + ac->hc_stats.hc_rx_mcast_bytes = resp.hc_rx_mcast_bytes; + ac->hc_stats.hc_tx_err_gf_disabled = resp.tx_err_gf_disabled; + ac->hc_stats.hc_tx_err_vport_disabled = resp.tx_err_vport_disabled; + ac->hc_stats.hc_tx_err_inval_vportoffset_pkt = resp.tx_err_inval_vport_offset_pkt; - apc->eth_stats.hc_tx_err_vlan_enforcement = + ac->hc_stats.hc_tx_err_vlan_enforcement = resp.tx_err_vlan_enforcement; - apc->eth_stats.hc_tx_err_eth_type_enforcement = + ac->hc_stats.hc_tx_err_eth_type_enforcement = resp.tx_err_ethtype_enforcement; - apc->eth_stats.hc_tx_err_sa_enforcement = resp.tx_err_SA_enforcement; - apc->eth_stats.hc_tx_err_sqpdid_enforcement = + ac->hc_stats.hc_tx_err_sa_enforcement = resp.tx_err_SA_enforcement; + ac->hc_stats.hc_tx_err_sqpdid_enforcement = resp.tx_err_SQPDID_enforcement; - apc->eth_stats.hc_tx_err_cqpdid_enforcement = + ac->hc_stats.hc_tx_err_cqpdid_enforcement = resp.tx_err_CQPDID_enforcement; - apc->eth_stats.hc_tx_err_mtu_violation = resp.tx_err_mtu_violation; - apc->eth_stats.hc_tx_err_inval_oob = resp.tx_err_inval_oob; - apc->eth_stats.hc_tx_bytes = resp.hc_tx_bytes; - apc->eth_stats.hc_tx_ucast_pkts = resp.hc_tx_ucast_pkts; - apc->eth_stats.hc_tx_ucast_bytes = resp.hc_tx_ucast_bytes; - apc->eth_stats.hc_tx_bcast_pkts = resp.hc_tx_bcast_pkts; - apc->eth_stats.hc_tx_bcast_bytes = resp.hc_tx_bcast_bytes; - apc->eth_stats.hc_tx_mcast_pkts = resp.hc_tx_mcast_pkts; - apc->eth_stats.hc_tx_mcast_bytes = resp.hc_tx_mcast_bytes; - apc->eth_stats.hc_tx_err_gdma = resp.tx_err_gdma; + ac->hc_stats.hc_tx_err_mtu_violation = resp.tx_err_mtu_violation; + ac->hc_stats.hc_tx_err_inval_oob = resp.tx_err_inval_oob; + ac->hc_stats.hc_tx_bytes = resp.hc_tx_bytes; + ac->hc_stats.hc_tx_ucast_pkts = resp.hc_tx_ucast_pkts; + ac->hc_stats.hc_tx_ucast_bytes = resp.hc_tx_ucast_bytes; + ac->hc_stats.hc_tx_bcast_pkts = resp.hc_tx_bcast_pkts; + ac->hc_stats.hc_tx_bcast_bytes = resp.hc_tx_bcast_bytes; + ac->hc_stats.hc_tx_mcast_pkts = resp.hc_tx_mcast_pkts; + ac->hc_stats.hc_tx_mcast_bytes = resp.hc_tx_mcast_bytes; + ac->hc_stats.hc_tx_err_gdma = resp.tx_err_gdma; }void mana_query_phy_stats (struct mana_port_context *apc)
drivers/net/ethernet/microsoft/mana/mana_ethtool.c +47 -38
149 lines not on this page · raw message
include/net/mana/mana.h +9 -5
@@ -375,6 +375,13 @@ struct mana_tx_qp {struct mana_ethtool_stats {u64 stop_queue;u64 wake_queue; +u64 tx_cqe_err; +u64 tx_cqe_unknown_type; +u64 rx_coalesced_err; +u64 rx_cqe_unknown_type; +}; + +struct mana_ethtool_hc_stats {u64 hc_rx_discards_no_wqe;u64 hc_rx_err_vport_disabled;u64 hc_rx_bytes;
@@ -402,10 +409,6 @@ struct mana_ethtool_stats {u64 hc_tx_mcast_pkts;u64 hc_tx_mcast_bytes;u64 hc_tx_err_gdma; -u64 tx_cqe_err; -u64 tx_cqe_unknown_type; -u64 rx_coalesced_err; -u64 rx_cqe_unknown_type; };struct mana_ethtool_phy_stats {
@@ -473,6 +476,7 @@ struct mana_context {u16 num_ports;u8 bm_hostmode; +struct mana_ethtool_hc_stats hc_stats;struct mana_eq *eqs;struct dentry *mana_eqs_debugfs;
@@ -578,7 +582,7 @@ u32 mana_run_xdp(struct net_device *ndev, struct mana_rxq *rxq,struct bpf_prog *mana_xdp_get (struct mana_port_context *apc);void mana_chn_setxdp (struct mana_port_context *apc,struct bpf_prog *prog);int mana_bpf (struct net_device *ndev,struct netdev_bpf *bpf); -void mana_query_gf_stats (struct mana_port_context *apc); +void mana_query_gf_stats (struct mana_context *ac);int mana_query_link_cfg (struct mana_port_context *apc);int mana_set_bw_clamp (struct mana_port_context *apc,u32 speed,int enable_clamping);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 704/957 net: mana: Add standard counter rx_missed_errors
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Erni Sri Satya Vennela <[email protected]>
[ Upstream commit be4f1d67ec56f23f37714ac73c01094e63c7ff28 ]
Report standard counter stats->rx_missed_errors
using hc_rx_discards_no_wqe from the hardware.
Add a global workqueue to periodically run mana_query_gf_stats every 2 seconds to get the latest info in eth_stats and define a driver capability flag to notify hardware of the periodic queries.
To avoid repeated failures and log flooding, the workqueue is not rescheduled if mana_query_gf_stats fails on HWC timeout error and the stats are reset to 0. Other errors are transient which will not need a VF reset for recovery.
Stable-dep-of: a7fdaf069bd0 ("net: mana: Don't overwrite port probe error with add_adev result")
--- drivers/net/ethernet/microsoft/mana/mana_en.c | 36 +++++++++++++++++-- .../ethernet/microsoft/mana/mana_ethtool.c | 2 -- include/net/mana/gdma.h | 6 +++- include/net/mana/mana.h | 6 +++- 4 files changed, 43 insertions(+), 7 deletions(-)
drivers/net/ethernet/microsoft/mana/mana_en.c +33 -3
@@ -534,6 +534,11 @@ static void mana_get_stats64(struct net_device *ndev,netdev_stats_to_stats64 (st, &ndev->stats); +if (apc->ac->hwc_timeout_occurred) +netdev_warn_once (ndev,"HWC timeout occurred\n" ); + + st->rx_missed_errors = apc->ac->hc_stats.hc_rx_discards_no_wqe; +for (q =0 ; q < num_queues; q++) { rx_stats = &apc->rxqs[q]->stats;
@@ -2836,7 +2841,7 @@ int mana_config_rss(struct mana_port_context *apc, enum TRI_STATE rx,return 0 ; } -void mana_query_gf_stats (struct mana_context *ac) +int mana_query_gf_stats (struct mana_context *ac) {struct gdma_context *gc = ac->gdma_dev->gdma_context;struct mana_query_gf_stat_resp resp = {};
@@ -2879,14 +2884,14 @@ void mana_query_gf_stats(struct mana_context *ac) sizeof(resp));if (err) {dev_err (dev,"Failed to query GF stats: %d\n" , err); -return ; +return err; } err =mana_verify_resp_hdr (&resp.hdr,MANA_QUERY_GF_STAT ,sizeof (resp));if (err || resp.hdr.status) {dev_err (dev,"Failed to query GF stats: %d, 0x%x\n" , err, resp.hdr.status); -return ; +return err; } ac->hc_stats.hc_rx_discards_no_wqe = resp.rx_discards_nowqe;
@@ -2921,6 +2926,8 @@ void mana_query_gf_stats(struct mana_context *ac) ac->hc_stats.hc_tx_mcast_pkts = resp.hc_tx_mcast_pkts; ac->hc_stats.hc_tx_mcast_bytes = resp.hc_tx_mcast_bytes; ac->hc_stats.hc_tx_err_gdma = resp.tx_err_gdma; + +return 0 ; }void mana_query_phy_stats (struct mana_port_context *apc)
@@ -3459,6 +3466,24 @@ int mana_rdma_service_event(struct gdma_context *gc, enum gdma_service_type evenreturn 0 ; } +#define MANA_GF_STATS_PERIOD (2 * HZ) + +static void mana_gf_stats_work_handler (struct work_struct *work) +{ +struct mana_context *ac = + container_of(to_delayed_work(work), struct mana_context, gf_stats_work); +int err; + + err =mana_query_gf_stats (ac); +if (err == -ETIMEDOUT ) { +/* HWC timeout detected - reset stats and stop rescheduling */ + ac->hwc_timeout_occurred = true; +memset (&ac->hc_stats,0 ,sizeof (ac->hc_stats)); +return ; + } +schedule_delayed_work (&ac->gf_stats_work,MANA_GF_STATS_PERIOD ); +} +int mana_probe (struct gdma_dev *gd,bool resuming) {struct gdma_context *gc = gd->gdma_context;
@@ -3551,6 +3576,10 @@ int mana_probe(struct gdma_dev *gd, bool resuming) } err =add_adev (gd,"eth" ); + +INIT_DELAYED_WORK (&ac->gf_stats_work, mana_gf_stats_work_handler); +schedule_delayed_work (&ac->gf_stats_work,MANA_GF_STATS_PERIOD ); + out:if (err) {mana_remove (gd, false);
@@ -3580,6 +3609,7 @@ void mana_remove(struct gdma_dev *gd, bool suspending) dev = gc->dev;disable_work_sync (&ac->link_change_work); +cancel_delayed_work_sync (&ac->gf_stats_work);/* adev currently doesn't support suspending, always remove it */ if (gd->adev)
drivers/net/ethernet/microsoft/mana/mana_ethtool.c +0 -2
@@ -213,8 +213,6 @@ static void mana_get_ethtool_stats(struct net_device *ndev,if (!apc->port_is_up)return ; -/* we call mana function to update stats from GDMA */ -mana_query_gf_stats (apc->ac);/* We call this mana function to get the phy stats from GDMA and includes * aggregate tx/rx drop counters, Per-TC(Traffic Channel) tx/rx and pause
include/net/mana/gdma.h +5 -1
@@ -592,6 +592,9 @@ enum {#define GDMA_DRV_CAP_FLAG_1_HANDLE_RECONFIG_EQE BIT(17)#define GDMA_DRV_CAP_FLAG_1_HW_VPORT_LINK_AWARE BIT(6) +/* Driver can send HWC periodically to query stats */ +#define GDMA_DRV_CAP_FLAG_1_PERIODIC_STATS_QUERY BIT(21) +#define GDMA_DRV_CAP_FLAGS1 \ (GDMA_DRV_CAP_FLAG_1_EQ_SHARING_MULTI_VPORT | \ GDMA_DRV_CAP_FLAG_1_NAPI_WKDONE_FIX | \
@@ -601,7 +604,8 @@ enum {GDMA_DRV_CAP_FLAG_1_DYNAMIC_IRQ_ALLOC_SUPPORT | \GDMA_DRV_CAP_FLAG_1_SELF_RESET_ON_EQE | \GDMA_DRV_CAP_FLAG_1_HANDLE_RECONFIG_EQE | \ - GDMA_DRV_CAP_FLAG_1_HW_VPORT_LINK_AWARE) +GDMA_DRV_CAP_FLAG_1_HW_VPORT_LINK_AWARE | \ + GDMA_DRV_CAP_FLAG_1_PERIODIC_STATS_QUERY) #define GDMA_DRV_CAP_FLAGS2 0
include/net/mana/mana.h +5 -1
@@ -480,6 +480,10 @@ struct mana_context {struct mana_eq *eqs;struct dentry *mana_eqs_debugfs; +/* Workqueue for querying hardware stats */ +struct delayed_work gf_stats_work; +bool hwc_timeout_occurred; +struct net_device *ports[MAX_PORTS_IN_MANA_DEV ];/* Link state change work */
@@ -582,7 +586,7 @@ u32 mana_run_xdp(struct net_device *ndev, struct mana_rxq *rxq,struct bpf_prog *mana_xdp_get (struct mana_port_context *apc);void mana_chn_setxdp (struct mana_port_context *apc,struct bpf_prog *prog);int mana_bpf (struct net_device *ndev,struct netdev_bpf *bpf); -void mana_query_gf_stats (struct mana_context *ac); +int mana_query_gf_stats (struct mana_context *ac);int mana_query_link_cfg (struct mana_port_context *apc);int mana_set_bw_clamp (struct mana_port_context *apc,u32 speed,int enable_clamping);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 705/957 net: mana: Dont overwrite port probe error with add_adev result
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Erni Sri Satya Vennela <[email protected]>
[ Upstream commit a7fdaf069bd031fcc234581fa6a580be11bf2175 ]
In mana_probe(), if mana_probe_port() fails for any port, the error is stored in 'err' and the loop breaks. However, the subsequent unconditional 'err = add_adev(gd, "eth")' overwrites this error. If add_adev() succeeds, mana_probe() returns success despite ports being left in a partially initialized state (ac->ports[i] == NULL).
Only call add_adev() when there is no prior error, so the probe correctly fails and triggers mana_remove() cleanup.
--- drivers/net/ethernet/microsoft/mana/mana_en.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-)
drivers/net/ethernet/microsoft/mana/mana_en.c +8 -9
@@ -3548,10 +3548,9 @@ int mana_probe(struct gdma_dev *gd, bool resuming)if (!resuming) {for (i =0 ; i < ac->num_ports; i++) { err =mana_probe_port (ac, i, &ac->ports[i]); -/* we log the port for which the probe failed and stop -* probes for subsequent ports. -* Note that we keep running ports, for which the probes -* were successful, unless add_adev fails too +/* Log the port for which the probe failed, stop probing +* subsequent ports, and skip add_adev. +* mana_remove() will clean up already-probed ports. */ if (err) {dev_err (dev,"Probe Failed for port %d\n" , i);
@@ -3563,10 +3562,9 @@ int mana_probe(struct gdma_dev *gd, bool resuming)rtnl_lock (); err =mana_attach (ac->ports[i]);rtnl_unlock (); -/* we log the port for which the attach failed and stop -* attach for subsequent ports -* Note that we keep running ports, for which the attach -* were successful, unless add_adev fails too +/* Log the port for which the attach failed, stop +* attaching subsequent ports, and skip add_adev. +* mana_remove() will clean up already-attached ports. */ if (err) {dev_err (dev,"Attach Failed for port %d\n" , i);
@@ -3575,7 +3573,8 @@ int mana_probe(struct gdma_dev *gd, bool resuming) } } - err =add_adev (gd,"eth" ); +if (!err) + err =add_adev (gd,"eth" );INIT_DELAYED_WORK (&ac->gf_stats_work, mana_gf_stats_work_handler);schedule_delayed_work (&ac->gf_stats_work,MANA_GF_STATS_PERIOD );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 706/957 net: mana: Handle SKB if TX SGEs exceed hardware limit
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Aditya Garg <[email protected]>
[ Upstream commit 934fa943b53795339486cc0026b3ab7ad39dc600 ]
The MANA hardware supports a maximum of 30 scatter-gather entries (SGEs) per TX WQE. Exceeding this limit can cause TX failures.
Add ndo_features_check() callback to validate SKB layout before transmission. For GSO SKBs that would exceed the hardware SGE limit, clear NETIF_F_GSO_MASK to enforce software segmentation in the stack. Add a fallback in mana_start_xmit() to linearize non-GSO SKBs that still exceed the SGE limit.
Also, Add ethtool counter for SKBs linearized
Stable-dep-of: 65267c9c4f28 ("net: mana: Fix EQ leak in mana_remove on NULL port")
--- drivers/net/ethernet/microsoft/mana/mana_en.c | 40 ++++++++++++++++++- .../ethernet/microsoft/mana/mana_ethtool.c | 2 + include/net/mana/gdma.h | 8 +++- include/net/mana/mana.h | 1 + 4 files changed, 48 insertions(+), 3 deletions(-)
drivers/net/ethernet/microsoft/mana/mana_en.c +38 -2
@@ -11,6 +11,7 @@#include <linux/mm.h> #include <linux/pci.h> #include <linux/export.h> +#include <linux/skbuff.h> #include <net/checksum.h> #include <net/ip6_checksum.h>
@@ -329,6 +330,21 @@ netdev_tx_t mana_start_xmit(struct sk_buff *skb, struct net_device *ndev) cq = &apc->tx_qp[txq_idx].tx_cq; tx_stats = &txq->stats; +BUILD_BUG_ON (MAX_TX_WQE_SGL_ENTRIES !=MANA_MAX_TX_WQE_SGL_ENTRIES ); +if (MAX_SKB_FRAGS +2 >MAX_TX_WQE_SGL_ENTRIES && +skb_shinfo (skb)->nr_frags +2 >MAX_TX_WQE_SGL_ENTRIES ) { +/* GSO skb with Hardware SGE limit exceeded is not expected here +* as they are handled in mana_features_check() callback +*/ +if (skb_linearize (skb)) { +netdev_warn_once (ndev,"Failed to linearize skb with nr_frags=%d and is_gso=%d\n" , +skb_shinfo (skb)->nr_frags, +skb_is_gso (skb)); + goto tx_drop_count; + } + apc->eth_stats.tx_linear_pkt_cnt++; + } + pkg.tx_oob.s_oob.vcq_num = cq->gdma_id; pkg.tx_oob.s_oob.vsq_frame = txq->vsq_frame;
@@ -442,8 +458,6 @@ netdev_tx_t mana_start_xmit(struct sk_buff *skb, struct net_device *ndev) } } -WARN_ON_ONCE (pkg.wqe_req.num_sge >MAX_TX_WQE_SGL_ENTRIES ); -if (pkg.wqe_req.num_sge <=ARRAY_SIZE (pkg.sgl_array)) { pkg.wqe_req.sgl = pkg.sgl_array; }else {
@@ -518,6 +532,25 @@ netdev_tx_t mana_start_xmit(struct sk_buff *skb, struct net_device *ndev)return NETDEV_TX_OK ; } +#if (MAX_SKB_FRAGS +2 >MANA_MAX_TX_WQE_SGL_ENTRIES ) +static netdev_features_t mana_features_check (struct sk_buff *skb, +struct net_device *ndev, +netdev_features_t features) +{ +if (skb_shinfo (skb)->nr_frags +2 >MAX_TX_WQE_SGL_ENTRIES ) { +/* Exceeds HW SGE limit. +* GSO case: +* Disable GSO so the stack will software-segment the skb +* into smaller skbs that fit the SGE budget. +* Non-GSO case: +* The xmit path will attempt skb_linearize() as a fallback. +*/ + features &= ~NETIF_F_GSO_MASK ; + } +return features; +} +#endif +static void mana_get_stats64 (struct net_device *ndev,struct rtnl_link_stats64 *st) {
@@ -890,6 +923,9 @@ static const struct net_device_ops mana_devops = { .ndo_open = mana_open, .ndo_stop = mana_close, .ndo_select_queue = mana_select_queue, +#if (MAX_SKB_FRAGS + 2 > MANA_MAX_TX_WQE_SGL_ENTRIES) + .ndo_features_check = mana_features_check, +#endif .ndo_start_xmit = mana_start_xmit, .ndo_validate_addr = eth_validate_addr, .ndo_get_stats64 = mana_get_stats64,
drivers/net/ethernet/microsoft/mana/mana_ethtool.c +2 -0
@@ -18,6 +18,8 @@ static const struct mana_stats_desc mana_eth_stats[] = { {"tx_cq_err", offsetof(struct mana_ethtool_stats, tx_cqe_err)}, {"tx_cqe_unknown_type", offsetof(struct mana_ethtool_stats, tx_cqe_unknown_type)}, + {"tx_linear_pkt_cnt", offsetof(struct mana_ethtool_stats, + tx_linear_pkt_cnt)}, {"rx_coalesced_err", offsetof(struct mana_ethtool_stats, rx_coalesced_err)}, {"rx_cqe_unknown_type", offsetof(struct mana_ethtool_stats,
include/net/mana/gdma.h +7 -1
@@ -486,6 +486,8 @@ struct gdma_wqe {#define INLINE_OOB_SMALL_SIZE 8#define INLINE_OOB_LARGE_SIZE 24 +#define MANA_MAX_TX_WQE_SGL_ENTRIES 30 +#define MAX_TX_WQE_SIZE 512#define MAX_RX_WQE_SIZE 256
@@ -592,6 +594,9 @@ enum {#define GDMA_DRV_CAP_FLAG_1_HANDLE_RECONFIG_EQE BIT(17)#define GDMA_DRV_CAP_FLAG_1_HW_VPORT_LINK_AWARE BIT(6) +/* Driver supports linearizing the skb when num_sge exceeds hardware limit */ +#define GDMA_DRV_CAP_FLAG_1_SKB_LINEARIZE BIT(20) +/* Driver can send HWC periodically to query stats */ #define GDMA_DRV_CAP_FLAG_1_PERIODIC_STATS_QUERY BIT(21)
@@ -605,7 +610,8 @@ enum {GDMA_DRV_CAP_FLAG_1_SELF_RESET_ON_EQE | \GDMA_DRV_CAP_FLAG_1_HANDLE_RECONFIG_EQE | \GDMA_DRV_CAP_FLAG_1_HW_VPORT_LINK_AWARE | \ - GDMA_DRV_CAP_FLAG_1_PERIODIC_STATS_QUERY) +GDMA_DRV_CAP_FLAG_1_PERIODIC_STATS_QUERY | \ + GDMA_DRV_CAP_FLAG_1_SKB_LINEARIZE) #define GDMA_DRV_CAP_FLAGS2 0
include/net/mana/mana.h +1 -0
@@ -377,6 +377,7 @@ struct mana_ethtool_stats {u64 wake_queue;u64 tx_cqe_err;u64 tx_cqe_unknown_type; +u64 tx_linear_pkt_cnt;u64 rx_coalesced_err;u64 rx_cqe_unknown_type; };
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 707/957 net: mana: Handle hardware recovery events when probing the device
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Long Li <[email protected]>
[ Upstream commit 9bf66036d686b9a67000ba22bd94be13a4ea79ac ]
When MANA is being probed, it's possible that hardware is in recovery mode and the device may get GDMA_EQE_HWC_RESET_REQUEST over HWC in the middle of the probe. Detect such condition and go through the recovery service procedure.
Stable-dep-of: 65267c9c4f28 ("net: mana: Fix EQ leak in mana_remove on NULL port")
--- .../net/ethernet/microsoft/mana/gdma_main.c | 176 ++++++++++++++++-- include/net/mana/gdma.h | 12 +- 2 files changed, 170 insertions(+), 18 deletions(-)
drivers/net/ethernet/microsoft/mana/gdma_main.c +159 -17
@@ -15,6 +15,20 @@struct dentry *mana_debugfs_root; +struct mana_dev_recovery { +struct list_head list; +struct pci_dev *pdev; +enum gdma_eqe_type type; +}; + +static struct mana_dev_recovery_work { +struct list_head dev_list; +struct delayed_work work; + +/* Lock for dev_list above */ +spinlock_t lock; +} mana_dev_recovery_work; +static u32 mana_gd_r32 (struct gdma_context *g,u64 offset) {return readl (g->bar0_va + offset);
@@ -387,6 +401,25 @@ EXPORT_SYMBOL_NS(mana_gd_ring_cq, "NET_MANA");#define MANA_SERVICE_PERIOD 10 +static void mana_serv_rescan (struct pci_dev *pdev) +{ +struct pci_bus *parent; + +pci_lock_rescan_remove (); + + parent = pdev->bus; +if (!parent) { +dev_err (&pdev->dev,"MANA service: no parent bus\n" ); + goto out; + } + +pci_stop_and_remove_bus_device (pdev); +pci_rescan_bus (parent); + +out: +pci_unlock_rescan_remove (); +} +static void mana_serv_fpga (struct pci_dev *pdev) {struct pci_bus *bus, *parent;
@@ -419,9 +452,12 @@ static void mana_serv_reset(struct pci_dev *pdev) {struct gdma_context *gc =pci_get_drvdata (pdev);struct hw_channel_context *hwc; +int ret;if (!gc) { -dev_err (&pdev->dev,"MANA service: no GC\n" ); +/* Perform PCI rescan on device if GC is not set up */ +dev_err (&pdev->dev,"MANA service: GC not setup, rescanning\n" ); +mana_serv_rescan (pdev);return ; }
@@ -440,9 +476,18 @@ static void mana_serv_reset(struct pci_dev *pdev)msleep (MANA_SERVICE_PERIOD *1000 ); -mana_gd_resume (pdev); + ret =mana_gd_resume (pdev); +if (ret == -ETIMEDOUT || ret == -EPROTO ) { +/* Perform PCI rescan on device if we failed on HWC */ +dev_err (&pdev->dev,"MANA service: resume failed, rescanning\n" ); +mana_serv_rescan (pdev); + goto out; + } -dev_info (&pdev->dev,"MANA reset cycle completed\n" ); +if (ret) +dev_info (&pdev->dev,"MANA reset cycle failed err %d\n" , ret); +else +dev_info (&pdev->dev,"MANA reset cycle completed\n" ); out: gc->in_service = false;
@@ -454,18 +499,9 @@ struct mana_serv_work {enum gdma_eqe_type type; }; -static void mana_serv_func (struct work_struct *w) +static void mana_do_service (enum gdma_eqe_type type,struct pci_dev *pdev) { -struct mana_serv_work *mns_wk; -struct pci_dev *pdev; - - mns_wk = container_of(w, struct mana_serv_work, serv_work); - pdev = mns_wk->pdev; - -if (!pdev) - goto out; - -switch (mns_wk->type) { +switch (type) {case GDMA_EQE_HWC_FPGA_RECONFIG :mana_serv_fpga (pdev);break ;
@@ -475,12 +511,48 @@ static void mana_serv_func(struct work_struct *w)break ;default : -dev_err (&pdev->dev,"MANA service: unknown type %d\n" , - mns_wk->type); +dev_err (&pdev->dev,"MANA service: unknown type %d\n" , type);break ; } +} + +static void mana_recovery_delayed_func (struct work_struct *w) +{ +struct mana_dev_recovery_work *work; +struct mana_dev_recovery *dev; +unsigned long flags; + + work = container_of(w, struct mana_dev_recovery_work, work.work); + +spin_lock_irqsave (&work->lock, flags); + +while (!list_empty (&work->dev_list)) { + dev =list_first_entry (&work->dev_list, + struct mana_dev_recovery, list); +list_del (&dev->list); +spin_unlock_irqrestore (&work->lock, flags); + +mana_do_service (dev->type, dev->pdev); +pci_dev_put (dev->pdev); +kfree (dev); + +spin_lock_irqsave (&work->lock, flags); + } + +spin_unlock_irqrestore (&work->lock, flags); +} + +static void mana_serv_func (struct work_struct *w) +{ +struct mana_serv_work *mns_wk; +struct pci_dev *pdev; + + mns_wk = container_of(w, struct mana_serv_work, serv_work); + pdev = mns_wk->pdev; + +if (pdev) +mana_do_service (mns_wk->type, pdev); -out:pci_dev_put (pdev);kfree (mns_wk);module_put (THIS_MODULE );
@@ -541,6 +613,17 @@ static void mana_gd_process_eqe(struct gdma_queue *eq)case GDMA_EQE_HWC_RESET_REQUEST :dev_info (gc->dev,"Recv MANA service type:%d\n" , type); +if (!test_and_set_bit (GC_PROBE_SUCCEEDED , &gc->flags)) { +/* +* Device is in probe and we received a hardware reset +* event, the probe function will detect that the flag +* has changed and perform service procedure. +*/ +dev_info (gc->dev, +"Service is to be processed in probe\n" ); +break ; + } +if (gc->in_service) {dev_info (gc->dev,"Already in service\n" );break ;
@@ -1943,8 +2026,19 @@ static int mana_gd_probe(struct pci_dev *pdev, const struct pci_device_id *ent)if (err) goto cleanup_mana; +/* +* If a hardware reset event has occurred over HWC during probe, +* rollback and perform hardware reset procedure. +*/ +if (test_and_set_bit (GC_PROBE_SUCCEEDED , &gc->flags)) { + err = -EPROTO ; + goto cleanup_mana_rdma; + } +return 0 ; +cleanup_mana_rdma: +mana_rdma_remove (&gc->mana_ib); cleanup_mana:mana_remove (&gc->mana, false); cleanup_gd:
@@ -1968,6 +2062,35 @@ static int mana_gd_probe(struct pci_dev *pdev, const struct pci_device_id *ent) disable_dev:pci_disable_device (pdev);dev_err (&pdev->dev,"gdma probe failed: err = %d\n" , err); + +/* +* Hardware could be in recovery mode and the HWC returns TIMEDOUT or +* EPROTO from mana_gd_setup(), mana_probe() or mana_rdma_probe(), or +* we received a hardware reset event over HWC interrupt. In this case, +* perform the device recovery procedure after MANA_SERVICE_PERIOD +* seconds. +*/ +if (err == -ETIMEDOUT || err == -EPROTO ) { +struct mana_dev_recovery *dev; +unsigned long flags; + +dev_info (&pdev->dev,"Start MANA recovery mode\n" ); + + dev =kzalloc (sizeof (*dev),GFP_KERNEL ); +if (!dev) +return err; + + dev->pdev =pci_dev_get (pdev); + dev->type =GDMA_EQE_HWC_RESET_REQUEST ; + +spin_lock_irqsave (&mana_dev_recovery_work.lock, flags); +list_add_tail (&dev->list, &mana_dev_recovery_work.dev_list); +spin_unlock_irqrestore (&mana_dev_recovery_work.lock, flags); + +schedule_delayed_work (&mana_dev_recovery_work.work, +secs_to_jiffies (MANA_SERVICE_PERIOD )); + } +return err; }
@@ -2072,6 +2195,10 @@ static int __init mana_driver_init(void) {int err; +INIT_LIST_HEAD (&mana_dev_recovery_work.dev_list); +spin_lock_init (&mana_dev_recovery_work.lock); +INIT_DELAYED_WORK (&mana_dev_recovery_work.work, mana_recovery_delayed_func); + mana_debugfs_root =debugfs_create_dir ("mana" ,NULL ); err =pci_register_driver (&mana_driver);
@@ -2085,6 +2212,21 @@ static int __init mana_driver_init(void) static void __exit mana_driver_exit(void) { +struct mana_dev_recovery *dev; +unsigned long flags; + +disable_delayed_work_sync (&mana_dev_recovery_work.work); + +spin_lock_irqsave (&mana_dev_recovery_work.lock, flags); +while (!list_empty (&mana_dev_recovery_work.dev_list)) { + dev =list_first_entry (&mana_dev_recovery_work.dev_list, + struct mana_dev_recovery, list); +list_del (&dev->list); +pci_dev_put (dev->pdev); +kfree (dev); + } +spin_unlock_irqrestore (&mana_dev_recovery_work.lock, flags); +pci_unregister_driver (&mana_driver);debugfs_remove (mana_debugfs_root);
include/net/mana/gdma.h +11 -1
@@ -382,6 +382,10 @@ struct gdma_irq_context {char name[MANA_IRQ_NAME_SZ ]; }; +enum gdma_context_flags { +GC_PROBE_SUCCEEDED =0 , +}; +struct gdma_context {struct device *dev;struct dentry *mana_pci_debugfs;
@@ -430,6 +434,8 @@ struct gdma_context {u64 pf_cap_flags1;struct workqueue_struct *service_wq; + +unsigned long flags; };static inline bool mana_gd_is_mana (struct gdma_dev *gd)
@@ -600,6 +606,9 @@ enum {/* Driver can send HWC periodically to query stats */ #define GDMA_DRV_CAP_FLAG_1_PERIODIC_STATS_QUERY BIT(21) +/* Driver can handle hardware recovery events during probe */ +#define GDMA_DRV_CAP_FLAG_1_PROBE_RECOVERY BIT(22) +#define GDMA_DRV_CAP_FLAGS1 \ (GDMA_DRV_CAP_FLAG_1_EQ_SHARING_MULTI_VPORT | \ GDMA_DRV_CAP_FLAG_1_NAPI_WKDONE_FIX | \
@@ -611,7 +620,8 @@ enum {GDMA_DRV_CAP_FLAG_1_HANDLE_RECONFIG_EQE | \GDMA_DRV_CAP_FLAG_1_HW_VPORT_LINK_AWARE | \GDMA_DRV_CAP_FLAG_1_PERIODIC_STATS_QUERY | \ - GDMA_DRV_CAP_FLAG_1_SKB_LINEARIZE) +GDMA_DRV_CAP_FLAG_1_SKB_LINEARIZE | \ + GDMA_DRV_CAP_FLAG_1_PROBE_RECOVERY) #define GDMA_DRV_CAP_FLAGS2 0
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 708/957 net: mana: Implement ndo_tx_timeout and serialize queue resets per port.
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dipayaan Roy <[email protected]>
[ Upstream commit 3b194343c25084a8d2fa0c0f2c9e80f3080fd732 ]
Implement .ndo_tx_timeout for MANA so any stalled TX queue can be detected and a device-controlled port reset for all queues can be scheduled to a ordered workqueue. The reset for all queues on stall detection is recomended by hardware team.
Stable-dep-of: 65267c9c4f28 ("net: mana: Fix EQ leak in mana_remove on NULL port")
--- drivers/net/ethernet/microsoft/mana/mana_en.c | 77 ++++++++++++++++++- include/net/mana/gdma.h | 7 +- include/net/mana/mana.h | 3 +- 3 files changed, 84 insertions(+), 3 deletions(-)
drivers/net/ethernet/microsoft/mana/mana_en.c +76 -1
@@ -299,6 +299,39 @@ static int mana_get_gso_hs(struct sk_buff *skb)return gso_hs; } +static void mana_per_port_queue_reset_work_handler (struct work_struct *work) +{ +struct mana_port_context *apc =container_of (work, + struct mana_port_context, + queue_reset_work); +struct net_device *ndev = apc->ndev; +int err; + +rtnl_lock (); + +/* Pre-allocate buffers to prevent failure in mana_attach later */ + err =mana_pre_alloc_rxbufs (apc, ndev->mtu, apc->num_queues); +if (err) { +netdev_err (ndev,"Insufficient memory for reset post tx stall detection\n" ); + goto out; + } + + err =mana_detach (ndev, false); +if (err) { +netdev_err (ndev,"mana_detach failed: %d\n" , err); + goto dealloc_pre_rxbufs; + } + + err =mana_attach (ndev); +if (err) +netdev_err (ndev,"mana_attach failed: %d\n" , err); + +dealloc_pre_rxbufs: +mana_pre_dealloc_rxbufs (apc); +out: +rtnl_unlock (); +} +netdev_tx_t mana_start_xmit (struct sk_buff *skb,struct net_device *ndev) {enum mana_tx_pkt_format pkt_fmt =MANA_SHORT_PKT_FMT ;
@@ -847,6 +880,23 @@ static int mana_change_mtu(struct net_device *ndev, int new_mtu)return err; } +static void mana_tx_timeout (struct net_device *netdev,unsigned int txqueue) +{ +struct mana_port_context *apc =netdev_priv (netdev); +struct mana_context *ac = apc->ac; +struct gdma_context *gc = ac->gdma_dev->gdma_context; + +/* Already in service, hence tx queue reset is not required.*/ +if (gc->in_service) +return ; + +/* Note: If there are pending queue reset work for this port(apc), +* subsequent request queued up from here are ignored. This is because +* we are using the same work instance per port(apc). +*/ +queue_work (ac->per_port_queue_reset_wq, &apc->queue_reset_work); +} +static int mana_shaper_set (struct net_shaper_binding *binding,const struct net_shaper *shaper,struct netlink_ext_ack *extack)
@@ -932,6 +982,7 @@ static const struct net_device_ops mana_devops = { .ndo_bpf = mana_bpf, .ndo_xdp_xmit = mana_xdp_xmit, .ndo_change_mtu = mana_change_mtu, + .ndo_tx_timeout = mana_tx_timeout, .net_shaper_ops = &mana_shaper_ops, };
@@ -3319,6 +3370,8 @@ static int mana_probe_port(struct mana_context *ac, int port_idx, ndev->min_mtu =ETH_MIN_MTU ; ndev->needed_headroom =MANA_HEADROOM ; ndev->dev_port = port_idx; +/* Recommended timeout based on HW FPGA re-config scenario. */ + ndev->watchdog_timeo =15 *HZ ;SET_NETDEV_DEV (ndev, gc->dev);netif_set_tso_max_size (ndev,GSO_MAX_SIZE );
@@ -3335,6 +3388,10 @@ static int mana_probe_port(struct mana_context *ac, int port_idx,if (err) goto reset_apc; +/* Initialize the per port queue reset work.*/ +INIT_WORK (&apc->queue_reset_work, + mana_per_port_queue_reset_work_handler); +netdev_lockdep_set_classes (ndev); ndev->hw_features =NETIF_F_SG |NETIF_F_IP_CSUM |NETIF_F_IPV6_CSUM ;
@@ -3524,6 +3581,7 @@ int mana_probe(struct gdma_dev *gd, bool resuming) {struct gdma_context *gc = gd->gdma_context;struct mana_context *ac = gd->driver_data; +struct mana_port_context *apc =NULL ;struct device *dev = gc->dev;u8 bm_hostmode =0 ;u16 num_ports =0 ;
@@ -3581,6 +3639,14 @@ int mana_probe(struct gdma_dev *gd, bool resuming)if (ac->num_ports >MAX_PORTS_IN_MANA_DEV ) ac->num_ports =MAX_PORTS_IN_MANA_DEV ; + ac->per_port_queue_reset_wq = +create_singlethread_workqueue ("mana_per_port_queue_reset_wq" ); +if (!ac->per_port_queue_reset_wq) { +dev_err (dev,"Failed to allocate per port queue reset workqueue\n" ); + err = -ENOMEM ; + goto out; + } +if (!resuming) {for (i =0 ; i < ac->num_ports; i++) { err =mana_probe_port (ac, i, &ac->ports[i]);
@@ -3596,6 +3662,8 @@ int mana_probe(struct gdma_dev *gd, bool resuming) }else {for (i =0 ; i < ac->num_ports; i++) {rtnl_lock (); + apc =netdev_priv (ac->ports[i]); +enable_work (&apc->queue_reset_work); err =mana_attach (ac->ports[i]);rtnl_unlock ();/* Log the port for which the attach failed, stop
@@ -3652,13 +3720,15 @@ void mana_remove(struct gdma_dev *gd, bool suspending)for (i =0 ; i < ac->num_ports; i++) { ndev = ac->ports[i]; - apc =netdev_priv (ndev);if (!ndev) {if (i ==0 )dev_err (dev,"No net device to remove\n" ); goto out; } + apc =netdev_priv (ndev); +disable_work_sync (&apc->queue_reset_work); +/* All cleanup actions should stay after rtnl_lock(), otherwise * other functions may access partially cleaned up data. */
@@ -3685,6 +3755,11 @@ void mana_remove(struct gdma_dev *gd, bool suspending)mana_destroy_eq (ac); out: +if (ac->per_port_queue_reset_wq) { +destroy_workqueue (ac->per_port_queue_reset_wq); + ac->per_port_queue_reset_wq =NULL ; + } +mana_gd_deregister_device (gd);if (suspending)
include/net/mana/gdma.h +6 -1
@@ -598,6 +598,10 @@ enum {/* Driver can self reset on FPGA Reconfig EQE notification */ #define GDMA_DRV_CAP_FLAG_1_HANDLE_RECONFIG_EQE BIT(17) + +/* Driver detects stalled send queues and recovers them */ +#define GDMA_DRV_CAP_FLAG_1_HANDLE_STALL_SQ_RECOVERY BIT(18) +#define GDMA_DRV_CAP_FLAG_1_HW_VPORT_LINK_AWARE BIT(6)/* Driver supports linearizing the skb when num_sge exceeds hardware limit */
@@ -621,7 +625,8 @@ enum {GDMA_DRV_CAP_FLAG_1_HW_VPORT_LINK_AWARE | \GDMA_DRV_CAP_FLAG_1_PERIODIC_STATS_QUERY | \GDMA_DRV_CAP_FLAG_1_SKB_LINEARIZE | \ - GDMA_DRV_CAP_FLAG_1_PROBE_RECOVERY) +GDMA_DRV_CAP_FLAG_1_PROBE_RECOVERY | \ + GDMA_DRV_CAP_FLAG_1_HANDLE_STALL_SQ_RECOVERY) #define GDMA_DRV_CAP_FLAGS2 0
include/net/mana/mana.h +2 -1
@@ -480,7 +480,7 @@ struct mana_context {struct mana_ethtool_hc_stats hc_stats;struct mana_eq *eqs;struct dentry *mana_eqs_debugfs; - +struct workqueue_struct *per_port_queue_reset_wq;/* Workqueue for querying hardware stats */ struct delayed_work gf_stats_work;bool hwc_timeout_occurred;
@@ -495,6 +495,7 @@ struct mana_context {struct mana_port_context {struct mana_context *ac;struct net_device *ndev; +struct work_struct queue_reset_work;u8 mac_addr[ETH_ALEN ];
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 709/957 net: mana: Fix EQ leak in mana_remove on NULL port
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Erni Sri Satya Vennela <[email protected]>
[ Upstream commit 65267c9c4f28199985505977bc2c628c82fc50ef ]
In mana_remove(), when a NULL port is encountered in the port iteration loop, 'goto out' skips the mana_destroy_eq(ac) call, leaking the event queues allocated earlier by mana_create_eq().
This can happen when mana_probe_port() fails for port 0, leaving ac->ports[0] as NULL. On driver unload or error cleanup, mana_remove() hits the NULL entry and jumps past mana_destroy_eq().
Change 'goto out' to 'break' so the for-loop exits normally and mana_destroy_eq() is always reached. Remove the now-unreferenced out:
label.
--- drivers/net/ethernet/microsoft/mana/mana_en.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/net/ethernet/microsoft/mana/mana_en.c +2 -2
@@ -3723,7 +3723,7 @@ void mana_remove(struct gdma_dev *gd, bool suspending)if (!ndev) {if (i ==0 )dev_err (dev,"No net device to remove\n" ); - goto out; +break ; } apc =netdev_priv (ndev);
@@ -3754,7 +3754,7 @@ void mana_remove(struct gdma_dev *gd, bool suspending) }mana_destroy_eq (ac); -out: +if (ac->per_port_queue_reset_wq) {destroy_workqueue (ac->per_port_queue_reset_wq); ac->per_port_queue_reset_wq =NULL ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 710/957 vsock/virtio: fix MSG_ZEROCOPY pinned-pages accounting
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Stefano Garzarella <[email protected]>
[ Upstream commit 1cb36e252211506f51095fe7ced8286cc77b4c80 ]
virtio_transport_init_zcopy_skb() uses iter->count as the size argument for msg_zerocopy_realloc(), which in turn passes it to mm_account_pinned_pages() for RLIMIT_MEMLOCK accounting. However, this function is called after virtio_transport_fill_skb() has already consumed the iterator via __zerocopy_sg_from_iter(), so on the last skb, iter->count will be 0, skipping the RLIMIT_MEMLOCK enforcement.
Pass pkt_len (the total bytes being sent) as an explicit parameter to virtio_transport_init_zcopy_skb() instead of reading the already-consumed iter->count.
This matches TCP and UDP, which both call msg_zerocopy_realloc() with the original message size.
--- net/vmw_vsock/virtio_transport_common.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-)
net/vmw_vsock/virtio_transport_common.c +8 -3
@@ -75,6 +75,7 @@ static bool virtio_transport_can_zcopy(const struct virtio_transport *t_ops,static int virtio_transport_init_zcopy_skb (struct vsock_sock *vsk,struct sk_buff *skb,struct msghdr *msg, +size_t pkt_len,bool zerocopy) {struct ubuf_info *uarg;
@@ -83,12 +84,10 @@ static int virtio_transport_init_zcopy_skb(struct vsock_sock *vsk, uarg = msg->msg_ubuf;net_zcopy_get (uarg); }else { -struct iov_iter *iter = &msg->msg_iter;struct ubuf_info_msgzc *uarg_zc; uarg =msg_zerocopy_realloc (sk_vsock (vsk), - iter->count, -NULL , false); + pkt_len,NULL , false);if (!uarg)return -1 ;
@@ -385,11 +384,17 @@ static int virtio_transport_send_pkt_info(struct vsock_sock *vsk,* each iteration. If this is last skb for this buffer * and MSG_ZEROCOPY mode is in use - we must allocate * completion for the current syscall. +* +* Pass pkt_len because msg iter is already consumed +* by virtio_transport_fill_skb(), so iter->count +* can not be used for RLIMIT_MEMLOCK pinned-pages +* accounting done by msg_zerocopy_realloc(). */ if (info->msg && info->msg->msg_flags &MSG_ZEROCOPY && skb_len == rest_len && info->op ==VIRTIO_VSOCK_OP_RW ) {if (virtio_transport_init_zcopy_skb (vsk, skb, info->msg, + pkt_len, can_zcopy)) {kfree_skb (skb); ret = -ENOMEM ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 711/957 virtio_net: sync rss_trailer.max_tx_vq on queue_pairs change via VQ_PAIRS_SET
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Brett Creeley <[email protected]>
[ Upstream commit 3bc06da858ef17cfe94b49efc0d9713727012835 ]
When netif_is_rxfh_configured() is true (i.e., the user has explicitly configured the RSS indirection table), virtnet_set_queues() skips the RSS update path and falls through to the VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET command to change the number of queue pairs. However, it does not update vi->rss_trailer.max_tx_vq to reflect the new queue_pairs value.
This causes a mismatch between vi->curr_queue_pairs and vi->rss_trailer.max_tx_vq. Any subsequent RSS reconfiguration (e.g., via ethtool -X) calls virtnet_commit_rss_command(), which sends the stale max_tx_vq to the device, silently reverting the queue count.
Reproduction:
1. User configured RSS
ethtool -X eth0 equal 8
2. VQ_PAIRS_SET path; max_tx_vq stays 16
ethtool -L eth0 combined 12
3. RSS commit uses max_tx_vq=16 instead of 12
ethtool -X eth0 equal 4
Fix this by updating vi->rss_trailer.max_tx_vq after a successful VQ_PAIRS_SET command when RSS is enabled, keeping it in sync with curr_queue_pairs.
--- drivers/net/virtio_net.c | 6 ++++++ 1 file changed, 6 insertions(+)
drivers/net/virtio_net.c +6 -0
@@ -3828,6 +3828,12 @@ static int virtnet_set_queues(struct virtnet_info *vi, u16 queue_pairs) queue_pairs);return -EINVAL ; } + +/* Keep max_tx_vq in sync so that a later RSS command does not +* revert queue_pairs to a stale value. +*/ +if (vi->has_rss) + vi->rss_trailer.max_tx_vq =cpu_to_le16 (queue_pairs); succ: vi->curr_queue_pairs = queue_pairs;if (dev->flags &IFF_UP ) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 712/957 nfp: fix swapped arguments in nfp_encode_basic_qdr() calls
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alexey Kodanev <[email protected]>
[ Upstream commit 4078c5611d7585548b249377ebd60c272e410490 ]
There is a mismatch between the passed arguments and the actual nfp_encode_basic_qdr() function parameter names:
static int nfp_encode_basic_qdr(u64 addr, int dest_island, int cpp_tgt,
int mode, bool addr40, int isld1, int isld0)
{
...But "dest_island" and "cpp_tgt" are swapped at every call-site. For example:
return nfp_encode_basic_qdr(*addr, cpp_tgt, dest_island,
mode, addr40, isld1, isld0);
As a result, nfp_encode_basic_qdr() receives "dest_island" as CPP target type, which is always NFP_CPP_TARGET_QDR(2) for these calls, and "cpp_tgt" as the destination island ID, which can accidentally match or be outside the valid NFP_CPP_TARGET_* types (e.g. '-1' for any destination).
Since code already worked for years, also add extra pr_warn() to error paths in nfp_encode_basic_qdr() to help identify any potential address verification failures.
Detected using the static analysis tool - Svace.
--- .../ethernet/netronome/nfp/nfpcore/nfp_target.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-)
drivers/net/ethernet/netronome/nfp/nfpcore/nfp_target.c +11 -6
@@ -435,12 +435,17 @@ static int nfp_encode_basic_qdr(u64 addr, int dest_island, int cpp_tgt,/* Full Island ID and channel bits overlap? */ ret =nfp_decode_basic (addr, &v, cpp_tgt, mode, addr40, isld1, isld0); -if (ret) +if (ret) { +pr_warn ("%s: decode dest_island failed: %d\n" , __func__, ret);return ret; + }/* The current address won't go where expected? */ -if (dest_island !=-1 && dest_island != v) +if (dest_island !=-1 && dest_island != v) { +pr_warn ("%s: dest_island mismatch: current (%d) != decoded (%d)\n" , + __func__, dest_island, v);return -EINVAL ; + }/* If dest_island was -1, we don't care where it goes. */ return 0 ;
@@ -493,7 +498,7 @@ static int nfp_encode_basic(u64 *addr, int dest_island, int cpp_tgt,* the address but we can verify if the existing * contents will point to a valid island. */ -return nfp_encode_basic_qdr (*addr, cpp_tgt, dest_island, +return nfp_encode_basic_qdr (*addr, dest_island, cpp_tgt, mode, addr40, isld1, isld0); iid_lsb = addr40 ?34 :26 ;
@@ -504,7 +509,7 @@ static int nfp_encode_basic(u64 *addr, int dest_island, int cpp_tgt,return 0 ;case 1 :if (cpp_tgt ==NFP_CPP_TARGET_QDR && !addr40) -return nfp_encode_basic_qdr (*addr, cpp_tgt, dest_island, +return nfp_encode_basic_qdr (*addr, dest_island, cpp_tgt, mode, addr40, isld1, isld0); idx_lsb = addr40 ?39 :31 ;
@@ -530,7 +535,7 @@ static int nfp_encode_basic(u64 *addr, int dest_island, int cpp_tgt,* be set before hand and with them select an island. * So we need to confirm that it's at least plausible. */ -return nfp_encode_basic_qdr (*addr, cpp_tgt, dest_island, +return nfp_encode_basic_qdr (*addr, dest_island, cpp_tgt, mode, addr40, isld1, isld0);/* Make sure we compare against isldN values
@@ -551,7 +556,7 @@ static int nfp_encode_basic(u64 *addr, int dest_island, int cpp_tgt,* iid<1> = addr<30> = channel<0> * channel<1> = addr<31> = Index */ -return nfp_encode_basic_qdr (*addr, cpp_tgt, dest_island, +return nfp_encode_basic_qdr (*addr, dest_island, cpp_tgt, mode, addr40, isld1, isld0); isld[0 ] &= ~3 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 713/957 tcp: send a challenge ACK on SEG.ACK > SND.NXT
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiayuan Chen <[email protected]>
[ Upstream commit 42726ec644cbdde0035c3e0417fee8ed9547e120 ]
RFC 5961 Section 5.2 validates an incoming segment's ACK value against the range [SND.UNA - MAX.SND.WND, SND.NXT] and states:
"All incoming segments whose ACK value doesn't satisfy the above
condition MUST be discarded and an ACK sent back."
Commit 354e4aa391ed ("tcp: RFC 5961 5.2 Blind Data Injection Attack Mitigation") opted Linux into this mitigation and implements the challenge ACK on the lower side (SEG.ACK < SND.UNA - MAX.SND.WND), but the symmetric upper side (SEG.ACK > SND.NXT) still takes the pre-RFC-5961 path and silently returns SKB_DROP_REASON_TCP_ACK_UNSENT_DATA, even though RFC 793 Section 3.9 (now RFC 9293 Section 3.10.7.4) has always required:
"If the ACK acknowledges something not yet sent (SEG.ACK > SND.NXT)
then send an ACK, drop the segment, and return."
Complete the mitigation by sending a challenge ACK on that branch, reusing the existing tcp_send_challenge_ack() path which already enforces the per-socket RFC 5961 Section 7 rate limit via __tcp_oow_rate_limited(). FLAG_NO_CHALLENGE_ACK is honoured for symmetry with the lower-edge case.
Update the existing tcp_ts_recent_invalid_ack.pkt selftest, which drives this exact path, to consume the new challenge ACK.
--- net/ipv4/tcp_input.c | 10 +++++++--- .../net/packetdrill/tcp_ts_recent_invalid_ack.pkt | 4 +++- 2 files changed, 10 insertions(+), 4 deletions(-)
net/ipv4/tcp_input.c +7 -3
@@ -4025,11 +4025,15 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) goto old_ack; } -/* If the ack includes data we haven't sent yet, discard -* this segment (RFC793 Section 3.9). +/* If the ack includes data we haven't sent yet, drop the +* segment. RFC 793 Section 3.9 and RFC 5961 Section 5.2 +* require us to send an ACK back in that case. */ -if (after (ack, tp->snd_nxt)) +if (after (ack, tp->snd_nxt)) { +if (!(flag &FLAG_NO_CHALLENGE_ACK )) +tcp_send_challenge_ack (sk, false);return -SKB_DROP_REASON_TCP_ACK_UNSENT_DATA ; + }if (after (ack, prior_snd_una)) { flag |=FLAG_SND_UNA_ADVANCED ;
tools/testing/selftests/net/packetdrill/tcp_ts_recent_invalid_ack.pkt +3 -1
@@ -19,7 +19,9 @@ // bad packet with high tsval (its ACK sequence is above our sndnxt) +0 < F. 1:1(0) ack 9999 win 20000 <nop,nop,TS val 200000 ecr 100> - +// Challenge ACK for SEG.ACK > SND.NXT (RFC 5961 5.2 / RFC 793 3.9). +// ecr=200 (not 200000) proves ts_recent was not updated from the bad packet. + +0 > . 1:1(0) ack 1 <nop,nop,TS val 200 ecr 200> +0 < . 1:1001(1000) ack 1 win 20000 <nop,nop,TS val 201 ecr 100> +0 > . 1:1(0) ack 1001 <nop,nop,TS val 200 ecr 201>
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 714/957 tipc: fix double-free in tipc_buf_append()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lee Jones <[email protected]>
[ Upstream commit d293ca716e7d5dffdaecaf6b9b2f857a33dc3d3a ]
tipc_msg_validate() can potentially reallocate the skb it is validating, freeing the old one. In tipc_buf_append(), it was being called with a pointer to a local variable which was a copy of the caller's skb pointer.
If the skb was reallocated and validation subsequently failed, the error handling path would free the original skb pointer, which had already been freed, leading to double-free.
Fix this by checking if head now points to a newly allocated reassembled skb. If it does, reassign *headbuf for later freeing operations.
--- net/tipc/msg.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-)
net/tipc/msg.c +13 -1
@@ -177,8 +177,20 @@ int tipc_buf_append(struct sk_buff **headbuf, struct sk_buff **buf)if (fragid ==LAST_FRAGMENT ) {TIPC_SKB_CB (head)->validated =0 ; -if (unlikely (!tipc_msg_validate (&head))) + +/* If the reassembled skb has been freed in +* tipc_msg_validate() because of an invalid truesize, +* then head will point to a newly allocated reassembled +* skb, while *headbuf points to freed reassembled skb. +* In such cases, correct *headbuf for freeing the newly +* allocated reassembled skb later. +*/ +if (unlikely (!tipc_msg_validate (&head))) { +if (head != *headbuf) + *headbuf = head; goto err; + } + *buf = head;TIPC_SKB_CB (head)->tail =NULL ; *headbuf =NULL ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 715/957 vhost_net: fix sleeping with preempt-disabled in vhost_net_busy_poll()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kohei Enju <[email protected]>
[ Upstream commit e08a9fac5cf8c3fecf4755e7e3ac059f78b8f83d ]
syzbot reported "sleeping function called from invalid context" in vhost_net_busy_poll().
Commit 030881372460 ("vhost_net: basic polling support") introduced a busy-poll loop and preempt_{disable,enable}() around it, where each iteration calls a sleepable function inside the loop.
The purpose of disabling preemption was to keep local_clock()-based timeout accounting on a single CPU, rather than as a requirement of busy-poll itself:
https://lore.kernel.org/[email protected]
Quoted 1 line
From this perspective,
migrate_disable()is sufficient here, so replace
preempt_disable() with migrate_disable(), avoiding sleepable accesses from a preempt-disabled context.
--- drivers/vhost/net.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/vhost/net.c +2 -2
@@ -562,7 +562,7 @@ static void vhost_net_busy_poll(struct vhost_net *net, busyloop_timeout = poll_rx ? rvq->busyloop_timeout: tvq->busyloop_timeout; -preempt_disable (); +migrate_disable (); endtime =busy_clock () + busyloop_timeout;while (vhost_can_busy_poll (endtime)) {
@@ -579,7 +579,7 @@ static void vhost_net_busy_poll(struct vhost_net *net,cpu_relax (); } -preempt_enable (); +migrate_enable ();if (poll_rx ||sock_has_rx_data (sock))vhost_net_busy_poll_try_queue (net, vq);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 716/957 nstree: fix func. parameter kernel-doc warnings
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Randy Dunlap <[email protected]>
[ Upstream commit 43eb354ecb471426e97b0ce6a0c922ec20f82027 ]
Use the correct parameter name ("__ns") for function parameter kernel-doc to avoid 3 warnings:
Warning: include/linux/nstree.h:68 function parameter '__ns' not described in 'ns_tree_add_raw'
Warning: include/linux/nstree.h:77 function parameter '__ns' not described in 'ns_tree_add'
Warning: include/linux/nstree.h:88 function parameter '__ns' not described in 'ns_tree_remove'
--- include/linux/nstree.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
include/linux/nstree.h +3 -3
@@ -45,7 +45,7 @@ static inline void __ns_tree_add(struct ns_common *ns, struct ns_tree *ns_tree)/** * ns_tree_add_raw - Add a namespace to a namespace -* @ns: Namespace to add +* @__ns: Namespace to add * * This function adds a namespace to the appropriate namespace tree * without assigning a id.
@@ -54,7 +54,7 @@ static inline void __ns_tree_add(struct ns_common *ns, struct ns_tree *ns_tree)/** * ns_tree_add - Add a namespace to a namespace tree -* @ns: Namespace to add +* @__ns: Namespace to add * * This function assigns a new id to the namespace and adds it to the * appropriate namespace tree and list.
@@ -63,7 +63,7 @@ static inline void __ns_tree_add(struct ns_common *ns, struct ns_tree *ns_tree)/** * ns_tree_remove - Remove a namespace from a namespace tree -* @ns: Namespace to remove +* @__ns: Namespace to remove * * This function removes a namespace from the appropriate namespace * tree and list.
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 717/957 eventpoll: use hlist_is_singular_node() in __ep_remove()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
[ Upstream commit 3d9fd0abc94d8cd430cc7cd7d37ce5e5aae2cd2b ]
Replace the open-coded "epi is the only entry in file->f_ep" check with hlist_is_singular_node(). Same semantics, and the helper avoids the head-cacheline access in the common false case.
Stable-dep-of: a6dc643c6931 ("eventpoll: fix ep_remove struct eventpoll / struct file UAF")
--- fs/eventpoll.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/eventpoll.c +1 -1
@@ -856,7 +856,7 @@ static bool __ep_remove(struct eventpoll *ep, struct epitem *epi, bool force) to_free =NULL ; head = file->f_ep; -if (head->first == &epi->fllink && !epi->fllink.next) { +if (hlist_is_singular_node (&epi->fllink, head)) {/* See eventpoll_release() for details. */ WRITE_ONCE (file->f_ep,NULL );if (!is_file_epoll (file)) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 718/957 eventpoll: split __ep_remove()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
[ Upstream commit 0f7bdfd413000985de09fc39eb9efa1e091a3ce0 ]
Split __ep_remove() to delineate file removal from epoll item removal.
Stable-dep-of: a6dc643c6931 ("eventpoll: fix ep_remove struct eventpoll / struct file UAF")
--- fs/eventpoll.c | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-)
fs/eventpoll.c +23 -4
@@ -826,6 +826,9 @@ static void ep_free(struct eventpoll *ep)kfree_rcu (ep, rcu); } +static void __ep_remove_file (struct eventpoll *ep,struct epitem *epi,struct file *file); +static bool __ep_remove_epi (struct eventpoll *ep,struct epitem *epi); +/* * Removes a "struct epitem" from the eventpoll RB tree and deallocates * all the associated resources. Must be called with "mtx" held.
@@ -837,8 +840,6 @@ static void ep_free(struct eventpoll *ep)static bool __ep_remove (struct eventpoll *ep,struct epitem *epi,bool force) {struct file *file = epi->ffd.file; -struct epitems_head *to_free; -struct hlist_head *head;lockdep_assert_irqs_enabled ();
@@ -854,8 +855,21 @@ static bool __ep_remove(struct eventpoll *ep, struct epitem *epi, bool force)return false; } - to_free =NULL ; - head = file->f_ep; +__ep_remove_file (ep, epi, file); +return __ep_remove_epi (ep, epi); +} + +/* +* Called with &file->f_lock held, +* returns with it released +*/ +static void __ep_remove_file (struct eventpoll *ep,struct epitem *epi,struct file *file) +{ +struct epitems_head *to_free =NULL ; +struct hlist_head *head = file->f_ep; + +lockdep_assert_held (&ep->mtx); +if (hlist_is_singular_node (&epi->fllink, head)) {/* See eventpoll_release() for details. */ WRITE_ONCE (file->f_ep,NULL );
@@ -869,6 +883,11 @@ static bool __ep_remove(struct eventpoll *ep, struct epitem *epi, bool force)hlist_del_rcu (&epi->fllink);spin_unlock (&file->f_lock);free_ephead (to_free); +} + +static bool __ep_remove_epi (struct eventpoll *ep,struct epitem *epi) +{ +lockdep_assert_held (&ep->mtx);rb_erase_cached (&epi->rbn, &ep->rbr);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 719/957 eventpoll: kill __ep_remove()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
[ Upstream commit e9e5cd40d7c403e19f21d0f7b8b8ba3a76b58330 ]
Remove the boolean conditional in __ep_remove() and restructure the code so the check for racing with eventpoll_release_file() are only done in the ep_remove_safe() path where they belong.
Stable-dep-of: a6dc643c6931 ("eventpoll: fix ep_remove struct eventpoll / struct file UAF")
--- fs/eventpoll.c | 67 ++++++++++++++++++++++---------------------------- 1 file changed, 30 insertions(+), 37 deletions(-)
fs/eventpoll.c +30 -37
@@ -826,49 +826,18 @@ static void ep_free(struct eventpoll *ep)kfree_rcu (ep, rcu); } -static void __ep_remove_file (struct eventpoll *ep,struct epitem *epi,struct file *file); -static bool __ep_remove_epi (struct eventpoll *ep,struct epitem *epi); - -/* -* Removes a "struct epitem" from the eventpoll RB tree and deallocates -* all the associated resources. Must be called with "mtx" held. -* If the dying flag is set, do the removal only if force is true. -* This prevents ep_clear_and_put() from dropping all the ep references -* while running concurrently with eventpoll_release_file(). -* Returns true if the eventpoll can be disposed. -*/ -static bool __ep_remove (struct eventpoll *ep,struct epitem *epi,bool force) -{ -struct file *file = epi->ffd.file; - -lockdep_assert_irqs_enabled (); - -/* -* Removes poll wait queue hooks. -*/ -ep_unregister_pollwait (ep, epi); - -/* Remove the current item from the list of epoll hooks */ -spin_lock (&file->f_lock); -if (epi->dying && !force) { -spin_unlock (&file->f_lock); -return false; - } - -__ep_remove_file (ep, epi, file); -return __ep_remove_epi (ep, epi); -} -/* * Called with &file->f_lock held, * returns with it released */ -static void __ep_remove_file (struct eventpoll *ep,struct epitem *epi,struct file *file) +static void __ep_remove_file (struct eventpoll *ep,struct epitem *epi, +struct file *file) {struct epitems_head *to_free =NULL ;struct hlist_head *head = file->f_ep;lockdep_assert_held (&ep->mtx); +lockdep_assert_held (&file->f_lock);if (hlist_is_singular_node (&epi->fllink, head)) {/* See eventpoll_release() for details. */
@@ -915,7 +884,25 @@ static bool __ep_remove_epi(struct eventpoll *ep, struct epitem *epi)*/ static void ep_remove_safe (struct eventpoll *ep,struct epitem *epi) { -if (__ep_remove (ep, epi, false)) +struct file *file = epi->ffd.file; + +lockdep_assert_irqs_enabled (); +lockdep_assert_held (&ep->mtx); + +ep_unregister_pollwait (ep, epi); + +/* sync with eventpoll_release_file() */ +if (unlikely (READ_ONCE (epi->dying))) +return ; + +spin_lock (&file->f_lock); +if (epi->dying) { +spin_unlock (&file->f_lock); +return ; + } +__ep_remove_file (ep, epi, file); + +if (__ep_remove_epi (ep, epi))WARN_ON_ONCE (ep_refcount_dec_and_test (ep)); }
@@ -1147,7 +1134,7 @@ void eventpoll_release_file(struct file *file)spin_lock (&file->f_lock);if (file->f_ep && file->f_ep->first) { epi = hlist_entry(file->f_ep->first, struct epitem, fllink); - epi->dying = true; +WRITE_ONCE (epi->dying, true);spin_unlock (&file->f_lock);/*
@@ -1156,7 +1143,13 @@ void eventpoll_release_file(struct file *file)*/ ep = epi->ep;mutex_lock (&ep->mtx); - dispose =__ep_remove (ep, epi, true); + +ep_unregister_pollwait (ep, epi); + +spin_lock (&file->f_lock); +__ep_remove_file (ep, epi, file); + dispose =__ep_remove_epi (ep, epi); +mutex_unlock (&ep->mtx);if (dispose &&ep_refcount_dec_and_test (ep))
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 720/957 eventpoll: drop vestigial __ prefix from ep_remove_{file,epi}()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
[ Upstream commit 0feaf644f7180c4a91b6b405a881afbfd958f1cf ]
With __ep_remove() gone, the double-underscore on __ep_remove_file() and __ep_remove_epi() no longer contrasts with a __-less parent and just reads as noise. Rename both to ep_remove_file() and ep_remove_epi(). No functional change.
Stable-dep-of: a6dc643c6931 ("eventpoll: fix ep_remove struct eventpoll / struct file UAF")
--- fs/eventpoll.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
fs/eventpoll.c +6 -6
@@ -830,7 +830,7 @@ static void ep_free(struct eventpoll *ep)* Called with &file->f_lock held, * returns with it released */ -static void __ep_remove_file (struct eventpoll *ep,struct epitem *epi, +static void ep_remove_file (struct eventpoll *ep,struct epitem *epi,struct file *file) {struct epitems_head *to_free =NULL ;
@@ -854,7 +854,7 @@ static void __ep_remove_file(struct eventpoll *ep, struct epitem *epi,free_ephead (to_free); } -static bool __ep_remove_epi (struct eventpoll *ep,struct epitem *epi) +static bool ep_remove_epi (struct eventpoll *ep,struct epitem *epi) {lockdep_assert_held (&ep->mtx);
@@ -900,9 +900,9 @@ static void ep_remove_safe(struct eventpoll *ep, struct epitem *epi)spin_unlock (&file->f_lock);return ; } -__ep_remove_file (ep, epi, file); +ep_remove_file (ep, epi, file); -if (__ep_remove_epi (ep, epi)) +if (ep_remove_epi (ep, epi))WARN_ON_ONCE (ep_refcount_dec_and_test (ep)); }
@@ -1147,8 +1147,8 @@ void eventpoll_release_file(struct file *file)ep_unregister_pollwait (ep, epi);spin_lock (&file->f_lock); -__ep_remove_file (ep, epi, file); - dispose =__ep_remove_epi (ep, epi); +ep_remove_file (ep, epi, file); + dispose =ep_remove_epi (ep, epi);mutex_unlock (&ep->mtx);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 721/957 eventpoll: move epi_fget() up
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
[ Upstream commit 86e87059e6d1fd5115a31949726450ed03c1073b ]
We'll need it when removing files so move it up. No functional change.
Stable-dep-of: a6dc643c6931 ("eventpoll: fix ep_remove struct eventpoll / struct file UAF")
--- fs/eventpoll.c | 56 +++++++++++++++++++++++++------------------------- 1 file changed, 28 insertions(+), 28 deletions(-)
fs/eventpoll.c +28 -28
@@ -826,6 +826,34 @@ static void ep_free(struct eventpoll *ep)kfree_rcu (ep, rcu); } +/* +* The ffd.file pointer may be in the process of being torn down due to +* being closed, but we may not have finished eventpoll_release() yet. +* +* Normally, even with the atomic_long_inc_not_zero, the file may have +* been free'd and then gotten re-allocated to something else (since +* files are not RCU-delayed, they are SLAB_TYPESAFE_BY_RCU). +* +* But for epoll, users hold the ep->mtx mutex, and as such any file in +* the process of being free'd will block in eventpoll_release_file() +* and thus the underlying file allocation will not be free'd, and the +* file re-use cannot happen. +* +* For the same reason we can avoid a rcu_read_lock() around the +* operation - 'ffd.file' cannot go away even if the refcount has +* reached zero (but we must still not call out to ->poll() functions +* etc). +*/ +static struct file *epi_fget (const struct epitem *epi) +{ +struct file *file; + + file = epi->ffd.file; +if (!file_ref_get (&file->f_ref)) + file =NULL ; +return file; +} +/* * Called with &file->f_lock held, * returns with it released
@@ -1018,34 +1046,6 @@ static __poll_t __ep_eventpoll_poll(struct file *file, poll_table *wait, int depreturn res; } -/* -* The ffd.file pointer may be in the process of being torn down due to -* being closed, but we may not have finished eventpoll_release() yet. -* -* Normally, even with the atomic_long_inc_not_zero, the file may have -* been free'd and then gotten re-allocated to something else (since -* files are not RCU-delayed, they are SLAB_TYPESAFE_BY_RCU). -* -* But for epoll, users hold the ep->mtx mutex, and as such any file in -* the process of being free'd will block in eventpoll_release_file() -* and thus the underlying file allocation will not be free'd, and the -* file re-use cannot happen. -* -* For the same reason we can avoid a rcu_read_lock() around the -* operation - 'ffd.file' cannot go away even if the refcount has -* reached zero (but we must still not call out to ->poll() functions -* etc). -*/ -static struct file *epi_fget (const struct epitem *epi) -{ -struct file *file; - - file = epi->ffd.file; -if (!file_ref_get (&file->f_ref)) - file =NULL ; -return file; -} -/* * Differs from ep_eventpoll_poll() in that internal callers already have * the ep->mtx so we need to start from depth=1, such that mutex_lock_nested()
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 722/957 eventpoll: fix ep_remove struct eventpoll / struct file UAF
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
[ Upstream commit a6dc643c69311677c574a0f17a3f4d66a5f3744b ]
ep_remove() (via ep_remove_file()) cleared file->f_ep under file->f_lock but then kept using @file inside the critical section (is_file_epoll(), hlist_del_rcu() through the head, spin_unlock). A concurrent __fput() taking the eventpoll_release() fastpath in that window observed the transient NULL, skipped eventpoll_release_file() and ran to f_op->release / file_free().
For the epoll-watches-epoll case, f_op->release is ep_eventpoll_release() -> ep_clear_and_put() -> ep_free(), which kfree()s the watched struct eventpoll. Its embedded ->refs hlist_head is exactly where epi->fllink.pprev points, so the subsequent hlist_del_rcu()'s "*pprev = next" scribbles into freed kmalloc-192 memory.
In addition, struct file is SLAB_TYPESAFE_BY_RCU, so the slot backing @file could be recycled by alloc_empty_file() -- reinitializing f_lock and f_ep -- while ep_remove() is still nominally inside that lock. The upshot is an attacker-controllable kmem_cache_free() against the wrong slab cache.
Pin @file via epi_fget() at the top of ep_remove() and gate the critical section on the pin succeeding. With the pin held @file cannot reach refcount zero, which holds __fput() off and transitively keeps the watched struct eventpoll alive across the hlist_del_rcu() and the f_lock use, closing both UAFs.
If the pin fails @file has already reached refcount zero and its __fput() is in flight. Because we bailed before clearing f_ep, that path takes the eventpoll_release() slow path into eventpoll_release_file() and blocks on ep->mtx until the waiter side's ep_clear_and_put() drops it. The bailed epi's share of ep->refcount stays intact, so the trailing ep_refcount_dec_and_test() in ep_clear_and_put() cannot free the eventpoll out from under eventpoll_release_file(); the orphaned epi is then cleaned up there.
A successful pin also proves we are not racing eventpoll_release_file() on this epi, so drop the now-redundant re-check of epi->dying under f_lock. The cheap lockless READ_ONCE(epi->dying) fast-path bailout stays.
--- fs/eventpoll.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-)
fs/eventpoll.c +10 -6
@@ -912,22 +912,26 @@ static bool ep_remove_epi(struct eventpoll *ep, struct epitem *epi)*/ static void ep_remove_safe (struct eventpoll *ep,struct epitem *epi) { -struct file *file = epi->ffd.file; + struct file *file __free(fput) = NULL;lockdep_assert_irqs_enabled ();lockdep_assert_held (&ep->mtx);ep_unregister_pollwait (ep, epi); -/* sync with eventpoll_release_file() */ +/* cheap sync with eventpoll_release_file() */ if (unlikely (READ_ONCE (epi->dying)))return ; -spin_lock (&file->f_lock); -if (epi->dying) { -spin_unlock (&file->f_lock); +/* +* If we manage to grab a reference it means we're not in +* eventpoll_release_file() and aren't going to be. +*/ + file =epi_fget (epi); +if (!file)return ; - } + +spin_lock (&file->f_lock);ep_remove_file (ep, epi, file);if (ep_remove_epi (ep, epi))
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 723/957 fs/adfs: validate nzones in adfs_validate_bblk()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bae Yeonju <[email protected]>
[ Upstream commit dd9d3e16c2d5fa166e13dce07413be51f42c8f5d ]
Reject ADFS disc records with a zero zone count during boot block validation, before the disc record is used.
When nzones is 0, adfs_read_map() passes it to kmalloc_array(0, ...) which returns ZERO_SIZE_PTR, and adfs_map_layout() then writes to dm[-1], causing an out-of-bounds write before the allocated buffer.
adfs_validate_dr0() already rejects nzones != 1 for old-format images. Add the equivalent check to adfs_validate_bblk() for new-format images so that a crafted image with nzones == 0 is rejected at probe time.
Found by syzkaller.
--- fs/adfs/super.c | 3 +++ 1 file changed, 3 insertions(+)
fs/adfs/super.c +3 -0
@@ -317,6 +317,9 @@ static int adfs_validate_bblk(struct super_block *sb, struct buffer_head *bh,if (adfs_checkdiscrecord (dr))return -EILSEQ ; +if ((dr->nzones | dr->nzones_high <<8 ) ==0 ) +return -EILSEQ ; + *drp = dr;return 0 ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 724/957 rtc: abx80x: Disable alarm feature if no interrupt attached
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Anthony Pighin (Nokia) <[email protected]>
[ Upstream commit 0fedce7244e4b85c049ce579c87e298a1b0b811d ]
Commit 795cda8338ea ("rtc: interface: Fix long-standing race when setting alarm") exposed an issue where the rtc-abx80x driver does not clear the alarm feature bit, but instead relies on the set_alarm operation to return invalid.
For example, when a RTC_UIE_ON ioctl is handled, it should abort at the feature validation. Instead, it proceeds to the rtc_timer_enqueue(), which used to return an error from the set_alarm call. However, following the race condition handling, which likely should not be discarding predecing errors, a success condition is returned to the ioctl() caller. This results in (for example):
hwclock: select() to /dev/rtc0 to wait for clock tick timed out
Notwithstanding the validity of the race condition handling, if an interrupt wasn't specified, or could not be attached, the driver should clear the alarm feature bit.
--- drivers/rtc/rtc-abx80x.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/rtc/rtc-abx80x.c +2 -0
@@ -933,6 +933,8 @@ static int abx80x_probe(struct i2c_client *client) client->irq =0 ; } } +if (client->irq <=0 ) +clear_bit (RTC_FEATURE_ALARM , priv->rtc->features); err =rtc_add_group (priv->rtc, &rtc_calib_attr_group);if (err) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 725/957 kbuild: builddeb - avoid recompiles for non-cross-compiles
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mathias Krause <[email protected]>
[ Upstream commit 2452dcf4d740effff5aa71b7f6529ee8c04fd8f6 ]
Commit e2c318225ac1 ("kbuild: deb-pkg: add pkg.linux-upstream.nokernelheaders build profile") changed how install-extmod-build gets called, making it always rebuild the host programs below scripts/ if HOSTCC wasn't specified with its full triplet on the make command line. That is, apparently, needed to fix up commit f1d87664b82a ("kbuild: cross-compile linux-headers package when possible") for cross-compiles. However, in the much more common case of non-cross-compile builds this will lead to unnecessary rebuilding of host tools including gcc plugins. This, in turn, will lead to a full kernel rebuild on the next 'make bindeb-pkg' which is unfortunate.
Avoid that by only triggering the rebuild of host tools for actual cross-compile builds.
--- scripts/package/builddeb | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
scripts/package/builddeb +7 -1
@@ -139,7 +139,13 @@ install_kernel_headers () { pdir=debian/$1 version=${1#linux-headers-} - CC="${DEB_HOST_GNU_TYPE}-gcc" "${srctree}/scripts/package/install-extmod-build" "${pdir}/usr/src/linux-headers-${version}" + # Override $CC only for cross-compiles, to not unnecessarily rebuild + # scripts/ including plugins, which may lead to a full kernel rebuild. + if [ -n "${CROSS_COMPILE}" ]; then + CC="${DEB_HOST_GNU_TYPE}-gcc" "${srctree}/scripts/package/install-extmod-build" "${pdir}/usr/src/linux-headers-${version}" + else + "${srctree}/scripts/package/install-extmod-build" "${pdir}/usr/src/linux-headers-${version}" + fi mkdir -p $pdir/lib/modules/$version/ ln -s /usr/src/linux-headers-$version $pdir/lib/modules/$version/build
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 726/957 fbdev: offb: fix PCI device reference leak on probe failure
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yuho Choi <[email protected]>
[ Upstream commit 869b93ba04088713596e68453c1146f52f713290 ]
offb_init_nodriver() gets a referenced PCI device with pci_get_device(). If pci_enable_device() fails, the function returns without dropping that reference.
Release the PCI device reference before returning from the pci_enable_device() failure path.
--- drivers/video/fbdev/offb.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
drivers/video/fbdev/offb.c +6 -1
@@ -640,8 +640,13 @@ static void offb_init_nodriver(struct platform_device *parent, struct device_nod vid =be32_to_cpup (vidp); did =be32_to_cpup (didp); pdev =pci_get_device (vid, did,NULL ); -if (!pdev ||pci_enable_device (pdev)) +if (!pdev)return ; + +if (pci_enable_device (pdev)) { +pci_dev_put (pdev); +return ; + } }#endif /* kludge for valkyrie */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 727/957 tools/power turbostat.8: Document the "--force" option
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Len Brown <[email protected]>
[ Upstream commit 785953cf6e63aa5a9fcdfa9577b1411e0281c4bc ]
Starting in turbostat v2025.01.14, turbostat refused to run on unsupported hardware, pointing to "RUN THE LATEST VERSION" on turbostat(8).
At that time, turbostat supported and advertised the "--force" parameter to run anyway (with unsupported results).
Also document "--force" on turbostat.8.
Stable-dep-of: ce012c966b51 ("tools/power turbostat: Fix unrecognized option '-P'")
--- tools/power/x86/turbostat/turbostat.8 | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-)
tools/power/x86/turbostat/turbostat.8 +7 -3
@@ -111,6 +111,8 @@ The column name "all" can be used to enable all disabled-by-default built-in cou .PP \fB--no-perf\fP Disable all the uses of the perf API. .PP +\fB--force\fPForce turbostat to run on an unsupported platform (minimal defaults). +.PP \fB--interval seconds\fP overrides the default 5.0 second measurement interval. .PP \fB--num_iterations num\fP number of the measurement iterations.
@@ -161,9 +163,9 @@ The system configuration dump (if --quiet is not used) is followed by statistics .PP \fBC1, C2, C3...\fP The number times Linux requested the C1, C2, C3 idle state during the measurement interval. The system summary line shows the sum for all CPUs. These are C-state names as exported in /sys/devices/system/cpu/cpu*/cpuidle/state*/name. While their names are generic, their attributes are processor specific. They the system description section of output shows what MWAIT sub-states they are mapped to on each system. These counters are in the "cpuidle" group, which is disabled, by default. .PP -\fBC1+, C2+, C3+...\fP The idle governor idle state misprediction statistics. Inidcates the number times Linux requested the C1, C2, C3 idle state during the measurement interval, but should have requested a deeper idle state (if it exists and enabled). These statistics come from the /sys/devices/system/cpu/cpu*/cpuidle/state*/below file. These counters are in the "cpuidle" group, which is disabled, by default. +\fBC1+, C2+, C3+...\fP The idle governor idle state misprediction statistics. Indicates the number times Linux requested the C1, C2, C3 idle state during the measurement interval, but should have requested a deeper idle state (if it exists and enabled). These statistics come from the /sys/devices/system/cpu/cpu*/cpuidle/state*/below file. These counters are in the "cpuidle" group, which is disabled, by default. .PP -\fBC1-, C2-, C3-...\fP The idle governor idle state misprediction statistics. Inidcates the number times Linux requested the C1, C2, C3 idle state during the measurement interval, but should have requested a shallower idle state (if it exists and enabled). These statistics come from the /sys/devices/system/cpu/cpu*/cpuidle/state*/above file. These counters are in the "cpuidle" group, which is disabled, by default. +\fBC1-, C2-, C3-...\fP The idle governor idle state misprediction statistics. Indicates the number times Linux requested the C1, C2, C3 idle state during the measurement interval, but should have requested a shallower idle state (if it exists and enabled). These statistics come from the /sys/devices/system/cpu/cpu*/cpuidle/state*/above file. These counters are in the "cpuidle" group, which is disabled, by default. .PP \fBC1%, C2%, C3%\fP The residency percentage that Linux requested C1, C2, C3.... The system summary is the average of all CPUs in the system. Note that these are software, reflecting what was requested. The hardware counters reflect what was actually achieved. These counters are in the "pct_idle" group, which is enabled by default. .PP
@@ -193,7 +195,7 @@ The system configuration dump (if --quiet is not used) is followed by statistics .PP \fBGFX%C0\fP Percentage of time that at least one GFX compute engine is busy. .PP -\fBCPUGFX%\fP Percentage of time that at least one CPU is busy at the same time as at least one Graphics compute enginer is busy. +\fBCPUGFX%\fP Percentage of time that at least one CPU is busy at the same time as at least one Graphics compute engine is busy. .PP \fBPkg%pc2, Pkg%pc3, Pkg%pc6, Pkg%pc7\fP percentage residency in hardware package idle states. These numbers are from hardware residency counters. .PP
@@ -556,6 +558,8 @@ If the upstream version isn't new enough, the development tree can be found here If the development tree doesn't work, please contact the author via chat, or via email with the word "turbostat" on the Subject line. +An old turbostat binary may run on unknown hardware by using "--force", +but results are unsupported. .SH FILES .ta .nf
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 728/957 tools/power turbostat: Use strtoul() for iteration parsing
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kaushlendra Kumar <[email protected]>
[ Upstream commit 8e5c0cc326f2e95a71bb6e6063e65caa60c8f951 ]
Replace strtod() with strtoul() and check errno for -n/-N options, since num_iterations and header_iterations are unsigned long counters. Reject zero and conversion errors; negative inputs wrap to large positive values per standard unsigned semantics.
Stable-dep-of: ce012c966b51 ("tools/power turbostat: Fix unrecognized option '-P'")
--- tools/power/x86/turbostat/turbostat.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-)
tools/power/x86/turbostat/turbostat.c +8 -6
@@ -11133,18 +11133,20 @@ void cmdline(int argc, char **argv)/* Parsed earlier */ break ;case 'n' : - num_iterations =strtod (optarg,NULL ); + num_iterations =strtoul (optarg,NULL ,0 ); + errno =0 ; -if (num_iterations <=0 ) { -fprintf (outf,"iterations %d should be positive number\n" , num_iterations); +if (errno || num_iterations ==0 ) { +fprintf (outf,"invalid iteration count: %s\n" , optarg);exit (2 ); }break ;case 'N' : - header_iterations =strtod (optarg,NULL ); + header_iterations =strtoul (optarg,NULL ,0 ); + errno =0 ; -if (header_iterations <=0 ) { -fprintf (outf,"iterations %d should be positive number\n" , header_iterations); +if (errno || header_iterations ==0 ) { +fprintf (outf,"invalid header iteration count: %s\n" , optarg);exit (2 ); }break ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 729/957 tools/power turbostat: Fix and document --header_iterations
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Len Brown <[email protected]>
[ Upstream commit 96718ad296af4a6d984b3a09276b165ab6a3b0c8 ]
The "header_iterations" option is commonly used to de-clutter the screen of redundant header label rows in an interactive session:
Eg. every 10 rows:
$ sudo turbostat --header_iterations 10 -S -q -i 1
But --header_iterations was missing from turbostat.8
Also turbostat help advertised the "-N" short option that did not actually work:
$ turbostat --help -N, --header_iterations num print header every num iterations
Repair "-N"
Document "--header_iterations" on turbostat.8
Stable-dep-of: ce012c966b51 ("tools/power turbostat: Fix unrecognized option '-P'")
--- tools/power/x86/turbostat/turbostat.8 | 4 +++- tools/power/x86/turbostat/turbostat.c | 20 +++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-)
tools/power/x86/turbostat/turbostat.8 +3 -1
@@ -111,12 +111,14 @@ The column name "all" can be used to enable all disabled-by-default built-in cou .PP \fB--no-perf\fP Disable all the uses of the perf API. .PP -\fB--force\fPForce turbostat to run on an unsupported platform (minimal defaults). +\fB--force\fP Force turbostat to run on an unsupported platform (minimal defaults). .PP \fB--interval seconds\fP overrides the default 5.0 second measurement interval. .PP \fB--num_iterations num\fP number of the measurement iterations. .PP +\fB--header_iterations num\fP print header every num iterations. +.PP \fB--out output_file\fP turbostat output is written to the specified output_file. The file is truncated if it already exists, and it is created if it does not exist. .PP
tools/power/x86/turbostat/turbostat.c +9 -11
@@ -11040,7 +11040,7 @@ void cmdline(int argc, char **argv)* Parse some options early, because they may make other options invalid, * like adding the MSR counter with --add and at the same time using --no-msr. */ -while ((opt =getopt_long_only (argc, argv,"+MPn:" , long_options, &option_index)) !=-1 ) { +while ((opt =getopt_long_only (argc, argv,"+:MP" , long_options, &option_index)) !=-1 ) {switch (opt) {case 'M' : no_msr =1 ;
@@ -11054,7 +11054,7 @@ void cmdline(int argc, char **argv) } optind =0 ; -while ((opt =getopt_long_only (argc, argv,"+C:c:Dde:hi:Jn:o:qMST:v" , long_options, &option_index)) !=-1 ) { +while ((opt =getopt_long_only (argc, argv,"+C:c:Dde:hi:Jn:N:o:qMST:v" , long_options, &option_index)) !=-1 ) {switch (opt) {case 'a' :parse_add_command (optarg);
@@ -11097,7 +11097,6 @@ void cmdline(int argc, char **argv) }break ;case 'h' : -default :help ();exit (1 );case 'i' :
@@ -11136,19 +11135,15 @@ void cmdline(int argc, char **argv) num_iterations =strtoul (optarg,NULL ,0 ); errno =0 ; -if (errno || num_iterations ==0 ) { -fprintf (outf,"invalid iteration count: %s\n" , optarg); -exit (2 ); - } +if (errno || num_iterations ==0 ) +errx (-1 ,"invalid iteration count: %s" , optarg);break ;case 'N' : header_iterations =strtoul (optarg,NULL ,0 ); errno =0 ; -if (errno || header_iterations ==0 ) { -fprintf (outf,"invalid header iteration count: %s\n" , optarg); -exit (2 ); - } +if (errno || header_iterations ==0 ) +errx (-1 ,"invalid header iteration count: %s" , optarg);break ;case 's' :/*
@@ -11171,6 +11166,9 @@ void cmdline(int argc, char **argv)print_version ();exit (0 );break ; +default : +help (); +exit (1 ); } } }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 730/957 tools/power turbostat: Fix unrecognized option -P
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Arcari <[email protected]>
[ Upstream commit ce012c966b518c53475ba9a4e979242d7322d819 ]
The '-P' short option (shorthand for --no-perf) is not present in the optstring of the second call to getopt_long_only(). This results in the "unrecognized option" error when the tool reaches the main parsing loop.
Add 'P' to the second getopt_long_only() call to ensure it is consistently recognized.
--- tools/power/x86/turbostat/turbostat.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
tools/power/x86/turbostat/turbostat.c +1 -1
@@ -11054,7 +11054,7 @@ void cmdline(int argc, char **argv) } optind =0 ; -while ((opt =getopt_long_only (argc, argv,"+C:c:Dde:hi:Jn:N:o:qMST:v" , long_options, &option_index)) !=-1 ) { +while ((opt =getopt_long_only (argc, argv,"+C:c:Dde:hi:Jn:N:o:qMPST:v" , long_options, &option_index)) !=-1 ) {switch (opt) {case 'a' :parse_add_command (optarg);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 731/957 kbuild: Never respect CONFIG_WERROR / W=e to fixdep
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thomas Weißschuh <[email protected]>
[ Upstream commit 75f7c47ccd78c947cf1b6ddb18ea453ff0555716 ]
The fixdep hostprog may be built multiple times during a single build. Once during the configuration phase and later during the regular phase. As only the regular build phase respects CONFIG_WERROR / W=e, the compiler flags might change between the phases, leading to rebuilds.
Example, the rebuilds will happen twice on each invocation of the build:
$ make allyesconfig prepare
make[1]: Entering directory '/tmp/deleteme'
HOSTCC scripts/basic/fixdep
#
# No change to .config
#
HOSTCC scripts/basic/fixdep
DESCEND objtool
INSTALL libsubcmd_headers
make[1]: Leaving directory '/tmp/deleteme'Fix the compilation flags used for scripts/basic/ before scripts/Makefile.warn is evaluated to stop CONFIG_WERROR / W=e influencing the fixdep build to avoid the spurious rebuilds.
--- Makefile | 2 ++ 1 file changed, 2 insertions(+)
Makefile +2 -0
@@ -655,6 +655,8 @@ export RCS_FIND_IGNORE := \( -name SCCS -o -name BitKeeper -o -name .svn -o \ # Basic helpers built in scripts/basic/ PHONY += scripts_basic +scripts_basic: KBUILD_HOSTCFLAGS := $(KBUILD_HOSTCFLAGS) +scripts_basic: KBUILD_HOSTLDFLAGS := $(KBUILD_HOSTLDFLAGS) scripts_basic: $(Q)$(MAKE) $(build)=scripts/basic
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 732/957 mailbox: mtk-cmdq: Fix CURR and END addr for task insert case
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jason-JH Lin <[email protected]>
[ Upstream commit d2591db9c8ef19fbb4d24ed15e0c6edfa6bc7917 ]
Fix CURR and END address calculation for inserting a cmdq task into the task list by using cmdq_reg_shift_addr() for proper address converting. This ensures both CURR and END addresses are set correctly when enabling the thread.
--- drivers/mailbox/mtk-cmdq-mailbox.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
drivers/mailbox/mtk-cmdq-mailbox.c +4 -4
@@ -434,14 +434,14 @@ static int cmdq_mbox_send_data(struct mbox_chan *chan, void *data)if (curr_pa == end_pa -CMDQ_INST_SIZE || curr_pa == end_pa) {/* set to this task directly */ -writel (task->pa_base >> cmdq->pdata->shift, - thread->base +CMDQ_THR_CURR_ADDR ); + gce_addr =cmdq_convert_gce_addr (task->pa_base, cmdq->pdata); +writel (gce_addr, thread->base +CMDQ_THR_CURR_ADDR ); }else {cmdq_task_insert_into_thread (task);smp_mb ();/* modify jump before enable thread */ } -writel ((task->pa_base + pkt->cmd_buf_size) >> cmdq->pdata->shift, - thread->base +CMDQ_THR_END_ADDR ); + gce_addr =cmdq_convert_gce_addr (task->pa_base + pkt->cmd_buf_size, cmdq->pdata); +writel (gce_addr, thread->base +CMDQ_THR_END_ADDR );cmdq_thread_resume (thread); }list_move_tail (&task->list_entry, &thread->task_busy_list);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 733/957 mailbox: mailbox-test: free channels on probe error
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Wolfram Sang <[email protected]>
[ Upstream commit c02053a9055d5fdfd32432287cca8958db1d5bc5 ]
On probe error, free the previously obtained channels. This not only prevents a leak, but also UAF scenarios because the client structure will be removed nonetheless because it was allocated with devm.
--- drivers/mailbox/mailbox-test.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-)
drivers/mailbox/mailbox-test.c +12 -3
@@ -404,18 +404,27 @@ static int mbox_test_probe(struct platform_device *pdev)if (tdev->rx_channel) { tdev->rx_buffer =devm_kzalloc (&pdev->dev,MBOX_MAX_MSG_LEN ,GFP_KERNEL ); -if (!tdev->rx_buffer) -return -ENOMEM ; +if (!tdev->rx_buffer) { + ret = -ENOMEM ; + goto err_free_chans; + } } ret =mbox_test_add_debugfs (pdev, tdev);if (ret) -return ret; + goto err_free_chans;init_waitqueue_head (&tdev->waitq);dev_info (&pdev->dev,"Successfully registered\n" );return 0 ; + +err_free_chans: +if (tdev->tx_channel) +mbox_free_channel (tdev->tx_channel); +if (tdev->rx_channel) +mbox_free_channel (tdev->rx_channel); +return ret; }static void mbox_test_remove (struct platform_device *pdev)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 734/957 sched/psi: fix race between file release and pressure write
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Edward Adam Davis <[email protected]>
[ Upstream commit a5b98009f16d8a5fb4a8ff9a193f5735515c38fa ]
A potential race condition exists between pressure write and cgroup file release regarding the priv member of struct kernfs_open_file, which triggers the uaf reported in [1].
Consider the following scenario involving execution on two separate CPUs:
CPU0 CPU1
==== ====
vfs_rmdir() kernfs_iop_rmdir() cgroup_rmdir() cgroup_kn_lock_live() cgroup_destroy_locked() cgroup_addrm_files() cgroup_rm_file() kernfs_remove_by_name() kernfs_remove_by_name_ns() vfs_write() __kernfs_remove() new_sync_write() kernfs_drain() kernfs_fop_write_iter() kernfs_drain_open_files() cgroup_file_write() kernfs_release_file() pressure_write() cgroup_file_release() ctx = of->priv; kfree(ctx);
of->priv = NULL; cgroup_kn_unlock()
cgroup_kn_lock_live()
cgroup_get(cgrp)
cgroup_kn_unlock()
if (ctx->psi.trigger) // here, trigger uaf for ctx, that is of->priv
The cgroup_rmdir() is protected by the cgroup_mutex, it also safeguards the memory deallocation of of->priv performed within cgroup_file_release(). However, the operations involving of->priv executed within pressure_write() are not entirely covered by the protection of cgroup_mutex. Consequently, if the code in pressure_write(), specifically the section handling the ctx variable executes after cgroup_file_release() has completed, a uaf vulnerability involving of->priv is triggered.
Therefore, the issue can be resolved by extending the scope of the cgroup_mutex lock within pressure_write() to encompass all code paths involving of->priv, thereby properly synchronizing the race condition occurring between cgroup_file_release() and pressure_write().
And, if an live kn lock can be successfully acquired while executing the pressure write operation, it indicates that the cgroup deletion process has not yet reached its final stage; consequently, the priv pointer within open_file cannot be NULL. Therefore, the operation to retrieve the ctx value must be moved to a point *after* the live kn lock has been successfully acquired.
In another situation, specifically after entering cgroup_kn_lock_live() but before acquiring cgroup_mutex, there exists a different class of race condition:
CPU0: write memory.pressure CPU1: write cgroup.pressure=0
=========================== =============================
kernfs_fop_write_iter()
kernfs_get_active_of(of)
pressure_write()
cgroup_kn_lock_live(memory.pressure)
cgroup_tryget(cgrp)
kernfs_break_active_protection(kn)
... blocks on cgroup_mutex
cgroup_pressure_write() cgroup_kn_lock_live(cgroup.pressure) cgroup_file_show(memory.pressure, false)
kernfs_show(false)
kernfs_drain_open_files()
cgroup_file_release(of)
kfree(ctx)
of->priv = NULL
cgroup_kn_unlock()
... acquires cgroup_mutex
ctx = of->priv; // may now be NULL
if (ctx->psi.trigger) // NULL dereference
Consequently, there is a possibility that of->priv is NULL, the pressure write needs to check for this.
Now that the scope of the cgroup_mutex has been expanded, the original explicit cgroup_get/put operations are no longer necessary, this is because acquiring/releasing the live kn lock inherently executes a cgroup get/put operation.
[1]
BUG: KASAN: slab-use-after-free in pressure_write+0xa4/0x210 kernel/cgroup/cgroup.c:4011 Call Trace: pressure_write+0xa4/0x210 kernel/cgroup/cgroup.c:4011 cgroup_file_write+0x36f/0x790 kernel/cgroup/cgroup.c:4311 kernfs_fop_write_iter+0x3b0/0x540 fs/kernfs/file.c:352 Allocated by task 9352: cgroup_file_open+0x90/0x3a0 kernel/cgroup/cgroup.c:4256 kernfs_fop_open+0x9eb/0xcb0 fs/kernfs/file.c:724 do_dentry_open+0x83d/0x13e0 fs/open.c:949 Freed by task 9353: cgroup_file_release+0xd6/0x100 kernel/cgroup/cgroup.c:4283 kernfs_release_file fs/kernfs/file.c:764 [inline] kernfs_drain_open_files+0x392/0x720 fs/kernfs/file.c:834 kernfs_drain+0x470/0x600 fs/kernfs/dir.c:525
--- kernel/cgroup/cgroup.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-)
kernel/cgroup/cgroup.c +16 -8
@@ -3997,33 +3997,41 @@ static int cgroup_cpu_pressure_show(struct seq_file *seq, void *v)static ssize_t pressure_write (struct kernfs_open_file *of,char *buf,size_t nbytes,enum psi_res res) { -struct cgroup_file_ctx *ctx = of->priv; +struct cgroup_file_ctx *ctx;struct psi_trigger *new;struct cgroup *cgrp;struct psi_group *psi; +ssize_t ret =0 ; cgrp =cgroup_kn_lock_live (of->kn, false);if (!cgrp)return -ENODEV ; -cgroup_get (cgrp); -cgroup_kn_unlock (of->kn); + ctx = of->priv; +if (!ctx) { + ret = -ENODEV ; + goto out_unlock; + }/* Allow only one trigger per file descriptor */ if (ctx->psi.trigger) { -cgroup_put (cgrp); -return -EBUSY ; + ret = -EBUSY ; + goto out_unlock; } psi =cgroup_psi (cgrp); new =psi_trigger_create (psi, buf, res, of->file, of);if (IS_ERR (new)) { -cgroup_put (cgrp); -return PTR_ERR (new); + ret =PTR_ERR (new); + goto out_unlock; }smp_store_release (&ctx->psi.trigger, new); -cgroup_put (cgrp); + +out_unlock: +cgroup_kn_unlock (of->kn); +if (ret) +return ret;return nbytes; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 735/957 cgroup/rdma: fix integer overflow in rdmacg_try_charge()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: cuitao <[email protected]>
[ Upstream commit c802f460dd485c1332b5a35e7adcfb2bc22536a2 ]
The expression rpool->resources[index].usage + 1 is computed in int arithmetic before being assigned to s64 variable new. When usage equals INT_MAX (the default "max" value), the addition overflows to INT_MIN. This negative value then passes the new > max check incorrectly, allowing a charge that should be rejected and corrupting usage to negative.
Fix by casting usage to s64 before the addition so the arithmetic is done in 64-bit.
--- kernel/cgroup/rdma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/cgroup/rdma.c +1 -1
@@ -283,7 +283,7 @@ int rdmacg_try_charge(struct rdma_cgroup **rdmacg, ret =PTR_ERR (rpool); goto err; }else { - new = rpool->resources[index].usage +1 ; + new = (s64 )rpool->resources[index].usage +1 ;if (new > rpool->resources[index].max) { ret = -EAGAIN ; goto err;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 736/957 mailbox: add sanity check for channel array
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Wolfram Sang <[email protected]>
[ Upstream commit c1aad75595fb67edc7fda8af249d3b886efa1be9 ]
Fail gracefully if there is no channel array attached to the mailbox controller. Otherwise the later dereference will cause an OOPS which might not be seen because mailbox controllers might instantiate very early. Remove the comment explaining the obvious while here.
--- drivers/mailbox/mailbox.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/mailbox/mailbox.c +1 -2
@@ -505,8 +505,7 @@ int mbox_controller_register(struct mbox_controller *mbox) {int i, txdone; -/* Sanity check */ -if (!mbox || !mbox->dev || !mbox->ops || !mbox->num_chans) +if (!mbox || !mbox->dev || !mbox->ops || !mbox->chans || !mbox->num_chans)return -EINVAL ;if (mbox->txdone_irq)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 737/957 mailbox: mailbox-test: dont free the reused channel
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Wolfram Sang <[email protected]>
[ Upstream commit 88ebadbf0deefdaccdab868b44ff70a0a257f473 ]
The RX channel can be aliased to the TX channel if it has a different MMIO. This special case needs to be handled when freeing the channels otherwise a double-free occurs.
--- drivers/mailbox/mailbox-test.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/mailbox/mailbox-test.c +2 -2
@@ -422,7 +422,7 @@ static int mbox_test_probe(struct platform_device *pdev) err_free_chans:if (tdev->tx_channel)mbox_free_channel (tdev->tx_channel); -if (tdev->rx_channel) +if (tdev->rx_channel && tdev->rx_channel != tdev->tx_channel)mbox_free_channel (tdev->rx_channel);return ret; }
@@ -435,7 +435,7 @@ static void mbox_test_remove(struct platform_device *pdev)if (tdev->tx_channel)mbox_free_channel (tdev->tx_channel); -if (tdev->rx_channel) +if (tdev->rx_channel && tdev->rx_channel != tdev->tx_channel)mbox_free_channel (tdev->rx_channel); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 738/957 mailbox: mailbox-test: initialize struct earlier
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Wolfram Sang <[email protected]>
[ Upstream commit bbcf9af68bfedb3d9cc3c7eae62f5c844d8b78b9 ]
The waitqueue must be initialized before the debugfs files are created because from that time, requests from userspace can already be made. Similarily, drvdata and spinlock needs to be initialized before we request the channel, otherwise dangling irqs might run into problems like a NULL pointer exception.
--- drivers/mailbox/mailbox-test.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-)
drivers/mailbox/mailbox-test.c +6 -7
@@ -366,6 +366,12 @@ static int mbox_test_probe(struct platform_device *pdev)if (!tdev)return -ENOMEM ; + tdev->dev = &pdev->dev; +spin_lock_init (&tdev->lock); +mutex_init (&tdev->mutex); +init_waitqueue_head (&tdev->waitq); +platform_set_drvdata (pdev, tdev); +/* It's okay for MMIO to be NULL */ tdev->tx_mmio =devm_platform_get_and_ioremap_resource (pdev,0 , &res);if (PTR_ERR (tdev->tx_mmio) == -EBUSY ) {
@@ -395,12 +401,6 @@ static int mbox_test_probe(struct platform_device *pdev)if (!tdev->rx_channel && (tdev->rx_mmio != tdev->tx_mmio)) tdev->rx_channel = tdev->tx_channel; - tdev->dev = &pdev->dev; -platform_set_drvdata (pdev, tdev); - -spin_lock_init (&tdev->lock); -mutex_init (&tdev->mutex); -if (tdev->rx_channel) { tdev->rx_buffer =devm_kzalloc (&pdev->dev,MBOX_MAX_MSG_LEN ,GFP_KERNEL );
@@ -414,7 +414,6 @@ static int mbox_test_probe(struct platform_device *pdev)if (ret) goto err_free_chans; -init_waitqueue_head (&tdev->waitq);dev_info (&pdev->dev,"Successfully registered\n" );return 0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 739/957 mailbox: mailbox-test: make data_ready a per-instance variable
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Wolfram Sang <[email protected]>
[ Upstream commit 6e937f4e769e60947909e3525965f0137b9039e8 ]
While not the default case, multiple tests can be run simultaneously. Then, data_ready being a global variable will be overwritten and the per-instance lock will not help. Turn the global variable into a per-instance one to avoid this problem.
--- drivers/mailbox/mailbox-test.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-)
drivers/mailbox/mailbox-test.c +4 -5
@@ -28,8 +28,6 @@#define MBOX_HEXDUMP_MAX_LEN (MBOX_HEXDUMP_LINE_LEN * \ (MBOX_MAX_MSG_LEN / MBOX_BYTES_PER_LINE)) -static bool mbox_data_ready; -struct mbox_test_device {struct device *dev;void __iomem *tx_mmio;
@@ -42,6 +40,7 @@ struct mbox_test_device {spinlock_t lock;struct mutex mutex;wait_queue_head_t waitq; +bool data_ready;struct fasync_struct *async_queue;struct dentry *root_debugfs_dir; };
@@ -162,7 +161,7 @@ static bool mbox_test_message_data_ready(struct mbox_test_device *tdev)unsigned long flags;spin_lock_irqsave (&tdev->lock, flags); - data_ready = mbox_data_ready; + data_ready = tdev->data_ready;spin_unlock_irqrestore (&tdev->lock, flags);return data_ready;
@@ -227,7 +226,7 @@ static ssize_t mbox_test_message_read(struct file *filp, char __user *userbuf, *(touser + l) ='\0' ;memset (tdev->rx_buffer,0 ,MBOX_MAX_MSG_LEN ); - mbox_data_ready = false; + tdev->data_ready = false;spin_unlock_irqrestore (&tdev->lock, flags);
@@ -297,7 +296,7 @@ static void mbox_test_receive_message(struct mbox_client *client, void *message) message, MBOX_MAX_MSG_LEN);memcpy (tdev->rx_buffer, message,MBOX_MAX_MSG_LEN ); } - mbox_data_ready = true; + tdev->data_ready = true;spin_unlock_irqrestore (&tdev->lock, flags);wake_up_interruptible (&tdev->waitq);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 740/957 fsnotify: fix inode reference leak in fsnotify_recalc_mask()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Amir Goldstein <[email protected]>
[ Upstream commit 4aca914ac152f5d055ddcb36704d1e539ac08977 ]
fsnotify_recalc_mask() fails to handle the return value of __fsnotify_recalc_mask(), which may return an inode pointer that needs to be released via fsnotify_drop_object() when the connector's HAS_IREF flag transitions from set to cleared.
This manifests as a hung task with the following call trace:
INFO: task umount:1234 blocked for more than 120 seconds. Call Trace: __schedule schedule fsnotify_sb_delete generic_shutdown_super kill_anon_super cleanup_mnt task_work_run do_exit do_group_exit
The race window that triggers the iref leak:
Thread A (adding mark) Thread B (removing mark)
────────────────────── ────────────────────────
fsnotify_add_mark_locked():
fsnotify_add_mark_list():
spin_lock(conn->lock)
add mark_B(evictable) to list
spin_unlock(conn->lock)
return
/* ---- gap: no lock held ---- */
fsnotify_detach_mark(mark_A):
spin_lock(mark_A->lock)
clear ATTACHED flag on mark_A
spin_unlock(mark_A->lock)
fsnotify_put_mark(mark_A)
fsnotify_recalc_mask():
spin_lock(conn->lock)
__fsnotify_recalc_mask():
/* mark_A skipped: ATTACHED cleared */
/* only mark_B(evictable) remains */
want_iref = false
has_iref = true /* not yet cleared */
-> HAS_IREF transitions true -> false
-> returns inode pointer
spin_unlock(conn->lock)
/* BUG: return value discarded!
* iput() and fsnotify_put_sb_watched_objects()
* are never called */Fix this by deferring the transition true -> false of HAS_IREF flag from fsnotify_recalc_mask() (Thread A) to fsnotify_put_mark() (thread B).
--- fs/notify/mark.c | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-)
fs/notify/mark.c +36 -3
@@ -237,7 +237,12 @@ static struct inode *fsnotify_update_iref(struct fsnotify_mark_connector *conn,return inode; } -static void *__fsnotify_recalc_mask (struct fsnotify_mark_connector *conn) +/* +* Calculate mask of events for a list of marks. +* +* Return true if any of the attached marks want to hold an inode reference. +*/ +static bool __fsnotify_recalc_mask (struct fsnotify_mark_connector *conn) {u32 new_mask =0 ;bool want_iref = false;
@@ -261,6 +266,34 @@ static void *__fsnotify_recalc_mask(struct fsnotify_mark_connector *conn)*/ WRITE_ONCE (*fsnotify_conn_mask_p (conn), new_mask); +return want_iref; +} + +/* +* Calculate mask of events for a list of marks after attach/modify mark +* and get an inode reference for the connector if needed. +* +* A concurrent add of evictable mark and detach of non-evictable mark can +* lead to __fsnotify_recalc_mask() returning false want_iref, but in this +* case we defer clearing iref to fsnotify_recalc_mask_clear_iref() called +* from fsnotify_put_mark(). +*/ +static void fsnotify_recalc_mask_set_iref (struct fsnotify_mark_connector *conn) +{ +bool has_iref = conn->flags &FSNOTIFY_CONN_FLAG_HAS_IREF ; +bool want_iref =__fsnotify_recalc_mask (conn) || has_iref; + + (void )fsnotify_update_iref (conn, want_iref); +} + +/* +* Calculate mask of events for a list of marks after detach mark +* and return the inode object if its reference is no longer needed. +*/ +static void *fsnotify_recalc_mask_clear_iref (struct fsnotify_mark_connector *conn) +{ +bool want_iref =__fsnotify_recalc_mask (conn); +return fsnotify_update_iref (conn, want_iref); }
@@ -297,7 +330,7 @@ void fsnotify_recalc_mask(struct fsnotify_mark_connector *conn)spin_lock (&conn->lock); update_children = !fsnotify_conn_watches_children (conn); -__fsnotify_recalc_mask (conn); +fsnotify_recalc_mask_set_iref (conn); update_children &=fsnotify_conn_watches_children (conn);spin_unlock (&conn->lock);/*
@@ -415,7 +448,7 @@ void fsnotify_put_mark(struct fsnotify_mark *mark)/* Update watched objects after detaching mark */ if (sb)fsnotify_update_sb_watchers (sb, conn); - objp =__fsnotify_recalc_mask (conn); + objp =fsnotify_recalc_mask_clear_iref (conn); type = conn->type; }WRITE_ONCE (mark->connector,NULL );
Signature · 1 line
2.53.0