|
|
Log in / Subscribe / Register

VMware's signal code

/* ****************************************************************
 * Portions Copyright 2005, 2009, 2010, 2012 VMware, Inc.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * ****************************************************************/

/*
 * VMKLinux supports a subset of the Linux signal-related operations and
 * state.  We only support signals for kernel threads (no user-level
 * signal delivery).  We only support pre-RT signals (signals 1 through
 * 31).  None of the "realtime" (i.e., POSIX signal queuing) signals are
 * supported.
 *
 * Signals may not be ignored.  Signals do not have handlers.  (Kernel
 * threads must explicitly check for pending signals, and flush all
 * signals after handling any they are interested in.)
 *
 * While Linux's existing task_struct is used to store the signal state,
 * we don't use much of it.  Just the signal masks for pending and blocked
 * low-numbered signals (task->pending.signal.sig[0] and
 * task->blocked.sig[0]) are used.
 */

#include <linux/list.h>
#include <linux/version.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/fs.h>

#include "linux_task.h"
#include "linux_stubs.h"
#include "vmklinux_log.h"
#include "vmkapi.h"

/*
 * Only support the first 32 signals (none of the queuing or real-time
 * signals)
 */
#define LINUX_SIG_MAX 32


/*
 *----------------------------------------------------------------------
 *
 * siginitsetinv --
 *
 * 	Initialize all entries in the signal set (we only have 1) to the
 * 	*inverse* of the given mask.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

void
siginitsetinv(sigset_t *set,
              unsigned long mask)
{
   struct task_struct *task;

   task = get_current();
   if (task) {
      unsigned long prevIRQL;
      LinuxTaskExt *te;
      
      te = container_of(task, LinuxTaskExt, task);
      prevIRQL = LinuxTask_Lock(te);
      /* Make sure the blocked signals array has only one element */
      VMK_ASSERT_ON_COMPILE((sizeof(task->blocked.sig) /
                             sizeof(task->blocked.sig[0])) == 1);
      task->blocked.sig[0] = ~mask;
      LinuxTask_Unlock(te, prevIRQL);
   }
}


/*
 *----------------------------------------------------------------------
 *
 * LinuxSignalPendingInt --
 *
 *	 Same as signal_pending, but signal lock already held
 *
 * Results:
 *      0 if no signals pending on given task, non-zero if any are.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static int
LinuxSignalPendingInt(const struct task_struct* task)
{
   LinuxTask_AssertLocked(container_of(task, LinuxTaskExt, task));

   /* Make sure there's only one element in the array of signals */
   VMK_ASSERT_ON_COMPILE((sizeof(task->blocked.sig)/
                          sizeof(task->blocked.sig[0])) == 1);
   VMK_ASSERT_ON_COMPILE((sizeof(task->pending.signal.sig)/
                          sizeof(task->pending.signal.sig)) == 1);

   VMKLNX_DEBUG(3, "task=%p pending=%#lx blocked=%#lx",
                 task, task->pending.signal.sig[0], task->blocked.sig[0]);
   return task->pending.signal.sig[0] & ~(task->blocked.sig[0]);
}

/**                                          
 *  signal_pending - Test if any non-blocked signals are pending
 *  @task: pointer to struct task_struct
 *                                           
 *  Tests if any non-blocked signals are pending
 *
 *  ESX Deviation Notes:
 *  @task must be current.
 *                                           
 *  RETURN VALUE:
 *       Return 0 if no signals are pending on given task, 
 *       non-zero if any.                                           
 */                                          
/* _VMKLNX_CODECHECK_: signal_pending */
int
signal_pending(struct task_struct* task)
{
   unsigned long prevIRQL;
   LinuxTaskExt *te;
   int rval;

   VMK_ASSERT(vmk_PreemptionIsEnabled() == VMK_FALSE);
   VMK_ASSERT(task == current);
   VMKLNX_DEBUG(4, "task=%p", task);

   te = container_of(task, LinuxTaskExt, task);

   prevIRQL = LinuxTask_Lock(te);
   rval = LinuxSignalPendingInt(task);
   LinuxTask_Unlock(te, prevIRQL);
   return rval;
}
EXPORT_SYMBOL(signal_pending);


/*
 *----------------------------------------------------------------------
 *
 * recalc_sigpending --
 *
 *	Refresh signal pending bit in core vmkernel for the current task.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

void
recalc_sigpending(void)
{
   struct task_struct* task = get_current();
   unsigned long prevIRQL;
   int pending;
   LinuxTaskExt *te = container_of(task, LinuxTaskExt, task);
   
   /*
    * Since drivers may twiddle .pending or .blocked directly, we'll
    * refresh the world-based signal pending bit.
    */

   prevIRQL = LinuxTask_Lock(te);
   pending = LinuxSignalPendingInt(task);
   LinuxTask_Unlock(te, prevIRQL);

   VMKLNX_DEBUG(4, "pending=%#x", pending);
}


/*
 *----------------------------------------------------------------------
 *
 * flush_signals --
 *
 *	Mark all signals as handled on given task (must map to current
 *	world).
 *
 * Results:
 *      None.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

void 
flush_signals(struct task_struct* task)
{
   LinuxTaskExt *te;
   unsigned long prevIRQL;

   VMK_ASSERT(vmk_PreemptionIsEnabled() == VMK_FALSE);
   VMK_ASSERT(task != NULL);
   VMKLNX_DEBUG(1, "task=%p", task);

   // Only on ourselves
   VMK_ASSERT(get_current() == task);

   te = container_of(task, LinuxTaskExt, task);
   
   /* Ensure there is only 1 entry in the pending signal sig array */
   VMK_ASSERT_ON_COMPILE((sizeof(task->pending.signal.sig)/
                          sizeof(task->pending.signal.sig[0])) == 1);

   prevIRQL = LinuxTask_Lock(te);
   task->pending.signal.sig[0] = 0;
   LinuxTask_Unlock(te, prevIRQL);
}
EXPORT_SYMBOL(flush_signals);


/*
 *----------------------------------------------------------------------
 *
 * block_all_signals --
 *
 *      Unsupported
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

void
block_all_signals(int (*notifier)(void *priv),
                  void *priv,
                  sigset_t *mask)
{
   VMKLNX_DEBUG(0, "not implemented. notifier=%p priv=%p mask=...", 
                 notifier, priv);
}


/*
 *----------------------------------------------------------------------
 *
 * unblock_all_signals --
 *
 *      Unsupported.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

void
unblock_all_signals(void)
{
   // Not used by ISCSI, so not implemented.
   VMKLNX_DEBUG(0, "not implemented");   
}


/*
 *----------------------------------------------------------------------
 *
 * LinuxSignalSendByTask --
 *
 *	Send the given signal to the given task.
 *
 * Results:
 *      0 if signalled, -ESRCH otherwise.
 *
 * Side effects:
 *	Signal added to pending mask on target
 *
 *----------------------------------------------------------------------
 */

static int
LinuxSignalSendByTask(struct task_struct *task,
                      int sig)
{
   LinuxTaskExt *te;
   unsigned long prevIRQL;
   int p;

   VMK_ASSERT(task != NULL);

   te = container_of(task, LinuxTaskExt, task);
   VMK_ASSERT(LinuxTask_ValidTask(te) == VMK_TRUE);

   prevIRQL = LinuxTask_Lock(te);

   // Subtract 1 because signal 0 isn't valid, but bit 0 is
   task->pending.signal.sig[0] |= (1UL << (sig - 1));

   p = LinuxSignalPendingInt(task);

   LinuxTask_Unlock(te, prevIRQL);

   if (p != 0
       && (task->state == TASK_INTERRUPTIBLE) 
       && (te->flags & LT_SUSPENDED)) {
      /*
       * Wake up task since the signal is not blocked and the task is
       * suspended.
       */
      LinuxTask_Resume(te);
   }

   return 0;
}


