}
}
+/* Converts to the dpif_flow format, using 'key_buf' and 'mask_buf' for
+ * storing the netlink-formatted key/mask. 'key_buf' may be the same as
+ * 'mask_buf'. Actions will be returned without copying, by relying on RCU to
+ * protect them. */
static void
-dp_netdev_flow_to_dpif_flow(const struct dp_netdev_flow *netdev_flow,
- struct ofpbuf *buffer, struct dpif_flow *flow)
+dp_netdev_flow_to_dpif_flow(const struct dpif *dpif,
+ const struct dp_netdev_flow *netdev_flow,
+ struct ofpbuf *key_buf, struct ofpbuf *mask_buf,
+ struct dpif_flow *flow)
{
struct flow_wildcards wc;
struct dp_netdev_actions *actions;
+ size_t offset;
miniflow_expand(&netdev_flow->cr.mask->mf, &wc.masks);
- odp_flow_key_from_mask(buffer, &wc.masks, &netdev_flow->flow,
+
+ /* Key */
+ offset = ofpbuf_size(key_buf);
+ flow->key = ofpbuf_tail(key_buf);
+ odp_flow_key_from_flow(key_buf, &netdev_flow->flow, &wc.masks,
+ netdev_flow->flow.in_port.odp_port, true);
+ flow->key_len = ofpbuf_size(key_buf) - offset;
+
+ /* Mask */
+ offset = ofpbuf_size(mask_buf);
+ flow->mask = ofpbuf_tail(mask_buf);
+ odp_flow_key_from_mask(mask_buf, &wc.masks, &netdev_flow->flow,
odp_to_u32(wc.masks.in_port.odp_port),
SIZE_MAX, true);
- flow->mask = ofpbuf_data(buffer);
- flow->mask_len = ofpbuf_size(buffer);
+ flow->mask_len = ofpbuf_size(mask_buf) - offset;
+ /* Actions */
actions = dp_netdev_flow_get_actions(netdev_flow);
flow->actions = actions->actions;
flow->actions_len = actions->size;
+ dpif_flow_hash(dpif, &netdev_flow->flow, sizeof netdev_flow->flow,
+ &flow->ufid);
get_dpif_flow_stats(netdev_flow, &flow->stats);
}
netdev_flow = dp_netdev_find_flow(dp, &key);
if (netdev_flow) {
- dp_netdev_flow_to_dpif_flow(netdev_flow, get->buffer, get->flow);
+ dp_netdev_flow_to_dpif_flow(dpif, netdev_flow, get->buffer,
+ get->buffer, get->flow);
} else {
error = ENOENT;
}
struct odputil_keybuf *keybuf = &thread->keybuf[i];
struct dp_netdev_flow *netdev_flow = netdev_flows[i];
struct dpif_flow *f = &flows[i];
- struct dp_netdev_actions *dp_actions;
- struct flow_wildcards wc;
- struct ofpbuf buf;
-
- miniflow_expand(&netdev_flow->cr.mask->mf, &wc.masks);
-
- /* Key. */
- ofpbuf_use_stack(&buf, keybuf, sizeof *keybuf);
- odp_flow_key_from_flow(&buf, &netdev_flow->flow, &wc.masks,
- netdev_flow->flow.in_port.odp_port, true);
- f->key = ofpbuf_data(&buf);
- f->key_len = ofpbuf_size(&buf);
-
- /* Mask. */
- ofpbuf_use_stack(&buf, maskbuf, sizeof *maskbuf);
- odp_flow_key_from_mask(&buf, &wc.masks, &netdev_flow->flow,
- odp_to_u32(wc.masks.in_port.odp_port),
- SIZE_MAX, true);
- f->mask = ofpbuf_data(&buf);
- f->mask_len = ofpbuf_size(&buf);
-
- /* Actions. */
- dp_actions = dp_netdev_flow_get_actions(netdev_flow);
- f->actions = dp_actions->actions;
- f->actions_len = dp_actions->size;
+ struct ofpbuf key, mask;
- /* Stats. */
- get_dpif_flow_stats(netdev_flow, &f->stats);
+ ofpbuf_use_stack(&key, keybuf, sizeof *keybuf);
+ ofpbuf_use_stack(&mask, maskbuf, sizeof *maskbuf);
+ dp_netdev_flow_to_dpif_flow(&dpif->dpif, netdev_flow, &key, &mask, f);
}
return n_flows;
static int
dp_netdev_upcall(struct dp_netdev *dp, struct dpif_packet *packet_,
- struct flow *flow, struct flow_wildcards *wc,
+ struct flow *flow, struct flow_wildcards *wc, ovs_u128 *ufid,
enum dpif_upcall_type type, const struct nlattr *userdata,
struct ofpbuf *actions, struct ofpbuf *put_actions)
{
ds_destroy(&ds);
}
- return dp->upcall_cb(packet, flow, type, userdata, actions, wc,
+ return dp->upcall_cb(packet, flow, ufid, type, userdata, actions, wc,
put_actions, dp->upcall_aux);
}
if (OVS_UNLIKELY(any_miss) && !fat_rwlock_tryrdlock(&dp->upcall_rwlock)) {
uint64_t actions_stub[512 / 8], slow_stub[512 / 8];
struct ofpbuf actions, put_actions;
+ ovs_u128 ufid;
ofpbuf_use_stub(&actions, actions_stub, sizeof actions_stub);
ofpbuf_use_stub(&put_actions, slow_stub, sizeof slow_stub);
ofpbuf_clear(&actions);
ofpbuf_clear(&put_actions);
+ dpif_flow_hash(dp->dpif, &match.flow, sizeof match.flow, &ufid);
error = dp_netdev_upcall(dp, packets[i], &match.flow, &match.wc,
- DPIF_UC_MISS, NULL, &actions,
+ &ufid, DPIF_UC_MISS, NULL, &actions,
&put_actions);
if (OVS_UNLIKELY(error && error != ENOSPC)) {
continue;
const struct nlattr *userdata;
struct ofpbuf actions;
struct flow flow;
+ ovs_u128 ufid;
userdata = nl_attr_find_nested(a, OVS_USERSPACE_ATTR_USERDATA);
ofpbuf_init(&actions, 0);
ofpbuf_clear(&actions);
flow_extract(&packets[i]->ofpbuf, &packets[i]->md, &flow);
- error = dp_netdev_upcall(dp, packets[i], &flow, NULL,
- DPIF_UC_ACTION, userdata, &actions,
+ dpif_flow_hash(dp->dpif, &flow, sizeof flow, &ufid);
+ error = dp_netdev_upcall(dp, packets[i], &flow, NULL, &ufid,
+ DPIF_UC_ACTION, userdata,&actions,
NULL);
if (!error || error == ENOSPC) {
dp_netdev_execute_actions(pmd, &packets[i], 1, may_steal,
struct ofpbuf **bufp);
static void dpif_netlink_flow_get_stats(const struct dpif_netlink_flow *,
struct dpif_flow_stats *);
-static void dpif_netlink_flow_to_dpif_flow(struct dpif_flow *,
+static void dpif_netlink_flow_to_dpif_flow(struct dpif *, struct dpif_flow *,
const struct dpif_netlink_flow *);
/* One of the dpif channels between the kernel and userspace. */
}
static void
-dpif_netlink_flow_to_dpif_flow(struct dpif_flow *dpif_flow,
+dpif_netlink_flow_to_dpif_flow(struct dpif *dpif, struct dpif_flow *dpif_flow,
const struct dpif_netlink_flow *datapath_flow)
{
dpif_flow->key = datapath_flow->key;
dpif_flow->mask_len = datapath_flow->mask_len;
dpif_flow->actions = datapath_flow->actions;
dpif_flow->actions_len = datapath_flow->actions_len;
+ dpif_flow_hash(dpif, datapath_flow->key, datapath_flow->key_len,
+ &dpif_flow->ufid);
dpif_netlink_flow_get_stats(datapath_flow, &dpif_flow->stats);
}
if (datapath_flow.actions) {
/* Common case: the flow includes actions. */
- dpif_netlink_flow_to_dpif_flow(&flows[n_flows++], &datapath_flow);
+ dpif_netlink_flow_to_dpif_flow(&dpif->dpif, &flows[n_flows++],
+ &datapath_flow);
} else {
/* Rare case: the flow does not include actions. Retrieve this
* individual flow again to get the actions. */
/* Save this flow. Then exit, because we only have one buffer to
* handle this case. */
- dpif_netlink_flow_to_dpif_flow(&flows[n_flows++], &datapath_flow);
+ dpif_netlink_flow_to_dpif_flow(&dpif->dpif, &flows[n_flows++],
+ &datapath_flow);
break;
}
}
op->error = dpif_netlink_flow_from_ofpbuf(&reply, txn->reply);
if (!op->error) {
- dpif_netlink_flow_to_dpif_flow(get->flow, &reply);
+ dpif_netlink_flow_to_dpif_flow(&dpif->dpif, get->flow,
+ &reply);
}
}
break;
}
static int
-parse_odp_packet(struct ofpbuf *buf, struct dpif_upcall *upcall,
- int *dp_ifindex)
+parse_odp_packet(const struct dpif_netlink *dpif, struct ofpbuf *buf,
+ struct dpif_upcall *upcall, int *dp_ifindex)
{
static const struct nl_policy ovs_packet_policy[] = {
/* Always present. */
upcall->key = CONST_CAST(struct nlattr *,
nl_attr_get(a[OVS_PACKET_ATTR_KEY]));
upcall->key_len = nl_attr_get_size(a[OVS_PACKET_ATTR_KEY]);
+ dpif_flow_hash(&dpif->dpif, upcall->key, upcall->key_len, &upcall->ufid);
upcall->userdata = a[OVS_PACKET_ATTR_USERDATA];
upcall->out_tun_key = a[OVS_PACKET_ATTR_EGRESS_TUN_KEY];
return error;
}
- error = parse_odp_packet(buf, upcall, &dp_ifindex);
+ error = parse_odp_packet(dpif, buf, upcall, &dp_ifindex);
if (!error && dp_ifindex == dpif->dp_ifindex) {
return 0;
} else if (error) {
}
}
+/* Places the hash of the 'key_len' bytes starting at 'key' into '*hash'. */
+void
+dpif_flow_hash(const struct dpif *dpif OVS_UNUSED,
+ const void *key, size_t key_len, ovs_u128 *hash)
+{
+ static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
+ static uint32_t secret;
+
+ if (ovsthread_once_start(&once)) {
+ secret = random_uint32();
+ ovsthread_once_done(&once);
+ }
+ hash_bytes128(key, key_len, secret, hash);
+}
+
/* Deletes all flows from 'dpif'. Returns 0 if successful, otherwise a
* positive errno value. */
int
DPIF_FP_PROBE = 1 << 3 /* Suppress error messages, if any. */
};
+void dpif_flow_hash(const struct dpif *, const void *key, size_t key_len,
+ ovs_u128 *hash);
int dpif_flow_flush(struct dpif *);
int dpif_flow_put(struct dpif *, enum dpif_flow_put_flags,
const struct nlattr *key, size_t key_len,
size_t mask_len; /* 'mask' length in bytes. */
const struct nlattr *actions; /* Actions, as OVS_ACTION_ATTR_ */
size_t actions_len; /* 'actions' length in bytes. */
+ ovs_u128 ufid; /* Unique flow identifier. */
struct dpif_flow_stats stats; /* Flow statistics. */
};
int dpif_flow_dump_next(struct dpif_flow_dump_thread *,
struct ofpbuf packet; /* Packet data. */
struct nlattr *key; /* Flow key. */
size_t key_len; /* Length of 'key' in bytes. */
+ ovs_u128 ufid; /* Unique flow identifier for 'key'. */
/* DPIF_UC_ACTION only. */
struct nlattr *userdata; /* Argument to OVS_ACTION_ATTR_USERSPACE. */
/* A callback to process an upcall, currently implemented only by dpif-netdev.
*
- * The caller provides the 'packet' and 'flow' to process, the 'type' of the
- * upcall, and if 'type' is DPIF_UC_ACTION then the 'userdata' attached to the
- * action.
+ * The caller provides the 'packet' and 'flow' to process, the corresponding
+ * 'ufid' as generated by dpif_flow_hash(), the 'type' of the upcall, and if
+ * 'type' is DPIF_UC_ACTION then the 'userdata' attached to the action.
*
* The callback must fill in 'actions' with the datapath actions to apply to
* 'packet'. 'wc' and 'put_actions' will either be both null or both nonnull.
* flow should be installed, or some otherwise a positive errno value. */
typedef int upcall_callback(const struct ofpbuf *packet,
const struct flow *flow,
+ ovs_u128 *ufid,
enum dpif_upcall_type type,
const struct nlattr *userdata,
struct ofpbuf *actions,
struct dpif *dpif; /* Datapath handle. */
struct dpif_backer *backer; /* Opaque dpif_backer pointer. */
- uint32_t secret; /* Random seed for upcall hash. */
-
struct handler *handlers; /* Upcall handlers. */
size_t n_handlers;
* dpif-netdev. If a modification is absolutely necessary, a const cast
* may be used with other datapaths. */
const struct flow *flow; /* Parsed representation of the packet. */
+ const ovs_u128 *ufid; /* Unique identifier for 'flow'. */
const struct ofpbuf *packet; /* Packet associated with this upcall. */
ofp_port_t in_port; /* OpenFlow in port, or OFPP_NONE. */
const struct nlattr *mask; /* Datapath flow mask. */
size_t mask_len; /* Length of 'mask'. */
struct ofpbuf *actions; /* Datapath flow actions as nlattrs. */
+ ovs_u128 ufid; /* Unique flow identifier. */
uint32_t hash; /* Pre-computed hash for 'key'. */
struct ovs_mutex mutex; /* Guards the following. */
static void upcall_unixctl_purge(struct unixctl_conn *conn, int argc,
const char *argv[], void *aux);
-static struct udpif_key *ukey_create_from_upcall(const struct udpif *,
- const struct upcall *);
+static struct udpif_key *ukey_create_from_upcall(const struct upcall *);
static struct udpif_key *ukey_create_from_dpif_flow(const struct udpif *,
const struct dpif_flow *);
static bool ukey_install_start(struct udpif *, struct udpif_key *ukey);
static bool ukey_install_finish(struct udpif_key *ukey, int error);
static bool ukey_install(struct udpif *udpif, struct udpif_key *ukey);
-static struct udpif_key *ukey_lookup(struct udpif *udpif, uint32_t hash,
- const struct nlattr *key, size_t key_len);
+static struct udpif_key *ukey_lookup(struct udpif *udpif,
+ const ovs_u128 *ufid);
static int ukey_acquire(struct udpif *, const struct dpif_flow *,
struct udpif_key **result);
static void ukey_delete__(struct udpif_key *);
static int upcall_receive(struct upcall *, const struct dpif_backer *,
const struct ofpbuf *packet, enum dpif_upcall_type,
- const struct nlattr *userdata, const struct flow *);
+ const struct nlattr *userdata, const struct flow *,
+ const ovs_u128 *ufid);
static void upcall_uninit(struct upcall *);
static upcall_callback upcall_cb;
udpif->dpif = dpif;
udpif->backer = backer;
atomic_init(&udpif->flow_limit, MIN(ofproto_flow_limit, 10000));
- udpif->secret = random_uint32();
udpif->reval_seq = seq_create();
udpif->dump_seq = seq_create();
latch_init(&udpif->exit_latch);
}
error = upcall_receive(upcall, udpif->backer, &dupcall->packet,
- dupcall->type, dupcall->userdata, flow);
+ dupcall->type, dupcall->userdata, flow,
+ &dupcall->ufid);
if (error) {
if (error == ENODEV) {
/* Received packet on datapath port for which we couldn't
upcall->key = dupcall->key;
upcall->key_len = dupcall->key_len;
+ upcall->ufid = &dupcall->ufid;
upcall->out_tun_key = dupcall->out_tun_key;
static int
upcall_receive(struct upcall *upcall, const struct dpif_backer *backer,
const struct ofpbuf *packet, enum dpif_upcall_type type,
- const struct nlattr *userdata, const struct flow *flow)
+ const struct nlattr *userdata, const struct flow *flow,
+ const ovs_u128 *ufid)
{
int error;
upcall->flow = flow;
upcall->packet = packet;
+ upcall->ufid = ufid;
upcall->type = type;
upcall->userdata = userdata;
ofpbuf_init(&upcall->put_actions, 0);
&upcall->put_actions);
}
- upcall->ukey = ukey_create_from_upcall(udpif, upcall);
+ upcall->ukey = ukey_create_from_upcall(upcall);
}
static void
}
static int
-upcall_cb(const struct ofpbuf *packet, const struct flow *flow,
+upcall_cb(const struct ofpbuf *packet, const struct flow *flow, ovs_u128 *ufid,
enum dpif_upcall_type type, const struct nlattr *userdata,
struct ofpbuf *actions, struct flow_wildcards *wc,
struct ofpbuf *put_actions, void *aux)
atomic_read_relaxed(&udpif->flow_limit, &flow_limit);
error = upcall_receive(&upcall, udpif->backer, packet, type, userdata,
- flow);
+ flow, ufid);
if (error) {
return error;
}
upcall->ukey_persists = true;
op = &ops[n_ops++];
+
op->ukey = ukey;
op->dop.type = DPIF_OP_FLOW_PUT;
op->dop.u.flow_put.flags = DPIF_FP_CREATE;
}
}
+static uint32_t
+get_ufid_hash(const ovs_u128 *ufid)
+{
+ return ufid->u32[0];
+}
+
static struct udpif_key *
-ukey_lookup(struct udpif *udpif, uint32_t hash, const struct nlattr *key,
- size_t key_len)
+ukey_lookup(struct udpif *udpif, const ovs_u128 *ufid)
{
struct udpif_key *ukey;
- struct cmap *cmap = &udpif->ukeys[hash % N_UMAPS].cmap;
+ int idx = get_ufid_hash(ufid) % N_UMAPS;
+ struct cmap *cmap = &udpif->ukeys[idx].cmap;
- CMAP_FOR_EACH_WITH_HASH (ukey, cmap_node, hash, cmap) {
- if (ukey->key_len == key_len && !memcmp(ukey->key, key, key_len)) {
+ CMAP_FOR_EACH_WITH_HASH (ukey, cmap_node, get_ufid_hash(ufid), cmap) {
+ if (ovs_u128_equal(&ukey->ufid, ufid)) {
return ukey;
}
}
}
static struct udpif_key *
-ukey_create__(const struct udpif *udpif,
- const struct nlattr *key, size_t key_len,
+ukey_create__(const struct nlattr *key, size_t key_len,
const struct nlattr *mask, size_t mask_len,
- const struct ofpbuf *actions,
+ const ovs_u128 *ufid, const struct ofpbuf *actions,
uint64_t dump_seq, uint64_t reval_seq, long long int used)
OVS_NO_THREAD_SAFETY_ANALYSIS
{
memcpy(&ukey->maskbuf, mask, mask_len);
ukey->mask = &ukey->maskbuf.nla;
ukey->mask_len = mask_len;
- ukey->hash = hash_bytes(ukey->key, ukey->key_len, udpif->secret);
+ ukey->ufid = *ufid;
+ ukey->hash = get_ufid_hash(&ukey->ufid);
ukey->actions = ofpbuf_clone(actions);
ovs_mutex_init(&ukey->mutex);
}
static struct udpif_key *
-ukey_create_from_upcall(const struct udpif *udpif, const struct upcall *upcall)
+ukey_create_from_upcall(const struct upcall *upcall)
{
struct odputil_keybuf keystub, maskstub;
struct ofpbuf keybuf, maskbuf;
UINT32_MAX, max_mpls, recirc);
}
- return ukey_create__(udpif, ofpbuf_data(&keybuf), ofpbuf_size(&keybuf),
+ return ukey_create__(ofpbuf_data(&keybuf), ofpbuf_size(&keybuf),
ofpbuf_data(&maskbuf), ofpbuf_size(&maskbuf),
- &upcall->put_actions, upcall->dump_seq,
+ upcall->ufid, &upcall->put_actions, upcall->dump_seq,
upcall->reval_seq, 0);
}
dump_seq = seq_read(udpif->dump_seq);
reval_seq = seq_read(udpif->reval_seq);
ofpbuf_use_const(&actions, &flow->actions, flow->actions_len);
- return ukey_create__(udpif, flow->key, flow->key_len,
- flow->mask, flow->mask_len, &actions,
+ return ukey_create__(flow->key, flow->key_len,
+ flow->mask, flow->mask_len, &flow->ufid, &actions,
dump_seq, reval_seq, flow->stats.used);
}
idx = new_ukey->hash % N_UMAPS;
umap = &udpif->ukeys[idx];
ovs_mutex_lock(&umap->mutex);
- old_ukey = ukey_lookup(udpif, new_ukey->hash, new_ukey->key,
- new_ukey->key_len);
+ old_ukey = ukey_lookup(udpif, &new_ukey->ufid);
if (old_ukey) {
/* Uncommon case: A ukey is already installed with the same UFID. */
if (old_ukey->key_len == new_ukey->key_len
OVS_TRY_LOCK(true, (*result)->mutex)
{
struct udpif_key *ukey;
- uint32_t hash;
bool locked = false;
- hash = hash_bytes(flow->key, flow->key_len, udpif->secret);
- ukey = ukey_lookup(udpif, hash, flow->key, flow->key_len);
+ ukey = ukey_lookup(udpif, &flow->ufid);
if (ukey) {
if (!ovs_mutex_trylock(&ukey->mutex)) {
locked = true;