1 /*
2 *
3 * Copyright (c) 2003 The Regents of the University of California. All
4 * rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 *
13 * - Neither the name of the University nor the names of its
14 * contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS''
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
19 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
21 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
25 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 *
29 */
30
31
32
33 #include "common.h"
34 #include "dijkstra.h"
35 #include "gls_i.h"
36 #include "hash.h"
37
38 #define MY_LOG_LEVEL LOG_DEBUG(4)
39
40 struct dijkstra_args
41 {
42 /* options governing operations of the node on which this algorithm is
43 * running */
44 struct nodeconf *cfg;
45 /* this parameter is used by callbacks to keep track of the node that is
46 * being currently processed */
47 ulong curId;
48 /* adjacency matrix */
49 ulong **adj;
50 /* when creating an adjacency matrix, we can't use "real" node ids, since
51 * they may be non-consecutive. Hence, we map node ids to consecutive
52 * numbers (which we call dijkstra ids). This table holds the mappings */
53 GHashTable *nodeToDijkstra;
54 /* array mapping dijkstra ids to "real" ids */
55 ulong *dijkstraToNode;
56 /* need to make use of the global configuration table while figuring out
57 * edge weights, etc */
58 GHashTable *nodeTable;
59 };
60
61 void
62 destroy_routedev_nodeinfo(gpointer data)
63 {
64 assert(data);
65 g_free(data);
66 }
67
68 /* TODO: explanation for the function */
69 static void
70 map_dijkstra(gpointer key, gpointer value, gpointer user_data)
71 {
72 ulong **dijkstra_to_node_element = (ulong **)user_data;
73
74 assert(dijkstra_to_node_element);
75 assert(* dijkstra_to_node_element);
76 assert(value);
77 assert(key);
78
79 elog (MY_LOG_LEVEL, "mapping key %lu",* (ulong *)key);
80
81 /* before calling the g_hash_table_foreach(), we set the (*
82 * dijkstra_to_node_element) to point to the first element of the
83 * dijkstra_to_node array, and then keep moving the pointer while walking
84 * the table */
85 (** dijkstra_to_node_element) = * (ulong *)key;
86 /* advance the pointer to point to the next element */
87 (* dijkstra_to_node_element) ++;
88 }
89
90 /* x - # of elts in the row
91 * y - # of rows */
92 ulong **
93 allocate_2d_array(int x, int y)
94 {
95 ulong **a;
96 int i;
97
98 assert((x > 0) && (y > 0));
99
100 /* allocate array of pointers to rows */
101 a = g_malloc(y * sizeof(ulong *));
102 /* allocate contiguous array to store the 2d array */
103 a[0] = g_malloc(x * y * sizeof(ulong *));
104 /* adjust pointers in the row-pointer array to point to rows in 2d array
105 * (start with 1, as #0 is already set by virtue of g_malloc) */
106 for (i = 1; i < y; i++)
107 {
108 a[i] = a[0] + x * i;
109 }
110
111 return a;
112 }
113
114 /* TODO: allocate/destroy functions should probably be elsewhere */
115 void
116 destroy_2d_array(ulong **a)
117 {
118 assert(a && (*a));
119 g_free(a[0]); /* free contiguous space */
120 g_free(a); /* free the array of rows */
121 }
122
123 /* function to compute weights (distances) between the two nodes 'n1' and 'n2',
124 * given the information available from configuration structure 'cfg' */
125 ulong
126 compute_weight(GHashTable *global, ulong n1, ulong n2)
127 {
128 struct nodeconf *cfgOne;
129 struct nodeconf *cfgTwo;
130 unsigned int dist;
131 unsigned int dist_x;
132 unsigned int dist_y;
133
134 assert(global);
135
136 cfgOne = (struct nodeconf *)g_hash_table_lookup(global, &n1);
137 assert(cfgOne);
138
139 cfgTwo = (struct nodeconf *)g_hash_table_lookup(global, &n2);
140 assert(cfgTwo);
141
142 /*
143 * if weight of the link between these two nodes was not given to us (we
144 * would not be in this function, if it was), compute this weight as a basic
145 * cartesian distance
146 */
147
148 /* take care to make sure our unsigned integers do not wrap */
149 if (cfgOne->geo.x > cfgTwo->geo.x)
150 {
151 dist_x = cfgOne->geo.x - cfgTwo->geo.x;
152 }
153 else
154 {
155 dist_x = cfgTwo->geo.x - cfgOne->geo.x;
156 }
157
158 if (cfgOne->geo.y > cfgTwo->geo.y)
159 {
160 dist_y = cfgOne->geo.y - cfgTwo->geo.y;
161 }
162 else
163 {
164 dist_y = cfgTwo->geo.y - cfgOne->geo.y;
165 }
166
167 /* multiply by 100 since we want to keep weights as ulongs, but also
168 * dont want to lose too much precision. Only relative weights matter
169 * anyway */
170 dist = ROUND_TO_ULONG(100 * sqrt(dist_x * dist_x + dist_y * dist_y));
171
172 return dist;
173 }
174
175 static void
176 add_edge(gpointer key, gpointer value, gpointer user_data)
177 {
178 struct dijkstra_args *args = (struct dijkstra_args *)user_data;
179 /* node configuration of the node we're interested in */
180 struct gls_neighbor *curNeighbor = (struct gls_neighbor *)value;
181 ulong weight;
182 ulong n1, n2;
183
184 assert(args);
185 assert(curNeighbor);
186 assert(args->cfg);
187 assert(args->nodeTable);
188
189 /*
190 * finally, add edge and weight information to proper arrays
191 */
192
193 /* lookup dijkstra id of node who's neighborlist we're walking */
194 n1 = (ulong) g_hash_table_lookup(args->nodeToDijkstra,
195 (gpointer)args->curId);
196 /* lookup dijkstra id of the current neighbor */
197 n2 = (ulong) g_hash_table_lookup(args->nodeToDijkstra,
198 (gpointer)curNeighbor->id);
199
200 elog (MY_LOG_LEVEL, "n1->n2 = %lu->%lu",n1,n2);
201
202 /* we want to make sure this is not a problem: when using direct hashing
203 * (storing integers (longs in our case) in the space for pointers (value in
204 * the hash table)) we could legitimately store and find a value of ''. At
205 * the same time, g_hash_table_lookup() returns NULL if it can not find
206 * information we requested. So, to differentiate between the two cases, we
207 * do this extra reverse-check. It is totally unexpected for us to NOT find
208 * a dijkstra id of the node, hence the assert() */
209 assert(args->curId == args->dijkstraToNode[n1]);
210
211 /* it's possible to have the node on the neighborlist, but not have any
212 * information about the node */
213 if (curNeighbor->id != args->dijkstraToNode[n2])
214 {
215 /* yeah, we don't know anything about the neighbor( except the fact that
216 * it is present on the node's neighborlist..) */
217 return;
218 }
219
220 /* are we supposed to compute weights of edges? */
221 if (args->cfg->use_cartesian_symmetric_weights)
222 {
223 /* compute weight of the edge between the node who's neighborlist we
224 * are currently processing (curNeighbor->id) and the current
225 * neighbor */
226 weight = compute_weight(args->nodeTable, args->curId, curNeighbor->id);
227
228 /* fill the weight in the adjacency matrix (in this case, we assume
229 * symmetric links */
230 args->adj[n1][n2] = weight;
231 args->adj[n2][n1] = weight;
232
233 elog (MY_LOG_LEVEL, "Symmetric: assigning weight args->adj[%lu][%lu]: %lu",
234 n1, n2, weight);
235
236 }
237 else /* don't assume symmetric links, and compute weights */
238 {
239
240 /* in this case, we simply take the value of the weight from the
241 * neighbor structure. Notice, we're not setting adj[n2][n1], since
242 * links are not symmetric -- this will get set, if necessary, when
243 * walking the list of neighbors for n2 */
244 args->adj[n1][n2] = curNeighbor->weight;
245
246 elog (MY_LOG_LEVEL, "Asymmetric: assigning weight args->adj[%lu][%lu]: %lu",
247 n1,n2, curNeighbor->weight);
248 }
249 }
250
251 /* walks the neighborlist. Each callback will 'add an edge' to the adjacency
252 * matrix */
253 static void
254 walk_neighborlist(gpointer key, gpointer value, gpointer user_data)
255 {
256 struct dijkstra_args *args = (struct dijkstra_args *)user_data;
257 /* node configuration of the node we're interested in */
258 struct nodeconf *curNodeCfg = (struct nodeconf *)value;
259
260 buf_t *bf = buf_new();
261
262
263 assert(args);
264 assert(curNodeCfg);
265
266 elog (MY_LOG_LEVEL, "Walking NodeID: %d",curNodeCfg->id);
267 print_neighborlist(curNodeCfg,bf);
268 elog_g(MY_LOG_LEVEL, "%s",bf->buf);
269 buf_free(bf);
270
271 /* set id of the node, who's neighborlist we're about to process */
272 args->curId = * (ulong *)key;
273 /* walk the neighborlist */
274 g_hash_table_foreach(curNodeCfg->neighbor_list, add_edge, (gpointer)args);
275 }
276
277 /* TODO: needs a comment on why we're doing what we're doing with conversions,
278 * etc */
279 /* TODO: perhaps rename this function to 'rebuild_routing_table', as it will
280 * indicate that we actually destroy/reallocate it? */
281 void
282 compute_routing_table(struct routedev_info *tables, struct nodeconf *config)
283 {
284 /* table mapping dijkstra consecutive ids to node ids */
285 static ulong *dijkstra_to_node = NULL;
286 /* table mapping node ids to consecutive dijkstra ids */
287 GHashTable *nodeToDijkstra;
288 /* temp counter(s) */
289 unsigned int i, j;
290 /* dijkstra id of the node we're running on (node from which we will seek
291 * out the paths */
292 ulong thisNode;
293 /* arguments needed for the dijkstra algorithm */
294 struct dijkstra_args args;
295 /* predecessor array: element p[j] is a predecessor of node j on the path
296 * from the node we're running on now */
297 static ulong *p = NULL;
298 static unsigned long int nodeid_table_size = 0;
299 struct routedev_nodeinfo *curNodePtr = NULL;
300 unsigned long int nextHop = 0;
301 unsigned long int cur = 0;
302
303 /* definitions for printing out routing paths */
304 gboolean self = FALSE;
305 gboolean path_exists = FALSE;
306 GSList *path;
307 GSList *cur_path_elt;
308
309
310 ulong *dijkstra_to_node_element = NULL;
311
312 assert(tables);
313 /* noteid table must've been created by the time this function is called */
314 assert(tables->nodeid);
315 assert(config);
316
317 elog (MY_LOG_LEVEL, "sizeof(tables->nodeid) = %d",g_hash_table_size(tables->nodeid));
318
319 /* if number of nodes we're dealing with changed... */
320 if (nodeid_table_size != g_hash_table_size(tables->nodeid))
321 {
322 /* ... reallocate the dijkstra_to_node table */
323 nodeid_table_size = g_hash_table_size(tables->nodeid);
324 dijkstra_to_node = g_realloc(dijkstra_to_node,
325 sizeof(ulong) * nodeid_table_size);
326 /* ... reallocate predecessor array */
327 p = g_realloc(p, sizeof(ulong) * nodeid_table_size);
328 }
329
330 dijkstra_to_node_element = dijkstra_to_node;
331 /* walk the nodeid table and for each nodeid we encounter, add an entry to
332 * the dijkstra_to_node table */
333 g_hash_table_foreach(tables->nodeid, map_dijkstra,
334 &dijkstra_to_node_element);
335
336 /* now lets generate reverse mappings: nodeId -> dijkstraId */
337 nodeToDijkstra = g_hash_table_new(g_direct_hash, g_direct_equal);
338 assert(nodeToDijkstra);
339
340 for (i = 0; i < nodeid_table_size; i ++)
341 {
342 g_hash_table_insert(nodeToDijkstra,
343 (gpointer)(dijkstra_to_node[i]),
344 (gpointer)i);
345 }
346
347 elog (MY_LOG_LEVEL, "config->id:%d",config->id);
348
349 thisNode = (ulong)g_hash_table_lookup(nodeToDijkstra, (gpointer)config->id);
350
351 elog (MY_LOG_LEVEL, "config->id:%d ThisNode:%lu",config->id,thisNode);
352
353 /* now we need to go create an adjacency matrix */
354 args.cfg = config;
355 args.nodeToDijkstra = nodeToDijkstra;
356 args.dijkstraToNode = dijkstra_to_node;
357 args.nodeTable = tables->nodeid;
358 args.adj = allocate_2d_array(nodeid_table_size,
359 nodeid_table_size);
360
361 /* initialize the 2d adjacency matrix prior to use */
362 for (i = 0; i < nodeid_table_size; i++)
363 {
364 for (j = 0; j < nodeid_table_size; j++)
365 {
366 args.adj[i][j] = INFINITY;
367 }
368 }
369
370 /* walk the node table, and for every configuration structure encountered
371 * there call the function, which will in turn walk the neighborlist and add
372 * proper edges to the dijkstra_args structure 'args' */
373 g_hash_table_foreach(tables->nodeid, walk_neighborlist, (gpointer)&args);
374
375
376 /* Save adjacency matrix for status device to print */
377
378 if (tables->adjbuf != NULL) buf_free(tables->adjbuf);
379 tables->adjbuf = buf_new();
380 // buf = tables->adjbuf;
381
382 /* print an adjacency table */
383 bufprintf(tables->adjbuf,"----------------------------------------\n");
384 bufprintf(tables->adjbuf,"ADJACENCY TABLE\n");
385 bufprintf(tables->adjbuf,"----------------------------------------\n");
386 bufprintf(tables->adjbuf," ");
387
388 for (i = 0; i < nodeid_table_size; i++)
389 {
390 bufprintf(tables->adjbuf,"%03lu ", dijkstra_to_node[i]);
391 }
392
393 bufprintf(tables->adjbuf,"\n");
394
395 for (i = 0; i < nodeid_table_size; i++)
396 {
397 bufprintf(tables->adjbuf,"%03lu : ", dijkstra_to_node[i]);
398 for (j = 0; j < nodeid_table_size; j++)
399 {
400 if (INFINITY == args.adj[i][j])
401 bufprintf(tables->adjbuf,"xxx ");
402 else
403 bufprintf(tables->adjbuf,"%3lu ", args.adj[i][j]);
404 }
405
406 bufprintf(tables->adjbuf,"\n");
407 }
408
409 elog(MY_LOG_LEVEL,"%s",(tables->adjbuf)->buf);
410 /* End of Print Adjacency Matrix */
411
412 /* initialize all predecessors to some sentinel value, which means "no
413 * route" */
414 for (i = 0; i < nodeid_table_size; i++)
415 {
416 p[i] = INFINITY;
417 }
418
419 /* now the adjacency matrix is filled in, and we're ready to run dijkstra
420 * algorithm to compute the next hop information */
421 (void)dijkstra(args.adj, nodeid_table_size, thisNode, p);
422
423 /* print node to dijkstra array */
424 for (i = 0; i < nodeid_table_size; i++)
425 elog (MY_LOG_LEVEL,"DijkId:%d NodeId:%lu", i, dijkstra_to_node[i]);
426
427 /* print predecessor array */
428 for (i = 0; i < nodeid_table_size; i++)
429 {
430 if (p[i] == INFINITY)
431 elog (MY_LOG_LEVEL, "ThisNode:%lu p[%d] = INFINITY",dijkstra_to_node[thisNode],i);
432 else
433 elog (MY_LOG_LEVEL, "ThisNode:%lu p[%d] = %lu",dijkstra_to_node[thisNode],i,p[i]);
434 }
435
436
437 /* if there already was a routing table, this one may be quite different, so
438 * we remove all of the old entries */
439 if (tables->rtable)
440 {
441 /* individual tables will be deallocated using the hook we pass in when
442 * making a table (destroy_routedev_nodeinfo) */
443 g_hash_table_destroy(tables->rtable);
444 }
445
446 /* ok, lets create our routing table. It will contain routedev_nodeinfo
447 * structures, which have their id as the first element, which means we can
448 * reuse the hash_u32 and hash_compare_u32. This table contains only
449 * information in terms of nodeids. nothing else. For mapping of nodeid to
450 * other node attributes/addresses refer to the tables->nodeid table */
451 if (! (tables->rtable = g_hash_table_new_full(hash_u32,
452 hash_compare_u32,
453 NULL /* key destroy */,
454 destroy_routedev_nodeinfo)))
455 {
456 printf("Error allocating routing table!\n");
457 exit(1);
458 }
459
460 /* Printing Paths */
461 if (tables->rtablebuf) buf_free(tables->rtablebuf);
462 tables->rtablebuf = buf_new();
463
464 bufprintf(tables->rtablebuf,"----------------------------------------\n");
465 bufprintf(tables->rtablebuf,"ROUTING PATHS\n");
466 bufprintf(tables->rtablebuf,"----------------------------------------\n");
467 /* End Printing Paths */
468
469 /* trace back the path from node 'i' to the starting node, to get the
470 * "next hop" information from the node we're running dijkstra for, to
471 * all others */
472 for (i = 0; i < nodeid_table_size; i++)
473 {
474
475 /* Printing Paths */
476 self = FALSE;
477 path_exists = FALSE;
478
479 if (i == thisNode)
480 {
481 self = TRUE;
482 }
483 /* End Printing Paths */
484
485 nextHop = INFINITY;
486 cur = i;
487
488 /* Printing Paths */
489 path = NULL;
490 bufprintf(tables->rtablebuf,"Route from %03lu to %03lu : ",
491 dijkstra_to_node[thisNode], dijkstra_to_node[i]);
492 /* End Printing Paths */
493
494 /* we'll hit the loop break out condition when there is
495 * no path to the starting node -- this happens when we reach a node
496 * whos predecessor is the node itself. Well, this will also happen for
497 * the starting node, since it's always a predecessor of itself. We
498 * check for the latter after we break out */
499 for (cur = i, curNodePtr = NULL; p[cur] != cur; cur = p[cur])
500 {
501 /* when the predecessor node of the one we're looking at now is a
502 * 'thisNode', the node we're looking at now is the next hop (since
503 * we're looking backwards), so we stop. */
504 if (thisNode == p[cur])
505 {
506 /* Printing Paths */
507 path_exists = TRUE;
508 /* End Printing Paths */
509 nextHop = cur;
510 break;
511 }
512 else if (INFINITY == p[cur]) /* sentinel, meaning "no predecessor"*/
513 {
514 /* Printing Paths */
515 path_exists = FALSE;
516 /* End Printing Paths */
517 break;
518 }
519
520 /* Printing Paths */
521 path = g_slist_prepend(path, (gpointer)dijkstra_to_node[p[cur]]);
522 /* End Printing Paths */
523 }
524
525 /* Printing Paths */
526 if (TRUE == self)
527 {
528 bufprintf(tables->rtablebuf,"Self");
529 }
530 else if (FALSE == path_exists)
531 {
532 bufprintf(tables->rtablebuf,"No Path");
533 }
534 else
535 {
536 int k = 0;
537
538 bufprintf(tables->rtablebuf,"%lu ", (unsigned long int)config->id);
539
540 /* go through the list and print the path */
541 for (cur_path_elt = path, k = 0;
542 cur_path_elt;
543 cur_path_elt = g_slist_next(cur_path_elt))
544 {
545 bufprintf(tables->rtablebuf,"-> %lu ", (unsigned long int)cur_path_elt->data);
546
547 if (k < 10)
548 {
549 k++;
550 }
551 else
552 {
553 bufprintf(tables->rtablebuf,"\n : ");
554 k = 0;
555 }
556 }
557
558 bufprintf(tables->rtablebuf,"-> %lu ", (unsigned long int)dijkstra_to_node[i]);
559 }
560
561 bufprintf(tables->rtablebuf,"\n");
562
563 /* ok, now that we've printed everything, free the list */
564 g_slist_free(path);
565 path = NULL;
566
567 elog(MY_LOG_LEVEL,"%s",(tables->rtablebuf)->buf);
568 /* End Printing Paths */
569
570 if (i != thisNode) /* we dont enter routes to self into the table */
571 {
572 /* ... but we do enter "no route" information. i.e. we dont check
573 * whether nextHop is INFINITY or not. Even if it is, we enter
574 * anyway */
575 curNodePtr = g_new0(struct routedev_nodeinfo, 1);
576
577 curNodePtr->id = dijkstra_to_node[i];
578
579 if (INFINITY == nextHop)
580 {
581 curNodePtr->next_hop = INFINITY;
582 }
583 else
584 {
585 curNodePtr->next_hop = dijkstra_to_node[nextHop];
586 }
587
588 // add an item to the hash table
589 g_hash_table_insert(tables->rtable, &(curNodePtr->id), curNodePtr);
590 }
591
592 } /* for */
593
594 /* deallocate everything we've allocated */
595
596 /* we only have to destroy the table, as we don't dynamically allocate it's
597 * elements - we use direct hashing */
598 g_hash_table_destroy(nodeToDijkstra);
599
600 /* destroy adjacency table. The next time we're called, it may be completely
601 * different */
602 destroy_2d_array(args.adj);
603 }
604
605
606 static void
607 print_routedev_nodeinfo(gpointer key, gpointer value, gpointer user_data)
608 {
609 /* we dont care to print the 'key's, only the values */
610 struct routedev_nodeinfo *route = value;
611
612 assert(route);
613 elog(MY_LOG_LEVEL, "Nexthop to %lu -> %lu",route->id,route->next_hop);
614 }
615
616
617 void
618 print_routing_table(GHashTable *rtable)
619 {
620 assert(rtable);
621 g_hash_table_foreach(rtable, print_routedev_nodeinfo, NULL);
622 }
623
624 static int rtablestatus_printable(status_context_t *info, buf_t *buf)
625 {
626 struct routedev_info *tables = (struct routedev_info *)sd_data(info);
627
628 if (!tables->rtablebuf)
629 bufprintf(buf, "No Routing Table");
630 else
631 bufcpy(buf,(tables->rtablebuf)->buf,(tables->rtablebuf)->len);
632
633 return STATUS_MSG_COMPLETE;
634 }
635
636 static int adjstatus_printable(status_context_t *info, buf_t *buf)
637 {
638 struct routedev_info *tables = (struct routedev_info *)sd_data(info);
639
640 if (!tables->adjbuf)
641 bufprintf(buf, "No Adjacency List");
642 else
643 bufprintf(buf,"%s",(tables->adjbuf)->buf);
644 // bufcpy(buf,(tables->adjbuf)->buf,(tables->adjbuf)->len);
645
646 return STATUS_MSG_COMPLETE;
647 }
648
649 void tables_status_init(struct routedev_info *tables)
650 {
651 /* Basic opts for our neighbor status device */
652 status_dev_opts_t rtable_status_opts = {
653 device: {
654 devname: sim_path("/dev/link/routing/gls_rtable"),
655 device_info: tables,
656 },
657 printable: rtablestatus_printable
658 };
659
660 /* Basic opts for our neighbor status device */
661 status_dev_opts_t adj_status_opts = {
662 device: {
663 devname: sim_path("/dev/link/routing/gls_adjlist"),
664 device_info: tables,
665 },
666 printable: adjstatus_printable
667 };
668
669 tables->rtablestatus = NULL;
670
671 /* Now create the status device */
672 if (g_status_dev(&rtable_status_opts, &tables->rtablestatus) < 0) {
673 elog(LOG_CRIT, "can't create status device /dev/link/routing/gls_rtable");
674 exit(1);
675 }
676
677 tables->adjstatus = NULL;
678
679 /* Now create the status device */
680 if (g_status_dev(&adj_status_opts, &tables->adjstatus) < 0) {
681 elog(LOG_CRIT, "can't create status device /dev/link/routing/gls_adjlist");
682 // elog(LOG_CRIT, "can't create status device %s: %m", adj_status_opts.device.devname);
683 exit(1);
684 }
685 }
686
687
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.