Home | History | Annotate | Line # | Download | only in hpc
      1 /*	$NetBSD: config_hook.c,v 1.12 2020/11/21 21:08:32 thorpej Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 1999-2001
      5  *         Shin Takemura and PocketBSD Project. All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. All advertising materials mentioning features or use of this software
     16  *    must display the following acknowledgement:
     17  *	This product includes software developed by the PocketBSD project
     18  *	and its contributors.
     19  * 4. Neither the name of the project nor the names of its contributors
     20  *    may be used to endorse or promote products derived from this software
     21  *    without specific prior written permission.
     22  *
     23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     33  * SUCH DAMAGE.
     34  *
     35  */
     36 
     37 #include <sys/cdefs.h>
     38 __KERNEL_RCSID(0, "$NetBSD: config_hook.c,v 1.12 2020/11/21 21:08:32 thorpej Exp $");
     39 
     40 #include <sys/param.h>
     41 #include <sys/device.h>
     42 #include <sys/kmem.h>
     43 #include <sys/systm.h>
     44 #include <sys/kernel.h>
     45 
     46 #include <machine/config_hook.h>
     47 
     48 struct hook_rec {
     49 	TAILQ_ENTRY(hook_rec) hr_link;
     50 	void *hr_ctx;
     51 	int hr_type;
     52 	long hr_id;
     53 	enum config_hook_mode hr_mode;
     54 	int (*hr_func)(void *, int, long, void *);
     55 };
     56 
     57 TAILQ_HEAD(hook_list, hook_rec);
     58 struct hook_list hook_lists[CONFIG_HOOK_NTYPES];
     59 struct hook_list call_list;
     60 
     61 void
     62 config_hook_init(void)
     63 {
     64 	int i;
     65 
     66 	for (i = 0; i < CONFIG_HOOK_NTYPES; i++) {
     67 		TAILQ_INIT(&hook_lists[i]);
     68 	}
     69 	TAILQ_INIT(&call_list);
     70 }
     71 
     72 config_hook_tag
     73 config_hook(int type, long id, enum config_hook_mode mode,
     74     int (*func)(void *, int, long, void *), void *ctx)
     75 {
     76 	struct hook_rec *hr, *cr, *prev_hr;
     77 	int s;
     78 
     79 	/* check type value */
     80 	if (type < 0 || CONFIG_HOOK_NTYPES <= type) {
     81 		panic("config_hook: invalid hook type");
     82 	}
     83 
     84 	/* check mode compatibility */
     85 	prev_hr = NULL;
     86 	TAILQ_FOREACH(hr, &hook_lists[type], hr_link) {
     87 		if (hr->hr_id == id) {
     88 			if (hr->hr_mode != mode) {
     89 				panic("config_hook: incompatible mode on "
     90 				    "type=%d/id=%ld != %d",
     91 				    type, id, hr->hr_mode);
     92 			}
     93 			prev_hr = hr;
     94 		}
     95 	}
     96 	switch (mode) {
     97 	case CONFIG_HOOK_SHARE:
     98 		/* nothing to do */
     99 		break;
    100 	case CONFIG_HOOK_REPLACE:
    101 		if (prev_hr != NULL) {
    102 			printf("config_hook: type=%d/id=%ld is replaced",
    103 			    type, id);
    104 			s = splhigh();
    105 			TAILQ_REMOVE(&hook_lists[type], prev_hr, hr_link);
    106 			TAILQ_NEXT(prev_hr, hr_link) = NULL;
    107 			splx(s);
    108 		}
    109 		break;
    110 	case CONFIG_HOOK_EXCLUSIVE:
    111 		if (prev_hr != NULL) {
    112 			panic("config_hook: type=%d/id=%ld is already "
    113 			    "hooked(%p)", type, id, prev_hr);
    114 		}
    115 		break;
    116 	default:
    117 		break;
    118 	}
    119 
    120 	/* allocate new record */
    121 	hr = kmem_alloc(sizeof(*hr), KM_SLEEP);
    122 	hr->hr_ctx = ctx;
    123 	hr->hr_type = type;
    124 	hr->hr_id = id;
    125 	hr->hr_func = func;
    126 	hr->hr_mode = mode;
    127 
    128 	s = splhigh();
    129 	TAILQ_INSERT_HEAD(&hook_lists[type], hr, hr_link);
    130 
    131 	/* update call list */
    132 	TAILQ_FOREACH(cr, &call_list, hr_link) {
    133 		if (cr->hr_type == type && cr->hr_id == id) {
    134 			if (cr->hr_func != NULL &&
    135 			    cr->hr_mode != mode) {
    136 				panic("config_hook: incompatible mode on "
    137 				    "type=%d/id=%ld != %d",
    138 				    type, id, cr->hr_mode);
    139 			}
    140 			cr->hr_ctx = ctx;
    141 			cr->hr_func = func;
    142 			cr->hr_mode = mode;
    143 		}
    144 	}
    145 	splx(s);
    146 
    147 	return (hr);
    148 }
    149 
    150 void
    151 config_unhook(config_hook_tag hrx)
    152 {
    153 	int s;
    154 	struct hook_rec *hr = (struct hook_rec*)hrx, *cr;
    155 
    156 	if (TAILQ_NEXT(hr, hr_link) != NULL) {
    157 		s = splhigh();
    158 		TAILQ_REMOVE(&hook_lists[hr->hr_type], hr, hr_link);
    159 		TAILQ_NEXT(hr, hr_link) = NULL;
    160 		/* update call list */
    161 		TAILQ_FOREACH(cr, &call_list, hr_link) {
    162 			if (cr->hr_type == hr->hr_type &&
    163 			    cr->hr_id == hr->hr_id)
    164 				cr->hr_func = NULL;
    165 		}
    166 		splx(s);
    167 	}
    168 	kmem_free(hr, sizeof(*hr));
    169 }
    170 
    171 int
    172 __config_hook_call(int type, long id, void *msg, int reverse)
    173 {
    174 	int res;
    175 	struct hook_rec *hr;
    176 
    177 	/* Check type value. */
    178 	if (type < 0 || CONFIG_HOOK_NTYPES <= type) {
    179 		panic("config_hook: invalid hook type");
    180 	}
    181 
    182 	res = -1;
    183 	if (reverse) {
    184 		TAILQ_FOREACH_REVERSE(hr, &hook_lists[type], hook_list,
    185 		    hr_link) {
    186 			if (hr->hr_id == id)
    187 				res = (*hr->hr_func)(hr->hr_ctx, type, id,msg);
    188 		}
    189 	} else {
    190 		TAILQ_FOREACH(hr, &hook_lists[type], hr_link) {
    191 			if (hr->hr_id == id)
    192 				res = (*hr->hr_func)(hr->hr_ctx, type, id,msg);
    193 		}
    194 	}
    195 
    196 	return (res);
    197 }
    198 
    199 config_hook_tag
    200 config_connect(int type, long id)
    201 {
    202 	int s;
    203 	struct hook_rec *cr, *hr;
    204 
    205 	/* check type value */
    206 	if (type < 0 || CONFIG_HOOK_NTYPES <= type) {
    207 		panic("config_hook: invalid hook type");
    208 	}
    209 
    210 	/* allocate new record */
    211 	cr = kmem_alloc(sizeof(*hr), KM_SLEEP);
    212 	cr->hr_func = NULL;
    213 	cr->hr_type = type;
    214 	cr->hr_id = id;
    215 
    216 	s = splhigh();
    217 	/* insert the record into the call list */
    218 	TAILQ_INSERT_HEAD(&call_list, cr, hr_link);
    219 
    220 	/* scan hook list */
    221 	TAILQ_FOREACH(hr, &hook_lists[type], hr_link) {
    222 		if (hr->hr_id == id) {
    223 			if (hr->hr_mode == CONFIG_HOOK_SHARE)
    224 				panic("config_connect: can't connect with "
    225 				    "shared hook, type=%d id=%ld", type, id);
    226 			cr->hr_ctx = hr->hr_ctx;
    227 			cr->hr_func = hr->hr_func;
    228 			cr->hr_mode = hr->hr_mode;
    229 		}
    230 	}
    231 	splx(s);
    232 
    233 	return (cr);
    234 }
    235 
    236 void
    237 config_disconnect(config_call_tag crx)
    238 {
    239 	int s;
    240 	struct hook_rec *cr = (struct hook_rec*)crx;
    241 
    242 	s = splhigh();
    243 	TAILQ_REMOVE(&call_list, cr, hr_link);
    244 	splx(s);
    245 
    246 	kmem_free(cr, sizeof(*cr));
    247 }
    248 
    249 int
    250 config_connected_call(config_call_tag crx, void *msg)
    251 {
    252 	int res;
    253 	struct hook_rec *cr = (struct hook_rec*)crx;
    254 
    255 	if (cr->hr_func != NULL)
    256 		res = (*cr->hr_func)(cr->hr_ctx, cr->hr_type, cr->hr_id, msg);
    257 	else
    258 		res = -1;
    259 
    260 	return (res);
    261 }
    262