pipe.c 25.8 KB
Newer Older
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
/*
 * Copyright (C) 2001,2002,2003,2004 Philippe Gerum <rpm@xenomai.org>.
 * Copyright (C) 2005 Dmitry Adamushko <dmitry.adamushko@gmail.com>
 *
 * Xenomai 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, Inc., 675 Mass Ave, Cambridge MA
 * 02139, USA; either version 2 of the License, or (at your option)
 * any later version.
 *
 * Xenomai 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.
 */
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/fcntl.h>
#include <linux/poll.h>
#include <linux/termios.h>
#include <linux/spinlock.h>
#include <linux/device.h>
29
#include <linux/uaccess.h>
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
30
#include <asm/io.h>
31
#include <asm/xenomai/syscall.h>
32
#include <cobalt/kernel/sched.h>
33 34 35
#include <cobalt/kernel/heap.h>
#include <cobalt/kernel/pipe.h>
#include <cobalt/kernel/apc.h>
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
36 37 38

static int xnpipe_asyncsig = SIGIO;

39
struct xnpipe_state xnpipe_states[XNPIPE_NDEVS];
40
EXPORT_SYMBOL_GPL(xnpipe_states);
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
41

42
#define XNPIPE_BITMAP_SIZE	((XNPIPE_NDEVS + BITS_PER_LONG - 1) / BITS_PER_LONG)
43

44 45
static unsigned long xnpipe_bitmap[XNPIPE_BITMAP_SIZE];

46 47 48
static LIST_HEAD(xnpipe_sleepq);

static LIST_HEAD(xnpipe_asyncq);
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
49 50 51

int xnpipe_wakeup_apc;

52
static struct class *xnpipe_class;
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
53

54 55 56 57
/* Allocation of minor values */

static inline int xnpipe_minor_alloc(int minor)
{
58
	spl_t s;
59

60 61
	if ((minor < 0 && minor != XNPIPE_MINOR_AUTO) || minor >= XNPIPE_NDEVS)
		return -ENODEV;
62

63
	xnlock_get_irqsave(&nklock, s);
64

65 66
	if (minor == XNPIPE_MINOR_AUTO)
		minor = find_first_zero_bit(xnpipe_bitmap, XNPIPE_NDEVS);
67

68
	if (minor == XNPIPE_NDEVS ||
69 70
	    (xnpipe_bitmap[minor / BITS_PER_LONG] &
	     (1UL << (minor % BITS_PER_LONG))))
71 72
		minor = -EBUSY;
	else
73 74
		xnpipe_bitmap[minor / BITS_PER_LONG] |=
			(1UL << (minor % BITS_PER_LONG));
75

76
	xnlock_put_irqrestore(&nklock, s);
77

78
	return minor;
79 80 81 82
}

static inline void xnpipe_minor_free(int minor)
{
83 84
	xnpipe_bitmap[minor / BITS_PER_LONG] &=
		~(1UL << (minor % BITS_PER_LONG));
85 86
}

87
static inline void xnpipe_enqueue_wait(struct xnpipe_state *state, int mask)
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
88
{
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
89
	if (state->wcount != 0x7fffffff && state->wcount++ == 0)
90
		list_add_tail(&state->slink, &xnpipe_sleepq);
91

92
	state->status |= mask;
93
}
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
94

95
static inline void xnpipe_dequeue_wait(struct xnpipe_state *state, int mask)
96
{
97
	if (state->status & mask)
98
		if (--state->wcount == 0) {
99
			list_del(&state->slink);
100
			state->status &= ~mask;
101 102 103
		}
}

104
static inline void xnpipe_dequeue_all(struct xnpipe_state *state, int mask)
105
{
106
	if (state->status & mask) {
107 108
		if (state->wcount) {
			state->wcount = 0;
109
			list_del(&state->slink);
110
			state->status &= ~mask;
111
		}
112
	}
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
113 114
}

115
/* Must be entered with nklock held, interrupts off. */
116 117 118 119 120 121 122 123 124 125 126 127 128
#define xnpipe_wait(__state, __mask, __s, __cond)			\
({									\
	wait_queue_head_t *__waitq;					\
	DEFINE_WAIT(__wait);						\
	int __sigpending;						\
									\
	if ((__mask) & XNPIPE_USER_WREAD)				\
		__waitq = &(__state)->readq;				\
	else								\
		__waitq = &(__state)->syncq;				\
									\
	xnpipe_enqueue_wait(__state, __mask);				\
	xnlock_put_irqrestore(&nklock, __s);				\
Philippe Gerum's avatar
Philippe Gerum committed
129
									\
130 131 132 133 134 135 136
	for (;;) {							\
		__sigpending = signal_pending(current);			\
		if (__sigpending)					\
			break;						\
		prepare_to_wait_exclusive(__waitq, &__wait, TASK_INTERRUPTIBLE); \
		if (__cond)						\
			break;						\
Philippe Gerum's avatar
Philippe Gerum committed
137
		schedule();						\
138
	}								\
Philippe Gerum's avatar
Philippe Gerum committed
139 140 141 142 143
									\
	finish_wait(__waitq, &__wait);					\
									\
	/* Restore the interrupt state initially set by the caller. */	\
	xnlock_get_irqsave(&nklock, __s);				\
144
	xnpipe_dequeue_wait(__state, __mask);				\
Philippe Gerum's avatar
Philippe Gerum committed
145 146 147
									\
	__sigpending;							\
})
148

Philippe Gerum's avatar
Philippe Gerum committed
149
static void xnpipe_wakeup_proc(void *cookie)
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
150
{
151
	struct xnpipe_state *state;
152
	unsigned long rbits;
153
	spl_t s;
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
154

155
	xnlock_get_irqsave(&nklock, s);
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
156

157 158 159 160 161 162 163 164 165 166 167 168 169 170
	/*
	 * NOTE: sleepers might enter/leave the queue while we don't
	 * hold the nklock in these wakeup loops. So we iterate over
	 * each sleeper list until we find no more candidate for
	 * wakeup after an entire scan, redoing the scan from the list
	 * head otherwise.
	 */
	for (;;) {
		if (list_empty(&xnpipe_sleepq))
			goto check_async;

		state = list_first_entry(&xnpipe_sleepq, struct xnpipe_state, slink);

		for (;;) {
171
			rbits = state->status & XNPIPE_USER_ALL_READY;
172 173 174 175 176 177 178
			if (rbits)
				break;
			if (list_is_last(&state->slink, &xnpipe_sleepq))
				goto check_async;
			state = list_next_entry(state, slink);
		}

179
		state->status &= ~rbits;
180 181 182 183 184 185

		if ((rbits & XNPIPE_USER_WREAD_READY) != 0) {
			if (waitqueue_active(&state->readq)) {
				xnlock_put_irqrestore(&nklock, s);
				wake_up_interruptible(&state->readq);
				xnlock_get_irqsave(&nklock, s);
Philippe Gerum's avatar
Philippe Gerum committed
186
			}
187 188 189 190 191 192
		}
		if ((rbits & XNPIPE_USER_WSYNC_READY) != 0) {
			if (waitqueue_active(&state->syncq)) {
				xnlock_put_irqrestore(&nklock, s);
				wake_up_interruptible(&state->syncq);
				xnlock_get_irqsave(&nklock, s);
Philippe Gerum's avatar
Philippe Gerum committed
193
			}
194 195
		}
	}
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
196

197
check_async:
198 199 200 201
	/*
	 * Scan the async queue, sending the proper signal to
	 * subscribers.
	 */
202 203 204
	for (;;) {
		if (list_empty(&xnpipe_asyncq))
			goto out;
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
205

206
		state = list_first_entry(&xnpipe_asyncq, struct xnpipe_state, alink);
Philippe Gerum's avatar
Philippe Gerum committed
207

208
		for (;;) {
209
			if (state->status & XNPIPE_USER_SIGIO)
210 211 212 213
				break;
			if (list_is_last(&state->alink, &xnpipe_asyncq))
				goto out;
			state = list_next_entry(state, alink);
214
		}
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
215

216
		state->status &= ~XNPIPE_USER_SIGIO;
217 218 219 220 221
		xnlock_put_irqrestore(&nklock, s);
		kill_fasync(&state->asyncq, xnpipe_asyncsig, POLL_IN);
		xnlock_get_irqsave(&nklock, s);
	}
out:
222
	xnlock_put_irqrestore(&nklock, s);
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
223 224
}

225
static inline void xnpipe_schedule_request(void) /* hw IRQs off */
Philippe Gerum's avatar
Philippe Gerum committed
226
{
Philippe Gerum's avatar
Philippe Gerum committed
227
	__xnapc_schedule(xnpipe_wakeup_apc);
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
228 229
}

230
static inline ssize_t xnpipe_flush_bufq(void (*fn)(void *buf, void *xstate),
231
					struct list_head *q,
232 233
					void *xstate)
{
234
	struct xnpipe_mh *mh, *tmp;
235 236
	ssize_t n = 0;

237 238 239
	if (list_empty(q))
		return 0;

240
	/* Queue is private, no locking is required. */
241 242
	list_for_each_entry_safe(mh, tmp, q, link) {
		list_del(&mh->link);
243 244 245 246
		n += xnpipe_m_size(mh);
		fn(mh, xstate);
	}

247
	/* Return the overall count of bytes flushed. */
248 249
	return n;
}
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
250

251 252
/*
 * Move the specified queue contents to a private queue, then call the
253
 * flush handler to purge it. The latter runs without locking.
254 255 256 257 258
 * Returns the number of bytes flushed. Must be entered with nklock
 * held, interrupts off.
 */
#define xnpipe_flushq(__state, __q, __f, __s)				\
({									\
259 260
	LIST_HEAD(__privq);						\
	ssize_t __n;							\
261
									\
262 263
	list_splice_init(&(state)->__q, &__privq);			\
	(__state)->nr ## __q = 0;					\
264
	xnlock_put_irqrestore(&nklock, (__s));				\
265
	__n = xnpipe_flush_bufq((__state)->ops.__f, &__privq, (__state)->xstate);	\
266 267
	xnlock_get_irqsave(&nklock, (__s));				\
									\
268
	__n;								\
269 270 271
})

static void *xnpipe_default_alloc_ibuf(size_t size, void *xstate)
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
272
{
273 274 275
	void *buf;

	buf = xnmalloc(size);
Philippe Gerum's avatar
Philippe Gerum committed
276
	if (likely(buf != NULL))
277 278
		return buf;

279
	if (size > xnheap_get_size(&cobalt_heap))
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
		/* Request will never succeed. */
		return (struct xnpipe_mh *)-1;

	return NULL;
}

static void xnpipe_default_free_ibuf(void *buf, void *xstate)
{
	xnfree(buf);
}

static void xnpipe_default_release(void *xstate)
{
}

static inline int xnpipe_set_ops(struct xnpipe_state *state,
				 struct xnpipe_operations *ops)
{
	state->ops = *ops;

	if (ops->free_obuf == NULL)
		/*
		 * Caller must provide a way to free unread outgoing
		 * buffers.
		 */
		return -EINVAL;

	/* Set some default handlers for common usage. */
	if (ops->alloc_ibuf == NULL)
		state->ops.alloc_ibuf = xnpipe_default_alloc_ibuf;
	if (ops->free_ibuf == NULL)
		state->ops.free_ibuf = xnpipe_default_free_ibuf;
	if (ops->release == NULL)
		state->ops.release = xnpipe_default_release;

	return 0;
}

