aboutsummaryrefslogtreecommitdiffstats
path: root/net/nfc
diff options
context:
space:
mode:
authorSamuel Ortiz <sameo@linux.intel.com>2012-06-25 09:46:28 -0400
committerJohn W. Linville <linville@tuxdriver.com>2012-07-09 16:42:19 -0400
commit8f50020ed9b81ba909ce9573f9d05263cdebf502 (patch)
tree19bd36d2e294dd5af9bbf6d885d5d994473a63b0 /net/nfc
parentebbb16d9ebbdf08aaf2963b7993d0b4a9e41b15e (diff)
NFC: LLCP late binding
With the LLCP 16 local SAPs we can potentially quickly run out of source SAPs for non well known services. With the so called late binding we will reserve an SAP only when we actually get a client connection for a local service. The SAP will be released once the last client is gone, leaving it available to other services. Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Diffstat (limited to 'net/nfc')
-rw-r--r--net/nfc/llcp/llcp.c241
-rw-r--r--net/nfc/llcp/llcp.h2
2 files changed, 166 insertions, 77 deletions
diff --git a/net/nfc/llcp/llcp.c b/net/nfc/llcp/llcp.c
index 9ab17ec50ce7..6094a2099bd6 100644
--- a/net/nfc/llcp/llcp.c
+++ b/net/nfc/llcp/llcp.c
@@ -131,6 +131,44 @@ int nfc_llcp_local_put(struct nfc_llcp_local *local)
131 return kref_put(&local->ref, local_release); 131 return kref_put(&local->ref, local_release);
132} 132}
133 133
134static struct nfc_llcp_sock *nfc_llcp_sock_get(struct nfc_llcp_local *local,
135 u8 ssap, u8 dsap)
136{
137 struct sock *sk;
138 struct hlist_node *node;
139 struct nfc_llcp_sock *llcp_sock;
140
141 pr_debug("ssap dsap %d %d\n", ssap, dsap);
142
143 if (ssap == 0 && dsap == 0)
144 return NULL;
145
146 read_lock(&local->sockets.lock);
147
148 llcp_sock = NULL;
149
150 sk_for_each(sk, node, &local->sockets.head) {
151 llcp_sock = nfc_llcp_sock(sk);
152
153 if (llcp_sock->ssap == ssap && llcp_sock->dsap == dsap)
154 break;
155 }
156
157 read_unlock(&local->sockets.lock);
158
159 if (llcp_sock == NULL)
160 return NULL;
161
162 sock_hold(&llcp_sock->sk);
163
164 return llcp_sock;
165}
166
167static void nfc_llcp_sock_put(struct nfc_llcp_sock *sock)
168{
169 sock_put(&sock->sk);
170}
171
134static void nfc_llcp_timeout_work(struct work_struct *work) 172static void nfc_llcp_timeout_work(struct work_struct *work)
135{ 173{
136 struct nfc_llcp_local *local = container_of(work, struct nfc_llcp_local, 174 struct nfc_llcp_local *local = container_of(work, struct nfc_llcp_local,
@@ -191,6 +229,51 @@ static int nfc_llcp_wks_sap(char *service_name, size_t service_name_len)
191 return -EINVAL; 229 return -EINVAL;
192} 230}
193 231
232static
233struct nfc_llcp_sock *nfc_llcp_sock_from_sn(struct nfc_llcp_local *local,
234 u8 *sn, size_t sn_len)
235{
236 struct sock *sk;
237 struct hlist_node *node;
238 struct nfc_llcp_sock *llcp_sock, *tmp_sock;
239
240 pr_debug("sn %zd %p\n", sn_len, sn);
241
242 if (sn == NULL || sn_len == 0)
243 return NULL;
244
245 read_lock(&local->sockets.lock);
246
247 llcp_sock = NULL;
248
249 sk_for_each(sk, node, &local->sockets.head) {
250 tmp_sock = nfc_llcp_sock(sk);
251
252 pr_debug("llcp sock %p\n", tmp_sock);
253
254 if (tmp_sock->sk.sk_state != LLCP_LISTEN)
255 continue;
256
257 if (tmp_sock->service_name == NULL ||
258 tmp_sock->service_name_len == 0)
259 continue;
260
261 if (tmp_sock->service_name_len != sn_len)
262 continue;
263
264 if (memcmp(sn, tmp_sock->service_name, sn_len) == 0) {
265 llcp_sock = tmp_sock;
266 break;
267 }
268 }
269
270 read_unlock(&local->sockets.lock);
271
272 pr_debug("Found llcp sock %p\n", llcp_sock);
273
274 return llcp_sock;
275}
276
194u8 nfc_llcp_get_sdp_ssap(struct nfc_llcp_local *local, 277u8 nfc_llcp_get_sdp_ssap(struct nfc_llcp_local *local,
195 struct nfc_llcp_sock *sock) 278 struct nfc_llcp_sock *sock)
196{ 279{
@@ -217,22 +300,19 @@ u8 nfc_llcp_get_sdp_ssap(struct nfc_llcp_local *local,
217 } 300 }
218 301
219 /* 302 /*
220 * This is not a well known service, 303 * Check if there already is a non WKS socket bound
221 * we should try to find a local SDP free spot 304 * to this service name.
222 */ 305 */
223 ssap = find_first_zero_bit(&local->local_sdp, LLCP_SDP_NUM_SAP); 306 if (nfc_llcp_sock_from_sn(local, sock->service_name,
224 if (ssap == LLCP_SDP_NUM_SAP) { 307 sock->service_name_len) != NULL) {
225 mutex_unlock(&local->sdp_lock); 308 mutex_unlock(&local->sdp_lock);
226 309
227 return LLCP_SAP_MAX; 310 return LLCP_SAP_MAX;
228 } 311 }
229 312
230 pr_debug("SDP ssap %d\n", LLCP_WKS_NUM_SAP + ssap);
231
232 set_bit(ssap, &local->local_sdp);
233 mutex_unlock(&local->sdp_lock); 313 mutex_unlock(&local->sdp_lock);
234 314
235 return LLCP_WKS_NUM_SAP + ssap; 315 return LLCP_SDP_UNBOUND;
236 316
237 } else if (sock->ssap != 0 && sock->ssap < LLCP_WKS_NUM_SAP) { 317 } else if (sock->ssap != 0 && sock->ssap < LLCP_WKS_NUM_SAP) {
238 if (!test_bit(sock->ssap, &local->local_wks)) { 318 if (!test_bit(sock->ssap, &local->local_wks)) {
@@ -276,8 +356,34 @@ void nfc_llcp_put_ssap(struct nfc_llcp_local *local, u8 ssap)
276 local_ssap = ssap; 356 local_ssap = ssap;
277 sdp = &local->local_wks; 357 sdp = &local->local_wks;
278 } else if (ssap < LLCP_LOCAL_NUM_SAP) { 358 } else if (ssap < LLCP_LOCAL_NUM_SAP) {
359 atomic_t *client_cnt;
360
279 local_ssap = ssap - LLCP_WKS_NUM_SAP; 361 local_ssap = ssap - LLCP_WKS_NUM_SAP;
280 sdp = &local->local_sdp; 362 sdp = &local->local_sdp;
363 client_cnt = &local->local_sdp_cnt[local_ssap];
364
365 pr_debug("%d clients\n", atomic_read(client_cnt));
366
367 mutex_lock(&local->sdp_lock);
368
369 if (atomic_dec_and_test(client_cnt)) {
370 struct nfc_llcp_sock *l_sock;
371
372 pr_debug("No more clients for SAP %d\n", ssap);
373
374 clear_bit(local_ssap, sdp);
375
376 /* Find the listening sock and set it back to UNBOUND */
377 l_sock = nfc_llcp_sock_get(local, ssap, LLCP_SAP_SDP);
378 if (l_sock) {
379 l_sock->ssap = LLCP_SDP_UNBOUND;
380 nfc_llcp_sock_put(l_sock);
381 }
382 }
383
384 mutex_unlock(&local->sdp_lock);
385
386 return;
281 } else if (ssap < LLCP_MAX_SAP) { 387 } else if (ssap < LLCP_MAX_SAP) {
282 local_ssap = ssap - LLCP_LOCAL_NUM_SAP; 388 local_ssap = ssap - LLCP_LOCAL_NUM_SAP;
283 sdp = &local->local_sap; 389 sdp = &local->local_sap;
@@ -292,6 +398,28 @@ void nfc_llcp_put_ssap(struct nfc_llcp_local *local, u8 ssap)
292 mutex_unlock(&local->sdp_lock); 398 mutex_unlock(&local->sdp_lock);
293} 399}
294 400
401static u8 nfc_llcp_reserve_sdp_ssap(struct nfc_llcp_local *local)
402{
403 u8 ssap;
404
405 mutex_lock(&local->sdp_lock);
406
407 ssap = find_first_zero_bit(&local->local_sdp, LLCP_SDP_NUM_SAP);
408 if (ssap == LLCP_SDP_NUM_SAP) {
409 mutex_unlock(&local->sdp_lock);
410
411 return LLCP_SAP_MAX;
412 }
413
414 pr_debug("SDP ssap %d\n", LLCP_WKS_NUM_SAP + ssap);
415
416 set_bit(ssap, &local->local_sdp);
417
418 mutex_unlock(&local->sdp_lock);
419
420 return LLCP_WKS_NUM_SAP + ssap;
421}
422
295static int nfc_llcp_build_gb(struct nfc_llcp_local *local) 423static int nfc_llcp_build_gb(struct nfc_llcp_local *local)
296{ 424{
297 u8 *gb_cur, *version_tlv, version, version_length; 425 u8 *gb_cur, *version_tlv, version, version_length;
@@ -493,74 +621,12 @@ out:
493 return llcp_sock; 621 return llcp_sock;
494} 622}
495 623
496static struct nfc_llcp_sock *nfc_llcp_sock_get(struct nfc_llcp_local *local,
497 u8 ssap, u8 dsap)
498{
499 struct sock *sk;
500 struct hlist_node *node;
501 struct nfc_llcp_sock *llcp_sock;
502
503 pr_debug("ssap dsap %d %d\n", ssap, dsap);
504
505 if (ssap == 0 && dsap == 0)
506 return NULL;
507
508 read_lock(&local->sockets.lock);
509
510 llcp_sock = NULL;
511
512 sk_for_each(sk, node, &local->sockets.head) {
513 llcp_sock = nfc_llcp_sock(sk);
514
515 if (llcp_sock->ssap == ssap &&
516 llcp_sock->dsap == dsap)
517 break;
518 }
519
520 read_unlock(&local->sockets.lock);
521
522 if (llcp_sock == NULL)
523 return NULL;
524
525 sock_hold(&llcp_sock->sk);
526
527 return llcp_sock;
528}
529
530static struct nfc_llcp_sock *nfc_llcp_sock_get_sn(struct nfc_llcp_local *local, 624static struct nfc_llcp_sock *nfc_llcp_sock_get_sn(struct nfc_llcp_local *local,
531 u8 *sn, size_t sn_len) 625 u8 *sn, size_t sn_len)
532{ 626{
533 struct sock *sk;
534 struct hlist_node *node;
535 struct nfc_llcp_sock *llcp_sock; 627 struct nfc_llcp_sock *llcp_sock;
536 628
537 pr_debug("sn %zd\n", sn_len); 629 llcp_sock = nfc_llcp_sock_from_sn(local, sn, sn_len);
538
539 if (sn == NULL || sn_len == 0)
540 return NULL;
541
542 read_lock(&local->sockets.lock);
543
544 llcp_sock = NULL;
545
546 sk_for_each(sk, node, &local->sockets.head) {
547 llcp_sock = nfc_llcp_sock(sk);
548
549 if (llcp_sock->sk.sk_state != LLCP_LISTEN)
550 continue;
551
552 if (llcp_sock->service_name == NULL ||
553 llcp_sock->service_name_len == 0)
554 continue;
555
556 if (llcp_sock->service_name_len != sn_len)
557 continue;
558
559 if (memcmp(sn, llcp_sock->service_name, sn_len) == 0)
560 break;
561 }
562
563 read_unlock(&local->sockets.lock);
564 630
565 if (llcp_sock == NULL) 631 if (llcp_sock == NULL)
566 return NULL; 632 return NULL;
@@ -570,11 +636,6 @@ static struct nfc_llcp_sock *nfc_llcp_sock_get_sn(struct nfc_llcp_local *local,
570 return llcp_sock; 636 return llcp_sock;
571} 637}
572 638
573static void nfc_llcp_sock_put(struct nfc_llcp_sock *sock)
574{
575 sock_put(&sock->sk);
576}
577
578static u8 *nfc_llcp_connect_sn(struct sk_buff *skb, size_t *sn_len) 639static u8 *nfc_llcp_connect_sn(struct sk_buff *skb, size_t *sn_len)
579{ 640{
580 u8 *tlv = &skb->data[2], type, length; 641 u8 *tlv = &skb->data[2], type, length;
@@ -646,6 +707,21 @@ static void nfc_llcp_recv_connect(struct nfc_llcp_local *local,
646 goto fail; 707 goto fail;
647 } 708 }
648 709
710 if (sock->ssap == LLCP_SDP_UNBOUND) {
711 u8 ssap = nfc_llcp_reserve_sdp_ssap(local);
712
713 pr_debug("First client, reserving %d\n", ssap);
714
715 if (ssap == LLCP_SAP_MAX) {
716 reason = LLCP_DM_REJ;
717 release_sock(&sock->sk);
718 sock_put(&sock->sk);
719 goto fail;
720 }
721
722 sock->ssap = ssap;
723 }
724
649 new_sk = nfc_llcp_sock_alloc(NULL, parent->sk_type, GFP_ATOMIC); 725 new_sk = nfc_llcp_sock_alloc(NULL, parent->sk_type, GFP_ATOMIC);
650 if (new_sk == NULL) { 726 if (new_sk == NULL) {
651 reason = LLCP_DM_REJ; 727 reason = LLCP_DM_REJ;
@@ -659,10 +735,21 @@ static void nfc_llcp_recv_connect(struct nfc_llcp_local *local,
659 new_sock->local = nfc_llcp_local_get(local); 735 new_sock->local = nfc_llcp_local_get(local);
660 new_sock->miu = local->remote_miu; 736 new_sock->miu = local->remote_miu;
661 new_sock->nfc_protocol = sock->nfc_protocol; 737 new_sock->nfc_protocol = sock->nfc_protocol;
662 new_sock->ssap = sock->ssap;
663 new_sock->dsap = ssap; 738 new_sock->dsap = ssap;
664 new_sock->target_idx = local->target_idx; 739 new_sock->target_idx = local->target_idx;
665 new_sock->parent = parent; 740 new_sock->parent = parent;
741 new_sock->ssap = sock->ssap;
742 if (sock->ssap < LLCP_LOCAL_NUM_SAP && sock->ssap >= LLCP_WKS_NUM_SAP) {
743 atomic_t *client_count;
744
745 pr_debug("reserved_ssap %d for %p\n", sock->ssap, new_sock);
746
747 client_count =
748 &local->local_sdp_cnt[sock->ssap - LLCP_WKS_NUM_SAP];
749
750 atomic_inc(client_count);
751 new_sock->reserved_ssap = sock->ssap;
752 }
666 753
667 nfc_llcp_parse_connection_tlv(new_sock, &skb->data[LLCP_HEADER_SIZE], 754 nfc_llcp_parse_connection_tlv(new_sock, &skb->data[LLCP_HEADER_SIZE],
668 skb->len - LLCP_HEADER_SIZE); 755 skb->len - LLCP_HEADER_SIZE);
diff --git a/net/nfc/llcp/llcp.h b/net/nfc/llcp/llcp.h
index 374cc4779af4..83b8bba5a280 100644
--- a/net/nfc/llcp/llcp.h
+++ b/net/nfc/llcp/llcp.h
@@ -37,6 +37,7 @@ enum llcp_state {
37#define LLCP_LOCAL_NUM_SAP 32 37#define LLCP_LOCAL_NUM_SAP 32
38#define LLCP_LOCAL_SAP_OFFSET (LLCP_WKS_NUM_SAP + LLCP_SDP_NUM_SAP) 38#define LLCP_LOCAL_SAP_OFFSET (LLCP_WKS_NUM_SAP + LLCP_SDP_NUM_SAP)
39#define LLCP_MAX_SAP (LLCP_WKS_NUM_SAP + LLCP_SDP_NUM_SAP + LLCP_LOCAL_NUM_SAP) 39#define LLCP_MAX_SAP (LLCP_WKS_NUM_SAP + LLCP_SDP_NUM_SAP + LLCP_LOCAL_NUM_SAP)
40#define LLCP_SDP_UNBOUND (LLCP_MAX_SAP + 1)
40 41
41struct nfc_llcp_sock; 42struct nfc_llcp_sock;
42 43
@@ -69,6 +70,7 @@ struct nfc_llcp_local {
69 unsigned long local_wks; /* Well known services */ 70 unsigned long local_wks; /* Well known services */
70 unsigned long local_sdp; /* Local services */ 71 unsigned long local_sdp; /* Local services */
71 unsigned long local_sap; /* Local SAPs, not available for discovery */ 72 unsigned long local_sap; /* Local SAPs, not available for discovery */
73 atomic_t local_sdp_cnt[LLCP_SDP_NUM_SAP];
72 74
73 /* local */ 75 /* local */
74 u8 gb[NFC_MAX_GT_LEN]; 76 u8 gb[NFC_MAX_GT_LEN];