Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

View file

@ -0,0 +1,16 @@
#
# VMware VMCI device
#
config VMWARE_VMCI
tristate "VMware VMCI Driver"
depends on X86 && PCI
help
This is VMware's Virtual Machine Communication Interface. It enables
high-speed communication between host and guest in a virtual
environment via the VMCI virtual device.
If unsure, say N.
To compile this driver as a module, choose M here: the
module will be called vmw_vmci.

View file

@ -0,0 +1,4 @@
obj-$(CONFIG_VMWARE_VMCI) += vmw_vmci.o
vmw_vmci-y += vmci_context.o vmci_datagram.o vmci_doorbell.o \
vmci_driver.o vmci_event.o vmci_guest.o vmci_handle_array.o \
vmci_host.o vmci_queue_pair.o vmci_resource.o vmci_route.o

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,182 @@
/*
* VMware VMCI driver (vmciContext.h)
*
* 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.
*/
#ifndef _VMCI_CONTEXT_H_
#define _VMCI_CONTEXT_H_
#include <linux/vmw_vmci_defs.h>
#include <linux/atomic.h>
#include <linux/kref.h>
#include <linux/types.h>
#include <linux/wait.h>
#include "vmci_handle_array.h"
#include "vmci_datagram.h"
/* Used to determine what checkpoint state to get and set. */
enum {
VMCI_NOTIFICATION_CPT_STATE = 1,
VMCI_WELLKNOWN_CPT_STATE = 2,
VMCI_DG_OUT_STATE = 3,
VMCI_DG_IN_STATE = 4,
VMCI_DG_IN_SIZE_STATE = 5,
VMCI_DOORBELL_CPT_STATE = 6,
};
/* Host specific struct used for signalling */
struct vmci_host {
wait_queue_head_t wait_queue;
};
struct vmci_handle_list {
struct list_head node;
struct vmci_handle handle;
};
struct vmci_ctx {
struct list_head list_item; /* For global VMCI list. */
u32 cid;
struct kref kref;
struct list_head datagram_queue; /* Head of per VM queue. */
u32 pending_datagrams;
size_t datagram_queue_size; /* Size of datagram queue in bytes. */
/*
* Version of the code that created
* this context; e.g., VMX.
*/
int user_version;
spinlock_t lock; /* Locks callQueue and handle_arrays. */
/*
* queue_pairs attached to. The array of
* handles for queue pairs is accessed
* from the code for QP API, and there
* it is protected by the QP lock. It
* is also accessed from the context
* clean up path, which does not
* require a lock. VMCILock is not
* used to protect the QP array field.
*/
struct vmci_handle_arr *queue_pair_array;
/* Doorbells created by context. */
struct vmci_handle_arr *doorbell_array;
/* Doorbells pending for context. */
struct vmci_handle_arr *pending_doorbell_array;
/* Contexts current context is subscribing to. */
struct list_head notifier_list;
unsigned int n_notifiers;
struct vmci_host host_context;
u32 priv_flags;
const struct cred *cred;
bool *notify; /* Notify flag pointer - hosted only. */
struct page *notify_page; /* Page backing the notify UVA. */
};
/* VMCINotifyAddRemoveInfo: Used to add/remove remote context notifications. */
struct vmci_ctx_info {
u32 remote_cid;
int result;
};
/* VMCICptBufInfo: Used to set/get current context's checkpoint state. */
struct vmci_ctx_chkpt_buf_info {
u64 cpt_buf;
u32 cpt_type;
u32 buf_size;
s32 result;
u32 _pad;
};
/*
* VMCINotificationReceiveInfo: Used to recieve pending notifications
* for doorbells and queue pairs.
*/
struct vmci_ctx_notify_recv_info {
u64 db_handle_buf_uva;
u64 db_handle_buf_size;
u64 qp_handle_buf_uva;
u64 qp_handle_buf_size;
s32 result;
u32 _pad;
};
/*
* Utilility function that checks whether two entities are allowed
* to interact. If one of them is restricted, the other one must
* be trusted.
*/
static inline bool vmci_deny_interaction(u32 part_one, u32 part_two)
{
return ((part_one & VMCI_PRIVILEGE_FLAG_RESTRICTED) &&
!(part_two & VMCI_PRIVILEGE_FLAG_TRUSTED)) ||
((part_two & VMCI_PRIVILEGE_FLAG_RESTRICTED) &&
!(part_one & VMCI_PRIVILEGE_FLAG_TRUSTED));
}
struct vmci_ctx *vmci_ctx_create(u32 cid, u32 flags,
uintptr_t event_hnd, int version,
const struct cred *cred);
void vmci_ctx_destroy(struct vmci_ctx *context);
bool vmci_ctx_supports_host_qp(struct vmci_ctx *context);
int vmci_ctx_enqueue_datagram(u32 cid, struct vmci_datagram *dg);
int vmci_ctx_dequeue_datagram(struct vmci_ctx *context,
size_t *max_size, struct vmci_datagram **dg);
int vmci_ctx_pending_datagrams(u32 cid, u32 *pending);
struct vmci_ctx *vmci_ctx_get(u32 cid);
void vmci_ctx_put(struct vmci_ctx *context);
bool vmci_ctx_exists(u32 cid);
int vmci_ctx_add_notification(u32 context_id, u32 remote_cid);
int vmci_ctx_remove_notification(u32 context_id, u32 remote_cid);
int vmci_ctx_get_chkpt_state(u32 context_id, u32 cpt_type,
u32 *num_cids, void **cpt_buf_ptr);
int vmci_ctx_set_chkpt_state(u32 context_id, u32 cpt_type,
u32 num_cids, void *cpt_buf);
int vmci_ctx_qp_create(struct vmci_ctx *context, struct vmci_handle handle);
int vmci_ctx_qp_destroy(struct vmci_ctx *context, struct vmci_handle handle);
bool vmci_ctx_qp_exists(struct vmci_ctx *context, struct vmci_handle handle);
void vmci_ctx_check_signal_notify(struct vmci_ctx *context);
void vmci_ctx_unset_notify(struct vmci_ctx *context);
int vmci_ctx_dbell_create(u32 context_id, struct vmci_handle handle);
int vmci_ctx_dbell_destroy(u32 context_id, struct vmci_handle handle);
int vmci_ctx_dbell_destroy_all(u32 context_id);
int vmci_ctx_notify_dbell(u32 cid, struct vmci_handle handle,
u32 src_priv_flags);
int vmci_ctx_rcv_notifications_get(u32 context_id, struct vmci_handle_arr
**db_handle_array, struct vmci_handle_arr
**qp_handle_array);
void vmci_ctx_rcv_notifications_release(u32 context_id, struct vmci_handle_arr
*db_handle_array, struct vmci_handle_arr
*qp_handle_array, bool success);
static inline u32 vmci_ctx_get_id(struct vmci_ctx *context)
{
if (!context)
return VMCI_INVALID_ID;
return context->cid;
}
#endif /* _VMCI_CONTEXT_H_ */

View file

@ -0,0 +1,503 @@
/*
* 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/module.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/bug.h>
#include "vmci_datagram.h"
#include "vmci_resource.h"
#include "vmci_context.h"
#include "vmci_driver.h"
#include "vmci_event.h"
#include "vmci_route.h"
/*
* struct datagram_entry describes the datagram entity. It is used for datagram
* entities created only on the host.
*/
struct datagram_entry {
struct vmci_resource resource;
u32 flags;
bool run_delayed;
vmci_datagram_recv_cb recv_cb;
void *client_data;
u32 priv_flags;
};
struct delayed_datagram_info {
struct datagram_entry *entry;
struct work_struct work;
bool in_dg_host_queue;
/* msg and msg_payload must be together. */
struct vmci_datagram msg;
u8 msg_payload[];
};
/* Number of in-flight host->host datagrams */
static atomic_t delayed_dg_host_queue_size = ATOMIC_INIT(0);
/*
* Create a datagram entry given a handle pointer.
*/
static int dg_create_handle(u32 resource_id,
u32 flags,
u32 priv_flags,
vmci_datagram_recv_cb recv_cb,
void *client_data, struct vmci_handle *out_handle)
{
int result;
u32 context_id;
struct vmci_handle handle;
struct datagram_entry *entry;
if ((flags & VMCI_FLAG_WELLKNOWN_DG_HND) != 0)
return VMCI_ERROR_INVALID_ARGS;
if ((flags & VMCI_FLAG_ANYCID_DG_HND) != 0) {
context_id = VMCI_INVALID_ID;
} else {
context_id = vmci_get_context_id();
if (context_id == VMCI_INVALID_ID)
return VMCI_ERROR_NO_RESOURCES;
}
handle = vmci_make_handle(context_id, resource_id);
entry = kmalloc(sizeof(*entry), GFP_KERNEL);
if (!entry) {
pr_warn("Failed allocating memory for datagram entry\n");
return VMCI_ERROR_NO_MEM;
}
entry->run_delayed = (flags & VMCI_FLAG_DG_DELAYED_CB) ? true : false;
entry->flags = flags;
entry->recv_cb = recv_cb;
entry->client_data = client_data;
entry->priv_flags = priv_flags;
/* Make datagram resource live. */
result = vmci_resource_add(&entry->resource,
VMCI_RESOURCE_TYPE_DATAGRAM,
handle);
if (result != VMCI_SUCCESS) {
pr_warn("Failed to add new resource (handle=0x%x:0x%x), error: %d\n",
handle.context, handle.resource, result);
kfree(entry);
return result;
}
*out_handle = vmci_resource_handle(&entry->resource);
return VMCI_SUCCESS;
}
/*
* Internal utility function with the same purpose as
* vmci_datagram_get_priv_flags that also takes a context_id.
*/
static int vmci_datagram_get_priv_flags(u32 context_id,
struct vmci_handle handle,
u32 *priv_flags)
{
if (context_id == VMCI_INVALID_ID)
return VMCI_ERROR_INVALID_ARGS;
if (context_id == VMCI_HOST_CONTEXT_ID) {
struct datagram_entry *src_entry;
struct vmci_resource *resource;
resource = vmci_resource_by_handle(handle,
VMCI_RESOURCE_TYPE_DATAGRAM);
if (!resource)
return VMCI_ERROR_INVALID_ARGS;
src_entry = container_of(resource, struct datagram_entry,
resource);
*priv_flags = src_entry->priv_flags;
vmci_resource_put(resource);
} else if (context_id == VMCI_HYPERVISOR_CONTEXT_ID)
*priv_flags = VMCI_MAX_PRIVILEGE_FLAGS;
else
*priv_flags = vmci_context_get_priv_flags(context_id);
return VMCI_SUCCESS;
}
/*
* Calls the specified callback in a delayed context.
*/
static void dg_delayed_dispatch(struct work_struct *work)
{
struct delayed_datagram_info *dg_info =
container_of(work, struct delayed_datagram_info, work);
dg_info->entry->recv_cb(dg_info->entry->client_data, &dg_info->msg);
vmci_resource_put(&dg_info->entry->resource);
if (dg_info->in_dg_host_queue)
atomic_dec(&delayed_dg_host_queue_size);
kfree(dg_info);
}
/*
* Dispatch datagram as a host, to the host, or other vm context. This
* function cannot dispatch to hypervisor context handlers. This should
* have been handled before we get here by vmci_datagram_dispatch.
* Returns number of bytes sent on success, error code otherwise.
*/
static int dg_dispatch_as_host(u32 context_id, struct vmci_datagram *dg)
{
int retval;
size_t dg_size;
u32 src_priv_flags;
dg_size = VMCI_DG_SIZE(dg);
/* Host cannot send to the hypervisor. */
if (dg->dst.context == VMCI_HYPERVISOR_CONTEXT_ID)
return VMCI_ERROR_DST_UNREACHABLE;
/* Check that source handle matches sending context. */
if (dg->src.context != context_id) {
pr_devel("Sender context (ID=0x%x) is not owner of src datagram entry (handle=0x%x:0x%x)\n",
context_id, dg->src.context, dg->src.resource);
return VMCI_ERROR_NO_ACCESS;
}
/* Get hold of privileges of sending endpoint. */
retval = vmci_datagram_get_priv_flags(context_id, dg->src,
&src_priv_flags);
if (retval != VMCI_SUCCESS) {
pr_warn("Couldn't get privileges (handle=0x%x:0x%x)\n",
dg->src.context, dg->src.resource);
return retval;
}
/* Determine if we should route to host or guest destination. */
if (dg->dst.context == VMCI_HOST_CONTEXT_ID) {
/* Route to host datagram entry. */
struct datagram_entry *dst_entry;
struct vmci_resource *resource;
if (dg->src.context == VMCI_HYPERVISOR_CONTEXT_ID &&
dg->dst.resource == VMCI_EVENT_HANDLER) {
return vmci_event_dispatch(dg);
}
resource = vmci_resource_by_handle(dg->dst,
VMCI_RESOURCE_TYPE_DATAGRAM);
if (!resource) {
pr_devel("Sending to invalid destination (handle=0x%x:0x%x)\n",
dg->dst.context, dg->dst.resource);
return VMCI_ERROR_INVALID_RESOURCE;
}
dst_entry = container_of(resource, struct datagram_entry,
resource);
if (vmci_deny_interaction(src_priv_flags,
dst_entry->priv_flags)) {
vmci_resource_put(resource);
return VMCI_ERROR_NO_ACCESS;
}
/*
* If a VMCI datagram destined for the host is also sent by the
* host, we always run it delayed. This ensures that no locks
* are held when the datagram callback runs.
*/
if (dst_entry->run_delayed ||
dg->src.context == VMCI_HOST_CONTEXT_ID) {
struct delayed_datagram_info *dg_info;
if (atomic_add_return(1, &delayed_dg_host_queue_size)
== VMCI_MAX_DELAYED_DG_HOST_QUEUE_SIZE) {
atomic_dec(&delayed_dg_host_queue_size);
vmci_resource_put(resource);
return VMCI_ERROR_NO_MEM;
}
dg_info = kmalloc(sizeof(*dg_info) +
(size_t) dg->payload_size, GFP_ATOMIC);
if (!dg_info) {
atomic_dec(&delayed_dg_host_queue_size);
vmci_resource_put(resource);
return VMCI_ERROR_NO_MEM;
}
dg_info->in_dg_host_queue = true;
dg_info->entry = dst_entry;
memcpy(&dg_info->msg, dg, dg_size);
INIT_WORK(&dg_info->work, dg_delayed_dispatch);
schedule_work(&dg_info->work);
retval = VMCI_SUCCESS;
} else {
retval = dst_entry->recv_cb(dst_entry->client_data, dg);
vmci_resource_put(resource);
if (retval < VMCI_SUCCESS)
return retval;
}
} else {
/* Route to destination VM context. */
struct vmci_datagram *new_dg;
if (context_id != dg->dst.context) {
if (vmci_deny_interaction(src_priv_flags,
vmci_context_get_priv_flags
(dg->dst.context))) {
return VMCI_ERROR_NO_ACCESS;
} else if (VMCI_CONTEXT_IS_VM(context_id)) {
/*
* If the sending context is a VM, it
* cannot reach another VM.
*/
pr_devel("Datagram communication between VMs not supported (src=0x%x, dst=0x%x)\n",
context_id, dg->dst.context);
return VMCI_ERROR_DST_UNREACHABLE;
}
}
/* We make a copy to enqueue. */
new_dg = kmalloc(dg_size, GFP_KERNEL);
if (new_dg == NULL)
return VMCI_ERROR_NO_MEM;
memcpy(new_dg, dg, dg_size);
retval = vmci_ctx_enqueue_datagram(dg->dst.context, new_dg);
if (retval < VMCI_SUCCESS) {
kfree(new_dg);
return retval;
}
}
/*
* We currently truncate the size to signed 32 bits. This doesn't
* matter for this handler as it only support 4Kb messages.
*/
return (int)dg_size;
}
/*
* Dispatch datagram as a guest, down through the VMX and potentially to
* the host.
* Returns number of bytes sent on success, error code otherwise.
*/
static int dg_dispatch_as_guest(struct vmci_datagram *dg)
{
int retval;
struct vmci_resource *resource;
resource = vmci_resource_by_handle(dg->src,
VMCI_RESOURCE_TYPE_DATAGRAM);
if (!resource)
return VMCI_ERROR_NO_HANDLE;
retval = vmci_send_datagram(dg);
vmci_resource_put(resource);
return retval;
}
/*
* Dispatch datagram. This will determine the routing for the datagram
* and dispatch it accordingly.
* Returns number of bytes sent on success, error code otherwise.
*/
int vmci_datagram_dispatch(u32 context_id,
struct vmci_datagram *dg, bool from_guest)
{
int retval;
enum vmci_route route;
BUILD_BUG_ON(sizeof(struct vmci_datagram) != 24);
if (dg->payload_size > VMCI_MAX_DG_SIZE ||
VMCI_DG_SIZE(dg) > VMCI_MAX_DG_SIZE) {
pr_devel("Payload (size=%llu bytes) too big to send\n",
(unsigned long long)dg->payload_size);
return VMCI_ERROR_INVALID_ARGS;
}
retval = vmci_route(&dg->src, &dg->dst, from_guest, &route);
if (retval < VMCI_SUCCESS) {
pr_devel("Failed to route datagram (src=0x%x, dst=0x%x, err=%d)\n",
dg->src.context, dg->dst.context, retval);
return retval;
}
if (VMCI_ROUTE_AS_HOST == route) {
if (VMCI_INVALID_ID == context_id)
context_id = VMCI_HOST_CONTEXT_ID;
return dg_dispatch_as_host(context_id, dg);
}
if (VMCI_ROUTE_AS_GUEST == route)
return dg_dispatch_as_guest(dg);
pr_warn("Unknown route (%d) for datagram\n", route);
return VMCI_ERROR_DST_UNREACHABLE;
}
/*
* Invoke the handler for the given datagram. This is intended to be
* called only when acting as a guest and receiving a datagram from the
* virtual device.
*/
int vmci_datagram_invoke_guest_handler(struct vmci_datagram *dg)
{
struct vmci_resource *resource;
struct datagram_entry *dst_entry;
resource = vmci_resource_by_handle(dg->dst,
VMCI_RESOURCE_TYPE_DATAGRAM);
if (!resource) {
pr_devel("destination (handle=0x%x:0x%x) doesn't exist\n",
dg->dst.context, dg->dst.resource);
return VMCI_ERROR_NO_HANDLE;
}
dst_entry = container_of(resource, struct datagram_entry, resource);
if (dst_entry->run_delayed) {
struct delayed_datagram_info *dg_info;
dg_info = kmalloc(sizeof(*dg_info) + (size_t)dg->payload_size,
GFP_ATOMIC);
if (!dg_info) {
vmci_resource_put(resource);
return VMCI_ERROR_NO_MEM;
}
dg_info->in_dg_host_queue = false;
dg_info->entry = dst_entry;
memcpy(&dg_info->msg, dg, VMCI_DG_SIZE(dg));
INIT_WORK(&dg_info->work, dg_delayed_dispatch);
schedule_work(&dg_info->work);
} else {
dst_entry->recv_cb(dst_entry->client_data, dg);
vmci_resource_put(resource);
}
return VMCI_SUCCESS;
}
/*
* vmci_datagram_create_handle_priv() - Create host context datagram endpoint
* @resource_id: The resource ID.
* @flags: Datagram Flags.
* @priv_flags: Privilege Flags.
* @recv_cb: Callback when receiving datagrams.
* @client_data: Pointer for a datagram_entry struct
* @out_handle: vmci_handle that is populated as a result of this function.
*
* Creates a host context datagram endpoint and returns a handle to it.
*/
int vmci_datagram_create_handle_priv(u32 resource_id,
u32 flags,
u32 priv_flags,
vmci_datagram_recv_cb recv_cb,
void *client_data,
struct vmci_handle *out_handle)
{
if (out_handle == NULL)
return VMCI_ERROR_INVALID_ARGS;
if (recv_cb == NULL) {
pr_devel("Client callback needed when creating datagram\n");
return VMCI_ERROR_INVALID_ARGS;
}
if (priv_flags & ~VMCI_PRIVILEGE_ALL_FLAGS)
return VMCI_ERROR_INVALID_ARGS;
return dg_create_handle(resource_id, flags, priv_flags, recv_cb,
client_data, out_handle);
}
EXPORT_SYMBOL_GPL(vmci_datagram_create_handle_priv);
/*
* vmci_datagram_create_handle() - Create host context datagram endpoint
* @resource_id: Resource ID.
* @flags: Datagram Flags.
* @recv_cb: Callback when receiving datagrams.
* @client_ata: Pointer for a datagram_entry struct
* @out_handle: vmci_handle that is populated as a result of this function.
*
* Creates a host context datagram endpoint and returns a handle to
* it. Same as vmci_datagram_create_handle_priv without the priviledge
* flags argument.
*/
int vmci_datagram_create_handle(u32 resource_id,
u32 flags,
vmci_datagram_recv_cb recv_cb,
void *client_data,
struct vmci_handle *out_handle)
{
return vmci_datagram_create_handle_priv(
resource_id, flags,
VMCI_DEFAULT_PROC_PRIVILEGE_FLAGS,
recv_cb, client_data,
out_handle);
}
EXPORT_SYMBOL_GPL(vmci_datagram_create_handle);
/*
* vmci_datagram_destroy_handle() - Destroys datagram handle
* @handle: vmci_handle to be destroyed and reaped.
*
* Use this function to destroy any datagram handles created by
* vmci_datagram_create_handle{,Priv} functions.
*/
int vmci_datagram_destroy_handle(struct vmci_handle handle)
{
struct datagram_entry *entry;
struct vmci_resource *resource;
resource = vmci_resource_by_handle(handle, VMCI_RESOURCE_TYPE_DATAGRAM);
if (!resource) {
pr_devel("Failed to destroy datagram (handle=0x%x:0x%x)\n",
handle.context, handle.resource);
return VMCI_ERROR_NOT_FOUND;
}
entry = container_of(resource, struct datagram_entry, resource);
vmci_resource_put(&entry->resource);
vmci_resource_remove(&entry->resource);
kfree(entry);
return VMCI_SUCCESS;
}
EXPORT_SYMBOL_GPL(vmci_datagram_destroy_handle);
/*
* vmci_datagram_send() - Send a datagram
* @msg: The datagram to send.
*
* Sends the provided datagram on its merry way.
*/
int vmci_datagram_send(struct vmci_datagram *msg)
{
if (msg == NULL)
return VMCI_ERROR_INVALID_ARGS;
return vmci_datagram_dispatch(VMCI_INVALID_ID, msg, false);
}
EXPORT_SYMBOL_GPL(vmci_datagram_send);

View file

@ -0,0 +1,52 @@
/*
* 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.
*/
#ifndef _VMCI_DATAGRAM_H_
#define _VMCI_DATAGRAM_H_
#include <linux/types.h>
#include <linux/list.h>
#include "vmci_context.h"
#define VMCI_MAX_DELAYED_DG_HOST_QUEUE_SIZE 256
/*
* The struct vmci_datagram_queue_entry is a queue header for the in-kernel VMCI
* datagram queues. It is allocated in non-paged memory, as the
* content is accessed while holding a spinlock. The pending datagram
* itself may be allocated from paged memory. We shadow the size of
* the datagram in the non-paged queue entry as this size is used
* while holding the same spinlock as above.
*/
struct vmci_datagram_queue_entry {
struct list_head list_item; /* For queuing. */
size_t dg_size; /* Size of datagram. */
struct vmci_datagram *dg; /* Pending datagram. */
};
/* VMCIDatagramSendRecvInfo */
struct vmci_datagram_snd_rcv_info {
u64 addr;
u32 len;
s32 result;
};
/* Datagram API for non-public use. */
int vmci_datagram_dispatch(u32 context_id, struct vmci_datagram *dg,
bool from_guest);
int vmci_datagram_invoke_guest_handler(struct vmci_datagram *dg);
#endif /* _VMCI_DATAGRAM_H_ */

View file

