summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorneal <neal>2008-06-27 15:17:27 +0000
committerneal <neal>2008-06-27 15:17:27 +0000
commitc314b447a633a3aa2d1e4a882675038fd381e873 (patch)
treec3d2625fc63af15c7e6200203a5347e68f192713
parent8305c7cd3d379fda42cee8c58783bf2ae52f4ff0 (diff)
hurd/
2008-06-27 Neal H. Walfield <neal@gnu.org> * activity.h (RM_activity_stats): Don't define. (RM_activity_info): Replace with this. (struct activity_stats): Add field pressure_local. Make field pressure an 8 bit integer. (struct activity_stats_buffer): Remove structure. (struct activity_info): Replace with this structure. (activity_info_stats): Define. (activity_info_pressure): Likewise. (activity_stats): Remove method. (activity_info): Replace with this. viengoos/ 2008-06-27 Neal H. Walfield <neal@gnu.org> * activity.h (struct activity): Add fields free_bad_karma, free_goal, free_allocations and free_initial_allocation. * rm.h (rm_method_id_string): Replace RM_activity_stats handling with RM_activity_info handling. * thread.h (THREAD_WAIT_STATS): Remove. (THREAD_WAIT_ACTIVITY_INFO): Replace with this. (struct thread): Add field wait_reason_arg2. * ager.c (update_stats): Account for local pressure when calculating availability. Don't use activity_stats to inform threads of new statistics but activity_info. * object.c (object_desc_claim): Adjust the activity's FREE_GOAL, FREE_ALLOCATIONS, and FRAMES_EXCLUDED fields as appropriate. * pager.c (pager_collect): When selecting a victim, don't include an activity's excluded frames in its allocation. Don't even consider activities for which FREE_ALLOCATIONS is non-zero. Having selected a victim, don't increase the pressure do drastically. Update the local pressure. Having selected a victim from which to revoke pages, send any waiting threads a message to free memory. If VICTIM->FREE_BAD_KARMA is 0, assume that the memory will be freed and give the activity 100 claims to do it. If the activity has bad karma, decrease it by one. * server.c (server_loop): Replace activity_stats implementation with implementation appropriate for activity_info. hieronymus/ 2008-06-27 Neal H. Walfield <neal@gnu.org> * hieronymus.c (do_gather_stats): Change to use activity_info instead of activity_stats. (main): Introduce code to delay process creation. ruth/ 2008-06-27 Neal H. Walfield <neal@gnu.org> * ruth.c (main): Update to use activity_info instead of activity_stats. benchmarks/ 2008-06-27 Neal H. Walfield <neal@gnu.org> * shared-memory-distribution.c (main): Update to use activity_info instead of activity_status. * activity-distribution.c (main): Likewise. * GCbench.c: Include <stdint.h> and <stdbool.h>. (now): New function. (struct stats): Add fields time, gcs and iter. (have_a_hog): New variable. (mem_hog) [__gnu_hurd_viengoos__]: Rename from this... (helper) [__gnu_hurd_viengoos__]: ... to this. Use activity_info, not activity_stats. Gather more data. (helper) [! __gnu_hurd_viengoos__]: New function. (tid): Rename from this... (helper_tid): ... to this. (helper_fork) [__gnu_hurd_viengoos__]: Name activities. (main): Improve output. benchmarks/boehm-gc/ 2008-06-27 Neal H. Walfield <neal@gnu.org> * patches/05-viengoos-scheduler.patch: Update to use activity_info in place of activity_stats. Listen for pressure messages and act appropriately. Tighten adaptive code. Improve profiling code.
-rw-r--r--benchmarks/ChangeLog19
-rw-r--r--benchmarks/GCbench.c219
-rw-r--r--benchmarks/activity-distribution.c46
-rw-r--r--benchmarks/boehm-gc/ChangeLog6
-rw-r--r--benchmarks/boehm-gc/patches/05-viengoos-scheduler.patch580
-rw-r--r--benchmarks/shared-memory-distribution.c55
-rw-r--r--hieronymus/ChangeLog7
-rw-r--r--hieronymus/hieronymus.c34
-rw-r--r--hurd/ChangeLog13
-rw-r--r--hurd/activity.h80
-rw-r--r--ruth/ChangeLog5
-rw-r--r--ruth/ruth.c10
-rw-r--r--viengoos/ChangeLog26
-rw-r--r--viengoos/activity.h15
-rw-r--r--viengoos/ager.c75
-rw-r--r--viengoos/object.c57
-rw-r--r--viengoos/pager.c112
-rw-r--r--viengoos/rm.h4
-rw-r--r--viengoos/server.c64
-rw-r--r--viengoos/thread.h9
20 files changed, 1040 insertions, 396 deletions
diff --git a/benchmarks/ChangeLog b/benchmarks/ChangeLog
index 02bf58b..98f58f4 100644
--- a/benchmarks/ChangeLog
+++ b/benchmarks/ChangeLog
@@ -1,3 +1,22 @@
+2008-06-27 Neal H. Walfield <neal@gnu.org>
+
+ * shared-memory-distribution.c (main): Update to use activity_info
+ instead of activity_status.
+ * activity-distribution.c (main): Likewise.
+
+ * GCbench.c: Include <stdint.h> and <stdbool.h>.
+ (now): New function.
+ (struct stats): Add fields time, gcs and iter.
+ (have_a_hog): New variable.
+ (mem_hog) [__gnu_hurd_viengoos__]: Rename from this...
+ (helper) [__gnu_hurd_viengoos__]: ... to this. Use activity_info,
+ not activity_stats. Gather more data.
+ (helper) [! __gnu_hurd_viengoos__]: New function.
+ (tid): Rename from this...
+ (helper_tid): ... to this.
+ (helper_fork) [__gnu_hurd_viengoos__]: Name activities.
+ (main): Improve output.
+
2008-06-23 Neal H. Walfield <neal@gnu.org>
* GCbench.c (struct stats) [__gnu_hurd_viengoos__]: New structure.
diff --git a/benchmarks/GCbench.c b/benchmarks/GCbench.c
index 44e3cc2..ab05665 100644
--- a/benchmarks/GCbench.c
+++ b/benchmarks/GCbench.c
@@ -52,18 +52,41 @@
# include <gc/gc.h>
#endif
+#include <stdint.h>
+#include <stdbool.h>
+
+static inline uint64_t
+now (void)
+{
+ struct timeval t;
+ struct timezone tz;
+
+ if (gettimeofday( &t, &tz ) == -1)
+ return 0;
+ return (t.tv_sec * 1000000ULL + t.tv_usec);
+}
+uint64_t epoch;
+
#include <assert.h>
-#ifdef __gnu_hurd_viengoos__
-#define STATS 1000
+#define STATS 5000
+
struct stats
{
int alloced[2];
int available[2];
+ uint64_t time;
int period;
-} stats[1000];
+ int gcs;
+ int iter;
+} stats[STATS];
int stat_count;
+static int done;
+
+static int iter;
+
+#ifdef __gnu_hurd_viengoos__
#include <hurd/storage.h>
#include <hurd/cap.h>
#include <hurd/activity.h>
@@ -71,18 +94,17 @@ int stat_count;
#include <hurd/anonymous.h>
#include <string.h>
-static int done;
-
addr_t gc_activity;
addr_t hog_activity;
+bool have_a_hog = false;
void *
-mem_hog (void *arg)
+helper (void *arg)
{
pthread_setactivity_np (hog_activity);
- struct activity_stats_buffer buffer;
+ struct activity_info info;
void wait_read_stats (void)
{
@@ -90,82 +112,94 @@ mem_hog (void *arg)
/* First the main thread. */
error_t err;
- err = rm_activity_stats (gc_activity,
- stat_count == 0 ? 0 : buffer.stats[0].period + 1,
- &buffer, &count);
+
+ err = rm_activity_info (gc_activity, activity_info_stats,
+ stat_count == 0
+ ? 0 : stats[stat_count - 1].period + 1,
+ &info);
assert_perror (err);
+ assert (info.event == activity_info_stats);
+ assert (info.stats.count > 0);
stats[stat_count].alloced[0]
- = buffer.stats[0].clean + buffer.stats[0].dirty
- + buffer.stats[0].pending_eviction;
- stats[stat_count].available[0] = buffer.stats[0].available;
+ = info.stats.stats[0].clean + info.stats.stats[0].dirty
+ + info.stats.stats[0].pending_eviction;
+ stats[stat_count].available[0] = info.stats.stats[0].available;
- stats[stat_count].period = buffer.stats[0].period;
+ stats[stat_count].time = now () - epoch;
+ stats[stat_count].period = info.stats.stats[0].period;
+ stats[stat_count].gcs = GC_gc_no;
+ stats[stat_count].iter = iter;
/* Then, the hog. */
- err = rm_activity_stats (hog_activity,
- stat_count == 0 ? 0 : buffer.stats[0].period,
- &buffer, &count);
+ err = rm_activity_info (hog_activity, activity_info_stats,
+ stat_count == 0
+ ? 0 : stats[stat_count - 1].period + 1,
+ &info);
assert_perror (err);
+ assert (info.event == activity_info_stats);
+ assert (info.stats.count > 0);
stats[stat_count].alloced[1]
- = buffer.stats[0].clean + buffer.stats[0].dirty
- + buffer.stats[0].pending_eviction;
- stats[stat_count].available[1] = buffer.stats[0].available;
+ = info.stats.stats[0].clean + info.stats.stats[0].dirty
+ + info.stats.stats[0].pending_eviction;
+ stats[stat_count].available[1] = info.stats.stats[0].available;
stat_count ++;
if (stat_count % 10 == 0)
debug (0, DEBUG_BOLD ("Period %d"), stat_count);
}
- /* Wait a minute before starting. */
- int i;
- for (i = 0; i < 30; i ++)
- wait_read_stats ();
+ if (have_a_hog)
+ {
+ /* Wait a minute before starting. */
+ int i;
+ for (i = 0; i < 20; i ++)
+ wait_read_stats ();
- /* Now, allocate a 10MB chunk of memory every 2 seconds until we've
- allocate half the initially available memory. */
+ /* Now, allocate a 10MB chunk of memory every 2 seconds until we've
+ allocate half the initially available memory. */
- int available = stats[1].available[0] * PAGESIZE;
+ int available = stats[1].available[0] * PAGESIZE;
- printf (DEBUG_BOLD ("mem hog starting (avail: %d, period: %d)!") "\n",
- available, stats[stat_count - 1].period);
+ printf (DEBUG_BOLD ("mem hog starting (avail: %d)!") "\n", available);
- /* The chunk size. */
- int s = 5 * 1024 * 1024;
- int total = available / 2 / s;
- struct anonymous_pager *pagers[total];
- void *buffers[total];
+ /* The chunk size. */
+ int s = 5 * 1024 * 1024;
+ int total = available / 2 / s;
+ struct anonymous_pager *pagers[total];
+ void *buffers[total];
- int c;
- for (c = 0; c < total && ! done; c ++)
- {
- pagers[c]
- = anonymous_pager_alloc (hog_activity, NULL, s, MAP_ACCESS_ALL,
- OBJECT_POLICY (false, OBJECT_PRIORITY_LRU), 0,
- NULL, &buffers[c]);
- assert (pagers[c]);
- assert (buffers[c]);
+ int c;
+ for (c = 0; c < total && ! done; c ++)
+ {
+ pagers[c]
+ = anonymous_pager_alloc (hog_activity, NULL, s, MAP_ACCESS_ALL,
+ OBJECT_POLICY (false, OBJECT_PRIORITY_LRU), 0,
+ NULL, &buffers[c]);
+ assert (pagers[c]);
+ assert (buffers[c]);
- memset (buffers[c], 0, s);
+ memset (buffers[c], 0, s);
- wait_read_stats ();
- }
+ wait_read_stats ();
+ }
- /* Wait a minute before freeing. */
- for (i = 0; i < 30 && ! done; i ++)
- wait_read_stats ();
+ /* Wait a minute before freeing. */
+ for (i = 0; i < 20 && ! done; i ++)
+ wait_read_stats ();
- printf (DEBUG_BOLD ("mem hog releasing memory!") "\n");
+ printf (DEBUG_BOLD ("mem hog releasing memory!") "\n");
- /* Release the memory. */
- for (i = 0; i < total && ! done; i ++)
- {
- wait_read_stats ();
+ /* Release the memory. */
+ for (i = 0; i < total && ! done; i ++)
+ {
+ wait_read_stats ();
- printf (DEBUG_BOLD ("release: %d!") "\n", i);
- anonymous_pager_destroy (pagers[i]);
+ printf (DEBUG_BOLD ("release: %d!") "\n", i);
+ anonymous_pager_destroy (pagers[i]);
+ }
}
/* Finally, wait until the main thread is done. */
@@ -174,24 +208,55 @@ mem_hog (void *arg)
return 0;
}
+#else
+#define have_a_hog false
+
+void *
+helper (void *arg)
+{
+ while (! done)
+ {
+ sleep (2);
+ stats[stat_count].alloced[0] = GC_get_heap_size ();
+ stats[stat_count].available[0] = 0;
+ stats[stat_count].time = now () - epoch;
+ stats[stat_count].gcs = GC_gc_no;
+ stats[stat_count].iter = iter;
+ stat_count ++;
+ }
+
+ return 0;
+}
+#endif
+
-pthread_t tid;
+pthread_t helper_tid;
void
-mem_hog_fork (void)
+helper_fork (void)
{
+ int err;
+
+#ifdef __gnu_hurd_viengoos__
gc_activity = storage_alloc (ADDR_VOID,
cap_activity_control, STORAGE_LONG_LIVED,
OBJECT_POLICY_DEFAULT, ADDR_VOID).addr;
if (ADDR_IS_VOID (gc_activity))
panic ("Failed to allocate main activity");
+ struct object_name name;
+ snprintf (&name.name[0], sizeof (name.name), "gc.%x", l4_myself ());
+ rm_object_name (ADDR_VOID, gc_activity, name);
+
hog_activity = storage_alloc (ADDR_VOID,
cap_activity_control, STORAGE_LONG_LIVED,
OBJECT_POLICY_DEFAULT, ADDR_VOID).addr;
if (ADDR_IS_VOID (hog_activity))
panic ("Failed to allocate hog activity");
+ snprintf (&name.name[0], sizeof (name.name), "hog.%x", l4_myself ());
+ rm_object_name (ADDR_VOID, hog_activity, name);
+
/* We give the main thread and the hog the same priority and
weight. */
struct activity_policy in, out;
@@ -202,7 +267,6 @@ mem_hog_fork (void)
in.child_rel.priority = 2;
in.child_rel.weight = 20;
- error_t err;
err = rm_activity_policy (ADDR_VOID,
ACTIVITY_POLICY_CHILD_REL_SET, in, &out);
assert (err == 0);
@@ -217,11 +281,10 @@ mem_hog_fork (void)
pthread_setactivity_np (gc_activity);
+#endif
- err = pthread_create (&tid, NULL, mem_hog, NULL);
- assert_perror (err);
+ pthread_create (&helper_tid, NULL, helper, NULL);
}
-#endif
#if 1
#define DEBUG(fmt, ...)
@@ -400,10 +463,11 @@ int main() {
extern int GC_print_stats;
// GC_print_stats = 1;
extern int GC_viengoos_scheduler;
- // GC_viengoos_scheduler = 0;
-#ifdef __gnu_hurd_viengoos__
- mem_hog_fork ();
-#endif
+ GC_viengoos_scheduler = 1;
+
+ helper_fork ();
+ epoch = now ();
+
Node root;
Node longLivedTree;
@@ -465,10 +529,9 @@ int main() {
PrintDiagnostics();
#define ITERATIONS 100
- int j;
- for (j = 0; j < ITERATIONS; j ++)
+ for (iter = 0; iter < ITERATIONS; iter ++)
{
- printf ("Iteration %d\n", j);
+ printf ("Iteration %d\n", iter);
for (d = kMinTreeDepth; d <= kMaxTreeDepth; d += 2) {
TimeConstruction(d);
}
@@ -495,22 +558,28 @@ int main() {
dump_profile();
# endif
-#ifdef __gnu_hurd_viengoos__
done = 1;
void *status;
- pthread_join (tid, &status);
+ pthread_join (helper_tid, &status);
{
+ printf ("%s scheduler, %smemory hog\n"
+ "time\tgc alloc'd\tgc avail\thog alloc'd\thog avail\tgcs\titeration\n",
+ GC_viengoos_scheduler ? "Viengoos" : "Boehm",
+ have_a_hog ? "" : "no ");
+
int i;
- printf ("period, main alloc'd + hog alloc'd, main avail, hog alloc'd, hog avail\n");
for (i = 0; i < stat_count; i ++)
{
- printf ("%d %d %d %d %d\n",
+ printf ("%lld.%lld\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n",
+ stats[i].time / 1000000,
+ (stats[i].time / 100000) % 10,
stats[i].period,
stats[i].alloced[0], stats[i].available[0],
- stats[i].alloced[1], stats[i].available[1]);
+ stats[i].alloced[1], stats[i].available[1],
+ stats[i].gcs,
+ stats[i].iter);
}
}
-#endif
}
diff --git a/benchmarks/activity-distribution.c b/benchmarks/activity-distribution.c
index e287d28..cffc288 100644
--- a/benchmarks/activity-distribution.c
+++ b/benchmarks/activity-distribution.c
@@ -5,6 +5,7 @@
#include <errno.h>
#include <string.h>
#include <pthread.h>
+#include <assert.h>
#include <hurd/activity.h>
#include <hurd/storage.h>
@@ -43,10 +44,11 @@ main (int argc, char *argv[])
ADDR_VOID).addr;
struct activity_policy in;
+ in.sibling_rel.priority = i == 0 ? 2 : 1;
in.sibling_rel.weight = i + 1;
struct activity_policy out;
err = rm_activity_policy (activities[i],
- ACTIVITY_POLICY_SIBLING_REL_WEIGHT_SET, in,
+ ACTIVITY_POLICY_SIBLING_REL_SET, in,
&out);
assert (err == 0);
}
@@ -58,14 +60,14 @@ main (int argc, char *argv[])
int available;
{
- int count;
- struct activity_stats_buffer buffer;
+ struct activity_info info;
- err = rm_activity_stats (activity, 1, &buffer, &count);
+ err = rm_activity_info (activity, activity_info_stats, 1, &info);
assert (err == 0);
- assert (count > 0);
+ assert (info.event == activity_info_stats);
+ assert (info.stats.count >= 1);
- available = buffer.stats[0].available * PAGESIZE;
+ available = info.stats.stats[0].available * PAGESIZE;
}
printf ("%d kb memory available\n", available / 1024);
@@ -153,26 +155,29 @@ main (int argc, char *argv[])
uintptr_t next_period = 0;
for (i = 0; i < ITERATIONS; i ++)
{
- debug (0, DEBUG_BOLD ("starting iteration %d (%x)"), i, l4_myself ());
+ printf ("Iteration: %d\n", i);
- int count;
- struct activity_stats_buffer buffer;
+ struct activity_info info;
- rm_activity_stats (activity, next_period, &buffer, &count);
- assert (count > 0);
+ rm_activity_info (activity, activity_info_stats,
+ next_period, &info);
+ assert (info.event == activity_info_stats);
+ assert (info.stats.count > 0);
if (i != 0)
- assertx (buffer.stats[0].period != stats[i - 1][0].period,
+ assertx (info.stats.stats[0].period != stats[i - 1][0].period,
"%d == %d",
- buffer.stats[0].period, stats[i - 1][0].period);
+ info.stats.stats[0].period, stats[i - 1][0].period);
- stats[i][0] = buffer.stats[0];
+ stats[i][0] = info.stats.stats[0];
int j;
for (j = 0; j < THREADS; j ++)
{
- rm_activity_stats (activities[j], next_period, &buffer, &count);
- assert (count > 0);
- stats[i][1 + j] = buffer.stats[0];
+ rm_activity_info (activity, activity_info_stats,
+ next_period, &info);
+ assert (info.event == activity_info_stats);
+ assert (info.stats.count > 0);
+ stats[i][1 + j] = info.stats.stats[0];
}
next_period = stats[i][0].period + 1;
@@ -194,10 +199,13 @@ main (int argc, char *argv[])
{
int j;
- printf ("%d ", (int) stats[i][0].period);
+ printf ("%d", (int) stats[i][0].period);
for (j = 0; j < 1 + THREADS; j ++)
- printf ("%d ", (int) stats[i][j].clean + (int) stats[i][j].dirty);
+ printf ("\t%d\t%d\t%d\t%d", (int) stats[i][j].clean,
+ (int) stats[i][j].dirty,
+ (int) stats[i][j].pending_eviction,
+ (int) stats[i][j].discarded);
printf ("\n");
}
diff --git a/benchmarks/boehm-gc/ChangeLog b/benchmarks/boehm-gc/ChangeLog
index ba04584..ae9ad6d 100644
--- a/benchmarks/boehm-gc/ChangeLog
+++ b/benchmarks/boehm-gc/ChangeLog
@@ -1,3 +1,9 @@
+2008-06-27 Neal H. Walfield <neal@gnu.org>
+
+ * patches/05-viengoos-scheduler.patch: Update to use activity_info
+ in place of activity_stats. Listen for pressure messages and act
+ appropriately. Tighten adaptive code. Improve profiling code.
+
2008-06-26 Neal H. Walfield <neal@gnu.org>
* Makefile.am (noinst_LIBRARIES): Remove target.
diff --git a/benchmarks/boehm-gc/patches/05-viengoos-scheduler.patch b/benchmarks/boehm-gc/patches/05-viengoos-scheduler.patch
index 816103d..824eba9 100644
--- a/benchmarks/boehm-gc/patches/05-viengoos-scheduler.patch
+++ b/benchmarks/boehm-gc/patches/05-viengoos-scheduler.patch
@@ -5,9 +5,11 @@ exit $?
Patch to support the Viengoos specific scheduler. To disable it, set
GC_viengoos_scheduler to 0.
-diff -upr gc-7.0/allchblk.c gc/allchblk.c
---- gc-7.0/allchblk.c 2007-06-07 02:40:07.000000000 +0200
-+++ gc/allchblk.c 2008-06-22 13:13:44.000000000 +0200
+
+Only in gc: 05-viengoos-scheduler.patch.applied
+diff -upr -x Makefile.in -x configure -x config.guess -x config.sub -x aclocal.m4 gc-old/allchblk.c gc/allchblk.c
+--- gc-old/allchblk.c 2007-06-07 02:40:07.000000000 +0200
++++ gc/allchblk.c 2008-06-27 17:01:49.000000000 +0200
@@ -117,7 +117,9 @@ void GC_print_hblkfreelist()
while (h != 0) {
hhdr = HDR(h);
@@ -19,7 +21,7 @@ diff -upr gc-7.0/allchblk.c gc/allchblk.c
total_free += sz;
if (GC_is_black_listed(h, HBLKSIZE) != 0) {
GC_printf("start black listed\n");
-@@ -381,31 +383,130 @@ void GC_add_to_fl(struct hblk *h, hdr *h
+@@ -381,31 +383,135 @@ void GC_add_to_fl(struct hblk *h, hdr *h
#ifdef USE_MUNMAP
@@ -46,10 +48,15 @@ diff -upr gc-7.0/allchblk.c gc/allchblk.c
+ start:
+ if (GC_viengoos_scheduler
+ && (GC_get_heap_size() - GC_unmapped_bytes
-+ < 6 * (GC_available_bytes / 8))) {
-+ /* The number of mapped bytes is at most 6/8s the available
++ < 7 * (GC_available_bytes / 8))) {
++ /* The number of mapped bytes is at most 7/8s the available
+ memory. That's good enough for now. (Recall: the high-water
-+ mark is 7/8s the available memory.) */
++ mark is 15/16s the available memory.) */
++ if (0)
++ printf ("%x: After unmapping %d used (%d available)\n",
++ l4_myself (),
++ (GC_get_heap_size() - GC_unmapped_bytes) / PAGESIZE,
++ GC_available_bytes / PAGESIZE);
+ return;
+ }
+
@@ -160,7 +167,7 @@ diff -upr gc-7.0/allchblk.c gc/allchblk.c
}
/* Merge all unmapped blocks that are adjacent to other free */
-@@ -413,6 +514,12 @@ void GC_unmap_old(void)
+@@ -413,6 +519,12 @@ void GC_unmap_old(void)
/* fully mapped or fully unmapped. */
void GC_merge_unmapped(void)
{
@@ -173,7 +180,7 @@ diff -upr gc-7.0/allchblk.c gc/allchblk.c
struct hblk * h, *next;
hdr * hhdr, *nexthdr;
word size, nextsize;
-@@ -600,12 +707,26 @@ GC_allochblk_nth(size_t sz, int kind, un
+@@ -600,12 +712,26 @@ GC_allochblk_nth(size_t sz, int kind, un
size_needed = HBLKSIZE * OBJ_SZ_TO_BLOCKS(sz);
@@ -200,7 +207,7 @@ diff -upr gc-7.0/allchblk.c gc/allchblk.c
if (size_avail != size_needed
&& !GC_use_entire_heap
&& !GC_dont_gc
-@@ -642,7 +763,7 @@ GC_allochblk_nth(size_t sz, int kind, un
+@@ -642,7 +768,7 @@ GC_allochblk_nth(size_t sz, int kind, un
next_size = (signed_word)(thishdr -> hb_sz);
if (next_size < size_avail
&& next_size >= size_needed
@@ -209,7 +216,7 @@ diff -upr gc-7.0/allchblk.c gc/allchblk.c
continue;
}
}
-@@ -748,10 +869,12 @@ GC_allochblk_nth(size_t sz, int kind, un
+@@ -748,10 +874,12 @@ GC_allochblk_nth(size_t sz, int kind, un
/* hbp may be on the wrong freelist; the parameter n */
/* is important. */
hbp = GC_get_first_part(hbp, hhdr, size_needed, n);
@@ -223,9 +230,10 @@ diff -upr gc-7.0/allchblk.c gc/allchblk.c
if (0 == hbp) return 0;
/* Add it to map of valid blocks */
-diff -upr -x config.guess -x config.sub -x Makefile -x Makefile.in gc-7.0/alloc.c gc/alloc.c
---- gc-7.0/alloc.c 2008-06-24 18:14:01.000000000 +0200
-+++ gc/alloc.c 2008-06-24 17:35:59.000000000 +0200
+Only in gc: allchblk.c~
+diff -upr -x Makefile.in -x configure -x config.guess -x config.sub -x aclocal.m4 gc-old/alloc.c gc/alloc.c
+--- gc-old/alloc.c 2008-06-27 17:02:33.000000000 +0200
++++ gc/alloc.c 2008-06-27 17:01:08.000000000 +0200
@@ -15,6 +15,13 @@
*
*/
@@ -240,24 +248,140 @@ diff -upr -x config.guess -x config.sub -x Makefile -x Makefile.in gc-7.0/alloc.
# include "private/gc_priv.h"
-@@ -34,8 +41,7 @@ now (void)
+@@ -24,68 +31,7 @@
+ # include <sys/types.h>
+ # endif
- if (gettimeofday( &t, &tz ) == -1)
- return 0;
+-uint64_t gc_time;
+-
+-static inline uint64_t
+-now (void)
+-{
+- struct timeval t;
+- struct timezone tz;
+-
+- if (gettimeofday( &t, &tz ) == -1)
+- return 0;
- return (t.tv_sec * 1000000 + t.tv_usec);
-
-+ return (t.tv_sec * 1000000ULL + t.tv_usec);
- }
+-}
+-
+-static int timing;
+-
+-static uint64_t
+-start_timing (const char *func, int lineno)
+-{
+- static const char *timing_func;
+- static int timing_lineno;
+-
+- if (timing)
+- {
+- printf ("Timing for %s:%d but start_timing called from %s:%d\n",
+- timing_func, timing_lineno, func, lineno);
+- assert (! timing);
+- }
+-
+- timing_func = func;
+- timing_lineno = lineno;
+-
+- timing = 1;
+-
+- return now ();
+-}
+-
+-static void
+-end_timing (const char *func, int lineno, uint64_t start)
+-{
+- uint64_t end = now ();
+-
+- if (! timing)
+- {
+- printf ("Timing not started by end_timing called from %s:%d!\n",
+- func, lineno);
+- assert (! timing);
+- }
+- timing = 0;
+-
+- assert (start <= end);
+- gc_time += end - start;
+-}
+-
+-#define start_timing() \
+- do { \
+- uint64_t start = start_timing (__FUNCTION__, __LINE__)
+-
+-#define end_timing() \
+- end_timing (__FUNCTION__, __LINE__, start); \
+- } while (0)
+-
++#include "profile.h"
- static int timing;
-@@ -286,9 +292,106 @@ void GC_clear_a_few_frames()
+ /*
+ * Separate free lists are maintained for different sized objects
+@@ -286,9 +232,134 @@ void GC_clear_a_few_frames()
/* limits used by blacklisting. */
static word GC_collect_at_heapsize = (word)(-1);
+int GC_viengoos_scheduler = 1;
-+int GC_available_bytes;
++int GC_available_bytes = (256 + 128) * 1024 * 1024;
+int GC_could_unmap;
+
++#define THRESHOLD (15 * (GC_available_bytes / 16))
++
++#ifdef __gnu_hurd_viengoos__
++static void *
++gather_stats (void *arg)
++{
++ pthread_detach (pthread_self ());
++
++ int period = 0;
++ for (;;)
++ {
++ struct activity_info info;
++ error_t err = rm_activity_info (ACTIVITY,
++ activity_info_stats
++ | activity_info_pressure,
++ period, &info);
++ assert_perror (err);
++
++ switch (info.event)
++ {
++ case activity_info_stats:
++ GC_available_bytes = info.stats.stats[0].available_local * PAGESIZE;
++ period = info.stats.stats[0].period + 1;
++ break;
++
++ case activity_info_pressure:
++ if (-info.pressure.amount * PAGESIZE < GC_available_bytes)
++ GC_available_bytes -= -info.pressure.amount * PAGESIZE;
++ else
++ /* Huh? */
++ GC_available_bytes = 0;
++ break;
++
++ default:
++ panic ("Unknown event and unrequested event: %d", info.event);
++ }
++
++ if (0)
++ printf ("%x: %s: %d alloced: %d, heap: %d, "
++ "mapped:%d, unmapped: %d, available: %d, "
++ "low-water: %d\n",
++ l4_myself (),
++ info.event == activity_info_stats
++ ? "Period" : DEBUG_BOLD ("PRESSURE"),
++ (int) (info.event == activity_info_stats
++ ? period : info.pressure.amount),
++ (int) GC_adj_bytes_allocd() / PAGESIZE,
++ (int) GC_get_heap_size () / PAGESIZE,
++ (int) (GC_get_heap_size () - GC_unmapped_bytes) / PAGESIZE,
++ (int) GC_unmapped_bytes / PAGESIZE,
++ (int) GC_available_bytes / PAGESIZE,
++ (int) THRESHOLD / PAGESIZE);
++ }
++}
++#endif
++
/* Have we allocated enough to amortize a collection? */
GC_bool GC_should_collect(void)
{
@@ -273,51 +397,23 @@ diff -upr -x config.guess -x config.sub -x Makefile -x Makefile.in gc-7.0/alloc.
+ collection, don't do a collection. */
+ return FALSE;
+
-+ static uint64_t last_check;
-+
+#ifdef __gnu_hurd_viengoos__
-+ static struct activity_stats_buffer stats;
-+
-+ struct timeval tp;
-+ gettimeofday (&tp, NULL);
-+ uint64_t t = tp.tv_sec * 1000000ULL + tp.tv_usec;
-+ if (! last_check || t - last_check > 1000000)
-+ /* Last check was more than a second ago. */
++ static int init;
++ if (! init)
+ {
-+ last_check = t;
-+
-+ int count;
-+ error_t err = rm_activity_stats (ACTIVITY, 1, &stats, &count);
++ pthread_t tid;
++ error_t err = pthread_create (&tid, NULL, gather_stats, NULL);
+ assert_perror (err);
+
-+ GC_available_bytes = stats.stats[0].available * PAGESIZE;
-+
-+ if (0)
-+ printf ("Period: %d alloced: %dkb, heap: %dkb, "
-+ "mapped:%dkb, unmapped: %dkb, available: %dkb, "
-+ "low-water: %dkb\n",
-+ (int) stats.stats[0].period,
-+ (int) alloced / 1024,
-+ (int) GC_get_heap_size () / 1024,
-+ (int) (GC_get_heap_size () - GC_unmapped_bytes) / 1024,
-+ (int) GC_unmapped_bytes / 1024,
-+ (int) GC_available_bytes / 1024,
-+ (int) 7 * (GC_available_bytes / 8 / 1024));
++ init = 1;
+ }
-+
-+ int period = stats.stats[0].period;
-+#else
-+#define GC_available_bytes ((256 + 128) * 1024 * 1024)
-+ int period = 0;
+#endif
+
-+ /* Do some clean up if the mapped memory is 7/8s the available
-+ memory. We choose 7/8s as we need to consider meta-data
-+ overhead and as we really want to avoid the system pager
-+ kicking in. */
-+ GC_bool r;
-+ r = ((GC_get_heap_size() - GC_unmapped_bytes)
-+ > 7 * (GC_available_bytes / 8));
++ /* Do some clean up if the mapped memory is more than 15/16s the
++ available memory. We choose 15/16s as we need to consider
++ meta-data overhead and as we really want to avoid the system
++ pager kicking in. */
++ GC_bool r = GC_get_heap_size() - GC_unmapped_bytes > THRESHOLD;
+
+ if (r && (GC_get_heap_size () - GC_unmapped_bytes - alloced
+ > GC_available_bytes / 3)){
@@ -325,8 +421,8 @@ diff -upr -x config.guess -x config.sub -x Makefile -x Makefile.in gc-7.0/alloc.
+ of the total available memory. Before doing a GC, try to
+ unmap some free mapped pages. */
+ GC_unmap_old ();
-+ r = ((GC_get_heap_size() - GC_unmapped_bytes)
-+ > 7 * (GC_available_bytes / 8));
++
++ r = GC_get_heap_size() - GC_unmapped_bytes > THRESHOLD;
+ }
+
+ if (r) {
@@ -337,17 +433,17 @@ diff -upr -x config.guess -x config.sub -x Makefile -x Makefile.in gc-7.0/alloc.
+ if (warning != GC_gc_no) {
+ warning = GC_gc_no;
+ if (0)
-+ printf ("(%d) Scheduling GC: (%d) alloced: %dkb, heap: %dkb, "
++ printf ("Scheduling GC: (%u) alloced: %dkb, heap: %dkb, "
+ "mapped: %dkb, unmapped: %dkb, available: %dkb, "
+ "low-water: %dkb\n",
-+ (int) period, GC_gc_no,
++ GC_gc_no,
+ (int) alloced / 1024,
+ (int) GC_get_heap_size () / 1024,
+ (int) (GC_get_heap_size () - GC_unmapped_bytes)
+ / 1024,
+ (int) GC_unmapped_bytes / 1024,
+ (int) GC_available_bytes / 1024,
-+ (int) 7 * (GC_available_bytes / 8 / 1024));
++ (int) THRESHOLD);
+ }
+ }
+
@@ -357,7 +453,38 @@ diff -upr -x config.guess -x config.sub -x Makefile -x Makefile.in gc-7.0/alloc.
return(GC_adj_bytes_allocd() >= min_bytes_allocd()
|| GC_heapsize >= GC_collect_at_heapsize);
}
-@@ -396,9 +499,17 @@ GC_bool GC_try_to_collect_inner(GC_stop_
+@@ -319,7 +390,7 @@ void GC_maybe_gc(void)
+ n_partial_gcs = 0;
+ return;
+ } else {
+- start_timing ();
++ profile_start (GC_TIMER);
+
+ # ifdef PARALLEL_MARK
+ GC_wait_for_reclaim();
+@@ -342,10 +413,10 @@ void GC_maybe_gc(void)
+ n_partial_gcs++;
+ }
+
+- end_timing ();
++ profile_end (GC_TIMER);
+ }
+
+- start_timing ();
++ profile_start (GC_TIMER);
+ /* We try to mark with the world stopped. */
+ /* If we run out of time, this turns into */
+ /* incremental marking. */
+@@ -365,7 +436,7 @@ void GC_maybe_gc(void)
+ }
+ }
+
+- end_timing ();
++ profile_end (GC_TIMER);
+ }
+ }
+
+@@ -396,10 +467,18 @@ GC_bool GC_try_to_collect_inner(GC_stop_
GC_log_printf(
"Initiating full world-stop collection %lu after %ld allocd bytes\n",
(unsigned long)GC_gc_no+1, (long)GC_bytes_allocd);
@@ -372,11 +499,59 @@ diff -upr -x config.guess -x config.sub -x Makefile -x Makefile.in gc-7.0/alloc.
}
- GC_bool ret = true;
+- start_timing ();
+ GC_bool ret = TRUE;
- start_timing ();
++ profile_start (GC_TIMER);
GC_promote_black_lists();
-@@ -1110,6 +1221,8 @@ ptr_t GC_allocobj(size_t gran, int kind)
+ /* Make sure all blocks have been reclaimed, so sweep routines */
+@@ -414,7 +493,7 @@ GC_bool GC_try_to_collect_inner(GC_stop_
+ && !GC_reclaim_all(stop_func, FALSE)) {
+ /* Aborted. So far everything is still consistent. */
+ ret = FALSE;
+- break;
++ goto out;
+ }
+ GC_invalidate_mark_state(); /* Flush mark stack. */
+ GC_clear_marks();
+@@ -432,7 +511,7 @@ GC_bool GC_try_to_collect_inner(GC_stop_
+ } /* else we claim the world is already still consistent. We'll */
+ /* finish incrementally. */
+ ret = FALSE;
+- break;
++ goto out;
+ }
+ GC_finish_collection();
+ if (GC_print_stats) {
+@@ -441,7 +520,8 @@ GC_bool GC_try_to_collect_inner(GC_stop_
+ MS_TIME_DIFF(current_time,start_time));
+ }
+
+- end_timing ();
++ out:
++ profile_end (GC_TIMER);
+ return ret;
+ }
+
+@@ -470,7 +550,7 @@ void GC_collect_a_little_inner(int n)
+
+ if (GC_dont_gc) return;
+ if (GC_incremental && GC_collection_in_progress()) {
+- start_timing ();
++ profile_start (GC_TIMER);
+
+ for (i = GC_deficit; i < GC_RATE*n; i++) {
+ if (GC_mark_some((ptr_t)0)) {
+@@ -498,7 +578,7 @@ void GC_collect_a_little_inner(int n)
+ if (GC_deficit > 0) GC_deficit -= GC_RATE*n;
+ if (GC_deficit < 0) GC_deficit = 0;
+
+- end_timing ();
++ profile_end (GC_TIMER);
+ } else {
+ GC_maybe_gc();
+ }
+@@ -1110,6 +1190,8 @@ ptr_t GC_allocobj(size_t gran, int kind)
void
GC_dump_stats (void)
{
@@ -385,39 +560,139 @@ diff -upr -x config.guess -x config.sub -x Makefile -x Makefile.in gc-7.0/alloc.
printf ("GC: "
#ifdef THREADS
"multi-threaded"
-@@ -1125,8 +1238,9 @@ GC_dump_stats (void)
+@@ -1125,10 +1207,123 @@ GC_dump_stats (void)
GC_incremental ? "generational" : "stop the world",
GC_dirty_maintained ? "" : "not ");
printf("%d collections\n", (int) GC_gc_no);
- printf ("%lld.%03lld seconds spent collecting\n",
- gc_time / 1000000, (gc_time % 1000000) / 1000);
-+ printf ("%lld.%03lld seconds spent collecting, %lld.%03lld mapping\n",
-+ gc_time / 1000000, (gc_time % 1000000) / 1000,
-+ gc_map_time / 1000000, (gc_map_time % 1000000) / 1000);
printf("Heap size: %d (%d kb)\n",
GC_get_heap_size(), GC_get_heap_size() / 1024);
printf("Total bytes allocated: %d (%d kb)\n",
-diff -upr -x config.guess -x config.sub -x Makefile -x Makefile.in gc-7.0/configure gc/configure
---- gc-7.0/configure 2007-06-30 17:40:25.000000000 +0200
-+++ gc/configure 2008-06-23 22:12:43.000000000 +0200
-@@ -21558,14 +21558,6 @@ fi
-
- if test -n "${with_cross_host}"; then
- cat >>confdefs.h <<\_ACEOF
--#define NO_CLOCK 1
--_ACEOF
--
-- cat >>confdefs.h <<\_ACEOF
--#define SMALL_CONFIG 1
--_ACEOF
--
-- cat >>confdefs.h <<\_ACEOF
- #define NO_DEBUGGING 1
- _ACEOF
-
-diff -upr -x config.guess -x config.sub -x Makefile -x Makefile.in gc-7.0/include/private/gcconfig.h gc/include/private/gcconfig.h
---- gc-7.0/include/private/gcconfig.h 2008-06-24 18:14:01.000000000 +0200
-+++ gc/include/private/gcconfig.h 2008-06-23 22:12:34.000000000 +0200
+ GC_get_total_bytes (), GC_get_total_bytes () / 1024);
++
++ profile_stats_dump ();
++}
++
++/* profile.c - Profiling support implementation.
++ Copyright (C) 2008 Free Software Foundation, Inc.
++ Written by Neal H. Walfield <neal@gnu.org>.
++
++ This file is part of the GNU Hurd.
++
++ The GNU Hurd 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 3 of the
++ License, or (at your option) any later version.
++
++ The 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
++ General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program. If not, see
++ <http://www.gnu.org/licenses/>. */
++
++#define SIZE 10
++struct site
++{
++ const char *name;
++ uint64_t time;
++ uint64_t start;
++ int calls;
++ int pending;
++} sites[SIZE];
++
++static uint64_t epoch;
++static uint64_t calls;
++static uint64_t total_time;
++/* Number of extant profiling calls. We only update total_time if
++ EXTANT is 0. The result is that the time spent profiling is
++ correct, and the percent of the time profile that a function has
++ been is more meaningful. */
++static int extant;
++
++static inline uint64_t
++now (void)
++{
++ struct timeval t;
++ struct timezone tz;
++
++ if (gettimeofday( &t, &tz ) == -1)
++ return 0;
++ return (t.tv_sec * 1000000ULL + t.tv_usec);
++}
++
++void
++profile_stats_dump (void)
++{
++ uint64_t n = now ();
++
++ int i;
++ for (i = 0; i < SIZE; i ++)
++ if (sites[i].calls)
++ printf ("%s:\t%d calls,\t%lld ms,\t%lld.%d us per call,\t"
++ "%d%% total time,\t%d%% profiled time\n",
++ sites[i].name,
++ sites[i].calls,
++ sites[i].time / 1000,
++ sites[i].time / sites[i].calls,
++ (int) ((10 * sites[i].time) / sites[i].calls) % 10,
++ (int) ((100 * sites[i].time) / (n - epoch)),
++ (int) ((100 * sites[i].time) / total_time));
++
++ printf ("profiled time: %lld ms, calls: %lld\n",
++ total_time / 1000, calls);
++ printf ("uptime: %lld ms\n", (n - epoch) / 1000);
++}
++
++void
++profile_start (uintptr_t id, const char *name)
++{
++ if (! epoch)
++ epoch = now ();
++
++ struct site *site = &sites[id];
++ site->name = name;
++
++ extant ++;
++
++ site->pending ++;
++ if (site->pending == 1)
++ site->start = now ();
+ }
++
++void
++profile_end (uintptr_t id, const char *name)
++{
++ struct site *site = &sites[id];
++ assert (site->pending);
++
++ extant --;
++
++ site->pending --;
++ if (site->pending == 0)
++ {
++ uint64_t n = now ();
++
++ site->time += n - site->start;
++
++ if (extant == 0)
++ total_time += n - site->start;
++
++ site->calls ++;
++ calls ++;
++ }
++}
++
+Only in gc: alloc.c~
+Only in gc: atomic_ops.c
+Only in gc: atomic_ops_sysdeps.S
+Only in gc: autom4te.cache
+diff -upr -x Makefile.in -x configure -x config.guess -x config.sub -x aclocal.m4 gc-old/include/private/gcconfig.h gc/include/private/gcconfig.h
+--- gc-old/include/private/gcconfig.h 2008-06-27 17:02:33.000000000 +0200
++++ gc/include/private/gcconfig.h 2008-06-26 10:41:11.000000000 +0200
@@ -1087,6 +1087,8 @@
# ifdef LINUX
# define OS_TYPE "LINUX"
@@ -440,9 +715,16 @@ diff -upr -x config.guess -x config.sub -x Makefile -x Makefile.in gc-7.0/includ
# endif
# ifdef DARWIN
# define OS_TYPE "DARWIN"
-diff -upr -x config.guess -x config.sub -x Makefile -x Makefile.in gc-7.0/malloc.c gc/malloc.c
---- gc-7.0/malloc.c 2007-06-06 22:48:35.000000000 +0200
-+++ gc/malloc.c 2008-06-23 22:12:34.000000000 +0200
+Only in gc: libatomic_ops
+Only in gc/libatomic_ops-1.2: .cvsignore
+Only in gc/libatomic_ops-1.2/doc: .cvsignore
+Only in gc/libatomic_ops-1.2/src/atomic_ops: .cvsignore
+Only in gc/libatomic_ops-1.2/src/atomic_ops/sysdeps: .cvsignore
+Only in gc/libatomic_ops-1.2/src: .cvsignore
+Only in gc/libatomic_ops-1.2/tests: .cvsignore
+diff -upr -x Makefile.in -x configure -x config.guess -x config.sub -x aclocal.m4 gc-old/malloc.c gc/malloc.c
+--- gc-old/malloc.c 2007-06-06 22:48:35.000000000 +0200
++++ gc/malloc.c 2008-06-26 10:41:13.000000000 +0200
@@ -269,7 +269,9 @@ void * GC_generic_malloc(size_t lb, int
LOCK();
if( EXPECT((op = *opp) == 0, 0) ) {
@@ -468,10 +750,10 @@ diff -upr -x config.guess -x config.sub -x Makefile -x Makefile.in gc-7.0/malloc
}
}
-diff -upr -x config.guess -x config.sub -x Makefile -x Makefile.in gc-7.0/os_dep.c gc/os_dep.c
---- gc-7.0/os_dep.c 2007-06-29 21:17:44.000000000 +0200
-+++ gc/os_dep.c 2008-06-24 17:35:30.000000000 +0200
-@@ -2064,6 +2064,75 @@ ptr_t GC_unmap_end(ptr_t start, size_t b
+diff -upr -x Makefile.in -x configure -x config.guess -x config.sub -x aclocal.m4 gc-old/os_dep.c gc/os_dep.c
+--- gc-old/os_dep.c 2007-06-29 21:17:44.000000000 +0200
++++ gc/os_dep.c 2008-06-26 10:41:13.000000000 +0200
+@@ -2064,6 +2064,18 @@ ptr_t GC_unmap_end(ptr_t start, size_t b
return end_addr;
}
@@ -485,73 +767,16 @@ diff -upr -x config.guess -x config.sub -x Makefile -x Makefile.in gc-7.0/os_dep
+
+#endif
+
-+uint64_t gc_map_time;
-+
-+static inline uint64_t
-+now (void)
-+{
-+ struct timeval t;
-+ struct timezone tz;
-+
-+ if (gettimeofday( &t, &tz ) == -1)
-+ return 0;
-+ return (t.tv_sec * 1000000ULL + t.tv_usec);
-+
-+}
-+
-+static int timing;
-+
-+static uint64_t
-+start_timing (const char *func, int lineno)
-+{
-+ static const char *timing_func;
-+ static int timing_lineno;
-+
-+ if (timing)
-+ panic ("Timing for %s:%d but start_timing called from %s:%d\n",
-+ timing_func, timing_lineno, func, lineno);
-+
-+ timing_func = func;
-+ timing_lineno = lineno;
-+
-+ timing = 1;
-+
-+ return now ();
-+}
-+
-+static void
-+end_timing (const char *func, int lineno, uint64_t start)
-+{
-+ uint64_t end = now ();
-+
-+ if (! timing)
-+ panic ("Timing not started by end_timing called from %s:%d!\n",
-+ func, lineno);
-+
-+ timing = 0;
-+
-+ if (start > end)
-+ panic ("%lld > %lld!", start, end);
-+ gc_map_time += end - start;
-+}
-+
-+#define start_timing() \
-+ do { \
-+ uint64_t __start = start_timing (__FUNCTION__, __LINE__)
-+
-+#define end_timing() \
-+ end_timing (__FUNCTION__, __LINE__, __start); \
-+ } while (0)
-+
++#include "profile.h"
+
/* Under Win32/WinCE we commit (map) and decommit (unmap) */
/* memory using VirtualAlloc and VirtualFree. These functions */
/* work on individual allocations of virtual memory, made */
-@@ -2077,10 +2146,12 @@ ptr_t GC_unmap_end(ptr_t start, size_t b
+@@ -2077,10 +2089,12 @@ ptr_t GC_unmap_end(ptr_t start, size_t b
/* round the endpoints in both places. */
void GC_unmap(ptr_t start, size_t bytes)
{
-+ start_timing ();
++ profile_start (MAP_TIMER);
+
ptr_t start_addr = GC_unmap_start(start, bytes);
ptr_t end_addr = GC_unmap_end(start, bytes);
@@ -561,7 +786,7 @@ diff -upr -x config.guess -x config.sub -x Makefile -x Makefile.in gc-7.0/os_dep
# if defined(MSWIN32) || defined(MSWINCE)
while (len != 0) {
MEMORY_BASIC_INFORMATION mem_info;
-@@ -2095,6 +2166,9 @@ void GC_unmap(ptr_t start, size_t bytes)
+@@ -2095,6 +2109,9 @@ void GC_unmap(ptr_t start, size_t bytes)
start_addr += free_len;
len -= free_len;
}
@@ -571,19 +796,19 @@ diff -upr -x config.guess -x config.sub -x Makefile -x Makefile.in gc-7.0/os_dep
# else
/* We immediately remap it to prevent an intervening mmap from */
/* accidentally grabbing the same address space. */
-@@ -2107,19 +2181,25 @@ void GC_unmap(ptr_t start, size_t bytes)
+@@ -2107,19 +2124,25 @@ void GC_unmap(ptr_t start, size_t bytes)
}
GC_unmapped_bytes += len;
# endif
+
+ out:
-+ end_timing ();
++ profile_end (MAP_TIMER);
}
void GC_remap(ptr_t start, size_t bytes)
{
-+ start_timing ();
++ profile_start (MAP_TIMER);
+
ptr_t start_addr = GC_unmap_start(start, bytes);
ptr_t end_addr = GC_unmap_end(start, bytes);
@@ -598,7 +823,7 @@ diff -upr -x config.guess -x config.sub -x Makefile -x Makefile.in gc-7.0/os_dep
while (len != 0) {
MEMORY_BASIC_INFORMATION mem_info;
GC_word alloc_len;
-@@ -2137,11 +2217,14 @@ void GC_remap(ptr_t start, size_t bytes)
+@@ -2137,11 +2160,14 @@ void GC_remap(ptr_t start, size_t bytes)
start_addr += alloc_len;
len -= alloc_len;
}
@@ -614,26 +839,26 @@ diff -upr -x config.guess -x config.sub -x Makefile -x Makefile.in gc-7.0/os_dep
result = mprotect(start_addr, len,
PROT_READ | PROT_WRITE | OPT_PROT_EXEC);
if (result != 0) {
-@@ -2152,6 +2235,9 @@ void GC_remap(ptr_t start, size_t bytes)
+@@ -2152,6 +2178,9 @@ void GC_remap(ptr_t start, size_t bytes)
}
GC_unmapped_bytes -= len;
# endif
+
+ out:
-+ end_timing ();
++ profile_end (MAP_TIMER);
}
/* Two adjacent blocks have already been unmapped and are about to */
-@@ -2160,6 +2246,8 @@ void GC_remap(ptr_t start, size_t bytes)
+@@ -2160,6 +2189,8 @@ void GC_remap(ptr_t start, size_t bytes)
/* unmapped due to alignment constraints. */
void GC_unmap_gap(ptr_t start1, size_t bytes1, ptr_t start2, size_t bytes2)
{
-+ start_timing ();
++ profile_start (MAP_TIMER);
+
ptr_t start1_addr = GC_unmap_start(start1, bytes1);
ptr_t end1_addr = GC_unmap_end(start1, bytes1);
ptr_t start2_addr = GC_unmap_start(start2, bytes2);
-@@ -2170,7 +2258,7 @@ void GC_unmap_gap(ptr_t start1, size_t b
+@@ -2170,7 +2201,7 @@ void GC_unmap_gap(ptr_t start1, size_t b
GC_ASSERT(start1 + bytes1 == start2);
if (0 == start1_addr) start_addr = GC_unmap_start(start1, bytes1 + bytes2);
if (0 == start2_addr) end_addr = GC_unmap_end(start1, bytes1 + bytes2);
@@ -642,19 +867,22 @@ diff -upr -x config.guess -x config.sub -x Makefile -x Makefile.in gc-7.0/os_dep
len = end_addr - start_addr;
# if defined(MSWIN32) || defined(MSWINCE)
while (len != 0) {
-@@ -2190,6 +2278,9 @@ void GC_unmap_gap(ptr_t start1, size_t b
+@@ -2190,6 +2221,9 @@ void GC_unmap_gap(ptr_t start1, size_t b
if (len != 0 && munmap(start_addr, len) != 0) ABORT("munmap failed");
GC_unmapped_bytes += len;
# endif
+
+ out:
-+ end_timing ();
++ profile_end (MAP_TIMER);
}
#endif /* USE_MUNMAP */
-diff -upr -x config.guess -x config.sub -x Makefile -x Makefile.in gc-7.0/tests/test.c gc/tests/test.c
---- gc-7.0/tests/test.c 2007-06-19 00:18:01.000000000 +0200
-+++ gc/tests/test.c 2008-06-23 22:12:34.000000000 +0200
+Only in gc: os_dep.c~
+Only in gc: patch.stamp
+Only in gc: profile.h
+diff -upr -x Makefile.in -x configure -x config.guess -x config.sub -x aclocal.m4 gc-old/tests/test.c gc/tests/test.c
+--- gc-old/tests/test.c 2007-06-19 00:18:01.000000000 +0200
++++ gc/tests/test.c 2008-06-26 10:41:14.000000000 +0200
@@ -1334,6 +1334,18 @@ void SetMinimumStack(long minSize)
# endif
# endif
@@ -674,4 +902,4 @@ diff -upr -x config.guess -x config.sub -x Makefile -x Makefile.in gc-7.0/tests/
run_one_test();
check_heap_stats();
# ifndef MSWINCE
-
+Only in gc: unpack.stamp
diff --git a/benchmarks/shared-memory-distribution.c b/benchmarks/shared-memory-distribution.c
index 4043718..04b37c2 100644
--- a/benchmarks/shared-memory-distribution.c
+++ b/benchmarks/shared-memory-distribution.c
@@ -62,14 +62,24 @@ main (int argc, char *argv[])
int count = 0;
while (! terminate)
{
- uintptr_t *p = buffer + (rand () % PAGES) * PAGESIZE;
- t = *p;
-
- count ++;
- if (count % 100000 == 0)
- debug (0, DEBUG_BOLD ("Read %d pages so far"), count);
-
+ int i;
+ for (i = 0; i < 1; i ++)
+ {
+ uintptr_t *p = buffer + (rand () % PAGES) * PAGESIZE;
+ t += *p;
+
+ count ++;
+
+ if (count % 100000 == 0)
+ debug (0, DEBUG_BOLD ("Read %d pages so far"), count);
+ }
+
+#if 0
+ /* ~128Hz. */
+ l4_sleep (l4_time_period (1 << 13));
+#elif 1
l4_thread_switch (tids[rand () % THREADS]);
+#endif
}
printf ("Thread %d: %d operations\n", w, count);
@@ -98,24 +108,26 @@ main (int argc, char *argv[])
{
debug (0, DEBUG_BOLD ("starting iteration %d (%x)"), i, l4_myself ());
- int count;
- struct activity_stats_buffer buffer;
+ struct activity_info info;
- rm_activity_stats (activity, next_period, &buffer, &count);
- assert (count > 0);
+ rm_activity_info (activity, activity_info_stats, next_period, &info);
+ assert (info.event == activity_info_stats);
+ assert (info.stats.count > 0);
if (i != 0)
- assertx (buffer.stats[0].period != stats[i - 1][0].period,
+ assertx (info.stats.stats[0].period != stats[i - 1][0].period,
"%x != %x",
- buffer.stats[0].period, stats[i - 1][0].period);
+ info.stats.stats[0].period, stats[i - 1][0].period);
- stats[i][0] = buffer.stats[0];
+ stats[i][0] = info.stats.stats[0];
int j;
for (j = 0; j < THREADS; j ++)
{
- rm_activity_stats (activities[j], next_period, &buffer, &count);
- assert (count > 0);
- stats[i][1 + j] = buffer.stats[0];
+ rm_activity_info (activity, activity_info_stats, next_period,
+ &info);
+ assert (info.event == activity_info_stats);
+ assert (info.stats.count > 0);
+ stats[i][1 + j] = info.stats.stats[0];
}
next_period = stats[i][0].period + 1;
@@ -137,10 +149,15 @@ main (int argc, char *argv[])
{
int j;
- printf ("%d ", (int) stats[i][0].period);
+ printf ("%d", (int) stats[i][0].period);
for (j = 0; j < 1 + THREADS; j ++)
- printf ("%d ", (int) stats[i][j].clean + (int) stats[i][j].dirty);
+ printf ("\t%d\t%d\t%d\t%d\t%d\t%d", (int) stats[i][j].clean,
+ (int) stats[i][j].dirty,
+ (int) stats[i][j].pending_eviction,
+ (int) stats[i][j].discarded,
+ (int) stats[i][j].freeloading,
+ (int) stats[i][j].freeloaded);
printf ("\n");
}
diff --git a/hieronymus/ChangeLog b/hieronymus/ChangeLog
index 32cd062..37b8e33 100644
--- a/hieronymus/ChangeLog
+++ b/hieronymus/ChangeLog
@@ -1,3 +1,10 @@
+2008-06-27 Neal H. Walfield <neal@gnu.org>
+
+ * hieronymus.c (do_gather_stats): Change to use activity_info
+ instead of activity_stats.
+
+ (main): Introduce code to delay process creation.
+
2008-06-26 Neal H. Walfield <neal@gnu.org>
* hieronymus.c: Include <stdlib.h>.
diff --git a/hieronymus/hieronymus.c b/hieronymus/hieronymus.c
index 4fac2d5..a1ecfc0 100644
--- a/hieronymus/hieronymus.c
+++ b/hieronymus/hieronymus.c
@@ -111,8 +111,7 @@ do_gather_stats (void *arg)
while (! all_done)
{
- int count;
- struct activity_stats_buffer buffer;
+ struct activity_info info;
if (size == stats_count)
{
@@ -134,8 +133,11 @@ do_gather_stats (void *arg)
for (i = 0; i < module_count; i ++, stat ++)
{
error_t err;
- err = rm_activity_stats (activities[0],
- period, &buffer, &count);
+ err = rm_activity_info (activities[i], activity_info_stats,
+ period, &info);
+ assert_perror (err);
+ assert (info.event == activity_info_stats);
+ assert (info.stats.count > 0);
if (err)
{
stat->alloced = 0;
@@ -143,13 +145,14 @@ do_gather_stats (void *arg)
}
else
{
- stat->alloced = buffer.stats[0].clean + buffer.stats[0].dirty
- + buffer.stats[0].pending_eviction;
- stat->available = buffer.stats[0].available;
+ stat->alloced = info.stats.stats[0].clean
+ + info.stats.stats[0].dirty
+ + info.stats.stats[0].pending_eviction;
+ stat->available = info.stats.stats[0].available;
if (n == 0)
- n = buffer.stats[0].period + 1;
+ n = info.stats.stats[0].period + 1;
}
}
@@ -208,21 +211,30 @@ main (int argc, char *argv[])
addr_t thread[module_count];
for (i = 0; i < module_count; i ++)
{
- int count;
- struct activity_stats_buffer buffer;
+ /* Delay starting each process. */
+#if 0
+ error_t err;
+ struct activity_info info;
+ err = rm_activity_info (activities[i], activity_info_stats,
+ i * 50, &info);
+ assert_perror (err);
+ assert (info.event == activity_info_stats);
+ assert (info.stats.count > 0);
+
const char *argv[] = { modules[i].name, modules[i].commandline, NULL };
const char *env[] = { NULL };
thread[i] = process_spawn (activities[i],
modules[i].start, modules[i].end,
argv, env, false);
+#endif
/* Free the memory used by the module's binary. */
/* XXX: This doesn't free folios or note pages that may be
partially freed. The latter is important because a page may
be used by two modules and after the second module is loaded,
it could be freed. */
- count = 0;
+ int count = 0;
int j;
for (j = 0; j < __hurd_startup_data->desc_count; j ++)
{
diff --git a/hurd/ChangeLog b/hurd/ChangeLog
index 1df93a2..2e0c3f7 100644
--- a/hurd/ChangeLog
+++ b/hurd/ChangeLog
@@ -1,5 +1,18 @@
2008-06-27 Neal H. Walfield <neal@gnu.org>
+ * activity.h (RM_activity_stats): Don't define.
+ (RM_activity_info): Replace with this.
+ (struct activity_stats): Add field pressure_local. Make field
+ pressure an 8 bit integer.
+ (struct activity_stats_buffer): Remove structure.
+ (struct activity_info): Replace with this structure.
+ (activity_info_stats): Define.
+ (activity_info_pressure): Likewise.
+ (activity_stats): Remove method.
+ (activity_info): Replace with this.
+
+2008-06-27 Neal H. Walfield <neal@gnu.org>
+
* stddef.h (do_debug): If DEBUG_ELIDE is defined to an integer,
statically exclude those debug statements that have a level
greater than or equal to it.
diff --git a/hurd/activity.h b/hurd/activity.h
index d26a233..5c3a1e5 100644
--- a/hurd/activity.h
+++ b/hurd/activity.h
@@ -28,7 +28,7 @@
enum
{
RM_activity_policy = 700,
- RM_activity_stats,
+ RM_activity_info,
};
struct activity_memory_policy
@@ -79,10 +79,11 @@ struct activity_stats
uint32_t available;
uint32_t available_local;
- /* The maximum amount of memory that the user of this activity ought
- to allocate in the next few seconds. If negative, the amount of
- memory the activity ought to consider freeing. */
- int32_t damping_factor;
+ /* Log2 the maximum amount of memory (in pages) that the user of
+ this activity ought to allocate in the next few seconds. If
+ negative, the amount of memory the activity ought to consider
+ freeing. */
+ int8_t damping_factor;
/* If pressure is non-zero, then this activity is causing PRESSURE.
@@ -99,7 +100,8 @@ struct activity_stats
system if it does not yield memory. However, if the activity has
memory which is yielding a low return, it would be friendly of it
to return it. */
- uint32_t pressure;
+ uint8_t pressure;
+ uint8_t pressure_local;
/* The number of clean and dirty frames that are accounted to this
activity. (Does not include frames scheduled for eviction.) The
@@ -157,10 +159,6 @@ struct activity_stats
evicted, then the process is trashing.) */
uint32_t saved;
};
-struct activity_stats_buffer
-{
- struct activity_stats stats[ACTIVITY_STATS_PERIODS];
-};
#define ACTIVITY_POLICY(__ap_sibling_rel, __ap_child_rel, __ap_storage) \
(struct activity_policy) { __ap_sibling_rel, __ap_child_rel, __ap_storage }
@@ -201,17 +199,59 @@ RPC (activity_policy, 3, 1, addr_t, activity,
/* Out: */
struct activity_policy, out);
-/* Return statistics about activity ACTIVITY. Waits until the current
- period is older than UNTIL_PERIOD. (This can be used to register a
- callback that is sent when the statistics are next available. For
- example, call with until_period is 0 to get the current statistics
- and then examine the period field. Use this as the base for the
- next call.) COUNT is the number of returned samples. Samples are
- ordered by recency with the youngest towards the start of the
- buffer. */
-RPC (activity_stats, 2, 2, addr_t, activity, uintptr_t, until_period,
+enum
+ {
+ /* Return statistics. */
+ activity_info_stats = 1 << 0,
+ /* Asynchronous change in availability. */
+ activity_info_pressure = 1 << 1,
+ };
+
+struct activity_info
+{
+ /* The returned event. */
+ uintptr_t event;
+ union
+ {
+ /* If EVENT is activity_info_stats. */
+ struct
+ {
+ /* The number of samples. */
+ int count;
+ /* Samples are ordered by recency with the youngest towards the
+ start of the buffer. */
+ struct activity_stats stats[ACTIVITY_STATS_PERIODS];
+ } stats;
+
+ /* If EVENT is activity_info_free. */
+ struct
+ {
+ /* The number of pages the caller should try to free (negative)
+ or may allocate (positive). */
+ int amount;
+ } pressure;
+ };
+};
+
+/* Return some information about the activity ACTIVITY. FLAGS is a
+ bit-wise or of events the caller is interested. Only one event
+ will be returned.
+
+ If FLAGS contains activity_info_stats, may return the next
+ statistic that comes at or after UNTIL_PERIOD. (This can be used
+ to register a callback that is sent when the statistics are next
+ available. For example, call with UNTIL_PERIOD equal to 0 to get
+ the current statistics and then examine the period field. Use this
+ as the base for the next call.)
+
+ If FLAGS contains activity_info_free, may return an upcall
+ indicating that the activity must free some memory or will be such
+ subject to paging. In this case, the activity should try to free
+ at least the indicated number of pages as quickly as possible. */
+RPC (activity_info, 3, 1, addr_t, activity,
+ uintptr_t, flags, uintptr_t, until_period,
/* Out: */
- struct activity_stats_buffer, stats, int, count)
+ struct activity_info, info)
#undef RPC_STUB_PREFIX
#undef RPC_ID_PREFIX
diff --git a/ruth/ChangeLog b/ruth/ChangeLog
index e492156..da4ce19 100644
--- a/ruth/ChangeLog
+++ b/ruth/ChangeLog
@@ -1,3 +1,8 @@
+2008-06-27 Neal H. Walfield <neal@gnu.org>
+
+ * ruth.c (main): Update to use activity_info instead of
+ activity_stats.
+
2008-06-20 Neal H. Walfield <neal@gnu.org>
* ruth.c (visit): When encountering a folio, return -1
diff --git a/ruth/ruth.c b/ruth/ruth.c
index 027240e..fe40d8c 100644
--- a/ruth/ruth.c
+++ b/ruth/ruth.c
@@ -961,16 +961,16 @@ main (int argc, char *argv[])
return true;
}
- struct activity_stats_buffer stats;
uint32_t frames;
do
{
- int c;
- error_t err = rm_activity_stats (ADDR_VOID, 0, &stats, &c);
+ struct activity_info info;
+ error_t err = rm_activity_info (ADDR_VOID, activity_info_stats, 1,
+ &info);
assert_perror (err);
- assert (c >= 1);
+ assert (info.stats.count >= 1);
- frames = stats.stats[0].available;
+ frames = info.stats.stats[0].available;
}
while (frames == 0);
diff --git a/viengoos/ChangeLog b/viengoos/ChangeLog
index b12a7e0..b0ef8d2 100644
--- a/viengoos/ChangeLog
+++ b/viengoos/ChangeLog
@@ -1,3 +1,29 @@
+2008-06-27 Neal H. Walfield <neal@gnu.org>
+
+ * activity.h (struct activity): Add fields free_bad_karma,
+ free_goal, free_allocations and free_initial_allocation.
+ * rm.h (rm_method_id_string): Replace RM_activity_stats handling
+ with RM_activity_info handling.
+ * thread.h (THREAD_WAIT_STATS): Remove.
+ (THREAD_WAIT_ACTIVITY_INFO): Replace with this.
+ (struct thread): Add field wait_reason_arg2.
+ * ager.c (update_stats): Account for local pressure when
+ calculating availability. Don't use activity_stats to inform
+ threads of new statistics but activity_info.
+ * object.c (object_desc_claim): Adjust the activity's FREE_GOAL,
+ FREE_ALLOCATIONS, and FRAMES_EXCLUDED fields as appropriate.
+ * pager.c (pager_collect): When selecting a victim, don't include
+ an activity's excluded frames in its allocation. Don't even
+ consider activities for which FREE_ALLOCATIONS is non-zero.
+ Having selected a victim, don't increase the pressure do
+ drastically. Update the local pressure. Having selected a victim
+ from which to revoke pages, send any waiting threads a message to
+ free memory. If VICTIM->FREE_BAD_KARMA is 0, assume that the
+ memory will be freed and give the activity 100 claims to do it.
+ If the activity has bad karma, decrease it by one.
+ * server.c (server_loop): Replace activity_stats implementation
+ with implementation appropriate for activity_info.
+
2008-06-26 Neal H. Walfield <neal@gnu.org>
* viengoos.c (main): Assign viengoos_tid after we test that that
diff --git a/viengoos/activity.h b/viengoos/activity.h
index 38d9997..8ffcc94 100644
--- a/viengoos/activity.h
+++ b/viengoos/activity.h
@@ -115,6 +115,21 @@ struct activity
unsigned char current_period;
struct activity_stats stats[ACTIVITY_STATS_PERIODS + 1];
+ /* When an activity is choosen to yield pages, we first send it a
+ signal asking it to free memory. We exclude it from eviction for
+ the next FREE_ALLOCATIONS allocations. If, after that many
+ allocations, the activity has not dropped below free_goal, we
+ simply start evicting. We also set its free bad karma. Each
+ time it is choosen for deallocation, we decrement it. Only when
+ FREE_BAD_KARMA is again zero do we give it this chance again. */
+ uintptr_t free_bad_karma;
+ uintptr_t free_goal;
+ uintptr_t free_allocations;
+ uintptr_t free_initial_allocation;
+ /* The number of pages that are to be excluded from FRAMES_TOTAL
+ when looking for a process to page. */
+ uintptr_t frames_excluded;
+
struct object_name name;
};
diff --git a/viengoos/ager.c b/viengoos/ager.c
index a7f8808..b6dd8b7 100644
--- a/viengoos/ager.c
+++ b/viengoos/ager.c
@@ -86,6 +86,8 @@ update_stats (void)
ACTIVITY_STATS (activity)->pressure +=
ACTIVITY_STATS_LAST (activity)->pressure >> 1;
+ ACTIVITY_STATS (activity)->pressure_local +=
+ ACTIVITY_STATS_LAST (activity)->pressure_local >> 1;
if (activity->frames_total > frames
|| ACTIVITY_STATS (activity)->pressure)
@@ -104,18 +106,7 @@ update_stats (void)
frames, frames - dec);
frames -= dec;
-
- ACTIVITY_STATS (activity)->damping_factor
- = -1024 * 1024 / PAGESIZE;
}
- else if (activity->frames_total > 7 * (frames / 8))
- /* The allocated amount is close to the available frames.
- Encourage it not to allocate. */
- ACTIVITY_STATS (activity)->damping_factor = 0;
- else
- /* It can allocate. */
- ACTIVITY_STATS (activity)->damping_factor
- = 1024 * 1024 / PAGESIZE;
ACTIVITY_STATS (activity)->available = frames;
@@ -318,11 +309,42 @@ update_stats (void)
}
if (activity->policy.child_rel.priority == priority)
- ACTIVITY_STATS (activity)->available_local
- = comp_avail (activity,
- activity->policy.child_rel.weight,
- activity->frames_local,
- 0, 0);
+ {
+ int avail = comp_avail (activity,
+ activity->policy.child_rel.weight,
+ activity->frames_local,
+ 0, 0);
+
+ if (activity->frames_local > avail
+ || ACTIVITY_STATS (activity)->pressure_local)
+ {
+ /* The amount by which we decrease is proportional to
+ pressure. */
+ int dec = avail / 8;
+ dec /= 5 - MIN (ACTIVITY_STATS (activity)->pressure_local, 4);
+
+ debug (1, "Due to pressure (%d), decreasing frames locally "
+ "available to " OBJECT_NAME_FMT " from %d to %d",
+ ACTIVITY_STATS (activity)->pressure_local,
+ OBJECT_NAME_PRINTF ((struct object *) activity),
+ avail, avail - dec);
+
+ avail -= dec;
+
+ ACTIVITY_STATS (activity)->damping_factor
+ = -l4_msb (1024 * 1024 / PAGESIZE);
+ }
+ else if (activity->frames_total > 7 * (frames / 8))
+ /* The allocated amount is close to the available frames.
+ Encourage it not to allocate. */
+ ACTIVITY_STATS (activity)->damping_factor = 0;
+ else
+ /* It can allocate. */
+ ACTIVITY_STATS (activity)->damping_factor
+ = l4_msb (1024 * 1024 / PAGESIZE);
+
+ ACTIVITY_STATS (activity)->available_local = avail;
+ }
debug (5, "%d frames for %d activities with priority %d, "
"total weight: %lld, alloced: %d, excess: %d",
@@ -357,7 +379,8 @@ update_stats (void)
}
debug (5, OBJECT_NAME_FMT " (s: %d/%d; c: %d/%d): "
- "%d/%d frames, %d/%d avail (" OBJECT_NAME_FMT ")",
+ "%d/%d frames, %d/%d avail, %d free goal, %d bad_karma "
+ "(" OBJECT_NAME_FMT ")",
OBJECT_NAME_PRINTF ((struct object *) activity),
activity->policy.sibling_rel.priority,
activity->policy.sibling_rel.weight,
@@ -367,6 +390,7 @@ update_stats (void)
activity->frames_total,
ACTIVITY_STATS (activity)->available_local,
ACTIVITY_STATS (activity)->available,
+ activity->free_goal, activity->free_bad_karma,
OBJECT_NAME_PRINTF ((struct object *)
(activity->parent ?: root_activity)));
@@ -381,13 +405,16 @@ update_stats (void)
struct thread *thread;
object_wait_queue_for_each (activity, (struct object *) activity,
thread)
- if (thread->wait_reason == THREAD_WAIT_STATS
- && thread->wait_reason_arg <= period / FREQ)
+ if (thread->wait_reason == THREAD_WAIT_ACTIVITY_INFO
+ && (thread->wait_reason_arg & activity_info_stats)
+ && thread->wait_reason_arg2 <= period / FREQ)
{
object_wait_queue_dequeue (activity, thread);
/* XXX: Only return valid stat buffers. */
- struct activity_stats_buffer buffer;
+ struct activity_info info;
+ info.event = activity_info_stats;
+
int i;
for (i = 0; i < ACTIVITY_STATS_PERIODS; i ++)
{
@@ -395,13 +422,13 @@ update_stats (void)
if (period < 0)
period = (ACTIVITY_STATS_PERIODS + 1) + period;
- buffer.stats[i] = activity->stats[period];
+ info.stats.stats[i] = activity->stats[period];
}
+ info.stats.count = ACTIVITY_STATS_PERIODS;
+
l4_msg_t msg;
- rm_activity_stats_reply_marshal (&msg,
- buffer,
- ACTIVITY_STATS_PERIODS);
+ rm_activity_info_reply_marshal (&msg, info);
l4_msg_tag_t msg_tag = l4_msg_msg_tag (msg);
l4_set_propagation (&msg_tag);
l4_msg_set_msg_tag (msg, msg_tag);
diff --git a/viengoos/object.c b/viengoos/object.c
index f5708f7..b1433e3 100644
--- a/viengoos/object.c
+++ b/viengoos/object.c
@@ -818,6 +818,31 @@ object_desc_claim (struct activity *activity, struct object_desc *desc,
if (activity != desc->activity && update_accounting)
activity_charge (desc->activity, -1);
}
+
+ if ((activity != desc->activity
+ || (desc->eviction_candidate
+ && ! (desc->dirty && ! desc->policy.discardable)))
+ && update_accounting
+ && desc->activity->free_goal)
+ {
+ desc->activity->free_goal --;
+ if (desc->activity->free_goal == 0)
+ /* The activity met the free goal! */
+ {
+ debug (0, DEBUG_BOLD (OBJECT_NAME_FMT " met goal."),
+ OBJECT_NAME_PRINTF ((struct object *) desc->activity));
+
+ struct activity *ancestor = desc->activity;
+ activity_for_each_ancestor
+ (ancestor,
+ ({
+ ancestor->frames_excluded
+ -= desc->activity->free_initial_allocation;
+ }));
+
+ desc->activity->free_allocations = 0;
+ }
+ }
}
if (! activity)
@@ -844,7 +869,37 @@ object_desc_claim (struct activity *activity, struct object_desc *desc,
|| (desc->eviction_candidate
&& ! (desc->dirty && ! desc->policy.discardable)))
&& update_accounting)
- activity_charge (activity, 1);
+ {
+ activity_charge (activity, 1);
+
+ if (activity->free_allocations)
+ {
+ activity->free_allocations --;
+ if (activity->free_allocations == 0)
+ {
+ if (activity->free_goal)
+ /* Bad boy! */
+ {
+ activity->free_bad_karma = 8;
+
+ debug (0, DEBUG_BOLD (OBJECT_NAME_FMT
+ " failed to free %d pages."),
+ OBJECT_NAME_PRINTF ((struct object *) activity),
+ activity->free_goal);
+ }
+
+ struct activity *ancestor = activity;
+ activity_for_each_ancestor
+ (ancestor,
+ ({
+ ancestor->frames_excluded
+ -= activity->free_initial_allocation;
+ }));
+
+ activity->free_goal = 0;
+ }
+ }
+ }
desc->eviction_candidate = false;
desc->activity = activity;
diff --git a/viengoos/pager.c b/viengoos/pager.c
index db3f6cf..0dc7ad4 100644
--- a/viengoos/pager.c
+++ b/viengoos/pager.c
@@ -286,13 +286,14 @@ reclaim_from (struct activity *victim, int goal)
ancestor->frames_pending_eviction += laundry_count;
}));
- debug (5, "Reclaimed from " OID_FMT ": goal: %d; %d frames; "
+ debug (5, "Reclaimed from " OBJECT_NAME_FMT ": goal: %d; %d frames; "
DEBUG_BOLD ("%d in laundry, %d made available (%d discarded)") " "
- "(now: avail: %d, laundry: %d)",
- OID_PRINTF (object_oid ((struct object *) victim)), goal,
+ "(now: free: %d, avail: %d, laundry: %d)",
+ OBJECT_NAME_PRINTF ((struct object *) victim), goal,
victim->frames_local,
laundry_count, count - laundry_count, discarded,
- available_list_count (&available), laundry_list_count (&laundry));
+ zalloc_memory, available_list_count (&available),
+ laundry_list_count (&laundry));
assertx (victim->priorities_count
+ activity_lru_list_count (&victim->active)
@@ -324,7 +325,7 @@ pager_collect (int goal)
int available_memory = zalloc_memory + available_list_count (&available);
- debug (5, "Frames: %d, available: %d (%d%%), pending page out: %d, "
+ debug (0, "Frames: %d, available: %d (%d%%), pending page out: %d, "
"low water: %d, goal: %d",
memory_total,
available_memory, (available_memory * 100) / memory_total,
@@ -466,18 +467,34 @@ pager_collect (int goal)
child->policy.sibling_rel.priority))
{
have_self = true;
- int frames = parent->frames_local
- - eviction_list_count (&parent->eviction_dirty)
- - (ACTIVITY_STATS_LAST (parent)->active_local
- >> factor);
- if (frames > 0)
- done = process (parent, parent->policy.child_rel,
- frames);
- if (done)
- break;
+
+ /* If PARENT->FREE_ALLOCATIONS is non-zero, we
+ have already selected this activity for
+ eviction and have sent it a message asking it
+ to free memory. It may make
+ PARENT->FREE_ALLOCATIONS before it is again
+ eligible. */
+ if (! parent->free_allocations)
+ {
+ int frames = parent->frames_local
+ - eviction_list_count (&parent->eviction_dirty)
+ - (ACTIVITY_STATS_LAST (parent)->active_local
+ >> factor);
+ if (frames > 0)
+ done = process (parent, parent->policy.child_rel,
+ frames);
+ if (done)
+ break;
+ }
+ else
+ debug (0, "Excluding " OBJECT_NAME_FMT
+ ": %d free frames, %d excluded",
+ parent->free_allocations,
+ parent->frames_excluded);
}
int frames = child->frames_total
+ - child->frames_excluded
- child->frames_pending_eviction
- (ACTIVITY_STATS_LAST (child)->active >> factor);
if (frames > 0)
@@ -489,6 +506,7 @@ pager_collect (int goal)
if (! done && ! have_self)
{
int frames = parent->frames_local
+ - child->frames_excluded
- eviction_list_count (&parent->eviction_dirty)
- (ACTIVITY_STATS_LAST (parent)->active_local >> factor);
if (frames > 0)
@@ -504,7 +522,7 @@ pager_collect (int goal)
if (! victim)
break;
- ACTIVITY_STATS (victim)->pressure += 4;
+ ACTIVITY_STATS (victim)->pressure ++;
assert (victim);
}
@@ -515,6 +533,8 @@ pager_collect (int goal)
/* We steal from VICTIM. */
+ ACTIVITY_STATS (victim)->pressure_local += 4;
+
/* Calculate VICTIM's share of the frames allocated to all the
activity's at this priority level. */
int share = 0;
@@ -531,8 +551,10 @@ pager_collect (int goal)
assertx (victim_frames >= share,
"%d < %d", victim_frames, share);
- debug (5, DEBUG_BOLD ("Revoking from activity " OBJECT_NAME_FMT ", ")
+ debug (5, "%d of %d pages available; "
+ DEBUG_BOLD ("Revoking from activity " OBJECT_NAME_FMT ", ")
"%d/%d frames (pending eviction: %d/%d), share: %d, goal: %d",
+ zalloc_memory + available_list_count (&available), memory_total,
OBJECT_NAME_PRINTF ((struct object *) victim),
victim->frames_local, victim->frames_total,
eviction_list_count (&victim->eviction_dirty),
@@ -543,7 +565,63 @@ pager_collect (int goal)
if (reclaim > goal)
reclaim = goal;
- total_freed += reclaim_from (victim, reclaim);
+ bool need_reclaim = true;
+
+ struct thread *thread;
+ object_wait_queue_for_each (victim, (struct object *) victim,
+ thread)
+ if (thread->wait_reason == THREAD_WAIT_ACTIVITY_INFO
+ && (thread->wait_reason_arg & activity_info_pressure))
+ break;
+
+ if (thread)
+ {
+ debug (0, DEBUG_BOLD ("Requesting that " OBJECT_NAME_FMT " free "
+ "%d pages.") " Karma: %d",
+ OBJECT_NAME_PRINTF ((struct object *) victim),
+ goal, victim->free_bad_karma);
+
+ if (! victim->free_goal
+ && victim->free_bad_karma == 0)
+ /* If the activity manages half the goal, we'll be
+ happy. */
+ {
+ need_reclaim = false;
+
+ victim->free_goal = goal / 2;
+ victim->free_allocations = 100;
+ victim->free_initial_allocation = victim->frames_local;
+
+ struct activity *ancestor = victim;
+ activity_for_each_ancestor
+ (ancestor,
+ ({
+ ancestor->frames_excluded += victim->frames_local;
+ }));
+
+ total_freed += goal;
+ }
+
+ struct activity_info info;
+ info.event = activity_info_pressure;
+ info.pressure.amount = - goal;
+
+ object_wait_queue_for_each (victim,
+ (struct object *) victim,
+ thread)
+ if (thread->wait_reason == THREAD_WAIT_ACTIVITY_INFO
+ && (thread->wait_reason_arg & activity_info_pressure))
+ {
+ object_wait_queue_dequeue (victim, thread);
+ rm_activity_info_reply (thread->tid, info);
+ }
+ }
+
+ if (victim->free_bad_karma)
+ victim->free_bad_karma --;
+
+ if (need_reclaim)
+ total_freed += reclaim_from (victim, reclaim);
}
if (zalloc_memory + available_list_count (&available)
diff --git a/viengoos/rm.h b/viengoos/rm.h
index 3954ca3..04e2a06 100644
--- a/viengoos/rm.h
+++ b/viengoos/rm.h
@@ -77,8 +77,8 @@ rm_method_id_string (int id)
return "thread_wait_object_destroyed";
case RM_activity_policy:
return "activity_policy";
- case RM_activity_stats:
- return "activity_stats";
+ case RM_activity_info:
+ return "activity_info";
case RM_futex:
return "futex";
default:
diff --git a/viengoos/server.c b/viengoos/server.c
index 832fe5d..153477a 100644
--- a/viengoos/server.c
+++ b/viengoos/server.c
@@ -1362,37 +1362,40 @@ server_loop (void)
break;
}
- case RM_activity_stats:
+ case RM_activity_info:
{
+ uintptr_t flags;
uintptr_t until_period;
- err = rm_activity_stats_send_unmarshal (&msg, &principal_addr,
- &until_period);
+
+ err = rm_activity_info_send_unmarshal (&msg, &principal_addr,
+ &flags,
+ &until_period);
if (err)
REPLY (err);
- DEBUG (4, "%d", until_period);
-
int period = principal->current_period - 1;
if (period < 0)
period = (ACTIVITY_STATS_PERIODS + 1) + period;
- if (principal->stats[period].period < until_period)
- /* Queue thread on the activity. */
- {
- thread->wait_reason = THREAD_WAIT_STATS;
- thread->wait_reason_arg = until_period;
-
- object_wait_queue_enqueue (principal,
- (struct object *) principal,
- thread);
-
- do_reply = 0;
- }
- else
+ DEBUG (4, OBJECT_NAME_FMT ": %s%s%s(%d), "
+ "period: %d (current: %d)\n",
+ OBJECT_NAME_PRINTF ((struct object *) principal),
+ flags & activity_info_stats ? "stats" : "",
+ (flags == (activity_info_free|activity_info_stats))
+ ? ", " : "",
+ flags & activity_info_free ? "free" : "",
+ flags,
+ until_period, principal->stats[period].period);
+
+ if ((flags & activity_info_stats)
+ && principal->stats[period].period > 0
+ && principal->stats[period].period >= until_period)
/* Return the available statistics. */
{
/* XXX: Only return valid stat buffers. */
- struct activity_stats_buffer buffer;
+ struct activity_info info;
+ info.event = activity_info_stats;
+
int i;
for (i = 0; i < ACTIVITY_STATS_PERIODS; i ++)
{
@@ -1400,13 +1403,28 @@ server_loop (void)
if (period < 0)
period = (ACTIVITY_STATS_PERIODS + 1) + period;
- buffer.stats[i] = principal->stats[period];
+ info.stats.stats[i] = principal->stats[period];
}
- rm_activity_stats_reply_marshal (&msg,
- buffer,
- ACTIVITY_STATS_PERIODS);
+ info.stats.count = ACTIVITY_STATS_PERIODS;
+
+ rm_activity_info_reply_marshal (&msg, info);
}
+ else if (flags)
+ /* Queue thread on the activity. */
+ {
+ thread->wait_reason = THREAD_WAIT_ACTIVITY_INFO;
+ thread->wait_reason_arg = flags;
+ thread->wait_reason_arg2 = until_period;
+
+ object_wait_queue_enqueue (principal,
+ (struct object *) principal,
+ thread);
+
+ do_reply = 0;
+ }
+ else
+ REPLY (EINVAL);
break;
}
diff --git a/viengoos/thread.h b/viengoos/thread.h
index 27321f6..728bf7b 100644
--- a/viengoos/thread.h
+++ b/viengoos/thread.h
@@ -46,10 +46,10 @@ enum
/* THREAD is blocked on an object waiting for the object to be
destroyed. */
THREAD_WAIT_DESTROY,
- /* THREAD is blocked on an activity waiting for the stats for a
- particular period, which is stored in
- thread->wait_reason_arg. */
- THREAD_WAIT_STATS,
+ /* THREAD is blocked on an activity waiting for information. The
+ type of information is stored in wait_reason_arg. The period
+ in wait_reason_arg2. */
+ THREAD_WAIT_ACTIVITY_INFO,
};
struct thread
@@ -99,6 +99,7 @@ struct thread
uint32_t wait_reason : 28;
/* More information about the reason. */
uint32_t wait_reason_arg;
+ uint32_t wait_reason_arg2;
/* The object the thread is waiting on. Only meaningful if
WAIT_QUEUE_P is true. */