示例#1
0
/*
 *  get next block from a queue (up to a limit)
 */
Block*
qbread(Queue *q, int len)
{
	Block *b, *nb;
	int n;

	qlock(&q->rlock);
	if(waserror()){
		qunlock(&q->rlock);
		nexterror();
	}

	ilock(q);
	switch(qwait(q)){
	case 0:
		/* queue closed */
		iunlock(q);
		qunlock(&q->rlock);
		poperror();
		return nil;
	case -1:
		/* multiple reads on a closed queue */
		iunlock(q);
		error(q->err);
	}

	/* if we get here, there's at least one block in the queue */
	b = qremove(q);
	n = BLEN(b);

	/* split block if it's too big and this is not a message queue */
	nb = b;
	if(n > len){
		if((q->state&Qmsg) == 0){
			n -= len;
			b = allocb(n);
			memmove(b->wp, nb->rp+len, n);
			b->wp += n;
			qputback(q, b);
		}
		nb->wp = nb->rp + len;
	}

	/* restart producer */
	qwakeup_iunlock(q);

	poperror();
	qunlock(&q->rlock);
	return nb;
}
示例#2
0
文件: qio.c 项目: dhootha/akaros
/*
 *  get next block from a queue (up to a limit)
 */
struct block *qbread(struct queue *q, int len)
{
	ERRSTACK(1);
	struct block *b, *nb;
	int n;

	qlock(&q->rlock);
	if (waserror()) {
		qunlock(&q->rlock);
		nexterror();
	}

	spin_lock_irqsave(&q->lock);
	if (!qwait(q)) {
		/* queue closed */
		spin_unlock_irqsave(&q->lock);
		qunlock(&q->rlock);
		poperror();
		return NULL;
	}

	/* if we get here, there's at least one block in the queue */
	b = qremove(q);
	n = BLEN(b);

	/* split block if it's too big and this is not a message queue */
	nb = b;
	if (n > len) {
		PANIC_EXTRA(b);
		if ((q->state & Qmsg) == 0) {
			n -= len;
			b = allocb(n);
			memmove(b->wp, nb->rp + len, n);
			b->wp += n;
			qputback(q, b);
		}
		nb->wp = nb->rp + len;
	}

	/* restart producer */
	qwakeup_iunlock(q);

	poperror();
	qunlock(&q->rlock);
	return nb;
}
示例#3
0
文件: qio.c 项目: mtaufen/akaros
/* Helper and back-end for __qbread: extracts and returns a list of blocks
 * containing up to len bytes.  It may contain less than len even if q has more
 * data.
 *
 * Returns a code interpreted by __qbread, and the returned blist in ret. */
static int __try_qbread(struct queue *q, size_t len, int qio_flags,
                        struct block **real_ret, struct block *spare)
{
	struct block *ret, *ret_last, *first;
	size_t blen;

	if (qio_flags & QIO_CAN_ERR_SLEEP) {
		if (!qwait_and_ilock(q, qio_flags)) {
			spin_unlock_irqsave(&q->lock);
			return QBR_FAIL;
		}
		/* we qwaited and still hold the lock, so the q is not empty */
		first = q->bfirst;
	} else {
		spin_lock_irqsave(&q->lock);
		first = q->bfirst;
		if (!first) {
			spin_unlock_irqsave(&q->lock);
			return QBR_FAIL;
		}
	}
	blen = BLEN(first);
	if ((q->state & Qcoalesce) && (blen == 0)) {
		freeb(pop_first_block(q));
		spin_unlock_irqsave(&q->lock);
		/* Need to retry to make sure we have a first block */
		return QBR_AGAIN;
	}
	/* Qmsg is a bit weird.  The old 9ns code seemed to yank the entire block,
	 * regardless of len.  We'll do the same, and just return the minimum: the
	 * first block.  I'd be happy to remove this. */
	if (q->state & Qmsg) {
		ret = pop_first_block(q);
		goto out_ok;
	}
	/* Let's get at least something first - makes the code easier.  This way,
	 * we'll only ever split the block once. */
	if (blen <= len) {
		ret = pop_first_block(q);
		len -= blen;
	} else {
		/* need to split the block.  we won't actually take the first block out
		 * of the queue - we're just extracting a little bit. */
		if (!spare) {
			/* We have nothing and need a spare block.  Retry! */
			spin_unlock_irqsave(&q->lock);
			return QBR_SPARE;
		}
		copy_from_first_block(q, spare, len);
		ret = spare;
		goto out_ok;
	}
	/* At this point, we just grabbed the first block.  We can try to grab some
	 * more, up to len (if they want). */
	if (qio_flags & QIO_JUST_ONE_BLOCK)
		goto out_ok;
	ret_last = ret;
	while (q->bfirst && (len > 0)) {
		blen = BLEN(q->bfirst);
		if ((q->state & Qcoalesce) && (blen == 0)) {
			/* remove the intermediate 0 blocks */
			freeb(pop_first_block(q));
			continue;
		}
		if (blen > len) {
			/* We could try to split the block, but that's a huge pain.  For
			 * instance, we might need to move the main body of b into an
			 * extra_data of ret_last.  lots of ways for that to fail, and lots
			 * of cases to consider.  Easier to just bail out.  This is why I
			 * did the first block above: we don't need to worry about this. */
			 break;
		}
		ret_last->next = pop_first_block(q);
		ret_last = ret_last->next;
		len -= blen;
	}
out_ok:
	qwakeup_iunlock(q);
	*real_ret = ret;
	return QBR_OK;
}
示例#4
0
/*
 *  read a queue.  if no data is queued, post a Block
 *  and wait on its Rendez.
 */
long
qread(Queue *q, void *vp, int len)
{
	Block *b, *first, **l;
	int m, n;

	qlock(&q->rlock);
	if(waserror()){
		qunlock(&q->rlock);
		nexterror();
	}

	ilock(q);
again:
	switch(qwait(q)){
	case 0:
		/* queue closed */
		iunlock(q);
		qunlock(&q->rlock);
		poperror();
		return 0;
	case -1:
		/* multiple reads on a closed queue */
		iunlock(q);
		error(q->err);
	}

	/* if we get here, there's at least one block in the queue */
	if(q->state & Qcoalesce){
		/* when coalescing, 0 length blocks just go away */
		b = q->bfirst;
		if(BLEN(b) <= 0){
			freeb(qremove(q));
			goto again;
		}

		/*  grab the first block plus as many
		 *  following blocks as will completely
		 *  fit in the read.
		 */
		n = 0;
		l = &first;
		m = BLEN(b);
		for(;;) {
			*l = qremove(q);
			l = &b->next;
			n += m;

			b = q->bfirst;
			if(b == nil)
				break;
			m = BLEN(b);
			if(n+m > len)
				break;
		}
	} else {
		first = qremove(q);
		n = BLEN(first);
	}

	/* copy to user space outside of the ilock */
	iunlock(q);
	b = bl2mem(vp, first, len);
	ilock(q);

	/* take care of any left over partial block */
	if(b != nil){
		n -= BLEN(b);
		if(q->state & Qmsg)
			freeb(b);
		else
			qputback(q, b);
	}

	/* restart producer */
	qwakeup_iunlock(q);

	poperror();
	qunlock(&q->rlock);
	return n;
}
示例#5
0
文件: qio.c 项目: dhootha/akaros
/*
 *  read a queue.  if no data is queued, post a struct block
 *  and wait on its Rendez.
 */
long qread(struct queue *q, void *vp, int len)
{
	ERRSTACK(1);
	struct block *b, *first, **l;
	int m, n;

	qlock(&q->rlock);
	if (waserror()) {
		qunlock(&q->rlock);
		nexterror();
	}

	spin_lock_irqsave(&q->lock);
again:
	if (!qwait(q)) {
		/* queue closed */
		spin_unlock_irqsave(&q->lock);
		qunlock(&q->rlock);
		poperror();
		return 0;
	}

	/* if we get here, there's at least one block in the queue */
	// TODO: Consider removing the Qcoalesce flag and force a coalescing
	// strategy by default.
	if (q->state & Qcoalesce) {
		/* when coalescing, 0 length blocks just go away */
		b = q->bfirst;
		if (BLEN(b) <= 0) {
			freeb(qremove(q));
			goto again;
		}

		/*  grab the first block plus as many
		 *  following blocks as will completely
		 *  fit in the read.
		 */
		n = 0;
		l = &first;
		m = BLEN(b);
		for (;;) {
			*l = qremove(q);
			l = &b->next;
			n += m;

			b = q->bfirst;
			if (b == NULL)
				break;
			m = BLEN(b);
			if (n + m > len)
				break;
		}
	} else {
		first = qremove(q);
		n = BLEN(first);
	}

	/* copy to user space outside of the ilock */
	spin_unlock_irqsave(&q->lock);
	b = bl2mem(vp, first, len);
	spin_lock_irqsave(&q->lock);

	/* take care of any left over partial block */
	if (b != NULL) {
		n -= BLEN(b);
		if (q->state & Qmsg)
			freeb(b);
		else
			qputback(q, b);
	}

	/* restart producer */
	qwakeup_iunlock(q);

	poperror();
	qunlock(&q->rlock);
	return n;
}