@ -0,0 +1,601 @@
/*
* 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/completion.h>
#include <linux/hash.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include "vmci_datagram.h"
#include "vmci_doorbell.h"
#include "vmci_resource.h"
#include "vmci_driver.h"
#include "vmci_route.h"
#define VMCI_DOORBELL_INDEX_BITS 6
#define VMCI_DOORBELL_INDEX_TABLE_SIZE (1 << VMCI_DOORBELL_INDEX_BITS)
#define VMCI_DOORBELL_HASH(_idx) hash_32(_idx, VMCI_DOORBELL_INDEX_BITS)
/*
* DoorbellEntry describes the a doorbell notification handle allocated by the
* host.
*/
struct dbell_entry {
struct vmci_resource resource;
struct hlist_node node;
struct work_struct work;
vmci_callback notify_cb;
void *client_data;
u32 idx;
u32 priv_flags;
bool run_delayed;
atomic_t active; /* Only used by guest personality */
};
/* The VMCI index table keeps track of currently registered doorbells. */
struct dbell_index_table {
spinlock_t lock; /* Index table lock */
struct hlist_head entries[VMCI_DOORBELL_INDEX_TABLE_SIZE];
};
static struct dbell_index_table vmci_doorbell_it = {
.lock = __SPIN_LOCK_UNLOCKED(vmci_doorbell_it.lock),
};
/*
* The max_notify_idx is one larger than the currently known bitmap index in
* use, and is used to determine how much of the bitmap needs to be scanned.
*/
static u32 max_notify_idx;
/*
* The notify_idx_count is used for determining whether there are free entries
* within the bitmap (if notify_idx_count + 1 < max_notify_idx).
*/
static u32 notify_idx_count;
/*
* The last_notify_idx_reserved is used to track the last index handed out - in
* the case where multiple handles share a notification index, we hand out
* indexes round robin based on last_notify_idx_reserved.
*/
static u32 last_notify_idx_reserved;
/* This is a one entry cache used to by the index allocation. */
static u32 last_notify_idx_released = PAGE_SIZE;
/*
* Utility function that retrieves the privilege flags associated
* with a given doorbell handle. For guest endpoints, the
* privileges are determined by the context ID, but for host
* endpoints privileges are associated with the complete
* handle. Hypervisor endpoints are not yet supported.
*/
int vmci_dbell_get_priv_flags(struct vmci_handle handle, u32 *priv_flags)
{
if (priv_flags == NULL || handle.context == VMCI_INVALID_ID)
return VMCI_ERROR_INVALID_ARGS;
if (handle.context == VMCI_HOST_CONTEXT_ID) {
struct dbell_entry *entry;
struct vmci_resource *resource;
resource = vmci_resource_by_handle(handle,
VMCI_RESOURCE_TYPE_DOORBELL);
if (!resource)
return VMCI_ERROR_NOT_FOUND;
entry = container_of(resource, struct dbell_entry, resource);
*priv_flags = entry->priv_flags;
vmci_resource_put(resource);
} else if (handle.context == VMCI_HYPERVISOR_CONTEXT_ID) {
/*
* Hypervisor endpoints for notifications are not
* supported (yet).
*/
return VMCI_ERROR_INVALID_ARGS;
} else {
*priv_flags = vmci_context_get_priv_flags(handle.context);
}
return VMCI_SUCCESS;
}
/*
* Find doorbell entry by bitmap index.
*/
static struct dbell_entry *dbell_index_table_find(u32 idx)
{
u32 bucket = VMCI_DOORBELL_HASH(idx);
struct dbell_entry *dbell;
hlist_for_each_entry(dbell, &vmci_doorbell_it.entries[bucket],
node) {
if (idx == dbell->idx)
return dbell;
}
return NULL;
}
/*
* Add the given entry to the index table. This willi take a reference to the
* entry's resource so that the entry is not deleted before it is removed from
* the * table.
*/
static void dbell_index_table_add(struct dbell_entry *entry)
{
u32 bucket;
u32 new_notify_idx;
vmci_resource_get(&entry->resource);
spin_lock_bh(&vmci_doorbell_it.lock);
/*
* Below we try to allocate an index in the notification
* bitmap with "not too much" sharing between resources. If we
* use less that the full bitmap, we either add to the end if
* there are no unused flags within the currently used area,
* or we search for unused ones. If we use the full bitmap, we
* allocate the index round robin.
*/
if (max_notify_idx < PAGE_SIZE || notify_idx_count < PAGE_SIZE) {
if (last_notify_idx_released < max_notify_idx &&
!dbell_index_table_find(last_notify_idx_released)) {
new_notify_idx = last_notify_idx_released;
last_notify_idx_released = PAGE_SIZE;
} else {
bool reused = false;
new_notify_idx = last_notify_idx_reserved;
if (notify_idx_count + 1 < max_notify_idx) {
do {
if (!dbell_index_table_find
(new_notify_idx)) {
reused = true;
break;
}
new_notify_idx = (new_notify_idx + 1) %
max_notify_idx;
} while (new_notify_idx !=
last_notify_idx_released);
}
if (!reused) {
new_notify_idx = max_notify_idx;
max_notify_idx++;
}
}
} else {
new_notify_idx = (last_notify_idx_reserved + 1) % PAGE_SIZE;
}
last_notify_idx_reserved = new_notify_idx;
notify_idx_count++;
entry->idx = new_notify_idx;
bucket = VMCI_DOORBELL_HASH(entry->idx);
hlist_add_head(&entry->node, &vmci_doorbell_it.entries[bucket]);
spin_unlock_bh(&vmci_doorbell_it.lock);
}
/*
* Remove the given entry from the index table. This will release() the
* entry's resource.
*/
static void dbell_index_table_remove(struct dbell_entry *entry)
{
spin_lock_bh(&vmci_doorbell_it.lock);
hlist_del_init(&entry->node);
notify_idx_count--;
if (entry->idx == max_notify_idx - 1) {
/*
* If we delete an entry with the maximum known
* notification index, we take the opportunity to
* prune the current max. As there might be other
* unused indices immediately below, we lower the
* maximum until we hit an index in use.
*/
while (max_notify_idx > 0 &&
!dbell_index_table_find(max_notify_idx - 1))
max_notify_idx--;
}
last_notify_idx_released = entry->idx;
spin_unlock_bh(&vmci_doorbell_it.lock);
vmci_resource_put(&entry->resource);
}
/*
* Creates a link between the given doorbell handle and the given
* index in the bitmap in the device backend. A notification state
* is created in hypervisor.
*/
static int dbell_link(struct vmci_handle handle, u32 notify_idx)
{
struct vmci_doorbell_link_msg link_msg;
link_msg.hdr.dst = vmci_make_handle(VMCI_HYPERVISOR_CONTEXT_ID,
VMCI_DOORBELL_LINK);
link_msg.hdr.src = VMCI_ANON_SRC_HANDLE;
link_msg.hdr.payload_size = sizeof(link_msg) - VMCI_DG_HEADERSIZE;
link_msg.handle = handle;
link_msg.notify_idx = notify_idx;
return vmci_send_datagram(&link_msg.hdr);
}
/*
* Unlinks the given doorbell handle from an index in the bitmap in
* the device backend. The notification state is destroyed in hypervisor.
*/
static int dbell_unlink(struct vmci_handle handle)
{
struct vmci_doorbell_unlink_msg unlink_msg;
unlink_msg.hdr.dst = vmci_make_handle(VMCI_HYPERVISOR_CONTEXT_ID,
VMCI_DOORBELL_UNLINK);
unlink_msg.hdr.src = VMCI_ANON_SRC_HANDLE;
unlink_msg.hdr.payload_size = sizeof(unlink_msg) - VMCI_DG_HEADERSIZE;
unlink_msg.handle = handle;
return vmci_send_datagram(&unlink_msg.hdr);
}
/*
* Notify another guest or the host. We send a datagram down to the
* host via the hypervisor with the notification info.
*/
static int dbell_notify_as_guest(struct vmci_handle handle, u32 priv_flags)
{
struct vmci_doorbell_notify_msg notify_msg;
notify_msg.hdr.dst = vmci_make_handle(VMCI_HYPERVISOR_CONTEXT_ID,
VMCI_DOORBELL_NOTIFY);
notify_msg.hdr.src = VMCI_ANON_SRC_HANDLE;
notify_msg.hdr.payload_size = sizeof(notify_msg) - VMCI_DG_HEADERSIZE;
notify_msg.handle = handle;
return vmci_send_datagram(&notify_msg.hdr);
}
/*
* Calls the specified callback in a delayed context.
*/
static void dbell_delayed_dispatch(struct work_struct *work)
{
struct dbell_entry *entry = container_of(work,
struct dbell_entry, work);
entry->notify_cb(entry->client_data);
vmci_resource_put(&entry->resource);
}
/*
* Dispatches a doorbell notification to the host context.
*/
int vmci_dbell_host_context_notify(u32 src_cid, struct vmci_handle handle)
{
struct dbell_entry *entry;
struct vmci_resource *resource;
if (vmci_handle_is_invalid(handle)) {
pr_devel("Notifying an invalid doorbell (handle=0x%x:0x%x)\n",
handle.context, handle.resource);
return VMCI_ERROR_INVALID_ARGS;
}
resource = vmci_resource_by_handle(handle,
VMCI_RESOURCE_TYPE_DOORBELL);
if (!resource) {
pr_devel("Notifying an unknown doorbell (handle=0x%x:0x%x)\n",
handle.context, handle.resource);
return VMCI_ERROR_NOT_FOUND;
}
entry = container_of(resource, struct dbell_entry, resource);
if (entry->run_delayed) {
schedule_work(&entry->work);
} else {
entry->notify_cb(entry->client_data);
vmci_resource_put(resource);
}
return VMCI_SUCCESS;
}
/*
* Register the notification bitmap with the host.
*/
bool vmci_dbell_register_notification_bitmap(u32 bitmap_ppn)
{
int result;
struct vmci_notify_bm_set_msg bitmap_set_msg;
bitmap_set_msg.hdr.dst = vmci_make_handle(VMCI_HYPERVISOR_CONTEXT_ID,
VMCI_SET_NOTIFY_BITMAP);
bitmap_set_msg.hdr.src = VMCI_ANON_SRC_HANDLE;
bitmap_set_msg.hdr.payload_size = sizeof(bitmap_set_msg) -
VMCI_DG_HEADERSIZE;
bitmap_set_msg.bitmap_ppn = bitmap_ppn;
result = vmci_send_datagram(&bitmap_set_msg.hdr);
if (result != VMCI_SUCCESS) {
pr_devel("Failed to register (PPN=%u) as notification bitmap (error=%d)\n",
bitmap_ppn, result);
return false;
}
return true;
}
/*
* Executes or schedules the handlers for a given notify index.
*/
static void dbell_fire_entries(u32 notify_idx)
{
u32 bucket = VMCI_DOORBELL_HASH(notify_idx);
struct dbell_entry *dbell;
spin_lock_bh(&vmci_doorbell_it.lock);
hlist_for_each_entry(dbell, &vmci_doorbell_it.entries[bucket], node) {
if (dbell->idx == notify_idx &&
atomic_read(&dbell->active) == 1) {
if (dbell->run_delayed) {
vmci_resource_get(&dbell->resource);
schedule_work(&dbell->work);
} else {
dbell->notify_cb(dbell->client_data);
}
}
}
spin_unlock_bh(&vmci_doorbell_it.lock);
}
/*
* Scans the notification bitmap, collects pending notifications,
* resets the bitmap and invokes appropriate callbacks.
*/
void vmci_dbell_scan_notification_entries(u8 *bitmap)
{
u32 idx;
for (idx = 0; idx < max_notify_idx; idx++) {
if (bitmap[idx] & 0x1) {
bitmap[idx] &= ~1;
dbell_fire_entries(idx);
}
}
}
/*
* vmci_doorbell_create() - Creates a doorbell
* @handle: A handle used to track the resource. Can be invalid.
* @flags: Flag that determines context of callback.
* @priv_flags: Privileges flags.
* @notify_cb: The callback to be ivoked when the doorbell fires.
* @client_data: A parameter to be passed to the callback.
*
* Creates a doorbell with the given callback. If the handle is
* VMCI_INVALID_HANDLE, a free handle will be assigned, if
* possible. The callback can be run immediately (potentially with
* locks held - the default) or delayed (in a kernel thread) by
* specifying the flag VMCI_FLAG_DELAYED_CB. If delayed execution
* is selected, a given callback may not be run if the kernel is
* unable to allocate memory for the delayed execution (highly
* unlikely).
*/
int vmci_doorbell_create(struct vmci_handle *handle,
u32 flags,
u32 priv_flags,
vmci_callback notify_cb, void *client_data)
{
struct dbell_entry *entry;
struct vmci_handle new_handle;
int result;
if (!handle || !notify_cb || flags & ~VMCI_FLAG_DELAYED_CB ||
priv_flags & ~VMCI_PRIVILEGE_ALL_FLAGS)
return VMCI_ERROR_INVALID_ARGS;
entry = kmalloc(sizeof(*entry), GFP_KERNEL);
if (entry == NULL) {
pr_warn("Failed allocating memory for datagram entry\n");
return VMCI_ERROR_NO_MEM;
}
if (vmci_handle_is_invalid(*handle)) {
u32 context_id = vmci_get_context_id();
/* Let resource code allocate a free ID for us */
new_handle = vmci_make_handle(context_id, VMCI_INVALID_ID);
} else {
bool valid_context = false;
/*
* Validate the handle. We must do both of the checks below
* because we can be acting as both a host and a guest at the
* same time. We always allow the host context ID, since the
* host functionality is in practice always there with the
* unified driver.
*/
if (handle->context == VMCI_HOST_CONTEXT_ID ||
(vmci_guest_code_active() &&
vmci_get_context_id() == handle->context)) {
valid_context = true;
}
if (!valid_context || handle->resource == VMCI_INVALID_ID) {
pr_devel("Invalid argument (handle=0x%x:0x%x)\n",
handle->context, handle->resource);
result = VMCI_ERROR_INVALID_ARGS;
goto free_mem;
}
new_handle = *handle;
}
entry->idx = 0;
INIT_HLIST_NODE(&entry->node);
entry->priv_flags = priv_flags;
INIT_WORK(&entry->work, dbell_delayed_dispatch);
entry->run_delayed = flags & VMCI_FLAG_DELAYED_CB;
entry->notify_cb = notify_cb;
entry->client_data = client_data;
atomic_set(&entry->active, 0);
result = vmci_resource_add(&entry->resource,
VMCI_RESOURCE_TYPE_DOORBELL,
new_handle);
if (result != VMCI_SUCCESS) {
pr_warn("Failed to add new resource (handle=0x%x:0x%x), error: %d\n",
new_handle.context, new_handle.resource, result);
goto free_mem;
}
new_handle = vmci_resource_handle(&entry->resource);
if (vmci_guest_code_active()) {
dbell_index_table_add(entry);
result = dbell_link(new_handle, entry->idx);
if (VMCI_SUCCESS != result)
goto destroy_resource;
atomic_set(&entry->active, 1);
}
*handle = new_handle;
return result;
destroy_resource:
dbell_index_table_remove(entry);
vmci_resource_remove(&entry->resource);
free_mem:
kfree(entry);
return result;
}
EXPORT_SYMBOL_GPL(vmci_doorbell_create);
/*
* vmci_doorbell_destroy() - Destroy a doorbell.
* @handle: The handle tracking the resource.
*
* Destroys a doorbell previously created with vmcii_doorbell_create. This
* operation may block waiting for a callback to finish.
*/
int vmci_doorbell_destroy(struct vmci_handle handle)
{
struct dbell_entry *entry;
struct vmci_resource *resource;
if (vmci_handle_is_invalid(handle))
return VMCI_ERROR_INVALID_ARGS;
resource = vmci_resource_by_handle(handle,
VMCI_RESOURCE_TYPE_DOORBELL);
if (!resource) {
pr_devel("Failed to destroy doorbell (handle=0x%x:0x%x)\n",
handle.context, handle.resource);
return VMCI_ERROR_NOT_FOUND;
}
entry = container_of(resource, struct dbell_entry, resource);
if (vmci_guest_code_active()) {
int result;
dbell_index_table_remove(entry);
result = dbell_unlink(handle);
if (VMCI_SUCCESS != result) {
/*
* The only reason this should fail would be
* an inconsistency between guest and
* hypervisor state, where the guest believes
* it has an active registration whereas the
* hypervisor doesn't. One case where this may
* happen is if a doorbell is unregistered
* following a hibernation at a time where the
* doorbell state hasn't been restored on the
* hypervisor side yet. Since the handle has
* now been removed in the guest, we just
* print a warning and return success.
*/
pr_devel("Unlink of doorbell (handle=0x%x:0x%x) unknown by hypervisor (error=%d)\n",
handle.context, handle.resource, result);
}
}
/*
* Now remove the resource from the table. It might still be in use
* after this, in a callback or still on the delayed work queue.
*/
vmci_resource_put(&entry->resource);
vmci_resource_remove(&entry->resource);
kfree(entry);
return VMCI_SUCCESS;
}
EXPORT_SYMBOL_GPL(vmci_doorbell_destroy);
/*
* vmci_doorbell_notify() - Ring the doorbell (and hide in the bushes).
* @dst: The handlle identifying the doorbell resource
* @priv_flags: Priviledge flags.
*
* Generates a notification on the doorbell identified by the
* handle. For host side generation of notifications, the caller
* can specify what the privilege of the calling side is.
*/
int vmci_doorbell_notify(struct vmci_handle dst, u32 priv_flags)
{
int retval;
enum vmci_route route;
struct vmci_handle src;
if (vmci_handle_is_invalid(dst) ||
(priv_flags & ~VMCI_PRIVILEGE_ALL_FLAGS))
return VMCI_ERROR_INVALID_ARGS;
src = VMCI_INVALID_HANDLE;
retval = vmci_route(&src, &dst, false, &route);
if (retval < VMCI_SUCCESS)
return retval;
if (VMCI_ROUTE_AS_HOST == route)
return vmci_ctx_notify_dbell(VMCI_HOST_CONTEXT_ID,
dst, priv_flags);
if (VMCI_ROUTE_AS_GUEST == route)
return dbell_notify_as_guest(dst, priv_flags);
pr_warn("Unknown route (%d) for doorbell\n", route);
return VMCI_ERROR_DST_UNREACHABLE;
}
EXPORT_SYMBOL_GPL(vmci_doorbell_notify);

View file

@ -0,0 +1,51 @@
/*
* 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.
*/
#ifndef VMCI_DOORBELL_H
#define VMCI_DOORBELL_H
#include <linux/vmw_vmci_defs.h>
#include <linux/types.h>
#include "vmci_driver.h"
/*
* VMCINotifyResourceInfo: Used to create and destroy doorbells, and
* generate a notification for a doorbell or queue pair.
*/
struct vmci_dbell_notify_resource_info {
struct vmci_handle handle;
u16 resource;
u16 action;
s32 result;
};
/*
* Structure used for checkpointing the doorbell mappings. It is
* written to the checkpoint as is, so changing this structure will
* break checkpoint compatibility.
*/
struct dbell_cpt_state {
struct vmci_handle handle;
u64 bitmap_idx;
};
int vmci_dbell_host_context_notify(u32 src_cid, struct vmci_handle handle);
int vmci_dbell_get_priv_flags(struct vmci_handle handle, u32 *priv_flags);
bool vmci_dbell_register_notification_bitmap(u32 bitmap_ppn);
void vmci_dbell_scan_notification_entries(u8 *bitmap);
#endif /* VMCI_DOORBELL_H */

View file

@ -0,0 +1,117 @@
/*
* 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/atomic.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include "vmci_driver.h"
#include "vmci_event.h"
static bool vmci_disable_host;
module_param_named(disable_host, vmci_disable_host, bool, 0);
MODULE_PARM_DESC(disable_host,
"Disable driver host personality (default=enabled)");
static bool vmci_disable_guest;
module_param_named(disable_guest, vmci_disable_guest, bool, 0);
MODULE_PARM_DESC(disable_guest,
"Disable driver guest personality (default=enabled)");
static bool vmci_guest_personality_initialized;
static bool vmci_host_personality_initialized;
/*
* vmci_get_context_id() - Gets the current context ID.
*
* Returns the current context ID. Note that since this is accessed only
* from code running in the host, this always returns the host context ID.
*/
u32 vmci_get_context_id(void)
{
if (vmci_guest_code_active())
return vmci_get_vm_context_id();
else if (vmci_host_code_active())
return VMCI_HOST_CONTEXT_ID;
return VMCI_INVALID_ID;
}
EXPORT_SYMBOL_GPL(vmci_get_context_id);
static int __init vmci_drv_init(void)
{
int vmci_err;
int error;
vmci_err = vmci_event_init();
if (vmci_err < VMCI_SUCCESS) {
pr_err("Failed to initialize VMCIEvent (result=%d)\n",
vmci_err);
return -EINVAL;
}
if (!vmci_disable_guest) {
error = vmci_guest_init();
if (error) {
pr_warn("Failed to initialize guest personality (err=%d)\n",
error);
} else {
vmci_guest_personality_initialized = true;
pr_info("Guest personality initialized and is %s\n",
vmci_guest_code_active() ?
"active" : "inactive");
}
}
if (!vmci_disable_host) {
error = vmci_host_init();
if (error) {
pr_warn("Unable to initialize host personality (err=%d)\n",
error);
} else {
vmci_host_personality_initialized = true;
pr_info("Initialized host personality\n");
}
}
if (!vmci_guest_personality_initialized &&
!vmci_host_personality_initialized) {
vmci_event_exit();
return -ENODEV;
}
return 0;
}
module_init(vmci_drv_init);
static void __exit vmci_drv_exit(void)
{
if (vmci_guest_personality_initialized)
vmci_guest_exit();
if (vmci_host_personality_initialized)
vmci_host_exit();
vmci_event_exit();
}
module_exit(vmci_drv_exit);
MODULE_AUTHOR("VMware, Inc.");
MODULE_DESCRIPTION("VMware Virtual Machine Communication Interface.");
MODULE_VERSION("1.1.0.0-k");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,57 @@
/*
* 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.
*/
#ifndef _VMCI_DRIVER_H_
#define _VMCI_DRIVER_H_
#include <linux/vmw_vmci_defs.h>
#include <linux/wait.h>
#include "vmci_queue_pair.h"
#include "vmci_context.h"
enum vmci_obj_type {
VMCIOBJ_VMX_VM = 10,
VMCIOBJ_CONTEXT,
VMCIOBJ_SOCKET,
VMCIOBJ_NOT_SET,
};
/* For storing VMCI structures in file handles. */
struct vmci_obj {
void *ptr;
enum vmci_obj_type type;
};
/*
* Needed by other components of this module. It's okay to have one global
* instance of this because there can only ever be one VMCI device. Our
* virtual hardware enforces this.
*/
extern struct pci_dev *vmci_pdev;
u32 vmci_get_context_id(void);
int vmci_send_datagram(struct vmci_datagram *dg);
int vmci_host_init(void);
void vmci_host_exit(void);
bool vmci_host_code_active(void);
int vmci_guest_init(void);
void vmci_guest_exit(void);
bool vmci_guest_code_active(void);
u32 vmci_get_vm_context_id(void);
#endif /* _VMCI_DRIVER_H_ */

View file

@ -0,0 +1,224 @@
/*
* 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/list.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include "vmci_driver.h"
#include "vmci_event.h"
#define EVENT_MAGIC 0xEABE0000
#define VMCI_EVENT_MAX_ATTEMPTS 10
struct vmci_subscription {
u32 id;
u32 event;
vmci_event_cb callback;
void *callback_data;
struct list_head node; /* on one of subscriber lists */
};
static struct list_head subscriber_array[VMCI_EVENT_MAX];
static DEFINE_MUTEX(subscriber_mutex);
int __init vmci_event_init(void)
{
int i;
for (i = 0; i < VMCI_EVENT_MAX; i++)
INIT_LIST_HEAD(&subscriber_array[i]);
return VMCI_SUCCESS;
}
void vmci_event_exit(void)
{
int e;
/* We free all memory at exit. */
for (e = 0; e < VMCI_EVENT_MAX; e++) {
struct vmci_subscription *cur, *p2;
list_for_each_entry_safe(cur, p2, &subscriber_array[e], node) {
/*
* We should never get here because all events
* should have been unregistered before we try
* to unload the driver module.
*/
pr_warn("Unexpected free events occurring\n");
list_del(&cur->node);
kfree(cur);
}
}
}
/*
* Find entry. Assumes subscriber_mutex is held.
*/
static struct vmci_subscription *event_find(u32 sub_id)
{
int e;
for (e = 0; e < VMCI_EVENT_MAX; e++) {
struct vmci_subscription *cur;
list_for_each_entry(cur, &subscriber_array[e], node) {
if (cur->id == sub_id)
return cur;
}
}
return NULL;
}
/*
* Actually delivers the events to the subscribers.
* The callback function for each subscriber is invoked.
*/
static void event_deliver(struct vmci_event_msg *event_msg)
{
struct vmci_subscription *cur;
struct list_head *subscriber_list;
rcu_read_lock();
subscriber_list = &subscriber_array[event_msg->event_data.event];
list_for_each_entry_rcu(cur, subscriber_list, node) {
cur->callback(cur->id, &event_msg->event_data,
cur->callback_data);
}
rcu_read_unlock();
}
/*
* Dispatcher for the VMCI_EVENT_RECEIVE datagrams. Calls all
* subscribers for given event.
*/
int vmci_event_dispatch(struct vmci_datagram *msg)
{
struct vmci_event_msg *event_msg = (struct vmci_event_msg *)msg;
if (msg->payload_size < sizeof(u32) ||
msg->payload_size > sizeof(struct vmci_event_data_max))
return VMCI_ERROR_INVALID_ARGS;
if (!VMCI_EVENT_VALID(event_msg->event_data.event))
return VMCI_ERROR_EVENT_UNKNOWN;
event_deliver(event_msg);
return VMCI_SUCCESS;
}
/*
* vmci_event_subscribe() - Subscribe to a given event.
* @event: The event to subscribe to.
* @callback: The callback to invoke upon the event.
* @callback_data: Data to pass to the callback.
* @subscription_id: ID used to track subscription. Used with
* vmci_event_unsubscribe()
*
* Subscribes to the provided event. The callback specified will be
* fired from RCU critical section and therefore must not sleep.
*/
int vmci_event_subscribe(u32 event,
vmci_event_cb callback,
void *callback_data,
u32 *new_subscription_id)
{
struct vmci_subscription *sub;
int attempts;
int retval;
bool have_new_id = false;
if (!new_subscription_id) {
pr_devel("%s: Invalid subscription (NULL)\n", __func__);
return VMCI_ERROR_INVALID_ARGS;
}
if (!VMCI_EVENT_VALID(event) || !callback) {
pr_devel("%s: Failed to subscribe to event (type=%d) (callback=%p) (data=%p)\n",
__func__, event, callback, callback_data);
return VMCI_ERROR_INVALID_ARGS;
}
sub = kzalloc(sizeof(*sub), GFP_KERNEL);
if (!sub)
return VMCI_ERROR_NO_MEM;
sub->id = VMCI_EVENT_MAX;
sub->event = event;
sub->callback = callback;
sub->callback_data = callback_data;
INIT_LIST_HEAD(&sub->node);
mutex_lock(&subscriber_mutex);
/* Creation of a new event is always allowed. */
for (attempts = 0; attempts < VMCI_EVENT_MAX_ATTEMPTS; attempts++) {
static u32 subscription_id;
/*
* We try to get an id a couple of time before
* claiming we are out of resources.
*/
/* Test for duplicate id. */
if (!event_find(++subscription_id)) {
sub->id = subscription_id;
have_new_id = true;
break;
}
}
if (have_new_id) {
list_add_rcu(&sub->node, &subscriber_array[event]);
retval = VMCI_SUCCESS;
} else {
retval = VMCI_ERROR_NO_RESOURCES;
}
mutex_unlock(&subscriber_mutex);
*new_subscription_id = sub->id;
return retval;
}
EXPORT_SYMBOL_GPL(vmci_event_subscribe);
/*
* vmci_event_unsubscribe() - unsubscribe from an event.
* @sub_id: A subscription ID as provided by vmci_event_subscribe()
*
* Unsubscribe from given event. Removes it from list and frees it.
* Will return callback_data if requested by caller.
*/
int vmci_event_unsubscribe(u32 sub_id)
{
struct vmci_subscription *s;
mutex_lock(&subscriber_mutex);
s = event_find(sub_id);
if (s)
list_del_rcu(&s->node);
mutex_unlock(&subscriber_mutex);
if (!s)
return VMCI_ERROR_NOT_FOUND;
synchronize_rcu();
kfree(s);
return VMCI_SUCCESS;
}
EXPORT_SYMBOL_GPL(vmci_event_unsubscribe);

View file

@ -0,0 +1,25 @@
/*
* 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.
*/
#ifndef __VMCI_EVENT_H__
#define __VMCI_EVENT_H__
#include <linux/vmw_vmci_api.h>
int vmci_event_init(void);
void vmci_event_exit(void);
int vmci_event_dispatch(struct vmci_datagram *msg);
#endif /*__VMCI_EVENT_H__ */

View file

@ -0,0 +1,771 @@
/*
* 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/interrupt.h>
#include <linux/highmem.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/smp.h>
#include <linux/io.h>
#include <linux/vmalloc.h>
#include "vmci_datagram.h"
#include "vmci_doorbell.h"
#include "vmci_context.h"
#include "vmci_driver.h"
#include "vmci_event.h"
#define PCI_DEVICE_ID_VMWARE_VMCI 0x0740
#define VMCI_UTIL_NUM_RESOURCES 1
static bool vmci_disable_msi;
module_param_named(disable_msi, vmci_disable_msi, bool, 0);
MODULE_PARM_DESC(disable_msi, "Disable MSI use in driver - (default=0)");
static bool vmci_disable_msix;
module_param_named(disable_msix, vmci_disable_msix, bool, 0);
MODULE_PARM_DESC(disable_msix, "Disable MSI-X use in driver - (default=0)");
static u32 ctx_update_sub_id = VMCI_INVALID_ID;
static u32 vm_context_id = VMCI_INVALID_ID;
struct vmci_guest_device {
struct device *dev; /* PCI device we are attached to */
void __iomem *iobase;
unsigned int irq;
unsigned int intr_type;
bool exclusive_vectors;
struct msix_entry msix_entries[VMCI_MAX_INTRS];
struct tasklet_struct datagram_tasklet;
struct tasklet_struct bm_tasklet;
void *data_buffer;
void *notification_bitmap;
dma_addr_t notification_base;
};
/* vmci_dev singleton device and supporting data*/
struct pci_dev *vmci_pdev;
static struct vmci_guest_device *vmci_dev_g;
static DEFINE_SPINLOCK(vmci_dev_spinlock);
static atomic_t vmci_num_guest_devices = ATOMIC_INIT(0);
bool vmci_guest_code_active(void)
{
return atomic_read(&vmci_num_guest_devices) != 0;
}
u32 vmci_get_vm_context_id(void)
{
if (vm_context_id == VMCI_INVALID_ID) {
struct vmci_datagram get_cid_msg;
get_cid_msg.dst =
vmci_make_handle(VMCI_HYPERVISOR_CONTEXT_ID,
VMCI_GET_CONTEXT_ID);
get_cid_msg.src = VMCI_ANON_SRC_HANDLE;
get_cid_msg.payload_size = 0;
vm_context_id = vmci_send_datagram(&get_cid_msg);
}
return vm_context_id;
}
/*
* VM to hypervisor call mechanism. We use the standard VMware naming
* convention since shared code is calling this function as well.
*/
int vmci_send_datagram(struct vmci_datagram *dg)
{
unsigned long flags;
int result;
/* Check args. */
if (dg == NULL)
return VMCI_ERROR_INVALID_ARGS;
/*
* Need to acquire spinlock on the device because the datagram
* data may be spread over multiple pages and the monitor may
* interleave device user rpc calls from multiple
* VCPUs. Acquiring the spinlock precludes that
* possibility. Disabling interrupts to avoid incoming
* datagrams during a "rep out" and possibly landing up in
* this function.
*/
spin_lock_irqsave(&vmci_dev_spinlock, flags);
if (vmci_dev_g) {
iowrite8_rep(vmci_dev_g->iobase + VMCI_DATA_OUT_ADDR,
dg, VMCI_DG_SIZE(dg));
result = ioread32(vmci_dev_g->iobase + VMCI_RESULT_LOW_ADDR);
} else {
result = VMCI_ERROR_UNAVAILABLE;
}
spin_unlock_irqrestore(&vmci_dev_spinlock, flags);
return result;
}
EXPORT_SYMBOL_GPL(vmci_send_datagram);
/*
* Gets called with the new context id if updated or resumed.
* Context id.
*/
static void vmci_guest_cid_update(u32 sub_id,
const struct vmci_event_data *event_data,
void *client_data)
{
const struct vmci_event_payld_ctx *ev_payload =
vmci_event_data_const_payload(event_data);
if (sub_id != ctx_update_sub_id) {
pr_devel("Invalid subscriber (ID=0x%x)\n", sub_id);
return;
}
if (!event_data || ev_payload->context_id == VMCI_INVALID_ID) {
pr_devel("Invalid event data\n");
return;
}
pr_devel("Updating context from (ID=0x%x) to (ID=0x%x) on event (type=%d)\n",
vm_context_id, ev_payload->context_id, event_data->event);
vm_context_id = ev_payload->context_id;
}
/*
* Verify that the host supports the hypercalls we need. If it does not,
* try to find fallback hypercalls and use those instead. Returns
* true if required hypercalls (or fallback hypercalls) are
* supported by the host, false otherwise.
*/
static int vmci_check_host_caps(struct pci_dev *pdev)
{
bool result;
struct vmci_resource_query_msg *msg;
u32 msg_size = sizeof(struct vmci_resource_query_hdr) +
VMCI_UTIL_NUM_RESOURCES * sizeof(u32);
struct vmci_datagram *check_msg;
check_msg = kmalloc(msg_size, GFP_KERNEL);
if (!check_msg) {
dev_err(&pdev->dev, "%s: Insufficient memory\n", __func__);
return -ENOMEM;
}
check_msg->dst = vmci_make_handle(VMCI_HYPERVISOR_CONTEXT_ID,
VMCI_RESOURCES_QUERY);
check_msg->src = VMCI_ANON_SRC_HANDLE;
check_msg->payload_size = msg_size - VMCI_DG_HEADERSIZE;
msg = (struct vmci_resource_query_msg *)VMCI_DG_PAYLOAD(check_msg);
msg->num_resources = VMCI_UTIL_NUM_RESOURCES;
msg->resources[0] = VMCI_GET_CONTEXT_ID;
/* Checks that hyper calls are supported */
result = vmci_send_datagram(check_msg) == 0x01;
kfree(check_msg);
dev_dbg(&pdev->dev, "%s: Host capability check: %s\n",
__func__, result ? "PASSED" : "FAILED");
/* We need the vector. There are no fallbacks. */
return result ? 0 : -ENXIO;
}
/*
* Reads datagrams from the data in port and dispatches them. We
* always start reading datagrams into only the first page of the
* datagram buffer. If the datagrams don't fit into one page, we
* use the maximum datagram buffer size for the remainder of the
* invocation. This is a simple heuristic for not penalizing
* small datagrams.
*
* This function assumes that it has exclusive access to the data
* in port for the duration of the call.
*/
static void vmci_dispatch_dgs(unsigned long data)
{
struct vmci_guest_device *vmci_dev = (struct vmci_guest_device *)data;
u8 *dg_in_buffer = vmci_dev->data_buffer;
struct vmci_datagram *dg;
size_t dg_in_buffer_size = VMCI_MAX_DG_SIZE;
size_t current_dg_in_buffer_size = PAGE_SIZE;
size_t remaining_bytes;
BUILD_BUG_ON(VMCI_MAX_DG_SIZE < PAGE_SIZE);
ioread8_rep(vmci_dev->iobase + VMCI_DATA_IN_ADDR,
vmci_dev->data_buffer, current_dg_in_buffer_size);
dg = (struct vmci_datagram *)dg_in_buffer;
remaining_bytes = current_dg_in_buffer_size;
while (dg->dst.resource != VMCI_INVALID_ID ||
remaining_bytes > PAGE_SIZE) {
unsigned dg_in_size;
/*
* When the input buffer spans multiple pages, a datagram can
* start on any page boundary in the buffer.
*/
if (dg->dst.resource == VMCI_INVALID_ID) {
dg = (struct vmci_datagram *)roundup(
(uintptr_t)dg + 1, PAGE_SIZE);
remaining_bytes =
(size_t)(dg_in_buffer +
current_dg_in_buffer_size -
(u8 *)dg);
continue;
}
dg_in_size = VMCI_DG_SIZE_ALIGNED(dg);
if (dg_in_size <= dg_in_buffer_size) {
int result;
/*
* If the remaining bytes in the datagram
* buffer doesn't contain the complete
* datagram, we first make sure we have enough
* room for it and then we read the reminder
* of the datagram and possibly any following
* datagrams.
*/
if (dg_in_size > remaining_bytes) {
if (remaining_bytes !=
current_dg_in_buffer_size) {
/*
* We move the partial
* datagram to the front and
* read the reminder of the
* datagram and possibly
* following calls into the
* following bytes.
*/
memmove(dg_in_buffer, dg_in_buffer +
current_dg_in_buffer_size -
remaining_bytes,
remaining_bytes);
dg = (struct vmci_datagram *)
dg_in_buffer;
}
if (current_dg_in_buffer_size !=
dg_in_buffer_size)
current_dg_in_buffer_size =
dg_in_buffer_size;
ioread8_rep(vmci_dev->iobase +
VMCI_DATA_IN_ADDR,
vmci_dev->data_buffer +
remaining_bytes,
current_dg_in_buffer_size -
remaining_bytes);
}
/*
* We special case event datagrams from the
* hypervisor.
*/
if (dg->src.context == VMCI_HYPERVISOR_CONTEXT_ID &&
dg->dst.resource == VMCI_EVENT_HANDLER) {
result = vmci_event_dispatch(dg);
} else {
result = vmci_datagram_invoke_guest_handler(dg);
}
if (result < VMCI_SUCCESS)
dev_dbg(vmci_dev->dev,
"Datagram with resource (ID=0x%x) failed (err=%d)\n",
dg->dst.resource, result);
/* On to the next datagram. */
dg = (struct vmci_datagram *)((u8 *)dg +
dg_in_size);
} else {
size_t bytes_to_skip;
/*
* Datagram doesn't fit in datagram buffer of maximal
* size. We drop it.
*/
dev_dbg(vmci_dev->dev,
"Failed to receive datagram (size=%u bytes)\n",
dg_in_size);
bytes_to_skip = dg_in_size - remaining_bytes;
if (current_dg_in_buffer_size != dg_in_buffer_size)
current_dg_in_buffer_size = dg_in_buffer_size;
for (;;) {
ioread8_rep(vmci_dev->iobase +
VMCI_DATA_IN_ADDR,
vmci_dev->data_buffer,
current_dg_in_buffer_size);
if (bytes_to_skip <= current_dg_in_buffer_size)
break;
bytes_to_skip -= current_dg_in_buffer_size;
}
dg = (struct vmci_datagram *)(dg_in_buffer +
bytes_to_skip);
}
remaining_bytes =
(size_t) (dg_in_buffer + current_dg_in_buffer_size -
(u8 *)dg);
if (remaining_bytes < VMCI_DG_HEADERSIZE) {
/* Get the next batch of datagrams. */
ioread8_rep(vmci_dev->iobase + VMCI_DATA_IN_ADDR,
vmci_dev->data_buffer,
current_dg_in_buffer_size);
dg = (struct vmci_datagram *)dg_in_buffer;
remaining_bytes = current_dg_in_buffer_size;
}
}
}
/*
* Scans the notification bitmap for raised flags, clears them
* and handles the notifications.
*/
static void vmci_process_bitmap(unsigned long data)
{
struct vmci_guest_device *dev = (struct vmci_guest_device *)data;
if (!dev->notification_bitmap) {
dev_dbg(dev->dev, "No bitmap present in %s\n", __func__);
return;
}
vmci_dbell_scan_notification_entries(dev->notification_bitmap);
}
/*
* Enable MSI-X. Try exclusive vectors first, then shared vectors.
*/
static int vmci_enable_msix(struct pci_dev *pdev,
struct vmci_guest_device *vmci_dev)
{
int i;
int result;
for (i = 0; i < VMCI_MAX_INTRS; ++i) {
vmci_dev->msix_entries[i].entry = i;
vmci_dev->msix_entries[i].vector = i;
}
result = pci_enable_msix_exact(pdev,
vmci_dev->msix_entries, VMCI_MAX_INTRS);
if (result == 0)
vmci_dev->exclusive_vectors = true;
else if (result == -ENOSPC)
result = pci_enable_msix_exact(pdev, vmci_dev->msix_entries, 1);
return result;
}
/*
* Interrupt handler for legacy or MSI interrupt, or for first MSI-X
* interrupt (vector VMCI_INTR_DATAGRAM).
*/
static irqreturn_t vmci_interrupt(int irq, void *_dev)
{
struct vmci_guest_device *dev = _dev;
/*
* If we are using MSI-X with exclusive vectors then we simply schedule
* the datagram tasklet, since we know the interrupt was meant for us.
* Otherwise we must read the ICR to determine what to do.
*/
if (dev->intr_type == VMCI_INTR_TYPE_MSIX && dev->exclusive_vectors) {
tasklet_schedule(&dev->datagram_tasklet);
} else {
unsigned int icr;
/* Acknowledge interrupt and determine what needs doing. */
icr = ioread32(dev->iobase + VMCI_ICR_ADDR);
if (icr == 0 || icr == ~0)
return IRQ_NONE;
if (icr & VMCI_ICR_DATAGRAM) {
tasklet_schedule(&dev->datagram_tasklet);
icr &= ~VMCI_ICR_DATAGRAM;
}
if (icr & VMCI_ICR_NOTIFICATION) {
tasklet_schedule(&dev->bm_tasklet);
icr &= ~VMCI_ICR_NOTIFICATION;
}
if (icr != 0)
dev_warn(dev->dev,
"Ignoring unknown interrupt cause (%d)\n",
icr);
}
return IRQ_HANDLED;
}
/*
* Interrupt handler for MSI-X interrupt vector VMCI_INTR_NOTIFICATION,
* which is for the notification bitmap. Will only get called if we are
* using MSI-X with exclusive vectors.
*/
static irqreturn_t vmci_interrupt_bm(int irq, void *_dev)
{
struct vmci_guest_device *dev = _dev;
/* For MSI-X we can just assume it was meant for us. */
tasklet_schedule(&dev->bm_tasklet);
return IRQ_HANDLED;
}
/*
* Most of the initialization at module load time is done here.
*/
static int vmci_guest_probe_device(struct pci_dev *pdev,
const struct pci_device_id *id)
{
struct vmci_guest_device *vmci_dev;
void __iomem *iobase;
unsigned int capabilities;
unsigned long cmd;
int vmci_err;
int error;
dev_dbg(&pdev->dev, "Probing for vmci/PCI guest device\n");
error = pcim_enable_device(pdev);
if (error) {
dev_err(&pdev->dev,
"Failed to enable VMCI device: %d\n", error);
return error;
}
error = pcim_iomap_regions(pdev, 1 << 0, KBUILD_MODNAME);
if (error) {
dev_err(&pdev->dev, "Failed to reserve/map IO regions\n");
return error;
}
iobase = pcim_iomap_table(pdev)[0];
dev_info(&pdev->dev, "Found VMCI PCI device at %#lx, irq %u\n",
(unsigned long)iobase, pdev->irq);
vmci_dev = devm_kzalloc(&pdev->dev, sizeof(*vmci_dev), GFP_KERNEL);
if (!vmci_dev) {
dev_err(&pdev->dev,
"Can't allocate memory for VMCI device\n");
return -ENOMEM;
}
vmci_dev->dev = &pdev->dev;
vmci_dev->intr_type = VMCI_INTR_TYPE_INTX;
vmci_dev->exclusive_vectors = false;
vmci_dev->iobase = iobase;
tasklet_init(&vmci_dev->datagram_tasklet,
vmci_dispatch_dgs, (unsigned long)vmci_dev);
tasklet_init(&vmci_dev->bm_tasklet,
vmci_process_bitmap, (unsigned long)vmci_dev);
vmci_dev->data_buffer = vmalloc(VMCI_MAX_DG_SIZE);
if (!vmci_dev->data_buffer) {
dev_err(&pdev->dev,
"Can't allocate memory for datagram buffer\n");
return -ENOMEM;
}
pci_set_master(pdev); /* To enable queue_pair functionality. */
/*
* Verify that the VMCI Device supports the capabilities that
* we need. If the device is missing capabilities that we would
* like to use, check for fallback capabilities and use those
* instead (so we can run a new VM on old hosts). Fail the load if
* a required capability is missing and there is no fallback.
*
* Right now, we need datagrams. There are no fallbacks.
*/
capabilities = ioread32(vmci_dev->iobase + VMCI_CAPS_ADDR);
if (!(capabilities & VMCI_CAPS_DATAGRAM)) {
dev_err(&pdev->dev, "Device does not support datagrams\n");
error = -ENXIO;
goto err_free_data_buffer;
}
/*
* If the hardware supports notifications, we will use that as
* well.
*/
if (capabilities & VMCI_CAPS_NOTIFICATIONS) {
vmci_dev->notification_bitmap = dma_alloc_coherent(
&pdev->dev, PAGE_SIZE, &vmci_dev->notification_base,
GFP_KERNEL);
if (!vmci_dev->notification_bitmap) {
dev_warn(&pdev->dev,
"Unable to allocate notification bitmap\n");
} else {
memset(vmci_dev->notification_bitmap, 0, PAGE_SIZE);
capabilities |= VMCI_CAPS_NOTIFICATIONS;
}
}
dev_info(&pdev->dev, "Using capabilities 0x%x\n", capabilities);
/* Let the host know which capabilities we intend to use. */
iowrite32(capabilities, vmci_dev->iobase + VMCI_CAPS_ADDR);
/* Set up global device so that we can start sending datagrams */
spin_lock_irq(&vmci_dev_spinlock);
vmci_dev_g = vmci_dev;
vmci_pdev = pdev;
spin_unlock_irq(&vmci_dev_spinlock);
/*
* Register notification bitmap with device if that capability is
* used.
*/
if (capabilities & VMCI_CAPS_NOTIFICATIONS) {
unsigned long bitmap_ppn =
vmci_dev->notification_base >> PAGE_SHIFT;
if (!vmci_dbell_register_notification_bitmap(bitmap_ppn)) {
dev_warn(&pdev->dev,
"VMCI device unable to register notification bitmap with PPN 0x%x\n",
(u32) bitmap_ppn);
error = -ENXIO;
goto err_remove_vmci_dev_g;
}
}
/* Check host capabilities. */
error = vmci_check_host_caps(pdev);
if (error)
goto err_remove_bitmap;
/* Enable device. */
/*
* We subscribe to the VMCI_EVENT_CTX_ID_UPDATE here so we can
* update the internal context id when needed.
*/
vmci_err = vmci_event_subscribe(VMCI_EVENT_CTX_ID_UPDATE,
vmci_guest_cid_update, NULL,
&ctx_update_sub_id);
if (vmci_err < VMCI_SUCCESS)
dev_warn(&pdev->dev,
"Failed to subscribe to event (type=%d): %d\n",
VMCI_EVENT_CTX_ID_UPDATE, vmci_err);
/*
* Enable interrupts. Try MSI-X first, then MSI, and then fallback on
* legacy interrupts.
*/
if (!vmci_disable_msix && !vmci_enable_msix(pdev, vmci_dev)) {
vmci_dev->intr_type = VMCI_INTR_TYPE_MSIX;
vmci_dev->irq = vmci_dev->msix_entries[0].vector;
} else if (!vmci_disable_msi && !pci_enable_msi(pdev)) {
vmci_dev->intr_type = VMCI_INTR_TYPE_MSI;
vmci_dev->irq = pdev->irq;
} else {
vmci_dev->intr_type = VMCI_INTR_TYPE_INTX;
vmci_dev->irq = pdev->irq;
}
/*
* Request IRQ for legacy or MSI interrupts, or for first
* MSI-X vector.
*/
error = request_irq(vmci_dev->irq, vmci_interrupt, IRQF_SHARED,
KBUILD_MODNAME, vmci_dev);
if (error) {
dev_err(&pdev->dev, "Irq %u in use: %d\n",
vmci_dev->irq, error);
goto err_disable_msi;
}
/*
* For MSI-X with exclusive vectors we need to request an
* interrupt for each vector so that we get a separate
* interrupt handler routine. This allows us to distinguish
* between the vectors.
*/
if (vmci_dev->exclusive_vectors) {
error = request_irq(vmci_dev->msix_entries[1].vector,
vmci_interrupt_bm, 0, KBUILD_MODNAME,
vmci_dev);
if (error) {
dev_err(&pdev->dev,
"Failed to allocate irq %u: %d\n",
vmci_dev->msix_entries[1].vector, error);
goto err_free_irq;
}
}
dev_dbg(&pdev->dev, "Registered device\n");
atomic_inc(&vmci_num_guest_devices);
/* Enable specific interrupt bits. */
cmd = VMCI_IMR_DATAGRAM;
if (capabilities & VMCI_CAPS_NOTIFICATIONS)
cmd |= VMCI_IMR_NOTIFICATION;
iowrite32(cmd, vmci_dev->iobase + VMCI_IMR_ADDR);
/* Enable interrupts. */
iowrite32(VMCI_CONTROL_INT_ENABLE,
vmci_dev->iobase + VMCI_CONTROL_ADDR);
pci_set_drvdata(pdev, vmci_dev);
return 0;
err_free_irq:
free_irq(vmci_dev->irq, vmci_dev);
tasklet_kill(&vmci_dev->datagram_tasklet);
tasklet_kill(&vmci_dev->bm_tasklet);
err_disable_msi:
if (vmci_dev->intr_type == VMCI_INTR_TYPE_MSIX)
pci_disable_msix(pdev);
else if (vmci_dev->intr_type == VMCI_INTR_TYPE_MSI)
pci_disable_msi(pdev);
vmci_err = vmci_event_unsubscribe(ctx_update_sub_id);
if (vmci_err < VMCI_SUCCESS)
dev_warn(&pdev->dev,
"Failed to unsubscribe from event (type=%d) with subscriber (ID=0x%x): %d\n",
VMCI_EVENT_CTX_ID_UPDATE, ctx_update_sub_id, vmci_err);
err_remove_bitmap:
if (vmci_dev->notification_bitmap) {
iowrite32(VMCI_CONTROL_RESET,
vmci_dev->iobase + VMCI_CONTROL_ADDR);
dma_free_coherent(&pdev->dev, PAGE_SIZE,
vmci_dev->notification_bitmap,
vmci_dev->notification_base);
}
err_remove_vmci_dev_g:
spin_lock_irq(&vmci_dev_spinlock);
vmci_pdev = NULL;
vmci_dev_g = NULL;
spin_unlock_irq(&vmci_dev_spinlock);
err_free_data_buffer:
vfree(vmci_dev->data_buffer);
/* The rest are managed resources and will be freed by PCI core */
return error;
}
static void vmci_guest_remove_device(struct pci_dev *pdev)
{
struct vmci_guest_device *vmci_dev = pci_get_drvdata(pdev);
int vmci_err;
dev_dbg(&pdev->dev, "Removing device\n");
atomic_dec(&vmci_num_guest_devices);
vmci_qp_guest_endpoints_exit();
vmci_err = vmci_event_unsubscribe(ctx_update_sub_id);
if (vmci_err < VMCI_SUCCESS)
dev_warn(&pdev->dev,
"Failed to unsubscribe from event (type=%d) with subscriber (ID=0x%x): %d\n",
VMCI_EVENT_CTX_ID_UPDATE, ctx_update_sub_id, vmci_err);
spin_lock_irq(&vmci_dev_spinlock);
vmci_dev_g = NULL;
vmci_pdev = NULL;
spin_unlock_irq(&vmci_dev_spinlock);
dev_dbg(&pdev->dev, "Resetting vmci device\n");
iowrite32(VMCI_CONTROL_RESET, vmci_dev->iobase + VMCI_CONTROL_ADDR);
/*
* Free IRQ and then disable MSI/MSI-X as appropriate. For
* MSI-X, we might have multiple vectors, each with their own
* IRQ, which we must free too.
*/
free_irq(vmci_dev->irq, vmci_dev);
if (vmci_dev->intr_type == VMCI_INTR_TYPE_MSIX) {
if (vmci_dev->exclusive_vectors)
free_irq(vmci_dev->msix_entries[1].vector, vmci_dev);
pci_disable_msix(pdev);
} else if (vmci_dev->intr_type == VMCI_INTR_TYPE_MSI) {
pci_disable_msi(pdev);
}
tasklet_kill(&vmci_dev->datagram_tasklet);
tasklet_kill(&vmci_dev->bm_tasklet);
if (vmci_dev->notification_bitmap) {
/*
* The device reset above cleared the bitmap state of the
* device, so we can safely free it here.
*/
dma_free_coherent(&pdev->dev, PAGE_SIZE,
vmci_dev->notification_bitmap,
vmci_dev->notification_base);
}
vfree(vmci_dev->data_buffer);
/* The rest are managed resources and will be freed by PCI core */
}
static const struct pci_device_id vmci_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_VMWARE, PCI_DEVICE_ID_VMWARE_VMCI), },
{ 0 },
};
MODULE_DEVICE_TABLE(pci, vmci_ids);
static struct pci_driver vmci_guest_driver = {
.name = KBUILD_MODNAME,
.id_table = vmci_ids,
.probe = vmci_guest_probe_device,
.remove = vmci_guest_remove_device,
};
int __init vmci_guest_init(void)
{
return pci_register_driver(&vmci_guest_driver);
}
void __exit vmci_guest_exit(void)
{
pci_unregister_driver(&vmci_guest_driver);
}

