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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
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
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
|
/*-
* Copyright (c) 2009-2012,2016 Microsoft Corp.
* Copyright (c) 2012 NetApp Inc.
* Copyright (c) 2012 Citrix Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice unmodified, this list of conditions, and the following
* disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/sysctl.h>
#include <dev/hyperv/vmbus/vmbus_reg.h>
#include <dev/hyperv/vmbus/vmbus_brvar.h>
/* Amount of space available for write */
#define VMBUS_BR_WAVAIL(r, w, z) \
(((w) >= (r)) ? ((z) - ((w) - (r))) : ((r) - (w)))
/* Increase bufing index */
#define VMBUS_BR_IDXINC(idx, inc, sz) (((idx) + (inc)) % (sz))
static int vmbus_br_sysctl_state(SYSCTL_HANDLER_ARGS);
static int vmbus_br_sysctl_state_bin(SYSCTL_HANDLER_ARGS);
static void vmbus_br_setup(struct vmbus_br *, void *, int);
static int
vmbus_br_sysctl_state(SYSCTL_HANDLER_ARGS)
{
const struct vmbus_br *br = arg1;
uint32_t rindex, windex, imask, ravail, wavail;
char state[256];
rindex = br->vbr_rindex;
windex = br->vbr_windex;
imask = br->vbr_imask;
wavail = VMBUS_BR_WAVAIL(rindex, windex, br->vbr_dsize);
ravail = br->vbr_dsize - wavail;
snprintf(state, sizeof(state),
"rindex:%u windex:%u imask:%u ravail:%u wavail:%u",
rindex, windex, imask, ravail, wavail);
return sysctl_handle_string(oidp, state, sizeof(state), req);
}
/*
* Binary bufring states.
*/
static int
vmbus_br_sysctl_state_bin(SYSCTL_HANDLER_ARGS)
{
#define BR_STATE_RIDX 0
#define BR_STATE_WIDX 1
#define BR_STATE_IMSK 2
#define BR_STATE_RSPC 3
#define BR_STATE_WSPC 4
#define BR_STATE_MAX 5
const struct vmbus_br *br = arg1;
uint32_t rindex, windex, wavail, state[BR_STATE_MAX];
rindex = br->vbr_rindex;
windex = br->vbr_windex;
wavail = VMBUS_BR_WAVAIL(rindex, windex, br->vbr_dsize);
state[BR_STATE_RIDX] = rindex;
state[BR_STATE_WIDX] = windex;
state[BR_STATE_IMSK] = br->vbr_imask;
state[BR_STATE_WSPC] = wavail;
state[BR_STATE_RSPC] = br->vbr_dsize - wavail;
return sysctl_handle_opaque(oidp, state, sizeof(state), req);
}
void
vmbus_br_sysctl_create(struct sysctl_ctx_list *ctx, struct sysctl_oid *br_tree,
struct vmbus_br *br, const char *name)
{
struct sysctl_oid *tree;
char desc[64];
tree = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(br_tree), OID_AUTO,
name, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
if (tree == NULL)
return;
snprintf(desc, sizeof(desc), "%s state", name);
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "state",
CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
br, 0, vmbus_br_sysctl_state, "A", desc);
snprintf(desc, sizeof(desc), "%s binary state", name);
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "state_bin",
CTLTYPE_OPAQUE | CTLFLAG_RD | CTLFLAG_MPSAFE,
br, 0, vmbus_br_sysctl_state_bin, "IU", desc);
}
void
vmbus_rxbr_intr_mask(struct vmbus_rxbr *rbr)
{
rbr->rxbr_imask = 1;
mb();
}
static __inline uint32_t
vmbus_rxbr_avail(const struct vmbus_rxbr *rbr)
{
uint32_t rindex, windex;
/* Get snapshot */
rindex = rbr->rxbr_rindex;
windex = rbr->rxbr_windex;
return (rbr->rxbr_dsize -
VMBUS_BR_WAVAIL(rindex, windex, rbr->rxbr_dsize));
}
uint32_t
vmbus_rxbr_intr_unmask(struct vmbus_rxbr *rbr)
{
rbr->rxbr_imask = 0;
mb();
/*
* Now check to see if the ring buffer is still empty.
* If it is not, we raced and we need to process new
* incoming channel packets.
*/
return vmbus_rxbr_avail(rbr);
}
static void
vmbus_br_setup(struct vmbus_br *br, void *buf, int blen)
{
br->vbr = buf;
br->vbr_dsize = blen - sizeof(struct vmbus_bufring);
}
void
vmbus_rxbr_init(struct vmbus_rxbr *rbr)
{
mtx_init(&rbr->rxbr_lock, "vmbus_rxbr", NULL, MTX_SPIN);
}
void
vmbus_rxbr_deinit(struct vmbus_rxbr *rbr)
{
mtx_destroy(&rbr->rxbr_lock);
}
void
vmbus_rxbr_setup(struct vmbus_rxbr *rbr, void *buf, int blen)
{
vmbus_br_setup(&rbr->rxbr, buf, blen);
}
void
vmbus_txbr_init(struct vmbus_txbr *tbr)
{
mtx_init(&tbr->txbr_lock, "vmbus_txbr", NULL, MTX_SPIN);
}
void
vmbus_txbr_deinit(struct vmbus_txbr *tbr)
{
mtx_destroy(&tbr->txbr_lock);
}
void
vmbus_txbr_setup(struct vmbus_txbr *tbr, void *buf, int blen)
{
vmbus_br_setup(&tbr->txbr, buf, blen);
}
/*
* When we write to the ring buffer, check if the host needs to be
* signaled.
*
* The contract:
* - The host guarantees that while it is draining the TX bufring,
* it will set the br_imask to indicate it does not need to be
* interrupted when new data are added.
* - The host guarantees that it will completely drain the TX bufring
* before exiting the read loop. Further, once the TX bufring is
* empty, it will clear the br_imask and re-check to see if new
* data have arrived.
*/
static __inline boolean_t
vmbus_txbr_need_signal(const struct vmbus_txbr *tbr, uint32_t old_windex)
{
mb();
if (tbr->txbr_imask)
return (FALSE);
__compiler_membar();
/*
* This is the only case we need to signal when the
* ring transitions from being empty to non-empty.
*/
if (old_windex == tbr->txbr_rindex)
return (TRUE);
return (FALSE);
}
static __inline uint32_t
vmbus_txbr_avail(const struct vmbus_txbr *tbr)
{
uint32_t rindex, windex;
/* Get snapshot */
rindex = tbr->txbr_rindex;
windex = tbr->txbr_windex;
return VMBUS_BR_WAVAIL(rindex, windex, tbr->txbr_dsize);
}
static __inline uint32_t
vmbus_txbr_copyto(const struct vmbus_txbr *tbr, uint32_t windex,
const void *src0, uint32_t cplen)
{
const uint8_t *src = src0;
uint8_t *br_data = tbr->txbr_data;
uint32_t br_dsize = tbr->txbr_dsize;
if (cplen > br_dsize - windex) {
uint32_t fraglen = br_dsize - windex;
/* Wrap-around detected */
memcpy(br_data + windex, src, fraglen);
memcpy(br_data, src + fraglen, cplen - fraglen);
} else {
memcpy(br_data + windex, src, cplen);
}
return VMBUS_BR_IDXINC(windex, cplen, br_dsize);
}
/*
* Write scattered channel packet to TX bufring.
*
* The offset of this channel packet is written as a 64bits value
* immediately after this channel packet.
*/
int
vmbus_txbr_write(struct vmbus_txbr *tbr, const struct iovec iov[], int iovlen,
boolean_t *need_sig)
{
uint32_t old_windex, windex, total;
uint64_t save_windex;
int i;
total = 0;
for (i = 0; i < iovlen; i++)
total += iov[i].iov_len;
total += sizeof(save_windex);
mtx_lock_spin(&tbr->txbr_lock);
/*
* NOTE:
* If this write is going to make br_windex same as br_rindex,
* i.e. the available space for write is same as the write size,
* we can't do it then, since br_windex == br_rindex means that
* the bufring is empty.
*/
if (vmbus_txbr_avail(tbr) <= total) {
mtx_unlock_spin(&tbr->txbr_lock);
return (EAGAIN);
}
/* Save br_windex for later use */
old_windex = tbr->txbr_windex;
/*
* Copy the scattered channel packet to the TX bufring.
*/
windex = old_windex;
for (i = 0; i < iovlen; i++) {
windex = vmbus_txbr_copyto(tbr, windex,
iov[i].iov_base, iov[i].iov_len);
}
/*
* Set the offset of the current channel packet.
*/
save_windex = ((uint64_t)old_windex) << 32;
windex = vmbus_txbr_copyto(tbr, windex, &save_windex,
sizeof(save_windex));
/*
* Update the write index _after_ the channel packet
* is copied.
*/
__compiler_membar();
tbr->txbr_windex = windex;
mtx_unlock_spin(&tbr->txbr_lock);
*need_sig = vmbus_txbr_need_signal(tbr, old_windex);
return (0);
}
static __inline uint32_t
vmbus_rxbr_copyfrom(const struct vmbus_rxbr *rbr, uint32_t rindex,
void *dst0, int cplen)
{
uint8_t *dst = dst0;
const uint8_t *br_data = rbr->rxbr_data;
uint32_t br_dsize = rbr->rxbr_dsize;
if (cplen > br_dsize - rindex) {
uint32_t fraglen = br_dsize - rindex;
/* Wrap-around detected. */
memcpy(dst, br_data + rindex, fraglen);
memcpy(dst + fraglen, br_data, cplen - fraglen);
} else {
memcpy(dst, br_data + rindex, cplen);
}
return VMBUS_BR_IDXINC(rindex, cplen, br_dsize);
}
int
vmbus_rxbr_peek(struct vmbus_rxbr *rbr, void *data, int dlen)
{
mtx_lock_spin(&rbr->rxbr_lock);
/*
* The requested data and the 64bits channel packet
* offset should be there at least.
*/
if (vmbus_rxbr_avail(rbr) < dlen + sizeof(uint64_t)) {
mtx_unlock_spin(&rbr->rxbr_lock);
return (EAGAIN);
}
vmbus_rxbr_copyfrom(rbr, rbr->rxbr_rindex, data, dlen);
mtx_unlock_spin(&rbr->rxbr_lock);
return (0);
}
/*
* NOTE:
* We assume (dlen + skip) == sizeof(channel packet).
*/
int
vmbus_rxbr_read(struct vmbus_rxbr *rbr, void *data, int dlen, uint32_t skip)
{
uint32_t rindex, br_dsize = rbr->rxbr_dsize;
KASSERT(dlen + skip > 0, ("invalid dlen %d, offset %u", dlen, skip));
mtx_lock_spin(&rbr->rxbr_lock);
if (vmbus_rxbr_avail(rbr) < dlen + skip + sizeof(uint64_t)) {
mtx_unlock_spin(&rbr->rxbr_lock);
return (EAGAIN);
}
/*
* Copy channel packet from RX bufring.
*/
rindex = VMBUS_BR_IDXINC(rbr->rxbr_rindex, skip, br_dsize);
rindex = vmbus_rxbr_copyfrom(rbr, rindex, data, dlen);
/*
* Discard this channel packet's 64bits offset, which is useless to us.
*/
rindex = VMBUS_BR_IDXINC(rindex, sizeof(uint64_t), br_dsize);
/*
* Update the read index _after_ the channel packet is fetched.
*/
__compiler_membar();
rbr->rxbr_rindex = rindex;
mtx_unlock_spin(&rbr->rxbr_lock);
return (0);
}
|