X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=datapath%2Fvport-patch.c;h=501eb7ad4577c71c7f05b92af18971bbf4dd0a00;hb=5ca1ba484bd9ade5116a49cf241cb98219d7d696;hp=96e1a10f84dd7b556a011e563e629f2b8b8b4be6;hpb=4fe648861042761f3a132ba8a998aacd03ad8e4a;p=sliver-openvswitch.git diff --git a/datapath/vport-patch.c b/datapath/vport-patch.c index 96e1a10f8..501eb7ad4 100644 --- a/datapath/vport-patch.c +++ b/datapath/vport-patch.c @@ -1,80 +1,86 @@ /* - * Copyright (c) 2010 Nicira Networks. - * Distributed under the terms of the GNU GPL version 2. + * Copyright (c) 2007-2012 Nicira, Inc. * - * Significant portions of this file may be copied from parts of the Linux - * kernel, by Linus Torvalds and others. + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA */ -#include #include +#include #include +#include +#include +#include "compat.h" #include "datapath.h" #include "vport.h" -#include "vport-generic.h" -struct device_config { +struct patch_config { struct rcu_head rcu; + char peer_name[IFNAMSIZ]; unsigned char eth_addr[ETH_ALEN]; - unsigned int mtu; }; struct patch_vport { + struct rcu_head rcu; + char name[IFNAMSIZ]; /* Protected by RTNL lock. */ - char peer_name[IFNAMSIZ]; struct hlist_node hash_node; - /* Protected by RCU. */ - struct vport *peer; - - /* Protected by RCU. */ - struct device_config *devconf; + struct vport __rcu *peer; + struct patch_config __rcu *patchconf; }; -struct vport_ops patch_vport_ops; - /* Protected by RTNL lock. */ static struct hlist_head *peer_table; #define PEER_HASH_BUCKETS 256 -static inline struct patch_vport * -patch_vport_priv(const struct vport *vport) +static void update_peers(struct net *, const char *name, struct vport *); + +static struct patch_vport *patch_vport_priv(const struct vport *vport) { return vport_priv(vport); } /* RCU callback. */ -static void -free_config(struct rcu_head *rcu) +static void free_config(struct rcu_head *rcu) { - struct device_config *c = container_of(rcu, struct device_config, rcu); + struct patch_config *c = container_of(rcu, struct patch_config, rcu); kfree(c); } -static void -assign_config_rcu(struct vport *vport, struct device_config *new_config) +static void assign_config_rcu(struct vport *vport, + struct patch_config *new_config) { struct patch_vport *patch_vport = patch_vport_priv(vport); - struct device_config *old_config; + struct patch_config *old_config; - old_config = rcu_dereference(patch_vport->devconf); - rcu_assign_pointer(patch_vport->devconf, new_config); + old_config = rtnl_dereference(patch_vport->patchconf); + rcu_assign_pointer(patch_vport->patchconf, new_config); call_rcu(&old_config->rcu, free_config); } -static struct hlist_head * -hash_bucket(const char *name) +static struct hlist_head *hash_bucket(struct net *net, const char *name) { - unsigned int hash = full_name_hash(name, strlen(name)); + unsigned int hash = jhash(name, strlen(name), (unsigned long) net); return &peer_table[hash & (PEER_HASH_BUCKETS - 1)]; } -static int -patch_init(void) +static int patch_init(void) { peer_table = kzalloc(PEER_HASH_BUCKETS * sizeof(struct hlist_head), GFP_KERNEL); @@ -84,47 +90,56 @@ patch_init(void) return 0; } -static void -patch_exit(void) +static void patch_exit(void) { kfree(peer_table); } -static int -set_config(struct vport *vport, const void __user *uconfig) +static const struct nla_policy patch_policy[OVS_PATCH_ATTR_MAX + 1] = { +#ifdef HAVE_NLA_NUL_STRING + [OVS_PATCH_ATTR_PEER] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ - 1 }, +#endif +}; + +static int patch_set_config(struct vport *vport, const struct nlattr *options, + struct patch_config *patchconf) { struct patch_vport *patch_vport = patch_vport_priv(vport); - char peer_name[IFNAMSIZ]; - int retval; + struct nlattr *a[OVS_PATCH_ATTR_MAX + 1]; + const char *peer_name; + int err; - retval = strncpy_from_user(peer_name, uconfig, IFNAMSIZ); - if (retval < 0) - return -EFAULT; - else if (retval >= IFNAMSIZ) - return -ENAMETOOLONG; + if (!options) + return -EINVAL; - if (!strcmp(patch_vport->name, peer_name)) + err = nla_parse_nested(a, OVS_PATCH_ATTR_MAX, options, patch_policy); + if (err) + return err; + + if (!a[OVS_PATCH_ATTR_PEER] || + CHECK_NUL_STRING(a[OVS_PATCH_ATTR_PEER], IFNAMSIZ - 1)) return -EINVAL; - strcpy(patch_vport->peer_name, peer_name); + peer_name = nla_data(a[OVS_PATCH_ATTR_PEER]); + if (!strcmp(patch_vport->name, peer_name)) + return -EINVAL; - if (vport_get_dp_port(vport)) { - hlist_del(&patch_vport->hash_node); - rcu_assign_pointer(patch_vport->peer, vport_locate(patch_vport->peer_name)); - hlist_add_head(&patch_vport->hash_node, hash_bucket(patch_vport->peer_name)); - } + strcpy(patchconf->peer_name, peer_name); return 0; } -static struct vport * -patch_create(const char *name, const void __user *config) +static struct vport *patch_create(const struct vport_parms *parms) { struct vport *vport; struct patch_vport *patch_vport; + const char *peer_name; + struct patch_config *patchconf; + struct net *net = ovs_dp_get_net(parms->dp); int err; - vport = vport_alloc(sizeof(struct patch_vport), &patch_vport_ops); + vport = ovs_vport_alloc(sizeof(struct patch_vport), + &ovs_patch_vport_ops, parms); if (IS_ERR(vport)) { err = PTR_ERR(vport); goto error; @@ -132,140 +147,143 @@ patch_create(const char *name, const void __user *config) patch_vport = patch_vport_priv(vport); - strcpy(patch_vport->name, name); + strcpy(patch_vport->name, parms->name); - err = set_config(vport, config); - if (err) - goto error_free_vport; - - patch_vport->devconf = kmalloc(sizeof(struct device_config), GFP_KERNEL); - if (!patch_vport->devconf) { + patchconf = kmalloc(sizeof(struct patch_config), GFP_KERNEL); + if (!patchconf) { err = -ENOMEM; goto error_free_vport; } - vport_gen_rand_ether_addr(patch_vport->devconf->eth_addr); - patch_vport->devconf->mtu = ETH_DATA_LEN; + err = patch_set_config(vport, parms->options, patchconf); + if (err) + goto error_free_patchconf; + + random_ether_addr(patchconf->eth_addr); + + rcu_assign_pointer(patch_vport->patchconf, patchconf); + + peer_name = patchconf->peer_name; + hlist_add_head(&patch_vport->hash_node, hash_bucket(net, peer_name)); + rcu_assign_pointer(patch_vport->peer, ovs_vport_locate(net, peer_name)); + update_peers(net, patch_vport->name, vport); return vport; +error_free_patchconf: + kfree(patchconf); error_free_vport: - vport_free(vport); + ovs_vport_free(vport); error: return ERR_PTR(err); } -static int -patch_modify(struct vport *vport, const void __user *config) +static void free_port_rcu(struct rcu_head *rcu) { - return set_config(vport, config); -} - -static int -patch_destroy(struct vport *vport) -{ - struct patch_vport *patch_vport = patch_vport_priv(vport); + struct patch_vport *patch_vport = container_of(rcu, + struct patch_vport, rcu); - kfree(patch_vport->devconf); - vport_free(vport); - - return 0; + kfree((struct patch_config __force *)patch_vport->patchconf); + ovs_vport_free(vport_from_priv(patch_vport)); } -static void -update_peers(const char *name, struct vport *vport) +static void patch_destroy(struct vport *vport) { - struct hlist_head *bucket = hash_bucket(name); - struct patch_vport *peer_vport; - struct hlist_node *node; + struct patch_vport *patch_vport = patch_vport_priv(vport); - hlist_for_each_entry(peer_vport, node, bucket, hash_node) - if (!strcmp(peer_vport->peer_name, name)) - rcu_assign_pointer(peer_vport->peer, vport); + update_peers(ovs_dp_get_net(vport->dp), patch_vport->name, NULL); + hlist_del(&patch_vport->hash_node); + call_rcu(&patch_vport->rcu, free_port_rcu); } -static int -patch_attach(struct vport *vport) +static int patch_set_options(struct vport *vport, struct nlattr *options) { struct patch_vport *patch_vport = patch_vport_priv(vport); + struct patch_config *patchconf; + int err; - hlist_add_head(&patch_vport->hash_node, hash_bucket(patch_vport->peer_name)); + patchconf = kmemdup(rtnl_dereference(patch_vport->patchconf), + sizeof(struct patch_config), GFP_KERNEL); + if (!patchconf) { + err = -ENOMEM; + goto error; + } - rcu_assign_pointer(patch_vport->peer, vport_locate(patch_vport->peer_name)); - update_peers(patch_vport->name, vport); + err = patch_set_config(vport, options, patchconf); + if (err) + goto error_free; - return 0; -} + assign_config_rcu(vport, patchconf); -static int -patch_detach(struct vport *vport) -{ - struct patch_vport *patch_vport = patch_vport_priv(vport); + hlist_del(&patch_vport->hash_node); - update_peers(patch_vport->name, NULL); - rcu_assign_pointer(patch_vport->peer, NULL); + rcu_assign_pointer(patch_vport->peer, + ovs_vport_locate(ovs_dp_get_net(vport->dp), patchconf->peer_name)); - hlist_del(&patch_vport->hash_node); + hlist_add_head(&patch_vport->hash_node, + hash_bucket(ovs_dp_get_net(vport->dp), patchconf->peer_name)); return 0; +error_free: + kfree(patchconf); +error: + return err; } -static int -patch_set_mtu(struct vport *vport, int mtu) +static void update_peers(struct net *net, const char *name, struct vport *vport) { - struct patch_vport *patch_vport = patch_vport_priv(vport); - struct device_config *devconf; - - devconf = kmemdup(patch_vport->devconf, sizeof(struct device_config), GFP_KERNEL); - if (!devconf) - return -ENOMEM; + struct hlist_head *bucket = hash_bucket(net, name); + struct patch_vport *peer_vport; + struct hlist_node *node; - devconf->mtu = mtu; - assign_config_rcu(vport, devconf); + hlist_for_each_entry(peer_vport, node, bucket, hash_node) { + struct vport *curr_vport = vport_from_priv(peer_vport); + const char *peer_name; - return 0; + peer_name = rtnl_dereference(peer_vport->patchconf)->peer_name; + if (!strcmp(peer_name, name) && net_eq(ovs_dp_get_net(curr_vport->dp), net)) + rcu_assign_pointer(peer_vport->peer, vport); + } } -static int -patch_set_addr(struct vport *vport, const unsigned char *addr) +static int patch_set_addr(struct vport *vport, const unsigned char *addr) { struct patch_vport *patch_vport = patch_vport_priv(vport); - struct device_config *devconf; + struct patch_config *patchconf; - devconf = kmemdup(patch_vport->devconf, sizeof(struct device_config), GFP_KERNEL); - if (!devconf) + patchconf = kmemdup(rtnl_dereference(patch_vport->patchconf), + sizeof(struct patch_config), GFP_KERNEL); + if (!patchconf) return -ENOMEM; - memcpy(devconf->eth_addr, addr, ETH_ALEN); - assign_config_rcu(vport, devconf); + memcpy(patchconf->eth_addr, addr, ETH_ALEN); + assign_config_rcu(vport, patchconf); return 0; } -static const char * -patch_get_name(const struct vport *vport) +static const char *patch_get_name(const struct vport *vport) { const struct patch_vport *patch_vport = patch_vport_priv(vport); return patch_vport->name; } -static const unsigned char * -patch_get_addr(const struct vport *vport) +static const unsigned char *patch_get_addr(const struct vport *vport) { const struct patch_vport *patch_vport = patch_vport_priv(vport); - return rcu_dereference(patch_vport->devconf)->eth_addr; + return rcu_dereference_rtnl(patch_vport->patchconf)->eth_addr; } -static int -patch_get_mtu(const struct vport *vport) +static int patch_get_options(const struct vport *vport, struct sk_buff *skb) { - const struct patch_vport *patch_vport = patch_vport_priv(vport); - return rcu_dereference(patch_vport->devconf)->mtu; + struct patch_vport *patch_vport = patch_vport_priv(vport); + struct patch_config *patchconf = rcu_dereference_rtnl(patch_vport->patchconf); + + return nla_put_string(skb, OVS_PATCH_ATTR_PEER, patchconf->peer_name); } -static int -patch_send(struct vport *vport, struct sk_buff *skb) +static int patch_send(struct vport *vport, struct sk_buff *skb) { struct patch_vport *patch_vport = patch_vport_priv(vport); struct vport *peer = rcu_dereference(patch_vport->peer); @@ -273,32 +291,25 @@ patch_send(struct vport *vport, struct sk_buff *skb) if (!peer) { kfree_skb(skb); - vport_record_error(vport, VPORT_E_TX_DROPPED); + ovs_vport_record_error(vport, VPORT_E_TX_DROPPED); return 0; } - vport_receive(peer, skb); + ovs_vport_receive(peer, skb); return skb_len; } -struct vport_ops patch_vport_ops = { - .type = "patch", - .flags = VPORT_F_GEN_STATS, +const struct vport_ops ovs_patch_vport_ops = { + .type = OVS_VPORT_TYPE_PATCH, .init = patch_init, .exit = patch_exit, .create = patch_create, - .modify = patch_modify, .destroy = patch_destroy, - .attach = patch_attach, - .detach = patch_detach, - .set_mtu = patch_set_mtu, .set_addr = patch_set_addr, .get_name = patch_get_name, .get_addr = patch_get_addr, - .get_dev_flags = vport_gen_get_dev_flags, - .is_running = vport_gen_is_running, - .get_operstate = vport_gen_get_operstate, - .get_mtu = patch_get_mtu, + .get_options = patch_get_options, + .set_options = patch_set_options, .send = patch_send, };