/* * kernel/cpuset.c * * Processor and Memory placement constraints for sets of tasks. * * Copyright (C) 2003 BULL SA. * Copyright (C) 2004 Silicon Graphics, Inc. * * Portions derived from Patrick Mochel's sysfs code. * sysfs is Copyright (c) 2001-3 Patrick Mochel * Portions Copyright (c) 2004 Silicon Graphics, Inc. * * 2003-10-10 Written by Simon Derr <simon.derr@bull.net> * 2003-10-22 Updates by Stephen Hemminger. * 2004 May-July Rework by Paul Jackson <pj@sgi.com> * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of the Linux * distribution for more details. */ #include <linux/config.h>#include <linux/cpu.h>#include <linux/cpumask.h>#include <linux/cpuset.h>#include <linux/err.h>#include <linux/errno.h>#include <linux/file.h>#include <linux/fs.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/kernel.h>#include <linux/kmod.h>#include <linux/list.h>#include <linux/mm.h>#include <linux/module.h>#include <linux/mount.h>#include <linux/namei.h>#include <linux/pagemap.h>#include <linux/proc_fs.h>#include <linux/sched.h>#include <linux/seq_file.h>#include <linux/slab.h>#include <linux/smp_lock.h>#include <linux/spinlock.h>#include <linux/stat.h>#include <linux/string.h>#include <linux/time.h>#include <linux/backing-dev.h>#include <linux/sort.h> #include <asm/uaccess.h>#include <asm/atomic.h>#include <asm/semaphore.h> #define CPUSET_SUPER_MAGIC 0x27e0eb struct cpuset { unsigned long flags; /* "unsigned long" so bitops work */ cpumask_t cpus_allowed; /* CPUs allowed to tasks in cpuset */ nodemask_t mems_allowed; /* Memory Nodes allowed to tasks */ atomic_t count; /* count tasks using this cpuset */ /* * We link our 'sibling' struct into our parents 'children'. * Our children link their 'sibling' into our 'children'. */ struct list_head sibling; /* my parents children */ struct list_head children; /* my children */ struct cpuset *parent; /* my parent */ struct dentry *dentry; /* cpuset fs entry */ /* * Copy of global cpuset_mems_generation as of the most * recent time this cpuset changed its mems_allowed. */ int mems_generation;}; /* bits in struct cpuset flags field */typedef enum { CS_CPU_EXCLUSIVE, CS_MEM_EXCLUSIVE, CS_REMOVED, CS_NOTIFY_ON_RELEASE} cpuset_flagbits_t; /* convenient tests for these bits */static inline int is_cpu_exclusive(const struct cpuset *cs){ return !!test_bit(CS_CPU_EXCLUSIVE, &cs->flags);} static inline int is_mem_exclusive(const struct cpuset *cs){ return !!test_bit(CS_MEM_EXCLUSIVE, &cs->flags);} static inline int is_removed(const struct cpuset *cs){ return !!test_bit(CS_REMOVED, &cs->flags);} static inline int notify_on_release(const struct cpuset *cs){ return !!test_bit(CS_NOTIFY_ON_RELEASE, &cs->flags);} /* * Increment this atomic integer everytime any cpuset changes its * mems_allowed value. Users of cpusets can track this generation * number, and avoid having to lock and reload mems_allowed unless * the cpuset they're using changes generation. * * A single, global generation is needed because attach_task() could * reattach a task to a different cpuset, which must not have its * generation numbers aliased with those of that tasks previous cpuset. * * Generations are needed for mems_allowed because one task cannot * modify anothers memory placement. So we must enable every task, * on every visit to __alloc_pages(), to efficiently check whether * its current->cpuset->mems_allowed has changed, requiring an update * of its current->mems_allowed. */static atomic_t cpuset_mems_generation = ATOMIC_INIT(1); static struct cpuset top_cpuset = { .flags = ((1 << CS_CPU_EXCLUSIVE) | (1 << CS_MEM_EXCLUSIVE)), .cpus_allowed = CPU_MASK_ALL, .mems_allowed = NODE_MASK_ALL, .count = ATOMIC_INIT(0), .sibling = LIST_HEAD_INIT(top_cpuset.sibling), .children = LIST_HEAD_INIT(top_cpuset.children), .parent = NULL, .dentry = NULL, .mems_generation = 0,}; static struct vfsmount *cpuset_mount;static struct super_block *cpuset_sb = NULL; /* * cpuset_sem should be held by anyone who is depending on the children * or sibling lists of any cpuset, or performing non-atomic operations * on the flags or *_allowed values of a cpuset, such as raising the * CS_REMOVED flag bit iff it is not already raised, or reading and * conditionally modifying the *_allowed values. One kernel global * cpuset semaphore should be sufficient - these things don't change * that much. * * The code that modifies cpusets holds cpuset_sem across the entire * operation, from cpuset_common_file_write() down, single threading * all cpuset modifications (except for counter manipulations from * fork and exit) across the system. This presumes that cpuset * modifications are rare - better kept simple and safe, even if slow. * * The code that reads cpusets, such as in cpuset_common_file_read() * and below, only holds cpuset_sem across small pieces of code, such * as when reading out possibly multi-word cpumasks and nodemasks, as * the risks are less, and the desire for performance a little greater. * The proc_cpuset_show() routine needs to hold cpuset_sem to insure * that no cs->dentry is NULL, as it walks up the cpuset tree to root. * * The hooks from fork and exit, cpuset_fork() and cpuset_exit(), don't * (usually) grab cpuset_sem. These are the two most performance * critical pieces of code here. The exception occurs on exit(), * when a task in a notify_on_release cpuset exits. Then cpuset_sem * is taken, and if the cpuset count is zero, a usermode call made * to /sbin/cpuset_release_agent with the name of the cpuset (path * relative to the root of cpuset file system) as the argument. * * A cpuset can only be deleted if both its 'count' of using tasks is * zero, and its list of 'children' cpusets is empty. Since all tasks * in the system use _some_ cpuset, and since there is always at least * one task in the system (init, pid == 1), therefore, top_cpuset * always has either children cpusets and/or using tasks. So no need * for any special hack to ensure that top_cpuset cannot be deleted. */ static DECLARE_MUTEX(cpuset_sem); /* * A couple of forward declarations required, due to cyclic reference loop: * cpuset_mkdir -> cpuset_create -> cpuset_populate_dir -> cpuset_add_file * -> cpuset_create_file -> cpuset_dir_inode_operations -> cpuset_mkdir. */ static int cpuset_mkdir(struct inode *dir, struct dentry *dentry, int mode);static int cpuset_rmdir(struct inode *unused_dir, struct dentry *dentry); static struct backing_dev_info cpuset_backing_dev_info = { .ra_pages = 0, /* No readahead */ .capabilities = BDI_CAP_NO_ACCT_DIRTY | BDI_CAP_NO_WRITEBACK,}; static struct inode *cpuset_new_inode(mode_t mode){ struct inode *inode = new_inode(cpuset_sb); if (inode) { inode->i_mode = mode; inode->i_uid = current->fsuid; inode->i_gid = current->fsgid; inode->i_blksize = PAGE_CACHE_SIZE; inode->i_blocks = 0; inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; inode->i_mapping->backing_dev_info = &cpuset_backing_dev_info; } return inode;} static void cpuset_diput(struct dentry *dentry, struct inode *inode){ /* is dentry a directory ? if so, kfree() associated cpuset */ if (S_ISDIR(inode->i_mode)) { struct cpuset *cs = dentry->d_fsdata; BUG_ON(!(is_removed(cs))); kfree(cs); } iput(inode);} static struct dentry_operations cpuset_dops = { .d_iput = cpuset_diput,}; static struct dentry *cpuset_get_dentry(struct dentry *parent, const char *name){ struct dentry *d = lookup_one_len(name, parent, strlen(name)); if (!IS_ERR(d)) d->d_op = &cpuset_dops; return d;} static void remove_dir(struct dentry *d){ struct dentry *parent = dget(d->d_parent); d_delete(d); simple_rmdir(parent->d_inode, d); dput(parent);} /* * NOTE : the dentry must have been dget()'ed */static void cpuset_d_remove_dir(struct dentry *dentry){ struct list_head *node; spin_lock(&dcache_lock); node = dentry->d_subdirs.next; while (node != &dentry->d_subdirs) { struct dentry *d = list_entry(node, struct dentry, d_child); list_del_init(node); if (d->d_inode) { d = dget_locked(d); spin_unlock(&dcache_lock); d_delete(d); simple_unlink(dentry->d_inode, d); dput(d); spin_lock(&dcache_lock); } node = dentry->d_subdirs.next; } list_del_init(&dentry->d_child); spin_unlock(&dcache_lock); remove_dir(dentry);} static struct super_operations cpuset_ops = { .statfs = simple_statfs, .drop_inode = generic_delete_inode,}; static int cpuset_fill_super(struct super_block *sb, void *unused_data, int unused_silent){ struct inode *inode; struct dentry *root; sb->s_blocksize = PAGE_CACHE_SIZE; sb->s_blocksize_bits = PAGE_CACHE_SHIFT; sb->s_magic = CPUSET_SUPER_MAGIC; sb->s_op = &cpuset_ops; cpuset_sb = sb; inode = cpuset_new_inode(S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR); if (inode) { inode->i_op = &simple_dir_inode_operations; inode->i_fop = &simple_dir_operations; /* directories start off with i_nlink == 2 (for "." entry) */ inode->i_nlink++; } else { return -ENOMEM; } root = d_alloc_root(inode); if (!root) { iput(inode); return -ENOMEM; } sb->s_root = root; return 0;} static struct super_block *cpuset_get_sb(struct file_system_type *fs_type, int flags, const char *unused_dev_name, void *data){ return get_sb_single(fs_type, flags, data, cpuset_fill_super);} static struct file_system_type cpuset_fs_type = { .name = "cpuset", .get_sb = cpuset_get_sb, .kill_sb = kill_litter_super,}; /* struct cftype: * * The files in the cpuset filesystem mostly have a very simple read/write * handling, some common function will take care of it. Nevertheless some cases * (read tasks) are special and therefore I define this structure for every * kind of file. * * * When reading/writing to a file: * - the cpuset to use in file->f_dentry->d_parent->d_fsdata * - the 'cftype' of the file is file->f_dentry->d_fsdata */ struct cftype { char *name; int private; int (*open) (struct inode *inode, struct file *file); ssize_t (*read) (struct file *file, char __user *buf, size_t nbytes, loff_t *ppos); int (*write) (struct file *file, const char __user *buf, size_t nbytes, loff_t *ppos); int (*release) (struct inode *inode, struct file *file);}; static inline struct cpuset *__d_cs(struct dentry *dentry){ return dentry->d_fsdata;} static inline struct cftype *__d_cft(struct dentry *dentry){ return dentry->d_fsdata;} /* * Call with cpuset_sem held. Writes path of cpuset into buf. * Returns 0 on success, -errno on error. */ static int cpuset_path(const struct cpuset *cs, char *buf, int buflen){ char *start; start = buf + buflen; *--start = '\0'; for (;;) { int len = cs->dentry->d_name.len; if ((start -= len) < buf) return -ENAMETOOLONG; memcpy(start, cs->dentry->d_name.name, len); cs = cs->parent; if (!cs) break; if (!cs->parent) continue; if (--start < buf) return -ENAMETOOLONG; *start = '/'; } memmove(buf, start, buf + buflen - start); return 0;} /* * Notify userspace when a cpuset is released, by running * /sbin/cpuset_release_agent with the name of the cpuset (path * relative to the root of cpuset file system) as the argument. * * Most likely, this user command will try to rmdir this cpuset. * * This races with the possibility that some other task will be * attached to this cpuset before it is removed, or that some other * user task will 'mkdir' a child cpuset of this cpuset. That's ok. * The presumed 'rmdir' will fail quietly if this cpuset is no longer * unused, and this cpuset will be reprieved from its death sentence, * to continue to serve a useful existence. Next time it's released, * we will get notified again, if it still has 'notify_on_release' set. * * The final arg to call_usermodehelper() is 0, which means don't * wait. The separate /sbin/cpuset_release_agent task is forked by * call_usermodehelper(), then control in this thread returns here, * without waiting for the release agent task. We don't bother to * wait because the caller of this routine has no use for the exit * status of the /sbin/cpuset_release_agent task, so no sense holding * our caller up for that. * * The simple act of forking that task might require more memory, * which might need cpuset_sem. So this routine must be called while * cpuset_sem is not held, to avoid a possible deadlock. See also * comments for check_for_release(), below. */ static void cpuset_release_agent(const char *pathbuf){ char *argv[3], *envp[3]; int i; if (!pathbuf) return; i = 0; argv[i++] = "/sbin/cpuset_release_agent"; argv[i++] = (char *)pathbuf; argv[i] = NULL; i = 0; /* minimal command environment */ envp[i++] = "HOME=/"; envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; envp[i] = NULL; call_usermodehelper(argv[0], argv, envp, 0); kfree(pathbuf);} /* * Either cs->count of using tasks transitioned to zero, or the * cs->children list of child cpusets just became empty. If this * cs is notify_on_release() and now both the user count is zero and * the list of children is empty, prepare cpuset path in a kmalloc'd * buffer, to be returned via ppathbuf, so that the caller can invoke * cpuset_release_agent() with it later on, once cpuset_sem is dropped. * Call here with cpuset_sem held. * * This check_for_release() routine is responsible for kmalloc'ing * pathbuf. The above cpuset_release_agent() is responsible for * kfree'ing pathbuf. The caller of these routines is responsible * for providing a pathbuf pointer, initialized to NULL, then * calling check_for_release() with cpuset_sem held and the address * of the pathbuf pointer, then dropping cpuset_sem, then calling * cpuset_release_agent() with pathbuf, as set by check_for_release(). */ static void check_for_release(struct cpuset *cs, char **ppathbuf){ if (notify_on_release(cs) && atomic_read(&cs->count) == 0 && list_empty(&cs->children)) { char *buf; buf = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!buf) return; if (cpuset_path(cs, buf, PAGE_SIZE) < 0) kfree(buf); else *ppathbuf = buf; }} /* * Return in *pmask the portion of a cpusets's cpus_allowed that * are online. If none are online, walk up the cpuset hierarchy * until we find one that does have some online cpus. If we get * all the way to the top and still haven't found any online cpus, * return cpu_online_map. Or if passed a NULL cs from an exit'ing * task, return cpu_online_map. * * One way or another, we guarantee to return some non-empty subset * of cpu_online_map. * * Call with cpuset_sem held. */ static void guarantee_online_cpus(const struct cpuset *cs, cpumask_t *pmask){ while (cs && !cpus_intersects(cs->cpus_allowed, cpu_online_map)) cs = cs->parent; if (cs) cpus_and(*pmask, cs->cpus_allowed, cpu_online_map); else *pmask = cpu_online_map; BUG_ON(!cpus_intersects(*pmask, cpu_online_map));} /* * Return in *pmask the portion of a cpusets's mems_allowed that * are online. If none are online, walk up the cpuset hierarchy * until we find one that does have some online mems. If we getLines 1–500 of 1688. Load more
/* * kernel/cpuset.c * * Processor and Memory placement constraints for sets of tasks. * * Copyright (C) 2003 BULL SA. * Copyright (C) 2004 Silicon Graphics, Inc. * * Portions derived from Patrick Mochel's sysfs code. * sysfs is Copyright (c) 2001-3 Patrick Mochel * Portions Copyright (c) 2004 Silicon Graphics, Inc. * * 2003-10-10 Written by Simon Derr <simon.derr@bull.net> * 2003-10-22 Updates by Stephen Hemminger. * 2004 May-July Rework by Paul Jackson <pj@sgi.com> * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of the Linux * distribution for more details. */ #include <linux/config.h> #include <linux/cpu.h> #include <linux/cpumask.h> #include <linux/cpuset.h> #include <linux/err.h> #include <linux/errno.h> #include <linux/file.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/kmod.h> #include <linux/list.h> #include <linux/mm.h> #include <linux/module.h> #include <linux/mount.h> #include <linux/namei.h> #include <linux/pagemap.h> #include <linux/proc_fs.h> #include <linux/sched.h> #include <linux/seq_file.h> #include <linux/slab.h> #include <linux/smp_lock.h> #include <linux/spinlock.h> #include <linux/stat.h> #include <linux/string.h> #include <linux/time.h> #include <linux/backing-dev.h> #include <linux/sort.h> #include <asm/uaccess.h> #include <asm/atomic.h> #include <asm/semaphore.h> #define CPUSET_SUPER_MAGIC 0x27e0eb struct cpuset { unsigned long flags; /* "unsigned long" so bitops work */ cpumask_t cpus_allowed; /* CPUs allowed to tasks in cpuset */ nodemask_t mems_allowed; /* Memory Nodes allowed to tasks */ atomic_t count; /* count tasks using this cpuset */ /* * We link our 'sibling' struct into our parents 'children'. * Our children link their 'sibling' into our 'children'. */ struct list_head sibling; /* my parents children */ struct list_head children; /* my children */ struct cpuset *parent; /* my parent */ struct dentry *dentry; /* cpuset fs entry */ /* * Copy of global cpuset_mems_generation as of the most * recent time this cpuset changed its mems_allowed. */ int mems_generation; }; /* bits in struct cpuset flags field */ typedef enum { CS_CPU_EXCLUSIVE, CS_MEM_EXCLUSIVE, CS_REMOVED, CS_NOTIFY_ON_RELEASE } cpuset_flagbits_t; /* convenient tests for these bits */ static inline int is_cpu_exclusive(const struct cpuset *cs) { return !!test_bit(CS_CPU_EXCLUSIVE, &cs->flags); } static inline int is_mem_exclusive(const struct cpuset *cs) { return !!test_bit(CS_MEM_EXCLUSIVE, &cs->flags); } static inline int is_removed(const struct cpuset *cs) { return !!test_bit(CS_REMOVED, &cs->flags); } static inline int notify_on_release(const struct cpuset *cs) { return !!test_bit(CS_NOTIFY_ON_RELEASE, &cs->flags); } /* * Increment this atomic integer everytime any cpuset changes its * mems_allowed value. Users of cpusets can track this generation * number, and avoid having to lock and reload mems_allowed unless * the cpuset they're using changes generation. * * A single, global generation is needed because attach_task() could * reattach a task to a different cpuset, which must not have its * generation numbers aliased with those of that tasks previous cpuset. * * Generations are needed for mems_allowed because one task cannot * modify anothers memory placement. So we must enable every task, * on every visit to __alloc_pages(), to efficiently check whether * its current->cpuset->mems_allowed has changed, requiring an update * of its current->mems_allowed. */ static atomic_t cpuset_mems_generation = ATOMIC_INIT(1); static struct cpuset top_cpuset = { .flags = ((1 << CS_CPU_EXCLUSIVE) | (1 << CS_MEM_EXCLUSIVE)), .cpus_allowed = CPU_MASK_ALL, .mems_allowed = NODE_MASK_ALL, .count = ATOMIC_INIT(0), .sibling = LIST_HEAD_INIT(top_cpuset.sibling), .children = LIST_HEAD_INIT(top_cpuset.children), .parent = NULL, .dentry = NULL, .mems_generation = 0, }; static struct vfsmount *cpuset_mount; static struct super_block *cpuset_sb = NULL; /* * cpuset_sem should be held by anyone who is depending on the children * or sibling lists of any cpuset, or performing non-atomic operations * on the flags or *_allowed values of a cpuset, such as raising the * CS_REMOVED flag bit iff it is not already raised, or reading and * conditionally modifying the *_allowed values. One kernel global * cpuset semaphore should be sufficient - these things don't change * that much. * * The code that modifies cpusets holds cpuset_sem across the entire * operation, from cpuset_common_file_write() down, single threading * all cpuset modifications (except for counter manipulations from * fork and exit) across the system. This presumes that cpuset * modifications are rare - better kept simple and safe, even if slow. * * The code that reads cpusets, such as in cpuset_common_file_read() * and below, only holds cpuset_sem across small pieces of code, such * as when reading out possibly multi-word cpumasks and nodemasks, as * the risks are less, and the desire for performance a little greater. * The proc_cpuset_show() routine needs to hold cpuset_sem to insure * that no cs->dentry is NULL, as it walks up the cpuset tree to root. * * The hooks from fork and exit, cpuset_fork() and cpuset_exit(), don't * (usually) grab cpuset_sem. These are the two most performance * critical pieces of code here. The exception occurs on exit(), * when a task in a notify_on_release cpuset exits. Then cpuset_sem * is taken, and if the cpuset count is zero, a usermode call made * to /sbin/cpuset_release_agent with the name of the cpuset (path * relative to the root of cpuset file system) as the argument. * * A cpuset can only be deleted if both its 'count' of using tasks is * zero, and its list of 'children' cpusets is empty. Since all tasks * in the system use _some_ cpuset, and since there is always at least * one task in the system (init, pid == 1), therefore, top_cpuset * always has either children cpusets and/or using tasks. So no need * for any special hack to ensure that top_cpuset cannot be deleted. */ static DECLARE_MUTEX(cpuset_sem); /* * A couple of forward declarations required, due to cyclic reference loop: * cpuset_mkdir -> cpuset_create -> cpuset_populate_dir -> cpuset_add_file * -> cpuset_create_file -> cpuset_dir_inode_operations -> cpuset_mkdir. */ static int cpuset_mkdir(struct inode *dir, struct dentry *dentry, int mode); static int cpuset_rmdir(struct inode *unused_dir, struct dentry *dentry); static struct backing_dev_info cpuset_backing_dev_info = { .ra_pages = 0, /* No readahead */ .capabilities = BDI_CAP_NO_ACCT_DIRTY | BDI_CAP_NO_WRITEBACK, }; static struct inode *cpuset_new_inode(mode_t mode) { struct inode *inode = new_inode(cpuset_sb); if (inode) { inode->i_mode = mode; inode->i_uid = current->fsuid; inode->i_gid = current->fsgid; inode->i_blksize = PAGE_CACHE_SIZE; inode->i_blocks = 0; inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; inode->i_mapping->backing_dev_info = &cpuset_backing_dev_info; } return inode; } static void cpuset_diput(struct dentry *dentry, struct inode *inode) { /* is dentry a directory ? if so, kfree() associated cpuset */ if (S_ISDIR(inode->i_mode)) { struct cpuset *cs = dentry->d_fsdata; BUG_ON(!(is_removed(cs))); kfree(cs); } iput(inode); } static struct dentry_operations cpuset_dops = { .d_iput = cpuset_diput, }; static struct dentry *cpuset_get_dentry(struct dentry *parent, const char *name) { struct dentry *d = lookup_one_len(name, parent, strlen(name)); if (!IS_ERR(d)) d->d_op = &cpuset_dops; return d; } static void remove_dir(struct dentry *d) { struct dentry *parent = dget(d->d_parent); d_delete(d); simple_rmdir(parent->d_inode, d); dput(parent); } /* * NOTE : the dentry must have been dget()'ed */ static void cpuset_d_remove_dir(struct dentry *dentry) { struct list_head *node; spin_lock(&dcache_lock); node = dentry->d_subdirs.next; while (node != &dentry->d_subdirs) { struct dentry *d = list_entry(node, struct dentry, d_child); list_del_init(node); if (d->d_inode) { d = dget_locked(d); spin_unlock(&dcache_lock); d_delete(d); simple_unlink(dentry->d_inode, d); dput(d); spin_lock(&dcache_lock); } node = dentry->d_subdirs.next; } list_del_init(&dentry->d_child); spin_unlock(&dcache_lock); remove_dir(dentry); } static struct super_operations cpuset_ops = { .statfs = simple_statfs, .drop_inode = generic_delete_inode, }; static int cpuset_fill_super(struct super_block *sb, void *unused_data, int unused_silent) { struct inode *inode; struct dentry *root; sb->s_blocksize = PAGE_CACHE_SIZE; sb->s_blocksize_bits = PAGE_CACHE_SHIFT; sb->s_magic = CPUSET_SUPER_MAGIC; sb->s_op = &cpuset_ops; cpuset_sb = sb; inode = cpuset_new_inode(S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR); if (inode) { inode->i_op = &simple_dir_inode_operations; inode->i_fop = &simple_dir_operations; /* directories start off with i_nlink == 2 (for "." entry) */ inode->i_nlink++; } else { return -ENOMEM; } root = d_alloc_root(inode); if (!root) { iput(inode); return -ENOMEM; } sb->s_root = root; return 0; } static struct super_block *cpuset_get_sb(struct file_system_type *fs_type, int flags, const char *unused_dev_name, void *data) { return get_sb_single(fs_type, flags, data, cpuset_fill_super); } static struct file_system_type cpuset_fs_type = { .name = "cpuset", .get_sb = cpuset_get_sb, .kill_sb = kill_litter_super, }; /* struct cftype: * * The files in the cpuset filesystem mostly have a very simple read/write * handling, some common function will take care of it. Nevertheless some cases * (read tasks) are special and therefore I define this structure for every * kind of file. * * * When reading/writing to a file: * - the cpuset to use in file->f_dentry->d_parent->d_fsdata * - the 'cftype' of the file is file->f_dentry->d_fsdata */ struct cftype { char *name; int private; int (*open) (struct inode *inode, struct file *file); ssize_t (*read) (struct file *file, char __user *buf, size_t nbytes, loff_t *ppos); int (*write) (struct file *file, const char __user *buf, size_t nbytes, loff_t *ppos); int (*release) (struct inode *inode, struct file *file); }; static inline struct cpuset *__d_cs(struct dentry *dentry) { return dentry->d_fsdata; } static inline struct cftype *__d_cft(struct dentry *dentry) { return dentry->d_fsdata; } /* * Call with cpuset_sem held. Writes path of cpuset into buf. * Returns 0 on success, -errno on error. */ static int cpuset_path(const struct cpuset *cs, char *buf, int buflen) { char *start; start = buf + buflen; *--start = '\0'; for (;;) { int len = cs->dentry->d_name.len; if ((start -= len) < buf) return -ENAMETOOLONG; memcpy(start, cs->dentry->d_name.name, len); cs = cs->parent; if (!cs) break; if (!cs->parent) continue; if (--start < buf) return -ENAMETOOLONG; *start = '/'; } memmove(buf, start, buf + buflen - start); return 0; } /* * Notify userspace when a cpuset is released, by running * /sbin/cpuset_release_agent with the name of the cpuset (path * relative to the root of cpuset file system) as the argument. * * Most likely, this user command will try to rmdir this cpuset. * * This races with the possibility that some other task will be * attached to this cpuset before it is removed, or that some other * user task will 'mkdir' a child cpuset of this cpuset. That's ok. * The presumed 'rmdir' will fail quietly if this cpuset is no longer * unused, and this cpuset will be reprieved from its death sentence, * to continue to serve a useful existence. Next time it's released, * we will get notified again, if it still has 'notify_on_release' set. * * The final arg to call_usermodehelper() is 0, which means don't * wait. The separate /sbin/cpuset_release_agent task is forked by * call_usermodehelper(), then control in this thread returns here, * without waiting for the release agent task. We don't bother to * wait because the caller of this routine has no use for the exit * status of the /sbin/cpuset_release_agent task, so no sense holding * our caller up for that. * * The simple act of forking that task might require more memory, * which might need cpuset_sem. So this routine must be called while * cpuset_sem is not held, to avoid a possible deadlock. See also * comments for check_for_release(), below. */ static void cpuset_release_agent(const char *pathbuf) { char *argv[3], *envp[3]; int i; if (!pathbuf) return; i = 0; argv[i++] = "/sbin/cpuset_release_agent"; argv[i++] = (char *)pathbuf; argv[i] = NULL; i = 0; /* minimal command environment */ envp[i++] = "HOME=/"; envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; envp[i] = NULL; call_usermodehelper(argv[0], argv, envp, 0); kfree(pathbuf); } /* * Either cs->count of using tasks transitioned to zero, or the * cs->children list of child cpusets just became empty. If this * cs is notify_on_release() and now both the user count is zero and * the list of children is empty, prepare cpuset path in a kmalloc'd * buffer, to be returned via ppathbuf, so that the caller can invoke * cpuset_release_agent() with it later on, once cpuset_sem is dropped. * Call here with cpuset_sem held. * * This check_for_release() routine is responsible for kmalloc'ing * pathbuf. The above cpuset_release_agent() is responsible for * kfree'ing pathbuf. The caller of these routines is responsible * for providing a pathbuf pointer, initialized to NULL, then * calling check_for_release() with cpuset_sem held and the address * of the pathbuf pointer, then dropping cpuset_sem, then calling * cpuset_release_agent() with pathbuf, as set by check_for_release(). */ static void check_for_release(struct cpuset *cs, char **ppathbuf) { if (notify_on_release(cs) && atomic_read(&cs->count) == 0 && list_empty(&cs->children)) { char *buf; buf = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!buf) return; if (cpuset_path(cs, buf, PAGE_SIZE) < 0) kfree(buf); else *ppathbuf = buf; } } /* * Return in *pmask the portion of a cpusets's cpus_allowed that * are online. If none are online, walk up the cpuset hierarchy * until we find one that does have some online cpus. If we get * all the way to the top and still haven't found any online cpus, * return cpu_online_map. Or if passed a NULL cs from an exit'ing * task, return cpu_online_map. * * One way or another, we guarantee to return some non-empty subset * of cpu_online_map. * * Call with cpuset_sem held. */ static void guarantee_online_cpus(const struct cpuset *cs, cpumask_t *pmask) { while (cs && !cpus_intersects(cs->cpus_allowed, cpu_online_map)) cs = cs->parent; if (cs) cpus_and(*pmask, cs->cpus_allowed, cpu_online_map); else *pmask = cpu_online_map; BUG_ON(!cpus_intersects(*pmask, cpu_online_map)); } /* * Return in *pmask the portion of a cpusets's mems_allowed that * are online. If none are online, walk up the cpuset hierarchy * until we find one that does have some online mems. If we get