/
zram-init
executable file
·331 lines (307 loc) · 8.78 KB
/
zram-init
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
#!/usr/bin/env sh
# (C) Martin V\"ath <martin@mvath.de>
set -u
Echo() {
printf '%s\n' "$*"
}
SysCtl() {
printf '%s' "$1" >|"$2"
}
SysCtlN() {
printf '%s\n' "$1" >|"$2"
}
Error() {
Echo "${0##*/}: $*" >&2
}
Warning() {
Error "warning: $*" >&2
}
Fatal() {
Error "$*"
exit 1
}
Push() {
PushA_=`push.sh 2>/dev/null` || Fatal \
'push.sh from https://github.com/vaeth/push (v2.0 or newer) required'
eval "$PushA_"
Push "$@"
}
IsNumeric() {
case ${1:-x} in
*[!01234567890]*)
return 1;;
esac
:
}
Usage() {
Echo "Usage: ${0##*/} [options] SIZE [DIR]
Prepare a zram device and use it as swap (resp. mount it under DIR).
SIZE is the maximal size in megabytes.
For umounting/freeing the zram device, use SIZE=0.
If DIR is - then only a filesystem is created in /dev/zram\$DEV (or the device
is removed if SIZE is 0) but it is not mounted
(options -o -c -m -T take no effect in this case, of course).
The latter can be useful e.g. for btrfs if multiple devices should be mounted
jointly afterwards.
The following options are available.
An empty argument means the same as if the option is not specified.
-d DEV Use zram device DEV. If not specified, DEV=0 is assumed.
Make sure to use the matching value for umounting (SIZE=0)!
-D NUM If modprobe needs to be used, require NUM devices. This is not
recommended. Rely instead on /etc/modprobe.d/zram.conf with the line
options zram num_devices=NUM
-s NUM Use up to NUM parallel compression streams for the device
-S MAX Use maximally MAX megabytes of uncompressed memory for the device
-b DEV Use DEV as backing device
-a ALGO Set compression algorithm to ALGO (e.g. lz4 or lzo)
-c OWNER If specified, chown to OWNER (resp. OWNER:GROUP) after mounting.
If not specified, the default owner (root:root) is not changed.
-m MODE If specified, chmod DIR to MODE after mounting.
-o OPTS If specified, mount DIR with -o OPTS.
-p PRIO Use priority PRIO for the swap device.
If not specified, PRIO=16383 is assumed.
Use PRIO=- if you want to keep the default priority (-1).
-t TYPE Use a filesystem of type TYPE if DIR is specified.
TYPE can be either ext2, ext4, or btrfs
If not specified, TYPE=ext4 is assumed.
-i IRATIO If specified override default bytes/inodes in fs (ext2, ext4)
-N INODES If specified override inode count (ext2, ext4)
-L LABEL Specify the label LABEL for the new filesystem
-U UUID Specify the uuid UUID for the new filesystem
-T If specified, do not use the discard (TRIM) feature of ext4/swap.
Use this option with linux-3.14 or earlier or when you want a slight
speed increase at the cost of possibly wasting a lot of memory
-l Do not use zramctl even if available
-k Do no attempt to umount/free a previously used zram under this device
If you have push.sh in \$PATH, you can also use accumulatively:
-K ARG Pass ARG to the respective mkswap or mkfs.* call
-M ARG Pass ARG to the respective swapon/mount call
-2 ARG Pass ARG to the tune2fs call (ignored unless for ext2 or ext4)
-Z ARG Pass ARG to the zramctl call"
exit ${1:-1}
}
PATH=${PATH-}${PATH:+:}/usr/sbin:/sbin
dev=
num=
streams=
algo=
prio=
fstype=
opts=
mode=
owgr=
iratio=
inodes=
label=
uuid=
keep=false
zramctl=:
discard=:
mkfs_opt=
mount_opt=
tune2fs_opt=
zramctl_opt=
mem_limit=
backing_dev=
OPTIND=1
while getopts 's:S:a:b:c:d:D:m:o:p:t:i:N:L:U:TlkK:M:2:Z:hH' opt
do case $opt in
s) streams=$OPTARG;;
S) mem_limit=$OPTARG;;
a) algo=$OPTARG;;
b) backing_dev=$OPTARG;;
c) owgr=$OPTARG;;
d) dev=$OPTARG;;
D) num=$OPTARG;;
m) mode=$OPTARG;;
o) opts=$OPTARG;;
p) prio=$OPTARG;;
t) fstype=$OPTARG;;
i) iratio=$OPTARG;;
N) inodes=$OPTARG;;
L) label=$OPTARG;;
U) uuid=$OPTARG;;
T) discard=false;;
l) zramctl=false;;
k) keep=:;;
K) Push mkfs_opt "$OPTARG";;
M) Push mount_opt "$OPTARG";;
2) Push tune2fs_opt "$OPTARG";;
Z) Push zramctl_opt "$OPTARG";;
'?') exit 1;;
*) Usage 0;;
esac
done
shift $(( $OPTIND -1 ))
[ $# -eq 1 ] || [ $# -eq 2 ] || Usage
if $zramctl
then command -v zramctl >/dev/null 2>&1 || zramctl=false
fi
# Defaults for empty/unspecified options:
: ${dev:=0} ${prio:=16383} ${fstype:=ext4}
IsNumeric "$dev" || Fatal "device $dev is not numeric"
IsNumeric "${1:-0}" || Fatal "size $1 is not numeric"
umount=:
if [ ${1:-0} -ne 0 ]
then umount=false
size=$(( $1 * 1024 * 1024 )) && [ $size -gt 0 ] || \
Fatal 'failed to calculate size'
fi
swap=:
if [ $# -eq 2 ]
then swap=false
mount=:
dir=${2%/}
if [ x"$dir" = x'-' ]
then mount=false
elif [ -z "$dir" ] || ! test -d "$dir"
then if $umount
then Warning "${dir:-(empty)} is not a directory. Umounting anyway"
else Fatal "${dir:-(empty)} is not a directory"
fi
fi
fi
devnode=/dev/zram$dev
zclass=/sys/class/zram-control
HotAdd() {
while :
do curradd=
exec 3<&0 && {
exec <"$zclass/hot_add" && \
read curradd || curradd=
exec 0<&3
exec 3<&-
}
IsNumeric "$curradd" || {
Warning "hot_add failed for $devnode"
break
}
[ $curradd -lt $dev ] || break
done
sleep=0
while ! test -b "$devnode"
do sleep 1
sleep=$(( $sleep + 1 ))
[ $sleep -lt 5 ] || Fatal "failed to create $devnode"
done
}
if ! test -b "$devnode"
then $umount && exit
IsNumeric "${num:-0}" || Fatal "device count $num is not numeric"
modprobe zram ${num:+num_devices=$num} >/dev/null 2>&1
sleep=0
while ! test -b "$devnode" && ! test -d "$zclass"
do sleep 1
sleep=$(( $sleep + 1 ))
[ $sleep -lt 5 ] || \
Fatal "cannot create $devnode: $zclass missing"
done
test -b "$devnode" || {
HotAdd
keep=:
}
fi
block=/sys/block/zram$dev
if ! $zramctl || [ -n "$mem_limit" ] || [ -n "${backing_dev:++}" ]
then test -d "$block" || Fatal "cannot find $block"
fi
status=0
if ! $keep
then if $swap
then swapoff -- "$devnode" >/dev/null 2>&1 || status=$?
elif $mount
then umount -- "$devnode" >/dev/null 2>&1 || status=$?
fi
if [ $status -eq 0 ]
then if $zramctl
then zramctl --reset -- "$devnode" || status=$?
else SysCtl 1 "$block/reset" || status=$?
if $umount && test -w "$zclass/hot_remove"
then SysCtl "$device" "$zclass/hot_remove" \
|| status=$?
fi
fi
[ $status -eq 0 ] || \
Warning "failed to reset zram$dev"
fi
fi
$umount && exit $status
test -b "$devnode" || HotAdd
if $zramctl && [ -z "${backing_dev:++}" ]
then eval "set -- a $zramctl_opt"
shift
zramctl --size "$size" \
${streams:+--streams "$streams"} \
${algo:+--algorithm "$algo"} ${1+"$@"} \
-- "$devnode" || Fatal "zramctl zram$dev failed"
else [ -z "$streams" ] || SysCtl "$streams" "$block/max_comp_streams" || \
Warning "failed to set zram$dev max_comp_streams to $streams"
[ -z "$algo" ] || SysCtl "$algo" "$block/comp_algorithm" || \
Warning "failed to set zram$dev comp_algorithm to $algo"
[ -z "${backing_dev:++}" ] || \
SysCtlN "$backing_dev" "$block/backing_dev" || \
Warning "failed to set zram$dev backing_dev"
SysCtl "$size" "$block/disksize" || Fatal "cannot set zram$dev size"
fi
[ -z "$mem_limit" ] || SysCtl "$mem_limit" "$block/mem_limit" || \
Warning "failed to set zram$dev mem_limit"
eval "set -- a $mkfs_opt"
shift
if $swap
then mkswap ${label:+-L "$label"} ${uuid:+-U "$uuid"} ${1+"$@"} \
-- "$devnode" >/dev/null || Fatal "mkswap $devnode failed"
discardopt=-d
$discard || discardopt=
[ x"$prio" != x'-' ] || prio=
swapon ${prio:+-p "$prio"} $discardopt -- "$devnode" \
|| Fatal "swapon $devnode failed"
exit 0
fi
fsopts='-O^huge_file,sparse_super'
case $fstype in
ext2)
mkfs.ext2 -m0 "$fsopts" ${iratio:+-i "$iratio"} \
${inodes:+-N "$inodes"} \
${label:+-L "$label"} ${uuid:+-U "$uuid"} ${1+"$@"} \
-- "$devnode" >/dev/null \
|| Fatal "mkfs.ext2 $devnode failed"
eval "set -- a $tune2fs_opt"
shift
tune2fs -c0 -i0 -m0 -- "$devnode" >/dev/null
$mount || exit 0
eval "set -- a $mount_opt"
shift
mount -t ext2 ${opts:+-o "$opts"} -- "$devnode" "$dir" \
|| Fatal "mount $devnode failed";;
ext4)
fsopts=$fsopts',extent,^uninit_bg,dir_nlink,extra_isize,^has_journal'
mkfs.ext4 -m0 "$fsopts" ${iratio:+-i "$iratio"} \
${inodes:+-N "$inodes"} \
${label:+-L "$label"} ${uuid:+-U "$uuid"} ${1+"$@"} \
-- "$devnode" >/dev/null \
|| Fatal "mkfs.ext4 $devnode failed"
eval "set -- a $tune2fs_opt"
shift
tune2fs -c0 -i0 -m0 -- "$devnode" >/dev/null
$mount || exit 0
! $discard || opts=$opts${opts:+,}'discard'
eval "set -- a $mount_opt"
shift
mount -t ext4 ${opts:+-o "$opts"} -- "$devnode" "$dir" \
|| Fatal "mount $devnode failed";;
btrfs)
mkfs.btrfs -d single -m single \
${label:+-L "$label"} ${uuid:+-U "$uuid"} ${1+"$@"} \
-- "$devnode" >/dev/null \
|| Fatal "mkfs.btrfs $extnode failed"
$mount || exit 0
eval "set -- a $mount_opt"
shift
mount -t btrfs ${opts:+-o "$opts"} -- "$devnode" "$dir" \
|| Fatal "mount $devnode failed";;
*)
Fatal "unsupported filesystem $fstype";;
esac
# Change owner/mode if requested
[ -z "$owgr" ] || chown -- "$owgr" "$dir"
[ -z "$mode" ] || chmod -- "$mode" "$dir"