9ae3a8
From a8b59fd3d7aa9f6a16caf4c13534039a81552118 Mon Sep 17 00:00:00 2001
9ae3a8
From: Richard Jones <rjones@redhat.com>
9ae3a8
Date: Thu, 11 Jun 2015 11:40:14 +0200
9ae3a8
Subject: [PATCH 14/30] block/curl: Implement the libcurl timer callback
9ae3a8
 interface
9ae3a8
9ae3a8
Message-id: <1434022828-13037-8-git-send-email-rjones@redhat.com>
9ae3a8
Patchwork-id: 65842
9ae3a8
O-Subject: [RHEL-7.2 qemu-kvm v3 PATCH 07/21] block/curl: Implement the libcurl timer callback interface
9ae3a8
Bugzilla: 1226684
9ae3a8
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
9ae3a8
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
9ae3a8
RH-Acked-by: Laszlo Ersek <lersek@redhat.com>
9ae3a8
9ae3a8
From: Peter Maydell <peter.maydell@linaro.org>
9ae3a8
9ae3a8
libcurl versions 7.16.0 and later have a timer callback interface which
9ae3a8
must be implemented in order for libcurl to make forward progress (it
9ae3a8
will sometimes rely on being called back on the timeout if there are
9ae3a8
no file descriptors registered). Implement the callback, and use a
9ae3a8
QEMU AIO timer to ensure we prod libcurl again when it asks us to.
9ae3a8
9ae3a8
Based on Peter's original patch plus my fix to add curl_multi_timeout_do.
9ae3a8
Should compile just fine even on older versions of libcurl.
9ae3a8
9ae3a8
I also tried copy-on-read and streaming:
9ae3a8
9ae3a8
    $ ./qemu-img create -f qcow2 -o \
9ae3a8
         backing_file=http://download.fedoraproject.org/pub/fedora/linux/releases/20/Live/x86_64/Fedora-Live-Desktop-x86_64-20-1.iso \
9ae3a8
         foo.qcow2 1G
9ae3a8
    $ x86_64-softmmu/qemu-system-x86_64 \
9ae3a8
         -drive if=none,file=foo.qcow2,copy-on-read=on,id=cd \
9ae3a8
         -device ide-cd,drive=cd --enable-kvm -m 1024
9ae3a8
9ae3a8
Direct http usage is probably too slow, but with copy-on-read ultimately
9ae3a8
the image does boot!
9ae3a8
9ae3a8
After some time, streaming gets canceled by an EIO, which needs further
9ae3a8
investigation.
9ae3a8
9ae3a8
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
9ae3a8
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
9ae3a8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9ae3a8
9ae3a8
Upstream-status: 031fd1be5618c347f9aeb44ec294f14a541e42b2
9ae3a8
9ae3a8
This patch is modified from upstream by adapting the patch
9ae3a8
to use the timers API from qemu 1.5.3.  (Thanks: Kevin Wolf)
9ae3a8
9ae3a8
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
9ae3a8
---
9ae3a8
 block/curl.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++---------
9ae3a8
 1 file changed, 70 insertions(+), 11 deletions(-)
9ae3a8
9ae3a8
diff --git a/block/curl.c b/block/curl.c
9ae3a8
index 7569dd5..a6631fe 100644
9ae3a8
--- a/block/curl.c
9ae3a8
+++ b/block/curl.c
9ae3a8
@@ -34,6 +34,11 @@
9ae3a8
 #define DPRINTF(fmt, ...) do { } while (0)
9ae3a8
 #endif
9ae3a8
 
9ae3a8
+#if LIBCURL_VERSION_NUM >= 0x071000
9ae3a8
+/* The multi interface timer callback was introduced in 7.16.0 */
9ae3a8
+#define NEED_CURL_TIMER_CALLBACK
9ae3a8
+#endif
9ae3a8
+
9ae3a8
 #define PROTOCOLS (CURLPROTO_HTTP | CURLPROTO_HTTPS | \
9ae3a8
                    CURLPROTO_FTP | CURLPROTO_FTPS | \
9ae3a8
                    CURLPROTO_TFTP)
9ae3a8
@@ -77,6 +82,7 @@ typedef struct CURLState
9ae3a8
 
9ae3a8
 typedef struct BDRVCURLState {
9ae3a8
     CURLM *multi;
9ae3a8
+    QEMUTimer *timer;
9ae3a8
     size_t len;
9ae3a8
     CURLState states[CURL_NUM_STATES];
9ae3a8
     char *url;
9ae3a8
@@ -87,6 +93,23 @@ static void curl_clean_state(CURLState *s);
9ae3a8
 static void curl_multi_do(void *arg);
9ae3a8
 static int curl_aio_flush(void *opaque);
9ae3a8
 
9ae3a8
+#ifdef NEED_CURL_TIMER_CALLBACK
9ae3a8
+static int curl_timer_cb(CURLM *multi, long timeout_ms, void *opaque)
9ae3a8
+{
9ae3a8
+    BDRVCURLState *s = opaque;
9ae3a8
+
9ae3a8
+    DPRINTF("CURL: timer callback timeout_ms %ld\n", timeout_ms);
9ae3a8
+    if (timeout_ms == -1) {
9ae3a8
+        qemu_del_timer(s->timer);
9ae3a8
+    } else {
9ae3a8
+        int64_t timeout_ns = (int64_t)timeout_ms * 1000 * 1000;
9ae3a8
+        qemu_mod_timer(s->timer,
9ae3a8
+                  qemu_get_clock_ns(rt_clock) + timeout_ns);
9ae3a8
+    }
9ae3a8
+    return 0;
9ae3a8
+}
9ae3a8
+#endif
9ae3a8
+
9ae3a8
 static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action,
9ae3a8
                         void *s, void *sp)
