Commit 39b6b299 authored by David S. Miller's avatar David S. Miller

Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/jesse/openvswitch

Jesse Gross says:

====================
[GIT net-next] Open vSwitch

Open vSwitch changes for net-next/3.14. Highlights are:
 * Performance improvements in the mechanism to get packets to userspace
   using memory mapped netlink and skb zero copy where appropriate.
 * Per-cpu flow stats in situations where flows are likely to be shared
   across CPUs. Standard flow stats are used in other situations to save
   memory and allocation time.
 * A handful of code cleanups and rationalization.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 56a4342d 443cd88c
...@@ -2445,6 +2445,9 @@ int skb_splice_bits(struct sk_buff *skb, unsigned int offset, ...@@ -2445,6 +2445,9 @@ int skb_splice_bits(struct sk_buff *skb, unsigned int offset,
struct pipe_inode_info *pipe, unsigned int len, struct pipe_inode_info *pipe, unsigned int len,
unsigned int flags); unsigned int flags);
void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to); void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to);
unsigned int skb_zerocopy_headlen(const struct sk_buff *from);
void skb_zerocopy(struct sk_buff *to, const struct sk_buff *from,
int len, int hlen);
void skb_split(struct sk_buff *skb, struct sk_buff *skb1, const u32 len); void skb_split(struct sk_buff *skb, struct sk_buff *skb1, const u32 len);
int skb_shift(struct sk_buff *tgt, struct sk_buff *skb, int shiftlen); int skb_shift(struct sk_buff *tgt, struct sk_buff *skb, int shiftlen);
void skb_scrub_packet(struct sk_buff *skb, bool xnet); void skb_scrub_packet(struct sk_buff *skb, bool xnet);
......
...@@ -73,6 +73,7 @@ struct genl_family { ...@@ -73,6 +73,7 @@ struct genl_family {
* @attrs: netlink attributes * @attrs: netlink attributes
* @_net: network namespace * @_net: network namespace
* @user_ptr: user pointers * @user_ptr: user pointers
* @dst_sk: destination socket
*/ */
struct genl_info { struct genl_info {
u32 snd_seq; u32 snd_seq;
...@@ -85,6 +86,7 @@ struct genl_info { ...@@ -85,6 +86,7 @@ struct genl_info {
struct net * _net; struct net * _net;
#endif #endif
void * user_ptr[2]; void * user_ptr[2];
struct sock * dst_sk;
}; };
static inline struct net *genl_info_net(struct genl_info *info) static inline struct net *genl_info_net(struct genl_info *info)
...@@ -177,6 +179,8 @@ void genl_notify(struct genl_family *family, ...@@ -177,6 +179,8 @@ void genl_notify(struct genl_family *family,
struct sk_buff *skb, struct net *net, u32 portid, struct sk_buff *skb, struct net *net, u32 portid,
u32 group, struct nlmsghdr *nlh, gfp_t flags); u32 group, struct nlmsghdr *nlh, gfp_t flags);
struct sk_buff *genlmsg_new_unicast(size_t payload, struct genl_info *info,
gfp_t flags);
void *genlmsg_put(struct sk_buff *skb, u32 portid, u32 seq, void *genlmsg_put(struct sk_buff *skb, u32 portid, u32 seq,
struct genl_family *family, int flags, u8 cmd); struct genl_family *family, int flags, u8 cmd);
......
...@@ -40,7 +40,15 @@ struct ovs_header { ...@@ -40,7 +40,15 @@ struct ovs_header {
#define OVS_DATAPATH_FAMILY "ovs_datapath" #define OVS_DATAPATH_FAMILY "ovs_datapath"
#define OVS_DATAPATH_MCGROUP "ovs_datapath" #define OVS_DATAPATH_MCGROUP "ovs_datapath"
#define OVS_DATAPATH_VERSION 0x1
/* V2:
* - API users are expected to provide OVS_DP_ATTR_USER_FEATURES
* when creating the datapath.
*/
#define OVS_DATAPATH_VERSION 2
/* First OVS datapath version to support features */
#define OVS_DP_VER_FEATURES 2
enum ovs_datapath_cmd { enum ovs_datapath_cmd {
OVS_DP_CMD_UNSPEC, OVS_DP_CMD_UNSPEC,
...@@ -75,6 +83,7 @@ enum ovs_datapath_attr { ...@@ -75,6 +83,7 @@ enum ovs_datapath_attr {
OVS_DP_ATTR_UPCALL_PID, /* Netlink PID to receive upcalls */ OVS_DP_ATTR_UPCALL_PID, /* Netlink PID to receive upcalls */
OVS_DP_ATTR_STATS, /* struct ovs_dp_stats */ OVS_DP_ATTR_STATS, /* struct ovs_dp_stats */
OVS_DP_ATTR_MEGAFLOW_STATS, /* struct ovs_dp_megaflow_stats */ OVS_DP_ATTR_MEGAFLOW_STATS, /* struct ovs_dp_megaflow_stats */
OVS_DP_ATTR_USER_FEATURES, /* OVS_DP_F_* */
__OVS_DP_ATTR_MAX __OVS_DP_ATTR_MAX
}; };
...@@ -106,6 +115,9 @@ struct ovs_vport_stats { ...@@ -106,6 +115,9 @@ struct ovs_vport_stats {
__u64 tx_dropped; /* no space available in linux */ __u64 tx_dropped; /* no space available in linux */
}; };
/* Allow last Netlink attribute to be unaligned */
#define OVS_DP_F_UNALIGNED (1 << 0)
/* Fixed logical ports. */ /* Fixed logical ports. */
#define OVSP_LOCAL ((__u32)0) #define OVSP_LOCAL ((__u32)0)
......
...@@ -2121,6 +2121,91 @@ __wsum skb_copy_and_csum_bits(const struct sk_buff *skb, int offset, ...@@ -2121,6 +2121,91 @@ __wsum skb_copy_and_csum_bits(const struct sk_buff *skb, int offset,
} }
EXPORT_SYMBOL(skb_copy_and_csum_bits); EXPORT_SYMBOL(skb_copy_and_csum_bits);
/**
* skb_zerocopy_headlen - Calculate headroom needed for skb_zerocopy()
* @from: source buffer
*
* Calculates the amount of linear headroom needed in the 'to' skb passed
* into skb_zerocopy().
*/
unsigned int
skb_zerocopy_headlen(const struct sk_buff *from)
{
unsigned int hlen = 0;
if (!from->head_frag ||
skb_headlen(from) < L1_CACHE_BYTES ||
skb_shinfo(from)->nr_frags >= MAX_SKB_FRAGS)
hlen = skb_headlen(from);
if (skb_has_frag_list(from))
hlen = from->len;
return hlen;
}
EXPORT_SYMBOL_GPL(skb_zerocopy_headlen);
/**
* skb_zerocopy - Zero copy skb to skb
* @to: destination buffer
* @source: source buffer
* @len: number of bytes to copy from source buffer
* @hlen: size of linear headroom in destination buffer
*
* Copies up to `len` bytes from `from` to `to` by creating references
* to the frags in the source buffer.
*
* The `hlen` as calculated by skb_zerocopy_headlen() specifies the
* headroom in the `to` buffer.
*/
void
skb_zerocopy(struct sk_buff *to, const struct sk_buff *from, int len, int hlen)
{
int i, j = 0;
int plen = 0; /* length of skb->head fragment */
struct page *page;
unsigned int offset;
BUG_ON(!from->head_frag && !hlen);
/* dont bother with small payloads */
if (len <= skb_tailroom(to)) {
skb_copy_bits(from, 0, skb_put(to, len), len);
return;
}
if (hlen) {
skb_copy_bits(from, 0, skb_put(to, hlen), hlen);
len -= hlen;
} else {
plen = min_t(int, skb_headlen(from), len);
if (plen) {
page = virt_to_head_page(from->head);
offset = from->data - (unsigned char *)page_address(page);
__skb_fill_page_desc(to, 0, page, offset, plen);
get_page(page);
j = 1;
len -= plen;
}
}
to->truesize += len + plen;
to->len += len + plen;
to->data_len += len + plen;
for (i = 0; i < skb_shinfo(from)->nr_frags; i++) {
if (!len)
break;
skb_shinfo(to)->frags[j] = skb_shinfo(from)->frags[i];
skb_shinfo(to)->frags[j].size = min_t(int, skb_shinfo(to)->frags[j].size, len);
len -= skb_shinfo(to)->frags[j].size;
skb_frag_ref(to, j);
j++;
}
skb_shinfo(to)->nr_frags = j;
}
EXPORT_SYMBOL_GPL(skb_zerocopy);
void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to) void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to)
{ {
__wsum csum; __wsum csum;
......
...@@ -236,51 +236,6 @@ nfqnl_flush(struct nfqnl_instance *queue, nfqnl_cmpfn cmpfn, unsigned long data) ...@@ -236,51 +236,6 @@ nfqnl_flush(struct nfqnl_instance *queue, nfqnl_cmpfn cmpfn, unsigned long data)
spin_unlock_bh(&queue->lock); spin_unlock_bh(&queue->lock);
} }
static void
nfqnl_zcopy(struct sk_buff *to, const struct sk_buff *from, int len, int hlen)
{
int i, j = 0;
int plen = 0; /* length of skb->head fragment */
struct page *page;
unsigned int offset;
/* dont bother with small payloads */
if (len <= skb_tailroom(to)) {
skb_copy_bits(from, 0, skb_put(to, len), len);
return;
}
if (hlen) {
skb_copy_bits(from, 0, skb_put(to, hlen), hlen);
len -= hlen;
} else {
plen = min_t(int, skb_headlen(from), len);
if (plen) {
page = virt_to_head_page(from->head);
offset = from->data - (unsigned char *)page_address(page);
__skb_fill_page_desc(to, 0, page, offset, plen);
get_page(page);
j = 1;
len -= plen;
}
}
to->truesize += len + plen;
to->len += len + plen;
to->data_len += len + plen;
for (i = 0; i < skb_shinfo(from)->nr_frags; i++) {
if (!len)
break;
skb_shinfo(to)->frags[j] = skb_shinfo(from)->frags[i];
skb_shinfo(to)->frags[j].size = min_t(int, skb_shinfo(to)->frags[j].size, len);
len -= skb_shinfo(to)->frags[j].size;
skb_frag_ref(to, j);
j++;
}
skb_shinfo(to)->nr_frags = j;
}
static int static int
nfqnl_put_packet_info(struct sk_buff *nlskb, struct sk_buff *packet, nfqnl_put_packet_info(struct sk_buff *nlskb, struct sk_buff *packet,
bool csum_verify) bool csum_verify)
...@@ -330,7 +285,7 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue, ...@@ -330,7 +285,7 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue,
{ {
size_t size; size_t size;
size_t data_len = 0, cap_len = 0; size_t data_len = 0, cap_len = 0;
int hlen = 0; unsigned int hlen = 0;
struct sk_buff *skb; struct sk_buff *skb;
struct nlattr *nla; struct nlattr *nla;
struct nfqnl_msg_packet_hdr *pmsg; struct nfqnl_msg_packet_hdr *pmsg;
...@@ -382,14 +337,8 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue, ...@@ -382,14 +337,8 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue,
if (data_len > entskb->len) if (data_len > entskb->len)
data_len = entskb->len; data_len = entskb->len;
if (!entskb->head_frag || hlen = skb_zerocopy_headlen(entskb);
skb_headlen(entskb) < L1_CACHE_BYTES || hlen = min_t(unsigned int, hlen, data_len);
skb_shinfo(entskb)->nr_frags >= MAX_SKB_FRAGS)
hlen = skb_headlen(entskb);
if (skb_has_frag_list(entskb))
hlen = entskb->len;
hlen = min_t(int, data_len, hlen);
size += sizeof(struct nlattr) + hlen; size += sizeof(struct nlattr) + hlen;
cap_len = entskb->len; cap_len = entskb->len;
break; break;
...@@ -539,7 +488,7 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue, ...@@ -539,7 +488,7 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue,
nla->nla_type = NFQA_PAYLOAD; nla->nla_type = NFQA_PAYLOAD;
nla->nla_len = nla_attr_size(data_len); nla->nla_len = nla_attr_size(data_len);
nfqnl_zcopy(skb, entskb, data_len, hlen); skb_zerocopy(skb, entskb, data_len, hlen);
} }
nlh->nlmsg_len = skb->len; nlh->nlmsg_len = skb->len;
......
...@@ -1773,6 +1773,9 @@ struct sk_buff *netlink_alloc_skb(struct sock *ssk, unsigned int size, ...@@ -1773,6 +1773,9 @@ struct sk_buff *netlink_alloc_skb(struct sock *ssk, unsigned int size,
if (ring->pg_vec == NULL) if (ring->pg_vec == NULL)
goto out_put; goto out_put;
if (ring->frame_size - NL_MMAP_HDRLEN < size)
goto out_put;
skb = alloc_skb_head(gfp_mask); skb = alloc_skb_head(gfp_mask);
if (skb == NULL) if (skb == NULL)
goto err1; goto err1;
...@@ -1782,6 +1785,7 @@ struct sk_buff *netlink_alloc_skb(struct sock *ssk, unsigned int size, ...@@ -1782,6 +1785,7 @@ struct sk_buff *netlink_alloc_skb(struct sock *ssk, unsigned int size,
if (ring->pg_vec == NULL) if (ring->pg_vec == NULL)
goto out_free; goto out_free;
/* check again under lock */
maxlen = ring->frame_size - NL_MMAP_HDRLEN; maxlen = ring->frame_size - NL_MMAP_HDRLEN;
if (maxlen < size) if (maxlen < size)
goto out_free; goto out_free;
......
...@@ -460,6 +460,26 @@ int genl_unregister_family(struct genl_family *family) ...@@ -460,6 +460,26 @@ int genl_unregister_family(struct genl_family *family)
} }
EXPORT_SYMBOL(genl_unregister_family); EXPORT_SYMBOL(genl_unregister_family);
/**
* genlmsg_new_unicast - Allocate generic netlink message for unicast
* @payload: size of the message payload
* @info: information on destination
* @flags: the type of memory to allocate
*
* Allocates a new sk_buff large enough to cover the specified payload
* plus required Netlink headers. Will check receiving socket for
* memory mapped i/o capability and use it if enabled. Will fall back
* to non-mapped skb if message size exceeds the frame size of the ring.
*/
struct sk_buff *genlmsg_new_unicast(size_t payload, struct genl_info *info,
gfp_t flags)
{
size_t len = nlmsg_total_size(genlmsg_total_size(payload));
return netlink_alloc_skb(info->dst_sk, len, info->snd_portid, flags);
}
EXPORT_SYMBOL_GPL(genlmsg_new_unicast);
/** /**
* genlmsg_put - Add generic netlink header to netlink message * genlmsg_put - Add generic netlink header to netlink message
* @skb: socket buffer holding the message * @skb: socket buffer holding the message
...@@ -600,6 +620,7 @@ static int genl_family_rcv_msg(struct genl_family *family, ...@@ -600,6 +620,7 @@ static int genl_family_rcv_msg(struct genl_family *family,
info.genlhdr = nlmsg_data(nlh); info.genlhdr = nlmsg_data(nlh);
info.userhdr = nlmsg_data(nlh) + GENL_HDRLEN; info.userhdr = nlmsg_data(nlh) + GENL_HDRLEN;
info.attrs = attrbuf; info.attrs = attrbuf;
info.dst_sk = skb->sk;
genl_info_net_set(&info, net); genl_info_net_set(&info, net);
memset(&info.user_ptr, 0, sizeof(info.user_ptr)); memset(&info.user_ptr, 0, sizeof(info.user_ptr));
......
...@@ -108,10 +108,9 @@ int lockdep_ovsl_is_held(void) ...@@ -108,10 +108,9 @@ int lockdep_ovsl_is_held(void)
#endif #endif
static struct vport *new_vport(const struct vport_parms *); static struct vport *new_vport(const struct vport_parms *);
static int queue_gso_packets(struct net *, int dp_ifindex, struct sk_buff *, static int queue_gso_packets(struct datapath *dp, struct sk_buff *,
const struct dp_upcall_info *); const struct dp_upcall_info *);
static int queue_userspace_packet(struct net *, int dp_ifindex, static int queue_userspace_packet(struct datapath *dp, struct sk_buff *,
struct sk_buff *,
const struct dp_upcall_info *); const struct dp_upcall_info *);
/* Must be called with rcu_read_lock or ovs_mutex. */ /* Must be called with rcu_read_lock or ovs_mutex. */
...@@ -133,7 +132,7 @@ static struct datapath *get_dp(struct net *net, int dp_ifindex) ...@@ -133,7 +132,7 @@ static struct datapath *get_dp(struct net *net, int dp_ifindex)
} }
/* Must be called with rcu_read_lock or ovs_mutex. */ /* Must be called with rcu_read_lock or ovs_mutex. */
const char *ovs_dp_name(const struct datapath *dp) static const char *ovs_dp_name(const struct datapath *dp)
{ {
struct vport *vport = ovs_vport_ovsl_rcu(dp, OVSP_LOCAL); struct vport *vport = ovs_vport_ovsl_rcu(dp, OVSP_LOCAL);
return vport->ops->get_name(vport); return vport->ops->get_name(vport);
...@@ -234,7 +233,7 @@ void ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb) ...@@ -234,7 +233,7 @@ void ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb)
} }
/* Look up flow. */ /* Look up flow. */
flow = ovs_flow_tbl_lookup(&dp->table, &key, &n_mask_hit); flow = ovs_flow_tbl_lookup_stats(&dp->table, &key, &n_mask_hit);
if (unlikely(!flow)) { if (unlikely(!flow)) {
struct dp_upcall_info upcall; struct dp_upcall_info upcall;
...@@ -251,9 +250,9 @@ void ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb) ...@@ -251,9 +250,9 @@ void ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb)
OVS_CB(skb)->flow = flow; OVS_CB(skb)->flow = flow;
OVS_CB(skb)->pkt_key = &key; OVS_CB(skb)->pkt_key = &key;
stats_counter = &stats->n_hit; ovs_flow_stats_update(OVS_CB(skb)->flow, skb);
ovs_flow_used(OVS_CB(skb)->flow, skb);
ovs_execute_actions(dp, skb); ovs_execute_actions(dp, skb);
stats_counter = &stats->n_hit;
out: out:
/* Update datapath statistics. */ /* Update datapath statistics. */
...@@ -277,7 +276,6 @@ int ovs_dp_upcall(struct datapath *dp, struct sk_buff *skb, ...@@ -277,7 +276,6 @@ int ovs_dp_upcall(struct datapath *dp, struct sk_buff *skb,
const struct dp_upcall_info *upcall_info) const struct dp_upcall_info *upcall_info)
{ {
struct dp_stats_percpu *stats; struct dp_stats_percpu *stats;
int dp_ifindex;
int err; int err;
if (upcall_info->portid == 0) { if (upcall_info->portid == 0) {
...@@ -285,16 +283,10 @@ int ovs_dp_upcall(struct datapath *dp, struct sk_buff *skb, ...@@ -285,16 +283,10 @@ int ovs_dp_upcall(struct datapath *dp, struct sk_buff *skb,
goto err; goto err;
} }
dp_ifindex = get_dpifindex(dp);
if (!dp_ifindex) {
err = -ENODEV;
goto err;
}
if (!skb_is_gso(skb)) if (!skb_is_gso(skb))
err = queue_userspace_packet(ovs_dp_get_net(dp), dp_ifindex, skb, upcall_info); err = queue_userspace_packet(dp, skb, upcall_info);
else else
err = queue_gso_packets(ovs_dp_get_net(dp), dp_ifindex, skb, upcall_info); err = queue_gso_packets(dp, skb, upcall_info);
if (err) if (err)
goto err; goto err;
...@@ -310,8 +302,7 @@ int ovs_dp_upcall(struct datapath *dp, struct sk_buff *skb, ...@@ -310,8 +302,7 @@ int ovs_dp_upcall(struct datapath *dp, struct sk_buff *skb,
return err; return err;
} }
static int queue_gso_packets(struct net *net, int dp_ifindex, static int queue_gso_packets(struct datapath *dp, struct sk_buff *skb,
struct sk_buff *skb,
const struct dp_upcall_info *upcall_info) const struct dp_upcall_info *upcall_info)
{ {
unsigned short gso_type = skb_shinfo(skb)->gso_type; unsigned short gso_type = skb_shinfo(skb)->gso_type;
...@@ -320,14 +311,14 @@ static int queue_gso_packets(struct net *net, int dp_ifindex, ...@@ -320,14 +311,14 @@ static int queue_gso_packets(struct net *net, int dp_ifindex,
struct sk_buff *segs, *nskb; struct sk_buff *segs, *nskb;
int err; int err;
segs = __skb_gso_segment(skb, NETIF_F_SG | NETIF_F_HW_CSUM, false); segs = __skb_gso_segment(skb, NETIF_F_SG, false);
if (IS_ERR(segs)) if (IS_ERR(segs))
return PTR_ERR(segs); return PTR_ERR(segs);
/* Queue all of the segments. */ /* Queue all of the segments. */
skb = segs; skb = segs;
do { do {
err = queue_userspace_packet(net, dp_ifindex, skb, upcall_info); err = queue_userspace_packet(dp, skb, upcall_info);
if (err) if (err)
break; break;
...@@ -380,11 +371,11 @@ static size_t key_attr_size(void) ...@@ -380,11 +371,11 @@ static size_t key_attr_size(void)
+ nla_total_size(28); /* OVS_KEY_ATTR_ND */ + nla_total_size(28); /* OVS_KEY_ATTR_ND */
} }
static size_t upcall_msg_size(const struct sk_buff *skb, static size_t upcall_msg_size(const struct nlattr *userdata,
const struct nlattr *userdata) unsigned int hdrlen)
{ {
size_t size = NLMSG_ALIGN(sizeof(struct ovs_header)) size_t size = NLMSG_ALIGN(sizeof(struct ovs_header))
+ nla_total_size(skb->len) /* OVS_PACKET_ATTR_PACKET */ + nla_total_size(hdrlen) /* OVS_PACKET_ATTR_PACKET */
+ nla_total_size(key_attr_size()); /* OVS_PACKET_ATTR_KEY */ + nla_total_size(key_attr_size()); /* OVS_PACKET_ATTR_KEY */
/* OVS_PACKET_ATTR_USERDATA */ /* OVS_PACKET_ATTR_USERDATA */
...@@ -394,15 +385,24 @@ static size_t upcall_msg_size(const struct sk_buff *skb, ...@@ -394,15 +385,24 @@ static size_t upcall_msg_size(const struct sk_buff *skb,
return size; return size;
} }
static int queue_userspace_packet(struct net *net, int dp_ifindex, static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb,
struct sk_buff *skb,
const struct dp_upcall_info *upcall_info) const struct dp_upcall_info *upcall_info)
{ {
struct ovs_header *upcall; struct ovs_header *upcall;
struct sk_buff *nskb = NULL; struct sk_buff *nskb = NULL;
struct sk_buff *user_skb; /* to be queued to userspace */ struct sk_buff *user_skb; /* to be queued to userspace */
struct nlattr *nla; struct nlattr *nla;
int err; struct genl_info info = {
.dst_sk = ovs_dp_get_net(dp)->genl_sock,
.snd_portid = upcall_info->portid,
};
size_t len;
unsigned int hlen;
int err, dp_ifindex;
dp_ifindex = get_dpifindex(dp);
if (!dp_ifindex)
return -ENODEV;
if (vlan_tx_tag_present(skb)) { if (vlan_tx_tag_present(skb)) {
nskb = skb_clone(skb, GFP_ATOMIC); nskb = skb_clone(skb, GFP_ATOMIC);
...@@ -422,7 +422,22 @@ static int queue_userspace_packet(struct net *net, int dp_ifindex, ...@@ -422,7 +422,22 @@ static int queue_userspace_packet(struct net *net, int dp_ifindex,
goto out; goto out;
} }
user_skb = genlmsg_new(upcall_msg_size(skb, upcall_info->userdata), GFP_ATOMIC); /* Complete checksum if needed */
if (skb->ip_summed == CHECKSUM_PARTIAL &&
(err = skb_checksum_help(skb)))
goto out;
/* Older versions of OVS user space enforce alignment of the last
* Netlink attribute to NLA_ALIGNTO which would require extensive
* padding logic. Only perform zerocopy if padding is not required.
*/
if (dp->user_features & OVS_DP_F_UNALIGNED)
hlen = skb_zerocopy_headlen(skb);
else
hlen = skb->len;
len = upcall_msg_size(upcall_info->userdata, hlen);
user_skb = genlmsg_new_unicast(len, &info, GFP_ATOMIC);
if (!user_skb) { if (!user_skb) {
err = -ENOMEM; err = -ENOMEM;
goto out; goto out;
...@@ -441,26 +456,24 @@ static int queue_userspace_packet(struct net *net, int dp_ifindex, ...@@ -441,26 +456,24 @@ static int queue_userspace_packet(struct net *net, int dp_ifindex,
nla_len(upcall_info->userdata), nla_len(upcall_info->userdata),
nla_data(upcall_info->userdata)); nla_data(upcall_info->userdata));
nla = __nla_reserve(user_skb, OVS_PACKET_ATTR_PACKET, skb->len); /* Only reserve room for attribute header, packet data is added
* in skb_zerocopy() */
if (!(nla = nla_reserve(user_skb, OVS_PACKET_ATTR_PACKET, 0))) {
err = -ENOBUFS;
goto out;
}
nla->nla_len = nla_attr_size(skb->len);
skb_copy_and_csum_dev(skb, nla_data(nla)); skb_zerocopy(user_skb, skb, skb->len, hlen);
genlmsg_end(user_skb, upcall); ((struct nlmsghdr *) user_skb->data)->nlmsg_len = user_skb->len;
err = genlmsg_unicast(net, user_skb, upcall_info->portid);
err = genlmsg_unicast(ovs_dp_get_net(dp), user_skb, upcall_info->portid);
out: out:
kfree_skb(nskb); kfree_skb(nskb);
return err; return err;
} }
static void clear_stats(struct sw_flow *flow)
{
flow->used = 0;
flow->tcp_flags = 0;
flow->packet_count = 0;
flow->byte_count = 0;
}
static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info) static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
{ {
struct ovs_header *ovs_header = info->userhdr; struct ovs_header *ovs_header = info->userhdr;
...@@ -499,7 +512,7 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info) ...@@ -499,7 +512,7 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
packet->protocol = htons(ETH_P_802_2); packet->protocol = htons(ETH_P_802_2);
/* Build an sw_flow for sending this packet. */ /* Build an sw_flow for sending this packet. */
flow = ovs_flow_alloc(); flow = ovs_flow_alloc(false);
err = PTR_ERR(flow); err = PTR_ERR(flow);
if (IS_ERR(flow)) if (IS_ERR(flow))
goto err_kfree_skb; goto err_kfree_skb;
...@@ -635,10 +648,10 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp, ...@@ -635,10 +648,10 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp,
const int skb_orig_len = skb->len; const int skb_orig_len = skb->len;
struct nlattr *start; struct nlattr *start;
struct ovs_flow_stats stats; struct ovs_flow_stats stats;
__be16 tcp_flags;
unsigned long used;
struct ovs_header *ovs_header; struct ovs_header *ovs_header;
struct nlattr *nla; struct nlattr *nla;
unsigned long used;
u8 tcp_flags;
int err; int err;
ovs_header = genlmsg_put(skb, portid, seq, &dp_flow_genl_family, flags, cmd); ovs_header = genlmsg_put(skb, portid, seq, &dp_flow_genl_family, flags, cmd);
...@@ -667,24 +680,17 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp, ...@@ -667,24 +680,17 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp,
nla_nest_end(skb, nla); nla_nest_end(skb, nla);
spin_lock_bh(&flow->lock); ovs_flow_stats_get(flow, &stats, &used, &tcp_flags);
used = flow->used;
stats.n_packets = flow->packet_count;
stats.n_bytes = flow->byte_count;
tcp_flags = (u8)ntohs(flow->tcp_flags);
spin_unlock_bh(&flow->lock);
if (used && if (used &&
nla_put_u64(skb, OVS_FLOW_ATTR_USED, ovs_flow_used_time(used))) nla_put_u64(skb, OVS_FLOW_ATTR_USED, ovs_flow_used_time(used)))
goto nla_put_failure; goto nla_put_failure;
if (stats.n_packets && if (stats.n_packets &&
nla_put(skb, OVS_FLOW_ATTR_STATS, nla_put(skb, OVS_FLOW_ATTR_STATS, sizeof(struct ovs_flow_stats), &stats))
sizeof(struct ovs_flow_stats), &stats))
goto nla_put_failure; goto nla_put_failure;
if (tcp_flags && if ((u8)ntohs(tcp_flags) &&
nla_put_u8(skb, OVS_FLOW_ATTR_TCP_FLAGS, tcp_flags)) nla_put_u8(skb, OVS_FLOW_ATTR_TCP_FLAGS, (u8)ntohs(tcp_flags)))
goto nla_put_failure; goto nla_put_failure;
/* If OVS_FLOW_ATTR_ACTIONS doesn't fit, skip dumping the actions if /* If OVS_FLOW_ATTR_ACTIONS doesn't fit, skip dumping the actions if
...@@ -701,8 +707,7 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp, ...@@ -701,8 +707,7 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp,
if (start) { if (start) {
const struct sw_flow_actions *sf_acts; const struct sw_flow_actions *sf_acts;
sf_acts = rcu_dereference_check(flow->sf_acts, sf_acts = rcu_dereference_ovsl(flow->sf_acts);
lockdep_ovsl_is_held());
err = ovs_nla_put_actions(sf_acts->actions, err = ovs_nla_put_actions(sf_acts->actions,
sf_acts->actions_len, skb); sf_acts->actions_len, skb);
...@@ -726,39 +731,34 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp, ...@@ -726,39 +731,34 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp,
return err; return err;
} }
static struct sk_buff *ovs_flow_cmd_alloc_info(struct sw_flow *flow) static struct sk_buff *ovs_flow_cmd_alloc_info(struct sw_flow *flow,
struct genl_info *info)
{ {
const struct sw_flow_actions *sf_acts; size_t len;
sf_acts = ovsl_dereference(flow->sf_acts); len = ovs_flow_cmd_msg_size(ovsl_dereference(flow->sf_acts));
return genlmsg_new(ovs_flow_cmd_msg_size(sf_acts), GFP_KERNEL); return genlmsg_new_unicast(len, info, GFP_KERNEL);
} }
static struct sk_buff *ovs_flow_cmd_build_info(struct sw_flow *flow, static struct sk_buff *ovs_flow_cmd_build_info(struct sw_flow *flow,
struct datapath *dp, struct datapath *dp,
u32 portid, u32 seq, u8 cmd) struct genl_info *info,
u8 cmd)
{ {
struct sk_buff *skb; struct sk_buff *skb;
int retval; int retval;
skb = ovs_flow_cmd_alloc_info(flow); skb = ovs_flow_cmd_alloc_info(flow, info);
if (!skb) if (!skb)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
retval = ovs_flow_cmd_fill_info(flow, dp, skb, portid, seq, 0, cmd); retval = ovs_flow_cmd_fill_info(flow, dp, skb, info->snd_portid,
info->snd_seq, 0, cmd);
BUG_ON(retval < 0); BUG_ON(retval < 0);
return skb; return skb;
} }
static struct sw_flow *__ovs_flow_tbl_lookup(struct flow_table *tbl,
const struct sw_flow_key *key)
{
u32 __always_unused n_mask_hit;
return ovs_flow_tbl_lookup(tbl, key, &n_mask_hit);
}
static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
{ {
struct nlattr **a = info->attrs; struct nlattr **a = info->attrs;
...@@ -770,6 +770,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) ...@@ -770,6 +770,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
struct datapath *dp; struct datapath *dp;
struct sw_flow_actions *acts = NULL; struct sw_flow_actions *acts = NULL;
struct sw_flow_match match; struct sw_flow_match match;
bool exact_5tuple;
int error; int error;
/* Extract key. */ /* Extract key. */
...@@ -778,7 +779,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) ...@@ -778,7 +779,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
goto error; goto error;
ovs_match_init(&match, &key, &mask); ovs_match_init(&match, &key, &mask);
error = ovs_nla_get_match(&match, error = ovs_nla_get_match(&match, &exact_5tuple,
a[OVS_FLOW_ATTR_KEY], a[OVS_FLOW_ATTR_MASK]); a[OVS_FLOW_ATTR_KEY], a[OVS_FLOW_ATTR_MASK]);
if (error) if (error)
goto error; goto error;
...@@ -809,7 +810,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) ...@@ -809,7 +810,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
goto err_unlock_ovs; goto err_unlock_ovs;
/* Check if this is a duplicate flow */ /* Check if this is a duplicate flow */
flow = __ovs_flow_tbl_lookup(&dp->table, &key); flow = ovs_flow_tbl_lookup(&dp->table, &key);
if (!flow) { if (!flow) {
/* Bail out if we're not allowed to create a new flow. */ /* Bail out if we're not allowed to create a new flow. */
error = -ENOENT; error = -ENOENT;
...@@ -817,12 +818,11 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) ...@@ -817,12 +818,11 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
goto err_unlock_ovs; goto err_unlock_ovs;
/* Allocate flow. */ /* Allocate flow. */
flow = ovs_flow_alloc(); flow = ovs_flow_alloc(!exact_5tuple);
if (IS_ERR(flow)) { if (IS_ERR(flow)) {
error = PTR_ERR(flow); error = PTR_ERR(flow);
goto err_unlock_ovs; goto err_unlock_ovs;
} }
clear_stats(flow);
flow->key = masked_key; flow->key = masked_key;
flow->unmasked_key = key; flow->unmasked_key = key;
...@@ -835,8 +835,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) ...@@ -835,8 +835,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
goto err_flow_free; goto err_flow_free;
} }
reply = ovs_flow_cmd_build_info(flow, dp, info->snd_portid, reply = ovs_flow_cmd_build_info(flow, dp, info, OVS_FLOW_CMD_NEW);
info->snd_seq, OVS_FLOW_CMD_NEW);
} else { } else {
/* We found a matching flow. */ /* We found a matching flow. */
struct sw_flow_actions *old_acts; struct sw_flow_actions *old_acts;
...@@ -864,15 +863,11 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) ...@@ -864,15 +863,11 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
rcu_assign_pointer(flow->sf_acts, acts); rcu_assign_pointer(flow->sf_acts, acts);
ovs_nla_free_flow_actions(old_acts); ovs_nla_free_flow_actions(old_acts);
reply = ovs_flow_cmd_build_info(flow, dp, info->snd_portid, reply = ovs_flow_cmd_build_info(flow, dp, info, OVS_FLOW_CMD_NEW);
info->snd_seq, OVS_FLOW_CMD_NEW);
/* Clear stats. */ /* Clear stats. */
if (a[OVS_FLOW_ATTR_CLEAR]) { if (a[OVS_FLOW_ATTR_CLEAR])
spin_lock_bh(&flow->lock); ovs_flow_stats_clear(flow);
clear_stats(flow);
spin_unlock_bh(&flow->lock);
}
} }
ovs_unlock(); ovs_unlock();
...@@ -910,7 +905,7 @@ static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info) ...@@ -910,7 +905,7 @@ static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info)
} }
ovs_match_init(&match, &key, NULL); ovs_match_init(&match, &key, NULL);
err = ovs_nla_get_match(&match, a[OVS_FLOW_ATTR_KEY], NULL); err = ovs_nla_get_match(&match, NULL, a[OVS_FLOW_ATTR_KEY], NULL);
if (err) if (err)
return err; return err;
...@@ -921,14 +916,13 @@ static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info) ...@@ -921,14 +916,13 @@ static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info)
goto unlock; goto unlock;
} }
flow = __ovs_flow_tbl_lookup(&dp->table, &key); flow = ovs_flow_tbl_lookup(&dp->table, &key);
if (!flow || !ovs_flow_cmp_unmasked_key(flow, &match)) { if (!flow || !ovs_flow_cmp_unmasked_key(flow, &match)) {
err = -ENOENT; err = -ENOENT;
goto unlock; goto unlock;
} }
reply = ovs_flow_cmd_build_info(flow, dp, info->snd_portid, reply = ovs_flow_cmd_build_info(flow, dp, info, OVS_FLOW_CMD_NEW);
info->snd_seq, OVS_FLOW_CMD_NEW);
if (IS_ERR(reply)) { if (IS_ERR(reply)) {
err = PTR_ERR(reply); err = PTR_ERR(reply);
goto unlock; goto unlock;
...@@ -965,17 +959,17 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info) ...@@ -965,17 +959,17 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info)
} }
ovs_match_init(&match, &key, NULL); ovs_match_init(&match, &key, NULL);
err = ovs_nla_get_match(&match, a[OVS_FLOW_ATTR_KEY], NULL); err = ovs_nla_get_match(&match, NULL, a[OVS_FLOW_ATTR_KEY], NULL);
if (err) if (err)
goto unlock; goto unlock;
flow = __ovs_flow_tbl_lookup(&dp->table, &key); flow = ovs_flow_tbl_lookup(&dp->table, &key);
if (!flow || !ovs_flow_cmp_unmasked_key(flow, &match)) { if (!flow || !ovs_flow_cmp_unmasked_key(flow, &match)) {
err = -ENOENT; err = -ENOENT;
goto unlock; goto unlock;
} }
reply = ovs_flow_cmd_alloc_info(flow); reply = ovs_flow_cmd_alloc_info(flow, info);
if (!reply) { if (!reply) {
err = -ENOMEM; err = -ENOMEM;
goto unlock; goto unlock;
...@@ -1061,6 +1055,7 @@ static const struct genl_ops dp_flow_genl_ops[] = { ...@@ -1061,6 +1055,7 @@ static const struct genl_ops dp_flow_genl_ops[] = {
static const struct nla_policy datapath_policy[OVS_DP_ATTR_MAX + 1] = { static const struct nla_policy datapath_policy[OVS_DP_ATTR_MAX + 1] = {
[OVS_DP_ATTR_NAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ - 1 }, [OVS_DP_ATTR_NAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ - 1 },
[OVS_DP_ATTR_UPCALL_PID] = { .type = NLA_U32 }, [OVS_DP_ATTR_UPCALL_PID] = { .type = NLA_U32 },
[OVS_DP_ATTR_USER_FEATURES] = { .type = NLA_U32 },
}; };
static struct genl_family dp_datapath_genl_family = { static struct genl_family dp_datapath_genl_family = {
...@@ -1119,6 +1114,9 @@ static int ovs_dp_cmd_fill_info(struct datapath *dp, struct sk_buff *skb, ...@@ -1119,6 +1114,9 @@ static int ovs_dp_cmd_fill_info(struct datapath *dp, struct sk_buff *skb,
&dp_megaflow_stats)) &dp_megaflow_stats))
goto nla_put_failure; goto nla_put_failure;
if (nla_put_u32(skb, OVS_DP_ATTR_USER_FEATURES, dp->user_features))
goto nla_put_failure;
return genlmsg_end(skb, ovs_header); return genlmsg_end(skb, ovs_header);
nla_put_failure: nla_put_failure:
...@@ -1127,17 +1125,17 @@ static int ovs_dp_cmd_fill_info(struct datapath *dp, struct sk_buff *skb, ...@@ -1127,17 +1125,17 @@ static int ovs_dp_cmd_fill_info(struct datapath *dp, struct sk_buff *skb,
return -EMSGSIZE; return -EMSGSIZE;
} }
static struct sk_buff *ovs_dp_cmd_build_info(struct datapath *dp, u32 portid, static struct sk_buff *ovs_dp_cmd_build_info(struct datapath *dp,
u32 seq, u8 cmd) struct genl_info *info, u8 cmd)
{ {
struct sk_buff *skb; struct sk_buff *skb;
int retval; int retval;
skb = genlmsg_new(ovs_dp_cmd_msg_size(), GFP_KERNEL); skb = genlmsg_new_unicast(ovs_dp_cmd_msg_size(), info, GFP_KERNEL);
if (!skb) if (!skb)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
retval = ovs_dp_cmd_fill_info(dp, skb, portid, seq, 0, cmd); retval = ovs_dp_cmd_fill_info(dp, skb, info->snd_portid, info->snd_seq, 0, cmd);
if (retval < 0) { if (retval < 0) {
kfree_skb(skb); kfree_skb(skb);
return ERR_PTR(retval); return ERR_PTR(retval);
...@@ -1165,6 +1163,24 @@ static struct datapath *lookup_datapath(struct net *net, ...@@ -1165,6 +1163,24 @@ static struct datapath *lookup_datapath(struct net *net,
return dp ? dp : ERR_PTR(-ENODEV); return dp ? dp : ERR_PTR(-ENODEV);
} }
static void ovs_dp_reset_user_features(struct sk_buff *skb, struct genl_info *info)
{
struct datapath *dp;
dp = lookup_datapath(sock_net(skb->sk), info->userhdr, info->attrs);
if (!dp)
return;
WARN(dp->user_features, "Dropping previously announced user features\n");
dp->user_features = 0;
}
static void ovs_dp_change(struct datapath *dp, struct nlattr **a)
{
if (a[OVS_DP_ATTR_USER_FEATURES])
dp->user_features = nla_get_u32(a[OVS_DP_ATTR_USER_FEATURES]);
}
static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info) static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
{ {
struct nlattr **a = info->attrs; struct nlattr **a = info->attrs;
...@@ -1223,17 +1239,27 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info) ...@@ -1223,17 +1239,27 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
parms.port_no = OVSP_LOCAL; parms.port_no = OVSP_LOCAL;
parms.upcall_portid = nla_get_u32(a[OVS_DP_ATTR_UPCALL_PID]); parms.upcall_portid = nla_get_u32(a[OVS_DP_ATTR_UPCALL_PID]);
ovs_dp_change(dp, a);
vport = new_vport(&parms); vport = new_vport(&parms);
if (IS_ERR(vport)) { if (IS_ERR(vport)) {
err = PTR_ERR(vport); err = PTR_ERR(vport);
if (err == -EBUSY) if (err == -EBUSY)
err = -EEXIST; err = -EEXIST;
if (err == -EEXIST) {
/* An outdated user space instance that does not understand
* the concept of user_features has attempted to create a new
* datapath and is likely to reuse it. Drop all user features.
*/
if (info->genlhdr->version < OVS_DP_VER_FEATURES)
ovs_dp_reset_user_features(skb, info);
}
goto err_destroy_ports_array; goto err_destroy_ports_array;
} }
reply = ovs_dp_cmd_build_info(dp, info->snd_portid, reply = ovs_dp_cmd_build_info(dp, info, OVS_DP_CMD_NEW);
info->snd_seq, OVS_DP_CMD_NEW);
err = PTR_ERR(reply); err = PTR_ERR(reply);
if (IS_ERR(reply)) if (IS_ERR(reply))
goto err_destroy_local_port; goto err_destroy_local_port;
...@@ -1299,8 +1325,7 @@ static int ovs_dp_cmd_del(struct sk_buff *skb, struct genl_info *info) ...@@ -1299,8 +1325,7 @@ static int ovs_dp_cmd_del(struct sk_buff *skb, struct genl_info *info)
if (IS_ERR(dp)) if (IS_ERR(dp))
goto unlock; goto unlock;
reply = ovs_dp_cmd_build_info(dp, info->snd_portid, reply = ovs_dp_cmd_build_info(dp, info, OVS_DP_CMD_DEL);
info->snd_seq, OVS_DP_CMD_DEL);
err = PTR_ERR(reply); err = PTR_ERR(reply);
if (IS_ERR(reply)) if (IS_ERR(reply))
goto unlock; goto unlock;
...@@ -1328,8 +1353,9 @@ static int ovs_dp_cmd_set(struct sk_buff *skb, struct genl_info *info) ...@@ -1328,8 +1353,9 @@ static int ovs_dp_cmd_set(struct sk_buff *skb, struct genl_info *info)
if (IS_ERR(dp)) if (IS_ERR(dp))
goto unlock; goto unlock;
reply = ovs_dp_cmd_build_info(dp, info->snd_portid, ovs_dp_change(dp, info->attrs);
info->snd_seq, OVS_DP_CMD_NEW);
reply = ovs_dp_cmd_build_info(dp, info, OVS_DP_CMD_NEW);
if (IS_ERR(reply)) { if (IS_ERR(reply)) {
err = PTR_ERR(reply); err = PTR_ERR(reply);
genl_set_err(&dp_datapath_genl_family, sock_net(skb->sk), 0, genl_set_err(&dp_datapath_genl_family, sock_net(skb->sk), 0,
...@@ -1360,8 +1386,7 @@ static int ovs_dp_cmd_get(struct sk_buff *skb, struct genl_info *info) ...@@ -1360,8 +1386,7 @@ static int ovs_dp_cmd_get(struct sk_buff *skb, struct genl_info *info)
goto unlock; goto unlock;
} }
reply = ovs_dp_cmd_build_info(dp, info->snd_portid, reply = ovs_dp_cmd_build_info(dp, info, OVS_DP_CMD_NEW);
info->snd_seq, OVS_DP_CMD_NEW);
if (IS_ERR(reply)) { if (IS_ERR(reply)) {
err = PTR_ERR(reply); err = PTR_ERR(reply);
goto unlock; goto unlock;
...@@ -1441,7 +1466,7 @@ struct genl_family dp_vport_genl_family = { ...@@ -1441,7 +1466,7 @@ struct genl_family dp_vport_genl_family = {
.parallel_ops = true, .parallel_ops = true,
}; };
struct genl_multicast_group ovs_dp_vport_multicast_group = { static struct genl_multicast_group ovs_dp_vport_multicast_group = {
.name = OVS_VPORT_MCGROUP .name = OVS_VPORT_MCGROUP
}; };
......
...@@ -88,6 +88,8 @@ struct datapath { ...@@ -88,6 +88,8 @@ struct datapath {
/* Network namespace ref. */ /* Network namespace ref. */
struct net *net; struct net *net;
#endif #endif
u32 user_features;
}; };
/** /**
...@@ -145,6 +147,8 @@ int lockdep_ovsl_is_held(void); ...@@ -145,6 +147,8 @@ int lockdep_ovsl_is_held(void);
#define ASSERT_OVSL() WARN_ON(unlikely(!lockdep_ovsl_is_held())) #define ASSERT_OVSL() WARN_ON(unlikely(!lockdep_ovsl_is_held()))
#define ovsl_dereference(p) \ #define ovsl_dereference(p) \
rcu_dereference_protected(p, lockdep_ovsl_is_held()) rcu_dereference_protected(p, lockdep_ovsl_is_held())
#define rcu_dereference_ovsl(p) \
rcu_dereference_check(p, lockdep_ovsl_is_held())
static inline struct net *ovs_dp_get_net(struct datapath *dp) static inline struct net *ovs_dp_get_net(struct datapath *dp)
{ {
...@@ -178,14 +182,12 @@ static inline struct vport *ovs_vport_ovsl(const struct datapath *dp, int port_n ...@@ -178,14 +182,12 @@ static inline struct vport *ovs_vport_ovsl(const struct datapath *dp, int port_n
extern struct notifier_block ovs_dp_device_notifier; extern struct notifier_block ovs_dp_device_notifier;
extern struct genl_family dp_vport_genl_family; extern struct genl_family dp_vport_genl_family;
extern struct genl_multicast_group ovs_dp_vport_multicast_group;
void ovs_dp_process_received_packet(struct vport *, struct sk_buff *); void ovs_dp_process_received_packet(struct vport *, struct sk_buff *);
void ovs_dp_detach_port(struct vport *); void ovs_dp_detach_port(struct vport *);
int ovs_dp_upcall(struct datapath *, struct sk_buff *, int ovs_dp_upcall(struct datapath *, struct sk_buff *,
const struct dp_upcall_info *); const struct dp_upcall_info *);
const char *ovs_dp_name(const struct datapath *dp);
struct sk_buff *ovs_vport_cmd_build_info(struct vport *, u32 pid, u32 seq, struct sk_buff *ovs_vport_cmd_build_info(struct vport *, u32 pid, u32 seq,
u8 cmd); u8 cmd);
......
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include <linux/ip.h> #include <linux/ip.h>
#include <linux/ipv6.h> #include <linux/ipv6.h>
#include <linux/sctp.h> #include <linux/sctp.h>
#include <linux/smp.h>
#include <linux/tcp.h> #include <linux/tcp.h>
#include <linux/udp.h> #include <linux/udp.h>
#include <linux/icmp.h> #include <linux/icmp.h>
...@@ -60,10 +61,16 @@ u64 ovs_flow_used_time(unsigned long flow_jiffies) ...@@ -60,10 +61,16 @@ u64 ovs_flow_used_time(unsigned long flow_jiffies)
#define TCP_FLAGS_BE16(tp) (*(__be16 *)&tcp_flag_word(tp) & htons(0x0FFF)) #define TCP_FLAGS_BE16(tp) (*(__be16 *)&tcp_flag_word(tp) & htons(0x0FFF))
void ovs_flow_used(struct sw_flow *flow, struct sk_buff *skb) void ovs_flow_stats_update(struct sw_flow *flow, struct sk_buff *skb)
{ {
struct flow_stats *stats;
__be16 tcp_flags = 0; __be16 tcp_flags = 0;
if (!flow->stats.is_percpu)
stats = flow->stats.stat;
else
stats = this_cpu_ptr(flow->stats.cpu_stats);
if ((flow->key.eth.type == htons(ETH_P_IP) || if ((flow->key.eth.type == htons(ETH_P_IP) ||
flow->key.eth.type == htons(ETH_P_IPV6)) && flow->key.eth.type == htons(ETH_P_IPV6)) &&
flow->key.ip.proto == IPPROTO_TCP && flow->key.ip.proto == IPPROTO_TCP &&
...@@ -71,12 +78,87 @@ void ovs_flow_used(struct sw_flow *flow, struct sk_buff *skb) ...@@ -71,12 +78,87 @@ void ovs_flow_used(struct sw_flow *flow, struct sk_buff *skb)
tcp_flags = TCP_FLAGS_BE16(tcp_hdr(skb)); tcp_flags = TCP_FLAGS_BE16(tcp_hdr(skb));
} }
spin_lock(&flow->lock); spin_lock(&stats->lock);
flow->used = jiffies; stats->used = jiffies;
flow->packet_count++; stats->packet_count++;
flow->byte_count += skb->len; stats->byte_count += skb->len;
flow->tcp_flags |= tcp_flags; stats->tcp_flags |= tcp_flags;
spin_unlock(&flow->lock); spin_unlock(&stats->lock);
}
static void stats_read(struct flow_stats *stats,
struct ovs_flow_stats *ovs_stats,
unsigned long *used, __be16 *tcp_flags)
{
spin_lock(&stats->lock);
if (time_after(stats->used, *used))
*used = stats->used;
*tcp_flags |= stats->tcp_flags;
ovs_stats->n_packets += stats->packet_count;
ovs_stats->n_bytes += stats->byte_count;
spin_unlock(&stats->lock);
}
void ovs_flow_stats_get(struct sw_flow *flow, struct ovs_flow_stats *ovs_stats,
unsigned long *used, __be16 *tcp_flags)
{
int cpu, cur_cpu;
*used = 0;
*tcp_flags = 0;
memset(ovs_stats, 0, sizeof(*ovs_stats));
if (!flow->stats.is_percpu) {
stats_read(flow->stats.stat, ovs_stats, used, tcp_flags);
} else {
cur_cpu = get_cpu();
for_each_possible_cpu(cpu) {
struct flow_stats *stats;
if (cpu == cur_cpu)
local_bh_disable();
stats = per_cpu_ptr(flow->stats.cpu_stats, cpu);
stats_read(stats, ovs_stats, used, tcp_flags);
if (cpu == cur_cpu)
local_bh_enable();
}
put_cpu();
}
}
static void stats_reset(struct flow_stats *stats)
{
spin_lock(&stats->lock);
stats->used = 0;
stats->packet_count = 0;
stats->byte_count = 0;
stats->tcp_flags = 0;
spin_unlock(&stats->lock);
}
void ovs_flow_stats_clear(struct sw_flow *flow)
{
int cpu, cur_cpu;
if (!flow->stats.is_percpu) {
stats_reset(flow->stats.stat);
} else {
cur_cpu = get_cpu();
for_each_possible_cpu(cpu) {
if (cpu == cur_cpu)
local_bh_disable();
stats_reset(per_cpu_ptr(flow->stats.cpu_stats, cpu));
if (cpu == cur_cpu)
local_bh_enable();
}
put_cpu();
}
} }
static int check_header(struct sk_buff *skb, int len) static int check_header(struct sk_buff *skb, int len)
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#ifndef FLOW_H #ifndef FLOW_H
#define FLOW_H 1 #define FLOW_H 1
#include <linux/cache.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/netlink.h> #include <linux/netlink.h>
#include <linux/openvswitch.h> #include <linux/openvswitch.h>
...@@ -122,8 +123,8 @@ struct sw_flow_key { ...@@ -122,8 +123,8 @@ struct sw_flow_key {
} __aligned(BITS_PER_LONG/8); /* Ensure that we can do comparisons as longs. */ } __aligned(BITS_PER_LONG/8); /* Ensure that we can do comparisons as longs. */
struct sw_flow_key_range { struct sw_flow_key_range {
size_t start; unsigned short int start;
size_t end; unsigned short int end;
}; };
struct sw_flow_mask { struct sw_flow_mask {
...@@ -146,6 +147,22 @@ struct sw_flow_actions { ...@@ -146,6 +147,22 @@ struct sw_flow_actions {
struct nlattr actions[]; struct nlattr actions[];
}; };
struct flow_stats {
u64 packet_count; /* Number of packets matched. */
u64 byte_count; /* Number of bytes matched. */
unsigned long used; /* Last used time (in jiffies). */
spinlock_t lock; /* Lock for atomic stats update. */
__be16 tcp_flags; /* Union of seen TCP flags. */
};
struct sw_flow_stats {
bool is_percpu;
union {
struct flow_stats *stat;
struct flow_stats __percpu *cpu_stats;
};
};
struct sw_flow { struct sw_flow {
struct rcu_head rcu; struct rcu_head rcu;
struct hlist_node hash_node[2]; struct hlist_node hash_node[2];
...@@ -155,12 +172,7 @@ struct sw_flow { ...@@ -155,12 +172,7 @@ struct sw_flow {
struct sw_flow_key unmasked_key; struct sw_flow_key unmasked_key;
struct sw_flow_mask *mask; struct sw_flow_mask *mask;
struct sw_flow_actions __rcu *sf_acts; struct sw_flow_actions __rcu *sf_acts;
struct sw_flow_stats stats;
spinlock_t lock; /* Lock for values below. */
unsigned long used; /* Last used time (in jiffies). */
u64 packet_count; /* Number of packets matched. */
u64 byte_count; /* Number of bytes matched. */
__be16 tcp_flags; /* Union of seen TCP flags. */
}; };
struct arp_eth_header { struct arp_eth_header {
...@@ -177,7 +189,10 @@ struct arp_eth_header { ...@@ -177,7 +189,10 @@ struct arp_eth_header {
unsigned char ar_tip[4]; /* target IP address */ unsigned char ar_tip[4]; /* target IP address */
} __packed; } __packed;
void ovs_flow_used(struct sw_flow *, struct sk_buff *); void ovs_flow_stats_update(struct sw_flow *flow, struct sk_buff *skb);
void ovs_flow_stats_get(struct sw_flow *flow, struct ovs_flow_stats *stats,
unsigned long *used, __be16 *tcp_flags);
void ovs_flow_stats_clear(struct sw_flow *flow);
u64 ovs_flow_used_time(unsigned long flow_jiffies); u64 ovs_flow_used_time(unsigned long flow_jiffies);
int ovs_flow_extract(struct sk_buff *, u16 in_port, struct sw_flow_key *); int ovs_flow_extract(struct sk_buff *, u16 in_port, struct sw_flow_key *);
......
...@@ -266,6 +266,20 @@ static bool is_all_zero(const u8 *fp, size_t size) ...@@ -266,6 +266,20 @@ static bool is_all_zero(const u8 *fp, size_t size)
return true; return true;
} }
static bool is_all_set(const u8 *fp, size_t size)
{
int i;
if (!fp)
return false;
for (i = 0; i < size; i++)
if (fp[i] != 0xff)
return false;
return true;
}
static int __parse_flow_nlattrs(const struct nlattr *attr, static int __parse_flow_nlattrs(const struct nlattr *attr,
const struct nlattr *a[], const struct nlattr *a[],
u64 *attrsp, bool nz) u64 *attrsp, bool nz)
...@@ -487,8 +501,9 @@ static int metadata_from_nlattrs(struct sw_flow_match *match, u64 *attrs, ...@@ -487,8 +501,9 @@ static int metadata_from_nlattrs(struct sw_flow_match *match, u64 *attrs,
return 0; return 0;
} }
static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs, static int ovs_key_from_nlattrs(struct sw_flow_match *match, bool *exact_5tuple,
const struct nlattr **a, bool is_mask) u64 attrs, const struct nlattr **a,
bool is_mask)
{ {
int err; int err;
u64 orig_attrs = attrs; u64 orig_attrs = attrs;
...@@ -545,6 +560,11 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs, ...@@ -545,6 +560,11 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs,
SW_FLOW_KEY_PUT(match, eth.type, htons(ETH_P_802_2), is_mask); SW_FLOW_KEY_PUT(match, eth.type, htons(ETH_P_802_2), is_mask);
} }
if (is_mask && exact_5tuple) {
if (match->mask->key.eth.type != htons(0xffff))
*exact_5tuple = false;
}
if (attrs & (1 << OVS_KEY_ATTR_IPV4)) { if (attrs & (1 << OVS_KEY_ATTR_IPV4)) {
const struct ovs_key_ipv4 *ipv4_key; const struct ovs_key_ipv4 *ipv4_key;
...@@ -567,6 +587,13 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs, ...@@ -567,6 +587,13 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs,
SW_FLOW_KEY_PUT(match, ipv4.addr.dst, SW_FLOW_KEY_PUT(match, ipv4.addr.dst,
ipv4_key->ipv4_dst, is_mask); ipv4_key->ipv4_dst, is_mask);
attrs &= ~(1 << OVS_KEY_ATTR_IPV4); attrs &= ~(1 << OVS_KEY_ATTR_IPV4);
if (is_mask && exact_5tuple && *exact_5tuple) {
if (ipv4_key->ipv4_proto != 0xff ||
ipv4_key->ipv4_src != htonl(0xffffffff) ||
ipv4_key->ipv4_dst != htonl(0xffffffff))
*exact_5tuple = false;
}
} }
if (attrs & (1 << OVS_KEY_ATTR_IPV6)) { if (attrs & (1 << OVS_KEY_ATTR_IPV6)) {
...@@ -598,6 +625,13 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs, ...@@ -598,6 +625,13 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs,
is_mask); is_mask);
attrs &= ~(1 << OVS_KEY_ATTR_IPV6); attrs &= ~(1 << OVS_KEY_ATTR_IPV6);
if (is_mask && exact_5tuple && *exact_5tuple) {
if (ipv6_key->ipv6_proto != 0xff ||
!is_all_set((u8 *)ipv6_key->ipv6_src, sizeof(match->key->ipv6.addr.src)) ||
!is_all_set((u8 *)ipv6_key->ipv6_dst, sizeof(match->key->ipv6.addr.dst)))
*exact_5tuple = false;
}
} }
if (attrs & (1 << OVS_KEY_ATTR_ARP)) { if (attrs & (1 << OVS_KEY_ATTR_ARP)) {
...@@ -640,6 +674,11 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs, ...@@ -640,6 +674,11 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs,
tcp_key->tcp_dst, is_mask); tcp_key->tcp_dst, is_mask);
} }
attrs &= ~(1 << OVS_KEY_ATTR_TCP); attrs &= ~(1 << OVS_KEY_ATTR_TCP);
if (is_mask && exact_5tuple && *exact_5tuple &&
(tcp_key->tcp_src != htons(0xffff) ||
tcp_key->tcp_dst != htons(0xffff)))
*exact_5tuple = false;
} }
if (attrs & (1 << OVS_KEY_ATTR_TCP_FLAGS)) { if (attrs & (1 << OVS_KEY_ATTR_TCP_FLAGS)) {
...@@ -671,6 +710,11 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs, ...@@ -671,6 +710,11 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs,
udp_key->udp_dst, is_mask); udp_key->udp_dst, is_mask);
} }
attrs &= ~(1 << OVS_KEY_ATTR_UDP); attrs &= ~(1 << OVS_KEY_ATTR_UDP);
if (is_mask && exact_5tuple && *exact_5tuple &&
(udp_key->udp_src != htons(0xffff) ||
udp_key->udp_dst != htons(0xffff)))
*exact_5tuple = false;
} }
if (attrs & (1 << OVS_KEY_ATTR_SCTP)) { if (attrs & (1 << OVS_KEY_ATTR_SCTP)) {
...@@ -756,6 +800,7 @@ static void sw_flow_mask_set(struct sw_flow_mask *mask, ...@@ -756,6 +800,7 @@ static void sw_flow_mask_set(struct sw_flow_mask *mask,
* attribute specifies the mask field of the wildcarded flow. * attribute specifies the mask field of the wildcarded flow.
*/ */
int ovs_nla_get_match(struct sw_flow_match *match, int ovs_nla_get_match(struct sw_flow_match *match,
bool *exact_5tuple,
const struct nlattr *key, const struct nlattr *key,
const struct nlattr *mask) const struct nlattr *mask)
{ {
...@@ -803,10 +848,13 @@ int ovs_nla_get_match(struct sw_flow_match *match, ...@@ -803,10 +848,13 @@ int ovs_nla_get_match(struct sw_flow_match *match,
} }
} }
err = ovs_key_from_nlattrs(match, key_attrs, a, false); err = ovs_key_from_nlattrs(match, NULL, key_attrs, a, false);
if (err) if (err)
return err; return err;
if (exact_5tuple)
*exact_5tuple = true;
if (mask) { if (mask) {
err = parse_flow_mask_nlattrs(mask, a, &mask_attrs); err = parse_flow_mask_nlattrs(mask, a, &mask_attrs);
if (err) if (err)
...@@ -844,7 +892,7 @@ int ovs_nla_get_match(struct sw_flow_match *match, ...@@ -844,7 +892,7 @@ int ovs_nla_get_match(struct sw_flow_match *match,
} }
} }
err = ovs_key_from_nlattrs(match, mask_attrs, a, true); err = ovs_key_from_nlattrs(match, exact_5tuple, mask_attrs, a, true);
if (err) if (err)
return err; return err;
} else { } else {
...@@ -1128,19 +1176,11 @@ struct sw_flow_actions *ovs_nla_alloc_flow_actions(int size) ...@@ -1128,19 +1176,11 @@ struct sw_flow_actions *ovs_nla_alloc_flow_actions(int size)
return sfa; return sfa;
} }
/* RCU callback used by ovs_nla_free_flow_actions. */
static void rcu_free_acts_callback(struct rcu_head *rcu)
{
struct sw_flow_actions *sf_acts = container_of(rcu,
struct sw_flow_actions, rcu);
kfree(sf_acts);
}
/* Schedules 'sf_acts' to be freed after the next RCU grace period. /* Schedules 'sf_acts' to be freed after the next RCU grace period.
* The caller must hold rcu_read_lock for this to be sensible. */ * The caller must hold rcu_read_lock for this to be sensible. */
void ovs_nla_free_flow_actions(struct sw_flow_actions *sf_acts) void ovs_nla_free_flow_actions(struct sw_flow_actions *sf_acts)
{ {
call_rcu(&sf_acts->rcu, rcu_free_acts_callback); kfree_rcu(sf_acts, rcu);
} }
static struct nlattr *reserve_sfa_size(struct sw_flow_actions **sfa, static struct nlattr *reserve_sfa_size(struct sw_flow_actions **sfa,
......
...@@ -45,6 +45,7 @@ int ovs_nla_put_flow(const struct sw_flow_key *, ...@@ -45,6 +45,7 @@ int ovs_nla_put_flow(const struct sw_flow_key *,
int ovs_nla_get_flow_metadata(struct sw_flow *flow, int ovs_nla_get_flow_metadata(struct sw_flow *flow,
const struct nlattr *attr); const struct nlattr *attr);
int ovs_nla_get_match(struct sw_flow_match *match, int ovs_nla_get_match(struct sw_flow_match *match,
bool *exact_5tuple,
const struct nlattr *, const struct nlattr *,
const struct nlattr *); const struct nlattr *);
......
...@@ -44,8 +44,6 @@ ...@@ -44,8 +44,6 @@
#include <net/ipv6.h> #include <net/ipv6.h>
#include <net/ndisc.h> #include <net/ndisc.h>
#include "datapath.h"
#define TBL_MIN_BUCKETS 1024 #define TBL_MIN_BUCKETS 1024
#define REHASH_INTERVAL (10 * 60 * HZ) #define REHASH_INTERVAL (10 * 60 * HZ)
...@@ -72,19 +70,42 @@ void ovs_flow_mask_key(struct sw_flow_key *dst, const struct sw_flow_key *src, ...@@ -72,19 +70,42 @@ void ovs_flow_mask_key(struct sw_flow_key *dst, const struct sw_flow_key *src,
*d++ = *s++ & *m++; *d++ = *s++ & *m++;
} }
struct sw_flow *ovs_flow_alloc(void) struct sw_flow *ovs_flow_alloc(bool percpu_stats)
{ {
struct sw_flow *flow; struct sw_flow *flow;
int cpu;
flow = kmem_cache_alloc(flow_cache, GFP_KERNEL); flow = kmem_cache_alloc(flow_cache, GFP_KERNEL);
if (!flow) if (!flow)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
spin_lock_init(&flow->lock);
flow->sf_acts = NULL; flow->sf_acts = NULL;
flow->mask = NULL; flow->mask = NULL;
flow->stats.is_percpu = percpu_stats;
if (!percpu_stats) {
flow->stats.stat = kzalloc(sizeof(*flow->stats.stat), GFP_KERNEL);
if (!flow->stats.stat)
goto err;
spin_lock_init(&flow->stats.stat->lock);
} else {
flow->stats.cpu_stats = alloc_percpu(struct flow_stats);
if (!flow->stats.cpu_stats)
goto err;
for_each_possible_cpu(cpu) {
struct flow_stats *cpu_stats;
cpu_stats = per_cpu_ptr(flow->stats.cpu_stats, cpu);
spin_lock_init(&cpu_stats->lock);
}
}
return flow; return flow;
err:
kfree(flow);
return ERR_PTR(-ENOMEM);
} }
int ovs_flow_tbl_count(struct flow_table *table) int ovs_flow_tbl_count(struct flow_table *table)
...@@ -118,6 +139,10 @@ static struct flex_array *alloc_buckets(unsigned int n_buckets) ...@@ -118,6 +139,10 @@ static struct flex_array *alloc_buckets(unsigned int n_buckets)
static void flow_free(struct sw_flow *flow) static void flow_free(struct sw_flow *flow)
{ {
kfree((struct sf_flow_acts __force *)flow->sf_acts); kfree((struct sf_flow_acts __force *)flow->sf_acts);
if (flow->stats.is_percpu)
free_percpu(flow->stats.cpu_stats);
else
kfree(flow->stats.stat);
kmem_cache_free(flow_cache, flow); kmem_cache_free(flow_cache, flow);
} }
...@@ -128,13 +153,6 @@ static void rcu_free_flow_callback(struct rcu_head *rcu) ...@@ -128,13 +153,6 @@ static void rcu_free_flow_callback(struct rcu_head *rcu)
flow_free(flow); flow_free(flow);
} }
static void rcu_free_sw_flow_mask_cb(struct rcu_head *rcu)
{
struct sw_flow_mask *mask = container_of(rcu, struct sw_flow_mask, rcu);
kfree(mask);
}
static void flow_mask_del_ref(struct sw_flow_mask *mask, bool deferred) static void flow_mask_del_ref(struct sw_flow_mask *mask, bool deferred)
{ {
if (!mask) if (!mask)
...@@ -146,7 +164,7 @@ static void flow_mask_del_ref(struct sw_flow_mask *mask, bool deferred) ...@@ -146,7 +164,7 @@ static void flow_mask_del_ref(struct sw_flow_mask *mask, bool deferred)
if (!mask->ref_count) { if (!mask->ref_count) {
list_del_rcu(&mask->list); list_del_rcu(&mask->list);
if (deferred) if (deferred)
call_rcu(&mask->rcu, rcu_free_sw_flow_mask_cb); kfree_rcu(mask, rcu);
else else
kfree(mask); kfree(mask);
} }
...@@ -429,11 +447,11 @@ static struct sw_flow *masked_flow_lookup(struct table_instance *ti, ...@@ -429,11 +447,11 @@ static struct sw_flow *masked_flow_lookup(struct table_instance *ti,
return NULL; return NULL;
} }
struct sw_flow *ovs_flow_tbl_lookup(struct flow_table *tbl, struct sw_flow *ovs_flow_tbl_lookup_stats(struct flow_table *tbl,
const struct sw_flow_key *key, const struct sw_flow_key *key,
u32 *n_mask_hit) u32 *n_mask_hit)
{ {
struct table_instance *ti = rcu_dereference(tbl->ti); struct table_instance *ti = rcu_dereference_ovsl(tbl->ti);
struct sw_flow_mask *mask; struct sw_flow_mask *mask;
struct sw_flow *flow; struct sw_flow *flow;
...@@ -447,6 +465,14 @@ struct sw_flow *ovs_flow_tbl_lookup(struct flow_table *tbl, ...@@ -447,6 +465,14 @@ struct sw_flow *ovs_flow_tbl_lookup(struct flow_table *tbl,
return NULL; return NULL;
} }
struct sw_flow *ovs_flow_tbl_lookup(struct flow_table *tbl,
const struct sw_flow_key *key)
{
u32 __always_unused n_mask_hit;
return ovs_flow_tbl_lookup_stats(tbl, key, &n_mask_hit);
}
int ovs_flow_tbl_num_masks(const struct flow_table *table) int ovs_flow_tbl_num_masks(const struct flow_table *table)
{ {
struct sw_flow_mask *mask; struct sw_flow_mask *mask;
...@@ -514,11 +540,7 @@ static struct sw_flow_mask *flow_mask_find(const struct flow_table *tbl, ...@@ -514,11 +540,7 @@ static struct sw_flow_mask *flow_mask_find(const struct flow_table *tbl,
return NULL; return NULL;
} }
/** /* Add 'mask' into the mask list, if it is not already there. */
* add a new mask into the mask list.
* The caller needs to make sure that 'mask' is not the same
* as any masks that are already on the list.
*/
static int flow_mask_insert(struct flow_table *tbl, struct sw_flow *flow, static int flow_mask_insert(struct flow_table *tbl, struct sw_flow *flow,
struct sw_flow_mask *new) struct sw_flow_mask *new)
{ {
......
...@@ -55,7 +55,7 @@ struct flow_table { ...@@ -55,7 +55,7 @@ struct flow_table {
int ovs_flow_init(void); int ovs_flow_init(void);
void ovs_flow_exit(void); void ovs_flow_exit(void);
struct sw_flow *ovs_flow_alloc(void); struct sw_flow *ovs_flow_alloc(bool percpu_stats);
void ovs_flow_free(struct sw_flow *, bool deferred); void ovs_flow_free(struct sw_flow *, bool deferred);
int ovs_flow_tbl_init(struct flow_table *); int ovs_flow_tbl_init(struct flow_table *);
...@@ -69,9 +69,11 @@ void ovs_flow_tbl_remove(struct flow_table *table, struct sw_flow *flow); ...@@ -69,9 +69,11 @@ void ovs_flow_tbl_remove(struct flow_table *table, struct sw_flow *flow);
int ovs_flow_tbl_num_masks(const struct flow_table *table); int ovs_flow_tbl_num_masks(const struct flow_table *table);
struct sw_flow *ovs_flow_tbl_dump_next(struct table_instance *table, struct sw_flow *ovs_flow_tbl_dump_next(struct table_instance *table,
u32 *bucket, u32 *idx); u32 *bucket, u32 *idx);
struct sw_flow *ovs_flow_tbl_lookup(struct flow_table *, struct sw_flow *ovs_flow_tbl_lookup_stats(struct flow_table *,
const struct sw_flow_key *, const struct sw_flow_key *,
u32 *n_mask_hit); u32 *n_mask_hit);
struct sw_flow *ovs_flow_tbl_lookup(struct flow_table *,
const struct sw_flow_key *);
bool ovs_flow_cmp_unmasked_key(const struct sw_flow *flow, bool ovs_flow_cmp_unmasked_key(const struct sw_flow *flow,
struct sw_flow_match *match); struct sw_flow_match *match);
......
...@@ -33,6 +33,9 @@ ...@@ -33,6 +33,9 @@
#include "vport.h" #include "vport.h"
#include "vport-internal_dev.h" #include "vport-internal_dev.h"
static void ovs_vport_record_error(struct vport *,
enum vport_err_type err_type);
/* List of statically compiled vport implementations. Don't forget to also /* List of statically compiled vport implementations. Don't forget to also
* add yours to the list at the bottom of vport.h. */ * add yours to the list at the bottom of vport.h. */
static const struct vport_ops *vport_ops_list[] = { static const struct vport_ops *vport_ops_list[] = {
...@@ -396,7 +399,8 @@ int ovs_vport_send(struct vport *vport, struct sk_buff *skb) ...@@ -396,7 +399,8 @@ int ovs_vport_send(struct vport *vport, struct sk_buff *skb)
* If using the vport generic stats layer indicate that an error of the given * If using the vport generic stats layer indicate that an error of the given
* type has occurred. * type has occurred.
*/ */
void ovs_vport_record_error(struct vport *vport, enum vport_err_type err_type) static void ovs_vport_record_error(struct vport *vport,
enum vport_err_type err_type)
{ {
spin_lock(&vport->stats_lock); spin_lock(&vport->stats_lock);
......
...@@ -192,7 +192,6 @@ static inline struct vport *vport_from_priv(const void *priv) ...@@ -192,7 +192,6 @@ static inline struct vport *vport_from_priv(const void *priv)
void ovs_vport_receive(struct vport *, struct sk_buff *, void ovs_vport_receive(struct vport *, struct sk_buff *,
struct ovs_key_ipv4_tunnel *); struct ovs_key_ipv4_tunnel *);
void ovs_vport_record_error(struct vport *, enum vport_err_type err_type);
/* List of statically compiled vport implementations. Don't forget to also /* List of statically compiled vport implementations. Don't forget to also
* add yours to the list at the top of vport.c. */ * add yours to the list at the top of vport.c. */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment