From: Greg Kroah-Hartman Date: Wed, 3 Jan 2024 10:19:18 +0000 (+0100) Subject: 6.6-stable patches X-Git-Tag: v5.10.206~19 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6569c287b7534551e75b6365bea049cfa40c1fe0;p=thirdparty%2Fkernel%2Fstable-queue.git 6.6-stable patches added patches: ftrace-fix-modification-of-direct_function-hash-while-in-use.patch ring-buffer-fix-wake-ups-when-buffer_percent-is-set-to-100.patch tracing-fix-blocked-reader-of-snapshot-buffer.patch wifi-cfg80211-fix-cqm-for-non-range-use.patch wifi-nl80211-fix-deadlock-in-nl80211_set_cqm_rssi-6.6.x.patch --- diff --git a/queue-6.6/ftrace-fix-modification-of-direct_function-hash-while-in-use.patch b/queue-6.6/ftrace-fix-modification-of-direct_function-hash-while-in-use.patch new file mode 100644 index 00000000000..1b0dcae5b8b --- /dev/null +++ b/queue-6.6/ftrace-fix-modification-of-direct_function-hash-while-in-use.patch @@ -0,0 +1,305 @@ +From d05cb470663a2a1879277e544f69e660208f08f2 Mon Sep 17 00:00:00 2001 +From: "Steven Rostedt (Google)" +Date: Fri, 29 Dec 2023 11:51:34 -0500 +Subject: ftrace: Fix modification of direct_function hash while in use + +From: Steven Rostedt (Google) + +commit d05cb470663a2a1879277e544f69e660208f08f2 upstream. + +Masami Hiramatsu reported a memory leak in register_ftrace_direct() where +if the number of new entries are added is large enough to cause two +allocations in the loop: + + for (i = 0; i < size; i++) { + hlist_for_each_entry(entry, &hash->buckets[i], hlist) { + new = ftrace_add_rec_direct(entry->ip, addr, &free_hash); + if (!new) + goto out_remove; + entry->direct = addr; + } + } + +Where ftrace_add_rec_direct() has: + + if (ftrace_hash_empty(direct_functions) || + direct_functions->count > 2 * (1 << direct_functions->size_bits)) { + struct ftrace_hash *new_hash; + int size = ftrace_hash_empty(direct_functions) ? 0 : + direct_functions->count + 1; + + if (size < 32) + size = 32; + + new_hash = dup_hash(direct_functions, size); + if (!new_hash) + return NULL; + + *free_hash = direct_functions; + direct_functions = new_hash; + } + +The "*free_hash = direct_functions;" can happen twice, losing the previous +allocation of direct_functions. + +But this also exposed a more serious bug. + +The modification of direct_functions above is not safe. As +direct_functions can be referenced at any time to find what direct caller +it should call, the time between: + + new_hash = dup_hash(direct_functions, size); + and + direct_functions = new_hash; + +can have a race with another CPU (or even this one if it gets interrupted), +and the entries being moved to the new hash are not referenced. + +That's because the "dup_hash()" is really misnamed and is really a +"move_hash()". It moves the entries from the old hash to the new one. + +Now even if that was changed, this code is not proper as direct_functions +should not be updated until the end. That is the best way to handle +function reference changes, and is the way other parts of ftrace handles +this. + +The following is done: + + 1. Change add_hash_entry() to return the entry it created and inserted + into the hash, and not just return success or not. + + 2. Replace ftrace_add_rec_direct() with add_hash_entry(), and remove + the former. + + 3. Allocate a "new_hash" at the start that is made for holding both the + new hash entries as well as the existing entries in direct_functions. + + 4. Copy (not move) the direct_function entries over to the new_hash. + + 5. Copy the entries of the added hash to the new_hash. + + 6. If everything succeeds, then use rcu_pointer_assign() to update the + direct_functions with the new_hash. + +This simplifies the code and fixes both the memory leak as well as the +race condition mentioned above. + +Link: https://lore.kernel.org/all/170368070504.42064.8960569647118388081.stgit@devnote2/ +Link: https://lore.kernel.org/linux-trace-kernel/20231229115134.08dd5174@gandalf.local.home + +Cc: stable@vger.kernel.org +Cc: Mark Rutland +Cc: Mathieu Desnoyers +Cc: Jiri Olsa +Cc: Alexei Starovoitov +Cc: Daniel Borkmann +Acked-by: Masami Hiramatsu (Google) +Fixes: 763e34e74bb7d ("ftrace: Add register_ftrace_direct()") +Signed-off-by: Steven Rostedt (Google) +Signed-off-by: Greg Kroah-Hartman +--- + kernel/trace/ftrace.c | 100 +++++++++++++++++++++++--------------------------- + 1 file changed, 47 insertions(+), 53 deletions(-) + +--- a/kernel/trace/ftrace.c ++++ b/kernel/trace/ftrace.c +@@ -1183,18 +1183,19 @@ static void __add_hash_entry(struct ftra + hash->count++; + } + +-static int add_hash_entry(struct ftrace_hash *hash, unsigned long ip) ++static struct ftrace_func_entry * ++add_hash_entry(struct ftrace_hash *hash, unsigned long ip) + { + struct ftrace_func_entry *entry; + + entry = kmalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) +- return -ENOMEM; ++ return NULL; + + entry->ip = ip; + __add_hash_entry(hash, entry); + +- return 0; ++ return entry; + } + + static void +@@ -1349,7 +1350,6 @@ alloc_and_copy_ftrace_hash(int size_bits + struct ftrace_func_entry *entry; + struct ftrace_hash *new_hash; + int size; +- int ret; + int i; + + new_hash = alloc_ftrace_hash(size_bits); +@@ -1366,8 +1366,7 @@ alloc_and_copy_ftrace_hash(int size_bits + size = 1 << hash->size_bits; + for (i = 0; i < size; i++) { + hlist_for_each_entry(entry, &hash->buckets[i], hlist) { +- ret = add_hash_entry(new_hash, entry->ip); +- if (ret < 0) ++ if (add_hash_entry(new_hash, entry->ip) == NULL) + goto free_hash; + } + } +@@ -2536,7 +2535,7 @@ ftrace_find_unique_ops(struct dyn_ftrace + + #ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS + /* Protected by rcu_tasks for reading, and direct_mutex for writing */ +-static struct ftrace_hash *direct_functions = EMPTY_HASH; ++static struct ftrace_hash __rcu *direct_functions = EMPTY_HASH; + static DEFINE_MUTEX(direct_mutex); + int ftrace_direct_func_count; + +@@ -2555,39 +2554,6 @@ unsigned long ftrace_find_rec_direct(uns + return entry->direct; + } + +-static struct ftrace_func_entry* +-ftrace_add_rec_direct(unsigned long ip, unsigned long addr, +- struct ftrace_hash **free_hash) +-{ +- struct ftrace_func_entry *entry; +- +- if (ftrace_hash_empty(direct_functions) || +- direct_functions->count > 2 * (1 << direct_functions->size_bits)) { +- struct ftrace_hash *new_hash; +- int size = ftrace_hash_empty(direct_functions) ? 0 : +- direct_functions->count + 1; +- +- if (size < 32) +- size = 32; +- +- new_hash = dup_hash(direct_functions, size); +- if (!new_hash) +- return NULL; +- +- *free_hash = direct_functions; +- direct_functions = new_hash; +- } +- +- entry = kmalloc(sizeof(*entry), GFP_KERNEL); +- if (!entry) +- return NULL; +- +- entry->ip = ip; +- entry->direct = addr; +- __add_hash_entry(direct_functions, entry); +- return entry; +-} +- + static void call_direct_funcs(unsigned long ip, unsigned long pip, + struct ftrace_ops *ops, struct ftrace_regs *fregs) + { +@@ -4223,8 +4189,8 @@ enter_record(struct ftrace_hash *hash, s + /* Do nothing if it exists */ + if (entry) + return 0; +- +- ret = add_hash_entry(hash, rec->ip); ++ if (add_hash_entry(hash, rec->ip) == NULL) ++ ret = -ENOMEM; + } + return ret; + } +@@ -5266,7 +5232,8 @@ __ftrace_match_addr(struct ftrace_hash * + return 0; + } + +- return add_hash_entry(hash, ip); ++ entry = add_hash_entry(hash, ip); ++ return entry ? 0 : -ENOMEM; + } + + static int +@@ -5410,7 +5377,7 @@ static void remove_direct_functions_hash + */ + int register_ftrace_direct(struct ftrace_ops *ops, unsigned long addr) + { +- struct ftrace_hash *hash, *free_hash = NULL; ++ struct ftrace_hash *hash, *new_hash = NULL, *free_hash = NULL; + struct ftrace_func_entry *entry, *new; + int err = -EBUSY, size, i; + +@@ -5436,17 +5403,44 @@ int register_ftrace_direct(struct ftrace + } + } + +- /* ... and insert them to direct_functions hash. */ + err = -ENOMEM; ++ ++ /* Make a copy hash to place the new and the old entries in */ ++ size = hash->count + direct_functions->count; ++ if (size > 32) ++ size = 32; ++ new_hash = alloc_ftrace_hash(fls(size)); ++ if (!new_hash) ++ goto out_unlock; ++ ++ /* Now copy over the existing direct entries */ ++ size = 1 << direct_functions->size_bits; ++ for (i = 0; i < size; i++) { ++ hlist_for_each_entry(entry, &direct_functions->buckets[i], hlist) { ++ new = add_hash_entry(new_hash, entry->ip); ++ if (!new) ++ goto out_unlock; ++ new->direct = entry->direct; ++ } ++ } ++ ++ /* ... and add the new entries */ ++ size = 1 << hash->size_bits; + for (i = 0; i < size; i++) { + hlist_for_each_entry(entry, &hash->buckets[i], hlist) { +- new = ftrace_add_rec_direct(entry->ip, addr, &free_hash); ++ new = add_hash_entry(new_hash, entry->ip); + if (!new) +- goto out_remove; ++ goto out_unlock; ++ /* Update both the copy and the hash entry */ ++ new->direct = addr; + entry->direct = addr; + } + } + ++ free_hash = direct_functions; ++ rcu_assign_pointer(direct_functions, new_hash); ++ new_hash = NULL; ++ + ops->func = call_direct_funcs; + ops->flags = MULTI_FLAGS; + ops->trampoline = FTRACE_REGS_ADDR; +@@ -5454,17 +5448,17 @@ int register_ftrace_direct(struct ftrace + + err = register_ftrace_function_nolock(ops); + +- out_remove: +- if (err) +- remove_direct_functions_hash(hash, addr); +- + out_unlock: + mutex_unlock(&direct_mutex); + +- if (free_hash) { ++ if (free_hash && free_hash != EMPTY_HASH) { + synchronize_rcu_tasks(); + free_ftrace_hash(free_hash); + } ++ ++ if (new_hash) ++ free_ftrace_hash(new_hash); ++ + return err; + } + EXPORT_SYMBOL_GPL(register_ftrace_direct); +@@ -6309,7 +6303,7 @@ ftrace_graph_set_hash(struct ftrace_hash + + if (entry) + continue; +- if (add_hash_entry(hash, rec->ip) < 0) ++ if (add_hash_entry(hash, rec->ip) == NULL) + goto out; + } else { + if (entry) { diff --git a/queue-6.6/ring-buffer-fix-wake-ups-when-buffer_percent-is-set-to-100.patch b/queue-6.6/ring-buffer-fix-wake-ups-when-buffer_percent-is-set-to-100.patch new file mode 100644 index 00000000000..f995d2610ef --- /dev/null +++ b/queue-6.6/ring-buffer-fix-wake-ups-when-buffer_percent-is-set-to-100.patch @@ -0,0 +1,73 @@ +From 623b1f896fa8a669a277ee5a258307a16c7377a3 Mon Sep 17 00:00:00 2001 +From: "Steven Rostedt (Google)" +Date: Tue, 26 Dec 2023 12:59:02 -0500 +Subject: ring-buffer: Fix wake ups when buffer_percent is set to 100 + +From: Steven Rostedt (Google) + +commit 623b1f896fa8a669a277ee5a258307a16c7377a3 upstream. + +The tracefs file "buffer_percent" is to allow user space to set a +water-mark on how much of the tracing ring buffer needs to be filled in +order to wake up a blocked reader. + + 0 - is to wait until any data is in the buffer + 1 - is to wait for 1% of the sub buffers to be filled + 50 - would be half of the sub buffers are filled with data + 100 - is not to wake the waiter until the ring buffer is completely full + +Unfortunately the test for being full was: + + dirty = ring_buffer_nr_dirty_pages(buffer, cpu); + return (dirty * 100) > (full * nr_pages); + +Where "full" is the value for "buffer_percent". + +There is two issues with the above when full == 100. + +1. dirty * 100 > 100 * nr_pages will never be true + That is, the above is basically saying that if the user sets + buffer_percent to 100, more pages need to be dirty than exist in the + ring buffer! + +2. The page that the writer is on is never considered dirty, as dirty + pages are only those that are full. When the writer goes to a new + sub-buffer, it clears the contents of that sub-buffer. + +That is, even if the check was ">=" it would still not be equal as the +most pages that can be considered "dirty" is nr_pages - 1. + +To fix this, add one to dirty and use ">=" in the compare. + +Link: https://lore.kernel.org/linux-trace-kernel/20231226125902.4a057f1d@gandalf.local.home + +Cc: stable@vger.kernel.org +Cc: Mark Rutland +Cc: Mathieu Desnoyers +Acked-by: Masami Hiramatsu (Google) +Fixes: 03329f9939781 ("tracing: Add tracefs file buffer_percentage") +Signed-off-by: Steven Rostedt (Google) +Signed-off-by: Greg Kroah-Hartman +--- + kernel/trace/ring_buffer.c | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +--- a/kernel/trace/ring_buffer.c ++++ b/kernel/trace/ring_buffer.c +@@ -881,9 +881,14 @@ static __always_inline bool full_hit(str + if (!nr_pages || !full) + return true; + +- dirty = ring_buffer_nr_dirty_pages(buffer, cpu); ++ /* ++ * Add one as dirty will never equal nr_pages, as the sub-buffer ++ * that the writer is on is not counted as dirty. ++ * This is needed if "buffer_percent" is set to 100. ++ */ ++ dirty = ring_buffer_nr_dirty_pages(buffer, cpu) + 1; + +- return (dirty * 100) > (full * nr_pages); ++ return (dirty * 100) >= (full * nr_pages); + } + + /* diff --git a/queue-6.6/series b/queue-6.6/series index da29cdeb857..fce2f08781d 100644 --- a/queue-6.6/series +++ b/queue-6.6/series @@ -41,3 +41,8 @@ mm-migrate-high-order-folios-in-swap-cache-correctly.patch mm-memory-failure-cast-index-to-loff_t-before-shifting-it.patch mm-memory-failure-check-the-mapcount-of-the-precise-page.patch revert-nvme-fc-fix-race-between-error-recovery-and-creating-association.patch +ring-buffer-fix-wake-ups-when-buffer_percent-is-set-to-100.patch +ftrace-fix-modification-of-direct_function-hash-while-in-use.patch +tracing-fix-blocked-reader-of-snapshot-buffer.patch +wifi-cfg80211-fix-cqm-for-non-range-use.patch +wifi-nl80211-fix-deadlock-in-nl80211_set_cqm_rssi-6.6.x.patch diff --git a/queue-6.6/tracing-fix-blocked-reader-of-snapshot-buffer.patch b/queue-6.6/tracing-fix-blocked-reader-of-snapshot-buffer.patch new file mode 100644 index 00000000000..2e4e512c085 --- /dev/null +++ b/queue-6.6/tracing-fix-blocked-reader-of-snapshot-buffer.patch @@ -0,0 +1,105 @@ +From 39a7dc23a1ed0fe81141792a09449d124c5953bd Mon Sep 17 00:00:00 2001 +From: "Steven Rostedt (Google)" +Date: Thu, 28 Dec 2023 09:51:49 -0500 +Subject: tracing: Fix blocked reader of snapshot buffer + +From: Steven Rostedt (Google) + +commit 39a7dc23a1ed0fe81141792a09449d124c5953bd upstream. + +If an application blocks on the snapshot or snapshot_raw files, expecting +to be woken up when a snapshot occurs, it will not happen. Or it may +happen with an unexpected result. + +That result is that the application will be reading the main buffer +instead of the snapshot buffer. That is because when the snapshot occurs, +the main and snapshot buffers are swapped. But the reader has a descriptor +still pointing to the buffer that it originally connected to. + +This is fine for the main buffer readers, as they may be blocked waiting +for a watermark to be hit, and when a snapshot occurs, the data that the +main readers want is now on the snapshot buffer. + +But for waiters of the snapshot buffer, they are waiting for an event to +occur that will trigger the snapshot and they can then consume it quickly +to save the snapshot before the next snapshot occurs. But to do this, they +need to read the new snapshot buffer, not the old one that is now +receiving new data. + +Also, it does not make sense to have a watermark "buffer_percent" on the +snapshot buffer, as the snapshot buffer is static and does not receive new +data except all at once. + +Link: https://lore.kernel.org/linux-trace-kernel/20231228095149.77f5b45d@gandalf.local.home + +Cc: stable@vger.kernel.org +Cc: Mathieu Desnoyers +Cc: Mark Rutland +Acked-by: Masami Hiramatsu (Google) +Fixes: debdd57f5145f ("tracing: Make a snapshot feature available from userspace") +Signed-off-by: Steven Rostedt (Google) +Signed-off-by: Greg Kroah-Hartman +--- + kernel/trace/ring_buffer.c | 3 ++- + kernel/trace/trace.c | 20 +++++++++++++++++--- + 2 files changed, 19 insertions(+), 4 deletions(-) + +--- a/kernel/trace/ring_buffer.c ++++ b/kernel/trace/ring_buffer.c +@@ -949,7 +949,8 @@ void ring_buffer_wake_waiters(struct tra + /* make sure the waiters see the new index */ + smp_wmb(); + +- rb_wake_up_waiters(&rbwork->work); ++ /* This can be called in any context */ ++ irq_work_queue(&rbwork->work); + } + + /** +--- a/kernel/trace/trace.c ++++ b/kernel/trace/trace.c +@@ -1893,6 +1893,9 @@ update_max_tr(struct trace_array *tr, st + __update_max_tr(tr, tsk, cpu); + + arch_spin_unlock(&tr->max_lock); ++ ++ /* Any waiters on the old snapshot buffer need to wake up */ ++ ring_buffer_wake_waiters(tr->array_buffer.buffer, RING_BUFFER_ALL_CPUS); + } + + /** +@@ -1944,12 +1947,23 @@ update_max_tr_single(struct trace_array + + static int wait_on_pipe(struct trace_iterator *iter, int full) + { ++ int ret; ++ + /* Iterators are static, they should be filled or empty */ + if (trace_buffer_iter(iter, iter->cpu_file)) + return 0; + +- return ring_buffer_wait(iter->array_buffer->buffer, iter->cpu_file, +- full); ++ ret = ring_buffer_wait(iter->array_buffer->buffer, iter->cpu_file, full); ++ ++#ifdef CONFIG_TRACER_MAX_TRACE ++ /* ++ * Make sure this is still the snapshot buffer, as if a snapshot were ++ * to happen, this would now be the main buffer. ++ */ ++ if (iter->snapshot) ++ iter->array_buffer = &iter->tr->max_buffer; ++#endif ++ return ret; + } + + #ifdef CONFIG_FTRACE_STARTUP_TEST +@@ -8514,7 +8528,7 @@ tracing_buffers_splice_read(struct file + + wait_index = READ_ONCE(iter->wait_index); + +- ret = wait_on_pipe(iter, iter->tr->buffer_percent); ++ ret = wait_on_pipe(iter, iter->snapshot ? 0 : iter->tr->buffer_percent); + if (ret) + goto out; + diff --git a/queue-6.6/wifi-cfg80211-fix-cqm-for-non-range-use.patch b/queue-6.6/wifi-cfg80211-fix-cqm-for-non-range-use.patch new file mode 100644 index 00000000000..4b6d1c40448 --- /dev/null +++ b/queue-6.6/wifi-cfg80211-fix-cqm-for-non-range-use.patch @@ -0,0 +1,151 @@ +From leo@leolam.fr Sat Dec 16 06:48:17 2023 +From: "Léo Lam" +Date: Sat, 16 Dec 2023 05:47:15 +0000 +Subject: wifi: cfg80211: fix CQM for non-range use +To: stable@vger.kernel.org +Cc: "Johannes Berg" , "Greg Kroah-Hartman" , "Léo Lam" +Message-ID: <20231216054715.7729-2-leo@leolam.fr> + +From: Johannes Berg + +commit 7e7efdda6adb385fbdfd6f819d76bc68c923c394 upstream. + +[note: this is commit 4a7e92551618f3737b305f62451353ee05662f57 reapplied; +that commit had been reverted in 6.6.6 because it caused regressions, see +https://lore.kernel.org/stable/2023121450-habitual-transpose-68a1@gregkh/ +for details] + +My prior race fix here broke CQM when ranges aren't used, as +the reporting worker now requires the cqm_config to be set in +the wdev, but isn't set when there's no range configured. + +Rather than continuing to special-case the range version, set +the cqm_config always and configure accordingly, also tracking +if range was used or not to be able to clear the configuration +appropriately with the same API, which was actually not right +if both were implemented by a driver for some reason, as is +the case with mac80211 (though there the implementations are +equivalent so it doesn't matter.) + +Also, the original multiple-RSSI commit lost checking for the +callback, so might have potentially crashed if a driver had +neither implementation, and userspace tried to use it despite +not being advertised as supported. + +Cc: stable@vger.kernel.org +Fixes: 4a4b8169501b ("cfg80211: Accept multiple RSSI thresholds for CQM") +Fixes: 37c20b2effe9 ("wifi: cfg80211: fix cqm_config access race") +Signed-off-by: Johannes Berg +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: "Léo Lam" +Signed-off-by: Greg Kroah-Hartman +--- + net/wireless/core.h | 1 + net/wireless/nl80211.c | 50 ++++++++++++++++++++++++++++++------------------- + 2 files changed, 32 insertions(+), 19 deletions(-) + +--- a/net/wireless/core.h ++++ b/net/wireless/core.h +@@ -299,6 +299,7 @@ struct cfg80211_cqm_config { + u32 rssi_hyst; + s32 last_rssi_event_value; + enum nl80211_cqm_rssi_threshold_event last_rssi_event_type; ++ bool use_range_api; + int n_rssi_thresholds; + s32 rssi_thresholds[] __counted_by(n_rssi_thresholds); + }; +--- a/net/wireless/nl80211.c ++++ b/net/wireless/nl80211.c +@@ -12824,10 +12824,6 @@ static int cfg80211_cqm_rssi_update(stru + int i, n, low_index; + int err; + +- /* RSSI reporting disabled? */ +- if (!cqm_config) +- return rdev_set_cqm_rssi_range_config(rdev, dev, 0, 0); +- + /* + * Obtain current RSSI value if possible, if not and no RSSI threshold + * event has been received yet, we should receive an event after a +@@ -12902,18 +12898,6 @@ static int nl80211_set_cqm_rssi(struct g + wdev->iftype != NL80211_IFTYPE_P2P_CLIENT) + return -EOPNOTSUPP; + +- if (n_thresholds <= 1 && rdev->ops->set_cqm_rssi_config) { +- if (n_thresholds == 0 || thresholds[0] == 0) /* Disabling */ +- return rdev_set_cqm_rssi_config(rdev, dev, 0, 0); +- +- return rdev_set_cqm_rssi_config(rdev, dev, +- thresholds[0], hysteresis); +- } +- +- if (!wiphy_ext_feature_isset(&rdev->wiphy, +- NL80211_EXT_FEATURE_CQM_RSSI_LIST)) +- return -EOPNOTSUPP; +- + if (n_thresholds == 1 && thresholds[0] == 0) /* Disabling */ + n_thresholds = 0; + +@@ -12921,6 +12905,20 @@ static int nl80211_set_cqm_rssi(struct g + old = rcu_dereference_protected(wdev->cqm_config, + lockdep_is_held(&wdev->mtx)); + ++ /* if already disabled just succeed */ ++ if (!n_thresholds && !old) ++ return 0; ++ ++ if (n_thresholds > 1) { ++ if (!wiphy_ext_feature_isset(&rdev->wiphy, ++ NL80211_EXT_FEATURE_CQM_RSSI_LIST) || ++ !rdev->ops->set_cqm_rssi_range_config) ++ return -EOPNOTSUPP; ++ } else { ++ if (!rdev->ops->set_cqm_rssi_config) ++ return -EOPNOTSUPP; ++ } ++ + if (n_thresholds) { + cqm_config = kzalloc(struct_size(cqm_config, rssi_thresholds, + n_thresholds), +@@ -12935,13 +12933,26 @@ static int nl80211_set_cqm_rssi(struct g + memcpy(cqm_config->rssi_thresholds, thresholds, + flex_array_size(cqm_config, rssi_thresholds, + n_thresholds)); ++ cqm_config->use_range_api = n_thresholds > 1 || ++ !rdev->ops->set_cqm_rssi_config; + + rcu_assign_pointer(wdev->cqm_config, cqm_config); ++ ++ if (cqm_config->use_range_api) ++ err = cfg80211_cqm_rssi_update(rdev, dev, cqm_config); ++ else ++ err = rdev_set_cqm_rssi_config(rdev, dev, ++ thresholds[0], ++ hysteresis); + } else { + RCU_INIT_POINTER(wdev->cqm_config, NULL); ++ /* if enabled as range also disable via range */ ++ if (old->use_range_api) ++ err = rdev_set_cqm_rssi_range_config(rdev, dev, 0, 0); ++ else ++ err = rdev_set_cqm_rssi_config(rdev, dev, 0, 0); + } + +- err = cfg80211_cqm_rssi_update(rdev, dev, cqm_config); + if (err) { + rcu_assign_pointer(wdev->cqm_config, old); + kfree_rcu(cqm_config, rcu_head); +@@ -19131,10 +19142,11 @@ void cfg80211_cqm_rssi_notify_work(struc + wdev_lock(wdev); + cqm_config = rcu_dereference_protected(wdev->cqm_config, + lockdep_is_held(&wdev->mtx)); +- if (!wdev->cqm_config) ++ if (!cqm_config) + goto unlock; + +- cfg80211_cqm_rssi_update(rdev, wdev->netdev, cqm_config); ++ if (cqm_config->use_range_api) ++ cfg80211_cqm_rssi_update(rdev, wdev->netdev, cqm_config); + + rssi_level = cqm_config->last_rssi_event_value; + rssi_event = cqm_config->last_rssi_event_type; diff --git a/queue-6.6/wifi-nl80211-fix-deadlock-in-nl80211_set_cqm_rssi-6.6.x.patch b/queue-6.6/wifi-nl80211-fix-deadlock-in-nl80211_set_cqm_rssi-6.6.x.patch new file mode 100644 index 00000000000..1e4ebe85a95 --- /dev/null +++ b/queue-6.6/wifi-nl80211-fix-deadlock-in-nl80211_set_cqm_rssi-6.6.x.patch @@ -0,0 +1,80 @@ +From stable+bounces-6868-greg=kroah.com@vger.kernel.org Sat Dec 16 06:57:02 2023 +From: "Léo Lam" +Date: Sat, 16 Dec 2023 05:47:17 +0000 +Subject: wifi: nl80211: fix deadlock in nl80211_set_cqm_rssi (6.6.x) +To: stable@vger.kernel.org +Cc: "Léo Lam" , "Philip Müller" , "Johannes Berg" +Message-ID: <20231216054715.7729-4-leo@leolam.fr> + +From: "Léo Lam" + +Commit 008afb9f3d57 ("wifi: cfg80211: fix CQM for non-range use" +backported to 6.6.x) causes nl80211_set_cqm_rssi not to release the +wdev lock in some of the error paths. + +Of course, the ensuing deadlock causes userland network managers to +break pretty badly, and on typical systems this also causes lockups on +on suspend, poweroff and reboot. See [1], [2], [3] for example reports. + +The upstream commit 7e7efdda6adb ("wifi: cfg80211: fix CQM for non-range +use"), committed in November 2023, is completely fine because there was +another commit in August 2023 that removed the wdev lock: +see commit 076fc8775daf ("wifi: cfg80211: remove wdev mutex"). + +The reason things broke in 6.6.5 is that commit 4338058f6009 was applied +without also applying 076fc8775daf. + +Commit 076fc8775daf ("wifi: cfg80211: remove wdev mutex") is a rather +large commit; adjusting the error handling (which is what this commit does) +yields a much simpler patch and was tested to work properly. + +Fix the deadlock by releasing the lock before returning. + +[1] https://bugzilla.kernel.org/show_bug.cgi?id=218247 +[2] https://bbs.archlinux.org/viewtopic.php?id=290976 +[3] https://lore.kernel.org/all/87sf4belmm.fsf@turtle.gmx.de/ + +Link: https://lore.kernel.org/stable/e374bb16-5b13-44cc-b11a-2f4eefb1ecf5@manjaro.org/ +Fixes: 008afb9f3d57 ("wifi: cfg80211: fix CQM for non-range use") +Tested-by: "Léo Lam" +Tested-by: Philip Müller +Cc: stable@vger.kernel.org +Cc: Johannes Berg +Signed-off-by: "Léo Lam" +Signed-off-by: Greg Kroah-Hartman +--- + net/wireless/nl80211.c | 18 ++++++++++++------ + 1 file changed, 12 insertions(+), 6 deletions(-) + +--- a/net/wireless/nl80211.c ++++ b/net/wireless/nl80211.c +@@ -12906,17 +12906,23 @@ static int nl80211_set_cqm_rssi(struct g + lockdep_is_held(&wdev->mtx)); + + /* if already disabled just succeed */ +- if (!n_thresholds && !old) +- return 0; ++ if (!n_thresholds && !old) { ++ err = 0; ++ goto unlock; ++ } + + if (n_thresholds > 1) { + if (!wiphy_ext_feature_isset(&rdev->wiphy, + NL80211_EXT_FEATURE_CQM_RSSI_LIST) || +- !rdev->ops->set_cqm_rssi_range_config) +- return -EOPNOTSUPP; ++ !rdev->ops->set_cqm_rssi_range_config) { ++ err = -EOPNOTSUPP; ++ goto unlock; ++ } + } else { +- if (!rdev->ops->set_cqm_rssi_config) +- return -EOPNOTSUPP; ++ if (!rdev->ops->set_cqm_rssi_config) { ++ err = -EOPNOTSUPP; ++ goto unlock; ++ } + } + + if (n_thresholds) {