- Fix infinite loop in replay_prune(). When at least one entry was freed we were looping, because freed_one was never set back to FALSE. - Add missing locking in replay_setsize(). - Check the result of malloc(M_NOWAIT) in replay_alloc(). The called (replay_alloc()) knows how to handle replay_alloc() failure. - Eliminate 'freed_one' variable, it is not needed - when no entry is found rce will be NULL. - Add locking assertions where we expect a rc_lock to be held. Index: sys/rpc/replay.c =================================================================== --- sys/rpc/replay.c (wersja 211283) +++ sys/rpc/replay.c (kopia robocza) @@ -90,8 +90,10 @@ replay_setsize(struct replay_cache *rc, size_t newmaxsize) { + mtx_lock(&rc->rc_lock); rc->rc_maxsize = newmaxsize; replay_prune(rc); + mtx_unlock(&rc->rc_lock); } void @@ -111,8 +113,12 @@ { struct replay_cache_entry *rce; + mtx_assert(&rc->rc_lock, MA_OWNED); + rc->rc_count++; rce = malloc(sizeof(*rce), M_RPC, M_NOWAIT|M_ZERO); + if (!rce) + return (NULL); rce->rce_hash = h; rce->rce_msg = *msg; bcopy(addr, &rce->rce_addr, addr->sa_len); @@ -127,6 +133,8 @@ replay_free(struct replay_cache *rc, struct replay_cache_entry *rce) { + mtx_assert(&rc->rc_lock, MA_OWNED); + rc->rc_count--; TAILQ_REMOVE(&rc->rc_cache[rce->rce_hash], rce, rce_link); TAILQ_REMOVE(&rc->rc_all, rce, rce_alllink); @@ -141,26 +149,25 @@ replay_prune(struct replay_cache *rc) { struct replay_cache_entry *rce; - bool_t freed_one; - if (rc->rc_count >= REPLAY_MAX || rc->rc_size > rc->rc_maxsize) { - freed_one = FALSE; - do { - /* - * Try to free an entry. Don't free in-progress entries - */ - TAILQ_FOREACH_REVERSE(rce, &rc->rc_all, - replay_cache_list, rce_alllink) { - if (rce->rce_repmsg.rm_xid) { - replay_free(rc, rce); - freed_one = TRUE; - break; - } - } - } while (freed_one - && (rc->rc_count >= REPLAY_MAX - || rc->rc_size > rc->rc_maxsize)); - } + mtx_assert(&rc->rc_lock, MA_OWNED); + + if (rc->rc_count < REPLAY_MAX && rc->rc_size <= rc->rc_maxsize) + return; + + do { + /* + * Try to free an entry. Don't free in-progress entries. + */ + TAILQ_FOREACH_REVERSE(rce, &rc->rc_all, replay_cache_list, + rce_alllink) { + if (rce->rce_repmsg.rm_xid) + break; + } + if (rce) + replay_free(rc, rce); + } while (rce && (rc->rc_count >= REPLAY_MAX + || rc->rc_size > rc->rc_maxsize)); } enum replay_state