datapath: Change ODP_FLOW_GET to retrieve only a single flow at a time.
[sliver-openvswitch.git] / datapath / datapath.c
index 3f108c0..0f59e9a 100644 (file)
@@ -69,7 +69,7 @@ EXPORT_SYMBOL(dp_ioctl_hook);
 static struct datapath __rcu *dps[ODP_MAX];
 static DEFINE_MUTEX(dp_mutex);
 
-static int new_vport(struct datapath *, struct odp_port *, int port_no);
+static struct vport *new_vport(const struct vport_parms *);
 
 /* Must be called with rcu_read_lock or dp_mutex. */
 struct datapath *get_dp(int dp_idx)
@@ -208,8 +208,9 @@ static struct kobj_type dp_ktype = {
 
 static int create_dp(int dp_idx, const char __user *devnamep)
 {
-       struct odp_port internal_dev_port;
+       struct vport_parms parms;
        char devname[IFNAMSIZ];
+       struct vport *vport;
        struct datapath *dp;
        int err;
        int i;
@@ -264,11 +265,14 @@ static int create_dp(int dp_idx, const char __user *devnamep)
                goto err_free_dp;
 
        /* Set up our datapath device. */
-       BUILD_BUG_ON(sizeof(internal_dev_port.devname) != sizeof(devname));
-       strcpy(internal_dev_port.devname, devname);
-       strcpy(internal_dev_port.type, "internal");
-       err = new_vport(dp, &internal_dev_port, ODPP_LOCAL);
-       if (err) {
+       parms.name = devname;
+       parms.type = ODP_VPORT_TYPE_INTERNAL;
+       parms.options = NULL;
+       parms.dp = dp;
+       parms.port_no = ODPP_LOCAL;
+       vport = new_vport(&parms);
+       if (IS_ERR(vport)) {
+               err = PTR_ERR(vport);
                if (err == -EBUSY)
                        err = -EEXIST;
 
@@ -355,74 +359,23 @@ out:
 }
 
 /* Called with RTNL lock and dp->mutex. */
-static int new_vport(struct datapath *dp, struct odp_port *odp_port, int port_no)
+static struct vport *new_vport(const struct vport_parms *parms)
 {
-       struct vport_parms parms;
        struct vport *vport;
 
-       parms.name = odp_port->devname;
-       parms.type = odp_port->type;
-       parms.config = odp_port->config;
-       parms.dp = dp;
-       parms.port_no = port_no;
-
        vport_lock();
-       vport = vport_add(&parms);
-       vport_unlock();
-
-       if (IS_ERR(vport))
-               return PTR_ERR(vport);
-
-       rcu_assign_pointer(dp->ports[port_no], vport);
-       list_add_rcu(&vport->node, &dp->port_list);
-       dp->n_ports++;
-
-       dp_ifinfo_notify(RTM_NEWLINK, vport);
-
-       return 0;
-}
-
-static int attach_port(int dp_idx, struct odp_port __user *portp)
-{
-       struct datapath *dp;
-       struct odp_port port;
-       int port_no;
-       int err;
-
-       err = -EFAULT;
-       if (copy_from_user(&port, portp, sizeof(port)))
-               goto out;
-       port.devname[IFNAMSIZ - 1] = '\0';
-       port.type[VPORT_TYPE_SIZE - 1] = '\0';
-
-       rtnl_lock();
-       dp = get_dp_locked(dp_idx);
-       err = -ENODEV;
-       if (!dp)
-               goto out_unlock_rtnl;
-
-       for (port_no = 1; port_no < DP_MAX_PORTS; port_no++)
-               if (!dp->ports[port_no])
-                       goto got_port_no;
-       err = -EFBIG;
-       goto out_unlock_dp;
+       vport = vport_add(parms);
+       if (!IS_ERR(vport)) {
+               struct datapath *dp = parms->dp;
 
-got_port_no:
-       err = new_vport(dp, &port, port_no);
-       if (err)
-               goto out_unlock_dp;
-
-       set_internal_devs_mtu(dp);
-       dp_sysfs_add_if(get_vport_protected(dp, port_no));
+               rcu_assign_pointer(dp->ports[parms->port_no], vport);
+               list_add_rcu(&vport->node, &dp->port_list);
 
-       err = put_user(port_no, &portp->port);
+               dp_ifinfo_notify(RTM_NEWLINK, vport);
+       }
+       vport_unlock();
 
-out_unlock_dp:
-       mutex_unlock(&dp->mutex);
-out_unlock_rtnl:
-       rtnl_unlock();
-out:
-       return err;
+       return vport;
 }
 
 int dp_detach_port(struct vport *p)
@@ -436,7 +389,6 @@ int dp_detach_port(struct vport *p)
        dp_ifinfo_notify(RTM_DELLINK, p);
 
        /* First drop references to device. */
-       p->dp->n_ports--;
        list_del_rcu(&p->node);
        rcu_assign_pointer(p->dp->ports[p->port_no], NULL);
 
@@ -448,37 +400,6 @@ int dp_detach_port(struct vport *p)
        return err;
 }
 
-static int detach_port(int dp_idx, int port_no)
-{
-       struct vport *p;
-       struct datapath *dp;
-       int err;
-
-       err = -EINVAL;
-       if (port_no < 0 || port_no >= DP_MAX_PORTS || port_no == ODPP_LOCAL)
-               goto out;
-
-       rtnl_lock();
-       dp = get_dp_locked(dp_idx);
-       err = -ENODEV;
-       if (!dp)
-               goto out_unlock_rtnl;
-
-       p = get_vport_protected(dp, port_no);
-       err = -ENOENT;
-       if (!p)
-               goto out_unlock_dp;
-
-       err = dp_detach_port(p);
-
-out_unlock_dp:
-       mutex_unlock(&dp->mutex);
-out_unlock_rtnl:
-       rtnl_unlock();
-out:
-       return err;
-}
-
 /* Must be called with rcu_read_lock. */
 void dp_process_received_packet(struct vport *p, struct sk_buff *skb)
 {
@@ -858,7 +779,6 @@ static void get_stats(struct sw_flow *flow, struct odp_flow_stats *stats)
        stats->n_bytes = flow->byte_count;
        stats->reserved = 0;
        stats->tcp_flags = flow->tcp_flags;
-       stats->error = 0;
 }
 
 static void clear_stats(struct sw_flow *flow)
@@ -1096,55 +1016,25 @@ static int del_flow(struct datapath *dp, struct odp_flow __user *ufp)
        return error;
 }
 
-static int do_query_flows(struct datapath *dp, const struct odp_flowvec *flowvec)
+static int query_flow(struct datapath *dp, struct odp_flow __user *uflow)
 {
        struct tbl *table = get_table_protected(dp);
-       u32 i;
-
-       for (i = 0; i < flowvec->n_flows; i++) {
-               struct odp_flow __user *ufp = (struct odp_flow __user __force *)&flowvec->flows[i];
-               struct sw_flow_key key;
-               struct odp_flow uf;
-               struct tbl_node *flow_node;
-               int error;
-
-               if (copy_from_user(&uf, ufp, sizeof(uf)))
-                       return -EFAULT;
-
-               error = flow_copy_from_user(&key, (const struct nlattr __force __user *)uf.key, uf.key_len);
-               if (error)
-                       return error;
-
-               flow_node = tbl_lookup(table, &uf.key, flow_hash(&key), flow_cmp);
-               if (!flow_node)
-                       error = put_user(ENOENT, &ufp->stats.error);
-               else
-                       error = answer_query(dp, flow_cast(flow_node), uf.flags, ufp);
-               if (error)
-                       return -EFAULT;
-       }
-       return flowvec->n_flows;
-}
-
-static int do_flowvec_ioctl(struct datapath *dp, unsigned long argp,
-                           int (*function)(struct datapath *,
-                                           const struct odp_flowvec *))
-{
-       struct odp_flowvec __user *uflowvec;
-       struct odp_flowvec flowvec;
-       int retval;
+       struct tbl_node *flow_node;
+       struct sw_flow_key key;
+       struct odp_flow flow;
+       int error;
 
-       uflowvec = (struct odp_flowvec __user *)argp;
-       if (copy_from_user(&flowvec, uflowvec, sizeof(flowvec)))
+       if (copy_from_user(&flow, uflow, sizeof(flow)))
                return -EFAULT;
 
-       if (flowvec.n_flows > INT_MAX / sizeof(struct odp_flow))
-               return -EINVAL;
+       error = flow_copy_from_user(&key, (const struct nlattr __force __user *)flow.key, flow.key_len);
+       if (error)
+               return error;
 
-       retval = function(dp, &flowvec);
-       return (retval < 0 ? retval
-               : retval == flowvec.n_flows ? 0
-               : put_user(retval, &uflowvec->n_flows));
+       flow_node = tbl_lookup(table, &flow.key, flow_hash(&key), flow_cmp);
+       if (!flow_node)
+               return -ENOENT;
+       return answer_query(dp, flow_cast(flow_node), flow.flags, uflow);
 }
 
 static struct sw_flow *do_dump_flow(struct datapath *dp, u32 __user *state)
@@ -1276,15 +1166,9 @@ static int execute_packet(struct datapath *dp, const struct odp_execute __user *
 
 static int get_dp_stats(struct datapath *dp, struct odp_stats __user *statsp)
 {
-       struct tbl *table = get_table_protected(dp);
        struct odp_stats stats;
        int i;
 
-       stats.n_flows = tbl_count(table);
-       stats.cur_capacity = tbl_n_buckets(table);
-       stats.max_capacity = TBL_MAX_BUCKETS;
-       stats.n_ports = dp->n_ports;
-       stats.max_ports = DP_MAX_PORTS;
        stats.n_frags = stats.n_hit = stats.n_missed = stats.n_lost = 0;
        for_each_possible_cpu(i) {
                const struct dp_stats_percpu *percpu_stats;
@@ -1303,8 +1187,6 @@ static int get_dp_stats(struct datapath *dp, struct odp_stats __user *statsp)
                stats.n_missed += local_stats.n_missed;
                stats.n_lost += local_stats.n_lost;
        }
-       stats.max_miss_queue = DP_MAX_QUEUE_LEN;
-       stats.max_action_queue = DP_MAX_QUEUE_LEN;
        return copy_to_user(statsp, &stats, sizeof(stats)) ? -EFAULT : 0;
 }
 
@@ -1349,95 +1231,408 @@ void set_internal_devs_mtu(const struct datapath *dp)
        }
 }
 
