From 9c52546b52018970d97b33b31a0578497663737a Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Tue, 18 Jan 2011 16:54:27 -0800 Subject: [PATCH] datapath: Convert ODP_FLOW_* and ODP_EXECUTE to put dp_idx into message. When the datapath moves to the Netlink protocol it won't have a minor number to use, so we have to put the dp_idx in the message. This also changes the kernel implementation of ODP_FLOW_FLUSH to do the datapath locking inside flush_flows() instead of inside openvswitch_ioctl() but doesn't change that command's userspace interface, which still passes a datapath number as the ioctl argument. Signed-off-by: Ben Pfaff Acked-by: Jesse Gross --- datapath/datapath.c | 391 ++++++++++++++---------- datapath/odp-compat.h | 4 + include/openvswitch/datapath-protocol.h | 4 + lib/dpif-linux.c | 16 +- 4 files changed, 257 insertions(+), 158 deletions(-) diff --git a/datapath/datapath.c b/datapath/datapath.c index 04d8bbd99..f6a2824c5 100644 --- a/datapath/datapath.c +++ b/datapath/datapath.c @@ -642,20 +642,34 @@ err: return err; } -static int flush_flows(struct datapath *dp) +static int flush_flows(int dp_idx) { - struct tbl *old_table = get_table_protected(dp); + struct tbl *old_table; struct tbl *new_table; + struct datapath *dp; + int err; + + dp = get_dp_locked(dp_idx); + err = -ENODEV; + if (!dp) + goto exit; + old_table = get_table_protected(dp); new_table = tbl_create(TBL_MIN_BUCKETS); + err = -ENOMEM; if (!new_table) - return -ENOMEM; + goto exit_unlock; rcu_assign_pointer(dp->table, new_table); tbl_deferred_destroy(old_table, flow_free_tbl); - return 0; + err = 0; + +exit_unlock: + mutex_unlock(&dp->mutex); +exit: + return err; } static int validate_actions(const struct nlattr *actions, u32 actions_len) @@ -906,24 +920,29 @@ error: return error; } -static int put_flow(struct datapath *dp, struct odp_flow_put __user *ufp) +static int put_flow(struct odp_flow_put __user *ufp) { struct odp_flow_stats stats; struct odp_flow_put uf; + struct datapath *dp; int error; if (copy_from_user(&uf, ufp, sizeof(struct odp_flow_put))) return -EFAULT; - error = do_put_flow(dp, &uf, &stats); - if (error) - return error; + dp = get_dp_locked(uf.flow.dp_idx); + if (!dp) + return -ENODEV; - if (copy_to_user(&ufp->flow.stats, &stats, - sizeof(struct odp_flow_stats))) - return -EFAULT; + error = do_put_flow(dp, &uf, &stats); + if (!error) { + if (copy_to_user(&ufp->flow.stats, &stats, + sizeof(struct odp_flow_stats))) + error = -EFAULT; + } + mutex_unlock(&dp->mutex); - return 0; + return error; } static int do_answer_query(struct datapath *dp, struct sw_flow *flow, @@ -993,90 +1012,110 @@ static struct sw_flow *do_del_flow(struct datapath *dp, const struct nlattr __us return flow_cast(flow_node); } -static int del_flow(struct datapath *dp, struct odp_flow __user *ufp) +static int del_flow(struct odp_flow __user *ufp) { struct sw_flow *flow; + struct datapath *dp; struct odp_flow uf; int error; if (copy_from_user(&uf, ufp, sizeof(uf))) return -EFAULT; + dp = get_dp_locked(uf.dp_idx); + if (!dp) + return -ENODEV; + flow = do_del_flow(dp, (const struct nlattr __force __user *)uf.key, uf.key_len); - if (IS_ERR(flow)) - return PTR_ERR(flow); + error = PTR_ERR(flow); + if (!IS_ERR(flow)) { + error = answer_query(dp, flow, ufp); + flow_deferred_free(flow); + } + mutex_unlock(&dp->mutex); - error = answer_query(dp, flow, ufp); - flow_deferred_free(flow); return error; } -static int query_flow(struct datapath *dp, struct odp_flow __user *uflow) +static int query_flow(struct odp_flow __user *uflow) { - struct tbl *table = get_table_protected(dp); struct tbl_node *flow_node; struct sw_flow_key key; struct odp_flow flow; + struct datapath *dp; int error; if (copy_from_user(&flow, uflow, sizeof(flow))) return -EFAULT; - 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, &flow.key, flow_hash(&key), flow_cmp); - if (!flow_node) - return -ENOENT; - return answer_query(dp, flow_cast(flow_node), uflow); -} - -static struct sw_flow *do_dump_flow(struct datapath *dp, u32 __user *state) -{ - struct tbl *table = get_table_protected(dp); - struct tbl_node *tbl_node; - u32 bucket, obj; - - if (get_user(bucket, &state[0]) || get_user(obj, &state[1])) - return ERR_PTR(-EFAULT); - - tbl_node = tbl_next(table, &bucket, &obj); + dp = get_dp_locked(flow.dp_idx); + if (!dp) + return -ENODEV; - if (put_user(bucket, &state[0]) || put_user(obj, &state[1])) - return ERR_PTR(-EFAULT); + error = flow_copy_from_user(&key, (const struct nlattr __force __user *)flow.key, flow.key_len); + if (!error) { + struct tbl *table = get_table_protected(dp); + flow_node = tbl_lookup(table, &flow.key, flow_hash(&key), flow_cmp); + if (flow_node) + error = answer_query(dp, flow_cast(flow_node), uflow); + else + error = -ENOENT; + } + mutex_unlock(&dp->mutex); - return tbl_node ? flow_cast(tbl_node) : NULL; + return error; } -static int dump_flow(struct datapath *dp, struct odp_flow_dump __user *udumpp) +static int dump_flow(struct odp_flow_dump __user *udump) { - struct odp_flow __user *uflowp; + struct odp_flow __user *uflow; struct nlattr __user *ukey; + struct tbl_node *tbl_node; + struct odp_flow_dump dump; struct sw_flow *flow; + struct datapath *dp; + struct tbl *table; u32 key_len; + int err; - flow = do_dump_flow(dp, udumpp->state); - if (IS_ERR(flow)) - return PTR_ERR(flow); + err = -EFAULT; + if (copy_from_user(&dump, udump, sizeof(struct odp_flow_dump))) + goto exit; + uflow = (struct odp_flow __user __force *)dump.flow; - if (get_user(uflowp, (struct odp_flow __user *__user*)&udumpp->flow)) - return -EFAULT; + dp = get_dp_locked(dump.dp_idx); + err = -ENODEV; + if (!dp) + goto exit; - if (!flow) - return put_user(0, &uflowp->key_len); + table = get_table_protected(dp); + tbl_node = tbl_next(table, &dump.state[0], &dump.state[1]); + if (!tbl_node) { + err = put_user(0, &uflow->key_len); + goto exit_unlock; + } + flow = flow_cast(tbl_node); - if (get_user(ukey, (struct nlattr __user * __user*)&uflowp->key) || - get_user(key_len, &uflowp->key_len)) - return -EFAULT; + err = -EFAULT; + if (copy_to_user(udump->state, dump.state, 2 * sizeof(uint32_t)) || + get_user(ukey, (struct nlattr __user * __user*)&uflow->key) || + get_user(key_len, &uflow->key_len)) + goto exit_unlock; key_len = flow_copy_to_user(ukey, &flow->key, key_len); + err = key_len; if (key_len < 0) - return key_len; - if (put_user(key_len, &uflowp->key_len)) - return -EFAULT; + goto exit_unlock; + err = -EFAULT; + if (put_user(key_len, &uflow->key_len)) + goto exit_unlock; + + err = answer_query(dp, flow, uflow); - return answer_query(dp, flow, uflowp); +exit_unlock: + mutex_unlock(&dp->mutex); +exit: + return err; } static int do_execute(struct datapath *dp, const struct odp_execute *execute) @@ -1148,14 +1187,22 @@ error: return err; } -static int execute_packet(struct datapath *dp, const struct odp_execute __user *executep) +static int execute_packet(const struct odp_execute __user *executep) { struct odp_execute execute; + struct datapath *dp; + int error; if (copy_from_user(&execute, executep, sizeof(execute))) return -EFAULT; - return do_execute(dp, &execute); + dp = get_dp_locked(execute.dp_idx); + if (!dp) + return -ENODEV; + error = do_execute(dp, &execute); + mutex_unlock(&dp->mutex); + + return error; } static int get_dp_stats(struct datapath *dp, struct odp_stats __user *statsp) @@ -1667,6 +1714,30 @@ static long openvswitch_ioctl(struct file *f, unsigned int cmd, case ODP_VPORT_DUMP: err = dump_vport((struct odp_vport __user *)argp); goto exit; + + case ODP_FLOW_FLUSH: + err = flush_flows(argp); + goto exit; + + case ODP_FLOW_PUT: + err = put_flow((struct odp_flow_put __user *)argp); + goto exit; + + case ODP_FLOW_DEL: + err = del_flow((struct odp_flow __user *)argp); + goto exit; + + case ODP_FLOW_GET: + err = query_flow((struct odp_flow __user *)argp); + goto exit; + + case ODP_FLOW_DUMP: + err = dump_flow((struct odp_flow_dump __user *)argp); + goto exit; + + case ODP_EXECUTE: + err = execute_packet((struct odp_execute __user *)argp); + goto exit; } dp = get_dp_locked(dp_idx); @@ -1719,30 +1790,6 @@ static long openvswitch_ioctl(struct file *f, unsigned int cmd, dp->sflow_probability = sflow_probability; break; - case ODP_FLOW_FLUSH: - err = flush_flows(dp); - break; - - case ODP_FLOW_PUT: - err = put_flow(dp, (struct odp_flow_put __user *)argp); - break; - - case ODP_FLOW_DEL: - err = del_flow(dp, (struct odp_flow __user *)argp); - break; - - case ODP_FLOW_GET: - err = query_flow(dp, (struct odp_flow __user *)argp); - break; - - case ODP_FLOW_DUMP: - err = dump_flow(dp, (struct odp_flow_dump __user *)argp); - break; - - case ODP_EXECUTE: - err = execute_packet(dp, (struct odp_execute __user *)argp); - break; - default: err = -ENOIOCTLCMD; break; @@ -1780,27 +1827,33 @@ static int compat_get_flow(struct odp_flow *flow, const struct compat_odp_flow _ return 0; } -static int compat_put_flow(struct datapath *dp, struct compat_odp_flow_put __user *ufp) +static int compat_put_flow(struct compat_odp_flow_put __user *ufp) { struct odp_flow_stats stats; - struct odp_flow_put fp; + struct odp_flow_put uf; + struct datapath *dp; int error; - if (compat_get_flow(&fp.flow, &ufp->flow) || - get_user(fp.flags, &ufp->flags)) + if (compat_get_flow(&uf.flow, &ufp->flow) || + get_user(uf.flags, &ufp->flags)) return -EFAULT; - error = do_put_flow(dp, &fp, &stats); - if (error) - return error; + dp = get_dp_locked(uf.flow.dp_idx); + if (!dp) + return -ENODEV; - if (copy_to_user(&ufp->flow.stats, &stats, - sizeof(struct odp_flow_stats))) - return -EFAULT; + error = do_put_flow(dp, &uf, &stats); + if (!error) { + if (copy_to_user(&ufp->flow.stats, &stats, + sizeof(struct odp_flow_stats))) + error = -EFAULT; + } + mutex_unlock(&dp->mutex); - return 0; + return error; } + static int compat_answer_query(struct datapath *dp, struct sw_flow *flow, struct compat_odp_flow __user *ufp) { @@ -1813,84 +1866,124 @@ static int compat_answer_query(struct datapath *dp, struct sw_flow *flow, compat_ptr(actions), &ufp->actions_len); } -static int compat_del_flow(struct datapath *dp, struct compat_odp_flow __user *ufp) +static int compat_del_flow(struct compat_odp_flow __user *ufp) { struct sw_flow *flow; + struct datapath *dp; struct odp_flow uf; int error; if (compat_get_flow(&uf, ufp)) return -EFAULT; + dp = get_dp_locked(uf.dp_idx); + if (!dp) + return -ENODEV; + flow = do_del_flow(dp, (const struct nlattr __force __user *)uf.key, uf.key_len); - if (IS_ERR(flow)) - return PTR_ERR(flow); + error = PTR_ERR(flow); + if (!IS_ERR(flow)) { + error = compat_answer_query(dp, flow, ufp); + flow_deferred_free(flow); + } + mutex_unlock(&dp->mutex); - error = compat_answer_query(dp, flow, ufp); - flow_deferred_free(flow); return error; } -static int compat_query_flow(struct datapath *dp, struct compat_odp_flow __user *uflow) +static int compat_query_flow(struct compat_odp_flow __user *uflow) { - struct tbl *table = get_table_protected(dp); struct tbl_node *flow_node; struct sw_flow_key key; struct odp_flow flow; + struct datapath *dp; int error; if (compat_get_flow(&flow, uflow)) return -EFAULT; + dp = get_dp_locked(flow.dp_idx); + if (!dp) + return -ENODEV; + error = flow_copy_from_user(&key, (const struct nlattr __force __user *)flow.key, flow.key_len); - if (error) - return error; + if (!error) { + struct tbl *table = get_table_protected(dp); + flow_node = tbl_lookup(table, &flow.key, flow_hash(&key), flow_cmp); + if (flow_node) + error = compat_answer_query(dp, flow_cast(flow_node), uflow); + else + error = -ENOENT; + } + mutex_unlock(&dp->mutex); - 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), uflow); + return error; } -static int compat_dump_flow(struct datapath *dp, struct compat_odp_flow_dump __user *udumpp) +static int compat_dump_flow(struct compat_odp_flow_dump __user *udump) { - struct compat_odp_flow __user *uflowp; - compat_uptr_t compat_ufp; + struct compat_odp_flow __user *uflow; + struct nlattr __user *ukey; + struct tbl_node *tbl_node; + struct compat_odp_flow_dump dump; struct sw_flow *flow; - compat_uptr_t ukey; + compat_uptr_t ukey32; + struct datapath *dp; + struct tbl *table; u32 key_len; + int err; - flow = do_dump_flow(dp, udumpp->state); - if (IS_ERR(flow)) - return PTR_ERR(flow); + err = -EFAULT; + if (copy_from_user(&dump, udump, sizeof(struct compat_odp_flow_dump))) + goto exit; + uflow =compat_ptr(dump.flow); - if (get_user(compat_ufp, &udumpp->flow)) - return -EFAULT; - uflowp = compat_ptr(compat_ufp); + dp = get_dp_locked(dump.dp_idx); + err = -ENODEV; + if (!dp) + goto exit; - if (!flow) - return put_user(0, &uflowp->key_len); + table = get_table_protected(dp); + tbl_node = tbl_next(table, &dump.state[0], &dump.state[1]); + if (!tbl_node) { + err = put_user(0, &uflow->key_len); + goto exit_unlock; + } + flow = flow_cast(tbl_node); - if (get_user(ukey, &uflowp->key) || - get_user(key_len, &uflowp->key_len)) - return -EFAULT; + err = -EFAULT; + if (copy_to_user(udump->state, dump.state, 2 * sizeof(uint32_t)) || + get_user(ukey32, &uflow->key) || + get_user(key_len, &uflow->key_len)) + goto exit_unlock; + ukey = compat_ptr(ukey32); - key_len = flow_copy_to_user(compat_ptr(ukey), &flow->key, key_len); + key_len = flow_copy_to_user(ukey, &flow->key, key_len); + err = key_len; if (key_len < 0) - return key_len; - if (put_user(key_len, &uflowp->key_len)) - return -EFAULT; + goto exit_unlock; + err = -EFAULT; + if (put_user(key_len, &uflow->key_len)) + goto exit_unlock; + + err = compat_answer_query(dp, flow, uflow); - return compat_answer_query(dp, flow, uflowp); +exit_unlock: + mutex_unlock(&dp->mutex); +exit: + return err; } -static int compat_execute(struct datapath *dp, const struct compat_odp_execute __user *uexecute) +static int compat_execute(const struct compat_odp_execute __user *uexecute) { struct odp_execute execute; compat_uptr_t actions; compat_uptr_t data; + struct datapath *dp; + int error; if (!access_ok(VERIFY_READ, uexecute, sizeof(struct compat_odp_execute)) || + __get_user(execute.dp_idx, &uexecute->dp_idx) || __get_user(actions, &uexecute->actions) || __get_user(execute.actions_len, &uexecute->actions_len) || __get_user(data, &uexecute->data) || @@ -1900,15 +1993,17 @@ static int compat_execute(struct datapath *dp, const struct compat_odp_execute _ execute.actions = (struct nlattr __force *)compat_ptr(actions); execute.data = (const void __force *)compat_ptr(data); - return do_execute(dp, &execute); + dp = get_dp_locked(execute.dp_idx); + if (!dp) + return -ENODEV; + error = do_execute(dp, &execute); + mutex_unlock(&dp->mutex); + + return error; } static long openvswitch_compat_ioctl(struct file *f, unsigned int cmd, unsigned long argp) { - int dp_idx = iminor(f->f_dentry->d_inode); - struct datapath *dp; - int err; - switch (cmd) { case ODP_DP_DESTROY: case ODP_FLOW_FLUSH: @@ -1930,41 +2025,25 @@ static long openvswitch_compat_ioctl(struct file *f, unsigned int cmd, unsigned case ODP_GET_SFLOW_PROBABILITY: /* Ioctls that just need their pointer argument extended. */ return openvswitch_ioctl(f, cmd, (unsigned long)compat_ptr(argp)); - } - - dp = get_dp_locked(dp_idx); - err = -ENODEV; - if (!dp) - goto exit; - switch (cmd) { case ODP_FLOW_PUT32: - err = compat_put_flow(dp, compat_ptr(argp)); - break; + return compat_put_flow(compat_ptr(argp)); case ODP_FLOW_DEL32: - err = compat_del_flow(dp, compat_ptr(argp)); - break; + return compat_del_flow(compat_ptr(argp)); case ODP_FLOW_GET32: - err = compat_query_flow(dp, compat_ptr(argp)); - break; + return compat_query_flow(compat_ptr(argp)); case ODP_FLOW_DUMP32: - err = compat_dump_flow(dp, compat_ptr(argp)); - break; + return compat_dump_flow(compat_ptr(argp)); case ODP_EXECUTE32: - err = compat_execute(dp, compat_ptr(argp)); - break; + return compat_execute(compat_ptr(argp)); default: - err = -ENOIOCTLCMD; - break; + return -ENOIOCTLCMD; } - mutex_unlock(&dp->mutex); -exit: - return err; } #endif diff --git a/datapath/odp-compat.h b/datapath/odp-compat.h index bccfaf9da..ae997e677 100644 --- a/datapath/odp-compat.h +++ b/datapath/odp-compat.h @@ -23,6 +23,7 @@ #define ODP_FLOW_DEL32 _IOWR('O', 17, struct compat_odp_flow) struct compat_odp_flow { + uint32_t dp_idx; struct odp_flow_stats stats; compat_uptr_t key; u32 key_len; @@ -36,11 +37,14 @@ struct compat_odp_flow_put { }; struct compat_odp_flow_dump { + uint32_t dp_idx; compat_uptr_t flow; uint32_t state[2]; }; struct compat_odp_execute { + uint32_t dp_idx; + compat_uptr_t actions; u32 actions_len; diff --git a/include/openvswitch/datapath-protocol.h b/include/openvswitch/datapath-protocol.h index 0078874ff..cf81cb31c 100644 --- a/include/openvswitch/datapath-protocol.h +++ b/include/openvswitch/datapath-protocol.h @@ -272,6 +272,7 @@ struct odp_key_arp { }; struct odp_flow { + uint32_t dp_idx; struct odp_flow_stats stats; struct nlattr *key; uint32_t key_len; @@ -301,6 +302,7 @@ struct odp_flow_put { * unspecified. */ struct odp_flow_dump { + uint32_t dp_idx; struct odp_flow *flow; uint32_t state[2]; }; @@ -329,6 +331,8 @@ enum odp_action_type { #define ODPAT_MAX (__ODPAT_MAX - 1) struct odp_execute { + uint32_t dp_idx; + struct nlattr *actions; uint32_t actions_len; diff --git a/lib/dpif-linux.c b/lib/dpif-linux.c index d60950b6b..3c11be2ce 100644 --- a/lib/dpif-linux.c +++ b/lib/dpif-linux.c @@ -377,7 +377,8 @@ dpif_linux_get_max_ports(const struct dpif *dpif OVS_UNUSED) static int dpif_linux_flow_flush(struct dpif *dpif_) { - return do_ioctl(dpif_, ODP_FLOW_FLUSH, NULL); + struct dpif_linux *dpif = dpif_linux_cast(dpif_); + return ioctl(dpif->fd, ODP_FLOW_FLUSH, dpif->minor) ? errno : 0; } struct dpif_linux_port_state { @@ -474,11 +475,13 @@ dpif_linux_flow_get(const struct dpif *dpif_, const struct nlattr *key, size_t key_len, struct ofpbuf **actionsp, struct dpif_flow_stats *stats) { + struct dpif_linux *dpif = dpif_linux_cast(dpif_); struct ofpbuf *actions = NULL; struct odp_flow odp_flow; int error; memset(&odp_flow, 0, sizeof odp_flow); + odp_flow.dp_idx = dpif->minor; odp_flow.key = (struct nlattr *) key; odp_flow.key_len = key_len; if (actionsp) { @@ -510,10 +513,12 @@ dpif_linux_flow_put(struct dpif *dpif_, enum dpif_flow_put_flags flags, const struct nlattr *actions, size_t actions_len, struct dpif_flow_stats *stats) { + struct dpif_linux *dpif = dpif_linux_cast(dpif_); struct odp_flow_put put; int error; memset(&put, 0, sizeof put); + put.flow.dp_idx = dpif->minor; put.flow.key = (struct nlattr *) key; put.flow.key_len = key_len; put.flow.actions = (struct nlattr *) actions; @@ -540,10 +545,12 @@ dpif_linux_flow_del(struct dpif *dpif_, const struct nlattr *key, size_t key_len, struct dpif_flow_stats *stats) { + struct dpif_linux *dpif = dpif_linux_cast(dpif_); struct odp_flow odp_flow; int error; memset(&odp_flow, 0, sizeof odp_flow); + odp_flow.dp_idx = dpif->minor; odp_flow.key = (struct nlattr *) key; odp_flow.key_len = key_len; error = do_ioctl(dpif_, ODP_FLOW_DEL, &odp_flow); @@ -562,11 +569,13 @@ struct dpif_linux_flow_state { }; static int -dpif_linux_flow_dump_start(const struct dpif *dpif OVS_UNUSED, void **statep) +dpif_linux_flow_dump_start(const struct dpif *dpif_, void **statep) { + struct dpif_linux *dpif = dpif_linux_cast(dpif_); struct dpif_linux_flow_state *state; *statep = state = xmalloc(sizeof *state); + state->dump.dp_idx = dpif->minor; state->dump.state[0] = 0; state->dump.state[1] = 0; state->dump.flow = &state->flow; @@ -624,8 +633,11 @@ dpif_linux_execute(struct dpif *dpif_, const struct nlattr *actions, size_t actions_len, const struct ofpbuf *buf) { + struct dpif_linux *dpif = dpif_linux_cast(dpif_); struct odp_execute execute; + memset(&execute, 0, sizeof execute); + execute.dp_idx = dpif->minor; execute.actions = (struct nlattr *) actions; execute.actions_len = actions_len; execute.data = buf->data; -- 2.43.0