1 /*
2 * %CopyrightBegin%
3 *
4 * Copyright Ericsson AB 2001-2020. All Rights Reserved.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 * %CopyrightEnd%
19 */
20
21 #ifndef ERL_NODE_TABLES_BASIC__
22 #define ERL_NODE_TABLES_BASIC__
23
24 typedef struct dist_entry_ DistEntry;
25 typedef struct ErtsDistOutputBuf_ ErtsDistOutputBuf;
26 typedef struct ErtsDistOutputBufsContainer_ ErtsDistOutputBufsContainer;
27 void erts_ref_dist_entry(DistEntry *dep);
28 void erts_deref_dist_entry(DistEntry *dep);
29
30 #endif
31
32 #if !defined(ERL_NODE_TABLES_BASIC_ONLY) && !defined(ERL_NODE_TABLES_H__)
33 #define ERL_NODE_TABLES_H__
34
35 /*
36 * The "node_tables module" contain two (hash) tables: the node_table
37 * and the dist_table.
38 *
39 * The elements of the node_table represents a specific incarnation of
40 * an Erlang node and has {Nodename, Creation} pairs as keys. Elements
41 * in the node_table are referred to from node containers (see
42 * node_container_utils.h).
43 *
44 * The elements of the dist_table represents a (potential) connection
45 * to an Erlang node and has Nodename as key. Elements in the
46 * dist_table are either referred to from elements in the node_table
47 * or from the process or port structure of the entity controlling
48 * the connection.
49 *
50 * Both tables are garbage collected by reference counting.
51 */
52
53 #include "sys.h"
54 #include "hash.h"
55 #include "erl_alloc.h"
56 #define ERTS_PORT_TASK_ONLY_BASIC_TYPES__
57 #include "erl_port_task.h"
58 #undef ERTS_PORT_TASK_ONLY_BASIC_TYPES__
59 #include "erl_process.h"
60 #define ERTS_BINARY_TYPES_ONLY__
61 #include "erl_binary.h"
62 #undef ERTS_BINARY_TYPES_ONLY__
63 #include "erl_monitor_link.h"
64
65 #define ERTS_NODE_TAB_DELAY_GC_DEFAULT (60)
66 #define ERTS_NODE_TAB_DELAY_GC_MAX (100*1000*1000)
67 #define ERTS_NODE_TAB_DELAY_GC_INFINITY (ERTS_NODE_TAB_DELAY_GC_MAX+1)
68
69 #define ERST_INTERNAL_CHANNEL_NO 0
70
71 enum dist_entry_state {
72 ERTS_DE_STATE_IDLE,
73 ERTS_DE_STATE_PENDING,
74 ERTS_DE_STATE_CONNECTED,
75 ERTS_DE_STATE_EXITING
76 };
77
78 #define ERTS_DE_QFLG_BUSY (((erts_aint32_t) 1) << 0)
79 #define ERTS_DE_QFLG_EXIT (((erts_aint32_t) 1) << 1)
80 #define ERTS_DE_QFLG_REQ_INFO (((erts_aint32_t) 1) << 2)
81 #define ERTS_DE_QFLG_PORT_CTRL (((erts_aint32_t) 1) << 3)
82 #define ERTS_DE_QFLG_PROC_CTRL (((erts_aint32_t) 1) << 4)
83
84 #define ERTS_DE_QFLGS_ALL (ERTS_DE_QFLG_BUSY \
85 | ERTS_DE_QFLG_EXIT \
86 | ERTS_DE_QFLG_REQ_INFO \
87 | ERTS_DE_QFLG_PORT_CTRL \
88 | ERTS_DE_QFLG_PROC_CTRL)
89
90 #if defined(ARCH_64)
91 #define ERTS_DIST_OUTPUT_BUF_DBG_PATTERN ((Uint) 0xf713f713f713f713UL)
92 #else
93 #define ERTS_DIST_OUTPUT_BUF_DBG_PATTERN ((Uint) 0xf713f713)
94 #endif
95
96 struct ErtsDistOutputBuf_ {
97 #ifdef DEBUG
98 Uint dbg_pattern;
99 #endif
100 ErtsDistOutputBuf *next;
101 Binary *bin;
102 /*
103 * iov[0] reserved for driver
104 * iov[1] reserved for distribution header
105 * iov[2 ... vsize-1] data
106 */
107 ErlIOVec *eiov;
108 };
109
110 struct ErtsDistOutputBufsContainer_ {
111 Sint fragments;
112 byte *extp;
113 ErtsDistOutputBuf obuf[1]; /* longer if fragmented... */
114 };
115
116 typedef struct {
117 ErtsDistOutputBuf *first;
118 ErtsDistOutputBuf *last;
119 } ErtsDistOutputQueue;
120
121 struct ErtsProcList_;
122
123 /*
124 * Lock order:
125 * 1. dist_entry->rwmtx
126 * 2. erts_node_table_rwmtx
127 * 3. erts_dist_table_rwmtx
128 *
129 * Lock mutexes with lower numbers before mutexes with higher numbers and
130 * unlock mutexes with higher numbers before mutexes with higher numbers.
131 */
132
133 struct dist_entry_ {
134 HashBucket hash_bucket; /* Hash bucket */
135 struct dist_entry_ *next; /* Next entry in dist_table (not sorted) */
136 struct dist_entry_ *prev; /* Previous entry in dist_table (not sorted) */
137
138 erts_rwmtx_t rwmtx; /* Protects all fields below until lck_mtx. */
139 Eterm sysname; /* name@host atom for efficiency */
140 Uint32 creation; /* creation of connected node */
141 erts_atomic_t input_handler; /* Input handler */
142 Eterm cid; /* connection handler (pid or port),
143 NIL == free */
144 Uint32 connection_id; /* Connection id incremented on connect */
145 enum dist_entry_state state;
146 int pending_nodedown;
147 Process* suspended_nodeup;
148 Uint64 dflags; /* Distribution flags, like hidden,
149 atom cache etc. */
150 Uint32 opts;
151
152 ErtsMonLnkDist *mld; /* Monitors and links */
153
154 erts_mtx_t qlock; /* Protects qflgs and out_queue */
155 erts_atomic32_t qflgs;
156 erts_atomic_t qsize;
157 erts_atomic64_t in;
158 erts_atomic64_t out;
159 ErtsDistOutputQueue out_queue;
160 struct ErtsProcList_ *suspended;
161
162 ErtsDistOutputQueue tmp_out_queue;
163 ErtsDistOutputQueue finalized_out_queue;
164 erts_atomic_t dist_cmd_scheduled;
165 ErtsPortTaskHandle dist_cmd;
166
167 Uint (*send)(Port *prt, ErtsDistOutputBuf *obuf);
168
169 struct cache* cache; /* The atom cache */
170
171 ErtsThrPrgrLaterOp later_op;
172
173 struct dist_sequences *sequences; /* Ongoing distribution sequences */
174 };
175
176 /*
177 #define ERL_NODE_BOOKKEEP
178 * Bookkeeping of ErlNode inc and dec operations to help debug refc problems.
179 * This is best used together with cerl -rr. Type the below into gdb:
180 * gdb:
181 set pagination off
182 set $i = 0
183 set $node = referred_nodes[$node_ix].node
184 while $i < $node->slot.counter
185 printf "%s:%d ", $node->books[$i].file, $node->books[$i].line
186 printf "%p: ", $node->books[$i].term
187 etp-1 $node->books[$i].who
188 printf " "
189 p $node->books[$i].what
190 set $i++
191 end
192
193 * Then save that into a file called test.txt and run the below in
194 * an erlang shell in order to get all inc/dec that do not have a
195 * match.
196
197 f(), {ok, B} = file:read_file("test.txt").
198 Vs = [begin [Val, _, _, _, What] = All = string:lexemes(Ln, " "),{Val,What,All} end || Ln <- string:lexemes(B,"\n")].
199 Accs = lists:foldl(fun({V,<<"ERL_NODE_INC">>,_},M) -> Val = maps:get(V,M,0), M#{ V => Val + 1 }; ({V,<<"ERL_NODE_DEC">>,_},M) -> Val = maps:get(V,M,0), M#{ V => Val - 1 } end, #{}, Vs).
200 lists:usort(lists:filter(fun({V,N}) -> N /= 0 end, maps:to_list(Accs))).
201
202 * There are bound to be bugs in the the instrumentation code, but
203 * atleast this is a place to start when hunting refc bugs.
204 *
205 */
206 #ifdef ERL_NODE_BOOKKEEP
207 struct erl_node_bookkeeping {
208 Eterm who;
209 Eterm term;
210 char *file;
211 int line;
212 enum { ERL_NODE_INC, ERL_NODE_DEC } what;
213 };
214
215 #define ERTS_BOOKKEEP_SIZE (1024)
216 #endif
217
218 typedef struct erl_node_ {
219 HashBucket hash_bucket; /* Hash bucket */
220 erts_refc_t refc; /* Reference count */
221 Eterm sysname; /* name@host atom for efficiency */
222 Uint32 creation; /* Creation */
223 DistEntry *dist_entry; /* Corresponding dist entry */
224 #ifdef ERL_NODE_BOOKKEEP
225 struct erl_node_bookkeeping books[ERTS_BOOKKEEP_SIZE];
226 erts_atomic_t slot;
227 #endif
228 } ErlNode;
229
230
231 extern Hash erts_dist_table;
232 extern Hash erts_node_table;
233 extern erts_rwmtx_t erts_dist_table_rwmtx;
234 extern erts_rwmtx_t erts_node_table_rwmtx;
235
236 extern DistEntry *erts_hidden_dist_entries;
237 extern DistEntry *erts_visible_dist_entries;
238 extern DistEntry *erts_pending_dist_entries;
239 extern DistEntry *erts_not_connected_dist_entries;
240 extern Sint erts_no_of_hidden_dist_entries;
241 extern Sint erts_no_of_visible_dist_entries;
242 extern Sint erts_no_of_pending_dist_entries;
243 extern Sint erts_no_of_not_connected_dist_entries;
244
245 extern DistEntry *erts_this_dist_entry;
246 extern ErlNode *erts_this_node;
247 extern char *erts_this_node_sysname; /* must match erl_node_tables.c */
248
249 Uint erts_delayed_node_table_gc(void);
250 DistEntry *erts_channel_no_to_dist_entry(Uint);
251 DistEntry *erts_sysname_to_connected_dist_entry(Eterm);
252 DistEntry *erts_find_or_insert_dist_entry(Eterm);
253 DistEntry *erts_find_dist_entry(Eterm);
254 void erts_schedule_delete_dist_entry(DistEntry *);
255 Uint erts_dist_table_size(void);
256 void erts_dist_table_info(fmtfn_t, void *);
257 void erts_set_dist_entry_not_connected(DistEntry *);
258 void erts_set_dist_entry_pending(DistEntry *);
259 void erts_set_dist_entry_connected(DistEntry *, Eterm, Uint64);
260 ErlNode *erts_find_or_insert_node(Eterm, Uint32, Eterm);
261 void erts_schedule_delete_node(ErlNode *);
262 void erts_set_this_node(Eterm, Uint32);
263 Uint erts_node_table_size(void);
264 void erts_init_node_tables(int);
265 void erts_node_table_info(fmtfn_t, void *);
266 void erts_print_node_info(fmtfn_t, void *, Eterm, int*, int*);
267 Eterm erts_get_node_and_dist_references(struct process *);
268 #if defined(ERTS_ENABLE_LOCK_CHECK)
269 int erts_lc_is_de_rwlocked(DistEntry *);
270 int erts_lc_is_de_rlocked(DistEntry *);
271 #endif
272 int erts_dist_entry_destructor(Binary *bin);
273 DistEntry *erts_dhandle_to_dist_entry(Eterm dhandle, Uint32* connection_id);
274 #define ERTS_DHANDLE_SIZE (3+ERTS_MAGIC_REF_THING_SIZE)
275 Eterm erts_build_dhandle(Eterm **hpp, ErlOffHeap*, DistEntry*, Uint32 conn_id);
276 Eterm erts_make_dhandle(Process *c_p, DistEntry*, Uint32 conn_id);
277
278 ERTS_GLB_INLINE void erts_init_node_entry(ErlNode *np, erts_aint_t val);
279 #ifdef ERL_NODE_BOOKKEEP
280 #define erts_ref_node_entry(NP, MIN, T) erts_ref_node_entry__((NP), (MIN), (T), __FILE__, __LINE__)
281 #define erts_deref_node_entry(NP, T) erts_deref_node_entry__((NP), (T), __FILE__, __LINE__)
282 ERTS_GLB_INLINE erts_aint_t erts_ref_node_entry__(ErlNode *np, int min_val, Eterm term, char *file, int line);
283 ERTS_GLB_INLINE void erts_deref_node_entry__(ErlNode *np, Eterm term, char *file, int line);
284 #else
285 ERTS_GLB_INLINE erts_aint_t erts_ref_node_entry(ErlNode *np, int min_val, Eterm term);
286 ERTS_GLB_INLINE void erts_deref_node_entry(ErlNode *np, Eterm term);
287 #endif
288 ERTS_GLB_INLINE void erts_de_rlock(DistEntry *dep);
289 ERTS_GLB_INLINE void erts_de_runlock(DistEntry *dep);
290 ERTS_GLB_INLINE void erts_de_rwlock(DistEntry *dep);
291 ERTS_GLB_INLINE void erts_de_rwunlock(DistEntry *dep);
292 #ifdef ERL_NODE_BOOKKEEP
293 void erts_node_bookkeep(ErlNode *, Eterm , int, char *file, int line);
294 #else
295 #define erts_node_bookkeep(...)
296 #endif
297
298 #if ERTS_GLB_INLINE_INCL_FUNC_DEF
299
300 ERTS_GLB_INLINE void
erts_init_node_entry(ErlNode * np,erts_aint_t val)301 erts_init_node_entry(ErlNode *np, erts_aint_t val)
302 {
303 erts_refc_init(&np->refc, val);
304 }
305
306 #ifdef ERL_NODE_BOOKKEEP
307
308 ERTS_GLB_INLINE erts_aint_t
erts_ref_node_entry__(ErlNode * np,int min_val,Eterm term,char * file,int line)309 erts_ref_node_entry__(ErlNode *np, int min_val, Eterm term, char *file, int line)
310 {
311 erts_node_bookkeep(np, term, ERL_NODE_INC, file, line);
312 return erts_refc_inctest(&np->refc, min_val);
313 }
314
315 ERTS_GLB_INLINE void
erts_deref_node_entry__(ErlNode * np,Eterm term,char * file,int line)316 erts_deref_node_entry__(ErlNode *np, Eterm term, char *file, int line)
317 {
318 erts_node_bookkeep(np, term, ERL_NODE_DEC, file, line);
319 if (erts_refc_dectest(&np->refc, 0) == 0)
320 erts_schedule_delete_node(np);
321 }
322
323 #else
324
325 ERTS_GLB_INLINE erts_aint_t
erts_ref_node_entry(ErlNode * np,int min_val,Eterm term)326 erts_ref_node_entry(ErlNode *np, int min_val, Eterm term)
327 {
328 return erts_refc_inctest(&np->refc, min_val);
329 }
330
331 ERTS_GLB_INLINE void
erts_deref_node_entry(ErlNode * np,Eterm term)332 erts_deref_node_entry(ErlNode *np, Eterm term)
333 {
334 if (erts_refc_dectest(&np->refc, 0) == 0)
335 erts_schedule_delete_node(np);
336 }
337
338 #endif
339
340 ERTS_GLB_INLINE void
erts_de_rlock(DistEntry * dep)341 erts_de_rlock(DistEntry *dep)
342 {
343 erts_rwmtx_rlock(&dep->rwmtx);
344 }
345
346 ERTS_GLB_INLINE void
erts_de_runlock(DistEntry * dep)347 erts_de_runlock(DistEntry *dep)
348 {
349 erts_rwmtx_runlock(&dep->rwmtx);
350 }
351
352 ERTS_GLB_INLINE void
erts_de_rwlock(DistEntry * dep)353 erts_de_rwlock(DistEntry *dep)
354 {
355 erts_rwmtx_rwlock(&dep->rwmtx);
356 }
357
358 ERTS_GLB_INLINE void
erts_de_rwunlock(DistEntry * dep)359 erts_de_rwunlock(DistEntry *dep)
360 {
361 erts_rwmtx_rwunlock(&dep->rwmtx);
362 }
363
364 #endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */
365
366 void erts_debug_test_node_tab_delayed_delete(Sint64 millisecs);
367 void erts_lcnt_update_distribution_locks(int enable);
368
369 #endif
370