/*
- * Copyright (C) 2018 - Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ * Copyright (C) 2018-2020 Jérémie Galarneau <jeremie.galarneau@efficios.com>
*
- * 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 <urcu.h>
#include <urcu/list.h>
#include <urcu/rculfhash.h>
-#include <sys/stat.h>
-#include <sys/types.h>
#include <fcntl.h>
-#include <stdbool.h>
-#include <pthread.h>
#include <inttypes.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <sys/stat.h>
+#include <sys/types.h>
-#include "common/macros.h"
-#include "common/error.h"
-#include "common/defaults.h"
-#include "common/hashtable/utils.h"
-#include "common/hashtable/hashtable.h"
+#include <common/defaults.h>
+#include <common/error.h>
+#include <common/fs-handle-internal.h>
+#include <common/hashtable/hashtable.h>
+#include <common/hashtable/utils.h>
+#include <common/macros.h>
+#include <common/optional.h>
#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;
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 {
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).
*
* 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
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(const char *path,
- 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;
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;
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);
- path = lttng_inode_get_path(handle->inode);
+ lttng_inode_borrow_location(handle->inode, NULL, &path);
if (handle->fd >= 0) {
- DBG_NO_LOC(" %s [active, fd %d%s]",
- 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]", path);
}
/* Tracker lock must be held by the caller. */
-static
-int fs_handle_suspend(struct fs_handle *handle)
+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);
- path = lttng_inode_get_path(handle->inode);
+ 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. */
goto end;
}
- ret = stat(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",
+ PERROR("Filesystem handle to %s cannot be suspended as stat() failed",
path);
ret = -errno;
goto end;
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",
path);
ret = close(handle->fd);
if (ret) {
- PERROR("Filesystem handle to %s cannot be suspended as close() failed",
+ PERROR("Filesystem handle to %s cannot be suspended as close() failed",
path);
ret = -errno;
goto 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 = lttng_inode_get_path(handle->inode);
+ 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);
assert(path);
- ret = open_from_properties(path,
- &handle->properties);
+ ret = open_from_properties(
+ node_directory_handle, path, &handle->properties);
if (ret < 0) {
- PERROR("Failed to restore filesystem handle to %s, open() failed",
+ PERROR("Failed to restore filesystem handle to %s, open() failed",
path);
ret = -errno;
goto end;
ret = lseek(fd, handle->offset, SEEK_SET);
if (ret < 0) {
- PERROR("Failed to restore filesystem handle to %s, lseek() failed",
+ PERROR("Failed to restore filesystem handle to %s, lseek() failed",
path);
ret = -errno;
goto end;
return ret;
}
-static
-int open_from_properties(const char *path, 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;
* thus it is ignored here.
*/
if ((properties->flags & O_CREAT) && properties->mode.is_set) {
- ret = open(path, properties->flags,
- properties->mode.value);
+ ret = lttng_directory_handle_open_file(dir_handle, path,
+ properties->flags, properties->mode.value);
} else {
- ret = open(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
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));
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 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;
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");
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();
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.
}
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 = {
.flags = flags,
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);
goto error_mutex_init;
}
- handle->fd = open_from_properties(path, &properties);
+ handle->fd = open_from_properties(directory, path, &properties);
if (handle->fd < 0) {
PERROR("Failed to open fs handle to %s, open() returned", path);
goto error;
handle->properties = properties;
handle->inode = lttng_inode_registry_get_inode(tracker->inode_registry,
- handle->fd, path);
+ directory, path, handle->fd,
+ tracker->unlinked_file_pool);
if (!handle->inode) {
- ERR("Failed to get lttng_inode corresponding to file %s",
- path);
+ ERR("Failed to get lttng_inode corresponding to file %s", path);
goto error;
}
fd_tracker_track(tracker, handle);
end:
pthread_mutex_unlock(&tracker->lock);
- return handle;
+ return handle ? &handle->parent : NULL;
error:
if (handle->inode) {
lttng_inode_put(handle->inode);
}
/* 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;
}
/*
* 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;
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) {
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);
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. */
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) {
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);
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++;
}
/* 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--;
}
/* 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;
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.
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_unlink(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_defer_unlink(handle->inode);
+ ret = lttng_inode_unlink(handle->inode);
pthread_mutex_unlock(&handle->lock);
pthread_mutex_unlock(&handle->tracker->lock);
return ret;
}
-int fs_handle_close(struct fs_handle *handle)
+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;
pthread_mutex_lock(&handle->tracker->lock);
pthread_mutex_lock(&handle->lock);
if (handle->inode) {
- path = lttng_inode_get_path(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) {
}
handle->fd = -1;
}
- lttng_inode_put(handle->inode);
+ 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);
+ lttng_directory_handle_put(inode_directory_handle);
end:
return ret;
}