From c2427f654fd9fe2b9fcdf0ab1d91cbfe7cbc706c Mon Sep 17 00:00:00 2001 From: kamil Date: Fri, 20 Apr 2018 00:06:45 +0000 Subject: [PATCH] Add new example loadable kernel module: readhappy_mpsafe readhappy_mpsafe - demonstrates how to make a module MPSAFE This module contains an additional helper program test_readhappy.c, that is designed to exercise the kernel module with multiple threads. Submitted by --- sys/modules/examples/Makefile | 11 +- sys/modules/examples/README | 7 +- .../examples/readhappy_mpsafe/Makefile | 22 ++ .../readhappy_mpsafe/readhappy_mpsafe.c | 243 ++++++++++++++++++ .../readhappy_mpsafe/test_readhappy.c | 82 ++++++ 5 files changed, 357 insertions(+), 8 deletions(-) create mode 100644 sys/modules/examples/readhappy_mpsafe/Makefile create mode 100644 sys/modules/examples/readhappy_mpsafe/readhappy_mpsafe.c create mode 100644 sys/modules/examples/readhappy_mpsafe/test_readhappy.c diff --git a/sys/modules/examples/Makefile b/sys/modules/examples/Makefile index d028a05e96577..83f364e276300 100644 --- a/sys/modules/examples/Makefile +++ b/sys/modules/examples/Makefile @@ -1,14 +1,15 @@ -# $NetBSD: Makefile,v 1.4 2018/04/13 20:30:09 kamil Exp $ +# $NetBSD: Makefile,v 1.5 2018/04/20 00:06:45 kamil Exp $ .include -SUBDIR+= hello SUBDIR+= executor -#SUBDIR+= luahello # Nothing to build here, only text files -SUBDIR+= luareadhappy # Needs an additional Lua script -SUBDIR+= ping # Needs an additional helper program +SUBDIR+= hello +#SUBDIR+= luahello # Nothing to build here, only text files +SUBDIR+= luareadhappy # Needs an additional Lua script +SUBDIR+= ping # Needs an additional helper program SUBDIR+= properties SUBDIR+= readhappy +SUBDIR+= readhappy_mpsafe # Contains an additional helper program SUBDIR+= sysctl .include diff --git a/sys/modules/examples/README b/sys/modules/examples/README index 52ad568bcc6e4..b220f622cf476 100644 --- a/sys/modules/examples/README +++ b/sys/modules/examples/README @@ -1,4 +1,4 @@ - $NetBSD: README,v 1.6 2018/04/13 20:30:09 kamil Exp $ + $NetBSD: README,v 1.7 2018/04/20 00:06:45 kamil Exp $ Kernel Developer's Manual @@ -13,6 +13,7 @@ DESCRIPTION * properties - handle incoming properties during the module load * ping - basic ioctl(9) * readhappy - basic implementation of read(9) with happy numbers + * readhappy_mpsafe- demonstrates how to make a module MPSAFE * sysctl - demonstrates adding a sysctl handle dynamically To build the examples you need a local copy of NetBSD sources. You also @@ -58,8 +59,8 @@ HISTORY ping, luahello and luareadhappy) first appeared in NetBSD 8.0; they were written by Kamil Rytarowski. - The executor and sysctls module first appeared in NetBSD 9.0 and were - authored by Siddharth Muralee. + The readhappy_mpsafe, executor and sysctls modules first appeared in NetBSD + 9.0 and were authored by Siddharth Muralee. AUTHORS This document was written by Kamil Rytarowski. diff --git a/sys/modules/examples/readhappy_mpsafe/Makefile b/sys/modules/examples/readhappy_mpsafe/Makefile new file mode 100644 index 0000000000000..da70f2bcd7830 --- /dev/null +++ b/sys/modules/examples/readhappy_mpsafe/Makefile @@ -0,0 +1,22 @@ +# $NetBSD: Makefile,v 1.1 2018/04/20 00:06:45 kamil Exp $ + +.include "../Makefile.inc" + +#S?= /usr/src/sys +KMOD= readhappy_mpsafe +SRCS= readhappy_mpsafe.c + +.include + +# To make use of this module, you'll need to separately build the +# test_readhappy program, with a Makefile similar to +# +# MKMAN= NO +# PROG= test_readhappy +# +# CFLAGS+=-pthread +# +# LDADD+= -lpthread +# DPADD+= ${LIBPTHREAD} +# +# .include diff --git a/sys/modules/examples/readhappy_mpsafe/readhappy_mpsafe.c b/sys/modules/examples/readhappy_mpsafe/readhappy_mpsafe.c new file mode 100644 index 0000000000000..a532b08bbb2a4 --- /dev/null +++ b/sys/modules/examples/readhappy_mpsafe/readhappy_mpsafe.c @@ -0,0 +1,243 @@ +/* $NetBSD: readhappy_mpsafe.c,v 1.1 2018/04/20 00:06:45 kamil Exp $ */ + +/*- + * Copyright (c) 2018 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. + */ + +#include +__KERNEL_RCSID(0, "$NetBSD: readhappy_mpsafe.c,v 1.1 2018/04/20 00:06:45 kamil Exp $"); + +#include +#include +#include +#include +#include +#include +#include + +/* + * This module is a modification of the readhappy module to illustrate + * how to make a device MPSAFE (Multiprocessor Safe). + * + * 1. Supports opening device by multiple processes but allows only one at a time. + * 2. Supports multiple read() functions but allows only one at a time. + * 3. Uses mutex for ensuring synchonization. + * + * Create a device /dev/happy_mpsafe from which you can read sequential happy numbers. + * + * To use this device you need to do: + * mknod /dev/happy_mpsafe c 210 0 + * + * To test whether the device is MPSAFE. Compile and run the test_readhappy file + * provided. + */ + + +#define HAPPY_NUMBER 1 + +#define SAD_NUMBER 4 + +/* + * kmutex_t variables have been added to the structure to + * ensure proper synchronization while opened by multiple devices. + * + * kcondvar_t conditional variable added. Boolean part added to + * check whether the device is in use. + */ + +struct happy_softc { + kcondvar_t cv; + bool inuse_cv; + unsigned last; + kmutex_t lock; + kmutex_t read_lock; +}; + + +static struct happy_softc sc; + +static unsigned +dsum(unsigned n) +{ + unsigned sum, x; + + for (sum = 0; n; n /= 10) { + x = n % 10; + sum += x * x; + } + return sum; +} + +static int +check_happy(unsigned n) +{ + unsigned total; + + KASSERT(mutex_owned(&sc.read_lock)); + + for (;;) { + total = dsum(n); + + if (total == HAPPY_NUMBER) + return 1; + if (total == SAD_NUMBER) + return 0; + + n = total; + } +} + +dev_type_open(happy_open); +dev_type_close(happy_close); +dev_type_read(happy_read); + +/* + * Notice that the .d_flag has a additional D_MPSAFE flag to + * tag is as a multiprocessor safe device. + */ + +static struct cdevsw happy_cdevsw = { + .d_open = happy_open, + .d_close = happy_close, + .d_read = happy_read, + .d_write = nowrite, + .d_ioctl = noioctl, + .d_stop = nostop, + .d_tty = notty, + .d_poll = nopoll, + .d_mmap = nommap, + .d_kqfilter = nokqfilter, + .d_discard = nodiscard, + .d_flag = D_OTHER | D_MPSAFE, +}; + +/* + * happy_open : used to open the device for read. mutex_enter and mutex_exit: + * to lock the critical section and allow only a single process to open the + * device at a time. + */ +int +happy_open(dev_t self __unused, int flag __unused, int mode __unused, + struct lwp *l __unused) +{ + int error; + + error = 0; + + mutex_enter(&sc.lock); + while (sc.inuse_cv == true) { + error = cv_wait_sig(&sc.cv, &sc.lock); + if (error) + break; + } + if (!error) { + sc.inuse_cv = true; + sc.last = 0; + } + mutex_exit(&sc.lock); + + return 0; +} + +/* + * happy_close allows only a single process to close the device at a time. + * It uses mutex_enter and mutex_exit for the same. + */ +int +happy_close(dev_t self __unused, int flag __unused, int mode __unused, + struct lwp *l __unused) +{ + + mutex_enter(&sc.lock); + sc.inuse_cv = false; + cv_signal(&sc.cv); + mutex_exit(&sc.lock); + + return 0; +} + +/* + * happy_read allows only a single file descriptor to read at a point of time + * it uses mutex_enter and mutex_exit: to lock the critical section and allow + * only a single process to open the device at a time. + */ +int +happy_read(dev_t self __unused, struct uio *uio, int flags __unused) +{ + char line[80]; + int error, len; + + mutex_enter(&sc.read_lock); + + while (check_happy(++sc.last) == 0) + continue; + + len = snprintf(line, sizeof(line), "%u\n", sc.last); + + if (uio->uio_resid < len) { + --sc.last; + error = EINVAL; + goto fin; + } + + error = uiomove(line, len, uio); + +fin: + mutex_exit(&sc.read_lock); + + return error; +} + +MODULE(MODULE_CLASS_MISC, happy_mpsafe, NULL); + +/* + * Initializing mutex and conditional variables for read() and open(). + * when the module is being loaded. + */ +static int +happy_mpsafe_modcmd(modcmd_t cmd, void *arg __unused) +{ + int cmajor = 210, bmajor = -1; + + switch (cmd) { + case MODULE_CMD_INIT: + if (devsw_attach("happy_mpsafe", NULL, &bmajor, &happy_cdevsw, + &cmajor)) + return ENXIO; + + mutex_init(&sc.lock, MUTEX_DEFAULT, IPL_NONE); + mutex_init(&sc.read_lock, MUTEX_DEFAULT, IPL_NONE); + cv_init(&sc.cv, "example conditional variable"); + return 0; + case MODULE_CMD_FINI: + mutex_destroy(&sc.lock); + mutex_destroy(&sc.read_lock); + cv_destroy(&sc.cv); + devsw_detach(NULL, &happy_cdevsw); + return 0; + default: + return ENOTTY; + } +} diff --git a/sys/modules/examples/readhappy_mpsafe/test_readhappy.c b/sys/modules/examples/readhappy_mpsafe/test_readhappy.c new file mode 100644 index 0000000000000..9919af629d389 --- /dev/null +++ b/sys/modules/examples/readhappy_mpsafe/test_readhappy.c @@ -0,0 +1,82 @@ +/* $NetBSD: test_readhappy.c,v 1.1 2018/04/20 00:06:45 kamil Exp $ */ + +/* + * Copyright (c) 2018 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. + */ + +#include +#include +#include +#include +#include + +/* + * Compile this program with -pthread and run it to test the readhappy_mpsafe + * module. This program should print out happy numbers read from /dev/happy_mpsafe. + * Insert the module and make the /dev/happy_mpsafe node before executing this file. + */ + + +static void* read_happy(void *); + +static FILE *fp; + +/* + * thread function used to read /dev/happy_mpsafe and print them out. + */ +static void * +read_happy(void *unused) +{ + int line; + + for (;;) { + fscanf(fp, "%d", &line); + printf("%d\n", line); + fflush(stdout); + } + /* NOTREACHED */ + return NULL; +} + +/* + * main function : opens /dev/happy_mpsafe and sets the global file descriptor + * creates 100 threads which try to use the file descriptor to read + * from /dev/happy_mpsafe. + * This function does not return anything and will run in an infinite loop. + */ +int +main(int argc, const char *argv[]) +{ + pthread_t thr[100]; + + fp = fopen("/dev/happy_mpsafe","r"); + if (fp == NULL) + err(EXIT_FAILURE, "open"); + for (int i = 0; i < 100; i++) + pthread_create(&thr[i], NULL, read_happy, NULL); + (void)read_happy(NULL); + /* NOTREACHED */ + return EXIT_SUCCESS; +}