aboutsummaryrefslogtreecommitdiffstats
path: root/erts
diff options
context:
space:
mode:
Diffstat (limited to 'erts')
-rw-r--r--erts/doc/src/erl.xml17
-rw-r--r--erts/doc/src/erl_nif.xml39
-rw-r--r--erts/emulator/beam/erl_db_hash.c34
-rw-r--r--erts/emulator/beam/erl_db_hash.h2
-rw-r--r--erts/emulator/beam/erl_nif.c25
-rw-r--r--erts/emulator/beam/external.c19
-rw-r--r--erts/emulator/drivers/common/inet_drv.c247
-rw-r--r--erts/emulator/sys/common/erl_mmap.c8
-rw-r--r--erts/emulator/sys/common/erl_mmap.h12
-rw-r--r--erts/emulator/sys/unix/sys_time.c16
-rw-r--r--erts/emulator/test/trace_port_SUITE.erl22
11 files changed, 276 insertions, 165 deletions
diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml
index 7b90a1ccca..37387f2c59 100644
--- a/erts/doc/src/erl.xml
+++ b/erts/doc/src/erl.xml
@@ -915,6 +915,13 @@
number of dirty CPU schedulers online can be changed at run time via
<seealso marker="erlang#system_flag_dirty_cpu_schedulers_online">erlang:system_flag(dirty_cpu_schedulers_online, DirtyCPUSchedulersOnline)</seealso>.
</p>
+ <p>
+ The amount of dirty CPU schedulers is limited by the amount of
+ normal schedulers in order to limit the effect on processes
+ executing on ordinary schedulers. If the amount of dirty CPU
+ schedulers was allowed to be unlimited, dirty CPU bound jobs would
+ potentially starve normal jobs.
+ </p>
<p>This option is ignored if the emulator doesn't have threading support
enabled. Currently, <em>this option is experimental</em> and is supported only
if the emulator was configured and built with support for dirty schedulers
@@ -944,7 +951,7 @@
enabled (it's disabled by default).
</p>
</item>
- <tag><marker id="+SDio"/><c><![CDATA[+SDio IOSchedulers]]></c></tag>
+ <tag><marker id="+SDio"/><c><![CDATA[+SDio DirtyIOSchedulers]]></c></tag>
<item>
<p>Sets the number of dirty I/O scheduler threads to create when threading
support has been enabled. The valid range is 0-1024. By default, the number
@@ -952,6 +959,14 @@
threads in the <seealso marker="#async_thread_pool_size">async thread pool
</seealso>.
</p>
+ <p>
+ The amount of dirty IO schedulers is not limited by the amount of
+ normal schedulers <seealso marker="#+SDcpu">like the amount of
+ dirty CPU schedulers</seealso>. This since only I/O bound work is
+ expected to execute on dirty I/O schedulers. If the user should schedule CPU
+ bound jobs on dirty I/O schedulers, these jobs might starve ordinary
+ jobs executing on ordinary schedulers.
+ </p>
<p>This option is ignored if the emulator doesn't have threading support
enabled. Currently, <em>this option is experimental</em> and is supported only
if the emulator was configured and built with support for dirty schedulers
diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml
index 8b02b3bae1..f3921f1922 100644
--- a/erts/doc/src/erl_nif.xml
+++ b/erts/doc/src/erl_nif.xml
@@ -385,18 +385,39 @@ ok
<p>
A NIF that cannot be split and cannot execute in a millisecond or
less is called a "dirty NIF" because it performs work that the
- Erlang runtime cannot handle cleanly. Applications that make use
- of such functions must indicate to the runtime that the functions
- are dirty so they can be handled specially. To schedule a dirty
- NIF for execution, the appropriate flags value can be set for the
- NIF in its <seealso marker="#ErlNifFunc"><c>ErlNifFunc</c></seealso>
+ ordinary schedulers of the Erlang runtime system cannot handle cleanly.
+ Applications that make use of such functions must indicate to the
+ runtime that the functions are dirty so they can be handled
+ specially. This is handled by executing dirty jobs on a separate
+ set of schedulers called dirty schedulers. A dirty NIF executing
+ on a dirty scheduler does not have the same duration restriction
+ as a normal NIF.
+ </p>
+
+ <p>
+ It is important to classify the dirty job correct. An I/O bound
+ job should be classified as such, and a CPU bound job should be
+ classified as such. If you should classify CPU bound jobs
+ as I/O bound jobs, dirty I/O schedulers might starve ordinary
+ schedulers. I/O bound jobs are expected to either block waiting
+ for I/O, and/or spend a limited amount of time moving data.
+ </p>
+
+ <p>
+ To schedule a dirty NIF for execution, the appropriate
+ <c>flags</c> value can be set for the NIF in its
+ <seealso marker="#ErlNifFunc"><c>ErlNifFunc</c></seealso>
entry, or the application can call
<seealso marker="#enif_schedule_nif"><c>enif_schedule_nif</c></seealso>,
passing to it a pointer to the dirty NIF to be executed and
indicating with the <c>flags</c> argument whether it expects the
- operation to be CPU-bound or I/O-bound. A dirty NIF executing
- on a dirty scheduler does not have the same duration restriction
- as a normal NIF.
+ operation to be CPU-bound or I/O-bound. A job that alternates
+ between I/O bound and CPU bound can be reclassified and
+ rescheduled using <c>enif_schedule_nif</c> so that it executes on
+ the correct type of dirty scheduler at all times. For more
+ information see the documentation of the <c>erl</c> command line
+ arguments <seealso marker="erl#+SDcpu"><c>+SDcpu</c></seealso>,
+ and <seealso marker="erl#+SDio"><c>+SDio</c></seealso>.
</p>
<p>
@@ -1702,7 +1723,7 @@ enif_map_iterator_destroy(env, &amp;iter);
be converted to an atom, <c>enif_schedule_nif</c> returns a <c>badarg</c> exception.</p>
<p>The <c>flags</c> argument must be set to 0 for a regular NIF, or if the emulator was built the
experimental dirty scheduler support enabled, <c>flags</c> can be set to either <c>ERL_NIF_DIRTY_JOB_CPU_BOUND</c>
- if the job is expected to be primarily CPU-bound, or <c>ERL_NIF_DIRTY_JOB_IO_BOUND</c> for jobs that will
+ if the job is expected to be CPU-bound, or <c>ERL_NIF_DIRTY_JOB_IO_BOUND</c> for jobs that will
be I/O-bound. If dirty scheduler threads are not available in the emulator, a try to schedule such a job
will result in a <c>badarg</c> exception.</p>
diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c
index 074ac6d64e..74979f984a 100644
--- a/erts/emulator/beam/erl_db_hash.c
+++ b/erts/emulator/beam/erl_db_hash.c
@@ -95,8 +95,7 @@
/*
* The following symbols can be manipulated to "tune" the linear hash array
*/
-#define GROW_LIMIT(NACTIVE) ((NACTIVE)*2)
-#define SHRINK_LIMIT(NACTIVE) ((NACTIVE) / 2)
+#define CHAIN_LEN 6 /* Medium bucket chain len */
/* Number of slots per segment */
#define SEGSZ_EXP 8
@@ -464,7 +463,7 @@ db_finalize_dbterm_hash(int cret, DbUpdateHandle* handle);
static ERTS_INLINE void try_shrink(DbTableHash* tb)
{
int nactive = NACTIVE(tb);
- if (nactive > SEGSZ && NITEMS(tb) < SHRINK_LIMIT(nactive)
+ if (nactive > SEGSZ && NITEMS(tb) < (nactive * CHAIN_LEN)
&& !IS_FIXED(tb)) {
shrink(tb, nactive);
}
@@ -671,8 +670,8 @@ int db_create_hash(Process *p, DbTable *tbl)
tb->nsegs = NSEG_1;
tb->nslots = SEGSZ;
-#ifdef ERTS_SMP
erts_smp_atomic_init_nob(&tb->is_resizing, 0);
+#ifdef ERTS_SMP
if (tb->common.type & DB_FINE_LOCKED) {
erts_smp_rwmtx_opt_t rwmtx_opt = ERTS_SMP_RWMTX_OPT_DEFAULT_INITER;
int i;
@@ -863,7 +862,7 @@ Lnew:
WUNLOCK_HASH(lck);
{
int nactive = NACTIVE(tb);
- if (nitems > GROW_LIMIT(nactive) && !IS_FIXED(tb)) {
+ if (nitems > nactive * (CHAIN_LEN+1) && !IS_FIXED(tb)) {
grow(tb, nactive);
}
}
@@ -2251,12 +2250,12 @@ static int db_free_table_continue_hash(DbTable *tbl)
done /= 2;
while(tb->nslots != 0) {
- done += 1 + SEGSZ/64 + free_seg(tb, 1);
+ free_seg(tb, 1);
/*
* If we have done enough work, get out here.
*/
- if (done >= DELETE_RECORD_LIMIT) {
+ if (++done >= (DELETE_RECORD_LIMIT / CHAIN_LEN / SEGSZ)) {
return 0; /* Not done */
}
}
@@ -2605,22 +2604,23 @@ static Eterm build_term_list(Process* p, HashDbTerm* ptr1, HashDbTerm* ptr2,
static ERTS_INLINE int
begin_resizing(DbTableHash* tb)
{
-#ifdef ERTS_SMP
if (DB_USING_FINE_LOCKING(tb))
- return !erts_atomic_xchg_acqb(&tb->is_resizing, 1);
- else
- ERTS_LC_ASSERT(erts_lc_rwmtx_is_rwlocked(&tb->common.rwlock));
-#endif
- return 1;
+ return !erts_smp_atomic_xchg_acqb(&tb->is_resizing, 1);
+ else {
+ if (erts_smp_atomic_read_nob(&tb->is_resizing))
+ return 0;
+ erts_smp_atomic_set_nob(&tb->is_resizing, 1);
+ return 1;
+ }
}
static ERTS_INLINE void
done_resizing(DbTableHash* tb)
{
-#ifdef ERTS_SMP
if (DB_USING_FINE_LOCKING(tb))
- erts_atomic_set_relb(&tb->is_resizing, 0);
-#endif
+ erts_smp_atomic_set_relb(&tb->is_resizing, 0);
+ else
+ erts_smp_atomic_set_nob(&tb->is_resizing, 0);
}
/* Grow table with one new bucket.
@@ -2871,7 +2871,7 @@ db_lookup_dbterm_hash(Process *p, DbTable *tbl, Eterm key, Eterm obj,
int nitems = erts_smp_atomic_inc_read_nob(&tb->common.nitems);
int nactive = NACTIVE(tb);
- if (nitems > GROW_LIMIT(nactive) && !IS_FIXED(tb)) {
+ if (nitems > nactive * (CHAIN_LEN + 1) && !IS_FIXED(tb)) {
grow(tb, nactive);
}
}
diff --git a/erts/emulator/beam/erl_db_hash.h b/erts/emulator/beam/erl_db_hash.h
index 081ff8fafc..e654363cd5 100644
--- a/erts/emulator/beam/erl_db_hash.h
+++ b/erts/emulator/beam/erl_db_hash.h
@@ -60,8 +60,8 @@ typedef struct db_table_hash {
/* List of slots where elements have been deleted while table was fixed */
erts_smp_atomic_t fixdel; /* (FixedDeletion*) */
erts_smp_atomic_t nactive; /* Number of "active" slots */
-#ifdef ERTS_SMP
erts_smp_atomic_t is_resizing; /* grow/shrink in progress */
+#ifdef ERTS_SMP
DbTableHashFineLocks* locks;
#endif
#ifdef VALGRIND
diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c
index 039f97ef43..23931f0e54 100644
--- a/erts/emulator/beam/erl_nif.c
+++ b/erts/emulator/beam/erl_nif.c
@@ -769,32 +769,25 @@ enif_port_command(ErlNifEnv *env, const ErlNifPort* to_port,
if (scheduler > 0)
prt = erts_port_lookup(to_port->port_id, iflags);
-#ifdef ERTS_DIRTY_SCHEDULERS
- else if (scheduler < 0) {
+ else {
+#ifdef ERTS_SMP
if (ERTS_PROC_IS_EXITING(c_p))
return 0;
prt = erts_thr_port_lookup(to_port->port_id, iflags);
- }
+#else
+ erts_exit(ERTS_ABORT_EXIT,
+ "enif_port_command: called from non-scheduler "
+ "thread on non-SMP VM");
#endif
- else {
- erts_exit(ERTS_ABORT_EXIT, "enif_port_command: "
- "called from non-scheduler thread");
}
if (!prt)
res = 0;
- else {
-
- if (IS_TRACED_FL(prt, F_TRACE_RECEIVE))
- trace_port_receive(prt, c_p->common.id, am_command, msg);
-
- res = erts_port_output_async(prt, c_p->common.id, msg);
- }
+ else
+ res = erts_port_output_async(prt, c_p->common.id, msg);
-#ifdef ERTS_DIRTY_SCHEDULERS
- if (scheduler < 0)
+ if (scheduler <= 0)
erts_port_dec_refc(prt);
-#endif
return res;
}
diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c
index 3c002d43a7..beed847578 100644
--- a/erts/emulator/beam/external.c
+++ b/erts/emulator/beam/external.c
@@ -2159,12 +2159,23 @@ enc_atom(ErtsAtomCacheMap *acmp, Eterm atom, byte *ep, Uint32 dflags)
return ep;
}
+/*
+ * We use this atom as sysname in local pid/port/refs
+ * for the ETS compressed format (DFLAG_INTERNAL_TAGS).
+ *
+ * We used atom '' earlier but that turned out to cause problems
+ * for buggy erl_interface/ic usage of c-nodes with empty node names.
+ * A long atom reduces risk of nodes actually called this and the length
+ * does not matter anyway as it's encoded with atom index (ATOM_INTERNAL_REF2).
+ */
+#define INTERNAL_LOCAL_SYSNAME am_await_microstate_accounting_modifications
+
static byte*
enc_pid(ErtsAtomCacheMap *acmp, Eterm pid, byte* ep, Uint32 dflags)
{
Uint on, os;
Eterm sysname = ((is_internal_pid(pid) && (dflags & DFLAG_INTERNAL_TAGS))
- ? am_Empty : pid_node_name(pid));
+ ? INTERNAL_LOCAL_SYSNAME : pid_node_name(pid));
Uint32 creation = pid_creation(pid);
byte* tagp = ep++;
@@ -2268,7 +2279,7 @@ dec_atom(ErtsDistExternal *edep, byte* ep, Eterm* objp)
static ERTS_INLINE ErlNode* dec_get_node(Eterm sysname, Uint32 creation)
{
- if (sysname == am_Empty) /* && DFLAG_INTERNAL_TAGS */
+ if (sysname == INTERNAL_LOCAL_SYSNAME) /* && DFLAG_INTERNAL_TAGS */
return erts_this_node;
if (sysname == erts_this_node->sysname
@@ -2555,7 +2566,7 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
case EXTERNAL_REF_DEF: {
Uint32 *ref_num;
Eterm sysname = (((dflags & DFLAG_INTERNAL_TAGS) && is_internal_ref(obj))
- ? am_Empty : ref_node_name(obj));
+ ? INTERNAL_LOCAL_SYSNAME : ref_node_name(obj));
Uint32 creation = ref_creation(obj);
byte* tagp = ep++;
@@ -2584,7 +2595,7 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
case PORT_DEF:
case EXTERNAL_PORT_DEF: {
Eterm sysname = (((dflags & DFLAG_INTERNAL_TAGS) && is_internal_port(obj))
- ? am_Empty : port_node_name(obj));
+ ? INTERNAL_LOCAL_SYSNAME : port_node_name(obj));
Uint32 creation = port_creation(obj);
byte* tagp = ep++;
diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c
index 93ea9f5dcb..5ce0e1de9e 100644
--- a/erts/emulator/drivers/common/inet_drv.c
+++ b/erts/emulator/drivers/common/inet_drv.c
@@ -591,6 +591,8 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n)
(((unsigned char*) (s))[1] << 8) | \
(((unsigned char*) (s))[0]))
+#ifdef HAVE_SYS_UN_H
+
/* strnlen doesn't exist everywhere */
static size_t my_strnlen(const char *s, size_t maxlen)
{
@@ -608,6 +610,8 @@ static int is_nonzero(const char *s, size_t n)
return 0;
}
+#endif
+
#ifdef VALGRIND
# include <valgrind/memcheck.h>
#else
@@ -1301,6 +1305,8 @@ static ErlDrvTermData am_ssl_tls;
static ErlDrvTermData am_udp;
static ErlDrvTermData am_udp_passive;
static ErlDrvTermData am_udp_error;
+#endif
+#ifdef HAVE_SYS_UN_H
static ErlDrvTermData am_local;
#endif
#ifdef HAVE_SCTP
@@ -1328,7 +1334,10 @@ static ErlDrvTermData am_ipv6_v6only;
static ErlDrvTermData am_netns;
#endif
-/* speical errors for bad ports and sequences */
+static char str_eafnosupport[] = "eafnosupport";
+static char str_einval[] = "einval";
+
+/* special errors for bad ports and sequences */
#define EXBADPORT "exbadport"
#define EXBADSEQ "exbadseq"
@@ -3846,6 +3855,8 @@ static int inet_init()
#ifdef HAVE_UDP
INIT_ATOM(udp_passive);
INIT_ATOM(udp_error);
+#endif
+#ifdef HAVE_SYS_UN_H
INIT_ATOM(local);
#endif
INIT_ATOM(empty_out_q);
@@ -3955,74 +3966,88 @@ static int inet_init()
/*
** Set an inaddr structure:
-** src = [P1,P0,X1,X2,.....]
+** *src = [P1,P0,X1,X2,.....]
** dst points to a structure large enugh to keep any kind
** of inaddr.
** *len is set to length of src on call
** and is set to actual length of dst on return
-** return NULL on error and ptr after port address on success
+** return NULL if ok or ptr to errno string for error
*/
static char* inet_set_address(int family, inet_address* dst,
- char* src, ErlDrvSizeT* len)
+ char* *src, ErlDrvSizeT* len)
{
short port;
- if ((family == AF_INET) && (*len >= 2+4)) {
+ switch (family) {
+ case AF_INET: {
+ if (*len < 2+4) return str_einval;
sys_memzero((char*)dst, sizeof(struct sockaddr_in));
- port = get_int16(src);
+ port = get_int16(*src);
#ifndef NO_SA_LEN
dst->sai.sin_len = sizeof(struct sockaddr_in);
#endif
dst->sai.sin_family = family;
dst->sai.sin_port = sock_htons(port);
- sys_memcpy(&dst->sai.sin_addr, src+2, 4);
+ sys_memcpy(&dst->sai.sin_addr, (*src)+2, 4);
*len = sizeof(struct sockaddr_in);
- return src + 2+4;
+ *src += 2 + 4;
+ return NULL;
}
#if defined(HAVE_IN6) && defined(AF_INET6)
- else if ((family == AF_INET6) && (*len >= 2+16)) {
+ case AF_INET6: {
+ if (*len < 2+16) return str_einval;
sys_memzero((char*)dst, sizeof(struct sockaddr_in6));
- port = get_int16(src);
+ port = get_int16(*src);
#ifndef NO_SA_LEN
dst->sai6.sin6_len = sizeof(struct sockaddr_in6);
#endif
dst->sai6.sin6_family = family;
dst->sai6.sin6_port = sock_htons(port);
dst->sai6.sin6_flowinfo = 0; /* XXX this may be set as well ?? */
- sys_memcpy(&dst->sai6.sin6_addr, src+2, 16);
- *len = sizeof(struct sockaddr_in6);
- return src + 2+16;
+ sys_memcpy(&dst->sai6.sin6_addr, (*src)+2, 16);
+ *len = sizeof(struct sockaddr_in6);
+ *src += 2 + 16;
+ return NULL;
}
#endif
#ifdef HAVE_SYS_UN_H
- else if ((family == AF_UNIX) && (*len >= 1)) {
- int n = *((unsigned char*)src);
- if ((*len < 1+n) || (sizeof(dst->sal.sun_path) < n+1))
- return NULL;
+ case AF_UNIX: {
+ int n;
+ if (*len == 0) return str_einval;
+ n = *((unsigned char*)(*src)); /* Length field */
+ if ((*len < 1+n) || (sizeof(dst->sal.sun_path) < n+1)) {
+ return str_einval;
+ }
sys_memzero((char*)dst, sizeof(struct sockaddr_un));
dst->sal.sun_family = family;
- sys_memcpy(dst->sal.sun_path, src+1, n);
+ sys_memcpy(dst->sal.sun_path, (*src)+1, n);
*len = offsetof(struct sockaddr_un, sun_path) + n;
- return src + 1 + n;
+ *src += 1 + n;
+ return NULL;
}
#endif
- return NULL;
+ }
+ return str_eafnosupport;
}
/*
** Set an inaddr structure, address family comes from source data,
** or from argument if source data specifies constant address.
**
-** src = [TAG,P1,P0] when TAG = INET_AF_ANY | INET_AF_LOOPBACK
-** src = [TAG,P1,P0,X1,X2,...] when TAG = INET_AF_INET | INET_AF_INET6 | INET_AF_LOCAL
+** *src = [TAG,P1,P0]
+** when TAG = INET_AF_ANY | INET_AF_LOOPBACK
+** *src = [TAG,P1,P0,X1,X2,...]
+** when TAG = INET_AF_INET | INET_AF_INET6 | INET_AF_LOCAL
+** *src = [TAG,Len,...]
+** when TAG = INET_AF_LOCAL
*/
static char *inet_set_faddress(int family, inet_address* dst,
- char *src, ErlDrvSizeT* len) {
+ char* *src, ErlDrvSizeT* len) {
int tag;
- if (*len < 1) return NULL;
+ if (*len < 1) return str_einval;
(*len) --;
- tag = *(src ++);
+ tag = *((*src) ++);
switch (tag) {
case INET_AF_INET:
family = AF_INET;
@@ -4042,8 +4067,8 @@ static char *inet_set_faddress(int family, inet_address* dst,
case INET_AF_LOOPBACK: {
int port;
- if (*len < 2) return NULL;
- port = get_int16(src);
+ if (*len < 2) return str_einval;
+ port = get_int16(*src);
switch (family) {
case AF_INET: {
struct in_addr addr;
@@ -4055,7 +4080,7 @@ static char *inet_set_faddress(int family, inet_address* dst,
addr.s_addr = sock_htonl(INADDR_LOOPBACK);
break;
default:
- return NULL;
+ return str_einval;
}
sys_memzero((char*)dst, sizeof(struct sockaddr_in));
#ifndef NO_SA_LEN
@@ -4077,7 +4102,7 @@ static char *inet_set_faddress(int family, inet_address* dst,
paddr = &in6addr_loopback;
break;
default:
- return NULL;
+ return str_einval;
}
sys_memzero((char*)dst, sizeof(struct sockaddr_in6));
#ifndef NO_SA_LEN
@@ -4091,12 +4116,13 @@ static char *inet_set_faddress(int family, inet_address* dst,
} break;
# endif
default:
- return NULL;
+ return str_einval;
}
- return src + 2;
+ *src += 2;
+ return NULL;
} break;
default:
- return NULL;
+ return str_eafnosupport;
}
return inet_set_address(family, dst, src, len);
}
@@ -6691,7 +6717,6 @@ static int sctp_set_opts(inet_descriptor* desc, char* ptr, int len)
case SCTP_OPT_SET_PEER_PRIMARY_ADDR:
{
ErlDrvSizeT alen;
- char *after;
CHKLEN(curr, ASSOC_ID_LEN);
/* XXX: These 2 opts have isomorphic value data structures,
@@ -6702,12 +6727,9 @@ static int sctp_set_opts(inet_descriptor* desc, char* ptr, int len)
/* Fill in "arg.prim.sspp_addr": */
alen = ptr + len - curr;
- after = inet_set_faddress(desc->sfamily,
- (inet_address*) (&arg.prim.sspp_addr),
- curr, &alen);
- if (after == NULL)
- return -1;
- curr = after;
+ if (inet_set_faddress
+ (desc->sfamily, (inet_address*) (&arg.prim.sspp_addr),
+ &curr, &alen) != NULL) return -1;
proto = IPPROTO_SCTP;
if (eopt == SCTP_OPT_PRIMARY_ADDR)
@@ -6733,7 +6755,6 @@ static int sctp_set_opts(inet_descriptor* desc, char* ptr, int len)
case SCTP_OPT_PEER_ADDR_PARAMS:
{
ErlDrvSizeT alen;
- char *after;
# ifdef HAVE_STRUCT_SCTP_PADDRPARAMS_SPP_FLAGS
int eflags, cflags, hb_enable, hb_disable,
pmtud_enable, pmtud_disable;
@@ -6748,12 +6769,9 @@ static int sctp_set_opts(inet_descriptor* desc, char* ptr, int len)
/* Fill in "pap.spp_address": */
alen = ptr + len - curr;
- after = inet_set_faddress(desc->sfamily,
- (inet_address*) (&arg.pap.spp_address),
- curr, &alen);
- if (after == NULL)
- return -1;
- curr = after;
+ if (inet_set_faddress
+ (desc->sfamily, (inet_address*) (&arg.pap.spp_address),
+ &curr, &alen) != NULL) return -1;
CHKLEN(curr, 4 + 2 + 3*4);
@@ -7740,7 +7758,7 @@ static ErlDrvSSizeT sctp_fill_opts(inet_descriptor* desc,
struct sctp_paddrparams ap;
unsigned int sz = sizeof(ap);
int n;
- char *after;
+ char *before, *xerror;
ErlDrvSizeT alen;
if (buflen < ASSOC_ID_LEN) RETURN_ERROR(spec, -EINVAL);
@@ -7748,13 +7766,22 @@ static ErlDrvSSizeT sctp_fill_opts(inet_descriptor* desc,
buf += ASSOC_ID_LEN;
buflen -= ASSOC_ID_LEN;
alen = buflen;
- after = inet_set_faddress(desc->sfamily,
- (inet_address*) (&ap.spp_address),
- buf, &alen);
- if (after == NULL) RETURN_ERROR(spec, -EINVAL);
- buflen -= after - buf;
- buf = after;
-
+ before = buf;
+ xerror =
+ inet_set_faddress
+ (desc->sfamily, (inet_address*) (&ap.spp_address),
+ &buf, &alen);
+ if (xerror != NULL) {
+#ifdef EAFNOSUPPORT
+ if (xerror == str_eafnosupport) {
+ RETURN_ERROR(spec, -EAFNOSUPPORT);
+ }
+#else
+ RETURN_ERROR(spec, -EINVAL);
+#endif
+ }
+ buflen -= buf - before;
+
if (sock_getopt(desc->s, IPPROTO_SCTP, SCTP_PEER_ADDR_PARAMS,
&ap, &sz) < 0) continue;
/* Fill in the response: */
@@ -7980,7 +8007,7 @@ static ErlDrvSSizeT sctp_fill_opts(inet_descriptor* desc,
{
struct sctp_paddrinfo pai;
unsigned int sz = sizeof(pai);
- char *after;
+ char *before, *xerror;
ErlDrvSizeT alen;
if (buflen < ASSOC_ID_LEN) RETURN_ERROR(spec, -EINVAL);
@@ -7988,13 +8015,22 @@ static ErlDrvSSizeT sctp_fill_opts(inet_descriptor* desc,
buf += ASSOC_ID_LEN;
buflen -= ASSOC_ID_LEN;
alen = buflen;
- after = inet_set_faddress(desc->sfamily,
- (inet_address*) (&pai.spinfo_address),
- buf, &alen);
- if (after == NULL) RETURN_ERROR(spec, -EINVAL);
- buflen -= after - buf;
- buf = after;
-
+ before = buf;
+ xerror =
+ inet_set_faddress
+ (desc->sfamily, (inet_address*) (&pai.spinfo_address),
+ &buf, &alen);
+ if (xerror != NULL) {
+#ifdef EAFNOSUPPORT
+ if (xerror == str_eafnosupport) {
+ RETURN_ERROR(spec, -EAFNOSUPPORT);
+ }
+#else
+ RETURN_ERROR(spec, -EINVAL);
+#endif
+ }
+ buflen -= buf - before;
+
if (sock_getopt(desc->s, IPPROTO_SCTP, SCTP_GET_PEER_ADDR_INFO,
&pai, &sz) < 0) continue;
/* Fill in the response: */
@@ -8511,15 +8547,16 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf,
}
case INET_REQ_SETPEER: { /* set fake peername Port Address */
+ char *xerror;
if (len == 0) {
desc->peer_ptr = NULL;
return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize);
}
else if (len < 2)
return ctl_error(EINVAL, rbuf, rsize);
- else if (inet_set_faddress
- (desc->sfamily, &desc->peer_addr, buf, &len) == NULL)
- return ctl_error(EINVAL, rbuf, rsize);
+ else if ((xerror = inet_set_faddress
+ (desc->sfamily, &desc->peer_addr, &buf, &len)) != NULL)
+ return ctl_xerror(xerror, rbuf, rsize);
else {
desc->peer_ptr = &desc->peer_addr;
desc->peer_addr_len = (SOCKLEN_T) len;
@@ -8585,15 +8622,16 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf,
}
case INET_REQ_SETNAME: { /* set fake sockname Port Address */
+ char *xerror;
if (len == 0) {
desc->name_ptr = NULL;
return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize);
}
else if (len < 2)
return ctl_error(EINVAL, rbuf, rsize);
- else if (inet_set_faddress
- (desc->sfamily, &desc->name_addr, buf, &len) == NULL)
- return ctl_error(EINVAL, rbuf, rsize);
+ else if ((xerror = inet_set_faddress
+ (desc->sfamily, &desc->name_addr, &buf, &len)) != NULL)
+ return ctl_xerror(xerror, rbuf, rsize);
else {
desc->name_ptr = &desc->name_addr;
desc->name_addr_len = (SOCKLEN_T) len;
@@ -8602,7 +8640,7 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf,
}
case INET_REQ_BIND: { /* bind socket */
- char tbuf[2];
+ char tbuf[2], *xerror;
inet_address local;
int port;
@@ -8613,8 +8651,9 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf,
if (desc->state != INET_STATE_OPEN)
return ctl_xerror(EXBADPORT, rbuf, rsize);
- if (inet_set_faddress(desc->sfamily, &local, buf, &len) == NULL)
- return ctl_error(EINVAL, rbuf, rsize);
+ if ((xerror = inet_set_faddress
+ (desc->sfamily, &local, &buf, &len)) != NULL)
+ return ctl_xerror(xerror, rbuf, rsize);
if (IS_SOCKET_ERROR(sock_bind(desc->s,(struct sockaddr*) &local, len)))
return ctl_error(sock_errno(), rbuf, rsize);
@@ -9081,10 +9120,6 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd,
case INET_AF_INET6:
domain = AF_INET6;
break;
-#else
- case INET_AF_INET6:
- return ctl_xerror("eafnosupport", rbuf, rsize);
- break;
#endif
#ifdef HAVE_SYS_UN_H
case INET_AF_LOCAL:
@@ -9092,7 +9127,7 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd,
break;
#endif
default:
- return ctl_error(EINVAL, rbuf, rsize);
+ return ctl_xerror(str_eafnosupport, rbuf, rsize);
}
if (buf[1] != INET_TYPE_STREAM) return ctl_error(EINVAL, rbuf, rsize);
return inet_ctl_open(INETP(desc), domain, SOCK_STREAM, rbuf, rsize);
@@ -9112,10 +9147,6 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd,
case INET_AF_INET6:
domain = AF_INET6;
break;
-#else
- case INET_AF_INET6:
- return ctl_xerror("eafnosupport", rbuf, rsize);
- break;
#endif
#ifdef HAVE_SYS_UN_H
case INET_AF_LOCAL:
@@ -9123,7 +9154,7 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd,
break;
#endif
default:
- return ctl_error(EINVAL, rbuf, rsize);
+ return ctl_xerror(str_eafnosupport, rbuf, rsize);
}
if (buf[1] != INET_TYPE_STREAM) return ctl_error(EINVAL, rbuf, rsize);
@@ -9156,7 +9187,7 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd,
case INET_REQ_CONNECT: { /* do async connect */
int code;
- char tbuf[2];
+ char tbuf[2], *xerror;
unsigned timeout;
DEBUGF(("tcp_inet_ctl(%ld): CONNECT\r\n", (long)desc->inet.port));
@@ -9173,9 +9204,9 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd,
timeout = get_int32(buf);
buf += 4;
len -= 4;
- if (inet_set_faddress
- (desc->inet.sfamily, &desc->inet.remote, buf, &len) == NULL)
- return ctl_error(EINVAL, rbuf, rsize);
+ if ((xerror = inet_set_faddress
+ (desc->inet.sfamily, &desc->inet.remote, &buf, &len)) != NULL)
+ return ctl_xerror(xerror, rbuf, rsize);
code = sock_connect(desc->inet.s,
(struct sockaddr*) &desc->inet.remote, len);
@@ -10955,16 +10986,12 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf,
case INET_AF_INET: af = AF_INET; break;
#if defined(HAVE_IN6) && defined(AF_INET6)
case INET_AF_INET6: af = AF_INET6; break;
-#else
- case INET_AF_INET6:
- return ctl_xerror("eafnosupport", rbuf, rsize);
- break;
#endif
#ifdef HAVE_SYS_UN_H
case INET_AF_LOCAL: af = AF_UNIX; break;
#endif
default:
- return ctl_error(EINVAL, rbuf, rsize);
+ return ctl_xerror(str_eafnosupport, rbuf, rsize);
}
switch (buf[1]) {
case INET_TYPE_STREAM: type = SOCK_STREAM; break;
@@ -11008,16 +11035,12 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf,
case INET_AF_INET: af = AF_INET; break;
#if defined(HAVE_IN6) && defined(AF_INET6)
case INET_AF_INET6: af = AF_INET6; break;
-#else
- case INET_AF_INET6:
- return ctl_xerror("eafnosupport", rbuf, rsize);
- break;
#endif
#ifdef HAVE_SYS_UN_H
case INET_AF_LOCAL: af = AF_UNIX; break;
#endif
default:
- return ctl_error(EINVAL, rbuf, rsize);
+ return ctl_xerror(str_eafnosupport, rbuf, rsize);
}
switch (buf[1]) {
case INET_TYPE_STREAM: type = SOCK_STREAM; break;
@@ -11085,6 +11108,7 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf,
#ifdef HAVE_SCTP
if (IS_SCTP(desc)) {
inet_address remote;
+ char *xerror;
if (IS_CONNECTING(desc))
return ctl_error(EINVAL, rbuf, rsize);
@@ -11096,8 +11120,9 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf,
/* For SCTP, we do not set the peer's addr in desc->remote, as
multiple peers are possible: */
- if (inet_set_faddress(desc->sfamily, &remote, buf, &len) == NULL)
- return ctl_error(EINVAL, rbuf, rsize);
+ if ((xerror = inet_set_faddress
+ (desc->sfamily, &remote, &buf, &len)) != NULL)
+ return ctl_xerror(xerror, rbuf, rsize);
sock_select(desc, FD_CONNECT, 1);
code = sock_connect(desc->s, &remote.sa, len);
@@ -11133,12 +11158,13 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf,
else if (len < 6)
return ctl_error(EINVAL, rbuf, rsize);
else {
+ char *xerror;
/* Ignore timeout */
buf += 4;
len -= 4;
- if (inet_set_faddress
- (desc->sfamily, &desc->remote, buf, &len) == NULL)
- return ctl_error(EINVAL, rbuf, rsize);
+ if ((xerror = inet_set_faddress
+ (desc->sfamily, &desc->remote, &buf, &len)) != NULL)
+ return ctl_xerror(xerror, rbuf, rsize);
code = sock_connect(desc->s,
(struct sockaddr*) &desc->remote, len);
@@ -11202,11 +11228,13 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf,
while (curr < buf+len)
{
+ char *xerror;
/* List item format: see "inet_set_faddress": */
ErlDrvSizeT alen = buf + len - curr;
- curr = inet_set_faddress(desc->sfamily, &addr, curr, &alen);
- if (curr == NULL)
- return ctl_error(EINVAL, rbuf, rsize);
+ xerror = inet_set_faddress
+ (desc->sfamily, &addr, &curr, &alen);
+ if (xerror != NULL)
+ return ctl_xerror(xerror, rbuf, rsize);
/* Invoke the call: */
if (p_sctp_bindx(desc->s, (struct sockaddr *)&addr, 1,
@@ -11325,6 +11353,7 @@ static void packet_inet_command(ErlDrvData e, char* buf, ErlDrvSizeT len)
inet_descriptor* desc = INETP(udesc);
char* ptr = buf;
char* qtr;
+ char* xerror;
ErlDrvSizeT sz;
int code;
inet_address other;
@@ -11387,9 +11416,10 @@ static void packet_inet_command(ErlDrvData e, char* buf, ErlDrvSizeT len)
/* UDP socket. Even if it is connected, there is an address prefix
here -- ignored for connected sockets: */
sz = len;
- qtr = inet_set_faddress(desc->sfamily, &other, ptr, &sz);
- if (qtr == NULL) {
- inet_reply_error(desc, EINVAL);
+ qtr = ptr;
+ xerror = inet_set_faddress(desc->sfamily, &other, &qtr, &sz);
+ if (xerror != NULL) {
+ inet_reply_error_am(desc, driver_mk_atom(xerror));
return;
}
len -= (qtr - ptr);
@@ -11975,7 +12005,7 @@ void erts_sock_close(erts_sock_t socket)
int erts_sock_connect(erts_sock_t socket, byte *ip_addr, int len, Uint16 port)
{
SOCKET s = (SOCKET) socket;
- char buf[2 + 4];
+ char buf[2 + 4], *p;
ErlDrvSizeT blen = 6;
inet_address addr;
@@ -11985,7 +12015,8 @@ int erts_sock_connect(erts_sock_t socket, byte *ip_addr, int len, Uint16 port)
put_int16(port, buf);
memcpy((void *) (buf + 2), (void *) ip_addr, 4);
- if (!inet_set_address(AF_INET, &addr, buf, &blen))
+ p = buf;
+ if (inet_set_address(AF_INET, &addr, &p, &blen) != NULL)
return 0;
if (IS_SOCKET_ERROR
diff --git a/erts/emulator/sys/common/erl_mmap.c b/erts/emulator/sys/common/erl_mmap.c
index 53009a1481..7bbb406f29 100644
--- a/erts/emulator/sys/common/erl_mmap.c
+++ b/erts/emulator/sys/common/erl_mmap.c
@@ -1334,9 +1334,17 @@ os_mremap(void *ptr, UWord old_size, UWord new_size, int try_superalign)
#define ERTS_MMAP_RESERVE_PROT_EXEC (ERTS_MMAP_PROT_EXEC)
#define ERTS_MMAP_RESERVE_FLAGS (ERTS_MMAP_FLAGS|MAP_FIXED)
#define ERTS_MMAP_UNRESERVE_PROT (PROT_NONE)
+#if defined(__FreeBSD__)
+#define ERTS_MMAP_UNRESERVE_FLAGS (ERTS_MMAP_FLAGS|MAP_FIXED)
+#else
#define ERTS_MMAP_UNRESERVE_FLAGS (ERTS_MMAP_FLAGS|MAP_NORESERVE|MAP_FIXED)
+#endif /* __FreeBSD__ */
#define ERTS_MMAP_VIRTUAL_PROT (PROT_NONE)
+#if defined(__FreeBSD__)
+#define ERTS_MMAP_VIRTUAL_FLAGS (ERTS_MMAP_FLAGS)
+#else
#define ERTS_MMAP_VIRTUAL_FLAGS (ERTS_MMAP_FLAGS|MAP_NORESERVE)
+#endif /* __FreeBSD__ */
static int
os_reserve_physical(char *ptr, UWord size, int exec)
diff --git a/erts/emulator/sys/common/erl_mmap.h b/erts/emulator/sys/common/erl_mmap.h
index 7ac61a82c1..fa51b663fa 100644
--- a/erts/emulator/sys/common/erl_mmap.h
+++ b/erts/emulator/sys/common/erl_mmap.h
@@ -38,7 +38,17 @@
# if HAVE_MREMAP
# define ERTS_HAVE_OS_MREMAP 1
# endif
-# if defined(MAP_FIXED) && defined(MAP_NORESERVE)
+/*
+ * MAP_NORESERVE is undefined in FreeBSD 10.x and later.
+ * This is to enable 64bit HiPE experimentally on FreeBSD.
+ * Note that on FreeBSD MAP_NORESERVE was "never implemented"
+ * even before 11.x (and the flag does not exist in /usr/src/sys/vm/mmap.c
+ * of 10.3-STABLE r301478 either), and HiPE was working on OTP 18.3.3,
+ * so mandating MAP_NORESERVE on FreeBSD might not be needed.
+ * See the following message on how MAP_NORESERVE was treated on FreeBSD:
+ * <http://lists.llvm.org/pipermail/cfe-commits/Week-of-Mon-20150202/122958.html>
+ */
+# if defined(MAP_FIXED) && (defined(MAP_NORESERVE) || defined(__FreeBSD__))
# define ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION 1
# endif
#endif
diff --git a/erts/emulator/sys/unix/sys_time.c b/erts/emulator/sys/unix/sys_time.c
index 60f8decd96..4f26639703 100644
--- a/erts/emulator/sys/unix/sys_time.c
+++ b/erts/emulator/sys/unix/sys_time.c
@@ -219,7 +219,10 @@ sys_init_time(ErtsSysInitTimeResult *init_resp)
#endif
init_resp->os_monotonic_time_info.resolution = (Uint64) 1000*1000*1000;
-#if defined(HAVE_CLOCK_GETRES) && defined(MONOTONIC_CLOCK_ID)
+#if defined(ERTS_HAVE_MACH_CLOCK_GETRES) && defined(MONOTONIC_CLOCK_ID)
+ init_resp->os_monotonic_time_info.resolution
+ = mach_clock_getres(&internal_state.r.o.mach.clock.monotonic);
+#elif defined(HAVE_CLOCK_GETRES) && defined(MONOTONIC_CLOCK_ID)
{
struct timespec ts;
if (clock_getres(MONOTONIC_CLOCK_ID, &ts) == 0) {
@@ -229,9 +232,6 @@ sys_init_time(ErtsSysInitTimeResult *init_resp)
init_resp->os_monotonic_time_info.resolution = 1;
}
}
-#elif defined(ERTS_HAVE_MACH_CLOCK_GETRES) && defined(MONOTONIC_CLOCK_ID)
- init_resp->os_monotonic_time_info.resolution
- = mach_clock_getres(&internal_state.r.o.mach.clock.monotonic);
#endif
#ifdef MONOTONIC_CLOCK_ID_STR
@@ -379,7 +379,10 @@ sys_init_time(ErtsSysInitTimeResult *init_resp)
init_resp->os_system_time_info.locked_use = 0;
init_resp->os_system_time_info.resolution = (Uint64) 1000*1000*1000;
-#if defined(HAVE_CLOCK_GETRES) && defined(WALL_CLOCK_ID)
+#if defined(ERTS_HAVE_MACH_CLOCK_GETRES) && defined(WALL_CLOCK_ID)
+ init_resp->os_system_time_info.resolution
+ = mach_clock_getres(&internal_state.r.o.mach.clock.wall);
+#elif defined(HAVE_CLOCK_GETRES) && defined(WALL_CLOCK_ID)
{
struct timespec ts;
if (clock_getres(WALL_CLOCK_ID, &ts) == 0) {
@@ -389,9 +392,6 @@ sys_init_time(ErtsSysInitTimeResult *init_resp)
init_resp->os_system_time_info.resolution = 1;
}
}
-#elif defined(ERTS_HAVE_MACH_CLOCK_GETRES) && defined(WALL_CLOCK_ID)
- init_resp->os_system_time_info.resolution
- = mach_clock_getres(&internal_state.r.o.mach.clock.wall);
#endif
#if defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME)
diff --git a/erts/emulator/test/trace_port_SUITE.erl b/erts/emulator/test/trace_port_SUITE.erl
index a66563d15b..e4db368ea1 100644
--- a/erts/emulator/test/trace_port_SUITE.erl
+++ b/erts/emulator/test/trace_port_SUITE.erl
@@ -26,6 +26,7 @@
return_trace/1,
send/1,
receive_trace/1,
+ receive_trace_non_scheduler/1,
process_events/1,
schedule/1,
gc/1,
@@ -40,6 +41,7 @@ suite() ->
all() ->
[call_trace, return_trace, send, receive_trace,
+ receive_trace_non_scheduler,
process_events, schedule, gc,
default_tracer, tracer_port_crash].
@@ -184,6 +186,26 @@ receive_trace(Config) when is_list(Config) ->
expect({trace_ts,Receiver,'receive',Huge,ts}),
ok.
+%% Test sending receive traces to a port.
+receive_trace_non_scheduler(Config) when is_list(Config) ->
+ start_tracer(Config),
+ S = self(),
+ Receiver = spawn(
+ fun() ->
+ receive
+ go ->
+ Ref = S ! erlang:trace_delivered(all),
+ receive {trace_delivered, Ref, all} -> ok end
+ end
+ end),
+ trac(Receiver, true, ['receive']),
+ Receiver ! go,
+ Ref = receive R -> R end,
+ expect({trace,Receiver,'receive',go}),
+ expect({trace,Receiver,'receive',{trace_delivered, all, Ref}}),
+
+ ok.
+
%% Tests a few process events (like getting linked).
process_events(Config) when is_list(Config) ->
start_tracer(Config),