blob: d238cf7d373a587b484fdcd77cb7b397331b7760 [file] [log] [blame] [edit]
/*****************************************************************************\
* xcgroup.c - cgroup related primitives
*****************************************************************************
* Copyright (C) 2009 CEA/DAM/DIF
* Written by Matthieu Hautreux <matthieu.hautreux@cea.fr>
*
* This file is part of SLURM, a resource management program.
* For details, see <https://computing.llnl.gov/linux/slurm/>.
* Please also read the included file: DISCLAIMER.
*
* SLURM is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* In addition, as a special exception, the copyright holders give permission
* to link the code of portions of this program with the OpenSSL library under
* certain conditions as described in each individual source file, and
* distribute linked combinations including the two. You must obey the GNU
* General Public License in all respects for all of the code used other than
* OpenSSL. If you modify file(s) with this exception, you may extend this
* exception to your version of the file(s), but you are not obligated to do
* so. If you do not wish to do so, delete this exception statement from your
* version. If you delete this exception statement from all source files in
* the program, then also delete it here.
*
* SLURM is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along
* with SLURM; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\*****************************************************************************/
#if HAVE_CONFIG_H
# include "config.h"
#endif
#if HAVE_STDINT_H
# include <stdint.h>
#endif
#if HAVE_INTTYPES_H
# include <inttypes.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <slurm/slurm.h>
#include <slurm/slurm_errno.h>
#include "src/common/log.h"
#include "src/common/xmalloc.h"
#include "src/common/xstring.h"
#include "src/slurmd/slurmstepd/slurmstepd_job.h"
#include "xcgroup.h"
#ifndef PATH_MAX
#define PATH_MAX 256
#endif
/* internal functions */
size_t _file_getsize(int fd);
int _file_read_uint32s(char* file_path,uint32_t** pvalues,int* pnb);
int _file_write_uint32s(char* file_path,uint32_t* values,int nb);
int _file_read_uint64s(char* file_path,uint64_t** pvalues,int* pnb);
int _file_write_uint64s(char* file_path,uint64_t* values,int nb);
int _file_read_content(char* file_path,char** content,size_t *csize);
int _file_write_content(char* file_path,char* content,size_t csize);
int _xcgroup_cpuset_init(char* file_path);
/* xcgroup primitives */
int xcgroup_is_available(void)
{
char* value;
size_t s;
if ( xcgroup_get_param(CGROUP_BASEDIR,"release_agent",
&value,&s) != XCGROUP_SUCCESS )
return 0;
else {
xfree(value);
return 1;
}
}
int xcgroup_set_release_agent(char* agent)
{
int fstatus;
char* rag;
char* value;
size_t s;
if ( agent == NULL )
return XCGROUP_ERROR;
rag = (char*) xstrdup("release_agent=");
fstatus = xcgroup_get_param(CGROUP_BASEDIR,"release_agent",
&value,&s);
if ( fstatus == XCGROUP_SUCCESS ) {
if ( strcmp(value,agent) != 0 ) {
xstrcat(rag,agent);
fstatus = xcgroup_set_params(CGROUP_BASEDIR,rag);
}
xfree(value);
}
xfree(rag);
return fstatus;
}
int xcgroup_mount(char* mount_opts)
{
char* mount_cmd_fmt;
char mount_cmd[1024];
mode_t cmask;
mode_t omask;
cmask = S_IWGRP | S_IWOTH;
omask = umask(cmask);
if ( mkdir(CGROUP_BASEDIR,0755) && errno != EEXIST) {
debug("unable to create cgroup directory '%s'"
" : %m",CGROUP_BASEDIR);
umask(omask);
return XCGROUP_ERROR;
}
umask(omask);
if ( mount_opts == NULL ||
strlen(mount_opts) == 0 ) {
mount_cmd_fmt="/bin/mount -t cgroup none " CGROUP_BASEDIR;
}
else
mount_cmd_fmt="/bin/mount -o %s -t cgroup none " CGROUP_BASEDIR;
if ( snprintf(mount_cmd,1024,mount_cmd_fmt,
mount_opts) >= 1024 ) {
debug2("unable to build mount cmd line");
return XCGROUP_ERROR;
}
else
debug3("cgroup mount cmd line is '%s'",mount_cmd);
if ( system(mount_cmd) )
return XCGROUP_ERROR;
else
return XCGROUP_SUCCESS;
}
int xcgroup_create(char* file_path,xcgroup_opts_t* opts)
{
int fstatus;
uid_t uid;
gid_t gid;
int create_only;
int notify;
mode_t cmask;
mode_t omask;
uid=opts->uid;
gid=opts->gid;
create_only=opts->create_only;
notify=opts->notify;
fstatus = XCGROUP_ERROR;
/* save current mask and apply working one */
cmask = S_IWGRP | S_IWOTH;
omask = umask(cmask);
/* build cgroup */
if ( mkdir(file_path,0755) ) {
if ( create_only || errno != EEXIST ) {
debug2("unable to create cgroup '%s' : %m",
file_path);
umask(omask);
return fstatus;
}
}
umask(omask);
/* initialize cpuset support (if enabled in cgroup ) */
if ( _xcgroup_cpuset_init(file_path) != XCGROUP_SUCCESS ) {
debug2("unable to initialize cpuset cgroup component");
rmdir(file_path);
return fstatus;
}
/* change cgroup ownership as requested */
if ( chown(file_path,uid,gid) ) {
debug2("unable to chown %d:%d cgroup '%s' : %m",
uid,gid,file_path);
return fstatus;
}
/* following operations failure might not result in a general
* failure so set output status to success */
fstatus = XCGROUP_SUCCESS;
/* set notify on release flag */
if ( notify == 1 )
xcgroup_set_params(file_path,"notify_on_release=1");
else if ( notify == 0 )
xcgroup_set_params(file_path,"notify_on_release=0");
return fstatus;
}
int xcgroup_destroy(char* file_path)
{
/*
* nothing to be done here, notify_on_release was set
* so hope that all will works perfectly...
*
* with memory cgroup some pages can still be accounted
* to the cgroup but no more processes are present, this results
* in a directory not being removed until the pages are accounted
* to an other cgroup...
* echoing 1 into memory.force_empty can purge this memory but
* as slurmstepd is still present in the cgroup and use pages,
* this is not sufficient as it could leave some other pages too..
* we should have a way to ask the cgroup to force_empty
* on last process exit but I did not find any for now
*/
//xcgroup_set_params(file_path,"memory.force_empty=1");
return XCGROUP_SUCCESS;
}
int xcgroup_add_pids(char* cpath,pid_t* pids,int npids)
{
int fstatus;
char file_path[PATH_MAX];
fstatus = XCGROUP_ERROR;
if ( snprintf(file_path,PATH_MAX,"%s/tasks",
cpath) >= PATH_MAX ) {
debug2("unable to add pids to '%s' : %m",cpath);
return fstatus;
}
fstatus = _file_write_uint32s(file_path,(uint32_t*)pids,npids);
if ( fstatus != XCGROUP_SUCCESS )
debug2("unable to add pids to '%s'",cpath);
return fstatus;
}
int
xcgroup_get_pids(char* cpath, pid_t **pids, int *npids)
{
int fstatus;
char file_path[PATH_MAX];
fstatus = XCGROUP_ERROR;
if ( pids == NULL || npids == NULL )
return SLURM_ERROR;
if ( snprintf(file_path,PATH_MAX,"%s/tasks",
cpath) >= PATH_MAX ) {
debug2("unable to get pids of '%s' : %m",cpath);
return fstatus;
}
fstatus = _file_read_uint32s(file_path,(uint32_t**)pids,npids);
if ( fstatus != XCGROUP_SUCCESS )
debug2("unable to get pids of '%s'",cpath);
return fstatus;
}
int
xcgroup_find_by_pid(char* cpath, pid_t pid)
{
int fstatus = SLURM_ERROR;
char file_path[PATH_MAX];
char* buf;
size_t fsize;
char* p;
char* e;
char* entry;
/* build pid cgroup meta filepath */
if ( snprintf(file_path,PATH_MAX,"/proc/%u/cgroup",
pid) >= PATH_MAX ) {
debug2("unable to build cgroup meta filepath for pid=%u : %m",
pid);
return XCGROUP_ERROR;
}
/* read file content */
fstatus = _file_read_content(file_path,&buf,&fsize);
if ( fstatus == XCGROUP_SUCCESS ) {
fstatus = XCGROUP_ERROR;
p = buf;
if ( index(p,'\n') != NULL ) {
e = index(p,'\n');
*e='\0';
entry = rindex(p,':');
if ( entry != NULL ) {
entry++;
snprintf(cpath,PATH_MAX,"%s%s",
CGROUP_BASEDIR,entry);
fstatus = XCGROUP_SUCCESS;
}
}
xfree(buf);
}
return fstatus;
}
int xcgroup_set_memlimit(char* cpath,uint32_t memlimit)
{
int fstatus;
char file_path[PATH_MAX];
uint64_t ml;
fstatus = XCGROUP_ERROR;
if ( snprintf(file_path,PATH_MAX,"%s/memory.limit_in_bytes",
cpath) >= PATH_MAX ) {
debug2("unable to set memory limit of '%s' : %m",cpath);
return fstatus;
}
ml = (uint64_t) memlimit * 1024 * 1024;
fstatus = _file_write_uint64s(file_path,&ml,1);
if ( fstatus != XCGROUP_SUCCESS )
debug2("unable to set memory limit of '%s' : %m",cpath);
else
debug3("memory limit set to %uMB for '%s'",memlimit,cpath);
return fstatus;
}
int xcgroup_get_memlimit(char* cpath,uint32_t* memlimit)
{
int fstatus;
char file_path[PATH_MAX];
uint64_t* ml;
int i;
fstatus = XCGROUP_ERROR;
if ( snprintf(file_path,PATH_MAX,"%s/memory.limit_in_bytes",
cpath) >= PATH_MAX ) {
debug2("unable to get memory limit of '%s' : %m",cpath);
return fstatus;
}
fstatus = _file_read_uint64s(file_path,&ml,&i);
if ( fstatus != XCGROUP_SUCCESS ||
i == 0 )
debug2("unable to get memory limit of '%s' : %m",cpath);
else {
if ( *ml == 0 ) {
*memlimit = 0;
}
else {
/* convert into MB */
*ml /= 1024 * 1024;
/* memlimit is stored into a uint32_t */
/* so cap the memlimit value to the max value */
/* of an uint32_t */
*memlimit = -1 ;
if ( *ml < *memlimit ) {
*memlimit = *ml;
}
}
debug3("memory limit of '%s' is %uMB",cpath,*memlimit);
xfree(ml);
}
return fstatus;
}
int xcgroup_set_memswlimit(char* cpath,uint32_t memlimit)
{
int fstatus;
char file_path[PATH_MAX];
uint64_t ml;
fstatus = XCGROUP_ERROR;
if ( snprintf(file_path,PATH_MAX,"%s/memory.memsw.limit_in_bytes",
cpath) >= PATH_MAX ) {
debug2("unable to set memsw limit of '%s' : %m",cpath);
return fstatus;
}
ml = (uint64_t) memlimit * 1024 * 1024;
fstatus = _file_write_uint64s(file_path,&ml,1);
if ( fstatus != XCGROUP_SUCCESS )
debug2("unable to set memsw limit of '%s' : %m",cpath);
else
debug3("mem+swap limit set to %uMB for '%s'",memlimit,cpath);
return fstatus;
}
int xcgroup_get_memswlimit(char* cpath,uint32_t* memlimit)
{
int fstatus;
char file_path[PATH_MAX];
uint64_t *ml;
int i;
fstatus = XCGROUP_ERROR;
if ( snprintf(file_path,PATH_MAX,"%s/memory.memsw.limit_in_bytes",
cpath) >= PATH_MAX ) {
debug2("unable to get memsw limit of '%s' : %m",cpath);
return fstatus;
}
fstatus = _file_read_uint64s(file_path,&ml,&i);
if ( fstatus != XCGROUP_SUCCESS ||
i ==0 )
debug2("unable to get memsw limit of '%s' : %m",cpath);
else {
if ( *ml == 0 ) {
*memlimit = 0;
}
else {
/* convert into MB */
*ml /= 1024 * 1024;
/* memlimit is stored into a uint32_t */
/* so cap the memlimit value to the max value */
/* of an uint32_t */
*memlimit = -1 ;
if ( *ml < *memlimit ) {
*memlimit = *ml;
}
}
debug3("mem+swap limit of '%s' is %uMB",cpath,*memlimit);
xfree(ml);
}
return fstatus;
}
int xcgroup_set_mem_use_hierarchy(char* cpath,int flag)
{
if ( flag )
return xcgroup_set_params(cpath,"memory.use_hierarchy=1");
else
return xcgroup_set_params(cpath,"memory.use_hierarchy=0");
}
int xcgroup_set_cpuset_cpus(char* cpath,char* range)
{
int fstatus;
char file_path[PATH_MAX];
fstatus = XCGROUP_ERROR;
if ( snprintf(file_path,PATH_MAX,"%s/cpuset.cpus",
cpath) >= PATH_MAX ) {
debug2("unable to set cpuset.cpus to '%s' for '%s' : %m",
range,cpath);
return fstatus;
}
fstatus = _file_write_content(file_path,range,strlen(range));
if ( fstatus != XCGROUP_SUCCESS )
debug2("unable to set cpuset.cpus to '%s' for '%s' : %m",
range,cpath);
else
debug3("cpuset.cpus set to '%s' for '%s'",range,cpath);
return fstatus;
}
int xcgroup_set_params(char* cpath,char* parameters)
{
int fstatus;
char file_path[PATH_MAX];
char* params;
char* value;
char* p;
char* next;
fstatus = XCGROUP_ERROR;
params = (char*) xstrdup(parameters);
p = params;
while ( p != NULL && *p != '\0' ) {
next = index(p,' ');
if ( next ) {
*next='\0';
next++;
while ( *next == ' ' )
next++;
}
value = index(p,'=');
if ( value != NULL ) {
*value='\0';
value++;
if ( snprintf(file_path,PATH_MAX,"%s/%s",cpath,p)
>= PATH_MAX ) {
debug2("unable to build filepath for '%s' and"
" parameter '%s' : %m",cpath,p);
goto next_loop;
}
fstatus = _file_write_content(file_path,value,
strlen(value));
if ( fstatus != XCGROUP_SUCCESS )
debug2("unable to set parameter '%s' to "
"'%s' for '%s'",p,value,cpath);
else
debug3("parameter '%s' set to '%s' for '%s'",
p,value,cpath);
}
else
debug2("bad parameters format for entry '%s'",p);
next_loop:
p = next;
}
xfree(params);
return fstatus;
}
int xcgroup_get_param(char* cpath,char* parameter,char **content,size_t *csize)
{
int fstatus;
char file_path[PATH_MAX];
fstatus = XCGROUP_ERROR;
if ( snprintf(file_path,PATH_MAX,"%s/%s",cpath,parameter)
>= PATH_MAX ) {
debug2("unable to build filepath for '%s' and"
" parameter '%s' : %m",cpath,parameter);
}
else {
fstatus = _file_read_content(file_path,content,csize);
if ( fstatus != XCGROUP_SUCCESS )
debug2("unable to get parameter '%s'", parameter);
}
return fstatus;
}
size_t _file_getsize(int fd)
{
int rc;
size_t fsize;
off_t offset;
char c;
/* store current position and rewind */
offset = lseek(fd,0,SEEK_CUR);
if ( offset < 0 )
return -1;
lseek(fd,0,SEEK_SET);
/* get file size */
fsize=0;
do {
rc = read(fd,(void*)&c,1);
if ( rc > 0 )
fsize++;
}
while ( (rc < 0 && errno == EINTR) || rc > 0 );
/* restore position */
lseek(fd,offset,SEEK_SET);
if ( rc < 0 )
return -1;
else
return fsize;
}
int
_file_write_uint64s(char* file_path,uint64_t* values,int nb)
{
int fstatus;
int rc;
int fd;
char tstr[256];
uint64_t value;
int i;
/* open file for writing */
fd = open(file_path, O_WRONLY, 0700);
if (fd < 0) {
debug2("unable to open '%s' for writing : %m",
file_path);
return XCGROUP_ERROR;
}
/* add one value per line */
fstatus = XCGROUP_SUCCESS;
for ( i=0 ; i < nb ; i++ ) {
value = values[i];
rc = snprintf(tstr, sizeof(tstr), "%"PRIu64"", value);
if ( rc < 0 ) {
debug2("unable to build %"PRIu64" string value, "
"skipping", value);
fstatus = XCGROUP_ERROR;
continue;
}
do {
rc = write(fd, tstr, strlen(tstr)+1);
}
while ( rc != 0 && errno == EINTR);
if (rc < 1) {
debug2("unable to add value '%s' to file '%s' : %m",
tstr,file_path);
fstatus = XCGROUP_ERROR;
}
}
/* close file */
close(fd);
return fstatus;
}
int
_file_read_uint64s(char* file_path,uint64_t** pvalues,int* pnb)
{
int rc;
int fd;
size_t fsize;
char* buf;
char* p;
uint64_t* pa=NULL;
int i;
/* check input pointers */
if ( pvalues == NULL || pnb == NULL )
return XCGROUP_ERROR;
/* open file for reading */
fd = open(file_path, O_RDONLY, 0700);
if (fd < 0) {
debug2("unable to open '%s' for reading : %m",
file_path);
return XCGROUP_ERROR;
}
/* get file size */
fsize=_file_getsize(fd);
if ( fsize == -1 ) {
close(fd);
return XCGROUP_ERROR;
}
/* read file contents */
buf = (char*) xmalloc((fsize+1)*sizeof(char));
do {
rc = read(fd,buf,fsize);
}
while ( rc < 0 && errno == EINTR );
close(fd);
buf[fsize]='\0';
/* count values (splitted by \n) */
i=0;
if ( rc > 0 ) {
p = buf;
while ( index(p,'\n') != NULL ) {
i++;
p = index(p,'\n') + 1;
}
}
/* build uint32_t list */
if ( i > 0 ) {
pa = (uint64_t*) xmalloc(sizeof(uint64_t) * i);
p = buf;
i = 0;
while ( index(p,'\n') != NULL ) {
long long unsigned int ll_tmp;
sscanf(p,"%llu", &ll_tmp);
pa[i++] = ll_tmp;
p = index(p,'\n') + 1;
}
}
/* free buffer */
xfree(buf);
/* set output values */
*pvalues = pa;
*pnb = i;
return XCGROUP_SUCCESS;
}
int
_file_write_uint32s(char* file_path,uint32_t* values,int nb)
{
int fstatus;
int rc;
int fd;
char tstr[256];
uint32_t value;
int i;
/* open file for writing */
fd = open(file_path, O_WRONLY, 0700);
if (fd < 0) {
debug2("unable to open '%s' for writing : %m",
file_path);
return XCGROUP_ERROR;
}
/* add one value per line */
fstatus = XCGROUP_SUCCESS;
for ( i=0 ; i < nb ; i++ ) {
value = values[i];
rc = snprintf(tstr, sizeof(tstr), "%u",value);
if ( rc < 0 ) {
debug2("unable to build %u string value, skipping",
value);
fstatus = XCGROUP_ERROR;
continue;
}
do {
rc = write(fd, tstr, strlen(tstr)+1);
}
while ( rc != 0 && errno == EINTR);
if (rc < 1) {
debug2("unable to add value '%s' to file '%s' : %m",
tstr,file_path);
fstatus = XCGROUP_ERROR;
}
}
/* close file */
close(fd);
return fstatus;
}
int
_file_read_uint32s(char* file_path,uint32_t** pvalues,int* pnb)
{
int rc;
int fd;
size_t fsize;
char* buf;
char* p;
uint32_t* pa=NULL;
int i;
/* check input pointers */
if ( pvalues == NULL || pnb == NULL )
return XCGROUP_ERROR;
/* open file for reading */
fd = open(file_path, O_RDONLY, 0700);
if (fd < 0) {
debug2("unable to open '%s' for reading : %m",
file_path);
return XCGROUP_ERROR;
}
/* get file size */
fsize=_file_getsize(fd);
if ( fsize == -1 ) {
close(fd);
return XCGROUP_ERROR;
}
/* read file contents */
buf = (char*) xmalloc((fsize+1)*sizeof(char));
do {
rc = read(fd,buf,fsize);
}
while ( rc < 0 && errno == EINTR );
close(fd);
buf[fsize]='\0';
/* count values (splitted by \n) */
i=0;
if ( rc > 0 ) {
p = buf;
while ( index(p,'\n') != NULL ) {
i++;
p = index(p,'\n') + 1;
}
}
/* build uint32_t list */
if ( i > 0 ) {
pa = (uint32_t*) xmalloc(sizeof(uint32_t) * i);
p = buf;
i = 0;
while ( index(p,'\n') != NULL ) {
sscanf(p,"%u",pa+i);
p = index(p,'\n') + 1;
i++;
}
}
/* free buffer */
xfree(buf);
/* set output values */
*pvalues = pa;
*pnb = i;
return XCGROUP_SUCCESS;
}
int
_file_write_content(char* file_path, char* content,size_t csize)
{
int fstatus;
int rc;
int fd;
/* open file for writing */
fd = open(file_path, O_WRONLY, 0700);
if (fd < 0) {
debug2("unable to open '%s' for writing : %m",
file_path);
return XCGROUP_ERROR;
}
/* write content */
do {
rc = write(fd,content,csize);
}
while ( rc != 0 && errno == EINTR);
/* check read size */
if (rc < csize) {
debug2("unable to write %zd bytes to file '%s' : %m",
csize, file_path);
fstatus = XCGROUP_ERROR;
}
else
fstatus = XCGROUP_SUCCESS;
/* close file */
close(fd);
return fstatus;
}
int
_file_read_content(char* file_path,char** content,size_t *csize)
{
int fstatus;
int rc;
int fd;
size_t fsize;
char* buf;
fstatus = XCGROUP_ERROR;
/* check input pointers */
if ( content == NULL || csize == NULL )
return fstatus;
/* open file for reading */
fd = open(file_path, O_RDONLY, 0700);
if (fd < 0) {
debug2("unable to open '%s' for reading : %m",
file_path);
return fstatus;
}
/* get file size */
fsize=_file_getsize(fd);
if ( fsize == -1 ) {
close(fd);
return fstatus;
}
/* read file contents */
buf = (char*) xmalloc((fsize+1)*sizeof(char));
buf[fsize]='\0';
do {
rc = read(fd,buf,fsize);
}
while ( rc < 0 && errno == EINTR );
/* set output values */
if ( rc >= 0 ) {
*content = buf;
*csize = rc;
fstatus = XCGROUP_SUCCESS;
}
/* close file */
close(fd);
return fstatus;
}
int _xcgroup_cpuset_init(char* file_path)
{
int fstatus;
char path[PATH_MAX];
char* cpuset_metafiles[] = {
"cpuset.cpus",
"cpuset.mems"
};
char* cpuset_meta;
char* cpuset_conf;
size_t csize;
int i;
fstatus = XCGROUP_ERROR;
/* when cgroups are configured with cpuset, at least
* cpuset.cpus and cpuset.mems must be set or the cgroup
* will not be available at all.
* we duplicate the ancestor configuration in the init step */
for ( i = 0 ; i < 2 ; i++ ) {
cpuset_meta = cpuset_metafiles[i];
/* try to read ancestor configuration */
if ( snprintf(path,PATH_MAX,"%s/../%s",
file_path,cpuset_meta) >= PATH_MAX ) {
debug2("unable to get ancestor %s for cgroup '%s' : %m",
cpuset_meta,file_path);
return fstatus;
}
if ( _file_read_content(path,&cpuset_conf,&csize) !=
XCGROUP_SUCCESS ) {
debug3("assuming no cpuset support for '%s'",path);
return XCGROUP_SUCCESS;
}
/* duplicate ancestor conf in current cgroup */
if ( snprintf(path,PATH_MAX,"%s/%s",
file_path,cpuset_meta) >= PATH_MAX ) {
debug2("unable to set %s for cgroup '%s' : %m",
cpuset_meta,file_path);
return fstatus;
}
if ( _file_write_content(path,cpuset_conf,csize) !=
XCGROUP_SUCCESS ) {
debug2("unable to write %s configuration (%s) of '%s'",
cpuset_meta,cpuset_conf,file_path);
return fstatus;
}
}
return XCGROUP_SUCCESS;
}