Browse Source

[peerdist] Add segment discovery mechanism

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 9 years ago
parent
commit
dc9d24e7d2
4 changed files with 671 additions and 0 deletions
  1. 3
    0
      src/config/fault.h
  2. 1
    0
      src/include/ipxe/errfile.h
  3. 116
    0
      src/include/ipxe/peerdisc.h
  4. 551
    0
      src/net/peerdisc.c

+ 3
- 0
src/config/fault.h View File

@@ -14,6 +14,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
14 14
 /* Drop every N transmitted or received network packets */
15 15
 #define	NETDEV_DISCARD_RATE 0
16 16
 
17
+/* Drop every N transmitted or received PeerDist discovery packets */
18
+#define PEERDISC_DISCARD_RATE 0
19
+
17 20
 #include <config/local/fault.h>
18 21
 
19 22
 #endif /* CONFIG_FAULT_H */

+ 1
- 0
src/include/ipxe/errfile.h View File

@@ -252,6 +252,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
252 252
 #define ERRFILE_httpauth		( ERRFILE_NET | 0x00420000 )
253 253
 #define ERRFILE_httpbasic		( ERRFILE_NET | 0x00430000 )
254 254
 #define ERRFILE_httpdigest		( ERRFILE_NET | 0x00440000 )
255
+#define ERRFILE_peerdisc		( ERRFILE_NET | 0x00450000 )
255 256
 
256 257
 #define ERRFILE_image		      ( ERRFILE_IMAGE | 0x00000000 )
257 258
 #define ERRFILE_elf		      ( ERRFILE_IMAGE | 0x00010000 )

+ 116
- 0
src/include/ipxe/peerdisc.h View File

@@ -0,0 +1,116 @@
1
+#ifndef _IPXE_PEERDISC_H
2
+#define _IPXE_PEERDISC_H
3
+
4
+/** @file
5
+ *
6
+ * Peer Content Caching and Retrieval (PeerDist) protocol peer discovery
7
+ *
8
+ */
9
+
10
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
11
+
12
+#include <stdint.h>
13
+#include <ipxe/refcnt.h>
14
+#include <ipxe/list.h>
15
+#include <ipxe/tables.h>
16
+#include <ipxe/retry.h>
17
+#include <ipxe/socket.h>
18
+#include <ipxe/interface.h>
19
+#include <ipxe/pccrc.h>
20
+
21
+/** A PeerDist discovery socket */
22
+struct peerdisc_socket {
23
+	/** Name */
24
+	const char *name;
25
+	/** Data transfer interface */
26
+	struct interface xfer;
27
+	/** Socket address */
28
+	union {
29
+		struct sockaddr sa;
30
+		struct sockaddr_in sin;
31
+		struct sockaddr_in6 sin6;
32
+	} address;
33
+};
34
+
35
+/** PeerDist discovery socket table */
36
+#define PEERDISC_SOCKETS __table ( struct peerdisc_socket, "peerdisc_sockets" )
37
+
38
+/** Declare a PeerDist discovery socket */
39
+#define __peerdisc_socket __table_entry ( PEERDISC_SOCKETS, 01 )
40
+
41
+/** A PeerDist discovery segment */
42
+struct peerdisc_segment {
43
+	/** Reference count */
44
+	struct refcnt refcnt;
45
+	/** List of segments */
46
+	struct list_head list;
47
+	/** Segment identifier string
48
+	 *
49
+	 * This is MS-PCCRC's "HoHoDk", transcribed as an upper-case
50
+	 * Base16-encoded string.
51
+	 */
52
+	const char *id;
53
+	/** Message UUID string */
54
+	const char *uuid;
55
+	/** List of discovered peers
56
+	 *
57
+	 * The list of peers may be appended to during the lifetime of
58
+	 * the discovery segment.  Discovered peers will not be
59
+	 * removed from the list until the last discovery has been
60
+	 * closed; this allows users to safely maintain a pointer to a
61
+	 * current position within the list.
62
+	 */
63
+	struct list_head peers;
64
+	/** List of active clients */
65
+	struct list_head clients;
66
+	/** Transmission timer */
67
+	struct retry_timer timer;
68
+};
69
+
70
+/** A PeerDist discovery peer */
71
+struct peerdisc_peer {
72
+	/** List of peers */
73
+	struct list_head list;
74
+	/** Peer location */
75
+	char location[0];
76
+};
77
+
78
+/** A PeerDist discovery client */
79
+struct peerdisc_client {
80
+	/** Discovery segment */
81
+	struct peerdisc_segment *segment;
82
+	/** List of clients */
83
+	struct list_head list;
84
+	/** Operations */
85
+	struct peerdisc_client_operations *op;
86
+};
87
+
88
+/** PeerDist discovery client operations */
89
+struct peerdisc_client_operations {
90
+	/** New peers have been discovered
91
+	 *
92
+	 * @v peerdisc		PeerDist discovery client
93
+	 */
94
+	void ( * discovered ) ( struct peerdisc_client *peerdisc );
95
+};
96
+
97
+/**
98
+ * Initialise PeerDist discovery
99
+ *
100
+ * @v peerdisc		PeerDist discovery client
101
+ * @v op		Discovery operations
102
+ */
103
+static inline __attribute__ (( always_inline )) void
104
+peerdisc_init ( struct peerdisc_client *peerdisc,
105
+		struct peerdisc_client_operations *op ) {
106
+
107
+	peerdisc->op = op;
108
+}
109
+
110
+extern unsigned int peerdisc_timeout_secs;
111
+
112
+extern int peerdisc_open ( struct peerdisc_client *peerdisc, const void *id,
113
+			   size_t len );
114
+extern void peerdisc_close ( struct peerdisc_client *peerdisc );
115
+
116
+#endif /* _IPXE_PEERDISC_H */

