#include <linux/fs.h>
#include <linux/fiemap.h>
#include <sys/ioctl.h>
+#include <sys/vfs.h>
#include "missing.h"
#include "util.h"
#include "ioprio.h"
#include "readahead-common.h"
-/*
- fixme:
+#define MINCORE_VEC_SIZE (READAHEAD_FILE_SIZE_MAX/PAGE_SIZE)
+#define TIMEOUT_USEC (2*USEC_PER_MINUTE)
+
+/* fixme:
+ *
+ * - detect ssd on btrfs/lvm...
+ * - read ahead directories
+ * - sd_readahead_cancel
+ * - gzip?
+ * - oom adjust
+ * - remount rw
+ * - are filenames from anotify normalized regards /../ and // and /./?
+ * - does ioprio_set work with fadvise()?
+ */
+
+static int btrfs_defrag(int fd) {
+ struct btrfs_ioctl_vol_args data;
- - BTRFS_IOC_DEFRAG
-*/
+ zero(data);
+ data.fd = fd;
-#define MINCORE_VEC_SIZE (READAHEAD_FILE_SIZE_MAX/PAGE_SIZE)
+ return ioctl(fd, BTRFS_IOC_DEFRAG, &data);
+}
-static int pack_file(FILE *pack, const char *fn) {
+static int pack_file(FILE *pack, const char *fn, bool on_btrfs) {
struct stat st;
void *start = MAP_FAILED;
uint8_t vec[MINCORE_VEC_SIZE];
goto finish;
}
+ if (on_btrfs)
+ btrfs_defrag(fd);
+
l = PAGE_ALIGN(st.st_size);
if ((start = mmap(NULL, l, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
log_warning("mmap(%s) failed: %m", fn);
pages = l / PAGE_SIZE;
mapped = false;
for (c = 0; c < pages; c++) {
- bool new_mapped = (vec[c] & 1);
+ bool new_mapped = !!(vec[c] & 1);
if (!mapped && new_mapped)
b = c;
sigset_t mask;
FILE *pack = NULL;
char *pack_fn_new = NULL, *pack_fn = NULL;
- bool on_ssd;
+ bool on_ssd, on_btrfs;
+ struct statfs sfs;
+ usec_t not_after;
assert(root);
goto finish;
}
- if ((fanotify_fd = fanotify_init(FAN_CLOEXEC, O_RDONLY|O_LARGEFILE|O_CLOEXEC|O_NOATIME)) < 0) {
+ if ((fanotify_fd = fanotify_init(FAN_CLOEXEC|FAN_NONBLOCK, O_RDONLY|O_LARGEFILE|O_CLOEXEC|O_NOATIME)) < 0) {
log_error("Failed to create fanotify object: %m");
r = -errno;
goto finish;
goto finish;
}
+ not_after = now(CLOCK_MONOTONIC) + TIMEOUT_USEC;
+
my_pid = getpid();
zero(pollfd);
} data;
ssize_t n;
struct fanotify_event_metadata *m;
+ usec_t t;
+ int h;
+
+ if (hashmap_size(files) > READAHEAD_FILES_MAX) {
+ log_debug("Reached maximum number of read ahead files, ending collection.");
+ break;
+ }
+
+ t = now(CLOCK_MONOTONIC);
+ if (t >= not_after) {
+ log_debug("Reached maximum collection time, ending collection.");
+ break;
+ }
- if (poll(pollfd, _FD_MAX, -1) < 0) {
+ if ((h = poll(pollfd, _FD_MAX, (int) ((not_after - t) / USEC_PER_MSEC))) < 0) {
if (errno == EINTR)
continue;
if (pollfd[FD_SIGNAL].revents != 0)
break;
+ if (h == 0) {
+ log_debug("Reached maximum collection time, ending collection.");
+ break;
+ }
+
if ((n = read(fanotify_fd, &data, sizeof(data))) < 0) {
if (errno == EINTR || errno == EAGAIN)
goto finish;
}
- m = &data.metadata;
- while (FAN_EVENT_OK(m, n)) {
+ for (m = &data.metadata; FAN_EVENT_OK(m, n); m = FAN_EVENT_NEXT(m, n)) {
if (m->pid != my_pid && m->fd >= 0) {
char fn[PATH_MAX];
ul = fd_first_block(m->fd);
if ((k = hashmap_put(files, p, ULONG_TO_PTR(ul))) < 0) {
-
- if (k != -EEXIST)
- log_warning("set_put() failed: %s", strerror(-k));
-
+ log_warning("set_put() failed: %s", strerror(-k));
free(p);
}
}
if (m->fd)
close_nointr_nofail(m->fd);
-
- m = FAN_EVENT_NEXT(m, n);
}
-
}
if (fanotify_fd >= 0) {
on_ssd = fs_on_ssd(root);
log_debug("On SSD: %s", yes_no(on_ssd));
+ on_btrfs = statfs(root, &sfs) >= 0 && sfs.f_type == BTRFS_SUPER_MAGIC;
+ log_debug("On btrfs: %s", yes_no(on_btrfs));
+
asprintf(&pack_fn, "%s/.readahead", root);
asprintf(&pack_fn_new, "%s/.readahead.new", root);
fputs(CANONICAL_HOST "\n", pack);
putc(on_ssd ? 'S' : 'R', pack);
- if (on_ssd) {
+ if (on_ssd || on_btrfs) {
- /* On SSD, just write things out in the order the
- * files where accessed */
+ /* On SSD or on btrfs, just write things out in the
+ * order the files were accessed. */
HASHMAP_FOREACH_KEY(q, p, files, i)
- pack_file(pack, p);
+ pack_file(pack, p, on_btrfs);
} else {
struct item *ordered, *j;
unsigned k, n;
qsort(ordered, n, sizeof(struct item), qsort_compare);
for (k = 0; k < n; k++)
- pack_file(pack, ordered[k].path);
+ pack_file(pack, ordered[k].path, on_btrfs);
free(ordered);
}
}
int main(int argc, char *argv[]) {
- /* log_set_target(LOG_TARGET_SYSLOG_OR_KMSG); */
+
+ log_set_target(LOG_TARGET_SYSLOG_OR_KMSG);
log_parse_environment();
log_open();
- log_set_max_level(LOG_DEBUG);
+ if (!enough_ram()) {
+ log_info("Disabling readahead collector due to low memory.");
+ return 0;
+ }
- if (collect("/") < 0)
+ if (collect(argc >= 2 ? argv[1] : "/") < 0)
return 1;
return 0;