📄 cpuset.c
字号:
break; case FILE_CPU_EXCLUSIVE: retval = update_flag(CS_CPU_EXCLUSIVE, cs, buffer); break; case FILE_MEM_EXCLUSIVE: retval = update_flag(CS_MEM_EXCLUSIVE, cs, buffer); break; case FILE_NOTIFY_ON_RELEASE: retval = update_flag(CS_NOTIFY_ON_RELEASE, cs, buffer); break; case FILE_MEMORY_MIGRATE: retval = update_flag(CS_MEMORY_MIGRATE, cs, buffer); break; case FILE_MEMORY_PRESSURE_ENABLED: retval = update_memory_pressure_enabled(cs, buffer); break; case FILE_MEMORY_PRESSURE: retval = -EACCES; break; case FILE_SPREAD_PAGE: retval = update_flag(CS_SPREAD_PAGE, cs, buffer); cs->mems_generation = cpuset_mems_generation++; break; case FILE_SPREAD_SLAB: retval = update_flag(CS_SPREAD_SLAB, cs, buffer); cs->mems_generation = cpuset_mems_generation++; break; case FILE_TASKLIST: retval = attach_task(cs, buffer, &pathbuf); break; default: retval = -EINVAL; goto out2; } if (retval == 0) retval = nbytes;out2: mutex_unlock(&manage_mutex); cpuset_release_agent(pathbuf);out1: kfree(buffer); return retval;}static ssize_t cpuset_file_write(struct file *file, const char __user *buf, size_t nbytes, loff_t *ppos){ ssize_t retval = 0; struct cftype *cft = __d_cft(file->f_path.dentry); if (!cft) return -ENODEV; /* special function ? */ if (cft->write) retval = cft->write(file, buf, nbytes, ppos); else retval = cpuset_common_file_write(file, buf, nbytes, ppos); return retval;}/* * These ascii lists should be read in a single call, by using a user * buffer large enough to hold the entire map. If read in smaller * chunks, there is no guarantee of atomicity. Since the display format * used, list of ranges of sequential numbers, is variable length, * and since these maps can change value dynamically, one could read * gibberish by doing partial reads while a list was changing. * A single large read to a buffer that crosses a page boundary is * ok, because the result being copied to user land is not recomputed * across a page fault. */static int cpuset_sprintf_cpulist(char *page, struct cpuset *cs){ cpumask_t mask; mutex_lock(&callback_mutex); mask = cs->cpus_allowed; mutex_unlock(&callback_mutex); return cpulist_scnprintf(page, PAGE_SIZE, mask);}static int cpuset_sprintf_memlist(char *page, struct cpuset *cs){ nodemask_t mask; mutex_lock(&callback_mutex); mask = cs->mems_allowed; mutex_unlock(&callback_mutex); return nodelist_scnprintf(page, PAGE_SIZE, mask);}static ssize_t cpuset_common_file_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos){ struct cftype *cft = __d_cft(file->f_path.dentry); struct cpuset *cs = __d_cs(file->f_path.dentry->d_parent); cpuset_filetype_t type = cft->private; char *page; ssize_t retval = 0; char *s; if (!(page = (char *)__get_free_page(GFP_KERNEL))) return -ENOMEM; s = page; switch (type) { case FILE_CPULIST: s += cpuset_sprintf_cpulist(s, cs); break; case FILE_MEMLIST: s += cpuset_sprintf_memlist(s, cs); break; case FILE_CPU_EXCLUSIVE: *s++ = is_cpu_exclusive(cs) ? '1' : '0'; break; case FILE_MEM_EXCLUSIVE: *s++ = is_mem_exclusive(cs) ? '1' : '0'; break; case FILE_NOTIFY_ON_RELEASE: *s++ = notify_on_release(cs) ? '1' : '0'; break; case FILE_MEMORY_MIGRATE: *s++ = is_memory_migrate(cs) ? '1' : '0'; break; case FILE_MEMORY_PRESSURE_ENABLED: *s++ = cpuset_memory_pressure_enabled ? '1' : '0'; break; case FILE_MEMORY_PRESSURE: s += sprintf(s, "%d", fmeter_getrate(&cs->fmeter)); break; case FILE_SPREAD_PAGE: *s++ = is_spread_page(cs) ? '1' : '0'; break; case FILE_SPREAD_SLAB: *s++ = is_spread_slab(cs) ? '1' : '0'; break; default: retval = -EINVAL; goto out; } *s++ = '\n'; retval = simple_read_from_buffer(buf, nbytes, ppos, page, s - page);out: free_page((unsigned long)page); return retval;}static ssize_t cpuset_file_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos){ ssize_t retval = 0; struct cftype *cft = __d_cft(file->f_path.dentry); if (!cft) return -ENODEV; /* special function ? */ if (cft->read) retval = cft->read(file, buf, nbytes, ppos); else retval = cpuset_common_file_read(file, buf, nbytes, ppos); return retval;}static int cpuset_file_open(struct inode *inode, struct file *file){ int err; struct cftype *cft; err = generic_file_open(inode, file); if (err) return err; cft = __d_cft(file->f_path.dentry); if (!cft) return -ENODEV; if (cft->open) err = cft->open(inode, file); else err = 0; return err;}static int cpuset_file_release(struct inode *inode, struct file *file){ struct cftype *cft = __d_cft(file->f_path.dentry); if (cft->release) return cft->release(inode, file); return 0;}/* * cpuset_rename - Only allow simple rename of directories in place. */static int cpuset_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry){ if (!S_ISDIR(old_dentry->d_inode->i_mode)) return -ENOTDIR; if (new_dentry->d_inode) return -EEXIST; if (old_dir != new_dir) return -EIO; return simple_rename(old_dir, old_dentry, new_dir, new_dentry);}static const struct file_operations cpuset_file_operations = { .read = cpuset_file_read, .write = cpuset_file_write, .llseek = generic_file_llseek, .open = cpuset_file_open, .release = cpuset_file_release,};static const struct inode_operations cpuset_dir_inode_operations = { .lookup = simple_lookup, .mkdir = cpuset_mkdir, .rmdir = cpuset_rmdir, .rename = cpuset_rename,};static int cpuset_create_file(struct dentry *dentry, int mode){ struct inode *inode; if (!dentry) return -ENOENT; if (dentry->d_inode) return -EEXIST; inode = cpuset_new_inode(mode); if (!inode) return -ENOMEM; if (S_ISDIR(mode)) { inode->i_op = &cpuset_dir_inode_operations; inode->i_fop = &simple_dir_operations; /* start off with i_nlink == 2 (for "." entry) */ inc_nlink(inode); } else if (S_ISREG(mode)) { inode->i_size = 0; inode->i_fop = &cpuset_file_operations; } d_instantiate(dentry, inode); dget(dentry); /* Extra count - pin the dentry in core */ return 0;}/* * cpuset_create_dir - create a directory for an object. * cs: the cpuset we create the directory for. * It must have a valid ->parent field * And we are going to fill its ->dentry field. * name: The name to give to the cpuset directory. Will be copied. * mode: mode to set on new directory. */static int cpuset_create_dir(struct cpuset *cs, const char *name, int mode){ struct dentry *dentry = NULL; struct dentry *parent; int error = 0; parent = cs->parent->dentry; dentry = cpuset_get_dentry(parent, name); if (IS_ERR(dentry)) return PTR_ERR(dentry); error = cpuset_create_file(dentry, S_IFDIR | mode); if (!error) { dentry->d_fsdata = cs; inc_nlink(parent->d_inode); cs->dentry = dentry; } dput(dentry); return error;}static int cpuset_add_file(struct dentry *dir, const struct cftype *cft){ struct dentry *dentry; int error; mutex_lock(&dir->d_inode->i_mutex); dentry = cpuset_get_dentry(dir, cft->name); if (!IS_ERR(dentry)) { error = cpuset_create_file(dentry, 0644 | S_IFREG); if (!error) dentry->d_fsdata = (void *)cft; dput(dentry); } else error = PTR_ERR(dentry); mutex_unlock(&dir->d_inode->i_mutex); return error;}/* * Stuff for reading the 'tasks' file. * * Reading this file can return large amounts of data if a cpuset has * *lots* of attached tasks. So it may need several calls to read(), * but we cannot guarantee that the information we produce is correct * unless we produce it entirely atomically. * * Upon tasks file open(), a struct ctr_struct is allocated, that * will have a pointer to an array (also allocated here). The struct * ctr_struct * is stored in file->private_data. Its resources will * be freed by release() when the file is closed. The array is used * to sprintf the PIDs and then used by read(). *//* cpusets_tasks_read array */struct ctr_struct { char *buf; int bufsz;};/* * Load into 'pidarray' up to 'npids' of the tasks using cpuset 'cs'. * Return actual number of pids loaded. No need to task_lock(p) * when reading out p->cpuset, as we don't really care if it changes * on the next cycle, and we are not going to try to dereference it. */static int pid_array_load(pid_t *pidarray, int npids, struct cpuset *cs){ int n = 0; struct task_struct *g, *p; read_lock(&tasklist_lock); do_each_thread(g, p) { if (p->cpuset == cs) { if (unlikely(n == npids)) goto array_full; pidarray[n++] = p->pid; } } while_each_thread(g, p);array_full: read_unlock(&tasklist_lock); return n;}static int cmppid(const void *a, const void *b){ return *(pid_t *)a - *(pid_t *)b;}/* * Convert array 'a' of 'npids' pid_t's to a string of newline separated * decimal pids in 'buf'. Don't write more than 'sz' chars, but return * count 'cnt' of how many chars would be written if buf were large enough. */static int pid_array_to_buf(char *buf, int sz, pid_t *a, int npids){ int cnt = 0; int i; for (i = 0; i < npids; i++) cnt += snprintf(buf + cnt, max(sz - cnt, 0), "%d\n", a[i]); return cnt;}/* * Handle an open on 'tasks' file. Prepare a buffer listing the * process id's of tasks currently attached to the cpuset being opened. * * Does not require any specific cpuset mutexes, and does not take any. */static int cpuset_tasks_open(struct inode *unused, struct file *file){ struct cpuset *cs = __d_cs(file->f_path.dentry->d_parent); struct ctr_struct *ctr; pid_t *pidarray; int npids; char c; if (!(file->f_mode & FMODE_READ)) return 0; ctr = kmalloc(sizeof(*ctr), GFP_KERNEL); if (!ctr) goto err0; /* * If cpuset gets more users after we read count, we won't have * enough space - tough. This race is indistinguishable to the * caller from the case that the additional cpuset users didn't * show up until sometime later on. */ npids = atomic_read(&cs->count); pidarray = kmalloc(npids * sizeof(pid_t), GFP_KERNEL); if (!pidarray) goto err1; npids = pid_array_load(pidarray, npids, cs); sort(pidarray, npids, sizeof(pid_t), cmppid, NULL); /* Call pid_array_to_buf() twice, first just to get bufsz */ ctr->bufsz = pid_array_to_buf(&c, sizeof(c), pidarray, npids) + 1; ctr->buf = kmalloc(ctr->bufsz, GFP_KERNEL); if (!ctr->buf) goto err2; ctr->bufsz = pid_array_to_buf(ctr->buf, ctr->bufsz, pidarray, npids); kfree(pidarray); file->private_data = ctr; return 0;err2: kfree(pidarray);err1: kfree(ctr);err0: return -ENOMEM;}static ssize_t cpuset_tasks_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos){ struct ctr_struct *ctr = file->private_data; return simple_read_from_buffer(buf, nbytes, ppos, ctr->buf, ctr->bufsz);}static int cpuset_tasks_release(struct inode *unused_inode, struct file *file){ struct ctr_struct *ctr; if (file->f_mode & FMODE_READ) { ctr = file->private_data; kfree(ctr->buf); kfree(ctr); } return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -