mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	libcxgbi: Add ipv6 api to driver
Signed-off-by: Anish Bhatt <anish@chelsio.com> Signed-off-by: Karen Xie <kxie@chelsio.com> Signed-off-by: Manoj Malviya <manojmalviya@chelsio.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									a3e3b2857d
								
							
						
					
					
						commit
						fc8d0590d9
					
				
					 2 changed files with 237 additions and 21 deletions
				
			
		| 
						 | 
				
			
			@ -24,6 +24,10 @@
 | 
			
		|||
#include <linux/inet.h>
 | 
			
		||||
#include <net/dst.h>
 | 
			
		||||
#include <net/route.h>
 | 
			
		||||
#include <net/ipv6.h>
 | 
			
		||||
#include <net/ip6_route.h>
 | 
			
		||||
#include <net/addrconf.h>
 | 
			
		||||
 | 
			
		||||
#include <linux/inetdevice.h>	/* ip_dev_find */
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
#include <net/tcp.h>
 | 
			
		||||
| 
						 | 
				
			
			@ -193,8 +197,8 @@ struct cxgbi_device *cxgbi_device_find_by_lldev(void *lldev)
 | 
			
		|||
}
 | 
			
		||||
EXPORT_SYMBOL_GPL(cxgbi_device_find_by_lldev);
 | 
			
		||||
 | 
			
		||||
static struct cxgbi_device *cxgbi_device_find_by_netdev(struct net_device *ndev,
 | 
			
		||||
							int *port)
 | 
			
		||||
