diff options
Diffstat (limited to 'erts/emulator/beam/io.c')
-rw-r--r-- | erts/emulator/beam/io.c | 179 |
1 files changed, 164 insertions, 15 deletions
diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 32f3a675fa..3fc124589b 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -226,8 +226,14 @@ static ERTS_INLINE void port_init_instr(Port *prt */ #ifdef ERTS_SMP ASSERT(prt->drv_ptr && prt->lock); - if (!prt->drv_ptr->lock) - erts_mtx_init_locked_x(prt->lock, "port_lock", id); + if (!prt->drv_ptr->lock) { + char *lock_str = "port_lock"; +#ifdef ERTS_ENABLE_LOCK_COUNT + if (!(erts_lcnt_rt_options & ERTS_LCNT_OPT_PORTLOCK)) + lock_str = NULL; +#endif + erts_mtx_init_locked_x(prt->lock, lock_str, id); + } #endif erts_port_task_init_sched(&prt->sched, id); } @@ -360,6 +366,7 @@ static Port *create_port(char *name, erts_port_task_handle_init(&prt->timeout_task); prt->psd = NULL; prt->drv_data = (SWord) 0; + prt->os_pid = -1; /* Set default tracing */ erts_get_default_tracing(&ERTS_TRACE_FLAGS(prt), &ERTS_TRACER_PROC(prt)); @@ -1276,6 +1283,35 @@ finalize_imm_drv_call(ErtsTryImmDrvCallState *sp) } } +/* + * force_imm_drv_call()/finalize_force_imm_drv_call() should *only* + * be used while crash dumping... + */ +static ErtsTryImmDrvCallResult +force_imm_drv_call(ErtsTryImmDrvCallState *sp) +{ + erts_aint32_t invalid_state; + Port *prt = sp->port; + + ASSERT(ERTS_IS_CRASH_DUMPING) + ASSERT(is_atom(sp->port_op)); + + invalid_state = sp->state; + sp->state = erts_atomic32_read_nob(&prt->state); + if (sp->state & invalid_state) + return ERTS_TRY_IMM_DRV_CALL_INVALID_PORT; + + sp->fpe_was_unmasked = erts_block_fpe(); + + return ERTS_TRY_IMM_DRV_CALL_OK; +} + +static void +finalize_force_imm_drv_call(ErtsTryImmDrvCallState *sp) +{ + erts_unblock_fpe(sp->fpe_was_unmasked); +} + #define ERTS_QUEUE_PORT_SCHED_OP_REPLY_SIZE (REF_THING_SIZE + 3) static ERTS_INLINE void @@ -1581,7 +1617,8 @@ call_driver_outputv(int bang_op, else { ErlDrvSizeT size = evp->size; - ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt)); + ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt) + || ERTS_IS_CRASH_DUMPING); #ifdef USE_VM_PROBES if (DTRACE_ENABLED(driver_outputv)) { @@ -1676,6 +1713,9 @@ call_driver_output(int bang_op, send_badsig(prt); else { + ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt) + || ERTS_IS_CRASH_DUMPING); + #ifdef USE_VM_PROBES if (DTRACE_ENABLED(driver_output)) { DTRACE_FORMAT_COMMON_PID_AND_PORT(caller, prt); @@ -1757,10 +1797,12 @@ erts_port_output(Process *c_p, ErlDrvBinary *cbin = NULL; ErlIOVec *evp = NULL; char *buf = NULL; + int force_immediate_call = (flags & ERTS_PORT_SIG_FLG_FORCE_IMM_CALL); ASSERT((flags & ~(ERTS_PORT_SIG_FLG_BANG_OP | ERTS_PORT_SIG_FLG_NOSUSPEND - | ERTS_PORT_SIG_FLG_FORCE)) == 0); + | ERTS_PORT_SIG_FLG_FORCE + | ERTS_PORT_SIG_FLG_FORCE_IMM_CALL)) == 0); busy_flgs = ((flags & ERTS_PORT_SIG_FLG_FORCE) ? ((erts_aint32_t) 0) @@ -1779,11 +1821,13 @@ erts_port_output(Process *c_p, ? ERTS_PORT_OP_DROPPED : ERTS_PORT_OP_BUSY); - try_call = !(sched_flags & (invalid_flags|ERTS_PTS_FLGS_FORCE_SCHEDULE_OP)); + try_call = (force_immediate_call /* crash dumping */ + || !(sched_flags & (invalid_flags + | ERTS_PTS_FLGS_FORCE_SCHEDULE_OP))); #ifdef USE_VM_PROBES if(DTRACE_ENABLED(port_command)) { - DTRACE_FORMAT_COMMON_PID_AND_PORT(c_p->common.id, prt); + DTRACE_FORMAT_COMMON_PID_AND_PORT(c_p ? c_p->common.id : ERTS_INVALID_PID, prt); DTRACE4(port_command, process_str, port_str, prt->name, "command"); } #endif @@ -1881,17 +1925,24 @@ erts_port_output(Process *c_p, invalid_flags, !refp, am_command); + try_call_state.pre_chk_sched_flags = 0; /* already checked */ - try_call_res = try_imm_drv_call(&try_call_state); + if (force_immediate_call) + try_call_res = force_imm_drv_call(&try_call_state); + else + try_call_res = try_imm_drv_call(&try_call_state); switch (try_call_res) { case ERTS_TRY_IMM_DRV_CALL_OK: call_driver_outputv(flags & ERTS_PORT_SIG_FLG_BANG_OP, - c_p->common.id, + c_p ? c_p->common.id : ERTS_INVALID_PID, from, prt, drv, evp); - finalize_imm_drv_call(&try_call_state); + if (force_immediate_call) + finalize_force_imm_drv_call(&try_call_state); + else + finalize_imm_drv_call(&try_call_state); /* Fall through... */ case ERTS_TRY_IMM_DRV_CALL_INVALID_PORT: driver_free_binary(cbin); @@ -2021,18 +2072,25 @@ erts_port_output(Process *c_p, r = erts_iolist_to_buf(list, buf, size); ASSERT(ERTS_IOLIST_TO_BUF_SUCCEEDED(r)); } + try_call_state.pre_chk_sched_flags = 0; /* already checked */ - try_call_res = try_imm_drv_call(&try_call_state); + if (force_immediate_call) + try_call_res = force_imm_drv_call(&try_call_state); + else + try_call_res = try_imm_drv_call(&try_call_state); switch (try_call_res) { case ERTS_TRY_IMM_DRV_CALL_OK: call_driver_output(flags & ERTS_PORT_SIG_FLG_BANG_OP, - c_p->common.id, + c_p ? c_p->common.id : ERTS_INVALID_PID, from, prt, drv, buf, size); - finalize_imm_drv_call(&try_call_state); + if (force_immediate_call) + finalize_force_imm_drv_call(&try_call_state); + else + finalize_imm_drv_call(&try_call_state); /* Fall through... */ case ERTS_TRY_IMM_DRV_CALL_INVALID_PORT: erts_free(ERTS_ALC_T_TMP, buf); @@ -2073,7 +2131,7 @@ erts_port_output(Process *c_p, res = erts_schedule_proc2port_signal(c_p, prt, - c_p->common.id, + c_p ? c_p->common.id : ERTS_INVALID_PID, refp, sigdp, task_flags, @@ -2596,6 +2654,63 @@ void erts_init_io(int port_tab_size, erts_smp_rwmtx_rwunlock(&erts_driver_list_lock); } +#if defined(ERTS_ENABLE_LOCK_COUNT) && defined(ERTS_SMP) + +static ERTS_INLINE void lcnt_enable_drv_lock_count(erts_driver_t *dp, int enable) +{ + if (dp->lock) { + if (enable) + erts_lcnt_init_lock_x(&dp->lock->lcnt, + "driver_lock", + ERTS_LCNT_LT_MUTEX, + am_atom_put(dp->name, + sys_strlen(dp->name))); + else + erts_lcnt_destroy_lock(&dp->lock->lcnt); + + } +} + +static ERTS_INLINE void lcnt_enable_port_lock_count(Port *prt, int enable) +{ + erts_aint32_t state = erts_atomic32_read_nob(&prt->state); + if (!enable) { + erts_lcnt_destroy_lock(&prt->sched.mtx.lcnt); + if (state & ERTS_PORT_SFLG_PORT_SPECIFIC_LOCK) + erts_lcnt_destroy_lock(&prt->lock->lcnt); + } + else { + erts_lcnt_init_lock_x(&prt->sched.mtx.lcnt, + "port_sched_lock", + ERTS_LCNT_LT_MUTEX, + prt->common.id); + if (state & ERTS_PORT_SFLG_PORT_SPECIFIC_LOCK) + erts_lcnt_init_lock_x(&prt->lock->lcnt, + "port_lock", + ERTS_LCNT_LT_MUTEX, + prt->common.id); + } +} + +void erts_lcnt_enable_io_lock_count(int enable) +{ + erts_driver_t *dp; + int i, max = erts_ptab_max(&erts_port); + + for (i = 0; i < max; i++) { + Port *prt = erts_pix2port(i); + if (prt) + lcnt_enable_port_lock_count(prt, enable); + } + + lcnt_enable_drv_lock_count(&vanilla_driver, enable); + lcnt_enable_drv_lock_count(&spawn_driver, enable); + lcnt_enable_drv_lock_count(&fd_driver, enable); + for (dp = driver_list; dp; dp = dp->next) + lcnt_enable_drv_lock_count(dp, enable); +} +#endif + /* * Buffering of data when using line oriented I/O on ports */ @@ -5353,6 +5468,8 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) Uint size = ptr[1]; Uint offset = ptr[2]; + erts_smp_atomic_add_nob(&erts_bytes_in, (erts_aint_t) size); + if (size <= ERL_ONHEAP_BIN_LIMIT) { ErlHeapBin* hbp = (ErlHeapBin *) hp; hp += heap_bin_size(size); @@ -5384,6 +5501,9 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) case ERL_DRV_BUF2BINARY: { /* char*, size */ byte *bufp = (byte *) ptr[0]; Uint size = (Uint) ptr[1]; + + erts_smp_atomic_add_nob(&erts_bytes_in, (erts_aint_t) size); + if (size <= ERL_ONHEAP_BIN_LIMIT) { ErlHeapBin* hbp = (ErlHeapBin *) hp; hp += heap_bin_size(size); @@ -5420,6 +5540,7 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) } case ERL_DRV_STRING: /* char*, length */ + erts_smp_atomic_add_nob(&erts_bytes_in, (erts_aint_t) ptr[1]); mess = buf_to_intlist(&hp, (char*)ptr[0], ptr[1], NIL); ptr += 2; break; @@ -7394,11 +7515,39 @@ int null_func(void) int erl_drv_putenv(char *key, char *value) { - return erts_write_env(key, value); + return erts_sys_putenv_raw(key, value); } int erl_drv_getenv(char *key, char *value, size_t *value_size) { - return erts_sys_getenv(key, value, value_size); + return erts_sys_getenv_raw(key, value, value_size); +} + +/* get heart_port + * used by erl_crash_dump + * - uses the fact that heart_port is registered when starting heart + */ + +Port *erts_get_heart_port(void) +{ + int ix, max = erts_ptab_max(&erts_port); + + for (ix = 0; ix < max; ix++) { + struct reg_proc *reg; + Port *port = erts_pix2port(ix); + + if (!port) + continue; + /* only examine undead or alive ports */ + if (erts_atomic32_read_nob(&port->state) & ERTS_PORT_SFLGS_DEAD) + continue; + /* immediate atom compare */ + reg = port->common.u.alive.reg; + if (reg && reg->name == am_heart_port) { + return port; + } + } + + return NULL; } |