diff mbox series

[net-next] idpf: Slightly simplify memory management in idpf_add_del_mac_filters()

Message ID fa4f19064be084d5e740e625dcf05805c0d71ad0.1724394169.git.christophe.jaillet@wanadoo.fr (mailing list archive)
State Changes Requested
Delegated to: Netdev Maintainers
Headers show
Series [net-next] idpf: Slightly simplify memory management in idpf_add_del_mac_filters() | expand

Checks

Context Check Description
netdev/series_format success Single patches do not need cover letters
netdev/tree_selection success Clearly marked for net-next
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 16 this patch: 16
netdev/build_tools success No tools touched, skip
netdev/cc_maintainers success CCed 7 of 7 maintainers
netdev/build_clang success Errors and warnings before: 16 this patch: 16
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 16 this patch: 16
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 17 lines checked
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 54 this patch: 54
netdev/source_inline success Was 0 now: 0
netdev/contest success net-next-2024-08-25--21-00 (tests: 714)

Commit Message

Christophe JAILLET Aug. 23, 2024, 6:23 a.m. UTC
In idpf_add_del_mac_filters(), filters are chunked up into multiple
messages to avoid sending a control queue message buffer that is too large.

Each chunk has up to IDPF_NUM_FILTERS_PER_MSG entries. So except for the
last iteration which can be smaller, space for exactly
IDPF_NUM_FILTERS_PER_MSG entries is allocated.

There is no need to free and reallocate a smaller array just for the last
iteration.

This slightly simplifies the code and avoid an (unlikely) memory allocation
failure.

Signed-off-by: Christophe JAILLET <christophe.jaillet@wanadoo.fr>
---
 drivers/net/ethernet/intel/idpf/idpf_virtchnl.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

Comments

