tc: support conversions to or from 64 bit nanosecond-based time

Using a 32 bit field to represent time in nanoseconds results in a
maximum value of about 4.3 seconds, which is well below many observed
delays in WiFi and LTE, and barely in the ballpark for a trip past the
Earth's moon, Luna.

Using 64 bit time fields in nanoseconds allows us to simulate
network diameters of several hundred light-years. However, only
conversions to and from ns, us, ms, and seconds are provided.

The iproute2 64 bit api uses signed values for time. Being able to
represent positive or negative time allows us to calculate +/- deltas
between, for example, the CLOCK_TAI and CLOCK_REALTIME clocks.

Time related utility functions in tc_util.c are moved to lib/utils.c.

Signed-off-by: Yousuk Seung <ysseung@google.com>
Signed-off-by: Dave Taht <dave.taht@gmail.com>
Signed-off-by: Neal Cardwell <ncardwell@google.com>
Signed-off-by: David Ahern <dsahern@gmail.com>
This commit is contained in:
Dave Taht 2018-08-26 19:42:28 -07:00 committed by David Ahern
parent c4e0ea8e9b
commit abf70ef494
8 changed files with 116 additions and 51 deletions

View File

@ -46,6 +46,11 @@ void incomplete_command(void) __attribute__((noreturn));
#define NEXT_ARG_FWD() do { argv++; argc--; } while(0)
#define PREV_ARG() do { argv--; argc++; } while(0)
#define TIME_UNITS_PER_SEC 1000000
#define NSEC_PER_USEC 1000
#define NSEC_PER_MSEC 1000000
#define NSEC_PER_SEC 1000000000LL
typedef struct
{
__u16 flags;
@ -310,4 +315,11 @@ size_t strlcat(char *dst, const char *src, size_t size);
void drop_cap(void);
int get_time(unsigned int *time, const char *str);
int get_time64(__s64 *time, const char *str);
void print_time(char *buf, int len, __u32 time);
void print_time64(char *buf, int len, __s64 time);
char *sprint_time(__u32 time, char *buf);
char *sprint_time64(__s64 time, char *buf);
#endif /* __UTILS_H__ */

View File

@ -1633,3 +1633,104 @@ void drop_cap(void)
}
#endif
}
int get_time(unsigned int *time, const char *str)
{
double t;
char *p;
t = strtod(str, &p);
if (p == str)
return -1;
if (*p) {
if (strcasecmp(p, "s") == 0 || strcasecmp(p, "sec") == 0 ||
strcasecmp(p, "secs") == 0)
t *= TIME_UNITS_PER_SEC;
else if (strcasecmp(p, "ms") == 0 || strcasecmp(p, "msec") == 0 ||
strcasecmp(p, "msecs") == 0)
t *= TIME_UNITS_PER_SEC/1000;
else if (strcasecmp(p, "us") == 0 || strcasecmp(p, "usec") == 0 ||
strcasecmp(p, "usecs") == 0)
t *= TIME_UNITS_PER_SEC/1000000;
else
return -1;
}
*time = t;
return 0;
}
void print_time(char *buf, int len, __u32 time)
{
double tmp = time;
if (tmp >= TIME_UNITS_PER_SEC)
snprintf(buf, len, "%.1fs", tmp/TIME_UNITS_PER_SEC);
else if (tmp >= TIME_UNITS_PER_SEC/1000)
snprintf(buf, len, "%.1fms", tmp/(TIME_UNITS_PER_SEC/1000));
else
snprintf(buf, len, "%uus", time);
}
char *sprint_time(__u32 time, char *buf)
{
print_time(buf, SPRINT_BSIZE-1, time);
return buf;
}
/* 64 bit times are represented internally in nanoseconds */
int get_time64(__s64 *time, const char *str)
{
double nsec;
char *p;
nsec = strtod(str, &p);
if (p == str)
return -1;
if (*p) {
if (strcasecmp(p, "s") == 0 ||
strcasecmp(p, "sec") == 0 ||
strcasecmp(p, "secs") == 0)
nsec *= NSEC_PER_SEC;
else if (strcasecmp(p, "ms") == 0 ||
strcasecmp(p, "msec") == 0 ||
strcasecmp(p, "msecs") == 0)
nsec *= NSEC_PER_MSEC;
else if (strcasecmp(p, "us") == 0 ||
strcasecmp(p, "usec") == 0 ||
strcasecmp(p, "usecs") == 0)
nsec *= NSEC_PER_USEC;
else if (strcasecmp(p, "ns") == 0 ||
strcasecmp(p, "nsec") == 0 ||
strcasecmp(p, "nsecs") == 0)
nsec *= 1;
else
return -1;
}
*time = nsec;
return 0;
}
void print_time64(char *buf, int len, __s64 time)
{
double nsec = time;
if (time >= NSEC_PER_SEC)
snprintf(buf, len, "%.3fs", nsec/NSEC_PER_SEC);
else if (time >= NSEC_PER_MSEC)
snprintf(buf, len, "%.3fms", nsec/NSEC_PER_MSEC);
else if (time >= NSEC_PER_USEC)
snprintf(buf, len, "%.3fus", nsec/NSEC_PER_USEC);
else
snprintf(buf, len, "%lldns", time);
}
char *sprint_time64(__s64 time, char *buf)
{
print_time64(buf, SPRINT_BSIZE-1, time);
return buf;
}

