Changeset View
Standalone View
sys/fs/fuse/fuse_ipc.c
Show All 27 Lines | |||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
* | * | ||||
* Copyright (C) 2005 Csaba Henk. | * Copyright (C) 2005 Csaba Henk. | ||||
* All rights reserved. | * All rights reserved. | ||||
* | * | ||||
* Copyright (c) 2019 The FreeBSD Foundation | |||||
* | |||||
* Portions of this software were developed by BFF Storage Systems, LLC under | |||||
* sponsorship from the FreeBSD Foundation. | |||||
* | |||||
* Redistribution and use in source and binary forms, with or without | * Redistribution and use in source and binary forms, with or without | ||||
* modification, are permitted provided that the following conditions | * modification, are permitted provided that the following conditions | ||||
* are met: | * are met: | ||||
* 1. Redistributions of source code must retain the above copyright | * 1. Redistributions of source code must retain the above copyright | ||||
* notice, this list of conditions and the following disclaimer. | * notice, this list of conditions and the following disclaimer. | ||||
* 2. Redistributions in binary form must reproduce the above copyright | * 2. Redistributions in binary form must reproduce the above copyright | ||||
* notice, this list of conditions and the following disclaimer in the | * notice, this list of conditions and the following disclaimer in the | ||||
* documentation and/or other materials provided with the distribution. | * documentation and/or other materials provided with the distribution. | ||||
Show All 12 Lines | |||||
*/ | */ | ||||
#include <sys/cdefs.h> | #include <sys/cdefs.h> | ||||
__FBSDID("$FreeBSD$"); | __FBSDID("$FreeBSD$"); | ||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/module.h> | #include <sys/module.h> | ||||
#include <sys/systm.h> | #include <sys/systm.h> | ||||
#include <sys/counter.h> | |||||
#include <sys/errno.h> | #include <sys/errno.h> | ||||
#include <sys/kernel.h> | #include <sys/kernel.h> | ||||
#include <sys/conf.h> | #include <sys/conf.h> | ||||
#include <sys/uio.h> | #include <sys/uio.h> | ||||
#include <sys/malloc.h> | #include <sys/malloc.h> | ||||
#include <sys/queue.h> | #include <sys/queue.h> | ||||
#include <sys/lock.h> | #include <sys/lock.h> | ||||
#include <sys/sx.h> | #include <sys/sx.h> | ||||
#include <sys/mutex.h> | #include <sys/mutex.h> | ||||
#include <sys/proc.h> | #include <sys/proc.h> | ||||
#include <sys/mount.h> | #include <sys/mount.h> | ||||
#include <sys/sdt.h> | #include <sys/sdt.h> | ||||
#include <sys/vnode.h> | #include <sys/vnode.h> | ||||
#include <sys/signalvar.h> | #include <sys/signalvar.h> | ||||
#include <sys/syscallsubr.h> | #include <sys/syscallsubr.h> | ||||
#include <sys/sysctl.h> | #include <sys/sysctl.h> | ||||
#include <vm/uma.h> | #include <vm/uma.h> | ||||
#include "fuse.h" | #include "fuse.h" | ||||
#include "fuse_node.h" | #include "fuse_node.h" | ||||
#include "fuse_ipc.h" | #include "fuse_ipc.h" | ||||
#include "fuse_internal.h" | #include "fuse_internal.h" | ||||
SDT_PROVIDER_DECLARE(fuse); | SDT_PROVIDER_DECLARE(fusefs); | ||||
/* | /* | ||||
* Fuse trace probe: | * Fuse trace probe: | ||||
* arg0: verbosity. Higher numbers give more verbose messages | * arg0: verbosity. Higher numbers give more verbose messages | ||||
* arg1: Textual message | * arg1: Textual message | ||||
*/ | */ | ||||
SDT_PROBE_DEFINE2(fuse, , ipc, trace, "int", "char*"); | SDT_PROBE_DEFINE2(fusefs, , ipc, trace, "int", "char*"); | ||||
static void fdisp_make_pid(struct fuse_dispatcher *fdip, enum fuse_opcode op, | |||||
struct fuse_data *data, uint64_t nid, pid_t pid, struct ucred *cred); | |||||
static void fuse_interrupt_send(struct fuse_ticket *otick, int err); | |||||
static struct fuse_ticket *fticket_alloc(struct fuse_data *data); | static struct fuse_ticket *fticket_alloc(struct fuse_data *data); | ||||
static void fticket_refresh(struct fuse_ticket *ftick); | static void fticket_refresh(struct fuse_ticket *ftick); | ||||
static void fticket_destroy(struct fuse_ticket *ftick); | static void fticket_destroy(struct fuse_ticket *ftick); | ||||
static int fticket_wait_answer(struct fuse_ticket *ftick); | static int fticket_wait_answer(struct fuse_ticket *ftick); | ||||
static inline int | static inline int | ||||
fticket_aw_pull_uio(struct fuse_ticket *ftick, | fticket_aw_pull_uio(struct fuse_ticket *ftick, | ||||
struct uio *uio); | struct uio *uio); | ||||
static int fuse_body_audit(struct fuse_ticket *ftick, size_t blen); | static int fuse_body_audit(struct fuse_ticket *ftick, size_t blen); | ||||
static fuse_handler_t fuse_standard_handler; | static fuse_handler_t fuse_standard_handler; | ||||
SYSCTL_NODE(_vfs, OID_AUTO, fusefs, CTLFLAG_RW, 0, "FUSE tunables"); | static counter_u64_t fuse_ticket_count; | ||||
SYSCTL_STRING(_vfs_fusefs, OID_AUTO, version, CTLFLAG_RD, | SYSCTL_COUNTER_U64(_vfs_fusefs_stats, OID_AUTO, ticket_count, CTLFLAG_RD, | ||||
FUSE_FREEBSD_VERSION, 0, "fuse-freebsd version"); | &fuse_ticket_count, "Number of allocated tickets"); | ||||
static int fuse_ticket_count = 0; | |||||
SYSCTL_INT(_vfs_fusefs, OID_AUTO, ticket_count, CTLFLAG_RW, | |||||
&fuse_ticket_count, 0, "number of allocated tickets"); | |||||
static long fuse_iov_permanent_bufsize = 1 << 19; | static long fuse_iov_permanent_bufsize = 1 << 19; | ||||
SYSCTL_LONG(_vfs_fusefs, OID_AUTO, iov_permanent_bufsize, CTLFLAG_RW, | SYSCTL_LONG(_vfs_fusefs, OID_AUTO, iov_permanent_bufsize, CTLFLAG_RW, | ||||
&fuse_iov_permanent_bufsize, 0, | &fuse_iov_permanent_bufsize, 0, | ||||
"limit for permanently stored buffer size for fuse_iovs"); | "limit for permanently stored buffer size for fuse_iovs"); | ||||
static int fuse_iov_credit = 16; | static int fuse_iov_credit = 16; | ||||
SYSCTL_INT(_vfs_fusefs, OID_AUTO, iov_credit, CTLFLAG_RW, | SYSCTL_INT(_vfs_fusefs, OID_AUTO, iov_credit, CTLFLAG_RW, | ||||
&fuse_iov_credit, 0, | &fuse_iov_credit, 0, | ||||
"how many times is an oversized fuse_iov tolerated"); | "how many times is an oversized fuse_iov tolerated"); | ||||
MALLOC_DEFINE(M_FUSEMSG, "fuse_msgbuf", "fuse message buffer"); | MALLOC_DEFINE(M_FUSEMSG, "fuse_msgbuf", "fuse message buffer"); | ||||
static uma_zone_t ticket_zone; | static uma_zone_t ticket_zone; | ||||
static void | /* | ||||
fuse_block_sigs(sigset_t *oldset) | * TODO: figure out how to timeout INTERRUPT requests, because the daemon may | ||||
* leagally never respond | |||||
*/ | |||||
static int | |||||
fuse_interrupt_callback(struct fuse_ticket *tick, struct uio *uio) | |||||
{ | { | ||||
sigset_t newset; | struct fuse_ticket *otick, *x_tick; | ||||
struct fuse_interrupt_in *fii; | |||||
struct fuse_data *data = tick->tk_data; | |||||
bool found = false; | |||||
SIGFILLSET(newset); | fii = (struct fuse_interrupt_in*)((char*)tick->tk_ms_fiov.base + | ||||
SIGDELSET(newset, SIGKILL); | sizeof(struct fuse_in_header)); | ||||
if (kern_sigprocmask(curthread, SIG_BLOCK, &newset, oldset, 0)) | |||||
panic("%s: Invalid operation for kern_sigprocmask()", | fuse_lck_mtx_lock(data->aw_mtx); | ||||
__func__); | TAILQ_FOREACH_SAFE(otick, &data->aw_head, tk_aw_link, x_tick) { | ||||
if (otick->tk_unique == fii->unique) { | |||||
found = true; | |||||
break; | |||||
} | } | ||||
} | |||||
fuse_lck_mtx_unlock(data->aw_mtx); | |||||
static void | if (!found) { | ||||
fuse_restore_sigs(sigset_t *oldset) | /* Original is already complete. Just return */ | ||||
return 0; | |||||
} | |||||
/* Clear the original ticket's interrupt association */ | |||||
otick->irq_unique = 0; | |||||
if (tick->tk_aw_ohead.error == ENOSYS) { | |||||
fsess_set_notimpl(data->mp, FUSE_INTERRUPT); | |||||
return 0; | |||||
} else if (tick->tk_aw_ohead.error == EAGAIN) { | |||||
/* | |||||
* There are two reasons we might get this: | |||||
* 1) the daemon received the INTERRUPT request before the | |||||
* original, or | |||||
* 2) the daemon received the INTERRUPT request after it | |||||
* completed the original request. | |||||
* In the first case we should re-send the INTERRUPT. In the | |||||
* second, we should ignore it. | |||||
*/ | |||||
/* Resend */ | |||||
fuse_interrupt_send(otick, EINTR); | |||||
return 0; | |||||
} else { | |||||
/* Illegal FUSE_INTERRUPT response */ | |||||
return EINVAL; | |||||
} | |||||
} | |||||
/* Interrupt the operation otick. Return err as its error code */ | |||||
void | |||||
fuse_interrupt_send(struct fuse_ticket *otick, int err) | |||||
{ | { | ||||
struct fuse_dispatcher fdi; | |||||
struct fuse_interrupt_in *fii; | |||||
struct fuse_in_header *ftick_hdr; | |||||
struct fuse_data *data = otick->tk_data; | |||||
struct fuse_ticket *tick, *xtick; | |||||
struct ucred reused_creds; | |||||
gid_t reused_groups[1]; | |||||
if (kern_sigprocmask(curthread, SIG_SETMASK, oldset, NULL, 0)) | if (otick->irq_unique == 0) { | ||||
panic("%s: Invalid operation for kern_sigprocmask()", | /* | ||||
__func__); | * If the daemon hasn't yet received otick, then we can answer | ||||
* it ourselves and return. | |||||
*/ | |||||
fuse_lck_mtx_lock(data->ms_mtx); | |||||
STAILQ_FOREACH_SAFE(tick, &otick->tk_data->ms_head, tk_ms_link, | |||||
xtick) { | |||||
if (tick == otick) { | |||||
STAILQ_REMOVE(&otick->tk_data->ms_head, tick, | |||||
fuse_ticket, tk_ms_link); | |||||
otick->tk_data->ms_count--; | |||||
otick->tk_ms_link.stqe_next = NULL; | |||||
fuse_lck_mtx_unlock(data->ms_mtx); | |||||
fuse_lck_mtx_lock(otick->tk_aw_mtx); | |||||
if (!fticket_answered(otick)) { | |||||
fticket_set_answered(otick); | |||||
otick->tk_aw_errno = err; | |||||
wakeup(otick); | |||||
} | } | ||||
fuse_lck_mtx_unlock(otick->tk_aw_mtx); | |||||
fuse_ticket_drop(tick); | |||||
return; | |||||
} | |||||
} | |||||
fuse_lck_mtx_unlock(data->ms_mtx); | |||||
/* | |||||
* If the fuse daemon doesn't support interrupts, then there's | |||||
* nothing more that we can do | |||||
*/ | |||||
if (!fsess_isimpl(data->mp, FUSE_INTERRUPT)) | |||||
return; | |||||
/* | |||||
* If the fuse daemon has already received otick, then we must | |||||
* send FUSE_INTERRUPT. | |||||
*/ | |||||
ftick_hdr = fticket_in_header(otick); | |||||
reused_creds.cr_uid = ftick_hdr->uid; | |||||
reused_groups[0] = ftick_hdr->gid; | |||||
reused_creds.cr_groups = reused_groups; | |||||
fdisp_init(&fdi, sizeof(*fii)); | |||||
fdisp_make_pid(&fdi, FUSE_INTERRUPT, data, ftick_hdr->nodeid, | |||||
ftick_hdr->pid, &reused_creds); | |||||
fii = fdi.indata; | |||||
fii->unique = otick->tk_unique; | |||||
fuse_insert_callback(fdi.tick, fuse_interrupt_callback); | |||||
otick->irq_unique = fdi.tick->tk_unique; | |||||
/* Interrupt ops should be delivered ASAP */ | |||||
fuse_insert_message(fdi.tick, true); | |||||
fdisp_destroy(&fdi); | |||||
} else { | |||||
/* This ticket has already been interrupted */ | |||||
} | |||||
} | |||||
void | void | ||||
fiov_init(struct fuse_iov *fiov, size_t size) | fiov_init(struct fuse_iov *fiov, size_t size) | ||||
{ | { | ||||
uint32_t msize = FU_AT_LEAST(size); | uint32_t msize = FU_AT_LEAST(size); | ||||
fiov->len = 0; | fiov->len = 0; | ||||
fiov->base = malloc(msize, M_FUSEMSG, M_WAITOK | M_ZERO); | fiov->base = malloc(msize, M_FUSEMSG, M_WAITOK | M_ZERO); | ||||
Show All 19 Lines | if (fiov->allocated_size < size || | ||||
fiov->base = realloc(fiov->base, FU_AT_LEAST(size), M_FUSEMSG, | fiov->base = realloc(fiov->base, FU_AT_LEAST(size), M_FUSEMSG, | ||||
M_WAITOK | M_ZERO); | M_WAITOK | M_ZERO); | ||||
if (!fiov->base) { | if (!fiov->base) { | ||||
panic("FUSE: realloc failed"); | panic("FUSE: realloc failed"); | ||||
} | } | ||||
fiov->allocated_size = FU_AT_LEAST(size); | fiov->allocated_size = FU_AT_LEAST(size); | ||||
fiov->credit = fuse_iov_credit; | fiov->credit = fuse_iov_credit; | ||||
/* Clear data buffer after reallocation */ | |||||
bzero(fiov->base, size); | |||||
} else if (size > fiov->len) { | |||||
/* Clear newly extended portion of data buffer */ | |||||
bzero((char*)fiov->base + fiov->len, size - fiov->len); | |||||
} | } | ||||
fiov->len = size; | fiov->len = size; | ||||
} | } | ||||
/* Resize the fiov if needed, and clear it's buffer */ | |||||
void | void | ||||
fiov_refresh(struct fuse_iov *fiov) | fiov_refresh(struct fuse_iov *fiov) | ||||
{ | { | ||||
bzero(fiov->base, fiov->len); | |||||
fiov_adjust(fiov, 0); | fiov_adjust(fiov, 0); | ||||
} | } | ||||
static int | static int | ||||
fticket_ctor(void *mem, int size, void *arg, int flags) | fticket_ctor(void *mem, int size, void *arg, int flags) | ||||
{ | { | ||||
struct fuse_ticket *ftick = mem; | struct fuse_ticket *ftick = mem; | ||||
struct fuse_data *data = arg; | struct fuse_data *data = arg; | ||||
FUSE_ASSERT_MS_DONE(ftick); | FUSE_ASSERT_MS_DONE(ftick); | ||||
FUSE_ASSERT_AW_DONE(ftick); | FUSE_ASSERT_AW_DONE(ftick); | ||||
ftick->tk_data = data; | ftick->tk_data = data; | ||||
if (ftick->tk_unique != 0) | if (ftick->tk_unique != 0) | ||||
fticket_refresh(ftick); | fticket_refresh(ftick); | ||||
/* May be truncated to 32 bits */ | /* May be truncated to 32 bits */ | ||||
ftick->tk_unique = atomic_fetchadd_long(&data->ticketer, 1); | ftick->tk_unique = atomic_fetchadd_long(&data->ticketer, 1); | ||||
if (ftick->tk_unique == 0) | if (ftick->tk_unique == 0) | ||||
ftick->tk_unique = atomic_fetchadd_long(&data->ticketer, 1); | ftick->tk_unique = atomic_fetchadd_long(&data->ticketer, 1); | ||||
ftick->irq_unique = 0; | |||||
refcount_init(&ftick->tk_refcount, 1); | refcount_init(&ftick->tk_refcount, 1); | ||||
atomic_add_acq_int(&fuse_ticket_count, 1); | counter_u64_add(fuse_ticket_count, 1); | ||||
return 0; | return 0; | ||||
} | } | ||||
static void | static void | ||||
fticket_dtor(void *mem, int size, void *arg) | fticket_dtor(void *mem, int size, void *arg) | ||||
{ | { | ||||
#ifdef INVARIANTS | #ifdef INVARIANTS | ||||
struct fuse_ticket *ftick = mem; | struct fuse_ticket *ftick = mem; | ||||
#endif | #endif | ||||
FUSE_ASSERT_MS_DONE(ftick); | FUSE_ASSERT_MS_DONE(ftick); | ||||
FUSE_ASSERT_AW_DONE(ftick); | FUSE_ASSERT_AW_DONE(ftick); | ||||
atomic_subtract_acq_int(&fuse_ticket_count, 1); | counter_u64_add(fuse_ticket_count, -1); | ||||
} | } | ||||
static int | static int | ||||
fticket_init(void *mem, int size, int flags) | fticket_init(void *mem, int size, int flags) | ||||
{ | { | ||||
struct fuse_ticket *ftick = mem; | struct fuse_ticket *ftick = mem; | ||||
bzero(ftick, sizeof(struct fuse_ticket)); | bzero(ftick, sizeof(struct fuse_ticket)); | ||||
Show All 25 Lines | |||||
} | } | ||||
static inline void | static inline void | ||||
fticket_destroy(struct fuse_ticket *ftick) | fticket_destroy(struct fuse_ticket *ftick) | ||||
{ | { | ||||
return uma_zfree(ticket_zone, ftick); | return uma_zfree(ticket_zone, ftick); | ||||
} | } | ||||
static inline | static inline | ||||
void | void | ||||
fticket_refresh(struct fuse_ticket *ftick) | fticket_refresh(struct fuse_ticket *ftick) | ||||
{ | { | ||||
FUSE_ASSERT_MS_DONE(ftick); | FUSE_ASSERT_MS_DONE(ftick); | ||||
FUSE_ASSERT_AW_DONE(ftick); | FUSE_ASSERT_AW_DONE(ftick); | ||||
fiov_refresh(&ftick->tk_ms_fiov); | fiov_refresh(&ftick->tk_ms_fiov); | ||||
ftick->tk_ms_bufdata = NULL; | ftick->tk_ms_bufdata = NULL; | ||||
ftick->tk_ms_bufsize = 0; | ftick->tk_ms_bufsize = 0; | ||||
ftick->tk_ms_type = FT_M_FIOV; | ftick->tk_ms_type = FT_M_FIOV; | ||||
bzero(&ftick->tk_aw_ohead, sizeof(struct fuse_out_header)); | bzero(&ftick->tk_aw_ohead, sizeof(struct fuse_out_header)); | ||||
fiov_refresh(&ftick->tk_aw_fiov); | fiov_refresh(&ftick->tk_aw_fiov); | ||||
ftick->tk_aw_errno = 0; | ftick->tk_aw_errno = 0; | ||||
ftick->tk_aw_bufdata = NULL; | ftick->tk_aw_bufdata = NULL; | ||||
ftick->tk_aw_bufsize = 0; | ftick->tk_aw_bufsize = 0; | ||||
ftick->tk_aw_type = FT_A_FIOV; | ftick->tk_aw_type = FT_A_FIOV; | ||||
ftick->tk_flag = 0; | ftick->tk_flag = 0; | ||||
} | } | ||||
/* Prepar the ticket to be reused, but don't clear its data buffers */ | |||||
static inline void | |||||
fticket_reset(struct fuse_ticket *ftick) | |||||
{ | |||||
FUSE_ASSERT_MS_DONE(ftick); | |||||
FUSE_ASSERT_AW_DONE(ftick); | |||||
ftick->tk_ms_bufdata = NULL; | |||||
ftick->tk_ms_bufsize = 0; | |||||
ftick->tk_ms_type = FT_M_FIOV; | |||||
bzero(&ftick->tk_aw_ohead, sizeof(struct fuse_out_header)); | |||||
ftick->tk_aw_errno = 0; | |||||
ftick->tk_aw_bufdata = NULL; | |||||
ftick->tk_aw_bufsize = 0; | |||||
ftick->tk_aw_type = FT_A_FIOV; | |||||
ftick->tk_flag = 0; | |||||
} | |||||
static int | static int | ||||
fticket_wait_answer(struct fuse_ticket *ftick) | fticket_wait_answer(struct fuse_ticket *ftick) | ||||
{ | { | ||||
sigset_t tset; | struct thread *td = curthread; | ||||
int err = 0; | sigset_t blockedset, oldset; | ||||
struct fuse_data *data; | int err = 0, stops_deferred; | ||||
struct fuse_data *data = ftick->tk_data; | |||||
bool interrupted = false; | |||||
if (fsess_isimpl(ftick->tk_data->mp, FUSE_INTERRUPT) && | |||||
data->dataflags & FSESS_INTR) { | |||||
SIGEMPTYSET(blockedset); | |||||
} else { | |||||
/* Block all signals except (implicitly) SIGKILL */ | |||||
SIGFILLSET(blockedset); | |||||
} | |||||
stops_deferred = sigdeferstop(SIGDEFERSTOP_SILENT); | |||||
kern_sigprocmask(td, SIG_BLOCK, NULL, &oldset, 0); | |||||
fuse_lck_mtx_lock(ftick->tk_aw_mtx); | fuse_lck_mtx_lock(ftick->tk_aw_mtx); | ||||
retry: | |||||
if (fticket_answered(ftick)) { | if (fticket_answered(ftick)) { | ||||
goto out; | goto out; | ||||
} | } | ||||
data = ftick->tk_data; | |||||
if (fdata_get_dead(data)) { | if (fdata_get_dead(data)) { | ||||
err = ENOTCONN; | err = ENOTCONN; | ||||
fticket_set_answered(ftick); | fticket_set_answered(ftick); | ||||
goto out; | goto out; | ||||
} | } | ||||
fuse_block_sigs(&tset); | kern_sigprocmask(td, SIG_BLOCK, &blockedset, NULL, 0); | ||||
err = msleep(ftick, &ftick->tk_aw_mtx, PCATCH, "fu_ans", | err = msleep(ftick, &ftick->tk_aw_mtx, PCATCH, "fu_ans", | ||||
data->daemon_timeout * hz); | data->daemon_timeout * hz); | ||||
fuse_restore_sigs(&tset); | kern_sigprocmask(td, SIG_SETMASK, &oldset, NULL, 0); | ||||
if (err == EAGAIN) { /* same as EWOULDBLOCK */ | if (err == EWOULDBLOCK) { | ||||
SDT_PROBE2(fusefs, , ipc, trace, 3, | |||||
"fticket_wait_answer: EWOULDBLOCK"); | |||||
#ifdef XXXIP /* die conditionally */ | #ifdef XXXIP /* die conditionally */ | ||||
if (!fdata_get_dead(data)) { | if (!fdata_get_dead(data)) { | ||||
fdata_set_dead(data); | fdata_set_dead(data); | ||||
} | } | ||||
#endif | #endif | ||||
err = ETIMEDOUT; | err = ETIMEDOUT; | ||||
fticket_set_answered(ftick); | fticket_set_answered(ftick); | ||||
} else if ((err == EINTR || err == ERESTART)) { | |||||
/* | |||||
* Whether we get EINTR or ERESTART depends on whether | |||||
* SA_RESTART was set by sigaction(2). | |||||
* | |||||
* Try to interrupt the operation and wait for an EINTR response | |||||
* to the original operation. If the file system does not | |||||
* support FUSE_INTERRUPT, then we'll just wait for it to | |||||
* complete like normal. If it does support FUSE_INTERRUPT, | |||||
* then it will either respond EINTR to the original operation, | |||||
* or EAGAIN to the interrupt. | |||||
*/ | |||||
sigset_t tmpset; | |||||
SDT_PROBE2(fusefs, , ipc, trace, 4, | |||||
"fticket_wait_answer: interrupt"); | |||||
fuse_lck_mtx_unlock(ftick->tk_aw_mtx); | |||||
fuse_interrupt_send(ftick, err); | |||||
PROC_LOCK(td->td_proc); | |||||
mtx_lock(&td->td_proc->p_sigacts->ps_mtx); | |||||
tmpset = td->td_proc->p_siglist; | |||||
SIGSETOR(tmpset, td->td_siglist); | |||||
mtx_unlock(&td->td_proc->p_sigacts->ps_mtx); | |||||
PROC_UNLOCK(td->td_proc); | |||||
kib: As was discussed elsewere, this is racy. Also, even if ignoring the race, sig_isfatal() is not… | |||||
Done Inline ActionsI believe your objection is based on the fact that trapsignal could end up delivering a signal to a thread that won't immediately return to userspace, if the daemon doesn't respond to FUSE_INTERRUPT. Is that correct? Could you suggest an alternate way to handle it? Does it make a difference to you that intr is a non-default mount option? (though weirdly, it's a "mount option" that's implemented in userspace within libfuse). asomers: I believe your objection is based on the fact that `trapsignal` could end up delivering a… | |||||
Not Done Inline ActionsNo, my objections are based on two facts:
kib: No, my objections are based on two facts:
- after your checks, the disposition of the signal… | |||||
Done Inline ActionsOk, so given the race potential maybe I should just remove sig_isfatal, and always wait for a response from the daemon, then. The downside is just increased signal latency. asomers: Ok, so given the race potential maybe I should just remove sig_isfatal, and always wait for a… | |||||
Not Done Inline ActionsWe already discussed this. I remember that I noted the "intr" mount option for NFS, and you can use it as an example. If "intr" is specified, then any signal interrupts the io, by means of specifying PCATCH to msleep(9). If it is not specified, you do not specify PCATCH. There could be some refinement, as used by NFS, e.g. you can mask all signals except SIGINT/SIGTERM/SIGQUIT around msleep(PCATCH). kib: We already discussed this. I remember that I noted the "intr" mount option for NFS, and you… | |||||
fuse_lck_mtx_lock(ftick->tk_aw_mtx); | |||||
if (!interrupted && !SIGISMEMBER(tmpset, SIGKILL)) { | |||||
/* | |||||
* Block all signals while we wait for an interrupt | |||||
* response. The protocol doesn't discriminate between | |||||
* different signals. | |||||
*/ | |||||
SIGFILLSET(blockedset); | |||||
interrupted = true; | |||||
goto retry; | |||||
} else { | |||||
/* | |||||
* Return immediately for fatal signals, or if this is | |||||
* the second interruption. We should only be | |||||
* interrupted twice if the thread is stopped, for | |||||
* example during sigexit. | |||||
*/ | |||||
} | } | ||||
} else if (err) { | |||||
SDT_PROBE2(fusefs, , ipc, trace, 6, | |||||
"fticket_wait_answer: other error"); | |||||
} else { | |||||
SDT_PROBE2(fusefs, , ipc, trace, 7, "fticket_wait_answer: OK"); | |||||
} | |||||
out: | out: | ||||
if (!(err || fticket_answered(ftick))) { | if (!(err || fticket_answered(ftick))) { | ||||
SDT_PROBE2(fuse, , ipc, trace, 1, | SDT_PROBE2(fusefs, , ipc, trace, 1, | ||||
"FUSE: requester was woken up but still no answer"); | "FUSE: requester was woken up but still no answer"); | ||||
err = ENXIO; | err = ENXIO; | ||||
} | } | ||||
fuse_lck_mtx_unlock(ftick->tk_aw_mtx); | fuse_lck_mtx_unlock(ftick->tk_aw_mtx); | ||||
sigallowstop(stops_deferred); | |||||
return err; | return err; | ||||
} | } | ||||
static inline | static inline | ||||
int | int | ||||
fticket_aw_pull_uio(struct fuse_ticket *ftick, struct uio *uio) | fticket_aw_pull_uio(struct fuse_ticket *ftick, struct uio *uio) | ||||
{ | { | ||||
Show All 39 Lines | |||||
{ | { | ||||
struct fuse_data *data; | struct fuse_data *data; | ||||
data = malloc(sizeof(struct fuse_data), M_FUSEMSG, M_WAITOK | M_ZERO); | data = malloc(sizeof(struct fuse_data), M_FUSEMSG, M_WAITOK | M_ZERO); | ||||
data->fdev = fdev; | data->fdev = fdev; | ||||
mtx_init(&data->ms_mtx, "fuse message list mutex", NULL, MTX_DEF); | mtx_init(&data->ms_mtx, "fuse message list mutex", NULL, MTX_DEF); | ||||
STAILQ_INIT(&data->ms_head); | STAILQ_INIT(&data->ms_head); | ||||
data->ms_count = 0; | |||||
knlist_init_mtx(&data->ks_rsel.si_note, &data->ms_mtx); | |||||
mtx_init(&data->aw_mtx, "fuse answer list mutex", NULL, MTX_DEF); | mtx_init(&data->aw_mtx, "fuse answer list mutex", NULL, MTX_DEF); | ||||
TAILQ_INIT(&data->aw_head); | TAILQ_INIT(&data->aw_head); | ||||
data->daemoncred = crhold(cred); | data->daemoncred = crhold(cred); | ||||
data->daemon_timeout = FUSE_DEFAULT_DAEMON_TIMEOUT; | data->daemon_timeout = FUSE_DEFAULT_DAEMON_TIMEOUT; | ||||
sx_init(&data->rename_lock, "fuse rename lock"); | sx_init(&data->rename_lock, "fuse rename lock"); | ||||
data->ref = 1; | data->ref = 1; | ||||
return data; | return data; | ||||
} | } | ||||
void | void | ||||
fdata_trydestroy(struct fuse_data *data) | fdata_trydestroy(struct fuse_data *data) | ||||
{ | { | ||||
data->ref--; | data->ref--; | ||||
MPASS(data->ref >= 0); | MPASS(data->ref >= 0); | ||||
if (data->ref != 0) | if (data->ref != 0) | ||||
return; | return; | ||||
/* Driving off stage all that stuff thrown at device... */ | /* Driving off stage all that stuff thrown at device... */ | ||||
mtx_destroy(&data->ms_mtx); | |||||
mtx_destroy(&data->aw_mtx); | |||||
sx_destroy(&data->rename_lock); | sx_destroy(&data->rename_lock); | ||||
crfree(data->daemoncred); | crfree(data->daemoncred); | ||||
mtx_destroy(&data->aw_mtx); | |||||
knlist_delete(&data->ks_rsel.si_note, curthread, 0); | |||||
knlist_destroy(&data->ks_rsel.si_note); | |||||
mtx_destroy(&data->ms_mtx); | |||||
free(data, M_FUSEMSG); | free(data, M_FUSEMSG); | ||||
} | } | ||||
void | void | ||||
fdata_set_dead(struct fuse_data *data) | fdata_set_dead(struct fuse_data *data) | ||||
{ | { | ||||
FUSE_LOCK(); | FUSE_LOCK(); | ||||
▲ Show 20 Lines • Show All 52 Lines • ▼ Show 20 Lines | fuse_insert_callback(struct fuse_ticket *ftick, fuse_handler_t * handler) | ||||
} | } | ||||
ftick->tk_aw_handler = handler; | ftick->tk_aw_handler = handler; | ||||
fuse_lck_mtx_lock(ftick->tk_data->aw_mtx); | fuse_lck_mtx_lock(ftick->tk_data->aw_mtx); | ||||
fuse_aw_push(ftick); | fuse_aw_push(ftick); | ||||
fuse_lck_mtx_unlock(ftick->tk_data->aw_mtx); | fuse_lck_mtx_unlock(ftick->tk_data->aw_mtx); | ||||
} | } | ||||
/* | |||||
* Insert a new upgoing ticket into the message queue | |||||
* | |||||
* If urgent is true, insert at the front of the queue. Otherwise, insert in | |||||
* FIFO order. | |||||
*/ | |||||
void | void | ||||
fuse_insert_message(struct fuse_ticket *ftick) | fuse_insert_message(struct fuse_ticket *ftick, bool urgent) | ||||
{ | { | ||||
if (ftick->tk_flag & FT_DIRTY) { | if (ftick->tk_flag & FT_DIRTY) { | ||||
panic("FUSE: ticket reused without being refreshed"); | panic("FUSE: ticket reused without being refreshed"); | ||||
} | } | ||||
ftick->tk_flag |= FT_DIRTY; | ftick->tk_flag |= FT_DIRTY; | ||||
if (fdata_get_dead(ftick->tk_data)) { | if (fdata_get_dead(ftick->tk_data)) { | ||||
return; | return; | ||||
} | } | ||||
fuse_lck_mtx_lock(ftick->tk_data->ms_mtx); | fuse_lck_mtx_lock(ftick->tk_data->ms_mtx); | ||||
if (urgent) | |||||
fuse_ms_push_head(ftick); | |||||
else | |||||
fuse_ms_push(ftick); | fuse_ms_push(ftick); | ||||
wakeup_one(ftick->tk_data); | wakeup_one(ftick->tk_data); | ||||
selwakeuppri(&ftick->tk_data->ks_rsel, PZERO + 1); | selwakeuppri(&ftick->tk_data->ks_rsel, PZERO + 1); | ||||
KNOTE_LOCKED(&ftick->tk_data->ks_rsel.si_note, 0); | |||||
fuse_lck_mtx_unlock(ftick->tk_data->ms_mtx); | fuse_lck_mtx_unlock(ftick->tk_data->ms_mtx); | ||||
} | } | ||||
static int | static int | ||||
fuse_body_audit(struct fuse_ticket *ftick, size_t blen) | fuse_body_audit(struct fuse_ticket *ftick, size_t blen) | ||||
{ | { | ||||
int err = 0; | int err = 0; | ||||
enum fuse_opcode opcode; | enum fuse_opcode opcode; | ||||
opcode = fticket_opcode(ftick); | opcode = fticket_opcode(ftick); | ||||
switch (opcode) { | switch (opcode) { | ||||
case FUSE_BMAP: | |||||
err = (blen == sizeof(struct fuse_bmap_out)) ? 0 : EINVAL; | |||||
break; | |||||
case FUSE_LINK: | |||||
case FUSE_LOOKUP: | case FUSE_LOOKUP: | ||||
err = (blen == sizeof(struct fuse_entry_out)) ? 0 : EINVAL; | case FUSE_MKDIR: | ||||
case FUSE_MKNOD: | |||||
case FUSE_SYMLINK: | |||||
if (fuse_libabi_geq(ftick->tk_data, 7, 9)) { | |||||
err = (blen == sizeof(struct fuse_entry_out)) ? | |||||
0 : EINVAL; | |||||
} else { | |||||
err = (blen == FUSE_COMPAT_ENTRY_OUT_SIZE) ? 0 : EINVAL; | |||||
} | |||||
break; | break; | ||||
case FUSE_FORGET: | case FUSE_FORGET: | ||||
panic("FUSE: a handler has been intalled for FUSE_FORGET"); | panic("FUSE: a handler has been intalled for FUSE_FORGET"); | ||||
break; | break; | ||||
case FUSE_GETATTR: | case FUSE_GETATTR: | ||||
err = (blen == sizeof(struct fuse_attr_out)) ? 0 : EINVAL; | |||||
break; | |||||
case FUSE_SETATTR: | case FUSE_SETATTR: | ||||
err = (blen == sizeof(struct fuse_attr_out)) ? 0 : EINVAL; | if (fuse_libabi_geq(ftick->tk_data, 7, 9)) { | ||||
err = (blen == sizeof(struct fuse_attr_out)) ? | |||||
0 : EINVAL; | |||||
} else { | |||||
err = (blen == FUSE_COMPAT_ATTR_OUT_SIZE) ? 0 : EINVAL; | |||||
} | |||||
break; | break; | ||||
case FUSE_READLINK: | case FUSE_READLINK: | ||||
err = (PAGE_SIZE >= blen) ? 0 : EINVAL; | err = (PAGE_SIZE >= blen) ? 0 : EINVAL; | ||||
break; | break; | ||||
case FUSE_SYMLINK: | |||||
err = (blen == sizeof(struct fuse_entry_out)) ? 0 : EINVAL; | |||||
break; | |||||
case FUSE_MKNOD: | |||||
err = (blen == sizeof(struct fuse_entry_out)) ? 0 : EINVAL; | |||||
break; | |||||
case FUSE_MKDIR: | |||||
err = (blen == sizeof(struct fuse_entry_out)) ? 0 : EINVAL; | |||||
break; | |||||
case FUSE_UNLINK: | case FUSE_UNLINK: | ||||
err = (blen == 0) ? 0 : EINVAL; | err = (blen == 0) ? 0 : EINVAL; | ||||
break; | break; | ||||
case FUSE_RMDIR: | case FUSE_RMDIR: | ||||
err = (blen == 0) ? 0 : EINVAL; | err = (blen == 0) ? 0 : EINVAL; | ||||
break; | break; | ||||
case FUSE_RENAME: | case FUSE_RENAME: | ||||
err = (blen == 0) ? 0 : EINVAL; | err = (blen == 0) ? 0 : EINVAL; | ||||
break; | break; | ||||
case FUSE_LINK: | |||||
err = (blen == sizeof(struct fuse_entry_out)) ? 0 : EINVAL; | |||||
break; | |||||
case FUSE_OPEN: | case FUSE_OPEN: | ||||
err = (blen == sizeof(struct fuse_open_out)) ? 0 : EINVAL; | err = (blen == sizeof(struct fuse_open_out)) ? 0 : EINVAL; | ||||
break; | break; | ||||
case FUSE_READ: | case FUSE_READ: | ||||
err = (((struct fuse_read_in *)( | err = (((struct fuse_read_in *)( | ||||
(char *)ftick->tk_ms_fiov.base + | (char *)ftick->tk_ms_fiov.base + | ||||
sizeof(struct fuse_in_header) | sizeof(struct fuse_in_header) | ||||
Show All 38 Lines | case FUSE_REMOVEXATTR: | ||||
err = (blen == 0) ? 0 : EINVAL; | err = (blen == 0) ? 0 : EINVAL; | ||||
break; | break; | ||||
case FUSE_FLUSH: | case FUSE_FLUSH: | ||||
err = (blen == 0) ? 0 : EINVAL; | err = (blen == 0) ? 0 : EINVAL; | ||||
break; | break; | ||||
case FUSE_INIT: | case FUSE_INIT: | ||||
if (blen == sizeof(struct fuse_init_out) || blen == 8) { | if (blen == sizeof(struct fuse_init_out) || | ||||
blen == FUSE_COMPAT_INIT_OUT_SIZE || | |||||
blen == FUSE_COMPAT_22_INIT_OUT_SIZE) { | |||||
err = 0; | err = 0; | ||||
} else { | } else { | ||||
err = EINVAL; | err = EINVAL; | ||||
} | } | ||||
break; | break; | ||||
case FUSE_OPENDIR: | case FUSE_OPENDIR: | ||||
err = (blen == sizeof(struct fuse_open_out)) ? 0 : EINVAL; | err = (blen == sizeof(struct fuse_open_out)) ? 0 : EINVAL; | ||||
Show All 10 Lines | case FUSE_RELEASEDIR: | ||||
err = (blen == 0) ? 0 : EINVAL; | err = (blen == 0) ? 0 : EINVAL; | ||||
break; | break; | ||||
case FUSE_FSYNCDIR: | case FUSE_FSYNCDIR: | ||||
err = (blen == 0) ? 0 : EINVAL; | err = (blen == 0) ? 0 : EINVAL; | ||||
break; | break; | ||||
case FUSE_GETLK: | case FUSE_GETLK: | ||||
panic("FUSE: no response body format check for FUSE_GETLK"); | err = (blen == sizeof(struct fuse_lk_out)) ? 0 : EINVAL; | ||||
break; | break; | ||||
case FUSE_SETLK: | case FUSE_SETLK: | ||||
panic("FUSE: no response body format check for FUSE_SETLK"); | err = (blen == 0) ? 0 : EINVAL; | ||||
break; | break; | ||||
case FUSE_SETLKW: | case FUSE_SETLKW: | ||||
panic("FUSE: no response body format check for FUSE_SETLKW"); | err = (blen == 0) ? 0 : EINVAL; | ||||
break; | break; | ||||
case FUSE_ACCESS: | case FUSE_ACCESS: | ||||
err = (blen == 0) ? 0 : EINVAL; | err = (blen == 0) ? 0 : EINVAL; | ||||
break; | break; | ||||
case FUSE_CREATE: | case FUSE_CREATE: | ||||
if (fuse_libabi_geq(ftick->tk_data, 7, 9)) { | |||||
err = (blen == sizeof(struct fuse_entry_out) + | err = (blen == sizeof(struct fuse_entry_out) + | ||||
sizeof(struct fuse_open_out)) ? 0 : EINVAL; | sizeof(struct fuse_open_out)) ? 0 : EINVAL; | ||||
} else { | |||||
err = (blen == FUSE_COMPAT_ENTRY_OUT_SIZE + | |||||
sizeof(struct fuse_open_out)) ? 0 : EINVAL; | |||||
} | |||||
break; | break; | ||||
case FUSE_DESTROY: | case FUSE_DESTROY: | ||||
err = (blen == 0) ? 0 : EINVAL; | err = (blen == 0) ? 0 : EINVAL; | ||||
break; | break; | ||||
default: | default: | ||||
panic("FUSE: opcodes out of sync (%d)\n", opcode); | panic("FUSE: opcodes out of sync (%d)\n", opcode); | ||||
Show All 9 Lines | |||||
{ | { | ||||
ihead->len = sizeof(*ihead) + blen; | ihead->len = sizeof(*ihead) + blen; | ||||
ihead->unique = ftick->tk_unique; | ihead->unique = ftick->tk_unique; | ||||
ihead->nodeid = nid; | ihead->nodeid = nid; | ||||
ihead->opcode = op; | ihead->opcode = op; | ||||
ihead->pid = pid; | ihead->pid = pid; | ||||
ihead->uid = cred->cr_uid; | ihead->uid = cred->cr_uid; | ||||
ihead->gid = cred->cr_rgid; | ihead->gid = cred->cr_groups[0]; | ||||
} | } | ||||
/* | /* | ||||
* fuse_standard_handler just pulls indata and wakes up pretender. | * fuse_standard_handler just pulls indata and wakes up pretender. | ||||
* Doesn't try to interpret data, that's left for the pretender. | * Doesn't try to interpret data, that's left for the pretender. | ||||
* Though might do a basic size verification before the pull-in takes place | * Though might do a basic size verification before the pull-in takes place | ||||
*/ | */ | ||||
Show All 11 Lines | if (!fticket_answered(ftick)) { | ||||
ftick->tk_aw_errno = err; | ftick->tk_aw_errno = err; | ||||
wakeup(ftick); | wakeup(ftick); | ||||
} | } | ||||
fuse_lck_mtx_unlock(ftick->tk_aw_mtx); | fuse_lck_mtx_unlock(ftick->tk_aw_mtx); | ||||
return err; | return err; | ||||
} | } | ||||
void | /* | ||||
fdisp_make_pid(struct fuse_dispatcher *fdip, enum fuse_opcode op, | * Reinitialize a dispatcher from a pid and node id, without resizing or | ||||
* clearing its data buffers | |||||
*/ | |||||
static void | |||||
fdisp_refresh_pid(struct fuse_dispatcher *fdip, enum fuse_opcode op, | |||||
struct mount *mp, uint64_t nid, pid_t pid, struct ucred *cred) | struct mount *mp, uint64_t nid, pid_t pid, struct ucred *cred) | ||||
{ | { | ||||
struct fuse_data *data = fuse_get_mpdata(mp); | MPASS(fdip->tick); | ||||
MPASS2(sizeof(fdip->finh) + fdip->iosize <= fdip->tick->tk_ms_fiov.len, | |||||
"Must use fdisp_make_pid to increase the size of the fiov"); | |||||
fticket_reset(fdip->tick); | |||||
FUSE_DIMALLOC(&fdip->tick->tk_ms_fiov, fdip->finh, | |||||
fdip->indata, fdip->iosize); | |||||
fuse_setup_ihead(fdip->finh, fdip->tick, nid, op, fdip->iosize, pid, | |||||
cred); | |||||
} | |||||
/* Initialize a dispatcher from a pid and node id */ | |||||
static void | |||||
fdisp_make_pid(struct fuse_dispatcher *fdip, enum fuse_opcode op, | |||||
struct fuse_data *data, uint64_t nid, pid_t pid, struct ucred *cred) | |||||
{ | |||||
if (fdip->tick) { | if (fdip->tick) { | ||||
fticket_refresh(fdip->tick); | fticket_refresh(fdip->tick); | ||||
} else { | } else { | ||||
fdip->tick = fuse_ticket_fetch(data); | fdip->tick = fuse_ticket_fetch(data); | ||||
} | } | ||||
/* FUSE_DIMALLOC will bzero the fiovs when it enlarges them */ | |||||
FUSE_DIMALLOC(&fdip->tick->tk_ms_fiov, fdip->finh, | FUSE_DIMALLOC(&fdip->tick->tk_ms_fiov, fdip->finh, | ||||
fdip->indata, fdip->iosize); | fdip->indata, fdip->iosize); | ||||
fuse_setup_ihead(fdip->finh, fdip->tick, nid, op, fdip->iosize, pid, cred); | fuse_setup_ihead(fdip->finh, fdip->tick, nid, op, fdip->iosize, pid, cred); | ||||
} | } | ||||
void | void | ||||
fdisp_make(struct fuse_dispatcher *fdip, enum fuse_opcode op, struct mount *mp, | fdisp_make(struct fuse_dispatcher *fdip, enum fuse_opcode op, struct mount *mp, | ||||
uint64_t nid, struct thread *td, struct ucred *cred) | uint64_t nid, struct thread *td, struct ucred *cred) | ||||
{ | { | ||||
struct fuse_data *data = fuse_get_mpdata(mp); | |||||
RECTIFY_TDCR(td, cred); | RECTIFY_TDCR(td, cred); | ||||
return fdisp_make_pid(fdip, op, mp, nid, td->td_proc->p_pid, cred); | return fdisp_make_pid(fdip, op, data, nid, td->td_proc->p_pid, cred); | ||||
} | } | ||||
void | void | ||||
fdisp_make_vp(struct fuse_dispatcher *fdip, enum fuse_opcode op, | fdisp_make_vp(struct fuse_dispatcher *fdip, enum fuse_opcode op, | ||||
struct vnode *vp, struct thread *td, struct ucred *cred) | struct vnode *vp, struct thread *td, struct ucred *cred) | ||||
{ | { | ||||
struct mount *mp = vnode_mount(vp); | |||||
struct fuse_data *data = fuse_get_mpdata(mp); | |||||
RECTIFY_TDCR(td, cred); | RECTIFY_TDCR(td, cred); | ||||
return fdisp_make_pid(fdip, op, vnode_mount(vp), VTOI(vp), | return fdisp_make_pid(fdip, op, data, VTOI(vp), | ||||
td->td_proc->p_pid, cred); | td->td_proc->p_pid, cred); | ||||
} | } | ||||
SDT_PROBE_DEFINE2(fuse, , ipc, fdisp_wait_answ_error, "char*", "int"); | /* Refresh a fuse_dispatcher so it can be reused, but don't zero its data */ | ||||
void | |||||
fdisp_refresh_vp(struct fuse_dispatcher *fdip, enum fuse_opcode op, | |||||
struct vnode *vp, struct thread *td, struct ucred *cred) | |||||
{ | |||||
RECTIFY_TDCR(td, cred); | |||||
return fdisp_refresh_pid(fdip, op, vnode_mount(vp), VTOI(vp), | |||||
td->td_proc->p_pid, cred); | |||||
} | |||||
void | |||||
fdisp_refresh(struct fuse_dispatcher *fdip) | |||||
{ | |||||
fticket_refresh(fdip->tick); | |||||
} | |||||
SDT_PROBE_DEFINE2(fusefs, , ipc, fdisp_wait_answ_error, "char*", "int"); | |||||
int | int | ||||
fdisp_wait_answ(struct fuse_dispatcher *fdip) | fdisp_wait_answ(struct fuse_dispatcher *fdip) | ||||
{ | { | ||||
int err = 0; | int err = 0; | ||||
fdip->answ_stat = 0; | fdip->answ_stat = 0; | ||||
fuse_insert_callback(fdip->tick, fuse_standard_handler); | fuse_insert_callback(fdip->tick, fuse_standard_handler); | ||||
fuse_insert_message(fdip->tick); | fuse_insert_message(fdip->tick, false); | ||||
if ((err = fticket_wait_answer(fdip->tick))) { | if ((err = fticket_wait_answer(fdip->tick))) { | ||||
fuse_lck_mtx_lock(fdip->tick->tk_aw_mtx); | fuse_lck_mtx_lock(fdip->tick->tk_aw_mtx); | ||||
if (fticket_answered(fdip->tick)) { | if (fticket_answered(fdip->tick)) { | ||||
/* | /* | ||||
* Just between noticing the interrupt and getting here, | * Just between noticing the interrupt and getting here, | ||||
* the standard handler has completed his job. | * the standard handler has completed his job. | ||||
* So we drop the ticket and exit as usual. | * So we drop the ticket and exit as usual. | ||||
*/ | */ | ||||
SDT_PROBE2(fuse, , ipc, fdisp_wait_answ_error, | SDT_PROBE2(fusefs, , ipc, fdisp_wait_answ_error, | ||||
"IPC: interrupted, already answered", err); | "IPC: interrupted, already answered", err); | ||||
fuse_lck_mtx_unlock(fdip->tick->tk_aw_mtx); | fuse_lck_mtx_unlock(fdip->tick->tk_aw_mtx); | ||||
goto out; | goto out; | ||||
} else { | } else { | ||||
/* | /* | ||||
* So we were faster than the standard handler. | * So we were faster than the standard handler. | ||||
* Then by setting the answered flag we get *him* | * Then by setting the answered flag we get *him* | ||||
* to drop the ticket. | * to drop the ticket. | ||||
*/ | */ | ||||
SDT_PROBE2(fuse, , ipc, fdisp_wait_answ_error, | SDT_PROBE2(fusefs, , ipc, fdisp_wait_answ_error, | ||||
"IPC: interrupted, setting to answered", err); | "IPC: interrupted, setting to answered", err); | ||||
fticket_set_answered(fdip->tick); | fticket_set_answered(fdip->tick); | ||||
fuse_lck_mtx_unlock(fdip->tick->tk_aw_mtx); | fuse_lck_mtx_unlock(fdip->tick->tk_aw_mtx); | ||||
return err; | return err; | ||||
} | } | ||||
} | } | ||||
if (fdip->tick->tk_aw_errno) { | if (fdip->tick->tk_aw_errno == ENOTCONN) { | ||||
SDT_PROBE2(fuse, , ipc, fdisp_wait_answ_error, | /* The daemon died while we were waiting for a response */ | ||||
err = ENOTCONN; | |||||
goto out; | |||||
} else if (fdip->tick->tk_aw_errno) { | |||||
/* | |||||
* There was some sort of communication error with the daemon | |||||
* that the client wouldn't understand. | |||||
*/ | |||||
SDT_PROBE2(fusefs, , ipc, fdisp_wait_answ_error, | |||||
"IPC: explicit EIO-ing", fdip->tick->tk_aw_errno); | "IPC: explicit EIO-ing", fdip->tick->tk_aw_errno); | ||||
err = EIO; | err = EIO; | ||||
goto out; | goto out; | ||||
} | } | ||||
if ((err = fdip->tick->tk_aw_ohead.error)) { | if ((err = fdip->tick->tk_aw_ohead.error)) { | ||||
SDT_PROBE2(fuse, , ipc, fdisp_wait_answ_error, | SDT_PROBE2(fusefs, , ipc, fdisp_wait_answ_error, | ||||
"IPC: setting status", fdip->tick->tk_aw_ohead.error); | "IPC: setting status", fdip->tick->tk_aw_ohead.error); | ||||
/* | /* | ||||
* This means a "proper" fuse syscall error. | * This means a "proper" fuse syscall error. | ||||
* We record this value so the caller will | * We record this value so the caller will | ||||
* be able to know it's not a boring messaging | * be able to know it's not a boring messaging | ||||
* failure, if she wishes so (and if not, she can | * failure, if she wishes so (and if not, she can | ||||
* just simply propagate the return value of this routine). | * just simply propagate the return value of this routine). | ||||
* [XXX Maybe a bitflag would do the job too, | * [XXX Maybe a bitflag would do the job too, | ||||
Show All 12 Lines | |||||
} | } | ||||
void | void | ||||
fuse_ipc_init(void) | fuse_ipc_init(void) | ||||
{ | { | ||||
ticket_zone = uma_zcreate("fuse_ticket", sizeof(struct fuse_ticket), | ticket_zone = uma_zcreate("fuse_ticket", sizeof(struct fuse_ticket), | ||||
fticket_ctor, fticket_dtor, fticket_init, fticket_fini, | fticket_ctor, fticket_dtor, fticket_init, fticket_fini, | ||||
UMA_ALIGN_PTR, 0); | UMA_ALIGN_PTR, 0); | ||||
fuse_ticket_count = counter_u64_alloc(M_WAITOK); | |||||
counter_u64_zero(fuse_ticket_count); | |||||
} | } | ||||
void | void | ||||
fuse_ipc_destroy(void) | fuse_ipc_destroy(void) | ||||
{ | { | ||||
counter_u64_free(fuse_ticket_count); | |||||
uma_zdestroy(ticket_zone); | uma_zdestroy(ticket_zone); | ||||
} | } |
As was discussed elsewere, this is racy. Also, even if ignoring the race, sig_isfatal() is not correct, for example consider the difference between trapsignal() and async signals.