aboutsummaryrefslogtreecommitdiffstats
path: root/erts
diff options
context:
space:
mode:
Diffstat (limited to 'erts')
-rw-r--r--erts/emulator/beam/erl_alloc.c27
-rw-r--r--erts/emulator/beam/erl_alloc_util.c10
-rw-r--r--erts/emulator/test/alloc_SUITE.erl78
-rw-r--r--erts/emulator/test/alloc_SUITE_data/Makefile.src3
-rw-r--r--erts/emulator/test/alloc_SUITE_data/allocator_test.h2
-rw-r--r--erts/emulator/test/alloc_SUITE_data/migration.c229
-rw-r--r--erts/emulator/test/alloc_SUITE_data/testcase_driver.c14
-rw-r--r--erts/emulator/test/alloc_SUITE_data/testcase_driver.h1
8 files changed, 350 insertions, 14 deletions
diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c
index 4223b357f1..0aa45acd82 100644
--- a/erts/emulator/beam/erl_alloc.c
+++ b/erts/emulator/beam/erl_alloc.c
@@ -3615,6 +3615,33 @@ UWord erts_alc_test(UWord op, UWord a1, UWord a2, UWord a3)
case 0xf15: erts_free(ERTS_ALC_T_TEST, (void*)a1); return 0;
+ case 0xf16: {
+ Uint extra_hdr_sz = UNIT_CEILING((Uint)a1);
+ ErtsAllocatorThrSpec_t* ts = &erts_allctr_thr_spec[ERTS_ALC_A_TEST];
+ Uint offset = ts->allctr[0]->mbc_header_size;
+ void* orig_creating_mbc = ts->allctr[0]->creating_mbc;
+ void* orig_destroying_mbc = ts->allctr[0]->destroying_mbc;
+ void* new_creating_mbc = *(void**)a2; /* inout arg */
+ void* new_destroying_mbc = *(void**)a3; /* inout arg */
+ int i;
+
+ for (i=0; i < ts->size; i++) {
+ Allctr_t* ap = ts->allctr[i];
+ if (ap->mbc_header_size != offset
+ || ap->creating_mbc != orig_creating_mbc
+ || ap->destroying_mbc != orig_destroying_mbc
+ || ap->mbc_list.first != NULL)
+ return -1;
+ }
+ for (i=0; i < ts->size; i++) {
+ ts->allctr[i]->mbc_header_size += extra_hdr_sz;
+ ts->allctr[i]->creating_mbc = new_creating_mbc;
+ ts->allctr[i]->destroying_mbc = new_destroying_mbc;
+ }
+ *(void**)a2 = orig_creating_mbc;
+ *(void**)a3 = orig_destroying_mbc;
+ return offset;
+ }
default:
break;
}
diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c
index 173cb091b6..8229a15824 100644
--- a/erts/emulator/beam/erl_alloc_util.c
+++ b/erts/emulator/beam/erl_alloc_util.c
@@ -6074,6 +6074,16 @@ erts_alcu_test(UWord op, UWord a1, UWord a2)
case 0x023: return (UWord) 0;
case 0x024: return (UWord) 0;
#endif
+ case 0x025: /* UMEM2BLK_TEST*/
+#ifdef DEBUG
+# ifdef HARD_DEBUG
+ return (UWord)UMEM2BLK(a1-3*sizeof(UWord));
+# else
+ return (UWord)UMEM2BLK(a1-2*sizeof(UWord));
+# endif
+#else
+ return (UWord)UMEM2BLK(a1);
+#endif
default: ASSERT(0); return ~((UWord) 0);
}
diff --git a/erts/emulator/test/alloc_SUITE.erl b/erts/emulator/test/alloc_SUITE.erl
index 7c7ddde5d4..9b0d4737b1 100644
--- a/erts/emulator/test/alloc_SUITE.erl
+++ b/erts/emulator/test/alloc_SUITE.erl
@@ -31,7 +31,8 @@
rbtree/1,
mseg_clear_cache/1,
erts_mmap/1,
- cpool/1]).
+ cpool/1,
+ migration/1]).
-export([init_per_testcase/2, end_per_testcase/2]).
@@ -43,7 +44,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[basic, coalesce, threads, realloc_copy, bucket_index,
- bucket_mask, rbtree, mseg_clear_cache, erts_mmap, cpool].
+ bucket_mask, rbtree, mseg_clear_cache, erts_mmap, cpool, migration].
groups() ->
[].
@@ -112,6 +113,8 @@ cpool(suite) -> [];
cpool(doc) -> [];
cpool(Cfg) -> ?line drv_case(Cfg).
+migration(Cfg) -> drv_case(Cfg, concurrent, "+MZe true").
+
erts_mmap(Config) when is_list(Config) ->
case {?t:os_type(), is_halfword_vm()} of
{{unix, _}, false} ->
@@ -176,18 +179,17 @@ erts_mmap_do(Config, SCO, SCRPM, SCRFSD) ->
%% %%
drv_case(Config) ->
- drv_case(Config, "").
+ drv_case(Config, one_shot, "").
-drv_case(Config, Command) when is_list(Config),
- is_list(Command) ->
+drv_case(Config, Mode, NodeOpts) when is_list(Config) ->
case ?t:os_type() of
{Family, _} when Family == unix; Family == win32 ->
- ?line {ok, Node} = start_node(Config),
+ ?line {ok, Node} = start_node(Config, NodeOpts),
?line Self = self(),
?line Ref = make_ref(),
?line spawn_link(Node,
fun () ->
- Res = run_drv_case(Config, Command),
+ Res = run_drv_case(Config, Mode),
Self ! {Ref, Res}
end),
?line Result = receive {Ref, Rslt} -> Rslt end,
@@ -199,7 +201,7 @@ drv_case(Config, Command) when is_list(Config),
| io_lib:format("~p",[SkipOs])])}
end.
-run_drv_case(Config, Command) ->
+run_drv_case(Config, Mode) ->
?line DataDir = ?config(data_dir,Config),
?line CaseName = ?config(testcase,Config),
case erl_ddll:load_driver(DataDir, CaseName) of
@@ -208,6 +210,19 @@ run_drv_case(Config, Command) ->
io:format("~s\n", [erl_ddll:format_error(Error)]),
?line ?t:fail()
end,
+
+ case Mode of
+ one_shot ->
+ Result = one_shot(CaseName, "");
+
+ concurrent ->
+ Result = concurrent(CaseName)
+ end,
+
+ ?line ok = erl_ddll:unload_driver(CaseName),
+ ?line Result.
+
+one_shot(CaseName, Command) ->
?line Port = open_port({spawn, atom_to_list(CaseName)}, []),
?line true = is_port(Port),
?line Port ! {self(), {command, Command}},
@@ -217,8 +232,45 @@ run_drv_case(Config, Command) ->
{Port, closed} ->
ok
end,
- ?line ok = erl_ddll:unload_driver(CaseName),
- ?line Result.
+ Result.
+
+
+many_shot(CaseName, Command) ->
+ ?line Port = open_port({spawn, atom_to_list(CaseName)}, []),
+ ?line true = is_port(Port),
+ Result = repeat_while(fun() ->
+ ?line Port ! {self(), {command, Command}},
+ receive_drv_result(Port, CaseName) =:= continue
+ end),
+ ?line Port ! {self(), close},
+ ?line receive
+ {Port, closed} ->
+ ok
+ end,
+ Result.
+
+concurrent(CaseName) ->
+ one_shot(CaseName, "init"),
+ PRs = lists:map(fun(I) -> spawn_opt(fun() ->
+ many_shot(CaseName, "")
+ end,
+ [monitor, {scheduler,I}])
+ end,
+ lists:seq(1, erlang:system_info(schedulers))),
+ lists:foreach(fun({Pid,Ref}) ->
+ receive {'DOWN', Ref, process, Pid, Reason} ->
+ Reason
+ end
+ end,
+ PRs),
+ ok.
+
+repeat_while(Fun) ->
+ io:format("~p calls fun\n", [self()]),
+ case Fun() of
+ true -> repeat_while(Fun);
+ false -> ok
+ end.
receive_drv_result(Port, CaseName) ->
?line receive
@@ -236,11 +288,11 @@ receive_drv_result(Port, CaseName) ->
{succeeded, Port, CaseName, ""} ->
?line succeeded;
{succeeded, Port, CaseName, Comment} ->
- ?line {comment, Comment}
+ ?line {comment, Comment};
+ continue ->
+ continue
end.
-start_node(Config) ->
- start_node(Config, []).
start_node(Config, Opts) when is_list(Config), is_list(Opts) ->
Pa = filename:dirname(code:which(?MODULE)),
Name = list_to_atom(atom_to_list(?MODULE)
diff --git a/erts/emulator/test/alloc_SUITE_data/Makefile.src b/erts/emulator/test/alloc_SUITE_data/Makefile.src
index a441fe946b..e31de54e1b 100644
--- a/erts/emulator/test/alloc_SUITE_data/Makefile.src
+++ b/erts/emulator/test/alloc_SUITE_data/Makefile.src
@@ -25,7 +25,8 @@ TEST_DRVS = basic@dll@ \
bucket_mask@dll@ \
rbtree@dll@ \
mseg_clear_cache@dll@ \
- cpool@dll@
+ cpool@dll@ \
+ migration@dll@
CC = @CC@
LD = @LD@
diff --git a/erts/emulator/test/alloc_SUITE_data/allocator_test.h b/erts/emulator/test/alloc_SUITE_data/allocator_test.h
index a7f12c6ca8..bfd0bb3094 100644
--- a/erts/emulator/test/alloc_SUITE_data/allocator_test.h
+++ b/erts/emulator/test/alloc_SUITE_data/allocator_test.h
@@ -85,6 +85,7 @@ typedef void* erts_cond;
#define CPOOL_DELETE(A,B) ((Carrier_t *) ALC_TEST2(0x022, (A), (B)))
#define CPOOL_IS_EMPTY(A) ((int) ALC_TEST1(0x023, (A)))
#define CPOOL_IS_IN_POOL(A,B) ((int) ALC_TEST2(0x024, (A), (B)))
+#define UMEM2BLK_TEST(P) ((Block_t*) ALC_TEST1(0x025, (P)))
/* From erl_goodfit_alloc.c */
#define BKT_IX(A, S) ((Ulong) ALC_TEST2(0x100, (A), (S)))
@@ -144,5 +145,6 @@ typedef void* erts_cond;
#define IS_SMP_ENABLED ((int) ALC_TEST0(0xf13))
#define ALLOC_TEST(S) ((void*) ALC_TEST1(0xf14, (S)))
#define FREE_TEST(P) ((void) ALC_TEST1(0xf15, (P)))
+#define SET_TEST_MBC_USER_HEADER(SZ,CMBC,DMBC) ((int)ALC_TEST3(0xf16, (SZ), (CMBC), (DMBC)))
#endif
diff --git a/erts/emulator/test/alloc_SUITE_data/migration.c b/erts/emulator/test/alloc_SUITE_data/migration.c
new file mode 100644
index 0000000000..dd58a0d3dd
--- /dev/null
+++ b/erts/emulator/test/alloc_SUITE_data/migration.c
@@ -0,0 +1,229 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2014. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Test the carrier migration logic
+ */
+
+#ifndef __WIN32__
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+#endif
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include "testcase_driver.h"
+#include "allocator_test.h"
+
+#define FATAL_ASSERT(A) \
+ ((void) ((A) \
+ ? 1 \
+ : (fatal_assert_failed(#A, \
+ (char *) __FILE__, \
+ __LINE__), \
+ 0)))
+
+static void
+fatal_assert_failed(char* expr, char* file, int line)
+{
+ fflush(stdout);
+ fprintf(stderr, "%s:%d: Assertion failed: %s\n",
+ file, line, expr);
+ fflush(stderr);
+ abort();
+}
+
+
+char *
+testcase_name(void)
+{
+ return "migration";
+}
+
+void
+testcase_cleanup(TestCaseState_t *tcs)
+{
+}
+
+#define MAX_BLOCK_PER_THR 100
+#define MAX_ROUNDS 10
+
+typedef struct MyBlock_ {
+ struct MyBlock_* next;
+ struct MyBlock_** prevp;
+} MyBlock;
+
+typedef struct {
+ MyBlock* blockv[MAX_BLOCK_PER_THR];
+ enum { GROWING, SHRINKING, CLEANUP, DONE } phase;
+ int ix;
+ int round;
+} MigrationState;
+
+typedef struct {
+ ErlDrvMutex* mtx;
+ int nblocks;
+ MyBlock* first;
+} MyCrrInfo;
+
+
+static int crr_info_offset = -1;
+static void (*orig_create_mbc_fn)(Allctr_t *allctr, Carrier_t *carrier);
+static void (*orig_destroying_mbc_fn)(Allctr_t *allctr, Carrier_t *carrier);
+
+static void my_creating_mbc(Allctr_t *allctr, Carrier_t *carrier)
+{
+ MyCrrInfo* mci = (MyCrrInfo*) ((char*)carrier + crr_info_offset);
+ if (orig_create_mbc_fn)
+ orig_create_mbc_fn(allctr, carrier);
+
+ mci->mtx = erl_drv_mutex_create("alloc_SUITE.migration");
+ mci->nblocks = 0;
+ mci->first = NULL;
+}
+
+static void my_destroying_mbc(Allctr_t *allctr, Carrier_t *carrier)
+{
+ MyCrrInfo* mci = (MyCrrInfo*) ((char*)carrier + crr_info_offset);
+
+ FATAL_ASSERT(mci->nblocks == 0);
+ FATAL_ASSERT(mci->first == NULL);
+ erl_drv_mutex_destroy(mci->mtx);
+
+ if (orig_destroying_mbc_fn)
+ orig_destroying_mbc_fn(allctr, carrier);
+}
+
+
+static void setup(TestCaseState_t* tcs)
+{
+ void* creating_mbc_arg = (void*)my_creating_mbc;
+ void* destroying_mbc_arg = (void*)my_destroying_mbc;
+ crr_info_offset = SET_TEST_MBC_USER_HEADER(sizeof(MyCrrInfo),
+ &creating_mbc_arg,
+ &destroying_mbc_arg);
+ ASSERT(tcs, crr_info_offset >= 0);
+ orig_create_mbc_fn = creating_mbc_arg;
+ orig_destroying_mbc_fn = destroying_mbc_arg;
+}
+
+static void add_block(MyBlock* p)
+{
+ MyCrrInfo* mci = (MyCrrInfo*)((char*)BLK_TO_MBC(UMEM2BLK_TEST(p)) + crr_info_offset);
+
+ erl_drv_mutex_lock(mci->mtx);
+ mci->nblocks++;
+ p->next = mci->first;
+ p->prevp = &mci->first;
+ mci->first = p;
+ if (p->next)
+ p->next->prevp = &p->next;
+ erl_drv_mutex_unlock(mci->mtx);
+}
+
+static void remove_block(MyBlock* p)
+{
+ MyCrrInfo* mci = (MyCrrInfo*)((char*)BLK_TO_MBC(UMEM2BLK_TEST(p)) + crr_info_offset);
+
+ erl_drv_mutex_lock(mci->mtx);
+ mci->nblocks--;
+ if (p->next)
+ p->next->prevp = p->prevp;
+ *p->prevp = p->next;
+ erl_drv_mutex_unlock(mci->mtx);
+}
+
+void
+testcase_run(TestCaseState_t *tcs)
+{
+ MigrationState* state = (MigrationState*) tcs->extra;
+
+ if (tcs->command_len == 4
+ && memcmp(tcs->command, "init", tcs->command_len) == 0) {
+ setup(tcs);
+ return;
+ }
+
+ if (!tcs->extra) {
+ if (!IS_SMP_ENABLED)
+ testcase_skipped(tcs, "No SMP support");
+
+ tcs->extra = driver_alloc(sizeof(MigrationState));
+ state = (MigrationState*) tcs->extra;
+ memset(state->blockv, 0, sizeof(state->blockv));
+ state->phase = GROWING;
+ state->ix = 0;
+ state->round = 0;
+ }
+
+ switch (state->phase) {
+ case GROWING: {
+ MyBlock* p;
+ FATAL_ASSERT(!state->blockv[state->ix]);
+ p = ALLOC_TEST((1 << 18) / 5);
+ FATAL_ASSERT(p);
+ add_block(p);
+ state->blockv[state->ix] = p;
+ do {
+ if (++state->ix >= MAX_BLOCK_PER_THR) {
+ state->phase = SHRINKING;
+ state->ix = 0;
+ break;
+ }
+ } while (state->blockv[state->ix] != NULL);
+ break;
+ }
+ case SHRINKING:
+ FATAL_ASSERT(state->blockv[state->ix]);
+ remove_block(state->blockv[state->ix]);
+ FREE_TEST(state->blockv[state->ix]);
+ state->blockv[state->ix] = NULL;
+
+ state->ix += 1 + ((state->ix % 3) == 0);
+ if (state->ix >= MAX_BLOCK_PER_THR) {
+ if (++state->round >= MAX_ROUNDS) {
+ state->phase = CLEANUP;
+ } else {
+ state->phase = GROWING;
+ }
+ state->ix = 0;
+ }
+ break;
+
+ case CLEANUP:
+ if (state->blockv[state->ix]) {
+ remove_block(state->blockv[state->ix]);
+ FREE_TEST(state->blockv[state->ix]);
+ }
+ if (++state->ix >= MAX_BLOCK_PER_THR)
+ state->phase = DONE;
+ break;
+
+ default:
+ FATAL_ASSERT(!"Invalid phase");
+ }
+
+ if (state->phase == DONE) {
+ driver_free(tcs->extra);
+ tcs->extra = NULL;
+ }
+ else
+ testcase_continue(tcs);
+}
diff --git a/erts/emulator/test/alloc_SUITE_data/testcase_driver.c b/erts/emulator/test/alloc_SUITE_data/testcase_driver.c
index bc674c56b7..d04eb1bcb0 100644
--- a/erts/emulator/test/alloc_SUITE_data/testcase_driver.c
+++ b/erts/emulator/test/alloc_SUITE_data/testcase_driver.c
@@ -39,6 +39,7 @@
#define TESTCASE_FAILED 0
#define TESTCASE_SKIPPED 1
#define TESTCASE_SUCCEEDED 2
+#define TESTCASE_CONTINUE 3
typedef struct {
TestCaseState_t visible;
@@ -129,6 +130,12 @@ testcase_drv_run(ErlDrvData drv_data, char *buf, ErlDrvSizeT len)
}
switch (itcs->result) {
+ case TESTCASE_CONTINUE:
+ msg[0] = ERL_DRV_ATOM;
+ msg[1] = driver_mk_atom("continue");
+ erl_drv_output_term(itcs->port_id, msg, 2);
+ return;
+
case TESTCASE_SUCCEEDED:
result_atom = driver_mk_atom("succeeded");
break;
@@ -239,6 +246,13 @@ void testcase_skipped(TestCaseState_t *tcs, char *frmt, ...)
longjmp(itcs->done_jmp_buf, 1);
}
+void testcase_continue(TestCaseState_t *tcs)
+{
+ InternalTestCaseState_t *itcs = (InternalTestCaseState_t *) tcs;
+ itcs->result = TESTCASE_CONTINUE;
+ longjmp(itcs->done_jmp_buf, 1);
+}
+
void testcase_failed(TestCaseState_t *tcs, char *frmt, ...)
{
InternalTestCaseState_t *itcs = (InternalTestCaseState_t *) tcs;
diff --git a/erts/emulator/test/alloc_SUITE_data/testcase_driver.h b/erts/emulator/test/alloc_SUITE_data/testcase_driver.h
index 5d17eaec64..5d439735b7 100644
--- a/erts/emulator/test/alloc_SUITE_data/testcase_driver.h
+++ b/erts/emulator/test/alloc_SUITE_data/testcase_driver.h
@@ -37,6 +37,7 @@ typedef struct {
void testcase_printf(TestCaseState_t *tcs, char *frmt, ...);
void testcase_succeeded(TestCaseState_t *tcs, char *frmt, ...);
void testcase_skipped(TestCaseState_t *tcs, char *frmt, ...);
+void testcase_continue(TestCaseState_t *tcs);
void testcase_failed(TestCaseState_t *tcs, char *frmt, ...);
int testcase_assertion_failed(TestCaseState_t *tcs, char *file, int line,
char *assertion);