X-Git-Url: https://git.lttng.org/?p=lttng-tools.git;a=blobdiff_plain;f=src%2Fcommon%2Ffd-tracker%2Ffd-tracker.c;h=772bf04bc5994efa5fc351a2ed933514506de36c;hp=67da7eab4d51bd98b2a7d082e5520ab92e836708;hb=ea05fafd4088958df7f994fcf903627bc609808d;hpb=df038819ef174fb8b0d5a76c293a3b94ce2a43b9 diff --git a/src/common/fd-tracker/fd-tracker.c b/src/common/fd-tracker/fd-tracker.c index 67da7eab4..772bf04bc 100644 --- a/src/common/fd-tracker/fd-tracker.c +++ b/src/common/fd-tracker/fd-tracker.c @@ -1,67 +1,57 @@ /* - * Copyright (C) 2018 - Jérémie Galarneau + * Copyright (C) 2018-2020 Jérémie Galarneau * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License, version 2 only, as - * published by the Free Software Foundation. + * SPDX-License-Identifier: GPL-2.0-only * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 51 - * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include -#include -#include #include -#include -#include #include +#include +#include +#include +#include -#include "common/macros.h" -#include "common/error.h" -#include "common/defaults.h" -#include "common/hashtable/utils.h" -#include "common/hashtable/hashtable.h" +#include +#include +#include +#include +#include +#include +#include #include "fd-tracker.h" +#include "inode.h" /* Tracker lock must be taken by the user. */ -#define TRACKED_COUNT(tracker) \ - (tracker->count.suspendable.active + \ - tracker->count.suspendable.suspended + \ - tracker->count.unsuspendable) +#define TRACKED_COUNT(tracker) \ + (tracker->count.suspendable.active + \ + tracker->count.suspendable.suspended + \ + tracker->count.unsuspendable) /* Tracker lock must be taken by the user. */ -#define ACTIVE_COUNT(tracker) \ - (tracker->count.suspendable.active + \ - tracker->count.unsuspendable) +#define ACTIVE_COUNT(tracker) \ + (tracker->count.suspendable.active + tracker->count.unsuspendable) /* Tracker lock must be taken by the user. */ -#define SUSPENDED_COUNT(tracker) \ - (tracker->count.suspendable.suspended) +#define SUSPENDED_COUNT(tracker) (tracker->count.suspendable.suspended) /* Tracker lock must be taken by the user. */ -#define SUSPENDABLE_COUNT(tracker) \ - (tracker->count.suspendable.active + \ - tracker->count.suspendable.suspended) +#define SUSPENDABLE_COUNT(tracker) \ + (tracker->count.suspendable.active + \ + tracker->count.suspendable.suspended) /* Tracker lock must be taken by the user. */ -#define UNSUSPENDABLE_COUNT(tracker) \ - (tracker->count.unsuspendable) +#define UNSUSPENDABLE_COUNT(tracker) (tracker->count.unsuspendable) struct fd_tracker { pthread_mutex_t lock; struct { - struct { + struct { unsigned int active; unsigned int suspended; } suspendable; @@ -84,19 +74,19 @@ struct fd_tracker { struct cds_list_head active_handles; struct cds_list_head suspended_handles; struct cds_lfht *unsuspendable_fds; + struct lttng_inode_registry *inode_registry; + /* Unlinked files are moved in this directory under a unique name. */ + struct lttng_directory_handle *unlink_directory_handle; + struct lttng_unlinked_file_pool *unlinked_file_pool; }; struct open_properties { - char *path; int flags; - struct { - bool is_set; - mode_t value; - } mode; + LTTNG_OPTIONAL(mode_t) mode; }; /* - * A fs_handle is not ref-counted. Therefore, it is assumed that a + * A fs_handle_tracked is not ref-counted. Therefore, it is assumed that a * handle is never in-use while it is being reclaimed. It can be * shared by multiple threads, but external synchronization is required * to ensure it is not still being used when it is reclaimed (close method). @@ -104,7 +94,8 @@ struct open_properties { * * The fs_handle lock always nests _within_ the tracker's lock. */ -struct fs_handle { +struct fs_handle_tracked { + struct fs_handle parent; pthread_mutex_t lock; /* * Weak reference to the tracker. All fs_handles are assumed to have @@ -112,6 +103,7 @@ struct fs_handle { */ struct fd_tracker *tracker; struct open_properties properties; + struct lttng_inode *inode; int fd; /* inode number of the file at the time of the handle's creation. */ uint64_t ino; @@ -142,46 +134,48 @@ static struct { static int match_fd(struct cds_lfht_node *node, const void *key); static void unsuspendable_fd_destroy(struct unsuspendable_fd *entry); -static struct unsuspendable_fd *unsuspendable_fd_create(const char *name, - int fd); -static int open_from_properties(struct open_properties *properties); - -static void fs_handle_log(struct fs_handle *handle); -static int fs_handle_suspend(struct fs_handle *handle); -static int fs_handle_restore(struct fs_handle *handle); - -static void fd_tracker_track(struct fd_tracker *tracker, - struct fs_handle *handle); -static void fd_tracker_untrack(struct fd_tracker *tracker, - struct fs_handle *handle); -static int fd_tracker_suspend_handles(struct fd_tracker *tracker, - unsigned int count); -static int fd_tracker_restore_handle(struct fd_tracker *tracker, - struct fs_handle *handle); +static struct unsuspendable_fd *unsuspendable_fd_create( + const char *name, int fd); +static int open_from_properties(const struct lttng_directory_handle *dir_handle, + const char *path, struct open_properties *properties); + +static void fs_handle_tracked_log(struct fs_handle_tracked *handle); +static int fs_handle_tracked_suspend(struct fs_handle_tracked *handle); +static int fs_handle_tracked_restore(struct fs_handle_tracked *handle); +static int fs_handle_tracked_get_fd(struct fs_handle *_handle); +static void fs_handle_tracked_put_fd(struct fs_handle *_handle); +static int fs_handle_tracked_unlink(struct fs_handle *_handle); +static int fs_handle_tracked_close(struct fs_handle *_handle); + +static void fd_tracker_track( + struct fd_tracker *tracker, struct fs_handle_tracked *handle); +static void fd_tracker_untrack( + struct fd_tracker *tracker, struct fs_handle_tracked *handle); +static int fd_tracker_suspend_handles( + struct fd_tracker *tracker, unsigned int count); +static int fd_tracker_restore_handle( + struct fd_tracker *tracker, struct fs_handle_tracked *handle); /* Match function of the tracker's unsuspendable_fds hash table. */ -static -int match_fd(struct cds_lfht_node *node, const void *key) +static int match_fd(struct cds_lfht_node *node, const void *key) { - struct unsuspendable_fd *entry = - caa_container_of(node, struct unsuspendable_fd, tracker_node); + struct unsuspendable_fd *entry = caa_container_of( + node, struct unsuspendable_fd, tracker_node); - return hash_match_key_ulong((void *) (unsigned long) entry->fd, - (void *) key); + return hash_match_key_ulong( + (void *) (unsigned long) entry->fd, (void *) key); } -static -void delete_unsuspendable_fd(struct rcu_head *head) +static void delete_unsuspendable_fd(struct rcu_head *head) { - struct unsuspendable_fd *fd = caa_container_of(head, - struct unsuspendable_fd, rcu_head); + struct unsuspendable_fd *fd = caa_container_of( + head, struct unsuspendable_fd, rcu_head); free(fd->name); free(fd); } -static -void unsuspendable_fd_destroy(struct unsuspendable_fd *entry) +static void unsuspendable_fd_destroy(struct unsuspendable_fd *entry) { if (!entry) { return; @@ -189,11 +183,10 @@ void unsuspendable_fd_destroy(struct unsuspendable_fd *entry) call_rcu(&entry->rcu_head, delete_unsuspendable_fd); } -static -struct unsuspendable_fd *unsuspendable_fd_create(const char *name, int fd) +static struct unsuspendable_fd *unsuspendable_fd_create( + const char *name, int fd) { - struct unsuspendable_fd *entry = - zmalloc(sizeof(*entry)); + struct unsuspendable_fd *entry = zmalloc(sizeof(*entry)); if (!entry) { goto error; @@ -212,28 +205,33 @@ error: return NULL; } -static -void fs_handle_log(struct fs_handle *handle) +static void fs_handle_tracked_log(struct fs_handle_tracked *handle) { + const char *path; + pthread_mutex_lock(&handle->lock); + lttng_inode_borrow_location(handle->inode, NULL, &path); + if (handle->fd >= 0) { - DBG_NO_LOC(" %s [active, fd %d%s]", - handle->properties.path, - handle->fd, + DBG_NO_LOC(" %s [active, fd %d%s]", path, handle->fd, handle->in_use ? ", in use" : ""); } else { - DBG_NO_LOC(" %s [suspended]", handle->properties.path); + DBG_NO_LOC(" %s [suspended]", path); } pthread_mutex_unlock(&handle->lock); } -static -int fs_handle_suspend(struct fs_handle *handle) +/* Tracker lock must be held by the caller. */ +static int fs_handle_tracked_suspend(struct fs_handle_tracked *handle) { int ret = 0; struct stat fs_stat; + const char *path; + const struct lttng_directory_handle *node_directory_handle; pthread_mutex_lock(&handle->lock); + lttng_inode_borrow_location( + handle->inode, &node_directory_handle, &path); assert(handle->fd >= 0); if (handle->in_use) { /* This handle can't be suspended as it is currently in use. */ @@ -241,10 +239,11 @@ int fs_handle_suspend(struct fs_handle *handle) goto end; } - ret = stat(handle->properties.path, &fs_stat); + ret = lttng_directory_handle_stat( + node_directory_handle, path, &fs_stat); if (ret) { - PERROR("Filesystem handle to %s cannot be suspended as stat() failed", - handle->properties.path); + PERROR("Filesystem handle to %s cannot be suspended as stat() failed", + path); ret = -errno; goto end; } @@ -252,28 +251,28 @@ int fs_handle_suspend(struct fs_handle *handle) if (fs_stat.st_ino != handle->ino) { /* Don't suspend as the handle would not be restorable. */ WARN("Filesystem handle to %s cannot be suspended as its inode changed", - handle->properties.path); + path); ret = -ENOENT; goto end; } - handle->offset = lseek(handle->fd, 0, SEEK_CUR); + handle->offset = lseek(handle->fd, 0, SEEK_CUR); if (handle->offset == -1) { WARN("Filesystem handle to %s cannot be suspended as lseek() failed to sample its current position", - handle->properties.path); + path); ret = -errno; goto end; } ret = close(handle->fd); if (ret) { - PERROR("Filesystem handle to %s cannot be suspended as close() failed", - handle->properties.path); + PERROR("Filesystem handle to %s cannot be suspended as close() failed", + path); ret = -errno; goto end; } DBG("Suspended filesystem handle to %s (fd %i) at position %" PRId64, - handle->properties.path, handle->fd, handle->offset); + path, handle->fd, handle->offset); handle->fd = -1; end: if (ret) { @@ -284,16 +283,22 @@ end: } /* Caller must hold the tracker and handle's locks. */ -static -int fs_handle_restore(struct fs_handle *handle) +static int fs_handle_tracked_restore(struct fs_handle_tracked *handle) { int ret, fd = -1; + const char *path; + const struct lttng_directory_handle *node_directory_handle; + + lttng_inode_borrow_location( + handle->inode, &node_directory_handle, &path); assert(handle->fd == -1); - ret = open_from_properties(&handle->properties); + assert(path); + ret = open_from_properties( + node_directory_handle, path, &handle->properties); if (ret < 0) { - PERROR("Failed to restore filesystem handle to %s, open() failed", - handle->properties.path); + PERROR("Failed to restore filesystem handle to %s, open() failed", + path); ret = -errno; goto end; } @@ -301,13 +306,13 @@ int fs_handle_restore(struct fs_handle *handle) ret = lseek(fd, handle->offset, SEEK_SET); if (ret < 0) { - PERROR("Failed to restore filesystem handle to %s, lseek() failed", - handle->properties.path); + PERROR("Failed to restore filesystem handle to %s, lseek() failed", + path); ret = -errno; goto end; } DBG("Restored filesystem handle to %s (fd %i) at position %" PRId64, - handle->properties.path, fd, handle->offset); + path, fd, handle->offset); ret = 0; handle->fd = fd; fd = -1; @@ -318,8 +323,8 @@ end: return ret; } -static -int open_from_properties(struct open_properties *properties) +static int open_from_properties(const struct lttng_directory_handle *dir_handle, + const char *path, struct open_properties *properties) { int ret; @@ -330,10 +335,11 @@ int open_from_properties(struct open_properties *properties) * thus it is ignored here. */ if ((properties->flags & O_CREAT) && properties->mode.is_set) { - ret = open(properties->path, properties->flags, - properties->mode.value); + ret = lttng_directory_handle_open_file(dir_handle, path, + properties->flags, properties->mode.value); } else { - ret = open(properties->path, properties->flags); + ret = lttng_directory_handle_open_file(dir_handle, path, + properties->flags, 0); } /* * Some flags should not be used beyond the initial open() of a @@ -354,7 +360,9 @@ end: return ret; } -struct fd_tracker *fd_tracker_create(unsigned int capacity) +LTTNG_HIDDEN +struct fd_tracker *fd_tracker_create(const char *unlinked_file_path, + unsigned int capacity) { struct fd_tracker *tracker = zmalloc(sizeof(struct fd_tracker)); @@ -374,15 +382,34 @@ struct fd_tracker *fd_tracker_create(unsigned int capacity) tracker->capacity = capacity; tracker->unsuspendable_fds = cds_lfht_new(DEFAULT_HT_SIZE, 1, 0, CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL); + if (!tracker->unsuspendable_fds) { + ERR("Failed to create fd-tracker's unsuspendable_fds hash table"); + goto error; + } + tracker->inode_registry = lttng_inode_registry_create(); + if (!tracker->inode_registry) { + ERR("Failed to create fd-tracker's inode registry"); + goto error; + } + + tracker->unlinked_file_pool = + lttng_unlinked_file_pool_create(unlinked_file_path); + if (!tracker->unlinked_file_pool) { + goto error; + } DBG("File descriptor tracker created with a limit of %u simultaneously-opened FDs", capacity); end: return tracker; +error: + fd_tracker_destroy(tracker); + return NULL; } +LTTNG_HIDDEN void fd_tracker_log(struct fd_tracker *tracker) { - struct fs_handle *handle; + struct fs_handle_tracked *handle; struct unsuspendable_fd *unsuspendable_fd; struct cds_lfht_iter iter; @@ -400,13 +427,13 @@ void fd_tracker_log(struct fd_tracker *tracker) DBG_NO_LOC(" capacity: %u", tracker->capacity); DBG_NO_LOC(" Tracked suspendable file descriptors"); - cds_list_for_each_entry(handle, &tracker->active_handles, - handles_list_node) { - fs_handle_log(handle); + cds_list_for_each_entry( + handle, &tracker->active_handles, handles_list_node) { + fs_handle_tracked_log(handle); } cds_list_for_each_entry(handle, &tracker->suspended_handles, handles_list_node) { - fs_handle_log(handle); + fs_handle_tracked_log(handle); } if (!SUSPENDABLE_COUNT(tracker)) { DBG_NO_LOC(" None"); @@ -416,7 +443,8 @@ void fd_tracker_log(struct fd_tracker *tracker) rcu_read_lock(); cds_lfht_for_each_entry(tracker->unsuspendable_fds, &iter, unsuspendable_fd, tracker_node) { - DBG_NO_LOC(" %s [active, fd %d]", unsuspendable_fd->name ? : "Unnamed", + DBG_NO_LOC(" %s [active, fd %d]", + unsuspendable_fd->name ?: "Unnamed", unsuspendable_fd->fd); } rcu_read_unlock(); @@ -427,10 +455,14 @@ void fd_tracker_log(struct fd_tracker *tracker) pthread_mutex_unlock(&tracker->lock); } +LTTNG_HIDDEN int fd_tracker_destroy(struct fd_tracker *tracker) { int ret = 0; + if (!tracker) { + goto end; + } /* * Refuse to destroy the tracker as fs_handles may still old * weak references to the tracker. @@ -446,48 +478,51 @@ int fd_tracker_destroy(struct fd_tracker *tracker) } pthread_mutex_unlock(&tracker->lock); - ret = cds_lfht_destroy(tracker->unsuspendable_fds, NULL); - assert(!ret); + if (tracker->unsuspendable_fds) { + ret = cds_lfht_destroy(tracker->unsuspendable_fds, NULL); + assert(!ret); + } + + lttng_inode_registry_destroy(tracker->inode_registry); + lttng_unlinked_file_pool_destroy(tracker->unlinked_file_pool); pthread_mutex_destroy(&tracker->lock); free(tracker); end: return ret; } +LTTNG_HIDDEN struct fs_handle *fd_tracker_open_fs_handle(struct fd_tracker *tracker, - const char *path, int flags, mode_t *mode) + struct lttng_directory_handle *directory, + const char *path, + int flags, + mode_t *mode) { int ret; - struct fs_handle *handle = NULL; + struct fs_handle_tracked *handle = NULL; struct stat fd_stat; struct open_properties properties = { - .path = strdup(path), .flags = flags, .mode.is_set = !!mode, .mode.value = mode ? *mode : 0, }; - if (!properties.path) { - goto end; - } - pthread_mutex_lock(&tracker->lock); if (ACTIVE_COUNT(tracker) == tracker->capacity) { if (tracker->count.suspendable.active > 0) { ret = fd_tracker_suspend_handles(tracker, 1); if (ret) { - goto error_destroy; + goto end; } } else { /* * There are not enough active suspendable file - * descriptors to open a new fd and still accomodate the - * tracker's capacity. + * descriptors to open a new fd and still accommodate + * the tracker's capacity. */ WARN("Cannot open file system handle, too many unsuspendable file descriptors are opened (%u)", tracker->count.unsuspendable); - ret = -EMFILE; - goto error_destroy; + goto end; } } @@ -495,107 +530,132 @@ struct fs_handle *fd_tracker_open_fs_handle(struct fd_tracker *tracker, if (!handle) { goto end; } + handle->parent = (typeof(handle->parent)) { + .get_fd = fs_handle_tracked_get_fd, + .put_fd = fs_handle_tracked_put_fd, + .unlink = fs_handle_tracked_unlink, + .close = fs_handle_tracked_close, + }; + + handle->tracker = tracker; ret = pthread_mutex_init(&handle->lock, NULL); if (ret) { PERROR("Failed to initialize handle mutex while creating fs handle"); - free(handle); - goto end; + goto error_mutex_init; } - handle->fd = open_from_properties(&properties); + handle->fd = open_from_properties(directory, path, &properties); if (handle->fd < 0) { PERROR("Failed to open fs handle to %s, open() returned", path); - ret = -errno; - goto error_destroy; + goto error; } handle->properties = properties; - properties.path = NULL; + + handle->inode = lttng_inode_registry_get_inode(tracker->inode_registry, + directory, path, handle->fd, + tracker->unlinked_file_pool); + if (!handle->inode) { + ERR("Failed to get lttng_inode corresponding to file %s", path); + goto error; + } if (fstat(handle->fd, &fd_stat)) { PERROR("Failed to retrieve file descriptor inode while creating fs handle, fstat() returned"); - ret = -errno; - goto error_destroy; + goto error; } handle->ino = fd_stat.st_ino; fd_tracker_track(tracker, handle); - handle->tracker = tracker; - pthread_mutex_unlock(&tracker->lock); end: - free(properties.path); - return handle; -error_destroy: pthread_mutex_unlock(&tracker->lock); - (void) fs_handle_close(handle); + return handle ? &handle->parent : NULL; +error: + if (handle->inode) { + lttng_inode_put(handle->inode); + } + pthread_mutex_destroy(&handle->lock); +error_mutex_init: + free(handle); handle = NULL; goto end; } /* Caller must hold the tracker's lock. */ -static -int fd_tracker_suspend_handles(struct fd_tracker *tracker, - unsigned int count) +static int fd_tracker_suspend_handles( + struct fd_tracker *tracker, unsigned int count) { unsigned int left_to_close = count; - struct fs_handle *handle, *tmp; + unsigned int attempts_left = tracker->count.suspendable.active; + struct fs_handle_tracked *handle, *tmp; cds_list_for_each_entry_safe(handle, tmp, &tracker->active_handles, handles_list_node) { int ret; fd_tracker_untrack(tracker, handle); - ret = fs_handle_suspend(handle); + ret = fs_handle_tracked_suspend(handle); fd_tracker_track(tracker, handle); if (!ret) { left_to_close--; } + attempts_left--; - if (!left_to_close) { + if (left_to_close == 0 || attempts_left == 0) { break; } } return left_to_close ? -EMFILE : 0; } +LTTNG_HIDDEN int fd_tracker_open_unsuspendable_fd(struct fd_tracker *tracker, - int *out_fds, const char **names, unsigned int fd_count, - fd_open_cb open, void *user_data) + int *out_fds, + const char **names, + unsigned int fd_count, + fd_open_cb open, + void *user_data) { int ret, user_ret, i, fds_to_suspend; unsigned int active_fds; - struct unsuspendable_fd *entries[fd_count]; + struct unsuspendable_fd **entries; - memset(entries, 0, sizeof(entries)); + entries = zmalloc(fd_count * sizeof(*entries)); + if (!entries) { + ret = -1; + goto end; + } pthread_mutex_lock(&tracker->lock); active_fds = ACTIVE_COUNT(tracker); - fds_to_suspend = (int) active_fds + (int) fd_count - (int) tracker->capacity; + fds_to_suspend = (int) active_fds + (int) fd_count - + (int) tracker->capacity; if (fds_to_suspend > 0) { if (fds_to_suspend <= tracker->count.suspendable.active) { - ret = fd_tracker_suspend_handles(tracker, fds_to_suspend); + ret = fd_tracker_suspend_handles( + tracker, fds_to_suspend); if (ret) { - goto end; + goto end_unlock; } } else { /* * There are not enough active suspendable file - * descriptors to open a new fd and still accomodate the + * descriptors to open a new fd and still accommodates the * tracker's capacity. */ WARN("Cannot open unsuspendable fd, too many unsuspendable file descriptors are opened (%u)", tracker->count.unsuspendable); ret = -EMFILE; - goto end; + goto end_unlock; } } user_ret = open(user_data, out_fds); if (user_ret) { ret = user_ret; - goto end; + goto end_unlock; } /* @@ -603,9 +663,8 @@ int fd_tracker_open_unsuspendable_fd(struct fd_tracker *tracker, * of unsuspendable fds. */ for (i = 0; i < fd_count; i++) { - struct unsuspendable_fd *entry = - unsuspendable_fd_create(names ? names[i] : NULL, - out_fds[i]); + struct unsuspendable_fd *entry = unsuspendable_fd_create( + names ? names[i] : NULL, out_fds[i]); if (!entry) { ret = -1; @@ -619,12 +678,11 @@ int fd_tracker_open_unsuspendable_fd(struct fd_tracker *tracker, struct cds_lfht_node *node; struct unsuspendable_fd *entry = entries[i]; - node = cds_lfht_add_unique( - tracker->unsuspendable_fds, - hash_key_ulong((void *) (unsigned long) out_fds[i], + node = cds_lfht_add_unique(tracker->unsuspendable_fds, + hash_key_ulong((void *) (unsigned long) + out_fds[i], seed.value), - match_fd, - (void *) (unsigned long) out_fds[i], + match_fd, (void *) (unsigned long) out_fds[i], &entry->tracker_node); if (node != &entry->tracker_node) { @@ -637,27 +695,37 @@ int fd_tracker_open_unsuspendable_fd(struct fd_tracker *tracker, tracker->count.unsuspendable += fd_count; rcu_read_unlock(); ret = user_ret; -end: +end_unlock: pthread_mutex_unlock(&tracker->lock); +end: + free(entries); return ret; end_free_entries: for (i = 0; i < fd_count; i++) { unsuspendable_fd_destroy(entries[i]); } - goto end; + goto end_unlock; } +LTTNG_HIDDEN int fd_tracker_close_unsuspendable_fd(struct fd_tracker *tracker, - int *fds_in, unsigned int fd_count, fd_close_cb close, + int *fds_in, + unsigned int fd_count, + fd_close_cb close, void *user_data) { int i, ret, user_ret; - int fds[fd_count]; + int *fds = NULL; /* * Maintain a local copy of fds_in as the user's callback may modify its * contents (e.g. setting the fd(s) to -1 after close). */ + fds = malloc(sizeof(*fds) * fd_count); + if (!fds) { + ret = -1; + goto end; + } memcpy(fds, fds_in, sizeof(*fds) * fd_count); pthread_mutex_lock(&tracker->lock); @@ -667,7 +735,7 @@ int fd_tracker_close_unsuspendable_fd(struct fd_tracker *tracker, user_ret = close(user_data, fds_in); if (user_ret) { ret = user_ret; - goto end; + goto end_unlock; } /* Untrack the fds that were just closed by the user's callback. */ @@ -679,8 +747,7 @@ int fd_tracker_close_unsuspendable_fd(struct fd_tracker *tracker, cds_lfht_lookup(tracker->unsuspendable_fds, hash_key_ulong((void *) (unsigned long) fds[i], seed.value), - match_fd, - (void *) (unsigned long) fds[i], + match_fd, (void *) (unsigned long) fds[i], &iter); node = cds_lfht_iter_get_node(&iter); if (!node) { @@ -688,11 +755,10 @@ int fd_tracker_close_unsuspendable_fd(struct fd_tracker *tracker, WARN("Untracked file descriptor %d passed to fd_tracker_close_unsuspendable_fd()", fds[i]); ret = -EINVAL; - goto end; + goto end_unlock; } - entry = caa_container_of(node, - struct unsuspendable_fd, - tracker_node); + entry = caa_container_of( + node, struct unsuspendable_fd, tracker_node); cds_lfht_del(tracker->unsuspendable_fds, node); unsuspendable_fd_destroy(entry); @@ -701,15 +767,17 @@ int fd_tracker_close_unsuspendable_fd(struct fd_tracker *tracker, tracker->count.unsuspendable -= fd_count; ret = 0; -end: +end_unlock: rcu_read_unlock(); pthread_mutex_unlock(&tracker->lock); + free(fds); +end: return ret; } /* Caller must have taken the tracker's and handle's locks. */ -static -void fd_tracker_track(struct fd_tracker *tracker, struct fs_handle *handle) +static void fd_tracker_track( + struct fd_tracker *tracker, struct fs_handle_tracked *handle) { if (handle->fd >= 0) { tracker->count.suspendable.active++; @@ -723,8 +791,8 @@ void fd_tracker_track(struct fd_tracker *tracker, struct fs_handle *handle) } /* Caller must have taken the tracker's and handle's locks. */ -static -void fd_tracker_untrack(struct fd_tracker *tracker, struct fs_handle *handle) +static void fd_tracker_untrack( + struct fd_tracker *tracker, struct fs_handle_tracked *handle) { if (handle->fd >= 0) { tracker->count.suspendable.active--; @@ -735,9 +803,8 @@ void fd_tracker_untrack(struct fd_tracker *tracker, struct fs_handle *handle) } /* Caller must have taken the tracker's and handle's locks. */ -static -int fd_tracker_restore_handle(struct fd_tracker *tracker, - struct fs_handle *handle) +static int fd_tracker_restore_handle( + struct fd_tracker *tracker, struct fs_handle_tracked *handle) { int ret; @@ -748,15 +815,17 @@ int fd_tracker_restore_handle(struct fd_tracker *tracker, goto end; } } - ret = fs_handle_restore(handle); + ret = fs_handle_tracked_restore(handle); end: fd_tracker_track(tracker, handle); return ret ? ret : handle->fd; } -int fs_handle_get_fd(struct fs_handle *handle) +static int fs_handle_tracked_get_fd(struct fs_handle *_handle) { int ret; + struct fs_handle_tracked *handle = + container_of(_handle, struct fs_handle_tracked, parent); /* * TODO This should be optimized as it is a fairly hot path. @@ -794,16 +863,37 @@ end: return ret; } -void fs_handle_put_fd(struct fs_handle *handle) +static void fs_handle_tracked_put_fd(struct fs_handle *_handle) { + struct fs_handle_tracked *handle = + container_of(_handle, struct fs_handle_tracked, parent); + pthread_mutex_lock(&handle->lock); handle->in_use = false; pthread_mutex_unlock(&handle->lock); } -int fs_handle_close(struct fs_handle *handle) +static int fs_handle_tracked_unlink(struct fs_handle *_handle) +{ + int ret; + struct fs_handle_tracked *handle = + container_of(_handle, struct fs_handle_tracked, parent); + + pthread_mutex_lock(&handle->tracker->lock); + pthread_mutex_lock(&handle->lock); + ret = lttng_inode_unlink(handle->inode); + pthread_mutex_unlock(&handle->lock); + pthread_mutex_unlock(&handle->tracker->lock); + return ret; +} + +static int fs_handle_tracked_close(struct fs_handle *_handle) { int ret = 0; + const char *path = NULL; + struct fs_handle_tracked *handle = + container_of(_handle, struct fs_handle_tracked, parent); + struct lttng_directory_handle *inode_directory_handle = NULL; if (!handle) { ret = -EINVAL; @@ -812,6 +902,31 @@ int fs_handle_close(struct fs_handle *handle) pthread_mutex_lock(&handle->tracker->lock); pthread_mutex_lock(&handle->lock); + if (handle->inode) { + lttng_inode_borrow_location(handle->inode, NULL, &path); + /* + * Here a reference to the inode's directory handle is acquired + * to prevent the last reference to it from being released while + * the tracker's lock is taken. + * + * If this wasn't done, the directory handle could attempt to + * close its underlying directory file descriptor, which would + * attempt to lock the tracker's lock, resulting in a deadlock. + * + * Since a new reference to the directory handle is taken within + * the scope of this function, it is not possible for the last + * reference to the inode's location directory handle to be + * released during the call to lttng_inode_put(). + * + * We wait until the tracker's lock is released to release the + * reference. Hence, the call to the tracker is delayed just + * enough to not attempt to recursively acquire the tracker's + * lock twice. + */ + inode_directory_handle = + lttng_inode_get_location_directory_handle( + handle->inode); + } fd_tracker_untrack(handle->tracker, handle); if (handle->fd >= 0) { /* @@ -819,16 +934,19 @@ int fs_handle_close(struct fs_handle *handle) * isn't much the user can do about it. */ if (close(handle->fd)) { - PERROR("Failed to close the file descritptor (%d) of fs handle to %s, close() returned", - handle->fd, handle->properties.path); + PERROR("Failed to close the file descriptor (%d) of fs handle to %s, close() returned", + handle->fd, path ? path : "Unknown"); } handle->fd = -1; } + if (handle->inode) { + lttng_inode_put(handle->inode); + } pthread_mutex_unlock(&handle->lock); pthread_mutex_destroy(&handle->lock); pthread_mutex_unlock(&handle->tracker->lock); - free(handle->properties.path); free(handle); + lttng_directory_handle_put(inode_directory_handle); end: return ret; }