/*
 *----------------------------------------------------------------------
 *
 * kill_proc --
 *
 *	Send given signal to given pid.  fromKernel must be 1.
 *
 * Results:
 *      0 on OK, -<errno> on error
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

int
kill_proc(pid_t pid,
          int sig,
          int fromKernel) // sent by kernel or kernel-thread.  Ignored.
{
   LinuxTaskExt *te;
   int status;

   VMK_ASSERT(pid);
   VMK_ASSERT(fromKernel == 1);
   VMK_ASSERT(sig > 0);
   VMK_ASSERT(sig < LINUX_SIG_MAX);
   
   VMKLNX_DEBUG(0, "pid=%d, sig=%d", pid, sig);

   if (unlikely(pid < 0)) {
      VMKLNX_WARN("World %d tried to signal invalid pid (%d)...",
                  vmk_WorldGetID(), pid);
         return -ESRCH;
   }

   te = LinuxTask_Find(pid);
   if (unlikely(te == NULL)) {
      VMKLNX_WARN("World %d tried to signal non-existing pid (%d)...",
                  vmk_WorldGetID(), pid);
      return -ESRCH;
   }

   status = LinuxSignalSendByTask(&te->task, sig);

   LinuxTask_Release(te);

   return status;
}


/*
 *----------------------------------------------------------------------
 *
 * kill_proc_info_as_uid --
 *
 *	Send given signal to given pid.
 *
 * Results:
 *      0 on OK, -<errno> on error
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

int 
kill_proc_info_as_uid(int signum,
                      struct siginfo *sinfo,
                      pid_t pid,
                      uid_t uid,
                      uid_t euid,
                      u32   secid)
{
   VMK_ASSERT(vmk_PreemptionIsEnabled() == VMK_FALSE);
   VMK_ASSERT(signum > 0);
   VMK_ASSERT(signum < LINUX_SIG_MAX);
   VMK_ASSERT(!uid);
   VMK_ASSERT(!euid);

   VMKLNX_DEBUG(0, "kill_proc_info_as_uid with signum=%d, "
                    "1st 8 bytes of siginfo=%llx, pid=%d",
                    signum, 
                    *(unsigned long long *)sinfo, pid);

   return kill_proc(pid, signum, 1);
}
EXPORT_SYMBOL(kill_proc_info_as_uid);


/*
 *----------------------------------------------------------------------
 *
 * send_sig --
 *
 * Results:
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

int 
send_sig(int signum,
         struct task_struct *task, 
         int fromKernel)
{
   VMK_ASSERT(vmk_PreemptionIsEnabled() == VMK_FALSE);
   VMK_ASSERT(signum > 0);
   VMK_ASSERT(signum < LINUX_SIG_MAX);
   VMK_ASSERT(fromKernel == 1);

   return LinuxSignalSendByTask(task, signum);
}
EXPORT_SYMBOL(send_sig);


/*
 *----------------------------------------------------------------------
 *
 * send_sig_info --
 *
 * Results:
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

int 
send_sig_info(int signum,
              struct siginfo *sinfo,
              struct task_struct *task)
{
   VMK_ASSERT(signum > 0);
   VMK_ASSERT(signum < LINUX_SIG_MAX);

   return LinuxSignalSendByTask(task, signum);
}


/**                                          
 *  fasync_helper - Initialize an fasync_struct
 *  @fd: ignored
 *  @filep: ignored
 *  @on: ignored
 *  @fapp: ignored
 *
 *  NOTE:                   
 *  This function is currently unsupported.  This is a
 *  nonfunctional stub only.  For notifications to user-space 
 *  applications from character devices, use poll() instead.
 *
 *  ESX Deviation Notes:
 *  This function is a placeholder only and does nothing.
 *
 *  RETURN VALUES:
 *  0 for success,
 *  -EFAULT if the given fapp is NULL
 */                                          
/* _VMKLNX_CODECHECK_: fasync_helper */
int
fasync_helper(int fd, struct file *filep, int on, struct fasync_struct **fapp)
{
   VMK_ASSERT(vmk_PreemptionIsEnabled() == VMK_FALSE);
   /*
    * On Linux, this function saves the fd and filep in an fsync_struct
    * and enqueues the struct in a linked list.  This function is
    * unsupported on vmklinux, however.
    */
   if (fapp == NULL)
      return -EFAULT;

   return 0;
}
EXPORT_SYMBOL(fasync_helper);


/**                                          
 *  kill_fasync - Send an fasync signal on behalf of a device 
 *  @fapp: ignored
 *  @sig: ignored
 *  @band: ignored
 *   
 *  NOTE:                                        
 *  This function is currently unsupported.  This is a
 *  nonfunctional stub only.  For notifications to user-space 
 *  applications from character devices, use poll() instead.
 *                                           
 *  ESX Deviation Notes:
 *  This function is a placeholder only and does nothing.
 *                                           
 *  RETURN VALUE:
 *  This function does not return a value.
 */                                          
/* _VMKLNX_CODECHECK_: kill_fasync */
void
kill_fasync(struct fasync_struct **fapp, int sig, int band)
{
   VMK_ASSERT(vmk_PreemptionIsEnabled() == VMK_FALSE);
   return;
}
EXPORT_SYMBOL(kill_fasync);



to post comments


Copyright © 2015, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds