* [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.