X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=docs%2Fgettingstarted%2Fdevelopers%2Finfrastructure.md;h=12f96d5ad41e8eb873d486465f4f999fe5681991;hb=78a1877463e59c15d0554bab61b5ad803625834f;hp=b99460732c5944ccd7a802d04b31d2b2cd61bf35;hpb=4659d0e82343e6c061261f0a2aa977b0d00f199d;p=vpp.git diff --git a/docs/gettingstarted/developers/infrastructure.md b/docs/gettingstarted/developers/infrastructure.md index b99460732c5..12f96d5ad41 100644 --- a/docs/gettingstarted/developers/infrastructure.md +++ b/docs/gettingstarted/developers/infrastructure.md @@ -48,8 +48,8 @@ macro! It's smart about NULL pointers.\] Typically, the user header is not present. User headers allow for other data structures to be built atop vppinfra vectors. Users may specify the -alignment for first data element of a vector via the [vec]()\*\_aligned -macros. +alignment for first data element of a vector via the \[vec\]()\*\_aligned +macros. Vector elements can be any C type e.g. (int, double, struct bar). This is also true for data types built atop vectors (e.g. heap, pool, etc.). @@ -57,7 +57,7 @@ Many macros have \_a variants supporting alignment of vector elements and \_h variants supporting non-zero-length vector headers. The \_ha variants support both. Additionally cacheline alignment within a vector element structure can be specified using the -[CLIB_CACHE_LINE_ALIGN_MARK]() macro. +\[CLIB_CACHE_LINE_ALIGN_MARK\]() macro. Inconsistent usage of header and/or alignment related macro variants will cause delayed, confusing failures. @@ -114,6 +114,63 @@ key\_pointer. It is usually a bad mistake to pass the address of a vector element as the second argument to hash\_set\_mem. It is perfectly fine to memorize constant string addresses in the text segment. +Timekeeping +----------- + +Vppinfra includes high-precision, low-cost timing services. The +datatype clib_time_t and associated functions reside in +./src/vppinfra/time.\[ch\]. Call clib_time_init (clib_time_t \*cp) to +initialize the clib_time_t object. + +Clib_time_init(...) can use a variety of different ways to establish +the hardware clock frequency. At the end of the day, vppinfra +timekeeping takes the attitude that the operating system's clock is +the closest thing to a gold standard it has handy. + +When properly configured, NTP maintains kernel clock synchronization +with a highly accurate off-premises reference clock. Notwithstanding +network propagation delays, a synchronized NTP client will keep the +kernel clock accurate to within 50ms or so. + +Why should one care? Simply put, oscillators used to generate CPU +ticks aren't super accurate. They work pretty well, but a 0.1% error +wouldn't be out of the question. That's a minute and a half's worth of +error in 1 day. The error changes constantly, due to temperature +variation, and a host of other physical factors. + +It's far too expensive to use system calls for timing, so we're left +with the problem of continously adjusting our view of the CPU tick +register's clocks_per_second parameter. + +The clock rate adjustment algorithm measures the number of cpu ticks +and the "gold standard" reference time across an interval of +approximately 16 seconds. We calculate clocks_per_second for the +interval: use rdtsc (on x86_64) and a system call to get the latest +cpu tick count and the kernel's latest nanosecond timestamp. We +subtract the previous interval end values, and use exponential +smoothing to merge the new clock rate sample into the clocks_per_second +parameter. + +As of this writing, we maintain the clock rate by way of the following +first-order differential equation: + + +``` + clocks_per_second(t) = clocks_per_second(t-1) * K + sample_cps(t)*(1-K) + where K = e**(-1.0/3.75); +``` + +This yields a per observation "half-life" of 1 minute. Empirically, +the clock rate converges within 5 minutes, and appears to maintain +near-perfect agreement with the kernel clock in the face of ongoing +NTP time adjustments. + +See ./src/vppinfra/time.c:clib_time_verify_frequency(...) to look at +the rate adjustment algorithm. The code rejects frequency samples +corresponding to the sort of adjustment which might occur if someone +changes the gold standard kernel clock by several seconds. + + Format ------ @@ -138,8 +195,8 @@ schemes may be used: ```c vec_add1 (result, 0) - or - result = format (result, "%c", 0); + or + result = format (result, "%c", 0); ``` Remember to vec\_free() the result if appropriate. Be careful not to @@ -161,8 +218,8 @@ format specification. For example: format\_junk() can invoke other user-format functions if desired. The programmer shoulders responsibility for argument type-checking. It is -typical for user format functions to blow up if the va\_arg(va, -type) macros don't match the caller's idea of reality. +typical for user format functions to blow up spectacularly if the +va\_arg(va, type) macros don't match the caller's idea of reality. Unformat -------- @@ -185,20 +242,132 @@ follows: Then loop parsing individual elements: ```c - while (unformat_check_input (&input) != UNFORMAT_END_OF_INPUT) + while (unformat_check_input (&input) != UNFORMAT_END_OF_INPUT) { if (unformat (&input, "value1 %d", &value1)) ;/* unformat sets value1 */ else if (unformat (&input, "value2 %d", &value2) ;/* unformat sets value2 */ else - return clib_error_return (0, "unknown input '%U'", + return clib_error_return (0, "unknown input '%U'", format_unformat_error, input); } ``` As with format, unformat implements a user-unformat function capability -via a "%U" user unformat function scheme. +via a "%U" user unformat function scheme. Generally, one can trivially +transform "format (s, "foo %d", foo) -> "unformat (input, "foo %d", &foo)". + +Unformat implements a couple of handy non-scanf-like format specifiers: + +```c + unformat (input, "enable %=", &enable, 1 /* defaults to 1 */); + unformat (input, "bitzero %|", &mask, (1<<0)); + unformat (input, "bitone %|", &mask, (1<<1)); + +``` + +The phrase "enable %=" means "set the supplied variable to the default +value" if unformat parses the "enable" keyword all by itself. If +unformat parses "enable 123" set the supplied variable to 123. + +We could clean up a number of hand-rolled "verbose" + "verbose %d" +argument parsing codes using "%=". + +The phrase "bitzero %|" means "set the specified bit in the supplied +bitmask" if unformat parses "bitzero". Although it looks like it could +be fairly handy, it's very lightly used in the code base. + +### How to parse a single input line + +Debug CLI command functions MUST NOT accidentally consume input +belonging to other debug CLI commands. Otherwise, it's impossible to +script a set of debug CLI commands which "work fine" when issued one +at a time. + +This bit of code is NOT correct: + +```c + /* Eats script input NOT beloging to it, and chokes! */ + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, ...)) + ; + else if (unformat (input, ...)) + ; + else + return clib_error_return (0, "parse error: '%U'", + format_unformat_error, input); + } + } +``` + +When executed as part of a script, such a function will return "parse +error: ''" every time, unless it happens to be the +last command in the script. + +Instead, use "unformat_line_input" to consume the rest of a line's +worth of input - everything past the path specified in the +VLIB_CLI_COMMAND declaration. + +For example, unformat_line_input with "my_command" set up as shown +below and user input "my path is clear" will produce an +unformat_input_t that contains "is clear". + +```c + VLIB_CLI_COMMAND (...) = { + .path = "my path", + }; +``` + +Here's a bit of code which shows the required mechanics, in full: + +```c + static clib_error_t * + my_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) + { + unformat_input_t _line_input, *line_input = &_line_input; + u32 this, that; + clib_error_t *error = 0; + + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + /* + * Here, UNFORMAT_END_OF_INPUT is at the end of the line we consumed, + * not at the end of the script... + */ + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "this %u", &this)) + ; + else if (unformat (line_input, "that %u", &that)) + ; + else + { + error = clib_error_return (0, "parse error: '%U'", + format_unformat_error, line_input); + goto done; + } + } + + + + done: + unformat_free (line_input); + return error; + } + /* *INDENT-OFF* */ + VLIB_CLI_COMMAND (my_command, static) = { + .path = "my path", + .function = my_command_fn", + }; + /* *INDENT-ON* */ + +``` + Vppinfra errors and warnings ----------------------------