|  | // SPDX-License-Identifier: GPL-2.0+ | 
|  | /* | 
|  | * IBM Power Systems Virtual Management Channel Support. | 
|  | * | 
|  | * Copyright (c) 2004, 2018 IBM Corp. | 
|  | *   Dave Engebretsen [email protected] | 
|  | *   Steven Royer [email protected] | 
|  | *   Adam Reznechek [email protected] | 
|  | *   Bryant G. Ly <[email protected]> | 
|  | */ | 
|  |  | 
|  | #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; | 
|  | kzfree(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 */ | 
|  | kzfree(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); | 
|  |  | 
|  | spin_lock_irqsave(&hmc->lock, flags); | 
|  | hmc->index = 0; | 
|  | hmc->state = ibmhmc_state_free; | 
|  | hmc->queue_head = 0; | 
|  | hmc->queue_tail = 0; | 
|  | buffer = hmc->buffer; | 
|  | for (i = 0; i < ibmvmc_max_buf_pool_size; i++) { | 
|  | if (buffer[i].valid) { | 
|  | free_dma_buffer(vdev, | 
|  | ibmvmc.max_mtu, | 
|  | buffer[i].real_addr_local, | 
|  | buffer[i].dma_addr_local); | 
|  | dev_dbg(adapter->dev, "Forgot buffer id 0x%lx\n", i); | 
|  | } | 
|  | memset(&buffer[i], 0, sizeof(struct ibmvmc_buffer)); | 
|  |  | 
|  | hmc->queue_outbound_msgs[i] = VMC_INVALID_BUFFER_ID; | 
|  | } | 
|  |  | 
|  | spin_unlock_irqrestore(&hmc->lock, flags); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * ibmvmc_send_open - Interface Open | 
|  | * @buffer: Pointer to ibmvmc_buffer struct | 
|  | * @hmc: Pointer to ibmvmc_hmc struct | 
|  | * | 
|  | * This command is sent by the management partition as the result of a | 
|  | * management partition device request. It causes the hypervisor to | 
|  | * prepare a set of data buffers for the management application connection | 
|  | * indicated HMC idx. A unique HMC Idx would be used if multiple management | 
|  | * applications running concurrently were desired. Before responding to this | 
|  | * command, the hypervisor must provide the management partition with at | 
|  | * least one of these new buffers via the Add Buffer. This indicates whether | 
|  | * the messages are inbound or outbound from the hypervisor. | 
|  | * | 
|  | * Return: | 
|  | *	0 - Success | 
|  | *	Non-zero - Failure | 
|  | */ | 
|  | static int ibmvmc_send_open(struct ibmvmc_buffer *buffer, | 
|  | struct ibmvmc_hmc *hmc) | 
|  | { | 
|  | struct ibmvmc_crq_msg crq_msg; | 
|  | struct crq_server_adapter *adapter; | 
|  | __be64 *crq_as_u64 = (__be64 *)&crq_msg; | 
|  | int rc = 0; | 
|  |  | 
|  | if (!hmc || !hmc->adapter) | 
|  | return -EIO; | 
|  |  | 
|  | adapter = hmc->adapter; | 
|  |  | 
|  | dev_dbg(adapter->dev, "send_open: 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx\n", | 
|  | (unsigned long)buffer->size, (unsigned long)adapter->liobn, | 
|  | (unsigned long)buffer->dma_addr_local, | 
|  | (unsigned long)adapter->riobn, | 
|  | (unsigned long)buffer->dma_addr_remote); | 
|  |  | 
|  | rc = h_copy_rdma(buffer->size, | 
|  | adapter->liobn, | 
|  | buffer->dma_addr_local, | 
|  | adapter->riobn, | 
|  | buffer->dma_addr_remote); | 
|  | if (rc) { | 
|  | dev_err(adapter->dev, "Error: In send_open, h_copy_rdma rc 0x%x\n", | 
|  | rc); | 
|  | return -EIO; | 
|  | } | 
|  |  | 
|  | hmc->state = ibmhmc_state_opening; | 
|  |  | 
|  | crq_msg.valid = 0x80; | 
|  | crq_msg.type = VMC_MSG_OPEN; | 
|  | crq_msg.status = 0; | 
|  | crq_msg.var1.rsvd = 0; | 
|  | crq_msg.hmc_session = hmc->session; | 
|  | crq_msg.hmc_index = hmc->index; | 
|  | crq_msg.var2.buffer_id = cpu_to_be16(buffer->id); | 
|  | crq_msg.rsvd = 0; | 
|  | crq_msg.var3.rsvd = 0; | 
|  |  | 
|  | ibmvmc_send_crq(adapter, be64_to_cpu(crq_as_u64[0]), | 
|  | be64_to_cpu(crq_as_u64[1])); | 
|  |  | 
|  | return rc; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * ibmvmc_send_close - Interface Close | 
|  | * @hmc: Pointer to ibmvmc_hmc struct | 
|  | * | 
|  | * This command is sent by the management partition to terminate a | 
|  | * management application to hypervisor connection. When this command is | 
|  | * sent, the management partition has quiesced all I/O operations to all | 
|  | * buffers associated with this management application connection, and | 
|  | * has freed any storage for these buffers. | 
|  | * | 
|  | * Return: | 
|  | *	0 - Success | 
|  | *	Non-zero - Failure | 
|  | */ | 
|  | static int ibmvmc_send_close(struct ibmvmc_hmc *hmc) | 
|  | { | 
|  | struct ibmvmc_crq_msg crq_msg; | 
|  | struct crq_server_adapter *adapter; | 
|  | __be64 *crq_as_u64 = (__be64 *)&crq_msg; | 
|  | int rc = 0; | 
|  |  | 
|  | if (!hmc || !hmc->adapter) | 
|  | return -EIO; | 
|  |  | 
|  | adapter = hmc->adapter; | 
|  |  | 
|  | dev_info(adapter->dev, "CRQ send: close\n"); | 
|  |  | 
|  | crq_msg.valid = 0x80; | 
|  | crq_msg.type = VMC_MSG_CLOSE; | 
|  | crq_msg.status = 0; | 
|  | crq_msg.var1.rsvd = 0; | 
|  | crq_msg.hmc_session = hmc->session; | 
|  | crq_msg.hmc_index = hmc->index; | 
|  | crq_msg.var2.rsvd = 0; | 
|  | crq_msg.rsvd = 0; | 
|  | crq_msg.var3.rsvd = 0; | 
|  |  | 
|  | ibmvmc_send_crq(adapter, be64_to_cpu(crq_as_u64[0]), | 
|  | be64_to_cpu(crq_as_u64[1])); | 
|  |  | 
|  | return rc; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * ibmvmc_send_capabilities - Send VMC Capabilities | 
|  | * | 
|  | * @adapter:	crq_server_adapter struct | 
|  | * | 
|  | * The capabilities message is an administrative message sent after the CRQ | 
|  | * initialization sequence of messages and is used to exchange VMC capabilities | 
|  | * between the management partition and the hypervisor. The management | 
|  | * partition must send this message and the hypervisor must respond with VMC | 
|  | * capabilities Response message before HMC interface message can begin. Any | 
|  | * HMC interface messages received before the exchange of capabilities has | 
|  | * complete are dropped. | 
|  | * | 
|  | * Return: | 
|  | *	0 - Success | 
|  | */ | 
|  | static int ibmvmc_send_capabilities(struct crq_server_adapter *adapter) | 
|  | { | 
|  | struct ibmvmc_admin_crq_msg crq_msg; | 
|  | __be64 *crq_as_u64 = (__be64 *)&crq_msg; | 
|  |  | 
|  | dev_dbg(adapter->dev, "ibmvmc: CRQ send: capabilities\n"); | 
|  | crq_msg.valid = 0x80; | 
|  | crq_msg.type = VMC_MSG_CAP; | 
|  | crq_msg.status = 0; | 
|  | crq_msg.rsvd[0] = 0; | 
|  | crq_msg.rsvd[1] = 0; | 
|  | crq_msg.max_hmc = ibmvmc_max_hmcs; | 
|  | crq_msg.max_mtu = cpu_to_be32(ibmvmc_max_mtu); | 
|  | crq_msg.pool_size = cpu_to_be16(ibmvmc_max_buf_pool_size); | 
|  | crq_msg.crq_size = cpu_to_be16(adapter->queue.size); | 
|  | crq_msg.version = cpu_to_be16(IBMVMC_PROTOCOL_VERSION); | 
|  |  | 
|  | ibmvmc_send_crq(adapter, be64_to_cpu(crq_as_u64[0]), | 
|  | be64_to_cpu(crq_as_u64[1])); | 
|  |  | 
|  | ibmvmc.state = ibmvmc_state_capabilities; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * ibmvmc_send_add_buffer_resp - Add Buffer Response | 
|  | * | 
|  | * @adapter:	crq_server_adapter struct | 
|  | * @status:	Status field | 
|  | * @hmc_session: HMC Session field | 
|  | * @hmc_index:	HMC Index field | 
|  | * @buffer_id:	Buffer Id field | 
|  | * | 
|  | * This command is sent by the management partition to the hypervisor in | 
|  | * response to the Add Buffer message. The Status field indicates the result of | 
|  | * the command. | 
|  | * | 
|  | * Return: | 
|  | *	0 - Success | 
|  | */ | 
|  | static int ibmvmc_send_add_buffer_resp(struct crq_server_adapter *adapter, | 
|  | u8 status, u8 hmc_session, | 
|  | u8 hmc_index, u16 buffer_id) | 
|  | { | 
|  | struct ibmvmc_crq_msg crq_msg; | 
|  | __be64 *crq_as_u64 = (__be64 *)&crq_msg; | 
|  |  | 
|  | dev_dbg(adapter->dev, "CRQ send: add_buffer_resp\n"); | 
|  | crq_msg.valid = 0x80; | 
|  | crq_msg.type = VMC_MSG_ADD_BUF_RESP; | 
|  | crq_msg.status = status; | 
|  | crq_msg.var1.rsvd = 0; | 
|  | crq_msg.hmc_session = hmc_session; | 
|  | crq_msg.hmc_index = hmc_index; | 
|  | crq_msg.var2.buffer_id = cpu_to_be16(buffer_id); | 
|  | crq_msg.rsvd = 0; | 
|  | crq_msg.var3.rsvd = 0; | 
|  |  | 
|  | ibmvmc_send_crq(adapter, be64_to_cpu(crq_as_u64[0]), | 
|  | be64_to_cpu(crq_as_u64[1])); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * ibmvmc_send_rem_buffer_resp - Remove Buffer Response | 
|  | * | 
|  | * @adapter:	crq_server_adapter struct | 
|  | * @status:	Status field | 
|  | * @hmc_session: HMC Session field | 
|  | * @hmc_index:	HMC Index field | 
|  | * @buffer_id:	Buffer Id field | 
|  | * | 
|  | * This command is sent by the management partition to the hypervisor in | 
|  | * response to the Remove Buffer message. The Buffer ID field indicates | 
|  | * which buffer the management partition selected to remove. The Status | 
|  | * field indicates the result of the command. | 
|  | * | 
|  | * Return: | 
|  | *	0 - Success | 
|  | */ | 
|  | static int ibmvmc_send_rem_buffer_resp(struct crq_server_adapter *adapter, | 
|  | u8 status, u8 hmc_session, | 
|  | u8 hmc_index, u16 buffer_id) | 
|  | { | 
|  | struct ibmvmc_crq_msg crq_msg; | 
|  | __be64 *crq_as_u64 = (__be64 *)&crq_msg; | 
|  |  | 
|  | dev_dbg(adapter->dev, "CRQ send: rem_buffer_resp\n"); | 
|  | crq_msg.valid = 0x80; | 
|  | crq_msg.type = VMC_MSG_REM_BUF_RESP; | 
|  | crq_msg.status = status; | 
|  | crq_msg.var1.rsvd = 0; | 
|  | crq_msg.hmc_session = hmc_session; | 
|  | crq_msg.hmc_index = hmc_index; | 
|  | crq_msg.var2.buffer_id = cpu_to_be16(buffer_id); | 
|  | crq_msg.rsvd = 0; | 
|  | crq_msg.var3.rsvd = 0; | 
|  |  | 
|  | ibmvmc_send_crq(adapter, be64_to_cpu(crq_as_u64[0]), | 
|  | be64_to_cpu(crq_as_u64[1])); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * ibmvmc_send_msg - Signal Message | 
|  | * | 
|  | * @adapter:	crq_server_adapter struct | 
|  | * @buffer:	ibmvmc_buffer struct | 
|  | * @hmc:	ibmvmc_hmc struct | 
|  | * @msg_length:	message length field | 
|  | * | 
|  | * This command is sent between the management partition and the hypervisor | 
|  | * in order to signal the arrival of an HMC protocol message. The command | 
|  | * can be sent by both the management partition and the hypervisor. It is | 
|  | * used for all traffic between the management application and the hypervisor, | 
|  | * regardless of who initiated the communication. | 
|  | * | 
|  | * There is no response to this message. | 
|  | * | 
|  | * Return: | 
|  | *	0 - Success | 
|  | *	Non-zero - Failure | 
|  | */ | 
|  | static int ibmvmc_send_msg(struct crq_server_adapter *adapter, | 
|  | struct ibmvmc_buffer *buffer, | 
|  | struct ibmvmc_hmc *hmc, int msg_len) | 
|  | { | 
|  | struct ibmvmc_crq_msg crq_msg; | 
|  | __be64 *crq_as_u64 = (__be64 *)&crq_msg; | 
|  | int rc = 0; | 
|  |  | 
|  | dev_dbg(adapter->dev, "CRQ send: rdma to HV\n"); | 
|  | rc = h_copy_rdma(msg_len, | 
|  | adapter->liobn, | 
|  | buffer->dma_addr_local, | 
|  | adapter->riobn, | 
|  | buffer->dma_addr_remote); | 
|  | if (rc) { | 
|  | dev_err(adapter->dev, "Error in send_msg, h_copy_rdma rc 0x%x\n", | 
|  | rc); | 
|  | return rc; | 
|  | } | 
|  |  | 
|  | crq_msg.valid = 0x80; | 
|  | crq_msg.type = VMC_MSG_SIGNAL; | 
|  | crq_msg.status = 0; | 
|  | crq_msg.var1.rsvd = 0; | 
|  | crq_msg.hmc_session = hmc->session; | 
|  | crq_msg.hmc_index = hmc->index; | 
|  | crq_msg.var2.buffer_id = cpu_to_be16(buffer->id); | 
|  | crq_msg.var3.msg_len = cpu_to_be32(msg_len); | 
|  | dev_dbg(adapter->dev, "CRQ send: msg to HV 0x%llx 0x%llx\n", | 
|  | be64_to_cpu(crq_as_u64[0]), be64_to_cpu(crq_as_u64[1])); | 
|  |  | 
|  | buffer->owner = VMC_BUF_OWNER_HV; | 
|  | ibmvmc_send_crq(adapter, be64_to_cpu(crq_as_u64[0]), | 
|  | be64_to_cpu(crq_as_u64[1])); | 
|  |  | 
|  | return rc; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * ibmvmc_open - Open Session | 
|  | * | 
|  | * @inode:	inode struct | 
|  | * @file:	file struct | 
|  | * | 
|  | * Return: | 
|  | *	0 - Success | 
|  | *	Non-zero - Failure | 
|  | */ | 
|  | static int ibmvmc_open(struct inode *inode, struct file *file) | 
|  | { | 
|  | struct ibmvmc_file_session *session; | 
|  |  | 
|  | pr_debug("%s: inode = 0x%lx, file = 0x%lx, state = 0x%x\n", __func__, | 
|  | (unsigned long)inode, (unsigned long)file, | 
|  | ibmvmc.state); | 
|  |  | 
|  | session = kzalloc(sizeof(*session), GFP_KERNEL); | 
|  | if (!session) | 
|  | return -ENOMEM; | 
|  |  | 
|  | session->file = file; | 
|  | file->private_data = session; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * ibmvmc_close - Close Session | 
|  | * | 
|  | * @inode:	inode struct | 
|  | * @file:	file struct | 
|  | * | 
|  | * Return: | 
|  | *	0 - Success | 
|  | *	Non-zero - Failure | 
|  | */ | 
|  | static int ibmvmc_close(struct inode *inode, struct file *file) | 
|  | { | 
|  | struct ibmvmc_file_session *session; | 
|  | struct ibmvmc_hmc *hmc; | 
|  | int rc = 0; | 
|  | unsigned long flags; | 
|  |  | 
|  | pr_debug("%s: file = 0x%lx, state = 0x%x\n", __func__, | 
|  | (unsigned long)file, ibmvmc.state); | 
|  |  | 
|  | session = file->private_data; | 
|  | if (!session) | 
|  | return -EIO; | 
|  |  | 
|  | hmc = session->hmc; | 
|  | if (hmc) { | 
|  | if (!hmc->adapter) | 
|  | return -EIO; | 
|  |  | 
|  | if (ibmvmc.state == ibmvmc_state_failed) { | 
|  | dev_warn(hmc->adapter->dev, "close: state_failed\n"); | 
|  | return -EIO; | 
|  | } | 
|  |  | 
|  | spin_lock_irqsave(&hmc->lock, flags); | 
|  | if (hmc->state >= ibmhmc_state_opening) { | 
|  | rc = ibmvmc_send_close(hmc); | 
|  | if (rc) | 
|  | dev_warn(hmc->adapter->dev, "close: send_close failed.\n"); | 
|  | } | 
|  | spin_unlock_irqrestore(&hmc->lock, flags); | 
|  | } | 
|  |  | 
|  | kzfree(session); | 
|  |  | 
|  | return rc; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * ibmvmc_read - Read | 
|  | * | 
|  | * @file:	file struct | 
|  | * @buf:	Character buffer | 
|  | * @nbytes:	Size in bytes | 
|  | * @ppos:	Offset | 
|  | * | 
|  | * Return: | 
|  | *	0 - Success | 
|  | *	Non-zero - Failure | 
|  | */ | 
|  | static ssize_t ibmvmc_read(struct file *file, char *buf, size_t nbytes, | 
|  | loff_t *ppos) | 
|  | { | 
|  | struct ibmvmc_file_session *session; | 
|  | struct ibmvmc_hmc *hmc; | 
|  | struct crq_server_adapter *adapter; | 
|  | struct ibmvmc_buffer *buffer; | 
|  | ssize_t n; | 
|  | ssize_t retval = 0; | 
|  | unsigned long flags; | 
|  | DEFINE_WAIT(wait); | 
|  |  | 
|  | pr_debug("ibmvmc: read: file = 0x%lx, buf = 0x%lx, nbytes = 0x%lx\n", | 
|  | (unsigned long)file, (unsigned long)buf, | 
|  | (unsigned long)nbytes); | 
|  |  | 
|  | if (nbytes == 0) | 
|  | return 0; | 
|  |  | 
|  | if (nbytes > ibmvmc.max_mtu) { | 
|  | pr_warn("ibmvmc: read: nbytes invalid 0x%x\n", | 
|  | (unsigned int)nbytes); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | session = file->private_data; | 
|  | if (!session) { | 
|  | pr_warn("ibmvmc: read: no session\n"); | 
|  | return -EIO; | 
|  | } | 
|  |  | 
|  | hmc = session->hmc; | 
|  | if (!hmc) { | 
|  | pr_warn("ibmvmc: read: no hmc\n"); | 
|  | return -EIO; | 
|  | } | 
|  |  | 
|  | adapter = hmc->adapter; | 
|  | if (!adapter) { | 
|  | pr_warn("ibmvmc: read: no adapter\n"); | 
|  | return -EIO; | 
|  | } | 
|  |  | 
|  | do { | 
|  | prepare_to_wait(&ibmvmc_read_wait, &wait, TASK_INTERRUPTIBLE); | 
|  |  | 
|  | spin_lock_irqsave(&hmc->lock, flags); | 
|  | if (hmc->queue_tail != hmc->queue_head) | 
|  | /* Data is available */ | 
|  | break; | 
|  |  | 
|  | spin_unlock_irqrestore(&hmc->lock, flags); | 
|  |  | 
|  | if (!session->valid) { | 
|  | retval = -EBADFD; | 
|  | goto out; | 
|  | } | 
|  | if (file->f_flags & O_NONBLOCK) { | 
|  | retval = -EAGAIN; | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | schedule(); | 
|  |  | 
|  | if (signal_pending(current)) { | 
|  | retval = -ERESTARTSYS; | 
|  | goto out; | 
|  | } | 
|  | } while (1); | 
|  |  | 
|  | buffer = &(hmc->buffer[hmc->queue_outbound_msgs[hmc->queue_tail]]); | 
|  | hmc->queue_tail++; | 
|  | if (hmc->queue_tail == ibmvmc_max_buf_pool_size) | 
|  | hmc->queue_tail = 0; | 
|  | spin_unlock_irqrestore(&hmc->lock, flags); | 
|  |  | 
|  | nbytes = min_t(size_t, nbytes, buffer->msg_len); | 
|  | n = copy_to_user((void *)buf, buffer->real_addr_local, nbytes); | 
|  | dev_dbg(adapter->dev, "read: copy to user nbytes = 0x%lx.\n", nbytes); | 
|  | ibmvmc_free_hmc_buffer(hmc, buffer); | 
|  | retval = nbytes; | 
|  |  | 
|  | if (n) { | 
|  | dev_warn(adapter->dev, "read: copy to user failed.\n"); | 
|  | retval = -EFAULT; | 
|  | } | 
|  |  | 
|  | out: | 
|  | finish_wait(&ibmvmc_read_wait, &wait); | 
|  | dev_dbg(adapter->dev, "read: out %ld\n", retval); | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * ibmvmc_poll - Poll | 
|  | * | 
|  | * @file:	file struct | 
|  | * @wait:	Poll Table | 
|  | * | 
|  | * Return: | 
|  | *	poll.h return values | 
|  | */ | 
|  | static unsigned int ibmvmc_poll(struct file *file, poll_table *wait) | 
|  | { | 
|  | struct ibmvmc_file_session *session; | 
|  | struct ibmvmc_hmc *hmc; | 
|  | unsigned int mask = 0; | 
|  |  | 
|  | session = file->private_data; | 
|  | if (!session) | 
|  | return 0; | 
|  |  | 
|  | hmc = session->hmc; | 
|  | if (!hmc) | 
|  | return 0; | 
|  |  | 
|  | poll_wait(file, &ibmvmc_read_wait, wait); | 
|  |  | 
|  | if (hmc->queue_head != hmc->queue_tail) | 
|  | mask |= POLLIN | POLLRDNORM; | 
|  |  | 
|  | return mask; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * ibmvmc_write - Write | 
|  | * | 
|  | * @file:	file struct | 
|  | * @buf:	Character buffer | 
|  | * @count:	Count field | 
|  | * @ppos:	Offset | 
|  | * | 
|  | * Return: | 
|  | *	0 - Success | 
|  | *	Non-zero - Failure | 
|  | */ | 
|  | static ssize_t ibmvmc_write(struct file *file, const char *buffer, | 
|  | size_t count, loff_t *ppos) | 
|  | { | 
|  | struct ibmvmc_buffer *vmc_buffer; | 
|  | struct ibmvmc_file_session *session; | 
|  | struct crq_server_adapter *adapter; | 
|  | struct ibmvmc_hmc *hmc; | 
|  | unsigned char *buf; | 
|  | unsigned long flags; | 
|  | size_t bytes; | 
|  | const char *p = buffer; | 
|  | size_t c = count; | 
|  | int ret = 0; | 
|  |  | 
|  | session = file->private_data; | 
|  | if (!session) | 
|  | return -EIO; | 
|  |  | 
|  | hmc = session->hmc; | 
|  | if (!hmc) | 
|  | return -EIO; | 
|  |  | 
|  | spin_lock_irqsave(&hmc->lock, flags); | 
|  | if (hmc->state == ibmhmc_state_free) { | 
|  | /* HMC connection is not valid (possibly was reset under us). */ | 
|  | ret = -EIO; | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | adapter = hmc->adapter; | 
|  | if (!adapter) { | 
|  | ret = -EIO; | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | if (count > ibmvmc.max_mtu) { | 
|  | dev_warn(adapter->dev, "invalid buffer size 0x%lx\n", | 
|  | (unsigned long)count); | 
|  | ret = -EIO; | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | /* Waiting for the open resp message to the ioctl(1) - retry */ | 
|  | if (hmc->state == ibmhmc_state_opening) { | 
|  | ret = -EBUSY; | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | /* Make sure the ioctl() was called & the open msg sent, and that | 
|  | * the HMC connection has not failed. | 
|  | */ | 
|  | if (hmc->state != ibmhmc_state_ready) { | 
|  | ret = -EIO; | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | vmc_buffer = ibmvmc_get_valid_hmc_buffer(hmc->index); | 
|  | if (!vmc_buffer) { | 
|  | /* No buffer available for the msg send, or we have not yet | 
|  | * completed the open/open_resp sequence.  Retry until this is | 
|  | * complete. | 
|  | */ | 
|  | ret = -EBUSY; | 
|  | goto out; | 
|  | } | 
|  | if (!vmc_buffer->real_addr_local) { | 
|  | dev_err(adapter->dev, "no buffer storage assigned\n"); | 
|  | ret = -EIO; | 
|  | goto out; | 
|  | } | 
|  | buf = vmc_buffer->real_addr_local; | 
|  |  | 
|  | while (c > 0) { | 
|  | bytes = min_t(size_t, c, vmc_buffer->size); | 
|  |  | 
|  | bytes -= copy_from_user(buf, p, bytes); | 
|  | if (!bytes) { | 
|  | ret = -EFAULT; | 
|  | goto out; | 
|  | } | 
|  | c -= bytes; | 
|  | p += bytes; | 
|  | } | 
|  | if (p == buffer) | 
|  | goto out; | 
|  |  | 
|  | file->f_path.dentry->d_inode->i_mtime = current_time(file_inode(file)); | 
|  | mark_inode_dirty(file->f_path.dentry->d_inode); | 
|  |  | 
|  | dev_dbg(adapter->dev, "write: file = 0x%lx, count = 0x%lx\n", | 
|  | (unsigned long)file, (unsigned long)count); | 
|  |  | 
|  | ibmvmc_send_msg(adapter, vmc_buffer, hmc, count); | 
|  | ret = p - buffer; | 
|  | out: | 
|  | spin_unlock_irqrestore(&hmc->lock, flags); | 
|  | return (ssize_t)(ret); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * ibmvmc_setup_hmc - Setup the HMC | 
|  | * | 
|  | * @session:	ibmvmc_file_session struct | 
|  | * | 
|  | * Return: | 
|  | *	0 - Success | 
|  | *	Non-zero - Failure | 
|  | */ | 
|  | static long ibmvmc_setup_hmc(struct ibmvmc_file_session *session) | 
|  | { | 
|  | struct ibmvmc_hmc *hmc; | 
|  | unsigned int valid, free, index; | 
|  |  | 
|  | if (ibmvmc.state == ibmvmc_state_failed) { | 
|  | pr_warn("ibmvmc: Reserve HMC: state_failed\n"); | 
|  | return -EIO; | 
|  | } | 
|  |  | 
|  | if (ibmvmc.state < ibmvmc_state_ready) { | 
|  | pr_warn("ibmvmc: Reserve HMC: not state_ready\n"); | 
|  | return -EAGAIN; | 
|  | } | 
|  |  | 
|  | /* Device is busy until capabilities have been exchanged and we | 
|  | * have a generic buffer for each possible HMC connection. | 
|  | */ | 
|  | for (index = 0; index <= ibmvmc.max_hmc_index; index++) { | 
|  | valid = 0; | 
|  | ibmvmc_count_hmc_buffers(index, &valid, &free); | 
|  | if (valid == 0) { | 
|  | pr_warn("ibmvmc: buffers not ready for index %d\n", | 
|  | index); | 
|  | return -ENOBUFS; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Get an hmc object, and transition to ibmhmc_state_initial */ | 
|  | hmc = ibmvmc_get_free_hmc(); | 
|  | if (!hmc) { | 
|  | pr_warn("%s: free hmc not found\n", __func__); | 
|  | return -EBUSY; | 
|  | } | 
|  |  | 
|  | hmc->session = hmc->session + 1; | 
|  | if (hmc->session == 0xff) | 
|  | hmc->session = 1; | 
|  |  | 
|  | session->hmc = hmc; | 
|  | hmc->adapter = &ibmvmc_adapter; | 
|  | hmc->file_session = session; | 
|  | session->valid = 1; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * ibmvmc_ioctl_sethmcid - IOCTL Set HMC ID | 
|  | * | 
|  | * @session:	ibmvmc_file_session struct | 
|  | * @new_hmc_id:	HMC id field | 
|  | * | 
|  | * IOCTL command to setup the hmc id | 
|  | * | 
|  | * Return: | 
|  | *	0 - Success | 
|  | *	Non-zero - Failure | 
|  | */ | 
|  | static long ibmvmc_ioctl_sethmcid(struct ibmvmc_file_session *session, | 
|  | unsigned char __user *new_hmc_id) | 
|  | { | 
|  | struct ibmvmc_hmc *hmc; | 
|  | struct ibmvmc_buffer *buffer; | 
|  | size_t bytes; | 
|  | char print_buffer[HMC_ID_LEN + 1]; | 
|  | unsigned long flags; | 
|  | long rc = 0; | 
|  |  | 
|  | /* Reserve HMC session */ | 
|  | hmc = session->hmc; | 
|  | if (!hmc) { | 
|  | rc = ibmvmc_setup_hmc(session); | 
|  | if (rc) | 
|  | return rc; | 
|  |  | 
|  | hmc = session->hmc; | 
|  | if (!hmc) { | 
|  | pr_err("ibmvmc: setup_hmc success but no hmc\n"); | 
|  | return -EIO; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (hmc->state != ibmhmc_state_initial) { | 
|  | pr_warn("ibmvmc: sethmcid: invalid state to send open 0x%x\n", | 
|  | hmc->state); | 
|  | return -EIO; | 
|  | } | 
|  |  | 
|  | bytes = copy_from_user(hmc->hmc_id, new_hmc_id, HMC_ID_LEN); | 
|  | if (bytes) | 
|  | return -EFAULT; | 
|  |  | 
|  | /* Send Open Session command */ | 
|  | spin_lock_irqsave(&hmc->lock, flags); | 
|  | buffer = ibmvmc_get_valid_hmc_buffer(hmc->index); | 
|  | spin_unlock_irqrestore(&hmc->lock, flags); | 
|  |  | 
|  | if (!buffer || !buffer->real_addr_local) { | 
|  | pr_warn("ibmvmc: sethmcid: no buffer available\n"); | 
|  | return -EIO; | 
|  | } | 
|  |  | 
|  | /* Make sure buffer is NULL terminated before trying to print it */ | 
|  | memset(print_buffer, 0, HMC_ID_LEN + 1); | 
|  | strncpy(print_buffer, hmc->hmc_id, HMC_ID_LEN); | 
|  | pr_info("ibmvmc: sethmcid: Set HMC ID: \"%s\"\n", print_buffer); | 
|  |  | 
|  | memcpy(buffer->real_addr_local, hmc->hmc_id, HMC_ID_LEN); | 
|  | /* RDMA over ID, send open msg, change state to ibmhmc_state_opening */ | 
|  | rc = ibmvmc_send_open(buffer, hmc); | 
|  |  | 
|  | return rc; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * ibmvmc_ioctl_query - IOCTL Query | 
|  | * | 
|  | * @session:	ibmvmc_file_session struct | 
|  | * @ret_struct:	ibmvmc_query_struct | 
|  | * | 
|  | * Return: | 
|  | *	0 - Success | 
|  | *	Non-zero - Failure | 
|  | */ | 
|  | static long ibmvmc_ioctl_query(struct ibmvmc_file_session *session, | 
|  | struct ibmvmc_query_struct __user *ret_struct) | 
|  | { | 
|  | struct ibmvmc_query_struct query_struct; | 
|  | size_t bytes; | 
|  |  | 
|  | memset(&query_struct, 0, sizeof(query_struct)); | 
|  | query_struct.have_vmc = (ibmvmc.state > ibmvmc_state_initial); | 
|  | query_struct.state = ibmvmc.state; | 
|  | query_struct.vmc_drc_index = ibmvmc.vmc_drc_index; | 
|  |  | 
|  | bytes = copy_to_user(ret_struct, &query_struct, | 
|  | sizeof(query_struct)); | 
|  | if (bytes) | 
|  | return -EFAULT; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * ibmvmc_ioctl_requestvmc - IOCTL Request VMC | 
|  | * | 
|  | * @session:	ibmvmc_file_session struct | 
|  | * @ret_vmc_index:	VMC Index | 
|  | * | 
|  | * Return: | 
|  | *	0 - Success | 
|  | *	Non-zero - Failure | 
|  | */ | 
|  | static long ibmvmc_ioctl_requestvmc(struct ibmvmc_file_session *session, | 
|  | u32 __user *ret_vmc_index) | 
|  | { | 
|  | /* TODO: (adreznec) Add locking to control multiple process access */ | 
|  | size_t bytes; | 
|  | long rc; | 
|  | u32 vmc_drc_index; | 
|  |  | 
|  | /* Call to request the VMC device from phyp*/ | 
|  | rc = h_request_vmc(&vmc_drc_index); | 
|  | pr_debug("ibmvmc: requestvmc: H_REQUEST_VMC rc = 0x%lx\n", rc); | 
|  |  | 
|  | if (rc == H_SUCCESS) { | 
|  | rc = 0; | 
|  | } else if (rc == H_FUNCTION) { | 
|  | pr_err("ibmvmc: requestvmc: h_request_vmc not supported\n"); | 
|  | return -EPERM; | 
|  | } else if (rc == H_AUTHORITY) { | 
|  | pr_err("ibmvmc: requestvmc: hypervisor denied vmc request\n"); | 
|  | return -EPERM; | 
|  | } else if (rc == H_HARDWARE) { | 
|  | pr_err("ibmvmc: requestvmc: hypervisor hardware fault\n"); | 
|  | return -EIO; | 
|  | } else if (rc == H_RESOURCE) { | 
|  | pr_err("ibmvmc: requestvmc: vmc resource unavailable\n"); | 
|  | return -ENODEV; | 
|  | } else if (rc == H_NOT_AVAILABLE) { | 
|  | pr_err("ibmvmc: requestvmc: system cannot be vmc managed\n"); | 
|  | return -EPERM; | 
|  | } else if (rc == H_PARAMETER) { | 
|  | pr_err("ibmvmc: requestvmc: invalid parameter\n"); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | /* Success, set the vmc index in global struct */ | 
|  | ibmvmc.vmc_drc_index = vmc_drc_index; | 
|  |  | 
|  | bytes = copy_to_user(ret_vmc_index, &vmc_drc_index, | 
|  | sizeof(*ret_vmc_index)); | 
|  | if (bytes) { | 
|  | pr_warn("ibmvmc: requestvmc: copy to user failed.\n"); | 
|  | return -EFAULT; | 
|  | } | 
|  | return rc; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * ibmvmc_ioctl - IOCTL | 
|  | * | 
|  | * @session:	ibmvmc_file_session struct | 
|  | * @cmd:	cmd field | 
|  | * @arg:	Argument field | 
|  | * | 
|  | * Return: | 
|  | *	0 - Success | 
|  | *	Non-zero - Failure | 
|  | */ | 
|  | static long ibmvmc_ioctl(struct file *file, | 
|  | unsigned int cmd, unsigned long arg) | 
|  | { | 
|  | struct ibmvmc_file_session *session = file->private_data; | 
|  |  | 
|  | pr_debug("ibmvmc: ioctl file=0x%lx, cmd=0x%x, arg=0x%lx, ses=0x%lx\n", | 
|  | (unsigned long)file, cmd, arg, | 
|  | (unsigned long)session); | 
|  |  | 
|  | if (!session) { | 
|  | pr_warn("ibmvmc: ioctl: no session\n"); | 
|  | return -EIO; | 
|  | } | 
|  |  | 
|  | switch (cmd) { | 
|  | case VMC_IOCTL_SETHMCID: | 
|  | return ibmvmc_ioctl_sethmcid(session, | 
|  | (unsigned char __user *)arg); | 
|  | case VMC_IOCTL_QUERY: | 
|  | return ibmvmc_ioctl_query(session, | 
|  | (struct ibmvmc_query_struct __user *)arg); | 
|  | case VMC_IOCTL_REQUESTVMC: | 
|  | return ibmvmc_ioctl_requestvmc(session, | 
|  | (unsigned int __user *)arg); | 
|  | default: | 
|  | pr_warn("ibmvmc: unknown ioctl 0x%x\n", cmd); | 
|  | return -EINVAL; | 
|  | } | 
|  | } | 
|  |  | 
|  | static const struct file_operations ibmvmc_fops = { | 
|  | .owner		= THIS_MODULE, | 
|  | .read		= ibmvmc_read, | 
|  | .write		= ibmvmc_write, | 
|  | .poll		= ibmvmc_poll, | 
|  | .unlocked_ioctl	= ibmvmc_ioctl, | 
|  | .open           = ibmvmc_open, | 
|  | .release        = ibmvmc_close, | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * ibmvmc_add_buffer - Add Buffer | 
|  | * | 
|  | * @adapter: crq_server_adapter struct | 
|  | * @crq:	ibmvmc_crq_msg struct | 
|  | * | 
|  | * This message transfers a buffer from hypervisor ownership to management | 
|  | * partition ownership. The LIOBA is obtained from the virtual TCE table | 
|  | * associated with the hypervisor side of the VMC device, and points to a | 
|  | * buffer of size MTU (as established in the capabilities exchange). | 
|  | * | 
|  | * Typical flow for ading buffers: | 
|  | * 1. A new management application connection is opened by the management | 
|  | *	partition. | 
|  | * 2. The hypervisor assigns new buffers for the traffic associated with | 
|  | *	that connection. | 
|  | * 3. The hypervisor sends VMC Add Buffer messages to the management | 
|  | *	partition, informing it of the new buffers. | 
|  | * 4. The hypervisor sends an HMC protocol message (to the management | 
|  | *	application) notifying it of the new buffers. This informs the | 
|  | *	application that it has buffers available for sending HMC | 
|  | *	commands. | 
|  | * | 
|  | * Return: | 
|  | *	0 - Success | 
|  | *	Non-zero - Failure | 
|  | */ | 
|  | static int ibmvmc_add_buffer(struct crq_server_adapter *adapter, | 
|  | struct ibmvmc_crq_msg *crq) | 
|  | { | 
|  | struct ibmvmc_buffer *buffer; | 
|  | u8 hmc_index; | 
|  | u8 hmc_session; | 
|  | u16 buffer_id; | 
|  | unsigned long flags; | 
|  | int rc = 0; | 
|  |  | 
|  | if (!crq) | 
|  | return -1; | 
|  |  | 
|  | hmc_session = crq->hmc_session; | 
|  | hmc_index = crq->hmc_index; | 
|  | buffer_id = be16_to_cpu(crq->var2.buffer_id); | 
|  |  | 
|  | if (hmc_index > ibmvmc.max_hmc_index) { | 
|  | dev_err(adapter->dev, "add_buffer: invalid hmc_index = 0x%x\n", | 
|  | hmc_index); | 
|  | ibmvmc_send_add_buffer_resp(adapter, VMC_MSG_INVALID_HMC_INDEX, | 
|  | hmc_session, hmc_index, buffer_id); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | if (buffer_id >= ibmvmc.max_buffer_pool_size) { | 
|  | dev_err(adapter->dev, "add_buffer: invalid buffer_id = 0x%x\n", | 
|  | buffer_id); | 
|  | ibmvmc_send_add_buffer_resp(adapter, VMC_MSG_INVALID_BUFFER_ID, | 
|  | hmc_session, hmc_index, buffer_id); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | spin_lock_irqsave(&hmcs[hmc_index].lock, flags); | 
|  | buffer = &hmcs[hmc_index].buffer[buffer_id]; | 
|  |  | 
|  | if (buffer->real_addr_local || buffer->dma_addr_local) { | 
|  | dev_warn(adapter->dev, "add_buffer: already allocated id = 0x%lx\n", | 
|  | (unsigned long)buffer_id); | 
|  | spin_unlock_irqrestore(&hmcs[hmc_index].lock, flags); | 
|  | ibmvmc_send_add_buffer_resp(adapter, VMC_MSG_INVALID_BUFFER_ID, | 
|  | hmc_session, hmc_index, buffer_id); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | buffer->real_addr_local = alloc_dma_buffer(to_vio_dev(adapter->dev), | 
|  | ibmvmc.max_mtu, | 
|  | &buffer->dma_addr_local); | 
|  |  | 
|  | if (!buffer->real_addr_local) { | 
|  | dev_err(adapter->dev, "add_buffer: alloc_dma_buffer failed.\n"); | 
|  | spin_unlock_irqrestore(&hmcs[hmc_index].lock, flags); | 
|  | ibmvmc_send_add_buffer_resp(adapter, VMC_MSG_INTERFACE_FAILURE, | 
|  | hmc_session, hmc_index, buffer_id); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | buffer->dma_addr_remote = be32_to_cpu(crq->var3.lioba); | 
|  | buffer->size = ibmvmc.max_mtu; | 
|  | buffer->owner = crq->var1.owner; | 
|  | buffer->free = 1; | 
|  | /* Must ensure valid==1 is observable only after all other fields are */ | 
|  | dma_wmb(); | 
|  | buffer->valid = 1; | 
|  | buffer->id = buffer_id; | 
|  |  | 
|  | dev_dbg(adapter->dev, "add_buffer: successfully added a buffer:\n"); | 
|  | dev_dbg(adapter->dev, "   index: %d, session: %d, buffer: 0x%x, owner: %d\n", | 
|  | hmc_index, hmc_session, buffer_id, buffer->owner); | 
|  | dev_dbg(adapter->dev, "   local: 0x%x, remote: 0x%x\n", | 
|  | (u32)buffer->dma_addr_local, | 
|  | (u32)buffer->dma_addr_remote); | 
|  | spin_unlock_irqrestore(&hmcs[hmc_index].lock, flags); | 
|  |  | 
|  | ibmvmc_send_add_buffer_resp(adapter, VMC_MSG_SUCCESS, hmc_session, | 
|  | hmc_index, buffer_id); | 
|  |  | 
|  | return rc; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * ibmvmc_rem_buffer - Remove Buffer | 
|  | * | 
|  | * @adapter: crq_server_adapter struct | 
|  | * @crq:	ibmvmc_crq_msg struct | 
|  | * | 
|  | * This message requests an HMC buffer to be transferred from management | 
|  | * partition ownership to hypervisor ownership. The management partition may | 
|  | * not be able to satisfy the request at a particular point in time if all its | 
|  | * buffers are in use. The management partition requires a depth of at least | 
|  | * one inbound buffer to allow management application commands to flow to the | 
|  | * hypervisor. It is, therefore, an interface error for the hypervisor to | 
|  | * attempt to remove the management partition's last buffer. | 
|  | * | 
|  | * The hypervisor is expected to manage buffer usage with the management | 
|  | * application directly and inform the management partition when buffers may be | 
|  | * removed. The typical flow for removing buffers: | 
|  | * | 
|  | * 1. The management application no longer needs a communication path to a | 
|  | *	particular hypervisor function. That function is closed. | 
|  | * 2. The hypervisor and the management application quiesce all traffic to that | 
|  | *	function. The hypervisor requests a reduction in buffer pool size. | 
|  | * 3. The management application acknowledges the reduction in buffer pool size. | 
|  | * 4. The hypervisor sends a Remove Buffer message to the management partition, | 
|  | *	informing it of the reduction in buffers. | 
|  | * 5. The management partition verifies it can remove the buffer. This is | 
|  | *	possible if buffers have been quiesced. | 
|  | * | 
|  | * Return: | 
|  | *	0 - Success | 
|  | *	Non-zero - Failure | 
|  | */ | 
|  | /* | 
|  | * The hypervisor requested that we pick an unused buffer, and return it. | 
|  | * Before sending the buffer back, we free any storage associated with the | 
|  | * buffer. | 
|  | */ | 
|  | static int ibmvmc_rem_buffer(struct crq_server_adapter *adapter, | 
|  | struct ibmvmc_crq_msg *crq) | 
|  | { | 
|  | struct ibmvmc_buffer *buffer; | 
|  | u8 hmc_index; | 
|  | u8 hmc_session; | 
|  | u16 buffer_id = 0; | 
|  | unsigned long flags; | 
|  | int rc = 0; | 
|  |  | 
|  | if (!crq) | 
|  | return -1; | 
|  |  | 
|  | hmc_session = crq->hmc_session; | 
|  | hmc_index = crq->hmc_index; | 
|  |  | 
|  | if (hmc_index > ibmvmc.max_hmc_index) { | 
|  | dev_warn(adapter->dev, "rem_buffer: invalid hmc_index = 0x%x\n", | 
|  | hmc_index); | 
|  | ibmvmc_send_rem_buffer_resp(adapter, VMC_MSG_INVALID_HMC_INDEX, | 
|  | hmc_session, hmc_index, buffer_id); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | spin_lock_irqsave(&hmcs[hmc_index].lock, flags); | 
|  | buffer = ibmvmc_get_free_hmc_buffer(adapter, hmc_index); | 
|  | if (!buffer) { | 
|  | dev_info(adapter->dev, "rem_buffer: no buffer to remove\n"); | 
|  | spin_unlock_irqrestore(&hmcs[hmc_index].lock, flags); | 
|  | ibmvmc_send_rem_buffer_resp(adapter, VMC_MSG_NO_BUFFER, | 
|  | hmc_session, hmc_index, | 
|  | VMC_INVALID_BUFFER_ID); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | buffer_id = buffer->id; | 
|  |  | 
|  | if (buffer->valid) | 
|  | free_dma_buffer(to_vio_dev(adapter->dev), | 
|  | ibmvmc.max_mtu, | 
|  | buffer->real_addr_local, | 
|  | buffer->dma_addr_local); | 
|  |  | 
|  | memset(buffer, 0, sizeof(struct ibmvmc_buffer)); | 
|  | spin_unlock_irqrestore(&hmcs[hmc_index].lock, flags); | 
|  |  | 
|  | dev_dbg(adapter->dev, "rem_buffer: removed buffer 0x%x.\n", buffer_id); | 
|  | ibmvmc_send_rem_buffer_resp(adapter, VMC_MSG_SUCCESS, hmc_session, | 
|  | hmc_index, buffer_id); | 
|  |  | 
|  | return rc; | 
|  | } | 
|  |  | 
|  | static int ibmvmc_recv_msg(struct crq_server_adapter *adapter, | 
|  | struct ibmvmc_crq_msg *crq) | 
|  | { | 
|  | struct ibmvmc_buffer *buffer; | 
|  | struct ibmvmc_hmc *hmc; | 
|  | unsigned long msg_len; | 
|  | u8 hmc_index; | 
|  | u8 hmc_session; | 
|  | u16 buffer_id; | 
|  | unsigned long flags; | 
|  | int rc = 0; | 
|  |  | 
|  | if (!crq) | 
|  | return -1; | 
|  |  | 
|  | /* Hypervisor writes CRQs directly into our memory in big endian */ | 
|  | dev_dbg(adapter->dev, "Recv_msg: msg from HV 0x%016llx 0x%016llx\n", | 
|  | be64_to_cpu(*((unsigned long *)crq)), | 
|  | be64_to_cpu(*(((unsigned long *)crq) + 1))); | 
|  |  | 
|  | hmc_session = crq->hmc_session; | 
|  | hmc_index = crq->hmc_index; | 
|  | buffer_id = be16_to_cpu(crq->var2.buffer_id); | 
|  | msg_len = be32_to_cpu(crq->var3.msg_len); | 
|  |  | 
|  | if (hmc_index > ibmvmc.max_hmc_index) { | 
|  | dev_err(adapter->dev, "Recv_msg: invalid hmc_index = 0x%x\n", | 
|  | hmc_index); | 
|  | ibmvmc_send_add_buffer_resp(adapter, VMC_MSG_INVALID_HMC_INDEX, | 
|  | hmc_session, hmc_index, buffer_id); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | if (buffer_id >= ibmvmc.max_buffer_pool_size) { | 
|  | dev_err(adapter->dev, "Recv_msg: invalid buffer_id = 0x%x\n", | 
|  | buffer_id); | 
|  | ibmvmc_send_add_buffer_resp(adapter, VMC_MSG_INVALID_BUFFER_ID, | 
|  | hmc_session, hmc_index, buffer_id); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | hmc = &hmcs[hmc_index]; | 
|  | spin_lock_irqsave(&hmc->lock, flags); | 
|  |  | 
|  | if (hmc->state == ibmhmc_state_free) { | 
|  | dev_err(adapter->dev, "Recv_msg: invalid hmc state = 0x%x\n", | 
|  | hmc->state); | 
|  | /* HMC connection is not valid (possibly was reset under us). */ | 
|  | spin_unlock_irqrestore(&hmc->lock, flags); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | buffer = &hmc->buffer[buffer_id]; | 
|  |  | 
|  | if (buffer->valid == 0 || buffer->owner == VMC_BUF_OWNER_ALPHA) { | 
|  | dev_err(adapter->dev, "Recv_msg: not valid, or not HV.  0x%x 0x%x\n", | 
|  | buffer->valid, buffer->owner); | 
|  | spin_unlock_irqrestore(&hmc->lock, flags); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | /* RDMA the data into the partition. */ | 
|  | rc = h_copy_rdma(msg_len, | 
|  | adapter->riobn, | 
|  | buffer->dma_addr_remote, | 
|  | adapter->liobn, | 
|  | buffer->dma_addr_local); | 
|  |  | 
|  | dev_dbg(adapter->dev, "Recv_msg: msg_len = 0x%x, buffer_id = 0x%x, queue_head = 0x%x, hmc_idx = 0x%x\n", | 
|  | (unsigned int)msg_len, (unsigned int)buffer_id, | 
|  | (unsigned int)hmc->queue_head, (unsigned int)hmc_index); | 
|  | buffer->msg_len = msg_len; | 
|  | buffer->free = 0; | 
|  | buffer->owner = VMC_BUF_OWNER_ALPHA; | 
|  |  | 
|  | if (rc) { | 
|  | dev_err(adapter->dev, "Failure in recv_msg: h_copy_rdma = 0x%x\n", | 
|  | rc); | 
|  | spin_unlock_irqrestore(&hmc->lock, flags); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | /* Must be locked because read operates on the same data */ | 
|  | hmc->queue_outbound_msgs[hmc->queue_head] = buffer_id; | 
|  | hmc->queue_head++; | 
|  | if (hmc->queue_head == ibmvmc_max_buf_pool_size) | 
|  | hmc->queue_head = 0; | 
|  |  | 
|  | if (hmc->queue_head == hmc->queue_tail) | 
|  | dev_err(adapter->dev, "outbound buffer queue wrapped.\n"); | 
|  |  | 
|  | spin_unlock_irqrestore(&hmc->lock, flags); | 
|  |  | 
|  | wake_up_interruptible(&ibmvmc_read_wait); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * ibmvmc_process_capabilities - Process Capabilities | 
|  | * | 
|  | * @adapter:	crq_server_adapter struct | 
|  | * @crqp:	ibmvmc_crq_msg struct | 
|  | * | 
|  | */ | 
|  | static void ibmvmc_process_capabilities(struct crq_server_adapter *adapter, | 
|  | struct ibmvmc_crq_msg *crqp) | 
|  | { | 
|  | struct ibmvmc_admin_crq_msg *crq = (struct ibmvmc_admin_crq_msg *)crqp; | 
|  |  | 
|  | if ((be16_to_cpu(crq->version) >> 8) != | 
|  | (IBMVMC_PROTOCOL_VERSION >> 8)) { | 
|  | dev_err(adapter->dev, "init failed, incompatible versions 0x%x 0x%x\n", | 
|  | be16_to_cpu(crq->version), | 
|  | IBMVMC_PROTOCOL_VERSION); | 
|  | ibmvmc.state = ibmvmc_state_failed; | 
|  | return; | 
|  | } | 
|  |  | 
|  | ibmvmc.max_mtu = min_t(u32, ibmvmc_max_mtu, be32_to_cpu(crq->max_mtu)); | 
|  | ibmvmc.max_buffer_pool_size = min_t(u16, ibmvmc_max_buf_pool_size, | 
|  | be16_to_cpu(crq->pool_size)); | 
|  | ibmvmc.max_hmc_index = min_t(u8, ibmvmc_max_hmcs, crq->max_hmc) - 1; | 
|  | ibmvmc.state = ibmvmc_state_ready; | 
|  |  | 
|  | dev_info(adapter->dev, "Capabilities: mtu=0x%x, pool_size=0x%x, max_hmc=0x%x\n", | 
|  | ibmvmc.max_mtu, ibmvmc.max_buffer_pool_size, | 
|  | ibmvmc.max_hmc_index); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * ibmvmc_validate_hmc_session - Validate HMC Session | 
|  | * | 
|  | * @adapter:	crq_server_adapter struct | 
|  | * @crq:	ibmvmc_crq_msg struct | 
|  | * | 
|  | * Return: | 
|  | *	0 - Success | 
|  | *	Non-zero - Failure | 
|  | */ | 
|  | static int ibmvmc_validate_hmc_session(struct crq_server_adapter *adapter, | 
|  | struct ibmvmc_crq_msg *crq) | 
|  | { | 
|  | unsigned char hmc_index; | 
|  |  | 
|  | hmc_index = crq->hmc_index; | 
|  |  | 
|  | if (crq->hmc_session == 0) | 
|  | return 0; | 
|  |  | 
|  | if (hmc_index > ibmvmc.max_hmc_index) | 
|  | return -1; | 
|  |  | 
|  | if (hmcs[hmc_index].session != crq->hmc_session) { | 
|  | dev_warn(adapter->dev, "Drop, bad session: expected 0x%x, recv 0x%x\n", | 
|  | hmcs[hmc_index].session, crq->hmc_session); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * ibmvmc_reset - Reset | 
|  | * | 
|  | * @adapter:	crq_server_adapter struct | 
|  | * @xport_event:	export_event field | 
|  | * | 
|  | * Closes all HMC sessions and conditionally schedules a CRQ reset. | 
|  | * @xport_event: If true, the partner closed their CRQ; we don't need to reset. | 
|  | *               If false, we need to schedule a CRQ reset. | 
|  | */ | 
|  | static void ibmvmc_reset(struct crq_server_adapter *adapter, bool xport_event) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | if (ibmvmc.state != ibmvmc_state_sched_reset) { | 
|  | dev_info(adapter->dev, "*** Reset to initial state.\n"); | 
|  | for (i = 0; i < ibmvmc_max_hmcs; i++) | 
|  | ibmvmc_return_hmc(&hmcs[i], xport_event); | 
|  |  | 
|  | if (xport_event) { | 
|  | /* CRQ was closed by the partner.  We don't need to do | 
|  | * anything except set ourself to the correct state to | 
|  | * handle init msgs. | 
|  | */ | 
|  | ibmvmc.state = ibmvmc_state_crqinit; | 
|  | } else { | 
|  | /* The partner did not close their CRQ - instead, we're | 
|  | * closing the CRQ on our end. Need to schedule this | 
|  | * for process context, because CRQ reset may require a | 
|  | * sleep. | 
|  | * | 
|  | * Setting ibmvmc.state here immediately prevents | 
|  | * ibmvmc_open from completing until the reset | 
|  | * completes in process context. | 
|  | */ | 
|  | ibmvmc.state = ibmvmc_state_sched_reset; | 
|  | dev_dbg(adapter->dev, "Device reset scheduled"); | 
|  | wake_up_interruptible(&adapter->reset_wait_queue); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * ibmvmc_reset_task - Reset Task | 
|  | * | 
|  | * @data:	Data field | 
|  | * | 
|  | * Performs a CRQ reset of the VMC device in process context. | 
|  | * NOTE: This function should not be called directly, use ibmvmc_reset. | 
|  | */ | 
|  | static int ibmvmc_reset_task(void *data) | 
|  | { | 
|  | struct crq_server_adapter *adapter = data; | 
|  | int rc; | 
|  |  | 
|  | set_user_nice(current, -20); | 
|  |  | 
|  | while (!kthread_should_stop()) { | 
|  | wait_event_interruptible(adapter->reset_wait_queue, | 
|  | (ibmvmc.state == ibmvmc_state_sched_reset) || | 
|  | kthread_should_stop()); | 
|  |  | 
|  | if (kthread_should_stop()) | 
|  | break; | 
|  |  | 
|  | dev_dbg(adapter->dev, "CRQ resetting in process context"); | 
|  | tasklet_disable(&adapter->work_task); | 
|  |  | 
|  | rc = ibmvmc_reset_crq_queue(adapter); | 
|  |  | 
|  | if (rc != H_SUCCESS && rc != H_RESOURCE) { | 
|  | dev_err(adapter->dev, "Error initializing CRQ.  rc = 0x%x\n", | 
|  | rc); | 
|  | ibmvmc.state = ibmvmc_state_failed; | 
|  | } else { | 
|  | ibmvmc.state = ibmvmc_state_crqinit; | 
|  |  | 
|  | if (ibmvmc_send_crq(adapter, 0xC001000000000000LL, 0) | 
|  | != 0 && rc != H_RESOURCE) | 
|  | dev_warn(adapter->dev, "Failed to send initialize CRQ message\n"); | 
|  | } | 
|  |  | 
|  | vio_enable_interrupts(to_vio_dev(adapter->dev)); | 
|  | tasklet_enable(&adapter->work_task); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * ibmvmc_process_open_resp - Process Open Response | 
|  | * | 
|  | * @crq: ibmvmc_crq_msg struct | 
|  | * @adapter:    crq_server_adapter struct | 
|  | * | 
|  | * This command is sent by the hypervisor in response to the Interface | 
|  | * Open message. When this message is received, the indicated buffer is | 
|  | * again available for management partition use. | 
|  | */ | 
|  | static void ibmvmc_process_open_resp(struct ibmvmc_crq_msg *crq, | 
|  | struct crq_server_adapter *adapter) | 
|  | { | 
|  | unsigned char hmc_index; | 
|  | unsigned short buffer_id; | 
|  |  | 
|  | hmc_index = crq->hmc_index; | 
|  | if (hmc_index > ibmvmc.max_hmc_index) { | 
|  | /* Why would PHYP give an index > max negotiated? */ | 
|  | ibmvmc_reset(adapter, false); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (crq->status) { | 
|  | dev_warn(adapter->dev, "open_resp: failed - status 0x%x\n", | 
|  | crq->status); | 
|  | ibmvmc_return_hmc(&hmcs[hmc_index], false); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (hmcs[hmc_index].state == ibmhmc_state_opening) { | 
|  | buffer_id = be16_to_cpu(crq->var2.buffer_id); | 
|  | if (buffer_id >= ibmvmc.max_buffer_pool_size) { | 
|  | dev_err(adapter->dev, "open_resp: invalid buffer_id = 0x%x\n", | 
|  | buffer_id); | 
|  | hmcs[hmc_index].state = ibmhmc_state_failed; | 
|  | } else { | 
|  | ibmvmc_free_hmc_buffer(&hmcs[hmc_index], | 
|  | &hmcs[hmc_index].buffer[buffer_id]); | 
|  | hmcs[hmc_index].state = ibmhmc_state_ready; | 
|  | dev_dbg(adapter->dev, "open_resp: set hmc state = ready\n"); | 
|  | } | 
|  | } else { | 
|  | dev_warn(adapter->dev, "open_resp: invalid hmc state (0x%x)\n", | 
|  | hmcs[hmc_index].state); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * ibmvmc_process_close_resp - Process Close Response | 
|  | * | 
|  | * @crq: ibmvmc_crq_msg struct | 
|  | * @adapter:    crq_server_adapter struct | 
|  | * | 
|  | * This command is sent by the hypervisor in response to the managemant | 
|  | * application Interface Close message. | 
|  | * | 
|  | * If the close fails, simply reset the entire driver as the state of the VMC | 
|  | * must be in tough shape. | 
|  | */ | 
|  | static void ibmvmc_process_close_resp(struct ibmvmc_crq_msg *crq, | 
|  | struct crq_server_adapter *adapter) | 
|  | { | 
|  | unsigned char hmc_index; | 
|  |  | 
|  | hmc_index = crq->hmc_index; | 
|  | if (hmc_index > ibmvmc.max_hmc_index) { | 
|  | ibmvmc_reset(adapter, false); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (crq->status) { | 
|  | dev_warn(adapter->dev, "close_resp: failed - status 0x%x\n", | 
|  | crq->status); | 
|  | ibmvmc_reset(adapter, false); | 
|  | return; | 
|  | } | 
|  |  | 
|  | ibmvmc_return_hmc(&hmcs[hmc_index], false); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * ibmvmc_crq_process - Process CRQ | 
|  | * | 
|  | * @adapter:    crq_server_adapter struct | 
|  | * @crq:	ibmvmc_crq_msg struct | 
|  | * | 
|  | * Process the CRQ message based upon the type of message received. | 
|  | * | 
|  | */ | 
|  | static void ibmvmc_crq_process(struct crq_server_adapter *adapter, | 
|  | struct ibmvmc_crq_msg *crq) | 
|  | { | 
|  | switch (crq->type) { | 
|  | case VMC_MSG_CAP_RESP: | 
|  | dev_dbg(adapter->dev, "CRQ recv: capabilities resp (0x%x)\n", | 
|  | crq->type); | 
|  | if (ibmvmc.state == ibmvmc_state_capabilities) | 
|  | ibmvmc_process_capabilities(adapter, crq); | 
|  | else | 
|  | dev_warn(adapter->dev, "caps msg invalid in state 0x%x\n", | 
|  | ibmvmc.state); | 
|  | break; | 
|  | case VMC_MSG_OPEN_RESP: | 
|  | dev_dbg(adapter->dev, "CRQ recv: open resp (0x%x)\n", | 
|  | crq->type); | 
|  | if (ibmvmc_validate_hmc_session(adapter, crq) == 0) | 
|  | ibmvmc_process_open_resp(crq, adapter); | 
|  | break; | 
|  | case VMC_MSG_ADD_BUF: | 
|  | dev_dbg(adapter->dev, "CRQ recv: add buf (0x%x)\n", | 
|  | crq->type); | 
|  | if (ibmvmc_validate_hmc_session(adapter, crq) == 0) | 
|  | ibmvmc_add_buffer(adapter, crq); | 
|  | break; | 
|  | case VMC_MSG_REM_BUF: | 
|  | dev_dbg(adapter->dev, "CRQ recv: rem buf (0x%x)\n", | 
|  | crq->type); | 
|  | if (ibmvmc_validate_hmc_session(adapter, crq) == 0) | 
|  | ibmvmc_rem_buffer(adapter, crq); | 
|  | break; | 
|  | case VMC_MSG_SIGNAL: | 
|  | dev_dbg(adapter->dev, "CRQ recv: signal msg (0x%x)\n", | 
|  | crq->type); | 
|  | if (ibmvmc_validate_hmc_session(adapter, crq) == 0) | 
|  | ibmvmc_recv_msg(adapter, crq); | 
|  | break; | 
|  | case VMC_MSG_CLOSE_RESP: | 
|  | dev_dbg(adapter->dev, "CRQ recv: close resp (0x%x)\n", | 
|  | crq->type); | 
|  | if (ibmvmc_validate_hmc_session(adapter, crq) == 0) | 
|  | ibmvmc_process_close_resp(crq, adapter); | 
|  | break; | 
|  | case VMC_MSG_CAP: | 
|  | case VMC_MSG_OPEN: | 
|  | case VMC_MSG_CLOSE: | 
|  | case VMC_MSG_ADD_BUF_RESP: | 
|  | case VMC_MSG_REM_BUF_RESP: | 
|  | dev_warn(adapter->dev, "CRQ recv: unexpected msg (0x%x)\n", | 
|  | crq->type); | 
|  | break; | 
|  | default: | 
|  | dev_warn(adapter->dev, "CRQ recv: unknown msg (0x%x)\n", | 
|  | crq->type); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * ibmvmc_handle_crq_init - Handle CRQ Init | 
|  | * | 
|  | * @crq:	ibmvmc_crq_msg struct | 
|  | * @adapter:	crq_server_adapter struct | 
|  | * | 
|  | * Handle the type of crq initialization based on whether | 
|  | * it is a message or a response. | 
|  | * | 
|  | */ | 
|  | static void ibmvmc_handle_crq_init(struct ibmvmc_crq_msg *crq, | 
|  | struct crq_server_adapter *adapter) | 
|  | { | 
|  | switch (crq->type) { | 
|  | case 0x01:	/* Initialization message */ | 
|  | dev_dbg(adapter->dev, "CRQ recv: CRQ init msg - state 0x%x\n", | 
|  | ibmvmc.state); | 
|  | if (ibmvmc.state == ibmvmc_state_crqinit) { | 
|  | /* Send back a response */ | 
|  | if (ibmvmc_send_crq(adapter, 0xC002000000000000, | 
|  | 0) == 0) | 
|  | ibmvmc_send_capabilities(adapter); | 
|  | else | 
|  | dev_err(adapter->dev, " Unable to send init rsp\n"); | 
|  | } else { | 
|  | dev_err(adapter->dev, "Invalid state 0x%x mtu = 0x%x\n", | 
|  | ibmvmc.state, ibmvmc.max_mtu); | 
|  | } | 
|  |  | 
|  | break; | 
|  | case 0x02:	/* Initialization response */ | 
|  | dev_dbg(adapter->dev, "CRQ recv: initialization resp msg - state 0x%x\n", | 
|  | ibmvmc.state); | 
|  | if (ibmvmc.state == ibmvmc_state_crqinit) | 
|  | ibmvmc_send_capabilities(adapter); | 
|  | break; | 
|  | default: | 
|  | dev_warn(adapter->dev, "Unknown crq message type 0x%lx\n", | 
|  | (unsigned long)crq->type); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * ibmvmc_handle_crq - Handle CRQ | 
|  | * | 
|  | * @crq:	ibmvmc_crq_msg struct | 
|  | * @adapter:	crq_server_adapter struct | 
|  | * | 
|  | * Read the command elements from the command queue and execute the | 
|  | * requests based upon the type of crq message. | 
|  | * | 
|  | */ | 
|  | static void ibmvmc_handle_crq(struct ibmvmc_crq_msg *crq, | 
|  | struct crq_server_adapter *adapter) | 
|  | { | 
|  | switch (crq->valid) { | 
|  | case 0xC0:		/* initialization */ | 
|  | ibmvmc_handle_crq_init(crq, adapter); | 
|  | break; | 
|  | case 0xFF:	/* Hypervisor telling us the connection is closed */ | 
|  | dev_warn(adapter->dev, "CRQ recv: virtual adapter failed - resetting.\n"); | 
|  | ibmvmc_reset(adapter, true); | 
|  | break; | 
|  | case 0x80:	/* real payload */ | 
|  | ibmvmc_crq_process(adapter, crq); | 
|  | break; | 
|  | default: | 
|  | dev_warn(adapter->dev, "CRQ recv: unknown msg 0x%02x.\n", | 
|  | crq->valid); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | static void ibmvmc_task(unsigned long data) | 
|  | { | 
|  | struct crq_server_adapter *adapter = | 
|  | (struct crq_server_adapter *)data; | 
|  | struct vio_dev *vdev = to_vio_dev(adapter->dev); | 
|  | struct ibmvmc_crq_msg *crq; | 
|  | int done = 0; | 
|  |  | 
|  | while (!done) { | 
|  | /* Pull all the valid messages off the CRQ */ | 
|  | while ((crq = crq_queue_next_crq(&adapter->queue)) != NULL) { | 
|  | ibmvmc_handle_crq(crq, adapter); | 
|  | crq->valid = 0x00; | 
|  | /* CRQ reset was requested, stop processing CRQs. | 
|  | * Interrupts will be re-enabled by the reset task. | 
|  | */ | 
|  | if (ibmvmc.state == ibmvmc_state_sched_reset) | 
|  | return; | 
|  | } | 
|  |  | 
|  | vio_enable_interrupts(vdev); | 
|  | crq = crq_queue_next_crq(&adapter->queue); | 
|  | if (crq) { | 
|  | vio_disable_interrupts(vdev); | 
|  | ibmvmc_handle_crq(crq, adapter); | 
|  | crq->valid = 0x00; | 
|  | /* CRQ reset was requested, stop processing CRQs. | 
|  | * Interrupts will be re-enabled by the reset task. | 
|  | */ | 
|  | if (ibmvmc.state == ibmvmc_state_sched_reset) | 
|  | return; | 
|  | } else { | 
|  | done = 1; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * ibmvmc_init_crq_queue - Init CRQ Queue | 
|  | * | 
|  | * @adapter:	crq_server_adapter struct | 
|  | * | 
|  | * Return: | 
|  | *	0 - Success | 
|  | *	Non-zero - Failure | 
|  | */ | 
|  | static int ibmvmc_init_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; | 
|  | int retrc = 0; | 
|  |  | 
|  | queue->msgs = (struct ibmvmc_crq_msg *)get_zeroed_page(GFP_KERNEL); | 
|  |  | 
|  | if (!queue->msgs) | 
|  | goto malloc_failed; | 
|  |  | 
|  | queue->size = PAGE_SIZE / sizeof(*queue->msgs); | 
|  |  | 
|  | queue->msg_token = dma_map_single(adapter->dev, queue->msgs, | 
|  | queue->size * sizeof(*queue->msgs), | 
|  | DMA_BIDIRECTIONAL); | 
|  |  | 
|  | if (dma_mapping_error(adapter->dev, queue->msg_token)) | 
|  | goto map_failed; | 
|  |  | 
|  | retrc = plpar_hcall_norets(H_REG_CRQ, | 
|  | vdev->unit_address, | 
|  | queue->msg_token, PAGE_SIZE); | 
|  | rc = retrc; | 
|  |  | 
|  | if (rc == H_RESOURCE) | 
|  | rc = ibmvmc_reset_crq_queue(adapter); | 
|  |  | 
|  | if (rc == 2) { | 
|  | dev_warn(adapter->dev, "Partner adapter not ready\n"); | 
|  | retrc = 0; | 
|  | } else if (rc != 0) { | 
|  | dev_err(adapter->dev, "Error %d opening adapter\n", rc); | 
|  | goto reg_crq_failed; | 
|  | } | 
|  |  | 
|  | queue->cur = 0; | 
|  | spin_lock_init(&queue->lock); | 
|  |  | 
|  | tasklet_init(&adapter->work_task, ibmvmc_task, (unsigned long)adapter); | 
|  |  | 
|  | if (request_irq(vdev->irq, | 
|  | ibmvmc_handle_event, | 
|  | 0, "ibmvmc", (void *)adapter) != 0) { | 
|  | dev_err(adapter->dev, "couldn't register irq 0x%x\n", | 
|  | vdev->irq); | 
|  | goto req_irq_failed; | 
|  | } | 
|  |  | 
|  | rc = vio_enable_interrupts(vdev); | 
|  | if (rc != 0) { | 
|  | dev_err(adapter->dev, "Error %d enabling interrupts!!!\n", rc); | 
|  | goto req_irq_failed; | 
|  | } | 
|  |  | 
|  | return retrc; | 
|  |  | 
|  | req_irq_failed: | 
|  | /* Cannot have any work since we either never got our IRQ registered, | 
|  | * or never got interrupts enabled | 
|  | */ | 
|  | tasklet_kill(&adapter->work_task); | 
|  | h_free_crq(vdev->unit_address); | 
|  | reg_crq_failed: | 
|  | dma_unmap_single(adapter->dev, | 
|  | queue->msg_token, | 
|  | queue->size * sizeof(*queue->msgs), DMA_BIDIRECTIONAL); | 
|  | map_failed: | 
|  | free_page((unsigned long)queue->msgs); | 
|  | malloc_failed: | 
|  | return -ENOMEM; | 
|  | } | 
|  |  | 
|  | /* Fill in the liobn and riobn fields on the adapter */ | 
|  | static int read_dma_window(struct vio_dev *vdev, | 
|  | struct crq_server_adapter *adapter) | 
|  | { | 
|  | const __be32 *dma_window; | 
|  | const __be32 *prop; | 
|  |  | 
|  | /* TODO Using of_parse_dma_window would be better, but it doesn't give | 
|  | * a way to read multiple windows without already knowing the size of | 
|  | * a window or the number of windows | 
|  | */ | 
|  | dma_window = | 
|  | (const __be32 *)vio_get_attribute(vdev, "ibm,my-dma-window", | 
|  | NULL); | 
|  | if (!dma_window) { | 
|  | dev_warn(adapter->dev, "Couldn't find ibm,my-dma-window property\n"); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | adapter->liobn = be32_to_cpu(*dma_window); | 
|  | dma_window++; | 
|  |  | 
|  | prop = (const __be32 *)vio_get_attribute(vdev, "ibm,#dma-address-cells", | 
|  | NULL); | 
|  | if (!prop) { | 
|  | dev_warn(adapter->dev, "Couldn't find ibm,#dma-address-cells property\n"); | 
|  | dma_window++; | 
|  | } else { | 
|  | dma_window += be32_to_cpu(*prop); | 
|  | } | 
|  |  | 
|  | prop = (const __be32 *)vio_get_attribute(vdev, "ibm,#dma-size-cells", | 
|  | NULL); | 
|  | if (!prop) { | 
|  | dev_warn(adapter->dev, "Couldn't find ibm,#dma-size-cells property\n"); | 
|  | dma_window++; | 
|  | } else { | 
|  | dma_window += be32_to_cpu(*prop); | 
|  | } | 
|  |  | 
|  | /* dma_window should point to the second window now */ | 
|  | adapter->riobn = be32_to_cpu(*dma_window); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int ibmvmc_probe(struct vio_dev *vdev, const struct vio_device_id *id) | 
|  | { | 
|  | struct crq_server_adapter *adapter = &ibmvmc_adapter; | 
|  | int rc; | 
|  |  | 
|  | dev_set_drvdata(&vdev->dev, NULL); | 
|  | memset(adapter, 0, sizeof(*adapter)); | 
|  | adapter->dev = &vdev->dev; | 
|  |  | 
|  | dev_info(adapter->dev, "Probe for UA 0x%x\n", vdev->unit_address); | 
|  |  | 
|  | rc = read_dma_window(vdev, adapter); | 
|  | if (rc != 0) { | 
|  | ibmvmc.state = ibmvmc_state_failed; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | dev_dbg(adapter->dev, "Probe: liobn 0x%x, riobn 0x%x\n", | 
|  | adapter->liobn, adapter->riobn); | 
|  |  | 
|  | init_waitqueue_head(&adapter->reset_wait_queue); | 
|  | adapter->reset_task = kthread_run(ibmvmc_reset_task, adapter, "ibmvmc"); | 
|  | if (IS_ERR(adapter->reset_task)) { | 
|  | dev_err(adapter->dev, "Failed to start reset thread\n"); | 
|  | ibmvmc.state = ibmvmc_state_failed; | 
|  | rc = PTR_ERR(adapter->reset_task); | 
|  | adapter->reset_task = NULL; | 
|  | return rc; | 
|  | } | 
|  |  | 
|  | rc = ibmvmc_init_crq_queue(adapter); | 
|  | if (rc != 0 && rc != H_RESOURCE) { | 
|  | dev_err(adapter->dev, "Error initializing CRQ.  rc = 0x%x\n", | 
|  | rc); | 
|  | ibmvmc.state = ibmvmc_state_failed; | 
|  | goto crq_failed; | 
|  | } | 
|  |  | 
|  | ibmvmc.state = ibmvmc_state_crqinit; | 
|  |  | 
|  | /* Try to send an initialization message.  Note that this is allowed | 
|  | * to fail if the other end is not acive.  In that case we just wait | 
|  | * for the other side to initialize. | 
|  | */ | 
|  | if (ibmvmc_send_crq(adapter, 0xC001000000000000LL, 0) != 0 && | 
|  | rc != H_RESOURCE) | 
|  | dev_warn(adapter->dev, "Failed to send initialize CRQ message\n"); | 
|  |  | 
|  | dev_set_drvdata(&vdev->dev, adapter); | 
|  |  | 
|  | return 0; | 
|  |  | 
|  | crq_failed: | 
|  | kthread_stop(adapter->reset_task); | 
|  | adapter->reset_task = NULL; | 
|  | return -EPERM; | 
|  | } | 
|  |  | 
|  | static int ibmvmc_remove(struct vio_dev *vdev) | 
|  | { | 
|  | struct crq_server_adapter *adapter = dev_get_drvdata(&vdev->dev); | 
|  |  | 
|  | dev_info(adapter->dev, "Entering remove for UA 0x%x\n", | 
|  | vdev->unit_address); | 
|  | ibmvmc_release_crq_queue(adapter); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static struct vio_device_id ibmvmc_device_table[] = { | 
|  | { "ibm,vmc", "IBM,vmc" }, | 
|  | { "", "" } | 
|  | }; | 
|  | MODULE_DEVICE_TABLE(vio, ibmvmc_device_table); | 
|  |  | 
|  | static struct vio_driver ibmvmc_driver = { | 
|  | .name        = ibmvmc_driver_name, | 
|  | .id_table    = ibmvmc_device_table, | 
|  | .probe       = ibmvmc_probe, | 
|  | .remove      = ibmvmc_remove, | 
|  | }; | 
|  |  | 
|  | static void __init ibmvmc_scrub_module_parms(void) | 
|  | { | 
|  | if (ibmvmc_max_mtu > MAX_MTU) { | 
|  | pr_warn("ibmvmc: Max MTU reduced to %d\n", MAX_MTU); | 
|  | ibmvmc_max_mtu = MAX_MTU; | 
|  | } else if (ibmvmc_max_mtu < MIN_MTU) { | 
|  | pr_warn("ibmvmc: Max MTU increased to %d\n", MIN_MTU); | 
|  | ibmvmc_max_mtu = MIN_MTU; | 
|  | } | 
|  |  | 
|  | if (ibmvmc_max_buf_pool_size > MAX_BUF_POOL_SIZE) { | 
|  | pr_warn("ibmvmc: Max buffer pool size reduced to %d\n", | 
|  | MAX_BUF_POOL_SIZE); | 
|  | ibmvmc_max_buf_pool_size = MAX_BUF_POOL_SIZE; | 
|  | } else if (ibmvmc_max_buf_pool_size < MIN_BUF_POOL_SIZE) { | 
|  | pr_warn("ibmvmc: Max buffer pool size increased to %d\n", | 
|  | MIN_BUF_POOL_SIZE); | 
|  | ibmvmc_max_buf_pool_size = MIN_BUF_POOL_SIZE; | 
|  | } | 
|  |  | 
|  | if (ibmvmc_max_hmcs > MAX_HMCS) { | 
|  | pr_warn("ibmvmc: Max HMCs reduced to %d\n", MAX_HMCS); | 
|  | ibmvmc_max_hmcs = MAX_HMCS; | 
|  | } else if (ibmvmc_max_hmcs < MIN_HMCS) { | 
|  | pr_warn("ibmvmc: Max HMCs increased to %d\n", MIN_HMCS); | 
|  | ibmvmc_max_hmcs = MIN_HMCS; | 
|  | } | 
|  | } | 
|  |  | 
|  | static struct miscdevice ibmvmc_miscdev = { | 
|  | .name = ibmvmc_driver_name, | 
|  | .minor = MISC_DYNAMIC_MINOR, | 
|  | .fops = &ibmvmc_fops, | 
|  | }; | 
|  |  | 
|  | static int __init ibmvmc_module_init(void) | 
|  | { | 
|  | int rc, i, j; | 
|  |  | 
|  | ibmvmc.state = ibmvmc_state_initial; | 
|  | pr_info("ibmvmc: version %s\n", IBMVMC_DRIVER_VERSION); | 
|  |  | 
|  | rc = misc_register(&ibmvmc_miscdev); | 
|  | if (rc) { | 
|  | pr_err("ibmvmc: misc registration failed\n"); | 
|  | goto misc_register_failed; | 
|  | } | 
|  | pr_info("ibmvmc: node %d:%d\n", MISC_MAJOR, | 
|  | ibmvmc_miscdev.minor); | 
|  |  | 
|  | /* Initialize data structures */ | 
|  | memset(hmcs, 0, sizeof(struct ibmvmc_hmc) * MAX_HMCS); | 
|  | for (i = 0; i < MAX_HMCS; i++) { | 
|  | spin_lock_init(&hmcs[i].lock); | 
|  | hmcs[i].state = ibmhmc_state_free; | 
|  | for (j = 0; j < MAX_BUF_POOL_SIZE; j++) | 
|  | hmcs[i].queue_outbound_msgs[j] = VMC_INVALID_BUFFER_ID; | 
|  | } | 
|  |  | 
|  | /* Sanity check module parms */ | 
|  | ibmvmc_scrub_module_parms(); | 
|  |  | 
|  | /* | 
|  | * Initialize some reasonable values.  Might be negotiated smaller | 
|  | * values during the capabilities exchange. | 
|  | */ | 
|  | ibmvmc.max_mtu = ibmvmc_max_mtu; | 
|  | ibmvmc.max_buffer_pool_size = ibmvmc_max_buf_pool_size; | 
|  | ibmvmc.max_hmc_index = ibmvmc_max_hmcs - 1; | 
|  |  | 
|  | rc = vio_register_driver(&ibmvmc_driver); | 
|  |  | 
|  | if (rc) { | 
|  | pr_err("ibmvmc: rc %d from vio_register_driver\n", rc); | 
|  | goto vio_reg_failed; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  |  | 
|  | vio_reg_failed: | 
|  | misc_deregister(&ibmvmc_miscdev); | 
|  | misc_register_failed: | 
|  | return rc; | 
|  | } | 
|  |  | 
|  | static void __exit ibmvmc_module_exit(void) | 
|  | { | 
|  | pr_info("ibmvmc: module exit\n"); | 
|  | vio_unregister_driver(&ibmvmc_driver); | 
|  | misc_deregister(&ibmvmc_miscdev); | 
|  | } | 
|  |  | 
|  | module_init(ibmvmc_module_init); | 
|  | module_exit(ibmvmc_module_exit); | 
|  |  | 
|  | module_param_named(buf_pool_size, ibmvmc_max_buf_pool_size, | 
|  | int, 0644); | 
|  | MODULE_PARM_DESC(buf_pool_size, "Buffer pool size"); | 
|  | module_param_named(max_hmcs, ibmvmc_max_hmcs, int, 0644); | 
|  | MODULE_PARM_DESC(max_hmcs, "Max HMCs"); | 
|  | module_param_named(max_mtu, ibmvmc_max_mtu, int, 0644); | 
|  | MODULE_PARM_DESC(max_mtu, "Max MTU"); | 
|  |  | 
|  | MODULE_AUTHOR("Steven Royer <[email protected]>"); | 
|  | MODULE_DESCRIPTION("IBM VMC"); | 
|  | MODULE_VERSION(IBMVMC_DRIVER_VERSION); | 
|  | MODULE_LICENSE("GPL v2"); |