registry.c 24.3 KB
Newer Older
1 2
/*
 * Copyright (C) 2004 Philippe Gerum <rpm@xenomai.org>
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

19 20 21 22 23 24 25 26 27
#include <linux/slab.h>
#include <cobalt/kernel/sched.h>
#include <cobalt/kernel/heap.h>
#include <cobalt/kernel/registry.h>
#include <cobalt/kernel/thread.h>
#include <cobalt/kernel/apc.h>
#include <cobalt/kernel/assert.h>

/**
28 29
 * @ingroup cobalt_core
 * @defgroup cobalt_core_registry Registry services
30 31 32 33 34 35 36 37 38
 *
 * The registry provides a mean to index object descriptors on unique
 * alphanumeric keys. When labeled this way, an object is globally
 * exported; it can be searched for, and its descriptor returned to
 * the caller for further use; the latter operation is called a
 * "binding". When no object has been registered under the given name
 * yet, the registry can be asked to set up a rendez-vous, blocking
 * the caller until the object is eventually registered.
 *
39 40
 *@{
 */
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
41

42 43
struct xnobject *registry_obj_slots;
EXPORT_SYMBOL_GPL(registry_obj_slots);
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
44

45
static LIST_HEAD(free_object_list); /* Free objects. */
46

47
static LIST_HEAD(busy_object_list); /* Active and exported objects. */
48

49
static unsigned int nr_active_objects;
50

51
static unsigned long next_object_stamp;
52

53
static struct hlist_head *object_index;
54

55 56 57
static int nr_object_entries;

static struct xnsynch register_synch;
58 59 60 61

#ifdef CONFIG_XENO_OPT_VFILE

#include <linux/workqueue.h>
62

63
static void proc_callback(struct work_struct *work);
64 65 66

static void registry_proc_schedule(void *cookie);

67
static LIST_HEAD(proc_object_list);	/* Objects waiting for /proc handling. */
68

69
static DECLARE_WORK(registry_proc_work, proc_callback);
70

71
static int proc_apc;
72 73 74 75 76

static struct xnvfile_directory registry_vfroot;

static int usage_vfile_show(struct xnvfile_regular_iterator *it, void *data)
{
77 78 79
	xnvfile_printf(it, "%u/%u\n",
		       nr_active_objects,
		       CONFIG_XENO_OPT_REGISTRY_NRSLOTS);
80
	return 0;
81
}
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
82

83 84 85 86 87 88 89 90 91 92
static struct xnvfile_regular_ops usage_vfile_ops = {
	.show = usage_vfile_show,
};

static struct xnvfile_regular usage_vfile = {
	.ops = &usage_vfile_ops,
};

#endif /* CONFIG_XENO_OPT_VFILE */

93
unsigned xnregistry_hash_size(void)
94
{
95 96 97 98 99 100 101 102 103
	static const int primes[] = {
		101, 211, 307, 401, 503, 601,
		701, 809, 907, 1009, 1103
	};

#define obj_hash_max(n)			 \
((n) < sizeof(primes) / sizeof(int) ? \
 (n) : sizeof(primes) / sizeof(int) - 1)

104 105 106 107 108
	return primes[obj_hash_max(CONFIG_XENO_OPT_REGISTRY_NRSLOTS / 100)];
}

