[Spice-devel] [PATCH 3/5] vdagent: use virtio-serial, remove pipe usage

Arnon Gilboa agilboa at redhat.com
Wed Nov 7 05:19:49 PST 2012


---
 vdagent/vdagent.cpp    |  467 +++++++++++++++++++++++-------------------------
 vdagent/vdagent.vcproj |   32 +++-
 2 files changed, 256 insertions(+), 243 deletions(-)

diff --git a/vdagent/vdagent.cpp b/vdagent/vdagent.cpp
index 078e50f..9bb0898 100644
--- a/vdagent/vdagent.cpp
+++ b/vdagent/vdagent.cpp
@@ -16,11 +16,14 @@
 */
 
 #include "vdcommon.h"
+#include "virtio_vdi_port.h"
+#include "pci_vdi_port.h"
 #include "desktop_layout.h"
 #include "display_setting.h"
 #include "ximage.h"
 #undef max
 #undef min
+#include <wtsapi32.h>
 #include <lmcons.h>
 #include <queue>
 #include <set>
@@ -56,6 +59,18 @@ static ImageType image_types[] = {
     {VD_AGENT_CLIPBOARD_IMAGE_BMP, CXIMAGE_FORMAT_BMP},
 };
 
+typedef struct ALIGN_VC VDIChunk {
+    VDIChunkHeader hdr;
+    uint8_t data[0];
+} ALIGN_GCC VDIChunk;
+
+#define VD_MESSAGE_HEADER_SIZE (sizeof(VDIChunk) + sizeof(VDAgentMessage))
+
+enum {
+    VD_EVENT_CONTROL = 0,
+    VD_STATIC_EVENTS_COUNT // Must be last
+};
+
 class VDAgent {
 public:
     static VDAgent* get();
@@ -74,7 +89,9 @@ private:
     bool handle_clipboard_request(VDAgentClipboardRequest* clipboard_request);
     void handle_clipboard_release();
     bool handle_display_config(VDAgentDisplayConfig* display_config, uint32_t port);
-    bool handle_control(VDPipeMessage* msg);
+    void handle_port_in();
+    void handle_port_out();
+    void handle_chunk(VDIChunk* chunk);
     void on_clipboard_grab();
     void on_clipboard_request(UINT format);
     void on_clipboard_release();
@@ -82,8 +99,6 @@ private:
                              DWORD mask, DWORD down_flag, DWORD up_flag);
     static HGLOBAL utf8_alloc(LPCSTR data, int size);
     static LRESULT CALLBACK wnd_proc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam);
-    static VOID CALLBACK read_completion(DWORD err, DWORD bytes, LPOVERLAPPED overlap);
-    static VOID CALLBACK write_completion(DWORD err, DWORD bytes, LPOVERLAPPED overlap);
     static DWORD WINAPI event_thread_proc(LPVOID param);
     static void dispatch_message(VDAgentMessage* msg, uint32_t port);
     uint32_t get_clipboard_format(uint32_t type);
@@ -91,14 +106,14 @@ private:
     DWORD get_cximage_format(uint32_t type);
     enum { owner_none, owner_guest, owner_client };
     void set_clipboard_owner(int new_owner);
-    enum { CONTROL_STOP, CONTROL_DESKTOP_SWITCH };
+    enum { CONTROL_STOP, CONTROL_DESKTOP_SWITCH, CONTROL_LOGON };
     void set_control_event(int control_command);
     void handle_control_event();
-    VDPipeMessage* new_message(DWORD bytes = 0);
-    void enqueue_message(VDPipeMessage* msg);
+    VDIChunk* new_chunk(DWORD bytes = 0);
+    void enqueue_chunk(VDIChunk* msg);
     bool write_message(uint32_t type, uint32_t size, void* data);
     bool write_clipboard(VDAgentMessage* msg, uint32_t size);
-    bool connect_pipe();
+    bool init_vdi_port();
     bool send_input();
     void set_display_depth(uint32_t depth);
     void load_display_setting();
@@ -117,20 +132,20 @@ private:
     DWORD _input_time;
     HANDLE _control_event;
     HANDLE _clipboard_event;
+    HANDLE* _events;
     VDAgentMessage* _in_msg;
     uint32_t _in_msg_pos;
+    uint32_t _events_count;
     bool _pending_input;
-    bool _pending_write;
     bool _running;
     bool _desktop_switch;
     DesktopLayout* _desktop_layout;
     DisplaySetting _display_setting;
-    VDPipeState _pipe_state;
-    mutex_t _write_mutex;
+    VDIPort* _vdi_port;
     mutex_t _control_mutex;
     mutex_t _message_mutex;
     std::queue<int> _control_queue;
