/* 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)
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)
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);
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;
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);
.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);
/* 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)