diff --git a/lustre/include/obd_class.h b/lustre/include/obd_class.h
index cfb217eab271236c5413867dbd2f70471ca21e4b..19bf151fd070dae3ecab45407408db1a24225c8e 100644
--- a/lustre/include/obd_class.h
+++ b/lustre/include/obd_class.h
@@ -923,15 +923,12 @@ static inline int obd_brw_rqset(int cmd, struct obd_export *exp,
 {
         struct ptlrpc_request_set *set = NULL;
         struct obd_info oinfo = { { { 0 } } };
-        atomic_t nob;
         int rc = 0;
         ENTRY;
 
         set =  ptlrpc_prep_set();
         if (set == NULL)
                 RETURN(-ENOMEM);
-        atomic_set(&nob, 0);
-        set->set_countp = &nob;
 
         oinfo.oi_oa = oa;
         oinfo.oi_md = lsm;
@@ -940,8 +937,6 @@ static inline int obd_brw_rqset(int cmd, struct obd_export *exp,
                 rc = ptlrpc_set_wait(set);
                 if (rc)
                         CERROR("error from callback: rc = %d\n", rc);
-                else
-                        rc = atomic_read(&nob);
         } else {
                 CDEBUG(rc == -ENOSPC ? D_INODE : D_ERROR,
                        "error from obd_brw_async: rc = %d\n", rc);
diff --git a/lustre/osc/osc_request.c b/lustre/osc/osc_request.c
index 381631cadffbb34876b79d97c3da31b9694073a9..8f16dff6dd20e003814633e428b5f5cf0a302935 100644
--- a/lustre/osc/osc_request.c
+++ b/lustre/osc/osc_request.c
@@ -1364,8 +1364,6 @@ static int brw_interpret(struct ptlrpc_request *request, void *data, int rc)
                 if (rc == 0)
                         RETURN(0);
         }
-        if ((rc >= 0) && request->rq_set && request->rq_set->set_countp)
-                atomic_add(nob, (atomic_t *)request->rq_set->set_countp);
         client_obd_list_lock(&aa->aa_cli->cl_loi_list_lock);
         if (lustre_msg_get_opc(request->rq_reqmsg) == OST_WRITE)
                 aa->aa_cli->cl_w_in_flight--;
diff --git a/lustre/ptlrpc/client.c b/lustre/ptlrpc/client.c
index c182672cf12ab97791c53c8308e485f666351abe..00a7f1949b33a1a644aa33a6b66ce5cb2752bceb 100644
--- a/lustre/ptlrpc/client.c
+++ b/lustre/ptlrpc/client.c
@@ -573,7 +573,8 @@ struct ptlrpc_request_set *ptlrpc_prep_set(void)
         set->set_remaining = 0;
         spin_lock_init(&set->set_new_req_lock);
         CFS_INIT_LIST_HEAD(&set->set_new_requests);
-
+        CFS_INIT_LIST_HEAD(&set->set_cblist);
+        
         RETURN(set);
 }
 
@@ -632,6 +633,23 @@ void ptlrpc_set_destroy(struct ptlrpc_request_set *set)
         EXIT;
 }
 
+int ptlrpc_set_add_cb(struct ptlrpc_request_set *set,
+                      set_interpreter_func fn, void *data)
+{
+        struct ptlrpc_set_cbdata *cbdata;
+
+        OBD_SLAB_ALLOC(cbdata, ptlrpc_cbdata_slab,
+                        CFS_ALLOC_STD, sizeof(*cbdata));
+        if (cbdata == NULL)
+                RETURN(-ENOMEM);
+
+        cbdata->psc_interpret = fn;
+        cbdata->psc_data = data;
+        list_add_tail(&cbdata->psc_item, &set->set_cblist);
+
+        RETURN(0);
+}
+
 void ptlrpc_set_add_req(struct ptlrpc_request_set *set,
                         struct ptlrpc_request *req)
 {
@@ -1375,6 +1393,19 @@ int ptlrpc_set_wait(struct ptlrpc_request_set *set)
                 int (*interpreter)(struct ptlrpc_request_set *set,void *,int) =
                         set->set_interpret;
                 rc = interpreter (set, set->set_arg, rc);
+        } else {
+                struct ptlrpc_set_cbdata *cbdata, *n;
+                int err;
+
+                list_for_each_entry_safe(cbdata, n,
+                                         &set->set_cblist, psc_item) {
+                        list_del_init(&cbdata->psc_item);
+                        err = cbdata->psc_interpret(set, cbdata->psc_data, rc);
+                        if (err && !rc)
+                                rc = err;
+                        OBD_SLAB_FREE(cbdata, ptlrpc_cbdata_slab,
+                                        sizeof(*cbdata));
+                }
         }
 
         RETURN(rc);
diff --git a/lustre/ptlrpc/ptlrpc_internal.h b/lustre/ptlrpc/ptlrpc_internal.h
index f427fbe0293f42fa93980e29be9e49f8e32d9118..a769aa50605a2aea63898e2f4ae8a16c378a4f22 100644
--- a/lustre/ptlrpc/ptlrpc_internal.h
+++ b/lustre/ptlrpc/ptlrpc_internal.h
@@ -35,6 +35,7 @@ struct obd_import;
 struct ldlm_res_id;
 struct ptlrpc_request_set;
 extern int test_req_buffer_pressure;