int xnregistry_init(void)
{
109
	int n, ret __maybe_unused;
110 111 112 113 114 115 116

	registry_obj_slots = kmalloc(CONFIG_XENO_OPT_REGISTRY_NRSLOTS *
				     sizeof(struct xnobject), GFP_KERNEL);
	if (registry_obj_slots == NULL)
		return -ENOMEM;

#ifdef CONFIG_XENO_OPT_VFILE
117
	ret = xnvfile_init_dir("registry", &registry_vfroot, &cobalt_vfroot);
118 119 120 121 122 123 124 125
	if (ret)
		return ret;

	ret = xnvfile_init_regular("usage", &usage_vfile, &registry_vfroot);
	if (ret) {
		xnvfile_destroy_dir(&registry_vfroot);
		return ret;
	}
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
126

127
	proc_apc =
128
	    xnapc_alloc("registry_export", &registry_proc_schedule, NULL);
129

130
	if (proc_apc < 0) {
131 132
		xnvfile_destroy_regular(&usage_vfile);
		xnvfile_destroy_dir(&registry_vfroot);
133
		return proc_apc;
134 135 136
	}
#endif /* CONFIG_XENO_OPT_VFILE */

137
	next_object_stamp = 0;
138 139 140

	for (n = 0; n < CONFIG_XENO_OPT_REGISTRY_NRSLOTS; n++) {
		registry_obj_slots[n].objaddr = NULL;
141
		list_add_tail(&registry_obj_slots[n].link, &free_object_list);
142
	}
Philippe Gerum's avatar
Philippe Gerum committed
143

144 145 146
	/* Slot #0 is reserved/invalid. */
	list_get_entry(&free_object_list, struct xnobject, link);
	nr_active_objects = 1;
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
147

148
	nr_object_entries = xnregistry_hash_size();
149
	object_index = kmalloc(sizeof(*object_index) *
150
				      nr_object_entries, GFP_KERNEL);
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
151

152
	if (object_index == NULL) {
153 154 155
#ifdef CONFIG_XENO_OPT_VFILE
		xnvfile_destroy_regular(&usage_vfile);
		xnvfile_destroy_dir(&registry_vfroot);
156
		xnapc_free(proc_apc);
157 158
#endif /* CONFIG_XENO_OPT_VFILE */
		return -ENOMEM;
159
	}
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
160

161
	for (n = 0; n < nr_object_entries; n++)
162
		INIT_HLIST_HEAD(&object_index[n]);
163

164
	xnsynch_init(&register_synch, XNSYNCH_FIFO, NULL);
165

166
	return 0;
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
167 168
}

169
void xnregistry_cleanup(void)
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
170
{
171
#ifdef CONFIG_XENO_OPT_VFILE
172 173
	struct hlist_node *enext;
	struct xnobject *ecurr;
174 175 176 177 178
	struct xnpnode *pnode;
	int n;

	flush_scheduled_work();

179
	for (n = 0; n < nr_object_entries; n++)
180 181
		hlist_for_each_entry_safe(ecurr, enext, 
					&object_index[n], hlink) {
182 183 184 185 186 187 188 189 190 191 192 193 194
			pnode = ecurr->pnode;
			if (pnode == NULL)
				continue;

			pnode->ops->unexport(ecurr, pnode);

			if (--pnode->entries > 0)
				continue;

			xnvfile_destroy_dir(&pnode->vdir);

			if (--pnode->root->entries == 0)
				xnvfile_destroy_dir(&pnode->root->vdir);
195
		}
196 197
#endif /* CONFIG_XENO_OPT_VFILE */

198 199
	kfree(object_index);
	xnsynch_destroy(&register_synch);
200 201

#ifdef CONFIG_XENO_OPT_VFILE
202
	xnapc_free(proc_apc);
203 204 205 206 207 208
	flush_scheduled_work();
	xnvfile_destroy_regular(&usage_vfile);
	xnvfile_destroy_dir(&registry_vfroot);
#endif /* CONFIG_XENO_OPT_VFILE */

	kfree(registry_obj_slots);
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
209 210
}

211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
#ifdef CONFIG_XENO_OPT_VFILE

static DEFINE_SEMAPHORE(export_mutex);

/*
 * The following stuff implements the mechanism for delegating
 * export/unexport requests to/from the /proc interface from the
 * Xenomai domain to the Linux kernel (i.e. the "lower stage"). This
 * ends up being a bit complex due to the fact that such requests
 * might lag enough before being processed by the Linux kernel so that
 * subsequent requests might just contradict former ones before they
 * even had a chance to be applied (e.g. export -> unexport in the
 * Xenomai domain for short-lived objects). This situation and the
 * like are hopefully properly handled due to a careful
 * synchronization of operations across domains.
 */
