mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	In scif_node_connect() we were returning if the initialization of p2p_ji fails. But at that time p2p_ij has already been initialized and resources allocated for it. And since p2p_ij is not added to the list till now so we will have a leak. Lets deinitialize and release the resources connected to p2p_ij. Signed-off-by: Sudip Mukherjee <sudip@vectorindia.org> Reviewed-by: Sudeep Dutt <sudeep.dutt@intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
		
			
				
	
	
		
			1354 lines
		
	
	
	
		
			39 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1354 lines
		
	
	
	
		
			39 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * Intel MIC Platform Software Stack (MPSS)
 | 
						|
 *
 | 
						|
 * Copyright(c) 2014 Intel Corporation.
 | 
						|
 *
 | 
						|
 * This program is free software; you can redistribute it and/or modify
 | 
						|
 * it under the terms of the GNU General Public License, version 2, as
 | 
						|
 * published by the Free Software Foundation.
 | 
						|
 *
 | 
						|
 * This program is distributed in the hope that it will be useful, but
 | 
						|
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 | 
						|
 * General Public License for more details.
 | 
						|
 *
 | 
						|
 * Intel SCIF driver.
 | 
						|
 *
 | 
						|
 */
 | 
						|
#include "../bus/scif_bus.h"
 | 
						|
#include "scif_peer_bus.h"
 | 
						|
#include "scif_main.h"
 | 
						|
#include "scif_nodeqp.h"
 | 
						|
#include "scif_map.h"
 | 
						|
 | 
						|
/*
 | 
						|
 ************************************************************************
 | 
						|
 * SCIF node Queue Pair (QP) setup flow:
 | 
						|
 *
 | 
						|
 * 1) SCIF driver gets probed with a scif_hw_dev via the scif_hw_bus
 | 
						|
 * 2) scif_setup_qp(..) allocates the local qp and calls
 | 
						|
 *	scif_setup_qp_connect(..) which allocates and maps the local
 | 
						|
 *	buffer for the inbound QP
 | 
						|
 * 3) The local node updates the device page with the DMA address of the QP
 | 
						|
 * 4) A delayed work is scheduled (qp_dwork) which periodically reads if
 | 
						|
 *	the peer node has updated its QP DMA address
 | 
						|
 * 5) Once a valid non zero address is found in the QP DMA address field
 | 
						|
 *	in the device page, the local node maps the remote node's QP,
 | 
						|
 *	updates its outbound QP and sends a SCIF_INIT message to the peer
 | 
						|
 * 6) The SCIF_INIT message is received by the peer node QP interrupt bottom
 | 
						|
 *	half handler by calling scif_init(..)
 | 
						|
 * 7) scif_init(..) registers a new SCIF peer node by calling
 | 
						|
 *	scif_peer_register_device(..) which signifies the addition of a new
 | 
						|
 *	SCIF node
 | 
						|
 * 8) On the mgmt node, P2P network setup/teardown is initiated if all the
 | 
						|
 *	remote nodes are online via scif_p2p_setup(..)
 | 
						|
 * 9) For P2P setup, the host maps the remote nodes' aperture and memory
 | 
						|
 *	bars and sends a SCIF_NODE_ADD message to both nodes
 | 
						|
 * 10) As part of scif_nodeadd, both nodes set up their local inbound
 | 
						|
 *	QPs and send a SCIF_NODE_ADD_ACK to the mgmt node
 | 
						|
 * 11) As part of scif_node_add_ack(..) the mgmt node forwards the
 | 
						|
 *	SCIF_NODE_ADD_ACK to the remote nodes
 | 
						|
 * 12) As part of scif_node_add_ack(..) the remote nodes update their
 | 
						|
 *	outbound QPs, make sure they can access memory on the remote node
 | 
						|
 *	and then add a new SCIF peer node by calling
 | 
						|
 *	scif_peer_register_device(..) which signifies the addition of a new
 | 
						|
 *	SCIF node.
 | 
						|
 * 13) The SCIF network is now established across all nodes.
 | 
						|
 *
 | 
						|
 ************************************************************************
 | 
						|
 * SCIF node QP teardown flow (initiated by non mgmt node):
 | 
						|
 *
 | 
						|
 * 1) SCIF driver gets a remove callback with a scif_hw_dev via the scif_hw_bus
 | 
						|
 * 2) The device page QP DMA address field is updated with 0x0
 | 
						|
 * 3) A non mgmt node now cleans up all local data structures and sends a
 | 
						|
 *	SCIF_EXIT message to the peer and waits for a SCIF_EXIT_ACK
 | 
						|
 * 4) As part of scif_exit(..) handling scif_disconnect_node(..) is called
 | 
						|
 * 5) scif_disconnect_node(..) sends a SCIF_NODE_REMOVE message to all the
 | 
						|
 *	peers and waits for a SCIF_NODE_REMOVE_ACK
 | 
						|
 * 6) As part of scif_node_remove(..) a remote node unregisters the peer
 | 
						|
 *	node from the SCIF network and sends a SCIF_NODE_REMOVE_ACK
 | 
						|
 * 7) When the mgmt node has received all the SCIF_NODE_REMOVE_ACKs
 | 
						|
 *	it sends itself a node remove message whose handling cleans up local
 | 
						|
 *	data structures and unregisters the peer node from the SCIF network
 | 
						|
 * 8) The mgmt node sends a SCIF_EXIT_ACK
 | 
						|
 * 9) Upon receipt of the SCIF_EXIT_ACK the node initiating the teardown
 | 
						|
 *	completes the SCIF remove routine
 | 
						|
 * 10) The SCIF network is now torn down for the node initiating the
 | 
						|
 *	teardown sequence
 | 
						|
 *
 | 
						|
 ************************************************************************
 | 
						|
 * SCIF node QP teardown flow (initiated by mgmt node):
 | 
						|
 *
 | 
						|
 * 1) SCIF driver gets a remove callback with a scif_hw_dev via the scif_hw_bus
 | 
						|
 * 2) The device page QP DMA address field is updated with 0x0
 | 
						|
 * 3) The mgmt node calls scif_disconnect_node(..)
 | 
						|
 * 4) scif_disconnect_node(..) sends a SCIF_NODE_REMOVE message to all the peers
 | 
						|
 *	and waits for a SCIF_NODE_REMOVE_ACK
 | 
						|
 * 5) As part of scif_node_remove(..) a remote node unregisters the peer
 | 
						|
 *	node from the SCIF network and sends a SCIF_NODE_REMOVE_ACK
 | 
						|
 * 6) When the mgmt node has received all the SCIF_NODE_REMOVE_ACKs
 | 
						|
 *	it unregisters the peer node from the SCIF network
 | 
						|
 * 7) The mgmt node sends a SCIF_EXIT message and waits for a SCIF_EXIT_ACK.
 | 
						|
 * 8) A non mgmt node upon receipt of a SCIF_EXIT message calls scif_stop(..)
 | 
						|
 *	which would clean up local data structures for all SCIF nodes and
 | 
						|
 *	then send a SCIF_EXIT_ACK back to the mgmt node
 | 
						|
 * 9) Upon receipt of the SCIF_EXIT_ACK the the mgmt node sends itself a node
 | 
						|
 *	remove message whose handling cleans up local data structures and
 | 
						|
 *	destroys any P2P mappings.
 | 
						|
 * 10) The SCIF hardware device for which a remove callback was received is now
 | 
						|
 *	disconnected from the SCIF network.
 | 
						|
 */
 | 
						|
/*
 | 
						|
 * Initializes "local" data structures for the QP. Allocates the QP
 | 
						|
 * ring buffer (rb) and initializes the "in bound" queue.
 | 
						|
 */
 | 
						|
int scif_setup_qp_connect(struct scif_qp *qp, dma_addr_t *qp_offset,
 | 
						|
			  int local_size, struct scif_dev *scifdev)
 | 
						|
{
 | 
						|
	void *local_q = qp->inbound_q.rb_base;
 | 
						|
	int err = 0;
 | 
						|
	u32 tmp_rd = 0;
 | 
						|
 | 
						|
	spin_lock_init(&qp->send_lock);
 | 
						|
	spin_lock_init(&qp->recv_lock);
 | 
						|
 | 
						|
	/* Allocate rb only if not already allocated */
 | 
						|
	if (!local_q) {
 | 
						|
		local_q = kzalloc(local_size, GFP_KERNEL);
 | 
						|
		if (!local_q) {
 | 
						|
			err = -ENOMEM;
 | 
						|
			return err;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	err = scif_map_single(&qp->local_buf, local_q, scifdev, local_size);
 | 
						|
	if (err)
 | 
						|
		goto kfree;
 | 
						|
	/*
 | 
						|
	 * To setup the inbound_q, the buffer lives locally, the read pointer
 | 
						|
	 * is remote and the write pointer is local.
 | 
						|
	 */
 | 
						|
	scif_rb_init(&qp->inbound_q,
 | 
						|
		     &tmp_rd,
 | 
						|
		     &qp->local_write,
 | 
						|
		     local_q, get_count_order(local_size));
 | 
						|
	/*
 | 
						|
	 * The read pointer is NULL initially and it is unsafe to use the ring
 | 
						|
	 * buffer til this changes!
 | 
						|
	 */
 | 
						|
	qp->inbound_q.read_ptr = NULL;
 | 
						|
	err = scif_map_single(qp_offset, qp,
 | 
						|
			      scifdev, sizeof(struct scif_qp));
 | 
						|
	if (err)
 | 
						|
		goto unmap;
 | 
						|
	qp->local_qp = *qp_offset;
 | 
						|
	return err;
 | 
						|
unmap:
 | 
						|
	scif_unmap_single(qp->local_buf, scifdev, local_size);
 | 
						|
	qp->local_buf = 0;
 | 
						|
kfree:
 | 
						|
	kfree(local_q);
 | 
						|
	return err;
 | 
						|
}
 | 
						|
 | 
						|
/* When the other side has already done it's allocation, this is called */
 | 
						|
int scif_setup_qp_accept(struct scif_qp *qp, dma_addr_t *qp_offset,
 | 
						|
			 dma_addr_t phys, int local_size,
 | 
						|
			 struct scif_dev *scifdev)
 | 
						|
{
 | 
						|
	void *local_q;
 | 
						|
	void *remote_q;
 | 
						|
	struct scif_qp *remote_qp;
 | 
						|
	int remote_size;
 | 
						|
	int err = 0;
 | 
						|
 | 
						|
	spin_lock_init(&qp->send_lock);
 | 
						|
	spin_lock_init(&qp->recv_lock);
 | 
						|
	/* Start by figuring out where we need to point */
 | 
						|
	remote_qp = scif_ioremap(phys, sizeof(struct scif_qp), scifdev);
 | 
						|
	if (!remote_qp)
 | 
						|
		return -EIO;
 | 
						|
	qp->remote_qp = remote_qp;
 | 
						|
	if (qp->remote_qp->magic != SCIFEP_MAGIC) {
 | 
						|
		err = -EIO;
 | 
						|
		goto iounmap;
 | 
						|
	}
 | 
						|
	qp->remote_buf = remote_qp->local_buf;
 | 
						|
	remote_size = qp->remote_qp->inbound_q.size;
 | 
						|
	remote_q = scif_ioremap(qp->remote_buf, remote_size, scifdev);
 | 
						|
	if (!remote_q) {
 | 
						|
		err = -EIO;
 | 
						|
		goto iounmap;
 | 
						|
	}
 | 
						|
	qp->remote_qp->local_write = 0;
 | 
						|
	/*
 | 
						|
	 * To setup the outbound_q, the buffer lives in remote memory,
 | 
						|
	 * the read pointer is local, the write pointer is remote
 | 
						|
	 */
 | 
						|
	scif_rb_init(&qp->outbound_q,
 | 
						|
		     &qp->local_read,
 | 
						|
		     &qp->remote_qp->local_write,
 | 
						|
		     remote_q,
 | 
						|
		     get_count_order(remote_size));
 | 
						|
	local_q = kzalloc(local_size, GFP_KERNEL);
 | 
						|
	if (!local_q) {
 | 
						|
		err = -ENOMEM;
 | 
						|
		goto iounmap_1;
 | 
						|
	}
 | 
						|
	err = scif_map_single(&qp->local_buf, local_q, scifdev, local_size);
 | 
						|
	if (err)
 | 
						|
		goto kfree;
 | 
						|
	qp->remote_qp->local_read = 0;
 | 
						|
	/*
 | 
						|
	 * To setup the inbound_q, the buffer lives locally, the read pointer
 | 
						|
	 * is remote and the write pointer is local
 | 
						|
	 */
 | 
						|
	scif_rb_init(&qp->inbound_q,
 | 
						|
		     &qp->remote_qp->local_read,
 | 
						|
		     &qp->local_write,
 | 
						|
		     local_q, get_count_order(local_size));
 | 
						|
	err = scif_map_single(qp_offset, qp, scifdev,
 | 
						|
			      sizeof(struct scif_qp));
 | 
						|
	if (err)
 | 
						|
		goto unmap;
 | 
						|
	qp->local_qp = *qp_offset;
 | 
						|
	return err;
 | 
						|
unmap:
 | 
						|
	scif_unmap_single(qp->local_buf, scifdev, local_size);
 | 
						|
	qp->local_buf = 0;
 | 
						|
kfree:
 | 
						|
	kfree(local_q);
 | 
						|
iounmap_1:
 | 
						|
	scif_iounmap(remote_q, remote_size, scifdev);
 | 
						|
	qp->outbound_q.rb_base = NULL;
 | 
						|
iounmap:
 | 
						|
	scif_iounmap(qp->remote_qp, sizeof(struct scif_qp), scifdev);
 | 
						|
	qp->remote_qp = NULL;
 | 
						|
	return err;
 | 
						|
}
 | 
						|
 | 
						|
int scif_setup_qp_connect_response(struct scif_dev *scifdev,
 | 
						|
				   struct scif_qp *qp, u64 payload)
 | 
						|
{
 | 
						|
	int err = 0;
 | 
						|
	void *r_buf;
 | 
						|
	int remote_size;
 | 
						|
	phys_addr_t tmp_phys;
 | 
						|
 | 
						|
	qp->remote_qp = scif_ioremap(payload, sizeof(struct scif_qp), scifdev);
 | 
						|
 | 
						|
	if (!qp->remote_qp) {
 | 
						|
		err = -ENOMEM;
 | 
						|
		goto error;
 | 
						|
	}
 | 
						|
 | 
						|
	if (qp->remote_qp->magic != SCIFEP_MAGIC) {
 | 
						|
		dev_err(&scifdev->sdev->dev,
 | 
						|
			"SCIFEP_MAGIC mismatch between self %d remote %d\n",
 | 
						|
			scif_dev[scif_info.nodeid].node, scifdev->node);
 | 
						|
		err = -ENODEV;
 | 
						|
		goto error;
 | 
						|
	}
 | 
						|
 | 
						|
	tmp_phys = qp->remote_qp->local_buf;
 | 
						|
	remote_size = qp->remote_qp->inbound_q.size;
 | 
						|
	r_buf = scif_ioremap(tmp_phys, remote_size, scifdev);
 | 
						|
 | 
						|
	if (!r_buf)
 | 
						|
		return -EIO;
 | 
						|
 | 
						|
	qp->local_read = 0;
 | 
						|
	scif_rb_init(&qp->outbound_q,
 | 
						|
		     &qp->local_read,
 | 
						|
		     &qp->remote_qp->local_write,
 | 
						|
		     r_buf,
 | 
						|
		     get_count_order(remote_size));
 | 
						|
	/*
 | 
						|
	 * Because the node QP may already be processing an INIT message, set
 | 
						|
	 * the read pointer so the cached read offset isn't lost
 | 
						|
	 */
 | 
						|
	qp->remote_qp->local_read = qp->inbound_q.current_read_offset;
 | 
						|
	/*
 | 
						|
	 * resetup the inbound_q now that we know where the
 | 
						|
	 * inbound_read really is.
 | 
						|
	 */
 | 
						|
	scif_rb_init(&qp->inbound_q,
 | 
						|
		     &qp->remote_qp->local_read,
 | 
						|
		     &qp->local_write,
 | 
						|
		     qp->inbound_q.rb_base,
 | 
						|
		     get_count_order(qp->inbound_q.size));
 | 
						|
error:
 | 
						|
	return err;
 | 
						|
}
 | 
						|
 | 
						|
static __always_inline void
 | 
						|
scif_send_msg_intr(struct scif_dev *scifdev)
 | 
						|
{
 | 
						|
	struct scif_hw_dev *sdev = scifdev->sdev;
 | 
						|
 | 
						|
	if (scifdev_is_p2p(scifdev))
 | 
						|
		sdev->hw_ops->send_p2p_intr(sdev, scifdev->rdb, &scifdev->mmio);
 | 
						|
	else
 | 
						|
		sdev->hw_ops->send_intr(sdev, scifdev->rdb);
 | 
						|
}
 | 
						|
 | 
						|
int scif_qp_response(phys_addr_t phys, struct scif_dev *scifdev)
 | 
						|
{
 | 
						|
	int err = 0;
 | 
						|
	struct scifmsg msg;
 | 
						|
 | 
						|
	err = scif_setup_qp_connect_response(scifdev, scifdev->qpairs, phys);
 | 
						|
	if (!err) {
 | 
						|
		/*
 | 
						|
		 * Now that everything is setup and mapped, we're ready
 | 
						|
		 * to tell the peer about our queue's location
 | 
						|
		 */
 | 
						|
		msg.uop = SCIF_INIT;
 | 
						|
		msg.dst.node = scifdev->node;
 | 
						|
		err = scif_nodeqp_send(scifdev, &msg);
 | 
						|
	}
 | 
						|
	return err;
 | 
						|
}
 | 
						|
 | 
						|
void scif_send_exit(struct scif_dev *scifdev)
 | 
						|
{
 | 
						|
	struct scifmsg msg;
 | 
						|
	int ret;
 | 
						|
 | 
						|
	scifdev->exit = OP_IN_PROGRESS;
 | 
						|
	msg.uop = SCIF_EXIT;
 | 
						|
	msg.src.node = scif_info.nodeid;
 | 
						|
	msg.dst.node = scifdev->node;
 | 
						|
	ret = scif_nodeqp_send(scifdev, &msg);
 | 
						|
	if (ret)
 | 
						|
		goto done;
 | 
						|
	/* Wait for a SCIF_EXIT_ACK message */
 | 
						|
	wait_event_timeout(scif_info.exitwq, scifdev->exit == OP_COMPLETED,
 | 
						|
			   SCIF_NODE_ALIVE_TIMEOUT);
 | 
						|
done:
 | 
						|
	scifdev->exit = OP_IDLE;
 | 
						|
}
 | 
						|
 | 
						|
int scif_setup_qp(struct scif_dev *scifdev)
 | 
						|
{
 | 
						|
	int err = 0;
 | 
						|
	int local_size;
 | 
						|
	struct scif_qp *qp;
 | 
						|
 | 
						|
	local_size = SCIF_NODE_QP_SIZE;
 | 
						|
 | 
						|
	qp = kzalloc(sizeof(*qp), GFP_KERNEL);
 | 
						|
	if (!qp) {
 | 
						|
		err = -ENOMEM;
 | 
						|
		return err;
 | 
						|
	}
 | 
						|
	qp->magic = SCIFEP_MAGIC;
 | 
						|
	scifdev->qpairs = qp;
 | 
						|
	err = scif_setup_qp_connect(qp, &scifdev->qp_dma_addr,
 | 
						|
				    local_size, scifdev);
 | 
						|
	if (err)
 | 
						|
		goto free_qp;
 | 
						|
	/*
 | 
						|
	 * We're as setup as we can be. The inbound_q is setup, w/o a usable
 | 
						|
	 * outbound q.  When we get a message, the read_ptr will be updated,
 | 
						|
	 * and we will pull the message.
 | 
						|
	 */
 | 
						|
	return err;
 | 
						|
free_qp:
 | 
						|
	kfree(scifdev->qpairs);
 | 
						|
	scifdev->qpairs = NULL;
 | 
						|
	return err;
 | 
						|
}
 | 
						|
 | 
						|
static void scif_p2p_freesg(struct scatterlist *sg)
 | 
						|
{
 | 
						|
	kfree(sg);
 | 
						|
}
 | 
						|
 | 
						|
static struct scatterlist *
 | 
						|
scif_p2p_setsg(phys_addr_t pa, int page_size, int page_cnt)
 | 
						|
{
 | 
						|
	struct scatterlist *sg;
 | 
						|
	struct page *page;
 | 
						|
	int i;
 | 
						|
 | 
						|
	sg = kcalloc(page_cnt, sizeof(struct scatterlist), GFP_KERNEL);
 | 
						|
	if (!sg)
 | 
						|
		return NULL;
 | 
						|
	sg_init_table(sg, page_cnt);
 | 
						|
	for (i = 0; i < page_cnt; i++) {
 | 
						|
		page = pfn_to_page(pa >> PAGE_SHIFT);
 | 
						|
		sg_set_page(&sg[i], page, page_size, 0);
 | 
						|
		pa += page_size;
 | 
						|
	}
 | 
						|
	return sg;
 | 
						|
}
 | 
						|
 | 
						|
/* Init p2p mappings required to access peerdev from scifdev */
 | 
						|
static struct scif_p2p_info *
 | 
						|
scif_init_p2p_info(struct scif_dev *scifdev, struct scif_dev *peerdev)
 | 
						|
{
 | 
						|
	struct scif_p2p_info *p2p;
 | 
						|
	int num_mmio_pages, num_aper_pages, sg_page_shift, err, num_aper_chunks;
 | 
						|
	struct scif_hw_dev *psdev = peerdev->sdev;
 | 
						|
	struct scif_hw_dev *sdev = scifdev->sdev;
 | 
						|
 | 
						|
	num_mmio_pages = psdev->mmio->len >> PAGE_SHIFT;
 | 
						|
	num_aper_pages = psdev->aper->len >> PAGE_SHIFT;
 | 
						|
 | 
						|
	p2p = kzalloc(sizeof(*p2p), GFP_KERNEL);
 | 
						|
	if (!p2p)
 | 
						|
		return NULL;
 | 
						|
	p2p->ppi_sg[SCIF_PPI_MMIO] = scif_p2p_setsg(psdev->mmio->pa,
 | 
						|
						    PAGE_SIZE, num_mmio_pages);
 | 
						|
	if (!p2p->ppi_sg[SCIF_PPI_MMIO])
 | 
						|
		goto free_p2p;
 | 
						|
	p2p->sg_nentries[SCIF_PPI_MMIO] = num_mmio_pages;
 | 
						|
	sg_page_shift = get_order(min(psdev->aper->len, (u64)(1 << 30)));
 | 
						|
	num_aper_chunks = num_aper_pages >> (sg_page_shift - PAGE_SHIFT);
 | 
						|
	p2p->ppi_sg[SCIF_PPI_APER] = scif_p2p_setsg(psdev->aper->pa,
 | 
						|
						    1 << sg_page_shift,
 | 
						|
						    num_aper_chunks);
 | 
						|
	p2p->sg_nentries[SCIF_PPI_APER] = num_aper_chunks;
 | 
						|
	err = dma_map_sg(&sdev->dev, p2p->ppi_sg[SCIF_PPI_MMIO],
 | 
						|
			 num_mmio_pages, PCI_DMA_BIDIRECTIONAL);
 | 
						|
	if (err != num_mmio_pages)
 | 
						|
		goto scif_p2p_free;
 | 
						|
	err = dma_map_sg(&sdev->dev, p2p->ppi_sg[SCIF_PPI_APER],
 | 
						|
			 num_aper_chunks, PCI_DMA_BIDIRECTIONAL);
 | 
						|
	if (err != num_aper_chunks)
 | 
						|
		goto dma_unmap;
 | 
						|
	p2p->ppi_da[SCIF_PPI_MMIO] = sg_dma_address(p2p->ppi_sg[SCIF_PPI_MMIO]);
 | 
						|
	p2p->ppi_da[SCIF_PPI_APER] = sg_dma_address(p2p->ppi_sg[SCIF_PPI_APER]);
 | 
						|
	p2p->ppi_len[SCIF_PPI_MMIO] = num_mmio_pages;
 | 
						|
	p2p->ppi_len[SCIF_PPI_APER] = num_aper_pages;
 | 
						|
	p2p->ppi_peer_id = peerdev->node;
 | 
						|
	return p2p;
 | 
						|
dma_unmap:
 | 
						|
	dma_unmap_sg(&sdev->dev, p2p->ppi_sg[SCIF_PPI_MMIO],
 | 
						|
		     p2p->sg_nentries[SCIF_PPI_MMIO], DMA_BIDIRECTIONAL);
 | 
						|
scif_p2p_free:
 | 
						|
	scif_p2p_freesg(p2p->ppi_sg[SCIF_PPI_MMIO]);
 | 
						|
	scif_p2p_freesg(p2p->ppi_sg[SCIF_PPI_APER]);
 | 
						|
free_p2p:
 | 
						|
	kfree(p2p);
 | 
						|
	return NULL;
 | 
						|
}
 | 
						|
 | 
						|
/* Uninitialize and release resources from a p2p mapping */
 | 
						|
static void scif_deinit_p2p_info(struct scif_dev *scifdev,
 | 
						|
				 struct scif_p2p_info *p2p)
 | 
						|
{
 | 
						|
	struct scif_hw_dev *sdev = scifdev->sdev;
 | 
						|
 | 
						|
	dma_unmap_sg(&sdev->dev, p2p->ppi_sg[SCIF_PPI_MMIO],
 | 
						|
		     p2p->sg_nentries[SCIF_PPI_MMIO], DMA_BIDIRECTIONAL);
 | 
						|
	dma_unmap_sg(&sdev->dev, p2p->ppi_sg[SCIF_PPI_APER],
 | 
						|
		     p2p->sg_nentries[SCIF_PPI_APER], DMA_BIDIRECTIONAL);
 | 
						|
	scif_p2p_freesg(p2p->ppi_sg[SCIF_PPI_MMIO]);
 | 
						|
	scif_p2p_freesg(p2p->ppi_sg[SCIF_PPI_APER]);
 | 
						|
	kfree(p2p);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * scif_node_connect: Respond to SCIF_NODE_CONNECT interrupt message
 | 
						|
 * @dst: Destination node
 | 
						|
 *
 | 
						|
 * Connect the src and dst node by setting up the p2p connection
 | 
						|
 * between them. Management node here acts like a proxy.
 | 
						|
 */
 | 
						|
static void scif_node_connect(struct scif_dev *scifdev, int dst)
 | 
						|
{
 | 
						|
	struct scif_dev *dev_j = scifdev;
 | 
						|
	struct scif_dev *dev_i = NULL;
 | 
						|
	struct scif_p2p_info *p2p_ij = NULL;    /* bus addr for j from i */
 | 
						|
	struct scif_p2p_info *p2p_ji = NULL;    /* bus addr for i from j */
 | 
						|
	struct scif_p2p_info *p2p;
 | 
						|
	struct list_head *pos, *tmp;
 | 
						|
	struct scifmsg msg;
 | 
						|
	int err;
 | 
						|
	u64 tmppayload;
 | 
						|
 | 
						|
	if (dst < 1 || dst > scif_info.maxid)
 | 
						|
		return;
 | 
						|
 | 
						|
	dev_i = &scif_dev[dst];
 | 
						|
 | 
						|
	if (!_scifdev_alive(dev_i))
 | 
						|
		return;
 | 
						|
	/*
 | 
						|
	 * If the p2p connection is already setup or in the process of setting
 | 
						|
	 * up then just ignore this request. The requested node will get
 | 
						|
	 * informed by SCIF_NODE_ADD_ACK or SCIF_NODE_ADD_NACK
 | 
						|
	 */
 | 
						|
	if (!list_empty(&dev_i->p2p)) {
 | 
						|
		list_for_each_safe(pos, tmp, &dev_i->p2p) {
 | 
						|
			p2p = list_entry(pos, struct scif_p2p_info, ppi_list);
 | 
						|
			if (p2p->ppi_peer_id == dev_j->node)
 | 
						|
				return;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	p2p_ij = scif_init_p2p_info(dev_i, dev_j);
 | 
						|
	if (!p2p_ij)
 | 
						|
		return;
 | 
						|
	p2p_ji = scif_init_p2p_info(dev_j, dev_i);
 | 
						|
	if (!p2p_ji) {
 | 
						|
		scif_deinit_p2p_info(dev_i, p2p_ij);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	list_add_tail(&p2p_ij->ppi_list, &dev_i->p2p);
 | 
						|
	list_add_tail(&p2p_ji->ppi_list, &dev_j->p2p);
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Send a SCIF_NODE_ADD to dev_i, pass it its bus address
 | 
						|
	 * as seen from dev_j
 | 
						|
	 */
 | 
						|
	msg.uop = SCIF_NODE_ADD;
 | 
						|
	msg.src.node = dev_j->node;
 | 
						|
	msg.dst.node = dev_i->node;
 | 
						|
 | 
						|
	msg.payload[0] = p2p_ji->ppi_da[SCIF_PPI_APER];
 | 
						|
	msg.payload[1] = p2p_ij->ppi_da[SCIF_PPI_MMIO];
 | 
						|
	msg.payload[2] = p2p_ij->ppi_da[SCIF_PPI_APER];
 | 
						|
	msg.payload[3] = p2p_ij->ppi_len[SCIF_PPI_APER] << PAGE_SHIFT;
 | 
						|
 | 
						|
	err = scif_nodeqp_send(dev_i,  &msg);
 | 
						|
	if (err) {
 | 
						|
		dev_err(&scifdev->sdev->dev,
 | 
						|
			"%s %d error %d\n", __func__, __LINE__, err);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Same as above but to dev_j */
 | 
						|
	msg.uop = SCIF_NODE_ADD;
 | 
						|
	msg.src.node = dev_i->node;
 | 
						|
	msg.dst.node = dev_j->node;
 | 
						|
 | 
						|
	tmppayload = msg.payload[0];
 | 
						|
	msg.payload[0] = msg.payload[2];
 | 
						|
	msg.payload[2] = tmppayload;
 | 
						|
	msg.payload[1] = p2p_ji->ppi_da[SCIF_PPI_MMIO];
 | 
						|
	msg.payload[3] = p2p_ji->ppi_len[SCIF_PPI_APER] << PAGE_SHIFT;
 | 
						|
 | 
						|
	scif_nodeqp_send(dev_j, &msg);
 | 
						|
}
 | 
						|
 | 
						|
static void scif_p2p_setup(void)
 | 
						|
{
 | 
						|
	int i, j;
 | 
						|
 | 
						|
	if (!scif_info.p2p_enable)
 | 
						|
		return;
 | 
						|
 | 
						|
	for (i = 1; i <= scif_info.maxid; i++)
 | 
						|
		if (!_scifdev_alive(&scif_dev[i]))
 | 
						|
			return;
 | 
						|
 | 
						|
	for (i = 1; i <= scif_info.maxid; i++) {
 | 
						|
		for (j = 1; j <= scif_info.maxid; j++) {
 | 
						|
			struct scif_dev *scifdev = &scif_dev[i];
 | 
						|
 | 
						|
			if (i == j)
 | 
						|
				continue;
 | 
						|
			scif_node_connect(scifdev, j);
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static char *message_types[] = {"BAD",
 | 
						|
				"INIT",
 | 
						|
				"EXIT",
 | 
						|
				"SCIF_EXIT_ACK",
 | 
						|
				"SCIF_NODE_ADD",
 | 
						|
				"SCIF_NODE_ADD_ACK",
 | 
						|
				"SCIF_NODE_ADD_NACK",
 | 
						|
				"REMOVE_NODE",
 | 
						|
				"REMOVE_NODE_ACK",
 | 
						|
				"CNCT_REQ",
 | 
						|
				"CNCT_GNT",
 | 
						|
				"CNCT_GNTACK",
 | 
						|
				"CNCT_GNTNACK",
 | 
						|
				"CNCT_REJ",
 | 
						|
				"DISCNCT",
 | 
						|
				"DISCNT_ACK",
 | 
						|
				"CLIENT_SENT",
 | 
						|
				"CLIENT_RCVD",
 | 
						|
				"SCIF_GET_NODE_INFO",
 | 
						|
				"REGISTER",
 | 
						|
				"REGISTER_ACK",
 | 
						|
				"REGISTER_NACK",
 | 
						|
				"UNREGISTER",
 | 
						|
				"UNREGISTER_ACK",
 | 
						|
				"UNREGISTER_NACK",
 | 
						|
				"ALLOC_REQ",
 | 
						|
				"ALLOC_GNT",
 | 
						|
				"ALLOC_REJ",
 | 
						|
				"FREE_PHYS",
 | 
						|
				"FREE_VIRT",
 | 
						|
				"MUNMAP",
 | 
						|
				"MARK",
 | 
						|
				"MARK_ACK",
 | 
						|
				"MARK_NACK",
 | 
						|
				"WAIT",
 | 
						|
				"WAIT_ACK",
 | 
						|
				"WAIT_NACK",
 | 
						|
				"SIGNAL_LOCAL",
 | 
						|
				"SIGNAL_REMOTE",
 | 
						|
				"SIG_ACK",
 | 
						|
				"SIG_NACK"};
 | 
						|
 | 
						|
static void
 | 
						|
scif_display_message(struct scif_dev *scifdev, struct scifmsg *msg,
 | 
						|
		     const char *label)
 | 
						|
{
 | 
						|
	if (!scif_info.en_msg_log)
 | 
						|
		return;
 | 
						|
	if (msg->uop > SCIF_MAX_MSG) {
 | 
						|
		dev_err(&scifdev->sdev->dev,
 | 
						|
			"%s: unknown msg type %d\n", label, msg->uop);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	dev_info(&scifdev->sdev->dev,
 | 
						|
		 "%s: msg type %s, src %d:%d, dest %d:%d payload 0x%llx:0x%llx:0x%llx:0x%llx\n",
 | 
						|
		 label, message_types[msg->uop], msg->src.node, msg->src.port,
 | 
						|
		 msg->dst.node, msg->dst.port, msg->payload[0], msg->payload[1],
 | 
						|
		 msg->payload[2], msg->payload[3]);
 | 
						|
}
 | 
						|
 | 
						|
int _scif_nodeqp_send(struct scif_dev *scifdev, struct scifmsg *msg)
 | 
						|
{
 | 
						|
	struct scif_qp *qp = scifdev->qpairs;
 | 
						|
	int err = -ENOMEM, loop_cnt = 0;
 | 
						|
 | 
						|
	scif_display_message(scifdev, msg, "Sent");
 | 
						|
	if (!qp) {
 | 
						|
		err = -EINVAL;
 | 
						|
		goto error;
 | 
						|
	}
 | 
						|
	spin_lock(&qp->send_lock);
 | 
						|
 | 
						|
	while ((err = scif_rb_write(&qp->outbound_q,
 | 
						|
				    msg, sizeof(struct scifmsg)))) {
 | 
						|
		mdelay(1);
 | 
						|
#define SCIF_NODEQP_SEND_TO_MSEC (3 * 1000)
 | 
						|
		if (loop_cnt++ > (SCIF_NODEQP_SEND_TO_MSEC)) {
 | 
						|
			err = -ENODEV;
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if (!err)
 | 
						|
		scif_rb_commit(&qp->outbound_q);
 | 
						|
	spin_unlock(&qp->send_lock);
 | 
						|
	if (!err) {
 | 
						|
		if (scifdev_self(scifdev))
 | 
						|
			/*
 | 
						|
			 * For loopback we need to emulate an interrupt by
 | 
						|
			 * queuing work for the queue handling real node
 | 
						|
			 * Qp interrupts.
 | 
						|
			 */
 | 
						|
			queue_work(scifdev->intr_wq, &scifdev->intr_bh);
 | 
						|
		else
 | 
						|
			scif_send_msg_intr(scifdev);
 | 
						|
	}
 | 
						|
error:
 | 
						|
	if (err)
 | 
						|
		dev_dbg(&scifdev->sdev->dev,
 | 
						|
			"%s %d error %d uop %d\n",
 | 
						|
			 __func__, __LINE__, err, msg->uop);
 | 
						|
	return err;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * scif_nodeqp_send - Send a message on the node queue pair
 | 
						|
 * @scifdev: Scif Device.
 | 
						|
 * @msg: The message to be sent.
 | 
						|
 */
 | 
						|
int scif_nodeqp_send(struct scif_dev *scifdev, struct scifmsg *msg)
 | 
						|
{
 | 
						|
	int err;
 | 
						|
	struct device *spdev = NULL;
 | 
						|
 | 
						|
	if (msg->uop > SCIF_EXIT_ACK) {
 | 
						|
		/* Dont send messages once the exit flow has begun */
 | 
						|
		if (OP_IDLE != scifdev->exit)
 | 
						|
			return -ENODEV;
 | 
						|
		spdev = scif_get_peer_dev(scifdev);
 | 
						|
		if (IS_ERR(spdev)) {
 | 
						|
			err = PTR_ERR(spdev);
 | 
						|
			return err;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	err = _scif_nodeqp_send(scifdev, msg);
 | 
						|
	if (msg->uop > SCIF_EXIT_ACK)
 | 
						|
		scif_put_peer_dev(spdev);
 | 
						|
	return err;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * scif_misc_handler:
 | 
						|
 *
 | 
						|
 * Work queue handler for servicing miscellaneous SCIF tasks.
 | 
						|
 * Examples include:
 | 
						|
 * 1) Remote fence requests.
 | 
						|
 * 2) Destruction of temporary registered windows
 | 
						|
 *    created during scif_vreadfrom()/scif_vwriteto().
 | 
						|
 * 3) Cleanup of zombie endpoints.
 | 
						|
 */
 | 
						|
void scif_misc_handler(struct work_struct *work)
 | 
						|
{
 | 
						|
	scif_rma_handle_remote_fences();
 | 
						|
	scif_rma_destroy_windows();
 | 
						|
	scif_rma_destroy_tcw_invalid();
 | 
						|
	scif_cleanup_zombie_epd();
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * scif_init() - Respond to SCIF_INIT interrupt message
 | 
						|
 * @scifdev:    Remote SCIF device node
 | 
						|
 * @msg:        Interrupt message
 | 
						|
 */
 | 
						|
static __always_inline void
 | 
						|
scif_init(struct scif_dev *scifdev, struct scifmsg *msg)
 | 
						|
{
 | 
						|
	/*
 | 
						|
	 * Allow the thread waiting for device page updates for the peer QP DMA
 | 
						|
	 * address to complete initializing the inbound_q.
 | 
						|
	 */
 | 
						|
	flush_delayed_work(&scifdev->qp_dwork);
 | 
						|
 | 
						|
	scif_peer_register_device(scifdev);
 | 
						|
 | 
						|
	if (scif_is_mgmt_node()) {
 | 
						|
		mutex_lock(&scif_info.conflock);
 | 
						|
		scif_p2p_setup();
 | 
						|
		mutex_unlock(&scif_info.conflock);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * scif_exit() - Respond to SCIF_EXIT interrupt message
 | 
						|
 * @scifdev:    Remote SCIF device node
 | 
						|
 * @msg:        Interrupt message
 | 
						|
 *
 | 
						|
 * This function stops the SCIF interface for the node which sent
 | 
						|
 * the SCIF_EXIT message and starts waiting for that node to
 | 
						|
 * resetup the queue pair again.
 | 
						|
 */
 | 
						|
static __always_inline void
 | 
						|
scif_exit(struct scif_dev *scifdev, struct scifmsg *unused)
 | 
						|
{
 | 
						|
	scifdev->exit_ack_pending = true;
 | 
						|
	if (scif_is_mgmt_node())
 | 
						|
		scif_disconnect_node(scifdev->node, false);
 | 
						|
	else
 | 
						|
		scif_stop(scifdev);
 | 
						|
	schedule_delayed_work(&scifdev->qp_dwork,
 | 
						|
			      msecs_to_jiffies(1000));
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * scif_exitack() - Respond to SCIF_EXIT_ACK interrupt message
 | 
						|
 * @scifdev:    Remote SCIF device node
 | 
						|
 * @msg:        Interrupt message
 | 
						|
 *
 | 
						|
 */
 | 
						|
static __always_inline void
 | 
						|
scif_exit_ack(struct scif_dev *scifdev, struct scifmsg *unused)
 | 
						|
{
 | 
						|
	scifdev->exit = OP_COMPLETED;
 | 
						|
	wake_up(&scif_info.exitwq);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * scif_node_add() - Respond to SCIF_NODE_ADD interrupt message
 | 
						|
 * @scifdev:    Remote SCIF device node
 | 
						|
 * @msg:        Interrupt message
 | 
						|
 *
 | 
						|
 * When the mgmt node driver has finished initializing a MIC node queue pair it
 | 
						|
 * marks the node as online. It then looks for all currently online MIC cards
 | 
						|
 * and send a SCIF_NODE_ADD message to identify the ID of the new card for
 | 
						|
 * peer to peer initialization
 | 
						|
 *
 | 
						|
 * The local node allocates its incoming queue and sends its address in the
 | 
						|
 * SCIF_NODE_ADD_ACK message back to the mgmt node, the mgmt node "reflects"
 | 
						|
 * this message to the new node
 | 
						|
 */
 | 
						|
static __always_inline void
 | 
						|
scif_node_add(struct scif_dev *scifdev, struct scifmsg *msg)
 | 
						|
{
 | 
						|
	struct scif_dev *newdev;
 | 
						|
	dma_addr_t qp_offset;
 | 
						|
	int qp_connect;
 | 
						|
	struct scif_hw_dev *sdev;
 | 
						|
 | 
						|
	dev_dbg(&scifdev->sdev->dev,
 | 
						|
		"Scifdev %d:%d received NODE_ADD msg for node %d\n",
 | 
						|
		scifdev->node, msg->dst.node, msg->src.node);
 | 
						|
	dev_dbg(&scifdev->sdev->dev,
 | 
						|
		"Remote address for this node's aperture %llx\n",
 | 
						|
		msg->payload[0]);
 | 
						|
	newdev = &scif_dev[msg->src.node];
 | 
						|
	newdev->node = msg->src.node;
 | 
						|
	newdev->sdev = scif_dev[SCIF_MGMT_NODE].sdev;
 | 
						|
	sdev = newdev->sdev;
 | 
						|
 | 
						|
	if (scif_setup_intr_wq(newdev)) {
 | 
						|
		dev_err(&scifdev->sdev->dev,
 | 
						|
			"failed to setup interrupts for %d\n", msg->src.node);
 | 
						|
		goto interrupt_setup_error;
 | 
						|
	}
 | 
						|
	newdev->mmio.va = ioremap_nocache(msg->payload[1], sdev->mmio->len);
 | 
						|
	if (!newdev->mmio.va) {
 | 
						|
		dev_err(&scifdev->sdev->dev,
 | 
						|
			"failed to map mmio for %d\n", msg->src.node);
 | 
						|
		goto mmio_map_error;
 | 
						|
	}
 | 
						|
	newdev->qpairs = kzalloc(sizeof(*newdev->qpairs), GFP_KERNEL);
 | 
						|
	if (!newdev->qpairs)
 | 
						|
		goto qp_alloc_error;
 | 
						|
	/*
 | 
						|
	 * Set the base address of the remote node's memory since it gets
 | 
						|
	 * added to qp_offset
 | 
						|
	 */
 | 
						|
	newdev->base_addr = msg->payload[0];
 | 
						|
 | 
						|
	qp_connect = scif_setup_qp_connect(newdev->qpairs, &qp_offset,
 | 
						|
					   SCIF_NODE_QP_SIZE, newdev);
 | 
						|
	if (qp_connect) {
 | 
						|
		dev_err(&scifdev->sdev->dev,
 | 
						|
			"failed to setup qp_connect %d\n", qp_connect);
 | 
						|
		goto qp_connect_error;
 | 
						|
	}
 | 
						|
 | 
						|
	newdev->db = sdev->hw_ops->next_db(sdev);
 | 
						|
	newdev->cookie = sdev->hw_ops->request_irq(sdev, scif_intr_handler,
 | 
						|
						   "SCIF_INTR", newdev,
 | 
						|
						   newdev->db);
 | 
						|
	if (IS_ERR(newdev->cookie))
 | 
						|
		goto qp_connect_error;
 | 
						|
	newdev->qpairs->magic = SCIFEP_MAGIC;
 | 
						|
	newdev->qpairs->qp_state = SCIF_QP_OFFLINE;
 | 
						|
 | 
						|
	msg->uop = SCIF_NODE_ADD_ACK;
 | 
						|
	msg->dst.node = msg->src.node;
 | 
						|
	msg->src.node = scif_info.nodeid;
 | 
						|
	msg->payload[0] = qp_offset;
 | 
						|
	msg->payload[2] = newdev->db;
 | 
						|
	scif_nodeqp_send(&scif_dev[SCIF_MGMT_NODE], msg);
 | 
						|
	return;
 | 
						|
qp_connect_error:
 | 
						|
	kfree(newdev->qpairs);
 | 
						|
	newdev->qpairs = NULL;
 | 
						|
qp_alloc_error:
 | 
						|
	iounmap(newdev->mmio.va);
 | 
						|
	newdev->mmio.va = NULL;
 | 
						|
mmio_map_error:
 | 
						|
interrupt_setup_error:
 | 
						|
	dev_err(&scifdev->sdev->dev,
 | 
						|
		"node add failed for node %d\n", msg->src.node);
 | 
						|
	msg->uop = SCIF_NODE_ADD_NACK;
 | 
						|
	msg->dst.node = msg->src.node;
 | 
						|
	msg->src.node = scif_info.nodeid;
 | 
						|
	scif_nodeqp_send(&scif_dev[SCIF_MGMT_NODE], msg);
 | 
						|
}
 | 
						|
 | 
						|
void scif_poll_qp_state(struct work_struct *work)
 | 
						|
{
 | 
						|
#define SCIF_NODE_QP_RETRY 100
 | 
						|
#define SCIF_NODE_QP_TIMEOUT 100
 | 
						|
	struct scif_dev *peerdev = container_of(work, struct scif_dev,
 | 
						|
							p2p_dwork.work);
 | 
						|
	struct scif_qp *qp = &peerdev->qpairs[0];
 | 
						|
 | 
						|
	if (qp->qp_state != SCIF_QP_ONLINE ||
 | 
						|
	    qp->remote_qp->qp_state != SCIF_QP_ONLINE) {
 | 
						|
		if (peerdev->p2p_retry++ == SCIF_NODE_QP_RETRY) {
 | 
						|
			dev_err(&peerdev->sdev->dev,
 | 
						|
				"Warning: QP check timeout with state %d\n",
 | 
						|
				qp->qp_state);
 | 
						|
			goto timeout;
 | 
						|
		}
 | 
						|
		schedule_delayed_work(&peerdev->p2p_dwork,
 | 
						|
				      msecs_to_jiffies(SCIF_NODE_QP_TIMEOUT));
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	return;
 | 
						|
timeout:
 | 
						|
	dev_err(&peerdev->sdev->dev,
 | 
						|
		"%s %d remote node %d offline,  state = 0x%x\n",
 | 
						|
		__func__, __LINE__, peerdev->node, qp->qp_state);
 | 
						|
	qp->remote_qp->qp_state = SCIF_QP_OFFLINE;
 | 
						|
	scif_peer_unregister_device(peerdev);
 | 
						|
	scif_cleanup_scifdev(peerdev);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * scif_node_add_ack() - Respond to SCIF_NODE_ADD_ACK interrupt message
 | 
						|
 * @scifdev:    Remote SCIF device node
 | 
						|
 * @msg:        Interrupt message
 | 
						|
 *
 | 
						|
 * After a MIC node receives the SCIF_NODE_ADD_ACK message it send this
 | 
						|
 * message to the mgmt node to confirm the sequence is finished.
 | 
						|
 *
 | 
						|
 */
 | 
						|
static __always_inline void
 | 
						|
scif_node_add_ack(struct scif_dev *scifdev, struct scifmsg *msg)
 | 
						|
{
 | 
						|
	struct scif_dev *peerdev;
 | 
						|
	struct scif_qp *qp;
 | 
						|
	struct scif_dev *dst_dev = &scif_dev[msg->dst.node];
 | 
						|
 | 
						|
	dev_dbg(&scifdev->sdev->dev,
 | 
						|
		"Scifdev %d received SCIF_NODE_ADD_ACK msg src %d dst %d\n",
 | 
						|
		scifdev->node, msg->src.node, msg->dst.node);
 | 
						|
	dev_dbg(&scifdev->sdev->dev,
 | 
						|
		"payload %llx %llx %llx %llx\n", msg->payload[0],
 | 
						|
		msg->payload[1], msg->payload[2], msg->payload[3]);
 | 
						|
	if (scif_is_mgmt_node()) {
 | 
						|
		/*
 | 
						|
		 * the lock serializes with scif_qp_response_ack. The mgmt node
 | 
						|
		 * is forwarding the NODE_ADD_ACK message from src to dst we
 | 
						|
		 * need to make sure that the dst has already received a
 | 
						|
		 * NODE_ADD for src and setup its end of the qp to dst
 | 
						|
		 */
 | 
						|
		mutex_lock(&scif_info.conflock);
 | 
						|
		msg->payload[1] = scif_info.maxid;
 | 
						|
		scif_nodeqp_send(dst_dev, msg);
 | 
						|
		mutex_unlock(&scif_info.conflock);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	peerdev = &scif_dev[msg->src.node];
 | 
						|
	peerdev->sdev = scif_dev[SCIF_MGMT_NODE].sdev;
 | 
						|
	peerdev->node = msg->src.node;
 | 
						|
 | 
						|
	qp = &peerdev->qpairs[0];
 | 
						|
 | 
						|
	if ((scif_setup_qp_connect_response(peerdev, &peerdev->qpairs[0],
 | 
						|
					    msg->payload[0])))
 | 
						|
		goto local_error;
 | 
						|
	peerdev->rdb = msg->payload[2];
 | 
						|
	qp->remote_qp->qp_state = SCIF_QP_ONLINE;
 | 
						|
 | 
						|
	scif_peer_register_device(peerdev);
 | 
						|
 | 
						|
	schedule_delayed_work(&peerdev->p2p_dwork, 0);
 | 
						|
	return;
 | 
						|
local_error:
 | 
						|
	scif_cleanup_scifdev(peerdev);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * scif_node_add_nack: Respond to SCIF_NODE_ADD_NACK interrupt message
 | 
						|
 * @msg:        Interrupt message
 | 
						|
 *
 | 
						|
 * SCIF_NODE_ADD failed, so inform the waiting wq.
 | 
						|
 */
 | 
						|
static __always_inline void
 | 
						|
scif_node_add_nack(struct scif_dev *scifdev, struct scifmsg *msg)
 | 
						|
{
 | 
						|
	if (scif_is_mgmt_node()) {
 | 
						|
		struct scif_dev *dst_dev = &scif_dev[msg->dst.node];
 | 
						|
 | 
						|
		dev_dbg(&scifdev->sdev->dev,
 | 
						|
			"SCIF_NODE_ADD_NACK received from %d\n", scifdev->node);
 | 
						|
		scif_nodeqp_send(dst_dev, msg);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * scif_node_remove: Handle SCIF_NODE_REMOVE message
 | 
						|
 * @msg: Interrupt message
 | 
						|
 *
 | 
						|
 * Handle node removal.
 | 
						|
 */
 | 
						|
static __always_inline void
 | 
						|
scif_node_remove(struct scif_dev *scifdev, struct scifmsg *msg)
 | 
						|
{
 | 
						|
	int node = msg->payload[0];
 | 
						|
	struct scif_dev *scdev = &scif_dev[node];
 | 
						|
 | 
						|
	scdev->node_remove_ack_pending = true;
 | 
						|
	scif_handle_remove_node(node);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * scif_node_remove_ack: Handle SCIF_NODE_REMOVE_ACK message
 | 
						|
 * @msg: Interrupt message
 | 
						|
 *
 | 
						|
 * The peer has acked a SCIF_NODE_REMOVE message.
 | 
						|
 */
 | 
						|
static __always_inline void
 | 
						|
scif_node_remove_ack(struct scif_dev *scifdev, struct scifmsg *msg)
 | 
						|
{
 | 
						|
	struct scif_dev *sdev = &scif_dev[msg->payload[0]];
 | 
						|
 | 
						|
	atomic_inc(&sdev->disconn_rescnt);
 | 
						|
	wake_up(&sdev->disconn_wq);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * scif_get_node_info: Respond to SCIF_GET_NODE_INFO interrupt message
 | 
						|
 * @msg:        Interrupt message
 | 
						|
 *
 | 
						|
 * Retrieve node info i.e maxid and total from the mgmt node.
 | 
						|
 */
 | 
						|
static __always_inline void
 | 
						|
scif_get_node_info_resp(struct scif_dev *scifdev, struct scifmsg *msg)
 | 
						|
{
 | 
						|
	if (scif_is_mgmt_node()) {
 | 
						|
		swap(msg->dst.node, msg->src.node);
 | 
						|
		mutex_lock(&scif_info.conflock);
 | 
						|
		msg->payload[1] = scif_info.maxid;
 | 
						|
		msg->payload[2] = scif_info.total;
 | 
						|
		mutex_unlock(&scif_info.conflock);
 | 
						|
		scif_nodeqp_send(scifdev, msg);
 | 
						|
	} else {
 | 
						|
		struct completion *node_info =
 | 
						|
			(struct completion *)msg->payload[3];
 | 
						|
 | 
						|
		mutex_lock(&scif_info.conflock);
 | 
						|
		scif_info.maxid = msg->payload[1];
 | 
						|
		scif_info.total = msg->payload[2];
 | 
						|
		complete_all(node_info);
 | 
						|
		mutex_unlock(&scif_info.conflock);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
scif_msg_unknown(struct scif_dev *scifdev, struct scifmsg *msg)
 | 
						|
{
 | 
						|
	/* Bogus Node Qp Message? */
 | 
						|
	dev_err(&scifdev->sdev->dev,
 | 
						|
		"Unknown message 0x%xn scifdev->node 0x%x\n",
 | 
						|
		msg->uop, scifdev->node);
 | 
						|
}
 | 
						|
 | 
						|
static void (*scif_intr_func[SCIF_MAX_MSG + 1])
 | 
						|
	    (struct scif_dev *, struct scifmsg *msg) = {
 | 
						|
	scif_msg_unknown,	/* Error */
 | 
						|
	scif_init,		/* SCIF_INIT */
 | 
						|
	scif_exit,		/* SCIF_EXIT */
 | 
						|
	scif_exit_ack,		/* SCIF_EXIT_ACK */
 | 
						|
	scif_node_add,		/* SCIF_NODE_ADD */
 | 
						|
	scif_node_add_ack,	/* SCIF_NODE_ADD_ACK */
 | 
						|
	scif_node_add_nack,	/* SCIF_NODE_ADD_NACK */
 | 
						|
	scif_node_remove,	/* SCIF_NODE_REMOVE */
 | 
						|
	scif_node_remove_ack,	/* SCIF_NODE_REMOVE_ACK */
 | 
						|
	scif_cnctreq,		/* SCIF_CNCT_REQ */
 | 
						|
	scif_cnctgnt,		/* SCIF_CNCT_GNT */
 | 
						|
	scif_cnctgnt_ack,	/* SCIF_CNCT_GNTACK */
 | 
						|
	scif_cnctgnt_nack,	/* SCIF_CNCT_GNTNACK */
 | 
						|
	scif_cnctrej,		/* SCIF_CNCT_REJ */
 | 
						|
	scif_discnct,		/* SCIF_DISCNCT */
 | 
						|
	scif_discnt_ack,	/* SCIF_DISCNT_ACK */
 | 
						|
	scif_clientsend,	/* SCIF_CLIENT_SENT */
 | 
						|
	scif_clientrcvd,	/* SCIF_CLIENT_RCVD */
 | 
						|
	scif_get_node_info_resp,/* SCIF_GET_NODE_INFO */
 | 
						|
	scif_recv_reg,		/* SCIF_REGISTER */
 | 
						|
	scif_recv_reg_ack,	/* SCIF_REGISTER_ACK */
 | 
						|
	scif_recv_reg_nack,	/* SCIF_REGISTER_NACK */
 | 
						|
	scif_recv_unreg,	/* SCIF_UNREGISTER */
 | 
						|
	scif_recv_unreg_ack,	/* SCIF_UNREGISTER_ACK */
 | 
						|
	scif_recv_unreg_nack,	/* SCIF_UNREGISTER_NACK */
 | 
						|
	scif_alloc_req,		/* SCIF_ALLOC_REQ */
 | 
						|
	scif_alloc_gnt_rej,	/* SCIF_ALLOC_GNT */
 | 
						|
	scif_alloc_gnt_rej,	/* SCIF_ALLOC_REJ */
 | 
						|
	scif_free_virt,		/* SCIF_FREE_VIRT */
 | 
						|
	scif_recv_munmap,	/* SCIF_MUNMAP */
 | 
						|
	scif_recv_mark,		/* SCIF_MARK */
 | 
						|
	scif_recv_mark_resp,	/* SCIF_MARK_ACK */
 | 
						|
	scif_recv_mark_resp,	/* SCIF_MARK_NACK */
 | 
						|
	scif_recv_wait,		/* SCIF_WAIT */
 | 
						|
	scif_recv_wait_resp,	/* SCIF_WAIT_ACK */
 | 
						|
	scif_recv_wait_resp,	/* SCIF_WAIT_NACK */
 | 
						|
	scif_recv_sig_local,	/* SCIF_SIG_LOCAL */
 | 
						|
	scif_recv_sig_remote,	/* SCIF_SIG_REMOTE */
 | 
						|
	scif_recv_sig_resp,	/* SCIF_SIG_ACK */
 | 
						|
	scif_recv_sig_resp,	/* SCIF_SIG_NACK */
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * scif_nodeqp_msg_handler() - Common handler for node messages
 | 
						|
 * @scifdev: Remote device to respond to
 | 
						|
 * @qp: Remote memory pointer
 | 
						|
 * @msg: The message to be handled.
 | 
						|
 *
 | 
						|
 * This routine calls the appropriate routine to handle a Node Qp
 | 
						|
 * message receipt
 | 
						|
 */
 | 
						|
static int scif_max_msg_id = SCIF_MAX_MSG;
 | 
						|
 | 
						|
static void
 | 
						|
scif_nodeqp_msg_handler(struct scif_dev *scifdev,
 | 
						|
			struct scif_qp *qp, struct scifmsg *msg)
 | 
						|
{
 | 
						|
	scif_display_message(scifdev, msg, "Rcvd");
 | 
						|
 | 
						|
	if (msg->uop > (u32)scif_max_msg_id) {
 | 
						|
		/* Bogus Node Qp Message? */
 | 
						|
		dev_err(&scifdev->sdev->dev,
 | 
						|
			"Unknown message 0x%xn scifdev->node 0x%x\n",
 | 
						|
			msg->uop, scifdev->node);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	scif_intr_func[msg->uop](scifdev, msg);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * scif_nodeqp_intrhandler() - Interrupt handler for node messages
 | 
						|
 * @scifdev:    Remote device to respond to
 | 
						|
 * @qp:         Remote memory pointer
 | 
						|
 *
 | 
						|
 * This routine is triggered by the interrupt mechanism.  It reads
 | 
						|
 * messages from the node queue RB and calls the Node QP Message handling
 | 
						|
 * routine.
 | 
						|
 */
 | 
						|
void scif_nodeqp_intrhandler(struct scif_dev *scifdev, struct scif_qp *qp)
 | 
						|
{
 | 
						|
	struct scifmsg msg;
 | 
						|
	int read_size;
 | 
						|
 | 
						|
	do {
 | 
						|
		read_size = scif_rb_get_next(&qp->inbound_q, &msg, sizeof(msg));
 | 
						|
		if (!read_size)
 | 
						|
			break;
 | 
						|
		scif_nodeqp_msg_handler(scifdev, qp, &msg);
 | 
						|
		/*
 | 
						|
		 * The node queue pair is unmapped so skip the read pointer
 | 
						|
		 * update after receipt of a SCIF_EXIT_ACK
 | 
						|
		 */
 | 
						|
		if (SCIF_EXIT_ACK == msg.uop)
 | 
						|
			break;
 | 
						|
		scif_rb_update_read_ptr(&qp->inbound_q);
 | 
						|
	} while (1);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * scif_loopb_wq_handler - Loopback Workqueue Handler.
 | 
						|
 * @work: loop back work
 | 
						|
 *
 | 
						|
 * This work queue routine is invoked by the loopback work queue handler.
 | 
						|
 * It grabs the recv lock, dequeues any available messages from the head
 | 
						|
 * of the loopback message list, calls the node QP message handler,
 | 
						|
 * waits for it to return, then frees up this message and dequeues more
 | 
						|
 * elements of the list if available.
 | 
						|
 */
 | 
						|
static void scif_loopb_wq_handler(struct work_struct *unused)
 | 
						|
{
 | 
						|
	struct scif_dev *scifdev = scif_info.loopb_dev;
 | 
						|
	struct scif_qp *qp = scifdev->qpairs;
 | 
						|
	struct scif_loopb_msg *msg;
 | 
						|
 | 
						|
	do {
 | 
						|
		msg = NULL;
 | 
						|
		spin_lock(&qp->recv_lock);
 | 
						|
		if (!list_empty(&scif_info.loopb_recv_q)) {
 | 
						|
			msg = list_first_entry(&scif_info.loopb_recv_q,
 | 
						|
					       struct scif_loopb_msg,
 | 
						|
					       list);
 | 
						|
			list_del(&msg->list);
 | 
						|
		}
 | 
						|
		spin_unlock(&qp->recv_lock);
 | 
						|
 | 
						|
		if (msg) {
 | 
						|
			scif_nodeqp_msg_handler(scifdev, qp, &msg->msg);
 | 
						|
			kfree(msg);
 | 
						|
		}
 | 
						|
	} while (msg);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * scif_loopb_msg_handler() - Workqueue handler for loopback messages.
 | 
						|
 * @scifdev: SCIF device
 | 
						|
 * @qp: Queue pair.
 | 
						|
 *
 | 
						|
 * This work queue routine is triggered when a loopback message is received.
 | 
						|
 *
 | 
						|
 * We need special handling for receiving Node Qp messages on a loopback SCIF
 | 
						|
 * device via two workqueues for receiving messages.
 | 
						|
 *
 | 
						|
 * The reason we need the extra workqueue which is not required with *normal*
 | 
						|
 * non-loopback SCIF devices is the potential classic deadlock described below:
 | 
						|
 *
 | 
						|
 * Thread A tries to send a message on a loopback SCIF device and blocks since
 | 
						|
 * there is no space in the RB while it has the send_lock held or another
 | 
						|
 * lock called lock X for example.
 | 
						|
 *
 | 
						|
 * Thread B: The Loopback Node QP message receive workqueue receives the message
 | 
						|
 * and tries to send a message (eg an ACK) to the loopback SCIF device. It tries
 | 
						|
 * to grab the send lock again or lock X and deadlocks with Thread A. The RB
 | 
						|
 * cannot be drained any further due to this classic deadlock.
 | 
						|
 *
 | 
						|
 * In order to avoid deadlocks as mentioned above we have an extra level of
 | 
						|
 * indirection achieved by having two workqueues.
 | 
						|
 * 1) The first workqueue whose handler is scif_loopb_msg_handler reads
 | 
						|
 * messages from the Node QP RB, adds them to a list and queues work for the
 | 
						|
 * second workqueue.
 | 
						|
 *
 | 
						|
 * 2) The second workqueue whose handler is scif_loopb_wq_handler dequeues
 | 
						|
 * messages from the list, handles them, frees up the memory and dequeues
 | 
						|
 * more elements from the list if possible.
 | 
						|
 */
 | 
						|
int
 | 
						|
scif_loopb_msg_handler(struct scif_dev *scifdev, struct scif_qp *qp)
 | 
						|
{
 | 
						|
	int read_size;
 | 
						|
	struct scif_loopb_msg *msg;
 | 
						|
 | 
						|
	do {
 | 
						|
		msg = kmalloc(sizeof(*msg), GFP_KERNEL);
 | 
						|
		if (!msg)
 | 
						|
			return -ENOMEM;
 | 
						|
		read_size = scif_rb_get_next(&qp->inbound_q, &msg->msg,
 | 
						|
					     sizeof(struct scifmsg));
 | 
						|
		if (read_size != sizeof(struct scifmsg)) {
 | 
						|
			kfree(msg);
 | 
						|
			scif_rb_update_read_ptr(&qp->inbound_q);
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		spin_lock(&qp->recv_lock);
 | 
						|
		list_add_tail(&msg->list, &scif_info.loopb_recv_q);
 | 
						|
		spin_unlock(&qp->recv_lock);
 | 
						|
		queue_work(scif_info.loopb_wq, &scif_info.loopb_work);
 | 
						|
		scif_rb_update_read_ptr(&qp->inbound_q);
 | 
						|
	} while (read_size == sizeof(struct scifmsg));
 | 
						|
	return read_size;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * scif_setup_loopback_qp - One time setup work for Loopback Node Qp.
 | 
						|
 * @scifdev: SCIF device
 | 
						|
 *
 | 
						|
 * Sets up the required loopback workqueues, queue pairs and ring buffers
 | 
						|
 */
 | 
						|
int scif_setup_loopback_qp(struct scif_dev *scifdev)
 | 
						|
{
 | 
						|
	int err = 0;
 | 
						|
	void *local_q;
 | 
						|
	struct scif_qp *qp;
 | 
						|
 | 
						|
	err = scif_setup_intr_wq(scifdev);
 | 
						|
	if (err)
 | 
						|
		goto exit;
 | 
						|
	INIT_LIST_HEAD(&scif_info.loopb_recv_q);
 | 
						|
	snprintf(scif_info.loopb_wqname, sizeof(scif_info.loopb_wqname),
 | 
						|
		 "SCIF LOOPB %d", scifdev->node);
 | 
						|
	scif_info.loopb_wq =
 | 
						|
		alloc_ordered_workqueue(scif_info.loopb_wqname, 0);
 | 
						|
	if (!scif_info.loopb_wq) {
 | 
						|
		err = -ENOMEM;
 | 
						|
		goto destroy_intr;
 | 
						|
	}
 | 
						|
	INIT_WORK(&scif_info.loopb_work, scif_loopb_wq_handler);
 | 
						|
	/* Allocate Self Qpair */
 | 
						|
	scifdev->qpairs = kzalloc(sizeof(*scifdev->qpairs), GFP_KERNEL);
 | 
						|
	if (!scifdev->qpairs) {
 | 
						|
		err = -ENOMEM;
 | 
						|
		goto destroy_loopb_wq;
 | 
						|
	}
 | 
						|
 | 
						|
	qp = scifdev->qpairs;
 | 
						|
	qp->magic = SCIFEP_MAGIC;
 | 
						|
	spin_lock_init(&qp->send_lock);
 | 
						|
	spin_lock_init(&qp->recv_lock);
 | 
						|
 | 
						|
	local_q = kzalloc(SCIF_NODE_QP_SIZE, GFP_KERNEL);
 | 
						|
	if (!local_q) {
 | 
						|
		err = -ENOMEM;
 | 
						|
		goto free_qpairs;
 | 
						|
	}
 | 
						|
	/*
 | 
						|
	 * For loopback the inbound_q and outbound_q are essentially the same
 | 
						|
	 * since the Node sends a message on the loopback interface to the
 | 
						|
	 * outbound_q which is then received on the inbound_q.
 | 
						|
	 */
 | 
						|
	scif_rb_init(&qp->outbound_q,
 | 
						|
		     &qp->local_read,
 | 
						|
		     &qp->local_write,
 | 
						|
		     local_q, get_count_order(SCIF_NODE_QP_SIZE));
 | 
						|
 | 
						|
	scif_rb_init(&qp->inbound_q,
 | 
						|
		     &qp->local_read,
 | 
						|
		     &qp->local_write,
 | 
						|
		     local_q, get_count_order(SCIF_NODE_QP_SIZE));
 | 
						|
	scif_info.nodeid = scifdev->node;
 | 
						|
 | 
						|
	scif_peer_register_device(scifdev);
 | 
						|
 | 
						|
	scif_info.loopb_dev = scifdev;
 | 
						|
	return err;
 | 
						|
free_qpairs:
 | 
						|
	kfree(scifdev->qpairs);
 | 
						|
destroy_loopb_wq:
 | 
						|
	destroy_workqueue(scif_info.loopb_wq);
 | 
						|
destroy_intr:
 | 
						|
	scif_destroy_intr_wq(scifdev);
 | 
						|
exit:
 | 
						|
	return err;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * scif_destroy_loopback_qp - One time uninit work for Loopback Node Qp
 | 
						|
 * @scifdev: SCIF device
 | 
						|
 *
 | 
						|
 * Destroys the workqueues and frees up the Ring Buffer and Queue Pair memory.
 | 
						|
 */
 | 
						|
int scif_destroy_loopback_qp(struct scif_dev *scifdev)
 | 
						|
{
 | 
						|
	scif_peer_unregister_device(scifdev);
 | 
						|
	destroy_workqueue(scif_info.loopb_wq);
 | 
						|
	scif_destroy_intr_wq(scifdev);
 | 
						|
	kfree(scifdev->qpairs->outbound_q.rb_base);
 | 
						|
	kfree(scifdev->qpairs);
 | 
						|
	scifdev->sdev = NULL;
 | 
						|
	scif_info.loopb_dev = NULL;
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
void scif_destroy_p2p(struct scif_dev *scifdev)
 | 
						|
{
 | 
						|
	struct scif_dev *peer_dev;
 | 
						|
	struct scif_p2p_info *p2p;
 | 
						|
	struct list_head *pos, *tmp;
 | 
						|
	int bd;
 | 
						|
 | 
						|
	mutex_lock(&scif_info.conflock);
 | 
						|
	/* Free P2P mappings in the given node for all its peer nodes */
 | 
						|
	list_for_each_safe(pos, tmp, &scifdev->p2p) {
 | 
						|
		p2p = list_entry(pos, struct scif_p2p_info, ppi_list);
 | 
						|
		dma_unmap_sg(&scifdev->sdev->dev, p2p->ppi_sg[SCIF_PPI_MMIO],
 | 
						|
			     p2p->sg_nentries[SCIF_PPI_MMIO],
 | 
						|
			     DMA_BIDIRECTIONAL);
 | 
						|
		dma_unmap_sg(&scifdev->sdev->dev, p2p->ppi_sg[SCIF_PPI_APER],
 | 
						|
			     p2p->sg_nentries[SCIF_PPI_APER],
 | 
						|
			     DMA_BIDIRECTIONAL);
 | 
						|
		scif_p2p_freesg(p2p->ppi_sg[SCIF_PPI_MMIO]);
 | 
						|
		scif_p2p_freesg(p2p->ppi_sg[SCIF_PPI_APER]);
 | 
						|
		list_del(pos);
 | 
						|
		kfree(p2p);
 | 
						|
	}
 | 
						|
 | 
						|
	/* Free P2P mapping created in the peer nodes for the given node */
 | 
						|
	for (bd = SCIF_MGMT_NODE + 1; bd <= scif_info.maxid; bd++) {
 | 
						|
		peer_dev = &scif_dev[bd];
 | 
						|
		list_for_each_safe(pos, tmp, &peer_dev->p2p) {
 | 
						|
			p2p = list_entry(pos, struct scif_p2p_info, ppi_list);
 | 
						|
			if (p2p->ppi_peer_id == scifdev->node) {
 | 
						|
				dma_unmap_sg(&peer_dev->sdev->dev,
 | 
						|
					     p2p->ppi_sg[SCIF_PPI_MMIO],
 | 
						|
					     p2p->sg_nentries[SCIF_PPI_MMIO],
 | 
						|
					     DMA_BIDIRECTIONAL);
 | 
						|
				dma_unmap_sg(&peer_dev->sdev->dev,
 | 
						|
					     p2p->ppi_sg[SCIF_PPI_APER],
 | 
						|
					     p2p->sg_nentries[SCIF_PPI_APER],
 | 
						|
					     DMA_BIDIRECTIONAL);
 | 
						|
				scif_p2p_freesg(p2p->ppi_sg[SCIF_PPI_MMIO]);
 | 
						|
				scif_p2p_freesg(p2p->ppi_sg[SCIF_PPI_APER]);
 | 
						|
				list_del(pos);
 | 
						|
				kfree(p2p);
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	mutex_unlock(&scif_info.conflock);
 | 
						|
}
 |