struct ptr_ring { int producer ____cacheline_aligned_in_smp;
spinlock_t producer_lock; int consumer_head ____cacheline_aligned_in_smp; /* next valid entry */ int consumer_tail; /* next entry to invalidate */
spinlock_t consumer_lock; /* Shared consumer/producer data */ /* Read-only by both the producer and the consumer */ int size ____cacheline_aligned_in_smp; /* max entries in queue */ int batch; /* number of entries to consume in a batch */ void **queue;
};
/* Note: callers invoking this in a loop must use a compiler barrier, *forexamplecpu_relax(). * *NB:thisisunlike__ptr_ring_emptyinthatcallersmustholdproducer_lock: *seee.g.ptr_ring_full.
*/ staticinlinebool __ptr_ring_full(struct ptr_ring *r)
{ return r->queue[r->producer];
}
spin_lock_bh(&r->producer_lock);
ret = __ptr_ring_full(r);
spin_unlock_bh(&r->producer_lock);
return ret;
}
/* Note: callers invoking this in a loop must use a compiler barrier, *forexamplecpu_relax().Callersmustholdproducer_lock. *Callersareresponsibleformakingsurepointerthatisbeingqueued *pointstoavaliddata.
*/ staticinlineint __ptr_ring_produce(struct ptr_ring *r, void *ptr)
{ if (unlikely(!r->size) || r->queue[r->producer]) return -ENOSPC;
/* Make sure the pointer we are storing points to a valid data. */ /* Pairs with the dependency ordering in __ptr_ring_consume. */
smp_wmb();
spin_lock_bh(&r->consumer_lock);
ret = __ptr_ring_empty(r);
spin_unlock_bh(&r->consumer_lock);
return ret;
}
/* Must only be called after __ptr_ring_peek returned !NULL */ staticinlinevoid __ptr_ring_discard_one(struct ptr_ring *r)
{ /* Fundamentally, what we want to do is update consumer *indexandzeroouttheentrysoproducercanreuseit. *Doingitnaivelyateachconsumewouldbeassimpleas: *consumer=r->consumer; *r->queue[consumer++]=NULL; *if(unlikely(consumer>=r->size)) *consumer=0; *r->consumer=consumer; *butthatissuboptimalwhentheringisfullasproduceriswriting *outnewentriesinthesamecacheline.Defertheseupdatesuntila *batchofentrieshasbeenconsumed.
*/ /* Note: we must keep consumer_head valid at all times for __ptr_ring_empty *toworkcorrectly.
*/ int consumer_head = r->consumer_head; int head = consumer_head++;
/* Once we have processed enough entries invalidate them in *theringallatoncesoproducercanreusetheirspaceinthering. *Wealsodothiswhenwereachendofthering-notmandatory *buthelpskeeptheimplementationsimple.
*/ if (unlikely(consumer_head - r->consumer_tail >= r->batch ||
consumer_head >= r->size)) { /* Zero out entries in the reverse order: this way we touch the *cachelinethatproducermightcurrentlybereadingthelast; *producerwon'tmakeprogressandtouchothercachelines *besidesthefirstoneuntilwewriteoutallentries.
*/ while (likely(head >= r->consumer_tail))
r->queue[head--] = NULL;
r->consumer_tail = consumer_head;
} if (unlikely(consumer_head >= r->size)) {
consumer_head = 0;
r->consumer_tail = 0;
} /* matching READ_ONCE in __ptr_ring_empty for lockless tests */
WRITE_ONCE(r->consumer_head, consumer_head);
}
/* The READ_ONCE in __ptr_ring_peek guarantees that anyone *accessingdatathroughthepointerisuptodate.Pairs *withsmp_wmbin__ptr_ring_produce.
*/
ptr = __ptr_ring_peek(r); if (ptr)
__ptr_ring_discard_one(r);
return ptr;
}
staticinlineint __ptr_ring_consume_batched(struct ptr_ring *r, void **array, int n)
{ void *ptr; int i;
for (i = 0; i < n; i++) {
ptr = __ptr_ring_consume(r); if (!ptr) break;
array[i] = ptr;
}
staticinlineint ptr_ring_consume_batched(struct ptr_ring *r, void **array, int n)
{ int ret;
spin_lock(&r->consumer_lock);
ret = __ptr_ring_consume_batched(r, array, n);
spin_unlock(&r->consumer_lock);
return ret;
}
staticinlineint ptr_ring_consume_batched_irq(struct ptr_ring *r, void **array, int n)
{ int ret;
spin_lock_irq(&r->consumer_lock);
ret = __ptr_ring_consume_batched(r, array, n);
spin_unlock_irq(&r->consumer_lock);
return ret;
}
staticinlineint ptr_ring_consume_batched_any(struct ptr_ring *r, void **array, int n)
{ unsignedlong flags; int ret;
spin_lock_irqsave(&r->consumer_lock, flags);
ret = __ptr_ring_consume_batched(r, array, n);
spin_unlock_irqrestore(&r->consumer_lock, flags);
return ret;
}
staticinlineint ptr_ring_consume_batched_bh(struct ptr_ring *r, void **array, int n)
{ int ret;
spin_lock_bh(&r->consumer_lock);
ret = __ptr_ring_consume_batched(r, array, n);
spin_unlock_bh(&r->consumer_lock);
return ret;
}
/* Cast to structure type and call a function without discarding from FIFO. *Functionmustreturnavalue. *Callersmusttakeconsumer_lock.
*/ #define __PTR_RING_PEEK_CALL(r, f) ((f)(__ptr_ring_peek(r)))
/* Not all gfp_t flags (besides GFP_KERNEL) are allowed. See *documentationforvmallocforwhichofthemarelegal.
*/ staticinlinevoid **__ptr_ring_init_queue_alloc_noprof(unsignedint size, gfp_t gfp)
{ if (size > KMALLOC_MAX_SIZE / sizeof(void *)) return NULL; return kvmalloc_array_noprof(size, sizeof(void *), gfp | __GFP_ZERO);
}
staticinlinevoid __ptr_ring_set_size(struct ptr_ring *r, int size)
{
r->size = size;
r->batch = SMP_CACHE_BYTES * 2 / sizeof(*(r->queue)); /* We need to set batch at least to 1 to make logic *in__ptr_ring_discard_oneworkcorrectly. *Batchingtoomuch(becauseringissmall)wouldcausealotof *burstiness.Needstuning,fornowdisablebatching.
*/ if (r->batch > r->size / 2 || !r->batch)
r->batch = 1;
}
staticinlineint ptr_ring_init_noprof(struct ptr_ring *r, int size, gfp_t gfp)
{
r->queue = __ptr_ring_init_queue_alloc_noprof(size, gfp); if (!r->queue) return -ENOMEM;
/* *Cleanoutbufferedentries(forsimplicity).Thiswayfollowingcode *cantestentriesforNULLandifnotassumetheyarevalid.
*/
head = r->consumer_head - 1; while (likely(head >= r->consumer_tail))
r->queue[head--] = NULL;
r->consumer_tail = r->consumer_head;
/* *Gooverentriesinbatch,startmovingheadbackandcopyentries. *Stopwhenwerunintopreviouslyunconsumedentries.
*/ while (n) {
head = r->consumer_head - 1; if (head < 0)
head = r->size - 1; if (r->queue[head]) { /* This batch entry will have to be destroyed. */ goto done;
}
r->queue[head] = batch[--n];
r->consumer_tail = head; /* matching READ_ONCE in __ptr_ring_empty for lockless tests */
WRITE_ONCE(r->consumer_head, head);
}
done: /* Destroy all entries left in the batch. */ while (n)
destroy(batch[--n]);
spin_unlock(&r->producer_lock);
spin_unlock_irqrestore(&r->consumer_lock, flags);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.