1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * net/core/netclassid_cgroup.c Classid Cgroupfs Handling
4 *
5 * Authors: Thomas Graf <tgraf@suug.ch>
6 */
7
8#include <linux/slab.h>
9#include <linux/cgroup.h>
10#include <linux/fdtable.h>
11#include <linux/sched/task.h>
12
13#include <net/cls_cgroup.h>
14#include <net/sock.h>
15
16static inline struct cgroup_cls_state *css_cls_state(struct cgroup_subsys_state *css)
17{
18 return css ? container_of(css, struct cgroup_cls_state, css) : NULL;
19}
20
21struct cgroup_cls_state *task_cls_state(struct task_struct *p)
22{
23 return css_cls_state(task_css_check(p, net_cls_cgrp_id,
24 rcu_read_lock_held() ||
25 rcu_read_lock_bh_held() ||
26 rcu_read_lock_trace_held()));
27}
28EXPORT_SYMBOL_GPL(task_cls_state);
29
30static struct cgroup_subsys_state *
31cgrp_css_alloc(struct cgroup_subsys_state *parent_css)
32{
33 struct cgroup_cls_state *cs;
34
35 cs = kzalloc(sizeof(*cs), GFP_KERNEL);
36 if (!cs)
37 return ERR_PTR(error: -ENOMEM);
38
39 return &cs->css;
40}
41
42static int cgrp_css_online(struct cgroup_subsys_state *css)
43{
44 struct cgroup_cls_state *cs = css_cls_state(css);
45 struct cgroup_cls_state *parent = css_cls_state(css: css->parent);
46
47 if (parent)
48 cs->classid = parent->classid;
49
50 return 0;
51}
52
53static void cgrp_css_free(struct cgroup_subsys_state *css)
54{
55 kfree(objp: css_cls_state(css));
56}
57
58/*
59 * To avoid freezing of sockets creation for tasks with big number of threads
60 * and opened sockets lets release file_lock every 1000 iterated descriptors.
61 * New sockets will already have been created with new classid.
62 */
63
64struct update_classid_context {
65 u32 classid;
66 unsigned int batch;
67};
68
69#define UPDATE_CLASSID_BATCH 1000
70
71static int update_classid_sock(const void *v, struct file *file, unsigned int n)
72{
73 struct update_classid_context *ctx = (void *)v;
74 struct socket *sock = sock_from_file(file);
75
76 if (sock)
77 sock_cgroup_set_classid(skcd: &sock->sk->sk_cgrp_data, classid: ctx->classid);
78 if (--ctx->batch == 0) {
79 ctx->batch = UPDATE_CLASSID_BATCH;
80 return n + 1;
81 }
82 return 0;
83}
84
85static void update_classid_task(struct task_struct *p, u32 classid)
86{
87 struct update_classid_context ctx = {
88 .classid = classid,
89 .batch = UPDATE_CLASSID_BATCH
90 };
91 unsigned int fd = 0;
92
93 /* Only update the leader task, when many threads in this task,
94 * so it can avoid the useless traversal.
95 */
96 if (p != p->group_leader)
97 return;
98
99 do {
100 task_lock(p);
101 fd = iterate_fd(p->files, fd, update_classid_sock, &ctx);
102 task_unlock(p);
103 cond_resched();
104 } while (fd);
105}
106
107static void cgrp_attach(struct cgroup_taskset *tset)
108{
109 struct cgroup_subsys_state *css;
110 struct task_struct *p;
111
112 cgroup_taskset_for_each(p, css, tset) {
113 update_classid_task(p, classid: css_cls_state(css)->classid);
114 }
115}
116
117static u64 read_classid(struct cgroup_subsys_state *css, struct cftype *cft)
118{
119 return css_cls_state(css)->classid;
120}
121
122static int write_classid(struct cgroup_subsys_state *css, struct cftype *cft,
123 u64 value)
124{
125 struct cgroup_cls_state *cs = css_cls_state(css);
126 struct css_task_iter it;
127 struct task_struct *p;
128
129 cs->classid = (u32)value;
130
131 css_task_iter_start(css, flags: 0, it: &it);
132 while ((p = css_task_iter_next(it: &it)))
133 update_classid_task(p, classid: cs->classid);
134 css_task_iter_end(it: &it);
135
136 return 0;
137}
138
139static struct cftype ss_files[] = {
140 {
141 .name = "classid",
142 .read_u64 = read_classid,
143 .write_u64 = write_classid,
144 },
145 { } /* terminate */
146};
147
148struct cgroup_subsys net_cls_cgrp_subsys = {
149 .css_alloc = cgrp_css_alloc,
150 .css_online = cgrp_css_online,
151 .css_free = cgrp_css_free,
152 .attach = cgrp_attach,
153 .legacy_cftypes = ss_files,
154};
155