extern unsigned int peerdisc_timeout_secs;
+extern void peerdisc_stat ( struct interface *intf, struct peerdisc_peer *peer,
+ struct list_head *peers );
+#define peerdisc_stat_TYPE( object_type ) \
+ typeof ( void ( object_type, struct peerdisc_peer *peer, \
+ struct list_head *peers ) )
+
extern int peerdisc_open ( struct peerdisc_client *peerdisc, const void *id,
size_t len );
extern void peerdisc_close ( struct peerdisc_client *peerdisc );
struct interface xfer;
};
+/** PeerDist statistics */
+struct peerdist_statistics {
+ /** Maximum observed number of peers */
+ unsigned int peers;
+ /** Number of blocks downloaded in total */
+ unsigned int total;
+ /** Number of blocks downloaded from peers */
+ unsigned int local;
+};
+
/** A PeerDist download multiplexer */
struct peerdist_multiplexer {
/** Reference count */
struct list_head idle;
/** Block downloads */
struct peerdist_multiplexed_block block[PEERMUX_MAX_BLOCKS];
+
+ /** Statistics */
+ struct peerdist_statistics stats;
};
extern int peermux_filter ( struct interface *xfer, struct interface *info,
*/
static void peerblk_done ( struct peerdist_block *peerblk, int rc ) {
struct digest_algorithm *digest = peerblk->digest;
+ struct peerdisc_segment *segment = peerblk->discovery.segment;
+ struct peerdisc_peer *head;
+ struct peerdisc_peer *peer;
uint8_t hash[digest->digestsize];
unsigned long now = peerblk_timestamp();
profile_custom ( &peerblk_attempt_success_profiler,
( now - peerblk->attempted ) );
+ /* Report peer statistics */
+ head = list_entry ( &segment->peers, struct peerdisc_peer, list );
+ peer = ( ( peerblk->peer == head ) ? NULL : peerblk->peer );
+ peerdisc_stat ( &peerblk->xfer, peer, &segment->peers );
+
/* Close download */
peerblk_close ( peerblk, 0 );
return;
static int peerdisc_discovered ( struct peerdisc_segment *segment,
const char *location );
+/******************************************************************************
+ *
+ * Statistics reporting
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Report peer discovery statistics
+ *
+ * @v intf Interface
+ * @v peer Selected peer (or NULL)
+ * @v peers List of available peers
+ */
+void peerdisc_stat ( struct interface *intf, struct peerdisc_peer *peer,
+ struct list_head *peers ) {
+ struct interface *dest;
+ peerdisc_stat_TYPE ( void * ) *op =
+ intf_get_dest_op ( intf, peerdisc_stat, &dest );
+ void *object = intf_object ( dest );
+
+ if ( op ) {
+ op ( object, peer, peers );
+ } else {
+ /* Default is to do nothing */
+ }
+
+ intf_put ( dest );
+}
+
/******************************************************************************
*
* Discovery sockets
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdlib.h>
+#include <stdio.h>
#include <errno.h>
#include <ipxe/uri.h>
#include <ipxe/xferbuf.h>
+#include <ipxe/job.h>
#include <ipxe/peerblk.h>
#include <ipxe/peermux.h>
intf_shutdown ( &peermux->info, rc );
}
+/**
+ * Report progress of PeerDist download
+ *
+ * @v peermux PeerDist download multiplexer
+ * @v progress Progress report to fill in
+ * @ret ongoing_rc Ongoing job status code (if known)
+ */
+static int peermux_progress ( struct peerdist_multiplexer *peermux,
+ struct job_progress *progress ) {
+ struct peerdist_statistics *stats = &peermux->stats;
+ unsigned int percentage;
+
+ /* Construct PeerDist status message */
+ if ( stats->total ) {
+ percentage = ( ( 100 * stats->local ) / stats->total );
+ snprintf ( progress->message, sizeof ( progress->message ),
+ "%3d%% from %d peers", percentage, stats->peers );
+ }
+
+ return 0;
+}
+
/**
* Receive content information
*
return xfer_buffer ( &peermux->xfer );
}
+/**
+ * Record peer discovery statistics
+ *
+ * @v peermblk PeerDist multiplexed block download
+ * @v peer Selected peer (or NULL)
+ * @v peers List of available peers
+ */
+static void peermux_block_stat ( struct peerdist_multiplexed_block *peermblk,
+ struct peerdisc_peer *peer,
+ struct list_head *peers ) {
+ struct peerdist_multiplexer *peermux = peermblk->peermux;
+ struct peerdist_statistics *stats = &peermux->stats;
+ struct peerdisc_peer *tmp;
+ unsigned int count = 0;
+
+ /* Record maximum number of available peers */
+ list_for_each_entry ( tmp, peers, list )
+ count++;
+ if ( count > stats->peers )
+ stats->peers = count;
+
+ /* Update block counts */
+ if ( peer )
+ stats->local++;
+ stats->total++;
+ DBGC2 ( peermux, "PEERMUX %p downloaded %d/%d from %d peers\n",
+ peermux, stats->local, stats->total, stats->peers );
+}
+
/**
* Close multiplexed block download
*
/** Data transfer interface operations */
static struct interface_operation peermux_xfer_operations[] = {
+ INTF_OP ( job_progress, struct peerdist_multiplexer *,
+ peermux_progress ),
INTF_OP ( intf_close, struct peerdist_multiplexer *, peermux_close ),
};
peermux_block_deliver ),
INTF_OP ( xfer_buffer, struct peerdist_multiplexed_block *,
peermux_block_buffer ),
+ INTF_OP ( peerdisc_stat, struct peerdist_multiplexed_block *,
+ peermux_block_stat ),
INTF_OP ( intf_close, struct peerdist_multiplexed_block *,
peermux_block_close ),
};