Receive Side Scaling: the Toeplitz flow hash and per-owner steering
0

Configure Feed

Select the types of activity you want to include in your feed.

rss: align the flow-id codec and add a buckets knob

Match the flow-id encoding a share-nothing stack needs before adopting the
package: 1-based (((local-1)*count)+owner+1) so an edge-owned negative
identifier routes by its magnitude, and let owner take the RSS indirection
table size (buckets) rather than fixing it at 128.

+24 -16
+10 -7
lib/rss.ml
··· 59 59 let check_count who count = 60 60 if count < 1 then invalid_arg (who ^ ": count must be positive") 61 61 62 - (* The default RSS indirection: a 128-entry table filled [bucket i -> i], the 63 - hash's low 7 bits picking the bucket, folded onto [count] owners. *) 64 - let owner ~count hash = 62 + (* The default RSS indirection: a [buckets]-entry table filled [bucket i -> i], 63 + the hash's low [log2 buckets] bits picking the bucket, folded onto [count] 64 + owners. *) 65 + let owner ?(buckets = 128) ~count hash = 65 66 check_count "Rss.owner" count; 66 - hash land 0x7f mod count 67 + if not (buckets > 0 && buckets land (buckets - 1) = 0) then 68 + invalid_arg "Rss.owner: buckets must be a power of two"; 69 + hash land (buckets - 1) mod count 67 70 68 71 let dynamic_port_first = 49152 69 72 let dynamic_port_count = 65536 - dynamic_port_first ··· 90 93 91 94 let encode_flow_id ~count ~owner local = 92 95 check_owner "Rss.encode_flow_id" count owner; 93 - if local <= 0 then local else (local * count) + owner 96 + if local <= 0 then local else ((local - 1) * count) + owner + 1 94 97 95 98 let decode_flow_id ~count global = 96 99 check_count "Rss.decode_flow_id" count; 97 - if global <= 0 then global else global / count 100 + if global <= 0 then global else ((global - 1) / count) + 1 98 101 99 102 let owner_of_flow_id ~count global = 100 103 check_count "Rss.owner_of_flow_id" count; 101 - if global <= 0 then 0 else global mod count 104 + if global > 0 then (global - 1) mod count else (-global - 1) mod count
+11 -8
lib/rss.mli
··· 45 45 46 46 (** {1 Owner selection} *) 47 47 48 - val owner : count:int -> int -> int 48 + val owner : ?buckets:int -> count:int -> int -> int 49 49 (** [owner ~count hash] reduces a 32-bit [hash] to one of [count] owners 50 - (queues, cores, shards) through a 128-entry RSS indirection, so a 51 - well-distributed hash spreads evenly. 52 - @raise Invalid_argument if [count] is not positive. *) 50 + (queues, cores, shards) through a [buckets]-entry RSS indirection (default 51 + [128], a power of two), so a well-distributed hash spreads evenly. 52 + @raise Invalid_argument 53 + if [count] is not positive or [buckets] is not a power of two. *) 53 54 54 55 (** {1 Ephemeral-port lanes (RFC 6335)} *) 55 56 ··· 75 76 76 77 val encode_flow_id : count:int -> owner:int -> int -> int 77 78 (** [encode_flow_id ~count ~owner local] embeds [owner] in a positive [local] 78 - identifier so it is globally unique across owners; a non-positive [local] 79 - (an edge-owned sentinel) passes through unchanged. *) 79 + identifier ([((local - 1) * count) + owner + 1]) so it is globally unique 80 + across owners; a non-positive [local] (an edge-owned sentinel) passes 81 + through unchanged. *) 80 82 81 83 val decode_flow_id : count:int -> int -> int 82 84 (** [decode_flow_id ~count global] recovers the owner-local identifier from 83 85 {!encode_flow_id}. *) 84 86 85 87 val owner_of_flow_id : count:int -> int -> int 86 - (** [owner_of_flow_id ~count global] recovers the owner embedded in [global] by 87 - {!encode_flow_id] ([0] for a non-positive, edge-owned identifier). *) 88 + (** [owner_of_flow_id ~count global] recovers the owner of [global]: the owner 89 + {!encode_flow_id} embedded when [global] is positive, or the owner an 90 + edge-owned negative identifier belongs to ([(-global - 1) mod count]). *)
+3 -1
test/test_rss.ml
··· 122 122 done; 123 123 Alcotest.(check int) 124 124 "non-positive id passes through" (-1) 125 - (Rss.encode_flow_id ~count ~owner:0 (-1)) 125 + (Rss.encode_flow_id ~count ~owner:0 (-1)); 126 + (* an edge-owned negative identifier is routed by its magnitude *) 127 + Alcotest.(check int) "negative id owner" 2 (Rss.owner_of_flow_id ~count (-3)) 126 128 127 129 let suite = 128 130 ( "rss",