← Documents Documentation/bpf/map_lru_hash_update.dot GitHub 원문 ↗

Linux 6.18.37 · BPF

BPF LRU 해시 맵 갱신 상태 전이

BPF LRU 해시 맵 갱신이 현재 CPU의 freelist부터 global inactive·active 목록과 원격 CPU 캐시까지 노드를 회수하고, 최종 hashmap 갱신 또는 errno로 끝나는 상태 전이를 해설합니다.

Source pathDocumentation/bpf/map_lru_hash_update.dot
Source versionLinux v6.18.37
TranslationDUJINLABS 전문 번역 + 해설

요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.

1. 요약·해설

원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.

요약·해설

map_lru_hash_update.dot:1-172

BPF LRU 해시 맵 갱신이 현재 CPU의 freelist부터 global inactive·active 목록과 원격 CPU 캐시까지 노드를 회수하고, 최종 hashmap 갱신 또는 errno로 끝나는 상태 전이를 해설합니다.

색상 1-4는 local CPU LRU, LRU, hashtab, remote CPU LRU 잠금을 각각 뜻합니다. 점선 CPU 클러스터는 노드 확보에 성공하거나 모든 CPU를 소진할 때까지 반복됩니다.

LRU 해시 갱신 요약
현재 CPU local freeglobal inactive·active원격 CPU free·pendinghashmap 갱신0 또는 errno

가까운 캐시부터 더 넓은 회수 범위로 확장한 뒤 하나의 갱신 지점에 합류합니다.

2. 영어 원문 전체

번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.

