1a9148abdSDoug Rabson /*- 2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 3fe267a55SPedro F. Giffuni * 4a9148abdSDoug Rabson * Copyright (c) 2008 Isilon Inc http://www.isilon.com/ 5a9148abdSDoug Rabson * Authors: Doug Rabson <dfr@rabson.org> 6a9148abdSDoug Rabson * Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org> 7a9148abdSDoug Rabson * 8a9148abdSDoug Rabson * Redistribution and use in source and binary forms, with or without 9a9148abdSDoug Rabson * modification, are permitted provided that the following conditions 10a9148abdSDoug Rabson * are met: 11a9148abdSDoug Rabson * 1. Redistributions of source code must retain the above copyright 12a9148abdSDoug Rabson * notice, this list of conditions and the following disclaimer. 13a9148abdSDoug Rabson * 2. Redistributions in binary form must reproduce the above copyright 14a9148abdSDoug Rabson * notice, this list of conditions and the following disclaimer in the 15a9148abdSDoug Rabson * documentation and/or other materials provided with the distribution. 16a9148abdSDoug Rabson * 17a9148abdSDoug Rabson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18a9148abdSDoug Rabson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19a9148abdSDoug Rabson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20a9148abdSDoug Rabson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21a9148abdSDoug Rabson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22a9148abdSDoug Rabson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23a9148abdSDoug Rabson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24a9148abdSDoug Rabson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25a9148abdSDoug Rabson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26a9148abdSDoug Rabson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27a9148abdSDoug Rabson * SUCH DAMAGE. 28a9148abdSDoug Rabson */ 29a9148abdSDoug Rabson 30a9148abdSDoug Rabson #include <sys/cdefs.h> 31a9148abdSDoug Rabson __FBSDID("$FreeBSD$"); 32a9148abdSDoug Rabson 33a9148abdSDoug Rabson #include <sys/param.h> 34a9148abdSDoug Rabson #include <sys/hash.h> 35a9148abdSDoug Rabson #include <sys/kernel.h> 36a9148abdSDoug Rabson #include <sys/lock.h> 378ec07310SGleb Smirnoff #include <sys/malloc.h> 38a9148abdSDoug Rabson #include <sys/mbuf.h> 39a9148abdSDoug Rabson #include <sys/mutex.h> 40a9148abdSDoug Rabson #include <sys/queue.h> 41a9148abdSDoug Rabson 42a9148abdSDoug Rabson #include <rpc/rpc.h> 43a9148abdSDoug Rabson #include <rpc/replay.h> 44a9148abdSDoug Rabson 45a9148abdSDoug Rabson struct replay_cache_entry { 46a9148abdSDoug Rabson int rce_hash; 47a9148abdSDoug Rabson struct rpc_msg rce_msg; 48a9148abdSDoug Rabson struct sockaddr_storage rce_addr; 49a9148abdSDoug Rabson struct rpc_msg rce_repmsg; 50a9148abdSDoug Rabson struct mbuf *rce_repbody; 51a9148abdSDoug Rabson 52a9148abdSDoug Rabson TAILQ_ENTRY(replay_cache_entry) rce_link; 53a9148abdSDoug Rabson TAILQ_ENTRY(replay_cache_entry) rce_alllink; 54a9148abdSDoug Rabson }; 55a9148abdSDoug Rabson TAILQ_HEAD(replay_cache_list, replay_cache_entry); 56a9148abdSDoug Rabson 57a9148abdSDoug Rabson static struct replay_cache_entry * 58a9148abdSDoug Rabson replay_alloc(struct replay_cache *rc, struct rpc_msg *msg, 59a9148abdSDoug Rabson struct sockaddr *addr, int h); 60a9148abdSDoug Rabson static void replay_free(struct replay_cache *rc, 61a9148abdSDoug Rabson struct replay_cache_entry *rce); 62a9148abdSDoug Rabson static void replay_prune(struct replay_cache *rc); 63a9148abdSDoug Rabson 64a9148abdSDoug Rabson #define REPLAY_HASH_SIZE 256 65a9148abdSDoug Rabson #define REPLAY_MAX 1024 66a9148abdSDoug Rabson 67a9148abdSDoug Rabson struct replay_cache { 68a9148abdSDoug Rabson struct replay_cache_list rc_cache[REPLAY_HASH_SIZE]; 69a9148abdSDoug Rabson struct replay_cache_list rc_all; 70a9148abdSDoug Rabson struct mtx rc_lock; 71a9148abdSDoug Rabson int rc_count; 72a9148abdSDoug Rabson size_t rc_size; 73a9148abdSDoug Rabson size_t rc_maxsize; 74a9148abdSDoug Rabson }; 75a9148abdSDoug Rabson 76a9148abdSDoug Rabson struct replay_cache * 77a9148abdSDoug Rabson replay_newcache(size_t maxsize) 78a9148abdSDoug Rabson { 79a9148abdSDoug Rabson struct replay_cache *rc; 80a9148abdSDoug Rabson int i; 81a9148abdSDoug Rabson 82a9148abdSDoug Rabson rc = malloc(sizeof(*rc), M_RPC, M_WAITOK|M_ZERO); 83a9148abdSDoug Rabson for (i = 0; i < REPLAY_HASH_SIZE; i++) 84a9148abdSDoug Rabson TAILQ_INIT(&rc->rc_cache[i]); 85a9148abdSDoug Rabson TAILQ_INIT(&rc->rc_all); 86a9148abdSDoug Rabson mtx_init(&rc->rc_lock, "rc_lock", NULL, MTX_DEF); 87a9148abdSDoug Rabson rc->rc_maxsize = maxsize; 88a9148abdSDoug Rabson 89a9148abdSDoug Rabson return (rc); 90a9148abdSDoug Rabson } 91a9148abdSDoug Rabson 92a9148abdSDoug Rabson void 93a9148abdSDoug Rabson replay_setsize(struct replay_cache *rc, size_t newmaxsize) 94a9148abdSDoug Rabson { 95a9148abdSDoug Rabson 96d7dc2db4SRick Macklem mtx_lock(&rc->rc_lock); 97a9148abdSDoug Rabson rc->rc_maxsize = newmaxsize; 98a9148abdSDoug Rabson replay_prune(rc); 99d7dc2db4SRick Macklem mtx_unlock(&rc->rc_lock); 100a9148abdSDoug Rabson } 101a9148abdSDoug Rabson 102a9148abdSDoug Rabson void 103a9148abdSDoug Rabson replay_freecache(struct replay_cache *rc) 104a9148abdSDoug Rabson { 105a9148abdSDoug Rabson 106a9148abdSDoug Rabson mtx_lock(&rc->rc_lock); 107a9148abdSDoug Rabson while (TAILQ_FIRST(&rc->rc_all)) 108a9148abdSDoug Rabson replay_free(rc, TAILQ_FIRST(&rc->rc_all)); 109a9148abdSDoug Rabson mtx_destroy(&rc->rc_lock); 110a9148abdSDoug Rabson free(rc, M_RPC); 111a9148abdSDoug Rabson } 112a9148abdSDoug Rabson 113a9148abdSDoug Rabson static struct replay_cache_entry * 114a9148abdSDoug Rabson replay_alloc(struct replay_cache *rc, 115a9148abdSDoug Rabson struct rpc_msg *msg, struct sockaddr *addr, int h) 116a9148abdSDoug Rabson { 117a9148abdSDoug Rabson struct replay_cache_entry *rce; 118a9148abdSDoug Rabson 1190778b1d1SPawel Jakub Dawidek mtx_assert(&rc->rc_lock, MA_OWNED); 1200778b1d1SPawel Jakub Dawidek 121a9148abdSDoug Rabson rc->rc_count++; 122a9148abdSDoug Rabson rce = malloc(sizeof(*rce), M_RPC, M_NOWAIT|M_ZERO); 1230778b1d1SPawel Jakub Dawidek if (!rce) 1240778b1d1SPawel Jakub Dawidek return (NULL); 125a9148abdSDoug Rabson rce->rce_hash = h; 126a9148abdSDoug Rabson rce->rce_msg = *msg; 127a9148abdSDoug Rabson bcopy(addr, &rce->rce_addr, addr->sa_len); 128a9148abdSDoug Rabson 129a9148abdSDoug Rabson TAILQ_INSERT_HEAD(&rc->rc_cache[h], rce, rce_link); 130a9148abdSDoug Rabson TAILQ_INSERT_HEAD(&rc->rc_all, rce, rce_alllink); 131a9148abdSDoug Rabson 132a9148abdSDoug Rabson return (rce); 133a9148abdSDoug Rabson } 134a9148abdSDoug Rabson 135a9148abdSDoug Rabson static void 136a9148abdSDoug Rabson replay_free(struct replay_cache *rc, struct replay_cache_entry *rce) 137a9148abdSDoug Rabson { 138a9148abdSDoug Rabson 1390778b1d1SPawel Jakub Dawidek mtx_assert(&rc->rc_lock, MA_OWNED); 1400778b1d1SPawel Jakub Dawidek 141a9148abdSDoug Rabson rc->rc_count--; 142a9148abdSDoug Rabson TAILQ_REMOVE(&rc->rc_cache[rce->rce_hash], rce, rce_link); 143a9148abdSDoug Rabson TAILQ_REMOVE(&rc->rc_all, rce, rce_alllink); 144a9148abdSDoug Rabson if (rce->rce_repbody) { 145a9148abdSDoug Rabson rc->rc_size -= m_length(rce->rce_repbody, NULL); 146a9148abdSDoug Rabson m_freem(rce->rce_repbody); 147a9148abdSDoug Rabson } 148a9148abdSDoug Rabson free(rce, M_RPC); 149a9148abdSDoug Rabson } 150a9148abdSDoug Rabson 151a9148abdSDoug Rabson static void 152a9148abdSDoug Rabson replay_prune(struct replay_cache *rc) 153a9148abdSDoug Rabson { 154a9148abdSDoug Rabson struct replay_cache_entry *rce; 155a9148abdSDoug Rabson 1560778b1d1SPawel Jakub Dawidek mtx_assert(&rc->rc_lock, MA_OWNED); 1570778b1d1SPawel Jakub Dawidek 1580778b1d1SPawel Jakub Dawidek if (rc->rc_count < REPLAY_MAX && rc->rc_size <= rc->rc_maxsize) 1590778b1d1SPawel Jakub Dawidek return; 1600778b1d1SPawel Jakub Dawidek 161a9148abdSDoug Rabson do { 162a9148abdSDoug Rabson /* 1630778b1d1SPawel Jakub Dawidek * Try to free an entry. Don't free in-progress entries. 164a9148abdSDoug Rabson */ 1650778b1d1SPawel Jakub Dawidek TAILQ_FOREACH_REVERSE(rce, &rc->rc_all, replay_cache_list, 1660778b1d1SPawel Jakub Dawidek rce_alllink) { 1670778b1d1SPawel Jakub Dawidek if (rce->rce_repmsg.rm_xid) 168a9148abdSDoug Rabson break; 169a9148abdSDoug Rabson } 1700778b1d1SPawel Jakub Dawidek if (rce) 1710778b1d1SPawel Jakub Dawidek replay_free(rc, rce); 1720778b1d1SPawel Jakub Dawidek } while (rce && (rc->rc_count >= REPLAY_MAX 173a9148abdSDoug Rabson || rc->rc_size > rc->rc_maxsize)); 174a9148abdSDoug Rabson } 175a9148abdSDoug Rabson 176a9148abdSDoug Rabson enum replay_state 177a9148abdSDoug Rabson replay_find(struct replay_cache *rc, struct rpc_msg *msg, 178a9148abdSDoug Rabson struct sockaddr *addr, struct rpc_msg *repmsg, struct mbuf **mp) 179a9148abdSDoug Rabson { 180a9148abdSDoug Rabson int h = HASHSTEP(HASHINIT, msg->rm_xid) % REPLAY_HASH_SIZE; 181a9148abdSDoug Rabson struct replay_cache_entry *rce; 182a9148abdSDoug Rabson 183a9148abdSDoug Rabson mtx_lock(&rc->rc_lock); 184a9148abdSDoug Rabson TAILQ_FOREACH(rce, &rc->rc_cache[h], rce_link) { 185a9148abdSDoug Rabson if (rce->rce_msg.rm_xid == msg->rm_xid 186a9148abdSDoug Rabson && rce->rce_msg.rm_call.cb_prog == msg->rm_call.cb_prog 187a9148abdSDoug Rabson && rce->rce_msg.rm_call.cb_vers == msg->rm_call.cb_vers 188a9148abdSDoug Rabson && rce->rce_msg.rm_call.cb_proc == msg->rm_call.cb_proc 189a9148abdSDoug Rabson && rce->rce_addr.ss_len == addr->sa_len 190a9148abdSDoug Rabson && bcmp(&rce->rce_addr, addr, addr->sa_len) == 0) { 191a9148abdSDoug Rabson if (rce->rce_repmsg.rm_xid) { 192a9148abdSDoug Rabson /* 193a9148abdSDoug Rabson * We have a reply for this 194a9148abdSDoug Rabson * message. Copy it and return. Keep 195a9148abdSDoug Rabson * replay_all LRU sorted 196a9148abdSDoug Rabson */ 197a9148abdSDoug Rabson TAILQ_REMOVE(&rc->rc_all, rce, rce_alllink); 198a9148abdSDoug Rabson TAILQ_INSERT_HEAD(&rc->rc_all, rce, 199a9148abdSDoug Rabson rce_alllink); 200a9148abdSDoug Rabson *repmsg = rce->rce_repmsg; 201a9148abdSDoug Rabson if (rce->rce_repbody) { 202a9148abdSDoug Rabson *mp = m_copym(rce->rce_repbody, 203a9148abdSDoug Rabson 0, M_COPYALL, M_NOWAIT); 204a9148abdSDoug Rabson mtx_unlock(&rc->rc_lock); 205a9148abdSDoug Rabson if (!*mp) 206a9148abdSDoug Rabson return (RS_ERROR); 207a9148abdSDoug Rabson } else { 208a9148abdSDoug Rabson mtx_unlock(&rc->rc_lock); 209a9148abdSDoug Rabson } 210a9148abdSDoug Rabson return (RS_DONE); 211a9148abdSDoug Rabson } else { 212a9148abdSDoug Rabson mtx_unlock(&rc->rc_lock); 213a9148abdSDoug Rabson return (RS_INPROGRESS); 214a9148abdSDoug Rabson } 215a9148abdSDoug Rabson } 216a9148abdSDoug Rabson } 217a9148abdSDoug Rabson 218a9148abdSDoug Rabson replay_prune(rc); 219a9148abdSDoug Rabson 220a9148abdSDoug Rabson rce = replay_alloc(rc, msg, addr, h); 221a9148abdSDoug Rabson 222a9148abdSDoug Rabson mtx_unlock(&rc->rc_lock); 223a9148abdSDoug Rabson 224a9148abdSDoug Rabson if (!rce) 225a9148abdSDoug Rabson return (RS_ERROR); 226a9148abdSDoug Rabson else 227a9148abdSDoug Rabson return (RS_NEW); 228a9148abdSDoug Rabson } 229a9148abdSDoug Rabson 230a9148abdSDoug Rabson void 231a9148abdSDoug Rabson replay_setreply(struct replay_cache *rc, 232a9148abdSDoug Rabson struct rpc_msg *repmsg, struct sockaddr *addr, struct mbuf *m) 233a9148abdSDoug Rabson { 234a9148abdSDoug Rabson int h = HASHSTEP(HASHINIT, repmsg->rm_xid) % REPLAY_HASH_SIZE; 235a9148abdSDoug Rabson struct replay_cache_entry *rce; 236a9148abdSDoug Rabson 237a9148abdSDoug Rabson /* 238a9148abdSDoug Rabson * Copy the reply before the lock so we can sleep. 239a9148abdSDoug Rabson */ 240a9148abdSDoug Rabson if (m) 241a9148abdSDoug Rabson m = m_copym(m, 0, M_COPYALL, M_WAITOK); 242a9148abdSDoug Rabson 243a9148abdSDoug Rabson mtx_lock(&rc->rc_lock); 244a9148abdSDoug Rabson TAILQ_FOREACH(rce, &rc->rc_cache[h], rce_link) { 245a9148abdSDoug Rabson if (rce->rce_msg.rm_xid == repmsg->rm_xid 246a9148abdSDoug Rabson && rce->rce_addr.ss_len == addr->sa_len 247a9148abdSDoug Rabson && bcmp(&rce->rce_addr, addr, addr->sa_len) == 0) { 248a9148abdSDoug Rabson break; 249a9148abdSDoug Rabson } 250a9148abdSDoug Rabson } 251a9148abdSDoug Rabson if (rce) { 252a9148abdSDoug Rabson rce->rce_repmsg = *repmsg; 253a9148abdSDoug Rabson rce->rce_repbody = m; 254a9148abdSDoug Rabson if (m) 255a9148abdSDoug Rabson rc->rc_size += m_length(m, NULL); 256a9148abdSDoug Rabson } 257a9148abdSDoug Rabson mtx_unlock(&rc->rc_lock); 258a9148abdSDoug Rabson } 259