Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Linux/drivers/infiniband/core/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 143 kB image not shown  

Quelle  cma.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/*
 * Copyright (c) 2005 Voltaire Inc.  All rights reserved.
 * Copyright (c) 2002-2005, Network Appliance, Inc. All rights reserved.
 * Copyright (c) 1999-2019, Mellanox Technologies, Inc. All rights reserved.
 * Copyright (c) 2005-2006 Intel Corporation.  All rights reserved.
 */


#include <linux/completion.h>
#include <linux/in.h>
#include <linux/in6.h>
#include <linux/mutex.h>
#include <linux/random.h>
#include <linux/rbtree.h>
#include <linux/igmp.h>
#include <linux/xarray.h>
#include <linux/inetdevice.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <net/route.h>

#include <net/net_namespace.h>
#include <net/netns/generic.h>
#include <net/netevent.h>
#include <net/tcp.h>
#include <net/ipv6.h>
#include <net/ip_fib.h>
#include <net/ip6_route.h>

#include <rdma/rdma_cm.h>
#include <rdma/rdma_cm_ib.h>
#include <rdma/rdma_netlink.h>
#include <rdma/ib.h>
#include <rdma/ib_cache.h>
#include <rdma/ib_cm.h>
#include <rdma/ib_sa.h>
#include <rdma/iw_cm.h>

#include "core_priv.h"
#include "cma_priv.h"
#include "cma_trace.h"

MODULE_AUTHOR("Sean Hefty");
MODULE_DESCRIPTION("Generic RDMA CM Agent");
MODULE_LICENSE("Dual BSD/GPL");

#define CMA_CM_RESPONSE_TIMEOUT 20
#define CMA_MAX_CM_RETRIES 15
#define CMA_IBOE_PACKET_LIFETIME 16
#define CMA_PREFERRED_ROCE_GID_TYPE IB_GID_TYPE_ROCE_UDP_ENCAP

static const char * const cma_events[] = {
 [RDMA_CM_EVENT_ADDR_RESOLVED]  = "address resolved",
 [RDMA_CM_EVENT_ADDR_ERROR]  = "address error",
 [RDMA_CM_EVENT_ROUTE_RESOLVED]  = "route resolved ",
 [RDMA_CM_EVENT_ROUTE_ERROR]  = "route error",
 [RDMA_CM_EVENT_CONNECT_REQUEST]  = "connect request",
 [RDMA_CM_EVENT_CONNECT_RESPONSE] = "connect response",
 [RDMA_CM_EVENT_CONNECT_ERROR]  = "connect error",
 [RDMA_CM_EVENT_UNREACHABLE]  = "unreachable",
 [RDMA_CM_EVENT_REJECTED]  = "rejected",
 [RDMA_CM_EVENT_ESTABLISHED]  = "established",
 [RDMA_CM_EVENT_DISCONNECTED]  = "disconnected",
 [RDMA_CM_EVENT_DEVICE_REMOVAL]  = "device removal",
 [RDMA_CM_EVENT_MULTICAST_JOIN]  = "multicast join",
 [RDMA_CM_EVENT_MULTICAST_ERROR]  = "multicast error",
 [RDMA_CM_EVENT_ADDR_CHANGE]  = "address change",
 [RDMA_CM_EVENT_TIMEWAIT_EXIT]  = "timewait exit",
};

static void cma_iboe_set_mgid(struct sockaddr *addr, union ib_gid *mgid,
         enum ib_gid_type gid_type);

static void cma_netevent_work_handler(struct work_struct *_work);

const char *__attribute_const__ rdma_event_msg(enum rdma_cm_event_type event)
{
 size_t index = event;

 return (index < ARRAY_SIZE(cma_events) && cma_events[index]) ?
   cma_events[index] : "unrecognized event";
}
EXPORT_SYMBOL(rdma_event_msg);

const char *__attribute_const__ rdma_reject_msg(struct rdma_cm_id *id,
      int reason)
{
 if (rdma_ib_or_roce(id->device, id->port_num))
  return ibcm_reject_msg(reason);

 if (rdma_protocol_iwarp(id->device, id->port_num))
  return iwcm_reject_msg(reason);

 WARN_ON_ONCE(1);
 return "unrecognized transport";
}
EXPORT_SYMBOL(rdma_reject_msg);

/**
 * rdma_is_consumer_reject - return true if the consumer rejected the connect
 *                           request.
 * @id: Communication identifier that received the REJECT event.
 * @reason: Value returned in the REJECT event status field.
 */

static bool rdma_is_consumer_reject(struct rdma_cm_id *id, int reason)
{
 if (rdma_ib_or_roce(id->device, id->port_num))
  return reason == IB_CM_REJ_CONSUMER_DEFINED;

 if (rdma_protocol_iwarp(id->device, id->port_num))
  return reason == -ECONNREFUSED;

 WARN_ON_ONCE(1);
 return false;
}

const void *rdma_consumer_reject_data(struct rdma_cm_id *id,
          struct rdma_cm_event *ev, u8 *data_len)
{
 const void *p;

 if (rdma_is_consumer_reject(id, ev->status)) {
  *data_len = ev->param.conn.private_data_len;
  p = ev->param.conn.private_data;
 } else {
  *data_len = 0;
  p = NULL;
 }
 return p;
}
EXPORT_SYMBOL(rdma_consumer_reject_data);

/**
 * rdma_iw_cm_id() - return the iw_cm_id pointer for this cm_id.
 * @id: Communication Identifier
 */

struct iw_cm_id *rdma_iw_cm_id(struct rdma_cm_id *id)
{
 struct rdma_id_private *id_priv;

 id_priv = container_of(id, struct rdma_id_private, id);
 if (id->device->node_type == RDMA_NODE_RNIC)
  return id_priv->cm_id.iw;
 return NULL;
}
EXPORT_SYMBOL(rdma_iw_cm_id);

static int cma_add_one(struct ib_device *device);
static void cma_remove_one(struct ib_device *device, void *client_data);

static struct ib_client cma_client = {
 .name   = "cma",
 .add    = cma_add_one,
 .remove = cma_remove_one
};

static struct ib_sa_client sa_client;
static LIST_HEAD(dev_list);
static LIST_HEAD(listen_any_list);
static DEFINE_MUTEX(lock);
static struct rb_root id_table = RB_ROOT;
/* Serialize operations of id_table tree */
static DEFINE_SPINLOCK(id_table_lock);
static struct workqueue_struct *cma_wq;
static unsigned int cma_pernet_id;

struct cma_pernet {
 struct xarray tcp_ps;
 struct xarray udp_ps;
 struct xarray ipoib_ps;
 struct xarray ib_ps;
};

static struct cma_pernet *cma_pernet(struct net *net)
{
 return net_generic(net, cma_pernet_id);
}

static
struct xarray *cma_pernet_xa(struct net *net, enum rdma_ucm_port_space ps)
{
 struct cma_pernet *pernet = cma_pernet(net);

 switch (ps) {
 case RDMA_PS_TCP:
  return &pernet->tcp_ps;
 case RDMA_PS_UDP:
  return &pernet->udp_ps;
 case RDMA_PS_IPOIB:
  return &pernet->ipoib_ps;
 case RDMA_PS_IB:
  return &pernet->ib_ps;
 default:
  return NULL;
 }
}

struct id_table_entry {
 struct list_head id_list;
 struct rb_node rb_node;
};

struct cma_device {
 struct list_head list;
 struct ib_device *device;
 struct completion comp;
 refcount_t refcount;
 struct list_head id_list;
 enum ib_gid_type *default_gid_type;
 u8   *default_roce_tos;
};

struct rdma_bind_list {
 enum rdma_ucm_port_space ps;
 struct hlist_head owners;
 unsigned short  port;
};

static int cma_ps_alloc(struct net *net, enum rdma_ucm_port_space ps,
   struct rdma_bind_list *bind_list, int snum)
{
 struct xarray *xa = cma_pernet_xa(net, ps);

 return xa_insert(xa, snum, bind_list, GFP_KERNEL);
}

static struct rdma_bind_list *cma_ps_find(struct net *net,
       enum rdma_ucm_port_space ps, int snum)
{
 struct xarray *xa = cma_pernet_xa(net, ps);

 return xa_load(xa, snum);
}

static void cma_ps_remove(struct net *net, enum rdma_ucm_port_space ps,
     int snum)
{
 struct xarray *xa = cma_pernet_xa(net, ps);

 xa_erase(xa, snum);
}

enum {
 CMA_OPTION_AFONLY,
};

void cma_dev_get(struct cma_device *cma_dev)
{
 refcount_inc(&cma_dev->refcount);
}

void cma_dev_put(struct cma_device *cma_dev)
{
 if (refcount_dec_and_test(&cma_dev->refcount))
  complete(&cma_dev->comp);
}

struct cma_device *cma_enum_devices_by_ibdev(cma_device_filter filter,
          void  *cookie)
{
 struct cma_device *cma_dev;
 struct cma_device *found_cma_dev = NULL;

 mutex_lock(&lock);

 list_for_each_entry(cma_dev, &dev_list, list)
  if (filter(cma_dev->device, cookie)) {
   found_cma_dev = cma_dev;
   break;
  }

 if (found_cma_dev)
  cma_dev_get(found_cma_dev);
 mutex_unlock(&lock);
 return found_cma_dev;
}

int cma_get_default_gid_type(struct cma_device *cma_dev,
        u32 port)
{
 if (!rdma_is_port_valid(cma_dev->device, port))
  return -EINVAL;

 return cma_dev->default_gid_type[port - rdma_start_port(cma_dev->device)];
}

int cma_set_default_gid_type(struct cma_device *cma_dev,
        u32 port,
        enum ib_gid_type default_gid_type)
{
 unsigned long supported_gids;

 if (!rdma_is_port_valid(cma_dev->device, port))
  return -EINVAL;

 if (default_gid_type == IB_GID_TYPE_IB &&
     rdma_protocol_roce_eth_encap(cma_dev->device, port))
  default_gid_type = IB_GID_TYPE_ROCE;

 supported_gids = roce_gid_type_mask_support(cma_dev->device, port);

 if (!(supported_gids & 1 << default_gid_type))
  return -EINVAL;

 cma_dev->default_gid_type[port - rdma_start_port(cma_dev->device)] =
  default_gid_type;

 return 0;
}

int cma_get_default_roce_tos(struct cma_device *cma_dev, u32 port)
{
 if (!rdma_is_port_valid(cma_dev->device, port))
  return -EINVAL;

 return cma_dev->default_roce_tos[port - rdma_start_port(cma_dev->device)];
}

int cma_set_default_roce_tos(struct cma_device *cma_dev, u32 port,
        u8 default_roce_tos)
{
 if (!rdma_is_port_valid(cma_dev->device, port))
  return -EINVAL;

 cma_dev->default_roce_tos[port - rdma_start_port(cma_dev->device)] =
   default_roce_tos;

 return 0;
}
struct ib_device *cma_get_ib_dev(struct cma_device *cma_dev)
{
 return cma_dev->device;
}

/*
 * Device removal can occur at anytime, so we need extra handling to
 * serialize notifying the user of device removal with other callbacks.
 * We do this by disabling removal notification while a callback is in process,
 * and reporting it after the callback completes.
 */


struct cma_multicast {
 struct rdma_id_private *id_priv;
 union {
  struct ib_sa_multicast *sa_mc;
  struct {
   struct work_struct work;
   struct rdma_cm_event event;
  } iboe_join;
 };
 struct list_head list;
 void   *context;
 struct sockaddr_storage addr;
 u8   join_state;
};

struct cma_work {
 struct work_struct work;
 struct rdma_id_private *id;
 enum rdma_cm_state old_state;
 enum rdma_cm_state new_state;
 struct rdma_cm_event event;
};

union cma_ip_addr {
 struct in6_addr ip6;
 struct {
  __be32 pad[3];
  __be32 addr;
 } ip4;
};

struct cma_hdr {
 u8 cma_version;
 u8 ip_version; /* IP version: 7:4 */
 __be16 port;
 union cma_ip_addr src_addr;
 union cma_ip_addr dst_addr;
};

#define CMA_VERSION 0x00

struct cma_req_info {
 struct sockaddr_storage listen_addr_storage;
 struct sockaddr_storage src_addr_storage;
 struct ib_device *device;
 union ib_gid local_gid;
 __be64 service_id;
 int port;
 bool has_gid;
 u16 pkey;
};

static int cma_comp_exch(struct rdma_id_private *id_priv,
    enum rdma_cm_state comp, enum rdma_cm_state exch)
{
 unsigned long flags;
 int ret;

 /*
 * The FSM uses a funny double locking where state is protected by both
 * the handler_mutex and the spinlock. State is not allowed to change
 * to/from a handler_mutex protected value without also holding
 * handler_mutex.
 */

 if (comp == RDMA_CM_CONNECT || exch == RDMA_CM_CONNECT)
  lockdep_assert_held(&id_priv->handler_mutex);

 spin_lock_irqsave(&id_priv->lock, flags);
 if ((ret = (id_priv->state == comp)))
  id_priv->state = exch;
 spin_unlock_irqrestore(&id_priv->lock, flags);
 return ret;
}

static inline u8 cma_get_ip_ver(const struct cma_hdr *hdr)
{
 return hdr->ip_version >> 4;
}

static void cma_set_ip_ver(struct cma_hdr *hdr, u8 ip_ver)
{
 hdr->ip_version = (ip_ver << 4) | (hdr->ip_version & 0xF);
}

static struct sockaddr *cma_src_addr(struct rdma_id_private *id_priv)
{
 return (struct sockaddr *)&id_priv->id.route.addr.src_addr;
}

static inline struct sockaddr *cma_dst_addr(struct rdma_id_private *id_priv)
{
 return (struct sockaddr *)&id_priv->id.route.addr.dst_addr;
}

static int cma_igmp_send(struct net_device *ndev, union ib_gid *mgid, bool join)
{
 struct in_device *in_dev = NULL;

 if (ndev) {
  rtnl_lock();
  in_dev = __in_dev_get_rtnl(ndev);
  if (in_dev) {
   if (join)
    ip_mc_inc_group(in_dev,
      *(__be32 *)(mgid->raw + 12));
   else
    ip_mc_dec_group(in_dev,
      *(__be32 *)(mgid->raw + 12));
  }
  rtnl_unlock();
 }
 return (in_dev) ? 0 : -ENODEV;
}

static int compare_netdev_and_ip(int ifindex_a, struct sockaddr *sa,
     struct id_table_entry *entry_b)
{
 struct rdma_id_private *id_priv = list_first_entry(
  &entry_b->id_list, struct rdma_id_private, id_list_entry);
 int ifindex_b = id_priv->id.route.addr.dev_addr.bound_dev_if;
 struct sockaddr *sb = cma_dst_addr(id_priv);

 if (ifindex_a != ifindex_b)
  return (ifindex_a > ifindex_b) ? 1 : -1;

 if (sa->sa_family != sb->sa_family)
  return sa->sa_family - sb->sa_family;

 if (sa->sa_family == AF_INET &&
     __builtin_object_size(sa, 0) >= sizeof(struct sockaddr_in)) {
  return memcmp(&((struct sockaddr_in *)sa)->sin_addr,
         &((struct sockaddr_in *)sb)->sin_addr,
         sizeof(((struct sockaddr_in *)sa)->sin_addr));
 }

 if (sa->sa_family == AF_INET6 &&
     __builtin_object_size(sa, 0) >= sizeof(struct sockaddr_in6)) {
  return ipv6_addr_cmp(&((struct sockaddr_in6 *)sa)->sin6_addr,
         &((struct sockaddr_in6 *)sb)->sin6_addr);
 }

 return -1;
}

static int cma_add_id_to_tree(struct rdma_id_private *node_id_priv)
{
 struct rb_node **new, *parent = NULL;
 struct id_table_entry *this, *node;
 unsigned long flags;
 int result;

 node = kzalloc(sizeof(*node), GFP_KERNEL);
 if (!node)
  return -ENOMEM;

 spin_lock_irqsave(&id_table_lock, flags);
 new = &id_table.rb_node;
 while (*new) {
  this = container_of(*newstruct id_table_entry, rb_node);
  result = compare_netdev_and_ip(
   node_id_priv->id.route.addr.dev_addr.bound_dev_if,
   cma_dst_addr(node_id_priv), this);

  parent = *new;
  if (result < 0)
   new = &((*new)->rb_left);
  else if (result > 0)
   new = &((*new)->rb_right);
  else {
   list_add_tail(&node_id_priv->id_list_entry,
          &this->id_list);
   kfree(node);
   goto unlock;
  }
 }

 INIT_LIST_HEAD(&node->id_list);
 list_add_tail(&node_id_priv->id_list_entry, &node->id_list);

 rb_link_node(&node->rb_node, parent, new);
 rb_insert_color(&node->rb_node, &id_table);

unlock:
 spin_unlock_irqrestore(&id_table_lock, flags);
 return 0;
}

static struct id_table_entry *
node_from_ndev_ip(struct rb_root *root, int ifindex, struct sockaddr *sa)
{
 struct rb_node *node = root->rb_node;
 struct id_table_entry *data;
 int result;

 while (node) {
  data = container_of(node, struct id_table_entry, rb_node);
  result = compare_netdev_and_ip(ifindex, sa, data);
  if (result < 0)
   node = node->rb_left;
  else if (result > 0)
   node = node->rb_right;
  else
   return data;
 }

 return NULL;
}

static void cma_remove_id_from_tree(struct rdma_id_private *id_priv)
{
 struct id_table_entry *data;
 unsigned long flags;

 spin_lock_irqsave(&id_table_lock, flags);
 if (list_empty(&id_priv->id_list_entry))
  goto out;

 data = node_from_ndev_ip(&id_table,
     id_priv->id.route.addr.dev_addr.bound_dev_if,
     cma_dst_addr(id_priv));
 if (!data)
  goto out;

 list_del_init(&id_priv->id_list_entry);
 if (list_empty(&data->id_list)) {
  rb_erase(&data->rb_node, &id_table);
  kfree(data);
 }
out:
 spin_unlock_irqrestore(&id_table_lock, flags);
}

static void _cma_attach_to_dev(struct rdma_id_private *id_priv,
          struct cma_device *cma_dev)
{
 cma_dev_get(cma_dev);
 id_priv->cma_dev = cma_dev;
 id_priv->id.device = cma_dev->device;
 id_priv->id.route.addr.dev_addr.transport =
  rdma_node_get_transport(cma_dev->device->node_type);
 list_add_tail(&id_priv->device_item, &cma_dev->id_list);

 trace_cm_id_attach(id_priv, cma_dev->device);
}

static void cma_attach_to_dev(struct rdma_id_private *id_priv,
         struct cma_device *cma_dev)
{
 _cma_attach_to_dev(id_priv, cma_dev);
 id_priv->gid_type =
  cma_dev->default_gid_type[id_priv->id.port_num -
       rdma_start_port(cma_dev->device)];
}

static void cma_release_dev(struct rdma_id_private *id_priv)
{
 mutex_lock(&lock);
 list_del_init(&id_priv->device_item);
 cma_dev_put(id_priv->cma_dev);
 id_priv->cma_dev = NULL;
 id_priv->id.device = NULL;
 if (id_priv->id.route.addr.dev_addr.sgid_attr) {
  rdma_put_gid_attr(id_priv->id.route.addr.dev_addr.sgid_attr);
  id_priv->id.route.addr.dev_addr.sgid_attr = NULL;
 }
 mutex_unlock(&lock);
}

static inline unsigned short cma_family(struct rdma_id_private *id_priv)
{
 return id_priv->id.route.addr.src_addr.ss_family;
}

static int cma_set_default_qkey(struct rdma_id_private *id_priv)
{
 struct ib_sa_mcmember_rec rec;
 int ret = 0;

 switch (id_priv->id.ps) {
 case RDMA_PS_UDP:
 case RDMA_PS_IB:
  id_priv->qkey = RDMA_UDP_QKEY;
  break;
 case RDMA_PS_IPOIB:
  ib_addr_get_mgid(&id_priv->id.route.addr.dev_addr, &rec.mgid);
  ret = ib_sa_get_mcmember_rec(id_priv->id.device,
          id_priv->id.port_num, &rec.mgid,
          &rec);
  if (!ret)
   id_priv->qkey = be32_to_cpu(rec.qkey);
  break;
 default:
  break;
 }
 return ret;
}

static int cma_set_qkey(struct rdma_id_private *id_priv, u32 qkey)
{
 if (!qkey ||
     (id_priv->qkey && (id_priv->qkey != qkey)))
  return -EINVAL;

 id_priv->qkey = qkey;
 return 0;
}

static void cma_translate_ib(struct sockaddr_ib *sib, struct rdma_dev_addr *dev_addr)
{
 dev_addr->dev_type = ARPHRD_INFINIBAND;
 rdma_addr_set_sgid(dev_addr, (union ib_gid *) &sib->sib_addr);
 ib_addr_set_pkey(dev_addr, ntohs(sib->sib_pkey));
}

static int cma_translate_addr(struct sockaddr *addr, struct rdma_dev_addr *dev_addr)
{
 int ret;

 if (addr->sa_family != AF_IB) {
  ret = rdma_translate_ip(addr, dev_addr);
 } else {
  cma_translate_ib((struct sockaddr_ib *) addr, dev_addr);
  ret = 0;
 }

 return ret;
}

static const struct ib_gid_attr *
cma_validate_port(struct ib_device *device, u32 port,
    enum ib_gid_type gid_type,
    union ib_gid *gid,
    struct rdma_id_private *id_priv)
{
 struct rdma_dev_addr *dev_addr = &id_priv->id.route.addr.dev_addr;
 const struct ib_gid_attr *sgid_attr = ERR_PTR(-ENODEV);
 int bound_if_index = dev_addr->bound_dev_if;
 int dev_type = dev_addr->dev_type;
 struct net_device *ndev = NULL;
 struct net_device *pdev = NULL;

 if (!rdma_dev_access_netns(device, id_priv->id.route.addr.dev_addr.net))
  goto out;

 if ((dev_type == ARPHRD_INFINIBAND) && !rdma_protocol_ib(device, port))
  goto out;

 if ((dev_type != ARPHRD_INFINIBAND) && rdma_protocol_ib(device, port))
  goto out;

 /*
 * For drivers that do not associate more than one net device with
 * their gid tables, such as iWARP drivers, it is sufficient to
 * return the first table entry.
 *
 * Other driver classes might be included in the future.
 */

 if (rdma_protocol_iwarp(device, port)) {
  sgid_attr = rdma_get_gid_attr(device, port, 0);
  if (IS_ERR(sgid_attr))
   goto out;

  rcu_read_lock();
  ndev = rcu_dereference(sgid_attr->ndev);
  if (ndev->ifindex != bound_if_index) {
   pdev = dev_get_by_index_rcu(dev_addr->net, bound_if_index);
   if (pdev) {
    if (is_vlan_dev(pdev)) {
     pdev = vlan_dev_real_dev(pdev);
     if (ndev->ifindex == pdev->ifindex)
      bound_if_index = pdev->ifindex;
    }
    if (is_vlan_dev(ndev)) {
     pdev = vlan_dev_real_dev(ndev);
     if (bound_if_index == pdev->ifindex)
      bound_if_index = ndev->ifindex;
    }
   }
  }
  if (!net_eq(dev_net(ndev), dev_addr->net) ||
      ndev->ifindex != bound_if_index) {
   rdma_put_gid_attr(sgid_attr);
   sgid_attr = ERR_PTR(-ENODEV);
  }
  rcu_read_unlock();
  goto out;
 }

 /*
 * For a RXE device, it should work with TUN device and normal ethernet
 * devices. Use driver_id to check if a device is a RXE device or not.
 * ARPHDR_NONE means a TUN device.
 */

 if (device->ops.driver_id == RDMA_DRIVER_RXE) {
  if ((dev_type == ARPHRD_NONE || dev_type == ARPHRD_ETHER)
   && rdma_protocol_roce(device, port)) {
   ndev = dev_get_by_index(dev_addr->net, bound_if_index);
   if (!ndev)
    goto out;
  }
 } else {
  if (dev_type == ARPHRD_ETHER && rdma_protocol_roce(device, port)) {
   ndev = dev_get_by_index(dev_addr->net, bound_if_index);
   if (!ndev)
    goto out;
  } else {
   gid_type = IB_GID_TYPE_IB;
  }
 }

 sgid_attr = rdma_find_gid_by_port(device, gid, gid_type, port, ndev);
 dev_put(ndev);
out:
 return sgid_attr;
}

static void cma_bind_sgid_attr(struct rdma_id_private *id_priv,
          const struct ib_gid_attr *sgid_attr)
{
 WARN_ON(id_priv->id.route.addr.dev_addr.sgid_attr);
 id_priv->id.route.addr.dev_addr.sgid_attr = sgid_attr;
}

/**
 * cma_acquire_dev_by_src_ip - Acquire cma device, port, gid attribute
 * based on source ip address.
 * @id_priv: cm_id which should be bound to cma device
 *
 * cma_acquire_dev_by_src_ip() binds cm id to cma device, port and GID attribute
 * based on source IP address. It returns 0 on success or error code otherwise.
 * It is applicable to active and passive side cm_id.
 */

static int cma_acquire_dev_by_src_ip(struct rdma_id_private *id_priv)
{
 struct rdma_dev_addr *dev_addr = &id_priv->id.route.addr.dev_addr;
 const struct ib_gid_attr *sgid_attr;
 union ib_gid gid, iboe_gid, *gidp;
 struct cma_device *cma_dev;
 enum ib_gid_type gid_type;
 int ret = -ENODEV;
 u32 port;

 if (dev_addr->dev_type != ARPHRD_INFINIBAND &&
     id_priv->id.ps == RDMA_PS_IPOIB)
  return -EINVAL;

 rdma_ip2gid((struct sockaddr *)&id_priv->id.route.addr.src_addr,
      &iboe_gid);

 memcpy(&gid, dev_addr->src_dev_addr +
        rdma_addr_gid_offset(dev_addr), sizeof(gid));

 mutex_lock(&lock);
 list_for_each_entry(cma_dev, &dev_list, list) {
  rdma_for_each_port (cma_dev->device, port) {
   gidp = rdma_protocol_roce(cma_dev->device, port) ?
          &iboe_gid : &gid;
   gid_type = cma_dev->default_gid_type[port - 1];
   sgid_attr = cma_validate_port(cma_dev->device, port,
            gid_type, gidp, id_priv);
   if (!IS_ERR(sgid_attr)) {
    id_priv->id.port_num = port;
    cma_bind_sgid_attr(id_priv, sgid_attr);
    cma_attach_to_dev(id_priv, cma_dev);
    ret = 0;
    goto out;
   }
  }
 }
out:
 mutex_unlock(&lock);
 return ret;
}

/**
 * cma_ib_acquire_dev - Acquire cma device, port and SGID attribute
 * @id_priv: cm id to bind to cma device
 * @listen_id_priv: listener cm id to match against
 * @req: Pointer to req structure containaining incoming
 * request information
 * cma_ib_acquire_dev() acquires cma device, port and SGID attribute when
 * rdma device matches for listen_id and incoming request. It also verifies
 * that a GID table entry is present for the source address.
 * Returns 0 on success, or returns error code otherwise.
 */

static int cma_ib_acquire_dev(struct rdma_id_private *id_priv,
         const struct rdma_id_private *listen_id_priv,
         struct cma_req_info *req)
{
 struct rdma_dev_addr *dev_addr = &id_priv->id.route.addr.dev_addr;
 const struct ib_gid_attr *sgid_attr;
 enum ib_gid_type gid_type;
 union ib_gid gid;

 if (dev_addr->dev_type != ARPHRD_INFINIBAND &&
     id_priv->id.ps == RDMA_PS_IPOIB)
  return -EINVAL;

 if (rdma_protocol_roce(req->device, req->port))
  rdma_ip2gid((struct sockaddr *)&id_priv->id.route.addr.src_addr,
       &gid);
 else
  memcpy(&gid, dev_addr->src_dev_addr +
         rdma_addr_gid_offset(dev_addr), sizeof(gid));

 gid_type = listen_id_priv->cma_dev->default_gid_type[req->port - 1];
 sgid_attr = cma_validate_port(req->device, req->port,
          gid_type, &gid, id_priv);
 if (IS_ERR(sgid_attr))
  return PTR_ERR(sgid_attr);

 id_priv->id.port_num = req->port;
 cma_bind_sgid_attr(id_priv, sgid_attr);
 /* Need to acquire lock to protect against reader
 * of cma_dev->id_list such as cma_netdev_callback() and
 * cma_process_remove().
 */

 mutex_lock(&lock);
 cma_attach_to_dev(id_priv, listen_id_priv->cma_dev);
 mutex_unlock(&lock);
 rdma_restrack_add(&id_priv->res);
 return 0;
}

static int cma_iw_acquire_dev(struct rdma_id_private *id_priv,
         const struct rdma_id_private *listen_id_priv)
{
 struct rdma_dev_addr *dev_addr = &id_priv->id.route.addr.dev_addr;
 const struct ib_gid_attr *sgid_attr;
 struct cma_device *cma_dev;
 enum ib_gid_type gid_type;
 int ret = -ENODEV;
 union ib_gid gid;
 u32 port;

 if (dev_addr->dev_type != ARPHRD_INFINIBAND &&
     id_priv->id.ps == RDMA_PS_IPOIB)
  return -EINVAL;

 memcpy(&gid, dev_addr->src_dev_addr +
        rdma_addr_gid_offset(dev_addr), sizeof(gid));

 mutex_lock(&lock);

 cma_dev = listen_id_priv->cma_dev;
 port = listen_id_priv->id.port_num;
 gid_type = listen_id_priv->gid_type;
 sgid_attr = cma_validate_port(cma_dev->device, port,
          gid_type, &gid, id_priv);
 if (!IS_ERR(sgid_attr)) {
  id_priv->id.port_num = port;
  cma_bind_sgid_attr(id_priv, sgid_attr);
  ret = 0;
  goto out;
 }

 list_for_each_entry(cma_dev, &dev_list, list) {
  rdma_for_each_port (cma_dev->device, port) {
   if (listen_id_priv->cma_dev == cma_dev &&
       listen_id_priv->id.port_num == port)
    continue;

   gid_type = cma_dev->default_gid_type[port - 1];
   sgid_attr = cma_validate_port(cma_dev->device, port,
            gid_type, &gid, id_priv);
   if (!IS_ERR(sgid_attr)) {
    id_priv->id.port_num = port;
    cma_bind_sgid_attr(id_priv, sgid_attr);
    ret = 0;
    goto out;
   }
  }
 }

out:
 if (!ret) {
  cma_attach_to_dev(id_priv, cma_dev);
  rdma_restrack_add(&id_priv->res);
 }

 mutex_unlock(&lock);
 return ret;
}

/*
 * Select the source IB device and address to reach the destination IB address.
 */

