Implemented linux ionice funtionality
This commit is contained in:
@@ -161,4 +161,12 @@ prio:
|
||||
- siriknowledged
|
||||
- triald
|
||||
|
||||
ioprio:
|
||||
- name:
|
||||
- htop
|
||||
- top
|
||||
- atop
|
||||
class: 2
|
||||
prio: 1
|
||||
|
||||
# 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
|
||||
|
||||
BUILDOPTS=-ldflags="-s -w" -a -gcflags=all=-l -trimpath
|
||||
FILELIST=main.go types.go util.go
|
||||
|
||||
BINARY=reniced
|
||||
|
||||
all: clean build
|
||||
|
||||
build:
|
||||
go build ${BUILDOPTS} -o ${BINARY} ${FILELIST}
|
||||
|
||||
go build ${BUILDOPTS} -o ${BINARY}
|
||||
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
|
||||
**/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"
|
||||
|
||||
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"
|
||||
)
|
||||
|
||||
// Пул воркеров.
|
||||
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 основная функция программы.
|
||||
func main() {
|
||||
// Самое время поставить траппер сигналов.
|
||||
@@ -86,7 +77,6 @@ func reniced(cnf Config) {
|
||||
for _, p := range processList {
|
||||
ok := pool.TrySubmit(func() {
|
||||
if processName, err := p.Name(); err == nil {
|
||||
|
||||
// Для каждого процесса извлекаем его текущий priority.
|
||||
if currentPrioLevel, err := syscall.Getpriority(syscall.PRIO_PROCESS, int(p.Pid)); err == nil {
|
||||
if niceLevel := renice[processName]; niceLevel != 0 {
|
||||
@@ -121,11 +111,18 @@ func reniced(cnf Config) {
|
||||
)
|
||||
}
|
||||
} 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 {
|
||||
switch killSignal { //nolint:exhaustive
|
||||
@@ -134,8 +131,9 @@ func reniced(cnf Config) {
|
||||
|
||||
if cnf.Debug {
|
||||
log.Printf(
|
||||
"Matching processName with killProcessname (%s), SIG%s sent",
|
||||
"Matching processName with killProcessname %s(%d), SIG%s sent",
|
||||
processName,
|
||||
p.Pid,
|
||||
strings.ToUpper(killSignal.String()),
|
||||
)
|
||||
}
|
||||
@@ -145,8 +143,9 @@ func reniced(cnf Config) {
|
||||
|
||||
if cnf.Debug {
|
||||
log.Printf(
|
||||
"Matching processName with killProcessname (%s), SIG%s sent",
|
||||
"Matching processName with killProcessname %s(%d), SIG%s sent",
|
||||
processName,
|
||||
p.Pid,
|
||||
strings.ToUpper(killSignal.String()),
|
||||
)
|
||||
}
|
||||
|
||||
8
types.go
8
types.go
@@ -29,6 +29,8 @@ type Config struct {
|
||||
Kill Kill `yaml:"kill,omitempty"`
|
||||
|
||||
Prio Prio `yaml:"prio,omitempty"`
|
||||
|
||||
IOPrio IOPrio `yaml:"ioprio,omitempty"`
|
||||
}
|
||||
|
||||
type Prio []struct {
|
||||
@@ -41,4 +43,10 @@ type Kill []struct {
|
||||
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: */
|
||||
|
||||
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 {
|
||||
if len(v.Name) != 0 {
|
||||
switch v.Sig {
|
||||
|
||||
Reference in New Issue
Block a user