On 7/6/15 10:37 AM, Nikolay Aleksandrov wrote:
+static int vrf_add_slave(struct net_device *dev,
+                        struct net_device *port_dev)
+{
+       if (!dev || !port_dev || dev_net(dev) != dev_net(port_dev))
+               return -ENODEV;
+
+       if (!vrf_is_master(port_dev) && !vrf_is_slave(port_dev)) {
+               struct slave *s = kzalloc(sizeof(*s), GFP_KERNEL);
+               struct net_vrf *vrf = netdev_priv(dev);
+               struct slave_queue *queue = &vrf->queue;
+               bool is_running = netif_running(port_dev);
+               unsigned int flags = port_dev->flags;
+               int ret;
+
+               if (!s)
+                       return -ENOMEM;
+
+               s->dev = port_dev;
+
+               spin_lock_bh(&queue->lock);
+               __vrf_insert_slave(queue, s, dev);
+               spin_unlock_bh(&queue->lock);
+
+               port_dev->vrf_ptr = kmalloc(sizeof(*port_dev->vrf_ptr),
+                                           GFP_KERNEL);
+               if (!port_dev->vrf_ptr)
+                       return -ENOMEM;
                         ^^^^^^^^^
I believe you'll have a slave in the list with inconsistent state which could
even lead to null ptr derefernce if vrf_ptr is used, also __vrf_insert_slave
does dev_hold so the dev refcnt will be incorrect as well.

Right. Good catch, will fix.


+
+               port_dev->vrf_ptr->ifindex = dev->ifindex;
+               port_dev->vrf_ptr->tb_id = vrf->tb_id;
+
+               /* register the packet handler for slave ports */
+               ret = netdev_rx_handler_register(port_dev, vrf_handle_frame,
+                                                (void *)dev);
+               if (ret) {
+                       netdev_err(port_dev,
+                                  "Device %s failed to register rx_handler\n",
+                                  port_dev->name);
+                       kfree(port_dev->vrf_ptr);
+                       kfree(s);
+                       return ret;
                         ^^^^^^^^^^
The slave is being freed while on the list here, device's refcnt will be wrong 
etc.

ack. Will fix.


+               }
+
+               if (is_running) {
+                       ret = dev_change_flags(port_dev, flags & ~IFF_UP);
+                       if (ret < 0)
+                               goto out_fail;
+               }
+
+               ret = netdev_master_upper_dev_link(port_dev, dev);
+               if (ret < 0)
+                       goto out_fail;
+
+               if (is_running) {
+                       ret = dev_change_flags(port_dev, flags);
+                       if (ret < 0)
+                               goto out_fail;
+               }
+
+               port_dev->flags |= IFF_SLAVE;
+
+               return 0;
+
+out_fail:
+               spin_lock_bh(&queue->lock);
+               __vrf_kill_slave(queue, s);
+               spin_unlock_bh(&queue->lock);

__vrf_kill_slave() doesn't do upper device unlink and the device can be linked
if we fail in the dev_change_flags above.

will fix.


+
+               return ret;
+       }
+
+       return -EINVAL;
+}
^^^^
In my opinion the structure of the above function should change to something 
more
straightforward with proper exit labels and cleanup upon failure, also a level 
of
indentation can be avoided.

Sure. The indentation comes after the pointer checks so locals can be intialized when declared. Will work on the clean up/simplification for next rev.


+
+static int vrf_del_slave(struct net_device *dev,
+                        struct net_device *port_dev)
+{
+       struct net_vrf *vrf = netdev_priv(dev);
+       struct slave_queue *queue = &vrf->queue;
+       struct slave *slave = __vrf_find_slave_dev(queue, port_dev);
+       bool is_running = netif_running(port_dev);
+       unsigned int flags = port_dev->flags;
+       int ret = 0;

ret seems unused/unchecked in this function

It is used but not checked. I struggled with what to do on the error path. Do we want netdev_err() on a failure?


+
+       if (!slave)
+               return -EINVAL;
+
+       if (is_running)
+               ret = dev_change_flags(port_dev, flags & ~IFF_UP);
+
+       spin_lock_bh(&queue->lock);
+       __vrf_kill_slave(queue, slave);
+       spin_unlock_bh(&queue->lock);
+
+       netdev_upper_dev_unlink(port_dev, dev);
+
+       if (is_running)
+               ret = dev_change_flags(port_dev, flags);
+
+       return 0;
+}
+
+static int vrf_dev_init(struct net_device *dev)
+{
+       struct net_vrf *vrf = netdev_priv(dev);
+
+       spin_lock_init(&vrf->queue.lock);
+       INIT_LIST_HEAD(&vrf->queue.all_slaves);
+       vrf->queue.master_dev = dev;
+
+       dev->dstats = netdev_alloc_pcpu_stats(struct pcpu_dstats);
+       dev->flags  =  IFF_MASTER | IFF_NOARP;
+       if (!dev->dstats)
+               return -ENOMEM;
         ^^^^^
nit: I'd suggest moving the check after the allocation

agreed.

David
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to