aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mvp/commkm/comm_os_linux.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mvp/commkm/comm_os_linux.c')
-rw-r--r--arch/arm/mvp/commkm/comm_os_linux.c371
1 files changed, 371 insertions, 0 deletions
diff --git a/arch/arm/mvp/commkm/comm_os_linux.c b/arch/arm/mvp/commkm/comm_os_linux.c
new file mode 100644
index 0000000..74f99f5
--- /dev/null
+++ b/arch/arm/mvp/commkm/comm_os_linux.c
@@ -0,0 +1,371 @@
+/*
+ * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications
+ *
+ * Copyright (C) 2010-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 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 Linux-specific functions/types.
+ */
+
+#include "comm_os.h"
+
+#define DISPATCH_MAX_CYCLES 8192
+
+/* Type definitions */
+
+typedef struct workqueue_struct CommOSWorkQueue;
+
+
+/* Static data */
+
+static volatile int running;
+static int numCpus;
+static CommOSWorkQueue *dispatchWQ;
+static CommOSDispatchFunc dispatch;
+static CommOSWork dispatchWorksNow[NR_CPUS];
+static CommOSWork dispatchWorks[NR_CPUS];
+static unsigned int dispatchInterval = 1;
+static unsigned int dispatchMaxCycles = 2048;
+static CommOSWorkQueue *aioWQ;
+
+
+/**
+ * @brief Initializes a workqueue consisting of per-cpu kernel threads.
+ * @param name workqueue name
+ * @return workqueue handle if successful, NULL otherwise
+ */
+
+static inline CommOSWorkQueue *
+CreateWorkqueue(const char *name)
+{
+ return create_workqueue(name);
+}
+
+
+/**
+ * @brief Destroys a workqueue and stops its threads.
+ * @param[in,out] wq workqueue to destroy.
+ * @return workqueue handle is successful, NULL otherwise.
+ */
+
+static inline void
+DestroyWorkqueue(CommOSWorkQueue *wq)
+{
+ destroy_workqueue(wq);
+}
+
+
+/**
+ * @brief Force execution of a work item.
+ * @param[in,out] work work item to dequeue.
+ */
+
+static inline void
+FlushDelayedWork(CommOSWork *work)
+{
+ flush_delayed_work(work);
+}
+
+
+/**
+ * @brief Enqueue a work item to a workqueue for execution on a given cpu
+ * and after the specified interval.
+ * @param cpu cpu number. If negative, work item is enqueued on current cpu.
+ * @param[in,out] wq target work queue.
+ * @param[in,out] work work item to enqueue.
+ * @param jif delay interval.
+ * @return zero if successful, non-zero otherwise.
+ */
+
+static inline int
+QueueDelayedWorkOn(int cpu,
+ CommOSWorkQueue *wq,
+ CommOSWork *work,
+ unsigned long jif)
+{
+ if (cpu < 0) {
+ return !queue_delayed_work(wq, work, jif) ? -1 : 0;
+ } else {
+ return !queue_delayed_work_on(cpu, wq, work, jif) ? -1 : 0;
+ }
+}
+
+
+/**
+ * @brief Enqueues a work item to a workqueue for execution on the current cpu
+ * and after the specified interval.
+ * @param[in,out] wq target work queue.
+ * @param[in,out] work work item to enqueue.
+ * @param jif delay interval.
+ * @return zero if successful, non-zero otherwise.
+ */
+
+static inline int
+QueueDelayedWork(CommOSWorkQueue *wq,
+ CommOSWork *work,
+ unsigned long jif)
+{
+ return QueueDelayedWorkOn(-1, wq, work, jif);
+}
+
+
+/**
+ * @brief Cancels a queued delayed work item and synchronizes with its
+ * completion.
+ * @param[in,out] work work item to cancel
+ */
+
+static inline void
+WaitForDelayedWork(CommOSWork *work)
+{
+ cancel_delayed_work_sync(work);
+}
+
+
+/**
+ * @brief Discards work items queued to the specified workqueue.
+ * @param[in,out] wq work queue to flush.
+ */
+
+static inline void
+FlushWorkqueue(CommOSWorkQueue *wq)
+{
+ flush_workqueue(wq);
+}
+
+
+/**
+ * @brief Schedules dispatcher threads for immediate execution.
+ */
+
+void
+CommOS_ScheduleDisp(void)
+{
+ CommOSWork *work = &dispatchWorksNow[get_cpu()];
+
+ put_cpu();
+ if (running) {
+ QueueDelayedWork(dispatchWQ, work, 0);
+ }
+}
+
+
+/**
+ * @brief Default delayed work callback function implementation.
+ * Calls the input function specified at initialization.
+ * @param[in,out] work work item.
+ */
+
+static void
+DispatchWrapper(CommOSWork *work)
+{
+ unsigned int misses;
+
+ for (misses = 0; running && (misses < dispatchMaxCycles); ) {
+ /* We run for at most dispatchMaxCycles worth of channel no-ops. */
+
+ if (!dispatch()) {
+ /* No useful work was done, on any of the channels. */
+
+ misses++;
+ if ((misses % 32) == 0) {
+ CommOS_Yield();
+ }
+ } else {
+ misses = 0;
+ }
+ }
+
+ if (running &&
+ (work >= &dispatchWorks[0]) &&
+ (work <= &dispatchWorks[NR_CPUS - 1])) {
+ /*
+ * If still running _and_ this was a regular, time-based run, then
+ * re-arm the timer.
+ */
+
+ QueueDelayedWork(dispatchWQ, work, dispatchInterval);
+ }
+}
+
+
+/**
+ * @brief Initializes work item with specified callback function.
+ * @param[in,out] work work queue to initialize.
+ * @param func work item to initialize the queue with.
+ */
+
+void
+CommOS_InitWork(CommOSWork *work,
+ CommOSWorkFunc func)
+{
+ INIT_DELAYED_WORK(work, (work_func_t)func);
+}
+
+
+/**
+ * @brief Flush execution of a work item
+ * @param{in,out] work work item to dequeue
+ */
+void
+CommOS_FlushAIOWork(CommOSWork *work)
+{
+ if (aioWQ && work) {
+ FlushDelayedWork(work);
+ }
+}
+
+
+/**
+ * @brief Queue a work item to the AIO workqueue.
+ * @param[in,out] work work item to enqueue.
+ * @return zero if work enqueued, non-zero otherwise.
+ */
+
+int
+CommOS_ScheduleAIOWork(CommOSWork *work)
+{
+ if (running && aioWQ && work) {
+ return QueueDelayedWork(aioWQ, work, 0);
+ }
+ return -1;
+}
+
+
+/**
+ * @brief Initializes the base IO system.
+ * @param dispatchTaskName dispatch thread(s) name.
+ * @param dispatchFunc dispatch function.
+ * @param intervalMillis periodic interval in milliseconds to call dispatch.
+ * The floor is 1 jiffy, regardless of how small intervalMillis is
+ * @param maxCycles number of cycles to do adaptive polling before scheduling.
+ * The maximum number of cycles is DISPATCH_MAX_CYCLES.
+ * @param aioTaskName AIO thread(s) name. If NULL, AIO threads aren't started.
+ * @return zero is successful, -1 otherwise.
+ * @sideeffects Dispatch threads, and if applicable, AIO threads are started.
+ */
+
+int
+CommOS_StartIO(const char *dispatchTaskName, // IN
+ CommOSDispatchFunc dispatchFunc, // IN
+ unsigned int intervalMillis, // IN
+ unsigned int maxCycles, // IN
+ const char *aioTaskName) // IN
+{
+ int rc;
+ int cpu;
+
+ if (running) {
+ CommOS_Debug(("%s: I/O tasks already running.\n", __FUNCTION__));
+ return 0;
+ }
+
+ /*
+ * OK, let's test the handler against NULL. Though, the whole concept
+ * of checking for NULL pointers, outside cases where NULL is meaningful
+ * to the implementation, is relatively useless: garbage, random pointers
+ * rarely happen to be all-zeros.
+ */
+
+ if (!dispatchFunc) {
+ CommOS_Log(("%s: a NULL Dispatch handler was passed.\n", __FUNCTION__));
+ return -1;
+ }
+ dispatch = dispatchFunc;
+
+ if (intervalMillis == 0) {
+ intervalMillis = 4;
+ }
+ if ((dispatchInterval = msecs_to_jiffies(intervalMillis)) < 1) {
+ dispatchInterval = 1;
+ }
+ if (maxCycles > DISPATCH_MAX_CYCLES) {
+ dispatchMaxCycles = DISPATCH_MAX_CYCLES;
+ } else if (maxCycles > 0) {
+ dispatchMaxCycles = maxCycles;
+ }
+ CommOS_Debug(("%s: Interval millis %u (jif:%u).\n", __FUNCTION__,
+ intervalMillis, dispatchInterval));
+ CommOS_Debug(("%s: Max cycles %u.\n", __FUNCTION__, dispatchMaxCycles));
+
+ numCpus = num_present_cpus();
+ dispatchWQ = CreateWorkqueue(dispatchTaskName);
+ if (!dispatchWQ) {
+ CommOS_Log(("%s: Couldn't create %s task(s).\n", __FUNCTION__,
+ dispatchTaskName));
+ return -1;
+ }
+
+ if (aioTaskName) {
+ aioWQ = CreateWorkqueue(aioTaskName);
+ if (!aioWQ) {
+ CommOS_Log(("%s: Couldn't create %s task(s).\n", __FUNCTION__,
+ aioTaskName));
+ DestroyWorkqueue(dispatchWQ);
+ return -1;
+ }
+ } else {
+ aioWQ = NULL;
+ }
+
+ running = 1;
+ for (cpu = 0; cpu < numCpus; cpu++) {
+ CommOS_InitWork(&dispatchWorksNow[cpu], DispatchWrapper);
+ CommOS_InitWork(&dispatchWorks[cpu], DispatchWrapper);
+ rc = QueueDelayedWorkOn(cpu, dispatchWQ,
+ &dispatchWorks[cpu],
+ dispatchInterval);
+ if (rc != 0) {
+ CommOS_StopIO();
+ return -1;
+ }
+ }
+ CommOS_Log(("%s: Created I/O task(s) successfully.\n", __FUNCTION__));
+ return 0;
+}
+
+
+/**
+ * @brief Stops the base IO system.
+ * @sideeffects Dispatch threads, and if applicable, AIO threads are stopped.
+ */
+
+void
+CommOS_StopIO(void)
+{
+ int cpu;
+
+ if (running) {
+ running = 0;
+ if (aioWQ) {
+ FlushWorkqueue(aioWQ);
+ DestroyWorkqueue(aioWQ);
+ aioWQ = NULL;
+ }
+ FlushWorkqueue(dispatchWQ);
+ for (cpu = 0; cpu < numCpus; cpu++) {
+ WaitForDelayedWork(&dispatchWorksNow[cpu]);
+ WaitForDelayedWork(&dispatchWorks[cpu]);
+ }
+ DestroyWorkqueue(dispatchWQ);
+ dispatchWQ = NULL;
+ CommOS_Log(("%s: I/O tasks stopped.\n", __FUNCTION__));
+ }
+}