Previously, our 'cmds_to_issue' list was at most 1 element long,
because we reject all commands except from state READY, but don't get
back to state READY until the issue-commands sequence has completed.
However, this is not as friendly on the client - once we are in
transmission phase, a client may want to queue up another command
whether or not the state machine is still tied up in processing a
previous command. We still want to reject commands sent before the
first time we reach READY, as well as keep the cmd_issue event for
kicking the state machine into action when there is no previous
command being worked on, but otherwise, the state machine itself can
recognize when the command queue needs draining.
---
generator/states.c | 5 +++++
lib/rw.c | 32 +++++++++++++++++++++++++++-----
2 files changed, 32 insertions(+), 5 deletions(-)
diff --git a/generator/states.c b/generator/states.c
index 202c305..22ce853 100644
--- a/generator/states.c
+++ b/generator/states.c
@@ -110,6 +110,11 @@ send_from_wbuf (struct nbd_connection *conn)
/*----- End of prologue. -----*/
/* STATE MACHINE */ {
+ READY:
+ if (conn->cmds_to_issue)
+ SET_NEXT_STATE (%ISSUE_COMMAND.START);
+ return 0;
+
DEAD:
if (conn->sock) {
conn->sock->ops->close (conn->sock);
diff --git a/lib/rw.c b/lib/rw.c
index 8f6227d..594593a 100644
--- a/lib/rw.c
+++ b/lib/rw.c
@@ -24,6 +24,7 @@
#include <stdint.h>
#include <inttypes.h>
#include <errno.h>
+#include <assert.h>
#include "internal.h"
@@ -247,7 +248,15 @@ nbd_internal_command_common (struct nbd_connection *conn,
uint64_t offset, uint64_t count, void *data,
int64_t id, extent_fn extent)
{
- struct command_in_flight *cmd;
+ struct command_in_flight *cmd, *prev_cmd;
+
+ if (!nbd_unlocked_aio_is_ready (conn) &&
+ !nbd_unlocked_aio_is_processing (conn)) {
+ set_error (0, "command request %s is invalid in state %s",
+ nbd_internal_name_of_nbd_cmd (type),
+ nbd_internal_state_short_string (conn->state));
+ return -1;
+ }
switch (type) {
/* Commands which send or receive data are limited to MAX_REQUEST_SIZE. */
@@ -299,10 +308,23 @@ nbd_internal_command_common (struct nbd_connection *conn,
if (conn->structured_replies && cmd->data && type ==
NBD_CMD_READ)
memset (cmd->data, 0, cmd->count);
- cmd->next = conn->cmds_to_issue;
- conn->cmds_to_issue = cmd;
- if (nbd_internal_run (conn->h, conn, cmd_issue) == -1)
- return -1;
+ /* Add the command to the end of the queue. Kick the state machine
+ * if there is no other command being processed, otherwise, it will
+ * be handled automatically on a future cycle around to READY.
+ */
+ if (conn->cmds_to_issue != NULL) {
+ assert (nbd_unlocked_aio_is_processing (conn));
+ prev_cmd = conn->cmds_to_issue;
+ while (prev_cmd->next)
+ prev_cmd = prev_cmd->next;
+ prev_cmd->next = cmd;
+ }
+ else {
+ conn->cmds_to_issue = cmd;
+ if (nbd_unlocked_aio_is_ready (conn) &&
+ nbd_internal_run (conn->h, conn, cmd_issue) == -1)
+ return -1;
+ }
return cmd->handle;
}
--
2.20.1