View file

@ -0,0 +1,142 @@
/*
* 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/slab.h>
#include "vmci_handle_array.h"
static size_t handle_arr_calc_size(size_t capacity)
{
return sizeof(struct vmci_handle_arr) +
capacity * sizeof(struct vmci_handle);
}
struct vmci_handle_arr *vmci_handle_arr_create(size_t capacity)
{
struct vmci_handle_arr *array;
if (capacity == 0)
capacity = VMCI_HANDLE_ARRAY_DEFAULT_SIZE;
array = kmalloc(handle_arr_calc_size(capacity), GFP_ATOMIC);
if (!array)
return NULL;
array->capacity = capacity;
array->size = 0;
return array;
}
void vmci_handle_arr_destroy(struct vmci_handle_arr *array)
{
kfree(array);
}
void vmci_handle_arr_append_entry(struct vmci_handle_arr **array_ptr,
struct vmci_handle handle)
{
struct vmci_handle_arr *array = *array_ptr;
if (unlikely(array->size >= array->capacity)) {
/* reallocate. */
struct vmci_handle_arr *new_array;
size_t new_capacity = array->capacity * VMCI_ARR_CAP_MULT;
size_t new_size = handle_arr_calc_size(new_capacity);
new_array = krealloc(array, new_size, GFP_ATOMIC);
if (!new_array)
return;
new_array->capacity = new_capacity;
*array_ptr = array = new_array;
}
array->entries[array->size] = handle;
array->size++;
}
/*
* Handle that was removed, VMCI_INVALID_HANDLE if entry not found.
*/
struct vmci_handle vmci_handle_arr_remove_entry(struct vmci_handle_arr *array,
struct vmci_handle entry_handle)
{
struct vmci_handle handle = VMCI_INVALID_HANDLE;
size_t i;
for (i = 0; i < array->size; i++) {
if (vmci_handle_is_equal(array->entries[i], entry_handle)) {
handle = array->entries[i];
array->size--;
array->entries[i] = array->entries[array->size];
array->entries[array->size] = VMCI_INVALID_HANDLE;
break;
}
}
return handle;
}
/*
* Handle that was removed, VMCI_INVALID_HANDLE if array was empty.
*/
struct vmci_handle vmci_handle_arr_remove_tail(struct vmci_handle_arr *array)
{
struct vmci_handle handle = VMCI_INVALID_HANDLE;
if (array->size) {
array->size--;
handle = array->entries[array->size];
array->entries[array->size] = VMCI_INVALID_HANDLE;
}
return handle;
}
/*
* Handle at given index, VMCI_INVALID_HANDLE if invalid index.
*/
struct vmci_handle
vmci_handle_arr_get_entry(const struct vmci_handle_arr *array, size_t index)
{
if (unlikely(index >= array->size))
return VMCI_INVALID_HANDLE;
return array->entries[index];
}
bool vmci_handle_arr_has_entry(const struct vmci_handle_arr *array,
struct vmci_handle entry_handle)
{
size_t i;
for (i = 0; i < array->size; i++)
if (vmci_handle_is_equal(array->entries[i], entry_handle))
return true;
return false;
}
/*
* NULL if the array is empty. Otherwise, a pointer to the array
* of VMCI handles in the handle array.
*/
struct vmci_handle *vmci_handle_arr_get_handles(struct vmci_handle_arr *array)
{
if (array->size)
return array->entries;
return NULL;
}