int xnpipe_connect(int minor, struct xnpipe_operations *ops, void *xstate)
{
	struct xnpipe_state *state;
	int need_sched = 0, ret;
322
	spl_t s;
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
323

324 325 326
	minor = xnpipe_minor_alloc(minor);
	if (minor < 0)
		return minor;
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
327

328
	state = &xnpipe_states[minor];
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
329

330
	xnlock_get_irqsave(&nklock, s);
Philippe Gerum's avatar
Philippe Gerum committed
331

332 333 334 335 336
	ret = xnpipe_set_ops(state, ops);
	if (ret) {
		xnlock_put_irqrestore(&nklock, s);
		return ret;
	}
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
337

338
	state->status |= XNPIPE_KERN_CONN;
339
	xnsynch_init(&state->synchbase, XNSYNCH_FIFO, NULL);
340
	state->xstate = xstate;
341
	state->ionrd = 0;
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
342

343 344
	if (state->status & XNPIPE_USER_CONN) {
		if (state->status & XNPIPE_USER_WREAD) {
345 346 347 348
			/*
			 * Wake up the regular Linux task waiting for
			 * the kernel side to connect (xnpipe_open).
			 */
349
			state->status |= XNPIPE_USER_WREAD_READY;
350 351
			need_sched = 1;
		}
Philippe Gerum's avatar
Philippe Gerum committed
352

353
		if (state->asyncq) {	/* Schedule asynch sig. */
354
			state->status |= XNPIPE_USER_SIGIO;
355 356 357
			need_sched = 1;
		}
	}
Philippe Gerum's avatar
Philippe Gerum committed
358

359 360
	if (need_sched)
		xnpipe_schedule_request();
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
361

362 363
	xnlock_put_irqrestore(&nklock, s);

364
	return minor;
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
365
}
Philippe Gerum's avatar
Philippe Gerum committed
366
EXPORT_SYMBOL_GPL(xnpipe_connect);
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
367

Philippe Gerum's avatar
Philippe Gerum committed
368
int xnpipe_disconnect(int minor)
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
369
{
370
	struct xnpipe_state *state;
371 372
	int need_sched = 0;
	spl_t s;
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
373

374 375
	if (minor < 0 || minor >= XNPIPE_NDEVS)
		return -ENODEV;
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
376

377
	state = &xnpipe_states[minor];
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
378

379
	xnlock_get_irqsave(&nklock, s);
Philippe Gerum's avatar
Philippe Gerum committed
380

381
	if ((state->status & XNPIPE_KERN_CONN) == 0) {
382 383 384
		xnlock_put_irqrestore(&nklock, s);
		return -EBADF;
	}
Philippe Gerum's avatar
Philippe Gerum committed
385

386
	state->status &= ~XNPIPE_KERN_CONN;
Philippe Gerum's avatar
Philippe Gerum committed
387

388
	state->ionrd -= xnpipe_flushq(state, outq, free_obuf, s);
Philippe Gerum's avatar
Philippe Gerum committed
389

390
	if ((state->status & XNPIPE_USER_CONN) == 0)
391
		goto cleanup;
Philippe Gerum's avatar
Philippe Gerum committed
392

393
	xnpipe_flushq(state, inq, free_ibuf, s);
Philippe Gerum's avatar
Philippe Gerum committed
394

395
	if (xnsynch_destroy(&state->synchbase) == XNSYNCH_RESCHED)
396
		xnsched_run();
Philippe Gerum's avatar
Philippe Gerum committed
397

398
	if (state->status & XNPIPE_USER_WREAD) {
399 400 401 402 403
		/*
		 * Wake up the regular Linux task waiting for some
		 * operation from the Xenomai side (read/write or
		 * poll).
		 */
404
		state->status |= XNPIPE_USER_WREAD_READY;
405
		need_sched = 1;
406
	}
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
407

408
	if (state->asyncq) {	/* Schedule asynch sig. */
409
		state->status |= XNPIPE_USER_SIGIO;
410 411
		need_sched = 1;
	}
412

413 414 415 416 417 418
cleanup:
	/*
	 * If xnpipe_release() has not fully run, enter lingering
	 * close. This will prevent the extra state from being wiped
	 * out until then.
	 */
419
	if (state->status & XNPIPE_USER_CONN)
420
		state->status |= XNPIPE_KERN_LCLOSE;
421
	else {
422 423
		xnlock_put_irqrestore(&nklock, s);
		state->ops.release(state->xstate);
424
		xnlock_get_irqsave(&nklock, s);
425
		xnpipe_minor_free(minor);
426
	}
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
427

428 429
	if (need_sched)
		xnpipe_schedule_request();
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
430

431 432
	xnlock_put_irqrestore(&nklock, s);

433
	return 0;
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
434
}
Philippe Gerum's avatar
Philippe Gerum committed
435
EXPORT_SYMBOL_GPL(xnpipe_disconnect);
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
436

Philippe Gerum's avatar
Philippe Gerum committed
437
ssize_t xnpipe_send(int minor, struct xnpipe_mh *mh, size_t size, int flags)
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
438
{
439
	struct xnpipe_state *state;
440 441
	int need_sched = 0;
	spl_t s;
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
442

443 444
	if (minor < 0 || minor >= XNPIPE_NDEVS)
		return -ENODEV;
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
445

446 447
	if (size <= sizeof(*mh))
		return -EINVAL;
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
448

449
	state = &xnpipe_states[minor];
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
450

451
	xnlock_get_irqsave(&nklock, s);
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
452

453
	if ((state->status & XNPIPE_KERN_CONN) == 0) {
454 455 456
		xnlock_put_irqrestore(&nklock, s);
		return -EBADF;
	}
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
457

458
	xnpipe_m_size(mh) = size - sizeof(*mh);
459
	xnpipe_m_rdoff(mh) = 0;
460
	state->ionrd += xnpipe_m_size(mh);
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
461

462
	if (flags & XNPIPE_URGENT)
463
		list_add(&mh->link, &state->outq);
464
	else
465 466 467
		list_add_tail(&mh->link, &state->outq);

	state->nroutq++;
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
468

469
	if ((state->status & XNPIPE_USER_CONN) == 0) {
Philippe Gerum's avatar
Philippe Gerum committed
470 471 472 473
		xnlock_put_irqrestore(&nklock, s);
		return (ssize_t) size;
	}

474
	if (state->status & XNPIPE_USER_WREAD) {
475 476 477 478
		/*
		 * Wake up the regular Linux task waiting for input
		 * from the Xenomai side.
		 */
479
		state->status |= XNPIPE_USER_WREAD_READY;
480 481
		need_sched = 1;
	}
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
482

483
	if (state->asyncq) {	/* Schedule asynch sig. */
484
		state->status |= XNPIPE_USER_SIGIO;
485 486
		need_sched = 1;
	}
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
487

488 489
	if (need_sched)
		xnpipe_schedule_request();
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
490

491 492
	xnlock_put_irqrestore(&nklock, s);

493
	return (ssize_t) size;
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
494
}
Philippe Gerum's avatar
Philippe Gerum committed
495
EXPORT_SYMBOL_GPL(xnpipe_send);
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
496

497
ssize_t xnpipe_mfixup(int minor, struct xnpipe_mh *mh, ssize_t size)
Philippe Gerum's avatar
Philippe Gerum committed
498
{
499
	struct xnpipe_state *state;
Philippe Gerum's avatar
Philippe Gerum committed
500 501 502 503 504 505 506 507 508 509 510 511
	spl_t s;

	if (minor < 0 || minor >= XNPIPE_NDEVS)
		return -ENODEV;

	if (size < 0)
		return -EINVAL;

	state = &xnpipe_states[minor];

	xnlock_get_irqsave(&nklock, s);

512
	if ((state->status & XNPIPE_KERN_CONN) == 0) {
Philippe Gerum's avatar
Philippe Gerum committed
513 514 515 516 517 518 519 520 521 522 523
		xnlock_put_irqrestore(&nklock, s);
		return -EBADF;
	}

	xnpipe_m_size(mh) += size;
	state->ionrd += size;

	xnlock_put_irqrestore(&nklock, s);

	return (ssize_t) size;
}
Philippe Gerum's avatar
Philippe Gerum committed
524
EXPORT_SYMBOL_GPL(xnpipe_mfixup);
Philippe Gerum's avatar
Philippe Gerum committed
525

Philippe Gerum's avatar
Philippe Gerum committed
526
ssize_t xnpipe_recv(int minor, struct xnpipe_mh **pmh, xnticks_t timeout)
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
527
{
528
	struct xnpipe_state *state;
529 530
	struct xnpipe_mh *mh;
	xntmode_t mode;
531
	ssize_t ret;
Philippe Gerum's avatar
Philippe Gerum committed
532
	int info;
533
	spl_t s;
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
534

535 536
	if (minor < 0 || minor >= XNPIPE_NDEVS)
		return -ENODEV;
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
537

538
	if (xnsched_interrupt_p())
539
		return -EPERM;
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
540

541
	state = &xnpipe_states[minor];
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
542

543
	xnlock_get_irqsave(&nklock, s);
Philippe Gerum's avatar
Philippe Gerum committed
544

545
	if ((state->status & XNPIPE_KERN_CONN) == 0) {
546 547 548
		ret = -EBADF;
		goto unlock_and_exit;
	}
Philippe Gerum's avatar
Philippe Gerum committed
549

550 551 552 553 554 555 556
	/*
	 * If we received a relative timespec, rescale it to an
	 * absolute time value based on the monotonic clock.
	 */
	mode = XN_RELATIVE;
	if (timeout != XN_NONBLOCK && timeout != XN_INFINITE) {
		mode = XN_ABSOLUTE;
557
		timeout += xnclock_read_monotonic(&nkclock);
558 559 560 561 562
	}

	for (;;) {
		if (!list_empty(&state->inq))
			break;
563

564 565 566 567
		if (timeout == XN_NONBLOCK) {
			ret = -EWOULDBLOCK;
			goto unlock_and_exit;
		}
Philippe Gerum's avatar
Philippe Gerum committed
568

569 570
		info = xnsynch_sleep_on(&state->synchbase, timeout, mode);
		if (info & XNTIMEO) {
571 572 573
			ret = -ETIMEDOUT;
			goto unlock_and_exit;
		}
574
		if (info & XNBREAK) {
575 576 577
			ret = -EINTR;
			goto unlock_and_exit;
		}
578
		if (info & XNRMID) {
579 580 581 582
			ret = -EIDRM;
			goto unlock_and_exit;
		}
	}
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
583

584 585 586 587
	mh = list_get_entry(&state->inq, struct xnpipe_mh, link);
	*pmh = mh;
	state->nrinq--;
	ret = (ssize_t)xnpipe_m_size(mh);
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
588

589
	if (state->status & XNPIPE_USER_WSYNC) {
590
		state->status |= XNPIPE_USER_WSYNC_READY;
591
		xnpipe_schedule_request();
Philippe Gerum's avatar
Philippe Gerum committed
592 593
	}

594
unlock_and_exit:
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
595

596
	xnlock_put_irqrestore(&nklock, s);
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
597

598
	return ret;
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
599
}
Philippe Gerum's avatar
Philippe Gerum committed
600
EXPORT_SYMBOL_GPL(xnpipe_recv);
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
601

602 603
int xnpipe_flush(int minor, int mode)
{
604
	struct xnpipe_state *state;
605
	int msgcount;
606 607 608 609 610 611 612 613 614
	spl_t s;

	if (minor < 0 || minor >= XNPIPE_NDEVS)
		return -ENODEV;

	state = &xnpipe_states[minor];

	xnlock_get_irqsave(&nklock, s);

615
	if ((state->status & XNPIPE_KERN_CONN) == 0) {
616 617
		xnlock_put_irqrestore(&nklock, s);
		return -EBADF;
618 619
	}

620
	msgcount = state->nroutq + state->nrinq;
621

622 623
	if (mode & XNPIPE_OFLUSH)
		state->ionrd -= xnpipe_flushq(state, outq, free_obuf, s);
624

625 626
	if (mode & XNPIPE_IFLUSH)
		xnpipe_flushq(state, inq, free_ibuf, s);
627

628
	if ((state->status & XNPIPE_USER_WSYNC) &&
629
	    msgcount > state->nroutq + state->nrinq) {
630
		state->status |= XNPIPE_USER_WSYNC_READY;
631
		xnpipe_schedule_request();
632 633 634 635 636 637
	}

	xnlock_put_irqrestore(&nklock, s);

	return 0;
}
Philippe Gerum's avatar
Philippe Gerum committed
638
EXPORT_SYMBOL_GPL(xnpipe_flush);
639

