aboutsummaryrefslogtreecommitdiffstats
path: root/net/tipc/socket.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/tipc/socket.c')
-rw-r--r--net/tipc/socket.c247
1 files changed, 244 insertions, 3 deletions
diff --git a/net/tipc/socket.c b/net/tipc/socket.c
index 70eaceae1f8c..6a699671245b 100644
--- a/net/tipc/socket.c
+++ b/net/tipc/socket.c
@@ -35,7 +35,6 @@
35 */ 35 */
36 36
37#include "core.h" 37#include "core.h"
38#include "ref.h"
39#include "name_table.h" 38#include "name_table.h"
40#include "node.h" 39#include "node.h"
41#include "link.h" 40#include "link.h"
@@ -61,6 +60,11 @@ static int tipc_sk_publish(struct tipc_port *port, uint scope,
61 struct tipc_name_seq const *seq); 60 struct tipc_name_seq const *seq);
62static int tipc_sk_withdraw(struct tipc_port *port, uint scope, 61static int tipc_sk_withdraw(struct tipc_port *port, uint scope,
63 struct tipc_name_seq const *seq); 62 struct tipc_name_seq const *seq);
63static u32 tipc_sk_ref_acquire(struct tipc_sock *tsk);
64static void tipc_sk_ref_discard(u32 ref);
65static struct tipc_sock *tipc_sk_get(u32 ref);
66static struct tipc_sock *tipc_sk_get_next(u32 *ref);
67static void tipc_sk_put(struct tipc_sock *tsk);
64 68
65static const struct proto_ops packet_ops; 69static const struct proto_ops packet_ops;
66static const struct proto_ops stream_ops; 70static const struct proto_ops stream_ops;
@@ -271,7 +275,7 @@ static int tipc_sk_create(struct net *net, struct socket *sock,
271 275
272 tsk = tipc_sk(sk); 276 tsk = tipc_sk(sk);
273 port = &tsk->port; 277 port = &tsk->port;
274 ref = tipc_ref_acquire(tsk); 278 ref = tipc_sk_ref_acquire(tsk);
275 if (!ref) { 279 if (!ref) {
276 pr_warn("Socket create failed; reference table exhausted\n"); 280 pr_warn("Socket create failed; reference table exhausted\n");
277 return -ENOMEM; 281 return -ENOMEM;
@@ -434,7 +438,7 @@ static int tipc_release(struct socket *sock)
434 } 438 }
435 439
436 tipc_sk_withdraw(port, 0, NULL); 440 tipc_sk_withdraw(port, 0, NULL);
437 tipc_ref_discard(port->ref); 441 tipc_sk_ref_discard(port->ref);
438 k_cancel_timer(&port->timer); 442 k_cancel_timer(&port->timer);
439 if (port->connected) { 443 if (port->connected) {
440 buf = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, TIPC_CONN_MSG, 444 buf = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, TIPC_CONN_MSG,
@@ -2254,6 +2258,243 @@ void tipc_sk_reinit(void)
2254} 2258}
2255 2259
2256/** 2260/**
2261 * struct reference - TIPC socket reference entry
2262 * @tsk: pointer to socket associated with reference entry
2263 * @ref: reference value for socket (combines instance & array index info)
2264 */
2265struct reference {
2266 struct tipc_sock *tsk;
2267 u32 ref;
2268};
2269
2270/**
2271 * struct tipc_ref_table - table of TIPC socket reference entries
2272 * @entries: pointer to array of reference entries
2273 * @capacity: array index of first unusable entry
2274 * @init_point: array index of first uninitialized entry
2275 * @first_free: array index of first unused socket reference entry
2276 * @last_free: array index of last unused socket reference entry
2277 * @index_mask: bitmask for array index portion of reference values
2278 * @start_mask: initial value for instance value portion of reference values
2279 */
2280struct ref_table {
2281 struct reference *entries;
2282 u32 capacity;
2283 u32 init_point;
2284 u32 first_free;
2285 u32 last_free;
2286 u32 index_mask;
2287 u32 start_mask;
2288};
2289
2290/* Socket reference table consists of 2**N entries.
2291 *
2292 * State Socket ptr Reference
2293 * ----- ---------- ---------
2294 * In use non-NULL XXXX|own index
2295 * (XXXX changes each time entry is acquired)
2296 * Free NULL YYYY|next free index
2297 * (YYYY is one more than last used XXXX)
2298 * Uninitialized NULL 0
2299 *
2300 * Entry 0 is not used; this allows index 0 to denote the end of the free list.
2301 *
2302 * Note that a reference value of 0 does not necessarily indicate that an
2303 * entry is uninitialized, since the last entry in the free list could also
2304 * have a reference value of 0 (although this is unlikely).
2305 */
2306
2307static struct ref_table tipc_ref_table;
2308
2309static DEFINE_RWLOCK(ref_table_lock);
2310
2311/**
2312 * tipc_ref_table_init - create reference table for sockets
2313 */
2314int tipc_sk_ref_table_init(u32 req_sz, u32 start)
2315{
2316 struct reference *table;
2317 u32 actual_sz;
2318
2319 /* account for unused entry, then round up size to a power of 2 */
2320
2321 req_sz++;
2322 for (actual_sz = 16; actual_sz < req_sz; actual_sz <<= 1) {
2323 /* do nothing */
2324 };
2325
2326 /* allocate table & mark all entries as uninitialized */
2327 table = vzalloc(actual_sz * sizeof(struct reference));
2328 if (table == NULL)
2329 return -ENOMEM;
2330
2331 tipc_ref_table.entries = table;
2332 tipc_ref_table.capacity = req_sz;
2333 tipc_ref_table.init_point = 1;
2334 tipc_ref_table.first_free = 0;
2335 tipc_ref_table.last_free = 0;
2336 tipc_ref_table.index_mask = actual_sz - 1;
2337 tipc_ref_table.start_mask = start & ~tipc_ref_table.index_mask;
2338
2339 return 0;
2340}
2341
2342/**
2343 * tipc_ref_table_stop - destroy reference table for sockets
2344 */
2345void tipc_sk_ref_table_stop(void)
2346{
2347 if (!tipc_ref_table.entries)
2348 return;
2349 vfree(tipc_ref_table.entries);
2350 tipc_ref_table.entries = NULL;
2351}
2352
2353/* tipc_ref_acquire - create reference to a socket
2354 *
2355 * Register an socket pointer in the reference table.
2356 * Returns a unique reference value that is used from then on to retrieve the
2357 * socket pointer, or to determine if the socket has been deregistered.
2358 */
2359u32 tipc_sk_ref_acquire(struct tipc_sock *tsk)
2360{
2361 u32 index;
2362 u32 index_mask;
2363 u32 next_plus_upper;
2364 u32 ref = 0;
2365 struct reference *entry;
2366
2367 if (unlikely(!tsk)) {
2368 pr_err("Attempt to acquire ref. to non-existent obj\n");
2369 return 0;
2370 }
2371 if (unlikely(!tipc_ref_table.entries)) {
2372 pr_err("Ref. table not found in acquisition attempt\n");
2373 return 0;
2374 }
2375
2376 /* Take a free entry, if available; otherwise initialize a new one */
2377 write_lock_bh(&ref_table_lock);
2378 index = tipc_ref_table.first_free;
2379 entry = &tipc_ref_table.entries[index];
2380
2381 if (likely(index)) {
2382 index = tipc_ref_table.first_free;
2383 entry = &tipc_ref_table.entries[index];
2384 index_mask = tipc_ref_table.index_mask;
2385 next_plus_upper = entry->ref;
2386 tipc_ref_table.first_free = next_plus_upper & index_mask;
2387 ref = (next_plus_upper & ~index_mask) + index;
2388 entry->tsk = tsk;
2389 } else if (tipc_ref_table.init_point < tipc_ref_table.capacity) {
2390 index = tipc_ref_table.init_point++;
2391 entry = &tipc_ref_table.entries[index];
2392 ref = tipc_ref_table.start_mask + index;
2393 }
2394
2395 if (ref) {
2396 entry->ref = ref;
2397 entry->tsk = tsk;
2398 }
2399 write_unlock_bh(&ref_table_lock);
2400 return ref;
2401}
2402
2403/* tipc_sk_ref_discard - invalidate reference to an socket
2404 *
2405 * Disallow future references to an socket and free up the entry for re-use.
2406 */
2407void tipc_sk_ref_discard(u32 ref)
2408{
2409 struct reference *entry;
2410 u32 index;
2411 u32 index_mask;
2412
2413 if (unlikely(!tipc_ref_table.entries)) {
2414 pr_err("Ref. table not found during discard attempt\n");
2415 return;
2416 }
2417
2418 index_mask = tipc_ref_table.index_mask;
2419 index = ref & index_mask;
2420 entry = &tipc_ref_table.entries[index];
2421
2422 write_lock_bh(&ref_table_lock);
2423
2424 if (unlikely(!entry->tsk)) {
2425 pr_err("Attempt to discard ref. to non-existent socket\n");
2426 goto exit;
2427 }
2428 if (unlikely(entry->ref != ref)) {
2429 pr_err("Attempt to discard non-existent reference\n");
2430 goto exit;
2431 }
2432
2433 /* Mark entry as unused; increment instance part of entry's
2434 * reference to invalidate any subsequent references
2435 */
2436
2437 entry->tsk = NULL;
2438 entry->ref = (ref & ~index_mask) + (index_mask + 1);
2439
2440 /* Append entry to free entry list */
2441 if (unlikely(tipc_ref_table.first_free == 0))
2442 tipc_ref_table.first_free = index;
2443 else
2444 tipc_ref_table.entries[tipc_ref_table.last_free].ref |= index;
2445 tipc_ref_table.last_free = index;
2446exit:
2447 write_unlock_bh(&ref_table_lock);
2448}
2449
2450/* tipc_sk_get - find referenced socket and return pointer to it
2451 */
2452struct tipc_sock *tipc_sk_get(u32 ref)
2453{
2454 struct reference *entry;
2455 struct tipc_sock *tsk;
2456
2457 if (unlikely(!tipc_ref_table.entries))
2458 return NULL;
2459 read_lock_bh(&ref_table_lock);
2460 entry = &tipc_ref_table.entries[ref & tipc_ref_table.index_mask];
2461 tsk = entry->tsk;
2462 if (likely(tsk && (entry->ref == ref)))
2463 sock_hold(&tsk->sk);
2464 else
2465 tsk = NULL;
2466 read_unlock_bh(&ref_table_lock);
2467 return tsk;
2468}
2469
2470/* tipc_sk_get_next - lock & return next socket after referenced one
2471*/
2472struct tipc_sock *tipc_sk_get_next(u32 *ref)
2473{
2474 struct reference *entry;
2475 struct tipc_sock *tsk = NULL;
2476 uint index = *ref & tipc_ref_table.index_mask;
2477
2478 read_lock_bh(&ref_table_lock);
2479 while (++index < tipc_ref_table.capacity) {
2480 entry = &tipc_ref_table.entries[index];
2481 if (!entry->tsk)
2482 continue;
2483 tsk = entry->tsk;
2484 sock_hold(&tsk->sk);
2485 *ref = entry->ref;
2486 break;
2487 }
2488 read_unlock_bh(&ref_table_lock);
2489 return tsk;
2490}
2491
2492static void tipc_sk_put(struct tipc_sock *tsk)
2493{
2494 sock_put(&tsk->sk);
2495}
2496
2497/**
2257 * tipc_setsockopt - set socket option 2498 * tipc_setsockopt - set socket option
2258 * @sock: socket structure 2499 * @sock: socket structure
2259 * @lvl: option level 2500 * @lvl: option level