Skip to content

Commit 8f50020

Browse files
Samuel Ortizlinvjw
authored andcommitted
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 <[email protected]>
1 parent ebbb16d commit 8f50020

File tree

2 files changed

+166
-77
lines changed

2 files changed

+166
-77
lines changed

net/nfc/llcp/llcp.c

Lines changed: 164 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,44 @@ int nfc_llcp_local_put(struct nfc_llcp_local *local)
131131
return kref_put(&local->ref, local_release);
132132
}
133133

134+
static 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+
167+
static void nfc_llcp_sock_put(struct nfc_llcp_sock *sock)
168+
{
169+
sock_put(&sock->sk);
170+
}
171+
134172
static void nfc_llcp_timeout_work(struct work_struct *work)
135173
{
136174
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)
191229
return -EINVAL;
192230
}
193231

232+
static
233+
struct 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+
194277
u8 nfc_llcp_get_sdp_ssap(struct nfc_llcp_local *local,
195278
struct nfc_llcp_sock *sock)
196279
{
@@ -217,22 +300,19 @@ u8 nfc_llcp_get_sdp_ssap(struct nfc_llcp_local *local,
217300
}
218301

219302
/*
220-
* This is not a well known service,
221-
* we should try to find a local SDP free spot
303+
* Check if there already is a non WKS socket bound
304+
* to this service name.
222305
*/
223-
ssap = find_first_zero_bit(&local->local_sdp, LLCP_SDP_NUM_SAP);
224-
if (ssap == LLCP_SDP_NUM_SAP) {
306+
if (nfc_llcp_sock_from_sn(local, sock->service_name,
307+
sock->service_name_len) != NULL) {
225308
mutex_unlock(&local->sdp_lock);
226309

227310
return LLCP_SAP_MAX;
228311
}
229312

230-
pr_debug("SDP ssap %d\n", LLCP_WKS_NUM_SAP + ssap);
231-
232-
set_bit(ssap, &local->local_sdp);
233313
mutex_unlock(&local->sdp_lock);
234314

235-
return LLCP_WKS_NUM_SAP + ssap;
315+
return LLCP_SDP_UNBOUND;
236316

237317
} else if (sock->ssap != 0 && sock->ssap < LLCP_WKS_NUM_SAP) {
238318
if (!test_bit(sock->ssap, &local->local_wks)) {
@@ -276,8 +356,34 @@ void nfc_llcp_put_ssap(struct nfc_llcp_local *local, u8 ssap)
276356
local_ssap = ssap;
277357
sdp = &local->local_wks;
278358
} else if (ssap < LLCP_LOCAL_NUM_SAP) {
359+
atomic_t *client_cnt;
360+
279361
local_ssap = ssap - LLCP_WKS_NUM_SAP;
280362
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;
281387
} else if (ssap < LLCP_MAX_SAP) {
282388
local_ssap = ssap - LLCP_LOCAL_NUM_SAP;
283389
sdp = &local->local_sap;
@@ -292,6 +398,28 @@ void nfc_llcp_put_ssap(struct nfc_llcp_local *local, u8 ssap)
292398
mutex_unlock(&local->sdp_lock);
293399
}
294400

401+
static 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+
295423
static int nfc_llcp_build_gb(struct nfc_llcp_local *local)
296424
{
297425
u8 *gb_cur, *version_tlv, version, version_length;
@@ -493,74 +621,12 @@ static struct nfc_llcp_sock *nfc_llcp_connecting_sock_get(struct nfc_llcp_local
493621
return llcp_sock;
494622
}
495623

496-
static 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-
530624
static struct nfc_llcp_sock *nfc_llcp_sock_get_sn(struct nfc_llcp_local *local,
531625
u8 *sn, size_t sn_len)
532626
{
533-
struct sock *sk;
534-
struct hlist_node *node;
535627
struct nfc_llcp_sock *llcp_sock;
536628

537-
pr_debug("sn %zd\n", 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);
629+
llcp_sock = nfc_llcp_sock_from_sn(local, sn, sn_len);
564630

565631
if (llcp_sock == NULL)
566632
return NULL;
@@ -570,11 +636,6 @@ static struct nfc_llcp_sock *nfc_llcp_sock_get_sn(struct nfc_llcp_local *local,
570636
return llcp_sock;
571637
}
572638

573-
static void nfc_llcp_sock_put(struct nfc_llcp_sock *sock)
574-
{
575-
sock_put(&sock->sk);
576-
}
577-
578639
static u8 *nfc_llcp_connect_sn(struct sk_buff *skb, size_t *sn_len)
579640
{
580641
u8 *tlv = &skb->data[2], type, length;
@@ -646,6 +707,21 @@ static void nfc_llcp_recv_connect(struct nfc_llcp_local *local,
646707
goto fail;
647708
}
648709

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+
649725
new_sk = nfc_llcp_sock_alloc(NULL, parent->sk_type, GFP_ATOMIC);
650726
if (new_sk == NULL) {
651727
reason = LLCP_DM_REJ;
@@ -659,10 +735,21 @@ static void nfc_llcp_recv_connect(struct nfc_llcp_local *local,
659735
new_sock->local = nfc_llcp_local_get(local);
660736
new_sock->miu = local->remote_miu;
661737
new_sock->nfc_protocol = sock->nfc_protocol;
662-
new_sock->ssap = sock->ssap;
663738
new_sock->dsap = ssap;
664739
new_sock->target_idx = local->target_idx;
665740
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+
}
666753

667754
nfc_llcp_parse_connection_tlv(new_sock, &skb->data[LLCP_HEADER_SIZE],
668755
skb->len - LLCP_HEADER_SIZE);

net/nfc/llcp/llcp.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ enum llcp_state {
3737
#define LLCP_LOCAL_NUM_SAP 32
3838
#define LLCP_LOCAL_SAP_OFFSET (LLCP_WKS_NUM_SAP + LLCP_SDP_NUM_SAP)
3939
#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)
4041

4142
struct nfc_llcp_sock;
4243

@@ -69,6 +70,7 @@ struct nfc_llcp_local {
6970
unsigned long local_wks; /* Well known services */
7071
unsigned long local_sdp; /* Local services */
7172
unsigned long local_sap; /* Local SAPs, not available for discovery */
73+
atomic_t local_sdp_cnt[LLCP_SDP_NUM_SAP];
7274

7375
/* local */
7476
u8 gb[NFC_MAX_GT_LEN];

0 commit comments

Comments
 (0)