Add APIs for killing off processes inside a cgroup

The virCgroupKill method kills all PIDs found in a cgroup

The virCgroupKillRecursively method does this recursively
for child cgroups.

The virCgroupKillPainfully method does a recursive kill
several times in a row until everything has really died
This commit is contained in:
Daniel P. Berrange 2011-02-22 17:33:59 +00:00
parent 16ba2aafc4
commit 33191b419c
3 changed files with 236 additions and 0 deletions

View File

@ -75,6 +75,9 @@ virCgroupGetMemoryHardLimit;
virCgroupGetMemorySoftLimit;
virCgroupGetMemoryUsage;
virCgroupGetSwapHardLimit;
virCgroupKill;
virCgroupKillRecursive;
virCgroupKillPainfully;
virCgroupMounted;
virCgroupRemove;
virCgroupSetBlkioWeight;

View File

@ -22,6 +22,7 @@
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <signal.h>
#include <libgen.h>
#include <dirent.h>
@ -31,6 +32,7 @@
#include "cgroup.h"
#include "logging.h"
#include "files.h"
#include "hash.h"
#define CGROUP_MAX_VAL 512
@ -257,6 +259,19 @@ static int virCgroupPathOfController(virCgroupPtr group,
const char *key,
char **path)
{
if (controller == -1) {
int i;
for (i = 0 ; i < VIR_CGROUP_CONTROLLER_LAST ; i++) {
if (group->controllers[i].mountPoint &&
group->controllers[i].placement) {
controller = i;
break;
}
}
}
if (controller == -1)
return -ENOSYS;
if (group->controllers[controller].mountPoint == NULL)
return -ENOENT;
@ -1291,3 +1306,217 @@ int virCgroupGetFreezerState(virCgroupPtr group, char **state)
VIR_CGROUP_CONTROLLER_CPU,
"freezer.state", state);
}
static int virCgroupKillInternal(virCgroupPtr group, int signum, virHashTablePtr pids)
{
int rc;
int killedAny = 0;
char *keypath = NULL;
bool done = false;
VIR_DEBUG("group=%p path=%s signum=%d pids=%p", group, group->path, signum, pids);
rc = virCgroupPathOfController(group, -1, "tasks", &keypath);
if (rc != 0) {
VIR_DEBUG("No path of %s, tasks", group->path);
return rc;
}
/* PIDs may be forking as we kill them, so loop
* until there are no new PIDs found
*/
while (!done) {
done = true;
FILE *fp;
if (!(fp = fopen(keypath, "r"))) {
rc = -errno;
VIR_DEBUG("Failed to read %s: %m\n", keypath);
goto cleanup;
} else {
while (!feof(fp)) {
unsigned long pid;
if (fscanf(fp, "%lu", &pid) != 1) {
if (feof(fp))
break;
rc = -errno;
break;
}
if (virHashLookup(pids, (void*)pid))
continue;
VIR_DEBUG("pid=%lu", pid);
if (kill((pid_t)pid, signum) < 0) {
if (errno != ESRCH) {
rc = -errno;
goto cleanup;
}
/* Leave RC == 0 since we didn't kill one */
} else {
killedAny = 1;
done = false;
}
virHashAddEntry(pids, (void*)pid, (void*)1);
}
VIR_FORCE_FCLOSE(fp);
}
}
rc = killedAny ? 1 : 0;
cleanup:
VIR_FREE(keypath);
return rc;
}
static unsigned long virCgroupPidCode(const void *name)
{
return (unsigned long)name;
}
static bool virCgroupPidEqual(const void *namea, const void *nameb)
{
return namea == nameb;
}
static void *virCgroupPidCopy(const void *name)
{
return (void*)name;
}
/*
* Returns
* < 0 : errno that occurred
* 0 : no PIDs killed
* 1 : at least one PID killed
*/
int virCgroupKill(virCgroupPtr group, int signum)
{
VIR_DEBUG("group=%p path=%s signum=%d", group, group->path, signum);
int rc;
/* The 'tasks' file in cgroups can contain duplicated
* pids, so we use a hash to track which we've already
* killed.
*/
virHashTablePtr pids = virHashCreateFull(100,
NULL,
virCgroupPidCode,
virCgroupPidEqual,
virCgroupPidCopy,
NULL);
rc = virCgroupKillInternal(group, signum, pids);
virHashFree(pids);
return rc;
}
static int virCgroupKillRecursiveInternal(virCgroupPtr group, int signum, virHashTablePtr pids, bool dormdir)
{
int rc;
int killedAny = 0;
char *keypath = NULL;
DIR *dp;
virCgroupPtr subgroup = NULL;
struct dirent *ent;
VIR_DEBUG("group=%p path=%s signum=%d pids=%p", group, group->path, signum, pids);
rc = virCgroupPathOfController(group, -1, "", &keypath);
if (rc != 0) {
VIR_DEBUG("No path of %s, tasks", group->path);
return rc;
}
if ((rc = virCgroupKillInternal(group, signum, pids)) != 0)
return rc;
VIR_DEBUG("Iterate over children of %s", keypath);
if (!(dp = opendir(keypath))) {
rc = -errno;
return rc;
}
while ((ent = readdir(dp))) {
char *subpath;
if (STREQ(ent->d_name, "."))
continue;
if (STREQ(ent->d_name, ".."))
continue;
if (ent->d_type != DT_DIR)
continue;
VIR_DEBUG("Process subdir %s", ent->d_name);
if (virAsprintf(&subpath, "%s/%s", group->path, ent->d_name) < 0) {
rc = -ENOMEM;
goto cleanup;
}
if ((rc = virCgroupNew(subpath, &subgroup)) != 0)
goto cleanup;
if ((rc = virCgroupKillRecursiveInternal(subgroup, signum, pids, true)) < 0)
goto cleanup;
if (rc == 1)
killedAny = 1;
if (dormdir)
virCgroupRemove(subgroup);
virCgroupFree(&subgroup);
}
rc = killedAny;
cleanup:
virCgroupFree(&subgroup);
closedir(dp);
return rc;
}
int virCgroupKillRecursive(virCgroupPtr group, int signum)
{
int rc;
VIR_DEBUG("group=%p path=%s signum=%d", group, group->path, signum);
virHashTablePtr pids = virHashCreateFull(100,
NULL,
virCgroupPidCode,
virCgroupPidEqual,
virCgroupPidCopy,
NULL);
rc = virCgroupKillRecursiveInternal(group, signum, pids, false);
virHashFree(pids);
return rc;
}
int virCgroupKillPainfully(virCgroupPtr group)
{
int i;
int rc;
VIR_DEBUG("cgroup=%p path=%s", group, group->path);
for (i = 0 ; i < 15 ; i++) {
int signum;
if (i == 0)
signum = SIGTERM;
else if (i == 8)
signum = SIGKILL;
else
signum = 0; /* Just check for existance */
rc = virCgroupKillRecursive(group, signum);
VIR_DEBUG("Iteration %d rc=%d", i, rc);
/* If rc == -1 we hit error, if 0 we ran out of PIDs */
if (rc <= 0)
break;
usleep(200 * 1000);
}
VIR_DEBUG("Complete %d", rc);
return rc;
}

View File

@ -90,4 +90,8 @@ int virCgroupRemove(virCgroupPtr group);
void virCgroupFree(virCgroupPtr *group);
bool virCgroupMounted(virCgroupPtr cgroup, int controller);
int virCgroupKill(virCgroupPtr group, int signum);
int virCgroupKillRecursive(virCgroupPtr group, int signum);
int virCgroupKillPainfully(virCgroupPtr group);
#endif /* CGROUP_H */