/*
* VMware VMCI Driver
*
* Copyright (C) 2012 VMware, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation version 2 and no later version.
*
* 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.
*/
#include <linux/vmw_vmci_defs.h>
#include <linux/vmw_vmci_api.h>
#include <linux/moduleparam.h>
#include <linux/miscdevice.h>
#include <linux/interrupt.h>
#include <linux/highmem.h>
#include <linux/atomic.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/file.h>
#include <linux/init.h>
#include <linux/poll.h>
#include <linux/pci.h>
#include <linux/smp.h>
#include <linux/fs.h>
#include <linux/io.h>
#include "vmci_handle_array.h"
#include "vmci_queue_pair.h"
#include "vmci_datagram.h"
#include "vmci_doorbell.h"
#include "vmci_resource.h"
#include "vmci_context.h"
#include "vmci_driver.h"
#include "vmci_event.h"
#define VMCI_UTIL_NUM_RESOURCES 1
enum {
VMCI_NOTIFY_RESOURCE_QUEUE_PAIR = 0,
VMCI_NOTIFY_RESOURCE_DOOR_BELL = 1,
};
enum {
VMCI_NOTIFY_RESOURCE_ACTION_NOTIFY = 0,
VMCI_NOTIFY_RESOURCE_ACTION_CREATE = 1,
VMCI_NOTIFY_RESOURCE_ACTION_DESTROY = 2,
};
/*
* VMCI driver initialization. This block can also be used to
* pass initial group membership etc.
*/
struct vmci_init_blk {
u32 cid;
u32 flags;
};
/* VMCIqueue_pairAllocInfo_VMToVM */
struct vmci_qp_alloc_info_vmvm {
struct vmci_handle handle;
u32 peer;
u32 flags;
u64 produce_size;
u64 consume_size;
u64 produce_page_file; /* User VA. */
u64 consume_page_file; /* User VA. */
u64 produce_page_file_size; /* Size of the file name array. */
u64 consume_page_file_size; /* Size of the file name array. */
s32 result;
u32 _pad;
};
/* VMCISetNotifyInfo: Used to pass notify flag's address to the host driver. */
struct vmci_set_notify_info {
u64 notify_uva;
s32 result;
u32 _pad;
};
/*
* Per-instance host state
*/
struct vmci_host_dev {
struct vmci_ctx *context;
int user_version;
enum vmci_obj_type ct_type;
struct mutex lock; /* Mutex lock for vmci context access */
};
static struct vmci_ctx *host_context;
static bool vmci_host_device_initialized;
static atomic_t vmci_host_active_users = ATOMIC_INIT(0);
/*
* Determines whether the VMCI host personality is
* available. Since the core functionality of the host driver is
* always present, all guests could possibly use the host
* personality. However, to minimize the deviation from the
* pre-unified driver state of affairs, we only consider the host
* device active if there is no active guest device or if there
* are VMX'en with active VMCI contexts using the host device.
*/
bool vmci_host_code_active(void)
{
return vmci_host_device_initialized &&
(!vmci_guest_code_active() ||
atomic_read(&vmci_host_active_users) > 0);
}
/*
* Called on open of /dev/vmci.
*/
static int vmci_host_open(struct inode *inode, struct file *filp)
{
struct vmci_host_dev *vmci_host_dev;
vmci_host_dev = kzalloc(sizeof(struct vmci_host_dev), GFP_KERNEL);
if (vmci_host_dev == NULL)
return -ENOMEM;
vmci_host_dev->ct_type = VMCIOBJ_NOT_SET;
mutex_init(&vmci_host_dev->lock);
filp->private_data = vmci_host_dev;
return 0;
}
/*
* Called on close of /dev/vmci, most often when the process
* exits.
*/
static int vmci_host_close(struct inode *inode, struct file *filp)
{
struct vmci_host_dev *vmci_host_dev = filp->private_data;
if (vmci_host_dev->ct_type == VMCIOBJ_CONTEXT) {
vmci_ctx_destroy(vmci_host_dev->context);
vmci_host_dev->context = NULL;
/*
* The number of active contexts is used to track whether any
* VMX'en are using the host personality. It is incremented when
* a context is created through the IOCTL_VMCI_INIT_CONTEXT
* ioctl.
*/
atomic_dec(&vmci_host_active_users);
}
vmci_host_dev->ct_type = VMCIOBJ_NOT_SET;
kfree(vmci_host_dev);
filp->private_data = NULL;
return 0;
}
/*
* This is used to wake up the VMX when a VMCI call arrives, or
* to wake up select() or poll() at the next clock tick.
*/
static unsigned int vmci_host_poll(struct file *filp, poll_table *wait)
{
struct vmci_host_dev *vmci_host_dev = filp->private_data;
struct vmci_ctx *context = vmci_host_dev->context;
unsigned int mask = 0;
if (vmci_host_dev->ct_type == VMCIOBJ_CONTEXT) {
/* Check for VMCI calls to this VM context. */
if (wait)
poll_wait(filp, &context->host_context.wait_queue,
wait);
spin_lock(&context->lock);
if (context->pending_datagrams > 0 ||
vmci_handle_arr_get_siz