227
static void proc_callback(struct work_struct *work)
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
228
{
229 230
	struct xnvfile_directory *rdir, *dir;
	const char *rname, *type;
231
	struct xnobject *object;
232 233 234
	struct xnpnode *pnode;
	int ret;
	spl_t s;
235

236
	down(&export_mutex);
237

238
	xnlock_get_irqsave(&nklock, s);
239

240 241 242
	while (!list_empty(&proc_object_list)) {
		object = list_get_entry(&proc_object_list,
					struct xnobject, link);
243 244 245 246 247
		pnode = object->pnode;
		type = pnode->dirname;
		dir = &pnode->vdir;
		rdir = &pnode->root->vdir;
		rname = pnode->root->dirname;
248

249
		if (object->vfilp != XNOBJECT_EXPORT_SCHEDULED)
250
			goto unexport;
251

252
		object->vfilp = XNOBJECT_EXPORT_INPROGRESS;
253
		list_add_tail(&object->link, &busy_object_list);
254

255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298
		xnlock_put_irqrestore(&nklock, s);

		if (pnode->entries++ == 0) {
			if (pnode->root->entries++ == 0) {
				/* Create the root directory on the fly. */
				ret = xnvfile_init_dir(rname, rdir, &registry_vfroot);
				if (ret) {
					xnlock_get_irqsave(&nklock, s);
					object->pnode = NULL;
					pnode->root->entries = 0;
					pnode->entries = 0;
					continue;
				}
			}
			/* Create the class directory on the fly. */
			ret = xnvfile_init_dir(type, dir, rdir);
			if (ret) {
				if (pnode->root->entries == 1) {
					pnode->root->entries = 0;
					xnvfile_destroy_dir(rdir);
				}
				xnlock_get_irqsave(&nklock, s);
				object->pnode = NULL;
				pnode->entries = 0;
				continue;
			}
		}

		ret = pnode->ops->export(object, pnode);
		if (ret && --pnode->entries == 0) {
			xnvfile_destroy_dir(dir);
			if (--pnode->root->entries == 0)
				xnvfile_destroy_dir(rdir);
			xnlock_get_irqsave(&nklock, s);
			object->pnode = NULL;
		} else
			xnlock_get_irqsave(&nklock, s);

		continue;

	unexport:
		object->vfilp = NULL;
		object->pnode = NULL;

299 300 301
		if (object->vfilp == XNOBJECT_EXPORT_ABORTED)
			object->objaddr = NULL;

302
		if (object->objaddr)
303 304
			list_add_tail(&object->link, &busy_object_list);
		else {
305 306 307 308
			/*
			 * Trap the case where we are unexporting an
			 * already unregistered object.
			 */
309 310 311
			list_add_tail(&object->link, &free_object_list);
			nr_active_objects--;
		}
312 313 314 315 316 317 318 319 320 321 322 323 324

		xnlock_put_irqrestore(&nklock, s);

		pnode->ops->unexport(object, pnode);

		if (--pnode->entries == 0) {
			xnvfile_destroy_dir(dir);
			if (--pnode->root->entries == 0)
				xnvfile_destroy_dir(rdir);
		}

		xnlock_get_irqsave(&nklock, s);
	}
325

326 327 328
	xnlock_put_irqrestore(&nklock, s);

	up(&export_mutex);
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
329 330
}

331
static void registry_proc_schedule(void *cookie)
332
{
333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
	/*
	 * schedule_work() will check for us if the work has already
	 * been scheduled, so just be lazy and submit blindly.
	 */
	schedule_work(&registry_proc_work);
}

static int registry_export_vfsnap(struct xnobject *object,
				  struct xnpnode *pnode)
{
	struct xnpnode_snapshot *p;
	int ret;

	/*
	 * Make sure to initialize _all_ mandatory vfile fields; most
	 * of the time we are using sane NULL defaults based on static
	 * storage for the vfile struct, but here we are building up a
	 * vfile object explicitly.
	 */
	p = container_of(pnode, struct xnpnode_snapshot, node);
	object->vfile_u.vfsnap.file.datasz = p->vfile.datasz;
	object->vfile_u.vfsnap.file.privsz = p->vfile.privsz;
	/*
	 * Make the vfile refer to the provided tag struct if any,
	 * otherwise use our default tag space. In the latter case,
	 * each object family has its own private revision tag.
	 */
	object->vfile_u.vfsnap.file.tag = p->vfile.tag ?:
		&object->vfile_u.vfsnap.tag;
	object->vfile_u.vfsnap.file.ops = p->vfile.ops;
	object->vfile_u.vfsnap.file.entry.lockops = p->vfile.lockops;

