123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544 |
- /*
- * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support
- *
- * Copyright (C) 2010-2013 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 version 2 as published by
- * the Free Software Foundation.
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; see the file COPYING. If not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
- #line 5
- /**
- * @file
- *
- * @brief MVP host kernel implementation of the queue pairs API
- *
- */
- #include <linux/module.h>
- #include <linux/slab.h>
- #include <linux/mm.h>
- #include <linux/vmalloc.h>
- #include <linux/highmem.h>
- #include <linux/slab.h>
- #include "mvp.h"
- #include "mvpkm_kernel.h"
- #include "qp.h"
- #include "qp_host_kernel.h"
- static QPHandle queuePairs[QP_MAX_QUEUE_PAIRS];
- static QPListener listeners[QP_MAX_LISTENERS];
- /*
- * Protect listeners and queuePairs.
- */
- static DEFINE_MUTEX(qpLock);
- #define QPLock() mutex_lock(&qpLock)
- #define QPUnlock() mutex_unlock(&qpLock)
- /**
- * @brief Map a vector of pages into virtually contiguous kernel space
- * @param vm this vm's vm struct
- * @param base base machine page number that lists pages to map
- * @param nrPages number of pages to map
- * @param[out] qp handle to qp to set up
- * @param[out] hkva virtual address mapping
- * @return QP_SUCCESS on success, error code otherwise. Mapped address
- * is returned in hkva
- */
- static int32
- MapPages(struct MvpkmVM *vm,
- MPN base,
- uint32 nrPages,
- QPHandle *qp,
- HKVA *hkva)
- {
- HKVA *va;
- uint32 i;
- uint32 rc;
- struct page *basepfn = pfn_to_page(base);
- struct page **pages;
- BUG_ON(!vm); /* this would be very bad. */
- if (!hkva)
- return QP_ERROR_INVALID_ARGS;
- pages = kmalloc(nrPages * sizeof(MPN), GFP_KERNEL);
- if (!pages)
- return QP_ERROR_NO_MEM;
- /*
- * Map in the first page, read out the MPN vector
- */
- down_write(&vm->lockedSem);
- va = kmap(basepfn);
- if (!va) {
- rc = QP_ERROR_INVALID_ARGS;
- kfree(pages);
- qp->pages = NULL;
- goto out;
- }
- /*
- * Grab references and translate MPNs->PFNs
- */
- for (i = 0; i < nrPages; i++) {
- pages[i] = pfn_to_page(((MPN *)va)[i]);
- get_page(pages[i]);
- }
- /*
- * Clean up the first mapping and remap the entire vector
- */
- kunmap(basepfn);
- va = vmap(pages, nrPages, VM_MAP, PAGE_KERNEL);
- if (!va) {
- rc = QP_ERROR_NO_MEM;
- for (i = 0; i < nrPages; i++)
- put_page(pages[i]);
- kfree(pages);
- qp->pages = NULL;
- goto out;
- } else {
- *hkva = (HKVA)va;
- qp->pages = pages;
- }
- /*
- * Let's not leak mpns..
- */
- memset(va, 0x0, nrPages * PAGE_SIZE);
- rc = QP_SUCCESS;
- out:
- up_write(&vm->lockedSem);
- return rc;
- }
- /**
- * @brief Initialize all free queue pair entries and listeners
- */
- void
- QP_HostInit(void)
- {
- uint32 i;
- for (i = 0; i < QP_MAX_QUEUE_PAIRS; i++)
- QP_MakeInvalidQPHandle(&queuePairs[i]);
- for (i = 0; i < QP_MAX_LISTENERS; i++)
- listeners[i] = NULL;
- }
- /**
- * @brief Detaches a guest from a queue pair and notifies
- * any registered listeners through the detach callback
- * @param id id that guest requested a detach from, detaches all
- * queue pairs associated with a VM if the resource id == QP_INVALID_ID
- * @return QP_SUCCESS on success, appropriate error code otherwise
- */
- int32
- QP_GuestDetachRequest(QPId id)
- {
- QPHandle *qp;
- uint32 i;
- if (id.resource >= QP_MAX_ID && id.resource != QP_INVALID_ID)
- return QP_ERROR_INVALID_ARGS;
- QPLock();
- /*
- * Invalidate all queue pairs associated with this VM if
- * resource == QP_INVALID_ID
- */
- if (id.resource == QP_INVALID_ID) {
- for (i = 0; i < QP_MAX_QUEUE_PAIRS; i++) {
- qp = &queuePairs[i];
- if (qp->id.context == id.context && qp->peerDetachCB)
- qp->peerDetachCB(qp->detachData);
- }
- } else {
- qp = &queuePairs[id.resource];
- if (qp->peerDetachCB)
- qp->peerDetachCB(qp->detachData);
- }
- QPUnlock();
- return QP_SUCCESS;
- }
- /**
- * @brief Attaches a guest to shared memory region
- * @param vm guest to attach
- * @param args queue pair args structure:
- * - args->id: id of the region to attach to;
- * if id.resource == QP_INVALID_ID, then an id is assigned.
- * - args->capacity: total size of the region in bytes
- * - args->type: type of queue pair (e.g PVTCP)
- * @param base base machine page number that lists pages to map
- * @param nrPages number of pages to map
- * @return QP_SUCCESS on success, appropriate error code otherwise.
- */
- int32
- QP_GuestAttachRequest(struct MvpkmVM *vm,
- QPInitArgs *args,
- MPN base,
- uint32 nrPages)
- {
- int32 rc;
- HKVA hkva = 0;
- QPHandle *qp;
- uint32 i;
- if ((!QP_CheckArgs(args)) ||
- (vm->wsp->guestId != (Mksck_VmId)args->id.context) ||
- (args->capacity != (nrPages * PAGE_SIZE)))
- return QP_ERROR_INVALID_ARGS;
- QP_DBG("%s: Guest requested attach to [%u:%u] capacity: %u " \
- "type: %x base: %x nrPages: %u\n",
- __func__, args->id.context, args->id.resource, args->capacity,
- args->type, base, nrPages);
- QPLock();
- /*
- * Assign a resource id if id == QP_INVALID_ID
- */
- if (args->id.resource == QP_INVALID_ID) {
- for (i = 0; i < QP_MAX_QUEUE_PAIRS; i++)
- if (queuePairs[i].state == QP_STATE_FREE) {
- args->id.resource = i;
- QP_DBG("%s: Guest requested anonymous region," \
- " assigning resource id %u\n",
- __func__, args->id.resource);
- goto found;
- }
- rc = QP_ERROR_NO_MEM;
- goto out;
- }
- found:
- qp = queuePairs + args->id.resource;
- if (qp->state != QP_STATE_FREE) {
- rc = QP_ERROR_ALREADY_ATTACHED;
- goto out;
- }
- /*
- * Brand new queue pair, allocate some memory to back it and
- * initialize the entry.
- */
- rc = MapPages(vm, base, nrPages, qp, &hkva);
- if (rc != QP_SUCCESS)
- goto out;
- /* NB: reversed from the guest */
- qp->id = args->id;
- qp->capacity = args->capacity;
- qp->produceQ = (QHandle *)hkva;
- qp->consumeQ = (QHandle *)(hkva + args->capacity/2);
- qp->queueSize = args->capacity/2 - sizeof(QHandle);
- qp->type = args->type;
- qp->state = QP_STATE_GUEST_ATTACHED;
- /*
- * The qp is now assumed to be well-formed
- */
- QP_DBG("%s: Guest attached to region [%u:%u] capacity: %u HKVA: %x\n",
- __func__, args->id.context, args->id.resource,
- args->capacity, (uint32)hkva);
- rc = QP_SUCCESS;
- out:
- QPUnlock();
- if (rc != QP_SUCCESS)
- QP_DBG("%s: Failed to attach: %u\n", __func__, rc);
- return rc;
- }
- /**
- * @brief Attaches the host to the shared memory region. The guest
- * MUST have allocated the shmem region already or else this will fail.
- * @param args structure with the shared memory region id to attach to,
- * total size of the region in bytes, and type of queue pair (e.g PVTCP)
- * @param[in, out] qp handle to the queue pair to return
- * @return QP_SUCCESS on success, appropriate error code otherwise
- */
- int32
- QP_Attach(QPInitArgs *args,
- QPHandle **qp)
- {
- uint32 rc;
- if (!qp || !QP_CheckArgs(args))
- return QP_ERROR_INVALID_ARGS;
- QP_DBG("%s: Attaching to id: [%u:%u] capacity: %u\n",
- __func__, args->id.context, args->id.resource, args->capacity);
- QPLock();
- *qp = queuePairs + args->id.resource;
- if (!QP_CheckHandle(*qp)) {
- *qp = NULL;
- rc = QP_ERROR_INVALID_HANDLE;
- goto out;
- }
- if ((*qp)->state == QP_STATE_CONNECTED) {
- rc = QP_ERROR_ALREADY_ATTACHED;
- goto out;
- }
- if ((*qp)->state != QP_STATE_GUEST_ATTACHED) {
- rc = QP_ERROR_INVALID_HANDLE;
- goto out;
- }
- (*qp)->state = QP_STATE_CONNECTED;
- QP_DBG("%s: Attached!\n", __func__);
- rc = QP_SUCCESS;
- out:
- QPUnlock();
- return rc;
- }
- EXPORT_SYMBOL(QP_Attach);
- /**
- * @brief Detaches the host to the shared memory region.
- * @param[in, out] qp handle to the queue pair
- * @return QP_SUCCESS on success, appropriate error code otherwise
- * @sideeffects Frees memory
- */
- int32
- QP_Detach(QPHandle *qp)
- {
- uint32 rc;
- uint32 i;
- QPLock();
- if (!QP_CheckHandle(qp)) {
- rc = QP_ERROR_INVALID_HANDLE;
- goto out;
- }
- QP_DBG("%s: Freeing queue pair [%u:%u]\n",
- __func__, qp->id.context, qp->id.resource);
- BUG_ON(!qp->produceQ);
- BUG_ON(!qp->pages);
- BUG_ON((qp->state != QP_STATE_CONNECTED) &&
- (qp->state != QP_STATE_GUEST_ATTACHED));
- vunmap(qp->produceQ);
- for (i = 0; i < qp->capacity/PAGE_SIZE; i++)
- put_page(qp->pages[i]);
- kfree(qp->pages);
- QP_DBG("%s: Host detached from [%u:%u]\n",
- __func__, qp->id.context, qp->id.resource);
- QP_MakeInvalidQPHandle(qp);
- rc = QP_SUCCESS;
- out:
- QPUnlock();
- return rc;
- }
- /**
- * @brief Detaches and destroys all queue pairs associated with a given guest
- * @param vmID which VM to clean up
- * @sideeffects Destroys all queue pairs for guest vmID
- */
- void QP_DetachAll(Mksck_VmId vmID)
- {
- QPId id = {
- .context = (uint32)vmID,
- .resource = QP_INVALID_ID
- };
- QP_DBG("%s: Detaching all queue pairs from vmId context %u\n",
- __func__, vmID);
- QP_GuestDetachRequest(id);
- }
- /**
- * @brief Registers a listener into the queue pair system. Callbacks are
- * called with interrupts disabled and must not sleep.
- * @param listener listener to be called
- * @return QP_SUCCESS on success, QP_ERROR_NO_MEM if no more
- * listeners can be registered
- */
- int32
- QP_RegisterListener(const QPListener listener)
- {
- uint32 i;
- int32 rc = QP_ERROR_NO_MEM;
- QPLock();
- for (i = 0; i < QP_MAX_LISTENERS; i++)
- if (!listeners[i]) {
- listeners[i] = listener;
- QP_DBG("%s: Registered listener\n", __func__);
- rc = QP_SUCCESS;
- break;
- }
- QPUnlock();
- return rc;
- }
- EXPORT_SYMBOL(QP_RegisterListener);
- /**
- * @brief Unregister a listener service from the queue pair system.
- * @param listener listener to unregister
- * @return QP_SUCCESS on success, appropriate error code otherwise
- */
- int32
- QP_UnregisterListener(const QPListener listener)
- {
- uint32 i;
- int32 rc = QP_ERROR_INVALID_HANDLE;
- QPLock();
- for (i = 0; i < QP_MAX_LISTENERS; i++)
- if (listeners[i] == listener) {
- listeners[i] = NULL;
- QP_DBG("%s: Unregistered listener\n", __func__);
- rc = QP_SUCCESS;
- break;
- }
- QPUnlock();
- return rc;
- }
- EXPORT_SYMBOL(QP_UnregisterListener);
- /**
- * @brief Registers a callback to be called when the guest detaches
- * from a queue pair. Callbacks are called with interrupts and
- * must not sleep.
- * @param qp handle to the queue pair
- * @param callback callback to be called
- * @param data data to deliver to the callback
- * @return QP_SUCCESS on success, appropriate error code otherwise
- */
- int32
- QP_RegisterDetachCB(QPHandle *qp,
- void (*callback)(void *),
- void *data)
- {
- if (!QP_CheckHandle(qp))
- return QP_ERROR_INVALID_HANDLE;
- if (!callback)
- return QP_ERROR_INVALID_ARGS;
- qp->peerDetachCB = callback;
- qp->detachData = data;
- QP_DBG("%s: Registered detach callback\n", __func__);
- return QP_SUCCESS;
- }
- EXPORT_SYMBOL(QP_RegisterDetachCB);
- /**
- * @brief Noop on the host, only guests can initiate a notify
- * @param args noop
- * @return QP_SUCCESS
- */
- int32 QP_Notify(QPInitArgs *args)
- {
- return QP_SUCCESS;
- }
- EXPORT_SYMBOL(QP_Notify);
- /**
- * @brief Notify any registered listeners for the given queue pair
- * @param args initialization arguments used by the guest
- * @return QP_SUCCESS on success, error otherwise
- */
- int32 QP_NotifyListener(QPInitArgs *args)
- {
- int32 i;
- QPHandle *qp = NULL;
- if (!QP_CheckArgs(args))
- return QP_ERROR_INVALID_ARGS;
- /*
- * Iterate over listeners until one of them reports they handled it
- */
- QPLock();
- for (i = 0; i < QP_MAX_LISTENERS; i++)
- if (listeners[i]) {
- QP_DBG("Delivering attach event to listener...\n");
- if (listeners[i](args) == QP_SUCCESS)
- break;
- }
- if (i == QP_MAX_LISTENERS) {
- /*
- * No listener successfully probed this QP.
- * The guest DETACH HVC isn't implemented; we need compensate
- * for it by deallocating the QP here.
- * This is a workaround which assumes, more-or-less correctly,
- * that unsuccessful QP probes never lead to subsequent
- * host-attaching.
- */
- qp = &queuePairs[args->id.resource];
- }
- QPUnlock();
- if (qp)
- QP_Detach(qp);
- return QP_SUCCESS;
- }
- EXPORT_SYMBOL(QP_Detach);
|