-static void compose_odp_port(const struct vport *vport, struct odp_port *odp_port)
+static int get_listen_mask(const struct file *f)
+{
+       return (long)f->private_data;
+}
+
+static void set_listen_mask(struct file *f, int listen_mask)
+{
+       f->private_data = (void*)(long)listen_mask;
+}
+
+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();
-       strncpy(odp_port->devname, vport_get_name(vport), sizeof(odp_port->devname));
-       strncpy(odp_port->type, vport_get_type(vport), sizeof(odp_port->type));
-       vport_get_config(vport, odp_port->config);
-       odp_port->port = vport->port_no;
-       odp_port->dp_idx = vport->dp->dp_idx;
+       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 int query_port(int dp_idx, struct odp_port __user *uport)
+static struct sk_buff *copy_vport_from_user(struct odp_vport __user *uodp_vport,
+                                           struct nlattr *a[ODP_VPORT_ATTR_MAX + 1])
 {
-       struct odp_port port;
+       struct odp_vport *odp_vport;
+       struct sk_buff *skb;
+       u32 len;
+       int err;
 
-       if (copy_from_user(&port, uport, sizeof(port)))
-               return -EFAULT;
+       if (get_user(len, &uodp_vport->len))
+               return ERR_PTR(-EFAULT);
+       if (len < sizeof(struct odp_vport))
+               return ERR_PTR(-EINVAL);
 
-       if (port.devname[0]) {
-               struct vport *vport;
+       skb = alloc_skb(len, GFP_KERNEL);
+       if (!skb)
+               return ERR_PTR(-ENOMEM);
 
-               port.devname[IFNAMSIZ - 1] = '\0';
+       err = -EFAULT;
+       if (copy_from_user(__skb_put(skb, len), uodp_vport, len))
+               goto error_free_skb;
 
-               vport_lock();
-               vport = vport_locate(port.devname);
-               if (vport)
-                       compose_odp_port(vport, &port);
-               vport_unlock();
+       odp_vport = (struct odp_vport *)skb->data;
+       err = -EINVAL;
+       if (odp_vport->len != len)
+               goto error_free_skb;
 
-               if (!vport)
-                       return -ENODEV;
-       } else {
-               struct vport *vport;
-               struct datapath *dp;
+       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);
+}
 