9ae3a8
 {
9ae3a8
@@ -213,20 +236,10 @@ static int curl_find_buf(BDRVCURLState *s, size_t start, size_t len,
9ae3a8
     return FIND_RET_NONE;
9ae3a8
 }
9ae3a8
 
9ae3a8
-static void curl_multi_do(void *arg)
9ae3a8
+static void curl_multi_read(BDRVCURLState *s)
9ae3a8
 {
9ae3a8
-    BDRVCURLState *s = (BDRVCURLState *)arg;
9ae3a8
-    int running;
9ae3a8
-    int r;
9ae3a8
     int msgs_in_queue;
9ae3a8
 
9ae3a8
-    if (!s->multi)
9ae3a8
-        return;
9ae3a8
-
9ae3a8
-    do {
9ae3a8
-        r = curl_multi_socket_all(s->multi, &running);
9ae3a8
-    } while(r == CURLM_CALL_MULTI_PERFORM);
9ae3a8
-
9ae3a8
     /* Try to find done transfers, so we can free the easy
9ae3a8
      * handle again. */
9ae3a8
     do {
9ae3a8
@@ -271,6 +284,41 @@ static void curl_multi_do(void *arg)
9ae3a8
     } while(msgs_in_queue);
9ae3a8
 }
9ae3a8
 
9ae3a8
+static void curl_multi_do(void *arg)
9ae3a8
+{
9ae3a8
+    BDRVCURLState *s = (BDRVCURLState *)arg;
9ae3a8
+    int running;
9ae3a8
+    int r;
9ae3a8
+
9ae3a8
+    if (!s->multi) {
9ae3a8
+        return;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    do {
9ae3a8
+        r = curl_multi_socket_all(s->multi, &running);
9ae3a8
+    } while(r == CURLM_CALL_MULTI_PERFORM);
9ae3a8
+
9ae3a8
+    curl_multi_read(s);
9ae3a8
+}
9ae3a8
+
9ae3a8
+static void curl_multi_timeout_do(void *arg)
9ae3a8
+{
9ae3a8
+#ifdef NEED_CURL_TIMER_CALLBACK
9ae3a8
+    BDRVCURLState *s = (BDRVCURLState *)arg;
9ae3a8
+    int running;
9ae3a8
+
9ae3a8
+    if (!s->multi) {
9ae3a8
+        return;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    curl_multi_socket_action(s->multi, CURL_SOCKET_TIMEOUT, 0, &running);
9ae3a8
+
9ae3a8
+    curl_multi_read(s);
9ae3a8
+#else
9ae3a8
+    abort();
9ae3a8
+#endif
9ae3a8
+}
9ae3a8
+
9ae3a8
 static CURLState *curl_init_state(BDRVCURLState *s)
9ae3a8
 {
9ae3a8
     CURLState *state = NULL;
9ae3a8
@@ -462,12 +510,19 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
9ae3a8
     curl_easy_cleanup(state->curl);
9ae3a8
     state->curl = NULL;
9ae3a8
 
9ae3a8
+    s->timer = qemu_new_timer(rt_clock, SCALE_NS,
9ae3a8
+                              curl_multi_timeout_do, s);
9ae3a8
+
9ae3a8
     // Now we know the file exists and its size, so let's
9ae3a8
     // initialize the multi interface!
9ae3a8
 
9ae3a8
     s->multi = curl_multi_init();
9ae3a8
     curl_multi_setopt(s->multi, CURLMOPT_SOCKETDATA, s);
9ae3a8
     curl_multi_setopt(s->multi, CURLMOPT_SOCKETFUNCTION, curl_sock_cb);
9ae3a8
+#ifdef NEED_CURL_TIMER_CALLBACK
9ae3a8
+    curl_multi_setopt(s->multi, CURLMOPT_TIMERDATA, s);
9ae3a8
+    curl_multi_setopt(s->multi, CURLMOPT_TIMERFUNCTION, curl_timer_cb);
9ae3a8
+#endif
9ae3a8
     curl_multi_do(s);
9ae3a8
 
9ae3a8
     qemu_opts_del(opts);
9ae3a8
@@ -607,6 +662,10 @@ static void curl_close(BlockDriverState *bs)
9ae3a8
     }
9ae3a8
     if (s->multi)
9ae3a8
         curl_multi_cleanup(s->multi);
9ae3a8
+
9ae3a8
+    qemu_del_timer(s->timer);
9ae3a8
+    qemu_free_timer(s->timer);
9ae3a8
+
9ae3a8
     g_free(s->url);
9ae3a8
 }
9ae3a8
 
9ae3a8
-- 
9ae3a8
1.8.3.1
9ae3a8