640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665
int xnpipe_pollstate(int minor, unsigned int *mask_r)
{
	struct xnpipe_state *state;
	int ret = 0;
	spl_t s;

	if (minor < 0 || minor >= XNPIPE_NDEVS)
		return -ENODEV;

	state = xnpipe_states + minor;

	xnlock_get_irqsave(&nklock, s);

	if (state->status & XNPIPE_KERN_CONN) {
		*mask_r = POLLOUT;
		if (!list_empty(&state->inq))
			*mask_r |= POLLIN;
	} else
		ret = -EIO;

	xnlock_put_irqrestore(&nklock, s);

	return ret;
}
EXPORT_SYMBOL_GPL(xnpipe_pollstate);

666 667 668 669 670
/* Must be entered with nklock held, interrupts off. */
#define xnpipe_cleanup_user_conn(__state, __s)				\
	do {								\
		xnpipe_flushq((__state), outq, free_obuf, (__s));	\
		xnpipe_flushq((__state), inq, free_ibuf, (__s));	\
671
		(__state)->status &= ~XNPIPE_USER_CONN;			\
672
		if ((__state)->status & XNPIPE_KERN_LCLOSE) {		\
673
			(__state)->status &= ~XNPIPE_KERN_LCLOSE;	\
674 675 676
			xnlock_put_irqrestore(&nklock, (__s));		\
			(__state)->ops.release((__state)->xstate);	\
			xnlock_get_irqsave(&nklock, (__s));		\
677
			xnpipe_minor_free(xnminor_from_state(__state));	\
678 679
		}							\
	} while(0)
680

Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
681 682 683 684
/*
 * Open the pipe from user-space.
 */

Philippe Gerum's avatar
Philippe Gerum committed
685
static int xnpipe_open(struct inode *inode, struct file *file)
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
686
{
687
	int minor, err = 0, sigpending;
688
	struct xnpipe_state *state;
689
	spl_t s;
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
690

691
	minor = MINOR(inode->i_rdev);
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
692

693 694
	if (minor >= XNPIPE_NDEVS)
		return -ENXIO;	/* TssTss... stop playing with mknod() ;o) */
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
695

696
	state = &xnpipe_states[minor];
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
697

698
	xnlock_get_irqsave(&nklock, s);
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
699

700
	/* Enforce exclusive open for the message queues. */
701
	if (state->status & (XNPIPE_USER_CONN | XNPIPE_USER_LCONN)) {
702 703 704
		xnlock_put_irqrestore(&nklock, s);
		return -EBUSY;
	}
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
705

706 707 708 709
	state->status |= XNPIPE_USER_LCONN;

	xnlock_put_irqrestore(&nklock, s);

710 711
	file->private_data = state;
	init_waitqueue_head(&state->readq);
Philippe Gerum's avatar
Philippe Gerum committed
712
	init_waitqueue_head(&state->syncq);
713 714 715 716 717

	xnlock_get_irqsave(&nklock, s);

	state->status |= XNPIPE_USER_CONN;
	state->status &= ~XNPIPE_USER_LCONN;
718
	state->wcount = 0;
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
719

720 721
	state->status &=
		~(XNPIPE_USER_ALL_WAIT | XNPIPE_USER_ALL_READY |
722
		  XNPIPE_USER_SIGIO);
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
723

724 725
	if ((state->status & XNPIPE_KERN_CONN) == 0) {
		if (file->f_flags & O_NONBLOCK) {
726
			xnpipe_cleanup_user_conn(state, s);
727 728 729
			xnlock_put_irqrestore(&nklock, s);
			return -EWOULDBLOCK;
		}
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
730

Philippe Gerum's avatar
Philippe Gerum committed
731
		sigpending = xnpipe_wait(state, XNPIPE_USER_WREAD, s,
732
					 state->status & XNPIPE_KERN_CONN);
Philippe Gerum's avatar
Philippe Gerum committed
733
		if (sigpending) {
734
			xnpipe_cleanup_user_conn(state, s);
735 736 737 738
			xnlock_put_irqrestore(&nklock, s);
			return -ERESTARTSYS;
		}
	}
Philippe Gerum's avatar
Philippe Gerum committed
739

740
	if (err)
741 742 743
		xnpipe_cleanup_user_conn(state, s);

	xnlock_put_irqrestore(&nklock, s);
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
744

745
	return err;
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
746 747
}

