diff options
author | Amit Shah <amit.shah@redhat.com> | 2009-10-12 15:19:44 +0530 |
---|---|---|
committer | Amit Shah <amit.shah@redhat.com> | 2009-10-12 15:19:44 +0530 |
commit | 26e37aecb8de22dfdc679b3cfdb984c8d3c784e4 (patch) | |
tree | 61af0e2b05e997c83fcd904c0f3d1b5d3fdd4f6e /test-virtserial.c | |
download | test-virtserial-26e37aecb8de22dfdc679b3cfdb984c8d3c784e4.tar.gz test-virtserial-26e37aecb8de22dfdc679b3cfdb984c8d3c784e4.tar.xz test-virtserial-26e37aecb8de22dfdc679b3cfdb984c8d3c784e4.zip |
test-virtserial: A set of tests to check generic virtio serial ports
The old virtio-console is now capable of handling generic serial ports
as well. This code base targets at testing the various paths in the
virtio-console (or virtio-serial) codebase, in the kernel as well
as in qemu (kernel in the guest, qemu on the host).
This test is an interactive session.
An automated test will follow.
Signed-off-by: Amit Shah <amit.shah@redhat.com>
Diffstat (limited to 'test-virtserial.c')
-rw-r--r-- | test-virtserial.c | 325 |
1 files changed, 325 insertions, 0 deletions
diff --git a/test-virtserial.c b/test-virtserial.c new file mode 100644 index 0000000..29167e9 --- /dev/null +++ b/test-virtserial.c @@ -0,0 +1,325 @@ +/* + * test-virtserial: Exercise various options for virtio-serial ports + * + * This program itself isn't resistant to user errors so handle with care! + * + * Some information on the virtio serial ports is available at + * http://www.linux-kvm.org/page/VMchannel_Requirements + * https://fedoraproject.org/wiki/Features/VirtioSerial + * + * Copyright (C) 2009, Red Hat, Inc. + * + * Author(s): + * Amit Shah <amit.shah@redhat.com> + * + * Licensed under the GNU General Public License v2. See the file COPYING + * for more details. + */ + +/* + * Invocation on the host: + * $ qemu ... -device virtio-serial-pci \ + * -chardev socket,host=localhost,port=1234,id=1234 \ + * -chardev socket,host=localhost,port=1235,id=1235 \ + * -device virtserialport,chardev=1234,name=port1234 \ + * -device virtserialport,chardev=1235,name=port1235,cache_buffers=0 + * + * Verify on the guest: + * $ ls /dev/vcon* + * 'name' properties of ports: + * $ cat /sys/class/virtio-console/ * /name <without spaces> + * + * + * Playing around: + * On the host: + * Writing to the guest via 'nc': + * $ nc localhost 1234 (uses stdin) + * $ nc localhost 1234 < /path/to/some/file + * Reading from the guest: + * $ nc localhost 1234 (uses stdout) + * $ nc localhost 1234 > /path/to/some/file + * + * Running this program in the guest: + * $ ./test-virtserial /dev/vcon1 + * + * + * This program can be used to test quite a few paths in the guest as + * well as the host implementations. Each of the tests mentioned below + * is a standalone test and can be executed independently of any other + * test. + * + * 1. Open + * + * a. Without connecting the host chardev (nc), run this program on the + * guest with a port, eg, + * $ ./test-virtserial /dev/vcon1 + * opening should succeed. Any other action should instantly return + * to the prompt as the host is not connected. + * + * b. Connect the host chardev, eg, + * $ nc localhost 1234 + * opening the port in the guest should succeed. + * + * + * 2. Poll + * + * a. Without connecting the host chardev, run the program and + * select (p)oll. It should return instantly. + * + * b. Connect the host chardev, run the program and select + * (p)oll. It should wait indefinitely till some input is given + * on the host. + * + * c. An asterisk (*) is shown next to read or write if the port can + * be read from or written to without blocking. Try this by + * writing something to the host chardev. + * + * + * 3. Read + * + * a. Select (r)ead without having entered anything from the + * host. It should wait till it receives input. When something is + * entered on the host, it should be displayed here. It should + * exit back at the prompt only when the host closes down the + * connection (EOF). + * + * b. Select non(b)locking. Then select (r)ead. If there's nothing + * to be read, return back to the prompt. If there is something, + * display it and go back to the prompt. + * + * c. Caching / not caching of buffers: initialise a port on the + * host with the ,cache_buffers=0 option (default is + * enabled). Open the port on the guest, open the chardev on the + * host. Write something to the port from the host. On the guest, + * select (o)pen. (r)ead should be marked with a * indicating + * data is available to be read. (c)lose the port. (o)pen it + * again. There should be no * against (r)ead. + * + * Repeat the same with a port started with the default value of + * cache_buffers=1. After re-opening the port on the guest, the + * data should still be available for (r)eading. + * + * d. Guest Throttling: Pass on ,guest_throttle=1048600 (or any + * value greater than 1MB) to a serial port on its qemu command + * line. This should be higher than 1MB, that's the minimum + * number of bytes allowed. + * + * For a port without throttling: (o)pen the port in the guest, + * from the host, pass on a file > 1MB, eg via: + * $ nc localhost 1234 < /path/to/big/file + * The nc process should return as soon as the contents are + * transferred over to the guest. + * + * Now repeat the same process for a port with throttling enabled + * as described before. The nc process will not return + * immediately but wait for the guest to read some bytes and as + * room becomes available in the guest, the remaining data is + * sent out. + * + * The memory consumption in the guest can also be observed: for + * a port with no throttling, the guest memory consumption will + * increase as data is pumped in. For a port with throttling + * enabled, the amount of free memory will stabilise once the + * limit is reached (assuming there's nothing else happening in + * the guest). + * + * e. Host Throttling: Similar to the guest throttling test above, + * test by pumping a big file from the guest to the host. For a + * port with throttling on, the guest process should wait till + * the host consumes the data. For a port with throttling off, + * the guest process should finish the write request as soon as + * all the data is transferred. + * + * + * 4. Write + * + * a. For a port that has caching enabled (,cache_buffers=1), writes + * to the port when host is disconnected should be available when + * the host chardev is connected. + * + * b. For a port that has caching disabled (,cache_buffers=0), + * writes to the port when host is disconnected should not be + * available when the host connects. + */ + +#include <errno.h> +#include <error.h> +#include <fcntl.h> +#include <poll.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/types.h> + +#ifdef DEBUG +#define debug(fmt, ...) \ + do { \ + printf(fmt, __VA_ARGS__); \ + } while(0) +#else +#define debug(fmt, ...) do { } while (0) +#endif + +/* Vars used for poll() status */ +bool can_read; +bool can_write; + +/* Vars used for file permissions */ +bool read_ok; +bool write_ok; +bool is_blocking = true; + +int read_all(int fd) +{ + int ret; + char buf[4097]; + + while ((ret = read(fd, buf, sizeof(buf) - 1)) > 0) { + buf[ret] = 0; + printf("%s", buf); + } + return ret; +} + +int write_line(int fd) +{ + char *buf; + size_t size; + int ret; + + printf("write> "); + buf = NULL; + ret = getline(&buf, &size, stdin); + if (ret == -1) + return ret; + ret = write(fd, buf, size); + free(buf); + return ret; +} + +int present_menu(char *name) +{ + struct pollfd fds[1]; + long flags; + int fd, c, ret, timeout; + bool closed = true; + +start: + while (closed) { + printf("(o)pen port, (q)uit\n> "); + c = getchar(); + getchar(); /* Forget the 'Enter' */ + if (c == 'o') { + int mode; + + mode = O_RDWR; + if (!write_ok) + mode = O_RDONLY; + fd = open(name, mode); + if (fd == -1) + error(errno, errno, "open %s", name); + closed = false; + } else if (c == 'q') { + return 0; + } + } + /* File is opened. */ + fds[0].fd = fd; + fds[0].events = POLLIN|POLLOUT; + + /* By default, make poll only query for current status */ + timeout = 0; + while (1) { + can_read = can_write = false; + + /* Check if port is open to read/write */ + ret = poll(fds, 1, timeout); + timeout = 0; + debug("poll ret %d, events %u revents %u\n", + ret, fds[0].events, fds[0].revents); + if (ret == -1) { + close(fd); + error(errno, errno, "poll %s\n", name); + } + if (fds[0].revents & POLLIN) + can_read = true; + if (fds[0].revents & POLLOUT) + can_write = true; + printf("%c(r)ead, %c(w)rite, %s(b)locking, (c)lose, (p)oll, (q)uit\n> ", + can_read ? '*' : ' ', can_write ? '*' : ' ', + is_blocking ? "non" : ""); + c = getchar(); + getchar(); /* Forget the 'Enter' */ + switch (c) { + case 'p': + timeout = -1; + continue; + break; + case 'q': + goto out; + break; + case 'c': + closed = true; + goto out; + break; + case 'b': + flags = O_NONBLOCK; + if (!is_blocking) + flags = ~O_NONBLOCK; + ret = fcntl(fd, F_SETFL, flags); + if (!ret) + is_blocking = !is_blocking; + else + perror("ERR fcntl"); + break; + case 'r': + ret = read_all(fd); + if (ret == -1) + perror("ERR: read"); + break; + case 'w': + ret = write_line(fd); + if (ret == -1) + perror("ERR: write"); + break; + } + } +out: + close(fd); + if (closed) + goto start; + return 0; +} + +int main(int argc, char *argv[]) +{ + int ret; + + if (argc != 2) { + errno = EINVAL; + printf("Usage: %s /path/to/virtioserial/port\n", argv[0]); + error(errno, errno, NULL); + } + ret = access(argv[1], F_OK); + if (ret) { + error(errno, errno, "%s", argv[1]); + } + ret = access(argv[1], R_OK); + if (!ret) + read_ok = true; + ret = access(argv[1], W_OK); + if (!ret) + write_ok = true; + ret = access(argv[1], X_OK); + if (!ret) + fprintf(stderr, "WARN: Port %s executable\n", argv[1]); + + if (!read_ok) + error(EACCES, EACCES, "%s", argv[1]); + + present_menu(argv[1]); + + return 0; +} |