18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright (c) 2012-2016 VMware, Inc.  All rights reserved.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or
58c2ecf20Sopenharmony_ci * modify it under the terms of EITHER the GNU General Public License
68c2ecf20Sopenharmony_ci * version 2 as published by the Free Software Foundation or the BSD
78c2ecf20Sopenharmony_ci * 2-Clause License. This program is distributed in the hope that it
88c2ecf20Sopenharmony_ci * will be useful, but WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED
98c2ecf20Sopenharmony_ci * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
108c2ecf20Sopenharmony_ci * See the GNU General Public License version 2 for more details at
118c2ecf20Sopenharmony_ci * http://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html.
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * You should have received a copy of the GNU General Public License
148c2ecf20Sopenharmony_ci * along with this program available in the file COPYING in the main
158c2ecf20Sopenharmony_ci * directory of this source tree.
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci * The BSD 2-Clause License
188c2ecf20Sopenharmony_ci *
198c2ecf20Sopenharmony_ci *     Redistribution and use in source and binary forms, with or
208c2ecf20Sopenharmony_ci *     without modification, are permitted provided that the following
218c2ecf20Sopenharmony_ci *     conditions are met:
228c2ecf20Sopenharmony_ci *
238c2ecf20Sopenharmony_ci *      - Redistributions of source code must retain the above
248c2ecf20Sopenharmony_ci *        copyright notice, this list of conditions and the following
258c2ecf20Sopenharmony_ci *        disclaimer.
268c2ecf20Sopenharmony_ci *
278c2ecf20Sopenharmony_ci *      - Redistributions in binary form must reproduce the above
288c2ecf20Sopenharmony_ci *        copyright notice, this list of conditions and the following
298c2ecf20Sopenharmony_ci *        disclaimer in the documentation and/or other materials
308c2ecf20Sopenharmony_ci *        provided with the distribution.
318c2ecf20Sopenharmony_ci *
328c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
338c2ecf20Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
348c2ecf20Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
358c2ecf20Sopenharmony_ci * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
368c2ecf20Sopenharmony_ci * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
378c2ecf20Sopenharmony_ci * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
388c2ecf20Sopenharmony_ci * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
398c2ecf20Sopenharmony_ci * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
408c2ecf20Sopenharmony_ci * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
418c2ecf20Sopenharmony_ci * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
428c2ecf20Sopenharmony_ci * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
438c2ecf20Sopenharmony_ci * OF THE POSSIBILITY OF SUCH DAMAGE.
448c2ecf20Sopenharmony_ci */
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci#include <asm/page.h>
478c2ecf20Sopenharmony_ci#include <linux/inet.h>
488c2ecf20Sopenharmony_ci#include <linux/io.h>
498c2ecf20Sopenharmony_ci#include <rdma/ib_addr.h>
508c2ecf20Sopenharmony_ci#include <rdma/ib_smi.h>
518c2ecf20Sopenharmony_ci#include <rdma/ib_user_verbs.h>
528c2ecf20Sopenharmony_ci#include <rdma/vmw_pvrdma-abi.h>
538c2ecf20Sopenharmony_ci#include <rdma/uverbs_ioctl.h>
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci#include "pvrdma.h"
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci/**
588c2ecf20Sopenharmony_ci * pvrdma_query_device - query device
598c2ecf20Sopenharmony_ci * @ibdev: the device to query
608c2ecf20Sopenharmony_ci * @props: the device properties
618c2ecf20Sopenharmony_ci * @uhw: user data
628c2ecf20Sopenharmony_ci *
638c2ecf20Sopenharmony_ci * @return: 0 on success, otherwise negative errno
648c2ecf20Sopenharmony_ci */
658c2ecf20Sopenharmony_ciint pvrdma_query_device(struct ib_device *ibdev,
668c2ecf20Sopenharmony_ci			struct ib_device_attr *props,
678c2ecf20Sopenharmony_ci			struct ib_udata *uhw)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	struct pvrdma_dev *dev = to_vdev(ibdev);
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	if (uhw->inlen || uhw->outlen)
728c2ecf20Sopenharmony_ci		return -EINVAL;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	props->fw_ver = dev->dsr->caps.fw_ver;
758c2ecf20Sopenharmony_ci	props->sys_image_guid = dev->dsr->caps.sys_image_guid;
768c2ecf20Sopenharmony_ci	props->max_mr_size = dev->dsr->caps.max_mr_size;
778c2ecf20Sopenharmony_ci	props->page_size_cap = dev->dsr->caps.page_size_cap;
788c2ecf20Sopenharmony_ci	props->vendor_id = dev->dsr->caps.vendor_id;
798c2ecf20Sopenharmony_ci	props->vendor_part_id = dev->pdev->device;
808c2ecf20Sopenharmony_ci	props->hw_ver = dev->dsr->caps.hw_ver;
818c2ecf20Sopenharmony_ci	props->max_qp = dev->dsr->caps.max_qp;
828c2ecf20Sopenharmony_ci	props->max_qp_wr = dev->dsr->caps.max_qp_wr;
838c2ecf20Sopenharmony_ci	props->device_cap_flags = dev->dsr->caps.device_cap_flags;
848c2ecf20Sopenharmony_ci	props->max_send_sge = dev->dsr->caps.max_sge;
858c2ecf20Sopenharmony_ci	props->max_recv_sge = dev->dsr->caps.max_sge;
868c2ecf20Sopenharmony_ci	props->max_sge_rd = PVRDMA_GET_CAP(dev, dev->dsr->caps.max_sge,
878c2ecf20Sopenharmony_ci					   dev->dsr->caps.max_sge_rd);
888c2ecf20Sopenharmony_ci	props->max_srq = dev->dsr->caps.max_srq;
898c2ecf20Sopenharmony_ci	props->max_srq_wr = dev->dsr->caps.max_srq_wr;
908c2ecf20Sopenharmony_ci	props->max_srq_sge = dev->dsr->caps.max_srq_sge;
918c2ecf20Sopenharmony_ci	props->max_cq = dev->dsr->caps.max_cq;
928c2ecf20Sopenharmony_ci	props->max_cqe = dev->dsr->caps.max_cqe;
938c2ecf20Sopenharmony_ci	props->max_mr = dev->dsr->caps.max_mr;
948c2ecf20Sopenharmony_ci	props->max_pd = dev->dsr->caps.max_pd;
958c2ecf20Sopenharmony_ci	props->max_qp_rd_atom = dev->dsr->caps.max_qp_rd_atom;
968c2ecf20Sopenharmony_ci	props->max_qp_init_rd_atom = dev->dsr->caps.max_qp_init_rd_atom;
978c2ecf20Sopenharmony_ci	props->atomic_cap =
988c2ecf20Sopenharmony_ci		dev->dsr->caps.atomic_ops &
998c2ecf20Sopenharmony_ci		(PVRDMA_ATOMIC_OP_COMP_SWAP | PVRDMA_ATOMIC_OP_FETCH_ADD) ?
1008c2ecf20Sopenharmony_ci		IB_ATOMIC_HCA : IB_ATOMIC_NONE;
1018c2ecf20Sopenharmony_ci	props->masked_atomic_cap = props->atomic_cap;
1028c2ecf20Sopenharmony_ci	props->max_ah = dev->dsr->caps.max_ah;
1038c2ecf20Sopenharmony_ci	props->max_pkeys = dev->dsr->caps.max_pkeys;
1048c2ecf20Sopenharmony_ci	props->local_ca_ack_delay = dev->dsr->caps.local_ca_ack_delay;
1058c2ecf20Sopenharmony_ci	if ((dev->dsr->caps.bmme_flags & PVRDMA_BMME_FLAG_LOCAL_INV) &&
1068c2ecf20Sopenharmony_ci	    (dev->dsr->caps.bmme_flags & PVRDMA_BMME_FLAG_REMOTE_INV) &&
1078c2ecf20Sopenharmony_ci	    (dev->dsr->caps.bmme_flags & PVRDMA_BMME_FLAG_FAST_REG_WR)) {
1088c2ecf20Sopenharmony_ci		props->device_cap_flags |= IB_DEVICE_MEM_MGT_EXTENSIONS;
1098c2ecf20Sopenharmony_ci		props->max_fast_reg_page_list_len = PVRDMA_GET_CAP(dev,
1108c2ecf20Sopenharmony_ci				PVRDMA_MAX_FAST_REG_PAGES,
1118c2ecf20Sopenharmony_ci				dev->dsr->caps.max_fast_reg_page_list_len);
1128c2ecf20Sopenharmony_ci	}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	props->device_cap_flags |= IB_DEVICE_PORT_ACTIVE_EVENT |
1158c2ecf20Sopenharmony_ci				   IB_DEVICE_RC_RNR_NAK_GEN;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	return 0;
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci/**
1218c2ecf20Sopenharmony_ci * pvrdma_query_port - query device port attributes
1228c2ecf20Sopenharmony_ci * @ibdev: the device to query
1238c2ecf20Sopenharmony_ci * @port: the port number
1248c2ecf20Sopenharmony_ci * @props: the device properties
1258c2ecf20Sopenharmony_ci *
1268c2ecf20Sopenharmony_ci * @return: 0 on success, otherwise negative errno
1278c2ecf20Sopenharmony_ci */
1288c2ecf20Sopenharmony_ciint pvrdma_query_port(struct ib_device *ibdev, u8 port,
1298c2ecf20Sopenharmony_ci		      struct ib_port_attr *props)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	struct pvrdma_dev *dev = to_vdev(ibdev);
1328c2ecf20Sopenharmony_ci	union pvrdma_cmd_req req;
1338c2ecf20Sopenharmony_ci	union pvrdma_cmd_resp rsp;
1348c2ecf20Sopenharmony_ci	struct pvrdma_cmd_query_port *cmd = &req.query_port;
1358c2ecf20Sopenharmony_ci	struct pvrdma_cmd_query_port_resp *resp = &rsp.query_port_resp;
1368c2ecf20Sopenharmony_ci	int err;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	memset(cmd, 0, sizeof(*cmd));
1398c2ecf20Sopenharmony_ci	cmd->hdr.cmd = PVRDMA_CMD_QUERY_PORT;
1408c2ecf20Sopenharmony_ci	cmd->port_num = port;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	err = pvrdma_cmd_post(dev, &req, &rsp, PVRDMA_CMD_QUERY_PORT_RESP);
1438c2ecf20Sopenharmony_ci	if (err < 0) {
1448c2ecf20Sopenharmony_ci		dev_warn(&dev->pdev->dev,
1458c2ecf20Sopenharmony_ci			 "could not query port, error: %d\n", err);
1468c2ecf20Sopenharmony_ci		return err;
1478c2ecf20Sopenharmony_ci	}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	/* props being zeroed by the caller, avoid zeroing it here */
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	props->state = pvrdma_port_state_to_ib(resp->attrs.state);
1528c2ecf20Sopenharmony_ci	props->max_mtu = pvrdma_mtu_to_ib(resp->attrs.max_mtu);
1538c2ecf20Sopenharmony_ci	props->active_mtu = pvrdma_mtu_to_ib(resp->attrs.active_mtu);
1548c2ecf20Sopenharmony_ci	props->gid_tbl_len = resp->attrs.gid_tbl_len;
1558c2ecf20Sopenharmony_ci	props->port_cap_flags =
1568c2ecf20Sopenharmony_ci		pvrdma_port_cap_flags_to_ib(resp->attrs.port_cap_flags);
1578c2ecf20Sopenharmony_ci	props->port_cap_flags |= IB_PORT_CM_SUP;
1588c2ecf20Sopenharmony_ci	props->ip_gids = true;
1598c2ecf20Sopenharmony_ci	props->max_msg_sz = resp->attrs.max_msg_sz;
1608c2ecf20Sopenharmony_ci	props->bad_pkey_cntr = resp->attrs.bad_pkey_cntr;
1618c2ecf20Sopenharmony_ci	props->qkey_viol_cntr = resp->attrs.qkey_viol_cntr;
1628c2ecf20Sopenharmony_ci	props->pkey_tbl_len = resp->attrs.pkey_tbl_len;
1638c2ecf20Sopenharmony_ci	props->lid = resp->attrs.lid;
1648c2ecf20Sopenharmony_ci	props->sm_lid = resp->attrs.sm_lid;
1658c2ecf20Sopenharmony_ci	props->lmc = resp->attrs.lmc;
1668c2ecf20Sopenharmony_ci	props->max_vl_num = resp->attrs.max_vl_num;
1678c2ecf20Sopenharmony_ci	props->sm_sl = resp->attrs.sm_sl;
1688c2ecf20Sopenharmony_ci	props->subnet_timeout = resp->attrs.subnet_timeout;
1698c2ecf20Sopenharmony_ci	props->init_type_reply = resp->attrs.init_type_reply;
1708c2ecf20Sopenharmony_ci	props->active_width = pvrdma_port_width_to_ib(resp->attrs.active_width);
1718c2ecf20Sopenharmony_ci	props->active_speed = pvrdma_port_speed_to_ib(resp->attrs.active_speed);
1728c2ecf20Sopenharmony_ci	props->phys_state = resp->attrs.phys_state;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	return 0;
1758c2ecf20Sopenharmony_ci}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci/**
1788c2ecf20Sopenharmony_ci * pvrdma_query_gid - query device gid
1798c2ecf20Sopenharmony_ci * @ibdev: the device to query
1808c2ecf20Sopenharmony_ci * @port: the port number
1818c2ecf20Sopenharmony_ci * @index: the index
1828c2ecf20Sopenharmony_ci * @gid: the device gid value
1838c2ecf20Sopenharmony_ci *
1848c2ecf20Sopenharmony_ci * @return: 0 on success, otherwise negative errno
1858c2ecf20Sopenharmony_ci */
1868c2ecf20Sopenharmony_ciint pvrdma_query_gid(struct ib_device *ibdev, u8 port, int index,
1878c2ecf20Sopenharmony_ci		     union ib_gid *gid)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	struct pvrdma_dev *dev = to_vdev(ibdev);
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	if (index >= dev->dsr->caps.gid_tbl_len)
1928c2ecf20Sopenharmony_ci		return -EINVAL;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	memcpy(gid, &dev->sgid_tbl[index], sizeof(union ib_gid));
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	return 0;
1978c2ecf20Sopenharmony_ci}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci/**
2008c2ecf20Sopenharmony_ci * pvrdma_query_pkey - query device port's P_Key table
2018c2ecf20Sopenharmony_ci * @ibdev: the device to query
2028c2ecf20Sopenharmony_ci * @port: the port number
2038c2ecf20Sopenharmony_ci * @index: the index
2048c2ecf20Sopenharmony_ci * @pkey: the device P_Key value
2058c2ecf20Sopenharmony_ci *
2068c2ecf20Sopenharmony_ci * @return: 0 on success, otherwise negative errno
2078c2ecf20Sopenharmony_ci */
2088c2ecf20Sopenharmony_ciint pvrdma_query_pkey(struct ib_device *ibdev, u8 port, u16 index,
2098c2ecf20Sopenharmony_ci		      u16 *pkey)
2108c2ecf20Sopenharmony_ci{
2118c2ecf20Sopenharmony_ci	int err = 0;
2128c2ecf20Sopenharmony_ci	union pvrdma_cmd_req req;
2138c2ecf20Sopenharmony_ci	union pvrdma_cmd_resp rsp;
2148c2ecf20Sopenharmony_ci	struct pvrdma_cmd_query_pkey *cmd = &req.query_pkey;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	memset(cmd, 0, sizeof(*cmd));
2178c2ecf20Sopenharmony_ci	cmd->hdr.cmd = PVRDMA_CMD_QUERY_PKEY;
2188c2ecf20Sopenharmony_ci	cmd->port_num = port;
2198c2ecf20Sopenharmony_ci	cmd->index = index;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	err = pvrdma_cmd_post(to_vdev(ibdev), &req, &rsp,
2228c2ecf20Sopenharmony_ci			      PVRDMA_CMD_QUERY_PKEY_RESP);
2238c2ecf20Sopenharmony_ci	if (err < 0) {
2248c2ecf20Sopenharmony_ci		dev_warn(&to_vdev(ibdev)->pdev->dev,
2258c2ecf20Sopenharmony_ci			 "could not query pkey, error: %d\n", err);
2268c2ecf20Sopenharmony_ci		return err;
2278c2ecf20Sopenharmony_ci	}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	*pkey = rsp.query_pkey_resp.pkey;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	return 0;
2328c2ecf20Sopenharmony_ci}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_cienum rdma_link_layer pvrdma_port_link_layer(struct ib_device *ibdev,
2358c2ecf20Sopenharmony_ci					    u8 port)
2368c2ecf20Sopenharmony_ci{
2378c2ecf20Sopenharmony_ci	return IB_LINK_LAYER_ETHERNET;
2388c2ecf20Sopenharmony_ci}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ciint pvrdma_modify_device(struct ib_device *ibdev, int mask,
2418c2ecf20Sopenharmony_ci			 struct ib_device_modify *props)
2428c2ecf20Sopenharmony_ci{
2438c2ecf20Sopenharmony_ci	unsigned long flags;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	if (mask & ~(IB_DEVICE_MODIFY_SYS_IMAGE_GUID |
2468c2ecf20Sopenharmony_ci		     IB_DEVICE_MODIFY_NODE_DESC)) {
2478c2ecf20Sopenharmony_ci		dev_warn(&to_vdev(ibdev)->pdev->dev,
2488c2ecf20Sopenharmony_ci			 "unsupported device modify mask %#x\n", mask);
2498c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
2508c2ecf20Sopenharmony_ci	}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	if (mask & IB_DEVICE_MODIFY_NODE_DESC) {
2538c2ecf20Sopenharmony_ci		spin_lock_irqsave(&to_vdev(ibdev)->desc_lock, flags);
2548c2ecf20Sopenharmony_ci		memcpy(ibdev->node_desc, props->node_desc, 64);
2558c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&to_vdev(ibdev)->desc_lock, flags);
2568c2ecf20Sopenharmony_ci	}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	if (mask & IB_DEVICE_MODIFY_SYS_IMAGE_GUID) {
2598c2ecf20Sopenharmony_ci		mutex_lock(&to_vdev(ibdev)->port_mutex);
2608c2ecf20Sopenharmony_ci		to_vdev(ibdev)->sys_image_guid =
2618c2ecf20Sopenharmony_ci			cpu_to_be64(props->sys_image_guid);
2628c2ecf20Sopenharmony_ci		mutex_unlock(&to_vdev(ibdev)->port_mutex);
2638c2ecf20Sopenharmony_ci	}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	return 0;
2668c2ecf20Sopenharmony_ci}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci/**
2698c2ecf20Sopenharmony_ci * pvrdma_modify_port - modify device port attributes
2708c2ecf20Sopenharmony_ci * @ibdev: the device to modify
2718c2ecf20Sopenharmony_ci * @port: the port number
2728c2ecf20Sopenharmony_ci * @mask: attributes to modify
2738c2ecf20Sopenharmony_ci * @props: the device properties
2748c2ecf20Sopenharmony_ci *
2758c2ecf20Sopenharmony_ci * @return: 0 on success, otherwise negative errno
2768c2ecf20Sopenharmony_ci */
2778c2ecf20Sopenharmony_ciint pvrdma_modify_port(struct ib_device *ibdev, u8 port, int mask,
2788c2ecf20Sopenharmony_ci		       struct ib_port_modify *props)
2798c2ecf20Sopenharmony_ci{
2808c2ecf20Sopenharmony_ci	struct ib_port_attr attr;
2818c2ecf20Sopenharmony_ci	struct pvrdma_dev *vdev = to_vdev(ibdev);
2828c2ecf20Sopenharmony_ci	int ret;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	if (mask & ~IB_PORT_SHUTDOWN) {
2858c2ecf20Sopenharmony_ci		dev_warn(&vdev->pdev->dev,
2868c2ecf20Sopenharmony_ci			 "unsupported port modify mask %#x\n", mask);
2878c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
2888c2ecf20Sopenharmony_ci	}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	mutex_lock(&vdev->port_mutex);
2918c2ecf20Sopenharmony_ci	ret = ib_query_port(ibdev, port, &attr);
2928c2ecf20Sopenharmony_ci	if (ret)
2938c2ecf20Sopenharmony_ci		goto out;
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	vdev->port_cap_mask |= props->set_port_cap_mask;
2968c2ecf20Sopenharmony_ci	vdev->port_cap_mask &= ~props->clr_port_cap_mask;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	if (mask & IB_PORT_SHUTDOWN)
2998c2ecf20Sopenharmony_ci		vdev->ib_active = false;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ciout:
3028c2ecf20Sopenharmony_ci	mutex_unlock(&vdev->port_mutex);
3038c2ecf20Sopenharmony_ci	return ret;
3048c2ecf20Sopenharmony_ci}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci/**
3078c2ecf20Sopenharmony_ci * pvrdma_alloc_ucontext - allocate ucontext
3088c2ecf20Sopenharmony_ci * @uctx: the uverbs countext
3098c2ecf20Sopenharmony_ci * @udata: user data
3108c2ecf20Sopenharmony_ci *
3118c2ecf20Sopenharmony_ci * @return:  zero on success, otherwise errno.
3128c2ecf20Sopenharmony_ci */
3138c2ecf20Sopenharmony_ciint pvrdma_alloc_ucontext(struct ib_ucontext *uctx, struct ib_udata *udata)
3148c2ecf20Sopenharmony_ci{
3158c2ecf20Sopenharmony_ci	struct ib_device *ibdev = uctx->device;
3168c2ecf20Sopenharmony_ci	struct pvrdma_dev *vdev = to_vdev(ibdev);
3178c2ecf20Sopenharmony_ci	struct pvrdma_ucontext *context = to_vucontext(uctx);
3188c2ecf20Sopenharmony_ci	union pvrdma_cmd_req req = {};
3198c2ecf20Sopenharmony_ci	union pvrdma_cmd_resp rsp = {};
3208c2ecf20Sopenharmony_ci	struct pvrdma_cmd_create_uc *cmd = &req.create_uc;
3218c2ecf20Sopenharmony_ci	struct pvrdma_cmd_create_uc_resp *resp = &rsp.create_uc_resp;
3228c2ecf20Sopenharmony_ci	struct pvrdma_alloc_ucontext_resp uresp = {};
3238c2ecf20Sopenharmony_ci	int ret;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	if (!vdev->ib_active)
3268c2ecf20Sopenharmony_ci		return -EAGAIN;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	context->dev = vdev;
3298c2ecf20Sopenharmony_ci	ret = pvrdma_uar_alloc(vdev, &context->uar);
3308c2ecf20Sopenharmony_ci	if (ret)
3318c2ecf20Sopenharmony_ci		return -ENOMEM;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	/* get ctx_handle from host */
3348c2ecf20Sopenharmony_ci	if (vdev->dsr_version < PVRDMA_PPN64_VERSION)
3358c2ecf20Sopenharmony_ci		cmd->pfn = context->uar.pfn;
3368c2ecf20Sopenharmony_ci	else
3378c2ecf20Sopenharmony_ci		cmd->pfn64 = context->uar.pfn;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	cmd->hdr.cmd = PVRDMA_CMD_CREATE_UC;
3408c2ecf20Sopenharmony_ci	ret = pvrdma_cmd_post(vdev, &req, &rsp, PVRDMA_CMD_CREATE_UC_RESP);
3418c2ecf20Sopenharmony_ci	if (ret < 0) {
3428c2ecf20Sopenharmony_ci		dev_warn(&vdev->pdev->dev,
3438c2ecf20Sopenharmony_ci			 "could not create ucontext, error: %d\n", ret);
3448c2ecf20Sopenharmony_ci		goto err;
3458c2ecf20Sopenharmony_ci	}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	context->ctx_handle = resp->ctx_handle;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	/* copy back to user */
3508c2ecf20Sopenharmony_ci	uresp.qp_tab_size = vdev->dsr->caps.max_qp;
3518c2ecf20Sopenharmony_ci	ret = ib_copy_to_udata(udata, &uresp, sizeof(uresp));
3528c2ecf20Sopenharmony_ci	if (ret) {
3538c2ecf20Sopenharmony_ci		pvrdma_uar_free(vdev, &context->uar);
3548c2ecf20Sopenharmony_ci		pvrdma_dealloc_ucontext(&context->ibucontext);
3558c2ecf20Sopenharmony_ci		return -EFAULT;
3568c2ecf20Sopenharmony_ci	}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	return 0;
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_cierr:
3618c2ecf20Sopenharmony_ci	pvrdma_uar_free(vdev, &context->uar);
3628c2ecf20Sopenharmony_ci	return ret;
3638c2ecf20Sopenharmony_ci}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci/**
3668c2ecf20Sopenharmony_ci * pvrdma_dealloc_ucontext - deallocate ucontext
3678c2ecf20Sopenharmony_ci * @ibcontext: the ucontext
3688c2ecf20Sopenharmony_ci */
3698c2ecf20Sopenharmony_civoid pvrdma_dealloc_ucontext(struct ib_ucontext *ibcontext)
3708c2ecf20Sopenharmony_ci{
3718c2ecf20Sopenharmony_ci	struct pvrdma_ucontext *context = to_vucontext(ibcontext);
3728c2ecf20Sopenharmony_ci	union pvrdma_cmd_req req = {};
3738c2ecf20Sopenharmony_ci	struct pvrdma_cmd_destroy_uc *cmd = &req.destroy_uc;
3748c2ecf20Sopenharmony_ci	int ret;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	cmd->hdr.cmd = PVRDMA_CMD_DESTROY_UC;
3778c2ecf20Sopenharmony_ci	cmd->ctx_handle = context->ctx_handle;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	ret = pvrdma_cmd_post(context->dev, &req, NULL, 0);
3808c2ecf20Sopenharmony_ci	if (ret < 0)
3818c2ecf20Sopenharmony_ci		dev_warn(&context->dev->pdev->dev,
3828c2ecf20Sopenharmony_ci			 "destroy ucontext failed, error: %d\n", ret);
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	/* Free the UAR even if the device command failed */
3858c2ecf20Sopenharmony_ci	pvrdma_uar_free(to_vdev(ibcontext->device), &context->uar);
3868c2ecf20Sopenharmony_ci}
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci/**
3898c2ecf20Sopenharmony_ci * pvrdma_mmap - create mmap region
3908c2ecf20Sopenharmony_ci * @ibcontext: the user context
3918c2ecf20Sopenharmony_ci * @vma: the VMA
3928c2ecf20Sopenharmony_ci *
3938c2ecf20Sopenharmony_ci * @return: 0 on success, otherwise errno.
3948c2ecf20Sopenharmony_ci */
3958c2ecf20Sopenharmony_ciint pvrdma_mmap(struct ib_ucontext *ibcontext, struct vm_area_struct *vma)
3968c2ecf20Sopenharmony_ci{
3978c2ecf20Sopenharmony_ci	struct pvrdma_ucontext *context = to_vucontext(ibcontext);
3988c2ecf20Sopenharmony_ci	unsigned long start = vma->vm_start;
3998c2ecf20Sopenharmony_ci	unsigned long size = vma->vm_end - vma->vm_start;
4008c2ecf20Sopenharmony_ci	unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	dev_dbg(&context->dev->pdev->dev, "create mmap region\n");
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	if ((size != PAGE_SIZE) || (offset & ~PAGE_MASK)) {
4058c2ecf20Sopenharmony_ci		dev_warn(&context->dev->pdev->dev,
4068c2ecf20Sopenharmony_ci			 "invalid params for mmap region\n");
4078c2ecf20Sopenharmony_ci		return -EINVAL;
4088c2ecf20Sopenharmony_ci	}
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	/* Map UAR to kernel space, VM_LOCKED? */
4118c2ecf20Sopenharmony_ci	vma->vm_flags |= VM_DONTCOPY | VM_DONTEXPAND;
4128c2ecf20Sopenharmony_ci	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
4138c2ecf20Sopenharmony_ci	if (io_remap_pfn_range(vma, start, context->uar.pfn, size,
4148c2ecf20Sopenharmony_ci			       vma->vm_page_prot))
4158c2ecf20Sopenharmony_ci		return -EAGAIN;
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	return 0;
4188c2ecf20Sopenharmony_ci}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci/**
4218c2ecf20Sopenharmony_ci * pvrdma_alloc_pd - allocate protection domain
4228c2ecf20Sopenharmony_ci * @ibpd: PD pointer
4238c2ecf20Sopenharmony_ci * @udata: user data
4248c2ecf20Sopenharmony_ci *
4258c2ecf20Sopenharmony_ci * @return: the ib_pd protection domain pointer on success, otherwise errno.
4268c2ecf20Sopenharmony_ci */
4278c2ecf20Sopenharmony_ciint pvrdma_alloc_pd(struct ib_pd *ibpd, struct ib_udata *udata)
4288c2ecf20Sopenharmony_ci{
4298c2ecf20Sopenharmony_ci	struct ib_device *ibdev = ibpd->device;
4308c2ecf20Sopenharmony_ci	struct pvrdma_pd *pd = to_vpd(ibpd);
4318c2ecf20Sopenharmony_ci	struct pvrdma_dev *dev = to_vdev(ibdev);
4328c2ecf20Sopenharmony_ci	union pvrdma_cmd_req req = {};
4338c2ecf20Sopenharmony_ci	union pvrdma_cmd_resp rsp = {};
4348c2ecf20Sopenharmony_ci	struct pvrdma_cmd_create_pd *cmd = &req.create_pd;
4358c2ecf20Sopenharmony_ci	struct pvrdma_cmd_create_pd_resp *resp = &rsp.create_pd_resp;
4368c2ecf20Sopenharmony_ci	struct pvrdma_alloc_pd_resp pd_resp = {0};
4378c2ecf20Sopenharmony_ci	int ret;
4388c2ecf20Sopenharmony_ci	struct pvrdma_ucontext *context = rdma_udata_to_drv_context(
4398c2ecf20Sopenharmony_ci		udata, struct pvrdma_ucontext, ibucontext);
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	/* Check allowed max pds */
4428c2ecf20Sopenharmony_ci	if (!atomic_add_unless(&dev->num_pds, 1, dev->dsr->caps.max_pd))
4438c2ecf20Sopenharmony_ci		return -ENOMEM;
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	cmd->hdr.cmd = PVRDMA_CMD_CREATE_PD;
4468c2ecf20Sopenharmony_ci	cmd->ctx_handle = context ? context->ctx_handle : 0;
4478c2ecf20Sopenharmony_ci	ret = pvrdma_cmd_post(dev, &req, &rsp, PVRDMA_CMD_CREATE_PD_RESP);
4488c2ecf20Sopenharmony_ci	if (ret < 0) {
4498c2ecf20Sopenharmony_ci		dev_warn(&dev->pdev->dev,
4508c2ecf20Sopenharmony_ci			 "failed to allocate protection domain, error: %d\n",
4518c2ecf20Sopenharmony_ci			 ret);
4528c2ecf20Sopenharmony_ci		goto err;
4538c2ecf20Sopenharmony_ci	}
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	pd->privileged = !udata;
4568c2ecf20Sopenharmony_ci	pd->pd_handle = resp->pd_handle;
4578c2ecf20Sopenharmony_ci	pd->pdn = resp->pd_handle;
4588c2ecf20Sopenharmony_ci	pd_resp.pdn = resp->pd_handle;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	if (udata) {
4618c2ecf20Sopenharmony_ci		if (ib_copy_to_udata(udata, &pd_resp, sizeof(pd_resp))) {
4628c2ecf20Sopenharmony_ci			dev_warn(&dev->pdev->dev,
4638c2ecf20Sopenharmony_ci				 "failed to copy back protection domain\n");
4648c2ecf20Sopenharmony_ci			pvrdma_dealloc_pd(&pd->ibpd, udata);
4658c2ecf20Sopenharmony_ci			return -EFAULT;
4668c2ecf20Sopenharmony_ci		}
4678c2ecf20Sopenharmony_ci	}
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	/* u32 pd handle */
4708c2ecf20Sopenharmony_ci	return 0;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_cierr:
4738c2ecf20Sopenharmony_ci	atomic_dec(&dev->num_pds);
4748c2ecf20Sopenharmony_ci	return ret;
4758c2ecf20Sopenharmony_ci}
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci/**
4788c2ecf20Sopenharmony_ci * pvrdma_dealloc_pd - deallocate protection domain
4798c2ecf20Sopenharmony_ci * @pd: the protection domain to be released
4808c2ecf20Sopenharmony_ci * @udata: user data or null for kernel object
4818c2ecf20Sopenharmony_ci *
4828c2ecf20Sopenharmony_ci * @return: Always 0
4838c2ecf20Sopenharmony_ci */
4848c2ecf20Sopenharmony_ciint pvrdma_dealloc_pd(struct ib_pd *pd, struct ib_udata *udata)
4858c2ecf20Sopenharmony_ci{
4868c2ecf20Sopenharmony_ci	struct pvrdma_dev *dev = to_vdev(pd->device);
4878c2ecf20Sopenharmony_ci	union pvrdma_cmd_req req = {};
4888c2ecf20Sopenharmony_ci	struct pvrdma_cmd_destroy_pd *cmd = &req.destroy_pd;
4898c2ecf20Sopenharmony_ci	int ret;
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	cmd->hdr.cmd = PVRDMA_CMD_DESTROY_PD;
4928c2ecf20Sopenharmony_ci	cmd->pd_handle = to_vpd(pd)->pd_handle;
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	ret = pvrdma_cmd_post(dev, &req, NULL, 0);
4958c2ecf20Sopenharmony_ci	if (ret)
4968c2ecf20Sopenharmony_ci		dev_warn(&dev->pdev->dev,
4978c2ecf20Sopenharmony_ci			 "could not dealloc protection domain, error: %d\n",
4988c2ecf20Sopenharmony_ci			 ret);
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	atomic_dec(&dev->num_pds);
5018c2ecf20Sopenharmony_ci	return 0;
5028c2ecf20Sopenharmony_ci}
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci/**
5058c2ecf20Sopenharmony_ci * pvrdma_create_ah - create an address handle
5068c2ecf20Sopenharmony_ci * @ibah: the IB address handle
5078c2ecf20Sopenharmony_ci * @init_attr: the attributes of the AH
5088c2ecf20Sopenharmony_ci * @udata: pointer to user data
5098c2ecf20Sopenharmony_ci *
5108c2ecf20Sopenharmony_ci * @return: 0 on success, otherwise errno.
5118c2ecf20Sopenharmony_ci */
5128c2ecf20Sopenharmony_ciint pvrdma_create_ah(struct ib_ah *ibah, struct rdma_ah_init_attr *init_attr,
5138c2ecf20Sopenharmony_ci		     struct ib_udata *udata)
5148c2ecf20Sopenharmony_ci{
5158c2ecf20Sopenharmony_ci	struct rdma_ah_attr *ah_attr = init_attr->ah_attr;
5168c2ecf20Sopenharmony_ci	struct pvrdma_dev *dev = to_vdev(ibah->device);
5178c2ecf20Sopenharmony_ci	struct pvrdma_ah *ah = to_vah(ibah);
5188c2ecf20Sopenharmony_ci	const struct ib_global_route *grh;
5198c2ecf20Sopenharmony_ci	u8 port_num = rdma_ah_get_port_num(ah_attr);
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	if (!(rdma_ah_get_ah_flags(ah_attr) & IB_AH_GRH))
5228c2ecf20Sopenharmony_ci		return -EINVAL;
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	grh = rdma_ah_read_grh(ah_attr);
5258c2ecf20Sopenharmony_ci	if ((ah_attr->type != RDMA_AH_ATTR_TYPE_ROCE)  ||
5268c2ecf20Sopenharmony_ci	    rdma_is_multicast_addr((struct in6_addr *)grh->dgid.raw))
5278c2ecf20Sopenharmony_ci		return -EINVAL;
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	if (!atomic_add_unless(&dev->num_ahs, 1, dev->dsr->caps.max_ah))
5308c2ecf20Sopenharmony_ci		return -ENOMEM;
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	ah->av.port_pd = to_vpd(ibah->pd)->pd_handle | (port_num << 24);
5338c2ecf20Sopenharmony_ci	ah->av.src_path_bits = rdma_ah_get_path_bits(ah_attr);
5348c2ecf20Sopenharmony_ci	ah->av.src_path_bits |= 0x80;
5358c2ecf20Sopenharmony_ci	ah->av.gid_index = grh->sgid_index;
5368c2ecf20Sopenharmony_ci	ah->av.hop_limit = grh->hop_limit;
5378c2ecf20Sopenharmony_ci	ah->av.sl_tclass_flowlabel = (grh->traffic_class << 20) |
5388c2ecf20Sopenharmony_ci				      grh->flow_label;
5398c2ecf20Sopenharmony_ci	memcpy(ah->av.dgid, grh->dgid.raw, 16);
5408c2ecf20Sopenharmony_ci	memcpy(ah->av.dmac, ah_attr->roce.dmac, ETH_ALEN);
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	return 0;
5438c2ecf20Sopenharmony_ci}
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci/**
5468c2ecf20Sopenharmony_ci * pvrdma_destroy_ah - destroy an address handle
5478c2ecf20Sopenharmony_ci * @ah: the address handle to destroyed
5488c2ecf20Sopenharmony_ci * @flags: destroy address handle flags (see enum rdma_destroy_ah_flags)
5498c2ecf20Sopenharmony_ci *
5508c2ecf20Sopenharmony_ci */
5518c2ecf20Sopenharmony_ciint pvrdma_destroy_ah(struct ib_ah *ah, u32 flags)
5528c2ecf20Sopenharmony_ci{
5538c2ecf20Sopenharmony_ci	struct pvrdma_dev *dev = to_vdev(ah->device);
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	atomic_dec(&dev->num_ahs);
5568c2ecf20Sopenharmony_ci	return 0;
5578c2ecf20Sopenharmony_ci}
558