extern int jobacct_gather_cgroup_memory_attach_task(
	pid_t pid, jobacct_id_t *jobacct_id)
{
	xcgroup_t memory_cg;
	slurmd_job_t *job;
	uid_t uid;
	gid_t gid;
	uint32_t jobid;
	uint32_t stepid;
	uint32_t taskid;
	int fstatus = SLURM_SUCCESS;
	int rc;
	char* slurm_cgpath;

	job = jobacct_id->job;
	uid = job->uid;
	gid = job->gid;
	jobid = job->jobid;
	stepid = job->stepid;
	taskid = jobacct_id->taskid;

	/* create slurm root cg in this cg namespace */
	slurm_cgpath = jobacct_cgroup_create_slurm_cg(&memory_ns);
	if (!slurm_cgpath) {
		return SLURM_ERROR;
	}

	/* build user cgroup relative path if not set (should not be) */
	if (*user_cgroup_path == '\0') {
		if (snprintf(user_cgroup_path, PATH_MAX,
			     "%s/uid_%u", slurm_cgpath, uid) >= PATH_MAX) {
			error("unable to build uid %u cgroup relative "
			      "path : %m", uid);
			xfree(slurm_cgpath);
			return SLURM_ERROR;
		}
	}

	/* build job cgroup relative path if not set (may not be) */
	if (*job_cgroup_path == '\0') {
		if (snprintf(job_cgroup_path, PATH_MAX, "%s/job_%u",
			     user_cgroup_path, jobid) >= PATH_MAX) {
			error("jobacct_gather/cgroup: unable to build job %u "
			      "memory cg relative path : %m", jobid);
			return SLURM_ERROR;
		}
	}

	/* build job step cgroup relative path if not set (may not be) */
	if (*jobstep_cgroup_path == '\0') {
		if (snprintf(jobstep_cgroup_path, PATH_MAX, "%s/step_%u",
			     job_cgroup_path, stepid) >= PATH_MAX) {
			error("jobacct_gather/cgroup: unable to build job step "
			      "%u memory cg relative path : %m", stepid);
			return SLURM_ERROR;
		}
	}

	/* build task cgroup relative path */
	if (snprintf(task_cgroup_path, PATH_MAX, "%s/task_%u",
		     jobstep_cgroup_path, taskid) >= PATH_MAX) {
		error("jobacct_gather/cgroup: unable to build task %u "
		      "memory cg relative path : %m", taskid);
		return SLURM_ERROR;
	}

	fstatus = SLURM_SUCCESS;

	/*
	 * create memory root cg and lock it
	 *
	 * we will keep the lock until the end to avoid the effect of a release
	 * agent that would remove an existing cgroup hierarchy while we are
	 * setting it up. As soon as the step cgroup is created, we can release
	 * the lock.
	 * Indeed, consecutive slurm steps could result in cg being removed
	 * between the next EEXIST instanciation and the first addition of
	 * a task. The release_agent will have to lock the root memory cgroup
	 * to avoid this scenario.
	 */

	if (xcgroup_create(&memory_ns, &memory_cg, "", 0, 0)
	    != XCGROUP_SUCCESS) {
		error("jobacct_gather/cgroup: unable to create root memory "
		      "xcgroup");
		return SLURM_ERROR;
	}
	if (xcgroup_lock(&memory_cg) != XCGROUP_SUCCESS) {
		xcgroup_destroy(&memory_cg);
		error("jobacct_gather/cgroup: unable to lock root memory cg");
		return SLURM_ERROR;
	}

	/*
	 * Create user cgroup in the memory ns (it could already exist)
	 * Ask for hierarchical memory accounting starting from the user
	 * container in order to track the memory consumption up to the
	 * user.
	 */
	if (xcgroup_create(&memory_ns, &user_memory_cg,
			   user_cgroup_path,
			   uid, gid) != XCGROUP_SUCCESS) {
		error("jobacct_gather/cgroup: unable to create user %u memory "
		      "cgroup", uid);
		fstatus = SLURM_ERROR;
		goto error;
	}

	if (xcgroup_instanciate(&user_memory_cg) != XCGROUP_SUCCESS) {
		xcgroup_destroy(&user_memory_cg);
		error("jobacct_gather/cgroup: unable to instanciate user %u "
		      "memory cgroup", uid);
		fstatus = SLURM_ERROR;
		goto error;
	}

	/*
	 * Create job cgroup in the memory ns (it could already exist)
	 */
	if (xcgroup_create(&memory_ns, &job_memory_cg,
			   job_cgroup_path,
			   uid, gid) != XCGROUP_SUCCESS) {
		xcgroup_destroy(&user_memory_cg);
		error("jobacct_gather/cgroup: unable to create job %u memory "
		      "cgroup", jobid);
		fstatus = SLURM_ERROR;
		goto error;
	}

	if (xcgroup_instanciate(&job_memory_cg) != XCGROUP_SUCCESS) {
		xcgroup_destroy(&user_memory_cg);
		xcgroup_destroy(&job_memory_cg);
		error("jobacct_gather/cgroup: unable to instanciate job %u "
		      "memory cgroup", jobid);
		fstatus = SLURM_ERROR;
		goto error;
	}

	/*
	 * Create step cgroup in the memory ns (it could already exist)
	 */
	if (xcgroup_create(&memory_ns, &step_memory_cg,
			   jobstep_cgroup_path,
			   uid, gid) != XCGROUP_SUCCESS) {
		/* do not delete user/job cgroup as they can exist for other
		 * steps, but release cgroup structures */
		xcgroup_destroy(&user_memory_cg);
		xcgroup_destroy(&job_memory_cg);
		error("jobacct_gather/cgroup: unable to create jobstep %u.%u "
		      "memory cgroup", jobid, stepid);
		fstatus = SLURM_ERROR;
		goto error;
	}

	if (xcgroup_instanciate(&step_memory_cg) != XCGROUP_SUCCESS) {
		xcgroup_destroy(&user_memory_cg);
		xcgroup_destroy(&job_memory_cg);
		xcgroup_destroy(&step_memory_cg);
		error("jobacct_gather/cgroup: unable to instantiate jobstep "
		      "%u.%u memory cgroup", jobid, stepid);
		fstatus = SLURM_ERROR;
		goto error;
	}

	/*
	 * Create task cgroup in the memory ns
	 */
	if (xcgroup_create(&memory_ns, &task_memory_cg,
			   task_cgroup_path,
			   uid, gid) != XCGROUP_SUCCESS) {
		/* do not delete user/job cgroup as they can exist for other
		 * steps, but release cgroup structures */
		xcgroup_destroy(&user_memory_cg);
		xcgroup_destroy(&job_memory_cg);
		error("jobacct_gather/cgroup: unable to create jobstep %u.%u "
		      "task %u memory cgroup", jobid, stepid, taskid);
		fstatus = SLURM_ERROR;
		goto error;
	}

	if (xcgroup_instanciate(&task_memory_cg) != XCGROUP_SUCCESS) {
		xcgroup_destroy(&user_memory_cg);
		xcgroup_destroy(&job_memory_cg);
		xcgroup_destroy(&step_memory_cg);
		error("jobacct_gather/cgroup: unable to instantiate jobstep "
		      "%u.%u task %u memory cgroup", jobid, stepid, taskid);
		fstatus = SLURM_ERROR;
		goto error;
	}

	/*
	 * Attach the slurmstepd to the task memory cgroup
	 */
	rc = xcgroup_add_pids(&task_memory_cg, &pid, 1);
	if (rc != XCGROUP_SUCCESS) {
		error("jobacct_gather/cgroup: unable to add slurmstepd to "
		      "memory cg '%s'", task_memory_cg.path);
		fstatus = SLURM_ERROR;
	} else
		fstatus = SLURM_SUCCESS;

error:
	xcgroup_unlock(&memory_cg);
	xcgroup_destroy(&memory_cg);
	return fstatus;
}
extern int
jobacct_gather_cgroup_cpuacct_attach_task(pid_t pid, jobacct_id_t *jobacct_id)
{
	xcgroup_t cpuacct_cg;
	stepd_step_rec_t *job;
	uid_t uid;
	gid_t gid;
	uint32_t jobid;
	uint32_t stepid;
	uint32_t taskid;
	int fstatus = SLURM_SUCCESS;
	int rc;
	char* slurm_cgpath;

	job = jobacct_id->job;
	uid = job->uid;
	gid = job->gid;
	jobid = job->jobid;
	stepid = job->stepid;
	taskid = jobacct_id->taskid;

	if (taskid >= max_task_id)
		max_task_id = taskid;

	debug("%s: jobid %u stepid %u taskid %u max_task_id %u",
	      __func__, jobid, stepid, taskid, max_task_id);

	/* create slurm root cg in this cg namespace */
	slurm_cgpath = jobacct_cgroup_create_slurm_cg(&cpuacct_ns);
	if (!slurm_cgpath) {
		return SLURM_ERROR;
	}

	/* build user cgroup relative path if not set (may not be) */
	if (*user_cgroup_path == '\0') {
		if (snprintf(user_cgroup_path, PATH_MAX,
			     "%s/uid_%u", slurm_cgpath, uid) >= PATH_MAX) {
			error("jobacct_gather/cgroup: unable to build uid %u "
			      "cgroup relative path", uid);
			xfree(slurm_cgpath);
			return SLURM_ERROR;
		}
	}

	/* build job cgroup relative path if not set (may not be) */
	if (*job_cgroup_path == '\0') {
		if (snprintf(job_cgroup_path, PATH_MAX, "%s/job_%u",
			     user_cgroup_path, jobid) >= PATH_MAX) {
			error("jobacct_gather/cgroup: unable to build job %u "
			      "cpuacct cg relative path : %m", jobid);
			return SLURM_ERROR;
		}
	}

	/* build job step cgroup relative path if not set (may not be) */
	if (*jobstep_cgroup_path == '\0') {
		int len;
		if (stepid == SLURM_BATCH_SCRIPT) {
			len = snprintf(jobstep_cgroup_path, PATH_MAX,
				       "%s/step_batch", job_cgroup_path);
		} else if (stepid == SLURM_EXTERN_CONT) {
			len = snprintf(jobstep_cgroup_path, PATH_MAX,
				       "%s/step_extern", job_cgroup_path);
		} else {
			len = snprintf(jobstep_cgroup_path, PATH_MAX,
				       "%s/step_%u",
				       job_cgroup_path, stepid);
		}
		if (len >= PATH_MAX) {
			error("jobacct_gather/cgroup: unable to build job step "
			      " %u.%u cpuacct cg relative path: %m",
			      jobid, stepid);
			return SLURM_ERROR;
		}
	}

	/* build task cgroup relative path */
	if (snprintf(task_cgroup_path, PATH_MAX, "%s/task_%u",
		     jobstep_cgroup_path, taskid) >= PATH_MAX) {
		error("jobacct_gather/cgroup: unable to build task %u "
		      "cpuacct cg relative path : %m", taskid);
		return SLURM_ERROR;
	}

	/*
	 * create cpuacct root cg and lock it
	 *
	 * we will keep the lock until the end to avoid the effect of a release
	 * agent that would remove an existing cgroup hierarchy while we are
	 * setting it up. As soon as the step cgroup is created, we can release
	 * the lock.
	 * Indeed, consecutive slurm steps could result in cg being removed
	 * between the next EEXIST instanciation and the first addition of
	 * a task. The release_agent will have to lock the root cpuacct cgroup
	 * to avoid this scenario.
	 */

	if (xcgroup_create(&cpuacct_ns, &cpuacct_cg, "", 0, 0)
	    != XCGROUP_SUCCESS) {
		error("jobacct_gather/cgroup: unable to create root cpuacct "
		      "xcgroup");
		return SLURM_ERROR;
	}
	if (xcgroup_lock(&cpuacct_cg) != XCGROUP_SUCCESS) {
		xcgroup_destroy(&cpuacct_cg);
		error("jobacct_gather/cgroup: unable to lock root cpuacct cg");
		return SLURM_ERROR;
	}

	/*
	 * Create user cgroup in the cpuacct ns (it could already exist)
	 */
	if (xcgroup_create(&cpuacct_ns, &user_cpuacct_cg,
			   user_cgroup_path,
			   uid, gid) != XCGROUP_SUCCESS) {
		error("jobacct_gather/cgroup: unable to create user %u cpuacct "
		      "cgroup", uid);
		fstatus = SLURM_ERROR;
		goto error;
	}

	if (xcgroup_instanciate(&user_cpuacct_cg) != XCGROUP_SUCCESS) {
		xcgroup_destroy(&user_cpuacct_cg);
		error("jobacct_gather/cgroup: unable to instanciate user %u "
		      "cpuacct cgroup", uid);
		fstatus = SLURM_ERROR;
		goto error;
	}

	/*
	 * Create job cgroup in the cpuacct ns (it could already exist)
	 */
	if (xcgroup_create(&cpuacct_ns, &job_cpuacct_cg,
			   job_cgroup_path,
			   uid, gid) != XCGROUP_SUCCESS) {
		xcgroup_destroy(&user_cpuacct_cg);
		error("jobacct_gather/cgroup: unable to create job %u cpuacct "
		      "cgroup", jobid);
		fstatus = SLURM_ERROR;
		goto error;
	}

	if (xcgroup_instanciate(&job_cpuacct_cg) != XCGROUP_SUCCESS) {
		xcgroup_destroy(&user_cpuacct_cg);
		xcgroup_destroy(&job_cpuacct_cg);
		error("jobacct_gather/cgroup: unable to instanciate job %u "
		      "cpuacct cgroup", jobid);
		fstatus = SLURM_ERROR;
		goto error;
	}

	/*
	 * Create step cgroup in the cpuacct ns (it could already exist)
	 */
	if (xcgroup_create(&cpuacct_ns, &step_cpuacct_cg,
			   jobstep_cgroup_path,
			   uid, gid) != XCGROUP_SUCCESS) {
		/* do not delete user/job cgroup as they can exist for other
		 * steps, but release cgroup structures */
		xcgroup_destroy(&user_cpuacct_cg);
		xcgroup_destroy(&job_cpuacct_cg);
		error("jobacct_gather/cgroup: unable to create jobstep %u.%u "
		      "cpuacct cgroup", jobid, stepid);
		fstatus = SLURM_ERROR;
		goto error;
	}

	if (xcgroup_instanciate(&step_cpuacct_cg) != XCGROUP_SUCCESS) {
		xcgroup_destroy(&user_cpuacct_cg);
		xcgroup_destroy(&job_cpuacct_cg);
		xcgroup_destroy(&step_cpuacct_cg);
		error("jobacct_gather/cgroup: unable to instantiate jobstep "
		      "%u.%u cpuacct cgroup", jobid, stepid);
		fstatus = SLURM_ERROR;
		goto error;
	}

	/*
	 * Create task cgroup in the cpuacct ns
	 */
	if (xcgroup_create(&cpuacct_ns, &task_cpuacct_cg,
			   task_cgroup_path,
			   uid, gid) != XCGROUP_SUCCESS) {
		/* do not delete user/job cgroup as they can exist for other
		 * steps, but release cgroup structures */
		xcgroup_destroy(&user_cpuacct_cg);
		xcgroup_destroy(&job_cpuacct_cg);
		error("jobacct_gather/cgroup: unable to create jobstep %u.%u "
		      "task %u cpuacct cgroup", jobid, stepid, taskid);
		fstatus = SLURM_ERROR;
		goto error;
	}

	if (xcgroup_instanciate(&task_cpuacct_cg) != XCGROUP_SUCCESS) {
		xcgroup_destroy(&user_cpuacct_cg);
		xcgroup_destroy(&job_cpuacct_cg);
		xcgroup_destroy(&step_cpuacct_cg);
		error("jobacct_gather/cgroup: unable to instantiate jobstep "
		      "%u.%u task %u cpuacct cgroup", jobid, stepid, taskid);
		fstatus = SLURM_ERROR;
		goto error;
	}

	/*
	 * Attach the slurmstepd to the task cpuacct cgroup
	 */
	rc = xcgroup_add_pids(&task_cpuacct_cg, &pid, 1);
	if (rc != XCGROUP_SUCCESS) {
		error("jobacct_gather/cgroup: unable to add slurmstepd to "
		      "cpuacct cg '%s'", task_cpuacct_cg.path);
		fstatus = SLURM_ERROR;
	} else
		fstatus = SLURM_SUCCESS;

error:
	xcgroup_unlock(&cpuacct_cg);
	xcgroup_destroy(&cpuacct_cg);
	return fstatus;
}