{
BDRVNBDState *s = opaque;
uint64_t i;
+ int ret;
if (s->reply.handle == 0) {
- /* No reply already in flight. Fetch a header. */
- if (nbd_receive_reply(s->sock, &s->reply) < 0) {
+ /* No reply already in flight. Fetch a header. It is possible
+ * that another thread has done the same thing in parallel, so
+ * the socket is not readable anymore.
+ */
+ ret = nbd_receive_reply(s->sock, &s->reply);
+ if (ret == -EAGAIN) {
+ return;
+ }
+ if (ret < 0) {
s->reply.handle = 0;
goto fail;
}
/* That's all folks */
-#define read_sync(fd, buffer, size) nbd_wr_sync(fd, buffer, size, true)
-#define write_sync(fd, buffer, size) nbd_wr_sync(fd, buffer, size, false)
-
ssize_t nbd_wr_sync(int fd, void *buffer, size_t size, bool do_read)
{
size_t offset = 0;
err = socket_error();
/* recoverable error */
- if (err == EINTR || err == EAGAIN) {
+ if (err == EINTR || (offset > 0 && err == EAGAIN)) {
continue;
}
return offset;
}
+static ssize_t read_sync(int fd, void *buffer, size_t size)
+{
+ /* Sockets are kept in blocking mode in the negotiation phase. After
+ * that, a non-readable socket simply means that another thread stole
+ * our request/reply. Synchronization is done with recv_coroutine, so
+ * that this is coroutine-safe.
+ */
+ return nbd_wr_sync(fd, buffer, size, true);
+}
+
+static ssize_t write_sync(int fd, void *buffer, size_t size)
+{
+ int ret;
+ do {
+ /* For writes, we do expect the socket to be writable. */
+ ret = nbd_wr_sync(fd, buffer, size, false);
+ } while (ret == -EAGAIN);
+ return ret;
+}
+
static void combine_addr(char *buf, size_t len, const char* address,
uint16_t port)
{
[28 .. 151] reserved (0)
*/
+ socket_set_block(csock);
rc = -EINVAL;
TRACE("Beginning negotiation.");
TRACE("Negotiation succeeded.");
rc = 0;
fail:
+ socket_set_nonblock(csock);
return rc;
}
TRACE("Receiving negotiation.");
+ socket_set_block(csock);
rc = -EINVAL;
if (read_sync(csock, buf, 8) != 8) {
rc = 0;
fail:
+ socket_set_nonblock(csock);
return rc;
}
ssize_t rc;
client->recv_coroutine = qemu_coroutine_self();
- if (nbd_receive_request(csock, request) < 0) {
- rc = -EIO;
+ rc = nbd_receive_request(csock, request);
+ if (rc < 0) {
+ if (rc != -EAGAIN) {
+ rc = -EIO;
+ }
goto out;
}
TRACE("Reading request.");
ret = nbd_co_receive_request(req, &request);
+ if (ret == -EAGAIN) {
+ goto done;
+ }
if (ret == -EIO) {
goto out;
}
TRACE("Request/Reply complete");
+done:
nbd_request_put(req);
return;