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