+static
+long lttng_counter_ioctl_abi_counter_read(struct lttng_kernel_channel_counter *counter,
+ unsigned int cmd, unsigned long arg)
+{
+ size_t indexes[LTTNG_KERNEL_COUNTER_MAX_DIMENSIONS] = {};
+ struct lttng_kernel_abi_counter_read kcounter_read = {};
+ struct lttng_kernel_abi_counter_read __user *ucounter_read =
+ (struct lttng_kernel_abi_counter_read __user *) arg;
+ uint32_t len, number_dimensions;
+ bool overflow, underflow;
+ int64_t value;
+ int32_t cpu;
+ int ret, i;
+
+ ret = get_user(len, &ucounter_read->len);
+ if (ret)
+ return ret;
+ if (len > PAGE_SIZE)
+ return -E2BIG;
+ if (len < offsetofend(struct lttng_kernel_abi_counter_read, value))
+ return -EINVAL;
+ ret = lttng_copy_struct_from_user(&kcounter_read, sizeof(kcounter_read),
+ ucounter_read, len);
+ if (ret)
+ return ret;
+ number_dimensions = kcounter_read.index.number_dimensions;
+ if (!number_dimensions || number_dimensions > LTTNG_KERNEL_COUNTER_MAX_DIMENSIONS)
+ return -EINVAL;
+ /* Cast all indexes into size_t. */
+ for (i = 0; i < number_dimensions; i++) {
+ uint64_t __user *ptr = ((uint64_t __user *)(unsigned long)kcounter_read.index.ptr) + i;
+ uint64_t index;
+
+ ret = get_user(index, ptr);
+ if (ret)
+ return ret;
+ indexes[i] = index;
+ }
+ cpu = kcounter_read.cpu;
+ ret = lttng_kernel_counter_read(counter, indexes, cpu, &value, &overflow, &underflow);
+ if (ret)
+ return ret;
+ kcounter_read.value.value = value;
+ kcounter_read.value.flags |= underflow ? LTTNG_KERNEL_ABI_COUNTER_VALUE_FLAG_UNDERFLOW : 0;
+ kcounter_read.value.flags |= overflow ? LTTNG_KERNEL_ABI_COUNTER_VALUE_FLAG_OVERFLOW : 0;
+
+ if (copy_to_user(&ucounter_read->value, &kcounter_read.value, sizeof(kcounter_read.value)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static
+long lttng_counter_ioctl_abi_counter_aggregate(struct lttng_kernel_channel_counter *counter,
+ unsigned int cmd, unsigned long arg)
+{
+ size_t indexes[LTTNG_KERNEL_COUNTER_MAX_DIMENSIONS] = {};
+ struct lttng_kernel_abi_counter_aggregate kcounter_aggregate = {};
+ struct lttng_kernel_abi_counter_aggregate __user *ucounter_aggregate =
+ (struct lttng_kernel_abi_counter_aggregate __user *) arg;
+ uint32_t len, number_dimensions;
+ bool overflow, underflow;
+ int64_t value;
+ int ret, i;
+
+ ret = get_user(len, &ucounter_aggregate->len);
+ if (ret)
+ return ret;
+ if (len > PAGE_SIZE)
+ return -E2BIG;
+ if (len < offsetofend(struct lttng_kernel_abi_counter_aggregate, value))
+ return -EINVAL;
+ ret = lttng_copy_struct_from_user(&kcounter_aggregate, sizeof(kcounter_aggregate),
+ ucounter_aggregate, len);
+ if (ret)
+ return ret;
+ number_dimensions = kcounter_aggregate.index.number_dimensions;
+ if (!number_dimensions || number_dimensions > LTTNG_KERNEL_COUNTER_MAX_DIMENSIONS)
+ return -EINVAL;
+ /* Cast all indexes into size_t. */
+ for (i = 0; i < number_dimensions; i++) {
+ uint64_t __user *ptr = ((uint64_t __user *)(unsigned long)kcounter_aggregate.index.ptr) + i;
+ uint64_t index;
+
+ ret = get_user(index, ptr);
+ if (ret)
+ return ret;
+ indexes[i] = index;
+ }
+ ret = lttng_kernel_counter_aggregate(counter, indexes, &value, &overflow, &underflow);
+ if (ret)
+ return ret;
+ kcounter_aggregate.value.value = value;
+ kcounter_aggregate.value.flags |= underflow ? LTTNG_KERNEL_ABI_COUNTER_VALUE_FLAG_UNDERFLOW : 0;
+ kcounter_aggregate.value.flags |= overflow ? LTTNG_KERNEL_ABI_COUNTER_VALUE_FLAG_OVERFLOW : 0;
+
+ if (copy_to_user(&ucounter_aggregate->value, &kcounter_aggregate.value, sizeof(kcounter_aggregate.value)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static
+long lttng_counter_ioctl_abi_counter_clear(struct lttng_kernel_channel_counter *counter,
+ unsigned int cmd, unsigned long arg)
+{
+ size_t indexes[LTTNG_KERNEL_COUNTER_MAX_DIMENSIONS] = {};
+ struct lttng_kernel_abi_counter_clear kcounter_clear = {};
+ struct lttng_kernel_abi_counter_clear __user *ucounter_clear =
+ (struct lttng_kernel_abi_counter_clear __user *) arg;
+ uint32_t len, number_dimensions;
+ int ret, i;
+
+ ret = get_user(len, &ucounter_clear->len);
+ if (ret)
+ return ret;
+ if (len > PAGE_SIZE)
+ return -E2BIG;
+ if (len < offsetofend(struct lttng_kernel_abi_counter_clear, index))
+ return -EINVAL;
+ ret = lttng_copy_struct_from_user(&kcounter_clear, sizeof(kcounter_clear),
+ ucounter_clear, len);
+ if (ret)
+ return ret;
+ number_dimensions = kcounter_clear.index.number_dimensions;
+ if (!number_dimensions || number_dimensions > LTTNG_KERNEL_COUNTER_MAX_DIMENSIONS)
+ return -EINVAL;
+ /* Cast all indexes into size_t. */
+ for (i = 0; i < number_dimensions; i++) {
+ uint64_t __user *ptr = ((uint64_t __user *)(unsigned long)kcounter_clear.index.ptr) + i;
+ uint64_t index;
+
+ ret = get_user(index, ptr);
+ if (ret)
+ return ret;
+ indexes[i] = index;
+ }
+ return lttng_kernel_counter_clear(counter, indexes);
+}
+
+static
+long lttng_counter_ioctl_abi_counter_event(struct file *file,
+ struct lttng_kernel_channel_counter *counter,
+ unsigned int cmd, unsigned long arg)
+{
+ struct lttng_kernel_abi_counter_event __user *ucounter_event =
+ (struct lttng_kernel_abi_counter_event __user *) arg;
+ struct lttng_kernel_abi_counter_event kcounter_event = {};
+ struct lttng_kernel_counter_event *counter_event;
+ uint32_t len;
+ int ret;
+
+ ret = get_user(len, &ucounter_event->len);
+ if (ret)
+ return ret;
+ if (len > PAGE_SIZE)
+ return -E2BIG;
+ if (len < offsetofend(struct lttng_kernel_abi_counter_event, number_key_dimensions))
+ return -EINVAL;
+ counter_event = kzalloc(sizeof(*counter_event), GFP_KERNEL);
+ if (!counter_event)
+ return -ENOMEM;
+ ret = lttng_copy_struct_from_user(&kcounter_event, sizeof(kcounter_event),
+ ucounter_event, len);
+ if (ret)
+ goto end_counter_event;
+ memcpy(&counter_event->event_param, &kcounter_event.event, sizeof(counter_event->event_param));
+ ret = copy_user_event_param_ext(&counter_event->event_param_ext, &kcounter_event.event);
+ if (ret)
+ goto end_counter_event;
+ switch (kcounter_event.action) {
+ case LTTNG_KERNEL_ABI_COUNTER_ACTION_INCREMENT:
+ /* No specific data for this action. */
+ break;
+ default:
+ ret = -EINVAL;
+ goto end_counter_event;
+ }
+ ret = create_counter_key_from_abi_dimensions(&counter_event->counter_key,
+ kcounter_event.number_key_dimensions,
+ (void __user *) arg + len);
+ if (ret)
+ goto end_counter_event;
+ ret = lttng_abi_create_event_counter_enabler(file, counter_event);
+ destroy_counter_key(counter_event->counter_key);
+end_counter_event:
+ kfree(counter_event);
+ return ret;
+}
+
+static
+long lttng_counter_ioctl_abi_counter_map_descriptor(struct lttng_kernel_channel_counter *counter,
+ unsigned int cmd, unsigned long arg)
+{
+ struct lttng_kernel_abi_counter_map_descriptor __user *udescriptor =
+ (struct lttng_kernel_abi_counter_map_descriptor __user *) arg;
+ struct lttng_kernel_abi_counter_map_descriptor kdescriptor = {};
+ struct lttng_counter_map_descriptor *descriptor;
+ char key[LTTNG_KERNEL_COUNTER_KEY_LEN] = {};
+ uint64_t array_indexes[1];
+ size_t key_strlen;
+ uint32_t len;
+ int ret;
+
+ ret = get_user(len, &udescriptor->len);
+ if (ret)
+ return ret;
+ if (len > PAGE_SIZE)
+ return -E2BIG;
+ if (len < offsetofend(struct lttng_kernel_abi_counter_map_descriptor, array_indexes_len))
+ return -EINVAL;
+ ret = lttng_copy_struct_from_user(&kdescriptor, sizeof(kdescriptor), udescriptor, len);
+ if (ret)
+ return ret;
+ mutex_lock(&counter->priv->map.lock);
+ if (kdescriptor.descriptor_index >= counter->priv->map.nr_descriptors) {
+ ret = -EOVERFLOW;
+ goto map_descriptor_error_unlock;
+ }
+ if (kdescriptor.array_indexes_len < 1) {
+ ret = -EINVAL;
+ goto map_descriptor_error_unlock;
+ }
+ kdescriptor.array_indexes_len = 1;
+ descriptor = &counter->priv->map.descriptors[kdescriptor.descriptor_index];
+ kdescriptor.dimension = 0;
+ kdescriptor.user_token = descriptor->user_token;
+ memcpy(&key, descriptor->key, LTTNG_KERNEL_COUNTER_KEY_LEN);
+ array_indexes[0] = descriptor->array_index;
+ mutex_unlock(&counter->priv->map.lock);
+
+ key_strlen = strlen(key) + 1;
+ if (kdescriptor.key_string_len < key_strlen)
+ return -ENOSPC;
+ kdescriptor.key_string_len = key_strlen;
+ if (copy_to_user((char __user *)(unsigned long)kdescriptor.key_string, key, key_strlen))
+ return -EFAULT;
+ if (copy_to_user((uint64_t __user *)(unsigned long)kdescriptor.array_indexes, array_indexes, sizeof(uint64_t)))
+ return -EFAULT;
+ if (copy_to_user(udescriptor, &kdescriptor, min(sizeof(kdescriptor), (size_t)len)))
+ return -EFAULT;
+ return 0;
+
+map_descriptor_error_unlock:
+ mutex_unlock(&counter->priv->map.lock);
+ return ret;
+}
+
+static
+long lttng_counter_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct lttng_kernel_channel_counter *counter = file->private_data;
+
+ switch (cmd) {
+ case LTTNG_KERNEL_ABI_OLD_COUNTER_READ:
+ return lttng_counter_ioctl_abi_old_counter_read(counter, cmd, arg);
+ case LTTNG_KERNEL_ABI_OLD_COUNTER_AGGREGATE:
+ return lttng_counter_ioctl_abi_old_counter_aggregate(counter, cmd, arg);
+ case LTTNG_KERNEL_ABI_OLD_COUNTER_CLEAR:
+ return lttng_counter_ioctl_abi_old_counter_clear(counter, cmd, arg);
+ case LTTNG_KERNEL_ABI_COUNTER_READ:
+ return lttng_counter_ioctl_abi_counter_read(counter, cmd, arg);
+ case LTTNG_KERNEL_ABI_COUNTER_AGGREGATE:
+ return lttng_counter_ioctl_abi_counter_aggregate(counter, cmd, arg);
+ case LTTNG_KERNEL_ABI_COUNTER_CLEAR:
+ return lttng_counter_ioctl_abi_counter_clear(counter, cmd, arg);
+ case LTTNG_KERNEL_ABI_COUNTER_EVENT:
+ return lttng_counter_ioctl_abi_counter_event(file, counter, cmd, arg);
+ case LTTNG_KERNEL_ABI_ENABLE:
+ return lttng_channel_enable(&counter->parent);
+ case LTTNG_KERNEL_ABI_DISABLE:
+ return lttng_channel_disable(&counter->parent);
+ case LTTNG_KERNEL_ABI_SYSCALL_MASK:
+ return lttng_syscall_table_get_active_mask(&counter->priv->parent.syscall_table,
+ (struct lttng_kernel_abi_syscall_mask __user *) arg);
+ case LTTNG_KERNEL_ABI_COUNTER_MAP_NR_DESCRIPTORS:
+ {
+ uint64_t __user *user_nr_descriptors = (uint64_t __user *) arg;
+ uint64_t nr_descriptors;
+
+ mutex_lock(&counter->priv->map.lock);
+ nr_descriptors = counter->priv->map.nr_descriptors;
+ mutex_unlock(&counter->priv->map.lock);
+ return put_user(nr_descriptors, user_nr_descriptors);
+ }
+ case LTTNG_KERNEL_ABI_COUNTER_MAP_DESCRIPTOR:
+ return lttng_counter_ioctl_abi_counter_map_descriptor(counter, cmd, arg);
+ default:
+ return -ENOSYS;
+ }
+}
+
+static const struct file_operations lttng_counter_fops = {
+ .owner = THIS_MODULE,
+ .release = lttng_counter_release,
+ .unlocked_ioctl = lttng_counter_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = lttng_counter_ioctl,
+#endif
+};
+
+
+static
+enum tracker_type get_tracker_type(struct lttng_kernel_abi_tracker_args *tracker)
+{
+ switch (tracker->type) {
+ case LTTNG_KERNEL_ABI_TRACKER_PID:
+ return TRACKER_PID;
+ case LTTNG_KERNEL_ABI_TRACKER_VPID:
+ return TRACKER_VPID;
+ case LTTNG_KERNEL_ABI_TRACKER_UID:
+ return TRACKER_UID;
+ case LTTNG_KERNEL_ABI_TRACKER_VUID:
+ return TRACKER_VUID;
+ case LTTNG_KERNEL_ABI_TRACKER_GID:
+ return TRACKER_GID;
+ case LTTNG_KERNEL_ABI_TRACKER_VGID:
+ return TRACKER_VGID;
+ default:
+ return TRACKER_UNKNOWN;
+ }
+}
+
+static
+int lttng_abi_copy_user_old_counter_conf(struct lttng_kernel_counter_conf *counter_conf,
+ struct lttng_kernel_abi_old_counter_conf __user *old_ucounter_conf)
+{
+ struct lttng_kernel_abi_old_counter_conf old_kcounter_conf;
+ struct lttng_kernel_counter_dimension *dimension;
+ int ret;
+
+ memset(counter_conf, 0, sizeof(*counter_conf));
+ ret = copy_from_user(&old_kcounter_conf, old_ucounter_conf,
+ sizeof(old_kcounter_conf));
+ if (ret)
+ return ret;
+ if (!old_kcounter_conf.number_dimensions ||
+ old_kcounter_conf.number_dimensions > LTTNG_KERNEL_COUNTER_MAX_DIMENSIONS)
+ return -EINVAL;
+ switch (old_kcounter_conf.arithmetic) {
+ case LTTNG_KERNEL_ABI_COUNTER_ARITHMETIC_MODULAR:
+ counter_conf->arithmetic = LTTNG_KERNEL_COUNTER_ARITHMETIC_MODULAR;
+ break;
+ default:
+ return -EINVAL;
+ }
+ switch (old_kcounter_conf.bitness) {
+ case LTTNG_KERNEL_ABI_COUNTER_BITNESS_32:
+ counter_conf->bitness = LTTNG_KERNEL_COUNTER_BITNESS_32;
+ break;
+ case LTTNG_KERNEL_ABI_COUNTER_BITNESS_64:
+ counter_conf->bitness = LTTNG_KERNEL_COUNTER_BITNESS_64;
+ break;
+ default:
+ return -EINVAL;
+ }
+ counter_conf->global_sum_step = old_kcounter_conf.global_sum_step;
+ counter_conf->flags |= old_kcounter_conf.coalesce_hits ?
+ LTTNG_KERNEL_COUNTER_CONF_FLAG_COALESCE_HITS : 0;
+ dimension = &counter_conf->dimension_array[0];
+ dimension->flags |= old_kcounter_conf.dimensions[0].has_underflow ?
+ LTTNG_KERNEL_COUNTER_DIMENSION_FLAG_UNDERFLOW : 0;
+ dimension->flags |= old_kcounter_conf.dimensions[0].has_overflow ?
+ LTTNG_KERNEL_COUNTER_DIMENSION_FLAG_OVERFLOW : 0;
+ dimension->size = old_kcounter_conf.dimensions[0].size;
+ dimension->underflow_index = old_kcounter_conf.dimensions[0].underflow_index;
+ dimension->overflow_index = old_kcounter_conf.dimensions[0].overflow_index;
+ return 0;
+}
+
+static
+int lttng_abi_copy_user_counter_conf(struct lttng_kernel_counter_conf *counter_conf,
+ struct lttng_kernel_abi_counter_conf __user *ucounter_conf)
+{
+ uint32_t len, number_dimensions;
+ struct lttng_kernel_abi_counter_conf kcounter_conf = {};
+ struct lttng_kernel_counter_dimension *dimension;
+ struct lttng_kernel_abi_counter_dimension kdimension = {};
+ struct lttng_kernel_abi_counter_dimension __user *udimension;
+ int ret;
+
+ memset(counter_conf, 0, sizeof(*counter_conf));
+ ret = get_user(len, &ucounter_conf->len);
+ if (ret)
+ return ret;
+ if (len < offsetofend(struct lttng_kernel_abi_counter_conf, dimension_array))
+ return -EINVAL;
+ if (len > PAGE_SIZE)
+ return -EINVAL;
+
+ ret = lttng_copy_struct_from_user(&kcounter_conf, sizeof(kcounter_conf), ucounter_conf, len);
+ if (ret)
+ return ret;
+
+ /* Validate flags and enumerations */
+ switch (kcounter_conf.arithmetic) {
+ case LTTNG_KERNEL_ABI_COUNTER_ARITHMETIC_MODULAR:
+ counter_conf->arithmetic = LTTNG_KERNEL_COUNTER_ARITHMETIC_MODULAR;
+ break;
+ default:
+ return -EINVAL;
+ }
+ switch (kcounter_conf.bitness) {
+ case LTTNG_KERNEL_ABI_COUNTER_BITNESS_32:
+ counter_conf->bitness = LTTNG_KERNEL_COUNTER_BITNESS_32;
+ break;
+ case LTTNG_KERNEL_ABI_COUNTER_BITNESS_64:
+ counter_conf->bitness = LTTNG_KERNEL_COUNTER_BITNESS_64;
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (kcounter_conf.flags & ~LTTNG_KERNEL_ABI_COUNTER_CONF_FLAG_COALESCE_HITS)
+ return -EINVAL;
+ counter_conf->global_sum_step = kcounter_conf.global_sum_step;
+ counter_conf->flags |= (kcounter_conf.flags & LTTNG_KERNEL_ABI_COUNTER_CONF_FLAG_COALESCE_HITS) ?
+ LTTNG_KERNEL_COUNTER_CONF_FLAG_COALESCE_HITS : 0;
+
+ number_dimensions = kcounter_conf.dimension_array.number_dimensions;
+ if (!number_dimensions || number_dimensions > LTTNG_KERNEL_COUNTER_MAX_DIMENSIONS)
+ return -EINVAL;
+ dimension = &counter_conf->dimension_array[0];
+ len = kcounter_conf.dimension_array.elem_len;
+ if (len > PAGE_SIZE)
+ return -E2BIG;
+ if (len < offsetofend(struct lttng_kernel_abi_counter_dimension, overflow_index))
+ return -EINVAL;
+ udimension = (struct lttng_kernel_abi_counter_dimension __user *)(unsigned long)kcounter_conf.dimension_array.ptr;
+ ret = lttng_copy_struct_from_user(&kdimension, sizeof(kdimension), udimension, len);
+ if (ret)
+ return ret;
+
+ /* Validate flags */
+ if (kdimension.flags & ~(LTTNG_KERNEL_ABI_COUNTER_DIMENSION_FLAG_UNDERFLOW |
+ LTTNG_KERNEL_ABI_COUNTER_DIMENSION_FLAG_OVERFLOW))
+ return -EINVAL;
+
+ dimension->flags |= (kdimension.flags & LTTNG_KERNEL_ABI_COUNTER_DIMENSION_FLAG_UNDERFLOW) ?
+ LTTNG_KERNEL_COUNTER_DIMENSION_FLAG_UNDERFLOW : 0;
+ dimension->flags |= (kdimension.flags & LTTNG_KERNEL_ABI_COUNTER_DIMENSION_FLAG_OVERFLOW) ?
+ LTTNG_KERNEL_COUNTER_DIMENSION_FLAG_OVERFLOW : 0;
+ dimension->size = kdimension.size;
+ dimension->underflow_index = kdimension.underflow_index;
+ dimension->overflow_index = kdimension.overflow_index;
+ return 0;
+}
+
+/**
+ * lttng_session_ioctl - lttng session fd ioctl
+ *
+ * @file: the file
+ * @cmd: the command
+ * @arg: command arg
+ *
+ * This ioctl implements lttng commands:
+ * LTTNG_KERNEL_ABI_CHANNEL
+ * Returns a LTTng channel file descriptor
+ * LTTNG_KERNEL_ABI_ENABLE
+ * Enables tracing for a session (weak enable)
+ * LTTNG_KERNEL_ABI_DISABLE
+ * Disables tracing for a session (strong disable)
+ * LTTNG_KERNEL_ABI_METADATA
+ * Returns a LTTng metadata file descriptor
+ * LTTNG_KERNEL_ABI_SESSION_TRACK_PID
+ * Add PID to session PID tracker
+ * LTTNG_KERNEL_ABI_SESSION_UNTRACK_PID
+ * Remove PID from session PID tracker
+ * LTTNG_KERNEL_ABI_SESSION_TRACK_ID
+ * Add ID to tracker
+ * LTTNG_KERNEL_ABI_SESSION_UNTRACK_ID
+ * Remove ID from tracker
+ *
+ * The returned channel will be deleted when its file descriptor is closed.
+ */
+static
+long lttng_session_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct lttng_kernel_session *session = file->private_data;
+ struct lttng_kernel_abi_channel chan_param;
+ struct lttng_kernel_abi_old_channel old_chan_param;
+ int ret;
+
+ /*
+ * Handle backward compatibility. OLD commands have wrong
+ * directions, replace them by the correct direction.
+ */
+ switch (cmd) {
+ case LTTNG_KERNEL_ABI_OLD_SESSION_TRACK_PID:
+ cmd = LTTNG_KERNEL_ABI_SESSION_TRACK_PID;
+ break;
+ case LTTNG_KERNEL_ABI_OLD_SESSION_UNTRACK_PID:
+ cmd = LTTNG_KERNEL_ABI_SESSION_UNTRACK_PID;
+ break;
+ case LTTNG_KERNEL_ABI_OLD_SESSION_TRACK_ID:
+ cmd = LTTNG_KERNEL_ABI_SESSION_TRACK_ID;
+ break;
+ case LTTNG_KERNEL_ABI_OLD_SESSION_UNTRACK_ID:
+ cmd = LTTNG_KERNEL_ABI_SESSION_UNTRACK_ID;
+ break;
+ case LTTNG_KERNEL_ABI_OLD_SESSION_LIST_TRACKER_IDS:
+ cmd = LTTNG_KERNEL_ABI_SESSION_LIST_TRACKER_IDS;
+ break;
+ case LTTNG_KERNEL_ABI_OLD_SESSION_SET_NAME:
+ cmd = LTTNG_KERNEL_ABI_SESSION_SET_NAME;
+ break;
+ case LTTNG_KERNEL_ABI_OLD_SESSION_SET_CREATION_TIME:
+ cmd = LTTNG_KERNEL_ABI_SESSION_SET_CREATION_TIME;
+ break;
+ default:
+ /* Nothing to do. */
+ break;
+ }
+
+ switch (cmd) {
+ case LTTNG_KERNEL_ABI_OLD_CHANNEL:
+ {
+ if (copy_from_user(&old_chan_param,
+ (struct lttng_kernel_abi_old_channel __user *) arg,
+ sizeof(struct lttng_kernel_abi_old_channel)))
+ return -EFAULT;
+ chan_param.overwrite = old_chan_param.overwrite;
+ chan_param.subbuf_size = old_chan_param.subbuf_size;
+ chan_param.num_subbuf = old_chan_param.num_subbuf;
+ chan_param.switch_timer_interval = old_chan_param.switch_timer_interval;
+ chan_param.read_timer_interval = old_chan_param.read_timer_interval;
+ chan_param.output = old_chan_param.output;
+
+ return lttng_abi_create_channel(file, &chan_param,
+ PER_CPU_CHANNEL);
+ }
+ case LTTNG_KERNEL_ABI_CHANNEL:
+ {
+ if (copy_from_user(&chan_param,
+ (struct lttng_kernel_abi_channel __user *) arg,
+ sizeof(struct lttng_kernel_abi_channel)))
+ return -EFAULT;
+ return lttng_abi_create_channel(file, &chan_param,
+ PER_CPU_CHANNEL);
+ }
+ case LTTNG_KERNEL_ABI_OLD_SESSION_START:
+ case LTTNG_KERNEL_ABI_OLD_ENABLE:
+ case LTTNG_KERNEL_ABI_SESSION_START:
+ case LTTNG_KERNEL_ABI_ENABLE:
+ return lttng_session_enable(session);
+ case LTTNG_KERNEL_ABI_OLD_SESSION_STOP:
+ case LTTNG_KERNEL_ABI_OLD_DISABLE:
+ case LTTNG_KERNEL_ABI_SESSION_STOP:
+ case LTTNG_KERNEL_ABI_DISABLE:
+ return lttng_session_disable(session);
+ case LTTNG_KERNEL_ABI_OLD_METADATA:
+ {
+ if (copy_from_user(&old_chan_param,
+ (struct lttng_kernel_abi_old_channel __user *) arg,
+ sizeof(struct lttng_kernel_abi_old_channel)))
+ return -EFAULT;
+ chan_param.overwrite = old_chan_param.overwrite;
+ chan_param.subbuf_size = old_chan_param.subbuf_size;
+ chan_param.num_subbuf = old_chan_param.num_subbuf;
+ chan_param.switch_timer_interval = old_chan_param.switch_timer_interval;
+ chan_param.read_timer_interval = old_chan_param.read_timer_interval;
+ chan_param.output = old_chan_param.output;
+
+ return lttng_abi_create_channel(file, &chan_param,
+ METADATA_CHANNEL);
+ }
+ case LTTNG_KERNEL_ABI_METADATA:
+ {
+ if (copy_from_user(&chan_param,
+ (struct lttng_kernel_abi_channel __user *) arg,
+ sizeof(struct lttng_kernel_abi_channel)))
+ return -EFAULT;
+ return lttng_abi_create_channel(file, &chan_param,
+ METADATA_CHANNEL);
+ }
+ case LTTNG_KERNEL_ABI_SESSION_TRACK_PID:
+ return lttng_session_track_id(session, TRACKER_PID, (int) arg);
+ case LTTNG_KERNEL_ABI_SESSION_UNTRACK_PID:
+ return lttng_session_untrack_id(session, TRACKER_PID, (int) arg);
+ case LTTNG_KERNEL_ABI_SESSION_TRACK_ID:
+ {
+ struct lttng_kernel_abi_tracker_args tracker;
+ enum tracker_type tracker_type;
+
+ if (copy_from_user(&tracker,
+ (struct lttng_kernel_abi_tracker_args __user *) arg,
+ sizeof(struct lttng_kernel_abi_tracker_args)))
+ return -EFAULT;
+ tracker_type = get_tracker_type(&tracker);
+ if (tracker_type == TRACKER_UNKNOWN)
+ return -EINVAL;
+ return lttng_session_track_id(session, tracker_type, tracker.id);
+ }
+ case LTTNG_KERNEL_ABI_SESSION_UNTRACK_ID:
+ {
+ struct lttng_kernel_abi_tracker_args tracker;
+ enum tracker_type tracker_type;
+
+ if (copy_from_user(&tracker,
+ (struct lttng_kernel_abi_tracker_args __user *) arg,
+ sizeof(struct lttng_kernel_abi_tracker_args)))
+ return -EFAULT;
+ tracker_type = get_tracker_type(&tracker);
+ if (tracker_type == TRACKER_UNKNOWN)
+ return -EINVAL;
+ return lttng_session_untrack_id(session, tracker_type,
+ tracker.id);
+ }
+ case LTTNG_KERNEL_ABI_SESSION_LIST_TRACKER_PIDS:
+ return lttng_session_list_tracker_ids(session, TRACKER_PID);
+ case LTTNG_KERNEL_ABI_SESSION_LIST_TRACKER_IDS:
+ {
+ struct lttng_kernel_abi_tracker_args tracker;
+ enum tracker_type tracker_type;
+
+ if (copy_from_user(&tracker,
+ (struct lttng_kernel_abi_tracker_args __user *) arg,
+ sizeof(struct lttng_kernel_abi_tracker_args)))
+ return -EFAULT;
+ tracker_type = get_tracker_type(&tracker);
+ if (tracker_type == TRACKER_UNKNOWN)
+ return -EINVAL;
+ return lttng_session_list_tracker_ids(session, tracker_type);
+ }
+ case LTTNG_KERNEL_ABI_SESSION_METADATA_REGEN:
+ return lttng_session_metadata_regenerate(session);
+ case LTTNG_KERNEL_ABI_SESSION_STATEDUMP:
+ return lttng_session_statedump(session);
+ case LTTNG_KERNEL_ABI_SESSION_SET_NAME:
+ {
+ struct lttng_kernel_abi_session_name name;
+
+ if (copy_from_user(&name,
+ (struct lttng_kernel_abi_session_name __user *) arg,
+ sizeof(struct lttng_kernel_abi_session_name)))
+ return -EFAULT;
+ return lttng_abi_session_set_name(session, &name);
+ }
+ case LTTNG_KERNEL_ABI_SESSION_SET_CREATION_TIME:
+ {
+ struct lttng_kernel_abi_session_creation_time time;
+
+ if (copy_from_user(&time,
+ (struct lttng_kernel_abi_session_creation_time __user *) arg,
+ sizeof(struct lttng_kernel_abi_session_creation_time)))
+ return -EFAULT;
+ return lttng_abi_session_set_creation_time(session, &time);
+ }
+ case LTTNG_KERNEL_ABI_COUNTER:
+ {
+ struct lttng_kernel_counter_conf counter_conf;
+
+ ret = lttng_abi_copy_user_counter_conf(&counter_conf,
+ (struct lttng_kernel_abi_counter_conf __user *) arg);
+ if (ret)
+ return ret;
+ return lttng_abi_session_create_counter(session, &counter_conf);
+ }
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+/*
+ * Called when the last file reference is dropped.
+ *
+ * Big fat note: channels and events are invariant for the whole session after
+ * their creation. So this session destruction also destroys all channel and
+ * event structures specific to this session (they are not destroyed when their
+ * individual file is released).
+ */
+static
+int lttng_session_release(struct inode *inode, struct file *file)
+{
+ struct lttng_kernel_session *session = file->private_data;
+
+ if (session)
+ lttng_session_destroy(session);
+ return 0;
+}
+
+static const struct file_operations lttng_session_fops = {
+ .owner = THIS_MODULE,
+ .release = lttng_session_release,
+ .unlocked_ioctl = lttng_session_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = lttng_session_ioctl,
+#endif
+};
+
+/*
+ * When encountering empty buffer, flush current sub-buffer if non-empty
+ * and retry (if new data available to read after flush).
+ */
+static
+ssize_t lttng_event_notifier_group_notif_read(struct file *filp, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct lttng_event_notifier_group *event_notifier_group = filp->private_data;
+ struct lttng_kernel_ring_buffer_channel *chan = event_notifier_group->chan;
+ struct lttng_kernel_ring_buffer *buf = event_notifier_group->buf;
+ ssize_t read_count = 0, len;
+ size_t read_offset;
+
+ might_sleep();
+ if (!lttng_access_ok(VERIFY_WRITE, user_buf, count))
+ return -EFAULT;
+
+ /* Finish copy of previous record */
+ if (*ppos != 0) {
+ if (count != 0) {
+ len = chan->iter.len_left;
+ read_offset = *ppos;
+ goto skip_get_next;
+ }
+ }
+
+ while (read_count < count) {
+ size_t copy_len, space_left;
+
+ len = lib_ring_buffer_get_next_record(chan, buf);
+len_test:
+ if (len < 0) {
+ /*
+ * Check if buffer is finalized (end of file).