2008-11-24 04:21:23 +00:00
|
|
|
/*
|
2008-12-16 00:20:44 +00:00
|
|
|
* This file Copyright (C) 2008 Charles Kerr <charles@transmissionbt.com>
|
2008-11-24 04:21:23 +00:00
|
|
|
*
|
|
|
|
* This file is licensed by the GPL version 2. Works owned by the
|
|
|
|
* Transmission project are granted a special exemption to clause 2(b)
|
|
|
|
* so that the bulk of its code can remain under the MIT license.
|
|
|
|
* This exemption does not extend to derived works not owned by
|
|
|
|
* the Transmission project.
|
|
|
|
*
|
2008-12-24 17:45:27 +00:00
|
|
|
* $Id$
|
2008-11-24 04:21:23 +00:00
|
|
|
*/
|
|
|
|
|
2008-11-25 21:35:17 +00:00
|
|
|
#include <assert.h>
|
2008-11-24 04:21:23 +00:00
|
|
|
#include <limits.h>
|
2008-11-25 21:35:17 +00:00
|
|
|
|
|
|
|
#include "event.h"
|
|
|
|
|
2008-11-24 04:21:23 +00:00
|
|
|
#include "transmission.h"
|
|
|
|
#include "bandwidth.h"
|
2008-12-09 22:05:45 +00:00
|
|
|
#include "crypto.h"
|
2008-12-16 22:08:17 +00:00
|
|
|
#include "peer-io.h"
|
2008-11-25 21:35:17 +00:00
|
|
|
#include "ptrarray.h"
|
2008-11-24 04:21:23 +00:00
|
|
|
#include "utils.h"
|
|
|
|
|
2009-01-02 21:50:51 +00:00
|
|
|
#define dbgmsg( ... ) \
|
|
|
|
do { \
|
|
|
|
if( tr_deepLoggingIsActive( ) ) \
|
|
|
|
tr_deepLog( __FILE__, __LINE__, NULL, __VA_ARGS__ ); \
|
|
|
|
} while( 0 )
|
|
|
|
|
2008-11-24 04:21:23 +00:00
|
|
|
/***
|
|
|
|
****
|
|
|
|
***/
|
|
|
|
|
|
|
|
static float
|
2008-11-26 18:00:37 +00:00
|
|
|
getSpeed( const struct bratecontrol * r, int interval_msec )
|
2008-11-24 04:21:23 +00:00
|
|
|
{
|
|
|
|
uint64_t bytes = 0;
|
|
|
|
const uint64_t cutoff = tr_date ( ) - interval_msec;
|
|
|
|
int i = r->newest;
|
|
|
|
|
|
|
|
for( ;; )
|
|
|
|
{
|
|
|
|
if( r->transfers[i].date <= cutoff )
|
|
|
|
break;
|
|
|
|
|
|
|
|
bytes += r->transfers[i].size;
|
|
|
|
|
|
|
|
if( --i == -1 ) i = HISTORY_SIZE - 1; /* circular history */
|
|
|
|
if( i == r->newest ) break; /* we've come all the way around */
|
|
|
|
}
|
|
|
|
|
|
|
|
return ( bytes / 1024.0 ) * ( 1000.0 / interval_msec );
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2009-01-02 23:48:51 +00:00
|
|
|
bytesUsed( const uint64_t now, struct bratecontrol * r, size_t size )
|
2008-11-24 04:21:23 +00:00
|
|
|
{
|
|
|
|
if( r->transfers[r->newest].date + GRANULARITY_MSEC >= now )
|
|
|
|
r->transfers[r->newest].size += size;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if( ++r->newest == HISTORY_SIZE ) r->newest = 0;
|
|
|
|
r->transfers[r->newest].date = now;
|
|
|
|
r->transfers[r->newest].size = size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/******
|
|
|
|
*******
|
|
|
|
*******
|
|
|
|
******/
|
|
|
|
|
2009-01-02 06:28:22 +00:00
|
|
|
static inline int
|
2008-11-25 21:35:17 +00:00
|
|
|
comparePointers( const void * a, const void * b )
|
|
|
|
{
|
2008-12-22 05:19:52 +00:00
|
|
|
if( a != b )
|
|
|
|
return a < b ? -1 : 1;
|
|
|
|
|
|
|
|
return 0;
|
2008-11-25 21:35:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/***
|
|
|
|
****
|
|
|
|
***/
|
|
|
|
|
2008-11-24 04:21:23 +00:00
|
|
|
tr_bandwidth*
|
2009-01-02 19:56:06 +00:00
|
|
|
tr_bandwidthConstruct( tr_bandwidth * b, tr_session * session, tr_bandwidth * parent )
|
2008-11-24 04:21:23 +00:00
|
|
|
{
|
|
|
|
b->session = session;
|
2008-12-29 08:54:36 +00:00
|
|
|
b->children = TR_PTR_ARRAY_INIT;
|
2008-11-25 21:35:17 +00:00
|
|
|
b->magicNumber = MAGIC_NUMBER;
|
2008-12-21 18:15:00 +00:00
|
|
|
b->band[TR_UP].honorParentLimits = TRUE;
|
|
|
|
b->band[TR_DOWN].honorParentLimits = TRUE;
|
2008-11-25 21:35:17 +00:00
|
|
|
tr_bandwidthSetParent( b, parent );
|
2008-11-24 04:21:23 +00:00
|
|
|
return b;
|
|
|
|
}
|
|
|
|
|
2009-01-02 19:56:06 +00:00
|
|
|
tr_bandwidth*
|
|
|
|
tr_bandwidthDestruct( tr_bandwidth * b )
|
2008-11-24 04:21:23 +00:00
|
|
|
{
|
2008-12-21 18:15:00 +00:00
|
|
|
assert( tr_isBandwidth( b ) );
|
2008-11-25 21:35:17 +00:00
|
|
|
|
|
|
|
tr_bandwidthSetParent( b, NULL );
|
2008-12-29 08:54:36 +00:00
|
|
|
tr_ptrArrayDestruct( &b->children, NULL );
|
2008-11-25 21:35:17 +00:00
|
|
|
b->magicNumber = 0xDEAD;
|
2009-01-02 19:56:06 +00:00
|
|
|
|
|
|
|
return b;
|
2008-11-24 04:21:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/***
|
|
|
|
****
|
|
|
|
***/
|
|
|
|
|
2008-11-25 21:35:17 +00:00
|
|
|
void
|
|
|
|
tr_bandwidthSetParent( tr_bandwidth * b,
|
|
|
|
tr_bandwidth * parent )
|
|
|
|
{
|
2008-12-21 18:15:00 +00:00
|
|
|
assert( tr_isBandwidth( b ) );
|
2008-11-25 21:35:17 +00:00
|
|
|
assert( b != parent );
|
|
|
|
|
|
|
|
if( b->parent )
|
|
|
|
{
|
2008-12-21 18:15:00 +00:00
|
|
|
assert( tr_isBandwidth( b->parent ) );
|
2008-11-25 21:35:17 +00:00
|
|
|
|
2008-12-29 08:54:36 +00:00
|
|
|
tr_ptrArrayRemoveSorted( &b->parent->children, b, comparePointers );
|
2008-12-21 18:15:00 +00:00
|
|
|
b->parent = NULL;
|
2008-11-25 21:35:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if( parent )
|
|
|
|
{
|
2008-12-21 18:15:00 +00:00
|
|
|
assert( tr_isBandwidth( parent ) );
|
2008-11-25 21:35:17 +00:00
|
|
|
assert( parent->parent != b );
|
|
|
|
|
2008-12-29 08:54:36 +00:00
|
|
|
tr_ptrArrayInsertSorted( &parent->children, b, comparePointers );
|
2008-11-25 21:35:17 +00:00
|
|
|
b->parent = parent;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/***
|
|
|
|
****
|
|
|
|
***/
|
|
|
|
|
2008-11-26 18:54:35 +00:00
|
|
|
#if 0
|
|
|
|
#warning do not check the code in with this enabled
|
2008-11-26 15:58:26 +00:00
|
|
|
#define DEBUG_DIRECTION TR_UP
|
2008-11-25 21:35:17 +00:00
|
|
|
#endif
|
|
|
|
|
2008-12-09 22:05:45 +00:00
|
|
|
static void
|
|
|
|
allocateBandwidth( tr_bandwidth * b,
|
|
|
|
tr_direction dir,
|
|
|
|
int period_msec,
|
2008-12-16 22:08:17 +00:00
|
|
|
tr_ptrArray * peer_pool )
|
2008-11-24 04:21:23 +00:00
|
|
|
{
|
2008-12-21 18:15:00 +00:00
|
|
|
assert( tr_isBandwidth( b ) );
|
|
|
|
assert( tr_isDirection( dir ) );
|
2008-11-26 15:58:26 +00:00
|
|
|
|
2008-12-21 18:15:00 +00:00
|
|
|
/* set the available bandwidth */
|
2008-11-26 15:58:26 +00:00
|
|
|
if( b->band[dir].isLimited )
|
|
|
|
{
|
2008-11-27 18:56:58 +00:00
|
|
|
const double desiredSpeed = b->band[dir].desiredSpeed;
|
2008-12-04 03:31:14 +00:00
|
|
|
const double nextPulseSpeed = desiredSpeed;
|
2008-11-28 21:35:01 +00:00
|
|
|
b->band[dir].bytesLeft = MAX( 0.0, nextPulseSpeed * 1024.0 * period_msec / 1000.0 );
|
2008-11-25 21:35:17 +00:00
|
|
|
|
|
|
|
#ifdef DEBUG_DIRECTION
|
2008-11-26 15:58:26 +00:00
|
|
|
if( dir == DEBUG_DIRECTION )
|
2008-11-26 18:00:37 +00:00
|
|
|
fprintf( stderr, "bandwidth %p currentPieceSpeed(%5.2f of %5.2f) desiredSpeed(%5.2f), allocating %5.2f\n",
|
2008-11-26 15:58:26 +00:00
|
|
|
b, currentSpeed, tr_bandwidthGetRawSpeed( b, dir ), desiredSpeed,
|
2008-11-26 18:00:37 +00:00
|
|
|
b->band[dir].bytesLeft/1024.0 );
|
2008-11-25 21:35:17 +00:00
|
|
|
#endif
|
2008-11-26 15:58:26 +00:00
|
|
|
}
|
2008-11-25 21:35:17 +00:00
|
|
|
|
2009-01-02 19:56:06 +00:00
|
|
|
/* add this bandwidth's peer, if any, to the peer pool */
|
|
|
|
if( b->peer != NULL )
|
|
|
|
tr_ptrArrayAppend( peer_pool, b->peer );
|
2008-12-09 22:05:45 +00:00
|
|
|
|
2008-11-25 21:35:17 +00:00
|
|
|
#ifdef DEBUG_DIRECTION
|
2008-11-26 15:58:26 +00:00
|
|
|
if( ( dir == DEBUG_DIRECTION ) && ( n > 1 ) )
|
2008-12-16 22:08:17 +00:00
|
|
|
fprintf( stderr, "bandwidth %p has %d peers\n", b, n );
|
2008-11-25 21:35:17 +00:00
|
|
|
#endif
|
|
|
|
|
2009-01-02 19:56:06 +00:00
|
|
|
/* traverse & repeat for the subtree */
|
2008-11-25 21:35:17 +00:00
|
|
|
if( 1 ) {
|
2008-12-29 08:54:36 +00:00
|
|
|
int i;
|
|
|
|
struct tr_bandwidth ** children = (struct tr_bandwidth**) TR_PTR_ARRAY_DATA( &b->children );
|
|
|
|
const int n = TR_PTR_ARRAY_LENGTH( &b->children );
|
2008-11-25 21:35:17 +00:00
|
|
|
for( i=0; i<n; ++i )
|
2008-12-16 22:08:17 +00:00
|
|
|
allocateBandwidth( children[i], dir, period_msec, peer_pool );
|
2008-11-25 21:35:17 +00:00
|
|
|
}
|
2008-11-24 04:21:23 +00:00
|
|
|
}
|
|
|
|
|
2008-12-09 22:05:45 +00:00
|
|
|
void
|
|
|
|
tr_bandwidthAllocate( tr_bandwidth * b,
|
|
|
|
tr_direction dir,
|
|
|
|
int period_msec )
|
|
|
|
{
|
2008-12-20 22:19:34 +00:00
|
|
|
int i, n, peerCount;
|
2008-12-29 08:54:36 +00:00
|
|
|
tr_ptrArray tmp = TR_PTR_ARRAY_INIT;
|
2008-12-16 22:08:17 +00:00
|
|
|
struct tr_peerIo ** peers;
|
2008-12-24 17:45:27 +00:00
|
|
|
|
2008-12-20 22:19:34 +00:00
|
|
|
/* allocateBandwidth() is a helper function with two purposes:
|
|
|
|
* 1. allocate bandwidth to b and its subtree
|
|
|
|
* 2. accumulate an array of all the peerIos from b and its subtree. */
|
2008-12-29 08:54:36 +00:00
|
|
|
allocateBandwidth( b, dir, period_msec, &tmp );
|
2009-01-02 19:56:06 +00:00
|
|
|
peers = (struct tr_peerIo**) TR_PTR_ARRAY_DATA( &tmp );
|
|
|
|
peerCount = TR_PTR_ARRAY_LENGTH( &tmp );
|
2008-12-20 22:19:34 +00:00
|
|
|
|
|
|
|
/* Stop all peers from listening for the socket to be ready for IO.
|
|
|
|
* See "Second phase of IO" lower in this function for more info. */
|
|
|
|
for( i=0; i<peerCount; ++i )
|
|
|
|
tr_peerIoSetEnabled( peers[i], dir, FALSE );
|
|
|
|
|
2008-12-21 18:15:00 +00:00
|
|
|
/* First phase of IO. Tries to distribute bandwidth fairly to keep faster
|
|
|
|
* peers from starving the others. Loop through the peers, giving each a
|
2008-12-31 21:15:22 +00:00
|
|
|
* small chunk of bandwidth. Keep looping until we run out of bandwidth
|
|
|
|
* and/or peers that can use it */
|
2008-12-20 22:19:34 +00:00
|
|
|
n = peerCount;
|
2009-01-02 22:06:11 +00:00
|
|
|
dbgmsg( "%d peers to go round-robin for %s", n, (dir==TR_UP?"upload":"download") );
|
2008-12-20 22:19:34 +00:00
|
|
|
i = n ? tr_cryptoWeakRandInt( n ) : 0; /* pick a random starting point */
|
2009-01-02 04:46:38 +00:00
|
|
|
while( n > 1 )
|
2008-12-15 21:22:08 +00:00
|
|
|
{
|
2009-01-03 04:57:40 +00:00
|
|
|
const size_t increment = 1024;
|
|
|
|
const ssize_t bytesUsed = tr_peerIoFlush( peers[i], dir, increment );
|
2008-12-20 22:19:34 +00:00
|
|
|
|
2009-01-03 04:57:40 +00:00
|
|
|
dbgmsg( "peer #%d of %d used %zd bytes in this pass", i, n, bytesUsed );
|
2009-01-03 02:43:17 +00:00
|
|
|
|
2009-01-03 04:57:40 +00:00
|
|
|
if( bytesUsed == (ssize_t)increment )
|
2008-12-20 22:19:34 +00:00
|
|
|
++i;
|
|
|
|
else {
|
|
|
|
/* peer is done writing for now; move it to the end of the list */
|
2009-01-01 18:38:49 +00:00
|
|
|
tr_peerIo * pio = peers[i];
|
2008-12-20 22:19:34 +00:00
|
|
|
peers[i] = peers[n-1];
|
2009-01-01 18:38:49 +00:00
|
|
|
peers[n-1] = pio;
|
2008-12-20 22:19:34 +00:00
|
|
|
--n;
|
2008-12-15 21:22:08 +00:00
|
|
|
}
|
2008-12-20 22:19:34 +00:00
|
|
|
|
|
|
|
if( i == n )
|
|
|
|
i = 0;
|
2008-12-09 22:05:45 +00:00
|
|
|
}
|
|
|
|
|
2008-12-21 18:15:00 +00:00
|
|
|
/* Second phase of IO. To help us scale in high bandwidth situations,
|
|
|
|
* enable on-demand IO for peers with bandwidth left to burn.
|
|
|
|
* This on-demand IO is enabled until (1) the peer runs out of bandwidth,
|
|
|
|
* or (2) the next tr_bandwidthAllocate() call, when we start over again. */
|
2008-12-20 22:19:34 +00:00
|
|
|
for( i=0; i<peerCount; ++i )
|
|
|
|
if( tr_peerIoHasBandwidthLeft( peers[i], dir ) )
|
|
|
|
tr_peerIoSetEnabled( peers[i], dir, TRUE );
|
|
|
|
|
2008-12-15 21:22:08 +00:00
|
|
|
/* cleanup */
|
2008-12-29 08:54:36 +00:00
|
|
|
tr_ptrArrayDestruct( &tmp, NULL );
|
2008-12-09 22:05:45 +00:00
|
|
|
}
|
|
|
|
|
2008-11-25 21:35:17 +00:00
|
|
|
void
|
2009-01-02 19:56:06 +00:00
|
|
|
tr_bandwidthSetPeer( tr_bandwidth * b, tr_peerIo * peer )
|
2008-11-25 21:35:17 +00:00
|
|
|
{
|
2008-12-21 18:15:00 +00:00
|
|
|
assert( tr_isBandwidth( b ) );
|
2009-01-02 19:56:06 +00:00
|
|
|
assert( ( peer == NULL ) || tr_isPeerIo( peer ) );
|
2008-11-25 21:35:17 +00:00
|
|
|
|
2009-01-02 19:56:06 +00:00
|
|
|
b->peer = peer;
|
2008-11-24 04:21:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/***
|
|
|
|
****
|
|
|
|
***/
|
|
|
|
|
2008-11-25 21:35:17 +00:00
|
|
|
size_t
|
|
|
|
tr_bandwidthClamp( const tr_bandwidth * b,
|
|
|
|
tr_direction dir,
|
|
|
|
size_t byteCount )
|
|
|
|
{
|
2008-12-21 18:15:00 +00:00
|
|
|
assert( tr_isBandwidth( b ) );
|
|
|
|
assert( tr_isDirection( dir ) );
|
2008-11-25 21:35:17 +00:00
|
|
|
|
|
|
|
if( b )
|
|
|
|
{
|
|
|
|
if( b->band[dir].isLimited )
|
|
|
|
byteCount = MIN( byteCount, b->band[dir].bytesLeft );
|
|
|
|
|
|
|
|
if( b->parent && b->band[dir].honorParentLimits )
|
|
|
|
byteCount = tr_bandwidthClamp( b->parent, dir, byteCount );
|
|
|
|
}
|
|
|
|
|
|
|
|
return byteCount;
|
|
|
|
}
|
|
|
|
|
2008-11-24 04:21:23 +00:00
|
|
|
double
|
2008-11-25 21:35:17 +00:00
|
|
|
tr_bandwidthGetRawSpeed( const tr_bandwidth * b, tr_direction dir )
|
2008-11-24 04:21:23 +00:00
|
|
|
{
|
2008-12-21 18:15:00 +00:00
|
|
|
assert( tr_isBandwidth( b ) );
|
|
|
|
assert( tr_isDirection( dir ) );
|
2008-11-25 21:35:17 +00:00
|
|
|
|
2008-11-26 18:00:37 +00:00
|
|
|
return getSpeed( &b->band[dir].raw, HISTORY_MSEC );
|
2008-11-24 04:21:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
double
|
2008-11-25 21:35:17 +00:00
|
|
|
tr_bandwidthGetPieceSpeed( const tr_bandwidth * b, tr_direction dir )
|
2008-11-24 04:21:23 +00:00
|
|
|
{
|
2008-12-21 18:15:00 +00:00
|
|
|
assert( tr_isBandwidth( b ) );
|
|
|
|
assert( tr_isDirection( dir ) );
|
2008-11-25 21:35:17 +00:00
|
|
|
|
2008-11-26 18:00:37 +00:00
|
|
|
return getSpeed( &b->band[dir].piece, HISTORY_MSEC );
|
2008-11-24 04:21:23 +00:00
|
|
|
}
|
|
|
|
|
2009-01-02 23:48:51 +00:00
|
|
|
static void
|
|
|
|
bandwidthUsedImpl( tr_bandwidth * b,
|
|
|
|
tr_direction dir,
|
|
|
|
size_t byteCount,
|
|
|
|
tr_bool isPieceData,
|
|
|
|
uint64_t now )
|
2008-11-24 04:21:23 +00:00
|
|
|
{
|
2008-11-25 21:35:17 +00:00
|
|
|
struct tr_band * band;
|
2008-11-26 15:58:26 +00:00
|
|
|
size_t oldBytesLeft;
|
2008-11-25 21:35:17 +00:00
|
|
|
|
2008-12-21 18:15:00 +00:00
|
|
|
assert( tr_isBandwidth( b ) );
|
|
|
|
assert( tr_isDirection( dir ) );
|
2008-11-25 21:35:17 +00:00
|
|
|
|
|
|
|
band = &b->band[dir];
|
2008-11-24 04:21:23 +00:00
|
|
|
|
2008-11-26 15:58:26 +00:00
|
|
|
oldBytesLeft = band->bytesLeft;
|
|
|
|
|
2008-11-25 21:35:17 +00:00
|
|
|
if( band->isLimited && isPieceData )
|
|
|
|
band->bytesLeft -= MIN( band->bytesLeft, byteCount );
|
|
|
|
|
|
|
|
#ifdef DEBUG_DIRECTION
|
2008-11-26 15:58:26 +00:00
|
|
|
if( ( dir == DEBUG_DIRECTION ) && ( band->isLimited ) )
|
|
|
|
fprintf( stderr, "%p consumed %5zu bytes of %5s data... was %6zu, now %6zu left\n",
|
|
|
|
b, byteCount, (isPieceData?"piece":"raw"), oldBytesLeft, band->bytesLeft );
|
2008-11-25 21:35:17 +00:00
|
|
|
#endif
|
|
|
|
|
2009-01-02 23:48:51 +00:00
|
|
|
bytesUsed( now, &band->raw, byteCount );
|
2008-11-24 04:21:23 +00:00
|
|
|
|
|
|
|
if( isPieceData )
|
2009-01-02 23:48:51 +00:00
|
|
|
bytesUsed( now, &band->piece, byteCount );
|
2008-11-25 21:35:17 +00:00
|
|
|
|
|
|
|
if( b->parent != NULL )
|
2009-01-02 23:48:51 +00:00
|
|
|
bandwidthUsedImpl( b->parent, dir, byteCount, isPieceData, now );
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
tr_bandwidthUsed( tr_bandwidth * b,
|
|
|
|
tr_direction dir,
|
|
|
|
size_t byteCount,
|
|
|
|
tr_bool isPieceData )
|
|
|
|
{
|
|
|
|
bandwidthUsedImpl( b, dir, byteCount, isPieceData, tr_date( ) );
|
2008-11-24 04:21:23 +00:00
|
|
|
}
|