View File

@ -20,6 +20,7 @@
#include <arpa/inet.h>
#include <string.h>
#include "utils.h"
#include "tc_core.h"
#include "tc_cbq.h"

View File

@ -21,6 +21,7 @@
#include <arpa/inet.h>
#include <string.h>
#include "utils.h"
#include "tc_core.h"
#include <linux/atm.h>

View File

@ -5,8 +5,6 @@
#include <asm/types.h>
#include <linux/pkt_sched.h>
#define TIME_UNITS_PER_SEC 1000000
enum link_layer {
LINKLAYER_UNSPEC,
LINKLAYER_ETHERNET,

View File

@ -20,6 +20,7 @@
#include <arpa/inet.h>
#include <string.h>
#include "utils.h"
#include "tc_core.h"
int tc_setup_estimator(unsigned int A, unsigned int time_const, struct tc_estimator *est)

View File

@ -334,52 +334,6 @@ char *sprint_rate(__u64 rate, char *buf)
return buf;
}
int get_time(unsigned int *time, const char *str)
{
double t;
char *p;
t = strtod(str, &p);
if (p == str)
return -1;
if (*p) {
if (strcasecmp(p, "s") == 0 || strcasecmp(p, "sec") == 0 ||
strcasecmp(p, "secs") == 0)
t *= TIME_UNITS_PER_SEC;
else if (strcasecmp(p, "ms") == 0 || strcasecmp(p, "msec") == 0 ||
strcasecmp(p, "msecs") == 0)
t *= TIME_UNITS_PER_SEC/1000;
else if (strcasecmp(p, "us") == 0 || strcasecmp(p, "usec") == 0 ||
strcasecmp(p, "usecs") == 0)
t *= TIME_UNITS_PER_SEC/1000000;
else
return -1;
}
*time = t;
return 0;
}
void print_time(char *buf, int len, __u32 time)
{
double tmp = time;
if (tmp >= TIME_UNITS_PER_SEC)
snprintf(buf, len, "%.1fs", tmp/TIME_UNITS_PER_SEC);
else if (tmp >= TIME_UNITS_PER_SEC/1000)
snprintf(buf, len, "%.1fms", tmp/(TIME_UNITS_PER_SEC/1000));
else
snprintf(buf, len, "%uus", time);
}
char *sprint_time(__u32 time, char *buf)
{
print_time(buf, SPRINT_BSIZE-1, time);
return buf;
}
char *sprint_ticks(__u32 ticks, char *buf)
{
return sprint_time(tc_core_tick2time(ticks), buf);

View File

@ -81,13 +81,11 @@ int get_rate64(__u64 *rate, const char *str);
int get_percent_rate64(__u64 *rate, const char *str, const char *dev);
int get_size(unsigned int *size, const char *str);
int get_size_and_cell(unsigned int *size, int *cell_log, char *str);
int get_time(unsigned int *time, const char *str);
int get_linklayer(unsigned int *val, const char *arg);
void print_rate(char *buf, int len, __u64 rate);
void print_size(char *buf, int len, __u32 size);
void print_qdisc_handle(char *buf, int len, __u32 h);
void print_time(char *buf, int len, __u32 time);
void print_linklayer(char *buf, int len, unsigned int linklayer);
void print_devname(enum output_type type, int ifindex);
@ -95,7 +93,6 @@ char *sprint_rate(__u64 rate, char *buf);
char *sprint_size(__u32 size, char *buf);
char *sprint_qdisc_handle(__u32 h, char *buf);
char *sprint_tc_classid(__u32 h, char *buf);
char *sprint_time(__u32 time, char *buf);
char *sprint_ticks(__u32 ticks, char *buf);
char *sprint_linklayer(unsigned int linklayer, char *buf);