tech-kern archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: mutexes, IPL, tty locking



On Thu, Oct 22, 2009 at 12:32:12PM +0100, Mindaugas Rasiukevicius wrote:
 > IPL is only being raised and that works in reference counting principle.
 > Therefore IPL is lowered (and only to IPL_NONE) after the last release,
 > see ci_mtx_oldspl and ci_mtx_count.  Which means that order of releases
 > does not matter.  Gradual IPL lowering would unnecessary complicate the
 > implementation or interface.

Not that much. Here's some strawman code based on what's in one of my
kernels, adapted partly for NetBSD. It isn't tested (and doesn't
compile) but the original form does of course compile, run, and work
in its own environment.

I'm not suggesting that we use this as such (and it would take quite a
bit of work to merge it) but I think the general approach is worth
considering.

   ------ snip ------

/*
 * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009
 *      The President and Fellows of Harvard College.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#ifndef _SYS_SPL_H_
#define _SYS_SPL_H_

/*
 * Machine-independent interface to interrupt enable/disable.
 *
 * The number of defined interrupt levels (NIPL) is machine-dependent;
 * each one has an IPL_* symbol whose names must be taken from the
 * following list:
 *      IPL_HALT
 *      IPL_PAUSE
 *      IPL_PREEMPT
 *      IPL_SCHED
 *      IPL_SOFTAUDIO
 *      IPL_SOFTBIO
 *      IPL_SOFTCLOCK
 *      IPL_SOFTDDB
 *      IPL_SOFTFDC
 *      IPL_SOFTNET
 *      IPL_SOFTSERIAL
 *      IPL_VM
 * Additionally, IPL_NONE (always 0) and IPL_HIGH (the highest level)
 * must be defined, and IPL_HIGH should ordinarily be a distinct level
 * of its own and not an alias for one of the others.
 *
 * Each of these levels is asserted by the corresponding spl*
 * function: spl0(), splvm(), splsoftnet(), splhigh(), etc. splx()
 * sets a specific level. All these functions return the prior level.
 *
 * Note that these functions only affect interrupts on the current
 * processor.
 */

#include <sys/cdefs.h>
#include <machine/intr.h>

/*
 * Lower-level functions for explicitly raising and lowering
 * particular interrupt levels. These are used by splx() and by the
 * spinlock code.
 *
 * A previous setting of OLDIPL is cancelled and replaced with NEWIPL.
 *
 * For splraise, NEWIPL > OLDIPL, and for spllower, NEWIPL < OLDIPL.
 */
void splraise(int oldipl, int newipl);
void spllower(int oldipl, int newipl);

////////////////////////////////////////////////////////////

/* Inlining support - for making sure an out-of-line copy gets built */
#ifndef SPL_INLINE
#define SPL_INLINE INLINE
#endif

int splx(int);

#define MKSPL(splname, iplname) \
int splname(void);                              \
SPL_INLINE                                      \
int                                             \
splname(void)                                   \
{                                               \
        return splx(iplname);                   \
}

MKSPL(spl0, IPL_NONE);

#ifdef IPL_HALT
    MKSPL(splhalt, IPL_HALT);
#endif

#ifdef IPL_PAUSE
    MKSPL(splpause, IPL_PAUSE);
#endif

#ifdef IPL_PREEMPT
    MKSPL(splpreempt, IPL_PREEMPT);
#endif

#ifdef IPL_SCHED
    MKSPL(splsched, IPL_SCHED);
#endif

#ifdef IPL_SOFTAUDIO
    MKSPL(splsoftaudio, IPL_SOFTAUDIO);
#endif

#ifdef IPL_SOFTBIO
    MKSPL(splsoftbio, IPL_SOFTBIO);
#endif

#ifdef IPL_SOFTCLOCK
    MKSPL(splsoftclock, IPL_SOFTCLOCK);
#endif

#ifdef IPL_SOFTDDB
    MKSPL(splsoftddb, IPL_SOFTDDB);
#endif

#ifdef IPL_SOFTFDC
    MKSPL(splsoftfdc, IPL_SOFTFDC);
#endif

#ifdef IPL_SOFTNET
    MKSPL(splsoftnet, IPL_SOFTNET);
#endif

#ifdef IPL_SOFTSERIAL
    MKSPL(splsoftserial, IPL_SOFTSERIAL);
#endif

#ifdef IPL_VM
    MKSPL(splvm, IPL_VM);
#endif

MKSPL(splhigh, IPL_HIGH);

#undef MKSPL

#endif /* _SPL_H_ */

   ------ snip ------

/*
 * Copyright (c) 2009
 *      The President and Fellows of Harvard College.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/* Make sure to build out-of-line versions of spl inline functions */
#define SPL_INLINE      /* empty */

#include <sys/types.h>
#include <sys/spl.h>
#include <sys/proc.h>
/* ... more includes here ... */

/*
 * Machine-independent interrupt handling functions.
 *
 * This code assumes that each port provides NIPL interrupt levels
 * (where NIPL is at least 2) and a function cpu_setipl() that sets
 * the interrupt level in/for the current processor.
 */


/*
 * Raise and lower the interrupt priority level.
 *
 * Each spinlock acquisition can raise and lower the priority level
 * independently. The spl calls also raise and lower the priority
 * level independently of the spinlocks. This is necessary because in
 * general spinlock acquisitions and releases don't nest perfectly,
 * and don't necessarily nest with respect to spl calls either.
 *
 * For example:
 *
 *    struct mutex *red = ..., *blue = ...;
 *    int s;
 *
 *    mutex_enter(red);
 *    s = splhigh();
 *    mutex_enter(blue);
 *    splx(s);
 *    mutex_exit(red);
 *    mutex_exit(blue);
 *
 * In order to make this work we need to count the number of times
 * each interrupt priority level has been raised. Interrupts go off on
 * the first raise, and go on again only on the last lower.
 *
 * curlwp->l_iplcounts[NIPL-1] is used to track this.
 * curlwp->l_highestipl remembers the highest level currently
 * asserted.
 */
void
splraise(int oldspl, int newspl)
{
        struct lwp *cur = curlwp;

        KASSERT(oldspl < newspl);
        KASSERT(oldspl >= 0 && oldspl < NIPL);
        KASSERT(newspl > 0 && newspl < NIPL);

        if (newspl > cur->l_highestipl) {
           cpu_setipl(newspl);
           cur->l_highestipl = newspl;
        }

        if (oldspl > 0) {
           KASSERT(cur->l_iplcounts[oldspl-1] > 0);
           cur->l_iplcounts[oldspl-1]--;
        }
        cur->l_iplcounts[newspl-1]++;
}

void
spllower(int oldspl, int newspl)
{
        struct lwp *cur = curlwp;
        int highest;

        KASSERT(oldspl > newspl);
        KASSERT(oldspl > 0 && oldspl < NIPL);
        KASSERT(newspl >= 0 && newspl < NIPL);

        KASSERT(cur->l_iplcounts[oldspl-1] > 0);
        cur->l_iplcounts[oldspl-1]--;
        if (newspl > 0) {
           cur->l_iplcounts[newspl-1]++;
        }
        highest = cur->l_highestipl;
        KASSERT(oldspl <= highest);
        if (oldspl == highest && cur->l_iplcounts[highest-1] == 0) {
           while (highest > 0 && cur->l_iplcounts[highest-1] == 0) {
              highest--;
           }
           cur->l_highestipl = highest;
           cpu_setipl(highest);
        }
}


/*
 * Disable or enable interrupts and adjust curspl setting. Return old
 * spl level.
 */
int
splx(int spl)
{
        struct lwp *cur = curlwp;
        int ret;

        if (cur->l_curspl < spl) {
                /* turning interrupts off */
                splraise(cur->l_curspl, spl);
                ret = cur->l_curspl;
                cur->l_curspl = spl;
        }
        else if (cur->l_curspl > spl) {
                /* turning interrupts on */
                ret = cur->l_curspl;
                cur->l_curspl = spl;
                spllower(ret, spl);
        }
        else {
                /* do nothing */
                ret = spl;
        }

        return ret;
}

   ------ snip ------

-- 
David A. Holland
dholland%netbsd.org@localhost


Home | Main Index | Thread Index | Old Index