Disclaimer: This document is only for learning and exchange, do not use for other commercial purposes author: Chaoyang _tony
E-mail:linzhaolover@gmail.com
Create date:2013-8-5 19:31:33 Monday
Last Change:2013-8-6 14:33:21 Tuesday
Reprint please indicate the source: Http://blog.csdn.net/linzhaolover
This article please combine the Intel DPDK source code to read, the source code can go to the Http://dpdk.org/dev webpage to download; More official documents please visit http://dpdk.org Intel DPDK Exchange Group Hope everybody joins each other to learn, QQ group number: 289784125
This article is based on the Intel DPDK source 1.3.1 version of the explanation;
Reference Document: Intel-dpdk-programmers-guide.pdf, please go to Intel website to download http://www.intel.com/content/dam/www/public/us/en/documents/ Guides/intel-dpdk-programmers-guide.pdf
Summary
Intel DPDK provides a set of ring queue management code, support single producer products row, single consumer products out of the column, a number of producer products row, multiple product consumption this product column operation;
We explain the code in the App/test/test_ring.c file, and the TEST_RING_BASIC_EX () function completes a basic function test function; 1. Creation of ring
RP = Rte_ring_create ("test_ring_basic_ex", Ring_size, Socket_id_any,
Ring_f_sp_enq | RING_F_SC_DEQ);
Call the Rte_ring_create function to create a ring,
The first parameter "TEST_RING_BASIC_EX" is the name of the ring,
The second parameter ring_size is the size of the ring;
The third parameter is created on which socket ID, which is specified arbitrarily;
The fourth parameter is to specify this ring to support single entry;
Let me take a look at the Rte_ring_create function is mainly completed which operations;
Rte_rwlock_write_lock (Rte_eal_tailq_rwlock);
The lock operation of read-write lock is performed;
MZ = Rte_memzone_reserve (Mz_name, Ring_size, socket_id, mz_flags);
Reserving a portion of the memory space for the ring, the size of which is the size of the ring_size sizeof (struct rte_ring);
r = mz->addr;
/* init the ring structure/
memset (r, 0, sizeof (*r));
rte_snprintf (r->name, sizeof (R->name), "%s", name);
R->flags = flags;
R->prod.watermark = count;
R->prod.sp_enqueue =!! (Flags & Ring_f_sp_enq);
R->cons.sc_dequeue =!! (Flags & RING_F_SC_DEQ);
R->prod.size = R->cons.size = count;
R->prod.mask = R->cons.mask = count-1;
R->prod.head = R->cons.head = 0;
R->prod.tail = R->cons.tail = 0;
Tailq_insert_tail (Ring_list, R, next);
The virtual address will be obtained to the ring, then initialize her, prod on behalf of the creator, cons on behalf of consumers;
The producer can produce the largest number of count, the mask of its modulo is count-1; it is currently 0 products, so the producer's head and consumer head are set to 0, and its tail is not 0;
Rte_rwlock_write_unlock (Rte_eal_tailq_rwlock);
Write lock unlock operation to perform read-write lock;
2, the ring of single producer products row
Rte_ring_enqueue (RP, Obj[i])
A single row of the ring;
__rte_ring_sp_do_enqueue
The final call to the above function, for a single row, we look at its implementation;
Prod_head = r->prod.head;
Cons_tail = r->cons.tail;
The producer's head index and the consumer's tail index are temporarily handed over to the temporary variable;
Free_entries = mask + cons_tail-prod_head;
Calculate the amount of remaining storage space;
Prod_next = Prod_head + N;
R->prod.head = Prod_next;
If there is enough space left, we first move the temporary variable prod_next, and the colleague shifts the producer's head index back to N;
/* Write entries in ring *
/for (i = 0; likely (i < n); i++)
r->ring[(Prod_head + i) & mask] = obj_table[ I];
RTE_WMB ();
Write operation, the goal to join the operation, it does not have any large amount of data memory copy operation, just for the pointer assignment operation, so DPDK memory operation is very fast, should be counted as 0 copies;
R->prod.tail = Prod_next;
After successful writing, the producer's tail index is assigned to Prox_next, which is to move it back to n index; we succeeded in inserting N products; At present, it is a single operation, the index is currently n=1;
3, the ring of single consumer products out
Rte_ring_dequeue (RP, &obj[i]);
The same team also contains several layers of calls, eventually positioning to the __rte_ring_sc_do_dequeue function;
Cons_head = r->cons.head;
Prod_tail = r->prod.tail;
First, the consumer's head index and producer's head index are assigned to the temporary variable;
Entries = Prod_tail-cons_head;
Calculate how many products are currently in the ring;
Cons_next = Cons_head + N;
R->cons.head = Cons_next;
If you have enough products, move the temporary variable cons_next back to n values, point to where you want to take a few products, and move the consumer's head index back to N; This is currently n=1 because it is a single fetch;
/* Copy in Table *
/RTE_RMB ();
for (i = 0; likely (i < n); i++) {
Obj_table[i] = r->ring[(Cons_head + i) & mask];
Performs the read operation, also does not have any big data volume copy, just carries on the pointer assignment;
R->cons.tail = Cons_next;
Finally, the consumer's tail index also like to move n after the end of the consumer's head index;
4, the Ring of multi-producer products Row
The implementation of the row is in the __rte_ring_mp_do_enqueue () function, defined in the Dpdk/lib/librte_ring/rte_ring.h file; in fact, this function is similar to a single row function;
/* Move Prod.head atomically * * *
Reset N to the initial burst count
/n = max;
.................
Prod_next = Prod_head + N;
Success = Rte_atomic32_cmpset (&r->prod.head, Prod_head,
prod_next); while
(unlikely (success = 0));
In the single producer, the producer's head and the consumer's tail are directly assigned to the temporary variable to find the remaining storage space; Finally, the producer's head index is moved back N,
But in many producers, it is to be judged whether the head is competing with other producers,
Success = Rte_atomic32_cmpset (&r->prod.head, Prod_head,
Prod_next);
Whether there are other producers to modify the Prod.head, so this should be again to determine whether the prod.head is equal to Prod_head, if it is equal to, then move it back N, that is, the Prod_next value assigned to Prod.head;
If it is not equal, it will fail, and you need to go into the Do While loop to cycle again; refresh the values of Prod_head and Prod_next and Prod.head;
/* Write entries in ring *
/for (i = 0; likely (i < n); i++)
r->ring[(Prod_head + i) & mask] = obj_table[ I];
RTE_WMB ();
Perform product write operations;
After the write operation is completed, if the single producer should be directly modifying the producer tail index, it will be postponed to N, but at present it is a multi-producer operation; How is it achieved?
*
* If There are other enqueues in progress that preceeded us,
* We need to wait for them to complete
* * while (Unlikely (R->prod.tail!= prod_head))
rte_pause ();
R->prod.tail = Prod_next;
This also determines whether the current producer tail index is equal to the producer header index stored in the temporary variable.
If it is not equal to, the description, there are other threads are still executing, and should be stored before it, has not been able to update prod.tail, and other producers update tail, will make prod.tail==prod_head;
Then update, prod.tail back to move N, the best to achieve prod.tail==prod.head==prod_next==prod_head+n;
5, the ring of multiple consumer products out
Multiple consumers take the product at the same time in the __rte_ring_mc_do_dequeue () function, defined in the Dpdk/lib/librte_ring/rte_ring.h file;
/* Move Cons.head atomically *
/do {
/* Restore N as it may change every loop/
n = max;
Cons_head = r->cons.head;
Prod_tail = r->prod.tail;
...................
Cons_next = Cons_head + N;
Success = Rte_atomic32_cmpset (&r->cons.head, Cons_head,
cons_next); while
(unlikely (success = 0));
Like many producers, the outside contains a do-while loop to prevent competition from multiple consumer operations;
The first index of the consumer and the index of the producer are assigned to the temporary variable in the loop; Let's judge how many remaining products are in the loop queue,
If there are n products, the temporary variable Cons_next back to the N, and then determine whether the current consumer header index is also equal to the value just saved in the temporary variable cons_head, such as equality, indicating that no competition, the cons_next assigned to
The consumer's header index R->cons.head, if not equal, requires a do-while loop again;
/* Copy in Table *
/RTE_RMB ();
for (i = 0; likely (i < n); i++) {
Obj_table[i] = r->ring[(Cons_head + i) & mask];
After successfully updating the consumer header index, perform the read product operation, which does not have the big data copy operation, only carries on the pointer's re assignment operation;
*
* If There are other dequeues in progress that preceded us,
* We need to wait for them to complete
* * while (Unlikely (R->cons.tail!= cons_head))
rte_pause ();
__ring_stat_add (R, Deq_success, N);
R->cons.tail = Cons_next;
After the completion of the reading, it is necessary to update the consumer's tail index;
In order to avoid competition, it is necessary to determine whether there are other consumers to update the consumer tail index; if the current consumer tail index does not equal the value of the temporary variable cons_head, wait for other consumers to modify the tail index;
If equal, the machine can move the current consumer's tail index back to the N index value,
Implement r->cons.tail=r->cons.head=cons_next=cons_head+n;
6. Other decision functions of ring
Rte_ring_lookup ("TEST_RING_BASIC_EX")
Verify that the ring with TEST_RING_BASIC_EX is created successfully;
Rte_ring_empty (RP)
Determine whether the ring is empty;
Rte_ring_full (RP)
Judge whether the ring is full;
Rte_ring_free_count (RP)
To determine how much storage space the ring has left;
Technology needs to be improved, if the article has the wrong place to want the reader to correct, mutual exchange, learn from each other; O (∩_∩) o~