162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright (c) 2016 Mellanox Technologies Ltd. All rights reserved. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * This software is available to you under a choice of one of two 562306a36Sopenharmony_ci * licenses. You may choose to be licensed under the terms of the GNU 662306a36Sopenharmony_ci * General Public License (GPL) Version 2, available from the file 762306a36Sopenharmony_ci * COPYING in the main directory of this source tree, or the 862306a36Sopenharmony_ci * OpenIB.org BSD license below: 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or 1162306a36Sopenharmony_ci * without modification, are permitted provided that the following 1262306a36Sopenharmony_ci * conditions are met: 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * - Redistributions of source code must retain the above 1562306a36Sopenharmony_ci * copyright notice, this list of conditions and the following 1662306a36Sopenharmony_ci * disclaimer. 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * - Redistributions in binary form must reproduce the above 1962306a36Sopenharmony_ci * copyright notice, this list of conditions and the following 2062306a36Sopenharmony_ci * disclaimer in the documentation and/or other materials 2162306a36Sopenharmony_ci * provided with the distribution. 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 2462306a36Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 2562306a36Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 2662306a36Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 2762306a36Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 2862306a36Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 2962306a36Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 3062306a36Sopenharmony_ci * SOFTWARE. 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#include <linux/security.h> 3462306a36Sopenharmony_ci#include <linux/completion.h> 3562306a36Sopenharmony_ci#include <linux/list.h> 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#include <rdma/ib_verbs.h> 3862306a36Sopenharmony_ci#include <rdma/ib_cache.h> 3962306a36Sopenharmony_ci#include "core_priv.h" 4062306a36Sopenharmony_ci#include "mad_priv.h" 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic LIST_HEAD(mad_agent_list); 4362306a36Sopenharmony_ci/* Lock to protect mad_agent_list */ 4462306a36Sopenharmony_cistatic DEFINE_SPINLOCK(mad_agent_list_lock); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic struct pkey_index_qp_list *get_pkey_idx_qp_list(struct ib_port_pkey *pp) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci struct pkey_index_qp_list *pkey = NULL; 4962306a36Sopenharmony_ci struct pkey_index_qp_list *tmp_pkey; 5062306a36Sopenharmony_ci struct ib_device *dev = pp->sec->dev; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci spin_lock(&dev->port_data[pp->port_num].pkey_list_lock); 5362306a36Sopenharmony_ci list_for_each_entry (tmp_pkey, &dev->port_data[pp->port_num].pkey_list, 5462306a36Sopenharmony_ci pkey_index_list) { 5562306a36Sopenharmony_ci if (tmp_pkey->pkey_index == pp->pkey_index) { 5662306a36Sopenharmony_ci pkey = tmp_pkey; 5762306a36Sopenharmony_ci break; 5862306a36Sopenharmony_ci } 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci spin_unlock(&dev->port_data[pp->port_num].pkey_list_lock); 6162306a36Sopenharmony_ci return pkey; 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic int get_pkey_and_subnet_prefix(struct ib_port_pkey *pp, 6562306a36Sopenharmony_ci u16 *pkey, 6662306a36Sopenharmony_ci u64 *subnet_prefix) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci struct ib_device *dev = pp->sec->dev; 6962306a36Sopenharmony_ci int ret; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci ret = ib_get_cached_pkey(dev, pp->port_num, pp->pkey_index, pkey); 7262306a36Sopenharmony_ci if (ret) 7362306a36Sopenharmony_ci return ret; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci ib_get_cached_subnet_prefix(dev, pp->port_num, subnet_prefix); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci return ret; 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic int enforce_qp_pkey_security(u16 pkey, 8162306a36Sopenharmony_ci u64 subnet_prefix, 8262306a36Sopenharmony_ci struct ib_qp_security *qp_sec) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci struct ib_qp_security *shared_qp_sec; 8562306a36Sopenharmony_ci int ret; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci ret = security_ib_pkey_access(qp_sec->security, subnet_prefix, pkey); 8862306a36Sopenharmony_ci if (ret) 8962306a36Sopenharmony_ci return ret; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci list_for_each_entry(shared_qp_sec, 9262306a36Sopenharmony_ci &qp_sec->shared_qp_list, 9362306a36Sopenharmony_ci shared_qp_list) { 9462306a36Sopenharmony_ci ret = security_ib_pkey_access(shared_qp_sec->security, 9562306a36Sopenharmony_ci subnet_prefix, 9662306a36Sopenharmony_ci pkey); 9762306a36Sopenharmony_ci if (ret) 9862306a36Sopenharmony_ci return ret; 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci return 0; 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci/* The caller of this function must hold the QP security 10462306a36Sopenharmony_ci * mutex of the QP of the security structure in *pps. 10562306a36Sopenharmony_ci * 10662306a36Sopenharmony_ci * It takes separate ports_pkeys and security structure 10762306a36Sopenharmony_ci * because in some cases the pps will be for a new settings 10862306a36Sopenharmony_ci * or the pps will be for the real QP and security structure 10962306a36Sopenharmony_ci * will be for a shared QP. 11062306a36Sopenharmony_ci */ 11162306a36Sopenharmony_cistatic int check_qp_port_pkey_settings(struct ib_ports_pkeys *pps, 11262306a36Sopenharmony_ci struct ib_qp_security *sec) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci u64 subnet_prefix; 11562306a36Sopenharmony_ci u16 pkey; 11662306a36Sopenharmony_ci int ret = 0; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci if (!pps) 11962306a36Sopenharmony_ci return 0; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci if (pps->main.state != IB_PORT_PKEY_NOT_VALID) { 12262306a36Sopenharmony_ci ret = get_pkey_and_subnet_prefix(&pps->main, 12362306a36Sopenharmony_ci &pkey, 12462306a36Sopenharmony_ci &subnet_prefix); 12562306a36Sopenharmony_ci if (ret) 12662306a36Sopenharmony_ci return ret; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci ret = enforce_qp_pkey_security(pkey, 12962306a36Sopenharmony_ci subnet_prefix, 13062306a36Sopenharmony_ci sec); 13162306a36Sopenharmony_ci if (ret) 13262306a36Sopenharmony_ci return ret; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci if (pps->alt.state != IB_PORT_PKEY_NOT_VALID) { 13662306a36Sopenharmony_ci ret = get_pkey_and_subnet_prefix(&pps->alt, 13762306a36Sopenharmony_ci &pkey, 13862306a36Sopenharmony_ci &subnet_prefix); 13962306a36Sopenharmony_ci if (ret) 14062306a36Sopenharmony_ci return ret; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci ret = enforce_qp_pkey_security(pkey, 14362306a36Sopenharmony_ci subnet_prefix, 14462306a36Sopenharmony_ci sec); 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci return ret; 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci/* The caller of this function must hold the QP security 15162306a36Sopenharmony_ci * mutex. 15262306a36Sopenharmony_ci */ 15362306a36Sopenharmony_cistatic void qp_to_error(struct ib_qp_security *sec) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci struct ib_qp_security *shared_qp_sec; 15662306a36Sopenharmony_ci struct ib_qp_attr attr = { 15762306a36Sopenharmony_ci .qp_state = IB_QPS_ERR 15862306a36Sopenharmony_ci }; 15962306a36Sopenharmony_ci struct ib_event event = { 16062306a36Sopenharmony_ci .event = IB_EVENT_QP_FATAL 16162306a36Sopenharmony_ci }; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci /* If the QP is in the process of being destroyed 16462306a36Sopenharmony_ci * the qp pointer in the security structure is 16562306a36Sopenharmony_ci * undefined. It cannot be modified now. 16662306a36Sopenharmony_ci */ 16762306a36Sopenharmony_ci if (sec->destroying) 16862306a36Sopenharmony_ci return; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci ib_modify_qp(sec->qp, 17162306a36Sopenharmony_ci &attr, 17262306a36Sopenharmony_ci IB_QP_STATE); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci if (sec->qp->event_handler && sec->qp->qp_context) { 17562306a36Sopenharmony_ci event.element.qp = sec->qp; 17662306a36Sopenharmony_ci sec->qp->event_handler(&event, 17762306a36Sopenharmony_ci sec->qp->qp_context); 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci list_for_each_entry(shared_qp_sec, 18162306a36Sopenharmony_ci &sec->shared_qp_list, 18262306a36Sopenharmony_ci shared_qp_list) { 18362306a36Sopenharmony_ci struct ib_qp *qp = shared_qp_sec->qp; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci if (qp->event_handler && qp->qp_context) { 18662306a36Sopenharmony_ci event.element.qp = qp; 18762306a36Sopenharmony_ci event.device = qp->device; 18862306a36Sopenharmony_ci qp->event_handler(&event, 18962306a36Sopenharmony_ci qp->qp_context); 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic inline void check_pkey_qps(struct pkey_index_qp_list *pkey, 19562306a36Sopenharmony_ci struct ib_device *device, 19662306a36Sopenharmony_ci u32 port_num, 19762306a36Sopenharmony_ci u64 subnet_prefix) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci struct ib_port_pkey *pp, *tmp_pp; 20062306a36Sopenharmony_ci bool comp; 20162306a36Sopenharmony_ci LIST_HEAD(to_error_list); 20262306a36Sopenharmony_ci u16 pkey_val; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci if (!ib_get_cached_pkey(device, 20562306a36Sopenharmony_ci port_num, 20662306a36Sopenharmony_ci pkey->pkey_index, 20762306a36Sopenharmony_ci &pkey_val)) { 20862306a36Sopenharmony_ci spin_lock(&pkey->qp_list_lock); 20962306a36Sopenharmony_ci list_for_each_entry(pp, &pkey->qp_list, qp_list) { 21062306a36Sopenharmony_ci if (atomic_read(&pp->sec->error_list_count)) 21162306a36Sopenharmony_ci continue; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if (enforce_qp_pkey_security(pkey_val, 21462306a36Sopenharmony_ci subnet_prefix, 21562306a36Sopenharmony_ci pp->sec)) { 21662306a36Sopenharmony_ci atomic_inc(&pp->sec->error_list_count); 21762306a36Sopenharmony_ci list_add(&pp->to_error_list, 21862306a36Sopenharmony_ci &to_error_list); 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci spin_unlock(&pkey->qp_list_lock); 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci list_for_each_entry_safe(pp, 22562306a36Sopenharmony_ci tmp_pp, 22662306a36Sopenharmony_ci &to_error_list, 22762306a36Sopenharmony_ci to_error_list) { 22862306a36Sopenharmony_ci mutex_lock(&pp->sec->mutex); 22962306a36Sopenharmony_ci qp_to_error(pp->sec); 23062306a36Sopenharmony_ci list_del(&pp->to_error_list); 23162306a36Sopenharmony_ci atomic_dec(&pp->sec->error_list_count); 23262306a36Sopenharmony_ci comp = pp->sec->destroying; 23362306a36Sopenharmony_ci mutex_unlock(&pp->sec->mutex); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci if (comp) 23662306a36Sopenharmony_ci complete(&pp->sec->error_complete); 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci/* The caller of this function must hold the QP security 24162306a36Sopenharmony_ci * mutex. 24262306a36Sopenharmony_ci */ 24362306a36Sopenharmony_cistatic int port_pkey_list_insert(struct ib_port_pkey *pp) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci struct pkey_index_qp_list *tmp_pkey; 24662306a36Sopenharmony_ci struct pkey_index_qp_list *pkey; 24762306a36Sopenharmony_ci struct ib_device *dev; 24862306a36Sopenharmony_ci u32 port_num = pp->port_num; 24962306a36Sopenharmony_ci int ret = 0; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci if (pp->state != IB_PORT_PKEY_VALID) 25262306a36Sopenharmony_ci return 0; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci dev = pp->sec->dev; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci pkey = get_pkey_idx_qp_list(pp); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci if (!pkey) { 25962306a36Sopenharmony_ci bool found = false; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci pkey = kzalloc(sizeof(*pkey), GFP_KERNEL); 26262306a36Sopenharmony_ci if (!pkey) 26362306a36Sopenharmony_ci return -ENOMEM; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci spin_lock(&dev->port_data[port_num].pkey_list_lock); 26662306a36Sopenharmony_ci /* Check for the PKey again. A racing process may 26762306a36Sopenharmony_ci * have created it. 26862306a36Sopenharmony_ci */ 26962306a36Sopenharmony_ci list_for_each_entry(tmp_pkey, 27062306a36Sopenharmony_ci &dev->port_data[port_num].pkey_list, 27162306a36Sopenharmony_ci pkey_index_list) { 27262306a36Sopenharmony_ci if (tmp_pkey->pkey_index == pp->pkey_index) { 27362306a36Sopenharmony_ci kfree(pkey); 27462306a36Sopenharmony_ci pkey = tmp_pkey; 27562306a36Sopenharmony_ci found = true; 27662306a36Sopenharmony_ci break; 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci if (!found) { 28162306a36Sopenharmony_ci pkey->pkey_index = pp->pkey_index; 28262306a36Sopenharmony_ci spin_lock_init(&pkey->qp_list_lock); 28362306a36Sopenharmony_ci INIT_LIST_HEAD(&pkey->qp_list); 28462306a36Sopenharmony_ci list_add(&pkey->pkey_index_list, 28562306a36Sopenharmony_ci &dev->port_data[port_num].pkey_list); 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci spin_unlock(&dev->port_data[port_num].pkey_list_lock); 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci spin_lock(&pkey->qp_list_lock); 29162306a36Sopenharmony_ci list_add(&pp->qp_list, &pkey->qp_list); 29262306a36Sopenharmony_ci spin_unlock(&pkey->qp_list_lock); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci pp->state = IB_PORT_PKEY_LISTED; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci return ret; 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci/* The caller of this function must hold the QP security 30062306a36Sopenharmony_ci * mutex. 30162306a36Sopenharmony_ci */ 30262306a36Sopenharmony_cistatic void port_pkey_list_remove(struct ib_port_pkey *pp) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci struct pkey_index_qp_list *pkey; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci if (pp->state != IB_PORT_PKEY_LISTED) 30762306a36Sopenharmony_ci return; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci pkey = get_pkey_idx_qp_list(pp); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci spin_lock(&pkey->qp_list_lock); 31262306a36Sopenharmony_ci list_del(&pp->qp_list); 31362306a36Sopenharmony_ci spin_unlock(&pkey->qp_list_lock); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci /* The setting may still be valid, i.e. after 31662306a36Sopenharmony_ci * a destroy has failed for example. 31762306a36Sopenharmony_ci */ 31862306a36Sopenharmony_ci pp->state = IB_PORT_PKEY_VALID; 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistatic void destroy_qp_security(struct ib_qp_security *sec) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci security_ib_free_security(sec->security); 32462306a36Sopenharmony_ci kfree(sec->ports_pkeys); 32562306a36Sopenharmony_ci kfree(sec); 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci/* The caller of this function must hold the QP security 32962306a36Sopenharmony_ci * mutex. 33062306a36Sopenharmony_ci */ 33162306a36Sopenharmony_cistatic struct ib_ports_pkeys *get_new_pps(const struct ib_qp *qp, 33262306a36Sopenharmony_ci const struct ib_qp_attr *qp_attr, 33362306a36Sopenharmony_ci int qp_attr_mask) 33462306a36Sopenharmony_ci{ 33562306a36Sopenharmony_ci struct ib_ports_pkeys *new_pps; 33662306a36Sopenharmony_ci struct ib_ports_pkeys *qp_pps = qp->qp_sec->ports_pkeys; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci new_pps = kzalloc(sizeof(*new_pps), GFP_KERNEL); 33962306a36Sopenharmony_ci if (!new_pps) 34062306a36Sopenharmony_ci return NULL; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci if (qp_attr_mask & IB_QP_PORT) 34362306a36Sopenharmony_ci new_pps->main.port_num = qp_attr->port_num; 34462306a36Sopenharmony_ci else if (qp_pps) 34562306a36Sopenharmony_ci new_pps->main.port_num = qp_pps->main.port_num; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci if (qp_attr_mask & IB_QP_PKEY_INDEX) 34862306a36Sopenharmony_ci new_pps->main.pkey_index = qp_attr->pkey_index; 34962306a36Sopenharmony_ci else if (qp_pps) 35062306a36Sopenharmony_ci new_pps->main.pkey_index = qp_pps->main.pkey_index; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci if (((qp_attr_mask & IB_QP_PKEY_INDEX) && 35362306a36Sopenharmony_ci (qp_attr_mask & IB_QP_PORT)) || 35462306a36Sopenharmony_ci (qp_pps && qp_pps->main.state != IB_PORT_PKEY_NOT_VALID)) 35562306a36Sopenharmony_ci new_pps->main.state = IB_PORT_PKEY_VALID; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci if (qp_attr_mask & IB_QP_ALT_PATH) { 35862306a36Sopenharmony_ci new_pps->alt.port_num = qp_attr->alt_port_num; 35962306a36Sopenharmony_ci new_pps->alt.pkey_index = qp_attr->alt_pkey_index; 36062306a36Sopenharmony_ci new_pps->alt.state = IB_PORT_PKEY_VALID; 36162306a36Sopenharmony_ci } else if (qp_pps) { 36262306a36Sopenharmony_ci new_pps->alt.port_num = qp_pps->alt.port_num; 36362306a36Sopenharmony_ci new_pps->alt.pkey_index = qp_pps->alt.pkey_index; 36462306a36Sopenharmony_ci if (qp_pps->alt.state != IB_PORT_PKEY_NOT_VALID) 36562306a36Sopenharmony_ci new_pps->alt.state = IB_PORT_PKEY_VALID; 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci new_pps->main.sec = qp->qp_sec; 36962306a36Sopenharmony_ci new_pps->alt.sec = qp->qp_sec; 37062306a36Sopenharmony_ci return new_pps; 37162306a36Sopenharmony_ci} 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ciint ib_open_shared_qp_security(struct ib_qp *qp, struct ib_device *dev) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci struct ib_qp *real_qp = qp->real_qp; 37662306a36Sopenharmony_ci int ret; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci ret = ib_create_qp_security(qp, dev); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci if (ret) 38162306a36Sopenharmony_ci return ret; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci if (!qp->qp_sec) 38462306a36Sopenharmony_ci return 0; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci mutex_lock(&real_qp->qp_sec->mutex); 38762306a36Sopenharmony_ci ret = check_qp_port_pkey_settings(real_qp->qp_sec->ports_pkeys, 38862306a36Sopenharmony_ci qp->qp_sec); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci if (ret) 39162306a36Sopenharmony_ci goto ret; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci if (qp != real_qp) 39462306a36Sopenharmony_ci list_add(&qp->qp_sec->shared_qp_list, 39562306a36Sopenharmony_ci &real_qp->qp_sec->shared_qp_list); 39662306a36Sopenharmony_ciret: 39762306a36Sopenharmony_ci mutex_unlock(&real_qp->qp_sec->mutex); 39862306a36Sopenharmony_ci if (ret) 39962306a36Sopenharmony_ci destroy_qp_security(qp->qp_sec); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci return ret; 40262306a36Sopenharmony_ci} 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_civoid ib_close_shared_qp_security(struct ib_qp_security *sec) 40562306a36Sopenharmony_ci{ 40662306a36Sopenharmony_ci struct ib_qp *real_qp = sec->qp->real_qp; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci mutex_lock(&real_qp->qp_sec->mutex); 40962306a36Sopenharmony_ci list_del(&sec->shared_qp_list); 41062306a36Sopenharmony_ci mutex_unlock(&real_qp->qp_sec->mutex); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci destroy_qp_security(sec); 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ciint ib_create_qp_security(struct ib_qp *qp, struct ib_device *dev) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci unsigned int i; 41862306a36Sopenharmony_ci bool is_ib = false; 41962306a36Sopenharmony_ci int ret; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci rdma_for_each_port (dev, i) { 42262306a36Sopenharmony_ci is_ib = rdma_protocol_ib(dev, i); 42362306a36Sopenharmony_ci if (is_ib) 42462306a36Sopenharmony_ci break; 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci /* If this isn't an IB device don't create the security context */ 42862306a36Sopenharmony_ci if (!is_ib) 42962306a36Sopenharmony_ci return 0; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci qp->qp_sec = kzalloc(sizeof(*qp->qp_sec), GFP_KERNEL); 43262306a36Sopenharmony_ci if (!qp->qp_sec) 43362306a36Sopenharmony_ci return -ENOMEM; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci qp->qp_sec->qp = qp; 43662306a36Sopenharmony_ci qp->qp_sec->dev = dev; 43762306a36Sopenharmony_ci mutex_init(&qp->qp_sec->mutex); 43862306a36Sopenharmony_ci INIT_LIST_HEAD(&qp->qp_sec->shared_qp_list); 43962306a36Sopenharmony_ci atomic_set(&qp->qp_sec->error_list_count, 0); 44062306a36Sopenharmony_ci init_completion(&qp->qp_sec->error_complete); 44162306a36Sopenharmony_ci ret = security_ib_alloc_security(&qp->qp_sec->security); 44262306a36Sopenharmony_ci if (ret) { 44362306a36Sopenharmony_ci kfree(qp->qp_sec); 44462306a36Sopenharmony_ci qp->qp_sec = NULL; 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci return ret; 44862306a36Sopenharmony_ci} 44962306a36Sopenharmony_ciEXPORT_SYMBOL(ib_create_qp_security); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_civoid ib_destroy_qp_security_begin(struct ib_qp_security *sec) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci /* Return if not IB */ 45462306a36Sopenharmony_ci if (!sec) 45562306a36Sopenharmony_ci return; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci mutex_lock(&sec->mutex); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci /* Remove the QP from the lists so it won't get added to 46062306a36Sopenharmony_ci * a to_error_list during the destroy process. 46162306a36Sopenharmony_ci */ 46262306a36Sopenharmony_ci if (sec->ports_pkeys) { 46362306a36Sopenharmony_ci port_pkey_list_remove(&sec->ports_pkeys->main); 46462306a36Sopenharmony_ci port_pkey_list_remove(&sec->ports_pkeys->alt); 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci /* If the QP is already in one or more of those lists 46862306a36Sopenharmony_ci * the destroying flag will ensure the to error flow 46962306a36Sopenharmony_ci * doesn't operate on an undefined QP. 47062306a36Sopenharmony_ci */ 47162306a36Sopenharmony_ci sec->destroying = true; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci /* Record the error list count to know how many completions 47462306a36Sopenharmony_ci * to wait for. 47562306a36Sopenharmony_ci */ 47662306a36Sopenharmony_ci sec->error_comps_pending = atomic_read(&sec->error_list_count); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci mutex_unlock(&sec->mutex); 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_civoid ib_destroy_qp_security_abort(struct ib_qp_security *sec) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci int ret; 48462306a36Sopenharmony_ci int i; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci /* Return if not IB */ 48762306a36Sopenharmony_ci if (!sec) 48862306a36Sopenharmony_ci return; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci /* If a concurrent cache update is in progress this 49162306a36Sopenharmony_ci * QP security could be marked for an error state 49262306a36Sopenharmony_ci * transition. Wait for this to complete. 49362306a36Sopenharmony_ci */ 49462306a36Sopenharmony_ci for (i = 0; i < sec->error_comps_pending; i++) 49562306a36Sopenharmony_ci wait_for_completion(&sec->error_complete); 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci mutex_lock(&sec->mutex); 49862306a36Sopenharmony_ci sec->destroying = false; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci /* Restore the position in the lists and verify 50162306a36Sopenharmony_ci * access is still allowed in case a cache update 50262306a36Sopenharmony_ci * occurred while attempting to destroy. 50362306a36Sopenharmony_ci * 50462306a36Sopenharmony_ci * Because these setting were listed already 50562306a36Sopenharmony_ci * and removed during ib_destroy_qp_security_begin 50662306a36Sopenharmony_ci * we know the pkey_index_qp_list for the PKey 50762306a36Sopenharmony_ci * already exists so port_pkey_list_insert won't fail. 50862306a36Sopenharmony_ci */ 50962306a36Sopenharmony_ci if (sec->ports_pkeys) { 51062306a36Sopenharmony_ci port_pkey_list_insert(&sec->ports_pkeys->main); 51162306a36Sopenharmony_ci port_pkey_list_insert(&sec->ports_pkeys->alt); 51262306a36Sopenharmony_ci } 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci ret = check_qp_port_pkey_settings(sec->ports_pkeys, sec); 51562306a36Sopenharmony_ci if (ret) 51662306a36Sopenharmony_ci qp_to_error(sec); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci mutex_unlock(&sec->mutex); 51962306a36Sopenharmony_ci} 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_civoid ib_destroy_qp_security_end(struct ib_qp_security *sec) 52262306a36Sopenharmony_ci{ 52362306a36Sopenharmony_ci int i; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci /* Return if not IB */ 52662306a36Sopenharmony_ci if (!sec) 52762306a36Sopenharmony_ci return; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci /* If a concurrent cache update is occurring we must 53062306a36Sopenharmony_ci * wait until this QP security structure is processed 53162306a36Sopenharmony_ci * in the QP to error flow before destroying it because 53262306a36Sopenharmony_ci * the to_error_list is in use. 53362306a36Sopenharmony_ci */ 53462306a36Sopenharmony_ci for (i = 0; i < sec->error_comps_pending; i++) 53562306a36Sopenharmony_ci wait_for_completion(&sec->error_complete); 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci destroy_qp_security(sec); 53862306a36Sopenharmony_ci} 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_civoid ib_security_cache_change(struct ib_device *device, 54162306a36Sopenharmony_ci u32 port_num, 54262306a36Sopenharmony_ci u64 subnet_prefix) 54362306a36Sopenharmony_ci{ 54462306a36Sopenharmony_ci struct pkey_index_qp_list *pkey; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci list_for_each_entry (pkey, &device->port_data[port_num].pkey_list, 54762306a36Sopenharmony_ci pkey_index_list) { 54862306a36Sopenharmony_ci check_pkey_qps(pkey, 54962306a36Sopenharmony_ci device, 55062306a36Sopenharmony_ci port_num, 55162306a36Sopenharmony_ci subnet_prefix); 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci} 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_civoid ib_security_release_port_pkey_list(struct ib_device *device) 55662306a36Sopenharmony_ci{ 55762306a36Sopenharmony_ci struct pkey_index_qp_list *pkey, *tmp_pkey; 55862306a36Sopenharmony_ci unsigned int i; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci rdma_for_each_port (device, i) { 56162306a36Sopenharmony_ci list_for_each_entry_safe(pkey, 56262306a36Sopenharmony_ci tmp_pkey, 56362306a36Sopenharmony_ci &device->port_data[i].pkey_list, 56462306a36Sopenharmony_ci pkey_index_list) { 56562306a36Sopenharmony_ci list_del(&pkey->pkey_index_list); 56662306a36Sopenharmony_ci kfree(pkey); 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci} 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ciint ib_security_modify_qp(struct ib_qp *qp, 57262306a36Sopenharmony_ci struct ib_qp_attr *qp_attr, 57362306a36Sopenharmony_ci int qp_attr_mask, 57462306a36Sopenharmony_ci struct ib_udata *udata) 57562306a36Sopenharmony_ci{ 57662306a36Sopenharmony_ci int ret = 0; 57762306a36Sopenharmony_ci struct ib_ports_pkeys *tmp_pps; 57862306a36Sopenharmony_ci struct ib_ports_pkeys *new_pps = NULL; 57962306a36Sopenharmony_ci struct ib_qp *real_qp = qp->real_qp; 58062306a36Sopenharmony_ci bool special_qp = (real_qp->qp_type == IB_QPT_SMI || 58162306a36Sopenharmony_ci real_qp->qp_type == IB_QPT_GSI || 58262306a36Sopenharmony_ci real_qp->qp_type >= IB_QPT_RESERVED1); 58362306a36Sopenharmony_ci bool pps_change = ((qp_attr_mask & (IB_QP_PKEY_INDEX | IB_QP_PORT)) || 58462306a36Sopenharmony_ci (qp_attr_mask & IB_QP_ALT_PATH)); 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci WARN_ONCE((qp_attr_mask & IB_QP_PORT && 58762306a36Sopenharmony_ci rdma_protocol_ib(real_qp->device, qp_attr->port_num) && 58862306a36Sopenharmony_ci !real_qp->qp_sec), 58962306a36Sopenharmony_ci "%s: QP security is not initialized for IB QP: %u\n", 59062306a36Sopenharmony_ci __func__, real_qp->qp_num); 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci /* The port/pkey settings are maintained only for the real QP. Open 59362306a36Sopenharmony_ci * handles on the real QP will be in the shared_qp_list. When 59462306a36Sopenharmony_ci * enforcing security on the real QP all the shared QPs will be 59562306a36Sopenharmony_ci * checked as well. 59662306a36Sopenharmony_ci */ 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci if (pps_change && !special_qp && real_qp->qp_sec) { 59962306a36Sopenharmony_ci mutex_lock(&real_qp->qp_sec->mutex); 60062306a36Sopenharmony_ci new_pps = get_new_pps(real_qp, 60162306a36Sopenharmony_ci qp_attr, 60262306a36Sopenharmony_ci qp_attr_mask); 60362306a36Sopenharmony_ci if (!new_pps) { 60462306a36Sopenharmony_ci mutex_unlock(&real_qp->qp_sec->mutex); 60562306a36Sopenharmony_ci return -ENOMEM; 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci /* Add this QP to the lists for the new port 60862306a36Sopenharmony_ci * and pkey settings before checking for permission 60962306a36Sopenharmony_ci * in case there is a concurrent cache update 61062306a36Sopenharmony_ci * occurring. Walking the list for a cache change 61162306a36Sopenharmony_ci * doesn't acquire the security mutex unless it's 61262306a36Sopenharmony_ci * sending the QP to error. 61362306a36Sopenharmony_ci */ 61462306a36Sopenharmony_ci ret = port_pkey_list_insert(&new_pps->main); 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci if (!ret) 61762306a36Sopenharmony_ci ret = port_pkey_list_insert(&new_pps->alt); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci if (!ret) 62062306a36Sopenharmony_ci ret = check_qp_port_pkey_settings(new_pps, 62162306a36Sopenharmony_ci real_qp->qp_sec); 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci if (!ret) 62562306a36Sopenharmony_ci ret = real_qp->device->ops.modify_qp(real_qp, 62662306a36Sopenharmony_ci qp_attr, 62762306a36Sopenharmony_ci qp_attr_mask, 62862306a36Sopenharmony_ci udata); 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci if (new_pps) { 63162306a36Sopenharmony_ci /* Clean up the lists and free the appropriate 63262306a36Sopenharmony_ci * ports_pkeys structure. 63362306a36Sopenharmony_ci */ 63462306a36Sopenharmony_ci if (ret) { 63562306a36Sopenharmony_ci tmp_pps = new_pps; 63662306a36Sopenharmony_ci } else { 63762306a36Sopenharmony_ci tmp_pps = real_qp->qp_sec->ports_pkeys; 63862306a36Sopenharmony_ci real_qp->qp_sec->ports_pkeys = new_pps; 63962306a36Sopenharmony_ci } 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci if (tmp_pps) { 64262306a36Sopenharmony_ci port_pkey_list_remove(&tmp_pps->main); 64362306a36Sopenharmony_ci port_pkey_list_remove(&tmp_pps->alt); 64462306a36Sopenharmony_ci } 64562306a36Sopenharmony_ci kfree(tmp_pps); 64662306a36Sopenharmony_ci mutex_unlock(&real_qp->qp_sec->mutex); 64762306a36Sopenharmony_ci } 64862306a36Sopenharmony_ci return ret; 64962306a36Sopenharmony_ci} 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_cistatic int ib_security_pkey_access(struct ib_device *dev, 65262306a36Sopenharmony_ci u32 port_num, 65362306a36Sopenharmony_ci u16 pkey_index, 65462306a36Sopenharmony_ci void *sec) 65562306a36Sopenharmony_ci{ 65662306a36Sopenharmony_ci u64 subnet_prefix; 65762306a36Sopenharmony_ci u16 pkey; 65862306a36Sopenharmony_ci int ret; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci if (!rdma_protocol_ib(dev, port_num)) 66162306a36Sopenharmony_ci return 0; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci ret = ib_get_cached_pkey(dev, port_num, pkey_index, &pkey); 66462306a36Sopenharmony_ci if (ret) 66562306a36Sopenharmony_ci return ret; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci ib_get_cached_subnet_prefix(dev, port_num, &subnet_prefix); 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci return security_ib_pkey_access(sec, subnet_prefix, pkey); 67062306a36Sopenharmony_ci} 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_civoid ib_mad_agent_security_change(void) 67362306a36Sopenharmony_ci{ 67462306a36Sopenharmony_ci struct ib_mad_agent *ag; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci spin_lock(&mad_agent_list_lock); 67762306a36Sopenharmony_ci list_for_each_entry(ag, 67862306a36Sopenharmony_ci &mad_agent_list, 67962306a36Sopenharmony_ci mad_agent_sec_list) 68062306a36Sopenharmony_ci WRITE_ONCE(ag->smp_allowed, 68162306a36Sopenharmony_ci !security_ib_endport_manage_subnet(ag->security, 68262306a36Sopenharmony_ci dev_name(&ag->device->dev), ag->port_num)); 68362306a36Sopenharmony_ci spin_unlock(&mad_agent_list_lock); 68462306a36Sopenharmony_ci} 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ciint ib_mad_agent_security_setup(struct ib_mad_agent *agent, 68762306a36Sopenharmony_ci enum ib_qp_type qp_type) 68862306a36Sopenharmony_ci{ 68962306a36Sopenharmony_ci int ret; 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci if (!rdma_protocol_ib(agent->device, agent->port_num)) 69262306a36Sopenharmony_ci return 0; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci INIT_LIST_HEAD(&agent->mad_agent_sec_list); 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci ret = security_ib_alloc_security(&agent->security); 69762306a36Sopenharmony_ci if (ret) 69862306a36Sopenharmony_ci return ret; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci if (qp_type != IB_QPT_SMI) 70162306a36Sopenharmony_ci return 0; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci spin_lock(&mad_agent_list_lock); 70462306a36Sopenharmony_ci ret = security_ib_endport_manage_subnet(agent->security, 70562306a36Sopenharmony_ci dev_name(&agent->device->dev), 70662306a36Sopenharmony_ci agent->port_num); 70762306a36Sopenharmony_ci if (ret) 70862306a36Sopenharmony_ci goto free_security; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci WRITE_ONCE(agent->smp_allowed, true); 71162306a36Sopenharmony_ci list_add(&agent->mad_agent_sec_list, &mad_agent_list); 71262306a36Sopenharmony_ci spin_unlock(&mad_agent_list_lock); 71362306a36Sopenharmony_ci return 0; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_cifree_security: 71662306a36Sopenharmony_ci spin_unlock(&mad_agent_list_lock); 71762306a36Sopenharmony_ci security_ib_free_security(agent->security); 71862306a36Sopenharmony_ci return ret; 71962306a36Sopenharmony_ci} 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_civoid ib_mad_agent_security_cleanup(struct ib_mad_agent *agent) 72262306a36Sopenharmony_ci{ 72362306a36Sopenharmony_ci if (!rdma_protocol_ib(agent->device, agent->port_num)) 72462306a36Sopenharmony_ci return; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci if (agent->qp->qp_type == IB_QPT_SMI) { 72762306a36Sopenharmony_ci spin_lock(&mad_agent_list_lock); 72862306a36Sopenharmony_ci list_del(&agent->mad_agent_sec_list); 72962306a36Sopenharmony_ci spin_unlock(&mad_agent_list_lock); 73062306a36Sopenharmony_ci } 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci security_ib_free_security(agent->security); 73362306a36Sopenharmony_ci} 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ciint ib_mad_enforce_security(struct ib_mad_agent_private *map, u16 pkey_index) 73662306a36Sopenharmony_ci{ 73762306a36Sopenharmony_ci if (!rdma_protocol_ib(map->agent.device, map->agent.port_num)) 73862306a36Sopenharmony_ci return 0; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci if (map->agent.qp->qp_type == IB_QPT_SMI) { 74162306a36Sopenharmony_ci if (!READ_ONCE(map->agent.smp_allowed)) 74262306a36Sopenharmony_ci return -EACCES; 74362306a36Sopenharmony_ci return 0; 74462306a36Sopenharmony_ci } 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci return ib_security_pkey_access(map->agent.device, 74762306a36Sopenharmony_ci map->agent.port_num, 74862306a36Sopenharmony_ci pkey_index, 74962306a36Sopenharmony_ci map->agent.security); 75062306a36Sopenharmony_ci} 751