#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)
{
#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
*----------------------------------------------------------------------
*/
-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
*----------------------------------------------------------------------
*/
-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
}
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;
}
static Bool
HgfsTcpChannelOpen(HgfsTransportChannel *channel)
{
- return HgfsSocketChannelOpen(channel, HgfsTcpConnect);
+ return HgfsSocketChannelOpen(channel, HgfsCreateTcpSocket);
}
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;
}
*
* HgfsSocketChannelClose --
*
- * See above.
+ * Closes socket-based channel by closing socket and stopping the
+ * receiving thread.
*
* Results:
* None
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
*----------------------------------------------------------------------
*/
-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
*----------------------------------------------------------------------
*/
-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.
*
*/
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;
}
}
/*
*----------------------------------------------------------------------
*
- * HgfsTcpChannelSendAsync --
+ * HgfsSocketChannelSend --
*
- * Send the request via TCP channel.
+ * Send the request via a socket channel.
*
* Results:
* 0 on success, negative error on failure.
*/
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;
}
/*
*----------------------------------------------------------------------
*
- * 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
*----------------------------------------------------------------------
*/
-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();
-}
/*
*----------------------------------------------------------------------
*
*----------------------------------------------------------------------
*/
-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;
}
*----------------------------------------------------------------------
*/
-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;
}
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
*/
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
*/
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;
}
}
/*
*----------------------------------------------------------------------
*
- * 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);
}
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
*----------------------------------------------------------------------
*/
-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
*----------------------------------------------------------------------
*/
-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;
}
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);
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;
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);
}
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__));
}
+
+