diff options
Diffstat (limited to 'net/phonet')
-rw-r--r-- | net/phonet/Kconfig | 11 | ||||
-rw-r--r-- | net/phonet/af_phonet.c | 17 | ||||
-rw-r--r-- | net/phonet/datagram.c | 13 | ||||
-rw-r--r-- | net/phonet/pep.c | 477 | ||||
-rw-r--r-- | net/phonet/pn_dev.c | 5 | ||||
-rw-r--r-- | net/phonet/socket.c | 190 |
6 files changed, 707 insertions, 6 deletions
diff --git a/net/phonet/Kconfig b/net/phonet/Kconfig index 6ec7d55b1769..901956ada9c8 100644 --- a/net/phonet/Kconfig +++ b/net/phonet/Kconfig | |||
@@ -14,3 +14,14 @@ config PHONET | |||
14 | 14 | ||
15 | To compile this driver as a module, choose M here: the module | 15 | To compile this driver as a module, choose M here: the module |
16 | will be called phonet. If unsure, say N. | 16 | will be called phonet. If unsure, say N. |
17 | |||
18 | config PHONET_PIPECTRLR | ||
19 | bool "Phonet Pipe Controller" | ||
20 | depends on PHONET | ||
21 | default N | ||
22 | help | ||
23 | The Pipe Controller implementation in Phonet stack to support Pipe | ||
24 | data with Nokia Slim modems like WG2.5 used on ST-Ericsson U8500 | ||
25 | platform. | ||
26 | |||
27 | If unsure, say N. | ||
diff --git a/net/phonet/af_phonet.c b/net/phonet/af_phonet.c index 73aee7f2fcdc..fd95beb72f5d 100644 --- a/net/phonet/af_phonet.c +++ b/net/phonet/af_phonet.c | |||
@@ -251,6 +251,16 @@ int pn_skb_send(struct sock *sk, struct sk_buff *skb, | |||
251 | else if (phonet_address_lookup(net, daddr) == 0) { | 251 | else if (phonet_address_lookup(net, daddr) == 0) { |
252 | dev = phonet_device_get(net); | 252 | dev = phonet_device_get(net); |
253 | skb->pkt_type = PACKET_LOOPBACK; | 253 | skb->pkt_type = PACKET_LOOPBACK; |
254 | } else if (pn_sockaddr_get_object(target) == 0) { | ||
255 | /* Resource routing (small race until phonet_rcv()) */ | ||
256 | struct sock *sk = pn_find_sock_by_res(net, | ||
257 | target->spn_resource); | ||
258 | if (sk) { | ||
259 | sock_put(sk); | ||
260 | dev = phonet_device_get(net); | ||
261 | skb->pkt_type = PACKET_LOOPBACK; | ||
262 | } else | ||
263 | dev = phonet_route_output(net, daddr); | ||
254 | } else | 264 | } else |
255 | dev = phonet_route_output(net, daddr); | 265 | dev = phonet_route_output(net, daddr); |
256 | 266 | ||
@@ -383,6 +393,13 @@ static int phonet_rcv(struct sk_buff *skb, struct net_device *dev, | |||
383 | goto out; | 393 | goto out; |
384 | } | 394 | } |
385 | 395 | ||
396 | /* resource routing */ | ||
397 | if (pn_sockaddr_get_object(&sa) == 0) { | ||
398 | struct sock *sk = pn_find_sock_by_res(net, sa.spn_resource); | ||
399 | if (sk) | ||
400 | return sk_receive_skb(sk, skb, 0); | ||
401 | } | ||
402 | |||
386 | /* check if we are the destination */ | 403 | /* check if we are the destination */ |
387 | if (phonet_address_lookup(net, pn_sockaddr_get_addr(&sa)) == 0) { | 404 | if (phonet_address_lookup(net, pn_sockaddr_get_addr(&sa)) == 0) { |
388 | /* Phonet packet input */ | 405 | /* Phonet packet input */ |
diff --git a/net/phonet/datagram.c b/net/phonet/datagram.c index 1bd38db4fe1e..2f032381bd45 100644 --- a/net/phonet/datagram.c +++ b/net/phonet/datagram.c | |||
@@ -52,6 +52,19 @@ static int pn_ioctl(struct sock *sk, int cmd, unsigned long arg) | |||
52 | answ = skb ? skb->len : 0; | 52 | answ = skb ? skb->len : 0; |
53 | release_sock(sk); | 53 | release_sock(sk); |
54 | return put_user(answ, (int __user *)arg); | 54 | return put_user(answ, (int __user *)arg); |
55 | |||
56 | case SIOCPNADDRESOURCE: | ||
57 | case SIOCPNDELRESOURCE: { | ||
58 | u32 res; | ||
59 | if (get_user(res, (u32 __user *)arg)) | ||
60 | return -EFAULT; | ||
61 | if (res >= 256) | ||
62 | return -EINVAL; | ||
63 | if (cmd == SIOCPNADDRESOURCE) | ||
64 | return pn_sock_bind_res(sk, res); | ||
65 | else | ||
66 | return pn_sock_unbind_res(sk, res); | ||
67 | } | ||
55 | } | 68 | } |
56 | 69 | ||
57 | return -ENOIOCTLCMD; | 70 | return -ENOIOCTLCMD; |
diff --git a/net/phonet/pep.c b/net/phonet/pep.c index 15003021f4f0..aa3d8700d213 100644 --- a/net/phonet/pep.c +++ b/net/phonet/pep.c | |||
@@ -88,6 +88,15 @@ static int pep_reply(struct sock *sk, struct sk_buff *oskb, | |||
88 | const struct pnpipehdr *oph = pnp_hdr(oskb); | 88 | const struct pnpipehdr *oph = pnp_hdr(oskb); |
89 | struct pnpipehdr *ph; | 89 | struct pnpipehdr *ph; |
90 | struct sk_buff *skb; | 90 | struct sk_buff *skb; |
91 | #ifdef CONFIG_PHONET_PIPECTRLR | ||
92 | const struct phonethdr *hdr = pn_hdr(oskb); | ||
93 | struct sockaddr_pn spn = { | ||
94 | .spn_family = AF_PHONET, | ||
95 | .spn_resource = 0xD9, | ||
96 | .spn_dev = hdr->pn_sdev, | ||
97 | .spn_obj = hdr->pn_sobj, | ||
98 | }; | ||
99 | #endif | ||
91 | 100 | ||
92 | skb = alloc_skb(MAX_PNPIPE_HEADER + len, priority); | 101 | skb = alloc_skb(MAX_PNPIPE_HEADER + len, priority); |
93 | if (!skb) | 102 | if (!skb) |
@@ -105,10 +114,271 @@ static int pep_reply(struct sock *sk, struct sk_buff *oskb, | |||
105 | ph->pipe_handle = oph->pipe_handle; | 114 | ph->pipe_handle = oph->pipe_handle; |
106 | ph->error_code = code; | 115 | ph->error_code = code; |
107 | 116 | ||
117 | #ifdef CONFIG_PHONET_PIPECTRLR | ||
118 | return pn_skb_send(sk, skb, &spn); | ||
119 | #else | ||
108 | return pn_skb_send(sk, skb, &pipe_srv); | 120 | return pn_skb_send(sk, skb, &pipe_srv); |
121 | #endif | ||
109 | } | 122 | } |
110 | 123 | ||
111 | #define PAD 0x00 | 124 | #define PAD 0x00 |
125 | |||
126 | #ifdef CONFIG_PHONET_PIPECTRLR | ||
127 | static u8 pipe_negotiate_fc(u8 *host_fc, u8 *remote_fc, int len) | ||
128 | { | ||
129 | int i, j; | ||
130 | u8 base_fc, final_fc; | ||
131 | |||
132 | for (i = 0; i < len; i++) { | ||
133 | base_fc = host_fc[i]; | ||
134 | for (j = 0; j < len; j++) { | ||
135 | if (remote_fc[j] == base_fc) { | ||
136 | final_fc = base_fc; | ||
137 | goto done; | ||
138 | } | ||
139 | } | ||
140 | } | ||
141 | return -EINVAL; | ||
142 | |||
143 | done: | ||
144 | return final_fc; | ||
145 | |||
146 | } | ||
147 | |||
148 | static int pipe_get_flow_info(struct sock *sk, struct sk_buff *skb, | ||
149 | u8 *pref_rx_fc, u8 *req_tx_fc) | ||
150 | { | ||
151 | struct pnpipehdr *hdr; | ||
152 | u8 n_sb; | ||
153 | |||
154 | if (!pskb_may_pull(skb, sizeof(*hdr) + 4)) | ||
155 | return -EINVAL; | ||
156 | |||
157 | hdr = pnp_hdr(skb); | ||
158 | n_sb = hdr->data[4]; | ||
159 | |||
160 | __skb_pull(skb, sizeof(*hdr) + 4); | ||
161 | while (n_sb > 0) { | ||
162 | u8 type, buf[3], len = sizeof(buf); | ||
163 | u8 *data = pep_get_sb(skb, &type, &len, buf); | ||
164 | |||
165 | if (data == NULL) | ||
166 | return -EINVAL; | ||
167 | |||
168 | switch (type) { | ||
169 | case PN_PIPE_SB_REQUIRED_FC_TX: | ||
170 | if (len < 3 || (data[2] | data[3] | data[4]) > 3) | ||
171 | break; | ||
172 | req_tx_fc[0] = data[2]; | ||
173 | req_tx_fc[1] = data[3]; | ||
174 | req_tx_fc[2] = data[4]; | ||
175 | break; | ||
176 | |||
177 | case PN_PIPE_SB_PREFERRED_FC_RX: | ||
178 | if (len < 3 || (data[2] | data[3] | data[4]) > 3) | ||
179 | break; | ||
180 | pref_rx_fc[0] = data[2]; | ||
181 | pref_rx_fc[1] = data[3]; | ||
182 | pref_rx_fc[2] = data[4]; | ||
183 | break; | ||
184 | |||
185 | } | ||
186 | n_sb--; | ||
187 | } | ||
188 | return 0; | ||
189 | } | ||
190 | |||
191 | static int pipe_handler_send_req(struct sock *sk, u16 dobj, u8 utid, | ||
192 | u8 msg_id, u8 p_handle, gfp_t priority) | ||
193 | { | ||
194 | int len; | ||
195 | struct pnpipehdr *ph; | ||
196 | struct sk_buff *skb; | ||
197 | struct sockaddr_pn spn = { | ||
198 | .spn_family = AF_PHONET, | ||
199 | .spn_resource = 0xD9, | ||
200 | .spn_dev = pn_dev(dobj), | ||
201 | .spn_obj = pn_obj(dobj), | ||
202 | }; | ||
203 | |||
204 | static const u8 data[4] = { | ||
205 | PAD, PAD, PAD, PAD, | ||
206 | }; | ||
207 | |||
208 | switch (msg_id) { | ||
209 | case PNS_PEP_CONNECT_REQ: | ||
210 | len = sizeof(data); | ||
211 | break; | ||
212 | |||
213 | case PNS_PEP_DISCONNECT_REQ: | ||
214 | case PNS_PEP_ENABLE_REQ: | ||
215 | case PNS_PEP_DISABLE_REQ: | ||
216 | len = 0; | ||
217 | break; | ||
218 | |||
219 | default: | ||
220 | return -EINVAL; | ||
221 | } | ||
222 | |||
223 | skb = alloc_skb(MAX_PNPIPE_HEADER + len, priority); | ||
224 | if (!skb) | ||
225 | return -ENOMEM; | ||
226 | skb_set_owner_w(skb, sk); | ||
227 | |||
228 | skb_reserve(skb, MAX_PNPIPE_HEADER); | ||
229 | if (len) { | ||
230 | __skb_put(skb, len); | ||
231 | skb_copy_to_linear_data(skb, data, len); | ||
232 | } | ||
233 | __skb_push(skb, sizeof(*ph)); | ||
234 | skb_reset_transport_header(skb); | ||
235 | ph = pnp_hdr(skb); | ||
236 | ph->utid = utid; | ||
237 | ph->message_id = msg_id; | ||
238 | ph->pipe_handle = p_handle; | ||
239 | ph->error_code = PN_PIPE_NO_ERROR; | ||
240 | |||
241 | return pn_skb_send(sk, skb, &spn); | ||
242 | } | ||
243 | |||
244 | static int pipe_handler_send_created_ind(struct sock *sk, u16 dobj, | ||
245 | u8 utid, u8 p_handle, u8 msg_id, u8 tx_fc, u8 rx_fc) | ||
246 | { | ||
247 | int err_code; | ||
248 | struct pnpipehdr *ph; | ||
249 | struct sk_buff *skb; | ||
250 | struct sockaddr_pn spn = { | ||
251 | .spn_family = AF_PHONET, | ||
252 | .spn_resource = 0xD9, | ||
253 | .spn_dev = pn_dev(dobj), | ||
254 | .spn_obj = pn_obj(dobj), | ||
255 | }; | ||
256 | |||
257 | static u8 data[4] = { | ||
258 | 0x03, 0x04, | ||
259 | }; | ||
260 | data[2] = tx_fc; | ||
261 | data[3] = rx_fc; | ||
262 | |||
263 | /* | ||
264 | * actually, below is number of sub-blocks and not error code. | ||
265 | * Pipe_created_ind message format does not have any | ||
266 | * error code field. However, the Phonet stack will always send | ||
267 | * an error code as part of pnpipehdr. So, use that err_code to | ||
268 | * specify the number of sub-blocks. | ||
269 | */ | ||
270 | err_code = 0x01; | ||
271 | |||
272 | skb = alloc_skb(MAX_PNPIPE_HEADER + sizeof(data), GFP_ATOMIC); | ||
273 | if (!skb) | ||
274 | return -ENOMEM; | ||
275 | skb_set_owner_w(skb, sk); | ||
276 | |||
277 | skb_reserve(skb, MAX_PNPIPE_HEADER); | ||
278 | __skb_put(skb, sizeof(data)); | ||
279 | skb_copy_to_linear_data(skb, data, sizeof(data)); | ||
280 | __skb_push(skb, sizeof(*ph)); | ||
281 | skb_reset_transport_header(skb); | ||
282 | ph = pnp_hdr(skb); | ||
283 | ph->utid = utid; | ||
284 | ph->message_id = msg_id; | ||
285 | ph->pipe_handle = p_handle; | ||
286 | ph->error_code = err_code; | ||
287 | |||
288 | return pn_skb_send(sk, skb, &spn); | ||
289 | } | ||
290 | |||
291 | static int pipe_handler_send_ind(struct sock *sk, u16 dobj, u8 utid, | ||
292 | u8 p_handle, u8 msg_id) | ||
293 | { | ||
294 | int err_code; | ||
295 | struct pnpipehdr *ph; | ||
296 | struct sk_buff *skb; | ||
297 | struct sockaddr_pn spn = { | ||
298 | .spn_family = AF_PHONET, | ||
299 | .spn_resource = 0xD9, | ||
300 | .spn_dev = pn_dev(dobj), | ||
301 | .spn_obj = pn_obj(dobj), | ||
302 | }; | ||
303 | |||
304 | /* | ||
305 | * actually, below is a filler. | ||
306 | * Pipe_enabled/disabled_ind message format does not have any | ||
307 | * error code field. However, the Phonet stack will always send | ||
308 | * an error code as part of pnpipehdr. So, use that err_code to | ||
309 | * specify the filler value. | ||
310 | */ | ||
311 | err_code = 0x0; | ||
312 | |||
313 | skb = alloc_skb(MAX_PNPIPE_HEADER, GFP_ATOMIC); | ||
314 | if (!skb) | ||
315 | return -ENOMEM; | ||
316 | skb_set_owner_w(skb, sk); | ||
317 | |||
318 | skb_reserve(skb, MAX_PNPIPE_HEADER); | ||
319 | __skb_push(skb, sizeof(*ph)); | ||
320 | skb_reset_transport_header(skb); | ||
321 | ph = pnp_hdr(skb); | ||
322 | ph->utid = utid; | ||
323 | ph->message_id = msg_id; | ||
324 | ph->pipe_handle = p_handle; | ||
325 | ph->error_code = err_code; | ||
326 | |||
327 | return pn_skb_send(sk, skb, &spn); | ||
328 | } | ||
329 | |||
330 | static int pipe_handler_enable_pipe(struct sock *sk, int cmd) | ||
331 | { | ||
332 | int ret; | ||
333 | struct pep_sock *pn = pep_sk(sk); | ||
334 | |||
335 | switch (cmd) { | ||
336 | case PNPIPE_ENABLE: | ||
337 | ret = pipe_handler_send_req(sk, pn->pn_sk.sobject, | ||
338 | PNS_PIPE_ENABLE_UTID, PNS_PEP_ENABLE_REQ, | ||
339 | pn->pipe_handle, GFP_ATOMIC); | ||
340 | break; | ||
341 | |||
342 | case PNPIPE_DISABLE: | ||
343 | ret = pipe_handler_send_req(sk, pn->pn_sk.sobject, | ||
344 | PNS_PIPE_DISABLE_UTID, PNS_PEP_DISABLE_REQ, | ||
345 | pn->pipe_handle, GFP_ATOMIC); | ||
346 | break; | ||
347 | |||
348 | default: | ||
349 | ret = -EINVAL; | ||
350 | } | ||
351 | |||
352 | return ret; | ||
353 | } | ||
354 | |||
355 | static int pipe_handler_create_pipe(struct sock *sk, int pipe_handle, int cmd) | ||
356 | { | ||
357 | int ret; | ||
358 | struct pep_sock *pn = pep_sk(sk); | ||
359 | |||
360 | switch (cmd) { | ||
361 | case PNPIPE_CREATE: | ||
362 | ret = pipe_handler_send_req(sk, pn->pn_sk.sobject, | ||
363 | PNS_PEP_CONNECT_UTID, PNS_PEP_CONNECT_REQ, | ||
364 | pipe_handle, GFP_ATOMIC); | ||
365 | break; | ||
366 | |||
367 | case PNPIPE_DESTROY: | ||
368 | ret = pipe_handler_send_req(sk, pn->remote_pep, | ||
369 | PNS_PEP_DISCONNECT_UTID, | ||
370 | PNS_PEP_DISCONNECT_REQ, | ||
371 | pn->pipe_handle, GFP_ATOMIC); | ||
372 | break; | ||
373 | |||
374 | default: | ||
375 | ret = -EINVAL; | ||
376 | } | ||
377 | |||
378 | return ret; | ||
379 | } | ||
380 | #endif | ||
381 | |||
112 | static int pep_accept_conn(struct sock *sk, struct sk_buff *skb) | 382 | static int pep_accept_conn(struct sock *sk, struct sk_buff *skb) |
113 | { | 383 | { |
114 | static const u8 data[20] = { | 384 | static const u8 data[20] = { |
@@ -173,6 +443,14 @@ static int pipe_snd_status(struct sock *sk, u8 type, u8 status, gfp_t priority) | |||
173 | struct pep_sock *pn = pep_sk(sk); | 443 | struct pep_sock *pn = pep_sk(sk); |
174 | struct pnpipehdr *ph; | 444 | struct pnpipehdr *ph; |
175 | struct sk_buff *skb; | 445 | struct sk_buff *skb; |
446 | #ifdef CONFIG_PHONET_PIPECTRLR | ||
447 | struct sockaddr_pn spn = { | ||
448 | .spn_family = AF_PHONET, | ||
449 | .spn_resource = 0xD9, | ||
450 | .spn_dev = pn_dev(pn->remote_pep), | ||
451 | .spn_obj = pn_obj(pn->remote_pep), | ||
452 | }; | ||
453 | #endif | ||
176 | 454 | ||
177 | skb = alloc_skb(MAX_PNPIPE_HEADER + 4, priority); | 455 | skb = alloc_skb(MAX_PNPIPE_HEADER + 4, priority); |
178 | if (!skb) | 456 | if (!skb) |
@@ -192,7 +470,11 @@ static int pipe_snd_status(struct sock *sk, u8 type, u8 status, gfp_t priority) | |||
192 | ph->data[3] = PAD; | 470 | ph->data[3] = PAD; |
193 | ph->data[4] = status; | 471 | ph->data[4] = status; |
194 | 472 | ||
473 | #ifdef CONFIG_PHONET_PIPECTRLR | ||
474 | return pn_skb_send(sk, skb, &spn); | ||
475 | #else | ||
195 | return pn_skb_send(sk, skb, &pipe_srv); | 476 | return pn_skb_send(sk, skb, &pipe_srv); |
477 | #endif | ||
196 | } | 478 | } |
197 | 479 | ||
198 | /* Send our RX flow control information to the sender. | 480 | /* Send our RX flow control information to the sender. |
@@ -309,6 +591,12 @@ static int pipe_do_rcv(struct sock *sk, struct sk_buff *skb) | |||
309 | struct pnpipehdr *hdr = pnp_hdr(skb); | 591 | struct pnpipehdr *hdr = pnp_hdr(skb); |
310 | struct sk_buff_head *queue; | 592 | struct sk_buff_head *queue; |
311 | int err = 0; | 593 | int err = 0; |
594 | #ifdef CONFIG_PHONET_PIPECTRLR | ||
595 | struct phonethdr *ph = pn_hdr(skb); | ||
596 | static u8 host_pref_rx_fc[3], host_req_tx_fc[3]; | ||
597 | u8 remote_pref_rx_fc[3], remote_req_tx_fc[3]; | ||
598 | u8 negotiated_rx_fc, negotiated_tx_fc; | ||
599 | #endif | ||
312 | 600 | ||
313 | BUG_ON(sk->sk_state == TCP_CLOSE_WAIT); | 601 | BUG_ON(sk->sk_state == TCP_CLOSE_WAIT); |
314 | 602 | ||
@@ -317,6 +605,40 @@ static int pipe_do_rcv(struct sock *sk, struct sk_buff *skb) | |||
317 | pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE); | 605 | pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE); |
318 | break; | 606 | break; |
319 | 607 | ||
608 | #ifdef CONFIG_PHONET_PIPECTRLR | ||
609 | case PNS_PEP_CONNECT_RESP: | ||
610 | if ((ph->pn_sdev == pn_dev(pn->remote_pep)) && | ||
611 | (ph->pn_sobj == pn_obj(pn->remote_pep))) { | ||
612 | pipe_get_flow_info(sk, skb, remote_pref_rx_fc, | ||
613 | remote_req_tx_fc); | ||
614 | |||
615 | negotiated_tx_fc = pipe_negotiate_fc(remote_req_tx_fc, | ||
616 | host_pref_rx_fc, | ||
617 | sizeof(host_pref_rx_fc)); | ||
618 | negotiated_rx_fc = pipe_negotiate_fc(host_req_tx_fc, | ||
619 | remote_pref_rx_fc, | ||
620 | sizeof(host_pref_rx_fc)); | ||
621 | |||
622 | pn->pipe_state = PIPE_DISABLED; | ||
623 | pipe_handler_send_created_ind(sk, pn->remote_pep, | ||
624 | PNS_PIPE_CREATED_IND_UTID, | ||
625 | pn->pipe_handle, PNS_PIPE_CREATED_IND, | ||
626 | negotiated_tx_fc, negotiated_rx_fc); | ||
627 | pipe_handler_send_created_ind(sk, pn->pn_sk.sobject, | ||
628 | PNS_PIPE_CREATED_IND_UTID, | ||
629 | pn->pipe_handle, PNS_PIPE_CREATED_IND, | ||
630 | negotiated_tx_fc, negotiated_rx_fc); | ||
631 | } else { | ||
632 | pipe_handler_send_req(sk, pn->remote_pep, | ||
633 | PNS_PEP_CONNECT_UTID, | ||
634 | PNS_PEP_CONNECT_REQ, pn->pipe_handle, | ||
635 | GFP_ATOMIC); | ||
636 | pipe_get_flow_info(sk, skb, host_pref_rx_fc, | ||
637 | host_req_tx_fc); | ||
638 | } | ||
639 | break; | ||
640 | #endif | ||
641 | |||
320 | case PNS_PEP_DISCONNECT_REQ: | 642 | case PNS_PEP_DISCONNECT_REQ: |
321 | pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC); | 643 | pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC); |
322 | sk->sk_state = TCP_CLOSE_WAIT; | 644 | sk->sk_state = TCP_CLOSE_WAIT; |
@@ -324,11 +646,41 @@ static int pipe_do_rcv(struct sock *sk, struct sk_buff *skb) | |||
324 | sk->sk_state_change(sk); | 646 | sk->sk_state_change(sk); |
325 | break; | 647 | break; |
326 | 648 | ||
649 | #ifdef CONFIG_PHONET_PIPECTRLR | ||
650 | case PNS_PEP_DISCONNECT_RESP: | ||
651 | pn->pipe_state = PIPE_IDLE; | ||
652 | pipe_handler_send_req(sk, pn->pn_sk.sobject, | ||
653 | PNS_PEP_DISCONNECT_UTID, | ||
654 | PNS_PEP_DISCONNECT_REQ, pn->pipe_handle, | ||
655 | GFP_KERNEL); | ||
656 | break; | ||
657 | #endif | ||
658 | |||
327 | case PNS_PEP_ENABLE_REQ: | 659 | case PNS_PEP_ENABLE_REQ: |
328 | /* Wait for PNS_PIPE_(ENABLED|REDIRECTED)_IND */ | 660 | /* Wait for PNS_PIPE_(ENABLED|REDIRECTED)_IND */ |
329 | pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC); | 661 | pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC); |
330 | break; | 662 | break; |
331 | 663 | ||
664 | #ifdef CONFIG_PHONET_PIPECTRLR | ||
665 | case PNS_PEP_ENABLE_RESP: | ||
666 | if ((ph->pn_sdev == pn_dev(pn->remote_pep)) && | ||
667 | (ph->pn_sobj == pn_obj(pn->remote_pep))) { | ||
668 | pn->pipe_state = PIPE_ENABLED; | ||
669 | pipe_handler_send_ind(sk, pn->remote_pep, | ||
670 | PNS_PIPE_ENABLED_IND_UTID, | ||
671 | pn->pipe_handle, PNS_PIPE_ENABLED_IND); | ||
672 | pipe_handler_send_ind(sk, pn->pn_sk.sobject, | ||
673 | PNS_PIPE_ENABLED_IND_UTID, | ||
674 | pn->pipe_handle, PNS_PIPE_ENABLED_IND); | ||
675 | } else | ||
676 | pipe_handler_send_req(sk, pn->remote_pep, | ||
677 | PNS_PIPE_ENABLE_UTID, | ||
678 | PNS_PEP_ENABLE_REQ, pn->pipe_handle, | ||
679 | GFP_KERNEL); | ||
680 | |||
681 | break; | ||
682 | #endif | ||
683 | |||
332 | case PNS_PEP_RESET_REQ: | 684 | case PNS_PEP_RESET_REQ: |
333 | switch (hdr->state_after_reset) { | 685 | switch (hdr->state_after_reset) { |
334 | case PN_PIPE_DISABLE: | 686 | case PN_PIPE_DISABLE: |
@@ -347,6 +699,27 @@ static int pipe_do_rcv(struct sock *sk, struct sk_buff *skb) | |||
347 | pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC); | 699 | pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC); |
348 | break; | 700 | break; |
349 | 701 | ||
702 | #ifdef CONFIG_PHONET_PIPECTRLR | ||
703 | case PNS_PEP_DISABLE_RESP: | ||
704 | if ((ph->pn_sdev == pn_dev(pn->remote_pep)) && | ||
705 | (ph->pn_sobj == pn_obj(pn->remote_pep))) { | ||
706 | pn->pipe_state = PIPE_DISABLED; | ||
707 | pipe_handler_send_ind(sk, pn->remote_pep, | ||
708 | PNS_PIPE_DISABLED_IND_UTID, | ||
709 | pn->pipe_handle, | ||
710 | PNS_PIPE_DISABLED_IND); | ||
711 | pipe_handler_send_ind(sk, pn->pn_sk.sobject, | ||
712 | PNS_PIPE_DISABLED_IND_UTID, | ||
713 | pn->pipe_handle, | ||
714 | PNS_PIPE_DISABLED_IND); | ||
715 | } else | ||
716 | pipe_handler_send_req(sk, pn->remote_pep, | ||
717 | PNS_PIPE_DISABLE_UTID, | ||
718 | PNS_PEP_DISABLE_REQ, pn->pipe_handle, | ||
719 | GFP_KERNEL); | ||
720 | break; | ||
721 | #endif | ||
722 | |||
350 | case PNS_PEP_CTRL_REQ: | 723 | case PNS_PEP_CTRL_REQ: |
351 | if (skb_queue_len(&pn->ctrlreq_queue) >= PNPIPE_CTRLREQ_MAX) { | 724 | if (skb_queue_len(&pn->ctrlreq_queue) >= PNPIPE_CTRLREQ_MAX) { |
352 | atomic_inc(&sk->sk_drops); | 725 | atomic_inc(&sk->sk_drops); |
@@ -520,6 +893,9 @@ static int pep_connreq_rcv(struct sock *sk, struct sk_buff *skb) | |||
520 | newpn->rx_fc = newpn->tx_fc = PN_LEGACY_FLOW_CONTROL; | 893 | newpn->rx_fc = newpn->tx_fc = PN_LEGACY_FLOW_CONTROL; |
521 | newpn->init_enable = enabled; | 894 | newpn->init_enable = enabled; |
522 | newpn->aligned = aligned; | 895 | newpn->aligned = aligned; |
896 | #ifdef CONFIG_PHONET_PIPECTRLR | ||
897 | newpn->remote_pep = pn->remote_pep; | ||
898 | #endif | ||
523 | 899 | ||
524 | BUG_ON(!skb_queue_empty(&newsk->sk_receive_queue)); | 900 | BUG_ON(!skb_queue_empty(&newsk->sk_receive_queue)); |
525 | skb_queue_head(&newsk->sk_receive_queue, skb); | 901 | skb_queue_head(&newsk->sk_receive_queue, skb); |
@@ -621,6 +997,28 @@ drop: | |||
621 | return err; | 997 | return err; |
622 | } | 998 | } |
623 | 999 | ||
1000 | static int pipe_do_remove(struct sock *sk) | ||
1001 | { | ||
1002 | struct pep_sock *pn = pep_sk(sk); | ||
1003 | struct pnpipehdr *ph; | ||
1004 | struct sk_buff *skb; | ||
1005 | |||
1006 | skb = alloc_skb(MAX_PNPIPE_HEADER, GFP_KERNEL); | ||
1007 | if (!skb) | ||
1008 | return -ENOMEM; | ||
1009 | |||
1010 | skb_reserve(skb, MAX_PNPIPE_HEADER); | ||
1011 | __skb_push(skb, sizeof(*ph)); | ||
1012 | skb_reset_transport_header(skb); | ||
1013 | ph = pnp_hdr(skb); | ||
1014 | ph->utid = 0; | ||
1015 | ph->message_id = PNS_PIPE_REMOVE_REQ; | ||
1016 | ph->pipe_handle = pn->pipe_handle; | ||
1017 | ph->data[0] = PAD; | ||
1018 | |||
1019 | return pn_skb_send(sk, skb, &pipe_srv); | ||
1020 | } | ||
1021 | |||
624 | /* associated socket ceases to exist */ | 1022 | /* associated socket ceases to exist */ |
625 | static void pep_sock_close(struct sock *sk, long timeout) | 1023 | static void pep_sock_close(struct sock *sk, long timeout) |
626 | { | 1024 | { |
@@ -639,7 +1037,10 @@ static void pep_sock_close(struct sock *sk, long timeout) | |||
639 | sk_for_each_safe(sknode, p, n, &pn->ackq) | 1037 | sk_for_each_safe(sknode, p, n, &pn->ackq) |
640 | sk_del_node_init(sknode); | 1038 | sk_del_node_init(sknode); |
641 | sk->sk_state = TCP_CLOSE; | 1039 | sk->sk_state = TCP_CLOSE; |
642 | } | 1040 | } else if ((1 << sk->sk_state) & (TCPF_SYN_RECV|TCPF_ESTABLISHED)) |
1041 | /* Forcefully remove dangling Phonet pipe */ | ||
1042 | pipe_do_remove(sk); | ||
1043 | |||
643 | ifindex = pn->ifindex; | 1044 | ifindex = pn->ifindex; |
644 | pn->ifindex = 0; | 1045 | pn->ifindex = 0; |
645 | release_sock(sk); | 1046 | release_sock(sk); |
@@ -757,6 +1158,10 @@ static int pep_setsockopt(struct sock *sk, int level, int optname, | |||
757 | { | 1158 | { |
758 | struct pep_sock *pn = pep_sk(sk); | 1159 | struct pep_sock *pn = pep_sk(sk); |
759 | int val = 0, err = 0; | 1160 | int val = 0, err = 0; |
1161 | #ifdef CONFIG_PHONET_PIPECTRLR | ||
1162 | int remote_pep; | ||
1163 | int pipe_handle; | ||
1164 | #endif | ||
760 | 1165 | ||
761 | if (level != SOL_PNPIPE) | 1166 | if (level != SOL_PNPIPE) |
762 | return -ENOPROTOOPT; | 1167 | return -ENOPROTOOPT; |
@@ -767,6 +1172,48 @@ static int pep_setsockopt(struct sock *sk, int level, int optname, | |||
767 | 1172 | ||
768 | lock_sock(sk); | 1173 | lock_sock(sk); |
769 | switch (optname) { | 1174 | switch (optname) { |
1175 | #ifdef CONFIG_PHONET_PIPECTRLR | ||
1176 | case PNPIPE_CREATE: | ||
1177 | if (val) { | ||
1178 | if (pn->pipe_state > PIPE_IDLE) { | ||
1179 | err = -EFAULT; | ||
1180 | break; | ||
1181 | } | ||
1182 | remote_pep = val & 0xFFFF; | ||
1183 | pipe_handle = (val >> 16) & 0xFF; | ||
1184 | pn->remote_pep = remote_pep; | ||
1185 | err = pipe_handler_create_pipe(sk, pipe_handle, | ||
1186 | PNPIPE_CREATE); | ||
1187 | break; | ||
1188 | } | ||
1189 | |||
1190 | case PNPIPE_ENABLE: | ||
1191 | if (pn->pipe_state != PIPE_DISABLED) { | ||
1192 | err = -EFAULT; | ||
1193 | break; | ||
1194 | } | ||
1195 | err = pipe_handler_enable_pipe(sk, PNPIPE_ENABLE); | ||
1196 | break; | ||
1197 | |||
1198 | case PNPIPE_DISABLE: | ||
1199 | if (pn->pipe_state != PIPE_ENABLED) { | ||
1200 | err = -EFAULT; | ||
1201 | break; | ||
1202 | } | ||
1203 | |||
1204 | err = pipe_handler_enable_pipe(sk, PNPIPE_DISABLE); | ||
1205 | break; | ||
1206 | |||
1207 | case PNPIPE_DESTROY: | ||
1208 | if (pn->pipe_state < PIPE_DISABLED) { | ||
1209 | err = -EFAULT; | ||
1210 | break; | ||
1211 | } | ||
1212 | |||
1213 | err = pipe_handler_create_pipe(sk, 0x0, PNPIPE_DESTROY); | ||
1214 | break; | ||
1215 | #endif | ||
1216 | |||
770 | case PNPIPE_ENCAP: | 1217 | case PNPIPE_ENCAP: |
771 | if (val && val != PNPIPE_ENCAP_IP) { | 1218 | if (val && val != PNPIPE_ENCAP_IP) { |
772 | err = -EINVAL; | 1219 | err = -EINVAL; |
@@ -816,6 +1263,13 @@ static int pep_getsockopt(struct sock *sk, int level, int optname, | |||
816 | case PNPIPE_ENCAP: | 1263 | case PNPIPE_ENCAP: |
817 | val = pn->ifindex ? PNPIPE_ENCAP_IP : PNPIPE_ENCAP_NONE; | 1264 | val = pn->ifindex ? PNPIPE_ENCAP_IP : PNPIPE_ENCAP_NONE; |
818 | break; | 1265 | break; |
1266 | |||
1267 | #ifdef CONFIG_PHONET_PIPECTRLR | ||
1268 | case PNPIPE_INQ: | ||
1269 | val = pn->pipe_state; | ||
1270 | break; | ||
1271 | #endif | ||
1272 | |||
819 | case PNPIPE_IFINDEX: | 1273 | case PNPIPE_IFINDEX: |
820 | val = pn->ifindex; | 1274 | val = pn->ifindex; |
821 | break; | 1275 | break; |
@@ -835,6 +1289,15 @@ static int pipe_skb_send(struct sock *sk, struct sk_buff *skb) | |||
835 | { | 1289 | { |
836 | struct pep_sock *pn = pep_sk(sk); | 1290 | struct pep_sock *pn = pep_sk(sk); |
837 | struct pnpipehdr *ph; | 1291 | struct pnpipehdr *ph; |
1292 | int err; | ||
1293 | #ifdef CONFIG_PHONET_PIPECTRLR | ||
1294 | struct sockaddr_pn spn = { | ||
1295 | .spn_family = AF_PHONET, | ||
1296 | .spn_resource = 0xD9, | ||
1297 | .spn_dev = pn_dev(pn->remote_pep), | ||
1298 | .spn_obj = pn_obj(pn->remote_pep), | ||
1299 | }; | ||
1300 | #endif | ||
838 | 1301 | ||
839 | if (pn_flow_safe(pn->tx_fc) && | 1302 | if (pn_flow_safe(pn->tx_fc) && |
840 | !atomic_add_unless(&pn->tx_credits, -1, 0)) { | 1303 | !atomic_add_unless(&pn->tx_credits, -1, 0)) { |
@@ -852,8 +1315,16 @@ static int pipe_skb_send(struct sock *sk, struct sk_buff *skb) | |||
852 | } else | 1315 | } else |
853 | ph->message_id = PNS_PIPE_DATA; | 1316 | ph->message_id = PNS_PIPE_DATA; |
854 | ph->pipe_handle = pn->pipe_handle; | 1317 | ph->pipe_handle = pn->pipe_handle; |
1318 | #ifdef CONFIG_PHONET_PIPECTRLR | ||
1319 | err = pn_skb_send(sk, skb, &spn); | ||
1320 | #else | ||
1321 | err = pn_skb_send(sk, skb, &pipe_srv); | ||
1322 | #endif | ||
1323 | |||
1324 | if (err && pn_flow_safe(pn->tx_fc)) | ||
1325 | atomic_inc(&pn->tx_credits); | ||
1326 | return err; | ||
855 | 1327 | ||
856 | return pn_skb_send(sk, skb, &pipe_srv); | ||
857 | } | 1328 | } |
858 | 1329 | ||
859 | static int pep_sendmsg(struct kiocb *iocb, struct sock *sk, | 1330 | static int pep_sendmsg(struct kiocb *iocb, struct sock *sk, |
@@ -873,7 +1344,7 @@ static int pep_sendmsg(struct kiocb *iocb, struct sock *sk, | |||
873 | skb = sock_alloc_send_skb(sk, MAX_PNPIPE_HEADER + len, | 1344 | skb = sock_alloc_send_skb(sk, MAX_PNPIPE_HEADER + len, |
874 | flags & MSG_DONTWAIT, &err); | 1345 | flags & MSG_DONTWAIT, &err); |
875 | if (!skb) | 1346 | if (!skb) |
876 | return -ENOBUFS; | 1347 | return err; |
877 | 1348 | ||
878 | skb_reserve(skb, MAX_PHONET_HEADER + 3); | 1349 | skb_reserve(skb, MAX_PHONET_HEADER + 3); |
879 | err = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len); | 1350 | err = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len); |
diff --git a/net/phonet/pn_dev.c b/net/phonet/pn_dev.c index b18e48fae975..947038ddd04c 100644 --- a/net/phonet/pn_dev.c +++ b/net/phonet/pn_dev.c | |||
@@ -292,8 +292,7 @@ static void phonet_route_autodel(struct net_device *dev) | |||
292 | if (bitmap_empty(deleted, 64)) | 292 | if (bitmap_empty(deleted, 64)) |
293 | return; /* short-circuit RCU */ | 293 | return; /* short-circuit RCU */ |
294 | synchronize_rcu(); | 294 | synchronize_rcu(); |
295 | for (i = find_first_bit(deleted, 64); i < 64; | 295 | for_each_set_bit(i, deleted, 64) { |
296 | i = find_next_bit(deleted, 64, i + 1)) { | ||
297 | rtm_phonet_notify(RTM_DELROUTE, dev, i); | 296 | rtm_phonet_notify(RTM_DELROUTE, dev, i); |
298 | dev_put(dev); | 297 | dev_put(dev); |
299 | } | 298 | } |
@@ -374,6 +373,7 @@ int __init phonet_device_init(void) | |||
374 | if (err) | 373 | if (err) |
375 | return err; | 374 | return err; |
376 | 375 | ||
376 | proc_net_fops_create(&init_net, "pnresource", 0, &pn_res_seq_fops); | ||
377 | register_netdevice_notifier(&phonet_device_notifier); | 377 | register_netdevice_notifier(&phonet_device_notifier); |
378 | err = phonet_netlink_register(); | 378 | err = phonet_netlink_register(); |
379 | if (err) | 379 | if (err) |
@@ -386,6 +386,7 @@ void phonet_device_exit(void) | |||
386 | rtnl_unregister_all(PF_PHONET); | 386 | rtnl_unregister_all(PF_PHONET); |
387 | unregister_netdevice_notifier(&phonet_device_notifier); | 387 | unregister_netdevice_notifier(&phonet_device_notifier); |
388 | unregister_pernet_device(&phonet_net_ops); | 388 | unregister_pernet_device(&phonet_net_ops); |
389 | proc_net_remove(&init_net, "pnresource"); | ||
389 | } | 390 | } |
390 | 391 | ||
391 | int phonet_route_add(struct net_device *dev, u8 daddr) | 392 | int phonet_route_add(struct net_device *dev, u8 daddr) |
diff --git a/net/phonet/socket.c b/net/phonet/socket.c index 6e9848bf0370..aca8fba099e9 100644 --- a/net/phonet/socket.c +++ b/net/phonet/socket.c | |||
@@ -158,6 +158,7 @@ void pn_sock_unhash(struct sock *sk) | |||
158 | spin_lock_bh(&pnsocks.lock); | 158 | spin_lock_bh(&pnsocks.lock); |
159 | sk_del_node_init(sk); | 159 | sk_del_node_init(sk); |
160 | spin_unlock_bh(&pnsocks.lock); | 160 | spin_unlock_bh(&pnsocks.lock); |
161 | pn_sock_unbind_all_res(sk); | ||
161 | } | 162 | } |
162 | EXPORT_SYMBOL(pn_sock_unhash); | 163 | EXPORT_SYMBOL(pn_sock_unhash); |
163 | 164 | ||
@@ -281,7 +282,9 @@ static unsigned int pn_socket_poll(struct file *file, struct socket *sock, | |||
281 | if (!mask && sk->sk_state == TCP_CLOSE_WAIT) | 282 | if (!mask && sk->sk_state == TCP_CLOSE_WAIT) |
282 | return POLLHUP; | 283 | return POLLHUP; |
283 | 284 | ||
284 | if (sk->sk_state == TCP_ESTABLISHED && atomic_read(&pn->tx_credits)) | 285 | if (sk->sk_state == TCP_ESTABLISHED && |
286 | atomic_read(&sk->sk_wmem_alloc) < sk->sk_sndbuf && | ||
287 | atomic_read(&pn->tx_credits)) | ||
285 | mask |= POLLOUT | POLLWRNORM | POLLWRBAND; | 288 | mask |= POLLOUT | POLLWRNORM | POLLWRBAND; |
286 | 289 | ||
287 | return mask; | 290 | return mask; |
@@ -563,3 +566,188 @@ const struct file_operations pn_sock_seq_fops = { | |||
563 | .release = seq_release_net, | 566 | .release = seq_release_net, |
564 | }; | 567 | }; |
565 | #endif | 568 | #endif |
569 | |||
570 | static struct { | ||
571 | struct sock *sk[256]; | ||
572 | } pnres; | ||
573 | |||
574 | /* | ||
575 | * Find and hold socket based on resource. | ||
576 | */ | ||
577 | struct sock *pn_find_sock_by_res(struct net *net, u8 res) | ||
578 | { | ||
579 | struct sock *sk; | ||
580 | |||
581 | if (!net_eq(net, &init_net)) | ||
582 | return NULL; | ||
583 | |||
584 | rcu_read_lock(); | ||
585 | sk = rcu_dereference(pnres.sk[res]); | ||
586 | if (sk) | ||
587 | sock_hold(sk); | ||
588 | rcu_read_unlock(); | ||
589 | return sk; | ||
590 | } | ||
591 | |||
592 | static DEFINE_MUTEX(resource_mutex); | ||
593 | |||
594 | int pn_sock_bind_res(struct sock *sk, u8 res) | ||
595 | { | ||
596 | int ret = -EADDRINUSE; | ||
597 | |||
598 | if (!net_eq(sock_net(sk), &init_net)) | ||
599 | return -ENOIOCTLCMD; | ||
600 | if (!capable(CAP_SYS_ADMIN)) | ||
601 | return -EPERM; | ||
602 | if (pn_socket_autobind(sk->sk_socket)) | ||
603 | return -EAGAIN; | ||
604 | |||
605 | mutex_lock(&resource_mutex); | ||
606 | if (pnres.sk[res] == NULL) { | ||
607 | sock_hold(sk); | ||
608 | rcu_assign_pointer(pnres.sk[res], sk); | ||
609 | ret = 0; | ||
610 | } | ||
611 | mutex_unlock(&resource_mutex); | ||
612 | return ret; | ||
613 | } | ||
614 | |||
615 | int pn_sock_unbind_res(struct sock *sk, u8 res) | ||
616 | { | ||
617 | int ret = -ENOENT; | ||
618 | |||
619 | if (!capable(CAP_SYS_ADMIN)) | ||
620 | return -EPERM; | ||
621 | |||
622 | mutex_lock(&resource_mutex); | ||
623 | if (pnres.sk[res] == sk) { | ||
624 | rcu_assign_pointer(pnres.sk[res], NULL); | ||
625 | ret = 0; | ||
626 | } | ||
627 | mutex_unlock(&resource_mutex); | ||
628 | |||
629 | if (ret == 0) { | ||
630 | synchronize_rcu(); | ||
631 | sock_put(sk); | ||
632 | } | ||
633 | return ret; | ||
634 | } | ||
635 | |||
636 | void pn_sock_unbind_all_res(struct sock *sk) | ||
637 | { | ||
638 | unsigned res, match = 0; | ||
639 | |||
640 | mutex_lock(&resource_mutex); | ||
641 | for (res = 0; res < 256; res++) { | ||
642 | if (pnres.sk[res] == sk) { | ||
643 | rcu_assign_pointer(pnres.sk[res], NULL); | ||
644 | match++; | ||
645 | } | ||
646 | } | ||
647 | mutex_unlock(&resource_mutex); | ||
648 | |||
649 | if (match == 0) | ||
650 | return; | ||
651 | synchronize_rcu(); | ||
652 | while (match > 0) { | ||
653 | sock_put(sk); | ||
654 | match--; | ||
655 | } | ||
656 | } | ||
657 | |||
658 | #ifdef CONFIG_PROC_FS | ||
659 | static struct sock **pn_res_get_idx(struct seq_file *seq, loff_t pos) | ||
660 | { | ||
661 | struct net *net = seq_file_net(seq); | ||
662 | unsigned i; | ||
663 | |||
664 | if (!net_eq(net, &init_net)) | ||
665 | return NULL; | ||
666 | |||
667 | for (i = 0; i < 256; i++) { | ||
668 | if (pnres.sk[i] == NULL) | ||
669 | continue; | ||
670 | if (!pos) | ||
671 | return pnres.sk + i; | ||
672 | pos--; | ||
673 | } | ||
674 | return NULL; | ||
675 | } | ||
676 | |||
677 | static struct sock **pn_res_get_next(struct seq_file *seq, struct sock **sk) | ||
678 | { | ||
679 | struct net *net = seq_file_net(seq); | ||
680 | unsigned i; | ||
681 | |||
682 | BUG_ON(!net_eq(net, &init_net)); | ||
683 | |||
684 | for (i = (sk - pnres.sk) + 1; i < 256; i++) | ||
685 | if (pnres.sk[i]) | ||
686 | return pnres.sk + i; | ||
687 | return NULL; | ||
688 | } | ||
689 | |||
690 | static void *pn_res_seq_start(struct seq_file *seq, loff_t *pos) | ||
691 | __acquires(resource_mutex) | ||
692 | { | ||
693 | mutex_lock(&resource_mutex); | ||
694 | return *pos ? pn_res_get_idx(seq, *pos - 1) : SEQ_START_TOKEN; | ||
695 | } | ||
696 | |||
697 | static void *pn_res_seq_next(struct seq_file *seq, void *v, loff_t *pos) | ||
698 | { | ||
699 | struct sock **sk; | ||
700 | |||
701 | if (v == SEQ_START_TOKEN) | ||
702 | sk = pn_res_get_idx(seq, 0); | ||
703 | else | ||
704 | sk = pn_res_get_next(seq, v); | ||
705 | (*pos)++; | ||
706 | return sk; | ||
707 | } | ||
708 | |||
709 | static void pn_res_seq_stop(struct seq_file *seq, void *v) | ||
710 | __releases(resource_mutex) | ||
711 | { | ||
712 | mutex_unlock(&resource_mutex); | ||
713 | } | ||
714 | |||
715 | static int pn_res_seq_show(struct seq_file *seq, void *v) | ||
716 | { | ||
717 | int len; | ||
718 | |||
719 | if (v == SEQ_START_TOKEN) | ||
720 | seq_printf(seq, "%s%n", "rs uid inode", &len); | ||
721 | else { | ||
722 | struct sock **psk = v; | ||
723 | struct sock *sk = *psk; | ||
724 | |||
725 | seq_printf(seq, "%02X %5d %lu%n", | ||
726 | (int) (psk - pnres.sk), sock_i_uid(sk), | ||
727 | sock_i_ino(sk), &len); | ||
728 | } | ||
729 | seq_printf(seq, "%*s\n", 63 - len, ""); | ||
730 | return 0; | ||
731 | } | ||
732 | |||
733 | static const struct seq_operations pn_res_seq_ops = { | ||
734 | .start = pn_res_seq_start, | ||
735 | .next = pn_res_seq_next, | ||
736 | .stop = pn_res_seq_stop, | ||
737 | .show = pn_res_seq_show, | ||
738 | }; | ||
739 | |||
740 | static int pn_res_open(struct inode *inode, struct file *file) | ||
741 | { | ||
742 | return seq_open_net(inode, file, &pn_res_seq_ops, | ||
743 | sizeof(struct seq_net_private)); | ||
744 | } | ||
745 | |||
746 | const struct file_operations pn_res_seq_fops = { | ||
747 | .owner = THIS_MODULE, | ||
748 | .open = pn_res_open, | ||
749 | .read = seq_read, | ||
750 | .llseek = seq_lseek, | ||
751 | .release = seq_release_net, | ||
752 | }; | ||
753 | #endif | ||