+extern cfs_mem_cache_t *ptlrpc_cbdata_slab;
 
 void ptlrpc_request_handle_notconn(struct ptlrpc_request *);
 void lustre_assert_wire_constants(void);
diff --git a/lustre/ptlrpc/ptlrpc_module.c b/lustre/ptlrpc/ptlrpc_module.c
index 66d51b9f8671dd84af0397fb359d6d5e133d48e0..889aa2d3959a1c1b89fecfa742c4a8e1335fe6b3 100644
--- a/lustre/ptlrpc/ptlrpc_module.c
+++ b/lustre/ptlrpc/ptlrpc_module.c
@@ -38,6 +38,7 @@
 
 #include "ptlrpc_internal.h"
 
+cfs_mem_cache_t *ptlrpc_cbdata_slab;
 extern spinlock_t ptlrpc_last_xid_lock;
 extern spinlock_t ptlrpc_rs_debug_lock;
 extern spinlock_t ptlrpc_all_services_lock;
@@ -78,10 +79,19 @@ __init int ptlrpc_init(void)
         rc = ldlm_init();
         if (rc)
                 GOTO(cleanup, rc);
+        cleanup_phase = 4;
+
+        ptlrpc_cbdata_slab = cfs_mem_cache_create("ptlrpc_cbdatas",
+                                sizeof (struct ptlrpc_set_cbdata), 0,
+                                SLAB_HWCACHE_ALIGN);
+        if (ptlrpc_cbdata_slab == NULL)
+                GOTO(cleanup, rc);
         RETURN(0);
 
 cleanup:
         switch(cleanup_phase) {
+        case 4:
+                ldlm_exit();
         case 3:
                 ptlrpc_stop_pinger();
         case 2:
@@ -101,6 +111,7 @@ static void __exit ptlrpc_exit(void)
         ptlrpc_stop_pinger();
         ptlrpc_exit_portals();
         ptlrpc_cleanup_connection();
+        cfs_mem_cache_destroy(ptlrpc_cbdata_slab);
 }
 
 /* connection.c */
@@ -149,6 +160,7 @@ EXPORT_SYMBOL(ptlrpc_retain_replayable_request);
 EXPORT_SYMBOL(ptlrpc_next_xid);
 
 EXPORT_SYMBOL(ptlrpc_prep_set);
+EXPORT_SYMBOL(ptlrpc_set_add_cb);
 EXPORT_SYMBOL(ptlrpc_set_add_req);
 EXPORT_SYMBOL(ptlrpc_set_add_new_req);
 EXPORT_SYMBOL(ptlrpc_set_destroy);
diff --git a/lustre/tests/directio.c b/lustre/tests/directio.c
index ebcedb2c781063e69a3716234228b480efd27622..1108cba9bdcf897aa3eb07d3329bb6e1146de7e4 100644
--- a/lustre/tests/directio.c
+++ b/lustre/tests/directio.c
@@ -23,11 +23,12 @@ int main(int argc, char **argv)
         long len;
         off64_t seek;
         struct stat64 st;
+        char pad = 0xba;
         int action;
         int rc;
 
         if (argc < 5 || argc > 6) {
-                printf("Usage: %s <read/write/rdwr> file seek nr_blocks [blocksize]\n", argv[0]);
+                printf("Usage: %s <read/write/rdwr/readhole> file seek nr_blocks [blocksize]\n", argv[0]);
                 return 1;
         }
 
@@ -37,7 +38,10 @@ int main(int argc, char **argv)
                 action = O_WRONLY;
         else if (!strcmp(argv[1], "rdwr"))
                 action = O_RDWR;
-        else {
+        else if (!strcmp(argv[1], "readhole")) {
+                action = O_RDONLY;
+                pad = 0;
+        } else {
                 printf("Usage: %s <read/write/rdwr> file seek nr_blocks [blocksize]\n", argv[0]);
                 return 1;
         }
@@ -74,7 +78,7 @@ int main(int argc, char **argv)
                 printf("No memory %s\n", strerror(errno));
                 return 1;
         }
-        memset(wbuf, 0xba, len);
+        memset(wbuf, pad, len);
 
         if (action == O_WRONLY || action == O_RDWR) {
                 if (lseek64(fd, seek, SEEK_SET) < 0) {
diff --git a/lustre/tests/sanity.sh b/lustre/tests/sanity.sh
index ab063888d5b4bd76c23897981215b493c802c7de..391af95d56cecc37e3e24e24ef1cc504c153fa32 100644
--- a/lustre/tests/sanity.sh
+++ b/lustre/tests/sanity.sh
@@ -4299,9 +4299,19 @@ test_119b() # bug 11737
         sync
         multiop $DIR/$tfile oO_RDONLY:O_DIRECT:r$((2048 * 1024)) || \
                 error "direct read failed"
+        rm -f $DIR/$tfile
 }
 run_test 119b "Sparse directIO read must return actual read amount"
 
+test_119b() # bug 13099
+{
+        BSIZE=1048576
+        directio write $DIR/$tfile 3 1 $BSIZE || error "direct write failed"
+        directio readhole $DIR/$tfile 0 2 $BSIZE || error "reading hole failed"
+        rm -f $DIR/$tfile
+}
+run_test 119b "Testing for direct read hitting hole"
+
 LDLM_POOL_CTL_RECALC=1
 LDLM_POOL_CTL_SHRINK=2