I'm currently modifying perf in Linux source tree to implement a tool that I need.
perf originally has a signal handler installed(handler for SIGUSR2) but I want to extend its functionality by handling two signals(SIGUSR1 and SIGUSR2)
As shown below, the kernel (not a user process) sends the two signals consecutively.
asmlinkage long sys_pt_signal_perf(void) //new syscall No.500
{
return (long)pt_signal_perf_wrapper();
}
int pt_signal_perf_wrapper(void)
{
if(signal_perf(2) != -1) { //SIGUSR2
/* Some dummy loop */
signal_perf(1); //SIGUSR1
printk(KERN_EMERG "Marker chunk : %d\n", j);
printk(KERN_EMERG "[SEMAPHORE LOCK] Marker Chunk\n");
pt_down_interruptible(&decode_semaphore); //perf user process unlocks this semaphore lock
printk(KERN_EMERG "Decoding for this syscall seems to be finished\n");
return 1;
}
else {
return 0;
}
}
EXPORT_SYMBOL(pt_signal_perf_wrapper);
int signal_perf(int sig_type)
{
int signal_to_send;
if(sig_type == 1)
signal_to_send = SIGUSR1;
else
signal_to_send = SIGUSR2;
if(!perf_pid) {
return -1;
}
if(!target_pid) {
return -1;
}
struct siginfo info;
memset(&info, 0, sizeof(struct siginfo));
info.si_signo = signal_to_send;
info.si_code = SI_QUEUE;
info.si_int = SI_USER;
struct task_struct *t = find_task_by_vpid(perf_pid);
if(!t) {
printk(KERN_EMERG "No task struct for %d!\n", perf_pid);
return -1;
}
int send_sig_info_result = send_sig_info(signal_to_send, &info, t);
if(send_sig_info_result < 0)
printk(KERN_EMERG "KKM : Error sending signal!!\n");
else
printk(KERN_EMERG "KKM : sending signal successful!!\n");
return (long)perf_pid;
}
The perf, the signal receipient, runs as a user process.
static sigset_t pt_sigset;
... (in main function)
signal(SIGUSR2, snapshot_sig_handler);
signal(SIGUSR1, snapshot_sig_handler);
/* set pt_sigset to block SIGUSR1/2 when they are being processed */
sigemptyset(&pt_sigset);
if(sigaddset(&pt_sigset, SIGUSR1) == -1) {
pr_err("sigaddset for SIGUSR1 returned -1\n");
return -1;
}
if(sigaddset(&pt_sigset, SIGUSR2) == -1) {
pr_err("sigaddset for SIGUSR2 returned -1\n");
return -1;
}
in signal handler, when a signal(either SIGUSR1 or SIGUSR2) is received , it blocks the both SIGUSR1 and SIGUSR2 signals using sigprocmask.
static void snapshot_sig_handler(int sig __maybe_unused)
{
pr_err("Blocking the two signals\n");
if(sigprocmask(SIG_BLOCK, &pt_sigset, NULL) == -1) {
pr_err("sigprocmask returned with error\n");
}
if(sig == SIGUSR2) {
pr_err("SIG_HANDLER from a non-marker chunk\n");
}
else if(sig == SIGUSR1) {
pr_err("SIG_HANDLER from a marker chunk\n");
}
else {
pr_err("NOT A VALID SIGNAL!\n");
return;
}
/* Real operation which is omitted here */
}
Unblocking the two signals is not shown but definitely is there.
But when I actually run the code, there are some cases where the signal handler function is called even when the two signals are unblocked. (like below)
Blocking the two signals
SIG_HANDLER from a non-marker chunk
Blocking the two signals
SIG_HANDLER from a marker chunk
Unblocking the two signals
Unblocking the two signals
It seems as if blocking of the two signals never even work, although sigprocmask() doesn't return with error.
Is this because the signal is sent from a higher privilege?(kernel->user) Unlike most cases where signal transactions are done between two user processes?
Thank you very much in advance.