All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1 of 2] tools/ocaml: libxc bindings: Fix stub_xc_readconsolering()
@ 2013-02-14 19:29 Andrew Cooper
  2013-02-14 19:29 ` [PATCH 2 of 2] tools/ocaml: libxc bindings: Fix failwith_xc() Andrew Cooper
  2013-02-15 14:26 ` [PATCH 1 of 2] tools/ocaml: libxc bindings: Fix stub_xc_readconsolering() Ian Campbell
  0 siblings, 2 replies; 5+ messages in thread
From: Andrew Cooper @ 2013-02-14 19:29 UTC (permalink / raw)
  To: xen-devel; +Cc: Ian Jackson, Ian Campbell

There are two problems with this function:
  * The caml_{enter,leave}_blocking_section() calls mean that two threads can
    compete for use of the static ring[] buffer.
  * It fails to retrieve the entire buffer if the hypervisor has used 32K or
    more of its console ring.

Unfortunately, fixing the code in a sensible is rather difficult.  The Ocaml
caller expects to get the entire console ring in a single buffer.  There are
no mechanisms to ask Xen for the size of the console ring (which is fixed
after boot).  Even if there was a way to get the size of the console ring, the
XEN_SYSCTL_readconsole hypercall itself races with printk(), leading to
possibly discontinuities if the ring is full.  The requirement for a NULL
terminating string means that a full console ring will appear to fail when use
the correct power of two, forcing us to double up again.  Furthermore, libxc
will bounce the allocated buffer, as will caml_copy_string().

On the other hand, this is very definitely for debugging and testing, so lets
do the best we can to get the entire buffer, and accept the inefficiencies.

Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>

diff -r 63594ce1708f -r 1daa30509f1c tools/ocaml/libs/xc/xenctrl_stubs.c
--- a/tools/ocaml/libs/xc/xenctrl_stubs.c
+++ b/tools/ocaml/libs/xc/xenctrl_stubs.c
@@ -523,27 +523,55 @@ CAMLprim value stub_xc_evtchn_reset(valu
 	CAMLreturn(Val_unit);
 }
 
-
-#define RING_SIZE 32768
-static char ring[RING_SIZE];
-
+/* Maximum size of console ring we are prepared to read, 16MB */
+#define MAX_CONSOLE_RING_SIZE (1U << 24)
 CAMLprim value stub_xc_readconsolering(value xch)
 {
-	unsigned int size = RING_SIZE - 1;
-	char *ring_ptr = ring;
-	int retval;
+	/* 32K starting size (adj for loop doubling at start) */
+	unsigned int ring_size = 1U << 14;
+	unsigned int nr_chars = ring_size;
+	char * ring = NULL;
+	int r;
 
 	CAMLparam1(xch);
+	CAMLlocal1(conring);
 
-	caml_enter_blocking_section();
-	retval = xc_readconsolering(_H(xch), ring_ptr, &size, 0, 0, NULL);
-	caml_leave_blocking_section();
+	do
+	{
+		ring_size *= 2;
+		if ( ring_size > MAX_CONSOLE_RING_SIZE )
+		{
+			caml_failwith("Console ring too large");
+			CAMLreturn(Val_unit);
+		}
+		nr_chars = ring_size;
 
-	if (retval)
-		failwith_xc(_H(xch));
+		free(ring);
+		if ( ! ( ring = malloc(ring_size) ) )
+		{
+			caml_failwith("Out of memory");
+			CAMLreturn(Val_unit);
+		}
 
-	ring[size] = '\0';
-	CAMLreturn(caml_copy_string(ring));
+		caml_enter_blocking_section();
+		r = xc_readconsolering(_H(xch), ring, &nr_chars, 0, 0, NULL);
+		caml_leave_blocking_section();
+
+		if ( r )
+		{
+			failwith_xc(_H(xch));
+			free(ring);
+			CAMLreturn(Val_unit);
+		}
+
+		/* If nr_chars == ring_size, we have not read the entire ring.
+		 * Try again with a larger buffer. */
+	} while ( nr_chars >= ring_size );
+
+	ring[nr_chars] = '\0';
+	conring = caml_copy_string(ring);
+	free(ring);
+	CAMLreturn(conring);
 }
 
 CAMLprim value stub_xc_send_debug_keys(value xch, value keys)

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 2 of 2] tools/ocaml: libxc bindings: Fix failwith_xc()
  2013-02-14 19:29 [PATCH 1 of 2] tools/ocaml: libxc bindings: Fix stub_xc_readconsolering() Andrew Cooper
@ 2013-02-14 19:29 ` Andrew Cooper
  2013-02-15 14:27   ` Ian Campbell
  2013-02-15 14:26 ` [PATCH 1 of 2] tools/ocaml: libxc bindings: Fix stub_xc_readconsolering() Ian Campbell
  1 sibling, 1 reply; 5+ messages in thread
From: Andrew Cooper @ 2013-02-14 19:29 UTC (permalink / raw)
  To: xen-devel; +Cc: Ian Jackson, Ian Campbell

The static error_str[] buffer is not thread-safe, and 1024 bytes is
unreasonably large.  Reduce to 256 bytes (which is still much larger than any
current use), and move it to being a stack variable.

Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>

diff -r 1daa30509f1c -r 39ae53386eb2 tools/ocaml/libs/xc/xenctrl_stubs.c
--- a/tools/ocaml/libs/xc/xenctrl_stubs.c
+++ b/tools/ocaml/libs/xc/xenctrl_stubs.c
@@ -51,21 +51,22 @@
 	i1 = (uint32_t) Int64_val(Field(input, 0)); \
 	i2 = ((Field(input, 1) == Val_none) ? 0xffffffff : (uint32_t) Int64_val(Field(Field(input, 1), 0)));
 
-#define ERROR_STRLEN 1024
 void failwith_xc(xc_interface *xch)
 {
-	static char error_str[ERROR_STRLEN];
+	char error_str[256];
 	if (xch) {
 		const xc_error *error = xc_get_last_error(xch);
 		if (error->code == XC_ERROR_NONE)
-                	snprintf(error_str, ERROR_STRLEN, "%d: %s", errno, strerror(errno));
+                	snprintf(error_str, sizeof(error_str),
+				 "%d: %s", errno, strerror(errno));
 		else
-			snprintf(error_str, ERROR_STRLEN, "%d: %s: %s",
-				 error->code,
+			snprintf(error_str, sizeof(error_str),
+				 "%d: %s: %s", error->code,
 				 xc_error_code_to_desc(error->code),
 				 error->message);
 	} else {
-		snprintf(error_str, ERROR_STRLEN, "Unable to open XC interface");
+		snprintf(error_str, sizeof(error_str),
+			 "Unable to open XC interface");
 	}
 	caml_raise_with_string(*caml_named_value("xc.error"), error_str);
 }

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 1 of 2] tools/ocaml: libxc bindings: Fix stub_xc_readconsolering()
  2013-02-14 19:29 [PATCH 1 of 2] tools/ocaml: libxc bindings: Fix stub_xc_readconsolering() Andrew Cooper
  2013-02-14 19:29 ` [PATCH 2 of 2] tools/ocaml: libxc bindings: Fix failwith_xc() Andrew Cooper
@ 2013-02-15 14:26 ` Ian Campbell
  2013-02-15 15:17   ` Andrew Cooper
  1 sibling, 1 reply; 5+ messages in thread
From: Ian Campbell @ 2013-02-15 14:26 UTC (permalink / raw)
  To: Andrew Cooper; +Cc: Ian Jackson, xen-devel@lists.xen.org

On Thu, 2013-02-14 at 19:29 +0000, Andrew Cooper wrote:
> There are two problems with this function:
>   * The caml_{enter,leave}_blocking_section() calls mean that two threads can
>     compete for use of the static ring[] buffer.
>   * It fails to retrieve the entire buffer if the hypervisor has used 32K or
>     more of its console ring.
> 
> Unfortunately, fixing the code in a sensible is rather difficult.  The Ocaml
> caller expects to get the entire console ring in a single buffer.  There are
> no mechanisms to ask Xen for the size of the console ring (which is fixed
> after boot).

It would be easy to add a sysctl to get this

>   Even if there was a way to get the size of the console ring, the
> XEN_SYSCTL_readconsole hypercall itself races with printk(), leading to
> possibly discontinuities if the ring is full.

