[prev in list] [next in list] [prev in thread] [next in thread]
List: busybox
Subject: Re: [RFC/PATCH v2 5/5] readahead: applet extension
From: Bartosz_Gołaszewski <bartekgola () gmail ! com>
Date: 2015-08-21 22:15:47
Message-ID: CAFdkumgWNw8eeTJ7pSs-DOeZdGVthWYf3S3+cV86mOBt-pS0OQ () mail ! gmail ! com
[Download RAW message or body]
2015-08-21 16:23 GMT+02:00 Bartosz Golaszewski <bartekgola@gmail.com>:
> Implement optional daemon mode for readahead.
>
> Bloatcheck without daemon mode:
> function old new delta
> readahead_main 127 123 -4
> ------------------------------------------------------------------------------
> (add/remove: 0/0 grow/shrink: 0/1 up/down: 0/-4) Total: -4 bytes
>
> Bloatcheck with daemon mode:
> function old new delta
> readahead_main 127 2472 +2345
> .rodata 157453 158029 +576
> qsort_cmp - 54 +54
> packed_usage 30522 30569 +47
> tree_add_item - 40 +40
> move_item_to_array - 38 +38
> tree_cmp - 11 +11
> ------------------------------------------------------------------------------
> (add/remove: 4/0 grow/shrink: 3/0 up/down: 3111/0) Total: 3111 bytes
>
> Signed-off-by: Bartosz Golaszewski <bartekgola@gmail.com>
> ---
> docs/readahead.txt | 39 +++
> miscutils/Config.src | 10 +
> miscutils/readahead.c | 647 ++++++++++++++++++++++++++++++++++++++++++++++++--
> 3 files changed, 681 insertions(+), 15 deletions(-)
> create mode 100644 docs/readahead.txt
>
> diff --git a/docs/readahead.txt b/docs/readahead.txt
> new file mode 100644
> index 0000000..b13170b
> --- /dev/null
> +++ b/docs/readahead.txt
> @@ -0,0 +1,39 @@
> +Readahead applet works in two modes. If at least one file is given via the
> +command-line, it just calls readahead() for all the files. Otherwise it works
> +as a daemon.
> +
> +In daemon mode it reads config options from /etc/readahead/readahead.conf,
> +readahead()s all the files that have already been saved in
> +/etc/readahead/readahead.lst and potentially starts collecting data about
> +files being accessed for a configured number of seconds. It then adds all
> +new entires to readahead.lst and increases the number in readahead.stamp
> +which is then used to determine the number of data acquisition passes
> +already done.
> +
> +If the value in readahead.stamp is equal or greater than the value of
> +COLLECT_PASSES config option readahead stops collecting data and only
> +readahead()s the files as soon as possible.
> +
> +Config file:
> +
> +/etc/readahead/readahead.conf contains simple key = value configuration
> +options.
> +
> +Available options:
> +
> +COLLECT_TIME - desired time of data collection in seconds
> +
> +RAM_MAX - max memory usage for readahead in bytes (half of
> + available RAM by default)
> +
> +COLLECT_PASSES - number of times readahead should collect data before
> + switching to passive mode
> +
> +If the config file doesn't exist, readahead works using reasonable default
> +settings.
> +
> +It is possible to run readahead either as a regular process during system
> +boot, or as init in which case it will spawn a second readahead process to
> +start the file preload as fast as possible and then re-exec as the real
> +init. The kernel command-line argument readahead_init can be used to specify
> +the init executable different than /sbin/init.
> diff --git a/miscutils/Config.src b/miscutils/Config.src
> index d69abf1..d4cea06 100644
> --- a/miscutils/Config.src
> +++ b/miscutils/Config.src
> @@ -469,6 +469,16 @@ config READAHEAD
> As readahead(2) blocks until each file has been read, it is best to
> run this applet as a background job.
>
> +config READAHEAD_DAEMON
> + bool "daemon mode"
> + default n
> + depends on READAHEAD
> + select BUNZIP2
> + help
> + Include the readahead daemon which runs in the background, records
> + the list of files that are accessed during boot and readahead()s
> + them in subsequent system start-ups to improve the boot-speed.
> +
> config RUNLEVEL
> bool "runlevel"
> default y
> diff --git a/miscutils/readahead.c b/miscutils/readahead.c
> index e22aaa4..b3edc1e 100644
> --- a/miscutils/readahead.c
> +++ b/miscutils/readahead.c
> @@ -5,43 +5,660 @@
> * Preloads the given files in RAM, to reduce access time.
> * Does this by calling the readahead(2) system call.
> *
> - * Copyright (C) 2006 Michael Opdenacker <michael@free-electrons.com>
> + * Copyright (C) 2006 Michael Opdenacker <michael@free-electrons.com>
> + * Copyright (C) 2015 Bartosz Golaszewski <bartekgola@gmail.com>
> *
> * Licensed under GPLv2 or later, see file LICENSE in this source tree.
> */
>
> +//usage:#ifndef CONFIG_READAHEAD_DAEMON
> //usage:#define readahead_trivial_usage
> //usage: "[FILE]..."
> //usage:#define readahead_full_usage "\n\n"
> //usage: "Preload FILEs to RAM"
> +//usage:#else
> +//usage:#define readahead_trivial_usage
> +//usage: "[-f] [FILE]"
> +//usage:#define readahead_full_usage "\n\n"
> +//usage: "Preload files to RAM (as a command-line tool or as a daemon)"
> +//usage: "\n -f don't fork in daemon mode\n\n"
> +//usage: "For detailed daemon configuration see readahead.txt."
> +//usage:#endif
>
> #include "libbb.h"
>
> +static off_t get_filelen(int fd)
> +{
> + off_t len;
> +
> + len = xlseek(fd, 0, SEEK_END);
> + xlseek(fd, 0, SEEK_SET);
> +
> + return len;
> +}
> +
> +#ifndef CONFIG_READAHEAD_DAEMON
> int readahead_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
> int readahead_main(int argc UNUSED_PARAM, char **argv)
> +#else
> +static int readahead_compat(char **argv)
> +#endif
> {
> - int retval = EXIT_SUCCESS;
> + int status, fd, retval = EXIT_SUCCESS;
> + off_t len;
> +
> +#ifndef CONFIG_READAHEAD_DAEMON
> + argv++;
> + if (!argv[0])
> + return retval;
> +#endif
> + /*
> + * The initial version of this applet only readahead() the list of
> + * files passed as command-line arguments. For backwards compatibility
> + * in daemon mode this code is called when any additional non-option
> + * arguments are passed via command-line. All option arguments are
> + * ignored in this mode.
> + */
> + do {
> + fd = open_or_warn(*argv, O_RDONLY);
> + if (fd >= 0) {
> + len = get_filelen(fd);
> + status = readahead(fd, 0LL, len);
> + close(fd);
> + if (status >= 0)
> + continue;
> + }
> + retval = EXIT_FAILURE;
> + } while(*++argv);
> +
> + return retval;
> +}
> +
> +#ifdef CONFIG_READAHEAD_DAEMON
> +
> +#include <search.h>
> +#include <sys/sysinfo.h>
> +#include <sys/signalfd.h>
> +#include <linux/fanotify.h>
> +#include <sys/fanotify.h>
> +
> +#define OPT_f (1 << 0)
> +#define FAN_POLL_INTERVAL 1000
> +#define PARSER_FLAGS (PARSE_NORMAL & ~(PARSE_TRIM | PARSE_COLLAPSE))
> +
> +#define RA_CONFIG "/etc/readahead/readahead.conf"
> +#define RA_FILE_LIST "/etc/readahead/readahead.lst.bz2"
> +#define RA_STAMP "/etc/readahead/readahead.stamp"
> +
> +#define BZIP2_CMD "/bin/busybox bzip2 -c >"
> +#define BZCAT_CMD "/bin/busybox bzcat"
> +
> +struct globals {
> + /* General config. */
> + unsigned long ram_max;
> + unsigned long collect_time;
> + unsigned long collect_passes;
> +
> + /* Number of collect passes already completed. */
> + unsigned passes_done;
> +
> + int do_collect;
> +
> + /* Root node of the file tree. */
> + void *root;
> + /* Number of items in the tree. */
> + size_t num_items;
> + /* Items sorted by access time. */
> + struct ra_item **items_array;
> + /* Helper index for moving items between the tree and the array. */
> + int item_index;
> +
> + volatile int stopped;
> + int mem_exceeded;
> + int fan_fd;
> + int sig_fd;
> +
> + struct timeval start_time;
> +
> + char *rdlink_buf;
> +} FIX_ALIASING;
> +
> +#define G (*(struct globals*)&bb_common_bufsiz1)
> +
> +/* Set some reasonable defaults for config options in INIT_G(). */
> +#define INIT_G() do { \
> + memset(&G, 0, sizeof(struct globals)); \
> + G.collect_time = 180; \
> + G.collect_passes = 2; \
> + G.do_collect = 0; \
> + G.ram_max = get_totalram() / 2; \
> + G.fan_fd = -1; \
> + G.sig_fd = -1; \
> + G.rdlink_buf = xzalloc(PATH_MAX); \
> + } while (0)
> +
> +struct ra_item {
> + char *path;
> + struct timeval access_time;
> +};
> +
> +static unsigned long get_totalram(void)
> +{
> + struct sysinfo info;
> +
> + (void)sysinfo(&info);
> +
> + return info.totalram;
> +}
> +
> +static int file_is_regular(int fd)
> +{
> + struct stat statbuf;
> + int status;
> +
> + status = fstat(fd, &statbuf);
> + if (status < 0)
> + return 0;
> +
> + return S_ISREG(statbuf.st_mode);
> +}
> +
> +static int qsort_cmp(const void *p1, const void *p2)
> +{
> + const struct ra_item *i1 = *(const struct ra_item **)p1;
> + const struct ra_item *i2 = *(const struct ra_item **)p2;
> +
> + /* Compare first by time, then by path. */
> + if (timercmp(&i1->access_time, &i2->access_time, <))
> + return -1;
> + if (timercmp(&i1->access_time, &i2->access_time, >))
> + return 1;
> +
> + return strcmp(i1->path, i2->path);
> +}
> +
> +static int tree_cmp(const void *p1, const void *p2)
> +{
> + const struct ra_item *i1 = (const struct ra_item *)p1;
> + const struct ra_item *i2 = (const struct ra_item *)p2;
> +
> + return strcmp(i1->path, i2->path);
> +}
> +
> +static void tree_add_item(const struct ra_item *item)
> +{
> + void *rv;
> +
> + rv = tsearch(item, &G.root, tree_cmp);
> + if (rv == NULL)
> + bb_perror_msg_and_die("tsearch"); /* OOM */
> + G.num_items++;
> +}
> +
> +static void item_set_event_time(struct ra_item *item)
> +{
> + struct timeval now;
> +
> + gettimeofday(&now, NULL);
> + timersub(&now, &G.start_time, &item->access_time);
> +}
> +
> +static void parse_config(void)
> +{
> + char *tokens[2], *key, *val;
> + parser_t *parser;
> + int num_toks;
> +
> + parser = config_open2(RA_CONFIG, fopen_for_read);
> + if (!parser)
> + /* Don't complain, we can do without a config file. */
> + return;
> +
> + while ((num_toks = config_read(parser, tokens,
> + 2, 1, "#=", PARSER_FLAGS))) {
> + if (num_toks != 2)
> + continue;
>
> - if (!argv[1]) {
> - bb_show_usage();
> + key = tokens[0];
> + val = tokens[1];
> + trim(key);
> + trim(val);
> +
> + /*
> + * Too few configuration settings to make it worth playing
> + * with some advanced parsing. Just use strcmp().
> + */
> + if (strcmp(key, "COLLECT_TIME") == 0) {
> + G.collect_time = xstrtoul(val, 10);
> + } else if (strcmp(key, "RAM_MAX") == 0) {
> + G.ram_max = xstrtoul(val, 10);
> + } else if (strcmp(key, "COLLECT_PASSES") == 0) {
> + G.collect_passes = xstrtoul(val, 10);
> + } else {
> + bb_error_msg(
> + "ignoring unrecognized variable: '%s'", key);
> + }
> }
>
> - while (*++argv) {
> - int fd = open_or_warn(*argv, O_RDONLY);
> - if (fd >= 0) {
> - off_t len;
> - int r;
> + config_close(parser);
> +}
> +
> +static void read_stamp(void)
> +{
> + unsigned stamp;
> + FILE *fp;
> + int rv;
> +
> + fp = fopen(RA_STAMP, "r");
> + if (fp == NULL) {
> + G.passes_done = 0;
> + } else {
> + rv = fscanf(fp, "%u", &stamp);
> + if (rv != 1)
> + G.passes_done = 0;
> + else
> + G.passes_done = stamp;
> + fclose(fp);
> + }
> +}
> +
> +static void write_stamp(void)
> +{
> + FILE *fp;
> +
> + fp = fopen(RA_STAMP, "w");
> + if (fp == NULL) {
> + bb_perror_msg("error opening the stamp file");
> + return;
> + }
> +
> + fprintf(fp, "%u\n", G.passes_done);
> + fclose(fp);
> +}
> +
> +#if ENABLE_FEATURE_CLEAN_UP
> +static void free_node(void *nodep)
> +{
> + struct ra_item *item = (struct ra_item *)nodep;
> +
> + if (item) {
> + free(item->path);
> + free(item);
> + }
> +}
> +
> +static void file_tree_destroy(void)
> +{
> + tdestroy(G.root, free_node);
> +}
> +#endif /* ENABLE_FEATURE_CLEAN_UP */
> +
> +static void fork_and_exec_init(void)
> +{
> + const char *ra_init;
> + pid_t pid;
>
> - /* fdlength was reported to be unreliable - use seek */
> - len = xlseek(fd, 0, SEEK_END);
> - xlseek(fd, 0, SEEK_SET);
> - r = readahead(fd, 0, len);
> + pid = xfork();
> + if (pid > 0) {
> + ra_init = getenv("readahead_init");
> + if (!ra_init)
> + ra_init = "/sbin/init";
> + execl(ra_init, ra_init, (char *)NULL);
> + bb_perror_msg_and_die(
> + "error executing '%s'", ra_init);
> + }
> +}
> +
> +static void daemonize(void)
> +{
> + int status;
> +
> + /* Daemonize, but retain the console. */
> + status = daemon(0, 1);
> + if (status < 0)
> + bb_perror_msg_and_die("unable to run in background");
> +}
> +
> +/*
> + * Do the actual readahead if the list file exists before starting to collect
> + * data.
> + *
> + * The files are expected to be generated by readahead_collect() and are not
> + * checked for repetitions and ordering. They are checked however for being
> + * regular files.
> + *
> + * In case of open() errors the applet continues silently.
> + */
> +static int readahead_files(void)
> +{
> + enum { TOK_PATH = 0, TOK_SEC, TOK_USEC, _TOK_MAX };
> +
> + int status, retval = EXIT_SUCCESS, fd, num_tok, bzrv;
> + char popen_cmp[sizeof(BZCAT_CMD RA_FILE_LIST) + 2];
> + unsigned long len, ram_taken = 0;
> + char *token[_TOK_MAX], *path;
> + struct ra_item *item;
> + parser_t *parser;
> + long sec, usec;
> + FILE *fp;
> +
> + snprintf(popen_cmp, sizeof(popen_cmp),
> + "%s %s", BZCAT_CMD, RA_FILE_LIST);
> +
> + if (access(RA_FILE_LIST, F_OK))
> + return retval;
> +
> + fp = popen(popen_cmp, "r");
> + if (!fp)
> + bb_perror_msg_and_die("popen bzcat");
> +
> + parser = config_from_fp(fp);
> + if (parser) {
> + while ((num_tok = config_read(parser, token,
> + 3, 1, "#:", PARSER_FLAGS))) {
> + if (str_isblank(token[0]))
> + continue;
> +
> + if (num_tok < 3)
> + bb_error_msg_and_die(
> + "%s: invalid format", RA_FILE_LIST);
> +
> + path = token[0];
> + sec = xstrtol(token[1], 10);
> + usec = xstrtol(token[2], 10);
> +
> + trim(path);
> +
> + fd = open(path, O_RDONLY);
> + if (fd < 0)
> + continue;
> +
> + if (!file_is_regular(fd)) {
> + close(fd);
> + continue;
> + }
> +
> + if (G.mem_exceeded)
> + goto add_file;
> +
> + len = get_filelen(fd);
> + if (G.ram_max && (ram_taken + len) > G.ram_max) {
> + bb_error_msg("memory treshold exceeded");
> + G.mem_exceeded = 1;
> + close(fd);
> + goto add_file;
> + }
> + ram_taken += len;
> +
> + status = readahead(fd, 0LL, len);
> close(fd);
> - if (r >= 0)
> + if (status < 0) {
> + bb_perror_msg("readahead(\"%s\"):", path);
> + retval = EXIT_FAILURE;
> continue;
> + }
> +
> +add_file:
> + /*
> + * If the file could be readahead() properly and
> + * we're in collecting mode - add it to the tree.
> + */
> + if (G.do_collect) {
> + item = xzalloc(sizeof(struct ra_item));
> + item->path = xstrdup(path);
> + item->access_time.tv_sec = sec;
> + item->access_time.tv_usec = usec;
> + tree_add_item(item);
> + }
> }
> - retval = EXIT_FAILURE;
> +
> + config_free(parser);
> + bzrv = pclose(fp);
> + if (bzrv != EXIT_SUCCESS)
> + bb_error_msg_and_die("error reading file list");
> }
>
> return retval;
> }
> +
> +static int setup_fanotify(void)
> +{
> + int fd, status, init_flags, event_flags, mark_flags, mark_mask;
> +
> + init_flags = FAN_CLOEXEC | FAN_NONBLOCK | FAN_CLASS_CONTENT;
> + event_flags = O_RDONLY | O_LARGEFILE;
> + mark_flags = FAN_MARK_ADD | FAN_MARK_MOUNT;
> + mark_mask = FAN_OPEN;
> +
> + fd = fanotify_init(init_flags, event_flags);
> + if (fd < 0)
> + bb_perror_msg_and_die("fanotify_init");
> +
> + status = fanotify_mark(fd, mark_flags, mark_mask, 0, "/");
> + if (status < 0)
> + bb_perror_msg_and_die("fanotify_mark");
> +
> + return fd;
> +}
> +
> +static int setup_signalfd(void)
> +{
> + sigset_t sigmask;
> + int fd, status;
> +
> + sigemptyset(&sigmask);
> + sigaddset(&sigmask, SIGTERM);
> + sigaddset(&sigmask, SIGINT);
> + sigaddset(&sigmask, SIGALRM);
> +
> + status = sigprocmask(SIG_BLOCK, &sigmask, NULL);
> + if (status < 0)
> + bb_perror_msg_and_die("sigprocmask");
> +
> + fd = signalfd(-1, &sigmask, SFD_NONBLOCK | SFD_CLOEXEC);
> + if (fd < 0)
> + bb_perror_msg_and_die("signalfd");
> +
> + return fd;
> +}
> +
> +static void handle_fanotify_events(void)
> +{
> + char procpath[sizeof("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
> + struct fanotify_event_metadata fan_data;
> + struct ra_item tmp_item, *item;
> + void *tf_ptr;
> + ssize_t rd;
> +
> + /*
> + * Check the stopped condition again so that we don't
> + * end up processing all new events after having been
> + * asked to stop.
> + */
Note to self - this is impossible since signals are now handled via
signalfd in the same process.
> + while (!G.stopped) {
> + memset(&fan_data, 0, FAN_EVENT_METADATA_LEN);
> + rd = read(G.fan_fd, &fan_data, FAN_EVENT_METADATA_LEN);
> + if (rd < 0) {
> + if (errno == EAGAIN)
> + break; /* No more events. */
> + if (errno == EINTR)
> + continue;
> +
> + bb_perror_msg_and_die("fanotify event read");
> + }
> +
> + if (!FAN_EVENT_OK(&fan_data, sizeof(fan_data))
> + || fan_data.fd == FAN_NOFD)
> + continue;
> +
> + snprintf(procpath, sizeof(procpath),
> + "/proc/self/fd/%d", fan_data.fd);
> +
> + memset(G.rdlink_buf, 0, PATH_MAX);
> + rd = readlink(procpath, G.rdlink_buf, PATH_MAX);
> + if (rd < 0) {
> + /* Don't complain if file was just removed. */
> + if (errno != ENOENT)
> + bb_perror_msg("readlink");
> + close(fan_data.fd);
> + continue;
> + }
> +
> + /*
> + * Make sure we don't store deleted files.
> + * Ignore files in /tmp as well as it's
> + * usually tmpfs.
> + */
> + if (ends_with(G.rdlink_buf, " (deleted)")
> + || is_prefixed_with(G.rdlink_buf, "/tmp/")) {
> + close(fan_data.fd);
> + continue;
> + }
> +
> + tmp_item.path = G.rdlink_buf;
> + tf_ptr = tfind(&tmp_item, &G.root, tree_cmp);
> + if (tf_ptr == NULL) {
> + /* New file -> add it. */
> + item = xzalloc(sizeof(struct ra_item));
> + item->path = xstrdup(G.rdlink_buf);
> + item_set_event_time(item);
> + tree_add_item(item);
> + }
> +
> + close(fan_data.fd);
> + }
> +}
> +
> +static void readahead_collect(void)
> +{
> + enum { FD_FANOTIFY = 0, FD_SIGNAL, FD_NUM };
> +
> + struct pollfd fds[FD_NUM];
> + int status;
> +
> + G.fan_fd = setup_fanotify();
> + G.sig_fd = setup_signalfd();
> +
> + fds[FD_FANOTIFY].fd = G.fan_fd;
> + fds[FD_FANOTIFY].events = POLLIN;
> + fds[FD_SIGNAL].fd = G.sig_fd;
> + fds[FD_SIGNAL].events = POLLIN;
> +
> + if (G.collect_time)
> + alarm(G.collect_time);
> +
> + G.stopped = 0;
> + while (!G.stopped) {
> + status = poll(fds, FD_NUM, FAN_POLL_INTERVAL);
> + if (status < 0) {
> + if (errno == EINTR)
> + continue;
> + bb_perror_msg_and_die("poll");
> + } else if (status == 0) {
> + continue; /* timeout */
> + }
> +
> + if (fds[FD_FANOTIFY].revents)
> + handle_fanotify_events();
> +
> + if (fds[FD_SIGNAL].revents) {
> + /*
> + * Any expected signal will do - don't waste time
> + * and code reading the event data.
> + */
> + G.stopped = 1;
> + close(G.sig_fd);
> + }
> + }
> +
> + close(G.fan_fd);
> +}
> +
> +static void move_item_to_array(const void *nodep,
> + const VISIT which, const int UNUSED_PARAM depth)
> +{
> + struct ra_item *item = *(struct ra_item **)nodep;
> +
> + if (which == leaf || which == postorder)
> + G.items_array[G.item_index++] = item;
> +}
> +
> +static void sort_by_time(void)
> +{
> + G.items_array = xmalloc(G.num_items * sizeof(struct ra_item *));
> + twalk(G.root, move_item_to_array);
> + qsort(G.items_array, G.num_items, sizeof(struct ra_item *), qsort_cmp);
> +}
> +
> +static void save_lst_file(void)
> +{
> + char cmd[sizeof(BZIP2_CMD RA_FILE_LIST) + 2];
> + struct ra_item *item;
> + int i, rv;
> + FILE *fp;
> +
> + (void)mkdir("/etc/readahead", 0777);
> +
> + snprintf(cmd, sizeof(cmd), "%s %s", BZIP2_CMD, RA_FILE_LIST);
> + fp = popen(cmd, "w");
> + if (!fp)
> + bb_perror_msg_and_die("popen saving lst file");
> +
> + for (i = 0; i < G.num_items; i++) {
> + item = G.items_array[i];
> + fprintf(fp, "%s:%ld:%ld\n", item->path,
> + item->access_time.tv_sec, item->access_time.tv_usec);
> + }
> +
> + rv = pclose(fp);
> + if (rv != EXIT_SUCCESS)
> + bb_error_msg_and_die("error saving to '%s'", RA_FILE_LIST);
> +}
> +
> +int readahead_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
> +int readahead_main(int argc UNUSED_PARAM, char **argv)
> +{
> + int retval = EXIT_SUCCESS;
> + unsigned opts;
> +
> + INIT_G();
> +
> + opts = getopt32(argv, "f");
> + argv += optind;
> +
> + if (argv[0])
> + return readahead_compat(argv);
> +
> + if (getpid() == 1) {
> + /*
> + * If we are being run as init - spawn a separate process
> + * for readahead daemon and exec real init in pid 1.
> + */
> + fork_and_exec_init();
> + } else if (!(opts & OPT_f)) {
> + daemonize();
> + }
> +
> + parse_config();
> + read_stamp();
> + if (G.passes_done < G.collect_passes)
> + G.do_collect = 1;
> +
> + retval = readahead_files();
> +
> + if (G.do_collect) {
> + gettimeofday(&G.start_time, NULL);
> + readahead_collect();
> + sort_by_time();
> + save_lst_file();
> + G.passes_done++;
> + write_stamp();
> +
> + IF_FEATURE_CLEAN_UP(file_tree_destroy());
> + IF_FEATURE_CLEAN_UP(free(G.items_array));
> + }
> +
> + return retval;
> +}
> +
> +#endif /* CONFIG_READAHEAD_DAEMON */
> --
> 2.1.4
>
--
Best regards,
Bartosz Golaszewski
_______________________________________________
busybox mailing list
busybox@busybox.net
http://lists.busybox.net/mailman/listinfo/busybox
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic