source: trunk/libtransmission/fdlimit.c @ 12363

Last change on this file since 12363 was 12363, checked in by jordan, 11 years ago

(trunk libT) #4165 "FD_SETSIZE impact on open-file-limit and peer-limit-global" -- add safety guard in the file cache to prevent too many open files.

  • Property svn:keywords set to Date Rev Author Id
File size: 19.2 KB
Line 
1/*
2 * This file Copyright (C) Mnemosyne LLC
3 *
4 * This file is licensed by the GPL version 2. Works owned by the
5 * Transmission project are granted a special exemption to clause 2(b)
6 * so that the bulk of its code can remain under the MIT license.
7 * This exemption does not extend to derived works not owned by
8 * the Transmission project.
9 *
10 * $Id: fdlimit.c 12363 2011-04-16 22:33:29Z jordan $
11 */
12
13#ifndef WIN32
14 #define HAVE_GETRLIMIT
15#endif
16
17#ifdef HAVE_POSIX_FADVISE
18 #ifdef _XOPEN_SOURCE
19  #undef _XOPEN_SOURCE
20 #endif
21 #define _XOPEN_SOURCE 600
22#endif
23
24#include <assert.h>
25#include <errno.h>
26#include <inttypes.h>
27#include <string.h>
28#ifdef SYS_DARWIN
29 #include <fcntl.h>
30#endif
31
32#ifdef HAVE_FALLOCATE64
33  /* FIXME can't find the right #include voodoo to pick up the declaration.. */
34  extern int fallocate64( int fd, int mode, uint64_t offset, uint64_t len );
35#endif
36
37#ifdef HAVE_XFS_XFS_H
38 #include <xfs/xfs.h>
39#endif
40
41#include <sys/types.h>
42#include <sys/stat.h>
43#ifdef HAVE_GETRLIMIT
44 #include <sys/time.h> /* getrlimit */
45 #include <sys/resource.h> /* getrlimit */
46#endif
47#include <fcntl.h> /* O_LARGEFILE posix_fadvise */
48#include <unistd.h> /* lseek(), write(), ftruncate(), pread(), pwrite(), etc */
49
50#include "transmission.h"
51#include "fdlimit.h"
52#include "net.h"
53#include "session.h"
54#include "torrent.h" /* tr_isTorrent() */
55
56#define dbgmsg( ... ) \
57    do { \
58        if( tr_deepLoggingIsActive( ) ) \
59            tr_deepLog( __FILE__, __LINE__, NULL, __VA_ARGS__ ); \
60    } while( 0 )
61
62/***
63****
64****  Local Files
65****
66***/
67
68#ifndef O_LARGEFILE
69 #define O_LARGEFILE 0
70#endif
71
72#ifndef O_BINARY
73 #define O_BINARY 0
74#endif
75
76#ifndef O_SEQUENTIAL
77 #define O_SEQUENTIAL 0
78#endif
79
80
81static bool
82preallocate_file_sparse( int fd, uint64_t length )
83{
84    const char zero = '\0';
85    bool success = 0;
86
87    if( !length )
88        success = true;
89
90#ifdef HAVE_FALLOCATE64
91    if( !success ) /* fallocate64 is always preferred, so try it first */
92        success = !fallocate64( fd, 0, 0, length );
93#endif
94
95    if( !success ) /* fallback: the old-style seek-and-write */
96        success = ( lseek( fd, length-1, SEEK_SET ) != -1 )
97               && ( write( fd, &zero, 1 ) != -1 )
98               && ( ftruncate( fd, length ) != -1 );
99
100    return success;
101}
102
103static bool
104preallocate_file_full( const char * filename, uint64_t length )
105{
106    bool success = 0;
107
108#ifdef WIN32
109
110    HANDLE hFile = CreateFile( filename, GENERIC_WRITE, 0, 0, CREATE_NEW, FILE_FLAG_RANDOM_ACCESS, 0 );
111    if( hFile != INVALID_HANDLE_VALUE )
112    {
113        LARGE_INTEGER li;
114        li.QuadPart = length;
115        success = SetFilePointerEx( hFile, li, NULL, FILE_BEGIN ) && SetEndOfFile( hFile );
116        CloseHandle( hFile );
117    }
118
119#else
120
121    int flags = O_RDWR | O_CREAT | O_LARGEFILE;
122    int fd = open( filename, flags, 0666 );
123    if( fd >= 0 )
124    {
125# ifdef HAVE_FALLOCATE64
126       if( !success )
127       {
128           success = !fallocate64( fd, 0, 0, length );
129       }
130# endif
131# ifdef HAVE_XFS_XFS_H
132        if( !success && platform_test_xfs_fd( fd ) )
133        {
134            xfs_flock64_t fl;
135            fl.l_whence = 0;
136            fl.l_start = 0;
137            fl.l_len = length;
138            success = !xfsctl( NULL, fd, XFS_IOC_RESVSP64, &fl );
139        }
140# endif
141# ifdef SYS_DARWIN
142        if( !success )
143        {
144            fstore_t fst;
145            fst.fst_flags = F_ALLOCATECONTIG;
146            fst.fst_posmode = F_PEOFPOSMODE;
147            fst.fst_offset = 0;
148            fst.fst_length = length;
149            fst.fst_bytesalloc = 0;
150            success = !fcntl( fd, F_PREALLOCATE, &fst );
151        }
152# endif
153# ifdef HAVE_POSIX_FALLOCATE
154        if( !success )
155        {
156            success = !posix_fallocate( fd, 0, length );
157        }
158# endif
159
160        if( !success ) /* if nothing else works, do it the old-fashioned way */
161        {
162            uint8_t buf[ 4096 ];
163            memset( buf, 0, sizeof( buf ) );
164            success = true;
165            while ( success && ( length > 0 ) )
166            {
167                const int thisPass = MIN( length, sizeof( buf ) );
168                success = write( fd, buf, thisPass ) == thisPass;
169                length -= thisPass;
170            }
171        }
172
173        close( fd );
174    }
175
176#endif
177
178    return success;
179}
180
181
182/* portability wrapper for fsync(). */
183int
184tr_fsync( int fd )
185{
186#ifdef WIN32
187    return _commit( fd );
188#else
189    return fsync( fd );
190#endif
191}
192
193
194/* Like pread and pwrite, except that the position is undefined afterwards.
195   And of course they are not thread-safe. */
196
197/* don't use pread/pwrite on old versions of uClibc because they're buggy.
198 * https://trac.transmissionbt.com/ticket/3826 */
199#ifdef __UCLIBC__
200#define TR_UCLIBC_CHECK_VERSION(major,minor,micro) \
201    (__UCLIBC_MAJOR__ > (major) || \
202     (__UCLIBC_MAJOR__ == (major) && __UCLIBC_MINOR__ > (minor)) || \
203     (__UCLIBC_MAJOR__ == (major) && __UCLIBC_MINOR__ == (minor) && \
204      __UCLIBC_SUBLEVEL__ >= (micro)))
205#if !TR_UCLIBC_CHECK_VERSION(0,9,28)
206 #undef HAVE_PREAD
207 #undef HAVE_PWRITE
208#endif
209#endif
210
211#ifdef SYS_DARWIN
212 #define HAVE_PREAD
213 #define HAVE_PWRITE
214#endif
215
216ssize_t
217tr_pread( int fd, void *buf, size_t count, off_t offset )
218{
219#ifdef HAVE_PREAD
220    return pread( fd, buf, count, offset );
221#else
222    const off_t lrc = lseek( fd, offset, SEEK_SET );
223    if( lrc < 0 )
224        return -1;
225    return read( fd, buf, count );
226#endif
227}
228
229ssize_t
230tr_pwrite( int fd, const void *buf, size_t count, off_t offset )
231{
232#ifdef HAVE_PWRITE
233    return pwrite( fd, buf, count, offset );
234#else
235    const off_t lrc = lseek( fd, offset, SEEK_SET );
236    if( lrc < 0 )
237        return -1;
238    return write( fd, buf, count );
239#endif
240}
241
242int
243tr_prefetch( int fd UNUSED, off_t offset UNUSED, size_t count UNUSED )
244{
245#ifdef HAVE_POSIX_FADVISE
246    return posix_fadvise( fd, offset, count, POSIX_FADV_WILLNEED );
247#elif defined(SYS_DARWIN)
248    struct radvisory radv;
249    radv.ra_offset = offset;
250    radv.ra_count = count;
251    return fcntl( fd, F_RDADVISE, &radv );
252#else
253    return 0;
254#endif
255}
256
257void
258tr_set_file_for_single_pass( int fd )
259{
260    if( fd >= 0 )
261    {
262        /* Set hints about the lookahead buffer and caching. It's okay
263           for these to fail silently, so don't let them affect errno */
264        const int err = errno;
265#ifdef HAVE_POSIX_FADVISE
266        posix_fadvise( fd, 0, 0, POSIX_FADV_SEQUENTIAL );
267#endif
268#ifdef SYS_DARWIN
269        fcntl( fd, F_RDAHEAD, 1 );
270        fcntl( fd, F_NOCACHE, 1 );
271#endif
272        errno = err;
273    }
274}
275
276static int
277open_local_file( const char * filename, int flags )
278{
279    const int fd = open( filename, flags, 0666 );
280    tr_set_file_for_single_pass( fd );
281    return fd;
282}
283int
284tr_open_file_for_writing( const char * filename )
285{
286    return open_local_file( filename, O_LARGEFILE|O_BINARY|O_CREAT|O_WRONLY );
287}
288int
289tr_open_file_for_scanning( const char * filename )
290{
291    return open_local_file( filename, O_LARGEFILE|O_BINARY|O_SEQUENTIAL|O_RDONLY );
292}
293
294void
295tr_close_file( int fd )
296{
297#if defined(HAVE_POSIX_FADVISE)
298    /* Set hint about not caching this file.
299       It's okay for this to fail silently, so don't let it affect errno */
300    const int err = errno;
301    posix_fadvise( fd, 0, 0, POSIX_FADV_DONTNEED );
302    errno = err;
303#endif
304#ifdef SYS_DARWIN
305    /* it's unclear to me from the man pages if this actually flushes out the cache,
306     * but it couldn't hurt... */
307    fcntl( fd, F_NOCACHE, 1 );
308#endif
309    close( fd );
310}
311
312/*****
313******
314******
315******
316*****/
317
318struct tr_cached_file
319{
320    bool             is_writable;
321    int              fd;
322    int              torrent_id;
323    tr_file_index_t  file_index;
324    time_t           used_at;
325};
326
327static inline bool
328cached_file_is_open( const struct tr_cached_file * o )
329{
330    assert( o != NULL );
331
332    return o->fd >= 0;
333}
334
335static void
336cached_file_close( struct tr_cached_file * o )
337{
338    assert( cached_file_is_open( o ) );
339
340    tr_close_file( o->fd );
341    o->fd = -1;
342}
343
344/**
345 * returns 0 on success, or an errno value on failure.
346 * errno values include ENOENT if the parent folder doesn't exist,
347 * plus the errno values set by tr_mkdirp() and open().
348 */
349static int
350cached_file_open( struct tr_cached_file  * o,
351                  const char             * existing_dir,
352                  const char             * filename,
353                  bool                     writable,
354                  tr_preallocation_mode    allocation,
355                  uint64_t                 file_size )
356{
357    int flags;
358    struct stat sb;
359    bool alreadyExisted;
360
361    /* confirm that existing_dir, if specified, exists on the disk */
362    if( existing_dir && *existing_dir && stat( existing_dir, &sb ) )
363    {
364        const int err = errno;
365        tr_err( _( "Couldn't open \"%1$s\": %2$s" ), existing_dir, tr_strerror( err ) );
366        return err;
367    }
368
369    /* create subfolders, if any */
370    if( writable )
371    {
372        char * dir = tr_dirname( filename );
373        const int err = tr_mkdirp( dir, 0777 ) ? errno : 0;
374        if( err ) {
375            tr_err( _( "Couldn't create \"%1$s\": %2$s" ), dir, tr_strerror( err ) );
376            tr_free( dir );
377            return err;
378        }
379        tr_free( dir );
380    }
381
382    alreadyExisted = !stat( filename, &sb ) && S_ISREG( sb.st_mode );
383
384    if( writable && !alreadyExisted && ( allocation == TR_PREALLOCATE_FULL ) )
385        if( preallocate_file_full( filename, file_size ) )
386            tr_dbg( "Preallocated file \"%s\"", filename );
387
388    /* open the file */
389    flags = writable ? ( O_RDWR | O_CREAT ) : O_RDONLY;
390    flags |= O_LARGEFILE | O_BINARY | O_SEQUENTIAL;
391    o->fd = open( filename, flags, 0666 );
392
393    if( o->fd == -1 )
394    {
395        const int err = errno;
396        tr_err( _( "Couldn't open \"%1$s\": %2$s" ), filename, tr_strerror( err ) );
397        return err;
398    }
399
400    /* If the file already exists and it's too large, truncate it.
401     * This is a fringe case that happens if a torrent's been updated
402     * and one of the updated torrent's files is smaller.
403     * http://trac.transmissionbt.com/ticket/2228
404     * https://bugs.launchpad.net/ubuntu/+source/transmission/+bug/318249
405     */
406    if( alreadyExisted && ( file_size < (uint64_t)sb.st_size ) )
407    {
408        if( ftruncate( o->fd, file_size ) == -1 )
409        {
410            const int err = errno;
411            tr_err( _( "Couldn't truncate \"%1$s\": %2$s" ), filename, tr_strerror( err ) );
412            return err;
413        }
414    }
415
416    if( writable && !alreadyExisted && ( allocation == TR_PREALLOCATE_SPARSE ) )
417        preallocate_file_sparse( o->fd, file_size );
418
419    /* Many (most?) clients request blocks in ascending order,
420     * so increase the readahead buffer.
421     * Also, disable OS-level caching because "inactive memory" angers users. */
422    tr_set_file_for_single_pass( o->fd );
423
424    return 0;
425}
426
427/***
428****
429***/
430
431struct tr_fileset
432{
433    struct tr_cached_file * begin;
434    const struct tr_cached_file * end;
435};
436
437static void
438fileset_construct( struct tr_fileset * set, int n )
439{
440    struct tr_cached_file * o;
441    const struct tr_cached_file TR_CACHED_FILE_INIT = { 0, -1, 0, 0, 0 };
442
443    set->begin = tr_new( struct tr_cached_file, n );
444    set->end = set->begin + n;
445
446    for( o=set->begin; o!=set->end; ++o )
447        *o = TR_CACHED_FILE_INIT;
448}
449
450static void
451fileset_close_all( struct tr_fileset * set )
452{
453    struct tr_cached_file * o;
454
455    if( set != NULL )
456        for( o=set->begin; o!=set->end; ++o )
457            if( cached_file_is_open( o ) )
458                cached_file_close( o );
459}
460
461static void
462fileset_destruct( struct tr_fileset * set )
463{
464    fileset_close_all( set );
465    tr_free( set->begin );
466    set->end = set->begin = NULL;
467}
468
469static void
470fileset_close_torrent( struct tr_fileset * set, int torrent_id )
471{
472    struct tr_cached_file * o;
473
474    if( set != NULL )
475        for( o=set->begin; o!=set->end; ++o )
476            if( ( o->torrent_id == torrent_id ) && cached_file_is_open( o ) )
477                cached_file_close( o );
478}
479
480static struct tr_cached_file *
481fileset_lookup( struct tr_fileset * set, int torrent_id, tr_file_index_t i )
482{
483    struct tr_cached_file * o;
484
485    if( set != NULL )
486        for( o=set->begin; o!=set->end; ++o )
487            if( ( torrent_id == o->torrent_id ) && ( i == o->file_index ) && cached_file_is_open( o ) )
488                return o;
489
490    return NULL;
491}
492
493static struct tr_cached_file *
494fileset_get_empty_slot( struct tr_fileset * set )
495{
496    struct tr_cached_file * o;
497    struct tr_cached_file * cull;
498
499    /* try to find an unused slot */
500    for( o=set->begin; o!=set->end; ++o )
501        if( !cached_file_is_open( o ) )
502            return o;
503
504    /* all slots are full... recycle the least recently used */
505    for( cull=NULL, o=set->begin; o!=set->end; ++o )
506        if( !cull || o->used_at < cull->used_at )
507            cull = o;
508    cached_file_close( cull );
509    return cull;
510}
511
512static int
513fileset_get_size( const struct tr_fileset * set )
514{
515    return set ? set->end - set->begin : 0;
516}
517
518/***
519****
520***/
521
522struct tr_fdInfo
523{
524    int socket_count;
525    int socket_limit;
526    int public_socket_limit;
527    struct tr_fileset fileset;
528};
529
530static struct tr_fileset*
531get_fileset( tr_session * session )
532{
533    return session && session->fdInfo ? &session->fdInfo->fileset : NULL;
534}
535
536void
537tr_fdFileClose( tr_session * s, const tr_torrent * tor, tr_file_index_t i )
538{
539    struct tr_cached_file * o;
540
541    if(( o = fileset_lookup( get_fileset( s ), tr_torrentId( tor ), i )))
542    {
543        /* flush writable files so that their mtimes will be
544         * up-to-date when this function returns to the caller... */
545        if( o->is_writable )
546            tr_fsync( o->fd );
547
548        cached_file_close( o );
549    }
550}
551
552int
553tr_fdFileGetCached( tr_session * s, int torrent_id, tr_file_index_t i, bool writable )
554{
555    struct tr_cached_file * o = fileset_lookup( get_fileset( s ), torrent_id, i );
556
557    if( !o || ( writable && !o->is_writable ) )
558        return -1;
559
560    o->used_at = tr_time( );
561    return o->fd;
562}
563
564void
565tr_fdTorrentClose( tr_session * session, int torrent_id )
566{
567    fileset_close_torrent( get_fileset( session ), torrent_id );
568}
569
570/* returns an fd on success, or a -1 on failure and sets errno */
571int
572tr_fdFileCheckout( tr_session             * session,
573                   int                      torrent_id,
574                   tr_file_index_t          i,
575                   const char             * existing_dir,
576                   const char             * filename,
577                   bool                     writable,
578                   tr_preallocation_mode    allocation,
579                   uint64_t                 file_size )
580{
581    struct tr_fileset * set = get_fileset( session );
582    struct tr_cached_file * o = fileset_lookup( set, torrent_id, i );
583
584    if( o && writable && !o->is_writable )
585        cached_file_close( o ); /* close it so we can reopen in rw mode */
586    else if( !o )
587        o = fileset_get_empty_slot( set );
588
589    if( !cached_file_is_open( o ) )
590    {
591        const int err = cached_file_open( o, existing_dir, filename, writable, allocation, file_size );
592        if( err ) {
593            errno = err;
594            return -1;
595        }
596
597        dbgmsg( "opened '%s' writable %c", filename, writable?'y':'n' );
598        o->is_writable = writable;
599    }
600
601    dbgmsg( "checking out '%s'", filename );
602    o->torrent_id = torrent_id;
603    o->file_index = i;
604    o->used_at = tr_time( );
605    return o->fd;
606}
607
608/***
609****
610****  Sockets
611****
612***/
613
614int
615tr_fdSocketCreate( tr_session * session, int domain, int type )
616{
617    int s = -1;
618    struct tr_fdInfo * gFd;
619
620    assert( tr_isSession( session ) );
621    assert( session->fdInfo != NULL );
622
623    gFd = session->fdInfo;
624
625    if( gFd->socket_count < gFd->socket_limit )
626        if(( s = socket( domain, type, 0 )) < 0 )
627            if( sockerrno != EAFNOSUPPORT )
628                tr_err( _( "Couldn't create socket: %s" ), tr_strerror( sockerrno ) );
629
630    if( s > -1 )
631        ++gFd->socket_count;
632
633    assert( gFd->socket_count >= 0 );
634
635    if( s >= 0 )
636    {
637        static bool buf_logged = false;
638        if( !buf_logged )
639        {
640            int i;
641            socklen_t size = sizeof( int );
642            buf_logged = true;
643            getsockopt( s, SOL_SOCKET, SO_SNDBUF, &i, &size );
644            tr_dbg( "SO_SNDBUF size is %d", i );
645            getsockopt( s, SOL_SOCKET, SO_RCVBUF, &i, &size );
646            tr_dbg( "SO_RCVBUF size is %d", i );
647        }
648    }
649
650    return s;
651}
652
653int
654tr_fdSocketAccept( tr_session * s, int sockfd, tr_address * addr, tr_port * port )
655{
656    int fd;
657    unsigned int len;
658    struct tr_fdInfo * gFd;
659    struct sockaddr_storage sock;
660
661    assert( tr_isSession( s ) );
662    assert( s->fdInfo != NULL );
663    assert( addr );
664    assert( port );
665
666    gFd = s->fdInfo;
667
668    len = sizeof( struct sockaddr_storage );
669    fd = accept( sockfd, (struct sockaddr *) &sock, &len );
670
671    if( fd >= 0 )
672    {
673        if( ( gFd->socket_count < gFd->socket_limit )
674            && tr_address_from_sockaddr_storage( addr, port, &sock ) )
675        {
676            ++gFd->socket_count;
677        }
678        else
679        {
680            tr_netCloseSocket( fd );
681            fd = -1;
682        }
683    }
684
685    return fd;
686}
687
688void
689tr_fdSocketClose( tr_session * session, int fd )
690{
691    assert( tr_isSession( session ) );
692
693    if( session->fdInfo != NULL )
694    {
695        struct tr_fdInfo * gFd = session->fdInfo;
696
697        if( fd >= 0 )
698        {
699            tr_netCloseSocket( fd );
700            --gFd->socket_count;
701        }
702
703        assert( gFd->socket_count >= 0 );
704    }
705}
706
707/***
708****
709****  Startup / Shutdown
710****
711***/
712
713static void
714ensureSessionFdInfoExists( tr_session * session )
715{
716    assert( tr_isSession( session ) );
717
718    if( session->fdInfo == NULL )
719        session->fdInfo = tr_new0( struct tr_fdInfo, 1 );
720}
721
722void
723tr_fdClose( tr_session * session )
724{
725    struct tr_fdInfo * gFd = session->fdInfo;
726
727    if( gFd != NULL )
728    {
729        fileset_destruct( &gFd->fileset );
730        tr_free( gFd );
731    }
732
733    session->fdInfo = NULL;
734}
735
736/***
737****
738***/
739
740int
741tr_fdGetFileLimit( tr_session * session )
742{
743    return fileset_get_size( get_fileset( session ) );
744}
745
746void
747tr_fdSetFileLimit( tr_session * session, int limit )
748{
749    int max;
750
751    /* This is a vaguely arbitrary number.
752       It takes announcer.c's MAX_CONCURRENT_TASKS into account,
753       plus extra positions for the listening sockets,
754       plus a few more just to be safe */
755    const int buffer_slots = 128;
756
757    ensureSessionFdInfoExists( session );
758
759    max = __FD_SETSIZE - session->fdInfo->socket_limit - buffer_slots;
760    if( limit > max )
761        limit = max;
762
763    if( limit != tr_fdGetFileLimit( session ) )
764    {
765        struct tr_fileset * set = get_fileset( session );
766        fileset_destruct( set );
767        fileset_construct( set, limit );
768    }
769}
770
771void
772tr_fdSetPeerLimit( tr_session * session, int socket_limit )
773{
774    struct tr_fdInfo * gFd;
775
776    ensureSessionFdInfoExists( session );
777
778    gFd = session->fdInfo;
779
780#ifdef HAVE_GETRLIMIT
781    {
782        struct rlimit rlim;
783        const int NOFILE_BUFFER = 512;
784        const int open_max = sysconf( _SC_OPEN_MAX );
785        getrlimit( RLIMIT_NOFILE, &rlim );
786        rlim.rlim_cur = MAX( 1024, open_max );
787        rlim.rlim_cur = MIN( rlim.rlim_cur, rlim.rlim_max );
788        setrlimit( RLIMIT_NOFILE, &rlim );
789        tr_dbg( "setrlimit( RLIMIT_NOFILE, %d )", (int)rlim.rlim_cur );
790        gFd->socket_limit = MIN( socket_limit, (int)rlim.rlim_cur - NOFILE_BUFFER );
791    }
792#else
793    gFd->socket_limit = socket_limit;
794#endif
795    gFd->public_socket_limit = socket_limit;
796
797    tr_dbg( "socket limit is %d", gFd->socket_limit );
798}
799
800int
801tr_fdGetPeerLimit( const tr_session * session )
802{
803    return session && session->fdInfo ? session->fdInfo->public_socket_limit : -1;
804}
Note: See TracBrowser for help on using the repository browser.