2 * Copyright (c) 1995 - 2002 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the Institute nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * Cache of connections
39 #include "arla_local.h"
44 #define CONNCACHESIZE 101
46 #define CONNFREELISTINC 17
48 /* Hashtable of connections */
49 static Hashtab
*connhtab
;
51 /* A list with free connections */
52 static List
*connfreelist
;
54 /* # of connections */
55 static unsigned nconnections
;
57 /* # of active connections */
58 static unsigned nactive_connections
;
60 /* List of connections to probe */
61 static List
*connprobelist
;
64 int conn_rxkad_level
= rxkad_crypt
;
68 * Random factor to add to rtts when comparing them.
69 * This is in microseconds/8
72 int RTT_FUZZ
= 400000;
75 * Functions for handling entries into the connection cache.
79 conncmp (void *a
, void *b
)
81 ConnCacheEntry
*c1
= (ConnCacheEntry
*)a
;
82 ConnCacheEntry
*c2
= (ConnCacheEntry
*)b
;
84 return c1
->cred
!= c2
->cred
85 || c1
->host
!= c2
->host
86 || c1
->service
!= c2
->service
87 || c1
->port
!= c2
->port
88 || c1
->securityindex
!= c2
->securityindex
;
94 ConnCacheEntry
*c
= (ConnCacheEntry
*)a
;
96 return c
->cred
+ c
->host
+ c
->service
+ c
->port
+ c
->securityindex
;
99 /* 2^MAX_RETRIES is the maximum number of seconds between probes */
101 #define MAX_RETRIES 8
104 * Add this entry again to the probe list but without restarting ntries.
108 re_probe (ConnCacheEntry
*e
)
113 assert (e
->probe
!= NULL
);
115 gettimeofday (&tv
, NULL
);
117 listdel (connprobelist
, e
->probe_le
);
118 e
->probe_next
= min(tv
.tv_sec
+ (1 << e
->ntries
), e
->probe_next
);
120 e
->probe_next
= tv
.tv_sec
+ (1 << e
->ntries
);
122 if (e
->ntries
<= MAX_RETRIES
)
125 for (item
= listhead (connprobelist
);
127 item
= listnext (connprobelist
, item
)) {
128 ConnCacheEntry
*this = (ConnCacheEntry
*)listdata (item
);
130 if (e
->probe_next
< this->probe_next
) {
131 e
->probe_le
= listaddbefore (connprobelist
, item
, e
);
132 LWP_NoYieldSignal (connprobelist
);
136 e
->probe_le
= listaddtail (connprobelist
, e
);
137 LWP_NoYieldSignal (connprobelist
);
141 * Initial add to probe list.
145 add_to_probe_list (ConnCacheEntry
*e
, int ntries
)
155 static PROCESS pinger_pid
;
158 * Loop waiting for things servers to probe.
169 const char *port_str
;
171 arla_warnx(ADEBCONN
, "running pinger");
173 while (listemptyp (connprobelist
))
174 LWP_WaitProcess (connprobelist
);
176 item
= listhead (connprobelist
);
177 e
= (ConnCacheEntry
*)listdata (item
);
179 assert (e
->probe_le
== item
);
181 gettimeofday (&tv
, NULL
);
182 if (tv
.tv_sec
< e
->probe_next
) {
183 unsigned long t
= e
->probe_next
- tv
.tv_sec
;
186 "pinger: sleeping %lu second(s)", t
);
191 listdel (connprobelist
, item
);
197 addr
.s_addr
= e
->host
;
198 port_str
= ports_num2name (e
->port
);
200 if (port_str
!= NULL
)
201 arla_warnx (ADEBCONN
, "pinger: probing %s/%s",
202 inet_ntoa(addr
), port_str
);
204 arla_warnx (ADEBCONN
, "pinger: probing %s/%d",
205 inet_ntoa(addr
), e
->port
);
207 if (e
->probe
== NULL
)
208 arla_warnx(ADEBWARN
, "pinger: probe function is NULL, "
209 "host: %s cell: %d port: %d",
210 inet_ntoa(addr
), e
->cell
, e
->port
);
212 if (connected_mode
== DISCONNECTED
) {
213 arla_warnx(ADEBCONN
, "pinger: ignoring host in disconnected mode");
214 } else if (e
->probe
&& ((*(e
->probe
))(e
->connection
) == 0)) {
225 * Create `n' ConnCacheEntry's and add them to `connfreelist'
229 create_new_connections (unsigned n
)
232 ConnCacheEntry
*entries
;
234 entries
= (ConnCacheEntry
*)calloc (n
, sizeof (ConnCacheEntry
));
236 arla_errx (1, ADEBERROR
, "conncache: calloc failed");
237 for (i
= 0; i
< n
; ++i
) {
238 entries
[i
].connection
= NULL
;
239 entries
[i
].refcount
= 0;
240 entries
[i
].parent
= NULL
;
241 entries
[i
].probe_le
= NULL
;
242 listaddhead (connfreelist
, &entries
[i
]);
248 * Initialize the connection cache.
252 conn_init (unsigned nentries
)
254 arla_warnx (ADEBCONN
, "initconncache");
256 connhtab
= hashtabnew (CONNCACHESIZE
, conncmp
, connhash
);
257 if (connhtab
== NULL
)
258 arla_errx (1, ADEBERROR
, "conn_init: hashtabnew failed");
259 connfreelist
= listnew ();
260 if (connfreelist
== NULL
)
261 arla_errx (1, ADEBERROR
, "conn_init: listnew failed");
262 connprobelist
= listnew ();
263 if (connprobelist
== NULL
)
264 arla_errx (1, ADEBERROR
, "conn_init: listnew failed");
267 if (LWP_CreateProcess (pinger
, 0, 1, NULL
, "pinger", &pinger_pid
))
268 arla_errx (1, ADEBERROR
,
269 "conn: cannot create pinger thread");
271 create_new_connections (nentries
);
276 * remove it from the hashtab, clear it out and place it on the freelist.
280 recycle_conn (ConnCacheEntry
*e
)
282 assert (e
->refcount
== 0);
284 if (e
->parent
!= NULL
) {
285 conn_free (e
->parent
);
288 if (e
->probe_le
!= NULL
) {
289 listdel (connprobelist
, e
->probe_le
);
292 if (!e
->flags
.killme
)
293 hashtabdel (connhtab
, e
);
294 rx_DestroyConnection (e
->connection
);
295 memset (e
, 0, sizeof(*e
));
296 listaddhead (connfreelist
, e
);
297 --nactive_connections
;
301 * Remove this connection from the hashtab and add it to the freelist
306 clear_conn (void *ptr
, void *arg
)
308 ConnCacheEntry
*e
= (ConnCacheEntry
*)ptr
;
310 if (e
->refcount
== 0)
316 * Get a free connection to use. Try to pick it from `connfreelist'.
317 * If there are no there, it's time to go through `connhtab' and GC
318 * unused connections. If that fails, allocate some more.
319 * And if that fails, give up.
322 static ConnCacheEntry
*
323 get_free_connection (void)
327 e
= (ConnCacheEntry
*)listdelhead (connfreelist
);
331 hashtabforeach (connhtab
, clear_conn
, NULL
);
333 e
= (ConnCacheEntry
*)listdelhead (connfreelist
);
337 create_new_connections (CONNFREELISTINC
);
339 e
= (ConnCacheEntry
*)listdelhead (connfreelist
);
343 arla_errx (1, ADEBERROR
,
344 "conncache: there was no way of getting a connection");
348 * Get a free connection, fill in all parameters and create a
352 static ConnCacheEntry
*
353 new_connection (int32_t cell
,
359 int (*probe
)(struct rx_connection
*),
360 struct rx_securityClass
*securityobject
)
364 assert (probe
!= NULL
);
366 e
= get_free_connection ();
371 e
->service
= service
;
372 e
->flags
.alivep
= TRUE
;
373 e
->flags
.old
= FALSE
;
376 e
->securityindex
= securityindex
;
379 e
->connection
= rx_NewConnection (host
,
384 if (e
->connection
== NULL
)
385 arla_errx (1, ADEBERROR
, "rx_NewConnection failed");
390 * Create a new connection and add it to `connhtab'.
393 static ConnCacheEntry
*
394 add_connection(int32_t cell
,
398 int (*probe
)(struct rx_connection
*),
402 struct rx_securityClass
*securityobj
;
407 securityindex
= ce
->securityindex
;
412 struct cred_rxgk
*cred
= (struct cred_rxgk
*)ce
->cred_data
;
413 unsigned char *base
= (unsigned char *)ce
->cred_data
;
414 RXGK_Ticket_Crypt ticket
;
415 struct rxgk_keyblock key
;
417 ticket
.len
= cred
->tokenlen
;
418 ticket
.val
= (void*)(base
+ sizeof(struct cred_rxgk
));
420 key
.enctype
= cred
->enctype
;
421 key
.length
= cred
->keylen
;
422 key
.data
= (void *)(base
+ sizeof(struct cred_rxgk
) + ticket
.len
);
424 securityobj
= rxgk_NewClientSecurityObject(cred
->level
,
430 struct cred_rxkad
*cred
= (struct cred_rxkad
*)ce
->cred_data
;
432 securityobj
= rxkad_NewClientSecurityObject(conn_rxkad_level
,
433 cred
->ct
.HandShakeKey
,
436 (void *)cred
->ticket
);
441 securityobj
= rxnull_NewClientSecurityObject ();
447 securityobj
= rxnull_NewClientSecurityObject ();
452 e
= new_connection (cell
, host
, port
, service
,
453 cred
, securityindex
, probe
, securityobj
);
455 hashtabadd (connhtab
, (void *)e
);
456 ++nactive_connections
;
463 * Find a connection from the cache given:
464 * (cell, host, port, service, cred).
465 * If there's no connection at all, create one.
468 static ConnCacheEntry
*
469 internal_get (int32_t cell
,
473 int (*probe
)(struct rx_connection
*),
480 if (connected_mode
== DISCONNECTED
)
486 key
.service
= service
;
488 key
.securityindex
= ce
->securityindex
;
490 e
= (ConnCacheEntry
*)hashtabsearch (connhtab
, (void *)&key
);
493 ConnCacheEntry
*parent
= NULL
;
495 if (ce
->securityindex
|| ce
->cred
) {
497 key
.securityindex
= 0;
498 parent
= (ConnCacheEntry
*)hashtabsearch (connhtab
, (void *)&key
);
499 if (parent
== NULL
) {
500 parent
= add_connection (cell
, host
, port
, service
,
506 e
= add_connection (cell
, host
, port
, service
, probe
, ce
);
512 * Since we only probe the parent entry (ie noauth), we make sure
513 * the status from the parent entry is pushed down to the
516 if(e
->parent
!= NULL
) {
517 e
->flags
.alivep
= e
->parent
->flags
.alivep
;
524 * Return a connection to (cell, host, port, service, ce)
528 conn_get (int32_t cell
,
532 int (*probe
)(struct rx_connection
*),
535 ConnCacheEntry
*e
= internal_get (cell
, host
, port
, service
, probe
, ce
);
542 * Add a new reference to a connection
546 conn_ref(ConnCacheEntry
*e
)
548 assert(e
->refcount
> 0);
553 * Free a reference to a ConnCacheEntry.
554 * If refcount drops to zero, it makes it eligible for re-use.
558 conn_free (ConnCacheEntry
*e
)
560 if (e
== NULL
) /* When in disconnected mode conn sets to NULL */
563 assert (e
->refcount
> 0);
566 if (e
->refcount
== 0 && e
->flags
.killme
)
571 * Given a host try to figure out what cell it's in.
575 conn_host2cell (uint32_t host
, uint16_t port
, uint16_t service
)
582 key
.service
= service
;
584 key
.securityindex
= 0;
586 e
= (ConnCacheEntry
*)hashtabsearch(connhtab
, (void *)&key
);
594 * Mark the server in `e' as being down.
598 conn_dead (ConnCacheEntry
*e
)
604 assert (e
->probe
!= NULL
);
606 e
->flags
.alivep
= FALSE
;
607 if (e
->parent
!= NULL
) {
609 e
->flags
.alivep
= FALSE
;
611 add_to_probe_list (e
, 0);
613 port
= ports_num2name (e
->port
);
615 snprintf(buf
, sizeof(buf
), "%d", e
->port
);
619 arla_warnx (ADEBWARN
, "Lost connection to %s/%s in cell %s",
620 inet_ntoa(a
), port
, cell_num2name (e
->cell
));
624 * Mark the server in `e' as being up.
628 conn_alive (ConnCacheEntry
*e
)
634 s
= ports_num2name (e
->port
);
636 arla_warnx (ADEBWARN
, "Server %s/%s up again", inet_ntoa(a
), s
);
638 arla_warnx (ADEBWARN
, "Server %s/%d up again", inet_ntoa(a
), e
->port
);
639 e
->flags
.alivep
= TRUE
;
640 if (e
->parent
!= NULL
)
641 e
->parent
->flags
.alivep
= TRUE
;
645 * Is this server known to be up?
649 conn_isalivep (ConnCacheEntry
*e
)
651 if (e
->parent
!= NULL
)
652 e
->flags
.alivep
= e
->parent
->flags
.alivep
;
654 return e
->flags
.alivep
;
658 * Probe the service in `e'
662 conn_probe (ConnCacheEntry
*e
)
669 if (e
->probe
== NULL
)
670 arla_warnx(ADEBWARN
, "conn_probe: probe function is NULL, "
671 "host: %s cell: %d port: %d",
672 inet_ntoa(a
), e
->cell
, e
->port
);
674 if (e
->probe
&& ((*(e
->probe
))(e
->connection
) == 0)) {
675 if (!e
->flags
.alivep
)
685 * Is the service at (cell, host, port, service) up?
689 conn_serverupp (uint32_t host
, uint16_t port
, uint16_t service
)
696 key
.service
= service
;
698 key
.securityindex
= 0;
700 e
= (ConnCacheEntry
*)hashtabsearch (connhtab
, (void *)&key
);
702 return e
->flags
.alivep
;
712 print_conn (void *ptr
, void *arg
)
714 ConnCacheEntry
*e
= (ConnCacheEntry
*)ptr
;
717 tmp
.s_addr
= e
->host
;
719 arla_log(ADEBVLOG
, "host = %s, port = %d, service = %d, "
721 "securityindex = %d, cred = %u, "
722 "conn = %p, alive = %d, "
723 "killme = %d, refcount = %d",
724 inet_ntoa(tmp
), e
->port
, e
->service
, e
->cell
,
725 cell_num2name (e
->cell
),
726 e
->securityindex
, e
->cred
, e
->connection
,
727 e
->flags
.alivep
, e
->flags
.killme
, e
->refcount
);
733 * Print the status of the connection cache.
739 arla_log(ADEBVLOG
, "%u(%u) connections",
740 nactive_connections
, nconnections
);
741 hashtabforeach (connhtab
, print_conn
, NULL
);
745 conn_get_fs_support64(const ConnCacheEntry
*e
)
747 return e
->flags
.no64support
? FALSE
: TRUE
;
751 conn_set_fs_support64(ConnCacheEntry
*e
, Bool status
)
753 e
->flags
.no64support
= status
== TRUE
? 0 : 1;
761 clear_state_mask mask
;
768 clear_cred (void *ptr
, void *arg
)
770 ConnCacheEntry
*e
= (ConnCacheEntry
*)ptr
;
771 struct clear_state
*s
= (struct clear_state
*)arg
;
773 if ((s
->mask
& CONN_CS_CRED
) && s
->cred
!= e
->cred
)
775 if ((s
->mask
& CONN_CS_CELL
) && s
->cell
!= e
->cell
)
777 if ((s
->mask
& CONN_CS_SECIDX
) && s
->securityindex
!= e
->securityindex
)
780 if (e
->refcount
> 0) {
782 hashtabdel (connhtab
, e
);
790 * Remove all connections matching mask + (cell, cred, securityindex).
794 conn_clearcred(clear_state_mask mask
,
795 int32_t cell
, nnpfs_pag_t cred
, int securityindex
)
797 struct clear_state s
;
802 s
.securityindex
= securityindex
;
804 hashtabforeach (connhtab
, clear_cred
, (void *)&s
);
808 * check if servers are up for cell `cell'
820 host_down (void *ptr
, void *arg
)
822 ConnCacheEntry
*e
= (ConnCacheEntry
*)ptr
;
823 struct down_state
*s
= (struct down_state
*)arg
;
826 if (s
->cell
== e
->cell
) {
828 if (!(s
->flags
& arla_CKSERV_DONTPING
)) {
835 if (s
->flags
& arla_CKSERV_FSONLY
&& e
->port
!= afsport
)
838 for (i
= 0; i
< s
->i
; ++i
)
839 if (s
->hosts
[i
] == e
->host
)
842 s
->hosts
[s
->i
] = e
->host
;
852 * Check what hosts are down.
854 * Flags is VIOCCKFLAGS
858 conn_downhosts(int32_t cell
, uint32_t *hosts
, int *num
, int flags
)
871 hashtabforeach (connhtab
, host_down
, (void *)&s
);
877 * Compare two ConnCacheEntries rtt-wise. Typically used when sorting
882 conn_rtt_cmp (const void *v1
, const void *v2
)
884 ConnCacheEntry
**e1
= (ConnCacheEntry
**)v1
;
885 ConnCacheEntry
**e2
= (ConnCacheEntry
**)v2
;
887 return (*e1
)->rtt
- (*e2
)->rtt
;
891 * Return true iff this error means we should mark the host as down
892 * due to network errors
896 host_downp (int error
)
899 case ARLA_CALL_DEAD
:
900 case ARLA_INVALID_OPERATION
:
901 case ARLA_CALL_TIMEOUT
:
903 case ARLA_PROTOCOL_ERROR
:
904 case ARLA_USER_ABORT
:
905 case ARLA_ADDRINUSE
:
907 case RXGEN_CC_MARSHAL
:
908 case RXGEN_CC_UNMARSHAL
:
909 case RXGEN_SS_MARSHAL
:
910 case RXGEN_SS_UNMARSHAL
:
913 case RXGEN_SS_XDRFREE
:
914 case RXGEN_CC_XDRFREE
: