diff options
Diffstat (limited to 'erts/emulator/drivers/common/efile_drv.c')
-rw-r--r-- | erts/emulator/drivers/common/efile_drv.c | 1350 |
1 files changed, 1130 insertions, 220 deletions
diff --git a/erts/emulator/drivers/common/efile_drv.c b/erts/emulator/drivers/common/efile_drv.c index f0ff3f54c5..912f5d3d8b 100644 --- a/erts/emulator/drivers/common/efile_drv.c +++ b/erts/emulator/drivers/common/efile_drv.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2011. All Rights Reserved. + * Copyright Ericsson AB 1996-2012. 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 @@ -55,6 +55,7 @@ #define FILE_READ_LINE 29 #define FILE_FDATASYNC 30 #define FILE_FADVISE 31 +#define FILE_SENDFILE 32 /* Return codes */ @@ -69,6 +70,7 @@ #define FILE_RESP_EOF 8 #define FILE_RESP_FNAME 9 #define FILE_RESP_ALL_DATA 10 +#define FILE_RESP_LFNAME 11 /* Options */ @@ -97,12 +99,20 @@ # include "config.h" #endif #include <stdlib.h> + +/* Need (NON)BLOCKING macros for sendfile */ +#ifndef WANT_NONBLOCKING +#define WANT_NONBLOCKING +#endif + #include "sys.h" + #include "erl_driver.h" #include "erl_efile.h" #include "erl_threads.h" #include "zlib.h" #include "gzio.h" +#include "dtrace-wrapper.h" #include <ctype.h> #include <sys/types.h> @@ -110,6 +120,39 @@ void erl_exit(int n, char *fmt, ...); static ErlDrvSysInfo sys_info; +/* For explanation of this var, see comment for same var in erl_async.c */ +static unsigned gcc_optimizer_hack = 0; + +#ifdef USE_VM_PROBES + +#define DTRACE_EFILE_BUFSIZ 128 + +#define DTRACE_INVOKE_SETUP(op) \ + do { DTRACE3(efile_drv_int_entry, d->sched_i1, d->sched_i2, op); } while (0) +#define DTRACE_INVOKE_SETUP_BY_NAME(op) \ + struct t_data *d = (struct t_data *) data ; \ + DTRACE_INVOKE_SETUP(op) +#define DTRACE_INVOKE_RETURN(op) \ + do { DTRACE3(efile_drv_int_return, d->sched_i1, d->sched_i2, \ + op); } while (0) ; gcc_optimizer_hack++ ; + +/* Assign human-friendlier id numbers to scheduler & I/O worker threads */ +int dt_driver_idnum = 0; +int dt_driver_io_worker_base = 5000; +erts_mtx_t dt_driver_mutex; +pthread_key_t dt_driver_key; + +typedef struct { + int thread_num; + Uint64 tag; +} dt_private; + +dt_private *get_dt_private(int); +#else /* USE_VM_PROBES */ +#define DTRACE_INVOKE_SETUP(op) do {} while (0) +#define DTRACE_INVOKE_SETUP_BY_NAME(op) do {} while (0) +#define DTRACE_INVOKE_RETURN(op) do {} while (0) +#endif /* USE_VM_PROBES */ /* #define TRACE 1 */ #ifdef TRACE @@ -139,6 +182,22 @@ static ErlDrvSysInfo sys_info; #define MUTEX_UNLOCK(m) #endif + +/** + * On DARWIN sendfile can deadlock with close if called in + * different threads. So until Apple fixes so that sendfile + * is not buggy we disable usage of the async pool for + * DARWIN. The testcase t_sendfile_crashduring reproduces + * this error when using +A 10. + */ +#if defined(__APPLE__) && defined(__MACH__) +#define USE_THRDS_FOR_SENDFILE 0 +#else +#define USE_THRDS_FOR_SENDFILE (sys_info.async_threads > 0) +#endif /* defined(__APPLE__) && defined(__MACH__) */ + + + #if 0 /* Experimental, for forcing all file operations to use the same thread. */ static unsigned file_fixed_key = 1; @@ -147,8 +206,14 @@ static ErlDrvSysInfo sys_info; # define KEY(desc) (&(desc)->key) #endif +#ifndef MAX +# define MAX(x, y) (((x) > (y)) ? (x) : (y)) +#endif #ifdef FILENAMES_16BIT +#ifdef USE_VM_PROBES +#error 16bit characters in filenames and dtrace in combination is not supported. +#endif # define FILENAME_BYTELEN(Str) filename_len_16bit(Str) # define FILENAME_COPY(To,From) filename_cpy_16bit((To),(From)) # define FILENAME_CHARSIZE 2 @@ -184,6 +249,7 @@ static ErlDrvSysInfo sys_info; # define RESBUFSIZE BUFSIZ #endif +#define READDIR_CHUNKS (5) @@ -215,17 +281,26 @@ typedef unsigned char uchar; static ErlDrvData file_start(ErlDrvPort port, char* command); static int file_init(void); static void file_stop(ErlDrvData); -static void file_output(ErlDrvData, char* buf, int len); -static int file_control(ErlDrvData, unsigned int command, - char* buf, int len, char **rbuf, int rlen); +static void file_output(ErlDrvData, char* buf, ErlDrvSizeT len); +static ErlDrvSSizeT file_control(ErlDrvData, unsigned int command, + char* buf, ErlDrvSizeT len, + char **rbuf, ErlDrvSizeT rlen); static void file_timeout(ErlDrvData); static void file_outputv(ErlDrvData, ErlIOVec*); static void file_async_ready(ErlDrvData, ErlDrvThreadData); static void file_flush(ErlDrvData); +#ifdef HAVE_SENDFILE +static void file_ready_output(ErlDrvData data, ErlDrvEvent event); +static void file_stop_select(ErlDrvEvent event, void* _); +#endif /* HAVE_SENDFILE */ enum e_timer {timer_idle, timer_again, timer_write}; +#ifdef HAVE_SENDFILE +enum e_sendfile {sending, not_sending}; +static void free_sendfile(void *data); +#endif /* HAVE_SENDFILE */ struct t_data; @@ -240,6 +315,9 @@ typedef struct { struct t_data *cq_head; /* Queue of incoming commands */ struct t_data *cq_tail; /* -""- */ enum e_timer timer_state; +#ifdef HAVE_SENDFILE + enum e_sendfile sendfile_state; +#endif /* HAVE_SENDFILE */ size_t read_bufsize; ErlDrvBinary *read_binp; size_t read_offset; @@ -251,6 +329,10 @@ typedef struct { ErlDrvPDL q_mtx; /* Mutex for the driver queue, known by the emulator. Also used for mutual exclusion when accessing field(s) below. */ size_t write_buffered; +#ifdef USE_VM_PROBES + int idnum; /* Unique ID # for this driver thread/desc */ + char port_str[DTRACE_TERM_BUF_SIZE]; +#endif } file_descriptor; @@ -262,7 +344,11 @@ struct erl_drv_entry efile_driver_entry = { file_stop, file_output, NULL, +#ifdef HAVE_SENDFILE + file_ready_output, +#else NULL, +#endif /* HAVE_SENDFILE */ "efile", NULL, NULL, @@ -277,7 +363,13 @@ struct erl_drv_entry efile_driver_entry = { ERL_DRV_EXTENDED_MAJOR_VERSION, ERL_DRV_EXTENDED_MINOR_VERSION, ERL_DRV_FLAG_USE_PORT_LOCKING, + NULL, + NULL, +#ifdef HAVE_SENDFILE + file_stop_select +#else NULL +#endif /* HAVE_SENDFILE */ }; @@ -317,15 +409,16 @@ struct t_preadv { Sint64 offsets[1]; }; -#define READDIR_BUFSIZE (8*1024) -#if READDIR_BUFSIZE < (FILENAME_CHARSIZE*2*(MAXPATHLEN+1)) +#define READDIR_BUFSIZE (8*1024)*READDIR_CHUNKS +#if READDIR_BUFSIZE < (1 + (2 + MAXPATHLEN)*FILENAME_CHARSIZE*READDIR_CHUNKS) # undef READDIR_BUFSIZE -# define READDIR_BUFSIZE (FILENAME_CHARSIZE*2*(MAXPATHLEN+1)) +# define READDIR_BUFSIZE (1 + (2 + MAXPATHLEN)*FILENAME_CHARSIZE*READDIR_CHUNKS) #endif struct t_readdir_buf { - struct t_readdir_buf *next; - char buf[READDIR_BUFSIZE]; + struct t_readdir_buf *next; + size_t n; + char buf[READDIR_BUFSIZE]; }; struct t_data @@ -337,6 +430,11 @@ struct t_data void (*free)(void *); int again; int reply; +#ifdef USE_VM_PROBES + int sched_i1; + Uint64 sched_i2; + char sched_utag[DTRACE_EFILE_BUFSIZ+1]; +#endif int result_ok; Efile_error errInfo; int flags; @@ -395,12 +493,20 @@ struct t_data Sint64 length; int advise; } fadvise; +#ifdef HAVE_SENDFILE + struct { + ErlDrvPort port; + ErlDrvPDL q_mtx; + int out_fd; + off_t offset; + Uint64 nbytes; + Uint64 written; + } sendfile; +#endif /* HAVE_SENDFILE */ } c; char b[1]; }; - - #define EF_ALLOC(S) driver_alloc((S)) #define EF_REALLOC(P, S) driver_realloc((P), (S)) #define EF_SAFE_ALLOC(S) ef_safe_alloc((S)) @@ -419,7 +525,7 @@ static void *ef_safe_alloc(Uint s) static void *ef_safe_realloc(void *op, Uint s) { void *p = EF_REALLOC(op, s); - if (!p) erl_exit(1, "efile drv: Can't reallocate %d bytes of memory\n", s); + if (!p) erl_exit(1, "efile drv: Can't reallocate %lu bytes of memory\n", (unsigned long)s); return p; } @@ -429,7 +535,7 @@ static void *ef_safe_realloc(void *op, Uint s) * ErlIOVec manipulation functions. */ -/* char EV_CHAR(ErlIOVec *ev, int p, int q) */ +/* char EV_CHAR_P(ErlIOVec *ev, int p, int q) */ #define EV_CHAR_P(ev, p, q) \ (((char *)(ev)->iov[(q)].iov_base) + (p)) @@ -482,7 +588,6 @@ static void *ef_safe_realloc(void *op, Uint s) : 0) - #if 0 static void ev_clear(ErlIOVec *ev) { @@ -610,7 +715,6 @@ static struct t_data *cq_deq(file_descriptor *desc) { } - /********************************************************************* * Driver entry point -> init */ @@ -625,6 +729,12 @@ file_init(void) ? atoi(buf) : 0); driver_system_info(&sys_info, sizeof(ErlDrvSysInfo)); + +#ifdef USE_VM_PROBES + erts_mtx_init(&dt_driver_mutex, "efile_drv dtrace mutex"); + pthread_key_create(&dt_driver_key, NULL); +#endif /* USE_VM_PROBES */ + return 0; } @@ -652,6 +762,9 @@ file_start(ErlDrvPort port, char* command) desc->cq_head = NULL; desc->cq_tail = NULL; desc->timer_state = timer_idle; +#ifdef HAVE_SENDFILE + desc->sendfile_state = not_sending; +#endif desc->read_bufsize = 0; desc->read_binp = NULL; desc->read_offset = 0; @@ -661,6 +774,10 @@ file_start(ErlDrvPort port, char* command) desc->write_error = 0; MUTEX_INIT(desc->q_mtx, port); /* Refc is one, referenced by emulator now */ desc->write_buffered = 0; +#ifdef USE_VM_PROBES + dtrace_drvport_str(port, desc->port_str); + get_dt_private(0); /* throw away return value */ +#endif /* USE_VM_PROBES */ return (ErlDrvData) desc; } @@ -680,8 +797,10 @@ static void do_close(int flags, SWord fd) { static void invoke_close(void *data) { struct t_data *d = (struct t_data *) data; + DTRACE_INVOKE_SETUP(FILE_CLOSE); d->again = 0; do_close(d->flags, d->fd); + DTRACE_INVOKE_RETURN(FILE_CLOSE); } /********************************************************************* @@ -759,7 +878,16 @@ static void reply_Uint_posix_error(file_descriptor *desc, Uint num, driver_output2(desc->port, response, t-response, NULL, 0); } +static void reply_string_error(file_descriptor *desc, char* str) { + char response[256]; /* Response buffer. */ + char* s; + char* t; + response[0] = FILE_RESP_ERROR; + for (s = str, t = response+1; *s; s++, t++) + *t = tolower(*s); + driver_output2(desc->port, response, t-response, NULL, 0); +} static int reply_error(file_descriptor *desc, Efile_error *errInfo) /* The error codes. */ @@ -890,8 +1018,6 @@ static int reply_eof(file_descriptor *desc) { driver_output2(desc->port, &c, 1, NULL, 0); return 0; } - - static void invoke_name(void *data, int (*f)(Efile_error *, char *)) { @@ -904,49 +1030,63 @@ static void invoke_name(void *data, int (*f)(Efile_error *, char *)) static void invoke_mkdir(void *data) { + DTRACE_INVOKE_SETUP_BY_NAME(FILE_MKDIR); invoke_name(data, efile_mkdir); + DTRACE_INVOKE_RETURN(FILE_MKDIR); } static void invoke_rmdir(void *data) { + DTRACE_INVOKE_SETUP_BY_NAME(FILE_RMDIR); invoke_name(data, efile_rmdir); + DTRACE_INVOKE_RETURN(FILE_RMDIR); } static void invoke_delete_file(void *data) { + DTRACE_INVOKE_SETUP_BY_NAME(FILE_DELETE); invoke_name(data, efile_delete_file); + DTRACE_INVOKE_RETURN(FILE_DELETE); } static void invoke_chdir(void *data) { + DTRACE_INVOKE_SETUP_BY_NAME(FILE_CHDIR); invoke_name(data, efile_chdir); + DTRACE_INVOKE_RETURN(FILE_CHDIR); } static void invoke_fdatasync(void *data) { struct t_data *d = (struct t_data *) data; int fd = (int) d->fd; + DTRACE_INVOKE_SETUP(FILE_FDATASYNC); d->again = 0; d->result_ok = efile_fdatasync(&d->errInfo, fd); + DTRACE_INVOKE_RETURN(FILE_FDATASYNC); } static void invoke_fsync(void *data) { struct t_data *d = (struct t_data *) data; int fd = (int) d->fd; + DTRACE_INVOKE_SETUP(FILE_FSYNC); d->again = 0; d->result_ok = efile_fsync(&d->errInfo, fd); + DTRACE_INVOKE_RETURN(FILE_FSYNC); } static void invoke_truncate(void *data) { struct t_data *d = (struct t_data *) data; int fd = (int) d->fd; + DTRACE_INVOKE_SETUP(FILE_TRUNCATE); d->again = 0; d->result_ok = efile_truncate_file(&d->errInfo, &fd, d->flags); + DTRACE_INVOKE_RETURN(FILE_TRUNCATE); } static void invoke_read(void *data) @@ -954,6 +1094,7 @@ static void invoke_read(void *data) struct t_data *d = (struct t_data *) data; int status, segment; size_t size, read_size; + DTRACE_INVOKE_SETUP(FILE_READ); segment = d->again && d->c.read.bin_size >= 2*FILE_SEGMENT_READ; if (segment) { @@ -988,6 +1129,7 @@ static void invoke_read(void *data) } else { d->again = 0; } + DTRACE_INVOKE_RETURN(FILE_READ); } static void free_read(void *data) @@ -1004,13 +1146,14 @@ static void invoke_read_line(void *data) int status; size_t read_size; int local_loop = (d->again == 0); + DTRACE_INVOKE_SETUP(FILE_READ_LINE); do { size_t size = (d->c.read_line.binp)->orig_size - d->c.read_line.read_offset - d->c.read_line.read_size; if (size == 0) { /* Need more place */ - size_t need = (d->c.read_line.read_size >= DEFAULT_LINEBUF_SIZE) ? + ErlDrvSizeT need = (d->c.read_line.read_size >= DEFAULT_LINEBUF_SIZE) ? d->c.read_line.read_size + DEFAULT_LINEBUF_SIZE : DEFAULT_LINEBUF_SIZE; ErlDrvBinary *newbin = driver_alloc_binary(need); if (newbin == NULL) { @@ -1095,6 +1238,7 @@ static void invoke_read_line(void *data) break; } } while (local_loop); + DTRACE_INVOKE_RETURN(FILE_READ_LINE); } static void free_read_line(void *data) @@ -1110,6 +1254,7 @@ static void invoke_read_file(void *data) struct t_data *d = (struct t_data *) data; size_t read_size; int chop; + DTRACE_INVOKE_SETUP(FILE_READ_FILE); if (! d->c.read_file.binp) { /* First invocation only */ int fd; @@ -1146,12 +1291,14 @@ static void invoke_read_file(void *data) &read_size); if (d->result_ok) { d->c.read_file.offset += read_size; - if (chop) return; /* again */ + if (chop) goto chop_done; /* again */ } close: efile_closefile((int) d->fd); done: d->again = 0; + chop_done: + DTRACE_INVOKE_RETURN(FILE_READ_FILE); } static void free_read_file(void *data) @@ -1171,6 +1318,7 @@ static void invoke_preadv(void *data) ErlIOVec *ev = &c->eiov; size_t bytes_read_so_far = 0; unsigned char *p = (unsigned char *)ev->iov[0].iov_base + 4+4+8*c->cnt; + DTRACE_INVOKE_SETUP(FILE_PREADV); while (c->cnt < c->n) { size_t read_size = ev->iov[1 + c->cnt].iov_len - c->size; @@ -1192,7 +1340,7 @@ static void invoke_preadv(void *data) bytes_read_so_far += bytes_read; if (chop && bytes_read == read_size) { c->size += bytes_read; - return; + goto done; } ASSERT(bytes_read <= read_size); ev->iov[1 + c->cnt].iov_len = bytes_read + c->size; @@ -1203,7 +1351,7 @@ static void invoke_preadv(void *data) if (d->again && bytes_read_so_far >= FILE_SEGMENT_READ && c->cnt < c->n) { - return; + goto done; } } else { /* In case of a read error, ev->size will not be correct, @@ -1214,6 +1362,8 @@ static void invoke_preadv(void *data) } } d->again = 0; + done: + DTRACE_INVOKE_RETURN(FILE_PREADV); } static void free_preadv(void *data) { @@ -1235,6 +1385,7 @@ static void invoke_ipread(void *data) size_t bytes_read = 0; char buf[2*sizeof(Uint32)]; Uint32 offset, size; + DTRACE_INVOKE_SETUP(FILE_IPREAD); /* Read indirection header */ if (! efile_pread(&d->errInfo, (int) d->fd, c->offsets[0], @@ -1273,14 +1424,17 @@ static void invoke_ipread(void *data) /* Read data block */ d->invoke = invoke_preadv; invoke_preadv(data); + DTRACE_INVOKE_RETURN(FILE_IPREAD); return; error: d->result_ok = 0; d->again = 0; + DTRACE_INVOKE_RETURN(FILE_IPREAD); return; done: d->result_ok = !0; d->again = 0; + DTRACE_INVOKE_RETURN(FILE_IPREAD); } /* invoke_writev and invoke_pwritev are the only thread functions that @@ -1303,6 +1457,7 @@ static void invoke_writev(void *data) { size_t size; size_t p; int segment; + DTRACE_INVOKE_SETUP(FILE_WRITE); segment = d->again && d->c.writev.size >= 2*FILE_SEGMENT_WRITE; if (segment) { @@ -1311,7 +1466,11 @@ static void invoke_writev(void *data) { size = d->c.writev.size; } - /* Copy the io vector to avoid locking the port que while writing */ + /* Copy the io vector to avoid locking the port que while writing, + * also, both we and efile_writev might/will change the SysIOVec + * when segmenting or due to partial write and we do not want to + * tamper with the actual queue that we get from driver_peekq + */ MUTEX_LOCK(d->c.writev.q_mtx); /* Lock before accessing the port queue */ iov0 = driver_peekq(d->c.writev.port, &iovlen); @@ -1350,7 +1509,7 @@ static void invoke_writev(void *data) { } else { d->result_ok = efile_writev(&d->errInfo, d->flags, (int) d->fd, - iov, iovcnt, size); + iov, iovcnt); } } else if (iovlen == 0) { d->result_ok = 1; @@ -1372,6 +1531,7 @@ static void invoke_writev(void *data) { TRACE_F(("w%lu", (unsigned long)size)); } + DTRACE_INVOKE_RETURN(FILE_WRITE); } static void free_writev(void *data) { @@ -1385,34 +1545,40 @@ static void free_writev(void *data) { static void invoke_pwd(void *data) { struct t_data *d = (struct t_data *) data; + DTRACE_INVOKE_SETUP(FILE_PWD); d->again = 0; d->result_ok = efile_getdcwd(&d->errInfo,d->drive, d->b+1, RESBUFSIZE-1); + DTRACE_INVOKE_RETURN(FILE_PWD); } static void invoke_readlink(void *data) { struct t_data *d = (struct t_data *) data; char resbuf[RESBUFSIZE]; /* Result buffer. */ + DTRACE_INVOKE_SETUP(FILE_READLINK); d->again = 0; d->result_ok = efile_readlink(&d->errInfo, d->b, resbuf+1, RESBUFSIZE-1); if (d->result_ok != 0) FILENAME_COPY((char *) d->b + 1, resbuf+1); + DTRACE_INVOKE_RETURN(FILE_READLINK); } static void invoke_altname(void *data) { struct t_data *d = (struct t_data *) data; char resbuf[RESBUFSIZE]; /* Result buffer. */ + DTRACE_INVOKE_SETUP(FILE_ALTNAME); d->again = 0; d->result_ok = efile_altname(&d->errInfo, d->b, resbuf+1, RESBUFSIZE-1); if (d->result_ok != 0) FILENAME_COPY((char *) d->b + 1, resbuf+1); + DTRACE_INVOKE_RETURN(FILE_ALTNAME); } static void invoke_pwritev(void *data) { @@ -1425,6 +1591,7 @@ static void invoke_pwritev(void *data) { size_t p; int segment; size_t size, write_size; + DTRACE_INVOKE_SETUP(FILE_PWRITEV); segment = d->again && c->size >= 2*FILE_SEGMENT_WRITE; if (segment) { @@ -1504,6 +1671,7 @@ static void invoke_pwritev(void *data) { } done: EF_FREE(iov); /* Free our copy of the vector, nothing to restore */ + DTRACE_INVOKE_RETURN(FILE_PWRITEV); } static void free_pwritev(void *data) { @@ -1519,9 +1687,14 @@ static void invoke_flstat(void *data) { struct t_data *d = (struct t_data *) data; + DTRACE3(efile_drv_int_entry, d->sched_i1, d->sched_i2, + d->command == FILE_LSTAT ? FILE_LSTAT : FILE_FSTAT); d->again = 0; d->result_ok = efile_fileinfo(&d->errInfo, &d->info, d->b, d->command == FILE_LSTAT); + DTRACE3(efile_drv_int_entry, d->sched_i1, d->sched_i2, + d->command == FILE_LSTAT ? FILE_LSTAT : FILE_FSTAT); + gcc_optimizer_hack++; } static void invoke_link(void *data) @@ -1529,10 +1702,12 @@ static void invoke_link(void *data) struct t_data *d = (struct t_data *) data; char *name = d->b; char *new_name; + DTRACE_INVOKE_SETUP(FILE_LINK); d->again = 0; new_name = name+FILENAME_BYTELEN(name)+FILENAME_CHARSIZE; d->result_ok = efile_link(&d->errInfo, name, new_name); + DTRACE_INVOKE_RETURN(FILE_LINK); } static void invoke_symlink(void *data) @@ -1540,10 +1715,12 @@ static void invoke_symlink(void *data) struct t_data *d = (struct t_data *) data; char *name = d->b; char *new_name; + DTRACE_INVOKE_SETUP(FILE_SYMLINK); d->again = 0; new_name = name+FILENAME_BYTELEN(name)+FILENAME_CHARSIZE; d->result_ok = efile_symlink(&d->errInfo, name, new_name); + DTRACE_INVOKE_RETURN(FILE_SYMLINK); } static void invoke_rename(void *data) @@ -1551,24 +1728,29 @@ static void invoke_rename(void *data) struct t_data *d = (struct t_data *) data; char *name = d->b; char *new_name; + DTRACE_INVOKE_SETUP(FILE_RENAME); d->again = 0; new_name = name+FILENAME_BYTELEN(name)+FILENAME_CHARSIZE; d->result_ok = efile_rename(&d->errInfo, name, new_name); + DTRACE_INVOKE_RETURN(FILE_RENAME); } static void invoke_write_info(void *data) { struct t_data *d = (struct t_data *) data; + DTRACE_INVOKE_SETUP(FILE_WRITE_INFO); d->again = 0; d->result_ok = efile_write_info(&d->errInfo, &d->info, d->b); + DTRACE_INVOKE_RETURN(FILE_WRITE_INFO); } static void invoke_lseek(void *data) { struct t_data *d = (struct t_data *) data; int status; + DTRACE_INVOKE_SETUP(FILE_LSEEK); d->again = 0; if (d->flags & EFILE_COMPRESSED) { @@ -1593,66 +1775,61 @@ static void invoke_lseek(void *data) &d->c.lseek.location); } d->result_ok = status; + DTRACE_INVOKE_RETURN(FILE_LSEEK); } static void invoke_readdir(void *data) { struct t_data *d = (struct t_data *) data; - int s; char *p = NULL; - int buf_sz = 0; - size_t tmp_bs; + size_t file_bs; + size_t n = 0, total = 0; + struct t_readdir_buf *b = NULL; + int res = 0; + DTRACE_INVOKE_SETUP(FILE_READDIR); d->again = 0; d->errInfo.posix_errno = 0; - while (1) { - char *str; - if (buf_sz < (4 /* sz */ + 1 /* cmd */ + - FILENAME_CHARSIZE*(MAXPATHLEN + 1))) { - struct t_readdir_buf *b; - if (p) { - put_int32(0, p); /* EOB */ - } - b = EF_SAFE_ALLOC(sizeof(struct t_readdir_buf)); - b->next = NULL; - if (d->c.read_dir.last_buf) - d->c.read_dir.last_buf->next = b; - else - d->c.read_dir.first_buf = b; - d->c.read_dir.last_buf = b; - p = &b->buf[0]; - buf_sz = READDIR_BUFSIZE - 4/* EOB */; - } - - p[4] = FILE_RESP_FNAME; - buf_sz -= 4 + 1; - str = p + 4 + 1; - ASSERT(buf_sz >= MAXPATHLEN + 1); - tmp_bs = buf_sz; - s = efile_readdir(&d->errInfo, d->b, &d->dir_handle, str, &tmp_bs); - - if (s) { - put_int32(tmp_bs + 1 /* 1 byte for opcode */, p); - p += 4 + tmp_bs + 1; - ASSERT(p == (str + tmp_bs)); - buf_sz -= tmp_bs; - } - else { - put_int32(1, p); - p += 4 + 1; - put_int32(0, p); /* EOB */ - d->result_ok = (d->errInfo.posix_errno == 0); - break; + do { + total = READDIR_BUFSIZE; + n = 1; + b = EF_SAFE_ALLOC(sizeof(struct t_readdir_buf)); + b->next = NULL; + + if (d->c.read_dir.last_buf) { + d->c.read_dir.last_buf->next = b; + } else { + d->c.read_dir.first_buf = b; } - } + d->c.read_dir.last_buf = b; + + p = &b->buf[0]; + p[0] = FILE_RESP_LFNAME; + file_bs = READDIR_BUFSIZE - n; + + do { + res = efile_readdir(&d->errInfo, d->b, &d->dir_handle, p + n + 2, &file_bs); + + if (res) { + put_int16((Uint16)file_bs, p + n); + n += 2 + file_bs; + file_bs = READDIR_BUFSIZE - n; + } + } while( res && ((total - n - 2) >= MAXPATHLEN*FILENAME_CHARSIZE)); + + b->n = n; + } while(res); + + d->result_ok = (d->errInfo.posix_errno == 0); + DTRACE_INVOKE_RETURN(FILE_READDIR); } static void invoke_open(void *data) { struct t_data *d = (struct t_data *) data; - int status = 1; /* Status of open call. */ + DTRACE_INVOKE_SETUP(FILE_OPEN); d->again = 0; if ((d->flags & EFILE_COMPRESSED) == 0) { @@ -1685,6 +1862,7 @@ static void invoke_open(void *data) } d->result_ok = status; + DTRACE_INVOKE_RETURN(FILE_OPEN); } static void invoke_fadvise(void *data) @@ -1694,15 +1872,92 @@ static void invoke_fadvise(void *data) off_t offset = (off_t) d->c.fadvise.offset; off_t length = (off_t) d->c.fadvise.length; int advise = (int) d->c.fadvise.advise; + DTRACE_INVOKE_SETUP(FILE_FADVISE); d->again = 0; d->result_ok = efile_fadvise(&d->errInfo, fd, offset, length, advise); + DTRACE_INVOKE_RETURN(FILE_FADVISE); +} + +#ifdef HAVE_SENDFILE +static void invoke_sendfile(void *data) +{ + struct t_data *d = (struct t_data *)data; + int fd = d->fd; + int out_fd = (int)d->c.sendfile.out_fd; + Uint64 nbytes = d->c.sendfile.nbytes; + int result = 0; + d->again = 0; + + result = efile_sendfile(&d->errInfo, fd, out_fd, &d->c.sendfile.offset, &nbytes, NULL); + + d->c.sendfile.written += nbytes; + + if (result == 1 || (result == 0 && USE_THRDS_FOR_SENDFILE)) { + d->result_ok = 0; + } else if (result == 0 && (d->errInfo.posix_errno == EAGAIN + || d->errInfo.posix_errno == EINTR)) { + if ((d->c.sendfile.nbytes - nbytes) != 0) { + d->result_ok = 1; + if (d->c.sendfile.nbytes != 0) + d->c.sendfile.nbytes -= nbytes; + } else + d->result_ok = 0; + } else { + d->result_ok = -1; + } +} + +static void free_sendfile(void *data) { + struct t_data *d = (struct t_data *)data; + if (USE_THRDS_FOR_SENDFILE) { + SET_NONBLOCKING(d->c.sendfile.out_fd); + } else { + MUTEX_LOCK(d->c.sendfile.q_mtx); + driver_deq(d->c.sendfile.port,1); + MUTEX_UNLOCK(d->c.sendfile.q_mtx); + driver_select(d->c.sendfile.port, (ErlDrvEvent)(long)d->c.sendfile.out_fd, ERL_DRV_USE_NO_CALLBACK|ERL_DRV_WRITE, 0); + } + EF_FREE(data); +} + +static void file_ready_output(ErlDrvData data, ErlDrvEvent event) +{ + file_descriptor* fd = (file_descriptor*) data; + + switch (fd->d->command) { + case FILE_SENDFILE: + driver_select(fd->d->c.sendfile.port, event, + (int)ERL_DRV_WRITE,(int) 0); + invoke_sendfile((void *)fd->d); + file_async_ready(data, (ErlDrvThreadData)fd->d); + break; + default: + break; + } +} + +static void file_stop_select(ErlDrvEvent event, void* _) +{ + +} + +static int flush_sendfile(file_descriptor *desc,void *_) { + if (desc->sendfile_state == sending) { + desc->d->result_ok = -1; + desc->d->errInfo.posix_errno = ECONNABORTED; + file_async_ready((ErlDrvData)desc,(ErlDrvThreadData)desc->d); + } + return 1; } +#endif /* HAVE_SENDFILE */ + static void free_readdir(void *data) { struct t_data *d = (struct t_data *) data; struct t_readdir_buf *b1 = d->c.read_dir.first_buf; + while (b1) { struct t_readdir_buf *b2 = b1; b1 = b1->next; @@ -1760,6 +2015,10 @@ static void cq_execute(file_descriptor *desc) { register void *void_ptr; /* Soft cast variable */ if (desc->timer_state == timer_again) return; +#ifdef HAVE_SENDFILE + if (desc->sendfile_state == sending) + return; +#endif if (! (d = cq_deq(desc))) return; TRACE_F(("x%i", (int) d->command)); @@ -1767,12 +2026,16 @@ static void cq_execute(file_descriptor *desc) { DRIVER_ASYNC(d->level, desc, d->invoke, void_ptr=d, d->free); } -static int async_write(file_descriptor *desc, int *errp, - int reply, Uint32 reply_size) { +static struct t_data *async_write(file_descriptor *desc, int *errp, + int reply, Uint32 reply_size +#ifdef USE_VM_PROBES + ,Sint64 *dt_i1, Sint64 *dt_i2, Sint64 *dt_i3 +#endif +) { struct t_data *d; if (! (d = EF_ALLOC(sizeof(struct t_data) - 1))) { if (errp) *errp = ENOMEM; - return -1; + return NULL; } TRACE_F(("w%lu", (unsigned long)desc->write_buffered)); d->command = FILE_WRITE; @@ -1781,6 +2044,13 @@ static int async_write(file_descriptor *desc, int *errp, d->c.writev.port = desc->port; d->c.writev.q_mtx = desc->q_mtx; d->c.writev.size = desc->write_buffered; +#ifdef USE_VM_PROBES + if (dt_i1 != NULL) { + *dt_i1 = d->fd; + *dt_i2 = d->flags; + *dt_i3 = d->c.writev.size; + } +#endif d->reply = reply; d->c.writev.free_size = 0; d->c.writev.reply_size = reply_size; @@ -1789,18 +2059,49 @@ static int async_write(file_descriptor *desc, int *errp, d->level = 1; cq_enq(desc, d); desc->write_buffered = 0; - return 0; + return d; } -static int flush_write(file_descriptor *desc, int *errp) { - int result; +static int flush_write(file_descriptor *desc, int *errp +#ifdef USE_VM_PROBES + , dt_private *dt_priv, char *dt_utag +#endif +) { + int result = 0; +#ifdef USE_VM_PROBES + Sint64 dt_i1 = 0, dt_i2 = 0, dt_i3 = 0; +#endif + struct t_data *d = NULL; + MUTEX_LOCK(desc->q_mtx); if (desc->write_buffered > 0) { - result = async_write(desc, errp, 0, 0); - } else { - result = 0; + if ((d = async_write(desc, errp, 0, 0 +#ifdef USE_VM_PROBES + ,&dt_i1, &dt_i2, &dt_i3 +#endif + )) == NULL) { + result = -1; + } } MUTEX_UNLOCK(desc->q_mtx); +#ifdef USE_VM_PROBES + if (d != NULL) { + d->sched_i1 = dt_priv->thread_num; + d->sched_i2 = dt_priv->tag; + d->sched_utag[0] = '\0'; + if (dt_utag != NULL) { + if (dt_utag[0] == '\0') { + dt_utag = NULL; + } else { + strncpy(d->sched_utag, dt_utag, sizeof(d->sched_utag) - 1); + d->sched_utag[sizeof(d->sched_utag) - 1] = '\0'; + } + } + DTRACE11(efile_drv_entry, dt_priv->thread_num, dt_priv->tag++, + dt_utag, FILE_WRITE, + NULL, NULL, dt_i1, dt_i2, dt_i3, 0, desc->port_str); + } +#endif /* USE_VM_PROBES */ return result; } @@ -1813,9 +2114,17 @@ static int check_write_error(file_descriptor *desc, int *errp) { return 0; } -static int flush_write_check_error(file_descriptor *desc, int *errp) { +static int flush_write_check_error(file_descriptor *desc, int *errp +#ifdef USE_VM_PROBES + , dt_private *dt_priv, char *dt_utag +#endif + ) { int r; - if ( (r = flush_write(desc, errp)) != 0) { + if ( (r = flush_write(desc, errp +#ifdef USE_VM_PROBES + , dt_priv, dt_utag +#endif + )) != 0) { check_write_error(desc, NULL); return r; } else { @@ -1823,12 +2132,16 @@ static int flush_write_check_error(file_descriptor *desc, int *errp) { } } -static int async_lseek(file_descriptor *desc, int *errp, int reply, - Sint64 offset, int origin) { +static struct t_data *async_lseek(file_descriptor *desc, int *errp, int reply, + Sint64 offset, int origin +#ifdef USE_VM_PROBES + , Sint64 *dt_i1, Sint64 *dt_i2, Sint64 *dt_i3 +#endif + ) { struct t_data *d; if (! (d = EF_ALLOC(sizeof(struct t_data)))) { *errp = ENOMEM; - return -1; + return NULL; } d->flags = desc->flags; d->fd = desc->fd; @@ -1836,11 +2149,18 @@ static int async_lseek(file_descriptor *desc, int *errp, int reply, d->reply = reply; d->c.lseek.offset = offset; d->c.lseek.origin = origin; +#ifdef USE_VM_PROBES + if (dt_i1 != NULL) { + *dt_i1 = d->fd; + *dt_i2 = d->c.lseek.offset; + *dt_i3 = d->c.lseek.origin; + } +#endif d->invoke = invoke_lseek; d->free = free_data; d->level = 1; cq_enq(desc, d); - return 0; + return d; } static void flush_read(file_descriptor *desc) { @@ -1852,18 +2172,45 @@ static void flush_read(file_descriptor *desc) { } } -static int lseek_flush_read(file_descriptor *desc, int *errp) { +static int lseek_flush_read(file_descriptor *desc, int *errp +#ifdef USE_VM_PROBES + ,dt_private *dt_priv, char *dt_utag +#endif + ) { int r = 0; size_t read_size = desc->read_size; +#ifdef USE_VM_PROBES + Sint64 dt_i1 = 0, dt_i2 = 0, dt_i3 = 0; +#endif + struct t_data *d; + + flush_read(desc); if (read_size != 0) { - flush_read(desc); - if ((r = async_lseek(desc, errp, 0, - -((ssize_t)read_size), EFILE_SEEK_CUR)) - < 0) { - return r; - } - } else { - flush_read(desc); + if ((d = async_lseek(desc, errp, 0, + -((ssize_t)read_size), EFILE_SEEK_CUR +#ifdef USE_VM_PROBES + , &dt_i1, &dt_i2, &dt_i3 +#endif + )) == NULL) { + r = -1; + } else { +#ifdef USE_VM_PROBES + d->sched_i1 = dt_priv->thread_num; + d->sched_i2 = dt_priv->tag; + d->sched_utag[0] = '\0'; + if (dt_utag != NULL) { + if (dt_utag[0] == '\0') { + dt_utag = NULL; + } else { + strncpy(d->sched_utag, dt_utag, sizeof(d->sched_utag) - 1); + d->sched_utag[sizeof(d->sched_utag) - 1] = '\0'; + } + } + DTRACE11(efile_drv_entry, dt_priv->thread_num, dt_priv->tag++, + dt_utag, FILE_LSEEK, + NULL, NULL, dt_i1, dt_i2, dt_i3, 0, desc->port_str); +#endif /* USE_VM_PROBES */ + } } return r; } @@ -1880,11 +2227,23 @@ file_async_ready(ErlDrvData e, ErlDrvThreadData data) struct t_data *d = (struct t_data *) data; char header[5]; /* result code + count */ char resbuf[RESBUFSIZE]; /* Result buffer. */ - +#ifdef USE_VM_PROBES + int sched_i1 = d->sched_i1, sched_i2 = d->sched_i2, command = d->command, + result_ok = d->result_ok, + posix_errno = d->result_ok ? 0 : d->errInfo.posix_errno; + DTRACE_CHARBUF(sched_utag, DTRACE_EFILE_BUFSIZ+1); + + sched_utag[0] = '\0'; + if (DTRACE_ENABLED(efile_drv_return)) { + strncpy(sched_utag, d->sched_utag, DTRACE_EFILE_BUFSIZ); + sched_utag[DTRACE_EFILE_BUFSIZ] = '\0'; + } +#endif /* USE_VM_PROBES */ TRACE_C('r'); if (try_again(desc, d)) { + /* DTRACE TODO: what kind of probe makes sense here? */ return; } @@ -2026,24 +2385,25 @@ file_async_ready(ErlDrvData e, ErlDrvThreadData data) if (d->result_ok) { resbuf[0] = FILE_RESP_INFO; - put_int32(d->info.size_high, &resbuf[1 + (0 * 4)]); - put_int32(d->info.size_low, &resbuf[1 + (1 * 4)]); - put_int32(d->info.type, &resbuf[1 + (2 * 4)]); - - PUT_TIME(d->info.accessTime, resbuf + 1 + 3*4); - PUT_TIME(d->info.modifyTime, resbuf + 1 + 9*4); - PUT_TIME(d->info.cTime, resbuf + 1 + 15*4); - - put_int32(d->info.mode, &resbuf[1 + (21 * 4)]); - put_int32(d->info.links, &resbuf[1 + (22 * 4)]); - put_int32(d->info.major_device, &resbuf[1 + (23 * 4)]); - put_int32(d->info.minor_device, &resbuf[1 + (24 * 4)]); - put_int32(d->info.inode, &resbuf[1 + (25 * 4)]); - put_int32(d->info.uid, &resbuf[1 + (26 * 4)]); - put_int32(d->info.gid, &resbuf[1 + (27 * 4)]); - put_int32(d->info.access, &resbuf[1 + (28 * 4)]); - -#define RESULT_SIZE (1 + (29 * 4)) + put_int32(d->info.size_high, &resbuf[1 + ( 0 * 4)]); + put_int32(d->info.size_low, &resbuf[1 + ( 1 * 4)]); + put_int32(d->info.type, &resbuf[1 + ( 2 * 4)]); + + /* Note 64 bit indexing in resbuf here */ + put_int64(d->info.accessTime, &resbuf[1 + ( 3 * 4)]); + put_int64(d->info.modifyTime, &resbuf[1 + ( 5 * 4)]); + put_int64(d->info.cTime, &resbuf[1 + ( 7 * 4)]); + + put_int32(d->info.mode, &resbuf[1 + ( 9 * 4)]); + put_int32(d->info.links, &resbuf[1 + (10 * 4)]); + put_int32(d->info.major_device, &resbuf[1 + (11 * 4)]); + put_int32(d->info.minor_device, &resbuf[1 + (12 * 4)]); + put_int32(d->info.inode, &resbuf[1 + (13 * 4)]); + put_int32(d->info.uid, &resbuf[1 + (14 * 4)]); + put_int32(d->info.gid, &resbuf[1 + (15 * 4)]); + put_int32(d->info.access, &resbuf[1 + (16 * 4)]); + +#define RESULT_SIZE (1 + (17 * 4)) TRACE_C('R'); driver_output2(desc->port, resbuf, RESULT_SIZE, NULL, 0); #undef RESULT_SIZE @@ -2053,30 +2413,24 @@ file_async_ready(ErlDrvData e, ErlDrvThreadData data) free_data(data); break; case FILE_READDIR: - if (!d->result_ok) + if (!d->result_ok) { reply_error(desc, &d->errInfo); - else { + } else { struct t_readdir_buf *b1 = d->c.read_dir.first_buf; + char op = FILE_RESP_LFNAME; + TRACE_C('R'); ASSERT(b1); + while (b1) { struct t_readdir_buf *b2 = b1; char *p = &b1->buf[0]; - int sz = get_int32(p); - while (sz) { /* 0 == EOB */ - p += 4; - if (sz - 1 > 0) { - driver_output2(desc->port, p, 1, p+1, sz-1); - } else { - driver_output2(desc->port, p, 1, NULL, 0); - } - p += sz; - sz = get_int32(p); - } + driver_output2(desc->port, p, 1, p + 1, b1->n - 1); b1 = b1->next; EF_FREE(b2); } - + driver_output2(desc->port, &op, 1, NULL, 0); + d->c.read_dir.first_buf = NULL; d->c.read_dir.last_buf = NULL; } @@ -2087,6 +2441,9 @@ file_async_ready(ErlDrvData e, ErlDrvThreadData data) if (d->reply) { TRACE_C('K'); reply_ok(desc); +#ifdef USE_VM_PROBES + result_ok = 1; +#endif } free_data(data); break; @@ -2116,9 +2473,34 @@ file_async_ready(ErlDrvData e, ErlDrvThreadData data) } free_preadv(data); break; +#ifdef HAVE_SENDFILE + case FILE_SENDFILE: + if (d->result_ok == -1) { + if (d->errInfo.posix_errno == ECONNRESET || + d->errInfo.posix_errno == ENOTCONN || + d->errInfo.posix_errno == EPIPE) + reply_string_error(desc,"closed"); + else + reply_error(desc, &d->errInfo); + desc->sendfile_state = not_sending; + free_sendfile(data); + } else if (d->result_ok == 0) { + reply_Sint64(desc, d->c.sendfile.written); + desc->sendfile_state = not_sending; + free_sendfile(data); + } else if (d->result_ok == 1) { // If we are using select to send the rest of the data + desc->sendfile_state = sending; + desc->d = d; + driver_select(desc->port, (ErlDrvEvent)(long)d->c.sendfile.out_fd, + ERL_DRV_USE_NO_CALLBACK|ERL_DRV_WRITE, 1); + } + break; +#endif default: abort(); } + DTRACE6(efile_drv_return, sched_i1, sched_i2, sched_utag, + command, result_ok, posix_errno); if (desc->write_buffered != 0 && desc->timer_state == timer_idle) { desc->timer_state = timer_write; driver_set_timer(desc->port, desc->write_delay); @@ -2126,11 +2508,12 @@ file_async_ready(ErlDrvData e, ErlDrvThreadData data) cq_execute(desc); } + /********************************************************************* * Driver entry point -> output */ static void -file_output(ErlDrvData e, char* buf, int count) +file_output(ErlDrvData e, char* buf, ErlDrvSizeT count) { file_descriptor* desc = (file_descriptor*)e; Efile_error errInfo; /* The error codes for the last operation. */ @@ -2140,7 +2523,15 @@ file_output(ErlDrvData e, char* buf, int count) char* name; /* Points to the filename in buf. */ int command; struct t_data *d = NULL; - +#ifdef USE_VM_PROBES + char *dt_utag = NULL; + char *dt_s1 = NULL, *dt_s2 = NULL; + Sint64 dt_i1 = 0; + Sint64 dt_i2 = 0; + Sint64 dt_i3 = 0; + Sint64 dt_i4 = 0; + dt_private *dt_priv = get_dt_private(0); +#endif /* USE_VM_PROBES */ TRACE_C('o'); @@ -2155,6 +2546,10 @@ file_output(ErlDrvData e, char* buf, int count) d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1 + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE); FILENAME_COPY(d->b, name); +#ifdef USE_VM_PROBES + dt_s1 = d->b; + dt_utag = name + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE; +#endif d->command = command; d->invoke = invoke_mkdir; d->free = free_data; @@ -2166,6 +2561,10 @@ file_output(ErlDrvData e, char* buf, int count) d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1 + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE); FILENAME_COPY(d->b, name); +#ifdef USE_VM_PROBES + dt_s1 = d->b; + dt_utag = name + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE; +#endif d->command = command; d->invoke = invoke_rmdir; d->free = free_data; @@ -2177,6 +2576,10 @@ file_output(ErlDrvData e, char* buf, int count) d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1 + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE); FILENAME_COPY(d->b, name); +#ifdef USE_VM_PROBES + dt_s1 = d->b; + dt_utag = name + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE; +#endif d->command = command; d->invoke = invoke_delete_file; d->free = free_data; @@ -2194,6 +2597,11 @@ file_output(ErlDrvData e, char* buf, int count) FILENAME_COPY(d->b, name); FILENAME_COPY(d->b + namelen, new_name); +#ifdef USE_VM_PROBES + dt_s1 = d->b; + dt_s2 = d->b + namelen; + dt_utag = buf + namelen + FILENAME_BYTELEN(new_name) + FILENAME_CHARSIZE; +#endif d->flags = desc->flags; d->fd = fd; d->command = command; @@ -2207,6 +2615,10 @@ file_output(ErlDrvData e, char* buf, int count) d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1 + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE); FILENAME_COPY(d->b, name); +#ifdef USE_VM_PROBES + dt_s1 = d->b; + dt_utag = name + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE; +#endif d->command = command; d->invoke = invoke_chdir; d->free = free_data; @@ -2218,6 +2630,9 @@ file_output(ErlDrvData e, char* buf, int count) d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1 + RESBUFSIZE + 1); d->drive = *(uchar*)buf; +#ifdef USE_VM_PROBES + dt_utag = buf + 1; +#endif d->command = command; d->invoke = invoke_pwd; d->free = free_data; @@ -2233,6 +2648,10 @@ file_output(ErlDrvData e, char* buf, int count) FILENAME_CHARSIZE); FILENAME_COPY(d->b, name); +#ifdef USE_VM_PROBES + dt_s1 = d->b; + dt_utag = name + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE; +#endif d->dir_handle = NULL; d->command = command; d->invoke = invoke_readdir; @@ -2246,23 +2665,65 @@ file_output(ErlDrvData e, char* buf, int count) #endif { size_t resbufsize; - char resbuf[RESBUFSIZE+1]; + size_t n = 0, total = 0; + int res = 0; + char resbuf[READDIR_BUFSIZE]; + EFILE_DIR_HANDLE dir_handle; /* Handle to open directory. */ + total = READDIR_BUFSIZE; errInfo.posix_errno = 0; - dir_handle = NULL; - resbuf[0] = FILE_RESP_FNAME; - resbufsize = RESBUFSIZE; - - while (efile_readdir(&errInfo, name, &dir_handle, - resbuf+1, &resbufsize)) { - driver_output2(desc->port, resbuf, 1, resbuf+1, resbufsize); - resbufsize = RESBUFSIZE; - } + dir_handle = NULL; + resbuf[0] = FILE_RESP_LFNAME; + +#ifdef USE_VM_PROBES + dt_s1 = name; + dt_utag = name + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE; +#endif + /* Fill the buffer with multiple directory listings before sending it to the + * receiving process. READDIR_CHUNKS is minimum number of files sent to the + * receiver. + * Format for each driver_output2: + * ------------------------------------ + * | Type | Len | Filename | ... + * | 1 byte | 2 bytes | Len bytes | ... + * ------------------------------------ + */ + + do { + n = 1; + resbufsize = READDIR_BUFSIZE - n; + + do { + res = efile_readdir(&errInfo, name, &dir_handle, resbuf + n + 2, &resbufsize); + + if (res) { + put_int16((Uint16)resbufsize, resbuf + n); + n += 2 + resbufsize; + resbufsize = READDIR_BUFSIZE - n; + } + } while( res && ((total - n - 2) >= MAXPATHLEN*FILENAME_CHARSIZE)); + + if (n > 1) { + driver_output2(desc->port, resbuf, 1, resbuf + 1, n - 1); + } + } while(res); + if (errInfo.posix_errno != 0) { reply_error(desc, &errInfo); return; } +#ifdef USE_VM_PROBES + if (dt_utag != NULL && dt_utag[0] == '\0') { + dt_utag = NULL; + } + + DTRACE11(efile_drv_entry, dt_priv->thread_num, dt_priv->tag, + dt_utag, command, name, dt_s2, + dt_i1, dt_i2, dt_i3, dt_i4, desc->port_str); + DTRACE6(efile_drv_return, dt_priv->thread_num, dt_priv->tag++, + dt_utag, command, 1, 0); +#endif TRACE_C('R'); driver_output2(desc->port, resbuf, 1, NULL, 0); return; @@ -2275,6 +2736,11 @@ file_output(ErlDrvData e, char* buf, int count) d->flags = get_int32((uchar*)buf); name = buf+4; FILENAME_COPY(d->b, name); +#ifdef USE_VM_PROBES + dt_i1 = d->flags; + dt_s1 = d->b; + dt_utag = name + FILENAME_BYTELEN(d->b) + FILENAME_CHARSIZE; +#endif d->command = command; d->invoke = invoke_open; d->free = free_data; @@ -2287,6 +2753,10 @@ file_output(ErlDrvData e, char* buf, int count) d = EF_SAFE_ALLOC(sizeof(struct t_data)); d->fd = fd; +#ifdef USE_VM_PROBES + dt_utag = name; + dt_i1 = fd; +#endif d->command = command; d->invoke = invoke_fdatasync; d->free = free_data; @@ -2299,6 +2769,10 @@ file_output(ErlDrvData e, char* buf, int count) d = EF_SAFE_ALLOC(sizeof(struct t_data)); d->fd = fd; +#ifdef USE_VM_PROBES + dt_utag = name; + dt_i1 = fd; +#endif d->command = command; d->invoke = invoke_fsync; d->free = free_data; @@ -2315,6 +2789,14 @@ file_output(ErlDrvData e, char* buf, int count) FILENAME_COPY(d->b, name); d->fd = fd; +#ifdef USE_VM_PROBES + dt_utag = name + FILENAME_BYTELEN(d->b) + FILENAME_CHARSIZE; + if (command == FILE_LSTAT) { + dt_s1 = d->b; + } else { + dt_i1 = fd; + } +#endif d->command = command; d->invoke = invoke_flstat; d->free = free_data; @@ -2328,6 +2810,11 @@ file_output(ErlDrvData e, char* buf, int count) d->flags = desc->flags; d->fd = fd; +#ifdef USE_VM_PROBES + dt_utag = name; + dt_i1 = fd; + dt_i2 = d->flags; +#endif d->command = command; d->invoke = invoke_truncate; d->free = free_data; @@ -2338,15 +2825,23 @@ file_output(ErlDrvData e, char* buf, int count) case FILE_WRITE_INFO: { d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1 - + FILENAME_BYTELEN(buf+21*4) + FILENAME_CHARSIZE); + + FILENAME_BYTELEN(buf + 9*4) + FILENAME_CHARSIZE); - d->info.mode = get_int32(buf + 0 * 4); - d->info.uid = get_int32(buf + 1 * 4); - d->info.gid = get_int32(buf + 2 * 4); - GET_TIME(d->info.accessTime, buf + 3 * 4); - GET_TIME(d->info.modifyTime, buf + 9 * 4); - GET_TIME(d->info.cTime, buf + 15 * 4); - FILENAME_COPY(d->b, buf+21*4); + d->info.mode = get_int32(buf + 0 * 4); + d->info.uid = get_int32(buf + 1 * 4); + d->info.gid = get_int32(buf + 2 * 4); + d->info.accessTime = (time_t)((Sint64)get_int64(buf + 3 * 4)); + d->info.modifyTime = (time_t)((Sint64)get_int64(buf + 5 * 4)); + d->info.cTime = (time_t)((Sint64)get_int64(buf + 7 * 4)); + + FILENAME_COPY(d->b, buf + 9*4); +#ifdef USE_VM_PROBES + dt_i1 = d->info.mode; + dt_i2 = d->info.uid; + dt_i3 = d->info.gid; + dt_s1 = d->b; + dt_utag = buf + 9 * 4 + FILENAME_BYTELEN(d->b) + FILENAME_CHARSIZE; +#endif d->command = command; d->invoke = invoke_write_info; d->free = free_data; @@ -2356,9 +2851,14 @@ file_output(ErlDrvData e, char* buf, int count) case FILE_READLINK: { - d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1 + RESBUFSIZE + 1); - + d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1 + + MAX(RESBUFSIZE, (FILENAME_BYTELEN(name) + + FILENAME_CHARSIZE)) + 1); FILENAME_COPY(d->b, name); +#ifdef USE_VM_PROBES + dt_s1 = d->b; + dt_utag = name + FILENAME_BYTELEN(d->b) + FILENAME_CHARSIZE; +#endif d->command = command; d->invoke = invoke_readlink; d->free = free_data; @@ -2368,8 +2868,14 @@ file_output(ErlDrvData e, char* buf, int count) case FILE_ALTNAME: { - d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1 + RESBUFSIZE + 1); + d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1 + + MAX(RESBUFSIZE, (FILENAME_BYTELEN(name) + + FILENAME_CHARSIZE)) + 1); FILENAME_COPY(d->b, name); +#ifdef USE_VM_PROBES + dt_s1 = d->b; + dt_utag = name + FILENAME_BYTELEN(d->b) + FILENAME_CHARSIZE; +#endif d->command = command; d->invoke = invoke_altname; d->free = free_data; @@ -2390,6 +2896,11 @@ file_output(ErlDrvData e, char* buf, int count) FILENAME_COPY(d->b, name); FILENAME_COPY(d->b + namelen, new_name); +#ifdef USE_VM_PROBES + dt_s1 = d->b; + dt_s2 = d->b + namelen; + dt_utag = buf + namelen + FILENAME_BYTELEN(dt_s2) + FILENAME_CHARSIZE; +#endif d->flags = desc->flags; d->fd = fd; d->command = command; @@ -2411,6 +2922,11 @@ file_output(ErlDrvData e, char* buf, int count) FILENAME_COPY(d->b, name); FILENAME_COPY(d->b + namelen, new_name); +#ifdef USE_VM_PROBES + dt_s1 = d->b; + dt_s2 = d->b + namelen; + dt_utag = buf + namelen + FILENAME_BYTELEN(dt_s2) + FILENAME_CHARSIZE; +#endif d->flags = desc->flags; d->fd = fd; d->command = command; @@ -2432,6 +2948,13 @@ file_output(ErlDrvData e, char* buf, int count) d->c.fadvise.offset = get_int64((uchar*) buf); d->c.fadvise.length = get_int64(((uchar*) buf) + sizeof(Sint64)); d->c.fadvise.advise = get_int32(((uchar*) buf) + 2 * sizeof(Sint64)); +#ifdef USE_VM_PROBES + dt_i1 = d->fd; + dt_i2 = d->c.fadvise.offset; + dt_i3 = d->c.fadvise.length; + dt_i4 = d->c.fadvise.advise; + dt_utag = buf + 3 * sizeof(Sint64); +#endif goto done; } @@ -2445,6 +2968,22 @@ file_output(ErlDrvData e, char* buf, int count) done: if (d) { +#ifdef USE_VM_PROBES + d->sched_i1 = dt_priv->thread_num; + d->sched_i2 = dt_priv->tag; + d->sched_utag[0] = '\0'; + if (dt_utag != NULL) { + if (dt_utag[0] == '\0') { + dt_utag = NULL; + } else { + strncpy(d->sched_utag, dt_utag, sizeof(d->sched_utag) - 1); + d->sched_utag[sizeof(d->sched_utag) - 1] = '\0'; + } + } + DTRACE11(efile_drv_entry, dt_priv->thread_num, dt_priv->tag++, + dt_utag, command, dt_s1, dt_s2, + dt_i1, dt_i2, dt_i3, dt_i4, desc->port_str); +#endif cq_enq(desc, d); } } @@ -2455,16 +2994,33 @@ file_output(ErlDrvData e, char* buf, int count) static void file_flush(ErlDrvData e) { file_descriptor *desc = (file_descriptor *)e; +#ifdef DEBUG int r; +#endif +#ifdef USE_VM_PROBES + dt_private *dt_priv = get_dt_private(dt_driver_io_worker_base); +#endif TRACE_C('f'); - r = flush_write(desc, NULL); +#ifdef HAVE_SENDFILE + flush_sendfile(desc, NULL); +#endif + +#ifdef DEBUG + r = +#endif + flush_write(desc, NULL +#ifdef USE_VM_PROBES + , dt_priv, (desc->d == NULL) ? NULL : desc->d->sched_utag +#endif + ); /* Only possible reason for bad return value is ENOMEM, and * there is nobody to tell... */ +#ifdef DEBUG ASSERT(r == 0); - r = 0; /* Avoiding warning */ +#endif cq_execute(desc); } @@ -2473,16 +3029,23 @@ file_flush(ErlDrvData e) { /********************************************************************* * Driver entry point -> control */ -static int +static ErlDrvSSizeT file_control(ErlDrvData e, unsigned int command, - char* buf, int len, char **rbuf, int rlen) { + char* buf, ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen) { + /* + * warning: variable ‘desc’ set but not used + * [-Wunused-but-set-variable] + * ... no kidding ... + * + * file_descriptor *desc = (file_descriptor *)e; switch (command) { default: return 0; - } /* switch (command) */ + } ASSERT(0); - desc = NULL; /* XXX Avoid warning while empty switch */ + desc = NULL; + */ return 0; } @@ -2493,6 +3056,9 @@ static void file_timeout(ErlDrvData e) { file_descriptor *desc = (file_descriptor *)e; enum e_timer timer_state = desc->timer_state; +#ifdef USE_VM_PROBES + dt_private *dt_priv = get_dt_private(dt_driver_io_worker_base); +#endif TRACE_C('t'); @@ -2507,12 +3073,18 @@ file_timeout(ErlDrvData e) { driver_async(desc->port, KEY(desc), desc->invoke, desc->d, desc->free); break; case timer_write: { - int r = flush_write(desc, NULL); +#ifdef DEBUG + int r = +#endif + flush_write(desc, NULL +#ifdef USE_VM_PROBES + , dt_priv, (desc->d == NULL) ? NULL : desc->d->sched_utag +#endif + ); /* Only possible reason for bad return value is ENOMEM, and * there is nobody to tell... */ ASSERT(r == 0); - r = 0; /* Avoiding warning */ cq_execute(desc); } break; } /* case */ @@ -2529,6 +3101,14 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { char command; int p, q; int err; + struct t_data *d = NULL; +#ifdef USE_VM_PROBES + Sint64 dt_i1 = 0, dt_i2 = 0, dt_i3 = 0; + Sint64 dt_i4 = 0; + char *dt_utag = NULL; + char *dt_s1 = NULL; + dt_private *dt_priv = get_dt_private(dt_driver_io_worker_base); +#endif TRACE_C('v'); @@ -2548,18 +3128,19 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { switch (command) { case FILE_CLOSE: { +#ifdef USE_VM_PROBES + dt_utag = EV_CHAR_P(ev, p, q); +#endif flush_read(desc); - if (flush_write_check_error(desc, &err) < 0) { + if (flush_write_check_error(desc, &err +#ifdef USE_VM_PROBES + , dt_priv, dt_utag +#endif + ) < 0) { reply_posix_error(desc, err); goto done; } - if (ev->size != 1) { - /* Wrong command length */ - reply_posix_error(desc, EINVAL); - goto done; - } if (desc->fd != FILE_FD_INVALID) { - struct t_data *d; if (! (d = EF_ALLOC(sizeof(struct t_data)))) { reply_posix_error(desc, ENOMEM); } else { @@ -2567,6 +3148,10 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { d->reply = !0; d->fd = desc->fd; d->flags = desc->flags; +#ifdef USE_VM_PROBES + dt_i1 = d->fd; + dt_i2 = d->flags; +#endif d->invoke = invoke_close; d->free = free_data; d->level = 2; @@ -2582,8 +3167,21 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { case FILE_READ: { Uint32 sizeH, sizeL; size_t size, alloc_size; - struct t_data *d; - if (flush_write_check_error(desc, &err) < 0) { + + if (!EV_GET_UINT32(ev, &sizeH, &p, &q) + || !EV_GET_UINT32(ev, &sizeL, &p, &q)) { + /* Wrong buffer length to contain the read count */ + reply_posix_error(desc, EINVAL); + goto done; + } +#ifdef USE_VM_PROBES + dt_utag = EV_CHAR_P(ev, p, q); +#endif + if (flush_write_check_error(desc, &err +#ifdef USE_VM_PROBES + , dt_priv, dt_utag +#endif + ) < 0) { reply_posix_error(desc, err); goto done; } @@ -2591,19 +3189,16 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { if (desc->read_bufsize == 0 && desc->read_binp != NULL && desc->read_size > 0) { /* We have allocated a buffer for line mode but should not really have a read-ahead buffer... */ - if (lseek_flush_read(desc, &err) < 0) { + if (lseek_flush_read(desc, &err +#ifdef USE_VM_PROBES + , dt_priv, dt_utag +#endif + ) < 0) { reply_posix_error(desc, err); goto done; } } #endif - if (ev->size != 1+8 - || !EV_GET_UINT32(ev, &sizeH, &p, &q) - || !EV_GET_UINT32(ev, &sizeL, &p, &q)) { - /* Wrong buffer length to contain the read count */ - reply_posix_error(desc, EINVAL); - goto done; - } #if SIZEOF_SIZE_T == 4 if (sizeH != 0) { reply_posix_error(desc, EINVAL); @@ -2679,6 +3274,11 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { d->c.read.bin_offset = desc->read_offset + desc->read_size; d->c.read.bin_size = desc->read_binp->orig_size - d->c.read.bin_offset; d->c.read.size = size; +#ifdef USE_VM_PROBES + dt_i1 = d->fd; + dt_i2 = d->flags; + dt_i3 = d->c.read.size; +#endif driver_binary_inc_refc(d->c.read.binp); d->invoke = invoke_read; d->free = free_read; @@ -2696,12 +3296,22 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { * allocated binary + dealing with offsets and lengts are done in file_async ready * for this OP. */ - struct t_data *d; - if (flush_write_check_error(desc, &err) < 0) { +#ifdef USE_VM_PROBES + dt_utag = EV_CHAR_P(ev, p, q); +#endif + if (flush_write_check_error(desc, &err +#ifdef USE_VM_PROBES + , dt_priv, dt_utag +#endif + ) < 0) { reply_posix_error(desc, err); goto done; } - if (ev->size != 1) { + if (ev->size != 1 +#ifdef USE_VM_PROBES + + FILENAME_BYTELEN(dt_utag) + FILENAME_CHARSIZE +#endif + ) { /* Wrong command length */ reply_posix_error(desc, EINVAL); goto done; @@ -2757,8 +3367,16 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { d->c.read_line.binp = desc->read_binp; d->c.read_line.read_offset = desc->read_offset; d->c.read_line.read_size = desc->read_size; +#ifdef USE_VM_PROBES + dt_i1 = d->fd; + dt_i2 = d->flags; + dt_i3 = d->c.read_line.read_offset; +#endif #if !ALWAYS_READ_LINE_AHEAD d->c.read_line.read_ahead = (desc->read_bufsize > 0); +#ifdef USE_VM_PROBES + dt_i4 = d->c.read_line.read_ahead; +#endif #endif driver_binary_inc_refc(d->c.read.binp); d->invoke = invoke_read_line; @@ -2766,10 +3384,22 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { d->level = 1; cq_enq(desc, d); } goto done; - case FILE_WRITE: { - int skip = 1; - int size = ev->size - skip; - if (lseek_flush_read(desc, &err) < 0) { + case FILE_WRITE: { /* Dtrace: The dtrace user tag is not last in message, + but follows the message tag directly. + This is handled specially in prim_file.erl */ + ErlDrvSizeT skip = 1; + ErlDrvSizeT size = ev->size - skip; + +#ifdef USE_VM_PROBES + dt_utag = EV_CHAR_P(ev, p, q); + skip += FILENAME_BYTELEN(dt_utag) + FILENAME_CHARSIZE; + size = ev->size - skip; +#endif + if (lseek_flush_read(desc, &err +#ifdef USE_VM_PROBES + , dt_priv, dt_utag +#endif + ) < 0) { reply_posix_error(desc, err); goto done; } @@ -2777,7 +3407,7 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { reply_posix_error(desc, EBADF); goto done; } - if (size <= 0) { + if (size == 0) { reply_Uint(desc, size); goto done; } @@ -2796,7 +3426,11 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { driver_set_timer(desc->port, desc->write_delay); } } else { - if (async_write(desc, &err, !0, size) != 0) { + if ((d = async_write(desc, &err, !0, size +#ifdef USE_VM_PROBES + , &dt_i1, &dt_i2, &dt_i3 +#endif + )) == NULL) { MUTEX_UNLOCK(desc->q_mtx); reply_posix_error(desc, err); goto done; @@ -2806,24 +3440,49 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { } } goto done; /* case FILE_WRITE */ - case FILE_PWRITEV: { + case FILE_PWRITEV: { /* Dtrace: The dtrace user tag is not last in message, + but follows the message tag directly. + This is handled specially in prim_file.erl */ Uint32 i, j, n; size_t total; - struct t_data *d; - if (lseek_flush_read(desc, &err) < 0) { - reply_Uint_posix_error(desc, 0, err); - goto done; - } - if (flush_write_check_error(desc, &err) < 0) { - reply_Uint_posix_error(desc, 0, err); - goto done; +#ifdef USE_VM_PROBES + char dt_tmp; + int dt_utag_bytes = 1; + + dt_utag = EV_CHAR_P(ev, p, q); + /* This will work for UTF-8, but not for UTF-16 - extra reminder here */ +#ifdef FILENAMES_16BIT +#error 16bit characters in filenames and dtrace in combination is not supported. +#endif + while (EV_GET_CHAR(ev, &dt_tmp, &p, &q) && dt_tmp != '\0') { + dt_utag_bytes++; } +#endif if (ev->size < 1+4 +#ifdef USE_VM_PROBES + + dt_utag_bytes +#endif || !EV_GET_UINT32(ev, &n, &p, &q)) { /* Buffer too short to contain even the number of pos/size specs */ reply_Uint_posix_error(desc, 0, EINVAL); goto done; } + if (lseek_flush_read(desc, &err +#ifdef USE_VM_PROBES + , dt_priv, dt_utag +#endif + ) < 0) { + reply_Uint_posix_error(desc, 0, err); + goto done; + } + if (flush_write_check_error(desc, &err +#ifdef USE_VM_PROBES + , dt_priv, dt_utag +#endif + ) < 0) { + reply_Uint_posix_error(desc, 0, err); + goto done; + } if (n == 0) { /* Trivial case - nothing to write */ if (ev->size != 1+4) { @@ -2833,7 +3492,11 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { } goto done; } - if (ev->size < 1+4+8*(2*n)) { + if (ev->size < 1+4+8*(2*n) +#ifdef USE_VM_PROBES + + dt_utag_bytes +#endif + ) { /* Buffer too short to contain even the pos/size specs */ reply_Uint_posix_error(desc, 0, EINVAL); goto done; @@ -2848,6 +3511,10 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { d->reply = !0; d->fd = desc->fd; d->flags = desc->flags; +#ifdef USE_VM_PROBES + dt_i1 = d->fd; + dt_i2 = d->flags; +#endif d->c.pwritev.port = desc->port; d->c.pwritev.q_mtx = desc->q_mtx; d->c.pwritev.n = n; @@ -2885,13 +3552,20 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { } } d->c.pwritev.size = total; +#ifdef USE_VM_PROBES + dt_i3 = d->c.pwritev.size; +#endif d->c.pwritev.free_size = 0; if (j == 0) { /* Trivial case - nothing to write */ EF_FREE(d); reply_Uint(desc, 0); } else { - size_t skip = 1 + 4 + 8*(2*n); + ErlDrvSizeT skip = 1 + 4 + 8 * (2*n) +#ifdef USE_VM_PROBES + + dt_utag_bytes +#endif + ; if (skip + total != ev->size) { /* Actual amount of data does not match * total of all pos/size specs @@ -2912,27 +3586,55 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { } } goto done; /* case FILE_PWRITEV: */ - case FILE_PREADV: { + case FILE_PREADV: { /* Dtrace: The dtrace user tag is not last in message, + but follows the message tag directly. + This is handled specially in prim_file.erl */ register void * void_ptr; Uint32 i, n; - struct t_data *d; ErlIOVec *res_ev; - if (lseek_flush_read(desc, &err) < 0) { +#ifdef USE_VM_PROBES + char dt_tmp; + int dt_utag_bytes = 1; + /* This will work for UTF-8, but not for UTF-16 - extra reminder here */ +#ifdef FILENAMES_16BIT +#error 16bit characters in filenames and dtrace in combination is not supported. +#endif + dt_utag = EV_CHAR_P(ev, p, q); + while (EV_GET_CHAR(ev, &dt_tmp, &p, &q) && dt_tmp != '\0') { + dt_utag_bytes++; + } +#endif + if (lseek_flush_read(desc, &err +#ifdef USE_VM_PROBES + , dt_priv, dt_utag +#endif + ) < 0) { reply_posix_error(desc, err); goto done; } - if (flush_write_check_error(desc, &err) < 0) { + if (flush_write_check_error(desc, &err +#ifdef USE_VM_PROBES + , dt_priv, dt_utag +#endif + ) < 0) { reply_posix_error(desc, err); goto done; } if (ev->size < 1+8 +#ifdef USE_VM_PROBES + + dt_utag_bytes +#endif || !EV_GET_UINT32(ev, &n, &p, &q) || !EV_GET_UINT32(ev, &n, &p, &q)) { /* Buffer too short to contain even the number of pos/size specs */ reply_posix_error(desc, EINVAL); goto done; } - if (ev->size != 1+8+8*(2*n)) { + if (ev->size < 1+8+8*(2*n) +#ifdef USE_VM_PROBES + + dt_utag_bytes +#endif + ) { /* Buffer wrong length to contain the pos/size specs */ reply_posix_error(desc, EINVAL); goto done; @@ -2952,6 +3654,10 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { d->reply = !0; d->fd = desc->fd; d->flags = desc->flags; +#ifdef USE_VM_PROBES + dt_i1 = d->fd; + dt_i2 = d->flags; +#endif d->c.preadv.n = n; d->c.preadv.cnt = 0; d->c.preadv.size = 0; @@ -2979,6 +3685,9 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { #else size = ((size_t)sizeH<<32) | sizeL; #endif +#ifdef USE_VM_PROBES + dt_i3 += size; +#endif if (! (res_ev->binv[i] = driver_alloc_binary(size))) { reply_posix_error(desc, ENOMEM); break; @@ -3025,42 +3734,68 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { } goto done; /* case FILE_PREADV: */ case FILE_LSEEK: { - Sint64 offset; /* Offset for seek */ + Sint64 offset; /* Offset for seek */ Uint32 origin; /* Origin of seek. */ - if (lseek_flush_read(desc, &err) < 0) { - reply_posix_error(desc, err); + + if (ev->size < 1+8+4 + || !EV_GET_UINT64(ev, &offset, &p, &q) + || !EV_GET_UINT32(ev, &origin, &p, &q)) { + /* Wrong length of buffer to contain offset and origin */ + reply_posix_error(desc, EINVAL); goto done; } - if (flush_write_check_error(desc, &err) < 0) { +#ifdef USE_VM_PROBES + dt_utag = EV_CHAR_P(ev, p, q); +#endif + if (lseek_flush_read(desc, &err +#ifdef USE_VM_PROBES + , dt_priv, dt_utag +#endif + ) < 0) { reply_posix_error(desc, err); goto done; } - if (ev->size != 1+8+4 - || !EV_GET_UINT64(ev, &offset, &p, &q) - || !EV_GET_UINT32(ev, &origin, &p, &q)) { - /* Wrong length of buffer to contain offset and origin */ - reply_posix_error(desc, EINVAL); + if (flush_write_check_error(desc, &err +#ifdef USE_VM_PROBES + , dt_priv, dt_utag +#endif + ) < 0) { + reply_posix_error(desc, err); goto done; } - if (async_lseek(desc, &err, !0, offset, origin) < 0) { + if ((d = async_lseek(desc, &err, !0, offset, origin +#ifdef USE_VM_PROBES + , &dt_i1, &dt_i2, &dt_i3 +#endif + )) == NULL) { reply_posix_error(desc, err); goto done; } } goto done; case FILE_READ_FILE: { - struct t_data *d; char *filename; if (ev->size < 1+1) { /* Buffer contains empty name */ reply_posix_error(desc, ENOENT); goto done; } +#ifndef USE_VM_PROBES + /* In the dtrace case, the iov has an extra element, the dtrace utag - we will need + another test to see that + the filename is in a single buffer: */ if (ev->size-1 != ev->iov[q].iov_len-p) { /* Name not in one single buffer */ reply_posix_error(desc, EINVAL); goto done; } +#else + if (((byte *)ev->iov[q].iov_base)[ev->iov[q].iov_len-1] != '\0') { + /* Name not in one single buffer */ + reply_posix_error(desc, EINVAL); + goto done; + } +#endif filename = EV_CHAR_P(ev, p, q); d = EF_ALLOC(sizeof(struct t_data) -1 + FILENAME_BYTELEN(filename) + FILENAME_CHARSIZE); if (! d) { @@ -3071,6 +3806,20 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { d->reply = !0; /* Copy name */ FILENAME_COPY(d->b, filename); +#ifdef USE_VM_PROBES + { + char dt_tmp; + + /* This will work for UTF-8, but not for UTF-16 - extra reminder here */ +#ifdef FILENAMES_16BIT +#error 16bit characters in filenames and dtrace in combination is not supported. +#endif + while (EV_GET_CHAR(ev, &dt_tmp, &p, &q) && dt_tmp != '\0') + ; + dt_s1 = d->b; + dt_utag = EV_CHAR_P(ev, p, q); + } +#endif d->c.read_file.binp = NULL; d->invoke = invoke_read_file; d->free = free_read_file; @@ -3090,7 +3839,6 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { char mode; Sint64 hdr_offset; Uint32 max_size; - struct t_data *d; ErlIOVec *res_ev; int vsize; if (! EV_GET_CHAR(ev, &mode, &p, &q)) { @@ -3102,14 +3850,6 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { reply_posix_error(desc, EINVAL); goto done; } - if (lseek_flush_read(desc, &err) < 0) { - reply_posix_error(desc, err); - goto done; - } - if (flush_write_check_error(desc, &err) < 0) { - reply_posix_error(desc, err); - goto done; - } if (ev->size < 1+1+8+4 || !EV_GET_UINT64(ev, &hdr_offset, &p, &q) || !EV_GET_UINT32(ev, &max_size, &p, &q)) { @@ -3118,6 +3858,25 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { reply_posix_error(desc, EINVAL); goto done; } +#ifdef USE_VM_PROBES + dt_utag = EV_CHAR_P(ev, p, q); +#endif + if (lseek_flush_read(desc, &err +#ifdef USE_VM_PROBES + , dt_priv, dt_utag +#endif + ) < 0) { + reply_posix_error(desc, err); + goto done; + } + if (flush_write_check_error(desc, &err +#ifdef USE_VM_PROBES + , dt_priv, dt_utag +#endif + ) < 0) { + reply_posix_error(desc, err); + goto done; + } /* Create the thread data structure with the contained ErlIOVec * and corresponding binaries for the response */ @@ -3134,6 +3893,12 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { d->flags = desc->flags; d->c.preadv.offsets[0] = hdr_offset; d->c.preadv.size = max_size; +#ifdef USE_VM_PROBES + dt_i1 = d->fd; + dt_i2 = d->flags; + dt_i3 = d->c.preadv.offsets[0]; + dt_i4 = d->c.preadv.size; +#endif res_ev = &d->c.preadv.eiov; /* XXX possible alignment problems here for weird machines */ res_ev->iov = void_ptr = d + 1; @@ -3148,16 +3913,24 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { case FILE_SETOPT: { char opt; + if (ev->size < 1+1 || !EV_GET_CHAR(ev, &opt, &p, &q)) { /* Buffer too short to contain even the option type */ reply_posix_error(desc, EINVAL); goto done; } +#ifdef USE_VM_PROBES + dt_i1 = opt; + dt_utag = EV_CHAR_P(ev, p, q); +#endif switch (opt) { case FILE_OPT_DELAYED_WRITE: { Uint32 sizeH, sizeL, delayH, delayL; if (ev->size != 1+1+4*sizeof(Uint32) +#ifdef USE_VM_PROBES + + FILENAME_BYTELEN(dt_utag) + FILENAME_CHARSIZE +#endif || !EV_GET_UINT32(ev, &sizeH, &p, &q) || !EV_GET_UINT32(ev, &sizeL, &p, &q) || !EV_GET_UINT32(ev, &delayH, &p, &q) @@ -3184,12 +3957,18 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { #else desc->write_delay = ((unsigned long)delayH << 32) | delayL; #endif +#ifdef USE_VM_PROBES + dt_i2 = desc->write_delay; +#endif TRACE_C('K'); reply_ok(desc); } goto done; case FILE_OPT_READ_AHEAD: { Uint32 sizeH, sizeL; if (ev->size != 1+1+2*sizeof(Uint32) +#ifdef USE_VM_PROBES + + FILENAME_BYTELEN(dt_utag)+FILENAME_CHARSIZE +#endif || !EV_GET_UINT32(ev, &sizeH, &p, &q) || !EV_GET_UINT32(ev, &sizeL, &p, &q)) { /* Buffer has wrong length to contain the option values */ @@ -3205,6 +3984,9 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { #else desc->read_bufsize = ((size_t)sizeH << 32) | sizeL; #endif +#ifdef USE_VM_PROBES + dt_i2 = desc->read_bufsize; +#endif TRACE_C('K'); reply_ok(desc); } goto done; @@ -3213,14 +3995,97 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { goto done; } /* case FILE_OPT_DELAYED_WRITE: */ } ASSERT(0); goto done; /* case FILE_SETOPT: */ - + + case FILE_SENDFILE: { + +#ifdef HAVE_SENDFILE + struct t_data *d; + Uint32 out_fd, offsetH, offsetL, hd_len, tl_len; + Uint64 nbytes; + char flags; + + if (ev->size < 1 + 7 * sizeof(Uint32) + sizeof(char) + || !EV_GET_UINT32(ev, &out_fd, &p, &q) + || !EV_GET_CHAR(ev, &flags, &p, &q) + || !EV_GET_UINT32(ev, &offsetH, &p, &q) + || !EV_GET_UINT32(ev, &offsetL, &p, &q) + || !EV_GET_UINT64(ev, &nbytes, &p, &q) + || !EV_GET_UINT32(ev, &hd_len, &p, &q) + || !EV_GET_UINT32(ev, &tl_len, &p, &q)) { + /* Buffer has wrong length to contain all the needed values */ + reply_posix_error(desc, EINVAL); + goto done; + } + + if (hd_len != 0 || tl_len != 0 || flags != 0) { + // We do not allow header, trailers and/or flags right now + reply_posix_error(desc, EINVAL); + goto done; + } + + d = EF_SAFE_ALLOC(sizeof(struct t_data)); + d->fd = desc->fd; + d->command = command; + d->invoke = invoke_sendfile; + d->free = free_sendfile; + d->level = 2; + + d->c.sendfile.out_fd = (int) out_fd; + d->c.sendfile.written = 0; + d->c.sendfile.port = desc->port; + d->c.sendfile.q_mtx = desc->q_mtx; + + #if SIZEOF_OFF_T == 4 + if (offsetH != 0) { + reply_posix_error(desc, EINVAL); + goto done; + } + d->c.sendfile.offset = (off_t) offsetL; + #else + d->c.sendfile.offset = ((off_t) offsetH << 32) | offsetL; + #endif + + d->c.sendfile.nbytes = nbytes; + + if (USE_THRDS_FOR_SENDFILE) { + SET_BLOCKING(d->c.sendfile.out_fd); + } else { + /** + * Write a place holder to queue in order to force file_flush + * to be called before the driver is closed. + */ + char tmp[1] = ""; + MUTEX_LOCK(d->c.sendfile.q_mtx); + if (driver_enq(d->c.sendfile.port, tmp, 1)) { + MUTEX_UNLOCK(d->c.sendfile.q_mtx); + reply_posix_error(desc, ENOMEM); + goto done; + } + MUTEX_UNLOCK(d->c.sendfile.q_mtx); + } + + cq_enq(desc, d); +#else + reply_posix_error(desc, ENOTSUP); +#endif + goto done; + } /* case FILE_SENDFILE: */ + } /* switch(command) */ - - if (lseek_flush_read(desc, &err) < 0) { + + if (lseek_flush_read(desc, &err +#ifdef USE_VM_PROBES + , dt_priv, dt_utag +#endif + ) < 0) { reply_posix_error(desc, err); goto done; } - if (flush_write_check_error(desc, &err) < 0) { + if (flush_write_check_error(desc, &err +#ifdef USE_VM_PROBES + , dt_priv, dt_utag +#endif + ) < 0) { reply_posix_error(desc, err); goto done; } else { @@ -3238,5 +4103,50 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { } done: + if (d != NULL) { +#ifdef USE_VM_PROBES + /* + * If d == NULL, then either: + * 1). There was an error of some sort, or + * 2). The command given to us is actually implemented + * by file_output() instead. + * + * Case #1 is probably a TODO item, perhaps? + * Case #2 we definitely don't want to activate a probe. + */ + d->sched_i1 = dt_priv->thread_num; + d->sched_i2 = dt_priv->tag; + d->sched_utag[0] = '\0'; + if (dt_utag != NULL) { + if (dt_utag[0] == '\0') { + dt_utag = NULL; + } else { + strncpy(d->sched_utag, dt_utag, sizeof(d->sched_utag) - 1); + d->sched_utag[sizeof(d->sched_utag) - 1] = '\0'; + } + } + DTRACE11(efile_drv_entry, dt_priv->thread_num, dt_priv->tag++, + dt_utag, command, dt_s1, NULL, dt_i1, dt_i2, dt_i3, dt_i4, + desc->port_str); +#endif + } cq_execute(desc); } + +#ifdef USE_VM_PROBES +dt_private * +get_dt_private(int base) +{ + dt_private *dt_priv = (dt_private *) pthread_getspecific(dt_driver_key); + + if (dt_priv == NULL) { + dt_priv = EF_SAFE_ALLOC(sizeof(dt_private)); + erts_mtx_lock(&dt_driver_mutex); + dt_priv->thread_num = (base + dt_driver_idnum++); + erts_mtx_unlock(&dt_driver_mutex); + dt_priv->tag = 0; + pthread_setspecific(dt_driver_key, dt_priv); + } + return dt_priv; +} +#endif /* USE_VM_PROBES */ |