unsigned int            b_page_count;   /* size of page array */
        unsigned int            b_offset;       /* page offset in first page */
        int                     b_error;        /* error code on I/O */
-       int                     b_last_error;   /* previous async I/O error */
+
+       /*
+        * async write failure retry count. Initialised to zero on the first
+        * failure, then when it exceeds the maximum configured without a
+        * success the write is considered to be failed permanently and the
+        * iodone handler will take appropriate action.
+        *
+        * For retry timeouts, we record the jiffie of the first failure. This
+        * means that we can change the retry timeout for buffers already under
+        * I/O and thus avoid getting stuck in a retry loop with a long timeout.
+        *
+        * last_error is used to ensure that we are getting repeated errors, not
+        * different errors. e.g. a block device might change ENOSPC to EIO when
+        * a failure timeout occurs, so we want to re-initialise the error
+        * retry behaviour appropriately when that happens.
+        */
+       int                     b_retries;
+       unsigned long           b_first_retry_time; /* in jiffies */
+       int                     b_last_error;
+
        const struct xfs_buf_ops        *b_ops;
 
 #ifdef XFS_BUF_LOCK_TRACKING
 
                bp->b_flags |= (XBF_WRITE | XBF_ASYNC |
                                XBF_DONE | XBF_WRITE_FAIL);
                bp->b_last_error = bp->b_error;
+               bp->b_retries = 0;
+               bp->b_first_retry_time = jiffies;
+
                xfs_buf_ioerror(bp, 0);
                xfs_buf_submit(bp);
                return true;
         * error configuration we have been set up to use.
         */
        cfg = xfs_error_get_cfg(mp, XFS_ERR_METADATA, bp->b_error);
-       if (!cfg->max_retries)
-               goto permanent_error;
+
+       if (cfg->max_retries != XFS_ERR_RETRY_FOREVER &&
+           ++bp->b_retries > cfg->max_retries)
+                       goto permanent_error;
+       if (cfg->retry_timeout &&
+           time_after(jiffies, cfg->retry_timeout + bp->b_first_retry_time))
+                       goto permanent_error;
 
        /* still a transient error, higher layers will retry */
        xfs_buf_ioerror(bp, 0);
         * retry state here in preparation for the next error that may occur.
         */
        bp->b_last_error = 0;
+       bp->b_retries = 0;
 
        xfs_buf_do_callbacks(bp);
        bp->b_fspriv = NULL;
 
  * and any other future type of IO (e.g. special inode or directory error
  * handling) we care to support.
  */
-static struct attribute *xfs_error_attrs[] = {
-       NULL,
-};
-
 static inline struct xfs_error_cfg *
 to_error_cfg(struct kobject *kobject)
 {
        return container_of(kobj, struct xfs_error_cfg, kobj);
 }
 
+static ssize_t
+max_retries_show(
+       struct kobject  *kobject,
+       char            *buf)
+{
+       struct xfs_error_cfg *cfg = to_error_cfg(kobject);
+
+       return snprintf(buf, PAGE_SIZE, "%d\n", cfg->max_retries);
+}
+
+static ssize_t
+max_retries_store(
+       struct kobject  *kobject,
+       const char      *buf,
+       size_t          count)
+{
+       struct xfs_error_cfg *cfg = to_error_cfg(kobject);
+       int             ret;
+       int             val;
+
+       ret = kstrtoint(buf, 0, &val);
+       if (ret)
+               return ret;
+
+       if (val < -1)
+               return -EINVAL;
+
+       cfg->max_retries = val;
+       return count;
+}
+XFS_SYSFS_ATTR_RW(max_retries);
+
+static ssize_t
+retry_timeout_seconds_show(
+       struct kobject  *kobject,
+       char            *buf)
+{
+       struct xfs_error_cfg *cfg = to_error_cfg(kobject);
+
+       return snprintf(buf, PAGE_SIZE, "%ld\n",
+                       jiffies_to_msecs(cfg->retry_timeout) / MSEC_PER_SEC);
+}
+
+static ssize_t
+retry_timeout_seconds_store(
+       struct kobject  *kobject,
+       const char      *buf,
+       size_t          count)
+{
+       struct xfs_error_cfg *cfg = to_error_cfg(kobject);
+       int             ret;
+       int             val;
+
+       ret = kstrtoint(buf, 0, &val);
+       if (ret)
+               return ret;
+
+       /* 1 day timeout maximum */
+       if (val < 0 || val > 86400)
+               return -EINVAL;
+
+       cfg->retry_timeout = msecs_to_jiffies(val * MSEC_PER_SEC);
+       return count;
+}
+XFS_SYSFS_ATTR_RW(retry_timeout_seconds);
+
+static struct attribute *xfs_error_attrs[] = {
+       ATTR_LIST(max_retries),
+       ATTR_LIST(retry_timeout_seconds),
+       NULL,
+};
+
+
 struct kobj_type xfs_error_cfg_ktype = {
        .release = xfs_sysfs_release,
        .sysfs_ops = &xfs_sysfs_ops,
 struct xfs_error_init {
        char            *name;
        int             max_retries;
+       int             retry_timeout;  /* in seconds */
 };
 
 static const struct xfs_error_init xfs_error_meta_init[XFS_ERR_ERRNO_MAX] = {
        { .name = "default",
          .max_retries = -1,
+         .retry_timeout = 0,
        },
 };
 
                        goto out_error;
 
                cfg->max_retries = init[i].max_retries;
+               cfg->retry_timeout = msecs_to_jiffies(
+                                       init[i].retry_timeout * MSEC_PER_SEC);
        }
        return 0;