replay.c revision 184588
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: head/sys/rpc/replay.c 184588 2008-11-03 10:38:00Z dfr $"); 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 93184588Sdfr rc->rc_maxsize = newmaxsize; 94184588Sdfr replay_prune(rc); 95184588Sdfr} 96184588Sdfr 97184588Sdfrvoid 98184588Sdfrreplay_freecache(struct replay_cache *rc) 99184588Sdfr{ 100184588Sdfr 101184588Sdfr mtx_lock(&rc->rc_lock); 102184588Sdfr while (TAILQ_FIRST(&rc->rc_all)) 103184588Sdfr replay_free(rc, TAILQ_FIRST(&rc->rc_all)); 104184588Sdfr mtx_destroy(&rc->rc_lock); 105184588Sdfr free(rc, M_RPC); 106184588Sdfr} 107184588Sdfr 108184588Sdfrstatic struct replay_cache_entry * 109184588Sdfrreplay_alloc(struct replay_cache *rc, 110184588Sdfr struct rpc_msg *msg, struct sockaddr *addr, int h) 111184588Sdfr{ 112184588Sdfr struct replay_cache_entry *rce; 113184588Sdfr 114184588Sdfr rc->rc_count++; 115184588Sdfr rce = malloc(sizeof(*rce), M_RPC, M_NOWAIT|M_ZERO); 116184588Sdfr rce->rce_hash = h; 117184588Sdfr rce->rce_msg = *msg; 118184588Sdfr bcopy(addr, &rce->rce_addr, addr->sa_len); 119184588Sdfr 120184588Sdfr TAILQ_INSERT_HEAD(&rc->rc_cache[h], rce, rce_link); 121184588Sdfr TAILQ_INSERT_HEAD(&rc->rc_all, rce, rce_alllink); 122184588Sdfr 123184588Sdfr return (rce); 124184588Sdfr} 125184588Sdfr 126184588Sdfrstatic void 127184588Sdfrreplay_free(struct replay_cache *rc, struct replay_cache_entry *rce) 128184588Sdfr{ 129184588Sdfr 130184588Sdfr rc->rc_count--; 131184588Sdfr TAILQ_REMOVE(&rc->rc_cache[rce->rce_hash], rce, rce_link); 132184588Sdfr TAILQ_REMOVE(&rc->rc_all, rce, rce_alllink); 133184588Sdfr if (rce->rce_repbody) { 134184588Sdfr rc->rc_size -= m_length(rce->rce_repbody, NULL); 135184588Sdfr m_freem(rce->rce_repbody); 136184588Sdfr } 137184588Sdfr free(rce, M_RPC); 138184588Sdfr} 139184588Sdfr 140184588Sdfrstatic void 141184588Sdfrreplay_prune(struct replay_cache *rc) 142184588Sdfr{ 143184588Sdfr struct replay_cache_entry *rce; 144184588Sdfr bool_t freed_one; 145184588Sdfr 146184588Sdfr if (rc->rc_count >= REPLAY_MAX || rc->rc_size > rc->rc_maxsize) { 147184588Sdfr freed_one = FALSE; 148184588Sdfr do { 149184588Sdfr /* 150184588Sdfr * Try to free an entry. Don't free in-progress entries 151184588Sdfr */ 152184588Sdfr TAILQ_FOREACH_REVERSE(rce, &rc->rc_all, 153184588Sdfr replay_cache_list, rce_alllink) { 154184588Sdfr if (rce->rce_repmsg.rm_xid) { 155184588Sdfr replay_free(rc, rce); 156184588Sdfr freed_one = TRUE; 157184588Sdfr break; 158184588Sdfr } 159184588Sdfr } 160184588Sdfr } while (freed_one 161184588Sdfr && (rc->rc_count >= REPLAY_MAX 162184588Sdfr || rc->rc_size > rc->rc_maxsize)); 163184588Sdfr } 164184588Sdfr} 165184588Sdfr 166184588Sdfrenum replay_state 167184588Sdfrreplay_find(struct replay_cache *rc, struct rpc_msg *msg, 168184588Sdfr struct sockaddr *addr, struct rpc_msg *repmsg, struct mbuf **mp) 169184588Sdfr{ 170184588Sdfr int h = HASHSTEP(HASHINIT, msg->rm_xid) % REPLAY_HASH_SIZE; 171184588Sdfr struct replay_cache_entry *rce; 172184588Sdfr 173184588Sdfr mtx_lock(&rc->rc_lock); 174184588Sdfr TAILQ_FOREACH(rce, &rc->rc_cache[h], rce_link) { 175184588Sdfr if (rce->rce_msg.rm_xid == msg->rm_xid 176184588Sdfr && rce->rce_msg.rm_call.cb_prog == msg->rm_call.cb_prog 177184588Sdfr && rce->rce_msg.rm_call.cb_vers == msg->rm_call.cb_vers 178184588Sdfr && rce->rce_msg.rm_call.cb_proc == msg->rm_call.cb_proc 179184588Sdfr && rce->rce_addr.ss_len == addr->sa_len 180184588Sdfr && bcmp(&rce->rce_addr, addr, addr->sa_len) == 0) { 181184588Sdfr if (rce->rce_repmsg.rm_xid) { 182184588Sdfr /* 183184588Sdfr * We have a reply for this 184184588Sdfr * message. Copy it and return. Keep 185184588Sdfr * replay_all LRU sorted 186184588Sdfr */ 187184588Sdfr TAILQ_REMOVE(&rc->rc_all, rce, rce_alllink); 188184588Sdfr TAILQ_INSERT_HEAD(&rc->rc_all, rce, 189184588Sdfr rce_alllink); 190184588Sdfr *repmsg = rce->rce_repmsg; 191184588Sdfr if (rce->rce_repbody) { 192184588Sdfr *mp = m_copym(rce->rce_repbody, 193184588Sdfr 0, M_COPYALL, M_NOWAIT); 194184588Sdfr mtx_unlock(&rc->rc_lock); 195184588Sdfr if (!*mp) 196184588Sdfr return (RS_ERROR); 197184588Sdfr } else { 198184588Sdfr mtx_unlock(&rc->rc_lock); 199184588Sdfr } 200184588Sdfr return (RS_DONE); 201184588Sdfr } else { 202184588Sdfr mtx_unlock(&rc->rc_lock); 203184588Sdfr return (RS_INPROGRESS); 204184588Sdfr } 205184588Sdfr } 206184588Sdfr } 207184588Sdfr 208184588Sdfr replay_prune(rc); 209184588Sdfr 210184588Sdfr rce = replay_alloc(rc, msg, addr, h); 211184588Sdfr 212184588Sdfr mtx_unlock(&rc->rc_lock); 213184588Sdfr 214184588Sdfr if (!rce) 215184588Sdfr return (RS_ERROR); 216184588Sdfr else 217184588Sdfr return (RS_NEW); 218184588Sdfr} 219184588Sdfr 220184588Sdfrvoid 221184588Sdfrreplay_setreply(struct replay_cache *rc, 222184588Sdfr struct rpc_msg *repmsg, struct sockaddr *addr, struct mbuf *m) 223184588Sdfr{ 224184588Sdfr int h = HASHSTEP(HASHINIT, repmsg->rm_xid) % REPLAY_HASH_SIZE; 225184588Sdfr struct replay_cache_entry *rce; 226184588Sdfr 227184588Sdfr /* 228184588Sdfr * Copy the reply before the lock so we can sleep. 229184588Sdfr */ 230184588Sdfr if (m) 231184588Sdfr m = m_copym(m, 0, M_COPYALL, M_WAITOK); 232184588Sdfr 233184588Sdfr mtx_lock(&rc->rc_lock); 234184588Sdfr TAILQ_FOREACH(rce, &rc->rc_cache[h], rce_link) { 235184588Sdfr if (rce->rce_msg.rm_xid == repmsg->rm_xid 236184588Sdfr && rce->rce_addr.ss_len == addr->sa_len 237184588Sdfr && bcmp(&rce->rce_addr, addr, addr->sa_len) == 0) { 238184588Sdfr break; 239184588Sdfr } 240184588Sdfr } 241184588Sdfr if (rce) { 242184588Sdfr rce->rce_repmsg = *repmsg; 243184588Sdfr rce->rce_repbody = m; 244184588Sdfr if (m) 245184588Sdfr rc->rc_size += m_length(m, NULL); 246184588Sdfr } 247184588Sdfr mtx_unlock(&rc->rc_lock); 248184588Sdfr} 249