xpvtap.c revision 7eea693d6b672899726e75993fddc4e95b52647f
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/ddi_impldefs.h>
#include <sys/sysmacros.h>
#include <sys/ddidevmap.h>
#include <vm/seg_kmem.h>
static struct cb_ops xpvtap_cb_ops = {
xpvtap_open, /* cb_open */
xpvtap_close, /* cb_close */
nodev, /* cb_strategy */
nodev, /* cb_print */
nodev, /* cb_dump */
nodev, /* cb_read */
nodev, /* cb_write */
xpvtap_ioctl, /* cb_ioctl */
xpvtap_devmap, /* cb_devmap */
nodev, /* cb_mmap */
xpvtap_segmap, /* cb_segmap */
xpvtap_chpoll, /* cb_chpoll */
ddi_prop_op, /* cb_prop_op */
NULL, /* cb_stream */
};
void **result);
static struct dev_ops xpvtap_dev_ops = {
DEVO_REV, /* devo_rev */
0, /* devo_refcnt */
xpvtap_getinfo, /* devo_getinfo */
nulldev, /* devo_identify */
nulldev, /* devo_probe */
xpvtap_attach, /* devo_attach */
xpvtap_detach, /* devo_detach */
nodev, /* devo_reset */
&xpvtap_cb_ops, /* devo_cb_ops */
NULL, /* devo_bus_ops */
NULL /* power */
};
static struct modldrv xpvtap_modldrv = {
&mod_driverops, /* Type of module. This one is a driver */
"xpvtap driver", /* Name of the module. */
&xpvtap_dev_ops, /* driver ops */
};
static struct modlinkage xpvtap_modlinkage = {
(void *) &xpvtap_modldrv,
};
void *xpvtap_statep;
static void xpvtap_user_thread(void *arg);
/*
* _init()
*/
int
_init(void)
{
int e;
if (e != 0) {
return (e);
}
e = mod_install(&xpvtap_modlinkage);
if (e != 0) {
return (e);
}
return (0);
}
/*
* _info()
*/
int
{
}
/*
* _fini()
*/
int
_fini(void)
{
int e;
e = mod_remove(&xpvtap_modlinkage);
if (e != 0) {
return (e);
}
return (0);
}
/*
* xpvtap_attach()
*/
static int
{
int instance;
int e;
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
/* initialize our state info */
return (DDI_FAILURE);
}
/* Initialize the guest ring */
if (e != DDI_SUCCESS) {
goto attachfail_ringinit;
}
DDI_PSEUDO, 0);
if (e != DDI_SUCCESS) {
goto attachfail_minor_node;
}
/* Report that driver was loaded */
return (DDI_SUCCESS);
return (DDI_FAILURE);
}
/*
* xpvtap_detach()
*/
static int
{
int instance;
return (DDI_FAILURE);
}
switch (cmd) {
case DDI_DETACH:
break;
case DDI_SUSPEND:
default:
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* xpvtap_getinfo()
*/
/*ARGSUSED*/
static int
{
int instance;
int e;
switch (cmd) {
case DDI_INFO_DEVT2DEVINFO:
return (DDI_FAILURE);
}
e = DDI_SUCCESS;
break;
case DDI_INFO_DEVT2INSTANCE:
e = DDI_SUCCESS;
break;
default:
e = DDI_FAILURE;
break;
}
return (e);
}
/*
* xpvtap_open()
*/
/*ARGSUSED*/
static int
{
int instance;
if (secpolicy_xvm_control(cred)) {
return (EPERM);
}
return (ENXIO);
}
/* we should only be opened once */
return (EBUSY);
}
/*
* since will be doing it in a separate kernel thread.
*/
return (0);
}
/*
* xpvtap_close()
*/
/*ARGSUSED*/
static int
{
int instance;
return (ENXIO);
}
/*
* wake thread so it can cleanup and wait for it to exit so we can
*/
}
/*
* when the ring is brought down, a userland hotplug script is run
* which tries to bring the userland app down. We'll wait for a bit
* for the user app to exit. Notify the thread waiting that the app
* has closed the driver.
*/
return (0);
}
/*
* xpvtap_ioctl()
*/
/*ARGSUSED*/
static int
int *rval)
{
int instance;
if (secpolicy_xvm_control(cred)) {
return (EPERM);
}
if (instance == -1) {
return (EBADF);
}
return (EBADF);
}
switch (cmd) {
case XPVTAP_IOCTL_RESP_PUSH:
/*
* wake thread, thread handles guest requests and user app
* responses.
*/
break;
default:
return (ENXIO);
}
return (0);
}
/*
* xpvtap_segmap()
*/
/*ARGSUSED*/
static int
{
struct segmf_crargs a;
int instance;
int e;
if (secpolicy_xvm_control(cred_p)) {
return (EPERM);
}
return (EBADF);
}
/* the user app should be doing a MAP_SHARED mapping */
return (EINVAL);
}
/*
* if this is the user ring (offset = 0), devmap it (which ends up in
* xpvtap_devmap). devmap will alloc and map the ring into the
* app's VA space.
*/
if (off == 0) {
return (e);
}
/* this should be the mmap for the gref pages (offset = PAGESIZE) */
return (EINVAL);
}
/* make sure we get the size we're expecting */
if (len != XPVTAP_GREF_BUFSIZE) {
return (EINVAL);
}
/*
* reserve user app VA space for the gref pages and use segmf to
* manage the backing store for the physical memory. segmf will
*/
return (ENOMEM);
}
} else {
/* User specified address */
}
if (e != 0) {
return (e);
}
/*
* Stash user base address, and compute address where the request
* array will end up.
*/
/* register an as callback so we can cleanup when the app goes away */
if (e != 0) {
return (EINVAL);
}
/* wake thread to see if there are requests already queued up */
return (0);
}
/*
* xpvtap_devmap()
*/
/*ARGSUSED*/
static int
{
int instance;
int e;
return (EBADF);
}
/* we should only get here if the offset was == 0 */
if (off != 0) {
return (EINVAL);
}
/* we should only be mapping in one page */
return (EINVAL);
}
/*
* we already allocated the user ring during driver attach, all we
* need to do is map it into the user app's VA.
*/
if (e < 0) {
return (e);
}
/* return the size to compete the devmap */
return (0);
}
/*
* xpvtap_chpoll()
*/
static int
{
int instance;
if (instance == -1) {
return (EBADF);
}
return (EBADF);
}
*reventsp = 0;
return (EINVAL);
}
/*
* if we pushed requests on the user ring since the last poll, wakeup
* the user app
*/
/*
* XXX - is this faster here or xpvtap_user_request_push??
* prelim data says here. Because less membars or because
* user thread will spin in poll requests before getting to
* responses?
*/
/* no new requests */
} else {
*reventsp = 0;
if (!anyyet) {
}
}
return (0);
}
/*
* xpvtap_drv_init()
*/
static xpvtap_state_t *
xpvtap_drv_init(int instance)
{
int e;
if (e != DDI_SUCCESS) {
return (NULL);
}
}
/* initialize user ring, thread, mapping state */
e = xpvtap_user_init(state);
if (e != DDI_SUCCESS) {
goto drvinitfail_userinit;
}
return (state);
return (NULL);
}
/*
* xpvtap_drv_fini()
*/
static void
{
}
/*
* xpvtap_intr()
* this routine will be called when we have a request on the guest ring.
*/
static uint_t
{
/* wake thread, thread handles guest requests and user app responses */
return (DDI_INTR_CLAIMED);
}
/*
* xpvtap_segmf_register()
*/
static int
{
int i;
if (pgcnt == 0) {
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
/*
* lock down the htables so the HAT can't steal them. Register the
* PTE MA's for each gref page with seg_mf so we can do user space
* gref mappings.
*/
for (i = 0; i < pgcnt; i++) {
}
return (DDI_SUCCESS);
}
/*
* xpvtap_segmf_unregister()
* as_callback routine
*/
/*ARGSUSED*/
static void
{
int i;
return;
}
/* unmap any outstanding req's grefs */
/* Unlock the gref pages */
for (i = 0; i < pgcnt; i++) {
/* XXX Need to verify if we still need this */
}
/* remove the callback (which is this routine) */
}
/*
* xpvtap_user_init()
*/
static int
{
int e;
/* Setup the ring between the driver and user app */
e = xpvtap_user_ring_init(state);
if (e != DDI_SUCCESS) {
return (DDI_FAILURE);
}
/*
* the user ring can handle BLKIF_RING_SIZE outstanding requests. This
* is the same number of requests as the guest ring. Initialize the
* state we use to track request IDs to the user app. These IDs will
* also identify which group of gref pages correspond with the
* request.
*/
/*
* allocate the space to store a copy of each outstanding requests. We
* will need to reference the ID and the number of segments when we
* get the response from the user app.
*/
KM_SLEEP);
/*
* initialize the thread we use to process guest requests and user
* responses.
*/
e = xpvtap_user_thread_init(state);
if (e != DDI_SUCCESS) {
}
return (DDI_SUCCESS);
return (DDI_FAILURE);
}
/*
* xpvtap_user_ring_init()
*/
static int
{
/* alocate and initialize the page for the shared user ring */
usring->ur_prod_polled = 0;
return (DDI_SUCCESS);
}
/*
* xpvtap_user_thread_init()
*/
static int
{
char taskqname[32];
/* create but don't start the user thread */
TASKQ_DEFAULTPRI, 0);
}
return (DDI_SUCCESS);
return (DDI_FAILURE);
}
/*
* xpvtap_user_thread_start()
*/
static void
{
int e;
/* start the user thread */
if (e != DDI_SUCCESS) {
}
}
/*
* xpvtap_user_thread_stop()
*/
static void
{
/* wake thread so it can exit */
}
}
/*
* xpvtap_user_fini()
*/
static void
{
}
/*
* xpvtap_user_ring_fini()
*/
static void
{
}
/*
* xpvtap_user_thread_fini()
*/
static void
{
}
/*
* xpvtap_user_thread()
*/
static void
xpvtap_user_thread(void *arg)
{
boolean_t b;
int e;
/* See if we are supposed to exit */
return;
}
/*
* if we aren't supposed to be awake, wait until someone wakes us.
* when we wake up, check for a kill or someone telling us to exit.
*/
goto xpvtap_thread_start;
}
}
/* if someone didn't wake us, go back to the start of the thread */
goto xpvtap_thread_start;
}
/* we are awake */
/* process requests from the guest */
do {
/*
* check for requests from the guest. if we don't have any,
* break out of the loop.
*/
if (e == B_FALSE) {
break;
}
/* we got a request, map the grefs into the user app's VA */
if (e != DDI_SUCCESS) {
/*
* If we couldn't map the request (e.g. user app hasn't
* opened the device yet), requeue it and try again
* later
*/
break;
}
/* push the request to the user app */
if (e != DDI_SUCCESS) {
}
/* process reponses from the user app */
do {
/*
* check for responses from the user app. if we don't have any,
* break out of the loop.
*/
if (b != B_TRUE) {
break;
}
/*
* if we got a response, unmap the grefs from the matching
* request.
*/
/* push the response to the guest */
goto xpvtap_thread_start;
}
/*
* xpvtap_user_request_map()
*/
static int
{
int i;
int e;
return (DDI_FAILURE);
}
/* has to happen after segmap returns */
/* register the pte's with segmf */
e = xpvtap_segmf_register(state);
if (e != DDI_SUCCESS) {
return (DDI_FAILURE);
}
}
/* alloc an ID for the user ring */
if (e != DDI_SUCCESS) {
return (DDI_FAILURE);
}
/* if we don't have any segments to map, we're done */
(req->nr_segments == 0)) {
return (DDI_SUCCESS);
}
/* get the apps gref address */
return (DDI_FAILURE);
}
/* if we are reading from disk, we are writing into memory */
flags = 0;
flags |= SEGMF_GREF_WR;
}
/* Load the grefs into seg_mf */
for (i = 0; i < req->nr_segments; i++) {
}
domid);
return (DDI_SUCCESS);
}
/*
* xpvtap_user_request_push()
*/
static int
{
/*
* Save request from the frontend. used for ID mapping and unmap
*/
/* put the request on the user ring */
uring->req_prod_pvt++;
return (DDI_SUCCESS);
}
static void
{
int e;
return;
}
/* get a copy of the original request */
/* unmap the grefs for this request */
(req->nr_segments != 0)) {
return;
}
if (e != 0) {
}
}
/* free up the user ring id */
}
static int
{
if (!RING_HAS_UNCONSUMED_RESPONSES(uring)) {
return (B_FALSE);
}
return (B_FALSE);
}
/* copy out the user app response */
/* restore the quests id from the original request */
return (B_TRUE);
}
/*
* xpvtap_user_app_stop()
*/
{
/*
* Give the app 10 secs to exit. If it doesn't exit, it's not a serious
* problem, we just won't auto-detach the driver.
*/
if (rc <= 0) {
"deferring detach\n");
}
}
}
/*
* xpvtap_rs_init()
* Initialize the resource structure. init() returns a handle to be used
* for the rest of the resource functions. This code is written assuming
* that min_val will be close to 0. Therefore, we will allocate the free
* buffer only taking max_val into account.
*/
static void
{
/* alloc space for resource structure */
/*
* Test to see if the max value is 64-bit aligned. If so, we don't need
* to allocate an extra 64-bit word. alloc space for free buffer
* (8 bytes per uint64_t).
*/
if ((max_val & 0x3F) == 0) {
} else {
}
/* Initialize resource structure */
/* Mark all resources as free */
}
/* setup handle which is returned from this function */
}
/*
* xpvtap_rs_fini()
* Frees up the space allocated in init(). Notice that a pointer to the
* handle is used for the parameter. fini() will set the handle to NULL
* before returning.
*/
static void
{
/* set handle to null. This helps catch bugs. */
}
/*
* xpvtap_rs_alloc()
* alloc a resource. If alloc fails, we are out of resources.
*/
static int
{
/*
* Find a free resource. This will return out of the loop once it finds
* a free resource. There are a total of 'max'-'min'+1 resources.
* Performs a round robin allocation.
*/
/* if the next resource to check is free */
/* we are using this resource */
/* take it out of the free list */
/*
* increment the last count so we start checking the
* next resource on the next alloc(). Note the rollover
* at 'max'+1.
*/
}
/* unlock the resource structure */
return (DDI_SUCCESS);
}
/*
* This resource is not free, lets go to the next one. Note the
* rollover at 'max'.
*/
}
}
return (DDI_FAILURE);
}
/*
* xpvtap_rs_free()
* Free the previously alloc'd resource. Once a resource has been free'd,
* it can be used again when alloc is called.
*/
static void
{
if (!rstruct->rs_flushing) {
}
/* Put the resource back in the free list */
if (!rstruct->rs_flushing) {
}
}
/*
* xpvtap_rs_flush()
*/
static void
void *arg)
{
/*
* for all resources not free, call the callback routine to clean it
* up.
*/
/* if the next resource to check is not free */
/* call the callback to cleanup */
/* put it back in the free list */
}
/* go to the next one. Note the rollover at 'max' */
}
}
}