Implemented linux ionice funtionality
This commit is contained in:
@@ -161,4 +161,12 @@ prio:
|
|||||||
- siriknowledged
|
- siriknowledged
|
||||||
- triald
|
- triald
|
||||||
|
|
||||||
|
ioprio:
|
||||||
|
- name:
|
||||||
|
- htop
|
||||||
|
- top
|
||||||
|
- atop
|
||||||
|
class: 2
|
||||||
|
prio: 1
|
||||||
|
|
||||||
# vim: set ft=yaml et ai ts=2 sw=2 sts=2:
|
# vim: set ft=yaml et ai ts=2 sw=2 sts=2:
|
||||||
|
|||||||
4
Makefile
4
Makefile
@@ -1,15 +1,13 @@
|
|||||||
#!/usr/bin/env gmake -f
|
#!/usr/bin/env gmake -f
|
||||||
|
|
||||||
BUILDOPTS=-ldflags="-s -w" -a -gcflags=all=-l -trimpath
|
BUILDOPTS=-ldflags="-s -w" -a -gcflags=all=-l -trimpath
|
||||||
FILELIST=main.go types.go util.go
|
|
||||||
|
|
||||||
BINARY=reniced
|
BINARY=reniced
|
||||||
|
|
||||||
all: clean build
|
all: clean build
|
||||||
|
|
||||||
build:
|
build:
|
||||||
go build ${BUILDOPTS} -o ${BINARY} ${FILELIST}
|
go build ${BUILDOPTS} -o ${BINARY}
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
go clean
|
go clean
|
||||||
|
|
||||||
|
|||||||
21
README.md
21
README.md
@@ -60,6 +60,27 @@ Unlike supplpied systemd unit, supplied System V Init script assumes reniced run
|
|||||||
setting must be set to true). Script also expects to find pid-file at **/var/run/reniced.pid** and executable itself at
|
setting must be set to true). Script also expects to find pid-file at **/var/run/reniced.pid** and executable itself at
|
||||||
**/usr/sbin/reniced**.
|
**/usr/sbin/reniced**.
|
||||||
|
|
||||||
|
## Note on ionice operation
|
||||||
|
|
||||||
|
Currently implemented only linux io prio. On all other reniced-supported systems it is noop. Please note that io
|
||||||
|
priority can be set only by digits. Refer to *man ionice* for specific io classes and priorities inplemented there.
|
||||||
|
|
||||||
|
Here is table with numbers for defined class and priorities.
|
||||||
|
|
||||||
|
| class name | class number | list of possible priorities |
|
||||||
|
|:-----------|-------------:|----------------------------:|
|
||||||
|
| NONE |0 |0 |
|
||||||
|
| RealTime |1 |0, 1, 2, 3, 4, 5, 6, 7 |
|
||||||
|
| BestEffort |2 |0, 1, 2, 3, 4, 5, 6, 7 |
|
||||||
|
| Idle |3 |0 |
|
||||||
|
|
||||||
|
None is default io prio if process is not ioreniced. And it is the same value and behaviour as BestEffort with priority
|
||||||
|
of 4.
|
||||||
|
|
||||||
|
The lower priority value means higher priority and process will eat more io.
|
||||||
|
|
||||||
|
RealTime prio is not available for regular users but for root only.
|
||||||
|
|
||||||
## Special "thanks"
|
## Special "thanks"
|
||||||
|
|
||||||
To security assholes that disrupt workflows and make this world worse.
|
To security assholes that disrupt workflows and make this world worse.
|
||||||
|
|||||||
54
globals.go
Normal file
54
globals.go
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/alitto/pond"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Пул воркеров.
|
||||||
|
var pool *pond.WorkerPool
|
||||||
|
|
||||||
|
// Канал, в который приходят уведомления для хэндлера сигналов от траппера сигналов.
|
||||||
|
var sigChan = make(chan os.Signal, 1)
|
||||||
|
|
||||||
|
var kill = make(map[string]syscall.Signal)
|
||||||
|
var renice = make(map[string]int)
|
||||||
|
var ioreniceClass = make(map[string]uint32)
|
||||||
|
var ioreniceClassdata = make(map[string]uint32)
|
||||||
|
|
||||||
|
var NotImplementedError = fmt.Errorf("SYSCALL Not Implemented on current platform") //nolint:revive,stylecheck
|
||||||
|
|
||||||
|
const IOPRIO_CLASS_NONE = uint32(0) //nolint:revive,stylecheck
|
||||||
|
const IOPRIO_CLASS_RT = uint32(1) //nolint:revive,stylecheck
|
||||||
|
const IOPRIO_CLASS_BE = uint32(2) //nolint:revive,stylecheck
|
||||||
|
const IOPRIO_CLASS_IDLE = uint32(3) //nolint:revive,stylecheck
|
||||||
|
|
||||||
|
const IOPRIO_WHO_PROCESS = 1 //nolint:revive,stylecheck
|
||||||
|
const IOPRIO_WHO_PGRP = 2 //nolint:revive,stylecheck
|
||||||
|
const IOPRIO_WHO_USER = 3 //nolint:revive,stylecheck
|
||||||
|
|
||||||
|
const IOPRIO_CLASS_SHIFT = uint32(13) //nolint:revive,stylecheck
|
||||||
|
const IOPRIO_PRIO_MASK = ((uint32(1) << IOPRIO_CLASS_SHIFT) - 1) //nolint:revive,stylecheck
|
||||||
|
|
||||||
|
var ClassToString = map[uint32]string{
|
||||||
|
IOPRIO_CLASS_NONE: "none",
|
||||||
|
IOPRIO_CLASS_RT: "realtime",
|
||||||
|
IOPRIO_CLASS_BE: "best-effort",
|
||||||
|
IOPRIO_CLASS_IDLE: "idle",
|
||||||
|
}
|
||||||
|
|
||||||
|
var StringToClass = map[string]uint32{
|
||||||
|
"none": IOPRIO_CLASS_NONE,
|
||||||
|
"0": IOPRIO_CLASS_NONE,
|
||||||
|
"realtime": IOPRIO_CLASS_RT,
|
||||||
|
"1": IOPRIO_CLASS_RT,
|
||||||
|
"best-effort": IOPRIO_CLASS_BE,
|
||||||
|
"2": IOPRIO_CLASS_BE,
|
||||||
|
"idle": IOPRIO_CLASS_IDLE,
|
||||||
|
"3": IOPRIO_CLASS_IDLE,
|
||||||
|
}
|
||||||
|
|
||||||
|
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */
|
||||||
17
ionice.go
Normal file
17
ionice.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
func PrioToClass(prio uint32) uint32 {
|
||||||
|
return prio >> IOPRIO_CLASS_SHIFT
|
||||||
|
}
|
||||||
|
|
||||||
|
func PrioToClassdata(prio uint32) uint32 {
|
||||||
|
return prio & IOPRIO_PRIO_MASK
|
||||||
|
}
|
||||||
|
|
||||||
|
func ClassAndClassdataToPrio(class uint32, classdata uint32) uint32 {
|
||||||
|
return ((class) << IOPRIO_CLASS_SHIFT) | classdata
|
||||||
|
}
|
||||||
|
|
||||||
|
func PrioToClassAndClassdata(prio uint32) (uint32, uint32) {
|
||||||
|
return PrioToClass(prio), PrioToClassdata(prio)
|
||||||
|
}
|
||||||
80
ionice_linux.go
Normal file
80
ionice_linux.go
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
//go:build linux
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
proc "github.com/shirou/gopsutil/v3/process"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetIOPriority достаёт приоритет вводв-вывода для укаанного процесса.
|
||||||
|
func GetIOPriority(which int, who int) (uint32, error) {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
r0, _, e1 = syscall.Syscall(syscall.SYS_IOPRIO_GET, uintptr(which), uintptr(who), 0)
|
||||||
|
prio = uint32(r0)
|
||||||
|
)
|
||||||
|
|
||||||
|
if e1 != 0 {
|
||||||
|
err = fmt.Errorf(fmt.Sprintf("Received error number %v", e1))
|
||||||
|
}
|
||||||
|
|
||||||
|
return prio, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetIOPriority ставит приоритет вводв-вывода для укаанного процесса.
|
||||||
|
func SetIOPriority(which int, who int, prio uint32) error {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
_, _, e1 = syscall.Syscall(syscall.SYS_IOPRIO_SET, uintptr(which), uintptr(who), uintptr(prio))
|
||||||
|
)
|
||||||
|
|
||||||
|
if e1 != 0 {
|
||||||
|
err = fmt.Errorf(fmt.Sprintf("Received error number %v", e1))
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// IORenice выставляет процессу приоритет для операций ввода-вывода.
|
||||||
|
func IORenice(cnf Config, p *proc.Process, processName string) {
|
||||||
|
if currentIOPrioLevel, err := GetIOPriority(IOPRIO_WHO_PROCESS, int(p.Pid)); err == nil {
|
||||||
|
currentClass, currentClassdata := PrioToClassAndClassdata(currentIOPrioLevel)
|
||||||
|
class := ioreniceClass[processName]
|
||||||
|
classdata := ioreniceClassdata[processName]
|
||||||
|
|
||||||
|
if class == currentClass && classdata == currentClassdata {
|
||||||
|
if cnf.Debug && currentIOPrioLevel != 0 { // 0 - это дефолтный уровень io niceness.
|
||||||
|
log.Printf(
|
||||||
|
"IONiceness for %s(%d) already set to %d %d",
|
||||||
|
processName,
|
||||||
|
p.Pid,
|
||||||
|
currentClass,
|
||||||
|
currentClassdata,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
iopriority := ClassAndClassdataToPrio(class, classdata)
|
||||||
|
|
||||||
|
_ = SetIOPriority(IOPRIO_WHO_PROCESS, int(p.Pid), iopriority)
|
||||||
|
|
||||||
|
if cnf.Debug {
|
||||||
|
log.Printf(
|
||||||
|
"Set ioniceness for %s(%d) to %d %d (was %d %d)",
|
||||||
|
processName,
|
||||||
|
p.Pid,
|
||||||
|
class,
|
||||||
|
classdata,
|
||||||
|
currentClass,
|
||||||
|
currentClassdata,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Printf("Unable to get ioprio for %s(%d)", processName, p.Pid)
|
||||||
|
}
|
||||||
|
}
|
||||||
16
ionice_other.go
Normal file
16
ionice_other.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
//go:build !linux
|
||||||
|
// +build !linux
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import proc "github.com/shirou/gopsutil/v3/process"
|
||||||
|
|
||||||
|
func GetIOPriority(which int, who int) (prio uint32, err error) {
|
||||||
|
return 0, NotImplementedError
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetIOPriority(which int, who int, prio uint32) (err error) {
|
||||||
|
return NotImplementedError
|
||||||
|
}
|
||||||
|
|
||||||
|
func IORenice(cnf Config, p *proc.Process, processName string) {}
|
||||||
25
main.go
25
main.go
@@ -15,15 +15,6 @@ import (
|
|||||||
proc "github.com/shirou/gopsutil/v3/process"
|
proc "github.com/shirou/gopsutil/v3/process"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Пул воркеров.
|
|
||||||
var pool *pond.WorkerPool
|
|
||||||
|
|
||||||
// Канал, в который приходят уведомления для хэндлера сигналов от траппера сигналов.
|
|
||||||
var sigChan = make(chan os.Signal, 1)
|
|
||||||
|
|
||||||
var kill = make(map[string]syscall.Signal)
|
|
||||||
var renice = make(map[string]int)
|
|
||||||
|
|
||||||
// main основная функция программы.
|
// main основная функция программы.
|
||||||
func main() {
|
func main() {
|
||||||
// Самое время поставить траппер сигналов.
|
// Самое время поставить траппер сигналов.
|
||||||
@@ -86,7 +77,6 @@ func reniced(cnf Config) {
|
|||||||
for _, p := range processList {
|
for _, p := range processList {
|
||||||
ok := pool.TrySubmit(func() {
|
ok := pool.TrySubmit(func() {
|
||||||
if processName, err := p.Name(); err == nil {
|
if processName, err := p.Name(); err == nil {
|
||||||
|
|
||||||
// Для каждого процесса извлекаем его текущий priority.
|
// Для каждого процесса извлекаем его текущий priority.
|
||||||
if currentPrioLevel, err := syscall.Getpriority(syscall.PRIO_PROCESS, int(p.Pid)); err == nil {
|
if currentPrioLevel, err := syscall.Getpriority(syscall.PRIO_PROCESS, int(p.Pid)); err == nil {
|
||||||
if niceLevel := renice[processName]; niceLevel != 0 {
|
if niceLevel := renice[processName]; niceLevel != 0 {
|
||||||
@@ -121,11 +111,18 @@ func reniced(cnf Config) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else if cnf.Debug {
|
} else if cnf.Debug {
|
||||||
log.Printf("Niceness for %d already set to %d", p.Pid, niceLevel)
|
log.Printf(
|
||||||
|
"Niceness for %s(%d) already set to %d",
|
||||||
|
processName,
|
||||||
|
p.Pid,
|
||||||
|
niceLevel,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IORenice(cnf, p, processName)
|
||||||
|
|
||||||
// Посылаем процессу сигналы, если таковые есть в конфиге.
|
// Посылаем процессу сигналы, если таковые есть в конфиге.
|
||||||
if killSignal := kill[processName]; killSignal != 0 {
|
if killSignal := kill[processName]; killSignal != 0 {
|
||||||
switch killSignal { //nolint:exhaustive
|
switch killSignal { //nolint:exhaustive
|
||||||
@@ -134,8 +131,9 @@ func reniced(cnf Config) {
|
|||||||
|
|
||||||
if cnf.Debug {
|
if cnf.Debug {
|
||||||
log.Printf(
|
log.Printf(
|
||||||
"Matching processName with killProcessname (%s), SIG%s sent",
|
"Matching processName with killProcessname %s(%d), SIG%s sent",
|
||||||
processName,
|
processName,
|
||||||
|
p.Pid,
|
||||||
strings.ToUpper(killSignal.String()),
|
strings.ToUpper(killSignal.String()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -145,8 +143,9 @@ func reniced(cnf Config) {
|
|||||||
|
|
||||||
if cnf.Debug {
|
if cnf.Debug {
|
||||||
log.Printf(
|
log.Printf(
|
||||||
"Matching processName with killProcessname (%s), SIG%s sent",
|
"Matching processName with killProcessname %s(%d), SIG%s sent",
|
||||||
processName,
|
processName,
|
||||||
|
p.Pid,
|
||||||
strings.ToUpper(killSignal.String()),
|
strings.ToUpper(killSignal.String()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
8
types.go
8
types.go
@@ -29,6 +29,8 @@ type Config struct {
|
|||||||
Kill Kill `yaml:"kill,omitempty"`
|
Kill Kill `yaml:"kill,omitempty"`
|
||||||
|
|
||||||
Prio Prio `yaml:"prio,omitempty"`
|
Prio Prio `yaml:"prio,omitempty"`
|
||||||
|
|
||||||
|
IOPrio IOPrio `yaml:"ioprio,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Prio []struct {
|
type Prio []struct {
|
||||||
@@ -41,4 +43,10 @@ type Kill []struct {
|
|||||||
Name []string `yaml:"name,omitempty"`
|
Name []string `yaml:"name,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type IOPrio []struct {
|
||||||
|
Class uint32 `yaml:"class,omitempty"`
|
||||||
|
Prio uint32 `yaml:"prio,omitempty"`
|
||||||
|
Name []string `yaml:"name,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */
|
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */
|
||||||
|
|||||||
27
util.go
27
util.go
@@ -122,6 +122,33 @@ func readConfFile(path string) (Config, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, v := range cfg.IOPrio {
|
||||||
|
switch {
|
||||||
|
case len(v.Name) == 0:
|
||||||
|
log.Println("Skipping empty name entry in ioprio config block")
|
||||||
|
case v.Class > 3:
|
||||||
|
log.Printf("Skipping entry %s with class > 3 in ioprio config block", v.Name)
|
||||||
|
case v.Class == 0 && v.Prio != 0:
|
||||||
|
log.Printf("Skipping entry %s with class 0 and prio not 0 in ioprio config block", v.Name)
|
||||||
|
case v.Class == 3 && v.Prio != 0:
|
||||||
|
log.Printf("Skipping entry %s with class 3 and prio not 0 in ioprio config block", v.Name)
|
||||||
|
case v.Class == 1 && v.Prio > 7:
|
||||||
|
log.Printf("Skipping entry %s with class 1 and prio > 7 in ioprio config block", v.Name)
|
||||||
|
case v.Class == 2 && v.Prio > 7:
|
||||||
|
log.Printf("Skipping entry %s with class 2 and prio > 7 in ioprio config block", v.Name)
|
||||||
|
default:
|
||||||
|
for _, processName := range v.Name {
|
||||||
|
ioreniceClass[processName] = v.Class
|
||||||
|
ioreniceClassdata[processName] = v.Prio
|
||||||
|
|
||||||
|
if cfg.Debug {
|
||||||
|
log.Printf("Add %s to list of ionice class %d processes.", processName, v.Class)
|
||||||
|
log.Printf("Add %s to list of ionice prio %d processes.", processName, v.Prio)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for _, v := range cfg.Kill {
|
for _, v := range cfg.Kill {
|
||||||
if len(v.Name) != 0 {
|
if len(v.Name) != 0 {
|
||||||
switch v.Sig {
|
switch v.Sig {
|
||||||
|
|||||||
Reference in New Issue
Block a user