Page MenuHomeFreeBSD

libalias: Promote per instance global variable timeStamp
ClosedPublic

Authored by donner on May 31 2021, 11:43 AM.

Details

Summary
  • Use LibAliasTime as a real global variable for central timekeeping.
  • Reduce number of syscalls in user space considerably.
  • Dynamically adjust the packet counters to match the second resolution.
  • Only checkt the first few packets after a time increase for expiry.

Depends on D30277

Test Plan

Timings

before:

Running perfomance test with parameters:
  Maximum Runtime (max_seconds) = 10
  Amount of valid connections (batch_size) = 2000
  Amount of random, incoming packets (batch_size) = 1000
  Repeat count of a random, incoming packet (attack_size) = 1000
  Amount of open port forwardings (redir_size) = 2000

RND SECOND newNAT RANDOM ATTACK useNAT
  1    0.0   6.64   6.32   6.06   6.14
  2    1.0   6.77   6.16   6.03   6.24
  3    1.9   6.91   6.21   5.93   6.14
  4    2.8   7.05   6.32   6.05   6.21
  5    3.8   7.43   6.28   6.06   6.12
  6    4.7   7.16   6.16   6.09   6.13
  7    5.7   7.25   6.31   6.06   6.08
  8    6.6   7.78   6.89   6.11   6.14
  9    7.6   7.77   6.52   6.11   6.15
 10    8.5   7.75   6.37   5.94   6.07
 11    9.4   7.87   6.65   6.05

Results
   Rounds  :        10
newNAT ok  :     22000
newNAT fail:         0
useNAT ok  :    173623 (out)
useNAT fail:         0 (out)
useNAT ok  :   1406619 (in)
useNAT fail:         0 (in)
RANDOM ok  :       143
RANDOM fail:     10857
ATTACK ok  :         0
ATTACK fail:     11000
             ---------
      Total:   1624242

after:

Running perfomance test with parameters:
  Maximum Runtime (max_seconds) = 10
  Amount of valid connections (batch_size) = 2000
  Amount of random, incoming packets (batch_size) = 1000
  Repeat count of a random, incoming packet (attack_size) = 1000
  Amount of open port forwardings (redir_size) = 2000

RND SECOND newNAT RANDOM ATTACK useNAT
  1    0.0   0.55   0.18   0.06   0.10
  2    0.0   0.67   0.16   0.06   0.10
  3    0.0   0.74   0.19   0.06   0.10
  4    0.1   0.87   0.23   0.06   0.10
  5    0.1   0.97   0.24   0.06   0.10
  6    0.1   1.08   0.27   0.07   0.10
  7    0.1   1.50   0.32   0.08   0.10
  8    0.1   1.32   0.32   0.06   0.10
  9    0.1   1.38   0.35   0.07   0.10
 10    0.2   1.44   0.38   0.08   0.10
 ...
135    9.5  52.71  15.06   1.18   0.10
136    9.7  53.56  15.97   0.95   0.10
137    9.8  56.73  17.03   1.04   0.10
138   10.0

Results
   Rounds  :       137
newNAT ok  :    274593
newNAT fail:         0
useNAT ok  :   2266259 (out)
useNAT fail:         0 (out)
useNAT ok  :  18330354 (in)
useNAT fail:         0 (in)
RANDOM ok  :      1795
RANDOM fail:    135205
ATTACK ok  :         0
ATTACK fail:    137000
             ---------
      Total:  21145206

In user space, there is a speedup of factor 20 due to a reduce of
syscalls by 1:1000. This effect is not seen in kernel space, but I
can't test it, that easily.

Diff Detail

Repository
rG FreeBSD src repository
Lint
Automatic diff as part of commit; lint not applicable.
Unit
Automatic diff as part of commit; unit tests not applicable.

Event Timeline

  • Do not try time(3) for every packet over the limit, but only if a

Just putting my comment here.

Creating a pthread and caching the time, might be a better solution, from my point of view.

Just putting my comment here.

Creating a pthread and caching the time, might be a better solution, from my point of view.

Any idea how to do this in a library, which is used by arbitrary third party programs and inside kernel modules?

Just putting my comment here.

Creating a pthread and caching the time, might be a better solution, from my point of view.

Any idea how to do this in a library, which is used by arbitrary third party programs and inside kernel modules?

Yes. I can give you a full example, if needed.

In this example I use 32-bit timestamps, and then it is assumed that the code needs to handle wraparound.

#ifndef _KERNEL
int time_uptime;

static void *
time_cache(void *arg)
{
   while (1) {
    usleep(1000000);
    time_uptime = XXX;
   }
   return (NULL);
}
static void __attribute__((__constructor__))
init_function(void *arg)
{
    pthread_t pt;
    pthread_create(&pt, NULL, time_cache, NULL);
}
#endif
sys/netinet/libalias/alias_db.c
824

You need to use a cast here, to ensure the compiler handles time wraparound!

In this example I use 32-bit timestamps, and then it is assumed that the code needs to handle wraparound.

#ifndef _KERNEL
int time_uptime;

static void *
time_cache(void *arg)
{
   while (1) {
    usleep(1000000);
    time_uptime = XXX;
   }
   return (NULL);
}
static void __attribute__((__constructor__))
init_function(void *arg)
{
    pthread_t pt;
    pthread_create(&pt, NULL, time_cache, NULL);
}
#endif

Currently the code runs under a lock. With a coprocess the code needs to be either use atomic, or a separate lock which is used at all the places where the value is needed. Sounds really complicated, especially if the kernel and the userland code diverges further due to this change.

I hesitate to include such a solution during this restructure process. I'd open a separate review for this idea, okay?

sys/netinet/libalias/alias_db.c
824

LibAliasTime, .timestamp and .expire_time are of type int.
LibAliasTime is slightly larger than .timestamp and .expire_time is a small positive value.

At the wraparound of a two's complement value the following arithmetic apply:

   0x81 0b10000001 -127
 - 0x7f 0b01111111 +127
------------------------
   0x02 0b00000010

Can you please explain which type of cast do you refer to?

This revision was not accepted when it landed; it landed in state Needs Review.Jun 19 2021, 4:38 PM
This revision was automatically updated to reflect the committed changes.