+static int get_datapath(struct odp_datapath __user *uodp_datapath)
+{
+ struct nlattr *a[ODP_DP_ATTR_MAX + 1];
+ struct odp_datapath *odp_datapath;
+ struct datapath *dp;
+ struct sk_buff *skb;
+ int err;
+
+ skb = copy_datapath_from_user(uodp_datapath, a);
+ err = PTR_ERR(skb);
+ if (IS_ERR(skb))
+ goto exit;
+ odp_datapath = (struct odp_datapath *)skb->data;
+
+ mutex_lock(&dp_mutex);
+ dp = lookup_datapath(odp_datapath, a);
+ mutex_unlock(&dp_mutex);
+
+ err = PTR_ERR(dp);
+ if (IS_ERR(dp))
+ goto exit_free;
+
+ err = copy_datapath_to_user(uodp_datapath, dp, odp_datapath->total_len);
+ mutex_unlock(&dp->mutex);
+exit_free:
+ kfree_skb(skb);
+exit:
+ return err;
+}
+
+static int dump_datapath(struct odp_datapath __user *uodp_datapath)
+{
+ struct nlattr *a[ODP_DP_ATTR_MAX + 1];
+ struct odp_datapath *odp_datapath;
+ struct sk_buff *skb;
+ u32 dp_idx;
+ int err;
+
+ skb = copy_datapath_from_user(uodp_datapath, a);
+ err = PTR_ERR(skb);
+ if (IS_ERR(skb))
+ goto exit;
+ odp_datapath = (struct odp_datapath *)skb->data;
+
+ mutex_lock(&dp_mutex);
+ for (dp_idx = odp_datapath->dp_idx; dp_idx < ARRAY_SIZE(dps); dp_idx++) {
+ struct datapath *dp = get_dp(dp_idx);
+ if (!dp)
+ continue;
+
+ mutex_lock(&dp->mutex);
+ mutex_unlock(&dp_mutex);
+ err = copy_datapath_to_user(uodp_datapath, dp, odp_datapath->total_len);
+ mutex_unlock(&dp->mutex);
+ goto exit_free;
+ }
+ mutex_unlock(&dp_mutex);
+ err = -ENODEV;
+
+exit_free:
+ kfree_skb(skb);
+exit:
+ return err;
+}
+
+static const struct nla_policy vport_policy[ODP_VPORT_ATTR_MAX + 1] = {
+ [ODP_VPORT_ATTR_NAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ - 1 },
+ [ODP_VPORT_ATTR_PORT_NO] = { .type = NLA_U32 },
+ [ODP_VPORT_ATTR_TYPE] = { .type = NLA_U32 },
+ [ODP_VPORT_ATTR_STATS] = { .len = sizeof(struct rtnl_link_stats64) },
+ [ODP_VPORT_ATTR_ADDRESS] = { .len = ETH_ALEN },
+ [ODP_VPORT_ATTR_MTU] = { .type = NLA_U32 },
+ [ODP_VPORT_ATTR_OPTIONS] = { .type = NLA_NESTED },
+};
+
+static int copy_vport_to_user(void __user *dst, struct vport *vport, uint32_t total_len)
+{
+ struct odp_vport *odp_vport;
+ struct sk_buff *skb;
+ struct nlattr *nla;
+ int ifindex, iflink;
+ int err;
+
+ skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
+ err = -ENOMEM;
+ if (!skb)
+ goto exit;
+
+ rcu_read_lock();
+ odp_vport = (struct odp_vport*)__skb_put(skb, sizeof(struct odp_vport));
+ odp_vport->dp_idx = vport->dp->dp_idx;
+ odp_vport->total_len = total_len;
+
+ NLA_PUT_U32(skb, ODP_VPORT_ATTR_PORT_NO, vport->port_no);
+ NLA_PUT_U32(skb, ODP_VPORT_ATTR_TYPE, vport_get_type(vport));
+ NLA_PUT_STRING(skb, ODP_VPORT_ATTR_NAME, vport_get_name(vport));
+
+ nla = nla_reserve(skb, ODP_VPORT_ATTR_STATS, sizeof(struct rtnl_link_stats64));
+ if (!nla)
+ goto nla_put_failure;
+ if (vport_get_stats(vport, nla_data(nla)))
+ __skb_trim(skb, skb->len - nla->nla_len);
+
+ NLA_PUT(skb, ODP_VPORT_ATTR_ADDRESS, ETH_ALEN, vport_get_addr(vport));
+
+ NLA_PUT_U32(skb, ODP_VPORT_ATTR_MTU, vport_get_mtu(vport));
+
+ err = vport_get_options(vport, skb);
+
+ ifindex = vport_get_ifindex(vport);
+ if (ifindex > 0)
+ NLA_PUT_U32(skb, ODP_VPORT_ATTR_IFINDEX, ifindex);
+
+ iflink = vport_get_iflink(vport);
+ if (iflink > 0)
+ NLA_PUT_U32(skb, ODP_VPORT_ATTR_IFLINK, iflink);
+
+ err = -EMSGSIZE;
+ if (skb->len > total_len)
+ goto exit_unlock;
+
+ odp_vport->len = skb->len;
+ err = copy_to_user(dst, skb->data, skb->len) ? -EFAULT : 0;
+ goto exit_unlock;
+
+nla_put_failure:
+ err = -EMSGSIZE;
+exit_unlock:
+ rcu_read_unlock();
+ kfree_skb(skb);
+exit:
+ return err;
+}
+
+static struct sk_buff *copy_vport_from_user(struct odp_vport __user *uodp_vport,
+ struct nlattr *a[ODP_VPORT_ATTR_MAX + 1])
+{
+ struct odp_vport *odp_vport;
+ struct sk_buff *skb;
+ u32 len;
+ int err;
+
+ if (get_user(len, &uodp_vport->len))
+ return ERR_PTR(-EFAULT);
+ if (len < sizeof(struct odp_vport))
+ return ERR_PTR(-EINVAL);
+
+ skb = alloc_skb(len, GFP_KERNEL);
+ if (!skb)
+ return ERR_PTR(-ENOMEM);
+
+ err = -EFAULT;
+ if (copy_from_user(__skb_put(skb, len), uodp_vport, len))
+ goto error_free_skb;
+
+ odp_vport = (struct odp_vport *)skb->data;
+ err = -EINVAL;
+ if (odp_vport->len != len)
+ goto error_free_skb;
+
+ err = nla_parse(a, ODP_VPORT_ATTR_MAX, (struct nlattr *)(skb->data + sizeof(struct odp_vport)),
+ skb->len - sizeof(struct odp_vport), vport_policy);
+ if (err)
+ goto error_free_skb;
+
+ err = VERIFY_NUL_STRING(a[ODP_VPORT_ATTR_NAME], IFNAMSIZ - 1);
+ if (err)
+ goto error_free_skb;
+
+ return skb;
+
+error_free_skb:
+ kfree_skb(skb);
+ return ERR_PTR(err);
+}
+
+
+/* Called without any locks (or with RTNL lock).
+ * Returns holding vport->dp->mutex.
+ */
+static struct vport *lookup_vport(struct odp_vport *odp_vport,
+ struct nlattr *a[ODP_VPORT_ATTR_MAX + 1])
+{
+ struct datapath *dp;
+ struct vport *vport;
+
+ if (a[ODP_VPORT_ATTR_NAME]) {
+ int dp_idx, port_no;
+
+ retry:
+ vport_lock();
+ vport = vport_locate(nla_data(a[ODP_VPORT_ATTR_NAME]));
+ if (!vport) {
+ vport_unlock();
+ return ERR_PTR(-ENODEV);
+ }
+ dp_idx = vport->dp->dp_idx;
+ port_no = vport->port_no;
+ vport_unlock();
+
+ dp = get_dp_locked(dp_idx);
+ if (!dp)
+ goto retry;
+
+ vport = get_vport_protected(dp, port_no);
+ if (!vport ||
+ strcmp(vport_get_name(vport), nla_data(a[ODP_VPORT_ATTR_NAME]))) {
+ mutex_unlock(&dp->mutex);
+ goto retry;
+ }
+
+ return vport;
+ } else if (a[ODP_VPORT_ATTR_PORT_NO]) {
+ u32 port_no = nla_get_u32(a[ODP_VPORT_ATTR_PORT_NO]);
+
+ if (port_no >= DP_MAX_PORTS)
+ return ERR_PTR(-EINVAL);
+
+ dp = get_dp_locked(odp_vport->dp_idx);
+ if (!dp)
+ return ERR_PTR(-ENODEV);
+
+ vport = get_vport_protected(dp, port_no);
+ if (!vport) {
+ mutex_unlock(&dp->mutex);
+ return ERR_PTR(-ENOENT);
+ }
+ return vport;
+ } else
+ return ERR_PTR(-EINVAL);
+}
+
+static int change_vport(struct vport *vport, struct nlattr *a[ODP_VPORT_ATTR_MAX + 1])
+{
+ int err = 0;
+ if (a[ODP_VPORT_ATTR_STATS])
+ err = vport_set_stats(vport, nla_data(a[ODP_VPORT_ATTR_STATS]));
+ if (!err && a[ODP_VPORT_ATTR_ADDRESS])
+ err = vport_set_addr(vport, nla_data(a[ODP_VPORT_ATTR_ADDRESS]));
+ if (!err && a[ODP_VPORT_ATTR_MTU])
+ err = vport_set_mtu(vport, nla_get_u32(a[ODP_VPORT_ATTR_MTU]));
+ return err;
+}
+
+static int attach_vport(struct odp_vport __user *uodp_vport)
+{
+ struct nlattr *a[ODP_VPORT_ATTR_MAX + 1];
+ struct odp_vport *odp_vport;
+ struct vport_parms parms;
+ struct vport *vport;
+ struct sk_buff *skb;
+ struct datapath *dp;
+ u32 port_no;
+ int err;
+
+ skb = copy_vport_from_user(uodp_vport, a);
+ err = PTR_ERR(skb);
+ if (IS_ERR(skb))
+ goto exit;
+ odp_vport = (struct odp_vport *)skb->data;
+
+ err = -EINVAL;
+ if (!a[ODP_VPORT_ATTR_NAME] || !a[ODP_VPORT_ATTR_TYPE])
+ goto exit_kfree_skb;
+
+ rtnl_lock();
+
+ dp = get_dp_locked(odp_vport->dp_idx);
+ err = -ENODEV;
+ if (!dp)
+ goto exit_unlock_rtnl;
+
+ if (a[ODP_VPORT_ATTR_PORT_NO]) {
+ port_no = nla_get_u32(a[ODP_VPORT_ATTR_PORT_NO]);
+
+ err = -EFBIG;
+ if (port_no >= DP_MAX_PORTS)
+ goto exit_unlock_dp;
+
+ vport = get_vport_protected(dp, port_no);
+ err = -EBUSY;
+ if (vport)
+ goto exit_unlock_dp;
+ } else {
+ for (port_no = 1; ; port_no++) {
+ if (port_no >= DP_MAX_PORTS) {
+ err = -EFBIG;
+ goto exit_unlock_dp;
+ }
+ vport = get_vport_protected(dp, port_no);
+ if (!vport)
+ break;
+ }
+ }
+
+ parms.name = nla_data(a[ODP_VPORT_ATTR_NAME]);
+ parms.type = nla_get_u32(a[ODP_VPORT_ATTR_TYPE]);
+ parms.options = a[ODP_VPORT_ATTR_OPTIONS];
+ parms.dp = dp;
+ parms.port_no = port_no;
+
+ vport = new_vport(&parms);
+ err = PTR_ERR(vport);
+ if (IS_ERR(vport))
+ goto exit_unlock_dp;
+
+ set_internal_devs_mtu(dp);
+ dp_sysfs_add_if(vport);
+
+ err = change_vport(vport, a);
+ if (err) {
+ dp_detach_port(vport);
+ goto exit_unlock_dp;
+ }
+
+ err = copy_vport_to_user(uodp_vport, vport, odp_vport->total_len);
+
+exit_unlock_dp:
+ mutex_unlock(&dp->mutex);
+exit_unlock_rtnl:
+ rtnl_unlock();
+exit_kfree_skb:
+ kfree_skb(skb);
+exit:
+ return err;
+}
+
+static int set_vport(unsigned int cmd, struct odp_vport __user *uodp_vport)
+{
+ struct nlattr *a[ODP_VPORT_ATTR_MAX + 1];
+ struct vport *vport;
+ struct sk_buff *skb;
+ int err;
+
+ skb = copy_vport_from_user(uodp_vport, a);
+ err = PTR_ERR(skb);
+ if (IS_ERR(skb))
+ goto exit;
+
+ rtnl_lock();
+ vport = lookup_vport((struct odp_vport *)skb->data, a);
+ err = PTR_ERR(vport);
+ if (IS_ERR(vport))
+ goto exit_free;
+
+ err = 0;
+ if (a[ODP_VPORT_ATTR_OPTIONS])
+ err = vport_set_options(vport, a[ODP_VPORT_ATTR_OPTIONS]);
+ if (!err)
+ err = change_vport(vport, a);
+
+ mutex_unlock(&vport->dp->mutex);
+exit_free:
+ kfree_skb(skb);
+ rtnl_unlock();
+exit:
+ return err;