diff --git a/src/include/ipxe/peerdisc.h b/src/include/ipxe/peerdisc.h index f08ccaae..45d592e7 100644 --- a/src/include/ipxe/peerdisc.h +++ b/src/include/ipxe/peerdisc.h @@ -109,6 +109,12 @@ peerdisc_init ( struct peerdisc_client *peerdisc, 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 ); diff --git a/src/include/ipxe/peermux.h b/src/include/ipxe/peermux.h index 44cbdb9d..54acbfec 100644 --- a/src/include/ipxe/peermux.h +++ b/src/include/ipxe/peermux.h @@ -41,6 +41,16 @@ struct peerdist_multiplexed_block { 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 */ @@ -65,6 +75,9 @@ struct peerdist_multiplexer { 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, diff --git a/src/net/peerblk.c b/src/net/peerblk.c index 9fd52b73..78888d2d 100644 --- a/src/net/peerblk.c +++ b/src/net/peerblk.c @@ -270,6 +270,9 @@ static int peerblk_deliver ( struct peerdist_block *peerblk, */ 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(); @@ -296,6 +299,11 @@ static void peerblk_done ( struct peerdist_block *peerblk, int rc ) { 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; diff --git a/src/net/peerdisc.c b/src/net/peerdisc.c index 4c3cd2ea..20ac2427 100644 --- a/src/net/peerdisc.c +++ b/src/net/peerdisc.c @@ -76,6 +76,36 @@ static struct peerdisc_segment * peerdisc_find ( const char *id ); 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 diff --git a/src/net/peermux.c b/src/net/peermux.c index 634c6999..a391ed37 100644 --- a/src/net/peermux.c +++ b/src/net/peermux.c @@ -24,9 +24,11 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include +#include #include #include #include +#include #include #include @@ -74,6 +76,28 @@ static void peermux_close ( struct peerdist_multiplexer *peermux, int rc ) { 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 * @@ -274,6 +298,35 @@ peermux_block_buffer ( struct peerdist_multiplexed_block *peermblk ) { 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 * @@ -303,6 +356,8 @@ static void peermux_block_close ( struct peerdist_multiplexed_block *peermblk, /** 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 ), }; @@ -330,6 +385,8 @@ static struct interface_operation peermux_block_operations[] = { 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 ), };