--- /dev/null
+From a4373a52c02c78578e1f41a621e5297f376afd73 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 7 Feb 2025 13:58:36 +0000
+Subject: arp: use RCU protection in arp_xmit()
+
+From: Eric Dumazet <edumazet@google.com>
+
+[ Upstream commit a42b69f692165ec39db42d595f4f65a4c8f42e44 ]
+
+arp_xmit() can be called without RTNL or RCU protection.
+
+Use RCU protection to avoid potential UAF.
+
+Fixes: 29a26a568038 ("netfilter: Pass struct net into the netfilter hooks")
+Signed-off-by: Eric Dumazet <edumazet@google.com>
+Reviewed-by: David Ahern <dsahern@kernel.org>
+Reviewed-by: Kuniyuki Iwashima <kuniyu@amazon.com>
+Link: https://patch.msgid.link/20250207135841.1948589-5-edumazet@google.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/ipv4/arp.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c
+index 0d0d725b46ad0..02776453bf97a 100644
+--- a/net/ipv4/arp.c
++++ b/net/ipv4/arp.c
+@@ -658,10 +658,12 @@ static int arp_xmit_finish(struct net *net, struct sock *sk, struct sk_buff *skb
+ */
+ void arp_xmit(struct sk_buff *skb)
+ {
++ rcu_read_lock();
+ /* Send it off, maybe filter it using firewalling first. */
+ NF_HOOK(NFPROTO_ARP, NF_ARP_OUT,
+- dev_net(skb->dev), NULL, skb, NULL, skb->dev,
++ dev_net_rcu(skb->dev), NULL, skb, NULL, skb->dev,
+ arp_xmit_finish);
++ rcu_read_unlock();
+ }
+ EXPORT_SYMBOL(arp_xmit);
+
+--
+2.39.5
+
--- /dev/null
+From f4b49af215b2381b38525bc1225c90c5e29806f2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 31 Jan 2025 12:33:23 -0500
+Subject: clocksource: Use migrate_disable() to avoid calling get_random_u32()
+ in atomic context
+
+From: Waiman Long <longman@redhat.com>
+
+[ Upstream commit 6bb05a33337b2c842373857b63de5c9bf1ae2a09 ]
+
+The following bug report happened with a PREEMPT_RT kernel:
+
+ BUG: sleeping function called from invalid context at kernel/locking/spinlock_rt.c:48
+ in_atomic(): 1, irqs_disabled(): 0, non_block: 0, pid: 2012, name: kwatchdog
+ preempt_count: 1, expected: 0
+ RCU nest depth: 0, expected: 0
+ get_random_u32+0x4f/0x110
+ clocksource_verify_choose_cpus+0xab/0x1a0
+ clocksource_verify_percpu.part.0+0x6b/0x330
+ clocksource_watchdog_kthread+0x193/0x1a0
+
+It is due to the fact that clocksource_verify_choose_cpus() is invoked with
+preemption disabled. This function invokes get_random_u32() to obtain
+random numbers for choosing CPUs. The batched_entropy_32 local lock and/or
+the base_crng.lock spinlock in driver/char/random.c will be acquired during
+the call. In PREEMPT_RT kernel, they are both sleeping locks and so cannot
+be acquired in atomic context.
+
+Fix this problem by using migrate_disable() to allow smp_processor_id() to
+be reliably used without introducing atomic context. preempt_disable() is
+then called after clocksource_verify_choose_cpus() but before the
+clocksource measurement is being run to avoid introducing unexpected
+latency.
+
+Fixes: 7560c02bdffb ("clocksource: Check per-CPU clock synchronization when marked unstable")
+Suggested-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+Signed-off-by: Waiman Long <longman@redhat.com>
+Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
+Reviewed-by: Paul E. McKenney <paulmck@kernel.org>
+Reviewed-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+Link: https://lore.kernel.org/all/20250131173323.891943-2-longman@redhat.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ kernel/time/clocksource.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c
+index 6b5b7c9c14732..3130f24daf597 100644
+--- a/kernel/time/clocksource.c
++++ b/kernel/time/clocksource.c
+@@ -351,10 +351,10 @@ void clocksource_verify_percpu(struct clocksource *cs)
+ cpumask_clear(&cpus_ahead);
+ cpumask_clear(&cpus_behind);
+ cpus_read_lock();
+- preempt_disable();
++ migrate_disable();
+ clocksource_verify_choose_cpus();
+ if (cpumask_empty(&cpus_chosen)) {
+- preempt_enable();
++ migrate_enable();
+ cpus_read_unlock();
+ pr_warn("Not enough CPUs to check clocksource '%s'.\n", cs->name);
+ return;
+@@ -362,6 +362,7 @@ void clocksource_verify_percpu(struct clocksource *cs)
+ testcpu = smp_processor_id();
+ pr_info("Checking clocksource %s synchronization from CPU %d to CPUs %*pbl.\n",
+ cs->name, testcpu, cpumask_pr_args(&cpus_chosen));
++ preempt_disable();
+ for_each_cpu(cpu, &cpus_chosen) {
+ if (cpu == testcpu)
+ continue;
+@@ -381,6 +382,7 @@ void clocksource_verify_percpu(struct clocksource *cs)
+ cs_nsec_min = cs_nsec;
+ }
+ preempt_enable();
++ migrate_enable();
+ cpus_read_unlock();
+ if (!cpumask_empty(&cpus_ahead))
+ pr_warn(" CPUs %*pbl ahead of CPU %d for clocksource %s.\n",
+--
+2.39.5
+
--- /dev/null
+From b28dd542264fa90d8c0988eeac61f7ceba087a80 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jan 2025 20:54:41 -0500
+Subject: clocksource: Use pr_info() for "Checking clocksource synchronization"
+ message
+
+From: Waiman Long <longman@redhat.com>
+
+[ Upstream commit 1f566840a82982141f94086061927a90e79440e5 ]
+
+The "Checking clocksource synchronization" message is normally printed
+when clocksource_verify_percpu() is called for a given clocksource if
+both the CLOCK_SOURCE_UNSTABLE and CLOCK_SOURCE_VERIFY_PERCPU flags
+are set.
+
+It is an informational message and so pr_info() is the correct choice.
+
+Signed-off-by: Waiman Long <longman@redhat.com>
+Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
+Reviewed-by: Paul E. McKenney <paulmck@kernel.org>
+Acked-by: John Stultz <jstultz@google.com>
+Link: https://lore.kernel.org/all/20250125015442.3740588-1-longman@redhat.com
+Stable-dep-of: 6bb05a33337b ("clocksource: Use migrate_disable() to avoid calling get_random_u32() in atomic context")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ kernel/time/clocksource.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c
+index aa864999dc21b..6b5b7c9c14732 100644
+--- a/kernel/time/clocksource.c
++++ b/kernel/time/clocksource.c
+@@ -360,7 +360,8 @@ void clocksource_verify_percpu(struct clocksource *cs)
+ return;
+ }
+ testcpu = smp_processor_id();
+- pr_warn("Checking clocksource %s synchronization from CPU %d to CPUs %*pbl.\n", cs->name, testcpu, cpumask_pr_args(&cpus_chosen));
++ pr_info("Checking clocksource %s synchronization from CPU %d to CPUs %*pbl.\n",
++ cs->name, testcpu, cpumask_pr_args(&cpus_chosen));
+ for_each_cpu(cpu, &cpus_chosen) {
+ if (cpu == testcpu)
+ continue;
+--
+2.39.5
+
--- /dev/null
+From 8335e45f7d639dec74d53ee96a1ab33d7cf6f737 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 5 Feb 2025 15:51:17 +0000
+Subject: flow_dissector: use RCU protection to fetch dev_net()
+
+From: Eric Dumazet <edumazet@google.com>
+
+[ Upstream commit afec62cd0a4191cde6dd3a75382be4d51a38ce9b ]
+
+__skb_flow_dissect() can be called from arbitrary contexts.
+
+It must extend its RCU protection section to include
+the call to dev_net(), which can become dev_net_rcu().
+
+This makes sure the net structure can not disappear under us.
+
+Fixes: 9b52e3f267a6 ("flow_dissector: handle no-skb use case")
+Signed-off-by: Eric Dumazet <edumazet@google.com>
+Reviewed-by: Kuniyuki Iwashima <kuniyu@amazon.com>
+Link: https://patch.msgid.link/20250205155120.1676781-10-edumazet@google.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/core/flow_dissector.c | 21 +++++++++++----------
+ 1 file changed, 11 insertions(+), 10 deletions(-)
+
+diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c
+index b22d20cc417b2..00a5c41c1831d 100644
+--- a/net/core/flow_dissector.c
++++ b/net/core/flow_dissector.c
+@@ -1084,10 +1084,12 @@ bool __skb_flow_dissect(const struct net *net,
+ FLOW_DISSECTOR_KEY_BASIC,
+ target_container);
+
++ rcu_read_lock();
++
+ if (skb) {
+ if (!net) {
+ if (skb->dev)
+- net = dev_net(skb->dev);
++ net = dev_net_rcu(skb->dev);
+ else if (skb->sk)
+ net = sock_net(skb->sk);
+ }
+@@ -1098,7 +1100,6 @@ bool __skb_flow_dissect(const struct net *net,
+ enum netns_bpf_attach_type type = NETNS_BPF_FLOW_DISSECTOR;
+ struct bpf_prog_array *run_array;
+
+- rcu_read_lock();
+ run_array = rcu_dereference(init_net.bpf.run_array[type]);
+ if (!run_array)
+ run_array = rcu_dereference(net->bpf.run_array[type]);
+@@ -1126,17 +1127,17 @@ bool __skb_flow_dissect(const struct net *net,
+ prog = READ_ONCE(run_array->items[0].prog);
+ result = bpf_flow_dissect(prog, &ctx, n_proto, nhoff,
+ hlen, flags);
+- if (result == BPF_FLOW_DISSECTOR_CONTINUE)
+- goto dissect_continue;
+- __skb_flow_bpf_to_target(&flow_keys, flow_dissector,
+- target_container);
+- rcu_read_unlock();
+- return result == BPF_OK;
++ if (result != BPF_FLOW_DISSECTOR_CONTINUE) {
++ __skb_flow_bpf_to_target(&flow_keys, flow_dissector,
++ target_container);
++ rcu_read_unlock();
++ return result == BPF_OK;
++ }
+ }
+-dissect_continue:
+- rcu_read_unlock();
+ }
+
++ rcu_read_unlock();
++
+ if (dissector_uses_key(flow_dissector,
+ FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
+ struct ethhdr *eth = eth_hdr(skb);
+--
+2.39.5
+
--- /dev/null
+From 985feefa48ba4b5652d705192fca2f19e97672ad Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 20 Apr 2024 12:34:18 +0000
+Subject: HID: hid-steam: Add Deck IMU support
+
+From: Max Maisel <mmm-1@posteo.net>
+
+[ Upstream commit 3347e1654f24dbbd357ea4e3c0d8dcc12d8586c7 ]
+
+The Deck's controller features an accelerometer and gyroscope which
+send their measurement values by default in the main HID input report.
+Expose both sensors to userspace through a separate evdev node as it
+is done by the hid-nintendo and hid-playstation drivers.
+
+Signed-off-by: Max Maisel <mmm-1@posteo.net>
+Reviewed-by: Vicki Pfau <vi@endrift.com>
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Stable-dep-of: 79504249d7e2 ("HID: hid-steam: Move hidraw input (un)registering to work")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hid/hid-steam.c | 155 +++++++++++++++++++++++++++++++++++++---
+ 1 file changed, 147 insertions(+), 8 deletions(-)
+
+diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c
+index 59b46163bc526..29a0e1f395339 100644
+--- a/drivers/hid/hid-steam.c
++++ b/drivers/hid/hid-steam.c
+@@ -66,6 +66,14 @@ static LIST_HEAD(steam_devices);
+ #define STEAM_DECK_TRIGGER_RESOLUTION 5461
+ /* Joystick runs are about 5 mm and 32768 units */
+ #define STEAM_DECK_JOYSTICK_RESOLUTION 6553
++/* Accelerometer has 16 bit resolution and a range of +/- 2g */
++#define STEAM_DECK_ACCEL_RES_PER_G 16384
++#define STEAM_DECK_ACCEL_RANGE 32768
++#define STEAM_DECK_ACCEL_FUZZ 32
++/* Gyroscope has 16 bit resolution and a range of +/- 2000 dps */
++#define STEAM_DECK_GYRO_RES_PER_DPS 16
++#define STEAM_DECK_GYRO_RANGE 32768
++#define STEAM_DECK_GYRO_FUZZ 1
+
+ #define STEAM_PAD_FUZZ 256
+
+@@ -288,6 +296,7 @@ struct steam_device {
+ struct mutex report_mutex;
+ bool client_opened;
+ struct input_dev __rcu *input;
++ struct input_dev __rcu *sensors;
+ unsigned long quirks;
+ struct work_struct work_connect;
+ bool connected;
+@@ -302,6 +311,7 @@ struct steam_device {
+ struct work_struct rumble_work;
+ u16 rumble_left;
+ u16 rumble_right;
++ unsigned int sensor_timestamp_us;
+ };
+
+ static int steam_recv_report(struct steam_device *steam,
+@@ -824,6 +834,74 @@ static int steam_input_register(struct steam_device *steam)
+ return ret;
+ }
+
++static int steam_sensors_register(struct steam_device *steam)
++{
++ struct hid_device *hdev = steam->hdev;
++ struct input_dev *sensors;
++ int ret;
++
++ if (!(steam->quirks & STEAM_QUIRK_DECK))
++ return 0;
++
++ rcu_read_lock();
++ sensors = rcu_dereference(steam->sensors);
++ rcu_read_unlock();
++ if (sensors) {
++ dbg_hid("%s: already connected\n", __func__);
++ return 0;
++ }
++
++ sensors = input_allocate_device();
++ if (!sensors)
++ return -ENOMEM;
++
++ input_set_drvdata(sensors, steam);
++ sensors->dev.parent = &hdev->dev;
++
++ sensors->name = "Steam Deck Motion Sensors";
++ sensors->phys = hdev->phys;
++ sensors->uniq = steam->serial_no;
++ sensors->id.bustype = hdev->bus;
++ sensors->id.vendor = hdev->vendor;
++ sensors->id.product = hdev->product;
++ sensors->id.version = hdev->version;
++
++ __set_bit(INPUT_PROP_ACCELEROMETER, sensors->propbit);
++ __set_bit(EV_MSC, sensors->evbit);
++ __set_bit(MSC_TIMESTAMP, sensors->mscbit);
++
++ input_set_abs_params(sensors, ABS_X, -STEAM_DECK_ACCEL_RANGE,
++ STEAM_DECK_ACCEL_RANGE, STEAM_DECK_ACCEL_FUZZ, 0);
++ input_set_abs_params(sensors, ABS_Y, -STEAM_DECK_ACCEL_RANGE,
++ STEAM_DECK_ACCEL_RANGE, STEAM_DECK_ACCEL_FUZZ, 0);
++ input_set_abs_params(sensors, ABS_Z, -STEAM_DECK_ACCEL_RANGE,
++ STEAM_DECK_ACCEL_RANGE, STEAM_DECK_ACCEL_FUZZ, 0);
++ input_abs_set_res(sensors, ABS_X, STEAM_DECK_ACCEL_RES_PER_G);
++ input_abs_set_res(sensors, ABS_Y, STEAM_DECK_ACCEL_RES_PER_G);
++ input_abs_set_res(sensors, ABS_Z, STEAM_DECK_ACCEL_RES_PER_G);
++
++ input_set_abs_params(sensors, ABS_RX, -STEAM_DECK_GYRO_RANGE,
++ STEAM_DECK_GYRO_RANGE, STEAM_DECK_GYRO_FUZZ, 0);
++ input_set_abs_params(sensors, ABS_RY, -STEAM_DECK_GYRO_RANGE,
++ STEAM_DECK_GYRO_RANGE, STEAM_DECK_GYRO_FUZZ, 0);
++ input_set_abs_params(sensors, ABS_RZ, -STEAM_DECK_GYRO_RANGE,
++ STEAM_DECK_GYRO_RANGE, STEAM_DECK_GYRO_FUZZ, 0);
++ input_abs_set_res(sensors, ABS_RX, STEAM_DECK_GYRO_RES_PER_DPS);
++ input_abs_set_res(sensors, ABS_RY, STEAM_DECK_GYRO_RES_PER_DPS);
++ input_abs_set_res(sensors, ABS_RZ, STEAM_DECK_GYRO_RES_PER_DPS);
++
++ ret = input_register_device(sensors);
++ if (ret)
++ goto sensors_register_fail;
++
++ rcu_assign_pointer(steam->sensors, sensors);
++ return 0;
++
++sensors_register_fail:
++ input_free_device(sensors);
++ return ret;
++}
++
+ static void steam_input_unregister(struct steam_device *steam)
+ {
+ struct input_dev *input;
+@@ -837,6 +915,24 @@ static void steam_input_unregister(struct steam_device *steam)
+ input_unregister_device(input);
+ }
+
++static void steam_sensors_unregister(struct steam_device *steam)
++{
++ struct input_dev *sensors;
++
++ if (!(steam->quirks & STEAM_QUIRK_DECK))
++ return;
++
++ rcu_read_lock();
++ sensors = rcu_dereference(steam->sensors);
++ rcu_read_unlock();
++
++ if (!sensors)
++ return;
++ RCU_INIT_POINTER(steam->sensors, NULL);
++ synchronize_rcu();
++ input_unregister_device(sensors);
++}
++
+ static void steam_battery_unregister(struct steam_device *steam)
+ {
+ struct power_supply *battery;
+@@ -889,18 +985,28 @@ static int steam_register(struct steam_device *steam)
+ spin_lock_irqsave(&steam->lock, flags);
+ client_opened = steam->client_opened;
+ spin_unlock_irqrestore(&steam->lock, flags);
++
+ if (!client_opened) {
+ steam_set_lizard_mode(steam, lizard_mode);
+ ret = steam_input_register(steam);
+- } else
+- ret = 0;
++ if (ret != 0)
++ goto steam_register_input_fail;
++ ret = steam_sensors_register(steam);
++ if (ret != 0)
++ goto steam_register_sensors_fail;
++ }
++ return 0;
+
++steam_register_sensors_fail:
++ steam_input_unregister(steam);
++steam_register_input_fail:
+ return ret;
+ }
+
+ static void steam_unregister(struct steam_device *steam)
+ {
+ steam_battery_unregister(steam);
++ steam_sensors_unregister(steam);
+ steam_input_unregister(steam);
+ if (steam->serial_no[0]) {
+ hid_info(steam->hdev, "Steam Controller '%s' disconnected",
+@@ -1009,6 +1115,7 @@ static int steam_client_ll_open(struct hid_device *hdev)
+ steam->client_opened = true;
+ spin_unlock_irqrestore(&steam->lock, flags);
+
++ steam_sensors_unregister(steam);
+ steam_input_unregister(steam);
+
+ return 0;
+@@ -1029,6 +1136,7 @@ static void steam_client_ll_close(struct hid_device *hdev)
+ if (connected) {
+ steam_set_lizard_mode(steam, lizard_mode);
+ steam_input_register(steam);
++ steam_sensors_register(steam);
+ }
+ }
+
+@@ -1120,6 +1228,7 @@ static int steam_probe(struct hid_device *hdev,
+ INIT_DELAYED_WORK(&steam->mode_switch, steam_mode_switch_cb);
+ INIT_LIST_HEAD(&steam->list);
+ INIT_WORK(&steam->rumble_work, steam_haptic_rumble_cb);
++ steam->sensor_timestamp_us = 0;
+
+ /*
+ * With the real steam controller interface, do not connect hidraw.
+@@ -1379,12 +1488,12 @@ static void steam_do_input_event(struct steam_device *steam,
+ * 18-19 | s16 | ABS_HAT0Y | left-pad Y value
+ * 20-21 | s16 | ABS_HAT1X | right-pad X value
+ * 22-23 | s16 | ABS_HAT1Y | right-pad Y value
+- * 24-25 | s16 | -- | accelerometer X value
+- * 26-27 | s16 | -- | accelerometer Y value
+- * 28-29 | s16 | -- | accelerometer Z value
+- * 30-31 | s16 | -- | gyro X value
+- * 32-33 | s16 | -- | gyro Y value
+- * 34-35 | s16 | -- | gyro Z value
++ * 24-25 | s16 | IMU ABS_X | accelerometer X value
++ * 26-27 | s16 | IMU ABS_Z | accelerometer Y value
++ * 28-29 | s16 | IMU ABS_Y | accelerometer Z value
++ * 30-31 | s16 | IMU ABS_RX | gyro X value
++ * 32-33 | s16 | IMU ABS_RZ | gyro Y value
++ * 34-35 | s16 | IMU ABS_RY | gyro Z value
+ * 36-37 | s16 | -- | quaternion W value
+ * 38-39 | s16 | -- | quaternion X value
+ * 40-41 | s16 | -- | quaternion Y value
+@@ -1545,6 +1654,32 @@ static void steam_do_deck_input_event(struct steam_device *steam,
+ input_sync(input);
+ }
+
++static void steam_do_deck_sensors_event(struct steam_device *steam,
++ struct input_dev *sensors, u8 *data)
++{
++ /*
++ * The deck input report is received every 4 ms on average,
++ * with a jitter of +/- 4 ms even though the USB descriptor claims
++ * that it uses 1 kHz.
++ * Since the HID report does not include a sensor timestamp,
++ * use a fixed increment here.
++ */
++ steam->sensor_timestamp_us += 4000;
++
++ if (!steam->gamepad_mode)
++ return;
++
++ input_event(sensors, EV_MSC, MSC_TIMESTAMP, steam->sensor_timestamp_us);
++ input_report_abs(sensors, ABS_X, steam_le16(data + 24));
++ input_report_abs(sensors, ABS_Z, -steam_le16(data + 26));
++ input_report_abs(sensors, ABS_Y, steam_le16(data + 28));
++ input_report_abs(sensors, ABS_RX, steam_le16(data + 30));
++ input_report_abs(sensors, ABS_RZ, -steam_le16(data + 32));
++ input_report_abs(sensors, ABS_RY, steam_le16(data + 34));
++
++ input_sync(sensors);
++}
++
+ /*
+ * The size for this message payload is 11.
+ * The known values are:
+@@ -1582,6 +1717,7 @@ static int steam_raw_event(struct hid_device *hdev,
+ {
+ struct steam_device *steam = hid_get_drvdata(hdev);
+ struct input_dev *input;
++ struct input_dev *sensors;
+ struct power_supply *battery;
+
+ if (!steam)
+@@ -1627,6 +1763,9 @@ static int steam_raw_event(struct hid_device *hdev,
+ input = rcu_dereference(steam->input);
+ if (likely(input))
+ steam_do_deck_input_event(steam, input, data);
++ sensors = rcu_dereference(steam->sensors);
++ if (likely(sensors))
++ steam_do_deck_sensors_event(steam, sensors, data);
+ rcu_read_unlock();
+ break;
+ case ID_CONTROLLER_WIRELESS:
+--
+2.39.5
+
--- /dev/null
+From 327baaefb2fef1b920ba1500e5b53d2a664b6d4b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 19 Dec 2023 19:38:37 -0800
+Subject: HID: hid-steam: Add gamepad-only mode switched to by holding options
+
+From: Vicki Pfau <vi@endrift.com>
+
+[ Upstream commit cd438e57dd05b077f4e87c1567beafb2377b6d6b ]
+
+This commit adds a hotkey to switch between "gamepad" mode (mouse and keyboard
+disabled) and "desktop" mode (gamepad disabled) by holding down the options
+button (mapped here as the start button). This mirrors the behavior of the
+official Steam client.
+
+This also adds and uses a function for generating haptic pulses, as Steam also
+does when engaging this hotkey.
+
+Signed-off-by: Vicki Pfau <vi@endrift.com>
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Stable-dep-of: 79504249d7e2 ("HID: hid-steam: Move hidraw input (un)registering to work")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hid/hid-steam.c | 113 ++++++++++++++++++++++++++++++++++++----
+ 1 file changed, 103 insertions(+), 10 deletions(-)
+
+diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c
+index 991db5acf5ddb..2f87026f01de1 100644
+--- a/drivers/hid/hid-steam.c
++++ b/drivers/hid/hid-steam.c
+@@ -273,6 +273,11 @@ enum {
+ TRACKPAD_GESTURE_KEYBOARD,
+ };
+
++/* Pad identifiers for the deck */
++#define STEAM_PAD_LEFT 0
++#define STEAM_PAD_RIGHT 1
++#define STEAM_PAD_BOTH 2
++
+ /* Other random constants */
+ #define STEAM_SERIAL_LEN 10
+
+@@ -291,6 +296,9 @@ struct steam_device {
+ struct power_supply __rcu *battery;
+ u8 battery_charge;
+ u16 voltage;
++ struct delayed_work mode_switch;
++ bool did_mode_switch;
++ bool gamepad_mode;
+ struct work_struct rumble_work;
+ u16 rumble_left;
+ u16 rumble_right;
+@@ -459,6 +467,37 @@ static inline int steam_request_conn_status(struct steam_device *steam)
+ return ret;
+ }
+
++/*
++ * Send a haptic pulse to the trackpads
++ * Duration and interval are measured in microseconds, count is the number
++ * of pulses to send for duration time with interval microseconds between them
++ * and gain is measured in decibels, ranging from -24 to +6
++ */
++static inline int steam_haptic_pulse(struct steam_device *steam, u8 pad,
++ u16 duration, u16 interval, u16 count, u8 gain)
++{
++ int ret;
++ u8 report[10] = {ID_TRIGGER_HAPTIC_PULSE, 8};
++
++ /* Left and right are swapped on this report for legacy reasons */
++ if (pad < STEAM_PAD_BOTH)
++ pad ^= 1;
++
++ report[2] = pad;
++ report[3] = duration & 0xFF;
++ report[4] = duration >> 8;
++ report[5] = interval & 0xFF;
++ report[6] = interval >> 8;
++ report[7] = count & 0xFF;
++ report[8] = count >> 8;
++ report[9] = gain;
++
++ mutex_lock(&steam->report_mutex);
++ ret = steam_send_report(steam, report, sizeof(report));
++ mutex_unlock(&steam->report_mutex);
++ return ret;
++}
++
+ static inline int steam_haptic_rumble(struct steam_device *steam,
+ u16 intensity, u16 left_speed, u16 right_speed,
+ u8 left_gain, u8 right_gain)
+@@ -504,6 +543,9 @@ static int steam_play_effect(struct input_dev *dev, void *data,
+
+ static void steam_set_lizard_mode(struct steam_device *steam, bool enable)
+ {
++ if (steam->gamepad_mode)
++ enable = false;
++
+ if (enable) {
+ mutex_lock(&steam->report_mutex);
+ /* enable esc, enter, cursors */
+@@ -541,11 +583,18 @@ static int steam_input_open(struct input_dev *dev)
+ unsigned long flags;
+ bool set_lizard_mode;
+
+- spin_lock_irqsave(&steam->lock, flags);
+- set_lizard_mode = !steam->client_opened && lizard_mode;
+- spin_unlock_irqrestore(&steam->lock, flags);
+- if (set_lizard_mode)
+- steam_set_lizard_mode(steam, false);
++ /*
++ * Disabling lizard mode automatically is only done on the Steam
++ * Controller. On the Steam Deck, this is toggled manually by holding
++ * the options button instead, handled by steam_mode_switch_cb.
++ */
++ if (!(steam->quirks & STEAM_QUIRK_DECK)) {
++ spin_lock_irqsave(&steam->lock, flags);
++ set_lizard_mode = !steam->client_opened && lizard_mode;
++ spin_unlock_irqrestore(&steam->lock, flags);
++ if (set_lizard_mode)
++ steam_set_lizard_mode(steam, false);
++ }
+
+ return 0;
+ }
+@@ -556,11 +605,13 @@ static void steam_input_close(struct input_dev *dev)
+ unsigned long flags;
+ bool set_lizard_mode;
+
+- spin_lock_irqsave(&steam->lock, flags);
+- set_lizard_mode = !steam->client_opened && lizard_mode;
+- spin_unlock_irqrestore(&steam->lock, flags);
+- if (set_lizard_mode)
+- steam_set_lizard_mode(steam, true);
++ if (!(steam->quirks & STEAM_QUIRK_DECK)) {
++ spin_lock_irqsave(&steam->lock, flags);
++ set_lizard_mode = !steam->client_opened && lizard_mode;
++ spin_unlock_irqrestore(&steam->lock, flags);
++ if (set_lizard_mode)
++ steam_set_lizard_mode(steam, true);
++ }
+ }
+
+ static enum power_supply_property steam_battery_props[] = {
+@@ -885,6 +936,34 @@ static void steam_work_connect_cb(struct work_struct *work)
+ }
+ }
+
++static void steam_mode_switch_cb(struct work_struct *work)
++{
++ struct steam_device *steam = container_of(to_delayed_work(work),
++ struct steam_device, mode_switch);
++ unsigned long flags;
++ bool client_opened;
++ steam->gamepad_mode = !steam->gamepad_mode;
++ if (!lizard_mode)
++ return;
++
++ if (steam->gamepad_mode)
++ steam_set_lizard_mode(steam, false);
++ else {
++ spin_lock_irqsave(&steam->lock, flags);
++ client_opened = steam->client_opened;
++ spin_unlock_irqrestore(&steam->lock, flags);
++ if (!client_opened)
++ steam_set_lizard_mode(steam, lizard_mode);
++ }
++
++ steam_haptic_pulse(steam, STEAM_PAD_RIGHT, 0x190, 0, 1, 0);
++ if (steam->gamepad_mode) {
++ steam_haptic_pulse(steam, STEAM_PAD_LEFT, 0x14D, 0x14D, 0x2D, 0);
++ } else {
++ steam_haptic_pulse(steam, STEAM_PAD_LEFT, 0x1F4, 0x1F4, 0x1E, 0);
++ }
++}
++
+ static bool steam_is_valve_interface(struct hid_device *hdev)
+ {
+ struct hid_report_enum *rep_enum;
+@@ -1039,6 +1118,7 @@ static int steam_probe(struct hid_device *hdev,
+ mutex_init(&steam->report_mutex);
+ steam->quirks = id->driver_data;
+ INIT_WORK(&steam->work_connect, steam_work_connect_cb);
++ INIT_DELAYED_WORK(&steam->mode_switch, steam_mode_switch_cb);
+ INIT_LIST_HEAD(&steam->list);
+ INIT_WORK(&steam->rumble_work, steam_haptic_rumble_cb);
+
+@@ -1096,6 +1176,7 @@ static int steam_probe(struct hid_device *hdev,
+ hid_hw_open_fail:
+ hid_hw_start_fail:
+ cancel_work_sync(&steam->work_connect);
++ cancel_delayed_work_sync(&steam->mode_switch);
+ cancel_work_sync(&steam->rumble_work);
+ steam_alloc_fail:
+ hid_err(hdev, "%s: failed with error %d\n",
+@@ -1112,6 +1193,7 @@ static void steam_remove(struct hid_device *hdev)
+ return;
+ }
+
++ cancel_delayed_work_sync(&steam->mode_switch);
+ cancel_work_sync(&steam->work_connect);
+ hid_destroy_device(steam->client_hdev);
+ steam->client_hdev = NULL;
+@@ -1397,6 +1479,17 @@ static void steam_do_deck_input_event(struct steam_device *steam,
+ b13 = data[13];
+ b14 = data[14];
+
++ if (!(b9 & BIT(6)) && steam->did_mode_switch) {
++ steam->did_mode_switch = false;
++ cancel_delayed_work_sync(&steam->mode_switch);
++ } else if (!steam->client_opened && (b9 & BIT(6)) && !steam->did_mode_switch) {
++ steam->did_mode_switch = true;
++ schedule_delayed_work(&steam->mode_switch, 45 * HZ / 100);
++ }
++
++ if (!steam->gamepad_mode)
++ return;
++
+ lpad_touched = b10 & BIT(3);
+ rpad_touched = b10 & BIT(4);
+
+--
+2.39.5
+
--- /dev/null
+From 0f8400facd03cd52f854dec6f79173f69876931d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 19 Dec 2023 19:38:31 -0800
+Subject: HID: hid-steam: Avoid overwriting smoothing parameter
+
+From: Vicki Pfau <vi@endrift.com>
+
+[ Upstream commit 34281b4d916f167a6f77975380e1df07f06248b7 ]
+
+The original implementation of this driver incorrectly guessed the function of
+this register. It's not only unnecessary to write to this register for lizard
+mode but actually counter-productive since it overwrites whatever previous
+value was intentionally set, for example by Steam.
+
+Signed-off-by: Vicki Pfau <vi@endrift.com>
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Stable-dep-of: 79504249d7e2 ("HID: hid-steam: Move hidraw input (un)registering to work")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hid/hid-steam.c | 5 -----
+ 1 file changed, 5 deletions(-)
+
+diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c
+index b110818fc9458..7aefd52e945a1 100644
+--- a/drivers/hid/hid-steam.c
++++ b/drivers/hid/hid-steam.c
+@@ -340,9 +340,6 @@ static void steam_set_lizard_mode(struct steam_device *steam, bool enable)
+ steam_send_report_byte(steam, STEAM_CMD_DEFAULT_MAPPINGS);
+ /* enable mouse */
+ steam_send_report_byte(steam, STEAM_CMD_DEFAULT_MOUSE);
+- steam_write_registers(steam,
+- STEAM_REG_RPAD_MARGIN, 0x01, /* enable margin */
+- 0);
+
+ cancel_delayed_work_sync(&steam->heartbeat);
+ } else {
+@@ -351,7 +348,6 @@ static void steam_set_lizard_mode(struct steam_device *steam, bool enable)
+
+ if (steam->quirks & STEAM_QUIRK_DECK) {
+ steam_write_registers(steam,
+- STEAM_REG_RPAD_MARGIN, 0x00, /* disable margin */
+ STEAM_REG_LPAD_MODE, 0x07, /* disable mouse */
+ STEAM_REG_RPAD_MODE, 0x07, /* disable mouse */
+ STEAM_REG_LPAD_CLICK_PRESSURE, 0xFFFF, /* disable clicky pad */
+@@ -365,7 +361,6 @@ static void steam_set_lizard_mode(struct steam_device *steam, bool enable)
+ schedule_delayed_work(&steam->heartbeat, 5 * HZ);
+ } else {
+ steam_write_registers(steam,
+- STEAM_REG_RPAD_MARGIN, 0x00, /* disable margin */
+ STEAM_REG_LPAD_MODE, 0x07, /* disable mouse */
+ STEAM_REG_RPAD_MODE, 0x07, /* disable mouse */
+ 0);
+--
+2.39.5
+
--- /dev/null
+From ef363458e9ac38c1019db4749f54cb142646cb16 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 19 Dec 2023 19:38:33 -0800
+Subject: HID: hid-steam: Clean up locking
+
+From: Vicki Pfau <vi@endrift.com>
+
+[ Upstream commit 691ead124a0c35e56633dbb73e43711ff3db23ef ]
+
+This cleans up the locking logic so that the spinlock is consistently used for
+access to a small handful of struct variables, and the mutex is exclusively and
+consistently used for ensuring that mutliple threads aren't trying to
+send/receive reports at the same time. Previously, only some report
+transactions were guarded by this mutex, potentially breaking atomicity. The
+mutex has been renamed to reflect this usage.
+
+Signed-off-by: Vicki Pfau <vi@endrift.com>
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Stable-dep-of: 79504249d7e2 ("HID: hid-steam: Move hidraw input (un)registering to work")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hid/hid-steam.c | 122 +++++++++++++++++++++++-----------------
+ 1 file changed, 69 insertions(+), 53 deletions(-)
+
+diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c
+index efd297e0ea8c2..57cb58941c9fc 100644
+--- a/drivers/hid/hid-steam.c
++++ b/drivers/hid/hid-steam.c
+@@ -124,7 +124,7 @@ struct steam_device {
+ struct list_head list;
+ spinlock_t lock;
+ struct hid_device *hdev, *client_hdev;
+- struct mutex mutex;
++ struct mutex report_mutex;
+ bool client_opened;
+ struct input_dev __rcu *input;
+ unsigned long quirks;
+@@ -267,21 +267,26 @@ static int steam_get_serial(struct steam_device *steam)
+ * Send: 0xae 0x15 0x01
+ * Recv: 0xae 0x15 0x01 serialnumber (10 chars)
+ */
+- int ret;
++ int ret = 0;
+ u8 cmd[] = {STEAM_CMD_GET_SERIAL, 0x15, 0x01};
+ u8 reply[3 + STEAM_SERIAL_LEN + 1];
+
++ mutex_lock(&steam->report_mutex);
+ ret = steam_send_report(steam, cmd, sizeof(cmd));
+ if (ret < 0)
+- return ret;
++ goto out;
+ ret = steam_recv_report(steam, reply, sizeof(reply));
+ if (ret < 0)
+- return ret;
+- if (reply[0] != 0xae || reply[1] != 0x15 || reply[2] != 0x01)
+- return -EIO;
++ goto out;
++ if (reply[0] != 0xae || reply[1] != 0x15 || reply[2] != 0x01) {
++ ret = -EIO;
++ goto out;
++ }
+ reply[3 + STEAM_SERIAL_LEN] = 0;
+ strscpy(steam->serial_no, reply + 3, sizeof(steam->serial_no));
+- return 0;
++out:
++ mutex_unlock(&steam->report_mutex);
++ return ret;
+ }
+
+ /*
+@@ -291,13 +296,18 @@ static int steam_get_serial(struct steam_device *steam)
+ */
+ static inline int steam_request_conn_status(struct steam_device *steam)
+ {
+- return steam_send_report_byte(steam, STEAM_CMD_REQUEST_COMM_STATUS);
++ int ret;
++ mutex_lock(&steam->report_mutex);
++ ret = steam_send_report_byte(steam, STEAM_CMD_REQUEST_COMM_STATUS);
++ mutex_unlock(&steam->report_mutex);
++ return ret;
+ }
+
+ static inline int steam_haptic_rumble(struct steam_device *steam,
+ u16 intensity, u16 left_speed, u16 right_speed,
+ u8 left_gain, u8 right_gain)
+ {
++ int ret;
+ u8 report[11] = {STEAM_CMD_HAPTIC_RUMBLE, 9};
+
+ report[3] = intensity & 0xFF;
+@@ -309,7 +319,10 @@ static inline int steam_haptic_rumble(struct steam_device *steam,
+ report[9] = left_gain;
+ report[10] = right_gain;
+
+- return steam_send_report(steam, report, sizeof(report));
++ mutex_lock(&steam->report_mutex);
++ ret = steam_send_report(steam, report, sizeof(report));
++ mutex_unlock(&steam->report_mutex);
++ return ret;
+ }
+
+ static void steam_haptic_rumble_cb(struct work_struct *work)
+@@ -336,11 +349,14 @@ static int steam_play_effect(struct input_dev *dev, void *data,
+ static void steam_set_lizard_mode(struct steam_device *steam, bool enable)
+ {
+ if (enable) {
++ mutex_lock(&steam->report_mutex);
+ /* enable esc, enter, cursors */
+ steam_send_report_byte(steam, STEAM_CMD_DEFAULT_MAPPINGS);
+ /* enable mouse */
+ steam_send_report_byte(steam, STEAM_CMD_DEFAULT_MOUSE);
++ mutex_unlock(&steam->report_mutex);
+ } else {
++ mutex_lock(&steam->report_mutex);
+ /* disable esc, enter, cursor */
+ steam_send_report_byte(steam, STEAM_CMD_CLEAR_MAPPINGS);
+
+@@ -352,11 +368,13 @@ static void steam_set_lizard_mode(struct steam_device *steam, bool enable)
+ STEAM_REG_RPAD_CLICK_PRESSURE, 0xFFFF, /* disable clicky pad */
+ STEAM_REG_WATCHDOG_ENABLE, 0, /* disable watchdog that tests if Steam is active */
+ 0);
++ mutex_unlock(&steam->report_mutex);
+ } else {
+ steam_write_registers(steam,
+ STEAM_REG_LPAD_MODE, 0x07, /* disable mouse */
+ STEAM_REG_RPAD_MODE, 0x07, /* disable mouse */
+ 0);
++ mutex_unlock(&steam->report_mutex);
+ }
+ }
+ }
+@@ -364,22 +382,29 @@ static void steam_set_lizard_mode(struct steam_device *steam, bool enable)
+ static int steam_input_open(struct input_dev *dev)
+ {
+ struct steam_device *steam = input_get_drvdata(dev);
++ unsigned long flags;
++ bool set_lizard_mode;
+
+- mutex_lock(&steam->mutex);
+- if (!steam->client_opened && lizard_mode)
++ spin_lock_irqsave(&steam->lock, flags);
++ set_lizard_mode = !steam->client_opened && lizard_mode;
++ spin_unlock_irqrestore(&steam->lock, flags);
++ if (set_lizard_mode)
+ steam_set_lizard_mode(steam, false);
+- mutex_unlock(&steam->mutex);
++
+ return 0;
+ }
+
+ static void steam_input_close(struct input_dev *dev)
+ {
+ struct steam_device *steam = input_get_drvdata(dev);
++ unsigned long flags;
++ bool set_lizard_mode;
+
+- mutex_lock(&steam->mutex);
+- if (!steam->client_opened && lizard_mode)
++ spin_lock_irqsave(&steam->lock, flags);
++ set_lizard_mode = !steam->client_opened && lizard_mode;
++ spin_unlock_irqrestore(&steam->lock, flags);
++ if (set_lizard_mode)
+ steam_set_lizard_mode(steam, true);
+- mutex_unlock(&steam->mutex);
+ }
+
+ static enum power_supply_property steam_battery_props[] = {
+@@ -624,6 +649,7 @@ static int steam_register(struct steam_device *steam)
+ {
+ int ret;
+ bool client_opened;
++ unsigned long flags;
+
+ /*
+ * This function can be called several times in a row with the
+@@ -636,11 +662,9 @@ static int steam_register(struct steam_device *steam)
+ * Unlikely, but getting the serial could fail, and it is not so
+ * important, so make up a serial number and go on.
+ */
+- mutex_lock(&steam->mutex);
+ if (steam_get_serial(steam) < 0)
+ strscpy(steam->serial_no, "XXXXXXXXXX",
+ sizeof(steam->serial_no));
+- mutex_unlock(&steam->mutex);
+
+ hid_info(steam->hdev, "Steam Controller '%s' connected",
+ steam->serial_no);
+@@ -655,15 +679,13 @@ static int steam_register(struct steam_device *steam)
+ mutex_unlock(&steam_devices_lock);
+ }
+
+- mutex_lock(&steam->mutex);
++ spin_lock_irqsave(&steam->lock, flags);
+ client_opened = steam->client_opened;
+- if (!client_opened)
++ spin_unlock_irqrestore(&steam->lock, flags);
++ if (!client_opened) {
+ steam_set_lizard_mode(steam, lizard_mode);
+- mutex_unlock(&steam->mutex);
+-
+- if (!client_opened)
+ ret = steam_input_register(steam);
+- else
++ } else
+ ret = 0;
+
+ return ret;
+@@ -746,10 +768,11 @@ static void steam_client_ll_stop(struct hid_device *hdev)
+ static int steam_client_ll_open(struct hid_device *hdev)
+ {
+ struct steam_device *steam = hdev->driver_data;
++ unsigned long flags;
+
+- mutex_lock(&steam->mutex);
++ spin_lock_irqsave(&steam->lock, flags);
+ steam->client_opened = true;
+- mutex_unlock(&steam->mutex);
++ spin_unlock_irqrestore(&steam->lock, flags);
+
+ steam_input_unregister(steam);
+
+@@ -764,17 +787,14 @@ static void steam_client_ll_close(struct hid_device *hdev)
+ bool connected;
+
+ spin_lock_irqsave(&steam->lock, flags);
+- connected = steam->connected;
++ steam->client_opened = false;
++ connected = steam->connected && !steam->client_opened;
+ spin_unlock_irqrestore(&steam->lock, flags);
+
+- mutex_lock(&steam->mutex);
+- steam->client_opened = false;
+- if (connected)
++ if (connected) {
+ steam_set_lizard_mode(steam, lizard_mode);
+- mutex_unlock(&steam->mutex);
+-
+- if (connected)
+ steam_input_register(steam);
++ }
+ }
+
+ static int steam_client_ll_raw_request(struct hid_device *hdev,
+@@ -860,19 +880,12 @@ static int steam_probe(struct hid_device *hdev,
+ steam->hdev = hdev;
+ hid_set_drvdata(hdev, steam);
+ spin_lock_init(&steam->lock);
+- mutex_init(&steam->mutex);
++ mutex_init(&steam->report_mutex);
+ steam->quirks = id->driver_data;
+ INIT_WORK(&steam->work_connect, steam_work_connect_cb);
+ INIT_LIST_HEAD(&steam->list);
+ INIT_WORK(&steam->rumble_work, steam_haptic_rumble_cb);
+
+- steam->client_hdev = steam_create_client_hid(hdev);
+- if (IS_ERR(steam->client_hdev)) {
+- ret = PTR_ERR(steam->client_hdev);
+- goto client_hdev_fail;
+- }
+- steam->client_hdev->driver_data = steam;
+-
+ /*
+ * With the real steam controller interface, do not connect hidraw.
+ * Instead, create the client_hid and connect that.
+@@ -881,10 +894,6 @@ static int steam_probe(struct hid_device *hdev,
+ if (ret)
+ goto hid_hw_start_fail;
+
+- ret = hid_add_device(steam->client_hdev);
+- if (ret)
+- goto client_hdev_add_fail;
+-
+ ret = hid_hw_open(hdev);
+ if (ret) {
+ hid_err(hdev,
+@@ -910,15 +919,26 @@ static int steam_probe(struct hid_device *hdev,
+ }
+ }
+
++ steam->client_hdev = steam_create_client_hid(hdev);
++ if (IS_ERR(steam->client_hdev)) {
++ ret = PTR_ERR(steam->client_hdev);
++ goto client_hdev_fail;
++ }
++ steam->client_hdev->driver_data = steam;
++
++ ret = hid_add_device(steam->client_hdev);
++ if (ret)
++ goto client_hdev_add_fail;
++
+ return 0;
+
+-input_register_fail:
+-hid_hw_open_fail:
+ client_hdev_add_fail:
+ hid_hw_stop(hdev);
+-hid_hw_start_fail:
+- hid_destroy_device(steam->client_hdev);
+ client_hdev_fail:
++ hid_destroy_device(steam->client_hdev);
++input_register_fail:
++hid_hw_open_fail:
++hid_hw_start_fail:
+ cancel_work_sync(&steam->work_connect);
+ cancel_work_sync(&steam->rumble_work);
+ steam_alloc_fail:
+@@ -936,12 +956,10 @@ static void steam_remove(struct hid_device *hdev)
+ return;
+ }
+
++ cancel_work_sync(&steam->work_connect);
+ hid_destroy_device(steam->client_hdev);
+- mutex_lock(&steam->mutex);
+ steam->client_hdev = NULL;
+ steam->client_opened = false;
+- mutex_unlock(&steam->mutex);
+- cancel_work_sync(&steam->work_connect);
+ if (steam->quirks & STEAM_QUIRK_WIRELESS) {
+ hid_info(hdev, "Steam wireless receiver disconnected");
+ }
+@@ -1408,10 +1426,8 @@ static int steam_param_set_lizard_mode(const char *val,
+
+ mutex_lock(&steam_devices_lock);
+ list_for_each_entry(steam, &steam_devices, list) {
+- mutex_lock(&steam->mutex);
+ if (!steam->client_opened)
+ steam_set_lizard_mode(steam, lizard_mode);
+- mutex_unlock(&steam->mutex);
+ }
+ mutex_unlock(&steam_devices_lock);
+ return 0;
+--
+2.39.5
+
--- /dev/null
+From 6d2bc1a78f46ebade44c91cbe6652762bb876f68 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 19 Dec 2023 19:38:32 -0800
+Subject: HID: hid-steam: Disable watchdog instead of using a heartbeat
+
+From: Vicki Pfau <vi@endrift.com>
+
+[ Upstream commit 917972636e8271c5691710ce5dcd66c2d3bd04f2 ]
+
+The Steam Deck has a setting that controls whether or not the watchdog is
+enabled, so instead of using a heartbeat to keep the watchdog from triggering,
+this commit changes the behavior to simply disable the watchdog instead.
+
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Stable-dep-of: 79504249d7e2 ("HID: hid-steam: Move hidraw input (un)registering to work")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hid/hid-steam.c | 30 ++----------------------------
+ 1 file changed, 2 insertions(+), 28 deletions(-)
+
+diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c
+index 7aefd52e945a1..efd297e0ea8c2 100644
+--- a/drivers/hid/hid-steam.c
++++ b/drivers/hid/hid-steam.c
+@@ -101,6 +101,7 @@ static LIST_HEAD(steam_devices);
+ #define STEAM_REG_GYRO_MODE 0x30
+ #define STEAM_REG_LPAD_CLICK_PRESSURE 0x34
+ #define STEAM_REG_RPAD_CLICK_PRESSURE 0x35
++#define STEAM_REG_WATCHDOG_ENABLE 0x47
+
+ /* Raw event identifiers */
+ #define STEAM_EV_INPUT_DATA 0x01
+@@ -134,7 +135,6 @@ struct steam_device {
+ struct power_supply __rcu *battery;
+ u8 battery_charge;
+ u16 voltage;
+- struct delayed_work heartbeat;
+ struct work_struct rumble_work;
+ u16 rumble_left;
+ u16 rumble_right;
+@@ -340,8 +340,6 @@ static void steam_set_lizard_mode(struct steam_device *steam, bool enable)
+ steam_send_report_byte(steam, STEAM_CMD_DEFAULT_MAPPINGS);
+ /* enable mouse */
+ steam_send_report_byte(steam, STEAM_CMD_DEFAULT_MOUSE);
+-
+- cancel_delayed_work_sync(&steam->heartbeat);
+ } else {
+ /* disable esc, enter, cursor */
+ steam_send_report_byte(steam, STEAM_CMD_CLEAR_MAPPINGS);
+@@ -352,13 +350,8 @@ static void steam_set_lizard_mode(struct steam_device *steam, bool enable)
+ STEAM_REG_RPAD_MODE, 0x07, /* disable mouse */
+ STEAM_REG_LPAD_CLICK_PRESSURE, 0xFFFF, /* disable clicky pad */
+ STEAM_REG_RPAD_CLICK_PRESSURE, 0xFFFF, /* disable clicky pad */
++ STEAM_REG_WATCHDOG_ENABLE, 0, /* disable watchdog that tests if Steam is active */
+ 0);
+- /*
+- * The Steam Deck has a watchdog that automatically enables
+- * lizard mode if it doesn't see any traffic for too long
+- */
+- if (!work_busy(&steam->heartbeat.work))
+- schedule_delayed_work(&steam->heartbeat, 5 * HZ);
+ } else {
+ steam_write_registers(steam,
+ STEAM_REG_LPAD_MODE, 0x07, /* disable mouse */
+@@ -733,22 +726,6 @@ static bool steam_is_valve_interface(struct hid_device *hdev)
+ return !list_empty(&rep_enum->report_list);
+ }
+
+-static void steam_lizard_mode_heartbeat(struct work_struct *work)
+-{
+- struct steam_device *steam = container_of(work, struct steam_device,
+- heartbeat.work);
+-
+- mutex_lock(&steam->mutex);
+- if (!steam->client_opened && steam->client_hdev) {
+- steam_send_report_byte(steam, STEAM_CMD_CLEAR_MAPPINGS);
+- steam_write_registers(steam,
+- STEAM_REG_RPAD_MODE, 0x07, /* disable mouse */
+- 0);
+- schedule_delayed_work(&steam->heartbeat, 5 * HZ);
+- }
+- mutex_unlock(&steam->mutex);
+-}
+-
+ static int steam_client_ll_parse(struct hid_device *hdev)
+ {
+ struct steam_device *steam = hdev->driver_data;
+@@ -887,7 +864,6 @@ static int steam_probe(struct hid_device *hdev,
+ steam->quirks = id->driver_data;
+ INIT_WORK(&steam->work_connect, steam_work_connect_cb);
+ INIT_LIST_HEAD(&steam->list);
+- INIT_DEFERRABLE_WORK(&steam->heartbeat, steam_lizard_mode_heartbeat);
+ INIT_WORK(&steam->rumble_work, steam_haptic_rumble_cb);
+
+ steam->client_hdev = steam_create_client_hid(hdev);
+@@ -944,7 +920,6 @@ static int steam_probe(struct hid_device *hdev,
+ hid_destroy_device(steam->client_hdev);
+ client_hdev_fail:
+ cancel_work_sync(&steam->work_connect);
+- cancel_delayed_work_sync(&steam->heartbeat);
+ cancel_work_sync(&steam->rumble_work);
+ steam_alloc_fail:
+ hid_err(hdev, "%s: failed with error %d\n",
+@@ -965,7 +940,6 @@ static void steam_remove(struct hid_device *hdev)
+ mutex_lock(&steam->mutex);
+ steam->client_hdev = NULL;
+ steam->client_opened = false;
+- cancel_delayed_work_sync(&steam->heartbeat);
+ mutex_unlock(&steam->mutex);
+ cancel_work_sync(&steam->work_connect);
+ if (steam->quirks & STEAM_QUIRK_WIRELESS) {
+--
+2.39.5
+
--- /dev/null
+From e0d238c36b7994c94fcf698c471e21babf731599 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 12 Jan 2024 17:35:06 +0300
+Subject: HID: hid-steam: Fix cleanup in probe()
+
+From: Dan Carpenter <dan.carpenter@linaro.org>
+
+[ Upstream commit a9f1da09c69f13ef471db8b22107a28042d230ca ]
+
+There are a number of issues in this code. First of all if
+steam_create_client_hid() fails then it leads to an error pointer
+dereference when we call hid_destroy_device(steam->client_hdev).
+
+Also there are a number of leaks. hid_hw_stop() is not called if
+hid_hw_open() fails for example. And it doesn't call steam_unregister()
+or hid_hw_close().
+
+Fixes: 691ead124a0c ("HID: hid-steam: Clean up locking")
+Signed-off-by: Dan Carpenter <dan.carpenter@linaro.org>
+Reviewed-by: Vicki Pfau <vi@endrift.com>
+Link: https://lore.kernel.org/r/1fd87904-dabf-4879-bb89-72d13ebfc91e@moroto.mountain
+Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>
+Stable-dep-of: 79504249d7e2 ("HID: hid-steam: Move hidraw input (un)registering to work")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hid/hid-steam.c | 26 +++++++++++++++-----------
+ 1 file changed, 15 insertions(+), 11 deletions(-)
+
+diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c
+index 2f11f77ae2153..59b46163bc526 100644
+--- a/drivers/hid/hid-steam.c
++++ b/drivers/hid/hid-steam.c
+@@ -1127,14 +1127,14 @@ static int steam_probe(struct hid_device *hdev,
+ */
+ ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_HIDRAW);
+ if (ret)
+- goto hid_hw_start_fail;
++ goto err_cancel_work;
+
+ ret = hid_hw_open(hdev);
+ if (ret) {
+ hid_err(hdev,
+ "%s:hid_hw_open\n",
+ __func__);
+- goto hid_hw_open_fail;
++ goto err_hw_stop;
+ }
+
+ if (steam->quirks & STEAM_QUIRK_WIRELESS) {
+@@ -1150,33 +1150,37 @@ static int steam_probe(struct hid_device *hdev,
+ hid_err(hdev,
+ "%s:steam_register failed with error %d\n",
+ __func__, ret);
+- goto input_register_fail;
++ goto err_hw_close;
+ }
+ }
+
+ steam->client_hdev = steam_create_client_hid(hdev);
+ if (IS_ERR(steam->client_hdev)) {
+ ret = PTR_ERR(steam->client_hdev);
+- goto client_hdev_fail;
++ goto err_stream_unregister;
+ }
+ steam->client_hdev->driver_data = steam;
+
+ ret = hid_add_device(steam->client_hdev);
+ if (ret)
+- goto client_hdev_add_fail;
++ goto err_destroy;
+
+ return 0;
+
+-client_hdev_add_fail:
+- hid_hw_stop(hdev);
+-client_hdev_fail:
++err_destroy:
+ hid_destroy_device(steam->client_hdev);
+-input_register_fail:
+-hid_hw_open_fail:
+-hid_hw_start_fail:
++err_stream_unregister:
++ if (steam->connected)
++ steam_unregister(steam);
++err_hw_close:
++ hid_hw_close(hdev);
++err_hw_stop:
++ hid_hw_stop(hdev);
++err_cancel_work:
+ cancel_work_sync(&steam->work_connect);
+ cancel_delayed_work_sync(&steam->mode_switch);
+ cancel_work_sync(&steam->rumble_work);
++
+ return ret;
+ }
+
+--
+2.39.5
+
--- /dev/null
+From 47e14b4af54f700a9b0a0bf11817d43f1857db0e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 25 Dec 2024 18:34:24 -0800
+Subject: HID: hid-steam: Make sure rumble work is canceled on removal
+
+From: Vicki Pfau <vi@endrift.com>
+
+[ Upstream commit cc4f952427aaa44ecfd92542e10a65cce67bd6f4 ]
+
+When a force feedback command is sent from userspace, work is scheduled to pass
+this data to the controller without blocking userspace itself. However, in
+theory, this work might not be properly canceled if the controller is removed
+at the exact right time. This patch ensures the work is properly canceled when
+the device is removed.
+
+Signed-off-by: Vicki Pfau <vi@endrift.com>
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Stable-dep-of: 79504249d7e2 ("HID: hid-steam: Move hidraw input (un)registering to work")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hid/hid-steam.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c
+index 29a0e1f395339..a25d0034dc1ea 100644
+--- a/drivers/hid/hid-steam.c
++++ b/drivers/hid/hid-steam.c
+@@ -1304,6 +1304,7 @@ static void steam_remove(struct hid_device *hdev)
+
+ cancel_delayed_work_sync(&steam->mode_switch);
+ cancel_work_sync(&steam->work_connect);
++ cancel_work_sync(&steam->rumble_work);
+ hid_destroy_device(steam->client_hdev);
+ steam->client_hdev = NULL;
+ steam->client_opened = false;
+--
+2.39.5
+
--- /dev/null
+From 9e2f8177b284e249ae0a53a5658c6e41bedb7d9e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 4 Feb 2025 19:55:27 -0800
+Subject: HID: hid-steam: Move hidraw input (un)registering to work
+
+From: Vicki Pfau <vi@endrift.com>
+
+[ Upstream commit 79504249d7e27cad4a3eeb9afc6386e418728ce0 ]
+
+Due to an interplay between locking in the input and hid transport subsystems,
+attempting to register or deregister the relevant input devices during the
+hidraw open/close events can lead to a lock ordering issue. Though this
+shouldn't cause a deadlock, this commit moves the input device manipulation to
+deferred work to sidestep the issue.
+
+Fixes: 385a4886778f6 ("HID: steam: remove input device when a hid client is running.")
+Signed-off-by: Vicki Pfau <vi@endrift.com>
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hid/hid-steam.c | 38 +++++++++++++++++++++++++++++++-------
+ 1 file changed, 31 insertions(+), 7 deletions(-)
+
+diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c
+index a25d0034dc1ea..ee3c27e133443 100644
+--- a/drivers/hid/hid-steam.c
++++ b/drivers/hid/hid-steam.c
+@@ -312,6 +312,7 @@ struct steam_device {
+ u16 rumble_left;
+ u16 rumble_right;
+ unsigned int sensor_timestamp_us;
++ struct work_struct unregister_work;
+ };
+
+ static int steam_recv_report(struct steam_device *steam,
+@@ -1070,6 +1071,31 @@ static void steam_mode_switch_cb(struct work_struct *work)
+ }
+ }
+
++static void steam_work_unregister_cb(struct work_struct *work)
++{
++ struct steam_device *steam = container_of(work, struct steam_device,
++ unregister_work);
++ unsigned long flags;
++ bool connected;
++ bool opened;
++
++ spin_lock_irqsave(&steam->lock, flags);
++ opened = steam->client_opened;
++ connected = steam->connected;
++ spin_unlock_irqrestore(&steam->lock, flags);
++
++ if (connected) {
++ if (opened) {
++ steam_sensors_unregister(steam);
++ steam_input_unregister(steam);
++ } else {
++ steam_set_lizard_mode(steam, lizard_mode);
++ steam_input_register(steam);
++ steam_sensors_register(steam);
++ }
++ }
++}
++
+ static bool steam_is_valve_interface(struct hid_device *hdev)
+ {
+ struct hid_report_enum *rep_enum;
+@@ -1115,8 +1141,7 @@ static int steam_client_ll_open(struct hid_device *hdev)
+ steam->client_opened = true;
+ spin_unlock_irqrestore(&steam->lock, flags);
+
+- steam_sensors_unregister(steam);
+- steam_input_unregister(steam);
++ schedule_work(&steam->unregister_work);
+
+ return 0;
+ }
+@@ -1133,11 +1158,7 @@ static void steam_client_ll_close(struct hid_device *hdev)
+ connected = steam->connected && !steam->client_opened;
+ spin_unlock_irqrestore(&steam->lock, flags);
+
+- if (connected) {
+- steam_set_lizard_mode(steam, lizard_mode);
+- steam_input_register(steam);
+- steam_sensors_register(steam);
+- }
++ schedule_work(&steam->unregister_work);
+ }
+
+ static int steam_client_ll_raw_request(struct hid_device *hdev,
+@@ -1229,6 +1250,7 @@ static int steam_probe(struct hid_device *hdev,
+ INIT_LIST_HEAD(&steam->list);
+ INIT_WORK(&steam->rumble_work, steam_haptic_rumble_cb);
+ steam->sensor_timestamp_us = 0;
++ INIT_WORK(&steam->unregister_work, steam_work_unregister_cb);
+
+ /*
+ * With the real steam controller interface, do not connect hidraw.
+@@ -1289,6 +1311,7 @@ static int steam_probe(struct hid_device *hdev,
+ cancel_work_sync(&steam->work_connect);
+ cancel_delayed_work_sync(&steam->mode_switch);
+ cancel_work_sync(&steam->rumble_work);
++ cancel_work_sync(&steam->unregister_work);
+
+ return ret;
+ }
+@@ -1305,6 +1328,7 @@ static void steam_remove(struct hid_device *hdev)
+ cancel_delayed_work_sync(&steam->mode_switch);
+ cancel_work_sync(&steam->work_connect);
+ cancel_work_sync(&steam->rumble_work);
++ cancel_work_sync(&steam->unregister_work);
+ hid_destroy_device(steam->client_hdev);
+ steam->client_hdev = NULL;
+ steam->client_opened = false;
+--
+2.39.5
+
--- /dev/null
+From b89d1a0d7931a1018aee93f3306efe59242293ec Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 12 Jan 2024 17:34:14 +0300
+Subject: HID: hid-steam: remove pointless error message
+
+From: Dan Carpenter <dan.carpenter@linaro.org>
+
+[ Upstream commit a9668169961106f3598384fe95004106ec191201 ]
+
+This error message doesn't really add any information. If modprobe
+fails then the user will already know what the error code is. In the
+case of kmalloc() it's a style violation to print an error message for
+that because kmalloc has it's own better error messages built in.
+
+Signed-off-by: Dan Carpenter <dan.carpenter@linaro.org>
+Reviewed-by: Vicki Pfau <vi@endrift.com>
+Link: https://lore.kernel.org/r/305898fb-6bd4-4749-806c-05ec51bbeb80@moroto.mountain
+Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>
+Stable-dep-of: 79504249d7e2 ("HID: hid-steam: Move hidraw input (un)registering to work")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hid/hid-steam.c | 10 +++-------
+ 1 file changed, 3 insertions(+), 7 deletions(-)
+
+diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c
+index 2f87026f01de1..2f11f77ae2153 100644
+--- a/drivers/hid/hid-steam.c
++++ b/drivers/hid/hid-steam.c
+@@ -1108,10 +1108,9 @@ static int steam_probe(struct hid_device *hdev,
+ return hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+
+ steam = devm_kzalloc(&hdev->dev, sizeof(*steam), GFP_KERNEL);
+- if (!steam) {
+- ret = -ENOMEM;
+- goto steam_alloc_fail;
+- }
++ if (!steam)
++ return -ENOMEM;
++
+ steam->hdev = hdev;
+ hid_set_drvdata(hdev, steam);
+ spin_lock_init(&steam->lock);
+@@ -1178,9 +1177,6 @@ static int steam_probe(struct hid_device *hdev,
+ cancel_work_sync(&steam->work_connect);
+ cancel_delayed_work_sync(&steam->mode_switch);
+ cancel_work_sync(&steam->rumble_work);
+-steam_alloc_fail:
+- hid_err(hdev, "%s: failed with error %d\n",
+- __func__, ret);
+ return ret;
+ }
+
+--
+2.39.5
+
--- /dev/null
+From fe446bc089a6c7147b445f819cba6427d4f8a2fb Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 19 Dec 2023 19:38:35 -0800
+Subject: HID: hid-steam: Update list of identifiers from SDL
+
+From: Vicki Pfau <vi@endrift.com>
+
+[ Upstream commit 4f9a5a9769cc77075e606537e15747e8b8e9c7c9 ]
+
+SDL includes a list of settings (formerly called registers in this driver),
+reports (formerly cmds), and various other identifiers that were provided by
+Valve. This commit imports a significant chunk of that list as well as
+replacing most of the guessed names and a handful of magic constants. It also
+replaces bitmask definitions that used hex with the BIT macro.
+
+Signed-off-by: Vicki Pfau <vi@endrift.com>
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Stable-dep-of: 79504249d7e2 ("HID: hid-steam: Move hidraw input (un)registering to work")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hid/hid-steam.c | 286 +++++++++++++++++++++++++++++++---------
+ 1 file changed, 221 insertions(+), 65 deletions(-)
+
+diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c
+index 57cb58941c9fc..991db5acf5ddb 100644
+--- a/drivers/hid/hid-steam.c
++++ b/drivers/hid/hid-steam.c
+@@ -71,51 +71,207 @@ static LIST_HEAD(steam_devices);
+
+ /*
+ * Commands that can be sent in a feature report.
+- * Thanks to Valve for some valuable hints.
++ * Thanks to Valve and SDL for the names.
+ */
+-#define STEAM_CMD_SET_MAPPINGS 0x80
+-#define STEAM_CMD_CLEAR_MAPPINGS 0x81
+-#define STEAM_CMD_GET_MAPPINGS 0x82
+-#define STEAM_CMD_GET_ATTRIB 0x83
+-#define STEAM_CMD_GET_ATTRIB_LABEL 0x84
+-#define STEAM_CMD_DEFAULT_MAPPINGS 0x85
+-#define STEAM_CMD_FACTORY_RESET 0x86
+-#define STEAM_CMD_WRITE_REGISTER 0x87
+-#define STEAM_CMD_CLEAR_REGISTER 0x88
+-#define STEAM_CMD_READ_REGISTER 0x89
+-#define STEAM_CMD_GET_REGISTER_LABEL 0x8a
+-#define STEAM_CMD_GET_REGISTER_MAX 0x8b
+-#define STEAM_CMD_GET_REGISTER_DEFAULT 0x8c
+-#define STEAM_CMD_SET_MODE 0x8d
+-#define STEAM_CMD_DEFAULT_MOUSE 0x8e
+-#define STEAM_CMD_FORCEFEEDBAK 0x8f
+-#define STEAM_CMD_REQUEST_COMM_STATUS 0xb4
+-#define STEAM_CMD_GET_SERIAL 0xae
+-#define STEAM_CMD_HAPTIC_RUMBLE 0xeb
+-
+-/* Some useful register ids */
+-#define STEAM_REG_LPAD_MODE 0x07
+-#define STEAM_REG_RPAD_MODE 0x08
+-#define STEAM_REG_RPAD_MARGIN 0x18
+-#define STEAM_REG_LED 0x2d
+-#define STEAM_REG_GYRO_MODE 0x30
+-#define STEAM_REG_LPAD_CLICK_PRESSURE 0x34
+-#define STEAM_REG_RPAD_CLICK_PRESSURE 0x35
+-#define STEAM_REG_WATCHDOG_ENABLE 0x47
+-
+-/* Raw event identifiers */
+-#define STEAM_EV_INPUT_DATA 0x01
+-#define STEAM_EV_CONNECT 0x03
+-#define STEAM_EV_BATTERY 0x04
+-#define STEAM_EV_DECK_INPUT_DATA 0x09
++enum {
++ ID_SET_DIGITAL_MAPPINGS = 0x80,
++ ID_CLEAR_DIGITAL_MAPPINGS = 0x81,
++ ID_GET_DIGITAL_MAPPINGS = 0x82,
++ ID_GET_ATTRIBUTES_VALUES = 0x83,
++ ID_GET_ATTRIBUTE_LABEL = 0x84,
++ ID_SET_DEFAULT_DIGITAL_MAPPINGS = 0x85,
++ ID_FACTORY_RESET = 0x86,
++ ID_SET_SETTINGS_VALUES = 0x87,
++ ID_CLEAR_SETTINGS_VALUES = 0x88,
++ ID_GET_SETTINGS_VALUES = 0x89,
++ ID_GET_SETTING_LABEL = 0x8A,
++ ID_GET_SETTINGS_MAXS = 0x8B,
++ ID_GET_SETTINGS_DEFAULTS = 0x8C,
++ ID_SET_CONTROLLER_MODE = 0x8D,
++ ID_LOAD_DEFAULT_SETTINGS = 0x8E,
++ ID_TRIGGER_HAPTIC_PULSE = 0x8F,
++ ID_TURN_OFF_CONTROLLER = 0x9F,
++
++ ID_GET_DEVICE_INFO = 0xA1,
++
++ ID_CALIBRATE_TRACKPADS = 0xA7,
++ ID_RESERVED_0 = 0xA8,
++ ID_SET_SERIAL_NUMBER = 0xA9,
++ ID_GET_TRACKPAD_CALIBRATION = 0xAA,
++ ID_GET_TRACKPAD_FACTORY_CALIBRATION = 0xAB,
++ ID_GET_TRACKPAD_RAW_DATA = 0xAC,
++ ID_ENABLE_PAIRING = 0xAD,
++ ID_GET_STRING_ATTRIBUTE = 0xAE,
++ ID_RADIO_ERASE_RECORDS = 0xAF,
++ ID_RADIO_WRITE_RECORD = 0xB0,
++ ID_SET_DONGLE_SETTING = 0xB1,
++ ID_DONGLE_DISCONNECT_DEVICE = 0xB2,
++ ID_DONGLE_COMMIT_DEVICE = 0xB3,
++ ID_DONGLE_GET_WIRELESS_STATE = 0xB4,
++ ID_CALIBRATE_GYRO = 0xB5,
++ ID_PLAY_AUDIO = 0xB6,
++ ID_AUDIO_UPDATE_START = 0xB7,
++ ID_AUDIO_UPDATE_DATA = 0xB8,
++ ID_AUDIO_UPDATE_COMPLETE = 0xB9,
++ ID_GET_CHIPID = 0xBA,
++
++ ID_CALIBRATE_JOYSTICK = 0xBF,
++ ID_CALIBRATE_ANALOG_TRIGGERS = 0xC0,
++ ID_SET_AUDIO_MAPPING = 0xC1,
++ ID_CHECK_GYRO_FW_LOAD = 0xC2,
++ ID_CALIBRATE_ANALOG = 0xC3,
++ ID_DONGLE_GET_CONNECTED_SLOTS = 0xC4,
++
++ ID_RESET_IMU = 0xCE,
++
++ ID_TRIGGER_HAPTIC_CMD = 0xEA,
++ ID_TRIGGER_RUMBLE_CMD = 0xEB,
++};
++
++/* Settings IDs */
++enum {
++ /* 0 */
++ SETTING_MOUSE_SENSITIVITY,
++ SETTING_MOUSE_ACCELERATION,
++ SETTING_TRACKBALL_ROTATION_ANGLE,
++ SETTING_HAPTIC_INTENSITY_UNUSED,
++ SETTING_LEFT_GAMEPAD_STICK_ENABLED,
++ SETTING_RIGHT_GAMEPAD_STICK_ENABLED,
++ SETTING_USB_DEBUG_MODE,
++ SETTING_LEFT_TRACKPAD_MODE,
++ SETTING_RIGHT_TRACKPAD_MODE,
++ SETTING_MOUSE_POINTER_ENABLED,
++
++ /* 10 */
++ SETTING_DPAD_DEADZONE,
++ SETTING_MINIMUM_MOMENTUM_VEL,
++ SETTING_MOMENTUM_DECAY_AMMOUNT,
++ SETTING_TRACKPAD_RELATIVE_MODE_TICKS_PER_PIXEL,
++ SETTING_HAPTIC_INCREMENT,
++ SETTING_DPAD_ANGLE_SIN,
++ SETTING_DPAD_ANGLE_COS,
++ SETTING_MOMENTUM_VERTICAL_DIVISOR,
++ SETTING_MOMENTUM_MAXIMUM_VELOCITY,
++ SETTING_TRACKPAD_Z_ON,
++
++ /* 20 */
++ SETTING_TRACKPAD_Z_OFF,
++ SETTING_SENSITIVY_SCALE_AMMOUNT,
++ SETTING_LEFT_TRACKPAD_SECONDARY_MODE,
++ SETTING_RIGHT_TRACKPAD_SECONDARY_MODE,
++ SETTING_SMOOTH_ABSOLUTE_MOUSE,
++ SETTING_STEAMBUTTON_POWEROFF_TIME,
++ SETTING_UNUSED_1,
++ SETTING_TRACKPAD_OUTER_RADIUS,
++ SETTING_TRACKPAD_Z_ON_LEFT,
++ SETTING_TRACKPAD_Z_OFF_LEFT,
++
++ /* 30 */
++ SETTING_TRACKPAD_OUTER_SPIN_VEL,
++ SETTING_TRACKPAD_OUTER_SPIN_RADIUS,
++ SETTING_TRACKPAD_OUTER_SPIN_HORIZONTAL_ONLY,
++ SETTING_TRACKPAD_RELATIVE_MODE_DEADZONE,
++ SETTING_TRACKPAD_RELATIVE_MODE_MAX_VEL,
++ SETTING_TRACKPAD_RELATIVE_MODE_INVERT_Y,
++ SETTING_TRACKPAD_DOUBLE_TAP_BEEP_ENABLED,
++ SETTING_TRACKPAD_DOUBLE_TAP_BEEP_PERIOD,
++ SETTING_TRACKPAD_DOUBLE_TAP_BEEP_COUNT,
++ SETTING_TRACKPAD_OUTER_RADIUS_RELEASE_ON_TRANSITION,
++
++ /* 40 */
++ SETTING_RADIAL_MODE_ANGLE,
++ SETTING_HAPTIC_INTENSITY_MOUSE_MODE,
++ SETTING_LEFT_DPAD_REQUIRES_CLICK,
++ SETTING_RIGHT_DPAD_REQUIRES_CLICK,
++ SETTING_LED_BASELINE_BRIGHTNESS,
++ SETTING_LED_USER_BRIGHTNESS,
++ SETTING_ENABLE_RAW_JOYSTICK,
++ SETTING_ENABLE_FAST_SCAN,
++ SETTING_IMU_MODE,
++ SETTING_WIRELESS_PACKET_VERSION,
++
++ /* 50 */
++ SETTING_SLEEP_INACTIVITY_TIMEOUT,
++ SETTING_TRACKPAD_NOISE_THRESHOLD,
++ SETTING_LEFT_TRACKPAD_CLICK_PRESSURE,
++ SETTING_RIGHT_TRACKPAD_CLICK_PRESSURE,
++ SETTING_LEFT_BUMPER_CLICK_PRESSURE,
++ SETTING_RIGHT_BUMPER_CLICK_PRESSURE,
++ SETTING_LEFT_GRIP_CLICK_PRESSURE,
++ SETTING_RIGHT_GRIP_CLICK_PRESSURE,
++ SETTING_LEFT_GRIP2_CLICK_PRESSURE,
++ SETTING_RIGHT_GRIP2_CLICK_PRESSURE,
++
++ /* 60 */
++ SETTING_PRESSURE_MODE,
++ SETTING_CONTROLLER_TEST_MODE,
++ SETTING_TRIGGER_MODE,
++ SETTING_TRACKPAD_Z_THRESHOLD,
++ SETTING_FRAME_RATE,
++ SETTING_TRACKPAD_FILT_CTRL,
++ SETTING_TRACKPAD_CLIP,
++ SETTING_DEBUG_OUTPUT_SELECT,
++ SETTING_TRIGGER_THRESHOLD_PERCENT,
++ SETTING_TRACKPAD_FREQUENCY_HOPPING,
++
++ /* 70 */
++ SETTING_HAPTICS_ENABLED,
++ SETTING_STEAM_WATCHDOG_ENABLE,
++ SETTING_TIMP_TOUCH_THRESHOLD_ON,
++ SETTING_TIMP_TOUCH_THRESHOLD_OFF,
++ SETTING_FREQ_HOPPING,
++ SETTING_TEST_CONTROL,
++ SETTING_HAPTIC_MASTER_GAIN_DB,
++ SETTING_THUMB_TOUCH_THRESH,
++ SETTING_DEVICE_POWER_STATUS,
++ SETTING_HAPTIC_INTENSITY,
++
++ /* 80 */
++ SETTING_STABILIZER_ENABLED,
++ SETTING_TIMP_MODE_MTE,
++};
++
++/* Input report identifiers */
++enum
++{
++ ID_CONTROLLER_STATE = 1,
++ ID_CONTROLLER_DEBUG = 2,
++ ID_CONTROLLER_WIRELESS = 3,
++ ID_CONTROLLER_STATUS = 4,
++ ID_CONTROLLER_DEBUG2 = 5,
++ ID_CONTROLLER_SECONDARY_STATE = 6,
++ ID_CONTROLLER_BLE_STATE = 7,
++ ID_CONTROLLER_DECK_STATE = 9
++};
++
++/* String attribute idenitifiers */
++enum {
++ ATTRIB_STR_BOARD_SERIAL,
++ ATTRIB_STR_UNIT_SERIAL,
++};
+
+ /* Values for GYRO_MODE (bitmask) */
+-#define STEAM_GYRO_MODE_OFF 0x0000
+-#define STEAM_GYRO_MODE_STEERING 0x0001
+-#define STEAM_GYRO_MODE_TILT 0x0002
+-#define STEAM_GYRO_MODE_SEND_ORIENTATION 0x0004
+-#define STEAM_GYRO_MODE_SEND_RAW_ACCEL 0x0008
+-#define STEAM_GYRO_MODE_SEND_RAW_GYRO 0x0010
++enum {
++ SETTING_GYRO_MODE_OFF = 0,
++ SETTING_GYRO_MODE_STEERING = BIT(0),
++ SETTING_GYRO_MODE_TILT = BIT(1),
++ SETTING_GYRO_MODE_SEND_ORIENTATION = BIT(2),
++ SETTING_GYRO_MODE_SEND_RAW_ACCEL = BIT(3),
++ SETTING_GYRO_MODE_SEND_RAW_GYRO = BIT(4),
++};
++
++/* Trackpad modes */
++enum {
++ TRACKPAD_ABSOLUTE_MOUSE,
++ TRACKPAD_RELATIVE_MOUSE,
++ TRACKPAD_DPAD_FOUR_WAY_DISCRETE,
++ TRACKPAD_DPAD_FOUR_WAY_OVERLAP,
++ TRACKPAD_DPAD_EIGHT_WAY,
++ TRACKPAD_RADIAL_MODE,
++ TRACKPAD_ABSOLUTE_DPAD,
++ TRACKPAD_NONE,
++ TRACKPAD_GESTURE_KEYBOARD,
++};
+
+ /* Other random constants */
+ #define STEAM_SERIAL_LEN 10
+@@ -226,13 +382,13 @@ static inline int steam_send_report_byte(struct steam_device *steam, u8 cmd)
+ return steam_send_report(steam, &cmd, 1);
+ }
+
+-static int steam_write_registers(struct steam_device *steam,
++static int steam_write_settings(struct steam_device *steam,
+ /* u8 reg, u16 val */...)
+ {
+ /* Send: 0x87 len (reg valLo valHi)* */
+ u8 reg;
+ u16 val;
+- u8 cmd[64] = {STEAM_CMD_WRITE_REGISTER, 0x00};
++ u8 cmd[64] = {ID_SET_SETTINGS_VALUES, 0x00};
+ int ret;
+ va_list args;
+
+@@ -268,7 +424,7 @@ static int steam_get_serial(struct steam_device *steam)
+ * Recv: 0xae 0x15 0x01 serialnumber (10 chars)
+ */
+ int ret = 0;
+- u8 cmd[] = {STEAM_CMD_GET_SERIAL, 0x15, 0x01};
++ u8 cmd[] = {ID_GET_STRING_ATTRIBUTE, 0x15, ATTRIB_STR_UNIT_SERIAL};
+ u8 reply[3 + STEAM_SERIAL_LEN + 1];
+
+ mutex_lock(&steam->report_mutex);
+@@ -278,7 +434,7 @@ static int steam_get_serial(struct steam_device *steam)
+ ret = steam_recv_report(steam, reply, sizeof(reply));
+ if (ret < 0)
+ goto out;
+- if (reply[0] != 0xae || reply[1] != 0x15 || reply[2] != 0x01) {
++ if (reply[0] != ID_GET_STRING_ATTRIBUTE || reply[1] != 0x15 || reply[2] != ATTRIB_STR_UNIT_SERIAL) {
+ ret = -EIO;
+ goto out;
+ }
+@@ -298,7 +454,7 @@ static inline int steam_request_conn_status(struct steam_device *steam)
+ {
+ int ret;
+ mutex_lock(&steam->report_mutex);
+- ret = steam_send_report_byte(steam, STEAM_CMD_REQUEST_COMM_STATUS);
++ ret = steam_send_report_byte(steam, ID_DONGLE_GET_WIRELESS_STATE);
+ mutex_unlock(&steam->report_mutex);
+ return ret;
+ }
+@@ -308,7 +464,7 @@ static inline int steam_haptic_rumble(struct steam_device *steam,
+ u8 left_gain, u8 right_gain)
+ {
+ int ret;
+- u8 report[11] = {STEAM_CMD_HAPTIC_RUMBLE, 9};
++ u8 report[11] = {ID_TRIGGER_RUMBLE_CMD, 9};
+
+ report[3] = intensity & 0xFF;
+ report[4] = intensity >> 8;
+@@ -351,28 +507,28 @@ static void steam_set_lizard_mode(struct steam_device *steam, bool enable)
+ if (enable) {
+ mutex_lock(&steam->report_mutex);
+ /* enable esc, enter, cursors */
+- steam_send_report_byte(steam, STEAM_CMD_DEFAULT_MAPPINGS);
+- /* enable mouse */
+- steam_send_report_byte(steam, STEAM_CMD_DEFAULT_MOUSE);
++ steam_send_report_byte(steam, ID_SET_DEFAULT_DIGITAL_MAPPINGS);
++ /* reset settings */
++ steam_send_report_byte(steam, ID_LOAD_DEFAULT_SETTINGS);
+ mutex_unlock(&steam->report_mutex);
+ } else {
+ mutex_lock(&steam->report_mutex);
+ /* disable esc, enter, cursor */
+- steam_send_report_byte(steam, STEAM_CMD_CLEAR_MAPPINGS);
++ steam_send_report_byte(steam, ID_CLEAR_DIGITAL_MAPPINGS);
+
+ if (steam->quirks & STEAM_QUIRK_DECK) {
+- steam_write_registers(steam,
+- STEAM_REG_LPAD_MODE, 0x07, /* disable mouse */
+- STEAM_REG_RPAD_MODE, 0x07, /* disable mouse */
+- STEAM_REG_LPAD_CLICK_PRESSURE, 0xFFFF, /* disable clicky pad */
+- STEAM_REG_RPAD_CLICK_PRESSURE, 0xFFFF, /* disable clicky pad */
+- STEAM_REG_WATCHDOG_ENABLE, 0, /* disable watchdog that tests if Steam is active */
++ steam_write_settings(steam,
++ SETTING_LEFT_TRACKPAD_MODE, TRACKPAD_NONE, /* disable mouse */
++ SETTING_RIGHT_TRACKPAD_MODE, TRACKPAD_NONE, /* disable mouse */
++ SETTING_LEFT_TRACKPAD_CLICK_PRESSURE, 0xFFFF, /* disable haptic click */
++ SETTING_RIGHT_TRACKPAD_CLICK_PRESSURE, 0xFFFF, /* disable haptic click */
++ SETTING_STEAM_WATCHDOG_ENABLE, 0, /* disable watchdog that tests if Steam is active */
+ 0);
+ mutex_unlock(&steam->report_mutex);
+ } else {
+- steam_write_registers(steam,
+- STEAM_REG_LPAD_MODE, 0x07, /* disable mouse */
+- STEAM_REG_RPAD_MODE, 0x07, /* disable mouse */
++ steam_write_settings(steam,
++ SETTING_LEFT_TRACKPAD_MODE, TRACKPAD_NONE, /* disable mouse */
++ SETTING_RIGHT_TRACKPAD_MODE, TRACKPAD_NONE, /* disable mouse */
+ 0);
+ mutex_unlock(&steam->report_mutex);
+ }
+@@ -1362,7 +1518,7 @@ static int steam_raw_event(struct hid_device *hdev,
+ return 0;
+
+ switch (data[2]) {
+- case STEAM_EV_INPUT_DATA:
++ case ID_CONTROLLER_STATE:
+ if (steam->client_opened)
+ return 0;
+ rcu_read_lock();
+@@ -1371,7 +1527,7 @@ static int steam_raw_event(struct hid_device *hdev,
+ steam_do_input_event(steam, input, data);
+ rcu_read_unlock();
+ break;
+- case STEAM_EV_DECK_INPUT_DATA:
++ case ID_CONTROLLER_DECK_STATE:
+ if (steam->client_opened)
+ return 0;
+ rcu_read_lock();
+@@ -1380,7 +1536,7 @@ static int steam_raw_event(struct hid_device *hdev,
+ steam_do_deck_input_event(steam, input, data);
+ rcu_read_unlock();
+ break;
+- case STEAM_EV_CONNECT:
++ case ID_CONTROLLER_WIRELESS:
+ /*
+ * The payload of this event is a single byte:
+ * 0x01: disconnected.
+@@ -1395,7 +1551,7 @@ static int steam_raw_event(struct hid_device *hdev,
+ break;
+ }
+ break;
+- case STEAM_EV_BATTERY:
++ case ID_CONTROLLER_STATUS:
+ if (steam->quirks & STEAM_QUIRK_WIRELESS) {
+ rcu_read_lock();
+ battery = rcu_dereference(steam->battery);
+--
+2.39.5
+
--- /dev/null
+From 10b48585546bad0a85656701c640393bfa26df41 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 5 Feb 2025 15:51:10 +0000
+Subject: ipv4: add RCU protection to ip4_dst_hoplimit()
+
+From: Eric Dumazet <edumazet@google.com>
+
+[ Upstream commit 469308552ca4560176cfc100e7ca84add1bebd7c ]
+
+ip4_dst_hoplimit() must use RCU protection to make
+sure the net structure it reads does not disappear.
+
+Fixes: fa50d974d104 ("ipv4: Namespaceify ip_default_ttl sysctl knob")
+Signed-off-by: Eric Dumazet <edumazet@google.com>
+Reviewed-by: Kuniyuki Iwashima <kuniyu@amazon.com>
+Link: https://patch.msgid.link/20250205155120.1676781-3-edumazet@google.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/route.h | 9 +++++++--
+ 1 file changed, 7 insertions(+), 2 deletions(-)
+
+diff --git a/include/net/route.h b/include/net/route.h
+index 51a45b1887b56..0171e9e1bbea3 100644
+--- a/include/net/route.h
++++ b/include/net/route.h
+@@ -357,10 +357,15 @@ static inline int inet_iif(const struct sk_buff *skb)
+ static inline int ip4_dst_hoplimit(const struct dst_entry *dst)
+ {
+ int hoplimit = dst_metric_raw(dst, RTAX_HOPLIMIT);
+- struct net *net = dev_net(dst->dev);
+
+- if (hoplimit == 0)
++ if (hoplimit == 0) {
++ const struct net *net;
++
++ rcu_read_lock();
++ net = dev_net_rcu(dst->dev);
+ hoplimit = READ_ONCE(net->ipv4.sysctl_ip_default_ttl);
++ rcu_read_unlock();
++ }
+ return hoplimit;
+ }
+
+--
+2.39.5
+
--- /dev/null
+From 58a0718e856c47d597cb03e1ddbce04ae65eba2b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 5 Feb 2025 15:51:16 +0000
+Subject: ipv4: icmp: convert to dev_net_rcu()
+
+From: Eric Dumazet <edumazet@google.com>
+
+[ Upstream commit 4b8474a0951e605d2a27a2c483da4eb4b8c63760 ]
+
+__icmp_send() must ensure rcu_read_lock() is held, as spotted
+by Jakub.
+
+Other ICMP uses of dev_net() seem safe, change them to dev_net_rcu()
+to get LOCKDEP support.
+
+Fixes: dde1bc0e6f86 ("[NETNS]: Add namespace for ICMP replying code.")
+Closes: https://lore.kernel.org/netdev/20250203153633.46ce0337@kernel.org/
+Reported-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Eric Dumazet <edumazet@google.com>
+Link: https://patch.msgid.link/20250205155120.1676781-9-edumazet@google.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/ipv4/icmp.c | 31 +++++++++++++++++--------------
+ 1 file changed, 17 insertions(+), 14 deletions(-)
+
+diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
+index a6adf6a2ec4b5..a21d32b3ae6c3 100644
+--- a/net/ipv4/icmp.c
++++ b/net/ipv4/icmp.c
+@@ -403,10 +403,10 @@ static void icmp_push_reply(struct sock *sk,
+
+ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb)
+ {
+- struct ipcm_cookie ipc;
+ struct rtable *rt = skb_rtable(skb);
+- struct net *net = dev_net(rt->dst.dev);
++ struct net *net = dev_net_rcu(rt->dst.dev);
+ bool apply_ratelimit = false;
++ struct ipcm_cookie ipc;
+ struct flowi4 fl4;
+ struct sock *sk;
+ struct inet_sock *inet;
+@@ -609,12 +609,14 @@ void __icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info,
+ struct sock *sk;
+
+ if (!rt)
+- goto out;
++ return;
++
++ rcu_read_lock();
+
+ if (rt->dst.dev)
+- net = dev_net(rt->dst.dev);
++ net = dev_net_rcu(rt->dst.dev);
+ else if (skb_in->dev)
+- net = dev_net(skb_in->dev);
++ net = dev_net_rcu(skb_in->dev);
+ else
+ goto out;
+
+@@ -783,7 +785,8 @@ void __icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info,
+ icmp_xmit_unlock(sk);
+ out_bh_enable:
+ local_bh_enable();
+-out:;
++out:
++ rcu_read_unlock();
+ }
+ EXPORT_SYMBOL(__icmp_send);
+
+@@ -832,7 +835,7 @@ static void icmp_socket_deliver(struct sk_buff *skb, u32 info)
+ * avoid additional coding at protocol handlers.
+ */
+ if (!pskb_may_pull(skb, iph->ihl * 4 + 8)) {
+- __ICMP_INC_STATS(dev_net(skb->dev), ICMP_MIB_INERRORS);
++ __ICMP_INC_STATS(dev_net_rcu(skb->dev), ICMP_MIB_INERRORS);
+ return;
+ }
+
+@@ -866,7 +869,7 @@ static enum skb_drop_reason icmp_unreach(struct sk_buff *skb)
+ struct net *net;
+ u32 info = 0;
+
+- net = dev_net(skb_dst(skb)->dev);
++ net = dev_net_rcu(skb_dst(skb)->dev);
+
+ /*
+ * Incomplete header ?
+@@ -977,7 +980,7 @@ static enum skb_drop_reason icmp_unreach(struct sk_buff *skb)
+ static enum skb_drop_reason icmp_redirect(struct sk_buff *skb)
+ {
+ if (skb->len < sizeof(struct iphdr)) {
+- __ICMP_INC_STATS(dev_net(skb->dev), ICMP_MIB_INERRORS);
++ __ICMP_INC_STATS(dev_net_rcu(skb->dev), ICMP_MIB_INERRORS);
+ return SKB_DROP_REASON_PKT_TOO_SMALL;
+ }
+
+@@ -1009,7 +1012,7 @@ static enum skb_drop_reason icmp_echo(struct sk_buff *skb)
+ struct icmp_bxm icmp_param;
+ struct net *net;
+
+- net = dev_net(skb_dst(skb)->dev);
++ net = dev_net_rcu(skb_dst(skb)->dev);
+ /* should there be an ICMP stat for ignored echos? */
+ if (READ_ONCE(net->ipv4.sysctl_icmp_echo_ignore_all))
+ return SKB_NOT_DROPPED_YET;
+@@ -1038,9 +1041,9 @@ static enum skb_drop_reason icmp_echo(struct sk_buff *skb)
+
+ bool icmp_build_probe(struct sk_buff *skb, struct icmphdr *icmphdr)
+ {
++ struct net *net = dev_net_rcu(skb->dev);
+ struct icmp_ext_hdr *ext_hdr, _ext_hdr;
+ struct icmp_ext_echo_iio *iio, _iio;
+- struct net *net = dev_net(skb->dev);
+ struct inet6_dev *in6_dev;
+ struct in_device *in_dev;
+ struct net_device *dev;
+@@ -1179,7 +1182,7 @@ static enum skb_drop_reason icmp_timestamp(struct sk_buff *skb)
+ return SKB_NOT_DROPPED_YET;
+
+ out_err:
+- __ICMP_INC_STATS(dev_net(skb_dst(skb)->dev), ICMP_MIB_INERRORS);
++ __ICMP_INC_STATS(dev_net_rcu(skb_dst(skb)->dev), ICMP_MIB_INERRORS);
+ return SKB_DROP_REASON_PKT_TOO_SMALL;
+ }
+
+@@ -1196,7 +1199,7 @@ int icmp_rcv(struct sk_buff *skb)
+ {
+ enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
+ struct rtable *rt = skb_rtable(skb);
+- struct net *net = dev_net(rt->dst.dev);
++ struct net *net = dev_net_rcu(rt->dst.dev);
+ struct icmphdr *icmph;
+
+ if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
+@@ -1369,9 +1372,9 @@ int icmp_err(struct sk_buff *skb, u32 info)
+ struct iphdr *iph = (struct iphdr *)skb->data;
+ int offset = iph->ihl<<2;
+ struct icmphdr *icmph = (struct icmphdr *)(skb->data + offset);
++ struct net *net = dev_net_rcu(skb->dev);
+ int type = icmp_hdr(skb)->type;
+ int code = icmp_hdr(skb)->code;
+- struct net *net = dev_net(skb->dev);
+
+ /*
+ * Use ping_err to handle all icmp errors except those
+--
+2.39.5
+
--- /dev/null
+From 8c048c1c6f1ad0fd47a090345cb9e675d1317653 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 5 Feb 2025 15:51:15 +0000
+Subject: ipv4: use RCU protection in __ip_rt_update_pmtu()
+
+From: Eric Dumazet <edumazet@google.com>
+
+[ Upstream commit 139512191bd06f1b496117c76372b2ce372c9a41 ]
+
+__ip_rt_update_pmtu() must use RCU protection to make
+sure the net structure it reads does not disappear.
+
+Fixes: 2fbc6e89b2f1 ("ipv4: Update exception handling for multipath routes via same device")
+Fixes: 1de6b15a434c ("Namespaceify min_pmtu sysctl")
+Signed-off-by: Eric Dumazet <edumazet@google.com>
+Link: https://patch.msgid.link/20250205155120.1676781-8-edumazet@google.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/ipv4/route.c | 11 ++++++-----
+ 1 file changed, 6 insertions(+), 5 deletions(-)
+
+diff --git a/net/ipv4/route.c b/net/ipv4/route.c
+index 2e524c27a5171..97dc30a03dbf2 100644
+--- a/net/ipv4/route.c
++++ b/net/ipv4/route.c
+@@ -1020,9 +1020,9 @@ out: kfree_skb_reason(skb, reason);
+ static void __ip_rt_update_pmtu(struct rtable *rt, struct flowi4 *fl4, u32 mtu)
+ {
+ struct dst_entry *dst = &rt->dst;
+- struct net *net = dev_net(dst->dev);
+ struct fib_result res;
+ bool lock = false;
++ struct net *net;
+ u32 old_mtu;
+
+ if (ip_mtu_locked(dst))
+@@ -1032,6 +1032,8 @@ static void __ip_rt_update_pmtu(struct rtable *rt, struct flowi4 *fl4, u32 mtu)
+ if (old_mtu < mtu)
+ return;
+
++ rcu_read_lock();
++ net = dev_net_rcu(dst->dev);
+ if (mtu < net->ipv4.ip_rt_min_pmtu) {
+ lock = true;
+ mtu = min(old_mtu, net->ipv4.ip_rt_min_pmtu);
+@@ -1039,9 +1041,8 @@ static void __ip_rt_update_pmtu(struct rtable *rt, struct flowi4 *fl4, u32 mtu)
+
+ if (rt->rt_pmtu == mtu && !lock &&
+ time_before(jiffies, dst->expires - net->ipv4.ip_rt_mtu_expires / 2))
+- return;
++ goto out;
+
+- rcu_read_lock();
+ if (fib_lookup(net, fl4, &res, 0) == 0) {
+ struct fib_nh_common *nhc;
+
+@@ -1055,14 +1056,14 @@ static void __ip_rt_update_pmtu(struct rtable *rt, struct flowi4 *fl4, u32 mtu)
+ update_or_create_fnhe(nhc, fl4->daddr, 0, mtu, lock,
+ jiffies + net->ipv4.ip_rt_mtu_expires);
+ }
+- rcu_read_unlock();
+- return;
++ goto out;
+ }
+ #endif /* CONFIG_IP_ROUTE_MULTIPATH */
+ nhc = FIB_RES_NHC(res);
+ update_or_create_fnhe(nhc, fl4->daddr, 0, mtu, lock,
+ jiffies + net->ipv4.ip_rt_mtu_expires);
+ }
++out:
+ rcu_read_unlock();
+ }
+
+--
+2.39.5
+
--- /dev/null
+From 20f70039b88fe3b8aa98048e9a3c33eebbfaf4ff Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 5 Feb 2025 15:51:14 +0000
+Subject: ipv4: use RCU protection in inet_select_addr()
+
+From: Eric Dumazet <edumazet@google.com>
+
+[ Upstream commit 719817cd293e4fa389e1f69c396f3f816ed5aa41 ]
+
+inet_select_addr() must use RCU protection to make
+sure the net structure it reads does not disappear.
+
+Fixes: c4544c724322 ("[NETNS]: Process inet_select_addr inside a namespace.")
+Signed-off-by: Eric Dumazet <edumazet@google.com>
+Link: https://patch.msgid.link/20250205155120.1676781-7-edumazet@google.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/ipv4/devinet.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c
+index 4822f68edbf08..c33b1ecc591e4 100644
+--- a/net/ipv4/devinet.c
++++ b/net/ipv4/devinet.c
+@@ -1341,10 +1341,11 @@ __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope)
+ __be32 addr = 0;
+ unsigned char localnet_scope = RT_SCOPE_HOST;
+ struct in_device *in_dev;
+- struct net *net = dev_net(dev);
++ struct net *net;
+ int master_idx;
+
+ rcu_read_lock();
++ net = dev_net_rcu(dev);
+ in_dev = __in_dev_get_rcu(dev);
+ if (!in_dev)
+ goto no_in_dev;
+--
+2.39.5
+
--- /dev/null
+From 7966558932d043b89d988916b242917a170b33c0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 5 Feb 2025 15:51:12 +0000
+Subject: ipv4: use RCU protection in ipv4_default_advmss()
+
+From: Eric Dumazet <edumazet@google.com>
+
+[ Upstream commit 71b8471c93fa0bcab911fcb65da1eb6c4f5f735f ]
+
+ipv4_default_advmss() must use RCU protection to make
+sure the net structure it reads does not disappear.
+
+Fixes: 2e9589ff809e ("ipv4: Namespaceify min_adv_mss sysctl knob")
+Signed-off-by: Eric Dumazet <edumazet@google.com>
+Reviewed-by: Kuniyuki Iwashima <kuniyu@amazon.com>
+Link: https://patch.msgid.link/20250205155120.1676781-5-edumazet@google.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/ipv4/route.c | 11 ++++++++---
+ 1 file changed, 8 insertions(+), 3 deletions(-)
+
+diff --git a/net/ipv4/route.c b/net/ipv4/route.c
+index 61fc2166a870e..15cf2949ce951 100644
+--- a/net/ipv4/route.c
++++ b/net/ipv4/route.c
+@@ -1306,10 +1306,15 @@ static void set_class_tag(struct rtable *rt, u32 tag)
+
+ static unsigned int ipv4_default_advmss(const struct dst_entry *dst)
+ {
+- struct net *net = dev_net(dst->dev);
+ unsigned int header_size = sizeof(struct tcphdr) + sizeof(struct iphdr);
+- unsigned int advmss = max_t(unsigned int, ipv4_mtu(dst) - header_size,
+- net->ipv4.ip_rt_min_advmss);
++ unsigned int advmss;
++ struct net *net;
++
++ rcu_read_lock();
++ net = dev_net_rcu(dst->dev);
++ advmss = max_t(unsigned int, ipv4_mtu(dst) - header_size,
++ net->ipv4.ip_rt_min_advmss);
++ rcu_read_unlock();
+
+ return min(advmss, IPV4_MAX_PMTU - header_size);
+ }
+--
+2.39.5
+
--- /dev/null
+From 991bba8ee88b4f3b012da9d98d60a1fab39f0352 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 5 Feb 2025 15:51:13 +0000
+Subject: ipv4: use RCU protection in rt_is_expired()
+
+From: Eric Dumazet <edumazet@google.com>
+
+[ Upstream commit dd205fcc33d92d54eee4d7f21bb073af9bd5ce2b ]
+
+rt_is_expired() must use RCU protection to make
+sure the net structure it reads does not disappear.
+
+Fixes: e84f84f27647 ("netns: place rt_genid into struct net")
+Signed-off-by: Eric Dumazet <edumazet@google.com>
+Reviewed-by: Kuniyuki Iwashima <kuniyu@amazon.com>
+Link: https://patch.msgid.link/20250205155120.1676781-6-edumazet@google.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/ipv4/route.c | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+diff --git a/net/ipv4/route.c b/net/ipv4/route.c
+index 15cf2949ce951..c96837e6626c3 100644
+--- a/net/ipv4/route.c
++++ b/net/ipv4/route.c
+@@ -393,7 +393,13 @@ static inline int ip_rt_proc_init(void)
+
+ static inline bool rt_is_expired(const struct rtable *rth)
+ {
+- return rth->rt_genid != rt_genid_ipv4(dev_net(rth->dst.dev));
++ bool res;
++
++ rcu_read_lock();
++ res = rth->rt_genid != rt_genid_ipv4(dev_net_rcu(rth->dst.dev));
++ rcu_read_unlock();
++
++ return res;
+ }
+
+ void rt_cache_flush(struct net *net)
+--
+2.39.5
+
--- /dev/null
+From 2cd78e698c9ab4384991c6a383352e2fb889eef0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 5 Feb 2025 15:51:19 +0000
+Subject: ipv6: icmp: convert to dev_net_rcu()
+
+From: Eric Dumazet <edumazet@google.com>
+
+[ Upstream commit 34aef2b0ce3aa4eb4ef2e1f5cad3738d527032f5 ]
+
+icmp6_send() must acquire rcu_read_lock() sooner to ensure
+the dev_net() call done from a safe context.
+
+Other ICMPv6 uses of dev_net() seem safe, change them to
+dev_net_rcu() to get LOCKDEP support to catch bugs.
+
+Fixes: 9a43b709a230 ("[NETNS][IPV6] icmp6 - make icmpv6_socket per namespace")
+Signed-off-by: Eric Dumazet <edumazet@google.com>
+Link: https://patch.msgid.link/20250205155120.1676781-12-edumazet@google.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/ipv6/icmp.c | 42 +++++++++++++++++++++++-------------------
+ 1 file changed, 23 insertions(+), 19 deletions(-)
+
+diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c
+index 35df405ce1f75..fd91fd139d76c 100644
+--- a/net/ipv6/icmp.c
++++ b/net/ipv6/icmp.c
+@@ -76,7 +76,7 @@ static int icmpv6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
+ {
+ /* icmpv6_notify checks 8 bytes can be pulled, icmp6hdr is 8 bytes */
+ struct icmp6hdr *icmp6 = (struct icmp6hdr *) (skb->data + offset);
+- struct net *net = dev_net(skb->dev);
++ struct net *net = dev_net_rcu(skb->dev);
+
+ if (type == ICMPV6_PKT_TOOBIG)
+ ip6_update_pmtu(skb, net, info, skb->dev->ifindex, 0, sock_net_uid(net, NULL));
+@@ -473,7 +473,10 @@ void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
+
+ if (!skb->dev)
+ return;
+- net = dev_net(skb->dev);
++
++ rcu_read_lock();
++
++ net = dev_net_rcu(skb->dev);
+ mark = IP6_REPLY_MARK(net, skb->mark);
+ /*
+ * Make sure we respect the rules
+@@ -496,7 +499,7 @@ void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
+ !(type == ICMPV6_PARAMPROB &&
+ code == ICMPV6_UNK_OPTION &&
+ (opt_unrec(skb, info))))
+- return;
++ goto out;
+
+ saddr = NULL;
+ }
+@@ -526,7 +529,7 @@ void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
+ if ((addr_type == IPV6_ADDR_ANY) || (addr_type & IPV6_ADDR_MULTICAST)) {
+ net_dbg_ratelimited("icmp6_send: addr_any/mcast source [%pI6c > %pI6c]\n",
+ &hdr->saddr, &hdr->daddr);
+- return;
++ goto out;
+ }
+
+ /*
+@@ -535,7 +538,7 @@ void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
+ if (is_ineligible(skb)) {
+ net_dbg_ratelimited("icmp6_send: no reply to icmp error [%pI6c > %pI6c]\n",
+ &hdr->saddr, &hdr->daddr);
+- return;
++ goto out;
+ }
+
+ /* Needed by both icmpv6_global_allow and icmpv6_xmit_lock */
+@@ -582,7 +585,7 @@ void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
+ np = inet6_sk(sk);
+
+ if (!icmpv6_xrlim_allow(sk, type, &fl6, apply_ratelimit))
+- goto out;
++ goto out_unlock;
+
+ tmp_hdr.icmp6_type = type;
+ tmp_hdr.icmp6_code = code;
+@@ -600,7 +603,7 @@ void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
+
+ dst = icmpv6_route_lookup(net, skb, sk, &fl6);
+ if (IS_ERR(dst))
+- goto out;
++ goto out_unlock;
+
+ ipc6.hlimit = ip6_sk_dst_hoplimit(np, &fl6, dst);
+
+@@ -616,7 +619,6 @@ void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
+ goto out_dst_release;
+ }
+
+- rcu_read_lock();
+ idev = __in6_dev_get(skb->dev);
+
+ if (ip6_append_data(sk, icmpv6_getfrag, &msg,
+@@ -630,13 +632,15 @@ void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
+ icmpv6_push_pending_frames(sk, &fl6, &tmp_hdr,
+ len + sizeof(struct icmp6hdr));
+ }
+- rcu_read_unlock();
++
+ out_dst_release:
+ dst_release(dst);
+-out:
++out_unlock:
+ icmpv6_xmit_unlock(sk);
+ out_bh_enable:
+ local_bh_enable();
++out:
++ rcu_read_unlock();
+ }
+ EXPORT_SYMBOL(icmp6_send);
+
+@@ -679,8 +683,8 @@ int ip6_err_gen_icmpv6_unreach(struct sk_buff *skb, int nhs, int type,
+ skb_pull(skb2, nhs);
+ skb_reset_network_header(skb2);
+
+- rt = rt6_lookup(dev_net(skb->dev), &ipv6_hdr(skb2)->saddr, NULL, 0,
+- skb, 0);
++ rt = rt6_lookup(dev_net_rcu(skb->dev), &ipv6_hdr(skb2)->saddr,
++ NULL, 0, skb, 0);
+
+ if (rt && rt->dst.dev)
+ skb2->dev = rt->dst.dev;
+@@ -717,7 +721,7 @@ EXPORT_SYMBOL(ip6_err_gen_icmpv6_unreach);
+
+ static enum skb_drop_reason icmpv6_echo_reply(struct sk_buff *skb)
+ {
+- struct net *net = dev_net(skb->dev);
++ struct net *net = dev_net_rcu(skb->dev);
+ struct sock *sk;
+ struct inet6_dev *idev;
+ struct ipv6_pinfo *np;
+@@ -832,7 +836,7 @@ enum skb_drop_reason icmpv6_notify(struct sk_buff *skb, u8 type,
+ u8 code, __be32 info)
+ {
+ struct inet6_skb_parm *opt = IP6CB(skb);
+- struct net *net = dev_net(skb->dev);
++ struct net *net = dev_net_rcu(skb->dev);
+ const struct inet6_protocol *ipprot;
+ enum skb_drop_reason reason;
+ int inner_offset;
+@@ -889,7 +893,7 @@ enum skb_drop_reason icmpv6_notify(struct sk_buff *skb, u8 type,
+ static int icmpv6_rcv(struct sk_buff *skb)
+ {
+ enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
+- struct net *net = dev_net(skb->dev);
++ 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;
+@@ -921,7 +925,7 @@ static int icmpv6_rcv(struct sk_buff *skb)
+ skb_set_network_header(skb, nh);
+ }
+
+- __ICMP6_INC_STATS(dev_net(dev), idev, ICMP6_MIB_INMSGS);
++ __ICMP6_INC_STATS(dev_net_rcu(dev), idev, ICMP6_MIB_INMSGS);
+
+ saddr = &ipv6_hdr(skb)->saddr;
+ daddr = &ipv6_hdr(skb)->daddr;
+@@ -939,7 +943,7 @@ static int icmpv6_rcv(struct sk_buff *skb)
+
+ type = hdr->icmp6_type;
+
+- ICMP6MSGIN_INC_STATS(dev_net(dev), idev, type);
++ ICMP6MSGIN_INC_STATS(dev_net_rcu(dev), idev, type);
+
+ switch (type) {
+ case ICMPV6_ECHO_REQUEST:
+@@ -1034,9 +1038,9 @@ static int icmpv6_rcv(struct sk_buff *skb)
+
+ csum_error:
+ reason = SKB_DROP_REASON_ICMP_CSUM;
+- __ICMP6_INC_STATS(dev_net(dev), idev, ICMP6_MIB_CSUMERRORS);
++ __ICMP6_INC_STATS(dev_net_rcu(dev), idev, ICMP6_MIB_CSUMERRORS);
+ discard_it:
+- __ICMP6_INC_STATS(dev_net(dev), idev, ICMP6_MIB_INERRORS);
++ __ICMP6_INC_STATS(dev_net_rcu(dev), idev, ICMP6_MIB_INERRORS);
+ drop_no_count:
+ kfree_skb_reason(skb, reason);
+ return 0;
+--
+2.39.5
+
--- /dev/null
+From c21d9a56bd3435b3017070bb73df4c8cabd630d0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 12 Feb 2025 14:10:21 +0000
+Subject: ipv6: mcast: add RCU protection to mld_newpack()
+
+From: Eric Dumazet <edumazet@google.com>
+
+[ Upstream commit a527750d877fd334de87eef81f1cb5f0f0ca3373 ]
+
+mld_newpack() can be called without RTNL or RCU being held.
+
+Note that we no longer can use sock_alloc_send_skb() because
+ipv6.igmp_sk uses GFP_KERNEL allocations which can sleep.
+
+Instead use alloc_skb() and charge the net->ipv6.igmp_sk
+socket under RCU protection.
+
+Fixes: b8ad0cbc58f7 ("[NETNS][IPV6] mcast - handle several network namespace")
+Signed-off-by: Eric Dumazet <edumazet@google.com>
+Reviewed-by: David Ahern <dsahern@kernel.org>
+Link: https://patch.msgid.link/20250212141021.1663666-1-edumazet@google.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/ipv6/mcast.c | 14 ++++++++++----
+ 1 file changed, 10 insertions(+), 4 deletions(-)
+
+diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
+index a502669b2b28a..9bb246c09fcee 100644
+--- a/net/ipv6/mcast.c
++++ b/net/ipv6/mcast.c
+@@ -1729,21 +1729,19 @@ static struct sk_buff *mld_newpack(struct inet6_dev *idev, unsigned int mtu)
+ struct net_device *dev = idev->dev;
+ int hlen = LL_RESERVED_SPACE(dev);
+ int tlen = dev->needed_tailroom;
+- struct net *net = dev_net(dev);
+ const struct in6_addr *saddr;
+ struct in6_addr addr_buf;
+ struct mld2_report *pmr;
+ struct sk_buff *skb;
+ unsigned int size;
+ struct sock *sk;
+- int err;
++ struct net *net;
+
+- sk = net->ipv6.igmp_sk;
+ /* we assume size > sizeof(ra) here
+ * Also try to not allocate high-order pages for big MTU
+ */
+ size = min_t(int, mtu, PAGE_SIZE / 2) + hlen + tlen;
+- skb = sock_alloc_send_skb(sk, size, 1, &err);
++ skb = alloc_skb(size, GFP_KERNEL);
+ if (!skb)
+ return NULL;
+
+@@ -1751,6 +1749,12 @@ static struct sk_buff *mld_newpack(struct inet6_dev *idev, unsigned int mtu)
+ skb_reserve(skb, hlen);
+ skb_tailroom_reserve(skb, mtu, tlen);
+
++ rcu_read_lock();
++
++ net = dev_net_rcu(dev);
++ sk = net->ipv6.igmp_sk;
++ skb_set_owner_w(skb, sk);
++
+ if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) {
+ /* <draft-ietf-magma-mld-source-05.txt>:
+ * use unspecified address as the source address
+@@ -1762,6 +1766,8 @@ static struct sk_buff *mld_newpack(struct inet6_dev *idev, unsigned int mtu)
+
+ ip6_mc_hdr(sk, skb, dev, saddr, &mld2_all_mcr, NEXTHDR_HOP, 0);
+
++ rcu_read_unlock();
++
+ skb_put_data(skb, ra, sizeof(ra));
+
+ skb_set_transport_header(skb, skb_tail_pointer(skb) - skb->data);
+--
+2.39.5
+
--- /dev/null
+From 5cdc9562085f1ae474e09332713509620c55774d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 7 Feb 2025 13:58:40 +0000
+Subject: ipv6: mcast: extend RCU protection in igmp6_send()
+
+From: Eric Dumazet <edumazet@google.com>
+
+[ Upstream commit 087c1faa594fa07a66933d750c0b2610aa1a2946 ]
+
+igmp6_send() can be called without RTNL or RCU being held.
+
+Extend RCU protection so that we can safely fetch the net pointer
+and avoid a potential UAF.
+
+Note that we no longer can use sock_alloc_send_skb() because
+ipv6.igmp_sk uses GFP_KERNEL allocations which can sleep.
+
+Instead use alloc_skb() and charge the net->ipv6.igmp_sk
+socket under RCU protection.
+
+Fixes: b8ad0cbc58f7 ("[NETNS][IPV6] mcast - handle several network namespace")
+Signed-off-by: Eric Dumazet <edumazet@google.com>
+Reviewed-by: David Ahern <dsahern@kernel.org>
+Reviewed-by: Kuniyuki Iwashima <kuniyu@amazon.com>
+Link: https://patch.msgid.link/20250207135841.1948589-9-edumazet@google.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/ipv6/mcast.c | 31 +++++++++++++++----------------
+ 1 file changed, 15 insertions(+), 16 deletions(-)
+
+diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
+index 6e2f77a95a657..a502669b2b28a 100644
+--- a/net/ipv6/mcast.c
++++ b/net/ipv6/mcast.c
+@@ -2121,21 +2121,21 @@ static void mld_send_cr(struct inet6_dev *idev)
+
+ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
+ {
+- struct net *net = dev_net(dev);
+- struct sock *sk = net->ipv6.igmp_sk;
++ const struct in6_addr *snd_addr, *saddr;
++ int err, len, payload_len, full_len;
++ struct in6_addr addr_buf;
+ struct inet6_dev *idev;
+ struct sk_buff *skb;
+ struct mld_msg *hdr;
+- const struct in6_addr *snd_addr, *saddr;
+- struct in6_addr addr_buf;
+ int hlen = LL_RESERVED_SPACE(dev);
+ int tlen = dev->needed_tailroom;
+- int err, len, payload_len, full_len;
+ u8 ra[8] = { IPPROTO_ICMPV6, 0,
+ IPV6_TLV_ROUTERALERT, 2, 0, 0,
+ IPV6_TLV_PADN, 0 };
+- struct flowi6 fl6;
+ struct dst_entry *dst;
++ struct flowi6 fl6;
++ struct net *net;
++ struct sock *sk;
+
+ if (type == ICMPV6_MGM_REDUCTION)
+ snd_addr = &in6addr_linklocal_allrouters;
+@@ -2146,19 +2146,21 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
+ payload_len = len + sizeof(ra);
+ full_len = sizeof(struct ipv6hdr) + payload_len;
+
+- rcu_read_lock();
+- IP6_INC_STATS(net, __in6_dev_get(dev), IPSTATS_MIB_OUTREQUESTS);
+- rcu_read_unlock();
++ skb = alloc_skb(hlen + tlen + full_len, GFP_KERNEL);
+
+- skb = sock_alloc_send_skb(sk, hlen + tlen + full_len, 1, &err);
++ rcu_read_lock();
+
++ net = dev_net_rcu(dev);
++ idev = __in6_dev_get(dev);
++ IP6_INC_STATS(net, idev, IPSTATS_MIB_OUTREQUESTS);
+ if (!skb) {
+- rcu_read_lock();
+- IP6_INC_STATS(net, __in6_dev_get(dev),
+- IPSTATS_MIB_OUTDISCARDS);
++ IP6_INC_STATS(net, idev, IPSTATS_MIB_OUTDISCARDS);
+ rcu_read_unlock();
+ return;
+ }
++ sk = net->ipv6.igmp_sk;
++ skb_set_owner_w(skb, sk);
++
+ skb->priority = TC_PRIO_CONTROL;
+ skb_reserve(skb, hlen);
+
+@@ -2183,9 +2185,6 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
+ IPPROTO_ICMPV6,
+ csum_partial(hdr, len, 0));
+
+- rcu_read_lock();
+- idev = __in6_dev_get(skb->dev);
+-
+ icmpv6_flow_init(sk, &fl6, type,
+ &ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr,
+ skb->dev->ifindex);
+--
+2.39.5
+
--- /dev/null
+From f5a24e73ae93c7023166dea4c5d9b609932a6e39 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 5 Feb 2025 15:51:18 +0000
+Subject: ipv6: use RCU protection in ip6_default_advmss()
+
+From: Eric Dumazet <edumazet@google.com>
+
+[ Upstream commit 3c8ffcd248da34fc41e52a46e51505900115fc2a ]
+
+ip6_default_advmss() needs rcu protection to make
+sure the net structure it reads does not disappear.
+
+Fixes: 5578689a4e3c ("[NETNS][IPV6] route6 - make route6 per namespace")
+Signed-off-by: Eric Dumazet <edumazet@google.com>
+Reviewed-by: Kuniyuki Iwashima <kuniyu@amazon.com>
+Link: https://patch.msgid.link/20250205155120.1676781-11-edumazet@google.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/ipv6/route.c | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/net/ipv6/route.c b/net/ipv6/route.c
+index c5cee40a658b4..5715d54f3d0be 100644
+--- a/net/ipv6/route.c
++++ b/net/ipv6/route.c
+@@ -3188,13 +3188,18 @@ static unsigned int ip6_default_advmss(const struct dst_entry *dst)
+ {
+ struct net_device *dev = dst->dev;
+ unsigned int mtu = dst_mtu(dst);
+- struct net *net = dev_net(dev);
++ struct net *net;
+
+ mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
+
++ rcu_read_lock();
++
++ net = dev_net_rcu(dev);
+ if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss)
+ mtu = net->ipv6.sysctl.ip6_rt_min_advmss;
+
++ rcu_read_unlock();
++
+ /*
+ * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and
+ * corresponding MSS is IPV6_MAXPLEN - tcp_header_size.
+--
+2.39.5
+
--- /dev/null
+From 835aff5cd98c08aabf26924c57283e7f9912cafe Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 7 Feb 2025 13:58:39 +0000
+Subject: ndisc: extend RCU protection in ndisc_send_skb()
+
+From: Eric Dumazet <edumazet@google.com>
+
+[ Upstream commit ed6ae1f325d3c43966ec1b62ac1459e2b8e45640 ]
+
+ndisc_send_skb() can be called without RTNL or RCU held.
+
+Acquire rcu_read_lock() earlier, so that we can use dev_net_rcu()
+and avoid a potential UAF.
+
+Fixes: 1762f7e88eb3 ("[NETNS][IPV6] ndisc - make socket control per namespace")
+Signed-off-by: Eric Dumazet <edumazet@google.com>
+Reviewed-by: David Ahern <dsahern@kernel.org>
+Reviewed-by: Kuniyuki Iwashima <kuniyu@amazon.com>
+Link: https://patch.msgid.link/20250207135841.1948589-8-edumazet@google.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/ipv6/ndisc.c | 12 ++++++++----
+ 1 file changed, 8 insertions(+), 4 deletions(-)
+
+diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
+index ea3cb26da5846..8d853971f2f68 100644
+--- a/net/ipv6/ndisc.c
++++ b/net/ipv6/ndisc.c
+@@ -471,16 +471,20 @@ static void ip6_nd_hdr(struct sk_buff *skb,
+ void ndisc_send_skb(struct sk_buff *skb, const struct in6_addr *daddr,
+ const struct in6_addr *saddr)
+ {
++ struct icmp6hdr *icmp6h = icmp6_hdr(skb);
+ struct dst_entry *dst = skb_dst(skb);
+- struct net *net = dev_net(skb->dev);
+- struct sock *sk = net->ipv6.ndisc_sk;
+ struct inet6_dev *idev;
++ struct net *net;
++ struct sock *sk;
+ int err;
+- struct icmp6hdr *icmp6h = icmp6_hdr(skb);
+ u8 type;
+
+ type = icmp6h->icmp6_type;
+
++ rcu_read_lock();
++
++ net = dev_net_rcu(skb->dev);
++ sk = net->ipv6.ndisc_sk;
+ if (!dst) {
+ struct flowi6 fl6;
+ int oif = skb->dev->ifindex;
+@@ -488,6 +492,7 @@ void ndisc_send_skb(struct sk_buff *skb, const struct in6_addr *daddr,
+ icmpv6_flow_init(sk, &fl6, type, saddr, daddr, oif);
+ dst = icmp6_dst_alloc(skb->dev, &fl6);
+ if (IS_ERR(dst)) {
++ rcu_read_unlock();
+ kfree_skb(skb);
+ return;
+ }
+@@ -502,7 +507,6 @@ void ndisc_send_skb(struct sk_buff *skb, const struct in6_addr *daddr,
+
+ ip6_nd_hdr(skb, saddr, daddr, inet6_sk(sk)->hop_limit, skb->len);
+
+- rcu_read_lock();
+ idev = __in6_dev_get(dst->dev);
+ IP6_INC_STATS(net, idev, IPSTATS_MIB_OUTREQUESTS);
+
+--
+2.39.5
+
--- /dev/null
+From 647df0cc795f962a52832fe40c8a2b4ea1f081bc Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 7 Feb 2025 13:58:34 +0000
+Subject: ndisc: use RCU protection in ndisc_alloc_skb()
+
+From: Eric Dumazet <edumazet@google.com>
+
+[ Upstream commit 628e6d18930bbd21f2d4562228afe27694f66da9 ]
+
+ndisc_alloc_skb() can be called without RTNL or RCU being held.
+
+Add RCU protection to avoid possible UAF.
+
+Fixes: de09334b9326 ("ndisc: Introduce ndisc_alloc_skb() helper.")
+Signed-off-by: Eric Dumazet <edumazet@google.com>
+Reviewed-by: David Ahern <dsahern@kernel.org>
+Reviewed-by: Kuniyuki Iwashima <kuniyu@amazon.com>
+Link: https://patch.msgid.link/20250207135841.1948589-3-edumazet@google.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/ipv6/ndisc.c | 10 ++++------
+ 1 file changed, 4 insertions(+), 6 deletions(-)
+
+diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
+index 561972143ca42..ea3cb26da5846 100644
+--- a/net/ipv6/ndisc.c
++++ b/net/ipv6/ndisc.c
+@@ -418,15 +418,11 @@ static struct sk_buff *ndisc_alloc_skb(struct net_device *dev,
+ {
+ int hlen = LL_RESERVED_SPACE(dev);
+ int tlen = dev->needed_tailroom;
+- struct sock *sk = dev_net(dev)->ipv6.ndisc_sk;
+ struct sk_buff *skb;
+
+ skb = alloc_skb(hlen + sizeof(struct ipv6hdr) + len + tlen, GFP_ATOMIC);
+- if (!skb) {
+- ND_PRINTK(0, err, "ndisc: %s failed to allocate an skb\n",
+- __func__);
++ if (!skb)
+ return NULL;
+- }
+
+ skb->protocol = htons(ETH_P_IPV6);
+ skb->dev = dev;
+@@ -437,7 +433,9 @@ static struct sk_buff *ndisc_alloc_skb(struct net_device *dev,
+ /* Manually assign socket ownership as we avoid calling
+ * sock_alloc_send_pskb() to bypass wmem buffer limits
+ */
+- skb_set_owner_w(skb, sk);
++ rcu_read_lock();
++ skb_set_owner_w(skb, dev_net_rcu(dev)->ipv6.ndisc_sk);
++ rcu_read_unlock();
+
+ return skb;
+ }
+--
+2.39.5
+
--- /dev/null
+From 61f2092651726d360fd87255368c59b26a5b1830 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 22 Aug 2024 12:32:45 +0800
+Subject: neighbour: delete redundant judgment statements
+
+From: Li Zetao <lizetao1@huawei.com>
+
+[ Upstream commit c25bdd2ac8cf7da70a226f1a66cdce7af15ff86f ]
+
+The initial value of err is -ENOBUFS, and err is guaranteed to be
+less than 0 before all goto errout. Therefore, on the error path
+of errout, there is no need to repeatedly judge that err is less than 0,
+and delete redundant judgments to make the code more concise.
+
+Signed-off-by: Li Zetao <lizetao1@huawei.com>
+Reviewed-by: Petr Machata <petrm@nvidia.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Stable-dep-of: becbd5850c03 ("neighbour: use RCU protection in __neigh_notify()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/core/neighbour.c | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+diff --git a/net/core/neighbour.c b/net/core/neighbour.c
+index cb0c233e83962..118d932b3baa1 100644
+--- a/net/core/neighbour.c
++++ b/net/core/neighbour.c
+@@ -3526,8 +3526,7 @@ static void __neigh_notify(struct neighbour *n, int type, int flags,
+ rtnl_notify(skb, net, 0, RTNLGRP_NEIGH, NULL, GFP_ATOMIC);
+ return;
+ errout:
+- if (err < 0)
+- rtnl_set_sk_err(net, RTNLGRP_NEIGH, err);
++ rtnl_set_sk_err(net, RTNLGRP_NEIGH, err);
+ }
+
+ void neigh_app_ns(struct neighbour *n)
+--
+2.39.5
+
--- /dev/null
+From 245b248e640dd4b9c338a9b30646f92876c3ff92 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 7 Feb 2025 13:58:35 +0000
+Subject: neighbour: use RCU protection in __neigh_notify()
+
+From: Eric Dumazet <edumazet@google.com>
+
+[ Upstream commit becbd5850c03ed33b232083dd66c6e38c0c0e569 ]
+
+__neigh_notify() can be called without RTNL or RCU protection.
+
+Use RCU protection to avoid potential UAF.
+
+Fixes: 426b5303eb43 ("[NETNS]: Modify the neighbour table code so it handles multiple network namespaces")
+Signed-off-by: Eric Dumazet <edumazet@google.com>
+Reviewed-by: David Ahern <dsahern@kernel.org>
+Reviewed-by: Kuniyuki Iwashima <kuniyu@amazon.com>
+Link: https://patch.msgid.link/20250207135841.1948589-4-edumazet@google.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/core/neighbour.c | 8 ++++++--
+ 1 file changed, 6 insertions(+), 2 deletions(-)
+
+diff --git a/net/core/neighbour.c b/net/core/neighbour.c
+index 118d932b3baa1..e44feb39d459a 100644
+--- a/net/core/neighbour.c
++++ b/net/core/neighbour.c
+@@ -3508,10 +3508,12 @@ static const struct seq_operations neigh_stat_seq_ops = {
+ static void __neigh_notify(struct neighbour *n, int type, int flags,
+ u32 pid)
+ {
+- struct net *net = dev_net(n->dev);
+ struct sk_buff *skb;
+ int err = -ENOBUFS;
++ struct net *net;
+
++ rcu_read_lock();
++ net = dev_net_rcu(n->dev);
+ skb = nlmsg_new(neigh_nlmsg_size(), GFP_ATOMIC);
+ if (skb == NULL)
+ goto errout;
+@@ -3524,9 +3526,11 @@ static void __neigh_notify(struct neighbour *n, int type, int flags,
+ goto errout;
+ }
+ rtnl_notify(skb, net, 0, RTNLGRP_NEIGH, NULL, GFP_ATOMIC);
+- return;
++ goto out;
+ errout:
+ rtnl_set_sk_err(net, RTNLGRP_NEIGH, err);
++out:
++ rcu_read_unlock();
+ }
+
+ void neigh_app_ns(struct neighbour *n)
+--
+2.39.5
+
--- /dev/null
+From bdcf9427705b16f16f33183b8fc4a84d5e619506 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 5 Feb 2025 15:51:09 +0000
+Subject: net: add dev_net_rcu() helper
+
+From: Eric Dumazet <edumazet@google.com>
+
+[ Upstream commit 482ad2a4ace2740ca0ff1cbc8f3c7f862f3ab507 ]
+
+dev->nd_net can change, readers should either
+use rcu_read_lock() or RTNL.
+
+We currently use a generic helper, dev_net() with
+no debugging support. We probably have many hidden bugs.
+
+Add dev_net_rcu() helper for callers using rcu_read_lock()
+protection.
+
+Signed-off-by: Eric Dumazet <edumazet@google.com>
+Reviewed-by: Kuniyuki Iwashima <kuniyu@amazon.com>
+Link: https://patch.msgid.link/20250205155120.1676781-2-edumazet@google.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Stable-dep-of: 71b8471c93fa ("ipv4: use RCU protection in ipv4_default_advmss()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/linux/netdevice.h | 6 ++++++
+ include/net/net_namespace.h | 2 +-
+ 2 files changed, 7 insertions(+), 1 deletion(-)
+
+diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
+index 8b5121eb8757e..95ee88dfe0b9c 100644
+--- a/include/linux/netdevice.h
++++ b/include/linux/netdevice.h
+@@ -2593,6 +2593,12 @@ struct net *dev_net(const struct net_device *dev)
+ return read_pnet(&dev->nd_net);
+ }
+
++static inline
++struct net *dev_net_rcu(const struct net_device *dev)
++{
++ return read_pnet_rcu(&dev->nd_net);
++}
++
+ static inline
+ void dev_net_set(struct net_device *dev, struct net *net)
+ {
+diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h
+index b767bdcd9e122..ce3f84c6eb8eb 100644
+--- a/include/net/net_namespace.h
++++ b/include/net/net_namespace.h
+@@ -389,7 +389,7 @@ static inline struct net *read_pnet(const possible_net_t *pnet)
+ #endif
+ }
+
+-static inline struct net *read_pnet_rcu(possible_net_t *pnet)
++static inline struct net *read_pnet_rcu(const possible_net_t *pnet)
+ {
+ #ifdef CONFIG_NET_NS
+ return rcu_dereference(pnet->net);
+--
+2.39.5
+
--- /dev/null
+From b8a26376a2ec662e33d84dd6370300ed2ad9aad6 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 8 Nov 2024 09:34:24 +0000
+Subject: net: ipv4: Cache pmtu for all packet paths if multipath enabled
+
+From: Vladimir Vdovin <deliran@verdict.gg>
+
+[ Upstream commit 7d3f3b4367f315a61fc615e3138f3d320da8c466 ]
+
+Check number of paths by fib_info_num_path(),
+and update_or_create_fnhe() for every path.
+Problem is that pmtu is cached only for the oif
+that has received icmp message "need to frag",
+other oifs will still try to use "default" iface mtu.
+
+An example topology showing the problem:
+
+ | host1
+ +---------+
+ | dummy0 | 10.179.20.18/32 mtu9000
+ +---------+
+ +-----------+----------------+
+ +---------+ +---------+
+ | ens17f0 | 10.179.2.141/31 | ens17f1 | 10.179.2.13/31
+ +---------+ +---------+
+ | (all here have mtu 9000) |
+ +------+ +------+
+ | ro1 | 10.179.2.140/31 | ro2 | 10.179.2.12/31
+ +------+ +------+
+ | |
+---------+------------+-------------------+------
+ |
+ +-----+
+ | ro3 | 10.10.10.10 mtu1500
+ +-----+
+ |
+ ========================================
+ some networks
+ ========================================
+ |
+ +-----+
+ | eth0| 10.10.30.30 mtu9000
+ +-----+
+ | host2
+
+host1 have enabled multipath and
+sysctl net.ipv4.fib_multipath_hash_policy = 1:
+
+default proto static src 10.179.20.18
+ nexthop via 10.179.2.12 dev ens17f1 weight 1
+ nexthop via 10.179.2.140 dev ens17f0 weight 1
+
+When host1 tries to do pmtud from 10.179.20.18/32 to host2,
+host1 receives at ens17f1 iface an icmp packet from ro3 that ro3 mtu=1500.
+And host1 caches it in nexthop exceptions cache.
+
+Problem is that it is cached only for the iface that has received icmp,
+and there is no way that ro3 will send icmp msg to host1 via another path.
+
+Host1 now have this routes to host2:
+
+ip r g 10.10.30.30 sport 30000 dport 443
+10.10.30.30 via 10.179.2.12 dev ens17f1 src 10.179.20.18 uid 0
+ cache expires 521sec mtu 1500
+
+ip r g 10.10.30.30 sport 30033 dport 443
+10.10.30.30 via 10.179.2.140 dev ens17f0 src 10.179.20.18 uid 0
+ cache
+
+So when host1 tries again to reach host2 with mtu>1500,
+if packet flow is lucky enough to be hashed with oif=ens17f1 its ok,
+if oif=ens17f0 it blackholes and still gets icmp msgs from ro3 to ens17f1,
+until lucky day when ro3 will send it through another flow to ens17f0.
+
+Signed-off-by: Vladimir Vdovin <deliran@verdict.gg>
+Reviewed-by: Ido Schimmel <idosch@nvidia.com>
+Link: https://patch.msgid.link/20241108093427.317942-1-deliran@verdict.gg
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Stable-dep-of: 139512191bd0 ("ipv4: use RCU protection in __ip_rt_update_pmtu()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/ipv4/route.c | 13 ++++
+ tools/testing/selftests/net/pmtu.sh | 112 +++++++++++++++++++++++-----
+ 2 files changed, 108 insertions(+), 17 deletions(-)
+
+diff --git a/net/ipv4/route.c b/net/ipv4/route.c
+index c96837e6626c3..2e524c27a5171 100644
+--- a/net/ipv4/route.c
++++ b/net/ipv4/route.c
+@@ -1046,6 +1046,19 @@ static void __ip_rt_update_pmtu(struct rtable *rt, struct flowi4 *fl4, u32 mtu)
+ struct fib_nh_common *nhc;
+
+ fib_select_path(net, &res, fl4, NULL);
++#ifdef CONFIG_IP_ROUTE_MULTIPATH
++ if (fib_info_num_path(res.fi) > 1) {
++ int nhsel;
++
++ for (nhsel = 0; nhsel < fib_info_num_path(res.fi); nhsel++) {
++ nhc = fib_info_nhc(res.fi, nhsel);
++ update_or_create_fnhe(nhc, fl4->daddr, 0, mtu, lock,
++ jiffies + net->ipv4.ip_rt_mtu_expires);
++ }
++ rcu_read_unlock();
++ return;
++ }
++#endif /* CONFIG_IP_ROUTE_MULTIPATH */
+ nhc = FIB_RES_NHC(res);
+ update_or_create_fnhe(nhc, fl4->daddr, 0, mtu, lock,
+ jiffies + net->ipv4.ip_rt_mtu_expires);
+diff --git a/tools/testing/selftests/net/pmtu.sh b/tools/testing/selftests/net/pmtu.sh
+index 1c0dd2f781678..771f237c43534 100755
+--- a/tools/testing/selftests/net/pmtu.sh
++++ b/tools/testing/selftests/net/pmtu.sh
+@@ -197,6 +197,12 @@
+ #
+ # - pmtu_ipv6_route_change
+ # Same as above but with IPv6
++#
++# - pmtu_ipv4_mp_exceptions
++# Use the same topology as in pmtu_ipv4, but add routeable addresses
++# on host A and B on lo reachable via both routers. Host A and B
++# addresses have multipath routes to each other, b_r1 mtu = 1500.
++# Check that PMTU exceptions are created for both paths.
+
+ source lib.sh
+ source net_helper.sh
+@@ -266,7 +272,8 @@ tests="
+ list_flush_ipv4_exception ipv4: list and flush cached exceptions 1
+ list_flush_ipv6_exception ipv6: list and flush cached exceptions 1
+ pmtu_ipv4_route_change ipv4: PMTU exception w/route replace 1
+- pmtu_ipv6_route_change ipv6: PMTU exception w/route replace 1"
++ pmtu_ipv6_route_change ipv6: PMTU exception w/route replace 1
++ pmtu_ipv4_mp_exceptions ipv4: PMTU multipath nh exceptions 1"
+
+ # Addressing and routing for tests with routers: four network segments, with
+ # index SEGMENT between 1 and 4, a common prefix (PREFIX4 or PREFIX6) and an
+@@ -343,6 +350,9 @@ tunnel6_a_addr="fd00:2::a"
+ tunnel6_b_addr="fd00:2::b"
+ tunnel6_mask="64"
+
++host4_a_addr="192.168.99.99"
++host4_b_addr="192.168.88.88"
++
+ dummy6_0_prefix="fc00:1000::"
+ dummy6_1_prefix="fc00:1001::"
+ dummy6_mask="64"
+@@ -902,6 +912,52 @@ setup_ovs_bridge() {
+ run_cmd ip route add ${prefix6}:${b_r1}::1 via ${prefix6}:${a_r1}::2
+ }
+
++setup_multipath_new() {
++ # Set up host A with multipath routes to host B host4_b_addr
++ run_cmd ${ns_a} ip addr add ${host4_a_addr} dev lo
++ run_cmd ${ns_a} ip nexthop add id 401 via ${prefix4}.${a_r1}.2 dev veth_A-R1
++ run_cmd ${ns_a} ip nexthop add id 402 via ${prefix4}.${a_r2}.2 dev veth_A-R2
++ run_cmd ${ns_a} ip nexthop add id 403 group 401/402
++ run_cmd ${ns_a} ip route add ${host4_b_addr} src ${host4_a_addr} nhid 403
++
++ # Set up host B with multipath routes to host A host4_a_addr
++ run_cmd ${ns_b} ip addr add ${host4_b_addr} dev lo
++ run_cmd ${ns_b} ip nexthop add id 401 via ${prefix4}.${b_r1}.2 dev veth_B-R1
++ run_cmd ${ns_b} ip nexthop add id 402 via ${prefix4}.${b_r2}.2 dev veth_B-R2
++ run_cmd ${ns_b} ip nexthop add id 403 group 401/402
++ run_cmd ${ns_b} ip route add ${host4_a_addr} src ${host4_b_addr} nhid 403
++}
++
++setup_multipath_old() {
++ # Set up host A with multipath routes to host B host4_b_addr
++ run_cmd ${ns_a} ip addr add ${host4_a_addr} dev lo
++ run_cmd ${ns_a} ip route add ${host4_b_addr} \
++ src ${host4_a_addr} \
++ nexthop via ${prefix4}.${a_r1}.2 weight 1 \
++ nexthop via ${prefix4}.${a_r2}.2 weight 1
++
++ # Set up host B with multipath routes to host A host4_a_addr
++ run_cmd ${ns_b} ip addr add ${host4_b_addr} dev lo
++ run_cmd ${ns_b} ip route add ${host4_a_addr} \
++ src ${host4_b_addr} \
++ nexthop via ${prefix4}.${b_r1}.2 weight 1 \
++ nexthop via ${prefix4}.${b_r2}.2 weight 1
++}
++
++setup_multipath() {
++ if [ "$USE_NH" = "yes" ]; then
++ setup_multipath_new
++ else
++ setup_multipath_old
++ fi
++
++ # Set up routers with routes to dummies
++ run_cmd ${ns_r1} ip route add ${host4_a_addr} via ${prefix4}.${a_r1}.1
++ run_cmd ${ns_r2} ip route add ${host4_a_addr} via ${prefix4}.${a_r2}.1
++ run_cmd ${ns_r1} ip route add ${host4_b_addr} via ${prefix4}.${b_r1}.1
++ run_cmd ${ns_r2} ip route add ${host4_b_addr} via ${prefix4}.${b_r2}.1
++}
++
+ setup() {
+ [ "$(id -u)" -ne 0 ] && echo " need to run as root" && return $ksft_skip
+
+@@ -982,23 +1038,15 @@ link_get_mtu() {
+ }
+
+ route_get_dst_exception() {
+- ns_cmd="${1}"
+- dst="${2}"
+- dsfield="${3}"
++ ns_cmd="${1}"; shift
+
+- if [ -z "${dsfield}" ]; then
+- dsfield=0
+- fi
+-
+- ${ns_cmd} ip route get "${dst}" dsfield "${dsfield}"
++ ${ns_cmd} ip route get "$@"
+ }
+
+ route_get_dst_pmtu_from_exception() {
+- ns_cmd="${1}"
+- dst="${2}"
+- dsfield="${3}"
++ ns_cmd="${1}"; shift
+
+- mtu_parse "$(route_get_dst_exception "${ns_cmd}" "${dst}" "${dsfield}")"
++ mtu_parse "$(route_get_dst_exception "${ns_cmd}" "$@")"
+ }
+
+ check_pmtu_value() {
+@@ -1141,10 +1189,10 @@ test_pmtu_ipv4_dscp_icmp_exception() {
+ run_cmd "${ns_a}" ping -q -M want -Q "${dsfield}" -c 1 -w 1 -s "${len}" "${dst2}"
+
+ # Check that exceptions have been created with the correct PMTU
+- pmtu_1="$(route_get_dst_pmtu_from_exception "${ns_a}" "${dst1}" "${policy_mark}")"
++ pmtu_1="$(route_get_dst_pmtu_from_exception "${ns_a}" "${dst1}" dsfield "${policy_mark}")"
+ check_pmtu_value "1400" "${pmtu_1}" "exceeding MTU" || return 1
+
+- pmtu_2="$(route_get_dst_pmtu_from_exception "${ns_a}" "${dst2}" "${policy_mark}")"
++ pmtu_2="$(route_get_dst_pmtu_from_exception "${ns_a}" "${dst2}" dsfield "${policy_mark}")"
+ check_pmtu_value "1500" "${pmtu_2}" "exceeding MTU" || return 1
+ }
+
+@@ -1191,9 +1239,9 @@ test_pmtu_ipv4_dscp_udp_exception() {
+ UDP:"${dst2}":50000,tos="${dsfield}"
+
+ # Check that exceptions have been created with the correct PMTU
+- pmtu_1="$(route_get_dst_pmtu_from_exception "${ns_a}" "${dst1}" "${policy_mark}")"
++ pmtu_1="$(route_get_dst_pmtu_from_exception "${ns_a}" "${dst1}" dsfield "${policy_mark}")"
+ check_pmtu_value "1400" "${pmtu_1}" "exceeding MTU" || return 1
+- pmtu_2="$(route_get_dst_pmtu_from_exception "${ns_a}" "${dst2}" "${policy_mark}")"
++ pmtu_2="$(route_get_dst_pmtu_from_exception "${ns_a}" "${dst2}" dsfield "${policy_mark}")"
+ check_pmtu_value "1500" "${pmtu_2}" "exceeding MTU" || return 1
+ }
+
+@@ -2234,6 +2282,36 @@ test_pmtu_ipv6_route_change() {
+ test_pmtu_ipvX_route_change 6
+ }
+
++test_pmtu_ipv4_mp_exceptions() {
++ setup namespaces routing multipath || return $ksft_skip
++
++ trace "${ns_a}" veth_A-R1 "${ns_r1}" veth_R1-A \
++ "${ns_r1}" veth_R1-B "${ns_b}" veth_B-R1 \
++ "${ns_a}" veth_A-R2 "${ns_r2}" veth_R2-A \
++ "${ns_r2}" veth_R2-B "${ns_b}" veth_B-R2
++
++ # Set up initial MTU values
++ mtu "${ns_a}" veth_A-R1 2000
++ mtu "${ns_r1}" veth_R1-A 2000
++ mtu "${ns_r1}" veth_R1-B 1500
++ mtu "${ns_b}" veth_B-R1 1500
++
++ mtu "${ns_a}" veth_A-R2 2000
++ mtu "${ns_r2}" veth_R2-A 2000
++ mtu "${ns_r2}" veth_R2-B 1500
++ mtu "${ns_b}" veth_B-R2 1500
++
++ # Ping and expect two nexthop exceptions for two routes
++ run_cmd ${ns_a} ping -q -M want -i 0.1 -c 1 -s 1800 "${host4_b_addr}"
++
++ # Check that exceptions have been created with the correct PMTU
++ pmtu_a_R1="$(route_get_dst_pmtu_from_exception "${ns_a}" "${host4_b_addr}" oif veth_A-R1)"
++ pmtu_a_R2="$(route_get_dst_pmtu_from_exception "${ns_a}" "${host4_b_addr}" oif veth_A-R2)"
++
++ check_pmtu_value "1500" "${pmtu_a_R1}" "exceeding MTU (veth_A-R1)" || return 1
++ check_pmtu_value "1500" "${pmtu_a_R2}" "exceeding MTU (veth_A-R2)" || return 1
++}
++
+ usage() {
+ echo
+ echo "$0 [OPTIONS] [TEST]..."
+--
+2.39.5
+
--- /dev/null
+From fcd784b7fb946f6750ded7f88f9ce2fd88bc1b30 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 13 Oct 2023 14:10:23 +0200
+Subject: net: treat possible_net_t net pointer as an RCU one and add
+ read_pnet_rcu()
+
+From: Jiri Pirko <jiri@nvidia.com>
+
+[ Upstream commit 2034d90ae41ae93e30d492ebcf1f06f97a9cfba6 ]
+
+Make the net pointer stored in possible_net_t structure annotated as
+an RCU pointer. Change the access helpers to treat it as such.
+Introduce read_pnet_rcu() helper to allow caller to dereference
+the net pointer under RCU read lock.
+
+Signed-off-by: Jiri Pirko <jiri@nvidia.com>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Stable-dep-of: 71b8471c93fa ("ipv4: use RCU protection in ipv4_default_advmss()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/net_namespace.h | 15 ++++++++++++---
+ 1 file changed, 12 insertions(+), 3 deletions(-)
+
+diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h
+index 1befad79a6734..b767bdcd9e122 100644
+--- a/include/net/net_namespace.h
++++ b/include/net/net_namespace.h
+@@ -369,21 +369,30 @@ static inline void put_net_track(struct net *net, netns_tracker *tracker)
+
+ typedef struct {
+ #ifdef CONFIG_NET_NS
+- struct net *net;
++ struct net __rcu *net;
+ #endif
+ } possible_net_t;
+
+ static inline void write_pnet(possible_net_t *pnet, struct net *net)
+ {
+ #ifdef CONFIG_NET_NS
+- pnet->net = net;
++ rcu_assign_pointer(pnet->net, net);
+ #endif
+ }
+
+ static inline struct net *read_pnet(const possible_net_t *pnet)
+ {
+ #ifdef CONFIG_NET_NS
+- return pnet->net;
++ return rcu_dereference_protected(pnet->net, true);
++#else
++ return &init_net;
++#endif
++}
++
++static inline struct net *read_pnet_rcu(possible_net_t *pnet)
++{
++#ifdef CONFIG_NET_NS
++ return rcu_dereference(pnet->net);
+ #else
+ return &init_net;
+ #endif
+--
+2.39.5
+
--- /dev/null
+From 5b990f1f002ae421e6d4b4d6792189e0200647ec Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 7 Feb 2025 13:58:37 +0000
+Subject: openvswitch: use RCU protection in ovs_vport_cmd_fill_info()
+
+From: Eric Dumazet <edumazet@google.com>
+
+[ Upstream commit 90b2f49a502fa71090d9f4fe29a2f51fe5dff76d ]
+
+ovs_vport_cmd_fill_info() can be called without RTNL or RCU.
+
+Use RCU protection and dev_net_rcu() to avoid potential UAF.
+
+Fixes: 9354d4520342 ("openvswitch: reliable interface indentification in port dumps")
+Signed-off-by: Eric Dumazet <edumazet@google.com>
+Reviewed-by: Kuniyuki Iwashima <kuniyu@amazon.com>
+Link: https://patch.msgid.link/20250207135841.1948589-6-edumazet@google.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/openvswitch/datapath.c | 12 +++++++++---
+ 1 file changed, 9 insertions(+), 3 deletions(-)
+
+diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c
+index b7232142c13f8..cb52fac7caa3c 100644
+--- a/net/openvswitch/datapath.c
++++ b/net/openvswitch/datapath.c
+@@ -2103,6 +2103,7 @@ static int ovs_vport_cmd_fill_info(struct vport *vport, struct sk_buff *skb,
+ {
+ struct ovs_header *ovs_header;
+ struct ovs_vport_stats vport_stats;
++ struct net *net_vport;
+ int err;
+
+ ovs_header = genlmsg_put(skb, portid, seq, &dp_vport_genl_family,
+@@ -2119,12 +2120,15 @@ static int ovs_vport_cmd_fill_info(struct vport *vport, struct sk_buff *skb,
+ nla_put_u32(skb, OVS_VPORT_ATTR_IFINDEX, vport->dev->ifindex))
+ goto nla_put_failure;
+
+- if (!net_eq(net, dev_net(vport->dev))) {
+- int id = peernet2id_alloc(net, dev_net(vport->dev), gfp);
++ rcu_read_lock();
++ net_vport = dev_net_rcu(vport->dev);
++ if (!net_eq(net, net_vport)) {
++ int id = peernet2id_alloc(net, net_vport, GFP_ATOMIC);
+
+ if (nla_put_s32(skb, OVS_VPORT_ATTR_NETNSID, id))
+- goto nla_put_failure;
++ goto nla_put_failure_unlock;
+ }
++ rcu_read_unlock();
+
+ ovs_vport_get_stats(vport, &vport_stats);
+ if (nla_put_64bit(skb, OVS_VPORT_ATTR_STATS,
+@@ -2145,6 +2149,8 @@ static int ovs_vport_cmd_fill_info(struct vport *vport, struct sk_buff *skb,
+ genlmsg_end(skb, ovs_header);
+ return 0;
+
++nla_put_failure_unlock:
++ rcu_read_unlock();
+ nla_put_failure:
+ err = -EMSGSIZE;
+ error:
+--
+2.39.5
+
x86-mm-tlb-only-trim-the-mm_cpumask-once-a-second.patch
orangefs-fix-a-oob-in-orangefs_debug_write.patch
asoc-intel-bytcr_rt5640-add-dmi-quirk-for-vexia-edu-.patch
+clocksource-use-pr_info-for-checking-clocksource-syn.patch
+clocksource-use-migrate_disable-to-avoid-calling-get.patch
+ipv4-add-rcu-protection-to-ip4_dst_hoplimit.patch
+net-treat-possible_net_t-net-pointer-as-an-rcu-one-a.patch
+net-add-dev_net_rcu-helper.patch
+ipv4-use-rcu-protection-in-ipv4_default_advmss.patch
+ipv4-use-rcu-protection-in-rt_is_expired.patch
+ipv4-use-rcu-protection-in-inet_select_addr.patch
+net-ipv4-cache-pmtu-for-all-packet-paths-if-multipat.patch
+ipv4-use-rcu-protection-in-__ip_rt_update_pmtu.patch
+ipv4-icmp-convert-to-dev_net_rcu.patch
+flow_dissector-use-rcu-protection-to-fetch-dev_net.patch
+ipv6-use-rcu-protection-in-ip6_default_advmss.patch
+ipv6-icmp-convert-to-dev_net_rcu.patch
+hid-hid-steam-avoid-overwriting-smoothing-parameter.patch
+hid-hid-steam-disable-watchdog-instead-of-using-a-he.patch
+hid-hid-steam-clean-up-locking.patch
+hid-hid-steam-update-list-of-identifiers-from-sdl.patch
+hid-hid-steam-add-gamepad-only-mode-switched-to-by-h.patch
+hid-hid-steam-remove-pointless-error-message.patch
+hid-hid-steam-fix-cleanup-in-probe.patch
+hid-hid-steam-add-deck-imu-support.patch
+hid-hid-steam-make-sure-rumble-work-is-canceled-on-r.patch
+hid-hid-steam-move-hidraw-input-un-registering-to-wo.patch
+ndisc-use-rcu-protection-in-ndisc_alloc_skb.patch
+neighbour-delete-redundant-judgment-statements.patch
+neighbour-use-rcu-protection-in-__neigh_notify.patch
+arp-use-rcu-protection-in-arp_xmit.patch
+openvswitch-use-rcu-protection-in-ovs_vport_cmd_fill.patch
+ndisc-extend-rcu-protection-in-ndisc_send_skb.patch
+ipv6-mcast-extend-rcu-protection-in-igmp6_send.patch
+ipv6-mcast-add-rcu-protection-to-mld_newpack.patch