1*67a548a8Skamil /* $NetBSD: readhappy_mpsafe.c,v 1.2 2020/01/30 07:58:34 kamil Exp $ */
26a0b9e97Skamil
36a0b9e97Skamil /*-
46a0b9e97Skamil * Copyright (c) 2018 The NetBSD Foundation, Inc.
56a0b9e97Skamil * All rights reserved.
66a0b9e97Skamil *
76a0b9e97Skamil * Redistribution and use in source and binary forms, with or without
86a0b9e97Skamil * modification, are permitted provided that the following conditions
96a0b9e97Skamil * are met:
106a0b9e97Skamil * 1. Redistributions of source code must retain the above copyright
116a0b9e97Skamil * notice, this list of conditions and the following disclaimer.
126a0b9e97Skamil * 2. Redistributions in binary form must reproduce the above copyright
136a0b9e97Skamil * notice, this list of conditions and the following disclaimer in the
146a0b9e97Skamil * documentation and/or other materials provided with the distribution.
156a0b9e97Skamil *
166a0b9e97Skamil * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
176a0b9e97Skamil * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
186a0b9e97Skamil * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
196a0b9e97Skamil * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
206a0b9e97Skamil * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
216a0b9e97Skamil * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
226a0b9e97Skamil * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
236a0b9e97Skamil * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
246a0b9e97Skamil * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
256a0b9e97Skamil * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
266a0b9e97Skamil * POSSIBILITY OF SUCH DAMAGE.
276a0b9e97Skamil */
286a0b9e97Skamil
296a0b9e97Skamil #include <sys/cdefs.h>
30*67a548a8Skamil __KERNEL_RCSID(0, "$NetBSD: readhappy_mpsafe.c,v 1.2 2020/01/30 07:58:34 kamil Exp $");
316a0b9e97Skamil
326a0b9e97Skamil #include <sys/param.h>
336a0b9e97Skamil #include <sys/module.h>
346a0b9e97Skamil #include <sys/condvar.h>
356a0b9e97Skamil #include <sys/conf.h>
366a0b9e97Skamil #include <sys/device.h>
376a0b9e97Skamil #include <sys/kernel.h>
386a0b9e97Skamil #include <sys/mutex.h>
396a0b9e97Skamil
406a0b9e97Skamil /*
416a0b9e97Skamil * This module is a modification of the readhappy module to illustrate
426a0b9e97Skamil * how to make a device MPSAFE (Multiprocessor Safe).
436a0b9e97Skamil *
446a0b9e97Skamil * 1. Supports opening device by multiple processes but allows only one at a time.
456a0b9e97Skamil * 2. Supports multiple read() functions but allows only one at a time.
466a0b9e97Skamil * 3. Uses mutex for ensuring synchonization.
476a0b9e97Skamil *
486a0b9e97Skamil * Create a device /dev/happy_mpsafe from which you can read sequential happy numbers.
496a0b9e97Skamil *
506a0b9e97Skamil * To use this device you need to do:
51*67a548a8Skamil * mknod /dev/happy_mpsafe c 351 0
526a0b9e97Skamil *
536a0b9e97Skamil * To test whether the device is MPSAFE. Compile and run the test_readhappy file
546a0b9e97Skamil * provided.
556a0b9e97Skamil */
566a0b9e97Skamil
576a0b9e97Skamil
586a0b9e97Skamil #define HAPPY_NUMBER 1
596a0b9e97Skamil
606a0b9e97Skamil #define SAD_NUMBER 4
616a0b9e97Skamil
626a0b9e97Skamil /*
636a0b9e97Skamil * kmutex_t variables have been added to the structure to
646a0b9e97Skamil * ensure proper synchronization while opened by multiple devices.
656a0b9e97Skamil *
666a0b9e97Skamil * kcondvar_t conditional variable added. Boolean part added to
676a0b9e97Skamil * check whether the device is in use.
686a0b9e97Skamil */
696a0b9e97Skamil
706a0b9e97Skamil struct happy_softc {
716a0b9e97Skamil kcondvar_t cv;
726a0b9e97Skamil bool inuse_cv;
736a0b9e97Skamil unsigned last;
746a0b9e97Skamil kmutex_t lock;
756a0b9e97Skamil kmutex_t read_lock;
766a0b9e97Skamil };
776a0b9e97Skamil
786a0b9e97Skamil
796a0b9e97Skamil static struct happy_softc sc;
806a0b9e97Skamil
816a0b9e97Skamil static unsigned
dsum(unsigned n)826a0b9e97Skamil dsum(unsigned n)
836a0b9e97Skamil {
846a0b9e97Skamil unsigned sum, x;
856a0b9e97Skamil
866a0b9e97Skamil for (sum = 0; n; n /= 10) {
876a0b9e97Skamil x = n % 10;
886a0b9e97Skamil sum += x * x;
896a0b9e97Skamil }
906a0b9e97Skamil return sum;
916a0b9e97Skamil }
926a0b9e97Skamil
936a0b9e97Skamil static int
check_happy(unsigned n)946a0b9e97Skamil check_happy(unsigned n)
956a0b9e97Skamil {
966a0b9e97Skamil unsigned total;
976a0b9e97Skamil
986a0b9e97Skamil KASSERT(mutex_owned(&sc.read_lock));
996a0b9e97Skamil
1006a0b9e97Skamil for (;;) {
1016a0b9e97Skamil total = dsum(n);
1026a0b9e97Skamil
1036a0b9e97Skamil if (total == HAPPY_NUMBER)
1046a0b9e97Skamil return 1;
1056a0b9e97Skamil if (total == SAD_NUMBER)
1066a0b9e97Skamil return 0;
1076a0b9e97Skamil
1086a0b9e97Skamil n = total;
1096a0b9e97Skamil }
1106a0b9e97Skamil }
1116a0b9e97Skamil
1126a0b9e97Skamil dev_type_open(happy_open);
1136a0b9e97Skamil dev_type_close(happy_close);
1146a0b9e97Skamil dev_type_read(happy_read);
1156a0b9e97Skamil
1166a0b9e97Skamil /*
1176a0b9e97Skamil * Notice that the .d_flag has a additional D_MPSAFE flag to
1186a0b9e97Skamil * tag is as a multiprocessor safe device.
1196a0b9e97Skamil */
1206a0b9e97Skamil
1216a0b9e97Skamil static struct cdevsw happy_cdevsw = {
1226a0b9e97Skamil .d_open = happy_open,
1236a0b9e97Skamil .d_close = happy_close,
1246a0b9e97Skamil .d_read = happy_read,
1256a0b9e97Skamil .d_write = nowrite,
1266a0b9e97Skamil .d_ioctl = noioctl,
1276a0b9e97Skamil .d_stop = nostop,
1286a0b9e97Skamil .d_tty = notty,
1296a0b9e97Skamil .d_poll = nopoll,
1306a0b9e97Skamil .d_mmap = nommap,
1316a0b9e97Skamil .d_kqfilter = nokqfilter,
1326a0b9e97Skamil .d_discard = nodiscard,
1336a0b9e97Skamil .d_flag = D_OTHER | D_MPSAFE,
1346a0b9e97Skamil };
1356a0b9e97Skamil
1366a0b9e97Skamil /*
1376a0b9e97Skamil * happy_open : used to open the device for read. mutex_enter and mutex_exit:
1386a0b9e97Skamil * to lock the critical section and allow only a single process to open the
1396a0b9e97Skamil * device at a time.
1406a0b9e97Skamil */
1416a0b9e97Skamil int
happy_open(dev_t self __unused,int flag __unused,int mode __unused,struct lwp * l __unused)1426a0b9e97Skamil happy_open(dev_t self __unused, int flag __unused, int mode __unused,
1436a0b9e97Skamil struct lwp *l __unused)
1446a0b9e97Skamil {
1456a0b9e97Skamil int error;
1466a0b9e97Skamil
1476a0b9e97Skamil error = 0;
1486a0b9e97Skamil
1496a0b9e97Skamil mutex_enter(&sc.lock);
1506a0b9e97Skamil while (sc.inuse_cv == true) {
1516a0b9e97Skamil error = cv_wait_sig(&sc.cv, &sc.lock);
1526a0b9e97Skamil if (error)
1536a0b9e97Skamil break;
1546a0b9e97Skamil }
1556a0b9e97Skamil if (!error) {
1566a0b9e97Skamil sc.inuse_cv = true;
1576a0b9e97Skamil sc.last = 0;
1586a0b9e97Skamil }
1596a0b9e97Skamil mutex_exit(&sc.lock);
1606a0b9e97Skamil
1616a0b9e97Skamil return 0;
1626a0b9e97Skamil }
1636a0b9e97Skamil
1646a0b9e97Skamil /*
1656a0b9e97Skamil * happy_close allows only a single process to close the device at a time.
1666a0b9e97Skamil * It uses mutex_enter and mutex_exit for the same.
1676a0b9e97Skamil */
1686a0b9e97Skamil int
happy_close(dev_t self __unused,int flag __unused,int mode __unused,struct lwp * l __unused)1696a0b9e97Skamil happy_close(dev_t self __unused, int flag __unused, int mode __unused,
1706a0b9e97Skamil struct lwp *l __unused)
1716a0b9e97Skamil {
1726a0b9e97Skamil
1736a0b9e97Skamil mutex_enter(&sc.lock);
1746a0b9e97Skamil sc.inuse_cv = false;
1756a0b9e97Skamil cv_signal(&sc.cv);
1766a0b9e97Skamil mutex_exit(&sc.lock);
1776a0b9e97Skamil
1786a0b9e97Skamil return 0;
1796a0b9e97Skamil }
1806a0b9e97Skamil
1816a0b9e97Skamil /*
1826a0b9e97Skamil * happy_read allows only a single file descriptor to read at a point of time
1836a0b9e97Skamil * it uses mutex_enter and mutex_exit: to lock the critical section and allow
1846a0b9e97Skamil * only a single process to open the device at a time.
1856a0b9e97Skamil */
1866a0b9e97Skamil int
happy_read(dev_t self __unused,struct uio * uio,int flags __unused)1876a0b9e97Skamil happy_read(dev_t self __unused, struct uio *uio, int flags __unused)
1886a0b9e97Skamil {
1896a0b9e97Skamil char line[80];
1906a0b9e97Skamil int error, len;
1916a0b9e97Skamil
1926a0b9e97Skamil mutex_enter(&sc.read_lock);
1936a0b9e97Skamil
1946a0b9e97Skamil while (check_happy(++sc.last) == 0)
1956a0b9e97Skamil continue;
1966a0b9e97Skamil
1976a0b9e97Skamil len = snprintf(line, sizeof(line), "%u\n", sc.last);
1986a0b9e97Skamil
1996a0b9e97Skamil if (uio->uio_resid < len) {
2006a0b9e97Skamil --sc.last;
2016a0b9e97Skamil error = EINVAL;
2026a0b9e97Skamil goto fin;
2036a0b9e97Skamil }
2046a0b9e97Skamil
2056a0b9e97Skamil error = uiomove(line, len, uio);
2066a0b9e97Skamil
2076a0b9e97Skamil fin:
2086a0b9e97Skamil mutex_exit(&sc.read_lock);
2096a0b9e97Skamil
2106a0b9e97Skamil return error;
2116a0b9e97Skamil }
2126a0b9e97Skamil
2136a0b9e97Skamil MODULE(MODULE_CLASS_MISC, happy_mpsafe, NULL);
2146a0b9e97Skamil
2156a0b9e97Skamil /*
2166a0b9e97Skamil * Initializing mutex and conditional variables for read() and open().
2176a0b9e97Skamil * when the module is being loaded.
2186a0b9e97Skamil */
2196a0b9e97Skamil static int
happy_mpsafe_modcmd(modcmd_t cmd,void * arg __unused)2206a0b9e97Skamil happy_mpsafe_modcmd(modcmd_t cmd, void *arg __unused)
2216a0b9e97Skamil {
222*67a548a8Skamil int cmajor = 351, bmajor = -1;
2236a0b9e97Skamil
2246a0b9e97Skamil switch (cmd) {
2256a0b9e97Skamil case MODULE_CMD_INIT:
2266a0b9e97Skamil if (devsw_attach("happy_mpsafe", NULL, &bmajor, &happy_cdevsw,
2276a0b9e97Skamil &cmajor))
2286a0b9e97Skamil return ENXIO;
2296a0b9e97Skamil
2306a0b9e97Skamil mutex_init(&sc.lock, MUTEX_DEFAULT, IPL_NONE);
2316a0b9e97Skamil mutex_init(&sc.read_lock, MUTEX_DEFAULT, IPL_NONE);
2326a0b9e97Skamil cv_init(&sc.cv, "example conditional variable");
2336a0b9e97Skamil return 0;
2346a0b9e97Skamil case MODULE_CMD_FINI:
2356a0b9e97Skamil mutex_destroy(&sc.lock);
2366a0b9e97Skamil mutex_destroy(&sc.read_lock);
2376a0b9e97Skamil cv_destroy(&sc.cv);
2386a0b9e97Skamil devsw_detach(NULL, &happy_cdevsw);
2396a0b9e97Skamil return 0;
2406a0b9e97Skamil default:
2416a0b9e97Skamil return ENOTTY;
2426a0b9e97Skamil }
2436a0b9e97Skamil }
244