Introduce I/O channels framework
Subsystems (2)
cover Introduce I/O channels framework
This series of patches is a first formal submission for review of another chunk of my large series supporting TLS across chardevs, VNC and migration, previously shown here:
https://lists.gnu.org/archive/html/qemu-devel/2015-09/msg00829.html
In this series, I have provided only the general I/O channels framework, which will ultimately be used by VNC, chardev and migration code, whicih currently work as follows:
- VNC: uses raw sockets APIs directly, layering in TLS, SASL and websockets where needed. This has resulted in what should be fairly generic code for TLS and websockets being entangled with the rest of the VNC server code.
- Chardevs: uses GLib's GIOChannel framework. This only provides implementations for sockets and files. Its API lacks support for using struct iovec for read/writes, file descriptor passing, misc sockets ioctls/fcntls. While you can work around these problems by accessing the underling file descriptor directly, this breaks the encapsulation, requiring callers to know about specific implementations. It also does not have integration with QEMU Error reporting framework. So while the GIOChannel is extensible, extending it would result in throwing away pretty much the entire of the existing implementations
- Migration: uses QEMUFile framework. The provides an abstract interface for I/O channels used during migration. It has impls for sockets, files, memory buffers & commands. While it has struct iovec support for writes, it does not have the same for reads. It also doesn't support file descriptor passing or control of the sockets ioctls/fcntls. It also does not have any explicit event loop integration, expecting the callers to directly access the underling file desctriptor and setup events on that. This limits its utility in cases where you need channels which are not backed by file descriptors. It has no integration with QEMU Error object for error reporting, has fixed semantics wrt writes ie must always do a full write, no partial writes, and most strangely forces either input or output mode, but refuses to allow both, so no bi-directional channels!
Out of all of these, the migration code is probably closest to what is needed, but is still a good way off from being a generic framework that be can reused outside of the migration code.
There is also the GLib GIO library which provides a generic framework, but we can't depend on that due to the minimum GLib requirement. It also has various missing pieces such as file descriptor passing, and no support for struct iovec either.
Hence, this series was born, which tries to take the best of the designs for the GIOChannel, QIO and QEMUFile code to provide QIOChannel. Right from the start this new framework is explicitly isolated from any other QEMU subsystem, so its implementation will not get mixed up with specific use cases.
The QIOChannel abstract base class defines the overall API contract
- qio_channel_{write,writev,write_full} for writing data. The underling impl always uses struct iovec, but non-iov APIs are provided as simple wrappers for convenience
- qio_channel_{read,readv,read_full} for reading data. The underling impl always uses struct iovec, but non-iov APIs are provided as simple wrappers for convenience
- qio_channel_has_feature to determine which optional features the channel supports - eg file descriptor passing, nagle, etc
- qio_channel_set_{blocking,delay,cork} for various fcntl and ioctl controls
- qio_channel_{close,shutdown} for closing the I/O stream or individual channels
- qio_channel_seek for random access channels
- qio_channel_{add,create}_watch for integration into the main loop for event notifications
- qio_channel_wait for blocking of execution pending an event notification
- qio_channel_yield for switching coroutine until an event notification
All the APIs support Error ** object arguments where needed. The object is built using QOM, in order to get reference counting and sub-classing with type checking. They are *not* user creatable/visible objects though - these are internal infrastructure, so we will be free to adapt APIs/objects at will over time.
In this series there are a variety of implementations. Some of them provide an actual base layer data transport, while others provide a data translation layer:
- QIOChannelSocket - for socket based I/O, IPv4, IPV6 & UNIX, both stream and datagram.
- QIOChannelFile - any non-socket file, plain file, char dev, fifo, pipe, etc
- QIOChannelTLS - data translation layer to apply TLS protocol
- QIOChannelWebsock - data translation layer to apply the websockets server protocol
- QIOChannelCommand - spawn & communicate with a command via pipes
- QIOChannelBuffer - a simple in-memory buffer
These 6 implementations are sufficient to support the current needs of the VNC server, chardev network backends, and all the various migration protocols, except RDMA (which is part of the larger RFC series I've posted previously).
The sockets implementation in particular solves a long standing limitation of the qemu-sockets.c API by providing a truely asynchronous API for establishing listener sockets / connections. The current code is only async for the socket establishment, but still blocks the caller for DNS resolution.
I have not attempted to make the socket implementation support multiple listener sockets in a single object. I looked at this but it would significantly complicate the QIOChannelSocket implementation. I intend to suggest an alternative approach for that problem at a later date, whereby we define a QIONetworkService object, which provides the infrastructure for managing multiple QIOChannelSocket listeners, and clients, which was resuable for any QEMU network service.
There are unit tests for the socket, file, tls, and command channel implementations. I didn't create unit test for the websock impl, as it needs a websock client to usefully test it which doesn't exist in QEMU yet. The memory buffer impl also lacks unit tests for now, simply due to not getting around to it yet.
This series merely introduces all the new framework. To keep it an acceptable length for review, it doesn't include any of the patches which actually use the new APIs. If reviewers want to see real world usage of these APIs, they can refer to my previous RFC series.
https://lists.gnu.org/archive/html/qemu-devel/2015-09/msg00829.html
In particular
- VNC conversion to QIOChannelSocket
https://lists.gnu.org/archive/html/qemu-devel/2015-09/msg00851.html
- VNC conversion to QIOChannelTLS
https://lists.gnu.org/archive/html/qemu-devel/2015-09/msg00845.html
- VNC conversion to QIOChannelWebsock
https://lists.gnu.org/archive/html/qemu-devel/2015-09/msg00842.html
- Chardev conversion to QIOChannelSocket
https://lists.gnu.org/archive/html/qemu-devel/2015-09/msg00849.html
- Chardev addition of TLS support
https://lists.gnu.org/archive/html/qemu-devel/2015-09/msg00848.html
- All the migration ones, but most interesting addition of TLS
https://lists.gnu.org/archive/html/qemu-devel/2015-09/msg00871.html
David Gilbert had some feedback on my previous RFC series which I have incorporated into this new posting. I did forget his comments about using #defines instead of magic constants in the websock patch, so that's still on my todo list.
Finally I've listed myself as maintainer for the new io/ and include/io/ directory prefixes.
Daniel P. Berrange (16):
sockets: add helpers for creating SocketAddress from a socket
sockets: move qapi_copy_SocketAddress into qemu-sockets.c
sockets: allow port to be NULL when listening on IP address
ui: convert VNC startup code to use SocketAddress
osdep: add qemu_fork() wrapper for safely handling signals
coroutine: move into libqemuutil.a library
io: add abstract QIOChannel classes
io: add helper module for creating watches on FDs
io: pull Buffer code out of VNC module
io: add QIOTask class for async operations
io: add QIOChannelSocket class
io: add QIOChannelFile class
io: add QIOChannelTLS class
io: add QIOChannelWebsock class
io: add QIOChannelCommand class
io: add QIOChannelBuffer class
MAINTAINERS | 7 +
Makefile | 2 +
Makefile.objs | 9 +-
Makefile.target | 2 +
block.c | 2 +-
block/qcow2.h | 2 +-
block/vdi.c | 2 +-
block/write-threshold.c | 2 +-
blockjob.c | 2 +-
configure | 11 +
hw/9pfs/codir.c | 2 +-
hw/9pfs/cofile.c | 2 +-
hw/9pfs/cofs.c | 2 +-
hw/9pfs/coxattr.c | 2 +-
hw/9pfs/virtio-9p-coth.c | 2 +-
hw/9pfs/virtio-9p-coth.h | 2 +-
hw/9pfs/virtio-9p.h | 2 +-
include/block/block.h | 2 +-
include/block/block_int.h | 2 +-
include/io/buffer.h | 118 +++
include/io/channel-buffer.h | 59 ++
include/io/channel-command.h | 91 ++
include/io/channel-file.h | 93 ++
include/io/channel-socket.h | 244 ++++++
include/io/channel-tls.h | 142 +++
include/io/channel-watch.h | 72 ++
include/io/channel-websock.h | 108 +++
include/io/channel.h | 523 ++++++++++++
include/io/task.h | 256 ++++++
include/{block => qemu}/coroutine.h | 0
include/{block => qemu}/coroutine_int.h | 2 +-
include/qemu/osdep.h | 16 +
include/qemu/sockets.h | 34 +
io/Makefile.objs | 10 +
io/buffer.c | 65 ++
io/channel-buffer.c | 255 ++++++
io/channel-command.c | 369 ++++++++
io/channel-file.c | 237 ++++++
io/channel-socket.c | 763 +++++++++++++++++
io/channel-tls.c | 419 +++++++++
io/channel-watch.c | 200 +++++
io/channel-websock.c | 948 +++++++++++++++++++++
io/channel.c | 290 +++++++
io/task.c | 159 ++++
migration/qemu-file-buf.c | 2 +-
migration/qemu-file-stdio.c | 2 +-
migration/qemu-file-unix.c | 2 +-
migration/qemu-file.c | 2 +-
migration/rdma.c | 2 +-
nbd.c | 2 +-
qemu-char.c | 25 -
scripts/create_config | 9 +
tests/.gitignore | 7 +
tests/Makefile | 16 +
tests/io-channel-helpers.c | 247 ++++++
tests/io-channel-helpers.h | 33 +
tests/test-coroutine.c | 4 +-
tests/test-io-channel-command.c | 122 +++
tests/test-io-channel-file.c | 91 ++
tests/test-io-channel-socket.c | 367 ++++++++
tests/test-io-channel-tls.c | 335 ++++++++
tests/test-io-task.c | 276 ++++++
tests/test-vmstate.c | 2 +-
thread-pool.c | 2 +-
trace-events | 56 ++
ui/vnc-auth-sasl.c | 4 +-
ui/vnc-enc-tight.c | 38 +-
ui/vnc-enc-zlib.c | 6 +-
ui/vnc-enc-zrle.c | 18 +-
ui/vnc-jobs.c | 15 +-
ui/vnc-ws.c | 36 +-
ui/vnc-ws.h | 6 +-
ui/vnc.c | 228 +++--
ui/vnc.h | 50 +-
util/Makefile.objs | 3 +
coroutine-gthread.c => util/coroutine-gthread.c | 2 +-
.../coroutine-sigaltstack.c | 2 +-
coroutine-ucontext.c => util/coroutine-ucontext.c | 2 +-
coroutine-win32.c => util/coroutine-win32.c | 2 +-
util/oslib-posix.c | 71 ++
util/oslib-win32.c | 9 +
qemu-coroutine-io.c => util/qemu-coroutine-io.c | 2 +-
.../qemu-coroutine-lock.c | 4 +-
.../qemu-coroutine-sleep.c | 2 +-
qemu-coroutine.c => util/qemu-coroutine.c | 4 +-
util/qemu-sockets.c | 158 +++-
86 files changed, 7511 insertions(+), 287 deletions(-)
create mode 100644 include/io/buffer.h
create mode 100644 include/io/channel-buffer.h
create mode 100644 include/io/channel-command.h
create mode 100644 include/io/channel-file.h
create mode 100644 include/io/channel-socket.h
create mode 100644 include/io/channel-tls.h
create mode 100644 include/io/channel-watch.h
create mode 100644 include/io/channel-websock.h
create mode 100644 include/io/channel.h
create mode 100644 include/io/task.h
rename include/{block => qemu}/coroutine.h (100%)
rename include/{block => qemu}/coroutine_int.h (98%)
create mode 100644 io/Makefile.objs
create mode 100644 io/buffer.c
create mode 100644 io/channel-buffer.c
create mode 100644 io/channel-command.c
create mode 100644 io/channel-file.c
create mode 100644 io/channel-socket.c
create mode 100644 io/channel-tls.c
create mode 100644 io/channel-watch.c
create mode 100644 io/channel-websock.c
create mode 100644 io/channel.c
create mode 100644 io/task.c
create mode 100644 tests/io-channel-helpers.c
create mode 100644 tests/io-channel-helpers.h
create mode 100644 tests/test-io-channel-command.c
create mode 100644 tests/test-io-channel-file.c
create mode 100644 tests/test-io-channel-socket.c
create mode 100644 tests/test-io-channel-tls.c
create mode 100644 tests/test-io-task.c
rename coroutine-gthread.c => util/coroutine-gthread.c (99%)
rename coroutine-sigaltstack.c => util/coroutine-sigaltstack.c (99%)
rename coroutine-ucontext.c => util/coroutine-ucontext.c (99%)
rename coroutine-win32.c => util/coroutine-win32.c (98%)
rename qemu-coroutine-io.c => util/qemu-coroutine-io.c (99%)
rename qemu-coroutine-lock.c => util/qemu-coroutine-lock.c (98%)
rename qemu-coroutine-sleep.c => util/qemu-coroutine-sleep.c (96%)
rename qemu-coroutine.c => util/qemu-coroutine.c (98%)Signature · 1 line
2.4.3
PATCH 1/16 sockets: add helpers for creating SocketAddress from a socket
Add two helper methods that, given a socket file descriptor, can return a populated SocketAddress struct containing either the local or remote address information.
--- include/qemu/sockets.h | 30 ++++++++++++++ util/qemu-sockets.c | 110 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+)
include/qemu/sockets.h +30 -0
@@ -88,4 +88,34 @@ int socket_dgram(SocketAddress *remote, SocketAddress *local, Error **errp);int parse_host_port (struct sockaddr_in *saddr,const char *str);int socket_init (void ); +/** +* socket_local_address: +* @fd: the socket file handle +* @errp: pointer to uninitialized error object +* +* Get the string representation of the local socket +* address. A pointer to the allocated address information +* struct will be returned, which the caller is required to +* release with a call qapi_free_SocketAddress when no +* longer required. +* +* Returns: the socket address struct, or NULL on error +*/ +SocketAddress *socket_local_address (int fd,Error **errp); + +/** +* socket_remote_address: +* @fd: the socket file handle +* @errp: pointer to uninitialized error object +* +* Get the string representation of the remote socket +* address. A pointer to the allocated address information +* struct will be returned, which the caller is required to +* release with a call qapi_free_SocketAddress when no +* longer required. +* +* Returns: the socket address struct, or NULL on error +*/ +SocketAddress *socket_remote_address (int fd,Error **errp); +#endif /* QEMU_SOCKET_H */
util/qemu-sockets.c +110 -0
113 lines not on this page · raw message
Signature · 1 line
2.4.3
PATCH 2/16 sockets: move qapi_copy_SocketAddress into qemu-sockets.c
In reply to Daniel P. BerrangeThe qapi_copy_SocketAddress method is going to be useful in more places than just qemu-char.c, so move it into the qemu-sockets.c file to allow its reuse.
--- include/qemu/sockets.h | 4 ++++ qemu-char.c | 25 ------------------------- util/qemu-sockets.c | 30 ++++++++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 25 deletions(-)
include/qemu/sockets.h +4 -0
@@ -118,4 +118,8 @@ SocketAddress *socket_local_address(int fd, Error **errp);*/ SocketAddress *socket_remote_address (int fd,Error **errp); + +void qapi_copy_SocketAddress (SocketAddress **p_dest, +SocketAddress *src); +#endif /* QEMU_SOCKET_H */
qemu-char.c +0 -25
@@ -92,31 +92,6 @@/***********************************************************/ /* Socket address helpers */ -static void qapi_copy_SocketAddress (SocketAddress **p_dest, -SocketAddress *src) -{ -QmpOutputVisitor *qov; -QmpInputVisitor *qiv; -Visitor *ov, *iv; -QObject *obj; - - *p_dest =NULL ; - - qov =qmp_output_visitor_new (); - ov =qmp_output_get_visitor (qov); -visit_type_SocketAddress (ov, &src,NULL , &error_abort); - obj =qmp_output_get_qobject (qov); -qmp_output_visitor_cleanup (qov); -if (!obj) { -return ; - } - - qiv =qmp_input_visitor_new (obj); - iv =qmp_input_get_visitor (qiv); -visit_type_SocketAddress (iv, p_dest,NULL , &error_abort); -qmp_input_visitor_cleanup (qiv); -qobject_decref (obj); -} static int SocketAddress_to_str(char *dest, int max_len, const char *prefix, SocketAddress *addr,
util/qemu-sockets.c +30 -0
@@ -25,6 +25,9 @@#include "monitor/monitor.h" #include "qemu/sockets.h" #include "qemu/main-loop.h" +#include "qapi/qmp-input-visitor.h" +#include "qapi/qmp-output-visitor.h" +#include "qapi-visit.h" #ifndef AI_ADDRCONFIG # define AI_ADDRCONFIG 0
@@ -1125,3 +1128,30 @@ SocketAddress *socket_remote_address(int fd, Error **errp)return socket_sockaddr_to_address (&ss, sslen, errp); } + + +void qapi_copy_SocketAddress (SocketAddress **p_dest, +SocketAddress *src) +{ +QmpOutputVisitor *qov; +QmpInputVisitor *qiv; +Visitor *ov, *iv; +QObject *obj; + + *p_dest =NULL ; + + qov =qmp_output_visitor_new (); + ov =qmp_output_get_visitor (qov); +visit_type_SocketAddress (ov, &src,NULL , &error_abort); + obj =qmp_output_get_qobject (qov); +qmp_output_visitor_cleanup (qov); +if (!obj) { +return ; + } + + qiv =qmp_input_visitor_new (obj); + iv =qmp_input_get_visitor (qiv); +visit_type_SocketAddress (iv, p_dest,NULL , &error_abort); +qmp_input_visitor_cleanup (qiv); +qobject_decref (obj); +}
Signature · 1 line
2.4.3
PATCH 3/16 sockets: allow port to be NULL when listening on IP address
In reply to Daniel P. BerrangeIf the port in the SocketAddress struct is NULL, it can allow the kernel to automatically select a free port. This is useful in particular in unit tests to avoid a race trying to find a free port to run a test case on.
--- util/qemu-sockets.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-)
util/qemu-sockets.c +13 -5
@@ -128,12 +128,15 @@ int inet_listen_opts(QemuOpts *opts, int port_offset, Error **errp) ai.ai_family =PF_UNSPEC ; ai.ai_socktype =SOCK_STREAM ; -if ((qemu_opt_get (opts,"host" ) ==NULL ) || - (qemu_opt_get (opts,"port" ) ==NULL )) { -error_setg (errp,"host and/or port not specified" ); +if ((qemu_opt_get (opts,"host" ) ==NULL )) { +error_setg (errp,"host not specified" );return -1 ; } -pstrcpy (port,sizeof (port),qemu_opt_get (opts,"port" )); +if (qemu_opt_get (opts,"port" ) !=NULL ) { +pstrcpy (port,sizeof (port),qemu_opt_get (opts,"port" )); + }else { + port[0 ] ='\0' ; + } addr =qemu_opt_get (opts,"host" ); to =qemu_opt_get_number (opts,"to" ,0 );
@@ -145,6 +148,10 @@ int inet_listen_opts(QemuOpts *opts, int port_offset, Error **errp)/* lookup */ if (port_offset) {unsigned long long baseport; +if (strlen (port) ==0 ) { +error_setg (errp,"port not specified" ); +return -1 ; + }if (parse_uint_full (port, &baseport,10 ) <0 ) {error_setg (errp,"can't convert to a number: %s" , port);return -1 ;
@@ -156,7 +163,8 @@ int inet_listen_opts(QemuOpts *opts, int port_offset, Error **errp) }snprintf (port,sizeof (port),"%d" , (int )baseport + port_offset); } - rc =getaddrinfo (strlen (addr) ? addr :NULL , port, &ai, &res); + rc =getaddrinfo (strlen (addr) ? addr :NULL , +strlen (port) ? port :NULL , &ai, &res);if (rc !=0 ) {error_setg (errp,"address resolution failed for %s:%s: %s" , addr, port,gai_strerror (rc));
Signature · 1 line
2.4.3
PATCH 4/16 ui: convert VNC startup code to use SocketAddress
In reply to Daniel P. BerrangeThe VNC code is currently using QemuOpts to configure the sockets connections / listeners it needs. Convert it to use SocketAddress to bring it in line with modern QAPI based code elsewhere in QEMU.
--- ui/vnc.c | 161 ++++++++++++++++++++++++++++++++++++--------------------------- 1 file changed, 91 insertions(+), 70 deletions(-)
ui/vnc.c +91 -70
@@ -3506,18 +3506,14 @@ void vnc_display_open(const char *id, Error **errp) {VncDisplay *vs =vnc_display_find (id);QemuOpts *opts =qemu_opts_find (&qemu_vnc_opts, id); -QemuOpts *sopts, *wsopts; +SocketAddress *saddr =NULL , *wsaddr =NULL ;const char *share, *device_id;QemuConsole *con;bool password = false;bool reverse = false;const char *vnc; -const char *has_to;char *h; -bool has_ipv4 = false; -bool has_ipv6 = false;const char *credid; -const char *websocket;bool sasl = false;#ifdef CONFIG_VNC_SASL int saslErr;
@@ -3539,44 +3535,83 @@ void vnc_display_open(const char *id, Error **errp)return ; } - sopts =qemu_opts_create (&socket_optslist,NULL ,0 , &error_abort); - wsopts =qemu_opts_create (&socket_optslist,NULL ,0 , &error_abort); - h =strrchr (vnc,':' );if (h) { -char *host;size_t hlen = h - vnc; -if (vnc[0 ] =='[' && vnc[hlen -1 ] ==']' ) { - host =g_strndup (vnc +1 , hlen -2 ); +const char *websocket =qemu_opt_get (opts,"websocket" ); +int to =qemu_opt_get_number (opts,"to" ,0 ); +bool has_ipv4 =qemu_opt_get_bool (opts,"ipv4" , false); +bool has_ipv6 =qemu_opt_get_bool (opts,"ipv6" , false); + + saddr =g_new0 (SocketAddress,1 ); +if (websocket) { +if (!qcrypto_hash_supports (QCRYPTO_HASH_ALG_SHA1 )) { +error_setg (errp, +"SHA1 hash support is required for websockets" ); + goto fail; + } + + wsaddr =g_new0 (SocketAddress,1 ); + vs->ws_enabled = true; + } + +if (strncmp (vnc,"unix:" ,5 ) ==0 ) { + saddr->kind =SOCKET_ADDRESS_KIND_UNIX ; + saddr->q_unix =g_new0 (UnixSocketAddress,1 ); + saddr->q_unix->path =g_strdup (vnc +5 ); + +if (vs->ws_enabled) { +error_setg (errp,"UNIX sockets not supported with websock" ); + goto fail; + } }else { - host =g_strndup (vnc, hlen); +unsigned long long baseport; + saddr->kind =SOCKET_ADDRESS_KIND_INET ; + saddr->inet =g_new0 (InetSocketAddress,1 ); +if (vnc[0 ] =='[' && vnc[hlen -1 ] ==']' ) { + saddr->inet->host =g_strndup (vnc +1 , hlen -2 ); + }else { + saddr->inet->host =g_strndup (vnc, hlen); + } +if (parse_uint_full (h +1 , &baseport,10 ) <0 ) { +error_setg (errp,"can't convert to a number: %s" , h +1 ); + goto fail; + } +if (baseport >65535 || + baseport +5900 >65535 ) { +error_setg (errp,"port %s out of range" , h +1 ); + goto fail; + } + saddr->inet->port =g_strdup_printf ( +"%d" , (int )baseport +5900 ); + +if (to) { + saddr->inet->has_to = true; + saddr->inet->to = to; + } + saddr->inet->ipv4 = saddr->inet->has_ipv4 = has_ipv4; + saddr->inet->ipv6 = saddr->inet->has_ipv6 = has_ipv6; + +if (vs->ws_enabled) { + wsaddr->kind =SOCKET_ADDRESS_KIND_INET ; + wsaddr->inet =g_new0 (InetSocketAddress,1 ); + wsaddr->inet->host =g_strdup (saddr->inet->host); + wsaddr->inet->port =g_strdup (websocket); + +if (to) { + wsaddr->inet->has_to = true; + wsaddr->inet->to = to; + } + wsaddr->inet->ipv4 = wsaddr->inet->has_ipv4 = has_ipv4; + wsaddr->inet->ipv6 = wsaddr->inet->has_ipv6 = has_ipv6; + } } -qemu_opt_set (sopts,"host" , host, &error_abort); -qemu_opt_set (wsopts,"host" , host, &error_abort); -qemu_opt_set (sopts,"port" , h+1 , &error_abort); -g_free (host); }else {error_setg (errp,"no vnc port specified" ); goto fail; } - has_to =qemu_opt_get (opts,"to" ); - has_ipv4 =qemu_opt_get_bool (opts,"ipv4" , false); - has_ipv6 =qemu_opt_get_bool (opts,"ipv6" , false); -if (has_to) { -qemu_opt_set (sopts,"to" , has_to, &error_abort); -qemu_opt_set (wsopts,"to" , has_to, &error_abort); - } -if (has_ipv4) { -qemu_opt_set (sopts,"ipv4" ,"on" , &error_abort); -qemu_opt_set (wsopts,"ipv4" ,"on" , &error_abort); - } -if (has_ipv6) { -qemu_opt_set (sopts,"ipv6" ,"on" , &error_abort); -qemu_opt_set (wsopts,"ipv6" ,"on" , &error_abort); - } - password =qemu_opt_get_bool (opts,"password" , false);if (password) {if (fips_get_state ()) {
@@ -3682,16 +3717,6 @@ void vnc_display_open(const char *id, Error **errp) } vs->connections_limit =qemu_opt_get_number (opts,"connections" ,32 ); - websocket =qemu_opt_get (opts,"websocket" ); -if (websocket) { - vs->ws_enabled = true; -qemu_opt_set (wsopts,"port" , websocket, &error_abort); -if (!qcrypto_hash_supports (QCRYPTO_HASH_ALG_SHA1 )) { -error_setg (errp,"SHA1 hash support is required for websockets" ); - goto fail; - } - } -#ifdef CONFIG_VNC_JPEG vs->lossy =qemu_opt_get_bool (opts,"lossy" , false);#endif
@@ -3725,7 +3750,7 @@ void vnc_display_open(const char *id, Error **errp) }#endif -if (vnc_display_setup_auth (vs, password, sasl, websocket, errp) <0 ) { +if (vnc_display_setup_auth (vs, password, sasl, vs->ws_enabled, errp) <0 ) { goto fail; }
@@ -3770,37 +3795,32 @@ void vnc_display_open(const char *id, Error **errp)int csock; vs->lsock =-1 ; vs->lwebsock =-1 ; -if (strncmp (vnc,"unix:" ,5 ) ==0 ) { - csock =unix_connect (vnc+5 , errp); - }else { - csock =inet_connect (vnc, errp); +if (vs->ws_enabled) { +error_setg (errp,"Cannot use websockets in reverse mode" ); + goto fail; } + csock =socket_connect (saddr, errp, +NULL ,NULL );if (csock <0 ) { goto fail; } + vs->is_unix = saddr->kind ==SOCKET_ADDRESS_KIND_UNIX ;vnc_connect (vs, csock, false, false); }else {/* listen for connects */ -if (strncmp (vnc,"unix:" ,5 ) ==0 ) { - vs->lsock =unix_listen (vnc+5 ,NULL ,0 , errp); -if (vs->lsock <0 ) { - goto fail; - } - vs->is_unix = true; - }else { - vs->lsock =inet_listen_opts (sopts,5900 , errp); -if (vs->lsock <0 ) { - goto fail; - } -if (vs->ws_enabled) { - vs->lwebsock =inet_listen_opts (wsopts,0 , errp); -if (vs->lwebsock <0 ) { -if (vs->lsock !=-1 ) { -close (vs->lsock); - vs->lsock =-1 ; - } - goto fail; + vs->lsock =socket_listen (saddr, errp); +if (vs->lsock <0 ) { + goto fail; + } + vs->is_unix = saddr->kind ==SOCKET_ADDRESS_KIND_UNIX ; +if (vs->ws_enabled) { + vs->lwebsock =socket_listen (wsaddr, errp); +if (vs->lwebsock <0 ) { +if (vs->lsock !=-1 ) { +close (vs->lsock); + vs->lsock =-1 ; } + goto fail; } } vs->enabled = true;
@@ -3810,13 +3830,14 @@ void vnc_display_open(const char *id, Error **errp) NULL, vs); } } -qemu_opts_del (sopts); -qemu_opts_del (wsopts); + +qapi_free_SocketAddress (saddr); +qapi_free_SocketAddress (wsaddr);return ; fail: -qemu_opts_del (sopts); -qemu_opts_del (wsopts); +qapi_free_SocketAddress (saddr); +qapi_free_SocketAddress (wsaddr); vs->enabled = false; vs->ws_enabled = false; }
Signature · 1 line
2.4.3
PATCH 5/16 osdep: add qemu_fork() wrapper for safely handling signals
In reply to Daniel P. BerrangeWhen using regular fork() the child process of course inherits all the parents' signal handlers. If the child then proceeds to close() any open file descriptors, it may break some of those registered signal handlers. The child generally does not want to ever run any of the signal handlers tha parent may have installed in the short time before it exec's. The parent may also have blocked various signals which the child process will want enabled.
This introduces a wrapper qemu_fork() that takes care to sanitize signal handling across fork. Before forking it blocks all signals in the parent thread. After fork returns, the parent unblocks the signals and carries on as usual. The child, however, resets all the signal handlers back to their defaults before it unblocks signals. The child process can now exec the binary in a "clean" signal environment.
--- include/qemu/osdep.h | 16 ++++++++++++ util/oslib-posix.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++ util/oslib-win32.c | 9 +++++++ 3 files changed, 96 insertions(+)
include/qemu/osdep.h +16 -0
@@ -67,6 +67,8 @@#include "sysemu/os-posix.h" #endif +#include "qapi/error.h" +#if defined(CONFIG_SOLARIS ) &&CONFIG_SOLARIS_VERSION <10 /* [u]int_fast*_t not in <sys/int_types.h> */ typedef unsigned char uint_fast8_t ;
@@ -284,4 +286,18 @@ void os_mem_prealloc(int fd, char *area, size_t sz);int qemu_read_password (char *buf,int buf_size); +/** +* qemu_fork: +* +* A version of fork that avoids signal handler race +* conditions that can lead to child process getting +* signals that are otherwise only expected by the +* parent. It also resets all signal handlers to the +* default settings. +* +* Returns 0 to child process, pid number to parent +* or -1 on failure. +*/ +pid_t qemu_fork (Error **errp); +#endif
util/oslib-posix.c +71 -0
74 lines not on this page · raw message
util/oslib-win32.c +9 -0
@@ -494,3 +494,12 @@ int qemu_read_password(char *buf, int buf_size) buf[i] ='\0' ;return 0 ; } + + +pid_t qemu_fork (Error **errp) +{ + errno =ENOSYS ; +error_setg_errno (errp, errno, +"cannot fork child process" ); +return -1 ; +}
Signature · 1 line
2.4.3
PATCH 6/16 coroutine: move into libqemuutil.a library
In reply to Daniel P. BerrangeThe coroutine files are currently referenced by the block-obj-y variable. The coroutine functionality though is already used by more than just the block code. eg migration code uses coroutine yield. In the future the I/O channel code will also use the coroutine yield functionality. Since the coroutine code is nicely self-contained it can be easily built as part of the libqemuutil.a library, making it widely available.
The headers are also moved into include/qemu, instead of the include/block directory, since they are now part of the util codebase, and the impl was never in the block/ directory either.
---
Makefile.objs | 4 ----
block.c | 2 +-
block/qcow2.h | 2 +-
block/vdi.c | 2 +-
block/write-threshold.c | 2 +-
blockjob.c | 2 +-
hw/9pfs/codir.c | 2 +-
hw/9pfs/cofile.c | 2 +-
hw/9pfs/cofs.c | 2 +-
hw/9pfs/coxattr.c | 2 +-
hw/9pfs/virtio-9p-coth.c | 2 +-
hw/9pfs/virtio-9p-coth.h | 2 +-
hw/9pfs/virtio-9p.h | 2 +-
include/block/block.h | 2 +-
include/block/block_int.h | 2 +-
include/{block => qemu}/coroutine.h | 0
include/{block => qemu}/coroutine_int.h | 2 +-
migration/qemu-file-buf.c | 2 +-
migration/qemu-file-stdio.c | 2 +-
migration/qemu-file-unix.c | 2 +-
migration/qemu-file.c | 2 +-
migration/rdma.c | 2 +-
nbd.c | 2 +-
tests/test-coroutine.c | 4 ++--
tests/test-vmstate.c | 2 +-
thread-pool.c | 2 +-
util/Makefile.objs | 3 +++
coroutine-gthread.c => util/coroutine-gthread.c | 2 +-
coroutine-sigaltstack.c => util/coroutine-sigaltstack.c | 2 +-
coroutine-ucontext.c => util/coroutine-ucontext.c | 2 +-
coroutine-win32.c => util/coroutine-win32.c | 2 +-
qemu-coroutine-io.c => util/qemu-coroutine-io.c | 2 +-
qemu-coroutine-lock.c => util/qemu-coroutine-lock.c | 4 ++--
qemu-coroutine-sleep.c => util/qemu-coroutine-sleep.c | 2 +-
qemu-coroutine.c => util/qemu-coroutine.c | 4 ++--
35 files changed, 38 insertions(+), 39 deletions(-)
rename include/{block => qemu}/coroutine.h (100%)
rename include/{block => qemu}/coroutine_int.h (98%)
rename coroutine-gthread.c => util/coroutine-gthread.c (99%)
rename coroutine-sigaltstack.c => util/coroutine-sigaltstack.c (99%)
rename coroutine-ucontext.c => util/coroutine-ucontext.c (99%)
rename coroutine-win32.c => util/coroutine-win32.c (98%)
rename qemu-coroutine-io.c => util/qemu-coroutine-io.c (99%)
rename qemu-coroutine-lock.c => util/qemu-coroutine-lock.c (98%)
rename qemu-coroutine-sleep.c => util/qemu-coroutine-sleep.c (96%)
rename qemu-coroutine.c => util/qemu-coroutine.c (98%)Makefile.objs +0 -4
@@ -14,10 +14,6 @@ block-obj-$(CONFIG_WIN32) += aio-win32.o block-obj-y += block/ block-obj-y += qemu-io-cmds.o -block-obj-y += qemu-coroutine.o qemu-coroutine-lock.o qemu-coroutine-io.o -block-obj-y += qemu-coroutine-sleep.o -block-obj-y += coroutine-$(CONFIG_COROUTINE_BACKEND).o - block-obj-m = block/ #######################################################################
block.c +1 -1
@@ -33,7 +33,7 @@#include "sysemu/block-backend.h" #include "sysemu/sysemu.h" #include "qemu/notify.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" #include "block/qapi.h" #include "qmp-commands.h" #include "qemu/timer.h"
block/qcow2.h +1 -1
@@ -26,7 +26,7 @@#define BLOCK_QCOW2_H #include "crypto/cipher.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" //#define DEBUG_ALLOC //#define DEBUG_ALLOC2
block/vdi.c +1 -1
@@ -53,7 +53,7 @@#include "block/block_int.h" #include "qemu/module.h" #include "migration/migration.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" #if defined(CONFIG_UUID )#include <uuid/uuid.h>
block/write-threshold.c +1 -1
@@ -11,7 +11,7 @@*/ #include "block/block_int.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" #include "block/write-threshold.h" #include "qemu/notify.h" #include "qapi-event.h"
blockjob.c +1 -1
@@ -31,7 +31,7 @@#include "block/block_int.h" #include "qapi/qmp/qerror.h" #include "qapi/qmp/qjson.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" #include "qmp-commands.h" #include "qemu/timer.h" #include "qapi-event.h"
hw/9pfs/codir.c +1 -1
@@ -14,7 +14,7 @@#include "fsdev/qemu-fsdev.h" #include "qemu/thread.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" #include "virtio-9p-coth.h" int v9fs_co_readdir_r(V9fsPDU *pdu, V9fsFidState *fidp, struct dirent *dent,
hw/9pfs/cofile.c +1 -1
@@ -14,7 +14,7 @@#include "fsdev/qemu-fsdev.h" #include "qemu/thread.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" #include "virtio-9p-coth.h" int v9fs_co_st_gen(V9fsPDU *pdu, V9fsPath *path, mode_t st_mode,
hw/9pfs/cofs.c +1 -1
@@ -14,7 +14,7 @@#include "fsdev/qemu-fsdev.h" #include "qemu/thread.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" #include "virtio-9p-coth.h" static ssize_t __readlink (V9fsState *s,V9fsPath *path,V9fsString *buf)
hw/9pfs/coxattr.c +1 -1
@@ -14,7 +14,7 @@#include "fsdev/qemu-fsdev.h" #include "qemu/thread.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" #include "virtio-9p-coth.h" int v9fs_co_llistxattr (V9fsPDU *pdu,V9fsPath *path,void *value,size_t size)
hw/9pfs/virtio-9p-coth.c +1 -1
@@ -15,7 +15,7 @@#include "fsdev/qemu-fsdev.h" #include "qemu/thread.h" #include "qemu/event_notifier.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" #include "virtio-9p-coth.h" /* v9fs glib thread pool */
hw/9pfs/virtio-9p-coth.h +1 -1
@@ -16,7 +16,7 @@#define _QEMU_VIRTIO_9P_COTH_H#include "qemu/thread.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" #include "virtio-9p.h" #include <glib.h>
hw/9pfs/virtio-9p.h +1 -1
@@ -13,7 +13,7 @@#include "fsdev/file-op-9p.h" #include "fsdev/virtio-9p-marshal.h" #include "qemu/thread.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" enum {P9_TLERROR =6 ,
include/block/block.h +1 -1
@@ -4,7 +4,7 @@#include "block/aio.h" #include "qemu-common.h" #include "qemu/option.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" #include "block/accounting.h" #include "qapi/qmp/qobject.h" #include "qapi-types.h"
include/block/block_int.h +1 -1
@@ -28,7 +28,7 @@#include "block/block.h" #include "qemu/option.h" #include "qemu/queue.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" #include "qemu/timer.h" #include "qapi-types.h" #include "qemu/hbitmap.h"
include/qemu/coroutine.h renamed from include/block/coroutine.h +0 -0
include/qemu/coroutine_int.h renamed from include/block/coroutine_int.h +1 -1
@@ -26,7 +26,7 @@#define QEMU_COROUTINE_INT_H #include "qemu/queue.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" typedef enum { COROUTINE_YIELD = 1,
migration/qemu-file-buf.c +1 -1
@@ -29,7 +29,7 @@#include "qemu/error-report.h" #include "qemu/iov.h" #include "qemu/sockets.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" #include "migration/migration.h" #include "migration/qemu-file.h" #include "migration/qemu-file-internal.h"
migration/qemu-file-stdio.c +1 -1
@@ -22,7 +22,7 @@* THE SOFTWARE. */ #include "qemu-common.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" #include "migration/qemu-file.h" typedef struct QEMUFileStdio {
migration/qemu-file-unix.c +1 -1
@@ -24,7 +24,7 @@#include "qemu-common.h" #include "qemu/iov.h" #include "qemu/sockets.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" #include "migration/qemu-file.h" #include "migration/qemu-file-internal.h"
migration/qemu-file.c +1 -1
@@ -26,7 +26,7 @@#include "qemu/error-report.h" #include "qemu/iov.h" #include "qemu/sockets.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" #include "migration/migration.h" #include "migration/qemu-file.h" #include "migration/qemu-file-internal.h"
migration/rdma.c +1 -1
@@ -19,7 +19,7 @@#include "qemu/main-loop.h" #include "qemu/sockets.h" #include "qemu/bitmap.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" #include <stdio.h> #include <sys/types.h> #include <sys/socket.h>
nbd.c +1 -1
@@ -19,7 +19,7 @@#include "block/nbd.h" #include "sysemu/block-backend.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" #include <errno.h> #include <string.h>
tests/test-coroutine.c +2 -2
@@ -12,8 +12,8 @@*/ #include <glib.h> -#include "block/coroutine.h" -#include "block/coroutine_int.h" +#include "qemu/coroutine.h" +#include "qemu/coroutine_int.h" /* * Check that qemu_in_coroutine() works
tests/test-vmstate.c +1 -1
@@ -27,7 +27,7 @@#include "qemu-common.h" #include "migration/migration.h" #include "migration/vmstate.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" static char temp_file[] ="/tmp/vmst.test.XXXXXX" ;static int temp_fd;
thread-pool.c +1 -1
@@ -18,7 +18,7 @@#include "qemu/queue.h" #include "qemu/thread.h" #include "qemu/osdep.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" #include "trace.h" #include "block/thread-pool.h" #include "qemu/main-loop.h"
util/Makefile.objs +3 -0
@@ -18,3 +18,6 @@ util-obj-y += getauxval.o util-obj-y += readline.o util-obj-y += rfifolock.o util-obj-y += rcu.o +util-obj-y += qemu-coroutine.o qemu-coroutine-lock.o qemu-coroutine-io.o +util-obj-y += qemu-coroutine-sleep.o +util-obj-y += coroutine-$(CONFIG_COROUTINE_BACKEND).o
util/coroutine-gthread.c renamed from coroutine-gthread.c +1 -1
@@ -20,7 +20,7 @@#include <glib.h> #include "qemu-common.h" -#include "block/coroutine_int.h" +#include "qemu/coroutine_int.h" typedef struct { Coroutine base;
util/coroutine-sigaltstack.c renamed from coroutine-sigaltstack.c +1 -1
@@ -31,7 +31,7 @@#include <pthread.h> #include <signal.h> #include "qemu-common.h" -#include "block/coroutine_int.h" +#include "qemu/coroutine_int.h" typedef struct { Coroutine base;
util/coroutine-ucontext.c renamed from coroutine-ucontext.c +1 -1
@@ -27,7 +27,7 @@#include <stdint.h> #include <ucontext.h> #include "qemu-common.h" -#include "block/coroutine_int.h" +#include "qemu/coroutine_int.h" #ifdef CONFIG_VALGRIND_H #include <valgrind/valgrind.h>
util/coroutine-win32.c renamed from coroutine-win32.c +1 -1
@@ -23,7 +23,7 @@*/ #include "qemu-common.h" -#include "block/coroutine_int.h" +#include "qemu/coroutine_int.h" typedef struct {
util/qemu-coroutine-io.c renamed from qemu-coroutine-io.c +1 -1
@@ -24,7 +24,7 @@*/ #include "qemu-common.h" #include "qemu/sockets.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" #include "qemu/iov.h" #include "qemu/main-loop.h"
util/qemu-coroutine-lock.c renamed from qemu-coroutine-lock.c +2 -2
@@ -23,8 +23,8 @@*/ #include "qemu-common.h" -#include "block/coroutine.h" -#include "block/coroutine_int.h" +#include "qemu/coroutine.h" +#include "qemu/coroutine_int.h" #include "qemu/queue.h" #include "trace.h"
util/qemu-coroutine-sleep.c renamed from qemu-coroutine-sleep.c +1 -1
@@ -11,7 +11,7 @@* */ -#include "block/coroutine.h" +#include "qemu/coroutine.h" #include "qemu/timer.h" #include "block/aio.h"
util/qemu-coroutine.c renamed from qemu-coroutine.c +2 -2
@@ -16,8 +16,8 @@#include "qemu-common.h" #include "qemu/thread.h" #include "qemu/atomic.h" -#include "block/coroutine.h" -#include "block/coroutine_int.h" +#include "qemu/coroutine.h" +#include "qemu/coroutine_int.h" enum {POOL_BATCH_SIZE =64 ,
Signature · 1 line
2.4.3
Re: [Qemu-devel] [PATCH v1 06/16] coroutine: move into libqemuutil.a library
Quoted 8 lines · Daniel P. Berrange
The coroutine files are currently referenced by the block-obj-y variable. The coroutine functionality though is already used by more than just the block code. eg migration code uses coroutine yield. In the future the I/O channel code will also use the coroutine yield functionality. Since the coroutine code is nicely self-contained it can be easily built as part of the libqemuutil.a library, making it widely available.
Quoted 6 lines
The headers are also moved into
include/qemu, instead of theinclude/blockdirectory, since they are now part of the util codebase, and the impl was never in theblock/directory either.Signed-off-by Daniel P. Berrange <[email protected]>
Since you are at it, can you add an entry to the MAINTAINERS file, assigning it to Stefan and Kevin?
Paolo
Quoted 528 lines
--- Makefile.objs | 4 ---- block.c | 2 +- block/qcow2.h | 2 +- block/vdi.c | 2 +- block/write-threshold.c | 2 +- blockjob.c | 2 +- hw/9pfs/codir.c | 2 +- hw/9pfs/cofile.c | 2 +- hw/9pfs/cofs.c | 2 +- hw/9pfs/coxattr.c | 2 +- hw/9pfs/virtio-9p-coth.c | 2 +- hw/9pfs/virtio-9p-coth.h | 2 +- hw/9pfs/virtio-9p.h | 2 +- include/block/block.h | 2 +- include/block/block_int.h | 2 +- include/{block => qemu}/coroutine.h | 0 include/{block => qemu}/coroutine_int.h | 2 +- migration/qemu-file-buf.c | 2 +- migration/qemu-file-stdio.c | 2 +- migration/qemu-file-unix.c | 2 +- migration/qemu-file.c | 2 +- migration/rdma.c | 2 +- nbd.c | 2 +- tests/test-coroutine.c | 4 ++-- tests/test-vmstate.c | 2 +- thread-pool.c | 2 +- util/Makefile.objs | 3 +++ coroutine-gthread.c => util/coroutine-gthread.c | 2 +- coroutine-sigaltstack.c => util/coroutine-sigaltstack.c | 2 +- coroutine-ucontext.c => util/coroutine-ucontext.c | 2 +- coroutine-win32.c => util/coroutine-win32.c | 2 +- qemu-coroutine-io.c => util/qemu-coroutine-io.c | 2 +- qemu-coroutine-lock.c => util/qemu-coroutine-lock.c | 4 ++-- qemu-coroutine-sleep.c => util/qemu-coroutine-sleep.c | 2 +- qemu-coroutine.c => util/qemu-coroutine.c | 4 ++-- 35 files changed, 38 insertions(+), 39 deletions(-) rename include/{block => qemu}/coroutine.h (100%) rename include/{block => qemu}/coroutine_int.h (98%) rename coroutine-gthread.c => util/coroutine-gthread.c (99%) rename coroutine-sigaltstack.c => util/coroutine-sigaltstack.c (99%) rename coroutine-ucontext.c => util/coroutine-ucontext.c (99%) rename coroutine-win32.c => util/coroutine-win32.c (98%) rename qemu-coroutine-io.c => util/qemu-coroutine-io.c (99%) rename qemu-coroutine-lock.c => util/qemu-coroutine-lock.c (98%) rename qemu-coroutine-sleep.c => util/qemu-coroutine-sleep.c (96%) rename qemu-coroutine.c => util/qemu-coroutine.c (98%)Makefile.objs +0
-4@@ -14,10 +14,6 @@ block-obj-$(CONFIG_WIN32) += aio-win32.o block-obj-y += block/ block-obj-y += qemu-io-cmds.o -block-obj-y += qemu-coroutine.o qemu-coroutine-lock.o qemu-coroutine-io.o -block-obj-y += qemu-coroutine-sleep.o -block-obj-y += coroutine-$(CONFIG_COROUTINE_BACKEND).o - block-obj-m = block/ #######################################################################block.c +1
-1@@ -33,7 +33,7 @@#include "sysemu/block-backend.h" #include "sysemu/sysemu.h" #include "qemu/notify.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" #include "block/qapi.h" #include "qmp-commands.h" #include "qemu/timer.h" block/qcow2.h +1
-1@@ -26,7 +26,7 @@#define BLOCK_QCOW2_H #include "crypto/cipher.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" //#define DEBUG_ALLOC //#define DEBUG_ALLOC2 block/vdi.c +1
-1@@ -53,7 +53,7 @@#include "block/block_int.h" #include "qemu/module.h" #include "migration/migration.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" #if defined(CONFIG_UUID )#include <uuid/uuid.h> block/write-threshold.c +1
-1@@ -11,7 +11,7 @@*/ #include "block/block_int.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" #include "block/write-threshold.h" #include "qemu/notify.h" #include "qapi-event.h" blockjob.c +1
-1@@ -31,7 +31,7 @@#include "block/block_int.h" #include "qapi/qmp/qerror.h" #include "qapi/qmp/qjson.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" #include "qmp-commands.h" #include "qemu/timer.h" #include "qapi-event.h" hw/9pfs/codir.c +1
-1@@ -14,7 +14,7 @@#include "fsdev/qemu-fsdev.h" #include "qemu/thread.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" #include "virtio-9p-coth.h" int v9fs_co_readdir_r(V9fsPDU *pdu, V9fsFidState *fidp, struct dirent *dent,hw/9pfs/cofile.c +1
-1@@ -14,7 +14,7 @@#include "fsdev/qemu-fsdev.h" #include "qemu/thread.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" #include "virtio-9p-coth.h" int v9fs_co_st_gen(V9fsPDU *pdu, V9fsPath *path, mode_t st_mode,hw/9pfs/cofs.c +1
-1@@ -14,7 +14,7 @@#include "fsdev/qemu-fsdev.h" #include "qemu/thread.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" #include "virtio-9p-coth.h" static ssize_t __readlink (V9fsState *s,V9fsPath *path,V9fsString *buf)hw/9pfs/coxattr.c +1
-1@@ -14,7 +14,7 @@#include "fsdev/qemu-fsdev.h" #include "qemu/thread.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" #include "virtio-9p-coth.h" int v9fs_co_llistxattr (V9fsPDU *pdu,V9fsPath *path,void *value,size_t size)hw/9pfs/virtio-9p-coth.c +1
-1@@ -15,7 +15,7 @@#include "fsdev/qemu-fsdev.h" #include "qemu/thread.h" #include "qemu/event_notifier.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" #include "virtio-9p-coth.h" /* v9fs glib thread pool */ hw/9pfs/virtio-9p-coth.h +1
-1@@ -16,7 +16,7 @@#define _QEMU_VIRTIO_9P_COTH_H#include "qemu/thread.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" #include "virtio-9p.h" #include <glib.h> hw/9pfs/virtio-9p.h +1
-1@@ -13,7 +13,7 @@#include "fsdev/file-op-9p.h" #include "fsdev/virtio-9p-marshal.h" #include "qemu/thread.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" enum {P9_TLERROR =6 ,include/block/block.h +1
-1@@ -4,7 +4,7 @@#include "block/aio.h" #include "qemu-common.h" #include "qemu/option.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" #include "block/accounting.h" #include "qapi/qmp/qobject.h" #include "qapi-types.h" include/block/block_int.h +1
-1@@ -28,7 +28,7 @@#include "block/block.h" #include "qemu/option.h" #include "qemu/queue.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" #include "qemu/timer.h" #include "qapi-types.h" #include "qemu/hbitmap.h" include/qemu/coroutine.h renamed from include/block/coroutine.h +0
-0include/qemu/coroutine_int.h renamed from include/block/coroutine_int.h +1
-1@@ -26,7 +26,7 @@#define QEMU_COROUTINE_INT_H #include "qemu/queue.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" typedef enum { COROUTINE_YIELD = 1,migration/qemu-file-buf.c +1
-1@@ -29,7 +29,7 @@#include "qemu/error-report.h" #include "qemu/iov.h" #include "qemu/sockets.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" #include "migration/migration.h" #include "migration/qemu-file.h" #include "migration/qemu-file-internal.h" migration/qemu-file-stdio.c +1
-1@@ -22,7 +22,7 @@* THE SOFTWARE. */ #include "qemu-common.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" #include "migration/qemu-file.h" typedef struct QEMUFileStdio {migration/qemu-file-unix.c +1
-1@@ -24,7 +24,7 @@#include "qemu-common.h" #include "qemu/iov.h" #include "qemu/sockets.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" #include "migration/qemu-file.h" #include "migration/qemu-file-internal.h" migration/qemu-file.c +1
-1@@ -26,7 +26,7 @@#include "qemu/error-report.h" #include "qemu/iov.h" #include "qemu/sockets.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" #include "migration/migration.h" #include "migration/qemu-file.h" #include "migration/qemu-file-internal.h" migration/rdma.c +1
-1@@ -19,7 +19,7 @@#include "qemu/main-loop.h" #include "qemu/sockets.h" #include "qemu/bitmap.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> nbd.c +1
-1@@ -19,7 +19,7 @@#include "block/nbd.h" #include "sysemu/block-backend.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" #include <errno.h> #include <string.h> tests/test-coroutine.c +2
-2@@ -12,8 +12,8 @@*/ #include <glib.h> -#include "block/coroutine.h" -#include "block/coroutine_int.h" +#include "qemu/coroutine.h" +#include "qemu/coroutine_int.h" /* * Check that qemu_in_coroutine() works tests/test-vmstate.c +1
-1@@ -27,7 +27,7 @@#include "qemu-common.h" #include "migration/migration.h" #include "migration/vmstate.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" static char temp_file[] ="/tmp/vmst.test.XXXXXX" ;static int temp_fd;thread-pool.c +1
-1@@ -18,7 +18,7 @@#include "qemu/queue.h" #include "qemu/thread.h" #include "qemu/osdep.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" #include "trace.h" #include "block/thread-pool.h" #include "qemu/main-loop.h" util/Makefile.objs +3
-0@@ -18,3 +18,6 @@ util-obj-y += getauxval.o util-obj-y += readline.o util-obj-y += rfifolock.o util-obj-y += rcu.o +util-obj-y += qemu-coroutine.o qemu-coroutine-lock.o qemu-coroutine-io.o +util-obj-y += qemu-coroutine-sleep.o +util-obj-y += coroutine-$(CONFIG_COROUTINE_BACKEND).outil/coroutine-gthread.c renamed from coroutine-gthread.c +1
-1@@ -20,7 +20,7 @@#include <glib.h> #include "qemu-common.h" -#include "block/coroutine_int.h" +#include "qemu/coroutine_int.h" typedef struct { Coroutine base;util/coroutine-sigaltstack.c renamed from coroutine-sigaltstack.c +1
-1@@ -31,7 +31,7 @@#include <pthread.h> #include <signal.h> #include "qemu-common.h" -#include "block/coroutine_int.h" +#include "qemu/coroutine_int.h" typedef struct { Coroutine base;util/coroutine-ucontext.c renamed from coroutine-ucontext.c +1
-1@@ -27,7 +27,7 @@#include <stdint.h> #include <ucontext.h> #include "qemu-common.h" -#include "block/coroutine_int.h" +#include "qemu/coroutine_int.h" #ifdef CONFIG_VALGRIND_H #include <valgrind/valgrind.h> util/coroutine-win32.c renamed from coroutine-win32.c +1
-1@@ -23,7 +23,7 @@*/ #include "qemu-common.h" -#include "block/coroutine_int.h" +#include "qemu/coroutine_int.h" typedef struct {util/qemu-coroutine-io.c renamed from qemu-coroutine-io.c +1
-1@@ -24,7 +24,7 @@*/ #include "qemu-common.h" #include "qemu/sockets.h" -#include "block/coroutine.h" +#include "qemu/coroutine.h" #include "qemu/iov.h" #include "qemu/main-loop.h" util/qemu-coroutine-lock.c renamed from qemu-coroutine-lock.c +2
-2@@ -23,8 +23,8 @@*/ #include "qemu-common.h" -#include "block/coroutine.h" -#include "block/coroutine_int.h" +#include "qemu/coroutine.h" +#include "qemu/coroutine_int.h" #include "qemu/queue.h" #include "trace.h" util/qemu-coroutine-sleep.c renamed from qemu-coroutine-sleep.c +1
-1@@ -11,7 +11,7 @@* */ -#include "block/coroutine.h" +#include "qemu/coroutine.h" #include "qemu/timer.h" #include "block/aio.h" util/qemu-coroutine.c renamed from qemu-coroutine.c +2
-2@@ -16,8 +16,8 @@#include "qemu-common.h" #include "qemu/thread.h" #include "qemu/atomic.h" -#include "block/coroutine.h" -#include "block/coroutine_int.h" +#include "qemu/coroutine.h" +#include "qemu/coroutine_int.h" enum {POOL_BATCH_SIZE =64 ,
Re: [Qemu-devel] [PATCH v1 06/16] coroutine: move into libqemuutil.a library
Quoted 16 lines · Paolo Bonzini
Quoted 13 lines · Daniel P. Berrange
The coroutine files are currently referenced by the block-obj-y variable. The coroutine functionality though is already used by more than just the block code. eg migration code uses coroutine yield. In the future the I/O channel code will also use the coroutine yield functionality. Since the coroutine code is nicely self-contained it can be easily built as part of the libqemuutil.a library, making it widely available.
The headers are also moved into
include/qemu, instead of theinclude/blockdirectory, since they are now part of the util codebase, and the impl was never in theblock/directory either.
Quoted 4 lines
Quoted 1 line
Signed-off-by Daniel P. Berrange <[email protected]>Since you are at it, can you add an entry to the
MAINTAINERSfile, assigning it to Stefan and Kevin?
Yep, will do.
Regards,
Daniel
Signature · 4 lines
|: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
PATCH 7/16 io: add abstract QIOChannel classes
In reply to Daniel P. BerrangeStart the new generic I/O channel framework by defining a QIOChannel abstract base class. This is designed to feel similar to GLib's GIOChannel, but with the addition of support for using iovecs, qemu error reporting, file descriptor passing, coroutine integration and use of the QOM framework for easier sub-classing.
The intention is that anywhere in QEMU that almost anywhere that deals with sockets will use this new I/O infrastructure, so that it becomes trivial to then layer in support for TLS encryption. This will at least include the VNC server, char device backend and migration code.
--- MAINTAINERS | 7 + Makefile | 2 + Makefile.objs | 5 + Makefile.target | 2 + include/io/channel.h | 523 +++++++++++++++++++++++++++++++++++++++++++++++++++ io/Makefile.objs | 1 + io/channel.c | 290 ++++++++++++++++++++++++++++ 7 files changed, 830 insertions(+) create mode 100644 include/io/channel.h create mode 100644 io/Makefile.objs create mode 100644 io/channel.c
MAINTAINERS +7 -0
@@ -1078,6 +1078,13 @@ F: crypto/ F: include/crypto/ F: tests/test-crypto-* +I/O Channels +M: Daniel P. Berrange <[email protected]> +S: Maintained +F: io/ +F: include/io/ +F: tests/test-io-* + Usermode Emulation ------------------ Overall
Makefile +2 -0
@@ -155,6 +155,7 @@ dummy := $(call unnest-vars,, \ crypto-obj-y \ crypto-aes-obj-y \ qom-obj-y \ + io-obj-y \ common-obj-y \ common-obj-m)
@@ -178,6 +179,7 @@ SOFTMMU_SUBDIR_RULES=$(filter %-softmmu,$(SUBDIR_RULES)) $(SOFTMMU_SUBDIR_RULES): $(block-obj-y) $(SOFTMMU_SUBDIR_RULES): $(crypto-obj-y) $(SOFTMMU_SUBDIR_RULES): $(qom-obj-y) +$(SOFTMMU_SUBDIR_RULES): $(io-obj-y) $(SOFTMMU_SUBDIR_RULES): config-all-devices.mak subdir-%:
Makefile.objs +5 -0
@@ -27,6 +27,11 @@ crypto-aes-obj-y = crypto/ qom-obj-y = qom/ +####################################################################### +# io-obj-y is code used by both qemu system emulation and qemu-img + +io-obj-y = io/ + ###################################################################### # smartcard
Makefile.target +2 -0
@@ -176,6 +176,7 @@ dummy := $(call unnest-vars,.., \ crypto-obj-y \ crypto-aes-obj-y \ qom-obj-y \ + io-obj-y \ common-obj-y \ common-obj-m) target-obj-y := $(target-obj-y-save)
@@ -185,6 +186,7 @@ all-obj-y += $(qom-obj-y) all-obj-$(CONFIG_SOFTMMU) += $(block-obj-y) all-obj-$(CONFIG_USER_ONLY) += $(crypto-aes-obj-y) all-obj-$(CONFIG_SOFTMMU) += $(crypto-obj-y) +all-obj-$(CONFIG_SOFTMMU) += $(io-obj-y) $(QEMU_PROG_BUILD): config-devices.mak
include/io/channel.h new file +523 -0
523 lines not on this page · raw message
io/Makefile.objs new file +1 -0
@@ -0,0 +1 @@ +io-obj-y = channel.o
io/channel.c new file +290 -0
290 lines not on this page · raw message
Signature · 1 line
2.4.3
Re: [Qemu-devel] [PATCH v1 07/16] io: add abstract QIOChannel classes
include/io/channel.h line 47
Quoted 4 lines · Daniel P. Berrange
include/io/channel.h new file +4
-0+QIO_CHANNEL_FEATURE_FD_PASS = (1 <<0 ), +QIO_CHANNEL_FEATURE_SHUTDOWN = (1 <<1 ), +QIO_CHANNEL_FEATURE_DELAY = (1 <<2 ), + QIO_CHANNEL_FEATURE_CORK = (1 << 3),
TCP_NODELAY and TCP_CORK are just hints; I think it is okay to just ignore them if not supported. You obviously disagree, so the question is why? :)
Paolo
Re: [Qemu-devel] [PATCH v1 07/16] io: add abstract QIOChannel classes
Quoted 11 lines · Paolo Bonzini
include/io/channel.h line 47
Quoted 4 lines · Daniel P. Berrange
include/io/channel.h new file +4
-0+QIO_CHANNEL_FEATURE_FD_PASS = (1 <<0 ), +QIO_CHANNEL_FEATURE_SHUTDOWN = (1 <<1 ), +QIO_CHANNEL_FEATURE_DELAY = (1 <<2 ), + QIO_CHANNEL_FEATURE_CORK = (1 << 3),
TCP_NODELAYandTCP_CORKare just hints; I think it is okay to just ignore them if not supported. You obviously disagree, so the question is why? :)
Well I was just trying not to second guess what future uses we might have of the QIOChannel API, so I went for the approach of providing a way to probe any optional features upfront. Code doesn't have to use this if it doesn't want to - it can just ignore errors from the API call later.
Regards,
Daniel
Signature · 4 lines
|: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
Re: [Qemu-devel] [PATCH v1 07/16] io: add abstract QIOChannel classes
Quoted 13 lines · Daniel P. Berrange
Quoted 11 lines · Paolo Bonzini
include/io/channel.h line 47
Quoted 4 lines · Daniel P. Berrange
include/io/channel.h new file +4
-0+QIO_CHANNEL_FEATURE_FD_PASS = (1 <<0 ), +QIO_CHANNEL_FEATURE_SHUTDOWN = (1 <<1 ), +QIO_CHANNEL_FEATURE_DELAY = (1 <<2 ), + QIO_CHANNEL_FEATURE_CORK = (1 << 3),
TCP_NODELAYandTCP_CORKare just hints; I think it is okay to just ignore them if not supported. You obviously disagree, so the question is why? :)
Quoted 5 lines
Well I was just trying not to second guess what future uses we might have of the QIOChannel API, so I went for the approach of providing a way to probe any optional features upfront. Code doesn't have to use this if it doesn't want to - it can just ignore errors from the API call later.
Perhaps we can get to a middle ground where you can probe them but no errors are reported?
Reporting errors is relatively heavyweight (g_new, possibly in a hot path if the corresponding feature is not supported) and in general QEMU never checked for the return value of socket_set_cork and socket_set_nodelay.
The probe, however, should also test if SOL_TCP and TCP_CORK are defined. I think corking is a Linux-ism.
Paolo
Re: [Qemu-devel] [PATCH v1 07/16] io: add abstract QIOChannel classes
Quoted 25 lines · Paolo Bonzini
Quoted 13 lines · Daniel P. Berrange
Quoted 11 lines · Paolo Bonzini
include/io/channel.h line 47
Quoted 4 lines · Daniel P. Berrange
include/io/channel.h new file +4
-0+QIO_CHANNEL_FEATURE_FD_PASS = (1 <<0 ), +QIO_CHANNEL_FEATURE_SHUTDOWN = (1 <<1 ), +QIO_CHANNEL_FEATURE_DELAY = (1 <<2 ), + QIO_CHANNEL_FEATURE_CORK = (1 << 3),
TCP_NODELAYandTCP_CORKare just hints; I think it is okay to just ignore them if not supported. You obviously disagree, so the question is why? :)Quoted 5 lines
Well I was just trying not to second guess what future uses we might have of the QIOChannel API, so I went for the approach of providing a way to probe any optional features upfront. Code doesn't have to use this if it doesn't want to - it can just ignore errors from the API call later.
Perhaps we can get to a middle ground where you can probe them but no errors are reported?
Quoted 7 lines
Reporting errors is relatively heavyweight (
g_new, possibly in a hot path if the corresponding feature is not supported) and in general QEMU never checked for the return value ofsocket_set_corkandsocket_set_nodelay.The probe, however, should also test if
SOL_TCPandTCP_CORKare defined. I think corking is a Linux-ism.
Having slept on it now, I think I'll just remove these two feature flags and make the methods void with no error reporting. We can always add feature probing back at a later date if it becomes clearly required.
Regards,
Daniel
Signature · 4 lines
|: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
PATCH 8/16 io: add helper module for creating watches on FDs
In reply to Daniel P. BerrangeA number of the channel implementations will require the ability to create watches on file descriptors / sockets. To avoid duplicating this code in each channel, provide a helper API for dealing with file descriptor watches.
There are two watch implementations provided. The first is useful for bi-directional file descriptors such as sockets, regular files, character devices, etc. The second works with a pair of unidirectional file descriptors such as pipes.
--- include/io/channel-watch.h | 72 ++++++++++++++++ io/Makefile.objs | 1 + io/channel-watch.c | 200 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 273 insertions(+) create mode 100644 include/io/channel-watch.h create mode 100644 io/channel-watch.c
include/io/channel-watch.h new file +72 -0
@@ -0,0 +1,72 @@ +/* +* QEMU I/O channels watch helper APIs +* +* Copyright (c) 2015 Red Hat, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, see <http://www.gnu.org/licenses/>. +* +*/ + +#ifndef QIO_CHANNEL_WATCH_H__ +#define QIO_CHANNEL_WATCH_H__ + +#include "io/channel.h" + +/* +* This module provides helper functions that will be needed by +* the various QIOChannel implementations, for creating watches +* on file descriptors / sockets +*/ + +/** +* qio_channel_create_fd_watch: +* @ioc: the channel object +* @fd: the file descriptor +* @condition: the I/O condition +* +* Create a new main loop source that is able to +* monitor the file descriptor @fd for the +* I/O conditions in @condition. This is able +* monitor block devices, character devices, +* sockets, pipes but not plain files. +* +* Returns: the new main loop source +*/ +GSource *qio_channel_create_fd_watch (QIOChannel *ioc, +int fd, +GIOCondition condition); + +/** +* qio_channel_create_fd_pair_watch: +* @ioc: the channel object +* @fdread: the file descriptor for reading +* @fdwrite: the file descriptor for writing +* @condition: the I/O condition +* +* Create a new main loop source that is able to +* monitor the pair of file descriptors @fdread +* and @fdwrite for the I/O conditions in @condition. +* This is intended for monitoring unidirectional +* file descriptors such as pipes, where a pair +* of descriptors is required for bidirectional +* I/O +* +* Returns: the new main loop source +*/ +GSource *qio_channel_create_fd_pair_watch (QIOChannel *ioc, +int fdread, +int fdwrite, +GIOCondition condition); + +#endif /* QIO_CHANNEL_WATCH_H__ */
io/Makefile.objs +1 -0
@@ -1 +1,2 @@ io-obj-y = channel.o +io-obj-y += channel-watch.o
io/channel-watch.c new file +200 -0
200 lines not on this page · raw message
Signature · 1 line
2.4.3
PATCH 9/16 io: pull Buffer code out of VNC module
In reply to Daniel P. BerrangeThe Buffer code in the VNC server is useful for the IO channel code, so pull it out into a shared module, QIOBuffer.
--- include/io/buffer.h | 118 ++++++++++++++++++++++++++++++++++++++++++++++++++++ io/Makefile.objs | 3 +- io/buffer.c | 65 +++++++++++++++++++++++++++++ ui/vnc-auth-sasl.c | 4 +- ui/vnc-enc-tight.c | 38 ++++++++--------- ui/vnc-enc-zlib.c | 6 +-- ui/vnc-enc-zrle.c | 18 ++++---- ui/vnc-jobs.c | 15 +++---- ui/vnc-ws.c | 36 ++++++++-------- ui/vnc-ws.h | 6 +-- ui/vnc.c | 67 ++++++----------------------- ui/vnc.h | 50 ++++++++-------------- 12 files changed, 277 insertions(+), 149 deletions(-) create mode 100644 include/io/buffer.h create mode 100644 io/buffer.c
include/io/buffer.h new file +118 -0
@@ -0,0 +1,118 @@ +/* +* QEMU I/O buffers +* +* Copyright (c) 2015 Red Hat, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, see <http://www.gnu.org/licenses/>. +* +*/ + +#ifndef QIO_BUFFER_H__ +#define QIO_BUFFER_H__ + +#include "qemu-common.h" + +typedef struct QIOBuffer QIOBuffer ; + +/** +* QIOBuffer: +* +* The QIOBuffer object provides a simple dynamically resizing +* array, with separate tracking of capacity and usage. This +* is typically useful when buffering I/O data. +*/ + +struct QIOBuffer { +size_t capacity; +size_t offset; +uint8_t *buffer; +}; + +/** +* qio_buffer_reserve: +* @buffer: the buffer object +* @len: the minimum required free space +* +* Ensure that the buffer has space allocated for at least +* @len bytes. If the current buffer is too small, it will +* be reallocated, possibly to a larger size than requested. +*/ +void qio_buffer_reserve (QIOBuffer *buffer,size_t len); + +/** +* qio_buffer_reset: +* @buffer: the buffer object +* +* Reset the length of the stored data to zero, but do +* not free / reallocate the memory buffer +*/ +void qio_buffer_reset (QIOBuffer *buffer); + +/** +* qio_buffer_free: +* @buffer: the buffer object +* +* Reset the length of the stored data to zero and also +* free the internal memory buffer +*/ +void qio_buffer_free (QIOBuffer *buffer); + +/** +* qio_buffer_append: +* @buffer: the buffer object +* @data: the data block to append +* @len: the length of @data in bytes +* +* Append the contents of @data to the end of the buffer. +* The caller must ensure that the buffer has sufficient +* free space for @len bytes, typically by calling the +* qio_buffer_reserve() method prior to appending. +*/ +void qio_buffer_append (QIOBuffer *buffer,const void *data,size_t len); + +/** +* qio_buffer_advance: +* @buffer: the buffer object +* @len: the number of bytes to skip +* +* Remove @len bytes of data from the head of the buffer. +* The internal buffer will not be reallocated, so will +* have at least @len bytes of free space after this +* call completes +*/ +void qio_buffer_advance (QIOBuffer *buffer,size_t len); + +/** +* qio_buffer_end: +* @buffer: the buffer object +* +* Get a pointer to the tail end of the internal buffer +* The returned pointer is only valid until the next +* call to qio_buffer_reserve(). +* +* Returns: the tail of the buffer +*/ +uint8_t *qio_buffer_end (QIOBuffer *buffer); + +/** +* qio_buffer_empty: +* @buffer: the buffer object +* +* Determine if the buffer contains any current data +* +* Returns: true if the buffer holds data, false otherwise +*/ +gboolean qio_buffer_empty (QIOBuffer *buffer); + +#endif /* QIO_BUFFER_H__ */
io/Makefile.objs +2 -1
@@ -1,2 +1,3 @@ -io-obj-y = channel.o +io-obj-y = buffer.o +io-obj-y += channel.o io-obj-y += channel-watch.o
io/buffer.c new file +65 -0
65 lines not on this page · raw message
ui/vnc-auth-sasl.c +2 -2
@@ -113,8 +113,8 @@ long vnc_client_read_sasl(VncState *vs)return vnc_client_io_error (vs,-1 , -EIO );VNC_DEBUG ("Read SASL Encoded %p size %ld Decoded %p size %d\n" , encoded, ret, decoded, decodedLen); -buffer_reserve (&vs->input, decodedLen); -buffer_append (&vs->input, decoded, decodedLen); +qio_buffer_reserve (&vs->input, decodedLen); +qio_buffer_append (&vs->input, decoded, decodedLen);return decodedLen; }
ui/vnc-enc-tight.c +19 -19
@@ -856,7 +856,7 @@ static int tight_compress_data(VncState *vs, int stream_id, size_t bytes, }/* reserve memory in output buffer */ -buffer_reserve (&vs->tight.zlib, bytes +64 ); +qio_buffer_reserve (&vs->tight.zlib, bytes +64 );/* set pointers */ zstream->next_in = vs->tight.tight.buffer;
@@ -879,7 +879,7 @@ static int tight_compress_data(VncState *vs, int stream_id, size_t bytes,tight_send_compact_size (vs, bytes);vnc_write (vs, vs->tight.zlib.buffer, bytes); -buffer_reset (&vs->tight.zlib); +qio_buffer_reset (&vs->tight.zlib);return bytes; }
@@ -1053,7 +1053,7 @@ static bool send_gradient_rect(VncState *vs, int x, int y, int w, int h)vnc_write_u8 (vs, (stream |VNC_TIGHT_EXPLICIT_FILTER ) <<4 );vnc_write_u8 (vs,VNC_TIGHT_FILTER_GRADIENT ); -buffer_reserve (&vs->tight.gradient, w *3 *sizeof (int )); +qio_buffer_reserve (&vs->tight.gradient, w *3 *sizeof (int ));if (vs->tight.pixel24) {tight_filter_gradient24 (vs, vs->tight.tight.buffer, w, h);
@@ -1066,7 +1066,7 @@ static bool send_gradient_rect(VncState *vs, int x, int y, int w, int h) bytes =2 ; } -buffer_reset (&vs->tight.gradient); +qio_buffer_reset (&vs->tight.gradient); bytes = w * h * bytes; vs->tight.tight.offset = bytes;
@@ -1149,7 +1149,7 @@ static int send_palette_rect(VncState *vs, int x, int y,static void jpeg_init_destination (j_compress_ptr cinfo) {VncState *vs = cinfo->client_data; -Buffer *buffer = &vs->tight.jpeg; +QIOBuffer *buffer = &vs->tight.jpeg; cinfo->dest->next_output_byte = (JOCTET *)buffer->buffer + buffer->offset; cinfo->dest->free_in_buffer = (size_t )(buffer->capacity - buffer->offset);
@@ -1159,10 +1159,10 @@ static void jpeg_init_destination(j_compress_ptr cinfo)static boolean jpeg_empty_output_buffer (j_compress_ptr cinfo) {VncState *vs = cinfo->client_data; -Buffer *buffer = &vs->tight.jpeg; +QIOBuffer *buffer = &vs->tight.jpeg; buffer->offset = buffer->capacity; -buffer_reserve (buffer,2048 ); +qio_buffer_reserve (buffer,2048 );jpeg_init_destination (cinfo);return TRUE; }
@@ -1171,7 +1171,7 @@ static boolean jpeg_empty_output_buffer(j_compress_ptr cinfo)static void jpeg_term_destination (j_compress_ptr cinfo) {VncState *vs = cinfo->client_data; -Buffer *buffer = &vs->tight.jpeg; +QIOBuffer *buffer = &vs->tight.jpeg; buffer->offset = buffer->capacity - cinfo->dest->free_in_buffer; }
@@ -1190,7 +1190,7 @@ static int send_jpeg_rect(VncState *vs, int x, int y, int w, int h, int quality)return send_full_color_rect (vs, x, y, w, h); } -buffer_reserve (&vs->tight.jpeg,2048 ); +qio_buffer_reserve (&vs->tight.jpeg,2048 ); cinfo.err =jpeg_std_error (&jerr);jpeg_create_compress (&cinfo);
@@ -1227,7 +1227,7 @@ static int send_jpeg_rect(VncState *vs, int x, int y, int w, int h, int quality)tight_send_compact_size (vs, vs->tight.jpeg.offset);vnc_write (vs, vs->tight.jpeg.buffer, vs->tight.jpeg.offset); -buffer_reset (&vs->tight.jpeg); +qio_buffer_reset (&vs->tight.jpeg);return 1 ; }
@@ -1270,7 +1270,7 @@ static void png_write_data(png_structp png_ptr, png_bytep data, {VncState *vs =png_get_io_ptr (png_ptr); -buffer_reserve (&vs->tight.png, vs->tight.png.offset + length); +qio_buffer_reserve (&vs->tight.png, vs->tight.png.offset + length);memcpy (vs->tight.png.buffer + vs->tight.png.offset, data, length); vs->tight.png.offset += length;
@@ -1351,7 +1351,7 @@ static int send_png_rect(VncState *vs, int x, int y, int w, int h,png_write_info (png_ptr, info_ptr); -buffer_reserve (&vs->tight.png,2048 ); +qio_buffer_reserve (&vs->tight.png,2048 ); linebuf =qemu_pixman_linebuf_create (PIXMAN_BE_r8g8b8, w); buf = (uint8_t *)pixman_image_get_data (linebuf);for (dy =0 ; dy < h; dy++)
@@ -1377,14 +1377,14 @@ static int send_png_rect(VncState *vs, int x, int y, int w, int h,tight_send_compact_size (vs, vs->tight.png.offset);vnc_write (vs, vs->tight.png.buffer, vs->tight.png.offset); -buffer_reset (&vs->tight.png); +qio_buffer_reset (&vs->tight.png); return 1; }#endif /* CONFIG_VNC_PNG */ static void vnc_tight_start (VncState *vs) { -buffer_reset (&vs->tight.tight); +qio_buffer_reset (&vs->tight.tight);// make the output buffer be the zlib buffer, so we can compress it later vs->tight.tmp = vs->output;
@@ -1686,13 +1686,13 @@ void vnc_tight_clear(VncState *vs) } } -buffer_free (&vs->tight.tight); -buffer_free (&vs->tight.zlib); -buffer_free (&vs->tight.gradient); +qio_buffer_free (&vs->tight.tight); +qio_buffer_free (&vs->tight.zlib); +qio_buffer_free (&vs->tight.gradient);#ifdef CONFIG_VNC_JPEG -buffer_free (&vs->tight.jpeg); +qio_buffer_free (&vs->tight.jpeg);#endif #ifdef CONFIG_VNC_PNG -buffer_free (&vs->tight.png); +qio_buffer_free (&vs->tight.png);#endif }
ui/vnc-enc-zlib.c +3 -3
@@ -47,7 +47,7 @@ void vnc_zlib_zfree(void *x, void *addr)static void vnc_zlib_start (VncState *vs) { -buffer_reset (&vs->zlib.zlib); +qio_buffer_reset (&vs->zlib.zlib);// make the output buffer be the zlib buffer, so we can compress it later vs->zlib.tmp = vs->output;
@@ -96,7 +96,7 @@ static int vnc_zlib_stop(VncState *vs) }// reserve memory in output buffer -buffer_reserve (&vs->output, vs->zlib.zlib.offset +64 ); +qio_buffer_reserve (&vs->output, vs->zlib.zlib.offset +64 );// set pointers zstream->next_in = vs->zlib.zlib.buffer;
@@ -148,5 +148,5 @@ void vnc_zlib_clear(VncState *vs)if (vs->zlib.stream.opaque) {deflateEnd (&vs->zlib.stream); } -buffer_free (&vs->zlib.zlib); +qio_buffer_free (&vs->zlib.zlib); }
ui/vnc-enc-zrle.c +9 -9
@@ -36,7 +36,7 @@ static const int bits_per_packed_pixel[] = {static void vnc_zrle_start (VncState *vs) { -buffer_reset (&vs->zrle.zrle); +qio_buffer_reset (&vs->zrle.zrle);/* make the output buffer be the zlib buffer, so we can compress it later */ vs->zrle.tmp = vs->output;
@@ -53,10 +53,10 @@ static void vnc_zrle_stop(VncState *vs)static void *zrle_convert_fb (VncState *vs,int x,int y,int w,int h,int bpp) { -Buffer tmp; +QIOBuffer tmp; -buffer_reset (&vs->zrle.fb); -buffer_reserve (&vs->zrle.fb, w * h * bpp + bpp); +qio_buffer_reset (&vs->zrle.fb); +qio_buffer_reserve (&vs->zrle.fb, w * h * bpp + bpp); tmp = vs->output; vs->output = vs->zrle.fb;
@@ -72,7 +72,7 @@ static int zrle_compress_data(VncState *vs, int level) {z_streamp zstream = &vs->zrle.stream; -buffer_reset (&vs->zrle.zlib); +qio_buffer_reset (&vs->zrle.zlib);if (zstream->opaque != vs) {int err;
@@ -92,7 +92,7 @@ static int zrle_compress_data(VncState *vs, int level) }/* reserve memory in output buffer */ -buffer_reserve (&vs->zrle.zlib, vs->zrle.zrle.offset +64 ); +qio_buffer_reserve (&vs->zrle.zlib, vs->zrle.zrle.offset +64 );/* set pointers */ zstream->next_in = vs->zrle.zrle.buffer;
@@ -360,7 +360,7 @@ void vnc_zrle_clear(VncState *vs)if (vs->zrle.stream.opaque) {deflateEnd (&vs->zrle.stream); } -buffer_free (&vs->zrle.zrle); -buffer_free (&vs->zrle.fb); -buffer_free (&vs->zrle.zlib); +qio_buffer_free (&vs->zrle.zrle); +qio_buffer_free (&vs->zrle.fb); +qio_buffer_free (&vs->zrle.zlib); }
ui/vnc-jobs.c +8 -7
@@ -54,7 +54,7 @@ struct VncJobQueue {QemuCond cond;QemuMutex mutex;QemuThread thread; -Buffer buffer; +QIOBuffer buffer;bool exit; QTAILQ_HEAD(, VncJob) jobs; };
@@ -167,7 +167,7 @@ void vnc_jobs_consume_buffer(VncState *vs)vnc_lock_output (vs);if (vs->jobs_buffer.offset) {vnc_write (vs, vs->jobs_buffer.buffer, vs->jobs_buffer.offset); -buffer_reset (&vs->jobs_buffer); +qio_buffer_reset (&vs->jobs_buffer); } flush = vs->csock !=-1 && vs->abort != true;vnc_unlock_output (vs);
@@ -196,7 +196,7 @@ static void vnc_async_encoding_start(VncState *orig, VncState *local) local->output = queue->buffer; local->csock =-1 ;/* Don't do any network work on this thread */ -buffer_reset (&local->output); +qio_buffer_reset (&local->output); }static void vnc_async_encoding_end (VncState *orig,VncState *local)
@@ -273,10 +273,11 @@ static int vnc_worker_thread_loop(VncJobQueue *queue) vs.output.buffer[saved_offset +1 ] = n_rectangles &0xFF ;vnc_lock_output (job->vs); +if (job->vs->csock !=-1 ) { -buffer_reserve (&job->vs->jobs_buffer, vs.output.offset); -buffer_append (&job->vs->jobs_buffer, vs.output.buffer, - vs.output.offset); +qio_buffer_reserve (&job->vs->jobs_buffer, vs.output.offset); +qio_buffer_append (&job->vs->jobs_buffer, vs.output.buffer, + vs.output.offset);/* Copy persistent encoding data */ vnc_async_encoding_end (job->vs, &vs);
@@ -310,7 +311,7 @@ static void vnc_queue_clear(VncJobQueue *q) {qemu_cond_destroy (&queue->cond);qemu_mutex_destroy (&queue->mutex); -buffer_free (&queue->buffer); +qio_buffer_free (&queue->buffer);g_free (q); queue =NULL ;/* Unset global queue */ }
ui/vnc-ws.c +18 -18
@@ -95,8 +95,8 @@ void vncws_handshake_read(void *opaque)/* Typical HTTP headers from novnc are 512 bytes, so limiting * total header size to 4096 is easily enough. */ size_t want =4096 - vs->ws_input.offset; -buffer_reserve (&vs->ws_input, want); - ret =vnc_client_read_buf (vs,buffer_end (&vs->ws_input), want); +qio_buffer_reserve (&vs->ws_input, want); + ret =vnc_client_read_buf (vs,qio_buffer_end (&vs->ws_input), want);if (!ret) {if (vs->csock ==-1 ) {
@@ -111,7 +111,7 @@ void vncws_handshake_read(void *opaque)if (handshake_end) {qemu_set_fd_handler (vs->csock, vnc_client_read,NULL , vs);vncws_process_handshake (vs, vs->ws_input.buffer, vs->ws_input.offset); -buffer_advance (&vs->ws_input, handshake_end - vs->ws_input.buffer + +qio_buffer_advance (&vs->ws_input, handshake_end - vs->ws_input.buffer +strlen (WS_HANDSHAKE_END )); }else if (vs->ws_input.offset >=4096 ) {VNC_DEBUG ("End of headers not found in first 4096 bytes\n" );
@@ -127,8 +127,8 @@ long vnc_client_read_ws(VncState *vs)size_t payload_size, header_size;VNC_DEBUG ("Read websocket %p size %zd offset %zd\n" , vs->ws_input.buffer, vs->ws_input.capacity, vs->ws_input.offset); -buffer_reserve (&vs->ws_input,4096 ); - ret =vnc_client_read_buf (vs,buffer_end (&vs->ws_input),4096 ); +qio_buffer_reserve (&vs->ws_input,4096 ); + ret =vnc_client_read_buf (vs,qio_buffer_end (&vs->ws_input),4096 );if (!ret) {return 0 ; }
@@ -146,7 +146,7 @@ long vnc_client_read_ws(VncState *vs)return err; } -buffer_advance (&vs->ws_input, header_size); +qio_buffer_advance (&vs->ws_input, header_size); }if (vs->ws_payload_remain !=0 ) { err = vncws_decode_frame_payload(&vs->ws_input,
@@ -162,10 +162,10 @@ long vnc_client_read_ws(VncState *vs) } ret += err; -buffer_reserve (&vs->input, payload_size); -buffer_append (&vs->input, payload, payload_size); +qio_buffer_reserve (&vs->input, payload_size); +qio_buffer_append (&vs->input, payload, payload_size); -buffer_advance (&vs->ws_input, payload_size); +qio_buffer_advance (&vs->ws_input, payload_size); } }while (vs->ws_input.offset >0 );
@@ -178,13 +178,13 @@ long vnc_client_write_ws(VncState *vs)VNC_DEBUG ("Write WS: Pending output %p size %zd offset %zd\n" , vs->output.buffer, vs->output.capacity, vs->output.offset);vncws_encode_frame (&vs->ws_output, vs->output.buffer, vs->output.offset); -buffer_reset (&vs->output); +qio_buffer_reset (&vs->output); ret =vnc_client_write_buf (vs, vs->ws_output.buffer, vs->ws_output.offset);if (!ret) {return 0 ; } -buffer_advance (&vs->ws_output, ret); +qio_buffer_advance (&vs->ws_output, ret);if (vs->ws_output.offset ==0 ) {qemu_set_fd_handler (vs->csock, vnc_client_read,NULL , vs);
@@ -267,8 +267,8 @@ void vncws_process_handshake(VncState *vs, uint8_t *line, size_t size)g_free (key); } -void vncws_encode_frame (Buffer *output,const void *payload, -const size_t payload_size) +void vncws_encode_frame (QIOBuffer *output,const void *payload, +const size_t payload_size) {size_t header_size =0 ;unsigned char opcode =WS_OPCODE_BINARY_FRAME ;
@@ -295,12 +295,12 @@ void vncws_encode_frame(Buffer *output, const void *payload, header_size =10 ; } -buffer_reserve (output, header_size + payload_size); -buffer_append (output, header.buf, header_size); -buffer_append (output, payload, payload_size); +qio_buffer_reserve (output, header_size + payload_size); +qio_buffer_append (output, header.buf, header_size); +qio_buffer_append (output, payload, payload_size); } -int vncws_decode_frame_header (Buffer *input, +int vncws_decode_frame_header (QIOBuffer *input,size_t *header_size,size_t *payload_remain,WsMask *payload_mask)
@@ -354,7 +354,7 @@ int vncws_decode_frame_header(Buffer *input,return 1 ; } -int vncws_decode_frame_payload (Buffer *input, +int vncws_decode_frame_payload (QIOBuffer *input,size_t *payload_remain,WsMask *payload_mask,uint8_t **payload,size_t *payload_size) {
ui/vnc-ws.h +3 -3
@@ -77,13 +77,13 @@ void vncws_handshake_read(void *opaque);long vnc_client_write_ws (VncState *vs);long vnc_client_read_ws (VncState *vs);void vncws_process_handshake (VncState *vs,uint8_t *line,size_t size); -void vncws_encode_frame (Buffer *output,const void *payload, +void vncws_encode_frame (QIOBuffer *output,const void *payload,const size_t payload_size); -int vncws_decode_frame_header (Buffer *input, +int vncws_decode_frame_header (QIOBuffer *input,size_t *header_size,size_t *payload_remain,WsMask *payload_mask); -int vncws_decode_frame_payload (Buffer *input, +int vncws_decode_frame_payload (QIOBuffer *input,size_t *payload_remain,WsMask *payload_mask,uint8_t **payload,size_t *payload_size);
ui/vnc.c +12 -55
113 lines not on this page · raw message
ui/vnc.h +18 -32
101 lines not on this page · raw message
Signature · 1 line
2.4.3
Re: [Qemu-devel] [PATCH v1 09/16] io: pull Buffer code out of VNC module
Quoted 2 lines · Daniel P. Berrange
The Buffer code in the VNC server is useful for the IO channel code, so pull it out into a shared module, QIOBuffer.
Do all traces disappear from VNC once you're done? If not, can you instead move it to util/?
Paolo
Quoted 889 lines
Signed-off-by Daniel P. Berrange <[email protected]>--- include/io/buffer.h | 118 ++++++++++++++++++++++++++++++++++++++++++++++++++++ io/Makefile.objs | 3 +- io/buffer.c | 65 +++++++++++++++++++++++++++++ ui/vnc-auth-sasl.c | 4 +- ui/vnc-enc-tight.c | 38 ++++++++--------- ui/vnc-enc-zlib.c | 6 +-- ui/vnc-enc-zrle.c | 18 ++++---- ui/vnc-jobs.c | 15 +++---- ui/vnc-ws.c | 36 ++++++++-------- ui/vnc-ws.h | 6 +-- ui/vnc.c | 67 ++++++----------------------- ui/vnc.h | 50 ++++++++-------------- 12 files changed, 277 insertions(+), 149 deletions(-) create mode 100644 include/io/buffer.h create mode 100644 io/buffer.cinclude/io/buffer.h new file +118
-0@@ -0,0 +1,118 @@ +/* +* QEMU I/O buffers +* +* Copyright (c) 2015 Red Hat, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, see <http://www.gnu.org/licenses/>. +* +*/ + +#ifndef QIO_BUFFER_H__ +#define QIO_BUFFER_H__ + +#include "qemu-common.h" + +typedef struct QIOBuffer QIOBuffer ; + +/** +* QIOBuffer: +* +* The QIOBuffer object provides a simple dynamically resizing +* array, with separate tracking of capacity and usage. This +* is typically useful when buffering I/O data. +*/ + +struct QIOBuffer { +size_t capacity; +size_t offset; +uint8_t *buffer; +}; + +/** +* qio_buffer_reserve: +* @buffer: the buffer object +* @len: the minimum required free space +* +* Ensure that the buffer has space allocated for at least +* @len bytes. If the current buffer is too small, it will +* be reallocated, possibly to a larger size than requested. +*/ +void qio_buffer_reserve (QIOBuffer *buffer,size_t len); + +/** +* qio_buffer_reset: +* @buffer: the buffer object +* +* Reset the length of the stored data to zero, but do +* not free / reallocate the memory buffer +*/ +void qio_buffer_reset (QIOBuffer *buffer); + +/** +* qio_buffer_free: +* @buffer: the buffer object +* +* Reset the length of the stored data to zero and also +* free the internal memory buffer +*/ +void qio_buffer_free (QIOBuffer *buffer); + +/** +* qio_buffer_append: +* @buffer: the buffer object +* @data: the data block to append +* @len: the length of @data in bytes +* +* Append the contents of @data to the end of the buffer. +* The caller must ensure that the buffer has sufficient +* free space for @len bytes, typically by calling the +* qio_buffer_reserve() method prior to appending. +*/ +void qio_buffer_append (QIOBuffer *buffer,const void *data,size_t len); + +/** +* qio_buffer_advance: +* @buffer: the buffer object +* @len: the number of bytes to skip +* +* Remove @len bytes of data from the head of the buffer. +* The internal buffer will not be reallocated, so will +* have at least @len bytes of free space after this +* call completes +*/ +void qio_buffer_advance (QIOBuffer *buffer,size_t len); + +/** +* qio_buffer_end: +* @buffer: the buffer object +* +* Get a pointer to the tail end of the internal buffer +* The returned pointer is only valid until the next +* call to qio_buffer_reserve(). +* +* Returns: the tail of the buffer +*/ +uint8_t *qio_buffer_end (QIOBuffer *buffer); + +/** +* qio_buffer_empty: +* @buffer: the buffer object +* +* Determine if the buffer contains any current data +* +* Returns: true if the buffer holds data, false otherwise +*/ +gboolean qio_buffer_empty (QIOBuffer *buffer); + +#endif /* QIO_BUFFER_H__ */ io/Makefile.objs +2
-1@@ -1,2 +1,3 @@ -io-obj-y = channel.o +io-obj-y = buffer.o +io-obj-y += channel.o io-obj-y += channel-watch.oio/buffer.c new file +65
-0@@ -0,0 +1,65 @@ +/* +* QEMU I/O buffers +* +* Copyright (c) 2015 Red Hat, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, see <http://www.gnu.org/licenses/>. +* +*/ + +#include "io/buffer.h" + +void qio_buffer_reserve (QIOBuffer *buffer,size_t len) +{ +if ((buffer->capacity - buffer->offset) < len) { + buffer->capacity += (len +1024 ); + buffer->buffer =g_realloc (buffer->buffer, buffer->capacity); + } +} + +gboolean qio_buffer_empty (QIOBuffer *buffer) +{ +return buffer->offset ==0 ; +} + +uint8_t *qio_buffer_end (QIOBuffer *buffer) +{ +return buffer->buffer + buffer->offset; +} + +void qio_buffer_reset (QIOBuffer *buffer) +{ + buffer->offset =0 ; +} + +void qio_buffer_free (QIOBuffer *buffer) +{ +g_free (buffer->buffer); + buffer->offset =0 ; + buffer->capacity =0 ; + buffer->buffer =NULL ; +} + +void qio_buffer_append (QIOBuffer *buffer,const void *data,size_t len) +{ +memcpy (buffer->buffer + buffer->offset, data, len); + buffer->offset += len; +} + +void qio_buffer_advance (QIOBuffer *buffer,size_t len) +{ +memmove (buffer->buffer, buffer->buffer + len, + (buffer->offset - len)); + buffer->offset -= len; +}ui/vnc-auth-sasl.c +2
-2@@ -113,8 +113,8 @@ long vnc_client_read_sasl(VncState *vs)return vnc_client_io_error (vs,-1 , -EIO );VNC_DEBUG ("Read SASL Encoded %p size %ld Decoded %p size %d\n" , encoded, ret, decoded, decodedLen); -buffer_reserve (&vs->input, decodedLen); -buffer_append (&vs->input, decoded, decodedLen); +qio_buffer_reserve (&vs->input, decodedLen); +qio_buffer_append (&vs->input, decoded, decodedLen);return decodedLen; }ui/vnc-enc-tight.c +19
-19@@ -856,7 +856,7 @@ static int tight_compress_data(VncState *vs, int stream_id, size_t bytes, }/* reserve memory in output buffer */ -buffer_reserve (&vs->tight.zlib, bytes +64 ); +qio_buffer_reserve (&vs->tight.zlib, bytes +64 );/* set pointers */ zstream->next_in = vs->tight.tight.buffer;@@ -879,7 +879,7 @@ static int tight_compress_data(VncState *vs, int stream_id, size_t bytes,tight_send_compact_size (vs, bytes);vnc_write (vs, vs->tight.zlib.buffer, bytes); -buffer_reset (&vs->tight.zlib); +qio_buffer_reset (&vs->tight.zlib);return bytes; }@@ -1053,7 +1053,7 @@ static bool send_gradient_rect(VncState *vs, int x, int y, int w, int h)vnc_write_u8 (vs, (stream |VNC_TIGHT_EXPLICIT_FILTER ) <<4 );vnc_write_u8 (vs,VNC_TIGHT_FILTER_GRADIENT ); -buffer_reserve (&vs->tight.gradient, w *3 *sizeof (int )); +qio_buffer_reserve (&vs->tight.gradient, w *3 *sizeof (int ));if (vs->tight.pixel24) {tight_filter_gradient24 (vs, vs->tight.tight.buffer, w, h);@@ -1066,7 +1066,7 @@ static bool send_gradient_rect(VncState *vs, int x, int y, int w, int h) bytes =2 ; } -buffer_reset (&vs->tight.gradient); +qio_buffer_reset (&vs->tight.gradient); bytes = w * h * bytes; vs->tight.tight.offset = bytes;@@ -1149,7 +1149,7 @@ static int send_palette_rect(VncState *vs, int x, int y,static void jpeg_init_destination (j_compress_ptr cinfo) {VncState *vs = cinfo->client_data; -Buffer *buffer = &vs->tight.jpeg; +QIOBuffer *buffer = &vs->tight.jpeg; cinfo->dest->next_output_byte = (JOCTET *)buffer->buffer + buffer->offset; cinfo->dest->free_in_buffer = (size_t )(buffer->capacity - buffer->offset);@@ -1159,10 +1159,10 @@ static void jpeg_init_destination(j_compress_ptr cinfo)static boolean jpeg_empty_output_buffer (j_compress_ptr cinfo) {VncState *vs = cinfo->client_data; -Buffer *buffer = &vs->tight.jpeg; +QIOBuffer *buffer = &vs->tight.jpeg; buffer->offset = buffer->capacity; -buffer_reserve (buffer,2048 ); +qio_buffer_reserve (buffer,2048 );jpeg_init_destination (cinfo);return TRUE; }@@ -1171,7 +1171,7 @@ static boolean jpeg_empty_output_buffer(j_compress_ptr cinfo)static void jpeg_term_destination (j_compress_ptr cinfo) {VncState *vs = cinfo->client_data; -Buffer *buffer = &vs->tight.jpeg; +QIOBuffer *buffer = &vs->tight.jpeg; buffer->offset = buffer->capacity - cinfo->dest->free_in_buffer; }@@ -1190,7 +1190,7 @@ static int send_jpeg_rect(VncState *vs, int x, int y, int w, int h, int quality)return send_full_color_rect (vs, x, y, w, h); } -buffer_reserve (&vs->tight.jpeg,2048 ); +qio_buffer_reserve (&vs->tight.jpeg,2048 ); cinfo.err =jpeg_std_error (&jerr);jpeg_create_compress (&cinfo);@@ -1227,7 +1227,7 @@ static int send_jpeg_rect(VncState *vs, int x, int y, int w, int h, int quality)tight_send_compact_size (vs, vs->tight.jpeg.offset);vnc_write (vs, vs->tight.jpeg.buffer, vs->tight.jpeg.offset); -buffer_reset (&vs->tight.jpeg); +qio_buffer_reset (&vs->tight.jpeg);return 1 ; }@@ -1270,7 +1270,7 @@ static void png_write_data(png_structp png_ptr, png_bytep data, {VncState *vs =png_get_io_ptr (png_ptr); -buffer_reserve (&vs->tight.png, vs->tight.png.offset + length); +qio_buffer_reserve (&vs->tight.png, vs->tight.png.offset + length);memcpy (vs->tight.png.buffer + vs->tight.png.offset, data, length); vs->tight.png.offset += length;@@ -1351,7 +1351,7 @@ static int send_png_rect(VncState *vs, int x, int y, int w, int h,png_write_info (png_ptr, info_ptr); -buffer_reserve (&vs->tight.png,2048 ); +qio_buffer_reserve (&vs->tight.png,2048 ); linebuf =qemu_pixman_linebuf_create (PIXMAN_BE_r8g8b8, w); buf = (uint8_t *)pixman_image_get_data (linebuf);for (dy =0 ; dy < h; dy++)@@ -1377,14 +1377,14 @@ static int send_png_rect(VncState *vs, int x, int y, int w, int h,tight_send_compact_size (vs, vs->tight.png.offset);vnc_write (vs, vs->tight.png.buffer, vs->tight.png.offset); -buffer_reset (&vs->tight.png); +qio_buffer_reset (&vs->tight.png); return 1; }#endif /* CONFIG_VNC_PNG */ static void vnc_tight_start (VncState *vs) { -buffer_reset (&vs->tight.tight); +qio_buffer_reset (&vs->tight.tight);// make the output buffer be the zlib buffer, so we can compress it later vs->tight.tmp = vs->output;@@ -1686,13 +1686,13 @@ void vnc_tight_clear(VncState *vs) } } -buffer_free (&vs->tight.tight); -buffer_free (&vs->tight.zlib); -buffer_free (&vs->tight.gradient); +qio_buffer_free (&vs->tight.tight); +qio_buffer_free (&vs->tight.zlib); +qio_buffer_free (&vs->tight.gradient);#ifdef CONFIG_VNC_JPEG -buffer_free (&vs->tight.jpeg); +qio_buffer_free (&vs->tight.jpeg);#endif #ifdef CONFIG_VNC_PNG -buffer_free (&vs->tight.png); +qio_buffer_free (&vs->tight.png);#endif }ui/vnc-enc-zlib.c +3
-3@@ -47,7 +47,7 @@ void vnc_zlib_zfree(void *x, void *addr)static void vnc_zlib_start (VncState *vs) { -buffer_reset (&vs->zlib.zlib); +qio_buffer_reset (&vs->zlib.zlib);// make the output buffer be the zlib buffer, so we can compress it later vs->zlib.tmp = vs->output;@@ -96,7 +96,7 @@ static int vnc_zlib_stop(VncState *vs) }// reserve memory in output buffer -buffer_reserve (&vs->output, vs->zlib.zlib.offset +64 ); +qio_buffer_reserve (&vs->output, vs->zlib.zlib.offset +64 );// set pointers zstream->next_in = vs->zlib.zlib.buffer;@@ -148,5 +148,5 @@ void vnc_zlib_clear(VncState *vs)if (vs->zlib.stream.opaque) {deflateEnd (&vs->zlib.stream); } -buffer_free (&vs->zlib.zlib); +qio_buffer_free (&vs->zlib.zlib); }ui/vnc-enc-zrle.c +9
-9@@ -36,7 +36,7 @@ static const int bits_per_packed_pixel[] = {static void vnc_zrle_start (VncState *vs) { -buffer_reset (&vs->zrle.zrle); +qio_buffer_reset (&vs->zrle.zrle);/* make the output buffer be the zlib buffer, so we can compress it later */ vs->zrle.tmp = vs->output;@@ -53,10 +53,10 @@ static void vnc_zrle_stop(VncState *vs)static void *zrle_convert_fb (VncState *vs,int x,int y,int w,int h,int bpp) { -Buffer tmp; +QIOBuffer tmp; -buffer_reset (&vs->zrle.fb); -buffer_reserve (&vs->zrle.fb, w * h * bpp + bpp); +qio_buffer_reset (&vs->zrle.fb); +qio_buffer_reserve (&vs->zrle.fb, w * h * bpp + bpp); tmp = vs->output; vs->output = vs->zrle.fb;@@ -72,7 +72,7 @@ static int zrle_compress_data(VncState *vs, int level) {z_streamp zstream = &vs->zrle.stream; -buffer_reset (&vs->zrle.zlib); +qio_buffer_reset (&vs->zrle.zlib);if (zstream->opaque != vs) {int err;@@ -92,7 +92,7 @@ static int zrle_compress_data(VncState *vs, int level) }/* reserve memory in output buffer */ -buffer_reserve (&vs->zrle.zlib, vs->zrle.zrle.offset +64 ); +qio_buffer_reserve (&vs->zrle.zlib, vs->zrle.zrle.offset +64 );/* set pointers */ zstream->next_in = vs->zrle.zrle.buffer;@@ -360,7 +360,7 @@ void vnc_zrle_clear(VncState *vs)if (vs->zrle.stream.opaque) {deflateEnd (&vs->zrle.stream); } -buffer_free (&vs->zrle.zrle); -buffer_free (&vs->zrle.fb); -buffer_free (&vs->zrle.zlib); +qio_buffer_free (&vs->zrle.zrle); +qio_buffer_free (&vs->zrle.fb); +qio_buffer_free (&vs->zrle.zlib); }ui/vnc-jobs.c +8
-7@@ -54,7 +54,7 @@ struct VncJobQueue {QemuCond cond;QemuMutex mutex;QemuThread thread; -Buffer buffer; +QIOBuffer buffer;bool exit; QTAILQ_HEAD(, VncJob) jobs; };@@ -167,7 +167,7 @@ void vnc_jobs_consume_buffer(VncState *vs)vnc_lock_output (vs);if (vs->jobs_buffer.offset) {vnc_write (vs, vs->jobs_buffer.buffer, vs->jobs_buffer.offset); -buffer_reset (&vs->jobs_buffer); +qio_buffer_reset (&vs->jobs_buffer); } flush = vs->csock !=-1 && vs->abort != true;vnc_unlock_output (vs);@@ -196,7 +196,7 @@ static void vnc_async_encoding_start(VncState *orig, VncState *local) local->output = queue->buffer; local->csock =-1 ;/* Don't do any network work on this thread */ -buffer_reset (&local->output); +qio_buffer_reset (&local->output); }static void vnc_async_encoding_end (VncState *orig,VncState *local)@@ -273,10 +273,11 @@ static int vnc_worker_thread_loop(VncJobQueue *queue) vs.output.buffer[saved_offset +1 ] = n_rectangles &0xFF ;vnc_lock_output (job->vs); +if (job->vs->csock !=-1 ) { -buffer_reserve (&job->vs->jobs_buffer, vs.output.offset); -buffer_append (&job->vs->jobs_buffer, vs.output.buffer, - vs.output.offset); +qio_buffer_reserve (&job->vs->jobs_buffer, vs.output.offset); +qio_buffer_append (&job->vs->jobs_buffer, vs.output.buffer, + vs.output.offset);/* Copy persistent encoding data */ vnc_async_encoding_end (job->vs, &vs);@@ -310,7 +311,7 @@ static void vnc_queue_clear(VncJobQueue *q) {qemu_cond_destroy (&queue->cond);qemu_mutex_destroy (&queue->mutex); -buffer_free (&queue->buffer); +qio_buffer_free (&queue->buffer);g_free (q); queue =NULL ;/* Unset global queue */ }ui/vnc-ws.c +18
-18@@ -95,8 +95,8 @@ void vncws_handshake_read(void *opaque)/* Typical HTTP headers from novnc are 512 bytes, so limiting * total header size to 4096 is easily enough. */ size_t want =4096 - vs->ws_input.offset; -buffer_reserve (&vs->ws_input, want); - ret =vnc_client_read_buf (vs,buffer_end (&vs->ws_input), want); +qio_buffer_reserve (&vs->ws_input, want); + ret =vnc_client_read_buf (vs,qio_buffer_end (&vs->ws_input), want);if (!ret) {if (vs->csock ==-1 ) {@@ -111,7 +111,7 @@ void vncws_handshake_read(void *opaque)if (handshake_end) {qemu_set_fd_handler (vs->csock, vnc_client_read,NULL , vs);vncws_process_handshake (vs, vs->ws_input.buffer, vs->ws_input.offset); -buffer_advance (&vs->ws_input, handshake_end - vs->ws_input.buffer + +qio_buffer_advance (&vs->ws_input, handshake_end - vs->ws_input.buffer +strlen (WS_HANDSHAKE_END )); }else if (vs->ws_input.offset >=4096 ) {VNC_DEBUG ("End of headers not found in first 4096 bytes\n" );@@ -127,8 +127,8 @@ long vnc_client_read_ws(VncState *vs)size_t payload_size, header_size;VNC_DEBUG ("Read websocket %p size %zd offset %zd\n" , vs->ws_input.buffer, vs->ws_input.capacity, vs->ws_input.offset); -buffer_reserve (&vs->ws_input,4096 ); - ret =vnc_client_read_buf (vs,buffer_end (&vs->ws_input),4096 ); +qio_buffer_reserve (&vs->ws_input,4096 ); + ret =vnc_client_read_buf (vs,qio_buffer_end (&vs->ws_input),4096 );if (!ret) {return 0 ; }@@ -146,7 +146,7 @@ long vnc_client_read_ws(VncState *vs)return err; } -buffer_advance (&vs->ws_input, header_size); +qio_buffer_advance (&vs->ws_input, header_size); }if (vs->ws_payload_remain !=0 ) { err = vncws_decode_frame_payload(&vs->ws_input,@@ -162,10 +162,10 @@ long vnc_client_read_ws(VncState *vs) } ret += err; -buffer_reserve (&vs->input, payload_size); -buffer_append (&vs->input, payload, payload_size); +qio_buffer_reserve (&vs->input, payload_size); +qio_buffer_append (&vs->input, payload, payload_size); -buffer_advance (&vs->ws_input, payload_size); +qio_buffer_advance (&vs->ws_input, payload_size); } }while (vs->ws_input.offset >0 );@@ -178,13 +178,13 @@ long vnc_client_write_ws(VncState *vs)VNC_DEBUG ("Write WS: Pending output %p size %zd offset %zd\n" , vs->output.buffer, vs->output.capacity, vs->output.offset);vncws_encode_frame (&vs->ws_output, vs->output.buffer, vs->output.offset); -buffer_reset (&vs->output); +qio_buffer_reset (&vs->output); ret =vnc_client_write_buf (vs, vs->ws_output.buffer, vs->ws_output.offset);if (!ret) {return 0 ; } -buffer_advance (&vs->ws_output, ret); +qio_buffer_advance (&vs->ws_output, ret);if (vs->ws_output.offset ==0 ) {qemu_set_fd_handler (vs->csock, vnc_client_read,NULL , vs);@@ -267,8 +267,8 @@ void vncws_process_handshake(VncState *vs, uint8_t *line, size_t size)g_free (key); } -void vncws_encode_frame (Buffer *output,const void *payload, -const size_t payload_size) +void vncws_encode_frame (QIOBuffer *output,const void *payload, +const size_t payload_size) {size_t header_size =0 ;unsigned char opcode =WS_OPCODE_BINARY_FRAME ;@@ -295,12 +295,12 @@ void vncws_encode_frame(Buffer *output, const void *payload, header_size =10 ; } -buffer_reserve (output, header_size + payload_size); -buffer_append (output, header.buf, header_size); -buffer_append (output, payload, payload_size); +qio_buffer_reserve (output, header_size + payload_size); +qio_buffer_append (output, header.buf, header_size); +qio_buffer_append (output, payload, payload_size); } -int vncws_decode_frame_header (Buffer *input, +int vncws_decode_frame_header (QIOBuffer *input,size_t *header_size,size_t *payload_remain,WsMask *payload_mask)@@ -354,7 +354,7 @@ int vncws_decode_frame_header(Buffer *input,return 1 ; } -int vncws_decode_frame_payload (Buffer *input, +int vncws_decode_frame_payload (QIOBuffer *input,size_t *payload_remain,WsMask *payload_mask,uint8_t **payload,size_t *payload_size) {ui/vnc-ws.h +3
-3@@ -77,13 +77,13 @@ void vncws_handshake_read(void *opaque);long vnc_client_write_ws (VncState *vs);long vnc_client_read_ws (VncState *vs);void vncws_process_handshake (VncState *vs,uint8_t *line,size_t size); -void vncws_encode_frame (Buffer *output,const void *payload, +void vncws_encode_frame (QIOBuffer *output,const void *payload,const size_t payload_size); -int vncws_decode_frame_header (Buffer *input, +int vncws_decode_frame_header (QIOBuffer *input,size_t *header_size,size_t *payload_remain,WsMask *payload_mask); -int vncws_decode_frame_payload (Buffer *input, +int vncws_decode_frame_payload (QIOBuffer *input,size_t *payload_remain,WsMask *payload_mask,uint8_t **payload,size_t *payload_size);ui/vnc.c +12
-55@@ -647,49 +647,6 @@ void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h,vnc_write_s32 (vs, encoding); } -void buffer_reserve (Buffer *buffer,size_t len) -{ -if ((buffer->capacity - buffer->offset) < len) { - buffer->capacity += (len +1024 ); - buffer->buffer =g_realloc (buffer->buffer, buffer->capacity); - } -} - -static int buffer_empty (Buffer *buffer) -{ -return buffer->offset ==0 ; -} - -uint8_t *buffer_end (Buffer *buffer) -{ -return buffer->buffer + buffer->offset; -} - -void buffer_reset (Buffer *buffer) -{ - buffer->offset =0 ; -} - -void buffer_free (Buffer *buffer) -{ -g_free (buffer->buffer); - buffer->offset =0 ; - buffer->capacity =0 ; - buffer->buffer =NULL ; -} - -void buffer_append (Buffer *buffer,const void *data,size_t len) -{ -memcpy (buffer->buffer + buffer->offset, data, len); - buffer->offset += len; -} - -void buffer_advance (Buffer *buf,size_t len) -{ -memmove (buf->buffer, buf->buffer + len, - (buf->offset - len)); - buf->offset -= len; -}static void vnc_desktop_resize (VncState *vs) {@@ -1220,10 +1177,10 @@ void vnc_disconnect_finish(VncState *vs)vnc_lock_output (vs);vnc_qmp_event (vs,QAPI_EVENT_VNC_DISCONNECTED ); -buffer_free (&vs->input); -buffer_free (&vs->output); -buffer_free (&vs->ws_input); -buffer_free (&vs->ws_output); +qio_buffer_free (&vs->input); +qio_buffer_free (&vs->output); +qio_buffer_free (&vs->ws_input); +qio_buffer_free (&vs->ws_output);qapi_free_VncClientInfo (vs->info);@@ -1251,7 +1208,7 @@ void vnc_disconnect_finish(VncState *vs)if (vs->bh !=NULL ) {qemu_bh_delete (vs->bh); } -buffer_free (&vs->jobs_buffer); +qio_buffer_free (&vs->jobs_buffer);for (i =0 ; i <VNC_STAT_ROWS ; ++i) {g_free (vs->lossy_rect[i]);@@ -1393,7 +1350,7 @@ static ssize_t vnc_client_write_plain(VncState *vs)if (!ret)return 0 ; -buffer_advance (&vs->output, ret); +qio_buffer_advance (&vs->output, ret);if (vs->output.offset ==0 ) {qemu_set_fd_handler (vs->csock, vnc_client_read,NULL , vs);@@ -1496,8 +1453,8 @@ static ssize_t vnc_client_read_plain(VncState *vs)ssize_t ret;VNC_DEBUG ("Read plain %p size %zd offset %zd\n" , vs->input.buffer, vs->input.capacity, vs->input.offset); -buffer_reserve (&vs->input,4096 ); - ret =vnc_client_read_buf (vs,buffer_end (&vs->input),4096 ); +qio_buffer_reserve (&vs->input,4096 ); + ret =vnc_client_read_buf (vs,qio_buffer_end (&vs->input),4096 );if (!ret)return 0 ; vs->input.offset += ret;@@ -1555,7 +1512,7 @@ void vnc_client_read(void *opaque) }if (!ret) { -buffer_advance (&vs->input, len); +qio_buffer_advance (&vs->input, len); }else { vs->read_handler_expect = ret; }@@ -1564,13 +1521,13 @@ void vnc_client_read(void *opaque)void vnc_write (VncState *vs,const void *data,size_t len) { -buffer_reserve (&vs->output, len); +qio_buffer_reserve (&vs->output, len); -if (vs->csock !=-1 &&buffer_empty (&vs->output)) { +if (vs->csock !=-1 &&qio_buffer_empty (&vs->output)) {qemu_set_fd_handler (vs->csock, vnc_client_read, vnc_client_write, vs); } -buffer_append (&vs->output, data, len); +qio_buffer_append (&vs->output, data, len); }void vnc_write_s32 (VncState *vs,int32_t value)ui/vnc.h +18
-32@@ -34,6 +34,7 @@#include "audio/audio.h" #include "qemu/bitmap.h" #include "crypto/tlssession.h" +#include "io/buffer.h" #include <zlib.h> #include <stdbool.h> @@ -56,13 +57,6 @@* *****************************************************************************/ -typedef struct Buffer -{ -size_t capacity; -size_t offset; -uint8_t *buffer; -}Buffer ; -typedef struct VncState VncState ;typedef struct VncJob VncJob ;typedef struct VncRect VncRect ;@@ -191,15 +185,15 @@ typedef struct VncTight {uint8_t quality;uint8_t compression;uint8_t pixel24; -Buffer tight; -Buffer tmp; -Buffer zlib; -Buffer gradient; +QIOBuffer tight; +QIOBuffer tmp; +QIOBuffer zlib; +QIOBuffer gradient;#ifdef CONFIG_VNC_JPEG -Buffer jpeg; +QIOBuffer jpeg;#endif #ifdef CONFIG_VNC_PNG -Buffer png; +QIOBuffer png;#endif int levels[4 ];z_stream stream[4 ];@@ -210,18 +204,18 @@ typedef struct VncHextile { } VncHextile;typedef struct VncZlib { -Buffer zlib; -Buffer tmp; +QIOBuffer zlib; +QIOBuffer tmp;z_stream stream;int level; }VncZlib ;typedef struct VncZrle {int type; -Buffer fb; -Buffer zrle; -Buffer tmp; -Buffer zlib; +QIOBuffer fb; +QIOBuffer zrle; +QIOBuffer tmp; +QIOBuffer zlib;z_stream stream;VncPalette palette; }VncZrle ;@@ -290,10 +284,10 @@ struct VncStateVncClientInfo *info; -Buffer output; -Buffer input; -Buffer ws_input; -Buffer ws_output; +QIOBuffer output; +QIOBuffer input; +QIOBuffer ws_input; +QIOBuffer ws_output;size_t ws_payload_remain;WsMask ws_payload_mask;/* current output mode information */ @@ -315,7 +309,7 @@ struct VncStatebool initialized;QemuMutex output_mutex;QEMUBH *bh; -Buffer jobs_buffer; +QIOBuffer jobs_buffer;/* Encoding specific, if you add something here, don't forget to * update vnc_async_encoding_start() @@ -535,14 +529,6 @@ ssize_t vnc_client_io_error(VncState *vs, ssize_t ret, int last_errno);void start_client_init (VncState *vs);void start_auth_vnc (VncState *vs); -/* Buffer management */ -void buffer_reserve (Buffer *buffer,size_t len); -void buffer_reset (Buffer *buffer); -void buffer_free (Buffer *buffer); -void buffer_append (Buffer *buffer,const void *data,size_t len); -void buffer_advance (Buffer *buf,size_t len); -uint8_t *buffer_end (Buffer *buffer); -/* Misc helpers */
Re: [Qemu-devel] [PATCH v1 09/16] io: pull Buffer code out of VNC module
Quoted 8 lines · Paolo Bonzini
Quoted 2 lines · Daniel P. Berrange
The Buffer code in the VNC server is useful for the IO channel code, so pull it out into a shared module, QIOBuffer.
Do all traces disappear from VNC once you're done? If not, can you instead move it to util/?
It is still used in VNC code for decoding of various framebuffer encodings. Only the websock usage gets eliminated. I'm happy to move it to util/ directory. Any preference on a name for the APIs. I used QIOBuffer since i placed it under io/ directory. Could keep that name when moved to util/, or give it a different one.
Quoted 9 lines
Quoted 9 lines
Signed-off-by Daniel P. Berrange <[email protected]>--- include/io/buffer.h | 118 ++++++++++++++++++++++++++++++++++++++++++++++++++++ io/Makefile.objs | 3 +- io/buffer.c | 65 +++++++++++++++++++++++++++++ ui/vnc-auth-sasl.c | 4 +- ui/vnc-enc-tight.c | 38 ++++++++--------- ui/vnc-enc-zlib.c | 6 +-- ui/vnc-enc-zrle.c | 18 ++++----
Quoted 8 lines
Quoted 8 lines
ui/vnc-jobs.c | 15 +++---- ui/vnc-ws.c | 36 ++++++++-------- ui/vnc-ws.h | 6 +-- ui/vnc.c | 67 ++++++----------------------- ui/vnc.h | 50 ++++++++-------------- 12 files changed, 277 insertions(+), 149 deletions(-) create mode 100644 include/io/buffer.h create mode 100644 io/buffer.c
Regards,
Daniel
Signature · 4 lines
|: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
Re: [Qemu-devel] [PATCH v1 09/16] io: pull Buffer code out of VNC module
Quoted 6 lines · Daniel P. Berrange
It is still used in VNC code for decoding of various framebuffer encodings. Only the websock usage gets eliminated. I'm happy to move it to util/ directory. Any preference on a name for the APIs. I used QIOBuffer since i placed it under io/ directory. Could keep that name when moved to util/, or give it a different one.
Just keeping Buffer would be okay with me.
Paolo
PATCH 10/16 io: add QIOTask class for async operations
In reply to Daniel P. BerrangeA number of I/O operations need to be performed asynchronously to avoid blocking the main loop. The caller of such APIs need to provide a callback to be invoked on completion/error and need access to the error, if any. The small QIOTask provides a simple framework for dealing with such probes. The API docs inline provide an outline of how this is to be used.
Some functions don't have the ability to run asynchronously (eg getaddrinfo always blocks), so to facilitate their use, the task class provides a mechanism to run a blocking function in a thread, while triggering the completion callback in the main event loop thread. This easily allows any synchronous function to be made asynchronous, albeit at the cost of spawning a thread.
In this series, the QIOTask class will be used for things like the TLS handshake, the websockets handshake and TCP connect() progress.
The concept of QIOTask is inspired by the GAsyncResult interface / GTask class in the GIO libraries. The min version requirements on glib don't allow those to be used from QEMU, so QIOTask provides a facsimilie which can be easily switched to GTask in the future if the min version is increased.
--- include/io/task.h | 256 +++++++++++++++++++++++++++++++++++++++++++++++ io/Makefile.objs | 1 + io/task.c | 159 +++++++++++++++++++++++++++++ tests/.gitignore | 1 + tests/Makefile | 3 + tests/test-io-task.c | 276 +++++++++++++++++++++++++++++++++++++++++++++++++++ trace-events | 9 ++ 7 files changed, 705 insertions(+) create mode 100644 include/io/task.h create mode 100644 io/task.c create mode 100644 tests/test-io-task.c
include/io/task.h new file +256 -0
@@ -0,0 +1,256 @@ +/* +* QEMU I/O task +* +* Copyright (c) 2015 Red Hat, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, see <http://www.gnu.org/licenses/>. +* +*/ + +#ifndef QIO_TASK_H__ +#define QIO_TASK_H__ + +#include "qemu-common.h" +#include "qapi/error.h" +#include "qom/object.h" + +typedef struct QIOTask QIOTask ; + +typedef void (*QIOTaskFunc )(Object *source, +Error *err, +gpointer opaque); + +typedef int (*QIOTaskWorker )(QIOTask *task, +Error **errp, +gpointer opaque); + +/** +* QIOTask: +* +* The QIOTask object provides a simple mechanism for reporting +* success / failure of long running background operations. +* +* A object on which the operation is to be performed could have +* a public API which accepts a task callback: +* +* <example> +* <title>Task callback function signature</title> +* <programlisting> +* void myobject_operation(QMyObject *obj, +* QIOTaskFunc *func, +* gpointer opaque, +* GDestroyNotify *notify); +* </programlisting> +* </example> +* +* The 'func' parameter is the callback to be invoked, and 'opaque' +* is data to pass to it. The optional 'notify' function is used +* to free 'opaque' when no longer needed. +* +* Now, lets say the implementation of this method wants to set +* a timer to run once a second checking for completion of some +* activity. It would do something like +* +* <example> +* <title>Task callback function implementation</title> +* <programlisting> +* void myobject_operation(QMyObject *obj, +* QIOTaskFunc *func, +* gpointer opaque, +* GDestroyNotify *notify) +* { +* QIOTask *task; +* +* task = qio_task_new(OBJECT(obj), func, opaque, notify); +* +* g_timeout_add_full(G_PRIORITY_DEFAULT, +* 1000, +* myobject_operation_timer, +* task, +* NULL); +* } +* </programlisting> +* </example> +* +* It could equally have setup a watch on a file descriptor or +* created a background thread, or something else entirely. +* Notice that the source object is passed to the task, and +* QIOTask will hold a reference on that. This ensure that +* the QMyObject instance cannot be garbage collected while +* the async task is still in progress. +* +* In this case, myobject_operation_timer will fire after +* 3 secs and do +* +* <example> +* <title>Task timer function</title> +* <programlisting> +* gboolean myobject_operation_timer(gpointer opaque) +* { +* QIOTask *task = QIO_TASK(opaque); +* Error *err;* +* +* ...check something important... +* if (err) { +* qio_task_abort(task, err); +* error_free(task); +* return FALSE; +* } else if (...work is completed ...) { +* qio_task_complete(task); +* return FALSE; +* } +* ...carry on polling ... +* return TRUE; +* } +* </programlisting> +* </example> +* +* Once this function returns false, object_unref will be called +* automatically on the task causing it to be released and the +* ref on QMyObject dropped too. +* +* The QIOTask module can also be used to perform operations +* in a background thread context, while still reporting the +* results in the main event thread. This allows code which +* cannot easily be rewritten to be asychronous (such as DNS +* lookups) to be easily run non-blocking. Reporting the +* results in the main thread context means that the caller +* typically does not need to be concerned about thread +* safety wrt the QEMU global mutex. +* +* For example, the socket_listen() method will block the caller +* while DNS lookups take place if given a name, instead of IP +* address. The C library often do not provide a practical async +* DNS API, so the to get non-blocking DNS lookups in a portable +* manner requires use of a thread. So achieve a non-blocking +* socket listen using QIOTask would require: +* +* <example> +* static int myobject_listen_worker(QIOTask *task, +* Error **errp, +* gpointer opaque) +* { +* QMyObject obj = QMY_OBJECT(qio_task_get_source(task)); +* SocketAddress *addr = opaque; +* +* obj->fd = socket_listen(addr, errp); +* if (obj->fd < 0) { +* return -1; +* } +* return 0; +* } +* +* void myobject_listen_async(QMyObject *obj, +* SocketAddress *addr, +* QIOTaskFunc *func, +* gpointer opaque, +* GDestroyNotify *notify) +* { +* QIOTask *task; +* SocketAddress *addrCopy; +* +* qapi_copy_SocketAddress(&addrCopy, addr); +* task = qio_task_new(OBJECT(obj), func, opaque, notify); +* +* qio_task_run_in_thread(task, myobject_listen_worker, +* addrCopy, +* qapi_free_SocketAddress); +* } +* </example> +* +* NB, The 'func' callback passed into myobject_listen_async +* will be invoked from the main event thread, despite the +* actual operation being performed in a different thread. +*/ + +/** +* qio_task_new: +* @source: the object on which the operation is invoked +* @func: the callback to invoke when the task completes +* @opaque: opaque data to pass to @func when invoked +* @destroy: optional callback to free @opaque +* +* Creates a new task struct to track completion of a +* background operation running on the object @source. +* When the operation completes or fails, the callback +* @func will be invoked. The callback can access the +* 'err' attribute in the task object to determine if +* the operation was successful or not. +* +* The returned task will be released when one of +* qio_task_abort() or qio_task_complete() are invoked. +* +* Returns: the task struct +*/ +QIOTask *qio_task_new (Object *source, +QIOTaskFunc func, +gpointer opaque, +GDestroyNotify destroy); + +/** +* qio_task_run_in_thread: +* @task: the task struct +* @worker: the function to invoke in a thread +* @opaque: opaque data to pass to @worker +* @destroy: function to free @opaque +* +* Run a task in a background thread. If @worker +* returns 0 it will call qio_task_complete() in +* the main event thread context. If @worker +* returns -1 it will call qio_task_abort() in +* the main event thread context. +*/ +void qio_task_run_in_thread (QIOTask *task, +QIOTaskWorker worker, +gpointer opaque, +GDestroyNotify destroy); + +/** +* qio_task_complete: +* @task: the task struct +* +* Mark the operation as succesfully completed +* and free the memory for @task. +*/ +void qio_task_complete (QIOTask *task); + +/** +* qio_task_abort: +* @task: the task struct +* @err: the error to record for the operation +* +* Mark the operation as failed, with @err providing +* details about the failure. The @err may be freed +* afer the function returns, as the notification +* callback is invoked synchronously. The @task will +* be freed when this call completes. +*/ +void qio_task_abort (QIOTask *task, +Error *err); + + +/** +* qio_task_get_source: +* @task: the task struct +* +* Get the source object associated with the background +* task. This returns a new reference to the object, +* which the caller must released with object_unref() +* when no longer required. +* +* Returns: the source object +*/ +Object *qio_task_get_source (QIOTask *task); + +#endif /* QIO_TASK_H__ */
io/Makefile.objs +1 -0
@@ -1,3 +1,4 @@ io-obj-y = buffer.o +io-obj-y += task.o io-obj-y += channel.o io-obj-y += channel-watch.o
io/task.c new file +159 -0
159 lines not on this page · raw message
tests/.gitignore +1 -0
@@ -23,6 +23,7 @@ test-cutils test-hbitmap test-int128 test-iov +test-io-task test-mul64 test-opts-visitor test-qapi-event.[ch]
tests/Makefile +3 -0
@@ -80,6 +80,7 @@ check-unit-$(CONFIG_GNUTLS_HASH) += tests/test-crypto-hash$(EXESUF) check-unit-y += tests/test-crypto-cipher$(EXESUF) check-unit-$(CONFIG_GNUTLS) += tests/test-crypto-tlscredsx509$(EXESUF) check-unit-$(CONFIG_GNUTLS) += tests/test-crypto-tlssession$(EXESUF) +check-unit-y += tests/test-io-task$(EXESUF) check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
@@ -292,6 +293,7 @@ test-qapi-obj-y = tests/test-qapi-visit.o tests/test-qapi-types.o \ $(test-qom-obj-y) test-crypto-obj-y = $(crypto-obj-y) $(test-qom-obj-y) test-block-obj-y = $(block-obj-y) $(test-crypto-obj-y) +test-io-obj-y = $(io-obj-y) $(test-crypto-obj-y) tests/check-qint$(EXESUF): tests/check-qint.o $(test-util-obj-y) tests/check-qstring$(EXESUF): tests/check-qstring.o $(test-util-obj-y)
@@ -364,6 +366,7 @@ tests/test-crypto-tlscredsx509$(EXESUF): tests/test-crypto-tlscredsx509.o \ tests/crypto-tls-x509-helpers.o tests/pkix_asn1_tab.o $(test-crypto-obj-y) tests/test-crypto-tlssession$(EXESUF): tests/test-crypto-tlssession.o \ tests/crypto-tls-x509-helpers.o tests/pkix_asn1_tab.o $(test-crypto-obj-y) +tests/test-io-task$(EXESUF): tests/test-io-task.o $(test-io-obj-y) libqos-obj-y = tests/libqos/pci.o tests/libqos/fw_cfg.o tests/libqos/malloc.o libqos-obj-y += tests/libqos/i2c.o tests/libqos/libqos.o
tests/test-io-task.c new file +276 -0
276 lines not on this page · raw message
trace-events +9 -0
@@ -1685,3 +1685,12 @@ qcrypto_tls_creds_x509_load_cert_list(void *creds, const char *file) "TLS creds # crypto/tlssession.c qcrypto_tls_session_new(void *session, void *creds, const char *hostname, const char *aclname, int endpoint) "TLS session new session=%p creds=%p hostname=%s aclname=%s endpoint=%d" + +# io/task.c +qio_task_new(void *task, void *source, void *func, void *opaque) "Task new task=%p source=%p func=%p opaque=%p" +qio_task_complete(void *task) "Task complete task=%p" +qio_task_abort(void *task) "Task abort task=%p" +qio_task_thread_start(void *task, void *worker, void *opaque) "Task thread start task=%p worker=%p opaque=%p" +qio_task_thread_run(void *task) "Task thread run task=%p" +qio_task_thread_exit(void *task) "Task thread exit task=%p" +qio_task_thread_result(void *task) "Task thread result task=%p"
Signature · 1 line
2.4.3
PATCH 11/16 io: add QIOChannelSocket class
In reply to Daniel P. BerrangeImplement a QIOChannel subclass that supports sockets I/O. The implementation is able to manage a single socket file descriptor, whether a TCP/UNIX listener, TCP/UNIX connection, or a UDP datagram. It provides APIs which can listen and connect either asynchronously or synchronously. Since there is no asynchronous DNS lookup API available, it uses the QIOTask helper for spawning a background thread to ensure non-blocking operation.
--- configure | 11 + include/io/channel-socket.h | 244 +++++++++++++ io/Makefile.objs | 1 + io/channel-socket.c | 763 +++++++++++++++++++++++++++++++++++++++++ scripts/create_config | 9 + tests/.gitignore | 1 + tests/Makefile | 3 + tests/io-channel-helpers.c | 222 ++++++++++++ tests/io-channel-helpers.h | 30 ++ tests/test-io-channel-socket.c | 367 ++++++++++++++++++++ trace-events | 19 + 11 files changed, 1670 insertions(+) create mode 100644 include/io/channel-socket.h create mode 100644 io/channel-socket.c create mode 100644 tests/io-channel-helpers.c create mode 100644 tests/io-channel-helpers.h create mode 100644 tests/test-io-channel-socket.c
configure +11 -0
@@ -2262,6 +2262,14 @@ fi ########################################## +# getifaddrs (for tests/test-io-channel-socket ) + +have_ifaddrs_h=yes +if ! check_include "ifaddrs.h" ; then + have_ifaddrs_h=no +fi + +########################################## # VTE probe if test "$vte" != "no"; then
@@ -4934,6 +4942,9 @@ fi if test "$tasn1" = "yes" ; then echo "CONFIG_TASN1=y" >> $config_host_mak fi +if test "$have_ifaddrs_h" = "yes" ; then + echo "HAVE_IFADDRS_H=y" >> $config_host_mak +fi if test "$vte" = "yes" ; then echo "CONFIG_VTE=y" >> $config_host_mak echo "VTE_CFLAGS=$vte_cflags" >> $config_host_mak
include/io/channel-socket.h new file +244 -0
244 lines not on this page · raw message
io/Makefile.objs +1 -0
@@ -2,3 +2,4 @@ io-obj-y = buffer.o io-obj-y += task.o io-obj-y += channel.o io-obj-y += channel-watch.o +io-obj-y += channel-socket.o
io/channel-socket.c new file +763 -0
763 lines not on this page · raw message
scripts/create_config +9 -0
@@ -61,6 +61,15 @@ case $line in value=${line#*=} echo "#define $name $value" ;; + HAVE_*=y) # configuration + name=${line%=*} + echo "#define $name 1" + ;; + HAVE_*=*) # configuration + name=${line%=*} + value=${line#*=} + echo "#define $name $value" + ;; ARCH=*) # configuration arch=${line#*=} arch_name=`echo $arch | LC_ALL=C tr '[a-z]' '[A-Z]'`
tests/.gitignore +1 -0
@@ -23,6 +23,7 @@ test-cutils test-hbitmap test-int128 test-iov +test-io-channel-socket test-io-task test-mul64 test-opts-visitor
tests/Makefile +3 -0
@@ -81,6 +81,7 @@ check-unit-y += tests/test-crypto-cipher$(EXESUF) check-unit-$(CONFIG_GNUTLS) += tests/test-crypto-tlscredsx509$(EXESUF) check-unit-$(CONFIG_GNUTLS) += tests/test-crypto-tlssession$(EXESUF) check-unit-y += tests/test-io-task$(EXESUF) +check-unit-y += tests/test-io-channel-socket$(EXESUF) check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
@@ -367,6 +368,8 @@ tests/test-crypto-tlscredsx509$(EXESUF): tests/test-crypto-tlscredsx509.o \ tests/test-crypto-tlssession$(EXESUF): tests/test-crypto-tlssession.o \ tests/crypto-tls-x509-helpers.o tests/pkix_asn1_tab.o $(test-crypto-obj-y) tests/test-io-task$(EXESUF): tests/test-io-task.o $(test-io-obj-y) +tests/test-io-channel-socket$(EXESUF): tests/test-io-channel-socket.o \ + tests/io-channel-helpers.o $(test-io-obj-y) libqos-obj-y = tests/libqos/pci.o tests/libqos/fw_cfg.o tests/libqos/malloc.o libqos-obj-y += tests/libqos/i2c.o tests/libqos/libqos.o
tests/io-channel-helpers.c new file +222 -0
222 lines not on this page · raw message
tests/io-channel-helpers.h new file +30 -0
@@ -0,0 +1,30 @@ +/* +* QEMU I/O channel test helpers +* +* Copyright (c) 2015 Red Hat, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, see <http://www.gnu.org/licenses/>. +* +*/ + +#include "io/channel.h" + +#ifndef TEST_IO_CHANNEL_HELPERS +#define TEST_IO_CHANNEL_HELPERS + +void test_io_channel_comms (bool blocking, +QIOChannel *src, +QIOChannel *dst); + +#endif /* TEST_IO_CHANNEL_HELPERS */
tests/test-io-channel-socket.c new file +367 -0
367 lines not on this page · raw message
trace-events +19 -0
@@ -1694,3 +1694,22 @@ qio_task_thread_start(void *task, void *worker, void *opaque) "Task thread start qio_task_thread_run(void *task) "Task thread run task=%p" qio_task_thread_exit(void *task) "Task thread exit task=%p" qio_task_thread_result(void *task) "Task thread result task=%p" + +# io/channel-socket.c +qio_channel_socket_new(void *ioc) "Socket new ioc=%p" +qio_channel_socket_new_fd(void *ioc, int fd) "Socket new ioc=%p fd=%d" +qio_channel_socket_connect_sync(void *ioc, void *addr) "Socket connect sync ioc=%p addr=%p" +qio_channel_socket_connect_async(void *ioc, void *addr) "Socket connect async ioc=%p addr=%p" +qio_channel_socket_connect_fail(void *ioc) "Socket connect fail ioc=%p" +qio_channel_socket_connect_complete(void *ioc, int fd) "Socket connect complete ioc=%p fd=%d" +qio_channel_socket_listen_sync(void *ioc, void *addr) "Socket listen sync ioc=%p addr=%p" +qio_channel_socket_listen_async(void *ioc, void *addr) "Socket listen async ioc=%p addr=%p" +qio_channel_socket_listen_fail(void *ioc) "Socket listen fail ioc=%p" +qio_channel_socket_listen_complete(void *ioc, int fd) "Socket listen complete ioc=%p fd=%d" +qio_channel_socket_dgram_sync(void *ioc, void *localAddr, void *remoteAddr) "Socket dgram sync ioc=%p localAddr=%p remoteAddr=%p" +qio_channel_socket_dgram_async(void *ioc, void *localAddr, void *remoteAddr) "Socket dgram async ioc=%p localAddr=%p remoteAddr=%p" +qio_channel_socket_dgram_fail(void *ioc) "Socket dgram fail ioc=%p" +qio_channel_socket_dgram_complete(void *ioc, int fd) "Socket dgram complete ioc=%p fd=%d" +qio_channel_socket_accept(void *ioc) "Socket accept start ioc=%p" +qio_channel_socket_accept_fail(void *ioc) "Socket accept fail ioc=%p" +qio_channel_socket_accept_complete(void *ioc, void *cioc, int fd) "Socket accept complete ioc=%p cioc=%p fd=%d"
Signature · 1 line
2.4.3
PATCH 12/16 io: add QIOChannelFile class
In reply to Daniel P. BerrangeAdd a QIOChannel subclass that is capable of operating on things that are files, such as plain files, pipes, character/block devices, but notably not sockets.
--- include/io/channel-file.h | 93 +++++++++++++++++ io/Makefile.objs | 1 + io/channel-file.c | 237 +++++++++++++++++++++++++++++++++++++++++++ tests/.gitignore | 2 + tests/Makefile | 3 + tests/io-channel-helpers.c | 25 +++++ tests/io-channel-helpers.h | 3 + tests/test-io-channel-file.c | 91 +++++++++++++++++ trace-events | 4 + 9 files changed, 459 insertions(+) create mode 100644 include/io/channel-file.h create mode 100644 io/channel-file.c create mode 100644 tests/test-io-channel-file.c
include/io/channel-file.h new file +93 -0
@@ -0,0 +1,93 @@ +/* +* QEMU I/O channels files driver +* +* Copyright (c) 2015 Red Hat, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, see <http://www.gnu.org/licenses/>. +* +*/ + +#ifndef QIO_CHANNEL_FILE_H__ +#define QIO_CHANNEL_FILE_H__ + +#include "io/channel.h" + +#define TYPE_QIO_CHANNEL_FILE "qio-channel-file" +#define QIO_CHANNEL_FILE (obj) \ + OBJECT_CHECK(QIOChannelFile, (obj), TYPE_QIO_CHANNEL_FILE) + +typedef struct QIOChannelFile QIOChannelFile ; + +/** +* QIOChannelFile: +* +* The QIOChannelFile object provides a channel implementation +* that is able to perform I/O on block devices, character +* devices, FIFOs, pipes and plain files. While it is technically +* able to work on sockets too on the UNIX platform, this is not +* portable to Windows and lacks some extra sockets specific +* functionality. So the QIOChannelSocket object is recommended +* for that use case. +* +*/ + +struct QIOChannelFile { +QIOChannel parent; +int fd; +}; + + +/** +* qio_channel_file_new_fd: +* @fd: the file descriptor +* +* Create a new IO channel object for a file represented +* by the @fd parameter. @fd can be associated with a +* block device, character device, fifo, pipe, or a +* regular file. For sockets, the QIOChannelSocket class +* should be used instead, as this provides greater +* functionality and cross platform portability. +* +* The channel will own the passed in file descriptor +* and will take responsibility for closing it, so the +* caller must not close it. If appropriate the caller +* should dup() its FD before opening the channel. +* +* Returns: the new channel object +*/ +QIOChannelFile * +qio_channel_file_new_fd (int fd); + +/** +* qio_channel_file_new_path: +* @fd: the file descriptor +* @flags: the open flags (O_RDONLY|O_WRONLY|O_RDWR, etc) +* @mode: the file creation mode if O_WRONLY is set in @flags +* @errp: pointer to initialized error object +* +* Create a new IO channel object for a file represented +* by the @path parameter. @path can point to any +* type of file on which sequential I/O can be +* performed, whether it be a plain file, character +* device or block device. +* +* Returns: the new channel object +*/ +QIOChannelFile * +qio_channel_file_new_path (const char *path, +int flags, +mode_t mode, +Error **errp); + +#endif /* QIO_CHANNEL_FILE_H__ */
io/Makefile.objs +1 -0
@@ -3,3 +3,4 @@ io-obj-y += task.o io-obj-y += channel.o io-obj-y += channel-watch.o io-obj-y += channel-socket.o +io-obj-y += channel-file.o
io/channel-file.c new file +237 -0
237 lines not on this page · raw message
tests/.gitignore +2 -0
@@ -23,6 +23,8 @@ test-cutils test-hbitmap test-int128 test-iov +test-io-channel-file +test-io-channel-file.txt test-io-channel-socket test-io-task test-mul64
tests/Makefile +3 -0
@@ -82,6 +82,7 @@ check-unit-$(CONFIG_GNUTLS) += tests/test-crypto-tlscredsx509$(EXESUF) check-unit-$(CONFIG_GNUTLS) += tests/test-crypto-tlssession$(EXESUF) check-unit-y += tests/test-io-task$(EXESUF) check-unit-y += tests/test-io-channel-socket$(EXESUF) +check-unit-y += tests/test-io-channel-file$(EXESUF) check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
@@ -370,6 +371,8 @@ tests/test-crypto-tlssession$(EXESUF): tests/test-crypto-tlssession.o \ tests/test-io-task$(EXESUF): tests/test-io-task.o $(test-io-obj-y) tests/test-io-channel-socket$(EXESUF): tests/test-io-channel-socket.o \ tests/io-channel-helpers.o $(test-io-obj-y) +tests/test-io-channel-file$(EXESUF): tests/test-io-channel-file.o \ + tests/io-channel-helpers.o $(test-io-obj-y) libqos-obj-y = tests/libqos/pci.o tests/libqos/fw_cfg.o tests/libqos/malloc.o libqos-obj-y += tests/libqos/i2c.o tests/libqos/libqos.o
tests/io-channel-helpers.c +25 -0
@@ -220,3 +220,28 @@ void test_io_channel_comms(bool blocking,g_free (data->output);g_free (data); } + + +void test_io_channel_comms_serial (QIOChannel *src, +QIOChannel *dst) +{ +struct TestIOData *data; + + data =test_load_io_data ("libqemuutil.a" ); + data->src = src; + data->dst = dst; + data->blocking = true; + +test_io_thread_writer (data); +test_io_thread_reader (data); + +g_assert_cmpint (memcmp (data->input, + data->output, + data->len), ==, 0); +g_assert (data->readerr ==NULL ); +g_assert (data->writeerr ==NULL ); + +g_free (data->input); +g_free (data->output); +g_free (data); +}
tests/io-channel-helpers.h +3 -0
@@ -27,4 +27,7 @@ void test_io_channel_comms(bool blocking, QIOChannel *src, QIOChannel *dst); +void test_io_channel_comms_serial (QIOChannel *src, +QIOChannel *dst); +#endif /* TEST_IO_CHANNEL_HELPERS */
tests/test-io-channel-file.c new file +91 -0
91 lines not on this page · raw message
trace-events +4 -0
@@ -1713,3 +1713,7 @@ qio_channel_socket_dgram_complete(void *ioc, int fd) "Socket dgram complete ioc= qio_channel_socket_accept(void *ioc) "Socket accept start ioc=%p" qio_channel_socket_accept_fail(void *ioc) "Socket accept fail ioc=%p" qio_channel_socket_accept_complete(void *ioc, void *cioc, int fd) "Socket accept complete ioc=%p cioc=%p fd=%d" + +# io/channel-file.c +qio_channel_file_new_fd(void *ioc, int fd) "File new fd ioc=%p fd=%d" +qio_channel_file_new_path(void *ioc, const char *path, int flags, int mode, int fd) "File new fd ioc=%p path=%s flags=%d mode=%d fd=%d"
Signature · 1 line
2.4.3
PATCH 13/16 io: add QIOChannelTLS class
In reply to Daniel P. BerrangeAdd a QIOChannel subclass that can run the TLS protocol over the top of another QIOChannel instance. The object provides a simplified API to perform the handshake when starting the TLS session. The layering of TLS over the underlying channel does not have to be setup immediately. It is possible to take an existing QIOChannel that has done some handshake and then swap in the QIOChannelTLS layer. This allows for use with protocols which start TLS right away, and those which start plain text and then negotiate TLS.
--- include/io/channel-tls.h | 142 +++++++++++++++ io/Makefile.objs | 1 + io/channel-tls.c | 419 ++++++++++++++++++++++++++++++++++++++++++++ tests/.gitignore | 1 + tests/Makefile | 4 + tests/test-io-channel-tls.c | 335 +++++++++++++++++++++++++++++++++++ trace-events | 10 ++ 7 files changed, 912 insertions(+) create mode 100644 include/io/channel-tls.h create mode 100644 io/channel-tls.c create mode 100644 tests/test-io-channel-tls.c
include/io/channel-tls.h new file +142 -0
@@ -0,0 +1,142 @@ +/* +* QEMU I/O channels TLS driver +* +* Copyright (c) 2015 Red Hat, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, see <http://www.gnu.org/licenses/>. +* +*/ + +#ifndef QIO_CHANNEL_TLS_H__ +#define QIO_CHANNEL_TLS_H__ + +#include "io/channel.h" +#include "io/task.h" +#include "crypto/tlssession.h" + +#define TYPE_QIO_CHANNEL_TLS "qio-channel-tls" +#define QIO_CHANNEL_TLS (obj) \ + OBJECT_CHECK(QIOChannelTLS, (obj), TYPE_QIO_CHANNEL_TLS) + +typedef struct QIOChannelTLS QIOChannelTLS ; + +/** +* QIOChannelTLS +* +* The QIOChannelTLS class provides a channel wrapper which +* can transparently run the TLS encryption protocol. It is +* usually used over a TCP socket, but there is actually no +* technical restriction on which type of master channel is +* used as the transport. +* +* This channel object is capable of running as either a +* TLS server or TLS client. +*/ + +struct QIOChannelTLS { +QIOChannel parent; +QIOChannel *master; +QCryptoTLSSession *session; +}; + +/** +* qio_channel_tls_new_server: +* @master: the underlying channel object +* @creds: the credentials to use for TLS handshake +* @aclname: the access control list for validating clients +* @errp: pointer to an uninitialized error object +* +* Create a new TLS channel that runs the server side of +* a TLS session. The TLS session handshake will use the +* credentials provided in @creds. If the @aclname parameter +* is non-NULL, then the client will have to provide +* credentials (ie a x509 client certificate) which will +* then be validated against the ACL. +* +* After creating the channel, it is mandatory to call +* the qio_channel_tls_handshake() method before attempting +* todo any I/O on the channel. +* +* Once the handshake has completed, all I/O should be done +* via the new TLS channel object and not the original +* master channel +* +* Returns: the new TLS channel object, or NULL +*/ +QIOChannelTLS * +qio_channel_tls_new_server (QIOChannel *master, +QCryptoTLSCreds *creds, +const char *aclname, +Error **errp); + +/** +* qio_channel_tls_new_client: +* @master: the underlying channel object +* @creds: the credentials to use for TLS handshake +* @hostname: the user specified server hostname +* @errp: pointer to an uninitialized error object +* +* Create a new TLS channel that runs the client side of +* a TLS session. The TLS session handshake will use the +* credentials provided in @creds. The @hostname parameter +* should provide the user specified hostname of the server +* and will be validated against the server's credentials +* (ie CommonName of the x509 certificate) +* +* After creating the channel, it is mandatory to call +* the qio_channel_tls_handshake() method before attempting +* todo any I/O on the channel. +* +* Once the handshake has completed, all I/O should be done +* via the new TLS channel object and not the original +* master channel +* +* Returns: the new TLS channel object, or NULL +*/ +QIOChannelTLS * +qio_channel_tls_new_client (QIOChannel *master, +QCryptoTLSCreds *creds, +const char *hostname, +Error **errp); + +/** +* qio_channel_tls_handshake: +* @ioc: the TLS channel object +* @func: the callback to invoke when completed +* @opaque: opaque data to pass to @func +* @destroy: optional callback to free @opaque +* +* Perform the TLS session handshake. This method +* will return immediately and the handshake will +* continue in the background, provided the main +* loop is running. When the handshake is complete, +* or fails, the @func callback will be invoked. +*/ +void qio_channel_tls_handshake (QIOChannelTLS *ioc, +QIOTaskFunc func, +gpointer opaque, +GDestroyNotify destroy); + +/** +* qio_channel_tls_get_session: +* @ioc: the TLS channel object +* +* Get the TLS session used by the channel. +* +* Returns: the TLS session +*/ +QCryptoTLSSession * +qio_channel_tls_get_session (QIOChannelTLS *ioc); + +#endif /* QIO_CHANNEL_TLS_H__ */
io/Makefile.objs +1 -0
@@ -4,3 +4,4 @@ io-obj-y += channel.o io-obj-y += channel-watch.o io-obj-y += channel-socket.o io-obj-y += channel-file.o +io-obj-y += channel-tls.o
io/channel-tls.c new file +419 -0
419 lines not on this page · raw message
tests/.gitignore +1 -0
@@ -26,6 +26,7 @@ test-iov test-io-channel-file test-io-channel-file.txt test-io-channel-socket +test-io-channel-tls test-io-task test-mul64 test-opts-visitor
tests/Makefile +4 -0
@@ -83,6 +83,7 @@ check-unit-$(CONFIG_GNUTLS) += tests/test-crypto-tlssession$(EXESUF) check-unit-y += tests/test-io-task$(EXESUF) check-unit-y += tests/test-io-channel-socket$(EXESUF) check-unit-y += tests/test-io-channel-file$(EXESUF) +check-unit-$(CONFIG_GNUTLS) += tests/test-io-channel-tls$(EXESUF) check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
@@ -373,6 +374,9 @@ tests/test-io-channel-socket$(EXESUF): tests/test-io-channel-socket.o \ tests/io-channel-helpers.o $(test-io-obj-y) tests/test-io-channel-file$(EXESUF): tests/test-io-channel-file.o \ tests/io-channel-helpers.o $(test-io-obj-y) +tests/test-io-channel-tls$(EXESUF): tests/test-io-channel-tls.o \ + tests/crypto-tls-x509-helpers.o tests/pkix_asn1_tab.o \ + tests/io-channel-helpers.o $(test-io-obj-y) libqos-obj-y = tests/libqos/pci.o tests/libqos/fw_cfg.o tests/libqos/malloc.o libqos-obj-y += tests/libqos/i2c.o tests/libqos/libqos.o
tests/test-io-channel-tls.c new file +335 -0
335 lines not on this page · raw message
trace-events +10 -0
@@ -1717,3 +1717,13 @@ qio_channel_socket_accept_complete(void *ioc, void *cioc, int fd) "Socket accept # io/channel-file.c qio_channel_file_new_fd(void *ioc, int fd) "File new fd ioc=%p fd=%d" qio_channel_file_new_path(void *ioc, const char *path, int flags, int mode, int fd) "File new fd ioc=%p path=%s flags=%d mode=%d fd=%d" + +# io/channel-tls.c +qio_channel_tls_new_client(void *ioc, void *master, void *creds, const char *hostname) "TLS new client ioc=%p master=%p creds=%p hostname=%s" +qio_channel_tls_new_server(void *ioc, void *master, void *creds, const char *aclname) "TLS new client ioc=%p master=%p creds=%p acltname=%s" +qio_channel_tls_handshake_start(void *ioc) "TLS handshake start ioc=%p" +qio_channel_tls_handshake_pending(void *ioc, int status) "TLS handshake pending ioc=%p status=%d" +qio_channel_tls_handshake_fail(void *ioc) "TLS handshake fail ioc=%p" +qio_channel_tls_handshake_complete(void *ioc) "TLS handshake complete ioc=%p" +qio_channel_tls_credentials_allow(void *ioc) "TLS credentials allow ioc=%p" +qio_channel_tls_credentials_deny(void *ioc) "TLS credentials deny ioc=%p"
Signature · 1 line
2.4.3
PATCH 14/16 io: add QIOChannelWebsock class
In reply to Daniel P. BerrangeAdd a QIOChannel subclass that can run the websocket protocol over the top of another QIOChannel instance. This initial implementation is only capable of acting as a websockets server. There is no support for acting as a websockets client yet.
--- include/io/channel-websock.h | 108 +++++ io/Makefile.objs | 1 + io/channel-websock.c | 948 +++++++++++++++++++++++++++++++++++++++++++ trace-events | 8 + 4 files changed, 1065 insertions(+) create mode 100644 include/io/channel-websock.h create mode 100644 io/channel-websock.c
include/io/channel-websock.h new file +108 -0
@@ -0,0 +1,108 @@ +/* +* QEMU I/O channels driver websockets +* +* Copyright (c) 2015 Red Hat, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, see <http://www.gnu.org/licenses/>. +* +*/ + +#ifndef QIO_CHANNEL_WEBSOCK_H__ +#define QIO_CHANNEL_WEBSOCK_H__ + +#include "io/channel.h" +#include "io/buffer.h" +#include "io/task.h" + +#define TYPE_QIO_CHANNEL_WEBSOCK "qio-channel-websock" +#define QIO_CHANNEL_WEBSOCK (obj) \ + OBJECT_CHECK(QIOChannelWebsock, (obj), TYPE_QIO_CHANNEL_WEBSOCK) + +typedef struct QIOChannelWebsock QIOChannelWebsock ; +typedef union QIOChannelWebsockMask QIOChannelWebsockMask ; + +union QIOChannelWebsockMask { +char c[4 ]; +uint32_t u; +}; + +/** +* QIOChannelWebsock +* +* The QIOChannelWebsock class provides a channel wrapper which +* can transparently run the HTTP websockets protocol. This is +* usually used over a TCP socket, but there is actually no +* technical restriction on which type of master channel is +* used as the transport. +* +* This channel object is currently only capable of running as +* a websocket server and is a pretty crude implementation +* of it, not supporting the full websockets protocol feature +* set. It is sufficient to use with a simple websockets +* client for encapsulating VNC for noVNC in-browser client. +*/ + +struct QIOChannelWebsock { +QIOChannel parent; +QIOChannel *master; +QIOBuffer encinput; +QIOBuffer encoutput; +QIOBuffer rawinput; +QIOBuffer rawoutput; +size_t payload_remain; +QIOChannelWebsockMask mask; +guint io_tag; +Error *io_err; +gboolean io_eof; +}; + +/** +* qio_channel_websock_new_server: +* @master: the underlying channel object +* +* Create a new websockets channel that runs the server +* side of the protocol. +* +* After creating the channel, it is mandatory to call +* the qio_channel_websock_handshake() method before attempting +* todo any I/O on the channel. +* +* Once the handshake has completed, all I/O should be done +* via the new websocket channel object and not the original +* master channel +* +* Returns: the new websockets channel object +*/ +QIOChannelWebsock * +qio_channel_websock_new_server (QIOChannel *master); + +/** +* qio_channel_websock_handshake: +* @ioc: the websocket channel object +* @func: the callback to invoke when completed +* @opaque: opaque data to pass to @func +* @destroy: optional callback to free @opaque +* +* Perform the websocket handshake. This method +* will return immediately and the handshake will +* continue in the background, provided the main +* loop is running. When the handshake is complete, +* or fails, the @func callback will be invoked. +*/ +void qio_channel_websock_handshake (QIOChannelWebsock *ioc, +QIOTaskFunc func, +gpointer opaque, +GDestroyNotify destroy); + +#endif /* QIO_CHANNEL_WEBSOCK_H__ */
io/Makefile.objs +1 -0
@@ -5,3 +5,4 @@ io-obj-y += channel-watch.o io-obj-y += channel-socket.o io-obj-y += channel-file.o io-obj-y += channel-tls.o +io-obj-y += channel-websock.o
io/channel-websock.c new file +948 -0
948 lines not on this page · raw message
trace-events +8 -0
@@ -1727,3 +1727,11 @@ qio_channel_tls_handshake_fail(void *ioc) "TLS handshake fail ioc=%p" qio_channel_tls_handshake_complete(void *ioc) "TLS handshake complete ioc=%p" qio_channel_tls_credentials_allow(void *ioc) "TLS credentials allow ioc=%p" qio_channel_tls_credentials_deny(void *ioc) "TLS credentials deny ioc=%p" + +# io/channel-websock.c +qio_channel_websock_new_server(void *ioc, void *master) "Websock new client ioc=%p master=%p" +qio_channel_websock_handshake_start(void *ioc) "Websock handshake start ioc=%p" +qio_channel_websock_handshake_pending(void *ioc, int status) "Websock handshake pending ioc=%p status=%d" +qio_channel_websock_handshake_reply(void *ioc) "Websock handshake reply ioc=%p" +qio_channel_websock_handshake_fail(void *ioc) "Websock handshake fail ioc=%p" +qio_channel_websock_handshake_complete(void *ioc) "Websock handshake complete ioc=%p"
Signature · 1 line
2.4.3
PATCH 15/16 io: add QIOChannelCommand class
In reply to Daniel P. BerrangeAdd a QIOChannel subclass that is capable of performing I/O to/from a separate process, via a pair of pipes. The command can be used for unidirectional or bi-directional I/O.
--- include/io/channel-command.h | 91 ++++++++++ io/Makefile.objs | 1 + io/channel-command.c | 369 ++++++++++++++++++++++++++++++++++++++++ tests/.gitignore | 2 + tests/Makefile | 3 + tests/test-io-channel-command.c | 122 +++++++++++++ trace-events | 6 + 7 files changed, 594 insertions(+) create mode 100644 include/io/channel-command.h create mode 100644 io/channel-command.c create mode 100644 tests/test-io-channel-command.c
include/io/channel-command.h new file +91 -0
@@ -0,0 +1,91 @@ +/* +* QEMU I/O channels external command driver +* +* Copyright (c) 2015 Red Hat, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, see <http://www.gnu.org/licenses/>. +* +*/ + +#ifndef QIO_CHANNEL_COMMAND_H__ +#define QIO_CHANNEL_COMMAND_H__ + +#include "io/channel.h" + +#define TYPE_QIO_CHANNEL_COMMAND "qio-channel-command" +#define QIO_CHANNEL_COMMAND (obj) \ + OBJECT_CHECK(QIOChannelCommand, (obj), TYPE_QIO_CHANNEL_COMMAND) + +typedef struct QIOChannelCommand QIOChannelCommand ; + + +/** +* QIOChannelCommand: +* +* The QIOChannelCommand class provides a channel implementation +* that can transport data with an externally running command +* via its stdio streams. +*/ + +struct QIOChannelCommand { +QIOChannel parent; +int writefd; +int readfd; +pid_t pid; +}; + + +/** +* qio_channel_command_new_pid: +* @writefd: the FD connected to the command's stdin +* @readfd: the FD connected to the command's stdout +* @pid: the PID of the running child command +* @errp: pointer to an uninitialized error object +* +* Create a channel for performing I/O with the +* previously spawned command identified by @pid. +* The two file descriptors provide the connection +* to command's stdio streams, either one or which +* may be -1 to indicate that stream is not open. +* +* The channel will take ownership of the process +* @pid and will kill it when closing the channel. +* Similarly it will take responsibility for +* closing the file descriptors @writefd and @readfd. +* +* Returns: the command channel object, or NULL on error +*/ +QIOChannelCommand * +qio_channel_command_new_pid (int writefd, +int readfd, +pid_t pid); + +/** +* qio_channel_command_new_spawn: +* @argv: the NULL terminated list of command arguments +* @flags: the I/O mode, one of O_RDONLY, O_WRONLY, O_RDWR +* @errp: pointer to an uninitialized error object +* +* Create a channel for performing I/O with the +* command to be spawned with arguments @argv. +* +* Returns: the command channel object, or NULL on error +*/ +QIOChannelCommand * +qio_channel_command_new_spawn (const char *const argv[], +int flags, +Error **errp); + + +#endif /* QIO_CHANNEL_COMMAND_H__ */
io/Makefile.objs +1 -0
@@ -6,3 +6,4 @@ io-obj-y += channel-socket.o io-obj-y += channel-file.o io-obj-y += channel-tls.o io-obj-y += channel-websock.o +io-obj-y += channel-command.o
io/channel-command.c new file +369 -0
369 lines not on this page · raw message
tests/.gitignore +2 -0
@@ -23,6 +23,8 @@ test-cutils test-hbitmap test-int128 test-iov +test-io-channel-command +test-io-channel-command.fifo test-io-channel-file test-io-channel-file.txt test-io-channel-socket
tests/Makefile +3 -0
@@ -84,6 +84,7 @@ check-unit-y += tests/test-io-task$(EXESUF) check-unit-y += tests/test-io-channel-socket$(EXESUF) check-unit-y += tests/test-io-channel-file$(EXESUF) check-unit-$(CONFIG_GNUTLS) += tests/test-io-channel-tls$(EXESUF) +check-unit-y += tests/test-io-channel-command$(EXESUF) check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
@@ -377,6 +378,8 @@ tests/test-io-channel-file$(EXESUF): tests/test-io-channel-file.o \ tests/test-io-channel-tls$(EXESUF): tests/test-io-channel-tls.o \ tests/crypto-tls-x509-helpers.o tests/pkix_asn1_tab.o \ tests/io-channel-helpers.o $(test-io-obj-y) +tests/test-io-channel-command$(EXESUF): tests/test-io-channel-command.o \ + tests/io-channel-helpers.o $(test-io-obj-y) libqos-obj-y = tests/libqos/pci.o tests/libqos/fw_cfg.o tests/libqos/malloc.o libqos-obj-y += tests/libqos/i2c.o tests/libqos/libqos.o
tests/test-io-channel-command.c new file +122 -0
122 lines not on this page · raw message
trace-events +6 -0
@@ -1735,3 +1735,9 @@ qio_channel_websock_handshake_pending(void *ioc, int status) "Websock handshake qio_channel_websock_handshake_reply(void *ioc) "Websock handshake reply ioc=%p" qio_channel_websock_handshake_fail(void *ioc) "Websock handshake fail ioc=%p" qio_channel_websock_handshake_complete(void *ioc) "Websock handshake complete ioc=%p" + +# io/channel-command.c +qio_channel_command_new_pid(void *ioc, int writefd, int readfd, int pid) "Command new pid ioc=%p writefd=%d readfd=%d pid=%d" +qio_channel_command_new_spawn(void *ioc, const char *binary, int flags) "Command new spawn ioc=%p binary=%s flags=%d" +qio_channel_command_abort(void *ioc, int pid) "Command abort ioc=%p pid=%d" +qio_channel_command_wait(void *ioc, int pid, int ret, int status) "Command abort ioc=%p pid=%d ret=%d status=%d"
Signature · 1 line
2.4.3
PATCH 16/16 io: add QIOChannelBuffer class
In reply to Daniel P. BerrangeAdd a QIOChannel subclass that is capable of performing I/O to/from a memory buffer. This implementation does not attempt to support concurrent readers & writers. It is designed for serialized access where by a single thread at a time may write data, seek and then read data back out.
--- include/io/channel-buffer.h | 59 ++++++++++ io/Makefile.objs | 1 + io/channel-buffer.c | 255 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 315 insertions(+) create mode 100644 include/io/channel-buffer.h create mode 100644 io/channel-buffer.c
include/io/channel-buffer.h new file +59 -0
@@ -0,0 +1,59 @@ +/* +* QEMU I/O channels memory buffer driver +* +* Copyright (c) 2015 Red Hat, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, see <http://www.gnu.org/licenses/>. +* +*/ + +#ifndef QIO_CHANNEL_BUFFER_H__ +#define QIO_CHANNEL_BUFFER_H__ + +#include "io/channel.h" + +#define TYPE_QIO_CHANNEL_BUFFER "qio-channel-buffer" +#define QIO_CHANNEL_BUFFER (obj) \ + OBJECT_CHECK(QIOChannelBuffer, (obj), TYPE_QIO_CHANNEL_BUFFER) + +typedef struct QIOChannelBuffer QIOChannelBuffer ; + +/** +* QIOChannelBuffer: +* +* The QIOChannelBuffer object provides a channel implementation +* that is able to perform I/O to/from a memory buffer. +* +*/ + +struct QIOChannelBuffer { +QIOChannel parent; +size_t capacity;/* Total allocated memory */ +size_t usage;/* Current size of data */ +size_t offset;/* Offset for future I/O ops */ +char *data; +}; + + +/** +* qio_channel_buffer_new: +* +* Allocate a new buffer which is initially empty +* +* Returns: the new channel object +*/ +QIOChannelBuffer * +qio_channel_buffer_new (void ); + +#endif /* QIO_CHANNEL_BUFFER_H__ */
io/Makefile.objs +1 -0
@@ -7,3 +7,4 @@ io-obj-y += channel-file.o io-obj-y += channel-tls.o io-obj-y += channel-websock.o io-obj-y += channel-command.o +io-obj-y += channel-buffer.o
io/channel-buffer.c new file +255 -0
255 lines not on this page · raw message
Signature · 1 line
2.4.3
Re: [Qemu-devel] [PATCH v1 16/16] io: add QIOChannelBuffer class
io/channel-buffer.c line 60
Quoted 5 lines · Daniel P. Berrange
io/channel-buffer.c new file +5
-0+if (fds || nfds) { +error_setg_errno (errp,EINVAL , +"Channel does not support buffer descriptor passing" ); +return -1 ; + }
Typo (file descriptor passing) and the check could be hoisted to io/channel.c by using features.
In fact capabilities could perhaps be stored statically in struct QIOChannel as a bitmap?
Paolo
Re: [Qemu-devel] [PATCH v1 16/16] io: add QIOChannelBuffer class
Quoted 9 lines · Paolo Bonzini
io/channel-buffer.c line 60
Quoted 5 lines · Daniel P. Berrange
io/channel-buffer.c new file +5
-0+if (fds || nfds) { +error_setg_errno (errp,EINVAL , +"Channel does not support buffer descriptor passing" ); +return -1 ; + }
Quoted 5 lines
Typo (file descriptor passing) and the check could be hoisted to
io/channel.cby using features.In fact capabilities could perhaps be stored statically in struct QIOChannel as a bitmap?
In general capabilities are static per class, but the socket code is an exception, because whether you can do FD passing is a runtime check based on the type of socket you opened. I might still be able to hoist that up into the base class though, so will investigate..
Regards,
Daniel
Signature · 4 lines
|: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
Quoted 7 lines · Daniel P. Berrange
David Gilbert had some feedback on my previous RFC series which I have incorporated into this new posting. I did forget his comments about using #defines instead of magic constants in the websock patch, so that's still on my todo list.
Finally I've listed myself as maintainer for the new io/ and
include/io/directory prefixes.
I only made a few very small remarks. The API is reasonable and you've proved its utility by using it in the rest of your project.
I remember I had some remarks about the rest of the conversion and especially the integration with coroutines, but it's not part of this series.
Paolo
Quoted 8 lines · Paolo Bonzini
Quoted 5 lines · Daniel P. Berrange
David Gilbert had some feedback on my previous RFC series which I have incorporated into this new posting. I did forget his comments about using #defines instead of magic constants in the websock patch, so that's still on my todo list.
Quoted 5 lines
Quoted 2 lines
Finally I've listed myself as maintainer for the new io/ and
include/io/directory prefixes.I only made a few very small remarks. The API is reasonable and you've proved its utility by using it in the rest of your project.
Thanks, I'd still love one or more people to do a detailed review of the I/O channel implementations before I think about sending a PULL request, since there's a fair amount of code in there....
Quoted 3 lines
I remember I had some remarks about the rest of the conversion and especially the integration with coroutines, but it's not part of this series.
I'm curious now as to which bit you might be refering to here :-) IIRC the only place I had any interaction with coroutines was the migration code QEMUFile implementation, where I need to yield when reads block.
Regards,
Daniel
Signature · 4 lines
|: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
Quoted 3 lines · Daniel P. Berrange
Thanks, I'd still love one or more people to do a detailed review of the I/O channel implementations before I think about sending a PULL request, since there's a fair amount of code in there....
I did a review of the file, socket, command and buffer code. I made another small suggestion regarding error checking.
The command code could add support for shutdown by closing the parent's end of the pipe. It can be useful to avoid deadlocks in bidirectional pipes.
Quoted 3 lines
I'm curious now as to which bit you might be refering to here :-) IIRC the only place I had any interaction with coroutines was the migration code QEMUFile implementation, where I need to yield when reads block.
I think it was NBD.
Paolo
Quoted 7 lines · Paolo Bonzini
Quoted 3 lines · Daniel P. Berrange
Thanks, I'd still love one or more people to do a detailed review of the I/O channel implementations before I think about sending a PULL request, since there's a fair amount of code in there....
Quoted 6 lines
I did a review of the file, socket, command and buffer code. I made another small suggestion regarding error checking.
The command code could add support for shutdown by closing the parent's end of the pipe. It can be useful to avoid deadlocks in bidirectional pipes.
Hmm, yes, good idea.
Quoted 5 lines
Quoted 3 lines
I'm curious now as to which bit you might be refering to here :-) IIRC the only place I had any interaction with coroutines was the migration code QEMUFile implementation, where I need to yield when reads block.
I think it was NBD.
Ah ok. I did start looking at NBD but got scared by all the AIO context handling & event loop integration, which is somewhat different from just being able to register with gmainloop directly. So that's still something I need to explore....
Regards,
Daniel
Signature · 4 lines
|: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|