diff options
author | Anish Bhatt <anish@chelsio.com> | 2014-07-17 03:18:17 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-07-17 19:06:03 -0400 |
commit | fc8d0590d9142d01e4ccea3aa57c894bd6e53662 (patch) | |
tree | 870cc3db68da24e1216954ec4e37cf9f8ad72c85 /drivers/scsi/cxgbi | |
parent | a3e3b2857d35988819bc396c012c53898b8223e6 (diff) |
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>
Diffstat (limited to 'drivers/scsi/cxgbi')
-rw-r--r-- | drivers/scsi/cxgbi/libcxgbi.c | 237 | ||||
-rw-r--r-- | drivers/scsi/cxgbi/libcxgbi.h | 21 |
2 files changed, 237 insertions, 21 deletions
diff --git a/drivers/scsi/cxgbi/libcxgbi.c b/drivers/scsi/cxgbi/libcxgbi.c index b44c1cff3114..d2fe507fc695 100644 --- a/drivers/scsi/cxgbi/libcxgbi.c +++ b/drivers/scsi/cxgbi/libcxgbi.c | |||
@@ -24,6 +24,10 @@ | |||
24 | #include <linux/inet.h> | 24 | #include <linux/inet.h> |
25 | #include <net/dst.h> | 25 | #include <net/dst.h> |
26 | #include <net/route.h> | 26 | #include <net/route.h> |
27 | #include <net/ipv6.h> | ||
28 | #include <net/ip6_route.h> | ||
29 | #include <net/addrconf.h> | ||
30 | |||
27 | #include <linux/inetdevice.h> /* ip_dev_find */ | 31 | #include <linux/inetdevice.h> /* ip_dev_find */ |
28 | #include <linux/module.h> | 32 | #include <linux/module.h> |
29 | #include <net/tcp.h> | 33 | #include <net/tcp.h> |
@@ -193,8 +197,8 @@ struct cxgbi_device *cxgbi_device_find_by_lldev(void *lldev) | |||
193 | } | 197 | } |
194 | EXPORT_SYMBOL_GPL(cxgbi_device_find_by_lldev); | 198 | EXPORT_SYMBOL_GPL(cxgbi_device_find_by_lldev); |
195 | 199 | ||
196 | static struct cxgbi_device *cxgbi_device_find_by_netdev(struct net_device *ndev, | 200 | struct cxgbi_device *cxgbi_device_find_by_netdev(struct net_device *ndev, |
197 | int *port) | 201 | int *port) |
198 | { | 202 | { |
199 | struct net_device *vdev = NULL; | 203 | struct net_device *vdev = NULL; |
200 | struct cxgbi_device *cdev, *tmp; | 204 | struct cxgbi_device *cdev, *tmp; |
@@ -224,6 +228,40 @@ static struct cxgbi_device *cxgbi_device_find_by_netdev(struct net_device *ndev, | |||
224 | "ndev 0x%p, %s, NO match found.\n", ndev, ndev->name); | 228 | "ndev 0x%p, %s, NO match found.\n", ndev, ndev->name); |
225 | return NULL; | 229 | return NULL; |
226 | } | 230 | } |
231 | EXPORT_SYMBOL_GPL(cxgbi_device_find_by_netdev); | ||
232 | |||
233 | static struct cxgbi_device *cxgbi_device_find_by_mac(struct net_device *ndev, | ||
234 | int *port) | ||
235 | { | ||
236 | struct net_device *vdev = NULL; | ||
237 | struct cxgbi_device *cdev, *tmp; | ||
238 | int i; | ||
239 | |||
240 | if (ndev->priv_flags & IFF_802_1Q_VLAN) { | ||
241 | vdev = ndev; | ||
242 | ndev = vlan_dev_real_dev(ndev); | ||
243 | pr_info("vlan dev %s -> %s.\n", vdev->name, ndev->name); | ||
244 | } | ||
245 | |||
246 | mutex_lock(&cdev_mutex); | ||
247 | list_for_each_entry_safe(cdev, tmp, &cdev_list, list_head) { | ||
248 | for (i = 0; i < cdev->nports; i++) { | ||
249 | if (!memcmp(ndev->dev_addr, cdev->ports[i]->dev_addr, | ||
250 | MAX_ADDR_LEN)) { | ||
251 | cdev->hbas[i]->vdev = vdev; | ||
252 | mutex_unlock(&cdev_mutex); | ||
253 | if (port) | ||
254 | *port = i; | ||
255 | return cdev; | ||
256 | } | ||
257 | } | ||
258 | } | ||
259 | mutex_unlock(&cdev_mutex); | ||
260 | log_debug(1 << CXGBI_DBG_DEV, | ||
261 | "ndev 0x%p, %s, NO match mac found.\n", | ||
262 | ndev, ndev->name); | ||
263 | return NULL; | ||
264 | } | ||
227 | 265 | ||
228 | void cxgbi_hbas_remove(struct cxgbi_device *cdev) | 266 | void cxgbi_hbas_remove(struct cxgbi_device *cdev) |
229 | { | 267 | { |
@@ -320,6 +358,7 @@ static int sock_get_port(struct cxgbi_sock *csk) | |||
320 | struct cxgbi_ports_map *pmap = &cdev->pmap; | 358 | struct cxgbi_ports_map *pmap = &cdev->pmap; |
321 | unsigned int start; | 359 | unsigned int start; |
322 | int idx; | 360 | int idx; |
361 | __be16 *port; | ||
323 | 362 | ||
324 | if (!pmap->max_connect) { | 363 | if (!pmap->max_connect) { |
325 | pr_err("cdev 0x%p, p#%u %s, NO port map.\n", | 364 | 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) | |||
327 | return -EADDRNOTAVAIL; | 366 | return -EADDRNOTAVAIL; |
328 | } | 367 | } |
329 | 368 | ||
330 | if (csk->saddr.sin_port) { | 369 | if (csk->csk_family == AF_INET) |
370 | port = &csk->saddr.sin_port; | ||
371 | else /* ipv6 */ | ||
372 | port = &csk->saddr6.sin6_port; | ||
373 | |||
374 | if (*port) { | ||
331 | pr_err("source port NON-ZERO %u.\n", | 375 | pr_err("source port NON-ZERO %u.\n", |
332 | ntohs(csk->saddr.sin_port)); | 376 | ntohs(*port)); |
333 | return -EADDRINUSE; | 377 | return -EADDRINUSE; |
334 | } | 378 | } |
335 | 379 | ||
@@ -347,8 +391,7 @@ static int sock_get_port(struct cxgbi_sock *csk) | |||
347 | idx = 0; | 391 | idx = 0; |
348 | if (!pmap->port_csk[idx]) { | 392 | if (!pmap->port_csk[idx]) { |
349 | pmap->used++; | 393 | pmap->used++; |
350 | csk->saddr.sin_port = | 394 | *port = htons(pmap->sport_base + idx); |
351 | htons(pmap->sport_base + idx); | ||
352 | pmap->next = idx; | 395 | pmap->next = idx; |
353 | pmap->port_csk[idx] = csk; | 396 | pmap->port_csk[idx] = csk; |
354 | spin_unlock_bh(&pmap->lock); | 397 | spin_unlock_bh(&pmap->lock); |
@@ -374,16 +417,22 @@ static void sock_put_port(struct cxgbi_sock *csk) | |||
374 | { | 417 | { |
375 | struct cxgbi_device *cdev = csk->cdev; | 418 | struct cxgbi_device *cdev = csk->cdev; |
376 | struct cxgbi_ports_map *pmap = &cdev->pmap; | 419 | struct cxgbi_ports_map *pmap = &cdev->pmap; |
420 | __be16 *port; | ||
377 | 421 | ||
378 | if (csk->saddr.sin_port) { | 422 | if (csk->csk_family == AF_INET) |
379 | int idx = ntohs(csk->saddr.sin_port) - pmap->sport_base; | 423 | port = &csk->saddr.sin_port; |
424 | else /* ipv6 */ | ||
425 | port = &csk->saddr6.sin6_port; | ||
380 | 426 | ||
381 | csk->saddr.sin_port = 0; | 427 | if (*port) { |
428 | int idx = ntohs(*port) - pmap->sport_base; | ||
429 | |||
430 | *port = 0; | ||
382 | if (idx < 0 || idx >= pmap->max_connect) { | 431 | if (idx < 0 || idx >= pmap->max_connect) { |
383 | pr_err("cdev 0x%p, p#%u %s, port %u OOR.\n", | 432 | pr_err("cdev 0x%p, p#%u %s, port %u OOR.\n", |
384 | cdev, csk->port_id, | 433 | cdev, csk->port_id, |
385 | cdev->ports[csk->port_id]->name, | 434 | cdev->ports[csk->port_id]->name, |
386 | ntohs(csk->saddr.sin_port)); | 435 | ntohs(*port)); |
387 | return; | 436 | return; |
388 | } | 437 | } |
389 | 438 | ||
@@ -479,17 +528,11 @@ static struct cxgbi_sock *cxgbi_check_route(struct sockaddr *dst_addr) | |||
479 | int port = 0xFFFF; | 528 | int port = 0xFFFF; |
480 | int err = 0; | 529 | int err = 0; |
481 | 530 | ||
482 | if (daddr->sin_family != AF_INET) { | ||
483 | pr_info("address family 0x%x NOT supported.\n", | ||
484 | daddr->sin_family); | ||
485 | err = -EAFNOSUPPORT; | ||
486 | goto err_out; | ||
487 | } | ||
488 | |||
489 | rt = find_route_ipv4(&fl4, 0, daddr->sin_addr.s_addr, 0, daddr->sin_port, 0); | 531 | rt = find_route_ipv4(&fl4, 0, daddr->sin_addr.s_addr, 0, daddr->sin_port, 0); |
490 | if (!rt) { | 532 | if (!rt) { |
491 | pr_info("no route to ipv4 0x%x, port %u.\n", | 533 | pr_info("no route to ipv4 0x%x, port %u.\n", |
492 | daddr->sin_addr.s_addr, daddr->sin_port); | 534 | be32_to_cpu(daddr->sin_addr.s_addr), |
535 | be16_to_cpu(daddr->sin_port)); | ||
493 | err = -ENETUNREACH; | 536 | err = -ENETUNREACH; |
494 | goto err_out; | 537 | goto err_out; |
495 | } | 538 | } |
@@ -537,9 +580,12 @@ static struct cxgbi_sock *cxgbi_check_route(struct sockaddr *dst_addr) | |||
537 | csk->port_id = port; | 580 | csk->port_id = port; |
538 | csk->mtu = mtu; | 581 | csk->mtu = mtu; |
539 | csk->dst = dst; | 582 | csk->dst = dst; |
583 | |||
584 | csk->csk_family = AF_INET; | ||
540 | csk->daddr.sin_addr.s_addr = daddr->sin_addr.s_addr; | 585 | csk->daddr.sin_addr.s_addr = daddr->sin_addr.s_addr; |
541 | csk->daddr.sin_port = daddr->sin_port; | 586 | csk->daddr.sin_port = daddr->sin_port; |
542 | csk->daddr.sin_family = daddr->sin_family; | 587 | csk->daddr.sin_family = daddr->sin_family; |
588 | csk->saddr.sin_family = daddr->sin_family; | ||
543 | csk->saddr.sin_addr.s_addr = fl4.saddr; | 589 | csk->saddr.sin_addr.s_addr = fl4.saddr; |
544 | neigh_release(n); | 590 | neigh_release(n); |
545 | 591 | ||
@@ -556,6 +602,121 @@ err_out: | |||
556 | return ERR_PTR(err); | 602 | return ERR_PTR(err); |
557 | } | 603 | } |
558 | 604 | ||
605 | static struct rt6_info *find_route_ipv6(const struct in6_addr *saddr, | ||
606 | const struct in6_addr *daddr) | ||
607 | { | ||
608 | struct flowi6 fl; | ||
609 | |||
610 | if (saddr) | ||
611 | memcpy(&fl.saddr, saddr, sizeof(struct in6_addr)); | ||
612 | if (daddr) | ||
613 | memcpy(&fl.daddr, daddr, sizeof(struct in6_addr)); | ||
614 | return (struct rt6_info *)ip6_route_output(&init_net, NULL, &fl); | ||
615 | } | ||
616 | |||
617 | static struct cxgbi_sock *cxgbi_check_route6(struct sockaddr *dst_addr) | ||
618 | { | ||
619 | struct sockaddr_in6 *daddr6 = (struct sockaddr_in6 *)dst_addr; | ||
620 | struct dst_entry *dst; | ||
621 | struct net_device *ndev; | ||
622 | struct cxgbi_device *cdev; | ||
623 | struct rt6_info *rt = NULL; | ||
624 | struct neighbour *n; | ||
625 | struct in6_addr pref_saddr; | ||
626 | struct cxgbi_sock *csk = NULL; | ||
627 | unsigned int mtu = 0; | ||
628 | int port = 0xFFFF; | ||
629 | int err = 0; | ||
630 | |||
631 | rt = find_route_ipv6(NULL, &daddr6->sin6_addr); | ||
632 | |||
633 | if (!rt) { | ||
634 | pr_info("no route to ipv6 %pI6 port %u\n", | ||
635 | daddr6->sin6_addr.s6_addr, | ||
636 | be16_to_cpu(daddr6->sin6_port)); | ||
637 | err = -ENETUNREACH; | ||
638 | goto err_out; | ||
639 | } | ||
640 | |||
641 | dst = &rt->dst; | ||
642 | |||
643 | n = dst_neigh_lookup(dst, &daddr6->sin6_addr); | ||
644 | |||
645 | if (!n) { | ||
646 | pr_info("%pI6, port %u, dst no neighbour.\n", | ||
647 | daddr6->sin6_addr.s6_addr, | ||
648 | be16_to_cpu(daddr6->sin6_port)); | ||
649 | err = -ENETUNREACH; | ||
650 | goto rel_rt; | ||
651 | } | ||
652 | ndev = n->dev; | ||
653 | |||
654 | if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) { | ||
655 | pr_info("multi-cast route %pI6 port %u, dev %s.\n", | ||
656 | daddr6->sin6_addr.s6_addr, | ||
657 | ntohs(daddr6->sin6_port), ndev->name); | ||
658 | err = -ENETUNREACH; | ||
659 | goto rel_rt; | ||
660 | } | ||
661 | |||
662 | cdev = cxgbi_device_find_by_netdev(ndev, &port); | ||
663 | if (!cdev) | ||
664 | cdev = cxgbi_device_find_by_mac(ndev, &port); | ||
665 | if (!cdev) { | ||
666 | pr_info("dst %pI6 %s, NOT cxgbi device.\n", | ||
667 | daddr6->sin6_addr.s6_addr, ndev->name); | ||
668 | err = -ENETUNREACH; | ||
669 | goto rel_rt; | ||
670 | } | ||
671 | log_debug(1 << CXGBI_DBG_SOCK, | ||
672 | "route to %pI6 :%u, ndev p#%d,%s, cdev 0x%p.\n", | ||
673 | daddr6->sin6_addr.s6_addr, ntohs(daddr6->sin6_port), port, | ||
674 | ndev->name, cdev); | ||
675 | |||
676 | csk = cxgbi_sock_create(cdev); | ||
677 | if (!csk) { | ||
678 | err = -ENOMEM; | ||
679 | goto rel_rt; | ||
680 | } | ||
681 | csk->cdev = cdev; | ||
682 | csk->port_id = port; | ||
683 | csk->mtu = mtu; | ||
684 | csk->dst = dst; | ||
685 | |||
686 | if (ipv6_addr_any(&rt->rt6i_prefsrc.addr)) { | ||
687 | struct inet6_dev *idev = ip6_dst_idev((struct dst_entry *)rt); | ||
688 | |||
689 | err = ipv6_dev_get_saddr(&init_net, idev ? idev->dev : NULL, | ||
690 | &daddr6->sin6_addr, 0, &pref_saddr); | ||
691 | if (err) { | ||
692 | pr_info("failed to get source address to reach %pI6\n", | ||
693 | &daddr6->sin6_addr); | ||
694 | goto rel_rt; | ||
695 | } | ||
696 | } else { | ||
697 | pref_saddr = rt->rt6i_prefsrc.addr; | ||
698 | } | ||
699 | |||
700 | csk->csk_family = AF_INET6; | ||
701 | csk->daddr6.sin6_addr = daddr6->sin6_addr; | ||
702 | csk->daddr6.sin6_port = daddr6->sin6_port; | ||
703 | csk->daddr6.sin6_family = daddr6->sin6_family; | ||
704 | csk->saddr6.sin6_addr = pref_saddr; | ||
705 | |||
706 | neigh_release(n); | ||
707 | return csk; | ||
708 | |||
709 | rel_rt: | ||
710 | if (n) | ||
711 | neigh_release(n); | ||
712 | |||
713 | ip6_rt_put(rt); | ||
714 | if (csk) | ||
715 | cxgbi_sock_closed(csk); | ||
716 | err_out: | ||
717 | return ERR_PTR(err); | ||
718 | } | ||
719 | |||
559 | void cxgbi_sock_established(struct cxgbi_sock *csk, unsigned int snd_isn, | 720 | void cxgbi_sock_established(struct cxgbi_sock *csk, unsigned int snd_isn, |
560 | unsigned int opt) | 721 | unsigned int opt) |
561 | { | 722 | { |
@@ -2194,6 +2355,34 @@ int cxgbi_set_conn_param(struct iscsi_cls_conn *cls_conn, | |||
2194 | } | 2355 | } |
2195 | EXPORT_SYMBOL_GPL(cxgbi_set_conn_param); | 2356 | EXPORT_SYMBOL_GPL(cxgbi_set_conn_param); |
2196 | 2357 | ||
2358 | static inline int csk_print_port(struct cxgbi_sock *csk, char *buf) | ||
2359 | { | ||
2360 | int len; | ||
2361 | |||
2362 | cxgbi_sock_get(csk); | ||
2363 | len = sprintf(buf, "%hu\n", ntohs(csk->daddr.sin_port)); | ||
2364 | cxgbi_sock_put(csk); | ||
2365 | |||
2366 | return len; | ||
2367 | } | ||
2368 | |||
2369 | static inline int csk_print_ip(struct cxgbi_sock *csk, char *buf) | ||
2370 | { | ||
2371 | int len; | ||
2372 | |||
2373 | cxgbi_sock_get(csk); | ||
2374 | if (csk->csk_family == AF_INET) | ||
2375 | len = sprintf(buf, "%pI4", | ||
2376 | &csk->daddr.sin_addr.s_addr); | ||
2377 | else | ||
2378 | len = sprintf(buf, "%pI6", | ||
2379 | &csk->daddr6.sin6_addr); | ||
2380 | |||
2381 | cxgbi_sock_put(csk); | ||
2382 | |||
2383 | return len; | ||
2384 | } | ||
2385 | |||
2197 | int cxgbi_get_ep_param(struct iscsi_endpoint *ep, enum iscsi_param param, | 2386 | int cxgbi_get_ep_param(struct iscsi_endpoint *ep, enum iscsi_param param, |
2198 | char *buf) | 2387 | char *buf) |
2199 | { | 2388 | { |
@@ -2447,7 +2636,17 @@ struct iscsi_endpoint *cxgbi_ep_connect(struct Scsi_Host *shost, | |||
2447 | } | 2636 | } |
2448 | } | 2637 | } |
2449 | 2638 | ||
2450 | csk = cxgbi_check_route(dst_addr); | 2639 | if (dst_addr->sa_family == AF_INET) { |
2640 | csk = cxgbi_check_route(dst_addr); | ||
2641 | } else if (dst_addr->sa_family == AF_INET6) { | ||
2642 | csk = cxgbi_check_route6(dst_addr); | ||
2643 | } else { | ||
2644 | pr_info("address family 0x%x NOT supported.\n", | ||
2645 | dst_addr->sa_family); | ||
2646 | err = -EAFNOSUPPORT; | ||
2647 | return (struct iscsi_endpoint *)ERR_PTR(err); | ||
2648 | } | ||
2649 | |||
2451 | if (IS_ERR(csk)) | 2650 | if (IS_ERR(csk)) |
2452 | return (struct iscsi_endpoint *)csk; | 2651 | return (struct iscsi_endpoint *)csk; |
2453 | cxgbi_sock_get(csk); | 2652 | cxgbi_sock_get(csk); |
diff --git a/drivers/scsi/cxgbi/libcxgbi.h b/drivers/scsi/cxgbi/libcxgbi.h index 8135f04671af..8ad73d913f02 100644 --- a/drivers/scsi/cxgbi/libcxgbi.h +++ b/drivers/scsi/cxgbi/libcxgbi.h | |||
@@ -44,6 +44,15 @@ enum cxgbi_dbg_flag { | |||
44 | pr_info(fmt, ##__VA_ARGS__); \ | 44 | pr_info(fmt, ##__VA_ARGS__); \ |
45 | } while (0) | 45 | } while (0) |
46 | 46 | ||
47 | #define pr_info_ipaddr(fmt_trail, \ | ||
48 | addr1, addr2, args_trail...) \ | ||
49 | do { \ | ||
50 | if (!((1 << CXGBI_DBG_SOCK) & dbg_level)) \ | ||
51 | break; \ | ||
52 | pr_info("%pISpc - %pISpc, " fmt_trail, \ | ||
53 | addr1, addr2, args_trail); \ | ||
54 | } while (0) | ||
55 | |||
47 | /* max. connections per adapter */ | 56 | /* max. connections per adapter */ |
48 | #define CXGBI_MAX_CONN 16384 | 57 | #define CXGBI_MAX_CONN 16384 |
49 | 58 | ||
@@ -202,8 +211,15 @@ struct cxgbi_sock { | |||
202 | spinlock_t lock; | 211 | spinlock_t lock; |
203 | struct kref refcnt; | 212 | struct kref refcnt; |
204 | unsigned int state; | 213 | unsigned int state; |
205 | struct sockaddr_in saddr; | 214 | unsigned int csk_family; |
206 | struct sockaddr_in daddr; | 215 | union { |
216 | struct sockaddr_in saddr; | ||
217 | struct sockaddr_in6 saddr6; | ||
218 | }; | ||
219 | union { | ||
220 | struct sockaddr_in daddr; | ||
221 | struct sockaddr_in6 daddr6; | ||
222 | }; | ||
207 | struct dst_entry *dst; | 223 | struct dst_entry *dst; |
208 | struct sk_buff_head receive_queue; | 224 | struct sk_buff_head receive_queue; |
209 | struct sk_buff_head write_queue; | 225 | struct sk_buff_head write_queue; |
@@ -692,6 +708,7 @@ struct cxgbi_device *cxgbi_device_register(unsigned int, unsigned int); | |||
692 | void cxgbi_device_unregister(struct cxgbi_device *); | 708 | void cxgbi_device_unregister(struct cxgbi_device *); |
693 | void cxgbi_device_unregister_all(unsigned int flag); | 709 | void cxgbi_device_unregister_all(unsigned int flag); |
694 | struct cxgbi_device *cxgbi_device_find_by_lldev(void *); | 710 | struct cxgbi_device *cxgbi_device_find_by_lldev(void *); |
711 | struct cxgbi_device *cxgbi_device_find_by_netdev(struct net_device *, int *); | ||
695 | int cxgbi_hbas_add(struct cxgbi_device *, unsigned int, unsigned int, | 712 | int cxgbi_hbas_add(struct cxgbi_device *, unsigned int, unsigned int, |
696 | struct scsi_host_template *, | 713 | struct scsi_host_template *, |
697 | struct scsi_transport_template *); | 714 | struct scsi_transport_template *); |