static int cma_resolve_ib_dev(struct rdma_id_private *id_priv)
{
 struct cma_device *cma_dev, *cur_dev;
 struct sockaddr_ib *addr;
 union ib_gid gid, sgid, *dgid;
 unsigned int p;
 u16 pkey, index;
 enum ib_port_state port_state;
 int ret;
 int i;

 cma_dev = NULL;
 addr = (struct sockaddr_ib *) cma_dst_addr(id_priv);
 dgid = (union ib_gid *) &addr->sib_addr;
 pkey = ntohs(addr->sib_pkey);

 mutex_lock(&lock);
 list_for_each_entry(cur_dev, &dev_list, list) {
  rdma_for_each_port (cur_dev->device, p) {
   if (!rdma_cap_af_ib(cur_dev->device, p))
    continue;

   if (ib_find_cached_pkey(cur_dev->device, p, pkey, &index))
    continue;

   if (ib_get_cached_port_state(cur_dev->device, p, &port_state))
    continue;

   for (i = 0; i < cur_dev->device->port_data[p].immutable.gid_tbl_len;
        ++i) {
    ret = rdma_query_gid(cur_dev->device, p, i,
           &gid);
    if (ret)
     continue;

    if (!memcmp(&gid, dgid, sizeof(gid))) {
     cma_dev = cur_dev;
     sgid = gid;
     id_priv->id.port_num = p;
     goto found;
    }

    if (!cma_dev && (gid.global.subnet_prefix ==
        dgid->global.subnet_prefix) &&
        port_state == IB_PORT_ACTIVE) {
     cma_dev = cur_dev;
     sgid = gid;
     id_priv->id.port_num = p;
     goto found;
    }
   }
  }
 }
 mutex_unlock(&lock);
 return -ENODEV;

found:
 cma_attach_to_dev(id_priv, cma_dev);
 rdma_restrack_add(&id_priv->res);
 mutex_unlock(&lock);
 addr = (struct sockaddr_ib *)cma_src_addr(id_priv);
 memcpy(&addr->sib_addr, &sgid, sizeof(sgid));
 cma_translate_ib(addr, &id_priv->id.route.addr.dev_addr);
 return 0;
}

static void cma_id_get(struct rdma_id_private *id_priv)
{
 refcount_inc(&id_priv->refcount);
}

static void cma_id_put(struct rdma_id_private *id_priv)
{
 if (refcount_dec_and_test(&id_priv->refcount))
  complete(&id_priv->comp);
}

static struct rdma_id_private *
__rdma_create_id(struct net *net, rdma_cm_event_handler event_handler,
   void *context, enum rdma_ucm_port_space ps,
   enum ib_qp_type qp_type, const struct rdma_id_private *parent)
{
 struct rdma_id_private *id_priv;

 id_priv = kzalloc(sizeof *id_priv, GFP_KERNEL);
 if (!id_priv)
  return ERR_PTR(-ENOMEM);

 id_priv->state = RDMA_CM_IDLE;
 id_priv->id.context = context;
 id_priv->id.event_handler = event_handler;
 id_priv->id.ps = ps;
 id_priv->id.qp_type = qp_type;
 id_priv->tos_set = false;
 id_priv->timeout_set = false;
 id_priv->min_rnr_timer_set = false;
 id_priv->gid_type = IB_GID_TYPE_IB;
 spin_lock_init(&id_priv->lock);
 mutex_init(&id_priv->qp_mutex);
 init_completion(&id_priv->comp);
 refcount_set(&id_priv->refcount, 1);
 mutex_init(&id_priv->handler_mutex);
 INIT_LIST_HEAD(&id_priv->device_item);
 INIT_LIST_HEAD(&id_priv->id_list_entry);
 INIT_LIST_HEAD(&id_priv->listen_list);
 INIT_LIST_HEAD(&id_priv->mc_list);
 get_random_bytes(&id_priv->seq_num, sizeof id_priv->seq_num);
 id_priv->id.route.addr.dev_addr.net = get_net(net);
 id_priv->seq_num &= 0x00ffffff;
 INIT_WORK(&id_priv->id.net_work, cma_netevent_work_handler);

 rdma_restrack_new(&id_priv->res, RDMA_RESTRACK_CM_ID);
 if (parent)
  rdma_restrack_parent_name(&id_priv->res, &parent->res);

 return id_priv;
}

struct rdma_cm_id *
__rdma_create_kernel_id(struct net *net, rdma_cm_event_handler event_handler,
   void *context, enum rdma_ucm_port_space ps,
   enum ib_qp_type qp_type, const char *caller)
{
 struct rdma_id_private *ret;

 ret = __rdma_create_id(net, event_handler, context, ps, qp_type, NULL);
 if (IS_ERR(ret))
  return ERR_CAST(ret);

 rdma_restrack_set_name(&ret->res, caller);
 return &ret->id;
}
EXPORT_SYMBOL(__rdma_create_kernel_id);

struct rdma_cm_id *rdma_create_user_id(rdma_cm_event_handler event_handler,
           void *context,
           enum rdma_ucm_port_space ps,
           enum ib_qp_type qp_type)
{
 struct rdma_id_private *ret;

 ret = __rdma_create_id(current->nsproxy->net_ns, event_handler, context,
          ps, qp_type, NULL);
 if (IS_ERR(ret))
  return ERR_CAST(ret);

 rdma_restrack_set_name(&ret->res, NULL);
 return &ret->id;
}
EXPORT_SYMBOL(rdma_create_user_id);

static int cma_init_ud_qp(struct rdma_id_private *id_priv, struct ib_qp *qp)
{
 struct ib_qp_attr qp_attr;
 int qp_attr_mask, ret;

 qp_attr.qp_state = IB_QPS_INIT;
 ret = rdma_init_qp_attr(&id_priv->id, &qp_attr, &qp_attr_mask);
 if (ret)
  return ret;

 ret = ib_modify_qp(qp, &qp_attr, qp_attr_mask);
 if (ret)
  return ret;

 qp_attr.qp_state = IB_QPS_RTR;
 ret = ib_modify_qp(qp, &qp_attr, IB_QP_STATE);
 if (ret)
  return ret;

 qp_attr.qp_state = IB_QPS_RTS;
 qp_attr.sq_psn = 0;
 ret = ib_modify_qp(qp, &qp_attr, IB_QP_STATE | IB_QP_SQ_PSN);

 return ret;
}

static int cma_init_conn_qp(struct rdma_id_private *id_priv, struct ib_qp *qp)
{
 struct ib_qp_attr qp_attr;
 int qp_attr_mask, ret;

 qp_attr.qp_state = IB_QPS_INIT;
 ret = rdma_init_qp_attr(&id_priv->id, &qp_attr, &qp_attr_mask);
 if (ret)
  return ret;

 return ib_modify_qp(qp, &qp_attr, qp_attr_mask);
}

int rdma_create_qp(struct rdma_cm_id *id, struct ib_pd *pd,
     struct ib_qp_init_attr *qp_init_attr)
{
 struct rdma_id_private *id_priv;
 struct ib_qp *qp;
 int ret;

 id_priv = container_of(id, struct rdma_id_private, id);
 if (id->device != pd->device) {
  ret = -EINVAL;
  goto out_err;
 }

 qp_init_attr->port_num = id->port_num;
 qp = ib_create_qp(pd, qp_init_attr);
 if (IS_ERR(qp)) {
  ret = PTR_ERR(qp);
  goto out_err;
 }

 if (id->qp_type == IB_QPT_UD)
  ret = cma_init_ud_qp(id_priv, qp);
 else
  ret = cma_init_conn_qp(id_priv, qp);
 if (ret)
  goto out_destroy;

 id->qp = qp;
 id_priv->qp_num = qp->qp_num;
 id_priv->srq = (qp->srq != NULL);
 trace_cm_qp_create(id_priv, pd, qp_init_attr, 0);
 return 0;
out_destroy:
 ib_destroy_qp(qp);
out_err:
 trace_cm_qp_create(id_priv, pd, qp_init_attr, ret);
 return ret;
}
EXPORT_SYMBOL(rdma_create_qp);

void rdma_destroy_qp(struct rdma_cm_id *id)
{
 struct rdma_id_private *id_priv;

 id_priv = container_of(id, struct rdma_id_private, id);
 trace_cm_qp_destroy(id_priv);
 mutex_lock(&id_priv->qp_mutex);
 ib_destroy_qp(id_priv->id.qp);
 id_priv->id.qp = NULL;
 mutex_unlock(&id_priv->qp_mutex);
}
EXPORT_SYMBOL(rdma_destroy_qp);

static int cma_modify_qp_rtr(struct rdma_id_private *id_priv,
        struct rdma_conn_param *conn_param)
{
 struct ib_qp_attr qp_attr;
 int qp_attr_mask, ret;

 mutex_lock(&id_priv->qp_mutex);
 if (!id_priv->id.qp) {
  ret = 0;
  goto out;
 }

 /* Need to update QP attributes from default values. */
 qp_attr.qp_state = IB_QPS_INIT;
 ret = rdma_init_qp_attr(&id_priv->id, &qp_attr, &qp_attr_mask);
 if (ret)
  goto out;

 ret = ib_modify_qp(id_priv->id.qp, &qp_attr, qp_attr_mask);
 if (ret)
  goto out;

 qp_attr.qp_state = IB_QPS_RTR;
 ret = rdma_init_qp_attr(&id_priv->id, &qp_attr, &qp_attr_mask);
 if (ret)
  goto out;

 BUG_ON(id_priv->cma_dev->device != id_priv->id.device);

 if (conn_param)
  qp_attr.max_dest_rd_atomic = conn_param->responder_resources;
 ret = ib_modify_qp(id_priv->id.qp, &qp_attr, qp_attr_mask);
out:
 mutex_unlock(&id_priv->qp_mutex);
 return ret;
}

static int cma_modify_qp_rts(struct rdma_id_private *id_priv,
        struct rdma_conn_param *conn_param)
{
 struct ib_qp_attr qp_attr;
 int qp_attr_mask, ret;

 mutex_lock(&id_priv->qp_mutex);
 if (!id_priv->id.qp) {
  ret = 0;
  goto out;
 }

 qp_attr.qp_state = IB_QPS_RTS;
 ret = rdma_init_qp_attr(&id_priv->id, &qp_attr, &qp_attr_mask);
 if (ret)
  goto out;

 if (conn_param)
  qp_attr.max_rd_atomic = conn_param->initiator_depth;
 ret = ib_modify_qp(id_priv->id.qp, &qp_attr, qp_attr_mask);
out:
 mutex_unlock(&id_priv->qp_mutex);
 return ret;
}

static int cma_modify_qp_err(struct rdma_id_private *id_priv)
{
 struct ib_qp_attr qp_attr;
 int ret;

 mutex_lock(&id_priv->qp_mutex);
 if (!id_priv->id.qp) {
  ret = 0;
  goto out;
 }

 qp_attr.qp_state = IB_QPS_ERR;
 ret = ib_modify_qp(id_priv->id.qp, &qp_attr, IB_QP_STATE);
out:
 mutex_unlock(&id_priv->qp_mutex);
 return ret;
}

static int cma_ib_init_qp_attr(struct rdma_id_private *id_priv,
          struct ib_qp_attr *qp_attr, int *qp_attr_mask)
{
 struct rdma_dev_addr *dev_addr = &id_priv->id.route.addr.dev_addr;
 int ret;
 u16 pkey;

 if (rdma_cap_eth_ah(id_priv->id.device, id_priv->id.port_num))
  pkey = 0xffff;
 else
  pkey = ib_addr_get_pkey(dev_addr);

