tech-userlevel archive

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

RFC: generic notification mechanism



Hi!

dyoung proposed pmem(9) some time ago:
http://mail-index.netbsd.org/tech-kern/2008/03/28/msg000781.html


The producers are the MD bootstrap and bus scanning code.
The consumers of this API are the device drivers.

It is obvious, that device drivers wants to get notified when
a memory page turns into bad state, for example.


I have an generic and light-weight notification mechanism
here (see attachment)
which should be useful for many other things as well.

The documentation needs to be converted into real manpage syntax.
I intend to put it somewhere into src/common/ since I think,
it is useful for userland as well.

Any comments ?

Christoph
/* $NetBSD: */
/*
 * Copyright (c) 2008 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * 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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
 */


/* This code has been taken from
 * the GGI project (http://www.ggi-project.org) and adopted to NetBSD.
 */

/*
******************************************************************************

   LibGG - Channel functions

   Copyright (C) 2006 Eric Faurot       [eric.faurot%gmail.com@localhost]

   Permission is hereby granted, free of charge, to any person obtaining a
   copy of this software and associated documentation files (the "Software"),
   to deal in the Software without restriction, including without limitation
   the rights to use, copy, modify, merge, publish, distribute, sublicense,
   and/or sell copies of the Software, and to permit persons to whom the
   Software is furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included in
   all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
   THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
   IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

******************************************************************************
*/

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/queue.h>

#include "nb_channel.h"

#ifdef DEBUG
#define DPRINT(fmt, ...)        printf(fmt, __VA_ARGS__)
#else
#define DPRINT(fmt, ...)        do { } while(0)
#endif

struct nb_observer {
        nbfunc_channel_observe_cb *cb;
        void *arg;
        LIST_ENTRY(nb_observer) _others;
};

struct nb_channel {
        LIST_HEAD(nb_observer_list, nb_observer) observers;
#define NHANDLERS 2
        nbfunc_channel_control_cb *handler[NHANDLERS];
        void *arg;
};


static void
nbClearChannel(struct nb_channel *channel)
{
        struct nb_observer *curr, *next;

        DPRINT("%s(channel=%p)\n", __func__, channel);

        for (curr = LIST_FIRST(&(channel->bservers));
             curr != NULL;
             curr = next)
        {
                next = LIST_NEXT(curr, _others);
                DPRINT("! observer cb=%p, arg=%p still registered\n",
                        curr->cb, curr->arg);
                LIST_REMOVE(curr, _others);
                curr->cb = NULL;
                curr->arg = NULL;
                free(curr);
        }
}


struct nb_channel *
nbNewChannel(void *arg, nbfunc_channel_control_cb *cb)
{
        struct nb_channel *channel;

        if ((channel = malloc(sizeof(*channel))) == NULL)
                return NULL;

        LIST_INIT(&(channel->observers));

        channel->arg = arg;
        channel->handler[0] = cb;
        channel->handler[1] = NULL;

        return channel;
}


void
nbDelChannel(struct nb_channel *channel)
{
        ASSERT(channel != NULL, "invalid channel\n");

        nbClearChannel(channel);
        free(channel);
}


int
nbSetController(struct nb_channel *channel, nbfunc_channel_control_cb *cb)
{
        channel->handler[1] = cb;

        return 0;
}


struct nb_observer *
nbObserve(struct nb_channel *channel, nbfunc_channel_observe_cb *cb, void *arg)
{
        struct nb_observer *observer;

        DPRINT("%s(channel=%p, cb=%p, arg=%p)\n", channel, cb, arg);

        observer = calloc(1, sizeof(*observer));
        if (observer == NULL) {
                DPRINT("! can not alloc mem for observer.\n");
                return NULL;
        }

        observer->arg = arg;
        observer->cb = cb;
        LIST_INSERT_HEAD(&(channel->observers), observer, _others);

        return observer;
}


void
nbDelObserver(struct nb_observer *observer)
{
        ASSERT(observer != NULL, "invalid observer\n");

        DPRINT("%s(observer=%p)\n", observer);

        LIST_REMOVE(observer, _others);
        observer->cb = NULL;
        observer->arg = NULL;
        free(observer);
}


void
nbBroadcast(struct nb_channel *channel, uint32_t msg, void *data)
{
        struct nb_observer *curr, *next;

        DPRINT("%s(channel=%p, msg=0x%x, data=%p)\n",
                channel, msg, data);

        for (curr = LIST_FIRST(&(channel->observers));
             curr != NULL;
             curr = next)
        {
                next = LIST_NEXT(curr, _others);
                if (curr->cb(curr->arg, msg, data)) {
                        nbDelObserver(curr);
                }
        }
}


int
nbControl(struct nb_channel *channel, uint32_t ctl, void *data)
{
        int err, i;

        DPRINT("%s(channel=%p, ctl=0x%x, data=%p)\n",
                channel, ctl, data);

        err = ENOENT;

        for (i = 0; i < NHANDLERS && err == ENOENT; i++) {
                if (channel->handler[i]) {
                        err = channel->handler[i](channel->arg, ctl, data);
                }
        }

        return err;
}
/* $NetBSD: */
/*
 * Copyright (c) 2008 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * 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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
 */


/* This code has been taken from
 * the GGI project (http://www.ggi-project.org) and adopted to NetBSD.
 */

/*
******************************************************************************

   LibGG - Channel functions

   Copyright (C) 2006 Eric Faurot       [eric.faurot%gmail.com@localhost]

   Permission is hereby granted, free of charge, to any person obtaining a
   copy of this software and associated documentation files (the "Software"),
   to deal in the Software without restriction, including without limitation
   the rights to use, copy, modify, merge, publish, distribute, sublicense,
   and/or sell copies of the Software, and to permit persons to whom the
   Software is furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included in
   all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
   THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
   IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

******************************************************************************
*/

#include <stdint.h>

struct nb_channel;

typedef int (nbfunc_channel_control_cb)(void *arg, uint32_t ctl, void *data);

typedef int (nbfunc_channel_observe_cb)(void *arg, uint32_t msg, void *data);


struct nb_channel *nbNewChannel(void *, nbfunc_channel_control_cb *);

void nbDelChannel(struct nb_channel *);

int nbSetController(struct nb_channel *, nbfunc_channel_control_cb *);

struct nb_observer *nbObserve(struct nb_channel *,
                                nbfunc_channel_observe_cb *,
                                void *);

void nbDelObserver(struct nb_observer *);

void nbBroadcast(struct nb_channel *, uint32_t, void *);

int nbControl(struct nb_channel *, uint32_t, void *);

nbchannel(3)

NAME
        nbNewChannel, nbDelChannel, nbSetController, nbObserve, nbDelObserver,
        nbBroadcast, nbControl : generic message framework

SYNOPSIS
        #include <sys/nbchannel.h>

        typedef int (nbfunc_channel_control_cb)(void *arg, uint32_t ctl, void 
*data);

        typedef int (nbfunc_channel_observe_cb)(void *arg, uint32_t msg, void 
*data);


        struct nb_channel *nbNewChannel(void *arg, nbfunc_channel_control_cb 
*cb);

        void nbDelChannel(struct nb_channel *channel);

        int nbSetController(struct nb_channel *channel, 
nbfunc_channel_control_cb *cb);

        struct nb_observer *nbObserve(struct nb_channel *,
                                        nbfunc_channel_observe_cb *,
                                        void *);

        void nbDelObserver(struct nb_observer *observer);

        void nbBroadcast(struct nb_channel *channel, uint32_t msg, void *data);

        int nbControl(struct nb_channel *channel, uint32_t ctl, void *data);

DESCRIPTION
       This set of functions provide a simple message-passing  infrastructure,
       consisting of a general observer pattern and command dispatching imple-
       mentation.  It is primary intended to support the light-weight coopera-
       tion  model  within  kernel subsystems. It is also very useful to any
       userland application or library writer.

       struct  nb_channel  defines  a channel on which observers can be regis-
       tered. An observer is simply an opaque value and a  callback  receiving
       that value as first argument, a flag, and an opaque event-specific mes-
       sage.  The idea is that if you know the channel  you're  listening  on,
       you know the semantics behind the flag and the message.  When the chan-
       nel is triggered, all observers' callbacks  will  be  fired.   Specific
       control  commands  can  also  sent to a channel, pretty much in the way
       fcntl(2) or sysctl(3) work.

       nbNewChannel creates a new communication  channel.  The  arg  parameter
       will  be  passed as the first argument of controllers callback.  Gener-
       ally, this will be a pointer to the entity that is managed through this
       specific  channel.  The cb callback is the primary controller that will
       be tried when nbControl is called on that channel.  If this callback is
       NULL,  or if it returns ENOENT, then the secondary controller  (set  by
       nbSetController)  will  be  tried.  This  allows  the  creator  of  the
       channel to define a set of non-overridable controls, and let some other
       code (for example a module) install an additional set of specific  con-
       trols.

       nbSetController  installs  cb as the secondary controller on a channel,
       as discussed above.  If one was already set, it is replaced.

       nbDelChannel frees a channel as well as observers still  registered  on
       it.  If at least one is left, then there is probably a logical error in
       the observer code, since it must already have been notified somehow  of
       the channel going down, and unregistered all callbacks before.

       nbObserve registers a new observer on the channel. The callback will be
       fired when an event is broadcasted on that channel.  The arg  parameter
       is  a user value that will be passed as the first argument of the call-
       back.

       nbDelObserver unregisters the given observer from its channel and frees
       it.  Note  that  within  a callback, an observer must not call nbDelOb-
       server on itselt to unregister.  Instead  it  must  return  a  non-zero
       value.

       nbBroadcast  triggers  all observers registered on the channel. msg and
       data will be passed to the observers' callback.

       nbControl sends a specific control code identified by ctl to the  chan-
       nel.  This triggers the channel controllers.

RETURN VALUES
       nbNewChannel returns a newly allocated channel, or NULL on error.

       nbSetController returns 0 or a negative error code on failure.

       nbObserve  returns  a  newly  constructed  observer hook. Normally, the
       caller will have to keep a reference to it if he needs to call nbDelOb-
       server later.

       nbControl  returns  0 or a negative error code on failure.  The  ENOENT
       error code means that the requested control code  was  not  recognized.

EXAMPLE
       #include <sys/nb_channel.h>
       #include <stdio.h>

       int update(void *o, uint32_t msg, void *d)
       {
           printf("update called for observer %p, msg=%i, data=%p\n", o, f ,d);
           if (msg == 1) {
              return 1; /* unregister */
           }
           return 0;
       }

       int control(void *a, uint32_t ctl, void *s)
       {
         switch(ctl) {
         case 0:
             printf("You say \"%s\", I say \"Hello\"!\n", );
             break;
         default:
             return GGI_NOFUNC;
         }

         return 0;
       }

       int main(void)
       {
           struct nb_channel *chn;
           struct nb_observer *o1, *o2;

           chn = nbNewChannel(NULL, control);

           o1 = nbObserve(chn, update, (void*)1);
           o2 = nbObserve(chn, update, (void*)2);

           nbBroadcast(chn, 0, NULL);
           nbBroadcast(chn, 1, NULL);

           nbControl(chn, 0, "Goodbye");

           nbDelChannel(chn);

           return 0;
       }


Home | Main Index | Thread Index | Old Index