Thank you for your contribution! Sashiko AI review found 1 potential
issue(s) to consider:

Critical: 0 · High: 0 · Medium: 1 · Low: 0

- [Medium] `try_destroy_classifier()`
  (drivers/net/virtio_net.c:5871-5888) treats a failed…

--

virtio_net: Use existing classifier if possible

This adds a refcount to struct virtnet_classifier and makes
setup_classifier() scan ff->classifiers for an identical classifier
object, reusing it instead of creating a duplicate device object.  The
release side becomes try_destroy_classifier(), which only destroys the
device object when the last reference goes away.

> diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
> index 6217c5bcfee9f..6c07eab047afe 100644
> --- a/drivers/net/virtio_net.c
> +++ b/drivers/net/virtio_net.c

[ ... ]

> @@ -5823,11 +5825,24 @@ static bool validate_mask(const struct virtnet_ff *ff,
>       return false;
>  }
>  
> -static int setup_classifier(struct virtnet_ff *ff, struct virtnet_classifier 
> *c)
> +static int setup_classifier(struct virtnet_ff *ff,
> +                         struct virtnet_classifier **c)
>  {
> +     struct virtnet_classifier *tmp;
> +     unsigned long i;
>       int err;
>  
> -     err = xa_alloc(&ff->classifiers, &c->id, c,
> +     xa_for_each(&ff->classifiers, i, tmp) {
> +             if ((*c)->size == tmp->size &&
> +                 !memcmp(&tmp->obj, &(*c)->obj, tmp->size)) {
> +                     refcount_inc(&tmp->refcount);
> +                     kfree(*c);
> +                     *c = tmp;
> +                     goto out;
> +             }
> +     }
> +
> +     err = xa_alloc(&ff->classifiers, &(*c)->id, *c,
>                      XA_LIMIT(0, le32_to_cpu(ff->ff_caps->classifiers_limit) 
> - 1),
>                      GFP_KERNEL);
>       if (err)

[ ... ]

> @@ -5835,30 +5850,31 @@ static int setup_classifier(struct virtnet_ff *ff, 
> struct virtnet_classifier *c)
>  
> -static void destroy_classifier(struct virtnet_ff *ff,
> -                            u32 classifier_id)
> +static void try_destroy_classifier(struct virtnet_ff *ff, u32 classifier_id)
>  {
>       struct virtnet_classifier *c;
>       int err;
>  
>       c = xa_load(&ff->classifiers, classifier_id);
> -     if (c) {
> +     if (c && refcount_dec_and_test(&c->refcount)) {
>               err = virtio_admin_obj_destroy(ff->vdev,
>                                              
> VIRTIO_NET_RESOURCE_OBJ_FF_CLASSIFIER,
>                                              c->id,

[Severity: Medium]
With this patch ff->classifiers becomes the authoritative mirror of the
classifier objects that exist in the device, since setup_classifier() now
scans it with memcmp() to decide whether an equivalent device object is
already present.

Given that, is the unconditional cleanup in the tail of
try_destroy_classifier() still correct?

drivers/net/virtio_net.c:try_destroy_classifier() {
        c = xa_load(&ff->classifiers, classifier_id);
        if (c && refcount_dec_and_test(&c->refcount)) {
                err = virtio_admin_obj_destroy(ff->vdev,
                                               
VIRTIO_NET_RESOURCE_OBJ_FF_CLASSIFIER,
                                               c->id,
                                               VIRTIO_ADMIN_GROUP_TYPE_SELF,
                                               0);
                WARN_ON_ONCE(err);

                xa_erase(&ff->classifiers, c->id);
                kfree(c);
        }
}

virtio_admin_obj_destroy() is synchronous and returns non-zero when the
object was not removed, and it can fail without any device malfunction:

drivers/virtio/virtio_admin_commands.c:virtio_admin_obj_destroy() {
        if (!vdev->config->admin_cmd_exec)
                return -EOPNOTSUPP;

        data = kzalloc_obj(*data);
        if (!data)
                return -ENOMEM;
        ...
        err = vdev->config->admin_cmd_exec(vdev, &cmd);

After such a failure the device still holds the classifier object while the
driver has erased and freed its tracking entry.  Does this leak one of the
device's ff_caps->classifiers_limit slots permanently?

Two follow-on effects also look reachable from that state:

- the id is immediately available to xa_alloc() again, so a later
  virtio_admin_obj_create() for that id can be rejected by the device and
  userspace sees an ETHTOOL_SRXCLSRLINS failure the driver cannot explain.

- an identical classifier requested afterwards no longer matches anything in
  the xarray, so a second device object is created for it, which is the
  duplicate-classifier case the commit message says this change avoids.

Would it be better to leave the entry in ff->classifiers (and restore the
refcount) when virtio_admin_obj_destroy() reports an error?

Separately, in destroy_ethtool_rule() the FF_RULE destroy result is saved but
not acted on before the classifier reference is dropped:

drivers/net/virtio_net.c:destroy_ethtool_rule() {
        err = virtio_admin_obj_destroy(ff->vdev,
                                       VIRTIO_NET_RESOURCE_OBJ_FF_RULE,
                                       eth_rule->flow_spec.location,
                                       VIRTIO_ADMIN_GROUP_TYPE_SELF,
                                       0);

        xa_erase(&ff->ethtool.rules, eth_rule->flow_spec.location);
        try_destroy_classifier(ff, eth_rule->classifier_id);
        kfree(eth_rule);

        return err;
}

If the rule destroy fails, can the now shared classifier be destroyed on the
device while a device-side rule still references its id?

>  @@ -5885,7 +5901,7 @@ static int destroy_ethtool_rule(struct virtnet_ff *ff,
>                                      0);
>  
>       xa_erase(&ff->ethtool.rules, eth_rule->flow_spec.location);
> -     destroy_classifier(ff, eth_rule->classifier_id);
> +     try_destroy_classifier(ff, eth_rule->classifier_id);
>       kfree(eth_rule);
>  
>       return err;

[ ... ]

-- 
Sashiko AI review · 
https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260831161109.2999926-1-shshitrit%40nvidia.com

Reply via email to