	ret = xnvfile_init_snapshot(object->key, &object->vfile_u.vfsnap.file,
				    &pnode->vdir);
	if (ret)
		return ret;

	object->vfilp = &object->vfile_u.vfsnap.file.entry;
	object->vfilp->private = object->objaddr;
372 373 374 375

	return 0;
}

376 377
static void registry_unexport_vfsnap(struct xnobject *object,
				    struct xnpnode *pnode)
378
{
379 380
	xnvfile_destroy_snapshot(&object->vfile_u.vfsnap.file);
}
381

382 383 384 385
static void registry_touch_vfsnap(struct xnobject *object)
{
	xnvfile_touch(&object->vfile_u.vfsnap.file);
}
386

387 388 389 390 391 392
struct xnpnode_ops xnregistry_vfsnap_ops = {
	.export = registry_export_vfsnap,
	.unexport = registry_unexport_vfsnap,
	.touch = registry_touch_vfsnap,
};
EXPORT_SYMBOL_GPL(xnregistry_vfsnap_ops);
393

394 395 396 397 398
static int registry_export_vfreg(struct xnobject *object,
				 struct xnpnode *pnode)
{
	struct xnpnode_regular *p;
	int ret;
399

400 401 402 403 404 405 406 407 408 409 410 411 412
	/* See registry_export_vfsnap() for hints. */
	p = container_of(pnode, struct xnpnode_regular, node);
	object->vfile_u.vfreg.privsz = p->vfile.privsz;
	object->vfile_u.vfreg.ops = p->vfile.ops;
	object->vfile_u.vfreg.entry.lockops = p->vfile.lockops;

	ret = xnvfile_init_regular(object->key, &object->vfile_u.vfreg,
				   &pnode->vdir);
	if (ret)
		return ret;

	object->vfilp = &object->vfile_u.vfreg.entry;
	object->vfilp->private = object->objaddr;
413 414 415 416

	return 0;
}

417 418
static void registry_unexport_vfreg(struct xnobject *object,
				    struct xnpnode *pnode)
419
{
420 421
	xnvfile_destroy_regular(&object->vfile_u.vfreg);
}
422

423 424 425 426 427
struct xnpnode_ops xnregistry_vfreg_ops = {
	.export = registry_export_vfreg,
	.unexport = registry_unexport_vfreg,
};
EXPORT_SYMBOL_GPL(xnregistry_vfreg_ops);
428

429 430 431 432 433 434
static int registry_export_vlink(struct xnobject *object,
				 struct xnpnode *pnode)
{
	struct xnpnode_link *link_desc;
	char *link_target;
	int ret;
435

436 437 438 439
	link_desc = container_of(pnode, struct xnpnode_link, node);
	link_target = link_desc->target(object->objaddr);
	if (link_target == NULL)
		return -ENOMEM;
440

441 442 443 444 445
	ret = xnvfile_init_link(object->key, link_target,
				&object->vfile_u.link, &pnode->vdir);
	kfree(link_target);
	if (ret)
		return ret;
446

447 448
	object->vfilp = &object->vfile_u.link.entry;
	object->vfilp->private = object->objaddr;
449 450 451 452

	return 0;
}

453 454
static void registry_unexport_vlink(struct xnobject *object,
				    struct xnpnode *pnode)
455
{
456 457
	xnvfile_destroy_link(&object->vfile_u.link);
}
458

459 460 461 462 463
struct xnpnode_ops xnregistry_vlink_ops = {
	.export = registry_export_vlink,
	.unexport = registry_unexport_vlink,
};
EXPORT_SYMBOL_GPL(xnregistry_vlink_ops);
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
464

465 466 467
static inline void registry_export_pnode(struct xnobject *object,
					 struct xnpnode *pnode)
{
468
	object->vfilp = XNOBJECT_EXPORT_SCHEDULED;
469
	object->pnode = pnode;
470 471 472
	list_del(&object->link);
	list_add_tail(&object->link, &proc_object_list);
	__xnapc_schedule(proc_apc);
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
473 474
}

475
static inline void registry_unexport_pnode(struct xnobject *object)
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
476
{
477
	if (object->vfilp != XNOBJECT_EXPORT_SCHEDULED) {
478 479 480 481 482 483 484 485
		/*
		 * We might have preempted a v-file read op, so bump
		 * the object's revtag to make sure the data
		 * collection is aborted next, if we end up deleting
		 * the object being read.
		 */
		if (object->pnode->ops->touch)
			object->pnode->ops->touch(object);
486 487 488
		list_del(&object->link);
		list_add_tail(&object->link, &proc_object_list);
		__xnapc_schedule(proc_apc);
489 490 491 492 493 494
	} else {
		/*
		 * Unexporting before the lower stage has had a chance
		 * to export. Move back the object to the busyq just
		 * like if no export had been requested.
		 */
495 496
		list_del(&object->link);
		list_add_tail(&object->link, &busy_object_list);
497 498 499
		object->pnode = NULL;
		object->vfilp = NULL;
	}
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
500 501
}

502
#endif /* CONFIG_XENO_OPT_VFILE */
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
503

504 505 506
static unsigned registry_hash_crunch(const char *key)
{
	unsigned int h = 0, g;
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
507

508 509
#define HQON    24		/* Higher byte position */
#define HBYTE   0xf0000000	/* Higher nibble on */
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
510

511 512 513 514 515
	while (*key) {
		h = (h << 4) + *key++;
		if ((g = (h & HBYTE)) != 0)
			h = (h ^ (g >> HQON)) ^ g;
	}
Philippe Gerum's avatar
Philippe Gerum committed
516

517
	return h % nr_object_entries;
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
518 519
}

520
static inline int registry_hash_enter(const char *key, struct xnobject *object)
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
521
{
522 523
	struct xnobject *ecurr;
	unsigned s;
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
524

525 526
	object->key = key;
	s = registry_hash_crunch(key);
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
527

528
	hlist_for_each_entry(ecurr, &object_index[s], hlink)
529
		if (ecurr == object || strcmp(key, ecurr->key) == 0)
530
			return -EEXIST;
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
531

532
	hlist_add_head(&object->hlink, &object_index[s]);
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
533

534
	return 0;
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
535 536
}

537
static inline int registry_hash_remove(struct xnobject *object)
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
538
{
539
	unsigned int s = registry_hash_crunch(object->key);
540
	struct xnobject *ecurr;
541

542
	hlist_for_each_entry(ecurr, &object_index[s], hlink)
543
		if (ecurr == object) {
544
			hlist_del(&ecurr->hlink);
545 546
			return 0;
		}
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
547

548
	return -ESRCH;
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
549 550
}

551
static struct xnobject *registry_hash_find(const char *key)
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
552
{
553
	struct xnobject *ecurr;
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
554

555 556
	hlist_for_each_entry(ecurr, 
			&object_index[registry_hash_crunch(key)], hlink)
557
		if (strcmp(key, ecurr->key) == 0)
558
			return ecurr;
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
559

560 561
	return NULL;
}
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
562

563 564 565 566 567
struct registry_wait_context {
	struct xnthread_wait_context wc;
	const char *key;
};

568
static inline int registry_wakeup_sleepers(const char *key)
569
{
570 571
	struct registry_wait_context *rwc;
	struct xnthread_wait_context *wc;
572 573
	struct xnthread *sleeper, *tmp;
	int cnt = 0;
574

575
	xnsynch_for_each_sleeper_safe(sleeper, tmp, &register_synch) {
576 577 578
		wc = xnthread_get_wait_context(sleeper);
		rwc = container_of(wc, struct registry_wait_context, wc);
		if (*key == *rwc->key && strcmp(key, rwc->key) == 0) {
579
			xnsynch_wakeup_this_sleeper(&register_synch, sleeper);
580
			++cnt;
581
		}
582
	}
583

584
	return cnt;
585
}
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
586

587 588 589 590 591 592 593 594 595 596 597
/**
 * @fn int xnregistry_enter(const char *key,void *objaddr,xnhandle_t *phandle,struct xnpnode *pnode)
 * @brief Register a real-time object.
 *
 * This service allocates a new registry slot for an associated
 * object, and indexes it by an alphanumeric key for later retrieval.
 *
 * @param key A valid NULL-terminated string by which the object will
 * be indexed and later retrieved in the registry. Since it is assumed
 * that such key is stored into the registered object, it will *not*
 * be copied but only kept by reference in the registry. Pass an empty
598
 * or NULL string if the object shall only occupy a registry slot for
599 600
 * handle-based lookups. The slash character is not accepted in @a key
 * if @a pnode is non-NULL.
601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617
 *
 * @param objaddr An opaque pointer to the object to index by @a
 * key.
 *
 * @param phandle A pointer to a generic handle defined by the
 * registry which will uniquely identify the indexed object, until the
 * latter is unregistered using the xnregistry_remove() service.
 *
 * @param pnode A pointer to an optional /proc node class
 * descriptor. This structure provides the information needed to
 * export all objects from the given class through the /proc
 * filesystem, under the /proc/xenomai/registry entry. Passing NULL
 * indicates that no /proc support is available for the newly
 * registered object.
 *
 * @return 0 is returned upon success. Otherwise:
 *
618 619 620 621
 * - -EINVAL is returned if @a objaddr is NULL.
 *
 * - -EINVAL if @a pnode is non-NULL, and @a key points to a valid
 * string containing a '/' character.
622 623 624 625 626 627 628
 *
 * - -ENOMEM is returned if the system fails to get enough dynamic
 * memory from the global real-time heap in order to register the
 * object.
 *
 * - -EEXIST is returned if the @a key is already in use.
 *
629
 * @coretags{unrestricted, might-switch, atomic-entry}
630 631 632
 */
int xnregistry_enter(const char *key, void *objaddr,
		     xnhandle_t *phandle, struct xnpnode *pnode)
633
{
634 635 636
	struct xnobject *object;
	spl_t s;
	int ret;
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
637

638 639
	if (objaddr == NULL ||
	    (pnode != NULL && key != NULL && strchr(key, '/')))
640 641 642
		return -EINVAL;

	xnlock_get_irqsave(&nklock, s);
Jan Kiszka's avatar
Jan Kiszka committed
643

644
	if (list_empty(&free_object_list)) {
645
		ret = -EAGAIN;
646
		goto unlock_and_exit;
Jan Kiszka's avatar
Jan Kiszka committed
647
	}
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
648

649 650
	object = list_get_entry(&free_object_list, struct xnobject, link);
	nr_active_objects++;
651
	object->objaddr = objaddr;
652
	object->cstamp = ++next_object_stamp;
653 654 655
#ifdef CONFIG_XENO_OPT_VFILE
	object->pnode = NULL;
#endif
656
	if (key == NULL || *key == '\0') {
657 658 659 660
		object->key = NULL;
		*phandle = object - registry_obj_slots;
		ret = 0;
		goto unlock_and_exit;
661
	}
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
662

663 664
	ret = registry_hash_enter(key, object);
	if (ret) {
665 666
		nr_active_objects--;
		list_add_tail(&object->link, &free_object_list);
667
		goto unlock_and_exit;
668
	}
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
669

670
	list_add_tail(&object->link, &busy_object_list);
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
671

672 673 674 675 676 677 678 679 680 681 682 683
	/*
	 * <!> Make sure the handle is written back before the
	 * rescheduling takes place.
	 */
	*phandle = object - registry_obj_slots;

#ifdef CONFIG_XENO_OPT_VFILE
	if (pnode)
		registry_export_pnode(object, pnode);
#endif /* CONFIG_XENO_OPT_VFILE */

	if (registry_wakeup_sleepers(key))
684
		xnsched_run();
685 686 687 688 689 690

unlock_and_exit:

	xnlock_put_irqrestore(&nklock, s);

	return ret;
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
691
}
692
EXPORT_SYMBOL_GPL(xnregistry_enter);
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
693