-               if (port.port >= DP_MAX_PORTS)
-                       return -EINVAL;
+
+/* 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)
-                       return -ENODEV;
+                       goto retry;
 
-               vport = get_vport_protected(dp, port.port);
-               if (vport)
-                       compose_odp_port(vport, &port);
-               mutex_unlock(&dp->mutex);
+               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;
+               }
 
-               if (!vport)
-                       return -ENOENT;
-       }
+               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);
 
-       return copy_to_user(uport, &port, sizeof(struct odp_port));
+               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 do_dump_port(struct datapath *dp, struct odp_vport_dump *dump)
+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;
 
-       for (port_no = dump->port_no; port_no < DP_MAX_PORTS; port_no++) {
-               struct vport *vport = get_vport_protected(dp, port_no);
-               if (vport) {
-                       struct odp_port odp_port;
+       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]);
 
-                       compose_odp_port(vport, &odp_port);
-                       return copy_to_user((struct odp_port __force __user*)dump->port, &odp_port, sizeof(struct odp_port));
+               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;
                }
        }
 
-       return put_user('\0', (char __force __user*)&dump->port->devname[0]);
+       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 dump_port(struct datapath *dp, struct odp_vport_dump __user *udump)
+static int set_vport(unsigned int cmd, struct odp_vport __user *uodp_vport)
 {
-       struct odp_vport_dump dump;
+       struct nlattr *a[ODP_VPORT_ATTR_MAX + 1];
+       struct vport *vport;
+       struct sk_buff *skb;
+       int err;
 
-       if (copy_from_user(&dump, udump, sizeof(dump)))
-               return -EFAULT;
+       skb = copy_vport_from_user(uodp_vport, a);
+       err = PTR_ERR(skb);
+       if (IS_ERR(skb))
+               goto exit;
 
-       return do_dump_port(dp, &dump);
+       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;
 }
 
-static int get_listen_mask(const struct file *f)
+static int del_vport(unsigned int cmd, struct odp_vport __user *uodp_vport)
 {
-       return (long)f->private_data;
+       struct nlattr *a[ODP_VPORT_ATTR_MAX + 1];
+       struct datapath *dp;
+       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;
+       dp = vport->dp;
+
+       err = -EINVAL;
+       if (vport->port_no == ODPP_LOCAL)
+               goto exit_free;
+
+       err = dp_detach_port(vport);
+       mutex_unlock(&dp->mutex);
+exit_free:
+       kfree_skb(skb);
+       rtnl_unlock();
+exit:
+       return err;
 }
 
-static void set_listen_mask(struct file *f, int listen_mask)
+static int get_vport(struct odp_vport __user *uodp_vport)
 {
-       f->private_data = (void*)(long)listen_mask;
+       struct nlattr *a[ODP_VPORT_ATTR_MAX + 1];
+       struct odp_vport *odp_vport;
+       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;
+       odp_vport = (struct odp_vport *)skb->data;
+
+       vport = lookup_vport(odp_vport, a);
+       err = PTR_ERR(vport);
+       if (IS_ERR(vport))
+               goto exit_free;
+
+       err = copy_vport_to_user(uodp_vport, vport, odp_vport->total_len);
+       mutex_unlock(&vport->dp->mutex);
+exit_free:
+       kfree_skb(skb);
+exit:
+       return err;
+}
+
+static int dump_vport(struct odp_vport __user *uodp_vport)
+{
+       struct nlattr *a[ODP_VPORT_ATTR_MAX + 1];
+       struct odp_vport *odp_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;
+
+       dp = get_dp_locked(odp_vport->dp_idx);
+       err = -ENODEV;
+       if (!dp)
+               goto exit_free;
+
+       port_no = 0;
+       if (a[ODP_VPORT_ATTR_PORT_NO])
+               port_no = nla_get_u32(a[ODP_VPORT_ATTR_PORT_NO]);
+       for (; port_no < DP_MAX_PORTS; port_no++) {
+               struct vport *vport = get_vport_protected(dp, port_no);
+               if (vport) {
+                       err = copy_vport_to_user(uodp_vport, vport, odp_vport->total_len);
+                       goto exit_unlock_dp;
+               }
+       }
+       err = -ENODEV;
+
+exit_unlock_dp:
+       mutex_unlock(&dp->mutex);
+exit_free:
+       kfree_skb(skb);
+exit:
+       return err;
 }
 
 static long openvswitch_ioctl(struct file *f, unsigned int cmd,
@@ -1445,7 +1640,7 @@ static long openvswitch_ioctl(struct file *f, unsigned int cmd,
 {
        int dp_idx = iminor(f->f_dentry->d_inode);
        struct datapath *dp;
-       int drop_frags, listeners, port_no;
+       int drop_frags, listeners;
        unsigned int sflow_probability;
        int err;
 
@@ -1459,46 +1654,24 @@ static long openvswitch_ioctl(struct file *f, unsigned int cmd,
                err = destroy_dp(dp_idx);
                goto exit;
 
-       case ODP_VPORT_ATTACH:
-               err = attach_port(dp_idx, (struct odp_port __user *)argp);
-               goto exit;
-
-       case ODP_VPORT_DETACH:
-               err = get_user(port_no, (int __user *)argp);
-               if (!err)
-                       err = detach_port(dp_idx, port_no);
-               goto exit;
-
-       case ODP_VPORT_QUERY:
-               err = query_port(dp_idx, (struct odp_port __user *)argp);
-               goto exit;
-
-       case ODP_VPORT_MOD:
-               err = vport_user_mod((struct odp_port __user *)argp);
-               goto exit;
-
-       case ODP_VPORT_STATS_GET:
-               err = vport_user_stats_get((struct odp_vport_stats_req __user *)argp);
-               goto exit;
-
-       case ODP_VPORT_STATS_SET:
-               err = vport_user_stats_set((struct odp_vport_stats_req __user *)argp);
+       case ODP_VPORT_NEW:
+               err = attach_vport((struct odp_vport __user *)argp);
                goto exit;
 
-       case ODP_VPORT_ETHER_GET:
-               err = vport_user_ether_get((struct odp_vport_ether __user *)argp);
+       case ODP_VPORT_GET:
+               err = get_vport((struct odp_vport __user *)argp);
                goto exit;
 
-       case ODP_VPORT_ETHER_SET:
-               err = vport_user_ether_set((struct odp_vport_ether __user *)argp);
+       case ODP_VPORT_DEL:
+               err = del_vport(cmd, (struct odp_vport __user *)argp);
                goto exit;
 
-       case ODP_VPORT_MTU_GET:
-               err = vport_user_mtu_get((struct odp_vport_mtu __user *)argp);
+       case ODP_VPORT_SET:
+               err = set_vport(cmd, (struct odp_vport __user *)argp);
                goto exit;
 
-       case ODP_VPORT_MTU_SET:
-               err = vport_user_mtu_set((struct odp_vport_mtu __user *)argp);
+       case ODP_VPORT_DUMP:
+               err = dump_vport((struct odp_vport __user *)argp);
                goto exit;
        }
 
@@ -1552,10 +1725,6 @@ static long openvswitch_ioctl(struct file *f, unsigned int cmd,
                        dp->sflow_probability = sflow_probability;
                break;
 
-       case ODP_VPORT_DUMP:
-               err = dump_port(dp, (struct odp_vport_dump __user *)argp);
-               break;
-
        case ODP_FLOW_FLUSH:
                err = flush_flows(dp);
                break;
@@ -1569,7 +1738,7 @@ static long openvswitch_ioctl(struct file *f, unsigned int cmd,
                break;
 
        case ODP_FLOW_GET:
-               err = do_flowvec_ioctl(dp, argp, do_query_flows);
+               err = query_flow(dp, (struct odp_flow __user *)argp);
                break;
 
        case ODP_FLOW_DUMP:
@@ -1600,20 +1769,6 @@ static int dp_has_packet_of_interest(struct datapath *dp, int listeners)
 }
 
 #ifdef CONFIG_COMPAT
-static int compat_dump_port(struct datapath *dp, struct compat_odp_vport_dump __user *compat)
-{
-       struct odp_vport_dump dump;
-       compat_uptr_t port;
-
-       if (!access_ok(VERIFY_READ, compat, sizeof(struct compat_odp_vport_dump)) ||
-           __get_user(port, &compat->port) ||
-           __get_user(dump.port_no, &compat->port_no))
-               return -EFAULT;
-
-       dump.port = (struct odp_port __force *)compat_ptr(port);
-       return do_dump_port(dp, &dump);
-}
-
 static int compat_get_flow(struct odp_flow *flow, const struct compat_odp_flow __user *compat)
 {
        compat_uptr_t key, actions;
@@ -1684,37 +1839,25 @@ static int compat_del_flow(struct datapath *dp, struct compat_odp_flow __user *u
        return error;
 }
 
-static int compat_query_flows(struct datapath *dp,
-                             struct compat_odp_flow __user *flows,
-                             u32 n_flows)
+static int compat_query_flow(struct datapath *dp, struct compat_odp_flow __user *uflow)
 {
        struct tbl *table = get_table_protected(dp);
-       u32 i;
-
-       for (i = 0; i < n_flows; i++) {
-               struct compat_odp_flow __user *ufp = &flows[i];
-               struct odp_flow uf;
-               struct tbl_node *flow_node;
-               struct sw_flow_key key;
-               int error;
+       struct tbl_node *flow_node;
+       struct sw_flow_key key;
+       struct odp_flow flow;
+       int error;
 
-               if (compat_get_flow(&uf, ufp))
-                       return -EFAULT;
+       if (compat_get_flow(&flow, uflow))
+               return -EFAULT;
 
-               error = flow_copy_from_user(&key, (const struct nlattr __force __user *) uf.key, uf.key_len);
-               if (error)
-                       return error;
+       error = flow_copy_from_user(&key, (const struct nlattr __force __user *)flow.key, flow.key_len);
+       if (error)
+               return error;
 
-               flow_node = tbl_lookup(table, &key, flow_hash(&key), flow_cmp);
-               if (!flow_node)
-                       error = put_user(ENOENT, &ufp->stats.error);
-               else
-                       error = compat_answer_query(dp, flow_cast(flow_node),
-                                                   uf.flags, ufp);
-               if (error)
-                       return -EFAULT;
-       }
-       return n_flows;
+       flow_node = tbl_lookup(table, &key, flow_hash(&key), flow_cmp);
+       if (!flow_node)
+               return -ENOENT;
+       return compat_answer_query(dp, flow_cast(flow_node), flow.flags, uflow);
 }
 
 static int compat_dump_flow(struct datapath *dp, struct compat_odp_flow_dump __user *udumpp)
@@ -1750,35 +1893,6 @@ static int compat_dump_flow(struct datapath *dp, struct compat_odp_flow_dump __u
        return compat_answer_query(dp, flow, 0, uflowp);
 }
 
-static int compat_flowvec_ioctl(struct datapath *dp, unsigned long argp,
-                               int (*function)(struct datapath *,
-                                               struct compat_odp_flow __user *,
-                                               u32 n_flows))
-{
-       struct compat_odp_flowvec __user *uflowvec;
-       struct compat_odp_flow __user *flows;
-       struct compat_odp_flowvec flowvec;
-       int retval;
-
-       uflowvec = compat_ptr(argp);
-       if (!access_ok(VERIFY_WRITE, uflowvec, sizeof(*uflowvec)) ||
-           copy_from_user(&flowvec, uflowvec, sizeof(flowvec)))
-               return -EFAULT;
-
-       if (flowvec.n_flows > INT_MAX / sizeof(struct compat_odp_flow))
-               return -EINVAL;
-
-       flows = compat_ptr(flowvec.flows);
-       if (!access_ok(VERIFY_WRITE, flows,
-                      flowvec.n_flows * sizeof(struct compat_odp_flow)))
-               return -EFAULT;
-
-       retval = function(dp, flows, flowvec.n_flows);
-       return (retval < 0 ? retval
-               : retval == flowvec.n_flows ? 0
-               : put_user(retval, &uflowvec->n_flows));
-}
-
 static int compat_execute(struct datapath *dp, const struct compat_odp_execute __user *uexecute)
 {
        struct odp_execute execute;
@@ -1811,15 +1925,11 @@ static long openvswitch_compat_ioctl(struct file *f, unsigned int cmd, unsigned
                return openvswitch_ioctl(f, cmd, argp);
 
        case ODP_DP_CREATE:
-       case ODP_VPORT_ATTACH:
-       case ODP_VPORT_DETACH:
-       case ODP_VPORT_MOD:
-       case ODP_VPORT_MTU_SET:
-       case ODP_VPORT_MTU_GET:
-       case ODP_VPORT_ETHER_SET:
-       case ODP_VPORT_ETHER_GET:
-       case ODP_VPORT_STATS_SET:
-       case ODP_VPORT_STATS_GET:
+       case ODP_VPORT_NEW:
+       case ODP_VPORT_DEL:
+       case ODP_VPORT_GET:
+       case ODP_VPORT_SET:
+       case ODP_VPORT_DUMP:
        case ODP_DP_STATS:
        case ODP_GET_DROP_FRAGS:
        case ODP_SET_DROP_FRAGS:
@@ -1827,7 +1937,6 @@ static long openvswitch_compat_ioctl(struct file *f, unsigned int cmd, unsigned
        case ODP_GET_LISTEN_MASK:
        case ODP_SET_SFLOW_PROBABILITY:
        case ODP_GET_SFLOW_PROBABILITY:
-       case ODP_VPORT_QUERY:
                /* Ioctls that just need their pointer argument extended. */
                return openvswitch_ioctl(f, cmd, (unsigned long)compat_ptr(argp));
        }
@@ -1838,10 +1947,6 @@ static long openvswitch_compat_ioctl(struct file *f, unsigned int cmd, unsigned
                goto exit;
 
        switch (cmd) {
-       case ODP_VPORT_DUMP32:
-               err = compat_dump_port(dp, compat_ptr(argp));
-               break;
-
        case ODP_FLOW_PUT32:
                err = compat_put_flow(dp, compat_ptr(argp));
                break;
@@ -1851,7 +1956,7 @@ static long openvswitch_compat_ioctl(struct file *f, unsigned int cmd, unsigned
                break;
 
        case ODP_FLOW_GET32:
-               err = compat_flowvec_ioctl(dp, argp, compat_query_flows);
+               err = compat_query_flow(dp, compat_ptr(argp));
                break;
 
        case ODP_FLOW_DUMP32: