Skip to contentSkip to conversation

crypto: caam: fix netdev memory leak in dpaa2_caam_probe

PATCH Superseded Jianpeng Chang 3 participants 7 messages Jan 16 – Jan 19

Subsystems (2)

v1 of 2 Superseded Latest is v2 →

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 crypto: caam: fix netdev memory leak in dpaa2_caam_probe

When commit 0e1a4d427f58 ("crypto: caam: Unembed net_dev structure in dpaa2") converted embedded net_device to dynamically allocated pointers, it added cleanup in dpaa2_dpseci_disable() but missed adding cleanup in dpaa2_dpseci_free() for error paths.

This causes memory leaks when dpaa2_dpseci_dpio_setup() fails during probe due to DPIO devices not being ready yet. The kernel's deferred probe mechanism handles the retry successfully, but the netdevs allocated during the failed probe attempt are never freed, resulting in kmemleak reports showing multiple leaked netdev-related allocations all traced back to dpaa2_caam_probe().

Fix this by preserving the CPU mask of allocated netdevs during setup and using it for cleanup in dpaa2_dpseci_free(). This approach ensures that only the CPUs that actually had netdevs allocated will be cleaned up, avoiding potential issues with CPU hotplug scenarios.

Fixes 0e1a4d427f58 ("crypto: caam: Unembed net_dev structure in dpaa2")
---
 drivers/crypto/caam/caamalg_qi2.c | 31 ++++++++++++++++---------------
 drivers/crypto/caam/caamalg_qi2.h |  2 ++
 2 files changed, 18 insertions(+), 15 deletions(-)
drivers/crypto/caam/caamalg_qi2.c +16 -15
index 107ccb2ade42..a66c62174a0f 100644
@@ -4810,6 +4810,17 @@ static void dpaa2_dpseci_congestion_free(struct dpaa2_caam_priv *priv)
 	kfree(priv->cscn_mem);
 }
 
+static void free_dpaa2_pcpu_netdev(struct dpaa2_caam_priv *priv, const cpumask_t *cpus)
+{
+	struct dpaa2_caam_priv_per_cpu *ppriv;
+	int i;
+
+	for_each_cpu(i, cpus) {
+		ppriv = per_cpu_ptr(priv->ppriv, i);
+		free_netdev(ppriv->net_dev);
+	}
+}
+
 static void dpaa2_dpseci_free(struct dpaa2_caam_priv *priv)
 {
 	struct device *dev = priv->dev;
@@ -4822,6 +4833,9 @@ static void dpaa2_dpseci_free(struct dpaa2_caam_priv *priv)
 			dev_err(dev, "dpseci_reset() failed\n");
 	}
 
+	free_dpaa2_pcpu_netdev(priv, priv->clean_mask);
+	free_cpumask_var(priv->clean_mask);
+
 	dpaa2_dpseci_congestion_free(priv);
 	dpseci_close(priv->mc_io, 0, ls_dev->mc_handle);
 }
@@ -4991,17 +5005,6 @@ static int dpaa2_dpseci_congestion_setup(struct dpaa2_caam_priv *priv,
 	return err;
 }
 
-static void free_dpaa2_pcpu_netdev(struct dpaa2_caam_priv *priv, const cpumask_t *cpus)
-{
-	struct dpaa2_caam_priv_per_cpu *ppriv;
-	int i;
-
-	for_each_cpu(i, cpus) {
-		ppriv = per_cpu_ptr(priv->ppriv, i);
-		free_netdev(ppriv->net_dev);
-	}
-}
-
 static int __cold dpaa2_dpseci_setup(struct fsl_mc_device *ls_dev)
 {
 	struct device *dev = &ls_dev->dev;
@@ -5126,8 +5129,8 @@ static int __cold dpaa2_dpseci_setup(struct fsl_mc_device *ls_dev)
 					 DPAA2_CAAM_NAPI_WEIGHT);
 	}
 
-	err = 0;
-	goto free_cpumask;
+	priv->clean_mask = clean_mask;
+	return 0;
 
 err_alloc_netdev:
 	free_dpaa2_pcpu_netdev(priv, clean_mask);
@@ -5136,7 +5139,6 @@ static int __cold dpaa2_dpseci_setup(struct fsl_mc_device *ls_dev)
 err_get_vers:
 	dpseci_close(priv->mc_io, 0, ls_dev->mc_handle);
 err_open:
-free_cpumask:
 	free_cpumask_var(clean_mask);
 err_cpumask:
 	return err;
@@ -5182,7 +5184,6 @@ static int __cold dpaa2_dpseci_disable(struct dpaa2_caam_priv *priv)
 		ppriv = per_cpu_ptr(priv->ppriv, i);
 		napi_disable(&ppriv->napi);
 		netif_napi_del(&ppriv->napi);
-		free_netdev(ppriv->net_dev);
 	}
 
 	return 0;
drivers/crypto/caam/caamalg_qi2.h +2 -0
index 61d1219a202f..8e65b4b28c7b 100644
@@ -42,6 +42,7 @@
  * @mc_io: pointer to MC portal's I/O object
  * @domain: IOMMU domain
  * @ppriv: per CPU pointers to privata data
+ * @clean_mask: CPU mask of CPUs that have allocated netdevs
  */
 struct dpaa2_caam_priv {
 	int dpsec_id;
@@ -65,6 +66,7 @@ struct dpaa2_caam_priv {
 
 	struct dpaa2_caam_priv_per_cpu __percpu *ppriv;
 	struct dentry *dfs_root;
+	cpumask_var_t clean_mask;
 };
 
 /**
Signature · 1 line
2.52.0
#1 lore raw
kernel test robot noticed the following build errors:

Hi Jianpeng,

kernel test robot noticed the following build errors:

[auto build test ERROR on herbert-cryptodev-2.6/master]
[also build test ERROR on herbert-crypto-2.6/master linus/master v6.19-rc5 next-20260115] [If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Jianpeng-Chang/crypto-caam-fix-netdev-memory-leak-in-dpaa2_caam_probe/20260116-094800
base:   https://git.kernel.org/pub/scm/linux/kernel/git/herbert/cryptodev-2.6.git master
patch link:    https://lore.kernel.org/r/20260116014455.2575351-1-jianpeng.chang.cn%40windriver.com
patch subject: [PATCH] crypto: caam: fix netdev memory leak in dpaa2_caam_probe
config: x86_64-buildonly-randconfig-004-20260116 (https://download.01.org/0day-ci/archive/20260116/[email protected]/config)
compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260116/[email protected]/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/

All errors (new ones prefixed by >>):

>> drivers/crypto/caam/caamalg_qi2.c:5132:19: error: array type 'cpumask_var_t' (aka 'struct cpumask[1]') is not assignable
    5132 |         priv->clean_mask = clean_mask;
         |         ~~~~~~~~~~~~~~~~ ^

1 error generated.

vim +5132 drivers/crypto/caam/caamalg_qi2.c

5007
5008 static int __cold dpaa2_dpseci_setup(struct fsl_mc_device *ls_dev)
5009 {
5010 struct device *dev = &ls_dev->dev;
5011 struct dpaa2_caam_priv *priv;
5012 struct dpaa2_caam_priv_per_cpu *ppriv;
5013 cpumask_var_t clean_mask;
5014 int err, cpu;
5015 u8 i;
5016
5017 err = -ENOMEM;
5018 if (!zalloc_cpumask_var(&clean_mask, GFP_KERNEL))
5019 goto err_cpumask;
5020
5021 priv = dev_get_drvdata(dev);
5022
5023 priv->dev = dev;
5024 priv->dpsec_id = ls_dev->obj_desc.id;
5025
5026 /* Get a handle for the DPSECI this interface is associate with */
5027 err = dpseci_open(priv->mc_io, 0, priv->dpsec_id, &ls_dev->mc_handle);
5028 if (err) {
5029 dev_err(dev, "dpseci_open() failed: %d\n", err);
5030 goto err_open;
5031 }
5032
5033 err = dpseci_get_api_version(priv->mc_io, 0, &priv->major_ver,
5034 &priv->minor_ver);
5035 if (err) {
5036 dev_err(dev, "dpseci_get_api_version() failed\n");
5037 goto err_get_vers;
5038 }
5039
5040 dev_info(dev, "dpseci v%d.%d\n", priv->major_ver, priv->minor_ver);
5041
5042 if (DPSECI_VER(priv->major_ver, priv->minor_ver) > DPSECI_VER(5, 3)) {
5043 err = dpseci_reset(priv->mc_io, 0, ls_dev->mc_handle);
5044 if (err) {
5045 dev_err(dev, "dpseci_reset() failed\n");
5046 goto err_get_vers;
5047 }
5048 }
5049
5050 err = dpseci_get_attributes(priv->mc_io, 0, ls_dev->mc_handle,
5051 &priv->dpseci_attr);
5052 if (err) {
5053 dev_err(dev, "dpseci_get_attributes() failed\n");
5054 goto err_get_vers;
5055 }
5056
5057 err = dpseci_get_sec_attr(priv->mc_io, 0, ls_dev->mc_handle,
5058 &priv->sec_attr);
5059 if (err) {
5060 dev_err(dev, "dpseci_get_sec_attr() failed\n");
5061 goto err_get_vers;
5062 }
5063
5064 err = dpaa2_dpseci_congestion_setup(priv, ls_dev->mc_handle);
5065 if (err) {
5066 dev_err(dev, "setup_congestion() failed\n");
5067 goto err_get_vers;
5068 }
5069
5070 priv->num_pairs = min(priv->dpseci_attr.num_rx_queues,
5071 priv->dpseci_attr.num_tx_queues);
5072 if (priv->num_pairs > num_online_cpus()) {
5073 dev_warn(dev, "%d queues won't be used\n",
5074 priv->num_pairs - num_online_cpus());
5075 priv->num_pairs = num_online_cpus();
5076 }
5077
5078 for (i = 0; i < priv->dpseci_attr.num_rx_queues; i++) {
5079 err = dpseci_get_rx_queue(priv->mc_io, 0, ls_dev->mc_handle, i,
5080 &priv->rx_queue_attr[i]);
5081 if (err) {
5082 dev_err(dev, "dpseci_get_rx_queue() failed\n");
5083 goto err_get_rx_queue;
5084 }
5085 }
5086
5087 for (i = 0; i < priv->dpseci_attr.num_tx_queues; i++) {
5088 err = dpseci_get_tx_queue(priv->mc_io, 0, ls_dev->mc_handle, i,
5089 &priv->tx_queue_attr[i]);
5090 if (err) {
5091 dev_err(dev, "dpseci_get_tx_queue() failed\n");
5092 goto err_get_rx_queue;
5093 }
5094 }
5095
5096 i = 0;
5097 for_each_online_cpu(cpu) {
5098 u8 j;
5099
5100 j = i % priv->num_pairs;
5101
5102 ppriv = per_cpu_ptr(priv->ppriv, cpu);
5103 ppriv->req_fqid = priv->tx_queue_attr[j].fqid;
5104
5105 /*
5106 * Allow all cores to enqueue, while only some of them
5107 * will take part in dequeuing.
5108 */
5109 if (++i > priv->num_pairs)
5110 continue;
5111
5112 ppriv->rsp_fqid = priv->rx_queue_attr[j].fqid;
5113 ppriv->prio = j;
5114
5115 dev_dbg(dev, "pair %d: rx queue %d, tx queue %d\n", j,
5116 priv->rx_queue_attr[j].fqid,
5117 priv->tx_queue_attr[j].fqid);
5118
5119 ppriv->net_dev = alloc_netdev_dummy(0);
5120 if (!ppriv->net_dev) {
5121 err = -ENOMEM;
5122 goto err_alloc_netdev;
5123 }
5124 cpumask_set_cpu(cpu, clean_mask);
5125 ppriv->net_dev->dev = *dev;
5126
5127 netif_napi_add_tx_weight(ppriv->net_dev, &ppriv->napi,
5128 dpaa2_dpseci_poll,
5129 DPAA2_CAAM_NAPI_WEIGHT);
5130 }
5131

> 5132		priv->clean_mask = clean_mask;

5133 return 0;
5134
5135 err_alloc_netdev:
5136 free_dpaa2_pcpu_netdev(priv, clean_mask);
5137 err_get_rx_queue:
5138 dpaa2_dpseci_congestion_free(priv);
5139 err_get_vers:
5140 dpseci_close(priv->mc_io, 0, ls_dev->mc_handle);
5141 err_open:
5142 free_cpumask_var(clean_mask);
5143 err_cpumask:
5144 return err;
5145 }
5146

Signature · 2 lines
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
#2 lore raw
Quoted 26 lines · kernel test robot

CAUTION: This email comes from a non Wind River email account!
Do not click links or open attachments unless you recognize the sender and know the content is safe.

Hi Jianpeng,

kernel test robot noticed the following build errors:

[auto build test ERROR on herbert-cryptodev-2.6/master]
[also build test ERROR on herbert-crypto-2.6/master linus/master v6.19-rc5 next-20260115] [If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Jianpeng-Chang/crypto-caam-fix-netdev-memory-leak-in-dpaa2_caam_probe/20260116-094800
base:   https://git.kernel.org/pub/scm/linux/kernel/git/herbert/cryptodev-2.6.git master
patch link:    https://lore.kernel.org/r/20260116014455.2575351-1-jianpeng.chang.cn%40windriver.com
patch subject: [PATCH] crypto: caam: fix netdev memory leak in dpaa2_caam_probe
config: x86_64-buildonly-randconfig-004-20260116 (https://download.01.org/0day-ci/archive/20260116/[email protected]/config)
compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260116/[email protected]/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/

Quoted 6 lines

All errors (new ones prefixed by >>):

Quoted 1 line
Quoted 1 line

drivers/crypto/caam/caamalg_qi2.c:5132:19: error: array type 'cpumask_var_t' (aka 'struct cpumask[1]') is not assignable

     5132 |         priv->clean_mask = clean_mask;
          |         ~~~~~~~~~~~~~~~~ ^

1 error generated.

Tested only with CONFIG_CPUMASK_OFFSTACK enabled.

Please ignore this patch. I will resend the patch with the error fixed.

Sorry for the inconvenience.

Regards,

Jianpeng

Quoted 148 lines

vim +5132 drivers/crypto/caam/caamalg_qi2.c

   5007
   5008  static int __cold dpaa2_dpseci_setup(struct fsl_mc_device *ls_dev)
   5009  {
   5010          struct device *dev = &ls_dev->dev;
   5011          struct dpaa2_caam_priv *priv;
   5012          struct dpaa2_caam_priv_per_cpu *ppriv;
   5013          cpumask_var_t clean_mask;
   5014          int err, cpu;
   5015          u8 i;
   5016
   5017          err = -ENOMEM;
   5018          if (!zalloc_cpumask_var(&clean_mask, GFP_KERNEL))
   5019                  goto err_cpumask;
   5020
   5021          priv = dev_get_drvdata(dev);
   5022
   5023          priv->dev = dev;
   5024          priv->dpsec_id = ls_dev->obj_desc.id;
   5025
   5026          /* Get a handle for the DPSECI this interface is associate with */
   5027          err = dpseci_open(priv->mc_io, 0, priv->dpsec_id, &ls_dev->mc_handle);
   5028          if (err) {
   5029                  dev_err(dev, "dpseci_open() failed: %d\n", err);
   5030                  goto err_open;
   5031          }
   5032
   5033          err = dpseci_get_api_version(priv->mc_io, 0, &priv->major_ver,
   5034                                       &priv->minor_ver);
   5035          if (err) {
   5036                  dev_err(dev, "dpseci_get_api_version() failed\n");
   5037                  goto err_get_vers;
   5038          }
   5039
   5040          dev_info(dev, "dpseci v%d.%d\n", priv->major_ver, priv->minor_ver);
   5041
   5042          if (DPSECI_VER(priv->major_ver, priv->minor_ver) > DPSECI_VER(5, 3)) {
   5043                  err = dpseci_reset(priv->mc_io, 0, ls_dev->mc_handle);
   5044                  if (err) {
   5045                          dev_err(dev, "dpseci_reset() failed\n");
   5046                          goto err_get_vers;
   5047                  }
   5048          }
   5049
   5050          err = dpseci_get_attributes(priv->mc_io, 0, ls_dev->mc_handle,
   5051                                      &priv->dpseci_attr);
   5052          if (err) {
   5053                  dev_err(dev, "dpseci_get_attributes() failed\n");
   5054                  goto err_get_vers;
   5055          }
   5056
   5057          err = dpseci_get_sec_attr(priv->mc_io, 0, ls_dev->mc_handle,
   5058                                    &priv->sec_attr);
   5059          if (err) {
   5060                  dev_err(dev, "dpseci_get_sec_attr() failed\n");
   5061                  goto err_get_vers;
   5062          }
   5063
   5064          err = dpaa2_dpseci_congestion_setup(priv, ls_dev->mc_handle);
   5065          if (err) {
   5066                  dev_err(dev, "setup_congestion() failed\n");
   5067                  goto err_get_vers;
   5068          }
   5069
   5070          priv->num_pairs = min(priv->dpseci_attr.num_rx_queues,
   5071                                priv->dpseci_attr.num_tx_queues);
   5072          if (priv->num_pairs > num_online_cpus()) {
   5073                  dev_warn(dev, "%d queues won't be used\n",
   5074                           priv->num_pairs - num_online_cpus());
   5075                  priv->num_pairs = num_online_cpus();
   5076          }
   5077
   5078          for (i = 0; i < priv->dpseci_attr.num_rx_queues; i++) {
   5079                  err = dpseci_get_rx_queue(priv->mc_io, 0, ls_dev->mc_handle, i,
   5080                                            &priv->rx_queue_attr[i]);
   5081                  if (err) {
   5082                          dev_err(dev, "dpseci_get_rx_queue() failed\n");
   5083                          goto err_get_rx_queue;
   5084                  }
   5085          }
   5086
   5087          for (i = 0; i < priv->dpseci_attr.num_tx_queues; i++) {
   5088                  err = dpseci_get_tx_queue(priv->mc_io, 0, ls_dev->mc_handle, i,
   5089                                            &priv->tx_queue_attr[i]);
   5090                  if (err) {
   5091                          dev_err(dev, "dpseci_get_tx_queue() failed\n");
   5092                          goto err_get_rx_queue;
   5093                  }
   5094          }
   5095
   5096          i = 0;
   5097          for_each_online_cpu(cpu) {
   5098                  u8 j;
   5099
   5100                  j = i % priv->num_pairs;
   5101
   5102                  ppriv = per_cpu_ptr(priv->ppriv, cpu);
   5103                  ppriv->req_fqid = priv->tx_queue_attr[j].fqid;
   5104
   5105                  /*
   5106                   * Allow all cores to enqueue, while only some of them
   5107                   * will take part in dequeuing.
   5108                   */
   5109                  if (++i > priv->num_pairs)
   5110                          continue;
   5111
   5112                  ppriv->rsp_fqid = priv->rx_queue_attr[j].fqid;
   5113                  ppriv->prio = j;
   5114
   5115                  dev_dbg(dev, "pair %d: rx queue %d, tx queue %d\n", j,
   5116                          priv->rx_queue_attr[j].fqid,
   5117                          priv->tx_queue_attr[j].fqid);
   5118
   5119                  ppriv->net_dev = alloc_netdev_dummy(0);
   5120                  if (!ppriv->net_dev) {
   5121                          err = -ENOMEM;
   5122                          goto err_alloc_netdev;
   5123                  }
   5124                  cpumask_set_cpu(cpu, clean_mask);
   5125                  ppriv->net_dev->dev = *dev;
   5126
   5127                  netif_napi_add_tx_weight(ppriv->net_dev, &ppriv->napi,
   5128                                           dpaa2_dpseci_poll,
   5129                                           DPAA2_CAAM_NAPI_WEIGHT);
   5130          }
   5131
Quoted 1 line

5132 priv->clean_mask = clean_mask;

   5133          return 0;
   5134
   5135  err_alloc_netdev:
   5136          free_dpaa2_pcpu_netdev(priv, clean_mask);
   5137  err_get_rx_queue:
   5138          dpaa2_dpseci_congestion_free(priv);
   5139  err_get_vers:
   5140          dpseci_close(priv->mc_io, 0, ls_dev->mc_handle);
   5141  err_open:
   5142          free_cpumask_var(clean_mask);
   5143  err_cpumask:
   5144          return err;
   5145  }
   5146

--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

#3 lore raw
In reply to Jianpeng Chang
Quoted 32 lines · Jianpeng Chang

When commit 0e1a4d427f58 ("crypto: caam: Unembed net_dev structure in dpaa2") converted embedded net_device to dynamically allocated pointers, it added cleanup in dpaa2_dpseci_disable() but missed adding cleanup in dpaa2_dpseci_free() for error paths.

This causes memory leaks when dpaa2_dpseci_dpio_setup() fails during probe due to DPIO devices not being ready yet. The kernel's deferred probe mechanism handles the retry successfully, but the netdevs allocated during the failed probe attempt are never freed, resulting in kmemleak reports showing multiple leaked netdev-related allocations all traced back to dpaa2_caam_probe().

Fix this by preserving the CPU mask of allocated netdevs during setup and using it for cleanup in dpaa2_dpseci_free(). This approach ensures that only the CPUs that actually had netdevs allocated will be cleaned up, avoiding potential issues with CPU hotplug scenarios.

Fixes 0e1a4d427f58 ("crypto: caam: Unembed net_dev structure in dpaa2")
---
 drivers/crypto/caam/caamalg_qi2.c | 31 ++++++++++++++++---------------
 drivers/crypto/caam/caamalg_qi2.h |  2 ++
 2 files changed, 18 insertions(+), 15 deletions(-)
drivers/crypto/caam/caamalg_qi2.c +0 -0
index 107ccb2ade42..a66c62174a0f 100644
@@ -4810,6 +4810,17 @@ static void dpaa2_dpseci_congestion_free(struct dpaa2_caam_priv *priv)
 	kfree(priv->cscn_mem);
 }
 

drivers/crypto/caam/caamalg_qi2.c static void dpaa2_dpseci_congestion_free(struct dpaa2_caam_priv *priv) · line 4822

Quoted 10 lines
drivers/crypto/caam/caamalg_qi2.c +10 -0
+static void free_dpaa2_pcpu_netdev(struct dpaa2_caam_priv *priv, const cpumask_t *cpus)
+{
+	struct dpaa2_caam_priv_per_cpu *ppriv;
+	int i;
+
+	for_each_cpu(i, cpus) {
+		ppriv = per_cpu_ptr(priv->ppriv, i);
+		free_netdev(ppriv->net_dev);
+	}
+}

Why is the function being moved here? Please keep code movement separate from functional changes, or at minimum explain why the move is necessary in the commit message.

#4 lore raw
Quoted 36 lines · Breno Leitao

CAUTION: This email comes from a non Wind River email account!
Do not click links or open attachments unless you recognize the sender and know the content is safe.

Quoted 32 lines · Jianpeng Chang

When commit 0e1a4d427f58 ("crypto: caam: Unembed net_dev structure in dpaa2") converted embedded net_device to dynamically allocated pointers, it added cleanup in dpaa2_dpseci_disable() but missed adding cleanup in dpaa2_dpseci_free() for error paths.

This causes memory leaks when dpaa2_dpseci_dpio_setup() fails during probe due to DPIO devices not being ready yet. The kernel's deferred probe mechanism handles the retry successfully, but the netdevs allocated during the failed probe attempt are never freed, resulting in kmemleak reports showing multiple leaked netdev-related allocations all traced back to dpaa2_caam_probe().

Fix this by preserving the CPU mask of allocated netdevs during setup and using it for cleanup in dpaa2_dpseci_free(). This approach ensures that only the CPUs that actually had netdevs allocated will be cleaned up, avoiding potential issues with CPU hotplug scenarios.

Fixes 0e1a4d427f58 ("crypto: caam: Unembed net_dev structure in dpaa2")
---
  drivers/crypto/caam/caamalg_qi2.c | 31 ++++++++++++++++---------------
  drivers/crypto/caam/caamalg_qi2.h |  2 ++
  2 files changed, 18 insertions(+), 15 deletions(-)
drivers/crypto/caam/caamalg_qi2.c +0 -0
index 107ccb2ade42..a66c62174a0f 100644
@@ -4810,6 +4810,17 @@ static void dpaa2_dpseci_congestion_free(struct dpaa2_caam_priv *priv)
       kfree(priv->cscn_mem);
  }
 
Quoted 14 lines

drivers/crypto/caam/caamalg_qi2.c static void dpaa2_dpseci_congestion_free(struct dpaa2_caam_priv *priv) · line 4822

Quoted 10 lines
drivers/crypto/caam/caamalg_qi2.c +10 -0
+static void free_dpaa2_pcpu_netdev(struct dpaa2_caam_priv *priv, const cpumask_t *cpus)
+{
+     struct dpaa2_caam_priv_per_cpu *ppriv;
+     int i;
+
+     for_each_cpu(i, cpus) {
+             ppriv = per_cpu_ptr(priv->ppriv, i);
+             free_netdev(ppriv->net_dev);
+     }
+}

Why is the function being moved here? Please keep code movement separate from functional changes, or at minimum explain why the move is necessary in the commit message.

Thank you for the feedback.

I moved the function because I thought reusing existing code would be cleaner in dpaa2_dpseci_free. I will add the explain in commit message.

For future reference, what's the preferred approach when needing to reuse a simple function (4-line loop) defined later in the file - forward declaration, move it with a separate change or just implement directly? Thanks for the guidance.

Regards,
Jianpeng

#5 lore raw

Hello Jianpeng,

Quoted 51 lines · Jianpeng (CN)
Quoted 38 lines · Breno Leitao

CAUTION: This email comes from a non Wind River email account!
Do not click links or open attachments unless you recognize the sender and know the content is safe.

Quoted 29 lines · Jianpeng Chang

When commit 0e1a4d427f58 ("crypto: caam: Unembed net_dev structure in dpaa2") converted embedded net_device to dynamically allocated pointers, it added cleanup in dpaa2_dpseci_disable() but missed adding cleanup in dpaa2_dpseci_free() for error paths.

This causes memory leaks when dpaa2_dpseci_dpio_setup() fails during probe due to DPIO devices not being ready yet. The kernel's deferred probe mechanism handles the retry successfully, but the netdevs allocated during the failed probe attempt are never freed, resulting in kmemleak reports showing multiple leaked netdev-related allocations all traced back to dpaa2_caam_probe().

Fix this by preserving the CPU mask of allocated netdevs during setup and using it for cleanup in dpaa2_dpseci_free(). This approach ensures that only the CPUs that actually had netdevs allocated will be cleaned up, avoiding potential issues with CPU hotplug scenarios.

Fixes 0e1a4d427f58 ("crypto: caam: Unembed net_dev structure in dpaa2")
---
  drivers/crypto/caam/caamalg_qi2.c | 31 ++++++++++++++++---------------
  drivers/crypto/caam/caamalg_qi2.h |  2 ++
  2 files changed, 18 insertions(+), 15 deletions(-)
drivers/crypto/caam/caamalg_qi2.c +0 -0
index 107ccb2ade42..a66c62174a0f 100644

drivers/crypto/caam/caamalg_qi2.c static void dpaa2_dpseci_congestion_free(struct dpaa2_caam_priv *priv) · line 4813

Quoted 4 lines
drivers/crypto/caam/caamalg_qi2.c +1 -0
@@ -4810,6 +4810,17 @@ static void dpaa2_dpseci_congestion_free(struct dpaa2_caam_priv *priv)
       kfree(priv->cscn_mem);
  }
 
+static void free_dpaa2_pcpu_netdev(struct dpaa2_caam_priv *priv, const cpumask_t *cpus)

c> +{

Quoted 12 lines

drivers/crypto/caam/caamalg_qi2.c static void dpaa2_dpseci_congestion_free(struct dpaa2_caam_priv *priv) · line 4822

Quoted 8 lines
drivers/crypto/caam/caamalg_qi2.c +8 -0
+     struct dpaa2_caam_priv_per_cpu *ppriv;
+     int i;
+
+     for_each_cpu(i, cpus) {
+             ppriv = per_cpu_ptr(priv->ppriv, i);
+             free_netdev(ppriv->net_dev);
+     }
+}

Why is the function being moved here? Please keep code movement separate from functional changes, or at minimum explain why the move is necessary in the commit message.

Quoted 8 lines

Thank you for the feedback.

I moved the function because I thought reusing existing code would be cleaner in dpaa2_dpseci_free. I will add the explain in commit message.

For future reference, what's the preferred approach when needing to reuse a simple function (4-line loop) defined later in the file - forward declaration, move it with a separate change or just implement directly?

It is fine to implement directly, but, I am a bit confused with the solution, let me back up a bit.

First, it seems the problem is real and thanks for fixing it.

Regarding the solution, I am wondering if it is not simpler to iterate the priv->num_pairs and kfreeing them in dpaa2_dpseci_free(), similarly to dpaa2_dpseci_disable().

#6 lore raw
Quoted 66 lines · Breno Leitao

CAUTION: This email comes from a non Wind River email account!
Do not click links or open attachments unless you recognize the sender and know the content is safe.

Hello Jianpeng,

Quoted 51 lines · Jianpeng (CN)
Quoted 38 lines · Breno Leitao

CAUTION: This email comes from a non Wind River email account!
Do not click links or open attachments unless you recognize the sender and know the content is safe.

Quoted 29 lines · Jianpeng Chang

When commit 0e1a4d427f58 ("crypto: caam: Unembed net_dev structure in dpaa2") converted embedded net_device to dynamically allocated pointers, it added cleanup in dpaa2_dpseci_disable() but missed adding cleanup in dpaa2_dpseci_free() for error paths.

