From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=100.103.45.18 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1794585689; b=fUPT7UQAz/qGiAg0mSIb/aMaq6m0v0vG3gMi0irLeKmBevN3Fy0JWP8AVP8SdJqg3Hf5 wOMq4ApfbPkUbRcBLWw1+pmnPJ3EBcodRim1Y0b8ao7pfUH8V/2+lpnzCRk/TN4s0vXzt Sc3ZHuFLM5zVQTEeadyreykMqV7MNCdZTf5Sw74mnic9bh+oIUPzKRRgylXINbCanih5h qkE/cV68LgHO+Ix7z27Wno21VQZYNfzd8A/s3Te3q4mFiNiLeFrgqvb7E/7ubh60NBeqw Y6Fjm+IhlwKakISf9rNc95djlKL4M8cSbl5z3I3vAucgpIUGQshB2TqZzTfIqnm/f1A== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1794585689; h=Received:DKIM-Signature:From:To:Cc:Subject:Date:Message-ID:X-Mailer: In-Reply-To:References:User-Agent:X-stable:X-Patchwork-Hint: MIME-Version:Content-Transfer-Encoding; bh=L+I6SSwFrMHnrbv0CyUv5qbO6i6rDNuHPPOf4eIDp/0=; b=v4fC0oD7f1iOjecYmg+Csx1CoXnmV3WqDiskNYXDwjf4p2fr2BP4k1hdYdRxPYG8Ngwo zgrgnB69CUhgIpeexQk0nZxE8aKg9/3K/ssKAqhZiCpLoljgEj8LjX+fUUt+/oVrG8+9l WJ1CWHJ8/ciETFxY6koyLP8aBsiwuA/nO0g/UVYvxe5p0YdIa6KHLNzaUe3QM1+XGy2q7 8LPCBYThwY7qx8S4TqBufkxklf0eYecuYHJ2xPh+xTJAhm9XOXdqbL5lQSsCtp/1xlFi/ IumEUsPDL4Qrpru7HZUu52OpwCKAH5RV+0ajbPza+iFO+37KD5FVZSRq1yr4dX12T7g== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass header.d=linuxfoundation.org header.i=@linuxfoundation.org header.a=rsa-sha256 header.s=korg header.b=ceEXASBV; arc=none smtp.remote-ip=100.103.45.18 Received: from smtp.kernel.org (quasi.space.kernel.org [100.103.45.18]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (prime256v1)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2FB0E60744ED; Fri, 13 Nov 2026 16:01:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linuxfoundation.org header.i=@linuxfoundation.org header.a=rsa-sha256 header.s=korg header.b=ceEXASBV Received: by smtp.kernel.org (Postfix) with ESMTPSA id D06C71F000C8; Fri, 13 Nov 2026 16:01:28 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linuxfoundation.org; s=korg; t=1794585689; bh=L+I6SSwFrMHnrbv0CyUv5qbO6i6rDNuHPPOf4eIDp/0=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=ceEXASBVM9hR7ft89b+b3FdXuHTI2LlvaxxdQCertAJwF4Mre3DM4GvTcg/H9iG/M PldlIoEVAX3H6upqe80fruOfNFUYPOjb4pRDtRew/LlkklSHWKQLGmTexE7fj4QdCS EyqLWURPgNwkmQPzY93bYN1iNhoZMyBUbLg4ig80= From: Greg Kroah-Hartman To: stable@vger.kernel.org Cc: Greg Kroah-Hartman , patches@lists.linux.dev, Runyu Xiao , Oliver Hartkopp , Marc Kleine-Budde , Sasha Levin Subject: [PATCH 7.3 298/507] can: proc: reset pkg_stats atomics individually Date: Fri, 13 Nov 2026 10:50:02 -0500 Message-ID: <20261113153805.344538863@linuxfoundation.org> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261113153753.358973689@linuxfoundation.org> References: <20261113153753.358973689@linuxfoundation.org> User-Agent: quilt/0.69 X-stable: review X-Patchwork-Hint: ignore X-Mailing-List: stable@vger.kernel.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Transfer-Encoding: 8bit 7.3-stable review patch. If anyone has any objections, please let me know. ------------------ From: Runyu Xiao [ Upstream commit 12297f855bbd943847cb8893a3614caef5a9c2a8 ] Commit 80b5f90158d1 ("can: statistics: use atomic access in hot path") converted several members of struct can_pkg_stats to atomic_long_t and updated the hot TX/RX and procfs read paths to use atomic_long_*() helpers. However, can_init_stats() still clears the whole struct with memset(). can_init_stats() is reached from can_stat_update() in timer context and also from the procfs reset path. Those paths can run while the TX/RX hot paths are concurrently updating rx_frames, tx_frames, matches and their *_delta counters. Hitting the whole-struct memset() in that window performs plain writes to fields that otherwise follow an atomic_long_t access contract, which can lose or mix live statistics updates. This issue was found by source-level API-misuse analysis looking for whole-object resets left behind after atomic_long_t conversions, then manually audited on Linux v6.18.21. Replace the whole-struct memset() with a helper that resets the atomic_long_t counters via atomic_long_set() and clears the derived scalar statistics explicitly. This preserves the existing reset semantics for scalar fields while restoring atomic access discipline for the live counters. Build-tested by compiling net/can/proc.o on x86_64 netdev/main. Runtime-tested with a QEMU + vcan setup on Linux v6.18.21 by driving concurrent traffic and reset_stats reads, which reproduced inconsistent exported statistics before the fix. [Hartkopp] can_stat_update() also read the *_delta counters at the beginning and cleared them with atomic_long_set() at the end. Increments from the hot path in between were lost for both the current and the next interval. Use atomic_long_xchg() to fetch and clear the *_delta counters in one atomic step instead. On x86_64 atomic_long_set() is a plain store like memset(), so the memset() is an actual problem only on architectures with lock-based atomics (e.g. parisc). The inconsistencies seen on x86_64 come from the *_delta handling and from the stale snapshots, which are fixed in "can: proc: skip stats calculation after reset in can_stat_update()". As before, the counters are reset one at a time and the derived scalar statistics use plain stores. Fixes: 80b5f90158d1 ("can: statistics: use atomic access in hot path") Cc: stable@vger.kernel.org Signed-off-by: Runyu Xiao Acked-by: Oliver Hartkopp Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20261027-can-rx-lock-v12-5-eb5afc2d8b9b@hartkopp.net Signed-off-by: Marc Kleine-Budde Signed-off-by: Sasha Levin --- net/can/proc.c | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/net/can/proc.c b/net/can/proc.c index de4d05ae34597..70104f13077e0 100644 --- a/net/can/proc.c +++ b/net/can/proc.c @@ -76,16 +76,39 @@ static const char rx_list_name[][8] = { * af_can statistics stuff */ +static void can_reset_pkg_stats(struct can_pkg_stats *pkg_stats) +{ + atomic_long_set(&pkg_stats->rx_frames, 0); + atomic_long_set(&pkg_stats->tx_frames, 0); + atomic_long_set(&pkg_stats->matches, 0); + + pkg_stats->total_rx_rate = 0; + pkg_stats->total_tx_rate = 0; + pkg_stats->total_rx_match_ratio = 0; + + pkg_stats->current_rx_rate = 0; + pkg_stats->current_tx_rate = 0; + pkg_stats->current_rx_match_ratio = 0; + + pkg_stats->max_rx_rate = 0; + pkg_stats->max_tx_rate = 0; + pkg_stats->max_rx_match_ratio = 0; + + atomic_long_set(&pkg_stats->rx_frames_delta, 0); + atomic_long_set(&pkg_stats->tx_frames_delta, 0); + atomic_long_set(&pkg_stats->matches_delta, 0); +} + static void can_init_stats(struct net *net) { struct can_pkg_stats *pkg_stats = net->can.pkg_stats; struct can_rcv_lists_stats *rcv_lists_stats = net->can.rcv_lists_stats; /* - * This memset function is called from a timer context (when + * This stats reset is called from a timer context (when * can_stattimer is active which is the default) OR in a process * context (reading the proc_fs when can_stattimer is disabled). */ - memset(pkg_stats, 0, sizeof(struct can_pkg_stats)); + can_reset_pkg_stats(pkg_stats); pkg_stats->jiffies_init = jiffies; rcv_lists_stats->stats_reset++; @@ -121,9 +144,10 @@ void can_stat_update(struct timer_list *t) long rx_frames = atomic_long_read(&pkg_stats->rx_frames); long tx_frames = atomic_long_read(&pkg_stats->tx_frames); long matches = atomic_long_read(&pkg_stats->matches); - long rx_frames_delta = atomic_long_read(&pkg_stats->rx_frames_delta); - long tx_frames_delta = atomic_long_read(&pkg_stats->tx_frames_delta); - long matches_delta = atomic_long_read(&pkg_stats->matches_delta); + /* fetch and clear values for 'current rate' calculation */ + long rx_frames_delta = atomic_long_xchg(&pkg_stats->rx_frames_delta, 0); + long tx_frames_delta = atomic_long_xchg(&pkg_stats->tx_frames_delta, 0); + long matches_delta = atomic_long_xchg(&pkg_stats->matches_delta, 0); /* restart counting in timer context on user request */ if (user_reset) @@ -172,11 +196,6 @@ void can_stat_update(struct timer_list *t) if (pkg_stats->max_rx_match_ratio < pkg_stats->current_rx_match_ratio) pkg_stats->max_rx_match_ratio = pkg_stats->current_rx_match_ratio; - /* clear values for 'current rate' calculation */ - atomic_long_set(&pkg_stats->tx_frames_delta, 0); - atomic_long_set(&pkg_stats->rx_frames_delta, 0); - atomic_long_set(&pkg_stats->matches_delta, 0); - /* restart timer (one second) */ mod_timer(&net->can.stattimer, round_jiffies(jiffies + HZ)); } -- 2.53.0