From 78ae7215614a7717d2cf838afefb1525fbb70602 Mon Sep 17 00:00:00 2001 From: Anand Avati Date: Tue, 5 Feb 2013 16:04:50 -0800 Subject: fuse-bridge: use READDIRPLUS support when available This patch makes use of READDIRPLUS call when support is available in the kernel. Change-Id: Iac78881179567856b55af1f46594a2b2859309f0 BUG: 908128 Signed-off-by: Anand V. Avati Reviewed-on: http://review.gluster.org/3905 Tested-by: Gluster Build System Reviewed-by: Amar Tumballi Reviewed-by: Brian Foster --- contrib/fuse-include/fuse_kernel.h | 12 +++ xlators/mount/fuse/src/fuse-bridge.c | 137 ++++++++++++++++++++++++++++++++++ xlators/mount/fuse/src/fuse-bridge.h | 2 +- xlators/mount/fuse/src/fuse-helpers.c | 4 +- 4 files changed, 153 insertions(+), 2 deletions(-) diff --git a/contrib/fuse-include/fuse_kernel.h b/contrib/fuse-include/fuse_kernel.h index e6fdcf809..c6396555c 100644 --- a/contrib/fuse-include/fuse_kernel.h +++ b/contrib/fuse-include/fuse_kernel.h @@ -161,6 +161,7 @@ struct fuse_file_lock { #define FUSE_EXPORT_SUPPORT (1 << 4) #define FUSE_BIG_WRITES (1 << 5) #define FUSE_DONT_MASK (1 << 6) +#define FUSE_DO_READDIRPLUS (1 << 13) /** * CUSE INIT request/reply flags @@ -260,6 +261,7 @@ enum fuse_opcode { FUSE_IOCTL = 39, FUSE_POLL = 40, + FUSE_READDIRPLUS = 44, /* CUSE specific operations */ CUSE_INIT = 4096, }; @@ -564,6 +566,16 @@ struct fuse_dirent { #define FUSE_DIRENT_SIZE(d) \ FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen) +struct fuse_direntplus { + struct fuse_entry_out entry_out; + struct fuse_dirent dirent; +}; + +#define FUSE_NAME_OFFSET_DIRENTPLUS \ + offsetof(struct fuse_direntplus, dirent.name) +#define FUSE_DIRENTPLUS_SIZE(d) \ + FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET_DIRENTPLUS + (d)->dirent.namelen) + struct fuse_notify_inval_inode_out { __u64 ino; __s64 off; diff --git a/xlators/mount/fuse/src/fuse-bridge.c b/xlators/mount/fuse/src/fuse-bridge.c index ede936acd..4ffaf59eb 100644 --- a/xlators/mount/fuse/src/fuse-bridge.c +++ b/xlators/mount/fuse/src/fuse-bridge.c @@ -2521,6 +2521,139 @@ fuse_readdir (xlator_t *this, fuse_in_header_t *finh, void *msg) fuse_resolve_and_resume (state, fuse_readdir_resume); } + +static int +fuse_readdirp_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, gf_dirent_t *entries, + dict_t *xdata) +{ + fuse_state_t *state = NULL; + fuse_in_header_t *finh = NULL; + int size = 0; + char *buf = NULL; + gf_dirent_t *entry = NULL; + struct fuse_direntplus *fde = NULL; + struct fuse_entry_out *feo = NULL; + fuse_private_t *priv = NULL; + + state = frame->root->state; + finh = state->finh; + priv = this->private; + + if (op_ret < 0) { + gf_log ("glusterfs-fuse", GF_LOG_WARNING, + "%"PRIu64": READDIRP => -1 (%s)", frame->root->unique, + strerror (op_errno)); + + send_fuse_err (this, finh, op_errno); + goto out; + } + + gf_log ("glusterfs-fuse", GF_LOG_TRACE, + "%"PRIu64": READDIRP => %d/%"GF_PRI_SIZET",%"PRId64, + frame->root->unique, op_ret, state->size, state->off); + + list_for_each_entry (entry, &entries->list, list) { + size += FUSE_DIRENT_ALIGN (FUSE_NAME_OFFSET_DIRENTPLUS + + strlen (entry->d_name)); + } + + if (size <= 0) { + send_fuse_data (this, finh, 0, 0); + goto out; + } + + buf = GF_CALLOC (1, size, gf_fuse_mt_char); + if (!buf) { + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRIu64": READDIRP => -1 (%s)", frame->root->unique, + strerror (ENOMEM)); + send_fuse_err (this, finh, ENOMEM); + goto out; + } + + size = 0; + list_for_each_entry (entry, &entries->list, list) { + inode_t *linked_inode; + + fde = (struct fuse_direntplus *)(buf + size); + feo = &fde->entry_out; + fde->dirent.ino = entry->d_ino; + fde->dirent.off = entry->d_off; + fde->dirent.type = entry->d_type; + fde->dirent.namelen = strlen (entry->d_name); + strncpy (fde->dirent.name, entry->d_name, fde->dirent.namelen); + size += FUSE_DIRENTPLUS_SIZE (fde); + + if (!entry->inode) + continue; + + entry->d_stat.ia_blksize = this->ctx->page_size; + gf_fuse_stat2attr (&entry->d_stat, &feo->attr, priv->enable_ino32); + + linked_inode = inode_link (entry->inode, state->fd->inode, + entry->d_name, &entry->d_stat); + if (!linked_inode) + continue; + + inode_lookup (linked_inode); + + feo->nodeid = inode_to_fuse_nodeid (linked_inode); + + inode_unref (linked_inode); + + feo->entry_valid = + calc_timeout_sec (priv->entry_timeout); + feo->entry_valid_nsec = + calc_timeout_nsec (priv->entry_timeout); + feo->attr_valid = + calc_timeout_sec (priv->attribute_timeout); + feo->attr_valid_nsec = + calc_timeout_nsec (priv->attribute_timeout); + } + + send_fuse_data (this, finh, buf, size); +out: + free_fuse_state (state); + STACK_DESTROY (frame->root); + GF_FREE (buf); + return 0; + +} + + +void +fuse_readdirp_resume (fuse_state_t *state) +{ + gf_log ("glusterfs-fuse", GF_LOG_TRACE, + "%"PRIu64": READDIRP (%p, size=%zu, offset=%"PRId64")", + state->finh->unique, state->fd, state->size, state->off); + + FUSE_FOP (state, fuse_readdirp_cbk, GF_FOP_READDIRP, + readdirp, state->fd, state->size, state->off, state->xdata); +} + + +static void +fuse_readdirp (xlator_t *this, fuse_in_header_t *finh, void *msg) +{ + struct fuse_read_in *fri = msg; + + fuse_state_t *state = NULL; + fd_t *fd = NULL; + + GET_STATE (this, finh, state); + state->size = fri->size; + state->off = fri->offset; + fd = FH_TO_FD (fri->fh); + state->fd = fd; + + fuse_resolve_fd_init (state, &state->resolve, fd); + + fuse_resolve_and_resume (state, fuse_readdirp_resume); +} + + static void fuse_releasedir (xlator_t *this, fuse_in_header_t *finh, void *msg) { @@ -3535,6 +3668,9 @@ fuse_init (xlator_t *this, fuse_in_header_t *finh, void *msg) if (fini->minor < 9) *priv->msg0_len_p = sizeof(*finh) + FUSE_COMPAT_WRITE_IN_SIZE; #endif + if (fini->flags & FUSE_DO_READDIRPLUS) + fino.flags |= FUSE_DO_READDIRPLUS; + ret = send_fuse_obj (this, finh, &fino); if (ret == 0) gf_log ("glusterfs-fuse", GF_LOG_INFO, @@ -4723,6 +4859,7 @@ static fuse_handler_t *fuse_std_ops[FUSE_OP_HIGH] = { /* [FUSE_NOTIFY_REPLY] */ /* [FUSE_BATCH_FORGET] */ /* [FUSE_FALLOCATE] */ + [FUSE_READDIRPLUS] = fuse_readdirp, }; diff --git a/xlators/mount/fuse/src/fuse-bridge.h b/xlators/mount/fuse/src/fuse-bridge.h index 37d29df6b..d90b85e72 100644 --- a/xlators/mount/fuse/src/fuse-bridge.h +++ b/xlators/mount/fuse/src/fuse-bridge.h @@ -48,7 +48,7 @@ #include "gidcache.h" #if defined(GF_LINUX_HOST_OS) || defined(__NetBSD__) -#define FUSE_OP_HIGH (FUSE_POLL + 1) +#define FUSE_OP_HIGH (FUSE_READDIRPLUS + 1) #endif #ifdef GF_DARWIN_HOST_OS #define FUSE_OP_HIGH (FUSE_DESTROY + 1) diff --git a/xlators/mount/fuse/src/fuse-helpers.c b/xlators/mount/fuse/src/fuse-helpers.c index e72669a9e..d4dcc2e61 100644 --- a/xlators/mount/fuse/src/fuse-helpers.c +++ b/xlators/mount/fuse/src/fuse-helpers.c @@ -339,7 +339,9 @@ fuse_ino_to_inode (uint64_t ino, xlator_t *fuse) uint64_t inode_to_fuse_nodeid (inode_t *inode) { - if (!inode || __is_root_gfid (inode->gfid)) + if (!inode) + return 0; + if (__is_root_gfid (inode->gfid)) return 1; return (unsigned long) inode; -- cgit