2432 lines · 61.0 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0+/* * IBM Power Systems Virtual Management Channel Support. * * Copyright (c) 2004, 2018 IBM Corp. *   Dave Engebretsen engebret@us.ibm.com *   Steven Royer seroyer@linux.vnet.ibm.com *   Adam Reznechek adreznec@linux.vnet.ibm.com *   Bryant G. Ly <bryantly@linux.vnet.ibm.com> */ #include <linux/module.h>#include <linux/kernel.h>#include <linux/kthread.h>#include <linux/major.h>#include <linux/string.h>#include <linux/fcntl.h>#include <linux/slab.h>#include <linux/poll.h>#include <linux/init.h>#include <linux/fs.h>#include <linux/interrupt.h>#include <linux/spinlock.h>#include <linux/percpu.h>#include <linux/delay.h>#include <linux/uaccess.h>#include <linux/io.h>#include <linux/miscdevice.h>#include <linux/sched/signal.h> #include <asm/byteorder.h>#include <asm/irq.h>#include <asm/vio.h> #include "ibmvmc.h" #define IBMVMC_DRIVER_VERSION "1.0" /* * Static global variables */static DECLARE_WAIT_QUEUE_HEAD(ibmvmc_read_wait); static const char ibmvmc_driver_name[] = "ibmvmc"; static struct ibmvmc_struct ibmvmc;static struct ibmvmc_hmc hmcs[MAX_HMCS];static struct crq_server_adapter ibmvmc_adapter; static int ibmvmc_max_buf_pool_size = DEFAULT_BUF_POOL_SIZE;static int ibmvmc_max_hmcs = DEFAULT_HMCS;static int ibmvmc_max_mtu = DEFAULT_MTU; static inline long h_copy_rdma(s64 length, u64 sliobn, u64 slioba,			       u64 dliobn, u64 dlioba){	long rc = 0; 	/* Ensure all writes to source memory are visible before hcall */	dma_wmb();	pr_debug("ibmvmc: h_copy_rdma(0x%llx, 0x%llx, 0x%llx, 0x%llx, 0x%llx\n",		 length, sliobn, slioba, dliobn, dlioba);	rc = plpar_hcall_norets(H_COPY_RDMA, length, sliobn, slioba,				dliobn, dlioba);	pr_debug("ibmvmc: h_copy_rdma rc = 0x%lx\n", rc); 	return rc;} static inline void h_free_crq(uint32_t unit_address){	long rc = 0; 	do {		if (H_IS_LONG_BUSY(rc))			msleep(get_longbusy_msecs(rc)); 		rc = plpar_hcall_norets(H_FREE_CRQ, unit_address);	} while ((rc == H_BUSY) || (H_IS_LONG_BUSY(rc)));} /** * h_request_vmc: - request a hypervisor virtual management channel device * @vmc_index: drc index of the vmc device created * * Requests the hypervisor create a new virtual management channel device, * allowing this partition to send hypervisor virtualization control * commands. * * Return: *	0 - Success *	Non-zero - Failure */static inline long h_request_vmc(u32 *vmc_index){	long rc = 0;	unsigned long retbuf[PLPAR_HCALL_BUFSIZE]; 	do {		if (H_IS_LONG_BUSY(rc))			msleep(get_longbusy_msecs(rc)); 		/* Call to request the VMC device from phyp */		rc = plpar_hcall(H_REQUEST_VMC, retbuf);		pr_debug("ibmvmc: %s rc = 0x%lx\n", __func__, rc);		*vmc_index = retbuf[0];	} while ((rc == H_BUSY) || (H_IS_LONG_BUSY(rc))); 	return rc;} /* routines for managing a command/response queue *//** * ibmvmc_handle_event: - Interrupt handler for crq events * @irq:        number of irq to handle, not used * @dev_instance: crq_server_adapter that received interrupt * * Disables interrupts and schedules ibmvmc_task * * Always returns IRQ_HANDLED */static irqreturn_t ibmvmc_handle_event(int irq, void *dev_instance){	struct crq_server_adapter *adapter =		(struct crq_server_adapter *)dev_instance; 	vio_disable_interrupts(to_vio_dev(adapter->dev));	tasklet_schedule(&adapter->work_task); 	return IRQ_HANDLED;} /** * ibmvmc_release_crq_queue - Release CRQ Queue * * @adapter:	crq_server_adapter struct * * Return: *	0 - Success *	Non-Zero - Failure */static void ibmvmc_release_crq_queue(struct crq_server_adapter *adapter){	struct vio_dev *vdev = to_vio_dev(adapter->dev);	struct crq_queue *queue = &adapter->queue; 	free_irq(vdev->irq, (void *)adapter);	tasklet_kill(&adapter->work_task); 	if (adapter->reset_task)		kthread_stop(adapter->reset_task); 	h_free_crq(vdev->unit_address);	dma_unmap_single(adapter->dev,			 queue->msg_token,			 queue->size * sizeof(*queue->msgs), DMA_BIDIRECTIONAL);	free_page((unsigned long)queue->msgs);} /** * ibmvmc_reset_crq_queue - Reset CRQ Queue * * @adapter:	crq_server_adapter struct * * This function calls h_free_crq and then calls H_REG_CRQ and does all the * bookkeeping to get us back to where we can communicate. * * Return: *	0 - Success *	Non-Zero - Failure */static int ibmvmc_reset_crq_queue(struct crq_server_adapter *adapter){	struct vio_dev *vdev = to_vio_dev(adapter->dev);	struct crq_queue *queue = &adapter->queue;	int rc = 0; 	/* Close the CRQ */	h_free_crq(vdev->unit_address); 	/* Clean out the queue */	memset(queue->msgs, 0x00, PAGE_SIZE);	queue->cur = 0; 	/* And re-open it again */	rc = plpar_hcall_norets(H_REG_CRQ,				vdev->unit_address,				queue->msg_token, PAGE_SIZE);	if (rc == 2)		/* Adapter is good, but other end is not ready */		dev_warn(adapter->dev, "Partner adapter not ready\n");	else if (rc != 0)		dev_err(adapter->dev, "couldn't register crq--rc 0x%x\n", rc); 	return rc;} /** * crq_queue_next_crq: - Returns the next entry in message queue * @queue:      crq_queue to use * * Returns pointer to next entry in queue, or NULL if there are no new * entried in the CRQ. */static struct ibmvmc_crq_msg *crq_queue_next_crq(struct crq_queue *queue){	struct ibmvmc_crq_msg *crq;	unsigned long flags; 	spin_lock_irqsave(&queue->lock, flags);	crq = &queue->msgs[queue->cur];	if (crq->valid & 0x80) {		if (++queue->cur == queue->size)			queue->cur = 0; 		/* Ensure the read of the valid bit occurs before reading any		 * other bits of the CRQ entry		 */		dma_rmb();	} else {		crq = NULL;	} 	spin_unlock_irqrestore(&queue->lock, flags); 	return crq;} /** * ibmvmc_send_crq - Send CRQ * * @adapter:	crq_server_adapter struct * @word1:	Word1 Data field * @word2:	Word2 Data field * * Return: *	0 - Success *	Non-Zero - Failure */static long ibmvmc_send_crq(struct crq_server_adapter *adapter,			    u64 word1, u64 word2){	struct vio_dev *vdev = to_vio_dev(adapter->dev);	long rc = 0; 	dev_dbg(adapter->dev, "(0x%x, 0x%016llx, 0x%016llx)\n",		vdev->unit_address, word1, word2); 	/*	 * Ensure the command buffer is flushed to memory before handing it	 * over to the other side to prevent it from fetching any stale data.	 */	dma_wmb();	rc = plpar_hcall_norets(H_SEND_CRQ, vdev->unit_address, word1, word2);	dev_dbg(adapter->dev, "rc = 0x%lx\n", rc); 	return rc;} /** * alloc_dma_buffer - Create DMA Buffer * * @vdev:	vio_dev struct * @size:	Size field * @dma_handle:	DMA address field * * Allocates memory for the command queue and maps remote memory into an * ioba. * * Returns a pointer to the buffer */static void *alloc_dma_buffer(struct vio_dev *vdev, size_t size,			      dma_addr_t *dma_handle){	/* allocate memory */	void *buffer = kzalloc(size, GFP_ATOMIC); 	if (!buffer) {		*dma_handle = 0;		return NULL;	} 	/* DMA map */	*dma_handle = dma_map_single(&vdev->dev, buffer, size,				     DMA_BIDIRECTIONAL); 	if (dma_mapping_error(&vdev->dev, *dma_handle)) {		*dma_handle = 0;		kfree_sensitive(buffer);		return NULL;	} 	return buffer;} /** * free_dma_buffer - Free DMA Buffer * * @vdev:	vio_dev struct * @size:	Size field * @vaddr:	Address field * @dma_handle:	DMA address field * * Releases memory for a command queue and unmaps mapped remote memory. */static void free_dma_buffer(struct vio_dev *vdev, size_t size, void *vaddr,			    dma_addr_t dma_handle){	/* DMA unmap */	dma_unmap_single(&vdev->dev, dma_handle, size, DMA_BIDIRECTIONAL); 	/* deallocate memory */	kfree_sensitive(vaddr);} /** * ibmvmc_get_valid_hmc_buffer - Retrieve Valid HMC Buffer * * @hmc_index:	HMC Index Field * * Return: *	Pointer to ibmvmc_buffer */static struct ibmvmc_buffer *ibmvmc_get_valid_hmc_buffer(u8 hmc_index){	struct ibmvmc_buffer *buffer;	struct ibmvmc_buffer *ret_buf = NULL;	unsigned long i; 	if (hmc_index > ibmvmc.max_hmc_index)		return NULL; 	buffer = hmcs[hmc_index].buffer; 	for (i = 0; i < ibmvmc_max_buf_pool_size; i++) {		if (buffer[i].valid && buffer[i].free &&		    buffer[i].owner == VMC_BUF_OWNER_ALPHA) {			buffer[i].free = 0;			ret_buf = &buffer[i];			break;		}	} 	return ret_buf;} /** * ibmvmc_get_free_hmc_buffer - Get Free HMC Buffer * * @adapter:	crq_server_adapter struct * @hmc_index:	Hmc Index field * * Return: *	Pointer to ibmvmc_buffer */static struct ibmvmc_buffer *ibmvmc_get_free_hmc_buffer(struct crq_server_adapter *adapter,							u8 hmc_index){	struct ibmvmc_buffer *buffer;	struct ibmvmc_buffer *ret_buf = NULL;	unsigned long i; 	if (hmc_index > ibmvmc.max_hmc_index) {		dev_info(adapter->dev, "get_free_hmc_buffer: invalid hmc_index=0x%x\n",			 hmc_index);		return NULL;	} 	buffer = hmcs[hmc_index].buffer; 	for (i = 0; i < ibmvmc_max_buf_pool_size; i++) {		if (buffer[i].free &&		    buffer[i].owner == VMC_BUF_OWNER_ALPHA) {			buffer[i].free = 0;			ret_buf = &buffer[i];			break;		}	} 	return ret_buf;} /** * ibmvmc_free_hmc_buffer - Free an HMC Buffer * * @hmc:	ibmvmc_hmc struct * @buffer:	ibmvmc_buffer struct * */static void ibmvmc_free_hmc_buffer(struct ibmvmc_hmc *hmc,				   struct ibmvmc_buffer *buffer){	unsigned long flags; 	spin_lock_irqsave(&hmc->lock, flags);	buffer->free = 1;	spin_unlock_irqrestore(&hmc->lock, flags);} /** * ibmvmc_count_hmc_buffers - Count HMC Buffers * * @hmc_index:	HMC Index field * @valid:	Valid number of buffers field * @free:	Free number of buffers field * */static void ibmvmc_count_hmc_buffers(u8 hmc_index, unsigned int *valid,				     unsigned int *free){	struct ibmvmc_buffer *buffer;	unsigned long i;	unsigned long flags; 	if (hmc_index > ibmvmc.max_hmc_index)		return; 	if (!valid || !free)		return; 	*valid = 0; *free = 0; 	buffer = hmcs[hmc_index].buffer;	spin_lock_irqsave(&hmcs[hmc_index].lock, flags); 	for (i = 0; i < ibmvmc_max_buf_pool_size; i++) {		if (buffer[i].valid) {			*valid = *valid + 1;			if (buffer[i].free)				*free = *free + 1;		}	} 	spin_unlock_irqrestore(&hmcs[hmc_index].lock, flags);} /** * ibmvmc_get_free_hmc - Get Free HMC * * Return: *	Pointer to an available HMC Connection *	Null otherwise */static struct ibmvmc_hmc *ibmvmc_get_free_hmc(void){	unsigned long i;	unsigned long flags; 	/*	 * Find an available HMC connection.	 */	for (i = 0; i <= ibmvmc.max_hmc_index; i++) {		spin_lock_irqsave(&hmcs[i].lock, flags);		if (hmcs[i].state == ibmhmc_state_free) {			hmcs[i].index = i;			hmcs[i].state = ibmhmc_state_initial;			spin_unlock_irqrestore(&hmcs[i].lock, flags);			return &hmcs[i];		}		spin_unlock_irqrestore(&hmcs[i].lock, flags);	} 	return NULL;} /** * ibmvmc_return_hmc - Return an HMC Connection * * @hmc:		ibmvmc_hmc struct * @release_readers:	Number of readers connected to session * * This function releases the HMC connections back into the pool. * * Return: *	0 - Success *	Non-zero - Failure */static int ibmvmc_return_hmc(struct ibmvmc_hmc *hmc, bool release_readers){	struct ibmvmc_buffer *buffer;	struct crq_server_adapter *adapter;	struct vio_dev *vdev;	unsigned long i;	unsigned long flags; 	if (!hmc || !hmc->adapter)		return -EIO; 	if (release_readers) {		if (hmc->file_session) {			struct ibmvmc_file_session *session = hmc->file_session; 			session->valid = 0;			wake_up_interruptible(&ibmvmc_read_wait);		}	} 	adapter = hmc->adapter;	vdev = to_vio_dev(adapter->dev); 

Lines 1–500 of 2432.