| Commit | Line | Data |
|---|---|---|
| 984263bc MD |
1 | /* |
| 2 | * Copyright 1998 Massachusetts Institute of Technology | |
| 3 | * | |
| 4 | * Permission to use, copy, modify, and distribute this software and | |
| 5 | * its documentation for any purpose and without fee is hereby | |
| 6 | * granted, provided that both the above copyright notice and this | |
| 7 | * permission notice appear in all copies, that both the above | |
| 8 | * copyright notice and this permission notice appear in all | |
| 9 | * supporting documentation, and that the name of M.I.T. not be used | |
| 10 | * in advertising or publicity pertaining to distribution of the | |
| 11 | * software without specific, written prior permission. M.I.T. makes | |
| 12 | * no representations about the suitability of this software for any | |
| 13 | * purpose. It is provided "as is" without express or implied | |
| 14 | * warranty. | |
| 15 | * | |
| 16 | * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS | |
| 17 | * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, | |
| 18 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
| 19 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT | |
| 20 | * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| 21 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
| 22 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | |
| 23 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
| 24 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |
| 25 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |
| 26 | * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| 27 | * SUCH DAMAGE. | |
| 28 | * | |
| 29 | * $FreeBSD: src/sys/kern/subr_rman.c,v 1.10.2.1 2001/06/05 08:06:08 imp Exp $ | |
| 0010e23a | 30 | * $DragonFly: src/sys/kern/subr_rman.c,v 1.15 2008/09/30 12:20:29 hasso Exp $ |
| 984263bc MD |
31 | */ |
| 32 | ||
| 33 | /* | |
| 34 | * The kernel resource manager. This code is responsible for keeping track | |
| 35 | * of hardware resources which are apportioned out to various drivers. | |
| 36 | * It does not actually assign those resources, and it is not expected | |
| 37 | * that end-device drivers will call into this code directly. Rather, | |
| 38 | * the code which implements the buses that those devices are attached to, | |
| 39 | * and the code which manages CPU resources, will call this code, and the | |
| 40 | * end-device drivers will make upcalls to that code to actually perform | |
| 41 | * the allocation. | |
| 42 | * | |
| 43 | * There are two sorts of resources managed by this code. The first is | |
| 44 | * the more familiar array (RMAN_ARRAY) type; resources in this class | |
| 45 | * consist of a sequence of individually-allocatable objects which have | |
| 46 | * been numbered in some well-defined order. Most of the resources | |
| 47 | * are of this type, as it is the most familiar. The second type is | |
| 48 | * called a gauge (RMAN_GAUGE), and models fungible resources (i.e., | |
| 49 | * resources in which each instance is indistinguishable from every | |
| 50 | * other instance). The principal anticipated application of gauges | |
| 51 | * is in the context of power consumption, where a bus may have a specific | |
| 52 | * power budget which all attached devices share. RMAN_GAUGE is not | |
| 53 | * implemented yet. | |
| 54 | * | |
| 55 | * For array resources, we make one simplifying assumption: two clients | |
| 56 | * sharing the same resource must use the same range of indices. That | |
| 57 | * is to say, sharing of overlapping-but-not-identical regions is not | |
| 58 | * permitted. | |
| 59 | */ | |
| 60 | ||
| 61 | #include <sys/param.h> | |
| 62 | #include <sys/systm.h> | |
| 63 | #include <sys/kernel.h> | |
| 64 | #include <sys/lock.h> | |
| 65 | #include <sys/malloc.h> | |
| 66 | #include <sys/bus.h> /* XXX debugging */ | |
| 984263bc | 67 | #include <sys/rman.h> |
| d58bc8c4 | 68 | #include <sys/sysctl.h> |
| 984263bc | 69 | |
| d58bc8c4 YT |
70 | int rman_debug = 0; |
| 71 | TUNABLE_INT("debug.rman_debug", &rman_debug); | |
| 72 | SYSCTL_INT(_debug, OID_AUTO, rman_debug, CTLFLAG_RW, | |
| 73 | &rman_debug, 0, "rman debug"); | |
| 74 | ||
| 75 | #define DPRINTF(params) if (rman_debug) kprintf params | |
| fa1e3078 | 76 | |
| 984263bc MD |
77 | static MALLOC_DEFINE(M_RMAN, "rman", "Resource manager"); |
| 78 | ||
| 79 | struct rman_head rman_head; | |
| 8a8d5d85 | 80 | static struct lwkt_token rman_tok; /* mutex to protect rman_head */ |
| 984263bc MD |
81 | static int int_rman_activate_resource(struct rman *rm, struct resource *r, |
| 82 | struct resource **whohas); | |
| 83 | static int int_rman_deactivate_resource(struct resource *r); | |
| 84 | static int int_rman_release_resource(struct rman *rm, struct resource *r); | |
| 85 | ||
| 984263bc MD |
86 | int |
| 87 | rman_init(struct rman *rm) | |
| 88 | { | |
| 89 | static int once; | |
| 90 | ||
| 91 | if (once == 0) { | |
| 92 | once = 1; | |
| 93 | TAILQ_INIT(&rman_head); | |
| 3b998fa9 | 94 | lwkt_token_init(&rman_tok, 1); |
| 984263bc MD |
95 | } |
| 96 | ||
| 97 | if (rm->rm_type == RMAN_UNINIT) | |
| 98 | panic("rman_init"); | |
| 99 | if (rm->rm_type == RMAN_GAUGE) | |
| 100 | panic("implement RMAN_GAUGE"); | |
| 101 | ||
| ecd27a2e | 102 | TAILQ_INIT(&rm->rm_list); |
| efda3bd0 | 103 | rm->rm_slock = kmalloc(sizeof *rm->rm_slock, M_RMAN, M_NOWAIT); |
| 8a8d5d85 | 104 | if (rm->rm_slock == NULL) |
| 984263bc | 105 | return ENOMEM; |
| 3b998fa9 | 106 | lwkt_token_init(rm->rm_slock, 1); |
| 984263bc | 107 | |
| 3b998fa9 | 108 | lwkt_gettoken(&rman_tok); |
| 984263bc | 109 | TAILQ_INSERT_TAIL(&rman_head, rm, rm_link); |
| 3b998fa9 | 110 | lwkt_reltoken(&rman_tok); |
| 984263bc MD |
111 | return 0; |
| 112 | } | |
| 113 | ||
| 114 | /* | |
| 115 | * NB: this interface is not robust against programming errors which | |
| 116 | * add multiple copies of the same region. | |
| 117 | */ | |
| 118 | int | |
| 119 | rman_manage_region(struct rman *rm, u_long start, u_long end) | |
| 120 | { | |
| 121 | struct resource *r, *s; | |
| 122 | ||
| fa1e3078 YT |
123 | DPRINTF(("rman_manage_region: <%s> request: start %#lx, end %#lx\n", |
| 124 | rm->rm_descr, start, end)); | |
| e7b4468c | 125 | r = kmalloc(sizeof *r, M_RMAN, M_NOWAIT | M_ZERO); |
| 984263bc MD |
126 | if (r == 0) |
| 127 | return ENOMEM; | |
| 984263bc MD |
128 | r->r_sharehead = 0; |
| 129 | r->r_start = start; | |
| 130 | r->r_end = end; | |
| 131 | r->r_flags = 0; | |
| 132 | r->r_dev = 0; | |
| 133 | r->r_rm = rm; | |
| 134 | ||
| 3b998fa9 | 135 | lwkt_gettoken(rm->rm_slock); |
| ecd27a2e PA |
136 | for (s = TAILQ_FIRST(&rm->rm_list); |
| 137 | s && s->r_end < r->r_start; | |
| 138 | s = TAILQ_NEXT(s, r_link)) | |
| 984263bc MD |
139 | ; |
| 140 | ||
| ecd27a2e PA |
141 | if (s == NULL) |
| 142 | TAILQ_INSERT_TAIL(&rm->rm_list, r, r_link); | |
| 143 | else | |
| 144 | TAILQ_INSERT_BEFORE(s, r, r_link); | |
| 984263bc | 145 | |
| 3b998fa9 | 146 | lwkt_reltoken(rm->rm_slock); |
| 984263bc MD |
147 | return 0; |
| 148 | } | |
| 149 | ||
| 150 | int | |
| 151 | rman_fini(struct rman *rm) | |
| 152 | { | |
| 153 | struct resource *r; | |
| 154 | ||
| 3b998fa9 | 155 | lwkt_gettoken(rm->rm_slock); |
| ecd27a2e | 156 | TAILQ_FOREACH(r, &rm->rm_list, r_link) { |
| 984263bc | 157 | if (r->r_flags & RF_ALLOCATED) { |
| 3b998fa9 | 158 | lwkt_reltoken(rm->rm_slock); |
| 984263bc MD |
159 | return EBUSY; |
| 160 | } | |
| 161 | } | |
| 162 | ||
| 163 | /* | |
| 164 | * There really should only be one of these if we are in this | |
| 165 | * state and the code is working properly, but it can't hurt. | |
| 166 | */ | |
| ecd27a2e PA |
167 | while (!TAILQ_EMPTY(&rm->rm_list)) { |
| 168 | r = TAILQ_FIRST(&rm->rm_list); | |
| 169 | TAILQ_REMOVE(&rm->rm_list, r, r_link); | |
| efda3bd0 | 170 | kfree(r, M_RMAN); |
| 984263bc | 171 | } |
| 3b998fa9 MD |
172 | lwkt_reltoken(rm->rm_slock); |
| 173 | ||
| 41a01a4d | 174 | /* XXX what's the point of this if we are going to free the struct? */ |
| 3b998fa9 | 175 | lwkt_gettoken(&rman_tok); |
| 984263bc | 176 | TAILQ_REMOVE(&rman_head, rm, rm_link); |
| 3b998fa9 | 177 | lwkt_reltoken(&rman_tok); |
| efda3bd0 | 178 | kfree(rm->rm_slock, M_RMAN); |
| 984263bc MD |
179 | |
| 180 | return 0; | |
| 181 | } | |
| 182 | ||
| 183 | struct resource * | |
| 184 | rman_reserve_resource(struct rman *rm, u_long start, u_long end, u_long count, | |
| 185 | u_int flags, struct device *dev) | |
| 186 | { | |
| 187 | u_int want_activate; | |
| 188 | struct resource *r, *s, *rv; | |
| 189 | u_long rstart, rend; | |
| 190 | ||
| 191 | rv = 0; | |
| 192 | ||
| fa1e3078 YT |
193 | DPRINTF(("rman_reserve_resource: <%s> request: [%#lx, %#lx], length " |
| 194 | "%#lx, flags %u, device %s\n", rm->rm_descr, start, end, | |
| 195 | count, flags, | |
| 196 | dev == NULL ? "<null>" : device_get_nameunit(dev))); | |
| 984263bc MD |
197 | want_activate = (flags & RF_ACTIVE); |
| 198 | flags &= ~RF_ACTIVE; | |
| 199 | ||
| 3b998fa9 | 200 | lwkt_gettoken(rm->rm_slock); |
| 984263bc | 201 | |
| ecd27a2e PA |
202 | for (r = TAILQ_FIRST(&rm->rm_list); |
| 203 | r && r->r_end < start; | |
| 204 | r = TAILQ_NEXT(r, r_link)) | |
| 984263bc MD |
205 | ; |
| 206 | ||
| ecd27a2e | 207 | if (r == NULL) { |
| fa1e3078 | 208 | DPRINTF(("could not find a region\n")); |
| 984263bc MD |
209 | goto out; |
| 210 | } | |
| 211 | ||
| 212 | /* | |
| 213 | * First try to find an acceptable totally-unshared region. | |
| 214 | */ | |
| ecd27a2e | 215 | for (s = r; s; s = TAILQ_NEXT(s, r_link)) { |
| fa1e3078 | 216 | DPRINTF(("considering [%#lx, %#lx]\n", s->r_start, s->r_end)); |
| 984263bc | 217 | if (s->r_start > end) { |
| fa1e3078 YT |
218 | DPRINTF(("s->r_start (%#lx) > end (%#lx)\n", |
| 219 | s->r_start, end)); | |
| 984263bc MD |
220 | break; |
| 221 | } | |
| 222 | if (s->r_flags & RF_ALLOCATED) { | |
| fa1e3078 | 223 | DPRINTF(("region is allocated\n")); |
| 984263bc MD |
224 | continue; |
| 225 | } | |
| 226 | rstart = max(s->r_start, start); | |
| 227 | rstart = (rstart + ((1ul << RF_ALIGNMENT(flags))) - 1) & | |
| 228 | ~((1ul << RF_ALIGNMENT(flags)) - 1); | |
| 229 | rend = min(s->r_end, max(start + count, end)); | |
| fa1e3078 YT |
230 | DPRINTF(("truncated region: [%#lx, %#lx]; size %#lx (requested %#lx)\n", |
| 231 | rstart, rend, (rend - rstart + 1), count)); | |
| 984263bc MD |
232 | |
| 233 | if ((rend - rstart + 1) >= count) { | |
| fa1e3078 YT |
234 | DPRINTF(("candidate region: [%#lx, %#lx], size %#lx\n", |
| 235 | rstart, rend, (rend - rstart + 1))); | |
| 984263bc | 236 | if ((s->r_end - s->r_start + 1) == count) { |
| fa1e3078 | 237 | DPRINTF(("candidate region is entire chunk\n")); |
| 984263bc MD |
238 | rv = s; |
| 239 | rv->r_flags |= RF_ALLOCATED | flags; | |
| 240 | rv->r_dev = dev; | |
| 241 | goto out; | |
| 242 | } | |
| 243 | ||
| 244 | /* | |
| 245 | * If s->r_start < rstart and | |
| 246 | * s->r_end > rstart + count - 1, then | |
| 247 | * we need to split the region into three pieces | |
| 248 | * (the middle one will get returned to the user). | |
| 249 | * Otherwise, we are allocating at either the | |
| 250 | * beginning or the end of s, so we only need to | |
| 251 | * split it in two. The first case requires | |
| 252 | * two new allocations; the second requires but one. | |
| 253 | */ | |
| e7b4468c | 254 | rv = kmalloc(sizeof *rv, M_RMAN, M_NOWAIT | M_ZERO); |
| 984263bc MD |
255 | if (rv == 0) |
| 256 | goto out; | |
| 984263bc MD |
257 | rv->r_start = rstart; |
| 258 | rv->r_end = rstart + count - 1; | |
| 259 | rv->r_flags = flags | RF_ALLOCATED; | |
| 260 | rv->r_dev = dev; | |
| 261 | rv->r_sharehead = 0; | |
| 262 | rv->r_rm = rm; | |
| 263 | ||
| 264 | if (s->r_start < rv->r_start && s->r_end > rv->r_end) { | |
| fa1e3078 | 265 | DPRINTF(("splitting region in three parts: " |
| 984263bc MD |
266 | "[%#lx, %#lx]; [%#lx, %#lx]; [%#lx, %#lx]\n", |
| 267 | s->r_start, rv->r_start - 1, | |
| 268 | rv->r_start, rv->r_end, | |
| fa1e3078 | 269 | rv->r_end + 1, s->r_end)); |
| 984263bc MD |
270 | /* |
| 271 | * We are allocating in the middle. | |
| 272 | */ | |
| e7b4468c SW |
273 | r = kmalloc(sizeof *r, M_RMAN, |
| 274 | M_NOWAIT | M_ZERO); | |
| 984263bc | 275 | if (r == 0) { |
| efda3bd0 | 276 | kfree(rv, M_RMAN); |
| 984263bc MD |
277 | rv = 0; |
| 278 | goto out; | |
| 279 | } | |
| 984263bc MD |
280 | r->r_start = rv->r_end + 1; |
| 281 | r->r_end = s->r_end; | |
| 282 | r->r_flags = s->r_flags; | |
| 283 | r->r_dev = 0; | |
| 284 | r->r_sharehead = 0; | |
| 285 | r->r_rm = rm; | |
| 286 | s->r_end = rv->r_start - 1; | |
| ecd27a2e | 287 | TAILQ_INSERT_AFTER(&rm->rm_list, s, rv, |
| 984263bc | 288 | r_link); |
| ecd27a2e | 289 | TAILQ_INSERT_AFTER(&rm->rm_list, rv, r, |
| 984263bc MD |
290 | r_link); |
| 291 | } else if (s->r_start == rv->r_start) { | |
| fa1e3078 | 292 | DPRINTF(("allocating from the beginning\n")); |
| 984263bc MD |
293 | /* |
| 294 | * We are allocating at the beginning. | |
| 295 | */ | |
| 296 | s->r_start = rv->r_end + 1; | |
| ecd27a2e | 297 | TAILQ_INSERT_BEFORE(s, rv, r_link); |
| 984263bc | 298 | } else { |
| fa1e3078 | 299 | DPRINTF(("allocating at the end\n")); |
| 984263bc MD |
300 | /* |
| 301 | * We are allocating at the end. | |
| 302 | */ | |
| 303 | s->r_end = rv->r_start - 1; | |
| ecd27a2e | 304 | TAILQ_INSERT_AFTER(&rm->rm_list, s, rv, |
| 984263bc MD |
305 | r_link); |
| 306 | } | |
| 307 | goto out; | |
| 308 | } | |
| 309 | } | |
| 310 | ||
| 311 | /* | |
| 312 | * Now find an acceptable shared region, if the client's requirements | |
| 313 | * allow sharing. By our implementation restriction, a candidate | |
| 314 | * region must match exactly by both size and sharing type in order | |
| 315 | * to be considered compatible with the client's request. (The | |
| 316 | * former restriction could probably be lifted without too much | |
| 317 | * additional work, but this does not seem warranted.) | |
| 318 | */ | |
| fa1e3078 | 319 | DPRINTF(("no unshared regions found\n")); |
| 984263bc MD |
320 | if ((flags & (RF_SHAREABLE | RF_TIMESHARE)) == 0) |
| 321 | goto out; | |
| 322 | ||
| ecd27a2e | 323 | for (s = r; s; s = TAILQ_NEXT(s, r_link)) { |
| 984263bc MD |
324 | if (s->r_start > end) |
| 325 | break; | |
| 326 | if ((s->r_flags & flags) != flags) | |
| 327 | continue; | |
| 328 | rstart = max(s->r_start, start); | |
| 329 | rend = min(s->r_end, max(start + count, end)); | |
| 330 | if (s->r_start >= start && s->r_end <= end | |
| 331 | && (s->r_end - s->r_start + 1) == count) { | |
| e7b4468c | 332 | rv = kmalloc(sizeof *rv, M_RMAN, M_NOWAIT | M_ZERO); |
| 984263bc MD |
333 | if (rv == 0) |
| 334 | goto out; | |
| 984263bc MD |
335 | rv->r_start = s->r_start; |
| 336 | rv->r_end = s->r_end; | |
| 337 | rv->r_flags = s->r_flags & | |
| 338 | (RF_ALLOCATED | RF_SHAREABLE | RF_TIMESHARE); | |
| 339 | rv->r_dev = dev; | |
| 340 | rv->r_rm = rm; | |
| 341 | if (s->r_sharehead == 0) { | |
| 77652cad | 342 | s->r_sharehead = kmalloc(sizeof *s->r_sharehead, |
| e7b4468c SW |
343 | M_RMAN, |
| 344 | M_NOWAIT | M_ZERO); | |
| 984263bc | 345 | if (s->r_sharehead == 0) { |
| efda3bd0 | 346 | kfree(rv, M_RMAN); |
| 984263bc MD |
347 | rv = 0; |
| 348 | goto out; | |
| 349 | } | |
| 984263bc MD |
350 | LIST_INIT(s->r_sharehead); |
| 351 | LIST_INSERT_HEAD(s->r_sharehead, s, | |
| 352 | r_sharelink); | |
| 353 | s->r_flags |= RF_FIRSTSHARE; | |
| 354 | } | |
| 355 | rv->r_sharehead = s->r_sharehead; | |
| 356 | LIST_INSERT_HEAD(s->r_sharehead, rv, r_sharelink); | |
| 357 | goto out; | |
| 358 | } | |
| 359 | } | |
| 360 | ||
| 361 | /* | |
| 362 | * We couldn't find anything. | |
| 363 | */ | |
| 364 | out: | |
| 365 | /* | |
| 366 | * If the user specified RF_ACTIVE in the initial flags, | |
| 367 | * which is reflected in `want_activate', we attempt to atomically | |
| 368 | * activate the resource. If this fails, we release the resource | |
| 369 | * and indicate overall failure. (This behavior probably doesn't | |
| 370 | * make sense for RF_TIMESHARE-type resources.) | |
| 371 | */ | |
| 372 | if (rv && want_activate) { | |
| 373 | struct resource *whohas; | |
| 374 | if (int_rman_activate_resource(rm, rv, &whohas)) { | |
| 375 | int_rman_release_resource(rm, rv); | |
| 376 | rv = 0; | |
| 377 | } | |
| 378 | } | |
| 3b998fa9 | 379 | lwkt_reltoken(rm->rm_slock); |
| 984263bc MD |
380 | return (rv); |
| 381 | } | |
| 382 | ||
| 383 | static int | |
| 384 | int_rman_activate_resource(struct rman *rm, struct resource *r, | |
| 385 | struct resource **whohas) | |
| 386 | { | |
| 387 | struct resource *s; | |
| 388 | int ok; | |
| 389 | ||
| 390 | /* | |
| 391 | * If we are not timesharing, then there is nothing much to do. | |
| 392 | * If we already have the resource, then there is nothing at all to do. | |
| 393 | * If we are not on a sharing list with anybody else, then there is | |
| 394 | * little to do. | |
| 395 | */ | |
| 396 | if ((r->r_flags & RF_TIMESHARE) == 0 | |
| 397 | || (r->r_flags & RF_ACTIVE) != 0 | |
| 398 | || r->r_sharehead == 0) { | |
| 399 | r->r_flags |= RF_ACTIVE; | |
| 400 | return 0; | |
| 401 | } | |
| 402 | ||
| 403 | ok = 1; | |
| 404 | for (s = LIST_FIRST(r->r_sharehead); s && ok; | |
| 405 | s = LIST_NEXT(s, r_sharelink)) { | |
| 406 | if ((s->r_flags & RF_ACTIVE) != 0) { | |
| 407 | ok = 0; | |
| 408 | *whohas = s; | |
| 409 | } | |
| 410 | } | |
| 411 | if (ok) { | |
| 412 | r->r_flags |= RF_ACTIVE; | |
| 413 | return 0; | |
| 414 | } | |
| 415 | return EBUSY; | |
| 416 | } | |
| 417 | ||
| 418 | int | |
| 419 | rman_activate_resource(struct resource *r) | |
| 420 | { | |
| 421 | int rv; | |
| 422 | struct resource *whohas; | |
| 423 | struct rman *rm; | |
| 424 | ||
| 425 | rm = r->r_rm; | |
| 3b998fa9 | 426 | lwkt_gettoken(rm->rm_slock); |
| 984263bc | 427 | rv = int_rman_activate_resource(rm, r, &whohas); |
| 3b998fa9 | 428 | lwkt_reltoken(rm->rm_slock); |
| 984263bc MD |
429 | return rv; |
| 430 | } | |
| 431 | ||
| 41a01a4d MD |
432 | #if 0 |
| 433 | ||
| 434 | /* XXX */ | |
| 984263bc | 435 | int |
| 3b998fa9 | 436 | rman_await_resource(struct resource *r, int slpflags, int timo) |
| 984263bc | 437 | { |
| e43a034f | 438 | int rv; |
| 984263bc MD |
439 | struct resource *whohas; |
| 440 | struct rman *rm; | |
| 441 | ||
| 442 | rm = r->r_rm; | |
| 443 | for (;;) { | |
| 3b998fa9 | 444 | lwkt_gettoken(rm->rm_slock); |
| 984263bc MD |
445 | rv = int_rman_activate_resource(rm, r, &whohas); |
| 446 | if (rv != EBUSY) | |
| 41a01a4d | 447 | return (rv); /* returns with ilock held */ |
| 984263bc MD |
448 | |
| 449 | if (r->r_sharehead == 0) | |
| 450 | panic("rman_await_resource"); | |
| 451 | /* | |
| e43a034f MD |
452 | * A critical section will hopefully will prevent a race |
| 453 | * between lwkt_reltoken and tsleep where a process | |
| 984263bc | 454 | * could conceivably get in and release the resource |
| 8a8d5d85 | 455 | * before we have a chance to sleep on it. YYY |
| 984263bc | 456 | */ |
| e43a034f | 457 | crit_enter(); |
| 984263bc | 458 | whohas->r_flags |= RF_WANTED; |
| 377d4740 | 459 | rv = tsleep(r->r_sharehead, slpflags, "rmwait", timo); |
| 984263bc | 460 | if (rv) { |
| 3b998fa9 | 461 | lwkt_reltoken(rm->rm_slock); |
| e43a034f | 462 | crit_exit(); |
| 984263bc MD |
463 | return rv; |
| 464 | } | |
| e43a034f | 465 | crit_exit(); |
| 984263bc MD |
466 | } |
| 467 | } | |
| 468 | ||
| 41a01a4d MD |
469 | #endif |
| 470 | ||
| 984263bc MD |
471 | static int |
| 472 | int_rman_deactivate_resource(struct resource *r) | |
| 473 | { | |
| 474 | struct rman *rm; | |
| 475 | ||
| 476 | rm = r->r_rm; | |
| 477 | r->r_flags &= ~RF_ACTIVE; | |
| 478 | if (r->r_flags & RF_WANTED) { | |
| 479 | r->r_flags &= ~RF_WANTED; | |
| 480 | wakeup(r->r_sharehead); | |
| 481 | } | |
| 482 | return 0; | |
| 483 | } | |
| 484 | ||
| 485 | int | |
| 486 | rman_deactivate_resource(struct resource *r) | |
| 487 | { | |
| 41a01a4d | 488 | struct rman *rm; |
| 984263bc MD |
489 | |
| 490 | rm = r->r_rm; | |
| 3b998fa9 | 491 | lwkt_gettoken(rm->rm_slock); |
| 984263bc | 492 | int_rman_deactivate_resource(r); |
| 3b998fa9 | 493 | lwkt_reltoken(rm->rm_slock); |
| 984263bc MD |
494 | return 0; |
| 495 | } | |
| 496 | ||
| 497 | static int | |
| 498 | int_rman_release_resource(struct rman *rm, struct resource *r) | |
| 499 | { | |
| 500 | struct resource *s, *t; | |
| 501 | ||
| 502 | if (r->r_flags & RF_ACTIVE) | |
| 503 | int_rman_deactivate_resource(r); | |
| 504 | ||
| 505 | /* | |
| 506 | * Check for a sharing list first. If there is one, then we don't | |
| 507 | * have to think as hard. | |
| 508 | */ | |
| 509 | if (r->r_sharehead) { | |
| 510 | /* | |
| 511 | * If a sharing list exists, then we know there are at | |
| 512 | * least two sharers. | |
| 513 | * | |
| 514 | * If we are in the main circleq, appoint someone else. | |
| 515 | */ | |
| 516 | LIST_REMOVE(r, r_sharelink); | |
| 517 | s = LIST_FIRST(r->r_sharehead); | |
| 518 | if (r->r_flags & RF_FIRSTSHARE) { | |
| 519 | s->r_flags |= RF_FIRSTSHARE; | |
| ecd27a2e PA |
520 | TAILQ_INSERT_BEFORE(r, s, r_link); |
| 521 | TAILQ_REMOVE(&rm->rm_list, r, r_link); | |
| 984263bc MD |
522 | } |
| 523 | ||
| 524 | /* | |
| 525 | * Make sure that the sharing list goes away completely | |
| 526 | * if the resource is no longer being shared at all. | |
| 527 | */ | |
| 528 | if (LIST_NEXT(s, r_sharelink) == 0) { | |
| efda3bd0 | 529 | kfree(s->r_sharehead, M_RMAN); |
| 984263bc MD |
530 | s->r_sharehead = 0; |
| 531 | s->r_flags &= ~RF_FIRSTSHARE; | |
| 532 | } | |
| 533 | goto out; | |
| 534 | } | |
| 535 | ||
| 536 | /* | |
| 537 | * Look at the adjacent resources in the list and see if our | |
| 538 | * segment can be merged with any of them. | |
| 539 | */ | |
| ecd27a2e PA |
540 | s = TAILQ_PREV(r, resource_head, r_link); |
| 541 | t = TAILQ_NEXT(r, r_link); | |
| 984263bc | 542 | |
| ecd27a2e PA |
543 | if (s != NULL && (s->r_flags & RF_ALLOCATED) == 0 |
| 544 | && t != NULL && (t->r_flags & RF_ALLOCATED) == 0) { | |
| 984263bc MD |
545 | /* |
| 546 | * Merge all three segments. | |
| 547 | */ | |
| 548 | s->r_end = t->r_end; | |
| ecd27a2e PA |
549 | TAILQ_REMOVE(&rm->rm_list, r, r_link); |
| 550 | TAILQ_REMOVE(&rm->rm_list, t, r_link); | |
| efda3bd0 | 551 | kfree(t, M_RMAN); |
| ecd27a2e | 552 | } else if (s != NULL && (s->r_flags & RF_ALLOCATED) == 0) { |
| 984263bc MD |
553 | /* |
| 554 | * Merge previous segment with ours. | |
| 555 | */ | |
| 556 | s->r_end = r->r_end; | |
| ecd27a2e PA |
557 | TAILQ_REMOVE(&rm->rm_list, r, r_link); |
| 558 | } else if (t != NULL && (t->r_flags & RF_ALLOCATED) == 0) { | |
| 984263bc MD |
559 | /* |
| 560 | * Merge next segment with ours. | |
| 561 | */ | |
| 562 | t->r_start = r->r_start; | |
| ecd27a2e | 563 | TAILQ_REMOVE(&rm->rm_list, r, r_link); |
| 984263bc MD |
564 | } else { |
| 565 | /* | |
| 566 | * At this point, we know there is nothing we | |
| 567 | * can potentially merge with, because on each | |
| 568 | * side, there is either nothing there or what is | |
| 569 | * there is still allocated. In that case, we don't | |
| 570 | * want to remove r from the list; we simply want to | |
| 571 | * change it to an unallocated region and return | |
| 572 | * without freeing anything. | |
| 573 | */ | |
| 574 | r->r_flags &= ~RF_ALLOCATED; | |
| 575 | return 0; | |
| 576 | } | |
| 577 | ||
| 578 | out: | |
| efda3bd0 | 579 | kfree(r, M_RMAN); |
| 984263bc MD |
580 | return 0; |
| 581 | } | |
| 582 | ||
| 583 | int | |
| 584 | rman_release_resource(struct resource *r) | |
| 585 | { | |
| 984263bc | 586 | struct rman *rm = r->r_rm; |
| 41a01a4d | 587 | int rv; |
| 984263bc | 588 | |
| 3b998fa9 | 589 | lwkt_gettoken(rm->rm_slock); |
| 984263bc | 590 | rv = int_rman_release_resource(rm, r); |
| 3b998fa9 | 591 | lwkt_reltoken(rm->rm_slock); |
| 984263bc MD |
592 | return (rv); |
| 593 | } | |
| 594 | ||
| 595 | uint32_t | |
| 596 | rman_make_alignment_flags(uint32_t size) | |
| 597 | { | |
| 598 | int i; | |
| 599 | ||
| 600 | /* | |
| 601 | * Find the hightest bit set, and add one if more than one bit | |
| 602 | * set. We're effectively computing the ceil(log2(size)) here. | |
| 603 | */ | |
| 604 | for (i = 32; i > 0; i--) | |
| 605 | if ((1 << i) & size) | |
| 606 | break; | |
| 607 | if (~(1 << i) & size) | |
| 608 | i++; | |
| 609 | ||
| 610 | return(RF_ALIGNMENT_LOG2(i)); | |
| 611 | } | |
| 0010e23a HT |
612 | |
| 613 | /* | |
| 614 | * Sysctl interface for scanning the resource lists. | |
| 615 | * | |
| 616 | * We take two input parameters; the index into the list of resource | |
| 617 | * managers, and the resource offset into the list. | |
| 618 | */ | |
| 619 | static int | |
| 620 | sysctl_rman(SYSCTL_HANDLER_ARGS) | |
| 621 | { | |
| 622 | int *name = (int *)arg1; | |
| 623 | u_int namelen = arg2; | |
| 624 | int rman_idx, res_idx; | |
| 625 | struct rman *rm; | |
| 626 | struct resource *res; | |
| 627 | struct u_rman urm; | |
| 628 | struct u_resource ures; | |
| 629 | int error; | |
| 630 | ||
| 631 | if (namelen != 3) | |
| 632 | return (EINVAL); | |
| 633 | ||
| 634 | if (bus_data_generation_check(name[0])) | |
| 635 | return (EINVAL); | |
| 636 | rman_idx = name[1]; | |
| 637 | res_idx = name[2]; | |
| 638 | ||
| 639 | /* | |
| 640 | * Find the indexed resource manager | |
| 641 | */ | |
| 642 | TAILQ_FOREACH(rm, &rman_head, rm_link) { | |
| 643 | if (rman_idx-- == 0) | |
| 644 | break; | |
| 645 | } | |
| 646 | if (rm == NULL) | |
| 647 | return (ENOENT); | |
| 648 | ||
| 649 | /* | |
| 650 | * If the resource index is -1, we want details on the | |
| 651 | * resource manager. | |
| 652 | */ | |
| 653 | if (res_idx == -1) { | |
| 654 | urm.rm_handle = (uintptr_t)rm; | |
| 655 | strlcpy(urm.rm_descr, rm->rm_descr, RM_TEXTLEN); | |
| 656 | urm.rm_start = rm->rm_start; | |
| 657 | urm.rm_size = rm->rm_end - rm->rm_start + 1; | |
| 658 | urm.rm_type = rm->rm_type; | |
| 659 | ||
| 660 | error = SYSCTL_OUT(req, &urm, sizeof(urm)); | |
| 661 | return (error); | |
| 662 | } | |
| 663 | ||
| 664 | /* | |
| 665 | * Find the indexed resource and return it. | |
| 666 | */ | |
| ecd27a2e | 667 | TAILQ_FOREACH(res, &rm->rm_list, r_link) { |
| 0010e23a HT |
668 | if (res_idx-- == 0) { |
| 669 | ures.r_handle = (uintptr_t)res; | |
| 670 | ures.r_parent = (uintptr_t)res->r_rm; | |
| 671 | ures.r_device = (uintptr_t)res->r_dev; | |
| 672 | if (res->r_dev != NULL) { | |
| 673 | if (device_get_name(res->r_dev) != NULL) { | |
| 674 | ksnprintf(ures.r_devname, RM_TEXTLEN, | |
| 675 | "%s%d", | |
| 676 | device_get_name(res->r_dev), | |
| 677 | device_get_unit(res->r_dev)); | |
| 678 | } else { | |
| 679 | strlcpy(ures.r_devname, "nomatch", | |
| 680 | RM_TEXTLEN); | |
| 681 | } | |
| 682 | } else { | |
| 683 | ures.r_devname[0] = '\0'; | |
| 684 | } | |
| 685 | ures.r_start = res->r_start; | |
| 686 | ures.r_size = res->r_end - res->r_start + 1; | |
| 687 | ures.r_flags = res->r_flags; | |
| 688 | ||
| 689 | error = SYSCTL_OUT(req, &ures, sizeof(ures)); | |
| 690 | return (error); | |
| 691 | } | |
| 692 | } | |
| 693 | return (ENOENT); | |
| 694 | } | |
| 695 | ||
| 696 | SYSCTL_NODE(_hw_bus, OID_AUTO, rman, CTLFLAG_RD, sysctl_rman, | |
| 697 | "kernel resource manager"); |