aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/drivers/common/efile_drv.c
diff options
context:
space:
mode:
Diffstat (limited to 'erts/emulator/drivers/common/efile_drv.c')
-rw-r--r--erts/emulator/drivers/common/efile_drv.c1350
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 */