1184588Sdfr/*- 2184588Sdfr * Copyright (c) 2008 Isilon Inc http://www.isilon.com/ 3184588Sdfr * Authors: Doug Rabson <dfr@rabson.org> 4184588Sdfr * Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org> 5184588Sdfr * 6184588Sdfr * Redistribution and use in source and binary forms, with or without 7184588Sdfr * modification, are permitted provided that the following conditions 8184588Sdfr * are met: 9184588Sdfr * 1. Redistributions of source code must retain the above copyright 10184588Sdfr * notice, this list of conditions and the following disclaimer. 11184588Sdfr * 2. Redistributions in binary form must reproduce the above copyright 12184588Sdfr * notice, this list of conditions and the following disclaimer in the 13184588Sdfr * documentation and/or other materials provided with the distribution. 14184588Sdfr * 15184588Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16184588Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17184588Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18184588Sdfr * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19184588Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20184588Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21184588Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22184588Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23184588Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24184588Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25184588Sdfr * SUCH DAMAGE. 26184588Sdfr */ 27184588Sdfr 28184588Sdfr#include <sys/cdefs.h> 29184588Sdfr__FBSDID("$FreeBSD$"); 30184588Sdfr 31184588Sdfr#include <sys/param.h> 32184588Sdfr#include <sys/hash.h> 33184588Sdfr#include <sys/kernel.h> 34184588Sdfr#include <sys/lock.h> 35184588Sdfr#include <sys/mbuf.h> 36184588Sdfr#include <sys/mutex.h> 37184588Sdfr#include <sys/queue.h> 38184588Sdfr 39184588Sdfr#include <rpc/rpc.h> 40184588Sdfr#include <rpc/replay.h> 41184588Sdfr 42184588Sdfrstruct replay_cache_entry { 43184588Sdfr int rce_hash; 44184588Sdfr struct rpc_msg rce_msg; 45184588Sdfr struct sockaddr_storage rce_addr; 46184588Sdfr struct rpc_msg rce_repmsg; 47184588Sdfr struct mbuf *rce_repbody; 48184588Sdfr 49184588Sdfr TAILQ_ENTRY(replay_cache_entry) rce_link; 50184588Sdfr TAILQ_ENTRY(replay_cache_entry) rce_alllink; 51184588Sdfr}; 52184588SdfrTAILQ_HEAD(replay_cache_list, replay_cache_entry); 53184588Sdfr 54184588Sdfrstatic struct replay_cache_entry * 55184588Sdfr replay_alloc(struct replay_cache *rc, struct rpc_msg *msg, 56184588Sdfr struct sockaddr *addr, int h); 57184588Sdfrstatic void replay_free(struct replay_cache *rc, 58184588Sdfr struct replay_cache_entry *rce); 59184588Sdfrstatic void replay_prune(struct replay_cache *rc); 60184588Sdfr 61184588Sdfr#define REPLAY_HASH_SIZE 256 62184588Sdfr#define REPLAY_MAX 1024 63184588Sdfr 64184588Sdfrstruct replay_cache { 65184588Sdfr struct replay_cache_list rc_cache[REPLAY_HASH_SIZE]; 66184588Sdfr struct replay_cache_list rc_all; 67184588Sdfr struct mtx rc_lock; 68184588Sdfr int rc_count; 69184588Sdfr size_t rc_size; 70184588Sdfr size_t rc_maxsize; 71184588Sdfr}; 72184588Sdfr 73184588Sdfrstruct replay_cache * 74184588Sdfrreplay_newcache(size_t maxsize) 75184588Sdfr{ 76184588Sdfr struct replay_cache *rc; 77184588Sdfr int i; 78184588Sdfr 79184588Sdfr rc = malloc(sizeof(*rc), M_RPC, M_WAITOK|M_ZERO); 80184588Sdfr for (i = 0; i < REPLAY_HASH_SIZE; i++) 81184588Sdfr TAILQ_INIT(&rc->rc_cache[i]); 82184588Sdfr TAILQ_INIT(&rc->rc_all); 83184588Sdfr mtx_init(&rc->rc_lock, "rc_lock", NULL, MTX_DEF); 84184588Sdfr rc->rc_maxsize = maxsize; 85184588Sdfr 86184588Sdfr return (rc); 87184588Sdfr} 88184588Sdfr 89184588Sdfrvoid 90184588Sdfrreplay_setsize(struct replay_cache *rc, size_t newmaxsize) 91184588Sdfr{ 92184588Sdfr 93211830Srmacklem mtx_lock(&rc->rc_lock); 94184588Sdfr rc->rc_maxsize = newmaxsize; 95184588Sdfr replay_prune(rc); 96211830Srmacklem mtx_unlock(&rc->rc_lock); 97184588Sdfr} 98184588Sdfr 99184588Sdfrvoid 100184588Sdfrreplay_freecache(struct replay_cache *rc) 101184588Sdfr{ 102184588Sdfr 103184588Sdfr mtx_lock(&rc->rc_lock); 104184588Sdfr while (TAILQ_FIRST(&rc->rc_all)) 105184588Sdfr replay_free(rc, TAILQ_FIRST(&rc->rc_all)); 106184588Sdfr mtx_destroy(&rc->rc_lock); 107184588Sdfr free(rc, M_RPC); 108184588Sdfr} 109184588Sdfr 110184588Sdfrstatic struct replay_cache_entry * 111184588Sdfrreplay_alloc(struct replay_cache *rc, 112184588Sdfr struct rpc_msg *msg, struct sockaddr *addr, int h) 113184588Sdfr{ 114184588Sdfr struct replay_cache_entry *rce; 115184588Sdfr 116211853Spjd mtx_assert(&rc->rc_lock, MA_OWNED); 117211853Spjd 118184588Sdfr rc->rc_count++; 119184588Sdfr rce = malloc(sizeof(*rce), M_RPC, M_NOWAIT|M_ZERO); 120211853Spjd if (!rce) 121211853Spjd return (NULL); 122184588Sdfr rce->rce_hash = h; 123184588Sdfr rce->rce_msg = *msg; 124184588Sdfr bcopy(addr, &rce->rce_addr, addr->sa_len); 125184588Sdfr 126184588Sdfr TAILQ_INSERT_HEAD(&rc->rc_cache[h], rce, rce_link); 127184588Sdfr TAILQ_INSERT_HEAD(&rc->rc_all, rce, rce_alllink); 128184588Sdfr 129184588Sdfr return (rce); 130184588Sdfr} 131184588Sdfr 132184588Sdfrstatic void 133184588Sdfrreplay_free(struct replay_cache *rc, struct replay_cache_entry *rce) 134184588Sdfr{ 135184588Sdfr 136211853Spjd mtx_assert(&rc->rc_lock, MA_OWNED); 137211853Spjd 138184588Sdfr rc->rc_count--; 139184588Sdfr TAILQ_REMOVE(&rc->rc_cache[rce->rce_hash], rce, rce_link); 140184588Sdfr TAILQ_REMOVE(&rc->rc_all, rce, rce_alllink); 141184588Sdfr if (rce->rce_repbody) { 142184588Sdfr rc->rc_size -= m_length(rce->rce_repbody, NULL); 143184588Sdfr m_freem(rce->rce_repbody); 144184588Sdfr } 145184588Sdfr free(rce, M_RPC); 146184588Sdfr} 147184588Sdfr 148184588Sdfrstatic void 149184588Sdfrreplay_prune(struct replay_cache *rc) 150184588Sdfr{ 151184588Sdfr struct replay_cache_entry *rce; 152184588Sdfr 153211853Spjd mtx_assert(&rc->rc_lock, MA_OWNED); 154211853Spjd 155211853Spjd if (rc->rc_count < REPLAY_MAX && rc->rc_size <= rc->rc_maxsize) 156211853Spjd return; 157211853Spjd 158211853Spjd do { 159211853Spjd /* 160211853Spjd * Try to free an entry. Don't free in-progress entries. 161211853Spjd */ 162211853Spjd TAILQ_FOREACH_REVERSE(rce, &rc->rc_all, replay_cache_list, 163211853Spjd rce_alllink) { 164211853Spjd if (rce->rce_repmsg.rm_xid) 165211853Spjd break; 166211853Spjd } 167211853Spjd if (rce) 168211853Spjd replay_free(rc, rce); 169211853Spjd } while (rce && (rc->rc_count >= REPLAY_MAX 170211853Spjd || rc->rc_size > rc->rc_maxsize)); 171184588Sdfr} 172184588Sdfr 173184588Sdfrenum replay_state 174184588Sdfrreplay_find(struct replay_cache *rc, struct rpc_msg *msg, 175184588Sdfr struct sockaddr *addr, struct rpc_msg *repmsg, struct mbuf **mp) 176184588Sdfr{ 177184588Sdfr int h = HASHSTEP(HASHINIT, msg->rm_xid) % REPLAY_HASH_SIZE; 178184588Sdfr struct replay_cache_entry *rce; 179184588Sdfr 180184588Sdfr mtx_lock(&rc->rc_lock); 181184588Sdfr TAILQ_FOREACH(rce, &rc->rc_cache[h], rce_link) { 182184588Sdfr if (rce->rce_msg.rm_xid == msg->rm_xid 183184588Sdfr && rce->rce_msg.rm_call.cb_prog == msg->rm_call.cb_prog 184184588Sdfr && rce->rce_msg.rm_call.cb_vers == msg->rm_call.cb_vers 185184588Sdfr && rce->rce_msg.rm_call.cb_proc == msg->rm_call.cb_proc 186184588Sdfr && rce->rce_addr.ss_len == addr->sa_len 187184588Sdfr && bcmp(&rce->rce_addr, addr, addr->sa_len) == 0) { 188184588Sdfr if (rce->rce_repmsg.rm_xid) { 189184588Sdfr /* 190184588Sdfr * We have a reply for this 191184588Sdfr * message. Copy it and return. Keep 192184588Sdfr * replay_all LRU sorted 193184588Sdfr */ 194184588Sdfr TAILQ_REMOVE(&rc->rc_all, rce, rce_alllink); 195184588Sdfr TAILQ_INSERT_HEAD(&rc->rc_all, rce, 196184588Sdfr rce_alllink); 197184588Sdfr *repmsg = rce->rce_repmsg; 198184588Sdfr if (rce->rce_repbody) { 199184588Sdfr *mp = m_copym(rce->rce_repbody, 200184588Sdfr 0, M_COPYALL, M_NOWAIT); 201184588Sdfr mtx_unlock(&rc->rc_lock); 202184588Sdfr if (!*mp) 203184588Sdfr return (RS_ERROR); 204184588Sdfr } else { 205184588Sdfr mtx_unlock(&rc->rc_lock); 206184588Sdfr } 207184588Sdfr return (RS_DONE); 208184588Sdfr } else { 209184588Sdfr mtx_unlock(&rc->rc_lock); 210184588Sdfr return (RS_INPROGRESS); 211184588Sdfr } 212184588Sdfr } 213184588Sdfr } 214184588Sdfr 215184588Sdfr replay_prune(rc); 216184588Sdfr 217184588Sdfr rce = replay_alloc(rc, msg, addr, h); 218184588Sdfr 219184588Sdfr mtx_unlock(&rc->rc_lock); 220184588Sdfr 221184588Sdfr if (!rce) 222184588Sdfr return (RS_ERROR); 223184588Sdfr else 224184588Sdfr return (RS_NEW); 225184588Sdfr} 226184588Sdfr 227184588Sdfrvoid 228184588Sdfrreplay_setreply(struct replay_cache *rc, 229184588Sdfr struct rpc_msg *repmsg, struct sockaddr *addr, struct mbuf *m) 230184588Sdfr{ 231184588Sdfr int h = HASHSTEP(HASHINIT, repmsg->rm_xid) % REPLAY_HASH_SIZE; 232184588Sdfr struct replay_cache_entry *rce; 233184588Sdfr 234184588Sdfr /* 235184588Sdfr * Copy the reply before the lock so we can sleep. 236184588Sdfr */ 237184588Sdfr if (m) 238184588Sdfr m = m_copym(m, 0, M_COPYALL, M_WAITOK); 239184588Sdfr 240184588Sdfr mtx_lock(&rc->rc_lock); 241184588Sdfr TAILQ_FOREACH(rce, &rc->rc_cache[h], rce_link) { 242184588Sdfr if (rce->rce_msg.rm_xid == repmsg->rm_xid 243184588Sdfr && rce->rce_addr.ss_len == addr->sa_len 244184588Sdfr && bcmp(&rce->rce_addr, addr, addr->sa_len) == 0) { 245184588Sdfr break; 246184588Sdfr } 247184588Sdfr } 248184588Sdfr if (rce) { 249184588Sdfr rce->rce_repmsg = *repmsg; 250184588Sdfr rce->rce_repbody = m; 251184588Sdfr if (m) 252184588Sdfr rc->rc_size += m_length(m, NULL); 253184588Sdfr } 254184588Sdfr mtx_unlock(&rc->rc_lock); 255184588Sdfr} 256