This causes memory leaks when dpaa2_dpseci_dpio_setup() fails during probe due to DPIO devices not being ready yet. The kernel's deferred probe mechanism handles the retry successfully, but the netdevs allocated during the failed probe attempt are never freed, resulting in kmemleak reports showing multiple leaked netdev-related allocations all traced back to dpaa2_caam_probe().

Fix this by preserving the CPU mask of allocated netdevs during setup and using it for cleanup in dpaa2_dpseci_free(). This approach ensures that only the CPUs that actually had netdevs allocated will be cleaned up, avoiding potential issues with CPU hotplug scenarios.

Fixes 0e1a4d427f58 ("crypto: caam: Unembed net_dev structure in dpaa2")
---
   drivers/crypto/caam/caamalg_qi2.c | 31 ++++++++++++++++---------------
   drivers/crypto/caam/caamalg_qi2.h |  2 ++
   2 files changed, 18 insertions(+), 15 deletions(-)
drivers/crypto/caam/caamalg_qi2.c +0 -0
index 107ccb2ade42..a66c62174a0f 100644

drivers/crypto/caam/caamalg_qi2.c static void dpaa2_dpseci_congestion_free(struct dpaa2_caam_priv *priv) · line 4813

Quoted 4 lines
drivers/crypto/caam/caamalg_qi2.c +1 -0
@@ -4810,6 +4810,17 @@ static void dpaa2_dpseci_congestion_free(struct dpaa2_caam_priv *priv)
        kfree(priv->cscn_mem);
   }
 
+static void free_dpaa2_pcpu_netdev(struct dpaa2_caam_priv *priv, const cpumask_t *cpus)

c> +{

Quoted 12 lines

drivers/crypto/caam/caamalg_qi2.c static void dpaa2_dpseci_congestion_free(struct dpaa2_caam_priv *priv) · line 4822

Quoted 8 lines
drivers/crypto/caam/caamalg_qi2.c +8 -0
+     struct dpaa2_caam_priv_per_cpu *ppriv;
+     int i;
+
+     for_each_cpu(i, cpus) {
+             ppriv = per_cpu_ptr(priv->ppriv, i);
+             free_netdev(ppriv->net_dev);
+     }
+}

Why is the function being moved here? Please keep code movement separate from functional changes, or at minimum explain why the move is necessary in the commit message.

Quoted 8 lines

Thank you for the feedback.

I moved the function because I thought reusing existing code would be cleaner in dpaa2_dpseci_free. I will add the explain in commit message.

For future reference, what's the preferred approach when needing to reuse a simple function (4-line loop) defined later in the file - forward declaration, move it with a separate change or just implement directly?

Quoted 8 lines

It is fine to implement directly, but, I am a bit confused with the solution, let me back up a bit.

First, it seems the problem is real and thanks for fixing it.

Regarding the solution, I am wondering if it is not simpler to iterate the priv->num_pairs and kfreeing them in dpaa2_dpseci_free(), similarly to dpaa2_dpseci_disable().

Hi Leitao,

Thanks for you reply, I will implement directly instead of moving the function in v2.

I have considered using the approach of iterating through priv->num_pairs, but the index of num_pairs cannot represent the CPU number.

Consider a theoretical scenario where there are multiple CPUs and the cpu 2 is disabled. When iterating through num_pairs, we would get per_cpu_ptr(priv->ppriv, 2), but this would be meaningless.

This is not a critical issue, and I don't have a strong preference either way. I just think using a CPU mask to ensure precise cleanup might be more appropriate. Furthermore, there are risks in using num_pairs. If we manually disable a CPU and then disable/enable the driver, we would encounter an oops, but that's a separate issue.

Thanks,
Jianpeng