Blame SOURCES/kvm-block-curl-Implement-the-libcurl-timer-callback-inte.patch

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