From eb77d69be528580ca6e4c109762b862114beca87 Mon Sep 17 00:00:00 2001 From: Csaba Henk Date: Mon, 22 Oct 2018 00:59:05 +0200 Subject: fuse: SETLKW interrupt Use the (f)getxattr based clearlocks interface to interrupt a pending lock request. updates: #465 Change-Id: I4e91a4d8791fc688fed400a02de4c53487e61be2 Signed-off-by: Csaba Henk --- tests/features/flock_interrupt.t | 33 +++++++++ xlators/mount/fuse/src/fuse-bridge.c | 130 +++++++++++++++++++++++++++++++++++ 2 files changed, 163 insertions(+) create mode 100644 tests/features/flock_interrupt.t diff --git a/tests/features/flock_interrupt.t b/tests/features/flock_interrupt.t new file mode 100644 index 00000000000..8603b656c24 --- /dev/null +++ b/tests/features/flock_interrupt.t @@ -0,0 +1,33 @@ +#!/bin/bash + +. $(dirname $0)/../include.rc +. $(dirname $0)/../volume.rc + +cleanup; + +## Start and create a volume +TEST glusterd; +TEST pidof glusterd; + +TEST $CLI volume create $V0 $H0:$B0/${V0}0; + +## Verify volume is is created +EXPECT "$V0" volinfo_field $V0 'Volume Name'; +EXPECT 'Created' volinfo_field $V0 'Status'; + +## Start volume and verify +TEST $CLI volume start $V0; +EXPECT 'Started' volinfo_field $V0 'Status'; + +TEST $GFS --volfile-id=$V0 --volfile-server=$H0 $M0; +TEST touch $M0/testfile; + +function flock_interrupt { + flock $MO/testfile sleep 3 & flock -w 1 $M0/testfile true; + echo ok; +} + +EXPECT_WITHIN 2 ok flock_interrupt; + +## Finish up +cleanup; diff --git a/xlators/mount/fuse/src/fuse-bridge.c b/xlators/mount/fuse/src/fuse-bridge.c index 5bc070658e2..633398a3108 100644 --- a/xlators/mount/fuse/src/fuse-bridge.c +++ b/xlators/mount/fuse/src/fuse-bridge.c @@ -4557,6 +4557,17 @@ fuse_setlk_cbk(call_frame_t *frame, void *cookie, xlator_t *this, { uint32_t op = 0; fuse_state_t *state = NULL; + int ret = 0; + + ret = fuse_interrupt_finish_fop(frame, this, _gf_false, (void **)&state); + if (state) { + GF_FREE(state->name); + dict_unref(state->xdata); + GF_FREE(state); + } + if (ret) { + return 0; + } state = frame->root->state; op = state->finh->opcode; @@ -4603,9 +4614,128 @@ fuse_setlk_cbk(call_frame_t *frame, void *cookie, xlator_t *this, return 0; } +static int +fuse_setlk_interrupt_handler_cbk(call_frame_t *frame, void *cookie, + xlator_t *this, int32_t op_ret, + int32_t op_errno, dict_t *dict, dict_t *xdata) +{ + fuse_interrupt_state_t intstat = INTERRUPT_NONE; + fuse_interrupt_record_t *fir; + fuse_state_t *state = NULL; + int ret = 0; + + ret = dict_get_bin(xdata, "fuse-interrupt-record", (void **)&fir); + if (ret < 0) { + gf_log("glusterfs-fuse", GF_LOG_ERROR, "interrupt record not found"); + + goto out; + } + + intstat = op_ret >= 0 ? INTERRUPT_HANDLED : INTERRUPT_SQUELCHED; + + fuse_interrupt_finish_interrupt(this, fir, intstat, _gf_false, + (void **)&state); + if (state) { + GF_FREE(state->name); + dict_unref(state->xdata); + GF_FREE(state); + } + +out: + STACK_DESTROY(frame->root); + + return 0; +} + +static void +fuse_setlk_interrupt_handler(xlator_t *this, fuse_interrupt_record_t *fir) +{ + fuse_state_t *state = NULL; + call_frame_t *frame = NULL; + char *xattr_name = NULL; + int ret = 0; + + gf_log("glusterfs-fuse", GF_LOG_DEBUG, + "SETLK%s unique %" PRIu64 ": interrupt handler triggered", + fir->fuse_in_header.opcode == FUSE_SETLK ? "" : "W", + fir->fuse_in_header.unique); + + state = fir->data; + + ret = gf_asprintf( + &xattr_name, GF_XATTR_CLRLK_CMD ".tposix.kblocked.%hd,%jd-%jd", + state->lk_lock.l_whence, state->lk_lock.l_start, state->lk_lock.l_len); + if (ret == -1) { + xattr_name = NULL; + goto err; + } + + frame = get_call_frame_for_req(state); + if (!frame) { + goto err; + } + frame->root->state = state; + frame->root->op = GF_FOP_GETXATTR; + frame->op = GF_FOP_GETXATTR; + state->name = xattr_name; + + STACK_WIND(frame, fuse_setlk_interrupt_handler_cbk, state->active_subvol, + state->active_subvol->fops->fgetxattr, state->fd, xattr_name, + state->xdata); + + return; + +err: + GF_FREE(xattr_name); + fuse_interrupt_finish_interrupt(this, fir, INTERRUPT_SQUELCHED, _gf_false, + (void **)&state); + if (state) { + dict_unref(state->xdata); + GF_FREE(state); + } +} + void fuse_setlk_resume(fuse_state_t *state) { + fuse_interrupt_record_t *fir = NULL; + fuse_state_t *state_clone = NULL; + + fir = fuse_interrupt_record_new(state->finh, fuse_setlk_interrupt_handler); + state_clone = gf_memdup(state, sizeof(*state)); + if (state_clone) { + /* + * Calling this allocator with fir casted to (char *) seems like + * an abuse of this API, but in fact the API is stupid to assume + * a (char *) argument (in the funcion it's casted to (void *) + * anyway). + */ + state_clone->xdata = dict_for_key_value( + "fuse-interrupt-record", (char *)fir, sizeof(*fir), _gf_true); + } + if (!fir || !state_clone || !state_clone->xdata) { + if (fir) { + GF_FREE(fir); + } + if (state_clone) { + GF_FREE(state_clone); + } + send_fuse_err(state->this, state->finh, ENOMEM); + + gf_log("glusterfs-fuse", GF_LOG_ERROR, + "SETLK%s unique %" PRIu64 + ":" + " interrupt record allocation failed", + state->finh->opcode == FUSE_SETLK ? "" : "W", + state->finh->unique); + free_fuse_state(state); + + return; + } + state_clone->name = NULL; + fir->data = state_clone; + fuse_interrupt_record_insert(state->this, fir); + gf_log("glusterfs-fuse", GF_LOG_TRACE, "%" PRIu64 ": SETLK%s %p", state->finh->unique, state->finh->opcode == FUSE_SETLK ? "" : "W", state->fd); -- cgit