+int lttng_counter_release(struct inode *inode, struct file *file)
+{
+ struct lttng_counter *counter = file->private_data;
+
+ if (counter) {
+ /*
+ * Do not destroy the counter itself. Wait of the owner
+ * (event_notifier group) to be destroyed.
+ */
+ fput(counter->owner);
+ }
+
+ return 0;
+}
+
+static
+long lttng_counter_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct lttng_counter *counter = file->private_data;
+ size_t indexes[LTTNG_KERNEL_ABI_COUNTER_DIMENSION_MAX] = { 0 };
+ int i;
+
+ switch (cmd) {
+ case LTTNG_KERNEL_ABI_COUNTER_READ:
+ {
+ struct lttng_kernel_abi_counter_read local_counter_read;
+ struct lttng_kernel_abi_counter_read __user *ucounter_read =
+ (struct lttng_kernel_abi_counter_read __user *) arg;
+ bool overflow, underflow;
+ int64_t value;
+ int32_t cpu;
+ int ret;
+
+ if (copy_from_user(&local_counter_read, ucounter_read,
+ sizeof(local_counter_read)))
+ return -EFAULT;
+ if (validate_zeroed_padding(local_counter_read.padding,
+ sizeof(local_counter_read.padding)))
+ return -EINVAL;
+
+ /* Cast all indexes into size_t. */
+ for (i = 0; i < local_counter_read.index.number_dimensions; i++)
+ indexes[i] = (size_t) local_counter_read.index.dimension_indexes[i];
+ cpu = local_counter_read.cpu;
+
+ ret = lttng_kernel_counter_read(counter, indexes, cpu, &value,
+ &overflow, &underflow);
+ if (ret)
+ return ret;
+ local_counter_read.value.value = value;
+ local_counter_read.value.overflow = overflow;
+ local_counter_read.value.underflow = underflow;
+
+ if (copy_to_user(&ucounter_read->value, &local_counter_read.value,
+ sizeof(local_counter_read.value)))
+ return -EFAULT;
+
+ return 0;
+ }
+ case LTTNG_KERNEL_ABI_COUNTER_AGGREGATE:
+ {
+ struct lttng_kernel_abi_counter_aggregate local_counter_aggregate;
+ struct lttng_kernel_abi_counter_aggregate __user *ucounter_aggregate =
+ (struct lttng_kernel_abi_counter_aggregate __user *) arg;
+ bool overflow, underflow;
+ int64_t value;
+ int ret;
+
+ if (copy_from_user(&local_counter_aggregate, ucounter_aggregate,
+ sizeof(local_counter_aggregate)))
+ return -EFAULT;
+ if (validate_zeroed_padding(local_counter_aggregate.padding,
+ sizeof(local_counter_aggregate.padding)))
+ return -EINVAL;
+
+ /* Cast all indexes into size_t. */
+ for (i = 0; i < local_counter_aggregate.index.number_dimensions; i++)
+ indexes[i] = (size_t) local_counter_aggregate.index.dimension_indexes[i];
+
+ ret = lttng_kernel_counter_aggregate(counter, indexes, &value,
+ &overflow, &underflow);
+ if (ret)
+ return ret;
+ local_counter_aggregate.value.value = value;
+ local_counter_aggregate.value.overflow = overflow;
+ local_counter_aggregate.value.underflow = underflow;
+
+ if (copy_to_user(&ucounter_aggregate->value, &local_counter_aggregate.value,
+ sizeof(local_counter_aggregate.value)))
+ return -EFAULT;
+
+ return 0;
+ }
+ case LTTNG_KERNEL_ABI_COUNTER_CLEAR:
+ {
+ struct lttng_kernel_abi_counter_clear local_counter_clear;
+ struct lttng_kernel_abi_counter_clear __user *ucounter_clear =
+ (struct lttng_kernel_abi_counter_clear __user *) arg;
+
+ if (copy_from_user(&local_counter_clear, ucounter_clear,
+ sizeof(local_counter_clear)))
+ return -EFAULT;
+ if (validate_zeroed_padding(local_counter_clear.padding,
+ sizeof(local_counter_clear.padding)))
+ return -EINVAL;
+
+ /* Cast all indexes into size_t. */
+ for (i = 0; i < local_counter_clear.index.number_dimensions; i++)
+ indexes[i] = (size_t) local_counter_clear.index.dimension_indexes[i];
+
+ return lttng_kernel_counter_clear(counter, indexes);
+ }
+ default:
+ WARN_ON_ONCE(1);
+ 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)