Avoid clobbering output_function by concurrent CLI sessions doing vlib_process_wait_f... 48/1048/4
authorAndrew Yourtchenko <ayourtch@gmail.com>
Tue, 10 May 2016 10:51:34 +0000 (10:51 +0000)
committerDave Barach <openvpp@barachs.net>
Tue, 10 May 2016 17:13:00 +0000 (17:13 +0000)
A problem is easily reproducible by taking the test harness code from the commit,
and launching it in two terminals with some time overlap - the outputs will
be sent to the wrong session. This commit moves the output_function and argument
from a global structure into the process structure, thus the output_function
is not clobbered anymore and each session gets only its own output.

To ensure the callers can redirect the outputs to different destinations
(e.g. the API calls via shared memory, etc.) the existing logic
for vlib_cli_input() was retained.

To avoid the magic numbers usage in the logic that does the page-alignment
of the process stack, there are changes around the stack[] member
of vlib_process_t. Also added a compile-time assert to ensure that
the stack does indeed start on the page size multiple boundary.

Change-Id: I128680ac480735e5f214f81a884e414268e5d652
Signed-off-by: Andrew Yourtchenko <ayourtch@gmail.com>
vlib/vlib/cli.c
vlib/vlib/cli.h
vlib/vlib/node.h
vlib/vlib/unix/cli.c

index 8833f4a..5a0867b 100644 (file)
@@ -506,16 +506,17 @@ void vlib_cli_input (vlib_main_t * vm,
                     vlib_cli_output_function_t * function,
                     uword function_arg)
 {
+  vlib_process_t * cp = vlib_get_current_process(vm);
   vlib_cli_main_t * cm = &vm->cli_main;
   clib_error_t * error;
   vlib_cli_output_function_t * save_function;
   uword save_function_arg;
 
-  save_function = cm->output_function;
-  save_function_arg = cm->output_function_arg;
+  save_function = cp->output_function;
+  save_function_arg = cp->output_function_arg;
 
-  cm->output_function = function;
-  cm->output_function_arg = function_arg;
+  cp->output_function = function;
+  cp->output_function_arg = function_arg;
 
   do {
     vec_reset_length (cm->parse_rule_data);
@@ -529,14 +530,14 @@ void vlib_cli_input (vlib_main_t * vm,
       clib_error_free (error);
     }
 
-  cm->output_function = save_function;
-  cm->output_function_arg = save_function_arg;
+  cp->output_function = save_function;
+  cp->output_function_arg = save_function_arg;
 }
 
 /* Output to current CLI connection. */
 void vlib_cli_output (vlib_main_t * vm, char * fmt, ...)
 {
-  vlib_cli_main_t * cm = &vm->cli_main;
+  vlib_process_t * cp = vlib_get_current_process(vm);
   va_list va;
   u8 * s;
 
@@ -548,10 +549,10 @@ void vlib_cli_output (vlib_main_t * vm, char * fmt, ...)
   if (vec_len (s) > 0 && s[vec_len (s)-1] != '\n')
     vec_add1 (s, '\n');
 
-  if (! cm->output_function)
+  if ((! cp) || (! cp->output_function))
     fformat (stdout, "%v", s);
   else
-    cm->output_function (cm->output_function_arg, s, vec_len (s));
+    cp->output_function (cp->output_function_arg, s, vec_len (s));
 
   vec_free (s);
 }
@@ -665,6 +666,37 @@ VLIB_CLI_COMMAND (cmd_test_heap_validate,static) = {
     .function = test_heap_validate,
 };
 
+#ifdef TEST_CODE
+/*
+ * A trivial test harness to verify the per-process output_function
+ * is working correcty.
+ */
+
+static clib_error_t *
+sleep_ten_seconds (vlib_main_t * vm,
+                   unformat_input_t * input,
+                   vlib_cli_command_t * cmd)
+{
+  u16 i;
+  u16 my_id = rand();
+
+  vlib_cli_output(vm, "Starting 10 seconds sleep with id %u\n", my_id);
+
+  for(i=0; i<10; i++)
+    {
+      vlib_process_wait_for_event_or_clock(vm, 1.0);
+      vlib_cli_output(vm, "Iteration number %u, my id: %u\n", i, my_id);
+    }
+  vlib_cli_output(vm, "Done with sleep with id %u\n", my_id);
+  return 0;
+}
+
+VLIB_CLI_COMMAND (ping_command, static) = {
+  .path = "test sleep",
+  .function = sleep_ten_seconds,
+  .short_help = "Sleep for 10 seconds",
+};
+#endif /* ifdef TEST_CODE */
 
 static uword vlib_cli_normalize_path (char * input, char ** result)
 {
index 8c80247..22aa22e 100644 (file)
@@ -128,12 +128,6 @@ typedef void (vlib_cli_output_function_t) (uword arg,
                                           u8 * buffer,
                                           uword buffer_bytes);
 typedef struct {
-  /* Current output function. */
-  vlib_cli_output_function_t * output_function;
-
-  /* Opaque data for output function. */
-  uword output_function_arg;
-
   /* Vector of all known commands. */
   vlib_cli_command_t * commands;
 
index 348ad1f..2caede6 100644 (file)
@@ -496,17 +496,32 @@ typedef struct {
   /* When suspending saves cpu cycle counter when process is to be resumed. */
   u64 resume_cpu_time;
 
+  /* Default output function and its argument for any CLI outputs
+     within the process. */
+  vlib_cli_output_function_t *output_function;
+  uword output_function_arg;
+
 #ifdef CLIB_UNIX
   /* Pad to a multiple of the page size so we can mprotect process stacks */
-  CLIB_PAD_FROM_TO (0x140, 0x1000);
+#define PAGE_SIZE_MULTIPLE 0x1000
+#define ALIGN_ON_MULTIPLE_PAGE_BOUNDARY_FOR_MPROTECT  __attribute__ ((aligned (PAGE_SIZE_MULTIPLE)))
+#else
+#define ALIGN_ON_MULTIPLE_PAGE_BOUNDARY_FOR_MPROTECT
 #endif
+
   /* Process stack.  Starts here and extends 2^log2_n_stack_bytes
      bytes. */
 
 #define VLIB_PROCESS_STACK_MAGIC (0xdead7ead)
-  u32 stack[0];
+  u32 stack[0] ALIGN_ON_MULTIPLE_PAGE_BOUNDARY_FOR_MPROTECT;
 } vlib_process_t __attribute__ ((aligned (CLIB_CACHE_LINE_BYTES)));
 
+#ifdef CLIB_UNIX
+  /* Ensure that the stack is aligned on the multiple of the page size */
+typedef char assert_process_stack_must_be_aligned_exactly_to_page_size_multiple
+                [(sizeof(vlib_process_t) - PAGE_SIZE_MULTIPLE) == 0 ? 0 : -1];
+#endif
+
 typedef struct {
     u32 node_index;
 
index 0aa7f23..dc9ac2a 100644 (file)
@@ -1889,6 +1889,11 @@ static u32 unix_cli_file_add (unix_cli_main_t * cm, char * name, int fd)
   cf->input_vector = 0;
 
   vlib_start_process (vm, n->runtime_index);
+
+  vlib_process_t * p = vlib_get_process_from_node(vm, n);
+  p->output_function = unix_vlib_cli_output;
+  p->output_function_arg = cf - cm->cli_file_pool;
+
   return cf - cm->cli_file_pool;
 }