Right, that one is tricky, but as you say we could accept the
inefficiencies here.

>   The requirement for a NULL
> terminating string means that a full console ring will appear to fail when use
> the correct power of two, forcing us to double up again.

Double? Or just allocate buffersize + 1? We could also consider fixing
the XEN_SYSCTL_readconsole interface to remove this issue.

> Furthermore, libxc will bounce the allocated buffer, as will caml_copy_string().

You could avoid the first of these by using xc_hypercall_buffer_alloc or
xc_hypercall_buffer_alloc_pages in the caller, these are exposed from
libxc exactly to allow us to avoid bouncing of larger objects.


> On the other hand, this is very definitely for debugging and testing, so lets
> do the best we can to get the entire buffer, and accept the inefficiencies.
> 
> Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
> 
> diff -r 63594ce1708f -r 1daa30509f1c tools/ocaml/libs/xc/xenctrl_stubs.c
> --- a/tools/ocaml/libs/xc/xenctrl_stubs.c
> +++ b/tools/ocaml/libs/xc/xenctrl_stubs.c
> @@ -523,27 +523,55 @@ CAMLprim value stub_xc_evtchn_reset(valu
>  	CAMLreturn(Val_unit);
>  }
>  
> -
> -#define RING_SIZE 32768
> -static char ring[RING_SIZE];
> -
> +/* Maximum size of console ring we are prepared to read, 16MB */
> +#define MAX_CONSOLE_RING_SIZE (1U << 24)
>  CAMLprim value stub_xc_readconsolering(value xch)
>  {
> -	unsigned int size = RING_SIZE - 1;
> -	char *ring_ptr = ring;
> -	int retval;
> +	/* 32K starting size (adj for loop doubling at start) */
> +	unsigned int ring_size = 1U << 14;
> +	unsigned int nr_chars = ring_size;
> +	char * ring = NULL;
> +	int r;
>  
>  	CAMLparam1(xch);
> +	CAMLlocal1(conring);
>  
> -	caml_enter_blocking_section();
> -	retval = xc_readconsolering(_H(xch), ring_ptr, &size, 0, 0, NULL);
> -	caml_leave_blocking_section();
> +	do
> +	{
> +		ring_size *= 2;
> +		if ( ring_size > MAX_CONSOLE_RING_SIZE )
> +		{
> +			caml_failwith("Console ring too large");
> +			CAMLreturn(Val_unit);
> +		}
> +		nr_chars = ring_size;
>  
> -	if (retval)
> -		failwith_xc(_H(xch));
> +		free(ring);
> +		if ( ! ( ring = malloc(ring_size) ) )
> +		{
> +			caml_failwith("Out of memory");
> +			CAMLreturn(Val_unit);
> +		}
>  
> -	ring[size] = '\0';
> -	CAMLreturn(caml_copy_string(ring));
> +		caml_enter_blocking_section();
> +		r = xc_readconsolering(_H(xch), ring, &nr_chars, 0, 0, NULL);
> +		caml_leave_blocking_section();
> +
> +		if ( r )
> +		{
> +			failwith_xc(_H(xch));
> +			free(ring);
> +			CAMLreturn(Val_unit);
> +		}
> +
> +		/* If nr_chars == ring_size, we have not read the entire ring.
> +		 * Try again with a larger buffer. */
> +	} while ( nr_chars >= ring_size );
> +
> +	ring[nr_chars] = '\0';
> +	conring = caml_copy_string(ring);
> +	free(ring);
> +	CAMLreturn(conring);
>  }
>  
>  CAMLprim value stub_xc_send_debug_keys(value xch, value keys)

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 2 of 2] tools/ocaml: libxc bindings: Fix failwith_xc()
  2013-02-14 19:29 ` [PATCH 2 of 2] tools/ocaml: libxc bindings: Fix failwith_xc() Andrew Cooper
@ 2013-02-15 14:27   ` Ian Campbell
  0 siblings, 0 replies; 5+ messages in thread
From: Ian Campbell @ 2013-02-15 14:27 UTC (permalink / raw)
  To: Andrew Cooper; +Cc: Ian Jackson, xen-devel@lists.xen.org

On Thu, 2013-02-14 at 19:29 +0000, Andrew Cooper wrote:
> The static error_str[] buffer is not thread-safe, and 1024 bytes is
> unreasonably large.  Reduce to 256 bytes (which is still much larger than any
> current use), and move it to being a stack variable.
> 
> Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>

Looks plausible to me, but please could you cc xen-api@ for ocaml
changes so someone who knows ocaml can comment.

> 
> diff -r 1daa30509f1c -r 39ae53386eb2 tools/ocaml/libs/xc/xenctrl_stubs.c
> --- a/tools/ocaml/libs/xc/xenctrl_stubs.c
> +++ b/tools/ocaml/libs/xc/xenctrl_stubs.c
> @@ -51,21 +51,22 @@
>  	i1 = (uint32_t) Int64_val(Field(input, 0)); \
>  	i2 = ((Field(input, 1) == Val_none) ? 0xffffffff : (uint32_t) Int64_val(Field(Field(input, 1), 0)));
>  
> -#define ERROR_STRLEN 1024
>  void failwith_xc(xc_interface *xch)
>  {
> -	static char error_str[ERROR_STRLEN];
> +	char error_str[256];
>  	if (xch) {
>  		const xc_error *error = xc_get_last_error(xch);
>  		if (error->code == XC_ERROR_NONE)
> -                	snprintf(error_str, ERROR_STRLEN, "%d: %s", errno, strerror(errno));
> +                	snprintf(error_str, sizeof(error_str),
> +				 "%d: %s", errno, strerror(errno));
>  		else
> -			snprintf(error_str, ERROR_STRLEN, "%d: %s: %s",
> -				 error->code,
> +			snprintf(error_str, sizeof(error_str),
> +				 "%d: %s: %s", error->code,
>  				 xc_error_code_to_desc(error->code),
>  				 error->message);
>  	} else {
> -		snprintf(error_str, ERROR_STRLEN, "Unable to open XC interface");
> +		snprintf(error_str, sizeof(error_str),
> +			 "Unable to open XC interface");
>  	}
>  	caml_raise_with_string(*caml_named_value("xc.error"), error_str);
>  }

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 1 of 2] tools/ocaml: libxc bindings: Fix stub_xc_readconsolering()
  2013-02-15 14:26 ` [PATCH 1 of 2] tools/ocaml: libxc bindings: Fix stub_xc_readconsolering() Ian Campbell
@ 2013-02-15 15:17   ` Andrew Cooper
  0 siblings, 0 replies; 5+ messages in thread
From: Andrew Cooper @ 2013-02-15 15:17 UTC (permalink / raw)
  To: Ian Campbell; +Cc: Ian Jackson, xen-devel@lists.xen.org

On 15/02/13 14:26, Ian Campbell wrote:
> On Thu, 2013-02-14 at 19:29 +0000, Andrew Cooper wrote:
>> There are two problems with this function:
>>   * The caml_{enter,leave}_blocking_section() calls mean that two threads can
>>     compete for use of the static ring[] buffer.
>>   * It fails to retrieve the entire buffer if the hypervisor has used 32K or
>>     more of its console ring.
>>
>> Unfortunately, fixing the code in a sensible is rather difficult.  The Ocaml
>> caller expects to get the entire console ring in a single buffer.  There are
>> no mechanisms to ask Xen for the size of the console ring (which is fixed
>> after boot).
> It would be easy to add a sysctl to get this

Yes.  I didn't because of a combination of the reasons below, and
because I needed a fix for our automated testing, but I think I have had
a cunning idea.

>
>>   Even if there was a way to get the size of the console ring, the
>> XEN_SYSCTL_readconsole hypercall itself races with printk(), leading to
>> possibly discontinuities if the ring is full.
> Right, that one is tricky, but as you say we could accept the
> inefficiencies here.
>
>>   The requirement for a NULL
>> terminating string means that a full console ring will appear to fail when use
>> the correct power of two, forcing us to double up again.
> Double? Or just allocate buffersize + 1? We could also consider fixing
> the XEN_SYSCTL_readconsole interface to remove this issue.

Requires buffersize + 1 + nr_of_chars_from_a_raced_printk to get any
indication back from the hypercall that we have indeed read the full ring.

