Message ID | 20210429190926.5086-8-smalin@marvell.com (mailing list archive) |
---|---|
State | RFC |
Delegated to: | Netdev Maintainers |
Headers | show |
Series | NVMeTCP Offload ULP and QEDN Device Driver | expand |
Context | Check | Description |
---|---|---|
netdev/cover_letter | success | Link |
netdev/fixes_present | success | Link |
netdev/patch_count | fail | Series longer than 15 patches |
netdev/tree_selection | success | Guessed tree name to be net-next |
netdev/subject_prefix | success | Link |
netdev/cc_maintainers | warning | 2 maintainers not CCed: GR-everest-linux-l2@marvell.com davem@davemloft.net |
netdev/source_inline | success | Was 0 now: 0 |
netdev/verify_signedoff | success | Link |
netdev/module_param | success | Was 0 now: 0 |
netdev/build_32bit | success | Errors and warnings before: 0 this patch: 0 |
netdev/kdoc | success | Errors and warnings before: 0 this patch: 0 |
netdev/verify_fixes | success | Link |
netdev/checkpatch | warning | WARNING: added, moved or deleted file(s), does MAINTAINERS need updating? WARNING: line length of 82 exceeds 80 columns WARNING: line length of 89 exceeds 80 columns |
netdev/build_allmodconfig_warn | success | Errors and warnings before: 0 this patch: 0 |
netdev/header_inline | success | Link |
On 4/29/21 9:09 PM, Shai Malin wrote: > From: Nikolay Assa <nassa@marvell.com> > > This patch introduces APIs which the NVMeTCP Offload device (qedn) > will use through the paired net-device (qede). > It includes APIs for: > - ipv4/ipv6 routing > - get VLAN from net-device > - TCP ports reservation > > Acked-by: Igor Russkikh <irusskikh@marvell.com> > Signed-off-by: Nikolay Assa <nassa@marvell.com> > Signed-off-by: Prabhakar Kushwaha <pkushwaha@marvell.com> > Signed-off-by: Omkar Kulkarni <okulkarni@marvell.com> > Signed-off-by: Michal Kalderon <mkalderon@marvell.com> > Signed-off-by: Ariel Elior <aelior@marvell.com> > Signed-off-by: Shai Malin <smalin@marvell.com> > --- > .../qlogic/qed/qed_nvmetcp_ip_services.c | 239 ++++++++++++++++++ > .../linux/qed/qed_nvmetcp_ip_services_if.h | 29 +++ > 2 files changed, 268 insertions(+) > create mode 100644 drivers/net/ethernet/qlogic/qed/qed_nvmetcp_ip_services.c > create mode 100644 include/linux/qed/qed_nvmetcp_ip_services_if.h > > diff --git a/drivers/net/ethernet/qlogic/qed/qed_nvmetcp_ip_services.c b/drivers/net/ethernet/qlogic/qed/qed_nvmetcp_ip_services.c > new file mode 100644 > index 000000000000..2904b1a0830a > --- /dev/null > +++ b/drivers/net/ethernet/qlogic/qed/qed_nvmetcp_ip_services.c > @@ -0,0 +1,239 @@ > +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) > +/* > + * Copyright 2021 Marvell. All rights reserved. > + */ > + > +#include <linux/types.h> > +#include <asm/byteorder.h> > +#include <asm/param.h> > +#include <linux/delay.h> > +#include <linux/pci.h> > +#include <linux/dma-mapping.h> > +#include <linux/etherdevice.h> > +#include <linux/kernel.h> > +#include <linux/stddef.h> > +#include <linux/errno.h> > + > +#include <net/tcp.h> > + > +#include <linux/qed/qed_nvmetcp_ip_services_if.h> > + > +#define QED_IP_RESOL_TIMEOUT 4 > + > +int qed_route_ipv4(struct sockaddr_storage *local_addr, > + struct sockaddr_storage *remote_addr, > + struct sockaddr *hardware_address, > + struct net_device **ndev) > +{ > + struct neighbour *neigh = NULL; > + __be32 *loc_ip, *rem_ip; > + struct rtable *rt; > + int rc = -ENXIO; > + int retry; > + > + loc_ip = &((struct sockaddr_in *)local_addr)->sin_addr.s_addr; > + rem_ip = &((struct sockaddr_in *)remote_addr)->sin_addr.s_addr; > + *ndev = NULL; > + rt = ip_route_output(&init_net, *rem_ip, *loc_ip, 0/*tos*/, 0/*oif*/); > + if (IS_ERR(rt)) { > + pr_err("lookup route failed\n"); > + rc = PTR_ERR(rt); > + goto return_err; > + } > + > + neigh = dst_neigh_lookup(&rt->dst, rem_ip); > + if (!neigh) { > + rc = -ENOMEM; > + ip_rt_put(rt); > + goto return_err; > + } > + > + *ndev = rt->dst.dev; > + ip_rt_put(rt); > + > + /* If not resolved, kick-off state machine towards resolution */ > + if (!(neigh->nud_state & NUD_VALID)) > + neigh_event_send(neigh, NULL); > + > + /* query neighbor until resolved or timeout */ > + retry = QED_IP_RESOL_TIMEOUT; > + while (!(neigh->nud_state & NUD_VALID) && retry > 0) { > + msleep(1000); > + retry--; > + } > + > + if (neigh->nud_state & NUD_VALID) { > + /* copy resolved MAC address */ > + neigh_ha_snapshot(hardware_address->sa_data, neigh, *ndev); > + > + hardware_address->sa_family = (*ndev)->type; > + rc = 0; > + } > + > + neigh_release(neigh); > + if (!(*loc_ip)) { > + *loc_ip = inet_select_addr(*ndev, *rem_ip, RT_SCOPE_UNIVERSE); > + local_addr->ss_family = AF_INET; > + } > + > +return_err: > + > + return rc; > +} > +EXPORT_SYMBOL(qed_route_ipv4); > + > +int qed_route_ipv6(struct sockaddr_storage *local_addr, > + struct sockaddr_storage *remote_addr, > + struct sockaddr *hardware_address, > + struct net_device **ndev) > +{ > + struct neighbour *neigh = NULL; > + struct dst_entry *dst; > + struct flowi6 fl6; > + int rc = -ENXIO; > + int retry; > + > + memset(&fl6, 0, sizeof(fl6)); > + fl6.saddr = ((struct sockaddr_in6 *)local_addr)->sin6_addr; > + fl6.daddr = ((struct sockaddr_in6 *)remote_addr)->sin6_addr; > + > + dst = ip6_route_output(&init_net, NULL, &fl6); > + if (!dst || dst->error) { > + if (dst) { > + dst_release(dst); > + pr_err("lookup route failed %d\n", dst->error); > + } > + > + goto out; > + } > + > + neigh = dst_neigh_lookup(dst, &fl6.daddr); > + if (neigh) { > + *ndev = ip6_dst_idev(dst)->dev; > + > + /* If not resolved, kick-off state machine towards resolution */ > + if (!(neigh->nud_state & NUD_VALID)) > + neigh_event_send(neigh, NULL); > + > + /* query neighbor until resolved or timeout */ > + retry = QED_IP_RESOL_TIMEOUT; > + while (!(neigh->nud_state & NUD_VALID) && retry > 0) { > + msleep(1000); > + retry--; > + } > + > + if (neigh->nud_state & NUD_VALID) { > + neigh_ha_snapshot((u8 *)hardware_address->sa_data, neigh, *ndev); > + > + hardware_address->sa_family = (*ndev)->type; > + rc = 0; > + } > + > + neigh_release(neigh); > + > + if (ipv6_addr_any(&fl6.saddr)) { > + if (ipv6_dev_get_saddr(dev_net(*ndev), *ndev, > + &fl6.daddr, 0, &fl6.saddr)) { > + pr_err("Unable to find source IP address\n"); > + goto out; > + } > + > + local_addr->ss_family = AF_INET6; > + ((struct sockaddr_in6 *)local_addr)->sin6_addr = > + fl6.saddr; > + } > + } > + > + dst_release(dst); > + > +out: > + > + return rc; > +} > +EXPORT_SYMBOL(qed_route_ipv6); > + > +void qed_vlan_get_ndev(struct net_device **ndev, u16 *vlan_id) > +{ > + if (is_vlan_dev(*ndev)) { > + *vlan_id = vlan_dev_vlan_id(*ndev); > + *ndev = vlan_dev_real_dev(*ndev); > + } > +} > +EXPORT_SYMBOL(qed_vlan_get_ndev); > + > +struct pci_dev *qed_validate_ndev(struct net_device *ndev) > +{ > + struct pci_dev *pdev = NULL; > + struct net_device *upper; > + > + for_each_pci_dev(pdev) { > + if (pdev && pdev->driver && > + !strcmp(pdev->driver->name, "qede")) { > + upper = pci_get_drvdata(pdev); > + if (upper->ifindex == ndev->ifindex) > + return pdev; > + } > + } > + > + return NULL; > +} > +EXPORT_SYMBOL(qed_validate_ndev); > + > +__be16 qed_get_in_port(struct sockaddr_storage *sa) > +{ > + return sa->ss_family == AF_INET > + ? ((struct sockaddr_in *)sa)->sin_port > + : ((struct sockaddr_in6 *)sa)->sin6_port; > +} > +EXPORT_SYMBOL(qed_get_in_port); > + > +int qed_fetch_tcp_port(struct sockaddr_storage local_ip_addr, > + struct socket **sock, u16 *port) > +{ > + struct sockaddr_storage sa; > + int rc = 0; > + > + rc = sock_create(local_ip_addr.ss_family, SOCK_STREAM, IPPROTO_TCP, sock); > + if (rc) { > + pr_warn("failed to create socket: %d\n", rc); > + goto err; > + } > + > + (*sock)->sk->sk_allocation = GFP_KERNEL; > + sk_set_memalloc((*sock)->sk); > + > + rc = kernel_bind(*sock, (struct sockaddr *)&local_ip_addr, > + sizeof(local_ip_addr)); > + > + if (rc) { > + pr_warn("failed to bind socket: %d\n", rc); > + goto err_sock; > + } > + > + rc = kernel_getsockname(*sock, (struct sockaddr *)&sa); > + if (rc < 0) { > + pr_warn("getsockname() failed: %d\n", rc); > + goto err_sock; > + } > + > + *port = ntohs(qed_get_in_port(&sa)); > + > + return 0; > + > +err_sock: > + sock_release(*sock); > + sock = NULL; > +err: > + > + return rc; > +} > +EXPORT_SYMBOL(qed_fetch_tcp_port); > + > +void qed_return_tcp_port(struct socket *sock) > +{ > + if (sock && sock->sk) { > + tcp_set_state(sock->sk, TCP_CLOSE); > + sock_release(sock); > + } > +} > +EXPORT_SYMBOL(qed_return_tcp_port); > diff --git a/include/linux/qed/qed_nvmetcp_ip_services_if.h b/include/linux/qed/qed_nvmetcp_ip_services_if.h > new file mode 100644 > index 000000000000..3604aee53796 > --- /dev/null > +++ b/include/linux/qed/qed_nvmetcp_ip_services_if.h > @@ -0,0 +1,29 @@ > +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ > +/* > + * Copyright 2021 Marvell. All rights reserved. > + */ > + > +#ifndef _QED_IP_SERVICES_IF_H > +#define _QED_IP_SERVICES_IF_H > + > +#include <linux/types.h> > +#include <net/route.h> > +#include <net/ip6_route.h> > +#include <linux/inetdevice.h> > + > +int qed_route_ipv4(struct sockaddr_storage *local_addr, > + struct sockaddr_storage *remote_addr, > + struct sockaddr *hardware_address, > + struct net_device **ndev); > +int qed_route_ipv6(struct sockaddr_storage *local_addr, > + struct sockaddr_storage *remote_addr, > + struct sockaddr *hardware_address, > + struct net_device **ndev); > +void qed_vlan_get_ndev(struct net_device **ndev, u16 *vlan_id); > +struct pci_dev *qed_validate_ndev(struct net_device *ndev); > +void qed_return_tcp_port(struct socket *sock); > +int qed_fetch_tcp_port(struct sockaddr_storage local_ip_addr, > + struct socket **sock, u16 *port); > +__be16 qed_get_in_port(struct sockaddr_storage *sa); > + > +#endif /* _QED_IP_SERVICES_IF_H */ > Reviewed-by: Hannes Reinecke <hare@suse.de> Cheers, Hannes
On 5/2/21 2:26 PM, Hannes Reinecke wrote: > On 4/29/21 9:09 PM, Shai Malin wrote: > > From: Nikolay Assa <nassa@marvell.com> > > > > This patch introduces APIs which the NVMeTCP Offload device (qedn) > > will use through the paired net-device (qede). > > It includes APIs for: > > - ipv4/ipv6 routing > > - get VLAN from net-device > > - TCP ports reservation > > > > Acked-by: Igor Russkikh <irusskikh@marvell.com> > > Signed-off-by: Nikolay Assa <nassa@marvell.com> > > Signed-off-by: Prabhakar Kushwaha <pkushwaha@marvell.com> > > Signed-off-by: Omkar Kulkarni <okulkarni@marvell.com> > > Signed-off-by: Michal Kalderon <mkalderon@marvell.com> > > Signed-off-by: Ariel Elior <aelior@marvell.com> > > Signed-off-by: Shai Malin <smalin@marvell.com> > > --- > > .../qlogic/qed/qed_nvmetcp_ip_services.c | 239 ++++++++++++++++++ > > .../linux/qed/qed_nvmetcp_ip_services_if.h | 29 +++ > > 2 files changed, 268 insertions(+) > > create mode 100644 drivers/net/ethernet/qlogic/qed/qed_nvmetcp_ip_services.c > > create mode 100644 include/linux/qed/qed_nvmetcp_ip_services_if.h > > > > diff --git a/drivers/net/ethernet/qlogic/qed/qed_nvmetcp_ip_services.c b/drivers/net/ethernet/qlogic/qed/qed_nvmetcp_ip_services.c > > new file mode 100644 > > index 000000000000..2904b1a0830a > > --- /dev/null > > +++ b/drivers/net/ethernet/qlogic/qed/qed_nvmetcp_ip_services.c > > @@ -0,0 +1,239 @@ > > +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) > > +/* > > + * Copyright 2021 Marvell. All rights reserved. > > + */ > > + > > +#include <linux/types.h> > > +#include <asm/byteorder.h> > > +#include <asm/param.h> > > +#include <linux/delay.h> > > +#include <linux/pci.h> > > +#include <linux/dma-mapping.h> > > +#include <linux/etherdevice.h> > > +#include <linux/kernel.h> > > +#include <linux/stddef.h> > > +#include <linux/errno.h> > > + > > +#include <net/tcp.h> > > + > > +#include <linux/qed/qed_nvmetcp_ip_services_if.h> > > + > > +#define QED_IP_RESOL_TIMEOUT 4 > > + > > +int qed_route_ipv4(struct sockaddr_storage *local_addr, > > + struct sockaddr_storage *remote_addr, > > + struct sockaddr *hardware_address, > > + struct net_device **ndev) > > +{ > > + struct neighbour *neigh = NULL; > > + __be32 *loc_ip, *rem_ip; > > + struct rtable *rt; > > + int rc = -ENXIO; > > + int retry; > > + > > + loc_ip = &((struct sockaddr_in *)local_addr)->sin_addr.s_addr; > > + rem_ip = &((struct sockaddr_in *)remote_addr)->sin_addr.s_addr; > > + *ndev = NULL; > > + rt = ip_route_output(&init_net, *rem_ip, *loc_ip, 0/*tos*/, 0/*oif*/); > > + if (IS_ERR(rt)) { > > + pr_err("lookup route failed\n"); > > + rc = PTR_ERR(rt); > > + goto return_err; > > + } > > + > > + neigh = dst_neigh_lookup(&rt->dst, rem_ip); > > + if (!neigh) { > > + rc = -ENOMEM; > > + ip_rt_put(rt); > > + goto return_err; > > + } > > + > > + *ndev = rt->dst.dev; > > + ip_rt_put(rt); > > + > > + /* If not resolved, kick-off state machine towards resolution */ > > + if (!(neigh->nud_state & NUD_VALID)) > > + neigh_event_send(neigh, NULL); > > + > > + /* query neighbor until resolved or timeout */ > > + retry = QED_IP_RESOL_TIMEOUT; > > + while (!(neigh->nud_state & NUD_VALID) && retry > 0) { > > + msleep(1000); > > + retry--; > > + } > > + > > + if (neigh->nud_state & NUD_VALID) { > > + /* copy resolved MAC address */ > > + neigh_ha_snapshot(hardware_address->sa_data, neigh, *ndev); > > + > > + hardware_address->sa_family = (*ndev)->type; > > + rc = 0; > > + } > > + > > + neigh_release(neigh); > > + if (!(*loc_ip)) { > > + *loc_ip = inet_select_addr(*ndev, *rem_ip, RT_SCOPE_UNIVERSE); > > + local_addr->ss_family = AF_INET; > > + } > > + > > +return_err: > > + > > + return rc; > > +} > > +EXPORT_SYMBOL(qed_route_ipv4); > > + > > +int qed_route_ipv6(struct sockaddr_storage *local_addr, > > + struct sockaddr_storage *remote_addr, > > + struct sockaddr *hardware_address, > > + struct net_device **ndev) > > +{ > > + struct neighbour *neigh = NULL; > > + struct dst_entry *dst; > > + struct flowi6 fl6; > > + int rc = -ENXIO; > > + int retry; > > + > > + memset(&fl6, 0, sizeof(fl6)); > > + fl6.saddr = ((struct sockaddr_in6 *)local_addr)->sin6_addr; > > + fl6.daddr = ((struct sockaddr_in6 *)remote_addr)->sin6_addr; > > + > > + dst = ip6_route_output(&init_net, NULL, &fl6); > > + if (!dst || dst->error) { > > + if (dst) { > > + dst_release(dst); > > + pr_err("lookup route failed %d\n", dst->error); > > + } > > + > > + goto out; > > + } > > + > > + neigh = dst_neigh_lookup(dst, &fl6.daddr); > > + if (neigh) { > > + *ndev = ip6_dst_idev(dst)->dev; > > + > > + /* If not resolved, kick-off state machine towards resolution */ > > + if (!(neigh->nud_state & NUD_VALID)) > > + neigh_event_send(neigh, NULL); > > + > > + /* query neighbor until resolved or timeout */ > > + retry = QED_IP_RESOL_TIMEOUT; > > + while (!(neigh->nud_state & NUD_VALID) && retry > 0) { > > + msleep(1000); > > + retry--; > > + } > > + > > + if (neigh->nud_state & NUD_VALID) { > > + neigh_ha_snapshot((u8 *)hardware_address->sa_data, neigh, *ndev); > > + > > + hardware_address->sa_family = (*ndev)->type; > > + rc = 0; > > + } > > + > > + neigh_release(neigh); > > + > > + if (ipv6_addr_any(&fl6.saddr)) { > > + if (ipv6_dev_get_saddr(dev_net(*ndev), *ndev, > > + &fl6.daddr, 0, &fl6.saddr)) { > > + pr_err("Unable to find source IP address\n"); > > + goto out; > > + } > > + > > + local_addr->ss_family = AF_INET6; > > + ((struct sockaddr_in6 *)local_addr)->sin6_addr = > > + fl6.saddr; > > + } > > + } > > + > > + dst_release(dst); > > + > > +out: > > + > > + return rc; > > +} > > +EXPORT_SYMBOL(qed_route_ipv6); > > + > > +void qed_vlan_get_ndev(struct net_device **ndev, u16 *vlan_id) > > +{ > > + if (is_vlan_dev(*ndev)) { > > + *vlan_id = vlan_dev_vlan_id(*ndev); > > + *ndev = vlan_dev_real_dev(*ndev); > > + } > > +} > > +EXPORT_SYMBOL(qed_vlan_get_ndev); > > + > > +struct pci_dev *qed_validate_ndev(struct net_device *ndev) > > +{ > > + struct pci_dev *pdev = NULL; > > + struct net_device *upper; > > + > > + for_each_pci_dev(pdev) { > > + if (pdev && pdev->driver && > > + !strcmp(pdev->driver->name, "qede")) { > > + upper = pci_get_drvdata(pdev); > > + if (upper->ifindex == ndev->ifindex) > > + return pdev; > > + } > > + } > > + > > + return NULL; > > +} > > +EXPORT_SYMBOL(qed_validate_ndev); > > + > > +__be16 qed_get_in_port(struct sockaddr_storage *sa) > > +{ > > + return sa->ss_family == AF_INET > > + ? ((struct sockaddr_in *)sa)->sin_port > > + : ((struct sockaddr_in6 *)sa)->sin6_port; > > +} > > +EXPORT_SYMBOL(qed_get_in_port); > > + > > +int qed_fetch_tcp_port(struct sockaddr_storage local_ip_addr, > > + struct socket **sock, u16 *port) > > +{ > > + struct sockaddr_storage sa; > > + int rc = 0; > > + > > + rc = sock_create(local_ip_addr.ss_family, SOCK_STREAM, IPPROTO_TCP, sock); > > + if (rc) { > > + pr_warn("failed to create socket: %d\n", rc); > > + goto err; > > + } > > + > > + (*sock)->sk->sk_allocation = GFP_KERNEL; > > + sk_set_memalloc((*sock)->sk); > > + > > + rc = kernel_bind(*sock, (struct sockaddr *)&local_ip_addr, > > + sizeof(local_ip_addr)); > > + > > + if (rc) { > > + pr_warn("failed to bind socket: %d\n", rc); > > + goto err_sock; > > + } > > + > > + rc = kernel_getsockname(*sock, (struct sockaddr *)&sa); > > + if (rc < 0) { > > + pr_warn("getsockname() failed: %d\n", rc); > > + goto err_sock; > > + } > > + > > + *port = ntohs(qed_get_in_port(&sa)); > > + > > + return 0; > > + > > +err_sock: > > + sock_release(*sock); > > + sock = NULL; > > +err: > > + > > + return rc; > > +} > > +EXPORT_SYMBOL(qed_fetch_tcp_port); > > + > > +void qed_return_tcp_port(struct socket *sock) > > +{ > > + if (sock && sock->sk) { > > + tcp_set_state(sock->sk, TCP_CLOSE); > > + sock_release(sock); > > + } > > +} > > +EXPORT_SYMBOL(qed_return_tcp_port); > > diff --git a/include/linux/qed/qed_nvmetcp_ip_services_if.h b/include/linux/qed/qed_nvmetcp_ip_services_if.h > > new file mode 100644 > > index 000000000000..3604aee53796 > > --- /dev/null > > +++ b/include/linux/qed/qed_nvmetcp_ip_services_if.h > > @@ -0,0 +1,29 @@ > > +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ > > +/* > > + * Copyright 2021 Marvell. All rights reserved. > > + */ > > + > > +#ifndef _QED_IP_SERVICES_IF_H > > +#define _QED_IP_SERVICES_IF_H > > + > > +#include <linux/types.h> > > +#include <net/route.h> > > +#include <net/ip6_route.h> > > +#include <linux/inetdevice.h> > > + > > +int qed_route_ipv4(struct sockaddr_storage *local_addr, > > + struct sockaddr_storage *remote_addr, > > + struct sockaddr *hardware_address, > > + struct net_device **ndev); > > +int qed_route_ipv6(struct sockaddr_storage *local_addr, > > + struct sockaddr_storage *remote_addr, > > + struct sockaddr *hardware_address, > > + struct net_device **ndev); > > +void qed_vlan_get_ndev(struct net_device **ndev, u16 *vlan_id); > > +struct pci_dev *qed_validate_ndev(struct net_device *ndev); > > +void qed_return_tcp_port(struct socket *sock); > > +int qed_fetch_tcp_port(struct sockaddr_storage local_ip_addr, > > + struct socket **sock, u16 *port); > > +__be16 qed_get_in_port(struct sockaddr_storage *sa); > > + > > +#endif /* _QED_IP_SERVICES_IF_H */ > > > Reviewed-by: Hannes Reinecke <hare@suse.de> > Thanks. > Cheers, > > Hannes > -- > Dr. Hannes Reinecke Kernel Storage Architect > hare@suse.de +49 911 74053 688 > SUSE Software Solutions GmbH, Maxfeldstr. 5, 90409 Nürnberg > HRB 36809 (AG Nürnberg), Geschäftsführer: Felix Imendörffer
diff --git a/drivers/net/ethernet/qlogic/qed/qed_nvmetcp_ip_services.c b/drivers/net/ethernet/qlogic/qed/qed_nvmetcp_ip_services.c new file mode 100644 index 000000000000..2904b1a0830a --- /dev/null +++ b/drivers/net/ethernet/qlogic/qed/qed_nvmetcp_ip_services.c @@ -0,0 +1,239 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +/* + * Copyright 2021 Marvell. All rights reserved. + */ + +#include <linux/types.h> +#include <asm/byteorder.h> +#include <asm/param.h> +#include <linux/delay.h> +#include <linux/pci.h> +#include <linux/dma-mapping.h> +#include <linux/etherdevice.h> +#include <linux/kernel.h> +#include <linux/stddef.h> +#include <linux/errno.h> + +#include <net/tcp.h> + +#include <linux/qed/qed_nvmetcp_ip_services_if.h> + +#define QED_IP_RESOL_TIMEOUT 4 + +int qed_route_ipv4(struct sockaddr_storage *local_addr, + struct sockaddr_storage *remote_addr, + struct sockaddr *hardware_address, + struct net_device **ndev) +{ + struct neighbour *neigh = NULL; + __be32 *loc_ip, *rem_ip; + struct rtable *rt; + int rc = -ENXIO; + int retry; + + loc_ip = &((struct sockaddr_in *)local_addr)->sin_addr.s_addr; + rem_ip = &((struct sockaddr_in *)remote_addr)->sin_addr.s_addr; + *ndev = NULL; + rt = ip_route_output(&init_net, *rem_ip, *loc_ip, 0/*tos*/, 0/*oif*/); + if (IS_ERR(rt)) { + pr_err("lookup route failed\n"); + rc = PTR_ERR(rt); + goto return_err; + } + + neigh = dst_neigh_lookup(&rt->dst, rem_ip); + if (!neigh) { + rc = -ENOMEM; + ip_rt_put(rt); + goto return_err; + } + + *ndev = rt->dst.dev; + ip_rt_put(rt); + + /* If not resolved, kick-off state machine towards resolution */ + if (!(neigh->nud_state & NUD_VALID)) + neigh_event_send(neigh, NULL); + + /* query neighbor until resolved or timeout */ + retry = QED_IP_RESOL_TIMEOUT; + while (!(neigh->nud_state & NUD_VALID) && retry > 0) { + msleep(1000); + retry--; + } + + if (neigh->nud_state & NUD_VALID) { + /* copy resolved MAC address */ + neigh_ha_snapshot(hardware_address->sa_data, neigh, *ndev); + + hardware_address->sa_family = (*ndev)->type; + rc = 0; + } + + neigh_release(neigh); + if (!(*loc_ip)) { + *loc_ip = inet_select_addr(*ndev, *rem_ip, RT_SCOPE_UNIVERSE); + local_addr->ss_family = AF_INET; + } + +return_err: + + return rc; +} +EXPORT_SYMBOL(qed_route_ipv4); + +int qed_route_ipv6(struct sockaddr_storage *local_addr, + struct sockaddr_storage *remote_addr, + struct sockaddr *hardware_address, + struct net_device **ndev) +{ + struct neighbour *neigh = NULL; + struct dst_entry *dst; + struct flowi6 fl6; + int rc = -ENXIO; + int retry; + + memset(&fl6, 0, sizeof(fl6)); + fl6.saddr = ((struct sockaddr_in6 *)local_addr)->sin6_addr; + fl6.daddr = ((struct sockaddr_in6 *)remote_addr)->sin6_addr; + + dst = ip6_route_output(&init_net, NULL, &fl6); + if (!dst || dst->error) { + if (dst) { + dst_release(dst); + pr_err("lookup route failed %d\n", dst->error); + } + + goto out; + } + + neigh = dst_neigh_lookup(dst, &fl6.daddr); + if (neigh) { + *ndev = ip6_dst_idev(dst)->dev; + + /* If not resolved, kick-off state machine towards resolution */ + if (!(neigh->nud_state & NUD_VALID)) + neigh_event_send(neigh, NULL); + + /* query neighbor until resolved or timeout */ + retry = QED_IP_RESOL_TIMEOUT; + while (!(neigh->nud_state & NUD_VALID) && retry > 0) { + msleep(1000); + retry--; + } + + if (neigh->nud_state & NUD_VALID) { + neigh_ha_snapshot((u8 *)hardware_address->sa_data, neigh, *ndev); + + hardware_address->sa_family = (*ndev)->type; + rc = 0; + } + + neigh_release(neigh); + + if (ipv6_addr_any(&fl6.saddr)) { + if (ipv6_dev_get_saddr(dev_net(*ndev), *ndev, + &fl6.daddr, 0, &fl6.saddr)) { + pr_err("Unable to find source IP address\n"); + goto out; + } + + local_addr->ss_family = AF_INET6; + ((struct sockaddr_in6 *)local_addr)->sin6_addr = + fl6.saddr; + } + } + + dst_release(dst); + +out: + + return rc; +} +EXPORT_SYMBOL(qed_route_ipv6); + +void qed_vlan_get_ndev(struct net_device **ndev, u16 *vlan_id) +{ + if (is_vlan_dev(*ndev)) { + *vlan_id = vlan_dev_vlan_id(*ndev); + *ndev = vlan_dev_real_dev(*ndev); + } +} +EXPORT_SYMBOL(qed_vlan_get_ndev); + +struct pci_dev *qed_validate_ndev(struct net_device *ndev) +{ + struct pci_dev *pdev = NULL; + struct net_device *upper; + + for_each_pci_dev(pdev) { + if (pdev && pdev->driver && + !strcmp(pdev->driver->name, "qede")) { + upper = pci_get_drvdata(pdev); + if (upper->ifindex == ndev->ifindex) + return pdev; + } + } + + return NULL; +} +EXPORT_SYMBOL(qed_validate_ndev); + +__be16 qed_get_in_port(struct sockaddr_storage *sa) +{ + return sa->ss_family == AF_INET + ? ((struct sockaddr_in *)sa)->sin_port + : ((struct sockaddr_in6 *)sa)->sin6_port; +} +EXPORT_SYMBOL(qed_get_in_port); + +int qed_fetch_tcp_port(struct sockaddr_storage local_ip_addr, + struct socket **sock, u16 *port) +{ + struct sockaddr_storage sa; + int rc = 0; + + rc = sock_create(local_ip_addr.ss_family, SOCK_STREAM, IPPROTO_TCP, sock); + if (rc) { + pr_warn("failed to create socket: %d\n", rc); + goto err; + } + + (*sock)->sk->sk_allocation = GFP_KERNEL; + sk_set_memalloc((*sock)->sk); + + rc = kernel_bind(*sock, (struct sockaddr *)&local_ip_addr, + sizeof(local_ip_addr)); + + if (rc) { + pr_warn("failed to bind socket: %d\n", rc); + goto err_sock; + } + + rc = kernel_getsockname(*sock, (struct sockaddr *)&sa); + if (rc < 0) { + pr_warn("getsockname() failed: %d\n", rc); + goto err_sock; + } + + *port = ntohs(qed_get_in_port(&sa)); + + return 0; + +err_sock: + sock_release(*sock); + sock = NULL; +err: + + return rc; +} +EXPORT_SYMBOL(qed_fetch_tcp_port); + +void qed_return_tcp_port(struct socket *sock) +{ + if (sock && sock->sk) { + tcp_set_state(sock->sk, TCP_CLOSE); + sock_release(sock); + } +} +EXPORT_SYMBOL(qed_return_tcp_port); diff --git a/include/linux/qed/qed_nvmetcp_ip_services_if.h b/include/linux/qed/qed_nvmetcp_ip_services_if.h new file mode 100644 index 000000000000..3604aee53796 --- /dev/null +++ b/include/linux/qed/qed_nvmetcp_ip_services_if.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ +/* + * Copyright 2021 Marvell. All rights reserved. + */ + +#ifndef _QED_IP_SERVICES_IF_H +#define _QED_IP_SERVICES_IF_H + +#include <linux/types.h> +#include <net/route.h> +#include <net/ip6_route.h> +#include <linux/inetdevice.h> + +int qed_route_ipv4(struct sockaddr_storage *local_addr, + struct sockaddr_storage *remote_addr, + struct sockaddr *hardware_address, + struct net_device **ndev); +int qed_route_ipv6(struct sockaddr_storage *local_addr, + struct sockaddr_storage *remote_addr, + struct sockaddr *hardware_address, + struct net_device **ndev); +void qed_vlan_get_ndev(struct net_device **ndev, u16 *vlan_id); +struct pci_dev *qed_validate_ndev(struct net_device *ndev); +void qed_return_tcp_port(struct socket *sock); +int qed_fetch_tcp_port(struct sockaddr_storage local_ip_addr, + struct socket **sock, u16 *port); +__be16 qed_get_in_port(struct sockaddr_storage *sa); + +#endif /* _QED_IP_SERVICES_IF_H */