Skip to content
Permalink
Browse files

Merge #1375 branch 'snabbco/max-next' into next

  • Loading branch information...
lukego committed Aug 6, 2018
2 parents 4569546 + c65c1f4 commit a154efca91ab64640189cb1b95b29dcda64d6722
@@ -9,7 +9,7 @@ arriving on a free input port may be forwarded to all other output
ports. Packets arriving on an input port that belongs to a split-horizon
group are never forwarded to any output port belonging to the same
split-horizon group. There are two `bridge` implementations available:
`apps.bridge.flooding` and apps.bridge.learning`.
`apps.bridge.flooding` and `apps.bridge.learning`.

DIAGRAM: bridge
+----------+
@@ -19,7 +19,8 @@ local asm_status = ffi.new("uint32_t[1]")
|.arch x64
|.actionlist actions
local Dst = dasm.new(actions)
| mov dword [asm_status], 0xdeadbeef
| mov64 rax, asm_status
| mov dword [rax], 0xdeadbeef
| ret
code = Dst:build() -- assign to 'code' to avoid machine code being GC'd
fptr = ffi.cast("void(*)()", code)
@@ -10,7 +10,9 @@ included). The exporter can produce output in either the standard RFC

DIAGRAM: IPFIX
+-----------+
| |
input ---->* IPFIX *----> output
| |
+-----------+
See the `snabb ipfix probe` command-line interface for a program built
@@ -457,84 +457,8 @@ function root_check (message)
end
end

-- Simple token bucket for rate-limiting of events. A token bucket is
-- created through
--
-- local tb = token_bucket_new({ rate = <rate> })
--
-- where <rate> is the maximum allowed rate in Hz, which defaults to
-- 10. Conceptually, <rate> tokens are added to the bucket each
-- second and the bucket can hold no more than <rate> tokens but at
-- least one.
--

local token_bucket = {}
token_bucket.mt = { __index = token_bucket }
token_bucket.default = { rate = 10 }
function token_bucket_new (config)
local config = config or token_bucket.default
local tb = setmetatable({}, token_bucket.mt)
tb:rate(config.rate or token_bucket.default.rate)
tb._tstamp = C.get_monotonic_time()
return tb
end

-- The rate can be set with the rate() method at any time, which fills
-- the token bucket an also returns the previous value. If called
-- with a nil argument, returns the currently configured rate.
function token_bucket:rate (rate)
if rate ~= nil then
local old_rate = self._rate
self._rate = rate
self._max_tokens = math.max(rate, 1)
self._tokens = self._max_tokens
return old_rate
end
return self._rate
end

function token_bucket:_update (tokens)
local now = C.get_monotonic_time()
local tokens = math.min(self._max_tokens, tokens + self._rate*(now-self._tstamp))
self._tstamp = now
return tokens
end

-- The take() method tries to remove <n> tokens from the bucket. If
-- enough tokens are available, they are subtracted from the bucket
-- and a true value is returned. Otherwise, the bucket remains
-- unchanged and a false value is returned. For efficiency, the
-- tokens accumulated since the last call to take() or can_take() are
-- only added if the request can not be fulfilled by the state of the
-- bucket when the method is called.
function token_bucket:take (n)
local n = n or 1
local result = false
local tokens = self._tokens
if n > tokens then
tokens = self:_update(tokens)
end
if n <= tokens then
tokens = tokens - n
result = true
end
self._tokens = tokens
return result
end

-- The can_take() method returns a true value if the bucket contains
-- at least <n> tokens, false otherwise. The bucket is updated in a
-- layz fashion as described for the take() method.
function token_bucket:can_take (n)
local n = n or 1
local tokens = self._tokens
if n <= tokens then
return true
end
tokens = self:_update(tokens)
self._tokens = tokens
return n <= tokens
end
-- Backward compatibility
token_bucket_new = require("lib.token_bucket").new

-- Simple rate-limited logging facility. Usage:
--
@@ -0,0 +1,70 @@
### Token Bucket (lib.token_bucket)

This module implements a [token
bucket](https://en.wikipedia.org/wiki/Token_bucket) for rate-limiting
of arbitrary events. The bucket is filled with tokens at a constant
rate up to a given maximum called the *burst_size*. Tokens are added
and removed in integer quantities. An event can only take place if at
least one token is available. A burst of back-to-back events is
allowed to happen by consuming all available tokens at a given point
in time. The maximum size of such a burst is determined by the
capacity of the bucket, hence the name *burst_size*.

The token bucket is updated in a lazy fashion, i.e. only when a
request for tokens cannot be satisfied immediately.

By default, a token bucket uses the `rdtsc` time source via the
[`tsc`](./README.tsc.md) module to minimise overhead. To override,
the `default_source` parameter of the `tsc` module must be set
to the desired value.

#### Functions

— Function **new** *config*

Creates an instance of a token bucket. The required *config* argument
must be a table with the following keys.

— Key **rate**

*Required*. The rate in units of Hz at which tokens are placed in the
bucket as an arbitrary floating point number larger than zero.

— Key **burst_size**

*Optional*. The maximum number of tokens that can be stored in the
bucket. The default is **rate** tokens, i.e. the amount of tokens
accumulated over one second rounded up to the next integer.

#### Methods

The object returned by the **new** function provides the following
methods.

— Method **token_bucket:set** [*rate*], [*burst_size*]

Set the rate and burst size to the values *rate* and *burst_size*,
respectively, and fill the bucket to capacity. If *rate* is `nil`,
the rate remains unchanged. If *burst_size* is `nil`, the burst size
is set to the number of tokens that will be accumulated over one
second with the new rate (like in the **new** function).

— Method **token_bucket:get**

Returns the current rate and burst size.

— Method **token_bucket:can_take** [*n*]

Returns `true` if at least *n* tokens are available, `false`
otherwise. If *n* is `nil`, the bucket is checked for a single token.

— Method **token_bucket:take** [*n*]

If at least *n* tokens are available, they are removed from the bucket
and the method returns `true`. Otherwise, the bucket remains
unchanged and `false` is returned. If *n* is `nil`, the bucket is
checked for a single token.

— Method **token_bucket:take_burst**

Takes all available tokens from the bucket and returns that number.
@@ -0,0 +1,112 @@
### Time Stamp Counter (lib.tsc)

A Time Stamp Counter (TSC) is an unsigned 64-bit value which increases
at a fixed frequency. The latter property provides a measure of the
time that has passed between two readings of the counter in units of
clock ticks.

To convert a value in clock ticks to the corresponding number of
seconds requires a measurement of the actual frequency at which the
TSC runs. This is referred to as calibration. The `tsc` module
provides a uniform interface to TSCs based on different time sources.

Example usage
```lua
local tsc = require('lib.tsc')
local sleep = require("ffi").C.sleep
local function wait(t)
print("source " .. t:source())
local s1 = t:stamp()
sleep(1)
local s2 = t:stamp()
print(("clock ticks %s, nanoseconds %s"):format(tostring(s2 - s1),
tostring(t:to_ns(s2 - s1))))
end
wait(tsc.new())
-- Override default
tsc.default_source = 'system'
wait(tsc.new())
```

#### Parameters

Parameters can be set by

```lua
local tsc = require('lib.tsc')
tsc.<parameter> = <value>
```

— Parameter **default_source** *source*

The time source used by a new TSC instance if no **source** key is
specified. The default is `rdtsc`.

#### Functions

— Function **new** *config*

Create a new TSC instance. The optional *config* argument is a table
with the following keys.

— Key **source**

*Optional*. The name of the timing source to be used with this
instance. The following sources are available. The default is `rdtsc`
(or whatever has been set by the `default_source` parameter).

* `system`

This source uses `clock_gettime(2)` with `CLOCK_MONOTONIC` provided
by `ffi.C.get_time_ns()`. The frequency is exactly 1e9 Hz,
i.e. one tick per nanosecond.

* `rdtsc`

This source uses the [TSC](https://en.wikipedia.org/wiki/Time_Stamp_Counter) CPU
register via the `rdtsc` instruction, provided that the platform
supports the `constant_tsc` and `nonstop_tsc` features. If these
features are not present, a warning is printed and the TSC falls
back to the `system` time source.

The TSC register is consistent for all cores of a CPU. However,
the calling program is responsible for setting the CPU affinity on
multi-socket systems.

The `system` time source is used for calibration.

— Function **rdtsc**

Returns the current value of the CPU's TSC register through the
`rdtsc` instruction as a `uint64_t` object.

#### Methods

The object returned by the **new** function provides the following
methods.

— Method **tsc:source**

Returns the name of the time source, i.e. `rdtsc` or `system`.

— Method **tsc:time_fn**

Returns the function used to generate a time stamp for the configured
time source, which returns the value of the TSC as a `uint64_t`
object.

— Method **tsc:stamp**

Returns the current value of the TSC as a `uint64_t`. It is
equivalent to the call `tsc:time_fn()()`.

— Method **tsc:tps**

Returns the number of clock ticks per second as a `uint64_t`.

— Method **tsc:to_ns**, *ticks*

Returns *ticks* converted from clock ticks to nanoseconds as a
`uint64_t`. This method should be avoided in low-latency code paths
due to conversions from/to Lua numbers.
@@ -172,7 +172,7 @@ local function SSE(stride)
function asm.init(key)
local initial_state = load_initial_state(key)
table.insert(__anchor, initial_state)
| mov rax, initial_state
| mov64 rax, initial_state
| movddup xmm0, [rax]
| movddup xmm1, [rax+8]
| movddup xmm2, [rax+16]
@@ -253,7 +253,7 @@ local function AVX2(stride)
local initial_state = load_initial_state(key)
table.insert(__anchor, initial_state)
| vzeroupper
| mov rax, initial_state
| mov64 rax, initial_state
| vbroadcastsd ymm0, qword [rax]
| vbroadcastsd ymm1, qword [rax+8]
| vbroadcastsd ymm2, qword [rax+16]
@@ -345,7 +345,7 @@ local function AVX2(stride)
-- write out.
local control = ffi.new('uint32_t[8]', 1, 3, 5, 7, 0, 0, 0, 0)
table.insert(__anchor, control)
| mov rax, control
| mov64 rax, control
| vmovdqu ymm5, [rax]
| vpermd ymm(reg), ymm5, ymm(reg)
| vpslld xmm(reg), xmm(reg), 1
@@ -58,7 +58,7 @@ function gen(count, size)
tail_mask = ffi.new("uint8_t[32]")
for i=0,tail_size-1 do tail_mask[i]=255 end
table.insert(anchor, tail_mask)
| mov rax, tail_mask
| mov64 rax, tail_mask
| vmovdqu ymm15, [rax]
end

@@ -16,7 +16,8 @@ local icmp = subClass(header)
-- Class variables
icmp._name = "icmp"
icmp._ulp = {
class_map = { [135] = "lib.protocol.icmp.nd.ns",
class_map = { [2] = "lib.protocol.icmp.ptb",
[135] = "lib.protocol.icmp.nd.ns",
[136] = "lib.protocol.icmp.nd.na" },
method = "type" }
icmp:init(
@@ -0,0 +1,37 @@
-- Use of this source code is governed by the Apache 2.0 license; see COPYING.

module(..., package.seeall)
local ffi = require("ffi")
local C = ffi.C
local lib = require("core.lib")
local proto_header = require("lib.protocol.header")

local ptb = subClass(proto_header)

-- Class variables
ptb._name = "packet too big"
ptb._ulp = { method = nil }
proto_header.init(ptb,
{
-- The original packet follows the mtu. Because
-- it is of variable size, it is considered as
-- payload rather than part of the ICMP message
-- so it can be retrieved with the datagram
-- payload() method.
[1] = ffi.typeof[[
struct {
uint32_t mtu;
} __attribute__((packed))
]]
})

-- Instance methods

function ptb:mtu (mtu)
if mtu ~= nil then
self:header().mtu = lib.htonl(mtu)
end
return lib.ntohl(self:header().mtu)
end

return ptb
Oops, something went wrong.

0 comments on commit a154efc

Please sign in to comment.
You can’t perform that action at this time.