-    std::queue<VDPipeMessage*> _message_queue;
+    std::queue<VDIChunk*> _message_queue;
 
     bool _logon_desktop;
     bool _display_setting_initialized;
@@ -164,14 +179,16 @@ VDAgent::VDAgent()
     , _input_time (0)
     , _control_event (NULL)
     , _clipboard_event (NULL)
+    , _events (NULL)
     , _in_msg (NULL)
     , _in_msg_pos (0)
+    , _events_count (0)
     , _pending_input (false)
-    , _pending_write (false)
     , _running (false)
     , _desktop_switch (false)
     , _desktop_layout (NULL)
     , _display_setting (VD_AGENT_REGISTRY_KEY)
+    , _vdi_port (NULL)
     , _logon_desktop (false)
     , _display_setting_initialized (false)
     , _client_caps (NULL)
@@ -186,8 +203,6 @@ VDAgent::VDAgent()
         _log = VDLog::get(log_path);
     }
     ZeroMemory(&_input, sizeof(INPUT));
-    ZeroMemory(&_pipe_state, sizeof(VDPipeState));
-    MUTEX_INIT(_write_mutex);
     MUTEX_INIT(_control_mutex);
     MUTEX_INIT(_message_mutex);
 
@@ -196,6 +211,7 @@ VDAgent::VDAgent()
 
 VDAgent::~VDAgent()
 {
+    delete _events;
     delete _log;
     delete[] _client_caps;
 }
@@ -260,7 +276,8 @@ bool VDAgent::run()
     if (_desktop_layout->get_display_count() == 0) {
         vd_printf("No QXL devices!");
     }
-    if (!connect_pipe()) {
+    if (!init_vdi_port()) {
+        vd_printf("Failed to create VDIPort instance");
         cleanup();
         return false;
     }
@@ -272,7 +289,14 @@ bool VDAgent::run()
         return false;
     }
     send_announce_capabilities(true);
-    read_completion(0, 0, &_pipe_state.read.overlap);
+
+    _events_count = VD_STATIC_EVENTS_COUNT + _vdi_port->get_num_events();
+    _events = new HANDLE[_events_count];
+    ZeroMemory(_events, _events_count);
+    _events[VD_EVENT_CONTROL] = _control_event;
+    _vdi_port->fill_events(&_events[VD_STATIC_EVENTS_COUNT]);
+    vd_printf("Connected to server");
+
     while (_running) {
         input_desktop_message_loop();
         if (_clipboard_owner == owner_guest) {
@@ -289,7 +313,7 @@ void VDAgent::cleanup()
 {
     CloseHandle(_control_event);
     CloseHandle(_clipboard_event);
-    CloseHandle(_pipe_state.pipe);
+    delete _vdi_port;
     delete _desktop_layout;
 }
 
@@ -317,6 +341,17 @@ void VDAgent::handle_control_event()
         case CONTROL_DESKTOP_SWITCH:
             _desktop_switch = true;
             break;
+        case CONTROL_LOGON:
+            vd_printf("session logon");
+            // loading the display settings for the current session's logged on user only
+            // after 1) we receive logon event, and 2) the desktop switched from Winlogon
+            if (!_logon_desktop) {
+                vd_printf("LOGON display setting");
+                _display_setting.load();
+            } else {
+                _logon_occured = true;
+            }
+            break;
         default:
             vd_printf("Unsupported control command %u", control_command);
         }
@@ -372,25 +407,57 @@ void VDAgent::input_desktop_message_loop()
         _running = false;
         return;
     }
+    if (!WTSRegisterSessionNotification(_hwnd, NOTIFY_FOR_ALL_SESSIONS)) {
+        vd_printf("WTSRegisterSessionNotification() failed: %lu", GetLastError());
+    }
     _hwnd_next_viewer = SetClipboardViewer(_hwnd);
     while (_running && !_desktop_switch) {
-        wait_ret = MsgWaitForMultipleObjectsEx(1, &_control_event, INFINITE, QS_ALLINPUT,
-                                               MWMO_ALERTABLE);
-        switch (wait_ret) {
-        case WAIT_OBJECT_0:
-            handle_control_event();
+        int cont_read = _vdi_port->read();
+        int cont_write = _vdi_port->write();
+        bool cont = false;
+
+        if (cont_read >= 0 && cont_write >= 0) {
+            cont = cont_read || cont_write;
+        } else if (cont_read == VDI_PORT_ERROR || cont_write == VDI_PORT_ERROR) {
+            vd_printf("VDI Port error, read %d write %d", cont_read, cont_write);
+            _running = false;
             break;
-        case WAIT_OBJECT_0 + 1:
+        } else if (cont_read == VDI_PORT_RESET || cont_write == VDI_PORT_RESET) {
+            vd_printf("VDI Port reset, read %d write %d", cont_read, cont_write);
+            _running = false;
+            break;
+        }
+        if (cont_read) {
+            handle_port_in();
+        }
+        if (cont_write) {
+            handle_port_out();
+        }
+
+        wait_ret = MsgWaitForMultipleObjectsEx(_events_count, _events, cont ? 0 : INFINITE,
+                                               QS_ALLINPUT, MWMO_ALERTABLE);
+        if (wait_ret == WAIT_OBJECT_0 + _events_count) {
             while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
                 TranslateMessage(&msg);
                 DispatchMessage(&msg);
             }
+            continue;
+        }
+        switch (wait_ret) {
+        case WAIT_OBJECT_0 + VD_EVENT_CONTROL:
+            handle_control_event();
             break;
         case WAIT_IO_COMPLETION:
-            break;
         case WAIT_TIMEOUT:
+            break;
         default:
-            vd_printf("MsgWaitForMultipleObjectsEx(): %lu", wait_ret);
+            DWORD vdi_event = wait_ret - VD_STATIC_EVENTS_COUNT - WAIT_OBJECT_0;
+            if (vdi_event >= 0 && vdi_event < _vdi_port->get_num_events()) {
+                _running = _vdi_port->handle_event(vdi_event);
+            } else {
+                vd_printf("MsgWaitForMultipleObjectsEx failed: %lu %lu", wait_ret, GetLastError());
+                _running = false;
+            }
         }
     }
     _desktop_switch = false;
@@ -399,6 +466,7 @@ void VDAgent::input_desktop_message_loop()
         _pending_input = false;
     }
     ChangeClipboardChain(_hwnd, _hwnd_next_viewer);
+    WTSUnRegisterSessionNotification(_hwnd);
     DestroyWindow(_hwnd);
     CloseDesktop(hdesk);
 }
@@ -512,7 +580,7 @@ bool VDAgent::handle_mouse_event(VDAgentMouseState* state)
 
 bool VDAgent::handle_mon_config(VDAgentMonitorsConfig* mon_config, uint32_t port)
 {
-    VDPipeMessage* reply_pipe_msg;
+    VDIChunk* reply_chunk;
     VDAgentMessage* reply_msg;
     VDAgentReply* reply;
     size_t display_count;
@@ -540,14 +608,13 @@ bool VDAgent::handle_mon_config(VDAgentMonitorsConfig* mon_config, uint32_t port
     }
 
     DWORD msg_size = VD_MESSAGE_HEADER_SIZE + sizeof(VDAgentReply);
-    reply_pipe_msg = new_message(msg_size);
-    if (!reply_pipe_msg) {
+    reply_chunk = new_chunk(msg_size);
+    if (!reply_chunk) {
         return false;
     }
-    reply_pipe_msg->type = VD_AGENT_COMMAND;
-    reply_pipe_msg->opaque = port;
-    reply_pipe_msg->size = sizeof(VDAgentMessage) + sizeof(VDAgentReply);
-    reply_msg = (VDAgentMessage*)reply_pipe_msg->data;
+    reply_chunk->hdr.port = port;
+    reply_chunk->hdr.size = sizeof(VDAgentMessage) + sizeof(VDAgentReply);
+    reply_msg = (VDAgentMessage*)reply_chunk->data;
     reply_msg->protocol = VD_AGENT_PROTOCOL;
     reply_msg->type = VD_AGENT_REPLY;
     reply_msg->opaque = 0;
@@ -555,7 +622,7 @@ bool VDAgent::handle_mon_config(VDAgentMonitorsConfig* mon_config, uint32_t port
     reply = (VDAgentReply*)reply_msg->data;
     reply->type = VD_AGENT_MONITORS_CONFIG;
     reply->error = display_count ? VD_AGENT_SUCCESS : VD_AGENT_ERROR;
-    enqueue_message(reply_pipe_msg);
+    enqueue_chunk(reply_chunk);
     return true;
 }
 
@@ -661,22 +728,21 @@ void VDAgent::load_display_setting()
 bool VDAgent::send_announce_capabilities(bool request)
 {
     DWORD msg_size;
-    VDPipeMessage* caps_pipe_msg;
+    VDIChunk* caps_chunk;
     VDAgentMessage* caps_msg;
     VDAgentAnnounceCapabilities* caps;
     uint32_t caps_size;
     uint32_t internal_msg_size = sizeof(VDAgentAnnounceCapabilities) + VD_AGENT_CAPS_BYTES;
 
     msg_size = VD_MESSAGE_HEADER_SIZE + internal_msg_size;
-    caps_pipe_msg = new_message(msg_size);
-    if (!caps_pipe_msg) {
+    caps_chunk = new_chunk(msg_size);
+    if (!caps_chunk) {
         return false;
     }
     caps_size = VD_AGENT_CAPS_SIZE;
-    caps_pipe_msg->type = VD_AGENT_COMMAND;
-    caps_pipe_msg->opaque = VDP_CLIENT_PORT;
-    caps_pipe_msg->size = sizeof(VDAgentMessage) + internal_msg_size;
-    caps_msg = (VDAgentMessage*)caps_pipe_msg->data;
+    caps_chunk->hdr.port = VDP_CLIENT_PORT;
+    caps_chunk->hdr.size = sizeof(VDAgentMessage) + internal_msg_size;
+    caps_msg = (VDAgentMessage*)caps_chunk->data;
     caps_msg->protocol = VD_AGENT_PROTOCOL;
     caps_msg->type = VD_AGENT_ANNOUNCE_CAPABILITIES;
     caps_msg->opaque = 0;
@@ -693,7 +759,7 @@ bool VDAgent::send_announce_capabilities(bool request)
     for (uint32_t i = 0 ; i < caps_size; ++i) {
         vd_printf("%X", caps->caps[i]);
     }
-    enqueue_message(caps_pipe_msg);
+    enqueue_chunk(caps_chunk);
     return true;
 }
 
@@ -722,7 +788,7 @@ bool VDAgent::handle_announce_capabilities(VDAgentAnnounceCapabilities* announce
 bool VDAgent::handle_display_config(VDAgentDisplayConfig* display_config, uint32_t port)
 {
     DisplaySettingOptions disp_setting_opts;
-    VDPipeMessage* reply_pipe_msg;
+    VDIChunk* reply_chunk;
     VDAgentMessage* reply_msg;
     VDAgentReply* reply;
     DWORD msg_size;
@@ -746,14 +812,13 @@ bool VDAgent::handle_display_config(VDAgentDisplayConfig* display_config, uint32
     }
 
     msg_size = VD_MESSAGE_HEADER_SIZE + sizeof(VDAgentReply);
-    reply_pipe_msg = new_message(msg_size);
-    if (!reply_pipe_msg) {
+    reply_chunk = new_chunk(msg_size);
+    if (!reply_chunk) {
         return false;
     }
-    reply_pipe_msg->type = VD_AGENT_COMMAND;
-    reply_pipe_msg->opaque = port;
-    reply_pipe_msg->size = sizeof(VDAgentMessage) + sizeof(VDAgentReply);
-    reply_msg = (VDAgentMessage*)reply_pipe_msg->data;
+    reply_chunk->hdr.port = port;
+    reply_chunk->hdr.size = sizeof(VDAgentMessage) + sizeof(VDAgentReply);
+    reply_msg = (VDAgentMessage*)reply_chunk->data;
     reply_msg->protocol = VD_AGENT_PROTOCOL;
     reply_msg->type = VD_AGENT_REPLY;
     reply_msg->opaque = 0;
@@ -761,50 +826,12 @@ bool VDAgent::handle_display_config(VDAgentDisplayConfig* display_config, uint32
     reply = (VDAgentReply*)reply_msg->data;
     reply->type = VD_AGENT_DISPLAY_CONFIG;
     reply->error = VD_AGENT_SUCCESS;
-    enqueue_message(reply_pipe_msg);
-    return true;
-}
-
-bool VDAgent::handle_control(VDPipeMessage* msg)
-{
-    switch (msg->type) {
-    case VD_AGENT_RESET: {
-        vd_printf("Agent reset");
-        VDPipeMessage* ack = new_message(sizeof(VDPipeMessage));
-        if (!ack) {
-            return false;
-        }
-        ack->type = VD_AGENT_RESET_ACK;
-        ack->opaque = msg->opaque;
-        enqueue_message(ack);
-        break;
-    }
-    case VD_AGENT_SESSION_LOGON:
-        vd_printf("session logon");
-        // loading the display settings for the current session's logged on user only
-        // after 1) we receive logon event, and 2) the desktop switched from Winlogon
-        if (!_logon_desktop) {
-            vd_printf("LOGON display setting");
-            _display_setting.load();
-        } else {
-            _logon_occured = true;
-        }
-        break;
-    case VD_AGENT_QUIT:
-        vd_printf("Agent quit");
-        _running = false;
-        break;
-    default:
-        vd_printf("Unsupported control %u", msg->type);
-        return false;
-    }
+    enqueue_chunk(reply_chunk);
     return true;
 }
 
 #define MIN(a, b) ((a) > (b) ? (b) : (a))
 
-//FIXME: division to max size chunks should NOT be here, but in the service
-//       here we should write the max possible size to the pipe
 bool VDAgent::write_clipboard(VDAgentMessage* msg, uint32_t size)
 {
     uint32_t pos = 0;
@@ -814,18 +841,17 @@ bool VDAgent::write_clipboard(VDAgentMessage* msg, uint32_t size)
     //FIXME: do it smarter - no loop, no memcopy
     MUTEX_LOCK(_message_mutex);
     while (pos < size) {
-        DWORD n = MIN(sizeof(VDPipeMessage) + size - pos, VD_AGENT_MAX_DATA_SIZE);
-        VDPipeMessage* pipe_msg = new_message(n);
-        if (!pipe_msg) {
+        DWORD n = MIN(sizeof(VDIChunk) + size - pos, VD_AGENT_MAX_DATA_SIZE);
+        VDIChunk* chunk = new_chunk(n);
+        if (!chunk) {
             ret = false;
             break;
         }
-        pipe_msg->type = VD_AGENT_COMMAND;
-        pipe_msg->opaque = VDP_CLIENT_PORT;
-        pipe_msg->size = n - sizeof(VDPipeMessage);
-        memcpy(pipe_msg->data, (char*)msg + pos, n - sizeof(VDPipeMessage));
-        enqueue_message(pipe_msg);
-        pos += (n - sizeof(VDPipeMessage));
+        chunk->hdr.port = VDP_CLIENT_PORT;
+        chunk->hdr.size = n - sizeof(VDIChunk);
+        memcpy(chunk->data, (char*)msg + pos, n - sizeof(VDIChunk));
+        enqueue_chunk(chunk);
+        pos += (n - sizeof(VDIChunk));
     }
     MUTEX_UNLOCK(_message_mutex);
     return ret;
@@ -833,17 +859,16 @@ bool VDAgent::write_clipboard(VDAgentMessage* msg, uint32_t size)
 
 bool VDAgent::write_message(uint32_t type, uint32_t size = 0, void* data = NULL)
 {
-    VDPipeMessage* pipe_msg;
+    VDIChunk* chunk;
     VDAgentMessage* msg;
 
-    pipe_msg = new_message(VD_MESSAGE_HEADER_SIZE + size);
-    if (!pipe_msg) {
+    chunk = new_chunk(VD_MESSAGE_HEADER_SIZE + size);
+    if (!chunk) {
         return false;
     }
-    pipe_msg->type = VD_AGENT_COMMAND;
-    pipe_msg->opaque = VDP_CLIENT_PORT;
-    pipe_msg->size = sizeof(VDAgentMessage) + size;
-    msg = (VDAgentMessage*)pipe_msg->data;
+    chunk->hdr.port = VDP_CLIENT_PORT;
+    chunk->hdr.size = sizeof(VDAgentMessage) + size;
+    msg = (VDAgentMessage*)chunk->data;
     msg->protocol = VD_AGENT_PROTOCOL;
     msg->type = type;
     msg->opaque = 0;
@@ -851,7 +876,7 @@ bool VDAgent::write_message(uint32_t type, uint32_t size = 0, void* data = NULL)
     if (size && data) {
         memcpy(msg->data, data, size);
     }
-    enqueue_message(pipe_msg);
+    enqueue_chunk(chunk);
     return true;
 }
 
@@ -1119,32 +1144,29 @@ void VDAgent::set_clipboard_owner(int new_owner)
     _clipboard_owner = new_owner;
 }
 
-bool VDAgent::connect_pipe()
+VDIPort *create_virtio_vdi_port()
 {
-    VDAgent* a = _singleton;
-    HANDLE pipe;
+    return new VirtioVDIPort();
+}
 
-    ZeroMemory(&a->_pipe_state, sizeof(VDPipeState));
-    if (!WaitNamedPipe(VD_SERVICE_PIPE_NAME, NMPWAIT_USE_DEFAULT_WAIT)) {
-        vd_printf("WaitNamedPipe() failed: %lu", GetLastError());
-        return false;
-    }
-    //assuming vdservice created the named pipe before creating this vdagent process
-    pipe = CreateFile(VD_SERVICE_PIPE_NAME, GENERIC_READ | GENERIC_WRITE,
-                      0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
-    if (pipe == INVALID_HANDLE_VALUE) {
-        vd_printf("CreateFile() failed: %lu", GetLastError());
-        return false;
-    }
-    DWORD pipe_mode = PIPE_READMODE_MESSAGE | PIPE_WAIT;
-    if (!SetNamedPipeHandleState(pipe, &pipe_mode, NULL, NULL)) {
-        vd_printf("SetNamedPipeHandleState() failed: %lu", GetLastError());
-        CloseHandle(pipe);
-        return false;
+VDIPort *create_pci_vdi_port()
+{
+    return new PCIVDIPort();
+}
+
+bool VDAgent::init_vdi_port()
+{
+    VDIPort* (*creators[])(void) = { create_virtio_vdi_port, create_pci_vdi_port };
+
+    for (unsigned int i = 0 ; i < sizeof(creators)/sizeof(creators[0]); ++i) {
+        _vdi_port = creators[i]();
+        if (_vdi_port->init()) {
+            return true;
+        }
+        delete _vdi_port;
     }
-    a->_pipe_state.pipe = pipe;
-    vd_printf("Connected to service pipe");
-    return true;
+    _vdi_port = NULL;
+    return false;
 }
 
 void VDAgent::dispatch_message(VDAgentMessage* msg, uint32_t port)
@@ -1190,143 +1212,105 @@ void VDAgent::dispatch_message(VDAgentMessage* msg, uint32_t port)
     }
 }
 
-VOID CALLBACK VDAgent::read_completion(DWORD err, DWORD bytes, LPOVERLAPPED overlap)
+void VDAgent::handle_port_in()
 {
-    VDAgent* a = _singleton;
-    VDPipeState* ps = &a->_pipe_state;
-    DWORD len;
-
-    if (!a->_running) {
-        return;
-    }
-    if (err) {
-        vd_printf("vdservice disconnected (%lu)", err);
-        a->_running = false;
-        return;
-    }
-    ps->read.end += bytes;
-    while (a->_running && (len = ps->read.end - ps->read.start) >= sizeof(VDPipeMessage)) {
-        VDPipeMessage* pipe_msg = (VDPipeMessage*)&ps->read.data[ps->read.start];
-
-        if (pipe_msg->type != VD_AGENT_COMMAND) {
-            a->handle_control(pipe_msg);
-            ps->read.start += sizeof(VDPipeMessage);
-            continue;
-        }
-        if (len < sizeof(VDPipeMessage) + pipe_msg->size) {
-            break;
-        }
-
-        //FIXME: currently assumes that multi-part msg arrives only from client port
-        if (a->_in_msg_pos == 0 || pipe_msg->opaque == VDP_SERVER_PORT) {
-            if (len < VD_MESSAGE_HEADER_SIZE) {
+    static char buf[sizeof(VDIChunk) + VD_AGENT_MAX_DATA_SIZE] = {0, };
+    VDIChunk* chunk = (VDIChunk*)buf;
+    uint32_t chunk_size;
+
+    while (_running)  {
+        if (!chunk->hdr.size && _vdi_port->read_ring_size() >= sizeof(VDIChunk)) {
+            if (_vdi_port->ring_read(chunk, sizeof(VDIChunk)) != sizeof(VDIChunk)) {
+                vd_printf("ring_read of chunk header failed");
+                _running = false;
                 break;
             }
-            VDAgentMessage* msg = (VDAgentMessage*)pipe_msg->data;
-            if (msg->protocol != VD_AGENT_PROTOCOL) {
-                vd_printf("Invalid protocol %u bytes %lu", msg->protocol, bytes);
-                a->_running = false;
+            if (sizeof(VDIChunk) + chunk->hdr.size > sizeof(buf)) {
+                vd_printf("chunk is too large, size %u port %u", chunk->hdr.size, chunk->hdr.port);
+                _running = false;
                 break;
             }
-            uint32_t msg_size = sizeof(VDAgentMessage) + msg->size;
-            if (pipe_msg->size == msg_size) {
-                dispatch_message(msg, pipe_msg->opaque);
-            } else {
-                ASSERT(pipe_msg->size < msg_size);
-                a->_in_msg = (VDAgentMessage*)new uint8_t[msg_size];
-                memcpy(a->_in_msg, pipe_msg->data, pipe_msg->size);
-                a->_in_msg_pos = pipe_msg->size;
-            }
-        } else {
-            memcpy((uint8_t*)a->_in_msg + a->_in_msg_pos, pipe_msg->data, pipe_msg->size);
-            a->_in_msg_pos += pipe_msg->size;
-            if (a->_in_msg_pos == sizeof(VDAgentMessage) + a->_in_msg->size) {
-                dispatch_message(a->_in_msg, 0);
-                a->_in_msg_pos = 0;
-                delete[] (uint8_t *)a->_in_msg;
-                a->_in_msg = NULL;
-            }
         }
-
-        ps->read.start += (sizeof(VDPipeMessage) + pipe_msg->size);
-        if (ps->read.start == ps->read.end) {
-            ps->read.start = ps->read.end = 0;
+        chunk_size = chunk->hdr.size;
+        if (!chunk_size || _vdi_port->read_ring_size() < chunk_size) {
+            break;
         }
-    }
-    if (a->_running && ps->read.end < sizeof(ps->read.data) &&
-        !ReadFileEx(ps->pipe, ps->read.data + ps->read.end, sizeof(ps->read.data) - ps->read.end,
-                    overlap, read_completion)) {
-        vd_printf("ReadFileEx() failed: %lu", GetLastError());
-        a->_running = false;
-    }
+        if (_vdi_port->ring_read(chunk->data, chunk_size) != chunk_size) {
+            vd_printf("ring_read of chunk data failed");
+            _running = false;
+            break;
+        }
+        handle_chunk(chunk);
+        chunk->hdr.size = 0;
+   }
 }
 
-VOID CALLBACK VDAgent::write_completion(DWORD err, DWORD bytes, LPOVERLAPPED overlap)
+void VDAgent::handle_chunk(VDIChunk* chunk)
 {
-    VDAgent* a = _singleton;
-    VDPipeState* ps = &a->_pipe_state;
-    DWORD size_left;
-
-    if (!a->_running) {
-        return;
-    }
-    if (err) {
-        vd_printf("vdservice disconnected (%lu)", err);
-        a->_running = false;
-        return;
-    }
-    MUTEX_LOCK(a->_write_mutex);
-    ps->write.start += bytes;
-    if (ps->write.start == ps->write.end) {
-        ps->write.start = ps->write.end = 0;
+    //FIXME: currently assumes that multi-part msg arrives only from client port
+    if (_in_msg_pos == 0 || chunk->hdr.port == VDP_SERVER_PORT) {
+        if (chunk->hdr.size < sizeof(VDAgentMessage)) {
+            return;
+        }
+        VDAgentMessage* msg = (VDAgentMessage*)chunk->data;
+        if (msg->protocol != VD_AGENT_PROTOCOL) {
+            vd_printf("Invalid protocol %u", msg->protocol);
+            _running = false;
+            return;
+        }
+        uint32_t msg_size = sizeof(VDAgentMessage) + msg->size;
+        if (chunk->hdr.size == msg_size) {
+            dispatch_message(msg, chunk->hdr.port);
+        } else {
+            ASSERT(chunk->hdr.size < msg_size);
+            _in_msg = (VDAgentMessage*)new uint8_t[msg_size];
+            memcpy(_in_msg, chunk->data, chunk->hdr.size);
+            _in_msg_pos = chunk->hdr.size;
+        }
+    } else {
+        memcpy((uint8_t*)_in_msg + _in_msg_pos, chunk->data, chunk->hdr.size);
+        _in_msg_pos += chunk->hdr.size;
+        if (_in_msg_pos == sizeof(VDAgentMessage) + _in_msg->size) {
+            dispatch_message(_in_msg, 0);
+            _in_msg_pos = 0;
+            delete[] (uint8_t *)_in_msg;
+            _in_msg = NULL;
+        }
     }
+}
 
-    MUTEX_LOCK(a->_message_mutex);
-    size_left = sizeof(a->_pipe_state.write.data) - a->_pipe_state.write.end;
-    while (!a->_message_queue.empty()) {
-        VDPipeMessage* msg = a->_message_queue.front();
-        DWORD size = sizeof(VDPipeMessage) + msg->size;
+void VDAgent::handle_port_out()
+{
+    MUTEX_LOCK(_message_mutex);
+    while (_running && !_message_queue.empty()) {
+        VDIChunk* chunk = _message_queue.front();
+        DWORD size = sizeof(VDIChunk) + chunk->hdr.size;
 
-        if (size > size_left) {
+        if (size > _vdi_port->write_ring_free_space()) {
             break;
         }
-        a->_message_queue.pop();
-        memcpy(a->_pipe_state.write.data + a->_pipe_state.write.end, msg, size);
-        a->_pipe_state.write.end += size;
-        size_left -= size;
-        delete msg;
-    }
-    MUTEX_UNLOCK(a->_message_mutex);
-
-    if (ps->write.start < ps->write.end) {
-        if (WriteFileEx(ps->pipe, ps->write.data + ps->write.start,
-                               ps->write.end - ps->write.start, overlap, write_completion)) {
-            a->_pending_write = true;
-        } else {
-            vd_printf("WriteFileEx() failed: %lu", GetLastError());
-            a->_running = false;
+        _message_queue.pop();
+        if (_vdi_port->ring_write(chunk, size) != size) {
+            vd_printf("ring_write failed");
+            _running = false;
+            return;
         }
-    } else {
-        a->_pending_write = false;
+        delete chunk;
     }
-    MUTEX_UNLOCK(a->_write_mutex);
+    MUTEX_UNLOCK(_message_mutex);
 }
 
-VDPipeMessage* VDAgent::new_message(DWORD bytes)
+VDIChunk* VDAgent::new_chunk(DWORD bytes)
 {
-    return (VDPipeMessage*)(new char[bytes]);
+    return (VDIChunk*)(new char[bytes]);
 }
 
-void VDAgent::enqueue_message(VDPipeMessage* msg)
+void VDAgent::enqueue_chunk(VDIChunk* chunk)
 {
     MUTEX_LOCK(_message_mutex);
-    _message_queue.push(msg);
+    _message_queue.push(chunk);
     MUTEX_UNLOCK(_message_mutex);
-    MUTEX_LOCK(_write_mutex);
-    if (!_pending_write) {
-        write_completion(0, 0, &_pipe_state.write.overlap);
-    }
-    MUTEX_UNLOCK(_write_mutex);
+    handle_port_out();
 }
 
 LRESULT CALLBACK VDAgent::wnd_proc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
@@ -1369,6 +1353,11 @@ LRESULT CALLBACK VDAgent::wnd_proc(HWND hwnd, UINT message, WPARAM wparam, LPARA
             a->set_control_event(CONTROL_STOP);
         }
         break;
+    case WM_WTSSESSION_CHANGE:
+        if (wparam == WTS_SESSION_LOGON) {
+            a->set_control_event(CONTROL_LOGON);
+        }
+        break;
     default:
         return DefWindowProc(hwnd, message, wparam, lparam);
     }
diff --git a/vdagent/vdagent.vcproj b/vdagent/vdagent.vcproj
index 151a643..b99297e 100644
--- a/vdagent/vdagent.vcproj
+++ b/vdagent/vdagent.vcproj
@@ -65,7 +65,7 @@
 			/>
 			<Tool
 				Name="VCLinkerTool"
-				AdditionalDependencies="Version.lib zlibwapiD.lib png_d.lib cximage_d.lib"
+				AdditionalDependencies="Version.lib zlibwapiD.lib png_d.lib cximage_d.lib wtsapi32.lib"
 				LinkIncremental="2"
 				AdditionalLibraryDirectories=""$(SPICE_LIBS)\lib""
 				GenerateDebugInformation="true"
@@ -143,7 +143,7 @@
 			/>
 			<Tool
 				Name="VCLinkerTool"
-				AdditionalDependencies="Version.lib zlibwapiD.lib png_d.lib cximage_d.lib"
+				AdditionalDependencies="Version.lib zlibwapiD.lib png_d.lib cximage_d.lib wtsapi32.lib"
 				LinkIncremental="2"
 				AdditionalLibraryDirectories=""$(SPICE_LIBS)\lib64""
 				IgnoreDefaultLibraryNames=""
@@ -220,7 +220,7 @@
 			/>
 			<Tool
 				Name="VCLinkerTool"
-				AdditionalDependencies="Version.lib zlibwapi.lib png.lib cximage.lib"
+				AdditionalDependencies="Version.lib zlibwapi.lib png.lib cximage.lib wtsapi32.lib"
 				LinkIncremental="1"
 				AdditionalLibraryDirectories=""$(SPICE_LIBS)\lib""
 				GenerateDebugInformation="true"
@@ -299,7 +299,7 @@
 			/>
 			<Tool
 				Name="VCLinkerTool"
-				AdditionalDependencies="Version.lib zlibwapi.lib png.lib cximage.lib"
+				AdditionalDependencies="Version.lib zlibwapi.lib png.lib cximage.lib wtsapi32.lib"
 				LinkIncremental="1"
 				AdditionalLibraryDirectories=""$(SPICE_LIBS)\lib64""
 				GenerateDebugInformation="true"
@@ -350,13 +350,25 @@
 				>
 			</File>
 			<File
+				RelativePath=".\pci_vdi_port.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\vdagent.cpp"
 				>
 			</File>
 			<File
+				RelativePath=".\vdi_port.cpp"
+				>
+			</File>
+			<File
 				RelativePath="..\common\vdlog.cpp"
 				>
 			</File>
+			<File
+				RelativePath=".\virtio_vdi_port.cpp"
+				>
+			</File>
 		</Filter>
 		<Filter
 			Name="Header Files"
@@ -372,6 +384,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\pci_vdi_port.h"
+				>
+			</File>
+			<File
 				RelativePath=".\resource.h"
 				>
 			</File>
@@ -380,9 +396,17 @@
 				>
 			</File>
 			<File
+				RelativePath=".\vdi_port.h"
+				>
+			</File>
+			<File
 				RelativePath="..\common\vdlog.h"
 				>
 			</File>
+			<File
+				RelativePath=".\virtio_vdi_port.h"
+				>
+			</File>
 		</Filter>
 		<Filter
 			Name="Resource Files"
-- 
1.7.4.1



More information about the Spice-devel mailing list