blob: 2ca1cc9f107be34108acd7f2ef7f822104723a50 [file] [log] [blame]
Steven Luongdf7f8e82018-03-18 08:01:27 -07001/*
2 *------------------------------------------------------------------
3 * Copyright (c) 2018 Cisco and/or its affiliates.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at:
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *------------------------------------------------------------------
16 */
17
18#include <vlib/vlib.h>
19#include <vlib/unix/unix.h>
20#include <vlib/pci/pci.h>
21#include <vnet/ethernet/ethernet.h>
22#include <vnet/devices/devices.h>
23
24#include <vmxnet3/vmxnet3.h>
25
26static_always_inline void
27vmxnet3_tx_comp_ring_advance_next (vmxnet3_txq_t * txq)
28{
29 vmxnet3_tx_comp_ring *comp_ring = &txq->tx_comp_ring;
30
31 comp_ring->next++;
32 if (PREDICT_FALSE (comp_ring->next == txq->size))
33 {
34 comp_ring->next = 0;
35 comp_ring->gen ^= VMXNET3_TXCF_GEN;
36 }
37}
38
39static_always_inline void
40vmxnet3_tx_ring_advance_produce (vmxnet3_txq_t * txq)
41{
42 txq->tx_ring.produce++;
43 if (PREDICT_FALSE (txq->tx_ring.produce == txq->size))
44 {
45 txq->tx_ring.produce = 0;
46 txq->tx_ring.gen ^= VMXNET3_TXF_GEN;
47 }
48}
49
50static_always_inline void
51vmxnet3_tx_ring_advance_consume (vmxnet3_txq_t * txq)
52{
53 txq->tx_ring.consume++;
54 txq->tx_ring.consume &= txq->size - 1;
55}
56
57static_always_inline void
58vmxnet3_txq_release (vlib_main_t * vm, vmxnet3_device_t * vd,
59 vmxnet3_txq_t * txq)
60{
61 vmxnet3_tx_comp *tx_comp;
62 u32 bi0;
63 vmxnet3_tx_comp_ring *comp_ring;
64 u16 eop_idx, desc_idx;
65
66 comp_ring = &txq->tx_comp_ring;
67 tx_comp = &txq->tx_comp[comp_ring->next];
68
69 while ((tx_comp->flags & VMXNET3_TXCF_GEN) == comp_ring->gen)
70 {
71 eop_idx = tx_comp->index & VMXNET3_TXC_INDEX;
72 do
73 {
74 desc_idx = txq->tx_ring.consume;
75 bi0 = txq->tx_ring.bufs[desc_idx];
76 txq->tx_ring.bufs[desc_idx] = ~0;
77 vlib_buffer_free_no_next (vm, &bi0, 1);
78 vmxnet3_tx_ring_advance_consume (txq);
79 }
80 while (desc_idx != eop_idx);
81
82 vmxnet3_tx_comp_ring_advance_next (txq);
83 tx_comp = &txq->tx_comp[comp_ring->next];
84 }
85}
86
87static_always_inline u16
88vmxnet3_tx_ring_space_left (vmxnet3_txq_t * txq)
89{
90 u16 count;
91
92 count = (txq->tx_ring.consume - txq->tx_ring.produce - 1);
93 /* Wrapped? */
94 if (txq->tx_ring.produce >= txq->tx_ring.consume)
95 count += txq->size;
96 return count;
97}
98
99VNET_DEVICE_CLASS_TX_FN (vmxnet3_device_class) (vlib_main_t * vm,
100 vlib_node_runtime_t * node,
101 vlib_frame_t * frame)
102{
103 vmxnet3_main_t *vmxm = &vmxnet3_main;
104 vnet_interface_output_runtime_t *rd = (void *) node->runtime_data;
105 vmxnet3_device_t *vd = pool_elt_at_index (vmxm->devices, rd->dev_instance);
106 u32 *buffers = vlib_frame_args (frame);
107 u32 bi0;
108 vlib_buffer_t *b0;
109 vmxnet3_tx_desc *txd;
110 u32 desc_idx, generation, first_idx;
111 u16 space_left;
112 u16 n_left = frame->n_vectors;
113 vmxnet3_txq_t *txq;
114 u32 thread_index = vm->thread_index;
115 u16 qid = thread_index;
116 u16 n_retry = 5;
117
118 txq = vec_elt_at_index (vd->txqs, qid % vd->num_tx_queues);
119
120 clib_spinlock_lock_if_init (&txq->lock);
121
122retry:
123 vmxnet3_txq_release (vm, vd, txq);
124
125 while (n_left)
126 {
127 bi0 = buffers[0];
128 txd = 0;
129
130 /*
131 * Toggle the generation bit for SOP fragment to avoid device starts
132 * reading incomplete packet
133 */
134 generation = txq->tx_ring.gen ^ VMXNET3_TXF_GEN;
135 first_idx = txq->tx_ring.produce;
136 while (1)
137 {
138 b0 = vlib_get_buffer (vm, bi0);
139 VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b0);
140
141 space_left = vmxnet3_tx_ring_space_left (txq);
142 if (PREDICT_FALSE (space_left == 0))
143 {
144 break;
145 }
146
147 desc_idx = txq->tx_ring.produce;
148
149 vmxnet3_tx_ring_advance_produce (txq);
150 txq->tx_ring.bufs[desc_idx] = bi0;
151
152 txd = &txq->tx_desc[desc_idx];
153 txd->address =
154 vlib_get_buffer_data_physical_address (vm,
155 bi0) + b0->current_data;
156
157 txd->flags[0] = generation | b0->current_length;
158
159 generation = txq->tx_ring.gen;
160 if (b0->flags & VLIB_BUFFER_NEXT_PRESENT)
161 {
162 txd->flags[1] = 0;
163 bi0 = b0->next_buffer;
164 }
165 else
166 break;
167 }
168
169 if (PREDICT_TRUE (txd != 0))
170 {
171 txd->flags[1] = VMXNET3_TXF_CQ | VMXNET3_TXF_EOP;
172 asm volatile ("":::"memory");
173 /*
174 * Now toggle back the generation bit for the first segment.
175 * Device can start reading the packet
176 */
177 txq->tx_desc[first_idx].flags[0] ^= VMXNET3_TXF_GEN;
178 vmxnet3_reg_write (vd, 0, VMXNET3_REG_TXPROD, txq->tx_ring.produce);
179 }
180
181 if (PREDICT_FALSE (space_left == 0))
182 {
183 break;
184 }
185
186 buffers++;
187 n_left--;
188 }
189
190 if (PREDICT_FALSE (n_left))
191 {
192 if (PREDICT_TRUE (n_retry--))
193 goto retry;
194 vlib_buffer_free (vm, buffers, n_left);
195 vlib_error_count (vm, node->node_index, VMXNET3_TX_ERROR_NO_FREE_SLOTS,
196 n_left);
197 }
198 clib_spinlock_unlock_if_init (&txq->lock);
199
200 return (frame->n_vectors - n_left);
201}
202
203/*
204 * fd.io coding-style-patch-verification: ON
205 *
206 * Local Variables:
207 * eval: (c-set-style "gnu")
208 * End:
209 */