View file

@ -0,0 +1,52 @@
/*
* 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.
*/
#ifndef _VMCI_HANDLE_ARRAY_H_
#define _VMCI_HANDLE_ARRAY_H_
#include <linux/vmw_vmci_defs.h>
#include <linux/types.h>
#define VMCI_HANDLE_ARRAY_DEFAULT_SIZE 4
#define VMCI_ARR_CAP_MULT 2 /* Array capacity multiplier */
struct vmci_handle_arr {
size_t capacity;
size_t size;
struct vmci_handle entries[];
};
struct vmci_handle_arr *vmci_handle_arr_create(size_t capacity);
void vmci_handle_arr_destroy(struct vmci_handle_arr *array);
void vmci_handle_arr_append_entry(struct vmci_handle_arr **array_ptr,
struct vmci_handle handle);
struct vmci_handle vmci_handle_arr_remove_entry(struct vmci_handle_arr *array,
struct vmci_handle
entry_handle);
struct vmci_handle vmci_handle_arr_remove_tail(struct vmci_handle_arr *array);
struct vmci_handle
vmci_handle_arr_get_entry(const struct vmci_handle_arr *array, size_t index);
bool vmci_handle_arr_has_entry(const struct vmci_handle_arr *array,
struct vmci_handle entry_handle);
struct vmci_handle *vmci_handle_arr_get_handles(struct vmci_handle_arr *array);
static inline size_t vmci_handle_arr_get_size(
const struct vmci_handle_arr *array)
{
return array->size;
}
#endif /* _VMCI_HANDLE_ARRAY_H_ */

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,173 @@
/*
* 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.
*/
#ifndef _VMCI_QUEUE_PAIR_H_
#define _VMCI_QUEUE_PAIR_H_
#include <linux/vmw_vmci_defs.h>
#include <linux/types.h>
#include "vmci_context.h"
/* Callback needed for correctly waiting on events. */
typedef int (*vmci_event_release_cb) (void *client_data);
/* Guest device port I/O. */
struct ppn_set {
u64 num_produce_pages;
u64 num_consume_pages;
u32 *produce_ppns;
u32 *consume_ppns;
bool initialized;
};
/* VMCIqueue_pairAllocInfo */
struct vmci_qp_alloc_info {
struct vmci_handle handle;
u32 peer;
u32 flags;
u64 produce_size;
u64 consume_size;
u64 ppn_va; /* Start VA of queue pair PPNs. */
u64 num_ppns;
s32 result;
u32 version;
};
/* VMCIqueue_pairSetVAInfo */
struct vmci_qp_set_va_info {
struct vmci_handle handle;
u64 va; /* Start VA of queue pair PPNs. */
u64 num_ppns;
u32 version;
s32 result;
};
/*
* For backwards compatibility, here is a version of the
* VMCIqueue_pairPageFileInfo before host support end-points was added.
* Note that the current version of that structure requires VMX to
* pass down the VA of the mapped file. Before host support was added
* there was nothing of the sort. So, when the driver sees the ioctl
* with a parameter that is the sizeof
* VMCIqueue_pairPageFileInfo_NoHostQP then it can infer that the version
* of VMX running can't attach to host end points because it doesn't
* provide the VA of the mapped files.
*
* The Linux driver doesn't get an indication of the size of the
* structure passed down from user space. So, to fix a long standing
* but unfiled bug, the _pad field has been renamed to version.
* Existing versions of VMX always initialize the PageFileInfo
* structure so that _pad, er, version is set to 0.
*
* A version value of 1 indicates that the size of the structure has
* been increased to include two UVA's: produce_uva and consume_uva.
* These UVA's are of the mmap()'d queue contents backing files.
*
* In addition, if when VMX is sending down the
* VMCIqueue_pairPageFileInfo structure it gets an error then it will
* try again with the _NoHostQP version of the file to see if an older
* VMCI kernel module is running.
*/
/* VMCIqueue_pairPageFileInfo */
struct vmci_qp_page_file_info {
struct vmci_handle handle;
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 version; /* Was _pad. */
u64 produce_va; /* User VA of the mapped file. */
u64 consume_va; /* User VA of the mapped file. */
};
/* vmci queuepair detach info */
struct vmci_qp_dtch_info {
struct vmci_handle handle;
s32 result;
u32 _pad;
};
/*
* struct vmci_qp_page_store describes how the memory of a given queue pair
* is backed. When the queue pair is between the host and a guest, the
* page store consists of references to the guest pages. On vmkernel,
* this is a list of PPNs, and on hosted, it is a user VA where the
* queue pair is mapped into the VMX address space.
*/
struct vmci_qp_page_store {
/* Reference to pages backing the queue pair. */
u64 pages;
/* Length of pageList/virtual addres range (in pages). */
u32 len;
};
/*
* This data type contains the information about a queue.
* There are two queues (hence, queue pairs) per transaction model between a
* pair of end points, A & B. One queue is used by end point A to transmit
* commands and responses to B. The other queue is used by B to transmit
* commands and responses.
*
* struct vmci_queue_kern_if is a per-OS defined Queue structure. It contains
* either a direct pointer to the linear address of the buffer contents or a
* pointer to structures which help the OS locate those data pages. See
* vmciKernelIf.c for each platform for its definition.
*/
struct vmci_queue {
struct vmci_queue_header *q_header;
struct vmci_queue_header *saved_header;
struct vmci_queue_kern_if *kernel_if;
};
/*
* Utility function that checks whether the fields of the page
* store contain valid values.
* Result:
* true if the page store is wellformed. false otherwise.
*/
static inline bool
VMCI_QP_PAGESTORE_IS_WELLFORMED(struct vmci_qp_page_store *page_store)
{
return page_store->len >= 2;
}
void vmci_qp_broker_exit(void);
int vmci_qp_broker_alloc(struct vmci_handle handle, u32 peer,
u32 flags, u32 priv_flags,
u64 produce_size, u64 consume_size,
struct vmci_qp_page_store *page_store,
struct vmci_ctx *context);
int vmci_qp_broker_set_page_store(struct vmci_handle handle,
u64 produce_uva, u64 consume_uva,
struct vmci_ctx *context);
int vmci_qp_broker_detach(struct vmci_handle handle, struct vmci_ctx *context);
void vmci_qp_guest_endpoints_exit(void);
int vmci_qp_alloc(struct vmci_handle *handle,
struct vmci_queue **produce_q, u64 produce_size,
struct vmci_queue **consume_q, u64 consume_size,
u32 peer, u32 flags, u32 priv_flags,
bool guest_endpoint, vmci_event_release_cb wakeup_cb,
void *client_data);
int vmci_qp_broker_map(struct vmci_handle handle,
struct vmci_ctx *context, u64 guest_mem);
int vmci_qp_broker_unmap(struct vmci_handle handle,
struct vmci_ctx *context, u32 gid);
#endif /* _VMCI_QUEUE_PAIR_H_ */

View file