694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728
/**
 * @fn int xnregistry_bind(const char *key,xnticks_t timeout,int timeout_mode,xnhandle_t *phandle)
 * @brief Bind to a real-time object.
 *
 * This service retrieves the registry handle of a given object
 * identified by its key. Unless otherwise specified, this service
 * will block the caller if the object is not registered yet, waiting
 * for such registration to occur.
 *
 * @param key A valid NULL-terminated string which identifies the
 * object to bind to.
 *
 * @param timeout The timeout which may be used to limit the time the
 * thread wait for the object to be registered. This value is a wait
 * time given as a count of nanoseconds. It can either be relative,
 * absolute monotonic (XN_ABSOLUTE), or absolute adjustable
 * (XN_REALTIME) depending on @a timeout_mode. Passing XN_INFINITE @b
 * and setting @a timeout_mode to XN_RELATIVE specifies an unbounded
 * wait. Passing XN_NONBLOCK causes the service to return immediately
 * without waiting if the object is not registered on entry. All other
 * values are used as a wait limit.
 *
 * @param timeout_mode The mode of the @a timeout parameter. It can
 * either be set to XN_RELATIVE, XN_ABSOLUTE, or XN_REALTIME (see also
 * xntimer_start()).
 *
 * @param phandle A pointer to a memory location which will be written
 * upon success with the generic handle defined by the registry for
 * the retrieved object. Contents of this memory is undefined upon
 * failure.
 *
 * @return 0 is returned upon success. Otherwise:
 *
 * - -EINVAL is returned if @a key is NULL.
 *
729 730
 * - -EINTR is returned if xnthread_unblock() has been called for the
 * waiting thread before the retrieval has completed.
731 732 733 734 735 736 737 738 739 740
 *
 * - -EWOULDBLOCK is returned if @a timeout is equal to XN_NONBLOCK
 * and the searched object is not registered on entry. As a special
 * exception, this error is also returned if this service should
 * block, but was called from a context which cannot sleep
 * (e.g. interrupt, non-realtime or scheduler locked).
 *
 * - -ETIMEDOUT is returned if the object cannot be retrieved within
 * the specified amount of time.
 *
741
 * @coretags{primary-only, might-switch}
742 743 744 745 746 747
 *
 * @note xnregistry_bind() only returns the index portion of a handle,
 * which might include other fixed bits to be complete
 * (e.g. XNSYNCH_PSHARED). The caller is responsible for completing
 * the handle returned with those bits if applicable, depending on the
 * context.
748 749 750
 */
int xnregistry_bind(const char *key, xnticks_t timeout, int timeout_mode,
		    xnhandle_t *phandle)
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
751
{
752
	struct registry_wait_context rwc;
753
	struct xnobject *object;
Philippe Gerum's avatar
Philippe Gerum committed
754
	int ret = 0, info;
755 756
	spl_t s;

757 758
	if (key == NULL)
		return -EINVAL;
759

760 761 762 763
	xnlock_get_irqsave(&nklock, s);

	if (timeout_mode == XN_RELATIVE &&
	    timeout != XN_INFINITE && timeout != XN_NONBLOCK) {
764
		timeout_mode = XN_ABSOLUTE;
765
		timeout += xnclock_read_monotonic(&nkclock);
766 767
	}

768 769 770 771 772 773
	for (;;) {
		object = registry_hash_find(key);
		if (object) {
			*phandle = object - registry_obj_slots;
			goto unlock_and_exit;
		}
774

775
		if ((timeout_mode == XN_RELATIVE && timeout == XN_NONBLOCK) ||
776
		    xnsched_unblockable_p()) {
777
			ret = -EWOULDBLOCK;
778 779 780
			goto unlock_and_exit;
		}

781 782
		rwc.key = key;
		xnthread_prepare_wait(&rwc.wc);
783 784 785
		info = xnsynch_sleep_on(&register_synch, timeout, timeout_mode);
		if (info & XNTIMEO) {
			ret = -ETIMEDOUT;
786 787
			goto unlock_and_exit;
		}
788 789
		if (info & XNBREAK) {
			ret = -EINTR;
790 791 792 793
			goto unlock_and_exit;
		}
	}

794
unlock_and_exit:
795 796 797

	xnlock_put_irqrestore(&nklock, s);

798
	return ret;
799
}
800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816
EXPORT_SYMBOL_GPL(xnregistry_bind);

/**
 * @fn int xnregistry_remove(xnhandle_t handle)
 * @brief Forcibly unregister a real-time object.
 *
 * This service forcibly removes an object from the registry. The
 * removal is performed regardless of the current object's locking
 * status.
 *
 * @param handle The generic handle of the object to remove.
 *
 * @return 0 is returned upon success. Otherwise:
 *
 * - -ESRCH is returned if @a handle does not reference a registered
 * object.
 *
817
 * @coretags{unrestricted}
818 819
 */
int xnregistry_remove(xnhandle_t handle)
820
{
821
	struct xnobject *object;
822
	void *objaddr;
823
	int ret = 0;
824
	spl_t s;
825

826 827 828 829
	xnlock_get_irqsave(&nklock, s);

	object = xnregistry_validate(handle);
	if (object == NULL) {
830
		ret = -ESRCH;
831 832 833
		goto unlock_and_exit;
	}

834
	objaddr = object->objaddr;
835 836 837 838 839 840 841 842
	object->objaddr = NULL;
	object->cstamp = 0;

	if (object->key) {
		registry_hash_remove(object);

#ifdef CONFIG_XENO_OPT_VFILE
		if (object->pnode) {
843 844 845 846 847
			if (object->vfilp == XNOBJECT_EXPORT_INPROGRESS) {
				object->vfilp = XNOBJECT_EXPORT_ABORTED;
				object->objaddr = objaddr;
			}

848
			registry_unexport_pnode(object);
849 850 851 852
			/*
			 * Leave the update of the object queues to
			 * the work callback if it has been kicked.
			 */
853 854 855 856 857
			if (object->pnode)
				goto unlock_and_exit;
		}
#endif /* CONFIG_XENO_OPT_VFILE */

858
		list_del(&object->link);
859 860
	}

861 862 863 864
	if (!IS_ENABLED(CONFIG_XENO_OPT_VFILE) || !object->objaddr) {
		list_add_tail(&object->link, &free_object_list);
		nr_active_objects--;
	}
865

866
unlock_and_exit:
867 868 869

	xnlock_put_irqrestore(&nklock, s);

870
	return ret;
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
871
}
872 873
EXPORT_SYMBOL_GPL(xnregistry_remove);

874 875
/**
 * Turn a named object into an anonymous object
876
 *
877
 * @coretags{unrestricted}
878 879 880 881 882 883 884
 */
int xnregistry_unlink(const char *key)
{
	struct xnobject *object;
	int ret = 0;
	spl_t s;

885 886 887
	if (key == NULL)
		return -EINVAL;

888 889 890 891 892 893 894
	xnlock_get_irqsave(&nklock, s);

	object = registry_hash_find(key);
	if (object == NULL) {
		ret = -ESRCH;
		goto unlock_and_exit;
	}
895

896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912
	ret = registry_hash_remove(object);
	if (ret < 0)
		goto unlock_and_exit;

#ifdef CONFIG_XENO_OPT_VFILE
	if (object->pnode) {
		registry_unexport_pnode(object);
		/*
		 * Leave the update of the object queues to
		 * the work callback if it has been kicked.
		 */
		if (object->pnode)
			goto unlock_and_exit;
	}
#endif /* CONFIG_XENO_OPT_VFILE */

	list_del(&object->link);
913

914 915 916 917 918 919 920 921
	object->key = NULL;

unlock_and_exit:
	xnlock_put_irqrestore(&nklock, s);

	return ret;
}

922
/**
923
 * @fn void *xnregistry_lookup(xnhandle_t handle, unsigned long *cstamp_r)
924 925 926
 * @brief Find a real-time object into the registry.
 *
 * This service retrieves an object from its handle into the registry
927 928 929
 * and returns the memory address of its descriptor. Optionally, it
 * also copies back the object's creation stamp which is unique across
 * object registration calls.
930
 *
931
 * @param handle The generic handle of the object to fetch.
932
 *
933 934 935
 * @param cstamp_r If not-NULL, the object's creation stamp will be
 * copied to this memory area.
 *
936 937
 * @return The memory address of the object's descriptor is returned
 * on success. Otherwise, NULL is returned if @a handle does not
938
 * reference a registered object.
939
 *
940
 * @coretags{unrestricted}
941 942
 */

943
/** @} */