]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
idpf: fix memory leak of flow steer list on rmmod
authorSreedevi Joshi <sreedevi.joshi@intel.com>
Tue, 30 Sep 2025 21:23:51 +0000 (16:23 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 17 Jan 2026 15:35:27 +0000 (16:35 +0100)
[ Upstream commit f9841bd28b600526ca4f6713b0ca49bf7bb98452 ]

The flow steering list maintains entries that are added and removed as
ethtool creates and deletes flow steering rules. Module removal with active
entries causes memory leak as the list is not properly cleaned up.

Prevent this by iterating through the remaining entries in the list and
freeing the associated memory during module removal. Add a spinlock
(flow_steer_list_lock) to protect the list access from multiple threads.

Fixes: ada3e24b84a0 ("idpf: add flow steering support")
Reviewed-by: Przemek Kitszel <przemyslaw.kitszel@intel.com>
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
Signed-off-by: Sreedevi Joshi <sreedevi.joshi@intel.com>
Reviewed-by: Simon Horman <horms@kernel.org>
Tested-by: Mina Almasry <almasrymina@google.com>
Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/net/ethernet/intel/idpf/idpf.h
drivers/net/ethernet/intel/idpf/idpf_ethtool.c
drivers/net/ethernet/intel/idpf/idpf_lib.c

index 64142f8163fed7d60bcf0d95efa4f452a0b22e1a..af8deb5fa80f02fb2455539a1198ff1e903dc9c7 100644 (file)
@@ -558,6 +558,7 @@ struct idpf_vector_lifo {
  * @max_q: Maximum possible queues
  * @req_qs_chunks: Queue chunk data for requested queues
  * @mac_filter_list_lock: Lock to protect mac filters
+ * @flow_steer_list_lock: Lock to protect fsteer filters
  * @flags: See enum idpf_vport_config_flags
  */
 struct idpf_vport_config {
@@ -565,6 +566,7 @@ struct idpf_vport_config {
        struct idpf_vport_max_q max_q;
        struct virtchnl2_add_queues *req_qs_chunks;
        spinlock_t mac_filter_list_lock;
+       spinlock_t flow_steer_list_lock;
        DECLARE_BITMAP(flags, IDPF_VPORT_CONFIG_FLAGS_NBITS);
 };
 
index eed166bc46f38ef0869608aec83b4c8cb76cc831..8477e7ba28706346083c7a10bc58afcd18553f4a 100644 (file)
@@ -18,6 +18,7 @@ static int idpf_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd,
 {
        struct idpf_netdev_priv *np = netdev_priv(netdev);
        struct idpf_vport_user_config_data *user_config;
+       struct idpf_vport_config *vport_config;
        struct idpf_fsteer_fltr *f;
        struct idpf_vport *vport;
        unsigned int cnt = 0;
@@ -25,7 +26,8 @@ static int idpf_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd,
 
        idpf_vport_ctrl_lock(netdev);
        vport = idpf_netdev_to_vport(netdev);
-       user_config = &np->adapter->vport_config[np->vport_idx]->user_config;
+       vport_config = np->adapter->vport_config[np->vport_idx];
+       user_config = &vport_config->user_config;
 
        switch (cmd->cmd) {
        case ETHTOOL_GRXRINGS:
@@ -37,15 +39,18 @@ static int idpf_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd,
                break;
        case ETHTOOL_GRXCLSRULE:
                err = -EINVAL;
+               spin_lock_bh(&vport_config->flow_steer_list_lock);
                list_for_each_entry(f, &user_config->flow_steer_list, list)
                        if (f->loc == cmd->fs.location) {
                                cmd->fs.ring_cookie = f->q_index;
                                err = 0;
                                break;
                        }
+               spin_unlock_bh(&vport_config->flow_steer_list_lock);
                break;
        case ETHTOOL_GRXCLSRLALL:
                cmd->data = idpf_fsteer_max_rules(vport);
+               spin_lock_bh(&vport_config->flow_steer_list_lock);
                list_for_each_entry(f, &user_config->flow_steer_list, list) {
                        if (cnt == cmd->rule_cnt) {
                                err = -EMSGSIZE;
@@ -56,6 +61,7 @@ static int idpf_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd,
                }
                if (!err)
                        cmd->rule_cnt = user_config->num_fsteer_fltrs;
+               spin_unlock_bh(&vport_config->flow_steer_list_lock);
                break;
        default:
                break;
@@ -224,6 +230,7 @@ static int idpf_add_flow_steer(struct net_device *netdev,
 
        fltr->loc = fsp->location;
        fltr->q_index = q_index;
+       spin_lock_bh(&vport_config->flow_steer_list_lock);
        list_for_each_entry(f, &user_config->flow_steer_list, list) {
                if (f->loc >= fltr->loc)
                        break;
@@ -234,6 +241,7 @@ static int idpf_add_flow_steer(struct net_device *netdev,
                 list_add(&fltr->list, &user_config->flow_steer_list);
 
        user_config->num_fsteer_fltrs++;
+       spin_unlock_bh(&vport_config->flow_steer_list_lock);
 
 out:
        kfree(rule);
@@ -286,17 +294,20 @@ static int idpf_del_flow_steer(struct net_device *netdev,
                goto out;
        }
 
+       spin_lock_bh(&vport_config->flow_steer_list_lock);
        list_for_each_entry_safe(f, iter,
                                 &user_config->flow_steer_list, list) {
                if (f->loc == fsp->location) {
                        list_del(&f->list);
                        kfree(f);
                        user_config->num_fsteer_fltrs--;
-                       goto out;
+                       goto out_unlock;
                }
        }
        err = -EINVAL;
 
+out_unlock:
+       spin_unlock_bh(&vport_config->flow_steer_list_lock);
 out:
        kfree(rule);
        return err;
index e2ee8b137421f2a542725e411bf9e51ddf0447ef..d56366e676cf74d6ac615cb89ab92556880af372 100644 (file)
@@ -442,6 +442,29 @@ send_dealloc_vecs:
        return err;
 }
 
+/**
+ * idpf_del_all_flow_steer_filters - Delete all flow steer filters in list
+ * @vport: main vport struct
+ *
+ * Takes flow_steer_list_lock spinlock.  Deletes all filters
+ */
+static void idpf_del_all_flow_steer_filters(struct idpf_vport *vport)
+{
+       struct idpf_vport_config *vport_config;
+       struct idpf_fsteer_fltr *f, *ftmp;
+
+       vport_config = vport->adapter->vport_config[vport->idx];
+
+       spin_lock_bh(&vport_config->flow_steer_list_lock);
+       list_for_each_entry_safe(f, ftmp, &vport_config->user_config.flow_steer_list,
+                                list) {
+               list_del(&f->list);
+               kfree(f);
+       }
+       vport_config->user_config.num_fsteer_fltrs = 0;
+       spin_unlock_bh(&vport_config->flow_steer_list_lock);
+}
+
 /**
  * idpf_find_mac_filter - Search filter list for specific mac filter
  * @vconfig: Vport config structure
@@ -1107,8 +1130,10 @@ static void idpf_vport_dealloc(struct idpf_vport *vport)
                idpf_vport_stop(vport, true);
                idpf_decfg_netdev(vport);
        }
-       if (test_bit(IDPF_REMOVE_IN_PROG, adapter->flags))
+       if (test_bit(IDPF_REMOVE_IN_PROG, adapter->flags)) {
                idpf_del_all_mac_filters(vport);
+               idpf_del_all_flow_steer_filters(vport);
+       }
 
        if (adapter->netdevs[i]) {
                struct idpf_netdev_priv *np = netdev_priv(adapter->netdevs[i]);
@@ -1648,6 +1673,7 @@ void idpf_init_task(struct work_struct *work)
        vport_config = adapter->vport_config[index];
 
        spin_lock_init(&vport_config->mac_filter_list_lock);
+       spin_lock_init(&vport_config->flow_steer_list_lock);
 
        INIT_LIST_HEAD(&vport_config->user_config.mac_filter_list);
        INIT_LIST_HEAD(&vport_config->user_config.flow_steer_list);