Skip to content

Commit

Permalink
Merge tag 'wireless-2023-11-29' of git://git.kernel.org/pub/scm/linux…
Browse files Browse the repository at this point in the history
…/kernel/git/wireless/wireless

Johannes Berg says:

====================
wireless fixes:
 - debugfs had a deadlock (removal vs. use of files),
   fixes going through wireless ACKed by Greg
 - support for HT STAs on 320 MHz channels, even if it's
   not clear that should ever happen (that's 6 GHz), best
   not to WARN()
 - fix for the previous CQM fix that broke most cases
 - various wiphy locking fixes
 - various small driver fixes

* tag 'wireless-2023-11-29' of git://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless:
  wifi: mac80211: use wiphy locked debugfs for sdata/link
  wifi: mac80211: use wiphy locked debugfs helpers for agg_status
  wifi: cfg80211: add locked debugfs wrappers
  debugfs: add API to allow debugfs operations cancellation
  debugfs: annotate debugfs handlers vs. removal with lockdep
  debugfs: fix automount d_fsdata usage
  wifi: mac80211: handle 320 MHz in ieee80211_ht_cap_ie_to_sta_ht_cap
  wifi: avoid offset calculation on NULL pointer
  wifi: cfg80211: hold wiphy mutex for send_interface
  wifi: cfg80211: lock wiphy mutex for rfkill poll
  wifi: cfg80211: fix CQM for non-range use
  wifi: mac80211: do not pass AP_VLAN vif pointer to drivers during flush
  wifi: iwlwifi: mvm: fix an error code in iwl_mvm_mld_add_sta()
  wifi: mt76: mt7925: fix typo in mt7925_init_he_caps
  wifi: mt76: mt7921: fix 6GHz disabled by the missing default CLC config
====================

Link: https://lore.kernel.org/r/20231129150809.31083-3-johannes@sipsolutions.net
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
  • Loading branch information
kuba-moo committed Nov 30, 2023
2 parents 0d47fa5 + 4ded3bf commit 300fbb2
Show file tree
Hide file tree
Showing 19 changed files with 614 additions and 118 deletions.
4 changes: 1 addition & 3 deletions drivers/net/wireless/ath/ath9k/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,7 @@ config ATH9K_AHB

config ATH9K_DEBUGFS
bool "Atheros ath9k debugging"
depends on ATH9K && DEBUG_FS
select MAC80211_DEBUGFS
depends on ATH9K && DEBUG_FS && MAC80211_DEBUGFS
select ATH9K_COMMON_DEBUG
help
Say Y, if you need access to ath9k's statistics for
Expand All @@ -70,7 +69,6 @@ config ATH9K_DEBUGFS
config ATH9K_STATION_STATISTICS
bool "Detailed station statistics"
depends on ATH9K && ATH9K_DEBUGFS && DEBUG_FS
select MAC80211_DEBUGFS
default n
help
This option enables detailed statistics for association stations.
Expand Down
4 changes: 3 additions & 1 deletion drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c
Original file line number Diff line number Diff line change
Expand Up @@ -707,8 +707,10 @@ int iwl_mvm_mld_add_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
rcu_dereference_protected(mvm_sta->link[link_id],
lockdep_is_held(&mvm->mutex));

if (WARN_ON(!link_conf || !mvm_link_sta))
if (WARN_ON(!link_conf || !mvm_link_sta)) {
ret = -EINVAL;
goto err;
}

ret = iwl_mvm_mld_cfg_sta(mvm, sta, vif, link_sta, link_conf,
mvm_link_sta);
Expand Down
1 change: 1 addition & 0 deletions drivers/net/wireless/mediatek/mt76/mt7921/mcu.c
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,7 @@ static int mt7921_load_clc(struct mt792x_dev *dev, const char *fw_name)
int ret, i, len, offset = 0;
u8 *clc_base = NULL, hw_encap = 0;

dev->phy.clc_chan_conf = 0xff;
if (mt7921_disable_clc ||
mt76_is_usb(&dev->mt76))
return 0;
Expand Down
4 changes: 2 additions & 2 deletions drivers/net/wireless/mediatek/mt76/mt7925/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
static void
mt7925_init_he_caps(struct mt792x_phy *phy, enum nl80211_band band,
struct ieee80211_sband_iftype_data *data,
enum nl80211_iftype iftype)
enum nl80211_iftype iftype)
{
struct ieee80211_sta_he_cap *he_cap = &data->he_cap;
struct ieee80211_he_cap_elem *he_cap_elem = &he_cap->he_cap_elem;
Expand Down Expand Up @@ -53,7 +53,7 @@ mt7925_init_he_caps(struct mt792x_phy *phy, enum nl80211_band band,
IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO |
IEEE80211_HE_PHY_CAP2_UL_MU_PARTIAL_MU_MIMO;

switch (i) {
switch (iftype) {
case NL80211_IFTYPE_AP:
he_cap_elem->mac_cap_info[2] |=
IEEE80211_HE_MAC_CAP2_BSR;
Expand Down
100 changes: 100 additions & 0 deletions fs/debugfs/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,14 @@ int debugfs_file_get(struct dentry *dentry)
struct debugfs_fsdata *fsd;
void *d_fsd;

/*
* This could only happen if some debugfs user erroneously calls
* debugfs_file_get() on a dentry that isn't even a file, let
* them know about it.
*/
if (WARN_ON(!d_is_reg(dentry)))
return -EINVAL;

d_fsd = READ_ONCE(dentry->d_fsdata);
if (!((unsigned long)d_fsd & DEBUGFS_FSDATA_IS_REAL_FOPS_BIT)) {
fsd = d_fsd;
Expand All @@ -100,6 +108,14 @@ int debugfs_file_get(struct dentry *dentry)
kfree(fsd);
fsd = READ_ONCE(dentry->d_fsdata);
}
#ifdef CONFIG_LOCKDEP
fsd->lock_name = kasprintf(GFP_KERNEL, "debugfs:%pd", dentry);
lockdep_register_key(&fsd->key);
lockdep_init_map(&fsd->lockdep_map, fsd->lock_name ?: "debugfs",
&fsd->key, 0);
#endif
INIT_LIST_HEAD(&fsd->cancellations);
mutex_init(&fsd->cancellations_mtx);
}

/*
Expand All @@ -116,6 +132,8 @@ int debugfs_file_get(struct dentry *dentry)
if (!refcount_inc_not_zero(&fsd->active_users))
return -EIO;

lock_map_acquire_read(&fsd->lockdep_map);

return 0;
}
EXPORT_SYMBOL_GPL(debugfs_file_get);
Expand All @@ -133,11 +151,93 @@ void debugfs_file_put(struct dentry *dentry)
{
struct debugfs_fsdata *fsd = READ_ONCE(dentry->d_fsdata);

lock_map_release(&fsd->lockdep_map);

if (refcount_dec_and_test(&fsd->active_users))
complete(&fsd->active_users_drained);
}
EXPORT_SYMBOL_GPL(debugfs_file_put);

/**
* debugfs_enter_cancellation - enter a debugfs cancellation
* @file: the file being accessed
* @cancellation: the cancellation object, the cancel callback
* inside of it must be initialized
*
* When a debugfs file is removed it needs to wait for all active
* operations to complete. However, the operation itself may need
* to wait for hardware or completion of some asynchronous process
* or similar. As such, it may need to be cancelled to avoid long
* waits or even deadlocks.
*
* This function can be used inside a debugfs handler that may
* need to be cancelled. As soon as this function is called, the
* cancellation's 'cancel' callback may be called, at which point
* the caller should proceed to call debugfs_leave_cancellation()
* and leave the debugfs handler function as soon as possible.
* Note that the 'cancel' callback is only ever called in the
* context of some kind of debugfs_remove().
*
* This function must be paired with debugfs_leave_cancellation().
*/
void debugfs_enter_cancellation(struct file *file,
struct debugfs_cancellation *cancellation)
{
struct debugfs_fsdata *fsd;
struct dentry *dentry = F_DENTRY(file);

INIT_LIST_HEAD(&cancellation->list);

if (WARN_ON(!d_is_reg(dentry)))
return;

if (WARN_ON(!cancellation->cancel))
return;

fsd = READ_ONCE(dentry->d_fsdata);
if (WARN_ON(!fsd ||
((unsigned long)fsd & DEBUGFS_FSDATA_IS_REAL_FOPS_BIT)))
return;

mutex_lock(&fsd->cancellations_mtx);
list_add(&cancellation->list, &fsd->cancellations);
mutex_unlock(&fsd->cancellations_mtx);

/* if we're already removing wake it up to cancel */
if (d_unlinked(dentry))
complete(&fsd->active_users_drained);
}
EXPORT_SYMBOL_GPL(debugfs_enter_cancellation);

/**
* debugfs_leave_cancellation - leave cancellation section
* @file: the file being accessed
* @cancellation: the cancellation previously registered with
* debugfs_enter_cancellation()
*
* See the documentation of debugfs_enter_cancellation().
*/
void debugfs_leave_cancellation(struct file *file,
struct debugfs_cancellation *cancellation)
{
struct debugfs_fsdata *fsd;
struct dentry *dentry = F_DENTRY(file);

if (WARN_ON(!d_is_reg(dentry)))
return;

fsd = READ_ONCE(dentry->d_fsdata);
if (WARN_ON(!fsd ||
((unsigned long)fsd & DEBUGFS_FSDATA_IS_REAL_FOPS_BIT)))
return;

mutex_lock(&fsd->cancellations_mtx);
if (!list_empty(&cancellation->list))
list_del(&cancellation->list);
mutex_unlock(&fsd->cancellations_mtx);
}
EXPORT_SYMBOL_GPL(debugfs_leave_cancellation);

/*
* Only permit access to world-readable files when the kernel is locked down.
* We also need to exclude any file that has ways to write or alter it as root
Expand Down
71 changes: 63 additions & 8 deletions fs/debugfs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -236,17 +236,29 @@ static const struct super_operations debugfs_super_operations = {

static void debugfs_release_dentry(struct dentry *dentry)
{
void *fsd = dentry->d_fsdata;
struct debugfs_fsdata *fsd = dentry->d_fsdata;

if (!((unsigned long)fsd & DEBUGFS_FSDATA_IS_REAL_FOPS_BIT))
kfree(dentry->d_fsdata);
if ((unsigned long)fsd & DEBUGFS_FSDATA_IS_REAL_FOPS_BIT)
return;

/* check it wasn't a dir (no fsdata) or automount (no real_fops) */
if (fsd && fsd->real_fops) {
#ifdef CONFIG_LOCKDEP
lockdep_unregister_key(&fsd->key);
kfree(fsd->lock_name);
#endif
WARN_ON(!list_empty(&fsd->cancellations));
mutex_destroy(&fsd->cancellations_mtx);
}

kfree(fsd);
}

static struct vfsmount *debugfs_automount(struct path *path)
{
debugfs_automount_t f;
f = (debugfs_automount_t)path->dentry->d_fsdata;
return f(path->dentry, d_inode(path->dentry)->i_private);
struct debugfs_fsdata *fsd = path->dentry->d_fsdata;

return fsd->automount(path->dentry, d_inode(path->dentry)->i_private);
}

static const struct dentry_operations debugfs_dops = {
Expand Down Expand Up @@ -634,27 +646,38 @@ struct dentry *debugfs_create_automount(const char *name,
void *data)
{
struct dentry *dentry = start_creating(name, parent);
struct debugfs_fsdata *fsd;
struct inode *inode;

if (IS_ERR(dentry))
return dentry;

fsd = kzalloc(sizeof(*fsd), GFP_KERNEL);
if (!fsd) {
failed_creating(dentry);
return ERR_PTR(-ENOMEM);
}

fsd->automount = f;

if (!(debugfs_allow & DEBUGFS_ALLOW_API)) {
failed_creating(dentry);
kfree(fsd);
return ERR_PTR(-EPERM);
}

inode = debugfs_get_inode(dentry->d_sb);
if (unlikely(!inode)) {
pr_err("out of free dentries, can not create automount '%s'\n",
name);
kfree(fsd);
return failed_creating(dentry);
}

make_empty_dir_inode(inode);
inode->i_flags |= S_AUTOMOUNT;
inode->i_private = data;
dentry->d_fsdata = (void *)f;
dentry->d_fsdata = fsd;
/* directory inodes start off with i_nlink == 2 (for "." entry) */
inc_nlink(inode);
d_instantiate(dentry, inode);
Expand Down Expand Up @@ -731,8 +754,40 @@ static void __debugfs_file_removed(struct dentry *dentry)
fsd = READ_ONCE(dentry->d_fsdata);
if ((unsigned long)fsd & DEBUGFS_FSDATA_IS_REAL_FOPS_BIT)
return;
if (!refcount_dec_and_test(&fsd->active_users))

lock_map_acquire(&fsd->lockdep_map);
lock_map_release(&fsd->lockdep_map);

/* if we hit zero, just wait for all to finish */
if (!refcount_dec_and_test(&fsd->active_users)) {
wait_for_completion(&fsd->active_users_drained);
return;
}

/* if we didn't hit zero, try to cancel any we can */
while (refcount_read(&fsd->active_users)) {
struct debugfs_cancellation *c;

/*
* Lock the cancellations. Note that the cancellations
* structs are meant to be on the stack, so we need to
* ensure we either use them here or don't touch them,
* and debugfs_leave_cancellation() will wait for this
* to be finished processing before exiting one. It may
* of course win and remove the cancellation, but then
* chances are we never even got into this bit, we only
* do if the refcount isn't zero already.
*/
mutex_lock(&fsd->cancellations_mtx);
while ((c = list_first_entry_or_null(&fsd->cancellations,
typeof(*c), list))) {
list_del_init(&c->list);
c->cancel(dentry, c->cancel_data);
}
mutex_unlock(&fsd->cancellations_mtx);

wait_for_completion(&fsd->active_users_drained);
}
}

static void remove_one(struct dentry *victim)
Expand Down
21 changes: 19 additions & 2 deletions fs/debugfs/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

#ifndef _DEBUGFS_INTERNAL_H_
#define _DEBUGFS_INTERNAL_H_
#include <linux/lockdep.h>
#include <linux/list.h>

struct file_operations;

Expand All @@ -17,8 +19,23 @@ extern const struct file_operations debugfs_full_proxy_file_operations;

struct debugfs_fsdata {
const struct file_operations *real_fops;
refcount_t active_users;
struct completion active_users_drained;
union {
/* automount_fn is used when real_fops is NULL */
debugfs_automount_t automount;
struct {
refcount_t active_users;
struct completion active_users_drained;
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
struct lock_class_key key;
char *lock_name;
#endif

/* protect cancellations */
struct mutex cancellations_mtx;
struct list_head cancellations;
};
};
};

/*
Expand Down
19 changes: 19 additions & 0 deletions include/linux/debugfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,25 @@ ssize_t debugfs_write_file_bool(struct file *file, const char __user *user_buf,
ssize_t debugfs_read_file_str(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos);

/**
* struct debugfs_cancellation - cancellation data
* @list: internal, for keeping track
* @cancel: callback to call
* @cancel_data: extra data for the callback to call
*/
struct debugfs_cancellation {
struct list_head list;
void (*cancel)(struct dentry *, void *);
void *cancel_data;
};

void __acquires(cancellation)
debugfs_enter_cancellation(struct file *file,
struct debugfs_cancellation *cancellation);
void __releases(cancellation)
debugfs_leave_cancellation(struct file *file,
struct debugfs_cancellation *cancellation);

#else

#include <linux/err.h>
Expand Down
4 changes: 3 additions & 1 deletion include/linux/ieee80211.h
Original file line number Diff line number Diff line change
Expand Up @@ -2830,12 +2830,14 @@ ieee80211_he_oper_size(const u8 *he_oper_ie)
static inline const struct ieee80211_he_6ghz_oper *
ieee80211_he_6ghz_oper(const struct ieee80211_he_operation *he_oper)
{
const u8 *ret = (const void *)&he_oper->optional;
const u8 *ret;
u32 he_oper_params;

if (!he_oper)
return NULL;

ret = (const void *)&he_oper->optional;

he_oper_params = le32_to_cpu(he_oper->he_oper_params);

if (!(he_oper_params & IEEE80211_HE_OPERATION_6GHZ_OP_INFO))
Expand Down
Loading

0 comments on commit 300fbb2

Please sign in to comment.