 ret = ib_find_cached_pkey(id_priv->id.device, id_priv->id.port_num,
      pkey, &qp_attr->pkey_index);
 if (ret)
  return ret;

 qp_attr->port_num = id_priv->id.port_num;
 *qp_attr_mask = IB_QP_STATE | IB_QP_PKEY_INDEX | IB_QP_PORT;

 if (id_priv->id.qp_type == IB_QPT_UD) {
  ret = cma_set_default_qkey(id_priv);
  if (ret)
   return ret;

  qp_attr->qkey = id_priv->qkey;
  *qp_attr_mask |= IB_QP_QKEY;
 } else {
  qp_attr->qp_access_flags = 0;
  *qp_attr_mask |= IB_QP_ACCESS_FLAGS;
 }
 return 0;
}

int rdma_init_qp_attr(struct rdma_cm_id *id, struct ib_qp_attr *qp_attr,
         int *qp_attr_mask)
{
 struct rdma_id_private *id_priv;
 int ret = 0;

 id_priv = container_of(id, struct rdma_id_private, id);
 if (rdma_cap_ib_cm(id->device, id->port_num)) {
  if (!id_priv->cm_id.ib || (id_priv->id.qp_type == IB_QPT_UD))
   ret = cma_ib_init_qp_attr(id_priv, qp_attr, qp_attr_mask);
  else
   ret = ib_cm_init_qp_attr(id_priv->cm_id.ib, qp_attr,
       qp_attr_mask);

  if (qp_attr->qp_state == IB_QPS_RTR)
   qp_attr->rq_psn = id_priv->seq_num;
 } else if (rdma_cap_iw_cm(id->device, id->port_num)) {
  if (!id_priv->cm_id.iw) {
   qp_attr->qp_access_flags = 0;
   *qp_attr_mask = IB_QP_STATE | IB_QP_ACCESS_FLAGS;
  } else
   ret = iw_cm_init_qp_attr(id_priv->cm_id.iw, qp_attr,
       qp_attr_mask);
  qp_attr->port_num = id_priv->id.port_num;
  *qp_attr_mask |= IB_QP_PORT;
 } else {
  ret = -ENOSYS;
 }

 if ((*qp_attr_mask & IB_QP_TIMEOUT) && id_priv->timeout_set)
  qp_attr->timeout = id_priv->timeout;

 if ((*qp_attr_mask & IB_QP_MIN_RNR_TIMER) && id_priv->min_rnr_timer_set)
  qp_attr->min_rnr_timer = id_priv->min_rnr_timer;

 return ret;
}
EXPORT_SYMBOL(rdma_init_qp_attr);

static inline bool cma_zero_addr(const struct sockaddr *addr)
{
 switch (addr->sa_family) {
 case AF_INET:
  return ipv4_is_zeronet(((struct sockaddr_in *)addr)->sin_addr.s_addr);
 case AF_INET6:
  return ipv6_addr_any(&((struct sockaddr_in6 *)addr)->sin6_addr);
 case AF_IB:
  return ib_addr_any(&((struct sockaddr_ib *)addr)->sib_addr);
 default:
  return false;
 }
}

static inline bool cma_loopback_addr(const struct sockaddr *addr)
{
 switch (addr->sa_family) {
 case AF_INET:
  return ipv4_is_loopback(
   ((struct sockaddr_in *)addr)->sin_addr.s_addr);
 case AF_INET6:
  return ipv6_addr_loopback(
   &((struct sockaddr_in6 *)addr)->sin6_addr);
 case AF_IB:
  return ib_addr_loopback(
   &((struct sockaddr_ib *)addr)->sib_addr);
 default:
  return false;
 }
}

static inline bool cma_any_addr(const struct sockaddr *addr)
{
 return cma_zero_addr(addr) || cma_loopback_addr(addr);
}

static int cma_addr_cmp(const struct sockaddr *src, const struct sockaddr *dst)
{
 if (src->sa_family != dst->sa_family)
  return -1;

 switch (src->sa_family) {
 case AF_INET:
  return ((struct sockaddr_in *)src)->sin_addr.s_addr !=
         ((struct sockaddr_in *)dst)->sin_addr.s_addr;
 case AF_INET6: {
  struct sockaddr_in6 *src_addr6 = (struct sockaddr_in6 *)src;
  struct sockaddr_in6 *dst_addr6 = (struct sockaddr_in6 *)dst;
  bool link_local;

  if (ipv6_addr_cmp(&src_addr6->sin6_addr,
       &dst_addr6->sin6_addr))
   return 1;
  link_local = ipv6_addr_type(&dst_addr6->sin6_addr) &
        IPV6_ADDR_LINKLOCAL;
  /* Link local must match their scope_ids */
  return link_local ? (src_addr6->sin6_scope_id !=
         dst_addr6->sin6_scope_id) :
        0;
 }

 default:
  return ib_addr_cmp(&((struct sockaddr_ib *) src)->sib_addr,
       &((struct sockaddr_ib *) dst)->sib_addr);
 }
}

static __be16 cma_port(const struct sockaddr *addr)
{
 struct sockaddr_ib *sib;

 switch (addr->sa_family) {
 case AF_INET:
  return ((struct sockaddr_in *) addr)->sin_port;
 case AF_INET6:
  return ((struct sockaddr_in6 *) addr)->sin6_port;
 case AF_IB:
  sib = (struct sockaddr_ib *) addr;
  return htons((u16) (be64_to_cpu(sib->sib_sid) &
        be64_to_cpu(sib->sib_sid_mask)));
 default:
  return 0;
 }
}

static inline int cma_any_port(const struct sockaddr *addr)
{
 return !cma_port(addr);
}

static void cma_save_ib_info(struct sockaddr *src_addr,
        struct sockaddr *dst_addr,
        const struct rdma_cm_id *listen_id,
        const struct sa_path_rec *path)
{
 struct sockaddr_ib *listen_ib, *ib;

 listen_ib = (struct sockaddr_ib *) &listen_id->route.addr.src_addr;
 if (src_addr) {
  ib = (struct sockaddr_ib *)src_addr;
  ib->sib_family = AF_IB;
  if (path) {
   ib->sib_pkey = path->pkey;
   ib->sib_flowinfo = path->flow_label;
   memcpy(&ib->sib_addr, &path->sgid, 16);
   ib->sib_sid = path->service_id;
   ib->sib_scope_id = 0;
  } else {
   ib->sib_pkey = listen_ib->sib_pkey;
   ib->sib_flowinfo = listen_ib->sib_flowinfo;
   ib->sib_addr = listen_ib->sib_addr;
   ib->sib_sid = listen_ib->sib_sid;
   ib->sib_scope_id = listen_ib->sib_scope_id;
  }
  ib->sib_sid_mask = cpu_to_be64(0xffffffffffffffffULL);
 }
 if (dst_addr) {
  ib = (struct sockaddr_ib *)dst_addr;
  ib->sib_family = AF_IB;
  if (path) {
   ib->sib_pkey = path->pkey;
   ib->sib_flowinfo = path->flow_label;
   memcpy(&ib->sib_addr, &path->dgid, 16);
  }
 }
}

static void cma_save_ip4_info(struct sockaddr_in *src_addr,
         struct sockaddr_in *dst_addr,
         struct cma_hdr *hdr,
         __be16 local_port)
{
 if (src_addr) {
  *src_addr = (struct sockaddr_in) {
   .sin_family = AF_INET,
   .sin_addr.s_addr = hdr->dst_addr.ip4.addr,
   .sin_port = local_port,
  };
 }

 if (dst_addr) {
  *dst_addr = (struct sockaddr_in) {
   .sin_family = AF_INET,
   .sin_addr.s_addr = hdr->src_addr.ip4.addr,
   .sin_port = hdr->port,
  };
 }
}

static void cma_save_ip6_info(struct sockaddr_in6 *src_addr,
         struct sockaddr_in6 *dst_addr,
         struct cma_hdr *hdr,
         __be16 local_port)
{
 if (src_addr) {
  *src_addr = (struct sockaddr_in6) {
   .sin6_family = AF_INET6,
   .sin6_addr = hdr->dst_addr.ip6,
   .sin6_port = local_port,
  };
 }

 if (dst_addr) {
  *dst_addr = (struct sockaddr_in6) {
   .sin6_family = AF_INET6,
   .sin6_addr = hdr->src_addr.ip6,
   .sin6_port = hdr->port,
  };
 }
}

static u16 cma_port_from_service_id(__be64 service_id)
{
 return (u16)be64_to_cpu(service_id);
}

static int cma_save_ip_info(struct sockaddr *src_addr,
       struct sockaddr *dst_addr,
       const struct ib_cm_event *ib_event,
       __be64 service_id)
{
 struct cma_hdr *hdr;
 __be16 port;

 hdr = ib_event->private_data;
 if (hdr->cma_version != CMA_VERSION)
  return -EINVAL;

 port = htons(cma_port_from_service_id(service_id));

 switch (cma_get_ip_ver(hdr)) {
 case 4:
  cma_save_ip4_info((struct sockaddr_in *)src_addr,
      (struct sockaddr_in *)dst_addr, hdr, port);
  break;
 case 6:
  cma_save_ip6_info((struct sockaddr_in6 *)src_addr,
      (struct sockaddr_in6 *)dst_addr, hdr, port);
  break;
 default:
  return -EAFNOSUPPORT;
 }

 return 0;
}

static int cma_save_net_info(struct sockaddr *src_addr,
        struct sockaddr *dst_addr,
        const struct rdma_cm_id *listen_id,
        const struct ib_cm_event *ib_event,
        sa_family_t sa_family, __be64 service_id)
{
 if (sa_family == AF_IB) {
  if (ib_event->event == IB_CM_REQ_RECEIVED)
   cma_save_ib_info(src_addr, dst_addr, listen_id,
      ib_event->param.req_rcvd.primary_path);
  else if (ib_event->event == IB_CM_SIDR_REQ_RECEIVED)
   cma_save_ib_info(src_addr, dst_addr, listen_id, NULL);
  return 0;
 }

 return cma_save_ip_info(src_addr, dst_addr, ib_event, service_id);
}

static int cma_save_req_info(const struct ib_cm_event *ib_event,
        struct cma_req_info *req)
{
 const struct ib_cm_req_event_param *req_param =
  &ib_event->param.req_rcvd;
 const struct ib_cm_sidr_req_event_param *sidr_param =
  &ib_event->param.sidr_req_rcvd;

 switch (ib_event->event) {
 case IB_CM_REQ_RECEIVED:
  req->device = req_param->listen_id->device;
  req->port = req_param->port;
  memcpy(&req->local_gid, &req_param->primary_path->sgid,
         sizeof(req->local_gid));
  req->has_gid = true;
  req->service_id = req_param->primary_path->service_id;
  req->pkey = be16_to_cpu(req_param->primary_path->pkey);
  if (req->pkey != req_param->bth_pkey)
   pr_warn_ratelimited("RDMA CMA: got different BTH P_Key (0x%x) and primary path P_Key (0x%x)\n"
         "RDMA CMA: in the future this may cause the request to be dropped\n",
         req_param->bth_pkey, req->pkey);
  break;
 case IB_CM_SIDR_REQ_RECEIVED:
  req->device = sidr_param->listen_id->device;
  req->port = sidr_param->port;
  req->has_gid = false;
  req->service_id = sidr_param->service_id;
  req->pkey = sidr_param->pkey;
  if (req->pkey != sidr_param->bth_pkey)
   pr_warn_ratelimited("RDMA CMA: got different BTH P_Key (0x%x) and SIDR request payload P_Key (0x%x)\n"
         "RDMA CMA: in the future this may cause the request to be dropped\n",
         sidr_param->bth_pkey, req->pkey);
  break;
 default:
  return -EINVAL;
 }

 return 0;
}

static bool validate_ipv4_net_dev(struct net_device *net_dev,
      const struct sockaddr_in *dst_addr,
      const struct sockaddr_in *src_addr)
{
 __be32 daddr = dst_addr->sin_addr.s_addr,
        saddr = src_addr->sin_addr.s_addr;
 struct fib_result res;
 struct flowi4 fl4;
 int err;
 bool ret;

 if (ipv4_is_multicast(saddr) || ipv4_is_lbcast(saddr) ||
     ipv4_is_lbcast(daddr) || ipv4_is_zeronet(saddr) ||
     ipv4_is_zeronet(daddr) || ipv4_is_loopback(daddr) ||
     ipv4_is_loopback(saddr))
  return false;

 memset(&fl4, 0, sizeof(fl4));
 fl4.flowi4_oif = net_dev->ifindex;
 fl4.daddr = daddr;
 fl4.saddr = saddr;

 rcu_read_lock();
 err = fib_lookup(dev_net(net_dev), &fl4, &res, 0);
 ret = err == 0 && FIB_RES_DEV(res) == net_dev;
 rcu_read_unlock();

 return ret;
}

static bool validate_ipv6_net_dev(struct net_device *net_dev,
      const struct sockaddr_in6 *dst_addr,
      const struct sockaddr_in6 *src_addr)
{
#if IS_ENABLED(CONFIG_IPV6)
 const int strict = ipv6_addr_type(&dst_addr->sin6_addr) &
      IPV6_ADDR_LINKLOCAL;
 struct rt6_info *rt = rt6_lookup(dev_net(net_dev), &dst_addr->sin6_addr,
      &src_addr->sin6_addr, net_dev->ifindex,
      NULL, strict);
 bool ret;

 if (!rt)
  return false;

 ret = rt->rt6i_idev->dev == net_dev;
 ip6_rt_put(rt);

 return ret;
#else
 return false;
#endif
}

static bool validate_net_dev(struct net_device *net_dev,
        const struct sockaddr *daddr,
        const struct sockaddr *saddr)
{
 const struct sockaddr_in *daddr4 = (const struct sockaddr_in *)daddr;
 const struct sockaddr_in *saddr4 = (const struct sockaddr_in *)saddr;
 const struct sockaddr_in6 *daddr6 = (const struct sockaddr_in6 *)daddr;
 const struct sockaddr_in6 *saddr6 = (const struct sockaddr_in6 *)saddr;

 switch (daddr->sa_family) {
 case AF_INET:
  return saddr->sa_family == AF_INET &&
         validate_ipv4_net_dev(net_dev, daddr4, saddr4);

 case AF_INET6:
  return saddr->sa_family == AF_INET6 &&
         validate_ipv6_net_dev(net_dev, daddr6, saddr6);

 default:
  return false;
 }
}

static struct net_device *
roce_get_net_dev_by_cm_event(const struct ib_cm_event *ib_event)
{
 const struct ib_gid_attr *sgid_attr = NULL;
 struct net_device *ndev;

 if (ib_event->event == IB_CM_REQ_RECEIVED)
  sgid_attr = ib_event->param.req_rcvd.ppath_sgid_attr;
 else if (ib_event->event == IB_CM_SIDR_REQ_RECEIVED)
  sgid_attr = ib_event->param.sidr_req_rcvd.sgid_attr;

 if (!sgid_attr)
  return NULL;

 rcu_read_lock();
 ndev = rdma_read_gid_attr_ndev_rcu(sgid_attr);
 if (IS_ERR(ndev))
  ndev = NULL;
 else
  dev_hold(ndev);
 rcu_read_unlock();
 return ndev;
}

static struct net_device *cma_get_net_dev(const struct ib_cm_event *ib_event,
       struct cma_req_info *req)
{
 struct sockaddr *listen_addr =
   (struct sockaddr *)&req->listen_addr_storage;
 struct sockaddr *src_addr = (struct sockaddr *)&req->src_addr_storage;
 struct net_device *net_dev;
 const union ib_gid *gid = req->has_gid ? &req->local_gid : NULL;
 int err;

 err = cma_save_ip_info(listen_addr, src_addr, ib_event,
          req->service_id);
 if (err)
  return ERR_PTR(err);

 if (rdma_protocol_roce(req->device, req->port))
  net_dev = roce_get_net_dev_by_cm_event(ib_event);
 else
  net_dev = ib_get_net_dev_by_params(req->device, req->port,
         req->pkey,
         gid, listen_addr);
 if (!net_dev)
  return ERR_PTR(-ENODEV);

 return net_dev;
}

static enum rdma_ucm_port_space rdma_ps_from_service_id(__be64 service_id)
{
 return (be64_to_cpu(service_id) >> 16) & 0xffff;
}

static bool cma_match_private_data(struct rdma_id_private *id_priv,
       const struct cma_hdr *hdr)
{
 struct sockaddr *addr = cma_src_addr(id_priv);
 __be32 ip4_addr;
 struct in6_addr ip6_addr;

 if (cma_any_addr(addr) && !id_priv->afonly)
  return true;

 switch (addr->sa_family) {
 case AF_INET:
  ip4_addr = ((struct sockaddr_in *)addr)->sin_addr.s_addr;
  if (cma_get_ip_ver(hdr) != 4)
   return false;
  if (!cma_any_addr(addr) &&
      hdr->dst_addr.ip4.addr != ip4_addr)
   return false;
  break;
 case AF_INET6:
  ip6_addr = ((struct sockaddr_in6 *)addr)->sin6_addr;
  if (cma_get_ip_ver(hdr) != 6)
   return false;
  if (!cma_any_addr(addr) &&
      memcmp(&hdr->dst_addr.ip6, &ip6_addr, sizeof(ip6_addr)))
   return false;
  break;
 case AF_IB:
  return true;
 default:
  return false;
 }

 return true;
}

static bool cma_protocol_roce(const struct rdma_cm_id *id)
{
 struct ib_device *device = id->device;
 const u32 port_num = id->port_num ?: rdma_start_port(device);

 return rdma_protocol_roce(device, port_num);
}

static bool cma_is_req_ipv6_ll(const struct cma_req_info *req)
{
 const struct sockaddr *daddr =
   (const struct sockaddr *)&req->listen_addr_storage;
 const struct sockaddr_in6 *daddr6 = (const struct sockaddr_in6 *)daddr;

 /* Returns true if the req is for IPv6 link local */
 return (daddr->sa_family == AF_INET6 &&
  (ipv6_addr_type(&daddr6->sin6_addr) & IPV6_ADDR_LINKLOCAL));
}

static bool cma_match_net_dev(const struct rdma_cm_id *id,
         const struct net_device *net_dev,
         const struct cma_req_info *req)
{
 const struct rdma_addr *addr = &id->route.addr;

 if (!net_dev)
  /* This request is an AF_IB request */
  return (!id->port_num || id->port_num == req->port) &&
         (addr->src_addr.ss_family == AF_IB);

 /*
 * If the request is not for IPv6 link local, allow matching
 * request to any netdevice of the one or multiport rdma device.
 */

 if (!cma_is_req_ipv6_ll(req))
  return true;
 /*
 * Net namespaces must match, and if the listner is listening
 * on a specific netdevice than netdevice must match as well.
 */

 if (net_eq(dev_net(net_dev), addr->dev_addr.net) &&
     (!!addr->dev_addr.bound_dev_if ==
      (addr->dev_addr.bound_dev_if == net_dev->ifindex)))
  return true;
 else
  return false;
}

static struct rdma_id_private *cma_find_listener(
  const struct rdma_bind_list *bind_list,
  const struct ib_cm_id *cm_id,
  const struct ib_cm_event *ib_event,
  const struct cma_req_info *req,
  const struct net_device *net_dev)
{
 struct rdma_id_private *id_priv, *id_priv_dev;

 lockdep_assert_held(&lock);

 if (!bind_list)
  return ERR_PTR(-EINVAL);

 hlist_for_each_entry(id_priv, &bind_list->owners, node) {
  if (cma_match_private_data(id_priv, ib_event->private_data)) {
   if (id_priv->id.device == cm_id->device &&
       cma_match_net_dev(&id_priv->id, net_dev, req))
    return id_priv;
   list_for_each_entry(id_priv_dev,
         &id_priv->listen_list,
         listen_item) {
    if (id_priv_dev->id.device == cm_id->device &&
        cma_match_net_dev(&id_priv_dev->id,
            net_dev, req))
     return id_priv_dev;
   }
  }
 }

 return ERR_PTR(-EINVAL);
}

static struct rdma_id_private *
cma_ib_id_from_event(struct ib_cm_id *cm_id,
       const struct ib_cm_event *ib_event,
       struct cma_req_info *req,
       struct net_device **net_dev)
{
 struct rdma_bind_list *bind_list;
 struct rdma_id_private *id_priv;
 int err;

 err = cma_save_req_info(ib_event, req);
 if (err)
  return ERR_PTR(err);

 *net_dev = cma_get_net_dev(ib_event, req);
 if (IS_ERR(*net_dev)) {
  if (PTR_ERR(*net_dev) == -EAFNOSUPPORT) {
   /* Assuming the protocol is AF_IB */
   *net_dev = NULL;
  } else {
   return ERR_CAST(*net_dev);
  }
 }

 mutex_lock(&lock);
 /*
 * Net namespace might be getting deleted while route lookup,
 * cm_id lookup is in progress. Therefore, perform netdevice
 * validation, cm_id lookup under rcu lock.
 * RCU lock along with netdevice state check, synchronizes with
 * netdevice migrating to different net namespace and also avoids
 * case where net namespace doesn't get deleted while lookup is in
 * progress.
 * If the device state is not IFF_UP, its properties such as ifindex
 * and nd_net cannot be trusted to remain valid without rcu lock.
 * net/core/dev.c change_net_namespace() ensures to synchronize with
 * ongoing operations on net device after device is closed using
 * synchronize_net().
 */

 rcu_read_lock();
 if (*net_dev) {
  /*
 * If netdevice is down, it is likely that it is administratively
 * down or it might be migrating to different namespace.
 * In that case avoid further processing, as the net namespace
 * or ifindex may change.
 */

  if (((*net_dev)->flags & IFF_UP) == 0) {
   id_priv = ERR_PTR(-EHOSTUNREACH);
   goto err;
  }

  if (!validate_net_dev(*net_dev,
     (struct sockaddr *)&req->src_addr_storage,
     (struct sockaddr *)&req->listen_addr_storage)) {
   id_priv = ERR_PTR(-EHOSTUNREACH);
   goto err;
  }
 }

 bind_list = cma_ps_find(*net_dev ? dev_net(*net_dev) : &init_net,
    rdma_ps_from_service_id(req->service_id),
    cma_port_from_service_id(req->service_id));
 id_priv = cma_find_listener(bind_list, cm_id, ib_event, req, *net_dev);
err:
 rcu_read_unlock();
 mutex_unlock(&lock);
 if (IS_ERR(id_priv) && *net_dev) {
  dev_put(*net_dev);
  *net_dev = NULL;
 }
 return id_priv;
}

static inline u8 cma_user_data_offset(struct rdma_id_private *id_priv)
{
 return cma_family(id_priv) == AF_IB ? 0 : sizeof(struct cma_hdr);
}

static void cma_cancel_route(struct rdma_id_private *id_priv)
{
 if (rdma_cap_ib_sa(id_priv->id.device, id_priv->id.port_num)) {
  if (id_priv->query)
   ib_sa_cancel_query(id_priv->query_id, id_priv->query);
 }
}

static void _cma_cancel_listens(struct rdma_id_private *id_priv)
{
 struct rdma_id_private *dev_id_priv;

 lockdep_assert_held(&lock);

 /*
 * Remove from listen_any_list to prevent added devices from spawning
 * additional listen requests.
 */

 list_del_init(&id_priv->listen_any_item);

 while (!list_empty(&id_priv->listen_list)) {
  dev_id_priv =
   list_first_entry(&id_priv->listen_list,
      struct rdma_id_private, listen_item);
  /* sync with device removal to avoid duplicate destruction */
  list_del_init(&dev_id_priv->device_item);
  list_del_init(&dev_id_priv->listen_item);
  mutex_unlock(&lock);

  rdma_destroy_id(&dev_id_priv->id);
  mutex_lock(&lock);
 }
}

static void cma_cancel_listens(struct rdma_id_private *id_priv)
{
 mutex_lock(&lock);
 _cma_cancel_listens(id_priv);
 mutex_unlock(&lock);
}

static void cma_cancel_operation(struct rdma_id_private *id_priv,
     enum rdma_cm_state state)
{
 switch (state) {
 case RDMA_CM_ADDR_QUERY:
  /*
 * We can avoid doing the rdma_addr_cancel() based on state,
 * only RDMA_CM_ADDR_QUERY has a work that could still execute.
 * Notice that the addr_handler work could still be exiting
 * outside this state, however due to the interaction with the
 * handler_mutex the work is guaranteed not to touch id_priv
 * during exit.
 */

  rdma_addr_cancel(&id_priv->id.route.addr.dev_addr);
  break;
 case RDMA_CM_ROUTE_QUERY:
  cma_cancel_route(id_priv);
  break;
 case RDMA_CM_LISTEN:
  if (cma_any_addr(cma_src_addr(id_priv)) && !id_priv->cma_dev)
   cma_cancel_listens(id_priv);
  break;
 default:
  break;
 }
}

static void cma_release_port(struct rdma_id_private *id_priv)
{
 struct rdma_bind_list *bind_list = id_priv->bind_list;
 struct net *net = id_priv->id.route.addr.dev_addr.net;

 if (!bind_list)
  return;

 mutex_lock(&lock);
 hlist_del(&id_priv->node);
 if (hlist_empty(&bind_list->owners)) {
  cma_ps_remove(net, bind_list->ps, bind_list->port);
  kfree(bind_list);
 }
 mutex_unlock(&lock);
}

static void destroy_mc(struct rdma_id_private *id_priv,
         struct cma_multicast *mc)
{
 bool send_only = mc->join_state == BIT(SENDONLY_FULLMEMBER_JOIN);

 if (rdma_cap_ib_mcast(id_priv->id.device, id_priv->id.port_num))
  ib_sa_free_multicast(mc->sa_mc);

 if (rdma_protocol_roce(id_priv->id.device, id_priv->id.port_num)) {
  struct rdma_dev_addr *dev_addr =
   &id_priv->id.route.addr.dev_addr;
  struct net_device *ndev = NULL;

  if (dev_addr->bound_dev_if)
   ndev = dev_get_by_index(dev_addr->net,
      dev_addr->bound_dev_if);
  if (ndev && !send_only) {
   enum ib_gid_type gid_type;
   union ib_gid mgid;

   gid_type = id_priv->cma_dev->default_gid_type
        [id_priv->id.port_num -
         rdma_start_port(
          id_priv->cma_dev->device)];
   cma_iboe_set_mgid((struct sockaddr *)&mc->addr, &mgid,
       gid_type);
   cma_igmp_send(ndev, &mgid, false);
  }
  dev_put(ndev);

  cancel_work_sync(&mc->iboe_join.work);
 }
 kfree(mc);
}

static void cma_leave_mc_groups(struct rdma_id_private *id_priv)
{
 struct cma_multicast *mc;

 while (!list_empty(&id_priv->mc_list)) {
  mc = list_first_entry(&id_priv->mc_list, struct cma_multicast,
          list);
  list_del(&mc->list);
  destroy_mc(id_priv, mc);
 }
}

static void _destroy_id(struct rdma_id_private *id_priv,
   enum rdma_cm_state state)
{
 cma_cancel_operation(id_priv, state);

 rdma_restrack_del(&id_priv->res);
 cma_remove_id_from_tree(id_priv);
 if (id_priv->cma_dev) {
  if (rdma_cap_ib_cm(id_priv->id.device, 1)) {
   if (id_priv->cm_id.ib)
    ib_destroy_cm_id(id_priv->cm_id.ib);
  } else if (rdma_cap_iw_cm(id_priv->id.device, 1)) {
   if (id_priv->cm_id.iw)
    iw_destroy_cm_id(id_priv->cm_id.iw);
  }
  cma_leave_mc_groups(id_priv);
  cma_release_dev(id_priv);
 }

 cma_release_port(id_priv);
 cma_id_put(id_priv);
 wait_for_completion(&id_priv->comp);

 if (id_priv->internal_id)
  cma_id_put(id_priv->id.context);

 kfree(id_priv->id.route.path_rec);
 kfree(id_priv->id.route.path_rec_inbound);
 kfree(id_priv->id.route.path_rec_outbound);

 put_net(id_priv->id.route.addr.dev_addr.net);
 kfree(id_priv);
}

/*
 * destroy an ID from within the handler_mutex. This ensures that no other
 * handlers can start running concurrently.
 */

static void destroy_id_handler_unlock(struct rdma_id_private *id_priv)
 __releases(&idprv->handler_mutex)
{
 enum rdma_cm_state state;
 unsigned long flags;

 trace_cm_id_destroy(id_priv);

 /*
 * Setting the state to destroyed under the handler mutex provides a
 * fence against calling handler callbacks. If this is invoked due to
 * the failure of a handler callback then it guarentees that no future
 * handlers will be called.
 */

 lockdep_assert_held(&id_priv->handler_mutex);
 spin_lock_irqsave(&id_priv->lock, flags);
 state = id_priv->state;
 id_priv->state = RDMA_CM_DESTROYING;
 spin_unlock_irqrestore(&id_priv->lock, flags);
 mutex_unlock(&id_priv->handler_mutex);
 _destroy_id(id_priv, state);
}

void rdma_destroy_id(struct rdma_cm_id *id)
{
 struct rdma_id_private *id_priv =
  container_of(id, struct rdma_id_private, id);

 mutex_lock(&id_priv->handler_mutex);
 destroy_id_handler_unlock(id_priv);
}
EXPORT_SYMBOL(rdma_destroy_id);

static int cma_rep_recv(struct rdma_id_private *id_priv)
{
 int ret;

 ret = cma_modify_qp_rtr(id_priv, NULL);
 if (ret)
  goto reject;

 ret = cma_modify_qp_rts(id_priv, NULL);
 if (ret)
  goto reject;

 trace_cm_send_rtu(id_priv);
 ret = ib_send_cm_rtu(id_priv->cm_id.ib, NULL, 0);
 if (ret)
  goto reject;

 return 0;
reject:
 pr_debug_ratelimited("RDMA CM: CONNECT_ERROR: failed to handle reply. status %d\n", ret);
 cma_modify_qp_err(id_priv);
 trace_cm_send_rej(id_priv);
 ib_send_cm_rej(id_priv->cm_id.ib, IB_CM_REJ_CONSUMER_DEFINED,
         NULL, 0, NULL, 0);
 return ret;
}

static void cma_set_rep_event_data(struct rdma_cm_event *event,
       const struct ib_cm_rep_event_param *rep_data,
       void *private_data)
{
 event->param.conn.private_data = private_data;
 event->param.conn.private_data_len = IB_CM_REP_PRIVATE_DATA_SIZE;
 event->param.conn.responder_resources = rep_data->responder_resources;
 event->param.conn.initiator_depth = rep_data->initiator_depth;
 event->param.conn.flow_control = rep_data->flow_control;
 event->param.conn.rnr_retry_count = rep_data->rnr_retry_count;
 event->param.conn.srq = rep_data->srq;
 event->param.conn.qp_num = rep_data->remote_qpn;

 event->ece.vendor_id = rep_data->ece.vendor_id;
 event->ece.attr_mod = rep_data->ece.attr_mod;
}

static int cma_cm_event_handler(struct rdma_id_private *id_priv,
    struct rdma_cm_event *event)
{
 int ret;

 lockdep_assert_held(&id_priv->handler_mutex);

 trace_cm_event_handler(id_priv, event);
 ret = id_priv->id.event_handler(&id_priv->id, event);
 trace_cm_event_done(id_priv, event, ret);
 return ret;
}

static int cma_ib_handler(struct ib_cm_id *cm_id,
     const struct ib_cm_event *ib_event)
{
 struct rdma_id_private *id_priv = cm_id->context;
 struct rdma_cm_event event = {};
 enum rdma_cm_state state;
 int ret;

 mutex_lock(&id_priv->handler_mutex);
 state = READ_ONCE(id_priv->state);
 if ((ib_event->event != IB_CM_TIMEWAIT_EXIT &&
      state != RDMA_CM_CONNECT) ||
     (ib_event->event == IB_CM_TIMEWAIT_EXIT &&
      state != RDMA_CM_DISCONNECT))
  goto out;

 switch (ib_event->event) {
 case IB_CM_REQ_ERROR:
 case IB_CM_REP_ERROR:
  event.event = RDMA_CM_EVENT_UNREACHABLE;
  event.status = -ETIMEDOUT;
  break;
 case IB_CM_REP_RECEIVED:
  if (state == RDMA_CM_CONNECT &&
      (id_priv->id.qp_type != IB_QPT_UD)) {
   trace_cm_prepare_mra(id_priv);
   ib_prepare_cm_mra(cm_id);
  }
  if (id_priv->id.qp) {
   event.status = cma_rep_recv(id_priv);
   event.event = event.status ? RDMA_CM_EVENT_CONNECT_ERROR :
           RDMA_CM_EVENT_ESTABLISHED;
  } else {
   event.event = RDMA_CM_EVENT_CONNECT_RESPONSE;
  }
  cma_set_rep_event_data(&event, &ib_event->param.rep_rcvd,
           ib_event->private_data);
  break;
 case IB_CM_RTU_RECEIVED:
 case IB_CM_USER_ESTABLISHED:
  event.event = RDMA_CM_EVENT_ESTABLISHED;
  break;
 case IB_CM_DREQ_ERROR:
  event.status = -ETIMEDOUT;
  fallthrough;
 case IB_CM_DREQ_RECEIVED:
 case IB_CM_DREP_RECEIVED:
  if (!cma_comp_exch(id_priv, RDMA_CM_CONNECT,
       RDMA_CM_DISCONNECT))
   goto out;
  event.event = RDMA_CM_EVENT_DISCONNECTED;
  break;
 case IB_CM_TIMEWAIT_EXIT:
  event.event = RDMA_CM_EVENT_TIMEWAIT_EXIT;
  break;
 case IB_CM_MRA_RECEIVED:
  /* ignore event */
  goto out;
 case IB_CM_REJ_RECEIVED:
  pr_debug_ratelimited("RDMA CM: REJECTED: %s\n", rdma_reject_msg(&id_priv->id,
          ib_event->param.rej_rcvd.reason));
  cma_modify_qp_err(id_priv);
  event.status = ib_event->param.rej_rcvd.reason;
  event.event = RDMA_CM_EVENT_REJECTED;
  event.param.conn.private_data = ib_event->private_data;
  event.param.conn.private_data_len = IB_CM_REJ_PRIVATE_DATA_SIZE;
  break;
 default:
  pr_err("RDMA CMA: unexpected IB CM event: %d\n",
         ib_event->event);
  goto out;
 }

 ret = cma_cm_event_handler(id_priv, &event);
 if (ret) {
  /* Destroy the CM ID by returning a non-zero value. */
  id_priv->cm_id.ib = NULL;
  destroy_id_handler_unlock(id_priv);
  return ret;
 }
out:
 mutex_unlock(&id_priv->handler_mutex);
 return 0;
}

static struct rdma_id_private *
cma_ib_new_conn_id(const struct rdma_cm_id *listen_id,
     const struct ib_cm_event *ib_event,
     struct net_device *net_dev)
{
 struct rdma_id_private *listen_id_priv;
 struct rdma_id_private *id_priv;
 struct rdma_cm_id *id;
 struct rdma_route *rt;
 const sa_family_t ss_family = listen_id->route.addr.src_addr.ss_family;
 struct sa_path_rec *path = ib_event->param.req_rcvd.primary_path;
 const __be64 service_id =
  ib_event->param.req_rcvd.primary_path->service_id;
 int ret;

 listen_id_priv = container_of(listen_id, struct rdma_id_private, id);
 id_priv = __rdma_create_id(listen_id->route.addr.dev_addr.net,
       listen_id->event_handler, listen_id->context,
       listen_id->ps,
       ib_event->param.req_rcvd.qp_type,
       listen_id_priv);
 if (IS_ERR(id_priv))
  return NULL;

 id = &id_priv->id;
 if (cma_save_net_info((struct sockaddr *)&id->route.addr.src_addr,
         (struct sockaddr *)&id->route.addr.dst_addr,
         listen_id, ib_event, ss_family, service_id))
  goto err;

 rt = &id->route;
 rt->num_pri_alt_paths = ib_event->param.req_rcvd.alternate_path ? 2 : 1;
 rt->path_rec = kmalloc_array(rt->num_pri_alt_paths,
         sizeof(*rt->path_rec), GFP_KERNEL);
 if (!rt->path_rec)
  goto err;

 rt->path_rec[0] = *path;
 if (rt->num_pri_alt_paths == 2)
  rt->path_rec[1] = *ib_event->param.req_rcvd.alternate_path;

 if (net_dev) {
  rdma_copy_src_l2_addr(&rt->addr.dev_addr, net_dev);
 } else {
  if (!cma_protocol_roce(listen_id) &&
      cma_any_addr(cma_src_addr(id_priv))) {
   rt->addr.dev_addr.dev_type = ARPHRD_INFINIBAND;
   rdma_addr_set_sgid(&rt->addr.dev_addr, &rt->path_rec[0].sgid);
   ib_addr_set_pkey(&rt->addr.dev_addr, be16_to_cpu(rt->path_rec[0].pkey));
  } else if (!cma_any_addr(cma_src_addr(id_priv))) {
   ret = cma_translate_addr(cma_src_addr(id_priv), &rt->addr.dev_addr);
   if (ret)
    goto err;
  }
 }
 rdma_addr_set_dgid(&rt->addr.dev_addr, &rt->path_rec[0].dgid);

 id_priv->state = RDMA_CM_CONNECT;
 return id_priv;

err:
 rdma_destroy_id(id);
 return NULL;
}

static struct rdma_id_private *
cma_ib_new_udp_id(const struct rdma_cm_id *listen_id,
    const struct ib_cm_event *ib_event,
    struct net_device *net_dev)
{
 const struct rdma_id_private *listen_id_priv;
 struct rdma_id_private *id_priv;
 struct rdma_cm_id *id;
 const sa_family_t ss_family = listen_id->route.addr.src_addr.ss_family;
 struct net *net = listen_id->route.addr.dev_addr.net;
 int ret;

 listen_id_priv = container_of(listen_id, struct rdma_id_private, id);
 id_priv = __rdma_create_id(net, listen_id->event_handler,
       listen_id->context, listen_id->ps, IB_QPT_UD,
       listen_id_priv);
 if (IS_ERR(id_priv))
  return NULL;

 id = &id_priv->id;
 if (cma_save_net_info((struct sockaddr *)&id->route.addr.src_addr,
         (struct sockaddr *)&id->route.addr.dst_addr,
         listen_id, ib_event, ss_family,
         ib_event->param.sidr_req_rcvd.service_id))
  goto err;

 if (net_dev) {
  rdma_copy_src_l2_addr(&id->route.addr.dev_addr, net_dev);
 } else {
  if (!cma_any_addr(cma_src_addr(id_priv))) {
   ret = cma_translate_addr(cma_src_addr(id_priv),
       &id->route.addr.dev_addr);
   if (ret)
    goto err;
  }
 }

 id_priv->state = RDMA_CM_CONNECT;
 return id_priv;
err:
 rdma_destroy_id(id);
 return NULL;
}

static void cma_set_req_event_data(struct rdma_cm_event *event,
       const struct ib_cm_req_event_param *req_data,
       void *private_data, int offset)
{
 event->param.conn.private_data = private_data + offset;
 event->param.conn.private_data_len = IB_CM_REQ_PRIVATE_DATA_SIZE - offset;
 event->param.conn.responder_resources = req_data->responder_resources;
 event->param.conn.initiator_depth = req_data->initiator_depth;
 event->param.conn.flow_control = req_data->flow_control;
 event->param.conn.retry_count = req_data->retry_count;
 event->param.conn.rnr_retry_count = req_data->rnr_retry_count;
 event->param.conn.srq = req_data->srq;
 event->param.conn.qp_num = req_data->remote_qpn;

 event->ece.vendor_id = req_data->ece.vendor_id;
 event->ece.attr_mod = req_data->ece.attr_mod;
}

static int cma_ib_check_req_qp_type(const struct rdma_cm_id *id,
        const struct ib_cm_event *ib_event)
{
 return (((ib_event->event == IB_CM_REQ_RECEIVED) &&
   (ib_event->param.req_rcvd.qp_type == id->qp_type)) ||
  ((ib_event->event == IB_CM_SIDR_REQ_RECEIVED) &&
   (id->qp_type == IB_QPT_UD)) ||
  (!id->qp_type));
}

static int cma_ib_req_handler(struct ib_cm_id *cm_id,
         const struct ib_cm_event *ib_event)
{
 struct rdma_id_private *listen_id, *conn_id = NULL;
 struct rdma_cm_event event = {};
 struct cma_req_info req = {};
 struct net_device *net_dev;
 u8 offset;
 int ret;

 listen_id = cma_ib_id_from_event(cm_id, ib_event, &req, &net_dev);
 if (IS_ERR(listen_id))
  return PTR_ERR(listen_id);

 trace_cm_req_handler(listen_id, ib_event->event);
 if (!cma_ib_check_req_qp_type(&listen_id->id, ib_event)) {
  ret = -EINVAL;
  goto net_dev_put;
 }

 mutex_lock(&listen_id->handler_mutex);
 if (READ_ONCE(listen_id->state) != RDMA_CM_LISTEN) {
  ret = -ECONNABORTED;
  goto err_unlock;
 }

 offset = cma_user_data_offset(listen_id);
 event.event = RDMA_CM_EVENT_CONNECT_REQUEST;
 if (ib_event->event == IB_CM_SIDR_REQ_RECEIVED) {
  conn_id = cma_ib_new_udp_id(&listen_id->id, ib_event, net_dev);
  event.param.ud.private_data = ib_event->private_data + offset;
  event.param.ud.private_data_len =
    IB_CM_SIDR_REQ_PRIVATE_DATA_SIZE - offset;
 } else {
  conn_id = cma_ib_new_conn_id(&listen_id->id, ib_event, net_dev);
  cma_set_req_event_data(&event, &ib_event->param.req_rcvd,
           ib_event->private_data, offset);
 }
 if (!conn_id) {
  ret = -ENOMEM;
  goto err_unlock;
 }

--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=97 H=91 G=93

¤ Dauer der Verarbeitung: 0.24 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.