static void test_readahead(void) { unsigned long read_bytes, read_bytes_ra; long usec, usec_ra; unsigned long cached_max, cached_low, cached, cached_ra; char proc_io_fname[128]; sprintf(proc_io_fname, "/proc/%u/io", getpid()); /* find out how much can cache hold if we read whole file */ read_testfile(0, testfile, testfile_size, &read_bytes, &usec, &cached); cached_max = get_cached_size(); sync(); drop_caches(); cached_low = get_cached_size(); cached_max = cached_max - cached_low; tst_resm(TINFO, "read_testfile(0)"); read_testfile(0, testfile, testfile_size, &read_bytes, &usec, &cached); cached = cached - cached_low; sync(); drop_caches(); cached_low = get_cached_size(); tst_resm(TINFO, "read_testfile(1)"); read_testfile(1, testfile, testfile_size, &read_bytes_ra, &usec_ra, &cached_ra); cached_ra = cached_ra - cached_low; tst_resm(TINFO, "read_testfile(0) took: %ld usec", usec); tst_resm(TINFO, "read_testfile(1) took: %ld usec", usec_ra); if (has_file(proc_io_fname, 0)) { tst_resm(TINFO, "read_testfile(0) read: %ld bytes", read_bytes); tst_resm(TINFO, "read_testfile(1) read: %ld bytes", read_bytes_ra); /* actual number of read bytes depends on total RAM */ if (read_bytes_ra < read_bytes) tst_resm(TPASS, "readahead saved some I/O"); else tst_resm(TFAIL, "readahead failed to save any I/O"); } else { tst_resm(TCONF, "Your system doesn't have /proc/pid/io," " unable to determine read bytes during test"); } tst_resm(TINFO, "cache can hold at least: %ld kB", cached_max); tst_resm(TINFO, "read_testfile(0) used cache: %ld kB", cached); tst_resm(TINFO, "read_testfile(1) used cache: %ld kB", cached_ra); if (cached_max * 1024 >= testfile_size) { /* * if cache can hold ~testfile_size then cache increase * for readahead should be at least testfile_size/2 */ if (cached_ra * 1024 > testfile_size / 2) tst_resm(TPASS, "using cache as expected"); else tst_resm(TWARN, "using less cache than expected"); } else { tst_resm(TCONF, "Page cache on your system is too small " "to hold whole testfile."); } }
/* read_testfile - mmap testfile and read every page. * This functions measures how many I/O and time it takes to fully * read contents of test file. * * @do_readahead: call readahead prior to reading file content? * @fname: name of file to test * @fsize: how many bytes to read/mmap * @read_bytes: returns difference of bytes read, parsed from /proc/<pid>/io * @usec: returns how many microsecond it took to go over fsize bytes * @cached: returns cached kB from /proc/meminfo */ static void read_testfile(int do_readahead, const char *fname, size_t fsize, unsigned long *read_bytes, long *usec, unsigned long *cached) { int fd; size_t i; long read_bytes_start; unsigned char *p, tmp; unsigned long time_start_usec, time_end_usec; off_t offset; struct timeval now; fd = open(fname, O_RDONLY); if (fd < 0) tst_brkm(TBROK | TERRNO, cleanup, "Failed to open %s", fname); if (do_readahead) { /* read ahead in chunks, 2MB is maximum since 3.15-rc1 */ for (i = 0; i < fsize; i += 2*1024*1024) { TEST(ltp_syscall(__NR_readahead, fd, (off64_t) i, 2*1024*1024)); if (TEST_RETURN != 0) break; } check_ret(0); *cached = get_cached_size(); /* offset of file shouldn't change after readahead */ offset = lseek(fd, 0, SEEK_CUR); if (offset == (off_t) - 1) tst_brkm(TBROK | TERRNO, cleanup, "lseek failed"); if (offset == 0) tst_resm(TPASS, "offset is still at 0 as expected"); else tst_resm(TFAIL, "offset has changed to: %lu", offset); } if (gettimeofday(&now, NULL) == -1) tst_brkm(TBROK | TERRNO, cleanup, "gettimeofday failed"); time_start_usec = now.tv_sec * 1000000 + now.tv_usec; read_bytes_start = get_bytes_read(); p = mmap(NULL, fsize, PROT_READ, MAP_SHARED | MAP_POPULATE, fd, 0); if (p == MAP_FAILED) tst_brkm(TBROK | TERRNO, cleanup, "mmap failed"); /* for old kernels, where MAP_POPULATE doesn't work, touch each page */ tmp = 0; for (i = 0; i < fsize; i += pagesize) tmp = tmp ^ p[i]; /* prevent gcc from optimizing out loop above */ if (tmp != 0) tst_brkm(TBROK, NULL, "This line should not be reached"); if (!do_readahead) *cached = get_cached_size(); if (munmap(p, fsize) == -1) tst_brkm(TBROK | TERRNO, cleanup, "munmap failed"); *read_bytes = get_bytes_read() - read_bytes_start; if (gettimeofday(&now, NULL) == -1) tst_brkm(TBROK | TERRNO, cleanup, "gettimeofday failed"); time_end_usec = now.tv_sec * 1000000 + now.tv_usec; *usec = time_end_usec - time_start_usec; if (close(fd) == -1) tst_brkm(TBROK | TERRNO, cleanup, "close failed"); }
/* read_testfile - mmap testfile and read every page. * This functions measures how many I/O and time it takes to fully * read contents of test file. * * @do_readahead: call readahead prior to reading file content? * @fname: name of file to test * @fsize: how many bytes to read/mmap * @read_bytes: returns difference of bytes read, parsed from /proc/<pid>/io * @usec: returns how many microsecond it took to go over fsize bytes * @cached: returns cached kB from /proc/meminfo */ static void read_testfile(int do_readahead, const char *fname, size_t fsize, unsigned long *read_bytes, long *usec, unsigned long *cached) { int fd; size_t i = 0; long read_bytes_start; unsigned char *p, tmp; unsigned long time_start_usec, time_end_usec; unsigned long cached_start, max_ra_estimate = 0; off_t offset = 0; struct timeval now; fd = SAFE_OPEN(cleanup, fname, O_RDONLY); if (do_readahead) { cached_start = get_cached_size(); do { TEST(readahead(fd, offset, fsize - offset)); if (TEST_RETURN != 0) { check_ret(0); break; } /* estimate max readahead size based on first call */ if (!max_ra_estimate) { *cached = get_cached_size(); if (*cached > cached_start) { max_ra_estimate = (1024 * (*cached - cached_start)); tst_resm(TINFO, "max ra estimate: %lu", max_ra_estimate); } max_ra_estimate = MAX(max_ra_estimate, MIN_SANE_READAHEAD); } i++; offset += max_ra_estimate; } while ((size_t)offset < fsize); tst_resm(TINFO, "readahead calls made: %zu", i); *cached = get_cached_size(); /* offset of file shouldn't change after readahead */ offset = lseek(fd, 0, SEEK_CUR); if (offset == (off_t) - 1) tst_brkm(TBROK | TERRNO, cleanup, "lseek failed"); if (offset == 0) tst_resm(TPASS, "offset is still at 0 as expected"); else tst_resm(TFAIL, "offset has changed to: %lu", offset); } if (gettimeofday(&now, NULL) == -1) tst_brkm(TBROK | TERRNO, cleanup, "gettimeofday failed"); time_start_usec = now.tv_sec * 1000000 + now.tv_usec; read_bytes_start = get_bytes_read(); p = mmap(NULL, fsize, PROT_READ, MAP_SHARED | MAP_POPULATE, fd, 0); if (p == MAP_FAILED) tst_brkm(TBROK | TERRNO, cleanup, "mmap failed"); /* for old kernels, where MAP_POPULATE doesn't work, touch each page */ tmp = 0; for (i = 0; i < fsize; i += pagesize) tmp = tmp ^ p[i]; /* prevent gcc from optimizing out loop above */ if (tmp != 0) tst_brkm(TBROK, NULL, "This line should not be reached"); if (!do_readahead) *cached = get_cached_size(); SAFE_MUNMAP(cleanup, p, fsize); *read_bytes = get_bytes_read() - read_bytes_start; if (gettimeofday(&now, NULL) == -1) tst_brkm(TBROK | TERRNO, cleanup, "gettimeofday failed"); time_end_usec = now.tv_sec * 1000000 + now.tv_usec; *usec = time_end_usec - time_start_usec; SAFE_CLOSE(cleanup, fd); }
/* This is the main function that checks for a cached size and * possibly queries the widget class to compute the size if it's * not cached. If the for_size here is -1, then get_preferred_width() * or get_preferred_height() will be used. */ static void compute_size_for_orientation (GtkWidget *widget, GtkSizeGroupMode orientation, gint for_size, gint *minimum_size, gint *natural_size) { CachedSize *cached_size; gboolean found_in_cache = FALSE; gint min_size = 0; gint nat_size = 0; found_in_cache = get_cached_size (widget, orientation, for_size, &cached_size); if (!found_in_cache) { gint adjusted_min, adjusted_natural, adjusted_for_size = for_size; gtk_widget_ensure_style (widget); if (orientation == GTK_SIZE_GROUP_HORIZONTAL) { if (for_size < 0) { push_recursion_check (widget, orientation, for_size); GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, &min_size, &nat_size); pop_recursion_check (widget, orientation); } else { gint ignored_position = 0; gint minimum_height; gint natural_height; /* Pull the base natural height from the cache as it's needed to adjust * the proposed 'for_size' */ gtk_widget_get_preferred_height (widget, &minimum_height, &natural_height); /* convert for_size to unadjusted height (for_size is a proposed allocation) */ GTK_WIDGET_GET_CLASS (widget)->adjust_size_allocation (widget, GTK_ORIENTATION_VERTICAL, &minimum_height, &natural_height, &ignored_position, &adjusted_for_size); push_recursion_check (widget, orientation, for_size); GTK_WIDGET_GET_CLASS (widget)->get_preferred_width_for_height (widget, MAX (adjusted_for_size, minimum_height), &min_size, &nat_size); pop_recursion_check (widget, orientation); } } else { if (for_size < 0) { push_recursion_check (widget, orientation, for_size); GTK_WIDGET_GET_CLASS (widget)->get_preferred_height (widget, &min_size, &nat_size); pop_recursion_check (widget, orientation); } else { gint ignored_position = 0; gint minimum_width; gint natural_width; /* Pull the base natural width from the cache as it's needed to adjust * the proposed 'for_size' */ gtk_widget_get_preferred_width (widget, &minimum_width, &natural_width); /* convert for_size to unadjusted width (for_size is a proposed allocation) */ GTK_WIDGET_GET_CLASS (widget)->adjust_size_allocation (widget, GTK_ORIENTATION_HORIZONTAL, &minimum_width, &natural_width, &ignored_position, &adjusted_for_size); push_recursion_check (widget, orientation, for_size); GTK_WIDGET_GET_CLASS (widget)->get_preferred_height_for_width (widget, MAX (adjusted_for_size, minimum_width), &min_size, &nat_size); pop_recursion_check (widget, orientation); } } if (min_size > nat_size) { g_warning ("%s %p reported min size %d and natural size %d; natural size must be >= min size", G_OBJECT_TYPE_NAME (widget), widget, min_size, nat_size); } if (orientation == GTK_SIZE_GROUP_HORIZONTAL) _gtk_widget_set_width_request_needed (widget, FALSE); else _gtk_widget_set_height_request_needed (widget, FALSE); adjusted_min = min_size; adjusted_natural = nat_size; GTK_WIDGET_GET_CLASS (widget)->adjust_size_request (widget, orientation == GTK_SIZE_GROUP_HORIZONTAL ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL, &adjusted_min, &adjusted_natural); if (adjusted_min < min_size || adjusted_natural < nat_size) { g_warning ("%s %p adjusted size %s min %d natural %d must not decrease below min %d natural %d", G_OBJECT_TYPE_NAME (widget), widget, orientation == GTK_SIZE_GROUP_VERTICAL ? "vertical" : "horizontal", adjusted_min, adjusted_natural, min_size, nat_size); /* don't use the adjustment */ } else if (adjusted_min > adjusted_natural) { g_warning ("%s %p adjusted size %s min %d natural %d original min %d natural %d has min greater than natural", G_OBJECT_TYPE_NAME (widget), widget, orientation == GTK_SIZE_GROUP_VERTICAL ? "vertical" : "horizontal", adjusted_min, adjusted_natural, min_size, nat_size); /* don't use the adjustment */ } else { /* adjustment looks good */ min_size = adjusted_min; nat_size = adjusted_natural; } /* Update size-groups with our request and update our cached requests * with the size-group values in a single pass. */ _gtk_size_group_bump_requisition (widget, orientation, &min_size, &nat_size); commit_cached_size (widget, orientation, for_size, min_size, nat_size); } else { min_size = cached_size->minimum_size; nat_size = cached_size->natural_size; } if (minimum_size) *minimum_size = min_size; if (natural_size) *natural_size = nat_size; g_assert (min_size <= nat_size); GTK_NOTE (SIZE_REQUEST, g_print ("[%p] %s\t%s: %d is minimum %d and natural: %d (hit cache: %s)\n", widget, G_OBJECT_TYPE_NAME (widget), orientation == GTK_SIZE_GROUP_HORIZONTAL ? "width for height" : "height for width" , for_size, min_size, nat_size, found_in_cache ? "yes" : "no")); }
static void test_readahead(unsigned int n) { unsigned long read_bytes, read_bytes_ra; long long usec, usec_ra; unsigned long cached_high, cached_low, cached, cached_ra; int ret; struct tcase *tc = &tcases[n]; tst_res(TINFO, "Test #%d: %s", n, tc->tname); if (tc->use_overlay && !ovl_mounted) { tst_res(TCONF, "overlayfs is not configured in this kernel."); return; } create_testfile(tc->use_overlay); /* find out how much can cache hold if we read whole file */ read_testfile(tc, 0, testfile, testfile_size, &read_bytes, &usec, &cached); cached_high = get_cached_size(); sync(); drop_caches(); cached_low = get_cached_size(); cached_max = MAX(cached_max, cached_high - cached_low); tst_res(TINFO, "read_testfile(0)"); read_testfile(tc, 0, testfile, testfile_size, &read_bytes, &usec, &cached); if (cached > cached_low) cached = cached - cached_low; else cached = 0; sync(); drop_caches(); cached_low = get_cached_size(); tst_res(TINFO, "read_testfile(1)"); ret = read_testfile(tc, 1, testfile, testfile_size, &read_bytes_ra, &usec_ra, &cached_ra); if (ret == EINVAL) { if (tc->use_fadvise && (!tc->use_overlay || !fadvise_supported)) { fadvise_supported = 0; tst_res(TCONF, "CONFIG_ADVISE_SYSCALLS not configured " "in kernel?"); return; } if (!tc->use_overlay || !readahead_supported) { readahead_supported = 0; tst_res(TCONF, "readahead not supported on %s", tst_device->fs_type); return; } } if (ret) { tst_res(TFAIL | TTERRNO, "%s failed on %s", tc->use_fadvise ? "fadvise" : "readahead", tc->use_overlay ? "overlayfs" : tst_device->fs_type); return; } if (cached_ra > cached_low) cached_ra = cached_ra - cached_low; else cached_ra = 0; tst_res(TINFO, "read_testfile(0) took: %lli usec", usec); tst_res(TINFO, "read_testfile(1) took: %lli usec", usec_ra); if (has_file(PROC_IO_FNAME, 0)) { tst_res(TINFO, "read_testfile(0) read: %ld bytes", read_bytes); tst_res(TINFO, "read_testfile(1) read: %ld bytes", read_bytes_ra); /* actual number of read bytes depends on total RAM */ if (read_bytes_ra < read_bytes) tst_res(TPASS, "readahead saved some I/O"); else tst_res(TFAIL, "readahead failed to save any I/O"); } else { tst_res(TCONF, "Your system doesn't have /proc/self/io," " unable to determine read bytes during test"); } tst_res(TINFO, "cache can hold at least: %ld kB", cached_max); tst_res(TINFO, "read_testfile(0) used cache: %ld kB", cached); tst_res(TINFO, "read_testfile(1) used cache: %ld kB", cached_ra); if (cached_max * 1024 >= testfile_size) { /* * if cache can hold ~testfile_size then cache increase * for readahead should be at least testfile_size/2 */ if (cached_ra * 1024 > testfile_size / 2) tst_res(TPASS, "using cache as expected"); else if (!cached_ra) tst_res(TFAIL, "readahead failed to use any cache"); else tst_res(TWARN, "using less cache than expected"); } else { tst_res(TCONF, "Page cache on your system is too small " "to hold whole testfile."); } }
/* read_testfile - mmap testfile and read every page. * This functions measures how many I/O and time it takes to fully * read contents of test file. * * @do_readahead: call readahead prior to reading file content? * @fname: name of file to test * @fsize: how many bytes to read/mmap * @read_bytes: returns difference of bytes read, parsed from /proc/<pid>/io * @usec: returns how many microsecond it took to go over fsize bytes * @cached: returns cached kB from /proc/meminfo */ static int read_testfile(struct tcase *tc, int do_readahead, const char *fname, size_t fsize, unsigned long *read_bytes, long long *usec, unsigned long *cached) { int fd; size_t i = 0; long read_bytes_start; unsigned char *p, tmp; off_t offset = 0; fd = SAFE_OPEN(fname, O_RDONLY); if (do_readahead) { do { TEST(tc->readahead(fd, offset, fsize - offset)); if (TST_RET != 0) { SAFE_CLOSE(fd); return TST_ERR; } i++; offset += readahead_length; } while ((size_t)offset < fsize); tst_res(TINFO, "readahead calls made: %zu", i); *cached = get_cached_size(); /* offset of file shouldn't change after readahead */ offset = SAFE_LSEEK(fd, 0, SEEK_CUR); if (offset == 0) tst_res(TPASS, "offset is still at 0 as expected"); else tst_res(TFAIL, "offset has changed to: %lu", offset); } tst_timer_start(CLOCK_MONOTONIC); read_bytes_start = get_bytes_read(); p = SAFE_MMAP(NULL, fsize, PROT_READ, MAP_SHARED | MAP_POPULATE, fd, 0); /* for old kernels, where MAP_POPULATE doesn't work, touch each page */ tmp = 0; for (i = 0; i < fsize; i += pagesize) tmp = tmp ^ p[i]; /* prevent gcc from optimizing out loop above */ if (tmp != 0) tst_brk(TBROK, "This line should not be reached"); if (!do_readahead) *cached = get_cached_size(); SAFE_MUNMAP(p, fsize); *read_bytes = get_bytes_read() - read_bytes_start; tst_timer_stop(); *usec = tst_timer_elapsed_us(); SAFE_CLOSE(fd); return 0; }