* jiffies for either a BDI to exit congestion of the given @sync queue
  * or a write to complete.
  *
- * In the absence of zone congestion, a short sleep or a cond_resched is
- * performed to yield the processor and to allow other subsystems to make
- * a forward progress.
+ * In the absence of zone congestion, cond_resched() is called to yield
+ * the processor if necessary but otherwise does not sleep.
  *
  * The return value is 0 if the sleep is for the full timeout. Otherwise,
  * it is the number of jiffies that were still remaining when the function
         */
        if (atomic_read(&nr_wb_congested[sync]) == 0 ||
            !test_bit(ZONE_CONGESTED, &zone->flags)) {
-
-               /*
-                * Memory allocation/reclaim might be called from a WQ
-                * context and the current implementation of the WQ
-                * concurrency control doesn't recognize that a particular
-                * WQ is congested if the worker thread is looping without
-                * ever sleeping. Therefore we have to do a short sleep
-                * here rather than calling cond_resched().
-                */
-               if (current->flags & PF_WQ_WORKER)
-                       schedule_timeout_uninterruptible(1);
-               else
-                       cond_resched();
-
+               cond_resched();
                /* In case we scheduled, work out time remaining */
                ret = timeout - (jiffies - start);
                if (ret < 0)
 
        for_each_zone_zonelist_nodemask(zone, z, ac->zonelist, ac->high_zoneidx,
                                        ac->nodemask) {
                unsigned long available;
+               unsigned long reclaimable;
 
-               available = zone_reclaimable_pages(zone);
+               available = reclaimable = zone_reclaimable_pages(zone);
                available -= DIV_ROUND_UP(no_progress_loops * available,
                                          MAX_RECLAIM_RETRIES);
                available += zone_page_state_snapshot(zone, NR_FREE_PAGES);
                 * available?
                 */
                if (__zone_watermark_ok(zone, order, min_wmark_pages(zone),
-                               ac->high_zoneidx, alloc_flags, available)) {
-                       /* Wait for some write requests to complete then retry */
-                       wait_iff_congested(zone, BLK_RW_ASYNC, HZ/50);
+                               ac_classzone_idx(ac), alloc_flags, available)) {
+                       /*
+                        * If we didn't make any progress and have a lot of
+                        * dirty + writeback pages then we should wait for
+                        * an IO to complete to slow down the reclaim and
+                        * prevent from pre mature OOM
+                        */
+                       if (!did_some_progress) {
+                               unsigned long writeback;
+                               unsigned long dirty;
+
+                               writeback = zone_page_state_snapshot(zone,
+                                                                    NR_WRITEBACK);
+                               dirty = zone_page_state_snapshot(zone, NR_FILE_DIRTY);
+
+                               if (2*(writeback + dirty) > reclaimable) {
+                                       congestion_wait(BLK_RW_ASYNC, HZ/10);
+                                       return true;
+                               }
+                       }
+
+                       /*
+                        * Memory allocation/reclaim might be called from a WQ
+                        * context and the current implementation of the WQ
+                        * concurrency control doesn't recognize that
+                        * a particular WQ is congested if the worker thread is
+                        * looping without ever sleeping. Therefore we have to
+                        * do a short sleep here rather than calling
+                        * cond_resched().
+                        */
+                       if (current->flags & PF_WQ_WORKER)
+                               schedule_timeout_uninterruptible(1);
+                       else
+                               cond_resched();
+
                        return true;
                }
        }