+ 551
- 0
src/net/peerdisc.c View File

@@ -0,0 +1,551 @@
1
+/*
2
+ * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>.
3
+ *
4
+ * This program is free software; you can redistribute it and/or
5
+ * modify it under the terms of the GNU General Public License as
6
+ * published by the Free Software Foundation; either version 2 of the
7
+ * License, or (at your option) any later version.
8
+ *
9
+ * This program is distributed in the hope that it will be useful, but
10
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
+ * General Public License for more details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License
15
+ * along with this program; if not, write to the Free Software
16
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17
+ * 02110-1301, USA.
18
+ *
19
+ * You can also choose to distribute this program under the terms of
20
+ * the Unmodified Binary Distribution Licence (as given in the file
21
+ * COPYING.UBDL), provided that you have satisfied its requirements.
22
+ */
23
+
24
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25
+
26
+#include <stdlib.h>
27
+#include <string.h>
28
+#include <ctype.h>
29
+#include <errno.h>
30
+#include <assert.h>
31
+#include <ipxe/xfer.h>
32
+#include <ipxe/iobuf.h>
33
+#include <ipxe/open.h>
34
+#include <ipxe/tcpip.h>
35
+#include <ipxe/uuid.h>
36
+#include <ipxe/base16.h>
37
+#include <ipxe/netdevice.h>
38
+#include <ipxe/timer.h>
39
+#include <ipxe/fault.h>
40
+#include <ipxe/pccrd.h>
41
+#include <ipxe/peerdisc.h>
42
+
43
+/** @file
44
+ *
45
+ * Peer Content Caching and Retrieval (PeerDist) protocol peer discovery
46
+ *
47
+ */
48
+
49
+/** List of discovery segments */
50
+static LIST_HEAD ( peerdisc_segments );
51
+
52
+/** Number of repeated discovery attempts */
53
+#define PEERDISC_REPEAT_COUNT 2
54
+
55
+/** Time between repeated discovery attempts */
56
+#define PEERDISC_REPEAT_TIMEOUT ( 1 * TICKS_PER_SEC )
57
+
58
+/** Default discovery timeout (in seconds) */
59
+#define PEERDISC_DEFAULT_TIMEOUT_SECS 2
60
+
61
+/** Recommended discovery timeout (in seconds)
62
+ *
63
+ * We reduce the recommended discovery timeout whenever a segment
64
+ * fails to discover any peers, and restore the default value whenever
65
+ * a valid discovery reply is received.  We continue to send discovery
66
+ * requests even if the recommended timeout is reduced to zero.
67
+ *
68
+ * This strategy is intended to minimise discovery delays when no
69
+ * peers are available on the network, while allowing downloads to
70
+ * quickly switch back to using PeerDist acceleration if new peers
71
+ * become available.
72
+ */
73
+unsigned int peerdisc_timeout_secs = PEERDISC_DEFAULT_TIMEOUT_SECS;
74
+
75
+static struct peerdisc_segment * peerdisc_find ( const char *id );
76
+static int peerdisc_discovered ( struct peerdisc_segment *segment,
77
+				 const char *location );
78
+
79
+/******************************************************************************
80
+ *
81
+ * Discovery sockets
82
+ *
83
+ ******************************************************************************
84
+ */
85
+
86
+/**
87
+ * Open all PeerDist discovery sockets
88
+ *
89
+ * @ret rc		Return status code
90
+ */
91
+static int peerdisc_socket_open ( void ) {
92
+	struct peerdisc_socket *socket;
93
+	int rc;
94
+
95
+	/* Open each socket */
96
+	for_each_table_entry ( socket, PEERDISC_SOCKETS ) {
97
+		if ( ( rc = xfer_open_socket ( &socket->xfer, SOCK_DGRAM,
98
+					       &socket->address.sa,
99
+					       NULL ) ) != 0 ) {
100
+			DBGC ( socket, "PEERDISC %s could not open socket: "
101
+			       "%s\n", socket->name, strerror ( rc ) );
102
+			goto err;
103
+		}
104
+	}
105
+
106
+	return 0;
107
+
108
+ err:
109
+	for_each_table_entry_continue_reverse ( socket, PEERDISC_SOCKETS )
110
+		intf_restart ( &socket->xfer, rc );
111
+	return rc;
112
+}
113
+
114
+/**
115
+ * Attempt to transmit PeerDist discovery requests on all sockets
116
+ *
117
+ * @v uuid		Message UUID string
118
+ * @v id		Segment identifier string
119
+ */
120
+static void peerdisc_socket_tx ( const char *uuid, const char *id ) {
121
+	struct peerdisc_socket *socket;
122
+	struct net_device *netdev;
123
+	struct xfer_metadata meta;
124
+	union {
125
+		struct sockaddr sa;
126
+		struct sockaddr_tcpip st;
127
+	} address;
128
+	char *request;
129
+	size_t len;
130
+	int rc;
131
+
132
+	/* Construct discovery request */
133
+	request = peerdist_discovery_request ( uuid, id );
134
+	if ( ! request )
135
+		goto err_request;
136
+	len = strlen ( request );
137
+
138
+	/* Initialise data transfer metadata */
139
+	memset ( &meta, 0, sizeof ( meta ) );
140
+	meta.dest = &address.sa;
141
+
142
+	/* Send message on each socket */
143
+	for_each_table_entry ( socket, PEERDISC_SOCKETS ) {
144
+
145
+		/* Initialise socket address */
146
+		memcpy ( &address.sa, &socket->address.sa,
147
+			 sizeof ( address.sa ) );
148
+
149
+		/* Send message on each open network device */
150
+		for_each_netdev ( netdev ) {
151
+
152
+			/* Skip unopened network devices */
153
+			if ( ! netdev_is_open ( netdev ) )
154
+				continue;
155
+			address.st.st_scope_id = netdev->index;
156
+
157
+			/* Discard request (for test purposes) if applicable */
158
+			if ( inject_fault ( PEERDISC_DISCARD_RATE ) )
159
+				continue;
160
+
161
+			/* Transmit request */
162
+			if ( ( rc = xfer_deliver_raw_meta ( &socket->xfer,
163
+							    request, len,
164
+							    &meta ) ) != 0 ) {
165
+				DBGC ( socket, "PEERDISC %s could not transmit "
166
+				       "via %s: %s\n", socket->name,
167
+				       netdev->name, strerror ( rc ) );
168
+				/* Contine to try other net devices/sockets */
169
+				continue;
170
+			}
171
+		}
172
+	}
173
+
174
+	free ( request );
175
+ err_request:
176
+	return;
177
+}
178
+
179
+/**
180
+ * Handle received PeerDist discovery reply
181
+ *
182
+ * @v socket		PeerDist discovery socket
183
+ * @v iobuf		I/O buffer
184
+ * @v meta		Data transfer metadata
185
+ * @ret rc		Return status code
186
+ */
187
+static int peerdisc_socket_rx ( struct peerdisc_socket *socket,
188
+				struct io_buffer *iobuf,
189
+				struct xfer_metadata *meta __unused ) {
190
+	struct peerdist_discovery_reply reply;
191
+	struct peerdisc_segment *segment;
192
+	char *id;
193
+	char *location;
194
+	int rc;
195
+
196
+	/* Discard reply (for test purposes) if applicable */
197
+	if ( ( rc = inject_fault ( PEERDISC_DISCARD_RATE ) ) != 0 )
198
+		goto err;
199
+
200
+	/* Parse reply */
201
+	if ( ( rc = peerdist_discovery_reply ( iobuf->data, iob_len ( iobuf ),
202
+					       &reply ) ) != 0 ) {
203
+		DBGC ( socket, "PEERDISC %s could not parse reply: %s\n",
204
+		       socket->name, strerror ( rc ) );
205
+		DBGC_HDA ( socket, 0, iobuf->data, iob_len ( iobuf ) );
206
+		goto err;
207
+	}
208
+
209
+	/* Any kind of discovery reply indicates that there are active
210
+	 * peers on a local network, so restore the recommended
211
+	 * discovery timeout to its default value for future requests.
212
+	 */
213
+	if ( peerdisc_timeout_secs != PEERDISC_DEFAULT_TIMEOUT_SECS ) {
214
+		DBGC ( socket, "PEERDISC %s restoring timeout to %d seconds\n",
215
+		       socket->name, PEERDISC_DEFAULT_TIMEOUT_SECS );
216
+	}
217
+	peerdisc_timeout_secs = PEERDISC_DEFAULT_TIMEOUT_SECS;
218
+
219
+	/* Iterate over segment IDs */
220
+	for ( id = reply.ids ; *id ; id += ( strlen ( id ) + 1 /* NUL */ ) ) {
221
+
222
+		/* Find corresponding segment */
223
+		segment = peerdisc_find ( id );
224
+		if ( ! segment ) {
225
+			DBGC ( socket, "PEERDISC %s ignoring reply for %s\n",
226
+			       socket->name, id );
227
+			continue;
228
+		}
229
+
230
+		/* Report all discovered peer locations */
231
+		for ( location = reply.locations ; *location ;
232
+		      location += ( strlen ( location ) + 1 /* NUL */ ) ) {
233
+
234
+			/* Report discovered peer location */
235
+			if ( ( rc = peerdisc_discovered ( segment,
236
+							  location ) ) != 0 )
237
+				goto err;
238
+		}
239
+	}
240
+
241
+ err:
242
+	free_iob ( iobuf );
243
+	return rc;
244
+}
245
+
246
+/**
247
+ * Close all PeerDist discovery sockets
248
+ *
249
+ * @v rc		Reason for close
250
+ */
251
+static void peerdisc_socket_close ( int rc ) {
252
+	struct peerdisc_socket *socket;
253
+
254
+	/* Close all sockets */
255
+	for_each_table_entry ( socket, PEERDISC_SOCKETS )
256
+		intf_restart ( &socket->xfer, rc );
257
+}
258
+
259
+/** PeerDist discovery socket interface operations */
260
+static struct interface_operation peerdisc_socket_operations[] = {
261
+	INTF_OP ( xfer_deliver, struct peerdisc_socket *, peerdisc_socket_rx ),
262
+};
263
+
264
+/** PeerDist discovery socket interface descriptor */
265
+static struct interface_descriptor peerdisc_socket_desc =
266
+	INTF_DESC ( struct peerdisc_socket, xfer, peerdisc_socket_operations );
267
+
268
+/** PeerDist discovery IPv4 socket */
269
+struct peerdisc_socket peerdisc_socket_ipv4 __peerdisc_socket = {
270
+	.name = "IPv4",
271
+	.address = {
272
+		.sin = {
273
+			.sin_family = AF_INET,
274
+			.sin_port = htons ( PEERDIST_DISCOVERY_PORT ),
275
+			.sin_addr.s_addr = htonl ( PEERDIST_DISCOVERY_IPV4 ),
276
+		},
277
+	},
278
+	.xfer = INTF_INIT ( peerdisc_socket_desc ),
279
+};
280
+
281
+/** PeerDist discovery IPv6 socket */
282
+struct peerdisc_socket peerdisc_socket_ipv6 __peerdisc_socket = {
283
+	.name = "IPv6",
284
+	.address = {
285
+		.sin6 = {
286
+			.sin6_family = AF_INET6,
287
+			.sin6_port = htons ( PEERDIST_DISCOVERY_PORT ),
288
+			.sin6_addr.s6_addr = PEERDIST_DISCOVERY_IPV6,
289
+		},
290
+	},
291
+	.xfer = INTF_INIT ( peerdisc_socket_desc ),
292
+};
293
+
294
+/******************************************************************************
295
+ *
296
+ * Discovery segments
297
+ *
298
+ ******************************************************************************
299
+ */
300
+
301
+/**
302
+ * Free PeerDist discovery segment
303
+ *
304
+ * @v refcnt		Reference count
305
+ */
306
+static void peerdisc_free ( struct refcnt *refcnt ) {
307
+	struct peerdisc_segment *segment =
308
+		container_of ( refcnt, struct peerdisc_segment, refcnt );
309
+	struct peerdisc_peer *peer;
310
+	struct peerdisc_peer *tmp;
311
+
312
+	/* Free all discovered peers */
313
+	list_for_each_entry_safe ( peer, tmp, &segment->peers, list ) {
314
+		list_del ( &peer->list );
315
+		free ( peer );
316
+	}
317
+
318
+	/* Free segment */
319
+	free ( segment );
320
+}
321
+
322
+/**
323
+ * Find PeerDist discovery segment
324
+ *
325
+ * @v id		Segment ID
326
+ * @ret segment		PeerDist discovery segment, or NULL if not found
327
+ */
328
+static struct peerdisc_segment * peerdisc_find ( const char *id ) {
329
+	struct peerdisc_segment *segment;
330
+
331
+	/* Look for a matching segment */
332
+	list_for_each_entry ( segment, &peerdisc_segments, list ) {
333
+		if ( strcmp ( id, segment->id ) == 0 )
334
+			return segment;
335
+	}
336
+
337
+	return NULL;
338
+}
339
+
340
+/**
341
+ * Add discovered PeerDist peer
342
+ *
343
+ * @v segment		PeerDist discovery segment
344
+ * @v location		Peer location
345
+ * @ret rc		Return status code
346
+ */
347
+static int peerdisc_discovered ( struct peerdisc_segment *segment,
348
+				 const char *location ) {
349
+	struct peerdisc_peer *peer;
350
+	struct peerdisc_client *peerdisc;
351
+	struct peerdisc_client *tmp;
352
+
353
+	/* Ignore duplicate peers */
354
+	list_for_each_entry ( peer, &segment->peers, list ) {
355
+		if ( strcmp ( peer->location, location ) == 0 ) {
356
+			DBGC2 ( segment, "PEERDISC %p duplicate %s\n",
357
+				segment, location );
358
+			return 0;
359
+		}
360
+	}
361
+	DBGC2 ( segment, "PEERDISC %p discovered %s\n", segment, location );
362
+
363
+	/* Allocate and initialise structure */
364
+	peer = zalloc ( sizeof ( *peer ) + strlen ( location ) + 1 /* NUL */ );
365
+	if ( ! peer )
366
+		return -ENOMEM;
367
+	strcpy ( peer->location, location );
368
+
369
+	/* Add to end of list of peers */
370
+	list_add_tail ( &peer->list, &segment->peers );
371
+
372
+	/* Notify all clients */
373
+	list_for_each_entry_safe ( peerdisc, tmp, &segment->clients, list )
374
+		peerdisc->op->discovered ( peerdisc );
375
+
376
+	return 0;
377
+}
378
+
379
+/**
380
+ * Handle discovery timer expiry
381
+ *
382
+ * @v timer		Discovery timer
383
+ * @v over		Failure indicator
384
+ */
385
+static void peerdisc_expired ( struct retry_timer *timer, int over __unused ) {
386
+	struct peerdisc_segment *segment =
387
+		container_of ( timer, struct peerdisc_segment, timer );
388
+
389
+	/* Attempt to transmit discovery requests */
390
+	peerdisc_socket_tx ( segment->uuid, segment->id );
391
+
392
+	/* Schedule next transmission, if applicable */
393
+	if ( timer->count < PEERDISC_REPEAT_COUNT )
394
+		start_timer_fixed ( &segment->timer, PEERDISC_REPEAT_TIMEOUT );
395
+}
396
+
397
+/**
398
+ * Create PeerDist discovery segment
399
+ *
400
+ * @v id		Segment ID
401
+ * @ret segment		PeerDist discovery segment, or NULL on error
402
+ */
403
+static struct peerdisc_segment * peerdisc_create ( const char *id ) {
404
+	struct peerdisc_segment *segment;
405
+	union {
406
+		union uuid uuid;
407
+		uint32_t dword[ sizeof ( union uuid ) / sizeof ( uint32_t ) ];
408
+	} random_uuid;
409
+	size_t uuid_len;
410
+	size_t id_len;
411
+	char *uuid;
412
+	char *uuid_copy;
413
+	char *id_copy;
414
+	unsigned int i;
415
+
416
+	/* Generate a random message UUID.  This does not require high
417
+	 * quality randomness.
418
+	 */
419
+	for ( i = 0 ; i < ( sizeof ( random_uuid.dword ) /
420
+			    sizeof ( random_uuid.dword[0] ) ) ; i++ )
421
+		random_uuid.dword[i] = random();
422
+	uuid = uuid_ntoa ( &random_uuid.uuid );
423
+
424
+	/* Calculate string lengths */
425
+	id_len = ( strlen ( id ) + 1 /* NUL */ );
426
+	uuid_len = ( strlen ( uuid ) + 1 /* NUL */ );
427
+
428
+	/* Allocate and initialise structure */
429
+	segment = zalloc ( sizeof ( *segment ) + id_len + uuid_len );
430
+	if ( ! segment )
431
+		return NULL;
432
+	id_copy = ( ( ( void * ) segment ) + sizeof ( *segment ) );
433
+	memcpy ( id_copy, id, id_len );
434
+	uuid_copy = ( ( ( void * ) id_copy ) + id_len );
435
+	memcpy ( uuid_copy, uuid, uuid_len );
436
+	ref_init ( &segment->refcnt, peerdisc_free );
437
+	segment->id = id_copy;
438
+	segment->uuid = uuid_copy;
439
+	INIT_LIST_HEAD ( &segment->peers );
440
+	INIT_LIST_HEAD ( &segment->clients );
441
+	timer_init ( &segment->timer, peerdisc_expired, &segment->refcnt );
442
+	DBGC2 ( segment, "PEERDISC %p discovering %s\n", segment, segment->id );
443
+
444
+	/* Start discovery timer */
445
+	start_timer_nodelay ( &segment->timer );
446
+
447
+	/* Add to list of segments, transfer reference to list, and return */
448
+	list_add_tail ( &segment->list, &peerdisc_segments );
449
+	return segment;
450
+}
451
+
452
+/**
453
+ * Destroy PeerDist discovery segment
454
+ *
455
+ * @v segment		PeerDist discovery segment
456
+ */
457
+static void peerdisc_destroy ( struct peerdisc_segment *segment ) {
458
+
459
+	/* Sanity check */
460
+	assert ( list_empty ( &segment->clients ) );
461
+
462
+	/* Stop timer */
463
+	stop_timer ( &segment->timer );
464
+
465
+	/* Remove from list of segments and drop list's reference */
466
+	list_del ( &segment->list );
467
+	ref_put ( &segment->refcnt );
468
+}
469
+
470
+/******************************************************************************
471
+ *
472
+ * Discovery clients
473
+ *
474
+ ******************************************************************************
475
+ */
476
+
477
+/**
478
+ * Open PeerDist discovery client
479
+ *
480
+ * @v peerdisc		PeerDist discovery client
481
+ * @v id		Segment ID
482
+ * @v len		Length of segment ID
483
+ * @ret rc		Return status code
484
+ */
485
+int peerdisc_open ( struct peerdisc_client *peerdisc, const void *id,
486
+		    size_t len ) {
487
+	struct peerdisc_segment *segment;
488
+	char id_string[ base16_encoded_len ( len ) + 1 /* NUL */ ];
489
+	char *id_chr;
490
+	int rc;
491
+
492
+	/* Construct ID string */
493
+	base16_encode ( id, len, id_string, sizeof ( id_string ) );
494
+	for ( id_chr = id_string ; *id_chr ; id_chr++ )
495
+		*id_chr = toupper ( *id_chr );
496
+
497
+	/* Sanity check */
498
+	assert ( peerdisc->segment == NULL );
499
+
500
+	/* Open socket if this is the first segment */
501
+	if ( list_empty ( &peerdisc_segments ) &&
502
+	     ( ( rc = peerdisc_socket_open() ) != 0 ) )
503
+		return rc;
504
+
505
+	/* Find or create segment */
506
+	if ( ! ( ( segment = peerdisc_find ( id_string ) ) ||
507
+		 ( segment = peerdisc_create ( id_string ) ) ) )
508
+		return -ENOMEM;
509
+
510
+	/* Add to list of clients */
511
+	ref_get ( &segment->refcnt );
512
+	peerdisc->segment = segment;
513
+	list_add_tail ( &peerdisc->list, &segment->clients );
514
+
515
+	return 0;
516
+}
517
+
518
+/**
519
+ * Close PeerDist discovery client
520
+ *
521
+ * @v peerdisc		PeerDist discovery client
522
+ */
523
+void peerdisc_close ( struct peerdisc_client *peerdisc ) {
524
+	struct peerdisc_segment *segment = peerdisc->segment;
525
+
526
+	/* Ignore if discovery is already closed */
527
+	if ( ! segment )
528
+		return;
529
+
530
+	/* If no peers were discovered, reduce the recommended
531
+	 * discovery timeout to minimise delays on future requests.
532
+	 */
533
+	if ( list_empty ( &segment->peers ) && peerdisc_timeout_secs ) {
534
+		peerdisc_timeout_secs--;
535
+		DBGC ( segment, "PEERDISC %p reducing timeout to %d "
536
+		       "seconds\n", peerdisc, peerdisc_timeout_secs );
537
+	}
538
+
539
+	/* Remove from list of clients */
540
+	peerdisc->segment = NULL;
541
+	list_del ( &peerdisc->list );
542
+	ref_put ( &segment->refcnt );
543
+
544
+	/* If this was the last clients, destroy the segment */
545
+	if ( list_empty ( &segment->clients ) )
546
+		peerdisc_destroy ( segment );
547
+
548
+	/* If there are no more segments, close the socket */
549
+	if ( list_empty ( &peerdisc_segments ) )
550
+		peerdisc_socket_close ( 0 );
551
+}

Loading…
Cancel
Save