-
Notifications
You must be signed in to change notification settings - Fork 30
/
aq_tsn.c
264 lines (218 loc) · 5.78 KB
/
aq_tsn.c
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
// SPDX-License-Identifier: GPL-2.0-only
/* Atlantic Network Driver
*
* Copyright (C) 2014-2019 aQuantia Corporation
* Copyright (C) 2019-2020 Marvell International Ltd.
*/
/*
* File aq_tsn.c:
* Definition of functions for TSN network support.
*/
#include "aq_nic.h"
#include "aq_hw.h"
#include "aq_ptp.h"
#include "aq_tsn.h"
#include "aq_ring.h"
#include "aq_nic.h"
#include "aq_hw_utils.h"
#include "aq_main.h"
#include "aq_sysfs.h"
#include <linux/moduleparam.h>
#ifdef TSN_SUPPORT
static int aq_get_link_status(struct aq_tsn_s *self, struct atl_link_cmd __user *ureq)
{
struct atl_link_cmd link = {ATL_LINK_DOWN};
switch(self->aq_nic->link_status.mbps)
{
case 100U:
link.speed = ATL_LINK_100M;
break;
case 1000U:
link.speed = ATL_LINK_1G;
break;
case 2500U:
link.speed = ATL_LINK_2_5G;
break;
case 5000U:
link.speed = ATL_LINK_5G;
break;
case 10000U:
link.speed = ATL_LINK_10G;
break;
}
return copy_to_user(ureq, &link, sizeof(link));
}
// aq_tsn->memreg_lock must be held
static void __aq_unlink_mem(struct aq_memreg *memreg)
{
idr_remove(&memreg->aq_tsn->memreg_idr, memreg->index);
list_del(&memreg->list);
}
static void aq_free_mem(struct aq_memreg *memreg)
{
aq_hide_memreg(memreg);
dma_free_coherent(aq_nic_get_dev(memreg->aq_tsn->aq_nic), memreg->real_size,
memreg->vaddr, memreg->paddr);
kobject_put(&memreg->kobj);
}
static int aq_allocate_buffer(struct aq_tsn_s *self, struct aq_alloc_mem __user *ureq)
{
int idx;
int ret = -ENOMEM;
struct aq_memreg *memreg;
struct aq_alloc_mem req;
if (copy_from_user(&req, ureq, sizeof(req)))
return -EFAULT;
memreg = kzalloc(sizeof(*memreg), GFP_KERNEL);
if (!memreg) {
pr_err("AQ_TSN : Can't alloc memreg\n");
goto out;
}
INIT_LIST_HEAD(&memreg->list);
memreg->aq_tsn = self;
kobject_init(&memreg->kobj, &memreg_type);
memreg->size = req.size;
req.size = ALIGN(req.size, PAGE_SIZE);
memreg->vaddr = dma_alloc_coherent(aq_nic_get_dev(self->aq_nic), req.size, &memreg->paddr, GFP_KERNEL);
if (!memreg->vaddr) {
pr_err("AQ_TSN : Can't alloc DMA memory (size %u)", req.size);
goto err_dma_alloc;
}
memreg->real_size = req.size;
#if LINUX_VERSION_CODE <= KERNEL_VERSION(3,15,0)
do {
ret = -ENOMEM;
if (!idr_pre_get(&self->memreg_idr, GFP_KERNEL))
goto err_idr;
mutex_lock(&self->memreg_lock);
if (test_bit(AQDSTATE_REMOVING, &self->state))
ret = -ENODEV;
else
ret = idr_get_new(&self->memreg_idr, memreg, &idx);
mutex_unlock(&self->memreg_lock);
} while (ret == -EAGAIN);
if (ret)
goto err_idr;
memreg->index = req.index = idx;
#else
idr_preload(GFP_KERNEL);
mutex_lock(&self->memreg_lock);
ret = idr_alloc(&self->memreg_idr, memreg,
0, 0, GFP_KERNEL);
mutex_unlock(&self->memreg_lock);
idr_preload_end();
if (ret < 0)
goto err_idr;
memreg->index = req.index = idx = ret;
#endif
ret = aq_publish_memreg(memreg);
if (ret) {
dev_err(aq_nic_get_dev(self->aq_nic), "Can't publish memreg to sysfs: %d", ret);
goto err_publish;
}
if(copy_to_user(ureq, &req, sizeof(req))) {
ret = -EFAULT;
goto err_copy_to_user;
}
mutex_lock(&self->memreg_lock);
list_add(&memreg->list, &self->mem_regions);
mutex_unlock(&self->memreg_lock);
return 0;
err_copy_to_user:
aq_hide_memreg(memreg);
err_publish:
mutex_lock(&self->memreg_lock);
idr_remove(&self->memreg_idr, idx);
mutex_unlock(&self->memreg_lock);
err_idr:
dma_free_coherent(aq_nic_get_dev(self->aq_nic), memreg->real_size,
memreg->vaddr, memreg->paddr);
err_dma_alloc:
kobject_put(&memreg->kobj);
out:
return ret;
}
int aq_tsn_init(struct aq_nic_s *aq_nic, struct ifreq *ifr)
{
int err = -ENOMEM;
struct aq_tsn_s *self = aq_nic->aq_tsn;
if (self) // TSN is already initialised
return 0;
self = kzalloc(sizeof(*self), GFP_KERNEL);
if (!self) {
pr_err("AQ_TSN : Can't alloc aq_tsn\n");
goto out;
}
idr_init(&self->memreg_idr);
mutex_init(&self->memreg_lock);
INIT_LIST_HEAD(&self->mem_regions);
self->aq_nic = aq_nic;
err = aq_create_attrs(self);
if (err) {
kfree(self);
goto out;
}
aq_nic->aq_tsn = self;
out:
return err;
}
int aq_tsn_release(struct aq_nic_s *aq_nic, struct ifreq *ifr)
{
int err = 0;
struct aq_tsn_s *self = aq_nic->aq_tsn;
struct aq_memreg *memreg, *tmp;
struct list_head memregs;
if (!self) // TSN is uninitialised
return 0;
aq_nic->aq_tsn = NULL; // TODO Issue if there is a thread which sleeps before prev cond and next step
aq_del_attrs(self);
mutex_lock(&self->memreg_lock);
#if LINUX_VERSION_CODE <= KERNEL_VERSION(3,15,0)
idr_remove_all(&self->memreg_idr);
#endif
idr_destroy(&self->memreg_idr);
list_replace_init(&self->mem_regions, &memregs);
mutex_unlock(&self->memreg_lock);
list_for_each_entry_safe(memreg, tmp, &memregs, list)
aq_free_mem(memreg);
mutex_destroy(&self->memreg_lock);
kfree(self);
return err;
}
int aq_tsn_alloc_dma_buf(struct aq_nic_s *aq_nic, struct ifreq *ifr)
{
int err = 0;
struct aq_tsn_s *self = aq_nic->aq_tsn;
if (!self) // TSN is uninitialised
return 0;
err = aq_allocate_buffer(self, (struct aq_alloc_mem __user *) ifr->ifr_data);
if (err == -ERESTARTSYS)
err = -EINTR; // Never restart if interrupted by a signal
return err;
}
int aq_tsn_free_dma_buf(struct aq_nic_s *aq_nic, struct ifreq *ifr)
{
int err = 0;
struct aq_tsn_s *self = aq_nic->aq_tsn;
struct aq_memreg *memreg;
if (!self) // TSN is uninitialised
return 0;
mutex_lock(&self->memreg_lock);
memreg = idr_find(&self->memreg_idr, ifr->ifr_metric);
if (memreg)
__aq_unlink_mem(memreg);
mutex_unlock(&self->memreg_lock);
if (!memreg)
return -ENOENT;
aq_free_mem(memreg);
return err;
}
int aq_tsn_get_link(struct aq_nic_s *aq_nic, struct ifreq *ifr)
{
struct aq_tsn_s *self = aq_nic->aq_tsn;
if (!self) // TSN is uninitialised
return 0;
return aq_get_link_status(self, (struct atl_link_cmd __user *) ifr->ifr_data);
}
#endif
//EOF