1
0
Fork 0
mirror of https://github.com/transmission/transmission synced 2025-03-15 16:29:34 +00:00

adding experimental implementation of Tamilmani's `Swift' tit-for-tat algorithm for testing. To tweak or disable, change the values around line 50 of libtransmission/peer.c

This commit is contained in:
Charles Kerr 2007-06-21 14:47:26 +00:00
parent da55c0df90
commit e161544761
5 changed files with 204 additions and 104 deletions

View file

@ -25,6 +25,51 @@
#include "transmission.h"
#include "peertree.h"
/*****
******
*****/
/**
*** The "SWIFT" system is described by Karthik Tamilmani,
*** Vinay Pai, and Alexander Mohr of Stony Brook University
*** in their paper "SWIFT: A System With Incentives For Trading"
*** http://citeseer.ist.psu.edu/tamilmani04swift.html
**/
/**
* Use SWIFT?
*/
static const int SWIFT_ENABLED = 1;
/**
* For every byte the peer uploads to us,
* allow them to download this many bytes from us
*/
static const double SWIFT_REPAYMENT_RATIO = 1.1;
/**
* Allow new peers to download this many bytes from
* us when getting started. This can prevent gridlock
* with other peers using tit-for-tat algorithms
*/
static const int SWIFT_INITIAL_CREDIT = 64 * 1024; /* 64 KiB */
/**
* We expend a fraction of our torrent's total upload speed
* on on largesse by uniformly distributing free credit to
* all of our peers. This too helps prevent gridlock.
*/
static const double SWIFT_LARGESSE = 0.05; /* 5% of our UL */
/**
* How frequently to extend largesse-based credit
*/
static const int SWIFT_REFRESH_INTERVAL_SEC = 5;
/*****
******
*****/
#define PERCENT_PEER_WANTED 25 /* Percent before we start relax peers min activeness */
#define MIN_UPLOAD_IDLE 60000 /* In high peer situations we wait only 1 min
until dropping peers for idling */
@ -39,7 +84,7 @@
#define MAX_CON_TIMEOUT 30000 /* Time to timeout connecting to peer,
during high peer situations */
#define MAX_REQUEST_COUNT 32
#define OUR_REQUEST_COUNT 8 /* TODO: we should detect if we are on a
#define OUR_REQUEST_COUNT 12 /* TODO: we should detect if we are on a
high-speed network and adapt */
#define PEX_PEER_CUTOFF 50 /* only try to add new peers from pex if
we have fewer existing peers than this */
@ -150,6 +195,8 @@ struct tr_peer_s
tr_ratecontrol_t * upload;
char * client;
int64_t credit;
};
#define peer_dbg( a... ) __peer_dbg( peer, ## a )
@ -190,6 +237,7 @@ tr_peer_t * tr_peerInit( struct in_addr addr, in_port_t port, int s, int from )
peer->addr = addr;
peer->port = port;
peer->from = from;
peer->credit = SWIFT_INITIAL_CREDIT;
if( s >= 0 )
{
assert( TR_PEER_FROM_INCOMING == from );
@ -219,14 +267,8 @@ void tr_peerDestroy( tr_peer_t * peer )
tr_bitfieldFree( peer->bitfield );
tr_bitfieldFree( peer->blamefield );
tr_bitfieldFree( peer->banfield );
if( peer->buf )
{
free( peer->buf );
}
if( peer->outMessages )
{
free( peer->outMessages );
}
tr_free( peer->buf );
tr_free( peer->outMessages );
if( peer->status > PEER_STATUS_IDLE )
{
tr_netClose( peer->socket );
@ -330,13 +372,6 @@ int tr_peerRead( tr_peer_t * peer )
peer->pos += ret;
if( NULL != tor )
{
tr_rcTransferred( peer->download, ret );
tr_rcTransferred( tor->download, ret );
if ( !tor->customDownloadLimit )
{
tr_rcTransferred( tor->handle->download, ret );
}
if( tr_peerAmInterested( peer ) && !tr_peerIsChoking( peer ) )
{
tor->activityDate = date;
@ -359,21 +394,11 @@ int tr_peerRead( tr_peer_t * peer )
return TR_OK;
}
uint64_t tr_peerDate( tr_peer_t * peer )
uint64_t tr_peerDate( const tr_peer_t * peer )
{
return peer->date;
}
/***********************************************************************
* tr_peerId
***********************************************************************
*
**********************************************************************/
uint8_t * tr_peerId( tr_peer_t * peer )
{
return & peer->id[0];
}
/***********************************************************************
* tr_peerAddress
***********************************************************************
@ -389,7 +414,7 @@ struct in_addr * tr_peerAddress( tr_peer_t * peer )
***********************************************************************
*
**********************************************************************/
uint8_t * tr_peerHash( tr_peer_t * peer )
const uint8_t * tr_peerHash( const tr_peer_t * peer )
{
return parseBufHash( peer );
}
@ -405,6 +430,7 @@ int tr_peerPulse( tr_peer_t * peer )
int ret, size;
uint8_t * p;
uint64_t date;
const int isSeeding = tr_cpGetStatus( tor->completion ) != TR_CP_INCOMPLETE;
if( ( ret = checkPeer( peer ) ) )
{
@ -511,6 +537,11 @@ writeBegin:
/* Send pieces if we can */
while( ( p = blockPending( tor, peer, &size ) ) )
{
if( SWIFT_ENABLED && !isSeeding && (peer->credit<0) )
{
break;
}
if( tor->customUploadLimit
? !tr_rcCanTransfer( tor->upload )
: !tr_rcCanTransfer( tor->handle->upload ) )
@ -529,12 +560,9 @@ writeBegin:
}
blockSent( peer, ret );
tr_rcTransferred( peer->upload, ret );
tr_rcTransferred( tor->upload, ret );
if ( !tor->customUploadLimit )
{
tr_rcTransferred( tor->handle->upload, ret );
}
if( ret > 0 )
tr_peerGotBlockFromUs( peer, ret );
tor->uploadedCur += ret;
peer->outTotal += ret;
@ -554,7 +582,7 @@ writeBegin:
writeEnd:
/* Ask for a block whenever possible */
if( tr_cpGetStatus( tor->completion ) == TR_CP_INCOMPLETE
if( !isSeeding
&& !peer->amInterested
&& tor->peerCount > TR_MAX_PEER_COUNT - 2 )
{
@ -604,39 +632,29 @@ writeEnd:
return TR_OK;
}
/***********************************************************************
* tr_peerIsConnected
***********************************************************************
*
**********************************************************************/
int tr_peerIsConnected( tr_peer_t * peer )
int tr_peerIsConnected( const tr_peer_t * peer )
{
return PEER_STATUS_CONNECTED == peer->status;
}
/***********************************************************************
* tr_peerIsIncoming
***********************************************************************
*
**********************************************************************/
int tr_peerIsFrom( tr_peer_t * peer )
int tr_peerIsFrom( const tr_peer_t * peer )
{
return peer->from;
}
int tr_peerAmChoking( tr_peer_t * peer )
int tr_peerAmChoking( const tr_peer_t * peer )
{
return peer->amChoking;
}
int tr_peerAmInterested( tr_peer_t * peer )
int tr_peerAmInterested( const tr_peer_t * peer )
{
return peer->amInterested;
}
int tr_peerIsChoking( tr_peer_t * peer )
int tr_peerIsChoking( const tr_peer_t * peer )
{
return peer->peerChoking;
}
int tr_peerIsInterested( tr_peer_t * peer )
int tr_peerIsInterested( const tr_peer_t * peer )
{
return peer->peerInterested;
}
@ -646,7 +664,7 @@ int tr_peerIsInterested( tr_peer_t * peer )
***********************************************************************
*
**********************************************************************/
float tr_peerProgress( tr_peer_t * peer )
float tr_peerProgress( const tr_peer_t * peer )
{
return peer->progress;
}
@ -656,27 +674,22 @@ float tr_peerProgress( tr_peer_t * peer )
***********************************************************************
* Returns peer's listening port in host byte order
**********************************************************************/
int tr_peerPort( tr_peer_t * peer )
int tr_peerPort( const tr_peer_t * peer )
{
return ntohs( peer->port );
}
/***********************************************************************
* tr_peerBitfield
***********************************************************************
*
**********************************************************************/
tr_bitfield_t * tr_peerBitfield( tr_peer_t * peer )
int tr_peerHasPiece( const tr_peer_t * peer, int pieceIndex )
{
return peer->bitfield;
return tr_bitfieldHas( peer->bitfield, pieceIndex );
}
float tr_peerDownloadRate( tr_peer_t * peer )
float tr_peerDownloadRate( const tr_peer_t * peer )
{
return tr_rcRate( peer->download );
}
float tr_peerUploadRate( tr_peer_t * peer )
float tr_peerUploadRate( const tr_peer_t * peer )
{
return tr_rcRate( peer->upload );
}
@ -693,7 +706,7 @@ void tr_peerUnchoke( tr_peer_t * peer )
peer->lastChoke = tr_date();
}
uint64_t tr_peerLastChoke( tr_peer_t * peer )
uint64_t tr_peerLastChoke( const tr_peer_t * peer )
{
return peer->lastChoke;
}
@ -703,19 +716,19 @@ void tr_peerSetOptimistic( tr_peer_t * peer, int o )
peer->optimistic = o;
}
int tr_peerIsOptimistic( tr_peer_t * peer )
int tr_peerIsOptimistic( const tr_peer_t * peer )
{
return peer->optimistic;
}
static inline int peerIsBad( tr_peer_t * peer )
static int peerIsBad( const tr_peer_t * peer )
{
return ( peer->badPcs > 4 + 2 * peer->goodPcs );
return peer->badPcs > 4 + 2 * peer->goodPcs;
}
static inline int peerIsGood( tr_peer_t * peer )
static int peerIsGood( const tr_peer_t * peer )
{
return ( peer->goodPcs > 3 * peer->badPcs );
return peer->goodPcs > 3 * peer->badPcs;
}
void tr_peerBlame( tr_peer_t * peer, int piece, int success )
@ -795,3 +808,85 @@ int tr_peerGetConnectable( const tr_torrent_t * tor, uint8_t ** _buf )
return count * 6;
}
/***
****
***/
void
tr_peerSentBlockToUs ( tr_peer_t * peer, int byteCount )
{
tr_torrent_t * tor = peer->tor;
tr_rcTransferred( peer->download, byteCount );
tr_rcTransferred( tor->download, byteCount );
if ( !tor->customUploadLimit )
tr_rcTransferred( tor->handle->download, byteCount );
peer->credit += (int)(byteCount * SWIFT_REPAYMENT_RATIO);
}
void
tr_peerGotBlockFromUs ( tr_peer_t * peer, int byteCount )
{
tr_torrent_t * tor = peer->tor;
tr_rcTransferred( peer->upload, byteCount );
tr_rcTransferred( tor->upload, byteCount );
if ( !tor->customDownloadLimit )
tr_rcTransferred( tor->handle->upload, byteCount );
peer->credit -= byteCount;
}
static void
tr_torrentSwiftPulse ( tr_torrent_t * tor )
{
int i;
int deadbeatCount = 0;
tr_peer_t ** deadbeats;
tr_lockLock( &tor->lock );
deadbeats = tr_calloc( tor->peerCount, sizeof(tr_peer_t*) );
for( i=0; i<tor->peerCount; ++i ) {
tr_peer_t * peer = tor->peers[ i ];
if( tr_peerIsConnected( peer ) && ( peer->credit < 0 ) )
deadbeats[deadbeatCount++] = peer;
}
if( deadbeatCount )
{
const double ul_KiBsec = tr_rcRate( tor->upload );
const double ul_KiB = ul_KiBsec * SWIFT_REFRESH_INTERVAL_SEC;
const double ul_bytes = ul_KiB * 1024;
const double freeCreditTotal = ul_bytes * SWIFT_LARGESSE;
const int freeCreditPerPeer = (int)( freeCreditTotal / deadbeatCount );
for( i=0; i<deadbeatCount; ++i )
deadbeats[i]->credit = freeCreditPerPeer;
tr_dbg( "torrent %s has %d deadbeats, "
"who are each being granted %d bytes' credit "
"for a total of %.1f KiB, "
"%d%% of the torrent's ul speed %.1f\n",
tor->info.name, deadbeatCount, freeCreditPerPeer,
ul_KiBsec*SWIFT_LARGESSE, (int)(SWIFT_LARGESSE*100), ul_KiBsec );
}
free( deadbeats );
tr_lockUnlock( &tor->lock );
}
void
tr_swiftPulse( tr_handle_t * h )
{
static time_t lastPulseTime = 0;
const time_t now = time (NULL);
tr_torrent_t * tor;
if( lastPulseTime + SWIFT_REFRESH_INTERVAL_SEC > now )
return;
lastPulseTime = now;
for( tor=h->torrentList; tor; tor=tor->next )
tr_torrentSwiftPulse( tor );
}

View file

@ -29,34 +29,37 @@
typedef struct tr_peer_s tr_peer_t;
tr_peer_t * tr_peerInit ( struct in_addr, in_port_t, int sock, int );
void tr_peerDestroy ( tr_peer_t * );
const char *tr_peerClient ( tr_peer_t * );
void tr_peerSetPrivate ( tr_peer_t *, int );
void tr_peerSetTorrent ( tr_peer_t *, tr_torrent_t * );
int tr_peerRead ( tr_peer_t * );
uint64_t tr_peerDate ( tr_peer_t * );
uint8_t * tr_peerId ( tr_peer_t * );
uint8_t * tr_peerHash ( tr_peer_t * );
int tr_peerPulse ( tr_peer_t * );
int tr_peerIsConnected ( tr_peer_t * );
int tr_peerIsFrom ( tr_peer_t * );
int tr_peerAmChoking ( tr_peer_t * );
int tr_peerAmInterested ( tr_peer_t * );
int tr_peerIsChoking ( tr_peer_t * );
int tr_peerIsInterested ( tr_peer_t * );
float tr_peerProgress ( tr_peer_t * );
int tr_peerPort ( tr_peer_t * );
tr_bitfield_t * tr_peerBitfield ( tr_peer_t * );
float tr_peerDownloadRate ( tr_peer_t * );
float tr_peerUploadRate ( tr_peer_t * );
void tr_peerChoke ( tr_peer_t * );
void tr_peerUnchoke ( tr_peer_t * );
uint64_t tr_peerLastChoke ( tr_peer_t * );
void tr_peerSetOptimistic ( tr_peer_t *, int );
int tr_peerIsOptimistic ( tr_peer_t * );
void tr_peerBlame ( tr_peer_t *, int piece, int success );
struct in_addr * tr_peerAddress ( tr_peer_t * );
int tr_peerGetConnectable( const tr_torrent_t *, uint8_t ** );
tr_peer_t * tr_peerInit ( struct in_addr, in_port_t, int sock, int );
void tr_peerDestroy ( tr_peer_t * );
const char *tr_peerClient ( tr_peer_t * );
void tr_peerSetPrivate ( tr_peer_t *, int );
void tr_peerSetTorrent ( tr_peer_t *, tr_torrent_t * );
void tr_peerSentBlockToUs ( tr_peer_t *, int byteCount );
void tr_peerGotBlockFromUs ( tr_peer_t *, int byteCount );
int tr_peerRead ( tr_peer_t * );
uint64_t tr_peerDate ( const tr_peer_t * );
const uint8_t * tr_peerHash ( const tr_peer_t * );
int tr_peerPulse ( tr_peer_t * );
int tr_peerIsConnected ( const tr_peer_t * );
int tr_peerIsFrom ( const tr_peer_t * );
int tr_peerAmChoking ( const tr_peer_t * );
int tr_peerAmInterested ( const tr_peer_t * );
int tr_peerIsChoking ( const tr_peer_t * );
int tr_peerIsInterested ( const tr_peer_t * );
float tr_peerProgress ( const tr_peer_t * );
int tr_peerPort ( const tr_peer_t * );
int tr_peerHasPiece ( const tr_peer_t *, int pieceIndex );
float tr_peerDownloadRate ( const tr_peer_t * );
float tr_peerUploadRate ( const tr_peer_t * );
void tr_peerChoke ( tr_peer_t * );
void tr_peerUnchoke ( tr_peer_t * );
uint64_t tr_peerLastChoke ( const tr_peer_t * );
void tr_peerSetOptimistic ( tr_peer_t *, int );
int tr_peerIsOptimistic ( const tr_peer_t * );
void tr_peerBlame ( tr_peer_t *, int piece, int success );
struct in_addr * tr_peerAddress ( tr_peer_t * );
int tr_peerGetConnectable ( const tr_torrent_t *, uint8_t ** );
void tr_swiftPulse ( tr_handle_t * );
#endif

View file

@ -344,6 +344,7 @@ static inline int parsePiece( tr_torrent_t * tor, tr_peer_t * peer,
return ret;
}
tr_cpBlockAdd( tor->completion, block );
tr_peerSentBlockToUs( peer, len-8 );
broadcastCancel( tor, index, begin, len - 8 );
if( !tr_cpPieceHasAllBlocks( tor->completion, index ) )
@ -585,7 +586,7 @@ static inline int parseBufHeader( tr_peer_t * peer )
return TR_OK;
}
static uint8_t * parseBufHash( tr_peer_t * peer )
static const uint8_t * parseBufHash( const tr_peer_t * peer )
{
if( 48 > peer->pos )
{

View file

@ -293,6 +293,8 @@ static void SharedLoop( void * _s )
lastchoke = date1;
}
tr_swiftPulse ( s->h );
/* Wait up to 20 ms */
date2 = tr_date();
if( date2 < date1 + 20 )
@ -382,14 +384,12 @@ static void ReadPeers( tr_shared_t * s )
static void DispatchPeers( tr_shared_t * s )
{
tr_handle_t * h = s->h;
tr_torrent_t * tor;
uint8_t * hash;
int ii;
uint64_t now = tr_date();
const uint64_t now = tr_date();
for( ii = 0; ii < s->peerCount; )
{
hash = tr_peerHash( s->peers[ii] );
const uint8_t * hash = tr_peerHash( s->peers[ii] );
if( !hash && now > tr_peerDate( s->peers[ii] ) + 10000 )
{
@ -399,6 +399,8 @@ static void DispatchPeers( tr_shared_t * s )
}
if( hash )
{
tr_torrent_t * tor;
for( tor = h->torrentList; tor; tor = tor->next )
{
tr_lockLock( &tor->lock );

View file

@ -606,8 +606,7 @@ void tr_torrentAvailability( tr_torrent_t * tor, int8_t * tab, int size )
tab[i] = 0;
for( j = 0; j < tor->peerCount; j++ )
{
if( tr_peerBitfield( tor->peers[j] ) &&
tr_bitfieldHas( tr_peerBitfield( tor->peers[j] ), piece ) )
if( tr_peerHasPiece( tor->peers[j], piece ) )
{
(tab[i])++;
}