struct cxgbi_device *cxgbi_device_find_by_netdev(struct net_device *ndev,
 | 
			
		||||
						 int *port)
 | 
			
		||||
{
 | 
			
		||||
	struct net_device *vdev = NULL;
 | 
			
		||||
	struct cxgbi_device *cdev, *tmp;
 | 
			
		||||
| 
						 | 
				
			
			@ -224,6 +228,40 @@ static struct cxgbi_device *cxgbi_device_find_by_netdev(struct net_device *ndev,
 | 
			
		|||
		"ndev 0x%p, %s, NO match found.\n", ndev, ndev->name);
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL_GPL(cxgbi_device_find_by_netdev);
 | 
			
		||||
 | 
			
		||||
static struct cxgbi_device *cxgbi_device_find_by_mac(struct net_device *ndev,
 | 
			
		||||
						     int *port)
 | 
			
		||||
{
 | 
			
		||||
	struct net_device *vdev = NULL;
 | 
			
		||||
	struct cxgbi_device *cdev, *tmp;
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	if (ndev->priv_flags & IFF_802_1Q_VLAN) {
 | 
			
		||||
		vdev = ndev;
 | 
			
		||||
		ndev = vlan_dev_real_dev(ndev);
 | 
			
		||||
		pr_info("vlan dev %s -> %s.\n", vdev->name, ndev->name);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	mutex_lock(&cdev_mutex);
 | 
			
		||||
	list_for_each_entry_safe(cdev, tmp, &cdev_list, list_head) {
 | 
			
		||||
		for (i = 0; i < cdev->nports; i++) {
 | 
			
		||||
			if (!memcmp(ndev->dev_addr, cdev->ports[i]->dev_addr,
 | 
			
		||||
				    MAX_ADDR_LEN)) {
 | 
			
		||||
				cdev->hbas[i]->vdev = vdev;
 | 
			
		||||
				mutex_unlock(&cdev_mutex);
 | 
			
		||||
				if (port)
 | 
			
		||||
					*port = i;
 | 
			
		||||
				return cdev;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	mutex_unlock(&cdev_mutex);
 | 
			
		||||
	log_debug(1 << CXGBI_DBG_DEV,
 | 
			
		||||
		  "ndev 0x%p, %s, NO match mac found.\n",
 | 
			
		||||
		  ndev, ndev->name);
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cxgbi_hbas_remove(struct cxgbi_device *cdev)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -320,6 +358,7 @@ static int sock_get_port(struct cxgbi_sock *csk)
 | 
			
		|||
	struct cxgbi_ports_map *pmap = &cdev->pmap;
 | 
			
		||||
	unsigned int start;
 | 
			
		||||
	int idx;
 | 
			
		||||
	__be16 *port;
 | 
			
		||||
 | 
			
		||||
	if (!pmap->max_connect) {
 | 
			
		||||
		pr_err("cdev 0x%p, p#%u %s, NO port map.\n",
 | 
			
		||||
| 
						 | 
				
			
			@ -327,9 +366,14 @@ static int sock_get_port(struct cxgbi_sock *csk)
 | 
			
		|||
		return -EADDRNOTAVAIL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (csk->saddr.sin_port) {
 | 
			
		||||
	if (csk->csk_family == AF_INET)
 | 
			
		||||
		port = &csk->saddr.sin_port;
 | 
			
		||||
	else /* ipv6 */
 | 
			
		||||
		port = &csk->saddr6.sin6_port;
 | 
			
		||||
 | 
			
		||||
	if (*port) {
 | 
			
		||||
		pr_err("source port NON-ZERO %u.\n",
 | 
			
		||||
			ntohs(csk->saddr.sin_port));
 | 
			
		||||
			ntohs(*port));
 | 
			
		||||
		return -EADDRINUSE;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -347,8 +391,7 @@ static int sock_get_port(struct cxgbi_sock *csk)
 | 
			
		|||
			idx = 0;
 | 
			
		||||
		if (!pmap->port_csk[idx]) {
 | 
			
		||||
			pmap->used++;
 | 
			
		||||
			csk->saddr.sin_port =
 | 
			
		||||
				htons(pmap->sport_base + idx);
 | 
			
		||||
			*port = htons(pmap->sport_base + idx);
 | 
			
		||||
			pmap->next = idx;
 | 
			
		||||
			pmap->port_csk[idx] = csk;
 | 
			
		||||
			spin_unlock_bh(&pmap->lock);
 | 
			
		||||
| 
						 | 
				
			
			@ -374,16 +417,22 @@ static void sock_put_port(struct cxgbi_sock *csk)
 | 
			
		|||
{
 | 
			
		||||
	struct cxgbi_device *cdev = csk->cdev;
 | 
			
		||||
	struct cxgbi_ports_map *pmap = &cdev->pmap;
 | 
			
		||||
	__be16 *port;
 | 
			
		||||
 | 
			
		||||
	if (csk->saddr.sin_port) {
 | 
			
		||||
		int idx = ntohs(csk->saddr.sin_port) - pmap->sport_base;
 | 
			
		||||
	if (csk->csk_family == AF_INET)
 | 
			
		||||
		port = &csk->saddr.sin_port;
 | 
			
		||||
	else /* ipv6 */
 | 
			
		||||
		port = &csk->saddr6.sin6_port;
 | 
			
		||||
 | 
			
		||||
		csk->saddr.sin_port = 0;
 | 
			
		||||
	if (*port) {
 | 
			
		||||
		int idx = ntohs(*port) - pmap->sport_base;
 | 
			
		||||
 | 
			
		||||
		*port = 0;
 | 
			
		||||
		if (idx < 0 || idx >= pmap->max_connect) {
 | 
			
		||||
			pr_err("cdev 0x%p, p#%u %s, port %u OOR.\n",
 | 
			
		||||
				cdev, csk->port_id,
 | 
			
		||||
				cdev->ports[csk->port_id]->name,
 | 
			
		||||
				ntohs(csk->saddr.sin_port));
 | 
			
		||||
				ntohs(*port));
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -479,17 +528,11 @@ static struct cxgbi_sock *cxgbi_check_route(struct sockaddr *dst_addr)
 | 
			
		|||
	int port = 0xFFFF;
 | 
			
		||||
	int err = 0;
 | 
			
		||||
 | 
			
		||||
	if (daddr->sin_family != AF_INET) {
 | 
			
		||||
		pr_info("address family 0x%x NOT supported.\n",
 | 
			
		||||
			daddr->sin_family);
 | 
			
		||||
		err = -EAFNOSUPPORT;
 | 
			
		||||
		goto err_out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rt = find_route_ipv4(&fl4, 0, daddr->sin_addr.s_addr, 0, daddr->sin_port, 0);
 | 
			
		||||
	if (!rt) {
 | 
			
		||||
		pr_info("no route to ipv4 0x%x, port %u.\n",
 | 
			
		||||
			daddr->sin_addr.s_addr, daddr->sin_port);
 | 
			
		||||
			be32_to_cpu(daddr->sin_addr.s_addr),
 | 
			
		||||
			be16_to_cpu(daddr->sin_port));
 | 
			
		||||
		err = -ENETUNREACH;
 | 
			
		||||
		goto err_out;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -537,9 +580,12 @@ static struct cxgbi_sock *cxgbi_check_route(struct sockaddr *dst_addr)
 | 
			
		|||
	csk->port_id = port;
 | 
			
		||||
	csk->mtu = mtu;
 | 
			
		||||
	csk->dst = dst;
 | 
			
		||||
 | 
			
		||||
	csk->csk_family = AF_INET;
 | 
			
		||||
	csk->daddr.sin_addr.s_addr = daddr->sin_addr.s_addr;
 | 
			
		||||
	csk->daddr.sin_port = daddr->sin_port;
 | 
			
		||||
	csk->daddr.sin_family = daddr->sin_family;
 | 
			
		||||
	csk->saddr.sin_family = daddr->sin_family;
 | 
			
		||||
	csk->saddr.sin_addr.s_addr = fl4.saddr;
 | 
			
		||||
	neigh_release(n);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -556,6 +602,121 @@ static struct cxgbi_sock *cxgbi_check_route(struct sockaddr *dst_addr)
 | 
			
		|||
	return ERR_PTR(err);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct rt6_info *find_route_ipv6(const struct in6_addr *saddr,
 | 
			
		||||
					const struct in6_addr *daddr)
 | 
			
		||||
{
 | 
			
		||||
	struct flowi6 fl;
 | 
			
		||||
 | 
			
		||||
	if (saddr)
 | 
			
		||||
		memcpy(&fl.saddr, saddr, sizeof(struct in6_addr));
 | 
			
		||||
	if (daddr)
 | 
			
		||||
		memcpy(&fl.daddr, daddr, sizeof(struct in6_addr));
 | 
			
		||||
	return (struct rt6_info *)ip6_route_output(&init_net, NULL, &fl);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct cxgbi_sock *cxgbi_check_route6(struct sockaddr *dst_addr)
 | 
			
		||||
{
 | 
			
		||||
	struct sockaddr_in6 *daddr6 = (struct sockaddr_in6 *)dst_addr;
 | 
			
		||||
	struct dst_entry *dst;
 | 
			
		||||
	struct net_device *ndev;
 | 
			
		||||
	struct cxgbi_device *cdev;
 | 
			
		||||
	struct rt6_info *rt = NULL;
 | 
			
		||||
	struct neighbour *n;
 | 
			
		||||
	struct in6_addr pref_saddr;
 | 
			
		||||
	struct cxgbi_sock *csk = NULL;
 | 
			
		||||
	unsigned int mtu = 0;
 | 
			
		||||
	int port = 0xFFFF;
 | 
			
		||||
	int err = 0;
 | 
			
		||||
 | 
			
		||||
	rt = find_route_ipv6(NULL, &daddr6->sin6_addr);
 | 
			
		||||
 | 
			
		||||
	if (!rt) {
 | 
			
		||||
		pr_info("no route to ipv6 %pI6 port %u\n",
 | 
			
		||||
			daddr6->sin6_addr.s6_addr,
 | 
			
		||||
			be16_to_cpu(daddr6->sin6_port));
 | 
			
		||||
		err = -ENETUNREACH;
 | 
			
		||||
		goto err_out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dst = &rt->dst;
 | 
			
		||||
 | 
			
		||||
	n = dst_neigh_lookup(dst, &daddr6->sin6_addr);
 | 
			
		||||
 | 
			
		||||
	if (!n) {
 | 
			
		||||
		pr_info("%pI6, port %u, dst no neighbour.\n",
 | 
			
		||||
			daddr6->sin6_addr.s6_addr,
 | 
			
		||||
			be16_to_cpu(daddr6->sin6_port));
 | 
			
		||||
		err = -ENETUNREACH;
 | 
			
		||||
		goto rel_rt;
 | 
			
		||||
	}
 | 
			
		||||
	ndev = n->dev;
 | 
			
		||||
 | 
			
		||||
	if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) {
 | 
			
		||||
		pr_info("multi-cast route %pI6 port %u, dev %s.\n",
 | 
			
		||||
			daddr6->sin6_addr.s6_addr,
 | 
			
		||||
			ntohs(daddr6->sin6_port), ndev->name);
 | 
			
		||||
		err = -ENETUNREACH;
 | 
			
		||||
		goto rel_rt;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cdev = cxgbi_device_find_by_netdev(ndev, &port);
 | 
			
		||||
	if (!cdev)
 | 
			
		||||
		cdev = cxgbi_device_find_by_mac(ndev, &port);
 | 
			
		||||
	if (!cdev) {
 | 
			
		||||
		pr_info("dst %pI6 %s, NOT cxgbi device.\n",
 | 
			
		||||
			daddr6->sin6_addr.s6_addr, ndev->name);
 | 
			
		||||
		err = -ENETUNREACH;
 | 
			
		||||
		goto rel_rt;
 | 
			
		||||
	}
 | 
			
		||||
	log_debug(1 << CXGBI_DBG_SOCK,
 | 
			
		||||
		  "route to %pI6 :%u, ndev p#%d,%s, cdev 0x%p.\n",
 | 
			
		||||
		  daddr6->sin6_addr.s6_addr, ntohs(daddr6->sin6_port), port,
 | 
			
		||||
		  ndev->name, cdev);
 | 
			
		||||
 | 
			
		||||
	csk = cxgbi_sock_create(cdev);
 | 
			
		||||
	if (!csk) {
 | 
			
		||||
		err = -ENOMEM;
 | 
			
		||||
		goto rel_rt;
 | 
			
		||||
	}
 | 
			
		||||
	csk->cdev = cdev;
 | 
			
		||||
	csk->port_id = port;
 | 
			
		||||
	csk->mtu = mtu;
 | 
			
		||||
	csk->dst = dst;
 | 
			
		||||
 | 
			
		||||
	if (ipv6_addr_any(&rt->rt6i_prefsrc.addr)) {
 | 
			
		||||
		struct inet6_dev *idev = ip6_dst_idev((struct dst_entry *)rt);
 | 
			
		||||
 | 
			
		||||
		err = ipv6_dev_get_saddr(&init_net, idev ? idev->dev : NULL,
 | 
			
		||||
					 &daddr6->sin6_addr, 0, &pref_saddr);
 | 
			
		||||
		if (err) {
 | 
			
		||||
			pr_info("failed to get source address to reach %pI6\n",
 | 
			
		||||
				&daddr6->sin6_addr);
 | 
			
		||||
			goto rel_rt;
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		pref_saddr = rt->rt6i_prefsrc.addr;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	csk->csk_family = AF_INET6;
 | 
			
		||||
	csk->daddr6.sin6_addr = daddr6->sin6_addr;
 | 
			
		||||
	csk->daddr6.sin6_port = daddr6->sin6_port;
 | 
			
		||||
	csk->daddr6.sin6_family = daddr6->sin6_family;
 | 
			
		||||
	csk->saddr6.sin6_addr = pref_saddr;
 | 
			
		||||
 | 
			
		||||
	neigh_release(n);
 | 
			
		||||
	return csk;
 | 
			
		||||
 | 
			
		||||
rel_rt:
 | 
			
		||||
	if (n)
 | 
			
		||||
		neigh_release(n);
 | 
			
		||||
 | 
			
		||||
	ip6_rt_put(rt);
 | 
			
		||||
	if (csk)
 | 
			
		||||
		cxgbi_sock_closed(csk);
 | 
			
		||||
err_out:
 | 
			
		||||
	return ERR_PTR(err);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cxgbi_sock_established(struct cxgbi_sock *csk, unsigned int snd_isn,
 | 
			
		||||
			unsigned int opt)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -2194,6 +2355,34 @@ int cxgbi_set_conn_param(struct iscsi_cls_conn *cls_conn,
 | 
			
		|||
}
 | 
			
		||||
EXPORT_SYMBOL_GPL(cxgbi_set_conn_param);
 | 
			
		||||
 | 
			
		||||
static inline int csk_print_port(struct cxgbi_sock *csk, char *buf)
 | 
			
		||||
{
 | 
			
		||||
	int len;
 | 
			
		||||
 | 
			
		||||
	cxgbi_sock_get(csk);
 | 
			
		||||
	len = sprintf(buf, "%hu\n", ntohs(csk->daddr.sin_port));
 | 
			
		||||
	cxgbi_sock_put(csk);
 | 
			
		||||
 | 
			
		||||
	return len;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int csk_print_ip(struct cxgbi_sock *csk, char *buf)
 | 
			
		||||
{
 | 
			
		||||
	int len;
 | 
			
		||||
 | 
			
		||||
	cxgbi_sock_get(csk);
 | 
			
		||||
	if (csk->csk_family == AF_INET)
 | 
			
		||||
		len = sprintf(buf, "%pI4",
 | 
			
		||||
			      &csk->daddr.sin_addr.s_addr);
 | 
			
		||||
	else
 | 
			
		||||
		len = sprintf(buf, "%pI6",
 | 
			
		||||
			      &csk->daddr6.sin6_addr);
 | 
			
		||||
 | 
			
		||||
	cxgbi_sock_put(csk);
 | 
			
		||||
 | 
			
		||||
	return len;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int cxgbi_get_ep_param(struct iscsi_endpoint *ep, enum iscsi_param param,
 | 
			
		||||
		       char *buf)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -2447,7 +2636,17 @@ struct iscsi_endpoint *cxgbi_ep_connect(struct Scsi_Host *shost,
 | 
			
		|||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	csk = cxgbi_check_route(dst_addr);
 | 
			
		||||
	if (dst_addr->sa_family == AF_INET) {
 | 
			
		||||
		csk = cxgbi_check_route(dst_addr);
 | 
			
		||||
	} else if (dst_addr->sa_family == AF_INET6) {
 | 
			
		||||
		csk = cxgbi_check_route6(dst_addr);
 | 
			
		||||
	} else {
 | 
			
		||||
		pr_info("address family 0x%x NOT supported.\n",
 | 
			
		||||
			dst_addr->sa_family);
 | 
			
		||||
		err = -EAFNOSUPPORT;
 | 
			
		||||
		return (struct iscsi_endpoint *)ERR_PTR(err);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (IS_ERR(csk))
 | 
			
		||||
		return (struct iscsi_endpoint *)csk;
 | 
			
		||||
	cxgbi_sock_get(csk);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -44,6 +44,15 @@ enum cxgbi_dbg_flag {
 | 
			
		|||
			pr_info(fmt, ##__VA_ARGS__); \
 | 
			
		||||
	} while (0)
 | 
			
		||||
 | 
			
		||||
#define pr_info_ipaddr(fmt_trail,					\
 | 
			
		||||
			addr1, addr2, args_trail...)			\
 | 
			
		||||
do {									\
 | 
			
		||||
	if (!((1 << CXGBI_DBG_SOCK) & dbg_level))			\
 | 
			
		||||
		break;							\
 | 
			
		||||
	pr_info("%pISpc - %pISpc, " fmt_trail,				\
 | 
			
		||||
		addr1, addr2, args_trail);				\
 | 
			
		||||
} while (0)
 | 
			
		||||
 | 
			
		||||
/* max. connections per adapter */
 | 
			
		||||
#define CXGBI_MAX_CONN		16384
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -202,8 +211,15 @@ struct cxgbi_sock {
 | 
			
		|||
	spinlock_t lock;
 | 
			
		||||
	struct kref refcnt;
 | 
			
		||||
	unsigned int state;
 | 
			
		||||
	struct sockaddr_in saddr;
 | 
			
		||||
	struct sockaddr_in daddr;
 | 
			
		||||
	unsigned int csk_family;
 | 
			
		||||
	union {
 | 
			
		||||
		struct sockaddr_in saddr;
 | 
			
		||||
		struct sockaddr_in6 saddr6;
 | 
			
		||||
	};
 | 
			
		||||
	union {
 | 
			
		||||
		struct sockaddr_in daddr;
 | 
			
		||||
		struct sockaddr_in6 daddr6;
 | 
			
		||||
	};
 | 
			
		||||
	struct dst_entry *dst;
 | 
			
		||||
	struct sk_buff_head receive_queue;
 | 
			
		||||
	struct sk_buff_head write_queue;
 | 
			
		||||
| 
						 | 
				
			
			@ -692,6 +708,7 @@ struct cxgbi_device *cxgbi_device_register(unsigned int, unsigned int);
 | 
			
		|||
void cxgbi_device_unregister(struct cxgbi_device *);
 | 
			
		||||
void cxgbi_device_unregister_all(unsigned int flag);
 | 
			
		||||
struct cxgbi_device *cxgbi_device_find_by_lldev(void *);
 | 
			
		||||
struct cxgbi_device *cxgbi_device_find_by_netdev(struct net_device *, int *);
 | 
			
		||||
int cxgbi_hbas_add(struct cxgbi_device *, unsigned int, unsigned int,
 | 
			
		||||
			struct scsi_host_template *,
 | 
			
		||||
			struct scsi_transport_template *);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue