blob: 304de0f3a0a81ce7a09f1a3d0af8c1439a611ee2 [file] [log] [blame]
#include <Python.h>
#include "py3compat.h"
#include <sched.h>
#include <errno.h>
#include <syscall.h>
#ifndef __unused
#define __unused __attribute__ ((unused))
#endif
#ifndef CPU_ALLOC
/* From glibc-headers-2.12-1.7.el6.x86_64 */
/* Basic access functions. */
#define __CPUELT(cpu) ((cpu) / __NCPUBITS)
#define __CPUMASK(cpu) ((__cpu_mask) 1 << ((cpu) % __NCPUBITS))
#define CPU_ALLOC_SIZE(ncpus) \
((((ncpus) + __NCPUBITS - 1) / __NCPUBITS) * sizeof (__cpu_mask))
/* Access functions for CPU masks. */
# if __GNUC_PREREQ (2, 91)
# define __CPU_ZERO_S(setsize, cpusetp) \
do __builtin_memset (cpusetp, '\0', setsize); while (0)
# else
# define __CPU_ZERO_S(setsize, cpusetp) \
do { \
size_t __i; \
size_t __imax = (setsize) / sizeof (__cpu_mask); \
__cpu_mask *__bits = (cpusetp)->__bits; \
for (__i = 0; __i < __imax; ++__i) \
__bits[__i] = 0; \
} while (0)
# endif
#define CPU_FREE(cpus) free(cpus)
#define CPU_ALLOC(ncpus) malloc(CPU_ALLOC_SIZE(ncpus))
# define __CPU_SET_S(cpu, setsize, cpusetp) \
(__extension__ \
({ size_t __cpu = (cpu); \
__cpu < 8 * (setsize) \
? (((__cpu_mask *) ((cpusetp)->__bits))[__CPUELT (__cpu)] \
|= __CPUMASK (__cpu)) \
: 0; }))
# define __CPU_ISSET_S(cpu, setsize, cpusetp) \
(__extension__ \
({ size_t __cpu = (cpu); \
__cpu < 8 * (setsize) \
? ((((__const __cpu_mask *) ((cpusetp)->__bits))[__CPUELT (__cpu)] \
& __CPUMASK (__cpu))) != 0 \
: 0; }))
#define CPU_SET_S(cpu, setsize, cpusetp) __CPU_SET_S (cpu, setsize, cpusetp)
#define CPU_ISSET_S(cpu, setsize, cpusetp) __CPU_ISSET_S (cpu, setsize, cpusetp)
#define CPU_ZERO_S(setsize, cpusetp) __CPU_ZERO_S (setsize, cpusetp)
#endif /* CPU_ALLOC */
#ifndef SCHED_RESET_ON_FORK
#define SCHED_RESET_ON_FORK 0x40000000
#endif
/*
* The following bitmask declarations, bitmask_*() routines, and associated
* _setbit() and _getbit() routines are:
* Copyright (c) 2004 Silicon Graphics, Inc. (SGI) All rights reserved.
* SGI publishes it under the terms of the GNU General Public License, v2,
* as published by the Free Software Foundation.
*/
#define howmany(x,y) (((x)+((y)-1))/(y))
#define bitsperlong (8 * sizeof(unsigned long))
#define longsperbits(n) howmany(n, bitsperlong)
#define bytesperbits(x) ((x+7)/8)
/*
* Number of bits in a CPU bitmask on current system
*/
static int __get_max_number_of_cpus(void)
{
int n;
int cpus = 2048;
for (;;) {
unsigned long buffer[longsperbits(cpus)];
memset(buffer, 0, sizeof(buffer));
/* the library version does not return size of cpumask_t */
n = syscall(SYS_sched_getaffinity, 0, bytesperbits(cpus), &buffer);
if (n < 0 && errno == EINVAL && cpus < 1024 * 1024) {
cpus *= 2;
continue;
}
return n * 8;
}
return -1;
}
static PyObject *get_max_number_of_cpus(PyObject *self __unused, PyObject *args __unused)
{
int ret = __get_max_number_of_cpus();
if (ret < 0)
return PyErr_SetFromErrno(PyExc_OSError);
return Py_BuildValue("i", ret);
}
static PyObject *get_affinity(PyObject *self __unused, PyObject *args)
{
PyObject *list = NULL;
cpu_set_t *cpus;
int pid, cpu;
size_t cpusetsize;
int max_cpus;
if (!PyArg_ParseTuple(args, "i", &pid))
goto out_error;
max_cpus = __get_max_number_of_cpus();
if (max_cpus < 0)
goto out_error;
cpus = CPU_ALLOC(max_cpus);
if (cpus == NULL)
return PyErr_NoMemory();
cpusetsize = CPU_ALLOC_SIZE(max_cpus);
CPU_ZERO_S(cpusetsize, cpus);
if (sched_getaffinity(pid, cpusetsize, cpus) < 0)
goto out_free;
list = PyList_New(0);
for (cpu = 0; cpu < max_cpus; ++cpu)
if (CPU_ISSET_S(cpu, cpusetsize, cpus))
PyList_Append(list, Py_BuildValue("i", cpu));
CPU_FREE(cpus);
return list;
out_free:
CPU_FREE(cpus);
out_error:
return PyErr_SetFromErrno(PyExc_OSError);
}
static PyObject *set_affinity(PyObject *self __unused, PyObject *args)
{
int pid, nr_elements, i, max_cpus;
cpu_set_t *cpus;
PyObject *list;
size_t cpusetsize;
if (!PyArg_ParseTuple(args, "iO", &pid, &list))
goto out_error;
max_cpus = __get_max_number_of_cpus();
if (max_cpus < 0)
goto out_error;
cpus = CPU_ALLOC(max_cpus);
if (cpus == NULL)
return PyErr_NoMemory();
cpusetsize = CPU_ALLOC_SIZE(max_cpus);
CPU_ZERO_S(cpusetsize, cpus);
nr_elements = PyList_Size(list);
for (i = 0; i < nr_elements; ++i) {
int cpu = PyInt_AsLong(PyList_GetItem(list, i));
if (cpu >= max_cpus) {
PyErr_SetString(PyExc_OSError, "Invalid CPU");
CPU_FREE(cpus);
return NULL;
}
CPU_SET_S(cpu, cpusetsize, cpus);
}
i = sched_setaffinity(pid, cpusetsize, cpus);
CPU_FREE(cpus);
if (i < 0)
return PyErr_SetFromErrno(PyExc_OSError);
Py_INCREF(Py_None);
return Py_None;
out_error:
return PyErr_SetFromErrno(PyExc_OSError);
}
static PyObject *get_scheduler(PyObject *self __unused, PyObject *args)
{
int pid, scheduler;
if (!PyArg_ParseTuple(args, "i", &pid))
return NULL;
scheduler = sched_getscheduler(pid);
if (scheduler < 0)
return PyErr_SetFromErrno(PyExc_OSError);
return Py_BuildValue("i", scheduler);
}
static PyObject *set_scheduler(PyObject *self __unused, PyObject *args)
{
int pid, policy, priority;
struct sched_param param;
if (!PyArg_ParseTuple(args, "iii", &pid, &policy, &priority))
return NULL;
memset(&param, 0, sizeof(param));
param.sched_priority = priority;
if (sched_setscheduler(pid, policy, &param) < 0)
return PyErr_SetFromErrno(PyExc_OSError);
Py_RETURN_NONE;
}
static PyObject *get_priority(PyObject *self __unused, PyObject *args)
{
int pid;
struct sched_param param = { .sched_priority = -1, };
if (!PyArg_ParseTuple(args, "i", &pid))
return NULL;
if (sched_getparam(pid, &param) != 0)
return PyErr_SetFromErrno(PyExc_OSError);
return Py_BuildValue("i", param.sched_priority);
}
#ifndef SCHED_BATCH
#define SCHED_BATCH 3
#endif
#ifndef SCHED_IDLE
#define SCHED_IDLE 5
#endif
#ifndef SCHED_DEADLINE
#define SCHED_DEADLINE 6
#endif
static PyObject *schedstr(PyObject *self __unused, PyObject *args)
{
int scheduler;
char *s;
if (!PyArg_ParseTuple(args, "i", &scheduler))
return NULL;
switch (scheduler & ~SCHED_RESET_ON_FORK) {
case SCHED_OTHER: s = "SCHED_OTHER"; break;
case SCHED_RR: s = "SCHED_RR"; break;
case SCHED_FIFO: s = "SCHED_FIFO"; break;
case SCHED_BATCH: s = "SCHED_BATCH"; break;
case SCHED_IDLE: s = "SCHED_IDLE"; break;
case SCHED_DEADLINE: s = "SCHED_DEADLINE"; break;
default: s = "UNKNOWN"; break;
}
return PyStr_FromString(s);
}
static PyObject *schedfromstr(PyObject *self __unused, PyObject *args)
{
int scheduler;
char *s;
if (!PyArg_ParseTuple(args, "s", &s))
return NULL;
if (strcmp(s, "SCHED_OTHER") == 0)
scheduler = SCHED_OTHER;
else if (strcmp(s, "SCHED_RR") == 0)
scheduler = SCHED_RR;
else if (strcmp(s, "SCHED_FIFO") == 0)
scheduler = SCHED_FIFO;
else if (strcmp(s, "SCHED_BATCH") == 0)
scheduler = SCHED_BATCH;
else if (strcmp(s, "SCHED_IDLE") == 0)
scheduler = SCHED_IDLE;
else if (strcmp(s, "SCHED_DEADLINE") == 0)
scheduler = SCHED_DEADLINE;
else {
PyErr_SetString(PyExc_OSError, "Unknown scheduler");
return NULL;
}
return Py_BuildValue("i", scheduler);
}
static PyObject *get_priority_min(PyObject *self __unused, PyObject *args)
{
int policy, min;
if (!PyArg_ParseTuple(args, "i", &policy))
return NULL;
min = sched_get_priority_min(policy);
if (min < 0)
return PyErr_SetFromErrno(PyExc_OSError);
return Py_BuildValue("i", min);
}
static PyObject *get_priority_max(PyObject *self __unused, PyObject *args)
{
int policy, max;
if (!PyArg_ParseTuple(args, "i", &policy))
return NULL;
max = sched_get_priority_max(policy);
if (max < 0)
return PyErr_SetFromErrno(PyExc_OSError);
return Py_BuildValue("i", max);
}
static struct PyMethodDef PySchedutilsModuleMethods[] = {
{
.ml_name = "get_affinity",
.ml_meth = (PyCFunction)get_affinity,
.ml_flags = METH_VARARGS,
},
{
.ml_name = "set_affinity",
.ml_meth = (PyCFunction)set_affinity,
.ml_flags = METH_VARARGS,
},
{
.ml_name = "get_scheduler",
.ml_meth = (PyCFunction)get_scheduler,
.ml_flags = METH_VARARGS,
},
{
.ml_name = "set_scheduler",
.ml_meth = (PyCFunction)set_scheduler,
.ml_flags = METH_VARARGS,
},
{
.ml_name = "get_priority",
.ml_meth = (PyCFunction)get_priority,
.ml_flags = METH_VARARGS,
},
{
.ml_name = "schedstr",
.ml_meth = (PyCFunction)schedstr,
.ml_flags = METH_VARARGS,
},
{
.ml_name = "schedfromstr",
.ml_meth = (PyCFunction)schedfromstr,
.ml_flags = METH_VARARGS,
},
{
.ml_name = "get_priority_min",
.ml_meth = (PyCFunction)get_priority_min,
.ml_flags = METH_VARARGS,
},
{
.ml_name = "get_priority_max",
.ml_meth = (PyCFunction)get_priority_max,
.ml_flags = METH_VARARGS,
},
{
.ml_name = "get_max_number_of_cpus",
.ml_meth = (PyCFunction)get_max_number_of_cpus,
.ml_flags = METH_NOARGS,
},
{ .ml_name = NULL, },
};
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
.m_name = "schedutils",
.m_doc = NULL,
.m_size = -1,
.m_methods = PySchedutilsModuleMethods,
};
MODULE_INIT_FUNC(schedutils)
{
PyObject *m = PyModule_Create(&moduledef);
if (m == NULL)
return NULL;
PyModule_AddIntConstant(m, "SCHED_OTHER", SCHED_OTHER);
PyModule_AddIntConstant(m, "SCHED_FIFO", SCHED_FIFO);
PyModule_AddIntConstant(m, "SCHED_RR", SCHED_RR);
PyModule_AddIntConstant(m, "SCHED_BATCH", SCHED_BATCH);
PyModule_AddIntConstant(m, "SCHED_IDLE", SCHED_IDLE);
PyModule_AddIntConstant(m, "SCHED_DEADLINE", SCHED_DEADLINE);
PyModule_AddIntConstant(m, "SCHED_RESET_ON_FORK", SCHED_RESET_ON_FORK);
return m;
}