Merge branch 'for-3.7' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/cgroup
Pull cgroup updates from Tejun Heo: - xattr support added. The implementation is shared with tmpfs. The usage is restricted and intended to be used to manage per-cgroup metadata by system software. tmpfs changes are routed through this branch with Hugh's permission. - cgroup subsystem ID handling simplified. * 'for-3.7' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/cgroup: cgroup: Define CGROUP_SUBSYS_COUNT according the configuration cgroup: Assign subsystem IDs during compile time cgroup: Do not depend on a given order when populating the subsys array cgroup: Wrap subsystem selection macro cgroup: Remove CGROUP_BUILTIN_SUBSYS_COUNT cgroup: net_prio: Do not define task_netpioidx() when not selected cgroup: net_cls: Do not define task_cls_classid() when not selected cgroup: net_cls: Move sock_update_classid() declaration to cls_cgroup.h cgroup: trivial fixes for Documentation/cgroups/cgroups.txt xattr: mark variable as uninitialized to make both gcc and smatch happy fs: add missing documentation to simple_xattr functions cgroup: add documentation on extended attributes usage cgroup: rename subsys_bits to subsys_mask cgroup: add xattr support cgroup: revise how we re-populate root directory xattr: extract simple_xattr code from tmpfs
This commit is contained in:
commit
c0e8a139a5
|
@ -29,7 +29,8 @@ CONTENTS:
|
||||||
3.1 Overview
|
3.1 Overview
|
||||||
3.2 Synchronization
|
3.2 Synchronization
|
||||||
3.3 Subsystem API
|
3.3 Subsystem API
|
||||||
4. Questions
|
4. Extended attributes usage
|
||||||
|
5. Questions
|
||||||
|
|
||||||
1. Control Groups
|
1. Control Groups
|
||||||
=================
|
=================
|
||||||
|
@ -62,9 +63,9 @@ an instance of the cgroup virtual filesystem associated with it.
|
||||||
At any one time there may be multiple active hierarchies of task
|
At any one time there may be multiple active hierarchies of task
|
||||||
cgroups. Each hierarchy is a partition of all tasks in the system.
|
cgroups. Each hierarchy is a partition of all tasks in the system.
|
||||||
|
|
||||||
User level code may create and destroy cgroups by name in an
|
User-level code may create and destroy cgroups by name in an
|
||||||
instance of the cgroup virtual file system, specify and query to
|
instance of the cgroup virtual file system, specify and query to
|
||||||
which cgroup a task is assigned, and list the task pids assigned to
|
which cgroup a task is assigned, and list the task PIDs assigned to
|
||||||
a cgroup. Those creations and assignments only affect the hierarchy
|
a cgroup. Those creations and assignments only affect the hierarchy
|
||||||
associated with that instance of the cgroup file system.
|
associated with that instance of the cgroup file system.
|
||||||
|
|
||||||
|
@ -72,7 +73,7 @@ On their own, the only use for cgroups is for simple job
|
||||||
tracking. The intention is that other subsystems hook into the generic
|
tracking. The intention is that other subsystems hook into the generic
|
||||||
cgroup support to provide new attributes for cgroups, such as
|
cgroup support to provide new attributes for cgroups, such as
|
||||||
accounting/limiting the resources which processes in a cgroup can
|
accounting/limiting the resources which processes in a cgroup can
|
||||||
access. For example, cpusets (see Documentation/cgroups/cpusets.txt) allows
|
access. For example, cpusets (see Documentation/cgroups/cpusets.txt) allow
|
||||||
you to associate a set of CPUs and a set of memory nodes with the
|
you to associate a set of CPUs and a set of memory nodes with the
|
||||||
tasks in each cgroup.
|
tasks in each cgroup.
|
||||||
|
|
||||||
|
@ -80,11 +81,11 @@ tasks in each cgroup.
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
||||||
There are multiple efforts to provide process aggregations in the
|
There are multiple efforts to provide process aggregations in the
|
||||||
Linux kernel, mainly for resource tracking purposes. Such efforts
|
Linux kernel, mainly for resource-tracking purposes. Such efforts
|
||||||
include cpusets, CKRM/ResGroups, UserBeanCounters, and virtual server
|
include cpusets, CKRM/ResGroups, UserBeanCounters, and virtual server
|
||||||
namespaces. These all require the basic notion of a
|
namespaces. These all require the basic notion of a
|
||||||
grouping/partitioning of processes, with newly forked processes ending
|
grouping/partitioning of processes, with newly forked processes ending
|
||||||
in the same group (cgroup) as their parent process.
|
up in the same group (cgroup) as their parent process.
|
||||||
|
|
||||||
The kernel cgroup patch provides the minimum essential kernel
|
The kernel cgroup patch provides the minimum essential kernel
|
||||||
mechanisms required to efficiently implement such groups. It has
|
mechanisms required to efficiently implement such groups. It has
|
||||||
|
@ -127,14 +128,14 @@ following lines:
|
||||||
/ \
|
/ \
|
||||||
Professors (15%) students (5%)
|
Professors (15%) students (5%)
|
||||||
|
|
||||||
Browsers like Firefox/Lynx go into the WWW network class, while (k)nfsd go
|
Browsers like Firefox/Lynx go into the WWW network class, while (k)nfsd goes
|
||||||
into NFS network class.
|
into the NFS network class.
|
||||||
|
|
||||||
At the same time Firefox/Lynx will share an appropriate CPU/Memory class
|
At the same time Firefox/Lynx will share an appropriate CPU/Memory class
|
||||||
depending on who launched it (prof/student).
|
depending on who launched it (prof/student).
|
||||||
|
|
||||||
With the ability to classify tasks differently for different resources
|
With the ability to classify tasks differently for different resources
|
||||||
(by putting those resource subsystems in different hierarchies) then
|
(by putting those resource subsystems in different hierarchies),
|
||||||
the admin can easily set up a script which receives exec notifications
|
the admin can easily set up a script which receives exec notifications
|
||||||
and depending on who is launching the browser he can
|
and depending on who is launching the browser he can
|
||||||
|
|
||||||
|
@ -145,19 +146,19 @@ a separate cgroup for every browser launched and associate it with
|
||||||
appropriate network and other resource class. This may lead to
|
appropriate network and other resource class. This may lead to
|
||||||
proliferation of such cgroups.
|
proliferation of such cgroups.
|
||||||
|
|
||||||
Also lets say that the administrator would like to give enhanced network
|
Also let's say that the administrator would like to give enhanced network
|
||||||
access temporarily to a student's browser (since it is night and the user
|
access temporarily to a student's browser (since it is night and the user
|
||||||
wants to do online gaming :)) OR give one of the students simulation
|
wants to do online gaming :)) OR give one of the student's simulation
|
||||||
apps enhanced CPU power,
|
apps enhanced CPU power.
|
||||||
|
|
||||||
With ability to write pids directly to resource classes, it's just a
|
With ability to write PIDs directly to resource classes, it's just a
|
||||||
matter of :
|
matter of:
|
||||||
|
|
||||||
# echo pid > /sys/fs/cgroup/network/<new_class>/tasks
|
# echo pid > /sys/fs/cgroup/network/<new_class>/tasks
|
||||||
(after some time)
|
(after some time)
|
||||||
# echo pid > /sys/fs/cgroup/network/<orig_class>/tasks
|
# echo pid > /sys/fs/cgroup/network/<orig_class>/tasks
|
||||||
|
|
||||||
Without this ability, he would have to split the cgroup into
|
Without this ability, the administrator would have to split the cgroup into
|
||||||
multiple separate ones and then associate the new cgroups with the
|
multiple separate ones and then associate the new cgroups with the
|
||||||
new resource classes.
|
new resource classes.
|
||||||
|
|
||||||
|
@ -184,20 +185,20 @@ Control Groups extends the kernel as follows:
|
||||||
field of each task_struct using the css_set, anchored at
|
field of each task_struct using the css_set, anchored at
|
||||||
css_set->tasks.
|
css_set->tasks.
|
||||||
|
|
||||||
- A cgroup hierarchy filesystem can be mounted for browsing and
|
- A cgroup hierarchy filesystem can be mounted for browsing and
|
||||||
manipulation from user space.
|
manipulation from user space.
|
||||||
|
|
||||||
- You can list all the tasks (by pid) attached to any cgroup.
|
- You can list all the tasks (by PID) attached to any cgroup.
|
||||||
|
|
||||||
The implementation of cgroups requires a few, simple hooks
|
The implementation of cgroups requires a few, simple hooks
|
||||||
into the rest of the kernel, none in performance critical paths:
|
into the rest of the kernel, none in performance-critical paths:
|
||||||
|
|
||||||
- in init/main.c, to initialize the root cgroups and initial
|
- in init/main.c, to initialize the root cgroups and initial
|
||||||
css_set at system boot.
|
css_set at system boot.
|
||||||
|
|
||||||
- in fork and exit, to attach and detach a task from its css_set.
|
- in fork and exit, to attach and detach a task from its css_set.
|
||||||
|
|
||||||
In addition a new file system, of type "cgroup" may be mounted, to
|
In addition, a new file system of type "cgroup" may be mounted, to
|
||||||
enable browsing and modifying the cgroups presently known to the
|
enable browsing and modifying the cgroups presently known to the
|
||||||
kernel. When mounting a cgroup hierarchy, you may specify a
|
kernel. When mounting a cgroup hierarchy, you may specify a
|
||||||
comma-separated list of subsystems to mount as the filesystem mount
|
comma-separated list of subsystems to mount as the filesystem mount
|
||||||
|
@ -230,13 +231,13 @@ as the path relative to the root of the cgroup file system.
|
||||||
Each cgroup is represented by a directory in the cgroup file system
|
Each cgroup is represented by a directory in the cgroup file system
|
||||||
containing the following files describing that cgroup:
|
containing the following files describing that cgroup:
|
||||||
|
|
||||||
- tasks: list of tasks (by pid) attached to that cgroup. This list
|
- tasks: list of tasks (by PID) attached to that cgroup. This list
|
||||||
is not guaranteed to be sorted. Writing a thread id into this file
|
is not guaranteed to be sorted. Writing a thread ID into this file
|
||||||
moves the thread into this cgroup.
|
moves the thread into this cgroup.
|
||||||
- cgroup.procs: list of tgids in the cgroup. This list is not
|
- cgroup.procs: list of thread group IDs in the cgroup. This list is
|
||||||
guaranteed to be sorted or free of duplicate tgids, and userspace
|
not guaranteed to be sorted or free of duplicate TGIDs, and userspace
|
||||||
should sort/uniquify the list if this property is required.
|
should sort/uniquify the list if this property is required.
|
||||||
Writing a thread group id into this file moves all threads in that
|
Writing a thread group ID into this file moves all threads in that
|
||||||
group into this cgroup.
|
group into this cgroup.
|
||||||
- notify_on_release flag: run the release agent on exit?
|
- notify_on_release flag: run the release agent on exit?
|
||||||
- release_agent: the path to use for release notifications (this file
|
- release_agent: the path to use for release notifications (this file
|
||||||
|
@ -261,7 +262,7 @@ cgroup file system directories.
|
||||||
|
|
||||||
When a task is moved from one cgroup to another, it gets a new
|
When a task is moved from one cgroup to another, it gets a new
|
||||||
css_set pointer - if there's an already existing css_set with the
|
css_set pointer - if there's an already existing css_set with the
|
||||||
desired collection of cgroups then that group is reused, else a new
|
desired collection of cgroups then that group is reused, otherwise a new
|
||||||
css_set is allocated. The appropriate existing css_set is located by
|
css_set is allocated. The appropriate existing css_set is located by
|
||||||
looking into a hash table.
|
looking into a hash table.
|
||||||
|
|
||||||
|
@ -292,7 +293,7 @@ file system) of the abandoned cgroup. This enables automatic
|
||||||
removal of abandoned cgroups. The default value of
|
removal of abandoned cgroups. The default value of
|
||||||
notify_on_release in the root cgroup at system boot is disabled
|
notify_on_release in the root cgroup at system boot is disabled
|
||||||
(0). The default value of other cgroups at creation is the current
|
(0). The default value of other cgroups at creation is the current
|
||||||
value of their parents notify_on_release setting. The default value of
|
value of their parents' notify_on_release settings. The default value of
|
||||||
a cgroup hierarchy's release_agent path is empty.
|
a cgroup hierarchy's release_agent path is empty.
|
||||||
|
|
||||||
1.5 What does clone_children do ?
|
1.5 What does clone_children do ?
|
||||||
|
@ -316,7 +317,7 @@ the "cpuset" cgroup subsystem, the steps are something like:
|
||||||
4) Create the new cgroup by doing mkdir's and write's (or echo's) in
|
4) Create the new cgroup by doing mkdir's and write's (or echo's) in
|
||||||
the /sys/fs/cgroup virtual file system.
|
the /sys/fs/cgroup virtual file system.
|
||||||
5) Start a task that will be the "founding father" of the new job.
|
5) Start a task that will be the "founding father" of the new job.
|
||||||
6) Attach that task to the new cgroup by writing its pid to the
|
6) Attach that task to the new cgroup by writing its PID to the
|
||||||
/sys/fs/cgroup/cpuset/tasks file for that cgroup.
|
/sys/fs/cgroup/cpuset/tasks file for that cgroup.
|
||||||
7) fork, exec or clone the job tasks from this founding father task.
|
7) fork, exec or clone the job tasks from this founding father task.
|
||||||
|
|
||||||
|
@ -344,7 +345,7 @@ and then start a subshell 'sh' in that cgroup:
|
||||||
2.1 Basic Usage
|
2.1 Basic Usage
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
Creating, modifying, using the cgroups can be done through the cgroup
|
Creating, modifying, using cgroups can be done through the cgroup
|
||||||
virtual filesystem.
|
virtual filesystem.
|
||||||
|
|
||||||
To mount a cgroup hierarchy with all available subsystems, type:
|
To mount a cgroup hierarchy with all available subsystems, type:
|
||||||
|
@ -441,7 +442,7 @@ You can attach the current shell task by echoing 0:
|
||||||
# echo 0 > tasks
|
# echo 0 > tasks
|
||||||
|
|
||||||
You can use the cgroup.procs file instead of the tasks file to move all
|
You can use the cgroup.procs file instead of the tasks file to move all
|
||||||
threads in a threadgroup at once. Echoing the pid of any task in a
|
threads in a threadgroup at once. Echoing the PID of any task in a
|
||||||
threadgroup to cgroup.procs causes all tasks in that threadgroup to be
|
threadgroup to cgroup.procs causes all tasks in that threadgroup to be
|
||||||
be attached to the cgroup. Writing 0 to cgroup.procs moves all tasks
|
be attached to the cgroup. Writing 0 to cgroup.procs moves all tasks
|
||||||
in the writing task's threadgroup.
|
in the writing task's threadgroup.
|
||||||
|
@ -479,7 +480,7 @@ in /proc/mounts and /proc/<pid>/cgroups.
|
||||||
There is mechanism which allows to get notifications about changing
|
There is mechanism which allows to get notifications about changing
|
||||||
status of a cgroup.
|
status of a cgroup.
|
||||||
|
|
||||||
To register new notification handler you need:
|
To register a new notification handler you need to:
|
||||||
- create a file descriptor for event notification using eventfd(2);
|
- create a file descriptor for event notification using eventfd(2);
|
||||||
- open a control file to be monitored (e.g. memory.usage_in_bytes);
|
- open a control file to be monitored (e.g. memory.usage_in_bytes);
|
||||||
- write "<event_fd> <control_fd> <args>" to cgroup.event_control.
|
- write "<event_fd> <control_fd> <args>" to cgroup.event_control.
|
||||||
|
@ -488,7 +489,7 @@ To register new notification handler you need:
|
||||||
eventfd will be woken up by control file implementation or when the
|
eventfd will be woken up by control file implementation or when the
|
||||||
cgroup is removed.
|
cgroup is removed.
|
||||||
|
|
||||||
To unregister notification handler just close eventfd.
|
To unregister a notification handler just close eventfd.
|
||||||
|
|
||||||
NOTE: Support of notifications should be implemented for the control
|
NOTE: Support of notifications should be implemented for the control
|
||||||
file. See documentation for the subsystem.
|
file. See documentation for the subsystem.
|
||||||
|
@ -502,7 +503,7 @@ file. See documentation for the subsystem.
|
||||||
Each kernel subsystem that wants to hook into the generic cgroup
|
Each kernel subsystem that wants to hook into the generic cgroup
|
||||||
system needs to create a cgroup_subsys object. This contains
|
system needs to create a cgroup_subsys object. This contains
|
||||||
various methods, which are callbacks from the cgroup system, along
|
various methods, which are callbacks from the cgroup system, along
|
||||||
with a subsystem id which will be assigned by the cgroup system.
|
with a subsystem ID which will be assigned by the cgroup system.
|
||||||
|
|
||||||
Other fields in the cgroup_subsys object include:
|
Other fields in the cgroup_subsys object include:
|
||||||
|
|
||||||
|
@ -516,7 +517,7 @@ Other fields in the cgroup_subsys object include:
|
||||||
at system boot.
|
at system boot.
|
||||||
|
|
||||||
Each cgroup object created by the system has an array of pointers,
|
Each cgroup object created by the system has an array of pointers,
|
||||||
indexed by subsystem id; this pointer is entirely managed by the
|
indexed by subsystem ID; this pointer is entirely managed by the
|
||||||
subsystem; the generic cgroup code will never touch this pointer.
|
subsystem; the generic cgroup code will never touch this pointer.
|
||||||
|
|
||||||
3.2 Synchronization
|
3.2 Synchronization
|
||||||
|
@ -639,7 +640,7 @@ void post_clone(struct cgroup *cgrp)
|
||||||
|
|
||||||
Called during cgroup_create() to do any parameter
|
Called during cgroup_create() to do any parameter
|
||||||
initialization which might be required before a task could attach. For
|
initialization which might be required before a task could attach. For
|
||||||
example in cpusets, no task may attach before 'cpus' and 'mems' are set
|
example, in cpusets, no task may attach before 'cpus' and 'mems' are set
|
||||||
up.
|
up.
|
||||||
|
|
||||||
void bind(struct cgroup *root)
|
void bind(struct cgroup *root)
|
||||||
|
@ -650,7 +651,26 @@ and root cgroup. Currently this will only involve movement between
|
||||||
the default hierarchy (which never has sub-cgroups) and a hierarchy
|
the default hierarchy (which never has sub-cgroups) and a hierarchy
|
||||||
that is being created/destroyed (and hence has no sub-cgroups).
|
that is being created/destroyed (and hence has no sub-cgroups).
|
||||||
|
|
||||||
4. Questions
|
4. Extended attribute usage
|
||||||
|
===========================
|
||||||
|
|
||||||
|
cgroup filesystem supports certain types of extended attributes in its
|
||||||
|
directories and files. The current supported types are:
|
||||||
|
- Trusted (XATTR_TRUSTED)
|
||||||
|
- Security (XATTR_SECURITY)
|
||||||
|
|
||||||
|
Both require CAP_SYS_ADMIN capability to set.
|
||||||
|
|
||||||
|
Like in tmpfs, the extended attributes in cgroup filesystem are stored
|
||||||
|
using kernel memory and it's advised to keep the usage at minimum. This
|
||||||
|
is the reason why user defined extended attributes are not supported, since
|
||||||
|
any user can do it and there's no limit in the value size.
|
||||||
|
|
||||||
|
The current known users for this feature are SELinux to limit cgroup usage
|
||||||
|
in containers and systemd for assorted meta data like main PID in a cgroup
|
||||||
|
(systemd creates a cgroup per service).
|
||||||
|
|
||||||
|
5. Questions
|
||||||
============
|
============
|
||||||
|
|
||||||
Q: what's up with this '/bin/echo' ?
|
Q: what's up with this '/bin/echo' ?
|
||||||
|
@ -660,5 +680,5 @@ A: bash's builtin 'echo' command does not check calls to write() against
|
||||||
|
|
||||||
Q: When I attach processes, only the first of the line gets really attached !
|
Q: When I attach processes, only the first of the line gets really attached !
|
||||||
A: We can only return one error code per call to write(). So you should also
|
A: We can only return one error code per call to write(). So you should also
|
||||||
put only ONE pid.
|
put only ONE PID.
|
||||||
|
|
||||||
|
|
|
@ -68,6 +68,7 @@
|
||||||
#include <net/netns/generic.h>
|
#include <net/netns/generic.h>
|
||||||
#include <net/rtnetlink.h>
|
#include <net/rtnetlink.h>
|
||||||
#include <net/sock.h>
|
#include <net/sock.h>
|
||||||
|
#include <net/cls_cgroup.h>
|
||||||
|
|
||||||
#include <asm/uaccess.h>
|
#include <asm/uaccess.h>
|
||||||
|
|
||||||
|
|
180
fs/xattr.c
180
fs/xattr.c
|
@ -791,3 +791,183 @@ EXPORT_SYMBOL(generic_getxattr);
|
||||||
EXPORT_SYMBOL(generic_listxattr);
|
EXPORT_SYMBOL(generic_listxattr);
|
||||||
EXPORT_SYMBOL(generic_setxattr);
|
EXPORT_SYMBOL(generic_setxattr);
|
||||||
EXPORT_SYMBOL(generic_removexattr);
|
EXPORT_SYMBOL(generic_removexattr);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allocate new xattr and copy in the value; but leave the name to callers.
|
||||||
|
*/
|
||||||
|
struct simple_xattr *simple_xattr_alloc(const void *value, size_t size)
|
||||||
|
{
|
||||||
|
struct simple_xattr *new_xattr;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
/* wrap around? */
|
||||||
|
len = sizeof(*new_xattr) + size;
|
||||||
|
if (len <= sizeof(*new_xattr))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
new_xattr = kmalloc(len, GFP_KERNEL);
|
||||||
|
if (!new_xattr)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
new_xattr->size = size;
|
||||||
|
memcpy(new_xattr->value, value, size);
|
||||||
|
return new_xattr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* xattr GET operation for in-memory/pseudo filesystems
|
||||||
|
*/
|
||||||
|
int simple_xattr_get(struct simple_xattrs *xattrs, const char *name,
|
||||||
|
void *buffer, size_t size)
|
||||||
|
{
|
||||||
|
struct simple_xattr *xattr;
|
||||||
|
int ret = -ENODATA;
|
||||||
|
|
||||||
|
spin_lock(&xattrs->lock);
|
||||||
|
list_for_each_entry(xattr, &xattrs->head, list) {
|
||||||
|
if (strcmp(name, xattr->name))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ret = xattr->size;
|
||||||
|
if (buffer) {
|
||||||
|
if (size < xattr->size)
|
||||||
|
ret = -ERANGE;
|
||||||
|
else
|
||||||
|
memcpy(buffer, xattr->value, xattr->size);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
spin_unlock(&xattrs->lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __simple_xattr_set(struct simple_xattrs *xattrs, const char *name,
|
||||||
|
const void *value, size_t size, int flags)
|
||||||
|
{
|
||||||
|
struct simple_xattr *xattr;
|
||||||
|
struct simple_xattr *uninitialized_var(new_xattr);
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
/* value == NULL means remove */
|
||||||
|
if (value) {
|
||||||
|
new_xattr = simple_xattr_alloc(value, size);
|
||||||
|
if (!new_xattr)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
new_xattr->name = kstrdup(name, GFP_KERNEL);
|
||||||
|
if (!new_xattr->name) {
|
||||||
|
kfree(new_xattr);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_lock(&xattrs->lock);
|
||||||
|
list_for_each_entry(xattr, &xattrs->head, list) {
|
||||||
|
if (!strcmp(name, xattr->name)) {
|
||||||
|
if (flags & XATTR_CREATE) {
|
||||||
|
xattr = new_xattr;
|
||||||
|
err = -EEXIST;
|
||||||
|
} else if (new_xattr) {
|
||||||
|
list_replace(&xattr->list, &new_xattr->list);
|
||||||
|
} else {
|
||||||
|
list_del(&xattr->list);
|
||||||
|
}
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (flags & XATTR_REPLACE) {
|
||||||
|
xattr = new_xattr;
|
||||||
|
err = -ENODATA;
|
||||||
|
} else {
|
||||||
|
list_add(&new_xattr->list, &xattrs->head);
|
||||||
|
xattr = NULL;
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
spin_unlock(&xattrs->lock);
|
||||||
|
if (xattr) {
|
||||||
|
kfree(xattr->name);
|
||||||
|
kfree(xattr);
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* simple_xattr_set - xattr SET operation for in-memory/pseudo filesystems
|
||||||
|
* @xattrs: target simple_xattr list
|
||||||
|
* @name: name of the new extended attribute
|
||||||
|
* @value: value of the new xattr. If %NULL, will remove the attribute
|
||||||
|
* @size: size of the new xattr
|
||||||
|
* @flags: %XATTR_{CREATE|REPLACE}
|
||||||
|
*
|
||||||
|
* %XATTR_CREATE is set, the xattr shouldn't exist already; otherwise fails
|
||||||
|
* with -EEXIST. If %XATTR_REPLACE is set, the xattr should exist;
|
||||||
|
* otherwise, fails with -ENODATA.
|
||||||
|
*
|
||||||
|
* Returns 0 on success, -errno on failure.
|
||||||
|
*/
|
||||||
|
int simple_xattr_set(struct simple_xattrs *xattrs, const char *name,
|
||||||
|
const void *value, size_t size, int flags)
|
||||||
|
{
|
||||||
|
if (size == 0)
|
||||||
|
value = ""; /* empty EA, do not remove */
|
||||||
|
return __simple_xattr_set(xattrs, name, value, size, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* xattr REMOVE operation for in-memory/pseudo filesystems
|
||||||
|
*/
|
||||||
|
int simple_xattr_remove(struct simple_xattrs *xattrs, const char *name)
|
||||||
|
{
|
||||||
|
return __simple_xattr_set(xattrs, name, NULL, 0, XATTR_REPLACE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool xattr_is_trusted(const char *name)
|
||||||
|
{
|
||||||
|
return !strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* xattr LIST operation for in-memory/pseudo filesystems
|
||||||
|
*/
|
||||||
|
ssize_t simple_xattr_list(struct simple_xattrs *xattrs, char *buffer,
|
||||||
|
size_t size)
|
||||||
|
{
|
||||||
|
bool trusted = capable(CAP_SYS_ADMIN);
|
||||||
|
struct simple_xattr *xattr;
|
||||||
|
size_t used = 0;
|
||||||
|
|
||||||
|
spin_lock(&xattrs->lock);
|
||||||
|
list_for_each_entry(xattr, &xattrs->head, list) {
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
/* skip "trusted." attributes for unprivileged callers */
|
||||||
|
if (!trusted && xattr_is_trusted(xattr->name))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
len = strlen(xattr->name) + 1;
|
||||||
|
used += len;
|
||||||
|
if (buffer) {
|
||||||
|
if (size < used) {
|
||||||
|
used = -ERANGE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
memcpy(buffer, xattr->name, len);
|
||||||
|
buffer += len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spin_unlock(&xattrs->lock);
|
||||||
|
|
||||||
|
return used;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Adds an extended attribute to the list
|
||||||
|
*/
|
||||||
|
void simple_xattr_list_add(struct simple_xattrs *xattrs,
|
||||||
|
struct simple_xattr *new_xattr)
|
||||||
|
{
|
||||||
|
spin_lock(&xattrs->lock);
|
||||||
|
list_add(&new_xattr->list, &xattrs->head);
|
||||||
|
spin_unlock(&xattrs->lock);
|
||||||
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include <linux/rwsem.h>
|
#include <linux/rwsem.h>
|
||||||
#include <linux/idr.h>
|
#include <linux/idr.h>
|
||||||
#include <linux/workqueue.h>
|
#include <linux/workqueue.h>
|
||||||
|
#include <linux/xattr.h>
|
||||||
|
|
||||||
#ifdef CONFIG_CGROUPS
|
#ifdef CONFIG_CGROUPS
|
||||||
|
|
||||||
|
@ -45,17 +46,13 @@ extern const struct file_operations proc_cgroup_operations;
|
||||||
|
|
||||||
/* Define the enumeration of all builtin cgroup subsystems */
|
/* Define the enumeration of all builtin cgroup subsystems */
|
||||||
#define SUBSYS(_x) _x ## _subsys_id,
|
#define SUBSYS(_x) _x ## _subsys_id,
|
||||||
|
#define IS_SUBSYS_ENABLED(option) IS_ENABLED(option)
|
||||||
enum cgroup_subsys_id {
|
enum cgroup_subsys_id {
|
||||||
#include <linux/cgroup_subsys.h>
|
#include <linux/cgroup_subsys.h>
|
||||||
CGROUP_BUILTIN_SUBSYS_COUNT
|
CGROUP_SUBSYS_COUNT,
|
||||||
};
|
};
|
||||||
|
#undef IS_SUBSYS_ENABLED
|
||||||
#undef SUBSYS
|
#undef SUBSYS
|
||||||
/*
|
|
||||||
* This define indicates the maximum number of subsystems that can be loaded
|
|
||||||
* at once. We limit to this many since cgroupfs_root has subsys_bits to keep
|
|
||||||
* track of all of them.
|
|
||||||
*/
|
|
||||||
#define CGROUP_SUBSYS_COUNT (BITS_PER_BYTE*sizeof(unsigned long))
|
|
||||||
|
|
||||||
/* Per-subsystem/per-cgroup state maintained by the system. */
|
/* Per-subsystem/per-cgroup state maintained by the system. */
|
||||||
struct cgroup_subsys_state {
|
struct cgroup_subsys_state {
|
||||||
|
@ -216,6 +213,9 @@ struct cgroup {
|
||||||
/* List of events which userspace want to receive */
|
/* List of events which userspace want to receive */
|
||||||
struct list_head event_list;
|
struct list_head event_list;
|
||||||
spinlock_t event_list_lock;
|
spinlock_t event_list_lock;
|
||||||
|
|
||||||
|
/* directory xattrs */
|
||||||
|
struct simple_xattrs xattrs;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -309,6 +309,9 @@ struct cftype {
|
||||||
/* CFTYPE_* flags */
|
/* CFTYPE_* flags */
|
||||||
unsigned int flags;
|
unsigned int flags;
|
||||||
|
|
||||||
|
/* file xattrs */
|
||||||
|
struct simple_xattrs xattrs;
|
||||||
|
|
||||||
int (*open)(struct inode *inode, struct file *file);
|
int (*open)(struct inode *inode, struct file *file);
|
||||||
ssize_t (*read)(struct cgroup *cgrp, struct cftype *cft,
|
ssize_t (*read)(struct cgroup *cgrp, struct cftype *cft,
|
||||||
struct file *file,
|
struct file *file,
|
||||||
|
@ -394,7 +397,7 @@ struct cftype {
|
||||||
*/
|
*/
|
||||||
struct cftype_set {
|
struct cftype_set {
|
||||||
struct list_head node; /* chained at subsys->cftsets */
|
struct list_head node; /* chained at subsys->cftsets */
|
||||||
const struct cftype *cfts;
|
struct cftype *cfts;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct cgroup_scanner {
|
struct cgroup_scanner {
|
||||||
|
@ -406,8 +409,8 @@ struct cgroup_scanner {
|
||||||
void *data;
|
void *data;
|
||||||
};
|
};
|
||||||
|
|
||||||
int cgroup_add_cftypes(struct cgroup_subsys *ss, const struct cftype *cfts);
|
int cgroup_add_cftypes(struct cgroup_subsys *ss, struct cftype *cfts);
|
||||||
int cgroup_rm_cftypes(struct cgroup_subsys *ss, const struct cftype *cfts);
|
int cgroup_rm_cftypes(struct cgroup_subsys *ss, struct cftype *cfts);
|
||||||
|
|
||||||
int cgroup_is_removed(const struct cgroup *cgrp);
|
int cgroup_is_removed(const struct cgroup *cgrp);
|
||||||
|
|
||||||
|
@ -521,7 +524,9 @@ struct cgroup_subsys {
|
||||||
};
|
};
|
||||||
|
|
||||||
#define SUBSYS(_x) extern struct cgroup_subsys _x ## _subsys;
|
#define SUBSYS(_x) extern struct cgroup_subsys _x ## _subsys;
|
||||||
|
#define IS_SUBSYS_ENABLED(option) IS_BUILTIN(option)
|
||||||
#include <linux/cgroup_subsys.h>
|
#include <linux/cgroup_subsys.h>
|
||||||
|
#undef IS_SUBSYS_ENABLED
|
||||||
#undef SUBSYS
|
#undef SUBSYS
|
||||||
|
|
||||||
static inline struct cgroup_subsys_state *cgroup_subsys_state(
|
static inline struct cgroup_subsys_state *cgroup_subsys_state(
|
||||||
|
|
|
@ -7,73 +7,73 @@
|
||||||
|
|
||||||
/* */
|
/* */
|
||||||
|
|
||||||
#ifdef CONFIG_CPUSETS
|
#if IS_SUBSYS_ENABLED(CONFIG_CPUSETS)
|
||||||
SUBSYS(cpuset)
|
SUBSYS(cpuset)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* */
|
/* */
|
||||||
|
|
||||||
#ifdef CONFIG_CGROUP_DEBUG
|
#if IS_SUBSYS_ENABLED(CONFIG_CGROUP_DEBUG)
|
||||||
SUBSYS(debug)
|
SUBSYS(debug)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* */
|
/* */
|
||||||
|
|
||||||
#ifdef CONFIG_CGROUP_SCHED
|
#if IS_SUBSYS_ENABLED(CONFIG_CGROUP_SCHED)
|
||||||
SUBSYS(cpu_cgroup)
|
SUBSYS(cpu_cgroup)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* */
|
/* */
|
||||||
|
|
||||||
#ifdef CONFIG_CGROUP_CPUACCT
|
#if IS_SUBSYS_ENABLED(CONFIG_CGROUP_CPUACCT)
|
||||||
SUBSYS(cpuacct)
|
SUBSYS(cpuacct)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* */
|
/* */
|
||||||
|
|
||||||
#ifdef CONFIG_MEMCG
|
#if IS_SUBSYS_ENABLED(CONFIG_MEMCG)
|
||||||
SUBSYS(mem_cgroup)
|
SUBSYS(mem_cgroup)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* */
|
/* */
|
||||||
|
|
||||||
#ifdef CONFIG_CGROUP_DEVICE
|
#if IS_SUBSYS_ENABLED(CONFIG_CGROUP_DEVICE)
|
||||||
SUBSYS(devices)
|
SUBSYS(devices)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* */
|
/* */
|
||||||
|
|
||||||
#ifdef CONFIG_CGROUP_FREEZER
|
#if IS_SUBSYS_ENABLED(CONFIG_CGROUP_FREEZER)
|
||||||
SUBSYS(freezer)
|
SUBSYS(freezer)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* */
|
/* */
|
||||||
|
|
||||||
#ifdef CONFIG_NET_CLS_CGROUP
|
#if IS_SUBSYS_ENABLED(CONFIG_NET_CLS_CGROUP)
|
||||||
SUBSYS(net_cls)
|
SUBSYS(net_cls)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* */
|
/* */
|
||||||
|
|
||||||
#ifdef CONFIG_BLK_CGROUP
|
#if IS_SUBSYS_ENABLED(CONFIG_BLK_CGROUP)
|
||||||
SUBSYS(blkio)
|
SUBSYS(blkio)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* */
|
/* */
|
||||||
|
|
||||||
#ifdef CONFIG_CGROUP_PERF
|
#if IS_SUBSYS_ENABLED(CONFIG_CGROUP_PERF)
|
||||||
SUBSYS(perf)
|
SUBSYS(perf)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* */
|
/* */
|
||||||
|
|
||||||
#ifdef CONFIG_NETPRIO_CGROUP
|
#if IS_SUBSYS_ENABLED(CONFIG_NETPRIO_CGROUP)
|
||||||
SUBSYS(net_prio)
|
SUBSYS(net_prio)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* */
|
/* */
|
||||||
|
|
||||||
#ifdef CONFIG_CGROUP_HUGETLB
|
#if IS_SUBSYS_ENABLED(CONFIG_CGROUP_HUGETLB)
|
||||||
SUBSYS(hugetlb)
|
SUBSYS(hugetlb)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include <linux/mempolicy.h>
|
#include <linux/mempolicy.h>
|
||||||
#include <linux/pagemap.h>
|
#include <linux/pagemap.h>
|
||||||
#include <linux/percpu_counter.h>
|
#include <linux/percpu_counter.h>
|
||||||
|
#include <linux/xattr.h>
|
||||||
|
|
||||||
/* inode in-kernel data */
|
/* inode in-kernel data */
|
||||||
|
|
||||||
|
@ -18,7 +19,7 @@ struct shmem_inode_info {
|
||||||
};
|
};
|
||||||
struct shared_policy policy; /* NUMA memory alloc policy */
|
struct shared_policy policy; /* NUMA memory alloc policy */
|
||||||
struct list_head swaplist; /* chain of maybes on swap */
|
struct list_head swaplist; /* chain of maybes on swap */
|
||||||
struct list_head xattr_list; /* list of shmem_xattr */
|
struct simple_xattrs xattrs; /* list of xattrs */
|
||||||
struct inode vfs_inode;
|
struct inode vfs_inode;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -59,7 +59,9 @@
|
||||||
|
|
||||||
#ifdef __KERNEL__
|
#ifdef __KERNEL__
|
||||||
|
|
||||||
|
#include <linux/slab.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
|
#include <linux/spinlock.h>
|
||||||
|
|
||||||
struct inode;
|
struct inode;
|
||||||
struct dentry;
|
struct dentry;
|
||||||
|
@ -96,6 +98,52 @@ ssize_t vfs_getxattr_alloc(struct dentry *dentry, const char *name,
|
||||||
char **xattr_value, size_t size, gfp_t flags);
|
char **xattr_value, size_t size, gfp_t flags);
|
||||||
int vfs_xattr_cmp(struct dentry *dentry, const char *xattr_name,
|
int vfs_xattr_cmp(struct dentry *dentry, const char *xattr_name,
|
||||||
const char *value, size_t size, gfp_t flags);
|
const char *value, size_t size, gfp_t flags);
|
||||||
|
|
||||||
|
struct simple_xattrs {
|
||||||
|
struct list_head head;
|
||||||
|
spinlock_t lock;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct simple_xattr {
|
||||||
|
struct list_head list;
|
||||||
|
char *name;
|
||||||
|
size_t size;
|
||||||
|
char value[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* initialize the simple_xattrs structure
|
||||||
|
*/
|
||||||
|
static inline void simple_xattrs_init(struct simple_xattrs *xattrs)
|
||||||
|
{
|
||||||
|
INIT_LIST_HEAD(&xattrs->head);
|
||||||
|
spin_lock_init(&xattrs->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* free all the xattrs
|
||||||
|
*/
|
||||||
|
static inline void simple_xattrs_free(struct simple_xattrs *xattrs)
|
||||||
|
{
|
||||||
|
struct simple_xattr *xattr, *node;
|
||||||
|
|
||||||
|
list_for_each_entry_safe(xattr, node, &xattrs->head, list) {
|
||||||
|
kfree(xattr->name);
|
||||||
|
kfree(xattr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct simple_xattr *simple_xattr_alloc(const void *value, size_t size);
|
||||||
|
int simple_xattr_get(struct simple_xattrs *xattrs, const char *name,
|
||||||
|
void *buffer, size_t size);
|
||||||
|
int simple_xattr_set(struct simple_xattrs *xattrs, const char *name,
|
||||||
|
const void *value, size_t size, int flags);
|
||||||
|
int simple_xattr_remove(struct simple_xattrs *xattrs, const char *name);
|
||||||
|
ssize_t simple_xattr_list(struct simple_xattrs *xattrs, char *buffer,
|
||||||
|
size_t size);
|
||||||
|
void simple_xattr_list_add(struct simple_xattrs *xattrs,
|
||||||
|
struct simple_xattr *new_xattr);
|
||||||
|
|
||||||
#endif /* __KERNEL__ */
|
#endif /* __KERNEL__ */
|
||||||
|
|
||||||
#endif /* _LINUX_XATTR_H */
|
#endif /* _LINUX_XATTR_H */
|
||||||
|
|
|
@ -17,14 +17,16 @@
|
||||||
#include <linux/hardirq.h>
|
#include <linux/hardirq.h>
|
||||||
#include <linux/rcupdate.h>
|
#include <linux/rcupdate.h>
|
||||||
|
|
||||||
#ifdef CONFIG_CGROUPS
|
#if IS_ENABLED(CONFIG_NET_CLS_CGROUP)
|
||||||
struct cgroup_cls_state
|
struct cgroup_cls_state
|
||||||
{
|
{
|
||||||
struct cgroup_subsys_state css;
|
struct cgroup_subsys_state css;
|
||||||
u32 classid;
|
u32 classid;
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef CONFIG_NET_CLS_CGROUP
|
extern void sock_update_classid(struct sock *sk);
|
||||||
|
|
||||||
|
#if IS_BUILTIN(CONFIG_NET_CLS_CGROUP)
|
||||||
static inline u32 task_cls_classid(struct task_struct *p)
|
static inline u32 task_cls_classid(struct task_struct *p)
|
||||||
{
|
{
|
||||||
int classid;
|
int classid;
|
||||||
|
@ -39,32 +41,33 @@ static inline u32 task_cls_classid(struct task_struct *p)
|
||||||
|
|
||||||
return classid;
|
return classid;
|
||||||
}
|
}
|
||||||
#else
|
#elif IS_MODULE(CONFIG_NET_CLS_CGROUP)
|
||||||
extern int net_cls_subsys_id;
|
|
||||||
|
|
||||||
static inline u32 task_cls_classid(struct task_struct *p)
|
static inline u32 task_cls_classid(struct task_struct *p)
|
||||||
{
|
{
|
||||||
int id;
|
struct cgroup_subsys_state *css;
|
||||||
u32 classid = 0;
|
u32 classid = 0;
|
||||||
|
|
||||||
if (in_interrupt())
|
if (in_interrupt())
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
id = rcu_dereference_index_check(net_cls_subsys_id,
|
css = task_subsys_state(p, net_cls_subsys_id);
|
||||||
rcu_read_lock_held());
|
if (css)
|
||||||
if (id >= 0)
|
classid = container_of(css,
|
||||||
classid = container_of(task_subsys_state(p, id),
|
|
||||||
struct cgroup_cls_state, css)->classid;
|
struct cgroup_cls_state, css)->classid;
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
|
|
||||||
return classid;
|
return classid;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#else
|
#else /* !CGROUP_NET_CLS_CGROUP */
|
||||||
|
static inline void sock_update_classid(struct sock *sk)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
static inline u32 task_cls_classid(struct task_struct *p)
|
static inline u32 task_cls_classid(struct task_struct *p)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif /* CGROUP_NET_CLS_CGROUP */
|
||||||
#endif /* _NET_CLS_CGROUP_H */
|
#endif /* _NET_CLS_CGROUP_H */
|
||||||
|
|
|
@ -18,23 +18,18 @@
|
||||||
#include <linux/rcupdate.h>
|
#include <linux/rcupdate.h>
|
||||||
|
|
||||||
|
|
||||||
|
#if IS_ENABLED(CONFIG_NETPRIO_CGROUP)
|
||||||
struct netprio_map {
|
struct netprio_map {
|
||||||
struct rcu_head rcu;
|
struct rcu_head rcu;
|
||||||
u32 priomap_len;
|
u32 priomap_len;
|
||||||
u32 priomap[];
|
u32 priomap[];
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef CONFIG_CGROUPS
|
|
||||||
|
|
||||||
struct cgroup_netprio_state {
|
struct cgroup_netprio_state {
|
||||||
struct cgroup_subsys_state css;
|
struct cgroup_subsys_state css;
|
||||||
u32 prioidx;
|
u32 prioidx;
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifndef CONFIG_NETPRIO_CGROUP
|
|
||||||
extern int net_prio_subsys_id;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
extern void sock_update_netprioidx(struct sock *sk, struct task_struct *task);
|
extern void sock_update_netprioidx(struct sock *sk, struct task_struct *task);
|
||||||
|
|
||||||
#if IS_BUILTIN(CONFIG_NETPRIO_CGROUP)
|
#if IS_BUILTIN(CONFIG_NETPRIO_CGROUP)
|
||||||
|
@ -56,33 +51,28 @@ static inline u32 task_netprioidx(struct task_struct *p)
|
||||||
|
|
||||||
static inline u32 task_netprioidx(struct task_struct *p)
|
static inline u32 task_netprioidx(struct task_struct *p)
|
||||||
{
|
{
|
||||||
struct cgroup_netprio_state *state;
|
struct cgroup_subsys_state *css;
|
||||||
int subsys_id;
|
|
||||||
u32 idx = 0;
|
u32 idx = 0;
|
||||||
|
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
subsys_id = rcu_dereference_index_check(net_prio_subsys_id,
|
css = task_subsys_state(p, net_prio_subsys_id);
|
||||||
rcu_read_lock_held());
|
if (css)
|
||||||
if (subsys_id >= 0) {
|
idx = container_of(css,
|
||||||
state = container_of(task_subsys_state(p, subsys_id),
|
struct cgroup_netprio_state, css)->prioidx;
|
||||||
struct cgroup_netprio_state, css);
|
|
||||||
idx = state->prioidx;
|
|
||||||
}
|
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
return idx;
|
return idx;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#else
|
#else /* !CONFIG_NETPRIO_CGROUP */
|
||||||
|
|
||||||
static inline u32 task_netprioidx(struct task_struct *p)
|
static inline u32 task_netprioidx(struct task_struct *p)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define sock_update_netprioidx(sk, task)
|
||||||
|
|
||||||
#endif /* CONFIG_NETPRIO_CGROUP */
|
#endif /* CONFIG_NETPRIO_CGROUP */
|
||||||
|
|
||||||
#else
|
|
||||||
#define sock_update_netprioidx(sk, task)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* _NET_CLS_CGROUP_H */
|
#endif /* _NET_CLS_CGROUP_H */
|
||||||
|
|
|
@ -1486,14 +1486,6 @@ extern void *sock_kmalloc(struct sock *sk, int size,
|
||||||
extern void sock_kfree_s(struct sock *sk, void *mem, int size);
|
extern void sock_kfree_s(struct sock *sk, void *mem, int size);
|
||||||
extern void sk_send_sigurg(struct sock *sk);
|
extern void sk_send_sigurg(struct sock *sk);
|
||||||
|
|
||||||
#ifdef CONFIG_CGROUPS
|
|
||||||
extern void sock_update_classid(struct sock *sk);
|
|
||||||
#else
|
|
||||||
static inline void sock_update_classid(struct sock *sk)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Functions to fill in entries in struct proto_ops when a protocol
|
* Functions to fill in entries in struct proto_ops when a protocol
|
||||||
* does not implement a particular function.
|
* does not implement a particular function.
|
||||||
|
|
320
kernel/cgroup.c
320
kernel/cgroup.c
|
@ -88,11 +88,12 @@ static DEFINE_MUTEX(cgroup_root_mutex);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Generate an array of cgroup subsystem pointers. At boot time, this is
|
* Generate an array of cgroup subsystem pointers. At boot time, this is
|
||||||
* populated up to CGROUP_BUILTIN_SUBSYS_COUNT, and modular subsystems are
|
* populated with the built in subsystems, and modular subsystems are
|
||||||
* registered after that. The mutable section of this array is protected by
|
* registered after that. The mutable section of this array is protected by
|
||||||
* cgroup_mutex.
|
* cgroup_mutex.
|
||||||
*/
|
*/
|
||||||
#define SUBSYS(_x) &_x ## _subsys,
|
#define SUBSYS(_x) [_x ## _subsys_id] = &_x ## _subsys,
|
||||||
|
#define IS_SUBSYS_ENABLED(option) IS_BUILTIN(option)
|
||||||
static struct cgroup_subsys *subsys[CGROUP_SUBSYS_COUNT] = {
|
static struct cgroup_subsys *subsys[CGROUP_SUBSYS_COUNT] = {
|
||||||
#include <linux/cgroup_subsys.h>
|
#include <linux/cgroup_subsys.h>
|
||||||
};
|
};
|
||||||
|
@ -111,13 +112,13 @@ struct cgroupfs_root {
|
||||||
* The bitmask of subsystems intended to be attached to this
|
* The bitmask of subsystems intended to be attached to this
|
||||||
* hierarchy
|
* hierarchy
|
||||||
*/
|
*/
|
||||||
unsigned long subsys_bits;
|
unsigned long subsys_mask;
|
||||||
|
|
||||||
/* Unique id for this hierarchy. */
|
/* Unique id for this hierarchy. */
|
||||||
int hierarchy_id;
|
int hierarchy_id;
|
||||||
|
|
||||||
/* The bitmask of subsystems currently attached to this hierarchy */
|
/* The bitmask of subsystems currently attached to this hierarchy */
|
||||||
unsigned long actual_subsys_bits;
|
unsigned long actual_subsys_mask;
|
||||||
|
|
||||||
/* A list running through the attached subsystems */
|
/* A list running through the attached subsystems */
|
||||||
struct list_head subsys_list;
|
struct list_head subsys_list;
|
||||||
|
@ -276,7 +277,8 @@ inline int cgroup_is_removed(const struct cgroup *cgrp)
|
||||||
|
|
||||||
/* bits in struct cgroupfs_root flags field */
|
/* bits in struct cgroupfs_root flags field */
|
||||||
enum {
|
enum {
|
||||||
ROOT_NOPREFIX, /* mounted subsystems have no named prefix */
|
ROOT_NOPREFIX, /* mounted subsystems have no named prefix */
|
||||||
|
ROOT_XATTR, /* supports extended attributes */
|
||||||
};
|
};
|
||||||
|
|
||||||
static int cgroup_is_releasable(const struct cgroup *cgrp)
|
static int cgroup_is_releasable(const struct cgroup *cgrp)
|
||||||
|
@ -556,7 +558,7 @@ static struct css_set *find_existing_css_set(
|
||||||
* won't change, so no need for locking.
|
* won't change, so no need for locking.
|
||||||
*/
|
*/
|
||||||
for (i = 0; i < CGROUP_SUBSYS_COUNT; i++) {
|
for (i = 0; i < CGROUP_SUBSYS_COUNT; i++) {
|
||||||
if (root->subsys_bits & (1UL << i)) {
|
if (root->subsys_mask & (1UL << i)) {
|
||||||
/* Subsystem is in this hierarchy. So we want
|
/* Subsystem is in this hierarchy. So we want
|
||||||
* the subsystem state from the new
|
* the subsystem state from the new
|
||||||
* cgroup */
|
* cgroup */
|
||||||
|
@ -824,7 +826,8 @@ EXPORT_SYMBOL_GPL(cgroup_unlock);
|
||||||
static int cgroup_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode);
|
static int cgroup_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode);
|
||||||
static struct dentry *cgroup_lookup(struct inode *, struct dentry *, unsigned int);
|
static struct dentry *cgroup_lookup(struct inode *, struct dentry *, unsigned int);
|
||||||
static int cgroup_rmdir(struct inode *unused_dir, struct dentry *dentry);
|
static int cgroup_rmdir(struct inode *unused_dir, struct dentry *dentry);
|
||||||
static int cgroup_populate_dir(struct cgroup *cgrp);
|
static int cgroup_populate_dir(struct cgroup *cgrp, bool base_files,
|
||||||
|
unsigned long subsys_mask);
|
||||||
static const struct inode_operations cgroup_dir_inode_operations;
|
static const struct inode_operations cgroup_dir_inode_operations;
|
||||||
static const struct file_operations proc_cgroupstats_operations;
|
static const struct file_operations proc_cgroupstats_operations;
|
||||||
|
|
||||||
|
@ -912,15 +915,19 @@ static void cgroup_diput(struct dentry *dentry, struct inode *inode)
|
||||||
*/
|
*/
|
||||||
BUG_ON(!list_empty(&cgrp->pidlists));
|
BUG_ON(!list_empty(&cgrp->pidlists));
|
||||||
|
|
||||||
|
simple_xattrs_free(&cgrp->xattrs);
|
||||||
|
|
||||||
kfree_rcu(cgrp, rcu_head);
|
kfree_rcu(cgrp, rcu_head);
|
||||||
} else {
|
} else {
|
||||||
struct cfent *cfe = __d_cfe(dentry);
|
struct cfent *cfe = __d_cfe(dentry);
|
||||||
struct cgroup *cgrp = dentry->d_parent->d_fsdata;
|
struct cgroup *cgrp = dentry->d_parent->d_fsdata;
|
||||||
|
struct cftype *cft = cfe->type;
|
||||||
|
|
||||||
WARN_ONCE(!list_empty(&cfe->node) &&
|
WARN_ONCE(!list_empty(&cfe->node) &&
|
||||||
cgrp != &cgrp->root->top_cgroup,
|
cgrp != &cgrp->root->top_cgroup,
|
||||||
"cfe still linked for %s\n", cfe->type->name);
|
"cfe still linked for %s\n", cfe->type->name);
|
||||||
kfree(cfe);
|
kfree(cfe);
|
||||||
|
simple_xattrs_free(&cft->xattrs);
|
||||||
}
|
}
|
||||||
iput(inode);
|
iput(inode);
|
||||||
}
|
}
|
||||||
|
@ -963,12 +970,29 @@ static int cgroup_rm_file(struct cgroup *cgrp, const struct cftype *cft)
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cgroup_clear_directory(struct dentry *dir)
|
/**
|
||||||
|
* cgroup_clear_directory - selective removal of base and subsystem files
|
||||||
|
* @dir: directory containing the files
|
||||||
|
* @base_files: true if the base files should be removed
|
||||||
|
* @subsys_mask: mask of the subsystem ids whose files should be removed
|
||||||
|
*/
|
||||||
|
static void cgroup_clear_directory(struct dentry *dir, bool base_files,
|
||||||
|
unsigned long subsys_mask)
|
||||||
{
|
{
|
||||||
struct cgroup *cgrp = __d_cgrp(dir);
|
struct cgroup *cgrp = __d_cgrp(dir);
|
||||||
|
struct cgroup_subsys *ss;
|
||||||
|
|
||||||
while (!list_empty(&cgrp->files))
|
for_each_subsys(cgrp->root, ss) {
|
||||||
cgroup_rm_file(cgrp, NULL);
|
struct cftype_set *set;
|
||||||
|
if (!test_bit(ss->subsys_id, &subsys_mask))
|
||||||
|
continue;
|
||||||
|
list_for_each_entry(set, &ss->cftsets, node)
|
||||||
|
cgroup_rm_file(cgrp, set->cfts);
|
||||||
|
}
|
||||||
|
if (base_files) {
|
||||||
|
while (!list_empty(&cgrp->files))
|
||||||
|
cgroup_rm_file(cgrp, NULL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -977,8 +1001,9 @@ static void cgroup_clear_directory(struct dentry *dir)
|
||||||
static void cgroup_d_remove_dir(struct dentry *dentry)
|
static void cgroup_d_remove_dir(struct dentry *dentry)
|
||||||
{
|
{
|
||||||
struct dentry *parent;
|
struct dentry *parent;
|
||||||
|
struct cgroupfs_root *root = dentry->d_sb->s_fs_info;
|
||||||
|
|
||||||
cgroup_clear_directory(dentry);
|
cgroup_clear_directory(dentry, true, root->subsys_mask);
|
||||||
|
|
||||||
parent = dentry->d_parent;
|
parent = dentry->d_parent;
|
||||||
spin_lock(&parent->d_lock);
|
spin_lock(&parent->d_lock);
|
||||||
|
@ -1022,22 +1047,22 @@ void cgroup_release_and_wakeup_rmdir(struct cgroup_subsys_state *css)
|
||||||
* returns an error, no reference counts are touched.
|
* returns an error, no reference counts are touched.
|
||||||
*/
|
*/
|
||||||
static int rebind_subsystems(struct cgroupfs_root *root,
|
static int rebind_subsystems(struct cgroupfs_root *root,
|
||||||
unsigned long final_bits)
|
unsigned long final_subsys_mask)
|
||||||
{
|
{
|
||||||
unsigned long added_bits, removed_bits;
|
unsigned long added_mask, removed_mask;
|
||||||
struct cgroup *cgrp = &root->top_cgroup;
|
struct cgroup *cgrp = &root->top_cgroup;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
BUG_ON(!mutex_is_locked(&cgroup_mutex));
|
BUG_ON(!mutex_is_locked(&cgroup_mutex));
|
||||||
BUG_ON(!mutex_is_locked(&cgroup_root_mutex));
|
BUG_ON(!mutex_is_locked(&cgroup_root_mutex));
|
||||||
|
|
||||||
removed_bits = root->actual_subsys_bits & ~final_bits;
|
removed_mask = root->actual_subsys_mask & ~final_subsys_mask;
|
||||||
added_bits = final_bits & ~root->actual_subsys_bits;
|
added_mask = final_subsys_mask & ~root->actual_subsys_mask;
|
||||||
/* Check that any added subsystems are currently free */
|
/* Check that any added subsystems are currently free */
|
||||||
for (i = 0; i < CGROUP_SUBSYS_COUNT; i++) {
|
for (i = 0; i < CGROUP_SUBSYS_COUNT; i++) {
|
||||||
unsigned long bit = 1UL << i;
|
unsigned long bit = 1UL << i;
|
||||||
struct cgroup_subsys *ss = subsys[i];
|
struct cgroup_subsys *ss = subsys[i];
|
||||||
if (!(bit & added_bits))
|
if (!(bit & added_mask))
|
||||||
continue;
|
continue;
|
||||||
/*
|
/*
|
||||||
* Nobody should tell us to do a subsys that doesn't exist:
|
* Nobody should tell us to do a subsys that doesn't exist:
|
||||||
|
@ -1062,7 +1087,7 @@ static int rebind_subsystems(struct cgroupfs_root *root,
|
||||||
for (i = 0; i < CGROUP_SUBSYS_COUNT; i++) {
|
for (i = 0; i < CGROUP_SUBSYS_COUNT; i++) {
|
||||||
struct cgroup_subsys *ss = subsys[i];
|
struct cgroup_subsys *ss = subsys[i];
|
||||||
unsigned long bit = 1UL << i;
|
unsigned long bit = 1UL << i;
|
||||||
if (bit & added_bits) {
|
if (bit & added_mask) {
|
||||||
/* We're binding this subsystem to this hierarchy */
|
/* We're binding this subsystem to this hierarchy */
|
||||||
BUG_ON(ss == NULL);
|
BUG_ON(ss == NULL);
|
||||||
BUG_ON(cgrp->subsys[i]);
|
BUG_ON(cgrp->subsys[i]);
|
||||||
|
@ -1075,7 +1100,7 @@ static int rebind_subsystems(struct cgroupfs_root *root,
|
||||||
if (ss->bind)
|
if (ss->bind)
|
||||||
ss->bind(cgrp);
|
ss->bind(cgrp);
|
||||||
/* refcount was already taken, and we're keeping it */
|
/* refcount was already taken, and we're keeping it */
|
||||||
} else if (bit & removed_bits) {
|
} else if (bit & removed_mask) {
|
||||||
/* We're removing this subsystem */
|
/* We're removing this subsystem */
|
||||||
BUG_ON(ss == NULL);
|
BUG_ON(ss == NULL);
|
||||||
BUG_ON(cgrp->subsys[i] != dummytop->subsys[i]);
|
BUG_ON(cgrp->subsys[i] != dummytop->subsys[i]);
|
||||||
|
@ -1088,7 +1113,7 @@ static int rebind_subsystems(struct cgroupfs_root *root,
|
||||||
list_move(&ss->sibling, &rootnode.subsys_list);
|
list_move(&ss->sibling, &rootnode.subsys_list);
|
||||||
/* subsystem is now free - drop reference on module */
|
/* subsystem is now free - drop reference on module */
|
||||||
module_put(ss->module);
|
module_put(ss->module);
|
||||||
} else if (bit & final_bits) {
|
} else if (bit & final_subsys_mask) {
|
||||||
/* Subsystem state should already exist */
|
/* Subsystem state should already exist */
|
||||||
BUG_ON(ss == NULL);
|
BUG_ON(ss == NULL);
|
||||||
BUG_ON(!cgrp->subsys[i]);
|
BUG_ON(!cgrp->subsys[i]);
|
||||||
|
@ -1105,7 +1130,7 @@ static int rebind_subsystems(struct cgroupfs_root *root,
|
||||||
BUG_ON(cgrp->subsys[i]);
|
BUG_ON(cgrp->subsys[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
root->subsys_bits = root->actual_subsys_bits = final_bits;
|
root->subsys_mask = root->actual_subsys_mask = final_subsys_mask;
|
||||||
synchronize_rcu();
|
synchronize_rcu();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1121,6 +1146,8 @@ static int cgroup_show_options(struct seq_file *seq, struct dentry *dentry)
|
||||||
seq_printf(seq, ",%s", ss->name);
|
seq_printf(seq, ",%s", ss->name);
|
||||||
if (test_bit(ROOT_NOPREFIX, &root->flags))
|
if (test_bit(ROOT_NOPREFIX, &root->flags))
|
||||||
seq_puts(seq, ",noprefix");
|
seq_puts(seq, ",noprefix");
|
||||||
|
if (test_bit(ROOT_XATTR, &root->flags))
|
||||||
|
seq_puts(seq, ",xattr");
|
||||||
if (strlen(root->release_agent_path))
|
if (strlen(root->release_agent_path))
|
||||||
seq_printf(seq, ",release_agent=%s", root->release_agent_path);
|
seq_printf(seq, ",release_agent=%s", root->release_agent_path);
|
||||||
if (clone_children(&root->top_cgroup))
|
if (clone_children(&root->top_cgroup))
|
||||||
|
@ -1132,7 +1159,7 @@ static int cgroup_show_options(struct seq_file *seq, struct dentry *dentry)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct cgroup_sb_opts {
|
struct cgroup_sb_opts {
|
||||||
unsigned long subsys_bits;
|
unsigned long subsys_mask;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
char *release_agent;
|
char *release_agent;
|
||||||
bool clone_children;
|
bool clone_children;
|
||||||
|
@ -1189,6 +1216,10 @@ static int parse_cgroupfs_options(char *data, struct cgroup_sb_opts *opts)
|
||||||
opts->clone_children = true;
|
opts->clone_children = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (!strcmp(token, "xattr")) {
|
||||||
|
set_bit(ROOT_XATTR, &opts->flags);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (!strncmp(token, "release_agent=", 14)) {
|
if (!strncmp(token, "release_agent=", 14)) {
|
||||||
/* Specifying two release agents is forbidden */
|
/* Specifying two release agents is forbidden */
|
||||||
if (opts->release_agent)
|
if (opts->release_agent)
|
||||||
|
@ -1237,7 +1268,7 @@ static int parse_cgroupfs_options(char *data, struct cgroup_sb_opts *opts)
|
||||||
/* Mutually exclusive option 'all' + subsystem name */
|
/* Mutually exclusive option 'all' + subsystem name */
|
||||||
if (all_ss)
|
if (all_ss)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
set_bit(i, &opts->subsys_bits);
|
set_bit(i, &opts->subsys_mask);
|
||||||
one_ss = true;
|
one_ss = true;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -1258,7 +1289,7 @@ static int parse_cgroupfs_options(char *data, struct cgroup_sb_opts *opts)
|
||||||
continue;
|
continue;
|
||||||
if (ss->disabled)
|
if (ss->disabled)
|
||||||
continue;
|
continue;
|
||||||
set_bit(i, &opts->subsys_bits);
|
set_bit(i, &opts->subsys_mask);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1270,19 +1301,19 @@ static int parse_cgroupfs_options(char *data, struct cgroup_sb_opts *opts)
|
||||||
* the cpuset subsystem.
|
* the cpuset subsystem.
|
||||||
*/
|
*/
|
||||||
if (test_bit(ROOT_NOPREFIX, &opts->flags) &&
|
if (test_bit(ROOT_NOPREFIX, &opts->flags) &&
|
||||||
(opts->subsys_bits & mask))
|
(opts->subsys_mask & mask))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
|
||||||
/* Can't specify "none" and some subsystems */
|
/* Can't specify "none" and some subsystems */
|
||||||
if (opts->subsys_bits && opts->none)
|
if (opts->subsys_mask && opts->none)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We either have to specify by name or by subsystems. (So all
|
* We either have to specify by name or by subsystems. (So all
|
||||||
* empty hierarchies must have a name).
|
* empty hierarchies must have a name).
|
||||||
*/
|
*/
|
||||||
if (!opts->subsys_bits && !opts->name)
|
if (!opts->subsys_mask && !opts->name)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1291,10 +1322,10 @@ static int parse_cgroupfs_options(char *data, struct cgroup_sb_opts *opts)
|
||||||
* take duplicate reference counts on a subsystem that's already used,
|
* take duplicate reference counts on a subsystem that's already used,
|
||||||
* but rebind_subsystems handles this case.
|
* but rebind_subsystems handles this case.
|
||||||
*/
|
*/
|
||||||
for (i = CGROUP_BUILTIN_SUBSYS_COUNT; i < CGROUP_SUBSYS_COUNT; i++) {
|
for (i = 0; i < CGROUP_SUBSYS_COUNT; i++) {
|
||||||
unsigned long bit = 1UL << i;
|
unsigned long bit = 1UL << i;
|
||||||
|
|
||||||
if (!(bit & opts->subsys_bits))
|
if (!(bit & opts->subsys_mask))
|
||||||
continue;
|
continue;
|
||||||
if (!try_module_get(subsys[i]->module)) {
|
if (!try_module_get(subsys[i]->module)) {
|
||||||
module_pin_failed = true;
|
module_pin_failed = true;
|
||||||
|
@ -1307,11 +1338,11 @@ static int parse_cgroupfs_options(char *data, struct cgroup_sb_opts *opts)
|
||||||
* raced with a module_delete call, and to the user this is
|
* raced with a module_delete call, and to the user this is
|
||||||
* essentially a "subsystem doesn't exist" case.
|
* essentially a "subsystem doesn't exist" case.
|
||||||
*/
|
*/
|
||||||
for (i--; i >= CGROUP_BUILTIN_SUBSYS_COUNT; i--) {
|
for (i--; i >= 0; i--) {
|
||||||
/* drop refcounts only on the ones we took */
|
/* drop refcounts only on the ones we took */
|
||||||
unsigned long bit = 1UL << i;
|
unsigned long bit = 1UL << i;
|
||||||
|
|
||||||
if (!(bit & opts->subsys_bits))
|
if (!(bit & opts->subsys_mask))
|
||||||
continue;
|
continue;
|
||||||
module_put(subsys[i]->module);
|
module_put(subsys[i]->module);
|
||||||
}
|
}
|
||||||
|
@ -1321,13 +1352,13 @@ static int parse_cgroupfs_options(char *data, struct cgroup_sb_opts *opts)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void drop_parsed_module_refcounts(unsigned long subsys_bits)
|
static void drop_parsed_module_refcounts(unsigned long subsys_mask)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
for (i = CGROUP_BUILTIN_SUBSYS_COUNT; i < CGROUP_SUBSYS_COUNT; i++) {
|
for (i = 0; i < CGROUP_SUBSYS_COUNT; i++) {
|
||||||
unsigned long bit = 1UL << i;
|
unsigned long bit = 1UL << i;
|
||||||
|
|
||||||
if (!(bit & subsys_bits))
|
if (!(bit & subsys_mask))
|
||||||
continue;
|
continue;
|
||||||
module_put(subsys[i]->module);
|
module_put(subsys[i]->module);
|
||||||
}
|
}
|
||||||
|
@ -1339,6 +1370,7 @@ static int cgroup_remount(struct super_block *sb, int *flags, char *data)
|
||||||
struct cgroupfs_root *root = sb->s_fs_info;
|
struct cgroupfs_root *root = sb->s_fs_info;
|
||||||
struct cgroup *cgrp = &root->top_cgroup;
|
struct cgroup *cgrp = &root->top_cgroup;
|
||||||
struct cgroup_sb_opts opts;
|
struct cgroup_sb_opts opts;
|
||||||
|
unsigned long added_mask, removed_mask;
|
||||||
|
|
||||||
mutex_lock(&cgrp->dentry->d_inode->i_mutex);
|
mutex_lock(&cgrp->dentry->d_inode->i_mutex);
|
||||||
mutex_lock(&cgroup_mutex);
|
mutex_lock(&cgroup_mutex);
|
||||||
|
@ -1350,27 +1382,31 @@ static int cgroup_remount(struct super_block *sb, int *flags, char *data)
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
|
|
||||||
/* See feature-removal-schedule.txt */
|
/* See feature-removal-schedule.txt */
|
||||||
if (opts.subsys_bits != root->actual_subsys_bits || opts.release_agent)
|
if (opts.subsys_mask != root->actual_subsys_mask || opts.release_agent)
|
||||||
pr_warning("cgroup: option changes via remount are deprecated (pid=%d comm=%s)\n",
|
pr_warning("cgroup: option changes via remount are deprecated (pid=%d comm=%s)\n",
|
||||||
task_tgid_nr(current), current->comm);
|
task_tgid_nr(current), current->comm);
|
||||||
|
|
||||||
|
added_mask = opts.subsys_mask & ~root->subsys_mask;
|
||||||
|
removed_mask = root->subsys_mask & ~opts.subsys_mask;
|
||||||
|
|
||||||
/* Don't allow flags or name to change at remount */
|
/* Don't allow flags or name to change at remount */
|
||||||
if (opts.flags != root->flags ||
|
if (opts.flags != root->flags ||
|
||||||
(opts.name && strcmp(opts.name, root->name))) {
|
(opts.name && strcmp(opts.name, root->name))) {
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
drop_parsed_module_refcounts(opts.subsys_bits);
|
drop_parsed_module_refcounts(opts.subsys_mask);
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = rebind_subsystems(root, opts.subsys_bits);
|
ret = rebind_subsystems(root, opts.subsys_mask);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
drop_parsed_module_refcounts(opts.subsys_bits);
|
drop_parsed_module_refcounts(opts.subsys_mask);
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* clear out any existing files and repopulate subsystem files */
|
/* clear out any existing files and repopulate subsystem files */
|
||||||
cgroup_clear_directory(cgrp->dentry);
|
cgroup_clear_directory(cgrp->dentry, false, removed_mask);
|
||||||
cgroup_populate_dir(cgrp);
|
/* re-populate subsystem files */
|
||||||
|
cgroup_populate_dir(cgrp, false, added_mask);
|
||||||
|
|
||||||
if (opts.release_agent)
|
if (opts.release_agent)
|
||||||
strcpy(root->release_agent_path, opts.release_agent);
|
strcpy(root->release_agent_path, opts.release_agent);
|
||||||
|
@ -1401,6 +1437,7 @@ static void init_cgroup_housekeeping(struct cgroup *cgrp)
|
||||||
mutex_init(&cgrp->pidlist_mutex);
|
mutex_init(&cgrp->pidlist_mutex);
|
||||||
INIT_LIST_HEAD(&cgrp->event_list);
|
INIT_LIST_HEAD(&cgrp->event_list);
|
||||||
spin_lock_init(&cgrp->event_list_lock);
|
spin_lock_init(&cgrp->event_list_lock);
|
||||||
|
simple_xattrs_init(&cgrp->xattrs);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void init_cgroup_root(struct cgroupfs_root *root)
|
static void init_cgroup_root(struct cgroupfs_root *root)
|
||||||
|
@ -1455,8 +1492,8 @@ static int cgroup_test_super(struct super_block *sb, void *data)
|
||||||
* If we asked for subsystems (or explicitly for no
|
* If we asked for subsystems (or explicitly for no
|
||||||
* subsystems) then they must match
|
* subsystems) then they must match
|
||||||
*/
|
*/
|
||||||
if ((opts->subsys_bits || opts->none)
|
if ((opts->subsys_mask || opts->none)
|
||||||
&& (opts->subsys_bits != root->subsys_bits))
|
&& (opts->subsys_mask != root->subsys_mask))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -1466,7 +1503,7 @@ static struct cgroupfs_root *cgroup_root_from_opts(struct cgroup_sb_opts *opts)
|
||||||
{
|
{
|
||||||
struct cgroupfs_root *root;
|
struct cgroupfs_root *root;
|
||||||
|
|
||||||
if (!opts->subsys_bits && !opts->none)
|
if (!opts->subsys_mask && !opts->none)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
root = kzalloc(sizeof(*root), GFP_KERNEL);
|
root = kzalloc(sizeof(*root), GFP_KERNEL);
|
||||||
|
@ -1479,7 +1516,7 @@ static struct cgroupfs_root *cgroup_root_from_opts(struct cgroup_sb_opts *opts)
|
||||||
}
|
}
|
||||||
init_cgroup_root(root);
|
init_cgroup_root(root);
|
||||||
|
|
||||||
root->subsys_bits = opts->subsys_bits;
|
root->subsys_mask = opts->subsys_mask;
|
||||||
root->flags = opts->flags;
|
root->flags = opts->flags;
|
||||||
if (opts->release_agent)
|
if (opts->release_agent)
|
||||||
strcpy(root->release_agent_path, opts->release_agent);
|
strcpy(root->release_agent_path, opts->release_agent);
|
||||||
|
@ -1511,7 +1548,7 @@ static int cgroup_set_super(struct super_block *sb, void *data)
|
||||||
if (!opts->new_root)
|
if (!opts->new_root)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
BUG_ON(!opts->subsys_bits && !opts->none);
|
BUG_ON(!opts->subsys_mask && !opts->none);
|
||||||
|
|
||||||
ret = set_anon_super(sb, NULL);
|
ret = set_anon_super(sb, NULL);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
@ -1629,7 +1666,7 @@ static struct dentry *cgroup_mount(struct file_system_type *fs_type,
|
||||||
if (ret)
|
if (ret)
|
||||||
goto unlock_drop;
|
goto unlock_drop;
|
||||||
|
|
||||||
ret = rebind_subsystems(root, root->subsys_bits);
|
ret = rebind_subsystems(root, root->subsys_mask);
|
||||||
if (ret == -EBUSY) {
|
if (ret == -EBUSY) {
|
||||||
free_cg_links(&tmp_cg_links);
|
free_cg_links(&tmp_cg_links);
|
||||||
goto unlock_drop;
|
goto unlock_drop;
|
||||||
|
@ -1669,7 +1706,7 @@ static struct dentry *cgroup_mount(struct file_system_type *fs_type,
|
||||||
BUG_ON(root->number_of_cgroups != 1);
|
BUG_ON(root->number_of_cgroups != 1);
|
||||||
|
|
||||||
cred = override_creds(&init_cred);
|
cred = override_creds(&init_cred);
|
||||||
cgroup_populate_dir(root_cgrp);
|
cgroup_populate_dir(root_cgrp, true, root->subsys_mask);
|
||||||
revert_creds(cred);
|
revert_creds(cred);
|
||||||
mutex_unlock(&cgroup_root_mutex);
|
mutex_unlock(&cgroup_root_mutex);
|
||||||
mutex_unlock(&cgroup_mutex);
|
mutex_unlock(&cgroup_mutex);
|
||||||
|
@ -1681,7 +1718,7 @@ static struct dentry *cgroup_mount(struct file_system_type *fs_type,
|
||||||
*/
|
*/
|
||||||
cgroup_drop_root(opts.new_root);
|
cgroup_drop_root(opts.new_root);
|
||||||
/* no subsys rebinding, so refcounts don't change */
|
/* no subsys rebinding, so refcounts don't change */
|
||||||
drop_parsed_module_refcounts(opts.subsys_bits);
|
drop_parsed_module_refcounts(opts.subsys_mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
kfree(opts.release_agent);
|
kfree(opts.release_agent);
|
||||||
|
@ -1695,7 +1732,7 @@ static struct dentry *cgroup_mount(struct file_system_type *fs_type,
|
||||||
drop_new_super:
|
drop_new_super:
|
||||||
deactivate_locked_super(sb);
|
deactivate_locked_super(sb);
|
||||||
drop_modules:
|
drop_modules:
|
||||||
drop_parsed_module_refcounts(opts.subsys_bits);
|
drop_parsed_module_refcounts(opts.subsys_mask);
|
||||||
out_err:
|
out_err:
|
||||||
kfree(opts.release_agent);
|
kfree(opts.release_agent);
|
||||||
kfree(opts.name);
|
kfree(opts.name);
|
||||||
|
@ -1745,6 +1782,8 @@ static void cgroup_kill_sb(struct super_block *sb) {
|
||||||
mutex_unlock(&cgroup_root_mutex);
|
mutex_unlock(&cgroup_root_mutex);
|
||||||
mutex_unlock(&cgroup_mutex);
|
mutex_unlock(&cgroup_mutex);
|
||||||
|
|
||||||
|
simple_xattrs_free(&cgrp->xattrs);
|
||||||
|
|
||||||
kill_litter_super(sb);
|
kill_litter_super(sb);
|
||||||
cgroup_drop_root(root);
|
cgroup_drop_root(root);
|
||||||
}
|
}
|
||||||
|
@ -2551,6 +2590,64 @@ static int cgroup_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||||
return simple_rename(old_dir, old_dentry, new_dir, new_dentry);
|
return simple_rename(old_dir, old_dentry, new_dir, new_dentry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct simple_xattrs *__d_xattrs(struct dentry *dentry)
|
||||||
|
{
|
||||||
|
if (S_ISDIR(dentry->d_inode->i_mode))
|
||||||
|
return &__d_cgrp(dentry)->xattrs;
|
||||||
|
else
|
||||||
|
return &__d_cft(dentry)->xattrs;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int xattr_enabled(struct dentry *dentry)
|
||||||
|
{
|
||||||
|
struct cgroupfs_root *root = dentry->d_sb->s_fs_info;
|
||||||
|
return test_bit(ROOT_XATTR, &root->flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool is_valid_xattr(const char *name)
|
||||||
|
{
|
||||||
|
if (!strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) ||
|
||||||
|
!strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cgroup_setxattr(struct dentry *dentry, const char *name,
|
||||||
|
const void *val, size_t size, int flags)
|
||||||
|
{
|
||||||
|
if (!xattr_enabled(dentry))
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
if (!is_valid_xattr(name))
|
||||||
|
return -EINVAL;
|
||||||
|
return simple_xattr_set(__d_xattrs(dentry), name, val, size, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cgroup_removexattr(struct dentry *dentry, const char *name)
|
||||||
|
{
|
||||||
|
if (!xattr_enabled(dentry))
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
if (!is_valid_xattr(name))
|
||||||
|
return -EINVAL;
|
||||||
|
return simple_xattr_remove(__d_xattrs(dentry), name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t cgroup_getxattr(struct dentry *dentry, const char *name,
|
||||||
|
void *buf, size_t size)
|
||||||
|
{
|
||||||
|
if (!xattr_enabled(dentry))
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
if (!is_valid_xattr(name))
|
||||||
|
return -EINVAL;
|
||||||
|
return simple_xattr_get(__d_xattrs(dentry), name, buf, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t cgroup_listxattr(struct dentry *dentry, char *buf, size_t size)
|
||||||
|
{
|
||||||
|
if (!xattr_enabled(dentry))
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
return simple_xattr_list(__d_xattrs(dentry), buf, size);
|
||||||
|
}
|
||||||
|
|
||||||
static const struct file_operations cgroup_file_operations = {
|
static const struct file_operations cgroup_file_operations = {
|
||||||
.read = cgroup_file_read,
|
.read = cgroup_file_read,
|
||||||
.write = cgroup_file_write,
|
.write = cgroup_file_write,
|
||||||
|
@ -2559,11 +2656,22 @@ static const struct file_operations cgroup_file_operations = {
|
||||||
.release = cgroup_file_release,
|
.release = cgroup_file_release,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct inode_operations cgroup_file_inode_operations = {
|
||||||
|
.setxattr = cgroup_setxattr,
|
||||||
|
.getxattr = cgroup_getxattr,
|
||||||
|
.listxattr = cgroup_listxattr,
|
||||||
|
.removexattr = cgroup_removexattr,
|
||||||
|
};
|
||||||
|
|
||||||
static const struct inode_operations cgroup_dir_inode_operations = {
|
static const struct inode_operations cgroup_dir_inode_operations = {
|
||||||
.lookup = cgroup_lookup,
|
.lookup = cgroup_lookup,
|
||||||
.mkdir = cgroup_mkdir,
|
.mkdir = cgroup_mkdir,
|
||||||
.rmdir = cgroup_rmdir,
|
.rmdir = cgroup_rmdir,
|
||||||
.rename = cgroup_rename,
|
.rename = cgroup_rename,
|
||||||
|
.setxattr = cgroup_setxattr,
|
||||||
|
.getxattr = cgroup_getxattr,
|
||||||
|
.listxattr = cgroup_listxattr,
|
||||||
|
.removexattr = cgroup_removexattr,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct dentry *cgroup_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
|
static struct dentry *cgroup_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
|
||||||
|
@ -2611,6 +2719,7 @@ static int cgroup_create_file(struct dentry *dentry, umode_t mode,
|
||||||
} else if (S_ISREG(mode)) {
|
} else if (S_ISREG(mode)) {
|
||||||
inode->i_size = 0;
|
inode->i_size = 0;
|
||||||
inode->i_fop = &cgroup_file_operations;
|
inode->i_fop = &cgroup_file_operations;
|
||||||
|
inode->i_op = &cgroup_file_inode_operations;
|
||||||
}
|
}
|
||||||
d_instantiate(dentry, inode);
|
d_instantiate(dentry, inode);
|
||||||
dget(dentry); /* Extra count - pin the dentry in core */
|
dget(dentry); /* Extra count - pin the dentry in core */
|
||||||
|
@ -2671,7 +2780,7 @@ static umode_t cgroup_file_mode(const struct cftype *cft)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cgroup_add_file(struct cgroup *cgrp, struct cgroup_subsys *subsys,
|
static int cgroup_add_file(struct cgroup *cgrp, struct cgroup_subsys *subsys,
|
||||||
const struct cftype *cft)
|
struct cftype *cft)
|
||||||
{
|
{
|
||||||
struct dentry *dir = cgrp->dentry;
|
struct dentry *dir = cgrp->dentry;
|
||||||
struct cgroup *parent = __d_cgrp(dir);
|
struct cgroup *parent = __d_cgrp(dir);
|
||||||
|
@ -2681,6 +2790,8 @@ static int cgroup_add_file(struct cgroup *cgrp, struct cgroup_subsys *subsys,
|
||||||
umode_t mode;
|
umode_t mode;
|
||||||
char name[MAX_CGROUP_TYPE_NAMELEN + MAX_CFTYPE_NAME + 2] = { 0 };
|
char name[MAX_CGROUP_TYPE_NAMELEN + MAX_CFTYPE_NAME + 2] = { 0 };
|
||||||
|
|
||||||
|
simple_xattrs_init(&cft->xattrs);
|
||||||
|
|
||||||
/* does @cft->flags tell us to skip creation on @cgrp? */
|
/* does @cft->flags tell us to skip creation on @cgrp? */
|
||||||
if ((cft->flags & CFTYPE_NOT_ON_ROOT) && !cgrp->parent)
|
if ((cft->flags & CFTYPE_NOT_ON_ROOT) && !cgrp->parent)
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -2721,9 +2832,9 @@ out:
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cgroup_addrm_files(struct cgroup *cgrp, struct cgroup_subsys *subsys,
|
static int cgroup_addrm_files(struct cgroup *cgrp, struct cgroup_subsys *subsys,
|
||||||
const struct cftype cfts[], bool is_add)
|
struct cftype cfts[], bool is_add)
|
||||||
{
|
{
|
||||||
const struct cftype *cft;
|
struct cftype *cft;
|
||||||
int err, ret = 0;
|
int err, ret = 0;
|
||||||
|
|
||||||
for (cft = cfts; cft->name[0] != '\0'; cft++) {
|
for (cft = cfts; cft->name[0] != '\0'; cft++) {
|
||||||
|
@ -2757,7 +2868,7 @@ static void cgroup_cfts_prepare(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cgroup_cfts_commit(struct cgroup_subsys *ss,
|
static void cgroup_cfts_commit(struct cgroup_subsys *ss,
|
||||||
const struct cftype *cfts, bool is_add)
|
struct cftype *cfts, bool is_add)
|
||||||
__releases(&cgroup_mutex) __releases(&cgroup_cft_mutex)
|
__releases(&cgroup_mutex) __releases(&cgroup_cft_mutex)
|
||||||
{
|
{
|
||||||
LIST_HEAD(pending);
|
LIST_HEAD(pending);
|
||||||
|
@ -2808,7 +2919,7 @@ static void cgroup_cfts_commit(struct cgroup_subsys *ss,
|
||||||
* function currently returns 0 as long as @cfts registration is successful
|
* function currently returns 0 as long as @cfts registration is successful
|
||||||
* even if some file creation attempts on existing cgroups fail.
|
* even if some file creation attempts on existing cgroups fail.
|
||||||
*/
|
*/
|
||||||
int cgroup_add_cftypes(struct cgroup_subsys *ss, const struct cftype *cfts)
|
int cgroup_add_cftypes(struct cgroup_subsys *ss, struct cftype *cfts)
|
||||||
{
|
{
|
||||||
struct cftype_set *set;
|
struct cftype_set *set;
|
||||||
|
|
||||||
|
@ -2838,7 +2949,7 @@ EXPORT_SYMBOL_GPL(cgroup_add_cftypes);
|
||||||
* Returns 0 on successful unregistration, -ENOENT if @cfts is not
|
* Returns 0 on successful unregistration, -ENOENT if @cfts is not
|
||||||
* registered with @ss.
|
* registered with @ss.
|
||||||
*/
|
*/
|
||||||
int cgroup_rm_cftypes(struct cgroup_subsys *ss, const struct cftype *cfts)
|
int cgroup_rm_cftypes(struct cgroup_subsys *ss, struct cftype *cfts)
|
||||||
{
|
{
|
||||||
struct cftype_set *set;
|
struct cftype_set *set;
|
||||||
|
|
||||||
|
@ -3843,18 +3954,29 @@ static struct cftype files[] = {
|
||||||
{ } /* terminate */
|
{ } /* terminate */
|
||||||
};
|
};
|
||||||
|
|
||||||
static int cgroup_populate_dir(struct cgroup *cgrp)
|
/**
|
||||||
|
* cgroup_populate_dir - selectively creation of files in a directory
|
||||||
|
* @cgrp: target cgroup
|
||||||
|
* @base_files: true if the base files should be added
|
||||||
|
* @subsys_mask: mask of the subsystem ids whose files should be added
|
||||||
|
*/
|
||||||
|
static int cgroup_populate_dir(struct cgroup *cgrp, bool base_files,
|
||||||
|
unsigned long subsys_mask)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
struct cgroup_subsys *ss;
|
struct cgroup_subsys *ss;
|
||||||
|
|
||||||
err = cgroup_addrm_files(cgrp, NULL, files, true);
|
if (base_files) {
|
||||||
if (err < 0)
|
err = cgroup_addrm_files(cgrp, NULL, files, true);
|
||||||
return err;
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
/* process cftsets of each subsystem */
|
/* process cftsets of each subsystem */
|
||||||
for_each_subsys(cgrp->root, ss) {
|
for_each_subsys(cgrp->root, ss) {
|
||||||
struct cftype_set *set;
|
struct cftype_set *set;
|
||||||
|
if (!test_bit(ss->subsys_id, &subsys_mask))
|
||||||
|
continue;
|
||||||
|
|
||||||
list_for_each_entry(set, &ss->cftsets, node)
|
list_for_each_entry(set, &ss->cftsets, node)
|
||||||
cgroup_addrm_files(cgrp, ss, set->cfts, true);
|
cgroup_addrm_files(cgrp, ss, set->cfts, true);
|
||||||
|
@ -3988,7 +4110,7 @@ static long cgroup_create(struct cgroup *parent, struct dentry *dentry,
|
||||||
|
|
||||||
list_add_tail(&cgrp->allcg_node, &root->allcg_list);
|
list_add_tail(&cgrp->allcg_node, &root->allcg_list);
|
||||||
|
|
||||||
err = cgroup_populate_dir(cgrp);
|
err = cgroup_populate_dir(cgrp, true, root->subsys_mask);
|
||||||
/* If err < 0, we have a half-filled directory - oh well ;) */
|
/* If err < 0, we have a half-filled directory - oh well ;) */
|
||||||
|
|
||||||
mutex_unlock(&cgroup_mutex);
|
mutex_unlock(&cgroup_mutex);
|
||||||
|
@ -4321,8 +4443,7 @@ int __init_or_module cgroup_load_subsys(struct cgroup_subsys *ss)
|
||||||
* since cgroup_init_subsys will have already taken care of it.
|
* since cgroup_init_subsys will have already taken care of it.
|
||||||
*/
|
*/
|
||||||
if (ss->module == NULL) {
|
if (ss->module == NULL) {
|
||||||
/* a few sanity checks */
|
/* a sanity check */
|
||||||
BUG_ON(ss->subsys_id >= CGROUP_BUILTIN_SUBSYS_COUNT);
|
|
||||||
BUG_ON(subsys[ss->subsys_id] != ss);
|
BUG_ON(subsys[ss->subsys_id] != ss);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -4330,24 +4451,8 @@ int __init_or_module cgroup_load_subsys(struct cgroup_subsys *ss)
|
||||||
/* init base cftset */
|
/* init base cftset */
|
||||||
cgroup_init_cftsets(ss);
|
cgroup_init_cftsets(ss);
|
||||||
|
|
||||||
/*
|
|
||||||
* need to register a subsys id before anything else - for example,
|
|
||||||
* init_cgroup_css needs it.
|
|
||||||
*/
|
|
||||||
mutex_lock(&cgroup_mutex);
|
mutex_lock(&cgroup_mutex);
|
||||||
/* find the first empty slot in the array */
|
subsys[ss->subsys_id] = ss;
|
||||||
for (i = CGROUP_BUILTIN_SUBSYS_COUNT; i < CGROUP_SUBSYS_COUNT; i++) {
|
|
||||||
if (subsys[i] == NULL)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (i == CGROUP_SUBSYS_COUNT) {
|
|
||||||
/* maximum number of subsystems already registered! */
|
|
||||||
mutex_unlock(&cgroup_mutex);
|
|
||||||
return -EBUSY;
|
|
||||||
}
|
|
||||||
/* assign ourselves the subsys_id */
|
|
||||||
ss->subsys_id = i;
|
|
||||||
subsys[i] = ss;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* no ss->create seems to need anything important in the ss struct, so
|
* no ss->create seems to need anything important in the ss struct, so
|
||||||
|
@ -4356,7 +4461,7 @@ int __init_or_module cgroup_load_subsys(struct cgroup_subsys *ss)
|
||||||
css = ss->create(dummytop);
|
css = ss->create(dummytop);
|
||||||
if (IS_ERR(css)) {
|
if (IS_ERR(css)) {
|
||||||
/* failure case - need to deassign the subsys[] slot. */
|
/* failure case - need to deassign the subsys[] slot. */
|
||||||
subsys[i] = NULL;
|
subsys[ss->subsys_id] = NULL;
|
||||||
mutex_unlock(&cgroup_mutex);
|
mutex_unlock(&cgroup_mutex);
|
||||||
return PTR_ERR(css);
|
return PTR_ERR(css);
|
||||||
}
|
}
|
||||||
|
@ -4372,7 +4477,7 @@ int __init_or_module cgroup_load_subsys(struct cgroup_subsys *ss)
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dummytop->subsys[ss->subsys_id] = NULL;
|
dummytop->subsys[ss->subsys_id] = NULL;
|
||||||
ss->destroy(dummytop);
|
ss->destroy(dummytop);
|
||||||
subsys[i] = NULL;
|
subsys[ss->subsys_id] = NULL;
|
||||||
mutex_unlock(&cgroup_mutex);
|
mutex_unlock(&cgroup_mutex);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -4439,7 +4544,6 @@ void cgroup_unload_subsys(struct cgroup_subsys *ss)
|
||||||
|
|
||||||
mutex_lock(&cgroup_mutex);
|
mutex_lock(&cgroup_mutex);
|
||||||
/* deassign the subsys_id */
|
/* deassign the subsys_id */
|
||||||
BUG_ON(ss->subsys_id < CGROUP_BUILTIN_SUBSYS_COUNT);
|
|
||||||
subsys[ss->subsys_id] = NULL;
|
subsys[ss->subsys_id] = NULL;
|
||||||
|
|
||||||
/* remove subsystem from rootnode's list of subsystems */
|
/* remove subsystem from rootnode's list of subsystems */
|
||||||
|
@ -4502,10 +4606,13 @@ int __init cgroup_init_early(void)
|
||||||
for (i = 0; i < CSS_SET_TABLE_SIZE; i++)
|
for (i = 0; i < CSS_SET_TABLE_SIZE; i++)
|
||||||
INIT_HLIST_HEAD(&css_set_table[i]);
|
INIT_HLIST_HEAD(&css_set_table[i]);
|
||||||
|
|
||||||
/* at bootup time, we don't worry about modular subsystems */
|
for (i = 0; i < CGROUP_SUBSYS_COUNT; i++) {
|
||||||
for (i = 0; i < CGROUP_BUILTIN_SUBSYS_COUNT; i++) {
|
|
||||||
struct cgroup_subsys *ss = subsys[i];
|
struct cgroup_subsys *ss = subsys[i];
|
||||||
|
|
||||||
|
/* at bootup time, we don't worry about modular subsystems */
|
||||||
|
if (!ss || ss->module)
|
||||||
|
continue;
|
||||||
|
|
||||||
BUG_ON(!ss->name);
|
BUG_ON(!ss->name);
|
||||||
BUG_ON(strlen(ss->name) > MAX_CGROUP_TYPE_NAMELEN);
|
BUG_ON(strlen(ss->name) > MAX_CGROUP_TYPE_NAMELEN);
|
||||||
BUG_ON(!ss->create);
|
BUG_ON(!ss->create);
|
||||||
|
@ -4538,9 +4645,12 @@ int __init cgroup_init(void)
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
/* at bootup time, we don't worry about modular subsystems */
|
for (i = 0; i < CGROUP_SUBSYS_COUNT; i++) {
|
||||||
for (i = 0; i < CGROUP_BUILTIN_SUBSYS_COUNT; i++) {
|
|
||||||
struct cgroup_subsys *ss = subsys[i];
|
struct cgroup_subsys *ss = subsys[i];
|
||||||
|
|
||||||
|
/* at bootup time, we don't worry about modular subsystems */
|
||||||
|
if (!ss || ss->module)
|
||||||
|
continue;
|
||||||
if (!ss->early_init)
|
if (!ss->early_init)
|
||||||
cgroup_init_subsys(ss);
|
cgroup_init_subsys(ss);
|
||||||
if (ss->use_id)
|
if (ss->use_id)
|
||||||
|
@ -4735,13 +4845,16 @@ void cgroup_fork_callbacks(struct task_struct *child)
|
||||||
{
|
{
|
||||||
if (need_forkexit_callback) {
|
if (need_forkexit_callback) {
|
||||||
int i;
|
int i;
|
||||||
/*
|
for (i = 0; i < CGROUP_SUBSYS_COUNT; i++) {
|
||||||
* forkexit callbacks are only supported for builtin
|
|
||||||
* subsystems, and the builtin section of the subsys array is
|
|
||||||
* immutable, so we don't need to lock the subsys array here.
|
|
||||||
*/
|
|
||||||
for (i = 0; i < CGROUP_BUILTIN_SUBSYS_COUNT; i++) {
|
|
||||||
struct cgroup_subsys *ss = subsys[i];
|
struct cgroup_subsys *ss = subsys[i];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* forkexit callbacks are only supported for
|
||||||
|
* builtin subsystems.
|
||||||
|
*/
|
||||||
|
if (!ss || ss->module)
|
||||||
|
continue;
|
||||||
|
|
||||||
if (ss->fork)
|
if (ss->fork)
|
||||||
ss->fork(child);
|
ss->fork(child);
|
||||||
}
|
}
|
||||||
|
@ -4846,12 +4959,13 @@ void cgroup_exit(struct task_struct *tsk, int run_callbacks)
|
||||||
tsk->cgroups = &init_css_set;
|
tsk->cgroups = &init_css_set;
|
||||||
|
|
||||||
if (run_callbacks && need_forkexit_callback) {
|
if (run_callbacks && need_forkexit_callback) {
|
||||||
/*
|
for (i = 0; i < CGROUP_SUBSYS_COUNT; i++) {
|
||||||
* modular subsystems can't use callbacks, so no need to lock
|
|
||||||
* the subsys array
|
|
||||||
*/
|
|
||||||
for (i = 0; i < CGROUP_BUILTIN_SUBSYS_COUNT; i++) {
|
|
||||||
struct cgroup_subsys *ss = subsys[i];
|
struct cgroup_subsys *ss = subsys[i];
|
||||||
|
|
||||||
|
/* modular subsystems can't use callbacks */
|
||||||
|
if (!ss || ss->module)
|
||||||
|
continue;
|
||||||
|
|
||||||
if (ss->exit) {
|
if (ss->exit) {
|
||||||
struct cgroup *old_cgrp =
|
struct cgroup *old_cgrp =
|
||||||
rcu_dereference_raw(cg->subsys[i])->cgroup;
|
rcu_dereference_raw(cg->subsys[i])->cgroup;
|
||||||
|
@ -5037,13 +5151,17 @@ static int __init cgroup_disable(char *str)
|
||||||
while ((token = strsep(&str, ",")) != NULL) {
|
while ((token = strsep(&str, ",")) != NULL) {
|
||||||
if (!*token)
|
if (!*token)
|
||||||
continue;
|
continue;
|
||||||
/*
|
for (i = 0; i < CGROUP_SUBSYS_COUNT; i++) {
|
||||||
* cgroup_disable, being at boot time, can't know about module
|
|
||||||
* subsystems, so we don't worry about them.
|
|
||||||
*/
|
|
||||||
for (i = 0; i < CGROUP_BUILTIN_SUBSYS_COUNT; i++) {
|
|
||||||
struct cgroup_subsys *ss = subsys[i];
|
struct cgroup_subsys *ss = subsys[i];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* cgroup_disable, being at boot time, can't
|
||||||
|
* know about module subsystems, so we don't
|
||||||
|
* worry about them.
|
||||||
|
*/
|
||||||
|
if (!ss || ss->module)
|
||||||
|
continue;
|
||||||
|
|
||||||
if (!strcmp(token, ss->name)) {
|
if (!strcmp(token, ss->name)) {
|
||||||
ss->disabled = 1;
|
ss->disabled = 1;
|
||||||
printk(KERN_INFO "Disabling %s control group"
|
printk(KERN_INFO "Disabling %s control group"
|
||||||
|
|
171
mm/shmem.c
171
mm/shmem.c
|
@ -77,13 +77,6 @@ static struct vfsmount *shm_mnt;
|
||||||
/* Symlink up to this size is kmalloc'ed instead of using a swappable page */
|
/* Symlink up to this size is kmalloc'ed instead of using a swappable page */
|
||||||
#define SHORT_SYMLINK_LEN 128
|
#define SHORT_SYMLINK_LEN 128
|
||||||
|
|
||||||
struct shmem_xattr {
|
|
||||||
struct list_head list; /* anchored by shmem_inode_info->xattr_list */
|
|
||||||
char *name; /* xattr name */
|
|
||||||
size_t size;
|
|
||||||
char value[0];
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* shmem_fallocate and shmem_writepage communicate via inode->i_private
|
* shmem_fallocate and shmem_writepage communicate via inode->i_private
|
||||||
* (with i_mutex making sure that it has only one user at a time):
|
* (with i_mutex making sure that it has only one user at a time):
|
||||||
|
@ -636,7 +629,6 @@ static int shmem_setattr(struct dentry *dentry, struct iattr *attr)
|
||||||
static void shmem_evict_inode(struct inode *inode)
|
static void shmem_evict_inode(struct inode *inode)
|
||||||
{
|
{
|
||||||
struct shmem_inode_info *info = SHMEM_I(inode);
|
struct shmem_inode_info *info = SHMEM_I(inode);
|
||||||
struct shmem_xattr *xattr, *nxattr;
|
|
||||||
|
|
||||||
if (inode->i_mapping->a_ops == &shmem_aops) {
|
if (inode->i_mapping->a_ops == &shmem_aops) {
|
||||||
shmem_unacct_size(info->flags, inode->i_size);
|
shmem_unacct_size(info->flags, inode->i_size);
|
||||||
|
@ -650,10 +642,7 @@ static void shmem_evict_inode(struct inode *inode)
|
||||||
} else
|
} else
|
||||||
kfree(info->symlink);
|
kfree(info->symlink);
|
||||||
|
|
||||||
list_for_each_entry_safe(xattr, nxattr, &info->xattr_list, list) {
|
simple_xattrs_free(&info->xattrs);
|
||||||
kfree(xattr->name);
|
|
||||||
kfree(xattr);
|
|
||||||
}
|
|
||||||
BUG_ON(inode->i_blocks);
|
BUG_ON(inode->i_blocks);
|
||||||
shmem_free_inode(inode->i_sb);
|
shmem_free_inode(inode->i_sb);
|
||||||
clear_inode(inode);
|
clear_inode(inode);
|
||||||
|
@ -1377,7 +1366,7 @@ static struct inode *shmem_get_inode(struct super_block *sb, const struct inode
|
||||||
spin_lock_init(&info->lock);
|
spin_lock_init(&info->lock);
|
||||||
info->flags = flags & VM_NORESERVE;
|
info->flags = flags & VM_NORESERVE;
|
||||||
INIT_LIST_HEAD(&info->swaplist);
|
INIT_LIST_HEAD(&info->swaplist);
|
||||||
INIT_LIST_HEAD(&info->xattr_list);
|
simple_xattrs_init(&info->xattrs);
|
||||||
cache_no_acl(inode);
|
cache_no_acl(inode);
|
||||||
|
|
||||||
switch (mode & S_IFMT) {
|
switch (mode & S_IFMT) {
|
||||||
|
@ -2059,28 +2048,6 @@ static void shmem_put_link(struct dentry *dentry, struct nameidata *nd, void *co
|
||||||
* filesystem level, though.
|
* filesystem level, though.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
* Allocate new xattr and copy in the value; but leave the name to callers.
|
|
||||||
*/
|
|
||||||
static struct shmem_xattr *shmem_xattr_alloc(const void *value, size_t size)
|
|
||||||
{
|
|
||||||
struct shmem_xattr *new_xattr;
|
|
||||||
size_t len;
|
|
||||||
|
|
||||||
/* wrap around? */
|
|
||||||
len = sizeof(*new_xattr) + size;
|
|
||||||
if (len <= sizeof(*new_xattr))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
new_xattr = kmalloc(len, GFP_KERNEL);
|
|
||||||
if (!new_xattr)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
new_xattr->size = size;
|
|
||||||
memcpy(new_xattr->value, value, size);
|
|
||||||
return new_xattr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Callback for security_inode_init_security() for acquiring xattrs.
|
* Callback for security_inode_init_security() for acquiring xattrs.
|
||||||
*/
|
*/
|
||||||
|
@ -2090,11 +2057,11 @@ static int shmem_initxattrs(struct inode *inode,
|
||||||
{
|
{
|
||||||
struct shmem_inode_info *info = SHMEM_I(inode);
|
struct shmem_inode_info *info = SHMEM_I(inode);
|
||||||
const struct xattr *xattr;
|
const struct xattr *xattr;
|
||||||
struct shmem_xattr *new_xattr;
|
struct simple_xattr *new_xattr;
|
||||||
size_t len;
|
size_t len;
|
||||||
|
|
||||||
for (xattr = xattr_array; xattr->name != NULL; xattr++) {
|
for (xattr = xattr_array; xattr->name != NULL; xattr++) {
|
||||||
new_xattr = shmem_xattr_alloc(xattr->value, xattr->value_len);
|
new_xattr = simple_xattr_alloc(xattr->value, xattr->value_len);
|
||||||
if (!new_xattr)
|
if (!new_xattr)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
@ -2111,91 +2078,12 @@ static int shmem_initxattrs(struct inode *inode,
|
||||||
memcpy(new_xattr->name + XATTR_SECURITY_PREFIX_LEN,
|
memcpy(new_xattr->name + XATTR_SECURITY_PREFIX_LEN,
|
||||||
xattr->name, len);
|
xattr->name, len);
|
||||||
|
|
||||||
spin_lock(&info->lock);
|
simple_xattr_list_add(&info->xattrs, new_xattr);
|
||||||
list_add(&new_xattr->list, &info->xattr_list);
|
|
||||||
spin_unlock(&info->lock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int shmem_xattr_get(struct dentry *dentry, const char *name,
|
|
||||||
void *buffer, size_t size)
|
|
||||||
{
|
|
||||||
struct shmem_inode_info *info;
|
|
||||||
struct shmem_xattr *xattr;
|
|
||||||
int ret = -ENODATA;
|
|
||||||
|
|
||||||
info = SHMEM_I(dentry->d_inode);
|
|
||||||
|
|
||||||
spin_lock(&info->lock);
|
|
||||||
list_for_each_entry(xattr, &info->xattr_list, list) {
|
|
||||||
if (strcmp(name, xattr->name))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
ret = xattr->size;
|
|
||||||
if (buffer) {
|
|
||||||
if (size < xattr->size)
|
|
||||||
ret = -ERANGE;
|
|
||||||
else
|
|
||||||
memcpy(buffer, xattr->value, xattr->size);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
spin_unlock(&info->lock);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int shmem_xattr_set(struct inode *inode, const char *name,
|
|
||||||
const void *value, size_t size, int flags)
|
|
||||||
{
|
|
||||||
struct shmem_inode_info *info = SHMEM_I(inode);
|
|
||||||
struct shmem_xattr *xattr;
|
|
||||||
struct shmem_xattr *new_xattr = NULL;
|
|
||||||
int err = 0;
|
|
||||||
|
|
||||||
/* value == NULL means remove */
|
|
||||||
if (value) {
|
|
||||||
new_xattr = shmem_xattr_alloc(value, size);
|
|
||||||
if (!new_xattr)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
new_xattr->name = kstrdup(name, GFP_KERNEL);
|
|
||||||
if (!new_xattr->name) {
|
|
||||||
kfree(new_xattr);
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
spin_lock(&info->lock);
|
|
||||||
list_for_each_entry(xattr, &info->xattr_list, list) {
|
|
||||||
if (!strcmp(name, xattr->name)) {
|
|
||||||
if (flags & XATTR_CREATE) {
|
|
||||||
xattr = new_xattr;
|
|
||||||
err = -EEXIST;
|
|
||||||
} else if (new_xattr) {
|
|
||||||
list_replace(&xattr->list, &new_xattr->list);
|
|
||||||
} else {
|
|
||||||
list_del(&xattr->list);
|
|
||||||
}
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (flags & XATTR_REPLACE) {
|
|
||||||
xattr = new_xattr;
|
|
||||||
err = -ENODATA;
|
|
||||||
} else {
|
|
||||||
list_add(&new_xattr->list, &info->xattr_list);
|
|
||||||
xattr = NULL;
|
|
||||||
}
|
|
||||||
out:
|
|
||||||
spin_unlock(&info->lock);
|
|
||||||
if (xattr)
|
|
||||||
kfree(xattr->name);
|
|
||||||
kfree(xattr);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct xattr_handler *shmem_xattr_handlers[] = {
|
static const struct xattr_handler *shmem_xattr_handlers[] = {
|
||||||
#ifdef CONFIG_TMPFS_POSIX_ACL
|
#ifdef CONFIG_TMPFS_POSIX_ACL
|
||||||
&generic_acl_access_handler,
|
&generic_acl_access_handler,
|
||||||
|
@ -2226,6 +2114,7 @@ static int shmem_xattr_validate(const char *name)
|
||||||
static ssize_t shmem_getxattr(struct dentry *dentry, const char *name,
|
static ssize_t shmem_getxattr(struct dentry *dentry, const char *name,
|
||||||
void *buffer, size_t size)
|
void *buffer, size_t size)
|
||||||
{
|
{
|
||||||
|
struct shmem_inode_info *info = SHMEM_I(dentry->d_inode);
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2240,12 +2129,13 @@ static ssize_t shmem_getxattr(struct dentry *dentry, const char *name,
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
return shmem_xattr_get(dentry, name, buffer, size);
|
return simple_xattr_get(&info->xattrs, name, buffer, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int shmem_setxattr(struct dentry *dentry, const char *name,
|
static int shmem_setxattr(struct dentry *dentry, const char *name,
|
||||||
const void *value, size_t size, int flags)
|
const void *value, size_t size, int flags)
|
||||||
{
|
{
|
||||||
|
struct shmem_inode_info *info = SHMEM_I(dentry->d_inode);
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2260,15 +2150,12 @@ static int shmem_setxattr(struct dentry *dentry, const char *name,
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
if (size == 0)
|
return simple_xattr_set(&info->xattrs, name, value, size, flags);
|
||||||
value = ""; /* empty EA, do not remove */
|
|
||||||
|
|
||||||
return shmem_xattr_set(dentry->d_inode, name, value, size, flags);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int shmem_removexattr(struct dentry *dentry, const char *name)
|
static int shmem_removexattr(struct dentry *dentry, const char *name)
|
||||||
{
|
{
|
||||||
|
struct shmem_inode_info *info = SHMEM_I(dentry->d_inode);
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2283,45 +2170,13 @@ static int shmem_removexattr(struct dentry *dentry, const char *name)
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
return shmem_xattr_set(dentry->d_inode, name, NULL, 0, XATTR_REPLACE);
|
return simple_xattr_remove(&info->xattrs, name);
|
||||||
}
|
|
||||||
|
|
||||||
static bool xattr_is_trusted(const char *name)
|
|
||||||
{
|
|
||||||
return !strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t shmem_listxattr(struct dentry *dentry, char *buffer, size_t size)
|
static ssize_t shmem_listxattr(struct dentry *dentry, char *buffer, size_t size)
|
||||||
{
|
{
|
||||||
bool trusted = capable(CAP_SYS_ADMIN);
|
struct shmem_inode_info *info = SHMEM_I(dentry->d_inode);
|
||||||
struct shmem_xattr *xattr;
|
return simple_xattr_list(&info->xattrs, buffer, size);
|
||||||
struct shmem_inode_info *info;
|
|
||||||
size_t used = 0;
|
|
||||||
|
|
||||||
info = SHMEM_I(dentry->d_inode);
|
|
||||||
|
|
||||||
spin_lock(&info->lock);
|
|
||||||
list_for_each_entry(xattr, &info->xattr_list, list) {
|
|
||||||
size_t len;
|
|
||||||
|
|
||||||
/* skip "trusted." attributes for unprivileged callers */
|
|
||||||
if (!trusted && xattr_is_trusted(xattr->name))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
len = strlen(xattr->name) + 1;
|
|
||||||
used += len;
|
|
||||||
if (buffer) {
|
|
||||||
if (size < used) {
|
|
||||||
used = -ERANGE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
memcpy(buffer, xattr->name, len);
|
|
||||||
buffer += len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
spin_unlock(&info->lock);
|
|
||||||
|
|
||||||
return used;
|
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_TMPFS_XATTR */
|
#endif /* CONFIG_TMPFS_XATTR */
|
||||||
|
|
||||||
|
|
|
@ -326,9 +326,7 @@ struct cgroup_subsys net_prio_subsys = {
|
||||||
.create = cgrp_create,
|
.create = cgrp_create,
|
||||||
.destroy = cgrp_destroy,
|
.destroy = cgrp_destroy,
|
||||||
.attach = net_prio_attach,
|
.attach = net_prio_attach,
|
||||||
#ifdef CONFIG_NETPRIO_CGROUP
|
|
||||||
.subsys_id = net_prio_subsys_id,
|
.subsys_id = net_prio_subsys_id,
|
||||||
#endif
|
|
||||||
.base_cftypes = ss_files,
|
.base_cftypes = ss_files,
|
||||||
.module = THIS_MODULE
|
.module = THIS_MODULE
|
||||||
};
|
};
|
||||||
|
@ -366,10 +364,6 @@ static int __init init_cgroup_netprio(void)
|
||||||
ret = cgroup_load_subsys(&net_prio_subsys);
|
ret = cgroup_load_subsys(&net_prio_subsys);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out;
|
goto out;
|
||||||
#ifndef CONFIG_NETPRIO_CGROUP
|
|
||||||
smp_wmb();
|
|
||||||
net_prio_subsys_id = net_prio_subsys.subsys_id;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
register_netdevice_notifier(&netprio_device_notifier);
|
register_netdevice_notifier(&netprio_device_notifier);
|
||||||
|
|
||||||
|
@ -386,11 +380,6 @@ static void __exit exit_cgroup_netprio(void)
|
||||||
|
|
||||||
cgroup_unload_subsys(&net_prio_subsys);
|
cgroup_unload_subsys(&net_prio_subsys);
|
||||||
|
|
||||||
#ifndef CONFIG_NETPRIO_CGROUP
|
|
||||||
net_prio_subsys_id = -1;
|
|
||||||
synchronize_rcu();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
rtnl_lock();
|
rtnl_lock();
|
||||||
for_each_netdev(&init_net, dev) {
|
for_each_netdev(&init_net, dev) {
|
||||||
old = rtnl_dereference(dev->priomap);
|
old = rtnl_dereference(dev->priomap);
|
||||||
|
|
|
@ -326,17 +326,6 @@ int __sk_backlog_rcv(struct sock *sk, struct sk_buff *skb)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(__sk_backlog_rcv);
|
EXPORT_SYMBOL(__sk_backlog_rcv);
|
||||||
|
|
||||||
#if defined(CONFIG_CGROUPS)
|
|
||||||
#if !defined(CONFIG_NET_CLS_CGROUP)
|
|
||||||
int net_cls_subsys_id = -1;
|
|
||||||
EXPORT_SYMBOL_GPL(net_cls_subsys_id);
|
|
||||||
#endif
|
|
||||||
#if !defined(CONFIG_NETPRIO_CGROUP)
|
|
||||||
int net_prio_subsys_id = -1;
|
|
||||||
EXPORT_SYMBOL_GPL(net_prio_subsys_id);
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static int sock_set_timeout(long *timeo_p, char __user *optval, int optlen)
|
static int sock_set_timeout(long *timeo_p, char __user *optval, int optlen)
|
||||||
{
|
{
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
|
@ -1224,6 +1213,7 @@ static void sk_prot_free(struct proto *prot, struct sock *sk)
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_CGROUPS
|
#ifdef CONFIG_CGROUPS
|
||||||
|
#if IS_ENABLED(CONFIG_NET_CLS_CGROUP)
|
||||||
void sock_update_classid(struct sock *sk)
|
void sock_update_classid(struct sock *sk)
|
||||||
{
|
{
|
||||||
u32 classid;
|
u32 classid;
|
||||||
|
@ -1235,7 +1225,9 @@ void sock_update_classid(struct sock *sk)
|
||||||
sk->sk_classid = classid;
|
sk->sk_classid = classid;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(sock_update_classid);
|
EXPORT_SYMBOL(sock_update_classid);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if IS_ENABLED(CONFIG_NETPRIO_CGROUP)
|
||||||
void sock_update_netprioidx(struct sock *sk, struct task_struct *task)
|
void sock_update_netprioidx(struct sock *sk, struct task_struct *task)
|
||||||
{
|
{
|
||||||
if (in_interrupt())
|
if (in_interrupt())
|
||||||
|
@ -1245,6 +1237,7 @@ void sock_update_netprioidx(struct sock *sk, struct task_struct *task)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(sock_update_netprioidx);
|
EXPORT_SYMBOL_GPL(sock_update_netprioidx);
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* sk_alloc - All socket objects are allocated here
|
* sk_alloc - All socket objects are allocated here
|
||||||
|
|
|
@ -77,9 +77,7 @@ struct cgroup_subsys net_cls_subsys = {
|
||||||
.name = "net_cls",
|
.name = "net_cls",
|
||||||
.create = cgrp_create,
|
.create = cgrp_create,
|
||||||
.destroy = cgrp_destroy,
|
.destroy = cgrp_destroy,
|
||||||
#ifdef CONFIG_NET_CLS_CGROUP
|
|
||||||
.subsys_id = net_cls_subsys_id,
|
.subsys_id = net_cls_subsys_id,
|
||||||
#endif
|
|
||||||
.base_cftypes = ss_files,
|
.base_cftypes = ss_files,
|
||||||
.module = THIS_MODULE,
|
.module = THIS_MODULE,
|
||||||
};
|
};
|
||||||
|
@ -283,12 +281,6 @@ static int __init init_cgroup_cls(void)
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
#ifndef CONFIG_NET_CLS_CGROUP
|
|
||||||
/* We can't use rcu_assign_pointer because this is an int. */
|
|
||||||
smp_wmb();
|
|
||||||
net_cls_subsys_id = net_cls_subsys.subsys_id;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
ret = register_tcf_proto_ops(&cls_cgroup_ops);
|
ret = register_tcf_proto_ops(&cls_cgroup_ops);
|
||||||
if (ret)
|
if (ret)
|
||||||
cgroup_unload_subsys(&net_cls_subsys);
|
cgroup_unload_subsys(&net_cls_subsys);
|
||||||
|
@ -301,11 +293,6 @@ static void __exit exit_cgroup_cls(void)
|
||||||
{
|
{
|
||||||
unregister_tcf_proto_ops(&cls_cgroup_ops);
|
unregister_tcf_proto_ops(&cls_cgroup_ops);
|
||||||
|
|
||||||
#ifndef CONFIG_NET_CLS_CGROUP
|
|
||||||
net_cls_subsys_id = -1;
|
|
||||||
synchronize_rcu();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
cgroup_unload_subsys(&net_cls_subsys);
|
cgroup_unload_subsys(&net_cls_subsys);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue