]> git.ipfire.org Git - thirdparty/open-vm-tools.git/commitdiff
Linux HGFS: avoid unnecessary copying of buffers
authorVMware, Inc <>
Thu, 15 Oct 2009 21:31:03 +0000 (14:31 -0700)
committerMarcelo Vanzin <mvanzin@vmware.com>
Thu, 15 Oct 2009 21:31:03 +0000 (14:31 -0700)
Instead of allocating additional memory chunk when sending over
TCP/Vsock channel and using local receiving buffer in TCP/Vsock
receiver thread implement per-transport memory allocation that
would allow allocating all necessary memory up-front. This also
lays ground work for allocating variable-size requests.

Additionally rework receiving thread logic so that we don't have
to wait for receive operation to time our before we can suspend
or shutdown TCP/Vsock channel.

Also move most of the duplicated channel handling logic in
generic transport code and reduce amount of locks (we really
only need channel lock).

Signed-off-by: Marcelo Vanzin <mvanzin@vmware.com>
open-vm-tools/modules/linux/vmhgfs/bdhandler.c
open-vm-tools/modules/linux/vmhgfs/filesystem.c
open-vm-tools/modules/linux/vmhgfs/module.c
open-vm-tools/modules/linux/vmhgfs/module.h
open-vm-tools/modules/linux/vmhgfs/request.c
open-vm-tools/modules/linux/vmhgfs/request.h
open-vm-tools/modules/linux/vmhgfs/tcp.c
open-vm-tools/modules/linux/vmhgfs/transport.c
open-vm-tools/modules/linux/vmhgfs/transport.h

index 0111e4df596e4e9779d5f07cca75f2c249c71f18..935c8a92e61156112269500190e4dfdb329bb3fc 100644 (file)
@@ -37,8 +37,6 @@
 #include "transport.h"
 #include "vm_assert.h"
 
-HgfsTransportChannel bdChannel;
-
 
 /*
  *-----------------------------------------------------------------------------
@@ -61,29 +59,14 @@ HgfsBdChannelOpen(HgfsTransportChannel *channel) // IN: Channel
 {
    Bool ret = FALSE;
 
-   compat_mutex_lock(&channel->connLock);
-   switch (channel->status) {
-   case HGFS_CHANNEL_UNINITIALIZED:
-      ret = FALSE;
-      break;
-   case HGFS_CHANNEL_CONNECTED:
+   ASSERT(channel->status == HGFS_CHANNEL_NOTCONNECTED);
+
+   if (HgfsBd_OpenBackdoor((RpcOut **)&channel->priv)) {
+      LOG(8, ("VMware hgfs: %s: backdoor opened.\n", __func__));
       ret = TRUE;
-      break;
-   case HGFS_CHANNEL_NOTCONNECTED:
-      if (HgfsBd_OpenBackdoor((RpcOut **)&channel->priv)) {
-         LOG(8, ("VMware hgfs: %s: backdoor opened.\n", __func__));
-         bdChannel.status = HGFS_CHANNEL_CONNECTED;
-         ret = TRUE;
-         ASSERT(channel->priv != NULL);
-      } else {
-         ret = FALSE;
-      }
-      break;
-   default:
-      ASSERT(0); /* Not reached. */
+      ASSERT(channel->priv != NULL);
    }
 
-   compat_mutex_unlock(&channel->connLock);
    return ret;
 }
 
@@ -107,18 +90,51 @@ HgfsBdChannelOpen(HgfsTransportChannel *channel) // IN: Channel
 static void
 HgfsBdChannelClose(HgfsTransportChannel *channel) // IN: Channel
 {
-   compat_mutex_lock(&channel->connLock);
-   if (channel->status == HGFS_CHANNEL_CONNECTED) {
-      ASSERT(channel->priv != NULL);
-      HgfsBd_CloseBackdoor((RpcOut **)&channel->priv);
-      ASSERT(channel->priv == NULL);
-      channel->status = HGFS_CHANNEL_NOTCONNECTED;
-   }
-   compat_mutex_unlock(&channel->connLock);
+   ASSERT(channel->priv != NULL);
+
+   HgfsBd_CloseBackdoor((RpcOut **)&channel->priv);
+   ASSERT(channel->priv == NULL);
+
    LOG(8, ("VMware hgfs: %s: backdoor closed.\n", __func__));
 }
 
 
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * HgfsBdChannelAllocate --
+ *
+ *      Allocate request uin the way that is suitable for sending through
+ *      backdoor.
+ *
+ * Results:
+ *      NULL on failure; otherwise address of the new request.
+ *
+ * Side effects:
+ *      None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static HgfsReq *
+HgfsBdChannelAllocate(size_t payloadSize) // IN: size of requests payload
+{
+   HgfsReq *req;
+
+   req = kmalloc(sizeof(*req) + HGFS_SYNC_REQREP_CLIENT_CMD_LEN + payloadSize,
+                 GFP_KERNEL);
+   if (likely(req)) {
+      /* Setup the packet prefix. */
+      memcpy(req->buffer, HGFS_SYNC_REQREP_CLIENT_CMD,
+             HGFS_SYNC_REQREP_CLIENT_CMD_LEN);
+
+      req->payload = req->buffer + HGFS_SYNC_REQREP_CLIENT_CMD_LEN;
+   }
+
+   return req;
+}
+
+
 /*
  *----------------------------------------------------------------------
  *
@@ -147,62 +163,24 @@ HgfsBdChannelSend(HgfsTransportChannel *channel, // IN: Channel
    ASSERT(req->state == HGFS_REQ_STATE_UNSENT);
    ASSERT(req->payloadSize <= HGFS_PACKET_MAX);
 
-   compat_mutex_lock(&channel->connLock);
-
-   if (channel->status != HGFS_CHANNEL_CONNECTED) {
-      LOG(6, (KERN_DEBUG "VMware hgfs: %s: Backdoor not opened\n", __func__));
-      compat_mutex_unlock(&channel->connLock);
-      return -ENOTCONN;
-   }
-
-   payloadSize = req->payloadSize;
    LOG(8, ("VMware hgfs: %s: backdoor sending.\n", __func__));
+   payloadSize = req->payloadSize;
    ret = HgfsBd_Dispatch(channel->priv, HGFS_REQ_PAYLOAD(req), &payloadSize,
                          &replyPacket);
    if (ret == 0) {
       LOG(8, ("VMware hgfs: %s: Backdoor reply received.\n", __func__));
       /* Request sent successfully. Copy the reply and wake the client. */
       ASSERT(replyPacket);
-      HgfsCompleteReq(req, replyPacket, payloadSize);
-   } else {
-      channel->priv = NULL;
-      channel->status = HGFS_CHANNEL_NOTCONNECTED;
+      ASSERT(payloadSize <= req->bufferSize);
+      memcpy(HGFS_REQ_PAYLOAD(req), replyPacket, payloadSize);
+      req->payloadSize = payloadSize;
+      HgfsCompleteReq(req);
    }
-   compat_mutex_unlock(&channel->connLock);
 
    return ret;
 }
 
 
-/*
- *----------------------------------------------------------------------
- *
- * HgfsBdChannelExit --
- *
- *     Tear down the channel.
- *
- * Results:
- *     None
- *
- * Side effects:
- *     None
- *
- *----------------------------------------------------------------------
- */
-
-static void
-HgfsBdChannelExit(HgfsTransportChannel *channel)  // IN
-{
-   compat_mutex_lock(&channel->connLock);
-   if (channel->priv != NULL) {
-      HgfsBd_CloseBackdoor((RpcOut **)&channel->priv);
-      ASSERT(channel->priv == NULL);
-   }
-   channel->status = HGFS_CHANNEL_UNINITIALIZED;
-   compat_mutex_unlock(&channel->connLock);
-}
-
-
 /*
  *----------------------------------------------------------------------
  *
@@ -222,14 +200,15 @@ HgfsBdChannelExit(HgfsTransportChannel *channel)  // IN
 HgfsTransportChannel*
 HgfsGetBdChannel(void)
 {
-   bdChannel.name = "backdoor";
-   bdChannel.ops.open = HgfsBdChannelOpen;
-   bdChannel.ops.close = HgfsBdChannelClose;
-   bdChannel.ops.send = HgfsBdChannelSend;
-   bdChannel.ops.recv = NULL;
-   bdChannel.ops.exit = HgfsBdChannelExit;
-   bdChannel.priv = NULL;
-   compat_mutex_init(&bdChannel.connLock);
-   bdChannel.status = HGFS_CHANNEL_NOTCONNECTED;
-   return &bdChannel;
+   static HgfsTransportChannel channel;
+
+   channel.name = "backdoor";
+   channel.ops.open = HgfsBdChannelOpen;
+   channel.ops.close = HgfsBdChannelClose;
+   channel.ops.allocate = HgfsBdChannelAllocate;
+   channel.ops.send = HgfsBdChannelSend;
+   channel.priv = NULL;
+   channel.status = HGFS_CHANNEL_NOTCONNECTED;
+
+   return &channel;
 }
index 3e3ddeff04cfb2f90553f7fbbe90eb5892f1bf95..645b57e76be1b02364a5118c403410b2d99b19c1 100644 (file)
@@ -72,7 +72,6 @@
 spinlock_t hgfsBigLock = SPIN_LOCK_UNLOCKED;
 
 /* Other variables. */
-compat_kmem_cache *hgfsReqCache = NULL;
 compat_kmem_cache *hgfsInodeCache = NULL;
 
 /* Global protocol version switch. */
@@ -621,17 +620,6 @@ HgfsInitFileSystem(void)
    /* Initialize primitives. */
    HgfsResetOps();
 
-   /* Setup the request slab allocator. */
-   hgfsReqCache = compat_kmem_cache_create("hgfsReqCache",
-                                           sizeof (HgfsReq),
-                                           0,
-                                           SLAB_HWCACHE_ALIGN,
-                                           NULL);
-   if (hgfsReqCache == NULL) {
-      printk(KERN_WARNING "VMware hgfs: failed to create request allocator\n");
-      goto error_caches;
-   }
-
    /* Setup the inode slab allocator. */
    hgfsInodeCache = compat_kmem_cache_create("hgfsInodeCache",
                                              sizeof (HgfsInodeInfo),
@@ -640,7 +628,7 @@ HgfsInitFileSystem(void)
                                              HgfsInodeCacheCtor);
    if (hgfsInodeCache == NULL) {
       printk(KERN_WARNING "VMware hgfs: failed to create inode allocator\n");
-      goto error_caches;
+      return FALSE;
    }
 
    /* Initialize the transport. */
@@ -652,22 +640,16 @@ HgfsInitFileSystem(void)
     */
    if (register_filesystem(&hgfsType)) {
       printk(KERN_WARNING "VMware hgfs: failed to register filesystem\n");
-      goto error_caches;
+      kmem_cache_destroy(hgfsInodeCache);
+      return FALSE;
    }
    LOG(4, (KERN_DEBUG "VMware hgfs: Module Loaded\n"));
+
 #ifdef HGFS_ENABLE_WRITEBACK
    LOG(4, (KERN_DEBUG "VMware hgfs: writeback cache enabled\n"));
 #endif
-   return TRUE;
 
-error_caches:
-   if (hgfsInodeCache != NULL) {
-      kmem_cache_destroy(hgfsInodeCache);
-   }
-   if (hgfsReqCache != NULL) {
-      kmem_cache_destroy(hgfsReqCache);
-   }
-   return FALSE;
+   return TRUE;
 }
 
 
@@ -692,14 +674,6 @@ HgfsCleanupFileSystem(void)
 {
    Bool success = TRUE;
 
-/* FIXME: Check actual kernel version when RR's modules went in */
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 45)
-   if (MOD_IN_USE) {
-      printk(KERN_WARNING "VMware hgfs: filesystem in use, removal failed\n");
-      success = FALSE;
-   }
-#endif
-
   /*
    * Unregister the filesystem. This should be the first thing we do in
    * the module cleanup code.
@@ -714,7 +688,6 @@ HgfsCleanupFileSystem(void)
 
    /* Destroy the inode and request slabs. */
    kmem_cache_destroy(hgfsInodeCache);
-   kmem_cache_destroy(hgfsReqCache);
 
    LOG(4, (KERN_DEBUG "VMware hgfs: Module Unloaded\n"));
    return success;
index 2490f8c3ef4975aeb1149bbb6dedf6ddc5e771e8..39a2cfaa0d423ce121803cf13061a954cd50266d 100644 (file)
  */
 
 int LOGLEVEL_THRESHOLD = 4;
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 9)
 module_param(LOGLEVEL_THRESHOLD, int, 0444);
-#else
-MODULE_PARM(LOGLEVEL_THRESHOLD, "i");
-#endif
-
 MODULE_PARM_DESC(LOGLEVEL_THRESHOLD, "Set verbosity (0 means no log, 10 means very verbose, 4 is default)");
 #endif
 
-char *HOST_IP = NULL;
-int HOST_PORT = 2000; /* Defaulted to 2000. */
-int HOST_VSOCKET_PORT = 0; /* Disabled by default. */
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 9)
-module_param(HOST_IP, charp, 0444);
-module_param(HOST_PORT, int, 0444);
-module_param(HOST_VSOCKET_PORT, int, 0444);
-#else
-MODULE_PARM(HOST_IP, "s");
-MODULE_PARM(HOST_PORT, "i");
-MODULE_PARM(HOST_VSOCKET_PORT, "i");
-#endif
-
 /* Module information. */
 MODULE_AUTHOR("VMware, Inc.");
 MODULE_DESCRIPTION("VMware Host/Guest File System");
index faeaaa7ed58e460b114a05b1d021775f90ae33b8..5423ef84a74228b96008e5b03404ee775017dcdb 100644 (file)
@@ -53,10 +53,6 @@ extern int LOGLEVEL_THRESHOLD;
 #define LOG(level, args)
 #endif
 
-extern char *HOST_IP;
-extern int HOST_PORT;
-extern int HOST_VSOCKET_PORT;
-
 /* Blocksize to be set in superblock. (XXX how is this used?) */
 #define HGFS_BLOCKSIZE 1024
 
@@ -231,7 +227,6 @@ extern struct file_operations HgfsDirFileOperations;
 extern struct address_space_operations HgfsAddressSpaceOperations;
 
 /* Other global state. */
-extern compat_kmem_cache *hgfsReqCache;
 extern compat_kmem_cache *hgfsInodeCache;
 
 extern HgfsOp hgfsVersionOpen;
index 344e789078f7cbd1c5b0a7b70535ce042d850461..0bd6cb8806a09f6108d298941455c7e8410622dc 100644 (file)
 #include "fsutil.h"
 #include "vm_assert.h"
 
-static uint64 hgfsIdCounter = 0;
-static spinlock_t hgfsIdLock = SPIN_LOCK_UNLOCKED;
+/*
+ *----------------------------------------------------------------------
+ *
+ * HgfsRequestInit --
+ *
+ *    Initializes new request structure.
+ *
+ * Results:
+ *    None
+ *
+ * Side effects:
+ *    None
+ *
+ *----------------------------------------------------------------------
+ */
 
+static void
+HgfsRequestInit(HgfsReq *req,       // IN: request to initialize
+                size_t bufferSize,  // Size of the buffer allocated with request
+                int requestId)      // IN: ID assigned to the request
+{
+   ASSERT(req);
+
+   kref_init(&req->kref);
+   INIT_LIST_HEAD(&req->list);
+   init_waitqueue_head(&req->queue);
+   req->id = requestId;
+   req->payloadSize = 0;
+   req->bufferSize = bufferSize;
+   req->state = HGFS_REQ_STATE_ALLOCATED;
+}
 
 /*
  *----------------------------------------------------------------------
  *
  * HgfsGetNewRequest --
  *
- *    Get a new request structure off the free list and initialize it.
+ *    Allocates and initializes new request structure.
  *
  * Results:
  *    On success the new struct is returned with all fields
@@ -65,28 +93,60 @@ static spinlock_t hgfsIdLock = SPIN_LOCK_UNLOCKED;
 HgfsReq *
 HgfsGetNewRequest(void)
 {
-   HgfsReq *req = NULL;
+   static atomic_t hgfsIdCounter = ATOMIC_INIT(0);
+   HgfsReq *req;
 
-   req = kmem_cache_alloc(hgfsReqCache, GFP_KERNEL);
+   req = HgfsTransportAllocateRequest(HGFS_PACKET_MAX);
    if (req == NULL) {
-      LOG(4, (KERN_DEBUG "VMware hgfs: HgfsGetNewRequest: "
-              "can't allocate memory\n"));
+      LOG(4, (KERN_DEBUG "VMware hgfs: %s: can't allocate memory\n", __func__));
       return NULL;
    }
-   INIT_LIST_HEAD(&req->list);
-   init_waitqueue_head(&req->queue);
-   req->payloadSize = 0;
-   req->state = HGFS_REQ_STATE_ALLOCATED;
-   /* Setup the packet prefix. */
-   memcpy(req->packet, HGFS_SYNC_REQREP_CLIENT_CMD,
-          HGFS_SYNC_REQREP_CLIENT_CMD_LEN);
-   spin_lock(&hgfsIdLock);
-   req->id = hgfsIdCounter;
-   hgfsIdCounter++;
-   spin_unlock(&hgfsIdLock);
+
+   HgfsRequestInit(req, HGFS_PACKET_MAX,
+                   atomic_inc_return(&hgfsIdCounter) - 1);
+
    return req;
 }
 
+/*
+ *----------------------------------------------------------------------
+ *
+ * HgfsCopyRequest --
+ *
+ *    Allocates and initializes new request structure and copies
+ *    existing request into it.
+ *
+ * Results:
+ *    On success the new struct is returned with all fields
+ *    initialized. Returns NULL on failure.
+ *
+ * Side effects:
+ *    None
+ *
+ *----------------------------------------------------------------------
+ */
+
+HgfsReq *
+HgfsCopyRequest(HgfsReq *req)   // IN: request to be copied
+{
+   HgfsReq *newReq;
+
+   ASSERT(req);
+
+   newReq = HgfsTransportAllocateRequest(req->bufferSize);
+   if (newReq == NULL) {
+      LOG(4, (KERN_DEBUG "VMware hgfs: %s: can't allocate memory\n", __func__));
+      return NULL;
+   }
+
+   HgfsRequestInit(newReq, req->bufferSize, req->id);
+
+   /* Copy payload from the original request. */
+   newReq->payloadSize = req->payloadSize;
+   memcpy(newReq->payload, req->payload, req->payloadSize);
+
+   return newReq;
+}
 
 /*
  *----------------------------------------------------------------------
@@ -119,16 +179,16 @@ HgfsSendRequest(HgfsReq *req)       // IN/OUT: Outgoing request
    ret = HgfsTransportSendRequest(req);
    LOG(8, (KERN_DEBUG "VMware hgfs: HgfsSendRequest: request finished, "
            "return %d\n", ret));
+
    return ret;
 }
 
-
 /*
  *----------------------------------------------------------------------
  *
- * HgfsFreeRequest --
+ * HgfsRequestFreeMemory --
  *
- *    Free an HGFS request.
+ *    Frees memory allocated for a request.
  *
  * Results:
  *    None
@@ -139,14 +199,68 @@ HgfsSendRequest(HgfsReq *req)       // IN/OUT: Outgoing request
  *----------------------------------------------------------------------
  */
 
