Subject: Re: [PATCH net-next v2 3/6] net: dcb: add new rewrite table

From: Petr Machata <[email-masked]>
Date: Wed, 18 Jan 2023 16:20:31 +0100

<[email-masked]> writes:

>> > +     rewr = nla_nest_start_noflag(skb, DCB_ATTR_DCB_REWR_TABLE);
>> > +     if (!rewr)
>> > +             return -EMSGSIZE;
>> 
>> This being new code, don't use _noflag please.
>
> Ack.
>
>> 
>> > +
>> > +     spin_lock_bh(&dcb_lock);
>> > +     list_for_each_entry(itr, &dcb_rewr_list, list) {
>> > +             if (itr->ifindex == netdev->ifindex) {
>> > +                     enum ieee_attrs_app type =
>> > +                             dcbnl_app_attr_type_get(itr->app.selector);
>> > +                     err = nla_put(skb, type, sizeof(itr->app), &itr->app);
>> > +                     if (err) {
>> > +                             spin_unlock_bh(&dcb_lock);
>> 
>> This should cancel the nest started above.
>
> Yes, it should.
>
>> 
>> I wonder if it would be cleaner in a separate function, so that there
>> can be a dedicated clean-up block to goto.
>
> Well yes. That would make sense if the function were reused for both APP
> and rewr.

I meant purely for to make the cleanup clear. The function would be
approximately:

static int dcbnl_ieee_fill_rewr(struct sk_buff *skb, struct net_device *netdev)
{
	struct dcb_app_type *itr;
	struct nlattr *rewr;
	int err;

	rewr = nla_nest_start_noflag(skb, DCB_ATTR_DCB_REWR_TABLE);
	if (!rewr)
		return -EMSGSIZE;

	spin_lock_bh(&dcb_lock);
	list_for_each_entry(itr, &dcb_rewr_list, list) {
		if (itr->ifindex == netdev->ifindex) {
			enum ieee_attrs_app type =
				dcbnl_app_attr_type_get(itr->app.selector);
			err = nla_put(skb, type, sizeof(itr->app), &itr->app);
			if (err)
				goto err_out;
		}
	}

	spin_unlock_bh(&dcb_lock);
	nla_nest_end(skb, rewr);
        return 0;

err_out:
	spin_unlock_bh(&dcb_lock);
	nla_nest_cancel(skb, rewr);
	return err;
}

Which uses an idiomatic style with the cleanup block at the end, instead
of stashing the individual cleanups before the return statement. I find
it easier to reason about.

But it's not a big deal. Your thing is readable just fine.

> Though in the APP equivalent code, nla_nest_start_noflag is used, and
> dcbnl_ops->getdcbx() is called. Is there any userspace side-effect of
> using nla_nest_start for APP too?

Yeah, the clients would be looking for code DCB_ATTR_IEEE_APP_TABLE, but
would get DCB_ATTR_IEEE_APP_TABLE | NLA_F_NESTED, and get confused.

For reuse between APP_TABLE and REWR_TABLE, you could just always call
_noflag in the helper, and pass the actual attribute in an argument.
Then the argument would be either DCB_ATTR_DCB_REWR_TABLE | NLA_F_NESTED,
or just plain DCB_ATTR_IEEE_APP_TABLE.

But that makes the code less clear, and I don't feel it brings much.

> dcbnl_ops->getdcbx() would then be left outside of the shared function.
> Does that call even have to hold the dcb_lock? Not as far as I can tell.
>
> something like:
>
> err = dcbnl_app_table_get(ndev, skb, &dcb_app_list,
> 			  DCB_ATTR_IEEE_APP_TABLE);
> if (err)
> 	return -EMSGSIZE;
>
> err = dcbnl_app_table_get(ndev, skb, &dcb_rewr_list,
> 			  DCB_ATTR_DCB_REWR_TABLE);
> if (err)
>         return -EMSGSIZE;
>
> if (netdev->dcbnl_ops->getdcbx)
> 	dcbx = netdev->dcbnl_ops->getdcbx(netdev); <-- without lock held
> else
> 	dcbx = -EOPNOTSUPP;
>
> Let me hear your thoughts.

Yeah, and the dcbx stuff is the added wrinkle.

Dunno, I'd not force it. This redundancy is not great, but the code is
small and easy to understand, so I find it's not an issue.