[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] Re: [PATCH] xenstore: correctly handle errors from read_message



On Mon, 30 Aug 2010, Daniel De Graaf wrote:
> On 08/30/2010 01:13 PM, Stefano Stabellini wrote:
> > On Mon, 30 Aug 2010, Daniel De Graaf wrote:
> >> The return value of read_message needs to be checked in order to avoid
> >> waiting forever for a message if there is an error on the communication
> >> channel with xenstore. Currently, this is only checked if USE_PTHREAD is
> >> defined (by checking for read thread exit), and that path is prone to
> >> deadlock if request_mutex is held while waiting.
> >>
> >> Since the failure of read_message leaves the socket in an undefined
> >> state, close the socket and force all threads waiting on a read to return.
> >>
> >> This also fixes xs_read_watch in the case where a read thread is not
> >> running (in particular, this will happen if !USE_PTHREAD) by having it
> >> read from the communication channel in the same way as read_reply.
> >>
> >> Signed-off-by: Daniel De Graaf <dgdegra@xxxxxxxxxxxxx>
> >>
> >> diff -r 3c4c3d48a835 tools/xenstore/xs.c
> >> --- a/tools/xenstore/xs.c  Thu Aug 26 11:16:56 2010 +0100
> >> +++ b/tools/xenstore/xs.c  Mon Aug 30 10:56:11 2010 -0400
> >> @@ -84,7 +84,7 @@
> >>  #define mutex_lock(m)             pthread_mutex_lock(m)
> >>  #define mutex_unlock(m)           pthread_mutex_unlock(m)
> >>  #define condvar_signal(c) pthread_cond_signal(c)
> >> -#define condvar_wait(c,m,hnd)     pthread_cond_wait(c,m)
> >> +#define condvar_wait(c,m) pthread_cond_wait(c,m)
> >>  #define cleanup_push(f, a)        \
> >>      pthread_cleanup_push((void (*)(void *))(f), (void *)(a))
> >>  /*
> >> @@ -111,7 +111,7 @@
> >>  #define mutex_lock(m)             ((void)0)
> >>  #define mutex_unlock(m)           ((void)0)
> >>  #define condvar_signal(c) ((void)0)
> >> -#define condvar_wait(c,m,hnd)     read_message(hnd)
> >> +#define condvar_wait(c,m) ((void)0)
> >>  #define cleanup_push(f, a)        ((void)0)
> >>  #define cleanup_pop(run)  ((void)0)
> >>  #define read_thread_exists(h)     (0)
> >> @@ -337,21 +337,20 @@
> >>  
> >>    read_from_thread = read_thread_exists(h);
> >>  
> >> -#ifdef USE_PTHREAD
> >>    /* Read from comms channel ourselves if there is no reader thread. */
> >>    if (!read_from_thread && (read_message(h) == -1))
> >>            return NULL;
> >> -#endif
> >>  
> >>    mutex_lock(&h->reply_mutex);
> >> -  while (list_empty(&h->reply_list) && (!read_from_thread || 
> >> read_thread_exists(h)))
> >> -          condvar_wait(&h->reply_condvar, &h->reply_mutex, h);
> >> -  if (read_from_thread && !read_thread_exists(h)) {
> >> +#ifdef USE_PTHREAD
> >> +  while (list_empty(&h->reply_list) && read_from_thread && h->fd != -1)
> >> +          condvar_wait(&h->reply_condvar, &h->reply_mutex);
> >> +#endif
> >> +  if (list_empty(&h->reply_list)) {
> >>            mutex_unlock(&h->reply_mutex);
> >>            errno = EINVAL;
> >>            return NULL;
> >>    }
> >> -  assert(!list_empty(&h->reply_list));
> >>    msg = list_top(&h->reply_list, struct xs_stored_msg, list);
> >>    list_del(&msg->list);
> >>    assert(list_empty(&h->reply_list));
> >> @@ -663,13 +662,22 @@
> >>    struct xs_stored_msg *msg;
> >>    char **ret, *strings, c = 0;
> >>    unsigned int num_strings, i;
> >> +  int read_from_thread;
> >> +
> >> +  read_from_thread = read_thread_exists(h);
> >> +
> >> +  /* Read from comms channel ourselves if there is no reader thread. */
> >> +  if (!read_from_thread && (read_message(h) == -1))
> >> +          return NULL;
> >>  
> >>    mutex_lock(&h->watch_mutex);
> >>  
> >>    /* Wait on the condition variable for a watch to fire. */
> >> -  while (list_empty(&h->watch_list) && read_thread_exists(h))
> >> -          condvar_wait(&h->watch_condvar, &h->watch_mutex, h);
> >> -  if (!read_thread_exists(h)) {
> >> +#ifdef USE_PTHREAD
> >> +  while (list_empty(&h->watch_list) && read_from_thread && h->fd != -1)
> >> +          condvar_wait(&h->watch_condvar, &h->watch_mutex);
> >> +#endif
> >> +  if (list_empty(&h->watch_list)) {
> >>            mutex_unlock(&h->watch_mutex);
> >>            errno = EINVAL;
> >>            return NULL;
> >> @@ -965,21 +973,27 @@
> >>  static void *read_thread(void *arg)
> >>  {
> >>    struct xs_handle *h = arg;
> >> +  int fd;
> >>  
> >>    while (read_message(h) != -1)
> >>            continue;
> >>  
> >> -  /* Kick anyone waiting for a reply */
> >> -  pthread_mutex_lock(&h->request_mutex);
> >> -  h->read_thr_exists = 0;
> >> -  pthread_mutex_unlock(&h->request_mutex);
> >> +  /* An error return from read_message leaves the socket in an undefined
> >> +   * state; we might have read only the header and not the message after
> >> +   * it, or (more commonly) the other end has closed the connection.
> >> +   * Since further communication is unsafe, close the socket.
> >> +   */
> >> +  fd = h->fd;
> >> +  h->fd = -1;
> >> +  close(fd);
> >>  
> > 
> > I like this patch, however shouldn't you be setting h->read_thr_exists
> > to 0 here?
> > If you don't set read_thr_exists to 0 the variable becomes meaningless
> > and you can go ahead and remove it altogether.
> 
> The variable exists as a way to detect if the thread has been started so
> that xs_daemon_close can cancel/join if needed; it starts at 0 and is
> set to 1 on thread creation. Since pthread_t is supposed to be opaque,
> we can't test it directly to see if the thread has been created. Setting
> it to zero within the thread will leave the thread hanging because it
> will never be joined and has not been detached.

I understand now, thanks for the explanation.
I reckon the patch is correct and I am going to apply it unless somebody
has any other comments.


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.