Commit 7c02dc94 authored by David Howells's avatar David Howells

rxrpc: Add a common object cache

Add a common object cache implementation for RxRPC.  This will be used to
cache objects of various types (calls, connections, local and remote
endpoint records).  Each object that would be cached must contain an
obj_node struct for the cache to use.  The object's usage count and link
pointers are here, plus other internal metadata.

Each object cache consists of a primary hash to which all objects of that
type must be added and a secondary hash to which objects may also be added
and removed a single time.  Objects are automatically removed from both
hashes when they expire.

Objects start off life with a usage count of 2 - one for the cache and one
for the caller.  When an object's usage count is reduced to 1, it sits in
the cache until its expiry time is reached, at which point the cache
attempts to reduce the count to 0 and, if successful, clean it up.  An
object with a usage count of 1 in the cache can be looked up and have its
usage count increased, thereby stopping the expiry process.

Objects are looked up, unlinked and destroyed under RCU-safe conditions.

A garbage collector cycles through all the hash buckets in the primary hash
and compares the expiry times of the usage-count-1 objects to the current
time, removing any that have expired.  This is kicked by a single timer for
the whole cache rather than having a timer per object.
Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
parent e74a386d
......@@ -18,7 +18,8 @@ af-rxrpc-y := \
ar-recvmsg.o \
ar-security.o \
ar-skbuff.o \
ar-transport.o
ar-transport.o \
objcache.o
af-rxrpc-$(CONFIG_PROC_FS) += ar-proc.o
af-rxrpc-$(CONFIG_SYSCTL) += sysctl.o
......
......@@ -9,7 +9,11 @@
* 2 of the License, or (at your option) any later version.
*/
#include <linux/net.h>
#include <net/sock.h>
#include <net/af_rxrpc.h>
#include <rxrpc/packet.h>
#include "objcache.h"
#if 0
#define CHECK_SLAB_OKAY(X) \
......@@ -467,6 +471,41 @@ extern atomic_t rxrpc_n_skbs;
extern u32 rxrpc_epoch;
extern atomic_t rxrpc_debug_id;
extern struct workqueue_struct *rxrpc_workqueue;
static inline void __rxrpc_queue_obj(struct work_struct *work,
struct objcache *cache,
struct obj_node *obj)
{
/* Pass the caller's ref to the workqueue or drop it if already
* queued.
*/
if (!queue_work(rxrpc_workqueue, work))
objcache_put(cache, obj);
}
static inline void rxrpc_queue_obj(struct work_struct *work,
struct objcache *cache,
struct obj_node *obj)
{
/* We don't want to queue the work item if the object is dead - but
* whilst we want to avoid calling objcache_put(), we really, really
* want to avoid calling cancel_sync_wait() or flush_workqueue().
*
* There is, however, a gap between calling queue_work() and doing
* something conditionally on its result that would allow the work item
* to be happen if we get interrupted - so we can't just increment the
* usage count if we queued the work and decrement it in the work func
* as the work func might decrement it *before* we manage to increment
* it here.
*
* So we have to attempt to increment the count before trying the queue
* operation and then correct afterwards if the work was already
* queued.
*/
if (objcache_get_maybe(obj) &&
!queue_work(rxrpc_workqueue, work))
objcache_put(cache, obj);
}
/*
* ar-accept.c
......
This diff is collapsed.
/* Common object cache definitions
*
* Copyright (C) 2015 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public Licence
* as published by the Free Software Foundation; either version
* 2 of the Licence, or (at your option) any later version.
*/
#ifndef _OBJCACHE_H
#define _OBJCACHE_H
#include <linux/rculist.h>
#include <linux/workqueue.h>
#include <linux/timer.h>
#include <linux/seq_file.h>
struct seq_file;
struct obj_node {
union {
struct rcu_head rcu;
struct list_head gc_link;
};
struct hlist_node link;
struct hlist_node link_2;
unsigned long full_hash_key;
time64_t put_timestamp;
atomic_t usage;
};
struct objcache {
/* Parameters that must be set before initialisation */
const char *name;
void (*prepare_for_gc)(struct obj_node *);
void (*gc_rcu)(struct rcu_head *);
unsigned long (*hash_key)(const void *);
int (*cmp_key)(const struct obj_node *, const void *);
struct hlist_head *hash_table;
unsigned gc_delay;
u16 nr_buckets;
/* Secondary hash parameters if we want one - also must be set before
* initialisation. Note that the secondary hash doesn't store its full
* hash key in the obj_node struct.
*/
u16 nr_buckets_2;
struct hlist_head *hash_table_2;
unsigned long (*hash_key_2)(const void *);
int (*cmp_key_2)(const struct obj_node *, const void *);
/* Internal data */
spinlock_t lock;
atomic_t count;
u8 shift;
u8 shift_2;
bool gc_needed;
bool gc_clear_all;
struct work_struct gc_work;
struct timer_list gc_timer;
time64_t gc_next_run;
unsigned gc_bucket;
unsigned gc_last_bucket;
};
static inline bool objcache_get_maybe(struct obj_node *obj)
{
return atomic_inc_not_zero(&obj->usage);
}
static inline void objcache_get(struct obj_node *obj)
{
atomic_inc(&obj->usage);
}
extern void objcache_init(struct objcache *);
extern struct obj_node *objcache_try_add(struct objcache *, struct obj_node *, const void *);
extern struct obj_node *objcache_lookup_rcu(struct objcache *, const void *);
extern bool objcache_add_2(struct objcache *, struct obj_node *, const void *, bool);
extern void objcache_del_2(struct objcache *, struct obj_node *);
extern struct obj_node *objcache_lookup_rcu_2(struct objcache *, const void *);
extern void objcache_put(struct objcache *, struct obj_node *);
extern void objcache_obj_rcu_done(struct objcache *);
extern void objcache_clear(struct objcache *);
#endif /* _OBJCACHE_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment