162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 or BSD-3-Clause
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright(c) 2016 - 2019 Intel Corporation.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/slab.h>
762306a36Sopenharmony_ci#include "ah.h"
862306a36Sopenharmony_ci#include "vt.h" /* for prints */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci/**
1162306a36Sopenharmony_ci * rvt_check_ah - validate the attributes of AH
1262306a36Sopenharmony_ci * @ibdev: the ib device
1362306a36Sopenharmony_ci * @ah_attr: the attributes of the AH
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci * If driver supports a more detailed check_ah function call back to it
1662306a36Sopenharmony_ci * otherwise just check the basics.
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci * Return: 0 on success
1962306a36Sopenharmony_ci */
2062306a36Sopenharmony_ciint rvt_check_ah(struct ib_device *ibdev,
2162306a36Sopenharmony_ci		 struct rdma_ah_attr *ah_attr)
2262306a36Sopenharmony_ci{
2362306a36Sopenharmony_ci	int err;
2462306a36Sopenharmony_ci	int port_num = rdma_ah_get_port_num(ah_attr);
2562306a36Sopenharmony_ci	struct ib_port_attr port_attr;
2662306a36Sopenharmony_ci	struct rvt_dev_info *rdi = ib_to_rvt(ibdev);
2762306a36Sopenharmony_ci	u8 ah_flags = rdma_ah_get_ah_flags(ah_attr);
2862306a36Sopenharmony_ci	u8 static_rate = rdma_ah_get_static_rate(ah_attr);
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	err = ib_query_port(ibdev, port_num, &port_attr);
3162306a36Sopenharmony_ci	if (err)
3262306a36Sopenharmony_ci		return -EINVAL;
3362306a36Sopenharmony_ci	if (port_num < 1 ||
3462306a36Sopenharmony_ci	    port_num > ibdev->phys_port_cnt)
3562306a36Sopenharmony_ci		return -EINVAL;
3662306a36Sopenharmony_ci	if (static_rate != IB_RATE_PORT_CURRENT &&
3762306a36Sopenharmony_ci	    ib_rate_to_mbps(static_rate) < 0)
3862306a36Sopenharmony_ci		return -EINVAL;
3962306a36Sopenharmony_ci	if ((ah_flags & IB_AH_GRH) &&
4062306a36Sopenharmony_ci	    rdma_ah_read_grh(ah_attr)->sgid_index >= port_attr.gid_tbl_len)
4162306a36Sopenharmony_ci		return -EINVAL;
4262306a36Sopenharmony_ci	if (rdi->driver_f.check_ah)
4362306a36Sopenharmony_ci		return rdi->driver_f.check_ah(ibdev, ah_attr);
4462306a36Sopenharmony_ci	return 0;
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ciEXPORT_SYMBOL(rvt_check_ah);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci/**
4962306a36Sopenharmony_ci * rvt_create_ah - create an address handle
5062306a36Sopenharmony_ci * @ibah: the IB address handle
5162306a36Sopenharmony_ci * @init_attr: the attributes of the AH
5262306a36Sopenharmony_ci * @udata: pointer to user's input output buffer information.
5362306a36Sopenharmony_ci *
5462306a36Sopenharmony_ci * This may be called from interrupt context.
5562306a36Sopenharmony_ci *
5662306a36Sopenharmony_ci * Return: 0 on success
5762306a36Sopenharmony_ci */
5862306a36Sopenharmony_ciint rvt_create_ah(struct ib_ah *ibah, struct rdma_ah_init_attr *init_attr,
5962306a36Sopenharmony_ci		  struct ib_udata *udata)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	struct rvt_ah *ah = ibah_to_rvtah(ibah);
6262306a36Sopenharmony_ci	struct rvt_dev_info *dev = ib_to_rvt(ibah->device);
6362306a36Sopenharmony_ci	unsigned long flags;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	if (rvt_check_ah(ibah->device, init_attr->ah_attr))
6662306a36Sopenharmony_ci		return -EINVAL;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	spin_lock_irqsave(&dev->n_ahs_lock, flags);
6962306a36Sopenharmony_ci	if (dev->n_ahs_allocated == dev->dparms.props.max_ah) {
7062306a36Sopenharmony_ci		spin_unlock_irqrestore(&dev->n_ahs_lock, flags);
7162306a36Sopenharmony_ci		return -ENOMEM;
7262306a36Sopenharmony_ci	}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	dev->n_ahs_allocated++;
7562306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->n_ahs_lock, flags);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	rdma_copy_ah_attr(&ah->attr, init_attr->ah_attr);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	if (dev->driver_f.notify_new_ah)
8062306a36Sopenharmony_ci		dev->driver_f.notify_new_ah(ibah->device,
8162306a36Sopenharmony_ci					    init_attr->ah_attr, ah);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	return 0;
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci/**
8762306a36Sopenharmony_ci * rvt_destroy_ah - Destroy an address handle
8862306a36Sopenharmony_ci * @ibah: address handle
8962306a36Sopenharmony_ci * @destroy_flags: destroy address handle flags (see enum rdma_destroy_ah_flags)
9062306a36Sopenharmony_ci * Return: 0 on success
9162306a36Sopenharmony_ci */
9262306a36Sopenharmony_ciint rvt_destroy_ah(struct ib_ah *ibah, u32 destroy_flags)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	struct rvt_dev_info *dev = ib_to_rvt(ibah->device);
9562306a36Sopenharmony_ci	struct rvt_ah *ah = ibah_to_rvtah(ibah);
9662306a36Sopenharmony_ci	unsigned long flags;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	spin_lock_irqsave(&dev->n_ahs_lock, flags);
9962306a36Sopenharmony_ci	dev->n_ahs_allocated--;
10062306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->n_ahs_lock, flags);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	rdma_destroy_ah_attr(&ah->attr);
10362306a36Sopenharmony_ci	return 0;
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci/**
10762306a36Sopenharmony_ci * rvt_modify_ah - modify an ah with given attrs
10862306a36Sopenharmony_ci * @ibah: address handle to modify
10962306a36Sopenharmony_ci * @ah_attr: attrs to apply
11062306a36Sopenharmony_ci *
11162306a36Sopenharmony_ci * Return: 0 on success
11262306a36Sopenharmony_ci */
11362306a36Sopenharmony_ciint rvt_modify_ah(struct ib_ah *ibah, struct rdma_ah_attr *ah_attr)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	struct rvt_ah *ah = ibah_to_rvtah(ibah);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	if (rvt_check_ah(ibah->device, ah_attr))
11862306a36Sopenharmony_ci		return -EINVAL;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	ah->attr = *ah_attr;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	return 0;
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci/**
12662306a36Sopenharmony_ci * rvt_query_ah - return attrs for ah
12762306a36Sopenharmony_ci * @ibah: address handle to query
12862306a36Sopenharmony_ci * @ah_attr: return info in this
12962306a36Sopenharmony_ci *
13062306a36Sopenharmony_ci * Return: always 0
13162306a36Sopenharmony_ci */
13262306a36Sopenharmony_ciint rvt_query_ah(struct ib_ah *ibah, struct rdma_ah_attr *ah_attr)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	struct rvt_ah *ah = ibah_to_rvtah(ibah);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	*ah_attr = ah->attr;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	return 0;
13962306a36Sopenharmony_ci}
140