static int gp_cleanup_delay;
 module_param(gp_cleanup_delay, int, 0444);
 
+/*
+ * This rcu parameter is runtime-read-only. It reflects
+ * a minimum allowed number of objects which can be cached
+ * per-CPU. Object size is equal to one page. This value
+ * can be changed at boot time.
+ */
+static int rcu_min_cached_objs = 2;
+module_param(rcu_min_cached_objs, int, 0444);
+
 /* Retrieve RCU kthreads priority for rcutorture */
 int rcu_get_gp_kthreads_prio(void)
 {
  * struct kfree_rcu_cpu - batch up kfree_rcu() requests for RCU grace period
  * @head: List of kfree_rcu() objects not yet waiting for a grace period
  * @bhead: Bulk-List of kfree_rcu() objects not yet waiting for a grace period
- * @bcached: Keeps at most one object for later reuse when build chain blocks
  * @krw_arr: Array of batches of kfree_rcu() objects waiting for a grace period
  * @lock: Synchronize access to this structure
  * @monitor_work: Promote @head to @head_free after KFREE_DRAIN_JIFFIES
 struct kfree_rcu_cpu {
        struct rcu_head *head;
        struct kfree_rcu_bulk_data *bhead;
-       struct kfree_rcu_bulk_data *bcached;
        struct kfree_rcu_cpu_work krw_arr[KFREE_N_BATCHES];
        raw_spinlock_t lock;
        struct delayed_work monitor_work;
        bool monitor_todo;
        bool initialized;
        int count;
+
+       /*
+        * A simple cache list that contains objects for
+        * reuse purpose. In order to save some per-cpu
+        * space the list is singular. Even though it is
+        * lockless an access has to be protected by the
+        * per-cpu lock.
+        */
+       struct llist_head bkvcache;
+       int nr_bkv_objs;
 };
 
 static DEFINE_PER_CPU(struct kfree_rcu_cpu, krc) = {
        local_irq_restore(flags);
 }
 
+static inline struct kfree_rcu_bulk_data *
+get_cached_bnode(struct kfree_rcu_cpu *krcp)
+{
+       if (!krcp->nr_bkv_objs)
+               return NULL;
+
+       krcp->nr_bkv_objs--;
+       return (struct kfree_rcu_bulk_data *)
+               llist_del_first(&krcp->bkvcache);
+}
+
+static inline bool
+put_cached_bnode(struct kfree_rcu_cpu *krcp,
+       struct kfree_rcu_bulk_data *bnode)
+{
+       // Check the limit.
+       if (krcp->nr_bkv_objs >= rcu_min_cached_objs)
+               return false;
+
+       llist_add((struct llist_node *) bnode, &krcp->bkvcache);
+       krcp->nr_bkv_objs++;
+       return true;
+
+}
+
 /*
  * This function is invoked in workqueue context after a grace period.
  * It frees all the objects queued on ->bhead_free or ->head_free.
                kfree_bulk(bhead->nr_records, bhead->records);
                rcu_lock_release(&rcu_callback_map);
 
-               if (cmpxchg(&krcp->bcached, NULL, bhead))
+               krcp = krc_this_cpu_lock(&flags);
+               if (put_cached_bnode(krcp, bhead))
+                       bhead = NULL;
+               krc_this_cpu_unlock(krcp, flags);
+
+               if (bhead)
                        free_page((unsigned long) bhead);
 
                cond_resched_tasks_rcu_qs();
        /* Check if a new block is required. */
        if (!krcp->bhead ||
                        krcp->bhead->nr_records == KFREE_BULK_MAX_ENTR) {
-               bnode = xchg(&krcp->bcached, NULL);
+               bnode = get_cached_bnode(krcp);
                if (!bnode) {
                        WARN_ON_ONCE(sizeof(struct kfree_rcu_bulk_data) > PAGE_SIZE);
 
 
        for_each_possible_cpu(cpu) {
                struct kfree_rcu_cpu *krcp = per_cpu_ptr(&krc, cpu);
+               struct kfree_rcu_bulk_data *bnode;
 
                for (i = 0; i < KFREE_N_BATCHES; i++) {
                        INIT_RCU_WORK(&krcp->krw_arr[i].rcu_work, kfree_rcu_work);
                        krcp->krw_arr[i].krcp = krcp;
                }
 
+               for (i = 0; i < rcu_min_cached_objs; i++) {
+                       bnode = (struct kfree_rcu_bulk_data *)
+                               __get_free_page(GFP_NOWAIT | __GFP_NOWARN);
+
+                       if (bnode)
+                               put_cached_bnode(krcp, bnode);
+                       else
+                               pr_err("Failed to preallocate for %d CPU!\n", cpu);
+               }
+
                INIT_DELAYED_WORK(&krcp->monitor_work, kfree_rcu_monitor);
                krcp->initialized = true;
        }