162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright (c) 2017 Mellanox Technologies. All rights reserved.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or without
562306a36Sopenharmony_ci * modification, are permitted provided that the following conditions are met:
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright
862306a36Sopenharmony_ci *    notice, this list of conditions and the following disclaimer.
962306a36Sopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright
1062306a36Sopenharmony_ci *    notice, this list of conditions and the following disclaimer in the
1162306a36Sopenharmony_ci *    documentation and/or other materials provided with the distribution.
1262306a36Sopenharmony_ci * 3. Neither the names of the copyright holders nor the names of its
1362306a36Sopenharmony_ci *    contributors may be used to endorse or promote products derived from
1462306a36Sopenharmony_ci *    this software without specific prior written permission.
1562306a36Sopenharmony_ci *
1662306a36Sopenharmony_ci * Alternatively, this software may be distributed under the terms of the
1762306a36Sopenharmony_ci * GNU General Public License ("GPL") version 2 as published by the Free
1862306a36Sopenharmony_ci * Software Foundation.
1962306a36Sopenharmony_ci *
2062306a36Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
2162306a36Sopenharmony_ci * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2262306a36Sopenharmony_ci * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2362306a36Sopenharmony_ci * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
2462306a36Sopenharmony_ci * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2562306a36Sopenharmony_ci * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2662306a36Sopenharmony_ci * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2762306a36Sopenharmony_ci * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2862306a36Sopenharmony_ci * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2962306a36Sopenharmony_ci * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3062306a36Sopenharmony_ci * POSSIBILITY OF SUCH DAMAGE.
3162306a36Sopenharmony_ci */
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#include <linux/module.h>
3462306a36Sopenharmony_ci#include <linux/pid.h>
3562306a36Sopenharmony_ci#include <linux/pid_namespace.h>
3662306a36Sopenharmony_ci#include <linux/mutex.h>
3762306a36Sopenharmony_ci#include <net/netlink.h>
3862306a36Sopenharmony_ci#include <rdma/rdma_cm.h>
3962306a36Sopenharmony_ci#include <rdma/rdma_netlink.h>
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#include "core_priv.h"
4262306a36Sopenharmony_ci#include "cma_priv.h"
4362306a36Sopenharmony_ci#include "restrack.h"
4462306a36Sopenharmony_ci#include "uverbs.h"
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_citypedef int (*res_fill_func_t)(struct sk_buff*, bool,
4762306a36Sopenharmony_ci			       struct rdma_restrack_entry*, uint32_t);
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci/*
5062306a36Sopenharmony_ci * Sort array elements by the netlink attribute name
5162306a36Sopenharmony_ci */
5262306a36Sopenharmony_cistatic const struct nla_policy nldev_policy[RDMA_NLDEV_ATTR_MAX] = {
5362306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_CHARDEV]		= { .type = NLA_U64 },
5462306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_CHARDEV_ABI]		= { .type = NLA_U64 },
5562306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_CHARDEV_NAME]		= { .type = NLA_NUL_STRING,
5662306a36Sopenharmony_ci					.len = RDMA_NLDEV_ATTR_EMPTY_STRING },
5762306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_CHARDEV_TYPE]		= { .type = NLA_NUL_STRING,
5862306a36Sopenharmony_ci					.len = RDMA_NLDEV_ATTR_CHARDEV_TYPE_SIZE },
5962306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_DEV_DIM]               = { .type = NLA_U8 },
6062306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_DEV_INDEX]		= { .type = NLA_U32 },
6162306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_DEV_NAME]		= { .type = NLA_NUL_STRING,
6262306a36Sopenharmony_ci					.len = IB_DEVICE_NAME_MAX },
6362306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_DEV_NODE_TYPE]		= { .type = NLA_U8 },
6462306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_DEV_PROTOCOL]		= { .type = NLA_NUL_STRING,
6562306a36Sopenharmony_ci					.len = RDMA_NLDEV_ATTR_EMPTY_STRING },
6662306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_DRIVER]		= { .type = NLA_NESTED },
6762306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_DRIVER_ENTRY]		= { .type = NLA_NESTED },
6862306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_DRIVER_PRINT_TYPE]	= { .type = NLA_U8 },
6962306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_DRIVER_STRING]		= { .type = NLA_NUL_STRING,
7062306a36Sopenharmony_ci					.len = RDMA_NLDEV_ATTR_EMPTY_STRING },
7162306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_DRIVER_S32]		= { .type = NLA_S32 },
7262306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_DRIVER_S64]		= { .type = NLA_S64 },
7362306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_DRIVER_U32]		= { .type = NLA_U32 },
7462306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_DRIVER_U64]		= { .type = NLA_U64 },
7562306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_FW_VERSION]		= { .type = NLA_NUL_STRING,
7662306a36Sopenharmony_ci					.len = RDMA_NLDEV_ATTR_EMPTY_STRING },
7762306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_LID]			= { .type = NLA_U32 },
7862306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_LINK_TYPE]		= { .type = NLA_NUL_STRING,
7962306a36Sopenharmony_ci					.len = IFNAMSIZ },
8062306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_LMC]			= { .type = NLA_U8 },
8162306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_NDEV_INDEX]		= { .type = NLA_U32 },
8262306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_NDEV_NAME]		= { .type = NLA_NUL_STRING,
8362306a36Sopenharmony_ci					.len = IFNAMSIZ },
8462306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_NODE_GUID]		= { .type = NLA_U64 },
8562306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_PORT_INDEX]		= { .type = NLA_U32 },
8662306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_PORT_PHYS_STATE]	= { .type = NLA_U8 },
8762306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_PORT_STATE]		= { .type = NLA_U8 },
8862306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_RES_CM_ID]		= { .type = NLA_NESTED },
8962306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_RES_CM_IDN]		= { .type = NLA_U32 },
9062306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_RES_CM_ID_ENTRY]	= { .type = NLA_NESTED },
9162306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_RES_CQ]		= { .type = NLA_NESTED },
9262306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_RES_CQE]		= { .type = NLA_U32 },
9362306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_RES_CQN]		= { .type = NLA_U32 },
9462306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_RES_CQ_ENTRY]		= { .type = NLA_NESTED },
9562306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_RES_CTX]		= { .type = NLA_NESTED },
9662306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_RES_CTXN]		= { .type = NLA_U32 },
9762306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_RES_CTX_ENTRY]		= { .type = NLA_NESTED },
9862306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_RES_DST_ADDR]		= {
9962306a36Sopenharmony_ci			.len = sizeof(struct __kernel_sockaddr_storage) },
10062306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_RES_IOVA]		= { .type = NLA_U64 },
10162306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_RES_KERN_NAME]		= { .type = NLA_NUL_STRING,
10262306a36Sopenharmony_ci					.len = RDMA_NLDEV_ATTR_EMPTY_STRING },
10362306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_RES_LKEY]		= { .type = NLA_U32 },
10462306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_RES_LOCAL_DMA_LKEY]	= { .type = NLA_U32 },
10562306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_RES_LQPN]		= { .type = NLA_U32 },
10662306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_RES_MR]		= { .type = NLA_NESTED },
10762306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_RES_MRLEN]		= { .type = NLA_U64 },
10862306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_RES_MRN]		= { .type = NLA_U32 },
10962306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_RES_MR_ENTRY]		= { .type = NLA_NESTED },
11062306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_RES_PATH_MIG_STATE]	= { .type = NLA_U8 },
11162306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_RES_PD]		= { .type = NLA_NESTED },
11262306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_RES_PDN]		= { .type = NLA_U32 },
11362306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_RES_PD_ENTRY]		= { .type = NLA_NESTED },
11462306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_RES_PID]		= { .type = NLA_U32 },
11562306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_RES_POLL_CTX]		= { .type = NLA_U8 },
11662306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_RES_PS]		= { .type = NLA_U32 },
11762306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_RES_QP]		= { .type = NLA_NESTED },
11862306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_RES_QP_ENTRY]		= { .type = NLA_NESTED },
11962306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_RES_RAW]		= { .type = NLA_BINARY },
12062306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_RES_RKEY]		= { .type = NLA_U32 },
12162306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_RES_RQPN]		= { .type = NLA_U32 },
12262306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_RES_RQ_PSN]		= { .type = NLA_U32 },
12362306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_RES_SQ_PSN]		= { .type = NLA_U32 },
12462306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_RES_SRC_ADDR]		= {
12562306a36Sopenharmony_ci			.len = sizeof(struct __kernel_sockaddr_storage) },
12662306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_RES_STATE]		= { .type = NLA_U8 },
12762306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_RES_SUMMARY]		= { .type = NLA_NESTED },
12862306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY]	= { .type = NLA_NESTED },
12962306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_CURR]= { .type = NLA_U64 },
13062306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_NAME]= { .type = NLA_NUL_STRING,
13162306a36Sopenharmony_ci					.len = RDMA_NLDEV_ATTR_EMPTY_STRING },
13262306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_RES_TYPE]		= { .type = NLA_U8 },
13362306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_RES_UNSAFE_GLOBAL_RKEY]= { .type = NLA_U32 },
13462306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_RES_USECNT]		= { .type = NLA_U64 },
13562306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_RES_SRQ]		= { .type = NLA_NESTED },
13662306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_RES_SRQN]		= { .type = NLA_U32 },
13762306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_RES_SRQ_ENTRY]		= { .type = NLA_NESTED },
13862306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_MIN_RANGE]		= { .type = NLA_U32 },
13962306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_MAX_RANGE]		= { .type = NLA_U32 },
14062306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_SM_LID]		= { .type = NLA_U32 },
14162306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_SUBNET_PREFIX]		= { .type = NLA_U64 },
14262306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_STAT_AUTO_MODE_MASK]	= { .type = NLA_U32 },
14362306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_STAT_MODE]		= { .type = NLA_U32 },
14462306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_STAT_RES]		= { .type = NLA_U32 },
14562306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_STAT_COUNTER]		= { .type = NLA_NESTED },
14662306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_STAT_COUNTER_ENTRY]	= { .type = NLA_NESTED },
14762306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_STAT_COUNTER_ID]       = { .type = NLA_U32 },
14862306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS]       = { .type = NLA_NESTED },
14962306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY]  = { .type = NLA_NESTED },
15062306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME] = { .type = NLA_NUL_STRING },
15162306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_VALUE] = { .type = NLA_U64 },
15262306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_SYS_IMAGE_GUID]	= { .type = NLA_U64 },
15362306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_UVERBS_DRIVER_ID]	= { .type = NLA_U32 },
15462306a36Sopenharmony_ci	[RDMA_NLDEV_NET_NS_FD]			= { .type = NLA_U32 },
15562306a36Sopenharmony_ci	[RDMA_NLDEV_SYS_ATTR_NETNS_MODE]	= { .type = NLA_U8 },
15662306a36Sopenharmony_ci	[RDMA_NLDEV_SYS_ATTR_COPY_ON_FORK]	= { .type = NLA_U8 },
15762306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_INDEX]	= { .type = NLA_U32 },
15862306a36Sopenharmony_ci	[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_DYNAMIC] = { .type = NLA_U8 },
15962306a36Sopenharmony_ci};
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_cistatic int put_driver_name_print_type(struct sk_buff *msg, const char *name,
16262306a36Sopenharmony_ci				      enum rdma_nldev_print_type print_type)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	if (nla_put_string(msg, RDMA_NLDEV_ATTR_DRIVER_STRING, name))
16562306a36Sopenharmony_ci		return -EMSGSIZE;
16662306a36Sopenharmony_ci	if (print_type != RDMA_NLDEV_PRINT_TYPE_UNSPEC &&
16762306a36Sopenharmony_ci	    nla_put_u8(msg, RDMA_NLDEV_ATTR_DRIVER_PRINT_TYPE, print_type))
16862306a36Sopenharmony_ci		return -EMSGSIZE;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	return 0;
17162306a36Sopenharmony_ci}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_cistatic int _rdma_nl_put_driver_u32(struct sk_buff *msg, const char *name,
17462306a36Sopenharmony_ci				   enum rdma_nldev_print_type print_type,
17562306a36Sopenharmony_ci				   u32 value)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	if (put_driver_name_print_type(msg, name, print_type))
17862306a36Sopenharmony_ci		return -EMSGSIZE;
17962306a36Sopenharmony_ci	if (nla_put_u32(msg, RDMA_NLDEV_ATTR_DRIVER_U32, value))
18062306a36Sopenharmony_ci		return -EMSGSIZE;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	return 0;
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_cistatic int _rdma_nl_put_driver_u64(struct sk_buff *msg, const char *name,
18662306a36Sopenharmony_ci				   enum rdma_nldev_print_type print_type,
18762306a36Sopenharmony_ci				   u64 value)
18862306a36Sopenharmony_ci{
18962306a36Sopenharmony_ci	if (put_driver_name_print_type(msg, name, print_type))
19062306a36Sopenharmony_ci		return -EMSGSIZE;
19162306a36Sopenharmony_ci	if (nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_DRIVER_U64, value,
19262306a36Sopenharmony_ci			      RDMA_NLDEV_ATTR_PAD))
19362306a36Sopenharmony_ci		return -EMSGSIZE;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	return 0;
19662306a36Sopenharmony_ci}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ciint rdma_nl_put_driver_string(struct sk_buff *msg, const char *name,
19962306a36Sopenharmony_ci			      const char *str)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	if (put_driver_name_print_type(msg, name,
20262306a36Sopenharmony_ci				       RDMA_NLDEV_PRINT_TYPE_UNSPEC))
20362306a36Sopenharmony_ci		return -EMSGSIZE;
20462306a36Sopenharmony_ci	if (nla_put_string(msg, RDMA_NLDEV_ATTR_DRIVER_STRING, str))
20562306a36Sopenharmony_ci		return -EMSGSIZE;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	return 0;
20862306a36Sopenharmony_ci}
20962306a36Sopenharmony_ciEXPORT_SYMBOL(rdma_nl_put_driver_string);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ciint rdma_nl_put_driver_u32(struct sk_buff *msg, const char *name, u32 value)
21262306a36Sopenharmony_ci{
21362306a36Sopenharmony_ci	return _rdma_nl_put_driver_u32(msg, name, RDMA_NLDEV_PRINT_TYPE_UNSPEC,
21462306a36Sopenharmony_ci				       value);
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ciEXPORT_SYMBOL(rdma_nl_put_driver_u32);
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ciint rdma_nl_put_driver_u32_hex(struct sk_buff *msg, const char *name,
21962306a36Sopenharmony_ci			       u32 value)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	return _rdma_nl_put_driver_u32(msg, name, RDMA_NLDEV_PRINT_TYPE_HEX,
22262306a36Sopenharmony_ci				       value);
22362306a36Sopenharmony_ci}
22462306a36Sopenharmony_ciEXPORT_SYMBOL(rdma_nl_put_driver_u32_hex);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ciint rdma_nl_put_driver_u64(struct sk_buff *msg, const char *name, u64 value)
22762306a36Sopenharmony_ci{
22862306a36Sopenharmony_ci	return _rdma_nl_put_driver_u64(msg, name, RDMA_NLDEV_PRINT_TYPE_UNSPEC,
22962306a36Sopenharmony_ci				       value);
23062306a36Sopenharmony_ci}
23162306a36Sopenharmony_ciEXPORT_SYMBOL(rdma_nl_put_driver_u64);
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ciint rdma_nl_put_driver_u64_hex(struct sk_buff *msg, const char *name, u64 value)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	return _rdma_nl_put_driver_u64(msg, name, RDMA_NLDEV_PRINT_TYPE_HEX,
23662306a36Sopenharmony_ci				       value);
23762306a36Sopenharmony_ci}
23862306a36Sopenharmony_ciEXPORT_SYMBOL(rdma_nl_put_driver_u64_hex);
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_cistatic int fill_nldev_handle(struct sk_buff *msg, struct ib_device *device)
24162306a36Sopenharmony_ci{
24262306a36Sopenharmony_ci	if (nla_put_u32(msg, RDMA_NLDEV_ATTR_DEV_INDEX, device->index))
24362306a36Sopenharmony_ci		return -EMSGSIZE;
24462306a36Sopenharmony_ci	if (nla_put_string(msg, RDMA_NLDEV_ATTR_DEV_NAME,
24562306a36Sopenharmony_ci			   dev_name(&device->dev)))
24662306a36Sopenharmony_ci		return -EMSGSIZE;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	return 0;
24962306a36Sopenharmony_ci}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_cistatic int fill_dev_info(struct sk_buff *msg, struct ib_device *device)
25262306a36Sopenharmony_ci{
25362306a36Sopenharmony_ci	char fw[IB_FW_VERSION_NAME_MAX];
25462306a36Sopenharmony_ci	int ret = 0;
25562306a36Sopenharmony_ci	u32 port;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	if (fill_nldev_handle(msg, device))
25862306a36Sopenharmony_ci		return -EMSGSIZE;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	if (nla_put_u32(msg, RDMA_NLDEV_ATTR_PORT_INDEX, rdma_end_port(device)))
26162306a36Sopenharmony_ci		return -EMSGSIZE;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	BUILD_BUG_ON(sizeof(device->attrs.device_cap_flags) != sizeof(u64));
26462306a36Sopenharmony_ci	if (nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_CAP_FLAGS,
26562306a36Sopenharmony_ci			      device->attrs.device_cap_flags,
26662306a36Sopenharmony_ci			      RDMA_NLDEV_ATTR_PAD))
26762306a36Sopenharmony_ci		return -EMSGSIZE;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	ib_get_device_fw_str(device, fw);
27062306a36Sopenharmony_ci	/* Device without FW has strlen(fw) = 0 */
27162306a36Sopenharmony_ci	if (strlen(fw) && nla_put_string(msg, RDMA_NLDEV_ATTR_FW_VERSION, fw))
27262306a36Sopenharmony_ci		return -EMSGSIZE;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	if (nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_NODE_GUID,
27562306a36Sopenharmony_ci			      be64_to_cpu(device->node_guid),
27662306a36Sopenharmony_ci			      RDMA_NLDEV_ATTR_PAD))
27762306a36Sopenharmony_ci		return -EMSGSIZE;
27862306a36Sopenharmony_ci	if (nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_SYS_IMAGE_GUID,
27962306a36Sopenharmony_ci			      be64_to_cpu(device->attrs.sys_image_guid),
28062306a36Sopenharmony_ci			      RDMA_NLDEV_ATTR_PAD))
28162306a36Sopenharmony_ci		return -EMSGSIZE;
28262306a36Sopenharmony_ci	if (nla_put_u8(msg, RDMA_NLDEV_ATTR_DEV_NODE_TYPE, device->node_type))
28362306a36Sopenharmony_ci		return -EMSGSIZE;
28462306a36Sopenharmony_ci	if (nla_put_u8(msg, RDMA_NLDEV_ATTR_DEV_DIM, device->use_cq_dim))
28562306a36Sopenharmony_ci		return -EMSGSIZE;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	/*
28862306a36Sopenharmony_ci	 * Link type is determined on first port and mlx4 device
28962306a36Sopenharmony_ci	 * which can potentially have two different link type for the same
29062306a36Sopenharmony_ci	 * IB device is considered as better to be avoided in the future,
29162306a36Sopenharmony_ci	 */
29262306a36Sopenharmony_ci	port = rdma_start_port(device);
29362306a36Sopenharmony_ci	if (rdma_cap_opa_mad(device, port))
29462306a36Sopenharmony_ci		ret = nla_put_string(msg, RDMA_NLDEV_ATTR_DEV_PROTOCOL, "opa");
29562306a36Sopenharmony_ci	else if (rdma_protocol_ib(device, port))
29662306a36Sopenharmony_ci		ret = nla_put_string(msg, RDMA_NLDEV_ATTR_DEV_PROTOCOL, "ib");
29762306a36Sopenharmony_ci	else if (rdma_protocol_iwarp(device, port))
29862306a36Sopenharmony_ci		ret = nla_put_string(msg, RDMA_NLDEV_ATTR_DEV_PROTOCOL, "iw");
29962306a36Sopenharmony_ci	else if (rdma_protocol_roce(device, port))
30062306a36Sopenharmony_ci		ret = nla_put_string(msg, RDMA_NLDEV_ATTR_DEV_PROTOCOL, "roce");
30162306a36Sopenharmony_ci	else if (rdma_protocol_usnic(device, port))
30262306a36Sopenharmony_ci		ret = nla_put_string(msg, RDMA_NLDEV_ATTR_DEV_PROTOCOL,
30362306a36Sopenharmony_ci				     "usnic");
30462306a36Sopenharmony_ci	return ret;
30562306a36Sopenharmony_ci}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_cistatic int fill_port_info(struct sk_buff *msg,
30862306a36Sopenharmony_ci			  struct ib_device *device, u32 port,
30962306a36Sopenharmony_ci			  const struct net *net)
31062306a36Sopenharmony_ci{
31162306a36Sopenharmony_ci	struct net_device *netdev = NULL;
31262306a36Sopenharmony_ci	struct ib_port_attr attr;
31362306a36Sopenharmony_ci	int ret;
31462306a36Sopenharmony_ci	u64 cap_flags = 0;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	if (fill_nldev_handle(msg, device))
31762306a36Sopenharmony_ci		return -EMSGSIZE;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	if (nla_put_u32(msg, RDMA_NLDEV_ATTR_PORT_INDEX, port))
32062306a36Sopenharmony_ci		return -EMSGSIZE;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	ret = ib_query_port(device, port, &attr);
32362306a36Sopenharmony_ci	if (ret)
32462306a36Sopenharmony_ci		return ret;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	if (rdma_protocol_ib(device, port)) {
32762306a36Sopenharmony_ci		BUILD_BUG_ON((sizeof(attr.port_cap_flags) +
32862306a36Sopenharmony_ci				sizeof(attr.port_cap_flags2)) > sizeof(u64));
32962306a36Sopenharmony_ci		cap_flags = attr.port_cap_flags |
33062306a36Sopenharmony_ci			((u64)attr.port_cap_flags2 << 32);
33162306a36Sopenharmony_ci		if (nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_CAP_FLAGS,
33262306a36Sopenharmony_ci				      cap_flags, RDMA_NLDEV_ATTR_PAD))
33362306a36Sopenharmony_ci			return -EMSGSIZE;
33462306a36Sopenharmony_ci		if (nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_SUBNET_PREFIX,
33562306a36Sopenharmony_ci				      attr.subnet_prefix, RDMA_NLDEV_ATTR_PAD))
33662306a36Sopenharmony_ci			return -EMSGSIZE;
33762306a36Sopenharmony_ci		if (nla_put_u32(msg, RDMA_NLDEV_ATTR_LID, attr.lid))
33862306a36Sopenharmony_ci			return -EMSGSIZE;
33962306a36Sopenharmony_ci		if (nla_put_u32(msg, RDMA_NLDEV_ATTR_SM_LID, attr.sm_lid))
34062306a36Sopenharmony_ci			return -EMSGSIZE;
34162306a36Sopenharmony_ci		if (nla_put_u8(msg, RDMA_NLDEV_ATTR_LMC, attr.lmc))
34262306a36Sopenharmony_ci			return -EMSGSIZE;
34362306a36Sopenharmony_ci	}
34462306a36Sopenharmony_ci	if (nla_put_u8(msg, RDMA_NLDEV_ATTR_PORT_STATE, attr.state))
34562306a36Sopenharmony_ci		return -EMSGSIZE;
34662306a36Sopenharmony_ci	if (nla_put_u8(msg, RDMA_NLDEV_ATTR_PORT_PHYS_STATE, attr.phys_state))
34762306a36Sopenharmony_ci		return -EMSGSIZE;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	netdev = ib_device_get_netdev(device, port);
35062306a36Sopenharmony_ci	if (netdev && net_eq(dev_net(netdev), net)) {
35162306a36Sopenharmony_ci		ret = nla_put_u32(msg,
35262306a36Sopenharmony_ci				  RDMA_NLDEV_ATTR_NDEV_INDEX, netdev->ifindex);
35362306a36Sopenharmony_ci		if (ret)
35462306a36Sopenharmony_ci			goto out;
35562306a36Sopenharmony_ci		ret = nla_put_string(msg,
35662306a36Sopenharmony_ci				     RDMA_NLDEV_ATTR_NDEV_NAME, netdev->name);
35762306a36Sopenharmony_ci	}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ciout:
36062306a36Sopenharmony_ci	if (netdev)
36162306a36Sopenharmony_ci		dev_put(netdev);
36262306a36Sopenharmony_ci	return ret;
36362306a36Sopenharmony_ci}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_cistatic int fill_res_info_entry(struct sk_buff *msg,
36662306a36Sopenharmony_ci			       const char *name, u64 curr)
36762306a36Sopenharmony_ci{
36862306a36Sopenharmony_ci	struct nlattr *entry_attr;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	entry_attr = nla_nest_start_noflag(msg,
37162306a36Sopenharmony_ci					   RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY);
37262306a36Sopenharmony_ci	if (!entry_attr)
37362306a36Sopenharmony_ci		return -EMSGSIZE;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	if (nla_put_string(msg, RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_NAME, name))
37662306a36Sopenharmony_ci		goto err;
37762306a36Sopenharmony_ci	if (nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_CURR, curr,
37862306a36Sopenharmony_ci			      RDMA_NLDEV_ATTR_PAD))
37962306a36Sopenharmony_ci		goto err;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	nla_nest_end(msg, entry_attr);
38262306a36Sopenharmony_ci	return 0;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_cierr:
38562306a36Sopenharmony_ci	nla_nest_cancel(msg, entry_attr);
38662306a36Sopenharmony_ci	return -EMSGSIZE;
38762306a36Sopenharmony_ci}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_cistatic int fill_res_info(struct sk_buff *msg, struct ib_device *device)
39062306a36Sopenharmony_ci{
39162306a36Sopenharmony_ci	static const char * const names[RDMA_RESTRACK_MAX] = {
39262306a36Sopenharmony_ci		[RDMA_RESTRACK_PD] = "pd",
39362306a36Sopenharmony_ci		[RDMA_RESTRACK_CQ] = "cq",
39462306a36Sopenharmony_ci		[RDMA_RESTRACK_QP] = "qp",
39562306a36Sopenharmony_ci		[RDMA_RESTRACK_CM_ID] = "cm_id",
39662306a36Sopenharmony_ci		[RDMA_RESTRACK_MR] = "mr",
39762306a36Sopenharmony_ci		[RDMA_RESTRACK_CTX] = "ctx",
39862306a36Sopenharmony_ci		[RDMA_RESTRACK_SRQ] = "srq",
39962306a36Sopenharmony_ci	};
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	struct nlattr *table_attr;
40262306a36Sopenharmony_ci	int ret, i, curr;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	if (fill_nldev_handle(msg, device))
40562306a36Sopenharmony_ci		return -EMSGSIZE;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	table_attr = nla_nest_start_noflag(msg, RDMA_NLDEV_ATTR_RES_SUMMARY);
40862306a36Sopenharmony_ci	if (!table_attr)
40962306a36Sopenharmony_ci		return -EMSGSIZE;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	for (i = 0; i < RDMA_RESTRACK_MAX; i++) {
41262306a36Sopenharmony_ci		if (!names[i])
41362306a36Sopenharmony_ci			continue;
41462306a36Sopenharmony_ci		curr = rdma_restrack_count(device, i);
41562306a36Sopenharmony_ci		ret = fill_res_info_entry(msg, names[i], curr);
41662306a36Sopenharmony_ci		if (ret)
41762306a36Sopenharmony_ci			goto err;
41862306a36Sopenharmony_ci	}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	nla_nest_end(msg, table_attr);
42162306a36Sopenharmony_ci	return 0;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_cierr:
42462306a36Sopenharmony_ci	nla_nest_cancel(msg, table_attr);
42562306a36Sopenharmony_ci	return ret;
42662306a36Sopenharmony_ci}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_cistatic int fill_res_name_pid(struct sk_buff *msg,
42962306a36Sopenharmony_ci			     struct rdma_restrack_entry *res)
43062306a36Sopenharmony_ci{
43162306a36Sopenharmony_ci	int err = 0;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	/*
43462306a36Sopenharmony_ci	 * For user resources, user is should read /proc/PID/comm to get the
43562306a36Sopenharmony_ci	 * name of the task file.
43662306a36Sopenharmony_ci	 */
43762306a36Sopenharmony_ci	if (rdma_is_kernel_res(res)) {
43862306a36Sopenharmony_ci		err = nla_put_string(msg, RDMA_NLDEV_ATTR_RES_KERN_NAME,
43962306a36Sopenharmony_ci				     res->kern_name);
44062306a36Sopenharmony_ci	} else {
44162306a36Sopenharmony_ci		pid_t pid;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci		pid = task_pid_vnr(res->task);
44462306a36Sopenharmony_ci		/*
44562306a36Sopenharmony_ci		 * Task is dead and in zombie state.
44662306a36Sopenharmony_ci		 * There is no need to print PID anymore.
44762306a36Sopenharmony_ci		 */
44862306a36Sopenharmony_ci		if (pid)
44962306a36Sopenharmony_ci			/*
45062306a36Sopenharmony_ci			 * This part is racy, task can be killed and PID will
45162306a36Sopenharmony_ci			 * be zero right here but it is ok, next query won't
45262306a36Sopenharmony_ci			 * return PID. We don't promise real-time reflection
45362306a36Sopenharmony_ci			 * of SW objects.
45462306a36Sopenharmony_ci			 */
45562306a36Sopenharmony_ci			err = nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_PID, pid);
45662306a36Sopenharmony_ci	}
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	return err ? -EMSGSIZE : 0;
45962306a36Sopenharmony_ci}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_cistatic int fill_res_qp_entry_query(struct sk_buff *msg,
46262306a36Sopenharmony_ci				   struct rdma_restrack_entry *res,
46362306a36Sopenharmony_ci				   struct ib_device *dev,
46462306a36Sopenharmony_ci				   struct ib_qp *qp)
46562306a36Sopenharmony_ci{
46662306a36Sopenharmony_ci	struct ib_qp_init_attr qp_init_attr;
46762306a36Sopenharmony_ci	struct ib_qp_attr qp_attr;
46862306a36Sopenharmony_ci	int ret;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	ret = ib_query_qp(qp, &qp_attr, 0, &qp_init_attr);
47162306a36Sopenharmony_ci	if (ret)
47262306a36Sopenharmony_ci		return ret;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	if (qp->qp_type == IB_QPT_RC || qp->qp_type == IB_QPT_UC) {
47562306a36Sopenharmony_ci		if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_RQPN,
47662306a36Sopenharmony_ci				qp_attr.dest_qp_num))
47762306a36Sopenharmony_ci			goto err;
47862306a36Sopenharmony_ci		if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_RQ_PSN,
47962306a36Sopenharmony_ci				qp_attr.rq_psn))
48062306a36Sopenharmony_ci			goto err;
48162306a36Sopenharmony_ci	}
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_SQ_PSN, qp_attr.sq_psn))
48462306a36Sopenharmony_ci		goto err;
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	if (qp->qp_type == IB_QPT_RC || qp->qp_type == IB_QPT_UC ||
48762306a36Sopenharmony_ci	    qp->qp_type == IB_QPT_XRC_INI || qp->qp_type == IB_QPT_XRC_TGT) {
48862306a36Sopenharmony_ci		if (nla_put_u8(msg, RDMA_NLDEV_ATTR_RES_PATH_MIG_STATE,
48962306a36Sopenharmony_ci			       qp_attr.path_mig_state))
49062306a36Sopenharmony_ci			goto err;
49162306a36Sopenharmony_ci	}
49262306a36Sopenharmony_ci	if (nla_put_u8(msg, RDMA_NLDEV_ATTR_RES_TYPE, qp->qp_type))
49362306a36Sopenharmony_ci		goto err;
49462306a36Sopenharmony_ci	if (nla_put_u8(msg, RDMA_NLDEV_ATTR_RES_STATE, qp_attr.qp_state))
49562306a36Sopenharmony_ci		goto err;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	if (dev->ops.fill_res_qp_entry)
49862306a36Sopenharmony_ci		return dev->ops.fill_res_qp_entry(msg, qp);
49962306a36Sopenharmony_ci	return 0;
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_cierr:	return -EMSGSIZE;
50262306a36Sopenharmony_ci}
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_cistatic int fill_res_qp_entry(struct sk_buff *msg, bool has_cap_net_admin,
50562306a36Sopenharmony_ci			     struct rdma_restrack_entry *res, uint32_t port)
50662306a36Sopenharmony_ci{
50762306a36Sopenharmony_ci	struct ib_qp *qp = container_of(res, struct ib_qp, res);
50862306a36Sopenharmony_ci	struct ib_device *dev = qp->device;
50962306a36Sopenharmony_ci	int ret;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	if (port && port != qp->port)
51262306a36Sopenharmony_ci		return -EAGAIN;
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	/* In create_qp() port is not set yet */
51562306a36Sopenharmony_ci	if (qp->port && nla_put_u32(msg, RDMA_NLDEV_ATTR_PORT_INDEX, qp->port))
51662306a36Sopenharmony_ci		return -EMSGSIZE;
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	ret = nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_LQPN, qp->qp_num);
51962306a36Sopenharmony_ci	if (ret)
52062306a36Sopenharmony_ci		return -EMSGSIZE;
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	if (!rdma_is_kernel_res(res) &&
52362306a36Sopenharmony_ci	    nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_PDN, qp->pd->res.id))
52462306a36Sopenharmony_ci		return -EMSGSIZE;
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	ret = fill_res_name_pid(msg, res);
52762306a36Sopenharmony_ci	if (ret)
52862306a36Sopenharmony_ci		return -EMSGSIZE;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	return fill_res_qp_entry_query(msg, res, dev, qp);
53162306a36Sopenharmony_ci}
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_cistatic int fill_res_qp_raw_entry(struct sk_buff *msg, bool has_cap_net_admin,
53462306a36Sopenharmony_ci				 struct rdma_restrack_entry *res, uint32_t port)
53562306a36Sopenharmony_ci{
53662306a36Sopenharmony_ci	struct ib_qp *qp = container_of(res, struct ib_qp, res);
53762306a36Sopenharmony_ci	struct ib_device *dev = qp->device;
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	if (port && port != qp->port)
54062306a36Sopenharmony_ci		return -EAGAIN;
54162306a36Sopenharmony_ci	if (!dev->ops.fill_res_qp_entry_raw)
54262306a36Sopenharmony_ci		return -EINVAL;
54362306a36Sopenharmony_ci	return dev->ops.fill_res_qp_entry_raw(msg, qp);
54462306a36Sopenharmony_ci}
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_cistatic int fill_res_cm_id_entry(struct sk_buff *msg, bool has_cap_net_admin,
54762306a36Sopenharmony_ci				struct rdma_restrack_entry *res, uint32_t port)
54862306a36Sopenharmony_ci{
54962306a36Sopenharmony_ci	struct rdma_id_private *id_priv =
55062306a36Sopenharmony_ci				container_of(res, struct rdma_id_private, res);
55162306a36Sopenharmony_ci	struct ib_device *dev = id_priv->id.device;
55262306a36Sopenharmony_ci	struct rdma_cm_id *cm_id = &id_priv->id;
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	if (port && port != cm_id->port_num)
55562306a36Sopenharmony_ci		return -EAGAIN;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	if (cm_id->port_num &&
55862306a36Sopenharmony_ci	    nla_put_u32(msg, RDMA_NLDEV_ATTR_PORT_INDEX, cm_id->port_num))
55962306a36Sopenharmony_ci		goto err;
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	if (id_priv->qp_num) {
56262306a36Sopenharmony_ci		if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_LQPN, id_priv->qp_num))
56362306a36Sopenharmony_ci			goto err;
56462306a36Sopenharmony_ci		if (nla_put_u8(msg, RDMA_NLDEV_ATTR_RES_TYPE, cm_id->qp_type))
56562306a36Sopenharmony_ci			goto err;
56662306a36Sopenharmony_ci	}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_PS, cm_id->ps))
56962306a36Sopenharmony_ci		goto err;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	if (nla_put_u8(msg, RDMA_NLDEV_ATTR_RES_STATE, id_priv->state))
57262306a36Sopenharmony_ci		goto err;
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	if (cm_id->route.addr.src_addr.ss_family &&
57562306a36Sopenharmony_ci	    nla_put(msg, RDMA_NLDEV_ATTR_RES_SRC_ADDR,
57662306a36Sopenharmony_ci		    sizeof(cm_id->route.addr.src_addr),
57762306a36Sopenharmony_ci		    &cm_id->route.addr.src_addr))
57862306a36Sopenharmony_ci		goto err;
57962306a36Sopenharmony_ci	if (cm_id->route.addr.dst_addr.ss_family &&
58062306a36Sopenharmony_ci	    nla_put(msg, RDMA_NLDEV_ATTR_RES_DST_ADDR,
58162306a36Sopenharmony_ci		    sizeof(cm_id->route.addr.dst_addr),
58262306a36Sopenharmony_ci		    &cm_id->route.addr.dst_addr))
58362306a36Sopenharmony_ci		goto err;
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_CM_IDN, res->id))
58662306a36Sopenharmony_ci		goto err;
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	if (fill_res_name_pid(msg, res))
58962306a36Sopenharmony_ci		goto err;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	if (dev->ops.fill_res_cm_id_entry)
59262306a36Sopenharmony_ci		return dev->ops.fill_res_cm_id_entry(msg, cm_id);
59362306a36Sopenharmony_ci	return 0;
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_cierr: return -EMSGSIZE;
59662306a36Sopenharmony_ci}
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_cistatic int fill_res_cq_entry(struct sk_buff *msg, bool has_cap_net_admin,
59962306a36Sopenharmony_ci			     struct rdma_restrack_entry *res, uint32_t port)
60062306a36Sopenharmony_ci{
60162306a36Sopenharmony_ci	struct ib_cq *cq = container_of(res, struct ib_cq, res);
60262306a36Sopenharmony_ci	struct ib_device *dev = cq->device;
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_CQE, cq->cqe))
60562306a36Sopenharmony_ci		return -EMSGSIZE;
60662306a36Sopenharmony_ci	if (nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_RES_USECNT,
60762306a36Sopenharmony_ci			      atomic_read(&cq->usecnt), RDMA_NLDEV_ATTR_PAD))
60862306a36Sopenharmony_ci		return -EMSGSIZE;
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	/* Poll context is only valid for kernel CQs */
61162306a36Sopenharmony_ci	if (rdma_is_kernel_res(res) &&
61262306a36Sopenharmony_ci	    nla_put_u8(msg, RDMA_NLDEV_ATTR_RES_POLL_CTX, cq->poll_ctx))
61362306a36Sopenharmony_ci		return -EMSGSIZE;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	if (nla_put_u8(msg, RDMA_NLDEV_ATTR_DEV_DIM, (cq->dim != NULL)))
61662306a36Sopenharmony_ci		return -EMSGSIZE;
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_CQN, res->id))
61962306a36Sopenharmony_ci		return -EMSGSIZE;
62062306a36Sopenharmony_ci	if (!rdma_is_kernel_res(res) &&
62162306a36Sopenharmony_ci	    nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_CTXN,
62262306a36Sopenharmony_ci			cq->uobject->uevent.uobject.context->res.id))
62362306a36Sopenharmony_ci		return -EMSGSIZE;
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	if (fill_res_name_pid(msg, res))
62662306a36Sopenharmony_ci		return -EMSGSIZE;
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	return (dev->ops.fill_res_cq_entry) ?
62962306a36Sopenharmony_ci		dev->ops.fill_res_cq_entry(msg, cq) : 0;
63062306a36Sopenharmony_ci}
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_cistatic int fill_res_cq_raw_entry(struct sk_buff *msg, bool has_cap_net_admin,
63362306a36Sopenharmony_ci				 struct rdma_restrack_entry *res, uint32_t port)
63462306a36Sopenharmony_ci{
63562306a36Sopenharmony_ci	struct ib_cq *cq = container_of(res, struct ib_cq, res);
63662306a36Sopenharmony_ci	struct ib_device *dev = cq->device;
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	if (!dev->ops.fill_res_cq_entry_raw)
63962306a36Sopenharmony_ci		return -EINVAL;
64062306a36Sopenharmony_ci	return dev->ops.fill_res_cq_entry_raw(msg, cq);
64162306a36Sopenharmony_ci}
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_cistatic int fill_res_mr_entry(struct sk_buff *msg, bool has_cap_net_admin,
64462306a36Sopenharmony_ci			     struct rdma_restrack_entry *res, uint32_t port)
64562306a36Sopenharmony_ci{
64662306a36Sopenharmony_ci	struct ib_mr *mr = container_of(res, struct ib_mr, res);
64762306a36Sopenharmony_ci	struct ib_device *dev = mr->pd->device;
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	if (has_cap_net_admin) {
65062306a36Sopenharmony_ci		if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_RKEY, mr->rkey))
65162306a36Sopenharmony_ci			return -EMSGSIZE;
65262306a36Sopenharmony_ci		if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_LKEY, mr->lkey))
65362306a36Sopenharmony_ci			return -EMSGSIZE;
65462306a36Sopenharmony_ci	}
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	if (nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_RES_MRLEN, mr->length,
65762306a36Sopenharmony_ci			      RDMA_NLDEV_ATTR_PAD))
65862306a36Sopenharmony_ci		return -EMSGSIZE;
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_MRN, res->id))
66162306a36Sopenharmony_ci		return -EMSGSIZE;
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	if (!rdma_is_kernel_res(res) &&
66462306a36Sopenharmony_ci	    nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_PDN, mr->pd->res.id))
66562306a36Sopenharmony_ci		return -EMSGSIZE;
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	if (fill_res_name_pid(msg, res))
66862306a36Sopenharmony_ci		return -EMSGSIZE;
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	return (dev->ops.fill_res_mr_entry) ?
67162306a36Sopenharmony_ci		       dev->ops.fill_res_mr_entry(msg, mr) :
67262306a36Sopenharmony_ci		       0;
67362306a36Sopenharmony_ci}
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_cistatic int fill_res_mr_raw_entry(struct sk_buff *msg, bool has_cap_net_admin,
67662306a36Sopenharmony_ci				 struct rdma_restrack_entry *res, uint32_t port)
67762306a36Sopenharmony_ci{
67862306a36Sopenharmony_ci	struct ib_mr *mr = container_of(res, struct ib_mr, res);
67962306a36Sopenharmony_ci	struct ib_device *dev = mr->pd->device;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	if (!dev->ops.fill_res_mr_entry_raw)
68262306a36Sopenharmony_ci		return -EINVAL;
68362306a36Sopenharmony_ci	return dev->ops.fill_res_mr_entry_raw(msg, mr);
68462306a36Sopenharmony_ci}
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_cistatic int fill_res_pd_entry(struct sk_buff *msg, bool has_cap_net_admin,
68762306a36Sopenharmony_ci			     struct rdma_restrack_entry *res, uint32_t port)
68862306a36Sopenharmony_ci{
68962306a36Sopenharmony_ci	struct ib_pd *pd = container_of(res, struct ib_pd, res);
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	if (has_cap_net_admin) {
69262306a36Sopenharmony_ci		if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_LOCAL_DMA_LKEY,
69362306a36Sopenharmony_ci				pd->local_dma_lkey))
69462306a36Sopenharmony_ci			goto err;
69562306a36Sopenharmony_ci		if ((pd->flags & IB_PD_UNSAFE_GLOBAL_RKEY) &&
69662306a36Sopenharmony_ci		    nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_UNSAFE_GLOBAL_RKEY,
69762306a36Sopenharmony_ci				pd->unsafe_global_rkey))
69862306a36Sopenharmony_ci			goto err;
69962306a36Sopenharmony_ci	}
70062306a36Sopenharmony_ci	if (nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_RES_USECNT,
70162306a36Sopenharmony_ci			      atomic_read(&pd->usecnt), RDMA_NLDEV_ATTR_PAD))
70262306a36Sopenharmony_ci		goto err;
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_PDN, res->id))
70562306a36Sopenharmony_ci		goto err;
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	if (!rdma_is_kernel_res(res) &&
70862306a36Sopenharmony_ci	    nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_CTXN,
70962306a36Sopenharmony_ci			pd->uobject->context->res.id))
71062306a36Sopenharmony_ci		goto err;
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	return fill_res_name_pid(msg, res);
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_cierr:	return -EMSGSIZE;
71562306a36Sopenharmony_ci}
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_cistatic int fill_res_ctx_entry(struct sk_buff *msg, bool has_cap_net_admin,
71862306a36Sopenharmony_ci			      struct rdma_restrack_entry *res, uint32_t port)
71962306a36Sopenharmony_ci{
72062306a36Sopenharmony_ci	struct ib_ucontext *ctx = container_of(res, struct ib_ucontext, res);
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	if (rdma_is_kernel_res(res))
72362306a36Sopenharmony_ci		return 0;
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_CTXN, ctx->res.id))
72662306a36Sopenharmony_ci		return -EMSGSIZE;
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	return fill_res_name_pid(msg, res);
72962306a36Sopenharmony_ci}
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_cistatic int fill_res_range_qp_entry(struct sk_buff *msg, uint32_t min_range,
73262306a36Sopenharmony_ci				   uint32_t max_range)
73362306a36Sopenharmony_ci{
73462306a36Sopenharmony_ci	struct nlattr *entry_attr;
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	if (!min_range)
73762306a36Sopenharmony_ci		return 0;
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	entry_attr = nla_nest_start(msg, RDMA_NLDEV_ATTR_RES_QP_ENTRY);
74062306a36Sopenharmony_ci	if (!entry_attr)
74162306a36Sopenharmony_ci		return -EMSGSIZE;
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	if (min_range == max_range) {
74462306a36Sopenharmony_ci		if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_LQPN, min_range))
74562306a36Sopenharmony_ci			goto err;
74662306a36Sopenharmony_ci	} else {
74762306a36Sopenharmony_ci		if (nla_put_u32(msg, RDMA_NLDEV_ATTR_MIN_RANGE, min_range))
74862306a36Sopenharmony_ci			goto err;
74962306a36Sopenharmony_ci		if (nla_put_u32(msg, RDMA_NLDEV_ATTR_MAX_RANGE, max_range))
75062306a36Sopenharmony_ci			goto err;
75162306a36Sopenharmony_ci	}
75262306a36Sopenharmony_ci	nla_nest_end(msg, entry_attr);
75362306a36Sopenharmony_ci	return 0;
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_cierr:
75662306a36Sopenharmony_ci	nla_nest_cancel(msg, entry_attr);
75762306a36Sopenharmony_ci	return -EMSGSIZE;
75862306a36Sopenharmony_ci}
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_cistatic int fill_res_srq_qps(struct sk_buff *msg, struct ib_srq *srq)
76162306a36Sopenharmony_ci{
76262306a36Sopenharmony_ci	uint32_t min_range = 0, prev = 0;
76362306a36Sopenharmony_ci	struct rdma_restrack_entry *res;
76462306a36Sopenharmony_ci	struct rdma_restrack_root *rt;
76562306a36Sopenharmony_ci	struct nlattr *table_attr;
76662306a36Sopenharmony_ci	struct ib_qp *qp = NULL;
76762306a36Sopenharmony_ci	unsigned long id = 0;
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	table_attr = nla_nest_start(msg, RDMA_NLDEV_ATTR_RES_QP);
77062306a36Sopenharmony_ci	if (!table_attr)
77162306a36Sopenharmony_ci		return -EMSGSIZE;
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	rt = &srq->device->res[RDMA_RESTRACK_QP];
77462306a36Sopenharmony_ci	xa_lock(&rt->xa);
77562306a36Sopenharmony_ci	xa_for_each(&rt->xa, id, res) {
77662306a36Sopenharmony_ci		if (!rdma_restrack_get(res))
77762306a36Sopenharmony_ci			continue;
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci		qp = container_of(res, struct ib_qp, res);
78062306a36Sopenharmony_ci		if (!qp->srq || (qp->srq->res.id != srq->res.id)) {
78162306a36Sopenharmony_ci			rdma_restrack_put(res);
78262306a36Sopenharmony_ci			continue;
78362306a36Sopenharmony_ci		}
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci		if (qp->qp_num < prev)
78662306a36Sopenharmony_ci			/* qp_num should be ascending */
78762306a36Sopenharmony_ci			goto err_loop;
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci		if (min_range == 0) {
79062306a36Sopenharmony_ci			min_range = qp->qp_num;
79162306a36Sopenharmony_ci		} else if (qp->qp_num > (prev + 1)) {
79262306a36Sopenharmony_ci			if (fill_res_range_qp_entry(msg, min_range, prev))
79362306a36Sopenharmony_ci				goto err_loop;
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci			min_range = qp->qp_num;
79662306a36Sopenharmony_ci		}
79762306a36Sopenharmony_ci		prev = qp->qp_num;
79862306a36Sopenharmony_ci		rdma_restrack_put(res);
79962306a36Sopenharmony_ci	}
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci	xa_unlock(&rt->xa);
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci	if (fill_res_range_qp_entry(msg, min_range, prev))
80462306a36Sopenharmony_ci		goto err;
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	nla_nest_end(msg, table_attr);
80762306a36Sopenharmony_ci	return 0;
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_cierr_loop:
81062306a36Sopenharmony_ci	rdma_restrack_put(res);
81162306a36Sopenharmony_ci	xa_unlock(&rt->xa);
81262306a36Sopenharmony_cierr:
81362306a36Sopenharmony_ci	nla_nest_cancel(msg, table_attr);
81462306a36Sopenharmony_ci	return -EMSGSIZE;
81562306a36Sopenharmony_ci}
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_cistatic int fill_res_srq_entry(struct sk_buff *msg, bool has_cap_net_admin,
81862306a36Sopenharmony_ci			      struct rdma_restrack_entry *res, uint32_t port)
81962306a36Sopenharmony_ci{
82062306a36Sopenharmony_ci	struct ib_srq *srq = container_of(res, struct ib_srq, res);
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_SRQN, srq->res.id))
82362306a36Sopenharmony_ci		goto err;
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	if (nla_put_u8(msg, RDMA_NLDEV_ATTR_RES_TYPE, srq->srq_type))
82662306a36Sopenharmony_ci		goto err;
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci	if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_PDN, srq->pd->res.id))
82962306a36Sopenharmony_ci		goto err;
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	if (ib_srq_has_cq(srq->srq_type)) {
83262306a36Sopenharmony_ci		if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_CQN,
83362306a36Sopenharmony_ci				srq->ext.cq->res.id))
83462306a36Sopenharmony_ci			goto err;
83562306a36Sopenharmony_ci	}
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	if (fill_res_srq_qps(msg, srq))
83862306a36Sopenharmony_ci		goto err;
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	return fill_res_name_pid(msg, res);
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_cierr:
84362306a36Sopenharmony_ci	return -EMSGSIZE;
84462306a36Sopenharmony_ci}
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_cistatic int fill_stat_counter_mode(struct sk_buff *msg,
84762306a36Sopenharmony_ci				  struct rdma_counter *counter)
84862306a36Sopenharmony_ci{
84962306a36Sopenharmony_ci	struct rdma_counter_mode *m = &counter->mode;
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	if (nla_put_u32(msg, RDMA_NLDEV_ATTR_STAT_MODE, m->mode))
85262306a36Sopenharmony_ci		return -EMSGSIZE;
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	if (m->mode == RDMA_COUNTER_MODE_AUTO) {
85562306a36Sopenharmony_ci		if ((m->mask & RDMA_COUNTER_MASK_QP_TYPE) &&
85662306a36Sopenharmony_ci		    nla_put_u8(msg, RDMA_NLDEV_ATTR_RES_TYPE, m->param.qp_type))
85762306a36Sopenharmony_ci			return -EMSGSIZE;
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci		if ((m->mask & RDMA_COUNTER_MASK_PID) &&
86062306a36Sopenharmony_ci		    fill_res_name_pid(msg, &counter->res))
86162306a36Sopenharmony_ci			return -EMSGSIZE;
86262306a36Sopenharmony_ci	}
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci	return 0;
86562306a36Sopenharmony_ci}
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_cistatic int fill_stat_counter_qp_entry(struct sk_buff *msg, u32 qpn)
86862306a36Sopenharmony_ci{
86962306a36Sopenharmony_ci	struct nlattr *entry_attr;
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	entry_attr = nla_nest_start(msg, RDMA_NLDEV_ATTR_RES_QP_ENTRY);
87262306a36Sopenharmony_ci	if (!entry_attr)
87362306a36Sopenharmony_ci		return -EMSGSIZE;
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_LQPN, qpn))
87662306a36Sopenharmony_ci		goto err;
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	nla_nest_end(msg, entry_attr);
87962306a36Sopenharmony_ci	return 0;
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_cierr:
88262306a36Sopenharmony_ci	nla_nest_cancel(msg, entry_attr);
88362306a36Sopenharmony_ci	return -EMSGSIZE;
88462306a36Sopenharmony_ci}
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_cistatic int fill_stat_counter_qps(struct sk_buff *msg,
88762306a36Sopenharmony_ci				 struct rdma_counter *counter)
88862306a36Sopenharmony_ci{
88962306a36Sopenharmony_ci	struct rdma_restrack_entry *res;
89062306a36Sopenharmony_ci	struct rdma_restrack_root *rt;
89162306a36Sopenharmony_ci	struct nlattr *table_attr;
89262306a36Sopenharmony_ci	struct ib_qp *qp = NULL;
89362306a36Sopenharmony_ci	unsigned long id = 0;
89462306a36Sopenharmony_ci	int ret = 0;
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	table_attr = nla_nest_start(msg, RDMA_NLDEV_ATTR_RES_QP);
89762306a36Sopenharmony_ci	if (!table_attr)
89862306a36Sopenharmony_ci		return -EMSGSIZE;
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	rt = &counter->device->res[RDMA_RESTRACK_QP];
90162306a36Sopenharmony_ci	xa_lock(&rt->xa);
90262306a36Sopenharmony_ci	xa_for_each(&rt->xa, id, res) {
90362306a36Sopenharmony_ci		qp = container_of(res, struct ib_qp, res);
90462306a36Sopenharmony_ci		if (!qp->counter || (qp->counter->id != counter->id))
90562306a36Sopenharmony_ci			continue;
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci		ret = fill_stat_counter_qp_entry(msg, qp->qp_num);
90862306a36Sopenharmony_ci		if (ret)
90962306a36Sopenharmony_ci			goto err;
91062306a36Sopenharmony_ci	}
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci	xa_unlock(&rt->xa);
91362306a36Sopenharmony_ci	nla_nest_end(msg, table_attr);
91462306a36Sopenharmony_ci	return 0;
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_cierr:
91762306a36Sopenharmony_ci	xa_unlock(&rt->xa);
91862306a36Sopenharmony_ci	nla_nest_cancel(msg, table_attr);
91962306a36Sopenharmony_ci	return ret;
92062306a36Sopenharmony_ci}
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ciint rdma_nl_stat_hwcounter_entry(struct sk_buff *msg, const char *name,
92362306a36Sopenharmony_ci				 u64 value)
92462306a36Sopenharmony_ci{
92562306a36Sopenharmony_ci	struct nlattr *entry_attr;
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci	entry_attr = nla_nest_start(msg, RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY);
92862306a36Sopenharmony_ci	if (!entry_attr)
92962306a36Sopenharmony_ci		return -EMSGSIZE;
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	if (nla_put_string(msg, RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME,
93262306a36Sopenharmony_ci			   name))
93362306a36Sopenharmony_ci		goto err;
93462306a36Sopenharmony_ci	if (nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_VALUE,
93562306a36Sopenharmony_ci			      value, RDMA_NLDEV_ATTR_PAD))
93662306a36Sopenharmony_ci		goto err;
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci	nla_nest_end(msg, entry_attr);
93962306a36Sopenharmony_ci	return 0;
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_cierr:
94262306a36Sopenharmony_ci	nla_nest_cancel(msg, entry_attr);
94362306a36Sopenharmony_ci	return -EMSGSIZE;
94462306a36Sopenharmony_ci}
94562306a36Sopenharmony_ciEXPORT_SYMBOL(rdma_nl_stat_hwcounter_entry);
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_cistatic int fill_stat_mr_entry(struct sk_buff *msg, bool has_cap_net_admin,
94862306a36Sopenharmony_ci			      struct rdma_restrack_entry *res, uint32_t port)
94962306a36Sopenharmony_ci{
95062306a36Sopenharmony_ci	struct ib_mr *mr = container_of(res, struct ib_mr, res);
95162306a36Sopenharmony_ci	struct ib_device *dev = mr->pd->device;
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_MRN, res->id))
95462306a36Sopenharmony_ci		goto err;
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	if (dev->ops.fill_stat_mr_entry)
95762306a36Sopenharmony_ci		return dev->ops.fill_stat_mr_entry(msg, mr);
95862306a36Sopenharmony_ci	return 0;
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_cierr:
96162306a36Sopenharmony_ci	return -EMSGSIZE;
96262306a36Sopenharmony_ci}
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_cistatic int fill_stat_counter_hwcounters(struct sk_buff *msg,
96562306a36Sopenharmony_ci					struct rdma_counter *counter)
96662306a36Sopenharmony_ci{
96762306a36Sopenharmony_ci	struct rdma_hw_stats *st = counter->stats;
96862306a36Sopenharmony_ci	struct nlattr *table_attr;
96962306a36Sopenharmony_ci	int i;
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	table_attr = nla_nest_start(msg, RDMA_NLDEV_ATTR_STAT_HWCOUNTERS);
97262306a36Sopenharmony_ci	if (!table_attr)
97362306a36Sopenharmony_ci		return -EMSGSIZE;
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	mutex_lock(&st->lock);
97662306a36Sopenharmony_ci	for (i = 0; i < st->num_counters; i++) {
97762306a36Sopenharmony_ci		if (test_bit(i, st->is_disabled))
97862306a36Sopenharmony_ci			continue;
97962306a36Sopenharmony_ci		if (rdma_nl_stat_hwcounter_entry(msg, st->descs[i].name,
98062306a36Sopenharmony_ci						 st->value[i]))
98162306a36Sopenharmony_ci			goto err;
98262306a36Sopenharmony_ci	}
98362306a36Sopenharmony_ci	mutex_unlock(&st->lock);
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	nla_nest_end(msg, table_attr);
98662306a36Sopenharmony_ci	return 0;
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_cierr:
98962306a36Sopenharmony_ci	mutex_unlock(&st->lock);
99062306a36Sopenharmony_ci	nla_nest_cancel(msg, table_attr);
99162306a36Sopenharmony_ci	return -EMSGSIZE;
99262306a36Sopenharmony_ci}
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_cistatic int fill_res_counter_entry(struct sk_buff *msg, bool has_cap_net_admin,
99562306a36Sopenharmony_ci				  struct rdma_restrack_entry *res,
99662306a36Sopenharmony_ci				  uint32_t port)
99762306a36Sopenharmony_ci{
99862306a36Sopenharmony_ci	struct rdma_counter *counter =
99962306a36Sopenharmony_ci		container_of(res, struct rdma_counter, res);
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci	if (port && port != counter->port)
100262306a36Sopenharmony_ci		return -EAGAIN;
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci	/* Dump it even query failed */
100562306a36Sopenharmony_ci	rdma_counter_query_stats(counter);
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	if (nla_put_u32(msg, RDMA_NLDEV_ATTR_PORT_INDEX, counter->port) ||
100862306a36Sopenharmony_ci	    nla_put_u32(msg, RDMA_NLDEV_ATTR_STAT_COUNTER_ID, counter->id) ||
100962306a36Sopenharmony_ci	    fill_stat_counter_mode(msg, counter) ||
101062306a36Sopenharmony_ci	    fill_stat_counter_qps(msg, counter) ||
101162306a36Sopenharmony_ci	    fill_stat_counter_hwcounters(msg, counter))
101262306a36Sopenharmony_ci		return -EMSGSIZE;
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	return 0;
101562306a36Sopenharmony_ci}
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_cistatic int nldev_get_doit(struct sk_buff *skb, struct nlmsghdr *nlh,
101862306a36Sopenharmony_ci			  struct netlink_ext_ack *extack)
101962306a36Sopenharmony_ci{
102062306a36Sopenharmony_ci	struct nlattr *tb[RDMA_NLDEV_ATTR_MAX];
102162306a36Sopenharmony_ci	struct ib_device *device;
102262306a36Sopenharmony_ci	struct sk_buff *msg;
102362306a36Sopenharmony_ci	u32 index;
102462306a36Sopenharmony_ci	int err;
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	err = nlmsg_parse_deprecated(nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1,
102762306a36Sopenharmony_ci				     nldev_policy, extack);
102862306a36Sopenharmony_ci	if (err || !tb[RDMA_NLDEV_ATTR_DEV_INDEX])
102962306a36Sopenharmony_ci		return -EINVAL;
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ci	index = nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]);
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci	device = ib_device_get_by_index(sock_net(skb->sk), index);
103462306a36Sopenharmony_ci	if (!device)
103562306a36Sopenharmony_ci		return -EINVAL;
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
103862306a36Sopenharmony_ci	if (!msg) {
103962306a36Sopenharmony_ci		err = -ENOMEM;
104062306a36Sopenharmony_ci		goto err;
104162306a36Sopenharmony_ci	}
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci	nlh = nlmsg_put(msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq,
104462306a36Sopenharmony_ci			RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, RDMA_NLDEV_CMD_GET),
104562306a36Sopenharmony_ci			0, 0);
104662306a36Sopenharmony_ci	if (!nlh) {
104762306a36Sopenharmony_ci		err = -EMSGSIZE;
104862306a36Sopenharmony_ci		goto err_free;
104962306a36Sopenharmony_ci	}
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci	err = fill_dev_info(msg, device);
105262306a36Sopenharmony_ci	if (err)
105362306a36Sopenharmony_ci		goto err_free;
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci	nlmsg_end(msg, nlh);
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	ib_device_put(device);
105862306a36Sopenharmony_ci	return rdma_nl_unicast(sock_net(skb->sk), msg, NETLINK_CB(skb).portid);
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_cierr_free:
106162306a36Sopenharmony_ci	nlmsg_free(msg);
106262306a36Sopenharmony_cierr:
106362306a36Sopenharmony_ci	ib_device_put(device);
106462306a36Sopenharmony_ci	return err;
106562306a36Sopenharmony_ci}
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_cistatic int nldev_set_doit(struct sk_buff *skb, struct nlmsghdr *nlh,
106862306a36Sopenharmony_ci			  struct netlink_ext_ack *extack)
106962306a36Sopenharmony_ci{
107062306a36Sopenharmony_ci	struct nlattr *tb[RDMA_NLDEV_ATTR_MAX];
107162306a36Sopenharmony_ci	struct ib_device *device;
107262306a36Sopenharmony_ci	u32 index;
107362306a36Sopenharmony_ci	int err;
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci	err = nlmsg_parse_deprecated(nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1,
107662306a36Sopenharmony_ci				     nldev_policy, extack);
107762306a36Sopenharmony_ci	if (err || !tb[RDMA_NLDEV_ATTR_DEV_INDEX])
107862306a36Sopenharmony_ci		return -EINVAL;
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci	index = nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]);
108162306a36Sopenharmony_ci	device = ib_device_get_by_index(sock_net(skb->sk), index);
108262306a36Sopenharmony_ci	if (!device)
108362306a36Sopenharmony_ci		return -EINVAL;
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci	if (tb[RDMA_NLDEV_ATTR_DEV_NAME]) {
108662306a36Sopenharmony_ci		char name[IB_DEVICE_NAME_MAX] = {};
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci		nla_strscpy(name, tb[RDMA_NLDEV_ATTR_DEV_NAME],
108962306a36Sopenharmony_ci			    IB_DEVICE_NAME_MAX);
109062306a36Sopenharmony_ci		if (strlen(name) == 0) {
109162306a36Sopenharmony_ci			err = -EINVAL;
109262306a36Sopenharmony_ci			goto done;
109362306a36Sopenharmony_ci		}
109462306a36Sopenharmony_ci		err = ib_device_rename(device, name);
109562306a36Sopenharmony_ci		goto done;
109662306a36Sopenharmony_ci	}
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	if (tb[RDMA_NLDEV_NET_NS_FD]) {
109962306a36Sopenharmony_ci		u32 ns_fd;
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci		ns_fd = nla_get_u32(tb[RDMA_NLDEV_NET_NS_FD]);
110262306a36Sopenharmony_ci		err = ib_device_set_netns_put(skb, device, ns_fd);
110362306a36Sopenharmony_ci		goto put_done;
110462306a36Sopenharmony_ci	}
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci	if (tb[RDMA_NLDEV_ATTR_DEV_DIM]) {
110762306a36Sopenharmony_ci		u8 use_dim;
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci		use_dim = nla_get_u8(tb[RDMA_NLDEV_ATTR_DEV_DIM]);
111062306a36Sopenharmony_ci		err = ib_device_set_dim(device,  use_dim);
111162306a36Sopenharmony_ci		goto done;
111262306a36Sopenharmony_ci	}
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_cidone:
111562306a36Sopenharmony_ci	ib_device_put(device);
111662306a36Sopenharmony_ciput_done:
111762306a36Sopenharmony_ci	return err;
111862306a36Sopenharmony_ci}
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_cistatic int _nldev_get_dumpit(struct ib_device *device,
112162306a36Sopenharmony_ci			     struct sk_buff *skb,
112262306a36Sopenharmony_ci			     struct netlink_callback *cb,
112362306a36Sopenharmony_ci			     unsigned int idx)
112462306a36Sopenharmony_ci{
112562306a36Sopenharmony_ci	int start = cb->args[0];
112662306a36Sopenharmony_ci	struct nlmsghdr *nlh;
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci	if (idx < start)
112962306a36Sopenharmony_ci		return 0;
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci	nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
113262306a36Sopenharmony_ci			RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, RDMA_NLDEV_CMD_GET),
113362306a36Sopenharmony_ci			0, NLM_F_MULTI);
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	if (!nlh || fill_dev_info(skb, device)) {
113662306a36Sopenharmony_ci		nlmsg_cancel(skb, nlh);
113762306a36Sopenharmony_ci		goto out;
113862306a36Sopenharmony_ci	}
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_ci	nlmsg_end(skb, nlh);
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci	idx++;
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ciout:	cb->args[0] = idx;
114562306a36Sopenharmony_ci	return skb->len;
114662306a36Sopenharmony_ci}
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_cistatic int nldev_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
114962306a36Sopenharmony_ci{
115062306a36Sopenharmony_ci	/*
115162306a36Sopenharmony_ci	 * There is no need to take lock, because
115262306a36Sopenharmony_ci	 * we are relying on ib_core's locking.
115362306a36Sopenharmony_ci	 */
115462306a36Sopenharmony_ci	return ib_enum_all_devs(_nldev_get_dumpit, skb, cb);
115562306a36Sopenharmony_ci}
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_cistatic int nldev_port_get_doit(struct sk_buff *skb, struct nlmsghdr *nlh,
115862306a36Sopenharmony_ci			       struct netlink_ext_ack *extack)
115962306a36Sopenharmony_ci{
116062306a36Sopenharmony_ci	struct nlattr *tb[RDMA_NLDEV_ATTR_MAX];
116162306a36Sopenharmony_ci	struct ib_device *device;
116262306a36Sopenharmony_ci	struct sk_buff *msg;
116362306a36Sopenharmony_ci	u32 index;
116462306a36Sopenharmony_ci	u32 port;
116562306a36Sopenharmony_ci	int err;
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_ci	err = nlmsg_parse_deprecated(nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1,
116862306a36Sopenharmony_ci				     nldev_policy, extack);
116962306a36Sopenharmony_ci	if (err ||
117062306a36Sopenharmony_ci	    !tb[RDMA_NLDEV_ATTR_DEV_INDEX] ||
117162306a36Sopenharmony_ci	    !tb[RDMA_NLDEV_ATTR_PORT_INDEX])
117262306a36Sopenharmony_ci		return -EINVAL;
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci	index = nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]);
117562306a36Sopenharmony_ci	device = ib_device_get_by_index(sock_net(skb->sk), index);
117662306a36Sopenharmony_ci	if (!device)
117762306a36Sopenharmony_ci		return -EINVAL;
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci	port = nla_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]);
118062306a36Sopenharmony_ci	if (!rdma_is_port_valid(device, port)) {
118162306a36Sopenharmony_ci		err = -EINVAL;
118262306a36Sopenharmony_ci		goto err;
118362306a36Sopenharmony_ci	}
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
118662306a36Sopenharmony_ci	if (!msg) {
118762306a36Sopenharmony_ci		err = -ENOMEM;
118862306a36Sopenharmony_ci		goto err;
118962306a36Sopenharmony_ci	}
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci	nlh = nlmsg_put(msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq,
119262306a36Sopenharmony_ci			RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, RDMA_NLDEV_CMD_GET),
119362306a36Sopenharmony_ci			0, 0);
119462306a36Sopenharmony_ci	if (!nlh) {
119562306a36Sopenharmony_ci		err = -EMSGSIZE;
119662306a36Sopenharmony_ci		goto err_free;
119762306a36Sopenharmony_ci	}
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ci	err = fill_port_info(msg, device, port, sock_net(skb->sk));
120062306a36Sopenharmony_ci	if (err)
120162306a36Sopenharmony_ci		goto err_free;
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_ci	nlmsg_end(msg, nlh);
120462306a36Sopenharmony_ci	ib_device_put(device);
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci	return rdma_nl_unicast(sock_net(skb->sk), msg, NETLINK_CB(skb).portid);
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_cierr_free:
120962306a36Sopenharmony_ci	nlmsg_free(msg);
121062306a36Sopenharmony_cierr:
121162306a36Sopenharmony_ci	ib_device_put(device);
121262306a36Sopenharmony_ci	return err;
121362306a36Sopenharmony_ci}
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_cistatic int nldev_port_get_dumpit(struct sk_buff *skb,
121662306a36Sopenharmony_ci				 struct netlink_callback *cb)
121762306a36Sopenharmony_ci{
121862306a36Sopenharmony_ci	struct nlattr *tb[RDMA_NLDEV_ATTR_MAX];
121962306a36Sopenharmony_ci	struct ib_device *device;
122062306a36Sopenharmony_ci	int start = cb->args[0];
122162306a36Sopenharmony_ci	struct nlmsghdr *nlh;
122262306a36Sopenharmony_ci	u32 idx = 0;
122362306a36Sopenharmony_ci	u32 ifindex;
122462306a36Sopenharmony_ci	int err;
122562306a36Sopenharmony_ci	unsigned int p;
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci	err = nlmsg_parse_deprecated(cb->nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1,
122862306a36Sopenharmony_ci				     nldev_policy, NULL);
122962306a36Sopenharmony_ci	if (err || !tb[RDMA_NLDEV_ATTR_DEV_INDEX])
123062306a36Sopenharmony_ci		return -EINVAL;
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci	ifindex = nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]);
123362306a36Sopenharmony_ci	device = ib_device_get_by_index(sock_net(skb->sk), ifindex);
123462306a36Sopenharmony_ci	if (!device)
123562306a36Sopenharmony_ci		return -EINVAL;
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci	rdma_for_each_port (device, p) {
123862306a36Sopenharmony_ci		/*
123962306a36Sopenharmony_ci		 * The dumpit function returns all information from specific
124062306a36Sopenharmony_ci		 * index. This specific index is taken from the netlink
124162306a36Sopenharmony_ci		 * messages request sent by user and it is available
124262306a36Sopenharmony_ci		 * in cb->args[0].
124362306a36Sopenharmony_ci		 *
124462306a36Sopenharmony_ci		 * Usually, the user doesn't fill this field and it causes
124562306a36Sopenharmony_ci		 * to return everything.
124662306a36Sopenharmony_ci		 *
124762306a36Sopenharmony_ci		 */
124862306a36Sopenharmony_ci		if (idx < start) {
124962306a36Sopenharmony_ci			idx++;
125062306a36Sopenharmony_ci			continue;
125162306a36Sopenharmony_ci		}
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_ci		nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid,
125462306a36Sopenharmony_ci				cb->nlh->nlmsg_seq,
125562306a36Sopenharmony_ci				RDMA_NL_GET_TYPE(RDMA_NL_NLDEV,
125662306a36Sopenharmony_ci						 RDMA_NLDEV_CMD_PORT_GET),
125762306a36Sopenharmony_ci				0, NLM_F_MULTI);
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci		if (!nlh || fill_port_info(skb, device, p, sock_net(skb->sk))) {
126062306a36Sopenharmony_ci			nlmsg_cancel(skb, nlh);
126162306a36Sopenharmony_ci			goto out;
126262306a36Sopenharmony_ci		}
126362306a36Sopenharmony_ci		idx++;
126462306a36Sopenharmony_ci		nlmsg_end(skb, nlh);
126562306a36Sopenharmony_ci	}
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ciout:
126862306a36Sopenharmony_ci	ib_device_put(device);
126962306a36Sopenharmony_ci	cb->args[0] = idx;
127062306a36Sopenharmony_ci	return skb->len;
127162306a36Sopenharmony_ci}
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_cistatic int nldev_res_get_doit(struct sk_buff *skb, struct nlmsghdr *nlh,
127462306a36Sopenharmony_ci			      struct netlink_ext_ack *extack)
127562306a36Sopenharmony_ci{
127662306a36Sopenharmony_ci	struct nlattr *tb[RDMA_NLDEV_ATTR_MAX];
127762306a36Sopenharmony_ci	struct ib_device *device;
127862306a36Sopenharmony_ci	struct sk_buff *msg;
127962306a36Sopenharmony_ci	u32 index;
128062306a36Sopenharmony_ci	int ret;
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci	ret = nlmsg_parse_deprecated(nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1,
128362306a36Sopenharmony_ci				     nldev_policy, extack);
128462306a36Sopenharmony_ci	if (ret || !tb[RDMA_NLDEV_ATTR_DEV_INDEX])
128562306a36Sopenharmony_ci		return -EINVAL;
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ci	index = nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]);
128862306a36Sopenharmony_ci	device = ib_device_get_by_index(sock_net(skb->sk), index);
128962306a36Sopenharmony_ci	if (!device)
129062306a36Sopenharmony_ci		return -EINVAL;
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ci	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
129362306a36Sopenharmony_ci	if (!msg) {
129462306a36Sopenharmony_ci		ret = -ENOMEM;
129562306a36Sopenharmony_ci		goto err;
129662306a36Sopenharmony_ci	}
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci	nlh = nlmsg_put(msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq,
129962306a36Sopenharmony_ci			RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, RDMA_NLDEV_CMD_RES_GET),
130062306a36Sopenharmony_ci			0, 0);
130162306a36Sopenharmony_ci	if (!nlh) {
130262306a36Sopenharmony_ci		ret = -EMSGSIZE;
130362306a36Sopenharmony_ci		goto err_free;
130462306a36Sopenharmony_ci	}
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_ci	ret = fill_res_info(msg, device);
130762306a36Sopenharmony_ci	if (ret)
130862306a36Sopenharmony_ci		goto err_free;
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_ci	nlmsg_end(msg, nlh);
131162306a36Sopenharmony_ci	ib_device_put(device);
131262306a36Sopenharmony_ci	return rdma_nl_unicast(sock_net(skb->sk), msg, NETLINK_CB(skb).portid);
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_cierr_free:
131562306a36Sopenharmony_ci	nlmsg_free(msg);
131662306a36Sopenharmony_cierr:
131762306a36Sopenharmony_ci	ib_device_put(device);
131862306a36Sopenharmony_ci	return ret;
131962306a36Sopenharmony_ci}
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_cistatic int _nldev_res_get_dumpit(struct ib_device *device,
132262306a36Sopenharmony_ci				 struct sk_buff *skb,
132362306a36Sopenharmony_ci				 struct netlink_callback *cb,
132462306a36Sopenharmony_ci				 unsigned int idx)
132562306a36Sopenharmony_ci{
132662306a36Sopenharmony_ci	int start = cb->args[0];
132762306a36Sopenharmony_ci	struct nlmsghdr *nlh;
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci	if (idx < start)
133062306a36Sopenharmony_ci		return 0;
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_ci	nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
133362306a36Sopenharmony_ci			RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, RDMA_NLDEV_CMD_RES_GET),
133462306a36Sopenharmony_ci			0, NLM_F_MULTI);
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci	if (!nlh || fill_res_info(skb, device)) {
133762306a36Sopenharmony_ci		nlmsg_cancel(skb, nlh);
133862306a36Sopenharmony_ci		goto out;
133962306a36Sopenharmony_ci	}
134062306a36Sopenharmony_ci	nlmsg_end(skb, nlh);
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_ci	idx++;
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ciout:
134562306a36Sopenharmony_ci	cb->args[0] = idx;
134662306a36Sopenharmony_ci	return skb->len;
134762306a36Sopenharmony_ci}
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_cistatic int nldev_res_get_dumpit(struct sk_buff *skb,
135062306a36Sopenharmony_ci				struct netlink_callback *cb)
135162306a36Sopenharmony_ci{
135262306a36Sopenharmony_ci	return ib_enum_all_devs(_nldev_res_get_dumpit, skb, cb);
135362306a36Sopenharmony_ci}
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_cistruct nldev_fill_res_entry {
135662306a36Sopenharmony_ci	enum rdma_nldev_attr nldev_attr;
135762306a36Sopenharmony_ci	u8 flags;
135862306a36Sopenharmony_ci	u32 entry;
135962306a36Sopenharmony_ci	u32 id;
136062306a36Sopenharmony_ci};
136162306a36Sopenharmony_ci
136262306a36Sopenharmony_cienum nldev_res_flags {
136362306a36Sopenharmony_ci	NLDEV_PER_DEV = 1 << 0,
136462306a36Sopenharmony_ci};
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_cistatic const struct nldev_fill_res_entry fill_entries[RDMA_RESTRACK_MAX] = {
136762306a36Sopenharmony_ci	[RDMA_RESTRACK_QP] = {
136862306a36Sopenharmony_ci		.nldev_attr = RDMA_NLDEV_ATTR_RES_QP,
136962306a36Sopenharmony_ci		.entry = RDMA_NLDEV_ATTR_RES_QP_ENTRY,
137062306a36Sopenharmony_ci		.id = RDMA_NLDEV_ATTR_RES_LQPN,
137162306a36Sopenharmony_ci	},
137262306a36Sopenharmony_ci	[RDMA_RESTRACK_CM_ID] = {
137362306a36Sopenharmony_ci		.nldev_attr = RDMA_NLDEV_ATTR_RES_CM_ID,
137462306a36Sopenharmony_ci		.entry = RDMA_NLDEV_ATTR_RES_CM_ID_ENTRY,
137562306a36Sopenharmony_ci		.id = RDMA_NLDEV_ATTR_RES_CM_IDN,
137662306a36Sopenharmony_ci	},
137762306a36Sopenharmony_ci	[RDMA_RESTRACK_CQ] = {
137862306a36Sopenharmony_ci		.nldev_attr = RDMA_NLDEV_ATTR_RES_CQ,
137962306a36Sopenharmony_ci		.flags = NLDEV_PER_DEV,
138062306a36Sopenharmony_ci		.entry = RDMA_NLDEV_ATTR_RES_CQ_ENTRY,
138162306a36Sopenharmony_ci		.id = RDMA_NLDEV_ATTR_RES_CQN,
138262306a36Sopenharmony_ci	},
138362306a36Sopenharmony_ci	[RDMA_RESTRACK_MR] = {
138462306a36Sopenharmony_ci		.nldev_attr = RDMA_NLDEV_ATTR_RES_MR,
138562306a36Sopenharmony_ci		.flags = NLDEV_PER_DEV,
138662306a36Sopenharmony_ci		.entry = RDMA_NLDEV_ATTR_RES_MR_ENTRY,
138762306a36Sopenharmony_ci		.id = RDMA_NLDEV_ATTR_RES_MRN,
138862306a36Sopenharmony_ci	},
138962306a36Sopenharmony_ci	[RDMA_RESTRACK_PD] = {
139062306a36Sopenharmony_ci		.nldev_attr = RDMA_NLDEV_ATTR_RES_PD,
139162306a36Sopenharmony_ci		.flags = NLDEV_PER_DEV,
139262306a36Sopenharmony_ci		.entry = RDMA_NLDEV_ATTR_RES_PD_ENTRY,
139362306a36Sopenharmony_ci		.id = RDMA_NLDEV_ATTR_RES_PDN,
139462306a36Sopenharmony_ci	},
139562306a36Sopenharmony_ci	[RDMA_RESTRACK_COUNTER] = {
139662306a36Sopenharmony_ci		.nldev_attr = RDMA_NLDEV_ATTR_STAT_COUNTER,
139762306a36Sopenharmony_ci		.entry = RDMA_NLDEV_ATTR_STAT_COUNTER_ENTRY,
139862306a36Sopenharmony_ci		.id = RDMA_NLDEV_ATTR_STAT_COUNTER_ID,
139962306a36Sopenharmony_ci	},
140062306a36Sopenharmony_ci	[RDMA_RESTRACK_CTX] = {
140162306a36Sopenharmony_ci		.nldev_attr = RDMA_NLDEV_ATTR_RES_CTX,
140262306a36Sopenharmony_ci		.flags = NLDEV_PER_DEV,
140362306a36Sopenharmony_ci		.entry = RDMA_NLDEV_ATTR_RES_CTX_ENTRY,
140462306a36Sopenharmony_ci		.id = RDMA_NLDEV_ATTR_RES_CTXN,
140562306a36Sopenharmony_ci	},
140662306a36Sopenharmony_ci	[RDMA_RESTRACK_SRQ] = {
140762306a36Sopenharmony_ci		.nldev_attr = RDMA_NLDEV_ATTR_RES_SRQ,
140862306a36Sopenharmony_ci		.flags = NLDEV_PER_DEV,
140962306a36Sopenharmony_ci		.entry = RDMA_NLDEV_ATTR_RES_SRQ_ENTRY,
141062306a36Sopenharmony_ci		.id = RDMA_NLDEV_ATTR_RES_SRQN,
141162306a36Sopenharmony_ci	},
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_ci};
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_cistatic int res_get_common_doit(struct sk_buff *skb, struct nlmsghdr *nlh,
141662306a36Sopenharmony_ci			       struct netlink_ext_ack *extack,
141762306a36Sopenharmony_ci			       enum rdma_restrack_type res_type,
141862306a36Sopenharmony_ci			       res_fill_func_t fill_func)
141962306a36Sopenharmony_ci{
142062306a36Sopenharmony_ci	const struct nldev_fill_res_entry *fe = &fill_entries[res_type];
142162306a36Sopenharmony_ci	struct nlattr *tb[RDMA_NLDEV_ATTR_MAX];
142262306a36Sopenharmony_ci	struct rdma_restrack_entry *res;
142362306a36Sopenharmony_ci	struct ib_device *device;
142462306a36Sopenharmony_ci	u32 index, id, port = 0;
142562306a36Sopenharmony_ci	bool has_cap_net_admin;
142662306a36Sopenharmony_ci	struct sk_buff *msg;
142762306a36Sopenharmony_ci	int ret;
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_ci	ret = nlmsg_parse_deprecated(nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1,
143062306a36Sopenharmony_ci				     nldev_policy, extack);
143162306a36Sopenharmony_ci	if (ret || !tb[RDMA_NLDEV_ATTR_DEV_INDEX] || !fe->id || !tb[fe->id])
143262306a36Sopenharmony_ci		return -EINVAL;
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ci	index = nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]);
143562306a36Sopenharmony_ci	device = ib_device_get_by_index(sock_net(skb->sk), index);
143662306a36Sopenharmony_ci	if (!device)
143762306a36Sopenharmony_ci		return -EINVAL;
143862306a36Sopenharmony_ci
143962306a36Sopenharmony_ci	if (tb[RDMA_NLDEV_ATTR_PORT_INDEX]) {
144062306a36Sopenharmony_ci		port = nla_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]);
144162306a36Sopenharmony_ci		if (!rdma_is_port_valid(device, port)) {
144262306a36Sopenharmony_ci			ret = -EINVAL;
144362306a36Sopenharmony_ci			goto err;
144462306a36Sopenharmony_ci		}
144562306a36Sopenharmony_ci	}
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_ci	if ((port && fe->flags & NLDEV_PER_DEV) ||
144862306a36Sopenharmony_ci	    (!port && ~fe->flags & NLDEV_PER_DEV)) {
144962306a36Sopenharmony_ci		ret = -EINVAL;
145062306a36Sopenharmony_ci		goto err;
145162306a36Sopenharmony_ci	}
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_ci	id = nla_get_u32(tb[fe->id]);
145462306a36Sopenharmony_ci	res = rdma_restrack_get_byid(device, res_type, id);
145562306a36Sopenharmony_ci	if (IS_ERR(res)) {
145662306a36Sopenharmony_ci		ret = PTR_ERR(res);
145762306a36Sopenharmony_ci		goto err;
145862306a36Sopenharmony_ci	}
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
146162306a36Sopenharmony_ci	if (!msg) {
146262306a36Sopenharmony_ci		ret = -ENOMEM;
146362306a36Sopenharmony_ci		goto err_get;
146462306a36Sopenharmony_ci	}
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ci	nlh = nlmsg_put(msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq,
146762306a36Sopenharmony_ci			RDMA_NL_GET_TYPE(RDMA_NL_NLDEV,
146862306a36Sopenharmony_ci					 RDMA_NL_GET_OP(nlh->nlmsg_type)),
146962306a36Sopenharmony_ci			0, 0);
147062306a36Sopenharmony_ci
147162306a36Sopenharmony_ci	if (!nlh || fill_nldev_handle(msg, device)) {
147262306a36Sopenharmony_ci		ret = -EMSGSIZE;
147362306a36Sopenharmony_ci		goto err_free;
147462306a36Sopenharmony_ci	}
147562306a36Sopenharmony_ci
147662306a36Sopenharmony_ci	has_cap_net_admin = netlink_capable(skb, CAP_NET_ADMIN);
147762306a36Sopenharmony_ci
147862306a36Sopenharmony_ci	ret = fill_func(msg, has_cap_net_admin, res, port);
147962306a36Sopenharmony_ci	if (ret)
148062306a36Sopenharmony_ci		goto err_free;
148162306a36Sopenharmony_ci
148262306a36Sopenharmony_ci	rdma_restrack_put(res);
148362306a36Sopenharmony_ci	nlmsg_end(msg, nlh);
148462306a36Sopenharmony_ci	ib_device_put(device);
148562306a36Sopenharmony_ci	return rdma_nl_unicast(sock_net(skb->sk), msg, NETLINK_CB(skb).portid);
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_cierr_free:
148862306a36Sopenharmony_ci	nlmsg_free(msg);
148962306a36Sopenharmony_cierr_get:
149062306a36Sopenharmony_ci	rdma_restrack_put(res);
149162306a36Sopenharmony_cierr:
149262306a36Sopenharmony_ci	ib_device_put(device);
149362306a36Sopenharmony_ci	return ret;
149462306a36Sopenharmony_ci}
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_cistatic int res_get_common_dumpit(struct sk_buff *skb,
149762306a36Sopenharmony_ci				 struct netlink_callback *cb,
149862306a36Sopenharmony_ci				 enum rdma_restrack_type res_type,
149962306a36Sopenharmony_ci				 res_fill_func_t fill_func)
150062306a36Sopenharmony_ci{
150162306a36Sopenharmony_ci	const struct nldev_fill_res_entry *fe = &fill_entries[res_type];
150262306a36Sopenharmony_ci	struct nlattr *tb[RDMA_NLDEV_ATTR_MAX];
150362306a36Sopenharmony_ci	struct rdma_restrack_entry *res;
150462306a36Sopenharmony_ci	struct rdma_restrack_root *rt;
150562306a36Sopenharmony_ci	int err, ret = 0, idx = 0;
150662306a36Sopenharmony_ci	struct nlattr *table_attr;
150762306a36Sopenharmony_ci	struct nlattr *entry_attr;
150862306a36Sopenharmony_ci	struct ib_device *device;
150962306a36Sopenharmony_ci	int start = cb->args[0];
151062306a36Sopenharmony_ci	bool has_cap_net_admin;
151162306a36Sopenharmony_ci	struct nlmsghdr *nlh;
151262306a36Sopenharmony_ci	unsigned long id;
151362306a36Sopenharmony_ci	u32 index, port = 0;
151462306a36Sopenharmony_ci	bool filled = false;
151562306a36Sopenharmony_ci
151662306a36Sopenharmony_ci	err = nlmsg_parse_deprecated(cb->nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1,
151762306a36Sopenharmony_ci				     nldev_policy, NULL);
151862306a36Sopenharmony_ci	/*
151962306a36Sopenharmony_ci	 * Right now, we are expecting the device index to get res information,
152062306a36Sopenharmony_ci	 * but it is possible to extend this code to return all devices in
152162306a36Sopenharmony_ci	 * one shot by checking the existence of RDMA_NLDEV_ATTR_DEV_INDEX.
152262306a36Sopenharmony_ci	 * if it doesn't exist, we will iterate over all devices.
152362306a36Sopenharmony_ci	 *
152462306a36Sopenharmony_ci	 * But it is not needed for now.
152562306a36Sopenharmony_ci	 */
152662306a36Sopenharmony_ci	if (err || !tb[RDMA_NLDEV_ATTR_DEV_INDEX])
152762306a36Sopenharmony_ci		return -EINVAL;
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_ci	index = nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]);
153062306a36Sopenharmony_ci	device = ib_device_get_by_index(sock_net(skb->sk), index);
153162306a36Sopenharmony_ci	if (!device)
153262306a36Sopenharmony_ci		return -EINVAL;
153362306a36Sopenharmony_ci
153462306a36Sopenharmony_ci	/*
153562306a36Sopenharmony_ci	 * If no PORT_INDEX is supplied, we will return all QPs from that device
153662306a36Sopenharmony_ci	 */
153762306a36Sopenharmony_ci	if (tb[RDMA_NLDEV_ATTR_PORT_INDEX]) {
153862306a36Sopenharmony_ci		port = nla_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]);
153962306a36Sopenharmony_ci		if (!rdma_is_port_valid(device, port)) {
154062306a36Sopenharmony_ci			ret = -EINVAL;
154162306a36Sopenharmony_ci			goto err_index;
154262306a36Sopenharmony_ci		}
154362306a36Sopenharmony_ci	}
154462306a36Sopenharmony_ci
154562306a36Sopenharmony_ci	nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
154662306a36Sopenharmony_ci			RDMA_NL_GET_TYPE(RDMA_NL_NLDEV,
154762306a36Sopenharmony_ci					 RDMA_NL_GET_OP(cb->nlh->nlmsg_type)),
154862306a36Sopenharmony_ci			0, NLM_F_MULTI);
154962306a36Sopenharmony_ci
155062306a36Sopenharmony_ci	if (!nlh || fill_nldev_handle(skb, device)) {
155162306a36Sopenharmony_ci		ret = -EMSGSIZE;
155262306a36Sopenharmony_ci		goto err;
155362306a36Sopenharmony_ci	}
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_ci	table_attr = nla_nest_start_noflag(skb, fe->nldev_attr);
155662306a36Sopenharmony_ci	if (!table_attr) {
155762306a36Sopenharmony_ci		ret = -EMSGSIZE;
155862306a36Sopenharmony_ci		goto err;
155962306a36Sopenharmony_ci	}
156062306a36Sopenharmony_ci
156162306a36Sopenharmony_ci	has_cap_net_admin = netlink_capable(cb->skb, CAP_NET_ADMIN);
156262306a36Sopenharmony_ci
156362306a36Sopenharmony_ci	rt = &device->res[res_type];
156462306a36Sopenharmony_ci	xa_lock(&rt->xa);
156562306a36Sopenharmony_ci	/*
156662306a36Sopenharmony_ci	 * FIXME: if the skip ahead is something common this loop should
156762306a36Sopenharmony_ci	 * use xas_for_each & xas_pause to optimize, we can have a lot of
156862306a36Sopenharmony_ci	 * objects.
156962306a36Sopenharmony_ci	 */
157062306a36Sopenharmony_ci	xa_for_each(&rt->xa, id, res) {
157162306a36Sopenharmony_ci		if (idx < start || !rdma_restrack_get(res))
157262306a36Sopenharmony_ci			goto next;
157362306a36Sopenharmony_ci
157462306a36Sopenharmony_ci		xa_unlock(&rt->xa);
157562306a36Sopenharmony_ci
157662306a36Sopenharmony_ci		filled = true;
157762306a36Sopenharmony_ci
157862306a36Sopenharmony_ci		entry_attr = nla_nest_start_noflag(skb, fe->entry);
157962306a36Sopenharmony_ci		if (!entry_attr) {
158062306a36Sopenharmony_ci			ret = -EMSGSIZE;
158162306a36Sopenharmony_ci			rdma_restrack_put(res);
158262306a36Sopenharmony_ci			goto msg_full;
158362306a36Sopenharmony_ci		}
158462306a36Sopenharmony_ci
158562306a36Sopenharmony_ci		ret = fill_func(skb, has_cap_net_admin, res, port);
158662306a36Sopenharmony_ci
158762306a36Sopenharmony_ci		rdma_restrack_put(res);
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_ci		if (ret) {
159062306a36Sopenharmony_ci			nla_nest_cancel(skb, entry_attr);
159162306a36Sopenharmony_ci			if (ret == -EMSGSIZE)
159262306a36Sopenharmony_ci				goto msg_full;
159362306a36Sopenharmony_ci			if (ret == -EAGAIN)
159462306a36Sopenharmony_ci				goto again;
159562306a36Sopenharmony_ci			goto res_err;
159662306a36Sopenharmony_ci		}
159762306a36Sopenharmony_ci		nla_nest_end(skb, entry_attr);
159862306a36Sopenharmony_ciagain:		xa_lock(&rt->xa);
159962306a36Sopenharmony_cinext:		idx++;
160062306a36Sopenharmony_ci	}
160162306a36Sopenharmony_ci	xa_unlock(&rt->xa);
160262306a36Sopenharmony_ci
160362306a36Sopenharmony_cimsg_full:
160462306a36Sopenharmony_ci	nla_nest_end(skb, table_attr);
160562306a36Sopenharmony_ci	nlmsg_end(skb, nlh);
160662306a36Sopenharmony_ci	cb->args[0] = idx;
160762306a36Sopenharmony_ci
160862306a36Sopenharmony_ci	/*
160962306a36Sopenharmony_ci	 * No more entries to fill, cancel the message and
161062306a36Sopenharmony_ci	 * return 0 to mark end of dumpit.
161162306a36Sopenharmony_ci	 */
161262306a36Sopenharmony_ci	if (!filled)
161362306a36Sopenharmony_ci		goto err;
161462306a36Sopenharmony_ci
161562306a36Sopenharmony_ci	ib_device_put(device);
161662306a36Sopenharmony_ci	return skb->len;
161762306a36Sopenharmony_ci
161862306a36Sopenharmony_cires_err:
161962306a36Sopenharmony_ci	nla_nest_cancel(skb, table_attr);
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_cierr:
162262306a36Sopenharmony_ci	nlmsg_cancel(skb, nlh);
162362306a36Sopenharmony_ci
162462306a36Sopenharmony_cierr_index:
162562306a36Sopenharmony_ci	ib_device_put(device);
162662306a36Sopenharmony_ci	return ret;
162762306a36Sopenharmony_ci}
162862306a36Sopenharmony_ci
162962306a36Sopenharmony_ci#define RES_GET_FUNCS(name, type)                                              \
163062306a36Sopenharmony_ci	static int nldev_res_get_##name##_dumpit(struct sk_buff *skb,          \
163162306a36Sopenharmony_ci						 struct netlink_callback *cb)  \
163262306a36Sopenharmony_ci	{                                                                      \
163362306a36Sopenharmony_ci		return res_get_common_dumpit(skb, cb, type,                    \
163462306a36Sopenharmony_ci					     fill_res_##name##_entry);         \
163562306a36Sopenharmony_ci	}                                                                      \
163662306a36Sopenharmony_ci	static int nldev_res_get_##name##_doit(struct sk_buff *skb,            \
163762306a36Sopenharmony_ci					       struct nlmsghdr *nlh,           \
163862306a36Sopenharmony_ci					       struct netlink_ext_ack *extack) \
163962306a36Sopenharmony_ci	{                                                                      \
164062306a36Sopenharmony_ci		return res_get_common_doit(skb, nlh, extack, type,             \
164162306a36Sopenharmony_ci					   fill_res_##name##_entry);           \
164262306a36Sopenharmony_ci	}
164362306a36Sopenharmony_ci
164462306a36Sopenharmony_ciRES_GET_FUNCS(qp, RDMA_RESTRACK_QP);
164562306a36Sopenharmony_ciRES_GET_FUNCS(qp_raw, RDMA_RESTRACK_QP);
164662306a36Sopenharmony_ciRES_GET_FUNCS(cm_id, RDMA_RESTRACK_CM_ID);
164762306a36Sopenharmony_ciRES_GET_FUNCS(cq, RDMA_RESTRACK_CQ);
164862306a36Sopenharmony_ciRES_GET_FUNCS(cq_raw, RDMA_RESTRACK_CQ);
164962306a36Sopenharmony_ciRES_GET_FUNCS(pd, RDMA_RESTRACK_PD);
165062306a36Sopenharmony_ciRES_GET_FUNCS(mr, RDMA_RESTRACK_MR);
165162306a36Sopenharmony_ciRES_GET_FUNCS(mr_raw, RDMA_RESTRACK_MR);
165262306a36Sopenharmony_ciRES_GET_FUNCS(counter, RDMA_RESTRACK_COUNTER);
165362306a36Sopenharmony_ciRES_GET_FUNCS(ctx, RDMA_RESTRACK_CTX);
165462306a36Sopenharmony_ciRES_GET_FUNCS(srq, RDMA_RESTRACK_SRQ);
165562306a36Sopenharmony_ci
165662306a36Sopenharmony_cistatic LIST_HEAD(link_ops);
165762306a36Sopenharmony_cistatic DECLARE_RWSEM(link_ops_rwsem);
165862306a36Sopenharmony_ci
165962306a36Sopenharmony_cistatic const struct rdma_link_ops *link_ops_get(const char *type)
166062306a36Sopenharmony_ci{
166162306a36Sopenharmony_ci	const struct rdma_link_ops *ops;
166262306a36Sopenharmony_ci
166362306a36Sopenharmony_ci	list_for_each_entry(ops, &link_ops, list) {
166462306a36Sopenharmony_ci		if (!strcmp(ops->type, type))
166562306a36Sopenharmony_ci			goto out;
166662306a36Sopenharmony_ci	}
166762306a36Sopenharmony_ci	ops = NULL;
166862306a36Sopenharmony_ciout:
166962306a36Sopenharmony_ci	return ops;
167062306a36Sopenharmony_ci}
167162306a36Sopenharmony_ci
167262306a36Sopenharmony_civoid rdma_link_register(struct rdma_link_ops *ops)
167362306a36Sopenharmony_ci{
167462306a36Sopenharmony_ci	down_write(&link_ops_rwsem);
167562306a36Sopenharmony_ci	if (WARN_ON_ONCE(link_ops_get(ops->type)))
167662306a36Sopenharmony_ci		goto out;
167762306a36Sopenharmony_ci	list_add(&ops->list, &link_ops);
167862306a36Sopenharmony_ciout:
167962306a36Sopenharmony_ci	up_write(&link_ops_rwsem);
168062306a36Sopenharmony_ci}
168162306a36Sopenharmony_ciEXPORT_SYMBOL(rdma_link_register);
168262306a36Sopenharmony_ci
168362306a36Sopenharmony_civoid rdma_link_unregister(struct rdma_link_ops *ops)
168462306a36Sopenharmony_ci{
168562306a36Sopenharmony_ci	down_write(&link_ops_rwsem);
168662306a36Sopenharmony_ci	list_del(&ops->list);
168762306a36Sopenharmony_ci	up_write(&link_ops_rwsem);
168862306a36Sopenharmony_ci}
168962306a36Sopenharmony_ciEXPORT_SYMBOL(rdma_link_unregister);
169062306a36Sopenharmony_ci
169162306a36Sopenharmony_cistatic int nldev_newlink(struct sk_buff *skb, struct nlmsghdr *nlh,
169262306a36Sopenharmony_ci			  struct netlink_ext_ack *extack)
169362306a36Sopenharmony_ci{
169462306a36Sopenharmony_ci	struct nlattr *tb[RDMA_NLDEV_ATTR_MAX];
169562306a36Sopenharmony_ci	char ibdev_name[IB_DEVICE_NAME_MAX];
169662306a36Sopenharmony_ci	const struct rdma_link_ops *ops;
169762306a36Sopenharmony_ci	char ndev_name[IFNAMSIZ];
169862306a36Sopenharmony_ci	struct net_device *ndev;
169962306a36Sopenharmony_ci	char type[IFNAMSIZ];
170062306a36Sopenharmony_ci	int err;
170162306a36Sopenharmony_ci
170262306a36Sopenharmony_ci	err = nlmsg_parse_deprecated(nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1,
170362306a36Sopenharmony_ci				     nldev_policy, extack);
170462306a36Sopenharmony_ci	if (err || !tb[RDMA_NLDEV_ATTR_DEV_NAME] ||
170562306a36Sopenharmony_ci	    !tb[RDMA_NLDEV_ATTR_LINK_TYPE] || !tb[RDMA_NLDEV_ATTR_NDEV_NAME])
170662306a36Sopenharmony_ci		return -EINVAL;
170762306a36Sopenharmony_ci
170862306a36Sopenharmony_ci	nla_strscpy(ibdev_name, tb[RDMA_NLDEV_ATTR_DEV_NAME],
170962306a36Sopenharmony_ci		    sizeof(ibdev_name));
171062306a36Sopenharmony_ci	if (strchr(ibdev_name, '%') || strlen(ibdev_name) == 0)
171162306a36Sopenharmony_ci		return -EINVAL;
171262306a36Sopenharmony_ci
171362306a36Sopenharmony_ci	nla_strscpy(type, tb[RDMA_NLDEV_ATTR_LINK_TYPE], sizeof(type));
171462306a36Sopenharmony_ci	nla_strscpy(ndev_name, tb[RDMA_NLDEV_ATTR_NDEV_NAME],
171562306a36Sopenharmony_ci		    sizeof(ndev_name));
171662306a36Sopenharmony_ci
171762306a36Sopenharmony_ci	ndev = dev_get_by_name(sock_net(skb->sk), ndev_name);
171862306a36Sopenharmony_ci	if (!ndev)
171962306a36Sopenharmony_ci		return -ENODEV;
172062306a36Sopenharmony_ci
172162306a36Sopenharmony_ci	down_read(&link_ops_rwsem);
172262306a36Sopenharmony_ci	ops = link_ops_get(type);
172362306a36Sopenharmony_ci#ifdef CONFIG_MODULES
172462306a36Sopenharmony_ci	if (!ops) {
172562306a36Sopenharmony_ci		up_read(&link_ops_rwsem);
172662306a36Sopenharmony_ci		request_module("rdma-link-%s", type);
172762306a36Sopenharmony_ci		down_read(&link_ops_rwsem);
172862306a36Sopenharmony_ci		ops = link_ops_get(type);
172962306a36Sopenharmony_ci	}
173062306a36Sopenharmony_ci#endif
173162306a36Sopenharmony_ci	err = ops ? ops->newlink(ibdev_name, ndev) : -EINVAL;
173262306a36Sopenharmony_ci	up_read(&link_ops_rwsem);
173362306a36Sopenharmony_ci	dev_put(ndev);
173462306a36Sopenharmony_ci
173562306a36Sopenharmony_ci	return err;
173662306a36Sopenharmony_ci}
173762306a36Sopenharmony_ci
173862306a36Sopenharmony_cistatic int nldev_dellink(struct sk_buff *skb, struct nlmsghdr *nlh,
173962306a36Sopenharmony_ci			  struct netlink_ext_ack *extack)
174062306a36Sopenharmony_ci{
174162306a36Sopenharmony_ci	struct nlattr *tb[RDMA_NLDEV_ATTR_MAX];
174262306a36Sopenharmony_ci	struct ib_device *device;
174362306a36Sopenharmony_ci	u32 index;
174462306a36Sopenharmony_ci	int err;
174562306a36Sopenharmony_ci
174662306a36Sopenharmony_ci	err = nlmsg_parse_deprecated(nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1,
174762306a36Sopenharmony_ci				     nldev_policy, extack);
174862306a36Sopenharmony_ci	if (err || !tb[RDMA_NLDEV_ATTR_DEV_INDEX])
174962306a36Sopenharmony_ci		return -EINVAL;
175062306a36Sopenharmony_ci
175162306a36Sopenharmony_ci	index = nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]);
175262306a36Sopenharmony_ci	device = ib_device_get_by_index(sock_net(skb->sk), index);
175362306a36Sopenharmony_ci	if (!device)
175462306a36Sopenharmony_ci		return -EINVAL;
175562306a36Sopenharmony_ci
175662306a36Sopenharmony_ci	if (!(device->attrs.kernel_cap_flags & IBK_ALLOW_USER_UNREG)) {
175762306a36Sopenharmony_ci		ib_device_put(device);
175862306a36Sopenharmony_ci		return -EINVAL;
175962306a36Sopenharmony_ci	}
176062306a36Sopenharmony_ci
176162306a36Sopenharmony_ci	ib_unregister_device_and_put(device);
176262306a36Sopenharmony_ci	return 0;
176362306a36Sopenharmony_ci}
176462306a36Sopenharmony_ci
176562306a36Sopenharmony_cistatic int nldev_get_chardev(struct sk_buff *skb, struct nlmsghdr *nlh,
176662306a36Sopenharmony_ci			     struct netlink_ext_ack *extack)
176762306a36Sopenharmony_ci{
176862306a36Sopenharmony_ci	struct nlattr *tb[RDMA_NLDEV_ATTR_MAX];
176962306a36Sopenharmony_ci	char client_name[RDMA_NLDEV_ATTR_CHARDEV_TYPE_SIZE];
177062306a36Sopenharmony_ci	struct ib_client_nl_info data = {};
177162306a36Sopenharmony_ci	struct ib_device *ibdev = NULL;
177262306a36Sopenharmony_ci	struct sk_buff *msg;
177362306a36Sopenharmony_ci	u32 index;
177462306a36Sopenharmony_ci	int err;
177562306a36Sopenharmony_ci
177662306a36Sopenharmony_ci	err = nlmsg_parse(nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1, nldev_policy,
177762306a36Sopenharmony_ci			  extack);
177862306a36Sopenharmony_ci	if (err || !tb[RDMA_NLDEV_ATTR_CHARDEV_TYPE])
177962306a36Sopenharmony_ci		return -EINVAL;
178062306a36Sopenharmony_ci
178162306a36Sopenharmony_ci	nla_strscpy(client_name, tb[RDMA_NLDEV_ATTR_CHARDEV_TYPE],
178262306a36Sopenharmony_ci		    sizeof(client_name));
178362306a36Sopenharmony_ci
178462306a36Sopenharmony_ci	if (tb[RDMA_NLDEV_ATTR_DEV_INDEX]) {
178562306a36Sopenharmony_ci		index = nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]);
178662306a36Sopenharmony_ci		ibdev = ib_device_get_by_index(sock_net(skb->sk), index);
178762306a36Sopenharmony_ci		if (!ibdev)
178862306a36Sopenharmony_ci			return -EINVAL;
178962306a36Sopenharmony_ci
179062306a36Sopenharmony_ci		if (tb[RDMA_NLDEV_ATTR_PORT_INDEX]) {
179162306a36Sopenharmony_ci			data.port = nla_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]);
179262306a36Sopenharmony_ci			if (!rdma_is_port_valid(ibdev, data.port)) {
179362306a36Sopenharmony_ci				err = -EINVAL;
179462306a36Sopenharmony_ci				goto out_put;
179562306a36Sopenharmony_ci			}
179662306a36Sopenharmony_ci		} else {
179762306a36Sopenharmony_ci			data.port = -1;
179862306a36Sopenharmony_ci		}
179962306a36Sopenharmony_ci	} else if (tb[RDMA_NLDEV_ATTR_PORT_INDEX]) {
180062306a36Sopenharmony_ci		return -EINVAL;
180162306a36Sopenharmony_ci	}
180262306a36Sopenharmony_ci
180362306a36Sopenharmony_ci	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
180462306a36Sopenharmony_ci	if (!msg) {
180562306a36Sopenharmony_ci		err = -ENOMEM;
180662306a36Sopenharmony_ci		goto out_put;
180762306a36Sopenharmony_ci	}
180862306a36Sopenharmony_ci	nlh = nlmsg_put(msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq,
180962306a36Sopenharmony_ci			RDMA_NL_GET_TYPE(RDMA_NL_NLDEV,
181062306a36Sopenharmony_ci					 RDMA_NLDEV_CMD_GET_CHARDEV),
181162306a36Sopenharmony_ci			0, 0);
181262306a36Sopenharmony_ci	if (!nlh) {
181362306a36Sopenharmony_ci		err = -EMSGSIZE;
181462306a36Sopenharmony_ci		goto out_nlmsg;
181562306a36Sopenharmony_ci	}
181662306a36Sopenharmony_ci
181762306a36Sopenharmony_ci	data.nl_msg = msg;
181862306a36Sopenharmony_ci	err = ib_get_client_nl_info(ibdev, client_name, &data);
181962306a36Sopenharmony_ci	if (err)
182062306a36Sopenharmony_ci		goto out_nlmsg;
182162306a36Sopenharmony_ci
182262306a36Sopenharmony_ci	err = nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_CHARDEV,
182362306a36Sopenharmony_ci				huge_encode_dev(data.cdev->devt),
182462306a36Sopenharmony_ci				RDMA_NLDEV_ATTR_PAD);
182562306a36Sopenharmony_ci	if (err)
182662306a36Sopenharmony_ci		goto out_data;
182762306a36Sopenharmony_ci	err = nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_CHARDEV_ABI, data.abi,
182862306a36Sopenharmony_ci				RDMA_NLDEV_ATTR_PAD);
182962306a36Sopenharmony_ci	if (err)
183062306a36Sopenharmony_ci		goto out_data;
183162306a36Sopenharmony_ci	if (nla_put_string(msg, RDMA_NLDEV_ATTR_CHARDEV_NAME,
183262306a36Sopenharmony_ci			   dev_name(data.cdev))) {
183362306a36Sopenharmony_ci		err = -EMSGSIZE;
183462306a36Sopenharmony_ci		goto out_data;
183562306a36Sopenharmony_ci	}
183662306a36Sopenharmony_ci
183762306a36Sopenharmony_ci	nlmsg_end(msg, nlh);
183862306a36Sopenharmony_ci	put_device(data.cdev);
183962306a36Sopenharmony_ci	if (ibdev)
184062306a36Sopenharmony_ci		ib_device_put(ibdev);
184162306a36Sopenharmony_ci	return rdma_nl_unicast(sock_net(skb->sk), msg, NETLINK_CB(skb).portid);
184262306a36Sopenharmony_ci
184362306a36Sopenharmony_ciout_data:
184462306a36Sopenharmony_ci	put_device(data.cdev);
184562306a36Sopenharmony_ciout_nlmsg:
184662306a36Sopenharmony_ci	nlmsg_free(msg);
184762306a36Sopenharmony_ciout_put:
184862306a36Sopenharmony_ci	if (ibdev)
184962306a36Sopenharmony_ci		ib_device_put(ibdev);
185062306a36Sopenharmony_ci	return err;
185162306a36Sopenharmony_ci}
185262306a36Sopenharmony_ci
185362306a36Sopenharmony_cistatic int nldev_sys_get_doit(struct sk_buff *skb, struct nlmsghdr *nlh,
185462306a36Sopenharmony_ci			      struct netlink_ext_ack *extack)
185562306a36Sopenharmony_ci{
185662306a36Sopenharmony_ci	struct nlattr *tb[RDMA_NLDEV_ATTR_MAX];
185762306a36Sopenharmony_ci	struct sk_buff *msg;
185862306a36Sopenharmony_ci	int err;
185962306a36Sopenharmony_ci
186062306a36Sopenharmony_ci	err = nlmsg_parse(nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1,
186162306a36Sopenharmony_ci			  nldev_policy, extack);
186262306a36Sopenharmony_ci	if (err)
186362306a36Sopenharmony_ci		return err;
186462306a36Sopenharmony_ci
186562306a36Sopenharmony_ci	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
186662306a36Sopenharmony_ci	if (!msg)
186762306a36Sopenharmony_ci		return -ENOMEM;
186862306a36Sopenharmony_ci
186962306a36Sopenharmony_ci	nlh = nlmsg_put(msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq,
187062306a36Sopenharmony_ci			RDMA_NL_GET_TYPE(RDMA_NL_NLDEV,
187162306a36Sopenharmony_ci					 RDMA_NLDEV_CMD_SYS_GET),
187262306a36Sopenharmony_ci			0, 0);
187362306a36Sopenharmony_ci	if (!nlh) {
187462306a36Sopenharmony_ci		nlmsg_free(msg);
187562306a36Sopenharmony_ci		return -EMSGSIZE;
187662306a36Sopenharmony_ci	}
187762306a36Sopenharmony_ci
187862306a36Sopenharmony_ci	err = nla_put_u8(msg, RDMA_NLDEV_SYS_ATTR_NETNS_MODE,
187962306a36Sopenharmony_ci			 (u8)ib_devices_shared_netns);
188062306a36Sopenharmony_ci	if (err) {
188162306a36Sopenharmony_ci		nlmsg_free(msg);
188262306a36Sopenharmony_ci		return err;
188362306a36Sopenharmony_ci	}
188462306a36Sopenharmony_ci
188562306a36Sopenharmony_ci	/*
188662306a36Sopenharmony_ci	 * Copy-on-fork is supported.
188762306a36Sopenharmony_ci	 * See commits:
188862306a36Sopenharmony_ci	 * 70e806e4e645 ("mm: Do early cow for pinned pages during fork() for ptes")
188962306a36Sopenharmony_ci	 * 4eae4efa2c29 ("hugetlb: do early cow when page pinned on src mm")
189062306a36Sopenharmony_ci	 * for more details. Don't backport this without them.
189162306a36Sopenharmony_ci	 *
189262306a36Sopenharmony_ci	 * Return value ignored on purpose, assume copy-on-fork is not
189362306a36Sopenharmony_ci	 * supported in case of failure.
189462306a36Sopenharmony_ci	 */
189562306a36Sopenharmony_ci	nla_put_u8(msg, RDMA_NLDEV_SYS_ATTR_COPY_ON_FORK, 1);
189662306a36Sopenharmony_ci
189762306a36Sopenharmony_ci	nlmsg_end(msg, nlh);
189862306a36Sopenharmony_ci	return rdma_nl_unicast(sock_net(skb->sk), msg, NETLINK_CB(skb).portid);
189962306a36Sopenharmony_ci}
190062306a36Sopenharmony_ci
190162306a36Sopenharmony_cistatic int nldev_set_sys_set_doit(struct sk_buff *skb, struct nlmsghdr *nlh,
190262306a36Sopenharmony_ci				  struct netlink_ext_ack *extack)
190362306a36Sopenharmony_ci{
190462306a36Sopenharmony_ci	struct nlattr *tb[RDMA_NLDEV_ATTR_MAX];
190562306a36Sopenharmony_ci	u8 enable;
190662306a36Sopenharmony_ci	int err;
190762306a36Sopenharmony_ci
190862306a36Sopenharmony_ci	err = nlmsg_parse(nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1,
190962306a36Sopenharmony_ci			  nldev_policy, extack);
191062306a36Sopenharmony_ci	if (err || !tb[RDMA_NLDEV_SYS_ATTR_NETNS_MODE])
191162306a36Sopenharmony_ci		return -EINVAL;
191262306a36Sopenharmony_ci
191362306a36Sopenharmony_ci	enable = nla_get_u8(tb[RDMA_NLDEV_SYS_ATTR_NETNS_MODE]);
191462306a36Sopenharmony_ci	/* Only 0 and 1 are supported */
191562306a36Sopenharmony_ci	if (enable > 1)
191662306a36Sopenharmony_ci		return -EINVAL;
191762306a36Sopenharmony_ci
191862306a36Sopenharmony_ci	err = rdma_compatdev_set(enable);
191962306a36Sopenharmony_ci	return err;
192062306a36Sopenharmony_ci}
192162306a36Sopenharmony_ci
192262306a36Sopenharmony_cistatic int nldev_stat_set_mode_doit(struct sk_buff *msg,
192362306a36Sopenharmony_ci				    struct netlink_ext_ack *extack,
192462306a36Sopenharmony_ci				    struct nlattr *tb[],
192562306a36Sopenharmony_ci				    struct ib_device *device, u32 port)
192662306a36Sopenharmony_ci{
192762306a36Sopenharmony_ci	u32 mode, mask = 0, qpn, cntn = 0;
192862306a36Sopenharmony_ci	int ret;
192962306a36Sopenharmony_ci
193062306a36Sopenharmony_ci	/* Currently only counter for QP is supported */
193162306a36Sopenharmony_ci	if (!tb[RDMA_NLDEV_ATTR_STAT_RES] ||
193262306a36Sopenharmony_ci	    nla_get_u32(tb[RDMA_NLDEV_ATTR_STAT_RES]) != RDMA_NLDEV_ATTR_RES_QP)
193362306a36Sopenharmony_ci		return -EINVAL;
193462306a36Sopenharmony_ci
193562306a36Sopenharmony_ci	mode = nla_get_u32(tb[RDMA_NLDEV_ATTR_STAT_MODE]);
193662306a36Sopenharmony_ci	if (mode == RDMA_COUNTER_MODE_AUTO) {
193762306a36Sopenharmony_ci		if (tb[RDMA_NLDEV_ATTR_STAT_AUTO_MODE_MASK])
193862306a36Sopenharmony_ci			mask = nla_get_u32(
193962306a36Sopenharmony_ci				tb[RDMA_NLDEV_ATTR_STAT_AUTO_MODE_MASK]);
194062306a36Sopenharmony_ci		return rdma_counter_set_auto_mode(device, port, mask, extack);
194162306a36Sopenharmony_ci	}
194262306a36Sopenharmony_ci
194362306a36Sopenharmony_ci	if (!tb[RDMA_NLDEV_ATTR_RES_LQPN])
194462306a36Sopenharmony_ci		return -EINVAL;
194562306a36Sopenharmony_ci
194662306a36Sopenharmony_ci	qpn = nla_get_u32(tb[RDMA_NLDEV_ATTR_RES_LQPN]);
194762306a36Sopenharmony_ci	if (tb[RDMA_NLDEV_ATTR_STAT_COUNTER_ID]) {
194862306a36Sopenharmony_ci		cntn = nla_get_u32(tb[RDMA_NLDEV_ATTR_STAT_COUNTER_ID]);
194962306a36Sopenharmony_ci		ret = rdma_counter_bind_qpn(device, port, qpn, cntn);
195062306a36Sopenharmony_ci		if (ret)
195162306a36Sopenharmony_ci			return ret;
195262306a36Sopenharmony_ci	} else {
195362306a36Sopenharmony_ci		ret = rdma_counter_bind_qpn_alloc(device, port, qpn, &cntn);
195462306a36Sopenharmony_ci		if (ret)
195562306a36Sopenharmony_ci			return ret;
195662306a36Sopenharmony_ci	}
195762306a36Sopenharmony_ci
195862306a36Sopenharmony_ci	if (nla_put_u32(msg, RDMA_NLDEV_ATTR_STAT_COUNTER_ID, cntn) ||
195962306a36Sopenharmony_ci	    nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_LQPN, qpn)) {
196062306a36Sopenharmony_ci		ret = -EMSGSIZE;
196162306a36Sopenharmony_ci		goto err_fill;
196262306a36Sopenharmony_ci	}
196362306a36Sopenharmony_ci
196462306a36Sopenharmony_ci	return 0;
196562306a36Sopenharmony_ci
196662306a36Sopenharmony_cierr_fill:
196762306a36Sopenharmony_ci	rdma_counter_unbind_qpn(device, port, qpn, cntn);
196862306a36Sopenharmony_ci	return ret;
196962306a36Sopenharmony_ci}
197062306a36Sopenharmony_ci
197162306a36Sopenharmony_cistatic int nldev_stat_set_counter_dynamic_doit(struct nlattr *tb[],
197262306a36Sopenharmony_ci					       struct ib_device *device,
197362306a36Sopenharmony_ci					       u32 port)
197462306a36Sopenharmony_ci{
197562306a36Sopenharmony_ci	struct rdma_hw_stats *stats;
197662306a36Sopenharmony_ci	struct nlattr *entry_attr;
197762306a36Sopenharmony_ci	unsigned long *target;
197862306a36Sopenharmony_ci	int rem, i, ret = 0;
197962306a36Sopenharmony_ci	u32 index;
198062306a36Sopenharmony_ci
198162306a36Sopenharmony_ci	stats = ib_get_hw_stats_port(device, port);
198262306a36Sopenharmony_ci	if (!stats)
198362306a36Sopenharmony_ci		return -EINVAL;
198462306a36Sopenharmony_ci
198562306a36Sopenharmony_ci	target = kcalloc(BITS_TO_LONGS(stats->num_counters),
198662306a36Sopenharmony_ci			 sizeof(*stats->is_disabled), GFP_KERNEL);
198762306a36Sopenharmony_ci	if (!target)
198862306a36Sopenharmony_ci		return -ENOMEM;
198962306a36Sopenharmony_ci
199062306a36Sopenharmony_ci	nla_for_each_nested(entry_attr, tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS],
199162306a36Sopenharmony_ci			    rem) {
199262306a36Sopenharmony_ci		index = nla_get_u32(entry_attr);
199362306a36Sopenharmony_ci		if ((index >= stats->num_counters) ||
199462306a36Sopenharmony_ci		    !(stats->descs[index].flags & IB_STAT_FLAG_OPTIONAL)) {
199562306a36Sopenharmony_ci			ret = -EINVAL;
199662306a36Sopenharmony_ci			goto out;
199762306a36Sopenharmony_ci		}
199862306a36Sopenharmony_ci
199962306a36Sopenharmony_ci		set_bit(index, target);
200062306a36Sopenharmony_ci	}
200162306a36Sopenharmony_ci
200262306a36Sopenharmony_ci	for (i = 0; i < stats->num_counters; i++) {
200362306a36Sopenharmony_ci		if (!(stats->descs[i].flags & IB_STAT_FLAG_OPTIONAL))
200462306a36Sopenharmony_ci			continue;
200562306a36Sopenharmony_ci
200662306a36Sopenharmony_ci		ret = rdma_counter_modify(device, port, i, test_bit(i, target));
200762306a36Sopenharmony_ci		if (ret)
200862306a36Sopenharmony_ci			goto out;
200962306a36Sopenharmony_ci	}
201062306a36Sopenharmony_ci
201162306a36Sopenharmony_ciout:
201262306a36Sopenharmony_ci	kfree(target);
201362306a36Sopenharmony_ci	return ret;
201462306a36Sopenharmony_ci}
201562306a36Sopenharmony_ci
201662306a36Sopenharmony_cistatic int nldev_stat_set_doit(struct sk_buff *skb, struct nlmsghdr *nlh,
201762306a36Sopenharmony_ci			       struct netlink_ext_ack *extack)
201862306a36Sopenharmony_ci{
201962306a36Sopenharmony_ci	struct nlattr *tb[RDMA_NLDEV_ATTR_MAX];
202062306a36Sopenharmony_ci	struct ib_device *device;
202162306a36Sopenharmony_ci	struct sk_buff *msg;
202262306a36Sopenharmony_ci	u32 index, port;
202362306a36Sopenharmony_ci	int ret;
202462306a36Sopenharmony_ci
202562306a36Sopenharmony_ci	ret = nlmsg_parse(nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1, nldev_policy,
202662306a36Sopenharmony_ci			  extack);
202762306a36Sopenharmony_ci	if (ret || !tb[RDMA_NLDEV_ATTR_DEV_INDEX] ||
202862306a36Sopenharmony_ci	    !tb[RDMA_NLDEV_ATTR_PORT_INDEX])
202962306a36Sopenharmony_ci		return -EINVAL;
203062306a36Sopenharmony_ci
203162306a36Sopenharmony_ci	index = nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]);
203262306a36Sopenharmony_ci	device = ib_device_get_by_index(sock_net(skb->sk), index);
203362306a36Sopenharmony_ci	if (!device)
203462306a36Sopenharmony_ci		return -EINVAL;
203562306a36Sopenharmony_ci
203662306a36Sopenharmony_ci	port = nla_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]);
203762306a36Sopenharmony_ci	if (!rdma_is_port_valid(device, port)) {
203862306a36Sopenharmony_ci		ret = -EINVAL;
203962306a36Sopenharmony_ci		goto err_put_device;
204062306a36Sopenharmony_ci	}
204162306a36Sopenharmony_ci
204262306a36Sopenharmony_ci	if (!tb[RDMA_NLDEV_ATTR_STAT_MODE] &&
204362306a36Sopenharmony_ci	    !tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS]) {
204462306a36Sopenharmony_ci		ret = -EINVAL;
204562306a36Sopenharmony_ci		goto err_put_device;
204662306a36Sopenharmony_ci	}
204762306a36Sopenharmony_ci
204862306a36Sopenharmony_ci	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
204962306a36Sopenharmony_ci	if (!msg) {
205062306a36Sopenharmony_ci		ret = -ENOMEM;
205162306a36Sopenharmony_ci		goto err_put_device;
205262306a36Sopenharmony_ci	}
205362306a36Sopenharmony_ci	nlh = nlmsg_put(msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq,
205462306a36Sopenharmony_ci			RDMA_NL_GET_TYPE(RDMA_NL_NLDEV,
205562306a36Sopenharmony_ci					 RDMA_NLDEV_CMD_STAT_SET),
205662306a36Sopenharmony_ci			0, 0);
205762306a36Sopenharmony_ci	if (!nlh || fill_nldev_handle(msg, device) ||
205862306a36Sopenharmony_ci	    nla_put_u32(msg, RDMA_NLDEV_ATTR_PORT_INDEX, port)) {
205962306a36Sopenharmony_ci		ret = -EMSGSIZE;
206062306a36Sopenharmony_ci		goto err_free_msg;
206162306a36Sopenharmony_ci	}
206262306a36Sopenharmony_ci
206362306a36Sopenharmony_ci	if (tb[RDMA_NLDEV_ATTR_STAT_MODE]) {
206462306a36Sopenharmony_ci		ret = nldev_stat_set_mode_doit(msg, extack, tb, device, port);
206562306a36Sopenharmony_ci		if (ret)
206662306a36Sopenharmony_ci			goto err_free_msg;
206762306a36Sopenharmony_ci	}
206862306a36Sopenharmony_ci
206962306a36Sopenharmony_ci	if (tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS]) {
207062306a36Sopenharmony_ci		ret = nldev_stat_set_counter_dynamic_doit(tb, device, port);
207162306a36Sopenharmony_ci		if (ret)
207262306a36Sopenharmony_ci			goto err_free_msg;
207362306a36Sopenharmony_ci	}
207462306a36Sopenharmony_ci
207562306a36Sopenharmony_ci	nlmsg_end(msg, nlh);
207662306a36Sopenharmony_ci	ib_device_put(device);
207762306a36Sopenharmony_ci	return rdma_nl_unicast(sock_net(skb->sk), msg, NETLINK_CB(skb).portid);
207862306a36Sopenharmony_ci
207962306a36Sopenharmony_cierr_free_msg:
208062306a36Sopenharmony_ci	nlmsg_free(msg);
208162306a36Sopenharmony_cierr_put_device:
208262306a36Sopenharmony_ci	ib_device_put(device);
208362306a36Sopenharmony_ci	return ret;
208462306a36Sopenharmony_ci}
208562306a36Sopenharmony_ci
208662306a36Sopenharmony_cistatic int nldev_stat_del_doit(struct sk_buff *skb, struct nlmsghdr *nlh,
208762306a36Sopenharmony_ci			       struct netlink_ext_ack *extack)
208862306a36Sopenharmony_ci{
208962306a36Sopenharmony_ci	struct nlattr *tb[RDMA_NLDEV_ATTR_MAX];
209062306a36Sopenharmony_ci	struct ib_device *device;
209162306a36Sopenharmony_ci	struct sk_buff *msg;
209262306a36Sopenharmony_ci	u32 index, port, qpn, cntn;
209362306a36Sopenharmony_ci	int ret;
209462306a36Sopenharmony_ci
209562306a36Sopenharmony_ci	ret = nlmsg_parse(nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1,
209662306a36Sopenharmony_ci			  nldev_policy, extack);
209762306a36Sopenharmony_ci	if (ret || !tb[RDMA_NLDEV_ATTR_STAT_RES] ||
209862306a36Sopenharmony_ci	    !tb[RDMA_NLDEV_ATTR_DEV_INDEX] || !tb[RDMA_NLDEV_ATTR_PORT_INDEX] ||
209962306a36Sopenharmony_ci	    !tb[RDMA_NLDEV_ATTR_STAT_COUNTER_ID] ||
210062306a36Sopenharmony_ci	    !tb[RDMA_NLDEV_ATTR_RES_LQPN])
210162306a36Sopenharmony_ci		return -EINVAL;
210262306a36Sopenharmony_ci
210362306a36Sopenharmony_ci	if (nla_get_u32(tb[RDMA_NLDEV_ATTR_STAT_RES]) != RDMA_NLDEV_ATTR_RES_QP)
210462306a36Sopenharmony_ci		return -EINVAL;
210562306a36Sopenharmony_ci
210662306a36Sopenharmony_ci	index = nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]);
210762306a36Sopenharmony_ci	device = ib_device_get_by_index(sock_net(skb->sk), index);
210862306a36Sopenharmony_ci	if (!device)
210962306a36Sopenharmony_ci		return -EINVAL;
211062306a36Sopenharmony_ci
211162306a36Sopenharmony_ci	port = nla_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]);
211262306a36Sopenharmony_ci	if (!rdma_is_port_valid(device, port)) {
211362306a36Sopenharmony_ci		ret = -EINVAL;
211462306a36Sopenharmony_ci		goto err;
211562306a36Sopenharmony_ci	}
211662306a36Sopenharmony_ci
211762306a36Sopenharmony_ci	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
211862306a36Sopenharmony_ci	if (!msg) {
211962306a36Sopenharmony_ci		ret = -ENOMEM;
212062306a36Sopenharmony_ci		goto err;
212162306a36Sopenharmony_ci	}
212262306a36Sopenharmony_ci	nlh = nlmsg_put(msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq,
212362306a36Sopenharmony_ci			RDMA_NL_GET_TYPE(RDMA_NL_NLDEV,
212462306a36Sopenharmony_ci					 RDMA_NLDEV_CMD_STAT_SET),
212562306a36Sopenharmony_ci			0, 0);
212662306a36Sopenharmony_ci	if (!nlh) {
212762306a36Sopenharmony_ci		ret = -EMSGSIZE;
212862306a36Sopenharmony_ci		goto err_fill;
212962306a36Sopenharmony_ci	}
213062306a36Sopenharmony_ci
213162306a36Sopenharmony_ci	cntn = nla_get_u32(tb[RDMA_NLDEV_ATTR_STAT_COUNTER_ID]);
213262306a36Sopenharmony_ci	qpn = nla_get_u32(tb[RDMA_NLDEV_ATTR_RES_LQPN]);
213362306a36Sopenharmony_ci	if (fill_nldev_handle(msg, device) ||
213462306a36Sopenharmony_ci	    nla_put_u32(msg, RDMA_NLDEV_ATTR_PORT_INDEX, port) ||
213562306a36Sopenharmony_ci	    nla_put_u32(msg, RDMA_NLDEV_ATTR_STAT_COUNTER_ID, cntn) ||
213662306a36Sopenharmony_ci	    nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_LQPN, qpn)) {
213762306a36Sopenharmony_ci		ret = -EMSGSIZE;
213862306a36Sopenharmony_ci		goto err_fill;
213962306a36Sopenharmony_ci	}
214062306a36Sopenharmony_ci
214162306a36Sopenharmony_ci	ret = rdma_counter_unbind_qpn(device, port, qpn, cntn);
214262306a36Sopenharmony_ci	if (ret)
214362306a36Sopenharmony_ci		goto err_fill;
214462306a36Sopenharmony_ci
214562306a36Sopenharmony_ci	nlmsg_end(msg, nlh);
214662306a36Sopenharmony_ci	ib_device_put(device);
214762306a36Sopenharmony_ci	return rdma_nl_unicast(sock_net(skb->sk), msg, NETLINK_CB(skb).portid);
214862306a36Sopenharmony_ci
214962306a36Sopenharmony_cierr_fill:
215062306a36Sopenharmony_ci	nlmsg_free(msg);
215162306a36Sopenharmony_cierr:
215262306a36Sopenharmony_ci	ib_device_put(device);
215362306a36Sopenharmony_ci	return ret;
215462306a36Sopenharmony_ci}
215562306a36Sopenharmony_ci
215662306a36Sopenharmony_cistatic int stat_get_doit_default_counter(struct sk_buff *skb,
215762306a36Sopenharmony_ci					 struct nlmsghdr *nlh,
215862306a36Sopenharmony_ci					 struct netlink_ext_ack *extack,
215962306a36Sopenharmony_ci					 struct nlattr *tb[])
216062306a36Sopenharmony_ci{
216162306a36Sopenharmony_ci	struct rdma_hw_stats *stats;
216262306a36Sopenharmony_ci	struct nlattr *table_attr;
216362306a36Sopenharmony_ci	struct ib_device *device;
216462306a36Sopenharmony_ci	int ret, num_cnts, i;
216562306a36Sopenharmony_ci	struct sk_buff *msg;
216662306a36Sopenharmony_ci	u32 index, port;
216762306a36Sopenharmony_ci	u64 v;
216862306a36Sopenharmony_ci
216962306a36Sopenharmony_ci	if (!tb[RDMA_NLDEV_ATTR_DEV_INDEX] || !tb[RDMA_NLDEV_ATTR_PORT_INDEX])
217062306a36Sopenharmony_ci		return -EINVAL;
217162306a36Sopenharmony_ci
217262306a36Sopenharmony_ci	index = nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]);
217362306a36Sopenharmony_ci	device = ib_device_get_by_index(sock_net(skb->sk), index);
217462306a36Sopenharmony_ci	if (!device)
217562306a36Sopenharmony_ci		return -EINVAL;
217662306a36Sopenharmony_ci
217762306a36Sopenharmony_ci	if (!device->ops.alloc_hw_port_stats || !device->ops.get_hw_stats) {
217862306a36Sopenharmony_ci		ret = -EINVAL;
217962306a36Sopenharmony_ci		goto err;
218062306a36Sopenharmony_ci	}
218162306a36Sopenharmony_ci
218262306a36Sopenharmony_ci	port = nla_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]);
218362306a36Sopenharmony_ci	stats = ib_get_hw_stats_port(device, port);
218462306a36Sopenharmony_ci	if (!stats) {
218562306a36Sopenharmony_ci		ret = -EINVAL;
218662306a36Sopenharmony_ci		goto err;
218762306a36Sopenharmony_ci	}
218862306a36Sopenharmony_ci
218962306a36Sopenharmony_ci	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
219062306a36Sopenharmony_ci	if (!msg) {
219162306a36Sopenharmony_ci		ret = -ENOMEM;
219262306a36Sopenharmony_ci		goto err;
219362306a36Sopenharmony_ci	}
219462306a36Sopenharmony_ci
219562306a36Sopenharmony_ci	nlh = nlmsg_put(msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq,
219662306a36Sopenharmony_ci			RDMA_NL_GET_TYPE(RDMA_NL_NLDEV,
219762306a36Sopenharmony_ci					 RDMA_NLDEV_CMD_STAT_GET),
219862306a36Sopenharmony_ci			0, 0);
219962306a36Sopenharmony_ci
220062306a36Sopenharmony_ci	if (!nlh || fill_nldev_handle(msg, device) ||
220162306a36Sopenharmony_ci	    nla_put_u32(msg, RDMA_NLDEV_ATTR_PORT_INDEX, port)) {
220262306a36Sopenharmony_ci		ret = -EMSGSIZE;
220362306a36Sopenharmony_ci		goto err_msg;
220462306a36Sopenharmony_ci	}
220562306a36Sopenharmony_ci
220662306a36Sopenharmony_ci	mutex_lock(&stats->lock);
220762306a36Sopenharmony_ci
220862306a36Sopenharmony_ci	num_cnts = device->ops.get_hw_stats(device, stats, port, 0);
220962306a36Sopenharmony_ci	if (num_cnts < 0) {
221062306a36Sopenharmony_ci		ret = -EINVAL;
221162306a36Sopenharmony_ci		goto err_stats;
221262306a36Sopenharmony_ci	}
221362306a36Sopenharmony_ci
221462306a36Sopenharmony_ci	table_attr = nla_nest_start(msg, RDMA_NLDEV_ATTR_STAT_HWCOUNTERS);
221562306a36Sopenharmony_ci	if (!table_attr) {
221662306a36Sopenharmony_ci		ret = -EMSGSIZE;
221762306a36Sopenharmony_ci		goto err_stats;
221862306a36Sopenharmony_ci	}
221962306a36Sopenharmony_ci	for (i = 0; i < num_cnts; i++) {
222062306a36Sopenharmony_ci		if (test_bit(i, stats->is_disabled))
222162306a36Sopenharmony_ci			continue;
222262306a36Sopenharmony_ci
222362306a36Sopenharmony_ci		v = stats->value[i] +
222462306a36Sopenharmony_ci			rdma_counter_get_hwstat_value(device, port, i);
222562306a36Sopenharmony_ci		if (rdma_nl_stat_hwcounter_entry(msg,
222662306a36Sopenharmony_ci						 stats->descs[i].name, v)) {
222762306a36Sopenharmony_ci			ret = -EMSGSIZE;
222862306a36Sopenharmony_ci			goto err_table;
222962306a36Sopenharmony_ci		}
223062306a36Sopenharmony_ci	}
223162306a36Sopenharmony_ci	nla_nest_end(msg, table_attr);
223262306a36Sopenharmony_ci
223362306a36Sopenharmony_ci	mutex_unlock(&stats->lock);
223462306a36Sopenharmony_ci	nlmsg_end(msg, nlh);
223562306a36Sopenharmony_ci	ib_device_put(device);
223662306a36Sopenharmony_ci	return rdma_nl_unicast(sock_net(skb->sk), msg, NETLINK_CB(skb).portid);
223762306a36Sopenharmony_ci
223862306a36Sopenharmony_cierr_table:
223962306a36Sopenharmony_ci	nla_nest_cancel(msg, table_attr);
224062306a36Sopenharmony_cierr_stats:
224162306a36Sopenharmony_ci	mutex_unlock(&stats->lock);
224262306a36Sopenharmony_cierr_msg:
224362306a36Sopenharmony_ci	nlmsg_free(msg);
224462306a36Sopenharmony_cierr:
224562306a36Sopenharmony_ci	ib_device_put(device);
224662306a36Sopenharmony_ci	return ret;
224762306a36Sopenharmony_ci}
224862306a36Sopenharmony_ci
224962306a36Sopenharmony_cistatic int stat_get_doit_qp(struct sk_buff *skb, struct nlmsghdr *nlh,
225062306a36Sopenharmony_ci			    struct netlink_ext_ack *extack, struct nlattr *tb[])
225162306a36Sopenharmony_ci
225262306a36Sopenharmony_ci{
225362306a36Sopenharmony_ci	static enum rdma_nl_counter_mode mode;
225462306a36Sopenharmony_ci	static enum rdma_nl_counter_mask mask;
225562306a36Sopenharmony_ci	struct ib_device *device;
225662306a36Sopenharmony_ci	struct sk_buff *msg;
225762306a36Sopenharmony_ci	u32 index, port;
225862306a36Sopenharmony_ci	int ret;
225962306a36Sopenharmony_ci
226062306a36Sopenharmony_ci	if (tb[RDMA_NLDEV_ATTR_STAT_COUNTER_ID])
226162306a36Sopenharmony_ci		return nldev_res_get_counter_doit(skb, nlh, extack);
226262306a36Sopenharmony_ci
226362306a36Sopenharmony_ci	if (!tb[RDMA_NLDEV_ATTR_STAT_MODE] ||
226462306a36Sopenharmony_ci	    !tb[RDMA_NLDEV_ATTR_DEV_INDEX] || !tb[RDMA_NLDEV_ATTR_PORT_INDEX])
226562306a36Sopenharmony_ci		return -EINVAL;
226662306a36Sopenharmony_ci
226762306a36Sopenharmony_ci	index = nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]);
226862306a36Sopenharmony_ci	device = ib_device_get_by_index(sock_net(skb->sk), index);
226962306a36Sopenharmony_ci	if (!device)
227062306a36Sopenharmony_ci		return -EINVAL;
227162306a36Sopenharmony_ci
227262306a36Sopenharmony_ci	port = nla_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]);
227362306a36Sopenharmony_ci	if (!rdma_is_port_valid(device, port)) {
227462306a36Sopenharmony_ci		ret = -EINVAL;
227562306a36Sopenharmony_ci		goto err;
227662306a36Sopenharmony_ci	}
227762306a36Sopenharmony_ci
227862306a36Sopenharmony_ci	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
227962306a36Sopenharmony_ci	if (!msg) {
228062306a36Sopenharmony_ci		ret = -ENOMEM;
228162306a36Sopenharmony_ci		goto err;
228262306a36Sopenharmony_ci	}
228362306a36Sopenharmony_ci
228462306a36Sopenharmony_ci	nlh = nlmsg_put(msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq,
228562306a36Sopenharmony_ci			RDMA_NL_GET_TYPE(RDMA_NL_NLDEV,
228662306a36Sopenharmony_ci					 RDMA_NLDEV_CMD_STAT_GET),
228762306a36Sopenharmony_ci			0, 0);
228862306a36Sopenharmony_ci	if (!nlh) {
228962306a36Sopenharmony_ci		ret = -EMSGSIZE;
229062306a36Sopenharmony_ci		goto err_msg;
229162306a36Sopenharmony_ci	}
229262306a36Sopenharmony_ci
229362306a36Sopenharmony_ci	ret = rdma_counter_get_mode(device, port, &mode, &mask);
229462306a36Sopenharmony_ci	if (ret)
229562306a36Sopenharmony_ci		goto err_msg;
229662306a36Sopenharmony_ci
229762306a36Sopenharmony_ci	if (fill_nldev_handle(msg, device) ||
229862306a36Sopenharmony_ci	    nla_put_u32(msg, RDMA_NLDEV_ATTR_PORT_INDEX, port) ||
229962306a36Sopenharmony_ci	    nla_put_u32(msg, RDMA_NLDEV_ATTR_STAT_MODE, mode)) {
230062306a36Sopenharmony_ci		ret = -EMSGSIZE;
230162306a36Sopenharmony_ci		goto err_msg;
230262306a36Sopenharmony_ci	}
230362306a36Sopenharmony_ci
230462306a36Sopenharmony_ci	if ((mode == RDMA_COUNTER_MODE_AUTO) &&
230562306a36Sopenharmony_ci	    nla_put_u32(msg, RDMA_NLDEV_ATTR_STAT_AUTO_MODE_MASK, mask)) {
230662306a36Sopenharmony_ci		ret = -EMSGSIZE;
230762306a36Sopenharmony_ci		goto err_msg;
230862306a36Sopenharmony_ci	}
230962306a36Sopenharmony_ci
231062306a36Sopenharmony_ci	nlmsg_end(msg, nlh);
231162306a36Sopenharmony_ci	ib_device_put(device);
231262306a36Sopenharmony_ci	return rdma_nl_unicast(sock_net(skb->sk), msg, NETLINK_CB(skb).portid);
231362306a36Sopenharmony_ci
231462306a36Sopenharmony_cierr_msg:
231562306a36Sopenharmony_ci	nlmsg_free(msg);
231662306a36Sopenharmony_cierr:
231762306a36Sopenharmony_ci	ib_device_put(device);
231862306a36Sopenharmony_ci	return ret;
231962306a36Sopenharmony_ci}
232062306a36Sopenharmony_ci
232162306a36Sopenharmony_cistatic int nldev_stat_get_doit(struct sk_buff *skb, struct nlmsghdr *nlh,
232262306a36Sopenharmony_ci			       struct netlink_ext_ack *extack)
232362306a36Sopenharmony_ci{
232462306a36Sopenharmony_ci	struct nlattr *tb[RDMA_NLDEV_ATTR_MAX];
232562306a36Sopenharmony_ci	int ret;
232662306a36Sopenharmony_ci
232762306a36Sopenharmony_ci	ret = nlmsg_parse(nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1,
232862306a36Sopenharmony_ci			  nldev_policy, extack);
232962306a36Sopenharmony_ci	if (ret)
233062306a36Sopenharmony_ci		return -EINVAL;
233162306a36Sopenharmony_ci
233262306a36Sopenharmony_ci	if (!tb[RDMA_NLDEV_ATTR_STAT_RES])
233362306a36Sopenharmony_ci		return stat_get_doit_default_counter(skb, nlh, extack, tb);
233462306a36Sopenharmony_ci
233562306a36Sopenharmony_ci	switch (nla_get_u32(tb[RDMA_NLDEV_ATTR_STAT_RES])) {
233662306a36Sopenharmony_ci	case RDMA_NLDEV_ATTR_RES_QP:
233762306a36Sopenharmony_ci		ret = stat_get_doit_qp(skb, nlh, extack, tb);
233862306a36Sopenharmony_ci		break;
233962306a36Sopenharmony_ci	case RDMA_NLDEV_ATTR_RES_MR:
234062306a36Sopenharmony_ci		ret = res_get_common_doit(skb, nlh, extack, RDMA_RESTRACK_MR,
234162306a36Sopenharmony_ci					  fill_stat_mr_entry);
234262306a36Sopenharmony_ci		break;
234362306a36Sopenharmony_ci	default:
234462306a36Sopenharmony_ci		ret = -EINVAL;
234562306a36Sopenharmony_ci		break;
234662306a36Sopenharmony_ci	}
234762306a36Sopenharmony_ci
234862306a36Sopenharmony_ci	return ret;
234962306a36Sopenharmony_ci}
235062306a36Sopenharmony_ci
235162306a36Sopenharmony_cistatic int nldev_stat_get_dumpit(struct sk_buff *skb,
235262306a36Sopenharmony_ci				 struct netlink_callback *cb)
235362306a36Sopenharmony_ci{
235462306a36Sopenharmony_ci	struct nlattr *tb[RDMA_NLDEV_ATTR_MAX];
235562306a36Sopenharmony_ci	int ret;
235662306a36Sopenharmony_ci
235762306a36Sopenharmony_ci	ret = nlmsg_parse(cb->nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1,
235862306a36Sopenharmony_ci			  nldev_policy, NULL);
235962306a36Sopenharmony_ci	if (ret || !tb[RDMA_NLDEV_ATTR_STAT_RES])
236062306a36Sopenharmony_ci		return -EINVAL;
236162306a36Sopenharmony_ci
236262306a36Sopenharmony_ci	switch (nla_get_u32(tb[RDMA_NLDEV_ATTR_STAT_RES])) {
236362306a36Sopenharmony_ci	case RDMA_NLDEV_ATTR_RES_QP:
236462306a36Sopenharmony_ci		ret = nldev_res_get_counter_dumpit(skb, cb);
236562306a36Sopenharmony_ci		break;
236662306a36Sopenharmony_ci	case RDMA_NLDEV_ATTR_RES_MR:
236762306a36Sopenharmony_ci		ret = res_get_common_dumpit(skb, cb, RDMA_RESTRACK_MR,
236862306a36Sopenharmony_ci					    fill_stat_mr_entry);
236962306a36Sopenharmony_ci		break;
237062306a36Sopenharmony_ci	default:
237162306a36Sopenharmony_ci		ret = -EINVAL;
237262306a36Sopenharmony_ci		break;
237362306a36Sopenharmony_ci	}
237462306a36Sopenharmony_ci
237562306a36Sopenharmony_ci	return ret;
237662306a36Sopenharmony_ci}
237762306a36Sopenharmony_ci
237862306a36Sopenharmony_cistatic int nldev_stat_get_counter_status_doit(struct sk_buff *skb,
237962306a36Sopenharmony_ci					      struct nlmsghdr *nlh,
238062306a36Sopenharmony_ci					      struct netlink_ext_ack *extack)
238162306a36Sopenharmony_ci{
238262306a36Sopenharmony_ci	struct nlattr *tb[RDMA_NLDEV_ATTR_MAX], *table, *entry;
238362306a36Sopenharmony_ci	struct rdma_hw_stats *stats;
238462306a36Sopenharmony_ci	struct ib_device *device;
238562306a36Sopenharmony_ci	struct sk_buff *msg;
238662306a36Sopenharmony_ci	u32 devid, port;
238762306a36Sopenharmony_ci	int ret, i;
238862306a36Sopenharmony_ci
238962306a36Sopenharmony_ci	ret = nlmsg_parse(nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1,
239062306a36Sopenharmony_ci			  nldev_policy, extack);
239162306a36Sopenharmony_ci	if (ret || !tb[RDMA_NLDEV_ATTR_DEV_INDEX] ||
239262306a36Sopenharmony_ci	    !tb[RDMA_NLDEV_ATTR_PORT_INDEX])
239362306a36Sopenharmony_ci		return -EINVAL;
239462306a36Sopenharmony_ci
239562306a36Sopenharmony_ci	devid = nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]);
239662306a36Sopenharmony_ci	device = ib_device_get_by_index(sock_net(skb->sk), devid);
239762306a36Sopenharmony_ci	if (!device)
239862306a36Sopenharmony_ci		return -EINVAL;
239962306a36Sopenharmony_ci
240062306a36Sopenharmony_ci	port = nla_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]);
240162306a36Sopenharmony_ci	if (!rdma_is_port_valid(device, port)) {
240262306a36Sopenharmony_ci		ret = -EINVAL;
240362306a36Sopenharmony_ci		goto err;
240462306a36Sopenharmony_ci	}
240562306a36Sopenharmony_ci
240662306a36Sopenharmony_ci	stats = ib_get_hw_stats_port(device, port);
240762306a36Sopenharmony_ci	if (!stats) {
240862306a36Sopenharmony_ci		ret = -EINVAL;
240962306a36Sopenharmony_ci		goto err;
241062306a36Sopenharmony_ci	}
241162306a36Sopenharmony_ci
241262306a36Sopenharmony_ci	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
241362306a36Sopenharmony_ci	if (!msg) {
241462306a36Sopenharmony_ci		ret = -ENOMEM;
241562306a36Sopenharmony_ci		goto err;
241662306a36Sopenharmony_ci	}
241762306a36Sopenharmony_ci
241862306a36Sopenharmony_ci	nlh = nlmsg_put(
241962306a36Sopenharmony_ci		msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq,
242062306a36Sopenharmony_ci		RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, RDMA_NLDEV_CMD_STAT_GET_STATUS),
242162306a36Sopenharmony_ci		0, 0);
242262306a36Sopenharmony_ci
242362306a36Sopenharmony_ci	ret = -EMSGSIZE;
242462306a36Sopenharmony_ci	if (!nlh || fill_nldev_handle(msg, device) ||
242562306a36Sopenharmony_ci	    nla_put_u32(msg, RDMA_NLDEV_ATTR_PORT_INDEX, port))
242662306a36Sopenharmony_ci		goto err_msg;
242762306a36Sopenharmony_ci
242862306a36Sopenharmony_ci	table = nla_nest_start(msg, RDMA_NLDEV_ATTR_STAT_HWCOUNTERS);
242962306a36Sopenharmony_ci	if (!table)
243062306a36Sopenharmony_ci		goto err_msg;
243162306a36Sopenharmony_ci
243262306a36Sopenharmony_ci	mutex_lock(&stats->lock);
243362306a36Sopenharmony_ci	for (i = 0; i < stats->num_counters; i++) {
243462306a36Sopenharmony_ci		entry = nla_nest_start(msg,
243562306a36Sopenharmony_ci				       RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY);
243662306a36Sopenharmony_ci		if (!entry)
243762306a36Sopenharmony_ci			goto err_msg_table;
243862306a36Sopenharmony_ci
243962306a36Sopenharmony_ci		if (nla_put_string(msg,
244062306a36Sopenharmony_ci				   RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME,
244162306a36Sopenharmony_ci				   stats->descs[i].name) ||
244262306a36Sopenharmony_ci		    nla_put_u32(msg, RDMA_NLDEV_ATTR_STAT_HWCOUNTER_INDEX, i))
244362306a36Sopenharmony_ci			goto err_msg_entry;
244462306a36Sopenharmony_ci
244562306a36Sopenharmony_ci		if ((stats->descs[i].flags & IB_STAT_FLAG_OPTIONAL) &&
244662306a36Sopenharmony_ci		    (nla_put_u8(msg, RDMA_NLDEV_ATTR_STAT_HWCOUNTER_DYNAMIC,
244762306a36Sopenharmony_ci				!test_bit(i, stats->is_disabled))))
244862306a36Sopenharmony_ci			goto err_msg_entry;
244962306a36Sopenharmony_ci
245062306a36Sopenharmony_ci		nla_nest_end(msg, entry);
245162306a36Sopenharmony_ci	}
245262306a36Sopenharmony_ci	mutex_unlock(&stats->lock);
245362306a36Sopenharmony_ci
245462306a36Sopenharmony_ci	nla_nest_end(msg, table);
245562306a36Sopenharmony_ci	nlmsg_end(msg, nlh);
245662306a36Sopenharmony_ci	ib_device_put(device);
245762306a36Sopenharmony_ci	return rdma_nl_unicast(sock_net(skb->sk), msg, NETLINK_CB(skb).portid);
245862306a36Sopenharmony_ci
245962306a36Sopenharmony_cierr_msg_entry:
246062306a36Sopenharmony_ci	nla_nest_cancel(msg, entry);
246162306a36Sopenharmony_cierr_msg_table:
246262306a36Sopenharmony_ci	mutex_unlock(&stats->lock);
246362306a36Sopenharmony_ci	nla_nest_cancel(msg, table);
246462306a36Sopenharmony_cierr_msg:
246562306a36Sopenharmony_ci	nlmsg_free(msg);
246662306a36Sopenharmony_cierr:
246762306a36Sopenharmony_ci	ib_device_put(device);
246862306a36Sopenharmony_ci	return ret;
246962306a36Sopenharmony_ci}
247062306a36Sopenharmony_ci
247162306a36Sopenharmony_cistatic const struct rdma_nl_cbs nldev_cb_table[RDMA_NLDEV_NUM_OPS] = {
247262306a36Sopenharmony_ci	[RDMA_NLDEV_CMD_GET] = {
247362306a36Sopenharmony_ci		.doit = nldev_get_doit,
247462306a36Sopenharmony_ci		.dump = nldev_get_dumpit,
247562306a36Sopenharmony_ci	},
247662306a36Sopenharmony_ci	[RDMA_NLDEV_CMD_GET_CHARDEV] = {
247762306a36Sopenharmony_ci		.doit = nldev_get_chardev,
247862306a36Sopenharmony_ci	},
247962306a36Sopenharmony_ci	[RDMA_NLDEV_CMD_SET] = {
248062306a36Sopenharmony_ci		.doit = nldev_set_doit,
248162306a36Sopenharmony_ci		.flags = RDMA_NL_ADMIN_PERM,
248262306a36Sopenharmony_ci	},
248362306a36Sopenharmony_ci	[RDMA_NLDEV_CMD_NEWLINK] = {
248462306a36Sopenharmony_ci		.doit = nldev_newlink,
248562306a36Sopenharmony_ci		.flags = RDMA_NL_ADMIN_PERM,
248662306a36Sopenharmony_ci	},
248762306a36Sopenharmony_ci	[RDMA_NLDEV_CMD_DELLINK] = {
248862306a36Sopenharmony_ci		.doit = nldev_dellink,
248962306a36Sopenharmony_ci		.flags = RDMA_NL_ADMIN_PERM,
249062306a36Sopenharmony_ci	},
249162306a36Sopenharmony_ci	[RDMA_NLDEV_CMD_PORT_GET] = {
249262306a36Sopenharmony_ci		.doit = nldev_port_get_doit,
249362306a36Sopenharmony_ci		.dump = nldev_port_get_dumpit,
249462306a36Sopenharmony_ci	},
249562306a36Sopenharmony_ci	[RDMA_NLDEV_CMD_RES_GET] = {
249662306a36Sopenharmony_ci		.doit = nldev_res_get_doit,
249762306a36Sopenharmony_ci		.dump = nldev_res_get_dumpit,
249862306a36Sopenharmony_ci	},
249962306a36Sopenharmony_ci	[RDMA_NLDEV_CMD_RES_QP_GET] = {
250062306a36Sopenharmony_ci		.doit = nldev_res_get_qp_doit,
250162306a36Sopenharmony_ci		.dump = nldev_res_get_qp_dumpit,
250262306a36Sopenharmony_ci	},
250362306a36Sopenharmony_ci	[RDMA_NLDEV_CMD_RES_CM_ID_GET] = {
250462306a36Sopenharmony_ci		.doit = nldev_res_get_cm_id_doit,
250562306a36Sopenharmony_ci		.dump = nldev_res_get_cm_id_dumpit,
250662306a36Sopenharmony_ci	},
250762306a36Sopenharmony_ci	[RDMA_NLDEV_CMD_RES_CQ_GET] = {
250862306a36Sopenharmony_ci		.doit = nldev_res_get_cq_doit,
250962306a36Sopenharmony_ci		.dump = nldev_res_get_cq_dumpit,
251062306a36Sopenharmony_ci	},
251162306a36Sopenharmony_ci	[RDMA_NLDEV_CMD_RES_MR_GET] = {
251262306a36Sopenharmony_ci		.doit = nldev_res_get_mr_doit,
251362306a36Sopenharmony_ci		.dump = nldev_res_get_mr_dumpit,
251462306a36Sopenharmony_ci	},
251562306a36Sopenharmony_ci	[RDMA_NLDEV_CMD_RES_PD_GET] = {
251662306a36Sopenharmony_ci		.doit = nldev_res_get_pd_doit,
251762306a36Sopenharmony_ci		.dump = nldev_res_get_pd_dumpit,
251862306a36Sopenharmony_ci	},
251962306a36Sopenharmony_ci	[RDMA_NLDEV_CMD_RES_CTX_GET] = {
252062306a36Sopenharmony_ci		.doit = nldev_res_get_ctx_doit,
252162306a36Sopenharmony_ci		.dump = nldev_res_get_ctx_dumpit,
252262306a36Sopenharmony_ci	},
252362306a36Sopenharmony_ci	[RDMA_NLDEV_CMD_RES_SRQ_GET] = {
252462306a36Sopenharmony_ci		.doit = nldev_res_get_srq_doit,
252562306a36Sopenharmony_ci		.dump = nldev_res_get_srq_dumpit,
252662306a36Sopenharmony_ci	},
252762306a36Sopenharmony_ci	[RDMA_NLDEV_CMD_SYS_GET] = {
252862306a36Sopenharmony_ci		.doit = nldev_sys_get_doit,
252962306a36Sopenharmony_ci	},
253062306a36Sopenharmony_ci	[RDMA_NLDEV_CMD_SYS_SET] = {
253162306a36Sopenharmony_ci		.doit = nldev_set_sys_set_doit,
253262306a36Sopenharmony_ci		.flags = RDMA_NL_ADMIN_PERM,
253362306a36Sopenharmony_ci	},
253462306a36Sopenharmony_ci	[RDMA_NLDEV_CMD_STAT_SET] = {
253562306a36Sopenharmony_ci		.doit = nldev_stat_set_doit,
253662306a36Sopenharmony_ci		.flags = RDMA_NL_ADMIN_PERM,
253762306a36Sopenharmony_ci	},
253862306a36Sopenharmony_ci	[RDMA_NLDEV_CMD_STAT_GET] = {
253962306a36Sopenharmony_ci		.doit = nldev_stat_get_doit,
254062306a36Sopenharmony_ci		.dump = nldev_stat_get_dumpit,
254162306a36Sopenharmony_ci	},
254262306a36Sopenharmony_ci	[RDMA_NLDEV_CMD_STAT_DEL] = {
254362306a36Sopenharmony_ci		.doit = nldev_stat_del_doit,
254462306a36Sopenharmony_ci		.flags = RDMA_NL_ADMIN_PERM,
254562306a36Sopenharmony_ci	},
254662306a36Sopenharmony_ci	[RDMA_NLDEV_CMD_RES_QP_GET_RAW] = {
254762306a36Sopenharmony_ci		.doit = nldev_res_get_qp_raw_doit,
254862306a36Sopenharmony_ci		.dump = nldev_res_get_qp_raw_dumpit,
254962306a36Sopenharmony_ci		.flags = RDMA_NL_ADMIN_PERM,
255062306a36Sopenharmony_ci	},
255162306a36Sopenharmony_ci	[RDMA_NLDEV_CMD_RES_CQ_GET_RAW] = {
255262306a36Sopenharmony_ci		.doit = nldev_res_get_cq_raw_doit,
255362306a36Sopenharmony_ci		.dump = nldev_res_get_cq_raw_dumpit,
255462306a36Sopenharmony_ci		.flags = RDMA_NL_ADMIN_PERM,
255562306a36Sopenharmony_ci	},
255662306a36Sopenharmony_ci	[RDMA_NLDEV_CMD_RES_MR_GET_RAW] = {
255762306a36Sopenharmony_ci		.doit = nldev_res_get_mr_raw_doit,
255862306a36Sopenharmony_ci		.dump = nldev_res_get_mr_raw_dumpit,
255962306a36Sopenharmony_ci		.flags = RDMA_NL_ADMIN_PERM,
256062306a36Sopenharmony_ci	},
256162306a36Sopenharmony_ci	[RDMA_NLDEV_CMD_STAT_GET_STATUS] = {
256262306a36Sopenharmony_ci		.doit = nldev_stat_get_counter_status_doit,
256362306a36Sopenharmony_ci	},
256462306a36Sopenharmony_ci};
256562306a36Sopenharmony_ci
256662306a36Sopenharmony_civoid __init nldev_init(void)
256762306a36Sopenharmony_ci{
256862306a36Sopenharmony_ci	rdma_nl_register(RDMA_NL_NLDEV, nldev_cb_table);
256962306a36Sopenharmony_ci}
257062306a36Sopenharmony_ci
257162306a36Sopenharmony_civoid nldev_exit(void)
257262306a36Sopenharmony_ci{
257362306a36Sopenharmony_ci	rdma_nl_unregister(RDMA_NL_NLDEV);
257462306a36Sopenharmony_ci}
257562306a36Sopenharmony_ci
257662306a36Sopenharmony_ciMODULE_ALIAS_RDMA_NETLINK(RDMA_NL_NLDEV, 5);
2577