Last active
July 5, 2025 02:51
-
-
Save oxavelar/0f52045f80ceda1b1379b2ed749e64b9 to your computer and use it in GitHub Desktop.
Synology DSM DS920+ System Optimizations
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/sh | |
# /usr/local/etc/rc.d/boot-tweaks.sh | |
# | |
# Put this in /usr/local/etc/rc.d/ | |
# chown this to root | |
# chmod this to 700 | |
# Must be run as root! | |
case $1 in | |
start) | |
# Enable NVMe as system + swap | |
# > mdadm --grow /dev/md0 --raid-devices=6 --force # sys | |
# > mdadm --grow /dev/md1 --raid-devices=6 --force # swp | |
mdadm --add /dev/md0 /dev/nvme0n1p1 /dev/nvme1n1p1 | |
mdadm --add /dev/md1 /dev/nvme0n1p2 /dev/nvme1n1p2 | |
for disk in $(find /sys/block/md[0-1]/ -name 'dev-sata[0-9]p[0-9]'); do | |
echo writemostly > "$disk/state" | |
done | |
# System optimizations | |
sysctl --system | |
mount -o noatime,remount / | |
# Reduce writes | |
# systemctl stop syslog-ng | |
# Power savings | |
hdparm -S 120 /dev/sata[0-9] | |
for raid in $(find /sys/block -name '^md[0-9]$'); do | |
(cd $raid | |
echo deadline > "$raid/queue/scheduler" | |
echo 256 > "$raid/queue/nr_requests" | |
echo 2 > "$raid/queue/rq_affinity" | |
echo 2048 > "$raid/queue/read_ahead_kb" | |
); | |
done | |
for disk in $(find /sys/block -name 'sata[0-9]'); do | |
(cd $disk | |
echo deadline > "$disk/queue/scheduler" | |
echo 256 > "$disk/queue/nr_requests" | |
echo 2 > "$disk/queue/rq_affinity" | |
echo 2048 > "$disk/queue/read_ahead_kb" | |
); | |
done | |
# Speed-up any re-builds | |
echo 200000 > /proc/sys/dev/raid/speed_limit_min | |
echo 800000 > /proc/sys/dev/raid/speed_limit_max | |
# Tailscale | |
if ! [ -c /dev/net/tun ]; then | |
mkdir -p /dev/net | |
mknod /dev/net/tun c 10 200 | |
chmod 0666 /dev/net/tun | |
fi | |
;; | |
stop) | |
;; | |
*) | |
echo "usage: $0 [start|stop]" | |
;; | |
esac |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/sh | |
# /usr/local/etc/rc.d/boot-tweaks.sh | |
# | |
# Put this in /usr/local/etc/rc.d/ | |
# chown this to root | |
# chmod this to 700 | |
# Must be run as root! | |
case $1 in | |
start) | |
sleep 300 | |
exec /usr/bin/env python -u - "$@" <<'EOF' | |
# Python code starts here | |
import os | |
import re | |
import sys | |
import time | |
import struct | |
import subprocess | |
from ctypes import * | |
from collections import namedtuple | |
SCEMD_PATH = "/usr/syno/bin/scemd" | |
SYNOSTORAGED_PATH = "/usr/syno/sbin/synostoraged" | |
# patches is a list of tuples (orig_pattern, new_pattern, description) | |
BinaryPatchSet = namedtuple("BinaryPatchSet", | |
["process_name", "binary_path", "patches"]) | |
scemd_patchset = BinaryPatchSet("scemd", SCEMD_PATH, [ | |
(b'\x48\x89\xDE\xBF\x01\x00\x00\x00\x48\x89\x04\\x24\xE8(....)\x48\x89\xDE\xBF\x02\x00\x00\x00\x89\xC5\xE8(....)\x48\x89\xDE\xBF\x07\x00\x00\x00\xE8(....)\x85\xED', | |
b'\x48\x89\xDE\xBF\x01\x00\x00\x00\x48\x89\x04\x24\xE8\g<01>\x48\x89\xDE\xBF\x02\x00\x00\x00\x89\xC5\xE8\g<02>\x48\x89\xDE\xBF\x0B\x00\x00\x00\xE8\g<03>\x85\xED', | |
"NVMe I/O HDD hibernation fix for DSM 7.0-7.1"), | |
(b'\x48\x89\xEE\xBF\x01\x00\x00\x00\x48\x89\x04\\x24\xE8(....)\x48\x89\xEE\xBF\x02\x00\x00\x00\x89\xC3\xE8(....)\x48\x89\xEE\xBF\x07\x00\x00\x00\xE8(....)\x85\xDB', | |
b'\x48\x89\xEE\xBF\x01\x00\x00\x00\x48\x89\x04\x24\xE8\g<01>\x48\x89\xEE\xBF\x02\x00\x00\x00\x89\xC3\xE8\g<02>\x48\x89\xEE\xBF\x0B\x00\x00\x00\xE8\g<03>\x85\xDB', | |
"NVMe I/O HDD hibernation fix for DSM 7.2"), | |
]) | |
synostoraged_patchset = BinaryPatchSet("synostgd-disk", SYNOSTORAGED_PATH, [ | |
(b'\x4C\x89\xEE\xBF\x03\x00\x00\x00\xE8(....)\x85\xC0\x0F\x88(....)\x4C\x89\xEE\xBF\x07\x00\x00\x00\xE8(....)\x85\xC0\x0F\x88(....)\x4C\x89\xEE\xBF\x0B\x00\x00\x00\xE8', | |
b'\x4C\x89\xEE\xBF\x03\x00\x00\x00\xE8\g<01>\x85\xC0\x0F\x88\g<02>\xEB\x13\xEE\xBF\x07\x00\x00\x00\xE8\g<03>\x85\xC0\x0F\x88\g<04>\x4C\x89\xEE\xBF\x0B\x00\x00\x00\xE8', | |
"NVMe I/O HDD hibernation fix for DSM 7.0-7.1"), | |
(b'\x48\x89\xDE\xBF\x03\x00\x00\x00\xE8(....)\x85\xC0\x0F\x88(....)\x48\x89\xDE\xBF\x07\x00\x00\x00\xE8(....)\x85\xC0\x0F\x88(....)\x48\x89\xDE\xBF\x0B\x00\x00\x00\xE8', | |
b'\x48\x89\xDE\xBF\x03\x00\x00\x00\xE8\g<01>\x85\xC0\x0F\x88\g<02>\xEB\x13\xDE\xBF\x07\x00\x00\x00\xE8\g<03>\x85\xC0\x0F\x88\g<04>\x48\x89\xDE\xBF\x0B\x00\x00\x00\xE8', | |
"NVMe I/O HDD hibernation fix for DSM 7.2"), | |
]) | |
def get_pid_by_proc_name(process_name): | |
try: | |
return int(subprocess.check_output(["pidof", process_name])) | |
except: | |
return None | |
ProcMapEntry = namedtuple("ProcMapEntry", ["start", "end", "perm", "offset", "dev", "inode", "pathname"]) | |
def parse_proc_maps(pid): | |
entries = [] | |
maps_line_re = re.compile(r"(?P<start>[\da-f]+)-(?P<end>[\da-f]+)\s(?P<perm>\S+)\s(?P<offset>[\da-f]+)\s(?P<dev>\S+)\s+(?P<inode>\d+)\s+(?P<pathname>.*)$") | |
try: | |
with open(f"/proc/{pid}/maps", "r") as f: | |
for line in f.readlines(): | |
m = maps_line_re.match(line) | |
if not m: | |
continue | |
start, end, perm, offset, dev, inode, pathname = m.groups() | |
entries.append(ProcMapEntry( | |
start = int(start, 16), | |
end = int(end, 16), | |
perm = perm, | |
offset = int(offset, 16), | |
dev = dev, | |
inode = inode, | |
pathname = pathname.strip())) | |
except: | |
pass | |
return entries | |
def get_module_base_addr(pid, module_name): | |
entries = parse_proc_maps(pid) | |
if not entries: | |
return None | |
filtered = [entry for entry in entries | |
if os.path.basename(entry.pathname) == module_name] | |
if not filtered: | |
return None | |
for entry in filtered: | |
if entry.offset == 0: | |
return entry.start | |
return None | |
def get_binary_patch_changelist(fpath, search_ptrn, replace_ptrn, max_matches=0): | |
changes = [] | |
try: | |
with open(fpath, "rb") as f: | |
data = f.read() | |
except: | |
print(f"cannot read {fpath}") | |
return None | |
nmatches = len(re.findall(search_ptrn, data, flags=re.DOTALL)) | |
if not nmatches: | |
return None | |
elif max_matches and nmatches > max_matches: | |
print("too many matches encountered") | |
return None | |
new_data = re.sub(search_ptrn, replace_ptrn, data, flags=re.DOTALL) | |
assert(len(new_data) == len(data)) | |
new_bytes = bytearray() | |
orig_bytes = bytearray() | |
changes_offset = 0 | |
in_same = True | |
for i in range(len(data)): | |
if in_same: | |
if new_data[i] == data[i]: | |
continue | |
else: | |
changes_offset = i | |
new_bytes = [new_data[i]] | |
orig_bytes = [data[i]] | |
in_same = False | |
else: | |
if new_data[i] != data[i]: | |
new_bytes.append(new_data[i]) | |
orig_bytes.append(data[i]) | |
else: | |
changes.append((changes_offset, bytes(orig_bytes), bytes(new_bytes))) | |
new_bytes = orig_bytes = [] | |
in_same = True | |
if not in_same and new_bytes: | |
changes.append((changes_offset, bytes(orig_bytes), bytes(new_bytes))) | |
return changes | |
libc = None | |
PTRACE_PEEKDATA = 2 | |
PTRACE_POKEDATA = 5 | |
PTRACE_ATTACH = 16 | |
PTRACE_DETACH = 17 | |
class iovec(Structure): | |
_fields_ = [("iov_base", c_void_p), | |
("iov_len", c_size_t)] | |
def init_ptrace(): | |
global libc | |
libc = CDLL("libc.so.6") | |
libc.process_vm_readv.argtypes = [c_uint64, POINTER(iovec), c_ulong, | |
POINTER(iovec), c_ulong, c_ulong] | |
libc.process_vm_readv.restype = c_ssize_t | |
libc.ptrace.argtypes = [c_uint64, c_uint64, c_void_p, c_void_p] | |
libc.ptrace.restype = c_uint64 | |
libc.__errno_location.restype = POINTER(c_int) | |
def read_mem_sg_list(pid, addrs_and_lens): | |
iovcnt = len(addrs_and_lens) | |
total_bytes = 0 | |
iovec_arr_type = iovec * iovcnt | |
local_iov = iovec_arr_type() | |
remote_iov = iovec_arr_type() | |
readbufs = [] | |
for i, (vaddr, part_len) in enumerate(addrs_and_lens): | |
total_bytes += part_len | |
entry_buf = create_string_buffer(part_len) | |
readbufs.append(entry_buf) | |
remote_iov[i] = iovec(c_void_p(vaddr), part_len) | |
local_iov[i] = iovec(cast(entry_buf, c_void_p), part_len) | |
ret = libc.process_vm_readv(pid, local_iov, iovcnt, remote_iov, iovcnt, 0) | |
if ret < 0 or ret != total_bytes: | |
print("failed to read remote process memory") | |
return [] | |
return [buf.raw for buf in readbufs] | |
def write_mem_sg_list(pid, sg_list): | |
ret = libc.ptrace(PTRACE_ATTACH, pid, None, None) | |
if ret < 0: | |
print(f"ptrace attach failed for pid {pid}") | |
return False | |
_, status = os.waitpid(pid, 0) | |
if not os.WIFSTOPPED(status): | |
print(f"ptrace stop failed for pid {pid}") | |
return False | |
result = True | |
for i, (vaddr, part_bytes) in enumerate(sg_list): | |
part_len = len(part_bytes) | |
for offset in range(0, part_len, 8): | |
val = libc.ptrace(PTRACE_PEEKDATA, pid, c_void_p(vaddr+offset), None) | |
if part_len - offset >= 8: | |
data = part_bytes[offset:offset+8] | |
else: | |
nbytes_left = part_len - offset | |
orig_bytes = struct.pack('<Q', val) | |
data = part_bytes[offset:offset+nbytes_left] + orig_bytes[nbytes_left:] | |
val = struct.unpack("<Q", data)[0] | |
ret = libc.ptrace(PTRACE_POKEDATA, pid, c_void_p(vaddr+offset), c_void_p(val)) | |
if ret < 0: | |
print(f"ptrace write mem failed for pid {pid}, addr {vaddr+offset:#x}") | |
result = False | |
break | |
ret = libc.ptrace(PTRACE_DETACH, pid, None, None) | |
if ret < 0: | |
print(f"ptrace detach failed for pid {pid}") | |
return result | |
def apply_in_memory_patches(pid, base_addr, changelist): | |
in_list = [(base_addr+offset, len(orig_bytes)) for offset, orig_bytes, _ in changelist] | |
out_list = read_mem_sg_list(pid, in_list) | |
if not out_list: | |
print(f"failed reading memory for pid {pid}") | |
return False | |
matched_original = True | |
matched_patched = True | |
for i, entry in enumerate(out_list): | |
if matched_original and entry != changelist[i][1]: | |
matched_original = False | |
if matched_patched and entry != changelist[i][2]: | |
matched_patched = False | |
if matched_patched: | |
print(f"pid {pid} already has in-memory patches applied") | |
return False | |
if not matched_original: | |
print(f"encountered memory content mismatch for pid {pid}") | |
return False | |
write_list = [(base_addr+offset, new_bytes) for offset, _, new_bytes in changelist] | |
print(f"applying in-memory patches for pid {pid}") | |
return write_mem_sg_list(pid, write_list) | |
def apply_binary_patchset(patchset): | |
proc_name = patchset.process_name | |
mod_fname = os.path.basename(patchset.binary_path) | |
pid = get_pid_by_proc_name(proc_name) | |
if not pid: | |
print(f"cannot find pid of {proc_name} process") | |
return False | |
image_base = get_module_base_addr(pid, mod_fname) | |
if not image_base: | |
print(f"cannot find {mod_fname} image base") | |
return False | |
matched_any = False | |
all_success = True | |
for orig_pattern, new_pattern, patch_descr in patchset.patches: | |
changelist = get_binary_patch_changelist(patchset.binary_path, | |
orig_pattern, | |
new_pattern, | |
max_matches=1) | |
if changelist: | |
matched_any = True | |
rc = apply_in_memory_patches(pid, image_base, changelist) | |
if not rc: | |
all_success = False | |
print(f"failed or skipped applying patch '{patch_descr}' for {proc_name}") | |
else: | |
print(f"successfully applied patch '{patch_descr}' for {proc_name}") | |
return matched_any and all_success | |
def do_in_memory_fixes(): | |
init_ptrace() | |
apply_binary_patchset(scemd_patchset) | |
apply_binary_patchset(synostoraged_patchset) | |
if __name__ == "__main__": | |
print("applying live in-memory patches...") | |
do_in_memory_fixes() | |
# Python code ends here | |
EOF | |
;; | |
stop) | |
;; | |
*) | |
echo "usage: $0 [start|stop]" | |
;; | |
esac |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment