Compare commits

..

10 Commits

Author SHA1 Message Date
Alexey Avramov
bf477da780
Merge pull request #136 from flaviut/fix-135
Fix for zfs > 2.2.0 (closes #135)
2024-01-28 11:22:25 +09:00
Flaviu Tamas
d19be1911d
Fix for zfs > 2.2.0 (closes #135)
- more reliable and readable stats file parsing
- uses original logic for older versions of zfs
  - I'm not convinced the original logic is right after reading
  https://utcc.utoronto.ca/~cks/space/blog/solaris/ZFSARCItsVariousSizes,
  but I don't want to potentially break things
2023-12-31 14:30:05 -05:00
Alexey Avramov
ecf0ba7c8e
Merge pull request #130 from kawaii-ghost/master
Fix deprecated stuff
2023-06-10 15:56:18 +09:00
F̷N̷
3bf5346766
Another threading.getName() to .name attribute 2023-06-10 13:30:47 +07:00
F̷N̷
72600a4251
Fix deprecated sre_constants
sre_constants is deprecated in favor of re.
2023-06-10 08:06:37 +07:00
F̷N̷
c9bfdf391f
Fix deprecated threading.getName()
threading.getName() is deprecated as for Python 3.10. Use name attribute "threading.name" instead.

https://stackoverflow.com/a/69656065
https://stackoverflow.com/a/69656065
2023-06-10 07:56:34 +07:00
Alexey Avramov
9811ac51aa
Merge pull request #127 from ElijahLynn/patch-3
Remove $ prefix from all install commands in the README.md to make copying work
2023-02-13 03:13:25 +09:00
Elijah Lynn
b5facbfc53
Remove $ prefix from all install commands to make copying work
All the install commands have a `$` prefixed to them and it makes copying not work because it includes the `$`. It would be easier to install without the `$`.
2023-02-10 14:39:50 -08:00
Alexey Avramov
d822dffdf0 fix typo 2023-01-19 01:42:31 +09:00
Alexey Avramov
7126c2ebdd Update README 2023-01-19 01:40:17 +09:00
2 changed files with 73 additions and 90 deletions

View File

@ -4,7 +4,6 @@
[![Build Status](https://travis-ci.org/hakavlad/nohang.svg?branch=master)](https://travis-ci.org/hakavlad/nohang) [![Build Status](https://travis-ci.org/hakavlad/nohang.svg?branch=master)](https://travis-ci.org/hakavlad/nohang)
![CodeQL](https://github.com/hakavlad/nohang/workflows/CodeQL/badge.svg) ![CodeQL](https://github.com/hakavlad/nohang/workflows/CodeQL/badge.svg)
[![Total alerts](https://img.shields.io/lgtm/alerts/g/hakavlad/nohang.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/hakavlad/nohang/alerts/)
[![Packaging status](https://repology.org/badge/tiny-repos/nohang.svg)](https://repology.org/project/nohang/versions) [![Packaging status](https://repology.org/badge/tiny-repos/nohang.svg)](https://repology.org/project/nohang/versions)
`nohang` package provides a highly configurable daemon for Linux which is able to correctly prevent [out of memory](https://en.wikipedia.org/wiki/Out_of_memory) (OOM) and keep system responsiveness in low memory conditions. `nohang` package provides a highly configurable daemon for Linux which is able to correctly prevent [out of memory](https://en.wikipedia.org/wiki/Out_of_memory) (OOM) and keep system responsiveness in low memory conditions.
@ -44,9 +43,10 @@ Use one of the userspace OOM killers:
- [systemd-oomd](https://man7.org/linux/man-pages/man8/systemd-oomd.service.8.html): Provided by systemd as `systemd-oomd.service` that uses cgroups-v2 and pressure stall information (PSI) to monitor and take action on processes before an OOM occurs in kernel space. It's used by default on [desktop versions of Fedora 34](https://fedoraproject.org/wiki/Changes/EnableSystemdOomd). - [systemd-oomd](https://man7.org/linux/man-pages/man8/systemd-oomd.service.8.html): Provided by systemd as `systemd-oomd.service` that uses cgroups-v2 and pressure stall information (PSI) to monitor and take action on processes before an OOM occurs in kernel space. It's used by default on [desktop versions of Fedora 34](https://fedoraproject.org/wiki/Changes/EnableSystemdOomd).
- [low-memory-monitor](https://gitlab.freedesktop.org/hadess/low-memory-monitor/): There's a [project announcement](http://www.hadess.net/2019/08/low-memory-monitor-new-project.html). - [low-memory-monitor](https://gitlab.freedesktop.org/hadess/low-memory-monitor/): There's a [project announcement](http://www.hadess.net/2019/08/low-memory-monitor-new-project.html).
- [psi-monitor](https://github.com/endlessm/eos-boot-helper/tree/master/psi-monitor): It's used by default on [Endless OS](https://endlessos.com/). - [psi-monitor](https://github.com/endlessm/eos-boot-helper/tree/master/psi-monitor): It's used by default on [Endless OS](https://endlessos.com/).
- `nohang`: nohang is earlyoom on steroids and has many useful features, see below. Maybe this is a good choice for modern desktops and servers if you need fine-tuning. It's used by default on [Garuda Linux](https://garudalinux.org/). - `nohang`: nohang is earlyoom on steroids and has many useful features, see below. Maybe this is a good choice for modern desktops and servers if you need fine-tuning. Previously it was used by default on [Garuda Linux](https://garudalinux.org/).
Use these tools to improve responsiveness during heavy swapping: Use these tools to improve responsiveness during heavy swapping:
- MGLRU patchset is merged in Linux 6.1. Setting `min_ttl_ms` > 50 can help you.
- [le9-patch](https://github.com/hakavlad/le9-patch): [PATCH] mm: Protect clean file pages under memory pressure to prevent thrashing, avoid high latency and prevent livelock in near-OOM conditions. It's kernel-side solution that can fix the OOM killer behavior. - [le9-patch](https://github.com/hakavlad/le9-patch): [PATCH] mm: Protect clean file pages under memory pressure to prevent thrashing, avoid high latency and prevent livelock in near-OOM conditions. It's kernel-side solution that can fix the OOM killer behavior.
- [prelockd](https://github.com/hakavlad/prelockd): Lock executables and shared libraries in memory to improve system responsiveness under low-memory conditions. - [prelockd](https://github.com/hakavlad/prelockd): Lock executables and shared libraries in memory to improve system responsiveness under low-memory conditions.
- [memavaild](https://github.com/hakavlad/memavaild): Keep amount of available memory by evicting memory of selected cgroups into swap space. - [memavaild](https://github.com/hakavlad/memavaild): Keep amount of available memory by evicting memory of selected cgroups into swap space.
@ -118,18 +118,16 @@ To show GUI notifications (optional):
## How to install ## How to install
#### To install on [Fedora](https://src.fedoraproject.org/rpms/nohang/): #### To install on [Fedora](https://src.fedoraproject.org/rpms/nohang/):
```bash
$ sudo dnf install nohang-desktop Orphaned for 6+ weeks, not available.
$ sudo systemctl enable --now nohang-desktop.service
```
#### To install on RHEL 7 and RHEL 8: #### To install on RHEL 7 and RHEL 8:
nohang is avaliable in [EPEL repos](https://fedoraproject.org/wiki/EPEL). nohang is avaliable in [EPEL repos](https://fedoraproject.org/wiki/EPEL).
```bash ```bash
$ sudo yum install nohang sudo yum install nohang
$ sudo systemctl enable nohang.service sudo systemctl enable nohang.service
$ sudo systemctl start nohang.service sudo systemctl start nohang.service
``` ```
To enable PSI on RHEL 8 pass `psi=1` to kernel boot cmdline. To enable PSI on RHEL 8 pass `psi=1` to kernel boot cmdline.
@ -137,18 +135,18 @@ To enable PSI on RHEL 8 pass `psi=1` to kernel boot cmdline.
Use your favorite [AUR helper](https://wiki.archlinux.org/index.php/AUR_helpers). For example, Use your favorite [AUR helper](https://wiki.archlinux.org/index.php/AUR_helpers). For example,
```bash ```bash
$ yay -S nohang-git yay -S nohang-git
$ sudo systemctl enable --now nohang-desktop.service sudo systemctl enable --now nohang-desktop.service
``` ```
#### To install on Ubuntu 20.04/20.10 #### To install on Ubuntu 20.04/20.10
To install from [PPA](https://launchpad.net/~oibaf/+archive/ubuntu/test/): To install from [PPA](https://launchpad.net/~oibaf/+archive/ubuntu/test/):
```bash ```bash
$ sudo add-apt-repository ppa:oibaf/test sudo add-apt-repository ppa:oibaf/test
$ sudo apt update sudo apt update
$ sudo apt install nohang sudo apt install nohang
$ sudo systemctl enable --now nohang-desktop.service sudo systemctl enable --now nohang-desktop.service
``` ```
#### To install on Debian and Ubuntu-based systems: #### To install on Debian and Ubuntu-based systems:
@ -157,23 +155,23 @@ Outdated and buggy nohang v0.1 release was packaged for [Debian 11](https://pack
It's easy to build a deb package with the latest git snapshot. Install build dependencies: It's easy to build a deb package with the latest git snapshot. Install build dependencies:
```bash ```bash
$ sudo apt install make fakeroot sudo apt install make fakeroot
``` ```
Clone the latest git snapshot and run the build script to build the package: Clone the latest git snapshot and run the build script to build the package:
```bash ```bash
$ git clone https://github.com/hakavlad/nohang.git && cd nohang git clone https://github.com/hakavlad/nohang.git && cd nohang
$ deb/build.sh deb/build.sh
``` ```
Install the package: Install the package:
```bash ```bash
$ sudo apt install --reinstall ./deb/package.deb sudo apt install --reinstall ./deb/package.deb
``` ```
Start and enable `nohang.service` or `nohang-desktop.service` after installing the package: Start and enable `nohang.service` or `nohang-desktop.service` after installing the package:
```bash ```bash
$ sudo systemctl enable --now nohang-desktop.service sudo systemctl enable --now nohang-desktop.service
``` ```
#### To install on Gentoo and derivatives (e.g. Funtoo): #### To install on Gentoo and derivatives (e.g. Funtoo):
@ -182,53 +180,53 @@ Add the [eph kit](https://git.sr.ht/~happy_shredder/eph_kit) overlay, for exampl
Then update your repos: Then update your repos:
```bash ```bash
$ sudo layman -S # if added via layman sudo layman -S # if added via layman
$ sudo emerge --sync # local repo on Gentoo sudo emerge --sync # local repo on Gentoo
$ sudo ego sync # local repo on Funtoo sudo ego sync # local repo on Funtoo
``` ```
Install: Install:
```bash ```bash
$ sudo emerge -a nohang sudo emerge -a nohang
``` ```
Start the service: Start the service:
```bash ```bash
$ sudo rc-service nohang-desktop start sudo rc-service nohang-desktop start
``` ```
Optionally add to startup: Optionally add to startup:
```bash ```bash
$ sudo rc-update add nohang-desktop default sudo rc-update add nohang-desktop default
``` ```
#### To install the latest version on any distro: #### To install the latest version on any distro:
```bash ```bash
$ git clone https://github.com/hakavlad/nohang.git && cd nohang git clone https://github.com/hakavlad/nohang.git && cd nohang
$ sudo make install sudo make install
``` ```
Config files will be located in `/usr/local/etc/nohang/`. To enable and start unit without GUI notifications: Config files will be located in `/usr/local/etc/nohang/`. To enable and start unit without GUI notifications:
```bash ```bash
$ sudo systemctl enable --now nohang.service sudo systemctl enable --now nohang.service
``` ```
To enable and start unit with GUI notifications: To enable and start unit with GUI notifications:
```bash ```bash
$ sudo systemctl enable --now nohang-desktop.service sudo systemctl enable --now nohang-desktop.service
``` ```
On systems with OpenRC: On systems with OpenRC:
```bash ```bash
$ sudo make install-openrc sudo make install-openrc
``` ```
To uninstall: To uninstall:
```bash ```bash
$ sudo make uninstall sudo make uninstall
``` ```
## Command line options ## Command line options
@ -408,11 +406,11 @@ Process with highest badness (found in 55 ms):
To view the latest entries in the log (for systemd users): To view the latest entries in the log (for systemd users):
```bash ```bash
$ sudo journalctl -eu nohang.service sudo journalctl -eu nohang.service
#### or #### or
$ sudo journalctl -eu nohang-desktop.service sudo journalctl -eu nohang-desktop.service
``` ```
You can also enable `separate_log` in the config to logging in `/var/log/nohang/nohang.log`. You can also enable `separate_log` in the config to logging in `/var/log/nohang/nohang.log`.
@ -424,7 +422,7 @@ You can also enable `separate_log` in the config to logging in `/var/log/nohang/
Usage: Usage:
```bash ```bash
$ oom-sort oom-sort
``` ```
<details> <details>

View File

@ -7,7 +7,7 @@ from time import sleep, monotonic
from operator import itemgetter from operator import itemgetter
from sys import stdout, stderr, argv, exit from sys import stdout, stderr, argv, exit
from re import search from re import search
from sre_constants import error as invalid_re from re import error as invalid_re
from signal import signal, SIGKILL, SIGTERM, SIGINT, SIGQUIT, SIGHUP, SIGUSR1 from signal import signal, SIGKILL, SIGTERM, SIGINT, SIGQUIT, SIGHUP, SIGUSR1
@ -165,41 +165,49 @@ def memload():
os.kill(self_pid, SIGUSR1) os.kill(self_pid, SIGUSR1)
def arcstats(): def parse_zfs_arcstats():
""" """
Parses '/proc/spl/kstat/zfs/arcstats'.
Returns a dictionary with 'name' as keys and 'data' as values.
""" """
with open(arcstats_path, 'rb') as f: parsed_data = {}
a_list = f.read().decode().split('\n')
for n, line in enumerate(a_list): with open(arcstats_path, 'r') as as_file:
if n == c_min_index: lines = iter(as_file.readlines())
c_min = int(line.rpartition(' ')[2]) / 1024
elif n == size_index:
size = int(line.rpartition(' ')[2]) / 1024
elif n == arc_meta_used_index: # consume lines until the header row:
arc_meta_used = int(line.rpartition(' ')[2]) / 1024 for line in lines:
if 'name' in line and 'data' in line:
break
elif n == arc_meta_min_index: # Continue iterating over the remaining lines
arc_meta_min = int(line.rpartition(' ')[2]) / 1024 for line in lines:
if line.strip():
parts = line.split()
name = parts[0]
data_type = parts[1]
data = parts[2]
if data_type == '4':
data = int(data)
parsed_data[name] = data
else: return parsed_data
continue
c_rec = size - c_min
if c_rec < 0: def zfs_arc_available():
c_rec = 0 """returns how many KiB of the zfs ARC are reclaimable"""
stats = parse_zfs_arcstats()
meta_rec = arc_meta_used - arc_meta_min c_rec = max(stats['size'] - stats['c_min'], 0)
if meta_rec < 0: # old zfs: consider arc_meta_used, arc_meta_min
meta_rec = 0 if 'arc_meta_used' in stats and 'arc_meta_min' in stats:
zfs_available = c_rec + meta_rec meta_rec = max(stats['arc_meta_used'] - stats['arc_meta_min'], 0)
return (c_rec + meta_rec) / 1024
# return c_min, size, arc_meta_used, arc_meta_min, zfs_available # new zfs: metadata is no longer accounted for separately,
# https://github.com/openzfs/zfs/commit/a8d83e2a24de6419dc58d2a7b8f38904985726cb
return zfs_available return c_rec / 1024
def exe(cmd): def exe(cmd):
@ -209,7 +217,7 @@ def exe(cmd):
cmd_num_dict['cmd_num'] += 1 cmd_num_dict['cmd_num'] += 1
cmd_num = cmd_num_dict['cmd_num'] cmd_num = cmd_num_dict['cmd_num']
th_name = threading.current_thread().getName() th_name = threading.current_thread().name
log('Executing Command-{} {} with timeout {}s in {}'.format( log('Executing Command-{} {} with timeout {}s in {}'.format(
cmd_num, cmd_num,
@ -237,11 +245,11 @@ def start_thread(func, *a, **k):
""" run function in a new thread """ run function in a new thread
""" """
th = threading.Thread(target=func, args=a, kwargs=k, daemon=True) th = threading.Thread(target=func, args=a, kwargs=k, daemon=True)
th_name = th.getName() th_name = th.name
if debug_threading: if debug_threading:
log('Starting {} from {}'.format( log('Starting {} from {}'.format(
th_name, threading.current_thread().getName() th_name, threading.current_thread().name
)) ))
try: try:
@ -350,7 +358,7 @@ def pop(cmd):
else: else:
wait_time = 30 wait_time = 30
th_name = threading.current_thread().getName() th_name = threading.current_thread().name
log('Executing Command-{} {} with timeout {}s in {}'.format( log('Executing Command-{} {} with timeout {}s in {}'.format(
cmd_num, cmd_num,
@ -1341,7 +1349,7 @@ def check_mem_and_swap():
sf = int(m_list[swap_free_index].split(':')[1]) sf = int(m_list[swap_free_index].split(':')[1])
if ZFS: if ZFS:
ma += arcstats() ma += zfs_arc_available()
return ma, st, sf return ma, st, sf
@ -1369,7 +1377,7 @@ def meminfo():
md['available'] = mem_available md['available'] = mem_available
if ZFS: if ZFS:
z = arcstats() z = zfs_arc_available()
mem_available += z mem_available += z
md['shared'] = shmem md['shared'] = shmem
@ -3695,7 +3703,7 @@ if 'max_victim_ancestry_depth' in config_dict:
errprint('Invalid max_victim_ancestry_depth value, not integer\nExit') errprint('Invalid max_victim_ancestry_depth value, not integer\nExit')
exit(1) exit(1)
if max_victim_ancestry_depth < 1: if max_victim_ancestry_depth < 1:
errprint('Invalud max_victim_ancestry_depth value\nExit') errprint('Invalid max_victim_ancestry_depth value\nExit')
exit(1) exit(1)
else: else:
missing_config_key('max_victim_ancestry_depth') missing_config_key('max_victim_ancestry_depth')
@ -3958,29 +3966,6 @@ if check_kmsg:
if ZFS: if ZFS:
log('WARNING: ZFS found. Available memory will not be calculated ' log('WARNING: ZFS found. Available memory will not be calculated '
'correctly (issue#89)') 'correctly (issue#89)')
try:
# find indexes
with open(arcstats_path, 'rb') as f:
a_list = f.read().decode().split('\n')
for n, line in enumerate(a_list):
if line.startswith('c_min '):
c_min_index = n
elif line.startswith('size '):
size_index = n
elif line.startswith('arc_meta_used '):
arc_meta_used_index = n
elif line.startswith('arc_meta_min '):
arc_meta_min_index = n
else:
continue
except Exception as e:
log(e)
ZFS = False
while True: while True: