[Spice-devel] [PATCH] spice: vdagent: virtio serial adaptations

Arnon Gilboa agilboa at redhat.com
Mon Aug 9 02:58:40 PDT 2010


From: Arnon Gilboa <agilboa at agilboa.usersys.redhat.com>

-mostly based on Alon's patches with cleanup
-adapt vdi_port class to virtio serial
-replace vdi_port callbacks with events
-add get_device_path() - using  setupapi.lib
-add struct VDIPortBuffer for read/write buffers
-add VD_EVENT_XXX enum for clarity
-add DEBUG_VDSERVICE for debugging as standalone executable
-add CLIPBOARD_ENABLED ifdefs in the agent for disabling clipboard support
-remove mutex.h usage, deines in vdcommon.h instead
-include the common spice/vd_agent.h
---
 common/vdcommon.h          |    8 ++-
 vdagent/desktop_layout.cpp |    1 +
 vdagent/desktop_layout.h   |    2 +-
 vdagent/vdagent.cpp        |    7 ++
 vdservice/vdi_port.cpp     |  255 +++++++++++++++++++++++++++++++-------------
 vdservice/vdi_port.h       |   33 ++++---
 vdservice/vdservice.cpp    |  135 +++++++++++++++---------
 vdservice/vdservice.vcproj |    4 +-
 8 files changed, 305 insertions(+), 140 deletions(-)

diff --git a/common/vdcommon.h b/common/vdcommon.h
index c807cbb..9095404 100644
--- a/common/vdcommon.h
+++ b/common/vdcommon.h
@@ -21,9 +21,15 @@
 #pragma warning(disable:4200)
 
 #include <windows.h>
-#include "vd_agent.h"
+#include "spice/vd_agent.h"
 #include "vdlog.h"
 
+typedef CRITICAL_SECTION mutex_t;
+
+#define MUTEX_INIT(mutex) InitializeCriticalSection(&mutex)
+#define MUTEX_LOCK(mutex) EnterCriticalSection(&mutex)
+#define MUTEX_UNLOCK(mutex) LeaveCriticalSection(&mutex)
+
 #define VD_SERVICE_PIPE_NAME   TEXT("\\\\.\\pipe\\vdservicepipe")
 #define VD_MESSAGE_HEADER_SIZE (sizeof(VDPipeMessage) + sizeof(VDAgentMessage))
 #define VD_PIPE_BUF_SIZE       (1024 * 1024)
diff --git a/vdagent/desktop_layout.cpp b/vdagent/desktop_layout.cpp
index 32502bc..4687483 100644
--- a/vdagent/desktop_layout.cpp
+++ b/vdagent/desktop_layout.cpp
@@ -16,6 +16,7 @@
 */
 
 #include "desktop_layout.h"
+#include "vdcommon.h"
 #include "vdlog.h"
 
 void DisplayMode::set_res(DWORD width, DWORD height, DWORD depth)
diff --git a/vdagent/desktop_layout.h b/vdagent/desktop_layout.h
index 797a82c..22bbc8f 100644
--- a/vdagent/desktop_layout.h
+++ b/vdagent/desktop_layout.h
@@ -18,7 +18,7 @@
 #ifndef _H_DESKTOP_LAYOUT
 #define _H_DESKTOP_LAYOUT
 
-#include "mutex.h"
+#include "vdcommon.h"
 #include <vector>
 
 class DisplayMode {
diff --git a/vdagent/vdagent.cpp b/vdagent/vdagent.cpp
index bf5f5f5..d6518a1 100644
--- a/vdagent/vdagent.cpp
+++ b/vdagent/vdagent.cpp
@@ -19,6 +19,8 @@
 #include "desktop_layout.h"
 #include <lmcons.h>
 
+#define CLIPBOARD_ENABLED
+
 #define VD_AGENT_LOG_PATH       TEXT("%svdagent.log")
 #define VD_AGENT_WINCLASS_NAME  TEXT("VDAGENT")
 #define VD_INPUT_INTERVAL_MS    20
@@ -608,6 +610,7 @@ void VDAgent::dispatch_message(VDAgentMessage* msg, uint32_t port)
             a->_running = false;
         }
         break;
+#ifdef CLIPBOARD_ENABLED
     case VD_AGENT_CLIPBOARD:
         if (!a->handle_clipboard((VDAgentClipboard*)msg->data,
                                  msg->size - sizeof(VDAgentClipboard))) {
@@ -615,6 +618,7 @@ void VDAgent::dispatch_message(VDAgentMessage* msg, uint32_t port)
             a->_running = false;
         }
         break;
+#endif // CLIPBOARD_ENABLED
     default:
         vd_printf("Unsupported message type %u size %u", msg->type, msg->size);
     }
@@ -670,6 +674,7 @@ VOID CALLBACK VDAgent::read_completion(DWORD err, DWORD bytes, LPOVERLAPPED over
         } else {
             memcpy((uint8_t*)a->_in_msg + a->_in_msg_pos, pipe_msg->data, pipe_msg->size);
             a->_in_msg_pos += pipe_msg->size;
+            //vd_printf("DEBUG: pipe_msg size %u pos %u total %u", pipe_msg->size, a->_in_msg_pos, sizeof(VDAgentMessage) + a->_in_msg->size);
             if (a->_in_msg_pos == sizeof(VDAgentMessage) + a->_in_msg->size) {
                 dispatch_message(a->_in_msg, 0);
                 a->_in_msg_pos = 0;
@@ -754,6 +759,7 @@ LRESULT CALLBACK VDAgent::wnd_proc(HWND hwnd, UINT message, WPARAM wparam, LPARA
     case WM_TIMER:
         a->send_input();
         break;
+#ifdef CLIPBOARD_ENABLED
     case WM_CHANGECBCHAIN:
         if (a->_hwnd_next_viewer == (HWND)wparam) {
             a->_hwnd_next_viewer = (HWND)lparam;
@@ -769,6 +775,7 @@ LRESULT CALLBACK VDAgent::wnd_proc(HWND hwnd, UINT message, WPARAM wparam, LPARA
         }
         SendMessage(a->_hwnd_next_viewer, message, wparam, lparam);
         break;
+#endif // CLIPBOARD_ENABLED
     default:
         return DefWindowProc(hwnd, message, wparam, lparam);
     }
diff --git a/vdservice/vdi_port.cpp b/vdservice/vdi_port.cpp
index 2de53b0..52bdfd8 100644
--- a/vdservice/vdi_port.cpp
+++ b/vdservice/vdi_port.cpp
@@ -19,31 +19,26 @@
 #include "vdi_port.h"
 #include "vdlog.h"
 
-#define VDI_PORT_DEV_NAME   TEXT("\\\\.\\VDIPort")
-#define FILE_DEVICE_UNKNOWN 0x00000022
-#define METHOD_BUFFERED     0
-#define FILE_ANY_ACCESS     0
+const GUID GUID_VIOSERIAL_PORT =
+    {0x6fde7521, 0x1b65, 0x48ae, 0xb6, 0x28, 0x80, 0xbe, 0x62, 0x1, 0x60, 0x26};
 
-#define CTL_CODE(DeviceType, Function, Method, Access) (                   \
-    ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) \
-)
-
-#define FIRST_AVAIL_IO_FUNC 0x800
-#define RED_TUNNEL_CTL_FUNC FIRST_AVAIL_IO_FUNC
-
-#define IOCTL_RED_TUNNEL_SET_EVENT \
-    CTL_CODE(FILE_DEVICE_UNKNOWN, RED_TUNNEL_CTL_FUNC, METHOD_BUFFERED, FILE_ANY_ACCESS)
+// Current limitation of virtio-serial windows driver (RHBZ 617000)
+#define VIOSERIAL_PORT_MAX_WRITE_BYTES      2048
+#define VIOSERIAL_PORT_MAX_RETRIES          30
+#define VIOSERIAL_PORT_RETRY_INTERVAL_MS    1000
 
 #define MIN(a, b) ((a) > (b) ? (b) : (a))
 
+VDIPort* VDIPort::_singleton;
+
 VDIPort::VDIPort()
     : _handle (INVALID_HANDLE_VALUE)
-    , _event (NULL)
-    , _write_start (_write_ring)
-    , _write_end (_write_ring)
-    , _read_start (_read_ring)
-    , _read_end (_read_ring)
 {
+    ZeroMemory(&_write, offsetof(VDIPortBuffer, ring));
+    _write.start = _write.end = _write.ring;
+    ZeroMemory(&_read, offsetof(VDIPortBuffer, ring));
+    _read.start = _read.end = _read.ring;
+    _singleton = this;
 }
 
 VDIPort::~VDIPort()
@@ -51,28 +46,84 @@ VDIPort::~VDIPort()
     if (_handle != INVALID_HANDLE_VALUE) {
         CloseHandle(_handle);
     }
-    if (_event) {
-        CloseHandle(_event);
+    if (_read.overlap.hEvent) {
+        CloseHandle(_read.overlap.hEvent);
+    }
+    if (_write.overlap.hEvent) {
+        CloseHandle(_write.overlap.hEvent);
     }
 }
 
+#include <setupapi.h>
+
+//Based on device.cpp from vioserial test app
+//FIXME: remove this call & lib?
+PTCHAR get_device_path(IN LPGUID interface_guid)
+{
+    HDEVINFO dev_info;
+    SP_DEVICE_INTERFACE_DATA dev_interface;
+    PSP_DEVICE_INTERFACE_DETAIL_DATA dev_interface_detail = NULL;
+    ULONG len, req_len = 0;
+
+    dev_info = SetupDiGetClassDevs(interface_guid, NULL, NULL,
+                                   DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
+    if (dev_info == INVALID_HANDLE_VALUE) {
+        vd_printf("Cannot get class devices");
+        return NULL;
+    }
+    dev_interface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
+    if (!SetupDiEnumDeviceInterfaces(dev_info, 0, interface_guid, 0, &dev_interface)) {
+        vd_printf("Cannot get enumerate device interfaces");
+        SetupDiDestroyDeviceInfoList(dev_info);
+        return NULL;
+    }
+    SetupDiGetDeviceInterfaceDetail(dev_info, &dev_interface, NULL, 0, &req_len, NULL);
+    dev_interface_detail = (PSP_DEVICE_INTERFACE_DETAIL_DATA)LocalAlloc(LMEM_FIXED, req_len);
+    if (dev_interface_detail == NULL) {
+        vd_printf("Cannot allocate memory");
+        SetupDiDestroyDeviceInfoList(dev_info);
+        return NULL;
+    }
+    dev_interface_detail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
+    len = req_len;
+    if (!SetupDiGetDeviceInterfaceDetail(dev_info, &dev_interface, dev_interface_detail, len,
+                                         &req_len, NULL)) {
+        vd_printf("Cannot get device interface details.\n");
+        SetupDiDestroyDeviceInfoList(dev_info);
+        LocalFree(dev_interface_detail);
+        return NULL;
+    }
+    return dev_interface_detail->DevicePath;
+}
+
 bool VDIPort::init()
 {
-    DWORD io_ret_len;
-    _handle = CreateFile(VDI_PORT_DEV_NAME, GENERIC_READ | GENERIC_WRITE, 0, NULL,
-                         OPEN_EXISTING, 0, NULL);
+    PTCHAR path = NULL;
+
+    for (int retry = 0; retry < VIOSERIAL_PORT_MAX_RETRIES && path == NULL; retry++) {
+        if (path = get_device_path((LPGUID)&GUID_VIOSERIAL_PORT)) {
+            break;
+        }
+        Sleep(VIOSERIAL_PORT_RETRY_INTERVAL_MS);
+    }
+    if (path == NULL) {
+        vd_printf("GetDevicePath failed - device/driver missing?");
+        return false;
+    }
+    _handle = CreateFile(path, GENERIC_READ | GENERIC_WRITE , 0, NULL,
+                         OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
     if (_handle == INVALID_HANDLE_VALUE) {
         vd_printf("CreateFile() failed: %u", GetLastError());
         return false;
     }
-    _event = CreateEvent(NULL, FALSE, FALSE, NULL);
-    if (_event == NULL) {
+    _write.overlap.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+    if (_write.overlap.hEvent == NULL) {
         vd_printf("CreateEvent() failed: %u", GetLastError());
         return false;
     }
-    if (!DeviceIoControl(_handle, IOCTL_RED_TUNNEL_SET_EVENT, &_event, sizeof(_event),
-                         NULL, 0, &io_ret_len, NULL)) {
-        vd_printf("DeviceIoControl() failed: %u", GetLastError());
+    _read.overlap.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+    if (_read.overlap.hEvent == NULL) {
+        vd_printf("CreateEvent() failed: %u", GetLastError());
         return false;
     }
     return true;
@@ -80,53 +131,89 @@ bool VDIPort::init()
 
 size_t VDIPort::write_ring_free_space()
 {
-    return (BUF_SIZE + _write_start - _write_end - 1) % BUF_SIZE;
+    return (BUF_SIZE + _write.start - _write.end - 1) % BUF_SIZE;
 }
 
 size_t VDIPort::ring_write(const void* buf, size_t size)
 {
-    size_t free_size = (BUF_SIZE + _write_start - _write_end - 1) % BUF_SIZE;
+    size_t free_size = (BUF_SIZE + _write.start - _write.end - 1) % BUF_SIZE;
     size_t n;
 
     if (size > free_size) {
         size = free_size;
     }
-    if (_write_end < _write_start) {
-        memcpy(_write_end, buf, size);
+    if (_write.end < _write.start) {
+        memcpy(_write.end, buf, size);
     } else {
-        n = MIN(size, (size_t)(&_write_ring[BUF_SIZE] - _write_end));
-        memcpy(_write_end, buf, n);
+        n = MIN(size, (size_t)(&_write.ring[BUF_SIZE] - _write.end));
+        memcpy(_write.end, buf, n);
         if (size > n) {
-            memcpy(_write_ring, (uint8_t*)buf + n, size - n);
+            memcpy(_write.ring, (uint8_t*)buf + n, size - n);
         }
     }
-    _write_end = _write_ring + (_write_end - _write_ring + size) % BUF_SIZE;
+    _write.end = _write.ring + (_write.end - _write.ring + size) % BUF_SIZE;
     return size;
 }
 
 int VDIPort::write()
 {
     int size;
-    int n;
+    int ret;
 
-    if (_write_start == _write_end) {
+    //FIXME: return VDI_PORT_NO_DATA
+    if (_write.start == _write.end) {
         return 0;
     }
-    if (_write_start < _write_end) {
-        size = (int)(_write_end - _write_start);
-    } else {
-        size = (int)(&_write_ring[BUF_SIZE] - _write_start);
+    if (!_write.pending) {
+        if (_write.start < _write.end) {
+            size = (int)(_write.end - _write.start);
+        } else {
+            size = (int)(&_write.ring[BUF_SIZE] - _write.start);
+        }
+        size = MIN(size, VIOSERIAL_PORT_MAX_WRITE_BYTES);
+        _write.pending = true;
+        if (WriteFile(_handle, _write.start, size, NULL, &_write.overlap)) {
+            write_completion();
+        } if (GetLastError() != ERROR_IO_PENDING) {
+            return handle_error();
+        }
+    }
+    ret = _write.bytes;
+    _write.bytes = 0;
+    return ret;
+}
+
+void VDIPort::write_completion()
+{
+    DWORD bytes;
+
+    if (!_write.pending) {
+        return;
     }
-    if (!WriteFile(_handle, _write_start, size, (LPDWORD)&n, NULL)) {
-        return handle_error();
+    if (!GetOverlappedResult(_handle, &_write.overlap, &bytes, FALSE)) {
+        vd_printf("GetOverlappedResult failed: %u", GetLastError());
+        return;
     }
-    _write_start = _write_ring + (_write_start - _write_ring + n) % BUF_SIZE;
-    return n;
+    _write.start = _write.ring + (_write.start - _write.ring + bytes) % BUF_SIZE;
+    _write.bytes = bytes;
+    _write.pending = false;
 }
 
 size_t VDIPort::read_ring_size()
 {
-    return (BUF_SIZE + _read_end - _read_start) % BUF_SIZE;
+    return (BUF_SIZE + _read.end - _read.start) % BUF_SIZE;
+}
+
+size_t VDIPort::read_ring_continuous_remaining_size()
+{
+    DWORD size;
+
+    if (_read.start <= _read.end) {
+        size = MIN(BUF_SIZE - 1, (int)(&_read.ring[BUF_SIZE] - _read.end));
+    } else {
+        size = (DWORD)(_read.start - _read.end - 1);
+    }
+    return size;
 }
 
 size_t VDIPort::ring_read(void* buf, size_t size)
@@ -134,45 +221,67 @@ size_t VDIPort::ring_read(void* buf, size_t size)
     size_t n;
     size_t m = 0;
 
-    if (_read_start == _read_end) {
+    if (_read.start == _read.end) {
         return 0;
     }
-    if (_read_start < _read_end) {
-        n = MIN(size, (size_t)(_read_end - _read_start));
-        memcpy(buf, _read_start, n);
+    if (_read.start < _read.end) {
+        n = MIN(size, (size_t)(_read.end - _read.start));
+        memcpy(buf, _read.start, n);
     } else {
-        n = MIN(size, (size_t)(&_read_ring[BUF_SIZE] - _read_start));
-        memcpy(buf, _read_start, n);
+        n = MIN(size, (size_t)(&_read.ring[BUF_SIZE] - _read.start));
+        memcpy(buf, _read.start, n);
         if (size > n) {
-            m = MIN(size - n, (size_t)(_read_end - _read_ring));
-            memcpy((uint8_t*)buf + n, _read_ring, m);
+            m = MIN(size - n, (size_t)(_read.end - _read.ring));
+            memcpy((uint8_t*)buf + n, _read.ring, m);
         }
     }
-    _read_start = _read_ring + (_read_start - _read_ring + n + m) % BUF_SIZE;
+    _read.start = _read.ring + (_read.start - _read.ring + n + m) % BUF_SIZE;
     return n + m;
 }
 
 int VDIPort::read()
 {
     int size;
-    int n;
+    int ret;
 
-    if ((_read_end - _read_ring + 1) % BUF_SIZE == _read_start - _read_ring) {
-        return 0;
-    }
-    if (_read_start == _read_end) {
-        _read_start = _read_end = _read_ring;
-    }
-    if (_read_start <= _read_end) {
-        size = MIN(BUF_SIZE - 1, (int)(&_read_ring[BUF_SIZE] - _read_end));
-    } else {
-        size = (int)(_read_start - _read_end - 1);
+    if (!_read.pending) {
+        //FIXME: read_ring_continuous_remaining_size? return VDI_PORT_BUFFER_FULL
+        if ((_read.end - _read.ring + 1) % BUF_SIZE == _read.start - _read.ring) {
+            vd_printf("DEBUG: buffer full");
+            return 0;
+        }
+        if (_read.start == _read.end) {
+            _read.start = _read.end = _read.ring;
+        }
+        if (_read.start <= _read.end) {
+            size = MIN(BUF_SIZE - 1, (int)(&_read.ring[BUF_SIZE] - _read.end));
+        } else {
+            size = (int)(_read.start - _read.end - 1);
+        }
+        _read.pending = true;
+        if (ReadFile(_handle, _read.end, size, NULL, &_read.overlap)) {
+            read_completion();
+        } else if (GetLastError() != ERROR_IO_PENDING) {
+            return handle_error();
+        }
     }
-    if (!ReadFile(_handle, _read_end, size, (LPDWORD)&n, NULL)) {
-        return handle_error();
+    ret = _read.bytes;
+    _read.bytes = 0;
+    return ret;
+}
+
+void VDIPort::read_completion()
+{
+    DWORD bytes;
+
+    if (!GetOverlappedResult(_handle, &_read.overlap, &bytes, FALSE) &&
+                                      GetLastError() != ERROR_MORE_DATA) {
+        vd_printf("GetOverlappedResult failed: %u", GetLastError());
+        return;
     }
-    _read_end = _read_ring + (_read_end - _read_ring + n) % BUF_SIZE;
-    return n;
+    _read.end = _read.ring + (_read.end - _read.ring + bytes) % BUF_SIZE;
+    _read.bytes = bytes;
+    _read.pending = false;
 }
 
 int VDIPort::handle_error()
@@ -180,8 +289,8 @@ int VDIPort::handle_error()
     switch (GetLastError()) {
     case ERROR_CONNECTION_INVALID:
         vd_printf("port reset");
-        _write_start = _write_end = _write_ring;
-        _read_start = _read_end = _read_ring;
+        _write.start = _write.end = _write.ring;
+        _read.start = _read.end = _read.ring;
         return VDI_PORT_RESET;
     default:
         vd_printf("port io failed: %u", GetLastError());
diff --git a/vdservice/vdi_port.h b/vdservice/vdi_port.h
index 2fbfb19..035d305 100644
--- a/vdservice/vdi_port.h
+++ b/vdservice/vdi_port.h
@@ -21,16 +21,21 @@
 #include <windows.h>
 #include <stdint.h>
 
-#define BUF_SIZE    (1024 * 1024)
-
-#define BUF_READ    (1 << 0)
-#define BUF_WRITE   (1 << 1)
-#define BUF_ALL     (BUF_READ | BUF_WRITE)
+#define BUF_SIZE            (1024 * 1024)
 
 #define VDI_PORT_BLOCKED    0
 #define VDI_PORT_RESET      -1
 #define VDI_PORT_ERROR      -2
 
+typedef struct VDIPortBuffer {
+    OVERLAPPED overlap;
+    uint8_t* start;
+    uint8_t* end;
+    bool pending;
+    int bytes;
+    uint8_t ring[BUF_SIZE];
+} VDIPortBuffer;
+
 class VDIPort {
 public:
     VDIPort();
@@ -40,22 +45,22 @@ public:
     size_t write_ring_free_space();
     size_t ring_read(void* buf, size_t size);
     size_t read_ring_size();
+    size_t read_ring_continuous_remaining_size();
+    HANDLE get_write_event() { return _write.overlap.hEvent; }
+    HANDLE get_read_event() { return _read.overlap.hEvent; }
     int write();
     int read();
-    HANDLE get_event() { return _event;}
-
+    void write_completion();
+    void read_completion();
+    
 private:
     int handle_error();
 
 private:
+    static VDIPort* _singleton;
     HANDLE _handle;
-    HANDLE _event;
-    uint8_t _write_ring[BUF_SIZE];
-    uint8_t* _write_start;
-    uint8_t* _write_end;
-    uint8_t _read_ring[BUF_SIZE];
-    uint8_t* _read_start;
-    uint8_t* _read_end;
+    VDIPortBuffer _write;
+    VDIPortBuffer _read;
 };
 
 // Ring notes:
diff --git a/vdservice/vdservice.cpp b/vdservice/vdservice.cpp
index 7c6f976..7d13e56 100644
--- a/vdservice/vdservice.cpp
+++ b/vdservice/vdservice.cpp
@@ -23,7 +23,8 @@
 #include <tlhelp32.h>
 #include "vdcommon.h"
 #include "vdi_port.h"
-#include "mutex.h"
+
+//#define DEBUG_VDSERVICE
 
 #define VD_SERVICE_DISPLAY_NAME TEXT("RHEV Spice Agent")
 #define VD_SERVICE_NAME         TEXT("vdservice")
@@ -35,11 +36,20 @@
 #define VD_AGENT_MAX_RESTARTS   10
 #define VD_AGENT_RESTART_INTERVAL 3000
 #define VD_AGENT_RESTART_COUNT_RESET_INTERVAL 60000
-#define VD_EVENTS_COUNT         4
 #define WINLOGON_FILENAME       TEXT("winlogon.exe")
 #define CREATE_PROC_MAX_RETRIES 10
 #define CREATE_PROC_INTERVAL_MS 500
 
+enum {
+    VD_EVENT_PIPE_READ = 0,
+    VD_EVENT_PIPE_WRITE,
+    VD_EVENT_CONTROL,
+    VD_EVENT_READ,
+    VD_EVENT_WRITE,
+    VD_EVENT_AGENT, // Must be before last
+    VD_EVENTS_COUNT // Must be last
+};
+
 class VDService {
 public:
     static VDService* get();
@@ -55,7 +65,7 @@ private:
     static DWORD WINAPI control_handler(DWORD control, DWORD event_type,
                                         LPVOID event_data, LPVOID context);
     static VOID WINAPI main(DWORD argc, TCHAR * argv[]);
-    static VOID CALLBACK write_completion(DWORD err, DWORD bytes, LPOVERLAPPED overlap);
+    void pipe_write_completion();
     void write_agent_control(uint32_t type, uint32_t opaque);
     void read_pipe();
     void handle_pipe_data(DWORD bytes);
@@ -149,6 +159,7 @@ VDService::VDService()
     ZeroMemory(_events, sizeof(_events));
     _system_version = supported_system_version();
     _control_event = CreateEvent(NULL, FALSE, FALSE, NULL);
+    _pipe_state.write.overlap.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
     _pipe_state.read.overlap.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
     _agent_path[0] = wchar_t('\0');
     MUTEX_INIT(_agent_mutex);
@@ -158,14 +169,20 @@ VDService::VDService()
 VDService::~VDService()
 {
     CloseHandle(_pipe_state.read.overlap.hEvent);
+    CloseHandle(_pipe_state.write.overlap.hEvent);
     CloseHandle(_control_event);
     delete _log;
 }
 
 bool VDService::run()
 {
+#ifndef DEBUG_VDSERVICE
     SERVICE_TABLE_ENTRY service_table[] = {{VD_SERVICE_NAME, main}, {0, 0}};
     return !!StartServiceCtrlDispatcher(service_table);
+#else
+    main(0, NULL);
+    return true;
+#endif
 }
 
 bool VDService::install()
@@ -329,7 +346,9 @@ VOID WINAPI VDService::main(DWORD argc, TCHAR* argv[])
                                                      NULL);
     if (!s->_status_handle) {
         printf("RegisterServiceCtrlHandler failed\n");
+#ifndef DEBUG_VDSERVICE
         return;
+#endif // DEBUG_VDSERVICE
     }
 
     // service is starting
@@ -351,7 +370,9 @@ VOID WINAPI VDService::main(DWORD argc, TCHAR* argv[])
     // service is stopped
     status->dwControlsAccepted &= ~VDSERVICE_ACCEPTED_CONTROLS;
     status->dwCurrentState = SERVICE_STOPPED;
+#ifndef DEBUG_VDSERVICE
     SetServiceStatus(s->_status_handle, status);
+#endif //DEBUG_VDSERVICE
 }
 
 typedef __declspec (align(1)) struct VDAgentDataChunk {
@@ -400,10 +421,12 @@ bool VDService::execute()
         return false;
     }
     vd_printf("Connected to server");
-    _events[0] = _vdi_port->get_event();
-    _events[1] = _pipe_state.read.overlap.hEvent;
-    _events[2] = _control_event;
-    _events[3] = _agent_proc_info.hProcess;
+    _events[VD_EVENT_PIPE_READ] = _pipe_state.read.overlap.hEvent;
+    _events[VD_EVENT_PIPE_WRITE] = _pipe_state.write.overlap.hEvent;
+    _events[VD_EVENT_CONTROL] = _control_event;
+    _events[VD_EVENT_READ] = _vdi_port->get_read_event();
+    _events[VD_EVENT_WRITE] = _vdi_port->get_write_event();
+    _events[VD_EVENT_AGENT] = _agent_proc_info.hProcess;
     _chunk_size = _chunk_port = 0;
     read_pipe();
     while (_running) {
@@ -429,19 +452,16 @@ bool VDService::execute()
             handle_pipe_data(0);
         }
         if (_running && (!cont || _pending_read || _pending_write)) {
-            DWORD events_count = _events[VD_EVENTS_COUNT - 1] ? VD_EVENTS_COUNT :
-                                                                VD_EVENTS_COUNT - 1;
-            DWORD wait_ret = WaitForMultipleObjectsEx(events_count, _events, FALSE,
-                                                      cont ? 0 : INFINITE, TRUE);
+            DWORD events_count = _events[VD_EVENT_AGENT] ? VD_EVENTS_COUNT : VD_EVENTS_COUNT - 1;
+            DWORD wait_ret = WaitForMultipleObjects(events_count, _events, FALSE,
+                                                              cont ? 0 : INFINITE);
             switch (wait_ret) {
-            case WAIT_OBJECT_0:
-                break;
-            case WAIT_OBJECT_0 + 1: {
+            case WAIT_OBJECT_0 + VD_EVENT_PIPE_READ: {
                 DWORD bytes = 0;
                 if (_pipe_connected && _pending_read) {
                     _pending_read = false;
-                    if (GetOverlappedResult(_pipe_state.pipe, &_pipe_state.read.overlap, &bytes,
-                                                                                           FALSE)) {
+                    if (GetOverlappedResult(_pipe_state.pipe, &_pipe_state.read.overlap,
+                                            &bytes, FALSE) || GetLastError() == ERROR_MORE_DATA) {
                         handle_pipe_data(bytes);
                         read_pipe();
                     } else {
@@ -452,10 +472,19 @@ bool VDService::execute()
                 }
                 break;
             }
-            case WAIT_OBJECT_0 + 2:
+            case WAIT_OBJECT_0 + VD_EVENT_PIPE_WRITE:
+                pipe_write_completion();
+                break;
+            case WAIT_OBJECT_0 + VD_EVENT_CONTROL:
                 vd_printf("Control event");
                 break;
-            case WAIT_OBJECT_0 + 3:
+            case WAIT_OBJECT_0 + VD_EVENT_READ:
+                _vdi_port->read_completion();                
+                break;
+            case WAIT_OBJECT_0 + VD_EVENT_WRITE:
+                _vdi_port->write_completion();
+                break;
+            case WAIT_OBJECT_0 + VD_EVENT_AGENT:
                 vd_printf("Agent killed");
                 if (_system_version == SYS_VER_WIN_XP) {
                     restart_agent(false);
@@ -463,11 +492,10 @@ bool VDService::execute()
                     kill_agent();
                 }
                 break;
-            case WAIT_IO_COMPLETION:
             case WAIT_TIMEOUT:
                 break;
             default:
-                vd_printf("WaitForMultipleObjectsEx failed %u", GetLastError());
+                vd_printf("WaitForMultipleObjects failed %u", GetLastError());
             }
         }
     }
@@ -766,7 +794,7 @@ bool VDService::launch_agent()
         vd_printf("ConnectNamedPipe() failed: %u", GetLastError());
         return false;
     }
-    _events[VD_EVENTS_COUNT - 1] = _agent_proc_info.hProcess;
+    _events[VD_EVENT_AGENT] = _agent_proc_info.hProcess;
     return true;
 }
 
@@ -779,7 +807,7 @@ bool VDService::kill_agent()
     if (!_agent_alive) {
         return true;
     }
-    _events[VD_EVENTS_COUNT - 1] = 0;
+    _events[VD_EVENT_AGENT] = 0;
     _agent_alive = false;
     if (_pipe_connected) {
         _pipe_connected = false;
@@ -846,31 +874,40 @@ void VDService::stop()
     }
 }
 
-VOID CALLBACK VDService::write_completion(DWORD err, DWORD bytes, LPOVERLAPPED overlap)
+void VDService::pipe_write_completion()
 {
-    VDService* s = _singleton;
-    VDPipeState* ps = &s->_pipe_state;
+    VDPipeState* ps = &this->_pipe_state;
+    DWORD bytes;
 
-    s->_pending_write = false;
-    if (!s->_running) {
-        return;
-    }
-    if (err) {
-        vd_printf("error %u", err);
+    if (!_running) {
         return;
     }
-    ps->write.start += bytes;
+    if (_pending_write) {
+        if (GetOverlappedResult(_pipe_state.pipe, &_pipe_state.write.overlap, &bytes, FALSE)) {
+            ps->write.start += bytes;
+            if (ps->write.start == ps->write.end) {
+                ps->write.start = ps->write.end = 0;
+            }
+        } else if (GetLastError() == ERROR_IO_PENDING){
+            vd_printf("Overlapped write is pending");
+            return;
+        } else {
+            vd_printf("GetOverlappedResult() failed : %d", GetLastError());
+        }
+	    _pending_write = false;
+	}
+
     if (ps->write.start < ps->write.end) {
-        s->_pending_write = true;
-        if (!WriteFileEx(ps->pipe, ps->write.data + ps->write.start,
-                         ps->write.end - ps->write.start, overlap, write_completion)) {
-            vd_printf("WriteFileEx() failed: %u", GetLastError());
-            s->_pending_write = false;
-            s->_pipe_connected = false;
-            DisconnectNamedPipe(s->_pipe_state.pipe);
+        _pending_write = true;
+        if (!WriteFile(ps->pipe, ps->write.data + ps->write.start,
+                       ps->write.end - ps->write.start, NULL, &_pipe_state.write.overlap)) {
+            vd_printf("WriteFile() failed: %u", GetLastError());
+            _pending_write = false;
+            _pipe_connected = false;
+            DisconnectNamedPipe(_pipe_state.pipe);
         }
     } else {
-        s->_pending_write = false;
+        _pending_write = false;
     }
 }
 
@@ -882,9 +919,9 @@ void VDService::read_pipe()
     if (ps->read.end < sizeof(ps->read.data)) {
         _pending_read = true;
         if (ReadFile(ps->pipe, ps->read.data + ps->read.end, sizeof(ps->read.data) - ps->read.end,
-                     &bytes, &ps->read.overlap)) {
+                     &bytes, &ps->read.overlap) || GetLastError() == ERROR_MORE_DATA) {
             _pending_read = false;
-            vd_printf("ReadFile without pending");
+            vd_printf("ReadFile without pending %u", bytes);
             handle_pipe_data(bytes);
             read_pipe();
         } else if (GetLastError() != ERROR_IO_PENDING) {
@@ -918,8 +955,11 @@ void VDService::handle_pipe_data(DWORD bytes)
             ps->read.start += sizeof(VDPipeMessage);
             continue;
         }
-        if (read_size < sizeof(VDPipeMessage) + pipe_msg->size ||
-                _vdi_port->write_ring_free_space() < sizeof(VDAgentDataChunk) + pipe_msg->size) {
+        if (read_size < sizeof(VDPipeMessage) + pipe_msg->size) {
+            break;
+        }
+        if (_vdi_port->write_ring_free_space() < sizeof(VDAgentDataChunk) + pipe_msg->size) {
+            //vd_printf("DEBUG: no space in write ring %u", _vdi_port->write_ring_free_space());
             break;
         }
         if (!_pending_reset) {
@@ -959,9 +999,6 @@ void VDService::handle_port_data()
                 break;
             }
             count = sizeof(VDPipeMessage) + chunk.size;
-            if (_pipe_state.write.start == _pipe_state.write.end) {
-                _pipe_state.write.start = _pipe_state.write.end = 0;
-            }
             if (_pipe_state.write.end + count > sizeof(_pipe_state.write.data)) {
                 vd_printf("chunk is too large, size %u port %u", chunk.size, chunk.port);
                 _running = false;
@@ -995,7 +1032,7 @@ void VDService::handle_port_data()
         }
     }
     if (_pipe_connected && chunks_count && !_pending_write) {
-        write_completion(0, 0, &_pipe_state.write.overlap);
+        pipe_write_completion();
     }
 }
 
@@ -1033,7 +1070,7 @@ void VDService::write_agent_control(uint32_t type, uint32_t opaque)
     msg->opaque = opaque;
     _pipe_state.write.end += sizeof(VDPipeMessage);
     if (!_pending_write) {
-        write_completion(0, 0, &_pipe_state.write.overlap);
+        pipe_write_completion();
     }
 }
 
diff --git a/vdservice/vdservice.vcproj b/vdservice/vdservice.vcproj
index 32ec7c8..1fd6827 100644
--- a/vdservice/vdservice.vcproj
+++ b/vdservice/vdservice.vcproj
@@ -65,7 +65,7 @@
 			/>
 			<Tool
 				Name="VCLinkerTool"
-				AdditionalDependencies="WtsApi32.lib Userenv.lib Version.lib"
+				AdditionalDependencies="WtsApi32.lib Userenv.lib Version.lib setupapi.lib"
 				LinkIncremental="2"
 				GenerateDebugInformation="true"
 				SubSystem="1"
@@ -216,7 +216,7 @@
 			/>
 			<Tool
 				Name="VCLinkerTool"
-				AdditionalDependencies="WtsApi32.lib Userenv.lib Version.lib"
+				AdditionalDependencies="WtsApi32.lib Userenv.lib Version.lib setupapi.lib"
 				LinkIncremental="1"
 				GenerateDebugInformation="true"
 				SubSystem="1"
-- 
1.5.5.6



More information about the Spice-devel mailing list