Dan Carpenter Aug. 23, 2024, 9:10 a.m. UTC | #1
On Fri, Aug 23, 2024 at 08:23:29AM +0200, Christophe JAILLET wrote:
> In idpf_add_del_mac_filters(), filters are chunked up into multiple
> messages to avoid sending a control queue message buffer that is too large.
> 
> Each chunk has up to IDPF_NUM_FILTERS_PER_MSG entries. So except for the
> last iteration which can be smaller, space for exactly
> IDPF_NUM_FILTERS_PER_MSG entries is allocated.
> 
> There is no need to free and reallocate a smaller array just for the last
> iteration.
> 
> This slightly simplifies the code and avoid an (unlikely) memory allocation
> failure.
> 
> Signed-off-by: Christophe JAILLET <christophe.jaillet@wanadoo.fr>
> ---
>  drivers/net/ethernet/intel/idpf/idpf_virtchnl.c | 7 +++++--
>  1 file changed, 5 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
> index 70986e12da28..b6f4b58e1094 100644
> --- a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
> +++ b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
> @@ -3669,12 +3669,15 @@ int idpf_add_del_mac_filters(struct idpf_vport *vport,
>  		entries_size = sizeof(struct virtchnl2_mac_addr) * num_entries;
>  		buf_size = struct_size(ma_list, mac_addr_list, num_entries);
>  
> -		if (!ma_list || num_entries != IDPF_NUM_FILTERS_PER_MSG) {
> -			kfree(ma_list);
> +		if (!ma_list) {
>  			ma_list = kzalloc(buf_size, GFP_ATOMIC);
>  			if (!ma_list)
>  				return -ENOMEM;
>  		} else {
> +			/* ma_list was allocated in the first iteration
> +			 * so IDPF_NUM_FILTERS_PER_MSG entries are
> +			 * available
> +			 */
>  			memset(ma_list, 0, buf_size);
>  		}

It would be even nicer to move the ma_list allocation outside the loop:

        buf_size = struct_size(ma_list, mac_addr_list, IDPF_NUM_FILTERS_PER_MSG);
        ma_list = kmalloc(buf_size, GFP_ATOMIC);

regards,
dan carpenter
Simon Horman Aug. 23, 2024, 4:12 p.m. UTC | #2
On Fri, Aug 23, 2024 at 08:23:29AM +0200, Christophe JAILLET wrote:
> In idpf_add_del_mac_filters(), filters are chunked up into multiple
> messages to avoid sending a control queue message buffer that is too large.
> 
> Each chunk has up to IDPF_NUM_FILTERS_PER_MSG entries. So except for the
> last iteration which can be smaller, space for exactly
> IDPF_NUM_FILTERS_PER_MSG entries is allocated.
> 
> There is no need to free and reallocate a smaller array just for the last
> iteration.
> 
> This slightly simplifies the code and avoid an (unlikely) memory allocation
> failure.
> 
> Signed-off-by: Christophe JAILLET <christophe.jaillet@wanadoo.fr>

Reviewed-by: Simon Horman <horms@kernel.org>
Przemek Kitszel Aug. 26, 2024, 9:15 a.m. UTC | #3
On 8/23/24 11:10, Dan Carpenter wrote:
> On Fri, Aug 23, 2024 at 08:23:29AM +0200, Christophe JAILLET wrote:
>> In idpf_add_del_mac_filters(), filters are chunked up into multiple
>> messages to avoid sending a control queue message buffer that is too large.
>>
>> Each chunk has up to IDPF_NUM_FILTERS_PER_MSG entries. So except for the
>> last iteration which can be smaller, space for exactly
>> IDPF_NUM_FILTERS_PER_MSG entries is allocated.
>>
>> There is no need to free and reallocate a smaller array just for the last
>> iteration.
>>
>> This slightly simplifies the code and avoid an (unlikely) memory allocation
>> failure.
>>

Thanks, that is indeed an improvement.

>> Signed-off-by: Christophe JAILLET <christophe.jaillet@wanadoo.fr>
>> ---
>>   drivers/net/ethernet/intel/idpf/idpf_virtchnl.c | 7 +++++--
>>   1 file changed, 5 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
>> index 70986e12da28..b6f4b58e1094 100644
>> --- a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
>> +++ b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
>> @@ -3669,12 +3669,15 @@ int idpf_add_del_mac_filters(struct idpf_vport *vport,
>>   		entries_size = sizeof(struct virtchnl2_mac_addr) * num_entries;
>>   		buf_size = struct_size(ma_list, mac_addr_list, num_entries);
>>   
>> -		if (!ma_list || num_entries != IDPF_NUM_FILTERS_PER_MSG) {
>> -			kfree(ma_list);
>> +		if (!ma_list) {
>>   			ma_list = kzalloc(buf_size, GFP_ATOMIC);
>>   			if (!ma_list)
>>   				return -ENOMEM;
>>   		} else {
>> +			/* ma_list was allocated in the first iteration
>> +			 * so IDPF_NUM_FILTERS_PER_MSG entries are
>> +			 * available
>> +			 */
>>   			memset(ma_list, 0, buf_size);
>>   		}
> 
> It would be even nicer to move the ma_list allocation outside the loop:
> 
>          buf_size = struct_size(ma_list, mac_addr_list, IDPF_NUM_FILTERS_PER_MSG);
>          ma_list = kmalloc(buf_size, GFP_ATOMIC);

good point

I've opened whole function for inspection and it asks for even more,
as of now, we allocate an array in atomic context, just to have a copy
of some stuff from the spinlock-protected list.

It would be good to have allocation as pointed by Dan prior to iteration
and fill it on the fly, sending when new message would not fit plus once
at the end. Sending procedure is safe to be called under a spinlock.

CCing author; CCing Olek to ask if there are already some refactors that
would conflict with this.

> 
> regards,
> dan carpenter
>
Christophe JAILLET Aug. 26, 2024, 5:14 p.m. UTC | #4
Le 26/08/2024 à 11:15, Przemek Kitszel a écrit :
> On 8/23/24 11:10, Dan Carpenter wrote:
>> On Fri, Aug 23, 2024 at 08:23:29AM +0200, Christophe JAILLET wrote:
>>> In idpf_add_del_mac_filters(), filters are chunked up into multiple
>>> messages to avoid sending a control queue message buffer that is too 
>>> large.
>>>
>>> Each chunk has up to IDPF_NUM_FILTERS_PER_MSG entries. So except for the
>>> last iteration which can be smaller, space for exactly
>>> IDPF_NUM_FILTERS_PER_MSG entries is allocated.
>>>
>>> There is no need to free and reallocate a smaller array just for the 
>>> last
>>> iteration.
>>>
>>> This slightly simplifies the code and avoid an (unlikely) memory 
>>> allocation
>>> failure.
>>>
> 
> Thanks, that is indeed an improvement.
> 
>>> Signed-off-by: Christophe JAILLET <christophe.jaillet@wanadoo.fr>
>>> ---
>>>   drivers/net/ethernet/intel/idpf/idpf_virtchnl.c | 7 +++++--
>>>   1 file changed, 5 insertions(+), 2 deletions(-)
>>>
>>> diff --git a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c 
>>> b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
>>> index 70986e12da28..b6f4b58e1094 100644
>>> --- a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
>>> +++ b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
>>> @@ -3669,12 +3669,15 @@ int idpf_add_del_mac_filters(struct 
>>> idpf_vport *vport,
>>>           entries_size = sizeof(struct virtchnl2_mac_addr) * 
>>> num_entries;
>>>           buf_size = struct_size(ma_list, mac_addr_list, num_entries);
>>> -        if (!ma_list || num_entries != IDPF_NUM_FILTERS_PER_MSG) {
>>> -            kfree(ma_list);
>>> +        if (!ma_list) {
>>>               ma_list = kzalloc(buf_size, GFP_ATOMIC);
>>>               if (!ma_list)
>>>                   return -ENOMEM;
>>>           } else {
>>> +            /* ma_list was allocated in the first iteration
>>> +             * so IDPF_NUM_FILTERS_PER_MSG entries are
>>> +             * available
>>> +             */
>>>               memset(ma_list, 0, buf_size);
>>>           }
>>
>> It would be even nicer to move the ma_list allocation outside the loop:
>>
>>          buf_size = struct_size(ma_list, mac_addr_list, 
>> IDPF_NUM_FILTERS_PER_MSG);
>>          ma_list = kmalloc(buf_size, GFP_ATOMIC);
> 
> good point
> 
> I've opened whole function for inspection and it asks for even more,
> as of now, we allocate an array in atomic context, just to have a copy
> of some stuff from the spinlock-protected list.
> 
> It would be good to have allocation as pointed by Dan prior to iteration
> and fill it on the fly, sending when new message would not fit plus once
> at the end. Sending procedure is safe to be called under a spinlock.

If I understand correctly, you propose to remove the initial copy in 
mac_addr and hold &vport_config->mac_filter_list_lock till the end of 
the function?

That's it?

There is a wait_for_completion_timeout() in idpf_vc_xn_exec() and the 
default time-out is IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC	(60 * 1000)

So, should an issue occurs, and the time out run till the end, we could 
hold the 'mac_filter_list_lock' spinlock for up to 60 seconds?
Is that ok?


And if in asynch update mode, idpf_mac_filter_async_handler() also takes 
&vport_config->mac_filter_list_lock;. Could we dead-lock?


So, I'm not sure to understand what you propose, or the code in 
idpf_add_del_mac_filters() and co.

> 
> CCing author; CCing Olek to ask if there are already some refactors that
> would conflict with this.

I'll way a few days for these feedbacks and send a v2.

CJ

> 
>>
>> regards,
>> dan carpenter
>>
> 
> 
>
Przemek Kitszel Aug. 27, 2024, 6:58 a.m. UTC | #5
On 8/26/24 19:14, Christophe JAILLET wrote:
> Le 26/08/2024 à 11:15, Przemek Kitszel a écrit :
>> On 8/23/24 11:10, Dan Carpenter wrote:
>>> On Fri, Aug 23, 2024 at 08:23:29AM +0200, Christophe JAILLET wrote:

>>> It would be even nicer to move the ma_list allocation outside the loop:
>>>
>>>          buf_size = struct_size(ma_list, mac_addr_list, 
>>> IDPF_NUM_FILTERS_PER_MSG);
>>>          ma_list = kmalloc(buf_size, GFP_ATOMIC);
>>
>> good point
>>
>> I've opened whole function for inspection and it asks for even more,
>> as of now, we allocate an array in atomic context, just to have a copy
>> of some stuff from the spinlock-protected list.
>>
>> It would be good to have allocation as pointed by Dan prior to iteration
>> and fill it on the fly, sending when new message would not fit plus once
>> at the end. Sending procedure is safe to be called under a spinlock.
> 
> If I understand correctly, you propose to remove the initial copy in 
> mac_addr and hold &vport_config->mac_filter_list_lock till the end of 
> the function?
> 
> That's it?

You got it right. Thanks for your further analysis below.

> 
> There is a wait_for_completion_timeout() in idpf_vc_xn_exec() and the 
> default time-out is IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC    (60 * 1000)
> 
> So, should an issue occurs, and the time out run till the end, we could 
> hold the 'mac_filter_list_lock' spinlock for up to 60 seconds?
> Is that ok?

Messing with this list while we are not done processing does not sound
right either.
But the most concerning part for me with my proposition is that it could
be very slow to just "abort and unload the driver".

> 
> 
> And if in asynch update mode, idpf_mac_filter_async_handler() also takes 
> &vport_config->mac_filter_list_lock;. Could we dead-lock?

indeed looks so :( sorry

> 
> 
> So, I'm not sure to understand what you propose, or the code in 
> idpf_add_del_mac_filters() and co.
> 
>>
>> CCing author; CCing Olek to ask if there are already some refactors that
>> would conflict with this.
> 
> I'll way a few days for these feedbacks and send a v2.

Would be good to have simple memory cleanup first, and later (if at all)
to untangle our locks a bit.
Alexander Lobakin Aug. 27, 2024, 2:09 p.m. UTC | #6
From: Przemek Kitszel <przemyslaw.kitszel@intel.com>
Date: Tue, 27 Aug 2024 08:58:33 +0200

> On 8/26/24 19:14, Christophe JAILLET wrote:
>> Le 26/08/2024 à 11:15, Przemek Kitszel a écrit :
>>> On 8/23/24 11:10, Dan Carpenter wrote:
>>>> On Fri, Aug 23, 2024 at 08:23:29AM +0200, Christophe JAILLET wrote:
> 
>>>> It would be even nicer to move the ma_list allocation outside the loop:
>>>>
>>>>          buf_size = struct_size(ma_list, mac_addr_list,
>>>> IDPF_NUM_FILTERS_PER_MSG);
>>>>          ma_list = kmalloc(buf_size, GFP_ATOMIC);

[...]

>> So, I'm not sure to understand what you propose, or the code in
>> idpf_add_del_mac_filters() and co.
>>
>>>
>>> CCing author; CCing Olek to ask if there are already some refactors that
>>> would conflict with this.

I'm not aware of any MAC filter code refactors.

>>
>> I'll way a few days for these feedbacks and send a v2.
> 
> Would be good to have simple memory cleanup first, and later (if at all)
> to untangle our locks a bit.

Thanks,
Olek
diff mbox series

Patch

diff --git a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
index 70986e12da28..b6f4b58e1094 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
@@ -3669,12 +3669,15 @@  int idpf_add_del_mac_filters(struct idpf_vport *vport,
 		entries_size = sizeof(struct virtchnl2_mac_addr) * num_entries;
 		buf_size = struct_size(ma_list, mac_addr_list, num_entries);
 
-		if (!ma_list || num_entries != IDPF_NUM_FILTERS_PER_MSG) {
-			kfree(ma_list);
+		if (!ma_list) {
 			ma_list = kzalloc(buf_size, GFP_ATOMIC);
 			if (!ma_list)
 				return -ENOMEM;
 		} else {
+			/* ma_list was allocated in the first iteration
+			 * so IDPF_NUM_FILTERS_PER_MSG entries are
+			 * available
+			 */
 			memset(ma_list, 0, buf_size);
 		}