From ee22651f34b467859959b17b881da465001c6bc8 Mon Sep 17 00:00:00 2001 From: Raghavendra G Date: Thu, 11 Jun 2009 04:58:30 +0000 Subject: libglusterfsclient: implement glusterfs_sendfile. Signed-off-by: Anand V. Avati --- libglusterfsclient/src/libglusterfsclient.c | 145 ++++++++++++++++++++++++++++ libglusterfsclient/src/libglusterfsclient.h | 26 +++++ 2 files changed, 171 insertions(+) (limited to 'libglusterfsclient') diff --git a/libglusterfsclient/src/libglusterfsclient.c b/libglusterfsclient/src/libglusterfsclient.c index 8dc63a6eb64..bfde21c6127 100755 --- a/libglusterfsclient/src/libglusterfsclient.c +++ b/libglusterfsclient/src/libglusterfsclient.c @@ -53,6 +53,7 @@ #define LIBGF_XL_NAME "libglusterfsclient" #define LIBGLUSTERFS_INODE_TABLE_LRU_LIMIT 1000 //14057 +#define LIBGF_SENDFILE_BLOCK_SIZE 4096 #define LIBGF_READDIR_BLOCK 4096 static inline xlator_t * @@ -6683,6 +6684,150 @@ out: return off; } + +struct libgf_client_sendfile_data { + int reads_sent; + int reads_completed; + int out_fd; + int32_t op_ret; + int32_t op_errno; + pthread_mutex_t lock; + pthread_cond_t cond; +}; + +int +libgf_client_sendfile_read_cbk (int op_ret, int op_errno, + glusterfs_iobuf_t *buf, void *cbk_data) +{ + struct libgf_client_sendfile_data *sendfile_data = cbk_data; + int bytes = 0; + + if (op_ret > 0) { + bytes = writev (sendfile_data->out_fd, buf->vector, buf->count); + if (bytes != op_ret) { + op_ret = -1; + op_errno = errno; + } + + glusterfs_free (buf); + } + + pthread_mutex_lock (&sendfile_data->lock); + { + if (sendfile_data->op_ret != -1) { + if (op_ret == -1) { + sendfile_data->op_ret = -1; + sendfile_data->op_errno = op_errno; + } else { + sendfile_data->op_ret += op_ret; + } + } + + sendfile_data->reads_completed++; + + if (sendfile_data->reads_completed + == sendfile_data->reads_sent) { + pthread_cond_broadcast (&sendfile_data->cond); + } + } + pthread_mutex_unlock (&sendfile_data->lock); + + return 0; +} + + +ssize_t +glusterfs_sendfile (int out_fd, glusterfs_file_t in_fd, off_t *offset, + size_t count) +{ + ssize_t ret = -1; + struct libgf_client_sendfile_data cbk_data = {0, }; + off_t off = -1; + size_t size = 0; + int flags = 0; + int non_block = 0; + + + pthread_mutex_init (&cbk_data.lock, NULL); + pthread_cond_init (&cbk_data.cond, NULL); + cbk_data.out_fd = out_fd; + + if (offset) { + off = *offset; + } + + flags = fcntl (out_fd, F_GETFL); + + if (flags != -1) { + non_block = flags & O_NONBLOCK; + + if (non_block) { + ret = fcntl (out_fd, F_SETFL, flags & ~O_NONBLOCK); + } + } + + while (count != 0) { + /* + * FIXME: what's the optimal size for reads and writes? + */ + size = (count > LIBGF_SENDFILE_BLOCK_SIZE) ? + LIBGF_SENDFILE_BLOCK_SIZE : count; + + /* + * we don't wait for reply to previous read, we just send all + * reads in a single go. + */ + ret = glusterfs_read_async (in_fd, size, off, + libgf_client_sendfile_read_cbk, + &cbk_data); + if (ret == -1) { + break; + } + + pthread_mutex_lock (&cbk_data.lock); + { + cbk_data.reads_sent++; + } + pthread_mutex_unlock (&cbk_data.lock); + + if (offset) { + off += size; + } + + count -= size; + } + + pthread_mutex_lock (&cbk_data.lock); + { + /* + * if we've not received replies to all the reads we've sent, + * wait for them + */ + if (cbk_data.reads_sent > cbk_data.reads_completed) { + pthread_cond_wait (&cbk_data.cond, + &cbk_data.lock); + } + } + pthread_mutex_unlock (&cbk_data.lock); + + if (offset != NULL) { + *offset = off; + } + + /* if we were able to stack_wind all the reads */ + + if (ret == 0) { + ret = cbk_data.op_ret; + errno = cbk_data.op_errno; + } + + if (non_block) { + fcntl (out_fd, F_SETFL, flags); + } + + return ret; +} + static struct xlator_fops libgf_client_fops = { }; diff --git a/libglusterfsclient/src/libglusterfsclient.h b/libglusterfsclient/src/libglusterfsclient.h index 9e03a8c59ac..e8b19f4bc51 100755 --- a/libglusterfsclient/src/libglusterfsclient.h +++ b/libglusterfsclient/src/libglusterfsclient.h @@ -1234,6 +1234,32 @@ glusterfs_seekdir (glusterfs_dir_t dirfd, off_t offset); */ off_t glusterfs_telldir (glusterfs_dir_t dirfd); + + +/* Write count bytes from in_fd to out_fd, starting at *offset. + * glusterfs_sendfile aims at eliminating memory copy at the end of + * each read from in_fd, copying the file directly to out_fd from the buffer + * provided by glusterfs. + * + * @out_fd: file descriptor opened for writing + * + * @in_fd: glusterfs file handle to the file to be read from. + * + * @offset: If offset is not NULL, then it points to a variable holding the file + * offset from which glusterfs_sendfile() will start reading data + * from in_fd. When glusterfs_sendfile() returns, this variable will + * be set to the offset of the byte following the last byte that was + * read. If offset is not NULL, then glusterfs_sendfile() does not + * modify the current file offset of in_fd; otherwise the current file + * offset is adjusted to reflect the number of bytes read from in_fd. + * + * @count: number of bytes to copy between the file descriptors. + */ + +ssize_t +glusterfs_sendfile (int out_fd, glusterfs_file_t in_fd, off_t *offset, + size_t count); + /* FIXME: review the need for these apis */ /* added for log related initialization in booster fork implementation */ void -- cgit