Choosing buffersize rounded up to the next PAGE_SIZE will suffice except
in extreme circumstances.

>
>> Furthermore, libxc will bounce the allocated buffer, as will caml_copy_string().
> You could avoid the first of these by using xc_hypercall_buffer_alloc or
> xc_hypercall_buffer_alloc_pages in the caller, these are exposed from
> libxc exactly to allow us to avoid bouncing of larger objects.

I will investigate using these.

~Andrew

>
>
>> On the other hand, this is very definitely for debugging and testing, so lets
>> do the best we can to get the entire buffer, and accept the inefficiencies.
>>
>> Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
>>
>> diff -r 63594ce1708f -r 1daa30509f1c tools/ocaml/libs/xc/xenctrl_stubs.c
>> --- a/tools/ocaml/libs/xc/xenctrl_stubs.c
>> +++ b/tools/ocaml/libs/xc/xenctrl_stubs.c
>> @@ -523,27 +523,55 @@ CAMLprim value stub_xc_evtchn_reset(valu
>>  	CAMLreturn(Val_unit);
>>  }
>>  
>> -
>> -#define RING_SIZE 32768
>> -static char ring[RING_SIZE];
>> -
>> +/* Maximum size of console ring we are prepared to read, 16MB */
>> +#define MAX_CONSOLE_RING_SIZE (1U << 24)
>>  CAMLprim value stub_xc_readconsolering(value xch)
>>  {
>> -	unsigned int size = RING_SIZE - 1;
>> -	char *ring_ptr = ring;
>> -	int retval;
>> +	/* 32K starting size (adj for loop doubling at start) */
>> +	unsigned int ring_size = 1U << 14;
>> +	unsigned int nr_chars = ring_size;
>> +	char * ring = NULL;
>> +	int r;
>>  
>>  	CAMLparam1(xch);
>> +	CAMLlocal1(conring);
>>  
>> -	caml_enter_blocking_section();
>> -	retval = xc_readconsolering(_H(xch), ring_ptr, &size, 0, 0, NULL);
>> -	caml_leave_blocking_section();
>> +	do
>> +	{
>> +		ring_size *= 2;
>> +		if ( ring_size > MAX_CONSOLE_RING_SIZE )
>> +		{
>> +			caml_failwith("Console ring too large");
>> +			CAMLreturn(Val_unit);
>> +		}
>> +		nr_chars = ring_size;
>>  
>> -	if (retval)
>> -		failwith_xc(_H(xch));
>> +		free(ring);
>> +		if ( ! ( ring = malloc(ring_size) ) )
>> +		{
>> +			caml_failwith("Out of memory");
>> +			CAMLreturn(Val_unit);
>> +		}
>>  
>> -	ring[size] = '\0';
>> -	CAMLreturn(caml_copy_string(ring));
>> +		caml_enter_blocking_section();
>> +		r = xc_readconsolering(_H(xch), ring, &nr_chars, 0, 0, NULL);
>> +		caml_leave_blocking_section();
>> +
>> +		if ( r )
>> +		{
>> +			failwith_xc(_H(xch));
>> +			free(ring);
>> +			CAMLreturn(Val_unit);
>> +		}
>> +
>> +		/* If nr_chars == ring_size, we have not read the entire ring.
>> +		 * Try again with a larger buffer. */
>> +	} while ( nr_chars >= ring_size );
>> +
>> +	ring[nr_chars] = '\0';
>> +	conring = caml_copy_string(ring);
>> +	free(ring);
>> +	CAMLreturn(conring);
>>  }
>>  
>>  CAMLprim value stub_xc_send_debug_keys(value xch, value keys)
>

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2013-02-15 15:17 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-02-14 19:29 [PATCH 1 of 2] tools/ocaml: libxc bindings: Fix stub_xc_readconsolering() Andrew Cooper
2013-02-14 19:29 ` [PATCH 2 of 2] tools/ocaml: libxc bindings: Fix failwith_xc() Andrew Cooper
2013-02-15 14:27   ` Ian Campbell
2013-02-15 14:26 ` [PATCH 1 of 2] tools/ocaml: libxc bindings: Fix stub_xc_readconsolering() Ian Campbell
2013-02-15 15:17   ` Andrew Cooper

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.