원문 전체 펼치기
1 // SPDX-License-Identifier: GPL-2.0-only
2 // Copyright (C) 2022-2023 Isovalent, Inc.
3 digraph {
4 node [colorscheme=accent4,style=filled] # Apply colorscheme to all nodes
5 graph [splines=ortho, nodesep=1]
6
7 subgraph cluster_key {
8 label = "Key\n(locks held during operation)";
9 rankdir = TB;
10
11 remote_lock [shape=rectangle,fillcolor=4,label="remote CPU LRU lock"]
12 hash_lock [shape=rectangle,fillcolor=3,label="hashtab lock"]
13 lru_lock [shape=rectangle,fillcolor=2,label="LRU lock"]
14 local_lock [shape=rectangle,fillcolor=1,label="local CPU LRU lock"]
15 no_lock [shape=rectangle,label="no locks held"]
16 }
17
18 begin [shape=oval,label="begin\nbpf_map_update()"]
19
20 // Nodes below with an 'fn_' prefix are roughly labeled by the C function
21 // names that initiate the corresponding logic in kernel/bpf/bpf_lru_list.c.
22 // Number suffixes and errno suffixes handle subsections of the corresponding
23 // logic in the function as of the writing of this dot.
24
25 // cf. __local_list_pop_free() / bpf_percpu_lru_pop_free()
26 local_freelist_check [shape=diamond,fillcolor=1,
27 label="Local freelist\nnode available?"];
28 use_local_node [shape=rectangle,
29 label="Use node owned\nby this CPU"]
30
31 // cf. bpf_lru_pop_free()
32 common_lru_check [shape=diamond,
33 label="Map created with\ncommon LRU?\n(!BPF_F_NO_COMMON_LRU)"];
34
35 fn_bpf_lru_list_pop_free_to_local [shape=rectangle,fillcolor=2,
36 label="Flush local pending,
37 Rotate Global list, move
38 target_free
39 from global -> local"]
40 // Also corresponds to:
41 // fn__local_list_flush()
42 // fn_bpf_lru_list_rotate()
43 fn___bpf_lru_node_move_to_free[shape=diamond,fillcolor=2,
44 label="Able to free\ntarget_free\nnodes?"]
45
46 fn___bpf_lru_list_shrink_inactive [shape=rectangle,fillcolor=3,
47 label="Shrink inactive list
48 up to remaining
49 target_free
50 (global LRU -> local)"]
51 fn___bpf_lru_list_shrink [shape=diamond,fillcolor=2,
52 label="> 0 entries in\nlocal free list?"]
53 fn___bpf_lru_list_shrink2 [shape=rectangle,fillcolor=2,
54 label="Steal one node from
55 inactive, or if empty,
56 from active global list"]
57 fn___bpf_lru_list_shrink3 [shape=rectangle,fillcolor=3,
58 label="Try to remove\nnode from hashtab"]
59
60 local_freelist_check2 [shape=diamond,label="Htab removal\nsuccessful?"]
61 common_lru_check2 [shape=diamond,
62 label="Map created with\ncommon LRU?\n(!BPF_F_NO_COMMON_LRU)"];
63
64 subgraph cluster_remote_lock {
65 label = "Iterate through CPUs\n(start from current)";
66 style = dashed;
67 rankdir=LR;
68
69 local_freelist_check5 [shape=diamond,fillcolor=4,
70 label="Steal a node from\nper-cpu freelist?"]
71 local_freelist_check6 [shape=rectangle,fillcolor=4,
72 label="Steal a node from
73 (1) Unreferenced pending, or
74 (2) Any pending node"]
75 local_freelist_check7 [shape=rectangle,fillcolor=3,
76 label="Try to remove\nnode from hashtab"]
77 fn_htab_lru_map_update_elem [shape=diamond,
78 label="Stole node\nfrom remote\nCPU?"]
79 fn_htab_lru_map_update_elem2 [shape=diamond,label="Iterated\nall CPUs?"]
80 // Also corresponds to:
81 // use_local_node()
82 // fn__local_list_pop_pending()
83 }
84
85 fn_bpf_lru_list_pop_free_to_local2 [shape=rectangle,
86 label="Use node that was\nnot recently referenced"]
87 local_freelist_check4 [shape=rectangle,
88 label="Use node that was\nactively referenced\nin global list"]
89 fn_htab_lru_map_update_elem_ENOMEM [shape=oval,label="return -ENOMEM"]
90 fn_htab_lru_map_update_elem3 [shape=rectangle,
91 label="Use node that was\nactively referenced\nin (another?) CPU's cache"]
92 fn_htab_lru_map_update_elem4 [shape=rectangle,fillcolor=3,
93 label="Update hashmap\nwith new element"]
94 fn_htab_lru_map_update_elem5 [shape=oval,label="return 0"]
95 fn_htab_lru_map_update_elem_EBUSY [shape=oval,label="return -EBUSY"]
96 fn_htab_lru_map_update_elem_EEXIST [shape=oval,label="return -EEXIST"]
97 fn_htab_lru_map_update_elem_ENOENT [shape=oval,label="return -ENOENT"]
98
99 begin -> local_freelist_check
100 local_freelist_check -> use_local_node [xlabel="Y"]
101 local_freelist_check -> common_lru_check [xlabel="N"]
102 common_lru_check -> fn_bpf_lru_list_pop_free_to_local [xlabel="Y"]
103 common_lru_check -> fn___bpf_lru_list_shrink_inactive [xlabel="N"]
104 fn_bpf_lru_list_pop_free_to_local -> fn___bpf_lru_node_move_to_free
105 fn___bpf_lru_node_move_to_free ->
106 fn_bpf_lru_list_pop_free_to_local2 [xlabel="Y"]
107 fn___bpf_lru_node_move_to_free ->
108 fn___bpf_lru_list_shrink_inactive [xlabel="N"]
109 fn___bpf_lru_list_shrink_inactive -> fn___bpf_lru_list_shrink
110 fn___bpf_lru_list_shrink -> fn_bpf_lru_list_pop_free_to_local2 [xlabel = "Y"]
111 fn___bpf_lru_list_shrink -> fn___bpf_lru_list_shrink2 [xlabel="N"]
112 fn___bpf_lru_list_shrink2 -> fn___bpf_lru_list_shrink3
113 fn___bpf_lru_list_shrink3 -> local_freelist_check2
114 local_freelist_check2 -> local_freelist_check4 [xlabel = "Y"]
115 local_freelist_check2 -> common_lru_check2 [xlabel = "N"]
116 common_lru_check2 -> local_freelist_check5 [xlabel = "Y"]
117 common_lru_check2 -> fn_htab_lru_map_update_elem_ENOMEM [xlabel = "N"]
118 local_freelist_check5 -> fn_htab_lru_map_update_elem [xlabel = "Y"]
119 local_freelist_check5 -> local_freelist_check6 [xlabel = "N"]
120 local_freelist_check6 -> local_freelist_check7
121 local_freelist_check7 -> fn_htab_lru_map_update_elem
122
123 fn_htab_lru_map_update_elem -> fn_htab_lru_map_update_elem3 [xlabel = "Y"]
124 fn_htab_lru_map_update_elem -> fn_htab_lru_map_update_elem2 [xlabel = "N"]
125 fn_htab_lru_map_update_elem2 ->
126 fn_htab_lru_map_update_elem_ENOMEM [xlabel = "Y"]
127 fn_htab_lru_map_update_elem2 -> local_freelist_check5 [xlabel = "N"]
128 fn_htab_lru_map_update_elem3 -> fn_htab_lru_map_update_elem4
129
130 use_local_node -> fn_htab_lru_map_update_elem4
131 fn_bpf_lru_list_pop_free_to_local2 -> fn_htab_lru_map_update_elem4
132 local_freelist_check4 -> fn_htab_lru_map_update_elem4
133
134 fn_htab_lru_map_update_elem4 -> fn_htab_lru_map_update_elem5 [headlabel="Success"]
135 fn_htab_lru_map_update_elem4 ->
136 fn_htab_lru_map_update_elem_EBUSY [xlabel="Hashtab lock failed"]
137 fn_htab_lru_map_update_elem4 ->
138 fn_htab_lru_map_update_elem_EEXIST [xlabel="BPF_EXIST set and\nkey already exists"]
139 fn_htab_lru_map_update_elem4 ->
140 fn_htab_lru_map_update_elem_ENOENT [headlabel="BPF_NOEXIST set\nand no such entry"]
141
142 // Create invisible pad nodes to line up various nodes
143 pad0 [style=invis]
144 pad1 [style=invis]
145 pad2 [style=invis]
146 pad3 [style=invis]
147 pad4 [style=invis]
148
149 // Line up the key with the top of the graph
150 no_lock -> local_lock [style=invis]
151 local_lock -> lru_lock [style=invis]
152 lru_lock -> hash_lock [style=invis]
153 hash_lock -> remote_lock [style=invis]
154 remote_lock -> local_freelist_check5 [style=invis]
155 remote_lock -> fn___bpf_lru_list_shrink [style=invis]
156
157 // Line up return code nodes at the bottom of the graph
158 fn_htab_lru_map_update_elem -> pad0 [style=invis]
159 pad0 -> pad1 [style=invis]
160 pad1 -> pad2 [style=invis]
161 //pad2-> fn_htab_lru_map_update_elem_ENOMEM [style=invis]
162 fn_htab_lru_map_update_elem4 -> pad3 [style=invis]
163 pad3 -> fn_htab_lru_map_update_elem5 [style=invis]
164 pad3 -> fn_htab_lru_map_update_elem_EBUSY [style=invis]
165 pad3 -> fn_htab_lru_map_update_elem_EEXIST [style=invis]
166 pad3 -> fn_htab_lru_map_update_elem_ENOENT [style=invis]
167
168 // Reduce diagram width by forcing some nodes to appear above others
169 local_freelist_check4 -> fn_htab_lru_map_update_elem3 [style=invis]
170 common_lru_check2 -> pad4 [style=invis]
171 pad4 -> local_freelist_check5 [style=invis]
172 }
173

3. 한국어 전문 번역

영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.

로컬·공통·원격 CPU LRU에서 갱신 노드를 확보하는 경로

1-172

이 Graphviz DOT 원문은 `bpf_map_update()`가 BPF LRU 해시 맵에 새 원소를 넣을 때 사용할 노드를 확보하고 해시 테이블을 갱신하는 흐름을 나타냅니다. 먼저 현재 CPU의 로컬 freelist를 확인하고, 노드가 없으면 공통 LRU 여부와 전역 목록 회수 결과에 따라 inactive·active 목록 또는 다른 CPU의 캐시까지 탐색합니다.

`fn_` 접두사가 붙은 노드는 `kernel/bpf/bpf_lru_list.c`에서 해당 논리를 시작하는 C 함수 이름을 대략 반영합니다. 숫자 접미사와 `ENOMEM`, `EBUSY`, `EEXIST`, `ENOENT` 접미사는 DOT 작성 당시 한 함수 안의 서로 다른 하위 구간과 반환 지점을 구분하기 위한 그래프 식별자입니다.

색은 노드 작업 중 보유하는 잠금을 뜻합니다. `accent4` 색상 1은 local CPU LRU lock, 2는 LRU lock, 3은 hashtab lock, 4는 remote CPU LRU lock이며 채움색을 따로 지정하지 않은 기능 노드는 잠금을 보유하지 않는 경로로 읽습니다. 점선 CPU 클러스터는 현재 CPU부터 모든 CPU를 순회하는 반복 영역입니다.

로컬 빠른 경로와 공통 LRU 회수
bpf_map_update()로컬 freelist 노드 있음현재 CPU 소유 노드 사용새 원소로 hashmap 갱신
bpf_map_update()로컬 freelist 비어 있음공통 LRU 사용local pending flushglobal list rotatetarget_free: global -> local해제 가능최근 참조되지 않은 노드 사용

현재 CPU가 즉시 노드를 제공하지 못하면 공통 LRU의 pending·global 목록을 정리하고 목표 노드를 로컬로 옮깁니다.

inactive·active 목록 축소 경로
inactive를 남은 target_free만큼 축소local free list에 항목 있음최근 참조되지 않은 노드 사용hashmap 갱신
inactive를 남은 target_free만큼 축소local free list 비어 있음inactive 또는 active global에서 한 노드 탈취hashtab에서 노드 제거 성공global active 참조 노드 사용hashmap 갱신
hashtab 제거 실패공통 LRU 아님return -ENOMEM

공통 이동이 충분하지 않거나 공통 LRU가 아닌 경우 inactive 목록과 active 목록에서 후보를 회수합니다.

원격 CPU 순회 경로
CPU 순회 시작per-CPU freelist에서 노드 탈취원격 CPU 노드 확보다른 CPU 캐시의 active 참조 노드 사용hashmap 갱신
per-CPU freelist 실패미참조 pending 우선, 없으면 임의 pending 탈취hashtab에서 제거원격 CPU 노드 확보hashmap 갱신
원격 CPU 노드 확보 실패모든 CPU 순회 완료return -ENOMEM
원격 CPU 노드 확보 실패남은 CPU 있음다음 CPU의 per-CPU freelist로 반복

공통 LRU 경로에서 로컬 회수가 실패하면 현재 CPU부터 CPU별 freelist와 pending 목록을 차례로 검사합니다.

최종 해시 맵 갱신 결과
Update hashmap with new elementSuccessreturn 0
Update hashmap with new elementHashtab lock failedreturn -EBUSY
Update hashmap with new elementBPF_EXIST set and key already existsreturn -EEXIST
Update hashmap with new elementBPF_NOEXIST set and no such entryreturn -ENOENT

어느 경로에서든 노드를 확보하면 동일한 갱신 노드로 합류하고 네 가지 종료 결과로 나뉩니다.

잠금 색상 범례
fillcolor잠금대표 노드
4remote CPU LRU lock원격 CPU per-CPU freelist·pending 탈취
3hashtab lockinactive 축소, hashtab 제거, 최종 hashmap 갱신
2LRU lockglobal rotate·move, free list 검사, inactive/active 탈취
1local CPU LRU lock현재 CPU local freelist 검사
별도 색 없음no locks held분기 판단, 확보 노드 사용, 반환

원문 7-16줄의 범례를 보유 잠금과 색상 번호로 옮깁니다.

주요 C 함수 대응 노드
DOT 노드관련 함수·주석처리
local_freelist_check`__local_list_pop_free()` / `bpf_percpu_lru_pop_free()`현재 CPU 로컬 free 노드 확인
common_lru_check`bpf_lru_pop_free()``!BPF_F_NO_COMMON_LRU` 조건 확인
fn_bpf_lru_list_pop_free_to_local`__local_list_flush()` / `bpf_lru_list_rotate()`pending flush, global rotate, target_free 이동
fn___bpf_lru_node_move_to_free`__bpf_lru_node_move_to_free()`target_free 노드를 실제로 해제할 수 있는지 판단
fn___bpf_lru_list_shrink_inactive`__bpf_lru_list_shrink_inactive()`inactive 목록을 남은 목표 수만큼 축소
fn___bpf_lru_list_shrink`__bpf_lru_list_shrink()`local free list의 항목 존재 여부 확인
fn___bpf_lru_list_shrink2같은 shrink 처리의 다음 구간inactive 우선, 비었으면 active global에서 한 노드 탈취
fn___bpf_lru_list_shrink3같은 shrink 처리의 제거 구간후보 노드를 hashtab에서 제거 시도
fn_htab_lru_map_update_elem`htab_lru_map_update_elem()` 하위 구간원격 CPU에서 노드를 얻었는지 판단
fn_htab_lru_map_update_elem4최종 갱신 구간확보한 노드로 새 hashmap 원소 갱신

그래프 식별자와 원문 라벨이 나타내는 `kernel/bpf/bpf_lru_list.c` 처리 구간입니다.

확보한 노드의 출처
노드원문 라벨합류 경로
use_local_nodeUse node owned by this CPU최초 local freelist 검사 성공
fn_bpf_lru_list_pop_free_to_local2Use node that was not recently referencedglobal 이동 또는 inactive 축소 뒤 local free 확보
local_freelist_check4Use node that was actively referenced in global listinactive/active 후보를 hashtab에서 제거 성공
fn_htab_lru_map_update_elem3Use node that was actively referenced in (another?) CPU's cache원격 CPU freelist 또는 pending에서 노드 확보

최종 `fn_htab_lru_map_update_elem4`로 합류하는 네 종류의 노드를 구분합니다.

반환 노드와 원문 분기 라벨
조건 라벨반환설명
Success0새 원소 갱신 성공
Hashtab lock failed`-EBUSY`hashtab 잠금 획득 실패
BPF_EXIST set and key already exists`-EEXIST`원문 DOT의 플래그·키 상태 라벨을 그대로 옮김
BPF_NOEXIST set and no such entry`-ENOENT`원문 DOT의 플래그·키 상태 라벨을 그대로 옮김
모든 노드 회수 경로 실패`-ENOMEM`사용할 LRU 노드를 확보하지 못함

최종 갱신 노드에서 나가는 라벨과 errno를 원문 그대로 보존합니다.

DOT 구조 수량
구성수량범위
선언 노드 전체36잠금 범례 5 + 기능 노드 26 + pad 노드 5
결정 노드9`shape=diamond`인 Y/N 분기
종료·시작 노드6시작 1 + 반환 5의 `shape=oval`
가시 상태 전이33원문 99-140줄의 실제 처리 흐름
비가시 배치 간선17범례·반환·폭 정렬용 `style=invis` 간선
pad 노드5`pad0`부터 `pad4`까지의 렌더링 전용 노드

기능 흐름과 렌더링 배치용 요소를 분리한 전용 감사 기준입니다.

원문 줄 좌표
원문 줄구성한국어 해설
1-5라이선스와 그래프 기본값GPL-2.0-only, accent4 채움, 직교 간선과 노드 간격을 선언합니다.
7-16cluster_key작업 중 보유 잠금의 네 색과 무잠금 상태를 정의합니다.
18-33시작·명명 규칙·로컬 검사`bpf_map_update()` 시작과 local freelist, common LRU 결정을 선언합니다.
35-62공통·global 목록 회수pending flush, rotate, target_free 이동, inactive/active 축소와 hashtab 제거를 정의합니다.
64-83cluster_remote_lock현재 CPU부터 원격 CPU freelist와 pending을 순회하는 점선 클러스터입니다.
85-97노드 사용과 반환 선언노드 출처 네 종류, 최종 hashmap 갱신, 다섯 반환 노드를 정의합니다.
99-121로컬·global·원격 진입 간선Y/N 결과에 따라 로컬 회수에서 원격 CPU 순회까지 연결합니다.
123-140원격 반복과 최종 반환 간선CPU 순회 반복, 갱신 합류, 성공 및 errno 분기를 연결합니다.
142-147pad0-pad4노드 정렬에만 쓰는 비가시 패딩 노드를 선언합니다.
149-171비가시 배치 간선범례 상단 정렬, 반환 하단 정렬, 그래프 폭 축소를 강제합니다.
172그래프 닫기`digraph` 블록을 끝냅니다.

172줄 DOT 원문을 의미와 렌더링 역할별로 나눕니다.

아래 코드는 Linux v6.18.37 원문 1-172줄 전체입니다. 표시 안정성을 위해 탭만 여덟 칸으로 정규화했으며 노드 ID, C 함수명, label, Y/N·errno 분기, 색상, 클러스터, 비가시 pad와 간선, 줄 순서는 변경하지 않았습니다.

// SPDX-License-Identifier: GPL-2.0-only
// Copyright (C) 2022-2023 Isovalent, Inc.
digraph {
  node [colorscheme=accent4,style=filled] # Apply colorscheme to all nodes
  graph [splines=ortho, nodesep=1]

  subgraph cluster_key {
    label = "Key\n(locks held during operation)";
    rankdir = TB;

    remote_lock [shape=rectangle,fillcolor=4,label="remote CPU LRU lock"]
    hash_lock [shape=rectangle,fillcolor=3,label="hashtab lock"]
    lru_lock [shape=rectangle,fillcolor=2,label="LRU lock"]
    local_lock [shape=rectangle,fillcolor=1,label="local CPU LRU lock"]
    no_lock [shape=rectangle,label="no locks held"]
  }

  begin [shape=oval,label="begin\nbpf_map_update()"]

  // Nodes below with an 'fn_' prefix are roughly labeled by the C function
  // names that initiate the corresponding logic in kernel/bpf/bpf_lru_list.c.
  // Number suffixes and errno suffixes handle subsections of the corresponding
  // logic in the function as of the writing of this dot.

  // cf. __local_list_pop_free() / bpf_percpu_lru_pop_free()
  local_freelist_check [shape=diamond,fillcolor=1,
    label="Local freelist\nnode available?"];
  use_local_node [shape=rectangle,
    label="Use node owned\nby this CPU"]

  // cf. bpf_lru_pop_free()
  common_lru_check [shape=diamond,
    label="Map created with\ncommon LRU?\n(!BPF_F_NO_COMMON_LRU)"];

  fn_bpf_lru_list_pop_free_to_local [shape=rectangle,fillcolor=2,
    label="Flush local pending,
    Rotate Global list, move
    target_free
    from global -> local"]
  // Also corresponds to:
  // fn__local_list_flush()
  // fn_bpf_lru_list_rotate()
  fn___bpf_lru_node_move_to_free[shape=diamond,fillcolor=2,
    label="Able to free\ntarget_free\nnodes?"]

  fn___bpf_lru_list_shrink_inactive [shape=rectangle,fillcolor=3,
    label="Shrink inactive list
      up to remaining
      target_free
      (global LRU -> local)"]
  fn___bpf_lru_list_shrink [shape=diamond,fillcolor=2,
    label="> 0 entries in\nlocal free list?"]
  fn___bpf_lru_list_shrink2 [shape=rectangle,fillcolor=2,
    label="Steal one node from
      inactive, or if empty,
      from active global list"]
  fn___bpf_lru_list_shrink3 [shape=rectangle,fillcolor=3,
    label="Try to remove\nnode from hashtab"]

  local_freelist_check2 [shape=diamond,label="Htab removal\nsuccessful?"]
  common_lru_check2 [shape=diamond,
    label="Map created with\ncommon LRU?\n(!BPF_F_NO_COMMON_LRU)"];

  subgraph cluster_remote_lock {
    label = "Iterate through CPUs\n(start from current)";
    style = dashed;
    rankdir=LR;

    local_freelist_check5 [shape=diamond,fillcolor=4,
      label="Steal a node from\nper-cpu freelist?"]
    local_freelist_check6 [shape=rectangle,fillcolor=4,
      label="Steal a node from
        (1) Unreferenced pending, or
        (2) Any pending node"]
    local_freelist_check7 [shape=rectangle,fillcolor=3,
      label="Try to remove\nnode from hashtab"]
    fn_htab_lru_map_update_elem [shape=diamond,
      label="Stole node\nfrom remote\nCPU?"]
    fn_htab_lru_map_update_elem2 [shape=diamond,label="Iterated\nall CPUs?"]
    // Also corresponds to:
    // use_local_node()
    // fn__local_list_pop_pending()
  }

  fn_bpf_lru_list_pop_free_to_local2 [shape=rectangle,
    label="Use node that was\nnot recently referenced"]
  local_freelist_check4 [shape=rectangle,
    label="Use node that was\nactively referenced\nin global list"]
  fn_htab_lru_map_update_elem_ENOMEM [shape=oval,label="return -ENOMEM"]
  fn_htab_lru_map_update_elem3 [shape=rectangle,
    label="Use node that was\nactively referenced\nin (another?) CPU's cache"]
  fn_htab_lru_map_update_elem4 [shape=rectangle,fillcolor=3,
    label="Update hashmap\nwith new element"]
  fn_htab_lru_map_update_elem5 [shape=oval,label="return 0"]
  fn_htab_lru_map_update_elem_EBUSY [shape=oval,label="return -EBUSY"]
  fn_htab_lru_map_update_elem_EEXIST [shape=oval,label="return -EEXIST"]
  fn_htab_lru_map_update_elem_ENOENT [shape=oval,label="return -ENOENT"]

  begin -> local_freelist_check
  local_freelist_check -> use_local_node [xlabel="Y"]
  local_freelist_check -> common_lru_check [xlabel="N"]
  common_lru_check -> fn_bpf_lru_list_pop_free_to_local [xlabel="Y"]
  common_lru_check -> fn___bpf_lru_list_shrink_inactive [xlabel="N"]
  fn_bpf_lru_list_pop_free_to_local -> fn___bpf_lru_node_move_to_free
  fn___bpf_lru_node_move_to_free ->
    fn_bpf_lru_list_pop_free_to_local2 [xlabel="Y"]
  fn___bpf_lru_node_move_to_free ->
    fn___bpf_lru_list_shrink_inactive [xlabel="N"]
  fn___bpf_lru_list_shrink_inactive -> fn___bpf_lru_list_shrink
  fn___bpf_lru_list_shrink -> fn_bpf_lru_list_pop_free_to_local2 [xlabel = "Y"]
  fn___bpf_lru_list_shrink -> fn___bpf_lru_list_shrink2 [xlabel="N"]
  fn___bpf_lru_list_shrink2 -> fn___bpf_lru_list_shrink3
  fn___bpf_lru_list_shrink3 -> local_freelist_check2
  local_freelist_check2 -> local_freelist_check4 [xlabel = "Y"]
  local_freelist_check2 -> common_lru_check2 [xlabel = "N"]
  common_lru_check2 -> local_freelist_check5 [xlabel = "Y"]
  common_lru_check2 -> fn_htab_lru_map_update_elem_ENOMEM [xlabel = "N"]
  local_freelist_check5 -> fn_htab_lru_map_update_elem [xlabel = "Y"]
  local_freelist_check5 -> local_freelist_check6 [xlabel = "N"]
  local_freelist_check6 -> local_freelist_check7
  local_freelist_check7 -> fn_htab_lru_map_update_elem

  fn_htab_lru_map_update_elem -> fn_htab_lru_map_update_elem3 [xlabel = "Y"]
  fn_htab_lru_map_update_elem -> fn_htab_lru_map_update_elem2  [xlabel = "N"]
  fn_htab_lru_map_update_elem2 ->
    fn_htab_lru_map_update_elem_ENOMEM [xlabel = "Y"]
  fn_htab_lru_map_update_elem2 -> local_freelist_check5 [xlabel = "N"]
  fn_htab_lru_map_update_elem3 -> fn_htab_lru_map_update_elem4

  use_local_node -> fn_htab_lru_map_update_elem4
  fn_bpf_lru_list_pop_free_to_local2 -> fn_htab_lru_map_update_elem4
  local_freelist_check4 -> fn_htab_lru_map_update_elem4

  fn_htab_lru_map_update_elem4 -> fn_htab_lru_map_update_elem5 [headlabel="Success"]
  fn_htab_lru_map_update_elem4 ->
    fn_htab_lru_map_update_elem_EBUSY [xlabel="Hashtab lock failed"]
  fn_htab_lru_map_update_elem4 ->
    fn_htab_lru_map_update_elem_EEXIST [xlabel="BPF_EXIST set and\nkey already exists"]
  fn_htab_lru_map_update_elem4 ->
    fn_htab_lru_map_update_elem_ENOENT [headlabel="BPF_NOEXIST set\nand no such entry"]

  // Create invisible pad nodes to line up various nodes
  pad0 [style=invis]
  pad1 [style=invis]
  pad2 [style=invis]
  pad3 [style=invis]
  pad4 [style=invis]

  // Line up the key with the top of the graph
  no_lock -> local_lock [style=invis]
  local_lock -> lru_lock [style=invis]
  lru_lock -> hash_lock [style=invis]
  hash_lock -> remote_lock [style=invis]
  remote_lock -> local_freelist_check5 [style=invis]
  remote_lock -> fn___bpf_lru_list_shrink [style=invis]

  // Line up return code nodes at the bottom of the graph
  fn_htab_lru_map_update_elem -> pad0 [style=invis]
  pad0 -> pad1 [style=invis]
  pad1 -> pad2 [style=invis]
  //pad2-> fn_htab_lru_map_update_elem_ENOMEM [style=invis]
  fn_htab_lru_map_update_elem4 -> pad3 [style=invis]
  pad3 -> fn_htab_lru_map_update_elem5  [style=invis]
  pad3 -> fn_htab_lru_map_update_elem_EBUSY  [style=invis]
  pad3 -> fn_htab_lru_map_update_elem_EEXIST  [style=invis]
  pad3 -> fn_htab_lru_map_update_elem_ENOENT  [style=invis]

  // Reduce diagram width by forcing some nodes to appear above others
  local_freelist_check4 -> fn_htab_lru_map_update_elem3 [style=invis]
  common_lru_check2 -> pad4 [style=invis]
  pad4 -> local_freelist_check5 [style=invis]
}