Patchwork [2/2] rbd: disable unsupported librbd functions at runtime

login
register
mail settings
Submitter Josh Durgin
Date April 4, 2013, 8:35 a.m.
Message ID <1365064515-23222-2-git-send-email-josh.durgin@inktank.com>
Download mbox | patch
Permalink /patch/233683/
State New
Headers show

Comments

Josh Durgin - April 4, 2013, 8:35 a.m.
QEMU may be compiled against a newer version of librbd, but run and
dynamically linked with an older version that does not support these
functions. Declare them as weak symbols so they can be checked for
existence at runtime.

Only rbd_aio_discard, rbd_aio_flush, and rbd_flush were added after
the initial version of librbd, so only they need to be checked.

Signed-off-by: Josh Durgin <josh.durgin@inktank.com>
---
 block/rbd.c |   19 +++++++++++++++++++
 1 file changed, 19 insertions(+)
Kevin Wolf - April 4, 2013, 10:10 a.m.
Am 04.04.2013 um 10:35 hat Josh Durgin geschrieben:
> QEMU may be compiled against a newer version of librbd, but run and
> dynamically linked with an older version that does not support these
> functions. Declare them as weak symbols so they can be checked for
> existence at runtime.
> 
> Only rbd_aio_discard, rbd_aio_flush, and rbd_flush were added after
> the initial version of librbd, so only they need to be checked.
> 
> Signed-off-by: Josh Durgin <josh.durgin@inktank.com>
> ---
>  block/rbd.c |   19 +++++++++++++++++++
>  1 file changed, 19 insertions(+)
> 
> diff --git a/block/rbd.c b/block/rbd.c
> index 037d82b..69a339a 100644
> --- a/block/rbd.c
> +++ b/block/rbd.c
> @@ -44,6 +44,15 @@
>   * leading "\".
>   */
>  
> +/*
> + * Treat newer librbd functions as weak symbols so we can detect
> + * whether they're supported at runtime, and disable their use
> + * if they aren't available.
> + */
> +#pragma weak rbd_aio_discard
> +#pragma weak rbd_aio_flush
> +#pragma weak rbd_flush
> +
>  /* rbd_aio_discard added in 0.1.2 */
>  #if LIBRBD_VERSION_CODE >= LIBRBD_VERSION(0, 1, 2)
>  #define LIBRBD_SUPPORTS_DISCARD
> @@ -970,6 +979,7 @@ static BlockDriver bdrv_rbd = {
>      .bdrv_aio_readv         = qemu_rbd_aio_readv,
>      .bdrv_aio_writev        = qemu_rbd_aio_writev,
>  
> +    /* select which of these to use at runtime in bdrv_rbd_init */
>      .bdrv_aio_flush         = qemu_rbd_aio_flush,
>      .bdrv_co_flush_to_disk  = qemu_rbd_co_flush,
>  
> @@ -985,6 +995,15 @@ static BlockDriver bdrv_rbd = {
>  
>  static void bdrv_rbd_init(void)
>  {
> +    if (!rbd_flush || rbd_aio_flush) {
> +        bdrv_rbd.bdrv_co_flush_to_disk = NULL;
> +    }
> +    if (!rbd_aio_flush) {
> +        bdrv_rbd.bdrv_aio_flush = NULL;
> +    }
> +    if (!rbd_aio_discard) {
> +        bdrv_rbd.bdrv_aio_discard = NULL;
> +    }
>      bdrv_register(&bdrv_rbd);
>  }

After searching the net a bit and trying out some things myself, I'm
afraid that this approach doesn't work. It does seem to do the right
thing when build and runtime version are the same, but new build -> old
runtime segfaults (because the symbol doesn't become NULL) and old build
-> new runtime segfaults (because the symbol stays NULL). Unless I
missed some build option that is different in qemu than in my test
program.

So it looks as if you had to use dlsym() instead.

Kevin
Josh Durgin - April 4, 2013, 4:50 p.m.
On 04/04/2013 03:10 AM, Kevin Wolf wrote:
> Am 04.04.2013 um 10:35 hat Josh Durgin geschrieben:
>> QEMU may be compiled against a newer version of librbd, but run and
>> dynamically linked with an older version that does not support these
>> functions. Declare them as weak symbols so they can be checked for
>> existence at runtime.
>>
>> Only rbd_aio_discard, rbd_aio_flush, and rbd_flush were added after
>> the initial version of librbd, so only they need to be checked.
>>
>> Signed-off-by: Josh Durgin <josh.durgin@inktank.com>
>> ---
>>   block/rbd.c |   19 +++++++++++++++++++
>>   1 file changed, 19 insertions(+)
>>
>> diff --git a/block/rbd.c b/block/rbd.c
>> index 037d82b..69a339a 100644
>> --- a/block/rbd.c
>> +++ b/block/rbd.c
>> @@ -44,6 +44,15 @@
>>    * leading "\".
>>    */
>>
>> +/*
>> + * Treat newer librbd functions as weak symbols so we can detect
>> + * whether they're supported at runtime, and disable their use
>> + * if they aren't available.
>> + */
>> +#pragma weak rbd_aio_discard
>> +#pragma weak rbd_aio_flush
>> +#pragma weak rbd_flush
>> +
>>   /* rbd_aio_discard added in 0.1.2 */
>>   #if LIBRBD_VERSION_CODE >= LIBRBD_VERSION(0, 1, 2)
>>   #define LIBRBD_SUPPORTS_DISCARD
>> @@ -970,6 +979,7 @@ static BlockDriver bdrv_rbd = {
>>       .bdrv_aio_readv         = qemu_rbd_aio_readv,
>>       .bdrv_aio_writev        = qemu_rbd_aio_writev,
>>
>> +    /* select which of these to use at runtime in bdrv_rbd_init */
>>       .bdrv_aio_flush         = qemu_rbd_aio_flush,
>>       .bdrv_co_flush_to_disk  = qemu_rbd_co_flush,
>>
>> @@ -985,6 +995,15 @@ static BlockDriver bdrv_rbd = {
>>
>>   static void bdrv_rbd_init(void)
>>   {
>> +    if (!rbd_flush || rbd_aio_flush) {
>> +        bdrv_rbd.bdrv_co_flush_to_disk = NULL;
>> +    }
>> +    if (!rbd_aio_flush) {
>> +        bdrv_rbd.bdrv_aio_flush = NULL;
>> +    }
>> +    if (!rbd_aio_discard) {
>> +        bdrv_rbd.bdrv_aio_discard = NULL;
>> +    }
>>       bdrv_register(&bdrv_rbd);
>>   }
>
> After searching the net a bit and trying out some things myself, I'm
> afraid that this approach doesn't work. It does seem to do the right
> thing when build and runtime version are the same, but new build -> old
> runtime segfaults (because the symbol doesn't become NULL) and old build
> -> new runtime segfaults (because the symbol stays NULL). Unless I
> missed some build option that is different in qemu than in my test
> program.

It worked when downgrading the runtime version of librbd for me, so
maybe something about qemu's build was different. This patch wouldn't
allow newer functions in a runtime version to be used though, since
they would be disabled if qemu were built against an older version.

> So it looks as if you had to use dlsym() instead.

I tried this initially, using glib's portable wrappers, but found that
it would require using the dlopen'd version only - not linking at
build time against librbd at all. I thought this might be too big
a change, but now it seems like the best way to go.

Using this approach, upgrading from a version of librbd that doesn't
support e.g. rbd_aio_flush to one that does would not require
recompiling qemu to be able to use the new function. In general, librbd
would not be needed at compile time for qemu to be able to use it,
which would make the rbd block driver much easier to install on
distros where rbd isn't enabled at build time.

If you don't mind this approach, I'll post another version using
only dlopen'd (via glib) librbd/librados.

Thanks for the reviews,
Josh
Kevin Wolf - April 5, 2013, 9:31 a.m.
Am 04.04.2013 um 18:50 hat Josh Durgin geschrieben:
> On 04/04/2013 03:10 AM, Kevin Wolf wrote:
> >After searching the net a bit and trying out some things myself, I'm
> >afraid that this approach doesn't work. It does seem to do the right
> >thing when build and runtime version are the same, but new build -> old
> >runtime segfaults (because the symbol doesn't become NULL) and old build
> >-> new runtime segfaults (because the symbol stays NULL). Unless I
> >missed some build option that is different in qemu than in my test
> >program.
> 
> It worked when downgrading the runtime version of librbd for me, so
> maybe something about qemu's build was different. This patch wouldn't
> allow newer functions in a runtime version to be used though, since
> they would be disabled if qemu were built against an older version.

Interesting that one way worked for you. At least it seems to be
unreliable, unfortunately.

> >So it looks as if you had to use dlsym() instead.
> 
> I tried this initially, using glib's portable wrappers, but found that
> it would require using the dlopen'd version only - not linking at
> build time against librbd at all. I thought this might be too big
> a change, but now it seems like the best way to go.

Aw, I didn't expect that... You're using g_module_open/symbol then? I
never used it, but from reading the docs maybe g_module_open with a NULL
filename might do the right thing when linked at build time?

On the other hand, not linking at build time at all and relying on
dlopen() only give us another nice feature: Distros can enable the rbd
block driver without introducing a hard dependency of qemu on librbd.
Maybe worth doing it for this reason alone.

> Using this approach, upgrading from a version of librbd that doesn't
> support e.g. rbd_aio_flush to one that does would not require
> recompiling qemu to be able to use the new function. In general, librbd
> would not be needed at compile time for qemu to be able to use it,
> which would make the rbd block driver much easier to install on
> distros where rbd isn't enabled at build time.
> 
> If you don't mind this approach, I'll post another version using
> only dlopen'd (via glib) librbd/librados.

Sure, if you don't mind implementing it I think it's a good idea. I
didn't mean to ask you for such a large change with my innocent
comments...

Kevin

Patch

diff --git a/block/rbd.c b/block/rbd.c
index 037d82b..69a339a 100644
--- a/block/rbd.c
+++ b/block/rbd.c
@@ -44,6 +44,15 @@ 
  * leading "\".
  */
 
+/*
+ * Treat newer librbd functions as weak symbols so we can detect
+ * whether they're supported at runtime, and disable their use
+ * if they aren't available.
+ */
+#pragma weak rbd_aio_discard
+#pragma weak rbd_aio_flush
+#pragma weak rbd_flush
+
 /* rbd_aio_discard added in 0.1.2 */
 #if LIBRBD_VERSION_CODE >= LIBRBD_VERSION(0, 1, 2)
 #define LIBRBD_SUPPORTS_DISCARD
@@ -970,6 +979,7 @@  static BlockDriver bdrv_rbd = {
     .bdrv_aio_readv         = qemu_rbd_aio_readv,
     .bdrv_aio_writev        = qemu_rbd_aio_writev,
 
+    /* select which of these to use at runtime in bdrv_rbd_init */
     .bdrv_aio_flush         = qemu_rbd_aio_flush,
     .bdrv_co_flush_to_disk  = qemu_rbd_co_flush,
 
@@ -985,6 +995,15 @@  static BlockDriver bdrv_rbd = {
 
 static void bdrv_rbd_init(void)
 {
+    if (!rbd_flush || rbd_aio_flush) {
+        bdrv_rbd.bdrv_co_flush_to_disk = NULL;
+    }
+    if (!rbd_aio_flush) {
+        bdrv_rbd.bdrv_aio_flush = NULL;
+    }
+    if (!rbd_aio_discard) {
+        bdrv_rbd.bdrv_aio_discard = NULL;
+    }
     bdrv_register(&bdrv_rbd);
 }