zram-advisor 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of zram-advisor might be problematic. Click here for more details.
- zram_advisor-1.0.0/.gitignore +2 -0
- zram_advisor-1.0.0/LICENSE +21 -0
- zram_advisor-1.0.0/PKG-INFO +116 -0
- zram_advisor-1.0.0/README.md +98 -0
- zram_advisor-1.0.0/pyproject.toml +69 -0
- zram_advisor-1.0.0/zram_advisor/__init__.py +4 -0
- zram_advisor-1.0.0/zram_advisor/fix-zram.sh +271 -0
- zram_advisor-1.0.0/zram_advisor/random-sites.txt +54 -0
- zram_advisor-1.0.0/zram_advisor/zram_advisor.py +411 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 Joe D
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: zram-advisor
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: zRAM status and setup tools
|
|
5
|
+
Project-URL: Homepage, https://github.com/joedefen/zram-advisor
|
|
6
|
+
Project-URL: Bug Tracker, https://github.com/joedefen/zram-advisor/issues
|
|
7
|
+
Author-email: Joe Defen <joedef@gmail.com>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: zram,zram service
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Requires-Python: >=3.8
|
|
15
|
+
Requires-Dist: importlib-metadata; python_version < '3.8'
|
|
16
|
+
Requires-Dist: requests
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
|
|
19
|
+
> **Quick-start**:
|
|
20
|
+
* If running python 5.11+, install `pipx`, and install with `pipx install zram-advisor`
|
|
21
|
+
* else install `pip`, and install with `pip install zram-advisor --user`
|
|
22
|
+
* `zram-advisor` - reports on currently running zRAM
|
|
23
|
+
* `zram-advisor --setup` - configure/starts zRAM w defaults on system w/o rRAM already configured.
|
|
24
|
+
|
|
25
|
+
# zram-advisor: Check/Setup/Test zRAM Tool
|
|
26
|
+
`zram-advisor` can:
|
|
27
|
+
* Check on your running zRAM and report ill advised settings and zRAM effectiveness.
|
|
28
|
+
* Install `fix-zram` which can setup your zRAM and/or reload it with different parameters (e.g., for testing).
|
|
29
|
+
* Provide a browser bookmark file to be imported to help testing your settings.
|
|
30
|
+
|
|
31
|
+
## Checking Your Running zRAM
|
|
32
|
+
#### Checking with zram-advisor
|
|
33
|
+
Here is the sample output of `zram-advisor` w/o arguments on a system with zRAM running:
|
|
34
|
+
```
|
|
35
|
+
$ zram-advisor
|
|
36
|
+
Distro : Linux Mint 21.3
|
|
37
|
+
180 : vm.swappiness.................. in [150, 200]
|
|
38
|
+
0 : vm.watermark_boost_factor...... in [0, 0]
|
|
39
|
+
125 : vm.watermark_scale_factor...... in [125, 125]
|
|
40
|
+
0 : vm.page-cluster................ in [0, 0]
|
|
41
|
+
1.6G : zRAM.disksize.................. >= 1.4G
|
|
42
|
+
================ 410s ================
|
|
43
|
+
952.4M : Total Memory eTotal=2.2G/239%
|
|
44
|
+
830.2M/87% : Used eUsed=1.4G/155%
|
|
45
|
+
122.3M/13% : Available eAvail=800.7M/84%
|
|
46
|
+
zram0:
|
|
47
|
+
uncmpr: 814.5M limit=1.6G
|
|
48
|
+
cmpr: 158.5M/17% factor=5.14
|
|
49
|
+
RAM: 165.5M/17% most=240M/25% limit=0
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
* The top section show key parameters for zRAM and suggested ranges (if it did not like those it would preface the range with "NOT in")
|
|
53
|
+
* The midsection shows traditional key memory stats on the left, and on the right, the "effective values":
|
|
54
|
+
* **eUsed**: is the amount of memory used if the compressed part in zRAM were expanded; than number is "true".
|
|
55
|
+
* **eTotal** and **eAvail** are projected numbers based on the current compression ratio; these number are more accurate as the zRAM memory footprint grows increases.
|
|
56
|
+
* The lower section are stats for each zram device .. typically, there is just one.
|
|
57
|
+
* **uncmpr**: is the amount of "original" memory stored by zRAM; its limit is officially called 'disksize' which is the name/value you see from `zramctl`.
|
|
58
|
+
* **RAM**: is the amount of physical RAM consumed by zRAM including overhead; **most** it the largest RAM used since boot.
|
|
59
|
+
|
|
60
|
+
#### Checking with pmemstat
|
|
61
|
+
Another app (installable with `pipx` or `pip`) is `pmemstat``. The top of it sample output (on the same system as above) was:
|
|
62
|
+
```
|
|
63
|
+
14:41:39 Tot=952.4M Used=741.5M Avail=210.9M Oth=0 Sh+Tmp=8.7M PIDs=122
|
|
64
|
+
0.6/ker zRAM=210.2M eTot:2.2G/240% eUsed:1.5G/166% eAvail:705.7M/74%
|
|
65
|
+
cpu_pct pswap other data ptotal key/info (exe by mem)
|
|
66
|
+
9.8 945 100 195 1,239 T 122x --TOTALS in MB --
|
|
67
|
+
───────────────────────────────────────────────────────────────────────────
|
|
68
|
+
3.3 396 65 106 567 23x chromium
|
|
69
|
+
```
|
|
70
|
+
* You can see those same "effective" key memory stats, plus you can see kernel cpu% (i.e., the `0.6/ker`). Kernal CPU (most the swap process) can be significant, and that CPU cost is the primary "cost" of using zRAM).
|
|
71
|
+
|
|
72
|
+
## zram-advisor Options
|
|
73
|
+
```
|
|
74
|
+
usage: zram-advisor [-h] [-s] [-d] [-t] [--DB]
|
|
75
|
+
options:
|
|
76
|
+
-s, --setup-fix-zram install "fix-zram" program and start zRAM
|
|
77
|
+
-d, --dump-fix-zram print "fix-zram.sh" for manual install
|
|
78
|
+
-t, --gen-test-sites print "bookmarks.html" to import to a web-browser for load test
|
|
79
|
+
```
|
|
80
|
+
* **--setup-fix-zram** installs a programs `fix-ram` and creates a service called `fix-zram-init` to run it on boot.
|
|
81
|
+
* **--dump-fix-zram** prints the stock `fix-zram.sh` (e.g., so you can modify it) and install your modified script by running it (e.g., `bash my-fix-zram.sh`).
|
|
82
|
+
* **--gen-test-sites** prints a .html that can be imported into most browsers which creates folders of sites that can be opened to create typical memory demands (of browsers at least).
|
|
83
|
+
|
|
84
|
+
**Note:** do not install `fix-zram` if w/o uninstalling any competing tool to configure zRAM.
|
|
85
|
+
|
|
86
|
+
## Controlling zRAM with fix-zram
|
|
87
|
+
`fix-zram.sh` is bash script bundled with `zram-advisor`. Its usage is:
|
|
88
|
+
```
|
|
89
|
+
fix-zram [--(load|reload|unload|setup|unsetup)] [-n|--dry-run] [-cN] [N.Nx] [Nm|Ng]
|
|
90
|
+
where:
|
|
91
|
+
--{command} defaults to 'load' but can be one of:
|
|
92
|
+
load - load/start zRAM with given params or their defaults
|
|
93
|
+
reload - remove any existing zRAM and load zram
|
|
94
|
+
unload - unloads any existing zRAM
|
|
95
|
+
setup - copy fix-zram to '/usr/local/bin' and setup service [dflt=no]
|
|
96
|
+
unsetup - remove '/usr/local/bin/fix-zram' and remove service [dflt=no]
|
|
97
|
+
-n,--dry-run - only print commands that would be executed
|
|
98
|
+
-c{integer} - set number of zram devices
|
|
99
|
+
{float}x - set zram-size to {float} * ram at most [dflt=1.75]
|
|
100
|
+
{integer}m - set gross zram-size to {integer} megabytes at most [dflt=12288m]
|
|
101
|
+
{integer}g - set gross zram-size to {integer} gigabytes at most
|
|
102
|
+
```
|
|
103
|
+
### fix-zram Run-Time Commands
|
|
104
|
+
`load`, `reload`, and `unload` affect the running system and any effect do not survive reboot. So, these can be used for testing (or initializing) zRAM.
|
|
105
|
+
* `load` and `reload` set the `vm.*` parameters shown by `vram-advisor` (but they do not unset them although a reboot will do that).
|
|
106
|
+
* `unload` and `reload` remove preexisting zRAM if running. Removal only works if all memory stored in zRAM can be placed in memory or another storage device.
|
|
107
|
+
|
|
108
|
+
Typical use:
|
|
109
|
+
* `fix-zram reload 3x 12g` - will unload the current zRAM (if exists and possible), and then install zRAM with sized at the minimum of 3xRAM and 12GB.
|
|
110
|
+
|
|
111
|
+
### fix-zram Setup Methods
|
|
112
|
+
* `fix-zram --setup` - installs `fix-zram` and creates a `zram-init-fix` service which will load zRAM per the defaults on each load with default values.
|
|
113
|
+
* `fix-zram --unsetup` - removes the installed `fix-zram` and removes the `zram-init-fix` service.
|
|
114
|
+
|
|
115
|
+
Typical use:
|
|
116
|
+
* `fix-zram setup 3x 12g` - installs a zRAM init service that start zRAM sized at the minimum of 3xRAM and 12GB on boot.
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
> **Quick-start**:
|
|
2
|
+
* If running python 5.11+, install `pipx`, and install with `pipx install zram-advisor`
|
|
3
|
+
* else install `pip`, and install with `pip install zram-advisor --user`
|
|
4
|
+
* `zram-advisor` - reports on currently running zRAM
|
|
5
|
+
* `zram-advisor --setup` - configure/starts zRAM w defaults on system w/o rRAM already configured.
|
|
6
|
+
|
|
7
|
+
# zram-advisor: Check/Setup/Test zRAM Tool
|
|
8
|
+
`zram-advisor` can:
|
|
9
|
+
* Check on your running zRAM and report ill advised settings and zRAM effectiveness.
|
|
10
|
+
* Install `fix-zram` which can setup your zRAM and/or reload it with different parameters (e.g., for testing).
|
|
11
|
+
* Provide a browser bookmark file to be imported to help testing your settings.
|
|
12
|
+
|
|
13
|
+
## Checking Your Running zRAM
|
|
14
|
+
#### Checking with zram-advisor
|
|
15
|
+
Here is the sample output of `zram-advisor` w/o arguments on a system with zRAM running:
|
|
16
|
+
```
|
|
17
|
+
$ zram-advisor
|
|
18
|
+
Distro : Linux Mint 21.3
|
|
19
|
+
180 : vm.swappiness.................. in [150, 200]
|
|
20
|
+
0 : vm.watermark_boost_factor...... in [0, 0]
|
|
21
|
+
125 : vm.watermark_scale_factor...... in [125, 125]
|
|
22
|
+
0 : vm.page-cluster................ in [0, 0]
|
|
23
|
+
1.6G : zRAM.disksize.................. >= 1.4G
|
|
24
|
+
================ 410s ================
|
|
25
|
+
952.4M : Total Memory eTotal=2.2G/239%
|
|
26
|
+
830.2M/87% : Used eUsed=1.4G/155%
|
|
27
|
+
122.3M/13% : Available eAvail=800.7M/84%
|
|
28
|
+
zram0:
|
|
29
|
+
uncmpr: 814.5M limit=1.6G
|
|
30
|
+
cmpr: 158.5M/17% factor=5.14
|
|
31
|
+
RAM: 165.5M/17% most=240M/25% limit=0
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
* The top section show key parameters for zRAM and suggested ranges (if it did not like those it would preface the range with "NOT in")
|
|
35
|
+
* The midsection shows traditional key memory stats on the left, and on the right, the "effective values":
|
|
36
|
+
* **eUsed**: is the amount of memory used if the compressed part in zRAM were expanded; than number is "true".
|
|
37
|
+
* **eTotal** and **eAvail** are projected numbers based on the current compression ratio; these number are more accurate as the zRAM memory footprint grows increases.
|
|
38
|
+
* The lower section are stats for each zram device .. typically, there is just one.
|
|
39
|
+
* **uncmpr**: is the amount of "original" memory stored by zRAM; its limit is officially called 'disksize' which is the name/value you see from `zramctl`.
|
|
40
|
+
* **RAM**: is the amount of physical RAM consumed by zRAM including overhead; **most** it the largest RAM used since boot.
|
|
41
|
+
|
|
42
|
+
#### Checking with pmemstat
|
|
43
|
+
Another app (installable with `pipx` or `pip`) is `pmemstat``. The top of it sample output (on the same system as above) was:
|
|
44
|
+
```
|
|
45
|
+
14:41:39 Tot=952.4M Used=741.5M Avail=210.9M Oth=0 Sh+Tmp=8.7M PIDs=122
|
|
46
|
+
0.6/ker zRAM=210.2M eTot:2.2G/240% eUsed:1.5G/166% eAvail:705.7M/74%
|
|
47
|
+
cpu_pct pswap other data ptotal key/info (exe by mem)
|
|
48
|
+
9.8 945 100 195 1,239 T 122x --TOTALS in MB --
|
|
49
|
+
───────────────────────────────────────────────────────────────────────────
|
|
50
|
+
3.3 396 65 106 567 23x chromium
|
|
51
|
+
```
|
|
52
|
+
* You can see those same "effective" key memory stats, plus you can see kernel cpu% (i.e., the `0.6/ker`). Kernal CPU (most the swap process) can be significant, and that CPU cost is the primary "cost" of using zRAM).
|
|
53
|
+
|
|
54
|
+
## zram-advisor Options
|
|
55
|
+
```
|
|
56
|
+
usage: zram-advisor [-h] [-s] [-d] [-t] [--DB]
|
|
57
|
+
options:
|
|
58
|
+
-s, --setup-fix-zram install "fix-zram" program and start zRAM
|
|
59
|
+
-d, --dump-fix-zram print "fix-zram.sh" for manual install
|
|
60
|
+
-t, --gen-test-sites print "bookmarks.html" to import to a web-browser for load test
|
|
61
|
+
```
|
|
62
|
+
* **--setup-fix-zram** installs a programs `fix-ram` and creates a service called `fix-zram-init` to run it on boot.
|
|
63
|
+
* **--dump-fix-zram** prints the stock `fix-zram.sh` (e.g., so you can modify it) and install your modified script by running it (e.g., `bash my-fix-zram.sh`).
|
|
64
|
+
* **--gen-test-sites** prints a .html that can be imported into most browsers which creates folders of sites that can be opened to create typical memory demands (of browsers at least).
|
|
65
|
+
|
|
66
|
+
**Note:** do not install `fix-zram` if w/o uninstalling any competing tool to configure zRAM.
|
|
67
|
+
|
|
68
|
+
## Controlling zRAM with fix-zram
|
|
69
|
+
`fix-zram.sh` is bash script bundled with `zram-advisor`. Its usage is:
|
|
70
|
+
```
|
|
71
|
+
fix-zram [--(load|reload|unload|setup|unsetup)] [-n|--dry-run] [-cN] [N.Nx] [Nm|Ng]
|
|
72
|
+
where:
|
|
73
|
+
--{command} defaults to 'load' but can be one of:
|
|
74
|
+
load - load/start zRAM with given params or their defaults
|
|
75
|
+
reload - remove any existing zRAM and load zram
|
|
76
|
+
unload - unloads any existing zRAM
|
|
77
|
+
setup - copy fix-zram to '/usr/local/bin' and setup service [dflt=no]
|
|
78
|
+
unsetup - remove '/usr/local/bin/fix-zram' and remove service [dflt=no]
|
|
79
|
+
-n,--dry-run - only print commands that would be executed
|
|
80
|
+
-c{integer} - set number of zram devices
|
|
81
|
+
{float}x - set zram-size to {float} * ram at most [dflt=1.75]
|
|
82
|
+
{integer}m - set gross zram-size to {integer} megabytes at most [dflt=12288m]
|
|
83
|
+
{integer}g - set gross zram-size to {integer} gigabytes at most
|
|
84
|
+
```
|
|
85
|
+
### fix-zram Run-Time Commands
|
|
86
|
+
`load`, `reload`, and `unload` affect the running system and any effect do not survive reboot. So, these can be used for testing (or initializing) zRAM.
|
|
87
|
+
* `load` and `reload` set the `vm.*` parameters shown by `vram-advisor` (but they do not unset them although a reboot will do that).
|
|
88
|
+
* `unload` and `reload` remove preexisting zRAM if running. Removal only works if all memory stored in zRAM can be placed in memory or another storage device.
|
|
89
|
+
|
|
90
|
+
Typical use:
|
|
91
|
+
* `fix-zram reload 3x 12g` - will unload the current zRAM (if exists and possible), and then install zRAM with sized at the minimum of 3xRAM and 12GB.
|
|
92
|
+
|
|
93
|
+
### fix-zram Setup Methods
|
|
94
|
+
* `fix-zram --setup` - installs `fix-zram` and creates a `zram-init-fix` service which will load zRAM per the defaults on each load with default values.
|
|
95
|
+
* `fix-zram --unsetup` - removes the installed `fix-zram` and removes the `zram-init-fix` service.
|
|
96
|
+
|
|
97
|
+
Typical use:
|
|
98
|
+
* `fix-zram setup 3x 12g` - installs a zRAM init service that start zRAM sized at the minimum of 3xRAM and 12GB on boot.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# HOW TO PUBLISH...
|
|
2
|
+
#
|
|
3
|
+
# Prep Work (ensure need modules are up-to-date):
|
|
4
|
+
# sudo pacman -Syu python-pip
|
|
5
|
+
# sudo pacman -Syu python-build
|
|
6
|
+
# sudo pacman -Syu python-twine
|
|
7
|
+
# -- antiquated way
|
|
8
|
+
# python3 -m pip install --upgrade pip
|
|
9
|
+
# python3 -m pip install --upgrade build
|
|
10
|
+
# python3 -m pip install --upgrade twine
|
|
11
|
+
#
|
|
12
|
+
# PUBLIC Build and deploy (from project directory):
|
|
13
|
+
# ## BUMP the version (below in [project])
|
|
14
|
+
# rm -r dist; python3 -m build; ls dist/.
|
|
15
|
+
# python3 -m twine upload dist/*
|
|
16
|
+
# ## Enter __token__ and the saved TOKEN (in bitwarden)
|
|
17
|
+
# pipx upgrade zram-advisor || pipx install zram-advisor # >= python3.11
|
|
18
|
+
# --OR-- sudo python3 -m pip install zram-advisor # <= python3.10
|
|
19
|
+
# ## VISIT https://pypi.org/project/zram-advisor and delete old versions
|
|
20
|
+
#
|
|
21
|
+
# TEST Build and test (from project directory):
|
|
22
|
+
# ## BUMP the version (below in [project])
|
|
23
|
+
# rm -r dist; python3 -m build
|
|
24
|
+
# python3 -m twine upload --repository testpypi dist/*
|
|
25
|
+
# ## Enter __token__ and the saved TOKEN (in bitwarden)
|
|
26
|
+
# pip install --upgrade --index-url https://test.pypi.org/simple/ --no-deps --user --break-system-packages zram-advisor
|
|
27
|
+
# pip uninstall --break-system-packages zram-advisor
|
|
28
|
+
# ## VISIT https://test.pypi.org/project/zram-advisor and delete old versions
|
|
29
|
+
|
|
30
|
+
[build-system]
|
|
31
|
+
requires = ["hatchling"]
|
|
32
|
+
build-backend = "hatchling.build"
|
|
33
|
+
|
|
34
|
+
[project]
|
|
35
|
+
version = "1.0.0"
|
|
36
|
+
name = "zram-advisor"
|
|
37
|
+
authors = [
|
|
38
|
+
{name = "Joe Defen", email = "joedef@gmail.com"},
|
|
39
|
+
]
|
|
40
|
+
description = "zRAM status and setup tools"
|
|
41
|
+
readme = "README.md"
|
|
42
|
+
requires-python = ">=3.8"
|
|
43
|
+
keywords = ["zram", "zram service"]
|
|
44
|
+
license = "MIT"
|
|
45
|
+
classifiers = [
|
|
46
|
+
"Programming Language :: Python :: 3",
|
|
47
|
+
"License :: OSI Approved :: MIT License",
|
|
48
|
+
"Operating System :: POSIX :: Linux"
|
|
49
|
+
]
|
|
50
|
+
dependencies = [
|
|
51
|
+
"requests",
|
|
52
|
+
'importlib-metadata; python_version<"3.8"',
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
[project.scripts]
|
|
56
|
+
zram-advisor = "zram_advisor.zram_advisor:run"
|
|
57
|
+
|
|
58
|
+
[tool.hatch.build.targets.wheel]
|
|
59
|
+
packages = ["zram_advisor"]
|
|
60
|
+
|
|
61
|
+
[tool.hatch.build.targets.sdist]
|
|
62
|
+
exclude = [
|
|
63
|
+
"__pycache__",
|
|
64
|
+
"zram_advisor/__pycache__",
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
[project.urls]
|
|
68
|
+
"Homepage" = "https://github.com/joedefen/zram-advisor"
|
|
69
|
+
"Bug Tracker" = "https://github.com/joedefen/zram-advisor/issues"
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
#
|
|
3
|
+
# default kernel settings
|
|
4
|
+
swappiness=180
|
|
5
|
+
watermark_boost_factor=0
|
|
6
|
+
watermark_scale_factor=125
|
|
7
|
+
page_cluster=0
|
|
8
|
+
priority=100
|
|
9
|
+
# default options to this script
|
|
10
|
+
factor=1.75
|
|
11
|
+
capM=$((12*1024)) # the default cap on zRAM
|
|
12
|
+
count=1
|
|
13
|
+
dry_run=no
|
|
14
|
+
command=start
|
|
15
|
+
|
|
16
|
+
usage() {
|
|
17
|
+
echo "$@"
|
|
18
|
+
cat <<END
|
|
19
|
+
USE: fix-zram [--(load|reload|unload|setup|unsetup)] [-n|--dry-run] [-cN] [N.Nx] [Nm|Ng]
|
|
20
|
+
where:
|
|
21
|
+
--{command} defaults to 'load' but can be one of:
|
|
22
|
+
load - load/start zRAM with given params or their defaults
|
|
23
|
+
reload - remove any existing zRAM and load zram
|
|
24
|
+
unload - unloads any existing zRAM
|
|
25
|
+
setup - copy fix-zram to '/usr/local/bin' and setup service
|
|
26
|
+
unsetup - remove '/usr/local/bin/fix-zram' and remove service
|
|
27
|
+
-n,--dry-run - only print commands that would be executed
|
|
28
|
+
-c{integer} - set number of zram devices
|
|
29
|
+
{float}x - set zram-size to {float} * ram at most [dflt=$factor]
|
|
30
|
+
{integer}m - set gross zram-size to {integer} megabytes at most [dflt=${capM}m]
|
|
31
|
+
{integer}g - set gross zram-size to {integer} gigabytes at most
|
|
32
|
+
END
|
|
33
|
+
exit 1
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
########################################################
|
|
37
|
+
# restart this program if not root
|
|
38
|
+
if [[ $(id --user) != 0 ]]; then
|
|
39
|
+
script_tmp_dir=$(realpath "$0")
|
|
40
|
+
exec sudo bash "${script_tmp_dir}" "$@"
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
########################################################
|
|
44
|
+
# Iterate over command-line arguments
|
|
45
|
+
shopt -s extglob # permit patterns like *(-)
|
|
46
|
+
for arg in "$@"; do
|
|
47
|
+
case "$arg" in
|
|
48
|
+
[0-9]*[xX]|[0-9]*.[0-9]*[xX])
|
|
49
|
+
factor="${arg%?}"
|
|
50
|
+
;;
|
|
51
|
+
[0-9]*[mM])
|
|
52
|
+
capM="${arg%?}"
|
|
53
|
+
;;
|
|
54
|
+
[0-9]*[gG])
|
|
55
|
+
capG="${arg%?}"
|
|
56
|
+
capM=$((capG*1024))
|
|
57
|
+
;;
|
|
58
|
+
*(-)c[0-9]*)
|
|
59
|
+
count="${arg##*c}"
|
|
60
|
+
;;
|
|
61
|
+
*(-)load) command=load ;;
|
|
62
|
+
*(-)reload) command=reload ;;
|
|
63
|
+
*(-)unload) command=unload ;;
|
|
64
|
+
*(-)setup) command=setup ;;
|
|
65
|
+
*(-)unsetup) command=unsetup ;;
|
|
66
|
+
-n|--dry-run) dry_run=yes ;;
|
|
67
|
+
-h|--help|-\?) usage ;;
|
|
68
|
+
*) usage "Unknown option: $arg" ;;
|
|
69
|
+
esac
|
|
70
|
+
done
|
|
71
|
+
|
|
72
|
+
########################################################
|
|
73
|
+
# Calculations verification
|
|
74
|
+
ramM=$(free -m | awk '/^Mem:/ {print $2}')
|
|
75
|
+
zram_sizeM=$(python3 -c "print(int(round(${factor} * ${ramM})))")
|
|
76
|
+
echo factor=$factor cap=$capM ramM=$ramM zram_size=$zram_sizeM
|
|
77
|
+
if (( capM < zram_sizeM )); then
|
|
78
|
+
zram_sizeM="${capM}"
|
|
79
|
+
fi
|
|
80
|
+
echo OPTS: $command dry-run=$dry_run count=$count factor=$factor capM=$capM
|
|
81
|
+
echo INFO: ramM=$ramM zram_sizeM=$zram_sizeM
|
|
82
|
+
|
|
83
|
+
########################################################
|
|
84
|
+
# run a command (or don't)
|
|
85
|
+
run() {
|
|
86
|
+
if [[ $dry_run == "yes" ]]; then
|
|
87
|
+
echo 'WOULD ++' "$@"
|
|
88
|
+
else
|
|
89
|
+
echo '++' "$@"
|
|
90
|
+
eval "$@"
|
|
91
|
+
fi
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
########################################################
|
|
95
|
+
# unload zram
|
|
96
|
+
unload_zram() {
|
|
97
|
+
found=no
|
|
98
|
+
shopt -s nullglob
|
|
99
|
+
devs=(/dev/zram*)
|
|
100
|
+
for dev in "${devs[@]}"; do
|
|
101
|
+
run "swapoff ${dev}"
|
|
102
|
+
found=yes
|
|
103
|
+
done
|
|
104
|
+
shopt -u nullglob
|
|
105
|
+
run 'modprobe -r zram'
|
|
106
|
+
if [[ $found == "no" ]]; then
|
|
107
|
+
echo 'NOTE: zram already unloaded'
|
|
108
|
+
fi
|
|
109
|
+
run "zramctl"
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
########################################################
|
|
113
|
+
# initialize zram
|
|
114
|
+
load_zram() {
|
|
115
|
+
shopt -s nullglob
|
|
116
|
+
devs=(/dev/zram*)
|
|
117
|
+
for dev in "${devs[@]}"; do
|
|
118
|
+
if [[ $command == 'reload' ]]; then
|
|
119
|
+
unload_zram
|
|
120
|
+
else
|
|
121
|
+
echo "ERROR: ${dev} exists; use --reload to first unload then load"
|
|
122
|
+
exit -1
|
|
123
|
+
fi
|
|
124
|
+
break
|
|
125
|
+
done
|
|
126
|
+
shopt -u nullglob
|
|
127
|
+
|
|
128
|
+
# unload zswap just in case
|
|
129
|
+
run "echo 0 > /sys/module/zswap/parameters/enabled"
|
|
130
|
+
|
|
131
|
+
run "modprobe zram num_devices=${count}"
|
|
132
|
+
sizeM=$(python3 -c "print(int(round(${zram_sizeM} / ${count} )))")
|
|
133
|
+
for (( ii=0; ii<count; ii++ )); do
|
|
134
|
+
run "echo zstd > /sys/block/zram${ii}/comp_algorithm"
|
|
135
|
+
run "echo ${sizeM}M > /sys/block/zram${ii}/disksize"
|
|
136
|
+
run "echo 0 > /sys/block/zram${ii}/mem_limit"
|
|
137
|
+
done
|
|
138
|
+
|
|
139
|
+
run "sysctl -w vm.swappiness=${swappiness} >/dev/null"
|
|
140
|
+
run "sysctl -w vm.watermark_boost_factor=${watermark_boost_factor} > /dev/null"
|
|
141
|
+
run "sysctl -w vm.watermark_scale_factor=${watermark_scale_factor} >/dev/null"
|
|
142
|
+
run "sysctl -w vm.page-cluster=${page_cluster} >/dev/null"
|
|
143
|
+
|
|
144
|
+
for (( ii=0; ii<count; ii++ )); do
|
|
145
|
+
run mkswap /dev/zram${ii}
|
|
146
|
+
run swapon --priority ${priority} /dev/zram${ii}
|
|
147
|
+
done
|
|
148
|
+
run "zramctl"
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
########################################################
|
|
153
|
+
# systemd service setup
|
|
154
|
+
setup_systemd() {
|
|
155
|
+
dest="/etc/systemd/system/fix-zram.service"
|
|
156
|
+
service=$(basename "$dest")
|
|
157
|
+
cat <<END > "${dest}"
|
|
158
|
+
#!/bin/sh
|
|
159
|
+
[Unit]
|
|
160
|
+
Description=Init zRAM on boot
|
|
161
|
+
|
|
162
|
+
[Service]
|
|
163
|
+
Type=simple
|
|
164
|
+
ExecStart=/bin/bash ${script_bin_path} -c${count} ${factor}x ${capM}M
|
|
165
|
+
|
|
166
|
+
[Install]
|
|
167
|
+
WantedBy=multi-user.target
|
|
168
|
+
END
|
|
169
|
+
|
|
170
|
+
run "chmod 644 '$dest'"
|
|
171
|
+
run "systemctl enable --now ${service}"
|
|
172
|
+
|
|
173
|
+
}
|
|
174
|
+
unsetup_systemd() {
|
|
175
|
+
dest="/etc/systemd/system/fix-zram.service"
|
|
176
|
+
service=$(basename "$dest")
|
|
177
|
+
if [ -f "$dest" ]; then
|
|
178
|
+
run "systemctl disable --now ${service}"
|
|
179
|
+
run "rm '$dest'"
|
|
180
|
+
fi
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
########################################################
|
|
184
|
+
# sysVinit service setup
|
|
185
|
+
setup_sysVinit() {
|
|
186
|
+
wrapper="/etc/init.d/fix-zram-init"
|
|
187
|
+
service=$(basename "${wrapper}")
|
|
188
|
+
|
|
189
|
+
cat <<END > "${wrapper}"
|
|
190
|
+
#!/bin/bash
|
|
191
|
+
### BEGIN INIT INFO
|
|
192
|
+
# Default-Start: 2 3 4 5
|
|
193
|
+
# Default-Stop:
|
|
194
|
+
### END INIT INFO
|
|
195
|
+
|
|
196
|
+
case "$1" in
|
|
197
|
+
start) sudo bash ${script_bin_path} load -c${count} ${factor}x ${capM}M;;
|
|
198
|
+
stop) sudo bash ${script_bin_path} --unload ;;
|
|
199
|
+
*) ;;
|
|
200
|
+
esac
|
|
201
|
+
exit 0
|
|
202
|
+
END
|
|
203
|
+
run "chmod 755 '$wrapper'"
|
|
204
|
+
|
|
205
|
+
if which chkconfig >/dev/null 2>&1; then
|
|
206
|
+
run "chkconfig --add ${service}"
|
|
207
|
+
elif which update-rc.d >/dev/null 2>&1; then
|
|
208
|
+
run "update-rc.d ${wrapper} defaults"
|
|
209
|
+
fi
|
|
210
|
+
if which service >/dev/null 2>&1; then
|
|
211
|
+
run "service $service enable"
|
|
212
|
+
run "service $service start"
|
|
213
|
+
elif which update-rc.d >/dev/null 2>&1; then
|
|
214
|
+
run "update-rd.d $service enable"
|
|
215
|
+
run "$wrapper start"
|
|
216
|
+
fi
|
|
217
|
+
|
|
218
|
+
}
|
|
219
|
+
unsetup_sysVinit() {
|
|
220
|
+
wrapper="/etc/init.d/fix-zram-init"
|
|
221
|
+
service=$(basename "${wrapper}")
|
|
222
|
+
if which service >/dev/null 2>&1; then
|
|
223
|
+
run "service ${service} stop"
|
|
224
|
+
run "service ${service} disable"
|
|
225
|
+
elif which update-rc.d >/dev/null 2>&1; then
|
|
226
|
+
run "${wrapper} stop"
|
|
227
|
+
run "update-rd.d ${service} disable"
|
|
228
|
+
fi
|
|
229
|
+
run "rm ${wrapper}"
|
|
230
|
+
if which update-rc.d >/dev/null 2>&1; then
|
|
231
|
+
run "update-rc.d -f ${service} remove"
|
|
232
|
+
fi
|
|
233
|
+
run "rm /var/log/${service}.log"
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
########################################################
|
|
237
|
+
# setup
|
|
238
|
+
setup() {
|
|
239
|
+
script_tmp_dir=$(realpath "$0")
|
|
240
|
+
script_bin_path="/usr/local/bin/fix-zram"
|
|
241
|
+
( run "cp '$script_tmp_dir' '$script_bin_path'" )
|
|
242
|
+
|
|
243
|
+
init_cmd=$(ps -p 1 -o comm=)
|
|
244
|
+
case "$init_cmd" in
|
|
245
|
+
init) ( setup_sysVinit ) ;;
|
|
246
|
+
systemd) ( setup_systemd ) ;;
|
|
247
|
+
*) echo unhandled init type "$init_cmd" ;;
|
|
248
|
+
esac
|
|
249
|
+
}
|
|
250
|
+
########################################################
|
|
251
|
+
# unsetup
|
|
252
|
+
unsetup() {
|
|
253
|
+
script_bin_path="/usr/local/bin/fix-zram"
|
|
254
|
+
init_cmd=$(ps -p 1 -o comm=)
|
|
255
|
+
case "$init_cmd" in
|
|
256
|
+
init) ( unsetup_sysVinit ) ;;
|
|
257
|
+
systemd) ( unsetup_systemd ) ;;
|
|
258
|
+
*) echo unhandled init type "$init_cmd" ;;
|
|
259
|
+
esac
|
|
260
|
+
rm $script_bin_path
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if [[ $command == 'setup' ]]; then
|
|
264
|
+
setup
|
|
265
|
+
elif [[ $command == 'unsetup' ]]; then
|
|
266
|
+
unsetup
|
|
267
|
+
elif [[ $command == 'unload' ]]; then
|
|
268
|
+
unload_zram
|
|
269
|
+
else
|
|
270
|
+
load_zram
|
|
271
|
+
fi
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
50waystogetajob.com
|
|
2
|
+
80000hours.org
|
|
3
|
+
www.teamblind.com
|
|
4
|
+
qz.com/work
|
|
5
|
+
brandyourself.com
|
|
6
|
+
www.payscale.com
|
|
7
|
+
getpocket.com
|
|
8
|
+
www.hubspot.com/resources/courses
|
|
9
|
+
internetingishard.netlify.app
|
|
10
|
+
teamtreehouse.com
|
|
11
|
+
www.shopify.com/guides
|
|
12
|
+
www.shopify.com/academy
|
|
13
|
+
www.ted.com
|
|
14
|
+
www.quora.com
|
|
15
|
+
www.blinkist.com
|
|
16
|
+
www.actionablebooks.com/en-ca/summaries
|
|
17
|
+
www.theschooloflife.com/thebookoflife
|
|
18
|
+
www.duolingo.com
|
|
19
|
+
zapier.com/learn
|
|
20
|
+
speaking.io
|
|
21
|
+
review.firstround.com
|
|
22
|
+
degreed.com
|
|
23
|
+
reviewmeta.com
|
|
24
|
+
fast.com
|
|
25
|
+
genius.com
|
|
26
|
+
www.padmapper.com
|
|
27
|
+
workflowy.com
|
|
28
|
+
www.grammarly.com
|
|
29
|
+
trello.com
|
|
30
|
+
www.rescuetime.com
|
|
31
|
+
www.crystalknows.com
|
|
32
|
+
cash.app
|
|
33
|
+
resumeworded.com
|
|
34
|
+
archive.org/web
|
|
35
|
+
discover.getrevue.co
|
|
36
|
+
ifttt.com
|
|
37
|
+
quotecatalog.com
|
|
38
|
+
www.insight-book.com/Quiz.aspx
|
|
39
|
+
ikonick.com
|
|
40
|
+
www.goodreads.com
|
|
41
|
+
weworkremotely.com
|
|
42
|
+
www.freelancewriting.com/jobs
|
|
43
|
+
authenticjobs.com
|
|
44
|
+
angel.co/jobs
|
|
45
|
+
startup.jobs
|
|
46
|
+
invoice-generator.com
|
|
47
|
+
www.hellobonsai.com
|
|
48
|
+
www.indiehackers.com
|
|
49
|
+
www.producthunt.com
|
|
50
|
+
discord.com
|
|
51
|
+
medium.com
|
|
52
|
+
www.patreon.com
|
|
53
|
+
stackexchange.com
|
|
54
|
+
www.thevectorimpact.com
|
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Program to analyze the zRAM configuration (if any) and suggest
|
|
5
|
+
some improvements.
|
|
6
|
+
|
|
7
|
+
===== References =====
|
|
8
|
+
www.kernel.org/doc/Documentation/blockdev/zram.txt
|
|
9
|
+
fedoraproject.org/wiki/Changes/Scale_ZRAM_to_full_memory_size
|
|
10
|
+
kernelnewbies.org/Linux_5.8 - when swappiness rose to 2020-08-02
|
|
11
|
+
|
|
12
|
+
Tested on:
|
|
13
|
+
- Linux Mint (Mate) 21.3 - 6.5 kernel (upgraded)
|
|
14
|
+
- Debian (KDE) 12.4.0
|
|
15
|
+
- Linux Lite 6.6 - 5.x kernel
|
|
16
|
+
- elementary OS 7.1 Horus
|
|
17
|
+
"""
|
|
18
|
+
# pylint: disable=invalid-name,multiple-statements,global-statement
|
|
19
|
+
# pylint: disable=too-many-instance-attributes,broad-exception-caught
|
|
20
|
+
|
|
21
|
+
import re
|
|
22
|
+
from types import SimpleNamespace
|
|
23
|
+
import shutil
|
|
24
|
+
import os
|
|
25
|
+
import sys
|
|
26
|
+
import configparser
|
|
27
|
+
import time
|
|
28
|
+
import argparse
|
|
29
|
+
import subprocess
|
|
30
|
+
|
|
31
|
+
##############################################################################
|
|
32
|
+
|
|
33
|
+
####################################################################################
|
|
34
|
+
|
|
35
|
+
class Term:
|
|
36
|
+
""" Escape sequences; e.g., see:
|
|
37
|
+
- https://en.wikipedia.org/wiki/ANSI_escape_code
|
|
38
|
+
- https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797#file-ansi-md
|
|
39
|
+
"""
|
|
40
|
+
esc = '\x1B'
|
|
41
|
+
# pylint: disable=missing-function-docstring,multiple-statements
|
|
42
|
+
@staticmethod
|
|
43
|
+
def erase_line(): return f'{Term.esc}[2K'
|
|
44
|
+
@staticmethod
|
|
45
|
+
def bold(): return f'{Term.esc}[1m'
|
|
46
|
+
@staticmethod
|
|
47
|
+
def reverse_video(): return f'{Term.esc}[7m'
|
|
48
|
+
@staticmethod
|
|
49
|
+
def normal_video(): return f'{Term.esc}[m'
|
|
50
|
+
@staticmethod
|
|
51
|
+
def pos_up(cnt): return f'{Term.esc}[{cnt}F' if cnt > 0 else ''
|
|
52
|
+
@staticmethod
|
|
53
|
+
def pos_down(cnt): return f'{Term.esc}[{cnt}E' if cnt > 0 else ''
|
|
54
|
+
@staticmethod
|
|
55
|
+
def col(pos): return f'{Term.esc}[{pos}G'
|
|
56
|
+
@staticmethod
|
|
57
|
+
def clear_screen(): return f'{Term.esc}[H{Term.esc}[2J{Term.esc}[3J'
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
##############################################################################
|
|
61
|
+
def human(number):
|
|
62
|
+
""" Return a concise number description."""
|
|
63
|
+
if number <= 0:
|
|
64
|
+
return '0'
|
|
65
|
+
suffixes = ['', 'K', 'M', 'G', 'T', 'Q']
|
|
66
|
+
while suffixes:
|
|
67
|
+
suffix = suffixes.pop(0)
|
|
68
|
+
if number < 999.95 or not suffixes:
|
|
69
|
+
num = f'{number:.1f}'
|
|
70
|
+
num = num[0:-2] if num.endswith('.0') else num
|
|
71
|
+
return f'{num}{suffix}'
|
|
72
|
+
number /= 1024
|
|
73
|
+
return 'TooHuge'
|
|
74
|
+
|
|
75
|
+
##############################################################################
|
|
76
|
+
class ZramAdvisor:
|
|
77
|
+
""" Workhorse class for zram-advisor """
|
|
78
|
+
def __init__(self):
|
|
79
|
+
self.meminfo = None #selected info from meminfo
|
|
80
|
+
self.effective = None # effective system params
|
|
81
|
+
self.probe = None # info about runnable programs
|
|
82
|
+
self.devs = None # info about zram devices
|
|
83
|
+
self.params = None # related kernel parameters
|
|
84
|
+
self.release = None
|
|
85
|
+
self.ram_total = 0
|
|
86
|
+
self.tab_count = 0 # number of tabs created so far
|
|
87
|
+
self.download_dir = os.path.expanduser('~/Downloads')
|
|
88
|
+
self.script_dir = os.path.dirname(os.path.realpath(__file__))
|
|
89
|
+
# options:
|
|
90
|
+
self.DB = False
|
|
91
|
+
self.limit_pct = 80 # arbitrary limit on amount of RAM used by zRAM
|
|
92
|
+
|
|
93
|
+
def human_pct(self, number):
|
|
94
|
+
""" Return the number in human form and as pct total memory. """
|
|
95
|
+
rv = human(number)
|
|
96
|
+
if number > 0 and self.ram_total > 0:
|
|
97
|
+
fraction = number/self.ram_total
|
|
98
|
+
pct = int(round(100*fraction))
|
|
99
|
+
rv += f'/{pct}%'
|
|
100
|
+
return rv
|
|
101
|
+
|
|
102
|
+
def get_meminfo(self):
|
|
103
|
+
"""Get most vital stats from /proc/meminfo'"""
|
|
104
|
+
meminfofile = '/proc/meminfo'
|
|
105
|
+
meminfo = {'MemTotal': 0, 'MemAvailable': 0, 'Dirty':0, 'Shmem':0}
|
|
106
|
+
keys = list(meminfo.keys())
|
|
107
|
+
|
|
108
|
+
with open(meminfofile, encoding='utf-8') as fileh:
|
|
109
|
+
for line in fileh:
|
|
110
|
+
match = re.match(r'^([^:]+):\s+(\d+)\s*kB', line)
|
|
111
|
+
if not match:
|
|
112
|
+
continue
|
|
113
|
+
key, value = match.group(1), int(match.group(2))
|
|
114
|
+
if key not in keys:
|
|
115
|
+
continue
|
|
116
|
+
meminfo[key] = value * 1024
|
|
117
|
+
keys.remove(key)
|
|
118
|
+
if not keys:
|
|
119
|
+
break
|
|
120
|
+
assert not keys, f'ALERT: cannot get vitals ({keys}) from {meminfofile}'
|
|
121
|
+
ns = SimpleNamespace(**meminfo)
|
|
122
|
+
ns.MemUsed = ns.MemTotal - ns.MemAvailable # synthetic
|
|
123
|
+
if self.DB: print(f'DB: meminfo: {ns}')
|
|
124
|
+
if self.DB: print(f'DB: RAM={human(ns.MemTotal)}')
|
|
125
|
+
self.ram_total = ns.MemTotal
|
|
126
|
+
return ns
|
|
127
|
+
|
|
128
|
+
@staticmethod
|
|
129
|
+
def keyword_in_manpage(manpage, keyword):
|
|
130
|
+
""" Is a given keyword in the man page?"""
|
|
131
|
+
try:
|
|
132
|
+
# Use subprocess to run the man command and grep for the keyword
|
|
133
|
+
command = f"man {manpage} | grep -q '{keyword}'"
|
|
134
|
+
subprocess.run(command, shell=True, check=True)
|
|
135
|
+
return True
|
|
136
|
+
except subprocess.CalledProcessError:
|
|
137
|
+
return False
|
|
138
|
+
|
|
139
|
+
def prober(self):
|
|
140
|
+
""" Probe for distro specific attributes """
|
|
141
|
+
def has(program): # is program on path?
|
|
142
|
+
return shutil.which(program) is not None
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
ns = SimpleNamespace(installer=None)
|
|
146
|
+
ns.fraction_str = ''
|
|
147
|
+
|
|
148
|
+
others = ('zramctl', )
|
|
149
|
+
for other in others:
|
|
150
|
+
setattr(ns, other, has(other))
|
|
151
|
+
if self.DB: print(f'DB: probe: {ns}')
|
|
152
|
+
return ns
|
|
153
|
+
|
|
154
|
+
def load_script(self):
|
|
155
|
+
""" Give the user the onboot script"""
|
|
156
|
+
pathname = os.path.join(self.script_dir, 'fix-zram.sh')
|
|
157
|
+
with open(pathname, "r", encoding='utf8') as fh:
|
|
158
|
+
install_script = fh.read()
|
|
159
|
+
return install_script
|
|
160
|
+
|
|
161
|
+
def setup_script(self, install_script):
|
|
162
|
+
""" Dump 'fix-zram' into /tmp and run it."""
|
|
163
|
+
pathname = os.path.join('/tmp', 'fix-zram.sh')
|
|
164
|
+
with open(pathname, "w", encoding='utf-8') as fh:
|
|
165
|
+
fh.write(install_script)
|
|
166
|
+
os.chmod(pathname, 0o755)
|
|
167
|
+
os.system(f'set -x; sudo bash {pathname} --setup')
|
|
168
|
+
|
|
169
|
+
def create_site_import(self):
|
|
170
|
+
""" Get a randomly chosen site """
|
|
171
|
+
random_site_file = os.path.join(self.script_dir, 'random-sites.txt')
|
|
172
|
+
with open(random_site_file, "r", encoding='utf8') as fh:
|
|
173
|
+
sites = [line.rstrip('\n') for line in fh]
|
|
174
|
+
rv = """<!DOCTYPE NETSCAPE-Bookmark-file-1>
|
|
175
|
+
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
|
|
176
|
+
<TITLE>Bookmarks</TITLE>
|
|
177
|
+
<H1>Bookmarks</H1><DL><p>
|
|
178
|
+
"""
|
|
179
|
+
rv += '<DT><H3>Rand0</H3><DL><p>\n'
|
|
180
|
+
for idx, site in enumerate(sites):
|
|
181
|
+
if idx and idx % 10 == 0:
|
|
182
|
+
rv += f'</DL><p>\n<DT><H3>Rand{idx//10}</H3><DL><p>\n'
|
|
183
|
+
href = f'https://{site}/'
|
|
184
|
+
site = site[4:] if site.startswith('www.') else site
|
|
185
|
+
rv += f' <DT><A HREF="{href}">{site}</A>\n'
|
|
186
|
+
rv += '</DL><p>\n</DL><p>\n'
|
|
187
|
+
return rv
|
|
188
|
+
|
|
189
|
+
def get_zram_stats(self):
|
|
190
|
+
""" Get only what we want
|
|
191
|
+
orig_data_size uncompressed size of data stored in this disk.
|
|
192
|
+
compr_data_size compressed size of data stored in this disk
|
|
193
|
+
mem_used_total the amount of memory allocated for this disk.
|
|
194
|
+
mem_limit max RAM ZRAM can use to store (0 means unlimited)
|
|
195
|
+
mem_used_max max RAM zram has consumed to store the data
|
|
196
|
+
"""
|
|
197
|
+
fields = ('orig_data_size compr_data_size mem_used_total'
|
|
198
|
+
+ ' mem_limit mem_used_max').split()
|
|
199
|
+
infos = {}
|
|
200
|
+
zram_devices = sorted([device for device in os.listdir('/sys/class/block/')
|
|
201
|
+
if device.startswith('zram')])
|
|
202
|
+
for device in zram_devices:
|
|
203
|
+
statfile = f'/sys/class/block/{device}/mm_stat'
|
|
204
|
+
if not os.path.exists(statfile):
|
|
205
|
+
continue # not active
|
|
206
|
+
with open(statfile, encoding='utf-8') as fileh:
|
|
207
|
+
ns = SimpleNamespace()
|
|
208
|
+
for line in fileh: # all the goodies are on 1st line
|
|
209
|
+
nums = line.split()
|
|
210
|
+
for idx, field in enumerate(fields):
|
|
211
|
+
setattr(ns, field, int(nums[idx]))
|
|
212
|
+
break
|
|
213
|
+
infos[device] = ns
|
|
214
|
+
for param in ('disksize', ):
|
|
215
|
+
statfile = f'/sys/class/block/{device}/{param}'
|
|
216
|
+
with open(statfile, encoding='utf-8') as fileh:
|
|
217
|
+
for line in fileh: # one value, one line
|
|
218
|
+
setattr(ns, param, int(line.strip()))
|
|
219
|
+
break
|
|
220
|
+
if self.DB: print(f'DB: {device}: {ns}')
|
|
221
|
+
|
|
222
|
+
return infos
|
|
223
|
+
|
|
224
|
+
def get_vm_params(self):
|
|
225
|
+
""" Looking for (and good values):
|
|
226
|
+
vm.swappiness = 180
|
|
227
|
+
vm.watermark_boost_factor = 0
|
|
228
|
+
vm.watermark_scale_factor = 125
|
|
229
|
+
vm.page-cluster = 0
|
|
230
|
+
"""
|
|
231
|
+
def add_param(coll, name, least, most):
|
|
232
|
+
key = name.replace('-', '_')
|
|
233
|
+
coll[key] = SimpleNamespace(
|
|
234
|
+
name=name, value=None, least=least, most=most, key=key)
|
|
235
|
+
params = {}
|
|
236
|
+
add_param(params, 'swappiness', 150, 200)
|
|
237
|
+
add_param(params, 'watermark_boost_factor', 0, 0)
|
|
238
|
+
add_param(params, 'watermark_scale_factor', 125, 125)
|
|
239
|
+
add_param(params, 'page-cluster', 0, 0)
|
|
240
|
+
|
|
241
|
+
vm_dirname = '/proc/sys/vm'
|
|
242
|
+
for param in params.values():
|
|
243
|
+
param_file = os.path.join(vm_dirname, param.name)
|
|
244
|
+
with open(param_file, encoding='utf-8') as fileh:
|
|
245
|
+
param.value = int(fileh.readline().strip())
|
|
246
|
+
if self.DB: print(f'DB: vm.params: {params}')
|
|
247
|
+
return params
|
|
248
|
+
|
|
249
|
+
def get_name_value_info(self, pathname):
|
|
250
|
+
""" Parse a name=value file; e.g., pathname
|
|
251
|
+
- keys are made lower case for uniformity because it varies
|
|
252
|
+
- values are left intact
|
|
253
|
+
"""
|
|
254
|
+
ns = SimpleNamespace()
|
|
255
|
+
parser = configparser.ConfigParser()
|
|
256
|
+
with open(pathname, encoding='utf-8') as stream:
|
|
257
|
+
parser.read_string('[top]\n' + stream.read())
|
|
258
|
+
|
|
259
|
+
for section in parser.sections():
|
|
260
|
+
for key, value in parser.items(section):
|
|
261
|
+
setattr(ns, key.lower(), value.replace('"', ''))
|
|
262
|
+
if self.DB: print(f'DB: {os.path.basename(pathname)}: {ns}')
|
|
263
|
+
return ns
|
|
264
|
+
|
|
265
|
+
def compute_effective(self):
|
|
266
|
+
""" Compute the effective values """
|
|
267
|
+
def dump_effective(ns, annotation=''):
|
|
268
|
+
nonlocal self
|
|
269
|
+
for key, value in vars(ns).items():
|
|
270
|
+
print(f'DB: {annotation} {key}: {self.human_pct(value)}')
|
|
271
|
+
|
|
272
|
+
if not self.devs:
|
|
273
|
+
return None
|
|
274
|
+
meminfo = self.meminfo
|
|
275
|
+
stats = None
|
|
276
|
+
for stat in self.devs.values():
|
|
277
|
+
# NOTE: generalization for more than one weak unless all
|
|
278
|
+
# are identically configured
|
|
279
|
+
if not stats:
|
|
280
|
+
stats = vars(stat).copy()
|
|
281
|
+
continue
|
|
282
|
+
for key, value in vars(stat).items():
|
|
283
|
+
stats[key] += value
|
|
284
|
+
stats = SimpleNamespace(**stats)
|
|
285
|
+
|
|
286
|
+
ns = SimpleNamespace(e_used=meminfo.MemUsed,
|
|
287
|
+
e_avail=meminfo.MemAvailable, e_total=meminfo.MemTotal,
|
|
288
|
+
e_max_used=meminfo.MemTotal, stats=stats)
|
|
289
|
+
e_used = meminfo.MemUsed - stats.mem_used_total + stats.orig_data_size
|
|
290
|
+
ratio = None
|
|
291
|
+
if e_used <= ns.e_used: # zram hurts. assume not enuf data to project
|
|
292
|
+
e_used = ns.e_used
|
|
293
|
+
ratio = 3.5 # wild guess for projection
|
|
294
|
+
# if self.DB: dump_effective(ns, '(unchg)')
|
|
295
|
+
# return ns
|
|
296
|
+
ns.e_used = e_used
|
|
297
|
+
limit_pct = self.limit_pct
|
|
298
|
+
if stats.mem_limit > 0:
|
|
299
|
+
stats_limit_pct = (stats.limit / meminfo.MemTotal)*100
|
|
300
|
+
limit_pct = min(1.0, limit_pct, stats_limit_pct)
|
|
301
|
+
# projected amount of orig used memory that fits in limit
|
|
302
|
+
if ratio is None: # hopefully about 4 ;-)
|
|
303
|
+
ratio = stats.orig_data_size / stats.mem_used_total
|
|
304
|
+
e_max_used = ratio * meminfo.MemTotal * limit_pct/100
|
|
305
|
+
# now limit by disksize
|
|
306
|
+
e_max_used = min(e_max_used, stats.disksize)
|
|
307
|
+
# now add uncompressed memory
|
|
308
|
+
e_max_used += meminfo.MemTotal - e_max_used/ratio
|
|
309
|
+
ns.e_max_used = e_max_used
|
|
310
|
+
ns.e_avail = e_max_used - e_used
|
|
311
|
+
if self.DB: dump_effective(ns)
|
|
312
|
+
return ns
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def show_system_summary(self):
|
|
316
|
+
""" Just the facts, ma'am """
|
|
317
|
+
print(f'{"Distro":>16} : {self.release.marquee}')
|
|
318
|
+
for param in self.params.values():
|
|
319
|
+
ok = '....' if param.least <= param.value <= param.most else ' NOT'
|
|
320
|
+
print(f'{param.value:>16} : vm.{param.name.ljust(24, ".")}'
|
|
321
|
+
f'{ok} in [{param.least}, {param.most}]')
|
|
322
|
+
if not self.devs:
|
|
323
|
+
print(' NO zRAM: zRAM is either not installed or not enabled')
|
|
324
|
+
sys.exit(1)
|
|
325
|
+
GB = 1024*1024*1024
|
|
326
|
+
ram = self.meminfo.MemTotal
|
|
327
|
+
min_disksize = (1.5*ram if ram <= 8*GB else 4*GB)
|
|
328
|
+
actual_disksize = self.effective.stats.disksize
|
|
329
|
+
ok = '....' if min_disksize <= actual_disksize else ' NOT'
|
|
330
|
+
print(f'{human(actual_disksize):>16} : {"zRAM.disksize".ljust(27, ".")}'
|
|
331
|
+
f'{ok} >= {human(min_disksize)}')
|
|
332
|
+
|
|
333
|
+
start = time.time()
|
|
334
|
+
time.sleep(2)
|
|
335
|
+
for _ in range(1000*1000):
|
|
336
|
+
meminfo = self.meminfo
|
|
337
|
+
eff = self.effective
|
|
338
|
+
delta = int(round(time.time() - start))
|
|
339
|
+
print('='*16, f'{delta}s', '='*16)
|
|
340
|
+
print(f'{human(self.meminfo.MemTotal):>16} : {"Total Memory":<16}',
|
|
341
|
+
f'eTotal={self.human_pct(eff.e_max_used)}')
|
|
342
|
+
print(f'{self.human_pct(meminfo.MemUsed):>16} : {"Used":<16}',
|
|
343
|
+
f' eUsed={self.human_pct(eff.e_used)}')
|
|
344
|
+
print(f'{self.human_pct(meminfo.MemAvailable):>16} : {"Available":<16}',
|
|
345
|
+
f'eAvail={self.human_pct(eff.e_avail)}')
|
|
346
|
+
for dev, stats in self.devs.items():
|
|
347
|
+
print(f'{dev}:')
|
|
348
|
+
print(f' uncmpr: {human(stats.orig_data_size)} limit={human(stats.disksize)}')
|
|
349
|
+
factor = (stats.orig_data_size/stats.compr_data_size
|
|
350
|
+
if stats.compr_data_size > 0 else 0)
|
|
351
|
+
print(f' cmpr: {self.human_pct(stats.compr_data_size)}',
|
|
352
|
+
f'factor={factor:.2f}')
|
|
353
|
+
print(f' RAM: {self.human_pct(stats.mem_used_total)}'
|
|
354
|
+
if stats.mem_used_total > 0 else 'n/a',
|
|
355
|
+
f'most={self.human_pct(stats.mem_used_max)}',
|
|
356
|
+
f'limit={self.human_pct(stats.mem_limit)}')
|
|
357
|
+
if self.DB:
|
|
358
|
+
break
|
|
359
|
+
time.sleep(1)
|
|
360
|
+
self.meminfo = self.get_meminfo()
|
|
361
|
+
self.devs = self.get_zram_stats()
|
|
362
|
+
self.effective = self.compute_effective()
|
|
363
|
+
|
|
364
|
+
emit = Term.pos_up(1) + Term.erase_line() + '\r'
|
|
365
|
+
print((4 + len(self.devs) * 4) * emit, end='')
|
|
366
|
+
|
|
367
|
+
def main(self):
|
|
368
|
+
""" Do something useful. """
|
|
369
|
+
parser = argparse.ArgumentParser()
|
|
370
|
+
parser.add_argument('-s', '--setup-fix-zram', action="store_true",
|
|
371
|
+
help='install "fix-zram" program and start zRAM')
|
|
372
|
+
parser.add_argument('-d', '--dump-fix-zram', action="store_true",
|
|
373
|
+
help='print "fix-zram.sh" for manual install')
|
|
374
|
+
parser.add_argument('-t', '--gen-test-sites', action="store_true",
|
|
375
|
+
help='print "bookmarks.html" to import to a web-browser for load test')
|
|
376
|
+
parser.add_argument('--DB', action="store_true",
|
|
377
|
+
help='debug creation of low-level objects/data')
|
|
378
|
+
opts = parser.parse_args()
|
|
379
|
+
|
|
380
|
+
try:
|
|
381
|
+
self.release = self.get_name_value_info('/etc/os-release')
|
|
382
|
+
self.release.marquee = self.release.pretty_name
|
|
383
|
+
except Exception:
|
|
384
|
+
try:
|
|
385
|
+
self.release = self.get_name_value_info('/etc/lsb-release')
|
|
386
|
+
self.release.marquee = self.distrib_description
|
|
387
|
+
except Exception:
|
|
388
|
+
print("cannot get os-release or lsb-release")
|
|
389
|
+
|
|
390
|
+
if opts.dump_fix_zram:
|
|
391
|
+
install_script = self.load_script()
|
|
392
|
+
print(install_script, end='')
|
|
393
|
+
elif opts.setup_fix_zram:
|
|
394
|
+
install_script = self.load_script()
|
|
395
|
+
self.setup_script(install_script)
|
|
396
|
+
elif opts.gen_test_sites:
|
|
397
|
+
import_html = self.create_site_import()
|
|
398
|
+
print(import_html, end='')
|
|
399
|
+
else:
|
|
400
|
+
self.meminfo = self.get_meminfo()
|
|
401
|
+
self.probe = self.prober()
|
|
402
|
+
self.devs = self.get_zram_stats()
|
|
403
|
+
self.params = self.get_vm_params()
|
|
404
|
+
self.effective = self.compute_effective()
|
|
405
|
+
self.show_system_summary()
|
|
406
|
+
def run():
|
|
407
|
+
""" Entry point"""
|
|
408
|
+
ZramAdvisor().main()
|
|
409
|
+
|
|
410
|
+
if __name__ == "__main__":
|
|
411
|
+
run()
|