162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2007-2014 Nicira, Inc. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/etherdevice.h> 762306a36Sopenharmony_ci#include <linux/if.h> 862306a36Sopenharmony_ci#include <linux/if_vlan.h> 962306a36Sopenharmony_ci#include <linux/jhash.h> 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/list.h> 1262306a36Sopenharmony_ci#include <linux/mutex.h> 1362306a36Sopenharmony_ci#include <linux/percpu.h> 1462306a36Sopenharmony_ci#include <linux/rcupdate.h> 1562306a36Sopenharmony_ci#include <linux/rtnetlink.h> 1662306a36Sopenharmony_ci#include <linux/compat.h> 1762306a36Sopenharmony_ci#include <net/net_namespace.h> 1862306a36Sopenharmony_ci#include <linux/module.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include "datapath.h" 2162306a36Sopenharmony_ci#include "vport.h" 2262306a36Sopenharmony_ci#include "vport-internal_dev.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistatic LIST_HEAD(vport_ops_list); 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* Protected by RCU read lock for reading, ovs_mutex for writing. */ 2762306a36Sopenharmony_cistatic struct hlist_head *dev_table; 2862306a36Sopenharmony_ci#define VPORT_HASH_BUCKETS 1024 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/** 3162306a36Sopenharmony_ci * ovs_vport_init - initialize vport subsystem 3262306a36Sopenharmony_ci * 3362306a36Sopenharmony_ci * Called at module load time to initialize the vport subsystem. 3462306a36Sopenharmony_ci */ 3562306a36Sopenharmony_ciint ovs_vport_init(void) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci dev_table = kcalloc(VPORT_HASH_BUCKETS, sizeof(struct hlist_head), 3862306a36Sopenharmony_ci GFP_KERNEL); 3962306a36Sopenharmony_ci if (!dev_table) 4062306a36Sopenharmony_ci return -ENOMEM; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci return 0; 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/** 4662306a36Sopenharmony_ci * ovs_vport_exit - shutdown vport subsystem 4762306a36Sopenharmony_ci * 4862306a36Sopenharmony_ci * Called at module exit time to shutdown the vport subsystem. 4962306a36Sopenharmony_ci */ 5062306a36Sopenharmony_civoid ovs_vport_exit(void) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci kfree(dev_table); 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic struct hlist_head *hash_bucket(const struct net *net, const char *name) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci unsigned int hash = jhash(name, strlen(name), (unsigned long) net); 5862306a36Sopenharmony_ci return &dev_table[hash & (VPORT_HASH_BUCKETS - 1)]; 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ciint __ovs_vport_ops_register(struct vport_ops *ops) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci int err = -EEXIST; 6462306a36Sopenharmony_ci struct vport_ops *o; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci ovs_lock(); 6762306a36Sopenharmony_ci list_for_each_entry(o, &vport_ops_list, list) 6862306a36Sopenharmony_ci if (ops->type == o->type) 6962306a36Sopenharmony_ci goto errout; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci list_add_tail(&ops->list, &vport_ops_list); 7262306a36Sopenharmony_ci err = 0; 7362306a36Sopenharmony_cierrout: 7462306a36Sopenharmony_ci ovs_unlock(); 7562306a36Sopenharmony_ci return err; 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__ovs_vport_ops_register); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_civoid ovs_vport_ops_unregister(struct vport_ops *ops) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci ovs_lock(); 8262306a36Sopenharmony_ci list_del(&ops->list); 8362306a36Sopenharmony_ci ovs_unlock(); 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ovs_vport_ops_unregister); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci/** 8862306a36Sopenharmony_ci * ovs_vport_locate - find a port that has already been created 8962306a36Sopenharmony_ci * 9062306a36Sopenharmony_ci * @net: network namespace 9162306a36Sopenharmony_ci * @name: name of port to find 9262306a36Sopenharmony_ci * 9362306a36Sopenharmony_ci * Must be called with ovs or RCU read lock. 9462306a36Sopenharmony_ci */ 9562306a36Sopenharmony_cistruct vport *ovs_vport_locate(const struct net *net, const char *name) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci struct hlist_head *bucket = hash_bucket(net, name); 9862306a36Sopenharmony_ci struct vport *vport; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci hlist_for_each_entry_rcu(vport, bucket, hash_node, 10162306a36Sopenharmony_ci lockdep_ovsl_is_held()) 10262306a36Sopenharmony_ci if (!strcmp(name, ovs_vport_name(vport)) && 10362306a36Sopenharmony_ci net_eq(ovs_dp_get_net(vport->dp), net)) 10462306a36Sopenharmony_ci return vport; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci return NULL; 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci/** 11062306a36Sopenharmony_ci * ovs_vport_alloc - allocate and initialize new vport 11162306a36Sopenharmony_ci * 11262306a36Sopenharmony_ci * @priv_size: Size of private data area to allocate. 11362306a36Sopenharmony_ci * @ops: vport device ops 11462306a36Sopenharmony_ci * @parms: information about new vport. 11562306a36Sopenharmony_ci * 11662306a36Sopenharmony_ci * Allocate and initialize a new vport defined by @ops. The vport will contain 11762306a36Sopenharmony_ci * a private data area of size @priv_size that can be accessed using 11862306a36Sopenharmony_ci * vport_priv(). Some parameters of the vport will be initialized from @parms. 11962306a36Sopenharmony_ci * @vports that are no longer needed should be released with 12062306a36Sopenharmony_ci * vport_free(). 12162306a36Sopenharmony_ci */ 12262306a36Sopenharmony_cistruct vport *ovs_vport_alloc(int priv_size, const struct vport_ops *ops, 12362306a36Sopenharmony_ci const struct vport_parms *parms) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci struct vport *vport; 12662306a36Sopenharmony_ci size_t alloc_size; 12762306a36Sopenharmony_ci int err; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci alloc_size = sizeof(struct vport); 13062306a36Sopenharmony_ci if (priv_size) { 13162306a36Sopenharmony_ci alloc_size = ALIGN(alloc_size, VPORT_ALIGN); 13262306a36Sopenharmony_ci alloc_size += priv_size; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci vport = kzalloc(alloc_size, GFP_KERNEL); 13662306a36Sopenharmony_ci if (!vport) 13762306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci vport->upcall_stats = netdev_alloc_pcpu_stats(struct vport_upcall_stats_percpu); 14062306a36Sopenharmony_ci if (!vport->upcall_stats) { 14162306a36Sopenharmony_ci err = -ENOMEM; 14262306a36Sopenharmony_ci goto err_kfree_vport; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci vport->dp = parms->dp; 14662306a36Sopenharmony_ci vport->port_no = parms->port_no; 14762306a36Sopenharmony_ci vport->ops = ops; 14862306a36Sopenharmony_ci INIT_HLIST_NODE(&vport->dp_hash_node); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci if (ovs_vport_set_upcall_portids(vport, parms->upcall_portids)) { 15162306a36Sopenharmony_ci err = -EINVAL; 15262306a36Sopenharmony_ci goto err_free_percpu; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci return vport; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cierr_free_percpu: 15862306a36Sopenharmony_ci free_percpu(vport->upcall_stats); 15962306a36Sopenharmony_cierr_kfree_vport: 16062306a36Sopenharmony_ci kfree(vport); 16162306a36Sopenharmony_ci return ERR_PTR(err); 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ovs_vport_alloc); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci/** 16662306a36Sopenharmony_ci * ovs_vport_free - uninitialize and free vport 16762306a36Sopenharmony_ci * 16862306a36Sopenharmony_ci * @vport: vport to free 16962306a36Sopenharmony_ci * 17062306a36Sopenharmony_ci * Frees a vport allocated with vport_alloc() when it is no longer needed. 17162306a36Sopenharmony_ci * 17262306a36Sopenharmony_ci * The caller must ensure that an RCU grace period has passed since the last 17362306a36Sopenharmony_ci * time @vport was in a datapath. 17462306a36Sopenharmony_ci */ 17562306a36Sopenharmony_civoid ovs_vport_free(struct vport *vport) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci /* vport is freed from RCU callback or error path, Therefore 17862306a36Sopenharmony_ci * it is safe to use raw dereference. 17962306a36Sopenharmony_ci */ 18062306a36Sopenharmony_ci kfree(rcu_dereference_raw(vport->upcall_portids)); 18162306a36Sopenharmony_ci free_percpu(vport->upcall_stats); 18262306a36Sopenharmony_ci kfree(vport); 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ovs_vport_free); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cistatic struct vport_ops *ovs_vport_lookup(const struct vport_parms *parms) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci struct vport_ops *ops; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci list_for_each_entry(ops, &vport_ops_list, list) 19162306a36Sopenharmony_ci if (ops->type == parms->type) 19262306a36Sopenharmony_ci return ops; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci return NULL; 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci/** 19862306a36Sopenharmony_ci * ovs_vport_add - add vport device (for kernel callers) 19962306a36Sopenharmony_ci * 20062306a36Sopenharmony_ci * @parms: Information about new vport. 20162306a36Sopenharmony_ci * 20262306a36Sopenharmony_ci * Creates a new vport with the specified configuration (which is dependent on 20362306a36Sopenharmony_ci * device type). ovs_mutex must be held. 20462306a36Sopenharmony_ci */ 20562306a36Sopenharmony_cistruct vport *ovs_vport_add(const struct vport_parms *parms) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci struct vport_ops *ops; 20862306a36Sopenharmony_ci struct vport *vport; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci ops = ovs_vport_lookup(parms); 21162306a36Sopenharmony_ci if (ops) { 21262306a36Sopenharmony_ci struct hlist_head *bucket; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (!try_module_get(ops->owner)) 21562306a36Sopenharmony_ci return ERR_PTR(-EAFNOSUPPORT); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci vport = ops->create(parms); 21862306a36Sopenharmony_ci if (IS_ERR(vport)) { 21962306a36Sopenharmony_ci module_put(ops->owner); 22062306a36Sopenharmony_ci return vport; 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci bucket = hash_bucket(ovs_dp_get_net(vport->dp), 22462306a36Sopenharmony_ci ovs_vport_name(vport)); 22562306a36Sopenharmony_ci hlist_add_head_rcu(&vport->hash_node, bucket); 22662306a36Sopenharmony_ci return vport; 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci /* Unlock to attempt module load and return -EAGAIN if load 23062306a36Sopenharmony_ci * was successful as we need to restart the port addition 23162306a36Sopenharmony_ci * workflow. 23262306a36Sopenharmony_ci */ 23362306a36Sopenharmony_ci ovs_unlock(); 23462306a36Sopenharmony_ci request_module("vport-type-%d", parms->type); 23562306a36Sopenharmony_ci ovs_lock(); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci if (!ovs_vport_lookup(parms)) 23862306a36Sopenharmony_ci return ERR_PTR(-EAFNOSUPPORT); 23962306a36Sopenharmony_ci else 24062306a36Sopenharmony_ci return ERR_PTR(-EAGAIN); 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci/** 24462306a36Sopenharmony_ci * ovs_vport_set_options - modify existing vport device (for kernel callers) 24562306a36Sopenharmony_ci * 24662306a36Sopenharmony_ci * @vport: vport to modify. 24762306a36Sopenharmony_ci * @options: New configuration. 24862306a36Sopenharmony_ci * 24962306a36Sopenharmony_ci * Modifies an existing device with the specified configuration (which is 25062306a36Sopenharmony_ci * dependent on device type). ovs_mutex must be held. 25162306a36Sopenharmony_ci */ 25262306a36Sopenharmony_ciint ovs_vport_set_options(struct vport *vport, struct nlattr *options) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci if (!vport->ops->set_options) 25562306a36Sopenharmony_ci return -EOPNOTSUPP; 25662306a36Sopenharmony_ci return vport->ops->set_options(vport, options); 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci/** 26062306a36Sopenharmony_ci * ovs_vport_del - delete existing vport device 26162306a36Sopenharmony_ci * 26262306a36Sopenharmony_ci * @vport: vport to delete. 26362306a36Sopenharmony_ci * 26462306a36Sopenharmony_ci * Detaches @vport from its datapath and destroys it. ovs_mutex must 26562306a36Sopenharmony_ci * be held. 26662306a36Sopenharmony_ci */ 26762306a36Sopenharmony_civoid ovs_vport_del(struct vport *vport) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci hlist_del_rcu(&vport->hash_node); 27062306a36Sopenharmony_ci module_put(vport->ops->owner); 27162306a36Sopenharmony_ci vport->ops->destroy(vport); 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci/** 27562306a36Sopenharmony_ci * ovs_vport_get_stats - retrieve device stats 27662306a36Sopenharmony_ci * 27762306a36Sopenharmony_ci * @vport: vport from which to retrieve the stats 27862306a36Sopenharmony_ci * @stats: location to store stats 27962306a36Sopenharmony_ci * 28062306a36Sopenharmony_ci * Retrieves transmit, receive, and error stats for the given device. 28162306a36Sopenharmony_ci * 28262306a36Sopenharmony_ci * Must be called with ovs_mutex or rcu_read_lock. 28362306a36Sopenharmony_ci */ 28462306a36Sopenharmony_civoid ovs_vport_get_stats(struct vport *vport, struct ovs_vport_stats *stats) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci const struct rtnl_link_stats64 *dev_stats; 28762306a36Sopenharmony_ci struct rtnl_link_stats64 temp; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci dev_stats = dev_get_stats(vport->dev, &temp); 29062306a36Sopenharmony_ci stats->rx_errors = dev_stats->rx_errors; 29162306a36Sopenharmony_ci stats->tx_errors = dev_stats->tx_errors; 29262306a36Sopenharmony_ci stats->tx_dropped = dev_stats->tx_dropped; 29362306a36Sopenharmony_ci stats->rx_dropped = dev_stats->rx_dropped; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci stats->rx_bytes = dev_stats->rx_bytes; 29662306a36Sopenharmony_ci stats->rx_packets = dev_stats->rx_packets; 29762306a36Sopenharmony_ci stats->tx_bytes = dev_stats->tx_bytes; 29862306a36Sopenharmony_ci stats->tx_packets = dev_stats->tx_packets; 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci/** 30262306a36Sopenharmony_ci * ovs_vport_get_upcall_stats - retrieve upcall stats 30362306a36Sopenharmony_ci * 30462306a36Sopenharmony_ci * @vport: vport from which to retrieve the stats. 30562306a36Sopenharmony_ci * @skb: sk_buff where upcall stats should be appended. 30662306a36Sopenharmony_ci * 30762306a36Sopenharmony_ci * Retrieves upcall stats for the given device. 30862306a36Sopenharmony_ci * 30962306a36Sopenharmony_ci * Must be called with ovs_mutex or rcu_read_lock. 31062306a36Sopenharmony_ci */ 31162306a36Sopenharmony_ciint ovs_vport_get_upcall_stats(struct vport *vport, struct sk_buff *skb) 31262306a36Sopenharmony_ci{ 31362306a36Sopenharmony_ci struct nlattr *nla; 31462306a36Sopenharmony_ci int i; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci __u64 tx_success = 0; 31762306a36Sopenharmony_ci __u64 tx_fail = 0; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci for_each_possible_cpu(i) { 32062306a36Sopenharmony_ci const struct vport_upcall_stats_percpu *stats; 32162306a36Sopenharmony_ci unsigned int start; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci stats = per_cpu_ptr(vport->upcall_stats, i); 32462306a36Sopenharmony_ci do { 32562306a36Sopenharmony_ci start = u64_stats_fetch_begin(&stats->syncp); 32662306a36Sopenharmony_ci tx_success += u64_stats_read(&stats->n_success); 32762306a36Sopenharmony_ci tx_fail += u64_stats_read(&stats->n_fail); 32862306a36Sopenharmony_ci } while (u64_stats_fetch_retry(&stats->syncp, start)); 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci nla = nla_nest_start_noflag(skb, OVS_VPORT_ATTR_UPCALL_STATS); 33262306a36Sopenharmony_ci if (!nla) 33362306a36Sopenharmony_ci return -EMSGSIZE; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci if (nla_put_u64_64bit(skb, OVS_VPORT_UPCALL_ATTR_SUCCESS, tx_success, 33662306a36Sopenharmony_ci OVS_VPORT_ATTR_PAD)) { 33762306a36Sopenharmony_ci nla_nest_cancel(skb, nla); 33862306a36Sopenharmony_ci return -EMSGSIZE; 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci if (nla_put_u64_64bit(skb, OVS_VPORT_UPCALL_ATTR_FAIL, tx_fail, 34262306a36Sopenharmony_ci OVS_VPORT_ATTR_PAD)) { 34362306a36Sopenharmony_ci nla_nest_cancel(skb, nla); 34462306a36Sopenharmony_ci return -EMSGSIZE; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci nla_nest_end(skb, nla); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci return 0; 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci/** 35262306a36Sopenharmony_ci * ovs_vport_get_options - retrieve device options 35362306a36Sopenharmony_ci * 35462306a36Sopenharmony_ci * @vport: vport from which to retrieve the options. 35562306a36Sopenharmony_ci * @skb: sk_buff where options should be appended. 35662306a36Sopenharmony_ci * 35762306a36Sopenharmony_ci * Retrieves the configuration of the given device, appending an 35862306a36Sopenharmony_ci * %OVS_VPORT_ATTR_OPTIONS attribute that in turn contains nested 35962306a36Sopenharmony_ci * vport-specific attributes to @skb. 36062306a36Sopenharmony_ci * 36162306a36Sopenharmony_ci * Returns 0 if successful, -EMSGSIZE if @skb has insufficient room, or another 36262306a36Sopenharmony_ci * negative error code if a real error occurred. If an error occurs, @skb is 36362306a36Sopenharmony_ci * left unmodified. 36462306a36Sopenharmony_ci * 36562306a36Sopenharmony_ci * Must be called with ovs_mutex or rcu_read_lock. 36662306a36Sopenharmony_ci */ 36762306a36Sopenharmony_ciint ovs_vport_get_options(const struct vport *vport, struct sk_buff *skb) 36862306a36Sopenharmony_ci{ 36962306a36Sopenharmony_ci struct nlattr *nla; 37062306a36Sopenharmony_ci int err; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci if (!vport->ops->get_options) 37362306a36Sopenharmony_ci return 0; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci nla = nla_nest_start_noflag(skb, OVS_VPORT_ATTR_OPTIONS); 37662306a36Sopenharmony_ci if (!nla) 37762306a36Sopenharmony_ci return -EMSGSIZE; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci err = vport->ops->get_options(vport, skb); 38062306a36Sopenharmony_ci if (err) { 38162306a36Sopenharmony_ci nla_nest_cancel(skb, nla); 38262306a36Sopenharmony_ci return err; 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci nla_nest_end(skb, nla); 38662306a36Sopenharmony_ci return 0; 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci/** 39062306a36Sopenharmony_ci * ovs_vport_set_upcall_portids - set upcall portids of @vport. 39162306a36Sopenharmony_ci * 39262306a36Sopenharmony_ci * @vport: vport to modify. 39362306a36Sopenharmony_ci * @ids: new configuration, an array of port ids. 39462306a36Sopenharmony_ci * 39562306a36Sopenharmony_ci * Sets the vport's upcall_portids to @ids. 39662306a36Sopenharmony_ci * 39762306a36Sopenharmony_ci * Returns 0 if successful, -EINVAL if @ids is zero length or cannot be parsed 39862306a36Sopenharmony_ci * as an array of U32. 39962306a36Sopenharmony_ci * 40062306a36Sopenharmony_ci * Must be called with ovs_mutex. 40162306a36Sopenharmony_ci */ 40262306a36Sopenharmony_ciint ovs_vport_set_upcall_portids(struct vport *vport, const struct nlattr *ids) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci struct vport_portids *old, *vport_portids; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci if (!nla_len(ids) || nla_len(ids) % sizeof(u32)) 40762306a36Sopenharmony_ci return -EINVAL; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci old = ovsl_dereference(vport->upcall_portids); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci vport_portids = kmalloc(sizeof(*vport_portids) + nla_len(ids), 41262306a36Sopenharmony_ci GFP_KERNEL); 41362306a36Sopenharmony_ci if (!vport_portids) 41462306a36Sopenharmony_ci return -ENOMEM; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci vport_portids->n_ids = nla_len(ids) / sizeof(u32); 41762306a36Sopenharmony_ci vport_portids->rn_ids = reciprocal_value(vport_portids->n_ids); 41862306a36Sopenharmony_ci nla_memcpy(vport_portids->ids, ids, nla_len(ids)); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci rcu_assign_pointer(vport->upcall_portids, vport_portids); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci if (old) 42362306a36Sopenharmony_ci kfree_rcu(old, rcu); 42462306a36Sopenharmony_ci return 0; 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci/** 42862306a36Sopenharmony_ci * ovs_vport_get_upcall_portids - get the upcall_portids of @vport. 42962306a36Sopenharmony_ci * 43062306a36Sopenharmony_ci * @vport: vport from which to retrieve the portids. 43162306a36Sopenharmony_ci * @skb: sk_buff where portids should be appended. 43262306a36Sopenharmony_ci * 43362306a36Sopenharmony_ci * Retrieves the configuration of the given vport, appending the 43462306a36Sopenharmony_ci * %OVS_VPORT_ATTR_UPCALL_PID attribute which is the array of upcall 43562306a36Sopenharmony_ci * portids to @skb. 43662306a36Sopenharmony_ci * 43762306a36Sopenharmony_ci * Returns 0 if successful, -EMSGSIZE if @skb has insufficient room. 43862306a36Sopenharmony_ci * If an error occurs, @skb is left unmodified. Must be called with 43962306a36Sopenharmony_ci * ovs_mutex or rcu_read_lock. 44062306a36Sopenharmony_ci */ 44162306a36Sopenharmony_ciint ovs_vport_get_upcall_portids(const struct vport *vport, 44262306a36Sopenharmony_ci struct sk_buff *skb) 44362306a36Sopenharmony_ci{ 44462306a36Sopenharmony_ci struct vport_portids *ids; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci ids = rcu_dereference_ovsl(vport->upcall_portids); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci if (vport->dp->user_features & OVS_DP_F_VPORT_PIDS) 44962306a36Sopenharmony_ci return nla_put(skb, OVS_VPORT_ATTR_UPCALL_PID, 45062306a36Sopenharmony_ci ids->n_ids * sizeof(u32), (void *)ids->ids); 45162306a36Sopenharmony_ci else 45262306a36Sopenharmony_ci return nla_put_u32(skb, OVS_VPORT_ATTR_UPCALL_PID, ids->ids[0]); 45362306a36Sopenharmony_ci} 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci/** 45662306a36Sopenharmony_ci * ovs_vport_find_upcall_portid - find the upcall portid to send upcall. 45762306a36Sopenharmony_ci * 45862306a36Sopenharmony_ci * @vport: vport from which the missed packet is received. 45962306a36Sopenharmony_ci * @skb: skb that the missed packet was received. 46062306a36Sopenharmony_ci * 46162306a36Sopenharmony_ci * Uses the skb_get_hash() to select the upcall portid to send the 46262306a36Sopenharmony_ci * upcall. 46362306a36Sopenharmony_ci * 46462306a36Sopenharmony_ci * Returns the portid of the target socket. Must be called with rcu_read_lock. 46562306a36Sopenharmony_ci */ 46662306a36Sopenharmony_ciu32 ovs_vport_find_upcall_portid(const struct vport *vport, 46762306a36Sopenharmony_ci struct sk_buff *skb) 46862306a36Sopenharmony_ci{ 46962306a36Sopenharmony_ci struct vport_portids *ids; 47062306a36Sopenharmony_ci u32 ids_index; 47162306a36Sopenharmony_ci u32 hash; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci ids = rcu_dereference(vport->upcall_portids); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci /* If there is only one portid, select it in the fast-path. */ 47662306a36Sopenharmony_ci if (ids->n_ids == 1) 47762306a36Sopenharmony_ci return ids->ids[0]; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci hash = skb_get_hash(skb); 48062306a36Sopenharmony_ci ids_index = hash - ids->n_ids * reciprocal_divide(hash, ids->rn_ids); 48162306a36Sopenharmony_ci return ids->ids[ids_index]; 48262306a36Sopenharmony_ci} 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci/** 48562306a36Sopenharmony_ci * ovs_vport_receive - pass up received packet to the datapath for processing 48662306a36Sopenharmony_ci * 48762306a36Sopenharmony_ci * @vport: vport that received the packet 48862306a36Sopenharmony_ci * @skb: skb that was received 48962306a36Sopenharmony_ci * @tun_info: tunnel (if any) that carried packet 49062306a36Sopenharmony_ci * 49162306a36Sopenharmony_ci * Must be called with rcu_read_lock. The packet cannot be shared and 49262306a36Sopenharmony_ci * skb->data should point to the Ethernet header. 49362306a36Sopenharmony_ci */ 49462306a36Sopenharmony_ciint ovs_vport_receive(struct vport *vport, struct sk_buff *skb, 49562306a36Sopenharmony_ci const struct ip_tunnel_info *tun_info) 49662306a36Sopenharmony_ci{ 49762306a36Sopenharmony_ci struct sw_flow_key key; 49862306a36Sopenharmony_ci int error; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci OVS_CB(skb)->input_vport = vport; 50162306a36Sopenharmony_ci OVS_CB(skb)->mru = 0; 50262306a36Sopenharmony_ci OVS_CB(skb)->cutlen = 0; 50362306a36Sopenharmony_ci if (unlikely(dev_net(skb->dev) != ovs_dp_get_net(vport->dp))) { 50462306a36Sopenharmony_ci u32 mark; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci mark = skb->mark; 50762306a36Sopenharmony_ci skb_scrub_packet(skb, true); 50862306a36Sopenharmony_ci skb->mark = mark; 50962306a36Sopenharmony_ci tun_info = NULL; 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci /* Extract flow from 'skb' into 'key'. */ 51362306a36Sopenharmony_ci error = ovs_flow_key_extract(tun_info, skb, &key); 51462306a36Sopenharmony_ci if (unlikely(error)) { 51562306a36Sopenharmony_ci kfree_skb(skb); 51662306a36Sopenharmony_ci return error; 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci ovs_dp_process_packet(skb, &key); 51962306a36Sopenharmony_ci return 0; 52062306a36Sopenharmony_ci} 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_cistatic int packet_length(const struct sk_buff *skb, 52362306a36Sopenharmony_ci struct net_device *dev) 52462306a36Sopenharmony_ci{ 52562306a36Sopenharmony_ci int length = skb->len - dev->hard_header_len; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci if (!skb_vlan_tag_present(skb) && 52862306a36Sopenharmony_ci eth_type_vlan(skb->protocol)) 52962306a36Sopenharmony_ci length -= VLAN_HLEN; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci /* Don't subtract for multiple VLAN tags. Most (all?) drivers allow 53262306a36Sopenharmony_ci * (ETH_LEN + VLAN_HLEN) in addition to the mtu value, but almost none 53362306a36Sopenharmony_ci * account for 802.1ad. e.g. is_skb_forwardable(). 53462306a36Sopenharmony_ci */ 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci return length > 0 ? length : 0; 53762306a36Sopenharmony_ci} 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_civoid ovs_vport_send(struct vport *vport, struct sk_buff *skb, u8 mac_proto) 54062306a36Sopenharmony_ci{ 54162306a36Sopenharmony_ci int mtu = vport->dev->mtu; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci switch (vport->dev->type) { 54462306a36Sopenharmony_ci case ARPHRD_NONE: 54562306a36Sopenharmony_ci if (mac_proto == MAC_PROTO_ETHERNET) { 54662306a36Sopenharmony_ci skb_reset_network_header(skb); 54762306a36Sopenharmony_ci skb_reset_mac_len(skb); 54862306a36Sopenharmony_ci skb->protocol = htons(ETH_P_TEB); 54962306a36Sopenharmony_ci } else if (mac_proto != MAC_PROTO_NONE) { 55062306a36Sopenharmony_ci WARN_ON_ONCE(1); 55162306a36Sopenharmony_ci goto drop; 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci break; 55462306a36Sopenharmony_ci case ARPHRD_ETHER: 55562306a36Sopenharmony_ci if (mac_proto != MAC_PROTO_ETHERNET) 55662306a36Sopenharmony_ci goto drop; 55762306a36Sopenharmony_ci break; 55862306a36Sopenharmony_ci default: 55962306a36Sopenharmony_ci goto drop; 56062306a36Sopenharmony_ci } 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci if (unlikely(packet_length(skb, vport->dev) > mtu && 56362306a36Sopenharmony_ci !skb_is_gso(skb))) { 56462306a36Sopenharmony_ci vport->dev->stats.tx_errors++; 56562306a36Sopenharmony_ci if (vport->dev->flags & IFF_UP) 56662306a36Sopenharmony_ci net_warn_ratelimited("%s: dropped over-mtu packet: " 56762306a36Sopenharmony_ci "%d > %d\n", vport->dev->name, 56862306a36Sopenharmony_ci packet_length(skb, vport->dev), 56962306a36Sopenharmony_ci mtu); 57062306a36Sopenharmony_ci goto drop; 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci skb->dev = vport->dev; 57462306a36Sopenharmony_ci skb_clear_tstamp(skb); 57562306a36Sopenharmony_ci vport->ops->send(skb); 57662306a36Sopenharmony_ci return; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_cidrop: 57962306a36Sopenharmony_ci kfree_skb(skb); 58062306a36Sopenharmony_ci} 581