From: Jesse Gross Date: Mon, 10 May 2010 20:07:28 +0000 (-0700) Subject: datapath: Add 'patch' vport. X-Git-Tag: v1.0.1~43 X-Git-Url: http://git.onelab.eu/?a=commitdiff_plain;h=4fe648861042761f3a132ba8a998aacd03ad8e4a;p=sliver-openvswitch.git datapath: Add 'patch' vport. Adds a new 'patch' vport that acts like a virtual patch cable to connect together two datapaths. --- diff --git a/datapath/Modules.mk b/datapath/Modules.mk index 37777436b..7ae1383c2 100644 --- a/datapath/Modules.mk +++ b/datapath/Modules.mk @@ -21,7 +21,8 @@ openvswitch_sources = \ vport-generic.c \ vport-gre.c \ vport-internal_dev.c \ - vport-netdev.c + vport-netdev.c \ + vport-patch.c openvswitch_headers = \ actions.h \ diff --git a/datapath/linux-2.6/.gitignore b/datapath/linux-2.6/.gitignore index d8b4a2565..4b5658b29 100644 --- a/datapath/linux-2.6/.gitignore +++ b/datapath/linux-2.6/.gitignore @@ -31,4 +31,5 @@ /vport-gre.c /vport-internal_dev.c /vport-netdev.c +/vport-patch.c /vport.c diff --git a/datapath/vport-patch.c b/datapath/vport-patch.c new file mode 100644 index 000000000..96e1a10f8 --- /dev/null +++ b/datapath/vport-patch.c @@ -0,0 +1,304 @@ +/* + * Copyright (c) 2010 Nicira Networks. + * Distributed under the terms of the GNU GPL version 2. + * + * Significant portions of this file may be copied from parts of the Linux + * kernel, by Linus Torvalds and others. + */ + +#include +#include +#include + +#include "datapath.h" +#include "vport.h" +#include "vport-generic.h" + +struct device_config { + struct rcu_head rcu; + + unsigned char eth_addr[ETH_ALEN]; + unsigned int mtu; +}; + +struct patch_vport { + 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_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) +{ + return vport_priv(vport); +} + +/* RCU callback. */ +static void +free_config(struct rcu_head *rcu) +{ + struct device_config *c = container_of(rcu, struct device_config, rcu); + kfree(c); +} + +static void +assign_config_rcu(struct vport *vport, struct device_config *new_config) +{ + struct patch_vport *patch_vport = patch_vport_priv(vport); + struct device_config *old_config; + + old_config = rcu_dereference(patch_vport->devconf); + rcu_assign_pointer(patch_vport->devconf, new_config); + call_rcu(&old_config->rcu, free_config); +} + +static struct hlist_head * +hash_bucket(const char *name) +{ + unsigned int hash = full_name_hash(name, strlen(name)); + return &peer_table[hash & (PEER_HASH_BUCKETS - 1)]; +} + +static int +patch_init(void) +{ + peer_table = kzalloc(PEER_HASH_BUCKETS * sizeof(struct hlist_head), + GFP_KERNEL); + if (!peer_table) + return -ENOMEM; + + return 0; +} + +static void +patch_exit(void) +{ + kfree(peer_table); +} + +static int +set_config(struct vport *vport, const void __user *uconfig) +{ + struct patch_vport *patch_vport = patch_vport_priv(vport); + char peer_name[IFNAMSIZ]; + int retval; + + retval = strncpy_from_user(peer_name, uconfig, IFNAMSIZ); + if (retval < 0) + return -EFAULT; + else if (retval >= IFNAMSIZ) + return -ENAMETOOLONG; + + if (!strcmp(patch_vport->name, peer_name)) + return -EINVAL; + + strcpy(patch_vport->peer_name, peer_name); + + 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)); + } + + return 0; +} + +static struct vport * +patch_create(const char *name, const void __user *config) +{ + struct vport *vport; + struct patch_vport *patch_vport; + int err; + + vport = vport_alloc(sizeof(struct patch_vport), &patch_vport_ops); + if (IS_ERR(vport)) { + err = PTR_ERR(vport); + goto error; + } + + patch_vport = patch_vport_priv(vport); + + strcpy(patch_vport->name, 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) { + err = -ENOMEM; + goto error_free_vport; + } + + vport_gen_rand_ether_addr(patch_vport->devconf->eth_addr); + patch_vport->devconf->mtu = ETH_DATA_LEN; + + return vport; + +error_free_vport: + vport_free(vport); +error: + return ERR_PTR(err); +} + +static int +patch_modify(struct vport *vport, const void __user *config) +{ + return set_config(vport, config); +} + +static int +patch_destroy(struct vport *vport) +{ + struct patch_vport *patch_vport = patch_vport_priv(vport); + + kfree(patch_vport->devconf); + vport_free(vport); + + return 0; +} + +static void +update_peers(const char *name, struct vport *vport) +{ + struct hlist_head *bucket = hash_bucket(name); + struct patch_vport *peer_vport; + struct hlist_node *node; + + hlist_for_each_entry(peer_vport, node, bucket, hash_node) + if (!strcmp(peer_vport->peer_name, name)) + rcu_assign_pointer(peer_vport->peer, vport); +} + +static int +patch_attach(struct vport *vport) +{ + struct patch_vport *patch_vport = patch_vport_priv(vport); + + hlist_add_head(&patch_vport->hash_node, hash_bucket(patch_vport->peer_name)); + + rcu_assign_pointer(patch_vport->peer, vport_locate(patch_vport->peer_name)); + update_peers(patch_vport->name, vport); + + return 0; +} + +static int +patch_detach(struct vport *vport) +{ + struct patch_vport *patch_vport = patch_vport_priv(vport); + + update_peers(patch_vport->name, NULL); + rcu_assign_pointer(patch_vport->peer, NULL); + + hlist_del(&patch_vport->hash_node); + + return 0; +} + +static int +patch_set_mtu(struct vport *vport, int mtu) +{ + 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; + + devconf->mtu = mtu; + assign_config_rcu(vport, devconf); + + return 0; +} + +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; + + devconf = kmemdup(patch_vport->devconf, sizeof(struct device_config), GFP_KERNEL); + if (!devconf) + return -ENOMEM; + + memcpy(devconf->eth_addr, addr, ETH_ALEN); + assign_config_rcu(vport, devconf); + + return 0; +} + + +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) +{ + const struct patch_vport *patch_vport = patch_vport_priv(vport); + return rcu_dereference(patch_vport->devconf)->eth_addr; +} + +static int +patch_get_mtu(const struct vport *vport) +{ + const struct patch_vport *patch_vport = patch_vport_priv(vport); + return rcu_dereference(patch_vport->devconf)->mtu; +} + +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); + int skb_len = skb->len; + + if (!peer) { + kfree_skb(skb); + vport_record_error(vport, VPORT_E_TX_DROPPED); + + return 0; + } + + vport_receive(peer, skb); + return skb_len; +} + +struct vport_ops patch_vport_ops = { + .type = "patch", + .flags = VPORT_F_GEN_STATS, + .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, + .send = patch_send, +}; diff --git a/datapath/vport.c b/datapath/vport.c index f1c78b11f..945ef4485 100644 --- a/datapath/vport.c +++ b/datapath/vport.c @@ -21,11 +21,13 @@ extern struct vport_ops netdev_vport_ops; extern struct vport_ops internal_vport_ops; +extern struct vport_ops patch_vport_ops; extern struct vport_ops gre_vport_ops; static struct vport_ops *base_vport_ops_list[] = { &netdev_vport_ops, &internal_vport_ops, + &patch_vport_ops, &gre_vport_ops, };