/* 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 #include #include #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, gfp_t gfp) { u32 epoch; int id; _enter(""); idr_preload(gfp); 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); 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); 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); } }