| /* |
| * GPL HEADER START |
| * |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 only, |
| * as published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, but |
| * WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * General Public License version 2 for more details (a copy is included |
| * in the LICENSE file that accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License |
| * version 2 along with this program; If not, see |
| * http://www.gnu.org/licenses/gpl-2.0.html |
| * |
| * GPL HEADER END |
| */ |
| /* |
| * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved. |
| * Use is subject to license terms. |
| * |
| * Copyright (c) 2011, Intel Corporation. |
| */ |
| /* |
| * This file is part of Lustre, http://www.lustre.org/ |
| * Lustre is a trademark of Sun Microsystems, Inc. |
| */ |
| |
| #define DEBUG_SUBSYSTEM S_RPC |
| #include <obd_support.h> |
| #include <obd_class.h> |
| #include <lustre_net.h> |
| |
| #include "ptlrpc_internal.h" |
| |
| static struct cfs_hash *conn_hash; |
| static struct cfs_hash_ops conn_hash_ops; |
| |
| struct ptlrpc_connection * |
| ptlrpc_connection_get(struct lnet_process_id peer, lnet_nid_t self, |
| struct obd_uuid *uuid) |
| { |
| struct ptlrpc_connection *conn, *conn2; |
| |
| conn = cfs_hash_lookup(conn_hash, &peer); |
| if (conn) |
| goto out; |
| |
| conn = kzalloc(sizeof(*conn), GFP_NOFS); |
| if (!conn) |
| return NULL; |
| |
| conn->c_peer = peer; |
| conn->c_self = self; |
| INIT_HLIST_NODE(&conn->c_hash); |
| atomic_set(&conn->c_refcount, 1); |
| if (uuid) |
| obd_str2uuid(&conn->c_remote_uuid, uuid->uuid); |
| |
| /* |
| * Add the newly created conn to the hash, on key collision we |
| * lost a racing addition and must destroy our newly allocated |
| * connection. The object which exists in the has will be |
| * returned and may be compared against out object. |
| */ |
| /* In the function below, .hs_keycmp resolves to |
| * conn_keycmp() |
| */ |
| /* coverity[overrun-buffer-val] */ |
| conn2 = cfs_hash_findadd_unique(conn_hash, &peer, &conn->c_hash); |
| if (conn != conn2) { |
| kfree(conn); |
| conn = conn2; |
| } |
| out: |
| CDEBUG(D_INFO, "conn=%p refcount %d to %s\n", |
| conn, atomic_read(&conn->c_refcount), |
| libcfs_nid2str(conn->c_peer.nid)); |
| return conn; |
| } |
| |
| int ptlrpc_connection_put(struct ptlrpc_connection *conn) |
| { |
| int rc = 0; |
| |
| if (!conn) |
| return rc; |
| |
| LASSERT(atomic_read(&conn->c_refcount) > 1); |
| |
| /* |
| * We do not remove connection from hashtable and |
| * do not free it even if last caller released ref, |
| * as we want to have it cached for the case it is |
| * needed again. |
| * |
| * Deallocating it and later creating new connection |
| * again would be wastful. This way we also avoid |
| * expensive locking to protect things from get/put |
| * race when found cached connection is freed by |
| * ptlrpc_connection_put(). |
| * |
| * It will be freed later in module unload time, |
| * when ptlrpc_connection_fini()->lh_exit->conn_exit() |
| * path is called. |
| */ |
| if (atomic_dec_return(&conn->c_refcount) == 1) |
| rc = 1; |
| |
| CDEBUG(D_INFO, "PUT conn=%p refcount %d to %s\n", |
| conn, atomic_read(&conn->c_refcount), |
| libcfs_nid2str(conn->c_peer.nid)); |
| |
| return rc; |
| } |
| |
| struct ptlrpc_connection * |
| ptlrpc_connection_addref(struct ptlrpc_connection *conn) |
| { |
| atomic_inc(&conn->c_refcount); |
| CDEBUG(D_INFO, "conn=%p refcount %d to %s\n", |
| conn, atomic_read(&conn->c_refcount), |
| libcfs_nid2str(conn->c_peer.nid)); |
| |
| return conn; |
| } |
| |
| int ptlrpc_connection_init(void) |
| { |
| conn_hash = cfs_hash_create("CONN_HASH", |
| HASH_CONN_CUR_BITS, |
| HASH_CONN_MAX_BITS, |
| HASH_CONN_BKT_BITS, 0, |
| CFS_HASH_MIN_THETA, |
| CFS_HASH_MAX_THETA, |
| &conn_hash_ops, CFS_HASH_DEFAULT); |
| if (!conn_hash) |
| return -ENOMEM; |
| |
| return 0; |
| } |
| |
| void ptlrpc_connection_fini(void) |
| { |
| cfs_hash_putref(conn_hash); |
| } |
| |
| /* |
| * Hash operations for net_peer<->connection |
| */ |
| static unsigned int |
| conn_hashfn(struct cfs_hash *hs, const void *key, unsigned int mask) |
| { |
| return cfs_hash_djb2_hash(key, sizeof(struct lnet_process_id), mask); |
| } |
| |
| static int |
| conn_keycmp(const void *key, struct hlist_node *hnode) |
| { |
| struct ptlrpc_connection *conn; |
| const struct lnet_process_id *conn_key; |
| |
| LASSERT(key); |
| conn_key = key; |
| conn = hlist_entry(hnode, struct ptlrpc_connection, c_hash); |
| |
| return conn_key->nid == conn->c_peer.nid && |
| conn_key->pid == conn->c_peer.pid; |
| } |
| |
| static void * |
| conn_key(struct hlist_node *hnode) |
| { |
| struct ptlrpc_connection *conn; |
| |
| conn = hlist_entry(hnode, struct ptlrpc_connection, c_hash); |
| return &conn->c_peer; |
| } |
| |
| static void * |
| conn_object(struct hlist_node *hnode) |
| { |
| return hlist_entry(hnode, struct ptlrpc_connection, c_hash); |
| } |
| |
| static void |
| conn_get(struct cfs_hash *hs, struct hlist_node *hnode) |
| { |
| struct ptlrpc_connection *conn; |
| |
| conn = hlist_entry(hnode, struct ptlrpc_connection, c_hash); |
| atomic_inc(&conn->c_refcount); |
| } |
| |
| static void |
| conn_put_locked(struct cfs_hash *hs, struct hlist_node *hnode) |
| { |
| struct ptlrpc_connection *conn; |
| |
| conn = hlist_entry(hnode, struct ptlrpc_connection, c_hash); |
| atomic_dec(&conn->c_refcount); |
| } |
| |
| static void |
| conn_exit(struct cfs_hash *hs, struct hlist_node *hnode) |
| { |
| struct ptlrpc_connection *conn; |
| |
| conn = hlist_entry(hnode, struct ptlrpc_connection, c_hash); |
| /* |
| * Nothing should be left. Connection user put it and |
| * connection also was deleted from table by this time |
| * so we should have 0 refs. |
| */ |
| LASSERTF(atomic_read(&conn->c_refcount) == 0, |
| "Busy connection with %d refs\n", |
| atomic_read(&conn->c_refcount)); |
| kfree(conn); |
| } |
| |
| static struct cfs_hash_ops conn_hash_ops = { |
| .hs_hash = conn_hashfn, |
| .hs_keycmp = conn_keycmp, |
| .hs_key = conn_key, |
| .hs_object = conn_object, |
| .hs_get = conn_get, |
| .hs_put_locked = conn_put_locked, |
| .hs_exit = conn_exit, |
| }; |