this repo has no description
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

libsystem_kernel: Cache UID and GID

As explained in `getuid.c`, the UID and GID getters are called very
frequently, and that was producing lots of traffic to the server in the
old implementation. Since the server never updates a process's UID and
GID values on its own (i.e. it only ever changes as a result of a call
from the process itself), we can cache the UID and GID values on the
client-side to significantly reduce RPC traffic.

I determined this was a call that needed to be optimized after checking
how often it was called. Using the following command, I generated a
list of how frequently each kind of call was made:
```
cat ~/.darling/private/var/log/dserver.log | grep 'Received call' | sed 's/.*(dserver_callnum_\([A-Za-z0-9_]\+\)).*/\1/g' | sort | uniq -c | sort -nr
```

I ran that command after doing a simple session of LLDB:
```
host> rm ~/.darling/private/var/log/dserver.log
host> darling shell zsh
darling> lldb -- ./hello
lldb> process launch
# control-c after it exits because LLDB doesn't currently detect that correctly
lldb> quit
darling> exit
```

The results before this change:
```
33245 uidgid
7173 pthread_canceled
2087 mach_msg_overwrite
1247 s2c
1008 kprintf
342 psynch_mutexwait
337 psynch_mutexdrop
327 mach_port_deallocate
226 thread_self_trap
112 mach_reply_port
102 task_self_trap
91 psynch_cvwait
85 host_self_trap
81 checkin
79 psynch_cvbroad
60 task_is_64_bit
59 vchroot_path
50 set_thread_handles
37 mldr_path
31 checkout
27 mach_port_insert_right
27 kqchan_proc_open
25 mach_port_allocate
21 mach_port_move_member
20 started_suspended
20 set_dyld_info
19 get_tracer
16 kqchan_mach_port_open
16 interrupt_enter
15 interrupt_exit
14 fork_wait_for_child
12 semaphore_signal
11 mach_port_request_notification
9 mach_port_get_attributes
9 console_open
8 thread_get_special_reply_port
8 semaphore_timedwait
8 mach_port_construct
7 sigprocess
4 thread_suspended
4 mach_port_destruct
3 mach_port_mod_refs
1 vchroot
1 tid_for_thread
1 task_for_pid
1 stop_after_exec
1 set_tracer
1 push_reply
1 ptrace_thupdate
1 ptrace_sigexc
1 pthread_markcancel
1 pid_for_task
```

And the results after this change:
```
7186 pthread_canceled
2122 mach_msg_overwrite
1247 s2c
1020 kprintf
332 mach_port_deallocate
295 psynch_mutexwait
295 psynch_mutexdrop
229 thread_self_trap
119 mach_reply_port
108 task_self_trap
89 host_self_trap
83 checkin
82 psynch_cvwait
79 psynch_cvbroad
62 vchroot_path
61 task_is_64_bit
52 set_thread_handles
44 uidgid
38 mldr_path
32 checkout
29 kqchan_proc_open
27 mach_port_insert_right
25 mach_port_allocate
21 started_suspended
21 set_dyld_info
21 mach_port_move_member
20 get_tracer
17 interrupt_enter
16 kqchan_mach_port_open
16 interrupt_exit
15 fork_wait_for_child
12 semaphore_signal
11 mach_port_request_notification
9 thread_get_special_reply_port
9 mach_port_get_attributes
9 console_open
8 semaphore_timedwait
8 mach_port_construct
7 sigprocess
4 thread_suspended
4 mach_port_destruct
3 mach_port_mod_refs
1 vchroot
1 tid_for_thread
1 task_for_pid
1 stop_after_exec
1 set_tracer
1 push_reply
1 ptrace_thupdate
1 ptrace_sigexc
1 pthread_markcancel
1 pid_for_task
```

As you can see, it went from over 33,000 UID/GID calls to just over 40.
Needless to say, this is a *significant* improvement.

It looks like the next call that could use some optimization is
`pthread_canceled` and I already have some ideas on how to do so.
However, that will have to wait until I get a few more issues fixed with
other things (like notifyd crashing).

+101 -21
+8
src/kernel/CMakeLists.txt
··· 67 67 $<TARGET_OBJECTS:mach_server_client_dyld> 68 68 ) 69 69 70 + target_link_libraries(system_kernel_static32 PRIVATE 71 + libsimple_darling 72 + ) 73 + 74 + target_link_libraries(system_kernel_static64 PRIVATE 75 + libsimple_darling 76 + ) 77 + 70 78 install(TARGETS system_kernel DESTINATION libexec/darling/usr/lib/system) 71 79 72 80 add_dependencies(system_kernel migcom)
+2 -3
src/kernel/emulation/linux/unistd/getegid.c
··· 3 3 #include <stddef.h> 4 4 #include <darlingserver/rpc.h> 5 5 #include "../simple.h" 6 + #include "getuid.h" 6 7 7 8 long sys_getegid(void) 8 9 { 9 10 int32_t gid; 10 - if (dserver_rpc_uidgid(-1, -1, NULL, &gid) < 0) { 11 - __simple_abort(); 12 - } 11 + __getuidgid(NULL, &gid); 13 12 return gid; 14 13 } 15 14
+2 -3
src/kernel/emulation/linux/unistd/geteuid.c
··· 3 3 #include <stddef.h> 4 4 #include <darlingserver/rpc.h> 5 5 #include "../simple.h" 6 + #include "getuid.h" 6 7 7 8 long sys_geteuid(void) 8 9 { 9 10 int32_t uid; 10 - if (dserver_rpc_uidgid(-1, -1, &uid, NULL) < 0) { 11 - __simple_abort(); 12 - } 11 + __getuidgid(&uid, NULL); 13 12 return uid; 14 13 } 15 14
+2 -3
src/kernel/emulation/linux/unistd/getgid.c
··· 3 3 #include <stddef.h> 4 4 #include <darlingserver/rpc.h> 5 5 #include "../simple.h" 6 + #include "getuid.h" 6 7 7 8 long sys_getgid(void) 8 9 { 9 10 int32_t gid; 10 - if (dserver_rpc_uidgid(-1, -1, NULL, &gid) < 0) { 11 - __simple_abort(); 12 - } 11 + __getuidgid(NULL, &gid); 13 12 return gid; 14 13 } 15 14
+2 -4
src/kernel/emulation/linux/unistd/gettid.c
··· 3 3 #include <stddef.h> 4 4 #include <darlingserver/rpc.h> 5 5 #include "../simple.h" 6 + #include "getuid.h" 6 7 7 8 long sys_gettid(int* uid, int* gid) 8 9 { 9 - if (dserver_rpc_uidgid(-1, -1, uid, gid) < 0) { 10 - __simple_abort(); 11 - } 12 - 10 + __getuidgid(uid, gid); 13 11 return 0; 14 12 } 15 13
+70 -3
src/kernel/emulation/linux/unistd/getuid.c
··· 3 3 #include <stddef.h> 4 4 #include <darlingserver/rpc.h> 5 5 #include "../simple.h" 6 + #include <libsimple/lock.h> 7 + #include "../errno.h" 8 + 9 + // the UID and GID related calls are called frequently, so making a server call for them each time is expensive. 10 + // instead, since the server never changes our UID and GID on its own (we're always the ones that tell it to change it), 11 + // we can just cache the UID and GID and only inform the server when we want to change them. 12 + 13 + static libsimple_rwlock_t uidgid_rwlock = LIBSIMPLE_RWLOCK_INITIALIZER; 14 + static int32_t stored_uid = -1; 15 + static int32_t stored_gid = -1; 16 + 17 + void __getuidgid(int32_t* uid, int32_t* gid) { 18 + int32_t tmp_uid = -1; 19 + int32_t tmp_gid = -1; 20 + 21 + libsimple_rwlock_lock_read(&uidgid_rwlock); 22 + tmp_uid = stored_uid; 23 + tmp_gid = stored_gid; 24 + libsimple_rwlock_unlock_read(&uidgid_rwlock); 25 + 26 + // if they're uninitialized, initialize them 27 + if (tmp_uid == -1 && tmp_gid == -1) { 28 + libsimple_rwlock_lock_write(&uidgid_rwlock); 29 + // check again if they're still uninitialized 30 + if (stored_uid == -1 && stored_gid == -1) { 31 + // fetch them from the server 32 + 33 + // NOTE: there's no possibility of deadlock here. the server needs the task lock to read/write 34 + // the UID and GID, but the task lock is only held while in a server call (i.e. it doesn't 35 + // remain locked after returning from a syscall). since the server doesn't even know about 36 + // the `uidgid_rwlock` here, there's no chance of someone holding the task lock in the 37 + // server while wanting the `uidgid_lock`. 38 + if (dserver_rpc_uidgid(-1, -1, &stored_uid, &stored_gid) < 0) { 39 + __simple_abort(); 40 + } 41 + } 42 + tmp_uid = stored_uid; 43 + tmp_gid = stored_gid; 44 + libsimple_rwlock_unlock_write(&uidgid_rwlock); 45 + } 46 + 47 + if (uid) { 48 + *uid = tmp_uid; 49 + } 50 + if (gid) { 51 + *gid = tmp_gid; 52 + } 53 + }; 54 + 55 + long __setuidgid(int32_t uid, int32_t gid) { 56 + int ret = 0; 57 + 58 + libsimple_rwlock_lock_write(&uidgid_rwlock); 59 + // see note about impossibility of deadlocks in __getuidgid 60 + ret = dserver_rpc_uidgid(uid, gid, NULL, NULL); 61 + if (ret == 0) { 62 + if (uid >= 0) { 63 + stored_uid = uid; 64 + } 65 + if (gid >= 0) { 66 + stored_gid = gid; 67 + } 68 + } else { 69 + ret = errno_linux_to_bsd(ret); 70 + } 71 + libsimple_rwlock_unlock_write(&uidgid_rwlock); 72 + 73 + return ret; 74 + }; 6 75 7 76 long sys_getuid(void) 8 77 { 9 78 int32_t uid; 10 - if (dserver_rpc_uidgid(-1, -1, &uid, NULL) < 0) { 11 - __simple_abort(); 12 - } 79 + __getuidgid(&uid, NULL); 13 80 return uid; 14 81 } 15 82
+5
src/kernel/emulation/linux/unistd/getuid.h
··· 1 1 #ifndef LINUX_GETUID_H 2 2 #define LINUX_GETUID_H 3 3 4 + #include <stdint.h> 5 + 4 6 long sys_getuid(void); 7 + 8 + void __getuidgid(int32_t* uid, int32_t* gid); 9 + long __setuidgid(int32_t uid, int32_t gid); 5 10 6 11 #endif 7 12
+2 -1
src/kernel/emulation/linux/unistd/setegid.c
··· 3 3 #include <darlingserver/rpc.h> 4 4 #include "../errno.h" 5 5 #include <stddef.h> 6 + #include "getuid.h" 6 7 7 8 long sys_setegid(int egid) 8 9 { 9 - return errno_linux_to_bsd(dserver_rpc_uidgid(-1, egid, NULL, NULL)); 10 + return __setuidgid(-1, egid); 10 11 } 11 12
+2 -1
src/kernel/emulation/linux/unistd/seteuid.c
··· 3 3 #include <darlingserver/rpc.h> 4 4 #include "../errno.h" 5 5 #include <stddef.h> 6 + #include "getuid.h" 6 7 7 8 long sys_seteuid(int euid) 8 9 { 9 - return errno_linux_to_bsd(dserver_rpc_uidgid(euid, -1, NULL, NULL)); 10 + return __setuidgid(euid, -1); 10 11 } 11 12
+2 -1
src/kernel/emulation/linux/unistd/setgid.c
··· 3 3 #include <darlingserver/rpc.h> 4 4 #include "../errno.h" 5 5 #include <stddef.h> 6 + #include "getuid.h" 6 7 7 8 long sys_setgid(int gid) 8 9 { 9 - return errno_linux_to_bsd(dserver_rpc_uidgid(-1, gid, NULL, NULL)); 10 + return __setuidgid(-1, gid); 10 11 } 11 12
+2 -1
src/kernel/emulation/linux/unistd/settid.c
··· 3 3 #include <darlingserver/rpc.h> 4 4 #include "../errno.h" 5 5 #include <stddef.h> 6 + #include "getuid.h" 6 7 7 8 long sys_settid(int uid, int gid) 8 9 { 9 - return errno_linux_to_bsd(dserver_rpc_uidgid(uid, gid, NULL, NULL)); 10 + return __setuidgid(uid, gid); 10 11 } 11 12
+2 -1
src/kernel/emulation/linux/unistd/setuid.c
··· 3 3 #include <darlingserver/rpc.h> 4 4 #include "../errno.h" 5 5 #include <stddef.h> 6 + #include "getuid.h" 6 7 7 8 long sys_setuid(int uid) 8 9 { 9 - return errno_linux_to_bsd(dserver_rpc_uidgid(uid, -1, NULL, NULL)); 10 + return __setuidgid(uid, -1); 10 11 } 11 12