diff options
Diffstat (limited to 'drivers/infiniband/core/addr.c')
| -rw-r--r-- | drivers/infiniband/core/addr.c | 213 | 
1 files changed, 161 insertions, 52 deletions
diff --git a/drivers/infiniband/core/addr.c b/drivers/infiniband/core/addr.c index 8aba0ba57de..8172d37f9ad 100644 --- a/drivers/infiniband/core/addr.c +++ b/drivers/infiniband/core/addr.c @@ -37,6 +37,7 @@  #include <linux/inetdevice.h>  #include <linux/slab.h>  #include <linux/workqueue.h> +#include <linux/module.h>  #include <net/arp.h>  #include <net/neighbour.h>  #include <net/route.h> @@ -44,6 +45,7 @@  #include <net/addrconf.h>  #include <net/ip6_route.h>  #include <rdma/ib_addr.h> +#include <rdma/ib.h>  MODULE_AUTHOR("Sean Hefty");  MODULE_DESCRIPTION("IB Address Translation"); @@ -69,6 +71,23 @@ static LIST_HEAD(req_list);  static DECLARE_DELAYED_WORK(work, process_req);  static struct workqueue_struct *addr_wq; +int rdma_addr_size(struct sockaddr *addr) +{ +	switch (addr->sa_family) { +	case AF_INET: +		return sizeof(struct sockaddr_in); +	case AF_INET6: +		return sizeof(struct sockaddr_in6); +	case AF_IB: +		return sizeof(struct sockaddr_ib); +	default: +		return 0; +	} +} +EXPORT_SYMBOL(rdma_addr_size); + +static struct rdma_addr_client self; +  void rdma_addr_register_client(struct rdma_addr_client *client)  {  	atomic_set(&client->refcount, 1); @@ -102,7 +121,8 @@ int rdma_copy_addr(struct rdma_dev_addr *dev_addr, struct net_device *dev,  }  EXPORT_SYMBOL(rdma_copy_addr); -int rdma_translate_ip(struct sockaddr *addr, struct rdma_dev_addr *dev_addr) +int rdma_translate_ip(struct sockaddr *addr, struct rdma_dev_addr *dev_addr, +		      u16 *vlan_id)  {  	struct net_device *dev;  	int ret = -EADDRNOTAVAIL; @@ -125,10 +145,12 @@ int rdma_translate_ip(struct sockaddr *addr, struct rdma_dev_addr *dev_addr)  			return ret;  		ret = rdma_copy_addr(dev_addr, dev, NULL); +		if (vlan_id) +			*vlan_id = rdma_vlan_dev_vlan_id(dev);  		dev_put(dev);  		break; -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) +#if IS_ENABLED(CONFIG_IPV6)  	case AF_INET6:  		rcu_read_lock();  		for_each_netdev_rcu(&init_net, dev) { @@ -136,6 +158,8 @@ int rdma_translate_ip(struct sockaddr *addr, struct rdma_dev_addr *dev_addr)  					  &((struct sockaddr_in6 *) addr)->sin6_addr,  					  dev, 1)) {  				ret = rdma_copy_addr(dev_addr, dev, NULL); +				if (vlan_id) +					*vlan_id = rdma_vlan_dev_vlan_id(dev);  				break;  			}  		} @@ -151,13 +175,11 @@ static void set_timeout(unsigned long time)  {  	unsigned long delay; -	cancel_delayed_work(&work); -  	delay = time - jiffies;  	if ((long)delay <= 0)  		delay = 1; -	queue_delayed_work(addr_wq, &work, delay); +	mod_delayed_work(addr_wq, &work, delay);  }  static void queue_req(struct addr_req *req) @@ -177,31 +199,53 @@ static void queue_req(struct addr_req *req)  	mutex_unlock(&lock);  } +static int dst_fetch_ha(struct dst_entry *dst, struct rdma_dev_addr *dev_addr, void *daddr) +{ +	struct neighbour *n; +	int ret; + +	n = dst_neigh_lookup(dst, daddr); + +	rcu_read_lock(); +	if (!n || !(n->nud_state & NUD_VALID)) { +		if (n) +			neigh_event_send(n, NULL); +		ret = -ENODATA; +	} else { +		ret = rdma_copy_addr(dev_addr, dst->dev, n->ha); +	} +	rcu_read_unlock(); + +	if (n) +		neigh_release(n); + +	return ret; +} +  static int addr4_resolve(struct sockaddr_in *src_in,  			 struct sockaddr_in *dst_in,  			 struct rdma_dev_addr *addr)  {  	__be32 src_ip = src_in->sin_addr.s_addr;  	__be32 dst_ip = dst_in->sin_addr.s_addr; -	struct flowi fl;  	struct rtable *rt; -	struct neighbour *neigh; +	struct flowi4 fl4;  	int ret; -	memset(&fl, 0, sizeof fl); -	fl.nl_u.ip4_u.daddr = dst_ip; -	fl.nl_u.ip4_u.saddr = src_ip; -	fl.oif = addr->bound_dev_if; - -	ret = ip_route_output_key(&init_net, &rt, &fl); -	if (ret) +	memset(&fl4, 0, sizeof(fl4)); +	fl4.daddr = dst_ip; +	fl4.saddr = src_ip; +	fl4.flowi4_oif = addr->bound_dev_if; +	rt = ip_route_output_key(&init_net, &fl4); +	if (IS_ERR(rt)) { +		ret = PTR_ERR(rt);  		goto out; - +	}  	src_in->sin_family = AF_INET; -	src_in->sin_addr.s_addr = rt->rt_src; +	src_in->sin_addr.s_addr = fl4.saddr;  	if (rt->dst.dev->flags & IFF_LOOPBACK) { -		ret = rdma_translate_ip((struct sockaddr *) dst_in, addr); +		ret = rdma_translate_ip((struct sockaddr *)dst_in, addr, NULL);  		if (!ret)  			memcpy(addr->dst_dev_addr, addr->src_dev_addr, MAX_ADDR_LEN);  		goto put; @@ -209,59 +253,47 @@ static int addr4_resolve(struct sockaddr_in *src_in,  	/* If the device does ARP internally, return 'done' */  	if (rt->dst.dev->flags & IFF_NOARP) { -		rdma_copy_addr(addr, rt->dst.dev, NULL); -		goto put; -	} - -	neigh = neigh_lookup(&arp_tbl, &rt->rt_gateway, rt->dst.dev); -	if (!neigh || !(neigh->nud_state & NUD_VALID)) { -		neigh_event_send(rt->dst.neighbour, NULL); -		ret = -ENODATA; -		if (neigh) -			goto release; +		ret = rdma_copy_addr(addr, rt->dst.dev, NULL);  		goto put;  	} -	ret = rdma_copy_addr(addr, neigh->dev, neigh->ha); -release: -	neigh_release(neigh); +	ret = dst_fetch_ha(&rt->dst, addr, &fl4.daddr);  put:  	ip_rt_put(rt);  out:  	return ret;  } -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) +#if IS_ENABLED(CONFIG_IPV6)  static int addr6_resolve(struct sockaddr_in6 *src_in,  			 struct sockaddr_in6 *dst_in,  			 struct rdma_dev_addr *addr)  { -	struct flowi fl; -	struct neighbour *neigh; +	struct flowi6 fl6;  	struct dst_entry *dst;  	int ret; -	memset(&fl, 0, sizeof fl); -	ipv6_addr_copy(&fl.fl6_dst, &dst_in->sin6_addr); -	ipv6_addr_copy(&fl.fl6_src, &src_in->sin6_addr); -	fl.oif = addr->bound_dev_if; +	memset(&fl6, 0, sizeof fl6); +	fl6.daddr = dst_in->sin6_addr; +	fl6.saddr = src_in->sin6_addr; +	fl6.flowi6_oif = addr->bound_dev_if; -	dst = ip6_route_output(&init_net, NULL, &fl); +	dst = ip6_route_output(&init_net, NULL, &fl6);  	if ((ret = dst->error))  		goto put; -	if (ipv6_addr_any(&fl.fl6_src)) { +	if (ipv6_addr_any(&fl6.saddr)) {  		ret = ipv6_dev_get_saddr(&init_net, ip6_dst_idev(dst)->dev, -					 &fl.fl6_dst, 0, &fl.fl6_src); +					 &fl6.daddr, 0, &fl6.saddr);  		if (ret)  			goto put;  		src_in->sin6_family = AF_INET6; -		ipv6_addr_copy(&src_in->sin6_addr, &fl.fl6_src); +		src_in->sin6_addr = fl6.saddr;  	}  	if (dst->dev->flags & IFF_LOOPBACK) { -		ret = rdma_translate_ip((struct sockaddr *) dst_in, addr); +		ret = rdma_translate_ip((struct sockaddr *)dst_in, addr, NULL);  		if (!ret)  			memcpy(addr->dst_dev_addr, addr->src_dev_addr, MAX_ADDR_LEN);  		goto put; @@ -273,14 +305,7 @@ static int addr6_resolve(struct sockaddr_in6 *src_in,  		goto put;  	} -	neigh = dst->neighbour; -	if (!neigh || !(neigh->nud_state & NUD_VALID)) { -		neigh_event_send(dst->neighbour, NULL); -		ret = -ENODATA; -		goto put; -	} - -	ret = rdma_copy_addr(addr, dst->dev, neigh->ha); +	ret = dst_fetch_ha(dst, addr, &fl6.daddr);  put:  	dst_release(dst);  	return ret; @@ -367,12 +392,12 @@ int rdma_resolve_ip(struct rdma_addr_client *client,  			goto err;  		} -		memcpy(src_in, src_addr, ip_addr_size(src_addr)); +		memcpy(src_in, src_addr, rdma_addr_size(src_addr));  	} else {  		src_in->sa_family = dst_addr->sa_family;  	} -	memcpy(dst_in, dst_addr, ip_addr_size(dst_addr)); +	memcpy(dst_in, dst_addr, rdma_addr_size(dst_addr));  	req->addr = addr;  	req->callback = callback;  	req->context = context; @@ -419,6 +444,88 @@ void rdma_addr_cancel(struct rdma_dev_addr *addr)  }  EXPORT_SYMBOL(rdma_addr_cancel); +struct resolve_cb_context { +	struct rdma_dev_addr *addr; +	struct completion comp; +}; + +static void resolve_cb(int status, struct sockaddr *src_addr, +	     struct rdma_dev_addr *addr, void *context) +{ +	memcpy(((struct resolve_cb_context *)context)->addr, addr, sizeof(struct +				rdma_dev_addr)); +	complete(&((struct resolve_cb_context *)context)->comp); +} + +int rdma_addr_find_dmac_by_grh(union ib_gid *sgid, union ib_gid *dgid, u8 *dmac, +			       u16 *vlan_id) +{ +	int ret = 0; +	struct rdma_dev_addr dev_addr; +	struct resolve_cb_context ctx; +	struct net_device *dev; + +	union { +		struct sockaddr     _sockaddr; +		struct sockaddr_in  _sockaddr_in; +		struct sockaddr_in6 _sockaddr_in6; +	} sgid_addr, dgid_addr; + + +	ret = rdma_gid2ip(&sgid_addr._sockaddr, sgid); +	if (ret) +		return ret; + +	ret = rdma_gid2ip(&dgid_addr._sockaddr, dgid); +	if (ret) +		return ret; + +	memset(&dev_addr, 0, sizeof(dev_addr)); + +	ctx.addr = &dev_addr; +	init_completion(&ctx.comp); +	ret = rdma_resolve_ip(&self, &sgid_addr._sockaddr, &dgid_addr._sockaddr, +			&dev_addr, 1000, resolve_cb, &ctx); +	if (ret) +		return ret; + +	wait_for_completion(&ctx.comp); + +	memcpy(dmac, dev_addr.dst_dev_addr, ETH_ALEN); +	dev = dev_get_by_index(&init_net, dev_addr.bound_dev_if); +	if (!dev) +		return -ENODEV; +	if (vlan_id) +		*vlan_id = rdma_vlan_dev_vlan_id(dev); +	dev_put(dev); +	return ret; +} +EXPORT_SYMBOL(rdma_addr_find_dmac_by_grh); + +int rdma_addr_find_smac_by_sgid(union ib_gid *sgid, u8 *smac, u16 *vlan_id) +{ +	int ret = 0; +	struct rdma_dev_addr dev_addr; +	union { +		struct sockaddr     _sockaddr; +		struct sockaddr_in  _sockaddr_in; +		struct sockaddr_in6 _sockaddr_in6; +	} gid_addr; + +	ret = rdma_gid2ip(&gid_addr._sockaddr, sgid); + +	if (ret) +		return ret; +	memset(&dev_addr, 0, sizeof(dev_addr)); +	ret = rdma_translate_ip(&gid_addr._sockaddr, &dev_addr, vlan_id); +	if (ret) +		return ret; + +	memcpy(smac, dev_addr.src_dev_addr, ETH_ALEN); +	return ret; +} +EXPORT_SYMBOL(rdma_addr_find_smac_by_sgid); +  static int netevent_callback(struct notifier_block *self, unsigned long event,  	void *ctx)  { @@ -443,11 +550,13 @@ static int __init addr_init(void)  		return -ENOMEM;  	register_netevent_notifier(&nb); +	rdma_addr_register_client(&self);  	return 0;  }  static void __exit addr_cleanup(void)  { +	rdma_addr_unregister_client(&self);  	unregister_netevent_notifier(&nb);  	destroy_workqueue(addr_wq);  }  | 
