756 lines · 18.2 KB · C
RawDownload
/* * Copyright (c) 2016 Mellanox Technologies Ltd.  All rights reserved. * * This software is available to you under a choice of one of two * licenses.  You may choose to be licensed under the terms of the GNU * General Public License (GPL) Version 2, available from the file * COPYING in the main directory of this source tree, or the * OpenIB.org BSD license below: * *     Redistribution and use in source and binary forms, with or *     without modification, are permitted provided that the following *     conditions are met: * *      - Redistributions of source code must retain the above *        copyright notice, this list of conditions and the following *        disclaimer. * *      - Redistributions in binary form must reproduce the above *        copyright notice, this list of conditions and the following *        disclaimer in the documentation and/or other materials *        provided with the distribution. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include <linux/security.h>#include <linux/completion.h>#include <linux/list.h> #include <rdma/ib_verbs.h>#include <rdma/ib_cache.h>#include "core_priv.h"#include "mad_priv.h" static LIST_HEAD(mad_agent_list);/* Lock to protect mad_agent_list */static DEFINE_SPINLOCK(mad_agent_list_lock); static struct pkey_index_qp_list *get_pkey_idx_qp_list(struct ib_port_pkey *pp){	struct pkey_index_qp_list *pkey = NULL;	struct pkey_index_qp_list *tmp_pkey;	struct ib_device *dev = pp->sec->dev; 	spin_lock(&dev->port_data[pp->port_num].pkey_list_lock);	list_for_each_entry (tmp_pkey, &dev->port_data[pp->port_num].pkey_list,			     pkey_index_list) {		if (tmp_pkey->pkey_index == pp->pkey_index) {			pkey = tmp_pkey;			break;		}	}	spin_unlock(&dev->port_data[pp->port_num].pkey_list_lock);	return pkey;} static int get_pkey_and_subnet_prefix(struct ib_port_pkey *pp,				      u16 *pkey,				      u64 *subnet_prefix){	struct ib_device *dev = pp->sec->dev;	int ret; 	ret = ib_get_cached_pkey(dev, pp->port_num, pp->pkey_index, pkey);	if (ret)		return ret; 	ib_get_cached_subnet_prefix(dev, pp->port_num, subnet_prefix); 	return ret;} static int enforce_qp_pkey_security(u16 pkey,				    u64 subnet_prefix,				    struct ib_qp_security *qp_sec){	struct ib_qp_security *shared_qp_sec;	int ret; 	ret = security_ib_pkey_access(qp_sec->security, subnet_prefix, pkey);	if (ret)		return ret; 	list_for_each_entry(shared_qp_sec,			    &qp_sec->shared_qp_list,			    shared_qp_list) {		ret = security_ib_pkey_access(shared_qp_sec->security,					      subnet_prefix,					      pkey);		if (ret)			return ret;	}	return 0;} /* The caller of this function must hold the QP security * mutex of the QP of the security structure in *pps. * * It takes separate ports_pkeys and security structure * because in some cases the pps will be for a new settings * or the pps will be for the real QP and security structure * will be for a shared QP. */static int check_qp_port_pkey_settings(struct ib_ports_pkeys *pps,				       struct ib_qp_security *sec){	u64 subnet_prefix;	u16 pkey;	int ret = 0; 	if (!pps)		return 0; 	if (pps->main.state != IB_PORT_PKEY_NOT_VALID) {		ret = get_pkey_and_subnet_prefix(&pps->main,						 &pkey,						 &subnet_prefix);		if (ret)			return ret; 		ret = enforce_qp_pkey_security(pkey,					       subnet_prefix,					       sec);		if (ret)			return ret;	} 	if (pps->alt.state != IB_PORT_PKEY_NOT_VALID) {		ret = get_pkey_and_subnet_prefix(&pps->alt,						 &pkey,						 &subnet_prefix);		if (ret)			return ret; 		ret = enforce_qp_pkey_security(pkey,					       subnet_prefix,					       sec);	} 	return ret;} /* The caller of this function must hold the QP security * mutex. */static void qp_to_error(struct ib_qp_security *sec){	struct ib_qp_security *shared_qp_sec;	struct ib_qp_attr attr = {		.qp_state = IB_QPS_ERR	};	struct ib_event event = {		.event = IB_EVENT_QP_FATAL	}; 	/* If the QP is in the process of being destroyed	 * the qp pointer in the security structure is	 * undefined.  It cannot be modified now.	 */	if (sec->destroying)		return; 	ib_modify_qp(sec->qp,		     &attr,		     IB_QP_STATE); 	if (sec->qp->event_handler && sec->qp->qp_context) {		event.element.qp = sec->qp;		sec->qp->event_handler(&event,				       sec->qp->qp_context);	} 	list_for_each_entry(shared_qp_sec,			    &sec->shared_qp_list,			    shared_qp_list) {		struct ib_qp *qp = shared_qp_sec->qp; 		if (qp->event_handler && qp->qp_context) {			event.element.qp = qp;			event.device = qp->device;			qp->event_handler(&event,					  qp->qp_context);		}	}} static inline void check_pkey_qps(struct pkey_index_qp_list *pkey,				  struct ib_device *device,				  u32 port_num,				  u64 subnet_prefix){	struct ib_port_pkey *pp, *tmp_pp;	bool comp;	LIST_HEAD(to_error_list);	u16 pkey_val; 	if (!ib_get_cached_pkey(device,				port_num,				pkey->pkey_index,				&pkey_val)) {		spin_lock(&pkey->qp_list_lock);		list_for_each_entry(pp, &pkey->qp_list, qp_list) {			if (atomic_read(&pp->sec->error_list_count))				continue; 			if (enforce_qp_pkey_security(pkey_val,						     subnet_prefix,						     pp->sec)) {				atomic_inc(&pp->sec->error_list_count);				list_add(&pp->to_error_list,					 &to_error_list);			}		}		spin_unlock(&pkey->qp_list_lock);	} 	list_for_each_entry_safe(pp,				 tmp_pp,				 &to_error_list,				 to_error_list) {		mutex_lock(&pp->sec->mutex);		qp_to_error(pp->sec);		list_del(&pp->to_error_list);		atomic_dec(&pp->sec->error_list_count);		comp = pp->sec->destroying;		mutex_unlock(&pp->sec->mutex); 		if (comp)			complete(&pp->sec->error_complete);	}} /* The caller of this function must hold the QP security * mutex. */static int port_pkey_list_insert(struct ib_port_pkey *pp){	struct pkey_index_qp_list *tmp_pkey;	struct pkey_index_qp_list *pkey;	struct ib_device *dev;	u32 port_num = pp->port_num;	int ret = 0; 	if (pp->state != IB_PORT_PKEY_VALID)		return 0; 	dev = pp->sec->dev; 	pkey = get_pkey_idx_qp_list(pp); 	if (!pkey) {		bool found = false; 		pkey = kzalloc_obj(*pkey);		if (!pkey)			return -ENOMEM; 		spin_lock(&dev->port_data[port_num].pkey_list_lock);		/* Check for the PKey again.  A racing process may		 * have created it.		 */		list_for_each_entry(tmp_pkey,				    &dev->port_data[port_num].pkey_list,				    pkey_index_list) {			if (tmp_pkey->pkey_index == pp->pkey_index) {				kfree(pkey);				pkey = tmp_pkey;				found = true;				break;			}		} 		if (!found) {			pkey->pkey_index = pp->pkey_index;			spin_lock_init(&pkey->qp_list_lock);			INIT_LIST_HEAD(&pkey->qp_list);			list_add(&pkey->pkey_index_list,				 &dev->port_data[port_num].pkey_list);		}		spin_unlock(&dev->port_data[port_num].pkey_list_lock);	} 	spin_lock(&pkey->qp_list_lock);	list_add(&pp->qp_list, &pkey->qp_list);	spin_unlock(&pkey->qp_list_lock); 	pp->state = IB_PORT_PKEY_LISTED; 	return ret;} /* The caller of this function must hold the QP security * mutex. */static void port_pkey_list_remove(struct ib_port_pkey *pp){	struct pkey_index_qp_list *pkey; 	if (pp->state != IB_PORT_PKEY_LISTED)		return; 	pkey = get_pkey_idx_qp_list(pp); 	spin_lock(&pkey->qp_list_lock);	list_del(&pp->qp_list);	spin_unlock(&pkey->qp_list_lock); 	/* The setting may still be valid, i.e. after	 * a destroy has failed for example.	 */	pp->state = IB_PORT_PKEY_VALID;} static void destroy_qp_security(struct ib_qp_security *sec){	security_ib_free_security(sec->security);	kfree(sec->ports_pkeys);	kfree(sec);} /* The caller of this function must hold the QP security * mutex. */static struct ib_ports_pkeys *get_new_pps(const struct ib_qp *qp,					  const struct ib_qp_attr *qp_attr,					  int qp_attr_mask){	struct ib_ports_pkeys *new_pps;	struct ib_ports_pkeys *qp_pps = qp->qp_sec->ports_pkeys; 	new_pps = kzalloc_obj(*new_pps);	if (!new_pps)		return NULL; 	if (qp_attr_mask & IB_QP_PORT)		new_pps->main.port_num = qp_attr->port_num;	else if (qp_pps)		new_pps->main.port_num = qp_pps->main.port_num; 	if (qp_attr_mask & IB_QP_PKEY_INDEX)		new_pps->main.pkey_index = qp_attr->pkey_index;	else if (qp_pps)		new_pps->main.pkey_index = qp_pps->main.pkey_index; 	if (((qp_attr_mask & IB_QP_PKEY_INDEX) &&	     (qp_attr_mask & IB_QP_PORT)) ||	    (qp_pps && qp_pps->main.state != IB_PORT_PKEY_NOT_VALID))		new_pps->main.state = IB_PORT_PKEY_VALID; 	if (qp_attr_mask & IB_QP_ALT_PATH) {		new_pps->alt.port_num = qp_attr->alt_port_num;		new_pps->alt.pkey_index = qp_attr->alt_pkey_index;		new_pps->alt.state = IB_PORT_PKEY_VALID;	} else if (qp_pps) {		new_pps->alt.port_num = qp_pps->alt.port_num;		new_pps->alt.pkey_index = qp_pps->alt.pkey_index;		if (qp_pps->alt.state != IB_PORT_PKEY_NOT_VALID)			new_pps->alt.state = IB_PORT_PKEY_VALID;	} 	new_pps->main.sec = qp->qp_sec;	new_pps->alt.sec = qp->qp_sec;	return new_pps;} int ib_open_shared_qp_security(struct ib_qp *qp, struct ib_device *dev){	struct ib_qp *real_qp = qp->real_qp;	int ret; 	ret = ib_create_qp_security(qp, dev); 	if (ret)		return ret; 	if (!qp->qp_sec)		return 0; 	mutex_lock(&real_qp->qp_sec->mutex);	ret = check_qp_port_pkey_settings(real_qp->qp_sec->ports_pkeys,					  qp->qp_sec); 	if (ret)		goto ret; 	if (qp != real_qp)		list_add(&qp->qp_sec->shared_qp_list,			 &real_qp->qp_sec->shared_qp_list);ret:	mutex_unlock(&real_qp->qp_sec->mutex);	if (ret)		destroy_qp_security(qp->qp_sec); 	return ret;} void ib_close_shared_qp_security(struct ib_qp_security *sec){	struct ib_qp *real_qp = sec->qp->real_qp; 	mutex_lock(&real_qp->qp_sec->mutex);	list_del(&sec->shared_qp_list);	mutex_unlock(&real_qp->qp_sec->mutex); 	destroy_qp_security(sec);} int ib_create_qp_security(struct ib_qp *qp, struct ib_device *dev){	unsigned int i;	bool is_ib = false;	int ret; 	rdma_for_each_port (dev, i) {		is_ib = rdma_protocol_ib(dev, i);		if (is_ib)			break;	} 	/* If this isn't an IB device don't create the security context */	if (!is_ib)		return 0; 	qp->qp_sec = kzalloc_obj(*qp->qp_sec);	if (!qp->qp_sec)		return -ENOMEM; 	qp->qp_sec->qp = qp;	qp->qp_sec->dev = dev;	mutex_init(&qp->qp_sec->mutex);	INIT_LIST_HEAD(&qp->qp_sec->shared_qp_list);	atomic_set(&qp->qp_sec->error_list_count, 0);	init_completion(&qp->qp_sec->error_complete);	ret = security_ib_alloc_security(&qp->qp_sec->security);	if (ret) {		kfree(qp->qp_sec);		qp->qp_sec = NULL;	} 	return ret;}EXPORT_SYMBOL(ib_create_qp_security); void ib_destroy_qp_security_begin(struct ib_qp_security *sec){	/* Return if not IB */	if (!sec)		return; 	mutex_lock(&sec->mutex); 	/* Remove the QP from the lists so it won't get added to	 * a to_error_list during the destroy process.	 */	if (sec->ports_pkeys) {		port_pkey_list_remove(&sec->ports_pkeys->main);		port_pkey_list_remove(&sec->ports_pkeys->alt);	} 	/* If the QP is already in one or more of those lists	 * the destroying flag will ensure the to error flow	 * doesn't operate on an undefined QP.	 */	sec->destroying = true; 	/* Record the error list count to know how many completions	 * to wait for.	 */	sec->error_comps_pending = atomic_read(&sec->error_list_count); 	mutex_unlock(&sec->mutex);} void ib_destroy_qp_security_abort(struct ib_qp_security *sec){	int ret;	int i; 	/* Return if not IB */	if (!sec)		return; 	/* If a concurrent cache update is in progress this	 * QP security could be marked for an error state	 * transition.  Wait for this to complete.	 */	for (i = 0; i < sec->error_comps_pending; i++)		wait_for_completion(&sec->error_complete); 	mutex_lock(&sec->mutex);	sec->destroying = false; 	/* Restore the position in the lists and verify

Lines 1–500 of 756.