@ -0,0 +1,227 @@
/*
* 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/hash.h>
#include <linux/types.h>
#include <linux/rculist.h>
#include "vmci_resource.h"
#include "vmci_driver.h"
#define VMCI_RESOURCE_HASH_BITS 7
#define VMCI_RESOURCE_HASH_BUCKETS (1 << VMCI_RESOURCE_HASH_BITS)
struct vmci_hash_table {
spinlock_t lock;
struct hlist_head entries[VMCI_RESOURCE_HASH_BUCKETS];
};
static struct vmci_hash_table vmci_resource_table = {
.lock = __SPIN_LOCK_UNLOCKED(vmci_resource_table.lock),
};
static unsigned int vmci_resource_hash(struct vmci_handle handle)
{
return hash_32(handle.resource, VMCI_RESOURCE_HASH_BITS);
}
/*
* Gets a resource (if one exists) matching given handle from the hash table.
*/
static struct vmci_resource *vmci_resource_lookup(struct vmci_handle handle,
enum vmci_resource_type type)
{
struct vmci_resource *r, *resource = NULL;
unsigned int idx = vmci_resource_hash(handle);
rcu_read_lock();
hlist_for_each_entry_rcu(r,
&vmci_resource_table.entries[idx], node) {
u32 cid = r->handle.context;
u32 rid = r->handle.resource;
if (r->type == type &&
rid == handle.resource &&
(cid == handle.context || cid == VMCI_INVALID_ID)) {
resource = r;
break;
}
}
rcu_read_unlock();
return resource;
}
/*
* Find an unused resource ID and return it. The first
* VMCI_RESERVED_RESOURCE_ID_MAX are reserved so we start from
* its value + 1.
* Returns VMCI resource id on success, VMCI_INVALID_ID on failure.
*/
static u32 vmci_resource_find_id(u32 context_id,
enum vmci_resource_type resource_type)
{
static u32 resource_id = VMCI_RESERVED_RESOURCE_ID_MAX + 1;
u32 old_rid = resource_id;
u32 current_rid;
/*
* Generate a unique resource ID. Keep on trying until we wrap around
* in the RID space.
*/
do {
struct vmci_handle handle;
current_rid = resource_id;
resource_id++;
if (unlikely(resource_id == VMCI_INVALID_ID)) {
/* Skip the reserved rids. */
resource_id = VMCI_RESERVED_RESOURCE_ID_MAX + 1;
}
handle = vmci_make_handle(context_id, current_rid);
if (!vmci_resource_lookup(handle, resource_type))
return current_rid;
} while (resource_id != old_rid);
return VMCI_INVALID_ID;
}
int vmci_resource_add(struct vmci_resource *resource,
enum vmci_resource_type resource_type,
struct vmci_handle handle)
{
unsigned int idx;
int result;
spin_lock(&vmci_resource_table.lock);
if (handle.resource == VMCI_INVALID_ID) {
handle.resource = vmci_resource_find_id(handle.context,
resource_type);
if (handle.resource == VMCI_INVALID_ID) {
result = VMCI_ERROR_NO_HANDLE;
goto out;
}
} else if (vmci_resource_lookup(handle, resource_type)) {
result = VMCI_ERROR_ALREADY_EXISTS;
goto out;
}
resource->handle = handle;
resource->type = resource_type;
INIT_HLIST_NODE(&resource->node);
kref_init(&resource->kref);
init_completion(&resource->done);
idx = vmci_resource_hash(resource->handle);
hlist_add_head_rcu(&resource->node, &vmci_resource_table.entries[idx]);
result = VMCI_SUCCESS;
out:
spin_unlock(&vmci_resource_table.lock);
return result;
}
void vmci_resource_remove(struct vmci_resource *resource)
{
struct vmci_handle handle = resource->handle;
unsigned int idx = vmci_resource_hash(handle);
struct vmci_resource *r;
/* Remove resource from hash table. */
spin_lock(&vmci_resource_table.lock);
hlist_for_each_entry(r, &vmci_resource_table.entries[idx], node) {
if (vmci_handle_is_equal(r->handle, resource->handle)) {
hlist_del_init_rcu(&r->node);
break;
}
}
spin_unlock(&vmci_resource_table.lock);
synchronize_rcu();
vmci_resource_put(resource);
wait_for_completion(&resource->done);
}
struct vmci_resource *
vmci_resource_by_handle(struct vmci_handle resource_handle,
enum vmci_resource_type resource_type)
{
struct vmci_resource *r, *resource = NULL;
rcu_read_lock();
r = vmci_resource_lookup(resource_handle, resource_type);
if (r &&
(resource_type == r->type ||
resource_type == VMCI_RESOURCE_TYPE_ANY)) {
resource = vmci_resource_get(r);
}
rcu_read_unlock();
return resource;
}
/*
* Get a reference to given resource.
*/
struct vmci_resource *vmci_resource_get(struct vmci_resource *resource)
{
kref_get(&resource->kref);
return resource;
}
static void vmci_release_resource(struct kref *kref)
{
struct vmci_resource *resource =
container_of(kref, struct vmci_resource, kref);
/* Verify the resource has been unlinked from hash table */
WARN_ON(!hlist_unhashed(&resource->node));
/* Signal that container of this resource can now be destroyed */
complete(&resource->done);
}
/*
* Resource's release function will get called if last reference.
* If it is the last reference, then we are sure that nobody else
* can increment the count again (it's gone from the resource hash
* table), so there's no need for locking here.
*/
int vmci_resource_put(struct vmci_resource *resource)
{
/*
* We propagate the information back to caller in case it wants to know
* whether entry was freed.
*/
return kref_put(&resource->kref, vmci_release_resource) ?
VMCI_SUCCESS_ENTRY_DEAD : VMCI_SUCCESS;
}
struct vmci_handle vmci_resource_handle(struct vmci_resource *resource)
{
return resource->handle;
}

View file

@ -0,0 +1,59 @@
/*
* 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.
*/
#ifndef _VMCI_RESOURCE_H_
#define _VMCI_RESOURCE_H_
#include <linux/vmw_vmci_defs.h>
#include <linux/types.h>
#include "vmci_context.h"
enum vmci_resource_type {
VMCI_RESOURCE_TYPE_ANY,
VMCI_RESOURCE_TYPE_API,
VMCI_RESOURCE_TYPE_GROUP,
VMCI_RESOURCE_TYPE_DATAGRAM,
VMCI_RESOURCE_TYPE_DOORBELL,
VMCI_RESOURCE_TYPE_QPAIR_GUEST,
VMCI_RESOURCE_TYPE_QPAIR_HOST
};
struct vmci_resource {
struct vmci_handle handle;
enum vmci_resource_type type;
struct hlist_node node;
struct kref kref;
struct completion done;
};
int vmci_resource_add(struct vmci_resource *resource,
enum vmci_resource_type resource_type,
struct vmci_handle handle);
void vmci_resource_remove(struct vmci_resource *resource);
struct vmci_resource *
vmci_resource_by_handle(struct vmci_handle resource_handle,
enum vmci_resource_type resource_type);
struct vmci_resource *vmci_resource_get(struct vmci_resource *resource);
int vmci_resource_put(struct vmci_resource *resource);
struct vmci_handle vmci_resource_handle(struct vmci_resource *resource);
#endif /* _VMCI_RESOURCE_H_ */

View file

@ -0,0 +1,226 @@
/*
* 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 "vmci_context.h"
#include "vmci_driver.h"
#include "vmci_route.h"
/*
* Make a routing decision for the given source and destination handles.
* This will try to determine the route using the handles and the available
* devices. Will set the source context if it is invalid.
*/
int vmci_route(struct vmci_handle *src,
const struct vmci_handle *dst,
bool from_guest,
enum vmci_route *route)
{
bool has_host_device = vmci_host_code_active();
bool has_guest_device = vmci_guest_code_active();
*route = VMCI_ROUTE_NONE;
/*
* "from_guest" is only ever set to true by
* IOCTL_VMCI_DATAGRAM_SEND (or by the vmkernel equivalent),
* which comes from the VMX, so we know it is coming from a
* guest.
*
* To avoid inconsistencies, test these once. We will test
* them again when we do the actual send to ensure that we do
* not touch a non-existent device.
*/
/* Must have a valid destination context. */
if (VMCI_INVALID_ID == dst->context)
return VMCI_ERROR_INVALID_ARGS;
/* Anywhere to hypervisor. */
if (VMCI_HYPERVISOR_CONTEXT_ID == dst->context) {
/*
* If this message already came from a guest then we
* cannot send it to the hypervisor. It must come
* from a local client.
*/
if (from_guest)
return VMCI_ERROR_DST_UNREACHABLE;
/*
* We must be acting as a guest in order to send to
* the hypervisor.
*/
if (!has_guest_device)
return VMCI_ERROR_DEVICE_NOT_FOUND;
/* And we cannot send if the source is the host context. */
if (VMCI_HOST_CONTEXT_ID == src->context)
return VMCI_ERROR_INVALID_ARGS;
/*
* If the client passed the ANON source handle then
* respect it (both context and resource are invalid).
* However, if they passed only an invalid context,
* then they probably mean ANY, in which case we
* should set the real context here before passing it
* down.
*/
if (VMCI_INVALID_ID == src->context &&
VMCI_INVALID_ID != src->resource)
src->context = vmci_get_context_id();
/* Send from local client down to the hypervisor. */
*route = VMCI_ROUTE_AS_GUEST;
return VMCI_SUCCESS;
}
/* Anywhere to local client on host. */
if (VMCI_HOST_CONTEXT_ID == dst->context) {
/*
* If it is not from a guest but we are acting as a
* guest, then we need to send it down to the host.
* Note that if we are also acting as a host then this
* will prevent us from sending from local client to
* local client, but we accept that restriction as a
* way to remove any ambiguity from the host context.
*/
if (src->context == VMCI_HYPERVISOR_CONTEXT_ID) {
/*
* If the hypervisor is the source, this is
* host local communication. The hypervisor
* may send vmci event datagrams to the host
* itself, but it will never send datagrams to
* an "outer host" through the guest device.
*/
if (has_host_device) {
*route = VMCI_ROUTE_AS_HOST;
return VMCI_SUCCESS;
} else {
return VMCI_ERROR_DEVICE_NOT_FOUND;
}
}
if (!from_guest && has_guest_device) {
/* If no source context then use the current. */
if (VMCI_INVALID_ID == src->context)
src->context = vmci_get_context_id();
/* Send it from local client down to the host. */
*route = VMCI_ROUTE_AS_GUEST;
return VMCI_SUCCESS;
}
/*
* Otherwise we already received it from a guest and
* it is destined for a local client on this host, or
* it is from another local client on this host. We
* must be acting as a host to service it.
*/
if (!has_host_device)
return VMCI_ERROR_DEVICE_NOT_FOUND;
if (VMCI_INVALID_ID == src->context) {
/*
* If it came from a guest then it must have a
* valid context. Otherwise we can use the
* host context.
*/
if (from_guest)
return VMCI_ERROR_INVALID_ARGS;
src->context = VMCI_HOST_CONTEXT_ID;
}
/* Route to local client. */
*route = VMCI_ROUTE_AS_HOST;
return VMCI_SUCCESS;
}
/*
* If we are acting as a host then this might be destined for
* a guest.
*/
if (has_host_device) {
/* It will have a context if it is meant for a guest. */
if (vmci_ctx_exists(dst->context)) {
if (VMCI_INVALID_ID == src->context) {
/*
* If it came from a guest then it
* must have a valid context.
* Otherwise we can use the host
* context.
*/
if (from_guest)
return VMCI_ERROR_INVALID_ARGS;
src->context = VMCI_HOST_CONTEXT_ID;
} else if (VMCI_CONTEXT_IS_VM(src->context) &&
src->context != dst->context) {
/*
* VM to VM communication is not
* allowed. Since we catch all
* communication destined for the host
* above, this must be destined for a
* VM since there is a valid context.
*/
return VMCI_ERROR_DST_UNREACHABLE;
}
/* Pass it up to the guest. */
*route = VMCI_ROUTE_AS_HOST;
return VMCI_SUCCESS;
} else if (!has_guest_device) {
/*
* The host is attempting to reach a CID
* without an active context, and we can't
* send it down, since we have no guest
* device.
*/
return VMCI_ERROR_DST_UNREACHABLE;
}
}
/*
* We must be a guest trying to send to another guest, which means
* we need to send it down to the host. We do not filter out VM to
* VM communication here, since we want to be able to use the guest
* driver on older versions that do support VM to VM communication.
*/
if (!has_guest_device) {
/*
* Ending up here means we have neither guest nor host
* device.
*/
return VMCI_ERROR_DEVICE_NOT_FOUND;
}
/* If no source context then use the current context. */
if (VMCI_INVALID_ID == src->context)
src->context = vmci_get_context_id();
/*
* Send it from local client down to the host, which will
* route it to the other guest for us.
*/
*route = VMCI_ROUTE_AS_GUEST;
return VMCI_SUCCESS;
}

View file

@ -0,0 +1,30 @@
/*
* 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.
*/
#ifndef _VMCI_ROUTE_H_
#define _VMCI_ROUTE_H_
#include <linux/vmw_vmci_defs.h>
enum vmci_route {
VMCI_ROUTE_NONE,
VMCI_ROUTE_AS_HOST,
VMCI_ROUTE_AS_GUEST,
};
int vmci_route(struct vmci_handle *src, const struct vmci_handle *dst,
bool from_guest, enum vmci_route *route);
#endif /* _VMCI_ROUTE_H_ */