diff --git a/lustre/ChangeLog b/lustre/ChangeLog
index d281ffde65a5e484b97081ac47d7c5a4830389e8..02d8d730869e1268a591a322885226a83ef7cf68 100644
--- a/lustre/ChangeLog
+++ b/lustre/ChangeLog
@@ -12,6 +12,10 @@ tbd  Sun Microsystems, Inc.
        * RHEL 4 and RHEL 5/SLES 10 clients behaves differently on 'cd' to a
         removed cwd "./" (refer to Bugzilla 14399).
 
+Severity   : normal
+Bugzilla   : 15210
+Description: add recount protection for osc callbacks, so avoid panic on shutdown
+
 Severity   : normal
 Bugzilla   : 12653
 Description: sanity test 65a fails if stripecount of -1 is set
diff --git a/lustre/include/lustre_cache.h b/lustre/include/lustre_cache.h
index 291d88293675b7ccefe54c20424aad5fbf8c4f29..d5a5337bd2ee5aa49d70c8c54a7ff43f0315baa9 100644
--- a/lustre/include/lustre_cache.h
+++ b/lustre/include/lustre_cache.h
@@ -13,6 +13,7 @@ struct osc_async_page;
 struct page_removal_cb_element {
         struct list_head        prce_list;
         obd_page_removal_cb_t   prce_callback;
+        atomic_t                prce_refcnt;
 };
 
 typedef int (*cache_iterate_extents_cb_t)(struct lustre_cache *,
@@ -27,6 +28,7 @@ struct lustre_cache {
         struct list_head         lc_locks_list;
         spinlock_t               lc_locks_list_lock;
         struct list_head         lc_page_removal_callback_list;
+        rwlock_t                 lc_page_removal_cb_lock; /* iterate vs modify list */
         struct obd_device       *lc_obd;
         obd_pin_extent_cb        lc_pin_extent_cb;
 };
diff --git a/lustre/osc/cache.c b/lustre/osc/cache.c
index 3dc93fb72f7d93d37a2ff6addca5490a71b7592f..25869743c01665c6c9813acbbd7eeda95b233734 100644
--- a/lustre/osc/cache.c
+++ b/lustre/osc/cache.c
@@ -138,16 +138,39 @@ int cache_add_extent(struct lustre_cache *cache, struct ldlm_res_id *res,
         RETURN(0);
 }
 
+static void cache_extent_removal_get(struct page_removal_cb_element *element)
+{
+        atomic_inc(&element->prce_refcnt);
+}
+
+static void cache_extent_removal_put(struct page_removal_cb_element *element)
+{
+        if(atomic_dec_and_test(&element->prce_refcnt))
+                OBD_FREE_PTR(element);
+}
+
 static int cache_extent_removal_event(struct lustre_cache *cache,
                                       void *data, int discard)
 {
         struct page *page = data;
+        struct list_head *iter;
         struct page_removal_cb_element *element;
 
-        list_for_each_entry(element, &cache->lc_page_removal_callback_list,
-                            prce_list) {
+        read_lock(&cache->lc_page_removal_cb_lock);
+        iter = cache->lc_page_removal_callback_list.next;
+        while(iter != &cache->lc_page_removal_callback_list) {
+                element = list_entry(iter, struct page_removal_cb_element, prce_list);
+                cache_extent_removal_get(element);
+                read_unlock(&cache->lc_page_removal_cb_lock);
+
                 element->prce_callback(page, discard);
+
+                read_lock(&cache->lc_page_removal_cb_lock);
+                iter = iter->next;
+                cache_extent_removal_put(element);
         }
+        read_unlock(&cache->lc_page_removal_cb_lock);
+
         return 0;
 }
 
@@ -167,12 +190,17 @@ int cache_add_extent_removal_cb(struct lustre_cache *cache,
 
         if (!func_cb)
                 return 0;
-        OBD_ALLOC(element, sizeof(*element));
+
+        OBD_ALLOC_PTR(element);
         if (!element)
                 return -ENOMEM;
         element->prce_callback = func_cb;
+        atomic_set(&element->prce_refcnt, 1);
+
+        write_lock(&cache->lc_page_removal_cb_lock);
         list_add_tail(&element->prce_list,
                       &cache->lc_page_removal_callback_list);
+        write_unlock(&cache->lc_page_removal_cb_lock);
 
         cache->lc_pin_extent_cb = pin_cb;
         return 0;
@@ -188,17 +216,21 @@ int cache_del_extent_removal_cb(struct lustre_cache *cache,
         int found = 0;
         struct page_removal_cb_element *element, *t;
 
+        write_lock(&cache->lc_page_removal_cb_lock);
         list_for_each_entry_safe(element, t,
                                  &cache->lc_page_removal_callback_list,
                                  prce_list) {
                 if (element->prce_callback == func_cb) {
                         list_del(&element->prce_list);
-                        OBD_FREE(element, sizeof(*element));
+                        write_unlock(&cache->lc_page_removal_cb_lock);
                         found = 1;
+                        cache_extent_removal_put(element);
+                        write_lock(&cache->lc_page_removal_cb_lock);
                         /* We continue iterating the list in case this function
                            was registered more than once */
                 }
         }
+        write_unlock(&cache->lc_page_removal_cb_lock);
 
         if (list_empty(&cache->lc_page_removal_callback_list))
                 cache->lc_pin_extent_cb = NULL;
@@ -358,6 +390,7 @@ struct lustre_cache *cache_create(struct obd_device *obd)
         spin_lock_init(&cache->lc_locks_list_lock);
         CFS_INIT_LIST_HEAD(&cache->lc_locks_list);
         CFS_INIT_LIST_HEAD(&cache->lc_page_removal_callback_list);
+        rwlock_init(&cache->lc_page_removal_cb_lock);
         cache->lc_obd = obd;
 
       out: