projectsroutingproposal

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
projectsroutingproposal [2015/08/07 19:34] – AlexanderChernikovprojectsroutingproposal [2016/01/18 19:23] (current) – glebius
Line 1: Line 1:
-This article outlines some routing-related problems in our network stack and describes some solutions proposed in projects/routing+This article outlines some generic routing-related problems in our network stack and describes the solutions proposed in projects/routing 
 ==== Current problems ==== ==== Current problems ====
 === Performance issues === === Performance issues ===
-Many people considered routing lookups slow (and created various mechanisms of avoiding such lookups, like route caching in tunnels, or having per-flow hash record (FLOWTABLE%%))%%.+Many people considered routing lookups slow (and created various mechanisms of avoiding such lookups, like route caching in tunnels (or even protocols), 
 +or having per-flow hash record (FLOWTABLE%%))%%.
  
 Typical packet processing (forwarding for router, or output for web server) path consists of: Typical packet processing (forwarding for router, or output for web server) path consists of:
-  * doing routing lookup (radix read rwlock + rte mutex lock) +  * doing routing lookup (radix read rwlock + routing entry (rte) mutex lock) 
-  * (optionally) ifa atomic refcount acquire/release +  * (optionally) interface address (ifa) atomic refcount acquire/release 
-  * doing LLE lookup (afdata read rwlock + llentry read (or write) lock) +  * doing link level entry (lle, llentry) lookup (afdata read rwlock + llentry read (or write) lock) 
-Actually, given number of locks is roughly like having single Giant lock: single-cpu performance (600-1mpps) is very similar to the results you can get using modern dual-cpu system with 16/32 cores. The most expensive operation is acquiring //struct rtentry// mutex (think about default route delivering HUGE contention on single lock, which is also hold for plenty of time). However, other stuff do heavily impact processing path - we saw small (10-20%) performance increases while eliminating more and more locks. Removing last (non-batched) atomic-based lock from processing path gave us more than 2x improvement. +Actually, given number of locks is roughly like having single Giant lock: single-cpu performance (600-1mpps) is very similar to the results you can get 
 +using modern dual-cpu system with 16/32 cores. The most expensive operation is acquiring //struct rtentry// mutex (think about default route delivering 
 +HUGE contention on single lock, which is also hold for plenty of time). However, other locks do heavily impact processing path.
 === Routing code itself === === Routing code itself ===
 Dealing with performance issues revealed number of problems which also have to be dealt with: Dealing with performance issues revealed number of problems which also have to be dealt with:
Line 25: Line 28:
 ==== Proposed solution ==== ==== Proposed solution ====
 Most of the performance issues described in "Problems" sections were addressed in [[https://svnweb.freebsd.org/base/projects/routing/sys|projects/routing]] branch (some already merged to HEAD). Most of the performance issues described in "Problems" sections were addressed in [[https://svnweb.freebsd.org/base/projects/routing/sys|projects/routing]] branch (some already merged to HEAD).
-These changes permits us to scale close-to linear based on number of cores: we were able to reach close to 12MPPS for IPv6 forwarding (2xE2660, 15 cores) which is 8-10 times faster than current benchmarks. +We managed to remove rwlocks/mutexes from hot path, so these changes permits us to scale close-to linear based on number of cores. 
-It should also be noted, that, in general, (current) changes available in project/routing are not about performance as is: they just _permit_ us to start performance-oriented work with regular benchmarks, etc - since tremendous number of locks does not permit us measure anything.+In fact, we were able to reach close to 12MPPS for IPv4 forwarding (2xE2660, 8 cores) which is 5-10 times faster than current benchmarks.
  
 +==== Some benchmarks ====
 +=== Forwarding benchmark ===
 +{{projectsroutingproposal/ipv4_routing_1.png}}
 +  * "routing" represents projects/routing branch results
 +  * "unlocked" represents projects/routing branch with route/llable rmlock calls commented out
 +  * "linear" represents linear appoximation of single-core projects/routing results
 +  * "head" represetns HEAD@r287996 branch
 +=== Forwarding/locking benhmark ===
 +{{projectsroutingproposal/lock_fwd.png}}
 +  * "rwlocked" represents HEAD w/o lle read lock/rtentry lock (lltable/radix are rwlocked)
 +  * "rmlocked" represents HEAD w/o lle read lock/rtentry lock (lltable/radix are rmlocked)
 +  * "rmlocked" represents HEAD w/o lle read lock/rtentry lock (lltable/radix are also not locked) (e.g. this is performance w/o locking impact
 +  * "cached" represents HEAD w/ lle & rtentry passed to ip_fastwrd (e.g. this is performance w/o locking AND w/o radix/lltable lookup impact)
 +  * "linear" represents linear appoximation of single-core "cached" results
 ==== High-level overview ==== ==== High-level overview ====
 The key concept is hiding all routing internals and providing pre-computed easy-to-use result to all consumers with minimal locking. The key concept is hiding all routing internals and providing pre-computed easy-to-use result to all consumers with minimal locking.
-Additionally, we provide separate functions for each address family (fib4/rib4/fib6/rib6).+We provide separate functions for each address family (fib4/fib6/rib).
 We also provide several different (in terms of returned info and relative overhead) methods of retrieving routing data. We also provide several different (in terms of returned info and relative overhead) methods of retrieving routing data.
-Typically, lookup result is copied to caller-provided 64-bytes nhop_* structure, with all necessary data _including_ the data that needs to be prepended to mbuf (currently - pre-computed L2 header). +Typically, lookup result is pre-computed next hop data, copied to caller-provided 64-bytes nhop_* structure, with (optionally) all necessary data _including_ the data that needs to be prepended to mbuf (currently - pre-computed L2 header).
- +
-New api/structutes: [[https://svnweb.freebsd.org/base/projects/routing/sys/net/rt_nhops.c?view=markup|rt_nhops.c]] [[https://svnweb.freebsd.org/base/projects/routing/sys/net/rt_nhops.h?view=markup|rt_nhops.h]] +
- +
-It is worth noting that all API/internal function were converted from sockaddr to plain addresses/addresses + scopes. +
-While it might have seen as a good "generic" approach in 80's, it is not really the case. After converting most kernel consumers to new API I'd say that per-AF functions and address-based approach is really simpler and easier to use. The only example of the opposite was some code in NFS. Additionally, sizeof(struct sockaddr_in6%%))%% once seen should make anyone avoid using it as much as possible. +
- +
-Another related change is avoiding IPv6 addresses with embedded scope: you could not be sure when the IP address has its scope embedded. Additionally, set/clearscope functions are REALLY complicated and might acquire locks. So, API/internals were designed to explicitly pass scopeid as another parameter.+
  
 === Locking changes === === Locking changes ===
- +  * Route table is [[https://svnweb.freebsd.org/base?view=revision&revision=287078|switched]] to ipfw-like dual-locking mode: fast path is protected with rmlock, configuration changes requires to acquire cfg rwlock AND write rmlock.
-  * Radix is [[https://svnweb.freebsd.org/base?view=revision&revision=274336|switched]] to ipfw-like dual-locking mode: fast path is protected with rmlock, configuration changes requires ackquiring cfg rwlock AND write rmlock.+
   * rte mutexes are mostly unused leading to single rmlock necessary for L3 lookup   * rte mutexes are mostly unused leading to single rmlock necessary for L3 lookup
-  * LLE if_afdata lock uses the same dual-locking approach.+  * LLE if_afdata lock uses the same dual-locking approach. (there are plans to move lltable lock inside lltable)
   * Additionally, number of steps are taken to avoid acquiring lle rwlock as much as we can   * Additionally, number of steps are taken to avoid acquiring lle rwlock as much as we can
 +
 +New api/structures are described separately in [[ProjectsRoutingProposal/API|API]] page.
  
 === More details on routing === === More details on routing ===
Line 53: Line 64:
   - L4 level code (like TCP, trying to extract some useful info like MTU) Here we might need to provide things like MTU or some L4-specific data saved in given rtenry.   - L4 level code (like TCP, trying to extract some useful info like MTU) Here we might need to provide things like MTU or some L4-specific data saved in given rtenry.
   - Forwarding code (_output() routines) Here again we need very basic info, so we can use optimised fib table lookups.   - Forwarding code (_output() routines) Here again we need very basic info, so we can use optimised fib table lookups.
 +  - Control plane code (rtsock, address routines) Use slower rib-based procedures returning individual rtentries
  
 === More details on LLE === === More details on LLE ===
   * LLTABLE was changed to provide more callbacks (like add/del/dump/hash/enumerate items) without enforcing particular storage (e.g. hash table) model.   * LLTABLE was changed to provide more callbacks (like add/del/dump/hash/enumerate items) without enforcing particular storage (e.g. hash table) model.
   * struct llentry is now basically split into 2 pieces: all fields within 64 bytes (amd64) are now protected by both ifdata lock AND lle lock e.g. you require both locks to be held exclusively for modification. All data necessary for fast path operations is kept here. other control-specific fields are protected by lle lock   * struct llentry is now basically split into 2 pieces: all fields within 64 bytes (amd64) are now protected by both ifdata lock AND lle lock e.g. you require both locks to be held exclusively for modification. All data necessary for fast path operations is kept here. other control-specific fields are protected by lle lock
-  * Immediate delete is now restricted (since you have to acquire afdata wlock which is now always possible) - delayed delete added instead +  * simple state machine was added to ARP: in most cases (unless record is going to expire in several seconds) we can simply use lle without side effects. However, prior to expire time we have to get some sort of feedback from data path indicating if entry is really used. This is done by (mutex-locked) setting .r_skip_verify field to 0, which is checked in timer code 
-  * simple state machine was added to ARP: in most cases (unless record is going to expire in several seconds) we can simply use lle without side effects. However, prior to expire time we have to get some sort of feedback from data path indicating if entry is really used. This is done by (unlocked) setting .used field to 1, which is checked in timer code +  * ND state machine started to be more complex: same trick with .r_skip_verify field was applied there
-  * ND state machine started to be more complex: same trick with .used field was applied there+
  
 ==== Regressions ==== ==== Regressions ====
Line 67: Line 78:
 ==== Merge plan ==== ==== Merge plan ====
 Basic plan looks like the following: Basic plan looks like the following:
-  - Merge most of LLE changes not related to locking/timers (e.g. all new lltable callbacks/enumerators/etc) +  - <del>Merge most of LLE changes not related to locking/timers (e.g. all new lltable callbacks/enumerators/etc)</del> 
-  - Discuss/merge LLE locking/timers stuff +  - <del>Discuss/merge LLE locking/timers stuff [[https://reviews.freebsd.org/D3688|D3688]] [[https://reviews.freebsd.org/D3780|D3780]]</del> 
-  - Merge new routing lookup API +  - Perform one-by-one conversion of non-critical route(4) consumers [PARTIALLY DONE] 
-  - Perform one-by-one conversion of route(4) consumers+  - Discuss/merge new routing lookup API for data path 
 +  - Discuss/merge lltable locking (afdata rwlock -> lltable rmlock+rwlock)
   - Discuss/merge routing locking changes   - Discuss/merge routing locking changes
  
-=== Considerations/Open questions ===+==== Status ==== 
 +Current conversion status is available [[ProjectsRoutingProposal/ConversionStatus|here]]. 
 +==== Considerations/Open questions ====
   - Wider use of rmlock in kernel. Rmlock has side effect of "pinning" current thread which might lead to more severe consequences when using (contested) mutexes/rwlocks. Here we try to avoid acquiring any additional locks as much as possible (no locks for routing lookups, rare lookups for LLE stuff). However, more stuff could be done (for example, explicitly avoid acquiring lock when sending LOTS of traffic to single dead host with no L2 lle info). Additionally, write lock costs a LOT more, so some sort of batching should be considered for inserting kernel routes/lle entries.   - Wider use of rmlock in kernel. Rmlock has side effect of "pinning" current thread which might lead to more severe consequences when using (contested) mutexes/rwlocks. Here we try to avoid acquiring any additional locks as much as possible (no locks for routing lookups, rare lookups for LLE stuff). However, more stuff could be done (for example, explicitly avoid acquiring lock when sending LOTS of traffic to single dead host with no L2 lle info). Additionally, write lock costs a LOT more, so some sort of batching should be considered for inserting kernel routes/lle entries.
-  - Is the current LLE datapath "feedback" method (e.g. if (->used == 0) ->used = 1) "safe" to use and what are the alternatives if not. 
   - Could we teach PFIL to start looking at mbuf starting from certain offset? (eliminate 2x LLE header copy)   - Could we teach PFIL to start looking at mbuf starting from certain offset? (eliminate 2x LLE header copy)
   - "slowpath" netisr queue: we'd better delay handling of some packets (like first packet being sent to host, triggering creation of LLE record) to separate netisr queue to batch such cases (since LLE link requires WLOCK)   - "slowpath" netisr queue: we'd better delay handling of some packets (like first packet being sent to host, triggering creation of LLE record) to separate netisr queue to batch such cases (since LLE link requires WLOCK)
   - Should be insert link-local routes into RIB? Typical routing lookup now looks like the following: "What is egress interface/gw for link-local address X and embedded scope Y?" "You know, it is interface route and the egress interface is Y!"   - Should be insert link-local routes into RIB? Typical routing lookup now looks like the following: "What is egress interface/gw for link-local address X and embedded scope Y?" "You know, it is interface route and the egress interface is Y!"
  
-==== Next steps ==== +==== Future plans ==== 
-  * Implement auto-growing array for per-radix nexthops+  * Implement auto-growing array for per-fib-per-af nexthops
   * Implement LLE tracking for nexthop calculations to be able to immediately provide all necessary info in single route lookup for gateway routes   * Implement LLE tracking for nexthop calculations to be able to immediately provide all necessary info in single route lookup for gateway routes
   * Implement multipath support for rtsock   * Implement multipath support for rtsock
   * Implement "tracked nexthops" for tunnels (e.g. _proper_ nexthop caching) permitting single-lookup result for tunnel traffic   * Implement "tracked nexthops" for tunnels (e.g. _proper_ nexthop caching) permitting single-lookup result for tunnel traffic
-  * Implement API for modular per-AF lookup (think about sorted array of addresses for single-interface-with-default boxes, or IPv6 hash-based routing, or special algos like DIR-24-8 or DXR for holding full-view)    +  * Implement API for modular per-AF FIB lookup (think about sorted array of prefixes for single-interface-with-default boxes, or IPv6 hash-based routing, or special algos like DIR-24-8 or DXR for holding full-view)
  
  • projectsroutingproposal.1438976087.txt.gz
  • Last modified: 2015/08/07 19:34
  • by AlexanderChernikov