diff options
Diffstat (limited to 'drivers/scsi/cxgbi/libcxgbi.c')
-rw-r--r-- | drivers/scsi/cxgbi/libcxgbi.c | 241 |
1 files changed, 222 insertions, 19 deletions
diff --git a/drivers/scsi/cxgbi/libcxgbi.c b/drivers/scsi/cxgbi/libcxgbi.c index dc812069046c..d65df6dc106f 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,123 @@ err_out: | |||
556 | return ERR_PTR(err); | 602 | return ERR_PTR(err); |
557 | } | 603 | } |
558 | 604 | ||
605 | #if IS_ENABLED(CONFIG_IPV6) | ||
606 | static struct rt6_info *find_route_ipv6(const struct in6_addr *saddr, | ||
607 | const struct in6_addr *daddr) | ||
608 | { | ||
609 | struct flowi6 fl; | ||
610 | |||
611 | if (saddr) | ||
612 | memcpy(&fl.saddr, saddr, sizeof(struct in6_addr)); | ||
613 | if (daddr) | ||
614 | memcpy(&fl.daddr, daddr, sizeof(struct in6_addr)); | ||
615 | return (struct rt6_info *)ip6_route_output(&init_net, NULL, &fl); | ||
616 | } | ||
617 | |||
618 | static struct cxgbi_sock *cxgbi_check_route6(struct sockaddr *dst_addr) | ||
619 | { | ||
620 | struct sockaddr_in6 *daddr6 = (struct sockaddr_in6 *)dst_addr; | ||
621 | struct dst_entry *dst; | ||
622 | struct net_device *ndev; | ||
623 | struct cxgbi_device *cdev; | ||
624 | struct rt6_info *rt = NULL; | ||
625 | struct neighbour *n; | ||
626 | struct in6_addr pref_saddr; | ||
627 | struct cxgbi_sock *csk = NULL; | ||
628 | unsigned int mtu = 0; | ||
629 | int port = 0xFFFF; | ||
630 | int err = 0; | ||
631 | |||
632 | rt = find_route_ipv6(NULL, &daddr6->sin6_addr); | ||
633 | |||
634 | if (!rt) { | ||
635 | pr_info("no route to ipv6 %pI6 port %u\n", | ||
636 | daddr6->sin6_addr.s6_addr, | ||
637 | be16_to_cpu(daddr6->sin6_port)); | ||
638 | err = -ENETUNREACH; | ||
639 | goto err_out; | ||
640 | } | ||
641 | |||
642 | dst = &rt->dst; | ||
643 | |||
644 | n = dst_neigh_lookup(dst, &daddr6->sin6_addr); | ||
645 | |||
646 | if (!n) { | ||
647 | pr_info("%pI6, port %u, dst no neighbour.\n", | ||
648 | daddr6->sin6_addr.s6_addr, | ||
649 | be16_to_cpu(daddr6->sin6_port)); | ||
650 | err = -ENETUNREACH; | ||
651 | goto rel_rt; | ||
652 | } | ||
653 | ndev = n->dev; | ||
654 | |||
655 | if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) { | ||
656 | pr_info("multi-cast route %pI6 port %u, dev %s.\n", | ||
657 | daddr6->sin6_addr.s6_addr, | ||
658 | ntohs(daddr6->sin6_port), ndev->name); | ||
659 | err = -ENETUNREACH; | ||
660 | goto rel_rt; | ||
661 | } | ||
662 | |||
663 | cdev = cxgbi_device_find_by_netdev(ndev, &port); | ||
664 | if (!cdev) | ||
665 | cdev = cxgbi_device_find_by_mac(ndev, &port); | ||
666 | if (!cdev) { | ||
667 | pr_info("dst %pI6 %s, NOT cxgbi device.\n", | ||
668 | daddr6->sin6_addr.s6_addr, ndev->name); | ||
669 | err = -ENETUNREACH; | ||
670 | goto rel_rt; | ||
671 | } | ||
672 | log_debug(1 << CXGBI_DBG_SOCK, | ||
673 | "route to %pI6 :%u, ndev p#%d,%s, cdev 0x%p.\n", | ||
674 | daddr6->sin6_addr.s6_addr, ntohs(daddr6->sin6_port), port, | ||
675 | ndev->name, cdev); | ||
676 | |||
677 | csk = cxgbi_sock_create(cdev); | ||
678 | if (!csk) { | ||
679 | err = -ENOMEM; | ||
680 | goto rel_rt; | ||
681 | } | ||
682 | csk->cdev = cdev; | ||
683 | csk->port_id = port; | ||
684 | csk->mtu = mtu; | ||
685 | csk->dst = dst; | ||
686 | |||
687 | if (ipv6_addr_any(&rt->rt6i_prefsrc.addr)) { | ||
688 | struct inet6_dev *idev = ip6_dst_idev((struct dst_entry *)rt); | ||
689 | |||
690 | err = ipv6_dev_get_saddr(&init_net, idev ? idev->dev : NULL, | ||
691 | &daddr6->sin6_addr, 0, &pref_saddr); | ||
692 | if (err) { | ||
693 | pr_info("failed to get source address to reach %pI6\n", | ||
694 | &daddr6->sin6_addr); | ||
695 | goto rel_rt; | ||
696 | } | ||
697 | } else { | ||
698 | pref_saddr = rt->rt6i_prefsrc.addr; | ||
699 | } | ||
700 | |||
701 | csk->csk_family = AF_INET6; | ||
702 | csk->daddr6.sin6_addr = daddr6->sin6_addr; | ||
703 | csk->daddr6.sin6_port = daddr6->sin6_port; | ||
704 | csk->daddr6.sin6_family = daddr6->sin6_family; | ||
705 | csk->saddr6.sin6_addr = pref_saddr; | ||
706 | |||
707 | neigh_release(n); | ||
708 | return csk; | ||
709 | |||
710 | rel_rt: | ||
711 | if (n) | ||
712 | neigh_release(n); | ||
713 | |||
714 | ip6_rt_put(rt); | ||
715 | if (csk) | ||
716 | cxgbi_sock_closed(csk); | ||
717 | err_out: | ||
718 | return ERR_PTR(err); | ||
719 | } | ||
720 | #endif /* IS_ENABLED(CONFIG_IPV6) */ | ||
721 | |||
559 | void cxgbi_sock_established(struct cxgbi_sock *csk, unsigned int snd_isn, | 722 | void cxgbi_sock_established(struct cxgbi_sock *csk, unsigned int snd_isn, |
560 | unsigned int opt) | 723 | unsigned int opt) |
561 | { | 724 | { |
@@ -2194,6 +2357,34 @@ int cxgbi_set_conn_param(struct iscsi_cls_conn *cls_conn, | |||
2194 | } | 2357 | } |
2195 | EXPORT_SYMBOL_GPL(cxgbi_set_conn_param); | 2358 | EXPORT_SYMBOL_GPL(cxgbi_set_conn_param); |
2196 | 2359 | ||
2360 | static inline int csk_print_port(struct cxgbi_sock *csk, char *buf) | ||
2361 | { | ||
2362 | int len; | ||
2363 | |||
2364 | cxgbi_sock_get(csk); | ||
2365 | len = sprintf(buf, "%hu\n", ntohs(csk->daddr.sin_port)); | ||
2366 | cxgbi_sock_put(csk); | ||
2367 | |||
2368 | return len; | ||
2369 | } | ||
2370 | |||
2371 | static inline int csk_print_ip(struct cxgbi_sock *csk, char *buf) | ||
2372 | { | ||
2373 | int len; | ||
2374 | |||
2375 | cxgbi_sock_get(csk); | ||
2376 | if (csk->csk_family == AF_INET) | ||
2377 | len = sprintf(buf, "%pI4", | ||
2378 | &csk->daddr.sin_addr.s_addr); | ||
2379 | else | ||
2380 | len = sprintf(buf, "%pI6", | ||
2381 | &csk->daddr6.sin6_addr); | ||
2382 | |||
2383 | cxgbi_sock_put(csk); | ||
2384 | |||
2385 | return len; | ||
2386 | } | ||
2387 | |||
2197 | int cxgbi_get_ep_param(struct iscsi_endpoint *ep, enum iscsi_param param, | 2388 | int cxgbi_get_ep_param(struct iscsi_endpoint *ep, enum iscsi_param param, |
2198 | char *buf) | 2389 | char *buf) |
2199 | { | 2390 | { |
@@ -2447,7 +2638,19 @@ struct iscsi_endpoint *cxgbi_ep_connect(struct Scsi_Host *shost, | |||
2447 | } | 2638 | } |
2448 | } | 2639 | } |
2449 | 2640 | ||
2450 | csk = cxgbi_check_route(dst_addr); | 2641 | if (dst_addr->sa_family == AF_INET) { |
2642 | csk = cxgbi_check_route(dst_addr); | ||
2643 | #if IS_ENABLED(CONFIG_IPV6) | ||
2644 | } else if (dst_addr->sa_family == AF_INET6) { | ||
2645 | csk = cxgbi_check_route6(dst_addr); | ||
2646 | #endif | ||
2647 | } else { | ||
2648 | pr_info("address family 0x%x NOT supported.\n", | ||
2649 | dst_addr->sa_family); | ||
2650 | err = -EAFNOSUPPORT; | ||
2651 | return (struct iscsi_endpoint *)ERR_PTR(err); | ||
2652 | } | ||
2653 | |||
2451 | if (IS_ERR(csk)) | 2654 | if (IS_ERR(csk)) |
2452 | return (struct iscsi_endpoint *)csk; | 2655 | return (struct iscsi_endpoint *)csk; |
2453 | cxgbi_sock_get(csk); | 2656 | cxgbi_sock_get(csk); |