Philippe Gerum's avatar
Philippe Gerum committed
748
static int xnpipe_release(struct inode *inode, struct file *file)
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
749
{
750
	struct xnpipe_state *state = file->private_data;
751
	spl_t s;
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
752

753
	xnlock_get_irqsave(&nklock, s);
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
754

755 756
	xnpipe_dequeue_all(state, XNPIPE_USER_WREAD);
	xnpipe_dequeue_all(state, XNPIPE_USER_WSYNC);
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
757

758
	if (state->status & XNPIPE_KERN_CONN) {
759
		/* Unblock waiters. */
760
		if (xnsynch_pended_p(&state->synchbase)) {
761
			xnsynch_flush(&state->synchbase, XNRMID);
762
			xnsched_run();
763
		}
764
	}
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
765

766 767
	if (state->ops.input)
		state->ops.input(NULL, -EPIPE, state->xstate);
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
768

769
	if (state->asyncq) {	/* Clear the async queue */
770
		list_del(&state->alink);
771
		state->status &= ~XNPIPE_USER_SIGIO;
772 773
		xnlock_put_irqrestore(&nklock, s);
		fasync_helper(-1, file, 0, &state->asyncq);
774
		xnlock_get_irqsave(&nklock, s);
775
	}
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
776

777 778 779 780
	xnpipe_cleanup_user_conn(state, s);
	/*
	 * The extra state may not be available from now on, if
	 * xnpipe_disconnect() entered lingering close before we got
Philippe Gerum's avatar
Philippe Gerum committed
781
	 * there; so calling xnpipe_cleanup_user_conn() should be the
782 783 784
	 * last thing we do.
	 */
	xnlock_put_irqrestore(&nklock, s);
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
785

786
	return 0;
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
787 788
}

Philippe Gerum's avatar
Philippe Gerum committed
789
static ssize_t xnpipe_read(struct file *file,
790
			   char *buf, size_t count, loff_t *ppos)
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
791
{
792
	struct xnpipe_state *state = file->private_data;
793
	int sigpending, err = 0;
Philippe Gerum's avatar
Philippe Gerum committed
794
	size_t nbytes, inbytes;
795
	struct xnpipe_mh *mh;
796
	ssize_t ret;
797 798
	spl_t s;

799
	if (!access_wok(buf, count))
800 801 802 803
		return -EFAULT;

	xnlock_get_irqsave(&nklock, s);

804
	if ((state->status & XNPIPE_KERN_CONN) == 0) {
805 806 807
		xnlock_put_irqrestore(&nklock, s);
		return -EPIPE;
	}
808 809 810 811
	/*
	 * Queue probe and proc enqueuing must be seen atomically,
	 * including from the Xenomai side.
	 */
812
	if (list_empty(&state->outq)) {
813 814
		if (file->f_flags & O_NONBLOCK) {
			xnlock_put_irqrestore(&nklock, s);
815
			return -EWOULDBLOCK;
816 817
		}

Philippe Gerum's avatar
Philippe Gerum committed
818
		sigpending = xnpipe_wait(state, XNPIPE_USER_WREAD, s,
819
					 !list_empty(&state->outq));
820

821
		if (list_empty(&state->outq)) {
822
			xnlock_put_irqrestore(&nklock, s);
823
			return sigpending ? -ERESTARTSYS : 0;
824 825 826
		}
	}

827 828 829
	mh = list_get_entry(&state->outq, struct xnpipe_mh, link);
	state->nroutq--;

830 831 832 833 834 835 836 837 838 839 840 841 842 843 844
	/*
	 * We allow more data to be appended to the current message
	 * bucket while its contents is being copied to the user
	 * buffer, therefore, we need to loop until: 1) all the data
	 * has been copied, 2) we consumed the user buffer space
	 * entirely.
	 */

	inbytes = 0;

	for (;;) {
		nbytes = xnpipe_m_size(mh) - xnpipe_m_rdoff(mh);

		if (nbytes + inbytes > count)
			nbytes = count - inbytes;
845

846 847
		if (nbytes == 0)
			break;
848

849
		xnlock_put_irqrestore(&nklock, s);
850

851
		/* More data could be appended while doing