From: adrian <> Date: Wed, 3 May 2000 23:15:38 +0000 (+0000) Subject: MODIO_1 commit. This change (including documentation) implements a more X-Git-Tag: SQUID_3_0_PRE1~1992 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=cd748f27e5282c445ef4e13a0e2eabd19b6b906f;p=thirdparty%2Fsquid.git MODIO_1 commit. This change (including documentation) implements a more modular storage directory system, which leaves object replacement and IO up to the storage modules. There is a lot of repeated code in the FS modules and some tidying up is in the pipeline. The documentation for this new API is in doc/Programming-Guide/prog-guide.sgml . --- diff --git a/configure b/configure index 4229359908..421290fa86 100755 --- a/configure +++ b/configure @@ -28,10 +28,23 @@ ac_help="$ac_help --enable-carp Enable CARP support" ac_help="$ac_help --enable-async-io[=N_THREADS] - Do ASYNC disk I/O using threads. - N_THREADS is the number of worker threads - defaults to 16. See also src/squid.h for - some additional platform tuning" + Shorthand for + --with-aio-threads=N_THREADS + --with-pthreads + --enable-storeio=ufs,aufs" +ac_help="$ac_help + --with-aio-threads=N_THREADS + Tune the number of worker threads for the aufs object + store." +ac_help="$ac_help + --with-pthreads Use POSIX Threads" +ac_help="$ac_help + --enable-storeio=\"list of modules\" + Build support for the list of store I/O modules. + The default is only to build the "ufs" module. + See src/fs for a list of available modules, or + Programmers Guide section + for details on how to build your custom store module" ac_help="$ac_help --enable-icmp Enable ICMP pinging" ac_help="$ac_help @@ -64,6 +77,8 @@ ac_help="$ac_help ac_help="$ac_help --enable-err-language=lang Select language for Error pages (see errors dir) " +ac_help="$ac_help + --with-coss-membuf-size COSS membuf size (default 1048576 bytes) " ac_help="$ac_help --enable-poll Enable poll() instead of select(). Normally poll is preferred over select, but configure knows poll @@ -110,17 +125,13 @@ ac_help="$ac_help --enable-heap-replacement This option allows you to use various cache replacement algorithms, instead of the standard - LRU algorithm. - " + LRU algorithm." ac_help="$ac_help --enable-auth-modules=\"list of modules\" This option selects wich proxy_auth helper modules to build and install as part of the normal build process. For a list of available modules see the auth_modules directory." -ac_help="$ac_help - --enable-diskd Uses shared memory, message queues, and external - processes for disk I/O." # Initialize some variables set by options. # The variables have the same names as the options, with @@ -632,7 +643,7 @@ fi -# From configure.in Revision +# From configure.in Revision: 1.185 ac_aux_dir= for ac_dir in cfgaux $srcdir/cfgaux; do if test -f $ac_dir/install-sh; then @@ -660,7 +671,7 @@ else { echo "configure: error: can not run $ac_config_sub" 1>&2; exit 1; } fi echo $ac_n "checking host system type""... $ac_c" 1>&6 -echo "configure:664: checking host system type" >&5 +echo "configure:675: checking host system type" >&5 host_alias=$host case "$host_alias" in @@ -728,7 +739,7 @@ PRESET_CFLAGS="$CFLAGS" # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:732: checking for $ac_word" >&5 +echo "configure:743: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -758,7 +769,7 @@ if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:762: checking for $ac_word" >&5 +echo "configure:773: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -809,7 +820,7 @@ fi # Extract the first word of "cl", so it can be a program name with args. set dummy cl; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:813: checking for $ac_word" >&5 +echo "configure:824: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -841,7 +852,7 @@ fi fi echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6 -echo "configure:845: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5 +echo "configure:856: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5 ac_ext=c # CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. @@ -852,12 +863,12 @@ cross_compiling=$ac_cv_prog_cc_cross cat > conftest.$ac_ext << EOF -#line 856 "configure" +#line 867 "configure" #include "confdefs.h" main(){return(0);} EOF -if { (eval echo configure:861: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:872: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then ac_cv_prog_cc_works=yes # If we can't run a trivial program, we are probably using a cross compiler. if (./conftest; exit) 2>/dev/null; then @@ -883,12 +894,12 @@ if test $ac_cv_prog_cc_works = no; then { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; } fi echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6 -echo "configure:887: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5 +echo "configure:898: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5 echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6 cross_compiling=$ac_cv_prog_cc_cross echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6 -echo "configure:892: checking whether we are using GNU C" >&5 +echo "configure:903: checking whether we are using GNU C" >&5 if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -897,7 +908,7 @@ else yes; #endif EOF -if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:901: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then +if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:912: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then ac_cv_prog_gcc=yes else ac_cv_prog_gcc=no @@ -916,7 +927,7 @@ ac_test_CFLAGS="${CFLAGS+set}" ac_save_CFLAGS="$CFLAGS" CFLAGS= echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6 -echo "configure:920: checking whether ${CC-cc} accepts -g" >&5 +echo "configure:931: checking whether ${CC-cc} accepts -g" >&5 if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1148,49 +1159,93 @@ EOF fi -# Check whether --enable-async_io or --disable-async_io was given. +# Check whether --enable-async-io or --disable-async-io was given. if test "${enable_async_io+set}" = set; then enableval="$enable_async_io" - case "$enableval" in + case $enableval in yes) - async_io=yes + with_pthreads=yes + STORE_MODULES="ufs aufs" ;; no) - async_io='' ;; *) - async_io=yes - cat >> confdefs.h <> confdefs.h <<\EOF -#define USE_ASYNC_IO 1 +# Check whether --with-aio-threads or --without-aio-threads was given. +if test "${with_aio_threads+set}" = set; then + withval="$with_aio_threads" + async_io_threads=$withval +fi + +if test "$async_io_threads"; then + echo "With $async_io_threads AIO threads" + with_pthreads=yes + cat >> confdefs.h <> confdefs.h <> confdefs.h <<\EOF -#define USE_DISKD 1 -EOF - - OPT_DISKD_EXE='$(DISKD_EXE)' - - DISKD_OBJS='diskd.o' - - fi - -fi - - # Force some compilers to use ANSI features # case "$host" in @@ -1641,7 +1692,7 @@ case "$host" in esac echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6 -echo "configure:1645: checking how to run the C preprocessor" >&5 +echo "configure:1696: checking how to run the C preprocessor" >&5 # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= @@ -1656,13 +1707,13 @@ else # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. cat > conftest.$ac_ext < Syntax Error EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:1666: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:1717: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then : @@ -1673,13 +1724,13 @@ else rm -rf conftest* CPP="${CC-cc} -E -traditional-cpp" cat > conftest.$ac_ext < Syntax Error EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:1683: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:1734: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then : @@ -1690,13 +1741,13 @@ else rm -rf conftest* CPP="${CC-cc} -nologo -E" cat > conftest.$ac_ext < Syntax Error EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:1700: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:1751: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then : @@ -1732,7 +1783,7 @@ echo "$ac_t""$CPP" 1>&6 # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # ./install, which can be erroneously created by make from ./install.sh. echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6 -echo "configure:1736: checking for a BSD compatible install" >&5 +echo "configure:1787: checking for a BSD compatible install" >&5 if test -z "$INSTALL"; then if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -1787,7 +1838,7 @@ test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' # Extract the first word of "ranlib", so it can be a program name with args. set dummy ranlib; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:1791: checking for $ac_word" >&5 +echo "configure:1842: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1815,7 +1866,7 @@ else fi echo $ac_n "checking whether ln -s works""... $ac_c" 1>&6 -echo "configure:1819: checking whether ln -s works" >&5 +echo "configure:1870: checking whether ln -s works" >&5 if eval "test \"`echo '$''{'ac_cv_prog_LN_S'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1838,7 +1889,7 @@ fi # Extract the first word of "sh", so it can be a program name with args. set dummy sh; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:1842: checking for $ac_word" >&5 +echo "configure:1893: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_path_SH'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1874,7 +1925,7 @@ fi # Extract the first word of "false", so it can be a program name with args. set dummy false; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:1878: checking for $ac_word" >&5 +echo "configure:1929: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_path_FALSE'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1910,7 +1961,7 @@ fi # Extract the first word of "true", so it can be a program name with args. set dummy true; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:1914: checking for $ac_word" >&5 +echo "configure:1965: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_path_TRUE'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1946,7 +1997,7 @@ fi # Extract the first word of "rm", so it can be a program name with args. set dummy rm; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:1950: checking for $ac_word" >&5 +echo "configure:2001: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_path_RM'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1982,7 +2033,7 @@ fi # Extract the first word of "mv", so it can be a program name with args. set dummy mv; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:1986: checking for $ac_word" >&5 +echo "configure:2037: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_path_MV'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -2018,7 +2069,7 @@ fi # Extract the first word of "mkdir", so it can be a program name with args. set dummy mkdir; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:2022: checking for $ac_word" >&5 +echo "configure:2073: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_path_MKDIR'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -2054,7 +2105,7 @@ fi # Extract the first word of "ln", so it can be a program name with args. set dummy ln; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:2058: checking for $ac_word" >&5 +echo "configure:2109: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_path_LN'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -2090,7 +2141,7 @@ fi # Extract the first word of "perl", so it can be a program name with args. set dummy perl; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:2094: checking for $ac_word" >&5 +echo "configure:2145: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_path_PERL'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -2126,7 +2177,7 @@ fi # Extract the first word of "makedepend", so it can be a program name with args. set dummy makedepend; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:2130: checking for $ac_word" >&5 +echo "configure:2181: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_path_MAKEDEPEND'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -2162,7 +2213,7 @@ fi # Extract the first word of "ar", so it can be a program name with args. set dummy ar; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:2166: checking for $ac_word" >&5 +echo "configure:2217: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_path_AR'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -2216,12 +2267,12 @@ for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h do ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` echo $ac_n "checking for $ac_hdr that defines DIR""... $ac_c" 1>&6 -echo "configure:2220: checking for $ac_hdr that defines DIR" >&5 +echo "configure:2271: checking for $ac_hdr that defines DIR" >&5 if eval "test \"`echo '$''{'ac_cv_header_dirent_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #include <$ac_hdr> @@ -2229,7 +2280,7 @@ int main() { DIR *dirp = 0; ; return 0; } EOF -if { (eval echo configure:2233: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:2284: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* eval "ac_cv_header_dirent_$ac_safe=yes" else @@ -2254,7 +2305,7 @@ done # Two versions of opendir et al. are in -ldir and -lx on SCO Xenix. if test $ac_header_dirent = dirent.h; then echo $ac_n "checking for opendir in -ldir""... $ac_c" 1>&6 -echo "configure:2258: checking for opendir in -ldir" >&5 +echo "configure:2309: checking for opendir in -ldir" >&5 ac_lib_var=`echo dir'_'opendir | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -2262,7 +2313,7 @@ else ac_save_LIBS="$LIBS" LIBS="-ldir $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:2328: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -2295,7 +2346,7 @@ fi else echo $ac_n "checking for opendir in -lx""... $ac_c" 1>&6 -echo "configure:2299: checking for opendir in -lx" >&5 +echo "configure:2350: checking for opendir in -lx" >&5 ac_lib_var=`echo x'_'opendir | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -2303,7 +2354,7 @@ else ac_save_LIBS="$LIBS" LIBS="-lx $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:2369: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -2337,12 +2388,12 @@ fi fi echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6 -echo "configure:2341: checking for ANSI C header files" >&5 +echo "configure:2392: checking for ANSI C header files" >&5 if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #include @@ -2350,7 +2401,7 @@ else #include EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:2354: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:2405: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then rm -rf conftest* @@ -2367,7 +2418,7 @@ rm -f conftest* if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat > conftest.$ac_ext < EOF @@ -2385,7 +2436,7 @@ fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat > conftest.$ac_ext < EOF @@ -2406,7 +2457,7 @@ if test "$cross_compiling" = yes; then : else cat > conftest.$ac_ext < #define ISLOWER(c) ('a' <= (c) && (c) <= 'z') @@ -2417,7 +2468,7 @@ if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2); exit (0); } EOF -if { (eval echo configure:2421: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +if { (eval echo configure:2472: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null then : else @@ -2508,17 +2559,17 @@ for ac_hdr in \ do ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 -echo "configure:2512: checking for $ac_hdr" >&5 +echo "configure:2563: checking for $ac_hdr" >&5 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:2522: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:2573: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then rm -rf conftest* @@ -2546,12 +2597,12 @@ done echo $ac_n "checking for working const""... $ac_c" 1>&6 -echo "configure:2550: checking for working const" >&5 +echo "configure:2601: checking for working const" >&5 if eval "test \"`echo '$''{'ac_cv_c_const'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:2655: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_c_const=yes else @@ -2621,14 +2672,14 @@ EOF fi echo $ac_n "checking whether byte ordering is bigendian""... $ac_c" 1>&6 -echo "configure:2625: checking whether byte ordering is bigendian" >&5 +echo "configure:2676: checking whether byte ordering is bigendian" >&5 if eval "test \"`echo '$''{'ac_cv_c_bigendian'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else ac_cv_c_bigendian=unknown # See if sys/param.h defines the BYTE_ORDER macro. cat > conftest.$ac_ext < #include @@ -2639,11 +2690,11 @@ int main() { #endif ; return 0; } EOF -if { (eval echo configure:2643: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:2694: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* # It does; now see whether it defined to BIG_ENDIAN or not. cat > conftest.$ac_ext < #include @@ -2654,7 +2705,7 @@ int main() { #endif ; return 0; } EOF -if { (eval echo configure:2658: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:2709: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_c_bigendian=yes else @@ -2674,7 +2725,7 @@ if test "$cross_compiling" = yes; then { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; } else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +if { (eval echo configure:2742: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null then ac_cv_c_bigendian=no else @@ -2712,20 +2763,20 @@ fi echo $ac_n "checking if ANSI prototypes work""... $ac_c" 1>&6 -echo "configure:2716: checking if ANSI prototypes work" >&5 +echo "configure:2767: checking if ANSI prototypes work" >&5 if eval "test \"`echo '$''{'ac_cv_have_ansi_prototypes'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:2780: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_have_ansi_prototypes="yes" else @@ -2747,13 +2798,13 @@ EOF fi echo $ac_n "checking for tm->tm_gmtoff""... $ac_c" 1>&6 -echo "configure:2751: checking for tm->tm_gmtoff" >&5 +echo "configure:2802: checking for tm->tm_gmtoff" >&5 if eval "test \"`echo '$''{'ac_cv_have_tm_gmoff'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #include @@ -2762,7 +2813,7 @@ struct tm foo; foo.tm_gmtoff = 0; ; return 0; } EOF -if { (eval echo configure:2766: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:2817: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_have_tm_gmoff="yes" else @@ -2784,13 +2835,13 @@ EOF fi echo $ac_n "checking for struct mallinfo""... $ac_c" 1>&6 -echo "configure:2788: checking for struct mallinfo" >&5 +echo "configure:2839: checking for struct mallinfo" >&5 if eval "test \"`echo '$''{'ac_cv_have_struct_mallinfo'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #if HAVE_MALLOC_H @@ -2808,7 +2859,7 @@ struct mallinfo foo; foo.keepcost = 0; ; return 0; } EOF -if { (eval echo configure:2812: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:2863: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_have_struct_mallinfo="yes" else @@ -2830,13 +2881,13 @@ EOF fi echo $ac_n "checking for extended mallinfo""... $ac_c" 1>&6 -echo "configure:2834: checking for extended mallinfo" >&5 +echo "configure:2885: checking for extended mallinfo" >&5 if eval "test \"`echo '$''{'ac_cv_have_ext_mallinfo'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #include @@ -2845,7 +2896,7 @@ struct mallinfo foo; foo.mxfast = 0; ; return 0; } EOF -if { (eval echo configure:2849: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:2900: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_have_ext_mallinfo="yes" else @@ -2867,13 +2918,13 @@ EOF fi echo $ac_n "checking for struct rusage""... $ac_c" 1>&6 -echo "configure:2871: checking for struct rusage" >&5 +echo "configure:2922: checking for struct rusage" >&5 if eval "test \"`echo '$''{'ac_cv_have_struct_rusage'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:2941: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_have_struct_rusage="yes" else @@ -2908,13 +2959,13 @@ EOF fi echo $ac_n "checking for ip->ip_hl""... $ac_c" 1>&6 -echo "configure:2912: checking for ip->ip_hl" >&5 +echo "configure:2963: checking for ip->ip_hl" >&5 if eval "test \"`echo '$''{'ac_cv_have_ip_hl'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #include @@ -2931,7 +2982,7 @@ struct iphdr ip; ip.ip_hl= 0; ; return 0; } EOF -if { (eval echo configure:2935: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:2986: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_have_ip_hl="yes" else @@ -2953,7 +3004,7 @@ EOF fi echo $ac_n "checking size of int""... $ac_c" 1>&6 -echo "configure:2957: checking size of int" >&5 +echo "configure:3008: checking size of int" >&5 if eval "test \"`echo '$''{'ac_cv_sizeof_int'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -2961,7 +3012,7 @@ else { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; } else cat > conftest.$ac_ext < main() @@ -2972,7 +3023,7 @@ main() exit(0); } EOF -if { (eval echo configure:2976: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +if { (eval echo configure:3027: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null then ac_cv_sizeof_int=`cat conftestval` else @@ -2992,7 +3043,7 @@ EOF echo $ac_n "checking size of long""... $ac_c" 1>&6 -echo "configure:2996: checking size of long" >&5 +echo "configure:3047: checking size of long" >&5 if eval "test \"`echo '$''{'ac_cv_sizeof_long'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -3000,7 +3051,7 @@ else { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; } else cat > conftest.$ac_ext < main() @@ -3011,7 +3062,7 @@ main() exit(0); } EOF -if { (eval echo configure:3015: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +if { (eval echo configure:3066: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null then ac_cv_sizeof_long=`cat conftestval` else @@ -3031,7 +3082,7 @@ EOF echo $ac_n "checking size of void *""... $ac_c" 1>&6 -echo "configure:3035: checking size of void *" >&5 +echo "configure:3086: checking size of void *" >&5 if eval "test \"`echo '$''{'ac_cv_sizeof_void_p'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -3039,7 +3090,7 @@ else { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; } else cat > conftest.$ac_ext < main() @@ -3050,7 +3101,7 @@ main() exit(0); } EOF -if { (eval echo configure:3054: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +if { (eval echo configure:3105: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null then ac_cv_sizeof_void_p=`cat conftestval` else @@ -3073,19 +3124,19 @@ EOF # The Ultrix 4.2 mips builtin alloca declared by alloca.h only works # for constant arguments. Useless! echo $ac_n "checking for working alloca.h""... $ac_c" 1>&6 -echo "configure:3077: checking for working alloca.h" >&5 +echo "configure:3128: checking for working alloca.h" >&5 if eval "test \"`echo '$''{'ac_cv_header_alloca_h'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < int main() { char *p = alloca(2 * sizeof(int)); ; return 0; } EOF -if { (eval echo configure:3089: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:3140: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* ac_cv_header_alloca_h=yes else @@ -3106,12 +3157,12 @@ EOF fi echo $ac_n "checking for alloca""... $ac_c" 1>&6 -echo "configure:3110: checking for alloca" >&5 +echo "configure:3161: checking for alloca" >&5 if eval "test \"`echo '$''{'ac_cv_func_alloca_works'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:3194: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* ac_cv_func_alloca_works=yes else @@ -3171,12 +3222,12 @@ EOF echo $ac_n "checking whether alloca needs Cray hooks""... $ac_c" 1>&6 -echo "configure:3175: checking whether alloca needs Cray hooks" >&5 +echo "configure:3226: checking whether alloca needs Cray hooks" >&5 if eval "test \"`echo '$''{'ac_cv_os_cray'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&6 if test $ac_cv_os_cray = yes; then for ac_func in _getb67 GETB67 getb67; do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 -echo "configure:3205: checking for $ac_func" >&5 +echo "configure:3256: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:3284: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else @@ -3256,7 +3307,7 @@ done fi echo $ac_n "checking stack direction for C alloca""... $ac_c" 1>&6 -echo "configure:3260: checking stack direction for C alloca" >&5 +echo "configure:3311: checking stack direction for C alloca" >&5 if eval "test \"`echo '$''{'ac_cv_c_stack_direction'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -3264,7 +3315,7 @@ else ac_cv_c_stack_direction=0 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +if { (eval echo configure:3338: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null then ac_cv_c_stack_direction=1 else @@ -3306,12 +3357,12 @@ fi echo $ac_n "checking for pid_t""... $ac_c" 1>&6 -echo "configure:3310: checking for pid_t" >&5 +echo "configure:3361: checking for pid_t" >&5 if eval "test \"`echo '$''{'ac_cv_type_pid_t'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #if STDC_HEADERS @@ -3339,12 +3390,12 @@ EOF fi echo $ac_n "checking for size_t""... $ac_c" 1>&6 -echo "configure:3343: checking for size_t" >&5 +echo "configure:3394: checking for size_t" >&5 if eval "test \"`echo '$''{'ac_cv_type_size_t'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #if STDC_HEADERS @@ -3372,12 +3423,12 @@ EOF fi echo $ac_n "checking for ssize_t""... $ac_c" 1>&6 -echo "configure:3376: checking for ssize_t" >&5 +echo "configure:3427: checking for ssize_t" >&5 if eval "test \"`echo '$''{'ac_cv_type_ssize_t'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #if STDC_HEADERS @@ -3405,12 +3456,12 @@ EOF fi echo $ac_n "checking for off_t""... $ac_c" 1>&6 -echo "configure:3409: checking for off_t" >&5 +echo "configure:3460: checking for off_t" >&5 if eval "test \"`echo '$''{'ac_cv_type_off_t'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #if STDC_HEADERS @@ -3438,12 +3489,12 @@ EOF fi echo $ac_n "checking for mode_t""... $ac_c" 1>&6 -echo "configure:3442: checking for mode_t" >&5 +echo "configure:3493: checking for mode_t" >&5 if eval "test \"`echo '$''{'ac_cv_type_mode_t'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #if STDC_HEADERS @@ -3471,12 +3522,12 @@ EOF fi echo $ac_n "checking for fd_mask""... $ac_c" 1>&6 -echo "configure:3475: checking for fd_mask" >&5 +echo "configure:3526: checking for fd_mask" >&5 if eval "test \"`echo '$''{'ac_cv_type_fd_mask'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #if STDC_HEADERS @@ -3504,12 +3555,12 @@ EOF fi echo $ac_n "checking for mtyp_t""... $ac_c" 1>&6 -echo "configure:3508: checking for mtyp_t" >&5 +echo "configure:3559: checking for mtyp_t" >&5 if eval "test \"`echo '$''{'ac_cv_type_mtyp_t'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #if STDC_HEADERS @@ -3538,13 +3589,13 @@ fi echo $ac_n "checking for socklen_t""... $ac_c" 1>&6 -echo "configure:3542: checking for socklen_t" >&5 +echo "configure:3593: checking for socklen_t" >&5 if eval "test \"`echo '$''{'ac_cv_type_socklen_t'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #include @@ -3575,7 +3626,7 @@ EOF fi echo $ac_n "checking for main in -lnsl""... $ac_c" 1>&6 -echo "configure:3579: checking for main in -lnsl" >&5 +echo "configure:3630: checking for main in -lnsl" >&5 ac_lib_var=`echo nsl'_'main | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -3583,14 +3634,14 @@ else ac_save_LIBS="$LIBS" LIBS="-lnsl $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:3645: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -3618,7 +3669,7 @@ else fi echo $ac_n "checking for main in -lsocket""... $ac_c" 1>&6 -echo "configure:3622: checking for main in -lsocket" >&5 +echo "configure:3673: checking for main in -lsocket" >&5 ac_lib_var=`echo socket'_'main | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -3626,14 +3677,14 @@ else ac_save_LIBS="$LIBS" LIBS="-lsocket $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:3688: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -3665,7 +3716,7 @@ if test "x$ac_cv_enabled_dlmalloc" = "xyes" ; then echo "skipping libmalloc check (--enable-dlmalloc specified)" else echo $ac_n "checking for main in -lgnumalloc""... $ac_c" 1>&6 -echo "configure:3669: checking for main in -lgnumalloc" >&5 +echo "configure:3720: checking for main in -lgnumalloc" >&5 ac_lib_var=`echo gnumalloc'_'main | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -3673,14 +3724,14 @@ else ac_save_LIBS="$LIBS" LIBS="-lgnumalloc $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:3735: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -3723,7 +3774,7 @@ fi *) echo $ac_n "checking for main in -lmalloc""... $ac_c" 1>&6 -echo "configure:3727: checking for main in -lmalloc" >&5 +echo "configure:3778: checking for main in -lmalloc" >&5 ac_lib_var=`echo malloc'_'main | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -3731,14 +3782,14 @@ else ac_save_LIBS="$LIBS" LIBS="-lmalloc $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:3793: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -3771,7 +3822,7 @@ fi fi echo $ac_n "checking for main in -lbsd""... $ac_c" 1>&6 -echo "configure:3775: checking for main in -lbsd" >&5 +echo "configure:3826: checking for main in -lbsd" >&5 ac_lib_var=`echo bsd'_'main | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -3779,14 +3830,14 @@ else ac_save_LIBS="$LIBS" LIBS="-lbsd $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:3841: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -3814,7 +3865,7 @@ else fi echo $ac_n "checking for main in -lregex""... $ac_c" 1>&6 -echo "configure:3818: checking for main in -lregex" >&5 +echo "configure:3869: checking for main in -lregex" >&5 ac_lib_var=`echo regex'_'main | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -3822,14 +3873,14 @@ else ac_save_LIBS="$LIBS" LIBS="-lregex $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:3884: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -3850,7 +3901,7 @@ else fi echo $ac_n "checking for gethostbyname in -lbind""... $ac_c" 1>&6 -echo "configure:3854: checking for gethostbyname in -lbind" >&5 +echo "configure:3905: checking for gethostbyname in -lbind" >&5 ac_lib_var=`echo bind'_'gethostbyname | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -3858,7 +3909,7 @@ else ac_save_LIBS="$LIBS" LIBS="-lbind $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:3924: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -3903,7 +3954,7 @@ if test $ac_cv_lib_bind_gethostbyname = "no" ; then ;; *) echo $ac_n "checking for inet_aton in -lresolv""... $ac_c" 1>&6 -echo "configure:3907: checking for inet_aton in -lresolv" >&5 +echo "configure:3958: checking for inet_aton in -lresolv" >&5 ac_lib_var=`echo resolv'_'inet_aton | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -3911,7 +3962,7 @@ else ac_save_LIBS="$LIBS" LIBS="-lresolv $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:3977: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -3938,7 +3989,7 @@ fi if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then echo "$ac_t""yes" 1>&6 echo $ac_n "checking for inet_aton in -l44bsd""... $ac_c" 1>&6 -echo "configure:3942: checking for inet_aton in -l44bsd" >&5 +echo "configure:3993: checking for inet_aton in -l44bsd" >&5 ac_lib_var=`echo 44bsd'_'inet_aton | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -3946,7 +3997,7 @@ else ac_save_LIBS="$LIBS" LIBS="-l44bsd $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:4012: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -3989,7 +4040,7 @@ else fi echo $ac_n "checking for main in -lresolv""... $ac_c" 1>&6 -echo "configure:3993: checking for main in -lresolv" >&5 +echo "configure:4044: checking for main in -lresolv" >&5 ac_lib_var=`echo resolv'_'main | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -3997,14 +4048,14 @@ else ac_save_LIBS="$LIBS" LIBS="-lresolv $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:4059: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -4035,7 +4086,7 @@ fi esac fi echo $ac_n "checking for main in -lm""... $ac_c" 1>&6 -echo "configure:4039: checking for main in -lm" >&5 +echo "configure:4090: checking for main in -lm" >&5 ac_lib_var=`echo m'_'main | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -4043,14 +4094,14 @@ else ac_save_LIBS="$LIBS" LIBS="-lm $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:4105: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -4079,7 +4130,7 @@ fi echo $ac_n "checking for crypt in -lcrypt""... $ac_c" 1>&6 -echo "configure:4083: checking for crypt in -lcrypt" >&5 +echo "configure:4134: checking for crypt in -lcrypt" >&5 ac_lib_var=`echo crypt'_'crypt | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -4087,7 +4138,7 @@ else ac_save_LIBS="$LIBS" LIBS="-lcrypt $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:4153: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -4131,7 +4182,7 @@ fi echo $ac_n "checking for main in -lpthread""... $ac_c" 1>&6 -echo "configure:4135: checking for main in -lpthread" >&5 +echo "configure:4186: checking for main in -lpthread" >&5 ac_lib_var=`echo pthread'_'main | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -4139,14 +4190,14 @@ else ac_save_LIBS="$LIBS" LIBS="-lpthread $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:4201: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -4181,7 +4232,7 @@ fi case "$host" in *-pc-sco3.2*) echo $ac_n "checking for strftime in -lintl""... $ac_c" 1>&6 -echo "configure:4185: checking for strftime in -lintl" >&5 +echo "configure:4236: checking for strftime in -lintl" >&5 ac_lib_var=`echo intl'_'strftime | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -4189,7 +4240,7 @@ else ac_save_LIBS="$LIBS" LIBS="-lintl $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:4255: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -4340,6 +4391,9 @@ for ac_func in \ mktime \ mstats \ poll \ + pthread_attr_setscope \ + pthread_setschedparam \ + pthread_attr_setschedparam \ putenv \ random \ regcomp \ @@ -4365,12 +4419,12 @@ for ac_func in \ do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 -echo "configure:4369: checking for $ac_func" >&5 +echo "configure:4423: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:4451: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else @@ -4418,70 +4472,8 @@ fi done -if test "$async_io" = "yes" ; then - for ac_func in \ - pthread_attr_setscope \ - pthread_setschedparam \ - pthread_attr_setschedparam \ - -do -echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 -echo "configure:4430: checking for $ac_func" >&5 -if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then - echo $ac_n "(cached) $ac_c" 1>&6 -else - cat > conftest.$ac_ext < -/* Override any gcc2 internal prototype to avoid an error. */ -/* We use char because int might match the return type of a gcc2 - builtin and then its argument prototype would still apply. */ -char $ac_func(); - -int main() { - -/* The GNU C library defines this for functions which it implements - to always fail with ENOSYS. Some functions are actually named - something starting with __ and the normal name is an alias. */ -#if defined (__stub_$ac_func) || defined (__stub___$ac_func) -choke me -#else -$ac_func(); -#endif - -; return 0; } -EOF -if { (eval echo configure:4458: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then - rm -rf conftest* - eval "ac_cv_func_$ac_func=yes" -else - echo "configure: failed program was:" >&5 - cat conftest.$ac_ext >&5 - rm -rf conftest* - eval "ac_cv_func_$ac_func=no" -fi -rm -f conftest* -fi - -if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then - echo "$ac_t""yes" 1>&6 - ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` - cat >> confdefs.h <&6 -fi -done - -fi - echo $ac_n "checking if setresuid is implemented""... $ac_c" 1>&6 -echo "configure:4485: checking if setresuid is implemented" >&5 +echo "configure:4477: checking if setresuid is implemented" >&5 if eval "test \"`echo '$''{'ac_cv_func_setresuid'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -4489,7 +4481,7 @@ else { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; } else cat > conftest.$ac_ext < @@ -4502,7 +4494,7 @@ else } EOF -if { (eval echo configure:4506: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +if { (eval echo configure:4498: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null then ac_cv_func_setresuid="yes" else @@ -4527,7 +4519,7 @@ fi if test "$IPF_TRANSPARENT" ; then echo $ac_n "checking if IP-Filter header files are installed""... $ac_c" 1>&6 -echo "configure:4531: checking if IP-Filter header files are installed" >&5 +echo "configure:4523: checking if IP-Filter header files are installed" >&5 if test "$ac_cv_header_ip_compat_h" = "yes" && test "$ac_cv_header_ip_fil_h" = "yes" && test "$ac_cv_header_ip_nat_h" = "yes" ; then @@ -4570,13 +4562,13 @@ if test -z "$USE_GNUREGEX" ; then esac fi echo $ac_n "checking if GNUregex needs to be compiled""... $ac_c" 1>&6 -echo "configure:4574: checking if GNUregex needs to be compiled" >&5 +echo "configure:4566: checking if GNUregex needs to be compiled" >&5 if test -z "$USE_GNUREGEX"; then if test "$ac_cv_func_regcomp" = "no" || test "$USE_GNUREGEX" = "yes" ; then USE_GNUREGEX="yes" else cat > conftest.$ac_ext < #include @@ -4584,7 +4576,7 @@ int main() { regex_t t; regcomp(&t,"",0); ; return 0; } EOF -if { (eval echo configure:4588: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:4580: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* USE_GNUREGEX="no" else @@ -4615,12 +4607,12 @@ for ac_func in \ do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 -echo "configure:4619: checking for $ac_func" >&5 +echo "configure:4611: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:4639: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else @@ -4671,12 +4663,12 @@ done echo $ac_n "checking Default FD_SETSIZE value""... $ac_c" 1>&6 -echo "configure:4675: checking Default FD_SETSIZE value" >&5 +echo "configure:4667: checking Default FD_SETSIZE value" >&5 if test "$cross_compiling" = yes; then DEFAULT_FD_SETSIZE=256 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +if { (eval echo configure:4697: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null then DEFAULT_FD_SETSIZE=`cat conftestval` else @@ -4720,7 +4712,7 @@ EOF echo $ac_n "checking Maximum number of filedescriptors we can open""... $ac_c" 1>&6 -echo "configure:4724: checking Maximum number of filedescriptors we can open" >&5 +echo "configure:4716: checking Maximum number of filedescriptors we can open" >&5 TLDFLAGS="$LDFLAGS" case $host in i386-unknown-freebsd*) @@ -4732,7 +4724,7 @@ if test "$cross_compiling" = yes; then SQUID_MAXFD=256 else cat > conftest.$ac_ext < @@ -4789,7 +4781,7 @@ main() { } EOF -if { (eval echo configure:4793: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +if { (eval echo configure:4785: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null then SQUID_MAXFD=`cat conftestval` else @@ -4816,12 +4808,12 @@ fi LDFLAGS="$TLDFLAGS" echo $ac_n "checking Default UDP send buffer size""... $ac_c" 1>&6 -echo "configure:4820: checking Default UDP send buffer size" >&5 +echo "configure:4812: checking Default UDP send buffer size" >&5 if test "$cross_compiling" = yes; then SQUID_UDP_SO_SNDBUF=16384 else cat > conftest.$ac_ext < @@ -4842,7 +4834,7 @@ main () } EOF -if { (eval echo configure:4846: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +if { (eval echo configure:4838: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null then SQUID_UDP_SO_SNDBUF=`cat conftestval` else @@ -4861,12 +4853,12 @@ EOF echo $ac_n "checking Default UDP receive buffer size""... $ac_c" 1>&6 -echo "configure:4865: checking Default UDP receive buffer size" >&5 +echo "configure:4857: checking Default UDP receive buffer size" >&5 if test "$cross_compiling" = yes; then SQUID_UDP_SO_RCVBUF=16384 else cat > conftest.$ac_ext < @@ -4887,7 +4879,7 @@ main () } EOF -if { (eval echo configure:4891: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +if { (eval echo configure:4883: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null then SQUID_UDP_SO_RCVBUF=`cat conftestval` else @@ -4906,12 +4898,12 @@ EOF echo $ac_n "checking Default TCP send buffer size""... $ac_c" 1>&6 -echo "configure:4910: checking Default TCP send buffer size" >&5 +echo "configure:4902: checking Default TCP send buffer size" >&5 if test "$cross_compiling" = yes; then SQUID_TCP_SO_SNDBUF=16384 else cat > conftest.$ac_ext < @@ -4932,7 +4924,7 @@ main () } EOF -if { (eval echo configure:4936: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +if { (eval echo configure:4928: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null then SQUID_TCP_SO_SNDBUF=`cat conftestval` else @@ -4951,12 +4943,12 @@ EOF echo $ac_n "checking Default TCP receive buffer size""... $ac_c" 1>&6 -echo "configure:4955: checking Default TCP receive buffer size" >&5 +echo "configure:4947: checking Default TCP receive buffer size" >&5 if test "$cross_compiling" = yes; then SQUID_TCP_SO_RCVBUF=16384 else cat > conftest.$ac_ext < @@ -4977,7 +4969,7 @@ main () } EOF -if { (eval echo configure:4981: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +if { (eval echo configure:4973: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null then SQUID_TCP_SO_RCVBUF=`cat conftestval` else @@ -4996,19 +4988,19 @@ EOF echo $ac_n "checking if sys_errlist is already defined""... $ac_c" 1>&6 -echo "configure:5000: checking if sys_errlist is already defined" >&5 +echo "configure:4992: checking if sys_errlist is already defined" >&5 if eval "test \"`echo '$''{'ac_cv_needs_sys_errlist'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < int main() { char *s = sys_errlist; ; return 0; } EOF -if { (eval echo configure:5012: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:5004: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_needs_sys_errlist="no" else @@ -5030,16 +5022,16 @@ EOF fi echo $ac_n "checking for libresolv _dns_ttl_ hack""... $ac_c" 1>&6 -echo "configure:5034: checking for libresolv _dns_ttl_ hack" >&5 +echo "configure:5026: checking for libresolv _dns_ttl_ hack" >&5 cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:5035: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* echo "$ac_t""yes" 1>&6 cat >> confdefs.h <<\EOF @@ -5055,12 +5047,12 @@ fi rm -f conftest* echo $ac_n "checking if inet_ntoa() actually works""... $ac_c" 1>&6 -echo "configure:5059: checking if inet_ntoa() actually works" >&5 +echo "configure:5051: checking if inet_ntoa() actually works" >&5 if test "$cross_compiling" = yes; then INET_NTOA_RESULT="broken" else cat > conftest.$ac_ext < @@ -5079,7 +5071,7 @@ main () } EOF -if { (eval echo configure:5083: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +if { (eval echo configure:5075: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null then INET_NTOA_RESULT=`cat conftestval` else @@ -5105,9 +5097,9 @@ fi if test "$ac_cv_header_sys_statvfs_h" = "yes" ; then echo $ac_n "checking for working statvfs() interface""... $ac_c" 1>&6 -echo "configure:5109: checking for working statvfs() interface" >&5 +echo "configure:5101: checking for working statvfs() interface" >&5 cat > conftest.$ac_ext < @@ -5124,7 +5116,7 @@ statvfs("/tmp", &sfs); ; return 0; } EOF -if { (eval echo configure:5128: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:5120: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_func_statvfs=yes else @@ -5144,12 +5136,12 @@ fi fi echo $ac_n "checking for _res.nsaddr_list""... $ac_c" 1>&6 -echo "configure:5148: checking for _res.nsaddr_list" >&5 +echo "configure:5140: checking for _res.nsaddr_list" >&5 if eval "test \"`echo '$''{'ac_cv_have_res_nsaddr_list'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:5168: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_have_res_nsaddr_list="yes" else @@ -5194,12 +5186,12 @@ fi if test $ac_cv_have_res_nsaddr_list = "no" ; then echo $ac_n "checking for _res.ns_list""... $ac_c" 1>&6 -echo "configure:5198: checking for _res.ns_list" >&5 +echo "configure:5190: checking for _res.ns_list" >&5 if eval "test \"`echo '$''{'ac_cv_have_res_ns_list'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:5218: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_have_res_ns_list="yes" else @@ -5375,6 +5367,11 @@ trap 'rm -fr `echo "\ ./scripts/RunCache \ ./scripts/RunAccel \ ./src/Makefile \ + ./src/fs/Makefile \ + ./src/fs/ufs/Makefile \ + ./src/fs/aufs/Makefile \ + ./src/fs/coss/Makefile \ + ./src/fs/diskd/Makefile \ ./contrib/Makefile \ $SNMP_MAKEFILE \ ./icons/Makefile \ @@ -5430,8 +5427,9 @@ s%@CACHE_ICP_PORT@%$CACHE_ICP_PORT%g s%@CC@%$CC%g s%@LIBDLMALLOC@%$LIBDLMALLOC%g s%@LIB_MALLOC@%$LIB_MALLOC%g -s%@ASYNC_OBJS@%$ASYNC_OBJS%g s%@SQUID_PTHREAD_LIB@%$SQUID_PTHREAD_LIB%g +s%@STORE_MODULES@%$STORE_MODULES%g +s%@STORE_OBJS@%$STORE_OBJS%g s%@DELAY_OBJS@%$DELAY_OBJS%g s%@SNMPLIB@%$SNMPLIB%g s%@SNMP_OBJS@%$SNMP_OBJS%g @@ -5440,8 +5438,6 @@ s%@HTCP_OBJS@%$HTCP_OBJS%g s%@ERR_LANGUAGE@%$ERR_LANGUAGE%g s%@LEAKFINDER_OBJS@%$LEAKFINDER_OBJS%g s%@AUTH_MODULES@%$AUTH_MODULES%g -s%@OPT_DISKD_EXE@%$OPT_DISKD_EXE%g -s%@DISKD_OBJS@%$DISKD_OBJS%g s%@CPP@%$CPP%g s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g s%@INSTALL_SCRIPT@%$INSTALL_SCRIPT%g @@ -5516,6 +5512,11 @@ CONFIG_FILES=\${CONFIG_FILES-"\ ./scripts/RunCache \ ./scripts/RunAccel \ ./src/Makefile \ + ./src/fs/Makefile \ + ./src/fs/ufs/Makefile \ + ./src/fs/aufs/Makefile \ + ./src/fs/coss/Makefile \ + ./src/fs/diskd/Makefile \ ./contrib/Makefile \ $SNMP_MAKEFILE \ ./icons/Makefile \ diff --git a/configure.in b/configure.in index b67ccdabeb..9557ba3e14 100644 --- a/configure.in +++ b/configure.in @@ -3,13 +3,13 @@ dnl Configuration input file for Squid dnl dnl Duane Wessels, wessels@nlanr.net, February 1996 (autoconf v2.9) dnl -dnl $Id: configure.in,v 1.185 2000/05/02 19:22:00 hno Exp $ +dnl $Id: configure.in,v 1.186 2000/05/03 17:15:39 adrian Exp $ dnl dnl dnl AC_INIT(src/main.c) AC_CONFIG_HEADER(include/autoconf.h) -AC_REVISION($Revision: 1.185 $)dnl +AC_REVISION($Revision: 1.186 $)dnl AC_PREFIX_DEFAULT(/usr/local/squid) AC_CONFIG_AUX_DIR(cfgaux) @@ -236,46 +236,90 @@ AC_ARG_ENABLE(carp, fi ]) -AC_ARG_ENABLE(async_io, +AC_ARG_ENABLE(async-io, [ --enable-async-io[=N_THREADS] - Do ASYNC disk I/O using threads. - N_THREADS is the number of worker threads - defaults to 16. See also src/squid.h for - some additional platform tuning], -[ case "$enableval" in + Shorthand for + --with-aio-threads=N_THREADS + --with-pthreads + --enable-storeio=ufs,aufs], +[ case $enableval in yes) - async_io=yes + with_pthreads=yes + STORE_MODULES="ufs aufs" ;; no) - async_io='' ;; *) - async_io=yes - AC_DEFINE_UNQUOTED(NUMTHREADS, $enableval) + async_io_threads=$enableval + with_pthreads=yes + STORE_MODULES="ufs aufs" ;; esac ]) -if test -n "$async_io" ; then - echo "Async I/O enabled" - async_io=yes - AC_DEFINE(USE_ASYNC_IO) - ASYNC_OBJS='$(ASYNC_OBJS)' +AC_ARG_WITH(aio-threads, +[ --with-aio-threads=N_THREADS + Tune the number of worker threads for the aufs object + store.], +[ async_io_threads=$withval ]) +if test "$async_io_threads"; then + echo "With $async_io_threads AIO threads" + with_pthreads=yes + AC_DEFINE_UNQUOTED(ASYNC_IO_THREADS,$async_io_threads) +fi + +AC_ARG_WITH(pthreads, +[ --with-pthreads Use POSIX Threads], +[ if test "$enableval" = "yes"; then + with_pthreads=yes + fi +]) +if test "$with_pthreads"; then + echo "With pthreads" SQUID_PTHREAD_LIB='$(PTHREADLIB)' CFLAGS="$CFLAGS -D_REENTRANT" case "$host" in i386-unknown-freebsd*) - if test "$GCC" = "yes" ; then - if test -z "$PRESET_LDFLAGS"; then - LDFLAGS="$LDFLAGS -pthread" - fi - fi - ;; + if test "$GCC" = "yes" ; then + if test -z "$PRESET_LDFLAGS"; then + LDFLAGS="$LDFLAGS -pthread" + fi + fi + ;; esac fi -AC_SUBST(ASYNC_OBJS) AC_SUBST(SQUID_PTHREAD_LIB) +AC_ARG_ENABLE(storeio, +[ --enable-storeio=\"list of modules\" + Build support for the list of store I/O modules. + The default is only to build the "ufs" module. + See src/fs for a list of available modules, or + Programmers Guide section + for details on how to build your custom store module], +[ case $enableval in + yes) + for module in $srcdir/src/fs/*; do + if test -f $module/Makefile.in; then + STORE_MODULES="$STORE_MODULES `basename $module`" + fi + done + ;; + no) + ;; + *) STORE_MODULES="`echo $enableval| sed -e 's/,/ /g;s/ */ /g'`" + ;; + esac +], +[ if test -z "$STORE_MODULES"; then + STORE_MODULES="ufs" + fi +]) +echo "Store moules built: $STORE_MODULES" +AC_SUBST(STORE_MODULES) +STORE_OBJS="fs/`echo $STORE_MODULES|sed -e's% %.a fs/%g'`.a" +AC_SUBST(STORE_OBJS) + AC_ARG_ENABLE(icmp, [ --enable-icmp Enable ICMP pinging], [ if test "$enableval" = "yes" ; then @@ -428,6 +472,15 @@ AC_ARG_ENABLE(err-language, ],[ERR_LANGUAGE="English"]) AC_SUBST(ERR_LANGUAGE) +dnl Size of COSS memory buffer +AC_ARG_WITH(coss-membuf-size, +[ --with-coss-membuf-size COSS membuf size (default 1048576 bytes) ], +[ if test "$with_coss_membuf_size"; then + echo "Setting COSS membuf size to $with_coss_membuf_size bytes" + AC_DEFINE_UNQUOTED(COSS_MEMBUF_SZ, $with_coss_membuf_size) + fi +]) + dnl Enable poll() AC_ARG_ENABLE(poll, [ --enable-poll Enable poll() instead of select(). Normally poll @@ -546,8 +599,7 @@ AC_ARG_ENABLE(heap-replacement, [ --enable-heap-replacement This option allows you to use various cache replacement algorithms, instead of the standard - LRU algorithm. - ], + LRU algorithm.], [ if test "$enableval" = "yes" ; then echo "Enabling HEAP_REPLACEMENT" AC_DEFINE(HEAP_REPLACEMENT, 1) @@ -581,20 +633,6 @@ if test -n "$AUTH_MODULES"; then fi AC_SUBST(AUTH_MODULES) -dnl Enable "diskd" code -AC_ARG_ENABLE(diskd, -[ --enable-diskd Uses shared memory, message queues, and external - processes for disk I/O.], -[ if test "$enableval" = "yes" ; then - echo "DISKD enabled" - AC_DEFINE(USE_DISKD, 1) - OPT_DISKD_EXE='$(DISKD_EXE)' - AC_SUBST(OPT_DISKD_EXE) - DISKD_OBJS='diskd.o' - AC_SUBST(DISKD_OBJS) - fi -]) - # Force some compilers to use ANSI features # case "$host" in @@ -1022,6 +1060,9 @@ AC_CHECK_FUNCS(\ mktime \ mstats \ poll \ + pthread_attr_setscope \ + pthread_setschedparam \ + pthread_attr_setschedparam \ putenv \ random \ regcomp \ @@ -1046,14 +1087,6 @@ AC_CHECK_FUNCS(\ vsnprintf \ ) -if test "$async_io" = "yes" ; then - AC_CHECK_FUNCS(\ - pthread_attr_setscope \ - pthread_setschedparam \ - pthread_attr_setschedparam \ - ) -fi - dnl Yay! Another Linux brokenness. Its not good enough dnl to know that setresuid() exists, because RedHat 5.0 declares dnl setresuid() but doesn't implement it. @@ -1506,6 +1539,11 @@ AC_OUTPUT(\ ./scripts/RunCache \ ./scripts/RunAccel \ ./src/Makefile \ + ./src/fs/Makefile \ + ./src/fs/ufs/Makefile \ + ./src/fs/aufs/Makefile \ + ./src/fs/coss/Makefile \ + ./src/fs/diskd/Makefile \ ./contrib/Makefile \ $SNMP_MAKEFILE \ ./icons/Makefile \ diff --git a/doc/Programming-Guide/prog-guide.sgml b/doc/Programming-Guide/prog-guide.sgml index 9757040ed4..fd90426900 100644 --- a/doc/Programming-Guide/prog-guide.sgml +++ b/doc/Programming-Guide/prog-guide.sgml @@ -2,7 +2,7 @@
Squid Programmers Guide Duane Wessels, Squid Developers -$Id: prog-guide.sgml,v 1.26 2000/04/27 07:58:09 adrian Exp $ +$Id: prog-guide.sgml,v 1.27 2000/05/03 17:15:40 adrian Exp $ Squid is a WWW Cache application developed by the National Laboratory @@ -106,22 +106,22 @@ Squid consists of the following major components Squid can quickly locate cached objects because it keeps (in memory) a hash table of all - Objects are saved to disk in a two-level directory structure. - For each object the Client-side requests register themselves with a Processing Client Requests +

+ To be written... + Storage Manager - -Filesystem Interface +Introduction + +

+ The Storage Manager is the glue between client and server + sides. Every object saved in the cache is allocated a + + Squid can quickly locate cached objects because it keeps + (in memory) a hash table of all + For each object the + Client-side requests register themselves with a Object storage + +

+ To be written... + +Object retreival + +

+ To be written... + + +Storage Interface Introduction @@ -673,134 +723,528 @@ Squid consists of the following major components We want to try out our own, customized filesystems with Squid. In order to do that, we need a well-defined interface for the bits of Squid that access the permanent storage - devices. + devices. We also require tigher control of the replacement + policy by each storage module, rather than a single global + replacement policy. + +Build structure + +

+ The storage types live in squid/src/fs/ . Each subdirectory corresponds + to the name of the storage type. When a new storage type is implemented + configure.in must be updated to autogenerate a Makefile in + squid/src/fs/$type/ from a Makefile.in file. -The Interface +

+ configure will take a list of storage types through the + Data Structures +

+ Each storage type must create an archive file + + Each storefs must export a function named - Every cache object that is ``opened'' for reading or writing - will have a - struct _storeIOState { - sfileno swap_file_number; - mode_t mode; - size_t st_size; /* do stat(2) after read open */ - off_t offset; /* current offset pointer */ - STIOCB *callback; - void *callback_data; - struct { - STRCB *callback; - void *callback_data; - } read; - struct { - unsigned int closing:1; /* debugging aid */ - } flags; - union { - struct { - int fd; - struct { - unsigned int close_request:1; - unsigned int reading:1; - unsigned int writing:1; - } flags; - } ufs; - } type; - }; + /* automatically generated by ./store_modules.sh ufs coss + * do not edit + */ + #include "squid.h" + + extern STSETUP storeFsSetup_ufs; + extern STSETUP storeFsSetup_coss; + void storeFsSetup(void) + { + storeFsAdd("ufs", storeFsSetup_ufs); + storeFsAdd("coss", storeFsSetup_coss); + } - Initialisation of a storage type + +

+ Each storage type initialises through the + void + storeFsSetup_ufs(storefs_entry_t *storefs) + { + assert(!ufs_initialised); + storefs->parsefunc = storeUfsDirParse; + storefs->reconfigurefunc = storeUfsDirReconfigure; + storefs->donefunc = storeUfsDirDone; + ufs_state_pool = memPoolCreate("UFS IO State data", sizeof(ufsstate_t)); + ufs_initialised = 1; + } + + +

+ There are five function pointers in the storefs_entry which require + initialising. In this example, some protection is made against the + setup function being called twice, and a memory pool is initialised + for use inside the storage module. - + Each function will be covered below. - done - The second, + + typedef void + STFSSHUTDOWN(void); + - The ugly union is used to hold filesystem-specific state - information. +

+ This function is called whenever the storage system is to be shut down. + It should take care of deallocating any resources currently allocated. - External Functions + + typedef void STFSPARSE(SwapDir *SD, int index, char *path); + typedef void STFSRECONFIGURE(SwapDir *SD, int index, char *path); + -Object I/O +

+ These functions handle configuring and reconfiguring a storage + directory. Additional arguments from the cache_dir configuration + line can be retrieved through calls to strtok() and GetInteger().

- These functions all relate to per-object I/O tasks: opening, - closing, reading, writing, and unlinking objects on disk. - These functions can all be found in - Note that the underlying storage system functions are - accessed through function pointers, kept in the - - struct _SwapDir { - .... - struct { - STOBJOPEN *open; - STOBJCLOSE *close; - STOBJREAD *read; - STOBJWRITE *write; - STOBJUNLINK *unlink; - } obj; - .... - }; + struct _SwapDir { + char *type; /* Pointer to the store dir type string */ + int cur_size; /* Current swapsize in kb */ + int low_size; /* ?? */ + int max_size; /* Maximum swapsize in kb */ + char *path; /* Path to store */ + int index; /* This entry's index into the swapDir array */ + int suggest; /* Suggestion for UFS style stores (??) */ + size_t max_objsize; /* Maximum object size for this store */ + union { /* Replacement policy-specific fields */ + #ifdef HEAP_REPLACEMENT + struct { + heap *heap; + } heap; + #endif + struct { + dlink_list list; + dlink_node *walker; + } lru; + } repl; + int removals; + int scanned; + struct { + unsigned int selected:1; /* Currently selected for write */ + unsigned int read_only:1; /* This store is read only */ + } flags; + STINIT *init; /* Initialise the fs */ + STNEWFS *newfs; /* Create a new fs */ + STDUMP *dump; /* Dump fs config snippet */ + STFREE *freefs; /* Free the fs data */ + STDBLCHECK *dblcheck; /* Double check the obj integrity */ + STSTATFS *statfs; /* Dump fs statistics */ + STMAINTAINFS *maintainfs; /* Replacement maintainence */ + STCHECKOBJ *checkob; /* Check if the fs will store an object, and get the FS load */ + /* These two are notifications */ + STREFOBJ *refobj; /* Reference this object */ + STUNREFOBJ *unrefobj; /* Unreference this object */ + STCALLBACK *callback; /* Handle pending callbacks */ + STSYNC *sync; /* Sync the directory */ + struct { + STOBJCREATE *create; /* Create a new object */ + STOBJOPEN *open; /* Open an existing object */ + STOBJCLOSE *close; /* Close an open object */ + STOBJREAD *read; /* Read from an open object */ + STOBJWRITE *write; /* Write to a created object */ + STOBJUNLINK *unlink; /* Remove the given object */ + } obj; + struct { + STLOGOPEN *open; /* Open the log */ + STLOGCLOSE *close; /* Close the log */ + STLOGWRITE *write; /* Write to the log */ + struct { + STLOGCLEANOPEN *open; /* Open a clean log */ + STLOGCLEANWRITE *write; /* Write to the log */ + void *state; /* Current state */ + } clean; + } log; + void *fsdata; /* FS-specific data */ + }; + + + + +Operation of a storage module + +

+ Squid understands the concept of multiple diverse storage directories. + Each storage directory provides a caching object store, with object + storage, retrieval, indexing and replacement. + +

+ Each open object has associated with it a + + struct _storeIOState { + sdirno swap_dirn; /* SwapDir index */ + sfileno swap_filen; /* Unique file index number */ + StoreEntry *e; /* Pointer to parent StoreEntry */ + mode_t mode; /* Mode - O_RDONLY or O_WRONLY */ + size_t st_size; /* Size of the object if known */ + off_t offset; /* current _on-disk_ offset pointer */ + STFNCB *file_callback; /* called on delayed sfileno assignments */ + STIOCB *callback; /* IO Error handler callback */ + void *callback_data; /* IO Error handler callback data */ + struct { + STRCB *callback; /* Read completion callback */ + void *callback_data; /* Read complation callback data */ + } read; + struct { + unsigned int closing:1; /* debugging aid */ + } flags; + void *fsstate; /* pointer to private fs state */ + }; + + +

+ Each + The specific filesystem operations listed in the SwapDir object are + covered below. + +initfs + +

+ + typedef void + STINIT(SwapDir *SD); + + +

+ Initialise the given newfs + +

+ + typedef void + STNEWFS(SwapDir *SD); + + +

+ Called for each configured dumpfs + +

+ + typedef void + STDUMP(StoreEntry *e, const char *path, SwapDir *SD); + + +

+ Dump the configuration of the current freefs + +

+ + typedef void + STFREE(SwapDir *SD); + + +

+ Free the fsdata/. + + +doublecheckfs + +

+ + typedef int + STDBLCHECK(SwapDir *SD, StoreEntry *e); + + +

+ Double-check the given object for validity. Called during rebuild if + the '-S' flag is given to squid on the command line. Returns 1 if the + object is indeed valid, and 0 if the object is found invalid. + + +statfs + +

+ + typedef void + STSTATFS(SwapDir *SD, StoreEntry *e); + + +

+ Called to retrieve filesystem statistics, such as usage, load and + errors. The information should be appended to the passed + maintainfs + +

+ + typedef void + STMAINTAINFS(SwapDir *SD); + + +

+ Called periodically to replace objects. The active replacement policy + should be used to timeout unused objects in order to make room for + new objects. + +callback + +

+ + typedef void + STCALLBACK(SwapDir *SD); + + +

+ This function is called inside the comm_select/comm_poll loop to handle + any callbacks pending. + + +sync + +

+ + typedef void + STSYNC(SwapDir *SD); + + +

+ This function is called whenever a sync to disk is required. This + function should not return until all pending data has been flushed to + disk. + + +parse/reconfigure + +

+ +checkobj + +

+ + typedef int + STCHECKOBJ(SwapDir *SD, const StoreEntry *e); + + +

+ Called by referenceobj + +

+ + typedef void + STREFOBJ(SwapDir *SD, StoreEntry *e); + + +

+ Called whenever an object is locked by unreferenceobj + +

+ + typedef void + STUNREFOBJ(SwapDir *SD, StoreEntry *e); + + +

+ Called whenever the object is unlocked by createobj + +

+ + typedef storeIOState * + STOBJCREATE(SwapDir *SD, StoreEntry *e, STFNCB *file_callback, STIOCB *io_callback, void *io_callback_data); + + +

+ Create an object in the + The IO callback should be called when an error occurs and when the + object is closed. Once the IO callback is called, the + openobj + +

+ + typedef storeIOState * + STOBJOPEN(SwapDir *SD, StoreEntry *e, STFNCB *file_callback, STIOCB *io_callback, void *io_callback_data); + + +

+ Open the closeobj + +

+ + typedef void + STOBJCLOSE(SwapDir *SD, storeIOState *sio); + + +

+ Close an opened object. The readobj + +

+ + typedef void + STOBJREAD(SwapDir *SD, storeIOState *sio, char *buf, size_t size, off_t offset, STRCB *read_callback, void *read_callback_data);

- Thus, a storage system must do something like this - when initializing its + If a read operation fails, the filesystem layer notifies the + calling module by calling the writeobj + +

+ + typedef void + STOBJWRITE(SwapDir *SD, storeIOState *sio, char *buf, size_t size, off_t offset, FREE *freefunc); + + +

+ Write the given block of data to the given store object. + If a write operation fails, the filesystem layer notifies the + calling module by calling the unlinkobj + +

- SwapDir->obj.open = storeFooOpen; - SwapDir->obj.close = storeFooClose; - SwapDir->obj.read = storeFooRead; - SwapDir->obj.write = storeFooWrite; - SwapDir->obj.unlink = storeFooUnlink; + typedef void STOBJUNLINK(SwapDir *, StoreEntry *); - + Remove the Store IO calls + +

+ These routines are used inside the storage manager to create and + retrieve objects from a storage directory. + +storeCreate()

storeIOState * - storeOpen(sfileno f, mode_t mode, STIOCB *callback, void *callback_data) + storeCreate(StoreEntry *e, STIOCB *file_callback, STIOCB *close_callback, void * callback_data)

- - storeOpen()

- void - storeClose(storeIOState *sio) + storeIOState * + storeOpen(StoreEntry *e, STFNCB * file_callback, STIOCB * callback, void *callback_data)

- + + storeRead() + +

void storeRead(storeIOState *sio, char *buf, size_t size, off_t offset, STRCB *callback, void *callback_data) @@ -842,7 +1297,7 @@ Squid consists of the following major components

- The caller is responsible for allocating and freeing +storeWrite() + +

void storeWrite(storeIOState *sio, char *buf, size_t size, off_t offset, FREE *free_func) @@ -862,7 +1318,7 @@ Squid consists of the following major components

+storeUnlink() + +

- void - storeUnlink(sfileno f) + void + storeUnlink(StoreEntry *e)

@@ -888,21 +1345,28 @@ Squid consists of the following major components does not need to be opened first. The filesystem layer will remove the object if it exists on the disk. - +storeOffset() + +

- off_t - storeOffset(storeIOState *sio) + off_t storeOffset(storeIOState *sio) + +

- Returns the current byte-offset of the cache object on - disk. For read-objects, this is the offset after the last - successful disk read operation. For write-objects, it is - the offset of the last successful disk write operation. + offset/ refers to the ondisk offset, or undefined + results will occur. For reads, this returns the current offset of + sucessfully read data, not including queued reads. + -Callbacks + + @@ -929,7 +1393,7 @@ Squid consists of the following major components Once the The @@ -946,43 +1410,11 @@ Squid consists of the following major components called if the read operation is successful. If it fails, then the Config file parsing - -

- There are three functions relating to the Squid configuration - file: parsing, dumping, and freeing. - -

- The parse function is called at startup, and during a reconfigure, - for a - The ``dump'' function is used to output a configuration - file from the in-memory configuration structure. It is - called with a - The free function is called during a reconfigure (and at - exit) to free up (or un-initialize) any memory or structures - associated with the configuration line. The Filesystem Startup, Initialization, and State Logging +State Logging

- These functions deal with initializing, state - logging, and related tasks for a squid storage system. + These functions deal with state + logging and related tasks for a squid storage system. These functions are used (called) in @@ -1010,36 +1442,7 @@ Squid consists of the following major components }; - - - void - STINIT(SwapDir *); - - -

- The - - void - STNEWFS(SwapDir *); - - -

- The @@ -1060,7 +1463,7 @@ Squid consists of the following major components the state log and then re-opens them. A @@ -1074,7 +1477,7 @@ Squid consists of the following major components the open state-holding log files (if any) for the storage system. - @@ -1089,7 +1492,7 @@ Squid consists of the following major components This feature may not be required by some storage systems and can be implemented as a null-function (no-op). - @@ -1111,7 +1514,7 @@ Squid consists of the following major components keep state information for the clean-writing process, but should not be accessed by upper layers. - @@ -1130,9 +1533,301 @@ Squid consists of the following major components to become the official state-holding log. +Replacement policy implementation + +

+The replacement policy can be updated during STOBJREAD/STOBJWRITE/STOBJOPEN/ +STOBJCLOSE as well as STREFOBJ and STUNREFOBJ. Care should be taken to +only modify the relevant replacement policy entries in the StoreEntry. +The responsibility of replacement policy maintainence has been moved into +each SwapDir so that the storage code can have tight control of the +replacement policy. Cyclic filesystems such as COSS require this tight +coupling between the storage layer and the replacement policy. + + +Future removal policy + +

+ (replaces the above Replace policy) + +

+ The removal policy is responsible for determining in which order + objects are deleted when Squid needs to reclaim space for new objects. + Such a policy is used by a object storage for maintaining the stored + objects and determining what to remove to reclaim space for new objects. + (together they implements a replacement policy) + +API +

+ It is implemented as a modular API where a storage directory or + memory creates a policy of choice for maintaining it's objects, + and modules registerering to be used by this API. + +createRemovalPolicy() + +

+ + RemovalPolicy policy = createRemovalPolicy(cons char *type, cons char *args) + + +

+ Creates a removal policy instance where object priority can be + maintained + +

+ The returned RemovalPolicy instance is cbdata registered + +policy.Free() + +

+ + policy->Free(RemovalPolicy *policy) + + +

+ Destroys the policy instance and frees all related memory. + +policy.Add() + +

+ + policy->Add(RemovalPolicy *policy, StoreEntry *, RemovalPolicyNode *node) + + +

+ Adds a StoreEntry to the policy instance. + +

+ datap is a pointer to where policy specific data can be stored + for the store entry, currently the size of one (void *) pointer. + +policy.Remove() + +

+ + policy->Remove(RemovalPolicy *policy, StoreEntry *, RemovalPolicyNode *node) + + +

+ Removes a StoreEntry from the policy instance out of + policy order. For example when an object is replaced + by a newer one or is manually purged from the store. + +

+ datap is a pointer to where policy specific data is stored + for the store entry, currently the size of one (void *) pointer. + +policy.Referenced() + +

+ + policy->Referenced(RemovalPolicy *policy, const StoreEntry *, RemovalPolicyNode *node) + + +

+ Tells the policy that a StoreEntry has been referenced. + +

+ datap is a pointer to where policy specific data is stored + for the store entry, currently the size of one (void *) pointer. + +policy.WalkInit() + +

+ + RemovalPolicyWalker walker = policy->WalkInit(RemovalPolicy *policy) + + +

+ Initiates a walk of all objects in the policy instance. + The objects is returned in an order suitable for using + as reinsertion order when rebuilding the policy. + +

+ The returned RemovalPolicyWalker instance is cbdata registered + +

+ Note: The walk must be performed as an atomic operation + with no other policy actions interveaning, or the outcome + will be undefined. + +walker.Next() + +

+ + const StoreEntry *entry = walker->Next(RemovalPolicyWalker *walker) + + +

+ Gets the next object in the walk chain + +

+ Return NULL when there is no further objects + +walker.Done() + +

+ + walker->Done(RemovalPolicyWalker *walker) + + +

+ Finishes a walk of the maintaned objects, destroys + walker. + +policy.PurgeInit() + +

+ + RemovalPurgeWalker purgewalker = policy->PurgeInit(RemovalPolicy *policy, int max_scan) + + +

+ Initiates a search for removal candidates. Seach depth is indicated + by max_scan. + +

+ The returned RemovalPurgeWalker instance is cbdata registered + +

+ Note: The walk must be performed as an atomic operation + with no other policy actions interveaning, or the outcome + will be undefined. + +purgewalker.Next() + +

+ + StoreEntry *entry = purgewalker->Next(RemovalPurgeWalker *purgewalker) + + +

+ Gets the next object to purge. The purgewalker will remove each + returned object from the policy. + +

It is the policys responsibility to verify that the object + isn't locked or otherwise prevented from being removed. What this + means is that the policy must not return objects where + storeEntryLocked() is true. + +

+ Return NULL when there is no further purgeable objects in the policy. + +purgewalker.Done() + +

+ + purgewalker->Done(RemovalPurgeWalker *purgewalker) + + +

+ Finishes a walk of the maintaned objects, destroys + walker and restores the policy to it's normal state. + +Future removal policy implementation + +Source layout + +

+ Policy implementations resides in src/repl/<name>/, and a make in + such a directory must result in a object archive src/repl/<name>.a + containing all the objects implementing the policy. + +Internal structures + +RemovalPolicy + +

+ + typedef struct _RemovalPolicy RemovalPolicy; + struct _RemovalPolicy { + char *_type; + void *_data; + void (*add)(RemovalPolicy *policy, StoreEntry *); + ... /* see the API definition above */ + }; + + +

+ The _type member is mainly for debugging and diagnostics purposes, and + should be a pointer to the name of the policy (same name as used for + creation) + +

+ The _data member is for storing policy specific information. + +RemovalPolicyWalker + +

+ + typedef struct _RemovalPolicyWalker RemovalPolicyWalker; + struct _RemovalPolicyWalker { + RemovalPolicy *_policy; + void *_data; + StoreEntry *(*next)(RemovalPolicyWalker *); + ... /* see the API definition above */ + }; + + +RemovalPolicyNode + +

+ + typedef struct _RemovalPolicyNode RemovalPolicyNode; + struct _RemovalPolicyNode { + void *data; + }; + + + Stores policy specific information about a entry. Currently + there is only space for a single pointer, but plans are to + maybe later provide more space here to allow simple policies + to store all their data "inline" to preserve some memory. + +Policy registration + +

+ Policies are automatically registered in the Squid binary from the + policy selection made by the user building Squid. In the future this + might get extended to support loadable modules. All registered + policies are available to object stores which wishes to use them. + +Policy instance creation + +

+ Each policy must implement a "create/new" function " + It should also populate the _data member with a pointer to policy + specific data. + +

+ Prior to returning the created instance must be registered as + callback-data by calling cbdataAdd(). + +Walker + +

+ When a walker is created the policy populates it with at least the API + methods supported. Currently all API calls are mandatory, but the + policy implementation must make sure to NULL fill the structure prior + to populating it in order to assure future API compability. + +

+ Prior to returning the created instance must be registered as + callback-data by calling cbdataAdd(). + Forwarding Selection +

+ To be written... + IP Cache and FQDN Cache @@ -1217,30 +1912,72 @@ Squid consists of the following major components Server Protocols HTTP + +

+ To be written... + FTP + +

+ To be written... + Gopher + +

+ To be written... + Wais + +

+ To be written... + SSL + +

+ To be written... + Passthrough +

+ To be written... + Timeouts +

+ To be written... + Events +

+ To be written... + Access Controls +

+ To be written... + ICP +

+ To be written... + Network Measurement Database +

+ To be written... + Error Pages +

+ To be written... + + Callback Data Database

@@ -1309,6 +2046,9 @@ Squid consists of the following major components Cache Manager +

+ To be written... + HTTP Headers diff --git a/include/autoconf.h.in b/include/autoconf.h.in index 5b9df6b245..ea7b51c539 100644 --- a/include/autoconf.h.in +++ b/include/autoconf.h.in @@ -67,11 +67,8 @@ #undef FORW_VIA_DB -/* Define to use async disk I/O operations */ -#undef USE_ASYNC_IO - /* Defines how many threads to use for async I/O */ -#undef NUMTHREADS +#undef ASYNC_IO_THREADS /* * If you want to use Squid's ICMP features (highly recommended!) then @@ -280,16 +277,14 @@ */ #undef HEAP_REPLACEMENT -/* - * Use DISKD - */ -#undef USE_DISKD - /* * message type for message queues */ #undef mtyp_t +/* Define if you want to set the COSS membuf size */ +#undef COSS_MEMBUF_SZ + /* The number of bytes in a int. */ #undef SIZEOF_INT @@ -413,6 +408,9 @@ /* Define if you have the srandom function. */ #undef HAVE_SRANDOM +/* Define if you have the statfs function. */ +#undef HAVE_STATFS + /* Define if you have the strerror function. */ #undef HAVE_STRERROR @@ -569,12 +567,12 @@ /* Define if you have the header file. */ #undef HAVE_SYS_IOCTL_H -/* Define if you have the header file. */ -#undef HAVE_SYS_MSG_H - /* Define if you have the header file. */ #undef HAVE_SYS_MOUNT_H +/* Define if you have the header file. */ +#undef HAVE_SYS_MSG_H + /* Define if you have the header file. */ #undef HAVE_SYS_NDIR_H diff --git a/src/Makefile.in b/src/Makefile.in index 885c2785f8..0f3c9b13e4 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -1,7 +1,7 @@ # # Makefile for the Squid Object Cache server # -# $Id: Makefile.in,v 1.184 2000/05/02 21:12:42 hno Exp $ +# $Id: Makefile.in,v 1.185 2000/05/03 17:15:40 adrian Exp $ # # Uncomment and customize the following to suit your needs: # @@ -18,6 +18,8 @@ localstatedir = @localstatedir@ srcdir = @srcdir@ VPATH = @srcdir@ +SUBDIRS = fs + # Gotta love the DOS legacy # SQUID_EXE = squid$(exec_suffix) @@ -26,8 +28,6 @@ DNSSERVER_EXE = dnsserver$(exec_suffix) UNLINKD_EXE = unlinkd$(exec_suffix) PINGER_EXE = pinger$(exec_suffix) CACHEMGR_EXE = cachemgr$(cgi_suffix) -DISKD_EXE = diskd$(exec_suffix) -OPT_DISKD_EXE = @OPT_DISKD_EXE@ DEFAULT_PREFIX = $(prefix) DEFAULT_CONFIG_FILE = $(sysconfdir)/squid.conf @@ -62,6 +62,8 @@ AC_CFLAGS = @CFLAGS@ LDFLAGS = @LDFLAGS@ XTRA_LIBS = @XTRA_LIBS@ XTRA_OBJS = @XTRA_OBJS@ +STORE_OBJS = @STORE_OBJS@ +STORE_MODULES = @STORE_MODULES@ MV = @MV@ RM = @RM@ SHELL = /bin/sh @@ -77,14 +79,13 @@ PINGER_LIBS = -L../lib -lmiscutil $(XTRA_LIBS) STD_APP_LIBS = -L../lib -lmiscutil $(XTRA_LIBS) PROGS = $(SQUID_EXE) $(CLIENT_EXE) -UTILS = $(DNSSERVER_EXE) $(UNLINKD_EXE) $(OPT_DISKD_EXE) +UTILS = $(DNSSERVER_EXE) $(UNLINKD_EXE) $(DISKD_EXE) SUID_UTILS = $(PINGER_EXE) CGIPROGS = $(CACHEMGR_EXE) OBJS = \ access_log.o \ acl.o \ asn.o \ - @ASYNC_OBJS@ \ authenticate.o \ cache_cf.o \ CacheDigest.o \ @@ -156,18 +157,17 @@ OBJS = \ stmem.o \ store.o \ store_io.o \ - store_io_ufs.o \ - @DISKD_OBJS@ \ store_client.o \ store_digest.o \ store_dir.o \ - store_dir_ufs.o \ store_key_md5.o \ store_log.o \ + store_modules.o \ store_rebuild.o \ store_swapin.o \ store_swapmeta.o \ store_swapout.o \ + store_heap_replacement.o \ string_arrays.o \ tools.o \ unlinkd.o \ @@ -187,28 +187,23 @@ HTCP_OBJS = htcp.o DELAY_OBJS = delay_pools.o -ASYNC_OBJS = \ - aiops.o \ - async_io.o \ - store_io_asyncufs.o - LEAKFINDER_OBJS = \ leakfinder.o DEFAULTS = \ -DDEFAULT_CONFIG_FILE=\"$(DEFAULT_CONFIG_FILE)\" -all: squid.conf $(PROGS) $(UTILS) $(SUID_UTILS) $(CGIPROGS) +all: squid.conf store_modules $(PROGS) $(UTILS) $(SUID_UTILS) $(CGIPROGS) $(OBJS): $(top_srcdir)/include/version.h $(SNMP_OBJS): ../snmplib/libsnmp.a $(top_srcdir)/include/cache_snmp.h -$(SQUID_EXE): $(OBJS) - $(CC) -o $@ $(LDFLAGS) $(OBJS) $(SQUID_LIBS) +$(SQUID_EXE): $(OBJS) $(STORE_OBJS) + $(CC) -o $@ $(LDFLAGS) $(OBJS) $(STORE_OBJS) $(SQUID_LIBS) globals.o: globals.c Makefile - $(CC) -c $< $(CFLAGS) -I$(srcdir) $(DEFAULTS) + $(CC) -c globals.c $(CFLAGS) -I$(srcdir) $(DEFAULTS) globals.c: globals.h mk-globals-c.pl $(PERL) $(srcdir)/mk-globals-c.pl < $(srcdir)/globals.h > $@ @@ -222,12 +217,6 @@ $(CLIENT_EXE): client.o $(DNSSERVER_EXE): dnsserver.o $(CC) -o $@ $(LDFLAGS) dnsserver.o $(DNSSERVER_LIBS) -$(DISKD_EXE): diskd-daemon.o - $(CC) -o $@ $(LDFLAGS) diskd-daemon.o $(DISKD_LIBS) - -diskd-daemon.o: diskd.c - $(CC) -c $(CFLAGS) -DDISKD_DAEMON=1 $(srcdir)/diskd.c -o $@ - $(CACHEMGR_EXE): cachemgr.o $(CC) -o $@ $(LDFLAGS) cachemgr.o $(CLIENT_LIBS) @@ -276,6 +265,17 @@ cf.data: cf.data.pre Makefile s%@DEFAULT_PREFIX@%$(DEFAULT_PREFIX)%g;"\ < $(srcdir)/cf.data.pre >$@ +store_modules.c: store_modules.sh Makefile + @sh $(srcdir)/store_modules.sh $(STORE_MODULES) >store_modules.c + +store_modules.o: store_modules.c + $(CC) -c store_modules.c $(CFLAGS) -I$(srcdir) + +$(STORE_OBJS): fs/stamp + +store_modules fs/stamp: + @sh -c "cd fs && $(MAKE) all" + install-mkdirs: -@if test ! -d $(prefix); then \ echo "mkdir $(prefix)"; \ @@ -309,6 +309,10 @@ install-mkdirs: # Michael Lupp wants to know about additions # to the install target. install: all install-mkdirs + @for dir in $(SUBDIRS); do \ + echo "Making $@ in $$dir..."; \ + (cd $$dir ; $(MAKE) $(MFLAGS) prefix="$(prefix)" $@) || exit 1; \ + done @for f in $(PROGS); do \ if test -f $(bindir)/$$f; then \ echo $(MV) $(bindir)/$$f $(bindir)/-$$f; \ @@ -378,13 +382,23 @@ install-pinger: clean: -rm -rf *.o *pure_* core $(PROGS) $(UTILS) $(CGIPROGS) $(SUID_UTILS) -rm -f cf_gen cf_gen_defines.h cf_parser.c cf.data globals.c string_arrays.c + -rm -f store_modules.c + @for dir in $(SUBDIRS); do \ + echo "Making $@ in $$dir..."; \ + (cd $$dir ; $(MAKE) $(MFLAGS) prefix="$(prefix)" $@) || exit 1; \ + done distclean: clean -rm -f Makefile squid.conf squid.conf.pre -rm -f Makefile.bak + -rm -f tags + @for dir in $(SUBDIRS); do \ + echo "Making $@ in $$dir..."; \ + (cd $$dir ; $(MAKE) $(MFLAGS) prefix="$(prefix)" $@) || exit 1; \ + done tags: ctags *.[ch] ../include/*.h ../lib/*.[ch] -depend: - $(MAKEDEPEND) -I../include -I. -fMakefile *.c +depend: cf_parser.c + $(MAKEDEPEND) $(INCLUDE) -fMakefile $(srcdir)/*.c diff --git a/src/cache_cf.cc b/src/cache_cf.cc index d4c9e961c2..82a253e294 100644 --- a/src/cache_cf.cc +++ b/src/cache_cf.cc @@ -1,6 +1,6 @@ /* - * $Id: cache_cf.cc,v 1.342 2000/05/02 20:58:30 hno Exp $ + * $Id: cache_cf.cc,v 1.343 2000/05/03 17:15:41 adrian Exp $ * * DEBUG: section 3 Configuration File Parsing * AUTHOR: Harvest Derived @@ -58,6 +58,7 @@ static const char *const list_sep = ", \t\n\r"; static int http_header_first; static int http_header_allowed = 0; +static void update_maxobjsize(void); static void configDoConfigure(void); static void parse_refreshpattern(refresh_t **); static int parseTimeUnits(const char *unit); @@ -192,6 +193,19 @@ GetInteger(void) return i; } +static void +update_maxobjsize(void) +{ + int i; + size_t ms = -1; + + for (i = 0; i < Config.cacheSwap.n_configured; i++) { + if (Config.cacheSwap.swapDirs[i].max_objsize > ms) + ms = Config.cacheSwap.swapDirs[i].max_objsize; + } + store_maxobjsize = ms; +} + int parseConfigFile(const char *file_name) { @@ -346,9 +360,7 @@ configDoConfigure(void) #if USE_DNSSERVERS requirePathnameExists("cache_dns_program", Config.Program.dnsserver); #endif -#if USE_UNLINKD requirePathnameExists("unlinkd_program", Config.Program.unlinkd); -#endif if (Config.Program.redirect) requirePathnameExists("redirect_program", Config.Program.redirect->key); if (Config.Program.authenticate) @@ -802,16 +814,7 @@ dump_cachedir(StoreEntry * entry, const char *name, cacheSwap swap) int i; for (i = 0; i < swap.n_configured; i++) { s = swap.swapDirs + i; - switch (s->type) { - case SWAPDIR_UFS: - case SWAPDIR_ASYNCUFS: - storeUfsDirDump(entry, name, s); - break; - default: - debug(0, 0) ("dump_cachedir doesn't know about type %d\n", - (int) s->type); - break; - } + s->dump(entry, name, s); } } @@ -844,25 +847,84 @@ allocate_new_swapdir(cacheSwap * swap) } } +static int +find_fstype(char *type) +{ + int i; + for (i = 0; storefs_list[i].typestr != NULL; i++) { + if (strcasecmp(type, storefs_list[i].typestr) == 0) { + return i; + } + } + return (-1); +} + static void parse_cachedir(cacheSwap * swap) { char *type_str; + char *path_str; + SwapDir *sd; + int i; + int fs; + size_t maxobjsize; + if ((type_str = strtok(NULL, w_space)) == NULL) self_destruct(); - if (0 == strcasecmp(type_str, "ufs")) { - storeUfsDirParse(swap); -#if USE_ASYNC_IO - } else if (0 == strcasecmp(type_str, "asyncufs")) { - storeAufsDirParse(swap); -#endif -#if USE_DISKD - } else if (0 == strcasecmp(type_str, "diskd")) { - storeDiskdDirParse(swap); -#endif - } else { - fatalf("Unknown cache_dir type '%s'\n", type_str); + + maxobjsize = (size_t)GetInteger(); + + if ((path_str = strtok(NULL, w_space)) == NULL) + self_destruct(); + + /* + * This bit of code is a little strange. + * See, if we find a path and type match for a given line, then + * as long as we're reconfiguring, we can just call its reconfigure + * function. No harm there. + * + * Trouble is, if we find a path match, but not a type match, we have + * a dilemma - we could gracefully shut down the fs, kill it, and + * create a new one of a new type in its place, BUT at this stage the + * fs is meant to be the *NEW* one, and so things go very strange. :-) + * + * So, we'll assume the person isn't going to change the fs type for now, + * and XXX later on we will make sure that its picked up. + * + * (moving around cache_dir lines will be looked at later in a little + * more sane detail..) + */ + + for (i = 0; i < swap->n_configured; i++) { + if (0 == strcasecmp(path_str, swap->swapDirs[i].path)) { + /* This is a little weird, you'll appreciate it later */ + fs = find_fstype(type_str); + if (fs < 0) { + fatalf("Unknown cache_dir type '%s'\n", type_str); + } + sd = swap->swapDirs + i; + storefs_list[fs].reconfigurefunc(sd, i, path_str); + sd->max_objsize = maxobjsize; + update_maxobjsize(); + return; + } + } + + fs = find_fstype(type_str); + if (fs < 0) { + /* If we get here, we didn't find a matching cache_dir type */ + fatalf("Unknown cache_dir type '%s'\n", type_str); } + + allocate_new_swapdir(swap); + sd = swap->swapDirs + swap->n_configured; + storefs_list[fs].parsefunc(sd, swap->n_configured, path_str); + /* XXX should we dupe the string here, in case it gets trodden on? */ + sd->type = storefs_list[fs].typestr; + sd->max_objsize = maxobjsize; + swap->n_configured++; + /* Update the max object size */ + update_maxobjsize(); } static void @@ -875,18 +937,8 @@ free_cachedir(cacheSwap * swap) return; for (i = 0; i < swap->n_configured; i++) { s = swap->swapDirs + i; - switch (s->type) { - case SWAPDIR_UFS: - case SWAPDIR_ASYNCUFS: - storeUfsDirFree(s); - break; - default: - debug(0, 0) ("dump_cachedir doesn't know about type %d\n", - (int) s->type); - break; - } + s->freefs(s); xfree(s->path); - filemapFreeMemory(s->map); } safe_free(swap->swapDirs); swap->swapDirs = NULL; diff --git a/src/cf.data.pre b/src/cf.data.pre index 9389574d10..79d0f4bdd2 100644 --- a/src/cf.data.pre +++ b/src/cf.data.pre @@ -1,6 +1,6 @@ # -# $Id: cf.data.pre,v 1.178 2000/05/02 21:38:12 hno Exp $ +# $Id: cf.data.pre,v 1.179 2000/05/03 17:15:41 adrian Exp $ # # # SQUID Internet Object Cache http://squid.nlanr.net/Squid/ @@ -588,12 +588,12 @@ COMMENT_END NAME: cache_dir TYPE: cachedir DEFAULT: none -DEFAULT_IF_NONE: ufs @DEFAULT_SWAP_DIR@ 100 16 256 +DEFAULT_IF_NONE: ufs -1 @DEFAULT_SWAP_DIR@ 100 16 256 LOC: Config.cacheSwap DOC_START Usage: - cache_dir Type Directory-Name Mbytes Level-1 Level2 + cache_dir Type Maxobjsize Directory-Name Mbytes Level-1 Level2 You can specify multiple cache_dir lines to spread the cache among different disk partitions. @@ -604,6 +604,10 @@ DOC_START want to try "asyncufs" as the type. Async IO support may be buggy, however, so beware. + Maxobjsize refers to the max object size this storedir supports. + It is used to initially choose the storedir to dump the object. + -1 means 'any size'. + 'Directory' is a top-level directory where cache swap files will be stored. If you want to use an entire disk for caching, then this can be the mount-point directory. @@ -1872,6 +1876,9 @@ DOC_START default is `0' which disables sending the announcement messages. + To enable announcing your cache, just uncomment the line + below. + NOCOMMENT_START #To enable announcing your cache, just uncomment the line below. #announce_period 1 day diff --git a/src/comm_select.cc b/src/comm_select.cc index dcaeb0c714..5ec60483fc 100644 --- a/src/comm_select.cc +++ b/src/comm_select.cc @@ -1,6 +1,6 @@ /* - * $Id: comm_select.cc,v 1.41 2000/03/06 16:23:30 wessels Exp $ + * $Id: comm_select.cc,v 1.42 2000/05/03 17:15:41 adrian Exp $ * * DEBUG: section 5 Socket Functions * @@ -34,13 +34,7 @@ #include "squid.h" -#if USE_ASYNC_IO -#define MAX_POLL_TIME 10 -#elif USE_DISKD -#define MAX_POLL_TIME 10 -#else -#define MAX_POLL_TIME 1000 -#endif +static int MAX_POLL_TIME = 1000; /* see also comm_quick_poll_required() */ #ifndef howmany #define howmany(x, y) (((x)+((y)-1))/(y)) @@ -332,12 +326,8 @@ comm_poll(int msec) getCurrentTime(); start = current_dtime; #endif -#if USE_ASYNC_IO - aioCheckCallbacks(); -#endif -#if USE_DISKD - storeDiskdReadQueue(); -#endif + /* Handle any fs callbacks that need doing */ + storeDirCallback(); #if DELAY_POOLS FD_ZERO(&slowfds); #endif @@ -645,6 +635,7 @@ comm_select(int msec) fd_set slowfds; #endif PF *hdl = NULL; + SwapDir *SD; int fd; int maxfd; int num; @@ -662,19 +653,16 @@ comm_select(int msec) struct timeval poll_time; double timeout = current_dtime + (msec / 1000.0); fde *F; + int i; do { #if !ALARM_UPDATES_TIME getCurrentTime(); #endif -#if USE_ASYNC_IO - aioCheckCallbacks(); -#endif -#if USE_DISKD - storeDiskdReadQueue(); -#endif #if DELAY_POOLS FD_ZERO(&slowfds); #endif + /* Handle any fs callbacks that need doing */ + storeDirCallback(); if (commCheckICPIncoming) comm_select_icp_incoming(); if (commCheckDNSIncoming) @@ -1090,3 +1078,10 @@ commUpdateWriteBits(int fd, PF * handler) nwritefds--; } } + +/* Called by async-io or diskd to speed up the polling */ +void +comm_quick_poll_required(void) +{ + MAX_POLL_TIME = 10; +} diff --git a/src/defines.h b/src/defines.h index 9a68bd97f9..e1c1fcf113 100644 --- a/src/defines.h +++ b/src/defines.h @@ -1,6 +1,6 @@ /* - * $Id: defines.h,v 1.79 2000/05/02 20:58:30 hno Exp $ + * $Id: defines.h,v 1.80 2000/05/03 17:15:41 adrian Exp $ * * * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ @@ -147,7 +147,6 @@ #define LOG_DISABLE 0 #define SM_PAGE_SIZE 4096 -#define DISK_PAGE_SIZE 8192 #define EBIT_SET(flag, bit) ((void)((flag) |= ((1L<<(bit))))) #define EBIT_CLR(flag, bit) ((void)((flag) &= ~((1L<<(bit))))) @@ -270,15 +269,3 @@ #define _PATH_DEVNULL "/dev/null" #endif -#if USE_ASYNC_IO -#ifndef NUMTHREADS -#define NUMTHREADS 16 -#endif -#endif - -#if USE_ASYNC_IO -#undef USE_UNLINKD -#else -#define USE_UNLINKD 1 -#endif - diff --git a/src/disk.cc b/src/disk.cc index bdd225de24..9c1eaf28c9 100644 --- a/src/disk.cc +++ b/src/disk.cc @@ -1,6 +1,6 @@ /* - * $Id: disk.cc,v 1.147 2000/03/06 16:23:30 wessels Exp $ + * $Id: disk.cc,v 1.148 2000/05/03 17:15:41 adrian Exp $ * * DEBUG: section 6 Disk I/O Routines * AUTHOR: Harvest Derived @@ -187,6 +187,8 @@ diskHandleWrite(int fd, void *notused) debug(6, 3) ("diskHandleWrite: FD %d writing %d bytes\n", fd, (int) (fdd->write_q->len - fdd->write_q->buf_offset)); errno = 0; + if (fdd->write_q->file_offset != -1) + lseek(fd, fdd->write_q->file_offset, SEEK_SET); len = write(fd, fdd->write_q->buf + fdd->write_q->buf_offset, fdd->write_q->len - fdd->write_q->buf_offset); diff --git a/src/enums.h b/src/enums.h index 7c9038ba6c..301abb32e6 100644 --- a/src/enums.h +++ b/src/enums.h @@ -1,6 +1,6 @@ /* - * $Id: enums.h,v 1.166 2000/05/02 21:38:12 hno Exp $ + * $Id: enums.h,v 1.167 2000/05/03 17:15:41 adrian Exp $ * * * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ @@ -507,7 +507,6 @@ typedef enum { MEM_ACL_NAME_LIST, MEM_ACL_PROXY_AUTH_USER, MEM_ACL_TIME_DATA, - MEM_AIO_RESULT_T, MEM_CACHEMGR_PASSWD, #if USE_CACHE_DIGESTS MEM_CACHE_DIGEST, @@ -521,7 +520,6 @@ typedef enum { #if USE_CACHE_DIGESTS MEM_DIGEST_FETCH_STATE, #endif - MEM_DISK_BUF, MEM_DLINK_LIST, MEM_DLINK_NODE, MEM_DNSSERVER_T, @@ -660,12 +658,3 @@ enum { NETDB_EX_RTT, NETDB_EX_HOPS }; - -typedef enum { - SWAPDIR_UFS, - SWAPDIR_ASYNCUFS, -#if USE_DISKD - SWAPDIR_DISKD, -#endif - SWAPDIR_MAX -} swapdir_t; diff --git a/src/fd.cc b/src/fd.cc index 4995a40ff6..c2a2af85fa 100644 --- a/src/fd.cc +++ b/src/fd.cc @@ -1,6 +1,6 @@ /* - * $Id: fd.cc,v 1.38 2000/03/06 16:23:31 wessels Exp $ + * $Id: fd.cc,v 1.39 2000/05/03 17:15:42 adrian Exp $ * * DEBUG: section 51 Filedescriptor Functions * AUTHOR: Duane Wessels @@ -95,12 +95,6 @@ fd_open(int fd, unsigned int type, const char *desc) { fde *F = &fd_table[fd]; assert(fd >= 0); -#if USE_ASYNC_IO - if (F->flags.closing) { - /* Reuse of a closed FD before we have noticed it is closed */ - fd_close(fd); - } -#endif if (F->flags.open) { debug(51, 1) ("WARNING: Closing open FD %4d\n", fd); fd_close(fd); diff --git a/src/fs/Makefile.in b/src/fs/Makefile.in new file mode 100644 index 0000000000..31e7f86ad7 --- /dev/null +++ b/src/fs/Makefile.in @@ -0,0 +1,35 @@ +# Makefile for storage modules in the Squid Object Cache server +# +# $Id: Makefile.in,v 1.1 2000/05/03 17:15:46 adrian Exp $ +# + +SUBDIRS = @STORE_MODULES@ + +all: + @test -z "$(SUBDIRS)" || for dir in $(SUBDIRS); do \ + sh -c "cd $$dir && $(MAKE) all" || exit 1; \ + done; \ + if [ ! -f stamp ]; then \ + touch stamp; \ + fi + +clean: + -rm -f *.a stamp + @for dir in *; do \ + if [ -f $$dir/Makefile ]; then \ + sh -c "cd $$dir && $(MAKE) $@" || exit 1;\ + fi; \ + done + +distclean: + -rm -f *.a Makefile + @for dir in *; do \ + if [ -f $$dir/Makefile ]; then \ + sh -c "cd $$dir && $(MAKE) distclean"; \ + fi; \ + done + +.DEFAULT: + @test -z "$(SUBDIRS)" || for dir in $(SUBDIRS); do \ + sh -c "cd $$dir && $(MAKE) $@" || exit 1; \ + done diff --git a/src/fs/aufs/Makefile.in b/src/fs/aufs/Makefile.in new file mode 100644 index 0000000000..a577db77d5 --- /dev/null +++ b/src/fs/aufs/Makefile.in @@ -0,0 +1,58 @@ +# +# Makefile for the AUFS storage driver for the Squid Object Cache server +# +# $Id: Makefile.in,v 1.1 2000/05/03 17:15:46 adrian Exp $ +# + +FS = aufs + +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ + +CC = @CC@ +MAKEDEPEND = @MAKEDEPEND@ +AR_R = @AR_R@ +RANLIB = @RANLIB@ +AC_CFLAGS = @CFLAGS@ +SHELL = /bin/sh + +INCLUDE = -I../../../include -I$(top_srcdir)/include -I$(top_srcdir)/src/ +CFLAGS = $(AC_CFLAGS) $(INCLUDE) $(DEFINES) + +OUT = ../$(FS).a + +OBJS = \ + aiops.o \ + async_io.o \ + store_dir_aufs.o \ + store_io_aufs.o + + +all: $(OUT) + +$(OUT): $(OBJS) + @rm -f ../stamp + $(AR_R) $(OUT) $(OBJS) + $(RANLIB) $(OUT) + +$(OBJS): $(top_srcdir)/include/version.h ../../../include/autoconf.h + +.c.o: + @rm -f ../stamp + $(CC) $(CFLAGS) -c $< + +clean: + -rm -rf *.o *pure_* core ../$(FS).a + +distclean: clean + -rm -f Makefile + -rm -f Makefile.bak + -rm -f tags + +install: + +tags: + ctags *.[ch] $(top_srcdir)/src/*.[ch] $(top_srcdir)/include/*.h $(top_srcdir)/lib/*.[ch] + +depend: + $(MAKEDEPEND) $(INCLUDE) -fMakefile *.c diff --git a/src/fs/aufs/aiops.cc b/src/fs/aufs/aiops.cc new file mode 100644 index 0000000000..e99af58d4c --- /dev/null +++ b/src/fs/aufs/aiops.cc @@ -0,0 +1,907 @@ +/* + * $Id: aiops.cc,v 1.1 2000/05/03 17:15:46 adrian Exp $ + * + * DEBUG: section 43 AIOPS + * AUTHOR: Stewart Forster + * + * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from the + * Internet community. Development is led by Duane Wessels of the + * National Laboratory for Applied Network Research and funded by the + * National Science Foundation. Squid is Copyrighted (C) 1998 by + * Duane Wessels and the University of California San Diego. Please + * see the COPYRIGHT file for full details. Squid incorporates + * software developed and/or copyrighted by other sources. Please see + * the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#include "squid.h" +#include "store_asyncufs.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#if HAVE_SCHED_H +#include +#endif + +#define RIDICULOUS_LENGTH 4096 + +#if defined(_SQUID_LINUX_) +/* Linux requires proper use of mutexes or it will segfault deep in the + * thread libraries. Observed on Alpha SMP Linux 2.2.10-ac12. + */ +#define AIO_PROPER_MUTEX 1 +#endif + +enum _aio_thread_status { + _THREAD_STARTING = 0, + _THREAD_WAITING, + _THREAD_BUSY, + _THREAD_FAILED, + _THREAD_DONE +}; + +enum _aio_request_type { + _AIO_OP_NONE = 0, + _AIO_OP_OPEN, + _AIO_OP_READ, + _AIO_OP_WRITE, + _AIO_OP_CLOSE, + _AIO_OP_UNLINK, + _AIO_OP_OPENDIR, + _AIO_OP_STAT +}; + +typedef struct aio_request_t { + enum _aio_request_type request_type; + int cancelled; + char *path; + int oflag; + mode_t mode; + int fd; + char *bufferp; + char *tmpbufp; + int buflen; + off_t offset; + int whence; + int ret; + int err; + struct stat *tmpstatp; + struct stat *statp; + aio_result_t *resultp; + struct aio_request_t *next; +} aio_request_t; + + +typedef struct aio_thread_t { + pthread_t thread; + enum _aio_thread_status status; + pthread_mutex_t mutex; /* Mutex for testing condition variable */ + pthread_cond_t cond; /* Condition variable */ + struct aio_request_t *volatile req; /* set by main, cleared by thread */ + struct aio_request_t *processed_req; /* reminder to main */ + struct aio_thread_t *next; +} aio_thread_t; + + +int aio_cancel(aio_result_t *); +int aio_open(const char *, int, mode_t, aio_result_t *); +int aio_read(int, char *, int, off_t, int, aio_result_t *); +int aio_write(int, char *, int, off_t, int, aio_result_t *); +int aio_close(int, aio_result_t *); +int aio_unlink(const char *, aio_result_t *); +int aio_opendir(const char *, aio_result_t *); +aio_result_t *aio_poll_done(); +int aio_sync(void); + +static void aio_init(void); +static void aio_queue_request(aio_request_t *); +static void aio_process_request_queue(void); +static void aio_cleanup_request(aio_request_t *); +static void *aio_thread_loop(void *); +static void aio_do_open(aio_request_t *); +static void aio_do_read(aio_request_t *); +static void aio_do_write(aio_request_t *); +static void aio_do_close(aio_request_t *); +static void aio_do_stat(aio_request_t *); +static void aio_do_unlink(aio_request_t *); +#if AIO_OPENDIR +static void *aio_do_opendir(aio_request_t *); +#endif +static void aio_debug(aio_request_t *); +static void aio_poll_threads(void); + +static aio_thread_t *threads; +static int aio_initialised = 0; + +static int request_queue_len = 0; +static MemPool *aio_request_pool = NULL; +static aio_request_t *request_queue_head = NULL; +static aio_request_t *request_queue_tail = NULL; +static aio_request_t *request_done_head = NULL; +static aio_request_t *request_done_tail = NULL; +static aio_thread_t *wait_threads = NULL; +static aio_thread_t *busy_threads_head = NULL; +static aio_thread_t *busy_threads_tail = NULL; +static pthread_attr_t globattr; +static struct sched_param globsched; +static pthread_t main_thread; + +static void +aio_init(void) +{ + int i; + aio_thread_t *threadp; + + if (aio_initialised) + return; + + pthread_attr_init(&globattr); +#if HAVE_PTHREAD_ATTR_SETSCOPE + pthread_attr_setscope(&globattr, PTHREAD_SCOPE_SYSTEM); +#endif + globsched.sched_priority = 1; + main_thread = pthread_self(); +#if HAVE_PTHREAD_SETSCHEDPARAM + pthread_setschedparam(main_thread, SCHED_OTHER, &globsched); +#endif + globsched.sched_priority = 2; +#if HAVE_PTHREAD_ATTR_SETSCHEDPARAM + pthread_attr_setschedparam(&globattr, &globsched); +#endif + + /* Create threads and get them to sit in their wait loop */ + threads = xcalloc(NUMTHREADS, sizeof(aio_thread_t)); + + for (i = 0; i < NUMTHREADS; i++) { + threadp = &threads[i]; + threadp->status = _THREAD_STARTING; + if (pthread_mutex_init(&(threadp->mutex), NULL)) { + threadp->status = _THREAD_FAILED; + continue; + } + if (pthread_cond_init(&(threadp->cond), NULL)) { + threadp->status = _THREAD_FAILED; + continue; + } + threadp->req = NULL; + threadp->processed_req = NULL; + if (pthread_create(&threadp->thread, &globattr, aio_thread_loop, threadp)) { + fprintf(stderr, "Thread creation failed\n"); + threadp->status = _THREAD_FAILED; + continue; + } + threadp->next = wait_threads; + wait_threads = threadp; +#if AIO_PROPER_MUTEX + pthread_mutex_lock(&threadp->mutex); +#endif + } + + /* Create request pool */ + aio_request_pool = memPoolCreate("aio_request", sizeof(aio_request_t)); + + aio_initialised = 1; +} + + +static void * +aio_thread_loop(void *ptr) +{ + aio_thread_t *threadp = ptr; + aio_request_t *request; + sigset_t new; +#if !AIO_PROPER_MUTEX + struct timespec wait_time; +#endif + + /* + * Make sure to ignore signals which may possibly get sent to + * the parent squid thread. Causes havoc with mutex's and + * condition waits otherwise + */ + + sigemptyset(&new); + sigaddset(&new, SIGPIPE); + sigaddset(&new, SIGCHLD); +#ifdef _SQUID_LINUX_THREADS_ + sigaddset(&new, SIGQUIT); + sigaddset(&new, SIGTRAP); +#else + sigaddset(&new, SIGUSR1); + sigaddset(&new, SIGUSR2); +#endif + sigaddset(&new, SIGHUP); + sigaddset(&new, SIGTERM); + sigaddset(&new, SIGINT); + sigaddset(&new, SIGALRM); + pthread_sigmask(SIG_BLOCK, &new, NULL); + + pthread_mutex_lock(&threadp->mutex); + while (1) { +#if AIO_PROPER_MUTEX + while (threadp->req == NULL) { + threadp->status = _THREAD_WAITING; + pthread_cond_wait(&threadp->cond, &threadp->mutex); + } +#else + /* The timeout is used to unlock the race condition where + * ->req is set between the check and pthread_cond_wait. + * The thread steps it's own clock on each timeout, to avoid a CPU + * spin situation if the main thread is suspended (paging), and + * squid_curtime is not being updated timely. + */ + wait_time.tv_sec = squid_curtime + 1; /* little quicker first time */ + wait_time.tv_nsec = 0; + while (threadp->req == NULL) { + threadp->status = _THREAD_WAITING; + pthread_cond_timedwait(&threadp->cond, &threadp->mutex, &wait_time); + wait_time.tv_sec += 3; /* then wait 3 seconds between each check */ + } +#endif + request = threadp->req; + errno = 0; + if (!request->cancelled) { + switch (request->request_type) { + case _AIO_OP_OPEN: + aio_do_open(request); + break; + case _AIO_OP_READ: + aio_do_read(request); + break; + case _AIO_OP_WRITE: + aio_do_write(request); + break; + case _AIO_OP_CLOSE: + aio_do_close(request); + break; + case _AIO_OP_UNLINK: + aio_do_unlink(request); + break; +#if AIO_OPENDIR /* Opendir not implemented yet */ + case _AIO_OP_OPENDIR: + aio_do_opendir(request); + break; +#endif + case _AIO_OP_STAT: + aio_do_stat(request); + break; + default: + request->ret = -1; + request->err = EINVAL; + break; + } + } else { /* cancelled */ + request->ret = -1; + request->err = EINTR; + } + threadp->req = NULL; /* tells main thread that we are done */ + } /* while */ + return NULL; +} /* aio_thread_loop */ + +static void +aio_do_request(aio_request_t * requestp) +{ + if (wait_threads == NULL && busy_threads_head == NULL) { + fprintf(stderr, "PANIC: No threads to service requests with!\n"); + exit(-1); + } + aio_queue_request(requestp); +} /* aio_do_request */ + + +static void +aio_queue_request(aio_request_t * requestp) +{ + aio_request_t *rp; + static int last_warn = 0; + static int high_start = 0; + static int queue_high, queue_low; + int i; + + /* Mark it as not executed (failing result, no error) */ + requestp->ret = -1; + requestp->err = 0; + /* Queue it on the request queue */ + if (request_queue_head == NULL) { + request_queue_head = requestp; + request_queue_tail = requestp; + } else { + request_queue_tail->next = requestp; + request_queue_tail = requestp; + } + requestp->next = NULL; + request_queue_len += 1; + /* Poll done threads if needed */ + if (wait_threads == NULL) + aio_poll_threads(); + /* Kick it rolling */ + aio_process_request_queue(); + /* Warn if out of threads */ + if (request_queue_len > (NUMTHREADS >> 1)) { + if (high_start == 0) { + high_start = squid_curtime; + queue_high = request_queue_len; + queue_low = request_queue_len; + } + if (request_queue_len > queue_high) + queue_high = request_queue_len; + if (request_queue_len < queue_low) + queue_low = request_queue_len; + if (squid_curtime >= (last_warn + 15) && + squid_curtime >= (high_start + 3)) { + debug(43, 1) ("aio_queue_request: WARNING - Running out of I/O threads\n"); + debug(43, 2) ("aio_queue_request: Queue Length: current=%d, high=%d, low=%d, duration=%d\n", + request_queue_len, queue_high, queue_low, squid_curtime - high_start); + debug(43, 1) ("aio_queue_request: Perhaps you should increase NUMTHREADS\n"); + debug(43, 1) ("aio_queue_request: Or install more disks to share the load\n"); + debug(43, 3) ("aio_queue_request: First %d items on request queue\n", NUMTHREADS); + rp = request_queue_head; + for (i = 1; i <= NUMTHREADS; i++) { + switch (rp->request_type) { + case _AIO_OP_OPEN: + debug(43, 3) ("aio_queue_request: %d : open -> %s\n", i, rp->path); + break; + case _AIO_OP_READ: + debug(43, 3) ("aio_queue_request: %d : read -> FD = %d\n", i, rp->fd); + break; + case _AIO_OP_WRITE: + debug(43, 3) ("aio_queue_request: %d : write -> FD = %d\n", i, rp->fd); + break; + case _AIO_OP_CLOSE: + debug(43, 3) ("aio_queue_request: %d : close -> FD = %d\n", i, rp->fd); + break; + case _AIO_OP_UNLINK: + debug(43, 3) ("aio_queue_request: %d : unlink -> %s\n", i, rp->path); + break; + case _AIO_OP_STAT: + debug(43, 3) ("aio_queue_request: %d : stat -> %s\n", i, rp->path); + break; + default: + debug(43, 1) ("aio_queue_request: %d : Unimplemented request type: %d\n", i, rp->request_type); + break; + } + if ((rp = rp->next) == NULL) + break; + } + last_warn = squid_curtime; + } + } else { + high_start = 0; + } + if (request_queue_len > RIDICULOUS_LENGTH) { + debug(43, 0) ("aio_queue_request: Async request queue growing uncontrollably!\n"); + debug(43, 0) ("aio_queue_request: Syncing pending I/O operations.. (blocking)\n"); + aio_sync(); + debug(43, 0) ("aio_queue_request: Synced\n"); + } +} /* aio_queue_request */ + + +static void +aio_process_request_queue(void) +{ + aio_thread_t *threadp; + aio_request_t *requestp; + + for (;;) { + if (wait_threads == NULL || request_queue_head == NULL) + return; + + requestp = request_queue_head; + if ((request_queue_head = requestp->next) == NULL) + request_queue_tail = NULL; + requestp->next = NULL; + request_queue_len--; + + if (requestp->cancelled) { + aio_cleanup_request(requestp); + continue; + } + threadp = wait_threads; + wait_threads = threadp->next; + threadp->next = NULL; + + if (busy_threads_head != NULL) + busy_threads_tail->next = threadp; + else + busy_threads_head = threadp; + busy_threads_tail = threadp; + + threadp->status = _THREAD_BUSY; + threadp->req = threadp->processed_req = requestp; + pthread_cond_signal(&(threadp->cond)); +#if AIO_PROPER_MUTEX + pthread_mutex_unlock(&threadp->mutex); +#endif + } +} /* aio_process_request_queue */ + + +static void +aio_cleanup_request(aio_request_t * requestp) +{ + aio_result_t *resultp = requestp->resultp; + int cancelled = requestp->cancelled; + + /* Free allocated structures and copy data back to user space if the */ + /* request hasn't been cancelled */ + switch (requestp->request_type) { + case _AIO_OP_STAT: + if (!cancelled && requestp->ret == 0) + xmemcpy(requestp->statp, requestp->tmpstatp, sizeof(struct stat)); + xfree(requestp->tmpstatp); + case _AIO_OP_OPEN: + if (cancelled && requestp->ret >= 0) + /* The open() was cancelled but completed */ + close(requestp->ret); + xfree(requestp->path); + break; + case _AIO_OP_CLOSE: + if (cancelled && requestp->ret < 0) + /* The close() was cancelled and never got executed */ + close(requestp->fd); + break; + case _AIO_OP_UNLINK: + case _AIO_OP_OPENDIR: + xfree(requestp->path); + break; + case _AIO_OP_READ: + if (!cancelled && requestp->ret > 0) + xmemcpy(requestp->bufferp, requestp->tmpbufp, requestp->ret); + case _AIO_OP_WRITE: + xfree(requestp->tmpbufp); + break; + default: + break; + } + if (resultp != NULL && !cancelled) { + resultp->aio_return = requestp->ret; + resultp->aio_errno = requestp->err; + } + memPoolFree(aio_request_pool, requestp); +} /* aio_cleanup_request */ + + +int +aio_cancel(aio_result_t * resultp) +{ + aio_thread_t *threadp; + aio_request_t *requestp; + + for (threadp = busy_threads_head; threadp != NULL; threadp = threadp->next) + if (threadp->processed_req->resultp == resultp) { + threadp->processed_req->cancelled = 1; + threadp->processed_req->resultp = NULL; + return 0; + } + for (requestp = request_queue_head; requestp != NULL; requestp = requestp->next) + if (requestp->resultp == resultp) { + requestp->cancelled = 1; + requestp->resultp = NULL; + return 0; + } + for (requestp = request_done_head; requestp != NULL; requestp = requestp->next) + if (requestp->resultp == resultp) { + requestp->cancelled = 1; + requestp->resultp = NULL; + return 0; + } + return 1; +} /* aio_cancel */ + + +int +aio_open(const char *path, int oflag, mode_t mode, aio_result_t * resultp) +{ + aio_request_t *requestp; + int len; + + if (!aio_initialised) + aio_init(); + if ((requestp = memPoolAlloc(aio_request_pool)) == NULL) { + errno = ENOMEM; + return -1; + } + len = strlen(path) + 1; + if ((requestp->path = (char *) xmalloc(len)) == NULL) { + memPoolFree(aio_request_pool, requestp); + errno = ENOMEM; + return -1; + } + strncpy(requestp->path, path, len); + requestp->oflag = oflag; + requestp->mode = mode; + requestp->resultp = resultp; + requestp->request_type = _AIO_OP_OPEN; + requestp->cancelled = 0; + + aio_do_request(requestp); + return 0; +} + + +static void +aio_do_open(aio_request_t * requestp) +{ + requestp->ret = open(requestp->path, requestp->oflag, requestp->mode); + requestp->err = errno; +} + + +int +aio_read(int fd, char *bufp, int bufs, off_t offset, int whence, aio_result_t * resultp) +{ + aio_request_t *requestp; + + if (!aio_initialised) + aio_init(); + if ((requestp = memPoolAlloc(aio_request_pool)) == NULL) { + errno = ENOMEM; + return -1; + } + requestp->fd = fd; + requestp->bufferp = bufp; + if ((requestp->tmpbufp = (char *) xmalloc(bufs)) == NULL) { + memPoolFree(aio_request_pool, requestp); + errno = ENOMEM; + return -1; + } + requestp->buflen = bufs; + requestp->offset = offset; + requestp->whence = whence; + requestp->resultp = resultp; + requestp->request_type = _AIO_OP_READ; + requestp->cancelled = 0; + + aio_do_request(requestp); + return 0; +} + + +static void +aio_do_read(aio_request_t * requestp) +{ + lseek(requestp->fd, requestp->offset, requestp->whence); + requestp->ret = read(requestp->fd, requestp->tmpbufp, requestp->buflen); + requestp->err = errno; +} + + +int +aio_write(int fd, char *bufp, int bufs, off_t offset, int whence, aio_result_t * resultp) +{ + aio_request_t *requestp; + + if (!aio_initialised) + aio_init(); + if ((requestp = memPoolAlloc(aio_request_pool)) == NULL) { + errno = ENOMEM; + return -1; + } + requestp->fd = fd; + if ((requestp->tmpbufp = (char *) xmalloc(bufs)) == NULL) { + memPoolFree(aio_request_pool, requestp); + errno = ENOMEM; + return -1; + } + xmemcpy(requestp->tmpbufp, bufp, bufs); + requestp->buflen = bufs; + requestp->offset = offset; + requestp->whence = whence; + requestp->resultp = resultp; + requestp->request_type = _AIO_OP_WRITE; + requestp->cancelled = 0; + + aio_do_request(requestp); + return 0; +} + + +static void +aio_do_write(aio_request_t * requestp) +{ + requestp->ret = write(requestp->fd, requestp->tmpbufp, requestp->buflen); + requestp->err = errno; +} + + +int +aio_close(int fd, aio_result_t * resultp) +{ + aio_request_t *requestp; + + if (!aio_initialised) + aio_init(); + if ((requestp = memPoolAlloc(aio_request_pool)) == NULL) { + errno = ENOMEM; + return -1; + } + requestp->fd = fd; + requestp->resultp = resultp; + requestp->request_type = _AIO_OP_CLOSE; + requestp->cancelled = 0; + + aio_do_request(requestp); + return 0; +} + + +static void +aio_do_close(aio_request_t * requestp) +{ + requestp->ret = close(requestp->fd); + requestp->err = errno; +} + + +int +aio_stat(const char *path, struct stat *sb, aio_result_t * resultp) +{ + aio_request_t *requestp; + int len; + + if (!aio_initialised) + aio_init(); + if ((requestp = memPoolAlloc(aio_request_pool)) == NULL) { + errno = ENOMEM; + return -1; + } + len = strlen(path) + 1; + if ((requestp->path = (char *) xmalloc(len)) == NULL) { + memPoolFree(aio_request_pool, requestp); + errno = ENOMEM; + return -1; + } + strncpy(requestp->path, path, len); + requestp->statp = sb; + if ((requestp->tmpstatp = (struct stat *) xmalloc(sizeof(struct stat))) == NULL) { + xfree(requestp->path); + memPoolFree(aio_request_pool, requestp); + errno = ENOMEM; + return -1; + } + requestp->resultp = resultp; + requestp->request_type = _AIO_OP_STAT; + requestp->cancelled = 0; + + aio_do_request(requestp); + return 0; +} + + +static void +aio_do_stat(aio_request_t * requestp) +{ + requestp->ret = stat(requestp->path, requestp->tmpstatp); + requestp->err = errno; +} + + +int +aio_unlink(const char *path, aio_result_t * resultp) +{ + aio_request_t *requestp; + int len; + + if (!aio_initialised) + aio_init(); + if ((requestp = memPoolAlloc(aio_request_pool)) == NULL) { + errno = ENOMEM; + return -1; + } + len = strlen(path) + 1; + if ((requestp->path = (char *) xmalloc(len)) == NULL) { + memPoolFree(aio_request_pool, requestp); + errno = ENOMEM; + return -1; + } + strncpy(requestp->path, path, len); + requestp->resultp = resultp; + requestp->request_type = _AIO_OP_UNLINK; + requestp->cancelled = 0; + + aio_do_request(requestp); + return 0; +} + + +static void +aio_do_unlink(aio_request_t * requestp) +{ + requestp->ret = unlink(requestp->path); + requestp->err = errno; +} + + +#if AIO_OPENDIR +/* XXX aio_opendir NOT implemented? */ + +int +aio_opendir(const char *path, aio_result_t * resultp) +{ + aio_request_t *requestp; + int len; + + if (!aio_initialised) + aio_init(); + if ((requestp = memPoolAlloc(aio_request_pool)) == NULL) { + errno = ENOMEM; + return -1; + } + return -1; +} + +static void +aio_do_opendir(aio_request_t * requestp) +{ + /* NOT IMPLEMENTED */ +} + +#endif + + +void +aio_poll_threads(void) +{ + aio_thread_t *prev; + aio_thread_t *threadp; + aio_request_t *requestp; + + do { /* while found completed thread */ + prev = NULL; + threadp = busy_threads_head; + while (threadp) { + debug(43, 9) ("aio_poll_threads: %p: request type %d -> status %d\n", + threadp, + threadp->processed_req->request_type, + threadp->status); +#if AIO_PROPER_MUTEX + if (threadp->req == NULL) + if (pthread_mutex_trylock(&threadp->mutex) == 0) + break; +#else + if (threadp->req == NULL) + break; +#endif + prev = threadp; + threadp = threadp->next; + } + if (threadp == NULL) + break; + + if (prev == NULL) + busy_threads_head = busy_threads_head->next; + else + prev->next = threadp->next; + + if (busy_threads_tail == threadp) + busy_threads_tail = prev; + + requestp = threadp->processed_req; + threadp->processed_req = NULL; + + threadp->next = wait_threads; + wait_threads = threadp; + + if (request_done_tail != NULL) + request_done_tail->next = requestp; + else + request_done_head = requestp; + request_done_tail = requestp; + } while (threadp); + + aio_process_request_queue(); +} /* aio_poll_threads */ + +aio_result_t * +aio_poll_done(void) +{ + aio_request_t *requestp; + aio_result_t *resultp; + int cancelled; + + AIO_REPOLL: + aio_poll_threads(); + if (request_done_head == NULL) { + return NULL; + } + requestp = request_done_head; + request_done_head = requestp->next; + if (!request_done_head) + request_done_tail = NULL; + + resultp = requestp->resultp; + cancelled = requestp->cancelled; + aio_debug(requestp); + debug(43, 5) ("DONE: %d -> %d\n", requestp->ret, requestp->err); + aio_cleanup_request(requestp); + if (cancelled) + goto AIO_REPOLL; + return resultp; +} /* aio_poll_done */ + +int +aio_operations_pending(void) +{ + return request_queue_len + (request_done_head != NULL) + (busy_threads_head != NULL); +} + +int +aio_overloaded(void) +{ + static time_t last_warn = 0; + if (aio_operations_pending() > RIDICULOUS_LENGTH / 4) { + if (squid_curtime >= (last_warn + 15)) { + debug(43, 0) ("Warning: Async-IO overloaded\n"); + last_warn = squid_curtime; + } + return 1; + } + return 0; +} + +int +aio_sync(void) +{ + int loop_count = 0; + do { + aio_poll_threads(); + assert(++loop_count < 10); + } while (request_queue_len > 0); + return aio_operations_pending(); +} + +int +aio_get_queue_len(void) +{ + return request_queue_len; +} + +static void +aio_debug(aio_request_t * requestp) +{ + switch (requestp->request_type) { + case _AIO_OP_OPEN: + debug(43, 5) ("OPEN of %s to FD %d\n", requestp->path, requestp->ret); + break; + case _AIO_OP_READ: + debug(43, 5) ("READ on fd: %d\n", requestp->fd); + break; + case _AIO_OP_WRITE: + debug(43, 5) ("WRITE on fd: %d\n", requestp->fd); + break; + case _AIO_OP_CLOSE: + debug(43, 5) ("CLOSE of fd: %d\n", requestp->fd); + break; + case _AIO_OP_UNLINK: + debug(43, 5) ("UNLINK of %s\n", requestp->path); + break; + default: + break; + } +} diff --git a/src/fs/aufs/async_io.cc b/src/fs/aufs/async_io.cc new file mode 100644 index 0000000000..084b4c6130 --- /dev/null +++ b/src/fs/aufs/async_io.cc @@ -0,0 +1,426 @@ + +/* + * $Id: async_io.cc,v 1.1 2000/05/03 17:15:46 adrian Exp $ + * + * DEBUG: section 32 Asynchronous Disk I/O + * AUTHOR: Pete Bentley + * AUTHOR: Stewart Forster + * + * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from the + * Internet community. Development is led by Duane Wessels of the + * National Laboratory for Applied Network Research and funded by the + * National Science Foundation. Squid is Copyrighted (C) 1998 by + * Duane Wessels and the University of California San Diego. Please + * see the COPYRIGHT file for full details. Squid incorporates + * software developed and/or copyrighted by other sources. Please see + * the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#include "squid.h" +#include "store_asyncufs.h" + +#define _AIO_OPEN 0 +#define _AIO_READ 1 +#define _AIO_WRITE 2 +#define _AIO_CLOSE 3 +#define _AIO_UNLINK 4 +#define _AIO_OPENDIR 5 +#define _AIO_STAT 6 + +typedef struct aio_ctrl_t { + struct aio_ctrl_t *next; + int fd; + int operation; + AIOCB *done_handler; + void *done_handler_data; + aio_result_t result; +} aio_ctrl_t; + +struct { + int open; + int close; + int cancel; + int write; + int read; + int stat; + int unlink; + int check_callback; +} aio_counts; + +typedef struct aio_unlinkq_t { + char *path; + struct aio_unlinkq_t *next; +} aio_unlinkq_t; + +static aio_ctrl_t *used_list = NULL; +static int initialised = 0; +static OBJH aioStats; +static MemPool *aio_ctrl_pool; +static void aioFDWasClosed(int fd); + +MemPool * aio_state_pool; + +static void +aioFDWasClosed(int fd) +{ + if (fd_table[fd].flags.closing) + fd_close(fd); +} + +void +aioInit(void) +{ + if (initialised) + return; + aio_ctrl_pool = memPoolCreate("aio_ctrl", sizeof(aio_ctrl_t)); + aio_state_pool = memPoolCreate("Async UFS IO State data", sizeof(aiostate_t)); + cachemgrRegister("aio_counts", "Async IO Function Counters", + aioStats, 0, 1); + initialised = 1; + comm_quick_poll_required(); +} + +void +aioDone(void) +{ + memPoolDestroy(aio_ctrl_pool); + memPoolDestroy(aio_state_pool); + initialised = 0; +} + +void +aioOpen(const char *path, int oflag, mode_t mode, AIOCB * callback, void *callback_data) +{ + aio_ctrl_t *ctrlp; + int ret; + + assert(initialised); + aio_counts.open++; + ctrlp = memPoolAlloc(aio_ctrl_pool); + ctrlp->fd = -2; + ctrlp->done_handler = callback; + ctrlp->done_handler_data = callback_data; + ctrlp->operation = _AIO_OPEN; + cbdataLock(callback_data); + if (aio_open(path, oflag, mode, &ctrlp->result) < 0) { + ret = open(path, oflag, mode); + if (callback) + (callback) (ctrlp->fd, callback_data, ret, errno); + cbdataUnlock(callback_data); + memPoolFree(aio_ctrl_pool, ctrlp); + return; + } + ctrlp->next = used_list; + used_list = ctrlp; + return; +} + +void +aioClose(int fd) +{ + aio_ctrl_t *ctrlp; + + assert(initialised); + aio_counts.close++; + aioCancel(fd); + ctrlp = memPoolAlloc(aio_ctrl_pool); + ctrlp->fd = fd; + ctrlp->done_handler = NULL; + ctrlp->done_handler_data = NULL; + ctrlp->operation = _AIO_CLOSE; + if (aio_close(fd, &ctrlp->result) < 0) { + close(fd); /* Can't create thread - do a normal close */ + memPoolFree(aio_ctrl_pool, ctrlp); + aioFDWasClosed(fd); + return; + } + ctrlp->next = used_list; + used_list = ctrlp; + return; +} + +void +aioCancel(int fd) +{ + aio_ctrl_t *curr; + aio_ctrl_t *prev; + aio_ctrl_t *next; + AIOCB *done_handler; + void *their_data; + + assert(initialised); + aio_counts.cancel++; + prev = NULL; + curr = used_list; + for (curr = used_list;; curr = next) { + while (curr != NULL) { + if (curr->fd == fd) + break; + prev = curr; + curr = curr->next; + } + if (curr == NULL) + break; + + aio_cancel(&curr->result); + + if ((done_handler = curr->done_handler)) { + their_data = curr->done_handler_data; + curr->done_handler = NULL; + curr->done_handler_data = NULL; + debug(0, 0) ("this be aioCancel\n"); + if (cbdataValid(their_data)) + done_handler(fd, their_data, -2, -2); + cbdataUnlock(their_data); + } + next = curr->next; + if (prev == NULL) + used_list = next; + else + prev->next = next; + + memPoolFree(aio_ctrl_pool, curr); + } +} + + +void +aioWrite(int fd, int offset, char *bufp, int len, AIOCB * callback, void *callback_data, FREE * free_func) +{ + aio_ctrl_t *ctrlp; + int seekmode; + + assert(initialised); + aio_counts.write++; + for (ctrlp = used_list; ctrlp != NULL; ctrlp = ctrlp->next) + if (ctrlp->fd == fd) + break; + if (ctrlp != NULL) { + debug(0, 0) ("aioWrite: EWOULDBLOCK\n"); + errno = EWOULDBLOCK; + if (callback) + (callback) (fd, callback_data, -1, errno); + if (free_func) + free_func(bufp); + return; + } + ctrlp = memPoolAlloc(aio_ctrl_pool); + ctrlp->fd = fd; + ctrlp->done_handler = callback; + ctrlp->done_handler_data = callback_data; + ctrlp->operation = _AIO_WRITE; + if (offset >= 0) + seekmode = SEEK_SET; + else { + seekmode = SEEK_END; + offset = 0; + } + cbdataLock(callback_data); + if (aio_write(fd, bufp, len, offset, seekmode, &ctrlp->result) < 0) { + if (errno == ENOMEM || errno == EAGAIN || errno == EINVAL) + errno = EWOULDBLOCK; + if (callback) + (callback) (fd, callback_data, -1, errno); + cbdataUnlock(callback_data); + memPoolFree(aio_ctrl_pool, ctrlp); + } else { + ctrlp->next = used_list; + used_list = ctrlp; + } + /* + * aio_write copies the buffer so we can free it here + */ + if (free_func) + free_func(bufp); +} /* aioWrite */ + + +void +aioRead(int fd, int offset, char *bufp, int len, AIOCB * callback, void *callback_data) +{ + aio_ctrl_t *ctrlp; + int seekmode; + + assert(initialised); + aio_counts.read++; + for (ctrlp = used_list; ctrlp != NULL; ctrlp = ctrlp->next) + if (ctrlp->fd == fd) + break; + if (ctrlp != NULL) { + errno = EWOULDBLOCK; + if (callback) + (callback) (fd, callback_data, -1, errno); + return; + } + ctrlp = memPoolAlloc(aio_ctrl_pool); + ctrlp->fd = fd; + ctrlp->done_handler = callback; + ctrlp->done_handler_data = callback_data; + ctrlp->operation = _AIO_READ; + if (offset >= 0) + seekmode = SEEK_SET; + else { + seekmode = SEEK_CUR; + offset = 0; + } + cbdataLock(callback_data); + if (aio_read(fd, bufp, len, offset, seekmode, &ctrlp->result) < 0) { + if (errno == ENOMEM || errno == EAGAIN || errno == EINVAL) + errno = EWOULDBLOCK; + if (callback) + (callback) (fd, callback_data, -1, errno); + cbdataUnlock(callback_data); + memPoolFree(aio_ctrl_pool, ctrlp); + return; + } + ctrlp->next = used_list; + used_list = ctrlp; + return; +} /* aioRead */ + +void +aioStat(char *path, struct stat *sb, AIOCB * callback, void *callback_data) +{ + aio_ctrl_t *ctrlp; + + assert(initialised); + aio_counts.stat++; + ctrlp = memPoolAlloc(aio_ctrl_pool); + ctrlp->fd = -2; + ctrlp->done_handler = callback; + ctrlp->done_handler_data = callback_data; + ctrlp->operation = _AIO_STAT; + cbdataLock(callback_data); + if (aio_stat(path, sb, &ctrlp->result) < 0) { + if (errno == ENOMEM || errno == EAGAIN || errno == EINVAL) + errno = EWOULDBLOCK; + if (callback) + (callback) (ctrlp->fd, callback_data, -1, errno); + cbdataUnlock(callback_data); + memPoolFree(aio_ctrl_pool, ctrlp); + return; + } + ctrlp->next = used_list; + used_list = ctrlp; + return; +} /* aioStat */ + +void +aioUnlink(const char *pathname, AIOCB * callback, void *callback_data) +{ + aio_ctrl_t *ctrlp; + char *path; + assert(initialised); + aio_counts.unlink++; + ctrlp = memPoolAlloc(aio_ctrl_pool); + ctrlp->fd = -2; + ctrlp->done_handler = callback; + ctrlp->done_handler_data = callback_data; + ctrlp->operation = _AIO_UNLINK; + path = xstrdup(pathname); + cbdataLock(callback_data); + if (aio_unlink(path, &ctrlp->result) < 0) { + int ret = unlink(path); + (callback) (ctrlp->fd, callback_data, ret, errno); + cbdataUnlock(callback_data); + memPoolFree(aio_ctrl_pool, ctrlp); + xfree(path); + return; + } + ctrlp->next = used_list; + used_list = ctrlp; + xfree(path); +} /* aioUnlink */ + + +void +aioCheckCallbacks(SwapDir *SD) +{ + aio_result_t *resultp; + aio_ctrl_t *ctrlp; + aio_ctrl_t *prev; + AIOCB *done_handler; + void *their_data; + + assert(initialised); + aio_counts.check_callback++; + for (;;) { + if ((resultp = aio_poll_done()) == NULL) + break; + prev = NULL; + for (ctrlp = used_list; ctrlp != NULL; prev = ctrlp, ctrlp = ctrlp->next) + if (&ctrlp->result == resultp) + break; + if (ctrlp == NULL) + continue; + if (prev == NULL) + used_list = ctrlp->next; + else + prev->next = ctrlp->next; + if ((done_handler = ctrlp->done_handler)) { + their_data = ctrlp->done_handler_data; + ctrlp->done_handler = NULL; + ctrlp->done_handler_data = NULL; + if (cbdataValid(their_data)) + done_handler(ctrlp->fd, their_data, + ctrlp->result.aio_return, ctrlp->result.aio_errno); + cbdataUnlock(their_data); + } + if (ctrlp->operation == _AIO_CLOSE) + aioFDWasClosed(ctrlp->fd); + memPoolFree(aio_ctrl_pool, ctrlp); + } +} + +void +aioStats(StoreEntry * sentry) +{ + storeAppendPrintf(sentry, "ASYNC IO Counters:\n"); + storeAppendPrintf(sentry, "open\t%d\n", aio_counts.open); + storeAppendPrintf(sentry, "close\t%d\n", aio_counts.close); + storeAppendPrintf(sentry, "cancel\t%d\n", aio_counts.cancel); + storeAppendPrintf(sentry, "write\t%d\n", aio_counts.write); + storeAppendPrintf(sentry, "read\t%d\n", aio_counts.read); + storeAppendPrintf(sentry, "stat\t%d\n", aio_counts.stat); + storeAppendPrintf(sentry, "unlink\t%d\n", aio_counts.unlink); + storeAppendPrintf(sentry, "check_callback\t%d\n", aio_counts.check_callback); + storeAppendPrintf(sentry, "queue\t%d\n", aio_get_queue_len()); +} + +/* Flush all pending I/O */ +void +aioSync(SwapDir *SD) +{ + if (!initialised) + return; /* nothing to do then */ + /* Flush all pending operations */ + debug(32, 1) ("aioSync: flushing pending I/O operations\n"); + do { + aioCheckCallbacks(SD); + } while (aio_sync()); + debug(32, 1) ("aioSync: done\n"); +} + +int +aioQueueSize(void) +{ + return memPoolInUseCount(aio_ctrl_pool); +} diff --git a/src/fs/aufs/store_asyncufs.h b/src/fs/aufs/store_asyncufs.h new file mode 100644 index 0000000000..012427e111 --- /dev/null +++ b/src/fs/aufs/store_asyncufs.h @@ -0,0 +1,99 @@ +/* + * store_aufs.h + * + * Internal declarations for the aufs routines + */ + +#ifndef __STORE_ASYNCUFS_H__ +#define __STORE_ASYNCUFS_H__ + +#ifdef ASYNC_IO_THREADS +#define NUMTHREADS ASYNC_IO_THREADS +#else +#define NUMTHREADS 16 +#endif + +#define MAGIC1 (NUMTHREADS/Config.cacheSwap.n_configured/2) + +struct _aio_result_t { + int aio_return; + int aio_errno; +}; + +typedef struct _aio_result_t aio_result_t; + +typedef void AIOCB(int fd, void *, int aio_return, int aio_errno); + +int aio_cancel(aio_result_t *); +int aio_open(const char *, int, mode_t, aio_result_t *); +int aio_read(int, char *, int, off_t, int, aio_result_t *); +int aio_write(int, char *, int, off_t, int, aio_result_t *); +int aio_close(int, aio_result_t *); +int aio_stat(const char *, struct stat *, aio_result_t *); +int aio_unlink(const char *, aio_result_t *); +int aio_opendir(const char *, aio_result_t *); +aio_result_t *aio_poll_done(void); +int aio_operations_pending(void); +int aio_overloaded(void); +int aio_sync(void); +int aio_get_queue_len(void); + +void aioInit(void); +void aioDone(void); +void aioCancel(int); +void aioOpen(const char *, int, mode_t, AIOCB *, void *); +void aioClose(int); +void aioWrite(int, int offset, char *, int size, AIOCB *, void *, FREE *); +void aioRead(int, int offset, char *, int size, AIOCB *, void *); +void aioStat(char *, struct stat *, AIOCB *, void *); +void aioUnlink(const char *, AIOCB *, void *); +void aioCheckCallbacks(SwapDir *); +void aioSync(SwapDir *); +int aioQueueSize(void); + +struct _aioinfo_t { + int swaplog_fd; + int l1; + int l2; + fileMap *map; + int suggest; +}; + +struct _aiostate_t { + int fd; + struct { + unsigned int close_request:1; + unsigned int reading:1; + unsigned int writing:1; + unsigned int opening:1; + } flags; + const char *read_buf; + link_list *pending_writes; + link_list *pending_reads; +}; + +typedef struct _aioinfo_t aioinfo_t; +typedef struct _aiostate_t aiostate_t; + +/* The aio_state memory pool */ +extern MemPool * aio_state_pool; + +extern void storeAufsDirMapBitReset(SwapDir *, sfileno); +extern int storeAufsDirMapBitAllocate(SwapDir *); + +extern char * storeAufsDirFullPath(SwapDir *SD, sfileno filn, char *fullpath); +extern void storeAufsDirUnlinkFile(SwapDir *, sfileno); +extern void storeAufsDirReplAdd(SwapDir *SD, StoreEntry *); +extern void storeAufsDirReplRemove(StoreEntry *); + +/* + * Store IO stuff + */ +extern STOBJCREATE storeAufsCreate; +extern STOBJOPEN storeAufsOpen; +extern STOBJCLOSE storeAufsClose; +extern STOBJREAD storeAufsRead; +extern STOBJWRITE storeAufsWrite; +extern STOBJUNLINK storeAufsUnlink; + +#endif diff --git a/src/fs/aufs/store_dir_aufs.cc b/src/fs/aufs/store_dir_aufs.cc new file mode 100644 index 0000000000..7e443a5c93 --- /dev/null +++ b/src/fs/aufs/store_dir_aufs.cc @@ -0,0 +1,1896 @@ + +/* + * $Id: store_dir_aufs.cc,v 1.1 2000/05/03 17:15:46 adrian Exp $ + * + * DEBUG: section 47 Store Directory Routines + * AUTHOR: Duane Wessels + * + * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from the + * Internet community. Development is led by Duane Wessels of the + * National Laboratory for Applied Network Research and funded by the + * National Science Foundation. Squid is Copyrighted (C) 1998 by + * Duane Wessels and the University of California San Diego. Please + * see the COPYRIGHT file for full details. Squid incorporates + * software developed and/or copyrighted by other sources. Please see + * the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#include "squid.h" +#if HAVE_STATVFS +#if HAVE_SYS_STATVFS_H +#include +#endif +#endif + +#include "store_asyncufs.h" + +#define DefaultLevelOneDirs 16 +#define DefaultLevelTwoDirs 256 +#define STORE_META_BASYNCUFSZ 4096 + +typedef struct _RebuildState RebuildState; +struct _RebuildState { + SwapDir *sd; + int n_read; + FILE *log; + int speed; + int curlvl1; + int curlvl2; + struct { + unsigned int need_to_validate:1; + unsigned int clean:1; + unsigned int init:1; + } flags; + int done; + int in_dir; + int fn; + struct dirent *entry; + DIR *td; + char fullpath[SQUID_MAXPATHLEN]; + char fullfilename[SQUID_MAXPATHLEN]; + struct _store_rebuild_data counts; +}; + +static int n_asyncufs_dirs = 0; +static int *asyncufs_dir_index = NULL; + +static char *storeAufsDirSwapSubDir(SwapDir *, int subdirn); +static int storeAufsDirCreateDirectory(const char *path, int); +static int storeAufsDirVerifyCacheDirs(SwapDir *); +static int storeAufsDirVerifyDirectory(const char *path); +static void storeAufsDirCreateSwapSubDirs(SwapDir *); +static char *storeAufsDirSwapLogFile(SwapDir *, const char *); +static EVH storeAufsDirRebuildFromDirectory; +static EVH storeAufsDirRebuildFromSwapLog; +static int storeAufsDirGetNextFile(RebuildState *, int *sfileno, int *size); +static StoreEntry *storeAufsDirAddDiskRestore(SwapDir *SD, const cache_key * key, + int file_number, + size_t swap_file_sz, + time_t expires, + time_t timestamp, + time_t lastref, + time_t lastmod, + u_num32 refcount, + u_short flags, + int clean); +static void storeAufsDirRebuild(SwapDir * sd); +static void storeAufsDirCloseTmpSwapLog(SwapDir * sd); +static FILE *storeAufsDirOpenTmpSwapLog(SwapDir *, int *, int *); +static STLOGOPEN storeAufsDirOpenSwapLog; +static STINIT storeAufsDirInit; +static STFREE storeAufsDirFree; +static STLOGCLEANOPEN storeAufsDirWriteCleanOpen; +static void storeAufsDirWriteCleanClose(SwapDir * sd); +static STLOGCLEANWRITE storeAufsDirWriteCleanEntry; +static STLOGCLOSE storeAufsDirCloseSwapLog; +static STLOGWRITE storeAufsDirSwapLog; +static STNEWFS storeAufsDirNewfs; +static STDUMP storeAufsDirDump; +static STMAINTAINFS storeAufsDirMaintain; +static STCHECKOBJ storeAufsDirCheckObj; +static STREFOBJ storeAufsDirRefObj; +static STUNREFOBJ storeAufsDirUnrefObj; +static QS rev_int_sort; +static int storeAufsDirClean(int swap_index); +static EVH storeAufsDirCleanEvent; +static int storeAufsDirIs(SwapDir * sd); +static int storeAufsFilenoBelongsHere(int fn, int F0, int F1, int F2); +static int storeAufsCleanupDoubleCheck(SwapDir *, StoreEntry *); +static void storeAufsDirStats(SwapDir *, StoreEntry *); +static void storeAufsDirInitBitmap(SwapDir *); +static int storeAufsDirValidFileno(SwapDir *, sfileno); +static int storeAufsDirCheckExpired(SwapDir *, StoreEntry *); +#if !HEAP_REPLACEMENT +static time_t storeAufsDirExpiredReferenceAge(SwapDir *); +#endif + +/* + * These functions were ripped straight out of the heart of store_dir.c. + * They assume that the given filenum is on a asyncufs partiton, which may or + * may not be true.. + * XXX this evilness should be tidied up at a later date! + */ + +int +storeAufsDirMapBitTest(SwapDir *SD, int fn) +{ + sfileno filn = fn; + aioinfo_t *aioinfo; + aioinfo = (aioinfo_t *)SD->fsdata; + return file_map_bit_test(aioinfo->map, filn); +} + +void +storeAufsDirMapBitSet(SwapDir *SD, int fn) +{ + sfileno filn = fn; + aioinfo_t *aioinfo; + aioinfo = (aioinfo_t *)SD->fsdata; + file_map_bit_set(aioinfo->map, filn); +} + +void +storeAufsDirMapBitReset(SwapDir *SD, int fn) +{ + sfileno filn = fn; + aioinfo_t *aioinfo; + aioinfo = (aioinfo_t *)SD->fsdata; + file_map_bit_reset(aioinfo->map, filn); +} + +int +storeAufsDirMapBitAllocate(SwapDir *SD) +{ + aioinfo_t *aioinfo = (aioinfo_t *)SD->fsdata; + int fn; + fn = file_map_allocate(aioinfo->map, aioinfo->suggest); + file_map_bit_set(aioinfo->map, fn); + aioinfo->suggest = fn + 1; + return fn; +} + + +/* + * Initialise the asyncufs bitmap + * + * If there already is a bitmap, and the numobjects is larger than currently + * configured, we allocate a new bitmap and 'grow' the old one into it. + */ +static void +storeAufsDirInitBitmap(SwapDir *sd) +{ + aioinfo_t *aioinfo = (aioinfo_t *)sd->fsdata; + + if (aioinfo->map == NULL) { + /* First time */ + aioinfo->map = file_map_create(); + } else if (aioinfo->map->max_n_files) { + /* it grew, need to expand */ + /* XXX We don't need it anymore .. */ + } + /* else it shrunk, and we leave the old one in place */ +} + +static char * +storeAufsDirSwapSubDir(SwapDir * sd, int subdirn) +{ + aioinfo_t *aioinfo = (aioinfo_t *)sd->fsdata; + + LOCAL_ARRAY(char, fullfilename, SQUID_MAXPATHLEN); + assert(0 <= subdirn && subdirn < aioinfo->l1); + snprintf(fullfilename, SQUID_MAXPATHLEN, "%s/%02X", sd->path, subdirn); + return fullfilename; +} + +static int +storeAufsDirCreateDirectory(const char *path, int should_exist) +{ + int created = 0; + struct stat st; + getCurrentTime(); + if (0 == stat(path, &st)) { + if (S_ISDIR(st.st_mode)) { + debug(20, should_exist ? 3 : 1) ("%s exists\n", path); + } else { + fatalf("Swap directory %s is not a directory.", path); + } + } else if (0 == mkdir(path, 0755)) { + debug(20, should_exist ? 1 : 3) ("%s created\n", path); + created = 1; + } else { + fatalf("Failed to make swap directory %s: %s", + path, xstrerror()); + } + return created; +} + +static int +storeAufsDirVerifyDirectory(const char *path) +{ + struct stat sb; + if (stat(path, &sb) < 0) { + debug(20, 0) ("%s: %s\n", path, xstrerror()); + return -1; + } + if (S_ISDIR(sb.st_mode) == 0) { + debug(20, 0) ("%s is not a directory\n", path); + return -1; + } + return 0; +} + +/* + * This function is called by storeAufsDirInit(). If this returns < 0, + * then Squid exits, complains about swap directories not + * existing, and instructs the admin to run 'squid -z' + */ +static int +storeAufsDirVerifyCacheDirs(SwapDir * sd) +{ + aioinfo_t *aioinfo = (aioinfo_t *)sd->fsdata; + int j; + const char *path = sd->path; + + if (storeAufsDirVerifyDirectory(path) < 0) + return -1; + for (j = 0; j < aioinfo->l1; j++) { + path = storeAufsDirSwapSubDir(sd, j); + if (storeAufsDirVerifyDirectory(path) < 0) + return -1; + } + return 0; +} + +static void +storeAufsDirCreateSwapSubDirs(SwapDir * sd) +{ + aioinfo_t *aioinfo = (aioinfo_t *)sd->fsdata; + int i, k; + int should_exist; + LOCAL_ARRAY(char, name, MAXPATHLEN); + for (i = 0; i < aioinfo->l1; i++) { + snprintf(name, MAXPATHLEN, "%s/%02X", sd->path, i); + if (storeAufsDirCreateDirectory(name, 0)) + should_exist = 0; + else + should_exist = 1; + debug(47, 1) ("Making directories in %s\n", name); + for (k = 0; k < aioinfo->l2; k++) { + snprintf(name, MAXPATHLEN, "%s/%02X/%02X", sd->path, i, k); + storeAufsDirCreateDirectory(name, should_exist); + } + } +} + +static char * +storeAufsDirSwapLogFile(SwapDir * sd, const char *ext) +{ + LOCAL_ARRAY(char, path, SQUID_MAXPATHLEN); + LOCAL_ARRAY(char, pathtmp, SQUID_MAXPATHLEN); + LOCAL_ARRAY(char, digit, 32); + char *pathtmp2; + if (Config.Log.swap) { + xstrncpy(pathtmp, sd->path, SQUID_MAXPATHLEN - 64); + while (index(pathtmp,'/')) + *index(pathtmp,'/')='.'; + while (strlen(pathtmp) && pathtmp[strlen(pathtmp)-1]=='.') + pathtmp[strlen(pathtmp)-1]= '\0'; + for(pathtmp2 = pathtmp; *pathtmp2 == '.'; pathtmp2++); + snprintf(path, SQUID_MAXPATHLEN-64, Config.Log.swap, pathtmp2); + if (strncmp(path, Config.Log.swap, SQUID_MAXPATHLEN - 64) == 0) { + strcat(path, "."); + snprintf(digit, 32, "%02d", sd->index); + strncat(path, digit, 3); + } + } else { + xstrncpy(path, sd->path, SQUID_MAXPATHLEN - 64); + strcat(path, "/swap.state"); + } + if (ext) + strncat(path, ext, 16); + return path; +} + +static void +storeAufsDirOpenSwapLog(SwapDir * sd) +{ + aioinfo_t *aioinfo = (aioinfo_t *)sd->fsdata; + char *path; + int fd; + path = storeAufsDirSwapLogFile(sd, NULL); + fd = file_open(path, O_WRONLY | O_CREAT); + if (fd < 0) { + debug(50, 1) ("%s: %s\n", path, xstrerror()); + fatal("storeAufsDirOpenSwapLog: Failed to open swap log."); + } + debug(47, 3) ("Cache Dir #%d log opened on FD %d\n", sd->index, fd); + aioinfo->swaplog_fd = fd; + if (0 == n_asyncufs_dirs) + assert(NULL == asyncufs_dir_index); + n_asyncufs_dirs++; + assert(n_asyncufs_dirs <= Config.cacheSwap.n_configured); +} + +static void +storeAufsDirCloseSwapLog(SwapDir * sd) +{ + aioinfo_t *aioinfo = (aioinfo_t *)sd->fsdata; + if (aioinfo->swaplog_fd < 0) /* not open */ + return; + file_close(aioinfo->swaplog_fd); + debug(47, 3) ("Cache Dir #%d log closed on FD %d\n", + sd->index, aioinfo->swaplog_fd); + aioinfo->swaplog_fd = -1; + n_asyncufs_dirs--; + assert(n_asyncufs_dirs >= 0); + if (0 == n_asyncufs_dirs) + safe_free(asyncufs_dir_index); +} + +static void +storeAufsDirInit(SwapDir * sd) +{ + static int started_clean_event = 0; + static const char *errmsg = + "\tFailed to verify one of the swap directories, Check cache.log\n" + "\tfor details. Run 'squid -z' to create swap directories\n" + "\tif needed, or if running Squid for the first time."; + storeAufsDirInitBitmap(sd); + if (storeAufsDirVerifyCacheDirs(sd) < 0) + fatal(errmsg); + storeAufsDirOpenSwapLog(sd); + storeAufsDirRebuild(sd); + if (!started_clean_event) { + eventAdd("storeDirClean", storeAufsDirCleanEvent, NULL, 15.0, 1); + started_clean_event = 1; + } +} + +static void +storeAufsDirRebuildFromDirectory(void *data) +{ + RebuildState *rb = data; + SwapDir *SD = rb->sd; + LOCAL_ARRAY(char, hdr_buf, SM_PAGE_SIZE); + StoreEntry *e = NULL; + StoreEntry tmpe; + cache_key key[MD5_DIGEST_CHARS]; + int sfileno = 0; + int count; + int size; + struct stat sb; + int swap_hdr_len; + int fd = -1; + tlv *tlv_list; + tlv *t; + assert(rb != NULL); + debug(20, 3) ("storeAufsDirRebuildFromDirectory: DIR #%d\n", rb->sd->index); + for (count = 0; count < rb->speed; count++) { + assert(fd == -1); + fd = storeAufsDirGetNextFile(rb, &sfileno, &size); + if (fd == -2) { + debug(20, 1) ("Done scanning %s swaplog (%d entries)\n", + rb->sd->path, rb->n_read); + store_dirs_rebuilding--; + storeAufsDirCloseTmpSwapLog(rb->sd); + storeRebuildComplete(&rb->counts); + cbdataFree(rb); + return; + } else if (fd < 0) { + continue; + } + assert(fd > -1); + /* lets get file stats here */ + if (fstat(fd, &sb) < 0) { + debug(20, 1) ("storeAufsDirRebuildFromDirectory: fstat(FD %d): %s\n", + fd, xstrerror()); + file_close(fd); + store_open_disk_fd--; + fd = -1; + continue; + } + if ((++rb->counts.scancount & 0xFFFF) == 0) + debug(20, 3) (" %s %7d files opened so far.\n", + rb->sd->path, rb->counts.scancount); + debug(20, 9) ("file_in: fd=%d %08X\n", fd, sfileno); + Counter.syscalls.disk.reads++; + if (read(fd, hdr_buf, SM_PAGE_SIZE) < 0) { + debug(20, 1) ("storeAufsDirRebuildFromDirectory: read(FD %d): %s\n", + fd, xstrerror()); + file_close(fd); + store_open_disk_fd--; + fd = -1; + continue; + } + file_close(fd); + store_open_disk_fd--; + fd = -1; + swap_hdr_len = 0; +#if USE_TRUNCATE + if (sb.st_size == 0) + continue; +#endif + tlv_list = storeSwapMetaUnpack(hdr_buf, &swap_hdr_len); + if (tlv_list == NULL) { + debug(20, 1) ("storeAufsDirRebuildFromDirectory: failed to get meta data\n"); + /* XXX shouldn't this be a call to storeAufsUnlink ? */ + storeAufsDirUnlinkFile(SD, sfileno); + continue; + } + debug(20, 3) ("storeAufsDirRebuildFromDirectory: successful swap meta unpacking\n"); + memset(key, '\0', MD5_DIGEST_CHARS); + memset(&tmpe, '\0', sizeof(StoreEntry)); + for (t = tlv_list; t; t = t->next) { + switch (t->type) { + case STORE_META_KEY: + assert(t->length == MD5_DIGEST_CHARS); + xmemcpy(key, t->value, MD5_DIGEST_CHARS); + break; + case STORE_META_STD: + assert(t->length == STORE_HDR_METASIZE); + xmemcpy(&tmpe.timestamp, t->value, STORE_HDR_METASIZE); + break; + default: + break; + } + } + storeSwapTLVFree(tlv_list); + tlv_list = NULL; + if (storeKeyNull(key)) { + debug(20, 1) ("storeAufsDirRebuildFromDirectory: NULL key\n"); + storeAufsDirUnlinkFile(SD, sfileno); + continue; + } + tmpe.key = key; + /* check sizes */ + if (tmpe.swap_file_sz == 0) { + tmpe.swap_file_sz = sb.st_size; + } else if (tmpe.swap_file_sz == sb.st_size - swap_hdr_len) { + tmpe.swap_file_sz = sb.st_size; + } else if (tmpe.swap_file_sz != sb.st_size) { + debug(20, 1) ("storeAufsDirRebuildFromDirectory: SIZE MISMATCH %d!=%d\n", + tmpe.swap_file_sz, (int) sb.st_size); + storeAufsDirUnlinkFile(SD, sfileno); + continue; + } + if (EBIT_TEST(tmpe.flags, KEY_PRIVATE)) { + storeAufsDirUnlinkFile(SD, sfileno); + rb->counts.badflags++; + continue; + } + e = storeGet(key); + if (e && e->lastref >= tmpe.lastref) { + /* key already exists, current entry is newer */ + /* keep old, ignore new */ + rb->counts.dupcount++; + continue; + } else if (NULL != e) { + /* URL already exists, this swapfile not being used */ + /* junk old, load new */ + storeRelease(e); /* release old entry */ + rb->counts.dupcount++; + } + rb->counts.objcount++; + storeEntryDump(&tmpe, 5); + e = storeAufsDirAddDiskRestore(SD, key, + sfileno, + tmpe.swap_file_sz, + tmpe.expires, + tmpe.timestamp, + tmpe.lastref, + tmpe.lastmod, + tmpe.refcount, /* refcount */ + tmpe.flags, /* flags */ + (int) rb->flags.clean); + storeDirSwapLog(e, SWAP_LOG_ADD); + } + eventAdd("storeRebuild", storeAufsDirRebuildFromDirectory, rb, 0.0, 1); +} + +static void +storeAufsDirRebuildFromSwapLog(void *data) +{ + RebuildState *rb = data; + SwapDir *SD = rb->sd; + StoreEntry *e = NULL; + storeSwapLogData s; + size_t ss = sizeof(storeSwapLogData); + int count; + int used; /* is swapfile already in use? */ + int disk_entry_newer; /* is the log entry newer than current entry? */ + double x; + assert(rb != NULL); + /* load a number of objects per invocation */ + for (count = 0; count < rb->speed; count++) { + if (fread(&s, ss, 1, rb->log) != 1) { + debug(20, 1) ("Done reading %s swaplog (%d entries)\n", + rb->sd->path, rb->n_read); + fclose(rb->log); + rb->log = NULL; + store_dirs_rebuilding--; + storeAufsDirCloseTmpSwapLog(rb->sd); + storeRebuildComplete(&rb->counts); + cbdataFree(rb); + return; + } + rb->n_read++; + if (s.op <= SWAP_LOG_NOP) + continue; + if (s.op >= SWAP_LOG_MAX) + continue; + debug(20, 3) ("storeAufsDirRebuildFromSwapLog: %s %s %08X\n", + swap_log_op_str[(int) s.op], + storeKeyText(s.key), + s.swap_filen); + if (s.op == SWAP_LOG_ADD) { + (void) 0; + } else if (s.op == SWAP_LOG_DEL) { + if ((e = storeGet(s.key)) != NULL) { + /* + * Make sure we don't unlink the file, it might be + * in use by a subsequent entry. Also note that + * we don't have to subtract from store_swap_size + * because adding to store_swap_size happens in + * the cleanup procedure. + */ + storeExpireNow(e); + storeReleaseRequest(e); + storeAufsDirReplRemove(e); + storeRelease(e); + if (e->swap_filen > -1) { + /* Fake a unlink here, this is a bad hack :( */ + e->swap_filen = -1; + e->swap_dirn = -1; + } + rb->counts.objcount--; + rb->counts.cancelcount++; + } + continue; + } else { + x = log(++rb->counts.bad_log_op) / log(10.0); + if (0.0 == x - (double) (int) x) + debug(20, 1) ("WARNING: %d invalid swap log entries found\n", + rb->counts.bad_log_op); + rb->counts.invalid++; + continue; + } + if ((++rb->counts.scancount & 0xFFFF) == 0) + debug(20, 3) (" %7d %s Entries read so far.\n", + rb->counts.scancount, rb->sd->path); + if (!storeAufsDirValidFileno(SD, s.swap_filen)) { + rb->counts.invalid++; + continue; + } + if (EBIT_TEST(s.flags, KEY_PRIVATE)) { + rb->counts.badflags++; + continue; + } + e = storeGet(s.key); + used = storeAufsDirMapBitTest(SD, s.swap_filen); + /* If this URL already exists in the cache, does the swap log + * appear to have a newer entry? Compare 'lastref' from the + * swap log to e->lastref. */ + disk_entry_newer = e ? (s.lastref > e->lastref ? 1 : 0) : 0; + if (used && !disk_entry_newer) { + /* log entry is old, ignore it */ + rb->counts.clashcount++; + continue; + } else if (used && e && e->swap_filen == s.swap_filen && e->swap_dirn == SD->index) { + /* swapfile taken, same URL, newer, update meta */ + if (e->store_status == STORE_OK) { + e->lastref = s.timestamp; + e->timestamp = s.timestamp; + e->expires = s.expires; + e->lastmod = s.lastmod; + e->flags = s.flags; + e->refcount += s.refcount; +#if HEAP_REPLACEMENT + storeHeapPositionUpdate(e, SD); + storeAufsDirUnrefObj(SD, e); +#endif + } else { + debug_trap("storeAufsDirRebuildFromSwapLog: bad condition"); + debug(20, 1) ("\tSee %s:%d\n", __FILE__, __LINE__); + } + continue; + } else if (used) { + /* swapfile in use, not by this URL, log entry is newer */ + /* This is sorta bad: the log entry should NOT be newer at this + * point. If the log is dirty, the filesize check should have + * caught this. If the log is clean, there should never be a + * newer entry. */ + debug(20, 1) ("WARNING: newer swaplog entry for dirno %d, fileno %08X\n", + SD->index, s.swap_filen); + /* I'm tempted to remove the swapfile here just to be safe, + * but there is a bad race condition in the NOVM version if + * the swapfile has recently been opened for writing, but + * not yet opened for reading. Because we can't map + * swapfiles back to StoreEntrys, we don't know the state + * of the entry using that file. */ + /* We'll assume the existing entry is valid, probably because + * were in a slow rebuild and the the swap file number got taken + * and the validation procedure hasn't run. */ + assert(rb->flags.need_to_validate); + rb->counts.clashcount++; + continue; + } else if (e && !disk_entry_newer) { + /* key already exists, current entry is newer */ + /* keep old, ignore new */ + rb->counts.dupcount++; + continue; + } else if (e) { + /* key already exists, this swapfile not being used */ + /* junk old, load new */ + storeExpireNow(e); + storeReleaseRequest(e); + storeAufsDirReplRemove(e); + storeRelease(e); + if (e->swap_filen > -1) { + /* Fake a unlink here, this is a bad hack :( */ + e->swap_filen = -1; + e->swap_dirn = -1; + } + rb->counts.dupcount++; + } else { + /* URL doesnt exist, swapfile not in use */ + /* load new */ + (void) 0; + } + /* update store_swap_size */ + rb->counts.objcount++; + e = storeAufsDirAddDiskRestore(SD, s.key, + s.swap_filen, + s.swap_file_sz, + s.expires, + s.timestamp, + s.lastref, + s.lastmod, + s.refcount, + s.flags, + (int) rb->flags.clean); + storeDirSwapLog(e, SWAP_LOG_ADD); + } + eventAdd("storeRebuild", storeAufsDirRebuildFromSwapLog, rb, 0.0, 1); +} + +static int +storeAufsDirGetNextFile(RebuildState * rb, int *sfileno, int *size) +{ + SwapDir *SD = rb->sd; + aioinfo_t *aioinfo = (aioinfo_t *)SD->fsdata; + int fd = -1; + int used = 0; + int dirs_opened = 0; + debug(20, 3) ("storeAufsDirGetNextFile: flag=%d, %d: /%02X/%02X\n", + rb->flags.init, + rb->sd->index, + rb->curlvl1, + rb->curlvl2); + if (rb->done) + return -2; + while (fd < 0 && rb->done == 0) { + fd = -1; + if (0 == rb->flags.init) { /* initialize, open first file */ + rb->done = 0; + rb->curlvl1 = 0; + rb->curlvl2 = 0; + rb->in_dir = 0; + rb->flags.init = 1; + assert(Config.cacheSwap.n_configured > 0); + } + if (0 == rb->in_dir) { /* we need to read in a new directory */ + snprintf(rb->fullpath, SQUID_MAXPATHLEN, "%s/%02X/%02X", + rb->sd->path, + rb->curlvl1, rb->curlvl2); + if (rb->flags.init && rb->td != NULL) + closedir(rb->td); + rb->td = NULL; + if (dirs_opened) + return -1; + rb->td = opendir(rb->fullpath); + dirs_opened++; + if (rb->td == NULL) { + debug(50, 1) ("storeAufsDirGetNextFile: opendir: %s: %s\n", + rb->fullpath, xstrerror()); + } else { + rb->entry = readdir(rb->td); /* skip . and .. */ + rb->entry = readdir(rb->td); + if (rb->entry == NULL && errno == ENOENT) + debug(20, 1) ("storeAufsDirGetNextFile: directory does not exist!.\n"); + debug(20, 3) ("storeAufsDirGetNextFile: Directory %s\n", rb->fullpath); + } + } + if (rb->td != NULL && (rb->entry = readdir(rb->td)) != NULL) { + rb->in_dir++; + if (sscanf(rb->entry->d_name, "%x", &rb->fn) != 1) { + debug(20, 3) ("storeAufsDirGetNextFile: invalid %s\n", + rb->entry->d_name); + continue; + } + if (!storeAufsFilenoBelongsHere(rb->fn, rb->sd->index, rb->curlvl1, rb->curlvl2)) { + debug(20, 3) ("storeAufsDirGetNextFile: %08X does not belong in %d/%d/%d\n", + rb->fn, rb->sd->index, rb->curlvl1, rb->curlvl2); + continue; + } + used = storeAufsDirMapBitTest(SD, rb->fn); + if (used) { + debug(20, 3) ("storeAufsDirGetNextFile: Locked, continuing with next.\n"); + continue; + } + snprintf(rb->fullfilename, SQUID_MAXPATHLEN, "%s/%s", + rb->fullpath, rb->entry->d_name); + debug(20, 3) ("storeAufsDirGetNextFile: Opening %s\n", rb->fullfilename); + fd = file_open(rb->fullfilename, O_RDONLY); + if (fd < 0) + debug(50, 1) ("storeAufsDirGetNextFile: %s: %s\n", rb->fullfilename, xstrerror()); + else + store_open_disk_fd++; + continue; + } + rb->in_dir = 0; + if (++rb->curlvl2 < aioinfo->l2) + continue; + rb->curlvl2 = 0; + if (++rb->curlvl1 < aioinfo->l1) + continue; + rb->curlvl1 = 0; + rb->done = 1; + } + *sfileno = rb->fn; + return fd; +} + +/* Add a new object to the cache with empty memory copy and pointer to disk + * use to rebuild store from disk. */ +static StoreEntry * +storeAufsDirAddDiskRestore(SwapDir *SD, const cache_key * key, + int file_number, + size_t swap_file_sz, + time_t expires, + time_t timestamp, + time_t lastref, + time_t lastmod, + u_num32 refcount, + u_short flags, + int clean) +{ + StoreEntry *e = NULL; + debug(20, 5) ("storeAufsAddDiskRestore: %s, fileno=%08X\n", storeKeyText(key), file_number); + /* if you call this you'd better be sure file_number is not + * already in use! */ + e = new_StoreEntry(STORE_ENTRY_WITHOUT_MEMOBJ, NULL, NULL); + e->store_status = STORE_OK; + storeSetMemStatus(e, NOT_IN_MEMORY); + e->swap_status = SWAPOUT_DONE; + e->swap_filen = file_number; + e->swap_dirn = SD->index; + e->swap_file_sz = swap_file_sz; + e->lock_count = 0; +#if !HEAP_REPLACEMENT + e->refcount = 0; +#endif + e->lastref = lastref; + e->timestamp = timestamp; + e->expires = expires; + e->lastmod = lastmod; + e->refcount = refcount; + e->flags = flags; + EBIT_SET(e->flags, ENTRY_CACHABLE); + EBIT_CLR(e->flags, RELEASE_REQUEST); + EBIT_CLR(e->flags, KEY_PRIVATE); + e->ping_status = PING_NONE; + EBIT_CLR(e->flags, ENTRY_VALIDATED); + storeAufsDirMapBitSet(SD, e->swap_filen); + storeHashInsert(e, key); /* do it after we clear KEY_PRIVATE */ + storeAufsDirReplAdd(SD, e); + return e; +} + +static void +storeAufsDirRebuild(SwapDir * sd) +{ + RebuildState *rb = xcalloc(1, sizeof(*rb)); + int clean = 0; + int zero = 0; + FILE *fp; + EVH *func = NULL; + rb->sd = sd; + rb->speed = opt_foreground_rebuild ? 1 << 30 : 50; + /* + * If the swap.state file exists in the cache_dir, then + * we'll use storeAufsDirRebuildFromSwapLog(), otherwise we'll + * use storeAufsDirRebuildFromDirectory() to open up each file + * and suck in the meta data. + */ + fp = storeAufsDirOpenTmpSwapLog(sd, &clean, &zero); + if (fp == NULL || zero) { + if (fp != NULL) + fclose(fp); + func = storeAufsDirRebuildFromDirectory; + } else { + func = storeAufsDirRebuildFromSwapLog; + rb->log = fp; + rb->flags.clean = (unsigned int) clean; + } + if (!clean) + rb->flags.need_to_validate = 1; + debug(20, 1) ("Rebuilding storage in %s (%s)\n", + sd->path, clean ? "CLEAN" : "DIRTY"); + store_dirs_rebuilding++; + cbdataAdd(rb, cbdataXfree, 0); + eventAdd("storeRebuild", func, rb, 0.0, 1); +} + +static void +storeAufsDirCloseTmpSwapLog(SwapDir * sd) +{ + aioinfo_t *aioinfo = (aioinfo_t *)sd->fsdata; + char *swaplog_path = xstrdup(storeAufsDirSwapLogFile(sd, NULL)); + char *new_path = xstrdup(storeAufsDirSwapLogFile(sd, ".new")); + int fd; + file_close(aioinfo->swaplog_fd); +#ifdef _SQUID_OS2_ + if (unlink(swaplog_path) < 0) { + debug(50, 0) ("%s: %s\n", swaplog_path, xstrerror()); + fatal("storeAufsDirCloseTmpSwapLog: unlink failed"); + } +#endif + if (xrename(new_path, swaplog_path) < 0) { + fatal("storeAufsDirCloseTmpSwapLog: rename failed"); + } + fd = file_open(swaplog_path, O_WRONLY | O_CREAT); + if (fd < 0) { + debug(50, 1) ("%s: %s\n", swaplog_path, xstrerror()); + fatal("storeAufsDirCloseTmpSwapLog: Failed to open swap log."); + } + safe_free(swaplog_path); + safe_free(new_path); + aioinfo->swaplog_fd = fd; + debug(47, 3) ("Cache Dir #%d log opened on FD %d\n", sd->index, fd); +} + +static FILE * +storeAufsDirOpenTmpSwapLog(SwapDir * sd, int *clean_flag, int *zero_flag) +{ + aioinfo_t *aioinfo = (aioinfo_t *)sd->fsdata; + char *swaplog_path = xstrdup(storeAufsDirSwapLogFile(sd, NULL)); + char *clean_path = xstrdup(storeAufsDirSwapLogFile(sd, ".last-clean")); + char *new_path = xstrdup(storeAufsDirSwapLogFile(sd, ".new")); + struct stat log_sb; + struct stat clean_sb; + FILE *fp; + int fd; + if (stat(swaplog_path, &log_sb) < 0) { + debug(47, 1) ("Cache Dir #%d: No log file\n", sd->index); + safe_free(swaplog_path); + safe_free(clean_path); + safe_free(new_path); + return NULL; + } + *zero_flag = log_sb.st_size == 0 ? 1 : 0; + /* close the existing write-only FD */ + if (aioinfo->swaplog_fd >= 0) + file_close(aioinfo->swaplog_fd); + /* open a write-only FD for the new log */ + fd = file_open(new_path, O_WRONLY | O_CREAT | O_TRUNC); + if (fd < 0) { + debug(50, 1) ("%s: %s\n", new_path, xstrerror()); + fatal("storeDirOpenTmpSwapLog: Failed to open swap log."); + } + aioinfo->swaplog_fd = fd; + /* open a read-only stream of the old log */ + fp = fopen(swaplog_path, "r"); + if (fp == NULL) { + debug(50, 0) ("%s: %s\n", swaplog_path, xstrerror()); + fatal("Failed to open swap log for reading"); + } + memset(&clean_sb, '\0', sizeof(struct stat)); + if (stat(clean_path, &clean_sb) < 0) + *clean_flag = 0; + else if (clean_sb.st_mtime < log_sb.st_mtime) + *clean_flag = 0; + else + *clean_flag = 1; + safeunlink(clean_path, 1); + safe_free(swaplog_path); + safe_free(clean_path); + safe_free(new_path); + return fp; +} + +struct _clean_state { + char *cur; + char *new; + char *cln; + char *outbuf; + off_t outbuf_offset; + int fd; +}; + +#define CLEAN_BUF_SZ 16384 +/* + * Begin the process to write clean cache state. For ASYNCUFS this means + * opening some log files and allocating write buffers. Return 0 if + * we succeed, and assign the 'func' and 'data' return pointers. + */ +static int +storeAufsDirWriteCleanOpen(SwapDir * sd) +{ + struct _clean_state *state = xcalloc(1, sizeof(*state)); + struct stat sb; + sd->log.clean.write = NULL; + sd->log.clean.state = NULL; + state->cur = xstrdup(storeAufsDirSwapLogFile(sd, NULL)); + state->new = xstrdup(storeAufsDirSwapLogFile(sd, ".clean")); + state->cln = xstrdup(storeAufsDirSwapLogFile(sd, ".last-clean")); + state->outbuf = xcalloc(CLEAN_BUF_SZ, 1); + state->outbuf_offset = 0; + unlink(state->new); + unlink(state->cln); + state->fd = file_open(state->new, O_WRONLY | O_CREAT | O_TRUNC); + if (state->fd < 0) + return -1; + debug(20, 3) ("storeDirWriteCleanLogs: opened %s, FD %d\n", + state->new, state->fd); +#if HAVE_FCHMOD + if (stat(state->cur, &sb) == 0) + fchmod(state->fd, sb.st_mode); +#endif + sd->log.clean.write = storeAufsDirWriteCleanEntry; + sd->log.clean.state = state; + return 0; +} + +/* + * "write" an entry to the clean log file. + */ +static void +storeAufsDirWriteCleanEntry(const StoreEntry * e, SwapDir * sd) +{ + storeSwapLogData s; + static size_t ss = sizeof(storeSwapLogData); + struct _clean_state *state = sd->log.clean.state; + if (NULL == e) { + storeAufsDirWriteCleanClose(sd); + return; + } + memset(&s, '\0', ss); + s.op = (char) SWAP_LOG_ADD; + s.swap_filen = e->swap_filen; + s.timestamp = e->timestamp; + s.lastref = e->lastref; + s.expires = e->expires; + s.lastmod = e->lastmod; + s.swap_file_sz = e->swap_file_sz; + s.refcount = e->refcount; + s.flags = e->flags; + xmemcpy(&s.key, e->key, MD5_DIGEST_CHARS); + xmemcpy(state->outbuf + state->outbuf_offset, &s, ss); + state->outbuf_offset += ss; + /* buffered write */ + if (state->outbuf_offset + ss > CLEAN_BUF_SZ) { + if (write(state->fd, state->outbuf, state->outbuf_offset) < 0) { + debug(50, 0) ("storeDirWriteCleanLogs: %s: write: %s\n", + state->new, xstrerror()); + debug(20, 0) ("storeDirWriteCleanLogs: Current swap logfile not replaced.\n"); + file_close(state->fd); + state->fd = -1; + unlink(state->new); + safe_free(state); + sd->log.clean.state = NULL; + sd->log.clean.write = NULL; + } + state->outbuf_offset = 0; + } +} + +static void +storeAufsDirWriteCleanClose(SwapDir * sd) +{ + struct _clean_state *state = sd->log.clean.state; + if (state->fd < 0) + return; + if (write(state->fd, state->outbuf, state->outbuf_offset) < 0) { + debug(50, 0) ("storeDirWriteCleanLogs: %s: write: %s\n", + state->new, xstrerror()); + debug(20, 0) ("storeDirWriteCleanLogs: Current swap logfile " + "not replaced.\n"); + file_close(state->fd); + state->fd = -1; + unlink(state->new); + } + safe_free(state->outbuf); + /* + * You can't rename open files on Microsoft "operating systems" + * so we have to close before renaming. + */ + storeAufsDirCloseSwapLog(sd); + /* rename */ + if (state->fd >= 0) { +#ifdef _SQUID_OS2_ + file_close(state->fd); + state->fd = -1; + if (unlink(cur) < 0) + debug(50, 0) ("storeDirWriteCleanLogs: unlinkd failed: %s, %s\n", + xstrerror(), cur); +#endif + xrename(state->new, state->cur); + } + /* touch a timestamp file if we're not still validating */ + if (store_dirs_rebuilding) + (void) 0; + else if (state->fd < 0) + (void) 0; + else + file_close(file_open(state->cln, O_WRONLY | O_CREAT | O_TRUNC)); + /* close */ + safe_free(state->cur); + safe_free(state->new); + safe_free(state->cln); + if (state->fd >= 0) + file_close(state->fd); + state->fd = -1; + safe_free(state); + sd->log.clean.state = NULL; + sd->log.clean.write = NULL; +} + +static void +storeAufsDirSwapLog(const SwapDir * sd, const StoreEntry * e, int op) +{ + aioinfo_t *aioinfo = (aioinfo_t *)sd->fsdata; + storeSwapLogData *s = xcalloc(1, sizeof(storeSwapLogData)); + s->op = (char) op; + s->swap_filen = e->swap_filen; + s->timestamp = e->timestamp; + s->lastref = e->lastref; + s->expires = e->expires; + s->lastmod = e->lastmod; + s->swap_file_sz = e->swap_file_sz; + s->refcount = e->refcount; + s->flags = e->flags; + xmemcpy(s->key, e->key, MD5_DIGEST_CHARS); + file_write(aioinfo->swaplog_fd, + -1, + s, + sizeof(storeSwapLogData), + NULL, + NULL, + xfree); +} + +static void +storeAufsDirNewfs(SwapDir * sd) +{ + debug(47, 3) ("Creating swap space in %s\n", sd->path); + storeAufsDirCreateDirectory(sd->path, 0); + storeAufsDirCreateSwapSubDirs(sd); +} + +static int +rev_int_sort(const void *A, const void *B) +{ + const int *i1 = A; + const int *i2 = B; + return *i2 - *i1; +} + +static int +storeAufsDirClean(int swap_index) +{ + DIR *dp = NULL; + struct dirent *de = NULL; + LOCAL_ARRAY(char, p1, MAXPATHLEN + 1); + LOCAL_ARRAY(char, p2, MAXPATHLEN + 1); +#if USE_TRUNCATE + struct stat sb; +#endif + int files[20]; + int swapfileno; + int fn; /* same as swapfileno, but with dirn bits set */ + int n = 0; + int k = 0; + int N0, N1, N2; + int D0, D1, D2; + SwapDir *SD; + aioinfo_t *aioinfo; + N0 = n_asyncufs_dirs; + D0 = asyncufs_dir_index[swap_index % N0]; + SD = &Config.cacheSwap.swapDirs[D0]; + aioinfo = (aioinfo_t *)SD->fsdata; + N1 = aioinfo->l1; + D1 = (swap_index / N0) % N1; + N2 = aioinfo->l2; + D2 = ((swap_index / N0) / N1) % N2; + snprintf(p1, SQUID_MAXPATHLEN, "%s/%02X/%02X", + Config.cacheSwap.swapDirs[D0].path, D1, D2); + debug(36, 3) ("storeDirClean: Cleaning directory %s\n", p1); + dp = opendir(p1); + if (dp == NULL) { + if (errno == ENOENT) { + debug(36, 0) ("storeDirClean: WARNING: Creating %s\n", p1); + if (mkdir(p1, 0777) == 0) + return 0; + } + debug(50, 0) ("storeDirClean: %s: %s\n", p1, xstrerror()); + safeunlink(p1, 1); + return 0; + } + while ((de = readdir(dp)) != NULL && k < 20) { + if (sscanf(de->d_name, "%X", &swapfileno) != 1) + continue; + fn = swapfileno; /* XXX should remove this cruft ! */ + if (storeAufsDirValidFileno(SD, fn)) + if (storeAufsDirMapBitTest(SD, fn)) + if (storeAufsFilenoBelongsHere(fn, D0, D1, D2)) + continue; +#if USE_TRUNCATE + if (!stat(de->d_name, &sb)) + if (sb.st_size == 0) + continue; +#endif + files[k++] = swapfileno; + } + closedir(dp); + if (k == 0) + return 0; + qsort(files, k, sizeof(int), rev_int_sort); + if (k > 10) + k = 10; + for (n = 0; n < k; n++) { + debug(36, 3) ("storeDirClean: Cleaning file %08X\n", files[n]); + snprintf(p2, MAXPATHLEN + 1, "%s/%08X", p1, files[n]); +#if USE_TRUNCATE + truncate(p2, 0); +#else + safeunlink(p2, 0); +#endif + Counter.swap_files_cleaned++; + } + debug(36, 3) ("Cleaned %d unused files from %s\n", k, p1); + return k; +} + +static void +storeAufsDirCleanEvent(void *unused) +{ + static int swap_index = 0; + int i; + int j = 0; + int n = 0; + /* + * Assert that there are ASYNCUFS cache_dirs configured, otherwise + * we should never be called. + */ + assert(n_asyncufs_dirs); + if (NULL == asyncufs_dir_index) { + SwapDir *sd; + aioinfo_t *aioinfo; + /* + * Initialize the little array that translates ASYNCUFS cache_dir + * number into the Config.cacheSwap.swapDirs array index. + */ + asyncufs_dir_index = xcalloc(n_asyncufs_dirs, sizeof(*asyncufs_dir_index)); + for (i = 0, n = 0; i < Config.cacheSwap.n_configured; i++) { + sd = &Config.cacheSwap.swapDirs[i]; + if (!storeAufsDirIs(sd)) + continue; + asyncufs_dir_index[n++] = i; + aioinfo = (aioinfo_t *)sd->fsdata; + j += (aioinfo->l1 * aioinfo->l2); + } + assert(n == n_asyncufs_dirs); + /* + * Start the storeAufsDirClean() swap_index with a random + * value. j equals the total number of ASYNCUFS level 2 + * swap directories + */ + swap_index = (int) (squid_random() % j); + } + if (0 == store_dirs_rebuilding) { + n = storeAufsDirClean(swap_index); + swap_index++; + } + eventAdd("storeDirClean", storeAufsDirCleanEvent, NULL, + 15.0 * exp(-0.25 * n), 1); +} + +static int +storeAufsDirIs(SwapDir * sd) +{ + if (strncmp(sd->type, "aufs", 3) == 0) + return 1; + return 0; +} + +/* + * Does swapfile number 'fn' belong in cachedir #F0, + * level1 dir #F1, level2 dir #F2? + */ +static int +storeAufsFilenoBelongsHere(int fn, int F0, int F1, int F2) +{ + int D1, D2; + int L1, L2; + int filn = fn; + aioinfo_t *aioinfo; + assert(F0 < Config.cacheSwap.n_configured); + aioinfo = (aioinfo_t *)Config.cacheSwap.swapDirs[F0].fsdata; + L1 = aioinfo->l1; + L2 = aioinfo->l2; + D1 = ((filn / L2) / L2) % L1; + if (F1 != D1) + return 0; + D2 = (filn / L2) % L2; + if (F2 != D2) + return 0; + return 1; +} + +int +storeAufsDirValidFileno(SwapDir *SD, sfileno filn) +{ + aioinfo_t *aioinfo = (aioinfo_t *)SD->fsdata; + if (filn < 0) + return 0; + if (filn > aioinfo->map->max_n_files) + return 0; + return 1; +} + +void +storeAufsDirMaintain(SwapDir *SD) +{ + StoreEntry *e = NULL; + int scanned = 0; + int locked = 0; + int expired = 0; + int max_scan; + int max_remove; + double f; + static time_t last_warn_time = 0; +#if !HEAP_REPLACEMENT + dlink_node *m; + dlink_node *prev = NULL; +#else + heap_key age; + heap_key min_age = 0.0; + link_list *locked_entries = NULL; +#if HEAP_REPLACEMENT_DEBUG + if (!verify_heap_property(SD->repl.heap.heap)) { + debug(20, 1) ("Heap property violated!\n"); + } +#endif +#endif + /* We can't delete objects while rebuilding swap */ + if (store_dirs_rebuilding) { + return; + } else { + f = (double) (store_swap_size - store_swap_low) / (store_swap_high - store_swap_low); + f = f < 0.0 ? 0.0 : f > 1.0 ? 1.0 : f; + max_scan = (int) (f * 400.0 + 100.0); + max_remove = (int) (f * 70.0 + 10.0); + /* + * This is kinda cheap, but so we need this priority hack? + */ +#if 0 + eventAdd("MaintainSwapSpace", storeMaintainSwapSpace, NULL, 1.0 - f, 1); +#endif + } + debug(20, 3) ("storeMaintainSwapSpace: f=%f, max_scan=%d, max_remove=%d\n", f, max_scan, max_remove); +#if HEAP_REPLACEMENT + while (heap_nodes(SD->repl.heap.heap) > 0) { + if (store_swap_size < store_swap_low) + break; + if (expired >= max_remove) + break; + if (scanned >= max_scan) + break; + age = heap_peepminkey(SD->repl.heap.heap); + e = heap_extractmin(SD->repl.heap.heap); + e->repl.node = NULL; /* no longer in the heap */ + scanned++; + if (storeEntryLocked(e)) { + /* + * Entry is in use ... put it in a linked list to ignore it. + */ + if (!EBIT_TEST(e->flags, ENTRY_SPECIAL)) { + /* + * If this was a "SPECIAL" do not add it back into the heap. + * It will always be "SPECIAL" and therefore never removed. + */ + debug(20, 4) ("storeAufsDirMaintain: locked url %s\n", + (e->mem_obj && e->mem_obj->url) ? e->mem_obj->url : storeKeyText(e-> +key)); + linklistPush(&locked_entries, e); + } + locked++; + continue; + } else if (storeAufsDirCheckExpired(SD, e)) { + /* + * Note: This will not check the reference age ifdef + * HEAP_REPLACEMENT, but it does some other useful + * checks... + */ + expired++; + debug(20, 3) ("Released store object age %f size %d refs %d key %s\n", + age, e->swap_file_sz, e->refcount, storeKeyText(e->key)); + min_age = age; + storeRelease(e); + } else { + /* + * Did not expire the object so we need to add it back + * into the heap! + */ + debug(20, 5) ("storeMaintainSwapSpace: non-expired %s\n", + storeKeyText(e->key)); + linklistPush(&locked_entries, e); + continue; + } + if (store_swap_size < store_swap_low) + break; + else if (expired >= max_remove) + break; + else if (scanned >= max_scan) + break; + } + /* + * Bump the heap age factor. + */ + if (min_age > 0.0) + SD->repl.heap.heap->age = min_age; + /* + * Reinsert all bumped locked entries back into heap... + */ + while ((e = linklistShift(&locked_entries))) + e->repl.node = heap_insert(SD->repl.heap.heap, e); +#else + for (m = SD->repl.lru.list.tail; m; m = prev) { + prev = m->prev; + e = m->data; + scanned++; + if (storeEntryLocked(e)) { + /* + * If there is a locked entry at the tail of the LRU list, + * move it to the beginning to get it out of the way. + * Theoretically, we might have all locked objects at the + * tail, and then we'll never remove anything here and the + * LRU age will go to zero. + */ + if (memInUse(MEM_STOREENTRY) > max_scan) { + dlinkDelete(&e->repl.lru, &SD->repl.lru.list); + dlinkAdd(e, &e->repl.lru, &SD->repl.lru.list); + } + locked++; + + } else if (storeAufsDirCheckExpired(SD, e)) { + expired++; + storeRelease(e); + } + if (expired >= max_remove) + break; + if (scanned >= max_scan) + break; + } +#endif + debug(20, (expired ? 2 : 3)) ("storeMaintainSwapSpace: scanned %d/%d removed %d/%d l +ocked %d f=%.03f\n", + scanned, max_scan, expired, max_remove, locked, f); + debug(20, 3) ("storeMaintainSwapSpace stats:\n"); + debug(20, 3) (" %6d objects\n", memInUse(MEM_STOREENTRY)); + debug(20, 3) (" %6d were scanned\n", scanned); + debug(20, 3) (" %6d were locked\n", locked); + debug(20, 3) (" %6d were expired\n", expired); + if (store_swap_size < Config.Swap.maxSize) + return; + if (squid_curtime - last_warn_time < 10) + return; + debug(20, 0) ("WARNING: Disk space over limit: %d KB > %d KB\n", + store_swap_size, Config.Swap.maxSize); + last_warn_time = squid_curtime; +} + +/* + * storeAufsDirCheckObj + * + * This routine is called by storeDirSelectSwapDir to see if the given + * object is able to be stored on this filesystem. AUFS filesystems will + * happily store anything as long as the LRU time isn't too small. + */ +int +storeAufsDirCheckObj(SwapDir *SD, const StoreEntry *e) +{ + int loadav; + int ql; + +#if !HEAP_REPLACEMENT + if (storeAufsDirExpiredReferenceAge(SD) < 300) { + debug(20, 3) ("storeAufsDirCheckObj: NO: LRU Age = %d\n", + storeAufsDirExpiredReferenceAge(SD)); + /* store_check_cachable_hist.no.lru_age_too_low++; */ + return -1; + } +#endif + ql = aioQueueSize(); + if (ql == 0) + loadav = 0; + else if (ql >= MAGIC1) /* Queue is too long, don't even consider it */ + loadav = -1; + else + loadav = MAGIC1 * 1000 / ql; + return loadav; +} + +/* + * storeAufsDirRefObj + * + * This routine is called whenever an object is referenced, so we can + * maintain replacement information within the storage fs. + */ +void +storeAufsDirRefObj(SwapDir *SD, StoreEntry *e) +{ + debug(1, 3) ("storeAufsDirRefObj: referencing %d/%d\n", e->swap_dirn, + e->swap_filen); +#if HEAP_REPLACEMENT + /* Nothing to do here */ +#else + /* Reference the object */ + if (!EBIT_TEST(e->flags, RELEASE_REQUEST) && + !EBIT_TEST(e->flags, ENTRY_SPECIAL)) { + dlinkDelete(&e->repl.lru, &SD->repl.lru.list); + dlinkAdd(e, &e->repl.lru, &SD->repl.lru.list); + } +#endif +} + +/* + * storeAufsDirUnrefObj + * This routine is called whenever the last reference to an object is + * removed, to maintain replacement information within the storage fs. + */ +void +storeAufsDirUnrefObj(SwapDir *SD, StoreEntry *e) +{ + debug(1, 3) ("storeAufsDirUnrefObj: referencing %d/%d\n", e->swap_dirn, + e->swap_filen); +#if HEAP_REPLACEMENT + if (e->repl.node) + heap_update(SD->repl.heap.heap, e->repl.node, e); +#endif +} + +/* + * storeAufsDirUnlinkFile + * + * This routine unlinks a file and pulls it out of the bitmap. + * It used to be in storeAufsUnlink(), however an interface change + * forced this bit of code here. Eeek. + */ +void +storeAufsDirUnlinkFile(SwapDir *SD, sfileno f) +{ + debug(79, 3) ("storeAufsDirUnlinkFile: unlinking fileno %08X\n", f); + storeAufsDirMapBitReset(SD, f); + aioUnlink(storeAufsDirFullPath(SD, f, NULL), NULL, NULL); +} + +#if !HEAP_REPLACEMENT +/* + * storeAufsDirExpiredReferenceAge + * + * The LRU age is scaled exponentially between 1 minute and + * Config.referenceAge , when store_swap_low < store_swap_size < + * store_swap_high. This keeps store_swap_size within the low and high + * water marks. If the cache is very busy then store_swap_size stays + * closer to the low water mark, if it is not busy, then it will stay + * near the high water mark. The LRU age value can be examined on the + * cachemgr 'info' page. + */ +static time_t +storeAufsDirExpiredReferenceAge(SwapDir *SD) +{ + double x; + double z; + time_t age; + long store_high, store_low; + + store_high = (long) (((float) SD->max_size * + (float) Config.Swap.highWaterMark) / (float) 100); + store_low = (long) (((float) SD->max_size * + (float) Config.Swap.lowWaterMark) / (float) 100); + debug(20, 20) ("RA: Dir %s, hi=%d, lo=%d, cur=%d\n", SD->path, store_high, store_low, SD->cur_size); + + x = (double) (store_high - SD->cur_size) / + (store_high - store_low); + x = x < 0.0 ? 0.0 : x > 1.0 ? 1.0 : x; + z = pow((double) (Config.referenceAge / 60), x); + age = (time_t) (z * 60.0); + if (age < 60) + age = 60; + else if (age > Config.referenceAge) + age = Config.referenceAge; + return age; +} +#endif + +/* + * storeAufsDirCheckExpired + * + * Check whether the given object is expired or not + * It breaks layering a little by calling the upper layers to find + * out whether the object is locked or not, but we can't help this + * right now. + */ +static int +storeAufsDirCheckExpired(SwapDir *SD, StoreEntry *e) +{ + if (storeEntryLocked(e)) + return 0; + if (EBIT_TEST(e->flags, RELEASE_REQUEST)) + return 1; + if (EBIT_TEST(e->flags, ENTRY_NEGCACHED) && squid_curtime >= e->expires) + return 1; + +#if HEAP_REPLACEMENT + /* + * with HEAP_REPLACEMENT we are not using the LRU reference age, the heap + * controls the replacement of objects. + */ + return 1; +#else + if (squid_curtime - e->lastref > storeAufsDirExpiredReferenceAge(SD)) + return 1; + return 0; +#endif +} + +/* + * Add and remove the given StoreEntry from the replacement policy in + * use. + */ + +void +storeAufsDirReplAdd(SwapDir *SD, StoreEntry *e) +{ + debug(20, 4) ("storeUfsDirReplAdd: added node %p to dir %d\n", e, + SD->index); +#if HEAP_REPLACEMENT + if (EBIT_TEST(e->flags, ENTRY_SPECIAL)) { + (void) 0; + } else { + e->repl.node = heap_insert(SD->repl.heap.heap, e); + debug(20, 4) ("storeUfsDirReplAdd: inserted node 0x%x\n", e->repl.node); + } +#else + /* Shouldn't we not throw special objects into the lru ? */ + dlinkAdd(e, &e->repl.lru, &SD->repl.lru.list); +#endif +} + + +void +storeAufsDirReplRemove(StoreEntry *e) +{ + SwapDir *SD = INDEXSD(e->swap_dirn); + debug(20, 4) ("storeUfsDirReplRemove: remove node %p from dir %d\n", e, + SD->index); +#if HEAP_REPLACEMENT + /* And now, release the object from the replacement policy */ + if (e->repl.node) { + debug(20, 4) ("storeUfsDirReplRemove: deleting node 0x%x\n", + e->repl.node); + heap_delete(SD->repl.heap.heap, e->repl.node); + e->repl.node = NULL; + } +#else + dlinkDelete(&e->repl.lru, &SD->repl.lru.list); +#endif +} + + +/* ========== LOCAL FUNCTIONS ABOVE, GLOBAL FUNCTIONS BELOW ========== */ + +void +storeAufsDirStats(SwapDir *SD, StoreEntry * sentry) +{ + aioinfo_t *aioinfo; +#if HAVE_STATVFS + struct statvfs sfs; +#endif + aioinfo = (aioinfo_t *)SD->fsdata; + storeAppendPrintf(sentry, "First level subdirectories: %d\n", aioinfo->l1); + storeAppendPrintf(sentry, "Second level subdirectories: %d\n", aioinfo->l2); + storeAppendPrintf(sentry, "Maximum Size: %d KB\n", SD->max_size); + storeAppendPrintf(sentry, "Current Size: %d KB\n", SD->cur_size); + storeAppendPrintf(sentry, "Percent Used: %0.2f%%\n", + 100.0 * SD->cur_size / SD->max_size); + storeAppendPrintf(sentry, "Filemap bits in use: %d of %d (%d%%)\n", + aioinfo->map->n_files_in_map, aioinfo->map->max_n_files, + percent(aioinfo->map->n_files_in_map, aioinfo->map->max_n_files)); +#if HAVE_STATVFS +#define fsbtoblk(num, fsbs, bs) \ + (((fsbs) != 0 && (fsbs) < (bs)) ? \ + (num) / ((bs) / (fsbs)) : (num) * ((fsbs) / (bs))) + if (!statvfs(SD->path, &sfs)) { + storeAppendPrintf(sentry, "Filesystem Space in use: %d/%d KB (%d%%)\n", + fsbtoblk((sfs.f_blocks - sfs.f_bfree), sfs.f_frsize, 1024), + fsbtoblk(sfs.f_blocks, sfs.f_frsize, 1024), + percent(sfs.f_blocks - sfs.f_bfree, sfs.f_blocks)); + storeAppendPrintf(sentry, "Filesystem Inodes in use: %d/%d (%d%%)\n", + sfs.f_files - sfs.f_ffree, sfs.f_files, + percent(sfs.f_files - sfs.f_ffree, sfs.f_files)); + } +#endif + storeAppendPrintf(sentry, "Flags:"); + if (SD->flags.selected) + storeAppendPrintf(sentry, " SELECTED"); + if (SD->flags.read_only) + storeAppendPrintf(sentry, " READ-ONLY"); + storeAppendPrintf(sentry, "\n"); +#if !HEAP_REPLACEMENT + storeAppendPrintf(sentry, "LRU Expiration Age: %6.2f days\n", + (double) storeAufsDirExpiredReferenceAge(SD) / 86400.0); +#else +#if 0 + storeAppendPrintf(sentry, "Storage Replacement Threshold:\t%f\n", + heap_peepminkey(sd.repl.heap.heap)); +#endif +#endif +} + +/* + * storeAufsDirReconfigure + * + * This routine is called when the given swapdir needs reconfiguring + */ +void +storeAufsDirReconfigure(SwapDir *sd, int index, char *path) +{ + char *token; + int i; + int size; + int l1; + int l2; + unsigned int read_only = 0; + + i = GetInteger(); + size = i << 10; /* Mbytes to kbytes */ + if (size <= 0) + fatal("storeAufsDirReconfigure: invalid size value"); + i = GetInteger(); + l1 = i; + if (l1 <= 0) + fatal("storeAufsDirReconfigure: invalid level 1 directories value"); + i = GetInteger(); + l2 = i; + if (l2 <= 0) + fatal("storeAufsDirReconfigure: invalid level 2 directories value"); + if ((token = strtok(NULL, w_space))) + if (!strcasecmp(token, "read-only")) + read_only = 1; + + /* just reconfigure it */ + if (size == sd->max_size) + debug(3, 1) ("Cache dir '%s' size remains unchanged at %d KB\n", + path, size); + else + debug(3, 1) ("Cache dir '%s' size changed to %d KB\n", + path, size); + sd->max_size = size; + if (sd->flags.read_only != read_only) + debug(3, 1) ("Cache dir '%s' now %s\n", + path, read_only ? "Read-Only" : "Read-Write"); + sd->flags.read_only = read_only; + return; +} + +void +storeAufsDirDump(StoreEntry * entry, const char *name, SwapDir * s) +{ + aioinfo_t *aioinfo = (aioinfo_t *)s->fsdata; + storeAppendPrintf(entry, "%s %s %s %d %d %d\n", + name, + "asyncufs", + s->path, + s->max_size >> 10, + aioinfo->l1, + aioinfo->l2); +} + +/* + * Only "free" the filesystem specific stuff here + */ +static void +storeAufsDirFree(SwapDir * s) +{ + aioinfo_t *aioinfo = (aioinfo_t *)s->fsdata; + if (aioinfo->swaplog_fd > -1) { + file_close(aioinfo->swaplog_fd); + aioinfo->swaplog_fd = -1; + } + filemapFreeMemory(aioinfo->map); + xfree(aioinfo); + s->fsdata = NULL; /* Will aid debugging... */ + +} + +char * +storeAufsDirFullPath(SwapDir *SD, sfileno filn, char *fullpath) +{ + LOCAL_ARRAY(char, fullfilename, SQUID_MAXPATHLEN); + aioinfo_t *aioinfo = (aioinfo_t *)SD->fsdata; + int L1 = aioinfo->l1; + int L2 = aioinfo->l2; + if (!fullpath) + fullpath = fullfilename; + fullpath[0] = '\0'; + snprintf(fullpath, SQUID_MAXPATHLEN, "%s/%02X/%02X/%08X", + SD->path, + ((filn / L2) / L2) % L1, + (filn / L2) % L2, + filn); + return fullpath; +} + +/* + * storeAufsCleanupDoubleCheck + * + * This is called by storeCleanup() if -S was given on the command line. + */ +static int +storeAufsCleanupDoubleCheck(SwapDir *sd, StoreEntry *e) +{ + struct stat sb; + + if (stat(storeAufsDirFullPath(sd, e->swap_filen, NULL), &sb) < 0) { + debug(20, 0) ("storeAufsCleanupDoubleCheck: MISSING SWAP FILE\n"); + debug(20, 0) ("storeAufsCleanupDoubleCheck: FILENO %08X\n", e->swap_filen); + debug(20, 0) ("storeAufsCleanupDoubleCheck: PATH %s\n", + storeAufsDirFullPath(sd, e->swap_filen, NULL)); + storeEntryDump(e, 0); + return -1; + } + if (e->swap_file_sz != sb.st_size) { + debug(20, 0) ("storeAufsCleanupDoubleCheck: SIZE MISMATCH\n"); + debug(20, 0) ("storeAufsCleanupDoubleCheck: FILENO %08X\n", e->swap_filen); + debug(20, 0) ("storeAufsCleanupDoubleCheck: PATH %s\n", + storeAufsDirFullPath(sd, e->swap_filen, NULL)); + debug(20, 0) ("storeAufsCleanupDoubleCheck: ENTRY SIZE: %d, FILE SIZE: %d\n", + e->swap_file_sz, (int) sb.st_size); + storeEntryDump(e, 0); + return -1; + } + return 0; +} + +/* + * storeAufsDirParse + * + * Called when a *new* fs is being setup. + */ +void +storeAufsDirParse(SwapDir *sd, int index, char *path) +{ + char *token; + int i; + int size; + int l1; + int l2; + unsigned int read_only = 0; + aioinfo_t *aioinfo; + + i = GetInteger(); + size = i << 10; /* Mbytes to kbytes */ + if (size <= 0) + fatal("storeAufsDirParse: invalid size value"); + i = GetInteger(); + l1 = i; + if (l1 <= 0) + fatal("storeAufsDirParse: invalid level 1 directories value"); + i = GetInteger(); + l2 = i; + if (l2 <= 0) + fatal("storeAufsDirParse: invalid level 2 directories value"); + if ((token = strtok(NULL, w_space))) + if (!strcasecmp(token, "read-only")) + read_only = 1; + + aioinfo = xmalloc(sizeof(aioinfo_t)); + if (aioinfo == NULL) + fatal("storeAufsDirParse: couldn't xmalloc() aioinfo_t!\n"); + + sd->index = index; + sd->path = xstrdup(path); + sd->max_size = size; + sd->fsdata = aioinfo; + aioinfo->l1 = l1; + aioinfo->l2 = l2; + aioinfo->swaplog_fd = -1; + aioinfo->map = NULL; /* Debugging purposes */ + aioinfo->suggest = 0; + sd->flags.read_only = read_only; + sd->init = storeAufsDirInit; + sd->newfs = storeAufsDirNewfs; + sd->dump = storeAufsDirDump; + sd->freefs = storeAufsDirFree; + sd->dblcheck = storeAufsCleanupDoubleCheck; + sd->statfs = storeAufsDirStats; + sd->maintainfs = storeAufsDirMaintain; + sd->checkobj = storeAufsDirCheckObj; + sd->refobj = storeAufsDirRefObj; + sd->unrefobj = storeAufsDirUnrefObj; + sd->callback = aioCheckCallbacks; + sd->sync = aioSync; + sd->obj.create = storeAufsCreate; + sd->obj.open = storeAufsOpen; + sd->obj.close = storeAufsClose; + sd->obj.read = storeAufsRead; + sd->obj.write = storeAufsWrite; + sd->obj.unlink = storeAufsUnlink; + sd->log.open = storeAufsDirOpenSwapLog; + sd->log.close = storeAufsDirCloseSwapLog; + sd->log.write = storeAufsDirSwapLog; + sd->log.clean.open = storeAufsDirWriteCleanOpen; + + /* Initialise replacement policy stuff */ +#if HEAP_REPLACEMENT + /* + * Create new heaps with cache replacement policies attached to them. + * The cache replacement policy is specified as either GDSF or LFUDA in + * the squid.conf configuration file. Note that the replacement policy + * applies only to the disk replacement algorithm. Memory replacement + * always uses GDSF since we want to maximize object hit rate. + */ + if (Config.replPolicy) { + if (tolower(Config.replPolicy[0]) == 'g') { + debug(20, 1) ("Using GDSF disk replacement policy\n"); + sd->repl.heap.heap = new_heap(10000, HeapKeyGen_StoreEntry_GDSF); + } else if (tolower(Config.replPolicy[0]) == 'l') { + if (tolower(Config.replPolicy[1]) == 'f') { + debug(20, 1) ("Using LFUDA disk replacement policy\n"); + sd->repl.heap.heap = new_heap(10000, HeapKeyGen_StoreEntry_LFUDA); + } else if (tolower(Config.replPolicy[1]) == 'r') { + debug(20, 1) ("Using LRU heap disk replacement policy\n"); + sd->repl.heap.heap = new_heap(10000, HeapKeyGen_StoreEntry_LRU); + } + } else { + debug(20, 1) ("Unrecognized replacement_policy; using GDSF\n"); + sd->repl.heap.heap = new_heap(10000, HeapKeyGen_StoreEntry_GDSF); + } + } else { + debug(20, 1) ("Using default disk replacement policy (GDSF)\n"); + sd->repl.heap.heap = new_heap(10000, HeapKeyGen_StoreEntry_GDSF); + } +#else + sd->repl.lru.list.head = NULL; + sd->repl.lru.list.tail = NULL; +#endif +} + +void +storeFsSetup_aufs(storefs_entry_t *storefs) +{ + storefs->parsefunc = storeAufsDirParse; + storefs->reconfigurefunc = storeAufsDirReconfigure; + storefs->donefunc = aioDone; + aioInit(); +} diff --git a/src/fs/aufs/store_io_aufs.cc b/src/fs/aufs/store_io_aufs.cc new file mode 100644 index 0000000000..70aa1475ec --- /dev/null +++ b/src/fs/aufs/store_io_aufs.cc @@ -0,0 +1,381 @@ + +/* + * DEBUG 78 + */ + +#include "squid.h" +#include "store_asyncufs.h" + +static AIOCB storeAufsReadDone; +static AIOCB storeAufsWriteDone; +static void storeAufsIOCallback(storeIOState * sio, int errflag); +static AIOCB storeAufsOpenDone; +static int storeAufsSomethingPending(storeIOState *); +static int storeAufsKickWriteQueue(storeIOState * sio); +static void storeAufsIOFreeEntry(void *, int); + +struct _queued_write { + char *buf; + size_t size; + off_t offset; + FREE *free_func; +}; + +struct _queued_read { + char *buf; + size_t size; + off_t offset; + STRCB *callback; + void *callback_data; +}; + +/* === PUBLIC =========================================================== */ + +/* open for reading */ +storeIOState * +storeAufsOpen(SwapDir *SD, StoreEntry *e, STFNCB * file_callback, + STIOCB * callback, void *callback_data) +{ + sfileno f = e->swap_filen; + char *path = storeAufsDirFullPath(SD, f, NULL); + storeIOState *sio; + debug(78, 3) ("storeAufsOpen: fileno %08X\n", f); + /* + * we should detect some 'too many files open' condition and return + * NULL here. + */ + while (aioQueueSize() > MAGIC1) + return NULL; + sio = memAllocate(MEM_STORE_IO); + cbdataAdd(sio, storeAufsIOFreeEntry, MEM_STORE_IO); + sio->fsstate = memPoolAlloc(aio_state_pool); + ((aiostate_t *)(sio->fsstate))->fd = -1; + ((aiostate_t *)(sio->fsstate))->flags.opening = 1; + sio->swap_filen = f; + sio->swap_dirn = SD->index; + sio->mode = O_RDONLY; + sio->callback = callback; + sio->callback_data = callback_data; + sio->e = e; + cbdataLock(callback_data); + aioOpen(path, O_RDONLY, 0644, storeAufsOpenDone, sio); + Opening_FD++; + store_open_disk_fd++; + return sio; +} + +/* open for creating */ +storeIOState * +storeAufsCreate(SwapDir *SD, StoreEntry *e, STFNCB *file_callback, STIOCB *callback, void *callback_data) +{ + char *path ; + storeIOState *sio; + sfileno filn; + sdirno dirn; + + /* Allocate a number */ + dirn = SD->index; + filn = storeAufsDirMapBitAllocate(SD); + path = storeAufsDirFullPath(SD, filn, NULL); + + debug(78, 3) ("storeAufsCreate: fileno %08X\n", filn); + /* + * we should detect some 'too many files open' condition and return + * NULL here. + */ + while (aioQueueSize() > MAGIC1) + return NULL; + sio = memAllocate(MEM_STORE_IO); + cbdataAdd(sio, storeAufsIOFreeEntry, MEM_STORE_IO); + sio->fsstate = memPoolAlloc(aio_state_pool); + ((aiostate_t *)(sio->fsstate))->fd = -1; + ((aiostate_t *)(sio->fsstate))->flags.opening = 1; + sio->swap_filen = filn; + sio->swap_dirn = dirn; + sio->mode = O_WRONLY; + sio->callback = callback; + sio->callback_data = callback_data; + sio->e = (StoreEntry *) e; + cbdataLock(callback_data); + aioOpen(path, O_WRONLY | O_CREAT | O_TRUNC, 0644, storeAufsOpenDone, sio); + Opening_FD++; + store_open_disk_fd++; + + /* now insert into the replacement policy */ + storeAufsDirReplAdd(SD, e); + return sio; + +} + + + +/* Close */ +void +storeAufsClose(SwapDir *SD, storeIOState * sio) +{ + aiostate_t *aiostate = (aiostate_t *)sio->fsstate; + debug(78, 3) ("storeAufsClose: dirno %d, fileno %08X, FD %d\n", + sio->swap_dirn, sio->swap_filen, aiostate->fd); + if (storeAufsSomethingPending(sio)) { + aiostate->flags.close_request = 1; + return; + } + storeAufsIOCallback(sio, 0); +} + + +/* Read */ +void +storeAufsRead(SwapDir *SD, storeIOState * sio, char *buf, size_t size, off_t offset, STRCB * callback, void *callback_data) +{ + aiostate_t *aiostate = (aiostate_t *)sio->fsstate; + assert(sio->read.callback == NULL); + assert(sio->read.callback_data == NULL); + assert(!aiostate->flags.reading); + if (aiostate->fd < 0) { + struct _queued_read *q; + debug(78, 3) ("storeAufsRead: queueing read because FD < 0\n"); + assert(aiostate->flags.opening); + assert(aiostate->pending_reads == NULL); + q = xcalloc(1, sizeof(*q)); + q->buf = buf; + q->size = size; + q->offset = offset; + q->callback = callback; + q->callback_data = callback_data; + linklistPush(&(aiostate->pending_reads), q); + return; + } + sio->read.callback = callback; + sio->read.callback_data = callback_data; + aiostate->read_buf = buf; + cbdataLock(callback_data); + debug(78, 3) ("storeAufsRead: dirno %d, fileno %08X, FD %d\n", + sio->swap_dirn, sio->swap_filen, aiostate->fd); + sio->offset = offset; + aiostate->flags.reading = 1; + aioRead(aiostate->fd, offset, buf, size, storeAufsReadDone, sio); +} + + +/* Write */ +void +storeAufsWrite(SwapDir *SD, storeIOState * sio, char *buf, size_t size, off_t offset, FREE * free_func) +{ + aiostate_t *aiostate = (aiostate_t *)sio->fsstate; + debug(78, 3) ("storeAufsWrite: dirno %d, fileno %08X, FD %d\n", + sio->swap_dirn, sio->swap_filen, aiostate->fd); + if (aiostate->fd < 0) { + /* disk file not opened yet */ + struct _queued_write *q; + assert(aiostate->flags.opening); + q = xcalloc(1, sizeof(*q)); + q->buf = buf; + q->size = size; + q->offset = offset; + q->free_func = free_func; + linklistPush(&(aiostate->pending_writes), q); + return; + } + if (aiostate->flags.writing) { + struct _queued_write *q; + debug(78, 3) ("storeAufsWrite: queuing write\n"); + q = xcalloc(1, sizeof(*q)); + q->buf = buf; + q->size = size; + q->offset = offset; + q->free_func = free_func; + linklistPush(&(aiostate->pending_writes), q); + return; + } + aiostate->flags.writing = 1; + /* + * XXX it might be nice if aioWrite() gave is immediate + * feedback here about EWOULDBLOCK instead of in the + * callback function + */ + aioWrite(aiostate->fd, offset, buf, size, storeAufsWriteDone, sio, + free_func); +} + +/* Unlink */ +void +storeAufsUnlink(SwapDir *SD, StoreEntry *e) +{ + debug(78, 3) ("storeAufsUnlink: dirno %d, fileno %08X\n", SD->index, e->swap_filen); + storeAufsDirReplRemove(e); + storeAufsDirUnlinkFile(SD, e->swap_filen); +} + +/* === STATIC =========================================================== */ + +static int +storeAufsKickWriteQueue(storeIOState * sio) +{ + aiostate_t *aiostate = (aiostate_t *)sio->fsstate; + struct _queued_write *q = linklistShift(&aiostate->pending_writes); + if (NULL == q) + return 0; + debug(78, 3) ("storeAufsKickWriteQueue: writing queued chunk of %d bytes\n", + q->size); + storeAufsWrite(INDEXSD(sio->swap_dirn), sio, q->buf, q->size, q->offset, q->free_func); + xfree(q); + return 1; +} + +static int +storeAufsKickReadQueue(storeIOState * sio) +{ + aiostate_t *aiostate = (aiostate_t *)sio->fsstate; + struct _queued_read *q = linklistShift(&(aiostate->pending_reads)); + if (NULL == q) + return 0; + debug(78, 3) ("storeAufsKickReadQueue: reading queued request of %d bytes\n", + q->size); + storeAufsRead(INDEXSD(sio->swap_dirn), sio, q->buf, q->size, q->offset, q->callback, q->callback_data); + xfree(q); + return 1; +} + +static void +storeAufsOpenDone(int unused, void *my_data, int fd, int errflag) +{ + storeIOState *sio = my_data; + aiostate_t *aiostate = (aiostate_t *)sio->fsstate; + debug(78, 3) ("storeAufsOpenDone: FD %d, errflag %d\n", fd, errflag); + Opening_FD--; + aiostate->flags.opening = 0; + if (errflag || fd < 0) { + errno = errflag; + debug(78, 0) ("storeAufsOpenDone: %s\n", xstrerror()); + debug(78, 1) ("\t%s\n", storeAufsDirFullPath(INDEXSD(sio->swap_dirn), sio->swap_filen, NULL)); + storeAufsIOCallback(sio, errflag); + return; + } + aiostate->fd = fd; + commSetCloseOnExec(fd); + fd_open(fd, FD_FILE, storeAufsDirFullPath(INDEXSD(sio->swap_dirn), sio->swap_filen, NULL)); + if (sio->mode == O_WRONLY) + storeAufsKickWriteQueue(sio); + else if (sio->mode == O_RDONLY) + storeAufsKickReadQueue(sio); + debug(78, 3) ("storeAufsOpenDone: exiting\n"); +} + +static void +storeAufsReadDone(int fd, void *my_data, int len, int errflag) +{ + storeIOState *sio = my_data; + aiostate_t *aiostate = (aiostate_t *)sio->fsstate; + STRCB *callback = sio->read.callback; + void *their_data = sio->read.callback_data; + ssize_t rlen; + debug(78, 3) ("storeAufsReadDone: dirno %d, fileno %08X, FD %d, len %d\n", + sio->swap_dirn, sio->swap_filen, fd, len); + aiostate->flags.reading = 0; + if (errflag) { + debug(78, 3) ("storeAufsReadDone: got failure (%d)\n", errflag); + rlen = -1; + } else { + rlen = (ssize_t) len; + sio->offset += len; + } + assert(callback); + assert(their_data); + sio->read.callback = NULL; + sio->read.callback_data = NULL; + if (cbdataValid(their_data)) + callback(their_data, aiostate->read_buf, rlen); + cbdataUnlock(their_data); + /* + * XXX is this safe? The above callback may have caused sio + * to be freed/closed already? Philip Guenther + * says it fixes his FD leaks, with no side effects. + */ + if (aiostate->flags.close_request) + storeAufsIOCallback(sio, DISK_OK); +} + +/* + * XXX TODO + * if errflag == EWOULDBLOCK, then we'll need to re-queue the + * chunk at the beginning of the write_pending list and try + * again later. + */ +static void +storeAufsWriteDone(int fd, void *my_data, int len, int errflag) +{ + static int loop_detect = 0; + storeIOState *sio = my_data; + aiostate_t *aiostate = (aiostate_t *)sio->fsstate; + debug(78, 3) ("storeAufsWriteDone: dirno %d, fileno %08X, FD %d, len %d\n", + sio->swap_dirn, sio->swap_filen, fd, len); + assert(++loop_detect < 10); + aiostate->flags.writing = 0; + if (errflag) { + debug(78, 0) ("storeAufsWriteDone: got failure (%d)\n", errflag); + storeAufsIOCallback(sio, errflag); + loop_detect--; + return; + } + sio->offset += len; + if (storeAufsKickWriteQueue(sio)) + (void) 0; + else if (aiostate->flags.close_request) + storeAufsIOCallback(sio, errflag); + loop_detect--; +} + +static void +storeAufsIOCallback(storeIOState * sio, int errflag) +{ + STIOCB *callback = sio->callback; + void *their_data = sio->callback_data; + aiostate_t *aiostate = (aiostate_t *)sio->fsstate; + int fd = aiostate->fd; + debug(78, 3) ("storeAufsIOCallback: errflag=%d\n", errflag); + sio->callback = NULL; + sio->callback_data = NULL; + debug(78, 3) ("%s:%d\n", __FILE__, __LINE__); + if (callback) + if (NULL == their_data || cbdataValid(their_data)) + callback(their_data, errflag, sio); + debug(78, 3) ("%s:%d\n", __FILE__, __LINE__); + cbdataUnlock(their_data); + aiostate->fd = -1; + cbdataFree(sio); + if (fd < 0) + return; + debug(78, 3) ("%s:%d\n", __FILE__, __LINE__); + aioClose(fd); + fd_close(fd); + store_open_disk_fd--; + debug(78, 3) ("%s:%d\n", __FILE__, __LINE__); +} + + +static int +storeAufsSomethingPending(storeIOState * sio) +{ + aiostate_t *aiostate = (aiostate_t *)sio->fsstate; + if (aiostate->flags.reading) + return 1; + if (aiostate->flags.writing) + return 1; + if (aiostate->flags.opening) + return 1; + return 0; +} + + +/* + * We can't pass memFree() as a free function here, because we need to free + * the fsstate variable .. + */ +static void +storeAufsIOFreeEntry(void *sio, int foo) +{ + memPoolFree(aio_state_pool, ((storeIOState *)sio)->fsstate); + memFree(sio, MEM_STORE_IO); +} + diff --git a/src/fs/coss/Makefile.in b/src/fs/coss/Makefile.in new file mode 100644 index 0000000000..059ab5ae7a --- /dev/null +++ b/src/fs/coss/Makefile.in @@ -0,0 +1,56 @@ +# +# Makefile for the COSS storage driver for the Squid Object Cache server +# +# $Id: Makefile.in,v 1.1 2000/05/03 17:15:47 adrian Exp $ +# + +FS = coss + +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ + +CC = @CC@ +MAKEDEPEND = @MAKEDEPEND@ +AR_R = @AR_R@ +RANLIB = @RANLIB@ +AC_CFLAGS = @CFLAGS@ +SHELL = /bin/sh + +INCLUDE = -I../../../include -I$(top_srcdir)/include -I$(top_srcdir)/src/ +CFLAGS = $(AC_CFLAGS) $(INCLUDE) $(DEFINES) + +OUT = ../$(FS).a + +OBJS = \ + store_dir_coss.o \ + store_io_coss.o + + +all: $(OUT) + +$(OUT): $(OBJS) + @rm -f ../stamp + $(AR_R) $(OUT) $(OBJS) + $(RANLIB) $(OUT) + +$(OBJS): $(top_srcdir)/include/version.h ../../../include/autoconf.h + +.c.o: + @rm -f ../stamp + $(CC) $(CFLAGS) -c $< + +clean: + -rm -rf *.o *pure_* core ../$(FS).a + +distclean: clean + -rm -f Makefile + -rm -f Makefile.bak + -rm -f tags + +install: + +tags: + ctags *.[ch] ../../*.[ch] ../../../include/*.h ../lib/*.[ch] + +depend: + $(MAKEDEPEND) $(INCLUDE) -fMakefile *.c diff --git a/src/fs/coss/store_coss.h b/src/fs/coss/store_coss.h new file mode 100644 index 0000000000..6e4f9634ef --- /dev/null +++ b/src/fs/coss/store_coss.h @@ -0,0 +1,82 @@ +#ifndef __COSS_H__ +#define __COSS_H__ + +#ifndef COSS_MEMBUF_SZ +#define COSS_MEMBUF_SZ 1048576 +#endif + +#define COSS_ALLOC_NOTIFY 0 +#define COSS_ALLOC_ALLOCATE 1 +#define COSS_ALLOC_REALLOC 2 + +struct _cossmembuf { + size_t diskstart; + size_t diskend; + SwapDir *SD; + int lockcount; + char buffer[COSS_MEMBUF_SZ]; + struct _cossmembuf_flags { + unsigned int full:1; + unsigned int writing:1; + } flags; + struct _cossmembuf *next; +}; + + +/* Per-storedir info */ +struct _cossinfo { + struct _cossmembuf *membufs; + struct _cossmembuf *current_membuf; + size_t current_offset; + int fd; + int swaplog_fd; + int numcollisions; +}; + + +/* Per-storeiostate info */ +struct _cossstate { + char *readbuffer; + char *requestbuf; + size_t requestlen; + size_t requestoffset; + sfileno reqdiskoffset; + struct { + unsigned int reading:1; + unsigned int writing:1; + } flags; +}; + +typedef struct _cossmembuf CossMemBuf; +typedef struct _cossinfo CossInfo; +typedef struct _cossstate CossState; + +/* Whether the coss system has been setup or not */ +extern int coss_initialised; +extern MemPool * coss_membuf_pool; +extern MemPool * coss_state_pool; + + +/* + * Store IO stuff + */ +extern STOBJCREATE storeCossCreate; +extern STOBJOPEN storeCossOpen; +extern STOBJCLOSE storeCossClose; +extern STOBJREAD storeCossRead; +extern STOBJWRITE storeCossWrite; +extern STOBJUNLINK storeCossUnlink; + +extern off_t storeCossAllocate(SwapDir *SD, const StoreEntry *e, int which); +extern void storeCossFree(StoreEntry *e); +extern void storeCossMaintainSwapSpace(SwapDir *SD); +extern void storeCossDirStats(SwapDir *, StoreEntry *); +extern void storeCossDirDump(StoreEntry * entry, const char *name, SwapDir* s); +extern void storeCossDirFree(SwapDir *); +extern SwapDir *storeCossDirPick(void); + +void storeFsSetup_ufs(storefs_entry_t *); + + + +#endif diff --git a/src/fs/coss/store_dir_coss.cc b/src/fs/coss/store_dir_coss.cc new file mode 100644 index 0000000000..1911e63fb8 --- /dev/null +++ b/src/fs/coss/store_dir_coss.cc @@ -0,0 +1,846 @@ + +/* + * $Id: store_dir_coss.cc,v 1.1 2000/05/03 17:15:47 adrian Exp $ + * + * DEBUG: section 81 Store COSS Directory Routines + * AUTHOR: Eric Stern + * + * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from the + * Internet community. Development is led by Duane Wessels of the + * National Laboratory for Applied Network Research and funded by the + * National Science Foundation. Squid is Copyrighted (C) 1998 by + * Duane Wessels and the University of California San Diego. Please + * see the COPYRIGHT file for full details. Squid incorporates + * software developed and/or copyrighted by other sources. Please see + * the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#include "squid.h" + +#include "store_coss.h" + +#define STORE_META_BUFSZ 4096 + +extern void storeCossFlushMemBufs(SwapDir *SD); + +int n_coss_dirs = 0; +/* static int last_coss_pick_index = -1; */ +int coss_initialised = 0; +MemPool * coss_membuf_pool = NULL; +MemPool * coss_state_pool = NULL; + +typedef struct _RebuildState RebuildState; +struct _RebuildState { + SwapDir *sd; + int n_read; + FILE *log; + int speed; + int curlvl1; + int curlvl2; + struct { + unsigned int need_to_validate:1; + unsigned int clean:1; + unsigned int init:1; + } flags; + int done; + int in_dir; + int fn; + struct dirent *entry; + DIR *td; + char fullpath[SQUID_MAXPATHLEN]; + char fullfilename[SQUID_MAXPATHLEN]; + struct _store_rebuild_data counts; +}; + +static char *storeCossDirSwapLogFile(SwapDir *, const char *); +static EVH storeCossRebuildFromSwapLog; +static StoreEntry *storeCossAddDiskRestore(SwapDir *SD,const cache_key *key, + int file_number, + size_t swap_file_sz, + time_t expires, + time_t timestamp, + time_t lastref, + time_t lastmod, + u_num32 refcount, + u_short flags, + int clean); +static void storeCossDirRebuild(SwapDir * sd); +static void storeCossDirCloseTmpSwapLog(SwapDir * sd); +static FILE *storeCossDirOpenTmpSwapLog(SwapDir *, int *, int *); +static STLOGOPEN storeCossDirOpenSwapLog; +static STINIT storeCossDirInit; +static STLOGCLEANOPEN storeCossDirWriteCleanOpen; +static void storeCossDirWriteCleanClose(SwapDir * sd); +static STLOGCLEANWRITE storeCossDirWriteCleanEntry; +static STLOGCLOSE storeCossDirCloseSwapLog; +static STLOGWRITE storeCossDirSwapLog; +static STNEWFS storeCossDirNewfs; +static STCHECKOBJ storeCossDirCheckObj; +static void storeCossDirShutdown(SwapDir * sd); +static void storeCossDirParse(SwapDir *, int, char *); +static void storeCossDirReconfigure(SwapDir *, int, char *); + +static char * +storeCossDirSwapLogFile(SwapDir * sd, const char *ext) +{ + LOCAL_ARRAY(char, path, SQUID_MAXPATHLEN); + LOCAL_ARRAY(char, pathtmp, SQUID_MAXPATHLEN); + LOCAL_ARRAY(char, digit, 32); + char *pathtmp2; + if (Config.Log.swap) { + xstrncpy(pathtmp, sd->path, SQUID_MAXPATHLEN - 64); + while (index(pathtmp,'/')) + *index(pathtmp,'/')='.'; + while (strlen(pathtmp) && pathtmp[strlen(pathtmp)-1]=='.') + pathtmp[strlen(pathtmp)-1]= '\0'; + for(pathtmp2 = pathtmp; *pathtmp2 == '.'; pathtmp2++); + snprintf(path, SQUID_MAXPATHLEN-64, Config.Log.swap, pathtmp2); + if (strncmp(path, Config.Log.swap, SQUID_MAXPATHLEN - 64) == 0) { + strcat(path, "."); + snprintf(digit, 32, "%02d", sd->index); + strncat(path, digit, 3); + } + } else { + xstrncpy(path, sd->path, SQUID_MAXPATHLEN - 64); + strcat(path, "/swap.state"); + } + if (ext) + strncat(path, ext, 16); + return path; +} + +static void +storeCossDirOpenSwapLog(SwapDir * sd) +{ + CossInfo *cs = (CossInfo *)sd->fsdata; + char *path; + int fd; + path = storeCossDirSwapLogFile(sd, NULL); + fd = file_open(path, O_WRONLY | O_CREAT); + if (fd < 0) { + debug(81, 1) ("%s: %s\n", path, xstrerror()); + fatal("storeCossDirOpenSwapLog: Failed to open swap log."); + } + debug(81, 3) ("Cache COSS Dir #%d log opened on FD %d\n", sd->index, fd); + cs->swaplog_fd = fd; +} + +static void +storeCossDirCloseSwapLog(SwapDir * sd) +{ + CossInfo *cs = (CossInfo *)sd->fsdata; + if (cs->swaplog_fd < 0) /* not open */ + return; + file_close(cs->swaplog_fd); + debug(81, 3) ("Cache COSS Dir #%d log closed on FD %d\n", + sd->index, cs->swaplog_fd); + cs->swaplog_fd = -1; +} + +static void +storeCossDirInit(SwapDir * sd) +{ + CossInfo *cs = (CossInfo *)sd->fsdata; + storeCossDirOpenSwapLog(sd); + storeCossDirRebuild(sd); + cs->fd = file_open(sd->path, O_RDWR | O_CREAT); + n_coss_dirs++; +} + +static void +storeCossRebuildFromSwapLog(void *data) +{ + RebuildState *rb = data; + StoreEntry *e = NULL; + storeSwapLogData s; + size_t ss = sizeof(storeSwapLogData); + int count; + double x; + assert(rb != NULL); + /* load a number of objects per invocation */ + for (count = 0; count < rb->speed; count++) { + if (fread(&s, ss, 1, rb->log) != 1) { + debug(81, 1) ("Done reading %s swaplog (%d entries)\n", + rb->sd->path, rb->n_read); + fclose(rb->log); + rb->log = NULL; + store_dirs_rebuilding--; + storeCossDirCloseTmpSwapLog(rb->sd); + storeRebuildComplete(&rb->counts); + cbdataFree(rb); + return; + } + rb->n_read++; + if (s.op <= SWAP_LOG_NOP) + continue; + if (s.op >= SWAP_LOG_MAX) + continue; + debug(20, 3) ("storeCossRebuildFromSwapLog: %s %s %08X\n", + swap_log_op_str[(int) s.op], + storeKeyText(s.key), + s.swap_filen); + if (s.op == SWAP_LOG_ADD) { + (void) 0; + } else if (s.op == SWAP_LOG_DEL) { + if ((e = storeGet(s.key)) != NULL) { + /* + * Make sure we don't unlink the file, it might be + * in use by a subsequent entry. Also note that + * we don't have to subtract from store_swap_size + * because adding to store_swap_size happens in + * the cleanup procedure. + */ + storeExpireNow(e); + storeReleaseRequest(e); + if (e->swap_filen > -1) { + e->swap_filen = -1; + } + storeRelease(e); + /* Fake an unlink here, this is a bad hack :( */ + dlinkDelete(&e->repl.lru, &rb->sd->repl.lru.list); + rb->counts.objcount--; + rb->counts.cancelcount++; + } + continue; + } else { + x = log(++rb->counts.bad_log_op) / log(10.0); + if (0.0 == x - (double) (int) x) + debug(20, 1) ("WARNING: %d invalid swap log entries found\n", + rb->counts.bad_log_op); + rb->counts.invalid++; + continue; + } + if ((++rb->counts.scancount & 0xFFFF) == 0) + debug(20, 3) (" %7d %s Entries read so far.\n", + rb->counts.scancount, rb->sd->path); + if (EBIT_TEST(s.flags, KEY_PRIVATE)) { + rb->counts.badflags++; + continue; + } + e = storeGet(s.key); + if (e) { + /* key already exists, current entry is newer */ + /* keep old, ignore new */ + rb->counts.dupcount++; + continue; + } + /* update store_swap_size */ + rb->counts.objcount++; + e = storeCossAddDiskRestore(rb->sd,s.key, + s.swap_filen, + s.swap_file_sz, + s.expires, + s.timestamp, + s.lastref, + s.lastmod, + s.refcount, + s.flags, + (int) rb->flags.clean); + storeDirSwapLog(e, SWAP_LOG_ADD); + } + eventAdd("storeCossRebuild", storeCossRebuildFromSwapLog, rb, 0.0, 1); +} + +/* Add a new object to the cache with empty memory copy and pointer to disk + * use to rebuild store from disk. */ +static StoreEntry * +storeCossAddDiskRestore(SwapDir *SD, const cache_key * key, + int file_number, + size_t swap_file_sz, + time_t expires, + time_t timestamp, + time_t lastref, + time_t lastmod, + u_num32 refcount, + u_short flags, + int clean) +{ + StoreEntry *e = NULL; + debug(20, 5) ("storeCossAddDiskRestore: %s, fileno=%08X\n", storeKeyText(key), file_number); + /* if you call this you'd better be sure file_number is not + * already in use! */ + e = new_StoreEntry(STORE_ENTRY_WITHOUT_MEMOBJ, NULL, NULL); + e->store_status = STORE_OK; + e->swap_dirn = SD->index; + storeSetMemStatus(e, NOT_IN_MEMORY); + e->swap_status = SWAPOUT_DONE; + e->swap_filen = file_number; + e->swap_file_sz = swap_file_sz; + e->lock_count = 0; +#if !HEAP_REPLACEMENT + e->refcount = 0; +#endif + e->lastref = lastref; + e->timestamp = timestamp; + e->expires = expires; + e->lastmod = lastmod; + e->refcount = refcount; + e->flags = flags; + EBIT_SET(e->flags, ENTRY_CACHABLE); + EBIT_CLR(e->flags, RELEASE_REQUEST); + EBIT_CLR(e->flags, KEY_PRIVATE); + e->ping_status = PING_NONE; + EBIT_CLR(e->flags, ENTRY_VALIDATED); + storeHashInsert(e, key); /* do it after we clear KEY_PRIVATE */ + dlinkAdd(e, &e->repl.lru, &SD->repl.lru.list); + e->swap_filen = storeCossAllocate(SD, e, COSS_ALLOC_NOTIFY); + return e; +} + +static void +storeCossDirRebuild(SwapDir * sd) +{ + RebuildState *rb = xcalloc(1, sizeof(*rb)); + int clean = 0; + int zero = 0; + FILE *fp; + EVH *func = NULL; + rb->sd = sd; + rb->speed = opt_foreground_rebuild ? 1 << 30 : 50; + /* + * If the swap.state file exists in the cache_dir, then + * we'll use storeCossRebuildFromSwapLog(). + */ + fp = storeCossDirOpenTmpSwapLog(sd, &clean, &zero); + debug(20, 1) ("Rebuilding COSS storage in %s (%s)\n", + sd->path, clean ? "CLEAN" : "DIRTY"); + if (!clean || fp == NULL) { + /* COSS cannot yet rebuild from a dirty state. If the log + * is dirty then the COSS contents is thrown away. + * Why? I guess it is because some contents will be lost, + * and COSS cannot verify this.. + */ + if (fp != NULL) + fclose(fp); + storeCossDirCloseTmpSwapLog(rb->sd); + /* + * XXX Make sure we don't trigger an assertion if this is the first + * storedir, since if we are, this call will cause storeRebuildComplete + * to prematurely complete the rebuild process, and then some other + * storedir will try to rebuild and eventually die. + */ + store_dirs_rebuilding++; + storeRebuildComplete(&rb->counts); + store_dirs_rebuilding--; + xfree(rb); + return; + } + func = storeCossRebuildFromSwapLog; + rb->log = fp; + rb->flags.clean = (unsigned int) clean; + store_dirs_rebuilding++; + cbdataAdd(rb, cbdataXfree, 0); + eventAdd("storeCossRebuild", func, rb, 0.0, 1); +} + +static void +storeCossDirCloseTmpSwapLog(SwapDir * sd) +{ + CossInfo *cs = (CossInfo *)sd->fsdata; + char *swaplog_path = xstrdup(storeCossDirSwapLogFile(sd, NULL)); + char *new_path = xstrdup(storeCossDirSwapLogFile(sd, ".new")); + int fd; + file_close(cs->swaplog_fd); +#ifdef _SQUID_OS2_ + if (unlink(swaplog_path) < 0) { + debug(50, 0) ("%s: %s\n", swaplog_path, xstrerror()); + fatal("storeCossDirCloseTmpSwapLog: unlink failed"); + } +#endif + if (xrename(new_path, swaplog_path) < 0) { + fatal("storeCossDirCloseTmpSwapLog: rename failed"); + } + fd = file_open(swaplog_path, O_WRONLY | O_CREAT); + if (fd < 0) { + debug(50, 1) ("%s: %s\n", swaplog_path, xstrerror()); + fatal("storeCossDirCloseTmpSwapLog: Failed to open swap log."); + } + safe_free(swaplog_path); + safe_free(new_path); + cs->swaplog_fd = fd; + debug(47, 3) ("Cache COSS Dir #%d log opened on FD %d\n", sd->index, fd); +} + +static FILE * +storeCossDirOpenTmpSwapLog(SwapDir * sd, int *clean_flag, int *zero_flag) +{ + CossInfo *cs = (CossInfo *)sd->fsdata; + char *swaplog_path = xstrdup(storeCossDirSwapLogFile(sd, NULL)); + char *clean_path = xstrdup(storeCossDirSwapLogFile(sd, ".last-clean")); + char *new_path = xstrdup(storeCossDirSwapLogFile(sd, ".new")); + struct stat log_sb; + struct stat clean_sb; + FILE *fp; + int fd; + if (stat(swaplog_path, &log_sb) < 0) { + debug(47, 1) ("Cache COSS Dir #%d: No log file\n", sd->index); + safe_free(swaplog_path); + safe_free(clean_path); + safe_free(new_path); + return NULL; + } + *zero_flag = log_sb.st_size == 0 ? 1 : 0; + /* close the existing write-only FD */ + if (cs->swaplog_fd >= 0) + file_close(cs->swaplog_fd); + /* open a write-only FD for the new log */ + fd = file_open(new_path, O_WRONLY | O_CREAT | O_TRUNC); + if (fd < 0) { + debug(50, 1) ("%s: %s\n", new_path, xstrerror()); + fatal("storeDirOpenTmpSwapLog: Failed to open swap log."); + } + cs->swaplog_fd = fd; + /* open a read-only stream of the old log */ + fp = fopen(swaplog_path, "r"); + if (fp == NULL) { + debug(50, 0) ("%s: %s\n", swaplog_path, xstrerror()); + fatal("Failed to open swap log for reading"); + } + memset(&clean_sb, '\0', sizeof(struct stat)); + if (stat(clean_path, &clean_sb) < 0) + *clean_flag = 0; + else if (clean_sb.st_mtime < log_sb.st_mtime) + *clean_flag = 0; + else + *clean_flag = 1; + safeunlink(clean_path, 1); + safe_free(swaplog_path); + safe_free(clean_path); + safe_free(new_path); + return fp; +} + +struct _clean_state { + char *cur; + char *new; + char *cln; + char *outbuf; + off_t outbuf_offset; + int fd; +}; + +#define CLEAN_BUF_SZ 16384 +/* + * Begin the process to write clean cache state. For COSS this means + * opening some log files and allocating write buffers. Return 0 if + * we succeed, and assign the 'func' and 'data' return pointers. + */ +static int +storeCossDirWriteCleanOpen(SwapDir * sd) +{ + struct _clean_state *state = xcalloc(1, sizeof(*state)); + struct stat sb; + sd->log.clean.write = NULL; + sd->log.clean.state = NULL; + state->cur = xstrdup(storeCossDirSwapLogFile(sd, NULL)); + state->new = xstrdup(storeCossDirSwapLogFile(sd, ".clean")); + state->cln = xstrdup(storeCossDirSwapLogFile(sd, ".last-clean")); + state->outbuf = xcalloc(CLEAN_BUF_SZ, 1); + state->outbuf_offset = 0; + unlink(state->new); + unlink(state->cln); + state->fd = file_open(state->new, O_WRONLY | O_CREAT | O_TRUNC); + if (state->fd < 0) + return -1; + debug(20, 3) ("storeCOssDirWriteCleanLogs: opened %s, FD %d\n", + state->new, state->fd); +#if HAVE_FCHMOD + if (stat(state->cur, &sb) == 0) + fchmod(state->fd, sb.st_mode); +#endif + sd->log.clean.write = storeCossDirWriteCleanEntry; + sd->log.clean.state = state; + return 0; +} + +/* + * "write" an entry to the clean log file. + */ +static void +storeCossDirWriteCleanEntry(const StoreEntry * e, SwapDir * sd) +{ + storeSwapLogData s; + static size_t ss = sizeof(storeSwapLogData); + struct _clean_state *state = sd->log.clean.state; + if (NULL == e) { + storeCossDirWriteCleanClose(sd); + return; + } + memset(&s, '\0', ss); + s.op = (char) SWAP_LOG_ADD; + s.swap_filen = e->swap_filen; + s.timestamp = e->timestamp; + s.lastref = e->lastref; + s.expires = e->expires; + s.lastmod = e->lastmod; + s.swap_file_sz = e->swap_file_sz; + s.refcount = e->refcount; + s.flags = e->flags; + xmemcpy(&s.key, e->key, MD5_DIGEST_CHARS); + xmemcpy(state->outbuf + state->outbuf_offset, &s, ss); + state->outbuf_offset += ss; + /* buffered write */ + if (state->outbuf_offset + ss > CLEAN_BUF_SZ) { + if (write(state->fd, state->outbuf, state->outbuf_offset) < 0) { + debug(50, 0) ("storeCossDirWriteCleanLogs: %s: write: %s\n", + state->new, xstrerror()); + debug(20, 0) ("storeCossDirWriteCleanLogs: Current swap logfile not replaced.\n"); + file_close(state->fd); + state->fd = -1; + unlink(state->new); + safe_free(state); + sd->log.clean.state = NULL; + sd->log.clean.write = NULL; + } + state->outbuf_offset = 0; + } +} + +static void +storeCossDirWriteCleanClose(SwapDir * sd) +{ + struct _clean_state *state = sd->log.clean.state; + if (state->fd < 0) + return; + if (write(state->fd, state->outbuf, state->outbuf_offset) < 0) { + debug(50, 0) ("storeCossDirWriteCleanLogs: %s: write: %s\n", + state->new, xstrerror()); + debug(20, 0) ("storeCossDirWriteCleanLogs: Current swap logfile " + "not replaced.\n"); + file_close(state->fd); + state->fd = -1; + unlink(state->new); + } + safe_free(state->outbuf); + /* + * You can't rename open files on Microsoft "operating systems" + * so we have to close before renaming. + */ + storeCossDirCloseSwapLog(sd); + /* rename */ + if (state->fd >= 0) { +#ifdef _SQUID_OS2_ + file_close(state->fd); + state->fd = -1; + if (unlink(cur) < 0) + debug(50, 0) ("storeCossDirWriteCleanLogs: unlinkd failed: %s, %s\n", + xstrerror(), cur); +#endif + xrename(state->new, state->cur); + } + /* touch a timestamp file if we're not still validating */ + if (store_dirs_rebuilding) + (void) 0; + else if (state->fd < 0) + (void) 0; + else + file_close(file_open(state->cln, O_WRONLY | O_CREAT | O_TRUNC)); + /* close */ + safe_free(state->cur); + safe_free(state->new); + safe_free(state->cln); + if (state->fd >= 0) + file_close(state->fd); + state->fd = -1; + safe_free(state); + sd->log.clean.state = NULL; + sd->log.clean.write = NULL; +} + +static void +storeCossDirSwapLog(const SwapDir * sd, const StoreEntry * e, int op) +{ + CossInfo *cs = (CossInfo *)sd->fsdata; + storeSwapLogData *s = xcalloc(1, sizeof(storeSwapLogData)); + s->op = (char) op; + s->swap_filen = e->swap_filen; + s->timestamp = e->timestamp; + s->lastref = e->lastref; + s->expires = e->expires; + s->lastmod = e->lastmod; + s->swap_file_sz = e->swap_file_sz; + s->refcount = e->refcount; + s->flags = e->flags; + xmemcpy(s->key, e->key, MD5_DIGEST_CHARS); + file_write(cs->swaplog_fd, + -1, + s, + sizeof(storeSwapLogData), + NULL, + NULL, + xfree); +} + +static void +storeCossDirNewfs(SwapDir * sd) +{ + debug(47, 3) ("Creating swap space in %s\n", sd->path); +} + +// we are shutting down, flush all membufs to disk +static void +storeCossDirShutdown(SwapDir *SD) +{ + CossInfo *cs = (CossInfo *)SD->fsdata; + CossMemBuf *t; + + // we need to do this synchronously! + for (t = cs->membufs; t; t = t->next) { + lseek(cs->fd, t->diskstart, SEEK_SET); + write(cs->fd, t->buffer, COSS_MEMBUF_SZ); + } + file_close(cs->fd); + cs->fd = -1; + + if (cs->swaplog_fd > -1) { + file_close(cs->swaplog_fd); + cs->swaplog_fd = -1; + } + + n_coss_dirs--; +} + +/* + * storeCossDirCheckObj + * + * This routine is called by storeDirSelectSwapDir to see if the given + * object is able to be stored on this filesystem. COSS filesystems will + * not store everything. We don't check for maxobjsize here since its + * done by the upper layers. + */ +int +storeCossDirCheckObj(SwapDir *SD, const StoreEntry *e) +{ + /* Check if the object is a special object, we can't cache these */ + if (EBIT_TEST(e->flags, ENTRY_SPECIAL)) + return -1; + + /* Otherwise, we're ok */ + /* Return 900 (90%) load */ + return 900; +} + +/* ========== LOCAL FUNCTIONS ABOVE, GLOBAL FUNCTIONS BELOW ========== */ + +void +storeCossDirStats(SwapDir *SD, StoreEntry * sentry) +{ + CossInfo *cs = (CossInfo *)SD->fsdata; + + storeAppendPrintf(sentry, "\n"); + storeAppendPrintf(sentry, "Maximum Size: %d KB\n", SD->max_size); + storeAppendPrintf(sentry, "Current Size: %d KB\n", SD->cur_size); + storeAppendPrintf(sentry, "Percent Used: %0.2f%%\n", + 100.0 * SD->cur_size / SD->max_size); + storeAppendPrintf(sentry, "Number of object collisions: %d\n", (int)cs->numcollisions); +#if 0 + /* is this applicable? I Hope not .. */ + storeAppendPrintf(sentry, "Filemap bits in use: %d of %d (%d%%)\n", + SD->map->n_files_in_map, SD->map->max_n_files, + percent(SD->map->n_files_in_map, SD->map->max_n_files)); +#endif + storeAppendPrintf(sentry, "Flags:"); + if (SD->flags.selected) + storeAppendPrintf(sentry, " SELECTED"); + if (SD->flags.read_only) + storeAppendPrintf(sentry, " READ-ONLY"); + storeAppendPrintf(sentry, "\n"); +} + +static void +storeCossDirParse(SwapDir *sd, int index, char *path) +{ + char *token; + unsigned int i; + unsigned int size; + unsigned int read_only = 0; + CossInfo *cs; + + i = GetInteger(); + size = i << 10; /* Mbytes to Kbytes */ + if (size <= 0) + fatal("storeCossDirParse: invalid size value"); + if ((token = strtok(NULL, w_space))) + if (!strcasecmp(token, "read-only")) + read_only = 1; + + cs = xmalloc(sizeof(CossInfo)); + if (cs == NULL) + fatal("storeCossDirParse: couldn't xmalloc() CossInfo!\n"); + + sd->index = index; + sd->path = xstrdup(path); + sd->max_size = size; + sd->fsdata = cs; + + cs->fd = -1; + cs->swaplog_fd = -1; + sd->flags.read_only = read_only; + + sd->init = storeCossDirInit; + sd->newfs = storeCossDirNewfs; + sd->dump = storeCossDirDump; + sd->freefs = storeCossDirShutdown; + sd->dblcheck = NULL; + sd->statfs = storeCossDirStats; + sd->maintainfs = NULL; + sd->checkobj = storeCossDirCheckObj; + sd->refobj = NULL; /* LRU is done in storeCossRead */ + sd->unrefobj = NULL; + sd->callback = NULL; + sd->sync = NULL; /* should we make it call the coss sync? */ + + sd->obj.create = storeCossCreate; + sd->obj.open = storeCossOpen; + sd->obj.close = storeCossClose; + sd->obj.read = storeCossRead; + sd->obj.write = storeCossWrite; + sd->obj.unlink = storeCossUnlink; + + sd->log.open = storeCossDirOpenSwapLog; + sd->log.close = storeCossDirCloseSwapLog; + sd->log.write = storeCossDirSwapLog; + sd->log.clean.open = storeCossDirWriteCleanOpen; + sd->log.clean.write = storeCossDirWriteCleanEntry; + + cs->current_offset = 0; + cs->fd = -1; + cs->swaplog_fd = -1; + cs->numcollisions = 0; + cs->membufs = memPoolAlloc(coss_membuf_pool); + cs->membufs->diskstart = 0; + cs->membufs->diskend = COSS_MEMBUF_SZ; + cs->membufs->lockcount = 0; + cs->membufs->flags.full = 0; + cs->membufs->flags.writing = 0; + cs->membufs->next = NULL; + cs->membufs->SD = sd; + cs->current_membuf = cs->membufs; + + sd->repl.lru.list.head = NULL; + sd->repl.lru.list.tail = NULL; +} + + +static void +storeCossDirReconfigure(SwapDir *sd, int index, char *path) +{ + char *token; + unsigned int i; + unsigned int size; + unsigned int read_only = 0; + + i = GetInteger(); + size = i << 10; /* Mbytes to Kbytes */ + if (size <= 0) + fatal("storeCossDirParse: invalid size value"); + if ((token = strtok(NULL, w_space))) + if (!strcasecmp(token, "read-only")) + read_only = 1; + + if (size == sd->max_size) + debug (3, 1) ("Cache COSS dir '%s' size remains unchanged at %d KB\n", path, size); + else { + debug (3, 1) ("Cache COSS dir '%s' size changed to %d KB\n", path, size); + sd->max_size = size; + } + + if (read_only != sd->flags.read_only) { + debug (3, 1) ("Cache COSS dir '%s' now %s\n", path, read_only ? "Read-Only" : "Read-Write"); + sd->flags.read_only = read_only; + } +} + +void +storeCossDirDump(StoreEntry * entry, const char *name, SwapDir * s) +{ + storeAppendPrintf(entry, "%s %s %s %d\n", + name, + s->type, + s->path, + s->max_size >> 20); +} + +#if 0 +SwapDir * +storeCossDirPick(void) +{ + int i,choosenext = 0; + SwapDir *SD; + + if (n_coss_dirs == 0) + return NULL; + for (i = 0; i < Config.cacheSwap.n_configured; i++) { + SD = &Config.cacheSwap.swapDirs[i]; + if (SD->type == SWAPDIR_COSS) { + if ((last_coss_pick_index == -1) || (n_coss_dirs == 1)) { + last_coss_pick_index = i; + return SD; + } else if (choosenext) { + last_coss_pick_index = i; + return SD; + } else if (last_coss_pick_index == i) { + choosenext = 1; + } + } + } + for (i = 0; i < Config.cacheSwap.n_configured; i++) { + SD = &Config.cacheSwap.swapDirs[i]; + if (SD->type == SWAPDIR_COSS) { + if ((last_coss_pick_index == -1) || (n_coss_dirs == 1)) { + last_coss_pick_index = i; + return SD; + } else if (choosenext) { + last_coss_pick_index = i; + return SD; + } else if (last_coss_pick_index == i) { + choosenext = 1; + } + } + } + return NULL; +} +#endif + +/* + * initial setup/done code + */ +static void +storeCossDirDone(void) +{ + memPoolDestroy(coss_membuf_pool); + memPoolDestroy(coss_state_pool); + coss_initialised = 0; +} + +void +storeFsSetup_coss(storefs_entry_t *storefs) +{ + assert(!coss_initialised); + + storefs->parsefunc = storeCossDirParse; + storefs->reconfigurefunc = storeCossDirReconfigure; + storefs->donefunc = storeCossDirDone; + coss_membuf_pool = memPoolCreate("COSS Membuf data", sizeof(CossMemBuf)); + coss_state_pool = memPoolCreate("COSS IO State data", sizeof(CossState)); + coss_initialised = 1; +} + diff --git a/src/fs/coss/store_io_coss.cc b/src/fs/coss/store_io_coss.cc new file mode 100644 index 0000000000..5aa2fb0d72 --- /dev/null +++ b/src/fs/coss/store_io_coss.cc @@ -0,0 +1,518 @@ + +/* + * $Id: store_io_coss.cc,v 1.1 2000/05/03 17:15:47 adrian Exp $ + * + * DEBUG: section 81 Storage Manager COSS Interface + * AUTHOR: Eric Stern + * + * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from the + * Internet community. Development is led by Duane Wessels of the + * National Laboratory for Applied Network Research and funded by the + * National Science Foundation. Squid is Copyrighted (C) 1998 by + * Duane Wessels and the University of California San Diego. Please + * see the COPYRIGHT file for full details. Squid incorporates + * software developed and/or copyrighted by other sources. Please see + * the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#include "squid.h" +#include "store_coss.h" + +static DWCB storeCossWriteMemBufDone; +static DRCB storeCossReadDone; +static void storeCossIOCallback(storeIOState * sio, int errflag); +static char *storeCossMemPointerFromDiskOffset(SwapDir *SD, size_t offset, CossMemBuf **mb); +static void storeCossMemBufLock(SwapDir *SD, storeIOState *e); +static void storeCossMemBufUnlock(SwapDir *SD, storeIOState *e); +static void storeCossWriteMemBuf(SwapDir *SD,CossMemBuf *t); +static void storeCossWriteMemBufDone(int fd, int errflag, size_t len, void *my_data); +static CossMemBuf *storeCossCreateMemBuf(SwapDir *SD, size_t start, + sfileno curfn, int *collision); +static void storeCossIOFreeEntry(void *, int); +static void storeCossMembufFree(void *, int); + +/* === PUBLIC =========================================================== */ + +/* + * This routine sucks. I want to rewrite it when possible, and I also think + * that we should check after creatmembuf() to see if the object has a + * RELEASE_REQUEST set on it (thanks Eric!) rather than this way which seems + * to work.. + * -- Adrian + */ +off_t +storeCossAllocate(SwapDir *SD, const StoreEntry *e, int which) +{ + CossInfo *cs = (CossInfo *)SD->fsdata; + CossMemBuf *newmb; + off_t retofs; + size_t allocsize; + int coll = 0; + sfileno checkf; + + /* Make sure we chcek collisions if reallocating */ + if (which == COSS_ALLOC_REALLOC) + checkf = e->swap_filen; + else + checkf = -1; + + retofs = e->swap_filen; /* Just for defaults */ + + if (e->swap_file_sz > 0) + allocsize = e->swap_file_sz; + else + allocsize = objectLen(e) + e->mem_obj->swap_hdr_sz; + + if (which != COSS_ALLOC_NOTIFY) { + if ((cs->current_offset + allocsize) > (SD->max_size << 10)) { + // tried to allocate past the end of the disk, so wrap + // back to the beginning + cs->current_membuf->flags.full = 1; + cs->current_membuf->diskend = cs->current_offset-1; + cs->current_offset = 0; // wrap back to beginning + newmb = storeCossCreateMemBuf(SD, 0, checkf, &coll); + cs->current_membuf = newmb; + } else if ((cs->current_offset + allocsize) > cs->current_membuf->diskend) { + cs->current_membuf->flags.full = 1; + cs->current_membuf->diskend = cs->current_offset-1; + newmb = storeCossCreateMemBuf(SD, cs->current_offset, + checkf, &coll); + cs->current_membuf = newmb; + } + if (coll == 0) { + retofs = cs->current_offset; + } else { + debug(81, 3) ("storeCossAllocate: Collision\n"); + } + } + if (coll == 0) { + cs->current_offset += allocsize; + return retofs; + } else { + return -1; + } +} + +void +storeCossUnlink(SwapDir *SD, StoreEntry *e) +{ + debug(81, 3) ("storeCossUnlink: offset %d\n", e->swap_filen); + dlinkDelete(&e->repl.lru, &SD->repl.lru.list); +} + + +storeIOState * +storeCossCreate(SwapDir *SD, StoreEntry *e, STFNCB *file_callback, STIOCB *callback, void *callback_data) +{ + CossState *cstate; + storeIOState *sio; + + sio = memAllocate(MEM_STORE_IO); + cbdataAdd(sio, storeCossIOFreeEntry, MEM_STORE_IO); + cstate = memPoolAlloc(coss_state_pool); + sio->fsstate = cstate; + sio->offset = 0; + sio->mode = O_WRONLY; + + /* + * this one is kinda strange - Eric called storeCossAllocate(), then + * storeCossOpen(O_RDONLY) .. weird. Anyway, I'm allocating this now. + */ + sio->st_size = objectLen(e) + e->mem_obj->swap_hdr_sz; + sio->swap_dirn = SD->index; + sio->swap_filen = storeCossAllocate(SD, e, COSS_ALLOC_ALLOCATE); + debug(81, 3) ("storeCossCreate: offset %d, size %d, end %d\n", sio->swap_filen, sio->st_size, sio->swap_filen + sio->st_size); + + sio->callback = callback; + sio->callback_data = callback_data; + cbdataLock(callback_data); + sio->e = (StoreEntry *)e; + sio->st_size = -1; // we won't know this until we read the metadata + + cstate->flags.writing = 0; + cstate->flags.reading = 0; + cstate->readbuffer = NULL; + cstate->reqdiskoffset = -1; + + /* Now add it into the LRU */ + dlinkAdd(e, &e->repl.lru, &SD->repl.lru.list); + + storeCossMemBufLock(SD, sio); + return sio; +} + +storeIOState * +storeCossOpen(SwapDir *SD, StoreEntry *e, STFNCB * file_callback, + STIOCB * callback, void *callback_data) +{ + storeIOState *sio; + char *p; + CossState *cstate; + sfileno f = e->swap_filen; + CossInfo *cs = (CossInfo *)SD->fsdata; + + debug(81, 3) ("storeCossOpen: offset %d\n", f); + + sio = memAllocate(MEM_STORE_IO); + cbdataAdd(sio, storeCossIOFreeEntry, MEM_STORE_IO); + cstate = memPoolAlloc(coss_state_pool); + + sio->fsstate = cstate; + sio->swap_filen = f; + sio->swap_dirn = SD->index; + sio->offset = 0; + sio->mode = O_RDONLY; + sio->callback = callback; + sio->file_callback = file_callback; + sio->callback_data = callback_data; + cbdataLock(callback_data); + sio->st_size = e->swap_file_sz; + sio->e = e; + + cstate->flags.writing = 0; + cstate->flags.reading = 0; + cstate->readbuffer = NULL; + cstate->reqdiskoffset = -1; + p = storeCossMemPointerFromDiskOffset(SD, f, NULL); + // make local copy so we don't have to lock membuf + if (p) { + cstate->readbuffer = xmalloc(sio->st_size); + memcpy(cstate->readbuffer, p, sio->st_size); + } else { + /* Do the allocation */ + /* this is the first time we've been called on a new sio + read the whole object into memory, then return the + requested amount + */ + /* + * This bit of code actually does the LRU disk thing - we realloc + * a place for the object here, and the file_read() reads the object + * into the cossmembuf for later writing .. + */ + cstate->reqdiskoffset = sio->swap_filen; + sio->swap_filen = -1; + sio->swap_filen = storeCossAllocate(SD, e, COSS_ALLOC_REALLOC); + if (sio->swap_filen == -1) { + /* We have to clean up neatly .. */ + cbdataFree(sio); + cs->numcollisions++; + debug(81, 2) ("storeCossOpen: Reallocation of %d/%d failed\n", e->swap_dirn, e->swap_filen); + /* XXX XXX XXX Will squid call storeUnlink for this object? */ + return NULL; + } + /* Notify the upper levels that we've changed file number */ + sio->file_callback(sio->callback_data, 0, sio); + + /* + * lock the buffer so it doesn't get swapped out on us + * this will get unlocked in storeCossReadDone + */ + storeCossMemBufLock(SD, sio); + + /* + * Do the LRU magic to keep the disk and memory LRUs identical + */ + dlinkDelete(&sio->e->repl.lru, &SD->repl.lru.list); + dlinkAdd(sio->e, &sio->e->repl.lru, &SD->repl.lru.list); + + /* + * Since we've reallocated a spot for this object, we need to + * write it to the cossmembuf *and* return it in the read .. + */ + cstate->readbuffer = NULL; + } + return sio; +} + +void +storeCossClose(SwapDir *SD, storeIOState * sio) +{ + debug(81, 3) ("storeCossClose: offset %d\n",sio->swap_filen); + if (sio->mode == O_WRONLY) + storeCossMemBufUnlock(SD, sio); + storeCossIOCallback(sio, 0); +} + +void +storeCossRead(SwapDir *SD, storeIOState * sio, char *buf, size_t size, off_t offset, STRCB * callback, void *callback_data) +{ + char *p; + CossState *cstate = (CossState *)sio->fsstate; + CossInfo *cs = (CossInfo *)SD->fsdata; + + assert(sio->read.callback == NULL); + assert(sio->read.callback_data == NULL); + sio->read.callback = callback; + sio->read.callback_data = callback_data; + cbdataLock(callback_data); + debug(81, 3) ("storeCossRead: offset %d\n", offset); + sio->offset = offset; + cstate->flags.reading = 1; + if ((offset + size) > sio->st_size) + size = sio->st_size - offset; + cstate->requestlen = size; + cstate->requestbuf = buf; + cstate->requestoffset = offset; + if (cstate->readbuffer == NULL) { + p = storeCossMemPointerFromDiskOffset(SD, sio->swap_filen, NULL); + file_read(cs->fd, + p, + sio->st_size, + cstate->reqdiskoffset, + storeCossReadDone, + sio); + cstate->reqdiskoffset = 0; /* XXX */ + } else { + storeCossReadDone(cs->fd, + cstate->readbuffer, + sio->st_size, + 0, + sio); + } +} + +void +storeCossWrite(SwapDir *SD, storeIOState * sio, char *buf, size_t size, off_t offset, FREE * free_func) +{ + char *dest; + CossMemBuf *membuf; + off_t diskoffset; + + debug(81, 3) ("storeCossWrite: offset %d, len %d\n",sio->offset, size); + diskoffset = sio->swap_filen + sio->offset; + dest = storeCossMemPointerFromDiskOffset(SD, diskoffset, &membuf); + assert(dest != NULL); + memcpy(dest, buf, size); + sio->offset += size; + if (free_func) + (free_func)(buf); +} + + +/* === STATIC =========================================================== */ + +static void +storeCossReadDone(int fd, const char *buf, int len, int errflag, void *my_data) +{ + storeIOState *sio = my_data; + char *p; + STRCB *callback = sio->read.callback; + void *their_data = sio->read.callback_data; + SwapDir *SD = INDEXSD(sio->swap_dirn); + CossState *cstate = (CossState *)sio->fsstate; + size_t rlen; + + debug(81, 3) ("storeCossReadDone: fileno %d, FD %d, len %d\n", + sio->swap_filen, fd, len); + cstate->flags.reading = 0; + if (errflag) { + debug(81, 3) ("storeCossReadDone: got failure (%d)\n", errflag); + rlen = -1; + } else { + if (cstate->readbuffer == NULL) { + cstate->readbuffer = xmalloc(sio->st_size); + p = storeCossMemPointerFromDiskOffset(SD, sio->swap_filen, NULL); + memcpy(cstate->readbuffer, p, sio->st_size); + storeCossMemBufUnlock(SD, sio); + } + sio->offset += len; + memcpy(cstate->requestbuf, &cstate->readbuffer[cstate->requestoffset], + cstate->requestlen); + rlen = (size_t) cstate->requestlen; + } + assert(callback); + assert(their_data); + sio->read.callback = NULL; + sio->read.callback_data = NULL; + if (cbdataValid(their_data)) + callback(their_data, cstate->requestbuf, rlen); + cbdataUnlock(their_data); +} + +static void +storeCossIOCallback(storeIOState * sio, int errflag) +{ + CossState *cstate = (CossState *)sio->fsstate; + debug(81, 3) ("storeCossIOCallback: errflag=%d\n", errflag); + xfree(cstate->readbuffer); + if (cbdataValid(sio->callback_data)) + sio->callback(sio->callback_data, errflag, sio); + sio->callback_data = NULL; + cbdataFree(sio); +} + +static char * +storeCossMemPointerFromDiskOffset(SwapDir *SD, size_t offset, CossMemBuf **mb) +{ + CossMemBuf *t; + CossInfo *cs = (CossInfo *)SD->fsdata; + + for (t=cs->membufs; t; t = t->next) + if ((offset >= t->diskstart) && (offset <= t->diskend)) { + if (mb) + *mb = t; + return &t->buffer[offset - t->diskstart]; + } + if (mb) + *mb = NULL; + return NULL; +} + +static void +storeCossMemBufLock(SwapDir *SD, storeIOState *e) +{ + CossMemBuf *t; + CossInfo *cs = (CossInfo *)SD->fsdata; + + for (t = cs->membufs; t; t = t->next) + if ((e->swap_filen >= t->diskstart) && (e->swap_filen <= t->diskend)) { + debug(81, 3) ("storeCossMemBufLock: locking %08X, lockcount %d\n",t, t->lockcount); + t->lockcount++; + return; + } + debug(81, 3) ("storeCossMemBufLock: FAILED to lock %08X\n",e); +} + +static void +storeCossMemBufUnlock(SwapDir *SD, storeIOState *e) +{ + CossMemBuf *t; + CossInfo *cs = (CossInfo *)SD->fsdata; + + for (t = cs->membufs; t; t = t->next) { + if ((e->swap_filen >= t->diskstart) && (e->swap_filen <= t->diskend)) { + t->lockcount--; + debug(81, 3) ("storeCossMemBufUnlock: unlocking %08X, lockcount %d\n",t, t->lockcount); + } + if (t->flags.full && !t->flags.writing && !t->lockcount) + storeCossWriteMemBuf(SD, t); + } +} + + +static void +storeCossWriteMemBuf(SwapDir *SD, CossMemBuf *t) +{ + CossInfo *cs = (CossInfo *)SD->fsdata; + debug(81, 3) ("storeCossWriteMemBuf: offset %d, len %d\n", + t->diskstart, t->diskend - t->diskstart); + cbdataAdd(t, storeCossMembufFree, 0); + file_write(cs->fd, t->diskstart, &t->buffer, + t->diskend - t->diskstart, storeCossWriteMemBufDone, t, NULL); + t->flags.writing = 1; +} + + +static void +storeCossWriteMemBufDone(int fd, int errflag, size_t len, void *my_data) +{ + CossMemBuf *t = my_data; + CossMemBuf *p,*prev; + CossInfo *cs = (CossInfo *)t->SD->fsdata; + + debug(81, 3) ("storeCossWriteMemBufDone: len %d\n",len); + if (errflag) { + debug(81, 0) ("storeCossMemBufWriteDone: got failure (%d)\n", errflag); + cbdataFree(t); + return; + } + + if (t == cs->membufs) { + cs->membufs = t->next; + cbdataFree(t); + return; + } + prev = t; + for (p = cs->membufs; p; p = p->next) { + if (t == p) { + prev->next = t->next; + cbdataFree(t); + return; + } + prev = p; + } + cbdataFree(t); +} + +static CossMemBuf *storeCossCreateMemBuf(SwapDir *SD, size_t start, + sfileno curfn, int *collision) +{ + CossMemBuf *newmb,*t; + StoreEntry *e; + dlink_node *m,*prev; + int numreleased=0; + CossInfo *cs = (CossInfo *)SD->fsdata; + + newmb = memPoolAlloc(coss_membuf_pool); + newmb->diskstart = start; + debug(81, 3) ("storeCossCreateMemBuf: creating new membuf at %d\n",newmb->diskstart); + newmb->diskend = newmb->diskstart + COSS_MEMBUF_SZ - 1; + newmb->flags.full = 0; + newmb->flags.writing = 0; + newmb->lockcount = 0; + newmb->SD = SD; + newmb->next = cs->membufs; + cs->membufs = newmb; + for (t = cs->membufs; t; t = t->next) + debug(81, 3) ("storeCossCreateMemBuf: membuflist %d lockcount %d\n",t->diskstart,t->lockcount); + + /* + * XXX more evil LRU specific code. This needs to be replaced. + */ + for (m = SD->repl.lru.list.tail; m; m = prev) { + prev = m->prev; + e = m->data; + if (curfn == e->swap_filen) + *collision = 1; /* Mark an object alloc collision */ + if ((e->swap_filen >= newmb->diskstart) && + (e->swap_filen <= newmb->diskend)) { + storeRelease(e); + numreleased++; + } else + break; + } + if (numreleased > 0) + debug(81, 3) ("storeCossCreateMemBuf: this allocation released %d storeEntries\n",numreleased); + return newmb; +} + +/* + * We can't pass memFree() as a free function here, because we need to free + * the fsstate variable .. + */ +static void +storeCossIOFreeEntry(void *sio, int foo) +{ + memPoolFree(coss_state_pool, ((storeIOState *)sio)->fsstate); + memFree(sio, MEM_STORE_IO); +} + +/* + * We can't pass memFree() as a free function here, since we have to pass it + * an int to memFree(), and we aren't using static memory pool allocation here. + * So we have this hack here .. + */ +static void +storeCossMembufFree(void *mb, int foo) +{ + memPoolFree(coss_membuf_pool, mb); +} + diff --git a/src/fs/diskd/Makefile.in b/src/fs/diskd/Makefile.in new file mode 100644 index 0000000000..59358f627b --- /dev/null +++ b/src/fs/diskd/Makefile.in @@ -0,0 +1,93 @@ +# +# Makefile for the DISKD storage driver for the Squid Object Cache server +# +# $Id: Makefile.in,v 1.1 2000/05/03 17:15:47 adrian Exp $ +# + +FS = diskd + +prefix = @prefix@ +exec_prefix = @exec_prefix@ +exec_suffix = @exec_suffix@ +cgi_suffix = @cgi_suffix@ +top_srcdir = @top_srcdir@ +bindir = @bindir@ +libexecdir = @libexecdir@ +sysconfdir = @sysconfdir@ +localstatedir = @localstatedir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +CC = @CC@ +MAKEDEPEND = @MAKEDEPEND@ +AR_R = @AR_R@ +RANLIB = @RANLIB@ +AC_CFLAGS = @CFLAGS@ +SHELL = /bin/sh +LDFLAGS = @LDFLAGS@ +INSTALL = @INSTALL@ +INSTALL_BIN = @INSTALL_PROGRAM@ +MV = @MV@ +RM = @RM@ + +INCLUDE = -I../../../include -I$(top_srcdir)/include -I$(top_srcdir)/src/ +CFLAGS = $(AC_CFLAGS) $(INCLUDE) $(DEFINES) + +OUT = ../$(FS).a +DISKD_EXE = diskd$(exec_suffix) + + +OBJS = \ + store_dir_diskd.o \ + store_io_diskd.o + +UTILS = \ + $(DISKD_EXE) + +all: $(OUT) $(UTILS) + +$(OUT): $(OBJS) + @rm -f ../stamp + $(AR_R) $(OUT) $(OBJS) + $(RANLIB) $(OUT) + +$(OBJS): $(top_srcdir)/include/version.h ../../../include/autoconf.h + +.c.o: + @rm -f ../stamp + $(CC) -DSQUID_PREFIX=\"$(prefix)\" $(CFLAGS) -c $< + +clean: + -rm -rf *.o *pure_* core ../$(FS).a diskd + +distclean: clean + -rm -f Makefile + -rm -f Makefile.bak + -rm -f tags + +tags: + ctags *.[ch] $(top_srcdir)/src/*.[ch] $(top_srcdir)/include/*.h $(top_srcdir)/lib/*.[ch] + +depend: + $(MAKEDEPEND) $(INCLUDE) -fMakefile *.c + +diskd.o: $(srcdir)/diskd.c + $(CC) $(CFLAGS) $(srcdir)/diskd.c -c -o diskd.o + +$(DISKD_EXE): diskd.o + $(CC) $(LDFLAGS) -L../../../lib/ $(CFLAGS) diskd.o -o $(DISKD_EXE) -lmiscutil -lm + +install: $(UTILS) + @for f in $(UTILS); do \ + if test -f $(libexecdir)/$$f; then \ + echo $(MV) $(libexecdir)/$$f $(libexecdir)/-$$f; \ + $(MV) $(libexecdir)/$$f $(libexecdir)/-$$f; \ + fi; \ + echo $(INSTALL_BIN) $$f $(libexecdir); \ + $(INSTALL_BIN) $$f $(libexecdir); \ + if test -f $(libexecdir)/-$$f; then \ + echo $(RM) -f $(libexecdir)/-$$f; \ + $(RM) -f $(libexecdir)/-$$f; \ + fi; \ + done + diff --git a/src/fs/diskd/diskd.cc b/src/fs/diskd/diskd.cc new file mode 100644 index 0000000000..478337aa28 --- /dev/null +++ b/src/fs/diskd/diskd.cc @@ -0,0 +1,310 @@ + +#include "config.h" +#include "squid.h" + + +#include "store_diskd.h" + +#include +#include +#include + +#undef assert +#include + + +#define STDERR_DEBUG 0 + +typedef struct _file_state file_state; + +struct _file_state { + void *key; + file_state *next; + int id; + int fd; + off_t offset; +}; + +static hash_table *hash = NULL; +static pid_t mypid; +static char *shmbuf; + +static int +do_open(diomsg * r, int len, const char *buf) +{ + int fd; + file_state *fs; + /* + * note r->offset holds open() flags + */ + fd = open(buf, r->offset, 0600); + if (fd < 0) { + fprintf(stderr, "%d %s: ", (int) mypid, buf); + perror("open"); + return -errno; + } + fs = xcalloc(1, sizeof(*fs)); + fs->id = r->id; + fs->key = &fs->id; /* gack */ + fs->fd = fd; + hash_join(hash, (hash_link *) fs); +#if STDERR_DEBUG + fprintf(stderr, "%d OPEN id %d, FD %d, fs %p\n", + (int) mypid, + fs->id, + fs->fd, + fs); +#endif + return fd; +} + +static int +do_close(diomsg * r, int len) +{ + int fd; + file_state *fs; + fs = (file_state *) hash_lookup(hash, &r->id); + if (NULL == fs) { + errno = EBADF; + fprintf(stderr, "%d CLOSE id %d: ", (int) mypid, r->id); + perror("do_close"); + return -EBADF; + } + fd = fs->fd; + hash_remove_link(hash, (hash_link *) fs); +#if STDERR_DEBUG + fprintf(stderr, "%d CLOSE id %d, FD %d, fs %p\n", + (int) mypid, + r->id, + fs->fd, + fs); +#endif + xfree(fs); + return close(fd); +} + +static int +do_read(diomsg * r, int len, char *buf) +{ + int x; + int readlen = r->size; + file_state *fs; + fs = (file_state *) hash_lookup(hash, &r->id); + if (NULL == fs) { + errno = EBADF; + fprintf(stderr, "%d READ id %d: ", (int) mypid, r->id); + perror("do_read"); + return -EBADF; + } + if (r->offset > -1 && r->offset != fs->offset) { +#if STDERR_DEBUG + fprintf(stderr, "seeking to %d\n", r->offset); +#endif + if (lseek(fs->fd, r->offset, SEEK_SET) < 0) { + fprintf(stderr, "%d FD %d, offset %d: ", (int) mypid, fs->fd, r->offset); + perror("lseek"); + } + } + x = read(fs->fd, buf, readlen); +#if STDERR_DEBUG + fprintf(stderr, "%d READ %d,%d,%d ret %d\n", (int) mypid, + fs->fd, readlen, r->offset, x); +#endif + if (x < 0) { + fprintf(stderr, "%d FD %d: ", (int) mypid, fs->fd); + perror("read"); + return -errno; + } + fs->offset = r->offset + x; + return x; +} + +static int +do_write(diomsg * r, int len, const char *buf) +{ + int wrtlen = r->size; + int x; + file_state *fs; + fs = (file_state *) hash_lookup(hash, &r->id); + if (NULL == fs) { + errno = EBADF; + fprintf(stderr, "%d WRITE id %d: ", (int) mypid, r->id); + perror("do_write"); + return -EBADF; + } + if (r->offset > -1 && r->offset != fs->offset) { + if (lseek(fs->fd, r->offset, SEEK_SET) < 0) { + fprintf(stderr, "%d FD %d, offset %d: ", (int) mypid, fs->fd, r->offset); + perror("lseek"); + } + } +#if STDERR_DEBUG + fprintf(stderr, "%d WRITE %d,%d,%d\n", (int) mypid, + fs->fd, wrtlen, r->offset); +#endif + x = write(fs->fd, buf, wrtlen); + if (x < 0) { + fprintf(stderr, "%d FD %d: ", (int) mypid, fs->fd); + perror("write"); + return -errno; + } + fs->offset = r->offset + x; + return x; +} + +static int +do_unlink(diomsg * r, int len, const char *buf) +{ + if (truncate(buf, 0) < 0) { + fprintf(stderr, "%d UNLNK id %d %s: ", (int) mypid, r->id, buf); + perror("truncate"); + return -errno; + } +#if STDERR_DEBUG + fprintf(stderr, "%d UNLNK %s\n", (int) mypid, buf); +#endif + return 0; +} + +static void +msg_handle(diomsg * r, int rl, diomsg * s) +{ + char *buf = NULL; + s->mtype = r->mtype; + s->callback_data = r->callback_data; + s->shm_offset = r->shm_offset; + s->id = r->id; + if (s->shm_offset > -1) + buf = shmbuf + s->shm_offset; + switch (r->mtype) { + case _MQD_OPEN: + s->status = do_open(r, rl, buf); + break; + case _MQD_CLOSE: + s->status = do_close(r, rl); + break; + case _MQD_READ: + s->status = do_read(r, rl, buf); + break; + case _MQD_WRITE: + s->status = do_write(r, rl, buf); + break; + case _MQD_UNLINK: + s->status = do_unlink(r, rl, buf); + break; + default: + assert(0); + break; + } +} + +int +fsCmp(const void *a, const void *b) +{ + const int *A = a; + const int *B = b; + return *A != *B; +} + +unsigned int +fsHash(const void *key, unsigned int n) +{ + /* note, n must be a power of 2! */ + const int *k = key; + return (*k & (--n)); +} + +static void +alarm_handler(int sig) +{ + (void) 0; +} + +int +main(int argc, char *argv[]) +{ + int key; + int rmsgid; + int smsgid; + int shmid; + diomsg rmsg; + diomsg smsg; + int rlen; + char rbuf[512]; + struct sigaction sa; + setbuf(stdout, NULL); + setbuf(stderr, NULL); + mypid = getpid(); + assert(4 == argc); + key = atoi(argv[1]); + rmsgid = msgget(key, 0600); + if (rmsgid < 0) { + perror("msgget"); + return 1; + } + key = atoi(argv[2]); + smsgid = msgget(key, 0600); + if (smsgid < 0) { + perror("msgget"); + return 1; + } + key = atoi(argv[3]); + shmid = shmget(key, 0, 0600); + if (shmid < 0) { + perror("shmget"); + return 1; + } + shmbuf = shmat(shmid, NULL, 0); + if (shmbuf == (void *) -1) { + perror("shmat"); + return 1; + } + hash = hash_create(fsCmp, 1 << 4, fsHash); + assert(hash); + fcntl(0, F_SETFL, SQUID_NONBLOCK); + memset(&sa, '\0', sizeof(sa)); + sa.sa_handler = alarm_handler; + sa.sa_flags = SA_RESTART; + sigaction(SIGALRM, &sa, NULL); + for (;;) { + alarm(1); + memset(&rmsg, '\0', sizeof(rmsg)); + rlen = msgrcv(rmsgid, &rmsg, msg_snd_rcv_sz, 0, 0); + if (rlen < 0) { + if (EINTR == errno) { + if (read(0, rbuf, 512) <= 0) { + if (EWOULDBLOCK == errno) + (void) 0; + else if (EAGAIN == errno) + (void) 0; + else + break; + } + } + if (EAGAIN == errno) { + continue; + } + perror("msgrcv"); + break; + } + alarm(0); + msg_handle(&rmsg, rlen, &smsg); + if (msgsnd(smsgid, &smsg, msg_snd_rcv_sz, 0) < 0) { + perror("msgsnd"); + break; + } + } +#if STDERR_DEBUG + fprintf(stderr, "%d diskd exiting\n", (int) mypid); +#endif + if (msgctl(rmsgid, IPC_RMID, 0) < 0) + perror("msgctl IPC_RMID"); + if (msgctl(smsgid, IPC_RMID, 0) < 0) + perror("msgctl IPC_RMID"); + if (shmdt(shmbuf) < 0) + perror("shmdt"); + if (shmctl(shmid, IPC_RMID, 0) < 0) + perror("shmctl IPC_RMID"); + return 0; +} + diff --git a/src/fs/diskd/store_dir_diskd.cc b/src/fs/diskd/store_dir_diskd.cc new file mode 100644 index 0000000000..5fbb7d1f45 --- /dev/null +++ b/src/fs/diskd/store_dir_diskd.cc @@ -0,0 +1,2125 @@ + +/* + * $Id: store_dir_diskd.cc,v 1.1 2000/05/03 17:15:47 adrian Exp $ + * + * DEBUG: section 47 Store Directory Routines + * AUTHOR: Duane Wessels + * + * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from the + * Internet community. Development is led by Duane Wessels of the + * National Laboratory for Applied Network Research and funded by the + * National Science Foundation. Squid is Copyrighted (C) 1998 by + * Duane Wessels and the University of California San Diego. Please + * see the COPYRIGHT file for full details. Squid incorporates + * software developed and/or copyrighted by other sources. Please see + * the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#include "squid.h" +#if HAVE_STATVFS +#if HAVE_SYS_STATVFS_H +#include +#endif +#endif + +#include +#include +#include + +#include "store_diskd.h" + +#define DefaultLevelOneDirs 16 +#define DefaultLevelTwoDirs 256 +#define STORE_META_BDISKDZ 4096 + +#ifndef SQUID_PREFIX +#error "SQUID_PREFIX needs defining!" +#endif + +diskd_stats_t diskd_stats; + +typedef struct _RebuildState RebuildState; +struct _RebuildState { + SwapDir *sd; + int n_read; + FILE *log; + int speed; + int curlvl1; + int curlvl2; + struct { + unsigned int need_to_validate:1; + unsigned int clean:1; + unsigned int init:1; + } flags; + int done; + int in_dir; + int fn; + struct dirent *entry; + DIR *td; + char fullpath[SQUID_MAXPATHLEN]; + char fullfilename[SQUID_MAXPATHLEN]; + struct _store_rebuild_data counts; +}; + +static int n_diskd_dirs = 0; +static int *diskd_dir_index = NULL; +MemPool * diskd_state_pool = NULL; +static int diskd_initialised = 0; + +static char *storeDiskdDirSwapSubDir(SwapDir *, int subdirn); +static int storeDiskdDirCreateDirectory(const char *path, int); +static int storeDiskdDirVerifyCacheDirs(SwapDir *); +static int storeDiskdDirVerifyDirectory(const char *path); +static void storeDiskdDirCreateSwapSubDirs(SwapDir *); +static char *storeDiskdDirSwapLogFile(SwapDir *, const char *); +static EVH storeDiskdDirRebuildFromDirectory; +static EVH storeDiskdDirRebuildFromSwapLog; +static int storeDiskdDirGetNextFile(RebuildState *, int *sfileno, int *size); +static StoreEntry *storeDiskdDirAddDiskRestore(SwapDir *SD, const cache_key * key, + int file_number, + size_t swap_file_sz, + time_t expires, + time_t timestamp, + time_t lastref, + time_t lastmod, + u_num32 refcount, + u_short flags, + int clean); +static void storeDiskdDirRebuild(SwapDir * sd); +static void storeDiskdDirCloseTmpSwapLog(SwapDir * sd); +static FILE *storeDiskdDirOpenTmpSwapLog(SwapDir *, int *, int *); +static STLOGOPEN storeDiskdDirOpenSwapLog; +static STINIT storeDiskdDirInit; +static STFREE storeDiskdDirFree; +static STLOGCLEANOPEN storeDiskdDirWriteCleanOpen; +static void storeDiskdDirWriteCleanClose(SwapDir * sd); +static STLOGCLEANWRITE storeDiskdDirWriteCleanEntry; +static STLOGCLOSE storeDiskdDirCloseSwapLog; +static STLOGWRITE storeDiskdDirSwapLog; +static STNEWFS storeDiskdDirNewfs; +static STDUMP storeDiskdDirDump; +static STMAINTAINFS storeDiskdDirMaintain; +static STCHECKOBJ storeDiskdDirCheckObj; +static STREFOBJ storeDiskdDirRefObj; +static STUNREFOBJ storeDiskdDirUnrefObj; +static QS rev_int_sort; +static int storeDiskdDirClean(int swap_index); +static EVH storeDiskdDirCleanEvent; +static int storeDiskdDirIs(SwapDir * sd); +static int storeDiskdFilenoBelongsHere(int fn, int F0, int F1, int F2); +static int storeDiskdCleanupDoubleCheck(SwapDir *, StoreEntry *); +static void storeDiskdDirStats(SwapDir *, StoreEntry *); +static void storeDiskdDirInitBitmap(SwapDir *); +static int storeDiskdDirValidFileno(SwapDir *, sfileno); +static int storeDiskdDirCheckExpired(SwapDir *, StoreEntry *); +#if !HEAP_REPLACEMENT +static time_t storeDiskdDirExpiredReferenceAge(SwapDir *); +#endif +static void storeDiskdStats(StoreEntry * sentry); +static void storeDiskdDirSync(SwapDir *); +static void storeDiskdDirCallback(SwapDir *); + + +/* + * These functions were ripped straight out of the heart of store_dir.c. + * They assume that the given filenum is on a diskd partiton, which may or + * may not be true.. + * XXX this evilness should be tidied up at a later date! + */ + +int +storeDiskdDirMapBitTest(SwapDir *SD, int fn) +{ + sfileno filn = fn; + diskdinfo_t *diskdinfo; + diskdinfo = (diskdinfo_t *)SD->fsdata; + return file_map_bit_test(diskdinfo->map, filn); +} + +void +storeDiskdDirMapBitSet(SwapDir *SD, int fn) +{ + sfileno filn = fn; + diskdinfo_t *diskdinfo; + diskdinfo = (diskdinfo_t *)SD->fsdata; + file_map_bit_set(diskdinfo->map, filn); +} + +void +storeDiskdDirMapBitReset(SwapDir *SD, int fn) +{ + sfileno filn = fn; + diskdinfo_t *diskdinfo; + diskdinfo = (diskdinfo_t *)SD->fsdata; + file_map_bit_reset(diskdinfo->map, filn); +} + +int +storeDiskdDirMapBitAllocate(SwapDir *SD) +{ + diskdinfo_t *diskdinfo = (diskdinfo_t *)SD->fsdata; + int fn; + fn = file_map_allocate(diskdinfo->map, diskdinfo->suggest); + file_map_bit_set(diskdinfo->map, fn); + diskdinfo->suggest = fn + 1; + return fn; +} + +/* + * Initialise the diskd bitmap + * + * If there already is a bitmap, and the numobjects is larger than currently + * configured, we allocate a new bitmap and 'grow' the old one into it. + */ +static void +storeDiskdDirInitBitmap(SwapDir *sd) +{ + diskdinfo_t *diskdinfo = (diskdinfo_t *)sd->fsdata; + + if (diskdinfo->map == NULL) { + /* First time */ + diskdinfo->map = file_map_create(); + } else if (diskdinfo->map->max_n_files) { + /* it grew, need to expand */ + /* XXX We don't need it anymore .. */ + } + /* else it shrunk, and we leave the old one in place */ +} + +static char * +storeDiskdDirSwapSubDir(SwapDir * sd, int subdirn) +{ + diskdinfo_t *diskdinfo = (diskdinfo_t *)sd->fsdata; + + LOCAL_ARRAY(char, fullfilename, SQUID_MAXPATHLEN); + assert(0 <= subdirn && subdirn < diskdinfo->l1); + snprintf(fullfilename, SQUID_MAXPATHLEN, "%s/%02X", sd->path, subdirn); + return fullfilename; +} + +static int +storeDiskdDirCreateDirectory(const char *path, int should_exist) +{ + int created = 0; + struct stat st; + getCurrentTime(); + if (0 == stat(path, &st)) { + if (S_ISDIR(st.st_mode)) { + debug(20, should_exist ? 3 : 1) ("%s exists\n", path); + } else { + fatalf("Swap directory %s is not a directory.", path); + } + } else if (0 == mkdir(path, 0755)) { + debug(20, should_exist ? 1 : 3) ("%s created\n", path); + created = 1; + } else { + fatalf("Failed to make swap directory %s: %s", + path, xstrerror()); + } + return created; +} + +static int +storeDiskdDirVerifyDirectory(const char *path) +{ + struct stat sb; + if (stat(path, &sb) < 0) { + debug(20, 0) ("%s: %s\n", path, xstrerror()); + return -1; + } + if (S_ISDIR(sb.st_mode) == 0) { + debug(20, 0) ("%s is not a directory\n", path); + return -1; + } + return 0; +} + +/* + * This function is called by storeDiskdDirInit(). If this returns < 0, + * then Squid exits, complains about swap directories not + * existing, and instructs the admin to run 'squid -z' + */ +static int +storeDiskdDirVerifyCacheDirs(SwapDir * sd) +{ + diskdinfo_t *diskdinfo = (diskdinfo_t *)sd->fsdata; + int j; + const char *path = sd->path; + + if (storeDiskdDirVerifyDirectory(path) < 0) + return -1; + for (j = 0; j < diskdinfo->l1; j++) { + path = storeDiskdDirSwapSubDir(sd, j); + if (storeDiskdDirVerifyDirectory(path) < 0) + return -1; + } + return 0; +} + +static void +storeDiskdDirCreateSwapSubDirs(SwapDir * sd) +{ + diskdinfo_t *diskdinfo = (diskdinfo_t *)sd->fsdata; + int i, k; + int should_exist; + LOCAL_ARRAY(char, name, MAXPATHLEN); + for (i = 0; i < diskdinfo->l1; i++) { + snprintf(name, MAXPATHLEN, "%s/%02X", sd->path, i); + if (storeDiskdDirCreateDirectory(name, 0)) + should_exist = 0; + else + should_exist = 1; + debug(47, 1) ("Making directories in %s\n", name); + for (k = 0; k < diskdinfo->l2; k++) { + snprintf(name, MAXPATHLEN, "%s/%02X/%02X", sd->path, i, k); + storeDiskdDirCreateDirectory(name, should_exist); + } + } +} + +static char * +storeDiskdDirSwapLogFile(SwapDir * sd, const char *ext) +{ + LOCAL_ARRAY(char, path, SQUID_MAXPATHLEN); + LOCAL_ARRAY(char, pathtmp, SQUID_MAXPATHLEN); + LOCAL_ARRAY(char, digit, 32); + char *pathtmp2; + if (Config.Log.swap) { + xstrncpy(pathtmp, sd->path, SQUID_MAXPATHLEN - 64); + while (index(pathtmp,'/')) + *index(pathtmp,'/')='.'; + while (strlen(pathtmp) && pathtmp[strlen(pathtmp)-1]=='.') + pathtmp[strlen(pathtmp)-1]= '\0'; + for(pathtmp2 = pathtmp; *pathtmp2 == '.'; pathtmp2++); + snprintf(path, SQUID_MAXPATHLEN-64, Config.Log.swap, pathtmp2); + if (strncmp(path, Config.Log.swap, SQUID_MAXPATHLEN - 64) == 0) { + strcat(path, "."); + snprintf(digit, 32, "%02d", sd->index); + strncat(path, digit, 3); + } + } else { + xstrncpy(path, sd->path, SQUID_MAXPATHLEN - 64); + strcat(path, "/swap.state"); + } + if (ext) + strncat(path, ext, 16); + return path; +} + +static void +storeDiskdDirOpenSwapLog(SwapDir * sd) +{ + diskdinfo_t *diskdinfo = (diskdinfo_t *)sd->fsdata; + char *path; + int fd; + path = storeDiskdDirSwapLogFile(sd, NULL); + fd = file_open(path, O_WRONLY | O_CREAT); + if (fd < 0) { + debug(50, 1) ("%s: %s\n", path, xstrerror()); + fatal("storeDiskdDirOpenSwapLog: Failed to open swap log."); + } + debug(47, 3) ("Cache Dir #%d log opened on FD %d\n", sd->index, fd); + diskdinfo->swaplog_fd = fd; + if (0 == n_diskd_dirs) + assert(NULL == diskd_dir_index); + n_diskd_dirs++; + assert(n_diskd_dirs <= Config.cacheSwap.n_configured); +} + +static void +storeDiskdDirCloseSwapLog(SwapDir * sd) +{ + diskdinfo_t *diskdinfo = (diskdinfo_t *)sd->fsdata; + if (diskdinfo->swaplog_fd < 0) /* not open */ + return; + file_close(diskdinfo->swaplog_fd); + debug(47, 3) ("Cache Dir #%d log closed on FD %d\n", + sd->index, diskdinfo->swaplog_fd); + diskdinfo->swaplog_fd = -1; + n_diskd_dirs--; + assert(n_diskd_dirs >= 0); + if (0 == n_diskd_dirs) + safe_free(diskd_dir_index); +} + +static void +storeDiskdDirInit(SwapDir * sd) +{ + static int started_clean_event = 0; + int x; + int i; + int rfd; + int ikey = (getpid() << 16) + (sd->index << 4); + char *args[5]; + char skey1[32]; + char skey2[32]; + char skey3[32]; + diskdinfo_t *diskdinfo = (diskdinfo_t *) sd->fsdata; + static const char *errmsg = + "\tFailed to verify one of the swap directories, Check cache.log\n" + "\tfor details. Run 'squid -z' to create swap directories\n" + "\tif needed, or if running Squid for the first time."; + + diskdinfo->smsgid = msgget((key_t) ikey, 0700 | IPC_CREAT); + if (diskdinfo->smsgid < 0) { + debug(50, 0) ("storeDiskdInit: msgget: %s\n", xstrerror()); + fatal("msgget failed"); + } + diskdinfo->rmsgid = msgget((key_t) (ikey + 1), 0700 | IPC_CREAT); + if (diskdinfo->rmsgid < 0) { + debug(50, 0) ("storeDiskdInit: msgget: %s\n", xstrerror()); + fatal("msgget failed"); + } + diskdinfo->shm.id = shmget((key_t) (ikey + 2), + SHMBUFS * SHMBUF_BLKSZ, 0600 | IPC_CREAT); + if (diskdinfo->shm.id < 0) { + debug(50, 0) ("storeDiskdInit: shmget: %s\n", xstrerror()); + fatal("shmget failed"); + } + diskdinfo->shm.buf = shmat(diskdinfo->shm.id, NULL, 0); + if (diskdinfo->shm.buf == (void *) -1) { + debug(50, 0) ("storeDiskdInit: shmat: %s\n", xstrerror()); + fatal("shmat failed"); + } + diskd_stats.shmbuf_count += SHMBUFS; + for (i = 0; i < SHMBUFS; i++) + storeDiskdShmPut(sd, i * SHMBUF_BLKSZ); + snprintf(skey1, 32, "%d", ikey); + snprintf(skey2, 32, "%d", ikey + 1); + snprintf(skey3, 32, "%d", ikey + 2); + args[0] = "diskd"; + args[1] = skey1; + args[2] = skey2; + args[3] = skey3; + args[4] = NULL; +#if HAVE_POLL && defined(_SQUID_OSF_) + /* pipes and poll() don't get along on DUNIX -DW */ + x = ipcCreate(IPC_TCP_SOCKET, +#else + x = ipcCreate(IPC_FIFO, +#endif + SQUID_PREFIX "/bin/diskd", + args, + "diskd", + &rfd, + &diskdinfo->wfd); + if (x < 0) + fatal("execl " SQUID_PREFIX "/bin/diskd failed"); + if (rfd != diskdinfo->wfd) + comm_close(rfd); + fd_note(diskdinfo->wfd, "squid -> diskd"); + commSetTimeout(diskdinfo->wfd, -1, NULL, NULL); + commSetNonBlocking(diskdinfo->wfd); + storeDiskdDirInitBitmap(sd); + if (storeDiskdDirVerifyCacheDirs(sd) < 0) + fatal(errmsg); + storeDiskdDirOpenSwapLog(sd); + storeDiskdDirRebuild(sd); + if (!started_clean_event) { + eventAdd("storeDirClean", storeDiskdDirCleanEvent, NULL, 15.0, 1); + started_clean_event = 1; + } +} + + +static void +storeDiskdStats(StoreEntry * sentry) +{ + storeAppendPrintf(sentry, "sent_count: %d\n", diskd_stats.sent_count); + storeAppendPrintf(sentry, "recv_count: %d\n", diskd_stats.recv_count); + storeAppendPrintf(sentry, "max_away: %d\n", diskd_stats.max_away); + storeAppendPrintf(sentry, "max_shmuse: %d\n", diskd_stats.max_shmuse); + storeAppendPrintf(sentry, "open_fail_queue_len: %d\n", diskd_stats.open_fail_queue_len); + storeAppendPrintf(sentry, "block_queue_len: %d\n", diskd_stats.block_queue_len); + diskd_stats.max_away = diskd_stats.max_shmuse = 0; +} + +/* + * storeDiskdDirSync + * + * Sync any pending data. We just sit around and read the queue + * until the data has finished writing. + */ +static void +storeDiskdDirSync(SwapDir *SD) +{ + /* XXX NOT DONE YET! */ +#warning "storeDiskdSync() needs to be written" +} + + +/* + * storeDiskdDirCallback + * + * Handle callbacks. If we have more than magic2 requests away, we block + * until the queue is below magic2. Otherwise, we simply return when we + * don't get a message. + */ +static void +storeDiskdDirCallback(SwapDir *SD) +{ + diomsg M; + int x; + diskdinfo_t *diskdinfo = (diskdinfo_t *)SD->fsdata; + + if (diskdinfo->away >= diskdinfo->magic2) + diskd_stats.block_queue_len++; + + if (diskd_stats.sent_count - diskd_stats.recv_count > + diskd_stats.max_away) { + diskd_stats.max_away = diskd_stats.sent_count - diskd_stats.recv_count; + diskd_stats.max_shmuse = diskd_stats.shmbuf_count; + } + + /* if we are above magic2, we do not break under any reason */ + while (1) { + memset(&M, '\0', sizeof(M)); + x = msgrcv(diskdinfo->rmsgid, &M, msg_snd_rcv_sz, 0, IPC_NOWAIT); + if (x < 0) { + if (diskdinfo->away >= diskdinfo->magic2) + continue; + else + break; + } else if (x != msg_snd_rcv_sz) { + debug(81, 1) ("storeDiskdReadIndividualQueue: msgget returns %d\n", + x); + break; + } + diskd_stats.recv_count++; + diskdinfo->away--; + storeDiskdHandle(&M); + if (M.shm_offset > -1) + storeDiskdShmPut(SD, M.shm_offset); + } +} + + + +static void +storeDiskdDirRebuildFromDirectory(void *data) +{ + RebuildState *rb = data; + SwapDir *SD = rb->sd; + LOCAL_ARRAY(char, hdr_buf, SM_PAGE_SIZE); + StoreEntry *e = NULL; + StoreEntry tmpe; + cache_key key[MD5_DIGEST_CHARS]; + int sfileno = 0; + int count; + int size; + struct stat sb; + int swap_hdr_len; + int fd = -1; + tlv *tlv_list; + tlv *t; + assert(rb != NULL); + debug(20, 3) ("storeDiskdDirRebuildFromDirectory: DIR #%d\n", rb->sd->index); + for (count = 0; count < rb->speed; count++) { + assert(fd == -1); + fd = storeDiskdDirGetNextFile(rb, &sfileno, &size); + if (fd == -2) { + debug(20, 1) ("Done scanning %s swaplog (%d entries)\n", + rb->sd->path, rb->n_read); + store_dirs_rebuilding--; + storeDiskdDirCloseTmpSwapLog(rb->sd); + storeRebuildComplete(&rb->counts); + cbdataFree(rb); + return; + } else if (fd < 0) { + continue; + } + assert(fd > -1); + /* lets get file stats here */ + if (fstat(fd, &sb) < 0) { + debug(20, 1) ("storeDiskdDirRebuildFromDirectory: fstat(FD %d): %s\n", + fd, xstrerror()); + file_close(fd); + store_open_disk_fd--; + fd = -1; + continue; + } + if ((++rb->counts.scancount & 0xFFFF) == 0) + debug(20, 3) (" %s %7d files opened so far.\n", + rb->sd->path, rb->counts.scancount); + debug(20, 9) ("file_in: fd=%d %08X\n", fd, sfileno); + Counter.syscalls.disk.reads++; + if (read(fd, hdr_buf, SM_PAGE_SIZE) < 0) { + debug(20, 1) ("storeDiskdDirRebuildFromDirectory: read(FD %d): %s\n", + fd, xstrerror()); + file_close(fd); + store_open_disk_fd--; + fd = -1; + continue; + } + file_close(fd); + store_open_disk_fd--; + fd = -1; + swap_hdr_len = 0; +#if USE_TRUNCATE + if (sb.st_size == 0) + continue; +#endif + tlv_list = storeSwapMetaUnpack(hdr_buf, &swap_hdr_len); + if (tlv_list == NULL) { + debug(20, 1) ("storeDiskdDirRebuildFromDirectory: failed to get meta data\n"); + /* XXX shouldn't this be a call to storeDiskdUnlink ? */ + storeDiskdDirUnlinkFile(SD, sfileno); + continue; + } + debug(20, 3) ("storeDiskdDirRebuildFromDirectory: successful swap meta unpacking\n"); + memset(key, '\0', MD5_DIGEST_CHARS); + memset(&tmpe, '\0', sizeof(StoreEntry)); + for (t = tlv_list; t; t = t->next) { + switch (t->type) { + case STORE_META_KEY: + assert(t->length == MD5_DIGEST_CHARS); + xmemcpy(key, t->value, MD5_DIGEST_CHARS); + break; + case STORE_META_STD: + assert(t->length == STORE_HDR_METASIZE); + xmemcpy(&tmpe.timestamp, t->value, STORE_HDR_METASIZE); + break; + default: + break; + } + } + storeSwapTLVFree(tlv_list); + tlv_list = NULL; + if (storeKeyNull(key)) { + debug(20, 1) ("storeDiskdDirRebuildFromDirectory: NULL key\n"); + storeDiskdDirUnlinkFile(SD, sfileno); + continue; + } + tmpe.key = key; + /* check sizes */ + if (tmpe.swap_file_sz == 0) { + tmpe.swap_file_sz = sb.st_size; + } else if (tmpe.swap_file_sz == sb.st_size - swap_hdr_len) { + tmpe.swap_file_sz = sb.st_size; + } else if (tmpe.swap_file_sz != sb.st_size) { + debug(20, 1) ("storeDiskdDirRebuildFromDirectory: SIZE MISMATCH %d!=%d\n", + tmpe.swap_file_sz, (int) sb.st_size); + storeDiskdDirUnlinkFile(SD, sfileno); + continue; + } + if (EBIT_TEST(tmpe.flags, KEY_PRIVATE)) { + storeDiskdDirUnlinkFile(SD, sfileno); + rb->counts.badflags++; + continue; + } + e = storeGet(key); + if (e && e->lastref >= tmpe.lastref) { + /* key already exists, current entry is newer */ + /* keep old, ignore new */ + rb->counts.dupcount++; + continue; + } else if (NULL != e) { + /* URL already exists, this swapfile not being used */ + /* junk old, load new */ + storeRelease(e); /* release old entry */ + rb->counts.dupcount++; + } + rb->counts.objcount++; + storeEntryDump(&tmpe, 5); + e = storeDiskdDirAddDiskRestore(SD, key, + sfileno, + tmpe.swap_file_sz, + tmpe.expires, + tmpe.timestamp, + tmpe.lastref, + tmpe.lastmod, + tmpe.refcount, /* refcount */ + tmpe.flags, /* flags */ + (int) rb->flags.clean); + storeDirSwapLog(e, SWAP_LOG_ADD); + } + eventAdd("storeRebuild", storeDiskdDirRebuildFromDirectory, rb, 0.0, 1); +} + +static void +storeDiskdDirRebuildFromSwapLog(void *data) +{ + RebuildState *rb = data; + SwapDir *SD = rb->sd; + StoreEntry *e = NULL; + storeSwapLogData s; + size_t ss = sizeof(storeSwapLogData); + int count; + int used; /* is swapfile already in use? */ + int disk_entry_newer; /* is the log entry newer than current entry? */ + double x; + assert(rb != NULL); + /* load a number of objects per invocation */ + for (count = 0; count < rb->speed; count++) { + if (fread(&s, ss, 1, rb->log) != 1) { + debug(20, 1) ("Done reading %s swaplog (%d entries)\n", + rb->sd->path, rb->n_read); + fclose(rb->log); + rb->log = NULL; + store_dirs_rebuilding--; + storeDiskdDirCloseTmpSwapLog(rb->sd); + storeRebuildComplete(&rb->counts); + cbdataFree(rb); + return; + } + rb->n_read++; + if (s.op <= SWAP_LOG_NOP) + continue; + if (s.op >= SWAP_LOG_MAX) + continue; + debug(20, 3) ("storeDiskdDirRebuildFromSwapLog: %s %s %08X\n", + swap_log_op_str[(int) s.op], + storeKeyText(s.key), + s.swap_filen); + if (s.op == SWAP_LOG_ADD) { + (void) 0; + } else if (s.op == SWAP_LOG_DEL) { + if ((e = storeGet(s.key)) != NULL) { + /* + * Make sure we don't unlink the file, it might be + * in use by a subsequent entry. Also note that + * we don't have to subtract from store_swap_size + * because adding to store_swap_size happens in + * the cleanup procedure. + */ + storeExpireNow(e); + storeReleaseRequest(e); + storeDiskdDirReplRemove(e); + if (e->swap_filen > -1) { + storeDiskdDirMapBitReset(SD, e->swap_filen); + e->swap_filen = -1; + e->swap_dirn = -1; + } + storeRelease(e); + rb->counts.objcount--; + rb->counts.cancelcount++; + } + continue; + } else { + x = log(++rb->counts.bad_log_op) / log(10.0); + if (0.0 == x - (double) (int) x) + debug(20, 1) ("WARNING: %d invalid swap log entries found\n", + rb->counts.bad_log_op); + rb->counts.invalid++; + continue; + } + if ((++rb->counts.scancount & 0xFFFF) == 0) + debug(20, 3) (" %7d %s Entries read so far.\n", + rb->counts.scancount, rb->sd->path); + if (!storeDiskdDirValidFileno(SD, s.swap_filen)) { + rb->counts.invalid++; + continue; + } + if (EBIT_TEST(s.flags, KEY_PRIVATE)) { + rb->counts.badflags++; + continue; + } + e = storeGet(s.key); + used = storeDiskdDirMapBitTest(SD, s.swap_filen); + /* If this URL already exists in the cache, does the swap log + * appear to have a newer entry? Compare 'lastref' from the + * swap log to e->lastref. */ + disk_entry_newer = e ? (s.lastref > e->lastref ? 1 : 0) : 0; + if (used && !disk_entry_newer) { + /* log entry is old, ignore it */ + rb->counts.clashcount++; + continue; + } else if (used && e && e->swap_filen == s.swap_filen && e->swap_dirn == SD->index) { + /* swapfile taken, same URL, newer, update meta */ + if (e->store_status == STORE_OK) { + e->lastref = s.timestamp; + e->timestamp = s.timestamp; + e->expires = s.expires; + e->lastmod = s.lastmod; + e->flags = s.flags; + e->refcount += s.refcount; +#if HEAP_REPLACEMENT + storeHeapPositionUpdate(e, SD); + storeDiskdDirUnrefObj(SD, e); +#endif + } else { + debug_trap("storeDiskdDirRebuildFromSwapLog: bad condition"); + debug(20, 1) ("\tSee %s:%d\n", __FILE__, __LINE__); + } + continue; + } else if (used) { + /* swapfile in use, not by this URL, log entry is newer */ + /* This is sorta bad: the log entry should NOT be newer at this + * point. If the log is dirty, the filesize check should have + * caught this. If the log is clean, there should never be a + * newer entry. */ + debug(20, 1) ("WARNING: newer swaplog entry for dirno %d, fileno %08X\n", + SD->index, s.swap_filen); + /* I'm tempted to remove the swapfile here just to be safe, + * but there is a bad race condition in the NOVM version if + * the swapfile has recently been opened for writing, but + * not yet opened for reading. Because we can't map + * swapfiles back to StoreEntrys, we don't know the state + * of the entry using that file. */ + /* We'll assume the existing entry is valid, probably because + * were in a slow rebuild and the the swap file number got taken + * and the validation procedure hasn't run. */ + assert(rb->flags.need_to_validate); + rb->counts.clashcount++; + continue; + } else if (e && !disk_entry_newer) { + /* key already exists, current entry is newer */ + /* keep old, ignore new */ + rb->counts.dupcount++; + continue; + } else if (e) { + /* key already exists, this swapfile not being used */ + /* junk old, load new */ + storeExpireNow(e); + storeReleaseRequest(e); + storeDiskdDirReplRemove(e); + if (e->swap_filen > -1) { + /* Make sure we don't actually unlink the file */ + storeDiskdDirMapBitReset(SD, e->swap_filen); + e->swap_filen = -1; + e->swap_dirn = -1; + } + storeRelease(e); + rb->counts.dupcount++; + } else { + /* URL doesnt exist, swapfile not in use */ + /* load new */ + (void) 0; + } + /* update store_swap_size */ + rb->counts.objcount++; + e = storeDiskdDirAddDiskRestore(SD, s.key, + s.swap_filen, + s.swap_file_sz, + s.expires, + s.timestamp, + s.lastref, + s.lastmod, + s.refcount, + s.flags, + (int) rb->flags.clean); + storeDirSwapLog(e, SWAP_LOG_ADD); + } + eventAdd("storeRebuild", storeDiskdDirRebuildFromSwapLog, rb, 0.0, 1); +} + +static int +storeDiskdDirGetNextFile(RebuildState * rb, int *sfileno, int *size) +{ + SwapDir *SD = rb->sd; + diskdinfo_t *diskdinfo = (diskdinfo_t *)SD->fsdata; + int fd = -1; + int used = 0; + int dirs_opened = 0; + debug(20, 3) ("storeDiskdDirGetNextFile: flag=%d, %d: /%02X/%02X\n", + rb->flags.init, + rb->sd->index, + rb->curlvl1, + rb->curlvl2); + if (rb->done) + return -2; + while (fd < 0 && rb->done == 0) { + fd = -1; + if (0 == rb->flags.init) { /* initialize, open first file */ + rb->done = 0; + rb->curlvl1 = 0; + rb->curlvl2 = 0; + rb->in_dir = 0; + rb->flags.init = 1; + assert(Config.cacheSwap.n_configured > 0); + } + if (0 == rb->in_dir) { /* we need to read in a new directory */ + snprintf(rb->fullpath, SQUID_MAXPATHLEN, "%s/%02X/%02X", + rb->sd->path, + rb->curlvl1, rb->curlvl2); + if (rb->flags.init && rb->td != NULL) + closedir(rb->td); + rb->td = NULL; + if (dirs_opened) + return -1; + rb->td = opendir(rb->fullpath); + dirs_opened++; + if (rb->td == NULL) { + debug(50, 1) ("storeDiskdDirGetNextFile: opendir: %s: %s\n", + rb->fullpath, xstrerror()); + } else { + rb->entry = readdir(rb->td); /* skip . and .. */ + rb->entry = readdir(rb->td); + if (rb->entry == NULL && errno == ENOENT) + debug(20, 1) ("storeDiskdDirGetNextFile: directory does not exist!.\n"); + debug(20, 3) ("storeDiskdDirGetNextFile: Directory %s\n", rb->fullpath); + } + } + if (rb->td != NULL && (rb->entry = readdir(rb->td)) != NULL) { + rb->in_dir++; + if (sscanf(rb->entry->d_name, "%x", &rb->fn) != 1) { + debug(20, 3) ("storeDiskdDirGetNextFile: invalid %s\n", + rb->entry->d_name); + continue; + } + if (!storeDiskdFilenoBelongsHere(rb->fn, rb->sd->index, rb->curlvl1, rb->curlvl2)) { + debug(20, 3) ("storeDiskdDirGetNextFile: %08X does not belong in %d/%d/%d\n", + rb->fn, rb->sd->index, rb->curlvl1, rb->curlvl2); + continue; + } + used = storeDiskdDirMapBitTest(SD, rb->fn); + if (used) { + debug(20, 3) ("storeDiskdDirGetNextFile: Locked, continuing with next.\n"); + continue; + } + snprintf(rb->fullfilename, SQUID_MAXPATHLEN, "%s/%s", + rb->fullpath, rb->entry->d_name); + debug(20, 3) ("storeDiskdDirGetNextFile: Opening %s\n", rb->fullfilename); + fd = file_open(rb->fullfilename, O_RDONLY); + if (fd < 0) + debug(50, 1) ("storeDiskdDirGetNextFile: %s: %s\n", rb->fullfilename, xstrerror()); + else + store_open_disk_fd++; + continue; + } + rb->in_dir = 0; + if (++rb->curlvl2 < diskdinfo->l2) + continue; + rb->curlvl2 = 0; + if (++rb->curlvl1 < diskdinfo->l1) + continue; + rb->curlvl1 = 0; + rb->done = 1; + } + *sfileno = rb->fn; + return fd; +} + +/* Add a new object to the cache with empty memory copy and pointer to disk + * use to rebuild store from disk. */ +static StoreEntry * +storeDiskdDirAddDiskRestore(SwapDir *SD, const cache_key * key, + int file_number, + size_t swap_file_sz, + time_t expires, + time_t timestamp, + time_t lastref, + time_t lastmod, + u_num32 refcount, + u_short flags, + int clean) +{ + StoreEntry *e = NULL; + debug(20, 5) ("storeDiskdAddDiskRestore: %s, fileno=%08X\n", storeKeyText(key), file_number); + /* if you call this you'd better be sure file_number is not + * already in use! */ + e = new_StoreEntry(STORE_ENTRY_WITHOUT_MEMOBJ, NULL, NULL); + e->store_status = STORE_OK; + storeSetMemStatus(e, NOT_IN_MEMORY); + e->swap_status = SWAPOUT_DONE; + e->swap_filen = file_number; + e->swap_dirn = SD->index; + e->swap_file_sz = swap_file_sz; + e->lock_count = 0; +#if !HEAP_REPLACEMENT + e->refcount = 0; +#endif + e->lastref = lastref; + e->timestamp = timestamp; + e->expires = expires; + e->lastmod = lastmod; + e->refcount = refcount; + e->flags = flags; + EBIT_SET(e->flags, ENTRY_CACHABLE); + EBIT_CLR(e->flags, RELEASE_REQUEST); + EBIT_CLR(e->flags, KEY_PRIVATE); + e->ping_status = PING_NONE; + EBIT_CLR(e->flags, ENTRY_VALIDATED); + storeDiskdDirMapBitSet(SD, e->swap_filen); + storeHashInsert(e, key); /* do it after we clear KEY_PRIVATE */ + storeDiskdDirReplAdd(SD, e); + return e; +} + +static void +storeDiskdDirRebuild(SwapDir * sd) +{ + RebuildState *rb = xcalloc(1, sizeof(*rb)); + int clean = 0; + int zero = 0; + FILE *fp; + EVH *func = NULL; + rb->sd = sd; + rb->speed = opt_foreground_rebuild ? 1 << 30 : 50; + /* + * If the swap.state file exists in the cache_dir, then + * we'll use storeDiskdDirRebuildFromSwapLog(), otherwise we'll + * use storeDiskdDirRebuildFromDirectory() to open up each file + * and suck in the meta data. + */ + fp = storeDiskdDirOpenTmpSwapLog(sd, &clean, &zero); + if (fp == NULL || zero) { + if (fp != NULL) + fclose(fp); + func = storeDiskdDirRebuildFromDirectory; + } else { + func = storeDiskdDirRebuildFromSwapLog; + rb->log = fp; + rb->flags.clean = (unsigned int) clean; + } + if (!clean) + rb->flags.need_to_validate = 1; + debug(20, 1) ("Rebuilding storage in %s (%s)\n", + sd->path, clean ? "CLEAN" : "DIRTY"); + store_dirs_rebuilding++; + cbdataAdd(rb, cbdataXfree, 0); + eventAdd("storeRebuild", func, rb, 0.0, 1); +} + +static void +storeDiskdDirCloseTmpSwapLog(SwapDir * sd) +{ + diskdinfo_t *diskdinfo = (diskdinfo_t *)sd->fsdata; + char *swaplog_path = xstrdup(storeDiskdDirSwapLogFile(sd, NULL)); + char *new_path = xstrdup(storeDiskdDirSwapLogFile(sd, ".new")); + int fd; + file_close(diskdinfo->swaplog_fd); +#ifdef _SQUID_OS2_ + if (unlink(swaplog_path) < 0) { + debug(50, 0) ("%s: %s\n", swaplog_path, xstrerror()); + fatal("storeDiskdDirCloseTmpSwapLog: unlink failed"); + } +#endif + if (xrename(new_path, swaplog_path) < 0) { + fatal("storeDiskdDirCloseTmpSwapLog: rename failed"); + } + fd = file_open(swaplog_path, O_WRONLY | O_CREAT); + if (fd < 0) { + debug(50, 1) ("%s: %s\n", swaplog_path, xstrerror()); + fatal("storeDiskdDirCloseTmpSwapLog: Failed to open swap log."); + } + safe_free(swaplog_path); + safe_free(new_path); + diskdinfo->swaplog_fd = fd; + debug(47, 3) ("Cache Dir #%d log opened on FD %d\n", sd->index, fd); +} + +static FILE * +storeDiskdDirOpenTmpSwapLog(SwapDir * sd, int *clean_flag, int *zero_flag) +{ + diskdinfo_t *diskdinfo = (diskdinfo_t *)sd->fsdata; + char *swaplog_path = xstrdup(storeDiskdDirSwapLogFile(sd, NULL)); + char *clean_path = xstrdup(storeDiskdDirSwapLogFile(sd, ".last-clean")); + char *new_path = xstrdup(storeDiskdDirSwapLogFile(sd, ".new")); + struct stat log_sb; + struct stat clean_sb; + FILE *fp; + int fd; + if (stat(swaplog_path, &log_sb) < 0) { + debug(47, 1) ("Cache Dir #%d: No log file\n", sd->index); + safe_free(swaplog_path); + safe_free(clean_path); + safe_free(new_path); + return NULL; + } + *zero_flag = log_sb.st_size == 0 ? 1 : 0; + /* close the existing write-only FD */ + if (diskdinfo->swaplog_fd >= 0) + file_close(diskdinfo->swaplog_fd); + /* open a write-only FD for the new log */ + fd = file_open(new_path, O_WRONLY | O_CREAT | O_TRUNC); + if (fd < 0) { + debug(50, 1) ("%s: %s\n", new_path, xstrerror()); + fatal("storeDirOpenTmpSwapLog: Failed to open swap log."); + } + diskdinfo->swaplog_fd = fd; + /* open a read-only stream of the old log */ + fp = fopen(swaplog_path, "r"); + if (fp == NULL) { + debug(50, 0) ("%s: %s\n", swaplog_path, xstrerror()); + fatal("Failed to open swap log for reading"); + } + memset(&clean_sb, '\0', sizeof(struct stat)); + if (stat(clean_path, &clean_sb) < 0) + *clean_flag = 0; + else if (clean_sb.st_mtime < log_sb.st_mtime) + *clean_flag = 0; + else + *clean_flag = 1; + safeunlink(clean_path, 1); + safe_free(swaplog_path); + safe_free(clean_path); + safe_free(new_path); + return fp; +} + +struct _clean_state { + char *cur; + char *new; + char *cln; + char *outbuf; + off_t outbuf_offset; + int fd; +}; + +#define CLEAN_BUF_SZ 16384 +/* + * Begin the process to write clean cache state. For DISKD this means + * opening some log files and allocating write buffers. Return 0 if + * we succeed, and assign the 'func' and 'data' return pointers. + */ +static int +storeDiskdDirWriteCleanOpen(SwapDir * sd) +{ + struct _clean_state *state = xcalloc(1, sizeof(*state)); + struct stat sb; + sd->log.clean.write = NULL; + sd->log.clean.state = NULL; + state->cur = xstrdup(storeDiskdDirSwapLogFile(sd, NULL)); + state->new = xstrdup(storeDiskdDirSwapLogFile(sd, ".clean")); + state->cln = xstrdup(storeDiskdDirSwapLogFile(sd, ".last-clean")); + state->outbuf = xcalloc(CLEAN_BUF_SZ, 1); + state->outbuf_offset = 0; + unlink(state->new); + unlink(state->cln); + state->fd = file_open(state->new, O_WRONLY | O_CREAT | O_TRUNC); + if (state->fd < 0) + return -1; + debug(20, 3) ("storeDirWriteCleanLogs: opened %s, FD %d\n", + state->new, state->fd); +#if HAVE_FCHMOD + if (stat(state->cur, &sb) == 0) + fchmod(state->fd, sb.st_mode); +#endif + sd->log.clean.write = storeDiskdDirWriteCleanEntry; + sd->log.clean.state = state; + return 0; +} + +/* + * "write" an entry to the clean log file. + */ +static void +storeDiskdDirWriteCleanEntry(const StoreEntry * e, SwapDir * sd) +{ + storeSwapLogData s; + static size_t ss = sizeof(storeSwapLogData); + struct _clean_state *state = sd->log.clean.state; + if (NULL == e) { + storeDiskdDirWriteCleanClose(sd); + return; + } + memset(&s, '\0', ss); + s.op = (char) SWAP_LOG_ADD; + s.swap_filen = e->swap_filen; + s.timestamp = e->timestamp; + s.lastref = e->lastref; + s.expires = e->expires; + s.lastmod = e->lastmod; + s.swap_file_sz = e->swap_file_sz; + s.refcount = e->refcount; + s.flags = e->flags; + xmemcpy(&s.key, e->key, MD5_DIGEST_CHARS); + xmemcpy(state->outbuf + state->outbuf_offset, &s, ss); + state->outbuf_offset += ss; + /* buffered write */ + if (state->outbuf_offset + ss > CLEAN_BUF_SZ) { + if (write(state->fd, state->outbuf, state->outbuf_offset) < 0) { + debug(50, 0) ("storeDirWriteCleanLogs: %s: write: %s\n", + state->new, xstrerror()); + debug(20, 0) ("storeDirWriteCleanLogs: Current swap logfile not replaced.\n"); + file_close(state->fd); + state->fd = -1; + unlink(state->new); + safe_free(state); + sd->log.clean.state = NULL; + sd->log.clean.write = NULL; + } + state->outbuf_offset = 0; + } +} + +static void +storeDiskdDirWriteCleanClose(SwapDir * sd) +{ + struct _clean_state *state = sd->log.clean.state; + if (state->fd < 0) + return; + if (write(state->fd, state->outbuf, state->outbuf_offset) < 0) { + debug(50, 0) ("storeDirWriteCleanLogs: %s: write: %s\n", + state->new, xstrerror()); + debug(20, 0) ("storeDirWriteCleanLogs: Current swap logfile " + "not replaced.\n"); + file_close(state->fd); + state->fd = -1; + unlink(state->new); + } + safe_free(state->outbuf); + /* + * You can't rename open files on Microsoft "operating systems" + * so we have to close before renaming. + */ + storeDiskdDirCloseSwapLog(sd); + /* rename */ + if (state->fd >= 0) { +#ifdef _SQUID_OS2_ + file_close(state->fd); + state->fd = -1; + if (unlink(cur) < 0) + debug(50, 0) ("storeDirWriteCleanLogs: unlinkd failed: %s, %s\n", + xstrerror(), cur); +#endif + xrename(state->new, state->cur); + } + /* touch a timestamp file if we're not still validating */ + if (store_dirs_rebuilding) + (void) 0; + else if (state->fd < 0) + (void) 0; + else + file_close(file_open(state->cln, O_WRONLY | O_CREAT | O_TRUNC)); + /* close */ + safe_free(state->cur); + safe_free(state->new); + safe_free(state->cln); + if (state->fd >= 0) + file_close(state->fd); + state->fd = -1; + safe_free(state); + sd->log.clean.state = NULL; + sd->log.clean.write = NULL; +} + +static void +storeDiskdDirSwapLog(const SwapDir * sd, const StoreEntry * e, int op) +{ + diskdinfo_t *diskdinfo = (diskdinfo_t *)sd->fsdata; + storeSwapLogData *s = xcalloc(1, sizeof(storeSwapLogData)); + s->op = (char) op; + s->swap_filen = e->swap_filen; + s->timestamp = e->timestamp; + s->lastref = e->lastref; + s->expires = e->expires; + s->lastmod = e->lastmod; + s->swap_file_sz = e->swap_file_sz; + s->refcount = e->refcount; + s->flags = e->flags; + xmemcpy(s->key, e->key, MD5_DIGEST_CHARS); + file_write(diskdinfo->swaplog_fd, + -1, + s, + sizeof(storeSwapLogData), + NULL, + NULL, + xfree); +} + +static void +storeDiskdDirNewfs(SwapDir * sd) +{ + debug(47, 3) ("Creating swap space in %s\n", sd->path); + storeDiskdDirCreateDirectory(sd->path, 0); + storeDiskdDirCreateSwapSubDirs(sd); +} + +static int +rev_int_sort(const void *A, const void *B) +{ + const int *i1 = A; + const int *i2 = B; + return *i2 - *i1; +} + +static int +storeDiskdDirClean(int swap_index) +{ + DIR *dp = NULL; + struct dirent *de = NULL; + LOCAL_ARRAY(char, p1, MAXPATHLEN + 1); + LOCAL_ARRAY(char, p2, MAXPATHLEN + 1); +#if USE_TRUNCATE + struct stat sb; +#endif + int files[20]; + int swapfileno; + int fn; /* same as swapfileno, but with dirn bits set */ + int n = 0; + int k = 0; + int N0, N1, N2; + int D0, D1, D2; + SwapDir *SD; + diskdinfo_t *diskdinfo; + N0 = n_diskd_dirs; + D0 = diskd_dir_index[swap_index % N0]; + SD = &Config.cacheSwap.swapDirs[D0]; + diskdinfo = (diskdinfo_t *)SD->fsdata; + N1 = diskdinfo->l1; + D1 = (swap_index / N0) % N1; + N2 = diskdinfo->l2; + D2 = ((swap_index / N0) / N1) % N2; + snprintf(p1, SQUID_MAXPATHLEN, "%s/%02X/%02X", + Config.cacheSwap.swapDirs[D0].path, D1, D2); + debug(36, 3) ("storeDirClean: Cleaning directory %s\n", p1); + dp = opendir(p1); + if (dp == NULL) { + if (errno == ENOENT) { + debug(36, 0) ("storeDirClean: WARNING: Creating %s\n", p1); + if (mkdir(p1, 0777) == 0) + return 0; + } + debug(50, 0) ("storeDirClean: %s: %s\n", p1, xstrerror()); + safeunlink(p1, 1); + return 0; + } + while ((de = readdir(dp)) != NULL && k < 20) { + if (sscanf(de->d_name, "%X", &swapfileno) != 1) + continue; + fn = swapfileno; /* XXX should remove this cruft ! */ + if (storeDiskdDirValidFileno(SD, fn)) + if (storeDiskdDirMapBitTest(SD, fn)) + if (storeDiskdFilenoBelongsHere(fn, D0, D1, D2)) + continue; +#if USE_TRUNCATE + if (!stat(de->d_name, &sb)) + if (sb.st_size == 0) + continue; +#endif + files[k++] = swapfileno; + } + closedir(dp); + if (k == 0) + return 0; + qsort(files, k, sizeof(int), rev_int_sort); + if (k > 10) + k = 10; + for (n = 0; n < k; n++) { + debug(36, 3) ("storeDirClean: Cleaning file %08X\n", files[n]); + snprintf(p2, MAXPATHLEN + 1, "%s/%08X", p1, files[n]); +#if USE_TRUNCATE + truncate(p2, 0); +#else + safeunlink(p2, 0); +#endif + Counter.swap_files_cleaned++; + } + debug(36, 3) ("Cleaned %d unused files from %s\n", k, p1); + return k; +} + +static void +storeDiskdDirCleanEvent(void *unused) +{ + static int swap_index = 0; + int i; + int j = 0; + int n = 0; + /* + * Assert that there are DISKD cache_dirs configured, otherwise + * we should never be called. + */ + assert(n_diskd_dirs); + if (NULL == diskd_dir_index) { + SwapDir *sd; + diskdinfo_t *diskdinfo; + /* + * Initialize the little array that translates DISKD cache_dir + * number into the Config.cacheSwap.swapDirs array index. + */ + diskd_dir_index = xcalloc(n_diskd_dirs, sizeof(*diskd_dir_index)); + for (i = 0, n = 0; i < Config.cacheSwap.n_configured; i++) { + sd = &Config.cacheSwap.swapDirs[i]; + if (!storeDiskdDirIs(sd)) + continue; + diskd_dir_index[n++] = i; + diskdinfo = (diskdinfo_t *)sd->fsdata; + j += (diskdinfo->l1 * diskdinfo->l2); + } + assert(n == n_diskd_dirs); + /* + * Start the storeDiskdDirClean() swap_index with a random + * value. j equals the total number of DISKD level 2 + * swap directories + */ + swap_index = (int) (squid_random() % j); + } + if (0 == store_dirs_rebuilding) { + n = storeDiskdDirClean(swap_index); + swap_index++; + } + eventAdd("storeDirClean", storeDiskdDirCleanEvent, NULL, + 15.0 * exp(-0.25 * n), 1); +} + +static int +storeDiskdDirIs(SwapDir * sd) +{ + if (strncmp(sd->type, "diskd", 3) == 0) + return 1; + return 0; +} + +/* + * Does swapfile number 'fn' belong in cachedir #F0, + * level1 dir #F1, level2 dir #F2? + */ +static int +storeDiskdFilenoBelongsHere(int fn, int F0, int F1, int F2) +{ + int D1, D2; + int L1, L2; + int filn = fn; + diskdinfo_t *diskdinfo; + assert(F0 < Config.cacheSwap.n_configured); + diskdinfo = (diskdinfo_t *)Config.cacheSwap.swapDirs[F0].fsdata; + L1 = diskdinfo->l1; + L2 = diskdinfo->l2; + D1 = ((filn / L2) / L2) % L1; + if (F1 != D1) + return 0; + D2 = (filn / L2) % L2; + if (F2 != D2) + return 0; + return 1; +} + +int +storeDiskdDirValidFileno(SwapDir *SD, sfileno filn) +{ + diskdinfo_t *diskdinfo = (diskdinfo_t *)SD->fsdata; + if (filn < 0) + return 0; + if (filn > diskdinfo->map->max_n_files) + return 0; + return 1; +} + +void +storeDiskdDirMaintain(SwapDir *SD) +{ + StoreEntry *e = NULL; + int scanned = 0; + int locked = 0; + int expired = 0; + int max_scan; + int max_remove; + double f; + static time_t last_warn_time = 0; +#if !HEAP_REPLACEMENT + dlink_node *m; + dlink_node *prev = NULL; +#else + heap_key age; + heap_key min_age = 0.0; + link_list *locked_entries = NULL; +#if HEAP_REPLACEMENT_DEBUG + if (!verify_heap_property(SD->repl.heap.heap)) { + debug(20, 1) ("Heap property violated!\n"); + } +#endif +#endif + /* We can't delete objects while rebuilding swap */ + if (store_dirs_rebuilding) { + return; + } else { + f = (double) (store_swap_size - store_swap_low) / (store_swap_high - store_swap_low); + f = f < 0.0 ? 0.0 : f > 1.0 ? 1.0 : f; + max_scan = (int) (f * 400.0 + 100.0); + max_remove = (int) (f * 70.0 + 10.0); + /* + * This is kinda cheap, but so we need this priority hack? + */ +#if 0 + eventAdd("MaintainSwapSpace", storeMaintainSwapSpace, NULL, 1.0 - f, 1); +#endif + } + debug(20, 3) ("storeMaintainSwapSpace: f=%f, max_scan=%d, max_remove=%d\n", f, max_scan, max_remove); +#if HEAP_REPLACEMENT + while (heap_nodes(SD->repl.heap.heap) > 0) { + if (store_swap_size < store_swap_low) + break; + if (expired >= max_remove) + break; + if (scanned >= max_scan) + break; + age = heap_peepminkey(SD->repl.heap.heap); + e = heap_extractmin(SD->repl.heap.heap); + e->repl.node = NULL; /* no longer in the heap */ + scanned++; + if (storeEntryLocked(e)) { + /* + * Entry is in use ... put it in a linked list to ignore it. + */ + if (!EBIT_TEST(e->flags, ENTRY_SPECIAL)) { + /* + * If this was a "SPECIAL" do not add it back into the heap. + * It will always be "SPECIAL" and therefore never removed. + */ + debug(20, 4) ("storeDiskdDirMaintain: locked url %s\n", + (e->mem_obj && e->mem_obj->url) ? e->mem_obj->url : storeKeyText(e-> +key)); + linklistPush(&locked_entries, e); + } + locked++; + continue; + } else if (storeDiskdDirCheckExpired(SD, e)) { + /* + * Note: This will not check the reference age ifdef + * HEAP_REPLACEMENT, but it does some other useful + * checks... + */ + expired++; + debug(20, 3) ("Released store object age %f size %d refs %d key %s\n", + age, e->swap_file_sz, e->refcount, storeKeyText(e->key)); + min_age = age; + storeRelease(e); + } else { + /* + * Did not expire the object so we need to add it back + * into the heap! + */ + debug(20, 5) ("storeMaintainSwapSpace: non-expired %s\n", + storeKeyText(e->key)); + linklistPush(&locked_entries, e); + continue; + } + if (store_swap_size < store_swap_low) + break; + else if (expired >= max_remove) + break; + else if (scanned >= max_scan) + break; + } + /* + * Bump the heap age factor. + */ + if (min_age > 0.0) + SD->repl.heap.heap->age = min_age; + /* + * Reinsert all bumped locked entries back into heap... + */ + while ((e = linklistShift(&locked_entries))) + e->repl.node = heap_insert(SD->repl.heap.heap, e); +#else + for (m = SD->repl.lru.list.tail; m; m = prev) { + prev = m->prev; + e = m->data; + scanned++; + if (storeEntryLocked(e)) { + /* + * If there is a locked entry at the tail of the LRU list, + * move it to the beginning to get it out of the way. + * Theoretically, we might have all locked objects at the + * tail, and then we'll never remove anything here and the + * LRU age will go to zero. + */ + if (memInUse(MEM_STOREENTRY) > max_scan) { + dlinkDelete(&e->repl.lru, &SD->repl.lru.list); + dlinkAdd(e, &e->repl.lru, &SD->repl.lru.list); + } + locked++; + + } else if (storeDiskdDirCheckExpired(SD, e)) { + expired++; + storeRelease(e); + } + if (expired >= max_remove) + break; + if (scanned >= max_scan) + break; + } +#endif + debug(20, (expired ? 2 : 3)) ("storeMaintainSwapSpace: scanned %d/%d removed %d/%d l +ocked %d f=%.03f\n", + scanned, max_scan, expired, max_remove, locked, f); + debug(20, 3) ("storeMaintainSwapSpace stats:\n"); + debug(20, 3) (" %6d objects\n", memInUse(MEM_STOREENTRY)); + debug(20, 3) (" %6d were scanned\n", scanned); + debug(20, 3) (" %6d were locked\n", locked); + debug(20, 3) (" %6d were expired\n", expired); + if (store_swap_size < Config.Swap.maxSize) + return; + if (squid_curtime - last_warn_time < 10) + return; + debug(20, 0) ("WARNING: Disk space over limit: %d KB > %d KB\n", + store_swap_size, Config.Swap.maxSize); + last_warn_time = squid_curtime; +} + +/* + * storeDiskdDirCheckObj + * + * This routine is called by storeDirSelectSwapDir to see if the given + * object is able to be stored on this filesystem. DISKD filesystems will + * happily store anything as long as the LRU time isn't too small. + */ +int +storeDiskdDirCheckObj(SwapDir *SD, const StoreEntry *e) +{ + int loadav; + + diskdinfo_t *diskdinfo = (diskdinfo_t *)SD->fsdata; +#if !HEAP_REPLACEMENT + if (storeDiskdDirExpiredReferenceAge(SD) < 300) { + debug(20, 3) ("storeDiskdDirCheckObj: NO: LRU Age = %d\n", + storeDiskdDirExpiredReferenceAge(SD)); + /* store_check_cachable_hist.no.lru_age_too_low++; */ + return -1; + } +#endif + + /* Check the queue length */ + if (diskdinfo->away >= diskdinfo->magic1) + return -1; + + /* Calculate the storedir load relative to magic2 on a scale of 0 .. 1000 */ + if (diskdinfo->away == 0) + loadav = 0; + else + loadav = diskdinfo->magic2 * 1000 / diskdinfo->away; + return loadav; +} + +/* + * storeDiskdDirRefObj + * + * This routine is called whenever an object is referenced, so we can + * maintain replacement information within the storage fs. + */ +void +storeDiskdDirRefObj(SwapDir *SD, StoreEntry *e) +{ + debug(1, 3) ("storeDiskdDirRefObj: referencing %p %d/%d\n", e, e->swap_dirn, + e->swap_filen); +#if HEAP_REPLACEMENT + /* Nothing to do here */ +#else + /* Reference the object */ + if (!EBIT_TEST(e->flags, RELEASE_REQUEST) && + !EBIT_TEST(e->flags, ENTRY_SPECIAL)) { + dlinkDelete(&e->repl.lru, &SD->repl.lru.list); + dlinkAdd(e, &e->repl.lru, &SD->repl.lru.list); + } +#endif +} + +/* + * storeDiskdDirUnrefObj + * This routine is called whenever the last reference to an object is + * removed, to maintain replacement information within the storage fs. + */ +void +storeDiskdDirUnrefObj(SwapDir *SD, StoreEntry *e) +{ + debug(1, 3) ("storeDiskdDirUnrefObj: referencing %p %d/%d\n", e, + e->swap_dirn, e->swap_filen); +#if HEAP_REPLACEMENT + if (e->repl.node) + heap_update(SD->repl.heap.heap, e->repl.node, e); +#endif +} + +/* + * storeDiskdDirUnlinkFile + * + * This is a *synchronous* unlink which is currently used in the rebuild + * process. This is bad, but it'll have to stay until the dir rebuild + * uses storeDiskdUnlink() .. + */ +void +storeDiskdDirUnlinkFile(SwapDir *SD, sfileno f) +{ + debug(79, 3) ("storeDiskdDirUnlinkFile: unlinking fileno %08X\n", f); + storeDiskdDirMapBitReset(SD, f); + unlinkdUnlink(storeDiskdDirFullPath(SD, f, NULL)); +} + +#if !HEAP_REPLACEMENT +/* + * storeDiskdDirExpiredReferenceAge + * + * The LRU age is scaled exponentially between 1 minute and + * Config.referenceAge , when store_swap_low < store_swap_size < + * store_swap_high. This keeps store_swap_size within the low and high + * water marks. If the cache is very busy then store_swap_size stays + * closer to the low water mark, if it is not busy, then it will stay + * near the high water mark. The LRU age value can be examined on the + * cachemgr 'info' page. + */ +static time_t +storeDiskdDirExpiredReferenceAge(SwapDir *SD) +{ + double x; + double z; + time_t age; + long store_high, store_low; + + store_high = (long) (((float) SD->max_size * + (float) Config.Swap.highWaterMark) / (float) 100); + store_low = (long) (((float) SD->max_size * + (float) Config.Swap.lowWaterMark) / (float) 100); + debug(20, 20) ("RA: Dir %s, hi=%d, lo=%d, cur=%d\n", SD->path, store_high, store_low, SD->cur_size); + + x = (double) (store_high - SD->cur_size) / + (store_high - store_low); + x = x < 0.0 ? 0.0 : x > 1.0 ? 1.0 : x; + z = pow((double) (Config.referenceAge / 60), x); + age = (time_t) (z * 60.0); + if (age < 60) + age = 60; + else if (age > Config.referenceAge) + age = Config.referenceAge; + return age; +} +#endif + +/* + * storeDiskdDirCheckExpired + * + * Check whether the given object is expired or not + * It breaks layering a little by calling the upper layers to find + * out whether the object is locked or not, but we can't help this + * right now. + */ +static int +storeDiskdDirCheckExpired(SwapDir *SD, StoreEntry *e) +{ + if (storeEntryLocked(e)) + return 0; + if (EBIT_TEST(e->flags, RELEASE_REQUEST)) + return 1; + if (EBIT_TEST(e->flags, ENTRY_NEGCACHED) && squid_curtime >= e->expires) + return 1; + +#if HEAP_REPLACEMENT + /* + * with HEAP_REPLACEMENT we are not using the LRU reference age, the heap + * controls the replacement of objects. + */ + return 1; +#else + if (squid_curtime - e->lastref > storeDiskdDirExpiredReferenceAge(SD)) + return 1; + return 0; +#endif +} + +/* + * Add and remove the given StoreEntry from the replacement policy in + * use. + */ + +void +storeDiskdDirReplAdd(SwapDir *SD, StoreEntry *e) +{ + debug(20, 4) ("storeDiskdDirReplAdd: added node %p to dir %d\n", e, + SD->index); +#if HEAP_REPLACEMENT + if (EBIT_TEST(e->flags, ENTRY_SPECIAL)) { + (void) 0; + } else { + e->repl.node = heap_insert(SD->repl.heap.heap, e); + debug(20, 4) ("storeDiskdDirReplAdd: inserted node 0x%x\n", e->repl.node); + } +#else + /* Shouldn't we not throw special objects into the lru ? */ + dlinkAdd(e, &e->repl.lru, &SD->repl.lru.list); +#endif +} + + +void +storeDiskdDirReplRemove(StoreEntry *e) +{ + SwapDir *SD = INDEXSD(e->swap_dirn); + debug(20, 4) ("storeDiskdDirReplRemove: remove node %p from dir %d\n", e, + SD->index); +#if HEAP_REPLACEMENT + /* And now, release the object from the replacement policy */ + if (e->repl.node) { + debug(20, 4) ("storeDiskdDirReplRemove: deleting node 0x%x\n", + e->repl.node); + heap_delete(SD->repl.heap.heap, e->repl.node); + e->repl.node = NULL; + } +#else + dlinkDelete(&e->repl.lru, &SD->repl.lru.list); +#endif +} + + + +/* + * SHM manipulation routines + */ + +void * +storeDiskdShmGet(SwapDir * sd, int *shm_offset) +{ + char *buf; + diskdinfo_t *diskdinfo = (diskdinfo_t *)sd->fsdata; + buf = linklistShift(&diskdinfo->shm.stack); + assert(buf); + *shm_offset = buf - diskdinfo->shm.buf; + assert(0 <= *shm_offset && *shm_offset < SHMBUFS * SHMBUF_BLKSZ); + diskd_stats.shmbuf_count++; + return buf; +} + +void +storeDiskdShmPut(SwapDir * sd, int offset) +{ + char *buf; + diskdinfo_t *diskdinfo = (diskdinfo_t *)sd->fsdata; + assert(offset >= 0); + assert(offset < SHMBUFS * SHMBUF_BLKSZ); + buf = diskdinfo->shm.buf + offset; + linklistPush(&diskdinfo->shm.stack, buf); + diskd_stats.shmbuf_count--; +} + + + + +/* ========== LOCAL FUNCTIONS ABOVE, GLOBAL FUNCTIONS BELOW ========== */ + +void +storeDiskdDirStats(SwapDir *SD, StoreEntry * sentry) +{ + diskdinfo_t *diskdinfo; +#if HAVE_STATVFS + struct statvfs sfs; +#endif + diskdinfo = (diskdinfo_t *)SD->fsdata; + storeAppendPrintf(sentry, "First level subdirectories: %d\n", diskdinfo->l1); + storeAppendPrintf(sentry, "Second level subdirectories: %d\n", diskdinfo->l2); + storeAppendPrintf(sentry, "Maximum Size: %d KB\n", SD->max_size); + storeAppendPrintf(sentry, "Current Size: %d KB\n", SD->cur_size); + storeAppendPrintf(sentry, "Percent Used: %0.2f%%\n", + 100.0 * SD->cur_size / SD->max_size); + storeAppendPrintf(sentry, "Filemap bits in use: %d of %d (%d%%)\n", + diskdinfo->map->n_files_in_map, diskdinfo->map->max_n_files, + percent(diskdinfo->map->n_files_in_map, diskdinfo->map->max_n_files)); +#if HAVE_STATVFS +#define fsbtoblk(num, fsbs, bs) \ + (((fsbs) != 0 && (fsbs) < (bs)) ? \ + (num) / ((bs) / (fsbs)) : (num) * ((fsbs) / (bs))) + if (!statvfs(SD->path, &sfs)) { + storeAppendPrintf(sentry, "Filesystem Space in use: %d/%d KB (%d%%)\n", + fsbtoblk((sfs.f_blocks - sfs.f_bfree), sfs.f_frsize, 1024), + fsbtoblk(sfs.f_blocks, sfs.f_frsize, 1024), + percent(sfs.f_blocks - sfs.f_bfree, sfs.f_blocks)); + storeAppendPrintf(sentry, "Filesystem Inodes in use: %d/%d (%d%%)\n", + sfs.f_files - sfs.f_ffree, sfs.f_files, + percent(sfs.f_files - sfs.f_ffree, sfs.f_files)); + } +#endif + storeAppendPrintf(sentry, "Flags:"); + if (SD->flags.selected) + storeAppendPrintf(sentry, " SELECTED"); + if (SD->flags.read_only) + storeAppendPrintf(sentry, " READ-ONLY"); + storeAppendPrintf(sentry, "\n"); +#if !HEAP_REPLACEMENT + storeAppendPrintf(sentry, "LRU Expiration Age: %6.2f days\n", + (double) storeDiskdDirExpiredReferenceAge(SD) / 86400.0); +#else +#if 0 + storeAppendPrintf(sentry, "Storage Replacement Threshold:\t%f\n", + heap_peepminkey(sd.repl.heap.heap)); +#endif +#endif + storeAppendPrintf(sentry, "Pending operations: %d\n", diskdinfo->away); +} + +/* + * storeDiskdDirReconfigure + * + * This routine is called when the given swapdir needs reconfiguring + */ +void +storeDiskdDirReconfigure(SwapDir *sd, int index, char *path) +{ + char *token; + int i; + int size; + int l1; + int l2; + int magic1, magic2; + unsigned int read_only = 0; + diskdinfo_t *diskdinfo; + + i = GetInteger(); + size = i << 10; /* Mbytes to kbytes */ + if (size <= 0) + fatal("storeDiskdDirReconfigure: invalid size value"); + i = GetInteger(); + l1 = i; + if (l1 <= 0) + fatal("storeDiskdDirReconfigure: invalid level 1 directories value"); + i = GetInteger(); + l2 = i; + if (l2 <= 0) + fatal("storeDiskdDirReconfigure: invalid level 2 directories value"); + i = GetInteger(); + magic1 = i; + if (magic1 <= 0) + fatal("storeDiskdDirParse: invalid magic1 value"); + i = GetInteger(); + magic2 = i; + if (magic2 <= 0) + fatal("storeDiskdDirParse: invalid magic2 value"); + if ((token = strtok(NULL, w_space))) + if (!strcasecmp(token, "read-only")) + read_only = 1; + + /* just reconfigure it */ + if (size == sd->max_size) + debug(3, 1) ("Cache dir '%s' size remains unchanged at %d KB\n", + path, size); + else + debug(3, 1) ("Cache dir '%s' size changed to %d KB\n", + path, size); + sd->max_size = size; + if (sd->flags.read_only != read_only) + debug(3, 1) ("Cache dir '%s' now %s\n", + path, read_only ? "Read-Only" : "Read-Write"); + diskdinfo = (diskdinfo_t *)sd->fsdata; + diskdinfo->magic1 = magic1; + diskdinfo->magic2 = magic2; + sd->flags.read_only = read_only; + return; +} + +void +storeDiskdDirDump(StoreEntry * entry, const char *name, SwapDir * s) +{ + diskdinfo_t *diskdinfo = (diskdinfo_t *)s->fsdata; + storeAppendPrintf(entry, "%s %s %s %d %d %d\n", + name, + "diskd", + s->path, + s->max_size >> 10, + diskdinfo->l1, + diskdinfo->l2); +} + +/* + * Only "free" the filesystem specific stuff here + */ +static void +storeDiskdDirFree(SwapDir * s) +{ + diskdinfo_t *diskdinfo = (diskdinfo_t *)s->fsdata; + if (diskdinfo->swaplog_fd > -1) { + file_close(diskdinfo->swaplog_fd); + diskdinfo->swaplog_fd = -1; + } + filemapFreeMemory(diskdinfo->map); + xfree(diskdinfo); + s->fsdata = NULL; /* Will aid debugging... */ + +} + +char * +storeDiskdDirFullPath(SwapDir *SD, sfileno filn, char *fullpath) +{ + LOCAL_ARRAY(char, fullfilename, SQUID_MAXPATHLEN); + diskdinfo_t *diskdinfo = (diskdinfo_t *)SD->fsdata; + int L1 = diskdinfo->l1; + int L2 = diskdinfo->l2; + if (!fullpath) + fullpath = fullfilename; + fullpath[0] = '\0'; + snprintf(fullpath, SQUID_MAXPATHLEN, "%s/%02X/%02X/%08X", + SD->path, + ((filn / L2) / L2) % L1, + (filn / L2) % L2, + filn); + return fullpath; +} + +/* + * storeDiskdCleanupDoubleCheck + * + * This is called by storeCleanup() if -S was given on the command line. + */ +static int +storeDiskdCleanupDoubleCheck(SwapDir *sd, StoreEntry *e) +{ + struct stat sb; + + if (stat(storeDiskdDirFullPath(sd, e->swap_filen, NULL), &sb) < 0) { + debug(20, 0) ("storeDiskdCleanupDoubleCheck: MISSING SWAP FILE\n"); + debug(20, 0) ("storeDiskdCleanupDoubleCheck: FILENO %08X\n", e->swap_filen); + debug(20, 0) ("storeDiskdCleanupDoubleCheck: PATH %s\n", + storeDiskdDirFullPath(sd, e->swap_filen, NULL)); + storeEntryDump(e, 0); + return -1; + } + if (e->swap_file_sz != sb.st_size) { + debug(20, 0) ("storeDiskdCleanupDoubleCheck: SIZE MISMATCH\n"); + debug(20, 0) ("storeDiskdCleanupDoubleCheck: FILENO %08X\n", e->swap_filen); + debug(20, 0) ("storeDiskdCleanupDoubleCheck: PATH %s\n", + storeDiskdDirFullPath(sd, e->swap_filen, NULL)); + debug(20, 0) ("storeDiskdCleanupDoubleCheck: ENTRY SIZE: %d, FILE SIZE: %d\n", + e->swap_file_sz, (int) sb.st_size); + storeEntryDump(e, 0); + return -1; + } + return 0; +} + +/* + * storeDiskdDirParse + * + * Called when a *new* fs is being setup. + */ +void +storeDiskdDirParse(SwapDir *sd, int index, char *path) +{ + char *token; + int i; + int size; + int l1; + int l2; + int magic1, magic2; + unsigned int read_only = 0; + diskdinfo_t *diskdinfo; + + i = GetInteger(); + size = i << 10; /* Mbytes to kbytes */ + if (size <= 0) + fatal("storeDiskdDirParse: invalid size value"); + i = GetInteger(); + l1 = i; + if (l1 <= 0) + fatal("storeDiskdDirParse: invalid level 1 directories value"); + i = GetInteger(); + l2 = i; + if (l2 <= 0) + fatal("storeDiskdDirParse: invalid level 2 directories value"); + i = GetInteger(); + magic1 = i; + if (magic1 <= 0) + fatal("storeDiskdDirParse: invalid magic1 value"); + i = GetInteger(); + magic2 = i; + if (magic2 <= 0) + fatal("storeDiskdDirParse: invalid magic2 value"); + + + if ((token = strtok(NULL, w_space))) + if (!strcasecmp(token, "read-only")) + read_only = 1; + + diskdinfo = xmalloc(sizeof(diskdinfo_t)); + if (diskdinfo == NULL) + fatal("storeDiskdDirParse: couldn't xmalloc() diskdinfo_t!\n"); + + sd->index = index; + sd->path = xstrdup(path); + sd->max_size = size; + sd->fsdata = diskdinfo; + diskdinfo->l1 = l1; + diskdinfo->l2 = l2; + diskdinfo->swaplog_fd = -1; + diskdinfo->map = NULL; /* Debugging purposes */ + diskdinfo->suggest = 0; + diskdinfo->magic1 = magic1; + diskdinfo->magic2 = magic2; + sd->flags.read_only = read_only; + sd->init = storeDiskdDirInit; + sd->newfs = storeDiskdDirNewfs; + sd->dump = storeDiskdDirDump; + sd->freefs = storeDiskdDirFree; + sd->dblcheck = storeDiskdCleanupDoubleCheck; + sd->statfs = storeDiskdDirStats; + sd->maintainfs = storeDiskdDirMaintain; + sd->checkobj = storeDiskdDirCheckObj; + sd->refobj = storeDiskdDirRefObj; + sd->unrefobj = storeDiskdDirUnrefObj; + sd->callback = storeDiskdDirCallback; + sd->sync = storeDiskdDirSync; + sd->obj.create = storeDiskdCreate; + sd->obj.open = storeDiskdOpen; + sd->obj.close = storeDiskdClose; + sd->obj.read = storeDiskdRead; + sd->obj.write = storeDiskdWrite; + sd->obj.unlink = storeDiskdUnlink; + sd->log.open = storeDiskdDirOpenSwapLog; + sd->log.close = storeDiskdDirCloseSwapLog; + sd->log.write = storeDiskdDirSwapLog; + sd->log.clean.open = storeDiskdDirWriteCleanOpen; + + /* Initialise replacement policy stuff */ +#if HEAP_REPLACEMENT + /* + * Create new heaps with cache replacement policies attached to them. + * The cache replacement policy is specified as either GDSF or LFUDA in + * the squid.conf configuration file. Note that the replacement policy + * applies only to the disk replacement algorithm. Memory replacement + * always uses GDSF since we want to maximize object hit rate. + */ + if (Config.replPolicy) { + if (tolower(Config.replPolicy[0]) == 'g') { + debug(20, 1) ("Using GDSF disk replacement policy\n"); + sd->repl.heap.heap = new_heap(10000, HeapKeyGen_StoreEntry_GDSF); + } else if (tolower(Config.replPolicy[0]) == 'l') { + if (tolower(Config.replPolicy[1]) == 'f') { + debug(20, 1) ("Using LFUDA disk replacement policy\n"); + sd->repl.heap.heap = new_heap(10000, HeapKeyGen_StoreEntry_LFUDA); + } else if (tolower(Config.replPolicy[1]) == 'r') { + debug(20, 1) ("Using LRU heap disk replacement policy\n"); + sd->repl.heap.heap = new_heap(10000, HeapKeyGen_StoreEntry_LRU); + } + } else { + debug(20, 1) ("Unrecognized replacement_policy; using GDSF\n"); + sd->repl.heap.heap = new_heap(10000, HeapKeyGen_StoreEntry_GDSF); + } + } else { + debug(20, 1) ("Using default disk replacement policy (GDSF)\n"); + sd->repl.heap.heap = new_heap(10000, HeapKeyGen_StoreEntry_GDSF); + } +#else + sd->repl.lru.list.head = NULL; + sd->repl.lru.list.tail = NULL; +#endif +} + +/* + * Initial setup / end destruction + */ +void +storeDiskdDirDone(void) +{ + memPoolDestroy(diskd_state_pool); + diskd_initialised = 0; +} + +void +storeFsSetup_diskd(storefs_entry_t *storefs) +{ + assert(!diskd_initialised); + storefs->parsefunc = storeDiskdDirParse; + storefs->reconfigurefunc = storeDiskdDirReconfigure; + storefs->donefunc = storeDiskdDirDone; + diskd_state_pool = memPoolCreate("DISKD IO State data", sizeof(diskdstate_t)); + memset(&diskd_stats, '\0', sizeof(diskd_stats)); + cachemgrRegister("diskd", "DISKD Stats", storeDiskdStats, 0, 1); + debug(81, 1) ("diskd started\n"); + diskd_initialised = 1; +} + diff --git a/src/fs/diskd/store_diskd.h b/src/fs/diskd/store_diskd.h new file mode 100644 index 0000000000..f397fddb1c --- /dev/null +++ b/src/fs/diskd/store_diskd.h @@ -0,0 +1,126 @@ +/* + * store_diskd.h + * + * Internal declarations for the diskd routines + */ + +#ifndef __STORE_DISKD_H__ +#define __STORE_DISKD_H__ + +/* + * MAGIC2 is the point at which we start blocking on msgsnd/msgrcv. + * If a queue has MAGIC2 (or more) messages away, then we read the + * queue until the level falls below MAGIC2. Recommended value + * is 75% of SHMBUFS. MAGIC1 is the number of messages away which we + * stop allowing open/create for. + */ + +struct _diskdinfo_t { + int swaplog_fd; + int l1; + int l2; + fileMap *map; + int suggest; + int smsgid; + int rmsgid; + int wfd; + int away; + struct { + char *buf; + link_list *stack; + int id; + } shm; + int magic1; + int magic2; +}; + +struct _diskdstate_t { + int id; + struct { + unsigned int close_request:1; + unsigned int reading:1; + unsigned int writing:1; + } flags; + char *read_buf; +}; + +enum { + _MQD_NOP, + _MQD_OPEN, + _MQD_CLOSE, + _MQD_READ, + _MQD_WRITE, + _MQD_UNLINK +}; + +typedef struct _diomsg { + mtyp_t mtype; + int id; + int seq_no; + void *callback_data; + int size; + int offset; + int status; + int shm_offset; +} diomsg; + +struct _diskd_stats { + int open_fail_queue_len; + int block_queue_len; + int max_away; + int max_shmuse; + int shmbuf_count; + int sent_count; + int recv_count; + int sio_id; +}; + +typedef struct _diskd_stats diskd_stats_t; +typedef struct _diskdinfo_t diskdinfo_t; +typedef struct _diskdstate_t diskdstate_t; + +static const int msg_snd_rcv_sz = sizeof(diomsg) - sizeof(mtyp_t); + +/* The diskd_state memory pool */ +extern MemPool * diskd_state_pool; + +extern void storeDiskdDirMapBitReset(SwapDir *, sfileno); +extern int storeDiskdDirMapBitAllocate(SwapDir *); +extern char * storeDiskdDirFullPath(SwapDir *SD, sfileno filn, char *fullpath); +extern void storeDiskdDirUnlinkFile(SwapDir *, sfileno); +extern void storeDiskdDirReplAdd(SwapDir *, StoreEntry *); +extern void storeDiskdDirReplRemove(StoreEntry *); +extern void storeDiskdShmPut(SwapDir *, int); +extern void *storeDiskdShmGet(SwapDir *, int *); +extern void storeDiskdHandle(diomsg *M); + + +/* + * Store IO stuff + */ +extern STOBJCREATE storeDiskdCreate; +extern STOBJOPEN storeDiskdOpen; +extern STOBJCLOSE storeDiskdClose; +extern STOBJREAD storeDiskdRead; +extern STOBJWRITE storeDiskdWrite; +extern STOBJUNLINK storeDiskdUnlink; + +/* + * SHMBUFS is the number of shared memory buffers to allocate for + * Each SwapDir. + */ +#define SHMBUFS 96 +#define SHMBUF_BLKSZ SM_PAGE_SIZE +/* + * MAGIC2 is the point at which we start blocking on msgsnd/msgrcv. + * If a queue has MAGIC2 (or more) messages away, then we read the + * queue until the level falls below MAGIC2. Recommended value + * is 75% of SHMBUFS. + */ +#define MAGIC1 Config.diskd.magic1 +#define MAGIC2 Config.diskd.magic2 + + +extern diskd_stats_t diskd_stats; + +#endif diff --git a/src/fs/diskd/store_io_diskd.cc b/src/fs/diskd/store_io_diskd.cc new file mode 100644 index 0000000000..19362a56ac --- /dev/null +++ b/src/fs/diskd/store_io_diskd.cc @@ -0,0 +1,464 @@ + +#include "config.h" +#include "squid.h" +#include "store_diskd.h" + +#include +#include +#include + +#undef assert +#include + + +/* + * DEBUG: section 81 Diskd Interface functions + */ + + +static int storeDiskdSend(int, SwapDir *, int, storeIOState *, int, int, int); +static void storeDiskdIOCallback(storeIOState * sio, int errflag); +static void storeDiskdIOFreeEntry(void *sio, int foo); + +/* + * SHMBUFS is the number of shared memory buffers to allocate for + * Each SwapDir. + */ +#define SHMBUFS 96 +#define SHMBUF_BLKSZ SM_PAGE_SIZE + + +/* === PUBLIC =========================================================== */ + +storeIOState * +storeDiskdOpen(SwapDir *SD, StoreEntry *e, STFNCB *file_callback, + STIOCB *callback, void *callback_data) +{ + sfileno f = e->swap_filen; + int x; + storeIOState *sio; + char *buf; + int shm_offset; + diskdinfo_t *diskdinfo = (diskdinfo_t *) SD->fsdata; + debug(81, 3) ("storeDiskdOpen: fileno %08X\n", f); + /* + * XXX Eventually there should be an option here to fail on open() + * If there are too many requests queued. + */ + if (diskdinfo->away > diskdinfo->magic1) { + debug(81, 3) ("storeDiskdOpen: FAILING, too many requests away\n"); + diskd_stats.open_fail_queue_len++; + return NULL; + } + sio = memAllocate(MEM_STORE_IO); + cbdataAdd(sio, storeDiskdIOFreeEntry, MEM_STORE_IO); + sio->fsstate = memPoolAlloc(diskd_state_pool); + + sio->swap_filen = f; + sio->swap_dirn = SD->index; + sio->mode = O_RDONLY; + sio->callback = callback; + sio->callback_data = callback_data; + sio->e = e; + cbdataLock(callback_data); + + ((diskdstate_t *)(sio->fsstate))->flags.writing = 0; + ((diskdstate_t *)(sio->fsstate))->flags.reading = 0; + ((diskdstate_t *)(sio->fsstate))->flags.close_request = 0; + ((diskdstate_t *)(sio->fsstate))->id = diskd_stats.sio_id++; + + buf = storeDiskdShmGet(SD, &shm_offset); + /* XXX WRONG!!! :) */ + strcpy(buf, storeDiskdDirFullPath(SD, f, NULL)); + x = storeDiskdSend(_MQD_OPEN, + SD, + ((diskdstate_t *)(sio->fsstate))->id, + sio, + strlen(buf) + 1, + O_RDONLY, + shm_offset); + if (x < 0) { + debug(50, 1) ("storeDiskdSend OPEN: %s\n", xstrerror()); + storeDiskdShmPut(SD, shm_offset); + cbdataUnlock(sio->callback_data); + cbdataFree(sio); + return NULL; + } + return sio; +} + +storeIOState * +storeDiskdCreate(SwapDir *SD, StoreEntry *e, STFNCB *file_callback, + STIOCB *callback, void *callback_data) +{ + sfileno f; + int x; + storeIOState *sio; + char *buf; + int shm_offset; + diskdinfo_t *diskdinfo = (diskdinfo_t *) SD->fsdata; + /* + * XXX Eventually there should be an option here to fail on open() + * If there are too many requests queued. + */ + if (diskdinfo->away > diskdinfo->magic1) { + diskd_stats.open_fail_queue_len++; + return NULL; + } + + /* Allocate a number */ + f = storeDiskdDirMapBitAllocate(SD); + debug(81, 3) ("storeDiskdCreate: fileno %08X\n", f); + + sio = memAllocate(MEM_STORE_IO); + cbdataAdd(sio, storeDiskdIOFreeEntry, MEM_STORE_IO); + sio->fsstate = memPoolAlloc(diskd_state_pool); + + sio->swap_filen = f; + sio->swap_dirn = SD->index; + sio->mode = O_WRONLY | O_CREAT | O_TRUNC; + sio->callback = callback; + sio->callback_data = callback_data; + sio->e = e; + cbdataLock(callback_data); + + ((diskdstate_t *)(sio->fsstate))->flags.writing = 0; + ((diskdstate_t *)(sio->fsstate))->flags.reading = 0; + ((diskdstate_t *)(sio->fsstate))->flags.close_request = 0; + ((diskdstate_t *)(sio->fsstate))->id = diskd_stats.sio_id++; + + buf = storeDiskdShmGet(SD, &shm_offset); + /* XXX WRONG!!! :) */ + strcpy(buf, storeDiskdDirFullPath(SD, f, NULL)); + x = storeDiskdSend(_MQD_OPEN, + SD, + ((diskdstate_t *)(sio->fsstate))->id, + sio, + strlen(buf) + 1, + sio->mode, + shm_offset); + if (x < 0) { + debug(50, 1) ("storeDiskdSend OPEN: %s\n", xstrerror()); + storeDiskdShmPut(SD, shm_offset); + cbdataUnlock(sio->callback_data); + cbdataFree(sio); + return NULL; + } + storeDiskdDirReplAdd(SD, e); + return sio; +} + + +void +storeDiskdClose(SwapDir *SD, storeIOState * sio) +{ + int x; + diskdstate_t *diskdstate = (diskdstate_t *)sio->fsstate; + debug(81, 3) ("storeDiskdClose: dirno %d, fileno %08X\n", SD->index, + sio->swap_filen); + x = storeDiskdSend(_MQD_CLOSE, + SD, + diskdstate->id, + sio, + 0, + 0, + -1); + if (x < 0) { + debug(50, 1) ("storeDiskdSend CLOSE: %s\n", xstrerror()); + storeDiskdIOCallback(sio, DISK_ERROR); + } +} + +void +storeDiskdRead(SwapDir *SD, storeIOState * sio, char *buf, size_t size, off_t offset, STRCB * callback, void *callback_data) +{ + int x; + int shm_offset; + char *rbuf; + diskdstate_t *diskdstate = (diskdstate_t *)sio->fsstate; + if (!cbdataValid(sio)) + return; + if (diskdstate->flags.reading) { + debug(81, 1) ("storeDiskdRead: already reading!\n"); + return; + } + assert(sio->read.callback == NULL); + assert(sio->read.callback_data == NULL); + sio->read.callback = callback; + sio->read.callback_data = callback_data; + diskdstate->read_buf = buf; /* the one passed from above */ + cbdataLock(sio->read.callback_data); + debug(81, 3) ("storeDiskdRead: dirno %d, fileno %08X\n", sio->swap_dirn, sio->swap_filen); + sio->offset = offset; + diskdstate->flags.reading = 1; + rbuf = storeDiskdShmGet(SD, &shm_offset); + assert(rbuf); + x = storeDiskdSend(_MQD_READ, + SD, + diskdstate->id, + sio, + (int) size, + (int) offset, + shm_offset); + if (x < 0) { + debug(50, 1) ("storeDiskdSend READ: %s\n", xstrerror()); + storeDiskdShmPut(SD, shm_offset); + storeDiskdIOCallback(sio, DISK_ERROR); + } +} + +void +storeDiskdWrite(SwapDir *SD, storeIOState * sio, char *buf, size_t size, off_t offset, FREE * free_func) +{ + int x; + char *sbuf; + int shm_offset; + diskdstate_t *diskdstate = (diskdstate_t *)sio->fsstate; + debug(81, 3) ("storeDiskdWrite: dirno %d, fileno %08X\n", SD->index, sio->swap_filen); + if (!cbdataValid(sio)) { + free_func(buf); + return; + } + diskdstate->flags.writing = 1; + sbuf = storeDiskdShmGet(SD, &shm_offset); + xmemcpy(sbuf, buf, size); + if (free_func) + free_func(buf); + x = storeDiskdSend(_MQD_WRITE, + SD, + diskdstate->id, + sio, + (int) size, + (int) offset, + shm_offset); + if (x < 0) { + debug(50, 1) ("storeDiskdSend WRITE: %s\n", xstrerror()); + storeDiskdShmPut(SD, shm_offset); + storeDiskdIOCallback(sio, DISK_ERROR); + } +} + +void +storeDiskdUnlink(SwapDir *SD, StoreEntry *e) +{ + int x; + int shm_offset; + char *buf; + diskdinfo_t *diskdinfo = (diskdinfo_t *)SD->fsdata; + + debug(81, 3) ("storeDiskdUnlink: dirno %d, fileno %08X\n", SD->index, + e->swap_filen); + storeDiskdDirReplRemove(e); + storeDiskdDirMapBitReset(SD, e->swap_filen); + if (diskdinfo->away >= diskdinfo->magic1) { + /* Damn, we need to issue a sync unlink here :( */ + debug(50, 2) ("storeDiskUnlink: Out of queue space, sync unlink\n"); + storeDiskdDirUnlinkFile(SD, e->swap_filen); + return; + } + + /* We can attempt a diskd unlink */ + buf = storeDiskdShmGet(SD, &shm_offset); + strcpy(buf, storeDiskdDirFullPath(SD, e->swap_filen, NULL)); + x = storeDiskdSend(_MQD_UNLINK, + SD, + e->swap_filen, + NULL, + 0, + 0, + shm_offset); + if (x < 0) { + debug(50, 1) ("storeDiskdSend UNLINK: %s\n", xstrerror()); + unlink(buf); /* XXX EWW! */ + storeDiskdShmPut(SD, shm_offset); + } +} + + +/* === STATIC =========================================================== */ + +static void +storeDiskdOpenDone(diomsg * M) +{ + storeIOState *sio = M->callback_data; + Counter.syscalls.disk.opens++; + debug(81, 3) ("storeDiskdOpenDone: dirno %d, fileno %08x status %d\n", + sio->swap_dirn, sio->swap_filen, M->status); + if (M->status < 0) { + storeDiskdIOCallback(sio, DISK_ERROR); + } +} + +static void +storeDiskdCloseDone(diomsg * M) +{ + storeIOState *sio = M->callback_data; + Counter.syscalls.disk.closes++; + debug(81, 3) ("storeDiskdCloseDone: dirno %d, fileno %08x status %d\n", + sio->swap_dirn, sio->swap_filen, M->status); + if (M->status < 0) { + storeDiskdIOCallback(sio, DISK_ERROR); + return; + } + storeDiskdIOCallback(sio, DISK_OK); +} + +static void +storeDiskdReadDone(diomsg * M) +{ + storeIOState *sio = M->callback_data; + STRCB *callback = sio->read.callback; + SwapDir *sd = INDEXSD(sio->swap_dirn); + diskdstate_t *diskdstate = sio->fsstate; + diskdinfo_t *diskdinfo = sd->fsdata; + void *their_data = sio->read.callback_data; + char *their_buf = diskdstate->read_buf; + char *sbuf; + size_t len; + int valid; + Counter.syscalls.disk.reads++; + diskdstate->flags.reading = 0; + valid = cbdataValid(sio->read.callback_data); + cbdataUnlock(sio->read.callback_data); + debug(81, 3) ("storeDiskdReadDone: dirno %d, fileno %08x status %d\n", + sio->swap_dirn, sio->swap_filen, M->status); + if (M->status < 0) { + storeDiskdIOCallback(sio, DISK_ERROR); + return; + } + sbuf = diskdinfo->shm.buf + M->shm_offset; + len = M->status; + xmemcpy(their_buf, sbuf, len); /* yucky copy */ + sio->offset += len; + assert(callback); + assert(their_data); + sio->read.callback = NULL; + sio->read.callback_data = NULL; + if (valid) + callback(their_data, their_buf, len); +} + +static void +storeDiskdWriteDone(diomsg * M) +{ + storeIOState *sio = M->callback_data; + diskdstate_t *diskdstate = (diskdstate_t *)sio->fsstate; + Counter.syscalls.disk.writes++; + diskdstate->flags.writing = 0; + debug(81, 3) ("storeDiskdWriteDone: dirno %d, fileno %08x status %d\n", + sio->swap_dirn, sio->swap_filen, M->status); + if (M->status < 0) { + storeDiskdIOCallback(sio, DISK_ERROR); + return; + } + sio->offset += M->status; +} + +static void +storeDiskdUnlinkDone(diomsg * M) +{ + debug(81, 3) ("storeDiskdUnlinkDone: fileno %08x status %d\n", + M->id, M->status); + Counter.syscalls.disk.unlinks++; +} + +void +storeDiskdHandle(diomsg * M) +{ + int valid = M->callback_data ? cbdataValid(M->callback_data) : 1; + if (M->callback_data) + cbdataUnlock(M->callback_data); + if (!valid) { + debug(81, 3) ("storeDiskdHandle: Invalid callback_data %p\n", + M->callback_data); + /* + * The read operation has its own callback. If we don't + * call storeDiskdReadDone(), then we must make sure the + * callback_data gets unlocked! + */ + if (_MQD_READ == M->mtype) { + storeIOState *sio = M->callback_data; + cbdataUnlock(sio->read.callback_data); + } + return; + } + switch (M->mtype) { + case _MQD_OPEN: + storeDiskdOpenDone(M); + break; + case _MQD_CLOSE: + storeDiskdCloseDone(M); + break; + case _MQD_READ: + storeDiskdReadDone(M); + break; + case _MQD_WRITE: + storeDiskdWriteDone(M); + break; + case _MQD_UNLINK: + storeDiskdUnlinkDone(M); + break; + default: + assert(0); + break; + } +} + +static void +storeDiskdIOCallback(storeIOState * sio, int errflag) +{ + int valid = cbdataValid(sio->callback_data); + debug(81, 3) ("storeUfsIOCallback: errflag=%d\n", errflag); + cbdataUnlock(sio->callback_data); + if (valid) + sio->callback(sio->callback_data, errflag, sio); + cbdataFree(sio); +} + +static int +storeDiskdSend(int mtype, SwapDir * sd, int id, storeIOState * sio, int size, int offset, int shm_offset) +{ + int x; + diomsg M; + static int send_errors = 0; + static int last_seq_no = 0; + static int seq_no = 0; + diskdinfo_t *diskdinfo = (diskdinfo_t *)sd->fsdata; + M.mtype = mtype; + M.callback_data = sio; + M.size = size; + M.offset = offset; + M.status = -1; + M.shm_offset = shm_offset; + M.id = id; + M.seq_no = ++seq_no; + if (M.callback_data) + cbdataLock(M.callback_data); + if (M.seq_no < last_seq_no) + debug(81, 1) ("WARNING: sequencing out of order\n"); + x = msgsnd(diskdinfo->smsgid, &M, msg_snd_rcv_sz, IPC_NOWAIT); + last_seq_no = M.seq_no; + if (0 == x) { + diskd_stats.sent_count++; + diskdinfo->away++; + } else { + debug(50, 1) ("storeDiskdSend: msgsnd: %s\n", xstrerror()); + if (M.callback_data) + cbdataUnlock(M.callback_data); + assert(++send_errors < 100); + } + return x; +} + + +/* + * We can't pass memFree() as a free function here, because we need to free + * the fsstate variable .. + */ +static void +storeDiskdIOFreeEntry(void *sio, int foo) +{ + memPoolFree(diskd_state_pool, ((storeIOState *)sio)->fsstate); + memFree(sio, MEM_STORE_IO); +} + diff --git a/src/fs/ufs/Makefile.in b/src/fs/ufs/Makefile.in new file mode 100644 index 0000000000..c9b90c638c --- /dev/null +++ b/src/fs/ufs/Makefile.in @@ -0,0 +1,56 @@ +# +# Makefile for the UFS storage driver for the Squid Object Cache server +# +# $Id: Makefile.in,v 1.1 2000/05/03 17:15:47 adrian Exp $ +# + +FS = ufs + +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ + +CC = @CC@ +MAKEDEPEND = @MAKEDEPEND@ +AR_R = @AR_R@ +RANLIB = @RANLIB@ +AC_CFLAGS = @CFLAGS@ +SHELL = /bin/sh + +INCLUDE = -I../../../include -I$(top_srcdir)/include -I$(top_srcdir)/src/ +CFLAGS = $(AC_CFLAGS) $(INCLUDE) $(DEFINES) + +OUT = ../$(FS).a + +OBJS = \ + store_dir_ufs.o \ + store_io_ufs.o + + +all: $(OUT) + +$(OUT): $(OBJS) + @rm -f ../stamp + $(AR_R) $(OUT) $(OBJS) + $(RANLIB) $(OUT) + +$(OBJS): $(top_srcdir)/include/version.h ../../../include/autoconf.h + +.c.o: + @rm -f ../stamp + $(CC) $(CFLAGS) -c $< + +clean: + -rm -rf *.o *pure_* core ../$(FS).a + +distclean: clean + -rm -f Makefile + -rm -f Makefile.bak + -rm -f tags + +install: + +tags: + ctags *.[ch] $(top_srcdir)/src/*.[ch] $(top_srcdir)/include/*.h $(top_srcdir)/lib/*.[ch] + +depend: + $(MAKEDEPEND) $(INCLUDE) -fMakefile *.c diff --git a/src/fs/ufs/store_dir_ufs.cc b/src/fs/ufs/store_dir_ufs.cc new file mode 100644 index 0000000000..c6493bd375 --- /dev/null +++ b/src/fs/ufs/store_dir_ufs.cc @@ -0,0 +1,1903 @@ + +/* + * $Id: store_dir_ufs.cc,v 1.1 2000/05/03 17:15:47 adrian Exp $ + * + * DEBUG: section 47 Store Directory Routines + * AUTHOR: Duane Wessels + * + * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from the + * Internet community. Development is led by Duane Wessels of the + * National Laboratory for Applied Network Research and funded by the + * National Science Foundation. Squid is Copyrighted (C) 1998 by + * Duane Wessels and the University of California San Diego. Please + * see the COPYRIGHT file for full details. Squid incorporates + * software developed and/or copyrighted by other sources. Please see + * the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#include "squid.h" +#if HAVE_STATVFS +#if HAVE_SYS_STATVFS_H +#include +#endif +#endif + +#include "store_ufs.h" + +#define DefaultLevelOneDirs 16 +#define DefaultLevelTwoDirs 256 +#define STORE_META_BUFSZ 4096 + +typedef struct _RebuildState RebuildState; +struct _RebuildState { + SwapDir *sd; + int n_read; + FILE *log; + int speed; + int curlvl1; + int curlvl2; + struct { + unsigned int need_to_validate:1; + unsigned int clean:1; + unsigned int init:1; + } flags; + int done; + int in_dir; + int fn; + struct dirent *entry; + DIR *td; + char fullpath[SQUID_MAXPATHLEN]; + char fullfilename[SQUID_MAXPATHLEN]; + struct _store_rebuild_data counts; +}; + +static int n_ufs_dirs = 0; +static int *ufs_dir_index = NULL; +MemPool * ufs_state_pool = NULL; +static int ufs_initialised = 0; + +static char *storeUfsDirSwapSubDir(SwapDir *, int subdirn); +static int storeUfsDirCreateDirectory(const char *path, int); +static int storeUfsDirVerifyCacheDirs(SwapDir *); +static int storeUfsDirVerifyDirectory(const char *path); +static void storeUfsDirCreateSwapSubDirs(SwapDir *); +static char *storeUfsDirSwapLogFile(SwapDir *, const char *); +static EVH storeUfsDirRebuildFromDirectory; +static EVH storeUfsDirRebuildFromSwapLog; +static int storeUfsDirGetNextFile(RebuildState *, int *sfileno, int *size); +static StoreEntry *storeUfsDirAddDiskRestore(SwapDir *SD, const cache_key * key, + int file_number, + size_t swap_file_sz, + time_t expires, + time_t timestamp, + time_t lastref, + time_t lastmod, + u_num32 refcount, + u_short flags, + int clean); +static void storeUfsDirRebuild(SwapDir * sd); +static void storeUfsDirCloseTmpSwapLog(SwapDir * sd); +static FILE *storeUfsDirOpenTmpSwapLog(SwapDir *, int *, int *); +static STLOGOPEN storeUfsDirOpenSwapLog; +static STINIT storeUfsDirInit; +static STFREE storeUfsDirFree; +static STLOGCLEANOPEN storeUfsDirWriteCleanOpen; +static void storeUfsDirWriteCleanClose(SwapDir * sd); +static STLOGCLEANWRITE storeUfsDirWriteCleanEntry; +static STLOGCLOSE storeUfsDirCloseSwapLog; +static STLOGWRITE storeUfsDirSwapLog; +static STNEWFS storeUfsDirNewfs; +static STDUMP storeUfsDirDump; +static STMAINTAINFS storeUfsDirMaintain; +static STCHECKOBJ storeUfsDirCheckObj; +static STREFOBJ storeUfsDirRefObj; +static STUNREFOBJ storeUfsDirUnrefObj; +static QS rev_int_sort; +static int storeUfsDirClean(int swap_index); +static EVH storeUfsDirCleanEvent; +static int storeUfsDirIs(SwapDir * sd); +static int storeUfsFilenoBelongsHere(int fn, int F0, int F1, int F2); +static int storeUfsCleanupDoubleCheck(SwapDir *, StoreEntry *); +static void storeUfsDirStats(SwapDir *, StoreEntry *); +static void storeUfsDirInitBitmap(SwapDir *); +static int storeUfsDirValidFileno(SwapDir *, sfileno); +static int storeUfsDirCheckExpired(SwapDir *, StoreEntry *); +#if !HEAP_REPLACEMENT +static time_t storeUfsDirExpiredReferenceAge(SwapDir *); +#endif + +/* + * These functions were ripped straight out of the heart of store_dir.c. + * They assume that the given filenum is on a ufs partiton, which may or + * may not be true.. + * XXX this evilness should be tidied up at a later date! + */ + +int +storeUfsDirMapBitTest(SwapDir *SD, int fn) +{ + sfileno filn = fn; + ufsinfo_t *ufsinfo; + ufsinfo = (ufsinfo_t *)SD->fsdata; + return file_map_bit_test(ufsinfo->map, filn); +} + +void +storeUfsDirMapBitSet(SwapDir *SD, int fn) +{ + sfileno filn = fn; + ufsinfo_t *ufsinfo; + ufsinfo = (ufsinfo_t *)SD->fsdata; + file_map_bit_set(ufsinfo->map, filn); +} + +void +storeUfsDirMapBitReset(SwapDir *SD, int fn) +{ + sfileno filn = fn; + ufsinfo_t *ufsinfo; + ufsinfo = (ufsinfo_t *)SD->fsdata; + file_map_bit_reset(ufsinfo->map, filn); +} + +int +storeUfsDirMapBitAllocate(SwapDir *SD) +{ + ufsinfo_t *ufsinfo = (ufsinfo_t *)SD->fsdata; + int fn; + fn = file_map_allocate(ufsinfo->map, ufsinfo->suggest); + file_map_bit_set(ufsinfo->map, fn); + ufsinfo->suggest = fn + 1; + return fn; +} + +/* + * Initialise the ufs bitmap + * + * If there already is a bitmap, and the numobjects is larger than currently + * configured, we allocate a new bitmap and 'grow' the old one into it. + */ +static void +storeUfsDirInitBitmap(SwapDir *sd) +{ + ufsinfo_t *ufsinfo = (ufsinfo_t *)sd->fsdata; + + if (ufsinfo->map == NULL) { + /* First time */ + ufsinfo->map = file_map_create(); + } else if (ufsinfo->map->max_n_files) { + /* it grew, need to expand */ + /* XXX We don't need it anymore .. */ + } + /* else it shrunk, and we leave the old one in place */ +} + +static char * +storeUfsDirSwapSubDir(SwapDir * sd, int subdirn) +{ + ufsinfo_t *ufsinfo = (ufsinfo_t *)sd->fsdata; + + LOCAL_ARRAY(char, fullfilename, SQUID_MAXPATHLEN); + assert(0 <= subdirn && subdirn < ufsinfo->l1); + snprintf(fullfilename, SQUID_MAXPATHLEN, "%s/%02X", sd->path, subdirn); + return fullfilename; +} + +static int +storeUfsDirCreateDirectory(const char *path, int should_exist) +{ + int created = 0; + struct stat st; + getCurrentTime(); + if (0 == stat(path, &st)) { + if (S_ISDIR(st.st_mode)) { + debug(20, should_exist ? 3 : 1) ("%s exists\n", path); + } else { + fatalf("Swap directory %s is not a directory.", path); + } + } else if (0 == mkdir(path, 0755)) { + debug(20, should_exist ? 1 : 3) ("%s created\n", path); + created = 1; + } else { + fatalf("Failed to make swap directory %s: %s", + path, xstrerror()); + } + return created; +} + +static int +storeUfsDirVerifyDirectory(const char *path) +{ + struct stat sb; + if (stat(path, &sb) < 0) { + debug(20, 0) ("%s: %s\n", path, xstrerror()); + return -1; + } + if (S_ISDIR(sb.st_mode) == 0) { + debug(20, 0) ("%s is not a directory\n", path); + return -1; + } + return 0; +} + +/* + * This function is called by storeUfsDirInit(). If this returns < 0, + * then Squid exits, complains about swap directories not + * existing, and instructs the admin to run 'squid -z' + */ +static int +storeUfsDirVerifyCacheDirs(SwapDir * sd) +{ + ufsinfo_t *ufsinfo = (ufsinfo_t *)sd->fsdata; + int j; + const char *path = sd->path; + + if (storeUfsDirVerifyDirectory(path) < 0) + return -1; + for (j = 0; j < ufsinfo->l1; j++) { + path = storeUfsDirSwapSubDir(sd, j); + if (storeUfsDirVerifyDirectory(path) < 0) + return -1; + } + return 0; +} + +static void +storeUfsDirCreateSwapSubDirs(SwapDir * sd) +{ + ufsinfo_t *ufsinfo = (ufsinfo_t *)sd->fsdata; + int i, k; + int should_exist; + LOCAL_ARRAY(char, name, MAXPATHLEN); + for (i = 0; i < ufsinfo->l1; i++) { + snprintf(name, MAXPATHLEN, "%s/%02X", sd->path, i); + if (storeUfsDirCreateDirectory(name, 0)) + should_exist = 0; + else + should_exist = 1; + debug(47, 1) ("Making directories in %s\n", name); + for (k = 0; k < ufsinfo->l2; k++) { + snprintf(name, MAXPATHLEN, "%s/%02X/%02X", sd->path, i, k); + storeUfsDirCreateDirectory(name, should_exist); + } + } +} + +static char * +storeUfsDirSwapLogFile(SwapDir * sd, const char *ext) +{ + LOCAL_ARRAY(char, path, SQUID_MAXPATHLEN); + LOCAL_ARRAY(char, pathtmp, SQUID_MAXPATHLEN); + LOCAL_ARRAY(char, digit, 32); + char *pathtmp2; + if (Config.Log.swap) { + xstrncpy(pathtmp, sd->path, SQUID_MAXPATHLEN - 64); + while (index(pathtmp,'/')) + *index(pathtmp,'/')='.'; + while (strlen(pathtmp) && pathtmp[strlen(pathtmp)-1]=='.') + pathtmp[strlen(pathtmp)-1]= '\0'; + for(pathtmp2 = pathtmp; *pathtmp2 == '.'; pathtmp2++); + snprintf(path, SQUID_MAXPATHLEN-64, Config.Log.swap, pathtmp2); + if (strncmp(path, Config.Log.swap, SQUID_MAXPATHLEN - 64) == 0) { + strcat(path, "."); + snprintf(digit, 32, "%02d", sd->index); + strncat(path, digit, 3); + } + } else { + xstrncpy(path, sd->path, SQUID_MAXPATHLEN - 64); + strcat(path, "/swap.state"); + } + if (ext) + strncat(path, ext, 16); + return path; +} + +static void +storeUfsDirOpenSwapLog(SwapDir * sd) +{ + ufsinfo_t *ufsinfo = (ufsinfo_t *)sd->fsdata; + char *path; + int fd; + path = storeUfsDirSwapLogFile(sd, NULL); + fd = file_open(path, O_WRONLY | O_CREAT); + if (fd < 0) { + debug(50, 1) ("%s: %s\n", path, xstrerror()); + fatal("storeUfsDirOpenSwapLog: Failed to open swap log."); + } + debug(47, 3) ("Cache Dir #%d log opened on FD %d\n", sd->index, fd); + ufsinfo->swaplog_fd = fd; + if (0 == n_ufs_dirs) + assert(NULL == ufs_dir_index); + n_ufs_dirs++; + assert(n_ufs_dirs <= Config.cacheSwap.n_configured); +} + +static void +storeUfsDirCloseSwapLog(SwapDir * sd) +{ + ufsinfo_t *ufsinfo = (ufsinfo_t *)sd->fsdata; + if (ufsinfo->swaplog_fd < 0) /* not open */ + return; + file_close(ufsinfo->swaplog_fd); + debug(47, 3) ("Cache Dir #%d log closed on FD %d\n", + sd->index, ufsinfo->swaplog_fd); + ufsinfo->swaplog_fd = -1; + n_ufs_dirs--; + assert(n_ufs_dirs >= 0); + if (0 == n_ufs_dirs) + safe_free(ufs_dir_index); +} + +static void +storeUfsDirInit(SwapDir * sd) +{ + static int started_clean_event = 0; + static const char *errmsg = + "\tFailed to verify one of the swap directories, Check cache.log\n" + "\tfor details. Run 'squid -z' to create swap directories\n" + "\tif needed, or if running Squid for the first time."; + storeUfsDirInitBitmap(sd); + if (storeUfsDirVerifyCacheDirs(sd) < 0) + fatal(errmsg); + storeUfsDirOpenSwapLog(sd); + storeUfsDirRebuild(sd); + if (!started_clean_event) { + eventAdd("storeDirClean", storeUfsDirCleanEvent, NULL, 15.0, 1); + started_clean_event = 1; + } +} + +static void +storeUfsDirRebuildFromDirectory(void *data) +{ + RebuildState *rb = data; + SwapDir *SD = rb->sd; + LOCAL_ARRAY(char, hdr_buf, SM_PAGE_SIZE); + StoreEntry *e = NULL; + StoreEntry tmpe; + cache_key key[MD5_DIGEST_CHARS]; + int sfileno = 0; + int count; + int size; + struct stat sb; + int swap_hdr_len; + int fd = -1; + tlv *tlv_list; + tlv *t; + assert(rb != NULL); + debug(20, 3) ("storeUfsDirRebuildFromDirectory: DIR #%d\n", rb->sd->index); + for (count = 0; count < rb->speed; count++) { + assert(fd == -1); + fd = storeUfsDirGetNextFile(rb, &sfileno, &size); + if (fd == -2) { + debug(20, 1) ("Done scanning %s swaplog (%d entries)\n", + rb->sd->path, rb->n_read); + store_dirs_rebuilding--; + storeUfsDirCloseTmpSwapLog(rb->sd); + storeRebuildComplete(&rb->counts); + cbdataFree(rb); + return; + } else if (fd < 0) { + continue; + } + assert(fd > -1); + /* lets get file stats here */ + if (fstat(fd, &sb) < 0) { + debug(20, 1) ("storeUfsDirRebuildFromDirectory: fstat(FD %d): %s\n", + fd, xstrerror()); + file_close(fd); + store_open_disk_fd--; + fd = -1; + continue; + } + if ((++rb->counts.scancount & 0xFFFF) == 0) + debug(20, 3) (" %s %7d files opened so far.\n", + rb->sd->path, rb->counts.scancount); + debug(20, 9) ("file_in: fd=%d %08X\n", fd, sfileno); + Counter.syscalls.disk.reads++; + if (read(fd, hdr_buf, SM_PAGE_SIZE) < 0) { + debug(20, 1) ("storeUfsDirRebuildFromDirectory: read(FD %d): %s\n", + fd, xstrerror()); + file_close(fd); + store_open_disk_fd--; + fd = -1; + continue; + } + file_close(fd); + store_open_disk_fd--; + fd = -1; + swap_hdr_len = 0; +#if USE_TRUNCATE + if (sb.st_size == 0) + continue; +#endif + tlv_list = storeSwapMetaUnpack(hdr_buf, &swap_hdr_len); + if (tlv_list == NULL) { + debug(20, 1) ("storeUfsDirRebuildFromDirectory: failed to get meta data\n"); + /* XXX shouldn't this be a call to storeUfsUnlink ? */ + storeUfsDirUnlinkFile(SD, sfileno); + continue; + } + debug(20, 3) ("storeUfsDirRebuildFromDirectory: successful swap meta unpacking\n"); + memset(key, '\0', MD5_DIGEST_CHARS); + memset(&tmpe, '\0', sizeof(StoreEntry)); + for (t = tlv_list; t; t = t->next) { + switch (t->type) { + case STORE_META_KEY: + assert(t->length == MD5_DIGEST_CHARS); + xmemcpy(key, t->value, MD5_DIGEST_CHARS); + break; + case STORE_META_STD: + assert(t->length == STORE_HDR_METASIZE); + xmemcpy(&tmpe.timestamp, t->value, STORE_HDR_METASIZE); + break; + default: + break; + } + } + storeSwapTLVFree(tlv_list); + tlv_list = NULL; + if (storeKeyNull(key)) { + debug(20, 1) ("storeUfsDirRebuildFromDirectory: NULL key\n"); + storeUfsDirUnlinkFile(SD, sfileno); + continue; + } + tmpe.key = key; + /* check sizes */ + if (tmpe.swap_file_sz == 0) { + tmpe.swap_file_sz = sb.st_size; + } else if (tmpe.swap_file_sz == sb.st_size - swap_hdr_len) { + tmpe.swap_file_sz = sb.st_size; + } else if (tmpe.swap_file_sz != sb.st_size) { + debug(20, 1) ("storeUfsDirRebuildFromDirectory: SIZE MISMATCH %d!=%d\n", + tmpe.swap_file_sz, (int) sb.st_size); + storeUfsDirUnlinkFile(SD, sfileno); + continue; + } + if (EBIT_TEST(tmpe.flags, KEY_PRIVATE)) { + storeUfsDirUnlinkFile(SD, sfileno); + rb->counts.badflags++; + continue; + } + e = storeGet(key); + if (e && e->lastref >= tmpe.lastref) { + /* key already exists, current entry is newer */ + /* keep old, ignore new */ + rb->counts.dupcount++; + continue; + } else if (NULL != e) { + /* URL already exists, this swapfile not being used */ + /* junk old, load new */ + storeRelease(e); /* release old entry */ + rb->counts.dupcount++; + } + rb->counts.objcount++; + storeEntryDump(&tmpe, 5); + e = storeUfsDirAddDiskRestore(SD, key, + sfileno, + tmpe.swap_file_sz, + tmpe.expires, + tmpe.timestamp, + tmpe.lastref, + tmpe.lastmod, + tmpe.refcount, /* refcount */ + tmpe.flags, /* flags */ + (int) rb->flags.clean); + storeDirSwapLog(e, SWAP_LOG_ADD); + } + eventAdd("storeRebuild", storeUfsDirRebuildFromDirectory, rb, 0.0, 1); +} + +static void +storeUfsDirRebuildFromSwapLog(void *data) +{ + RebuildState *rb = data; + SwapDir *SD = rb->sd; + StoreEntry *e = NULL; + storeSwapLogData s; + size_t ss = sizeof(storeSwapLogData); + int count; + int used; /* is swapfile already in use? */ + int disk_entry_newer; /* is the log entry newer than current entry? */ + double x; + assert(rb != NULL); + /* load a number of objects per invocation */ + for (count = 0; count < rb->speed; count++) { + if (fread(&s, ss, 1, rb->log) != 1) { + debug(20, 1) ("Done reading %s swaplog (%d entries)\n", + rb->sd->path, rb->n_read); + fclose(rb->log); + rb->log = NULL; + store_dirs_rebuilding--; + storeUfsDirCloseTmpSwapLog(rb->sd); + storeRebuildComplete(&rb->counts); + cbdataFree(rb); + return; + } + rb->n_read++; + if (s.op <= SWAP_LOG_NOP) + continue; + if (s.op >= SWAP_LOG_MAX) + continue; + debug(20, 3) ("storeUfsDirRebuildFromSwapLog: %s %s %08X\n", + swap_log_op_str[(int) s.op], + storeKeyText(s.key), + s.swap_filen); + if (s.op == SWAP_LOG_ADD) { + (void) 0; + } else if (s.op == SWAP_LOG_DEL) { + if ((e = storeGet(s.key)) != NULL) { + /* + * Make sure we don't unlink the file, it might be + * in use by a subsequent entry. Also note that + * we don't have to subtract from store_swap_size + * because adding to store_swap_size happens in + * the cleanup procedure. + */ + storeExpireNow(e); + storeReleaseRequest(e); + storeUfsDirReplRemove(e); + if (e->swap_filen > -1) { + storeUfsDirMapBitReset(SD, e->swap_filen); + e->swap_filen = -1; + e->swap_dirn = -1; + } + storeRelease(e); + rb->counts.objcount--; + rb->counts.cancelcount++; + } + continue; + } else { + x = log(++rb->counts.bad_log_op) / log(10.0); + if (0.0 == x - (double) (int) x) + debug(20, 1) ("WARNING: %d invalid swap log entries found\n", + rb->counts.bad_log_op); + rb->counts.invalid++; + continue; + } + if ((++rb->counts.scancount & 0xFFFF) == 0) + debug(20, 3) (" %7d %s Entries read so far.\n", + rb->counts.scancount, rb->sd->path); + if (!storeUfsDirValidFileno(SD, s.swap_filen)) { + rb->counts.invalid++; + continue; + } + if (EBIT_TEST(s.flags, KEY_PRIVATE)) { + rb->counts.badflags++; + continue; + } + e = storeGet(s.key); + used = storeUfsDirMapBitTest(SD, s.swap_filen); + /* If this URL already exists in the cache, does the swap log + * appear to have a newer entry? Compare 'lastref' from the + * swap log to e->lastref. */ + disk_entry_newer = e ? (s.lastref > e->lastref ? 1 : 0) : 0; + if (used && !disk_entry_newer) { + /* log entry is old, ignore it */ + rb->counts.clashcount++; + continue; + } else if (used && e && e->swap_filen == s.swap_filen && e->swap_dirn == SD->index) { + /* swapfile taken, same URL, newer, update meta */ + if (e->store_status == STORE_OK) { + e->lastref = s.timestamp; + e->timestamp = s.timestamp; + e->expires = s.expires; + e->lastmod = s.lastmod; + e->flags = s.flags; + e->refcount += s.refcount; +#if HEAP_REPLACEMENT + storeHeapPositionUpdate(e, SD); + storeUfsDirUnrefObj(SD, e); +#endif + } else { + debug_trap("storeUfsDirRebuildFromSwapLog: bad condition"); + debug(20, 1) ("\tSee %s:%d\n", __FILE__, __LINE__); + } + continue; + } else if (used) { + /* swapfile in use, not by this URL, log entry is newer */ + /* This is sorta bad: the log entry should NOT be newer at this + * point. If the log is dirty, the filesize check should have + * caught this. If the log is clean, there should never be a + * newer entry. */ + debug(20, 1) ("WARNING: newer swaplog entry for dirno %d, fileno %08X\n", + SD->index, s.swap_filen); + /* I'm tempted to remove the swapfile here just to be safe, + * but there is a bad race condition in the NOVM version if + * the swapfile has recently been opened for writing, but + * not yet opened for reading. Because we can't map + * swapfiles back to StoreEntrys, we don't know the state + * of the entry using that file. */ + /* We'll assume the existing entry is valid, probably because + * were in a slow rebuild and the the swap file number got taken + * and the validation procedure hasn't run. */ + assert(rb->flags.need_to_validate); + rb->counts.clashcount++; + continue; + } else if (e && !disk_entry_newer) { + /* key already exists, current entry is newer */ + /* keep old, ignore new */ + rb->counts.dupcount++; + continue; + } else if (e) { + /* key already exists, this swapfile not being used */ + /* junk old, load new */ + storeExpireNow(e); + storeReleaseRequest(e); + storeUfsDirReplRemove(e); + if (e->swap_filen > -1) { + /* Make sure we don't actually unlink the file */ + storeUfsDirMapBitReset(SD, e->swap_filen); + e->swap_filen = -1; + e->swap_dirn = -1; + } + storeRelease(e); + rb->counts.dupcount++; + } else { + /* URL doesnt exist, swapfile not in use */ + /* load new */ + (void) 0; + } + /* update store_swap_size */ + rb->counts.objcount++; + e = storeUfsDirAddDiskRestore(SD, s.key, + s.swap_filen, + s.swap_file_sz, + s.expires, + s.timestamp, + s.lastref, + s.lastmod, + s.refcount, + s.flags, + (int) rb->flags.clean); + storeDirSwapLog(e, SWAP_LOG_ADD); + } + eventAdd("storeRebuild", storeUfsDirRebuildFromSwapLog, rb, 0.0, 1); +} + +static int +storeUfsDirGetNextFile(RebuildState * rb, int *sfileno, int *size) +{ + SwapDir *SD = rb->sd; + ufsinfo_t *ufsinfo = (ufsinfo_t *)SD->fsdata; + int fd = -1; + int used = 0; + int dirs_opened = 0; + debug(20, 3) ("storeUfsDirGetNextFile: flag=%d, %d: /%02X/%02X\n", + rb->flags.init, + rb->sd->index, + rb->curlvl1, + rb->curlvl2); + if (rb->done) + return -2; + while (fd < 0 && rb->done == 0) { + fd = -1; + if (0 == rb->flags.init) { /* initialize, open first file */ + rb->done = 0; + rb->curlvl1 = 0; + rb->curlvl2 = 0; + rb->in_dir = 0; + rb->flags.init = 1; + assert(Config.cacheSwap.n_configured > 0); + } + if (0 == rb->in_dir) { /* we need to read in a new directory */ + snprintf(rb->fullpath, SQUID_MAXPATHLEN, "%s/%02X/%02X", + rb->sd->path, + rb->curlvl1, rb->curlvl2); + if (rb->flags.init && rb->td != NULL) + closedir(rb->td); + rb->td = NULL; + if (dirs_opened) + return -1; + rb->td = opendir(rb->fullpath); + dirs_opened++; + if (rb->td == NULL) { + debug(50, 1) ("storeUfsDirGetNextFile: opendir: %s: %s\n", + rb->fullpath, xstrerror()); + } else { + rb->entry = readdir(rb->td); /* skip . and .. */ + rb->entry = readdir(rb->td); + if (rb->entry == NULL && errno == ENOENT) + debug(20, 1) ("storeUfsDirGetNextFile: directory does not exist!.\n"); + debug(20, 3) ("storeUfsDirGetNextFile: Directory %s\n", rb->fullpath); + } + } + if (rb->td != NULL && (rb->entry = readdir(rb->td)) != NULL) { + rb->in_dir++; + if (sscanf(rb->entry->d_name, "%x", &rb->fn) != 1) { + debug(20, 3) ("storeUfsDirGetNextFile: invalid %s\n", + rb->entry->d_name); + continue; + } + if (!storeUfsFilenoBelongsHere(rb->fn, rb->sd->index, rb->curlvl1, rb->curlvl2)) { + debug(20, 3) ("storeUfsDirGetNextFile: %08X does not belong in %d/%d/%d\n", + rb->fn, rb->sd->index, rb->curlvl1, rb->curlvl2); + continue; + } + used = storeUfsDirMapBitTest(SD, rb->fn); + if (used) { + debug(20, 3) ("storeUfsDirGetNextFile: Locked, continuing with next.\n"); + continue; + } + snprintf(rb->fullfilename, SQUID_MAXPATHLEN, "%s/%s", + rb->fullpath, rb->entry->d_name); + debug(20, 3) ("storeUfsDirGetNextFile: Opening %s\n", rb->fullfilename); + fd = file_open(rb->fullfilename, O_RDONLY); + if (fd < 0) + debug(50, 1) ("storeUfsDirGetNextFile: %s: %s\n", rb->fullfilename, xstrerror()); + else + store_open_disk_fd++; + continue; + } + rb->in_dir = 0; + if (++rb->curlvl2 < ufsinfo->l2) + continue; + rb->curlvl2 = 0; + if (++rb->curlvl1 < ufsinfo->l1) + continue; + rb->curlvl1 = 0; + rb->done = 1; + } + *sfileno = rb->fn; + return fd; +} + +/* Add a new object to the cache with empty memory copy and pointer to disk + * use to rebuild store from disk. */ +static StoreEntry * +storeUfsDirAddDiskRestore(SwapDir *SD, const cache_key * key, + int file_number, + size_t swap_file_sz, + time_t expires, + time_t timestamp, + time_t lastref, + time_t lastmod, + u_num32 refcount, + u_short flags, + int clean) +{ + StoreEntry *e = NULL; + debug(20, 5) ("storeUfsAddDiskRestore: %s, fileno=%08X\n", storeKeyText(key), file_number); + /* if you call this you'd better be sure file_number is not + * already in use! */ + e = new_StoreEntry(STORE_ENTRY_WITHOUT_MEMOBJ, NULL, NULL); + e->store_status = STORE_OK; + storeSetMemStatus(e, NOT_IN_MEMORY); + e->swap_status = SWAPOUT_DONE; + e->swap_filen = file_number; + e->swap_dirn = SD->index; + e->swap_file_sz = swap_file_sz; + e->lock_count = 0; +#if !HEAP_REPLACEMENT + e->refcount = 0; +#endif + e->lastref = lastref; + e->timestamp = timestamp; + e->expires = expires; + e->lastmod = lastmod; + e->refcount = refcount; + e->flags = flags; + EBIT_SET(e->flags, ENTRY_CACHABLE); + EBIT_CLR(e->flags, RELEASE_REQUEST); + EBIT_CLR(e->flags, KEY_PRIVATE); + e->ping_status = PING_NONE; + EBIT_CLR(e->flags, ENTRY_VALIDATED); + storeUfsDirMapBitSet(SD, e->swap_filen); + storeHashInsert(e, key); /* do it after we clear KEY_PRIVATE */ + storeUfsDirReplAdd(SD, e); + return e; +} + +static void +storeUfsDirRebuild(SwapDir * sd) +{ + RebuildState *rb = xcalloc(1, sizeof(*rb)); + int clean = 0; + int zero = 0; + FILE *fp; + EVH *func = NULL; + rb->sd = sd; + rb->speed = opt_foreground_rebuild ? 1 << 30 : 50; + /* + * If the swap.state file exists in the cache_dir, then + * we'll use storeUfsDirRebuildFromSwapLog(), otherwise we'll + * use storeUfsDirRebuildFromDirectory() to open up each file + * and suck in the meta data. + */ + fp = storeUfsDirOpenTmpSwapLog(sd, &clean, &zero); + if (fp == NULL || zero) { + if (fp != NULL) + fclose(fp); + func = storeUfsDirRebuildFromDirectory; + } else { + func = storeUfsDirRebuildFromSwapLog; + rb->log = fp; + rb->flags.clean = (unsigned int) clean; + } + if (!clean) + rb->flags.need_to_validate = 1; + debug(20, 1) ("Rebuilding storage in %s (%s)\n", + sd->path, clean ? "CLEAN" : "DIRTY"); + store_dirs_rebuilding++; + cbdataAdd(rb, cbdataXfree, 0); + eventAdd("storeRebuild", func, rb, 0.0, 1); +} + +static void +storeUfsDirCloseTmpSwapLog(SwapDir * sd) +{ + ufsinfo_t *ufsinfo = (ufsinfo_t *)sd->fsdata; + char *swaplog_path = xstrdup(storeUfsDirSwapLogFile(sd, NULL)); + char *new_path = xstrdup(storeUfsDirSwapLogFile(sd, ".new")); + int fd; + file_close(ufsinfo->swaplog_fd); +#ifdef _SQUID_OS2_ + if (unlink(swaplog_path) < 0) { + debug(50, 0) ("%s: %s\n", swaplog_path, xstrerror()); + fatal("storeUfsDirCloseTmpSwapLog: unlink failed"); + } +#endif + if (xrename(new_path, swaplog_path) < 0) { + fatal("storeUfsDirCloseTmpSwapLog: rename failed"); + } + fd = file_open(swaplog_path, O_WRONLY | O_CREAT); + if (fd < 0) { + debug(50, 1) ("%s: %s\n", swaplog_path, xstrerror()); + fatal("storeUfsDirCloseTmpSwapLog: Failed to open swap log."); + } + safe_free(swaplog_path); + safe_free(new_path); + ufsinfo->swaplog_fd = fd; + debug(47, 3) ("Cache Dir #%d log opened on FD %d\n", sd->index, fd); +} + +static FILE * +storeUfsDirOpenTmpSwapLog(SwapDir * sd, int *clean_flag, int *zero_flag) +{ + ufsinfo_t *ufsinfo = (ufsinfo_t *)sd->fsdata; + char *swaplog_path = xstrdup(storeUfsDirSwapLogFile(sd, NULL)); + char *clean_path = xstrdup(storeUfsDirSwapLogFile(sd, ".last-clean")); + char *new_path = xstrdup(storeUfsDirSwapLogFile(sd, ".new")); + struct stat log_sb; + struct stat clean_sb; + FILE *fp; + int fd; + if (stat(swaplog_path, &log_sb) < 0) { + debug(47, 1) ("Cache Dir #%d: No log file\n", sd->index); + safe_free(swaplog_path); + safe_free(clean_path); + safe_free(new_path); + return NULL; + } + *zero_flag = log_sb.st_size == 0 ? 1 : 0; + /* close the existing write-only FD */ + if (ufsinfo->swaplog_fd >= 0) + file_close(ufsinfo->swaplog_fd); + /* open a write-only FD for the new log */ + fd = file_open(new_path, O_WRONLY | O_CREAT | O_TRUNC); + if (fd < 0) { + debug(50, 1) ("%s: %s\n", new_path, xstrerror()); + fatal("storeDirOpenTmpSwapLog: Failed to open swap log."); + } + ufsinfo->swaplog_fd = fd; + /* open a read-only stream of the old log */ + fp = fopen(swaplog_path, "r"); + if (fp == NULL) { + debug(50, 0) ("%s: %s\n", swaplog_path, xstrerror()); + fatal("Failed to open swap log for reading"); + } + memset(&clean_sb, '\0', sizeof(struct stat)); + if (stat(clean_path, &clean_sb) < 0) + *clean_flag = 0; + else if (clean_sb.st_mtime < log_sb.st_mtime) + *clean_flag = 0; + else + *clean_flag = 1; + safeunlink(clean_path, 1); + safe_free(swaplog_path); + safe_free(clean_path); + safe_free(new_path); + return fp; +} + +struct _clean_state { + char *cur; + char *new; + char *cln; + char *outbuf; + off_t outbuf_offset; + int fd; +}; + +#define CLEAN_BUF_SZ 16384 +/* + * Begin the process to write clean cache state. For UFS this means + * opening some log files and allocating write buffers. Return 0 if + * we succeed, and assign the 'func' and 'data' return pointers. + */ +static int +storeUfsDirWriteCleanOpen(SwapDir * sd) +{ + struct _clean_state *state = xcalloc(1, sizeof(*state)); + struct stat sb; + sd->log.clean.write = NULL; + sd->log.clean.state = NULL; + state->cur = xstrdup(storeUfsDirSwapLogFile(sd, NULL)); + state->new = xstrdup(storeUfsDirSwapLogFile(sd, ".clean")); + state->cln = xstrdup(storeUfsDirSwapLogFile(sd, ".last-clean")); + state->outbuf = xcalloc(CLEAN_BUF_SZ, 1); + state->outbuf_offset = 0; + unlink(state->new); + unlink(state->cln); + state->fd = file_open(state->new, O_WRONLY | O_CREAT | O_TRUNC); + if (state->fd < 0) + return -1; + debug(20, 3) ("storeDirWriteCleanLogs: opened %s, FD %d\n", + state->new, state->fd); +#if HAVE_FCHMOD + if (stat(state->cur, &sb) == 0) + fchmod(state->fd, sb.st_mode); +#endif + sd->log.clean.write = storeUfsDirWriteCleanEntry; + sd->log.clean.state = state; + return 0; +} + +/* + * "write" an entry to the clean log file. + */ +static void +storeUfsDirWriteCleanEntry(const StoreEntry * e, SwapDir * sd) +{ + storeSwapLogData s; + static size_t ss = sizeof(storeSwapLogData); + struct _clean_state *state = sd->log.clean.state; + if (NULL == e) { + storeUfsDirWriteCleanClose(sd); + return; + } + memset(&s, '\0', ss); + s.op = (char) SWAP_LOG_ADD; + s.swap_filen = e->swap_filen; + s.timestamp = e->timestamp; + s.lastref = e->lastref; + s.expires = e->expires; + s.lastmod = e->lastmod; + s.swap_file_sz = e->swap_file_sz; + s.refcount = e->refcount; + s.flags = e->flags; + xmemcpy(&s.key, e->key, MD5_DIGEST_CHARS); + xmemcpy(state->outbuf + state->outbuf_offset, &s, ss); + state->outbuf_offset += ss; + /* buffered write */ + if (state->outbuf_offset + ss > CLEAN_BUF_SZ) { + if (write(state->fd, state->outbuf, state->outbuf_offset) < 0) { + debug(50, 0) ("storeDirWriteCleanLogs: %s: write: %s\n", + state->new, xstrerror()); + debug(20, 0) ("storeDirWriteCleanLogs: Current swap logfile not replaced.\n"); + file_close(state->fd); + state->fd = -1; + unlink(state->new); + safe_free(state); + sd->log.clean.state = NULL; + sd->log.clean.write = NULL; + } + state->outbuf_offset = 0; + } +} + +static void +storeUfsDirWriteCleanClose(SwapDir * sd) +{ + struct _clean_state *state = sd->log.clean.state; + if (state->fd < 0) + return; + if (write(state->fd, state->outbuf, state->outbuf_offset) < 0) { + debug(50, 0) ("storeDirWriteCleanLogs: %s: write: %s\n", + state->new, xstrerror()); + debug(20, 0) ("storeDirWriteCleanLogs: Current swap logfile " + "not replaced.\n"); + file_close(state->fd); + state->fd = -1; + unlink(state->new); + } + safe_free(state->outbuf); + /* + * You can't rename open files on Microsoft "operating systems" + * so we have to close before renaming. + */ + storeUfsDirCloseSwapLog(sd); + /* rename */ + if (state->fd >= 0) { +#ifdef _SQUID_OS2_ + file_close(state->fd); + state->fd = -1; + if (unlink(cur) < 0) + debug(50, 0) ("storeDirWriteCleanLogs: unlinkd failed: %s, %s\n", + xstrerror(), cur); +#endif + xrename(state->new, state->cur); + } + /* touch a timestamp file if we're not still validating */ + if (store_dirs_rebuilding) + (void) 0; + else if (state->fd < 0) + (void) 0; + else + file_close(file_open(state->cln, O_WRONLY | O_CREAT | O_TRUNC)); + /* close */ + safe_free(state->cur); + safe_free(state->new); + safe_free(state->cln); + if (state->fd >= 0) + file_close(state->fd); + state->fd = -1; + safe_free(state); + sd->log.clean.state = NULL; + sd->log.clean.write = NULL; +} + +static void +storeUfsDirSwapLog(const SwapDir * sd, const StoreEntry * e, int op) +{ + ufsinfo_t *ufsinfo = (ufsinfo_t *)sd->fsdata; + storeSwapLogData *s = xcalloc(1, sizeof(storeSwapLogData)); + s->op = (char) op; + s->swap_filen = e->swap_filen; + s->timestamp = e->timestamp; + s->lastref = e->lastref; + s->expires = e->expires; + s->lastmod = e->lastmod; + s->swap_file_sz = e->swap_file_sz; + s->refcount = e->refcount; + s->flags = e->flags; + xmemcpy(s->key, e->key, MD5_DIGEST_CHARS); + file_write(ufsinfo->swaplog_fd, + -1, + s, + sizeof(storeSwapLogData), + NULL, + NULL, + xfree); +} + +static void +storeUfsDirNewfs(SwapDir * sd) +{ + debug(47, 3) ("Creating swap space in %s\n", sd->path); + storeUfsDirCreateDirectory(sd->path, 0); + storeUfsDirCreateSwapSubDirs(sd); +} + +static int +rev_int_sort(const void *A, const void *B) +{ + const int *i1 = A; + const int *i2 = B; + return *i2 - *i1; +} + +static int +storeUfsDirClean(int swap_index) +{ + DIR *dp = NULL; + struct dirent *de = NULL; + LOCAL_ARRAY(char, p1, MAXPATHLEN + 1); + LOCAL_ARRAY(char, p2, MAXPATHLEN + 1); +#if USE_TRUNCATE + struct stat sb; +#endif + int files[20]; + int swapfileno; + int fn; /* same as swapfileno, but with dirn bits set */ + int n = 0; + int k = 0; + int N0, N1, N2; + int D0, D1, D2; + SwapDir *SD; + ufsinfo_t *ufsinfo; + N0 = n_ufs_dirs; + D0 = ufs_dir_index[swap_index % N0]; + SD = &Config.cacheSwap.swapDirs[D0]; + ufsinfo = (ufsinfo_t *)SD->fsdata; + N1 = ufsinfo->l1; + D1 = (swap_index / N0) % N1; + N2 = ufsinfo->l2; + D2 = ((swap_index / N0) / N1) % N2; + snprintf(p1, SQUID_MAXPATHLEN, "%s/%02X/%02X", + Config.cacheSwap.swapDirs[D0].path, D1, D2); + debug(36, 3) ("storeDirClean: Cleaning directory %s\n", p1); + dp = opendir(p1); + if (dp == NULL) { + if (errno == ENOENT) { + debug(36, 0) ("storeDirClean: WARNING: Creating %s\n", p1); + if (mkdir(p1, 0777) == 0) + return 0; + } + debug(50, 0) ("storeDirClean: %s: %s\n", p1, xstrerror()); + safeunlink(p1, 1); + return 0; + } + while ((de = readdir(dp)) != NULL && k < 20) { + if (sscanf(de->d_name, "%X", &swapfileno) != 1) + continue; + fn = swapfileno; /* XXX should remove this cruft ! */ + if (storeUfsDirValidFileno(SD, fn)) + if (storeUfsDirMapBitTest(SD, fn)) + if (storeUfsFilenoBelongsHere(fn, D0, D1, D2)) + continue; +#if USE_TRUNCATE + if (!stat(de->d_name, &sb)) + if (sb.st_size == 0) + continue; +#endif + files[k++] = swapfileno; + } + closedir(dp); + if (k == 0) + return 0; + qsort(files, k, sizeof(int), rev_int_sort); + if (k > 10) + k = 10; + for (n = 0; n < k; n++) { + debug(36, 3) ("storeDirClean: Cleaning file %08X\n", files[n]); + snprintf(p2, MAXPATHLEN + 1, "%s/%08X", p1, files[n]); +#if USE_TRUNCATE + truncate(p2, 0); +#else + safeunlink(p2, 0); +#endif + Counter.swap_files_cleaned++; + } + debug(36, 3) ("Cleaned %d unused files from %s\n", k, p1); + return k; +} + +static void +storeUfsDirCleanEvent(void *unused) +{ + static int swap_index = 0; + int i; + int j = 0; + int n = 0; + /* + * Assert that there are UFS cache_dirs configured, otherwise + * we should never be called. + */ + assert(n_ufs_dirs); + if (NULL == ufs_dir_index) { + SwapDir *sd; + ufsinfo_t *ufsinfo; + /* + * Initialize the little array that translates UFS cache_dir + * number into the Config.cacheSwap.swapDirs array index. + */ + ufs_dir_index = xcalloc(n_ufs_dirs, sizeof(*ufs_dir_index)); + for (i = 0, n = 0; i < Config.cacheSwap.n_configured; i++) { + sd = &Config.cacheSwap.swapDirs[i]; + if (!storeUfsDirIs(sd)) + continue; + ufs_dir_index[n++] = i; + ufsinfo = (ufsinfo_t *)sd->fsdata; + j += (ufsinfo->l1 * ufsinfo->l2); + } + assert(n == n_ufs_dirs); + /* + * Start the storeUfsDirClean() swap_index with a random + * value. j equals the total number of UFS level 2 + * swap directories + */ + swap_index = (int) (squid_random() % j); + } + if (0 == store_dirs_rebuilding) { + n = storeUfsDirClean(swap_index); + swap_index++; + } + eventAdd("storeDirClean", storeUfsDirCleanEvent, NULL, + 15.0 * exp(-0.25 * n), 1); +} + +static int +storeUfsDirIs(SwapDir * sd) +{ + if (strncmp(sd->type, "ufs", 3) == 0) + return 1; + return 0; +} + +/* + * Does swapfile number 'fn' belong in cachedir #F0, + * level1 dir #F1, level2 dir #F2? + */ +static int +storeUfsFilenoBelongsHere(int fn, int F0, int F1, int F2) +{ + int D1, D2; + int L1, L2; + int filn = fn; + ufsinfo_t *ufsinfo; + assert(F0 < Config.cacheSwap.n_configured); + ufsinfo = (ufsinfo_t *)Config.cacheSwap.swapDirs[F0].fsdata; + L1 = ufsinfo->l1; + L2 = ufsinfo->l2; + D1 = ((filn / L2) / L2) % L1; + if (F1 != D1) + return 0; + D2 = (filn / L2) % L2; + if (F2 != D2) + return 0; + return 1; +} + +int +storeUfsDirValidFileno(SwapDir *SD, sfileno filn) +{ + ufsinfo_t *ufsinfo = (ufsinfo_t *)SD->fsdata; + if (filn < 0) + return 0; + if (filn > ufsinfo->map->max_n_files) + return 0; + return 1; +} + +void +storeUfsDirMaintain(SwapDir *SD) +{ + StoreEntry *e = NULL; + int scanned = 0; + int locked = 0; + int expired = 0; + int max_scan; + int max_remove; + double f; + static time_t last_warn_time = 0; +#if !HEAP_REPLACEMENT + dlink_node *m; + dlink_node *prev = NULL; +#else + heap_key age; + heap_key min_age = 0.0; + link_list *locked_entries = NULL; +#if HEAP_REPLACEMENT_DEBUG + if (!verify_heap_property(SD->repl.heap.heap)) { + debug(20, 1) ("Heap property violated!\n"); + } +#endif +#endif + /* We can't delete objects while rebuilding swap */ + if (store_dirs_rebuilding) { + return; + } else { + f = (double) (store_swap_size - store_swap_low) / (store_swap_high - store_swap_low); + f = f < 0.0 ? 0.0 : f > 1.0 ? 1.0 : f; + max_scan = (int) (f * 400.0 + 100.0); + max_remove = (int) (f * 70.0 + 10.0); + /* + * This is kinda cheap, but so we need this priority hack? + */ +#if 0 + eventAdd("MaintainSwapSpace", storeMaintainSwapSpace, NULL, 1.0 - f, 1); +#endif + } + debug(20, 3) ("storeMaintainSwapSpace: f=%f, max_scan=%d, max_remove=%d\n", f, max_scan, max_remove); +#if HEAP_REPLACEMENT + while (heap_nodes(SD->repl.heap.heap) > 0) { + if (store_swap_size < store_swap_low) + break; + if (expired >= max_remove) + break; + if (scanned >= max_scan) + break; + age = heap_peepminkey(SD->repl.heap.heap); + e = heap_extractmin(SD->repl.heap.heap); + e->repl.node = NULL; /* no longer in the heap */ + scanned++; + if (storeEntryLocked(e)) { + /* + * Entry is in use ... put it in a linked list to ignore it. + */ + if (!EBIT_TEST(e->flags, ENTRY_SPECIAL)) { + /* + * If this was a "SPECIAL" do not add it back into the heap. + * It will always be "SPECIAL" and therefore never removed. + */ + debug(20, 4) ("storeUfsDirMaintain: locked url %s\n", + (e->mem_obj && e->mem_obj->url) ? e->mem_obj->url : storeKeyText(e-> +key)); + linklistPush(&locked_entries, e); + } + locked++; + continue; + } else if (storeUfsDirCheckExpired(SD, e)) { + /* + * Note: This will not check the reference age ifdef + * HEAP_REPLACEMENT, but it does some other useful + * checks... + */ + expired++; + debug(20, 3) ("Released store object age %f size %d refs %d key %s\n", + age, e->swap_file_sz, e->refcount, storeKeyText(e->key)); + min_age = age; + storeRelease(e); + } else { + /* + * Did not expire the object so we need to add it back + * into the heap! + */ + debug(20, 5) ("storeMaintainSwapSpace: non-expired %s\n", + storeKeyText(e->key)); + linklistPush(&locked_entries, e); + continue; + } + if (store_swap_size < store_swap_low) + break; + else if (expired >= max_remove) + break; + else if (scanned >= max_scan) + break; + } + /* + * Bump the heap age factor. + */ + if (min_age > 0.0) + SD->repl.heap.heap->age = min_age; + /* + * Reinsert all bumped locked entries back into heap... + */ + while ((e = linklistShift(&locked_entries))) + e->repl.node = heap_insert(SD->repl.heap.heap, e); +#else + for (m = SD->repl.lru.list.tail; m; m = prev) { + prev = m->prev; + e = m->data; + scanned++; + if (storeEntryLocked(e)) { + /* + * If there is a locked entry at the tail of the LRU list, + * move it to the beginning to get it out of the way. + * Theoretically, we might have all locked objects at the + * tail, and then we'll never remove anything here and the + * LRU age will go to zero. + */ + if (memInUse(MEM_STOREENTRY) > max_scan) { + dlinkDelete(&e->repl.lru, &SD->repl.lru.list); + dlinkAdd(e, &e->repl.lru, &SD->repl.lru.list); + } + locked++; + + } else if (storeUfsDirCheckExpired(SD, e)) { + expired++; + storeRelease(e); + } + if (expired >= max_remove) + break; + if (scanned >= max_scan) + break; + } +#endif + debug(20, (expired ? 2 : 3)) ("storeMaintainSwapSpace: scanned %d/%d removed %d/%d l +ocked %d f=%.03f\n", + scanned, max_scan, expired, max_remove, locked, f); + debug(20, 3) ("storeMaintainSwapSpace stats:\n"); + debug(20, 3) (" %6d objects\n", memInUse(MEM_STOREENTRY)); + debug(20, 3) (" %6d were scanned\n", scanned); + debug(20, 3) (" %6d were locked\n", locked); + debug(20, 3) (" %6d were expired\n", expired); + if (store_swap_size < Config.Swap.maxSize) + return; + if (squid_curtime - last_warn_time < 10) + return; + debug(20, 0) ("WARNING: Disk space over limit: %d KB > %d KB\n", + store_swap_size, Config.Swap.maxSize); + last_warn_time = squid_curtime; +} + +/* + * storeUfsDirCheckObj + * + * This routine is called by storeDirSelectSwapDir to see if the given + * object is able to be stored on this filesystem. UFS filesystems will + * happily store anything as long as the LRU time isn't too small. + */ +int +storeUfsDirCheckObj(SwapDir *SD, const StoreEntry *e) +{ +#if !HEAP_REPLACEMENT + if (storeUfsDirExpiredReferenceAge(SD) < 300) { + debug(20, 3) ("storeUfsDirCheckObj: NO: LRU Age = %d\n", + storeUfsDirExpiredReferenceAge(SD)); + /* store_check_cachable_hist.no.lru_age_too_low++; */ + return -1; + } +#endif + /* Return 999 (99.9%) constant load */ + return 999; +} + +/* + * storeUfsDirRefObj + * + * This routine is called whenever an object is referenced, so we can + * maintain replacement information within the storage fs. + */ +void +storeUfsDirRefObj(SwapDir *SD, StoreEntry *e) +{ + debug(1, 3) ("storeUfsDirRefObj: referencing %p %d/%d\n", e, e->swap_dirn, + e->swap_filen); +#if HEAP_REPLACEMENT + /* Nothing to do here */ +#else + /* Reference the object */ + if (!EBIT_TEST(e->flags, RELEASE_REQUEST) && + !EBIT_TEST(e->flags, ENTRY_SPECIAL)) { + dlinkDelete(&e->repl.lru, &SD->repl.lru.list); + dlinkAdd(e, &e->repl.lru, &SD->repl.lru.list); + } +#endif +} + +/* + * storeUfsDirUnrefObj + * This routine is called whenever the last reference to an object is + * removed, to maintain replacement information within the storage fs. + */ +void +storeUfsDirUnrefObj(SwapDir *SD, StoreEntry *e) +{ + debug(1, 3) ("storeUfsDirUnrefObj: referencing %p %d/%d\n", e, e->swap_dirn, + e->swap_filen); +#if HEAP_REPLACEMENT + if (e->repl.node) + heap_update(SD->repl.heap.heap, e->repl.node, e); +#endif +} + +/* + * storeUfsDirUnlinkFile + * + * This routine unlinks a file and pulls it out of the bitmap. + * It used to be in storeUfsUnlink(), however an interface change + * forced this bit of code here. Eeek. + */ +void +storeUfsDirUnlinkFile(SwapDir *SD, sfileno f) +{ + debug(79, 3) ("storeUfsDirUnlinkFile: unlinking fileno %08X\n", f); + storeUfsDirMapBitReset(SD, f); + unlinkdUnlink(storeUfsDirFullPath(SD, f, NULL)); +} + +#if !HEAP_REPLACEMENT +/* + * storeUfsDirExpiredReferenceAge + * + * The LRU age is scaled exponentially between 1 minute and + * Config.referenceAge , when store_swap_low < store_swap_size < + * store_swap_high. This keeps store_swap_size within the low and high + * water marks. If the cache is very busy then store_swap_size stays + * closer to the low water mark, if it is not busy, then it will stay + * near the high water mark. The LRU age value can be examined on the + * cachemgr 'info' page. + */ +static time_t +storeUfsDirExpiredReferenceAge(SwapDir *SD) +{ + double x; + double z; + time_t age; + long store_high, store_low; + + store_high = (long) (((float) SD->max_size * + (float) Config.Swap.highWaterMark) / (float) 100); + store_low = (long) (((float) SD->max_size * + (float) Config.Swap.lowWaterMark) / (float) 100); + debug(20, 20) ("RA: Dir %s, hi=%d, lo=%d, cur=%d\n", SD->path, store_high, store_low, SD->cur_size); + + x = (double) (store_high - SD->cur_size) / + (store_high - store_low); + x = x < 0.0 ? 0.0 : x > 1.0 ? 1.0 : x; + z = pow((double) (Config.referenceAge / 60), x); + age = (time_t) (z * 60.0); + if (age < 60) + age = 60; + else if (age > Config.referenceAge) + age = Config.referenceAge; + return age; +} +#endif + +/* + * storeUfsDirCheckExpired + * + * Check whether the given object is expired or not + * It breaks layering a little by calling the upper layers to find + * out whether the object is locked or not, but we can't help this + * right now. + */ +static int +storeUfsDirCheckExpired(SwapDir *SD, StoreEntry *e) +{ + if (storeEntryLocked(e)) + return 0; + if (EBIT_TEST(e->flags, RELEASE_REQUEST)) + return 1; + if (EBIT_TEST(e->flags, ENTRY_NEGCACHED) && squid_curtime >= e->expires) + return 1; + +#if HEAP_REPLACEMENT + /* + * with HEAP_REPLACEMENT we are not using the LRU reference age, the heap + * controls the replacement of objects. + */ + return 1; +#else + if (squid_curtime - e->lastref > storeUfsDirExpiredReferenceAge(SD)) + return 1; + return 0; +#endif +} + +/* + * Add and remove the given StoreEntry from the replacement policy in + * use. + */ + +void +storeUfsDirReplAdd(SwapDir *SD, StoreEntry *e) +{ + debug(20, 4) ("storeUfsDirReplAdd: added node %p to dir %d\n", e, + SD->index); +#if HEAP_REPLACEMENT + if (EBIT_TEST(e->flags, ENTRY_SPECIAL)) { + (void) 0; + } else { + e->repl.node = heap_insert(SD->repl.heap.heap, e); + debug(20, 4) ("storeUfsDirReplAdd: inserted node 0x%x\n", e->repl.node); + } +#else + /* Shouldn't we not throw special objects into the lru ? */ + dlinkAdd(e, &e->repl.lru, &SD->repl.lru.list); +#endif +} + + +void +storeUfsDirReplRemove(StoreEntry *e) +{ + SwapDir *SD = INDEXSD(e->swap_dirn); + debug(20, 4) ("storeUfsDirReplRemove: remove node %p from dir %d\n", e, + SD->index); +#if HEAP_REPLACEMENT + /* And now, release the object from the replacement policy */ + if (e->repl.node) { + debug(20, 4) ("storeUfsDirReplRemove: deleting node 0x%x\n", + e->repl.node); + heap_delete(SD->repl.heap.heap, e->repl.node); + e->repl.node = NULL; + } +#else + dlinkDelete(&e->repl.lru, &SD->repl.lru.list); +#endif +} + + + +/* ========== LOCAL FUNCTIONS ABOVE, GLOBAL FUNCTIONS BELOW ========== */ + +void +storeUfsDirStats(SwapDir *SD, StoreEntry * sentry) +{ + ufsinfo_t *ufsinfo; +#if HAVE_STATVFS + struct statvfs sfs; +#endif + ufsinfo = (ufsinfo_t *)SD->fsdata; + storeAppendPrintf(sentry, "First level subdirectories: %d\n", ufsinfo->l1); + storeAppendPrintf(sentry, "Second level subdirectories: %d\n", ufsinfo->l2); + storeAppendPrintf(sentry, "Maximum Size: %d KB\n", SD->max_size); + storeAppendPrintf(sentry, "Current Size: %d KB\n", SD->cur_size); + storeAppendPrintf(sentry, "Percent Used: %0.2f%%\n", + 100.0 * SD->cur_size / SD->max_size); + storeAppendPrintf(sentry, "Filemap bits in use: %d of %d (%d%%)\n", + ufsinfo->map->n_files_in_map, ufsinfo->map->max_n_files, + percent(ufsinfo->map->n_files_in_map, ufsinfo->map->max_n_files)); +#if HAVE_STATVFS +#define fsbtoblk(num, fsbs, bs) \ + (((fsbs) != 0 && (fsbs) < (bs)) ? \ + (num) / ((bs) / (fsbs)) : (num) * ((fsbs) / (bs))) + if (!statvfs(SD->path, &sfs)) { + storeAppendPrintf(sentry, "Filesystem Space in use: %d/%d KB (%d%%)\n", + fsbtoblk((sfs.f_blocks - sfs.f_bfree), sfs.f_frsize, 1024), + fsbtoblk(sfs.f_blocks, sfs.f_frsize, 1024), + percent(sfs.f_blocks - sfs.f_bfree, sfs.f_blocks)); + storeAppendPrintf(sentry, "Filesystem Inodes in use: %d/%d (%d%%)\n", + sfs.f_files - sfs.f_ffree, sfs.f_files, + percent(sfs.f_files - sfs.f_ffree, sfs.f_files)); + } +#endif + storeAppendPrintf(sentry, "Flags:"); + if (SD->flags.selected) + storeAppendPrintf(sentry, " SELECTED"); + if (SD->flags.read_only) + storeAppendPrintf(sentry, " READ-ONLY"); + storeAppendPrintf(sentry, "\n"); +#if !HEAP_REPLACEMENT + storeAppendPrintf(sentry, "LRU Expiration Age: %6.2f days\n", + (double) storeUfsDirExpiredReferenceAge(SD) / 86400.0); +#else +#if 0 + storeAppendPrintf(sentry, "Storage Replacement Threshold:\t%f\n", + heap_peepminkey(sd.repl.heap.heap)); +#endif +#endif +} + +/* + * storeUfsDirReconfigure + * + * This routine is called when the given swapdir needs reconfiguring + */ +void +storeUfsDirReconfigure(SwapDir *sd, int index, char *path) +{ + char *token; + int i; + int size; + int l1; + int l2; + unsigned int read_only = 0; + + i = GetInteger(); + size = i << 10; /* Mbytes to kbytes */ + if (size <= 0) + fatal("storeUfsDirReconfigure: invalid size value"); + i = GetInteger(); + l1 = i; + if (l1 <= 0) + fatal("storeUfsDirReconfigure: invalid level 1 directories value"); + i = GetInteger(); + l2 = i; + if (l2 <= 0) + fatal("storeUfsDirReconfigure: invalid level 2 directories value"); + if ((token = strtok(NULL, w_space))) + if (!strcasecmp(token, "read-only")) + read_only = 1; + + /* just reconfigure it */ + if (size == sd->max_size) + debug(3, 1) ("Cache dir '%s' size remains unchanged at %d KB\n", + path, size); + else + debug(3, 1) ("Cache dir '%s' size changed to %d KB\n", + path, size); + sd->max_size = size; + if (sd->flags.read_only != read_only) + debug(3, 1) ("Cache dir '%s' now %s\n", + path, read_only ? "Read-Only" : "Read-Write"); + sd->flags.read_only = read_only; + return; +} + +void +storeUfsDirDump(StoreEntry * entry, const char *name, SwapDir * s) +{ + ufsinfo_t *ufsinfo = (ufsinfo_t *)s->fsdata; + storeAppendPrintf(entry, "%s %s %s %d %d %d\n", + name, + "ufs", + s->path, + s->max_size >> 10, + ufsinfo->l1, + ufsinfo->l2); +} + +/* + * Only "free" the filesystem specific stuff here + */ +static void +storeUfsDirFree(SwapDir * s) +{ + ufsinfo_t *ufsinfo = (ufsinfo_t *)s->fsdata; + if (ufsinfo->swaplog_fd > -1) { + file_close(ufsinfo->swaplog_fd); + ufsinfo->swaplog_fd = -1; + } + filemapFreeMemory(ufsinfo->map); + xfree(ufsinfo); + s->fsdata = NULL; /* Will aid debugging... */ + +} + +char * +storeUfsDirFullPath(SwapDir *SD, sfileno filn, char *fullpath) +{ + LOCAL_ARRAY(char, fullfilename, SQUID_MAXPATHLEN); + ufsinfo_t *ufsinfo = (ufsinfo_t *)SD->fsdata; + int L1 = ufsinfo->l1; + int L2 = ufsinfo->l2; + if (!fullpath) + fullpath = fullfilename; + fullpath[0] = '\0'; + snprintf(fullpath, SQUID_MAXPATHLEN, "%s/%02X/%02X/%08X", + SD->path, + ((filn / L2) / L2) % L1, + (filn / L2) % L2, + filn); + return fullpath; +} + +/* + * storeUfsCleanupDoubleCheck + * + * This is called by storeCleanup() if -S was given on the command line. + */ +static int +storeUfsCleanupDoubleCheck(SwapDir *sd, StoreEntry *e) +{ + struct stat sb; + + if (stat(storeUfsDirFullPath(sd, e->swap_filen, NULL), &sb) < 0) { + debug(20, 0) ("storeUfsCleanupDoubleCheck: MISSING SWAP FILE\n"); + debug(20, 0) ("storeUfsCleanupDoubleCheck: FILENO %08X\n", e->swap_filen); + debug(20, 0) ("storeUfsCleanupDoubleCheck: PATH %s\n", + storeUfsDirFullPath(sd, e->swap_filen, NULL)); + storeEntryDump(e, 0); + return -1; + } + if (e->swap_file_sz != sb.st_size) { + debug(20, 0) ("storeUfsCleanupDoubleCheck: SIZE MISMATCH\n"); + debug(20, 0) ("storeUfsCleanupDoubleCheck: FILENO %08X\n", e->swap_filen); + debug(20, 0) ("storeUfsCleanupDoubleCheck: PATH %s\n", + storeUfsDirFullPath(sd, e->swap_filen, NULL)); + debug(20, 0) ("storeUfsCleanupDoubleCheck: ENTRY SIZE: %d, FILE SIZE: %d\n", + e->swap_file_sz, (int) sb.st_size); + storeEntryDump(e, 0); + return -1; + } + return 0; +} + +/* + * storeUfsDirParse + * + * Called when a *new* fs is being setup. + */ +void +storeUfsDirParse(SwapDir *sd, int index, char *path) +{ + char *token; + int i; + int size; + int l1; + int l2; + unsigned int read_only = 0; + ufsinfo_t *ufsinfo; + + i = GetInteger(); + size = i << 10; /* Mbytes to kbytes */ + if (size <= 0) + fatal("storeUfsDirParse: invalid size value"); + i = GetInteger(); + l1 = i; + if (l1 <= 0) + fatal("storeUfsDirParse: invalid level 1 directories value"); + i = GetInteger(); + l2 = i; + if (l2 <= 0) + fatal("storeUfsDirParse: invalid level 2 directories value"); + if ((token = strtok(NULL, w_space))) + if (!strcasecmp(token, "read-only")) + read_only = 1; + + ufsinfo = xmalloc(sizeof(ufsinfo_t)); + if (ufsinfo == NULL) + fatal("storeUfsDirParse: couldn't xmalloc() ufsinfo_t!\n"); + + sd->index = index; + sd->path = xstrdup(path); + sd->max_size = size; + sd->fsdata = ufsinfo; + ufsinfo->l1 = l1; + ufsinfo->l2 = l2; + ufsinfo->swaplog_fd = -1; + ufsinfo->map = NULL; /* Debugging purposes */ + ufsinfo->suggest = 0; + sd->flags.read_only = read_only; + sd->init = storeUfsDirInit; + sd->newfs = storeUfsDirNewfs; + sd->dump = storeUfsDirDump; + sd->freefs = storeUfsDirFree; + sd->dblcheck = storeUfsCleanupDoubleCheck; + sd->statfs = storeUfsDirStats; + sd->maintainfs = storeUfsDirMaintain; + sd->checkobj = storeUfsDirCheckObj; + sd->refobj = storeUfsDirRefObj; + sd->unrefobj = storeUfsDirUnrefObj; + sd->callback = NULL; + sd->sync = NULL; + sd->obj.create = storeUfsCreate; + sd->obj.open = storeUfsOpen; + sd->obj.close = storeUfsClose; + sd->obj.read = storeUfsRead; + sd->obj.write = storeUfsWrite; + sd->obj.unlink = storeUfsUnlink; + sd->log.open = storeUfsDirOpenSwapLog; + sd->log.close = storeUfsDirCloseSwapLog; + sd->log.write = storeUfsDirSwapLog; + sd->log.clean.open = storeUfsDirWriteCleanOpen; + + /* Initialise replacement policy stuff */ +#if HEAP_REPLACEMENT + /* + * Create new heaps with cache replacement policies attached to them. + * The cache replacement policy is specified as either GDSF or LFUDA in + * the squid.conf configuration file. Note that the replacement policy + * applies only to the disk replacement algorithm. Memory replacement + * always uses GDSF since we want to maximize object hit rate. + */ + if (Config.replPolicy) { + if (tolower(Config.replPolicy[0]) == 'g') { + debug(20, 1) ("Using GDSF disk replacement policy\n"); + sd->repl.heap.heap = new_heap(10000, HeapKeyGen_StoreEntry_GDSF); + } else if (tolower(Config.replPolicy[0]) == 'l') { + if (tolower(Config.replPolicy[1]) == 'f') { + debug(20, 1) ("Using LFUDA disk replacement policy\n"); + sd->repl.heap.heap = new_heap(10000, HeapKeyGen_StoreEntry_LFUDA); + } else if (tolower(Config.replPolicy[1]) == 'r') { + debug(20, 1) ("Using LRU heap disk replacement policy\n"); + sd->repl.heap.heap = new_heap(10000, HeapKeyGen_StoreEntry_LRU); + } + } else { + debug(20, 1) ("Unrecognized replacement_policy; using GDSF\n"); + sd->repl.heap.heap = new_heap(10000, HeapKeyGen_StoreEntry_GDSF); + } + } else { + debug(20, 1) ("Using default disk replacement policy (GDSF)\n"); + sd->repl.heap.heap = new_heap(10000, HeapKeyGen_StoreEntry_GDSF); + } +#else + sd->repl.lru.list.head = NULL; + sd->repl.lru.list.tail = NULL; +#endif +} + +/* + * Initial setup / end destruction + */ +void +storeUfsDirDone(void) +{ + memPoolDestroy(ufs_state_pool); + ufs_initialised = 0; +} + +void +storeFsSetup_ufs(storefs_entry_t *storefs) +{ + assert(!ufs_initialised); + storefs->parsefunc = storeUfsDirParse; + storefs->reconfigurefunc = storeUfsDirReconfigure; + storefs->donefunc = storeUfsDirDone; + ufs_state_pool = memPoolCreate("UFS IO State data", sizeof(ufsstate_t)); + ufs_initialised = 1; +} + diff --git a/src/fs/ufs/store_io_ufs.cc b/src/fs/ufs/store_io_ufs.cc new file mode 100644 index 0000000000..3e241b93dc --- /dev/null +++ b/src/fs/ufs/store_io_ufs.cc @@ -0,0 +1,267 @@ + +/* + * $Id: store_io_ufs.cc,v 1.1 2000/05/03 17:15:48 adrian Exp $ + * + * DEBUG: section 79 Storage Manager UFS Interface + * AUTHOR: Duane Wessels + * + * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from the + * Internet community. Development is led by Duane Wessels of the + * National Laboratory for Applied Network Research and funded by the + * National Science Foundation. Squid is Copyrighted (C) 1998 by + * Duane Wessels and the University of California San Diego. Please + * see the COPYRIGHT file for full details. Squid incorporates + * software developed and/or copyrighted by other sources. Please see + * the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#include "squid.h" +#include "store_ufs.h" + + +static DRCB storeUfsReadDone; +static DWCB storeUfsWriteDone; +static void storeUfsIOCallback(storeIOState * sio, int errflag); +static void storeUfsIOFreeEntry(void *, int); + +/* === PUBLIC =========================================================== */ + +storeIOState * +storeUfsOpen(SwapDir *SD, StoreEntry *e, STFNCB * file_callback, + STIOCB * callback, void *callback_data) +{ + sfileno f = e->swap_filen; + char *path = storeUfsDirFullPath(SD, f, NULL); + storeIOState *sio; + struct stat sb; + int fd; + debug(79, 3) ("storeUfsOpen: fileno %08X\n", f); + fd = file_open(path, O_RDONLY); + if (fd < 0) { + debug(79, 3) ("storeUfsOpen: got failure (%d)\n", errno); + return NULL; + } + debug(79, 3) ("storeUfsOpen: opened FD %d\n", fd); + sio = memAllocate(MEM_STORE_IO); + cbdataAdd(sio, storeUfsIOFreeEntry, MEM_STORE_IO); + sio->fsstate = memPoolAlloc(ufs_state_pool); + + sio->swap_filen = f; + sio->swap_dirn = SD->index; + sio->mode = O_RDONLY; + sio->callback = callback; + sio->callback_data = callback_data; + cbdataLock(callback_data); + sio->e = e; + ((ufsstate_t *)(sio->fsstate))->fd = fd; + ((ufsstate_t *)(sio->fsstate))->flags.writing = 0; + ((ufsstate_t *)(sio->fsstate))->flags.reading = 0; + ((ufsstate_t *)(sio->fsstate))->flags.close_request = 0; + if (fstat(fd, &sb) == 0) + sio->st_size = sb.st_size; + store_open_disk_fd++; + + /* We should update the heap/dlink position here ! */ + return sio; +} + +storeIOState * +storeUfsCreate(SwapDir *SD, StoreEntry *e, STFNCB *file_callback, STIOCB *callback, void *callback_data) +{ + storeIOState *sio; + int fd; + int mode = (O_WRONLY | O_CREAT | O_TRUNC); + char *path; + ufsinfo_t *ufsinfo = (ufsinfo_t *)SD->fsdata; + sfileno filn; + sdirno dirn; + + /* Allocate a number */ + dirn = SD->index; + filn = storeUfsDirMapBitAllocate(SD); + ufsinfo->suggest = filn + 1; + /* Shouldn't we handle a 'bitmap full' error here? */ + path = storeUfsDirFullPath(SD, filn, NULL); + + debug(79, 3) ("storeUfsCreate: fileno %08X\n", filn); + fd = file_open(path, mode); + if (fd < 0) { + debug(79, 3) ("storeUfsCreate: got failure (%d)\n", errno); + return NULL; + } + debug(79, 3) ("storeUfsCreate: opened FD %d\n", fd); + sio = memAllocate(MEM_STORE_IO); + cbdataAdd(sio, storeUfsIOFreeEntry, MEM_STORE_IO); + sio->fsstate = memPoolAlloc(ufs_state_pool); + + sio->swap_filen = filn; + sio->swap_dirn = dirn; + sio->mode = mode; + sio->callback = callback; + sio->callback_data = callback_data; + cbdataLock(callback_data); + sio->e = (StoreEntry *) e; + ((ufsstate_t *)(sio->fsstate))->fd = fd; + ((ufsstate_t *)(sio->fsstate))->flags.writing = 0; + ((ufsstate_t *)(sio->fsstate))->flags.reading = 0; + ((ufsstate_t *)(sio->fsstate))->flags.close_request = 0; + store_open_disk_fd++; + + /* now insert into the replacement policy */ + storeUfsDirReplAdd(SD, e); + return sio; +} + +void +storeUfsClose(SwapDir *SD, storeIOState * sio) +{ + ufsstate_t *ufsstate = (ufsstate_t *)sio->fsstate; + + debug(79, 3) ("storeUfsClose: dirno %d, fileno %08X, FD %d\n", + sio->swap_dirn, sio->swap_filen, ufsstate->fd); + if (ufsstate->flags.reading || ufsstate->flags.writing) { + ufsstate->flags.close_request = 1; + return; + } + storeUfsIOCallback(sio, 0); +} + +void +storeUfsRead(SwapDir *SD, storeIOState * sio, char *buf, size_t size, off_t offset, STRCB * callback, void *callback_data) +{ + ufsstate_t *ufsstate = (ufsstate_t *)sio->fsstate; + + assert(sio->read.callback == NULL); + assert(sio->read.callback_data == NULL); + sio->read.callback = callback; + sio->read.callback_data = callback_data; + cbdataLock(callback_data); + debug(79, 3) ("storeUfsRead: dirno %d, fileno %08X, FD %d\n", + sio->swap_dirn, sio->swap_filen, ufsstate->fd); + sio->offset = offset; + ufsstate->flags.reading = 1; + file_read(ufsstate->fd, + buf, + size, + offset, + storeUfsReadDone, + sio); +} + +void +storeUfsWrite(SwapDir *SD, storeIOState * sio, char *buf, size_t size, off_t offset, FREE * free_func) +{ + ufsstate_t *ufsstate = (ufsstate_t *)sio->fsstate; + debug(79, 3) ("storeUfsWrite: dirn %d, fileno %08X, FD %d\n", sio->swap_dirn, sio->swap_filen, ufsstate->fd); + ufsstate->flags.writing = 1; + file_write(ufsstate->fd, + offset, + buf, + size, + storeUfsWriteDone, + sio, + free_func); +} + +void +storeUfsUnlink(SwapDir *SD, StoreEntry *e) +{ + debug(79, 3) ("storeUfsUnlink: fileno %08X\n", e->swap_filen); + storeUfsDirReplRemove(e); + storeUfsDirUnlinkFile(SD, e->swap_filen); +} + +/* === STATIC =========================================================== */ + +static void +storeUfsReadDone(int fd, const char *buf, int len, int errflag, void *my_data) +{ + storeIOState *sio = my_data; + ufsstate_t *ufsstate = (ufsstate_t *)sio->fsstate; + STRCB *callback = sio->read.callback; + void *their_data = sio->read.callback_data; + ssize_t rlen; + + debug(79, 3) ("storeUfsReadDone: dirno %d, fileno %08X, FD %d, len %d\n", + sio->swap_dirn, sio->swap_filen, fd, len); + ufsstate->flags.reading = 0; + if (errflag) { + debug(79, 3) ("storeUfsReadDone: got failure (%d)\n", errflag); + rlen = -1; + } else { + rlen = (ssize_t) len; + sio->offset += len; + } + assert(callback); + assert(their_data); + sio->read.callback = NULL; + sio->read.callback_data = NULL; + if (cbdataValid(their_data)) + callback(their_data, buf, (size_t) rlen); + cbdataUnlock(their_data); +} + +static void +storeUfsWriteDone(int fd, int errflag, size_t len, void *my_data) +{ + storeIOState *sio = my_data; + ufsstate_t *ufsstate = (ufsstate_t *)sio->fsstate; + debug(79, 3) ("storeUfsWriteDone: dirno %d, fileno %08X, FD %d, len %d\n", + sio->swap_dirn, sio->swap_filen, fd, len); + ufsstate->flags.writing = 0; + if (errflag) { + debug(79, 0) ("storeUfsWriteDone: got failure (%d)\n", errflag); + storeUfsIOCallback(sio, errflag); + return; + } + sio->offset += len; + if (ufsstate->flags.close_request) + storeUfsIOCallback(sio, errflag); +} + +static void +storeUfsIOCallback(storeIOState * sio, int errflag) +{ + ufsstate_t *ufsstate = (ufsstate_t *)sio->fsstate; + debug(79, 3) ("storeUfsIOCallback: errflag=%d\n", errflag); + if (ufsstate->fd > -1) { + file_close(ufsstate->fd); + store_open_disk_fd--; + } + if (cbdataValid(sio->callback_data)) + sio->callback(sio->callback_data, errflag, sio); + cbdataUnlock(sio->callback_data); + sio->callback_data = NULL; + sio->callback = NULL; + cbdataFree(sio); +} + + +/* + * We can't pass memFree() as a free function here, because we need to free + * the fsstate variable .. + */ +static void +storeUfsIOFreeEntry(void *sio, int foo) +{ + memPoolFree(ufs_state_pool, ((storeIOState *)sio)->fsstate); + memFree(sio, MEM_STORE_IO); +} diff --git a/src/fs/ufs/store_ufs.h b/src/fs/ufs/store_ufs.h new file mode 100644 index 0000000000..94ab590967 --- /dev/null +++ b/src/fs/ufs/store_ufs.h @@ -0,0 +1,50 @@ +/* + * store_ufs.h + * + * Internal declarations for the ufs routines + */ + +#ifndef __STORE_UFS_H__ +#define __STORE_UFS_H__ + +struct _ufsinfo_t { + int swaplog_fd; + int l1; + int l2; + fileMap *map; + int suggest; +}; + +struct _ufsstate_t { + int fd; + struct { + unsigned int close_request:1; + unsigned int reading:1; + unsigned int writing:1; + } flags; +}; + +typedef struct _ufsinfo_t ufsinfo_t; +typedef struct _ufsstate_t ufsstate_t; + +/* The ufs_state memory pool */ +extern MemPool * ufs_state_pool; + +extern void storeUfsDirMapBitReset(SwapDir *, sfileno); +extern int storeUfsDirMapBitAllocate(SwapDir *); +extern char * storeUfsDirFullPath(SwapDir *SD, sfileno filn, char *fullpath); +extern void storeUfsDirUnlinkFile(SwapDir *, sfileno); +extern void storeUfsDirReplAdd(SwapDir *SD, StoreEntry *); +extern void storeUfsDirReplRemove(StoreEntry *); + +/* + * Store IO stuff + */ +extern STOBJCREATE storeUfsCreate; +extern STOBJOPEN storeUfsOpen; +extern STOBJCLOSE storeUfsClose; +extern STOBJREAD storeUfsRead; +extern STOBJWRITE storeUfsWrite; +extern STOBJUNLINK storeUfsUnlink; + +#endif diff --git a/src/globals.h b/src/globals.h index 9dc83cadfd..f5765564f6 100644 --- a/src/globals.h +++ b/src/globals.h @@ -1,6 +1,6 @@ /* - * $Id: globals.h,v 1.87 2000/03/06 16:23:31 wessels Exp $ + * $Id: globals.h,v 1.88 2000/05/03 17:15:42 adrian Exp $ * * * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ @@ -126,10 +126,8 @@ extern double current_dtime; extern int store_hash_buckets; /* 0 */ extern hash_table *store_table; /* NULL */ #if HEAP_REPLACEMENT -extern heap *store_heap; extern heap *inmem_heap; #else -extern dlink_list store_list; #endif extern dlink_list ClientActiveRequests; extern const String StringNull; /* { 0, 0, NULL } */ @@ -151,3 +149,8 @@ extern int refresh_nocache_hack; /* 0 */ extern request_flags null_request_flags; extern int store_open_disk_fd; /* 0 */ extern const char *SwapDirType[]; +extern storefs_entry_t *storefs_list; /* NULL */ +extern int store_swap_low; +extern int store_swap_high; +extern int store_pages_max; +extern size_t store_maxobjsize; diff --git a/src/main.cc b/src/main.cc index 9140b7d45a..314d3fb55b 100644 --- a/src/main.cc +++ b/src/main.cc @@ -1,6 +1,6 @@ /* - * $Id: main.cc,v 1.309 2000/05/02 20:13:58 hno Exp $ + * $Id: main.cc,v 1.310 2000/05/03 17:15:42 adrian Exp $ * * DEBUG: section 1 Startup and Main Loop * AUTHOR: Harvest Derived @@ -101,6 +101,7 @@ usage(void) " -F Don't serve any requests until store is rebuilt.\n" " -N No daemon mode.\n" " -R Do not set REUSEADDR on port.\n" + " -S Double-check swap during rebuild.\n" " -V Virtual host httpd-accelerator.\n" " -X Force full debugging.\n" " -Y Only return UDP_HIT or UDP_MISS_NOFETCH during fast reload.\n", @@ -493,9 +494,6 @@ mainInitialize(void) #endif if (!configured_once) { -#if USE_ASYNC_IO - aioInit(); -#endif unlinkdInit(); urlInitialize(); cachemgrInit(); @@ -618,6 +616,7 @@ main(int argc, char **argv) #endif memInit(); /* memInit is required for config parsing */ eventInit(); /* eventInit() is required for config parsing */ + storeFsInit(); /* required for config parsing */ parse_err = parseConfigFile(ConfigFile); if (opt_parse_cfg_only) @@ -907,21 +906,16 @@ SquidShutdown(void *unused) releaseServerSockets(); commCloseAllSockets(); unlinkdClose(); -#if USE_ASYNC_IO - aioSync(); /* flush pending object writes / unlinks */ -#endif + storeDirSync(); /* Flush pending object writes/unlinks */ storeDirWriteCleanLogs(0); PrintRusage(); dumpMallocStats(); -#if USE_ASYNC_IO - aioSync(); /* flush log writes */ -#endif + storeDirSync(); /* Flush log writes */ storeLogClose(); accessLogClose(); -#if USE_ASYNC_IO - aioSync(); /* flush log close */ -#endif + storeDirSync(); /* Flush log close */ #if PURIFY || XMALLOC_TRACE + storeFsDone(); configFreeMemory(); storeFreeMemory(); /*stmemFreeMemory(); */ diff --git a/src/mem.cc b/src/mem.cc index 051c9b6f51..8827758ae2 100644 --- a/src/mem.cc +++ b/src/mem.cc @@ -1,6 +1,6 @@ /* - * $Id: mem.cc,v 1.44 2000/05/02 20:58:30 hno Exp $ + * $Id: mem.cc,v 1.45 2000/05/03 17:15:42 adrian Exp $ * * DEBUG: section 13 High Level Memory Pool Management * AUTHOR: Harvest Derived @@ -203,7 +203,6 @@ memInit(void) memDataInit(MEM_ACL_TIME_DATA, "acl_time_data", sizeof(acl_time_data), 0); memDataInit(MEM_ACL_PROXY_AUTH_USER, "acl_proxy_auth_user", sizeof(acl_proxy_auth_user), 0); - memDataInit(MEM_AIO_RESULT_T, "aio_result_t", sizeof(aio_result_t), 0); memDataInit(MEM_CACHEMGR_PASSWD, "cachemgr_passwd", sizeof(cachemgr_passwd), 0); #if USE_CACHE_DIGESTS @@ -218,7 +217,6 @@ memInit(void) #if USE_CACHE_DIGESTS memDataInit(MEM_DIGEST_FETCH_STATE, "DigestFetchState", sizeof(DigestFetchState), 0); #endif - memDataInit(MEM_DISK_BUF, "Disk I/O Buffer", DISK_PAGE_SIZE, 200); memDataInit(MEM_DLINK_LIST, "dlink_list", sizeof(dlink_list), 10); memDataInit(MEM_DLINK_NODE, "dlink_node", sizeof(dlink_node), 10); memDataInit(MEM_DNSSERVER_T, "dnsserver_t", sizeof(dnsserver_t), 0); @@ -352,8 +350,3 @@ memFree8K(void *p) memFree(p, MEM_8K_BUF); } -void -memFreeDISK(void *p) -{ - memFree(p, MEM_DISK_BUF); -} diff --git a/src/protos.h b/src/protos.h index 6573259746..9d06e61fd4 100644 --- a/src/protos.h +++ b/src/protos.h @@ -1,6 +1,6 @@ /* - * $Id: protos.h,v 1.362 2000/05/02 20:41:23 hno Exp $ + * $Id: protos.h,v 1.363 2000/05/03 17:15:42 adrian Exp $ * * * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ @@ -68,34 +68,6 @@ extern const char *aclTypeToStr(squid_acl); extern wordlist *aclDumpGeneric(const acl *); extern int aclPurgeMethodInUse(acl_access *); -#if USE_ASYNC_IO -extern int aio_cancel(aio_result_t *); -extern int aio_open(const char *, int, mode_t, aio_result_t *); -extern int aio_read(int, char *, int, off_t, int, aio_result_t *); -extern int aio_write(int, char *, int, off_t, int, aio_result_t *); -extern int aio_close(int, aio_result_t *); -extern int aio_stat(const char *, struct stat *, aio_result_t *); -extern int aio_unlink(const char *, aio_result_t *); -extern int aio_opendir(const char *, aio_result_t *); -extern aio_result_t *aio_poll_done(void); -extern int aio_operations_pending(void); -extern int aio_overloaded(void); -extern int aio_sync(void); -extern int aio_get_queue_len(void); - -extern void aioInit(void); -extern void aioCancel(int); -extern void aioOpen(const char *, int, mode_t, AIOCB *, void *); -extern void aioClose(int); -extern void aioWrite(int, int offset, char *, int size, AIOCB *, void *, FREE *); -extern void aioRead(int, int offset, char *, int size, AIOCB *, void *); -extern void aioStat(char *, struct stat *, AIOCB *, void *); -extern void aioUnlink(const char *, AIOCB *, void *); -extern void aioCheckCallbacks(void); -extern void aioSync(void); -extern int aioQueueSize(void); -#endif - /* * cache_cf.c */ @@ -190,6 +162,7 @@ extern int comm_select(int); #endif extern void commUpdateReadBits(int, PF *); extern void commUpdateWriteBits(int, PF *); +extern void comm_quick_poll_required(); extern void packerToStoreInit(Packer * p, StoreEntry * e); extern void packerToMemInit(Packer * p, MemBuf * mb); @@ -219,7 +192,7 @@ extern void xassert(const char *, const char *, int); /* packs, then prints an object using debug() */ extern void debugObj(int section, int level, const char *label, void *obj, ObjPackMethod pm); - +/* disk.c */ extern int file_open(const char *path, int mode); extern void file_close(int fd); extern void file_write(int, off_t, void *, int len, DWCB *, void *, FREE *); @@ -227,6 +200,19 @@ extern void file_write_mbuf(int fd, off_t, MemBuf mb, DWCB * handler, void *hand extern void file_read(int, char *, int, off_t, DRCB *, void *); extern void disk_init(void); +/* diskd.c */ +extern diskd_queue * afile_create_queue(void); +extern void afile_destroy_queue(diskd_queue *); +extern void afile_sync_queue(diskd_queue *); +extern void afile_sync(void); +extern void afile_open(const char *path, int mode, DOCB *, void *); +extern void afile_close(int fd, DCCB *callback, void *data); +extern void afile_write(int, off_t, void *, int len, DWCB *, void *, FREE *); +extern void afile_write_mbuf(int fd, off_t, MemBuf, DWCB *, void *); +extern void afile_read(int, char *, int, off_t, DRCB *, void *); +extern void afile_unlink(const char *path, DUCB *, void *); +extern void afile_truncate(const char *path, DTCB *, void *); + extern void dnsShutdown(void); extern void dnsInit(void); extern void dnsSubmit(const char *lookup, HLPCB * callback, void *data); @@ -740,7 +726,7 @@ extern double statRequestHitRatio(int minutes); extern double statRequestHitMemoryRatio(int minutes); extern double statRequestHitDiskRatio(int minutes); extern double statByteHitRatio(int minutes); - +extern int storeEntryLocked(const StoreEntry *); /* StatHist */ @@ -778,7 +764,6 @@ extern void memFreeBuf(size_t size, void *); extern void memFree2K(void *); extern void memFree4K(void *); extern void memFree8K(void *); -extern void memFreeDISK(void *); extern int memInUse(mem_type); extern size_t memTotalAllocated(void); extern void memDataInit(mem_type, const char *, size_t, int); @@ -843,9 +828,6 @@ extern int storeClientCopyPending(StoreEntry *, void *); extern void InvokeHandlers(StoreEntry *); extern int storeEntryValidToSend(StoreEntry *); extern void storeTimestampsSet(StoreEntry *); -#if !HEAP_REPLACEMENT -extern time_t storeExpiredReferenceAge(void); -#endif extern void storeRegisterAbort(StoreEntry * e, STABH * cb, void *); extern void storeUnregisterAbort(StoreEntry * e); extern void storeMemObjectDump(MemObject * mem); @@ -870,50 +852,31 @@ extern int contentLen(const StoreEntry * e); extern HttpReply *storeEntryReply(StoreEntry *); extern int storeTooManyDiskFilesOpen(void); extern void storeEntryReset(StoreEntry *); -extern void storeHeapPositionUpdate(StoreEntry *); +extern void storeHeapPositionUpdate(StoreEntry *, SwapDir *); extern void storeSwapFileNumberSet(StoreEntry * e, sfileno filn); +extern void storeFsInit(void); +extern void storeFsDone(void); +extern void storeFsAdd(char *, STSETUP *); + +/* store_heap_replacement.c */ +#ifdef HEAP_REPLACEMENT +extern heap_key HeapKeyGen_StoreEntry_LFUDA(void *, double); +extern heap_key HeapKeyGen_StoreEntry_GDSF(void *, double); +extern heap_key HeapKeyGen_StoreEntry_LRU(void *, double); +#endif + +/* store_modules.c */ +extern void storeFsSetup(void); /* store_io.c */ -extern STOBJOPEN storeOpen; -extern STOBJCLOSE storeClose; -extern STOBJREAD storeRead; -extern STOBJWRITE storeWrite; -extern STOBJUNLINK storeUnlink; +extern storeIOState * storeCreate(StoreEntry *, STFNCB *, STIOCB *, void *); +extern storeIOState * storeOpen(StoreEntry *, STFNCB *, STIOCB *, void *); +extern void storeClose(storeIOState *); +extern void storeRead(storeIOState *, char *, size_t, off_t, STRCB *, void *); +extern void storeWrite(storeIOState *, char *, size_t, off_t, FREE *); +extern void storeUnlink(StoreEntry *); extern off_t storeOffset(storeIOState *); -/* - * store_io_ufs.c - */ -extern storeIOState *storeUfsOpen(sfileno, mode_t, STIOCB *, void *); -extern void storeUfsClose(storeIOState * sio); -extern void storeUfsRead(storeIOState *, char *, size_t, off_t, STRCB *, void *); -extern void storeUfsWrite(storeIOState *, char *, size_t, off_t, FREE *); -extern void storeUfsUnlink(int fileno); - -#if USE_ASYNC_IO -/* - * store_io_ufs.c - */ -extern storeIOState *storeAufsOpen(sfileno, mode_t, STIOCB *, void *); -extern void storeAufsClose(storeIOState * sio); -extern void storeAufsRead(storeIOState *, char *, size_t, off_t, STRCB *, void *); -extern void storeAufsWrite(storeIOState *, char *, size_t, off_t, FREE *); -extern void storeAufsUnlink(int fileno); -#endif - -#if USE_DISKD -/* - * diskd.c - */ -extern storeIOState *storeDiskdOpen(sfileno, mode_t, STIOCB *, void *); -extern void storeDiskdClose(storeIOState * sio); -extern void storeDiskdRead(storeIOState *, char *, size_t, off_t, STRCB *, void *); -extern void storeDiskdWrite(storeIOState *, char *, size_t, off_t, FREE *); -extern void storeDiskdUnlink(int fileno); -extern STINIT storeDiskdInit; -extern void storeDiskdReadQueue(void); -#endif - /* * store_log.c */ @@ -957,45 +920,23 @@ extern char *storeSwapDir(int); extern char *storeSwapFullPath(int, char *); extern char *storeSwapSubSubDir(int, char *); extern const char *storeSwapPath(int); -extern int storeDirMapAllocate(void); -extern int storeDirMapBitTest(int fn); -extern int storeDirMapBitsInUse(void); -extern int storeDirNumber(int fileno); -extern int storeDirProperFileno(int dirn, int fn); -extern int storeDirValidFileno(int fn, int); extern int storeDirWriteCleanLogs(int reopen); +extern int storeDirSelectSwapDir(const StoreEntry *); extern int storeVerifySwapDirs(void); extern void storeCreateSwapDirectories(void); extern void storeDirCloseSwapLogs(void); extern void storeDirCloseTmpSwapLog(int dirn); extern void storeDirConfigure(void); -extern void storeDirDiskFull(int fn); +extern void storeDirDiskFull(sdirno); extern void storeDirInit(void); -extern void storeDirMapBitReset(int fn); -extern void storeDirMapBitSet(int fn); extern void storeDirOpenSwapLogs(void); extern void storeDirSwapLog(const StoreEntry *, int op); -extern void storeDirUpdateSwapSize(int fn, size_t size, int sign); +extern void storeDirUpdateSwapSize(SwapDir *, size_t size, int sign); +extern void storeDirSync(void); +extern void storeDirCallback(void); extern void storeDirLRUDelete(StoreEntry *); extern void storeDirLRUAdd(StoreEntry *); -/* - * store_dir_ufs.c - */ -extern OBJH storeUfsDirStats; -extern void storeUfsDirParse(cacheSwap * swap); -extern void storeUfsDirDump(StoreEntry * entry, const char *name, SwapDir * s); -extern void storeUfsDirFree(SwapDir *); -extern char *storeUfsFullPath(sfileno fn, char *fullpath); -extern STINIT storeUfsDirInit; -#if USE_ASYNC_IO -extern void storeAufsDirParse(cacheSwap * swap); -#endif -#if USE_DISKD -extern void storeDiskdDirParse(cacheSwap *); -#endif - - /* * store_swapmeta.c */ diff --git a/src/snmp_agent.cc b/src/snmp_agent.cc index a630752926..b135e36cf5 100644 --- a/src/snmp_agent.cc +++ b/src/snmp_agent.cc @@ -1,6 +1,6 @@ /* - * $Id: snmp_agent.cc,v 1.73 2000/03/06 16:23:34 wessels Exp $ + * $Id: snmp_agent.cc,v 1.74 2000/05/03 17:15:42 adrian Exp $ * * DEBUG: section 49 SNMP Interface * AUTHOR: Kostas Anagnostakis @@ -279,12 +279,9 @@ snmp_prfSysFn(variable_list * Var, snint * ErrP) ASN_INTEGER); break; case PERF_SYS_CURLRUEXP: + /* No global LRU info anymore */ Answer = snmp_var_new_integer(Var->name, Var->name_length, -#if !HEAP_REPLACEMENT - (snint) (storeExpiredReferenceAge() * 100), -#else - 0, -#endif + 0, SMI_TIMETICKS); break; case PERF_SYS_CURUNLREQ: diff --git a/src/squid.h b/src/squid.h index 85c9c04c0c..ee3c6fc752 100644 --- a/src/squid.h +++ b/src/squid.h @@ -1,6 +1,6 @@ /* - * $Id: squid.h,v 1.200 2000/05/02 20:58:30 hno Exp $ + * $Id: squid.h,v 1.201 2000/05/03 17:15:42 adrian Exp $ * * AUTHOR: Duane Wessels * @@ -420,7 +420,13 @@ struct rusage { #define SQUID_NONBLOCK O_NDELAY #endif -#define SWAP_DIR_SHIFT 24 -#define SWAP_FILE_MASK 0x00FFFFFF +#include +#include + +/* + * I'm sick of having to keep doing this .. + */ + +#define INDEXSD(i) (&Config.cacheSwap.swapDirs[(i)]) #endif /* SQUID_H */ diff --git a/src/stat.cc b/src/stat.cc index 9637c962f2..ce61d332e4 100644 --- a/src/stat.cc +++ b/src/stat.cc @@ -1,6 +1,6 @@ /* - * $Id: stat.cc,v 1.324 2000/03/06 16:23:34 wessels Exp $ + * $Id: stat.cc,v 1.325 2000/05/03 17:15:42 adrian Exp $ * * DEBUG: section 18 Cache Manager Statistics * AUTHOR: Harvest Derived @@ -264,8 +264,8 @@ statStoreEntry(StoreEntry * s, StoreEntry * e) (int) e->lock_count, storePendingNClients(e), (int) e->refcount); - storeAppendPrintf(s, "\tSwap File %#08X\n", - e->swap_file_number); + storeAppendPrintf(s, "\tSwap Dir %d, File %#08X\n", + e->swap_dirn, e->swap_filen); if (mem != NULL) { storeAppendPrintf(s, "\tinmem_lo: %d\n", (int) mem->inmem_lo); storeAppendPrintf(s, "\tinmem_hi: %d\n", (int) mem->inmem_hi); @@ -495,13 +495,6 @@ info_get(StoreEntry * sentry) store_swap_size); storeAppendPrintf(sentry, "\tStorage Mem size:\t%d KB\n", (int) (store_mem_size >> 10)); -#if HEAP_REPLACEMENT - storeAppendPrintf(sentry, "\tStorage Replacement Threshold:\t%f\n", - heap_peepminkey(store_heap)); -#else - storeAppendPrintf(sentry, "\tStorage LRU Expiration Age:\t%6.2f days\n", - (double) storeExpiredReferenceAge() / 86400.0); -#endif storeAppendPrintf(sentry, "\tMean Object Size:\t%0.2f KB\n", n_disk_objects ? (double) store_swap_size / n_disk_objects : 0.0); storeAppendPrintf(sentry, "\tRequests given to unlinkd:\t%d\n", @@ -616,8 +609,6 @@ info_get(StoreEntry * sentry) memInUse(MEM_MEMOBJECT)); storeAppendPrintf(sentry, "\t%6d Hot Object Cache Items\n", hot_obj_count); - storeAppendPrintf(sentry, "\t%6d Filemap bits set\n", - storeDirMapBitsInUse()); storeAppendPrintf(sentry, "\t%6d on-disk objects\n", n_disk_objects); diff --git a/src/store.cc b/src/store.cc index f709d21351..1e8713a266 100644 --- a/src/store.cc +++ b/src/store.cc @@ -1,6 +1,6 @@ /* - * $Id: store.cc,v 1.519 2000/05/02 21:07:36 hno Exp $ + * $Id: store.cc,v 1.520 2000/05/03 17:15:42 adrian Exp $ * * DEBUG: section 20 Storage Manager * AUTHOR: Harvest Derived @@ -74,8 +74,6 @@ typedef struct lock_ctrl_t { /* * local function prototypes */ -static int storeCheckExpired(const StoreEntry *); -static int storeEntryLocked(const StoreEntry *); static int storeEntryValidLength(const StoreEntry *); static void storeGetMemSpace(int); static void storeHashDelete(StoreEntry *); @@ -87,11 +85,6 @@ static int getKeyCounter(void); static int storeKeepInMemory(const StoreEntry *); static OBJH storeCheckCachableStats; static EVH storeLateRelease; -#if HEAP_REPLACEMENT -static heap_key_func HeapKeyGen_StoreEntry_LFUDA; -static heap_key_func HeapKeyGen_StoreEntry_GDSF; -static heap_key_func HeapKeyGen_StoreEntry_LRU; -#endif /* * local variables @@ -104,9 +97,9 @@ static heap_key_func HeapKeyGen_StoreEntry_LRU; #else static dlink_list inmem_list; #endif -static int store_pages_max = 0; -static int store_swap_high = 0; -static int store_swap_low = 0; +int store_pages_max = 0; +int store_swap_high = 0; +int store_swap_low = 0; static Stack LateReleaseStack; #if URL_CHECKSUM_DEBUG @@ -150,7 +143,8 @@ new_StoreEntry(int mem_obj_flag, const char *url, const char *log_url) e->mem_obj = new_MemObject(url, log_url); debug(20, 3) ("new_StoreEntry: returning %p\n", e); e->expires = e->lastmod = e->lastref = e->timestamp = -1; - e->swap_file_number = -1; + e->swap_filen = -1; + e->swap_dirn = -1; return e; } @@ -204,27 +198,12 @@ storeHashInsert(StoreEntry * e, const cache_key * key) e, storeKeyText(key)); e->key = storeKeyDup(key); hash_join(store_table, (hash_link *) e); -#if HEAP_REPLACEMENT - if (EBIT_TEST(e->flags, ENTRY_SPECIAL)) { - (void) 0; - } else { - e->node = heap_insert(store_heap, e); - debug(20, 4) ("storeHashInsert: inserted node %p\n", e->node); - } -#endif } static void storeHashDelete(StoreEntry * e) { hash_remove_link(store_table, (hash_link *) e); -#if HEAP_REPLACEMENT - if (e->node) { - debug(20, 4) ("storeHashDelete: deleting node %p\n", e->node); - heap_delete(store_heap, e->node); - e->node = NULL; - } -#endif storeKeyFree(e->key); e->key = NULL; } @@ -250,24 +229,20 @@ storePurgeMem(StoreEntry * e) void storeLockObject(StoreEntry * e) { - if (e->lock_count++ == 0) { -#if HEAP_REPLACEMENT - /* - * There is no reason to take any action here. Squid by - * default is moving locked objects to the end of the LRU - * list to keep them from getting bumped into by the - * replacement algorithm. We can't do that so we will just - * have to handle them. - */ - debug(20, 4) ("storeLockObject: just locked node %p\n", e->node); -#else - storeDirLRUDelete(e); - storeDirLRUAdd(e); -#endif - } + SwapDir *SD; + + if (e->swap_dirn > -1) + SD = INDEXSD(e->swap_dirn); + else + SD = NULL; + + e->lock_count++; debug(20, 3) ("storeLockObject: key '%s' count=%d\n", storeKeyText(e->key), (int) e->lock_count); e->lastref = squid_curtime; + /* Notify the fs that we're referencing this object again */ + if (SD != NULL && SD->refobj != NULL) + SD->refobj(SD, e); } void @@ -291,6 +266,7 @@ storeReleaseRequest(StoreEntry * e) int storeUnlockObject(StoreEntry * e) { + SwapDir *SD; e->lock_count--; debug(20, 3) ("storeUnlockObject: key '%s' count=%d\n", storeKeyText(e->key), e->lock_count); @@ -299,11 +275,21 @@ storeUnlockObject(StoreEntry * e) if (e->store_status == STORE_PENDING) EBIT_SET(e->flags, RELEASE_REQUEST); assert(storePendingNClients(e) == 0); + /* Notify the fs that we're not referencing this object any more */ + if (e->swap_filen > -1) + SD = INDEXSD(e->swap_dirn); + else + SD = NULL; + if (SD != NULL && SD->unrefobj != NULL) + SD->unrefobj(SD, e); #if HEAP_REPLACEMENT - storeHeapPositionUpdate(e); + storeHeapPositionUpdate(e, SD); #else +#if 0 + /* Note: From 2.4. Not sure how this relates to the unrefobj() call above */ storeDirLRUDelete(e); storeDirLRUAdd(e); +#endif #endif if (EBIT_TEST(e->flags, RELEASE_REQUEST)) storeRelease(e); @@ -351,7 +337,7 @@ storeSetPrivateKey(StoreEntry * e) if (e->key && EBIT_TEST(e->flags, KEY_PRIVATE)) return; /* is already private */ if (e->key) { - if (e->swap_file_number > -1) + if (e->swap_filen > -1) storeDirSwapLog(e, SWAP_LOG_DEL); storeHashDelete(e); } @@ -402,7 +388,7 @@ storeSetPublicKey(StoreEntry * e) storeHashDelete(e); EBIT_CLR(e->flags, KEY_PRIVATE); storeHashInsert(e, newkey); - if (e->swap_file_number > -1) + if (e->swap_filen > -1) storeDirSwapLog(e, SWAP_LOG_ADD); } @@ -431,7 +417,8 @@ storeCreateEntry(const char *url, const char *log_url, request_flags flags, meth e->store_status = STORE_PENDING; storeSetMemStatus(e, NOT_IN_MEMORY); e->swap_status = SWAPOUT_NONE; - e->swap_file_number = -1; + e->swap_filen = -1; + e->swap_dirn = -1; e->refcount = 0; e->lastref = squid_curtime; e->timestamp = 0; /* set in storeTimestampsSet() */ @@ -676,11 +663,8 @@ storeAbort(StoreEntry * e) } /* Notify the client side */ InvokeHandlers(e); - /* Do we need to close the swapout file? */ - /* Not if we never started swapping out */ - if (e->swap_file_number > -1) { - storeSwapOutFileClose(e); - } + /* Close any swapout file */ + storeSwapOutFileClose(e); storeUnlockObject(e); /* unlock */ } @@ -779,173 +763,23 @@ storeGetMemSpace(int size) void storeMaintainSwapSpace(void *datanotused) { - StoreEntry *e = NULL; - int scanned = 0; - int locked = 0; - int expired = 0; - int max_scan; - int max_remove; int i; - int j; - static int ndir = 0; - double f; - static time_t last_warn_time = 0; -#if !HEAP_REPLACEMENT - SwapDir *sd; -#else - heap_key age; - heap_key min_age = 0.0; - link_list *locked_entries = NULL; -#if HEAP_REPLACEMENT_DEBUG - if (!verify_heap_property(store_heap)) { - debug(20, 1) ("Heap property violated!\n"); - } -#endif -#endif - /* We can't delete objects while rebuilding swap */ - if (store_dirs_rebuilding) { - eventAdd("MaintainSwapSpace", storeMaintainSwapSpace, NULL, 1.0, 1); - return; - } else { - f = (double) (store_swap_size - store_swap_low) / (store_swap_high - store_swap_low); - f = f < 0.0 ? 0.0 : f > 1.0 ? 1.0 : f; - max_scan = (int) (f * 400.0 + 100.0); - if ((max_remove = stat5minClientRequests()) < 10) - max_remove = 10; - eventAdd("MaintainSwapSpace", storeMaintainSwapSpace, NULL, 1.0 - f, 1); - } - debug(20, 3) ("storeMaintainSwapSpace: f=%f, max_scan=%d, max_remove=%d\n", - f, max_scan, max_remove); -#if HEAP_REPLACEMENT - while (heap_nodes(store_heap) > 0) { - if (store_swap_size < store_swap_low) - break; - if (expired >= max_remove) - break; - if (scanned >= max_scan) - break; - age = heap_peepminkey(store_heap); - e = heap_extractmin(store_heap); - e->node = NULL; /* no longer in the heap */ - scanned++; - if (storeEntryLocked(e)) { - /* - * Entry is in use ... put it in a linked list to ignore it. - */ - if (!EBIT_TEST(e->flags, ENTRY_SPECIAL)) { - /* - * If this was a "SPECIAL" do not add it back into the heap. - * It will always be "SPECIAL" and therefore never removed. - */ - debug(20, 4) ("storeMaintainSwapSpace: locked url %s\n", - (e->mem_obj && e->mem_obj->url) ? e->mem_obj->url : storeKeyText(e->key)); - linklistPush(&locked_entries, e); - } - locked++; - continue; - } else if (storeCheckExpired(e)) { - /* - * Note: This will not check the reference age ifdef - * HEAP_REPLACEMENT, but it does some other useful - * checks... - */ - expired++; - debug(20, 3) ("Released store object age %f size %d refs %d key %s\n", - age, e->swap_file_sz, e->refcount, storeKeyText(e->key)); - min_age = age; - storeRelease(e); - } else { - /* - * Did not expire the object so we need to add it back - * into the heap! - */ - debug(20, 5) ("storeMaintainSwapSpace: non-expired %s\n", - storeKeyText(e->key)); - linklistPush(&locked_entries, e); - continue; - } - if (store_swap_size < store_swap_low) - break; - else if (expired >= max_remove) - break; - else if (scanned >= max_scan) - break; - } - /* - * Bump the heap age factor. - */ - if (min_age > 0.0) - store_heap->age = min_age; - /* - * Reinsert all bumped locked entries back into heap... - */ - while ((e = linklistShift(&locked_entries))) - e->node = heap_insert(store_heap, e); -#else + SwapDir *SD; + + /* walk each fs */ for (i = 0; i < Config.cacheSwap.n_configured; i++) { - sd = &Config.cacheSwap.swapDirs[i]; - sd->lru_walker = sd->lru_list.tail; + /* call the maintain function .. */ + SD = INDEXSD(i); + if (SD->maintainfs != NULL) + SD->maintainfs(SD); } - do { - j = 0; - for (i = 0; i < Config.cacheSwap.n_configured; i++) { - if (ndir >= Config.cacheSwap.n_configured) - ndir = ndir % Config.cacheSwap.n_configured; - sd = &Config.cacheSwap.swapDirs[ndir++]; - if (sd->cur_size < sd->low_size) - continue; - if (NULL == sd->lru_walker) - continue; - e = sd->lru_walker->data; - sd->lru_walker = sd->lru_walker->prev; - j++; - scanned++; - sd->scanned++; - if (storeEntryLocked(e)) { - /* - * If there is a locked entry at the tail of the LRU list, - * move it to the beginning to get it out of the way. - * Theoretically, we might have all locked objects at the - * tail, and then we'll never remove anything here and the - * LRU age will go to zero. - */ - if (memInUse(MEM_STOREENTRY) > max_scan) { - storeDirLRUDelete(e); - if (!EBIT_TEST(e->flags, ENTRY_SPECIAL)) - storeDirLRUAdd(e); - } - locked++; - } else if (storeCheckExpired(e)) { - expired++; - sd->removals++; - storeRelease(e); - } - if (expired >= max_remove) - break; - if (scanned >= max_scan) - break; - } - } while (j > 0 && expired < max_remove && scanned < max_scan); -#endif - debug(20, (expired ? 2 : 3)) ("storeMaintainSwapSpace: scanned %d/%d removed %d/%d locked %d f=%.03f\n", - scanned, max_scan, expired, max_remove, locked, f); - debug(20, 3) ("storeMaintainSwapSpace stats:\n"); - debug(20, 3) (" %6d objects\n", memInUse(MEM_STOREENTRY)); - debug(20, 3) (" %6d were scanned\n", scanned); - debug(20, 3) (" %6d were locked\n", locked); - debug(20, 3) (" %6d were expired\n", expired); - if (store_swap_size < Config.Swap.maxSize) - return; - if (squid_curtime - last_warn_time < 10) - return; - debug(20, 0) ("WARNING: Disk space over limit: %d KB > %d KB\n", - store_swap_size, Config.Swap.maxSize); - last_warn_time = squid_curtime; + + /* Reregister a maintain event .. */ + eventAdd("MaintainSwapSpace", storeMaintainSwapSpace, NULL, 1.0, 1); } /* release an object from a cache */ -/* return number of objects released. */ void storeRelease(StoreEntry * e) { @@ -958,13 +792,13 @@ storeRelease(StoreEntry * e) storeReleaseRequest(e); return; } - if (store_dirs_rebuilding && e->swap_file_number > -1) { + if (store_dirs_rebuilding && e->swap_filen > -1) { storeSetPrivateKey(e); if (e->mem_obj) { storeSetMemStatus(e, NOT_IN_MEMORY); destroy_MemObject(e); } - if (e->swap_file_number > -1) { + if (e->swap_filen > -1) { /* * Fake a call to storeLockObject(). When rebuilding is done, * we'll just call storeUnlockObject() on these. @@ -978,14 +812,17 @@ storeRelease(StoreEntry * e) } } storeLog(STORE_LOG_RELEASE, e); - if (e->swap_file_number > -1) { - storeUnlink(e->swap_file_number); + if (e->swap_filen > -1) { + storeUnlink(e); if (e->swap_status == SWAPOUT_DONE) if (EBIT_TEST(e->flags, ENTRY_VALIDATED)) - storeDirUpdateSwapSize(e->swap_file_number, e->swap_file_sz, -1); + storeDirUpdateSwapSize(&Config.cacheSwap.swapDirs[e->swap_dirn], e->swap_file_sz, -1); if (!EBIT_TEST(e->flags, KEY_PRIVATE)) storeDirSwapLog(e, SWAP_LOG_DEL); +#if 0 + /* From 2.4. I think we do this in storeUnlink? */ storeSwapFileNumberSet(e, -1); +#endif } storeSetMemStatus(e, NOT_IN_MEMORY); destroy_StoreEntry(e); @@ -1015,7 +852,7 @@ storeLateRelease(void *unused) } /* return 1 if a store entry is locked */ -static int +int storeEntryLocked(const StoreEntry * e) { if (e->lock_count) @@ -1094,10 +931,6 @@ storeInitHashValues(void) debug(20, 1) ("Max Swap size: %d KB\n", Config.Swap.maxSize); } -#if HEAP_REPLACEMENT -#include "store_heap_replacement.c" -#endif - void storeInit(void) { @@ -1108,36 +941,7 @@ storeInit(void) storeDigestInit(); storeLogOpen(); #if HEAP_REPLACEMENT - /* - * Create new heaps with cache replacement policies attached to them. - * The cache replacement policy is specified as either GDSF or LFUDA in - * the squid.conf configuration file. Note that the replacement policy - * applies only to the disk replacement algorithm. Memory replacement - * always uses GDSF since we want to maximize object hit rate. - */ inmem_heap = new_heap(1000, HeapKeyGen_StoreEntry_GDSF); - if (Config.replPolicy) { - if (tolower(Config.replPolicy[0]) == 'g') { - debug(20, 1) ("Using GDSF disk replacement policy\n"); - store_heap = new_heap(10000, HeapKeyGen_StoreEntry_GDSF); - } else if (tolower(Config.replPolicy[0]) == 'l') { - if (tolower(Config.replPolicy[1]) == 'f') { - debug(20, 1) ("Using LFUDA disk replacement policy\n"); - store_heap = new_heap(10000, HeapKeyGen_StoreEntry_LFUDA); - } else if (tolower(Config.replPolicy[1]) == 'r') { - debug(20, 1) ("Using LRU heap disk replacement policy\n"); - store_heap = new_heap(10000, HeapKeyGen_StoreEntry_LRU); - } - } else { - debug(20, 1) ("Unrecognized replacement_policy; using GDSF\n"); - store_heap = new_heap(10000, HeapKeyGen_StoreEntry_GDSF); - } - } else { - debug(20, 1) ("Using default disk replacement policy (GDSF)\n"); - store_heap = new_heap(10000, HeapKeyGen_StoreEntry_GDSF); - } -#else - inmem_list.head = inmem_list.tail = NULL; #endif stackInit(&LateReleaseStack); eventAdd("storeLateRelease", storeLateRelease, NULL, 1.0, 1); @@ -1172,48 +976,6 @@ storeKeepInMemory(const StoreEntry * e) return mem->inmem_lo == 0; } -static int -storeCheckExpired(const StoreEntry * e) -{ - if (storeEntryLocked(e)) - return 0; - if (EBIT_TEST(e->flags, RELEASE_REQUEST)) - return 1; - if (EBIT_TEST(e->flags, ENTRY_NEGCACHED) && squid_curtime >= e->expires) - return 1; - return 1; -} - -#if !HEAP_REPLACEMENT -/* - * storeExpiredReferenceAge - * - * The LRU age is scaled exponentially between 1 minute and - * Config.referenceAge , when store_swap_low < store_swap_size < - * store_swap_high. This keeps store_swap_size within the low and high - * water marks. If the cache is very busy then store_swap_size stays - * closer to the low water mark, if it is not busy, then it will stay - * near the high water mark. The LRU age value can be examined on the - * cachemgr 'info' page. - */ -time_t -storeExpiredReferenceAge(void) -{ - double x; - double z; - time_t age; - x = (double) (store_swap_high - store_swap_size) / (store_swap_high - store_swap_low); - x = x < 0.0 ? 0.0 : x > 1.0 ? 1.0 : x; - z = pow((double) (Config.referenceAge / 60), x); - age = (time_t) (z * 60.0); - if (age < 60) - age = 60; - else if (age > 31536000) - age = 31536000; - return age; -} -#endif - void storeNegativeCache(StoreEntry * e) { @@ -1343,7 +1105,8 @@ storeEntryDump(const StoreEntry * e, int l) debug(20, l) ("StoreEntry->swap_file_sz: %d\n", (int) e->swap_file_sz); debug(20, l) ("StoreEntry->refcount: %d\n", e->refcount); debug(20, l) ("StoreEntry->flags: %s\n", storeEntryFlags(e)); - debug(20, l) ("StoreEntry->swap_file_number: %d\n", (int) e->swap_file_number); + debug(20, l) ("StoreEntry->swap_dirn: %d\n", (int) e->swap_dirn); + debug(20, l) ("StoreEntry->swap_filen: %d\n", (int) e->swap_filen); debug(20, l) ("StoreEntry->lock_count: %d\n", (int) e->lock_count); debug(20, l) ("StoreEntry->mem_status: %d\n", (int) e->mem_status); debug(20, l) ("StoreEntry->ping_status: %d\n", (int) e->ping_status); @@ -1471,16 +1234,65 @@ storeEntryReset(StoreEntry * e) } #if HEAP_REPLACEMENT +/* + * This routine only handles memory updates these days + */ void -storeHeapPositionUpdate(StoreEntry * e) +storeHeapPositionUpdate(StoreEntry * e, SwapDir * SD) { - if (e->node) - heap_update(store_heap, e->node, e); if (e->mem_obj && e->mem_obj->node) heap_update(inmem_heap, e->mem_obj->node, e); } #endif +/* + * storeFsInit + * + * This routine calls the SETUP routine for each fs type. + * I don't know where the best place for this is, and I'm not going to shuffle + * around large chunks of code right now (that can be done once its working.) + */ +void +storeFsInit(void) +{ + storeFsSetup(); +} + + +/* + * similar to above, but is called when a graceful shutdown is to occur + * of each fs module. + */ +void +storeFsDone(void) +{ + int i = 0; + + while (storefs_list[i].typestr != NULL) { + storefs_list[i].donefunc(); + i++; + } +} + +/* + * called to add another store fs module + */ +void storeFsAdd(char *type, STSETUP *setup) +{ + int i; + /* find the number of currently known storefs types */ + for (i = 0; storefs_list && storefs_list[i].typestr; i++) { + assert(strcmp(storefs_list[i].typestr, type)!=0); + } + /* add the new type */ + storefs_list = xrealloc(storefs_list, (i + 2) * sizeof(storefs_entry_t)); + memset(&storefs_list[i+1],0,sizeof(storefs_entry_t)); + storefs_list[i].typestr = type; + /* Call the FS to set up capabilities and initialize the FS driver */ + setup(&storefs_list[i]); +} + +#if 0 void storeSwapFileNumberSet(StoreEntry * e, sfileno filn) { @@ -1497,3 +1309,4 @@ storeSwapFileNumberSet(StoreEntry * e, sfileno filn) storeDirLRUAdd(e); } } +#endif diff --git a/src/store_client.cc b/src/store_client.cc index 700bfe2624..78a4598d5b 100644 --- a/src/store_client.cc +++ b/src/store_client.cc @@ -1,6 +1,6 @@ /* - * $Id: store_client.cc,v 1.86 2000/04/18 06:06:17 wessels Exp $ + * $Id: store_client.cc,v 1.87 2000/05/03 17:15:43 adrian Exp $ * * DEBUG: section 20 Storage Manager Client-Side Interface * AUTHOR: Duane Wessels @@ -138,7 +138,7 @@ storeClientListAdd(StoreEntry * e, void *data) if (sc->type == STORE_DISK_CLIENT) /* assert we'll be able to get the data we want */ /* maybe we should open swapin_fd here */ - assert(e->swap_file_number > -1 || storeSwapOutAble(e)); + assert(e->swap_filen > -1 || storeSwapOutAble(e)); for (T = &mem->clients; *T; T = &(*T)->next); *T = sc; #if DELAY_POOLS @@ -152,7 +152,6 @@ storeClientCallback(store_client * sc, ssize_t sz) STCB *callback = sc->callback; char *buf = sc->copy_buf; assert(sc->callback); - assert(sc->copy_buf); sc->callback = NULL; sc->copy_buf = NULL; if (cbdataValid(sc->callback_data)) @@ -259,6 +258,7 @@ storeClientCopy3(StoreEntry * e, store_client * sc) { MemObject *mem = e->mem_obj; size_t sz; + if (storeClientNoMoreToSend(e, sc)) { /* There is no more to send! */ storeClientCallback(sc, 0); @@ -307,10 +307,11 @@ storeClientCopy3(StoreEntry * e, store_client * sc) /* What the client wants is in memory */ debug(20, 3) ("storeClientCopy3: Copying from memory\n"); sz = stmemCopy(&mem->data_hdr, - sc->copy_offset, sc->copy_buf, sc->copy_size); - storeClientCallback(sc, sz); - return; + sc->copy_offset, sc->copy_buf, sc->copy_size); + storeClientCallback(sc, sz); + return; } + /* What the client wants is not in memory. Schedule a disk read */ assert(STORE_DISK_CLIENT == sc->type); assert(!sc->flags.disk_io_pending); debug(20, 3) ("storeClientCopy3: reading from STORE\n"); @@ -386,7 +387,7 @@ storeClientReadHeader(void *data, const char *buf, ssize_t len) return; } if (tlv_list == NULL) { - debug(20, 1) ("WRNING: failed to unpack meta data\n"); + debug(20, 1) ("WARNING: failed to unpack meta data\n"); storeClientCallback(sc, -1); return; } @@ -527,6 +528,8 @@ storeLowestMemReaderOffset(const StoreEntry * entry) nx = sc->next; if (sc->callback_data == NULL) /* open slot */ continue; + if (sc->type != STORE_MEM_CLIENT) + continue; if (sc->type == STORE_DISK_CLIENT) if (NULL != sc->swapin_sio) continue; diff --git a/src/store_dir.cc b/src/store_dir.cc index 37ce8bc92b..c6c92366ce 100644 --- a/src/store_dir.cc +++ b/src/store_dir.cc @@ -1,6 +1,6 @@ /* - * $Id: store_dir.cc,v 1.106 2000/03/09 04:50:13 wessels Exp $ + * $Id: store_dir.cc,v 1.107 2000/05/03 17:15:43 adrian Exp $ * * DEBUG: section 47 Store Directory Routines * AUTHOR: Duane Wessels @@ -35,15 +35,10 @@ #include "squid.h" +static int storeDirValidSwapDirSize(int, size_t); static void storeDirLRUWalkInitHead(SwapDir * sd); static void *storeDirLRUWalkNext(SwapDir * sd); -const char *SwapDirType[] = -{ - "ufs", - "!ERROR!" -}; - void storeDirInit(void) { @@ -78,6 +73,33 @@ storeCreateSwapDirectories(void) } while (pid > 0 || (pid < 0 && errno == EINTR)); } +/* + * Determine whether the given directory can handle this object + * size + * + * Note: if the object size is -1, then the only swapdirs that + * will return true here are ones that have max_obj_size = -1, + * ie any-sized-object swapdirs. This is a good thing. + */ +static int +storeDirValidSwapDirSize(int swapdir, size_t objsize) +{ + /* + * If the swapdir's max_obj_size is -1, then it definitely can + */ + if (Config.cacheSwap.swapDirs[swapdir].max_objsize == -1) + return 1; + /* + * Else, make sure that the max object size is larger than objsize + */ + if (Config.cacheSwap.swapDirs[swapdir].max_objsize > objsize) + return 1; + else + return 0; +} + + +#if UNUSED /* Squid-2..4.DEVEL3 code */ /* * This new selection scheme simply does round-robin on all SwapDirs. * A SwapDir is skipped if it is over the max_size (100%) limit. If @@ -141,154 +163,72 @@ storeDirSelectSwapDir(void) } #endif -#if OLD +#endif /* Squid-2.4.DEVEL3 code */ + /* - * This is Stew Forster's selection algorithm. - * Spread load across least 3/4 of the store directories + * Spread load across all of the store directories + * + * Note: We should modify this later on to prefer sticking objects + * in the *tightest fit* swapdir to conserve space, along with the + * actual swapdir usage. But for now, this hack will do while + * testing, so you should order your swapdirs in the config file + * from smallest maxobjsize to unlimited (-1) maxobjsize. + * + * We also have to choose nleast == nconf since we need to consider + * ALL swapdirs, regardless of state. Again, this is a hack while + * we sort out the real usefulness of this algorithm. */ -static int -storeDirSelectSwapDir(void) +int +storeDirSelectSwapDir(const StoreEntry *e) { - double least_used = 1.0; - double high = (double) Config.Swap.highWaterMark / 100.0; - double u; - int dirn; - int i, j; + size_t objsize; + size_t least_size; + size_t least_objsize; + int least_load = 1000; + int load; + int dirn = -1; + int i; SwapDir *SD; - static int nleast = 0; - static int nconf = 0; - static int *dirq = NULL; - static double *diru = NULL; - /* - * Handle simplest case of a single swap directory immediately - */ - if (Config.cacheSwap.n_configured == 1) - return 0; - /* - * Initialise dirq on the first call or on change of number of dirs - */ - if (nconf != Config.cacheSwap.n_configured) { - nconf = Config.cacheSwap.n_configured; - nleast = (nconf * 3) / 4; - safe_free(dirq); - dirq = (int *) xmalloc(sizeof(int) * nleast); - safe_free(diru); - diru = (double *) xmalloc(sizeof(double) * nconf); - for (j = 0; j < nleast; j++) - dirq[j] = -1; - } - /* - * Scan for a non-negative dirn in the dirq array and return that one - */ - dirn = -1; - for (j = 0; j < nleast; j++) { - dirn = dirq[j]; - if (dirn < 0) - continue; - dirq[j] = -1; - break; - } - /* - * If we found a valid dirn return it - */ - if (dirn >= 0) - return dirn; - /* - * Now for the real guts of the algorithm - building the dirq array - */ - for (i = 0; i < nconf; i++) { - diru[i] = 1.1; - SD = &Config.cacheSwap.swapDirs[i]; + + /* Calculate the object size */ + objsize = objectLen(e); + if (objsize != -1) + objsize += e->mem_obj->swap_hdr_sz; + /* Initial defaults */ + least_size = Config.cacheSwap.swapDirs[0].cur_size; + least_objsize = Config.cacheSwap.swapDirs[0].max_objsize; + for (i = 0; i < Config.cacheSwap.n_configured; i++) { + SD = &Config.cacheSwap.swapDirs[i]; SD->flags.selected = 0; - if (SD->flags.read_only) - continue; - u = (double) SD->cur_size / SD->max_size; - if (u > high) + if (SD->flags.read_only) + continue; + /* Valid for object size check */ + if (!storeDirValidSwapDirSize(i, objsize)) + continue; + load = SD->checkobj(SD, e); + if (load < 0) continue; - diru[i] = u; + if (SD->cur_size > SD->max_size) + continue; + if (load > least_load) + continue; + if ((least_objsize > 0) && (objsize > least_objsize)) + continue; + /* Only use leastsize if the load is equal */ + if ((load == least_load) && (SD->cur_size > least_size)) + continue; + least_load = load; + least_size = SD->cur_size; + dirn = i; } - for (j = 0; j < nleast; j++) { - dirq[j] = -1; - least_used = 1.0; - dirn = -1; - for (i = 0; i < nconf; i++) { - if (diru[i] < least_used) { - least_used = diru[i]; - dirn = i; - } - } - if (dirn < 0) - break; - dirq[j] = dirn; - diru[dirn] = 1.1; - /* set selected flag for debugging/cachemgr only */ - Config.cacheSwap.swapDirs[dirn].flags.selected = 1; - } - /* - * Setup default return of 0 if no least found - */ - if (dirq[0] < 0) - dirq[0] = 0; - dirn = dirq[0]; - dirq[0] = -1; - return dirn; -} -#endif -int -storeDirValidFileno(int fn, int flag) -{ - int dirn = fn >> SWAP_DIR_SHIFT; - int filn = fn & SWAP_FILE_MASK; - if (dirn > Config.cacheSwap.n_configured) - return 0; - if (dirn < 0) - return 0; - if (filn < 0) - return 0; - /* - * If flag is set it means out-of-range file number should - * be considered invalid. - */ - if (flag) - if (filn > Config.cacheSwap.swapDirs[dirn].map->max_n_files) - return 0; - return 1; -} - -int -storeDirMapBitTest(int fn) -{ - int dirn = fn >> SWAP_DIR_SHIFT; - int filn = fn & SWAP_FILE_MASK; - return file_map_bit_test(Config.cacheSwap.swapDirs[dirn].map, filn); -} + if (dirn >= 0) + Config.cacheSwap.swapDirs[dirn].flags.selected = 1; -void -storeDirMapBitSet(int fn) -{ - int dirn = fn >> SWAP_DIR_SHIFT; - int filn = fn & SWAP_FILE_MASK; - file_map_bit_set(Config.cacheSwap.swapDirs[dirn].map, filn); + return dirn; } -void -storeDirMapBitReset(int fn) -{ - int dirn = fn >> SWAP_DIR_SHIFT; - int filn = fn & SWAP_FILE_MASK; - file_map_bit_reset(Config.cacheSwap.swapDirs[dirn].map, filn); -} -int -storeDirMapAllocate(void) -{ - int dirn = storeDirSelectSwapDir(); - SwapDir *SD = &Config.cacheSwap.swapDirs[dirn]; - int filn = file_map_allocate(SD->map, SD->suggest); - SD->suggest = filn + 1; - return (dirn << SWAP_DIR_SHIFT) | (filn & SWAP_FILE_MASK); -} char * storeSwapDir(int dirn) @@ -297,18 +237,6 @@ storeSwapDir(int dirn) return Config.cacheSwap.swapDirs[dirn].path; } -int -storeDirNumber(int swap_file_number) -{ - return swap_file_number >> SWAP_DIR_SHIFT; -} - -int -storeDirProperFileno(int dirn, int fn) -{ - return (dirn << SWAP_DIR_SHIFT) | (fn & SWAP_FILE_MASK); -} - /* * An entry written to the swap log MUST have the following * properties. @@ -316,36 +244,34 @@ storeDirProperFileno(int dirn, int fn) * a public ADD, change the key, then log a private * DEL. So we need to log a DEL before we change a * key from public to private. - * 2. It MUST have a valid (> -1) swap_file_number. + * 2. It MUST have a valid (> -1) swap_filen. */ void storeDirSwapLog(const StoreEntry * e, int op) { - int dirn = e->swap_file_number >> SWAP_DIR_SHIFT; SwapDir *sd; - assert(dirn < Config.cacheSwap.n_configured); assert(!EBIT_TEST(e->flags, KEY_PRIVATE)); - assert(e->swap_file_number >= 0); + assert(e->swap_filen >= 0); /* * icons and such; don't write them to the swap log */ if (EBIT_TEST(e->flags, ENTRY_SPECIAL)) return; assert(op > SWAP_LOG_NOP && op < SWAP_LOG_MAX); - debug(20, 3) ("storeDirSwapLog: %s %s %08X\n", + debug(20, 3) ("storeDirSwapLog: %s %s %d %08X\n", swap_log_op_str[op], storeKeyText(e->key), - e->swap_file_number); - sd = &Config.cacheSwap.swapDirs[dirn]; + e->swap_dirn, + e->swap_filen); + sd = &Config.cacheSwap.swapDirs[e->swap_dirn]; sd->log.write(sd, e, op); } void -storeDirUpdateSwapSize(int fn, size_t size, int sign) +storeDirUpdateSwapSize(SwapDir *SD, size_t size, int sign) { - int dirn = (fn >> SWAP_DIR_SHIFT) % Config.cacheSwap.n_configured; int k = ((size + 1023) >> 10) * sign; - Config.cacheSwap.swapDirs[dirn].cur_size += k; + SD->cur_size += k; store_swap_size += k; if (sign > 0) n_disk_objects++; @@ -356,6 +282,9 @@ storeDirUpdateSwapSize(int fn, size_t size, int sign) void storeDirStats(StoreEntry * sentry) { + int i; + SwapDir *SD; + storeAppendPrintf(sentry, "Store Directory Statistics:\n"); storeAppendPrintf(sentry, "Store Entries : %d\n", memInUse(MEM_STOREENTRY)); @@ -366,17 +295,15 @@ storeDirStats(StoreEntry * sentry) storeAppendPrintf(sentry, "Current Capacity : %d%% used, %d%% free\n", percent((int) store_swap_size, (int) Config.Swap.maxSize), percent((int) (Config.Swap.maxSize - store_swap_size), (int) Config.Swap.maxSize)); - storeUfsDirStats(sentry); /* XXX */ -} -int -storeDirMapBitsInUse(void) -{ - int i; - int n = 0; - for (i = 0; i < Config.cacheSwap.n_configured; i++) - n += Config.cacheSwap.swapDirs[i].map->n_files_in_map; - return n; + /* Now go through each swapdir, calling its statfs routine */ + for (i = 0; i < Config.cacheSwap.n_configured; i++) { + storeAppendPrintf(sentry, "\n"); + SD = &(Config.cacheSwap.swapDirs[i]); + storeAppendPrintf(sentry, "Store Directory #%d (%s): %s\n", i, SD->type, + storeSwapDir(i)); + SD->statfs(SD, sentry); + } } void @@ -388,17 +315,14 @@ storeDirConfigure(void) for (i = 0; i < Config.cacheSwap.n_configured; i++) { SD = &Config.cacheSwap.swapDirs[i];; Config.Swap.maxSize += SD->max_size; - if (NULL == SD->map) - SD->map = file_map_create(); SD->low_size = (int) (((float) SD->max_size * (float) Config.Swap.lowWaterMark) / 100.0); } } void -storeDirDiskFull(int fn) +storeDirDiskFull(sdirno dirn) { - int dirn = fn >> SWAP_DIR_SHIFT; SwapDir *SD = &Config.cacheSwap.swapDirs[dirn]; assert(0 <= dirn && dirn < Config.cacheSwap.n_configured); SD->max_size = SD->cur_size; @@ -432,6 +356,10 @@ storeDirCloseSwapLogs(void) * storeDirWriteCleanLogs * * Writes a "clean" swap log file from in-memory metadata. + * This is a rewrite of the original function to troll each + * StoreDir and write the logs, and flush at the end of + * the run. Thanks goes to Eric Stern, since this solution + * came out of his COSS code. */ #define CLEAN_BUF_SZ 16384 int @@ -445,8 +373,6 @@ storeDirWriteCleanLogs(int reopen) int dirn; #if HEAP_REPLACEMENT int node; -#else - int j; #endif if (store_dirs_rebuilding) { debug(20, 1) ("Not currently OK to rewrite swap log.\n"); @@ -462,58 +388,23 @@ storeDirWriteCleanLogs(int reopen) debug(20, 1) ("log.clean.open() failed for dir #%d\n", sd->index); continue; } -#if !HEAP_REPLACEMENT - storeDirLRUWalkInitHead(sd); -#endif - } -#if HEAP_REPLACEMENT - if (NULL == store_heap) - return 0; - for (node = 0; node < heap_nodes(store_heap); node++) { - e = (StoreEntry *) heap_peep(store_heap, node); - if (e->swap_file_number < 0) - continue; - if (e->swap_status != SWAPOUT_DONE) - continue; - if (e->swap_file_sz <= 0) - continue; - if (EBIT_TEST(e->flags, RELEASE_REQUEST)) - continue; - if (EBIT_TEST(e->flags, KEY_PRIVATE)) - continue; - if (EBIT_TEST(e->flags, ENTRY_SPECIAL)) - continue; - dirn = storeDirNumber(e->swap_file_number); - sd = &Config.cacheSwap.swapDirs[dirn]; - if (NULL == sd->log.clean.write) - continue; - sd->log.clean.write(e, sd); - if ((++n & 0xFFFF) == 0) { - getCurrentTime(); - debug(20, 1) (" %7d entries written so far.\n", n); - } - } - /* flush */ - for (dirn = 0; dirn < N; dirn++) { - sd = &Config.cacheSwap.swapDirs[dirn]; if (NULL == sd->log.clean.write) continue; - sd->log.clean.write(NULL, sd); - } +#if HEAP_REPLACEMENT + if (NULL == sd->repl.heap.heap) + continue; +#endif +#if HEAP_REPLACEMENT + for (node = 0; node < heap_nodes(sd->repl.heap.heap); node++) #else - do { - j = 0; - for (dirn = 0; dirn < Config.cacheSwap.n_configured; dirn++) { - sd = &Config.cacheSwap.swapDirs[dirn]; - if (NULL == sd->log.clean.write) - continue; - e = storeDirLRUWalkNext(sd); - if (NULL == e) { - sd->log.clean.write(NULL, sd); - continue; - } - j++; - if (e->swap_file_number < 0) + storeDirLRUWalkInitHead(sd); + while ((e = storeDirLRUWalkNext(sd)) != NULL) +#endif + { +#if HEAP_REPLACEMENT + e = (StoreEntry *) heap_peep(sd->repl.heap.heap, node); +#endif + if (e->swap_filen < 0) continue; if (e->swap_status != SWAPOUT_DONE) continue; @@ -531,8 +422,9 @@ storeDirWriteCleanLogs(int reopen) debug(20, 1) (" %7d entries written so far.\n", n); } } - } while (j > 0); -#endif + /* Flush */ + sd->log.clean.write(NULL, sd); + } if (reopen) storeDirOpenSwapLogs(); getCurrentTime(); @@ -544,14 +436,47 @@ storeDirWriteCleanLogs(int reopen) } #undef CLEAN_BUF_SZ +/* + * sync all avaliable fs'es .. + */ +void +storeDirSync(void) +{ + int i; + SwapDir *SD; + + for (i = 0; i < Config.cacheSwap.n_configured; i++) { + SD = &Config.cacheSwap.swapDirs[i]; + if (SD->sync != NULL) + SD->sync(SD); + } +} + +/* + * handle callbacks all avaliable fs'es .. + */ +void +storeDirCallback(void) +{ + int i; + SwapDir *SD; + + for (i = 0; i < Config.cacheSwap.n_configured; i++) { + SD = &Config.cacheSwap.swapDirs[i]; + if (SD->callback != NULL) + SD->callback(SD); + } +} + +#if 0 /* from Squid-2.4.DEVEL3 */ void storeDirLRUDelete(StoreEntry * e) { SwapDir *sd; - if (e->swap_file_number < 0) + if (e->swap_filen < 0) return; - sd = &Config.cacheSwap.swapDirs[e->swap_file_number >> SWAP_DIR_SHIFT]; - dlinkDelete(&e->lru, &sd->lru_list); + sd = &Config.cacheSwap.swapDirs[e->swap_dirn]; + dlinkDelete(&e->lru, &sd->repl.lru.list); } void @@ -560,23 +485,24 @@ storeDirLRUAdd(StoreEntry * e) SwapDir *sd; if (e->swap_file_number < 0) return; - sd = &Config.cacheSwap.swapDirs[e->swap_file_number >> SWAP_DIR_SHIFT]; - dlinkAdd(e, &e->lru, &sd->lru_list); + sd = &Config.cacheSwap.swapDirs[e->swap_dirn]; + dlinkAdd(e, &e->lru, &sd->repl.lru.list); } +#endif /* from Squid-2.4.DEVEL3 */ static void storeDirLRUWalkInitHead(SwapDir * sd) { - sd->lru_walker = sd->lru_list.head; + sd->repl.lru.walker = sd->repl.lru.list.head; } static void * storeDirLRUWalkNext(SwapDir * sd) { void *p; - if (NULL == sd->lru_walker) + if (NULL == sd->repl.lru.walker) return NULL; - p = sd->lru_walker->data; - sd->lru_walker = sd->lru_walker->next; + p = sd->repl.lru.walker->data; + sd->repl.lru.walker = sd->repl.lru.walker->next; return p; } diff --git a/src/store_io.cc b/src/store_io.cc index fcf6ae3696..a9d7396430 100644 --- a/src/store_io.cc +++ b/src/store_io.cc @@ -1,44 +1,83 @@ #include "squid.h" +/* + * submit a request to create a cache object for writing. + * The StoreEntry structure is sent as a hint to the filesystem + * to what will be stored in this object, to allow the filesystem + * to select different polices depending on object size or type. + */ +storeIOState * +storeCreate(StoreEntry *e, STIOCB *file_callback, STIOCB *close_callback, void *callback_data) +{ + size_t objsize; + sdirno dirn; + SwapDir *SD; + + /* This is just done for logging purposes */ + objsize = objectLen(e); + if (objsize != -1) + objsize += e->mem_obj->swap_hdr_sz; + + /* + * Pick the swapdir + * We assume that the header has been packed by now .. + */ + dirn = storeDirSelectSwapDir(e); + if (dirn == -1) { + debug(20, 2) ("storeCreate: no valid swapdirs for this object\n"); + return NULL; + } + debug (20, 2) ("storeCreate: Selected dir '%d' for obj size '%d'\n", dirn, objsize); + SD = &Config.cacheSwap.swapDirs[dirn]; + + /* Now that we have a fs to use, call its storeCreate function */ + return(SD->obj.create(SD, e, file_callback, close_callback, callback_data)); + + /* Done */ +} + +/* + * storeOpen() is purely for reading .. + */ storeIOState * -storeOpen(sfileno f, mode_t mode, STIOCB * callback, void *callback_data) +storeOpen(StoreEntry *e, STFNCB * file_callback, STIOCB * callback, + void *callback_data) { - SwapDir *SD = &Config.cacheSwap.swapDirs[f >> SWAP_DIR_SHIFT]; - assert(mode == O_RDONLY || mode == O_WRONLY); - return SD->obj.open(f, mode, callback, callback_data); + SwapDir *SD = &Config.cacheSwap.swapDirs[e->swap_dirn]; + return SD->obj.open(SD, e, file_callback, callback, callback_data); } void storeClose(storeIOState * sio) { - SwapDir *SD = &Config.cacheSwap.swapDirs[sio->swap_file_number >> SWAP_DIR_SHIFT]; + SwapDir *SD = &Config.cacheSwap.swapDirs[sio->swap_dirn]; if (sio->flags.closing) return; sio->flags.closing = 1; - SD->obj.close(sio); + SD->obj.close(SD, sio); } void storeRead(storeIOState * sio, char *buf, size_t size, off_t offset, STRCB * callback, void *callback_data) { - SwapDir *SD = &Config.cacheSwap.swapDirs[sio->swap_file_number >> SWAP_DIR_SHIFT]; - SD->obj.read(sio, buf, size, offset, callback, callback_data); + SwapDir *SD = &Config.cacheSwap.swapDirs[sio->swap_dirn]; + SD->obj.read(SD, sio, buf, size, offset, callback, callback_data); } void storeWrite(storeIOState * sio, char *buf, size_t size, off_t offset, FREE * free_func) { - SwapDir *SD = &Config.cacheSwap.swapDirs[sio->swap_file_number >> SWAP_DIR_SHIFT]; - SD->obj.write(sio, buf, size, offset, free_func); + SwapDir *SD = &Config.cacheSwap.swapDirs[sio->swap_dirn]; + SD->obj.write(SD, sio, buf, size, offset, free_func); } void -storeUnlink(sfileno f) +storeUnlink(StoreEntry *e) { - SwapDir *SD = &Config.cacheSwap.swapDirs[f >> SWAP_DIR_SHIFT]; - SD->obj.unlink(f); + SwapDir *SD = INDEXSD(e->swap_dirn); + SD->obj.unlink(SD, e); } off_t diff --git a/src/store_log.cc b/src/store_log.cc index 23855e613a..5f4de6803a 100644 --- a/src/store_log.cc +++ b/src/store_log.cc @@ -1,6 +1,6 @@ /* - * $Id: store_log.cc,v 1.12 2000/03/24 20:40:29 wessels Exp $ + * $Id: store_log.cc,v 1.13 2000/05/03 17:15:44 adrian Exp $ * * DEBUG: section 20 Storage Manager Logging Functions * AUTHOR: Duane Wessels @@ -62,11 +62,17 @@ storeLog(int tag, const StoreEntry * e) mem->log_url = xstrdup(mem->url); } reply = mem->reply; - logfilePrintf(storelog, "%9d.%03d %-7s %08X %4d %9d %9d %9d %s %d/%d %s %s\n", + /* + * XXX Ok, where should we print the dir number here? + * Because if we print it before the swap file number, it'll break + * the existing log format. + */ + logfilePrintf(storelog, "%9d.%03d %-7s %02d %08X %4d %9d %9d %9d %s %d/%d %s %s\n", (int) current_time.tv_sec, (int) current_time.tv_usec / 1000, storeLogTags[tag], - e->swap_file_number, + e->swap_dirn, + e->swap_filen, reply->sline.status, (int) reply->date, (int) reply->last_modified, diff --git a/src/store_rebuild.cc b/src/store_rebuild.cc index a8892cf612..5e41e60cad 100644 --- a/src/store_rebuild.cc +++ b/src/store_rebuild.cc @@ -1,6 +1,6 @@ /* - * $Id: store_rebuild.cc,v 1.69 2000/03/06 16:23:35 wessels Exp $ + * $Id: store_rebuild.cc,v 1.70 2000/05/03 17:15:44 adrian Exp $ * * DEBUG: section 20 Store Rebuild Routines * AUTHOR: Duane Wessels @@ -40,36 +40,10 @@ static struct timeval rebuild_start; static void storeCleanup(void *); static int -storeCleanupDoubleCheck(const StoreEntry * e) +storeCleanupDoubleCheck(StoreEntry * e) { - /* XXX too UFS specific */ - struct stat sb; - int dirn = e->swap_file_number >> SWAP_DIR_SHIFT; - if (Config.cacheSwap.swapDirs[dirn].type == SWAPDIR_UFS) - (void) 0; - if (Config.cacheSwap.swapDirs[dirn].type == SWAPDIR_ASYNCUFS) - (void) 0; - else - return 0; - if (stat(storeUfsFullPath(e->swap_file_number, NULL), &sb) < 0) { - debug(20, 0) ("storeCleanup: MISSING SWAP FILE\n"); - debug(20, 0) ("storeCleanup: FILENO %08X\n", e->swap_file_number); - debug(20, 0) ("storeCleanup: PATH %s\n", - storeUfsFullPath(e->swap_file_number, NULL)); - storeEntryDump(e, 0); - return -1; - } - if (e->swap_file_sz != sb.st_size) { - debug(20, 0) ("storeCleanup: SIZE MISMATCH\n"); - debug(20, 0) ("storeCleanup: FILENO %08X\n", e->swap_file_number); - debug(20, 0) ("storeCleanup: PATH %s\n", - storeUfsFullPath(e->swap_file_number, NULL)); - debug(20, 0) ("storeCleanup: ENTRY SIZE: %d, FILE SIZE: %d\n", - e->swap_file_sz, (int) sb.st_size); - storeEntryDump(e, 0); - return -1; - } - return 0; + SwapDir *SD = &Config.cacheSwap.swapDirs[e->swap_dirn]; + return (SD->dblcheck(SD, e)); } static void @@ -106,7 +80,7 @@ storeCleanup(void *datanotused) * Calling storeRelease() has no effect because we're * still in 'store_rebuilding' state */ - if (e->swap_file_number < 0) + if (e->swap_filen < 0) continue; if (opt_store_doublecheck) if (storeCleanupDoubleCheck(e)) @@ -116,7 +90,7 @@ storeCleanup(void *datanotused) * Only set the file bit if we know its a valid entry * otherwise, set it in the validation procedure */ - storeDirUpdateSwapSize(e->swap_file_number, e->swap_file_sz, 1); + storeDirUpdateSwapSize(&Config.cacheSwap.swapDirs[e->swap_dirn], e->swap_file_sz, 1); if ((++validnum & 0x3FFFF) == 0) debug(20, 1) (" %7d Entries Validated so far.\n", validnum); } diff --git a/src/store_swapin.cc b/src/store_swapin.cc index 8269bc9ab2..394f8153c5 100644 --- a/src/store_swapin.cc +++ b/src/store_swapin.cc @@ -1,6 +1,6 @@ /* - * $Id: store_swapin.cc,v 1.22 2000/03/06 16:23:35 wessels Exp $ + * $Id: store_swapin.cc,v 1.23 2000/05/03 17:15:44 adrian Exp $ * * DEBUG: section 20 Storage Manager Swapin Functions * AUTHOR: Duane Wessels @@ -36,6 +36,7 @@ #include "squid.h" static STIOCB storeSwapInFileClosed; +static STFNCB storeSwapInFileNotify; void storeSwapInStart(store_client * sc) @@ -46,24 +47,22 @@ storeSwapInStart(store_client * sc) /* We're still reloading and haven't validated this entry yet */ return; } - debug(20, 3) ("storeSwapInStart: called for %08X %s \n", - e->swap_file_number, storeKeyText(e->key)); + debug(20, 3) ("storeSwapInStart: called for %d %08X %s \n", + e->swap_dirn, e->swap_filen, storeKeyText(e->key)); if (e->swap_status != SWAPOUT_WRITING && e->swap_status != SWAPOUT_DONE) { debug(20, 1) ("storeSwapInStart: bad swap_status (%s)\n", swapStatusStr[e->swap_status]); return; } - if (e->swap_file_number < 0) { - debug(20, 1) ("storeSwapInStart: swap_file_number < 0\n"); + if (e->swap_filen < 0) { + debug(20, 1) ("storeSwapInStart: swap_filen < 0\n"); return; } assert(e->mem_obj != NULL); debug(20, 3) ("storeSwapInStart: Opening fileno %08X\n", - e->swap_file_number); - sc->swapin_sio = storeOpen(e->swap_file_number, - O_RDONLY, - storeSwapInFileClosed, - sc); + e->swap_filen); + sc->swapin_sio = storeOpen(e, storeSwapInFileNotify, storeSwapInFileClosed, + sc); cbdataLock(sc->swapin_sio); } @@ -82,3 +81,15 @@ storeSwapInFileClosed(void *data, int errflag, storeIOState * sio) callback(sc->callback_data, sc->copy_buf, errflag); } } + +static void +storeSwapInFileNotify(void *data, int errflag, storeIOState * sio) +{ + store_client *sc = data; + StoreEntry *e = sc->entry; + + debug(1, 3) ("storeSwapInFileNotify: changing %d/%d to %d/%d\n", e->swap_filen, e->swap_dirn, sio->swap_filen, sio->swap_dirn); + + e->swap_filen = sio->swap_filen; + e->swap_dirn = sio->swap_dirn; +} diff --git a/src/store_swapout.cc b/src/store_swapout.cc index c1ab144f67..c2de97109b 100644 --- a/src/store_swapout.cc +++ b/src/store_swapout.cc @@ -1,6 +1,6 @@ /* - * $Id: store_swapout.cc,v 1.64 2000/04/18 06:06:17 wessels Exp $ + * $Id: store_swapout.cc,v 1.65 2000/05/03 17:15:44 adrian Exp $ * * DEBUG: section 20 Storage Manager Swapout Functions * AUTHOR: Duane Wessels @@ -38,6 +38,7 @@ static off_t storeSwapOutObjectBytesOnDisk(const MemObject *); static void storeSwapOutStart(StoreEntry * e); static STIOCB storeSwapOutFileClosed; +static STIOCB storeSwapOutFileNotify; /* start swapping object to disk */ static void @@ -49,31 +50,51 @@ storeSwapOutStart(StoreEntry * e) tlv *tlv_list; char *buf; assert(mem); - storeLockObject(e); - storeSwapFileNumberSet(e, storeDirMapAllocate()); + /* Build the swap metadata, so the filesystem will know how much + * metadata there is to store + */ + debug(20, 5) ("storeSwapOutStart: Begin SwapOut '%s' to dirno %d, fileno %08X\n", + storeUrl(e), e->swap_dirn, e->swap_filen); + e->swap_status = SWAPOUT_WRITING; + tlv_list = storeSwapMetaBuild(e); + buf = storeSwapMetaPack(tlv_list, &swap_hdr_sz); + storeSwapTLVFree(tlv_list); + mem->swap_hdr_sz = (size_t) swap_hdr_sz; + /* Create the swap file */ c = xcalloc(1, sizeof(*c)); c->data = e; cbdataAdd(c, cbdataXfree, 0); - mem->swapout.sio = storeOpen(e->swap_file_number, - O_WRONLY, storeSwapOutFileClosed, c); + mem->swapout.sio = storeCreate(e, storeSwapOutFileNotify, storeSwapOutFileClosed, c); if (NULL == mem->swapout.sio) { e->swap_status = SWAPOUT_NONE; - storeSwapFileNumberSet(e, -1); cbdataFree(c); - storeUnlockObject(e); + xfree(buf); return; } - e->swap_status = SWAPOUT_WRITING; + storeLockObject(e); /* Don't lock until after create, or the replacement + * code might get confused */ + /* Pick up the file number if it was assigned immediately */ + e->swap_filen = mem->swapout.sio->swap_filen; + e->swap_dirn = mem->swapout.sio->swap_dirn; + /* write out the swap metadata */ cbdataLock(mem->swapout.sio); - debug(20, 5) ("storeSwapOutStart: Begin SwapOut '%s' to fileno %08X\n", - storeUrl(e), e->swap_file_number); - tlv_list = storeSwapMetaBuild(e); - buf = storeSwapMetaPack(tlv_list, &swap_hdr_sz); - storeSwapTLVFree(tlv_list); - mem->swap_hdr_sz = (size_t) swap_hdr_sz; storeWrite(mem->swapout.sio, buf, mem->swap_hdr_sz, 0, xfree); } +static void +storeSwapOutFileNotify(void *data, int errflag, storeIOState * sio) +{ + generic_cbdata *c = data; + StoreEntry *e = c->data; + MemObject *mem = e->mem_obj; + assert(e->swap_status == SWAPOUT_WRITING); + assert(mem); + assert(mem->swapout.sio == sio); + assert(errflag == 0); + e->swap_filen = mem->swapout.sio->swap_filen; + e->swap_dirn = mem->swapout.sio->swap_dirn; +} + void storeSwapOut(StoreEntry * e) { @@ -82,7 +103,6 @@ storeSwapOut(StoreEntry * e) off_t new_mem_lo; off_t on_disk = 0; size_t swapout_size; - char *swap_buf; ssize_t swap_buf_len; if (mem == NULL) return; @@ -108,13 +128,22 @@ storeSwapOut(StoreEntry * e) lowest_offset = storeLowestMemReaderOffset(e); debug(20, 7) ("storeSwapOut: lowest_offset = %d\n", (int) lowest_offset); + /* + * Grab the swapout_size and check to see whether we're going to defer + * the swapout based upon size + */ + swapout_size = (size_t) (mem->inmem_hi - mem->swapout.queue_offset); + if ((e->store_status != STORE_OK) && (swapout_size < store_maxobjsize)) { + debug (20, 5) ("storeSwapOut: Deferring starting swapping out\n"); + return; + } /* * Careful. lowest_offset can be greater than inmem_hi, such * as in the case of a range request. */ if (mem->inmem_hi < lowest_offset) new_mem_lo = lowest_offset; - else if (mem->inmem_hi - lowest_offset > DISK_PAGE_SIZE) + else if (mem->inmem_hi - lowest_offset > SM_PAGE_SIZE) new_mem_lo = lowest_offset; else new_mem_lo = mem->inmem_lo; @@ -142,7 +171,6 @@ storeSwapOut(StoreEntry * e) assert(mem->inmem_lo <= on_disk); if (!storeSwapOutAble(e)) return; - swapout_size = (size_t) (mem->inmem_hi - mem->swapout.queue_offset); debug(20, 7) ("storeSwapOut: swapout_size = %d\n", (int) swapout_size); if (swapout_size == 0) { @@ -152,7 +180,7 @@ storeSwapOut(StoreEntry * e) } if (e->store_status == STORE_PENDING) { /* wait for a full block to write */ - if (swapout_size < DISK_PAGE_SIZE) + if (swapout_size < SM_PAGE_SIZE) return; /* * Wait until we are below the disk FD limit, only if the @@ -161,6 +189,14 @@ storeSwapOut(StoreEntry * e) if (storeTooManyDiskFilesOpen() && !fwdCheckDeferRead(-1, e)) return; } + /* + * Don't start swapping out until its either all in memory, or bigger + * than the maximum object size (so we pick a -1 maxobjsize fs) + */ + if ((e->store_status != STORE_OK) && (swapout_size < store_maxobjsize)) { + debug (20, 5) ("storeSwapOut: Deferring starting swapping out\n"); + return; + } /* Ok, we have stuff to swap out. Is there a swapout.sio open? */ if (e->swap_status == SWAPOUT_NONE) { assert(mem->swapout.sio == NULL); @@ -174,35 +210,42 @@ storeSwapOut(StoreEntry * e) if (NULL == mem->swapout.sio) return; do { - if (swapout_size > DISK_PAGE_SIZE) - swapout_size = DISK_PAGE_SIZE; - swap_buf = memAllocate(MEM_DISK_BUF); - swap_buf_len = stmemCopy(&mem->data_hdr, - mem->swapout.queue_offset, - swap_buf, - swapout_size); - if (swap_buf_len < 0) { - debug(20, 1) ("stmemCopy returned %d for '%s'\n", swap_buf_len, storeKeyText(e->key)); - storeUnlink(e->swap_file_number); - storeSwapFileNumberSet(e, -1); - e->swap_status = SWAPOUT_NONE; - memFree(swap_buf, MEM_DISK_BUF); - storeReleaseRequest(e); - storeSwapOutFileClose(e); - return; + /* + * Evil hack time. + * We are paging out to disk in page size chunks. however, later on when + * we update the queue position, we might not have a page (I *think*), + * so we do the actual page update here. + */ + + if (mem->swapout.memnode == NULL) { + /* We need to swap out the first page */ + mem->swapout.memnode = mem->data_hdr.head; + } else { + /* We need to swap out the next page */ + mem->swapout.memnode = mem->swapout.memnode->next; } + /* + * Get the length of this buffer. We are assuming(!) that the buffer + * length won't change on this buffer, or things are going to be very + * strange. I think that after the copy to a buffer is done, the buffer + * size should stay fixed regardless so that this code isn't confused, + * but we can look at this at a later date or whenever the code results + * in bad swapouts, whichever happens first. :-) + */ + swap_buf_len = mem->swapout.memnode->len; + debug(20, 3) ("storeSwapOut: swap_buf_len = %d\n", (int) swap_buf_len); assert(swap_buf_len > 0); debug(20, 3) ("storeSwapOut: swapping out %d bytes from %d\n", swap_buf_len, (int) mem->swapout.queue_offset); mem->swapout.queue_offset += swap_buf_len; - storeWrite(mem->swapout.sio, swap_buf, swap_buf_len, -1, memFreeDISK); + storeWrite(mem->swapout.sio, mem->swapout.memnode->data, swap_buf_len, -1, NULL); /* the storeWrite() call might generate an error */ if (e->swap_status != SWAPOUT_WRITING) break; swapout_size = (size_t) (mem->inmem_hi - mem->swapout.queue_offset); if (e->store_status == STORE_PENDING) - if (swapout_size < DISK_PAGE_SIZE) + if (swapout_size < SM_PAGE_SIZE) break; } while (swapout_size > 0); if (NULL == mem->swapout.sio) @@ -240,30 +283,26 @@ storeSwapOutFileClosed(void *data, int errflag, storeIOState * sio) assert(e->swap_status == SWAPOUT_WRITING); cbdataFree(c); if (errflag) { - sfileno bad = e->swap_file_number; - debug(20, 1) ("storeSwapOutFileClosed: swapfile %08X, errflag=%d\n\t%s\n", - bad, errflag, xstrerror()); - storeSwapFileNumberSet(e, -1); - /* - * yuck. re-set the filemap bit for some errors so that - * we don't try re-using it over and over - */ - if (errno == EPERM) - storeDirMapBitSet(bad); + debug(20, 1) ("storeSwapOutFileClosed: dirno %d, swapfile %08X, errflag=%d\n\t%s\n", + e->swap_dirn, e->swap_filen, errflag, xstrerror()); if (errflag == DISK_NO_SPACE_LEFT) { - storeDirDiskFull(bad); + storeDirDiskFull(e->swap_dirn); storeDirConfigure(); storeConfigure(); } - storeReleaseRequest(e); + if (e->swap_filen > 0) + storeUnlink(e); + e->swap_filen = -1; + e->swap_dirn = -1; e->swap_status = SWAPOUT_NONE; + storeReleaseRequest(e); } else { /* swapping complete */ - debug(20, 3) ("storeSwapOutFileClosed: SwapOut complete: '%s' to %08X\n", - storeUrl(e), e->swap_file_number); + debug(20, 3) ("storeSwapOutFileClosed: SwapOut complete: '%s' to %d, %08X\n", + storeUrl(e), e->swap_dirn, e->swap_filen); e->swap_file_sz = objectLen(e) + mem->swap_hdr_sz; e->swap_status = SWAPOUT_DONE; - storeDirUpdateSwapSize(e->swap_file_number, e->swap_file_sz, 1); + storeDirUpdateSwapSize(&Config.cacheSwap.swapDirs[e->swap_dirn], e->swap_file_sz, 1); if (storeCheckCachable(e)) { storeLog(STORE_LOG_SWAPOUT, e); storeDirSwapLog(e, SWAP_LOG_ADD); diff --git a/src/structs.h b/src/structs.h index 919d626af8..566b811e37 100644 --- a/src/structs.h +++ b/src/structs.h @@ -1,6 +1,6 @@ /* - * $Id: structs.h,v 1.328 2000/05/02 21:35:24 hno Exp $ + * $Id: structs.h,v 1.329 2000/05/03 17:15:44 adrian Exp $ * * * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ @@ -153,11 +153,6 @@ struct _aclCheck_t { void *callback_data; }; -struct _aio_result_t { - int aio_return; - int aio_errno; -}; - struct _wordlist { char *key; wordlist *next; @@ -316,9 +311,7 @@ struct _SquidConfig { #if USE_ICMP char *pinger; #endif -#if USE_UNLINKD char *unlinkd; -#endif } Program; #if USE_DNSSERVERS int dnsChildren; @@ -1284,6 +1277,7 @@ struct _MemObject { int nclients; struct { off_t queue_offset; /* relative to in-mem data */ + mem_node *memnode; /* which node we're currently paging out */ storeIOState *sio; } swapout; HttpReply *reply; @@ -1325,12 +1319,14 @@ struct _StoreEntry { size_t swap_file_sz; u_short refcount; u_short flags; - sfileno swap_file_number; -#if HEAP_REPLACEMENT - heap_node *node; -#else - dlink_node lru; + sdirno swap_dirn; + sfileno swap_filen; + union { +#ifdef HEAP_REPLACEMENT + heap_node *node; #endif + dlink_node lru; + } repl; u_short lock_count; /* Assume < 65536! */ mem_status_t mem_status:3; ping_status_t ping_status:3; @@ -1339,23 +1335,46 @@ struct _StoreEntry { }; struct _SwapDir { - swapdir_t type; - fileMap *map; + char *type; int cur_size; int low_size; int max_size; char *path; int index; /* This entry's index into the swapDirs array */ - sfileno suggest; + int suggest; + size_t max_objsize; + union { +#ifdef HEAP_REPLACEMENT + struct { + heap *heap; + } heap; +#endif + struct { + dlink_list list; + dlink_node *walker; + } lru; + } repl; int removals; int scanned; struct { unsigned int selected:1; unsigned int read_only:1; } flags; - STINIT *init; - STNEWFS *newfs; - struct { + STINIT *init; /* Initialise the fs */ + STNEWFS *newfs; /* Create a new fs */ + STDUMP *dump; /* Dump fs config snippet */ + STFREE *freefs; /* Free the fs data */ + STDBLCHECK *dblcheck; /* Double check the obj integrity */ + STSTATFS *statfs; /* Dump fs statistics */ + STMAINTAINFS *maintainfs; /* Replacement maintainence */ + STCHECKOBJ *checkobj; /* Check if the fs will store an object */ + /* These two are notifications */ + STREFOBJ *refobj; /* Reference this object */ + STUNREFOBJ *unrefobj; /* Unreference this object */ + STCALLBACK *callback; /* Handle pending callbacks */ + STSYNC *sync; /* Sync the directory */ + struct { + STOBJCREATE *create; STOBJOPEN *open; STOBJCLOSE *close; STOBJREAD *read; @@ -1372,33 +1391,7 @@ struct _SwapDir { void *state; } clean; } log; -#if !HEAP_REPLACEMENT - dlink_list lru_list; - dlink_node *lru_walker; -#endif - union { - struct { - int l1; - int l2; - int swaplog_fd; - } ufs; -#if USE_DISKD - struct { - int l1; - int l2; - int swaplog_fd; - int smsgid; - int rmsgid; - int wfd; - int away; - struct { - char *buf; - link_list *stack; - int id; - } shm; - } diskd; -#endif - } u; + void *fsdata; }; struct _request_flags { @@ -1428,10 +1421,13 @@ struct _link_list { }; struct _storeIOState { - sfileno swap_file_number; + sdirno swap_dirn; + sfileno swap_filen; + StoreEntry *e; /* Need this so the FS layers can play god */ mode_t mode; size_t st_size; /* do stat(2) after read open */ - off_t offset; /* current offset pointer */ + off_t offset; /* current on-disk offset pointer */ + STFNCB *file_callback; /* called on delayed sfileno assignments */ STIOCB *callback; void *callback_data; struct { @@ -1441,38 +1437,7 @@ struct _storeIOState { struct { unsigned int closing:1; /* debugging aid */ } flags; - union { - struct { - int fd; - struct { - unsigned int close_request:1; - unsigned int reading:1; - unsigned int writing:1; - } flags; - } ufs; - struct { - int fd; - struct { - unsigned int close_request:1; - unsigned int reading:1; - unsigned int writing:1; - unsigned int opening:1; - } flags; - const char *read_buf; - link_list *pending_writes; - link_list *pending_reads; - } aufs; -#if USE_DISKD - struct { - int id; - struct { - unsigned int reading:1; - unsigned int writing:1; - } flags; - char *read_buf; - } diskd; -#endif - } type; + void *fsstate; }; struct _request_t { @@ -1714,9 +1679,13 @@ struct _tlv { tlv *next; }; +/* + * Do we need to have the dirn in here? I don't think so, since we already + * know the dirn .. + */ struct _storeSwapLogData { char op; - sfileno swap_file_number; + sfileno swap_filen; time_t timestamp; time_t lastref; time_t expires; @@ -1886,6 +1855,35 @@ struct _store_rebuild_data { int zero_object_sz; }; +/* + * This defines an fs type + */ + +struct _storefs_entry { + char *typestr; + STFSPARSE *parsefunc; + STFSRECONFIGURE *reconfigurefunc; + STFSSHUTDOWN *donefunc; +}; + +/* + * Async disk IO - this defines a async disk io queue + */ + +struct _diskd_queue { + int smsgid; /* send sysvmsg id */ + int rmsgid; /* recv sysvmsg id */ + int wfd; /* queue file descriptor ? */ + int away; /* number of requests away */ + int sent_count; /* number of messages sent */ + int recv_count; /* number of messages received */ + struct { + char *buf; /* shm buffer */ + link_list *stack; + int id; /* sysvshm id */ + } shm; +}; + struct _Logfile { int fd; char path[MAXPATHLEN]; diff --git a/src/typedefs.h b/src/typedefs.h index c53d979515..43e20e8a74 100644 --- a/src/typedefs.h +++ b/src/typedefs.h @@ -1,6 +1,6 @@ /* - * $Id: typedefs.h,v 1.101 2000/03/14 23:07:51 wessels Exp $ + * $Id: typedefs.h,v 1.102 2000/05/03 17:15:44 adrian Exp $ * * * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ @@ -39,6 +39,7 @@ typedef unsigned int mem_status_t; typedef unsigned int ping_status_t; typedef unsigned int swap_status_t; typedef int sfileno; +typedef int sdirno; typedef struct { size_t bytes; @@ -68,7 +69,6 @@ typedef struct _acl_snmp_comm acl_snmp_comm; typedef struct _acl_list acl_list; typedef struct _acl_access acl_access; typedef struct _aclCheck_t aclCheck_t; -typedef struct _aio_result_t aio_result_t; typedef struct _wordlist wordlist; typedef struct _intlist intlist; typedef struct _intrange intrange; @@ -168,6 +168,8 @@ typedef struct _helper_request helper_request; typedef struct _generic_cbdata generic_cbdata; typedef struct _storeIOState storeIOState; typedef struct _link_list link_list; +typedef struct _storefs_entry storefs_entry_t; +typedef struct _diskd_queue diskd_queue; typedef struct _Logfile Logfile; #if SQUID_SNMP @@ -181,8 +183,6 @@ typedef struct _delaySpecSet delaySpecSet; typedef struct _delaySpec delaySpec; #endif -/* define AIOCB even without USE_ASYNC_IO */ -typedef void AIOCB(int fd, void *, int aio_return, int aio_errno); typedef void CWCB(int fd, char *, size_t size, int flag, void *data); typedef void CNCB(int fd, int status, void *); @@ -191,8 +191,16 @@ typedef void CBDUNL(void *, int); typedef void FOCB(void *, int fd, int errcode); typedef void EVH(void *); typedef void PF(int, void *); -typedef void DRCB(int fd, const char *buf, int size, int errflag, void *data); -typedef void DWCB(int, int, size_t, void *); + +/* disk.c / diskd.c callback typedefs */ +typedef void DRCB(int, const char *buf, int size, int errflag, void *data); + /* Disk read CB */ +typedef void DWCB(int, int, size_t, void *); /* disk write CB */ +typedef void DOCB(int, int errflag, void *data); /* disk open CB */ +typedef void DCCB(int, int errflag, void *data); /* disk close CB */ +typedef void DUCB(int errflag, void *data); /* disk unlink CB */ +typedef void DTCB(int errflag, void *data); /* disk trunc CB */ + typedef void FQDNH(const char *, void *); typedef void IDCB(const char *ident, void *data); typedef void IPH(const ipcache_addrs *, void *); @@ -203,6 +211,7 @@ typedef void UH(void *data, wordlist *); typedef int DEFER(int fd, void *data); typedef void STIOCB(void *their_data, int errflag, storeIOState *); +typedef void STFNCB(void *their_data, int errflag, storeIOState *); typedef void STRCB(void *their_data, const char *buf, ssize_t len); typedef void SIH(storeIOState *, void *); /* swap in */ @@ -219,17 +228,39 @@ typedef void IDNSCB(void *, rfc1035_rr *, int); typedef void STINIT(SwapDir *); typedef void STNEWFS(SwapDir *); -typedef storeIOState *STOBJOPEN(sfileno, mode_t, STIOCB *, void *); -typedef void STOBJCLOSE(storeIOState *); -typedef void STOBJREAD(storeIOState *, char *, size_t, off_t, STRCB *, void *); -typedef void STOBJWRITE(storeIOState *, char *, size_t, off_t, FREE *); -typedef void STOBJUNLINK(sfileno); +typedef void STDUMP(StoreEntry *, const char *, SwapDir *); +typedef void STFREE(SwapDir *); +typedef int STDBLCHECK(SwapDir *, StoreEntry *); +typedef void STSTATFS(SwapDir *, StoreEntry *); +typedef void STMAINTAINFS(SwapDir *); +typedef int STCHECKOBJ(SwapDir *, const StoreEntry *); +typedef void STREFOBJ(SwapDir *, StoreEntry *); +typedef void STUNREFOBJ(SwapDir *, StoreEntry *); +typedef void STSETUP(storefs_entry_t *); +typedef void STDONE(void); +typedef void STCALLBACK(SwapDir *); +typedef void STSYNC(SwapDir *); + +typedef storeIOState *STOBJCREATE(SwapDir *, StoreEntry *, STFNCB *, STIOCB *, void *); +typedef storeIOState *STOBJOPEN(SwapDir *, StoreEntry *, STFNCB *, STIOCB *, void *); +typedef void STOBJCLOSE(SwapDir *, storeIOState *); +typedef void STOBJREAD(SwapDir *, storeIOState *, char *, size_t, off_t, STRCB *, void *); +typedef void STOBJWRITE(SwapDir *, storeIOState *, char *, size_t, off_t, FREE *); +typedef void STOBJUNLINK(SwapDir *, StoreEntry *); + typedef void STLOGOPEN(SwapDir *); typedef void STLOGCLOSE(SwapDir *); typedef void STLOGWRITE(const SwapDir *, const StoreEntry *, int); typedef int STLOGCLEANOPEN(SwapDir *); typedef void STLOGCLEANWRITE(const StoreEntry *, SwapDir *); +/* Store dir configuration routines */ +/* SwapDir *sd, char *path ( + char *opt later when the strtok mess is gone) */ +typedef void STFSPARSE(SwapDir *, int, char *); +typedef void STFSRECONFIGURE(SwapDir *, int, char *); +typedef void STFSSTARTUP(void); +typedef void STFSSHUTDOWN(void); + typedef double hbase_f(double); typedef void StatHistBinDumper(StoreEntry *, int idx, double val, double size, int count); diff --git a/src/unlinkd.cc b/src/unlinkd.cc index 189f7355e1..61a644b47f 100644 --- a/src/unlinkd.cc +++ b/src/unlinkd.cc @@ -1,5 +1,5 @@ /* - * $Id: unlinkd.cc,v 1.35 2000/05/02 20:13:58 hno Exp $ + * $Id: unlinkd.cc,v 1.36 2000/05/03 17:15:44 adrian Exp $ * * DEBUG: section 12 Unlink Daemon * AUTHOR: Duane Wessels @@ -70,17 +70,14 @@ main(int argc, char *argv[]) /* This code gets linked to Squid */ -#if USE_UNLINKD static int unlinkd_wfd = -1; static int unlinkd_rfd = -1; -#endif #define UNLINKD_QUEUE_LIMIT 20 void unlinkdUnlink(const char *path) { -#if USE_UNLINKD char buf[MAXPATHLEN]; int l; int x; @@ -143,13 +140,11 @@ unlinkdUnlink(const char *path) } Counter.unlink.requests++; queuelen++; -#endif } void unlinkdClose(void) { -#if USE_UNLINKD assert(unlinkd_wfd > -1); debug(12, 1) ("Closing unlinkd pipe on FD %d\n", unlinkd_wfd); file_close(unlinkd_wfd); @@ -157,13 +152,11 @@ unlinkdClose(void) file_close(unlinkd_rfd); unlinkd_wfd = -1; unlinkd_rfd = -1; -#endif } void unlinkdInit(void) { -#if USE_UNLINKD int x; char *args[2]; struct timeval slp; @@ -200,9 +193,6 @@ unlinkdInit(void) if (FD_PIPE == fd_table[unlinkd_wfd].type) commUnsetNonBlocking(unlinkd_wfd); debug(12, 1) ("Unlinkd pipe opened on FD %d\n", unlinkd_wfd); -#else - debug(12, 1) ("Unlinkd is disabled\n"); -#endif } #endif /* ndef UNLINK_DAEMON */ diff --git a/src/url.cc b/src/url.cc index bb781bfa00..c06322bcd1 100644 --- a/src/url.cc +++ b/src/url.cc @@ -1,6 +1,6 @@ /* - * $Id: url.cc,v 1.122 2000/05/02 20:22:55 hno Exp $ + * $Id: url.cc,v 1.123 2000/05/03 17:15:44 adrian Exp $ * * DEBUG: section 23 URL Parsing * AUTHOR: Duane Wessels @@ -243,10 +243,10 @@ urlParse(method_t method, char *url) port = urlDefaultPort(protocol); /* Is there any login informaiton? */ if ((t = strrchr(host, '@'))) { - strcpy(login, host); + strcpy((char *)login, (char *)host); t = strrchr(login, '@'); *t = 0; - strcpy(host, t + 1); + strcpy((char *)host, t + 1); } if ((t = strrchr(host, ':'))) { *t++ = '\0';