[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH 08 of 21] blktap3/drivers: Introduce handling of control commands
This patch copies the functionality of handling control commands from blktap2, with changes coming from blktap2.5. Also, it contains the following blktap3-related changes: * Replaced the minor number with type:/path/to/file or /path/to/file, depending on the occasion. * Removed VBD.attach/detach message handlers. And the following optimisations/clean-up: * Removed the unused uuid member from struct tapdisk_control. * Simplified message handling: all message handler functions accept the response as an argument and the caller of these functions initialises this response and handles errors. Signed-off-by: Thanos Makatos <thanos.makatos@xxxxxxxxxx> diff --git a/tools/blktap2/drivers/tapdisk-control.c b/tools/blktap3/drivers/tapdisk-control.c copy from tools/blktap2/drivers/tapdisk-control.c copy to tools/blktap3/drivers/tapdisk-control.c --- a/tools/blktap2/drivers/tapdisk-control.c +++ b/tools/blktap3/drivers/tapdisk-control.c @@ -25,6 +25,7 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + #include <stdio.h> #include <errno.h> #include <fcntl.h> @@ -37,42 +38,350 @@ #include <sys/types.h> #include <sys/ioctl.h> #include <sys/socket.h> +#include <sys/mman.h> +#include <sys/select.h> -#include "list.h" #include "tapdisk.h" -#include "blktap2.h" -#include "blktaplib.h" #include "tapdisk-vbd.h" #include "tapdisk-utils.h" #include "tapdisk-server.h" #include "tapdisk-message.h" #include "tapdisk-disktype.h" +#include "tapdisk-stats.h" +#include "tapdisk-control.h" +#include "sring/td-blkif.h" + +#define TD_CTL_MAX_CONNECTIONS 10 +#define TD_CTL_SOCK_BACKLOG 32 +#define TD_CTL_RECV_TIMEOUT 10 +#define TD_CTL_SEND_TIMEOUT 10 +#define TD_CTL_SEND_BUFSZ ((size_t)4096) + +#define DBG(_f, _a...) tlog_syslog(LOG_DEBUG, "%s:%d " _f, \ + __FILE__, __LINE__, ##_a) +#define ERR(err, _f, _a...) tlog_error(err, "%s:%d " _f, __FILE__, \ + __LINE__, ##_a) + +#define ASSERT(_p) \ + if (!(_p)) { \ + EPRINTF("%s:%d: FAILED ASSERTION: '%s'\n", \ + __FILE__, __LINE__, #_p); \ + td_panic(); \ + } + +#define WARN_ON(_p) \ + if (_p) { \ + EPRINTF("%s:%d: WARNING: '%s'\n", \ + __FILE__, __LINE__, #_p); \ + } + +struct tapdisk_ctl_conn { + int fd; + + struct { + void *buf; + size_t bufsz; + int event_id; + int done; + + void *prod; + void *cons; + } out; + + struct { + int event_id; + int busy; + } in; + + struct tapdisk_control_info *info; +}; + +#define TAPDISK_MSG_REENTER (1<<0) /* non-blocking, idempotent */ +#define TAPDISK_MSG_VERBOSE (1<<1) /* tell syslog about it */ +#define TAPDISK_MSG_VERBOSE_ERROR (1<<2) /* tell syslog about it, with errors */ + +struct tapdisk_control_info { + int (*handler) (struct tapdisk_ctl_conn *, tapdisk_message_t *, + tapdisk_message_t * const); + int flags; +}; struct tapdisk_control { char *path; int socket; int event_id; -}; + int busy; -struct tapdisk_control_connection { - int socket; - event_id_t event_id; + int n_conn; + struct tapdisk_ctl_conn __conn[TD_CTL_MAX_CONNECTIONS]; + struct tapdisk_ctl_conn *conn[TD_CTL_MAX_CONNECTIONS]; }; static struct tapdisk_control td_control; +static inline size_t page_align(size_t size) +{ + size_t page_size = sysconf(_SC_PAGE_SIZE); + return (size + page_size - 1) & ~(page_size - 1); +} + +static void tapdisk_ctl_conn_uninit(struct tapdisk_ctl_conn *conn) +{ + if (conn->out.buf) { + munmap(conn->out.buf, conn->out.bufsz); + conn->out.buf = NULL; + } +} + +static int +tapdisk_ctl_conn_init(struct tapdisk_ctl_conn *conn, size_t bufsz) +{ + int prot, flags, err; + + memset(conn, 0, sizeof(*conn)); + conn->out.event_id = -1; + conn->in.event_id = -1; + + prot = PROT_READ | PROT_WRITE; + flags = MAP_ANONYMOUS | MAP_PRIVATE; + + conn->out.buf = mmap(NULL, bufsz, prot, flags, -1, 0); + if (conn->out.buf == MAP_FAILED) { + conn->out.buf = NULL; + err = -ENOMEM; + goto fail; + } + conn->out.bufsz = page_align(bufsz); + + return 0; + + fail: + tapdisk_ctl_conn_uninit(conn); + return err; +} + +static int tapdisk_ctl_conn_connected(struct tapdisk_ctl_conn *conn) +{ + return conn->fd >= 1; +} + +static void tapdisk_ctl_conn_free(struct tapdisk_ctl_conn *conn) +{ + struct tapdisk_ctl_conn *prev, *next; + int i; + + i = --td_control.n_conn; + /* NB. bubble the freed connection off the active list. */ + prev = conn; + do { + ASSERT(i >= 0); + next = td_control.conn[i]; + td_control.conn[i] = prev; + prev = next; + i--; + } while (next != conn); +} + +static void tapdisk_ctl_conn_close(struct tapdisk_ctl_conn *conn) +{ + if (conn->out.event_id >= 0) { + tapdisk_server_unregister_event(conn->out.event_id); + conn->out.event_id = -1; + } + + if (conn->fd >= 0) { + close(conn->fd); + conn->fd = -1; + + tapdisk_ctl_conn_free(conn); + tapdisk_server_mask_event(td_control.event_id, 0); + } +} + +static void tapdisk_ctl_conn_mask_out(struct tapdisk_ctl_conn *conn) +{ + tapdisk_server_mask_event(conn->out.event_id, 1); +} + +static void tapdisk_ctl_conn_unmask_out(struct tapdisk_ctl_conn *conn) +{ + tapdisk_server_mask_event(conn->out.event_id, 0); +} + +static ssize_t tapdisk_ctl_conn_send_buf(struct tapdisk_ctl_conn *conn) +{ + ssize_t size; + + size = conn->out.prod - conn->out.cons; + if (!size) + return 0; + + size = send(conn->fd, conn->out.cons, size, MSG_DONTWAIT); + if (size < 0) + return -errno; + + conn->out.cons += size; + + return size; +} + static void -tapdisk_control_initialize(void) +tapdisk_ctl_conn_send_event(event_id_t id __attribute__((unused)), char mode, + void *private) { + struct tapdisk_ctl_conn *conn = private; + ssize_t rv; + + do { + rv = tapdisk_ctl_conn_send_buf(conn); + } while (rv > 0); + + if (rv == -EAGAIN) + return; + + if (rv < 0) + ERR(rv, "failure sending message at offset %td/%td\n", + conn->out.cons - conn->out.buf, + conn->out.prod - conn->out.buf); + + if (rv || conn->out.done || mode & SCHEDULER_POLL_TIMEOUT) + tapdisk_ctl_conn_close(conn); + else + tapdisk_ctl_conn_mask_out(conn); +} + +/* + * NB. the control interface is still not properly integrated into the + * server, therefore neither the scheduler. After the last close, the + * server will exit but we still have a pending close response in the + * output buffer. + */ +static void tapdisk_ctl_conn_drain(struct tapdisk_ctl_conn *conn) +{ + struct timeval tv = {.tv_sec = TD_CTL_SEND_TIMEOUT, + .tv_usec = 0 + }; + fd_set wfds; + int n, mode; + + ASSERT(conn->out.done); + ASSERT(conn->fd >= 0); + + while (tapdisk_ctl_conn_connected(conn)) { + FD_ZERO(&wfds); + FD_SET(conn->fd, &wfds); + + n = select(conn->fd + 1, NULL, &wfds, NULL, &tv); + if (n < 0) + break; + + if (n) + mode = SCHEDULER_POLL_WRITE_FD; + else + mode = SCHEDULER_POLL_TIMEOUT; + + tapdisk_ctl_conn_send_event(conn->out.event_id, mode, conn); + } +} + + +struct tapdisk_ctl_conn *tapdisk_ctl_conn_open(int fd) +{ + struct tapdisk_ctl_conn *conn; + + if (td_control.n_conn >= TD_CTL_MAX_CONNECTIONS) + return NULL; + + conn = td_control.conn[td_control.n_conn++]; + + conn->out.event_id = + tapdisk_server_register_event(SCHEDULER_POLL_WRITE_FD, + fd, TD_CTL_SEND_TIMEOUT, + tapdisk_ctl_conn_send_event, conn); + if (conn->out.event_id < 0) + return NULL; + + conn->fd = fd; + conn->out.prod = conn->out.buf; + conn->out.cons = conn->out.buf; + + tapdisk_ctl_conn_mask_out(conn); + + if (td_control.n_conn >= TD_CTL_MAX_CONNECTIONS) + tapdisk_server_mask_event(td_control.event_id, 1); + + return conn; +} + +static size_t +tapdisk_ctl_conn_write(struct tapdisk_ctl_conn *conn, void *buf, + size_t size) +{ + size_t rest; + + rest = conn->out.buf + conn->out.bufsz - conn->out.prod; + if (rest < size) + size = rest; + if (!size) + return 0; + + memcpy(conn->out.prod, buf, size); + conn->out.prod += size; + tapdisk_ctl_conn_unmask_out(conn); + + return size; +} + +static void tapdisk_ctl_conn_release(struct tapdisk_ctl_conn *conn) +{ + conn->out.done = 1; + + if (conn->out.prod == conn->out.cons) + tapdisk_ctl_conn_close(conn); +} + +static void tapdisk_control_initialize(void) +{ + struct tapdisk_ctl_conn *conn; + int i; + td_control.socket = -1; td_control.event_id = -1; signal(SIGPIPE, SIG_IGN); + + for (i = 0; i < TD_CTL_MAX_CONNECTIONS; i++) { + conn = &td_control.__conn[i]; + tapdisk_ctl_conn_init(conn, TD_CTL_SEND_BUFSZ); + td_control.conn[i] = conn; } -void -tapdisk_control_close(void) + td_control.n_conn = 0; + + DPRINTF("tapdisk-control: init, %d x %zuk buffers\n", + TD_CTL_MAX_CONNECTIONS, TD_CTL_SEND_BUFSZ >> 10); +} + +void tapdisk_control_close(void) { + struct tapdisk_ctl_conn *conn; + int i; + + DPRINTF("tapdisk-control: draining %d connections\n", + td_control.n_conn); + + while (td_control.n_conn) { + conn = td_control.conn[td_control.n_conn - 1]; + tapdisk_ctl_conn_drain(conn); + } + + for (i = 0; i < TD_CTL_MAX_CONNECTIONS; i++) { + conn = &td_control.__conn[i]; + tapdisk_ctl_conn_uninit(conn); + } + + DPRINTF("tapdisk-control: done\n"); + if (td_control.path) { unlink(td_control.path); free(td_control.path); @@ -85,40 +394,41 @@ tapdisk_control_close(void) } } -static struct tapdisk_control_connection * -tapdisk_control_allocate_connection(int fd) +static void +tapdisk_control_release_connection(struct tapdisk_ctl_conn *conn) { - struct tapdisk_control_connection *connection; - size_t sz; - - connection = calloc(1, sizeof(*connection)); - if (!connection) { - EPRINTF("calloc"); - return NULL; + if (conn->in.event_id) { + tapdisk_server_unregister_event(conn->in.event_id); + conn->in.event_id = -1; } - connection->socket = fd; - return connection; + tapdisk_ctl_conn_release(conn); } static void -tapdisk_control_close_connection(struct tapdisk_control_connection *connection) +tapdisk_control_close_connection(struct tapdisk_ctl_conn *conn) { - tapdisk_server_unregister_event(connection->event_id); - close(connection->socket); - free(connection); + tapdisk_control_release_connection(conn); + + if (tapdisk_ctl_conn_connected(conn)) + /* NB. best effort for write/close sequences. */ + tapdisk_ctl_conn_send_buf(conn); + + tapdisk_ctl_conn_close(conn); } + static int -tapdisk_control_read_message(int fd, tapdisk_message_t *message, int timeout) +tapdisk_control_read_message(int fd, tapdisk_message_t * message, + int timeout) { + const int len = sizeof(tapdisk_message_t); fd_set readfds; - int ret, len, offset; + int ret, offset, err = 0; struct timeval tv, *t; t = NULL; offset = 0; - len = sizeof(tapdisk_message_t); if (timeout) { tv.tv_sec = timeout; @@ -144,67 +454,31 @@ tapdisk_control_read_message(int fd, tap break; } - if (offset != len) { - EPRINTF("failure reading message (wanted %d but got %d)\n", - len, offset); - return -EIO; - } + if (ret < 0) + err = -errno; + else if (offset != len) + err = -EIO; + if (err) + ERR(err, "failure reading message at offset %d/%d\n", offset, len); - DPRINTF("received '%s' message (uuid = %u)\n", - tapdisk_message_name(message->type), message->cookie); - return 0; + return err; } -static int -tapdisk_control_write_message(int fd, tapdisk_message_t *message, int timeout) +static void +tapdisk_control_write_message(struct tapdisk_ctl_conn *conn, + tapdisk_message_t * message) { - fd_set writefds; - int ret, len, offset; - struct timeval tv, *t; + size_t size = sizeof(*message), count; - t = NULL; - offset = 0; - len = sizeof(tapdisk_message_t); + if (conn->info->flags & TAPDISK_MSG_VERBOSE) + DBG("sending '%s' message\n", tapdisk_message_name(message->type)); - if (timeout) { - tv.tv_sec = timeout; - tv.tv_usec = 0; - t = &tv; - } - - DPRINTF("sending '%s' message (uuid = %u)\n", - tapdisk_message_name(message->type), message->cookie); - - while (offset < len) { - FD_ZERO(&writefds); - FD_SET(fd, &writefds); - - /* we don't bother reinitializing tv. at worst, it will wait a - * bit more time than expected. */ - - ret = select(fd + 1, NULL, &writefds, NULL, t); - if (ret == -1) - break; - else if (FD_ISSET(fd, &writefds)) { - ret = write(fd, message + offset, len - offset); - if (ret <= 0) - break; - offset += ret; - } else - break; - } - - if (offset != len) { - EPRINTF("failure writing message\n"); - return -EIO; - } - - return 0; + count = tapdisk_ctl_conn_write(conn, message, size); + WARN_ON(count != size); } -static int -tapdisk_control_validate_request(tapdisk_message_t *request) +static int tapdisk_control_validate_request(tapdisk_message_t * request) { if (strnlen(request->u.params.path, TAPDISK_MESSAGE_MAX_PATH_LENGTH) >= @@ -214,8 +488,10 @@ tapdisk_control_validate_request(tapdisk return 0; } +/* XXX Commented out in blktap2.5. */ +#if 0 static void -tapdisk_control_list_minors(struct tapdisk_control_connection *connection, +tapdisk_control_list_minors(struct tapdisk_ctl_conn *conn, tapdisk_message_t *request) { int i; @@ -232,7 +508,11 @@ tapdisk_control_list_minors(struct tapdi head = tapdisk_server_get_all_vbds(); list_for_each_entry(vbd, head, next) { - response.u.minors.list[i++] = vbd->minor; + td_blktap_t *tap = vbd->tap; + if (!tap) + continue; + + response.u.minors.list[i++] = tap->minor; if (i >= TAPDISK_MESSAGE_MAX_MINORS) { response.type = TAPDISK_MESSAGE_ERROR; response.u.response.error = ERANGE; @@ -241,194 +521,101 @@ tapdisk_control_list_minors(struct tapdi } response.u.minors.count = i; - tapdisk_control_write_message(connection->socket, &response, 2); - tapdisk_control_close_connection(connection); + tapdisk_ctl_conn_write(conn, &response, 2); } +#endif -static void -tapdisk_control_list(struct tapdisk_control_connection *connection, - tapdisk_message_t *request) +static int +tapdisk_control_list(struct tapdisk_ctl_conn *conn, + tapdisk_message_t *request, tapdisk_message_t * const response) { td_vbd_t *vbd; - struct list_head *head; - tapdisk_message_t response; - int count, i; + struct tqh_td_vbd_handle *head; + int count; - memset(&response, 0, sizeof(response)); - response.type = TAPDISK_MESSAGE_LIST_RSP; - response.cookie = request->cookie; + assert(conn); + assert(request); + assert(response); + + response->type = TAPDISK_MESSAGE_LIST_RSP; head = tapdisk_server_get_all_vbds(); + /* + * Count all the VBDs. + * TODO avoid this by maintaining this number? + */ count = 0; - list_for_each_entry(vbd, head, next) + TAILQ_FOREACH(vbd, head, entry) count++; - list_for_each_entry(vbd, head, next) { - response.u.list.count = count--; - response.u.list.minor = vbd->minor; - response.u.list.state = vbd->state; - response.u.list.path[0] = 0; + TAILQ_FOREACH(vbd, head, entry) { + response->u.list.count = count--; + response->u.list.state = vbd->state; + response->u.list.path[0] = 0; - if (!list_empty(&vbd->images)) { - td_image_t *image = list_entry(vbd->images.next, - td_image_t, next); - snprintf(response.u.list.path, - sizeof(response.u.list.path), - "%s:%s", - tapdisk_disk_types[image->type]->name, - image->name); - } + if (vbd->name) + strncpy(response->u.list.path, vbd->name, + sizeof(response->u.list.path)); - tapdisk_control_write_message(connection->socket, &response, 2); + tapdisk_control_write_message(conn, response); } - response.u.list.count = count; - response.u.list.minor = -1; - response.u.list.path[0] = 0; + response->u.list.count = count; + response->u.list.path[0] = 0; - tapdisk_control_write_message(connection->socket, &response, 2); - tapdisk_control_close_connection(connection); + return 0; } -static void -tapdisk_control_get_pid(struct tapdisk_control_connection *connection, - tapdisk_message_t *request) +static int +tapdisk_control_get_pid(struct tapdisk_ctl_conn *conn __attribute__((unused)), + tapdisk_message_t *request __attribute__((unused)), + tapdisk_message_t * const response) { - tapdisk_message_t response; + assert(response); - memset(&response, 0, sizeof(response)); - response.type = TAPDISK_MESSAGE_PID_RSP; - response.cookie = request->cookie; - response.u.tapdisk_pid = getpid(); + response->type = TAPDISK_MESSAGE_PID_RSP; + response->u.tapdisk_pid = getpid(); - tapdisk_control_write_message(connection->socket, &response, 2); - tapdisk_control_close_connection(connection); + return 0; } -static void -tapdisk_control_attach_vbd(struct tapdisk_control_connection *connection, - tapdisk_message_t *request) +static int +tapdisk_control_open_image( + struct tapdisk_ctl_conn *conn __attribute__((unused)), + tapdisk_message_t *request, tapdisk_message_t * const response) { - tapdisk_message_t response; - char *devname; + int err; td_vbd_t *vbd; - struct blktap2_params params; - image_t image; - int minor, err; + td_flag_t flags; + td_disk_info_t info; + int prt_path_len; + char * prt_path; - /* - * TODO: check for max vbds per process - */ + assert(request); + assert(response); - vbd = tapdisk_server_get_vbd(request->cookie); - if (vbd) { - err = -EEXIST; - goto out; - } + /* TODO Check whether the image is already open by another VBD? */ + prt_path_len = strnlen(request->u.params.prt_path, + TAPDISK_MESSAGE_MAX_PATH_LENGTH); + if (unlikely(prt_path_len == TAPDISK_MESSAGE_MAX_PATH_LENGTH)) { + err = -EINVAL; + goto out; + } else if (prt_path_len == 0) + prt_path = NULL; + else + prt_path = request->u.params.prt_path; - minor = request->cookie; - if (minor < 0) { - err = -EINVAL; - goto out; - } - - vbd = tapdisk_vbd_create(minor); + vbd = tapdisk_vbd_create(); if (!vbd) { err = -ENOMEM; goto out; } - err = asprintf(&devname, BLKTAP2_RING_DEVICE"%d", minor); - if (err == -1) { - err = -ENOMEM; - goto fail_vbd; - } - - err = tapdisk_vbd_attach(vbd, devname, minor); - free(devname); - if (err) - goto fail_vbd; - + /* TODO Add after everything has been initialised? */ tapdisk_server_add_vbd(vbd); -out: - memset(&response, 0, sizeof(response)); - response.type = TAPDISK_MESSAGE_ATTACH_RSP; - response.cookie = request->cookie; - response.u.response.error = -err; - - tapdisk_control_write_message(connection->socket, &response, 2); - tapdisk_control_close_connection(connection); - - return; - -fail_vbd: - tapdisk_vbd_detach(vbd); - free(vbd); - goto out; -} - - -static void -tapdisk_control_detach_vbd(struct tapdisk_control_connection *connection, - tapdisk_message_t *request) -{ - tapdisk_message_t response; - td_vbd_t *vbd; - int err; - - vbd = tapdisk_server_get_vbd(request->cookie); - if (!vbd) { - err = -EINVAL; - goto out; - } - - tapdisk_vbd_detach(vbd); - - if (list_empty(&vbd->images)) { - tapdisk_server_remove_vbd(vbd); - free(vbd); - } - - err = 0; -out: - memset(&response, 0, sizeof(response)); - response.type = TAPDISK_MESSAGE_DETACH_RSP; - response.cookie = request->cookie; - response.u.response.error = -err; - - tapdisk_control_write_message(connection->socket, &response, 2); - tapdisk_control_close_connection(connection); -} - -static void -tapdisk_control_open_image(struct tapdisk_control_connection *connection, - tapdisk_message_t *request) -{ - int err; - image_t image; - td_vbd_t *vbd; - td_flag_t flags; - tapdisk_message_t response; - struct blktap2_params params; - - vbd = tapdisk_server_get_vbd(request->cookie); - if (!vbd) { - err = -EINVAL; - goto out; - } - - if (vbd->minor == -1) { - err = -EINVAL; - goto out; - } - - if (vbd->name) { - err = -EALREADY; - goto out; - } - + /* TODO check for unsupported flags */ flags = 0; if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_RDONLY) flags |= TD_OPEN_RDONLY; @@ -440,83 +627,92 @@ tapdisk_control_open_image(struct tapdis flags |= TD_OPEN_VHD_INDEX; if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_LOG_DIRTY) flags |= TD_OPEN_LOG_DIRTY; + if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_ADD_LCACHE) + flags |= TD_OPEN_LOCAL_CACHE; + if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_REUSE_PRT) + flags |= TD_OPEN_REUSE_PARENT; + if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_STANDBY) + flags |= TD_OPEN_STANDBY; + if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_SECONDARY) { + char *name = strdup(request->u.params.secondary); + if (!name) { + err = -errno; + goto out; + } + vbd->secondary_name = name; + flags |= TD_OPEN_SECONDARY; + } - vbd->name = strndup(request->u.params.path, - sizeof(request->u.params.path)); - if (!vbd->name) { - err = -ENOMEM; + err = tapdisk_vbd_open_vdi(vbd, request->u.params.path, flags, prt_path); + if (err) { + EPRINTF("failed to open VDI: %s\n", strerror(-err)); goto out; - } + } - err = tapdisk_vbd_parse_stack(vbd, request->u.params.path); - if (err) - goto out; - - err = tapdisk_vbd_open_stack(vbd, request->u.params.storage, flags); - if (err) - goto out; - - err = tapdisk_vbd_get_image_info(vbd, &image); - if (err) - goto fail_close; - - params.capacity = image.size; - params.sector_size = image.secsize; - strncpy(params.name, vbd->name, BLKTAP2_MAX_MESSAGE_LEN); - - err = ioctl(vbd->ring.fd, BLKTAP2_IOCTL_CREATE_DEVICE, ¶ms); - if (err && errno != EEXIST) { - err = -errno; - EPRINTF("create device failed: %d\n", err); + err = tapdisk_vbd_get_disk_info(vbd, &info); + if (err) { + EPRINTF("failed to get disk info: %s\n", strerror(-err)); goto fail_close; } err = 0; out: - memset(&response, 0, sizeof(response)); - response.cookie = request->cookie; + if (!err) { + response->u.image.sectors = info.size; + response->u.image.sector_size = info.sector_size; + response->u.image.info = info.info; + response->type = TAPDISK_MESSAGE_OPEN_RSP; + } - if (err) { - response.type = TAPDISK_MESSAGE_ERROR; - response.u.response.error = -err; - } else { - response.u.image.sectors = image.size; - response.u.image.sector_size = image.secsize; - response.u.image.info = image.info; - response.type = TAPDISK_MESSAGE_OPEN_RSP; - } - - tapdisk_control_write_message(connection->socket, &response, 2); - tapdisk_control_close_connection(connection); - - return; + return err; fail_close: tapdisk_vbd_close_vdi(vbd); - free(vbd->name); - vbd->name = NULL; + + if (vbd->name) { + free(vbd->name); + vbd->name = NULL; + } + goto out; } -static void -tapdisk_control_close_image(struct tapdisk_control_connection *connection, - tapdisk_message_t *request) +static int +tapdisk_control_close_image(struct tapdisk_ctl_conn *conn, + tapdisk_message_t *request, tapdisk_message_t * const response) { - tapdisk_message_t response; td_vbd_t *vbd; - int err; + int err = 0; + int len; - vbd = tapdisk_server_get_vbd(request->cookie); + assert(conn); + assert(request); + assert(response); + + len = strnlen(request->u.string.text, TAPDISK_MESSAGE_STRING_LENGTH); + if (len < 1) { + err = -EINVAL; + goto out; + } + if (len >= TAPDISK_MESSAGE_STRING_LENGTH) { + err = -ENAMETOOLONG; + goto out; + } + + vbd = tapdisk_server_get_vbd(request->u.string.text); if (!vbd) { - err = -EINVAL; + EPRINTF("no VBD \'%s\'", request->u.string.text); + err = -ENODEV; goto out; } - if (!list_empty(&vbd->pending_requests)) { - err = -EAGAIN; - goto out; - } + /* TODO How do we make sure that new requests won't enter the ring? + * I assume we have disconnected from the ring before? If yes, then + * make sure we check this. */ + + while (!TAILQ_EMPTY(&vbd->pending_requests)) + tapdisk_server_iterate(); tapdisk_vbd_close_vdi(vbd); @@ -526,35 +722,45 @@ tapdisk_control_close_image(struct tapdi free(vbd->name); vbd->name = NULL; - if (vbd->minor == -1) { - tapdisk_server_remove_vbd(vbd); - tapdisk_vbd_free(vbd); + tapdisk_server_remove_vbd(vbd); + +out: + if (!err) { + response->type = TAPDISK_MESSAGE_CLOSE_RSP; + response->u.response.error = -err; } - err = 0; -out: - memset(&response, 0, sizeof(response)); - response.type = TAPDISK_MESSAGE_CLOSE_RSP; - response.cookie = request->cookie; - response.u.response.error = -err; - - tapdisk_control_write_message(connection->socket, &response, 2); - tapdisk_control_close_connection(connection); + return err; } -static void -tapdisk_control_pause_vbd(struct tapdisk_control_connection *connection, - tapdisk_message_t *request) +static int +tapdisk_control_pause_vbd(struct tapdisk_ctl_conn *conn, + tapdisk_message_t *request, tapdisk_message_t * const response) { int err; td_vbd_t *vbd; - tapdisk_message_t response; + int len; - memset(&response, 0, sizeof(response)); + assert(conn); + assert(request); + assert(response); - response.type = TAPDISK_MESSAGE_PAUSE_RSP; + len = strnlen(request->u.string.text, TAPDISK_MESSAGE_STRING_LENGTH); - vbd = tapdisk_server_get_vbd(request->cookie); + /* TODO boilerplate */ + if (len < 1) { + err = -EINVAL; + goto out; + } + if (len >= TAPDISK_MESSAGE_STRING_LENGTH) { + err = -ENAMETOOLONG; + goto out; + } + + response->type = TAPDISK_MESSAGE_PAUSE_RSP; + + /* TODO Need to fix this in control/tap-ctl-pause.c */ + vbd = tapdisk_server_get_vbd(request->u.string.text); if (!vbd) { err = -EINVAL; goto out; @@ -567,155 +773,429 @@ tapdisk_control_pause_vbd(struct tapdisk break; tapdisk_server_iterate(); - } while (1); + + } while (conn->fd >= 0); out: - response.cookie = request->cookie; - response.u.response.error = -err; - tapdisk_control_write_message(connection->socket, &response, 2); - tapdisk_control_close_connection(connection); + if (!err) + /* TODO useless? */ + response->u.response.error = -err; + return err; } -static void -tapdisk_control_resume_vbd(struct tapdisk_control_connection *connection, - tapdisk_message_t *request) +static int +tapdisk_control_resume_vbd( + struct tapdisk_ctl_conn *conn __attribute__((unused)), + tapdisk_message_t *request, tapdisk_message_t * const response) { int err; td_vbd_t *vbd; - tapdisk_message_t response; + const char *desc = NULL; + int len; - memset(&response, 0, sizeof(response)); + assert(request); + assert(response); - response.type = TAPDISK_MESSAGE_RESUME_RSP; + len = strnlen(request->u.string.text, TAPDISK_MESSAGE_STRING_LENGTH); - vbd = tapdisk_server_get_vbd(request->cookie); + /* TODO boilerplate */ + if (len < 1) { + err = -EINVAL; + goto out; + } + if (len >= TAPDISK_MESSAGE_STRING_LENGTH) { + err = -ENAMETOOLONG; + goto out; + } + + response->type = TAPDISK_MESSAGE_RESUME_RSP; + + /* TODO Need to fix this in control/tap-ctl-pause.c */ + vbd = tapdisk_server_get_vbd(request->u.string.text); if (!vbd) { err = -EINVAL; goto out; } - if (!td_flag_test(vbd->state, TD_VBD_PAUSED)) { - err = -EINVAL; - goto out; + /* TODO What's this path? */ + if (request->u.params.path[0]) + desc = request->u.params.path; + + err = tapdisk_vbd_resume(vbd, desc); +out: + if (!err) + /* TODO useless? */ + response->u.response.error = -err; + return err; +} + +static int +tapdisk_control_stats(struct tapdisk_ctl_conn *conn __attribute__((unused)), + tapdisk_message_t * request, tapdisk_message_t * const response) +{ + td_stats_t _st, *st = &_st; + td_vbd_t *vbd; + size_t rv = 0; + int err = 0; + int len; + + assert(request); + assert(response); + + len = strnlen(request->u.string.text, TAPDISK_MESSAGE_STRING_LENGTH); + + tapdisk_stats_init(st, + conn->out.buf + sizeof(*response), + conn->out.bufsz - sizeof(*response)); + if (len > 1) { + if (len >= TAPDISK_MESSAGE_STRING_LENGTH) { + err = -ENAMETOOLONG; + goto out; + } + + vbd = tapdisk_server_get_vbd(request->u.string.text); + if (!vbd) { + err = -ENODEV; + goto out; + } + + tapdisk_vbd_stats(vbd, st); + + } else { + struct tqh_td_vbd_handle *list = tapdisk_server_get_all_vbds(); + + tapdisk_stats_enter(st, '['); + + TAILQ_FOREACH(vbd, list, entry) + tapdisk_vbd_stats(vbd, st); + + tapdisk_stats_leave(st, ']'); } - if (request->u.params.path[0]) { - free(vbd->name); - vbd->name = strndup(request->u.params.path, - sizeof(request->u.params.path)); - if (!vbd->name) { - err = -ENOMEM; - goto out; - } - } else if (!vbd->name) { - err = -EINVAL; + rv = tapdisk_stats_length(st); +out: + if (!err) { + response->type = TAPDISK_MESSAGE_STATS_RSP; + response->u.info.length = rv; + } + + /* TODO Should only be executed if err == 0? */ + if (rv > 0) + conn->out.prod += rv; + + return err; +} + +/** + * Message handler executed for TAPDISK_MESSAGE_XENBLKIF_CONNECT. + * + * This is the entry point for connecting the tapdisk to the shared ring. It + * also sets up the necessary structures/descriptors (TODO explain). + */ +static int +tapdisk_control_xenblkif_connect( + struct tapdisk_ctl_conn *conn __attribute__((unused)), + tapdisk_message_t *request, tapdisk_message_t * const response) +{ + /* + * Get the block interface parameters (domain ID, device ID, etc.). + */ + tapdisk_message_blkif_t *blkif; + + td_vbd_t *vbd; + const char *pool; + size_t len; + int err; + + assert(request); + assert(response); + + len = strnlen(request->u.string.text, TAPDISK_MESSAGE_STRING_LENGTH); + /* TODO boilerplate */ + if (len < 1) { + err = -EINVAL; + goto out; + } + if (len >= TAPDISK_MESSAGE_STRING_LENGTH) { + err = -ENAMETOOLONG; + goto out; + } + + vbd = tapdisk_server_get_vbd(request->u.blkif.params); + if (!vbd) { + err = -ENODEV; goto out; + } + + blkif = &request->u.blkif; + len = strnlen(blkif->pool, sizeof(blkif->pool)); + if (!len) + pool = NULL; + else if (len >= sizeof(blkif->pool)) { + err = -EINVAL; + goto out; + } else + pool = blkif->pool; + + DPRINTF("connecting VBD domid=%d, devid=%d, pool %s, evt %d\n", + blkif->domid, blkif->devid, pool, blkif->port); + + err = tapdisk_xenblkif_connect(blkif->domid, blkif->devid, blkif->gref, + blkif->order, blkif->port, blkif->proto, pool, vbd); +out: + if (!err) { + response->type = TAPDISK_MESSAGE_XENBLKIF_CONNECT_RSP; + /* TODO Useless? */ + response->u.response.error = -err; + } + return err; +} + +static int +tapdisk_control_xenblkif_disconnect( + struct tapdisk_ctl_conn *conn __attribute__((unused)), + tapdisk_message_t * request, tapdisk_message_t * const response) +{ + tapdisk_message_blkif_t *blkif; + int err; + + assert(request); + assert(response); + + blkif = &request->u.blkif; + + DPRINTF("disconnecting VBD domid=%d, devid=%d\n", blkif->domid, + blkif->devid); + + err = tapdisk_xenblkif_disconnect(blkif->domid, blkif->devid); + + if (!err) { + response->type = TAPDISK_MESSAGE_XENBLKIF_DISCONNECT_RSP; + /* TODO Useless? */ + response->u.response.error = -err; + } + return err; +} + +static int +tapdisk_control_disk_info( + struct tapdisk_ctl_conn *conn __attribute__((unused)), + tapdisk_message_t * request, tapdisk_message_t * const response) +{ + tapdisk_message_image_t *image; + int err; + td_vbd_t *vbd; + td_disk_info_t info; + int len; + + assert(request); + assert(response); + + image = &response->u.image; + len = strnlen(request->u.string.text, TAPDISK_MESSAGE_STRING_LENGTH); + + /* TODO boilerplate */ + if (len < 1) { + err = -EINVAL; + goto out; + } + if (len >= TAPDISK_MESSAGE_STRING_LENGTH) { + err = -ENAMETOOLONG; + goto out; + } + + DPRINTF("getting info vbd %s\n", request->u.string.text); + + vbd = tapdisk_server_get_vbd(request->u.string.text); + if (!vbd) { + err = -ENODEV; + goto out; } - err = tapdisk_vbd_parse_stack(vbd, vbd->name); - if (err) - goto out; + err = tapdisk_vbd_get_disk_info(vbd, &info); + if (err) { + EPRINTF("tapdisk_vbd_get_disk_info failed %d\n", err); + goto out; + } - err = tapdisk_vbd_resume(vbd, NULL, -1); - if (err) - goto out; - + EPRINTF("got disk info: %ld %d\n", info.sector_size, err); out: - response.cookie = request->cookie; - response.u.response.error = -err; - tapdisk_control_write_message(connection->socket, &response, 2); - tapdisk_control_close_connection(connection); + if (!err) { + response->type = TAPDISK_MESSAGE_DISK_INFO_RSP; + image->sectors = info.size; + image->sector_size = info.sector_size; + image->info = info.info; + } + return err; } -static void -tapdisk_control_handle_request(event_id_t id, char mode, void *private) +struct tapdisk_control_info message_infos[] = { + [TAPDISK_MESSAGE_PID] = { + .handler = tapdisk_control_get_pid, + .flags = TAPDISK_MSG_REENTER, + }, + [TAPDISK_MESSAGE_LIST] = { + .handler = tapdisk_control_list, + .flags = TAPDISK_MSG_REENTER, + }, + [TAPDISK_MESSAGE_OPEN] = { + .handler = tapdisk_control_open_image, + .flags = TAPDISK_MSG_VERBOSE, + }, + [TAPDISK_MESSAGE_PAUSE] = { + .handler = tapdisk_control_pause_vbd, + .flags = TAPDISK_MSG_VERBOSE, + }, + [TAPDISK_MESSAGE_RESUME] = { + .handler = tapdisk_control_resume_vbd, + .flags = TAPDISK_MSG_VERBOSE, + }, + [TAPDISK_MESSAGE_CLOSE] = { + .handler = tapdisk_control_close_image, + .flags = TAPDISK_MSG_VERBOSE, + }, + [TAPDISK_MESSAGE_STATS] = { + .handler = tapdisk_control_stats, + .flags = TAPDISK_MSG_REENTER, + }, + [TAPDISK_MESSAGE_XENBLKIF_CONNECT] = { + .handler = + tapdisk_control_xenblkif_connect, + .flags = + TAPDISK_MSG_VERBOSE | + TAPDISK_MSG_VERBOSE_ERROR, + }, + [TAPDISK_MESSAGE_XENBLKIF_DISCONNECT] = { + .handler = + tapdisk_control_xenblkif_disconnect, + .flags = TAPDISK_MSG_VERBOSE + || TAPDISK_MSG_VERBOSE_ERROR, + }, + [TAPDISK_MESSAGE_DISK_INFO] = { + .handler = tapdisk_control_disk_info, + .flags = + TAPDISK_MSG_VERBOSE | + TAPDISK_MSG_VERBOSE_ERROR, + }, +}; + + +static void tapdisk_control_handle_request( + event_id_t id __attribute__((unused)), + char mode __attribute__((unused)), void *private) { - int err; - tapdisk_message_t message; - struct tapdisk_control_connection *connection = - (struct tapdisk_control_connection *)private; + int err, excl; + tapdisk_message_t message, response; + struct tapdisk_ctl_conn *conn = private; + struct tapdisk_control_info *info; - if (tapdisk_control_read_message(connection->socket, &message, 2)) { - EPRINTF("failed to read message from %d\n", connection->socket); - tapdisk_control_close_connection(connection); + err = tapdisk_control_read_message(conn->fd, &message, 2); + if (err) + goto close; + + if (conn->in.busy) + goto busy; + + err = tapdisk_control_validate_request(&message); + if (err) + goto invalid; + + if (message.type > TAPDISK_MESSAGE_EXIT) + goto invalid; + + info = &message_infos[message.type]; + + if (!info->handler) + goto invalid; + + if (info->flags & TAPDISK_MSG_VERBOSE) + DBG("received '%s' message\n", + tapdisk_message_name(message.type)); + + excl = !(info->flags & TAPDISK_MSG_REENTER); + if (excl) { + if (td_control.busy) + goto busy; + + td_control.busy = 1; + } + conn->in.busy = 1; + conn->info = info; + + memset(&response, 0, sizeof(response)); + + err = info->handler(conn, &message, &response); + if (err) { + response.type = TAPDISK_MESSAGE_ERROR; + response.u.response.error = -err; + } + tapdisk_control_write_message(conn, &response); + + conn->in.busy = 0; + if (excl) + td_control.busy = 0; + + tapdisk_control_release_connection(conn); + return; + + error: + memset(&response, 0, sizeof(response)); + response.type = TAPDISK_MESSAGE_ERROR; + response.u.response.error = (err ? -err : EINVAL); + tapdisk_control_write_message(conn, &response); + + close: + tapdisk_control_close_connection(conn); + return; + + busy: + err = -EBUSY; + ERR(err, "rejecting message '%s' while busy\n", + tapdisk_message_name(message.type)); + goto error; + + invalid: + err = -EINVAL; + ERR(err, "rejecting unsupported message '%s'\n", + tapdisk_message_name(message.type)); + goto error; +} + +static void tapdisk_control_accept(event_id_t id __attribute__((unused)), + char mode __attribute__((unused)), + void *private __attribute__((unused))) +{ + int err, fd; + struct tapdisk_ctl_conn *conn; + + fd = accept(td_control.socket, NULL, NULL); + if (fd == -1) { + ERR(-errno, "failed to accept new control connection: %d\n", + errno); return; } - err = tapdisk_control_validate_request(&message); - if (err) - goto fail; - - switch (message.type) { - case TAPDISK_MESSAGE_PID: - return tapdisk_control_get_pid(connection, &message); - case TAPDISK_MESSAGE_LIST_MINORS: - return tapdisk_control_list_minors(connection, &message); - case TAPDISK_MESSAGE_LIST: - return tapdisk_control_list(connection, &message); - case TAPDISK_MESSAGE_ATTACH: - return tapdisk_control_attach_vbd(connection, &message); - case TAPDISK_MESSAGE_DETACH: - return tapdisk_control_detach_vbd(connection, &message); - case TAPDISK_MESSAGE_OPEN: - return tapdisk_control_open_image(connection, &message); - case TAPDISK_MESSAGE_PAUSE: - return tapdisk_control_pause_vbd(connection, &message); - case TAPDISK_MESSAGE_RESUME: - return tapdisk_control_resume_vbd(connection, &message); - case TAPDISK_MESSAGE_CLOSE: - return tapdisk_control_close_image(connection, &message); - default: { - tapdisk_message_t response; - fail: - - EPRINTF("received unsupported message '%s'\n", - tapdisk_message_name(message.type)); - - memset(&response, 0, sizeof(response)); - - response.type = TAPDISK_MESSAGE_ERROR; - response.u.response.error = (err ? -err : EINVAL); - tapdisk_control_write_message(connection->socket, &response, 2); - - tapdisk_control_close_connection(connection); - break; - } - } -} - -static void -tapdisk_control_accept(event_id_t id, char mode, void *private) -{ - int err, fd; - struct tapdisk_control_connection *connection; - - fd = accept(td_control.socket, NULL, NULL); - if (fd == -1) { - EPRINTF("failed to accept new control connection: %d\n", errno); - return; - } - - connection = tapdisk_control_allocate_connection(fd); - if (!connection) { + conn = tapdisk_ctl_conn_open(fd); + if (!conn) { close(fd); - EPRINTF("failed to allocate new control connection\n"); + ERR(-ENOMEM, "failed to allocate new control connection\n"); + return; } err = tapdisk_server_register_event(SCHEDULER_POLL_READ_FD, - connection->socket, 0, + conn->fd, TD_CTL_RECV_TIMEOUT, tapdisk_control_handle_request, - connection); + conn); if (err == -1) { - close(fd); - free(connection); - EPRINTF("failed to register new control event: %d\n", err); + tapdisk_control_close_connection(conn); + ERR(err, "failed to register new control event\n"); + return; } - connection->event_id = err; + conn->in.event_id = err; } -static int -tapdisk_control_mkdir(const char *dir) +static int tapdisk_control_mkdir(const char *dir) { int err; char *ptr, *name, *start; @@ -738,8 +1218,7 @@ tapdisk_control_mkdir(const char *dir) err = mkdir(name, 0755); if (err && errno != EEXIST) { err = -errno; - EPRINTF("failed to create directory %s: %d\n", - name, err); + EPRINTF("failed to create directory %s: %d\n", name, err); break; } @@ -755,21 +1234,20 @@ tapdisk_control_mkdir(const char *dir) return err; } -static int -tapdisk_control_create_socket(char **socket_path) +static int tapdisk_control_create_socket(char **socket_path) { - int err, flags; struct sockaddr_un saddr; + int err; - err = tapdisk_control_mkdir(BLKTAP2_CONTROL_DIR); + err = tapdisk_control_mkdir(BLKTAP3_CONTROL_DIR); if (err) { EPRINTF("failed to create directory %s: %d\n", - BLKTAP2_CONTROL_DIR, err); + BLKTAP3_CONTROL_DIR, err); return err; } err = asprintf(&td_control.path, "%s/%s%d", - BLKTAP2_CONTROL_DIR, BLKTAP2_CONTROL_SOCKET, getpid()); + BLKTAP3_CONTROL_DIR, BLKTAP3_CONTROL_SOCKET, getpid()); if (err == -1) { td_control.path = NULL; err = (errno ? : ENOMEM); @@ -801,7 +1279,7 @@ tapdisk_control_create_socket(char **soc goto fail; } - err = listen(td_control.socket, 10); + err = listen(td_control.socket, TD_CTL_SOCK_BACKLOG); if (err == -1) { err = errno; EPRINTF("failed to listen: %d\n", err); @@ -826,11 +1304,8 @@ fail: return err; } -int -tapdisk_control_open(char **path) +int tapdisk_control_open(char **path) { - int err; - tapdisk_control_initialize(); return tapdisk_control_create_socket(path); diff --git a/tools/blktap2/drivers/tapdisk-control.h b/tools/blktap3/drivers/tapdisk-control.h copy from tools/blktap2/drivers/tapdisk-control.h copy to tools/blktap3/drivers/tapdisk-control.h _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxx http://lists.xen.org/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |