The metadata stream should only reference the metadata cache, not the
session. Otherwise, we end up in a catch 22 situation:
- Stream POLLHUP is only given when the session is destroyed, but,
- The session is only destroyed when all references to session are
released, including references from channels, but,
- If the metadata stream holds a reference on the metadata session, we
end up with a circular dependency loop.
Fix this by making sure the metadata stream does not use any of the
lttng channel nor lttng session.
Reviewed-by: Julien Desfossez <jdesfossez@efficios.com>
Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+/*
+ * This is not used by anonymous file descriptors. This code is left
+ * there if we ever want to implement an inode with open() operation.
+ */
int lib_ring_buffer_open(struct inode *inode, struct file *file,
struct lib_ring_buffer *buf)
{
int lib_ring_buffer_open(struct inode *inode, struct file *file,
struct lib_ring_buffer *buf)
{
struct lttng_metadata_stream *stream = filp->private_data;
struct lib_ring_buffer *buf = stream->priv;
struct channel *chan = buf->backend.chan;
struct lttng_metadata_stream *stream = filp->private_data;
struct lib_ring_buffer *buf = stream->priv;
struct channel *chan = buf->backend.chan;
- struct lttng_channel *lttng_chan = channel_get_private(chan);
- ret = lttng_metadata_output_channel(lttng_chan, stream);
+ ret = lttng_metadata_output_channel(stream, chan);
if (ret > 0) {
lib_ring_buffer_switch_slow(buf, SWITCH_ACTIVE);
ret = 0;
if (ret > 0) {
lib_ring_buffer_switch_slow(buf, SWITCH_ACTIVE);
ret = 0;
+/*
+ * This is not used by anonymous file descriptors. This code is left
+ * there if we ever want to implement an inode with open() operation.
+ */
static
int lttng_metadata_ring_buffer_open(struct inode *inode, struct file *file)
{
static
int lttng_metadata_ring_buffer_open(struct inode *inode, struct file *file)
{
struct lib_ring_buffer *buf = stream->priv;
file->private_data = buf;
struct lib_ring_buffer *buf = stream->priv;
file->private_data = buf;
+ /*
+ * Since life-time of metadata cache differs from that of
+ * session, we need to keep our own reference on the transport.
+ */
+ if (!try_module_get(stream->transport->owner)) {
+ printk(KERN_WARNING "LTT : Can't lock transport module.\n");
+ return -EBUSY;
+ }
return lib_ring_buffer_open(inode, file, buf);
}
return lib_ring_buffer_open(inode, file, buf);
}
{
struct lttng_metadata_stream *stream = file->private_data;
struct lib_ring_buffer *buf = stream->priv;
{
struct lttng_metadata_stream *stream = file->private_data;
struct lib_ring_buffer *buf = stream->priv;
- struct channel *chan = buf->backend.chan;
- struct lttng_channel *lttng_chan = channel_get_private(chan);
kref_put(&stream->metadata_cache->refcount, metadata_cache_destroy);
kref_put(&stream->metadata_cache->refcount, metadata_cache_destroy);
- fput(lttng_chan->file);
-
+ module_put(stream->transport->owner);
return lib_ring_buffer_release(inode, file, buf);
}
return lib_ring_buffer_release(inode, file, buf);
}
metadata_stream = kzalloc(sizeof(struct lttng_metadata_stream),
GFP_KERNEL);
metadata_stream = kzalloc(sizeof(struct lttng_metadata_stream),
GFP_KERNEL);
- if (!metadata_stream)
- return -ENOMEM;
+ if (!metadata_stream) {
+ ret = -ENOMEM;
+ goto nomem;
+ }
metadata_stream->metadata_cache = session->metadata_cache;
init_waitqueue_head(&metadata_stream->read_wait);
metadata_stream->priv = buf;
stream_priv = metadata_stream;
metadata_stream->metadata_cache = session->metadata_cache;
init_waitqueue_head(&metadata_stream->read_wait);
metadata_stream->priv = buf;
stream_priv = metadata_stream;
+ metadata_stream->transport = channel->transport;
+
+ /*
+ * Since life-time of metadata cache differs from that of
+ * session, we need to keep our own reference on the transport.
+ */
+ if (!try_module_get(metadata_stream->transport->owner)) {
+ printk(KERN_WARNING "LTT : Can't lock transport module.\n");
+ ret = -EINVAL;
+ goto notransport;
+ }
+
ret = lttng_abi_create_stream_fd(channel_file, stream_priv,
<tng_metadata_ring_buffer_file_operations);
if (ret < 0)
goto fd_error;
ret = lttng_abi_create_stream_fd(channel_file, stream_priv,
<tng_metadata_ring_buffer_file_operations);
if (ret < 0)
goto fd_error;
- atomic_long_inc(&channel_file->f_count);
kref_get(&session->metadata_cache->refcount);
list_add(&metadata_stream->list,
&session->metadata_cache->metadata_stream);
return ret;
fd_error:
kref_get(&session->metadata_cache->refcount);
list_add(&metadata_stream->list,
&session->metadata_cache->metadata_stream);
return ret;
fd_error:
+ module_put(metadata_stream->transport->owner);
+notransport:
+ kfree(metadata_stream);
+nomem:
channel->ops->buffer_read_close(buf);
return ret;
}
channel->ops->buffer_read_close(buf);
return ret;
}
* remaining space left in packet and write, since mutual exclusion
* protects us from concurrent writes.
*/
* remaining space left in packet and write, since mutual exclusion
* protects us from concurrent writes.
*/
-int lttng_metadata_output_channel(struct lttng_channel *chan,
- struct lttng_metadata_stream *stream)
+int lttng_metadata_output_channel(struct lttng_metadata_stream *stream,
+ struct channel *chan)
{
struct lib_ring_buffer_ctx ctx;
int ret = 0;
{
struct lib_ring_buffer_ctx ctx;
int ret = 0;
if (!len)
return 0;
reserve_len = min_t(size_t,
if (!len)
return 0;
reserve_len = min_t(size_t,
- chan->ops->packet_avail_size(chan->chan),
+ stream->transport->ops.packet_avail_size(chan),
- lib_ring_buffer_ctx_init(&ctx, chan->chan, NULL, reserve_len,
+ lib_ring_buffer_ctx_init(&ctx, chan, NULL, reserve_len,
sizeof(char), -1);
/*
* If reservation failed, return an error to the caller.
*/
sizeof(char), -1);
/*
* If reservation failed, return an error to the caller.
*/
- ret = chan->ops->event_reserve(&ctx, 0);
+ ret = stream->transport->ops.event_reserve(&ctx, 0);
if (ret != 0) {
printk(KERN_WARNING "LTTng: Metadata event reservation failed\n");
goto end;
}
if (ret != 0) {
printk(KERN_WARNING "LTTng: Metadata event reservation failed\n");
goto end;
}
- chan->ops->event_write(&ctx,
+ stream->transport->ops.event_write(&ctx,
stream->metadata_cache->data + stream->metadata_in,
reserve_len);
stream->metadata_cache->data + stream->metadata_in,
reserve_len);
- chan->ops->event_commit(&ctx);
+ stream->transport->ops.event_commit(&ctx);
stream->metadata_in += reserve_len;
ret = reserve_len;
stream->metadata_in += reserve_len;
ret = reserve_len;
int finalized; /* Has channel been finalized */
wait_queue_head_t read_wait; /* Reader buffer-level wait queue */
struct list_head list; /* Stream list */
int finalized; /* Has channel been finalized */
wait_queue_head_t read_wait; /* Reader buffer-level wait queue */
struct list_head list; /* Stream list */
+ struct lttng_transport *transport;
};
struct lttng_session {
};
struct lttng_session {
int lttng_probes_init(void);
void lttng_probes_exit(void);
int lttng_probes_init(void);
void lttng_probes_exit(void);
-int lttng_metadata_output_channel(struct lttng_channel *chan,
- struct lttng_metadata_stream *stream);
+int lttng_metadata_output_channel(struct lttng_metadata_stream *stream,
+ struct channel *chan);
#if defined(CONFIG_HAVE_SYSCALL_TRACEPOINTS)
int lttng_syscalls_register(struct lttng_channel *chan, void *filter);
#if defined(CONFIG_HAVE_SYSCALL_TRACEPOINTS)
int lttng_syscalls_register(struct lttng_channel *chan, void *filter);