提交 aa9d4648 编写于 作者: L Linus Torvalds

Merge tag 'for-linus-ioctl' of git://git.kernel.org/pub/scm/linux/kernel/git/dledford/rdma

Pull rdma updates from Doug Ledford:
 "This is a big pull request.

  Of note is that I'm sending you the new ioctl API for the rdma
  subsystem. We put it up on linux-api@, but didn't get much response.
  The API is complex, but it solves two different problems in one go:

   1) The bi-directional nature of the RDMA file write calls, which
      created the security hole we had to handle (and for which the fix
      is now causing problems for systems in production, we were a bit
      over zealous in the fix and the ability to open a device, then
      fork, then create new queue pairs on the device and use them is
      broken).

   2) The bloat caused by different vendors implementing extensions to
      the base verbs API. Each vendor's hardware is slightly different,
      and the hardware might be suitable for one extension but not
      another.

      By the time we add generic extensions for all the different ways
      that the different hardware can offload things, the API becomes
      bloated. Things like our completion structs have started to exceed
      a cache line in size because of all the elements needed to support
      this. That in turn shows up heavily in the performance graphs with
      a noticable drop in performance on 100Gigabit links as our
      completion structs go from occupying one cache line to 1+.

      This API makes things like the completion structs modular in a
      very similar way to netlink so that your structs can only include
      the items needed for the offloads/features you are actually using
      on a given queue pair. In that way we support everything, but only
      use what we need, and our structs stay smaller.

  The ioctl API is better explained by the posting on linux-api@ than I
  can explain it here, so I'll just leave it at that.

  The rest of the pull request is typical stuff.

  Updates for 4.14 kernel merge window

   - Lots of hfi1 driver updates (mixed with a few qib and core updates
     as well)

   - rxe updates

   - various mlx updates

   - Set default roce type to RoCEv2

   - Several larger fixes for bnxt_re that were too big for -rc

   - Several larger fixes for qedr that, likewise, were too big for -rc

   - Misc core changes

   - Make the hns_roce driver compilable on arches other than aarch64 so
     we can more easily debug build issues related to it

   - Add rdma-netlink infrastructure updates

   - Add automatic IRQ affinity infrastructure

   - Add 32bit lid support

   - Lots of misc fixes across the subsystem from random people

   - Autoloading of RDMA netlink modules

   - PCI pool cleanups from Romain Perier

   - mlx5 driver feature additions and fixes

   - Hardware tag matchine feature

   - Fix sleeping in atomic when resolving roce ah

   - Add experimental ioctl interface as posted to linux-api@"

* tag 'for-linus-ioctl' of git://git.kernel.org/pub/scm/linux/kernel/git/dledford/rdma: (328 commits)
  IB/core: Expose ioctl interface through experimental Kconfig
  IB/core: Assign root to all drivers
  IB/core: Add completion queue (cq) object actions
  IB/core: Add legacy driver's user-data
  IB/core: Export ioctl enum types to user-space
  IB/core: Explicitly destroy an object while keeping uobject
  IB/core: Add macros for declaring methods and attributes
  IB/core: Add uverbs merge trees functionality
  IB/core: Add DEVICE object and root tree structure
  IB/core: Declare an object instead of declaring only type attributes
  IB/core: Add new ioctl interface
  RDMA/vmw_pvrdma: Fix a signedness
  RDMA/vmw_pvrdma: Report network header type in WC
  IB/core: Add might_sleep() annotation to ib_init_ah_from_wc()
  IB/cm: Fix sleeping in atomic when RoCE is used
  IB/core: Add support to finalize objects in one transaction
  IB/core: Add a generic way to execute an operation on a uobject
  Documentation: Hardware tag matching
  IB/mlx5: Support IB_SRQT_TM
  net/mlx5: Add XRQ support
  ...
Tag matching logic
The MPI standard defines a set of rules, known as tag-matching, for matching
source send operations to destination receives. The following parameters must
match the following source and destination parameters:
* Communicator
* User tag - wild card may be specified by the receiver
* Source rank – wild car may be specified by the receiver
* Destination rank – wild
The ordering rules require that when more than one pair of send and receive
message envelopes may match, the pair that includes the earliest posted-send
and the earliest posted-receive is the pair that must be used to satisfy the
matching operation. However, this doesn’t imply that tags are consumed in
the order they are created, e.g., a later generated tag may be consumed, if
earlier tags can’t be used to satisfy the matching rules.
When a message is sent from the sender to the receiver, the communication
library may attempt to process the operation either after or before the
corresponding matching receive is posted. If a matching receive is posted,
this is an expected message, otherwise it is called an unexpected message.
Implementations frequently use different matching schemes for these two
different matching instances.
To keep MPI library memory footprint down, MPI implementations typically use
two different protocols for this purpose:
1. The Eager protocol- the complete message is sent when the send is
processed by the sender. A completion send is received in the send_cq
notifying that the buffer can be reused.
2. The Rendezvous Protocol - the sender sends the tag-matching header,
and perhaps a portion of data when first notifying the receiver. When the
corresponding buffer is posted, the responder will use the information from
the header to initiate an RDMA READ operation directly to the matching buffer.
A fin message needs to be received in order for the buffer to be reused.
Tag matching implementation
There are two types of matching objects used, the posted receive list and the
unexpected message list. The application posts receive buffers through calls
to the MPI receive routines in the posted receive list and posts send messages
using the MPI send routines. The head of the posted receive list may be
maintained by the hardware, with the software expected to shadow this list.
When send is initiated and arrives at the receive side, if there is no
pre-posted receive for this arriving message, it is passed to the software and
placed in the unexpected message list. Otherwise the match is processed,
including rendezvous processing, if appropriate, delivering the data to the
specified receive buffer. This allows overlapping receive-side MPI tag
matching with computation.
When a receive-message is posted, the communication library will first check
the software unexpected message list for a matching receive. If a match is
found, data is delivered to the user buffer, using a software controlled
protocol. The UCX implementation uses either an eager or rendezvous protocol,
depending on data size. If no match is found, the entire pre-posted receive
list is maintained by the hardware, and there is space to add one more
pre-posted receive to this list, this receive is passed to the hardware.
Software is expected to shadow this list, to help with processing MPI cancel
operations. In addition, because hardware and software are not expected to be
tightly synchronized with respect to the tag-matching operation, this shadow
list is used to detect the case that a pre-posted receive is passed to the
hardware, as the matching unexpected message is being passed from the hardware
to the software.
...@@ -206,4 +206,9 @@ config BLK_MQ_VIRTIO ...@@ -206,4 +206,9 @@ config BLK_MQ_VIRTIO
depends on BLOCK && VIRTIO depends on BLOCK && VIRTIO
default y default y
config BLK_MQ_RDMA
bool
depends on BLOCK && INFINIBAND
default y
source block/Kconfig.iosched source block/Kconfig.iosched
...@@ -29,6 +29,7 @@ obj-$(CONFIG_BLK_CMDLINE_PARSER) += cmdline-parser.o ...@@ -29,6 +29,7 @@ obj-$(CONFIG_BLK_CMDLINE_PARSER) += cmdline-parser.o
obj-$(CONFIG_BLK_DEV_INTEGRITY) += bio-integrity.o blk-integrity.o t10-pi.o obj-$(CONFIG_BLK_DEV_INTEGRITY) += bio-integrity.o blk-integrity.o t10-pi.o
obj-$(CONFIG_BLK_MQ_PCI) += blk-mq-pci.o obj-$(CONFIG_BLK_MQ_PCI) += blk-mq-pci.o
obj-$(CONFIG_BLK_MQ_VIRTIO) += blk-mq-virtio.o obj-$(CONFIG_BLK_MQ_VIRTIO) += blk-mq-virtio.o
obj-$(CONFIG_BLK_MQ_RDMA) += blk-mq-rdma.o
obj-$(CONFIG_BLK_DEV_ZONED) += blk-zoned.o obj-$(CONFIG_BLK_DEV_ZONED) += blk-zoned.o
obj-$(CONFIG_BLK_WBT) += blk-wbt.o obj-$(CONFIG_BLK_WBT) += blk-wbt.o
obj-$(CONFIG_BLK_DEBUG_FS) += blk-mq-debugfs.o obj-$(CONFIG_BLK_DEBUG_FS) += blk-mq-debugfs.o
......
/*
* Copyright (c) 2017 Sagi Grimberg.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#include <linux/blk-mq.h>
#include <linux/blk-mq-rdma.h>
#include <rdma/ib_verbs.h>
/**
* blk_mq_rdma_map_queues - provide a default queue mapping for rdma device
* @set: tagset to provide the mapping for
* @dev: rdma device associated with @set.
* @first_vec: first interrupt vectors to use for queues (usually 0)
*
* This function assumes the rdma device @dev has at least as many available
* interrupt vetors as @set has queues. It will then query it's affinity mask
* and built queue mapping that maps a queue to the CPUs that have irq affinity
* for the corresponding vector.
*
* In case either the driver passed a @dev with less vectors than
* @set->nr_hw_queues, or @dev does not provide an affinity mask for a
* vector, we fallback to the naive mapping.
*/
int blk_mq_rdma_map_queues(struct blk_mq_tag_set *set,
struct ib_device *dev, int first_vec)
{
const struct cpumask *mask;
unsigned int queue, cpu;
for (queue = 0; queue < set->nr_hw_queues; queue++) {
mask = ib_get_vector_affinity(dev, first_vec + queue);
if (!mask)
goto fallback;
for_each_cpu(cpu, mask)
set->mq_map[cpu] = queue;
}
return 0;
fallback:
return blk_mq_map_queues(set);
}
EXPORT_SYMBOL_GPL(blk_mq_rdma_map_queues);
...@@ -34,6 +34,15 @@ config INFINIBAND_USER_ACCESS ...@@ -34,6 +34,15 @@ config INFINIBAND_USER_ACCESS
libibverbs, libibcm and a hardware driver library from libibverbs, libibcm and a hardware driver library from
<http://www.openfabrics.org/git/>. <http://www.openfabrics.org/git/>.
config INFINIBAND_EXP_USER_ACCESS
bool "Allow experimental support for Infiniband ABI"
depends on INFINIBAND_USER_ACCESS
---help---
IOCTL based ABI support for Infiniband. This allows userspace
to invoke the experimental IOCTL based ABI.
These commands are parsed via per-device parsing tree and
enables per-device features.
config INFINIBAND_USER_MEM config INFINIBAND_USER_MEM
bool bool
depends on INFINIBAND_USER_ACCESS != n depends on INFINIBAND_USER_ACCESS != n
......
...@@ -11,7 +11,8 @@ ib_core-y := packer.o ud_header.o verbs.o cq.o rw.o sysfs.o \ ...@@ -11,7 +11,8 @@ ib_core-y := packer.o ud_header.o verbs.o cq.o rw.o sysfs.o \
device.o fmr_pool.o cache.o netlink.o \ device.o fmr_pool.o cache.o netlink.o \
roce_gid_mgmt.o mr_pool.o addr.o sa_query.o \ roce_gid_mgmt.o mr_pool.o addr.o sa_query.o \
multicast.o mad.o smi.o agent.o mad_rmpp.o \ multicast.o mad.o smi.o agent.o mad_rmpp.o \
security.o security.o nldev.o
ib_core-$(CONFIG_INFINIBAND_USER_MEM) += umem.o ib_core-$(CONFIG_INFINIBAND_USER_MEM) += umem.o
ib_core-$(CONFIG_INFINIBAND_ON_DEMAND_PAGING) += umem_odp.o umem_rbtree.o ib_core-$(CONFIG_INFINIBAND_ON_DEMAND_PAGING) += umem_odp.o umem_rbtree.o
ib_core-$(CONFIG_CGROUP_RDMA) += cgroup.o ib_core-$(CONFIG_CGROUP_RDMA) += cgroup.o
...@@ -31,4 +32,5 @@ ib_umad-y := user_mad.o ...@@ -31,4 +32,5 @@ ib_umad-y := user_mad.o
ib_ucm-y := ucm.o ib_ucm-y := ucm.o
ib_uverbs-y := uverbs_main.o uverbs_cmd.o uverbs_marshall.o \ ib_uverbs-y := uverbs_main.o uverbs_cmd.o uverbs_marshall.o \
rdma_core.o uverbs_std_types.o rdma_core.o uverbs_std_types.o uverbs_ioctl.o \
uverbs_ioctl_merge.o
...@@ -130,13 +130,11 @@ static void ib_nl_process_good_ip_rsep(const struct nlmsghdr *nlh) ...@@ -130,13 +130,11 @@ static void ib_nl_process_good_ip_rsep(const struct nlmsghdr *nlh)
} }
int ib_nl_handle_ip_res_resp(struct sk_buff *skb, int ib_nl_handle_ip_res_resp(struct sk_buff *skb,
struct netlink_callback *cb) struct nlmsghdr *nlh,
struct netlink_ext_ack *extack)
{ {
const struct nlmsghdr *nlh = (struct nlmsghdr *)cb->nlh;
if ((nlh->nlmsg_flags & NLM_F_REQUEST) || if ((nlh->nlmsg_flags & NLM_F_REQUEST) ||
!(NETLINK_CB(skb).sk) || !(NETLINK_CB(skb).sk))
!netlink_capable(skb, CAP_NET_ADMIN))
return -EPERM; return -EPERM;
if (ib_nl_is_good_ip_resp(nlh)) if (ib_nl_is_good_ip_resp(nlh))
...@@ -186,7 +184,7 @@ static int ib_nl_ip_send_msg(struct rdma_dev_addr *dev_addr, ...@@ -186,7 +184,7 @@ static int ib_nl_ip_send_msg(struct rdma_dev_addr *dev_addr,
/* Repair the nlmsg header length */ /* Repair the nlmsg header length */
nlmsg_end(skb, nlh); nlmsg_end(skb, nlh);
ibnl_multicast(skb, nlh, RDMA_NL_GROUP_LS, GFP_KERNEL); rdma_nl_multicast(skb, RDMA_NL_GROUP_LS, GFP_KERNEL);
/* Make the request retry, so when we get the response from userspace /* Make the request retry, so when we get the response from userspace
* we will have something. * we will have something.
...@@ -326,7 +324,7 @@ static void queue_req(struct addr_req *req) ...@@ -326,7 +324,7 @@ static void queue_req(struct addr_req *req)
static int ib_nl_fetch_ha(struct dst_entry *dst, struct rdma_dev_addr *dev_addr, static int ib_nl_fetch_ha(struct dst_entry *dst, struct rdma_dev_addr *dev_addr,
const void *daddr, u32 seq, u16 family) const void *daddr, u32 seq, u16 family)
{ {
if (ibnl_chk_listeners(RDMA_NL_GROUP_LS)) if (rdma_nl_chk_listeners(RDMA_NL_GROUP_LS))
return -EADDRNOTAVAIL; return -EADDRNOTAVAIL;
/* We fill in what we can, the response will fill the rest */ /* We fill in what we can, the response will fill the rest */
......
...@@ -1199,30 +1199,23 @@ int ib_cache_setup_one(struct ib_device *device) ...@@ -1199,30 +1199,23 @@ int ib_cache_setup_one(struct ib_device *device)
device->cache.ports = device->cache.ports =
kzalloc(sizeof(*device->cache.ports) * kzalloc(sizeof(*device->cache.ports) *
(rdma_end_port(device) - rdma_start_port(device) + 1), GFP_KERNEL); (rdma_end_port(device) - rdma_start_port(device) + 1), GFP_KERNEL);
if (!device->cache.ports) { if (!device->cache.ports)
err = -ENOMEM; return -ENOMEM;
goto out;
}
err = gid_table_setup_one(device); err = gid_table_setup_one(device);
if (err) if (err) {
goto out; kfree(device->cache.ports);
device->cache.ports = NULL;
return err;
}
for (p = 0; p <= rdma_end_port(device) - rdma_start_port(device); ++p) for (p = 0; p <= rdma_end_port(device) - rdma_start_port(device); ++p)
ib_cache_update(device, p + rdma_start_port(device), true); ib_cache_update(device, p + rdma_start_port(device), true);
INIT_IB_EVENT_HANDLER(&device->cache.event_handler, INIT_IB_EVENT_HANDLER(&device->cache.event_handler,
device, ib_cache_event); device, ib_cache_event);
err = ib_register_event_handler(&device->cache.event_handler); ib_register_event_handler(&device->cache.event_handler);
if (err)
goto err;
return 0; return 0;
err:
gid_table_cleanup_one(device);
out:
return err;
} }
void ib_cache_release_one(struct ib_device *device) void ib_cache_release_one(struct ib_device *device)
......
...@@ -373,11 +373,19 @@ static int cm_alloc_msg(struct cm_id_private *cm_id_priv, ...@@ -373,11 +373,19 @@ static int cm_alloc_msg(struct cm_id_private *cm_id_priv,
return ret; return ret;
} }
static int cm_alloc_response_msg(struct cm_port *port, static struct ib_mad_send_buf *cm_alloc_response_msg_no_ah(struct cm_port *port,
struct ib_mad_recv_wc *mad_recv_wc, struct ib_mad_recv_wc *mad_recv_wc)
struct ib_mad_send_buf **msg) {
return ib_create_send_mad(port->mad_agent, 1, mad_recv_wc->wc->pkey_index,
0, IB_MGMT_MAD_HDR, IB_MGMT_MAD_DATA,
GFP_ATOMIC,
IB_MGMT_BASE_VERSION);
}
static int cm_create_response_msg_ah(struct cm_port *port,
struct ib_mad_recv_wc *mad_recv_wc,
struct ib_mad_send_buf *msg)
{ {
struct ib_mad_send_buf *m;
struct ib_ah *ah; struct ib_ah *ah;
ah = ib_create_ah_from_wc(port->mad_agent->qp->pd, mad_recv_wc->wc, ah = ib_create_ah_from_wc(port->mad_agent->qp->pd, mad_recv_wc->wc,
...@@ -385,27 +393,40 @@ static int cm_alloc_response_msg(struct cm_port *port, ...@@ -385,27 +393,40 @@ static int cm_alloc_response_msg(struct cm_port *port,
if (IS_ERR(ah)) if (IS_ERR(ah))
return PTR_ERR(ah); return PTR_ERR(ah);
m = ib_create_send_mad(port->mad_agent, 1, mad_recv_wc->wc->pkey_index, msg->ah = ah;
0, IB_MGMT_MAD_HDR, IB_MGMT_MAD_DATA,
GFP_ATOMIC,
IB_MGMT_BASE_VERSION);
if (IS_ERR(m)) {
rdma_destroy_ah(ah);
return PTR_ERR(m);
}
m->ah = ah;
*msg = m;
return 0; return 0;
} }
static void cm_free_msg(struct ib_mad_send_buf *msg) static void cm_free_msg(struct ib_mad_send_buf *msg)
{ {
rdma_destroy_ah(msg->ah); if (msg->ah)
rdma_destroy_ah(msg->ah);
if (msg->context[0]) if (msg->context[0])
cm_deref_id(msg->context[0]); cm_deref_id(msg->context[0]);
ib_free_send_mad(msg); ib_free_send_mad(msg);
} }
static int cm_alloc_response_msg(struct cm_port *port,
struct ib_mad_recv_wc *mad_recv_wc,
struct ib_mad_send_buf **msg)
{
struct ib_mad_send_buf *m;
int ret;
m = cm_alloc_response_msg_no_ah(port, mad_recv_wc);
if (IS_ERR(m))
return PTR_ERR(m);
ret = cm_create_response_msg_ah(port, mad_recv_wc, m);
if (ret) {
cm_free_msg(m);
return ret;
}
*msg = m;
return 0;
}
static void * cm_copy_private_data(const void *private_data, static void * cm_copy_private_data(const void *private_data,
u8 private_data_len) u8 private_data_len)
{ {
...@@ -1175,6 +1196,11 @@ static void cm_format_req(struct cm_req_msg *req_msg, ...@@ -1175,6 +1196,11 @@ static void cm_format_req(struct cm_req_msg *req_msg,
{ {
struct sa_path_rec *pri_path = param->primary_path; struct sa_path_rec *pri_path = param->primary_path;
struct sa_path_rec *alt_path = param->alternate_path; struct sa_path_rec *alt_path = param->alternate_path;
bool pri_ext = false;
if (pri_path->rec_type == SA_PATH_REC_TYPE_OPA)
pri_ext = opa_is_extended_lid(pri_path->opa.dlid,
pri_path->opa.slid);
cm_format_mad_hdr(&req_msg->hdr, CM_REQ_ATTR_ID, cm_format_mad_hdr(&req_msg->hdr, CM_REQ_ATTR_ID,
cm_form_tid(cm_id_priv, CM_MSG_SEQUENCE_REQ)); cm_form_tid(cm_id_priv, CM_MSG_SEQUENCE_REQ));
...@@ -1202,18 +1228,24 @@ static void cm_format_req(struct cm_req_msg *req_msg, ...@@ -1202,18 +1228,24 @@ static void cm_format_req(struct cm_req_msg *req_msg,
cm_req_set_srq(req_msg, param->srq); cm_req_set_srq(req_msg, param->srq);
} }
req_msg->primary_local_gid = pri_path->sgid;
req_msg->primary_remote_gid = pri_path->dgid;
if (pri_ext) {
req_msg->primary_local_gid.global.interface_id
= OPA_MAKE_ID(be32_to_cpu(pri_path->opa.slid));
req_msg->primary_remote_gid.global.interface_id
= OPA_MAKE_ID(be32_to_cpu(pri_path->opa.dlid));
}
if (pri_path->hop_limit <= 1) { if (pri_path->hop_limit <= 1) {
req_msg->primary_local_lid = req_msg->primary_local_lid = pri_ext ? 0 :
htons(ntohl(sa_path_get_slid(pri_path))); htons(ntohl(sa_path_get_slid(pri_path)));
req_msg->primary_remote_lid = req_msg->primary_remote_lid = pri_ext ? 0 :
htons(ntohl(sa_path_get_dlid(pri_path))); htons(ntohl(sa_path_get_dlid(pri_path)));
} else { } else {
/* Work-around until there's a way to obtain remote LID info */ /* Work-around until there's a way to obtain remote LID info */
req_msg->primary_local_lid = IB_LID_PERMISSIVE; req_msg->primary_local_lid = IB_LID_PERMISSIVE;
req_msg->primary_remote_lid = IB_LID_PERMISSIVE; req_msg->primary_remote_lid = IB_LID_PERMISSIVE;
} }
req_msg->primary_local_gid = pri_path->sgid;
req_msg->primary_remote_gid = pri_path->dgid;
cm_req_set_primary_flow_label(req_msg, pri_path->flow_label); cm_req_set_primary_flow_label(req_msg, pri_path->flow_label);
cm_req_set_primary_packet_rate(req_msg, pri_path->rate); cm_req_set_primary_packet_rate(req_msg, pri_path->rate);
req_msg->primary_traffic_class = pri_path->traffic_class; req_msg->primary_traffic_class = pri_path->traffic_class;
...@@ -1225,17 +1257,29 @@ static void cm_format_req(struct cm_req_msg *req_msg, ...@@ -1225,17 +1257,29 @@ static void cm_format_req(struct cm_req_msg *req_msg,
pri_path->packet_life_time)); pri_path->packet_life_time));
if (alt_path) { if (alt_path) {
bool alt_ext = false;
if (alt_path->rec_type == SA_PATH_REC_TYPE_OPA)
alt_ext = opa_is_extended_lid(alt_path->opa.dlid,
alt_path->opa.slid);
req_msg->alt_local_gid = alt_path->sgid;
req_msg->alt_remote_gid = alt_path->dgid;
if (alt_ext) {
req_msg->alt_local_gid.global.interface_id
= OPA_MAKE_ID(be32_to_cpu(alt_path->opa.slid));
req_msg->alt_remote_gid.global.interface_id
= OPA_MAKE_ID(be32_to_cpu(alt_path->opa.dlid));
}
if (alt_path->hop_limit <= 1) { if (alt_path->hop_limit <= 1) {
req_msg->alt_local_lid = req_msg->alt_local_lid = alt_ext ? 0 :
htons(ntohl(sa_path_get_slid(alt_path))); htons(ntohl(sa_path_get_slid(alt_path)));
req_msg->alt_remote_lid = req_msg->alt_remote_lid = alt_ext ? 0 :
htons(ntohl(sa_path_get_dlid(alt_path))); htons(ntohl(sa_path_get_dlid(alt_path)));
} else { } else {
req_msg->alt_local_lid = IB_LID_PERMISSIVE; req_msg->alt_local_lid = IB_LID_PERMISSIVE;
req_msg->alt_remote_lid = IB_LID_PERMISSIVE; req_msg->alt_remote_lid = IB_LID_PERMISSIVE;
} }
req_msg->alt_local_gid = alt_path->sgid;
req_msg->alt_remote_gid = alt_path->dgid;
cm_req_set_alt_flow_label(req_msg, cm_req_set_alt_flow_label(req_msg,
alt_path->flow_label); alt_path->flow_label);
cm_req_set_alt_packet_rate(req_msg, alt_path->rate); cm_req_set_alt_packet_rate(req_msg, alt_path->rate);
...@@ -1405,16 +1449,63 @@ static inline int cm_is_active_peer(__be64 local_ca_guid, __be64 remote_ca_guid, ...@@ -1405,16 +1449,63 @@ static inline int cm_is_active_peer(__be64 local_ca_guid, __be64 remote_ca_guid,
(be32_to_cpu(local_qpn) > be32_to_cpu(remote_qpn)))); (be32_to_cpu(local_qpn) > be32_to_cpu(remote_qpn))));
} }
static bool cm_req_has_alt_path(struct cm_req_msg *req_msg)
{
return ((req_msg->alt_local_lid) ||
(ib_is_opa_gid(&req_msg->alt_local_gid)));
}
static void cm_path_set_rec_type(struct ib_device *ib_device, u8 port_num,
struct sa_path_rec *path, union ib_gid *gid)
{
if (ib_is_opa_gid(gid) && rdma_cap_opa_ah(ib_device, port_num))
path->rec_type = SA_PATH_REC_TYPE_OPA;
else
path->rec_type = SA_PATH_REC_TYPE_IB;
}
static void cm_format_path_lid_from_req(struct cm_req_msg *req_msg,
struct sa_path_rec *primary_path,
struct sa_path_rec *alt_path)
{
u32 lid;
if (primary_path->rec_type != SA_PATH_REC_TYPE_OPA) {
sa_path_set_dlid(primary_path,
htonl(ntohs(req_msg->primary_local_lid)));
sa_path_set_slid(primary_path,
htonl(ntohs(req_msg->primary_remote_lid)));
} else {
lid = opa_get_lid_from_gid(&req_msg->primary_local_gid);
sa_path_set_dlid(primary_path, cpu_to_be32(lid));
lid = opa_get_lid_from_gid(&req_msg->primary_remote_gid);
sa_path_set_slid(primary_path, cpu_to_be32(lid));
}
if (!cm_req_has_alt_path(req_msg))
return;
if (alt_path->rec_type != SA_PATH_REC_TYPE_OPA) {
sa_path_set_dlid(alt_path,
htonl(ntohs(req_msg->alt_local_lid)));
sa_path_set_slid(alt_path,
htonl(ntohs(req_msg->alt_remote_lid)));
} else {
lid = opa_get_lid_from_gid(&req_msg->alt_local_gid);
sa_path_set_dlid(alt_path, cpu_to_be32(lid));
lid = opa_get_lid_from_gid(&req_msg->alt_remote_gid);
sa_path_set_slid(alt_path, cpu_to_be32(lid));
}
}
static void cm_format_paths_from_req(struct cm_req_msg *req_msg, static void cm_format_paths_from_req(struct cm_req_msg *req_msg,
struct sa_path_rec *primary_path, struct sa_path_rec *primary_path,
struct sa_path_rec *alt_path) struct sa_path_rec *alt_path)
{ {
primary_path->dgid = req_msg->primary_local_gid; primary_path->dgid = req_msg->primary_local_gid;
primary_path->sgid = req_msg->primary_remote_gid; primary_path->sgid = req_msg->primary_remote_gid;
sa_path_set_dlid(primary_path,
htonl(ntohs(req_msg->primary_local_lid)));
sa_path_set_slid(primary_path,
htonl(ntohs(req_msg->primary_remote_lid)));
primary_path->flow_label = cm_req_get_primary_flow_label(req_msg); primary_path->flow_label = cm_req_get_primary_flow_label(req_msg);
primary_path->hop_limit = req_msg->primary_hop_limit; primary_path->hop_limit = req_msg->primary_hop_limit;
primary_path->traffic_class = req_msg->primary_traffic_class; primary_path->traffic_class = req_msg->primary_traffic_class;
...@@ -1431,13 +1522,9 @@ static void cm_format_paths_from_req(struct cm_req_msg *req_msg, ...@@ -1431,13 +1522,9 @@ static void cm_format_paths_from_req(struct cm_req_msg *req_msg,
primary_path->packet_life_time -= (primary_path->packet_life_time > 0); primary_path->packet_life_time -= (primary_path->packet_life_time > 0);
primary_path->service_id = req_msg->service_id; primary_path->service_id = req_msg->service_id;
if (req_msg->alt_local_lid) { if (cm_req_has_alt_path(req_msg)) {
alt_path->dgid = req_msg->alt_local_gid; alt_path->dgid = req_msg->alt_local_gid;
alt_path->sgid = req_msg->alt_remote_gid; alt_path->sgid = req_msg->alt_remote_gid;
sa_path_set_dlid(alt_path,
htonl(ntohs(req_msg->alt_local_lid)));
sa_path_set_slid(alt_path,
htonl(ntohs(req_msg->alt_remote_lid)));
alt_path->flow_label = cm_req_get_alt_flow_label(req_msg); alt_path->flow_label = cm_req_get_alt_flow_label(req_msg);
alt_path->hop_limit = req_msg->alt_hop_limit; alt_path->hop_limit = req_msg->alt_hop_limit;
alt_path->traffic_class = req_msg->alt_traffic_class; alt_path->traffic_class = req_msg->alt_traffic_class;
...@@ -1454,6 +1541,7 @@ static void cm_format_paths_from_req(struct cm_req_msg *req_msg, ...@@ -1454,6 +1541,7 @@ static void cm_format_paths_from_req(struct cm_req_msg *req_msg,
alt_path->packet_life_time -= (alt_path->packet_life_time > 0); alt_path->packet_life_time -= (alt_path->packet_life_time > 0);
alt_path->service_id = req_msg->service_id; alt_path->service_id = req_msg->service_id;
} }
cm_format_path_lid_from_req(req_msg, primary_path, alt_path);
} }
static u16 cm_get_bth_pkey(struct cm_work *work) static u16 cm_get_bth_pkey(struct cm_work *work)
...@@ -1703,7 +1791,7 @@ static void cm_process_routed_req(struct cm_req_msg *req_msg, struct ib_wc *wc) ...@@ -1703,7 +1791,7 @@ static void cm_process_routed_req(struct cm_req_msg *req_msg, struct ib_wc *wc)
{ {
if (!cm_req_get_primary_subnet_local(req_msg)) { if (!cm_req_get_primary_subnet_local(req_msg)) {
if (req_msg->primary_local_lid == IB_LID_PERMISSIVE) { if (req_msg->primary_local_lid == IB_LID_PERMISSIVE) {
req_msg->primary_local_lid = cpu_to_be16(wc->slid); req_msg->primary_local_lid = ib_lid_be16(wc->slid);
cm_req_set_primary_sl(req_msg, wc->sl); cm_req_set_primary_sl(req_msg, wc->sl);
} }
...@@ -1713,7 +1801,7 @@ static void cm_process_routed_req(struct cm_req_msg *req_msg, struct ib_wc *wc) ...@@ -1713,7 +1801,7 @@ static void cm_process_routed_req(struct cm_req_msg *req_msg, struct ib_wc *wc)
if (!cm_req_get_alt_subnet_local(req_msg)) { if (!cm_req_get_alt_subnet_local(req_msg)) {
if (req_msg->alt_local_lid == IB_LID_PERMISSIVE) { if (req_msg->alt_local_lid == IB_LID_PERMISSIVE) {
req_msg->alt_local_lid = cpu_to_be16(wc->slid); req_msg->alt_local_lid = ib_lid_be16(wc->slid);
cm_req_set_alt_sl(req_msg, wc->sl); cm_req_set_alt_sl(req_msg, wc->sl);
} }
...@@ -1784,9 +1872,12 @@ static int cm_req_handler(struct cm_work *work) ...@@ -1784,9 +1872,12 @@ static int cm_req_handler(struct cm_work *work)
dev_net(gid_attr.ndev)); dev_net(gid_attr.ndev));
dev_put(gid_attr.ndev); dev_put(gid_attr.ndev);
} else { } else {
work->path[0].rec_type = SA_PATH_REC_TYPE_IB; cm_path_set_rec_type(work->port->cm_dev->ib_device,
work->port->port_num,
&work->path[0],
&req_msg->primary_local_gid);
} }
if (req_msg->alt_local_lid) if (cm_req_has_alt_path(req_msg))
work->path[1].rec_type = work->path[0].rec_type; work->path[1].rec_type = work->path[0].rec_type;
cm_format_paths_from_req(req_msg, &work->path[0], cm_format_paths_from_req(req_msg, &work->path[0],
&work->path[1]); &work->path[1]);
...@@ -1811,16 +1902,19 @@ static int cm_req_handler(struct cm_work *work) ...@@ -1811,16 +1902,19 @@ static int cm_req_handler(struct cm_work *work)
dev_net(gid_attr.ndev)); dev_net(gid_attr.ndev));
dev_put(gid_attr.ndev); dev_put(gid_attr.ndev);
} else { } else {
work->path[0].rec_type = SA_PATH_REC_TYPE_IB; cm_path_set_rec_type(work->port->cm_dev->ib_device,
work->port->port_num,
&work->path[0],
&req_msg->primary_local_gid);
} }
if (req_msg->alt_local_lid) if (cm_req_has_alt_path(req_msg))
work->path[1].rec_type = work->path[0].rec_type; work->path[1].rec_type = work->path[0].rec_type;
ib_send_cm_rej(cm_id, IB_CM_REJ_INVALID_GID, ib_send_cm_rej(cm_id, IB_CM_REJ_INVALID_GID,
&work->path[0].sgid, sizeof work->path[0].sgid, &work->path[0].sgid, sizeof work->path[0].sgid,
NULL, 0); NULL, 0);
goto rejected; goto rejected;
} }
if (req_msg->alt_local_lid) { if (cm_req_has_alt_path(req_msg)) {
ret = cm_init_av_by_path(&work->path[1], &cm_id_priv->alt_av, ret = cm_init_av_by_path(&work->path[1], &cm_id_priv->alt_av,
cm_id_priv); cm_id_priv);
if (ret) { if (ret) {
...@@ -2424,7 +2518,8 @@ static int cm_dreq_handler(struct cm_work *work) ...@@ -2424,7 +2518,8 @@ static int cm_dreq_handler(struct cm_work *work)
case IB_CM_TIMEWAIT: case IB_CM_TIMEWAIT:
atomic_long_inc(&work->port->counter_group[CM_RECV_DUPLICATES]. atomic_long_inc(&work->port->counter_group[CM_RECV_DUPLICATES].
counter[CM_DREQ_COUNTER]); counter[CM_DREQ_COUNTER]);
if (cm_alloc_response_msg(work->port, work->mad_recv_wc, &msg)) msg = cm_alloc_response_msg_no_ah(work->port, work->mad_recv_wc);
if (IS_ERR(msg))
goto unlock; goto unlock;
cm_format_drep((struct cm_drep_msg *) msg->mad, cm_id_priv, cm_format_drep((struct cm_drep_msg *) msg->mad, cm_id_priv,
...@@ -2432,7 +2527,8 @@ static int cm_dreq_handler(struct cm_work *work) ...@@ -2432,7 +2527,8 @@ static int cm_dreq_handler(struct cm_work *work)
cm_id_priv->private_data_len); cm_id_priv->private_data_len);
spin_unlock_irq(&cm_id_priv->lock); spin_unlock_irq(&cm_id_priv->lock);
if (ib_post_send_mad(msg, NULL)) if (cm_create_response_msg_ah(work->port, work->mad_recv_wc, msg) ||
ib_post_send_mad(msg, NULL))
cm_free_msg(msg); cm_free_msg(msg);
goto deref; goto deref;
case IB_CM_DREQ_RCVD: case IB_CM_DREQ_RCVD:
...@@ -2843,6 +2939,11 @@ static void cm_format_lap(struct cm_lap_msg *lap_msg, ...@@ -2843,6 +2939,11 @@ static void cm_format_lap(struct cm_lap_msg *lap_msg,
const void *private_data, const void *private_data,
u8 private_data_len) u8 private_data_len)
{ {
bool alt_ext = false;
if (alternate_path->rec_type == SA_PATH_REC_TYPE_OPA)
alt_ext = opa_is_extended_lid(alternate_path->opa.dlid,
alternate_path->opa.slid);
cm_format_mad_hdr(&lap_msg->hdr, CM_LAP_ATTR_ID, cm_format_mad_hdr(&lap_msg->hdr, CM_LAP_ATTR_ID,
cm_form_tid(cm_id_priv, CM_MSG_SEQUENCE_LAP)); cm_form_tid(cm_id_priv, CM_MSG_SEQUENCE_LAP));
lap_msg->local_comm_id = cm_id_priv->id.local_id; lap_msg->local_comm_id = cm_id_priv->id.local_id;
...@@ -2856,6 +2957,12 @@ static void cm_format_lap(struct cm_lap_msg *lap_msg, ...@@ -2856,6 +2957,12 @@ static void cm_format_lap(struct cm_lap_msg *lap_msg,
htons(ntohl(sa_path_get_dlid(alternate_path))); htons(ntohl(sa_path_get_dlid(alternate_path)));
lap_msg->alt_local_gid = alternate_path->sgid; lap_msg->alt_local_gid = alternate_path->sgid;
lap_msg->alt_remote_gid = alternate_path->dgid; lap_msg->alt_remote_gid = alternate_path->dgid;
if (alt_ext) {
lap_msg->alt_local_gid.global.interface_id
= OPA_MAKE_ID(be32_to_cpu(alternate_path->opa.slid));
lap_msg->alt_remote_gid.global.interface_id
= OPA_MAKE_ID(be32_to_cpu(alternate_path->opa.dlid));
}
cm_lap_set_flow_label(lap_msg, alternate_path->flow_label); cm_lap_set_flow_label(lap_msg, alternate_path->flow_label);
cm_lap_set_traffic_class(lap_msg, alternate_path->traffic_class); cm_lap_set_traffic_class(lap_msg, alternate_path->traffic_class);
lap_msg->alt_hop_limit = alternate_path->hop_limit; lap_msg->alt_hop_limit = alternate_path->hop_limit;
...@@ -2924,16 +3031,29 @@ out: spin_unlock_irqrestore(&cm_id_priv->lock, flags); ...@@ -2924,16 +3031,29 @@ out: spin_unlock_irqrestore(&cm_id_priv->lock, flags);
} }
EXPORT_SYMBOL(ib_send_cm_lap); EXPORT_SYMBOL(ib_send_cm_lap);
static void cm_format_path_lid_from_lap(struct cm_lap_msg *lap_msg,
struct sa_path_rec *path)
{
u32 lid;
if (path->rec_type != SA_PATH_REC_TYPE_OPA) {
sa_path_set_dlid(path, htonl(ntohs(lap_msg->alt_local_lid)));
sa_path_set_slid(path, htonl(ntohs(lap_msg->alt_remote_lid)));
} else {
lid = opa_get_lid_from_gid(&lap_msg->alt_local_gid);
sa_path_set_dlid(path, cpu_to_be32(lid));
lid = opa_get_lid_from_gid(&lap_msg->alt_remote_gid);
sa_path_set_slid(path, cpu_to_be32(lid));
}
}
static void cm_format_path_from_lap(struct cm_id_private *cm_id_priv, static void cm_format_path_from_lap(struct cm_id_private *cm_id_priv,
struct sa_path_rec *path, struct sa_path_rec *path,
struct cm_lap_msg *lap_msg) struct cm_lap_msg *lap_msg)
{ {
memset(path, 0, sizeof *path);
path->rec_type = SA_PATH_REC_TYPE_IB;
path->dgid = lap_msg->alt_local_gid; path->dgid = lap_msg->alt_local_gid;
path->sgid = lap_msg->alt_remote_gid; path->sgid = lap_msg->alt_remote_gid;
sa_path_set_dlid(path, htonl(ntohs(lap_msg->alt_local_lid)));
sa_path_set_slid(path, htonl(ntohs(lap_msg->alt_remote_lid)));
path->flow_label = cm_lap_get_flow_label(lap_msg); path->flow_label = cm_lap_get_flow_label(lap_msg);
path->hop_limit = lap_msg->alt_hop_limit; path->hop_limit = lap_msg->alt_hop_limit;
path->traffic_class = cm_lap_get_traffic_class(lap_msg); path->traffic_class = cm_lap_get_traffic_class(lap_msg);
...@@ -2947,6 +3067,7 @@ static void cm_format_path_from_lap(struct cm_id_private *cm_id_priv, ...@@ -2947,6 +3067,7 @@ static void cm_format_path_from_lap(struct cm_id_private *cm_id_priv,
path->packet_life_time_selector = IB_SA_EQ; path->packet_life_time_selector = IB_SA_EQ;
path->packet_life_time = cm_lap_get_local_ack_timeout(lap_msg); path->packet_life_time = cm_lap_get_local_ack_timeout(lap_msg);
path->packet_life_time -= (path->packet_life_time > 0); path->packet_life_time -= (path->packet_life_time > 0);
cm_format_path_lid_from_lap(lap_msg, path);
} }
static int cm_lap_handler(struct cm_work *work) static int cm_lap_handler(struct cm_work *work)
...@@ -2965,6 +3086,11 @@ static int cm_lap_handler(struct cm_work *work) ...@@ -2965,6 +3086,11 @@ static int cm_lap_handler(struct cm_work *work)
return -EINVAL; return -EINVAL;
param = &work->cm_event.param.lap_rcvd; param = &work->cm_event.param.lap_rcvd;
memset(&work->path[0], 0, sizeof(work->path[1]));
cm_path_set_rec_type(work->port->cm_dev->ib_device,
work->port->port_num,
&work->path[0],
&lap_msg->alt_local_gid);
param->alternate_path = &work->path[0]; param->alternate_path = &work->path[0];
cm_format_path_from_lap(cm_id_priv, param->alternate_path, lap_msg); cm_format_path_from_lap(cm_id_priv, param->alternate_path, lap_msg);
work->cm_event.private_data = &lap_msg->private_data; work->cm_event.private_data = &lap_msg->private_data;
...@@ -2980,7 +3106,8 @@ static int cm_lap_handler(struct cm_work *work) ...@@ -2980,7 +3106,8 @@ static int cm_lap_handler(struct cm_work *work)
case IB_CM_MRA_LAP_SENT: case IB_CM_MRA_LAP_SENT:
atomic_long_inc(&work->port->counter_group[CM_RECV_DUPLICATES]. atomic_long_inc(&work->port->counter_group[CM_RECV_DUPLICATES].
counter[CM_LAP_COUNTER]); counter[CM_LAP_COUNTER]);
if (cm_alloc_response_msg(work->port, work->mad_recv_wc, &msg)) msg = cm_alloc_response_msg_no_ah(work->port, work->mad_recv_wc);
if (IS_ERR(msg))
goto unlock; goto unlock;
cm_format_mra((struct cm_mra_msg *) msg->mad, cm_id_priv, cm_format_mra((struct cm_mra_msg *) msg->mad, cm_id_priv,
...@@ -2990,7 +3117,8 @@ static int cm_lap_handler(struct cm_work *work) ...@@ -2990,7 +3117,8 @@ static int cm_lap_handler(struct cm_work *work)
cm_id_priv->private_data_len); cm_id_priv->private_data_len);
spin_unlock_irq(&cm_id_priv->lock); spin_unlock_irq(&cm_id_priv->lock);
if (ib_post_send_mad(msg, NULL)) if (cm_create_response_msg_ah(work->port, work->mad_recv_wc, msg) ||
ib_post_send_mad(msg, NULL))
cm_free_msg(msg); cm_free_msg(msg);
goto deref; goto deref;
case IB_CM_LAP_RCVD: case IB_CM_LAP_RCVD:
...@@ -4201,7 +4329,7 @@ static int __init ib_cm_init(void) ...@@ -4201,7 +4329,7 @@ static int __init ib_cm_init(void)
goto error1; goto error1;
} }
cm.wq = create_workqueue("ib_cm"); cm.wq = alloc_workqueue("ib_cm", 0, 1);
if (!cm.wq) { if (!cm.wq) {
ret = -ENOMEM; ret = -ENOMEM;
goto error2; goto error2;
......
...@@ -72,6 +72,7 @@ MODULE_LICENSE("Dual BSD/GPL"); ...@@ -72,6 +72,7 @@ MODULE_LICENSE("Dual BSD/GPL");
#define CMA_MAX_CM_RETRIES 15 #define CMA_MAX_CM_RETRIES 15
#define CMA_CM_MRA_SETTING (IB_CM_MRA_FLAG_DELAY | 24) #define CMA_CM_MRA_SETTING (IB_CM_MRA_FLAG_DELAY | 24)
#define CMA_IBOE_PACKET_LIFETIME 18 #define CMA_IBOE_PACKET_LIFETIME 18
#define CMA_PREFERRED_ROCE_GID_TYPE IB_GID_TYPE_ROCE_UDP_ENCAP
static const char * const cma_events[] = { static const char * const cma_events[] = {
[RDMA_CM_EVENT_ADDR_RESOLVED] = "address resolved", [RDMA_CM_EVENT_ADDR_RESOLVED] = "address resolved",
...@@ -3998,7 +3999,8 @@ static void iboe_mcast_work_handler(struct work_struct *work) ...@@ -3998,7 +3999,8 @@ static void iboe_mcast_work_handler(struct work_struct *work)
kfree(mw); kfree(mw);
} }
static void cma_iboe_set_mgid(struct sockaddr *addr, union ib_gid *mgid) static void cma_iboe_set_mgid(struct sockaddr *addr, union ib_gid *mgid,
enum ib_gid_type gid_type)
{ {
struct sockaddr_in *sin = (struct sockaddr_in *)addr; struct sockaddr_in *sin = (struct sockaddr_in *)addr;
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)addr; struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)addr;
...@@ -4008,8 +4010,8 @@ static void cma_iboe_set_mgid(struct sockaddr *addr, union ib_gid *mgid) ...@@ -4008,8 +4010,8 @@ static void cma_iboe_set_mgid(struct sockaddr *addr, union ib_gid *mgid)
} else if (addr->sa_family == AF_INET6) { } else if (addr->sa_family == AF_INET6) {
memcpy(mgid, &sin6->sin6_addr, sizeof *mgid); memcpy(mgid, &sin6->sin6_addr, sizeof *mgid);
} else { } else {
mgid->raw[0] = 0xff; mgid->raw[0] = (gid_type == IB_GID_TYPE_IB) ? 0xff : 0;
mgid->raw[1] = 0x0e; mgid->raw[1] = (gid_type == IB_GID_TYPE_IB) ? 0x0e : 0;
mgid->raw[2] = 0; mgid->raw[2] = 0;
mgid->raw[3] = 0; mgid->raw[3] = 0;
mgid->raw[4] = 0; mgid->raw[4] = 0;
...@@ -4050,7 +4052,9 @@ static int cma_iboe_join_multicast(struct rdma_id_private *id_priv, ...@@ -4050,7 +4052,9 @@ static int cma_iboe_join_multicast(struct rdma_id_private *id_priv,
goto out1; goto out1;
} }
cma_iboe_set_mgid(addr, &mc->multicast.ib->rec.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(addr, &mc->multicast.ib->rec.mgid, gid_type);
mc->multicast.ib->rec.pkey = cpu_to_be16(0xffff); mc->multicast.ib->rec.pkey = cpu_to_be16(0xffff);
if (id_priv->id.ps == RDMA_PS_UDP) if (id_priv->id.ps == RDMA_PS_UDP)
...@@ -4066,8 +4070,6 @@ static int cma_iboe_join_multicast(struct rdma_id_private *id_priv, ...@@ -4066,8 +4070,6 @@ static int cma_iboe_join_multicast(struct rdma_id_private *id_priv,
mc->multicast.ib->rec.hop_limit = 1; mc->multicast.ib->rec.hop_limit = 1;
mc->multicast.ib->rec.mtu = iboe_get_mtu(ndev->mtu); mc->multicast.ib->rec.mtu = iboe_get_mtu(ndev->mtu);
gid_type = id_priv->cma_dev->default_gid_type[id_priv->id.port_num -
rdma_start_port(id_priv->cma_dev->device)];
if (addr->sa_family == AF_INET) { if (addr->sa_family == AF_INET) {
if (gid_type == IB_GID_TYPE_ROCE_UDP_ENCAP) { if (gid_type == IB_GID_TYPE_ROCE_UDP_ENCAP) {
mc->multicast.ib->rec.hop_limit = IPV6_DEFAULT_HOPLIMIT; mc->multicast.ib->rec.hop_limit = IPV6_DEFAULT_HOPLIMIT;
...@@ -4280,8 +4282,12 @@ static void cma_add_one(struct ib_device *device) ...@@ -4280,8 +4282,12 @@ static void cma_add_one(struct ib_device *device)
for (i = rdma_start_port(device); i <= rdma_end_port(device); i++) { for (i = rdma_start_port(device); i <= rdma_end_port(device); i++) {
supported_gids = roce_gid_type_mask_support(device, i); supported_gids = roce_gid_type_mask_support(device, i);
WARN_ON(!supported_gids); WARN_ON(!supported_gids);
cma_dev->default_gid_type[i - rdma_start_port(device)] = if (supported_gids & (1 << CMA_PREFERRED_ROCE_GID_TYPE))
find_first_bit(&supported_gids, BITS_PER_LONG); cma_dev->default_gid_type[i - rdma_start_port(device)] =
CMA_PREFERRED_ROCE_GID_TYPE;
else
cma_dev->default_gid_type[i - rdma_start_port(device)] =
find_first_bit(&supported_gids, BITS_PER_LONG);
cma_dev->default_roce_tos[i - rdma_start_port(device)] = 0; cma_dev->default_roce_tos[i - rdma_start_port(device)] = 0;
} }
...@@ -4452,9 +4458,8 @@ static int cma_get_id_stats(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -4452,9 +4458,8 @@ static int cma_get_id_stats(struct sk_buff *skb, struct netlink_callback *cb)
return skb->len; return skb->len;
} }
static const struct ibnl_client_cbs cma_cb_table[] = { static const struct rdma_nl_cbs cma_cb_table[] = {
[RDMA_NL_RDMA_CM_ID_STATS] = { .dump = cma_get_id_stats, [RDMA_NL_RDMA_CM_ID_STATS] = { .dump = cma_get_id_stats},
.module = THIS_MODULE },
}; };
static int cma_init_net(struct net *net) static int cma_init_net(struct net *net)
...@@ -4506,9 +4511,7 @@ static int __init cma_init(void) ...@@ -4506,9 +4511,7 @@ static int __init cma_init(void)
if (ret) if (ret)
goto err; goto err;
if (ibnl_add_client(RDMA_NL_RDMA_CM, ARRAY_SIZE(cma_cb_table), rdma_nl_register(RDMA_NL_RDMA_CM, cma_cb_table);
cma_cb_table))
pr_warn("RDMA CMA: failed to add netlink callback\n");
cma_configfs_init(); cma_configfs_init();
return 0; return 0;
...@@ -4525,7 +4528,7 @@ static int __init cma_init(void) ...@@ -4525,7 +4528,7 @@ static int __init cma_init(void)
static void __exit cma_cleanup(void) static void __exit cma_cleanup(void)
{ {
cma_configfs_exit(); cma_configfs_exit();
ibnl_remove_client(RDMA_NL_RDMA_CM); rdma_nl_unregister(RDMA_NL_RDMA_CM);
ib_unregister_client(&cma_client); ib_unregister_client(&cma_client);
unregister_netdevice_notifier(&cma_nb); unregister_netdevice_notifier(&cma_nb);
rdma_addr_unregister_client(&addr_client); rdma_addr_unregister_client(&addr_client);
...@@ -4534,5 +4537,7 @@ static void __exit cma_cleanup(void) ...@@ -4534,5 +4537,7 @@ static void __exit cma_cleanup(void)
destroy_workqueue(cma_wq); destroy_workqueue(cma_wq);
} }
MODULE_ALIAS_RDMA_NETLINK(RDMA_NL_RDMA_CM, 1);
module_init(cma_init); module_init(cma_init);
module_exit(cma_cleanup); module_exit(cma_cleanup);
...@@ -38,6 +38,7 @@ ...@@ -38,6 +38,7 @@
#include <linux/cgroup_rdma.h> #include <linux/cgroup_rdma.h>
#include <rdma/ib_verbs.h> #include <rdma/ib_verbs.h>
#include <rdma/opa_addr.h>
#include <rdma/ib_mad.h> #include <rdma/ib_mad.h>
#include "mad_priv.h" #include "mad_priv.h"
...@@ -102,6 +103,14 @@ void ib_enum_all_roce_netdevs(roce_netdev_filter filter, ...@@ -102,6 +103,14 @@ void ib_enum_all_roce_netdevs(roce_netdev_filter filter,
roce_netdev_callback cb, roce_netdev_callback cb,
void *cookie); void *cookie);
typedef int (*nldev_callback)(struct ib_device *device,
struct sk_buff *skb,
struct netlink_callback *cb,
unsigned int idx);
int ib_enum_all_devs(nldev_callback nldev_cb, struct sk_buff *skb,
struct netlink_callback *cb);
enum ib_cache_gid_default_mode { enum ib_cache_gid_default_mode {
IB_CACHE_GID_DEFAULT_MODE_SET, IB_CACHE_GID_DEFAULT_MODE_SET,
IB_CACHE_GID_DEFAULT_MODE_DELETE IB_CACHE_GID_DEFAULT_MODE_DELETE
...@@ -179,8 +188,8 @@ void ib_mad_cleanup(void); ...@@ -179,8 +188,8 @@ void ib_mad_cleanup(void);
int ib_sa_init(void); int ib_sa_init(void);
void ib_sa_cleanup(void); void ib_sa_cleanup(void);
int ibnl_init(void); int rdma_nl_init(void);
void ibnl_cleanup(void); void rdma_nl_exit(void);
/** /**
* Check if there are any listeners to the netlink group * Check if there are any listeners to the netlink group
...@@ -190,11 +199,14 @@ void ibnl_cleanup(void); ...@@ -190,11 +199,14 @@ void ibnl_cleanup(void);
int ibnl_chk_listeners(unsigned int group); int ibnl_chk_listeners(unsigned int group);
int ib_nl_handle_resolve_resp(struct sk_buff *skb, int ib_nl_handle_resolve_resp(struct sk_buff *skb,
struct netlink_callback *cb); struct nlmsghdr *nlh,
struct netlink_ext_ack *extack);
int ib_nl_handle_set_timeout(struct sk_buff *skb, int ib_nl_handle_set_timeout(struct sk_buff *skb,
struct netlink_callback *cb); struct nlmsghdr *nlh,
struct netlink_ext_ack *extack);
int ib_nl_handle_ip_res_resp(struct sk_buff *skb, int ib_nl_handle_ip_res_resp(struct sk_buff *skb,
struct netlink_callback *cb); struct nlmsghdr *nlh,
struct netlink_ext_ack *extack);
int ib_get_cached_subnet_prefix(struct ib_device *device, int ib_get_cached_subnet_prefix(struct ib_device *device,
u8 port_num, u8 port_num,
...@@ -301,4 +313,9 @@ static inline int ib_mad_enforce_security(struct ib_mad_agent_private *map, ...@@ -301,4 +313,9 @@ static inline int ib_mad_enforce_security(struct ib_mad_agent_private *map,
return 0; return 0;
} }
#endif #endif
struct ib_device *__ib_device_get_by_index(u32 ifindex);
/* RDMA device netlink */
void nldev_init(void);
void nldev_exit(void);
#endif /* _CORE_PRIV_H */ #endif /* _CORE_PRIV_H */
...@@ -134,6 +134,17 @@ static int ib_device_check_mandatory(struct ib_device *device) ...@@ -134,6 +134,17 @@ static int ib_device_check_mandatory(struct ib_device *device)
return 0; return 0;
} }
struct ib_device *__ib_device_get_by_index(u32 index)
{
struct ib_device *device;
list_for_each_entry(device, &device_list, core_list)
if (device->index == index)
return device;
return NULL;
}
static struct ib_device *__ib_device_get_by_name(const char *name) static struct ib_device *__ib_device_get_by_name(const char *name)
{ {
struct ib_device *device; struct ib_device *device;
...@@ -145,7 +156,6 @@ static struct ib_device *__ib_device_get_by_name(const char *name) ...@@ -145,7 +156,6 @@ static struct ib_device *__ib_device_get_by_name(const char *name)
return NULL; return NULL;
} }
static int alloc_name(char *name) static int alloc_name(char *name)
{ {
unsigned long *inuse; unsigned long *inuse;
...@@ -326,10 +336,10 @@ static int read_port_immutable(struct ib_device *device) ...@@ -326,10 +336,10 @@ static int read_port_immutable(struct ib_device *device)
return 0; return 0;
} }
void ib_get_device_fw_str(struct ib_device *dev, char *str, size_t str_len) void ib_get_device_fw_str(struct ib_device *dev, char *str)
{ {
if (dev->get_dev_fw_str) if (dev->get_dev_fw_str)
dev->get_dev_fw_str(dev, str, str_len); dev->get_dev_fw_str(dev, str);
else else
str[0] = '\0'; str[0] = '\0';
} }
...@@ -394,6 +404,30 @@ static int ib_security_change(struct notifier_block *nb, unsigned long event, ...@@ -394,6 +404,30 @@ static int ib_security_change(struct notifier_block *nb, unsigned long event,
return NOTIFY_OK; return NOTIFY_OK;
} }
/**
* __dev_new_index - allocate an device index
*
* Returns a suitable unique value for a new device interface
* number. It assumes that there are less than 2^32-1 ib devices
* will be present in the system.
*/
static u32 __dev_new_index(void)
{
/*
* The device index to allow stable naming.
* Similar to struct net -> ifindex.
*/
static u32 index;
for (;;) {
if (!(++index))
index = 1;
if (!__ib_device_get_by_index(index))
return index;
}
}
/** /**
* ib_register_device - Register an IB device with IB core * ib_register_device - Register an IB device with IB core
* @device:Device to register * @device:Device to register
...@@ -489,9 +523,10 @@ int ib_register_device(struct ib_device *device, ...@@ -489,9 +523,10 @@ int ib_register_device(struct ib_device *device,
device->reg_state = IB_DEV_REGISTERED; device->reg_state = IB_DEV_REGISTERED;
list_for_each_entry(client, &client_list, list) list_for_each_entry(client, &client_list, list)
if (client->add && !add_client_context(device, client)) if (!add_client_context(device, client) && client->add)
client->add(device); client->add(device);
device->index = __dev_new_index();
down_write(&lists_rwsem); down_write(&lists_rwsem);
list_add_tail(&device->core_list, &device_list); list_add_tail(&device->core_list, &device_list);
up_write(&lists_rwsem); up_write(&lists_rwsem);
...@@ -578,7 +613,7 @@ int ib_register_client(struct ib_client *client) ...@@ -578,7 +613,7 @@ int ib_register_client(struct ib_client *client)
mutex_lock(&device_mutex); mutex_lock(&device_mutex);
list_for_each_entry(device, &device_list, core_list) list_for_each_entry(device, &device_list, core_list)
if (client->add && !add_client_context(device, client)) if (!add_client_context(device, client) && client->add)
client->add(device); client->add(device);
down_write(&lists_rwsem); down_write(&lists_rwsem);
...@@ -712,7 +747,7 @@ EXPORT_SYMBOL(ib_set_client_data); ...@@ -712,7 +747,7 @@ EXPORT_SYMBOL(ib_set_client_data);
* chapter 11 of the InfiniBand Architecture Specification). This * chapter 11 of the InfiniBand Architecture Specification). This
* callback may occur in interrupt context. * callback may occur in interrupt context.
*/ */
int ib_register_event_handler (struct ib_event_handler *event_handler) void ib_register_event_handler(struct ib_event_handler *event_handler)
{ {
unsigned long flags; unsigned long flags;
...@@ -720,8 +755,6 @@ int ib_register_event_handler (struct ib_event_handler *event_handler) ...@@ -720,8 +755,6 @@ int ib_register_event_handler (struct ib_event_handler *event_handler)
list_add_tail(&event_handler->list, list_add_tail(&event_handler->list,
&event_handler->device->event_handler_list); &event_handler->device->event_handler_list);
spin_unlock_irqrestore(&event_handler->device->event_handler_lock, flags); spin_unlock_irqrestore(&event_handler->device->event_handler_lock, flags);
return 0;
} }
EXPORT_SYMBOL(ib_register_event_handler); EXPORT_SYMBOL(ib_register_event_handler);
...@@ -732,15 +765,13 @@ EXPORT_SYMBOL(ib_register_event_handler); ...@@ -732,15 +765,13 @@ EXPORT_SYMBOL(ib_register_event_handler);
* Unregister an event handler registered with * Unregister an event handler registered with
* ib_register_event_handler(). * ib_register_event_handler().
*/ */
int ib_unregister_event_handler(struct ib_event_handler *event_handler) void ib_unregister_event_handler(struct ib_event_handler *event_handler)
{ {
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&event_handler->device->event_handler_lock, flags); spin_lock_irqsave(&event_handler->device->event_handler_lock, flags);
list_del(&event_handler->list); list_del(&event_handler->list);
spin_unlock_irqrestore(&event_handler->device->event_handler_lock, flags); spin_unlock_irqrestore(&event_handler->device->event_handler_lock, flags);
return 0;
} }
EXPORT_SYMBOL(ib_unregister_event_handler); EXPORT_SYMBOL(ib_unregister_event_handler);
...@@ -893,6 +924,31 @@ void ib_enum_all_roce_netdevs(roce_netdev_filter filter, ...@@ -893,6 +924,31 @@ void ib_enum_all_roce_netdevs(roce_netdev_filter filter,
up_read(&lists_rwsem); up_read(&lists_rwsem);
} }
/**
* ib_enum_all_devs - enumerate all ib_devices
* @cb: Callback to call for each found ib_device
*
* Enumerates all ib_devices and calls callback() on each device.
*/
int ib_enum_all_devs(nldev_callback nldev_cb, struct sk_buff *skb,
struct netlink_callback *cb)
{
struct ib_device *dev;
unsigned int idx = 0;
int ret = 0;
down_read(&lists_rwsem);
list_for_each_entry(dev, &device_list, core_list) {
ret = nldev_cb(dev, skb, cb, idx);
if (ret)
break;
idx++;
}
up_read(&lists_rwsem);
return ret;
}
/** /**
* ib_query_pkey - Get P_Key table entry * ib_query_pkey - Get P_Key table entry
* @device:Device to query * @device:Device to query
...@@ -945,14 +1001,17 @@ int ib_modify_port(struct ib_device *device, ...@@ -945,14 +1001,17 @@ int ib_modify_port(struct ib_device *device,
u8 port_num, int port_modify_mask, u8 port_num, int port_modify_mask,
struct ib_port_modify *port_modify) struct ib_port_modify *port_modify)
{ {
if (!device->modify_port) int rc;
return -ENOSYS;
if (!rdma_is_port_valid(device, port_num)) if (!rdma_is_port_valid(device, port_num))
return -EINVAL; return -EINVAL;
return device->modify_port(device, port_num, port_modify_mask, if (device->modify_port)
port_modify); rc = device->modify_port(device, port_num, port_modify_mask,
port_modify);
else
rc = rdma_protocol_roce(device, port_num) ? 0 : -ENOSYS;
return rc;
} }
EXPORT_SYMBOL(ib_modify_port); EXPORT_SYMBOL(ib_modify_port);
...@@ -1087,29 +1146,21 @@ struct net_device *ib_get_net_dev_by_params(struct ib_device *dev, ...@@ -1087,29 +1146,21 @@ struct net_device *ib_get_net_dev_by_params(struct ib_device *dev,
} }
EXPORT_SYMBOL(ib_get_net_dev_by_params); EXPORT_SYMBOL(ib_get_net_dev_by_params);
static struct ibnl_client_cbs ibnl_ls_cb_table[] = { static const struct rdma_nl_cbs ibnl_ls_cb_table[] = {
[RDMA_NL_LS_OP_RESOLVE] = { [RDMA_NL_LS_OP_RESOLVE] = {
.dump = ib_nl_handle_resolve_resp, .doit = ib_nl_handle_resolve_resp,
.module = THIS_MODULE }, .flags = RDMA_NL_ADMIN_PERM,
},
[RDMA_NL_LS_OP_SET_TIMEOUT] = { [RDMA_NL_LS_OP_SET_TIMEOUT] = {
.dump = ib_nl_handle_set_timeout, .doit = ib_nl_handle_set_timeout,
.module = THIS_MODULE }, .flags = RDMA_NL_ADMIN_PERM,
},
[RDMA_NL_LS_OP_IP_RESOLVE] = { [RDMA_NL_LS_OP_IP_RESOLVE] = {
.dump = ib_nl_handle_ip_res_resp, .doit = ib_nl_handle_ip_res_resp,
.module = THIS_MODULE }, .flags = RDMA_NL_ADMIN_PERM,
},
}; };
static int ib_add_ibnl_clients(void)
{
return ibnl_add_client(RDMA_NL_LS, ARRAY_SIZE(ibnl_ls_cb_table),
ibnl_ls_cb_table);
}
static void ib_remove_ibnl_clients(void)
{
ibnl_remove_client(RDMA_NL_LS);
}
static int __init ib_core_init(void) static int __init ib_core_init(void)
{ {
int ret; int ret;
...@@ -1131,9 +1182,9 @@ static int __init ib_core_init(void) ...@@ -1131,9 +1182,9 @@ static int __init ib_core_init(void)
goto err_comp; goto err_comp;
} }
ret = ibnl_init(); ret = rdma_nl_init();
if (ret) { if (ret) {
pr_warn("Couldn't init IB netlink interface\n"); pr_warn("Couldn't init IB netlink interface: err %d\n", ret);
goto err_sysfs; goto err_sysfs;
} }
...@@ -1155,24 +1206,18 @@ static int __init ib_core_init(void) ...@@ -1155,24 +1206,18 @@ static int __init ib_core_init(void)
goto err_mad; goto err_mad;
} }
ret = ib_add_ibnl_clients();
if (ret) {
pr_warn("Couldn't register ibnl clients\n");
goto err_sa;
}
ret = register_lsm_notifier(&ibdev_lsm_nb); ret = register_lsm_notifier(&ibdev_lsm_nb);
if (ret) { if (ret) {
pr_warn("Couldn't register LSM notifier. ret %d\n", ret); pr_warn("Couldn't register LSM notifier. ret %d\n", ret);
goto err_ibnl_clients; goto err_sa;
} }
nldev_init();
rdma_nl_register(RDMA_NL_LS, ibnl_ls_cb_table);
ib_cache_setup(); ib_cache_setup();
return 0; return 0;
err_ibnl_clients:
ib_remove_ibnl_clients();
err_sa: err_sa:
ib_sa_cleanup(); ib_sa_cleanup();
err_mad: err_mad:
...@@ -1180,7 +1225,7 @@ static int __init ib_core_init(void) ...@@ -1180,7 +1225,7 @@ static int __init ib_core_init(void)
err_addr: err_addr:
addr_cleanup(); addr_cleanup();
err_ibnl: err_ibnl:
ibnl_cleanup(); rdma_nl_exit();
err_sysfs: err_sysfs:
class_unregister(&ib_class); class_unregister(&ib_class);
err_comp: err_comp:
...@@ -1192,18 +1237,21 @@ static int __init ib_core_init(void) ...@@ -1192,18 +1237,21 @@ static int __init ib_core_init(void)
static void __exit ib_core_cleanup(void) static void __exit ib_core_cleanup(void)
{ {
unregister_lsm_notifier(&ibdev_lsm_nb);
ib_cache_cleanup(); ib_cache_cleanup();
ib_remove_ibnl_clients(); nldev_exit();
rdma_nl_unregister(RDMA_NL_LS);
unregister_lsm_notifier(&ibdev_lsm_nb);
ib_sa_cleanup(); ib_sa_cleanup();
ib_mad_cleanup(); ib_mad_cleanup();
addr_cleanup(); addr_cleanup();
ibnl_cleanup(); rdma_nl_exit();
class_unregister(&ib_class); class_unregister(&ib_class);
destroy_workqueue(ib_comp_wq); destroy_workqueue(ib_comp_wq);
/* Make sure that any pending umem accounting work is done. */ /* Make sure that any pending umem accounting work is done. */
destroy_workqueue(ib_wq); destroy_workqueue(ib_wq);
} }
MODULE_ALIAS_RDMA_NETLINK(RDMA_NL_LS, 4);
module_init(ib_core_init); module_init(ib_core_init);
module_exit(ib_core_cleanup); module_exit(ib_core_cleanup);
...@@ -80,7 +80,7 @@ const char *__attribute_const__ iwcm_reject_msg(int reason) ...@@ -80,7 +80,7 @@ const char *__attribute_const__ iwcm_reject_msg(int reason)
} }
EXPORT_SYMBOL(iwcm_reject_msg); EXPORT_SYMBOL(iwcm_reject_msg);
static struct ibnl_client_cbs iwcm_nl_cb_table[] = { static struct rdma_nl_cbs iwcm_nl_cb_table[] = {
[RDMA_NL_IWPM_REG_PID] = {.dump = iwpm_register_pid_cb}, [RDMA_NL_IWPM_REG_PID] = {.dump = iwpm_register_pid_cb},
[RDMA_NL_IWPM_ADD_MAPPING] = {.dump = iwpm_add_mapping_cb}, [RDMA_NL_IWPM_ADD_MAPPING] = {.dump = iwpm_add_mapping_cb},
[RDMA_NL_IWPM_QUERY_MAPPING] = {.dump = iwpm_add_and_query_mapping_cb}, [RDMA_NL_IWPM_QUERY_MAPPING] = {.dump = iwpm_add_and_query_mapping_cb},
...@@ -1175,13 +1175,9 @@ static int __init iw_cm_init(void) ...@@ -1175,13 +1175,9 @@ static int __init iw_cm_init(void)
ret = iwpm_init(RDMA_NL_IWCM); ret = iwpm_init(RDMA_NL_IWCM);
if (ret) if (ret)
pr_err("iw_cm: couldn't init iwpm\n"); pr_err("iw_cm: couldn't init iwpm\n");
else
ret = ibnl_add_client(RDMA_NL_IWCM, ARRAY_SIZE(iwcm_nl_cb_table), rdma_nl_register(RDMA_NL_IWCM, iwcm_nl_cb_table);
iwcm_nl_cb_table); iwcm_wq = alloc_ordered_workqueue("iw_cm_wq", 0);
if (ret)
pr_err("iw_cm: couldn't register netlink callbacks\n");
iwcm_wq = alloc_ordered_workqueue("iw_cm_wq", WQ_MEM_RECLAIM);
if (!iwcm_wq) if (!iwcm_wq)
return -ENOMEM; return -ENOMEM;
...@@ -1200,9 +1196,11 @@ static void __exit iw_cm_cleanup(void) ...@@ -1200,9 +1196,11 @@ static void __exit iw_cm_cleanup(void)
{ {
unregister_net_sysctl_table(iwcm_ctl_table_hdr); unregister_net_sysctl_table(iwcm_ctl_table_hdr);
destroy_workqueue(iwcm_wq); destroy_workqueue(iwcm_wq);
ibnl_remove_client(RDMA_NL_IWCM); rdma_nl_unregister(RDMA_NL_IWCM);
iwpm_exit(RDMA_NL_IWCM); iwpm_exit(RDMA_NL_IWCM);
} }
MODULE_ALIAS_RDMA_NETLINK(RDMA_NL_IWCM, 2);
module_init(iw_cm_init); module_init(iw_cm_init);
module_exit(iw_cm_cleanup); module_exit(iw_cm_cleanup);
...@@ -42,7 +42,6 @@ int iwpm_valid_pid(void) ...@@ -42,7 +42,6 @@ int iwpm_valid_pid(void)
{ {
return iwpm_user_pid > 0; return iwpm_user_pid > 0;
} }
EXPORT_SYMBOL(iwpm_valid_pid);
/* /*
* iwpm_register_pid - Send a netlink query to user space * iwpm_register_pid - Send a netlink query to user space
...@@ -104,7 +103,7 @@ int iwpm_register_pid(struct iwpm_dev_data *pm_msg, u8 nl_client) ...@@ -104,7 +103,7 @@ int iwpm_register_pid(struct iwpm_dev_data *pm_msg, u8 nl_client)
pr_debug("%s: Multicasting a nlmsg (dev = %s ifname = %s iwpm = %s)\n", pr_debug("%s: Multicasting a nlmsg (dev = %s ifname = %s iwpm = %s)\n",
__func__, pm_msg->dev_name, pm_msg->if_name, iwpm_ulib_name); __func__, pm_msg->dev_name, pm_msg->if_name, iwpm_ulib_name);
ret = ibnl_multicast(skb, nlh, RDMA_NL_GROUP_IWPM, GFP_KERNEL); ret = rdma_nl_multicast(skb, RDMA_NL_GROUP_IWPM, GFP_KERNEL);
if (ret) { if (ret) {
skb = NULL; /* skb is freed in the netlink send-op handling */ skb = NULL; /* skb is freed in the netlink send-op handling */
iwpm_user_pid = IWPM_PID_UNAVAILABLE; iwpm_user_pid = IWPM_PID_UNAVAILABLE;
...@@ -122,7 +121,6 @@ int iwpm_register_pid(struct iwpm_dev_data *pm_msg, u8 nl_client) ...@@ -122,7 +121,6 @@ int iwpm_register_pid(struct iwpm_dev_data *pm_msg, u8 nl_client)
iwpm_free_nlmsg_request(&nlmsg_request->kref); iwpm_free_nlmsg_request(&nlmsg_request->kref);
return ret; return ret;
} }
EXPORT_SYMBOL(iwpm_register_pid);
/* /*
* iwpm_add_mapping - Send a netlink add mapping message * iwpm_add_mapping - Send a netlink add mapping message
...@@ -174,7 +172,7 @@ int iwpm_add_mapping(struct iwpm_sa_data *pm_msg, u8 nl_client) ...@@ -174,7 +172,7 @@ int iwpm_add_mapping(struct iwpm_sa_data *pm_msg, u8 nl_client)
goto add_mapping_error; goto add_mapping_error;
nlmsg_request->req_buffer = pm_msg; nlmsg_request->req_buffer = pm_msg;
ret = ibnl_unicast(skb, nlh, iwpm_user_pid); ret = rdma_nl_unicast_wait(skb, iwpm_user_pid);
if (ret) { if (ret) {
skb = NULL; /* skb is freed in the netlink send-op handling */ skb = NULL; /* skb is freed in the netlink send-op handling */
iwpm_user_pid = IWPM_PID_UNDEFINED; iwpm_user_pid = IWPM_PID_UNDEFINED;
...@@ -191,7 +189,6 @@ int iwpm_add_mapping(struct iwpm_sa_data *pm_msg, u8 nl_client) ...@@ -191,7 +189,6 @@ int iwpm_add_mapping(struct iwpm_sa_data *pm_msg, u8 nl_client)
iwpm_free_nlmsg_request(&nlmsg_request->kref); iwpm_free_nlmsg_request(&nlmsg_request->kref);
return ret; return ret;
} }
EXPORT_SYMBOL(iwpm_add_mapping);
/* /*
* iwpm_add_and_query_mapping - Send a netlink add and query * iwpm_add_and_query_mapping - Send a netlink add and query
...@@ -251,7 +248,7 @@ int iwpm_add_and_query_mapping(struct iwpm_sa_data *pm_msg, u8 nl_client) ...@@ -251,7 +248,7 @@ int iwpm_add_and_query_mapping(struct iwpm_sa_data *pm_msg, u8 nl_client)
goto query_mapping_error; goto query_mapping_error;
nlmsg_request->req_buffer = pm_msg; nlmsg_request->req_buffer = pm_msg;
ret = ibnl_unicast(skb, nlh, iwpm_user_pid); ret = rdma_nl_unicast_wait(skb, iwpm_user_pid);
if (ret) { if (ret) {
skb = NULL; /* skb is freed in the netlink send-op handling */ skb = NULL; /* skb is freed in the netlink send-op handling */
err_str = "Unable to send a nlmsg"; err_str = "Unable to send a nlmsg";
...@@ -267,7 +264,6 @@ int iwpm_add_and_query_mapping(struct iwpm_sa_data *pm_msg, u8 nl_client) ...@@ -267,7 +264,6 @@ int iwpm_add_and_query_mapping(struct iwpm_sa_data *pm_msg, u8 nl_client)
iwpm_free_nlmsg_request(&nlmsg_request->kref); iwpm_free_nlmsg_request(&nlmsg_request->kref);
return ret; return ret;
} }
EXPORT_SYMBOL(iwpm_add_and_query_mapping);
/* /*
* iwpm_remove_mapping - Send a netlink remove mapping message * iwpm_remove_mapping - Send a netlink remove mapping message
...@@ -312,7 +308,7 @@ int iwpm_remove_mapping(struct sockaddr_storage *local_addr, u8 nl_client) ...@@ -312,7 +308,7 @@ int iwpm_remove_mapping(struct sockaddr_storage *local_addr, u8 nl_client)
if (ret) if (ret)
goto remove_mapping_error; goto remove_mapping_error;
ret = ibnl_unicast(skb, nlh, iwpm_user_pid); ret = rdma_nl_unicast_wait(skb, iwpm_user_pid);
if (ret) { if (ret) {
skb = NULL; /* skb is freed in the netlink send-op handling */ skb = NULL; /* skb is freed in the netlink send-op handling */
iwpm_user_pid = IWPM_PID_UNDEFINED; iwpm_user_pid = IWPM_PID_UNDEFINED;
...@@ -328,7 +324,6 @@ int iwpm_remove_mapping(struct sockaddr_storage *local_addr, u8 nl_client) ...@@ -328,7 +324,6 @@ int iwpm_remove_mapping(struct sockaddr_storage *local_addr, u8 nl_client)
dev_kfree_skb_any(skb); dev_kfree_skb_any(skb);
return ret; return ret;
} }
EXPORT_SYMBOL(iwpm_remove_mapping);
/* netlink attribute policy for the received response to register pid request */ /* netlink attribute policy for the received response to register pid request */
static const struct nla_policy resp_reg_policy[IWPM_NLA_RREG_PID_MAX] = { static const struct nla_policy resp_reg_policy[IWPM_NLA_RREG_PID_MAX] = {
...@@ -397,7 +392,6 @@ int iwpm_register_pid_cb(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -397,7 +392,6 @@ int iwpm_register_pid_cb(struct sk_buff *skb, struct netlink_callback *cb)
up(&nlmsg_request->sem); up(&nlmsg_request->sem);
return 0; return 0;
} }
EXPORT_SYMBOL(iwpm_register_pid_cb);
/* netlink attribute policy for the received response to add mapping request */ /* netlink attribute policy for the received response to add mapping request */
static const struct nla_policy resp_add_policy[IWPM_NLA_RMANAGE_MAPPING_MAX] = { static const struct nla_policy resp_add_policy[IWPM_NLA_RMANAGE_MAPPING_MAX] = {
...@@ -466,7 +460,6 @@ int iwpm_add_mapping_cb(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -466,7 +460,6 @@ int iwpm_add_mapping_cb(struct sk_buff *skb, struct netlink_callback *cb)
up(&nlmsg_request->sem); up(&nlmsg_request->sem);
return 0; return 0;
} }
EXPORT_SYMBOL(iwpm_add_mapping_cb);
/* netlink attribute policy for the response to add and query mapping request /* netlink attribute policy for the response to add and query mapping request
* and response with remote address info */ * and response with remote address info */
...@@ -558,7 +551,6 @@ int iwpm_add_and_query_mapping_cb(struct sk_buff *skb, ...@@ -558,7 +551,6 @@ int iwpm_add_and_query_mapping_cb(struct sk_buff *skb,
up(&nlmsg_request->sem); up(&nlmsg_request->sem);
return 0; return 0;
} }
EXPORT_SYMBOL(iwpm_add_and_query_mapping_cb);
/* /*
* iwpm_remote_info_cb - Process a port mapper message, containing * iwpm_remote_info_cb - Process a port mapper message, containing
...@@ -627,7 +619,6 @@ int iwpm_remote_info_cb(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -627,7 +619,6 @@ int iwpm_remote_info_cb(struct sk_buff *skb, struct netlink_callback *cb)
"remote_info: Mapped remote sockaddr:"); "remote_info: Mapped remote sockaddr:");
return ret; return ret;
} }
EXPORT_SYMBOL(iwpm_remote_info_cb);
/* netlink attribute policy for the received request for mapping info */ /* netlink attribute policy for the received request for mapping info */
static const struct nla_policy resp_mapinfo_policy[IWPM_NLA_MAPINFO_REQ_MAX] = { static const struct nla_policy resp_mapinfo_policy[IWPM_NLA_MAPINFO_REQ_MAX] = {
...@@ -677,7 +668,6 @@ int iwpm_mapping_info_cb(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -677,7 +668,6 @@ int iwpm_mapping_info_cb(struct sk_buff *skb, struct netlink_callback *cb)
ret = iwpm_send_mapinfo(nl_client, iwpm_user_pid); ret = iwpm_send_mapinfo(nl_client, iwpm_user_pid);
return ret; return ret;
} }
EXPORT_SYMBOL(iwpm_mapping_info_cb);
/* netlink attribute policy for the received mapping info ack */ /* netlink attribute policy for the received mapping info ack */
static const struct nla_policy ack_mapinfo_policy[IWPM_NLA_MAPINFO_NUM_MAX] = { static const struct nla_policy ack_mapinfo_policy[IWPM_NLA_MAPINFO_NUM_MAX] = {
...@@ -707,7 +697,6 @@ int iwpm_ack_mapping_info_cb(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -707,7 +697,6 @@ int iwpm_ack_mapping_info_cb(struct sk_buff *skb, struct netlink_callback *cb)
atomic_set(&echo_nlmsg_seq, cb->nlh->nlmsg_seq); atomic_set(&echo_nlmsg_seq, cb->nlh->nlmsg_seq);
return 0; return 0;
} }
EXPORT_SYMBOL(iwpm_ack_mapping_info_cb);
/* netlink attribute policy for the received port mapper error message */ /* netlink attribute policy for the received port mapper error message */
static const struct nla_policy map_error_policy[IWPM_NLA_ERR_MAX] = { static const struct nla_policy map_error_policy[IWPM_NLA_ERR_MAX] = {
...@@ -751,4 +740,3 @@ int iwpm_mapping_error_cb(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -751,4 +740,3 @@ int iwpm_mapping_error_cb(struct sk_buff *skb, struct netlink_callback *cb)
up(&nlmsg_request->sem); up(&nlmsg_request->sem);
return 0; return 0;
} }
EXPORT_SYMBOL(iwpm_mapping_error_cb);
...@@ -54,8 +54,6 @@ static struct iwpm_admin_data iwpm_admin; ...@@ -54,8 +54,6 @@ static struct iwpm_admin_data iwpm_admin;
int iwpm_init(u8 nl_client) int iwpm_init(u8 nl_client)
{ {
int ret = 0; int ret = 0;
if (iwpm_valid_client(nl_client))
return -EINVAL;
mutex_lock(&iwpm_admin_lock); mutex_lock(&iwpm_admin_lock);
if (atomic_read(&iwpm_admin.refcount) == 0) { if (atomic_read(&iwpm_admin.refcount) == 0) {
iwpm_hash_bucket = kzalloc(IWPM_MAPINFO_HASH_SIZE * iwpm_hash_bucket = kzalloc(IWPM_MAPINFO_HASH_SIZE *
...@@ -83,7 +81,6 @@ int iwpm_init(u8 nl_client) ...@@ -83,7 +81,6 @@ int iwpm_init(u8 nl_client)
} }
return ret; return ret;
} }
EXPORT_SYMBOL(iwpm_init);
static void free_hash_bucket(void); static void free_hash_bucket(void);
static void free_reminfo_bucket(void); static void free_reminfo_bucket(void);
...@@ -109,7 +106,6 @@ int iwpm_exit(u8 nl_client) ...@@ -109,7 +106,6 @@ int iwpm_exit(u8 nl_client)
iwpm_set_registration(nl_client, IWPM_REG_UNDEF); iwpm_set_registration(nl_client, IWPM_REG_UNDEF);
return 0; return 0;
} }
EXPORT_SYMBOL(iwpm_exit);
static struct hlist_head *get_mapinfo_hash_bucket(struct sockaddr_storage *, static struct hlist_head *get_mapinfo_hash_bucket(struct sockaddr_storage *,
struct sockaddr_storage *); struct sockaddr_storage *);
...@@ -148,7 +144,6 @@ int iwpm_create_mapinfo(struct sockaddr_storage *local_sockaddr, ...@@ -148,7 +144,6 @@ int iwpm_create_mapinfo(struct sockaddr_storage *local_sockaddr,
spin_unlock_irqrestore(&iwpm_mapinfo_lock, flags); spin_unlock_irqrestore(&iwpm_mapinfo_lock, flags);
return ret; return ret;
} }
EXPORT_SYMBOL(iwpm_create_mapinfo);
int iwpm_remove_mapinfo(struct sockaddr_storage *local_sockaddr, int iwpm_remove_mapinfo(struct sockaddr_storage *local_sockaddr,
struct sockaddr_storage *mapped_local_addr) struct sockaddr_storage *mapped_local_addr)
...@@ -184,7 +179,6 @@ int iwpm_remove_mapinfo(struct sockaddr_storage *local_sockaddr, ...@@ -184,7 +179,6 @@ int iwpm_remove_mapinfo(struct sockaddr_storage *local_sockaddr,
spin_unlock_irqrestore(&iwpm_mapinfo_lock, flags); spin_unlock_irqrestore(&iwpm_mapinfo_lock, flags);
return ret; return ret;
} }
EXPORT_SYMBOL(iwpm_remove_mapinfo);
static void free_hash_bucket(void) static void free_hash_bucket(void)
{ {
...@@ -297,7 +291,6 @@ int iwpm_get_remote_info(struct sockaddr_storage *mapped_loc_addr, ...@@ -297,7 +291,6 @@ int iwpm_get_remote_info(struct sockaddr_storage *mapped_loc_addr,
spin_unlock_irqrestore(&iwpm_reminfo_lock, flags); spin_unlock_irqrestore(&iwpm_reminfo_lock, flags);
return ret; return ret;
} }
EXPORT_SYMBOL(iwpm_get_remote_info);
struct iwpm_nlmsg_request *iwpm_get_nlmsg_request(__u32 nlmsg_seq, struct iwpm_nlmsg_request *iwpm_get_nlmsg_request(__u32 nlmsg_seq,
u8 nl_client, gfp_t gfp) u8 nl_client, gfp_t gfp)
...@@ -383,15 +376,11 @@ int iwpm_get_nlmsg_seq(void) ...@@ -383,15 +376,11 @@ int iwpm_get_nlmsg_seq(void)
int iwpm_valid_client(u8 nl_client) int iwpm_valid_client(u8 nl_client)
{ {
if (nl_client >= RDMA_NL_NUM_CLIENTS)
return 0;
return iwpm_admin.client_list[nl_client]; return iwpm_admin.client_list[nl_client];
} }
void iwpm_set_valid(u8 nl_client, int valid) void iwpm_set_valid(u8 nl_client, int valid)
{ {
if (nl_client >= RDMA_NL_NUM_CLIENTS)
return;
iwpm_admin.client_list[nl_client] = valid; iwpm_admin.client_list[nl_client] = valid;
} }
...@@ -608,7 +597,7 @@ static int send_mapinfo_num(u32 mapping_num, u8 nl_client, int iwpm_pid) ...@@ -608,7 +597,7 @@ static int send_mapinfo_num(u32 mapping_num, u8 nl_client, int iwpm_pid)
&mapping_num, IWPM_NLA_MAPINFO_SEND_NUM); &mapping_num, IWPM_NLA_MAPINFO_SEND_NUM);
if (ret) if (ret)
goto mapinfo_num_error; goto mapinfo_num_error;
ret = ibnl_unicast(skb, nlh, iwpm_pid); ret = rdma_nl_unicast(skb, iwpm_pid);
if (ret) { if (ret) {
skb = NULL; skb = NULL;
err_str = "Unable to send a nlmsg"; err_str = "Unable to send a nlmsg";
...@@ -637,7 +626,7 @@ static int send_nlmsg_done(struct sk_buff *skb, u8 nl_client, int iwpm_pid) ...@@ -637,7 +626,7 @@ static int send_nlmsg_done(struct sk_buff *skb, u8 nl_client, int iwpm_pid)
return -ENOMEM; return -ENOMEM;
} }
nlh->nlmsg_type = NLMSG_DONE; nlh->nlmsg_type = NLMSG_DONE;
ret = ibnl_unicast(skb, (struct nlmsghdr *)skb->data, iwpm_pid); ret = rdma_nl_unicast(skb, iwpm_pid);
if (ret) if (ret)
pr_warn("%s Unable to send a nlmsg\n", __func__); pr_warn("%s Unable to send a nlmsg\n", __func__);
return ret; return ret;
......
...@@ -64,7 +64,7 @@ struct mad_rmpp_recv { ...@@ -64,7 +64,7 @@ struct mad_rmpp_recv {
__be64 tid; __be64 tid;
u32 src_qp; u32 src_qp;
u16 slid; u32 slid;
u8 mgmt_class; u8 mgmt_class;
u8 class_version; u8 class_version;
u8 method; u8 method;
......
/* /*
* Copyright (c) 2017 Mellanox Technologies Inc. All rights reserved.
* Copyright (c) 2010 Voltaire Inc. All rights reserved. * Copyright (c) 2010 Voltaire Inc. All rights reserved.
* *
* This software is available to you under a choice of one of two * This software is available to you under a choice of one of two
...@@ -37,239 +38,267 @@ ...@@ -37,239 +38,267 @@
#include <net/net_namespace.h> #include <net/net_namespace.h>
#include <net/sock.h> #include <net/sock.h>
#include <rdma/rdma_netlink.h> #include <rdma/rdma_netlink.h>
#include <linux/module.h>
#include "core_priv.h" #include "core_priv.h"
struct ibnl_client { #include "core_priv.h"
struct list_head list;
int index;
int nops;
const struct ibnl_client_cbs *cb_table;
};
static DEFINE_MUTEX(ibnl_mutex); static DEFINE_MUTEX(rdma_nl_mutex);
static struct sock *nls; static struct sock *nls;
static LIST_HEAD(client_list); static struct {
const struct rdma_nl_cbs *cb_table;
} rdma_nl_types[RDMA_NL_NUM_CLIENTS];
int ibnl_chk_listeners(unsigned int group) int rdma_nl_chk_listeners(unsigned int group)
{ {
if (netlink_has_listeners(nls, group) == 0) return (netlink_has_listeners(nls, group)) ? 0 : -1;
return -1;
return 0;
} }
EXPORT_SYMBOL(rdma_nl_chk_listeners);
int ibnl_add_client(int index, int nops, static bool is_nl_msg_valid(unsigned int type, unsigned int op)
const struct ibnl_client_cbs cb_table[])
{ {
struct ibnl_client *cur; static const unsigned int max_num_ops[RDMA_NL_NUM_CLIENTS - 1] = {
struct ibnl_client *nl_client; RDMA_NL_RDMA_CM_NUM_OPS,
RDMA_NL_IWPM_NUM_OPS,
0,
RDMA_NL_LS_NUM_OPS,
RDMA_NLDEV_NUM_OPS };
nl_client = kmalloc(sizeof *nl_client, GFP_KERNEL); /*
if (!nl_client) * This BUILD_BUG_ON is intended to catch addition of new
return -ENOMEM; * RDMA netlink protocol without updating the array above.
*/
BUILD_BUG_ON(RDMA_NL_NUM_CLIENTS != 6);
nl_client->index = index; if (type > RDMA_NL_NUM_CLIENTS - 1)
nl_client->nops = nops; return false;
nl_client->cb_table = cb_table;
mutex_lock(&ibnl_mutex); return (op < max_num_ops[type - 1]) ? true : false;
}
list_for_each_entry(cur, &client_list, list) { static bool is_nl_valid(unsigned int type, unsigned int op)
if (cur->index == index) { {
pr_warn("Client for %d already exists\n", index); const struct rdma_nl_cbs *cb_table;
mutex_unlock(&ibnl_mutex);
kfree(nl_client); if (!is_nl_msg_valid(type, op))
return -EINVAL; return false;
}
cb_table = rdma_nl_types[type].cb_table;
#ifdef CONFIG_MODULES
if (!cb_table) {
mutex_unlock(&rdma_nl_mutex);
request_module("rdma-netlink-subsys-%d", type);
mutex_lock(&rdma_nl_mutex);
cb_table = rdma_nl_types[type].cb_table;
} }
#endif
list_add_tail(&nl_client->list, &client_list); if (!cb_table || (!cb_table[op].dump && !cb_table[op].doit))
return false;
mutex_unlock(&ibnl_mutex); return true;
return 0;
} }
EXPORT_SYMBOL(ibnl_add_client);
int ibnl_remove_client(int index) void rdma_nl_register(unsigned int index,
const struct rdma_nl_cbs cb_table[])
{ {
struct ibnl_client *cur, *next; mutex_lock(&rdma_nl_mutex);
if (!is_nl_msg_valid(index, 0)) {
mutex_lock(&ibnl_mutex); /*
list_for_each_entry_safe(cur, next, &client_list, list) { * All clients are not interesting in success/failure of
if (cur->index == index) { * this call. They want to see the print to error log and
list_del(&(cur->list)); * continue their initialization. Print warning for them,
mutex_unlock(&ibnl_mutex); * because it is programmer's error to be here.
kfree(cur); */
return 0; mutex_unlock(&rdma_nl_mutex);
} WARN(true,
"The not-valid %u index was supplied to RDMA netlink\n",
index);
return;
} }
pr_warn("Can't remove callback for client idx %d. Not found\n", index);
mutex_unlock(&ibnl_mutex);
return -EINVAL; if (rdma_nl_types[index].cb_table) {
mutex_unlock(&rdma_nl_mutex);
WARN(true,
"The %u index is already registered in RDMA netlink\n",
index);
return;
}
rdma_nl_types[index].cb_table = cb_table;
mutex_unlock(&rdma_nl_mutex);
}
EXPORT_SYMBOL(rdma_nl_register);
void rdma_nl_unregister(unsigned int index)
{
mutex_lock(&rdma_nl_mutex);
rdma_nl_types[index].cb_table = NULL;
mutex_unlock(&rdma_nl_mutex);
} }
EXPORT_SYMBOL(ibnl_remove_client); EXPORT_SYMBOL(rdma_nl_unregister);
void *ibnl_put_msg(struct sk_buff *skb, struct nlmsghdr **nlh, int seq, void *ibnl_put_msg(struct sk_buff *skb, struct nlmsghdr **nlh, int seq,
int len, int client, int op, int flags) int len, int client, int op, int flags)
{ {
unsigned char *prev_tail; *nlh = nlmsg_put(skb, 0, seq, RDMA_NL_GET_TYPE(client, op), len, flags);
prev_tail = skb_tail_pointer(skb);
*nlh = nlmsg_put(skb, 0, seq, RDMA_NL_GET_TYPE(client, op),
len, flags);
if (!*nlh) if (!*nlh)
goto out_nlmsg_trim; return NULL;
(*nlh)->nlmsg_len = skb_tail_pointer(skb) - prev_tail;
return nlmsg_data(*nlh); return nlmsg_data(*nlh);
out_nlmsg_trim:
nlmsg_trim(skb, prev_tail);
return NULL;
} }
EXPORT_SYMBOL(ibnl_put_msg); EXPORT_SYMBOL(ibnl_put_msg);
int ibnl_put_attr(struct sk_buff *skb, struct nlmsghdr *nlh, int ibnl_put_attr(struct sk_buff *skb, struct nlmsghdr *nlh,
int len, void *data, int type) int len, void *data, int type)
{ {
unsigned char *prev_tail; if (nla_put(skb, type, len, data)) {
nlmsg_cancel(skb, nlh);
prev_tail = skb_tail_pointer(skb); return -EMSGSIZE;
if (nla_put(skb, type, len, data)) }
goto nla_put_failure;
nlh->nlmsg_len += skb_tail_pointer(skb) - prev_tail;
return 0; return 0;
nla_put_failure:
nlmsg_trim(skb, prev_tail - nlh->nlmsg_len);
return -EMSGSIZE;
} }
EXPORT_SYMBOL(ibnl_put_attr); EXPORT_SYMBOL(ibnl_put_attr);
static int ibnl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, static int rdma_nl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
{ {
struct ibnl_client *client;
int type = nlh->nlmsg_type; int type = nlh->nlmsg_type;
int index = RDMA_NL_GET_CLIENT(type); unsigned int index = RDMA_NL_GET_CLIENT(type);
unsigned int op = RDMA_NL_GET_OP(type); unsigned int op = RDMA_NL_GET_OP(type);
const struct rdma_nl_cbs *cb_table;
if (!is_nl_valid(index, op))
return -EINVAL;
cb_table = rdma_nl_types[index].cb_table;
list_for_each_entry(client, &client_list, list) { if ((cb_table[op].flags & RDMA_NL_ADMIN_PERM) &&
if (client->index == index) { !netlink_capable(skb, CAP_NET_ADMIN))
if (op >= client->nops || !client->cb_table[op].dump) return -EPERM;
return -EINVAL;
/* FIXME: Convert IWCM to properly handle doit callbacks */
/* if ((nlh->nlmsg_flags & NLM_F_DUMP) || index == RDMA_NL_RDMA_CM ||
* For response or local service set_timeout request, index == RDMA_NL_IWCM) {
* there is no need to use netlink_dump_start. struct netlink_dump_control c = {
*/ .dump = cb_table[op].dump,
if (!(nlh->nlmsg_flags & NLM_F_REQUEST) || };
(index == RDMA_NL_LS && return netlink_dump_start(nls, skb, nlh, &c);
op == RDMA_NL_LS_OP_SET_TIMEOUT)) {
struct netlink_callback cb = {
.skb = skb,
.nlh = nlh,
.dump = client->cb_table[op].dump,
.module = client->cb_table[op].module,
};
return cb.dump(skb, &cb);
}
{
struct netlink_dump_control c = {
.dump = client->cb_table[op].dump,
.module = client->cb_table[op].module,
};
return netlink_dump_start(nls, skb, nlh, &c);
}
}
} }
pr_info("Index %d wasn't found in client list\n", index); if (cb_table[op].doit)
return -EINVAL; return cb_table[op].doit(skb, nlh, extack);
return 0;
} }
static void ibnl_rcv_reply_skb(struct sk_buff *skb) /*
* This function is similar to netlink_rcv_skb with one exception:
* It calls to the callback for the netlink messages without NLM_F_REQUEST
* flag. These messages are intended for RDMA_NL_LS consumer, so it is allowed
* for that consumer only.
*/
static int rdma_nl_rcv_skb(struct sk_buff *skb, int (*cb)(struct sk_buff *,
struct nlmsghdr *,
struct netlink_ext_ack *))
{ {
struct netlink_ext_ack extack = {};
struct nlmsghdr *nlh; struct nlmsghdr *nlh;
int msglen; int err;
/*
* Process responses until there is no more message or the first
* request. Generally speaking, it is not recommended to mix responses
* with requests.
*/
while (skb->len >= nlmsg_total_size(0)) { while (skb->len >= nlmsg_total_size(0)) {
int msglen;
nlh = nlmsg_hdr(skb); nlh = nlmsg_hdr(skb);
err = 0;
if (nlh->nlmsg_len < NLMSG_HDRLEN || skb->len < nlh->nlmsg_len) if (nlh->nlmsg_len < NLMSG_HDRLEN || skb->len < nlh->nlmsg_len)
return; return 0;
/* Handle response only */
if (nlh->nlmsg_flags & NLM_F_REQUEST)
return;
ibnl_rcv_msg(skb, nlh, NULL);
/*
* Generally speaking, the only requests are handled
* by the kernel, but RDMA_NL_LS is different, because it
* runs backward netlink scheme. Kernel initiates messages
* and waits for reply with data to keep pathrecord cache
* in sync.
*/
if (!(nlh->nlmsg_flags & NLM_F_REQUEST) &&
(RDMA_NL_GET_CLIENT(nlh->nlmsg_type) != RDMA_NL_LS))
goto ack;
/* Skip control messages */
if (nlh->nlmsg_type < NLMSG_MIN_TYPE)
goto ack;
err = cb(skb, nlh, &extack);
if (err == -EINTR)
goto skip;
ack:
if (nlh->nlmsg_flags & NLM_F_ACK || err)
netlink_ack(skb, nlh, err, &extack);
skip:
msglen = NLMSG_ALIGN(nlh->nlmsg_len); msglen = NLMSG_ALIGN(nlh->nlmsg_len);
if (msglen > skb->len) if (msglen > skb->len)
msglen = skb->len; msglen = skb->len;
skb_pull(skb, msglen); skb_pull(skb, msglen);
} }
return 0;
} }
static void ibnl_rcv(struct sk_buff *skb) static void rdma_nl_rcv(struct sk_buff *skb)
{ {
mutex_lock(&ibnl_mutex); mutex_lock(&rdma_nl_mutex);
ibnl_rcv_reply_skb(skb); rdma_nl_rcv_skb(skb, &rdma_nl_rcv_msg);
netlink_rcv_skb(skb, &ibnl_rcv_msg); mutex_unlock(&rdma_nl_mutex);
mutex_unlock(&ibnl_mutex);
} }
int ibnl_unicast(struct sk_buff *skb, struct nlmsghdr *nlh, int rdma_nl_unicast(struct sk_buff *skb, u32 pid)
__u32 pid) {
int err;
err = netlink_unicast(nls, skb, pid, MSG_DONTWAIT);
return (err < 0) ? err : 0;
}
EXPORT_SYMBOL(rdma_nl_unicast);
int rdma_nl_unicast_wait(struct sk_buff *skb, __u32 pid)
{ {
int err; int err;
err = netlink_unicast(nls, skb, pid, 0); err = netlink_unicast(nls, skb, pid, 0);
return (err < 0) ? err : 0; return (err < 0) ? err : 0;
} }
EXPORT_SYMBOL(ibnl_unicast); EXPORT_SYMBOL(rdma_nl_unicast_wait);
int ibnl_multicast(struct sk_buff *skb, struct nlmsghdr *nlh, int rdma_nl_multicast(struct sk_buff *skb, unsigned int group, gfp_t flags)
unsigned int group, gfp_t flags)
{ {
return nlmsg_multicast(nls, skb, 0, group, flags); return nlmsg_multicast(nls, skb, 0, group, flags);
} }
EXPORT_SYMBOL(ibnl_multicast); EXPORT_SYMBOL(rdma_nl_multicast);
int __init ibnl_init(void) int __init rdma_nl_init(void)
{ {
struct netlink_kernel_cfg cfg = { struct netlink_kernel_cfg cfg = {
.input = ibnl_rcv, .input = rdma_nl_rcv,
}; };
nls = netlink_kernel_create(&init_net, NETLINK_RDMA, &cfg); nls = netlink_kernel_create(&init_net, NETLINK_RDMA, &cfg);
if (!nls) { if (!nls)
pr_warn("Failed to create netlink socket\n");
return -ENOMEM; return -ENOMEM;
}
nls->sk_sndtimeo = 10 * HZ; nls->sk_sndtimeo = 10 * HZ;
return 0; return 0;
} }
void ibnl_cleanup(void) void rdma_nl_exit(void)
{ {
struct ibnl_client *cur, *next; int idx;
mutex_lock(&ibnl_mutex); for (idx = 0; idx < RDMA_NL_NUM_CLIENTS; idx++)
list_for_each_entry_safe(cur, next, &client_list, list) { rdma_nl_unregister(idx);
list_del(&(cur->list));
kfree(cur);
}
mutex_unlock(&ibnl_mutex);
netlink_kernel_release(nls); netlink_kernel_release(nls);
} }
MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_RDMA);
/*
* Copyright (c) 2017 Mellanox Technologies. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the names of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* Alternatively, this software may be distributed under the terms of the
* GNU General Public License ("GPL") version 2 as published by the Free
* Software Foundation.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <linux/module.h>
#include <net/netlink.h>
#include <rdma/rdma_netlink.h>
#include "core_priv.h"
static const struct nla_policy nldev_policy[RDMA_NLDEV_ATTR_MAX] = {
[RDMA_NLDEV_ATTR_DEV_INDEX] = { .type = NLA_U32 },
[RDMA_NLDEV_ATTR_DEV_NAME] = { .type = NLA_NUL_STRING,
.len = IB_DEVICE_NAME_MAX - 1},
[RDMA_NLDEV_ATTR_PORT_INDEX] = { .type = NLA_U32 },
[RDMA_NLDEV_ATTR_FW_VERSION] = { .type = NLA_NUL_STRING,
.len = IB_FW_VERSION_NAME_MAX - 1},
[RDMA_NLDEV_ATTR_NODE_GUID] = { .type = NLA_U64 },
[RDMA_NLDEV_ATTR_SYS_IMAGE_GUID] = { .type = NLA_U64 },
[RDMA_NLDEV_ATTR_SUBNET_PREFIX] = { .type = NLA_U64 },
[RDMA_NLDEV_ATTR_LID] = { .type = NLA_U32 },
[RDMA_NLDEV_ATTR_SM_LID] = { .type = NLA_U32 },
[RDMA_NLDEV_ATTR_LMC] = { .type = NLA_U8 },
[RDMA_NLDEV_ATTR_PORT_STATE] = { .type = NLA_U8 },
[RDMA_NLDEV_ATTR_PORT_PHYS_STATE] = { .type = NLA_U8 },
[RDMA_NLDEV_ATTR_DEV_NODE_TYPE] = { .type = NLA_U8 },
};
static int fill_dev_info(struct sk_buff *msg, struct ib_device *device)
{
char fw[IB_FW_VERSION_NAME_MAX];
if (nla_put_u32(msg, RDMA_NLDEV_ATTR_DEV_INDEX, device->index))
return -EMSGSIZE;
if (nla_put_string(msg, RDMA_NLDEV_ATTR_DEV_NAME, device->name))
return -EMSGSIZE;
if (nla_put_u32(msg, RDMA_NLDEV_ATTR_PORT_INDEX, rdma_end_port(device)))
return -EMSGSIZE;
BUILD_BUG_ON(sizeof(device->attrs.device_cap_flags) != sizeof(u64));
if (nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_CAP_FLAGS,
device->attrs.device_cap_flags, 0))
return -EMSGSIZE;
ib_get_device_fw_str(device, fw);
/* Device without FW has strlen(fw) */
if (strlen(fw) && nla_put_string(msg, RDMA_NLDEV_ATTR_FW_VERSION, fw))
return -EMSGSIZE;
if (nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_NODE_GUID,
be64_to_cpu(device->node_guid), 0))
return -EMSGSIZE;
if (nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_SYS_IMAGE_GUID,
be64_to_cpu(device->attrs.sys_image_guid), 0))
return -EMSGSIZE;
if (nla_put_u8(msg, RDMA_NLDEV_ATTR_DEV_NODE_TYPE, device->node_type))
return -EMSGSIZE;
return 0;
}
static int fill_port_info(struct sk_buff *msg,
struct ib_device *device, u32 port)
{
struct ib_port_attr attr;
int ret;
if (nla_put_u32(msg, RDMA_NLDEV_ATTR_DEV_INDEX, device->index))
return -EMSGSIZE;
if (nla_put_string(msg, RDMA_NLDEV_ATTR_DEV_NAME, device->name))
return -EMSGSIZE;
if (nla_put_u32(msg, RDMA_NLDEV_ATTR_PORT_INDEX, port))
return -EMSGSIZE;
ret = ib_query_port(device, port, &attr);
if (ret)
return ret;
BUILD_BUG_ON(sizeof(attr.port_cap_flags) > sizeof(u64));
if (nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_CAP_FLAGS,
(u64)attr.port_cap_flags, 0))
return -EMSGSIZE;
if (rdma_protocol_ib(device, port) &&
nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_SUBNET_PREFIX,
attr.subnet_prefix, 0))
return -EMSGSIZE;
if (rdma_protocol_ib(device, port)) {
if (nla_put_u32(msg, RDMA_NLDEV_ATTR_LID, attr.lid))
return -EMSGSIZE;
if (nla_put_u32(msg, RDMA_NLDEV_ATTR_SM_LID, attr.sm_lid))
return -EMSGSIZE;
if (nla_put_u8(msg, RDMA_NLDEV_ATTR_LMC, attr.lmc))
return -EMSGSIZE;
}
if (nla_put_u8(msg, RDMA_NLDEV_ATTR_PORT_STATE, attr.state))
return -EMSGSIZE;
if (nla_put_u8(msg, RDMA_NLDEV_ATTR_PORT_PHYS_STATE, attr.phys_state))
return -EMSGSIZE;
return 0;
}
static int nldev_get_doit(struct sk_buff *skb, struct nlmsghdr *nlh,
struct netlink_ext_ack *extack)
{
struct nlattr *tb[RDMA_NLDEV_ATTR_MAX];
struct ib_device *device;
struct sk_buff *msg;
u32 index;
int err;
err = nlmsg_parse(nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1,
nldev_policy, extack);
if (err || !tb[RDMA_NLDEV_ATTR_DEV_INDEX])
return -EINVAL;
index = nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]);
device = __ib_device_get_by_index(index);
if (!device)
return -EINVAL;
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!msg)
return -ENOMEM;
nlh = nlmsg_put(msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq,
RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, RDMA_NLDEV_CMD_GET),
0, 0);
err = fill_dev_info(msg, device);
if (err) {
nlmsg_free(msg);
return err;
}
nlmsg_end(msg, nlh);
return rdma_nl_unicast(msg, NETLINK_CB(skb).portid);
}
static int _nldev_get_dumpit(struct ib_device *device,
struct sk_buff *skb,
struct netlink_callback *cb,
unsigned int idx)
{
int start = cb->args[0];
struct nlmsghdr *nlh;
if (idx < start)
return 0;
nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, RDMA_NLDEV_CMD_GET),
0, NLM_F_MULTI);
if (fill_dev_info(skb, device)) {
nlmsg_cancel(skb, nlh);
goto out;
}
nlmsg_end(skb, nlh);
idx++;
out: cb->args[0] = idx;
return skb->len;
}
static int nldev_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
{
/*
* There is no need to take lock, because
* we are relying on ib_core's lists_rwsem
*/
return ib_enum_all_devs(_nldev_get_dumpit, skb, cb);
}
static int nldev_port_get_doit(struct sk_buff *skb, struct nlmsghdr *nlh,
struct netlink_ext_ack *extack)
{
struct nlattr *tb[RDMA_NLDEV_ATTR_MAX];
struct ib_device *device;
struct sk_buff *msg;
u32 index;
u32 port;
int err;
err = nlmsg_parse(nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1,
nldev_policy, extack);
if (err || !tb[RDMA_NLDEV_ATTR_PORT_INDEX])
return -EINVAL;
index = nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]);
device = __ib_device_get_by_index(index);
if (!device)
return -EINVAL;
port = nla_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]);
if (!rdma_is_port_valid(device, port))
return -EINVAL;
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!msg)
return -ENOMEM;
nlh = nlmsg_put(msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq,
RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, RDMA_NLDEV_CMD_GET),
0, 0);
err = fill_port_info(msg, device, port);
if (err) {
nlmsg_free(msg);
return err;
}
nlmsg_end(msg, nlh);
return rdma_nl_unicast(msg, NETLINK_CB(skb).portid);
}
static int nldev_port_get_dumpit(struct sk_buff *skb,
struct netlink_callback *cb)
{
struct nlattr *tb[RDMA_NLDEV_ATTR_MAX];
struct ib_device *device;
int start = cb->args[0];
struct nlmsghdr *nlh;
u32 idx = 0;
u32 ifindex;
int err;
u32 p;
err = nlmsg_parse(cb->nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1,
nldev_policy, NULL);
if (err || !tb[RDMA_NLDEV_ATTR_DEV_INDEX])
return -EINVAL;
ifindex = nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]);
device = __ib_device_get_by_index(ifindex);
if (!device)
return -EINVAL;
for (p = rdma_start_port(device); p <= rdma_end_port(device); ++p) {
/*
* The dumpit function returns all information from specific
* index. This specific index is taken from the netlink
* messages request sent by user and it is available
* in cb->args[0].
*
* Usually, the user doesn't fill this field and it causes
* to return everything.
*
*/
if (idx < start) {
idx++;
continue;
}
nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq,
RDMA_NL_GET_TYPE(RDMA_NL_NLDEV,
RDMA_NLDEV_CMD_PORT_GET),
0, NLM_F_MULTI);
if (fill_port_info(skb, device, p)) {
nlmsg_cancel(skb, nlh);
goto out;
}
idx++;
nlmsg_end(skb, nlh);
}
out: cb->args[0] = idx;
return skb->len;
}
static const struct rdma_nl_cbs nldev_cb_table[] = {
[RDMA_NLDEV_CMD_GET] = {
.doit = nldev_get_doit,
.dump = nldev_get_dumpit,
},
[RDMA_NLDEV_CMD_PORT_GET] = {
.doit = nldev_port_get_doit,
.dump = nldev_port_get_dumpit,
},
};
void __init nldev_init(void)
{
rdma_nl_register(RDMA_NL_NLDEV, nldev_cb_table);
}
void __exit nldev_exit(void)
{
rdma_nl_unregister(RDMA_NL_NLDEV);
}
MODULE_ALIAS_RDMA_NETLINK(RDMA_NL_NLDEV, 5);
...@@ -35,10 +35,57 @@ ...@@ -35,10 +35,57 @@
#include <rdma/ib_verbs.h> #include <rdma/ib_verbs.h>
#include <rdma/uverbs_types.h> #include <rdma/uverbs_types.h>
#include <linux/rcupdate.h> #include <linux/rcupdate.h>
#include <rdma/uverbs_ioctl.h>
#include <rdma/rdma_user_ioctl.h>
#include "uverbs.h" #include "uverbs.h"
#include "core_priv.h" #include "core_priv.h"
#include "rdma_core.h" #include "rdma_core.h"
int uverbs_ns_idx(u16 *id, unsigned int ns_count)
{
int ret = (*id & UVERBS_ID_NS_MASK) >> UVERBS_ID_NS_SHIFT;
if (ret >= ns_count)
return -EINVAL;
*id &= ~UVERBS_ID_NS_MASK;
return ret;
}
const struct uverbs_object_spec *uverbs_get_object(const struct ib_device *ibdev,
uint16_t object)
{
const struct uverbs_root_spec *object_hash = ibdev->specs_root;
const struct uverbs_object_spec_hash *objects;
int ret = uverbs_ns_idx(&object, object_hash->num_buckets);
if (ret < 0)
return NULL;
objects = object_hash->object_buckets[ret];
if (object >= objects->num_objects)
return NULL;
return objects->objects[object];
}
const struct uverbs_method_spec *uverbs_get_method(const struct uverbs_object_spec *object,
uint16_t method)
{
const struct uverbs_method_spec_hash *methods;
int ret = uverbs_ns_idx(&method, object->num_buckets);
if (ret < 0)
return NULL;
methods = object->method_buckets[ret];
if (method >= methods->num_methods)
return NULL;
return methods->methods[method];
}
void uverbs_uobject_get(struct ib_uobject *uobject) void uverbs_uobject_get(struct ib_uobject *uobject)
{ {
kref_get(&uobject->ref); kref_get(&uobject->ref);
...@@ -404,6 +451,41 @@ int __must_check rdma_remove_commit_uobject(struct ib_uobject *uobj) ...@@ -404,6 +451,41 @@ int __must_check rdma_remove_commit_uobject(struct ib_uobject *uobj)
return ret; return ret;
} }
static int null_obj_type_class_remove_commit(struct ib_uobject *uobj,
enum rdma_remove_reason why)
{
return 0;
}
static const struct uverbs_obj_type null_obj_type = {
.type_class = &((const struct uverbs_obj_type_class){
.remove_commit = null_obj_type_class_remove_commit,
/* be cautious */
.needs_kfree_rcu = true}),
};
int rdma_explicit_destroy(struct ib_uobject *uobject)
{
int ret;
struct ib_ucontext *ucontext = uobject->context;
/* Cleanup is running. Calling this should have been impossible */
if (!down_read_trylock(&ucontext->cleanup_rwsem)) {
WARN(true, "ib_uverbs: Cleanup is running while removing an uobject\n");
return 0;
}
lockdep_check(uobject, true);
ret = uobject->type->type_class->remove_commit(uobject,
RDMA_REMOVE_DESTROY);
if (ret)
return ret;
uobject->type = &null_obj_type;
up_read(&ucontext->cleanup_rwsem);
return 0;
}
static void alloc_commit_idr_uobject(struct ib_uobject *uobj) static void alloc_commit_idr_uobject(struct ib_uobject *uobj)
{ {
uverbs_uobject_add(uobj); uverbs_uobject_add(uobj);
...@@ -625,3 +707,100 @@ const struct uverbs_obj_type_class uverbs_fd_class = { ...@@ -625,3 +707,100 @@ const struct uverbs_obj_type_class uverbs_fd_class = {
.needs_kfree_rcu = false, .needs_kfree_rcu = false,
}; };
struct ib_uobject *uverbs_get_uobject_from_context(const struct uverbs_obj_type *type_attrs,
struct ib_ucontext *ucontext,
enum uverbs_obj_access access,
int id)
{
switch (access) {
case UVERBS_ACCESS_READ:
return rdma_lookup_get_uobject(type_attrs, ucontext, id, false);
case UVERBS_ACCESS_DESTROY:
case UVERBS_ACCESS_WRITE:
return rdma_lookup_get_uobject(type_attrs, ucontext, id, true);
case UVERBS_ACCESS_NEW:
return rdma_alloc_begin_uobject(type_attrs, ucontext);
default:
WARN_ON(true);
return ERR_PTR(-EOPNOTSUPP);
}
}
int uverbs_finalize_object(struct ib_uobject *uobj,
enum uverbs_obj_access access,
bool commit)
{
int ret = 0;
/*
* refcounts should be handled at the object level and not at the
* uobject level. Refcounts of the objects themselves are done in
* handlers.
*/
switch (access) {
case UVERBS_ACCESS_READ:
rdma_lookup_put_uobject(uobj, false);
break;
case UVERBS_ACCESS_WRITE:
rdma_lookup_put_uobject(uobj, true);
break;
case UVERBS_ACCESS_DESTROY:
if (commit)
ret = rdma_remove_commit_uobject(uobj);
else
rdma_lookup_put_uobject(uobj, true);
break;
case UVERBS_ACCESS_NEW:
if (commit)
ret = rdma_alloc_commit_uobject(uobj);
else
rdma_alloc_abort_uobject(uobj);
break;
default:
WARN_ON(true);
ret = -EOPNOTSUPP;
}
return ret;
}
int uverbs_finalize_objects(struct uverbs_attr_bundle *attrs_bundle,
struct uverbs_attr_spec_hash * const *spec_hash,
size_t num,
bool commit)
{
unsigned int i;
int ret = 0;
for (i = 0; i < num; i++) {
struct uverbs_attr_bundle_hash *curr_bundle =
&attrs_bundle->hash[i];
const struct uverbs_attr_spec_hash *curr_spec_bucket =
spec_hash[i];
unsigned int j;
for (j = 0; j < curr_bundle->num_attrs; j++) {
struct uverbs_attr *attr;
const struct uverbs_attr_spec *spec;
if (!uverbs_attr_is_valid_in_hash(curr_bundle, j))
continue;
attr = &curr_bundle->attrs[j];
spec = &curr_spec_bucket->attrs[j];
if (spec->type == UVERBS_ATTR_TYPE_IDR ||
spec->type == UVERBS_ATTR_TYPE_FD) {
int current_ret;
current_ret = uverbs_finalize_object(attr->obj_attr.uobject,
spec->obj.access,
commit);
if (!ret)
ret = current_ret;
}
}
}
return ret;
}
...@@ -39,9 +39,15 @@ ...@@ -39,9 +39,15 @@
#include <linux/idr.h> #include <linux/idr.h>
#include <rdma/uverbs_types.h> #include <rdma/uverbs_types.h>
#include <rdma/uverbs_ioctl.h>
#include <rdma/ib_verbs.h> #include <rdma/ib_verbs.h>
#include <linux/mutex.h> #include <linux/mutex.h>
int uverbs_ns_idx(u16 *id, unsigned int ns_count);
const struct uverbs_object_spec *uverbs_get_object(const struct ib_device *ibdev,
uint16_t object);
const struct uverbs_method_spec *uverbs_get_method(const struct uverbs_object_spec *object,
uint16_t method);
/* /*
* These functions initialize the context and cleanups its uobjects. * These functions initialize the context and cleanups its uobjects.
* The context has a list of objects which is protected by a mutex * The context has a list of objects which is protected by a mutex
...@@ -75,4 +81,40 @@ void uverbs_uobject_put(struct ib_uobject *uobject); ...@@ -75,4 +81,40 @@ void uverbs_uobject_put(struct ib_uobject *uobject);
*/ */
void uverbs_close_fd(struct file *f); void uverbs_close_fd(struct file *f);
/*
* Get an ib_uobject that corresponds to the given id from ucontext, assuming
* the object is from the given type. Lock it to the required access when
* applicable.
* This function could create (access == NEW), destroy (access == DESTROY)
* or unlock (access == READ || access == WRITE) objects if required.
* The action will be finalized only when uverbs_finalize_object or
* uverbs_finalize_objects are called.
*/
struct ib_uobject *uverbs_get_uobject_from_context(const struct uverbs_obj_type *type_attrs,
struct ib_ucontext *ucontext,
enum uverbs_obj_access access,
int id);
int uverbs_finalize_object(struct ib_uobject *uobj,
enum uverbs_obj_access access,
bool commit);
/*
* Note that certain finalize stages could return a status:
* (a) alloc_commit could return a failure if the object is committed at the
* same time when the context is destroyed.
* (b) remove_commit could fail if the object wasn't destroyed successfully.
* Since multiple objects could be finalized in one transaction, it is very NOT
* recommended to have several finalize actions which have side effects.
* For example, it's NOT recommended to have a certain action which has both
* a commit action and a destroy action or two destroy objects in the same
* action. The rule of thumb is to have one destroy or commit action with
* multiple lookups.
* The first non zero return value of finalize_object is returned from this
* function. For example, this could happen when we couldn't destroy an
* object.
*/
int uverbs_finalize_objects(struct uverbs_attr_bundle *attrs_bundle,
struct uverbs_attr_spec_hash * const *spec_hash,
size_t num,
bool commit);
#endif /* RDMA_CORE_H */ #endif /* RDMA_CORE_H */
...@@ -44,6 +44,8 @@ ...@@ -44,6 +44,8 @@
static struct workqueue_struct *gid_cache_wq; static struct workqueue_struct *gid_cache_wq;
static struct workqueue_struct *gid_cache_wq;
enum gid_op_type { enum gid_op_type {
GID_DEL = 0, GID_DEL = 0,
GID_ADD GID_ADD
......
...@@ -50,6 +50,7 @@ ...@@ -50,6 +50,7 @@
#include <uapi/rdma/ib_user_sa.h> #include <uapi/rdma/ib_user_sa.h>
#include <rdma/ib_marshall.h> #include <rdma/ib_marshall.h>
#include <rdma/ib_addr.h> #include <rdma/ib_addr.h>
#include <rdma/opa_addr.h>
#include "sa.h" #include "sa.h"
#include "core_priv.h" #include "core_priv.h"
...@@ -861,7 +862,7 @@ static int ib_nl_send_msg(struct ib_sa_query *query, gfp_t gfp_mask) ...@@ -861,7 +862,7 @@ static int ib_nl_send_msg(struct ib_sa_query *query, gfp_t gfp_mask)
/* Repair the nlmsg header length */ /* Repair the nlmsg header length */
nlmsg_end(skb, nlh); nlmsg_end(skb, nlh);
ret = ibnl_multicast(skb, nlh, RDMA_NL_GROUP_LS, gfp_mask); ret = rdma_nl_multicast(skb, RDMA_NL_GROUP_LS, gfp_mask);
if (!ret) if (!ret)
ret = len; ret = len;
else else
...@@ -1021,9 +1022,9 @@ static void ib_nl_request_timeout(struct work_struct *work) ...@@ -1021,9 +1022,9 @@ static void ib_nl_request_timeout(struct work_struct *work)
} }
int ib_nl_handle_set_timeout(struct sk_buff *skb, int ib_nl_handle_set_timeout(struct sk_buff *skb,
struct netlink_callback *cb) struct nlmsghdr *nlh,
struct netlink_ext_ack *extack)
{ {
const struct nlmsghdr *nlh = (struct nlmsghdr *)cb->nlh;
int timeout, delta, abs_delta; int timeout, delta, abs_delta;
const struct nlattr *attr; const struct nlattr *attr;
unsigned long flags; unsigned long flags;
...@@ -1033,8 +1034,7 @@ int ib_nl_handle_set_timeout(struct sk_buff *skb, ...@@ -1033,8 +1034,7 @@ int ib_nl_handle_set_timeout(struct sk_buff *skb,
int ret; int ret;
if (!(nlh->nlmsg_flags & NLM_F_REQUEST) || if (!(nlh->nlmsg_flags & NLM_F_REQUEST) ||
!(NETLINK_CB(skb).sk) || !(NETLINK_CB(skb).sk))
!netlink_capable(skb, CAP_NET_ADMIN))
return -EPERM; return -EPERM;
ret = nla_parse(tb, LS_NLA_TYPE_MAX - 1, nlmsg_data(nlh), ret = nla_parse(tb, LS_NLA_TYPE_MAX - 1, nlmsg_data(nlh),
...@@ -1098,9 +1098,9 @@ static inline int ib_nl_is_good_resolve_resp(const struct nlmsghdr *nlh) ...@@ -1098,9 +1098,9 @@ static inline int ib_nl_is_good_resolve_resp(const struct nlmsghdr *nlh)
} }
int ib_nl_handle_resolve_resp(struct sk_buff *skb, int ib_nl_handle_resolve_resp(struct sk_buff *skb,
struct netlink_callback *cb) struct nlmsghdr *nlh,
struct netlink_ext_ack *extack)
{ {
const struct nlmsghdr *nlh = (struct nlmsghdr *)cb->nlh;
unsigned long flags; unsigned long flags;
struct ib_sa_query *query; struct ib_sa_query *query;
struct ib_mad_send_buf *send_buf; struct ib_mad_send_buf *send_buf;
...@@ -1109,8 +1109,7 @@ int ib_nl_handle_resolve_resp(struct sk_buff *skb, ...@@ -1109,8 +1109,7 @@ int ib_nl_handle_resolve_resp(struct sk_buff *skb,
int ret; int ret;
if ((nlh->nlmsg_flags & NLM_F_REQUEST) || if ((nlh->nlmsg_flags & NLM_F_REQUEST) ||
!(NETLINK_CB(skb).sk) || !(NETLINK_CB(skb).sk))
!netlink_capable(skb, CAP_NET_ADMIN))
return -EPERM; return -EPERM;
spin_lock_irqsave(&ib_nl_request_lock, flags); spin_lock_irqsave(&ib_nl_request_lock, flags);
...@@ -1241,6 +1240,11 @@ int ib_init_ah_from_path(struct ib_device *device, u8 port_num, ...@@ -1241,6 +1240,11 @@ int ib_init_ah_from_path(struct ib_device *device, u8 port_num,
ah_attr->type = rdma_ah_find_type(device, port_num); ah_attr->type = rdma_ah_find_type(device, port_num);
rdma_ah_set_dlid(ah_attr, be32_to_cpu(sa_path_get_dlid(rec))); rdma_ah_set_dlid(ah_attr, be32_to_cpu(sa_path_get_dlid(rec)));
if ((ah_attr->type == RDMA_AH_ATTR_TYPE_OPA) &&
(rdma_ah_get_dlid(ah_attr) == be16_to_cpu(IB_LID_PERMISSIVE)))
rdma_ah_set_make_grd(ah_attr, true);
rdma_ah_set_sl(ah_attr, rec->sl); rdma_ah_set_sl(ah_attr, rec->sl);
rdma_ah_set_path_bits(ah_attr, be32_to_cpu(sa_path_get_slid(rec)) & rdma_ah_set_path_bits(ah_attr, be32_to_cpu(sa_path_get_slid(rec)) &
get_src_path_mask(device, port_num)); get_src_path_mask(device, port_num));
...@@ -1420,7 +1424,7 @@ static int send_mad(struct ib_sa_query *query, int timeout_ms, gfp_t gfp_mask) ...@@ -1420,7 +1424,7 @@ static int send_mad(struct ib_sa_query *query, int timeout_ms, gfp_t gfp_mask)
if ((query->flags & IB_SA_ENABLE_LOCAL_SERVICE) && if ((query->flags & IB_SA_ENABLE_LOCAL_SERVICE) &&
(!(query->flags & IB_SA_QUERY_OPA))) { (!(query->flags & IB_SA_QUERY_OPA))) {
if (!ibnl_chk_listeners(RDMA_NL_GROUP_LS)) { if (!rdma_nl_chk_listeners(RDMA_NL_GROUP_LS)) {
if (!ib_nl_make_request(query, gfp_mask)) if (!ib_nl_make_request(query, gfp_mask))
return id; return id;
} }
...@@ -2290,12 +2294,15 @@ static void update_sm_ah(struct work_struct *work) ...@@ -2290,12 +2294,15 @@ static void update_sm_ah(struct work_struct *work)
rdma_ah_set_sl(&ah_attr, port_attr.sm_sl); rdma_ah_set_sl(&ah_attr, port_attr.sm_sl);
rdma_ah_set_port_num(&ah_attr, port->port_num); rdma_ah_set_port_num(&ah_attr, port->port_num);
if (port_attr.grh_required) { if (port_attr.grh_required) {
rdma_ah_set_ah_flags(&ah_attr, IB_AH_GRH); if (ah_attr.type == RDMA_AH_ATTR_TYPE_OPA) {
rdma_ah_set_make_grd(&ah_attr, true);
rdma_ah_set_subnet_prefix(&ah_attr, } else {
cpu_to_be64(port_attr.subnet_prefix)); rdma_ah_set_ah_flags(&ah_attr, IB_AH_GRH);
rdma_ah_set_interface_id(&ah_attr, rdma_ah_set_subnet_prefix(&ah_attr,
cpu_to_be64(IB_SA_WELL_KNOWN_GUID)); cpu_to_be64(port_attr.subnet_prefix));
rdma_ah_set_interface_id(&ah_attr,
cpu_to_be64(IB_SA_WELL_KNOWN_GUID));
}
} }
new_ah->ah = rdma_create_ah(port->agent->qp->pd, &ah_attr); new_ah->ah = rdma_create_ah(port->agent->qp->pd, &ah_attr);
...@@ -2410,8 +2417,7 @@ static void ib_sa_add_one(struct ib_device *device) ...@@ -2410,8 +2417,7 @@ static void ib_sa_add_one(struct ib_device *device)
*/ */
INIT_IB_EVENT_HANDLER(&sa_dev->event_handler, device, ib_sa_event); INIT_IB_EVENT_HANDLER(&sa_dev->event_handler, device, ib_sa_event);
if (ib_register_event_handler(&sa_dev->event_handler)) ib_register_event_handler(&sa_dev->event_handler);
goto err;
for (i = 0; i <= e - s; ++i) { for (i = 0; i <= e - s; ++i) {
if (rdma_cap_ib_sa(device, i + 1)) if (rdma_cap_ib_sa(device, i + 1))
......
...@@ -1210,8 +1210,8 @@ static ssize_t show_fw_ver(struct device *device, struct device_attribute *attr, ...@@ -1210,8 +1210,8 @@ static ssize_t show_fw_ver(struct device *device, struct device_attribute *attr,
{ {
struct ib_device *dev = container_of(device, struct ib_device, dev); struct ib_device *dev = container_of(device, struct ib_device, dev);
ib_get_device_fw_str(dev, buf, PAGE_SIZE); ib_get_device_fw_str(dev, buf);
strlcat(buf, "\n", PAGE_SIZE); strlcat(buf, "\n", IB_FW_VERSION_NAME_MAX);
return strlen(buf); return strlen(buf);
} }
......
...@@ -618,7 +618,7 @@ static ssize_t ib_ucm_init_qp_attr(struct ib_ucm_file *file, ...@@ -618,7 +618,7 @@ static ssize_t ib_ucm_init_qp_attr(struct ib_ucm_file *file,
if (result) if (result)
goto out; goto out;
ib_copy_qp_attr_to_user(&resp, &qp_attr); ib_copy_qp_attr_to_user(ctx->cm_id->device, &resp, &qp_attr);
if (copy_to_user((void __user *)(unsigned long)cmd.response, if (copy_to_user((void __user *)(unsigned long)cmd.response,
&resp, sizeof(resp))) &resp, sizeof(resp)))
......
...@@ -248,14 +248,15 @@ static void ucma_copy_conn_event(struct rdma_ucm_conn_param *dst, ...@@ -248,14 +248,15 @@ static void ucma_copy_conn_event(struct rdma_ucm_conn_param *dst,
dst->qp_num = src->qp_num; dst->qp_num = src->qp_num;
} }
static void ucma_copy_ud_event(struct rdma_ucm_ud_param *dst, static void ucma_copy_ud_event(struct ib_device *device,
struct rdma_ucm_ud_param *dst,
struct rdma_ud_param *src) struct rdma_ud_param *src)
{ {
if (src->private_data_len) if (src->private_data_len)
memcpy(dst->private_data, src->private_data, memcpy(dst->private_data, src->private_data,
src->private_data_len); src->private_data_len);
dst->private_data_len = src->private_data_len; dst->private_data_len = src->private_data_len;
ib_copy_ah_attr_to_user(&dst->ah_attr, &src->ah_attr); ib_copy_ah_attr_to_user(device, &dst->ah_attr, &src->ah_attr);
dst->qp_num = src->qp_num; dst->qp_num = src->qp_num;
dst->qkey = src->qkey; dst->qkey = src->qkey;
} }
...@@ -335,7 +336,8 @@ static int ucma_event_handler(struct rdma_cm_id *cm_id, ...@@ -335,7 +336,8 @@ static int ucma_event_handler(struct rdma_cm_id *cm_id,
uevent->resp.event = event->event; uevent->resp.event = event->event;
uevent->resp.status = event->status; uevent->resp.status = event->status;
if (cm_id->qp_type == IB_QPT_UD) if (cm_id->qp_type == IB_QPT_UD)
ucma_copy_ud_event(&uevent->resp.param.ud, &event->param.ud); ucma_copy_ud_event(cm_id->device, &uevent->resp.param.ud,
&event->param.ud);
else else
ucma_copy_conn_event(&uevent->resp.param.conn, ucma_copy_conn_event(&uevent->resp.param.conn,
&event->param.conn); &event->param.conn);
...@@ -1157,7 +1159,7 @@ static ssize_t ucma_init_qp_attr(struct ucma_file *file, ...@@ -1157,7 +1159,7 @@ static ssize_t ucma_init_qp_attr(struct ucma_file *file,
if (ret) if (ret)
goto out; goto out;
ib_copy_qp_attr_to_user(&resp, &qp_attr); ib_copy_qp_attr_to_user(ctx->cm_id->device, &resp, &qp_attr);
if (copy_to_user((void __user *)(unsigned long)cmd.response, if (copy_to_user((void __user *)(unsigned long)cmd.response,
&resp, sizeof(resp))) &resp, sizeof(resp)))
ret = -EFAULT; ret = -EFAULT;
......
...@@ -229,7 +229,7 @@ static void recv_handler(struct ib_mad_agent *agent, ...@@ -229,7 +229,7 @@ static void recv_handler(struct ib_mad_agent *agent,
packet->mad.hdr.status = 0; packet->mad.hdr.status = 0;
packet->mad.hdr.length = hdr_size(file) + mad_recv_wc->mad_len; packet->mad.hdr.length = hdr_size(file) + mad_recv_wc->mad_len;
packet->mad.hdr.qpn = cpu_to_be32(mad_recv_wc->wc->src_qp); packet->mad.hdr.qpn = cpu_to_be32(mad_recv_wc->wc->src_qp);
packet->mad.hdr.lid = cpu_to_be16(mad_recv_wc->wc->slid); packet->mad.hdr.lid = ib_lid_be16(mad_recv_wc->wc->slid);
packet->mad.hdr.sl = mad_recv_wc->wc->sl; packet->mad.hdr.sl = mad_recv_wc->wc->sl;
packet->mad.hdr.path_bits = mad_recv_wc->wc->dlid_path_bits; packet->mad.hdr.path_bits = mad_recv_wc->wc->dlid_path_bits;
packet->mad.hdr.pkey_index = mad_recv_wc->wc->pkey_index; packet->mad.hdr.pkey_index = mad_recv_wc->wc->pkey_index;
......
...@@ -100,6 +100,7 @@ struct ib_uverbs_device { ...@@ -100,6 +100,7 @@ struct ib_uverbs_device {
struct mutex lists_mutex; /* protect lists */ struct mutex lists_mutex; /* protect lists */
struct list_head uverbs_file_list; struct list_head uverbs_file_list;
struct list_head uverbs_events_file_list; struct list_head uverbs_events_file_list;
struct uverbs_root_spec *specs_root;
}; };
struct ib_uverbs_event_queue { struct ib_uverbs_event_queue {
...@@ -218,6 +219,8 @@ int uverbs_dealloc_mw(struct ib_mw *mw); ...@@ -218,6 +219,8 @@ int uverbs_dealloc_mw(struct ib_mw *mw);
void ib_uverbs_detach_umcast(struct ib_qp *qp, void ib_uverbs_detach_umcast(struct ib_qp *qp,
struct ib_uqp_object *uobj); struct ib_uqp_object *uobj);
long ib_uverbs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
struct ib_uverbs_flow_spec { struct ib_uverbs_flow_spec {
union { union {
union { union {
......
...@@ -91,9 +91,10 @@ ssize_t ib_uverbs_get_context(struct ib_uverbs_file *file, ...@@ -91,9 +91,10 @@ ssize_t ib_uverbs_get_context(struct ib_uverbs_file *file,
goto err; goto err;
} }
INIT_UDATA(&udata, buf + sizeof cmd, INIT_UDATA(&udata, buf + sizeof(cmd),
(unsigned long) cmd.response + sizeof resp, (unsigned long) cmd.response + sizeof(resp),
in_len - sizeof cmd, out_len - sizeof resp); in_len - sizeof(cmd) - sizeof(struct ib_uverbs_cmd_hdr),
out_len - sizeof(resp));
ret = ib_rdmacg_try_charge(&cg_obj, ib_dev, RDMACG_RESOURCE_HCA_HANDLE); ret = ib_rdmacg_try_charge(&cg_obj, ib_dev, RDMACG_RESOURCE_HCA_HANDLE);
if (ret) if (ret)
...@@ -275,8 +276,14 @@ ssize_t ib_uverbs_query_port(struct ib_uverbs_file *file, ...@@ -275,8 +276,14 @@ ssize_t ib_uverbs_query_port(struct ib_uverbs_file *file,
resp.bad_pkey_cntr = attr.bad_pkey_cntr; resp.bad_pkey_cntr = attr.bad_pkey_cntr;
resp.qkey_viol_cntr = attr.qkey_viol_cntr; resp.qkey_viol_cntr = attr.qkey_viol_cntr;
resp.pkey_tbl_len = attr.pkey_tbl_len; resp.pkey_tbl_len = attr.pkey_tbl_len;
resp.lid = attr.lid;
resp.sm_lid = attr.sm_lid; if (rdma_cap_opa_ah(ib_dev, cmd.port_num)) {
resp.lid = OPA_TO_IB_UCAST_LID(attr.lid);
resp.sm_lid = OPA_TO_IB_UCAST_LID(attr.sm_lid);
} else {
resp.lid = ib_lid_cpu16(attr.lid);
resp.sm_lid = ib_lid_cpu16(attr.sm_lid);
}
resp.lmc = attr.lmc; resp.lmc = attr.lmc;
resp.max_vl_num = attr.max_vl_num; resp.max_vl_num = attr.max_vl_num;
resp.sm_sl = attr.sm_sl; resp.sm_sl = attr.sm_sl;
...@@ -313,9 +320,10 @@ ssize_t ib_uverbs_alloc_pd(struct ib_uverbs_file *file, ...@@ -313,9 +320,10 @@ ssize_t ib_uverbs_alloc_pd(struct ib_uverbs_file *file,
if (copy_from_user(&cmd, buf, sizeof cmd)) if (copy_from_user(&cmd, buf, sizeof cmd))
return -EFAULT; return -EFAULT;
INIT_UDATA(&udata, buf + sizeof cmd, INIT_UDATA(&udata, buf + sizeof(cmd),
(unsigned long) cmd.response + sizeof resp, (unsigned long) cmd.response + sizeof(resp),
in_len - sizeof cmd, out_len - sizeof resp); in_len - sizeof(cmd) - sizeof(struct ib_uverbs_cmd_hdr),
out_len - sizeof(resp));
uobj = uobj_alloc(uobj_get_type(pd), file->ucontext); uobj = uobj_alloc(uobj_get_type(pd), file->ucontext);
if (IS_ERR(uobj)) if (IS_ERR(uobj))
...@@ -482,9 +490,10 @@ ssize_t ib_uverbs_open_xrcd(struct ib_uverbs_file *file, ...@@ -482,9 +490,10 @@ ssize_t ib_uverbs_open_xrcd(struct ib_uverbs_file *file,
if (copy_from_user(&cmd, buf, sizeof cmd)) if (copy_from_user(&cmd, buf, sizeof cmd))
return -EFAULT; return -EFAULT;
INIT_UDATA(&udata, buf + sizeof cmd, INIT_UDATA(&udata, buf + sizeof(cmd),
(unsigned long) cmd.response + sizeof resp, (unsigned long) cmd.response + sizeof(resp),
in_len - sizeof cmd, out_len - sizeof resp); in_len - sizeof(cmd) - sizeof(struct ib_uverbs_cmd_hdr),
out_len - sizeof(resp));
mutex_lock(&file->device->xrcd_tree_mutex); mutex_lock(&file->device->xrcd_tree_mutex);
...@@ -646,9 +655,10 @@ ssize_t ib_uverbs_reg_mr(struct ib_uverbs_file *file, ...@@ -646,9 +655,10 @@ ssize_t ib_uverbs_reg_mr(struct ib_uverbs_file *file,
if (copy_from_user(&cmd, buf, sizeof cmd)) if (copy_from_user(&cmd, buf, sizeof cmd))
return -EFAULT; return -EFAULT;
INIT_UDATA(&udata, buf + sizeof cmd, INIT_UDATA(&udata, buf + sizeof(cmd),
(unsigned long) cmd.response + sizeof resp, (unsigned long) cmd.response + sizeof(resp),
in_len - sizeof cmd, out_len - sizeof resp); in_len - sizeof(cmd) - sizeof(struct ib_uverbs_cmd_hdr),
out_len - sizeof(resp));
if ((cmd.start & ~PAGE_MASK) != (cmd.hca_va & ~PAGE_MASK)) if ((cmd.start & ~PAGE_MASK) != (cmd.hca_va & ~PAGE_MASK))
return -EINVAL; return -EINVAL;
...@@ -740,7 +750,8 @@ ssize_t ib_uverbs_rereg_mr(struct ib_uverbs_file *file, ...@@ -740,7 +750,8 @@ ssize_t ib_uverbs_rereg_mr(struct ib_uverbs_file *file,
INIT_UDATA(&udata, buf + sizeof(cmd), INIT_UDATA(&udata, buf + sizeof(cmd),
(unsigned long) cmd.response + sizeof(resp), (unsigned long) cmd.response + sizeof(resp),
in_len - sizeof(cmd), out_len - sizeof(resp)); in_len - sizeof(cmd) - sizeof(struct ib_uverbs_cmd_hdr),
out_len - sizeof(resp));
if (cmd.flags & ~IB_MR_REREG_SUPPORTED || !cmd.flags) if (cmd.flags & ~IB_MR_REREG_SUPPORTED || !cmd.flags)
return -EINVAL; return -EINVAL;
...@@ -1080,7 +1091,8 @@ ssize_t ib_uverbs_create_cq(struct ib_uverbs_file *file, ...@@ -1080,7 +1091,8 @@ ssize_t ib_uverbs_create_cq(struct ib_uverbs_file *file,
INIT_UDATA(&uhw, buf + sizeof(cmd), INIT_UDATA(&uhw, buf + sizeof(cmd),
(unsigned long)cmd.response + sizeof(resp), (unsigned long)cmd.response + sizeof(resp),
in_len - sizeof(cmd), out_len - sizeof(resp)); in_len - sizeof(cmd) - sizeof(struct ib_uverbs_cmd_hdr),
out_len - sizeof(resp));
memset(&cmd_ex, 0, sizeof(cmd_ex)); memset(&cmd_ex, 0, sizeof(cmd_ex));
cmd_ex.user_handle = cmd.user_handle; cmd_ex.user_handle = cmd.user_handle;
...@@ -1161,9 +1173,10 @@ ssize_t ib_uverbs_resize_cq(struct ib_uverbs_file *file, ...@@ -1161,9 +1173,10 @@ ssize_t ib_uverbs_resize_cq(struct ib_uverbs_file *file,
if (copy_from_user(&cmd, buf, sizeof cmd)) if (copy_from_user(&cmd, buf, sizeof cmd))
return -EFAULT; return -EFAULT;
INIT_UDATA(&udata, buf + sizeof cmd, INIT_UDATA(&udata, buf + sizeof(cmd),
(unsigned long) cmd.response + sizeof resp, (unsigned long) cmd.response + sizeof(resp),
in_len - sizeof cmd, out_len - sizeof resp); in_len - sizeof(cmd) - sizeof(struct ib_uverbs_cmd_hdr),
out_len - sizeof(resp));
cq = uobj_get_obj_read(cq, cmd.cq_handle, file->ucontext); cq = uobj_get_obj_read(cq, cmd.cq_handle, file->ucontext);
if (!cq) if (!cq)
...@@ -1185,7 +1198,8 @@ ssize_t ib_uverbs_resize_cq(struct ib_uverbs_file *file, ...@@ -1185,7 +1198,8 @@ ssize_t ib_uverbs_resize_cq(struct ib_uverbs_file *file,
return ret ? ret : in_len; return ret ? ret : in_len;
} }
static int copy_wc_to_user(void __user *dest, struct ib_wc *wc) static int copy_wc_to_user(struct ib_device *ib_dev, void __user *dest,
struct ib_wc *wc)
{ {
struct ib_uverbs_wc tmp; struct ib_uverbs_wc tmp;
...@@ -1199,7 +1213,10 @@ static int copy_wc_to_user(void __user *dest, struct ib_wc *wc) ...@@ -1199,7 +1213,10 @@ static int copy_wc_to_user(void __user *dest, struct ib_wc *wc)
tmp.src_qp = wc->src_qp; tmp.src_qp = wc->src_qp;
tmp.wc_flags = wc->wc_flags; tmp.wc_flags = wc->wc_flags;
tmp.pkey_index = wc->pkey_index; tmp.pkey_index = wc->pkey_index;
tmp.slid = wc->slid; if (rdma_cap_opa_ah(ib_dev, wc->port_num))
tmp.slid = OPA_TO_IB_UCAST_LID(wc->slid);
else
tmp.slid = ib_lid_cpu16(wc->slid);
tmp.sl = wc->sl; tmp.sl = wc->sl;
tmp.dlid_path_bits = wc->dlid_path_bits; tmp.dlid_path_bits = wc->dlid_path_bits;
tmp.port_num = wc->port_num; tmp.port_num = wc->port_num;
...@@ -1243,7 +1260,7 @@ ssize_t ib_uverbs_poll_cq(struct ib_uverbs_file *file, ...@@ -1243,7 +1260,7 @@ ssize_t ib_uverbs_poll_cq(struct ib_uverbs_file *file,
if (!ret) if (!ret)
break; break;
ret = copy_wc_to_user(data_ptr, &wc); ret = copy_wc_to_user(ib_dev, data_ptr, &wc);
if (ret) if (ret)
goto out_put; goto out_put;
...@@ -1383,8 +1400,9 @@ static int create_qp(struct ib_uverbs_file *file, ...@@ -1383,8 +1400,9 @@ static int create_qp(struct ib_uverbs_file *file,
attr.rwq_ind_tbl = ind_tbl; attr.rwq_ind_tbl = ind_tbl;
} }
if ((cmd_sz >= offsetof(typeof(*cmd), reserved1) + if (cmd_sz > sizeof(*cmd) &&
sizeof(cmd->reserved1)) && cmd->reserved1) { !ib_is_udata_cleared(ucore, sizeof(*cmd),
cmd_sz - sizeof(*cmd))) {
ret = -EOPNOTSUPP; ret = -EOPNOTSUPP;
goto err_put; goto err_put;
} }
...@@ -1420,7 +1438,7 @@ static int create_qp(struct ib_uverbs_file *file, ...@@ -1420,7 +1438,7 @@ static int create_qp(struct ib_uverbs_file *file,
if (cmd->is_srq) { if (cmd->is_srq) {
srq = uobj_get_obj_read(srq, cmd->srq_handle, srq = uobj_get_obj_read(srq, cmd->srq_handle,
file->ucontext); file->ucontext);
if (!srq || srq->srq_type != IB_SRQT_BASIC) { if (!srq || srq->srq_type == IB_SRQT_XRC) {
ret = -EINVAL; ret = -EINVAL;
goto err_put; goto err_put;
} }
...@@ -1482,11 +1500,21 @@ static int create_qp(struct ib_uverbs_file *file, ...@@ -1482,11 +1500,21 @@ static int create_qp(struct ib_uverbs_file *file,
IB_QP_CREATE_MANAGED_SEND | IB_QP_CREATE_MANAGED_SEND |
IB_QP_CREATE_MANAGED_RECV | IB_QP_CREATE_MANAGED_RECV |
IB_QP_CREATE_SCATTER_FCS | IB_QP_CREATE_SCATTER_FCS |
IB_QP_CREATE_CVLAN_STRIPPING)) { IB_QP_CREATE_CVLAN_STRIPPING |
IB_QP_CREATE_SOURCE_QPN)) {
ret = -EINVAL; ret = -EINVAL;
goto err_put; goto err_put;
} }
if (attr.create_flags & IB_QP_CREATE_SOURCE_QPN) {
if (!capable(CAP_NET_RAW)) {
ret = -EPERM;
goto err_put;
}
attr.source_qpn = cmd->source_qpn;
}
buf = (void *)cmd + sizeof(*cmd); buf = (void *)cmd + sizeof(*cmd);
if (cmd_sz > sizeof(*cmd)) if (cmd_sz > sizeof(*cmd))
if (!(buf[0] == 0 && !memcmp(buf, buf + 1, if (!(buf[0] == 0 && !memcmp(buf, buf + 1,
...@@ -1722,9 +1750,10 @@ ssize_t ib_uverbs_open_qp(struct ib_uverbs_file *file, ...@@ -1722,9 +1750,10 @@ ssize_t ib_uverbs_open_qp(struct ib_uverbs_file *file,
if (copy_from_user(&cmd, buf, sizeof cmd)) if (copy_from_user(&cmd, buf, sizeof cmd))
return -EFAULT; return -EFAULT;
INIT_UDATA(&udata, buf + sizeof cmd, INIT_UDATA(&udata, buf + sizeof(cmd),
(unsigned long) cmd.response + sizeof resp, (unsigned long) cmd.response + sizeof(resp),
in_len - sizeof cmd, out_len - sizeof resp); in_len - sizeof(cmd) - sizeof(struct ib_uverbs_cmd_hdr),
out_len - sizeof(resp));
obj = (struct ib_uqp_object *)uobj_alloc(uobj_get_type(qp), obj = (struct ib_uqp_object *)uobj_alloc(uobj_get_type(qp),
file->ucontext); file->ucontext);
...@@ -1791,6 +1820,28 @@ ssize_t ib_uverbs_open_qp(struct ib_uverbs_file *file, ...@@ -1791,6 +1820,28 @@ ssize_t ib_uverbs_open_qp(struct ib_uverbs_file *file,
return ret; return ret;
} }
static void copy_ah_attr_to_uverbs(struct ib_uverbs_qp_dest *uverb_attr,
struct rdma_ah_attr *rdma_attr)
{
const struct ib_global_route *grh;
uverb_attr->dlid = rdma_ah_get_dlid(rdma_attr);
uverb_attr->sl = rdma_ah_get_sl(rdma_attr);
uverb_attr->src_path_bits = rdma_ah_get_path_bits(rdma_attr);
uverb_attr->static_rate = rdma_ah_get_static_rate(rdma_attr);
uverb_attr->is_global = !!(rdma_ah_get_ah_flags(rdma_attr) &
IB_AH_GRH);
if (uverb_attr->is_global) {
grh = rdma_ah_read_grh(rdma_attr);
memcpy(uverb_attr->dgid, grh->dgid.raw, 16);
uverb_attr->flow_label = grh->flow_label;
uverb_attr->sgid_index = grh->sgid_index;
uverb_attr->hop_limit = grh->hop_limit;
uverb_attr->traffic_class = grh->traffic_class;
}
uverb_attr->port_num = rdma_ah_get_port_num(rdma_attr);
}
ssize_t ib_uverbs_query_qp(struct ib_uverbs_file *file, ssize_t ib_uverbs_query_qp(struct ib_uverbs_file *file,
struct ib_device *ib_dev, struct ib_device *ib_dev,
const char __user *buf, int in_len, const char __user *buf, int in_len,
...@@ -1801,7 +1852,6 @@ ssize_t ib_uverbs_query_qp(struct ib_uverbs_file *file, ...@@ -1801,7 +1852,6 @@ ssize_t ib_uverbs_query_qp(struct ib_uverbs_file *file,
struct ib_qp *qp; struct ib_qp *qp;
struct ib_qp_attr *attr; struct ib_qp_attr *attr;
struct ib_qp_init_attr *init_attr; struct ib_qp_init_attr *init_attr;
const struct ib_global_route *grh;
int ret; int ret;
if (copy_from_user(&cmd, buf, sizeof cmd)) if (copy_from_user(&cmd, buf, sizeof cmd))
...@@ -1851,39 +1901,8 @@ ssize_t ib_uverbs_query_qp(struct ib_uverbs_file *file, ...@@ -1851,39 +1901,8 @@ ssize_t ib_uverbs_query_qp(struct ib_uverbs_file *file,
resp.alt_port_num = attr->alt_port_num; resp.alt_port_num = attr->alt_port_num;
resp.alt_timeout = attr->alt_timeout; resp.alt_timeout = attr->alt_timeout;
resp.dest.dlid = rdma_ah_get_dlid(&attr->ah_attr); copy_ah_attr_to_uverbs(&resp.dest, &attr->ah_attr);
resp.dest.sl = rdma_ah_get_sl(&attr->ah_attr); copy_ah_attr_to_uverbs(&resp.alt_dest, &attr->alt_ah_attr);
resp.dest.src_path_bits = rdma_ah_get_path_bits(&attr->ah_attr);
resp.dest.static_rate = rdma_ah_get_static_rate(&attr->ah_attr);
resp.dest.is_global = !!(rdma_ah_get_ah_flags(&attr->ah_attr) &
IB_AH_GRH);
if (resp.dest.is_global) {
grh = rdma_ah_read_grh(&attr->ah_attr);
memcpy(resp.dest.dgid, grh->dgid.raw, 16);
resp.dest.flow_label = grh->flow_label;
resp.dest.sgid_index = grh->sgid_index;
resp.dest.hop_limit = grh->hop_limit;
resp.dest.traffic_class = grh->traffic_class;
}
resp.dest.port_num = rdma_ah_get_port_num(&attr->ah_attr);
resp.alt_dest.dlid = rdma_ah_get_dlid(&attr->alt_ah_attr);
resp.alt_dest.sl = rdma_ah_get_sl(&attr->alt_ah_attr);
resp.alt_dest.src_path_bits = rdma_ah_get_path_bits(&attr->alt_ah_attr);
resp.alt_dest.static_rate
= rdma_ah_get_static_rate(&attr->alt_ah_attr);
resp.alt_dest.is_global
= !!(rdma_ah_get_ah_flags(&attr->alt_ah_attr) &
IB_AH_GRH);
if (resp.alt_dest.is_global) {
grh = rdma_ah_read_grh(&attr->alt_ah_attr);
memcpy(resp.alt_dest.dgid, grh->dgid.raw, 16);
resp.alt_dest.flow_label = grh->flow_label;
resp.alt_dest.sgid_index = grh->sgid_index;
resp.alt_dest.hop_limit = grh->hop_limit;
resp.alt_dest.traffic_class = grh->traffic_class;
}
resp.alt_dest.port_num = rdma_ah_get_port_num(&attr->alt_ah_attr);
resp.max_send_wr = init_attr->cap.max_send_wr; resp.max_send_wr = init_attr->cap.max_send_wr;
resp.max_recv_wr = init_attr->cap.max_recv_wr; resp.max_recv_wr = init_attr->cap.max_recv_wr;
...@@ -1917,6 +1936,29 @@ static int modify_qp_mask(enum ib_qp_type qp_type, int mask) ...@@ -1917,6 +1936,29 @@ static int modify_qp_mask(enum ib_qp_type qp_type, int mask)
} }
} }
static void copy_ah_attr_from_uverbs(struct ib_device *dev,
struct rdma_ah_attr *rdma_attr,
struct ib_uverbs_qp_dest *uverb_attr)
{
rdma_attr->type = rdma_ah_find_type(dev, uverb_attr->port_num);
if (uverb_attr->is_global) {
rdma_ah_set_grh(rdma_attr, NULL,
uverb_attr->flow_label,
uverb_attr->sgid_index,
uverb_attr->hop_limit,
uverb_attr->traffic_class);
rdma_ah_set_dgid_raw(rdma_attr, uverb_attr->dgid);
} else {
rdma_ah_set_ah_flags(rdma_attr, 0);
}
rdma_ah_set_dlid(rdma_attr, uverb_attr->dlid);
rdma_ah_set_sl(rdma_attr, uverb_attr->sl);
rdma_ah_set_path_bits(rdma_attr, uverb_attr->src_path_bits);
rdma_ah_set_static_rate(rdma_attr, uverb_attr->static_rate);
rdma_ah_set_port_num(rdma_attr, uverb_attr->port_num);
rdma_ah_set_make_grd(rdma_attr, false);
}
static int modify_qp(struct ib_uverbs_file *file, static int modify_qp(struct ib_uverbs_file *file,
struct ib_uverbs_ex_modify_qp *cmd, struct ib_udata *udata) struct ib_uverbs_ex_modify_qp *cmd, struct ib_udata *udata)
{ {
...@@ -1964,48 +2006,12 @@ static int modify_qp(struct ib_uverbs_file *file, ...@@ -1964,48 +2006,12 @@ static int modify_qp(struct ib_uverbs_file *file,
attr->rate_limit = cmd->rate_limit; attr->rate_limit = cmd->rate_limit;
if (cmd->base.attr_mask & IB_QP_AV) if (cmd->base.attr_mask & IB_QP_AV)
attr->ah_attr.type = rdma_ah_find_type(qp->device, copy_ah_attr_from_uverbs(qp->device, &attr->ah_attr,
cmd->base.dest.port_num); &cmd->base.dest);
if (cmd->base.dest.is_global) {
rdma_ah_set_grh(&attr->ah_attr, NULL,
cmd->base.dest.flow_label,
cmd->base.dest.sgid_index,
cmd->base.dest.hop_limit,
cmd->base.dest.traffic_class);
rdma_ah_set_dgid_raw(&attr->ah_attr, cmd->base.dest.dgid);
} else {
rdma_ah_set_ah_flags(&attr->ah_attr, 0);
}
rdma_ah_set_dlid(&attr->ah_attr, cmd->base.dest.dlid);
rdma_ah_set_sl(&attr->ah_attr, cmd->base.dest.sl);
rdma_ah_set_path_bits(&attr->ah_attr, cmd->base.dest.src_path_bits);
rdma_ah_set_static_rate(&attr->ah_attr, cmd->base.dest.static_rate);
rdma_ah_set_port_num(&attr->ah_attr,
cmd->base.dest.port_num);
if (cmd->base.attr_mask & IB_QP_ALT_PATH) if (cmd->base.attr_mask & IB_QP_ALT_PATH)
attr->alt_ah_attr.type = copy_ah_attr_from_uverbs(qp->device, &attr->alt_ah_attr,
rdma_ah_find_type(qp->device, cmd->base.dest.port_num); &cmd->base.alt_dest);
if (cmd->base.alt_dest.is_global) {
rdma_ah_set_grh(&attr->alt_ah_attr, NULL,
cmd->base.alt_dest.flow_label,
cmd->base.alt_dest.sgid_index,
cmd->base.alt_dest.hop_limit,
cmd->base.alt_dest.traffic_class);
rdma_ah_set_dgid_raw(&attr->alt_ah_attr,
cmd->base.alt_dest.dgid);
} else {
rdma_ah_set_ah_flags(&attr->alt_ah_attr, 0);
}
rdma_ah_set_dlid(&attr->alt_ah_attr, cmd->base.alt_dest.dlid);
rdma_ah_set_sl(&attr->alt_ah_attr, cmd->base.alt_dest.sl);
rdma_ah_set_path_bits(&attr->alt_ah_attr,
cmd->base.alt_dest.src_path_bits);
rdma_ah_set_static_rate(&attr->alt_ah_attr,
cmd->base.alt_dest.static_rate);
rdma_ah_set_port_num(&attr->alt_ah_attr,
cmd->base.alt_dest.port_num);
ret = ib_modify_qp_with_udata(qp, attr, ret = ib_modify_qp_with_udata(qp, attr,
modify_qp_mask(qp->qp_type, modify_qp_mask(qp->qp_type,
...@@ -2037,7 +2043,8 @@ ssize_t ib_uverbs_modify_qp(struct ib_uverbs_file *file, ...@@ -2037,7 +2043,8 @@ ssize_t ib_uverbs_modify_qp(struct ib_uverbs_file *file,
return -EOPNOTSUPP; return -EOPNOTSUPP;
INIT_UDATA(&udata, buf + sizeof(cmd.base), NULL, INIT_UDATA(&udata, buf + sizeof(cmd.base), NULL,
in_len - sizeof(cmd.base), out_len); in_len - sizeof(cmd.base) - sizeof(struct ib_uverbs_cmd_hdr),
out_len);
ret = modify_qp(file, &cmd, &udata); ret = modify_qp(file, &cmd, &udata);
if (ret) if (ret)
...@@ -2543,7 +2550,8 @@ ssize_t ib_uverbs_create_ah(struct ib_uverbs_file *file, ...@@ -2543,7 +2550,8 @@ ssize_t ib_uverbs_create_ah(struct ib_uverbs_file *file,
INIT_UDATA(&udata, buf + sizeof(cmd), INIT_UDATA(&udata, buf + sizeof(cmd),
(unsigned long)cmd.response + sizeof(resp), (unsigned long)cmd.response + sizeof(resp),
in_len - sizeof(cmd), out_len - sizeof(resp)); in_len - sizeof(cmd) - sizeof(struct ib_uverbs_cmd_hdr),
out_len - sizeof(resp));
uobj = uobj_alloc(uobj_get_type(ah), file->ucontext); uobj = uobj_alloc(uobj_get_type(ah), file->ucontext);
if (IS_ERR(uobj)) if (IS_ERR(uobj))
...@@ -2556,6 +2564,7 @@ ssize_t ib_uverbs_create_ah(struct ib_uverbs_file *file, ...@@ -2556,6 +2564,7 @@ ssize_t ib_uverbs_create_ah(struct ib_uverbs_file *file,
} }
attr.type = rdma_ah_find_type(ib_dev, cmd.attr.port_num); attr.type = rdma_ah_find_type(ib_dev, cmd.attr.port_num);
rdma_ah_set_make_grd(&attr, false);
rdma_ah_set_dlid(&attr, cmd.attr.dlid); rdma_ah_set_dlid(&attr, cmd.attr.dlid);
rdma_ah_set_sl(&attr, cmd.attr.sl); rdma_ah_set_sl(&attr, cmd.attr.sl);
rdma_ah_set_path_bits(&attr, cmd.attr.src_path_bits); rdma_ah_set_path_bits(&attr, cmd.attr.src_path_bits);
...@@ -3472,6 +3481,9 @@ static int __uverbs_create_xsrq(struct ib_uverbs_file *file, ...@@ -3472,6 +3481,9 @@ static int __uverbs_create_xsrq(struct ib_uverbs_file *file,
if (IS_ERR(obj)) if (IS_ERR(obj))
return PTR_ERR(obj); return PTR_ERR(obj);
if (cmd->srq_type == IB_SRQT_TM)
attr.ext.tag_matching.max_num_tags = cmd->max_num_tags;
if (cmd->srq_type == IB_SRQT_XRC) { if (cmd->srq_type == IB_SRQT_XRC) {
xrcd_uobj = uobj_get_read(uobj_get_type(xrcd), cmd->xrcd_handle, xrcd_uobj = uobj_get_read(uobj_get_type(xrcd), cmd->xrcd_handle,
file->ucontext); file->ucontext);
...@@ -3488,10 +3500,12 @@ static int __uverbs_create_xsrq(struct ib_uverbs_file *file, ...@@ -3488,10 +3500,12 @@ static int __uverbs_create_xsrq(struct ib_uverbs_file *file,
obj->uxrcd = container_of(xrcd_uobj, struct ib_uxrcd_object, uobject); obj->uxrcd = container_of(xrcd_uobj, struct ib_uxrcd_object, uobject);
atomic_inc(&obj->uxrcd->refcnt); atomic_inc(&obj->uxrcd->refcnt);
}
attr.ext.xrc.cq = uobj_get_obj_read(cq, cmd->cq_handle, if (ib_srq_has_cq(cmd->srq_type)) {
file->ucontext); attr.ext.cq = uobj_get_obj_read(cq, cmd->cq_handle,
if (!attr.ext.xrc.cq) { file->ucontext);
if (!attr.ext.cq) {
ret = -EINVAL; ret = -EINVAL;
goto err_put_xrcd; goto err_put_xrcd;
} }
...@@ -3526,10 +3540,13 @@ static int __uverbs_create_xsrq(struct ib_uverbs_file *file, ...@@ -3526,10 +3540,13 @@ static int __uverbs_create_xsrq(struct ib_uverbs_file *file,
srq->event_handler = attr.event_handler; srq->event_handler = attr.event_handler;
srq->srq_context = attr.srq_context; srq->srq_context = attr.srq_context;
if (ib_srq_has_cq(cmd->srq_type)) {
srq->ext.cq = attr.ext.cq;
atomic_inc(&attr.ext.cq->usecnt);
}
if (cmd->srq_type == IB_SRQT_XRC) { if (cmd->srq_type == IB_SRQT_XRC) {
srq->ext.xrc.cq = attr.ext.xrc.cq;
srq->ext.xrc.xrcd = attr.ext.xrc.xrcd; srq->ext.xrc.xrcd = attr.ext.xrc.xrcd;
atomic_inc(&attr.ext.xrc.cq->usecnt);
atomic_inc(&attr.ext.xrc.xrcd->usecnt); atomic_inc(&attr.ext.xrc.xrcd->usecnt);
} }
...@@ -3552,10 +3569,12 @@ static int __uverbs_create_xsrq(struct ib_uverbs_file *file, ...@@ -3552,10 +3569,12 @@ static int __uverbs_create_xsrq(struct ib_uverbs_file *file,
goto err_copy; goto err_copy;
} }
if (cmd->srq_type == IB_SRQT_XRC) { if (cmd->srq_type == IB_SRQT_XRC)
uobj_put_read(xrcd_uobj); uobj_put_read(xrcd_uobj);
uobj_put_obj_read(attr.ext.xrc.cq);
} if (ib_srq_has_cq(cmd->srq_type))
uobj_put_obj_read(attr.ext.cq);
uobj_put_obj_read(pd); uobj_put_obj_read(pd);
uobj_alloc_commit(&obj->uevent.uobject); uobj_alloc_commit(&obj->uevent.uobject);
...@@ -3568,8 +3587,8 @@ static int __uverbs_create_xsrq(struct ib_uverbs_file *file, ...@@ -3568,8 +3587,8 @@ static int __uverbs_create_xsrq(struct ib_uverbs_file *file,
uobj_put_obj_read(pd); uobj_put_obj_read(pd);
err_put_cq: err_put_cq:
if (cmd->srq_type == IB_SRQT_XRC) if (ib_srq_has_cq(cmd->srq_type))
uobj_put_obj_read(attr.ext.xrc.cq); uobj_put_obj_read(attr.ext.cq);
err_put_xrcd: err_put_xrcd:
if (cmd->srq_type == IB_SRQT_XRC) { if (cmd->srq_type == IB_SRQT_XRC) {
...@@ -3599,6 +3618,7 @@ ssize_t ib_uverbs_create_srq(struct ib_uverbs_file *file, ...@@ -3599,6 +3618,7 @@ ssize_t ib_uverbs_create_srq(struct ib_uverbs_file *file,
if (copy_from_user(&cmd, buf, sizeof cmd)) if (copy_from_user(&cmd, buf, sizeof cmd))
return -EFAULT; return -EFAULT;
memset(&xcmd, 0, sizeof(xcmd));
xcmd.response = cmd.response; xcmd.response = cmd.response;
xcmd.user_handle = cmd.user_handle; xcmd.user_handle = cmd.user_handle;
xcmd.srq_type = IB_SRQT_BASIC; xcmd.srq_type = IB_SRQT_BASIC;
...@@ -3607,10 +3627,10 @@ ssize_t ib_uverbs_create_srq(struct ib_uverbs_file *file, ...@@ -3607,10 +3627,10 @@ ssize_t ib_uverbs_create_srq(struct ib_uverbs_file *file,
xcmd.max_sge = cmd.max_sge; xcmd.max_sge = cmd.max_sge;
xcmd.srq_limit = cmd.srq_limit; xcmd.srq_limit = cmd.srq_limit;
INIT_UDATA(&udata, buf + sizeof cmd, INIT_UDATA(&udata, buf + sizeof(cmd),
(unsigned long) cmd.response + sizeof resp, (unsigned long) cmd.response + sizeof(resp),
in_len - sizeof cmd - sizeof(struct ib_uverbs_cmd_hdr), in_len - sizeof(cmd) - sizeof(struct ib_uverbs_cmd_hdr),
out_len - sizeof resp); out_len - sizeof(resp));
ret = __uverbs_create_xsrq(file, ib_dev, &xcmd, &udata); ret = __uverbs_create_xsrq(file, ib_dev, &xcmd, &udata);
if (ret) if (ret)
...@@ -3634,10 +3654,10 @@ ssize_t ib_uverbs_create_xsrq(struct ib_uverbs_file *file, ...@@ -3634,10 +3654,10 @@ ssize_t ib_uverbs_create_xsrq(struct ib_uverbs_file *file,
if (copy_from_user(&cmd, buf, sizeof cmd)) if (copy_from_user(&cmd, buf, sizeof cmd))
return -EFAULT; return -EFAULT;
INIT_UDATA(&udata, buf + sizeof cmd, INIT_UDATA(&udata, buf + sizeof(cmd),
(unsigned long) cmd.response + sizeof resp, (unsigned long) cmd.response + sizeof(resp),
in_len - sizeof cmd - sizeof(struct ib_uverbs_cmd_hdr), in_len - sizeof(cmd) - sizeof(struct ib_uverbs_cmd_hdr),
out_len - sizeof resp); out_len - sizeof(resp));
ret = __uverbs_create_xsrq(file, ib_dev, &cmd, &udata); ret = __uverbs_create_xsrq(file, ib_dev, &cmd, &udata);
if (ret) if (ret)
...@@ -3848,6 +3868,16 @@ int ib_uverbs_ex_query_device(struct ib_uverbs_file *file, ...@@ -3848,6 +3868,16 @@ int ib_uverbs_ex_query_device(struct ib_uverbs_file *file,
resp.raw_packet_caps = attr.raw_packet_caps; resp.raw_packet_caps = attr.raw_packet_caps;
resp.response_length += sizeof(resp.raw_packet_caps); resp.response_length += sizeof(resp.raw_packet_caps);
if (ucore->outlen < resp.response_length + sizeof(resp.xrq_caps))
goto end;
resp.xrq_caps.max_rndv_hdr_size = attr.xrq_caps.max_rndv_hdr_size;
resp.xrq_caps.max_num_tags = attr.xrq_caps.max_num_tags;
resp.xrq_caps.max_ops = attr.xrq_caps.max_ops;
resp.xrq_caps.max_sge = attr.xrq_caps.max_sge;
resp.xrq_caps.flags = attr.xrq_caps.flags;
resp.response_length += sizeof(resp.xrq_caps);
end: end:
err = ib_copy_to_udata(ucore, &resp, resp.response_length); err = ib_copy_to_udata(ucore, &resp, resp.response_length);
return err; return err;
......
/*
* Copyright (c) 2017, Mellanox Technologies inc. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* OpenIB.org BSD license below:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <rdma/rdma_user_ioctl.h>
#include <rdma/uverbs_ioctl.h>
#include "rdma_core.h"
#include "uverbs.h"
static int uverbs_process_attr(struct ib_device *ibdev,
struct ib_ucontext *ucontext,
const struct ib_uverbs_attr *uattr,
u16 attr_id,
const struct uverbs_attr_spec_hash *attr_spec_bucket,
struct uverbs_attr_bundle_hash *attr_bundle_h,
struct ib_uverbs_attr __user *uattr_ptr)
{
const struct uverbs_attr_spec *spec;
struct uverbs_attr *e;
const struct uverbs_object_spec *object;
struct uverbs_obj_attr *o_attr;
struct uverbs_attr *elements = attr_bundle_h->attrs;
if (uattr->reserved)
return -EINVAL;
if (attr_id >= attr_spec_bucket->num_attrs) {
if (uattr->flags & UVERBS_ATTR_F_MANDATORY)
return -EINVAL;
else
return 0;
}
spec = &attr_spec_bucket->attrs[attr_id];
e = &elements[attr_id];
e->uattr = uattr_ptr;
switch (spec->type) {
case UVERBS_ATTR_TYPE_PTR_IN:
case UVERBS_ATTR_TYPE_PTR_OUT:
if (uattr->len < spec->len ||
(!(spec->flags & UVERBS_ATTR_SPEC_F_MIN_SZ) &&
uattr->len > spec->len))
return -EINVAL;
e->ptr_attr.data = uattr->data;
e->ptr_attr.len = uattr->len;
e->ptr_attr.flags = uattr->flags;
break;
case UVERBS_ATTR_TYPE_IDR:
if (uattr->data >> 32)
return -EINVAL;
/* fall through */
case UVERBS_ATTR_TYPE_FD:
if (uattr->len != 0 || !ucontext || uattr->data > INT_MAX)
return -EINVAL;
o_attr = &e->obj_attr;
object = uverbs_get_object(ibdev, spec->obj.obj_type);
if (!object)
return -EINVAL;
o_attr->type = object->type_attrs;
o_attr->id = (int)uattr->data;
o_attr->uobject = uverbs_get_uobject_from_context(
o_attr->type,
ucontext,
spec->obj.access,
o_attr->id);
if (IS_ERR(o_attr->uobject))
return PTR_ERR(o_attr->uobject);
if (spec->obj.access == UVERBS_ACCESS_NEW) {
u64 id = o_attr->uobject->id;
/* Copy the allocated id to the user-space */
if (put_user(id, &e->uattr->data)) {
uverbs_finalize_object(o_attr->uobject,
UVERBS_ACCESS_NEW,
false);
return -EFAULT;
}
}
break;
default:
return -EOPNOTSUPP;
}
set_bit(attr_id, attr_bundle_h->valid_bitmap);
return 0;
}
static int uverbs_uattrs_process(struct ib_device *ibdev,
struct ib_ucontext *ucontext,
const struct ib_uverbs_attr *uattrs,
size_t num_uattrs,
const struct uverbs_method_spec *method,
struct uverbs_attr_bundle *attr_bundle,
struct ib_uverbs_attr __user *uattr_ptr)
{
size_t i;
int ret = 0;
int num_given_buckets = 0;
for (i = 0; i < num_uattrs; i++) {
const struct ib_uverbs_attr *uattr = &uattrs[i];
u16 attr_id = uattr->attr_id;
struct uverbs_attr_spec_hash *attr_spec_bucket;
ret = uverbs_ns_idx(&attr_id, method->num_buckets);
if (ret < 0) {
if (uattr->flags & UVERBS_ATTR_F_MANDATORY) {
uverbs_finalize_objects(attr_bundle,
method->attr_buckets,
num_given_buckets,
false);
return ret;
}
continue;
}
/*
* ret is the found ns, so increase num_given_buckets if
* necessary.
*/
if (ret >= num_given_buckets)
num_given_buckets = ret + 1;
attr_spec_bucket = method->attr_buckets[ret];
ret = uverbs_process_attr(ibdev, ucontext, uattr, attr_id,
attr_spec_bucket, &attr_bundle->hash[ret],
uattr_ptr++);
if (ret) {
uverbs_finalize_objects(attr_bundle,
method->attr_buckets,
num_given_buckets,
false);
return ret;
}
}
return num_given_buckets;
}
static int uverbs_validate_kernel_mandatory(const struct uverbs_method_spec *method_spec,
struct uverbs_attr_bundle *attr_bundle)
{
unsigned int i;
for (i = 0; i < attr_bundle->num_buckets; i++) {
struct uverbs_attr_spec_hash *attr_spec_bucket =
method_spec->attr_buckets[i];
if (!bitmap_subset(attr_spec_bucket->mandatory_attrs_bitmask,
attr_bundle->hash[i].valid_bitmap,
attr_spec_bucket->num_attrs))
return -EINVAL;
}
return 0;
}
static int uverbs_handle_method(struct ib_uverbs_attr __user *uattr_ptr,
const struct ib_uverbs_attr *uattrs,
size_t num_uattrs,
struct ib_device *ibdev,
struct ib_uverbs_file *ufile,
const struct uverbs_method_spec *method_spec,
struct uverbs_attr_bundle *attr_bundle)
{
int ret;
int finalize_ret;
int num_given_buckets;
num_given_buckets = uverbs_uattrs_process(ibdev, ufile->ucontext, uattrs,
num_uattrs, method_spec,
attr_bundle, uattr_ptr);
if (num_given_buckets <= 0)
return -EINVAL;
attr_bundle->num_buckets = num_given_buckets;
ret = uverbs_validate_kernel_mandatory(method_spec, attr_bundle);
if (ret)
goto cleanup;
ret = method_spec->handler(ibdev, ufile, attr_bundle);
cleanup:
finalize_ret = uverbs_finalize_objects(attr_bundle,
method_spec->attr_buckets,
attr_bundle->num_buckets,
!ret);
return ret ? ret : finalize_ret;
}
#define UVERBS_OPTIMIZE_USING_STACK_SZ 256
static long ib_uverbs_cmd_verbs(struct ib_device *ib_dev,
struct ib_uverbs_file *file,
struct ib_uverbs_ioctl_hdr *hdr,
void __user *buf)
{
const struct uverbs_object_spec *object_spec;
const struct uverbs_method_spec *method_spec;
long err = 0;
unsigned int i;
struct {
struct ib_uverbs_attr *uattrs;
struct uverbs_attr_bundle *uverbs_attr_bundle;
} *ctx = NULL;
struct uverbs_attr *curr_attr;
unsigned long *curr_bitmap;
size_t ctx_size;
#ifdef UVERBS_OPTIMIZE_USING_STACK_SZ
uintptr_t data[UVERBS_OPTIMIZE_USING_STACK_SZ / sizeof(uintptr_t)];
#endif
if (hdr->reserved)
return -EINVAL;
object_spec = uverbs_get_object(ib_dev, hdr->object_id);
if (!object_spec)
return -EOPNOTSUPP;
method_spec = uverbs_get_method(object_spec, hdr->method_id);
if (!method_spec)
return -EOPNOTSUPP;
if ((method_spec->flags & UVERBS_ACTION_FLAG_CREATE_ROOT) ^ !file->ucontext)
return -EINVAL;
ctx_size = sizeof(*ctx) +
sizeof(struct uverbs_attr_bundle) +
sizeof(struct uverbs_attr_bundle_hash) * method_spec->num_buckets +
sizeof(*ctx->uattrs) * hdr->num_attrs +
sizeof(*ctx->uverbs_attr_bundle->hash[0].attrs) *
method_spec->num_child_attrs +
sizeof(*ctx->uverbs_attr_bundle->hash[0].valid_bitmap) *
(method_spec->num_child_attrs / BITS_PER_LONG +
method_spec->num_buckets);
#ifdef UVERBS_OPTIMIZE_USING_STACK_SZ
if (ctx_size <= UVERBS_OPTIMIZE_USING_STACK_SZ)
ctx = (void *)data;
if (!ctx)
#endif
ctx = kmalloc(ctx_size, GFP_KERNEL);
if (!ctx)
return -ENOMEM;
ctx->uverbs_attr_bundle = (void *)ctx + sizeof(*ctx);
ctx->uattrs = (void *)(ctx->uverbs_attr_bundle + 1) +
(sizeof(ctx->uverbs_attr_bundle->hash[0]) *
method_spec->num_buckets);
curr_attr = (void *)(ctx->uattrs + hdr->num_attrs);
curr_bitmap = (void *)(curr_attr + method_spec->num_child_attrs);
/*
* We just fill the pointers and num_attrs here. The data itself will be
* filled at a later stage (uverbs_process_attr)
*/
for (i = 0; i < method_spec->num_buckets; i++) {
unsigned int curr_num_attrs = method_spec->attr_buckets[i]->num_attrs;
ctx->uverbs_attr_bundle->hash[i].attrs = curr_attr;
curr_attr += curr_num_attrs;
ctx->uverbs_attr_bundle->hash[i].num_attrs = curr_num_attrs;
ctx->uverbs_attr_bundle->hash[i].valid_bitmap = curr_bitmap;
bitmap_zero(curr_bitmap, curr_num_attrs);
curr_bitmap += BITS_TO_LONGS(curr_num_attrs);
}
err = copy_from_user(ctx->uattrs, buf,
sizeof(*ctx->uattrs) * hdr->num_attrs);
if (err) {
err = -EFAULT;
goto out;
}
err = uverbs_handle_method(buf, ctx->uattrs, hdr->num_attrs, ib_dev,
file, method_spec, ctx->uverbs_attr_bundle);
out:
#ifdef UVERBS_OPTIMIZE_USING_STACK_SZ
if (ctx_size > UVERBS_OPTIMIZE_USING_STACK_SZ)
#endif
kfree(ctx);
return err;
}
#define IB_UVERBS_MAX_CMD_SZ 4096
long ib_uverbs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct ib_uverbs_file *file = filp->private_data;
struct ib_uverbs_ioctl_hdr __user *user_hdr =
(struct ib_uverbs_ioctl_hdr __user *)arg;
struct ib_uverbs_ioctl_hdr hdr;
struct ib_device *ib_dev;
int srcu_key;
long err;
srcu_key = srcu_read_lock(&file->device->disassociate_srcu);
ib_dev = srcu_dereference(file->device->ib_dev,
&file->device->disassociate_srcu);
if (!ib_dev) {
err = -EIO;
goto out;
}
if (cmd == RDMA_VERBS_IOCTL) {
err = copy_from_user(&hdr, user_hdr, sizeof(hdr));
if (err || hdr.length > IB_UVERBS_MAX_CMD_SZ ||
hdr.length != sizeof(hdr) + hdr.num_attrs * sizeof(struct ib_uverbs_attr)) {
err = -EINVAL;
goto out;
}
if (hdr.reserved) {
err = -EOPNOTSUPP;
goto out;
}
err = ib_uverbs_cmd_verbs(ib_dev, file, &hdr,
(__user void *)arg + sizeof(hdr));
} else {
err = -ENOIOCTLCMD;
}
out:
srcu_read_unlock(&file->device->disassociate_srcu, srcu_key);
return err;
}
此差异已折叠。
...@@ -49,6 +49,7 @@ ...@@ -49,6 +49,7 @@
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <rdma/ib.h> #include <rdma/ib.h>
#include <rdma/uverbs_std_types.h>
#include "uverbs.h" #include "uverbs.h"
#include "core_priv.h" #include "core_priv.h"
...@@ -595,7 +596,6 @@ struct file *ib_uverbs_alloc_async_event_file(struct ib_uverbs_file *uverbs_file ...@@ -595,7 +596,6 @@ struct file *ib_uverbs_alloc_async_event_file(struct ib_uverbs_file *uverbs_file
{ {
struct ib_uverbs_async_event_file *ev_file; struct ib_uverbs_async_event_file *ev_file;
struct file *filp; struct file *filp;
int ret;
ev_file = kzalloc(sizeof(*ev_file), GFP_KERNEL); ev_file = kzalloc(sizeof(*ev_file), GFP_KERNEL);
if (!ev_file) if (!ev_file)
...@@ -621,21 +621,11 @@ struct file *ib_uverbs_alloc_async_event_file(struct ib_uverbs_file *uverbs_file ...@@ -621,21 +621,11 @@ struct file *ib_uverbs_alloc_async_event_file(struct ib_uverbs_file *uverbs_file
INIT_IB_EVENT_HANDLER(&uverbs_file->event_handler, INIT_IB_EVENT_HANDLER(&uverbs_file->event_handler,
ib_dev, ib_dev,
ib_uverbs_event_handler); ib_uverbs_event_handler);
ret = ib_register_event_handler(&uverbs_file->event_handler); ib_register_event_handler(&uverbs_file->event_handler);
if (ret)
goto err_put_file;
/* At that point async file stuff was fully set */ /* At that point async file stuff was fully set */
return filp; return filp;
err_put_file:
fput(filp);
kref_put(&uverbs_file->async_file->ref,
ib_uverbs_release_async_event_file);
uverbs_file->async_file = NULL;
return ERR_PTR(ret);
err_put_refs: err_put_refs:
kref_put(&ev_file->uverbs_file->ref, ib_uverbs_release_file); kref_put(&ev_file->uverbs_file->ref, ib_uverbs_release_file);
kref_put(&ev_file->ref, ib_uverbs_release_async_event_file); kref_put(&ev_file->ref, ib_uverbs_release_async_event_file);
...@@ -949,6 +939,9 @@ static const struct file_operations uverbs_fops = { ...@@ -949,6 +939,9 @@ static const struct file_operations uverbs_fops = {
.open = ib_uverbs_open, .open = ib_uverbs_open,
.release = ib_uverbs_close, .release = ib_uverbs_close,
.llseek = no_llseek, .llseek = no_llseek,
#if IS_ENABLED(CONFIG_INFINIBAND_EXP_USER_ACCESS)
.unlocked_ioctl = ib_uverbs_ioctl,
#endif
}; };
static const struct file_operations uverbs_mmap_fops = { static const struct file_operations uverbs_mmap_fops = {
...@@ -958,6 +951,9 @@ static const struct file_operations uverbs_mmap_fops = { ...@@ -958,6 +951,9 @@ static const struct file_operations uverbs_mmap_fops = {
.open = ib_uverbs_open, .open = ib_uverbs_open,
.release = ib_uverbs_close, .release = ib_uverbs_close,
.llseek = no_llseek, .llseek = no_llseek,
#if IS_ENABLED(CONFIG_INFINIBAND_EXP_USER_ACCESS)
.unlocked_ioctl = ib_uverbs_ioctl,
#endif
}; };
static struct ib_client uverbs_client = { static struct ib_client uverbs_client = {
...@@ -1108,6 +1104,18 @@ static void ib_uverbs_add_one(struct ib_device *device) ...@@ -1108,6 +1104,18 @@ static void ib_uverbs_add_one(struct ib_device *device)
if (device_create_file(uverbs_dev->dev, &dev_attr_abi_version)) if (device_create_file(uverbs_dev->dev, &dev_attr_abi_version))
goto err_class; goto err_class;
if (!device->specs_root) {
const struct uverbs_object_tree_def *default_root[] = {
uverbs_default_get_objects()};
uverbs_dev->specs_root = uverbs_alloc_spec_tree(1,
default_root);
if (IS_ERR(uverbs_dev->specs_root))
goto err_class;
device->specs_root = uverbs_dev->specs_root;
}
ib_set_client_data(device, &uverbs_client, uverbs_dev); ib_set_client_data(device, &uverbs_client, uverbs_dev);
return; return;
...@@ -1239,6 +1247,11 @@ static void ib_uverbs_remove_one(struct ib_device *device, void *client_data) ...@@ -1239,6 +1247,11 @@ static void ib_uverbs_remove_one(struct ib_device *device, void *client_data)
ib_uverbs_comp_dev(uverbs_dev); ib_uverbs_comp_dev(uverbs_dev);
if (wait_clients) if (wait_clients)
wait_for_completion(&uverbs_dev->comp); wait_for_completion(&uverbs_dev->comp);
if (uverbs_dev->specs_root) {
uverbs_free_spec_tree(uverbs_dev->specs_root);
device->specs_root = NULL;
}
kobject_put(&uverbs_dev->kobj); kobject_put(&uverbs_dev->kobj);
} }
......
...@@ -33,10 +33,47 @@ ...@@ -33,10 +33,47 @@
#include <linux/export.h> #include <linux/export.h>
#include <rdma/ib_marshall.h> #include <rdma/ib_marshall.h>
void ib_copy_ah_attr_to_user(struct ib_uverbs_ah_attr *dst, #define OPA_DEFAULT_GID_PREFIX cpu_to_be64(0xfe80000000000000ULL)
struct rdma_ah_attr *src) static int rdma_ah_conv_opa_to_ib(struct ib_device *dev,
struct rdma_ah_attr *ib,
struct rdma_ah_attr *opa)
{ {
struct ib_port_attr port_attr;
int ret = 0;
/* Do structure copy and the over-write fields */
*ib = *opa;
ib->type = RDMA_AH_ATTR_TYPE_IB;
rdma_ah_set_grh(ib, NULL, 0, 0, 1, 0);
if (ib_query_port(dev, opa->port_num, &port_attr)) {
/* Set to default subnet to indicate error */
rdma_ah_set_subnet_prefix(ib, OPA_DEFAULT_GID_PREFIX);
ret = -EINVAL;
} else {
rdma_ah_set_subnet_prefix(ib,
cpu_to_be64(port_attr.subnet_prefix));
}
rdma_ah_set_interface_id(ib, OPA_MAKE_ID(rdma_ah_get_dlid(opa)));
return ret;
}
void ib_copy_ah_attr_to_user(struct ib_device *device,
struct ib_uverbs_ah_attr *dst,
struct rdma_ah_attr *ah_attr)
{
struct rdma_ah_attr *src = ah_attr;
struct rdma_ah_attr conv_ah;
memset(&dst->grh.reserved, 0, sizeof(dst->grh.reserved)); memset(&dst->grh.reserved, 0, sizeof(dst->grh.reserved));
if ((ah_attr->type == RDMA_AH_ATTR_TYPE_OPA) &&
(rdma_ah_get_dlid(ah_attr) >=
be16_to_cpu(IB_MULTICAST_LID_BASE)) &&
(!rdma_ah_conv_opa_to_ib(device, &conv_ah, ah_attr)))
src = &conv_ah;
dst->dlid = rdma_ah_get_dlid(src); dst->dlid = rdma_ah_get_dlid(src);
dst->sl = rdma_ah_get_sl(src); dst->sl = rdma_ah_get_sl(src);
dst->src_path_bits = rdma_ah_get_path_bits(src); dst->src_path_bits = rdma_ah_get_path_bits(src);
...@@ -57,7 +94,8 @@ void ib_copy_ah_attr_to_user(struct ib_uverbs_ah_attr *dst, ...@@ -57,7 +94,8 @@ void ib_copy_ah_attr_to_user(struct ib_uverbs_ah_attr *dst,
} }
EXPORT_SYMBOL(ib_copy_ah_attr_to_user); EXPORT_SYMBOL(ib_copy_ah_attr_to_user);
void ib_copy_qp_attr_to_user(struct ib_uverbs_qp_attr *dst, void ib_copy_qp_attr_to_user(struct ib_device *device,
struct ib_uverbs_qp_attr *dst,
struct ib_qp_attr *src) struct ib_qp_attr *src)
{ {
dst->qp_state = src->qp_state; dst->qp_state = src->qp_state;
...@@ -76,8 +114,8 @@ void ib_copy_qp_attr_to_user(struct ib_uverbs_qp_attr *dst, ...@@ -76,8 +114,8 @@ void ib_copy_qp_attr_to_user(struct ib_uverbs_qp_attr *dst,
dst->max_recv_sge = src->cap.max_recv_sge; dst->max_recv_sge = src->cap.max_recv_sge;
dst->max_inline_data = src->cap.max_inline_data; dst->max_inline_data = src->cap.max_inline_data;
ib_copy_ah_attr_to_user(&dst->ah_attr, &src->ah_attr); ib_copy_ah_attr_to_user(device, &dst->ah_attr, &src->ah_attr);
ib_copy_ah_attr_to_user(&dst->alt_ah_attr, &src->alt_ah_attr); ib_copy_ah_attr_to_user(device, &dst->alt_ah_attr, &src->alt_ah_attr);
dst->pkey_index = src->pkey_index; dst->pkey_index = src->pkey_index;
dst->alt_pkey_index = src->alt_pkey_index; dst->alt_pkey_index = src->alt_pkey_index;
......
...@@ -209,67 +209,244 @@ static int uverbs_hot_unplug_completion_event_file(struct ib_uobject_file *uobj_ ...@@ -209,67 +209,244 @@ static int uverbs_hot_unplug_completion_event_file(struct ib_uobject_file *uobj_
return 0; return 0;
}; };
const struct uverbs_obj_fd_type uverbs_type_attrs_comp_channel = { /*
.type = UVERBS_TYPE_ALLOC_FD(sizeof(struct ib_uverbs_completion_event_file), 0), * This spec is used in order to pass information to the hardware driver in a
.context_closed = uverbs_hot_unplug_completion_event_file, * legacy way. Every verb that could get driver specific data should get this
.fops = &uverbs_event_fops, * spec.
.name = "[infinibandevent]", */
.flags = O_RDONLY, static const struct uverbs_attr_def uverbs_uhw_compat_in =
}; UVERBS_ATTR_PTR_IN_SZ(UVERBS_UHW_IN, 0, UA_FLAGS(UVERBS_ATTR_SPEC_F_MIN_SZ));
static const struct uverbs_attr_def uverbs_uhw_compat_out =
UVERBS_ATTR_PTR_OUT_SZ(UVERBS_UHW_OUT, 0, UA_FLAGS(UVERBS_ATTR_SPEC_F_MIN_SZ));
const struct uverbs_obj_idr_type uverbs_type_attrs_cq = { static void create_udata(struct uverbs_attr_bundle *ctx,
.type = UVERBS_TYPE_ALLOC_IDR_SZ(sizeof(struct ib_ucq_object), 0), struct ib_udata *udata)
.destroy_object = uverbs_free_cq, {
}; /*
* This is for ease of conversion. The purpose is to convert all drivers
* to use uverbs_attr_bundle instead of ib_udata.
* Assume attr == 0 is input and attr == 1 is output.
*/
void __user *inbuf;
size_t inbuf_len = 0;
void __user *outbuf;
size_t outbuf_len = 0;
const struct uverbs_attr *uhw_in =
uverbs_attr_get(ctx, UVERBS_UHW_IN);
const struct uverbs_attr *uhw_out =
uverbs_attr_get(ctx, UVERBS_UHW_OUT);
if (!IS_ERR(uhw_in)) {
inbuf = uhw_in->ptr_attr.ptr;
inbuf_len = uhw_in->ptr_attr.len;
}
const struct uverbs_obj_idr_type uverbs_type_attrs_qp = { if (!IS_ERR(uhw_out)) {
.type = UVERBS_TYPE_ALLOC_IDR_SZ(sizeof(struct ib_uqp_object), 0), outbuf = uhw_out->ptr_attr.ptr;
.destroy_object = uverbs_free_qp, outbuf_len = uhw_out->ptr_attr.len;
}; }
const struct uverbs_obj_idr_type uverbs_type_attrs_mw = { INIT_UDATA_BUF_OR_NULL(udata, inbuf, outbuf, inbuf_len, outbuf_len);
.type = UVERBS_TYPE_ALLOC_IDR(0), }
.destroy_object = uverbs_free_mw,
};
const struct uverbs_obj_idr_type uverbs_type_attrs_mr = { static int uverbs_create_cq_handler(struct ib_device *ib_dev,
/* 1 is used in order to free the MR after all the MWs */ struct ib_uverbs_file *file,
.type = UVERBS_TYPE_ALLOC_IDR(1), struct uverbs_attr_bundle *attrs)
.destroy_object = uverbs_free_mr, {
}; struct ib_ucontext *ucontext = file->ucontext;
struct ib_ucq_object *obj;
struct ib_udata uhw;
int ret;
u64 user_handle;
struct ib_cq_init_attr attr = {};
struct ib_cq *cq;
struct ib_uverbs_completion_event_file *ev_file = NULL;
const struct uverbs_attr *ev_file_attr;
struct ib_uobject *ev_file_uobj;
if (!(ib_dev->uverbs_cmd_mask & 1ULL << IB_USER_VERBS_CMD_CREATE_CQ))
return -EOPNOTSUPP;
ret = uverbs_copy_from(&attr.comp_vector, attrs, CREATE_CQ_COMP_VECTOR);
if (!ret)
ret = uverbs_copy_from(&attr.cqe, attrs, CREATE_CQ_CQE);
if (!ret)
ret = uverbs_copy_from(&user_handle, attrs, CREATE_CQ_USER_HANDLE);
if (ret)
return ret;
const struct uverbs_obj_idr_type uverbs_type_attrs_srq = { /* Optional param, if it doesn't exist, we get -ENOENT and skip it */
.type = UVERBS_TYPE_ALLOC_IDR_SZ(sizeof(struct ib_usrq_object), 0), if (uverbs_copy_from(&attr.flags, attrs, CREATE_CQ_FLAGS) == -EFAULT)
.destroy_object = uverbs_free_srq, return -EFAULT;
};
const struct uverbs_obj_idr_type uverbs_type_attrs_ah = { ev_file_attr = uverbs_attr_get(attrs, CREATE_CQ_COMP_CHANNEL);
.type = UVERBS_TYPE_ALLOC_IDR(0), if (!IS_ERR(ev_file_attr)) {
.destroy_object = uverbs_free_ah, ev_file_uobj = ev_file_attr->obj_attr.uobject;
};
const struct uverbs_obj_idr_type uverbs_type_attrs_flow = { ev_file = container_of(ev_file_uobj,
.type = UVERBS_TYPE_ALLOC_IDR(0), struct ib_uverbs_completion_event_file,
.destroy_object = uverbs_free_flow, uobj_file.uobj);
}; uverbs_uobject_get(ev_file_uobj);
}
const struct uverbs_obj_idr_type uverbs_type_attrs_wq = { if (attr.comp_vector >= ucontext->ufile->device->num_comp_vectors) {
.type = UVERBS_TYPE_ALLOC_IDR_SZ(sizeof(struct ib_uwq_object), 0), ret = -EINVAL;
.destroy_object = uverbs_free_wq, goto err_event_file;
}; }
const struct uverbs_obj_idr_type uverbs_type_attrs_rwq_ind_table = { obj = container_of(uverbs_attr_get(attrs, CREATE_CQ_HANDLE)->obj_attr.uobject,
.type = UVERBS_TYPE_ALLOC_IDR(0), typeof(*obj), uobject);
.destroy_object = uverbs_free_rwq_ind_tbl, obj->uverbs_file = ucontext->ufile;
}; obj->comp_events_reported = 0;
obj->async_events_reported = 0;
INIT_LIST_HEAD(&obj->comp_list);
INIT_LIST_HEAD(&obj->async_list);
/* Temporary, only until drivers get the new uverbs_attr_bundle */
create_udata(attrs, &uhw);
cq = ib_dev->create_cq(ib_dev, &attr, ucontext, &uhw);
if (IS_ERR(cq)) {
ret = PTR_ERR(cq);
goto err_event_file;
}
const struct uverbs_obj_idr_type uverbs_type_attrs_xrcd = { cq->device = ib_dev;
.type = UVERBS_TYPE_ALLOC_IDR_SZ(sizeof(struct ib_uxrcd_object), 0), cq->uobject = &obj->uobject;
.destroy_object = uverbs_free_xrcd, cq->comp_handler = ib_uverbs_comp_handler;
}; cq->event_handler = ib_uverbs_cq_event_handler;
cq->cq_context = &ev_file->ev_queue;
obj->uobject.object = cq;
obj->uobject.user_handle = user_handle;
atomic_set(&cq->usecnt, 0);
ret = uverbs_copy_to(attrs, CREATE_CQ_RESP_CQE, &cq->cqe);
if (ret)
goto err_cq;
const struct uverbs_obj_idr_type uverbs_type_attrs_pd = { return 0;
/* 2 is used in order to free the PD after MRs */ err_cq:
.type = UVERBS_TYPE_ALLOC_IDR(2), ib_destroy_cq(cq);
.destroy_object = uverbs_free_pd,
err_event_file:
if (ev_file)
uverbs_uobject_put(ev_file_uobj);
return ret;
}; };
static DECLARE_UVERBS_METHOD(
uverbs_method_cq_create, UVERBS_CQ_CREATE, uverbs_create_cq_handler,
&UVERBS_ATTR_IDR(CREATE_CQ_HANDLE, UVERBS_OBJECT_CQ, UVERBS_ACCESS_NEW,
UA_FLAGS(UVERBS_ATTR_SPEC_F_MANDATORY)),
&UVERBS_ATTR_PTR_IN(CREATE_CQ_CQE, u32,
UA_FLAGS(UVERBS_ATTR_SPEC_F_MANDATORY)),
&UVERBS_ATTR_PTR_IN(CREATE_CQ_USER_HANDLE, u64,
UA_FLAGS(UVERBS_ATTR_SPEC_F_MANDATORY)),
&UVERBS_ATTR_FD(CREATE_CQ_COMP_CHANNEL, UVERBS_OBJECT_COMP_CHANNEL,
UVERBS_ACCESS_READ),
&UVERBS_ATTR_PTR_IN(CREATE_CQ_COMP_VECTOR, u32,
UA_FLAGS(UVERBS_ATTR_SPEC_F_MANDATORY)),
&UVERBS_ATTR_PTR_IN(CREATE_CQ_FLAGS, u32),
&UVERBS_ATTR_PTR_OUT(CREATE_CQ_RESP_CQE, u32,
UA_FLAGS(UVERBS_ATTR_SPEC_F_MANDATORY)),
&uverbs_uhw_compat_in, &uverbs_uhw_compat_out);
static int uverbs_destroy_cq_handler(struct ib_device *ib_dev,
struct ib_uverbs_file *file,
struct uverbs_attr_bundle *attrs)
{
struct ib_uverbs_destroy_cq_resp resp;
struct ib_uobject *uobj =
uverbs_attr_get(attrs, DESTROY_CQ_HANDLE)->obj_attr.uobject;
struct ib_ucq_object *obj = container_of(uobj, struct ib_ucq_object,
uobject);
int ret;
if (!(ib_dev->uverbs_cmd_mask & 1ULL << IB_USER_VERBS_CMD_DESTROY_CQ))
return -EOPNOTSUPP;
ret = rdma_explicit_destroy(uobj);
if (ret)
return ret;
resp.comp_events_reported = obj->comp_events_reported;
resp.async_events_reported = obj->async_events_reported;
return uverbs_copy_to(attrs, DESTROY_CQ_RESP, &resp);
}
static DECLARE_UVERBS_METHOD(
uverbs_method_cq_destroy, UVERBS_CQ_DESTROY, uverbs_destroy_cq_handler,
&UVERBS_ATTR_IDR(DESTROY_CQ_HANDLE, UVERBS_OBJECT_CQ,
UVERBS_ACCESS_DESTROY,
UA_FLAGS(UVERBS_ATTR_SPEC_F_MANDATORY)),
&UVERBS_ATTR_PTR_OUT(DESTROY_CQ_RESP, struct ib_uverbs_destroy_cq_resp,
UA_FLAGS(UVERBS_ATTR_SPEC_F_MANDATORY)));
DECLARE_UVERBS_OBJECT(uverbs_object_comp_channel,
UVERBS_OBJECT_COMP_CHANNEL,
&UVERBS_TYPE_ALLOC_FD(0,
sizeof(struct ib_uverbs_completion_event_file),
uverbs_hot_unplug_completion_event_file,
&uverbs_event_fops,
"[infinibandevent]", O_RDONLY));
DECLARE_UVERBS_OBJECT(uverbs_object_cq, UVERBS_OBJECT_CQ,
&UVERBS_TYPE_ALLOC_IDR_SZ(sizeof(struct ib_ucq_object), 0,
uverbs_free_cq),
&uverbs_method_cq_create,
&uverbs_method_cq_destroy);
DECLARE_UVERBS_OBJECT(uverbs_object_qp, UVERBS_OBJECT_QP,
&UVERBS_TYPE_ALLOC_IDR_SZ(sizeof(struct ib_uqp_object), 0,
uverbs_free_qp));
DECLARE_UVERBS_OBJECT(uverbs_object_mw, UVERBS_OBJECT_MW,
&UVERBS_TYPE_ALLOC_IDR(0, uverbs_free_mw));
DECLARE_UVERBS_OBJECT(uverbs_object_mr, UVERBS_OBJECT_MR,
/* 1 is used in order to free the MR after all the MWs */
&UVERBS_TYPE_ALLOC_IDR(1, uverbs_free_mr));
DECLARE_UVERBS_OBJECT(uverbs_object_srq, UVERBS_OBJECT_SRQ,
&UVERBS_TYPE_ALLOC_IDR_SZ(sizeof(struct ib_usrq_object), 0,
uverbs_free_srq));
DECLARE_UVERBS_OBJECT(uverbs_object_ah, UVERBS_OBJECT_AH,
&UVERBS_TYPE_ALLOC_IDR(0, uverbs_free_ah));
DECLARE_UVERBS_OBJECT(uverbs_object_flow, UVERBS_OBJECT_FLOW,
&UVERBS_TYPE_ALLOC_IDR(0, uverbs_free_flow));
DECLARE_UVERBS_OBJECT(uverbs_object_wq, UVERBS_OBJECT_WQ,
&UVERBS_TYPE_ALLOC_IDR_SZ(sizeof(struct ib_uwq_object), 0,
uverbs_free_wq));
DECLARE_UVERBS_OBJECT(uverbs_object_rwq_ind_table,
UVERBS_OBJECT_RWQ_IND_TBL,
&UVERBS_TYPE_ALLOC_IDR(0, uverbs_free_rwq_ind_tbl));
DECLARE_UVERBS_OBJECT(uverbs_object_xrcd, UVERBS_OBJECT_XRCD,
&UVERBS_TYPE_ALLOC_IDR_SZ(sizeof(struct ib_uxrcd_object), 0,
uverbs_free_xrcd));
DECLARE_UVERBS_OBJECT(uverbs_object_pd, UVERBS_OBJECT_PD,
/* 2 is used in order to free the PD after MRs */
&UVERBS_TYPE_ALLOC_IDR(2, uverbs_free_pd));
DECLARE_UVERBS_OBJECT(uverbs_object_device, UVERBS_OBJECT_DEVICE, NULL);
DECLARE_UVERBS_OBJECT_TREE(uverbs_default_objects,
&uverbs_object_device,
&uverbs_object_pd,
&uverbs_object_mr,
&uverbs_object_comp_channel,
&uverbs_object_cq,
&uverbs_object_qp,
&uverbs_object_ah,
&uverbs_object_mw,
&uverbs_object_srq,
&uverbs_object_flow,
&uverbs_object_wq,
&uverbs_object_rwq_ind_table,
&uverbs_object_xrcd);
...@@ -180,39 +180,29 @@ EXPORT_SYMBOL(ib_rate_to_mbps); ...@@ -180,39 +180,29 @@ EXPORT_SYMBOL(ib_rate_to_mbps);
__attribute_const__ enum rdma_transport_type __attribute_const__ enum rdma_transport_type
rdma_node_get_transport(enum rdma_node_type node_type) rdma_node_get_transport(enum rdma_node_type node_type)
{ {
switch (node_type) {
case RDMA_NODE_IB_CA: if (node_type == RDMA_NODE_USNIC)
case RDMA_NODE_IB_SWITCH:
case RDMA_NODE_IB_ROUTER:
return RDMA_TRANSPORT_IB;
case RDMA_NODE_RNIC:
return RDMA_TRANSPORT_IWARP;
case RDMA_NODE_USNIC:
return RDMA_TRANSPORT_USNIC; return RDMA_TRANSPORT_USNIC;
case RDMA_NODE_USNIC_UDP: if (node_type == RDMA_NODE_USNIC_UDP)
return RDMA_TRANSPORT_USNIC_UDP; return RDMA_TRANSPORT_USNIC_UDP;
default: if (node_type == RDMA_NODE_RNIC)
BUG(); return RDMA_TRANSPORT_IWARP;
return 0;
} return RDMA_TRANSPORT_IB;
} }
EXPORT_SYMBOL(rdma_node_get_transport); EXPORT_SYMBOL(rdma_node_get_transport);
enum rdma_link_layer rdma_port_get_link_layer(struct ib_device *device, u8 port_num) enum rdma_link_layer rdma_port_get_link_layer(struct ib_device *device, u8 port_num)
{ {
enum rdma_transport_type lt;
if (device->get_link_layer) if (device->get_link_layer)
return device->get_link_layer(device, port_num); return device->get_link_layer(device, port_num);
switch (rdma_node_get_transport(device->node_type)) { lt = rdma_node_get_transport(device->node_type);
case RDMA_TRANSPORT_IB: if (lt == RDMA_TRANSPORT_IB)
return IB_LINK_LAYER_INFINIBAND; return IB_LINK_LAYER_INFINIBAND;
case RDMA_TRANSPORT_IWARP:
case RDMA_TRANSPORT_USNIC: return IB_LINK_LAYER_ETHERNET;
case RDMA_TRANSPORT_USNIC_UDP:
return IB_LINK_LAYER_ETHERNET;
default:
return IB_LINK_LAYER_UNSPECIFIED;
}
} }
EXPORT_SYMBOL(rdma_port_get_link_layer); EXPORT_SYMBOL(rdma_port_get_link_layer);
...@@ -478,6 +468,8 @@ int ib_init_ah_from_wc(struct ib_device *device, u8 port_num, ...@@ -478,6 +468,8 @@ int ib_init_ah_from_wc(struct ib_device *device, u8 port_num,
union ib_gid dgid; union ib_gid dgid;
union ib_gid sgid; union ib_gid sgid;
might_sleep();
memset(ah_attr, 0, sizeof *ah_attr); memset(ah_attr, 0, sizeof *ah_attr);
ah_attr->type = rdma_ah_find_type(device, port_num); ah_attr->type = rdma_ah_find_type(device, port_num);
if (rdma_cap_eth_ah(device, port_num)) { if (rdma_cap_eth_ah(device, port_num)) {
...@@ -632,11 +624,13 @@ struct ib_srq *ib_create_srq(struct ib_pd *pd, ...@@ -632,11 +624,13 @@ struct ib_srq *ib_create_srq(struct ib_pd *pd,
srq->event_handler = srq_init_attr->event_handler; srq->event_handler = srq_init_attr->event_handler;
srq->srq_context = srq_init_attr->srq_context; srq->srq_context = srq_init_attr->srq_context;
srq->srq_type = srq_init_attr->srq_type; srq->srq_type = srq_init_attr->srq_type;
if (ib_srq_has_cq(srq->srq_type)) {
srq->ext.cq = srq_init_attr->ext.cq;
atomic_inc(&srq->ext.cq->usecnt);
}
if (srq->srq_type == IB_SRQT_XRC) { if (srq->srq_type == IB_SRQT_XRC) {
srq->ext.xrc.xrcd = srq_init_attr->ext.xrc.xrcd; srq->ext.xrc.xrcd = srq_init_attr->ext.xrc.xrcd;
srq->ext.xrc.cq = srq_init_attr->ext.xrc.cq;
atomic_inc(&srq->ext.xrc.xrcd->usecnt); atomic_inc(&srq->ext.xrc.xrcd->usecnt);
atomic_inc(&srq->ext.xrc.cq->usecnt);
} }
atomic_inc(&pd->usecnt); atomic_inc(&pd->usecnt);
atomic_set(&srq->usecnt, 0); atomic_set(&srq->usecnt, 0);
...@@ -677,18 +671,18 @@ int ib_destroy_srq(struct ib_srq *srq) ...@@ -677,18 +671,18 @@ int ib_destroy_srq(struct ib_srq *srq)
pd = srq->pd; pd = srq->pd;
srq_type = srq->srq_type; srq_type = srq->srq_type;
if (srq_type == IB_SRQT_XRC) { if (ib_srq_has_cq(srq_type))
cq = srq->ext.cq;
if (srq_type == IB_SRQT_XRC)
xrcd = srq->ext.xrc.xrcd; xrcd = srq->ext.xrc.xrcd;
cq = srq->ext.xrc.cq;
}
ret = srq->device->destroy_srq(srq); ret = srq->device->destroy_srq(srq);
if (!ret) { if (!ret) {
atomic_dec(&pd->usecnt); atomic_dec(&pd->usecnt);
if (srq_type == IB_SRQT_XRC) { if (srq_type == IB_SRQT_XRC)
atomic_dec(&xrcd->usecnt); atomic_dec(&xrcd->usecnt);
if (ib_srq_has_cq(srq_type))
atomic_dec(&cq->usecnt); atomic_dec(&cq->usecnt);
}
} }
return ret; return ret;
...@@ -1244,6 +1238,18 @@ int ib_resolve_eth_dmac(struct ib_device *device, ...@@ -1244,6 +1238,18 @@ int ib_resolve_eth_dmac(struct ib_device *device,
if (rdma_link_local_addr((struct in6_addr *)grh->dgid.raw)) { if (rdma_link_local_addr((struct in6_addr *)grh->dgid.raw)) {
rdma_get_ll_mac((struct in6_addr *)grh->dgid.raw, rdma_get_ll_mac((struct in6_addr *)grh->dgid.raw,
ah_attr->roce.dmac); ah_attr->roce.dmac);
return 0;
}
if (rdma_is_multicast_addr((struct in6_addr *)ah_attr->grh.dgid.raw)) {
if (ipv6_addr_v4mapped((struct in6_addr *)ah_attr->grh.dgid.raw)) {
__be32 addr = 0;
memcpy(&addr, ah_attr->grh.dgid.raw + 12, 4);
ip_eth_mc_map(addr, (char *)ah_attr->roce.dmac);
} else {
ipv6_eth_mc_map((struct in6_addr *)ah_attr->grh.dgid.raw,
(char *)ah_attr->roce.dmac);
}
} else { } else {
union ib_gid sgid; union ib_gid sgid;
struct ib_gid_attr sgid_attr; struct ib_gid_attr sgid_attr;
...@@ -1306,6 +1312,61 @@ int ib_modify_qp_with_udata(struct ib_qp *qp, struct ib_qp_attr *attr, ...@@ -1306,6 +1312,61 @@ int ib_modify_qp_with_udata(struct ib_qp *qp, struct ib_qp_attr *attr,
} }
EXPORT_SYMBOL(ib_modify_qp_with_udata); EXPORT_SYMBOL(ib_modify_qp_with_udata);
int ib_get_eth_speed(struct ib_device *dev, u8 port_num, u8 *speed, u8 *width)
{
int rc;
u32 netdev_speed;
struct net_device *netdev;
struct ethtool_link_ksettings lksettings;
if (rdma_port_get_link_layer(dev, port_num) != IB_LINK_LAYER_ETHERNET)
return -EINVAL;
if (!dev->get_netdev)
return -EOPNOTSUPP;
netdev = dev->get_netdev(dev, port_num);
if (!netdev)
return -ENODEV;
rtnl_lock();
rc = __ethtool_get_link_ksettings(netdev, &lksettings);
rtnl_unlock();
dev_put(netdev);
if (!rc) {
netdev_speed = lksettings.base.speed;
} else {
netdev_speed = SPEED_1000;
pr_warn("%s speed is unknown, defaulting to %d\n", netdev->name,
netdev_speed);
}
if (netdev_speed <= SPEED_1000) {
*width = IB_WIDTH_1X;
*speed = IB_SPEED_SDR;
} else if (netdev_speed <= SPEED_10000) {
*width = IB_WIDTH_1X;
*speed = IB_SPEED_FDR10;
} else if (netdev_speed <= SPEED_20000) {
*width = IB_WIDTH_4X;
*speed = IB_SPEED_DDR;
} else if (netdev_speed <= SPEED_25000) {
*width = IB_WIDTH_1X;
*speed = IB_SPEED_EDR;
} else if (netdev_speed <= SPEED_40000) {
*width = IB_WIDTH_4X;
*speed = IB_SPEED_FDR10;
} else {
*width = IB_WIDTH_4X;
*speed = IB_SPEED_EDR;
}
return 0;
}
EXPORT_SYMBOL(ib_get_eth_speed);
int ib_modify_qp(struct ib_qp *qp, int ib_modify_qp(struct ib_qp *qp,
struct ib_qp_attr *qp_attr, struct ib_qp_attr *qp_attr,
int qp_attr_mask) int qp_attr_mask)
...@@ -1573,15 +1634,53 @@ EXPORT_SYMBOL(ib_dealloc_fmr); ...@@ -1573,15 +1634,53 @@ EXPORT_SYMBOL(ib_dealloc_fmr);
/* Multicast groups */ /* Multicast groups */
static bool is_valid_mcast_lid(struct ib_qp *qp, u16 lid)
{
struct ib_qp_init_attr init_attr = {};
struct ib_qp_attr attr = {};
int num_eth_ports = 0;
int port;
/* If QP state >= init, it is assigned to a port and we can check this
* port only.
*/
if (!ib_query_qp(qp, &attr, IB_QP_STATE | IB_QP_PORT, &init_attr)) {
if (attr.qp_state >= IB_QPS_INIT) {
if (qp->device->get_link_layer(qp->device, attr.port_num) !=
IB_LINK_LAYER_INFINIBAND)
return true;
goto lid_check;
}
}
/* Can't get a quick answer, iterate over all ports */
for (port = 0; port < qp->device->phys_port_cnt; port++)
if (qp->device->get_link_layer(qp->device, port) !=
IB_LINK_LAYER_INFINIBAND)
num_eth_ports++;
/* If we have at lease one Ethernet port, RoCE annex declares that
* multicast LID should be ignored. We can't tell at this step if the
* QP belongs to an IB or Ethernet port.
*/
if (num_eth_ports)
return true;
/* If all the ports are IB, we can check according to IB spec. */
lid_check:
return !(lid < be16_to_cpu(IB_MULTICAST_LID_BASE) ||
lid == be16_to_cpu(IB_LID_PERMISSIVE));
}
int ib_attach_mcast(struct ib_qp *qp, union ib_gid *gid, u16 lid) int ib_attach_mcast(struct ib_qp *qp, union ib_gid *gid, u16 lid)
{ {
int ret; int ret;
if (!qp->device->attach_mcast) if (!qp->device->attach_mcast)
return -ENOSYS; return -ENOSYS;
if (gid->raw[0] != 0xff || qp->qp_type != IB_QPT_UD ||
lid < be16_to_cpu(IB_MULTICAST_LID_BASE) || if (!rdma_is_multicast_addr((struct in6_addr *)gid->raw) ||
lid == be16_to_cpu(IB_LID_PERMISSIVE)) qp->qp_type != IB_QPT_UD || !is_valid_mcast_lid(qp, lid))
return -EINVAL; return -EINVAL;
ret = qp->device->attach_mcast(qp, gid, lid); ret = qp->device->attach_mcast(qp, gid, lid);
...@@ -1597,9 +1696,9 @@ int ib_detach_mcast(struct ib_qp *qp, union ib_gid *gid, u16 lid) ...@@ -1597,9 +1696,9 @@ int ib_detach_mcast(struct ib_qp *qp, union ib_gid *gid, u16 lid)
if (!qp->device->detach_mcast) if (!qp->device->detach_mcast)
return -ENOSYS; return -ENOSYS;
if (gid->raw[0] != 0xff || qp->qp_type != IB_QPT_UD ||
lid < be16_to_cpu(IB_MULTICAST_LID_BASE) || if (!rdma_is_multicast_addr((struct in6_addr *)gid->raw) ||
lid == be16_to_cpu(IB_LID_PERMISSIVE)) qp->qp_type != IB_QPT_UD || !is_valid_mcast_lid(qp, lid))
return -EINVAL; return -EINVAL;
ret = qp->device->detach_mcast(qp, gid, lid); ret = qp->device->detach_mcast(qp, gid, lid);
......
...@@ -3,4 +3,4 @@ ccflags-y := -Idrivers/net/ethernet/broadcom/bnxt ...@@ -3,4 +3,4 @@ ccflags-y := -Idrivers/net/ethernet/broadcom/bnxt
obj-$(CONFIG_INFINIBAND_BNXT_RE) += bnxt_re.o obj-$(CONFIG_INFINIBAND_BNXT_RE) += bnxt_re.o
bnxt_re-y := main.o ib_verbs.o \ bnxt_re-y := main.o ib_verbs.o \
qplib_res.o qplib_rcfw.o \ qplib_res.o qplib_rcfw.o \
qplib_sp.o qplib_fp.o qplib_sp.o qplib_fp.o hw_counters.o
...@@ -85,7 +85,7 @@ struct bnxt_re_sqp_entries { ...@@ -85,7 +85,7 @@ struct bnxt_re_sqp_entries {
}; };
#define BNXT_RE_MIN_MSIX 2 #define BNXT_RE_MIN_MSIX 2
#define BNXT_RE_MAX_MSIX 16 #define BNXT_RE_MAX_MSIX 9
#define BNXT_RE_AEQ_IDX 0 #define BNXT_RE_AEQ_IDX 0
#define BNXT_RE_NQ_IDX 1 #define BNXT_RE_NQ_IDX 1
...@@ -116,7 +116,7 @@ struct bnxt_re_dev { ...@@ -116,7 +116,7 @@ struct bnxt_re_dev {
struct bnxt_qplib_rcfw rcfw; struct bnxt_qplib_rcfw rcfw;
/* NQ */ /* NQ */
struct bnxt_qplib_nq nq; struct bnxt_qplib_nq nq[BNXT_RE_MAX_MSIX];
/* Device Resources */ /* Device Resources */
struct bnxt_qplib_dev_attr dev_attr; struct bnxt_qplib_dev_attr dev_attr;
...@@ -140,6 +140,7 @@ struct bnxt_re_dev { ...@@ -140,6 +140,7 @@ struct bnxt_re_dev {
struct bnxt_re_qp *qp1_sqp; struct bnxt_re_qp *qp1_sqp;
struct bnxt_re_ah *sqp_ah; struct bnxt_re_ah *sqp_ah;
struct bnxt_re_sqp_entries sqp_tbl[1024]; struct bnxt_re_sqp_entries sqp_tbl[1024];
atomic_t nq_alloc_cnt;
}; };
#define to_bnxt_re_dev(ptr, member) \ #define to_bnxt_re_dev(ptr, member) \
......
/*
* Broadcom NetXtreme-E RoCE driver.
*
* Copyright (c) 2016 - 2017, Broadcom. All rights reserved. The term
* Broadcom refers to Broadcom Limited and/or its subsidiaries.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* BSD license below:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Description: Statistics
*
*/
#include <linux/interrupt.h>
#include <linux/types.h>
#include <linux/spinlock.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/prefetch.h>
#include <linux/delay.h>
#include <rdma/ib_addr.h>
#include "bnxt_ulp.h"
#include "roce_hsi.h"
#include "qplib_res.h"
#include "qplib_sp.h"
#include "qplib_fp.h"
#include "qplib_rcfw.h"
#include "bnxt_re.h"
#include "hw_counters.h"
static const char * const bnxt_re_stat_name[] = {
[BNXT_RE_ACTIVE_QP] = "active_qps",
[BNXT_RE_ACTIVE_SRQ] = "active_srqs",
[BNXT_RE_ACTIVE_CQ] = "active_cqs",
[BNXT_RE_ACTIVE_MR] = "active_mrs",
[BNXT_RE_ACTIVE_MW] = "active_mws",
[BNXT_RE_RX_PKTS] = "rx_pkts",
[BNXT_RE_RX_BYTES] = "rx_bytes",
[BNXT_RE_TX_PKTS] = "tx_pkts",
[BNXT_RE_TX_BYTES] = "tx_bytes",
[BNXT_RE_RECOVERABLE_ERRORS] = "recoverable_errors"
};
int bnxt_re_ib_get_hw_stats(struct ib_device *ibdev,
struct rdma_hw_stats *stats,
u8 port, int index)
{
struct bnxt_re_dev *rdev = to_bnxt_re_dev(ibdev, ibdev);
struct ctx_hw_stats *bnxt_re_stats = rdev->qplib_ctx.stats.dma;
if (!port || !stats)
return -EINVAL;
stats->value[BNXT_RE_ACTIVE_QP] = atomic_read(&rdev->qp_count);
stats->value[BNXT_RE_ACTIVE_SRQ] = atomic_read(&rdev->srq_count);
stats->value[BNXT_RE_ACTIVE_CQ] = atomic_read(&rdev->cq_count);
stats->value[BNXT_RE_ACTIVE_MR] = atomic_read(&rdev->mr_count);
stats->value[BNXT_RE_ACTIVE_MW] = atomic_read(&rdev->mw_count);
if (bnxt_re_stats) {
stats->value[BNXT_RE_RECOVERABLE_ERRORS] =
le64_to_cpu(bnxt_re_stats->tx_bcast_pkts);
stats->value[BNXT_RE_RX_PKTS] =
le64_to_cpu(bnxt_re_stats->rx_ucast_pkts);
stats->value[BNXT_RE_RX_BYTES] =
le64_to_cpu(bnxt_re_stats->rx_ucast_bytes);
stats->value[BNXT_RE_TX_PKTS] =
le64_to_cpu(bnxt_re_stats->tx_ucast_pkts);
stats->value[BNXT_RE_TX_BYTES] =
le64_to_cpu(bnxt_re_stats->tx_ucast_bytes);
}
return ARRAY_SIZE(bnxt_re_stat_name);
}
struct rdma_hw_stats *bnxt_re_ib_alloc_hw_stats(struct ib_device *ibdev,
u8 port_num)
{
BUILD_BUG_ON(ARRAY_SIZE(bnxt_re_stat_name) != BNXT_RE_NUM_COUNTERS);
/* We support only per port stats */
if (!port_num)
return NULL;
return rdma_alloc_hw_stats_struct(bnxt_re_stat_name,
ARRAY_SIZE(bnxt_re_stat_name),
RDMA_HW_STATS_DEFAULT_LIFESPAN);
}
/*
* Broadcom NetXtreme-E RoCE driver.
*
* Copyright (c) 2016 - 2017, Broadcom. All rights reserved. The term
* Broadcom refers to Broadcom Limited and/or its subsidiaries.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* BSD license below:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Description: Statistics (header)
*
*/
#ifndef __BNXT_RE_HW_STATS_H__
#define __BNXT_RE_HW_STATS_H__
enum bnxt_re_hw_stats {
BNXT_RE_ACTIVE_QP,
BNXT_RE_ACTIVE_SRQ,
BNXT_RE_ACTIVE_CQ,
BNXT_RE_ACTIVE_MR,
BNXT_RE_ACTIVE_MW,
BNXT_RE_RX_PKTS,
BNXT_RE_RX_BYTES,
BNXT_RE_TX_PKTS,
BNXT_RE_TX_BYTES,
BNXT_RE_RECOVERABLE_ERRORS,
BNXT_RE_NUM_COUNTERS
};
struct rdma_hw_stats *bnxt_re_ib_alloc_hw_stats(struct ib_device *ibdev,
u8 port_num);
int bnxt_re_ib_get_hw_stats(struct ib_device *ibdev,
struct rdma_hw_stats *stats,
u8 port, int index);
#endif /* __BNXT_RE_HW_STATS_H__ */
...@@ -141,9 +141,6 @@ int bnxt_re_modify_device(struct ib_device *ibdev, ...@@ -141,9 +141,6 @@ int bnxt_re_modify_device(struct ib_device *ibdev,
struct ib_device_modify *device_modify); struct ib_device_modify *device_modify);
int bnxt_re_query_port(struct ib_device *ibdev, u8 port_num, int bnxt_re_query_port(struct ib_device *ibdev, u8 port_num,
struct ib_port_attr *port_attr); struct ib_port_attr *port_attr);
int bnxt_re_modify_port(struct ib_device *ibdev, u8 port_num,
int port_modify_mask,
struct ib_port_modify *port_modify);
int bnxt_re_get_port_immutable(struct ib_device *ibdev, u8 port_num, int bnxt_re_get_port_immutable(struct ib_device *ibdev, u8 port_num,
struct ib_port_immutable *immutable); struct ib_port_immutable *immutable);
int bnxt_re_query_pkey(struct ib_device *ibdev, u8 port_num, int bnxt_re_query_pkey(struct ib_device *ibdev, u8 port_num,
......
...@@ -220,19 +220,20 @@ struct bnxt_qplib_q { ...@@ -220,19 +220,20 @@ struct bnxt_qplib_q {
u16 q_full_delta; u16 q_full_delta;
u16 max_sge; u16 max_sge;
u32 psn; u32 psn;
bool flush_in_progress;
bool condition; bool condition;
bool single; bool single;
bool send_phantom; bool send_phantom;
u32 phantom_wqe_cnt; u32 phantom_wqe_cnt;
u32 phantom_cqe_cnt; u32 phantom_cqe_cnt;
u32 next_cq_cons; u32 next_cq_cons;
bool flushed;
}; };
struct bnxt_qplib_qp { struct bnxt_qplib_qp {
struct bnxt_qplib_pd *pd; struct bnxt_qplib_pd *pd;
struct bnxt_qplib_dpi *dpi; struct bnxt_qplib_dpi *dpi;
u64 qp_handle; u64 qp_handle;
#define BNXT_QPLIB_QP_ID_INVALID 0xFFFFFFFF
u32 id; u32 id;
u8 type; u8 type;
u8 sig_type; u8 sig_type;
...@@ -296,6 +297,8 @@ struct bnxt_qplib_qp { ...@@ -296,6 +297,8 @@ struct bnxt_qplib_qp {
dma_addr_t sq_hdr_buf_map; dma_addr_t sq_hdr_buf_map;
void *rq_hdr_buf; void *rq_hdr_buf;
dma_addr_t rq_hdr_buf_map; dma_addr_t rq_hdr_buf_map;
struct list_head sq_flush;
struct list_head rq_flush;
}; };
#define BNXT_QPLIB_MAX_CQE_ENTRY_SIZE sizeof(struct cq_base) #define BNXT_QPLIB_MAX_CQE_ENTRY_SIZE sizeof(struct cq_base)
...@@ -351,6 +354,7 @@ struct bnxt_qplib_cq { ...@@ -351,6 +354,7 @@ struct bnxt_qplib_cq {
u16 period; u16 period;
struct bnxt_qplib_hwq hwq; struct bnxt_qplib_hwq hwq;
u32 cnq_hw_ring_id; u32 cnq_hw_ring_id;
struct bnxt_qplib_nq *nq;
bool resize_in_progress; bool resize_in_progress;
struct scatterlist *sghead; struct scatterlist *sghead;
u32 nmap; u32 nmap;
...@@ -360,6 +364,9 @@ struct bnxt_qplib_cq { ...@@ -360,6 +364,9 @@ struct bnxt_qplib_cq {
unsigned long flags; unsigned long flags;
#define CQ_FLAGS_RESIZE_IN_PROG 1 #define CQ_FLAGS_RESIZE_IN_PROG 1
wait_queue_head_t waitq; wait_queue_head_t waitq;
struct list_head sqf_head, rqf_head;
atomic_t arm_state;
spinlock_t compl_lock; /* synch CQ handlers */
}; };
#define BNXT_QPLIB_MAX_IRRQE_ENTRY_SIZE sizeof(struct xrrq_irrq) #define BNXT_QPLIB_MAX_IRRQE_ENTRY_SIZE sizeof(struct xrrq_irrq)
...@@ -400,6 +407,7 @@ struct bnxt_qplib_nq { ...@@ -400,6 +407,7 @@ struct bnxt_qplib_nq {
struct pci_dev *pdev; struct pci_dev *pdev;
int vector; int vector;
cpumask_t mask;
int budget; int budget;
bool requested; bool requested;
struct tasklet_struct worker; struct tasklet_struct worker;
...@@ -417,11 +425,19 @@ struct bnxt_qplib_nq { ...@@ -417,11 +425,19 @@ struct bnxt_qplib_nq {
(struct bnxt_qplib_nq *nq, (struct bnxt_qplib_nq *nq,
void *srq, void *srq,
u8 event); u8 event);
struct workqueue_struct *cqn_wq;
char name[32];
};
struct bnxt_qplib_nq_work {
struct work_struct work;
struct bnxt_qplib_nq *nq;
struct bnxt_qplib_cq *cq;
}; };
void bnxt_qplib_disable_nq(struct bnxt_qplib_nq *nq); void bnxt_qplib_disable_nq(struct bnxt_qplib_nq *nq);
int bnxt_qplib_enable_nq(struct pci_dev *pdev, struct bnxt_qplib_nq *nq, int bnxt_qplib_enable_nq(struct pci_dev *pdev, struct bnxt_qplib_nq *nq,
int msix_vector, int bar_reg_offset, int nq_idx, int msix_vector, int bar_reg_offset,
int (*cqn_handler)(struct bnxt_qplib_nq *nq, int (*cqn_handler)(struct bnxt_qplib_nq *nq,
struct bnxt_qplib_cq *cq), struct bnxt_qplib_cq *cq),
int (*srqn_handler)(struct bnxt_qplib_nq *nq, int (*srqn_handler)(struct bnxt_qplib_nq *nq,
...@@ -453,4 +469,13 @@ bool bnxt_qplib_is_cq_empty(struct bnxt_qplib_cq *cq); ...@@ -453,4 +469,13 @@ bool bnxt_qplib_is_cq_empty(struct bnxt_qplib_cq *cq);
void bnxt_qplib_req_notify_cq(struct bnxt_qplib_cq *cq, u32 arm_type); void bnxt_qplib_req_notify_cq(struct bnxt_qplib_cq *cq, u32 arm_type);
void bnxt_qplib_free_nq(struct bnxt_qplib_nq *nq); void bnxt_qplib_free_nq(struct bnxt_qplib_nq *nq);
int bnxt_qplib_alloc_nq(struct pci_dev *pdev, struct bnxt_qplib_nq *nq); int bnxt_qplib_alloc_nq(struct pci_dev *pdev, struct bnxt_qplib_nq *nq);
void bnxt_qplib_add_flush_qp(struct bnxt_qplib_qp *qp);
void bnxt_qplib_del_flush_qp(struct bnxt_qplib_qp *qp);
void bnxt_qplib_acquire_cq_locks(struct bnxt_qplib_qp *qp,
unsigned long *flags);
void bnxt_qplib_release_cq_locks(struct bnxt_qplib_qp *qp,
unsigned long *flags);
int bnxt_qplib_process_flush_list(struct bnxt_qplib_cq *cq,
struct bnxt_qplib_cqe *cqe,
int num_cqes);
#endif /* __BNXT_QPLIB_FP_H__ */ #endif /* __BNXT_QPLIB_FP_H__ */
...@@ -116,6 +116,7 @@ struct bnxt_qplib_sgid_tbl { ...@@ -116,6 +116,7 @@ struct bnxt_qplib_sgid_tbl {
u16 max; u16 max;
u16 active; u16 active;
void *ctx; void *ctx;
u8 *vlan;
}; };
struct bnxt_qplib_pkey_tbl { struct bnxt_qplib_pkey_tbl {
...@@ -188,6 +189,7 @@ struct bnxt_qplib_res { ...@@ -188,6 +189,7 @@ struct bnxt_qplib_res {
struct bnxt_qplib_sgid_tbl sgid_tbl; struct bnxt_qplib_sgid_tbl sgid_tbl;
struct bnxt_qplib_pkey_tbl pkey_tbl; struct bnxt_qplib_pkey_tbl pkey_tbl;
struct bnxt_qplib_dpi_tbl dpi_tbl; struct bnxt_qplib_dpi_tbl dpi_tbl;
bool prio;
}; };
#define to_bnxt_qplib(ptr, type, member) \ #define to_bnxt_qplib(ptr, type, member) \
......
...@@ -135,6 +135,8 @@ int bnxt_qplib_del_sgid(struct bnxt_qplib_sgid_tbl *sgid_tbl, ...@@ -135,6 +135,8 @@ int bnxt_qplib_del_sgid(struct bnxt_qplib_sgid_tbl *sgid_tbl,
int bnxt_qplib_add_sgid(struct bnxt_qplib_sgid_tbl *sgid_tbl, int bnxt_qplib_add_sgid(struct bnxt_qplib_sgid_tbl *sgid_tbl,
struct bnxt_qplib_gid *gid, u8 *mac, u16 vlan_id, struct bnxt_qplib_gid *gid, u8 *mac, u16 vlan_id,
bool update, u32 *index); bool update, u32 *index);
int bnxt_qplib_update_sgid(struct bnxt_qplib_sgid_tbl *sgid_tbl,
struct bnxt_qplib_gid *gid, u16 gid_idx, u8 *smac);
int bnxt_qplib_get_pkey(struct bnxt_qplib_res *res, int bnxt_qplib_get_pkey(struct bnxt_qplib_res *res,
struct bnxt_qplib_pkey_tbl *pkey_tbl, u16 index, struct bnxt_qplib_pkey_tbl *pkey_tbl, u16 index,
u16 *pkey); u16 *pkey);
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册