| /* Client connection-specific management code. |
| * |
| * Copyright (C) 2016 Red Hat, Inc. All Rights Reserved. |
| * Written by David Howells (dhowells@redhat.com) |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public Licence |
| * as published by the Free Software Foundation; either version |
| * 2 of the Licence, or (at your option) any later version. |
| */ |
| |
| #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
| |
| #include <linux/slab.h> |
| #include <linux/idr.h> |
| #include <linux/timer.h> |
| #include "ar-internal.h" |
| |
| /* |
| * We use machine-unique IDs for our client connections. |
| */ |
| DEFINE_IDR(rxrpc_client_conn_ids); |
| static DEFINE_SPINLOCK(rxrpc_conn_id_lock); |
| |
| /* |
| * Get a connection ID and epoch for a client connection from the global pool. |
| * The connection struct pointer is then recorded in the idr radix tree. The |
| * epoch is changed if this wraps. |
| * |
| * TODO: The IDR tree gets very expensive on memory if the connection IDs are |
| * widely scattered throughout the number space, so we shall need to retire |
| * connections that have, say, an ID more than four times the maximum number of |
| * client conns away from the current allocation point to try and keep the IDs |
| * concentrated. We will also need to retire connections from an old epoch. |
| */ |
| int rxrpc_get_client_connection_id(struct rxrpc_connection *conn, |
| struct rxrpc_transport *trans, |
| gfp_t gfp) |
| { |
| u32 epoch; |
| int id; |
| |
| _enter(""); |
| |
| idr_preload(gfp); |
| write_lock_bh(&trans->conn_lock); |
| spin_lock(&rxrpc_conn_id_lock); |
| |
| epoch = rxrpc_epoch; |
| |
| /* We could use idr_alloc_cyclic() here, but we really need to know |
| * when the thing wraps so that we can advance the epoch. |
| */ |
| if (rxrpc_client_conn_ids.cur == 0) |
| rxrpc_client_conn_ids.cur = 1; |
| id = idr_alloc(&rxrpc_client_conn_ids, conn, |
| rxrpc_client_conn_ids.cur, 0x40000000, GFP_NOWAIT); |
| if (id < 0) { |
| if (id != -ENOSPC) |
| goto error; |
| id = idr_alloc(&rxrpc_client_conn_ids, conn, |
| 1, 0x40000000, GFP_NOWAIT); |
| if (id < 0) |
| goto error; |
| epoch++; |
| rxrpc_epoch = epoch; |
| } |
| rxrpc_client_conn_ids.cur = id + 1; |
| |
| spin_unlock(&rxrpc_conn_id_lock); |
| write_unlock_bh(&trans->conn_lock); |
| idr_preload_end(); |
| |
| conn->proto.epoch = epoch; |
| conn->proto.cid = id << RXRPC_CIDSHIFT; |
| set_bit(RXRPC_CONN_HAS_IDR, &conn->flags); |
| _leave(" [CID %x:%x]", epoch, conn->proto.cid); |
| return 0; |
| |
| error: |
| spin_unlock(&rxrpc_conn_id_lock); |
| write_unlock_bh(&trans->conn_lock); |
| idr_preload_end(); |
| _leave(" = %d", id); |
| return id; |
| } |
| |
| /* |
| * Release a connection ID for a client connection from the global pool. |
| */ |
| void rxrpc_put_client_connection_id(struct rxrpc_connection *conn) |
| { |
| if (test_bit(RXRPC_CONN_HAS_IDR, &conn->flags)) { |
| spin_lock(&rxrpc_conn_id_lock); |
| idr_remove(&rxrpc_client_conn_ids, |
| conn->proto.cid >> RXRPC_CIDSHIFT); |
| spin_unlock(&rxrpc_conn_id_lock); |
| } |
| } |