diff options
Diffstat (limited to 'net/tipc/socket.c')
-rw-r--r-- | net/tipc/socket.c | 247 |
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); |
62 | static int tipc_sk_withdraw(struct tipc_port *port, uint scope, | 61 | static int tipc_sk_withdraw(struct tipc_port *port, uint scope, |
63 | struct tipc_name_seq const *seq); | 62 | struct tipc_name_seq const *seq); |
63 | static u32 tipc_sk_ref_acquire(struct tipc_sock *tsk); | ||
64 | static void tipc_sk_ref_discard(u32 ref); | ||
65 | static struct tipc_sock *tipc_sk_get(u32 ref); | ||
66 | static struct tipc_sock *tipc_sk_get_next(u32 *ref); | ||
67 | static void tipc_sk_put(struct tipc_sock *tsk); | ||
64 | 68 | ||
65 | static const struct proto_ops packet_ops; | 69 | static const struct proto_ops packet_ops; |
66 | static const struct proto_ops stream_ops; | 70 | static 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 | */ | ||
2265 | struct 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 | */ | ||
2280 | struct 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 | |||
2307 | static struct ref_table tipc_ref_table; | ||
2308 | |||
2309 | static DEFINE_RWLOCK(ref_table_lock); | ||
2310 | |||
2311 | /** | ||
2312 | * tipc_ref_table_init - create reference table for sockets | ||
2313 | */ | ||
2314 | int 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 | */ | ||
2345 | void 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 | */ | ||
2359 | u32 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 | */ | ||
2407 | void 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; | ||
2446 | exit: | ||
2447 | write_unlock_bh(&ref_table_lock); | ||
2448 | } | ||
2449 | |||
2450 | /* tipc_sk_get - find referenced socket and return pointer to it | ||
2451 | */ | ||
2452 | struct 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 | */ | ||
2472 | struct 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 | |||
2492 | static 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 |