brcompat: Refactor infrastructure for communication with ovs-brcompatd.
authorBen Pfaff <blp@nicira.com>
Wed, 15 Jul 2009 19:40:16 +0000 (12:40 -0700)
committerBen Pfaff <blp@nicira.com>
Thu, 16 Jul 2009 16:18:23 +0000 (09:18 -0700)
In an upcoming change, brcompat will need to receive bulk data from
ovs-brcompatd.  The existing brcompat infrastructure for receiving data
only supports returning an error code, so this commit adds the ability to
receive an arbitrary sk_buff and updates the existing users to handle it.

datapath/brcompat.c

index 47805bc..d945e4e 100644 (file)
@@ -40,10 +40,11 @@ static DEFINE_MUTEX(brc_serial);
 /* Userspace communication. */
 static DEFINE_SPINLOCK(brc_lock);    /* Ensure atomic access to these vars. */
 static DECLARE_COMPLETION(brc_done); /* Userspace signaled operation done? */
-static int brc_err;                 /* Error code from userspace. */
+static struct sk_buff *brc_reply;    /* Reply from userspace. */
 static u32 brc_seq;                 /* Sequence number for current op. */
 
-static int brc_send_command(const char *bridge, const char *port, int op);
+static struct sk_buff *brc_send_command(struct sk_buff *, struct nlattr **attrs);
+static int brc_send_simple_command(struct sk_buff *);
 
 static int
 get_dp_ifindices(int *indices, int num)
@@ -75,16 +76,55 @@ get_port_ifindices(struct datapath *dp, int *ifindices, int num)
        rcu_read_unlock();
 }
 
+static struct sk_buff *
+brc_make_request(int op, const char *bridge, const char *port)
+{
+       struct sk_buff *skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       if (!skb)
+               goto error;
+
+       genlmsg_put(skb, 0, 0, &brc_genl_family, 0, op);
+       NLA_PUT_STRING(skb, BRC_GENL_A_DP_NAME, bridge);
+       if (port)
+               NLA_PUT_STRING(skb, BRC_GENL_A_PORT_NAME, port);
+       return skb;
+
+nla_put_failure:
+       kfree_skb(skb);
+error:
+       return NULL;
+}
+
+static int brc_send_simple_command(struct sk_buff *request)
+{
+       struct nlattr *attrs[BRC_GENL_A_MAX + 1];
+       struct sk_buff *reply;
+       int error;
+
+       reply = brc_send_command(request, attrs);
+       if (IS_ERR(reply))
+               return PTR_ERR(reply);
+
+       error = nla_get_u32(attrs[BRC_GENL_A_ERR_CODE]);
+       kfree_skb(reply);
+       return -error;
+}
+
 static int brc_add_del_bridge(char __user *uname, int add)
 {
+       struct sk_buff *request;
        char name[IFNAMSIZ];
 
        if (copy_from_user(name, uname, IFNAMSIZ))
                return -EFAULT;
 
        name[IFNAMSIZ - 1] = 0;
-       return brc_send_command(name, NULL,
-                               add ? BRC_GENL_C_DP_ADD : BRC_GENL_C_DP_DEL);
+       request = brc_make_request(add ? BRC_GENL_C_DP_ADD : BRC_GENL_C_DP_DEL,
+                                  name, NULL);
+       if (!request)
+               return -ENOMEM;
+
+       return brc_send_simple_command(request);
 }
 
 static int brc_get_bridges(int __user *uindices, int n)
@@ -154,8 +194,8 @@ brc_ioctl_deviceless_stub(struct net *net, unsigned int cmd, void __user *uarg)
 static int
 brc_add_del_port(struct net_device *dev, int port_ifindex, int add)
 {
+       struct sk_buff *request;
        struct net_device *port;
-       char dev_name[IFNAMSIZ], port_name[IFNAMSIZ];
        int err;
 
        port = __dev_get_by_index(&init_net, port_ifindex);
@@ -163,13 +203,14 @@ brc_add_del_port(struct net_device *dev, int port_ifindex, int add)
                return -EINVAL;
 
        /* Save name of dev and port because there's a race between the
-        * rtnl_unlock() and the brc_send_command(). */
-       strcpy(dev_name, dev->name);
-       strcpy(port_name, port->name);
+        * rtnl_unlock() and the brc_send_simple_command(). */
+       request = brc_make_request(add ? BRC_GENL_C_PORT_ADD : BRC_GENL_C_PORT_DEL,
+                                  dev->name, port->name);
+       if (!request)
+               return -ENOMEM;
 
        rtnl_unlock();
-       err = brc_send_command(dev_name, port_name,
-                              add ? BRC_GENL_C_PORT_ADD : BRC_GENL_C_PORT_DEL);
+       err = brc_send_simple_command(request);
        rtnl_lock();
 
        return err;
@@ -330,12 +371,22 @@ brc_genl_dp_result(struct sk_buff *skb, struct genl_info *info)
        if (!info->attrs[BRC_GENL_A_ERR_CODE])
                return -EINVAL;
 
+       skb = skb_clone(skb, GFP_KERNEL);
+       if (!skb)
+               return -ENOMEM;
+
        spin_lock_irqsave(&brc_lock, flags);
        if (brc_seq == info->snd_seq) {
-               brc_err = nla_get_u32(info->attrs[BRC_GENL_A_ERR_CODE]);
+               brc_seq++;
+
+               if (brc_reply)
+                       kfree_skb(brc_reply);
+               brc_reply = skb;
+
                complete(&brc_done);
                err = 0;
        } else {
+               kfree_skb(skb);
                err = -ESTALE;
        }
        spin_unlock_irqrestore(&brc_lock, flags);
@@ -359,11 +410,10 @@ static struct genl_ops brc_genl_ops_set_proc = {
        .dumpit = NULL
 };
 
-static int brc_send_command(const char *bridge, const char *port, int op)
+static struct sk_buff *brc_send_command(struct sk_buff *request, struct nlattr **attrs)
 {
        unsigned long int flags;
-       struct sk_buff *skb;
-       void *data;
+       struct sk_buff *reply;
        int error;
 
        mutex_lock(&brc_serial);
@@ -371,41 +421,41 @@ static int brc_send_command(const char *bridge, const char *port, int op)
        /* Increment sequence number first, so that we ignore any replies
         * to stale requests. */
        spin_lock_irqsave(&brc_lock, flags);
-       brc_seq++;
+       nlmsg_hdr(request)->nlmsg_seq = ++brc_seq;
        INIT_COMPLETION(brc_done);
        spin_unlock_irqrestore(&brc_lock, flags);
 
-       /* Compose message. */
-       skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
-       error = -ENOMEM;
-       if (skb == NULL)
-               goto exit_unlock;
-       data = genlmsg_put(skb, 0, brc_seq, &brc_genl_family, 0, op);
-
-       NLA_PUT_STRING(skb, BRC_GENL_A_DP_NAME, bridge);
-       if (port)
-               NLA_PUT_STRING(skb, BRC_GENL_A_PORT_NAME, port);
-
-       genlmsg_end(skb, data);
+       nlmsg_end(request, nlmsg_hdr(request));
 
        /* Send message. */
-       error = genlmsg_multicast(skb, 0, brc_mc_group.id, GFP_KERNEL);
+       error = genlmsg_multicast(request, 0, brc_mc_group.id, GFP_KERNEL);
        if (error < 0)
-               goto exit_unlock;
+               goto error;
 
        /* Wait for reply. */
        error = -ETIMEDOUT;
        if (!wait_for_completion_timeout(&brc_done, BRC_TIMEOUT))
-               goto exit_unlock;
+               goto error;
 
-       error = -brc_err;
-       goto exit_unlock;
+       /* Grab reply. */
+       spin_lock_irqsave(&brc_lock, flags);
+       reply = brc_reply;
+       brc_reply = NULL;
+       spin_unlock_irqrestore(&brc_lock, flags);
 
-nla_put_failure:
-       kfree_skb(skb);
-exit_unlock:
        mutex_unlock(&brc_serial);
-       return error;
+
+       /* Re-parse message.  Can't fail, since it parsed correctly once
+        * already. */
+       error = nlmsg_parse(nlmsg_hdr(reply), GENL_HDRLEN,
+                           attrs, BRC_GENL_A_MAX, brc_genl_policy);
+       WARN_ON(error);
+
+       return reply;
+
+error:
+       mutex_unlock(&brc_serial);
+       return ERR_PTR(error);
 }
 
 int brc_add_dp(struct datapath *dp)