+static void HgfsRequestFreeMemory(struct kref *kref)
+{
+   HgfsReq *req = container_of(kref, HgfsReq, kref);
+
+   LOG(10, (KERN_DEBUG "VMware hgfs: %s: freeing request %d\n",
+            __func__, req->id));
+   kfree(req);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HgfsRequestPutRef --
+ *
+ *    Decrease reference count of HGFS request.
+ *
+ * Results:
+ *    None
+ *
+ * Side effects:
+ *    May cause request to be destroyed.
+ *
+ *----------------------------------------------------------------------
+ */
+
 void
-HgfsFreeRequest(HgfsReq *req) // IN: Request to free
+HgfsRequestPutRef(HgfsReq *req) // IN: Request
 {
-   ASSERT(hgfsReqCache);
+   if (req) {
+      LOG(10, (KERN_DEBUG "VMware hgfs: %s: request %d\n",
+               __func__, req->id));
+      kref_put(&req->kref, HgfsRequestFreeMemory);
+   }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HgfsRequestGetRef --
+ *
+ *    Increment reference count of HGFS request.
+ *
+ * Results:
+ *    Pointer to the same HGFS request.
+ *
+ * Side effects:
+ *    None
+ *
+ *----------------------------------------------------------------------
+ */
 
-   if (req != NULL) {
-      kmem_cache_free(hgfsReqCache, req);
+HgfsReq *
+HgfsRequestGetRef(HgfsReq *req) // IN: Request
+{
+   if (req) {
+      LOG(10, (KERN_DEBUG "VMware hgfs: %s: request %d\n",
+               __func__, req->id));
+      kref_get(&req->kref);
    }
+
+   return req;
 }
 
 
@@ -183,8 +297,7 @@ HgfsReplyStatus(HgfsReq *req)  // IN
  *
  * HgfsCompleteReq --
  *
- *    Copies the reply packet into the request structure and wakes up
- *    the associated client.
+ *    Marks request as completed and wakes up sender.
  *
  * Results:
  *    None
@@ -196,20 +309,41 @@ HgfsReplyStatus(HgfsReq *req)  // IN
  */
 
 void
-HgfsCompleteReq(HgfsReq *req,       // IN: Request
-                char const *reply,  // IN: Reply packet
-                size_t replySize)   // IN: Size of reply packet
+HgfsCompleteReq(HgfsReq *req)       // IN: Request
 {
    ASSERT(req);
-   ASSERT(reply);
-   ASSERT(replySize <= HGFS_PACKET_MAX);
 
-   memcpy(HGFS_REQ_PAYLOAD(req), reply, replySize);
-   req->payloadSize = replySize;
    req->state = HGFS_REQ_STATE_COMPLETED;
-   if (!list_empty(&req->list)) {
-      list_del_init(&req->list);
-   }
    /* Wake up the client process waiting for the reply to this request. */
    wake_up(&req->queue);
 }
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HgfsFailReq --
+ *
+ *    Marks request as failed and calls HgfsCompleteReq to wake up
+ *    sender.
+ *
+ * Results:
+ *    None
+ *
+ * Side effects:
+ *    None
+ *
+ *----------------------------------------------------------------------
+ */
+
+void HgfsFailReq(HgfsReq *req,   // IN: erequest to be marked failed
+                 int error)     // IN: error code
+{
+   HgfsReply *reply = req->payload;
+
+   reply->id = req->id;
+   reply->status = error;
+
+   req->payloadSize = sizeof *reply;
+   HgfsCompleteReq(req);
+}
index 1b3edeae2e07e6dd8aab69ddc57e6e96557a9094..e4a9bdebf8df694dcfdf7e4604fb1ae4fb5fcf10 100644 (file)
@@ -38,7 +38,7 @@
 #include "vm_basic_types.h"
 
 /* Macros for accessing the payload portion of the HGFS request packet. */
-#define HGFS_REQ_PAYLOAD(hgfsReq) ((hgfsReq)->packet + HGFS_CLIENT_CMD_LEN)
+#define HGFS_REQ_PAYLOAD(hgfsReq) ((hgfsReq)->payload)
 
 /* XXX: Needs change when VMCI is supported. */
 #define HGFS_REQ_PAYLOAD_V3(hgfsReq) (HGFS_REQ_PAYLOAD(hgfsReq) + sizeof(HgfsRequest))
@@ -73,7 +73,7 @@ typedef enum {
    HGFS_REQ_STATE_ALLOCATED,
    HGFS_REQ_STATE_UNSENT,
    HGFS_REQ_STATE_SUBMITTED,
-   HGFS_REQ_STATE_COMPLETED,
+   HGFS_REQ_STATE_COMPLETED,  /* Both header and payload were received. */
 } HgfsState;
 
 /*
@@ -81,9 +81,15 @@ typedef enum {
  */
 typedef struct HgfsReq {
 
+   /* Reference count */
+   struct kref kref;
+
    /* Links to place the object on various lists. */
    struct list_head list;
 
+   /* ID of the transport (its address) */
+   void *transportId;
+
    /*
     * When clients wait for the reply to a request, they'll wait on this
     * wait queue.
@@ -96,23 +102,35 @@ typedef struct HgfsReq {
    /* ID of this request */
    uint32 id;
 
+   /* Pointer to payload in the buffer */
+   void *payload;
+
    /* Total size of the payload.*/
    size_t payloadSize;
 
+   /*
+    * Size of the data buffer (below), not including size of chunk
+    * used by transport. Must be enough to hold both request and
+    * reply (but not at the same time).
+    */
+   size_t bufferSize;
+
    /*
     * Packet of data, for both incoming and outgoing messages.
     * Include room for the command.
     */
-   char packet[HGFS_PACKET_MAX + HGFS_CLIENT_CMD_LEN];
+   unsigned char buffer[];
 } HgfsReq;
 
 /* Public functions (with respect to the entire module). */
 HgfsReq *HgfsGetNewRequest(void);
+HgfsReq *HgfsCopyRequest(HgfsReq *req);
 int HgfsSendRequest(HgfsReq *req);
-void HgfsFreeRequest(HgfsReq *req);
+HgfsReq *HgfsRequestGetRef(HgfsReq *req);
+void HgfsRequestPutRef(HgfsReq *req);
+#define HgfsFreeRequest(req)  HgfsRequestPutRef(req)
 HgfsStatus HgfsReplyStatus(HgfsReq *req);
-void HgfsCompleteReq(HgfsReq *req,
-                     char const *reply,
-                     size_t replySize);
+void HgfsCompleteReq(HgfsReq *req);
+void HgfsFailReq(HgfsReq *req, int error);
 
 #endif // _HGFS_DRIVER_REQUEST_H_
index 82743a104a46d7af3b74c3f31ac6c53efd025cc8..a4e5f46428286e020dab93c7e76cd8048bbe2d59 100644 (file)
 #include "compat_sched.h"
 #include "compat_sock.h"
 #include "compat_timer.h"
+
 #include "vm_assert.h"
 
 #include "hgfsProto.h"
-
 #include "hgfsDevLinux.h"
 #include "module.h"
 #include "tcp.h"
 
+static char *HOST_IP;
+module_param(HOST_IP, charp, 0444);
+
+static int HOST_PORT = 2000; /* Defaulted to 2000. */
+module_param(HOST_PORT, int, 0444);
+
+static int HOST_VSOCKET_PORT = 0; /* Disabled by default. */
+module_param(HOST_VSOCKET_PORT, int, 0444);
+
+#define INCLUDE_VSOCKETS
+
 #ifdef INCLUDE_VSOCKETS
 
 #include "vmci_defs.h"
  *  Stubs for undefined functions.
  */
 
-int
-VMCISock_GetAFValue (void)
-{
-   return -1;
-}
-
 void
 VMCISock_KernelDeregister(void)
 {
@@ -81,178 +86,349 @@ VMCISock_KernelRegister(void)
 
 #endif
 
-
-/* Socket recv timeout value, counted in HZ. */
-#define HGFS_SOCKET_RECV_TIMEOUT 10
+/* Indicates that data is ready to be received */
+#define HGFS_REQ_THREAD_RECV        (1 << 0)
 
 /* Recv states for the recvBuffer. */
 typedef enum {
-   HGFS_TCP_CONN_RECV_HEADER,
-   HGFS_TCP_CONN_RECV_DATA,
-} HgfsTcpRecvState;
+   HGFS_CONN_RECV_SOCK_HDR,    /* Waiting for socket header */
+   HGFS_CONN_RECV_REP_HDR,     /* Waiting for HgfsReply header */
+   HGFS_CONN_RECV_REP_PAYLOAD, /* Waiting for reply payload */
+} HgfsSocketRecvState;
 
 /* HGFS receive buffer. */
-typedef struct HgfsTcpRecvBuffer {
+typedef struct HgfsSocketRecvBuffer {
    HgfsSocketHeader header;            /* Buffer for receiving header. */
-   char data[HGFS_PACKET_MAX];         /* Buffer for receiving packet. */
-   HgfsTcpRecvState state;             /* Reply receive state. */
+   HgfsReply reply;                    /* Buffer for receiving reply */
+   HgfsReq *req;                       /* Request currently being received. */
+   char sink[HGFS_PACKET_MAX];         /* Buffer for data to be discarded. */
+   HgfsSocketRecvState state;          /* Reply receive state. */
    int len;                            /* Number of bytes to receive. */
    char *buf;                          /* Pointer to the buffer. */
-} HgfsTcpRecvBuffer;
+} HgfsSocketRecvBuffer;
+
+static HgfsSocketRecvBuffer recvBuffer;   /* Accessed only by the recv thread. */
 
-static HgfsTcpRecvBuffer recvBuffer;   /* Accessed only by the recv thread. */
 static struct task_struct *recvThread; /* The recv thread. */
+static DECLARE_WAIT_QUEUE_HEAD(hgfsRecvThreadWait); /* Wait queue for recv thread. */
+static long hgfsRecvThreadFlags;       /* Used to signal recv data availability. */
+
+static void (*oldSocketDataReady)(struct sock *, int);
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HgfsSocketDataReady --
+ *
+ *     Called when there is data to read on the connected socket.
+ *
+ * Results:
+ *     None.
+ *
+ * Side effects:
+ *     Wakes up the receiving thread.
+ *
+ *----------------------------------------------------------------------
+ */
 
-HgfsTransportChannel tcpChannel;
-HgfsTransportChannel vsocketChannel;
-typedef Bool (*HgfsConnect)(HgfsTransportChannel *);
+static void
+HgfsSocketDataReady(struct sock *sk,   // IN: Server socket
+                    int len)           // IN: Data length
+{
+   LOG(4, (KERN_DEBUG "VMware hgfs: %s: data ready\n", __func__));
 
+   /* Call the original data_ready function. */
+   oldSocketDataReady(sk, len);
 
-/* Private functions. */
-static Bool HgfsTcpChannelOpen(HgfsTransportChannel *);
-static Bool HgfsVSocketChannelOpen(HgfsTransportChannel *);
-static void HgfsSocketChannelClose(HgfsTransportChannel *);
-static int HgfsTcpChannelRecvAsync(HgfsTransportChannel *,
-                                   char **replyPacket,
-                                   size_t *packetSize);
+   /* Wake up the recv thread. */
+   set_bit(HGFS_REQ_THREAD_RECV, &hgfsRecvThreadFlags);
+   wake_up_interruptible(&hgfsRecvThreadWait);
+}
 
 
 /*
  *----------------------------------------------------------------------
  *
- * HgfsTcpReceiveHandler --
+ * HgfsSocketResetRecvBuffer --
  *
- *     Function run in background thread and wait on the data in the
- *     connected channel.
+ *     Reset recv buffer.
  *
  * Results:
- *     Always returns zero.
+ *     None
  *
  * Side effects:
- *     Can be many.
+ *     None
  *
  *----------------------------------------------------------------------
  */
 
-static int
-HgfsTcpReceiveHandler(void *data)
+static void
+HgfsSocketResetRecvBuffer(void)
 {
-   char *receivedPacket = NULL;
-   size_t receivedSize = 0;
-   int ret = 0;
-   HgfsTransportChannel *channel = data;
+   recvBuffer.state = HGFS_CONN_RECV_SOCK_HDR;
+   recvBuffer.req = NULL;
+   recvBuffer.len = sizeof recvBuffer.header;
+   recvBuffer.buf = (char *)&recvBuffer.header;
+}
 
-   LOG(6, (KERN_DEBUG "VMware hgfs: %s: thread started\n", __func__));
 
-   compat_set_freezable();
-   for (;;) {
-      /* Kill yourself if told so. */
-      if (compat_kthread_should_stop()) {
-         LOG(6, (KERN_DEBUG "VMware hgfs: %s: told to exit\n", __func__));
-         break;
-      }
+/*
+ *----------------------------------------------------------------------
+ *
+ * HgfsSocketIsReceiverIdle --
+ *
+ *     Checks whether we are in the middle of receiving a packet.
+ *
+ * Results:
+ *     FALSE if receiving thread is in the middle of receiving packet,
+ *     otherwise TRUE.
+ *
+ * Side effects:
+ *     None
+ *
+ *----------------------------------------------------------------------
+ */
 
-      /* Check for suspend. */
-      if (compat_try_to_freeze()) {
-         LOG(6, (KERN_DEBUG "VMware hgfs: %s: closing transport and exiting "
-                 "the thread after resume.\n", __func__));
-         channel->ops.close(channel);
-         break;
-      }
+static Bool
+HgfsSocketIsReceiverIdle(void)
+{
+   return recvBuffer.state == HGFS_CONN_RECV_SOCK_HDR &&
+          recvBuffer.len == sizeof recvBuffer.header;
+}
 
-      /* Waiting on the data, may be blocked. */
-      ret = HgfsTcpChannelRecvAsync(channel, &receivedPacket, &receivedSize);
 
-      /* The connection is not lost for the following returns, just continue. */
-      if (ret == -EINTR || ret == -ERESTARTSYS || ret == -EAGAIN) {
-         continue;
-      }
+/*
+ *----------------------------------------------------------------------
+ *
+ * HgfsSocketRecvMsg --
+ *
+ *     Receive the message on the socket.
+ *
+ * Results:
+ *     On success returns number of bytes received.
+ *     On failure returns the negative errno.
+ *
+ * Side effects:
+ *     None
+ *
+ *----------------------------------------------------------------------
+ */
 
-      if (ret < 0) {
-         /* The connection is broken. Close it to free up the resources. */
-         channel->ops.close(channel);
-         if (!channel->ops.open(channel)) {
-            /* We are unable to reconnect to the server, exit the thread. */
-            LOG(6, (KERN_DEBUG "VMware hgfs: %s: recv error: %d. Lost the "
-                    "connection to server.\n", __func__, ret));
-            break;
-         }
-      } else if (ret > 0) {
-         /* Process the received packet. */
-         HgfsTransportProcessPacket(receivedPacket, receivedSize);
-      }
-   }
+static int
+HgfsSocketRecvMsg(struct socket *socket,   // IN: TCP socket
+                  char *buffer,            // IN: Buffer to recv the message
+                  size_t bufferLen)        // IN: Buffer length
+{
+   struct iovec iov;
+   struct msghdr msg;
+   int ret;
+   int flags = MSG_DONTWAIT | MSG_NOSIGNAL;
+   mm_segment_t oldfs = get_fs();
 
-   HgfsTransportBeforeExitingRecvThread();
-   LOG(6, (KERN_DEBUG "VMware hgfs: %s: thread exited\n", __func__));
-   recvThread = NULL;
-   return 0;
+   memset(&msg, 0, sizeof msg);
+   msg.msg_flags = flags;
+   msg.msg_iov = &iov;
+   msg.msg_iovlen = 1;
+   iov.iov_base = buffer;
+   iov.iov_len = bufferLen;
+
+   set_fs(KERNEL_DS);
+   ret = sock_recvmsg(socket, &msg, bufferLen, flags);
+   set_fs(oldfs);
+
+   return ret;
 }
 
 
 /*
  *----------------------------------------------------------------------
  *
- * HgfsTcpStopReceivingThread --
+ * HgfsSocketChannelRecvAsync --
  *
- *     Helper function to stop channel handler thread.
+ *     Receive as much data from the socket as possible without blocking.
+ *     Note, that we may return early with just a part of the packet
+ *     received.
  *
  * Results:
- *     None
+ *     On failure returns the negative errno, otherwise 0.
  *
  * Side effects:
- *     None
+ *     Changes state of the receive buffer depending on what part of
+ *     the packet has been received so far.
  *
  *----------------------------------------------------------------------
  */
 
-static void
-HgfsTcpStopReceivingThread(void)
+static int
+HgfsSocketChannelRecvAsync(HgfsTransportChannel *channel) // IN:  Channel
 {
-   if (recvThread && current != recvThread) { /* Don't stop myself. */
-      /* Wake up socket by sending signal. */
-      force_sig(SIGKILL, recvThread);
-      compat_kthread_stop(recvThread);
-      recvThread = NULL;
-      LOG(8, ("VMware hgfs: %s: recv thread stopped.\n", __func__));
+   int ret;
+
+   if (channel->status != HGFS_CHANNEL_CONNECTED) {
+      LOG(6, (KERN_DEBUG "VMware hgfs: %s: Connection lost.\n", __func__));
+      return -ENOTCONN;
    }
+
+   /* We want to read as much data as possible without blocking */
+   for (;;) {
+
+      LOG(10, (KERN_DEBUG "VMware hgfs: %s: receiving %s\n", __func__,
+               recvBuffer.state == HGFS_CONN_RECV_SOCK_HDR ? "header" :
+               recvBuffer.state == HGFS_CONN_RECV_REP_HDR ? "reply" : "data"));
+      ret = HgfsSocketRecvMsg(channel->priv, recvBuffer.buf, recvBuffer.len);
+      LOG(10, (KERN_DEBUG "VMware hgfs: %s: sock_recvmsg returns: %d\n",
+               __func__, ret));
+
+      if (ret <= 0) {
+         break;
+      }
+
+      ASSERT(ret <= recvBuffer.len);
+      recvBuffer.len -= ret;
+      recvBuffer.buf += ret;
+
+      if (recvBuffer.len != 0) {
+         continue;
+      }
+
+      /* Complete segment received. */
+      switch (recvBuffer.state) {
+
+      case HGFS_CONN_RECV_SOCK_HDR:
+         LOG(10, (KERN_DEBUG "VMware hgfs: %s: received packet header\n",
+                  __func__));
+         ASSERT(recvBuffer.header.version == HGFS_SOCKET_VERSION1);
+         ASSERT(recvBuffer.header.size == sizeof recvBuffer.header);
+         ASSERT(recvBuffer.header.status == HGFS_SOCKET_STATUS_SUCCESS);
+
+         recvBuffer.state = HGFS_CONN_RECV_REP_HDR;
+         recvBuffer.len = sizeof recvBuffer.reply;
+         recvBuffer.buf = (char *)&recvBuffer.reply;
+         break;
+
+      case HGFS_CONN_RECV_REP_HDR:
+         LOG(10, (KERN_DEBUG "VMware hgfs: %s: received packet reply\n",
+                  __func__));
+         recvBuffer.req = HgfsTransportGetPendingRequest(recvBuffer.reply.id);
+         if (recvBuffer.req) {
+            ASSERT(recvBuffer.header.packetLen <= recvBuffer.req->bufferSize);
+            recvBuffer.req->payloadSize = recvBuffer.header.packetLen;
+            memcpy(recvBuffer.req->payload, &recvBuffer.reply, sizeof recvBuffer.reply);
+            recvBuffer.buf = recvBuffer.req->payload + sizeof recvBuffer.reply;
+         } else {
+            recvBuffer.buf = recvBuffer.sink;
+         }
+
+         recvBuffer.state = HGFS_CONN_RECV_REP_PAYLOAD;
+         recvBuffer.len = recvBuffer.header.packetLen - sizeof recvBuffer.reply;
+         if (recvBuffer.len)
+            break;
+
+         /* There is no actual payload, fall through */
+
+      case HGFS_CONN_RECV_REP_PAYLOAD:
+         LOG(10, (KERN_DEBUG "VMware hgfs: %s: received packet payload\n",
+                  __func__));
+         if (recvBuffer.req) {
+            HgfsCompleteReq(recvBuffer.req);
+            HgfsRequestPutRef(recvBuffer.req);
+            recvBuffer.req = NULL;
+         }
+         HgfsSocketResetRecvBuffer();
+         break;
+
+      default:
+         ASSERT(0);
+      }
+   }
+
+   return ret;
 }
 
 
 /*
  *----------------------------------------------------------------------
  *
- * HgfsTcpResetRecvBuffer --
+ * HgfsSocketReceiveHandler --
  *
- *     Reset recv buffer.
+ *     Function run in background thread and wait on the data in the
+ *     connected channel.
  *
  * Results:
- *     None
+ *     Always returns zero.
  *
  * Side effects:
- *     None
+ *     Can be many.
  *
  *----------------------------------------------------------------------
  */
 
-static void
-HgfsTcpResetRecvBuffer(void)
+static int
+HgfsSocketReceiveHandler(void *data)
 {
-   recvBuffer.state = HGFS_TCP_CONN_RECV_HEADER;
-   recvBuffer.len = sizeof recvBuffer.header;
-   recvBuffer.buf = (char *)&recvBuffer.header;
+   HgfsTransportChannel *channel = data;
+   int ret;
+
+   LOG(6, (KERN_DEBUG "VMware hgfs: %s: thread started\n", __func__));
+
+   compat_set_freezable();
+
+   for (;;) {
+
+      /* Wait for data to become available */
+      wait_event_interruptible(hgfsRecvThreadWait,
+                  (HgfsSocketIsReceiverIdle() && compat_kthread_should_stop()) ||
+                  test_bit(HGFS_REQ_THREAD_RECV, &hgfsRecvThreadFlags));
+
+      /* Kill yourself if told so. */
+      if (compat_kthread_should_stop()) {
+         LOG(6, (KERN_DEBUG "VMware hgfs: %s: told to exit\n", __func__));
+         break;
+      }
+
+      /* Check for suspend. */
+      if (compat_try_to_freeze()) {
+         LOG(6, (KERN_DEBUG "VMware hgfs: %s: continuing after resume.\n",
+                 __func__));
+         continue;
+      }
+
+      if (test_and_clear_bit(HGFS_REQ_THREAD_RECV, &hgfsRecvThreadFlags)) {
+
+         /* There is some data witing for us, let's read it */
+         ret = HgfsSocketChannelRecvAsync(channel);
+
+         if (ret < 0 && ret != -EINTR && ret != -ERESTARTSYS && ret != -EAGAIN) {
+
+            if (recvBuffer.req) {
+               HgfsFailReq(recvBuffer.req, -EIO);
+               HgfsRequestPutRef(recvBuffer.req);
+               recvBuffer.req = NULL;
+            }
+
+            /* The connection is broken, leave it to the senders to restore it. */
+            HgfsTransportMarkDead();
+         }
+      }
+   }
+
+   LOG(6, (KERN_DEBUG "VMware hgfs: %s: thread exited\n", __func__));
+   recvThread = NULL;
+
+   return 0;
 }
 
 
 /*
  *----------------------------------------------------------------------
  *
- * HgfsTcpConnect --
+ * HgfsCreateTcpSocket --
  *
  *     Connect to HGFS TCP server.
  *
  * Results:
- *     TRUE on success, FALSE on failure.
+ *     NULL on failure; otherwise address of the newly created and
+ *     connected TCP socket.
  *
  * Side effects:
  *     None
@@ -260,55 +436,45 @@ HgfsTcpResetRecvBuffer(void)
  *----------------------------------------------------------------------
  */
 
-static Bool
-HgfsTcpConnect(HgfsTransportChannel *channel)
+static struct socket *
+HgfsCreateTcpSocket(void)
 {
-   int error;
-   struct socket *sock;
+   struct socket *socket;
    struct sockaddr_in addr;
-   Bool ret = FALSE;
-
-   if (channel->priv != NULL) {
-      sock_release(channel->priv);
-      channel->priv = NULL;
-   }
+   int error;
 
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = in_aton(HOST_IP);
    addr.sin_port = htons(HOST_PORT);
 
-   error = compat_sock_create_kern(AF_INET, SOCK_STREAM, IPPROTO_TCP, &sock);
-   if (error >= 0) {
-      error = sock->ops->connect(sock, (struct sockaddr *)&addr,
-                                 sizeof addr, 0);
-      if (error >= 0) {
-         channel->priv = sock;
-         /* Reset receive buffer when a new connection is connected. */
-         HgfsTcpResetRecvBuffer();
-         sock->sk->compat_sk_rcvtimeo = HGFS_SOCKET_RECV_TIMEOUT * HZ;
-         channel->status = HGFS_CHANNEL_CONNECTED;
-         LOG(8, ("%s: tcp channel connected.\n", __func__));
-         ret = TRUE;
-      } else {
-         LOG(8, ("%s: connect failed: %d.\n", __func__, error));
-         sock_release(sock);
-      }
-   } else {
+   error = compat_sock_create_kern(AF_INET, SOCK_STREAM, IPPROTO_TCP, &socket);
+   if (error < 0) {
       LOG(8, ("%s: sock_create_kern failed: %d.\n", __func__, error));
+      return NULL;
    }
-   return ret;
+
+   error = socket->ops->connect(socket, (struct sockaddr *)&addr,
+                                    sizeof addr, 0);
+   if (error < 0) {
+      LOG(8, ("%s: connect failed: %d.\n", __func__, error));
+      sock_release(socket);
+      return NULL;
+   }
+
+   return socket;
 }
 
 
 /*
  *----------------------------------------------------------------------
  *
- * HgfsVSocketConnect --
+ * HgfsCreateVsockSocket --
  *
- *     Connect to HGFS VSOCKET server.
+ *     Connect to HGFS VSocket server.
  *
  * Results:
- *     TRUE on success, FALSE on failure.
+ *     NULL on failure; otherwise address of the newly created and
+ *     connected VSock socket.
  *
  * Side effects:
  *     None
@@ -316,46 +482,38 @@ HgfsTcpConnect(HgfsTransportChannel *channel)
  *----------------------------------------------------------------------
  */
 
-static Bool
-HgfsVSocketConnect(HgfsTransportChannel *channel)
+static struct socket *
+HgfsCreateVsockSocket(void)
 {
 #ifdef INCLUDE_VSOCKETS
-   int error;
-   struct socket *sock;
+   struct socket *socket;
    struct sockaddr_vm addr;
-   int family;
-   Bool ret = FALSE;
+   int family = VMCISock_GetAFValue();
+   int error;
 
    memset(&addr, 0, sizeof addr);
-
-   family = VMCISock_GetAFValue();
-
    addr.svm_family = family;
    addr.svm_cid = VMCI_HOST_CONTEXT_ID;
    addr.svm_port = HOST_VSOCKET_PORT;
 
-   error = compat_sock_create_kern(family, SOCK_STREAM, 0, &sock);
-   if (error >= 0) {
-      error = sock->ops->connect(sock, (struct sockaddr *)&addr,
-                                 sizeof addr, 0);
-      if (error >= 0) {
-         channel->priv = sock;
-         /* Reset receive buffer when a new connection is connected. */
-         HgfsTcpResetRecvBuffer();
-         sock->sk->compat_sk_rcvtimeo = HGFS_SOCKET_RECV_TIMEOUT * HZ;
-         channel->status = HGFS_CHANNEL_CONNECTED;
-         LOG(8, ("%s: vsocket channel connected.\n", __func__));
-         ret = TRUE;
-      } else {
-         LOG(4, ("%s: connect failed: %d.\n", __func__, error));
-         sock_release(sock);
-      }
-   } else {
+   error = compat_sock_create_kern(family, SOCK_STREAM, IPPROTO_TCP, &socket);
+   if (error < 0) {
       LOG(8, ("%s: sock_create_kern failed: %d.\n", __func__, error));
+      return NULL;
    }
-   return ret;
+
+   error = socket->ops->connect(socket, (struct sockaddr *)&addr,
+                                    sizeof addr, 0);
+   if (error < 0) {
+      LOG(8, ("%s: connect failed: %d.\n", __func__, error));
+      sock_release(socket);
+      return NULL;
+   }
+
+   return socket;
+
 #else
-   return FALSE;
+   return NULL;
 #endif
 }
 
@@ -378,48 +536,45 @@ HgfsVSocketConnect(HgfsTransportChannel *channel)
 
 static Bool
 HgfsSocketChannelOpen(HgfsTransportChannel *channel,
-                      HgfsConnect connect)
+                      struct socket *(*create_socket)(void))
 {
-   Bool ret = FALSE;
-
-   compat_mutex_lock(&channel->connLock);
-   switch (channel->status) {
-   case HGFS_CHANNEL_UNINITIALIZED:
-      ret = FALSE;
-      break;
-   case HGFS_CHANNEL_CONNECTED:
-      ret = TRUE;
-      break;
-   case HGFS_CHANNEL_NOTCONNECTED:
-      ret = connect(channel);
-
-      /*
-       * Ensure this function won't race with HgfsTcpChannelRecvAsync, which
-       * is called only by recvThread. If this thread is not the recv thread,
-       * make sure the recv thread is finished before we proceed.
-       */
-      ASSERT(!recvThread || current == recvThread);
-
-      if (ret && !recvThread) {
-         /* Create the recv thread. */
-         recvThread = compat_kthread_run(HgfsTcpReceiveHandler,
-                                         channel, "vmhgfs-rep");
-         if (IS_ERR(recvThread)) {
-            LOG(4, (KERN_ERR "VMware hgfs: %s: failed to create recv thread\n",
-                    __func__));
-            recvThread = NULL;
-            channel->ops.close(channel);
-            ret = FALSE;
-            break;
-         }
-      }
-      break;
-   default:
-      ASSERT(0); /* Not reached. */
+   struct socket *socket;
+
+   ASSERT(channel->status == HGFS_CHANNEL_NOTCONNECTED);
+   ASSERT(!recvThread);
+
+   socket = create_socket();
+   if (socket == NULL)
+      return FALSE;
+
+   /*
+    * Install the new "data ready" handler that will wake up the
+    * receiving thread.
+    */
+   oldSocketDataReady = xchg(&socket->sk->compat_sk_data_ready,
+                             HgfsSocketDataReady);
+
+   /* Reset receive buffer when a new connection is connected. */
+   HgfsSocketResetRecvBuffer();
+
+   channel->priv = socket;
+
+   LOG(8, ("%s: socket channel connected.\n", __func__));
+
+   /* Create the recv thread. */
+   recvThread = compat_kthread_run(HgfsSocketReceiveHandler,
+                                   channel, "vmhgfs-rep");
+   if (IS_ERR(recvThread)) {
+      LOG(4, (KERN_ERR "VMware hgfs: %s: "
+              "failed to create recv thread, err %ld\n",
+              __func__, PTR_ERR(recvThread)));
+      recvThread = NULL;
+      sock_release(channel->priv);
+      channel->priv = NULL;
+      return FALSE;
    }
 
-   compat_mutex_unlock(&channel->connLock);
-   return ret;
+   return TRUE;
 }
 
 
@@ -442,7 +597,7 @@ HgfsSocketChannelOpen(HgfsTransportChannel *channel,
 static Bool
 HgfsTcpChannelOpen(HgfsTransportChannel *channel)
 {
-   return HgfsSocketChannelOpen(channel, HgfsTcpConnect);
+   return HgfsSocketChannelOpen(channel, HgfsCreateTcpSocket);
 }
 
 
@@ -465,38 +620,14 @@ HgfsTcpChannelOpen(HgfsTransportChannel *channel)
 static Bool
 HgfsVSocketChannelOpen(HgfsTransportChannel *channel)
 {
-   return HgfsSocketChannelOpen(channel, HgfsVSocketConnect);
-}
-
-
-/*
- *----------------------------------------------------------------------
- *
- * HgfsSocketChannelCloseWork --
- *
- *     Close the tcp channel in an idempotent way.
- *
- * Results:
- *     None
- *
- * Side effects:
- *     None
- *
- *----------------------------------------------------------------------
- */
+   VMCISock_KernelRegister();
 
-static void
-HgfsTcpChannelCloseWork(HgfsTransportChannel *channel)
-{
-   if (channel->status != HGFS_CHANNEL_CONNECTED) {
-      return;
+   if (!HgfsSocketChannelOpen(channel, HgfsCreateVsockSocket)) {
+      VMCISock_KernelDeregister();
+      return FALSE;
    }
 
-   if (channel->priv != NULL) {
-      sock_release(channel->priv);
-      channel->priv = NULL;
-   }
-   channel->status = HGFS_CHANNEL_NOTCONNECTED;
+   return TRUE;
 }
 
 
@@ -505,7 +636,8 @@ HgfsTcpChannelCloseWork(HgfsTransportChannel *channel)
  *
  * HgfsSocketChannelClose --
  *
- *     See above.
+ *     Closes socket-based channel by closing socket and stopping the
+ *     receiving thread.
  *
  * Results:
  *     None
@@ -520,23 +652,25 @@ static void
 HgfsSocketChannelClose(HgfsTransportChannel *channel)
 {
    /* Stop the recv thread before we change the channel status. */
-   HgfsTcpStopReceivingThread();
-   compat_mutex_lock(&channel->connLock);
-   HgfsTcpChannelCloseWork(channel);
-   compat_mutex_unlock(&channel->connLock);
-   LOG(8, ("VMware hgfs: %s: tcp channel closed.\n", __func__));
+   ASSERT(recvThread != NULL);
+   compat_kthread_stop(recvThread);
+
+   sock_release(channel->priv);
+   channel->priv = NULL;
+
+   LOG(8, ("VMware hgfs: %s: socket channel closed.\n", __func__));
 }
 
+
 /*
  *----------------------------------------------------------------------
  *
- * HgfsTcpRecvMsg --
+ * HgfsTcpChannelClose --
  *
- *     Receive the message on the socket.
+ *     Closes TCP channel.
  *
  * Results:
- *     On success returns number of bytes received.
- *     On failure returns the negative errno.
+ *     None
  *
  * Side effects:
  *     None
@@ -544,44 +678,24 @@ HgfsSocketChannelClose(HgfsTransportChannel *channel)
  *----------------------------------------------------------------------
  */
 
-static int
-HgfsTcpRecvMsg(struct socket *socket,   // IN: TCP socket
-               char *buffer,            // IN: Buffer to recv the message
-               size_t bufferLen)        // IN: Buffer length
+static void
+HgfsTcpChannelClose(HgfsTransportChannel *channel)
 {
-   struct iovec iov;
-   struct msghdr msg;
-   int ret;
-   int flags = 0;
-   mm_segment_t oldfs = get_fs();
+   HgfsSocketChannelClose(channel);
 
-   memset(&msg, 0, sizeof msg);
-   msg.msg_flags = flags;
-   msg.msg_iov = &iov;
-   msg.msg_iovlen = 1;
-   iov.iov_base = buffer;
-   iov.iov_len = bufferLen;
-
-   set_fs(KERNEL_DS);
-   ret = sock_recvmsg(socket, &msg, bufferLen, flags);
-   set_fs(oldfs);
-
-   return ret;
+   LOG(8, ("VMware hgfs: %s: tcp channel closed.\n", __func__));
 }
 
 
 /*
  *----------------------------------------------------------------------
  *
- * HgfsTcpChannelRecvAsync --
+ * HgfsVSocketChannelClose --
  *
- *     Receive the packet. Note, the recv may timeout and return just
- *     a part of the packet according to our setting of the socket.
+ *     See above.
  *
  * Results:
- *     On complete packet received, returns its size.
- *     On part of the packet received, returns 0.
- *     On failure returns the negative errno.
+ *     None
  *
  * Side effects:
  *     None
@@ -589,66 +703,20 @@ HgfsTcpRecvMsg(struct socket *socket,   // IN: TCP socket
  *----------------------------------------------------------------------
  */
 
-static int
-HgfsTcpChannelRecvAsync(HgfsTransportChannel *channel, // IN:  Channel
-                        char **replyPacket,            // OUT: Reply buffer
-                        size_t *packetSize)            // OUT: Packet size
+static void
+HgfsVSocketChannelClose(HgfsTransportChannel *channel)
 {
-   int ret = -EIO;
-
-   ASSERT(replyPacket);
-   ASSERT(packetSize);
-
-   if (channel->status != HGFS_CHANNEL_CONNECTED) {
-      LOG(6, (KERN_DEBUG "VMware hgfs: %s: Connection lost.\n", __func__));
-      return -ENOTCONN;
-   }
-
-   LOG(10, (KERN_DEBUG "VMware hgfs: %s: receiving %s\n", __func__,
-            recvBuffer.state == 0? "header" : "data"));
-   ret = HgfsTcpRecvMsg(channel->priv, recvBuffer.buf, recvBuffer.len);
-   LOG(10, (KERN_DEBUG "VMware hgfs: %s: sock_recvmsg returns: %d\n",
-            __func__, ret));
-   if (ret > 0) {
-      ASSERT(ret <= recvBuffer.len);
-      recvBuffer.len -= ret;
-      if ( recvBuffer.len == 0) {
-         /* Complete header or reply packet received. */
-         switch(recvBuffer.state) {
-         case HGFS_TCP_CONN_RECV_HEADER:
-            ASSERT(recvBuffer.header.version == HGFS_SOCKET_VERSION1);
-            ASSERT(recvBuffer.header.size == sizeof recvBuffer.header);
-            ASSERT(recvBuffer.header.status == HGFS_SOCKET_STATUS_SUCCESS);
-            *packetSize = 0;
-            recvBuffer.state = HGFS_TCP_CONN_RECV_DATA;
-            recvBuffer.len = recvBuffer.header.packetLen;
-            recvBuffer.buf = &recvBuffer.data[0];
-            ret = 0; /* To indicate the reply packet is not fully received. */
-            break;
-         case HGFS_TCP_CONN_RECV_DATA:
-            *packetSize = recvBuffer.header.packetLen;
-            *replyPacket = &recvBuffer.data[0];
-            HgfsTcpResetRecvBuffer();
-            break;
-         default:
-            ASSERT(0);
-         }
-      } else if (recvBuffer.len > 0) { /* No complete packet received. */
-         recvBuffer.buf += ret;
-         ret = 0;
-      }
-   }else if (ret == 0) { /* The connection is actually broken. */
-      ret = -ENOTCONN;
-   }
+   HgfsSocketChannelClose(channel);
+   VMCISock_KernelDeregister();
 
-   return ret;
+   LOG(8, ("VMware hgfs: %s: VSock channel closed.\n", __func__));
 }
 
 
 /*
  *----------------------------------------------------------------------
  *
- * HgfsTcpSendMsg --
+ * HgfsSocketSendMsg --
  *
  *     Send the message via the socket. Add the header before sending.
  *
@@ -663,72 +731,54 @@ HgfsTcpChannelRecvAsync(HgfsTransportChannel *channel, // IN:  Channel
  */
 
 static int
-HgfsTcpSendMsg(struct socket *socket,   // IN: socket
-               char *buffer,            // IN: Buffer to send
-               size_t bufferLen)        // IN: Buffer length
+HgfsSocketSendMsg(struct socket *socket,   // IN: socket
+                  char *buffer,            // IN: Buffer to send
+                  size_t bufferLen)        // IN: Buffer length
 {
-   HgfsSocketHeader sockHeader;
-   struct iovec iov[2];
+   struct iovec iov;
    struct msghdr msg;
-   int totalLen;
    int ret = 0;
    int i = 0;
    mm_segment_t oldfs = get_fs();
 
-   HgfsSocketHeaderInit(&sockHeader, HGFS_SOCKET_VERSION1, sizeof sockHeader,
-                        HGFS_SOCKET_STATUS_SUCCESS, bufferLen, 0);
-
    memset(&msg, 0, sizeof msg);
-   iov[0].iov_base = &sockHeader;
-   iov[0].iov_len = sizeof sockHeader;
-   iov[1].iov_base = buffer;
-   iov[1].iov_len = bufferLen;
-   msg.msg_iov = &iov[0];
-   msg.msg_iovlen = 2;
-   totalLen = sizeof sockHeader + bufferLen;
-
-   while (totalLen > 0) {
+   iov.iov_base = buffer;
+   iov.iov_len = bufferLen;
+   msg.msg_iov = &iov;
+   msg.msg_iovlen = 1;
+
+   while (bufferLen > 0) {
       set_fs(KERNEL_DS);
-      ret = sock_sendmsg(socket, &msg, totalLen);
+      ret = sock_sendmsg(socket, &msg, bufferLen);
       set_fs(oldfs);
-      LOG(6, ("VMware hgfs: %s: sock_sendmsg returns %d.\n", __func__, ret));
+      LOG(6, (KERN_DEBUG "VMware hgfs: %s: sock_sendmsg returns %d.\n",
+              __func__, ret));
 
-      if (ret == totalLen) { /* Common case. */
+      if (likely(ret == bufferLen)) { /* Common case. */
          break;
-      }
+      } else if (ret < 0) {
+         if (ret == -ENOSPC || ret == -EAGAIN) {
+            if (++i <= 12) {
+               LOG(6, (KERN_DEBUG "VMware hgfs: %s: "
+                       "Sleep for %d milliseconds before retry.\n",
+                       __func__, (1 << i)));
+               compat_msleep(1 << i);
+               continue;
+            }
 
-      if (ret == -ENOSPC || ret == -EAGAIN) {
-         i++;
-         if (i > 12) {
             LOG(2, ("VMware hgfs: %s: send stuck for 8 seconds.\n", __func__));
             ret = -EIO;
-            break;
          }
-         LOG(6, ("VMware hgfs: %s: Sleep for %d milliseconds before retry.\n",
-                 __func__, (1 << i)));
-         compat_msleep(1 << i);
-         continue;
-      } else if (ret < 0) {
          break;
-      } else if (ret > totalLen) {
+      } else if (ret >= bufferLen) {
          LOG(2, ("VMware hgfs: %s: sent more than expected bytes. Sent: %d, "
-                 "expected: %d\n", __func__, ret, totalLen));
+                 "expected: %d\n", __func__, ret, (int)bufferLen));
          break;
-      }
-
-      i = 0;
-      totalLen -= ret;
-      if (ret < iov[0].iov_len) {
-         /* Only part of the header has been sent out. */
-         iov[0].iov_base += ret;
-         iov[0].iov_len -= ret;
       } else {
-         /* The header and part of the body have been sent out. */
-         int temp = ret - iov[0].iov_len;
-         iov[1].iov_base += temp;
-         iov[1].iov_len -= temp;
-         msg.msg_iov = &iov[1];
-         msg.msg_iovlen = 1;
+         i = 0;
+         bufferLen -= ret;
+         iov.iov_base += ret;
+         iov.iov_len -= ret;
       }
    }
 
@@ -743,9 +793,9 @@ HgfsTcpSendMsg(struct socket *socket,   // IN: socket
 /*
  *----------------------------------------------------------------------
  *
- * HgfsTcpChannelSendAsync --
+ * HgfsSocketChannelSend --
  *
- *     Send the request via TCP channel.
+ *     Send the request via a socket channel.
  *
  * Results:
  *     0 on success, negative error on failure.
@@ -757,31 +807,23 @@ HgfsTcpSendMsg(struct socket *socket,   // IN: socket
  */
 
 static int
-HgfsTcpChannelSendAsync(HgfsTransportChannel *channel, // IN:  Channel
-                        HgfsReq *req)                  // IN: request to send
+HgfsSocketChannelSend(HgfsTransportChannel *channel, // IN:  Channel
+                      HgfsReq *req)                  // IN: request to send
 {
-   struct socket *socket;
    int result;
 
    ASSERT(req);
-   compat_mutex_lock(&channel->connLock);
-   if (channel->status != HGFS_CHANNEL_CONNECTED) {
-      LOG(6, (KERN_DEBUG "VMware hgfs: %s: Connection lost\n", __func__));
-      compat_mutex_unlock(&channel->connLock);
-      return -ENOTCONN;
-   }
-
-   req->state = HGFS_REQ_STATE_SUBMITTED;
-   socket = channel->priv;
-
-   result = HgfsTcpSendMsg(socket, HGFS_REQ_PAYLOAD(req), req->payloadSize);
 
-   compat_mutex_unlock(&channel->connLock);
+   HgfsSocketHeaderInit((HgfsSocketHeader *)req->buffer, HGFS_SOCKET_VERSION1,
+                        sizeof(HgfsSocketHeader), HGFS_SOCKET_STATUS_SUCCESS,
+                        req->payloadSize, 0);
 
+   req->state = HGFS_REQ_STATE_SUBMITTED;
+   result = HgfsSocketSendMsg((struct socket *)channel->priv, req->buffer,
+                              sizeof(HgfsSocketHeader) + req->payloadSize);
    if (result < 0) {
       LOG(4, (KERN_DEBUG "VMware hgfs: %s: sendmsg, err: %d.\n",
               __func__, result));
-      channel->ops.close(channel);
       req->state = HGFS_REQ_STATE_UNSENT;
    }
 
@@ -792,12 +834,13 @@ HgfsTcpChannelSendAsync(HgfsTransportChannel *channel, // IN:  Channel
 /*
  *----------------------------------------------------------------------
  *
- * HgfsSocketChannelExit --
+ * HgfsSocketChannelAllocate --
  *
- *     Tear down the channel.
+ *     Allocates memory for HgfsReq, its payload plus additional memory
+ *     needed for the socket transport itself.
  *
  * Results:
- *     None
+ *     NULL on failure otherwise address of allocated memory.
  *
  * Side effects:
  *     None
@@ -805,42 +848,21 @@ HgfsTcpChannelSendAsync(HgfsTransportChannel *channel, // IN:  Channel
  *----------------------------------------------------------------------
  */
 
-static void
-HgfsSocketChannelExit(HgfsTransportChannel* channel)  // IN OUT
+static HgfsReq *
+HgfsSocketChannelAllocate(size_t payloadSize) // IN: size of the payload
 {
-   HgfsTcpStopReceivingThread();
-   compat_mutex_lock(&channel->connLock);
-   if (channel->status != HGFS_CHANNEL_UNINITIALIZED) {
-      HgfsTcpChannelCloseWork(channel);
-      channel->status = HGFS_CHANNEL_UNINITIALIZED;
-      LOG(8, ("VMware hgfs: %s: tcp channel exited.\n", __func__));
+   HgfsReq *req;
+
+   req = kmalloc(sizeof(*req) + sizeof(HgfsSocketHeader) + payloadSize,
+                 GFP_KERNEL);
+   if (likely(req)) {
+      req->payload = req->buffer + sizeof(HgfsSocketHeader);
    }
-   compat_mutex_unlock(&channel->connLock);
+
+   return req;
 }
 
 
-/*
- *----------------------------------------------------------------------
- *
- * HgfsVSocketChannelExit --
- *
- *     Tear down the channel.
- *
- * Results:
- *     None
- *
- * Side effects:
- *     None
- *
- *----------------------------------------------------------------------
- */
-
-static void
-HgfsVSocketChannelExit(HgfsTransportChannel* channel)  // IN OUT
-{
-   HgfsSocketChannelExit(channel);
-   VMCISock_KernelDeregister();
-}
 /*
  *----------------------------------------------------------------------
  *
@@ -857,28 +879,24 @@ HgfsVSocketChannelExit(HgfsTransportChannel* channel)  // IN OUT
  *----------------------------------------------------------------------
  */
 
-HgfsTransportChannel*
+HgfsTransportChannel *
 HgfsGetTcpChannel(void)
 {
-   tcpChannel.name = "tcp";
-   tcpChannel.ops.open = HgfsTcpChannelOpen;
-   tcpChannel.ops.close = HgfsSocketChannelClose;
-   tcpChannel.ops.send = HgfsTcpChannelSendAsync;
-   tcpChannel.ops.recv = HgfsTcpChannelRecvAsync;
-   tcpChannel.ops.exit = HgfsSocketChannelExit;
-   tcpChannel.priv = NULL;
-   compat_mutex_init(&tcpChannel.connLock);
-
-   memset(&recvBuffer, 0, sizeof recvBuffer);
-   HgfsTcpResetRecvBuffer();
-   recvThread = NULL;
-   tcpChannel.status = HGFS_CHANNEL_NOTCONNECTED;
+   static HgfsTransportChannel channel;
+
+   channel.name = "tcp";
+   channel.ops.open = HgfsTcpChannelOpen;
+   channel.ops.close = HgfsTcpChannelClose;
+   channel.ops.allocate = HgfsSocketChannelAllocate;
+   channel.ops.send = HgfsSocketChannelSend;
+   channel.priv = NULL;
+   channel.status = HGFS_CHANNEL_NOTCONNECTED;
 
-   if (!HOST_IP) {/* HOST_IP is defined in module.c. */
+   if (!HOST_IP) {
       return NULL;
    }
 
-   return &tcpChannel;
+   return &channel;
 }
 
 
@@ -898,28 +916,21 @@ HgfsGetTcpChannel(void)
  *----------------------------------------------------------------------
  */
 
-HgfsTransportChannel*
+HgfsTransportChannel *
 HgfsGetVSocketChannel(void)
 {
-   vsocketChannel.name = "vsocket";
-   vsocketChannel.ops.open = HgfsVSocketChannelOpen;
-   vsocketChannel.ops.close = HgfsSocketChannelClose;
-   vsocketChannel.ops.send = HgfsTcpChannelSendAsync;
-   vsocketChannel.ops.recv = HgfsTcpChannelRecvAsync;
-   vsocketChannel.ops.exit = HgfsVSocketChannelExit;
-   vsocketChannel.priv = NULL;
-   compat_mutex_init(&vsocketChannel.connLock);
-
-   /* Initialize hgfsConn. */
-   memset(&recvBuffer, 0, sizeof recvBuffer);
-   HgfsTcpResetRecvBuffer();
-   recvThread = NULL;
-   vsocketChannel.status = HGFS_CHANNEL_NOTCONNECTED;
+   static HgfsTransportChannel channel;
 
-   if (!HOST_VSOCKET_PORT) {/* HOST_VSOCKET_PORT is defined in module.c. */
+   channel.name = "vsocket";
+   channel.ops.open = HgfsVSocketChannelOpen;
+   channel.ops.close = HgfsVSocketChannelClose;
+   channel.ops.send = HgfsSocketChannelSend;
+   channel.priv = NULL;
+   channel.status = HGFS_CHANNEL_NOTCONNECTED;
+
+   if (!HOST_VSOCKET_PORT) {
       return NULL;
    }
 
-   VMCISock_KernelRegister();
-   return &vsocketChannel;
+   return &channel;
 }
index e5defb86cd6d67b0357a4713661a83b1ac66d61b..747e92c7d20a9b123e2daf53a49602ec99e8289e 100644 (file)
@@ -60,22 +60,15 @@ static compat_mutex_t hgfsChannelLock;        /* Lock to protect hgfsChannel. */
 static struct list_head hgfsRepPending;       /* Reply pending queue. */
 static spinlock_t hgfsRepQueueLock;           /* Reply pending queue lock. */
 
-#define HgfsRequestId(req) ((HgfsRequest *)req)->id
-
-
-/*
- * Private function implementations.
- */
-
 /*
  *----------------------------------------------------------------------
  *
- * HgfsTransportSetupNewChannel --
+ * HgfsTransportOpenChannel --
  *
- *     Find a new workable channel.
+ *     Opens given communication channel with HGFS server.
  *
  * Results:
- *     TRUE on success, otherwise FALSE.
+ *     TRUE on success, FALSE on failure.
  *
  * Side effects:
  *     None
@@ -84,40 +77,43 @@ static spinlock_t hgfsRepQueueLock;           /* Reply pending queue lock. */
  */
 
 static Bool
-HgfsTransportSetupNewChannel(void)
+HgfsTransportOpenChannel(HgfsTransportChannel *channel)
 {
-   hgfsChannel = HgfsGetVSocketChannel();
-   if (hgfsChannel != NULL) {
-      if (hgfsChannel->ops.open(hgfsChannel)) {
-         return TRUE;
+   Bool ret;
+
+   switch (channel->status) {
+   case HGFS_CHANNEL_UNINITIALIZED:
+   case HGFS_CHANNEL_DEAD:
+      ret = FALSE;
+      break;
+
+   case HGFS_CHANNEL_CONNECTED:
+      ret = TRUE;
+      break;
+
+   case HGFS_CHANNEL_NOTCONNECTED:
+      ret = channel->ops.open(channel);
+      if (ret) {
+         channel->status = HGFS_CHANNEL_CONNECTED;
       }
-   }
+      break;
 
-   hgfsChannel = HgfsGetTcpChannel();
-   if (hgfsChannel != NULL) {
-      if (hgfsChannel->ops.open(hgfsChannel)) {
-         return TRUE;
-      }
-   }
-
-   hgfsChannel = HgfsGetBdChannel();
-   if (hgfsChannel != NULL) {
-      if (hgfsChannel->ops.open(hgfsChannel)) {
-         return TRUE;
-      }
+   default:
+      ret = FALSE;
+      ASSERT(0); /* Not reached. */
    }
 
-   hgfsChannel = NULL;
-   return FALSE;
+   return ret;
 }
 
 
 /*
  *----------------------------------------------------------------------
  *
- * HgfsTransportStopCurrentChannel --
+ * HgfsTransportCloseChannel --
  *
- *     Teardown current channel and stop current receive thread.
+ *     Closes currently open communication channel. Has to be called
+ *     while holdingChannelLock.
  *
  * Results:
  *     None
@@ -129,11 +125,13 @@ HgfsTransportSetupNewChannel(void)
  */
 
 static void
-HgfsTransportStopCurrentChannel(void)
+HgfsTransportCloseChannel(HgfsTransportChannel *channel)
 {
-   if (hgfsChannel) {
-      hgfsChannel->ops.exit(hgfsChannel);
-      hgfsChannel = NULL;
+   if (channel->status == HGFS_CHANNEL_CONNECTED ||
+       channel->status == HGFS_CHANNEL_DEAD) {
+
+      channel->ops.close(channel);
+      channel->status = HGFS_CHANNEL_NOTCONNECTED;
    }
 }
 
@@ -141,28 +139,44 @@ HgfsTransportStopCurrentChannel(void)
 /*
  *----------------------------------------------------------------------
  *
- * HgfsTransportChannelFailover --
+ * HgfsTransportSetupNewChannel --
  *
- *     Called when current channel doesn't work. Find a new channel
- *     for transport.
+ *     Find a new workable channel.
  *
  * Results:
- *     TRUE on success, otherwise FALSE;
+ *     TRUE on success, otherwise FALSE.
  *
  * Side effects:
- *     Teardown current opened channel and the receive thread, set up
- *     new channel and new receive thread.
+ *     None
  *
  *----------------------------------------------------------------------
  */
 
 static Bool
-HgfsTransportChannelFailover(void) {
-   Bool ret = FALSE;
-   HgfsTransportStopCurrentChannel();
-   ret = HgfsTransportSetupNewChannel();
-   LOG(8, ("VMware hgfs: %s result: %s.\n", __func__, ret ? "TRUE" : "FALSE"));
-   return ret;
+HgfsTransportSetupNewChannel(void)
+{
+   HgfsTransportChannel *newChannel;
+
+   newChannel = HgfsGetVSocketChannel();
+   if (newChannel != NULL) {
+      if (HgfsTransportOpenChannel(newChannel)) {
+         hgfsChannel = newChannel;
+         return TRUE;
+      }
+   }
+
+   newChannel = HgfsGetTcpChannel();
+   if (newChannel != NULL) {
+      if (HgfsTransportOpenChannel(newChannel)) {
+         hgfsChannel = newChannel;
+         return TRUE;
+      }
+   }
+
+   newChannel = HgfsGetBdChannel();
+   ASSERT(newChannel);
+   hgfsChannel = newChannel;
+   return HgfsTransportOpenChannel(newChannel);
 }
 
 
@@ -215,24 +229,18 @@ HgfsTransportRemovePendingRequest(HgfsReq *req)   // IN: Request to dequeue
    ASSERT(req);
 
    spin_lock(&hgfsRepQueueLock);
-   if (!list_empty(&req->list)) {
-      list_del_init(&req->list);
-   }
+   list_del_init(&req->list);
    spin_unlock(&hgfsRepQueueLock);
 }
 
 
-/*
- * Public function implementations.
- */
-
 /*
  *----------------------------------------------------------------------
  *
- * HgfsTransportProcessPacket --
+ * HgfsTransportFlushPendingRequests --
  *
- *     Helper function to process received packets, called by the channel
- *     handler thread.
+ *     Complete all submitted requests with an error, called when
+ *     we are about to tear down communication channel.
  *
  * Results:
  *     None
@@ -243,55 +251,72 @@ HgfsTransportRemovePendingRequest(HgfsReq *req)   // IN: Request to dequeue
  *----------------------------------------------------------------------
  */
 
-void
-HgfsTransportProcessPacket(char *receivedPacket,    //IN: received packet
-                           size_t receivedSize)     //IN: packet size
+static void
+HgfsTransportFlushPendingRequests(void)
 {
-   struct list_head *cur, *next;
-   HgfsHandle id;
-   Bool found = FALSE;
+   struct HgfsReq *req;
+
+   spin_lock(&hgfsRepQueueLock);
 
-   /* Got the reply. */
+   list_for_each_entry(req, &hgfsRepPending, list) {
+      if (req->state == HGFS_REQ_STATE_SUBMITTED) {
+         LOG(6, ("VMware hgfs: %s: injecting error reply to req id: %d\n",
+                 __func__, req->id));
+         HgfsFailReq(req, -EIO);
+      }
+   }
+
+   spin_unlock(&hgfsRepQueueLock);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HgfsTransportGetPendingRequest --
+ *
+ *     Attempts to locate request with specified ID in the queue of
+ *     pending (waiting for server's reply) requests.
+ *
+ * Results:
+ *     NULL if request not found; otherwise address of the request
+ *     structure.
+ *
+ * Side effects:
+ *     Increments reference count of the request.
+ *
+ *----------------------------------------------------------------------
+ */
+
+HgfsReq *
+HgfsTransportGetPendingRequest(HgfsHandle id)   // IN: id of the request
+{
+   HgfsReq *cur, *req = NULL;
 
-   ASSERT(receivedPacket != NULL && receivedSize > 0);
-   id = HgfsRequestId(receivedPacket);
-   LOG(8, ("VMware hgfs: %s entered.\n", __func__));
-   LOG(6, (KERN_DEBUG "VMware hgfs: %s: req id: %d\n", __func__, id));
-   /*
-    * Search through hgfsRepPending queue for the matching id and wake up
-    * the associated waiting process. Delete the req from the queue.
-    */
    spin_lock(&hgfsRepQueueLock);
-   list_for_each_safe(cur, next, &hgfsRepPending) {
-      HgfsReq *req;
-      req = list_entry(cur, HgfsReq, list);
-      if (req->id == id) {
-         ASSERT(req->state == HGFS_REQ_STATE_SUBMITTED);
-         HgfsCompleteReq(req, receivedPacket, receivedSize);
-         found = TRUE;
+
+   list_for_each_entry(cur, &hgfsRepPending, list) {
+      if (cur->id == id) {
+         ASSERT(cur->state == HGFS_REQ_STATE_SUBMITTED);
+         req = HgfsRequestGetRef(cur);
          break;
       }
    }
+
    spin_unlock(&hgfsRepQueueLock);
 
-   if (!found) {
-      LOG(4, ("VMware hgfs: %s: No matching id, dropping reply\n",
-              __func__));
-   }
-   LOG(8, ("VMware hgfs: %s exited.\n", __func__));
+   return req;
 }
 
 
 /*
  *----------------------------------------------------------------------
  *
- * HgfsTransportBeforeExitingRecvThread --
+ * HgfsTransportAllocateRequest --
  *
- *     The cleanup work to do before the recv thread exits, including
- *     completing pending requests with error.
+ *     Allocates HGFS request structre using channel-specific allocator.
  *
  * Results:
- *     None
+ *     NULL on failure; otherwisepointer to newly allocated request.
  *
  * Side effects:
  *     None
@@ -299,25 +324,25 @@ HgfsTransportProcessPacket(char *receivedPacket,    //IN: received packet
  *----------------------------------------------------------------------
  */
 
-void
-HgfsTransportBeforeExitingRecvThread(void)
+HgfsReq *
+HgfsTransportAllocateRequest(size_t bufferSize)   // IN: size of the buffer
 {
-   struct list_head *cur, *next;
+   HgfsReq *req = NULL;
+   /*
+    * We use a temporary variable to make sure we stamp the request with
+    * same channel as we used to make allocation since hgfsChannel can
+    * be changed while we do allocation.
+    */
+   HgfsTransportChannel *currentChannel = hgfsChannel;
 
-   /* Walk through hgfsRepPending queue and reply them with error. */
-   spin_lock(&hgfsRepQueueLock);
-   list_for_each_safe(cur, next, &hgfsRepPending) {
-      HgfsReq *req;
-      HgfsReply reply;
-
-      /* XXX: Make the request senders be aware of this error. */
-      reply.status = -EIO;
-      req = list_entry(cur, HgfsReq, list);
-      LOG(6, ("VMware hgfs: %s: injecting error reply to req id: %d\n",
-              __func__, req->id));
-      HgfsCompleteReq(req, (char *)&reply, sizeof reply);
+   ASSERT(currentChannel);
+
+   req = currentChannel->ops.allocate(bufferSize);
+   if (req) {
+         req->transportId = currentChannel;
    }
-   spin_unlock(&hgfsRepQueueLock);
+
+   return req;
 }
 
 
@@ -340,36 +365,67 @@ HgfsTransportBeforeExitingRecvThread(void)
 int
 HgfsTransportSendRequest(HgfsReq *req)   // IN: Request to send
 {
-   int ret;
+   HgfsReq *origReq = req;
+   int ret = -EIO;
+
    ASSERT(req);
    ASSERT(req->state == HGFS_REQ_STATE_UNSENT);
    ASSERT(req->payloadSize <= HGFS_PACKET_MAX);
 
    compat_mutex_lock(&hgfsChannelLock);
 
-   /* Try opening the channel. */
-   if (!hgfsChannel && !HgfsTransportSetupNewChannel()) {
-      compat_mutex_unlock(&hgfsChannelLock);
-      return -EPROTO;
-   }
+   HgfsTransportAddPendingRequest(req);
 
-   ASSERT(hgfsChannel->ops.send);
+   do {
 
-   HgfsTransportAddPendingRequest(req);
+      if (unlikely(hgfsChannel->status != HGFS_CHANNEL_CONNECTED)) {
+         if (hgfsChannel->status == HGFS_CHANNEL_DEAD) {
+            HgfsTransportCloseChannel(hgfsChannel);
+            HgfsTransportFlushPendingRequests();
+         }
+
+         if (!HgfsTransportSetupNewChannel()) {
+            ret = -EIO;
+            goto out;
+         }
+      }
+
+      ASSERT(hgfsChannel->ops.send);
+
+      /* If channel changed since we created request we need to adjust */
+      if (req->transportId != hgfsChannel) {
+
+         HgfsTransportRemovePendingRequest(req);
 
-   while ((ret = hgfsChannel->ops.send(hgfsChannel, req)) != 0) {
-      LOG(4, (KERN_DEBUG "VMware hgfs: %s: send failed. Return %d\n",
+         if (req != origReq) {
+            HgfsRequestPutRef(req);
+         }
+
+         req = HgfsCopyRequest(origReq);
+         if (req == NULL) {
+            req = origReq;
+            ret = -ENOMEM;
+            goto out;
+         }
+
+         HgfsTransportAddPendingRequest(req);
+      }
+
+      ret = hgfsChannel->ops.send(hgfsChannel, req);
+      if (likely(ret == 0))
+         break;
+
+      LOG(4, (KERN_DEBUG "VMware hgfs: %s: send failed with error %d\n",
               __func__, ret));
+
       if (ret == -EINTR) {
          /* Don't retry when we are interrupted by some signal. */
          goto out;
       }
-      if (!hgfsChannel->ops.open(hgfsChannel) && !HgfsTransportChannelFailover()) {
-         /* Can't establish a working channel, just report error. */
-         ret = -EIO;
-         goto out;
-      }
-   }
+
+      hgfsChannel->status = HGFS_CHANNEL_DEAD;
+
+   } while (1);
 
    ASSERT(req->state == HGFS_REQ_STATE_COMPLETED ||
           req->state == HGFS_REQ_STATE_SUBMITTED);
@@ -377,15 +433,25 @@ HgfsTransportSendRequest(HgfsReq *req)   // IN: Request to send
 out:
    compat_mutex_unlock(&hgfsChannelLock);
 
-   if (ret == 0) { /* Send succeeded. */
+   if (likely(ret == 0)) {
+      /* Send succeeded, wait for the reply */
       if (wait_event_interruptible(req->queue,
                                    req->state == HGFS_REQ_STATE_COMPLETED)) {
          ret = -EINTR; /* Interrupted by some signal. */
       }
-   } /* else send failed. */
+   }
+
+   HgfsTransportRemovePendingRequest(req);
 
-   if (ret < 0) {
-      HgfsTransportRemovePendingRequest(req);
+   /*
+    * If we used a copy of request because we changed transport we
+    * need to copy payload back into original request.
+    */
+   if (req != origReq) {
+      ASSERT(req->payloadSize <= origReq->bufferSize);
+      origReq->payloadSize = req->payloadSize;
+      memcpy(origReq->payload, req->payload, req->payloadSize);
+      HgfsRequestPutRef(req);
    }
 
    return ret;
@@ -418,7 +484,45 @@ HgfsTransportInit(void)
    spin_lock_init(&hgfsRepQueueLock);
    compat_mutex_init(&hgfsChannelLock);
 
-   hgfsChannel = NULL;
+   compat_mutex_lock(&hgfsChannelLock);
+
+   hgfsChannel = HgfsGetBdChannel();
+   ASSERT(hgfsChannel);
+
+   compat_mutex_unlock(&hgfsChannelLock);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HgfsTransportMarkDead --
+ *
+ *     Marks current channel as dead so it can be cleaned up and
+ *     fails all submitted requests.
+ *
+ * Results:
+ *     None
+ *
+ * Side effects:
+ *     None
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+HgfsTransportMarkDead(void)
+{
+   LOG(8, ("VMware hgfs: %s entered.\n", __func__));
+
+   compat_mutex_lock(&hgfsChannelLock);
+
+   if (hgfsChannel) {
+      hgfsChannel->status = HGFS_CHANNEL_DEAD;
+   }
+   HgfsTransportFlushPendingRequests();
+
+   compat_mutex_unlock(&hgfsChannelLock);
 }
 
 
@@ -442,10 +546,15 @@ void
 HgfsTransportExit(void)
 {
    LOG(8, ("VMware hgfs: %s entered.\n", __func__));
+
    compat_mutex_lock(&hgfsChannelLock);
-   HgfsTransportStopCurrentChannel();
+   ASSERT(hgfsChannel);
+   HgfsTransportCloseChannel(hgfsChannel);
+   hgfsChannel = NULL;
    compat_mutex_unlock(&hgfsChannelLock);
 
    ASSERT(list_empty(&hgfsRepPending));
    LOG(8, ("VMware hgfs: %s exited.\n", __func__));
 }
+
+
index a32584d26ffd136fd97f668d72ac56029f458f1d..478b59d116a0b72dbdb7ea0905b70bc65a12fd53 100644 (file)
@@ -25,6 +25,7 @@
 
 #include "request.h"
 #include "compat_mutex.h"
+#include "hgfsProto.h"
 
 /*
  * There are the operations a channel should implement.
@@ -33,15 +34,15 @@ struct HgfsTransportChannel;
 typedef struct HgfsTransportChannelOps {
    Bool (*open)(struct HgfsTransportChannel *);
    void (*close)(struct HgfsTransportChannel *);
+   HgfsReq* (*allocate)(size_t payloadSize);
    int (*send)(struct HgfsTransportChannel *, HgfsReq *);
-   int (*recv)(struct HgfsTransportChannel *, char **, size_t *);
-   void (*exit)(struct HgfsTransportChannel *);
 } HgfsTransportChannelOps;
 
 typedef enum {
    HGFS_CHANNEL_UNINITIALIZED,
    HGFS_CHANNEL_NOTCONNECTED,
    HGFS_CHANNEL_CONNECTED,
+   HGFS_CHANNEL_DEAD,   /* Error has been detected, need to shut it down. */
 } HgfsChannelStatus;
 
 typedef struct HgfsTransportChannel {
@@ -55,9 +56,11 @@ typedef struct HgfsTransportChannel {
 /* Public functions (with respect to the entire module). */
 void HgfsTransportInit(void);
 void HgfsTransportExit(void);
+HgfsReq *HgfsTransportAllocateRequest(size_t payloadSize);
 int HgfsTransportSendRequest(HgfsReq *req);
-void HgfsTransportProcessPacket(char *receivedPacket,
-                                size_t receivedSize);
-void HgfsTransportBeforeExitingRecvThread(void);
+HgfsReq *HgfsTransportGetPendingRequest(HgfsHandle id);
+void HgfsTransportFinishRequest(HgfsReq *req, Bool success, Bool do_put);
+void HgfsTransportFlushRequests(void);
+void HgfsTransportMarkDead(void);
 
 #endif // _HGFS_DRIVER_TRANSPORT_H_