+NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset,
+ off_t size, uint32_t nbdflags,
+ void (*close)(NBDExport *))
+{
+ NBDExport *exp = g_malloc0(sizeof(NBDExport));
+ QSIMPLEQ_INIT(&exp->requests);
+ exp->refcount = 1;
+ QTAILQ_INIT(&exp->clients);
+ exp->bs = bs;
+ exp->dev_offset = dev_offset;
+ exp->nbdflags = nbdflags;
+ exp->size = size == -1 ? bdrv_getlength(bs) : size;
+ exp->close = close;
+ return exp;
+}
+
+NBDExport *nbd_export_find(const char *name)
+{
+ NBDExport *exp;
+ QTAILQ_FOREACH(exp, &exports, next) {
+ if (strcmp(name, exp->name) == 0) {
+ return exp;
+ }
+ }
+
+ return NULL;
+}
+
+void nbd_export_set_name(NBDExport *exp, const char *name)
+{
+ if (exp->name == name) {
+ return;
+ }
+
+ nbd_export_get(exp);
+ if (exp->name != NULL) {
+ g_free(exp->name);
+ exp->name = NULL;
+ QTAILQ_REMOVE(&exports, exp, next);
+ nbd_export_put(exp);
+ }
+ if (name != NULL) {
+ nbd_export_get(exp);
+ exp->name = g_strdup(name);
+ QTAILQ_INSERT_TAIL(&exports, exp, next);
+ }
+ nbd_export_put(exp);
+}
+
+void nbd_export_close(NBDExport *exp)
+{
+ NBDClient *client, *next;
+
+ nbd_export_get(exp);
+ QTAILQ_FOREACH_SAFE(client, &exp->clients, next, next) {
+ nbd_client_close(client);
+ }
+ nbd_export_set_name(exp, NULL);
+ nbd_export_put(exp);
+}
+
+void nbd_export_get(NBDExport *exp)
+{
+ assert(exp->refcount > 0);
+ exp->refcount++;
+}
+
+void nbd_export_put(NBDExport *exp)
+{
+ assert(exp->refcount > 0);
+ if (exp->refcount == 1) {
+ nbd_export_close(exp);
+ }
+
+ if (--exp->refcount == 0) {
+ assert(exp->name == NULL);
+
+ if (exp->close) {
+ exp->close(exp);
+ }
+
+ while (!QSIMPLEQ_EMPTY(&exp->requests)) {
+ NBDRequest *first = QSIMPLEQ_FIRST(&exp->requests);
+ QSIMPLEQ_REMOVE_HEAD(&exp->requests, entry);
+ qemu_vfree(first->data);
+ g_free(first);
+ }
+
+ g_free(exp);
+ }
+}
+
+BlockDriverState *nbd_export_get_blockdev(NBDExport *exp)
+{
+ return exp->bs;
+}
+
+void nbd_export_close_all(void)
+{
+ NBDExport *exp, *next;
+
+ QTAILQ_FOREACH_SAFE(exp, &exports, next, next) {
+ nbd_export_close(exp);
+ }
+}
+
+static int nbd_can_read(void *opaque);
+static void nbd_read(void *opaque);
+static void nbd_restart_write(void *opaque);
+
+static ssize_t nbd_co_send_reply(NBDRequest *req, struct nbd_reply *reply,
+ int len)
+{
+ NBDClient *client = req->client;
+ int csock = client->sock;
+ ssize_t rc, ret;
+
+ qemu_co_mutex_lock(&client->send_lock);
+ qemu_set_fd_handler2(csock, nbd_can_read, nbd_read,
+ nbd_restart_write, client);
+ client->send_coroutine = qemu_coroutine_self();
+
+ if (!len) {
+ rc = nbd_send_reply(csock, reply);
+ } else {
+ socket_set_cork(csock, 1);
+ rc = nbd_send_reply(csock, reply);
+ if (rc >= 0) {
+ ret = qemu_co_send(csock, req->data, len);
+ if (ret != len) {
+ rc = -EIO;
+ }
+ }
+ socket_set_cork(csock, 0);
+ }
+
+ client->send_coroutine = NULL;
+ qemu_set_fd_handler2(csock, nbd_can_read, nbd_read, NULL, client);
+ qemu_co_mutex_unlock(&client->send_lock);
+ return rc;
+}
+
+static ssize_t nbd_co_receive_request(NBDRequest *req, struct nbd_request *request)
+{
+ NBDClient *client = req->client;
+ int csock = client->sock;
+ ssize_t rc;
+
+ client->recv_coroutine = qemu_coroutine_self();
+ rc = nbd_receive_request(csock, request);
+ if (rc < 0) {
+ if (rc != -EAGAIN) {
+ rc = -EIO;
+ }
+ goto out;
+ }
+
+ if (request->len > NBD_BUFFER_SIZE) {