-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathnettlp_msg.c
More file actions
203 lines (163 loc) · 4.8 KB
/
Copy pathnettlp_msg.c
File metadata and controls
203 lines (163 loc) · 4.8 KB
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
#include <linux/pci.h>
#include <net/udp_tunnel.h>
#include "nettlp_msg.h"
/*
* Messaging between NetTLP driver and libtlp.
*
* Messageing module for communication between libtlp and this
* driver. The messages are identified by signed 32bit integer. libtlp
* sends a 32bit int to the driver, and then the driver returns a
* corresponding struct.
*
* This code is independent from nettlp_main.c. It can be and will be
* shared with other NetTLP drivers, such as simple-nic software
* implementation.
*
* Implemented for NetTLP adapter v0.17.
*/
MODULE_LICENSE("GPL");
/* structure describing nettlp udp socket */
struct nettlp_sock {
struct socket *sock;
/* values to be used fro generating messages */
uint64_t bar4_start; /* physical addr of BAR4 for msg 1 */
struct nettlp_msg_id id; /** device id for msg 2 */
struct nettlp_msix msix[NETTLP_MAX_VEC]; /* MSIX table on BAR2 */
};
struct nettlp_sock *nsock = NULL; /* XXX: terrified */
/* callback from net/ipv4/udp.c */
static int nettlp_msg_rcv(struct sock *sk, struct sk_buff *skb)
{
int ret, req;
struct nettlp_sock *ns;
struct sockaddr_in sin;
struct iphdr *ip;
struct udphdr *udp;
struct kvec iov[1];
struct msghdr msg;
ns = rcu_dereference_sk_user_data(sk);
if (!ns) {
pr_err("%s: this socket doesn't have nettlp_sock struct\n",
__func__);
goto drop;
}
if (!pskb_may_pull(skb, sizeof(struct udphdr) + sizeof(int))) {
pr_err("%s: too short UDP packet\n", __func__);
goto drop;
}
/* prepare sockaddr for reply messages */
ip = ip_hdr(skb);
udp = udp_hdr(skb);
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = ip->saddr;
sin.sin_port = udp->source;
memset(&msg, 0, sizeof(msg));
msg.msg_name = &sin;
msg.msg_namelen = sizeof(sin);
req = *((int *)(udp_hdr(skb) + 1));
switch (req) {
case NETTLP_MSG_GET_BAR4_ADDR:
iov[0].iov_base = &ns->bar4_start;
iov[0].iov_len = sizeof(ns->bar4_start);
break;
case NETTLP_MSG_GET_DEV_ID:
iov[0].iov_base = &ns->id;
iov[0].iov_len = sizeof(ns->id);
break;
case NETTLP_MSG_GET_MSIX_TABLE:
iov[0].iov_base = &ns->msix;
iov[0].iov_len = sizeof(struct nettlp_msix) * NETTLP_MAX_VEC;
break;
}
ret = kernel_sendmsg(ns->sock, &msg, iov, 1, iov[0].iov_len);
if (ret < 0) {
pr_err("%s: failed to send reply\n", __func__);
return ret;
}
drop:
kfree_skb(skb);
return 0;
}
int nettlp_msg_fill_msix_table(void *bar2_virt, struct nettlp_msix *msix)
{
int n;
uint64_t upper, lower;
uint32_t data;
/*
* MSIX table address and data are located in BAR2. Read the
* BAR2, and fill the nettlp_dev->msix array with it.
*/
struct msix_table_entry {
uint32_t lower_addr;
uint32_t upper_addr;
uint32_t data;
uint32_t rsv;
} *e;
if (!bar2_virt) {
pr_err("%s: BAR2 is not ioremapped\n", __func__);
return -1;
}
for (n = 0; n < NETTLP_MAX_VEC; n++) {
e = bar2_virt + sizeof(struct msix_table_entry) * n;
upper = readl(&e->upper_addr);
lower = readl(&e->lower_addr);
data = readl(&e->data);
msix[n].addr = (upper << 32 | lower);
msix[n].data = data;
}
return 0;
}
/* initialize the messaging module */
int nettlp_msg_init(uint64_t bar4_start, uint16_t dev_id, void *bar2_virt)
{
int err;
struct nettlp_sock *ns;
struct socket *sock;
struct udp_port_cfg udp_conf;
struct udp_tunnel_sock_cfg tunnel_cfg;
pr_info("initialize nettlp message socket\n");
if (nsock) {
pr_err("%s: NetTLP message socket is already initialized\n",
__func__);
return -EEXIST;
}
/* initialize the structure and a socket */
ns = kzalloc(sizeof(*ns), GFP_KERNEL);
if (!ns)
return -ENOMEM;
/* gather the information to be sent */
ns->bar4_start = bar4_start;
ns->id.id = dev_id;
nettlp_msg_fill_msix_table(bar2_virt, ns->msix);
/* open UDP socket for receiving requests */
memset(&udp_conf, 0, sizeof(udp_conf));
udp_conf.family = AF_INET;
udp_conf.local_udp_port = htons(NETTLP_MSG_PORT);
err = udp_sock_create(&init_net, &udp_conf, &sock);
if (err < 0)
return err;
ns->sock = sock;
/* setup callback on udp tunnel */
memset(&tunnel_cfg, 0, sizeof(tunnel_cfg));
tunnel_cfg.sk_user_data = ns;
tunnel_cfg.encap_type = 1;
tunnel_cfg.encap_rcv = nettlp_msg_rcv;
tunnel_cfg.encap_destroy = NULL;
tunnel_cfg.gro_receive = NULL;
tunnel_cfg.gro_complete = NULL;
setup_udp_tunnel_sock(&init_net, sock, &tunnel_cfg);
nsock = ns; /* XXX: terrified. used for only release */
return 0;
}
void nettlp_msg_fini(void)
{
if (!nsock) {
pr_err("%s: NetTLP message socket is already released\n",
__func__);
return;
}
udp_tunnel_sock_release(nsock->sock);
kfree(nsock);
nsock = NULL;
}