summaryrefslogtreecommitdiff
path: root/libhurd-mm/madvise.c
diff options
context:
space:
mode:
Diffstat (limited to 'libhurd-mm/madvise.c')
-rw-r--r--libhurd-mm/madvise.c135
1 files changed, 135 insertions, 0 deletions
diff --git a/libhurd-mm/madvise.c b/libhurd-mm/madvise.c
new file mode 100644
index 0000000..5bdb962
--- /dev/null
+++ b/libhurd-mm/madvise.c
@@ -0,0 +1,135 @@
+/* madvise.c - madvise implementation.
+ Copyright (C) 2008 Free Software Foundation, Inc.
+ Written by Neal H. Walfield <neal@gnu.org>.
+
+ This file is part of the GNU Hurd.
+
+ GNU Hurd is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ GNU Hurd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with GNU Hurd. If not, see
+ <http://www.gnu.org/licenses/>. */
+
+#include <hurd/stddef.h>
+#include <hurd/addr.h>
+#include <hurd/as.h>
+#include <hurd/storage.h>
+#include <hurd/anonymous.h>
+#include <hurd/map.h>
+
+#include <sys/mman.h>
+#include <stdint.h>
+
+int
+madvise (void *addr, size_t length, int advice)
+{
+ if (((uintptr_t) addr & (PAGESIZE - 1)) != 0)
+ return EINVAL;
+ if ((length & (PAGESIZE - 1)) != 0)
+ return EINVAL;
+
+ switch (advice)
+ {
+ case MADV_NORMAL:
+ advice = pager_advice_normal;
+ break;
+ case MADV_RANDOM:
+ advice = pager_advice_random;
+ break;
+ case MADV_SEQUENTIAL:
+ advice = pager_advice_sequential;
+ break;
+ case MADV_WILLNEED:
+ advice = pager_advice_willneed;
+ break;
+ case MADV_DONTNEED:
+ advice = pager_advice_dontneed;
+ break;
+ default:
+ return EINVAL;
+ }
+
+ uintptr_t start = (uintptr_t) addr;
+ uintptr_t end = start + length - 1;
+
+ debug (0, "(%p, %x (%p), %d)", addr, length, end, advice);
+
+ struct region region = { (uintptr_t) addr, length };
+
+ maps_lock_lock ();
+
+ /* Find any pager that overlaps within the designated region. */
+ struct map *map = map_find (region);
+ if (! map)
+ /* There are none. We're done. */
+ {
+ maps_lock_unlock ();
+ return 0;
+ }
+
+ /* There may be pagers that come lexically before as well as after
+ PAGER. We start with PAGER and scan forward and then do the same
+ but scan backwards. */
+ struct map *prev = hurd_btree_map_prev (map);
+
+ int dir;
+ for (dir = 0; dir < 2; dir ++, map = prev)
+ for (;
+ map;
+ map = (dir == 0 ? hurd_btree_map_next (map)
+ : hurd_btree_map_prev (map)))
+ {
+ uintptr_t map_start = map->region.start;
+ uintptr_t map_end = map_start + map->region.length - 1;
+
+ debug (5, "(%x-%x): considering %x-%x",
+ start, end, map_start, map_end);
+
+ if (map_start > end || map_end < start)
+ break;
+
+ /*
+ map_start/map->offset map_end
+ / \ / \
+ v v v v
+ +------------------------------------+
+ | | <- pager
+ +------------------------------------+
+
+ ^ ^ ^ ^ ^ ^
+ \ | / \ | /
+ start end
+
+ */
+ uintptr_t s = map->offset;
+ uintptr_t l = map->region.length;
+ if (start > map_start)
+ {
+ s += start - map_start;
+ l -= start - map_start;
+ }
+ if (end < map_end)
+ l -= map_end - end;
+
+ if (map->pager->advise)
+ map->pager->advise (map->pager, s, l, advice);
+ }
+
+ maps_lock_unlock ();
+
+ return 0;
+}
+
+int
+posix_madvise (void *addr, size_t len, int advice)
+{
+ return madvise (addr, len, advice);
+}