qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Anthony Liguori <aliguori@linux.vnet.ibm.com>
To: Aurelien Jarno <aurelien@aurel32.net>
Cc: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>,
	Marcelo Tosatti <mtosatti@redhat.com>,
	qemu-devel@nongnu.org, Paul Brook <paul@codesourcery.com>,
	Arun Bharadwaj <arun@linux.vnet.ibm.com>,
	Paulo Bonzini <pbonzini@redhat.com>
Subject: Re: [Qemu-devel] [PATCH 3/7] Add support for glib based threading and convert qemu thread to use it
Date: Tue, 25 Jan 2011 09:34:01 -0600	[thread overview]
Message-ID: <4D3EED69.3030407@linux.vnet.ibm.com> (raw)
In-Reply-To: <20110125142448.GI23331@hall.aurel32.net>

On 01/25/2011 08:24 AM, Aurelien Jarno wrote:
> On Mon, Jan 24, 2011 at 03:00:41PM -0600, Anthony Liguori wrote:
>    
>> GLib is an extremely common library that has a portable thread implementation
>> along with tons of other goodies.
>>
>> GLib and GObject have a fantastic amount of infrastructure we can leverage in
>> QEMU including an object oriented programming infrastructure.
>>
>> Short term, it has a very nice thread pool implementation that we could leverage
>> in something like virtio-9p.
>>
>> Signed-off-by: Anthony Liguori<aliguori@us.ibm.com>
>>
>> diff --git a/Makefile b/Makefile
>> index 6d601ee..bf24d1b 100644
>> --- a/Makefile
>> +++ b/Makefile
>> @@ -104,6 +104,8 @@ audio/audio.o audio/fmodaudio.o: QEMU_CFLAGS += $(FMOD_CFLAGS)
>>
>>   QEMU_CFLAGS+=$(CURL_CFLAGS)
>>
>> +QEMU_CFLAGS+=$(GLIB_CFLAGS)
>> +
>>   ui/cocoa.o: ui/cocoa.m
>>
>>   ui/sdl.o audio/sdlaudio.o ui/sdl_zoom.o baum.o: QEMU_CFLAGS += $(SDL_CFLAGS)
>> diff --git a/Makefile.objs b/Makefile.objs
>> index c3e52c5..283e62a 100644
>> --- a/Makefile.objs
>> +++ b/Makefile.objs
>> @@ -316,3 +316,5 @@ vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)
>>
>>   vl.o: QEMU_CFLAGS+=$(SDL_CFLAGS)
>>
>> +vl.o: QEMU_CFLAGS+=$(GLIB_CFLAGS)
>> +
>> diff --git a/Makefile.target b/Makefile.target
>> index e15b1c4..2c1c90e 100644
>> --- a/Makefile.target
>> +++ b/Makefile.target
>> @@ -204,6 +204,7 @@ QEMU_CFLAGS += $(VNC_TLS_CFLAGS)
>>   QEMU_CFLAGS += $(VNC_SASL_CFLAGS)
>>   QEMU_CFLAGS += $(VNC_JPEG_CFLAGS)
>>   QEMU_CFLAGS += $(VNC_PNG_CFLAGS)
>> +QEMU_CFLAGS += $(GLIB_CFLAGS)
>>
>>   # xen backend driver support
>>   obj-$(CONFIG_XEN) += xen_machine_pv.o xen_domainbuild.o
>> diff --git a/configure b/configure
>> index d5ac074..820fde9 100755
>> --- a/configure
>> +++ b/configure
>> @@ -1658,6 +1658,17 @@ EOF
>>   fi
>>
>>   ##########################################
>> +# glib support probe
>> +if $pkg_config --modversion gthread-2.0>  /dev/null 2>&1 ; then
>> +    glib_cflags=`$pkg_config --cflags gthread-2.0 2>/dev/null`
>> +    glib_libs=`$pkg_config --libs gthread-2.0 2>/dev/null`
>> +    libs_softmmu="$glib_libs $libs_softmmu"
>> +else
>> +    echo "glib-2.0 required to compile QEMU"
>> +    exit 1
>> +fi
>> +
>> +##########################################
>>   # kvm probe
>>   if test "$kvm" != "no" ; then
>>       cat>  $TMPC<<EOF
>> @@ -2677,6 +2688,7 @@ if test "$bluez" = "yes" ; then
>>     echo "CONFIG_BLUEZ=y">>  $config_host_mak
>>     echo "BLUEZ_CFLAGS=$bluez_cflags">>  $config_host_mak
>>   fi
>> +echo "GLIB_CFLAGS=$glib_cflags">>  $config_host_mak
>>   if test "$xen" = "yes" ; then
>>     echo "CONFIG_XEN=y">>  $config_host_mak
>>   fi
>> diff --git a/qemu-thread.c b/qemu-thread.c
>> index fbc78fe..2c521ab 100644
>> --- a/qemu-thread.c
>> +++ b/qemu-thread.c
>> @@ -10,183 +10,142 @@
>>    * See the COPYING file in the top-level directory.
>>    *
>>    */
>> -#include<stdlib.h>
>> -#include<stdio.h>
>> -#include<errno.h>
>> -#include<time.h>
>> -#include<signal.h>
>> -#include<stdint.h>
>> -#include<string.h>
>>      
> Why removing<signal.h>?
>    

It looked like unnecessary includes crept in so I remove them all.  It 
still built for me but apparently not for you.  I'll update accordingly.

Maybe we should put signal.h in qemu-common.h?

Regards,

Anthony Liguori

>> -#include "qemu-thread.h"
>>
>> -static void error_exit(int err, const char *msg)
>> -{
>> -    fprintf(stderr, "qemu: %s: %s\n", msg, strerror(err));
>> -    exit(1);
>> -}
>> +#include "qemu-common.h"
>> +#include "qemu-thread.h"
>>
>>   void qemu_mutex_init(QemuMutex *mutex)
>>   {
>> -    int err;
>> -
>> -    err = pthread_mutex_init(&mutex->lock, NULL);
>> -    if (err)
>> -        error_exit(err, __func__);
>> +    g_static_mutex_init(&mutex->lock);
>>   }
>>
>>   void qemu_mutex_destroy(QemuMutex *mutex)
>>   {
>> -    int err;
>> -
>> -    err = pthread_mutex_destroy(&mutex->lock);
>> -    if (err)
>> -        error_exit(err, __func__);
>> +    g_static_mutex_free(&mutex->lock);
>>   }
>>
>>   void qemu_mutex_lock(QemuMutex *mutex)
>>   {
>> -    int err;
>> -
>> -    err = pthread_mutex_lock(&mutex->lock);
>> -    if (err)
>> -        error_exit(err, __func__);
>> +    g_static_mutex_lock(&mutex->lock);
>>   }
>>
>>   int qemu_mutex_trylock(QemuMutex *mutex)
>>   {
>> -    return pthread_mutex_trylock(&mutex->lock);
>> -}
>> -
>> -static void timespec_add_ms(struct timespec *ts, uint64_t msecs)
>> -{
>> -    ts->tv_sec = ts->tv_sec + (long)(msecs / 1000);
>> -    ts->tv_nsec = (ts->tv_nsec + ((long)msecs % 1000) * 1000000);
>> -    if (ts->tv_nsec>= 1000000000) {
>> -        ts->tv_nsec -= 1000000000;
>> -        ts->tv_sec++;
>> -    }
>> -}
>> -
>> -int qemu_mutex_timedlock(QemuMutex *mutex, uint64_t msecs)
>> -{
>> -    int err;
>> -    struct timespec ts;
>> -
>> -    clock_gettime(CLOCK_REALTIME,&ts);
>> -    timespec_add_ms(&ts, msecs);
>> -
>> -    err = pthread_mutex_timedlock(&mutex->lock,&ts);
>> -    if (err&&  err != ETIMEDOUT)
>> -        error_exit(err, __func__);
>> -    return err;
>> +    return g_static_mutex_trylock(&mutex->lock);
>>   }
>>
>>   void qemu_mutex_unlock(QemuMutex *mutex)
>>   {
>> -    int err;
>> -
>> -    err = pthread_mutex_unlock(&mutex->lock);
>> -    if (err)
>> -        error_exit(err, __func__);
>> +    g_static_mutex_unlock(&mutex->lock);
>>   }
>>
>>   void qemu_cond_init(QemuCond *cond)
>>   {
>> -    int err;
>> -
>> -    err = pthread_cond_init(&cond->cond, NULL);
>> -    if (err)
>> -        error_exit(err, __func__);
>> +    cond->cond = g_cond_new();
>>   }
>>
>>   void qemu_cond_destroy(QemuCond *cond)
>>   {
>> -    int err;
>> -
>> -    err = pthread_cond_destroy(&cond->cond);
>> -    if (err)
>> -        error_exit(err, __func__);
>> +    g_cond_free(cond->cond);
>>   }
>>
>>   void qemu_cond_signal(QemuCond *cond)
>>   {
>> -    int err;
>> -
>> -    err = pthread_cond_signal(&cond->cond);
>> -    if (err)
>> -        error_exit(err, __func__);
>> +    g_cond_signal(cond->cond);
>>   }
>>
>>   void qemu_cond_broadcast(QemuCond *cond)
>>   {
>> -    int err;
>> -
>> -    err = pthread_cond_broadcast(&cond->cond);
>> -    if (err)
>> -        error_exit(err, __func__);
>> +    g_cond_broadcast(cond->cond);
>>   }
>>
>>   void qemu_cond_wait(QemuCond *cond, QemuMutex *mutex)
>>   {
>> -    int err;
>> -
>> -    err = pthread_cond_wait(&cond->cond,&mutex->lock);
>> -    if (err)
>> -        error_exit(err, __func__);
>> +    g_cond_wait(cond->cond, g_static_mutex_get_mutex(&mutex->lock));
>>   }
>>
>>   int qemu_cond_timedwait(QemuCond *cond, QemuMutex *mutex, uint64_t msecs)
>>   {
>> -    struct timespec ts;
>> -    int err;
>> +    GTimeVal abs_time;
>> +
>> +    assert(cond->cond != NULL);
>>
>> -    clock_gettime(CLOCK_REALTIME,&ts);
>> -    timespec_add_ms(&ts, msecs);
>> +    g_get_current_time(&abs_time);
>> +    g_time_val_add(&abs_time, msecs * 1000); /* MSEC to USEC */
>>
>> -    err = pthread_cond_timedwait(&cond->cond,&mutex->lock,&ts);
>> -    if (err&&  err != ETIMEDOUT)
>> -        error_exit(err, __func__);
>> -    return err;
>> +    return g_cond_timed_wait(cond->cond,
>> +                             g_static_mutex_get_mutex(&mutex->lock),&abs_time);
>> +}
>> +
>> +struct trampoline_data
>> +{
>> +    QemuThread *thread;
>> +    void *(*startfn)(void *);
>> +    void *opaque;
>> +    QemuMutex lock;
>> +};
>> +
>> +static gpointer thread_trampoline(gpointer data)
>> +{
>> +    struct trampoline_data *td = data;
>> +    gpointer retval;
>> +
>> +    td->thread->tid = pthread_self();
>> +    qemu_mutex_unlock(&td->lock);
>> +
>> +    retval = td->startfn(td->opaque);
>> +    qemu_free(td);
>> +
>> +    return retval;
>>   }
>>
>>   void qemu_thread_create(QemuThread *thread,
>> -                       void *(*start_routine)(void*),
>> -                       void *arg)
>> +                        void *(*start_routine)(void*),
>> +                        void *arg)
>>   {
>> -    int err;
>> +    struct trampoline_data *td = qemu_malloc(sizeof(*td));
>> +    sigset_t set, old;
>>
>> -    /* Leave signal handling to the iothread.  */
>> -    sigset_t set, oldset;
>> +    td->startfn = start_routine;
>> +    td->opaque = arg;
>> +    td->thread = thread;
>> +    qemu_mutex_init(&td->lock);
>> +
>> +    /* on behalf of the new thread */
>> +    qemu_mutex_lock(&td->lock);
>>
>>       sigfillset(&set);
>>      
> If it is still in use here? This simply doesn't build:
>
> qemu-thread.c: In function ‘q_thread_create_nosignal’:
> qemu-thread.c:34: error: implicit declaration of function ‘sigfillset’
> qemu-thread.c:34: error: nested extern declaration of ‘sigfillset’
> qemu-thread.c:35: error: implicit declaration of function ‘pthread_sigmask’
> qemu-thread.c:35: error: nested extern declaration of ‘pthread_sigmask’
> qemu-thread.c:35: error: ‘SIG_SETMASK’ undeclared (first use in this function)
> qemu-thread.c:35: error: (Each undeclared identifier is reported only once
> qemu-thread.c:35: error: for each function it appears in.)
> qemu-thread.c: In function ‘qemu_sthread_create’:
> qemu-thread.c:80: error: ‘SIG_SETMASK’ undeclared (first use in this function)
> qemu-thread.c: In function ‘qemu_sthread_signal’:
> qemu-thread.c:96: error: implicit declaration of function ‘pthread_kill’
> qemu-thread.c:96: error: nested extern declaration of ‘pthread_kill’
>
>
>    
>> -    pthread_sigmask(SIG_SETMASK,&set,&oldset);
>> -    err = pthread_create(&thread->thread, NULL, start_routine, arg);
>> -    if (err)
>> -        error_exit(err, __func__);
>> +    pthread_sigmask(SIG_SETMASK,&set,&old);
>> +    thread->thread = g_thread_create(thread_trampoline, td, TRUE, NULL);
>> +    pthread_sigmask(SIG_SETMASK,&old, NULL);
>> +
>> +    /* we're transfering ownership of this lock to the thread so we no
>> +     * longer hold it here */
>>
>> -    pthread_sigmask(SIG_SETMASK,&oldset, NULL);
>> +    qemu_mutex_lock(&td->lock);
>> +    /* validate tid */
>> +    qemu_mutex_unlock(&td->lock);
>> +
>> +    qemu_mutex_destroy(&td->lock);
>>   }
>>
>>   void qemu_thread_signal(QemuThread *thread, int sig)
>>   {
>> -    int err;
>> -
>> -    err = pthread_kill(thread->thread, sig);
>> -    if (err)
>> -        error_exit(err, __func__);
>> +    pthread_kill(thread->tid, sig);
>>   }
>>
>>   void qemu_thread_self(QemuThread *thread)
>>   {
>> -    thread->thread = pthread_self();
>> +    thread->thread = g_thread_self();
>> +    thread->tid = pthread_self();
>>   }
>>
>>   int qemu_thread_equal(QemuThread *thread1, QemuThread *thread2)
>>   {
>> -   return pthread_equal(thread1->thread, thread2->thread);
>> +    return (thread1->thread == thread2->thread);
>>   }
>>
>>   void qemu_thread_exit(void *retval)
>>   {
>> -    pthread_exit(retval);
>> +    g_thread_exit(retval);
>>   }
>> diff --git a/qemu-thread.h b/qemu-thread.h
>> index 19bb30c..dc22a60 100644
>> --- a/qemu-thread.h
>> +++ b/qemu-thread.h
>> @@ -1,18 +1,19 @@
>>   #ifndef __QEMU_THREAD_H
>>   #define __QEMU_THREAD_H 1
>> -#include "semaphore.h"
>> -#include "pthread.h"
>> +#include<glib.h>
>> +#include<pthread.h>
>>
>>   struct QemuMutex {
>> -    pthread_mutex_t lock;
>> +    GStaticMutex lock;
>>   };
>>
>>   struct QemuCond {
>> -    pthread_cond_t cond;
>> +    GCond *cond;
>>   };
>>
>>   struct QemuThread {
>> -    pthread_t thread;
>> +    GThread *thread;
>> +    pthread_t tid;
>>   };
>>
>>   typedef struct QemuMutex QemuMutex;
>> diff --git a/vl.c b/vl.c
>> index 0292184..bbe0931 100644
>> --- a/vl.c
>> +++ b/vl.c
>> @@ -165,6 +165,8 @@ int main(int argc, char **argv)
>>
>>   #include "ui/qemu-spice.h"
>>
>> +#include<glib.h>
>> +
>>   //#define DEBUG_NET
>>   //#define DEBUG_SLIRP
>>
>> @@ -1918,6 +1920,8 @@ int main(int argc, char **argv, char **envp)
>>       atexit(qemu_run_exit_notifiers);
>>       error_set_progname(argv[0]);
>>
>> +    g_thread_init(NULL);
>> +
>>       init_clocks();
>>
>>       qemu_cache_utils_init(envp);
>> -- 
>> 1.7.0.4
>>
>>
>>
>>      
>    

  reply	other threads:[~2011-01-25 15:34 UTC|newest]

Thread overview: 101+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-01-24 21:00 [Qemu-devel] [RFC 0/7] Introduce hard dependency on glib Anthony Liguori
2011-01-24 21:00 ` [Qemu-devel] [PATCH 1/7] io-thread: make sure to initialize qemu_work_cond and qemu_cpu_cond Anthony Liguori
2011-02-08  8:53   ` [Qemu-devel] " Jan Kiszka
2011-02-08  9:01     ` Anthony Liguori
2011-01-24 21:00 ` [Qemu-devel] [PATCH 2/7] Enable I/O thread and VNC threads by default Anthony Liguori
2011-01-24 22:28   ` [Qemu-devel] " Anthony Liguori
2011-01-25  9:17     ` Edgar E. Iglesias
2011-01-25 13:34       ` Marcelo Tosatti
2011-02-07 10:12         ` Marcelo Tosatti
2011-02-07 16:03           ` Marcelo Tosatti
2011-02-07 16:23             ` Paolo Bonzini
2011-02-07 17:10               ` Jan Kiszka
2011-02-07 21:02                 ` Anthony Liguori
2011-02-07 21:45                   ` Aurelien Jarno
2011-02-08  2:09                     ` Anthony Liguori
2011-02-08  7:26                       ` Aurelien Jarno
2011-02-08  8:08                         ` Paolo Bonzini
2011-02-08  8:50                           ` Jan Kiszka
2011-02-08  9:05                             ` Aurelien Jarno
2011-02-08  9:12                               ` Anthony Liguori
2011-02-08  9:49                                 ` Paolo Bonzini
2011-02-08  9:51                               ` Jan Kiszka
2011-02-08  9:58                                 ` Aurelien Jarno
2011-02-08 10:03                                   ` Jan Kiszka
2011-02-08 10:06                                     ` Aurelien Jarno
2011-02-08 10:16                                       ` Alexander Graf
2011-02-08 10:17                                       ` Stefan Hajnoczi
2011-02-08 10:27                                         ` Aurelien Jarno
2011-02-08 10:31                                           ` Paolo Bonzini
2011-02-08 10:40                                           ` Jan Kiszka
2011-02-08 18:05                                           ` Anthony Liguori
2011-02-08 11:29                                             ` Aurelien Jarno
2011-02-08 12:38                                             ` Riku Voipio
2011-02-08 10:21                                       ` Jan Kiszka
2011-02-08 10:26                                         ` Aurelien Jarno
2011-02-08 10:30                                           ` Jan Kiszka
2011-02-08 17:58                                       ` Anthony Liguori
2011-02-08 11:07                                         ` Tristan Gingold
2011-02-08 11:46                                           ` Aurelien Jarno
2011-02-08 12:07                                             ` Paolo Bonzini
2011-02-08 19:21                                             ` Anthony Liguori
2011-02-08 11:15                                         ` Aurelien Jarno
2011-02-08 12:10                                           ` Paolo Bonzini
2011-02-08 13:31                                             ` Aurelien Jarno
2011-02-08 15:08                                               ` Aurelien Jarno
2011-02-09 17:35                                                 ` Aurelien Jarno
2011-02-09 20:07                                                   ` Anthony Liguori
2011-02-11  0:03                                                     ` Marcelo Tosatti
2011-02-08 19:17                                           ` Anthony Liguori
2011-02-08 13:30                                             ` Aurelien Jarno
2011-02-08 20:54                                               ` Anthony Liguori
2011-02-08 15:09                                                 ` Aurelien Jarno
2011-02-09 17:13                                                   ` Blue Swirl
2011-02-09 22:16                                                     ` [Qemu-devel] " Stefan Weil
2011-02-10  7:34                                                       ` Paolo Bonzini
2011-02-10  9:54                                                       ` Paolo Bonzini
2011-02-10 19:46                                                         ` Stefan Weil
2011-02-08 10:06                                   ` [Qemu-devel] " Paolo Bonzini
2011-02-07 18:35             ` Edgar E. Iglesias
2011-02-07 20:44             ` Aurelien Jarno
2011-02-07 21:30             ` Scott Wood
2011-02-07 20:47           ` Edgar E. Iglesias
2011-01-25  8:33   ` [Qemu-devel] [PATCH 0/2] vnc: the lost parts Corentin Chary
2011-01-25  8:33   ` [Qemu-devel] [PATCH 1/2] vl.c: set NULL upon deleting handlers in qemu_set_fd_handler2() Corentin Chary
2011-01-25 10:03     ` Stefan Hajnoczi
2011-01-25 10:13       ` Corentin Chary
2011-01-25 10:26         ` Stefan Hajnoczi
2011-01-25 12:05           ` Yoshiaki Tamura
2011-01-25  8:33   ` [Qemu-devel] [PATCH 2/2] vnc: qemu can die if the client is disconnected while updating screen Corentin Chary
2011-01-24 21:00 ` [Qemu-devel] [PATCH 3/7] Add support for glib based threading and convert qemu thread to use it Anthony Liguori
2011-01-25 14:24   ` Aurelien Jarno
2011-01-25 15:34     ` Anthony Liguori [this message]
2011-02-02 17:32   ` [Qemu-devel] " Paolo Bonzini
2011-02-02 17:35     ` Anthony Liguori
2011-01-24 21:00 ` [Qemu-devel] [PATCH 4/7] Get rid of QemuMutex and teach its callers about GStaticMutex Anthony Liguori
2011-01-24 22:24   ` [Qemu-devel] " Jan Kiszka
2011-01-25  0:02     ` Anthony Liguori
2011-01-25  7:39       ` Jan Kiszka
2011-01-24 21:00 ` [Qemu-devel] [PATCH 5/7] threads: get rid of QemuCond and teach callers about GCond Anthony Liguori
2011-01-24 21:00 ` [Qemu-devel] [PATCH 6/7] Teach vnc server to use GThread directly Anthony Liguori
2011-01-26 10:39   ` Stefan Hajnoczi
2011-01-24 21:00 ` [Qemu-devel] [PATCH 7/7] Rename QemuThread to QemuSThread to indicate that it is not a generic thread Anthony Liguori
2011-01-24 21:28 ` [Qemu-devel] Re: [RFC 0/7] Introduce hard dependency on glib Paolo Bonzini
2011-01-24 22:01   ` Anthony Liguori
2011-01-25 10:41     ` Paolo Bonzini
2011-01-25 11:14       ` Daniel P. Berrange
2011-01-25 11:21         ` Paolo Bonzini
2011-01-25  0:24 ` [Qemu-devel] " Anthony Liguori
2011-01-25  6:51   ` Edgar E. Iglesias
2011-01-25 10:24 ` Stefan Hajnoczi
2011-01-25 11:51 ` Gerd Hoffmann
2011-01-25 12:04   ` Daniel P. Berrange
2011-01-25 14:48   ` Stefano Stabellini
2011-01-25 17:48     ` Anthony Liguori
2011-01-25 18:12       ` Stefano Stabellini
2011-01-25 14:23 ` Aurelien Jarno
2011-01-25 15:35   ` Anthony Liguori
     [not found] ` <20110126044710.GU9566@redhat.com>
2011-01-26 15:53   ` Anthony Liguori
2011-01-26 21:23     ` Stefan Hajnoczi
2011-01-26 22:12       ` Anthony Liguori
2011-01-26 17:48 ` Johannes Stezenbach

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4D3EED69.3030407@linux.vnet.ibm.com \
    --to=aliguori@linux.vnet.ibm.com \
    --cc=arun@linux.vnet.ibm.com \
    --cc=aurelien@aurel32.net \
    --cc=mtosatti@redhat.com \
    --cc=paul@codesourcery.com \
    --cc=pbonzini@redhat.com \
    --cc=qemu-devel@nongnu.org \
    --cc=stefanha@linux.vnet.ibm.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).