]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blobdiff - kernel/events/core.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
[mirror_ubuntu-jammy-kernel.git] / kernel / events / core.c
index 7b9df353ba1b85e95b220572e5d847b82dee5541..f16f3c58f11ad008d145edc1517a57eccfb03d65 100644 (file)
@@ -468,14 +468,13 @@ static inline int perf_cgroup_connect(int fd, struct perf_event *event,
 {
        struct perf_cgroup *cgrp;
        struct cgroup_subsys_state *css;
-       struct file *file;
-       int ret = 0, fput_needed;
+       struct fd f = fdget(fd);
+       int ret = 0;
 
-       file = fget_light(fd, &fput_needed);
-       if (!file)
+       if (!f.file)
                return -EBADF;
 
-       css = cgroup_css_from_dir(file, perf_subsys_id);
+       css = cgroup_css_from_dir(f.file, perf_subsys_id);
        if (IS_ERR(css)) {
                ret = PTR_ERR(css);
                goto out;
@@ -501,7 +500,7 @@ static inline int perf_cgroup_connect(int fd, struct perf_event *event,
                ret = -EINVAL;
        }
 out:
-       fput_light(file, fput_needed);
+       fdput(f);
        return ret;
 }
 
@@ -3234,21 +3233,18 @@ unlock:
 
 static const struct file_operations perf_fops;
 
-static struct file *perf_fget_light(int fd, int *fput_needed)
+static inline int perf_fget_light(int fd, struct fd *p)
 {
-       struct file *file;
-
-       file = fget_light(fd, fput_needed);
-       if (!file)
-               return ERR_PTR(-EBADF);
+       struct fd f = fdget(fd);
+       if (!f.file)
+               return -EBADF;
 
-       if (file->f_op != &perf_fops) {
-               fput_light(file, *fput_needed);
-               *fput_needed = 0;
-               return ERR_PTR(-EBADF);
+       if (f.file->f_op != &perf_fops) {
+               fdput(f);
+               return -EBADF;
        }
-
-       return file;
+       *p = f;
+       return 0;
 }
 
 static int perf_event_set_output(struct perf_event *event,
@@ -3280,22 +3276,19 @@ static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 
        case PERF_EVENT_IOC_SET_OUTPUT:
        {
-               struct file *output_file = NULL;
-               struct perf_event *output_event = NULL;
-               int fput_needed = 0;
                int ret;
-
                if (arg != -1) {
-                       output_file = perf_fget_light(arg, &fput_needed);
-                       if (IS_ERR(output_file))
-                               return PTR_ERR(output_file);
-                       output_event = output_file->private_data;
+                       struct perf_event *output_event;
+                       struct fd output;
+                       ret = perf_fget_light(arg, &output);
+                       if (ret)
+                               return ret;
+                       output_event = output.file->private_data;
+                       ret = perf_event_set_output(event, output_event);
+                       fdput(output);
+               } else {
+                       ret = perf_event_set_output(event, NULL);
                }
-
-               ret = perf_event_set_output(event, output_event);
-               if (output_event)
-                       fput_light(output_file, fput_needed);
-
                return ret;
        }
 
@@ -6443,12 +6436,11 @@ SYSCALL_DEFINE5(perf_event_open,
        struct perf_event_attr attr;
        struct perf_event_context *ctx;
        struct file *event_file = NULL;
-       struct file *group_file = NULL;
+       struct fd group = {NULL, 0};
        struct task_struct *task = NULL;
        struct pmu *pmu;
        int event_fd;
        int move_group = 0;
-       int fput_needed = 0;
        int err;
 
        /* for future expandability... */
@@ -6478,17 +6470,15 @@ SYSCALL_DEFINE5(perf_event_open,
        if ((flags & PERF_FLAG_PID_CGROUP) && (pid == -1 || cpu == -1))
                return -EINVAL;
 
-       event_fd = get_unused_fd_flags(O_RDWR);
+       event_fd = get_unused_fd();
        if (event_fd < 0)
                return event_fd;
 
        if (group_fd != -1) {
-               group_file = perf_fget_light(group_fd, &fput_needed);
-               if (IS_ERR(group_file)) {
-                       err = PTR_ERR(group_file);
+               err = perf_fget_light(group_fd, &group);
+               if (err)
                        goto err_fd;
-               }
-               group_leader = group_file->private_data;
+               group_leader = group.file->private_data;
                if (flags & PERF_FLAG_FD_OUTPUT)
                        output_event = group_leader;
                if (flags & PERF_FLAG_FD_NO_GROUP)
@@ -6664,7 +6654,7 @@ SYSCALL_DEFINE5(perf_event_open,
         * of the group leader will find the pointer to itself in
         * perf_group_detach().
         */
-       fput_light(group_file, fput_needed);
+       fdput(group);
        fd_install(event_fd, event_file);
        return event_fd;
 
@@ -6678,7 +6668,7 @@ err_task:
        if (task)
                put_task_struct(task);
 err_group_fd:
-       fput_light(group_file, fput_needed);
+       fdput(group);
 err_fd:
        put_unused_fd(event_fd);
        return err;
@@ -7503,5 +7493,12 @@ struct cgroup_subsys perf_subsys = {
        .destroy        = perf_cgroup_destroy,
        .exit           = perf_cgroup_exit,
        .attach         = perf_cgroup_attach,
+
+       /*
+        * perf_event cgroup doesn't handle nesting correctly.
+        * ctx->nr_cgroups adjustments should be propagated through the
+        * cgroup hierarchy.  Fix it and remove the following.
+        */
+       .broken_hierarchy = true,
 };
 #endif /* CONFIG_CGROUP_PERF */