2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2004 Topspin Communications. All rights reserved.
|
2007-07-10 07:17:32 +08:00
|
|
|
* Copyright (c) 2005 Voltaire, Inc. All rights reserved.
|
2006-08-22 07:40:12 +08:00
|
|
|
* Copyright (c) 2006 Intel Corporation. All rights reserved.
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
|
|
|
* 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 <linux/init.h>
|
|
|
|
#include <linux/err.h>
|
|
|
|
#include <linux/random.h>
|
|
|
|
#include <linux/spinlock.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/dma-mapping.h>
|
|
|
|
#include <linux/kref.h>
|
2019-02-21 08:20:46 +08:00
|
|
|
#include <linux/xarray.h>
|
2005-10-31 07:03:48 +08:00
|
|
|
#include <linux/workqueue.h>
|
IB/core: Ethernet L2 attributes in verbs/cm structures
This patch add the support for Ethernet L2 attributes in the
verbs/cm/cma structures.
When dealing with L2 Ethernet, we should use smac, dmac, vlan ID and priority
in a similar manner that the IB L2 (and the L4 PKEY) attributes are used.
Thus, those attributes were added to the following structures:
* ib_ah_attr - added dmac
* ib_qp_attr - added smac and vlan_id, (sl remains vlan priority)
* ib_wc - added smac, vlan_id
* ib_sa_path_rec - added smac, dmac, vlan_id
* cm_av - added smac and vlan_id
For the path record structure, extra care was taken to avoid the new
fields when packing it into wire format, so we don't break the IB CM
and SA wire protocol.
On the active side, the CM fills. its internal structures from the
path provided by the ULP. We add there taking the ETH L2 attributes
and placing them into the CM Address Handle (struct cm_av).
On the passive side, the CM fills its internal structures from the WC
associated with the REQ message. We add there taking the ETH L2
attributes from the WC.
When the HW driver provides the required ETH L2 attributes in the WC,
they set the IB_WC_WITH_SMAC and IB_WC_WITH_VLAN flags. The IB core
code checks for the presence of these flags, and in their absence does
address resolution from the ib_init_ah_from_wc() helper function.
ib_modify_qp_is_ok is also updated to consider the link layer. Some
parameters are mandatory for Ethernet link layer, while they are
irrelevant for IB. Vendor drivers are modified to support the new
function signature.
Signed-off-by: Matan Barak <matanb@mellanox.com>
Signed-off-by: Or Gerlitz <ogerlitz@mellanox.com>
Signed-off-by: Roland Dreier <roland@purestorage.com>
2013-12-13 00:03:11 +08:00
|
|
|
#include <uapi/linux/if_ether.h>
|
2005-08-26 04:40:04 +08:00
|
|
|
#include <rdma/ib_pack.h>
|
2006-06-18 11:37:39 +08:00
|
|
|
#include <rdma/ib_cache.h>
|
2015-08-14 20:52:09 +08:00
|
|
|
#include <rdma/rdma_netlink.h>
|
|
|
|
#include <net/netlink.h>
|
|
|
|
#include <uapi/rdma/ib_user_sa.h>
|
|
|
|
#include <rdma/ib_marshall.h>
|
2015-12-23 20:56:53 +08:00
|
|
|
#include <rdma/ib_addr.h>
|
2017-08-05 04:54:16 +08:00
|
|
|
#include <rdma/opa_addr.h>
|
2022-09-08 18:09:01 +08:00
|
|
|
#include <rdma/rdma_cm.h>
|
2007-02-16 09:00:17 +08:00
|
|
|
#include "sa.h"
|
2015-12-23 20:56:53 +08:00
|
|
|
#include "core_priv.h"
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2015-08-14 20:52:09 +08:00
|
|
|
#define IB_SA_LOCAL_SVC_TIMEOUT_MIN 100
|
|
|
|
#define IB_SA_LOCAL_SVC_TIMEOUT_DEFAULT 2000
|
|
|
|
#define IB_SA_LOCAL_SVC_TIMEOUT_MAX 200000
|
2017-03-21 07:38:08 +08:00
|
|
|
#define IB_SA_CPI_MAX_RETRY_CNT 3
|
|
|
|
#define IB_SA_CPI_RETRY_WAIT 1000 /*msecs */
|
2015-08-14 20:52:09 +08:00
|
|
|
static int sa_local_svc_timeout_ms = IB_SA_LOCAL_SVC_TIMEOUT_DEFAULT;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
struct ib_sa_sm_ah {
|
|
|
|
struct ib_ah *ah;
|
|
|
|
struct kref ref;
|
2007-06-19 02:03:58 +08:00
|
|
|
u16 pkey_index;
|
2007-04-06 01:51:10 +08:00
|
|
|
u8 src_path_mask;
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
2017-04-29 07:20:26 +08:00
|
|
|
enum rdma_class_port_info_type {
|
|
|
|
RDMA_CLASS_PORT_INFO_IB,
|
|
|
|
RDMA_CLASS_PORT_INFO_OPA
|
|
|
|
};
|
|
|
|
|
|
|
|
struct rdma_class_port_info {
|
|
|
|
enum rdma_class_port_info_type type;
|
|
|
|
union {
|
|
|
|
struct ib_class_port_info ib;
|
|
|
|
struct opa_class_port_info opa;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2016-07-06 21:36:34 +08:00
|
|
|
struct ib_sa_classport_cache {
|
|
|
|
bool valid;
|
2017-03-21 07:38:08 +08:00
|
|
|
int retry_cnt;
|
2017-04-29 07:20:26 +08:00
|
|
|
struct rdma_class_port_info data;
|
2016-07-06 21:36:34 +08:00
|
|
|
};
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
struct ib_sa_port {
|
|
|
|
struct ib_mad_agent *agent;
|
|
|
|
struct ib_sa_sm_ah *sm_ah;
|
|
|
|
struct work_struct update_task;
|
2016-07-06 21:36:34 +08:00
|
|
|
struct ib_sa_classport_cache classport_info;
|
2017-03-21 07:38:08 +08:00
|
|
|
struct delayed_work ib_cpi_work;
|
2016-07-06 21:36:34 +08:00
|
|
|
spinlock_t classport_lock; /* protects class port info set */
|
2005-04-17 06:20:36 +08:00
|
|
|
spinlock_t ah_lock;
|
2021-03-01 15:04:20 +08:00
|
|
|
u32 port_num;
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
struct ib_sa_device {
|
|
|
|
int start_port, end_port;
|
|
|
|
struct ib_event_handler event_handler;
|
2020-02-13 09:04:25 +08:00
|
|
|
struct ib_sa_port port[];
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
struct ib_sa_query {
|
2022-09-08 18:09:01 +08:00
|
|
|
void (*callback)(struct ib_sa_query *sa_query, int status,
|
2023-01-04 16:03:41 +08:00
|
|
|
struct ib_sa_mad *mad);
|
2005-04-17 06:20:36 +08:00
|
|
|
void (*release)(struct ib_sa_query *);
|
2006-08-22 07:40:12 +08:00
|
|
|
struct ib_sa_client *client;
|
2005-10-26 01:51:39 +08:00
|
|
|
struct ib_sa_port *port;
|
|
|
|
struct ib_mad_send_buf *mad_buf;
|
|
|
|
struct ib_sa_sm_ah *sm_ah;
|
|
|
|
int id;
|
2015-08-14 20:52:09 +08:00
|
|
|
u32 flags;
|
|
|
|
struct list_head list; /* Local svc request list */
|
|
|
|
u32 seq; /* Local svc request sequence number */
|
|
|
|
unsigned long timeout; /* Local svc timeout */
|
|
|
|
u8 path_use; /* How will the pathrecord be used */
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
2015-08-14 20:52:09 +08:00
|
|
|
#define IB_SA_ENABLE_LOCAL_SERVICE 0x00000001
|
|
|
|
#define IB_SA_CANCEL 0x00000002
|
2017-04-29 07:20:26 +08:00
|
|
|
#define IB_SA_QUERY_OPA 0x00000004
|
2015-08-14 20:52:09 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
struct ib_sa_path_query {
|
2022-09-08 18:09:01 +08:00
|
|
|
void (*callback)(int status, struct sa_path_rec *rec,
|
2023-01-04 16:03:41 +08:00
|
|
|
unsigned int num_paths, void *context);
|
2005-04-17 06:20:36 +08:00
|
|
|
void *context;
|
|
|
|
struct ib_sa_query sa_query;
|
2017-04-28 07:06:02 +08:00
|
|
|
struct sa_path_rec *conv_pr;
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
2012-06-19 16:21:38 +08:00
|
|
|
struct ib_sa_guidinfo_query {
|
|
|
|
void (*callback)(int, struct ib_sa_guidinfo_rec *, void *);
|
|
|
|
void *context;
|
|
|
|
struct ib_sa_query sa_query;
|
|
|
|
};
|
|
|
|
|
2016-05-26 03:02:05 +08:00
|
|
|
struct ib_sa_classport_info_query {
|
2017-03-21 07:38:08 +08:00
|
|
|
void (*callback)(void *);
|
2016-05-26 03:02:05 +08:00
|
|
|
void *context;
|
|
|
|
struct ib_sa_query sa_query;
|
|
|
|
};
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
struct ib_sa_mcmember_query {
|
|
|
|
void (*callback)(int, struct ib_sa_mcmember_rec *, void *);
|
|
|
|
void *context;
|
|
|
|
struct ib_sa_query sa_query;
|
|
|
|
};
|
|
|
|
|
2015-08-14 20:52:09 +08:00
|
|
|
static LIST_HEAD(ib_nl_request_list);
|
|
|
|
static DEFINE_SPINLOCK(ib_nl_request_lock);
|
|
|
|
static atomic_t ib_nl_sa_request_seq;
|
|
|
|
static struct workqueue_struct *ib_nl_wq;
|
|
|
|
static struct delayed_work ib_nl_timed_work;
|
|
|
|
static const struct nla_policy ib_nl_policy[LS_NLA_TYPE_MAX] = {
|
|
|
|
[LS_NLA_TYPE_PATH_RECORD] = {.type = NLA_BINARY,
|
|
|
|
.len = sizeof(struct ib_path_rec_data)},
|
|
|
|
[LS_NLA_TYPE_TIMEOUT] = {.type = NLA_U32},
|
|
|
|
[LS_NLA_TYPE_SERVICE_ID] = {.type = NLA_U64},
|
|
|
|
[LS_NLA_TYPE_DGID] = {.type = NLA_BINARY,
|
|
|
|
.len = sizeof(struct rdma_nla_ls_gid)},
|
|
|
|
[LS_NLA_TYPE_SGID] = {.type = NLA_BINARY,
|
|
|
|
.len = sizeof(struct rdma_nla_ls_gid)},
|
|
|
|
[LS_NLA_TYPE_TCLASS] = {.type = NLA_U8},
|
|
|
|
[LS_NLA_TYPE_PKEY] = {.type = NLA_U16},
|
|
|
|
[LS_NLA_TYPE_QOS_CLASS] = {.type = NLA_U16},
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2020-04-22 01:24:40 +08:00
|
|
|
static int ib_sa_add_one(struct ib_device *device);
|
2015-07-30 22:50:14 +08:00
|
|
|
static void ib_sa_remove_one(struct ib_device *device, void *client_data);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
static struct ib_client sa_client = {
|
|
|
|
.name = "sa",
|
|
|
|
.add = ib_sa_add_one,
|
|
|
|
.remove = ib_sa_remove_one
|
|
|
|
};
|
|
|
|
|
2019-02-21 08:20:46 +08:00
|
|
|
static DEFINE_XARRAY_FLAGS(queries, XA_FLAGS_ALLOC | XA_FLAGS_LOCK_IRQ);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2009-09-06 11:24:23 +08:00
|
|
|
static DEFINE_SPINLOCK(tid_lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
static u32 tid;
|
|
|
|
|
|
|
|
#define PATH_REC_FIELD(field) \
|
2017-04-28 07:05:58 +08:00
|
|
|
.struct_offset_bytes = offsetof(struct sa_path_rec, field), \
|
2020-05-27 22:41:52 +08:00
|
|
|
.struct_size_bytes = sizeof_field(struct sa_path_rec, field), \
|
2005-04-17 06:20:36 +08:00
|
|
|
.field_name = "sa_path_rec:" #field
|
|
|
|
|
|
|
|
static const struct ib_field path_rec_table[] = {
|
2017-05-22 00:09:54 +08:00
|
|
|
{ PATH_REC_FIELD(service_id),
|
2005-04-17 06:20:36 +08:00
|
|
|
.offset_words = 0,
|
|
|
|
.offset_bits = 0,
|
2007-08-09 06:41:28 +08:00
|
|
|
.size_bits = 64 },
|
2005-04-17 06:20:36 +08:00
|
|
|
{ PATH_REC_FIELD(dgid),
|
|
|
|
.offset_words = 2,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 128 },
|
|
|
|
{ PATH_REC_FIELD(sgid),
|
|
|
|
.offset_words = 6,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 128 },
|
2017-04-28 07:06:00 +08:00
|
|
|
{ PATH_REC_FIELD(ib.dlid),
|
2005-04-17 06:20:36 +08:00
|
|
|
.offset_words = 10,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 16 },
|
2017-04-28 07:06:00 +08:00
|
|
|
{ PATH_REC_FIELD(ib.slid),
|
2005-04-17 06:20:36 +08:00
|
|
|
.offset_words = 10,
|
|
|
|
.offset_bits = 16,
|
|
|
|
.size_bits = 16 },
|
2017-04-28 07:06:00 +08:00
|
|
|
{ PATH_REC_FIELD(ib.raw_traffic),
|
2005-04-17 06:20:36 +08:00
|
|
|
.offset_words = 11,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 1 },
|
|
|
|
{ RESERVED,
|
|
|
|
.offset_words = 11,
|
|
|
|
.offset_bits = 1,
|
|
|
|
.size_bits = 3 },
|
|
|
|
{ PATH_REC_FIELD(flow_label),
|
|
|
|
.offset_words = 11,
|
|
|
|
.offset_bits = 4,
|
|
|
|
.size_bits = 20 },
|
|
|
|
{ PATH_REC_FIELD(hop_limit),
|
|
|
|
.offset_words = 11,
|
|
|
|
.offset_bits = 24,
|
|
|
|
.size_bits = 8 },
|
|
|
|
{ PATH_REC_FIELD(traffic_class),
|
|
|
|
.offset_words = 12,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 8 },
|
|
|
|
{ PATH_REC_FIELD(reversible),
|
|
|
|
.offset_words = 12,
|
|
|
|
.offset_bits = 8,
|
|
|
|
.size_bits = 1 },
|
|
|
|
{ PATH_REC_FIELD(numb_path),
|
|
|
|
.offset_words = 12,
|
|
|
|
.offset_bits = 9,
|
|
|
|
.size_bits = 7 },
|
|
|
|
{ PATH_REC_FIELD(pkey),
|
|
|
|
.offset_words = 12,
|
|
|
|
.offset_bits = 16,
|
|
|
|
.size_bits = 16 },
|
2007-08-09 06:41:28 +08:00
|
|
|
{ PATH_REC_FIELD(qos_class),
|
2005-04-17 06:20:36 +08:00
|
|
|
.offset_words = 13,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 12 },
|
|
|
|
{ PATH_REC_FIELD(sl),
|
|
|
|
.offset_words = 13,
|
|
|
|
.offset_bits = 12,
|
|
|
|
.size_bits = 4 },
|
|
|
|
{ PATH_REC_FIELD(mtu_selector),
|
|
|
|
.offset_words = 13,
|
|
|
|
.offset_bits = 16,
|
|
|
|
.size_bits = 2 },
|
|
|
|
{ PATH_REC_FIELD(mtu),
|
|
|
|
.offset_words = 13,
|
|
|
|
.offset_bits = 18,
|
|
|
|
.size_bits = 6 },
|
|
|
|
{ PATH_REC_FIELD(rate_selector),
|
|
|
|
.offset_words = 13,
|
|
|
|
.offset_bits = 24,
|
|
|
|
.size_bits = 2 },
|
|
|
|
{ PATH_REC_FIELD(rate),
|
|
|
|
.offset_words = 13,
|
|
|
|
.offset_bits = 26,
|
|
|
|
.size_bits = 6 },
|
|
|
|
{ PATH_REC_FIELD(packet_life_time_selector),
|
|
|
|
.offset_words = 14,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 2 },
|
|
|
|
{ PATH_REC_FIELD(packet_life_time),
|
|
|
|
.offset_words = 14,
|
|
|
|
.offset_bits = 2,
|
|
|
|
.size_bits = 6 },
|
|
|
|
{ PATH_REC_FIELD(preference),
|
|
|
|
.offset_words = 14,
|
|
|
|
.offset_bits = 8,
|
|
|
|
.size_bits = 8 },
|
|
|
|
{ RESERVED,
|
|
|
|
.offset_words = 14,
|
|
|
|
.offset_bits = 16,
|
|
|
|
.size_bits = 48 },
|
|
|
|
};
|
|
|
|
|
2017-04-28 07:06:02 +08:00
|
|
|
#define OPA_PATH_REC_FIELD(field) \
|
|
|
|
.struct_offset_bytes = \
|
|
|
|
offsetof(struct sa_path_rec, field), \
|
|
|
|
.struct_size_bytes = \
|
2020-05-27 22:41:52 +08:00
|
|
|
sizeof_field(struct sa_path_rec, field), \
|
2017-04-28 07:06:02 +08:00
|
|
|
.field_name = "sa_path_rec:" #field
|
|
|
|
|
|
|
|
static const struct ib_field opa_path_rec_table[] = {
|
2017-05-22 00:09:54 +08:00
|
|
|
{ OPA_PATH_REC_FIELD(service_id),
|
2017-04-28 07:06:02 +08:00
|
|
|
.offset_words = 0,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 64 },
|
|
|
|
{ OPA_PATH_REC_FIELD(dgid),
|
|
|
|
.offset_words = 2,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 128 },
|
|
|
|
{ OPA_PATH_REC_FIELD(sgid),
|
|
|
|
.offset_words = 6,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 128 },
|
|
|
|
{ OPA_PATH_REC_FIELD(opa.dlid),
|
|
|
|
.offset_words = 10,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 32 },
|
|
|
|
{ OPA_PATH_REC_FIELD(opa.slid),
|
|
|
|
.offset_words = 11,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 32 },
|
|
|
|
{ OPA_PATH_REC_FIELD(opa.raw_traffic),
|
|
|
|
.offset_words = 12,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 1 },
|
|
|
|
{ RESERVED,
|
|
|
|
.offset_words = 12,
|
|
|
|
.offset_bits = 1,
|
|
|
|
.size_bits = 3 },
|
|
|
|
{ OPA_PATH_REC_FIELD(flow_label),
|
|
|
|
.offset_words = 12,
|
|
|
|
.offset_bits = 4,
|
|
|
|
.size_bits = 20 },
|
|
|
|
{ OPA_PATH_REC_FIELD(hop_limit),
|
|
|
|
.offset_words = 12,
|
|
|
|
.offset_bits = 24,
|
|
|
|
.size_bits = 8 },
|
|
|
|
{ OPA_PATH_REC_FIELD(traffic_class),
|
|
|
|
.offset_words = 13,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 8 },
|
|
|
|
{ OPA_PATH_REC_FIELD(reversible),
|
|
|
|
.offset_words = 13,
|
|
|
|
.offset_bits = 8,
|
|
|
|
.size_bits = 1 },
|
|
|
|
{ OPA_PATH_REC_FIELD(numb_path),
|
|
|
|
.offset_words = 13,
|
|
|
|
.offset_bits = 9,
|
|
|
|
.size_bits = 7 },
|
|
|
|
{ OPA_PATH_REC_FIELD(pkey),
|
|
|
|
.offset_words = 13,
|
|
|
|
.offset_bits = 16,
|
|
|
|
.size_bits = 16 },
|
|
|
|
{ OPA_PATH_REC_FIELD(opa.l2_8B),
|
|
|
|
.offset_words = 14,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 1 },
|
|
|
|
{ OPA_PATH_REC_FIELD(opa.l2_10B),
|
|
|
|
.offset_words = 14,
|
|
|
|
.offset_bits = 1,
|
|
|
|
.size_bits = 1 },
|
|
|
|
{ OPA_PATH_REC_FIELD(opa.l2_9B),
|
|
|
|
.offset_words = 14,
|
|
|
|
.offset_bits = 2,
|
|
|
|
.size_bits = 1 },
|
|
|
|
{ OPA_PATH_REC_FIELD(opa.l2_16B),
|
|
|
|
.offset_words = 14,
|
|
|
|
.offset_bits = 3,
|
|
|
|
.size_bits = 1 },
|
|
|
|
{ RESERVED,
|
|
|
|
.offset_words = 14,
|
|
|
|
.offset_bits = 4,
|
|
|
|
.size_bits = 2 },
|
|
|
|
{ OPA_PATH_REC_FIELD(opa.qos_type),
|
|
|
|
.offset_words = 14,
|
|
|
|
.offset_bits = 6,
|
|
|
|
.size_bits = 2 },
|
|
|
|
{ OPA_PATH_REC_FIELD(opa.qos_priority),
|
|
|
|
.offset_words = 14,
|
|
|
|
.offset_bits = 8,
|
|
|
|
.size_bits = 8 },
|
|
|
|
{ RESERVED,
|
|
|
|
.offset_words = 14,
|
|
|
|
.offset_bits = 16,
|
|
|
|
.size_bits = 3 },
|
|
|
|
{ OPA_PATH_REC_FIELD(sl),
|
|
|
|
.offset_words = 14,
|
|
|
|
.offset_bits = 19,
|
|
|
|
.size_bits = 5 },
|
|
|
|
{ RESERVED,
|
|
|
|
.offset_words = 14,
|
|
|
|
.offset_bits = 24,
|
|
|
|
.size_bits = 8 },
|
|
|
|
{ OPA_PATH_REC_FIELD(mtu_selector),
|
|
|
|
.offset_words = 15,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 2 },
|
|
|
|
{ OPA_PATH_REC_FIELD(mtu),
|
|
|
|
.offset_words = 15,
|
|
|
|
.offset_bits = 2,
|
|
|
|
.size_bits = 6 },
|
|
|
|
{ OPA_PATH_REC_FIELD(rate_selector),
|
|
|
|
.offset_words = 15,
|
|
|
|
.offset_bits = 8,
|
|
|
|
.size_bits = 2 },
|
|
|
|
{ OPA_PATH_REC_FIELD(rate),
|
|
|
|
.offset_words = 15,
|
|
|
|
.offset_bits = 10,
|
|
|
|
.size_bits = 6 },
|
|
|
|
{ OPA_PATH_REC_FIELD(packet_life_time_selector),
|
|
|
|
.offset_words = 15,
|
|
|
|
.offset_bits = 16,
|
|
|
|
.size_bits = 2 },
|
|
|
|
{ OPA_PATH_REC_FIELD(packet_life_time),
|
|
|
|
.offset_words = 15,
|
|
|
|
.offset_bits = 18,
|
|
|
|
.size_bits = 6 },
|
|
|
|
{ OPA_PATH_REC_FIELD(preference),
|
|
|
|
.offset_words = 15,
|
|
|
|
.offset_bits = 24,
|
|
|
|
.size_bits = 8 },
|
|
|
|
};
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
#define MCMEMBER_REC_FIELD(field) \
|
|
|
|
.struct_offset_bytes = offsetof(struct ib_sa_mcmember_rec, field), \
|
2020-05-27 22:41:52 +08:00
|
|
|
.struct_size_bytes = sizeof_field(struct ib_sa_mcmember_rec, field), \
|
2005-04-17 06:20:36 +08:00
|
|
|
.field_name = "sa_mcmember_rec:" #field
|
|
|
|
|
|
|
|
static const struct ib_field mcmember_rec_table[] = {
|
|
|
|
{ MCMEMBER_REC_FIELD(mgid),
|
|
|
|
.offset_words = 0,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 128 },
|
|
|
|
{ MCMEMBER_REC_FIELD(port_gid),
|
|
|
|
.offset_words = 4,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 128 },
|
|
|
|
{ MCMEMBER_REC_FIELD(qkey),
|
|
|
|
.offset_words = 8,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 32 },
|
|
|
|
{ MCMEMBER_REC_FIELD(mlid),
|
|
|
|
.offset_words = 9,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 16 },
|
|
|
|
{ MCMEMBER_REC_FIELD(mtu_selector),
|
|
|
|
.offset_words = 9,
|
|
|
|
.offset_bits = 16,
|
|
|
|
.size_bits = 2 },
|
|
|
|
{ MCMEMBER_REC_FIELD(mtu),
|
|
|
|
.offset_words = 9,
|
|
|
|
.offset_bits = 18,
|
|
|
|
.size_bits = 6 },
|
|
|
|
{ MCMEMBER_REC_FIELD(traffic_class),
|
|
|
|
.offset_words = 9,
|
|
|
|
.offset_bits = 24,
|
|
|
|
.size_bits = 8 },
|
|
|
|
{ MCMEMBER_REC_FIELD(pkey),
|
|
|
|
.offset_words = 10,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 16 },
|
|
|
|
{ MCMEMBER_REC_FIELD(rate_selector),
|
|
|
|
.offset_words = 10,
|
|
|
|
.offset_bits = 16,
|
|
|
|
.size_bits = 2 },
|
|
|
|
{ MCMEMBER_REC_FIELD(rate),
|
|
|
|
.offset_words = 10,
|
|
|
|
.offset_bits = 18,
|
|
|
|
.size_bits = 6 },
|
|
|
|
{ MCMEMBER_REC_FIELD(packet_life_time_selector),
|
|
|
|
.offset_words = 10,
|
|
|
|
.offset_bits = 24,
|
|
|
|
.size_bits = 2 },
|
|
|
|
{ MCMEMBER_REC_FIELD(packet_life_time),
|
|
|
|
.offset_words = 10,
|
|
|
|
.offset_bits = 26,
|
|
|
|
.size_bits = 6 },
|
|
|
|
{ MCMEMBER_REC_FIELD(sl),
|
|
|
|
.offset_words = 11,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 4 },
|
|
|
|
{ MCMEMBER_REC_FIELD(flow_label),
|
|
|
|
.offset_words = 11,
|
|
|
|
.offset_bits = 4,
|
|
|
|
.size_bits = 20 },
|
|
|
|
{ MCMEMBER_REC_FIELD(hop_limit),
|
|
|
|
.offset_words = 11,
|
|
|
|
.offset_bits = 24,
|
|
|
|
.size_bits = 8 },
|
|
|
|
{ MCMEMBER_REC_FIELD(scope),
|
|
|
|
.offset_words = 12,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 4 },
|
|
|
|
{ MCMEMBER_REC_FIELD(join_state),
|
|
|
|
.offset_words = 12,
|
|
|
|
.offset_bits = 4,
|
|
|
|
.size_bits = 4 },
|
|
|
|
{ MCMEMBER_REC_FIELD(proxy_join),
|
|
|
|
.offset_words = 12,
|
|
|
|
.offset_bits = 8,
|
|
|
|
.size_bits = 1 },
|
|
|
|
{ RESERVED,
|
|
|
|
.offset_words = 12,
|
|
|
|
.offset_bits = 9,
|
|
|
|
.size_bits = 23 },
|
|
|
|
};
|
|
|
|
|
2016-05-26 03:02:05 +08:00
|
|
|
#define CLASSPORTINFO_REC_FIELD(field) \
|
|
|
|
.struct_offset_bytes = offsetof(struct ib_class_port_info, field), \
|
2020-05-27 22:41:52 +08:00
|
|
|
.struct_size_bytes = sizeof_field(struct ib_class_port_info, field), \
|
2016-05-26 03:02:05 +08:00
|
|
|
.field_name = "ib_class_port_info:" #field
|
|
|
|
|
2017-04-29 07:20:26 +08:00
|
|
|
static const struct ib_field ib_classport_info_rec_table[] = {
|
2016-05-26 03:02:05 +08:00
|
|
|
{ CLASSPORTINFO_REC_FIELD(base_version),
|
|
|
|
.offset_words = 0,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 8 },
|
|
|
|
{ CLASSPORTINFO_REC_FIELD(class_version),
|
|
|
|
.offset_words = 0,
|
|
|
|
.offset_bits = 8,
|
|
|
|
.size_bits = 8 },
|
|
|
|
{ CLASSPORTINFO_REC_FIELD(capability_mask),
|
|
|
|
.offset_words = 0,
|
|
|
|
.offset_bits = 16,
|
|
|
|
.size_bits = 16 },
|
|
|
|
{ CLASSPORTINFO_REC_FIELD(cap_mask2_resp_time),
|
|
|
|
.offset_words = 1,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 32 },
|
|
|
|
{ CLASSPORTINFO_REC_FIELD(redirect_gid),
|
|
|
|
.offset_words = 2,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 128 },
|
|
|
|
{ CLASSPORTINFO_REC_FIELD(redirect_tcslfl),
|
|
|
|
.offset_words = 6,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 32 },
|
|
|
|
{ CLASSPORTINFO_REC_FIELD(redirect_lid),
|
|
|
|
.offset_words = 7,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 16 },
|
|
|
|
{ CLASSPORTINFO_REC_FIELD(redirect_pkey),
|
|
|
|
.offset_words = 7,
|
|
|
|
.offset_bits = 16,
|
|
|
|
.size_bits = 16 },
|
|
|
|
|
|
|
|
{ CLASSPORTINFO_REC_FIELD(redirect_qp),
|
|
|
|
.offset_words = 8,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 32 },
|
|
|
|
{ CLASSPORTINFO_REC_FIELD(redirect_qkey),
|
|
|
|
.offset_words = 9,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 32 },
|
|
|
|
|
|
|
|
{ CLASSPORTINFO_REC_FIELD(trap_gid),
|
|
|
|
.offset_words = 10,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 128 },
|
|
|
|
{ CLASSPORTINFO_REC_FIELD(trap_tcslfl),
|
|
|
|
.offset_words = 14,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 32 },
|
|
|
|
|
|
|
|
{ CLASSPORTINFO_REC_FIELD(trap_lid),
|
|
|
|
.offset_words = 15,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 16 },
|
|
|
|
{ CLASSPORTINFO_REC_FIELD(trap_pkey),
|
|
|
|
.offset_words = 15,
|
|
|
|
.offset_bits = 16,
|
|
|
|
.size_bits = 16 },
|
|
|
|
|
|
|
|
{ CLASSPORTINFO_REC_FIELD(trap_hlqp),
|
|
|
|
.offset_words = 16,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 32 },
|
|
|
|
{ CLASSPORTINFO_REC_FIELD(trap_qkey),
|
|
|
|
.offset_words = 17,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 32 },
|
|
|
|
};
|
|
|
|
|
2017-04-29 07:20:26 +08:00
|
|
|
#define OPA_CLASSPORTINFO_REC_FIELD(field) \
|
|
|
|
.struct_offset_bytes =\
|
|
|
|
offsetof(struct opa_class_port_info, field), \
|
|
|
|
.struct_size_bytes = \
|
2020-05-27 22:41:52 +08:00
|
|
|
sizeof_field(struct opa_class_port_info, field), \
|
2017-04-29 07:20:26 +08:00
|
|
|
.field_name = "opa_class_port_info:" #field
|
|
|
|
|
|
|
|
static const struct ib_field opa_classport_info_rec_table[] = {
|
|
|
|
{ OPA_CLASSPORTINFO_REC_FIELD(base_version),
|
|
|
|
.offset_words = 0,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 8 },
|
|
|
|
{ OPA_CLASSPORTINFO_REC_FIELD(class_version),
|
|
|
|
.offset_words = 0,
|
|
|
|
.offset_bits = 8,
|
|
|
|
.size_bits = 8 },
|
|
|
|
{ OPA_CLASSPORTINFO_REC_FIELD(cap_mask),
|
|
|
|
.offset_words = 0,
|
|
|
|
.offset_bits = 16,
|
|
|
|
.size_bits = 16 },
|
|
|
|
{ OPA_CLASSPORTINFO_REC_FIELD(cap_mask2_resp_time),
|
|
|
|
.offset_words = 1,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 32 },
|
|
|
|
{ OPA_CLASSPORTINFO_REC_FIELD(redirect_gid),
|
|
|
|
.offset_words = 2,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 128 },
|
|
|
|
{ OPA_CLASSPORTINFO_REC_FIELD(redirect_tc_fl),
|
|
|
|
.offset_words = 6,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 32 },
|
|
|
|
{ OPA_CLASSPORTINFO_REC_FIELD(redirect_lid),
|
|
|
|
.offset_words = 7,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 32 },
|
|
|
|
{ OPA_CLASSPORTINFO_REC_FIELD(redirect_sl_qp),
|
|
|
|
.offset_words = 8,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 32 },
|
|
|
|
{ OPA_CLASSPORTINFO_REC_FIELD(redirect_qkey),
|
|
|
|
.offset_words = 9,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 32 },
|
|
|
|
{ OPA_CLASSPORTINFO_REC_FIELD(trap_gid),
|
|
|
|
.offset_words = 10,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 128 },
|
|
|
|
{ OPA_CLASSPORTINFO_REC_FIELD(trap_tc_fl),
|
|
|
|
.offset_words = 14,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 32 },
|
|
|
|
{ OPA_CLASSPORTINFO_REC_FIELD(trap_lid),
|
|
|
|
.offset_words = 15,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 32 },
|
|
|
|
{ OPA_CLASSPORTINFO_REC_FIELD(trap_hl_qp),
|
|
|
|
.offset_words = 16,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 32 },
|
|
|
|
{ OPA_CLASSPORTINFO_REC_FIELD(trap_qkey),
|
|
|
|
.offset_words = 17,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 32 },
|
|
|
|
{ OPA_CLASSPORTINFO_REC_FIELD(trap_pkey),
|
|
|
|
.offset_words = 18,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 16 },
|
|
|
|
{ OPA_CLASSPORTINFO_REC_FIELD(redirect_pkey),
|
|
|
|
.offset_words = 18,
|
|
|
|
.offset_bits = 16,
|
|
|
|
.size_bits = 16 },
|
|
|
|
{ OPA_CLASSPORTINFO_REC_FIELD(trap_sl_rsvd),
|
|
|
|
.offset_words = 19,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 8 },
|
|
|
|
{ RESERVED,
|
|
|
|
.offset_words = 19,
|
|
|
|
.offset_bits = 8,
|
|
|
|
.size_bits = 24 },
|
|
|
|
};
|
|
|
|
|
2012-06-19 16:21:38 +08:00
|
|
|
#define GUIDINFO_REC_FIELD(field) \
|
|
|
|
.struct_offset_bytes = offsetof(struct ib_sa_guidinfo_rec, field), \
|
2020-05-27 22:41:52 +08:00
|
|
|
.struct_size_bytes = sizeof_field(struct ib_sa_guidinfo_rec, field), \
|
2012-06-19 16:21:38 +08:00
|
|
|
.field_name = "sa_guidinfo_rec:" #field
|
|
|
|
|
|
|
|
static const struct ib_field guidinfo_rec_table[] = {
|
|
|
|
{ GUIDINFO_REC_FIELD(lid),
|
|
|
|
.offset_words = 0,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 16 },
|
|
|
|
{ GUIDINFO_REC_FIELD(block_num),
|
|
|
|
.offset_words = 0,
|
|
|
|
.offset_bits = 16,
|
|
|
|
.size_bits = 8 },
|
|
|
|
{ GUIDINFO_REC_FIELD(res1),
|
|
|
|
.offset_words = 0,
|
|
|
|
.offset_bits = 24,
|
|
|
|
.size_bits = 8 },
|
|
|
|
{ GUIDINFO_REC_FIELD(res2),
|
|
|
|
.offset_words = 1,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 32 },
|
|
|
|
{ GUIDINFO_REC_FIELD(guid_info_list),
|
|
|
|
.offset_words = 2,
|
|
|
|
.offset_bits = 0,
|
|
|
|
.size_bits = 512 },
|
|
|
|
};
|
|
|
|
|
2023-01-04 16:03:41 +08:00
|
|
|
#define RDMA_PRIMARY_PATH_MAX_REC_NUM 3
|
|
|
|
|
2015-08-14 20:52:09 +08:00
|
|
|
static inline void ib_sa_disable_local_svc(struct ib_sa_query *query)
|
|
|
|
{
|
|
|
|
query->flags &= ~IB_SA_ENABLE_LOCAL_SERVICE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int ib_sa_query_cancelled(struct ib_sa_query *query)
|
|
|
|
{
|
|
|
|
return (query->flags & IB_SA_CANCEL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ib_nl_set_path_rec_attrs(struct sk_buff *skb,
|
|
|
|
struct ib_sa_query *query)
|
|
|
|
{
|
2017-04-28 07:05:58 +08:00
|
|
|
struct sa_path_rec *sa_rec = query->mad_buf->context[1];
|
2015-08-14 20:52:09 +08:00
|
|
|
struct ib_sa_mad *mad = query->mad_buf->mad;
|
|
|
|
ib_sa_comp_mask comp_mask = mad->sa_hdr.comp_mask;
|
|
|
|
u16 val16;
|
|
|
|
u64 val64;
|
|
|
|
struct rdma_ls_resolve_header *header;
|
|
|
|
|
|
|
|
query->mad_buf->context[1] = NULL;
|
|
|
|
|
|
|
|
/* Construct the family header first */
|
networking: make skb_put & friends return void pointers
It seems like a historic accident that these return unsigned char *,
and in many places that means casts are required, more often than not.
Make these functions (skb_put, __skb_put and pskb_put) return void *
and remove all the casts across the tree, adding a (u8 *) cast only
where the unsigned char pointer was used directly, all done with the
following spatch:
@@
expression SKB, LEN;
typedef u8;
identifier fn = { skb_put, __skb_put };
@@
- *(fn(SKB, LEN))
+ *(u8 *)fn(SKB, LEN)
@@
expression E, SKB, LEN;
identifier fn = { skb_put, __skb_put };
type T;
@@
- E = ((T *)(fn(SKB, LEN)))
+ E = fn(SKB, LEN)
which actually doesn't cover pskb_put since there are only three
users overall.
A handful of stragglers were converted manually, notably a macro in
drivers/isdn/i4l/isdn_bsdcomp.c and, oddly enough, one of the many
instances in net/bluetooth/hci_sock.c. In the former file, I also
had to fix one whitespace problem spatch introduced.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 20:29:21 +08:00
|
|
|
header = skb_put(skb, NLMSG_ALIGN(sizeof(*header)));
|
2021-10-24 14:08:20 +08:00
|
|
|
strscpy_pad(header->device_name,
|
|
|
|
dev_name(&query->port->agent->device->dev),
|
|
|
|
LS_DEVICE_NAME_MAX);
|
2015-08-14 20:52:09 +08:00
|
|
|
header->port_num = query->port->port_num;
|
|
|
|
|
|
|
|
if ((comp_mask & IB_SA_PATH_REC_REVERSIBLE) &&
|
|
|
|
sa_rec->reversible != 0)
|
2022-09-08 18:09:01 +08:00
|
|
|
query->path_use = LS_RESOLVE_PATH_USE_ALL;
|
2015-08-14 20:52:09 +08:00
|
|
|
else
|
|
|
|
query->path_use = LS_RESOLVE_PATH_USE_UNIDIRECTIONAL;
|
|
|
|
header->path_use = query->path_use;
|
|
|
|
|
|
|
|
/* Now build the attributes */
|
|
|
|
if (comp_mask & IB_SA_PATH_REC_SERVICE_ID) {
|
2017-05-22 00:09:54 +08:00
|
|
|
val64 = be64_to_cpu(sa_rec->service_id);
|
2015-08-14 20:52:09 +08:00
|
|
|
nla_put(skb, RDMA_NLA_F_MANDATORY | LS_NLA_TYPE_SERVICE_ID,
|
|
|
|
sizeof(val64), &val64);
|
|
|
|
}
|
|
|
|
if (comp_mask & IB_SA_PATH_REC_DGID)
|
|
|
|
nla_put(skb, RDMA_NLA_F_MANDATORY | LS_NLA_TYPE_DGID,
|
|
|
|
sizeof(sa_rec->dgid), &sa_rec->dgid);
|
|
|
|
if (comp_mask & IB_SA_PATH_REC_SGID)
|
|
|
|
nla_put(skb, RDMA_NLA_F_MANDATORY | LS_NLA_TYPE_SGID,
|
|
|
|
sizeof(sa_rec->sgid), &sa_rec->sgid);
|
|
|
|
if (comp_mask & IB_SA_PATH_REC_TRAFFIC_CLASS)
|
|
|
|
nla_put(skb, RDMA_NLA_F_MANDATORY | LS_NLA_TYPE_TCLASS,
|
|
|
|
sizeof(sa_rec->traffic_class), &sa_rec->traffic_class);
|
|
|
|
|
|
|
|
if (comp_mask & IB_SA_PATH_REC_PKEY) {
|
|
|
|
val16 = be16_to_cpu(sa_rec->pkey);
|
|
|
|
nla_put(skb, RDMA_NLA_F_MANDATORY | LS_NLA_TYPE_PKEY,
|
|
|
|
sizeof(val16), &val16);
|
|
|
|
}
|
|
|
|
if (comp_mask & IB_SA_PATH_REC_QOS_CLASS) {
|
|
|
|
val16 = be16_to_cpu(sa_rec->qos_class);
|
|
|
|
nla_put(skb, RDMA_NLA_F_MANDATORY | LS_NLA_TYPE_QOS_CLASS,
|
|
|
|
sizeof(val16), &val16);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ib_nl_get_path_rec_attrs_len(ib_sa_comp_mask comp_mask)
|
|
|
|
{
|
|
|
|
int len = 0;
|
|
|
|
|
|
|
|
if (comp_mask & IB_SA_PATH_REC_SERVICE_ID)
|
|
|
|
len += nla_total_size(sizeof(u64));
|
|
|
|
if (comp_mask & IB_SA_PATH_REC_DGID)
|
|
|
|
len += nla_total_size(sizeof(struct rdma_nla_ls_gid));
|
|
|
|
if (comp_mask & IB_SA_PATH_REC_SGID)
|
|
|
|
len += nla_total_size(sizeof(struct rdma_nla_ls_gid));
|
|
|
|
if (comp_mask & IB_SA_PATH_REC_TRAFFIC_CLASS)
|
|
|
|
len += nla_total_size(sizeof(u8));
|
|
|
|
if (comp_mask & IB_SA_PATH_REC_PKEY)
|
|
|
|
len += nla_total_size(sizeof(u16));
|
|
|
|
if (comp_mask & IB_SA_PATH_REC_QOS_CLASS)
|
|
|
|
len += nla_total_size(sizeof(u16));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make sure that at least some of the required comp_mask bits are
|
|
|
|
* set.
|
|
|
|
*/
|
|
|
|
if (WARN_ON(len == 0))
|
|
|
|
return len;
|
|
|
|
|
|
|
|
/* Add the family header */
|
|
|
|
len += NLMSG_ALIGN(sizeof(struct rdma_ls_resolve_header));
|
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
2020-06-24 10:13:09 +08:00
|
|
|
static int ib_nl_make_request(struct ib_sa_query *query, gfp_t gfp_mask)
|
2015-08-14 20:52:09 +08:00
|
|
|
{
|
|
|
|
struct sk_buff *skb = NULL;
|
|
|
|
struct nlmsghdr *nlh;
|
|
|
|
void *data;
|
|
|
|
struct ib_sa_mad *mad;
|
|
|
|
int len;
|
2020-06-24 10:13:09 +08:00
|
|
|
unsigned long flags;
|
|
|
|
unsigned long delay;
|
|
|
|
gfp_t gfp_flag;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
INIT_LIST_HEAD(&query->list);
|
|
|
|
query->seq = (u32)atomic_inc_return(&ib_nl_sa_request_seq);
|
2015-08-14 20:52:09 +08:00
|
|
|
|
|
|
|
mad = query->mad_buf->mad;
|
|
|
|
len = ib_nl_get_path_rec_attrs_len(mad->sa_hdr.comp_mask);
|
|
|
|
if (len <= 0)
|
|
|
|
return -EMSGSIZE;
|
|
|
|
|
2015-10-30 20:23:45 +08:00
|
|
|
skb = nlmsg_new(len, gfp_mask);
|
2015-08-14 20:52:09 +08:00
|
|
|
if (!skb)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
/* Put nlmsg header only for now */
|
|
|
|
data = ibnl_put_msg(skb, &nlh, query->seq, 0, RDMA_NL_LS,
|
2015-08-21 02:20:42 +08:00
|
|
|
RDMA_NL_LS_OP_RESOLVE, NLM_F_REQUEST);
|
2015-08-14 20:52:09 +08:00
|
|
|
if (!data) {
|
2016-05-07 03:45:27 +08:00
|
|
|
nlmsg_free(skb);
|
2015-08-14 20:52:09 +08:00
|
|
|
return -EMSGSIZE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add attributes */
|
|
|
|
ib_nl_set_path_rec_attrs(skb, query);
|
|
|
|
|
|
|
|
/* Repair the nlmsg header length */
|
|
|
|
nlmsg_end(skb, nlh);
|
|
|
|
|
2020-06-24 10:13:09 +08:00
|
|
|
gfp_flag = ((gfp_mask & GFP_ATOMIC) == GFP_ATOMIC) ? GFP_ATOMIC :
|
|
|
|
GFP_NOWAIT;
|
2015-08-14 20:52:09 +08:00
|
|
|
|
2020-06-24 10:13:09 +08:00
|
|
|
spin_lock_irqsave(&ib_nl_request_lock, flags);
|
|
|
|
ret = rdma_nl_multicast(&init_net, skb, RDMA_NL_GROUP_LS, gfp_flag);
|
2015-08-14 20:52:09 +08:00
|
|
|
|
2020-06-24 10:13:09 +08:00
|
|
|
if (ret)
|
|
|
|
goto out;
|
2015-08-14 20:52:09 +08:00
|
|
|
|
2020-06-24 10:13:09 +08:00
|
|
|
/* Put the request on the list.*/
|
2015-08-14 20:52:09 +08:00
|
|
|
delay = msecs_to_jiffies(sa_local_svc_timeout_ms);
|
|
|
|
query->timeout = delay + jiffies;
|
|
|
|
list_add_tail(&query->list, &ib_nl_request_list);
|
|
|
|
/* Start the timeout if this is the only request */
|
|
|
|
if (ib_nl_request_list.next == &query->list)
|
|
|
|
queue_delayed_work(ib_nl_wq, &ib_nl_timed_work, delay);
|
|
|
|
|
2020-06-24 10:13:09 +08:00
|
|
|
out:
|
|
|
|
spin_unlock_irqrestore(&ib_nl_request_lock, flags);
|
2015-10-30 20:23:45 +08:00
|
|
|
|
2015-08-14 20:52:09 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ib_nl_cancel_request(struct ib_sa_query *query)
|
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
struct ib_sa_query *wait_query;
|
|
|
|
int found = 0;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&ib_nl_request_lock, flags);
|
|
|
|
list_for_each_entry(wait_query, &ib_nl_request_list, list) {
|
|
|
|
/* Let the timeout to take care of the callback */
|
|
|
|
if (query == wait_query) {
|
|
|
|
query->flags |= IB_SA_CANCEL;
|
|
|
|
query->timeout = jiffies;
|
|
|
|
list_move(&query->list, &ib_nl_request_list);
|
|
|
|
found = 1;
|
|
|
|
mod_delayed_work(ib_nl_wq, &ib_nl_timed_work, 1);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
spin_unlock_irqrestore(&ib_nl_request_lock, flags);
|
|
|
|
|
|
|
|
return found;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void send_handler(struct ib_mad_agent *agent,
|
|
|
|
struct ib_mad_send_wc *mad_send_wc);
|
|
|
|
|
|
|
|
static void ib_nl_process_good_resolve_rsp(struct ib_sa_query *query,
|
|
|
|
const struct nlmsghdr *nlh)
|
|
|
|
{
|
2023-01-04 16:03:41 +08:00
|
|
|
struct sa_path_rec recs[RDMA_PRIMARY_PATH_MAX_REC_NUM];
|
2022-09-08 18:09:01 +08:00
|
|
|
struct ib_sa_path_query *path_query;
|
2023-01-04 16:03:41 +08:00
|
|
|
struct ib_path_rec_data *rec_data;
|
2015-08-14 20:52:09 +08:00
|
|
|
struct ib_mad_send_wc mad_send_wc;
|
|
|
|
const struct nlattr *head, *curr;
|
2022-09-08 18:09:01 +08:00
|
|
|
struct ib_sa_mad *mad = NULL;
|
2023-01-04 16:03:41 +08:00
|
|
|
int len, rem, status = -EIO;
|
|
|
|
unsigned int num_prs = 0;
|
2015-08-14 20:52:09 +08:00
|
|
|
u32 mask = 0;
|
|
|
|
|
2022-09-08 18:09:01 +08:00
|
|
|
if (!query->callback)
|
|
|
|
goto out;
|
2015-08-14 20:52:09 +08:00
|
|
|
|
2022-09-08 18:09:01 +08:00
|
|
|
path_query = container_of(query, struct ib_sa_path_query, sa_query);
|
|
|
|
mad = query->mad_buf->mad;
|
|
|
|
|
|
|
|
head = (const struct nlattr *) nlmsg_data(nlh);
|
|
|
|
len = nlmsg_len(nlh);
|
|
|
|
switch (query->path_use) {
|
|
|
|
case LS_RESOLVE_PATH_USE_UNIDIRECTIONAL:
|
|
|
|
mask = IB_PATH_PRIMARY | IB_PATH_OUTBOUND;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case LS_RESOLVE_PATH_USE_ALL:
|
|
|
|
mask = IB_PATH_PRIMARY;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case LS_RESOLVE_PATH_USE_GMP:
|
|
|
|
default:
|
|
|
|
mask = IB_PATH_PRIMARY | IB_PATH_GMP |
|
|
|
|
IB_PATH_BIDIRECTIONAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
nla_for_each_attr(curr, head, len, rem) {
|
|
|
|
if (curr->nla_type != LS_NLA_TYPE_PATH_RECORD)
|
|
|
|
continue;
|
|
|
|
|
2023-01-04 16:03:41 +08:00
|
|
|
rec_data = nla_data(curr);
|
|
|
|
if ((rec_data->flags & mask) != mask)
|
2022-09-08 18:09:01 +08:00
|
|
|
continue;
|
|
|
|
|
2023-01-04 16:03:41 +08:00
|
|
|
if ((query->flags & IB_SA_QUERY_OPA) ||
|
|
|
|
path_query->conv_pr) {
|
|
|
|
mad->mad_hdr.method |= IB_MGMT_METHOD_RESP;
|
|
|
|
memcpy(mad->data, rec_data->path_rec,
|
|
|
|
sizeof(rec_data->path_rec));
|
|
|
|
query->callback(query, 0, mad);
|
|
|
|
goto out;
|
2015-08-14 20:52:09 +08:00
|
|
|
}
|
2022-09-08 18:09:01 +08:00
|
|
|
|
2023-01-04 16:03:41 +08:00
|
|
|
status = 0;
|
|
|
|
ib_unpack(path_rec_table, ARRAY_SIZE(path_rec_table),
|
|
|
|
rec_data->path_rec, &recs[num_prs]);
|
|
|
|
recs[num_prs].flags = rec_data->flags;
|
|
|
|
recs[num_prs].rec_type = SA_PATH_REC_TYPE_IB;
|
|
|
|
sa_path_set_dmac_zero(&recs[num_prs]);
|
|
|
|
|
2022-09-08 18:09:01 +08:00
|
|
|
num_prs++;
|
|
|
|
if (num_prs >= RDMA_PRIMARY_PATH_MAX_REC_NUM)
|
|
|
|
break;
|
2015-08-14 20:52:09 +08:00
|
|
|
}
|
|
|
|
|
2023-01-04 16:03:41 +08:00
|
|
|
if (!status) {
|
2022-09-08 18:09:01 +08:00
|
|
|
mad->mad_hdr.method |= IB_MGMT_METHOD_RESP;
|
2023-01-04 16:03:41 +08:00
|
|
|
path_query->callback(status, recs, num_prs,
|
|
|
|
path_query->context);
|
|
|
|
} else
|
|
|
|
query->callback(query, status, mad);
|
2022-09-08 18:09:01 +08:00
|
|
|
|
|
|
|
out:
|
2015-08-14 20:52:09 +08:00
|
|
|
mad_send_wc.send_buf = query->mad_buf;
|
|
|
|
mad_send_wc.status = IB_WC_SUCCESS;
|
|
|
|
send_handler(query->mad_buf->mad_agent, &mad_send_wc);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ib_nl_request_timeout(struct work_struct *work)
|
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
struct ib_sa_query *query;
|
|
|
|
unsigned long delay;
|
|
|
|
struct ib_mad_send_wc mad_send_wc;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&ib_nl_request_lock, flags);
|
|
|
|
while (!list_empty(&ib_nl_request_list)) {
|
|
|
|
query = list_entry(ib_nl_request_list.next,
|
|
|
|
struct ib_sa_query, list);
|
|
|
|
|
|
|
|
if (time_after(query->timeout, jiffies)) {
|
|
|
|
delay = query->timeout - jiffies;
|
|
|
|
if ((long)delay <= 0)
|
|
|
|
delay = 1;
|
|
|
|
queue_delayed_work(ib_nl_wq, &ib_nl_timed_work, delay);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
list_del(&query->list);
|
|
|
|
ib_sa_disable_local_svc(query);
|
|
|
|
/* Hold the lock to protect against query cancellation */
|
|
|
|
if (ib_sa_query_cancelled(query))
|
|
|
|
ret = -1;
|
|
|
|
else
|
|
|
|
ret = ib_post_send_mad(query->mad_buf, NULL);
|
|
|
|
if (ret) {
|
|
|
|
mad_send_wc.send_buf = query->mad_buf;
|
|
|
|
mad_send_wc.status = IB_WC_WR_FLUSH_ERR;
|
|
|
|
spin_unlock_irqrestore(&ib_nl_request_lock, flags);
|
|
|
|
send_handler(query->port->agent, &mad_send_wc);
|
|
|
|
spin_lock_irqsave(&ib_nl_request_lock, flags);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
spin_unlock_irqrestore(&ib_nl_request_lock, flags);
|
|
|
|
}
|
|
|
|
|
2016-05-19 22:12:35 +08:00
|
|
|
int ib_nl_handle_set_timeout(struct sk_buff *skb,
|
2017-06-15 19:20:39 +08:00
|
|
|
struct nlmsghdr *nlh,
|
|
|
|
struct netlink_ext_ack *extack)
|
2015-08-14 20:52:09 +08:00
|
|
|
{
|
|
|
|
int timeout, delta, abs_delta;
|
|
|
|
const struct nlattr *attr;
|
|
|
|
unsigned long flags;
|
|
|
|
struct ib_sa_query *query;
|
|
|
|
long delay = 0;
|
|
|
|
struct nlattr *tb[LS_NLA_TYPE_MAX];
|
|
|
|
int ret;
|
|
|
|
|
2016-01-21 21:41:31 +08:00
|
|
|
if (!(nlh->nlmsg_flags & NLM_F_REQUEST) ||
|
2017-06-12 21:00:19 +08:00
|
|
|
!(NETLINK_CB(skb).sk))
|
2015-08-14 20:52:09 +08:00
|
|
|
return -EPERM;
|
|
|
|
|
netlink: make validation more configurable for future strictness
We currently have two levels of strict validation:
1) liberal (default)
- undefined (type >= max) & NLA_UNSPEC attributes accepted
- attribute length >= expected accepted
- garbage at end of message accepted
2) strict (opt-in)
- NLA_UNSPEC attributes accepted
- attribute length >= expected accepted
Split out parsing strictness into four different options:
* TRAILING - check that there's no trailing data after parsing
attributes (in message or nested)
* MAXTYPE - reject attrs > max known type
* UNSPEC - reject attributes with NLA_UNSPEC policy entries
* STRICT_ATTRS - strictly validate attribute size
The default for future things should be *everything*.
The current *_strict() is a combination of TRAILING and MAXTYPE,
and is renamed to _deprecated_strict().
The current regular parsing has none of this, and is renamed to
*_parse_deprecated().
Additionally it allows us to selectively set one of the new flags
even on old policies. Notably, the UNSPEC flag could be useful in
this case, since it can be arranged (by filling in the policy) to
not be an incompatible userspace ABI change, but would then going
forward prevent forgetting attribute entries. Similar can apply
to the POLICY flag.
We end up with the following renames:
* nla_parse -> nla_parse_deprecated
* nla_parse_strict -> nla_parse_deprecated_strict
* nlmsg_parse -> nlmsg_parse_deprecated
* nlmsg_parse_strict -> nlmsg_parse_deprecated_strict
* nla_parse_nested -> nla_parse_nested_deprecated
* nla_validate_nested -> nla_validate_nested_deprecated
Using spatch, of course:
@@
expression TB, MAX, HEAD, LEN, POL, EXT;
@@
-nla_parse(TB, MAX, HEAD, LEN, POL, EXT)
+nla_parse_deprecated(TB, MAX, HEAD, LEN, POL, EXT)
@@
expression NLH, HDRLEN, TB, MAX, POL, EXT;
@@
-nlmsg_parse(NLH, HDRLEN, TB, MAX, POL, EXT)
+nlmsg_parse_deprecated(NLH, HDRLEN, TB, MAX, POL, EXT)
@@
expression NLH, HDRLEN, TB, MAX, POL, EXT;
@@
-nlmsg_parse_strict(NLH, HDRLEN, TB, MAX, POL, EXT)
+nlmsg_parse_deprecated_strict(NLH, HDRLEN, TB, MAX, POL, EXT)
@@
expression TB, MAX, NLA, POL, EXT;
@@
-nla_parse_nested(TB, MAX, NLA, POL, EXT)
+nla_parse_nested_deprecated(TB, MAX, NLA, POL, EXT)
@@
expression START, MAX, POL, EXT;
@@
-nla_validate_nested(START, MAX, POL, EXT)
+nla_validate_nested_deprecated(START, MAX, POL, EXT)
@@
expression NLH, HDRLEN, MAX, POL, EXT;
@@
-nlmsg_validate(NLH, HDRLEN, MAX, POL, EXT)
+nlmsg_validate_deprecated(NLH, HDRLEN, MAX, POL, EXT)
For this patch, don't actually add the strict, non-renamed versions
yet so that it breaks compile if I get it wrong.
Also, while at it, make nla_validate and nla_parse go down to a
common __nla_validate_parse() function to avoid code duplication.
Ultimately, this allows us to have very strict validation for every
new caller of nla_parse()/nlmsg_parse() etc as re-introduced in the
next patch, while existing things will continue to work as is.
In effect then, this adds fully strict validation for any new command.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-04-26 20:07:28 +08:00
|
|
|
ret = nla_parse_deprecated(tb, LS_NLA_TYPE_MAX - 1, nlmsg_data(nlh),
|
|
|
|
nlmsg_len(nlh), ib_nl_policy, NULL);
|
2015-08-14 20:52:09 +08:00
|
|
|
attr = (const struct nlattr *)tb[LS_NLA_TYPE_TIMEOUT];
|
|
|
|
if (ret || !attr)
|
|
|
|
goto settimeout_out;
|
|
|
|
|
|
|
|
timeout = *(int *) nla_data(attr);
|
|
|
|
if (timeout < IB_SA_LOCAL_SVC_TIMEOUT_MIN)
|
|
|
|
timeout = IB_SA_LOCAL_SVC_TIMEOUT_MIN;
|
|
|
|
if (timeout > IB_SA_LOCAL_SVC_TIMEOUT_MAX)
|
|
|
|
timeout = IB_SA_LOCAL_SVC_TIMEOUT_MAX;
|
|
|
|
|
|
|
|
delta = timeout - sa_local_svc_timeout_ms;
|
|
|
|
if (delta < 0)
|
|
|
|
abs_delta = -delta;
|
|
|
|
else
|
|
|
|
abs_delta = delta;
|
|
|
|
|
|
|
|
if (delta != 0) {
|
|
|
|
spin_lock_irqsave(&ib_nl_request_lock, flags);
|
|
|
|
sa_local_svc_timeout_ms = timeout;
|
|
|
|
list_for_each_entry(query, &ib_nl_request_list, list) {
|
|
|
|
if (delta < 0 && abs_delta > query->timeout)
|
|
|
|
query->timeout = 0;
|
|
|
|
else
|
|
|
|
query->timeout += delta;
|
|
|
|
|
|
|
|
/* Get the new delay from the first entry */
|
|
|
|
if (!delay) {
|
|
|
|
delay = query->timeout - jiffies;
|
|
|
|
if (delay <= 0)
|
|
|
|
delay = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (delay)
|
|
|
|
mod_delayed_work(ib_nl_wq, &ib_nl_timed_work,
|
|
|
|
(unsigned long)delay);
|
|
|
|
spin_unlock_irqrestore(&ib_nl_request_lock, flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
settimeout_out:
|
2019-12-16 20:04:36 +08:00
|
|
|
return 0;
|
2015-08-14 20:52:09 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline int ib_nl_is_good_resolve_resp(const struct nlmsghdr *nlh)
|
|
|
|
{
|
|
|
|
struct nlattr *tb[LS_NLA_TYPE_MAX];
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (nlh->nlmsg_flags & RDMA_NL_LS_F_ERR)
|
|
|
|
return 0;
|
|
|
|
|
netlink: make validation more configurable for future strictness
We currently have two levels of strict validation:
1) liberal (default)
- undefined (type >= max) & NLA_UNSPEC attributes accepted
- attribute length >= expected accepted
- garbage at end of message accepted
2) strict (opt-in)
- NLA_UNSPEC attributes accepted
- attribute length >= expected accepted
Split out parsing strictness into four different options:
* TRAILING - check that there's no trailing data after parsing
attributes (in message or nested)
* MAXTYPE - reject attrs > max known type
* UNSPEC - reject attributes with NLA_UNSPEC policy entries
* STRICT_ATTRS - strictly validate attribute size
The default for future things should be *everything*.
The current *_strict() is a combination of TRAILING and MAXTYPE,
and is renamed to _deprecated_strict().
The current regular parsing has none of this, and is renamed to
*_parse_deprecated().
Additionally it allows us to selectively set one of the new flags
even on old policies. Notably, the UNSPEC flag could be useful in
this case, since it can be arranged (by filling in the policy) to
not be an incompatible userspace ABI change, but would then going
forward prevent forgetting attribute entries. Similar can apply
to the POLICY flag.
We end up with the following renames:
* nla_parse -> nla_parse_deprecated
* nla_parse_strict -> nla_parse_deprecated_strict
* nlmsg_parse -> nlmsg_parse_deprecated
* nlmsg_parse_strict -> nlmsg_parse_deprecated_strict
* nla_parse_nested -> nla_parse_nested_deprecated
* nla_validate_nested -> nla_validate_nested_deprecated
Using spatch, of course:
@@
expression TB, MAX, HEAD, LEN, POL, EXT;
@@
-nla_parse(TB, MAX, HEAD, LEN, POL, EXT)
+nla_parse_deprecated(TB, MAX, HEAD, LEN, POL, EXT)
@@
expression NLH, HDRLEN, TB, MAX, POL, EXT;
@@
-nlmsg_parse(NLH, HDRLEN, TB, MAX, POL, EXT)
+nlmsg_parse_deprecated(NLH, HDRLEN, TB, MAX, POL, EXT)
@@
expression NLH, HDRLEN, TB, MAX, POL, EXT;
@@
-nlmsg_parse_strict(NLH, HDRLEN, TB, MAX, POL, EXT)
+nlmsg_parse_deprecated_strict(NLH, HDRLEN, TB, MAX, POL, EXT)
@@
expression TB, MAX, NLA, POL, EXT;
@@
-nla_parse_nested(TB, MAX, NLA, POL, EXT)
+nla_parse_nested_deprecated(TB, MAX, NLA, POL, EXT)
@@
expression START, MAX, POL, EXT;
@@
-nla_validate_nested(START, MAX, POL, EXT)
+nla_validate_nested_deprecated(START, MAX, POL, EXT)
@@
expression NLH, HDRLEN, MAX, POL, EXT;
@@
-nlmsg_validate(NLH, HDRLEN, MAX, POL, EXT)
+nlmsg_validate_deprecated(NLH, HDRLEN, MAX, POL, EXT)
For this patch, don't actually add the strict, non-renamed versions
yet so that it breaks compile if I get it wrong.
Also, while at it, make nla_validate and nla_parse go down to a
common __nla_validate_parse() function to avoid code duplication.
Ultimately, this allows us to have very strict validation for every
new caller of nla_parse()/nlmsg_parse() etc as re-introduced in the
next patch, while existing things will continue to work as is.
In effect then, this adds fully strict validation for any new command.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-04-26 20:07:28 +08:00
|
|
|
ret = nla_parse_deprecated(tb, LS_NLA_TYPE_MAX - 1, nlmsg_data(nlh),
|
|
|
|
nlmsg_len(nlh), ib_nl_policy, NULL);
|
2015-08-14 20:52:09 +08:00
|
|
|
if (ret)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2016-05-19 22:12:35 +08:00
|
|
|
int ib_nl_handle_resolve_resp(struct sk_buff *skb,
|
2017-06-15 19:20:39 +08:00
|
|
|
struct nlmsghdr *nlh,
|
|
|
|
struct netlink_ext_ack *extack)
|
2015-08-14 20:52:09 +08:00
|
|
|
{
|
|
|
|
unsigned long flags;
|
2022-03-31 17:16:34 +08:00
|
|
|
struct ib_sa_query *query = NULL, *iter;
|
2015-08-14 20:52:09 +08:00
|
|
|
struct ib_mad_send_buf *send_buf;
|
|
|
|
struct ib_mad_send_wc mad_send_wc;
|
|
|
|
int ret;
|
|
|
|
|
2016-01-21 21:41:31 +08:00
|
|
|
if ((nlh->nlmsg_flags & NLM_F_REQUEST) ||
|
2017-06-12 21:00:19 +08:00
|
|
|
!(NETLINK_CB(skb).sk))
|
2015-08-14 20:52:09 +08:00
|
|
|
return -EPERM;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&ib_nl_request_lock, flags);
|
2022-03-31 17:16:34 +08:00
|
|
|
list_for_each_entry(iter, &ib_nl_request_list, list) {
|
2015-08-14 20:52:09 +08:00
|
|
|
/*
|
|
|
|
* If the query is cancelled, let the timeout routine
|
|
|
|
* take care of it.
|
|
|
|
*/
|
2022-03-31 17:16:34 +08:00
|
|
|
if (nlh->nlmsg_seq == iter->seq) {
|
|
|
|
if (!ib_sa_query_cancelled(iter)) {
|
|
|
|
list_del(&iter->list);
|
|
|
|
query = iter;
|
|
|
|
}
|
2015-08-14 20:52:09 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-31 17:16:34 +08:00
|
|
|
if (!query) {
|
2015-08-14 20:52:09 +08:00
|
|
|
spin_unlock_irqrestore(&ib_nl_request_lock, flags);
|
|
|
|
goto resp_out;
|
|
|
|
}
|
|
|
|
|
|
|
|
send_buf = query->mad_buf;
|
|
|
|
|
|
|
|
if (!ib_nl_is_good_resolve_resp(nlh)) {
|
|
|
|
/* if the result is a failure, send out the packet via IB */
|
|
|
|
ib_sa_disable_local_svc(query);
|
|
|
|
ret = ib_post_send_mad(query->mad_buf, NULL);
|
|
|
|
spin_unlock_irqrestore(&ib_nl_request_lock, flags);
|
|
|
|
if (ret) {
|
|
|
|
mad_send_wc.send_buf = send_buf;
|
|
|
|
mad_send_wc.status = IB_WC_GENERAL_ERR;
|
|
|
|
send_handler(query->port->agent, &mad_send_wc);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
spin_unlock_irqrestore(&ib_nl_request_lock, flags);
|
|
|
|
ib_nl_process_good_resolve_rsp(query, nlh);
|
|
|
|
}
|
|
|
|
|
|
|
|
resp_out:
|
2019-12-16 20:04:36 +08:00
|
|
|
return 0;
|
2015-08-14 20:52:09 +08:00
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static void free_sm_ah(struct kref *kref)
|
|
|
|
{
|
|
|
|
struct ib_sa_sm_ah *sm_ah = container_of(kref, struct ib_sa_sm_ah, ref);
|
|
|
|
|
2018-12-12 17:09:06 +08:00
|
|
|
rdma_destroy_ah(sm_ah->ah, 0);
|
2005-04-17 06:20:36 +08:00
|
|
|
kfree(sm_ah);
|
|
|
|
}
|
|
|
|
|
2006-08-22 07:40:12 +08:00
|
|
|
void ib_sa_register_client(struct ib_sa_client *client)
|
|
|
|
{
|
|
|
|
atomic_set(&client->users, 1);
|
|
|
|
init_completion(&client->comp);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ib_sa_register_client);
|
|
|
|
|
|
|
|
void ib_sa_unregister_client(struct ib_sa_client *client)
|
|
|
|
{
|
|
|
|
ib_sa_client_put(client);
|
|
|
|
wait_for_completion(&client->comp);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ib_sa_unregister_client);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/**
|
|
|
|
* ib_sa_cancel_query - try to cancel an SA query
|
|
|
|
* @id:ID of query to cancel
|
|
|
|
* @query:query pointer to cancel
|
|
|
|
*
|
|
|
|
* Try to cancel an SA query. If the id and query don't match up or
|
|
|
|
* the query has already completed, nothing is done. Otherwise the
|
|
|
|
* query is canceled and will complete with a status of -EINTR.
|
|
|
|
*/
|
|
|
|
void ib_sa_cancel_query(int id, struct ib_sa_query *query)
|
|
|
|
{
|
|
|
|
unsigned long flags;
|
2005-10-26 01:51:39 +08:00
|
|
|
struct ib_mad_send_buf *mad_buf;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2019-02-21 08:20:46 +08:00
|
|
|
xa_lock_irqsave(&queries, flags);
|
|
|
|
if (xa_load(&queries, id) != query) {
|
|
|
|
xa_unlock_irqrestore(&queries, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
return;
|
|
|
|
}
|
2005-10-26 01:51:39 +08:00
|
|
|
mad_buf = query->mad_buf;
|
2019-02-21 08:20:46 +08:00
|
|
|
xa_unlock_irqrestore(&queries, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2015-08-14 20:52:09 +08:00
|
|
|
/*
|
|
|
|
* If the query is still on the netlink request list, schedule
|
|
|
|
* it to be cancelled by the timeout routine. Otherwise, it has been
|
|
|
|
* sent to the MAD layer and has to be cancelled from there.
|
|
|
|
*/
|
|
|
|
if (!ib_nl_cancel_request(query))
|
2021-06-02 18:27:06 +08:00
|
|
|
ib_cancel_mad(mad_buf);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ib_sa_cancel_query);
|
|
|
|
|
2021-03-01 15:04:20 +08:00
|
|
|
static u8 get_src_path_mask(struct ib_device *device, u32 port_num)
|
2007-04-06 01:51:10 +08:00
|
|
|
{
|
|
|
|
struct ib_sa_device *sa_dev;
|
|
|
|
struct ib_sa_port *port;
|
|
|
|
unsigned long flags;
|
|
|
|
u8 src_path_mask;
|
|
|
|
|
|
|
|
sa_dev = ib_get_client_data(device, &sa_client);
|
|
|
|
if (!sa_dev)
|
|
|
|
return 0x7f;
|
|
|
|
|
|
|
|
port = &sa_dev->port[port_num - sa_dev->start_port];
|
|
|
|
spin_lock_irqsave(&port->ah_lock, flags);
|
|
|
|
src_path_mask = port->sm_ah ? port->sm_ah->src_path_mask : 0x7f;
|
|
|
|
spin_unlock_irqrestore(&port->ah_lock, flags);
|
|
|
|
|
|
|
|
return src_path_mask;
|
|
|
|
}
|
|
|
|
|
2021-03-01 15:04:20 +08:00
|
|
|
static int init_ah_attr_grh_fields(struct ib_device *device, u32 port_num,
|
2018-03-13 22:06:20 +08:00
|
|
|
struct sa_path_rec *rec,
|
2018-06-19 15:59:19 +08:00
|
|
|
struct rdma_ah_attr *ah_attr,
|
|
|
|
const struct ib_gid_attr *gid_attr)
|
2018-03-13 22:06:20 +08:00
|
|
|
{
|
|
|
|
enum ib_gid_type type = sa_conv_pathrec_to_gid_type(rec);
|
2010-10-14 03:26:51 +08:00
|
|
|
|
2018-06-19 15:59:19 +08:00
|
|
|
if (!gid_attr) {
|
|
|
|
gid_attr = rdma_find_gid_by_port(device, &rec->sgid, type,
|
|
|
|
port_num, NULL);
|
|
|
|
if (IS_ERR(gid_attr))
|
|
|
|
return PTR_ERR(gid_attr);
|
|
|
|
} else
|
|
|
|
rdma_hold_gid_attr(gid_attr);
|
2015-12-23 20:56:53 +08:00
|
|
|
|
2018-06-19 15:59:16 +08:00
|
|
|
rdma_move_grh_sgid_attr(ah_attr, &rec->dgid,
|
|
|
|
be32_to_cpu(rec->flow_label),
|
|
|
|
rec->hop_limit, rec->traffic_class,
|
|
|
|
gid_attr);
|
2018-03-13 22:06:20 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2015-12-23 20:56:53 +08:00
|
|
|
|
2018-06-19 15:59:19 +08:00
|
|
|
/**
|
|
|
|
* ib_init_ah_attr_from_path - Initialize address handle attributes based on
|
|
|
|
* an SA path record.
|
|
|
|
* @device: Device associated ah attributes initialization.
|
|
|
|
* @port_num: Port on the specified device.
|
|
|
|
* @rec: path record entry to use for ah attributes initialization.
|
|
|
|
* @ah_attr: address handle attributes to initialization from path record.
|
2019-10-10 11:52:49 +08:00
|
|
|
* @gid_attr: SGID attribute to consider during initialization.
|
2018-06-19 15:59:19 +08:00
|
|
|
*
|
|
|
|
* When ib_init_ah_attr_from_path() returns success,
|
|
|
|
* (a) for IB link layer it optionally contains a reference to SGID attribute
|
|
|
|
* when GRH is present for IB link layer.
|
|
|
|
* (b) for RoCE link layer it contains a reference to SGID attribute.
|
|
|
|
* User must invoke rdma_destroy_ah_attr() to release reference to SGID
|
|
|
|
* attributes which are initialized using ib_init_ah_attr_from_path().
|
|
|
|
*/
|
2021-03-01 15:04:20 +08:00
|
|
|
int ib_init_ah_attr_from_path(struct ib_device *device, u32 port_num,
|
2018-03-13 22:06:20 +08:00
|
|
|
struct sa_path_rec *rec,
|
2018-06-19 15:59:19 +08:00
|
|
|
struct rdma_ah_attr *ah_attr,
|
|
|
|
const struct ib_gid_attr *gid_attr)
|
2018-03-13 22:06:20 +08:00
|
|
|
{
|
|
|
|
int ret = 0;
|
2015-10-15 23:38:47 +08:00
|
|
|
|
2018-03-13 22:06:20 +08:00
|
|
|
memset(ah_attr, 0, sizeof(*ah_attr));
|
|
|
|
ah_attr->type = rdma_ah_find_type(device, port_num);
|
|
|
|
rdma_ah_set_sl(ah_attr, rec->sl);
|
|
|
|
rdma_ah_set_port_num(ah_attr, port_num);
|
|
|
|
rdma_ah_set_static_rate(ah_attr, rec->rate);
|
2017-04-28 07:05:59 +08:00
|
|
|
|
2018-03-13 22:06:20 +08:00
|
|
|
if (sa_path_is_roce(rec)) {
|
2018-07-08 18:41:17 +08:00
|
|
|
ret = roce_resolve_route_from_path(rec, gid_attr);
|
2018-03-13 22:06:20 +08:00
|
|
|
if (ret)
|
2006-06-18 11:37:39 +08:00
|
|
|
return ret;
|
|
|
|
|
2018-03-13 22:06:20 +08:00
|
|
|
memcpy(ah_attr->roce.dmac, sa_path_get_dmac(rec), ETH_ALEN);
|
|
|
|
} else {
|
|
|
|
rdma_ah_set_dlid(ah_attr, be32_to_cpu(sa_path_get_dlid(rec)));
|
|
|
|
if (sa_path_is_opa(rec) &&
|
|
|
|
rdma_ah_get_dlid(ah_attr) == be16_to_cpu(IB_LID_PERMISSIVE))
|
|
|
|
rdma_ah_set_make_grd(ah_attr, true);
|
|
|
|
|
|
|
|
rdma_ah_set_path_bits(ah_attr,
|
|
|
|
be32_to_cpu(sa_path_get_slid(rec)) &
|
|
|
|
get_src_path_mask(device, port_num));
|
2017-04-28 07:06:00 +08:00
|
|
|
}
|
2015-12-23 20:56:53 +08:00
|
|
|
|
2018-03-13 22:06:20 +08:00
|
|
|
if (rec->hop_limit > 0 || sa_path_is_roce(rec))
|
2018-06-19 15:59:19 +08:00
|
|
|
ret = init_ah_attr_grh_fields(device, port_num,
|
|
|
|
rec, ah_attr, gid_attr);
|
2018-03-13 22:06:20 +08:00
|
|
|
return ret;
|
2006-06-18 11:37:39 +08:00
|
|
|
}
|
2017-11-14 20:52:16 +08:00
|
|
|
EXPORT_SYMBOL(ib_init_ah_attr_from_path);
|
2006-06-18 11:37:39 +08:00
|
|
|
|
2007-06-19 02:03:58 +08:00
|
|
|
static int alloc_mad(struct ib_sa_query *query, gfp_t gfp_mask)
|
|
|
|
{
|
2017-12-19 11:26:58 +08:00
|
|
|
struct rdma_ah_attr ah_attr;
|
2007-06-19 02:03:58 +08:00
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&query->port->ah_lock, flags);
|
2008-07-15 14:48:43 +08:00
|
|
|
if (!query->port->sm_ah) {
|
|
|
|
spin_unlock_irqrestore(&query->port->ah_lock, flags);
|
|
|
|
return -EAGAIN;
|
|
|
|
}
|
2007-06-19 02:03:58 +08:00
|
|
|
kref_get(&query->port->sm_ah->ref);
|
|
|
|
query->sm_ah = query->port->sm_ah;
|
|
|
|
spin_unlock_irqrestore(&query->port->ah_lock, flags);
|
|
|
|
|
2017-12-19 11:26:58 +08:00
|
|
|
/*
|
|
|
|
* Always check if sm_ah has valid dlid assigned,
|
|
|
|
* before querying for class port info
|
|
|
|
*/
|
|
|
|
if ((rdma_query_ah(query->sm_ah->ah, &ah_attr) < 0) ||
|
|
|
|
!rdma_is_valid_unicast_lid(&ah_attr)) {
|
|
|
|
kref_put(&query->sm_ah->ref, free_sm_ah);
|
|
|
|
return -EAGAIN;
|
|
|
|
}
|
2007-06-19 02:03:58 +08:00
|
|
|
query->mad_buf = ib_create_send_mad(query->port->agent, 1,
|
|
|
|
query->sm_ah->pkey_index,
|
|
|
|
0, IB_MGMT_SA_HDR, IB_MGMT_SA_DATA,
|
2015-06-07 02:38:28 +08:00
|
|
|
gfp_mask,
|
2017-04-29 07:20:26 +08:00
|
|
|
((query->flags & IB_SA_QUERY_OPA) ?
|
|
|
|
OPA_MGMT_BASE_VERSION :
|
|
|
|
IB_MGMT_BASE_VERSION));
|
2007-09-09 19:55:11 +08:00
|
|
|
if (IS_ERR(query->mad_buf)) {
|
2007-06-19 02:03:58 +08:00
|
|
|
kref_put(&query->sm_ah->ref, free_sm_ah);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
query->mad_buf->ah = query->sm_ah->ah;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void free_mad(struct ib_sa_query *query)
|
|
|
|
{
|
|
|
|
ib_free_send_mad(query->mad_buf);
|
|
|
|
kref_put(&query->sm_ah->ref, free_sm_ah);
|
|
|
|
}
|
|
|
|
|
2017-04-29 07:20:26 +08:00
|
|
|
static void init_mad(struct ib_sa_query *query, struct ib_mad_agent *agent)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2017-04-29 07:20:26 +08:00
|
|
|
struct ib_sa_mad *mad = query->mad_buf->mad;
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
memset(mad, 0, sizeof *mad);
|
|
|
|
|
2017-04-29 07:20:26 +08:00
|
|
|
if (query->flags & IB_SA_QUERY_OPA) {
|
|
|
|
mad->mad_hdr.base_version = OPA_MGMT_BASE_VERSION;
|
|
|
|
mad->mad_hdr.class_version = OPA_SA_CLASS_VERSION;
|
|
|
|
} else {
|
|
|
|
mad->mad_hdr.base_version = IB_MGMT_BASE_VERSION;
|
|
|
|
mad->mad_hdr.class_version = IB_SA_CLASS_VERSION;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
mad->mad_hdr.mgmt_class = IB_MGMT_CLASS_SUBN_ADM;
|
|
|
|
spin_lock_irqsave(&tid_lock, flags);
|
|
|
|
mad->mad_hdr.tid =
|
|
|
|
cpu_to_be64(((u64) agent->hi_tid) << 32 | tid++);
|
|
|
|
spin_unlock_irqrestore(&tid_lock, flags);
|
|
|
|
}
|
|
|
|
|
2018-10-11 22:30:05 +08:00
|
|
|
static int send_mad(struct ib_sa_query *query, unsigned long timeout_ms,
|
|
|
|
gfp_t gfp_mask)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
unsigned long flags;
|
2005-10-26 01:51:39 +08:00
|
|
|
int ret, id;
|
2021-08-13 00:12:35 +08:00
|
|
|
const int nmbr_sa_query_retries = 10;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2019-02-21 08:20:46 +08:00
|
|
|
xa_lock_irqsave(&queries, flags);
|
|
|
|
ret = __xa_alloc(&queries, &id, query, xa_limit_32b, gfp_mask);
|
|
|
|
xa_unlock_irqrestore(&queries, flags);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2021-08-13 00:12:35 +08:00
|
|
|
query->mad_buf->timeout_ms = timeout_ms / nmbr_sa_query_retries;
|
|
|
|
query->mad_buf->retries = nmbr_sa_query_retries;
|
|
|
|
if (!query->mad_buf->timeout_ms) {
|
|
|
|
/* Special case, very small timeout_ms */
|
|
|
|
query->mad_buf->timeout_ms = 1;
|
|
|
|
query->mad_buf->retries = timeout_ms;
|
|
|
|
}
|
2005-10-26 01:51:39 +08:00
|
|
|
query->mad_buf->context[0] = query;
|
|
|
|
query->id = id;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2017-04-28 07:06:02 +08:00
|
|
|
if ((query->flags & IB_SA_ENABLE_LOCAL_SERVICE) &&
|
|
|
|
(!(query->flags & IB_SA_QUERY_OPA))) {
|
2018-10-02 16:49:24 +08:00
|
|
|
if (rdma_nl_chk_listeners(RDMA_NL_GROUP_LS)) {
|
2015-10-30 20:23:45 +08:00
|
|
|
if (!ib_nl_make_request(query, gfp_mask))
|
2015-08-14 20:52:09 +08:00
|
|
|
return id;
|
|
|
|
}
|
|
|
|
ib_sa_disable_local_svc(query);
|
|
|
|
}
|
|
|
|
|
2005-10-26 01:51:39 +08:00
|
|
|
ret = ib_post_send_mad(query->mad_buf, NULL);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (ret) {
|
2019-02-21 08:20:46 +08:00
|
|
|
xa_lock_irqsave(&queries, flags);
|
|
|
|
__xa_erase(&queries, id);
|
|
|
|
xa_unlock_irqrestore(&queries, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2005-06-28 05:36:46 +08:00
|
|
|
/*
|
|
|
|
* It's not safe to dereference query any more, because the
|
|
|
|
* send may already have completed and freed the query in
|
2005-10-26 01:51:39 +08:00
|
|
|
* another context.
|
2005-06-28 05:36:46 +08:00
|
|
|
*/
|
2005-10-26 01:51:39 +08:00
|
|
|
return ret ? ret : id;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2017-04-28 07:05:58 +08:00
|
|
|
void ib_sa_unpack_path(void *attribute, struct sa_path_rec *rec)
|
2009-11-17 01:30:33 +08:00
|
|
|
{
|
|
|
|
ib_unpack(path_rec_table, ARRAY_SIZE(path_rec_table), attribute, rec);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ib_sa_unpack_path);
|
|
|
|
|
2017-04-28 07:05:58 +08:00
|
|
|
void ib_sa_pack_path(struct sa_path_rec *rec, void *attribute)
|
2013-05-30 01:09:26 +08:00
|
|
|
{
|
|
|
|
ib_pack(path_rec_table, ARRAY_SIZE(path_rec_table), rec, attribute);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ib_sa_pack_path);
|
|
|
|
|
2017-04-28 07:06:02 +08:00
|
|
|
static bool ib_sa_opa_pathrecord_support(struct ib_sa_client *client,
|
2020-04-08 07:20:09 +08:00
|
|
|
struct ib_sa_device *sa_dev,
|
2021-03-01 15:04:20 +08:00
|
|
|
u32 port_num)
|
2017-04-28 07:06:02 +08:00
|
|
|
{
|
|
|
|
struct ib_sa_port *port;
|
|
|
|
unsigned long flags;
|
|
|
|
bool ret = false;
|
|
|
|
|
|
|
|
port = &sa_dev->port[port_num - sa_dev->start_port];
|
|
|
|
spin_lock_irqsave(&port->classport_lock, flags);
|
|
|
|
if (!port->classport_info.valid)
|
|
|
|
goto ret;
|
|
|
|
|
|
|
|
if (port->classport_info.data.type == RDMA_CLASS_PORT_INFO_OPA)
|
|
|
|
ret = opa_get_cpi_capmask2(&port->classport_info.data.opa) &
|
|
|
|
OPA_CLASS_PORT_INFO_PR_SUPPORT;
|
|
|
|
ret:
|
|
|
|
spin_unlock_irqrestore(&port->classport_lock, flags);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
enum opa_pr_supported {
|
|
|
|
PR_NOT_SUPPORTED,
|
|
|
|
PR_OPA_SUPPORTED,
|
|
|
|
PR_IB_SUPPORTED
|
|
|
|
};
|
|
|
|
|
2021-01-19 06:39:21 +08:00
|
|
|
/*
|
2020-12-01 20:08:55 +08:00
|
|
|
* opa_pr_query_possible - Check if current PR query can be an OPA query.
|
|
|
|
*
|
2017-04-28 07:06:02 +08:00
|
|
|
* Retuns PR_NOT_SUPPORTED if a path record query is not
|
|
|
|
* possible, PR_OPA_SUPPORTED if an OPA path record query
|
|
|
|
* is possible and PR_IB_SUPPORTED if an IB path record
|
|
|
|
* query is possible.
|
|
|
|
*/
|
|
|
|
static int opa_pr_query_possible(struct ib_sa_client *client,
|
2020-04-08 07:20:09 +08:00
|
|
|
struct ib_sa_device *sa_dev,
|
2021-06-25 20:30:57 +08:00
|
|
|
struct ib_device *device, u32 port_num)
|
2017-04-28 07:06:02 +08:00
|
|
|
{
|
|
|
|
struct ib_port_attr port_attr;
|
|
|
|
|
|
|
|
if (ib_query_port(device, port_num, &port_attr))
|
|
|
|
return PR_NOT_SUPPORTED;
|
|
|
|
|
2020-04-08 07:20:09 +08:00
|
|
|
if (ib_sa_opa_pathrecord_support(client, sa_dev, port_num))
|
2017-04-28 07:06:02 +08:00
|
|
|
return PR_OPA_SUPPORTED;
|
|
|
|
|
|
|
|
if (port_attr.lid >= be16_to_cpu(IB_MULTICAST_LID_BASE))
|
|
|
|
return PR_NOT_SUPPORTED;
|
|
|
|
else
|
|
|
|
return PR_IB_SUPPORTED;
|
|
|
|
}
|
|
|
|
|
2023-01-04 16:03:41 +08:00
|
|
|
static void ib_sa_path_rec_callback(struct ib_sa_query *sa_query,
|
|
|
|
int status, struct ib_sa_mad *mad)
|
2022-09-08 18:09:01 +08:00
|
|
|
{
|
2023-01-04 16:03:41 +08:00
|
|
|
struct ib_sa_path_query *query =
|
|
|
|
container_of(sa_query, struct ib_sa_path_query, sa_query);
|
2022-09-08 18:09:01 +08:00
|
|
|
struct sa_path_rec rec = {};
|
|
|
|
|
2023-01-04 16:03:41 +08:00
|
|
|
if (!mad) {
|
|
|
|
query->callback(status, NULL, 0, query->context);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sa_query->flags & IB_SA_QUERY_OPA) {
|
|
|
|
ib_unpack(opa_path_rec_table, ARRAY_SIZE(opa_path_rec_table),
|
|
|
|
mad->data, &rec);
|
|
|
|
rec.rec_type = SA_PATH_REC_TYPE_OPA;
|
|
|
|
query->callback(status, &rec, 1, query->context);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-09-08 18:09:01 +08:00
|
|
|
ib_unpack(path_rec_table, ARRAY_SIZE(path_rec_table),
|
|
|
|
mad->data, &rec);
|
|
|
|
rec.rec_type = SA_PATH_REC_TYPE_IB;
|
|
|
|
sa_path_set_dmac_zero(&rec);
|
|
|
|
|
|
|
|
if (query->conv_pr) {
|
|
|
|
struct sa_path_rec opa;
|
|
|
|
|
|
|
|
memset(&opa, 0, sizeof(struct sa_path_rec));
|
|
|
|
sa_convert_path_ib_to_opa(&opa, &rec);
|
|
|
|
query->callback(status, &opa, 1, query->context);
|
|
|
|
} else {
|
|
|
|
query->callback(status, &rec, 1, query->context);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static void ib_sa_path_rec_release(struct ib_sa_query *sa_query)
|
|
|
|
{
|
2017-04-28 07:06:02 +08:00
|
|
|
struct ib_sa_path_query *query =
|
|
|
|
container_of(sa_query, struct ib_sa_path_query, sa_query);
|
|
|
|
|
|
|
|
kfree(query->conv_pr);
|
|
|
|
kfree(query);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* ib_sa_path_rec_get - Start a Path get query
|
2006-08-22 07:40:12 +08:00
|
|
|
* @client:SA client
|
2005-04-17 06:20:36 +08:00
|
|
|
* @device:device to send query on
|
|
|
|
* @port_num: port number to send query on
|
|
|
|
* @rec:Path Record to send in query
|
|
|
|
* @comp_mask:component mask to send in query
|
|
|
|
* @timeout_ms:time to wait for response
|
|
|
|
* @gfp_mask:GFP mask to use for internal allocations
|
|
|
|
* @callback:function called when query completes, times out or is
|
|
|
|
* canceled
|
|
|
|
* @context:opaque user context passed to callback
|
|
|
|
* @sa_query:query context, used to cancel query
|
|
|
|
*
|
|
|
|
* Send a Path Record Get query to the SA to look up a path. The
|
|
|
|
* callback function will be called when the query completes (or
|
|
|
|
* fails); status is 0 for a successful response, -EINTR if the query
|
|
|
|
* is canceled, -ETIMEDOUT is the query timed out, or -EIO if an error
|
|
|
|
* occurred sending the query. The resp parameter of the callback is
|
|
|
|
* only valid if status is 0.
|
|
|
|
*
|
|
|
|
* If the return value of ib_sa_path_rec_get() is negative, it is an
|
|
|
|
* error code. Otherwise it is a query ID that can be used to cancel
|
|
|
|
* the query.
|
|
|
|
*/
|
2006-08-22 07:40:12 +08:00
|
|
|
int ib_sa_path_rec_get(struct ib_sa_client *client,
|
2021-03-01 15:04:20 +08:00
|
|
|
struct ib_device *device, u32 port_num,
|
2017-04-28 07:05:58 +08:00
|
|
|
struct sa_path_rec *rec,
|
2005-04-17 06:20:36 +08:00
|
|
|
ib_sa_comp_mask comp_mask,
|
2018-10-11 22:30:05 +08:00
|
|
|
unsigned long timeout_ms, gfp_t gfp_mask,
|
2005-04-17 06:20:36 +08:00
|
|
|
void (*callback)(int status,
|
2017-04-28 07:05:58 +08:00
|
|
|
struct sa_path_rec *resp,
|
2023-01-04 16:03:41 +08:00
|
|
|
unsigned int num_paths, void *context),
|
2005-04-17 06:20:36 +08:00
|
|
|
void *context,
|
|
|
|
struct ib_sa_query **sa_query)
|
|
|
|
{
|
|
|
|
struct ib_sa_path_query *query;
|
|
|
|
struct ib_sa_device *sa_dev = ib_get_client_data(device, &sa_client);
|
2005-10-14 01:45:02 +08:00
|
|
|
struct ib_sa_port *port;
|
|
|
|
struct ib_mad_agent *agent;
|
2005-10-26 01:51:39 +08:00
|
|
|
struct ib_sa_mad *mad;
|
2017-04-28 07:06:02 +08:00
|
|
|
enum opa_pr_supported status;
|
2005-04-17 06:20:36 +08:00
|
|
|
int ret;
|
|
|
|
|
2005-10-14 01:45:02 +08:00
|
|
|
if (!sa_dev)
|
|
|
|
return -ENODEV;
|
|
|
|
|
2017-04-28 07:06:02 +08:00
|
|
|
if ((rec->rec_type != SA_PATH_REC_TYPE_IB) &&
|
|
|
|
(rec->rec_type != SA_PATH_REC_TYPE_OPA))
|
2017-04-28 07:05:59 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
2005-10-14 01:45:02 +08:00
|
|
|
port = &sa_dev->port[port_num - sa_dev->start_port];
|
|
|
|
agent = port->agent;
|
|
|
|
|
2015-08-14 20:52:08 +08:00
|
|
|
query = kzalloc(sizeof(*query), gfp_mask);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!query)
|
|
|
|
return -ENOMEM;
|
2005-10-26 01:51:39 +08:00
|
|
|
|
2007-06-19 02:03:58 +08:00
|
|
|
query->sa_query.port = port;
|
2017-04-28 07:06:02 +08:00
|
|
|
if (rec->rec_type == SA_PATH_REC_TYPE_OPA) {
|
2021-06-25 20:30:57 +08:00
|
|
|
status = opa_pr_query_possible(client, sa_dev, device, port_num);
|
2017-04-28 07:06:02 +08:00
|
|
|
if (status == PR_NOT_SUPPORTED) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto err1;
|
|
|
|
} else if (status == PR_OPA_SUPPORTED) {
|
|
|
|
query->sa_query.flags |= IB_SA_QUERY_OPA;
|
|
|
|
} else {
|
|
|
|
query->conv_pr =
|
|
|
|
kmalloc(sizeof(*query->conv_pr), gfp_mask);
|
|
|
|
if (!query->conv_pr) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto err1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-06-19 02:03:58 +08:00
|
|
|
ret = alloc_mad(&query->sa_query, gfp_mask);
|
|
|
|
if (ret)
|
2017-04-28 07:06:02 +08:00
|
|
|
goto err2;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-08-22 07:40:12 +08:00
|
|
|
ib_sa_client_get(client);
|
|
|
|
query->sa_query.client = client;
|
|
|
|
query->callback = callback;
|
|
|
|
query->context = context;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2005-10-26 01:51:39 +08:00
|
|
|
mad = query->sa_query.mad_buf->mad;
|
2017-04-29 07:20:26 +08:00
|
|
|
init_mad(&query->sa_query, agent);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2005-10-26 01:51:39 +08:00
|
|
|
query->sa_query.callback = callback ? ib_sa_path_rec_callback : NULL;
|
|
|
|
query->sa_query.release = ib_sa_path_rec_release;
|
|
|
|
mad->mad_hdr.method = IB_MGMT_METHOD_GET;
|
|
|
|
mad->mad_hdr.attr_id = cpu_to_be16(IB_SA_ATTR_PATH_REC);
|
|
|
|
mad->sa_hdr.comp_mask = comp_mask;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2017-04-28 07:06:02 +08:00
|
|
|
if (query->sa_query.flags & IB_SA_QUERY_OPA) {
|
|
|
|
ib_pack(opa_path_rec_table, ARRAY_SIZE(opa_path_rec_table),
|
|
|
|
rec, mad->data);
|
|
|
|
} else if (query->conv_pr) {
|
|
|
|
sa_convert_path_opa_to_ib(query->conv_pr, rec);
|
|
|
|
ib_pack(path_rec_table, ARRAY_SIZE(path_rec_table),
|
|
|
|
query->conv_pr, mad->data);
|
|
|
|
} else {
|
|
|
|
ib_pack(path_rec_table, ARRAY_SIZE(path_rec_table),
|
|
|
|
rec, mad->data);
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
*sa_query = &query->sa_query;
|
2005-06-28 05:36:46 +08:00
|
|
|
|
2015-08-14 20:52:09 +08:00
|
|
|
query->sa_query.flags |= IB_SA_ENABLE_LOCAL_SERVICE;
|
2017-04-28 07:06:02 +08:00
|
|
|
query->sa_query.mad_buf->context[1] = (query->conv_pr) ?
|
|
|
|
query->conv_pr : rec;
|
2015-08-14 20:52:09 +08:00
|
|
|
|
2006-07-14 15:23:56 +08:00
|
|
|
ret = send_mad(&query->sa_query, timeout_ms, gfp_mask);
|
2005-10-26 01:51:39 +08:00
|
|
|
if (ret < 0)
|
2017-04-28 07:06:02 +08:00
|
|
|
goto err3;
|
2005-10-26 01:51:39 +08:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
2017-04-28 07:06:02 +08:00
|
|
|
err3:
|
2005-10-26 01:51:39 +08:00
|
|
|
*sa_query = NULL;
|
2006-08-22 07:40:12 +08:00
|
|
|
ib_sa_client_put(query->sa_query.client);
|
2007-06-19 02:03:58 +08:00
|
|
|
free_mad(&query->sa_query);
|
2017-04-28 07:06:02 +08:00
|
|
|
err2:
|
|
|
|
kfree(query->conv_pr);
|
2005-10-26 01:51:39 +08:00
|
|
|
err1:
|
|
|
|
kfree(query);
|
2005-06-28 05:36:46 +08:00
|
|
|
return ret;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ib_sa_path_rec_get);
|
|
|
|
|
|
|
|
static void ib_sa_mcmember_rec_callback(struct ib_sa_query *sa_query,
|
2023-01-04 16:03:41 +08:00
|
|
|
int status, struct ib_sa_mad *mad)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct ib_sa_mcmember_query *query =
|
|
|
|
container_of(sa_query, struct ib_sa_mcmember_query, sa_query);
|
|
|
|
|
|
|
|
if (mad) {
|
|
|
|
struct ib_sa_mcmember_rec rec;
|
|
|
|
|
|
|
|
ib_unpack(mcmember_rec_table, ARRAY_SIZE(mcmember_rec_table),
|
|
|
|
mad->data, &rec);
|
|
|
|
query->callback(status, &rec, query->context);
|
|
|
|
} else
|
|
|
|
query->callback(status, NULL, query->context);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ib_sa_mcmember_rec_release(struct ib_sa_query *sa_query)
|
|
|
|
{
|
|
|
|
kfree(container_of(sa_query, struct ib_sa_mcmember_query, sa_query));
|
|
|
|
}
|
|
|
|
|
2006-08-22 07:40:12 +08:00
|
|
|
int ib_sa_mcmember_rec_query(struct ib_sa_client *client,
|
2021-03-01 15:04:20 +08:00
|
|
|
struct ib_device *device, u32 port_num,
|
2005-04-17 06:20:36 +08:00
|
|
|
u8 method,
|
|
|
|
struct ib_sa_mcmember_rec *rec,
|
|
|
|
ib_sa_comp_mask comp_mask,
|
2018-10-11 22:30:05 +08:00
|
|
|
unsigned long timeout_ms, gfp_t gfp_mask,
|
2005-04-17 06:20:36 +08:00
|
|
|
void (*callback)(int status,
|
|
|
|
struct ib_sa_mcmember_rec *resp,
|
|
|
|
void *context),
|
|
|
|
void *context,
|
|
|
|
struct ib_sa_query **sa_query)
|
|
|
|
{
|
|
|
|
struct ib_sa_mcmember_query *query;
|
|
|
|
struct ib_sa_device *sa_dev = ib_get_client_data(device, &sa_client);
|
2005-10-14 01:45:02 +08:00
|
|
|
struct ib_sa_port *port;
|
|
|
|
struct ib_mad_agent *agent;
|
2005-10-26 01:51:39 +08:00
|
|
|
struct ib_sa_mad *mad;
|
2005-04-17 06:20:36 +08:00
|
|
|
int ret;
|
|
|
|
|
2005-10-14 01:45:02 +08:00
|
|
|
if (!sa_dev)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
port = &sa_dev->port[port_num - sa_dev->start_port];
|
|
|
|
agent = port->agent;
|
|
|
|
|
2015-08-14 20:52:08 +08:00
|
|
|
query = kzalloc(sizeof(*query), gfp_mask);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!query)
|
|
|
|
return -ENOMEM;
|
2005-10-26 01:51:39 +08:00
|
|
|
|
2007-06-19 02:03:58 +08:00
|
|
|
query->sa_query.port = port;
|
|
|
|
ret = alloc_mad(&query->sa_query, gfp_mask);
|
|
|
|
if (ret)
|
2005-10-26 01:51:39 +08:00
|
|
|
goto err1;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-08-22 07:40:12 +08:00
|
|
|
ib_sa_client_get(client);
|
|
|
|
query->sa_query.client = client;
|
|
|
|
query->callback = callback;
|
|
|
|
query->context = context;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2005-10-26 01:51:39 +08:00
|
|
|
mad = query->sa_query.mad_buf->mad;
|
2017-04-29 07:20:26 +08:00
|
|
|
init_mad(&query->sa_query, agent);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2005-10-26 01:51:39 +08:00
|
|
|
query->sa_query.callback = callback ? ib_sa_mcmember_rec_callback : NULL;
|
|
|
|
query->sa_query.release = ib_sa_mcmember_rec_release;
|
|
|
|
mad->mad_hdr.method = method;
|
|
|
|
mad->mad_hdr.attr_id = cpu_to_be16(IB_SA_ATTR_MC_MEMBER_REC);
|
|
|
|
mad->sa_hdr.comp_mask = comp_mask;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
ib_pack(mcmember_rec_table, ARRAY_SIZE(mcmember_rec_table),
|
2005-10-26 01:51:39 +08:00
|
|
|
rec, mad->data);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
*sa_query = &query->sa_query;
|
2005-06-28 05:36:46 +08:00
|
|
|
|
2006-07-14 15:23:56 +08:00
|
|
|
ret = send_mad(&query->sa_query, timeout_ms, gfp_mask);
|
2005-10-26 01:51:39 +08:00
|
|
|
if (ret < 0)
|
|
|
|
goto err2;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2005-06-28 05:36:46 +08:00
|
|
|
return ret;
|
2005-10-26 01:51:39 +08:00
|
|
|
|
|
|
|
err2:
|
|
|
|
*sa_query = NULL;
|
2006-08-22 07:40:12 +08:00
|
|
|
ib_sa_client_put(query->sa_query.client);
|
2007-06-19 02:03:58 +08:00
|
|
|
free_mad(&query->sa_query);
|
2005-10-26 01:51:39 +08:00
|
|
|
|
|
|
|
err1:
|
|
|
|
kfree(query);
|
|
|
|
return ret;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2012-06-19 16:21:38 +08:00
|
|
|
/* Support GuidInfoRecord */
|
|
|
|
static void ib_sa_guidinfo_rec_callback(struct ib_sa_query *sa_query,
|
2023-01-04 16:03:41 +08:00
|
|
|
int status, struct ib_sa_mad *mad)
|
2012-06-19 16:21:38 +08:00
|
|
|
{
|
|
|
|
struct ib_sa_guidinfo_query *query =
|
|
|
|
container_of(sa_query, struct ib_sa_guidinfo_query, sa_query);
|
|
|
|
|
|
|
|
if (mad) {
|
|
|
|
struct ib_sa_guidinfo_rec rec;
|
|
|
|
|
|
|
|
ib_unpack(guidinfo_rec_table, ARRAY_SIZE(guidinfo_rec_table),
|
|
|
|
mad->data, &rec);
|
|
|
|
query->callback(status, &rec, query->context);
|
|
|
|
} else
|
|
|
|
query->callback(status, NULL, query->context);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ib_sa_guidinfo_rec_release(struct ib_sa_query *sa_query)
|
|
|
|
{
|
|
|
|
kfree(container_of(sa_query, struct ib_sa_guidinfo_query, sa_query));
|
|
|
|
}
|
|
|
|
|
|
|
|
int ib_sa_guid_info_rec_query(struct ib_sa_client *client,
|
2021-03-01 15:04:20 +08:00
|
|
|
struct ib_device *device, u32 port_num,
|
2012-06-19 16:21:38 +08:00
|
|
|
struct ib_sa_guidinfo_rec *rec,
|
|
|
|
ib_sa_comp_mask comp_mask, u8 method,
|
2018-10-11 22:30:05 +08:00
|
|
|
unsigned long timeout_ms, gfp_t gfp_mask,
|
2012-06-19 16:21:38 +08:00
|
|
|
void (*callback)(int status,
|
|
|
|
struct ib_sa_guidinfo_rec *resp,
|
|
|
|
void *context),
|
|
|
|
void *context,
|
|
|
|
struct ib_sa_query **sa_query)
|
|
|
|
{
|
|
|
|
struct ib_sa_guidinfo_query *query;
|
|
|
|
struct ib_sa_device *sa_dev = ib_get_client_data(device, &sa_client);
|
|
|
|
struct ib_sa_port *port;
|
|
|
|
struct ib_mad_agent *agent;
|
|
|
|
struct ib_sa_mad *mad;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!sa_dev)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
if (method != IB_MGMT_METHOD_GET &&
|
|
|
|
method != IB_MGMT_METHOD_SET &&
|
|
|
|
method != IB_SA_METHOD_DELETE) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
port = &sa_dev->port[port_num - sa_dev->start_port];
|
|
|
|
agent = port->agent;
|
|
|
|
|
2015-08-14 20:52:08 +08:00
|
|
|
query = kzalloc(sizeof(*query), gfp_mask);
|
2012-06-19 16:21:38 +08:00
|
|
|
if (!query)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
query->sa_query.port = port;
|
|
|
|
ret = alloc_mad(&query->sa_query, gfp_mask);
|
|
|
|
if (ret)
|
|
|
|
goto err1;
|
|
|
|
|
|
|
|
ib_sa_client_get(client);
|
|
|
|
query->sa_query.client = client;
|
|
|
|
query->callback = callback;
|
|
|
|
query->context = context;
|
|
|
|
|
|
|
|
mad = query->sa_query.mad_buf->mad;
|
2017-04-29 07:20:26 +08:00
|
|
|
init_mad(&query->sa_query, agent);
|
2012-06-19 16:21:38 +08:00
|
|
|
|
|
|
|
query->sa_query.callback = callback ? ib_sa_guidinfo_rec_callback : NULL;
|
|
|
|
query->sa_query.release = ib_sa_guidinfo_rec_release;
|
|
|
|
|
|
|
|
mad->mad_hdr.method = method;
|
|
|
|
mad->mad_hdr.attr_id = cpu_to_be16(IB_SA_ATTR_GUID_INFO_REC);
|
|
|
|
mad->sa_hdr.comp_mask = comp_mask;
|
|
|
|
|
|
|
|
ib_pack(guidinfo_rec_table, ARRAY_SIZE(guidinfo_rec_table), rec,
|
|
|
|
mad->data);
|
|
|
|
|
|
|
|
*sa_query = &query->sa_query;
|
|
|
|
|
|
|
|
ret = send_mad(&query->sa_query, timeout_ms, gfp_mask);
|
|
|
|
if (ret < 0)
|
|
|
|
goto err2;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
err2:
|
|
|
|
*sa_query = NULL;
|
|
|
|
ib_sa_client_put(query->sa_query.client);
|
|
|
|
free_mad(&query->sa_query);
|
|
|
|
|
|
|
|
err1:
|
|
|
|
kfree(query);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ib_sa_guid_info_rec_query);
|
|
|
|
|
2017-03-21 07:38:08 +08:00
|
|
|
struct ib_classport_info_context {
|
|
|
|
struct completion done;
|
|
|
|
struct ib_sa_query *sa_query;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void ib_classportinfo_cb(void *context)
|
|
|
|
{
|
|
|
|
struct ib_classport_info_context *cb_ctx = context;
|
|
|
|
|
|
|
|
complete(&cb_ctx->done);
|
|
|
|
}
|
|
|
|
|
2016-05-26 03:02:05 +08:00
|
|
|
static void ib_sa_classport_info_rec_callback(struct ib_sa_query *sa_query,
|
2023-01-04 16:03:41 +08:00
|
|
|
int status, struct ib_sa_mad *mad)
|
2016-05-26 03:02:05 +08:00
|
|
|
{
|
2016-07-06 21:36:34 +08:00
|
|
|
unsigned long flags;
|
2016-05-26 03:02:05 +08:00
|
|
|
struct ib_sa_classport_info_query *query =
|
|
|
|
container_of(sa_query, struct ib_sa_classport_info_query, sa_query);
|
2017-04-29 07:20:26 +08:00
|
|
|
struct ib_sa_classport_cache *info = &sa_query->port->classport_info;
|
2016-05-26 03:02:05 +08:00
|
|
|
|
|
|
|
if (mad) {
|
2017-04-29 07:20:26 +08:00
|
|
|
if (sa_query->flags & IB_SA_QUERY_OPA) {
|
|
|
|
struct opa_class_port_info rec;
|
2016-05-26 03:02:05 +08:00
|
|
|
|
2017-04-29 07:20:26 +08:00
|
|
|
ib_unpack(opa_classport_info_rec_table,
|
|
|
|
ARRAY_SIZE(opa_classport_info_rec_table),
|
|
|
|
mad->data, &rec);
|
|
|
|
|
|
|
|
spin_lock_irqsave(&sa_query->port->classport_lock,
|
|
|
|
flags);
|
|
|
|
if (!status && !info->valid) {
|
|
|
|
memcpy(&info->data.opa, &rec,
|
|
|
|
sizeof(info->data.opa));
|
|
|
|
|
|
|
|
info->valid = true;
|
|
|
|
info->data.type = RDMA_CLASS_PORT_INFO_OPA;
|
|
|
|
}
|
|
|
|
spin_unlock_irqrestore(&sa_query->port->classport_lock,
|
|
|
|
flags);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
struct ib_class_port_info rec;
|
2016-07-06 21:36:34 +08:00
|
|
|
|
2017-04-29 07:20:26 +08:00
|
|
|
ib_unpack(ib_classport_info_rec_table,
|
|
|
|
ARRAY_SIZE(ib_classport_info_rec_table),
|
|
|
|
mad->data, &rec);
|
2016-07-06 21:36:34 +08:00
|
|
|
|
2017-04-29 07:20:26 +08:00
|
|
|
spin_lock_irqsave(&sa_query->port->classport_lock,
|
|
|
|
flags);
|
|
|
|
if (!status && !info->valid) {
|
|
|
|
memcpy(&info->data.ib, &rec,
|
|
|
|
sizeof(info->data.ib));
|
|
|
|
|
|
|
|
info->valid = true;
|
|
|
|
info->data.type = RDMA_CLASS_PORT_INFO_IB;
|
|
|
|
}
|
|
|
|
spin_unlock_irqrestore(&sa_query->port->classport_lock,
|
|
|
|
flags);
|
2016-07-06 21:36:34 +08:00
|
|
|
}
|
2016-05-26 03:02:05 +08:00
|
|
|
}
|
2017-03-21 07:38:08 +08:00
|
|
|
query->callback(query->context);
|
2016-05-26 03:02:05 +08:00
|
|
|
}
|
|
|
|
|
2017-03-21 07:38:08 +08:00
|
|
|
static void ib_sa_classport_info_rec_release(struct ib_sa_query *sa_query)
|
2016-05-26 03:02:05 +08:00
|
|
|
{
|
|
|
|
kfree(container_of(sa_query, struct ib_sa_classport_info_query,
|
|
|
|
sa_query));
|
|
|
|
}
|
|
|
|
|
2017-03-21 07:38:08 +08:00
|
|
|
static int ib_sa_classport_info_rec_query(struct ib_sa_port *port,
|
2018-10-11 22:30:05 +08:00
|
|
|
unsigned long timeout_ms,
|
2017-03-21 07:38:08 +08:00
|
|
|
void (*callback)(void *context),
|
|
|
|
void *context,
|
|
|
|
struct ib_sa_query **sa_query)
|
2016-05-26 03:02:05 +08:00
|
|
|
{
|
|
|
|
struct ib_mad_agent *agent;
|
2017-03-21 07:38:08 +08:00
|
|
|
struct ib_sa_classport_info_query *query;
|
2016-05-26 03:02:05 +08:00
|
|
|
struct ib_sa_mad *mad;
|
2017-03-21 07:38:08 +08:00
|
|
|
gfp_t gfp_mask = GFP_KERNEL;
|
2016-05-26 03:02:05 +08:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
agent = port->agent;
|
|
|
|
|
|
|
|
query = kzalloc(sizeof(*query), gfp_mask);
|
|
|
|
if (!query)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
query->sa_query.port = port;
|
2017-04-29 07:20:26 +08:00
|
|
|
query->sa_query.flags |= rdma_cap_opa_ah(port->agent->device,
|
|
|
|
port->port_num) ?
|
|
|
|
IB_SA_QUERY_OPA : 0;
|
2016-05-26 03:02:05 +08:00
|
|
|
ret = alloc_mad(&query->sa_query, gfp_mask);
|
|
|
|
if (ret)
|
2017-03-21 07:38:08 +08:00
|
|
|
goto err_free;
|
2016-05-26 03:02:05 +08:00
|
|
|
|
2017-03-21 07:38:08 +08:00
|
|
|
query->callback = callback;
|
|
|
|
query->context = context;
|
2016-05-26 03:02:05 +08:00
|
|
|
|
|
|
|
mad = query->sa_query.mad_buf->mad;
|
2017-04-29 07:20:26 +08:00
|
|
|
init_mad(&query->sa_query, agent);
|
2016-05-26 03:02:05 +08:00
|
|
|
|
2017-03-21 07:38:08 +08:00
|
|
|
query->sa_query.callback = ib_sa_classport_info_rec_callback;
|
|
|
|
query->sa_query.release = ib_sa_classport_info_rec_release;
|
2016-05-26 03:02:05 +08:00
|
|
|
mad->mad_hdr.method = IB_MGMT_METHOD_GET;
|
|
|
|
mad->mad_hdr.attr_id = cpu_to_be16(IB_SA_ATTR_CLASS_PORTINFO);
|
|
|
|
mad->sa_hdr.comp_mask = 0;
|
|
|
|
*sa_query = &query->sa_query;
|
|
|
|
|
|
|
|
ret = send_mad(&query->sa_query, timeout_ms, gfp_mask);
|
|
|
|
if (ret < 0)
|
2017-03-21 07:38:08 +08:00
|
|
|
goto err_free_mad;
|
2016-05-26 03:02:05 +08:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
2017-03-21 07:38:08 +08:00
|
|
|
err_free_mad:
|
2016-05-26 03:02:05 +08:00
|
|
|
*sa_query = NULL;
|
|
|
|
free_mad(&query->sa_query);
|
|
|
|
|
2017-03-21 07:38:08 +08:00
|
|
|
err_free:
|
2016-05-26 03:02:05 +08:00
|
|
|
kfree(query);
|
|
|
|
return ret;
|
|
|
|
}
|
2017-03-21 07:38:08 +08:00
|
|
|
|
|
|
|
static void update_ib_cpi(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct ib_sa_port *port =
|
|
|
|
container_of(work, struct ib_sa_port, ib_cpi_work.work);
|
|
|
|
struct ib_classport_info_context *cb_context;
|
|
|
|
unsigned long flags;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* If the classport info is valid, nothing
|
|
|
|
* to do here.
|
|
|
|
*/
|
|
|
|
spin_lock_irqsave(&port->classport_lock, flags);
|
|
|
|
if (port->classport_info.valid) {
|
|
|
|
spin_unlock_irqrestore(&port->classport_lock, flags);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
spin_unlock_irqrestore(&port->classport_lock, flags);
|
|
|
|
|
|
|
|
cb_context = kmalloc(sizeof(*cb_context), GFP_KERNEL);
|
|
|
|
if (!cb_context)
|
|
|
|
goto err_nomem;
|
|
|
|
|
|
|
|
init_completion(&cb_context->done);
|
|
|
|
|
|
|
|
ret = ib_sa_classport_info_rec_query(port, 3000,
|
|
|
|
ib_classportinfo_cb, cb_context,
|
|
|
|
&cb_context->sa_query);
|
|
|
|
if (ret < 0)
|
|
|
|
goto free_cb_err;
|
|
|
|
wait_for_completion(&cb_context->done);
|
|
|
|
free_cb_err:
|
|
|
|
kfree(cb_context);
|
|
|
|
spin_lock_irqsave(&port->classport_lock, flags);
|
|
|
|
|
|
|
|
/* If the classport info is still not valid, the query should have
|
|
|
|
* failed for some reason. Retry issuing the query
|
|
|
|
*/
|
|
|
|
if (!port->classport_info.valid) {
|
|
|
|
port->classport_info.retry_cnt++;
|
|
|
|
if (port->classport_info.retry_cnt <=
|
|
|
|
IB_SA_CPI_MAX_RETRY_CNT) {
|
|
|
|
unsigned long delay =
|
|
|
|
msecs_to_jiffies(IB_SA_CPI_RETRY_WAIT);
|
|
|
|
|
|
|
|
queue_delayed_work(ib_wq, &port->ib_cpi_work, delay);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
spin_unlock_irqrestore(&port->classport_lock, flags);
|
|
|
|
|
|
|
|
err_nomem:
|
|
|
|
return;
|
|
|
|
}
|
2016-05-26 03:02:05 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static void send_handler(struct ib_mad_agent *agent,
|
|
|
|
struct ib_mad_send_wc *mad_send_wc)
|
|
|
|
{
|
2005-10-26 01:51:39 +08:00
|
|
|
struct ib_sa_query *query = mad_send_wc->send_buf->context[0];
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned long flags;
|
|
|
|
|
2005-05-26 03:31:29 +08:00
|
|
|
if (query->callback)
|
|
|
|
switch (mad_send_wc->status) {
|
|
|
|
case IB_WC_SUCCESS:
|
|
|
|
/* No callback -- already got recv */
|
|
|
|
break;
|
|
|
|
case IB_WC_RESP_TIMEOUT_ERR:
|
2023-01-04 16:03:41 +08:00
|
|
|
query->callback(query, -ETIMEDOUT, NULL);
|
2005-05-26 03:31:29 +08:00
|
|
|
break;
|
|
|
|
case IB_WC_WR_FLUSH_ERR:
|
2023-01-04 16:03:41 +08:00
|
|
|
query->callback(query, -EINTR, NULL);
|
2005-05-26 03:31:29 +08:00
|
|
|
break;
|
|
|
|
default:
|
2023-01-04 16:03:41 +08:00
|
|
|
query->callback(query, -EIO, NULL);
|
2005-05-26 03:31:29 +08:00
|
|
|
break;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2019-02-21 08:20:46 +08:00
|
|
|
xa_lock_irqsave(&queries, flags);
|
|
|
|
__xa_erase(&queries, query->id);
|
|
|
|
xa_unlock_irqrestore(&queries, flags);
|
2005-10-26 01:51:39 +08:00
|
|
|
|
2007-06-19 02:03:58 +08:00
|
|
|
free_mad(query);
|
2017-03-21 07:38:08 +08:00
|
|
|
if (query->client)
|
|
|
|
ib_sa_client_put(query->client);
|
2005-10-26 01:51:39 +08:00
|
|
|
query->release(query);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void recv_handler(struct ib_mad_agent *mad_agent,
|
2016-01-04 21:15:58 +08:00
|
|
|
struct ib_mad_send_buf *send_buf,
|
2005-04-17 06:20:36 +08:00
|
|
|
struct ib_mad_recv_wc *mad_recv_wc)
|
|
|
|
{
|
|
|
|
struct ib_sa_query *query;
|
|
|
|
|
2016-01-04 21:15:58 +08:00
|
|
|
if (!send_buf)
|
|
|
|
return;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2016-01-04 21:15:58 +08:00
|
|
|
query = send_buf->context[0];
|
2005-10-26 01:51:39 +08:00
|
|
|
if (query->callback) {
|
2005-04-17 06:20:36 +08:00
|
|
|
if (mad_recv_wc->wc->status == IB_WC_SUCCESS)
|
|
|
|
query->callback(query,
|
|
|
|
mad_recv_wc->recv_buf.mad->mad_hdr.status ?
|
2023-01-04 16:03:41 +08:00
|
|
|
-EINVAL : 0,
|
2005-04-17 06:20:36 +08:00
|
|
|
(struct ib_sa_mad *) mad_recv_wc->recv_buf.mad);
|
|
|
|
else
|
2023-01-04 16:03:41 +08:00
|
|
|
query->callback(query, -EIO, NULL);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
ib_free_recv_mad(mad_recv_wc);
|
|
|
|
}
|
|
|
|
|
2017-03-21 07:38:07 +08:00
|
|
|
static void update_sm_ah(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct ib_sa_port *port =
|
|
|
|
container_of(work, struct ib_sa_port, update_task);
|
|
|
|
struct ib_sa_sm_ah *new_ah;
|
|
|
|
struct ib_port_attr port_attr;
|
2017-04-30 02:41:18 +08:00
|
|
|
struct rdma_ah_attr ah_attr;
|
2018-07-04 20:57:50 +08:00
|
|
|
bool grh_required;
|
2017-03-21 07:38:07 +08:00
|
|
|
|
|
|
|
if (ib_query_port(port->agent->device, port->port_num, &port_attr)) {
|
|
|
|
pr_warn("Couldn't query port\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
new_ah = kmalloc(sizeof(*new_ah), GFP_KERNEL);
|
|
|
|
if (!new_ah)
|
|
|
|
return;
|
|
|
|
|
|
|
|
kref_init(&new_ah->ref);
|
|
|
|
new_ah->src_path_mask = (1 << port_attr.lmc) - 1;
|
|
|
|
|
|
|
|
new_ah->pkey_index = 0;
|
|
|
|
if (ib_find_pkey(port->agent->device, port->port_num,
|
|
|
|
IB_DEFAULT_PKEY_FULL, &new_ah->pkey_index))
|
|
|
|
pr_err("Couldn't find index for default PKey\n");
|
|
|
|
|
|
|
|
memset(&ah_attr, 0, sizeof(ah_attr));
|
2017-04-30 02:41:29 +08:00
|
|
|
ah_attr.type = rdma_ah_find_type(port->agent->device,
|
|
|
|
port->port_num);
|
2017-04-30 02:41:28 +08:00
|
|
|
rdma_ah_set_dlid(&ah_attr, port_attr.sm_lid);
|
|
|
|
rdma_ah_set_sl(&ah_attr, port_attr.sm_sl);
|
|
|
|
rdma_ah_set_port_num(&ah_attr, port->port_num);
|
2018-07-04 20:57:49 +08:00
|
|
|
|
2018-07-04 20:57:50 +08:00
|
|
|
grh_required = rdma_is_grh_required(port->agent->device,
|
|
|
|
port->port_num);
|
|
|
|
|
2018-07-04 20:57:49 +08:00
|
|
|
/*
|
|
|
|
* The OPA sm_lid of 0xFFFF needs special handling so that it can be
|
|
|
|
* differentiated from a permissive LID of 0xFFFF. We set the
|
|
|
|
* grh_required flag here so the SA can program the DGID in the
|
|
|
|
* address handle appropriately
|
|
|
|
*/
|
|
|
|
if (ah_attr.type == RDMA_AH_ATTR_TYPE_OPA &&
|
2018-07-04 20:57:50 +08:00
|
|
|
(grh_required ||
|
2018-07-04 20:57:49 +08:00
|
|
|
port_attr.sm_lid == be16_to_cpu(IB_LID_PERMISSIVE)))
|
|
|
|
rdma_ah_set_make_grd(&ah_attr, true);
|
|
|
|
|
2018-07-04 20:57:50 +08:00
|
|
|
if (ah_attr.type == RDMA_AH_ATTR_TYPE_IB && grh_required) {
|
2018-07-04 20:57:49 +08:00
|
|
|
rdma_ah_set_ah_flags(&ah_attr, IB_AH_GRH);
|
|
|
|
rdma_ah_set_subnet_prefix(&ah_attr,
|
|
|
|
cpu_to_be64(port_attr.subnet_prefix));
|
|
|
|
rdma_ah_set_interface_id(&ah_attr,
|
|
|
|
cpu_to_be64(IB_SA_WELL_KNOWN_GUID));
|
2017-03-21 07:38:07 +08:00
|
|
|
}
|
|
|
|
|
2018-12-12 17:09:05 +08:00
|
|
|
new_ah->ah = rdma_create_ah(port->agent->qp->pd, &ah_attr,
|
|
|
|
RDMA_CREATE_AH_SLEEPABLE);
|
2017-03-21 07:38:07 +08:00
|
|
|
if (IS_ERR(new_ah->ah)) {
|
|
|
|
pr_warn("Couldn't create new SM AH\n");
|
|
|
|
kfree(new_ah);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
spin_lock_irq(&port->ah_lock);
|
|
|
|
if (port->sm_ah)
|
|
|
|
kref_put(&port->sm_ah->ref, free_sm_ah);
|
|
|
|
port->sm_ah = new_ah;
|
|
|
|
spin_unlock_irq(&port->ah_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ib_sa_event(struct ib_event_handler *handler,
|
|
|
|
struct ib_event *event)
|
|
|
|
{
|
|
|
|
if (event->event == IB_EVENT_PORT_ERR ||
|
|
|
|
event->event == IB_EVENT_PORT_ACTIVE ||
|
|
|
|
event->event == IB_EVENT_LID_CHANGE ||
|
|
|
|
event->event == IB_EVENT_PKEY_CHANGE ||
|
|
|
|
event->event == IB_EVENT_SM_CHANGE ||
|
|
|
|
event->event == IB_EVENT_CLIENT_REREGISTER) {
|
|
|
|
unsigned long flags;
|
|
|
|
struct ib_sa_device *sa_dev =
|
|
|
|
container_of(handler, typeof(*sa_dev), event_handler);
|
2021-03-01 15:04:20 +08:00
|
|
|
u32 port_num = event->element.port_num - sa_dev->start_port;
|
2017-03-21 07:38:07 +08:00
|
|
|
struct ib_sa_port *port = &sa_dev->port[port_num];
|
|
|
|
|
|
|
|
if (!rdma_cap_ib_sa(handler->device, port->port_num))
|
|
|
|
return;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&port->ah_lock, flags);
|
|
|
|
if (port->sm_ah)
|
|
|
|
kref_put(&port->sm_ah->ref, free_sm_ah);
|
|
|
|
port->sm_ah = NULL;
|
|
|
|
spin_unlock_irqrestore(&port->ah_lock, flags);
|
|
|
|
|
|
|
|
if (event->event == IB_EVENT_SM_CHANGE ||
|
|
|
|
event->event == IB_EVENT_CLIENT_REREGISTER ||
|
2017-03-21 07:38:08 +08:00
|
|
|
event->event == IB_EVENT_LID_CHANGE ||
|
|
|
|
event->event == IB_EVENT_PORT_ACTIVE) {
|
|
|
|
unsigned long delay =
|
|
|
|
msecs_to_jiffies(IB_SA_CPI_RETRY_WAIT);
|
|
|
|
|
2017-03-21 07:38:07 +08:00
|
|
|
spin_lock_irqsave(&port->classport_lock, flags);
|
|
|
|
port->classport_info.valid = false;
|
2017-03-21 07:38:08 +08:00
|
|
|
port->classport_info.retry_cnt = 0;
|
2017-03-21 07:38:07 +08:00
|
|
|
spin_unlock_irqrestore(&port->classport_lock, flags);
|
2017-03-21 07:38:08 +08:00
|
|
|
queue_delayed_work(ib_wq,
|
|
|
|
&port->ib_cpi_work, delay);
|
2017-03-21 07:38:07 +08:00
|
|
|
}
|
|
|
|
queue_work(ib_wq, &sa_dev->port[port_num].update_task);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-22 01:24:40 +08:00
|
|
|
static int ib_sa_add_one(struct ib_device *device)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct ib_sa_device *sa_dev;
|
|
|
|
int s, e, i;
|
2015-05-05 20:50:22 +08:00
|
|
|
int count = 0;
|
2020-04-22 01:24:40 +08:00
|
|
|
int ret;
|
2006-08-04 05:02:42 +08:00
|
|
|
|
2015-06-29 21:57:00 +08:00
|
|
|
s = rdma_start_port(device);
|
|
|
|
e = rdma_end_port(device);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2023-09-18 05:21:36 +08:00
|
|
|
sa_dev = kzalloc(struct_size(sa_dev, port,
|
|
|
|
size_add(size_sub(e, s), 1)),
|
|
|
|
GFP_KERNEL);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!sa_dev)
|
2020-04-22 01:24:40 +08:00
|
|
|
return -ENOMEM;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
sa_dev->start_port = s;
|
|
|
|
sa_dev->end_port = e;
|
|
|
|
|
|
|
|
for (i = 0; i <= e - s; ++i) {
|
2010-09-28 08:51:11 +08:00
|
|
|
spin_lock_init(&sa_dev->port[i].ah_lock);
|
2015-05-05 20:50:36 +08:00
|
|
|
if (!rdma_cap_ib_sa(device, i + 1))
|
2010-09-28 08:51:11 +08:00
|
|
|
continue;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
sa_dev->port[i].sm_ah = NULL;
|
|
|
|
sa_dev->port[i].port_num = i + s;
|
|
|
|
|
2016-07-06 21:36:34 +08:00
|
|
|
spin_lock_init(&sa_dev->port[i].classport_lock);
|
|
|
|
sa_dev->port[i].classport_info.valid = false;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
sa_dev->port[i].agent =
|
|
|
|
ib_register_mad_agent(device, i + s, IB_QPT_GSI,
|
|
|
|
NULL, 0, send_handler,
|
2014-08-09 07:00:55 +08:00
|
|
|
recv_handler, sa_dev, 0);
|
2020-04-22 01:24:40 +08:00
|
|
|
if (IS_ERR(sa_dev->port[i].agent)) {
|
|
|
|
ret = PTR_ERR(sa_dev->port[i].agent);
|
2005-04-17 06:20:36 +08:00
|
|
|
goto err;
|
2020-04-22 01:24:40 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-11-22 22:57:56 +08:00
|
|
|
INIT_WORK(&sa_dev->port[i].update_task, update_sm_ah);
|
2017-03-21 07:38:08 +08:00
|
|
|
INIT_DELAYED_WORK(&sa_dev->port[i].ib_cpi_work,
|
|
|
|
update_ib_cpi);
|
2015-05-05 20:50:22 +08:00
|
|
|
|
|
|
|
count++;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2020-04-22 01:24:40 +08:00
|
|
|
if (!count) {
|
|
|
|
ret = -EOPNOTSUPP;
|
2015-05-05 20:50:22 +08:00
|
|
|
goto free;
|
2020-04-22 01:24:40 +08:00
|
|
|
}
|
2015-05-05 20:50:22 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
ib_set_client_data(device, &sa_client, sa_dev);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We register our event handler after everything is set up,
|
|
|
|
* and then update our cached info after the event handler is
|
|
|
|
* registered to avoid any problems if a port changes state
|
|
|
|
* during our initialization.
|
|
|
|
*/
|
|
|
|
|
|
|
|
INIT_IB_EVENT_HANDLER(&sa_dev->event_handler, device, ib_sa_event);
|
2017-08-17 20:50:36 +08:00
|
|
|
ib_register_event_handler(&sa_dev->event_handler);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2015-05-05 20:50:22 +08:00
|
|
|
for (i = 0; i <= e - s; ++i) {
|
2015-05-05 20:50:36 +08:00
|
|
|
if (rdma_cap_ib_sa(device, i + 1))
|
2010-09-28 08:51:11 +08:00
|
|
|
update_sm_ah(&sa_dev->port[i].update_task);
|
2015-05-05 20:50:22 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2020-04-22 01:24:40 +08:00
|
|
|
return 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
err:
|
2015-05-05 20:50:22 +08:00
|
|
|
while (--i >= 0) {
|
2015-05-05 20:50:36 +08:00
|
|
|
if (rdma_cap_ib_sa(device, i + 1))
|
2010-09-28 08:51:11 +08:00
|
|
|
ib_unregister_mad_agent(sa_dev->port[i].agent);
|
2015-05-05 20:50:22 +08:00
|
|
|
}
|
|
|
|
free:
|
2005-04-17 06:20:36 +08:00
|
|
|
kfree(sa_dev);
|
2020-04-22 01:24:40 +08:00
|
|
|
return ret;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2015-07-30 22:50:14 +08:00
|
|
|
static void ib_sa_remove_one(struct ib_device *device, void *client_data)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2015-07-30 22:50:14 +08:00
|
|
|
struct ib_sa_device *sa_dev = client_data;
|
2005-04-17 06:20:36 +08:00
|
|
|
int i;
|
|
|
|
|
|
|
|
ib_unregister_event_handler(&sa_dev->event_handler);
|
2011-01-24 19:06:54 +08:00
|
|
|
flush_workqueue(ib_wq);
|
2006-01-18 01:53:51 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
for (i = 0; i <= sa_dev->end_port - sa_dev->start_port; ++i) {
|
2015-05-05 20:50:36 +08:00
|
|
|
if (rdma_cap_ib_sa(device, i + 1)) {
|
2017-03-21 07:38:08 +08:00
|
|
|
cancel_delayed_work_sync(&sa_dev->port[i].ib_cpi_work);
|
2010-09-28 08:51:11 +08:00
|
|
|
ib_unregister_mad_agent(sa_dev->port[i].agent);
|
|
|
|
if (sa_dev->port[i].sm_ah)
|
|
|
|
kref_put(&sa_dev->port[i].sm_ah->ref, free_sm_ah);
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
kfree(sa_dev);
|
|
|
|
}
|
|
|
|
|
2016-05-19 22:12:33 +08:00
|
|
|
int ib_sa_init(void)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
get_random_bytes(&tid, sizeof tid);
|
|
|
|
|
2015-08-14 20:52:09 +08:00
|
|
|
atomic_set(&ib_nl_sa_request_seq, 0);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
ret = ib_register_client(&sa_client);
|
2007-02-16 09:00:17 +08:00
|
|
|
if (ret) {
|
2016-03-02 03:20:29 +08:00
|
|
|
pr_err("Couldn't register ib_sa client\n");
|
2007-02-16 09:00:17 +08:00
|
|
|
goto err1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = mcast_init();
|
|
|
|
if (ret) {
|
2016-03-02 03:20:29 +08:00
|
|
|
pr_err("Couldn't initialize multicast handling\n");
|
2007-02-16 09:00:17 +08:00
|
|
|
goto err2;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2016-08-16 01:57:46 +08:00
|
|
|
ib_nl_wq = alloc_ordered_workqueue("ib_nl_sa_wq", WQ_MEM_RECLAIM);
|
2015-08-14 20:52:09 +08:00
|
|
|
if (!ib_nl_wq) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto err3;
|
|
|
|
}
|
|
|
|
|
|
|
|
INIT_DELAYED_WORK(&ib_nl_timed_work, ib_nl_request_timeout);
|
|
|
|
|
2007-02-16 09:00:17 +08:00
|
|
|
return 0;
|
2016-05-19 22:12:35 +08:00
|
|
|
|
2015-08-14 20:52:09 +08:00
|
|
|
err3:
|
|
|
|
mcast_cleanup();
|
2007-02-16 09:00:17 +08:00
|
|
|
err2:
|
|
|
|
ib_unregister_client(&sa_client);
|
|
|
|
err1:
|
2005-04-17 06:20:36 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-05-19 22:12:33 +08:00
|
|
|
void ib_sa_cleanup(void)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2015-08-14 20:52:09 +08:00
|
|
|
cancel_delayed_work(&ib_nl_timed_work);
|
|
|
|
destroy_workqueue(ib_nl_wq);
|
2007-02-16 09:00:17 +08:00
|
|
|
mcast_cleanup();
|
2005-04-17 06:20:36 +08:00
|
|
|
ib_unregister_client(&sa_client);
|
2019-02-21 08:20:46 +08:00
|
|
|
WARN_ON(!xa_empty(&queries));
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|