]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
3.17-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 12 Nov 2014 01:07:37 +0000 (10:07 +0900)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 12 Nov 2014 01:07:37 +0000 (10:07 +0900)
added patches:
arm-dts-zynq-enable-pl-clocks-for-parallella.patch
btrfs-fix-kfree-on-list_head-in-btrfs_lookup_csums_range-error-cleanup.patch
hid-add-keyboard-input-assist-hid-usages.patch
of-fix-overflow-bug-in-string-property-parsing-functions.patch
xfs-bulkstat-btree-walk-doesn-t-terminate.patch
xfs-bulkstat-chunk-formatter-has-issues.patch
xfs-bulkstat-chunk-formatting-cursor-is-broken.patch
xfs-bulkstat-doesn-t-release-agi-buffer-on-error.patch
xfs-bulkstat-error-handling-is-broken.patch
xfs-bulkstat-main-loop-logic-is-a-mess.patch
xfs-check-error-during-inode-btree-iteration-in-xfs_bulkstat.patch
xfs-track-bulkstat-progress-by-agino.patch

13 files changed:
queue-3.17/arm-dts-zynq-enable-pl-clocks-for-parallella.patch [new file with mode: 0644]
queue-3.17/btrfs-fix-kfree-on-list_head-in-btrfs_lookup_csums_range-error-cleanup.patch [new file with mode: 0644]
queue-3.17/hid-add-keyboard-input-assist-hid-usages.patch [new file with mode: 0644]
queue-3.17/of-fix-overflow-bug-in-string-property-parsing-functions.patch [new file with mode: 0644]
queue-3.17/series
queue-3.17/xfs-bulkstat-btree-walk-doesn-t-terminate.patch [new file with mode: 0644]
queue-3.17/xfs-bulkstat-chunk-formatter-has-issues.patch [new file with mode: 0644]
queue-3.17/xfs-bulkstat-chunk-formatting-cursor-is-broken.patch [new file with mode: 0644]
queue-3.17/xfs-bulkstat-doesn-t-release-agi-buffer-on-error.patch [new file with mode: 0644]
queue-3.17/xfs-bulkstat-error-handling-is-broken.patch [new file with mode: 0644]
queue-3.17/xfs-bulkstat-main-loop-logic-is-a-mess.patch [new file with mode: 0644]
queue-3.17/xfs-check-error-during-inode-btree-iteration-in-xfs_bulkstat.patch [new file with mode: 0644]
queue-3.17/xfs-track-bulkstat-progress-by-agino.patch [new file with mode: 0644]

diff --git a/queue-3.17/arm-dts-zynq-enable-pl-clocks-for-parallella.patch b/queue-3.17/arm-dts-zynq-enable-pl-clocks-for-parallella.patch
new file mode 100644 (file)
index 0000000..3362ee8
--- /dev/null
@@ -0,0 +1,43 @@
+From 92c9e0c780e61f821ab8a08f0d4d4fd33ba1197c Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Andreas=20F=C3=A4rber?= <afaerber@suse.de>
+Date: Thu, 6 Nov 2014 18:22:10 +0100
+Subject: ARM: dts: zynq: Enable PL clocks for Parallella
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: =?UTF-8?q?Andreas=20F=C3=A4rber?= <afaerber@suse.de>
+
+commit 92c9e0c780e61f821ab8a08f0d4d4fd33ba1197c upstream.
+
+The Parallella board comes with a U-Boot bootloader that loads one of
+two predefined FPGA bitstreams before booting the kernel. Both define an
+AXI interface to the on-board Epiphany processor.
+
+Enable clocks FCLK0..FCLK3 for the Programmable Logic by default.
+
+Otherwise accessing, e.g., the ESYSRESET register freezes the board,
+as seen with the Epiphany SDK tools e-reset and e-hw-rev, using /dev/mem.
+
+Signed-off-by: Andreas Färber <afaerber@suse.de>
+Acked-by: Michal Simek <michal.simek@xilinx.com>
+Signed-off-by: Olof Johansson <olof@lixom.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ arch/arm/boot/dts/zynq-parallella.dts |    4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/arch/arm/boot/dts/zynq-parallella.dts
++++ b/arch/arm/boot/dts/zynq-parallella.dts
+@@ -34,6 +34,10 @@
+       };
+ };
++&clkc {
++      fclk-enable = <0xf>;
++};
++
+ &gem0 {
+       status = "okay";
+       phy-mode = "rgmii-id";
diff --git a/queue-3.17/btrfs-fix-kfree-on-list_head-in-btrfs_lookup_csums_range-error-cleanup.patch b/queue-3.17/btrfs-fix-kfree-on-list_head-in-btrfs_lookup_csums_range-error-cleanup.patch
new file mode 100644 (file)
index 0000000..d68b1d1
--- /dev/null
@@ -0,0 +1,36 @@
+From 6e5aafb27419f32575b27ef9d6a31e5d54661aca Mon Sep 17 00:00:00 2001
+From: Chris Mason <clm@fb.com>
+Date: Tue, 4 Nov 2014 06:59:04 -0800
+Subject: Btrfs: fix kfree on list_head in btrfs_lookup_csums_range error cleanup
+
+From: Chris Mason <clm@fb.com>
+
+commit 6e5aafb27419f32575b27ef9d6a31e5d54661aca upstream.
+
+If we hit any errors in btrfs_lookup_csums_range, we'll loop through all
+the csums we allocate and free them.  But the code was using list_entry
+incorrectly, and ended up trying to free the on-stack list_head instead.
+
+This bug came from commit 0678b6185
+
+btrfs: Don't BUG_ON kzalloc error in btrfs_lookup_csums_range()
+
+Signed-off-by: Chris Mason <clm@fb.com>
+Reported-by: Erik Berg <btrfs@slipsprogrammoer.no>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ fs/btrfs/file-item.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/fs/btrfs/file-item.c
++++ b/fs/btrfs/file-item.c
+@@ -423,7 +423,7 @@ int btrfs_lookup_csums_range(struct btrf
+       ret = 0;
+ fail:
+       while (ret < 0 && !list_empty(&tmplist)) {
+-              sums = list_entry(&tmplist, struct btrfs_ordered_sum, list);
++              sums = list_entry(tmplist.next, struct btrfs_ordered_sum, list);
+               list_del(&sums->list);
+               kfree(sums);
+       }
diff --git a/queue-3.17/hid-add-keyboard-input-assist-hid-usages.patch b/queue-3.17/hid-add-keyboard-input-assist-hid-usages.patch
new file mode 100644 (file)
index 0000000..d3939dc
--- /dev/null
@@ -0,0 +1,71 @@
+From f974008f07a62171a9dede08250c9a35c2b2b986 Mon Sep 17 00:00:00 2001
+From: Olivier Gay <ogay@logitech.com>
+Date: Sat, 18 Oct 2014 01:53:39 +0200
+Subject: HID: add keyboard input assist hid usages
+
+From: Olivier Gay <ogay@logitech.com>
+
+commit f974008f07a62171a9dede08250c9a35c2b2b986 upstream.
+
+Add keyboard input assist controls usages from approved
+hid usage table request HUTTR42:
+http://www.usb.org/developers/hidpage/HUTRR42c.pdf
+
+Signed-off-by: Olivier Gay <ogay@logitech.com>
+Acked-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
+Signed-off-by: Jiri Kosina <jkosina@suse.cz>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/hid/hid-debug.c    |    6 ++++++
+ drivers/hid/hid-input.c    |    7 +++++++
+ include/uapi/linux/input.h |    7 +++++++
+ 3 files changed, 20 insertions(+)
+
+--- a/drivers/hid/hid-debug.c
++++ b/drivers/hid/hid-debug.c
+@@ -946,6 +946,12 @@ static const char *keys[KEY_MAX + 1] = {
+       [KEY_BRIGHTNESS_MIN] = "BrightnessMin",
+       [KEY_BRIGHTNESS_MAX] = "BrightnessMax",
+       [KEY_BRIGHTNESS_AUTO] = "BrightnessAuto",
++      [KEY_KBDINPUTASSIST_PREV] = "KbdInputAssistPrev",
++      [KEY_KBDINPUTASSIST_NEXT] = "KbdInputAssistNext",
++      [KEY_KBDINPUTASSIST_PREVGROUP] = "KbdInputAssistPrevGroup",
++      [KEY_KBDINPUTASSIST_NEXTGROUP] = "KbdInputAssistNextGroup",
++      [KEY_KBDINPUTASSIST_ACCEPT] = "KbdInputAssistAccept",
++      [KEY_KBDINPUTASSIST_CANCEL] = "KbdInputAssistCancel",
+ };
+ static const char *relatives[REL_MAX + 1] = {
+--- a/drivers/hid/hid-input.c
++++ b/drivers/hid/hid-input.c
+@@ -859,6 +859,13 @@ static void hidinput_configure_usage(str
+               case 0x28b: map_key_clear(KEY_FORWARDMAIL);     break;
+               case 0x28c: map_key_clear(KEY_SEND);            break;
++              case 0x2c7: map_key_clear(KEY_KBDINPUTASSIST_PREV);             break;
++              case 0x2c8: map_key_clear(KEY_KBDINPUTASSIST_NEXT);             break;
++              case 0x2c9: map_key_clear(KEY_KBDINPUTASSIST_PREVGROUP);                break;
++              case 0x2ca: map_key_clear(KEY_KBDINPUTASSIST_NEXTGROUP);                break;
++              case 0x2cb: map_key_clear(KEY_KBDINPUTASSIST_ACCEPT);   break;
++              case 0x2cc: map_key_clear(KEY_KBDINPUTASSIST_CANCEL);   break;
++
+               default:    goto ignore;
+               }
+               break;
+--- a/include/uapi/linux/input.h
++++ b/include/uapi/linux/input.h
+@@ -739,6 +739,13 @@ struct input_keymap_entry {
+ #define KEY_BRIGHTNESS_MIN            0x250   /* Set Brightness to Minimum */
+ #define KEY_BRIGHTNESS_MAX            0x251   /* Set Brightness to Maximum */
++#define KEY_KBDINPUTASSIST_PREV               0x260
++#define KEY_KBDINPUTASSIST_NEXT               0x261
++#define KEY_KBDINPUTASSIST_PREVGROUP          0x262
++#define KEY_KBDINPUTASSIST_NEXTGROUP          0x263
++#define KEY_KBDINPUTASSIST_ACCEPT             0x264
++#define KEY_KBDINPUTASSIST_CANCEL             0x265
++
+ #define BTN_TRIGGER_HAPPY             0x2c0
+ #define BTN_TRIGGER_HAPPY1            0x2c0
+ #define BTN_TRIGGER_HAPPY2            0x2c1
diff --git a/queue-3.17/of-fix-overflow-bug-in-string-property-parsing-functions.patch b/queue-3.17/of-fix-overflow-bug-in-string-property-parsing-functions.patch
new file mode 100644 (file)
index 0000000..edb1eff
--- /dev/null
@@ -0,0 +1,382 @@
+From a87fa1d81a9fb5e9adca9820e16008c40ad09f33 Mon Sep 17 00:00:00 2001
+From: Grant Likely <grant.likely@linaro.org>
+Date: Mon, 3 Nov 2014 15:15:35 +0000
+Subject: of: Fix overflow bug in string property parsing functions
+
+From: Grant Likely <grant.likely@linaro.org>
+
+commit a87fa1d81a9fb5e9adca9820e16008c40ad09f33 upstream.
+
+The string property read helpers will run off the end of the buffer if
+it is handed a malformed string property. Rework the parsers to make
+sure that doesn't happen. At the same time add new test cases to make
+sure the functions behave themselves.
+
+The original implementations of of_property_read_string_index() and
+of_property_count_strings() both open-coded the same block of parsing
+code, each with it's own subtly different bugs. The fix here merges
+functions into a single helper and makes the original functions static
+inline wrappers around the helper.
+
+One non-bugfix aspect of this patch is the addition of a new wrapper,
+of_property_read_string_array(). The new wrapper is needed by the
+device_properties feature that Rafael is working on and planning to
+merge for v3.19. The implementation is identical both with and without
+the new static inline wrapper, so it just got left in to reduce the
+churn on the header file.
+
+Signed-off-by: Grant Likely <grant.likely@linaro.org>
+Cc: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+Cc: Mika Westerberg <mika.westerberg@linux.intel.com>
+Cc: Rob Herring <robh+dt@kernel.org>
+Cc: Arnd Bergmann <arnd@arndb.de>
+Cc: Darren Hart <darren.hart@intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/of/base.c                           |   88 +++++++---------------------
+ drivers/of/selftest.c                       |   66 +++++++++++++++++++--
+ drivers/of/testcase-data/tests-phandle.dtsi |    2 
+ include/linux/of.h                          |   84 ++++++++++++++++++++++----
+ 4 files changed, 154 insertions(+), 86 deletions(-)
+
+--- a/drivers/of/base.c
++++ b/drivers/of/base.c
+@@ -1277,52 +1277,6 @@ int of_property_read_string(struct devic
+ EXPORT_SYMBOL_GPL(of_property_read_string);
+ /**
+- * of_property_read_string_index - Find and read a string from a multiple
+- * strings property.
+- * @np:               device node from which the property value is to be read.
+- * @propname: name of the property to be searched.
+- * @index:    index of the string in the list of strings
+- * @out_string:       pointer to null terminated return string, modified only if
+- *            return value is 0.
+- *
+- * Search for a property in a device tree node and retrieve a null
+- * terminated string value (pointer to data, not a copy) in the list of strings
+- * contained in that property.
+- * Returns 0 on success, -EINVAL if the property does not exist, -ENODATA if
+- * property does not have a value, and -EILSEQ if the string is not
+- * null-terminated within the length of the property data.
+- *
+- * The out_string pointer is modified only if a valid string can be decoded.
+- */
+-int of_property_read_string_index(struct device_node *np, const char *propname,
+-                                int index, const char **output)
+-{
+-      struct property *prop = of_find_property(np, propname, NULL);
+-      int i = 0;
+-      size_t l = 0, total = 0;
+-      const char *p;
+-
+-      if (!prop)
+-              return -EINVAL;
+-      if (!prop->value)
+-              return -ENODATA;
+-      if (strnlen(prop->value, prop->length) >= prop->length)
+-              return -EILSEQ;
+-
+-      p = prop->value;
+-
+-      for (i = 0; total < prop->length; total += l, p += l) {
+-              l = strlen(p) + 1;
+-              if (i++ == index) {
+-                      *output = p;
+-                      return 0;
+-              }
+-      }
+-      return -ENODATA;
+-}
+-EXPORT_SYMBOL_GPL(of_property_read_string_index);
+-
+-/**
+  * of_property_match_string() - Find string in a list and return index
+  * @np: pointer to node containing string list property
+  * @propname: string list property name
+@@ -1348,7 +1302,7 @@ int of_property_match_string(struct devi
+       end = p + prop->length;
+       for (i = 0; p < end; i++, p += l) {
+-              l = strlen(p) + 1;
++              l = strnlen(p, end - p) + 1;
+               if (p + l > end)
+                       return -EILSEQ;
+               pr_debug("comparing %s with %s\n", string, p);
+@@ -1360,39 +1314,41 @@ int of_property_match_string(struct devi
+ EXPORT_SYMBOL_GPL(of_property_match_string);
+ /**
+- * of_property_count_strings - Find and return the number of strings from a
+- * multiple strings property.
++ * of_property_read_string_util() - Utility helper for parsing string properties
+  * @np:               device node from which the property value is to be read.
+  * @propname: name of the property to be searched.
++ * @out_strs: output array of string pointers.
++ * @sz:               number of array elements to read.
++ * @skip:     Number of strings to skip over at beginning of list.
+  *
+- * Search for a property in a device tree node and retrieve the number of null
+- * terminated string contain in it. Returns the number of strings on
+- * success, -EINVAL if the property does not exist, -ENODATA if property
+- * does not have a value, and -EILSEQ if the string is not null-terminated
+- * within the length of the property data.
++ * Don't call this function directly. It is a utility helper for the
++ * of_property_read_string*() family of functions.
+  */
+-int of_property_count_strings(struct device_node *np, const char *propname)
++int of_property_read_string_helper(struct device_node *np, const char *propname,
++                                 const char **out_strs, size_t sz, int skip)
+ {
+       struct property *prop = of_find_property(np, propname, NULL);
+-      int i = 0;
+-      size_t l = 0, total = 0;
+-      const char *p;
++      int l = 0, i = 0;
++      const char *p, *end;
+       if (!prop)
+               return -EINVAL;
+       if (!prop->value)
+               return -ENODATA;
+-      if (strnlen(prop->value, prop->length) >= prop->length)
+-              return -EILSEQ;
+-
+       p = prop->value;
++      end = p + prop->length;
+-      for (i = 0; total < prop->length; total += l, p += l, i++)
+-              l = strlen(p) + 1;
+-
+-      return i;
++      for (i = 0; p < end && (!out_strs || i < skip + sz); i++, p += l) {
++              l = strnlen(p, end - p) + 1;
++              if (p + l > end)
++                      return -EILSEQ;
++              if (out_strs && i >= skip)
++                      *out_strs++ = p;
++      }
++      i -= skip;
++      return i <= 0 ? -ENODATA : i;
+ }
+-EXPORT_SYMBOL_GPL(of_property_count_strings);
++EXPORT_SYMBOL_GPL(of_property_read_string_helper);
+ void of_print_phandle_args(const char *msg, const struct of_phandle_args *args)
+ {
+--- a/drivers/of/selftest.c
++++ b/drivers/of/selftest.c
+@@ -247,8 +247,9 @@ static void __init of_selftest_parse_pha
+       selftest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc);
+ }
+-static void __init of_selftest_property_match_string(void)
++static void __init of_selftest_property_string(void)
+ {
++      const char *strings[4];
+       struct device_node *np;
+       int rc;
+@@ -265,13 +266,66 @@ static void __init of_selftest_property_
+       rc = of_property_match_string(np, "phandle-list-names", "third");
+       selftest(rc == 2, "third expected:0 got:%i\n", rc);
+       rc = of_property_match_string(np, "phandle-list-names", "fourth");
+-      selftest(rc == -ENODATA, "unmatched string; rc=%i", rc);
++      selftest(rc == -ENODATA, "unmatched string; rc=%i\n", rc);
+       rc = of_property_match_string(np, "missing-property", "blah");
+-      selftest(rc == -EINVAL, "missing property; rc=%i", rc);
++      selftest(rc == -EINVAL, "missing property; rc=%i\n", rc);
+       rc = of_property_match_string(np, "empty-property", "blah");
+-      selftest(rc == -ENODATA, "empty property; rc=%i", rc);
++      selftest(rc == -ENODATA, "empty property; rc=%i\n", rc);
+       rc = of_property_match_string(np, "unterminated-string", "blah");
+-      selftest(rc == -EILSEQ, "unterminated string; rc=%i", rc);
++      selftest(rc == -EILSEQ, "unterminated string; rc=%i\n", rc);
++
++      /* of_property_count_strings() tests */
++      rc = of_property_count_strings(np, "string-property");
++      selftest(rc == 1, "Incorrect string count; rc=%i\n", rc);
++      rc = of_property_count_strings(np, "phandle-list-names");
++      selftest(rc == 3, "Incorrect string count; rc=%i\n", rc);
++      rc = of_property_count_strings(np, "unterminated-string");
++      selftest(rc == -EILSEQ, "unterminated string; rc=%i\n", rc);
++      rc = of_property_count_strings(np, "unterminated-string-list");
++      selftest(rc == -EILSEQ, "unterminated string array; rc=%i\n", rc);
++
++      /* of_property_read_string_index() tests */
++      rc = of_property_read_string_index(np, "string-property", 0, strings);
++      selftest(rc == 0 && !strcmp(strings[0], "foobar"), "of_property_read_string_index() failure; rc=%i\n", rc);
++      strings[0] = NULL;
++      rc = of_property_read_string_index(np, "string-property", 1, strings);
++      selftest(rc == -ENODATA && strings[0] == NULL, "of_property_read_string_index() failure; rc=%i\n", rc);
++      rc = of_property_read_string_index(np, "phandle-list-names", 0, strings);
++      selftest(rc == 0 && !strcmp(strings[0], "first"), "of_property_read_string_index() failure; rc=%i\n", rc);
++      rc = of_property_read_string_index(np, "phandle-list-names", 1, strings);
++      selftest(rc == 0 && !strcmp(strings[0], "second"), "of_property_read_string_index() failure; rc=%i\n", rc);
++      rc = of_property_read_string_index(np, "phandle-list-names", 2, strings);
++      selftest(rc == 0 && !strcmp(strings[0], "third"), "of_property_read_string_index() failure; rc=%i\n", rc);
++      strings[0] = NULL;
++      rc = of_property_read_string_index(np, "phandle-list-names", 3, strings);
++      selftest(rc == -ENODATA && strings[0] == NULL, "of_property_read_string_index() failure; rc=%i\n", rc);
++      strings[0] = NULL;
++      rc = of_property_read_string_index(np, "unterminated-string", 0, strings);
++      selftest(rc == -EILSEQ && strings[0] == NULL, "of_property_read_string_index() failure; rc=%i\n", rc);
++      rc = of_property_read_string_index(np, "unterminated-string-list", 0, strings);
++      selftest(rc == 0 && !strcmp(strings[0], "first"), "of_property_read_string_index() failure; rc=%i\n", rc);
++      strings[0] = NULL;
++      rc = of_property_read_string_index(np, "unterminated-string-list", 2, strings); /* should fail */
++      selftest(rc == -EILSEQ && strings[0] == NULL, "of_property_read_string_index() failure; rc=%i\n", rc);
++      strings[1] = NULL;
++
++      /* of_property_read_string_array() tests */
++      rc = of_property_read_string_array(np, "string-property", strings, 4);
++      selftest(rc == 1, "Incorrect string count; rc=%i\n", rc);
++      rc = of_property_read_string_array(np, "phandle-list-names", strings, 4);
++      selftest(rc == 3, "Incorrect string count; rc=%i\n", rc);
++      rc = of_property_read_string_array(np, "unterminated-string", strings, 4);
++      selftest(rc == -EILSEQ, "unterminated string; rc=%i\n", rc);
++      /* -- An incorrectly formed string should cause a failure */
++      rc = of_property_read_string_array(np, "unterminated-string-list", strings, 4);
++      selftest(rc == -EILSEQ, "unterminated string array; rc=%i\n", rc);
++      /* -- parsing the correctly formed strings should still work: */
++      strings[2] = NULL;
++      rc = of_property_read_string_array(np, "unterminated-string-list", strings, 2);
++      selftest(rc == 2 && strings[2] == NULL, "of_property_read_string_array() failure; rc=%i\n", rc);
++      strings[1] = NULL;
++      rc = of_property_read_string_array(np, "phandle-list-names", strings, 1);
++      selftest(rc == 1 && strings[1] == NULL, "Overwrote end of string array; rc=%i, str='%s'\n", rc, strings[1]);
+ }
+ #define propcmp(p1, p2) (((p1)->length == (p2)->length) && \
+@@ -783,7 +837,7 @@ static int __init of_selftest(void)
+       of_selftest_find_node_by_name();
+       of_selftest_dynamic();
+       of_selftest_parse_phandle_with_args();
+-      of_selftest_property_match_string();
++      of_selftest_property_string();
+       of_selftest_property_copy();
+       of_selftest_changeset();
+       of_selftest_parse_interrupts();
+--- a/drivers/of/testcase-data/tests-phandle.dtsi
++++ b/drivers/of/testcase-data/tests-phandle.dtsi
+@@ -39,7 +39,9 @@
+                               phandle-list-bad-args = <&provider2 1 0>,
+                                                       <&provider3 0>;
+                               empty-property;
++                              string-property = "foobar";
+                               unterminated-string = [40 41 42 43];
++                              unterminated-string-list = "first", "second", [40 41 42 43];
+                       };
+               };
+       };
+--- a/include/linux/of.h
++++ b/include/linux/of.h
+@@ -267,14 +267,12 @@ extern int of_property_read_u64(const st
+ extern int of_property_read_string(struct device_node *np,
+                                  const char *propname,
+                                  const char **out_string);
+-extern int of_property_read_string_index(struct device_node *np,
+-                                       const char *propname,
+-                                       int index, const char **output);
+ extern int of_property_match_string(struct device_node *np,
+                                   const char *propname,
+                                   const char *string);
+-extern int of_property_count_strings(struct device_node *np,
+-                                   const char *propname);
++extern int of_property_read_string_helper(struct device_node *np,
++                                            const char *propname,
++                                            const char **out_strs, size_t sz, int index);
+ extern int of_device_is_compatible(const struct device_node *device,
+                                  const char *);
+ extern int of_device_is_available(const struct device_node *device);
+@@ -486,15 +484,9 @@ static inline int of_property_read_strin
+       return -ENOSYS;
+ }
+-static inline int of_property_read_string_index(struct device_node *np,
+-                                              const char *propname, int index,
+-                                              const char **out_string)
+-{
+-      return -ENOSYS;
+-}
+-
+-static inline int of_property_count_strings(struct device_node *np,
+-                                          const char *propname)
++static inline int of_property_read_string_helper(struct device_node *np,
++                                               const char *propname,
++                                               const char **out_strs, size_t sz, int index)
+ {
+       return -ENOSYS;
+ }
+@@ -668,6 +660,70 @@ static inline int of_property_count_u64_
+ }
+ /**
++ * of_property_read_string_array() - Read an array of strings from a multiple
++ * strings property.
++ * @np:               device node from which the property value is to be read.
++ * @propname: name of the property to be searched.
++ * @out_strs: output array of string pointers.
++ * @sz:               number of array elements to read.
++ *
++ * Search for a property in a device tree node and retrieve a list of
++ * terminated string values (pointer to data, not a copy) in that property.
++ *
++ * If @out_strs is NULL, the number of strings in the property is returned.
++ */
++static inline int of_property_read_string_array(struct device_node *np,
++                                              const char *propname, const char **out_strs,
++                                              size_t sz)
++{
++      return of_property_read_string_helper(np, propname, out_strs, sz, 0);
++}
++
++/**
++ * of_property_count_strings() - Find and return the number of strings from a
++ * multiple strings property.
++ * @np:               device node from which the property value is to be read.
++ * @propname: name of the property to be searched.
++ *
++ * Search for a property in a device tree node and retrieve the number of null
++ * terminated string contain in it. Returns the number of strings on
++ * success, -EINVAL if the property does not exist, -ENODATA if property
++ * does not have a value, and -EILSEQ if the string is not null-terminated
++ * within the length of the property data.
++ */
++static inline int of_property_count_strings(struct device_node *np,
++                                          const char *propname)
++{
++      return of_property_read_string_helper(np, propname, NULL, 0, 0);
++}
++
++/**
++ * of_property_read_string_index() - Find and read a string from a multiple
++ * strings property.
++ * @np:               device node from which the property value is to be read.
++ * @propname: name of the property to be searched.
++ * @index:    index of the string in the list of strings
++ * @out_string:       pointer to null terminated return string, modified only if
++ *            return value is 0.
++ *
++ * Search for a property in a device tree node and retrieve a null
++ * terminated string value (pointer to data, not a copy) in the list of strings
++ * contained in that property.
++ * Returns 0 on success, -EINVAL if the property does not exist, -ENODATA if
++ * property does not have a value, and -EILSEQ if the string is not
++ * null-terminated within the length of the property data.
++ *
++ * The out_string pointer is modified only if a valid string can be decoded.
++ */
++static inline int of_property_read_string_index(struct device_node *np,
++                                              const char *propname,
++                                              int index, const char **output)
++{
++      int rc = of_property_read_string_helper(np, propname, output, 1, index);
++      return rc < 0 ? rc : 0;
++}
++
++/**
+  * of_property_read_bool - Findfrom a property
+  * @np:               device node from which the property value is to be read.
+  * @propname: name of the property to be searched.
index ea499b6fba5717fe590c88caaf4af01994c0a651..6d710de1fb3c7d79a836c00cdb4e0ff414d77e17 100644 (file)
@@ -305,3 +305,15 @@ irqchip-armada-370-xp-fix-msi-interrupt-handling.patch
 irqchip-armada-370-xp-fix-mpic-interrupt-handling.patch
 i2c-at91-don-t-account-as-iowait.patch
 sysfs-driver-core-fix-glue-dir-race-condition-by-gdp_mutex.patch
+arm-dts-zynq-enable-pl-clocks-for-parallella.patch
+of-fix-overflow-bug-in-string-property-parsing-functions.patch
+btrfs-fix-kfree-on-list_head-in-btrfs_lookup_csums_range-error-cleanup.patch
+xfs-bulkstat-doesn-t-release-agi-buffer-on-error.patch
+xfs-check-error-during-inode-btree-iteration-in-xfs_bulkstat.patch
+xfs-bulkstat-btree-walk-doesn-t-terminate.patch
+xfs-bulkstat-chunk-formatting-cursor-is-broken.patch
+xfs-bulkstat-chunk-formatter-has-issues.patch
+xfs-bulkstat-main-loop-logic-is-a-mess.patch
+xfs-bulkstat-error-handling-is-broken.patch
+xfs-track-bulkstat-progress-by-agino.patch
+hid-add-keyboard-input-assist-hid-usages.patch
diff --git a/queue-3.17/xfs-bulkstat-btree-walk-doesn-t-terminate.patch b/queue-3.17/xfs-bulkstat-btree-walk-doesn-t-terminate.patch
new file mode 100644 (file)
index 0000000..a1b274b
--- /dev/null
@@ -0,0 +1,93 @@
+From afa947cb52a8e73fe71915a0b0af6fcf98dfbe1a Mon Sep 17 00:00:00 2001
+From: Dave Chinner <dchinner@redhat.com>
+Date: Fri, 7 Nov 2014 08:29:57 +1100
+Subject: xfs: bulkstat btree walk doesn't terminate
+
+From: Dave Chinner <dchinner@redhat.com>
+
+commit afa947cb52a8e73fe71915a0b0af6fcf98dfbe1a upstream.
+
+The bulkstat code has several different ways of detecting the end of
+an AG when doing a walk. They are not consistently detected, and the
+code that checks for the end of AG conditions is not consistently
+coded. Hence the are conditions where the walk code can get stuck in
+an endless loop making no progress and not triggering any
+termination conditions.
+
+Convert all the "tmp/i" status return codes from btree operations
+to a common name (stat) and apply end-of-ag detection to these
+operations consistently.
+
+Signed-off-by: Dave Chinner <dchinner@redhat.com>
+Reviewed-by: Brian Foster <bfoster@redhat.com>
+Signed-off-by: Dave Chinner <david@fromorbit.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ fs/xfs/xfs_itable.c |   19 ++++++++++---------
+ 1 file changed, 10 insertions(+), 9 deletions(-)
+
+--- a/fs/xfs/xfs_itable.c
++++ b/fs/xfs/xfs_itable.c
+@@ -356,7 +356,6 @@ xfs_bulkstat(
+       int                     end_of_ag; /* set if we've seen the ag end */
+       int                     error;  /* error code */
+       int                     fmterror;/* bulkstat formatter result */
+-      int                     i;      /* loop index */
+       int                     icount; /* count of inodes good in irbuf */
+       size_t                  irbsize; /* size of irec buffer in bytes */
+       xfs_ino_t               ino;    /* inode number (filesystem) */
+@@ -366,11 +365,11 @@ xfs_bulkstat(
+       xfs_ino_t               lastino; /* last inode number returned */
+       int                     nirbuf; /* size of irbuf */
+       int                     rval;   /* return value error code */
+-      int                     tmp;    /* result value from btree calls */
+       int                     ubcount; /* size of user's buffer */
+       int                     ubleft; /* bytes left in user's buffer */
+       char                    __user *ubufp;  /* pointer into user's buffer */
+       int                     ubelem; /* spaces used in user's buffer */
++      int                     stat;
+       /*
+        * Get the last inode value, see if there's nothing to do.
+@@ -436,13 +435,15 @@ xfs_bulkstat(
+                               agino = r.ir_startino + XFS_INODES_PER_CHUNK;
+                       }
+                       /* Increment to the next record */
+-                      error = xfs_btree_increment(cur, 0, &tmp);
++                      error = xfs_btree_increment(cur, 0, &stat);
+               } else {
+                       /* Start of ag.  Lookup the first inode chunk */
+-                      error = xfs_inobt_lookup(cur, 0, XFS_LOOKUP_GE, &tmp);
++                      error = xfs_inobt_lookup(cur, 0, XFS_LOOKUP_GE, &stat);
+               }
+-              if (error)
++              if (error || stat == 0) {
++                      end_of_ag = 1;
+                       goto del_cursor;
++              }
+               /*
+                * Loop through inode btree records in this ag,
+@@ -451,8 +452,8 @@ xfs_bulkstat(
+               while (irbp < irbufend && icount < ubcount) {
+                       struct xfs_inobt_rec_incore     r;
+-                      error = xfs_inobt_get_rec(cur, &r, &i);
+-                      if (error || i == 0) {
++                      error = xfs_inobt_get_rec(cur, &r, &stat);
++                      if (error || stat == 0) {
+                               end_of_ag = 1;
+                               goto del_cursor;
+                       }
+@@ -473,8 +474,8 @@ xfs_bulkstat(
+                        * Set agino to after this chunk and bump the cursor.
+                        */
+                       agino = r.ir_startino + XFS_INODES_PER_CHUNK;
+-                      error = xfs_btree_increment(cur, 0, &tmp);
+-                      if (error) {
++                      error = xfs_btree_increment(cur, 0, &stat);
++                      if (error || stat == 0) {
+                               end_of_ag = 1;
+                               goto del_cursor;
+                       }
diff --git a/queue-3.17/xfs-bulkstat-chunk-formatter-has-issues.patch b/queue-3.17/xfs-bulkstat-chunk-formatter-has-issues.patch
new file mode 100644 (file)
index 0000000..3cc83ea
--- /dev/null
@@ -0,0 +1,120 @@
+From 2b831ac6bc87d3cbcbb1a8816827b6923403e461 Mon Sep 17 00:00:00 2001
+From: Dave Chinner <dchinner@redhat.com>
+Date: Fri, 7 Nov 2014 08:30:58 +1100
+Subject: xfs: bulkstat chunk-formatter has issues
+
+From: Dave Chinner <dchinner@redhat.com>
+
+commit 2b831ac6bc87d3cbcbb1a8816827b6923403e461 upstream.
+
+The loop construct has issues:
+       - clustidx is completely unused, so remove it.
+       - the loop tries to be smart by terminating when the
+         "freecount" tells it that all inodes are free. Just drop
+         it as in most cases we have to scan all inodes in the
+         chunk anyway.
+       - move the "user buffer left" condition check to the only
+         point where we consume space int eh user buffer.
+       - move the initialisation of agino out of the loop, leaving
+         just a simple loop control logic using the clusteridx.
+
+Also, double handling of the user buffer variables leads to problems
+tracking the current state - use the cursor variables directly
+rather than keeping local copies and then having to update the
+cursor before returning.
+
+Signed-off-by: Dave Chinner <dchinner@redhat.com>
+Reviewed-by: Brian Foster <bfoster@redhat.com>
+Signed-off-by: Dave Chinner <david@fromorbit.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ fs/xfs/xfs_itable.c |   58 +++++++++++++++++++++-------------------------------
+ 1 file changed, 24 insertions(+), 34 deletions(-)
+
+--- a/fs/xfs/xfs_itable.c
++++ b/fs/xfs/xfs_itable.c
+@@ -283,59 +283,49 @@ xfs_bulkstat_ag_ichunk(
+       xfs_ino_t                       *lastino)
+ {
+       char                            __user **ubufp = acp->ac_ubuffer;
+-      int                             ubleft = acp->ac_ubleft;
+-      int                             ubelem = acp->ac_ubelem;
+-      int                             chunkidx, clustidx;
++      int                             chunkidx;
+       int                             error = 0;
+       xfs_agino_t                     agino;
+-      for (agino = irbp->ir_startino, chunkidx = clustidx = 0;
+-           XFS_BULKSTAT_UBLEFT(ubleft) &&
+-           irbp->ir_freecount < XFS_INODES_PER_CHUNK;
+-           chunkidx++, clustidx++, agino++) {
+-              int             fmterror;       /* bulkstat formatter result */
++      agino = irbp->ir_startino;
++      for (chunkidx = 0; chunkidx < XFS_INODES_PER_CHUNK;
++           chunkidx++, agino++) {
++              int             fmterror;
+               int             ubused;
+               xfs_ino_t       ino = XFS_AGINO_TO_INO(mp, agno, agino);
+-              ASSERT(chunkidx < XFS_INODES_PER_CHUNK);
+-
+               /* Skip if this inode is free */
+               if (XFS_INOBT_MASK(chunkidx) & irbp->ir_free) {
+                       *lastino = ino;
+                       continue;
+               }
+-              /*
+-               * Count used inodes as free so we can tell when the
+-               * chunk is used up.
+-               */
+-              irbp->ir_freecount++;
+-
+               /* Get the inode and fill in a single buffer */
+               ubused = statstruct_size;
+-              error = formatter(mp, ino, *ubufp, ubleft, &ubused, &fmterror);
+-              if (fmterror == BULKSTAT_RV_NOTHING) {
+-                      if (error && error != -ENOENT && error != -EINVAL) {
+-                              ubleft = 0;
+-                              break;
+-                      }
+-                      *lastino = ino;
+-                      continue;
+-              }
+-              if (fmterror == BULKSTAT_RV_GIVEUP) {
+-                      ubleft = 0;
++              error = formatter(mp, ino, *ubufp, acp->ac_ubleft,
++                                &ubused, &fmterror);
++              if (fmterror == BULKSTAT_RV_GIVEUP ||
++                  (error && error != -ENOENT && error != -EINVAL)) {
++                      acp->ac_ubleft = 0;
+                       ASSERT(error);
+                       break;
+               }
+-              if (*ubufp)
+-                      *ubufp += ubused;
+-              ubleft -= ubused;
+-              ubelem++;
++
++              /* be careful not to leak error if at end of chunk */
++              if (fmterror == BULKSTAT_RV_NOTHING || error) {
++                      *lastino = ino;
++                      error = 0;
++                      continue;
++              }
++
++              *ubufp += ubused;
++              acp->ac_ubleft -= ubused;
++              acp->ac_ubelem++;
+               *lastino = ino;
+-      }
+-      acp->ac_ubleft = ubleft;
+-      acp->ac_ubelem = ubelem;
++              if (acp->ac_ubleft < statstruct_size)
++                      break;
++      }
+       return error;
+ }
diff --git a/queue-3.17/xfs-bulkstat-chunk-formatting-cursor-is-broken.patch b/queue-3.17/xfs-bulkstat-chunk-formatting-cursor-is-broken.patch
new file mode 100644 (file)
index 0000000..2fbe046
--- /dev/null
@@ -0,0 +1,223 @@
+From bf4a5af20d25ecc8876978ad34b8db83b4235f3c Mon Sep 17 00:00:00 2001
+From: Dave Chinner <dchinner@redhat.com>
+Date: Fri, 7 Nov 2014 08:30:30 +1100
+Subject: xfs: bulkstat chunk formatting cursor is broken
+
+From: Dave Chinner <dchinner@redhat.com>
+
+commit bf4a5af20d25ecc8876978ad34b8db83b4235f3c upstream.
+
+The xfs_bulkstat_agichunk formatting cursor takes buffer values from
+the main loop and passes them via the structure to the chunk
+formatter, and the writes the changed values back into the main loop
+local variables. Unfortunately, this complex dance is full of corner
+cases that aren't handled correctly.
+
+The biggest problem is that it is double handling the information in
+both the main loop and the chunk formatting function, leading to
+inconsistent updates and endless loops where progress is not made.
+
+To fix this, push the struct xfs_bulkstat_agichunk outwards to be
+the primary holder of user buffer information. this removes the
+double handling in the main loop.
+
+Also, pass the last inode processed by the chunk formatter as a
+separate parameter as it purely an output variable and is not
+related to the user buffer consumption cursor.
+
+Finally, the chunk formatting code is not shared by anyone, so make
+it local to xfs_itable.c.
+
+Signed-off-by: Dave Chinner <dchinner@redhat.com>
+Reviewed-by: Brian Foster <bfoster@redhat.com>
+Signed-off-by: Dave Chinner <david@fromorbit.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ fs/xfs/xfs_itable.c |   59 ++++++++++++++++++++++++----------------------------
+ fs/xfs/xfs_itable.h |   16 --------------
+ 2 files changed, 28 insertions(+), 47 deletions(-)
+
+--- a/fs/xfs/xfs_itable.c
++++ b/fs/xfs/xfs_itable.c
+@@ -262,20 +262,26 @@ xfs_bulkstat_grab_ichunk(
+ #define XFS_BULKSTAT_UBLEFT(ubleft)   ((ubleft) >= statstruct_size)
++struct xfs_bulkstat_agichunk {
++      char            __user **ac_ubuffer;/* pointer into user's buffer */
++      int             ac_ubleft;      /* bytes left in user's buffer */
++      int             ac_ubelem;      /* spaces used in user's buffer */
++};
++
+ /*
+  * Process inodes in chunk with a pointer to a formatter function
+  * that will iget the inode and fill in the appropriate structure.
+  */
+-int
++static int
+ xfs_bulkstat_ag_ichunk(
+       struct xfs_mount                *mp,
+       xfs_agnumber_t                  agno,
+       struct xfs_inobt_rec_incore     *irbp,
+       bulkstat_one_pf                 formatter,
+       size_t                          statstruct_size,
+-      struct xfs_bulkstat_agichunk    *acp)
++      struct xfs_bulkstat_agichunk    *acp,
++      xfs_ino_t                       *lastino)
+ {
+-      xfs_ino_t                       lastino = acp->ac_lastino;
+       char                            __user **ubufp = acp->ac_ubuffer;
+       int                             ubleft = acp->ac_ubleft;
+       int                             ubelem = acp->ac_ubelem;
+@@ -295,7 +301,7 @@ xfs_bulkstat_ag_ichunk(
+               /* Skip if this inode is free */
+               if (XFS_INOBT_MASK(chunkidx) & irbp->ir_free) {
+-                      lastino = ino;
++                      *lastino = ino;
+                       continue;
+               }
+@@ -313,7 +319,7 @@ xfs_bulkstat_ag_ichunk(
+                               ubleft = 0;
+                               break;
+                       }
+-                      lastino = ino;
++                      *lastino = ino;
+                       continue;
+               }
+               if (fmterror == BULKSTAT_RV_GIVEUP) {
+@@ -325,10 +331,9 @@ xfs_bulkstat_ag_ichunk(
+                       *ubufp += ubused;
+               ubleft -= ubused;
+               ubelem++;
+-              lastino = ino;
++              *lastino = ino;
+       }
+-      acp->ac_lastino = lastino;
+       acp->ac_ubleft = ubleft;
+       acp->ac_ubelem = ubelem;
+@@ -355,7 +360,6 @@ xfs_bulkstat(
+       xfs_btree_cur_t         *cur;   /* btree cursor for ialloc btree */
+       int                     end_of_ag; /* set if we've seen the ag end */
+       int                     error;  /* error code */
+-      int                     fmterror;/* bulkstat formatter result */
+       int                     icount; /* count of inodes good in irbuf */
+       size_t                  irbsize; /* size of irec buffer in bytes */
+       xfs_ino_t               ino;    /* inode number (filesystem) */
+@@ -366,10 +370,8 @@ xfs_bulkstat(
+       int                     nirbuf; /* size of irbuf */
+       int                     rval;   /* return value error code */
+       int                     ubcount; /* size of user's buffer */
+-      int                     ubleft; /* bytes left in user's buffer */
+-      char                    __user *ubufp;  /* pointer into user's buffer */
+-      int                     ubelem; /* spaces used in user's buffer */
+       int                     stat;
++      struct xfs_bulkstat_agichunk ac;
+       /*
+        * Get the last inode value, see if there's nothing to do.
+@@ -386,11 +388,13 @@ xfs_bulkstat(
+       }
+       ubcount = *ubcountp; /* statstruct's */
+-      ubleft = ubcount * statstruct_size; /* bytes */
+-      *ubcountp = ubelem = 0;
++      ac.ac_ubuffer = &ubuffer;
++      ac.ac_ubleft = ubcount * statstruct_size; /* bytes */;
++      ac.ac_ubelem = 0;
++
++      *ubcountp = 0;
+       *done = 0;
+-      fmterror = 0;
+-      ubufp = ubuffer;
++
+       irbuf = kmem_zalloc_greedy(&irbsize, PAGE_SIZE, PAGE_SIZE * 4);
+       if (!irbuf)
+               return -ENOMEM;
+@@ -402,7 +406,7 @@ xfs_bulkstat(
+        * inode returned; 0 means start of the allocation group.
+        */
+       rval = 0;
+-      while (XFS_BULKSTAT_UBLEFT(ubleft) && agno < mp->m_sb.sb_agcount) {
++      while (XFS_BULKSTAT_UBLEFT(ac.ac_ubleft) && agno < mp->m_sb.sb_agcount) {
+               cond_resched();
+               error = xfs_ialloc_read_agi(mp, NULL, agno, &agbp);
+               if (error)
+@@ -497,28 +501,21 @@ del_cursor:
+                */
+               irbufend = irbp;
+               for (irbp = irbuf;
+-                   irbp < irbufend && XFS_BULKSTAT_UBLEFT(ubleft); irbp++) {
+-                      struct xfs_bulkstat_agichunk ac;
+-
+-                      ac.ac_lastino = lastino;
+-                      ac.ac_ubuffer = &ubuffer;
+-                      ac.ac_ubleft = ubleft;
+-                      ac.ac_ubelem = ubelem;
++                   irbp < irbufend && XFS_BULKSTAT_UBLEFT(ac.ac_ubleft);
++                   irbp++) {
+                       error = xfs_bulkstat_ag_ichunk(mp, agno, irbp,
+-                                      formatter, statstruct_size, &ac);
++                                      formatter, statstruct_size, &ac,
++                                      &lastino);
+                       if (error)
+                               rval = error;
+-                      lastino = ac.ac_lastino;
+-                      ubleft = ac.ac_ubleft;
+-                      ubelem = ac.ac_ubelem;
+-
+                       cond_resched();
+               }
++
+               /*
+                * Set up for the next loop iteration.
+                */
+-              if (XFS_BULKSTAT_UBLEFT(ubleft)) {
++              if (XFS_BULKSTAT_UBLEFT(ac.ac_ubleft)) {
+                       if (end_of_ag) {
+                               agno++;
+                               agino = 0;
+@@ -531,11 +528,11 @@ del_cursor:
+        * Done, we're either out of filesystem or space to put the data.
+        */
+       kmem_free(irbuf);
+-      *ubcountp = ubelem;
++      *ubcountp = ac.ac_ubelem;
+       /*
+        * Found some inodes, return them now and return the error next time.
+        */
+-      if (ubelem)
++      if (ac.ac_ubelem)
+               rval = 0;
+       if (agno >= mp->m_sb.sb_agcount) {
+               /*
+--- a/fs/xfs/xfs_itable.h
++++ b/fs/xfs/xfs_itable.h
+@@ -30,22 +30,6 @@ typedef int (*bulkstat_one_pf)(struct xf
+                              int              *ubused,
+                              int              *stat);
+-struct xfs_bulkstat_agichunk {
+-      xfs_ino_t       ac_lastino;     /* last inode returned */
+-      char            __user **ac_ubuffer;/* pointer into user's buffer */
+-      int             ac_ubleft;      /* bytes left in user's buffer */
+-      int             ac_ubelem;      /* spaces used in user's buffer */
+-};
+-
+-int
+-xfs_bulkstat_ag_ichunk(
+-      struct xfs_mount                *mp,
+-      xfs_agnumber_t                  agno,
+-      struct xfs_inobt_rec_incore     *irbp,
+-      bulkstat_one_pf                 formatter,
+-      size_t                          statstruct_size,
+-      struct xfs_bulkstat_agichunk    *acp);
+-
+ /*
+  * Values for stat return value.
+  */
diff --git a/queue-3.17/xfs-bulkstat-doesn-t-release-agi-buffer-on-error.patch b/queue-3.17/xfs-bulkstat-doesn-t-release-agi-buffer-on-error.patch
new file mode 100644 (file)
index 0000000..51ccc80
--- /dev/null
@@ -0,0 +1,75 @@
+From a6bbce54efa9145dbcf3029c885549f7ebc40a3b Mon Sep 17 00:00:00 2001
+From: Dave Chinner <dchinner@redhat.com>
+Date: Wed, 29 Oct 2014 08:22:18 +1100
+Subject: xfs: bulkstat doesn't release AGI buffer on error
+
+From: Dave Chinner <dchinner@redhat.com>
+
+commit a6bbce54efa9145dbcf3029c885549f7ebc40a3b upstream.
+
+The recent refactoring of the bulkstat code left a small landmine in
+the code. If a inobt read fails, then the tree walk is aborted and
+returns without releasing the AGI buffer or freeing the cursor. This
+can lead to a subsequent bulkstat call hanging trying to grab the
+AGI buffer again.
+
+Signed-off-by: Dave Chinner <dchinner@redhat.com>
+Reviewed-by: Brian Foster <bfoster@redhat.com>
+Reviewed-by: Eric Sandeen <sandeen@redhat.com>
+Signed-off-by: Dave Chinner <david@fromorbit.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ fs/xfs/xfs_itable.c |   16 ++++++++++------
+ 1 file changed, 10 insertions(+), 6 deletions(-)
+
+--- a/fs/xfs/xfs_itable.c
++++ b/fs/xfs/xfs_itable.c
+@@ -427,7 +427,7 @@ xfs_bulkstat(
+                       error = xfs_bulkstat_grab_ichunk(cur, agino, &icount, &r);
+                       if (error)
+-                              break;
++                              goto del_cursor;
+                       if (icount) {
+                               irbp->ir_startino = r.ir_startino;
+                               irbp->ir_freecount = r.ir_freecount;
+@@ -442,7 +442,7 @@ xfs_bulkstat(
+                       error = xfs_inobt_lookup(cur, 0, XFS_LOOKUP_GE, &tmp);
+               }
+               if (error)
+-                      break;
++                      goto del_cursor;
+               /*
+                * Loop through inode btree records in this ag,
+@@ -454,7 +454,7 @@ xfs_bulkstat(
+                       error = xfs_inobt_get_rec(cur, &r, &i);
+                       if (error || i == 0) {
+                               end_of_ag = 1;
+-                              break;
++                              goto del_cursor;
+                       }
+                       /*
+@@ -476,13 +476,17 @@ xfs_bulkstat(
+                       error = xfs_btree_increment(cur, 0, &tmp);
+                       cond_resched();
+               }
++
+               /*
+-               * Drop the btree buffers and the agi buffer.
+-               * We can't hold any of the locks these represent
+-               * when calling iget.
++               * Drop the btree buffers and the agi buffer as we can't hold any
++               * of the locks these represent when calling iget. If there is a
++               * pending error, then we are done.
+                */
++del_cursor:
+               xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
+               xfs_buf_relse(agbp);
++              if (error)
++                      break;
+               /*
+                * Now format all the good inodes into the user's buffer.
+                */
diff --git a/queue-3.17/xfs-bulkstat-error-handling-is-broken.patch b/queue-3.17/xfs-bulkstat-error-handling-is-broken.patch
new file mode 100644 (file)
index 0000000..3cb55de
--- /dev/null
@@ -0,0 +1,120 @@
+From febe3cbe38b0bc0a925906dc90e8d59048851f87 Mon Sep 17 00:00:00 2001
+From: Dave Chinner <dchinner@redhat.com>
+Date: Fri, 7 Nov 2014 08:31:15 +1100
+Subject: xfs: bulkstat error handling is broken
+
+From: Dave Chinner <dchinner@redhat.com>
+
+commit febe3cbe38b0bc0a925906dc90e8d59048851f87 upstream.
+
+The error propagation is a horror - xfs_bulkstat() returns
+a rval variable which is only set if there are formatter errors. Any
+sort of btree walk error or corruption will cause the bulkstat walk
+to terminate but will not pass an error back to userspace. Worse
+is the fact that formatter errors will also be ignored if any inodes
+were correctly formatted into the user buffer.
+
+Hence bulkstat can fail badly yet still report success to userspace.
+This causes significant issues with xfsdump not dumping everything
+in the filesystem yet reporting success. It's not until a restore
+fails that there is any indication that the dump was bad and tha
+bulkstat failed. This patch now triggers xfsdump to fail with
+bulkstat errors rather than silently missing files in the dump.
+
+This now causes bulkstat to fail when the lastino cookie does not
+fall inside an existing inode chunk. The pre-3.17 code tolerated
+that error by allowing the code to move to the next inode chunk
+as the agino target is guaranteed to fall into the next btree
+record.
+
+With the fixes up to this point in the series, xfsdump now passes on
+the troublesome filesystem image that exposes all these bugs.
+
+Signed-off-by: Dave Chinner <dchinner@redhat.com>
+Reviewed-by: Brian Foster <bfoster@redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ fs/xfs/xfs_itable.c |   29 +++++++++++++++++++----------
+ 1 file changed, 19 insertions(+), 10 deletions(-)
+
+--- a/fs/xfs/xfs_itable.c
++++ b/fs/xfs/xfs_itable.c
+@@ -236,8 +236,10 @@ xfs_bulkstat_grab_ichunk(
+       XFS_WANT_CORRUPTED_RETURN(stat == 1);
+       /* Check if the record contains the inode in request */
+-      if (irec->ir_startino + XFS_INODES_PER_CHUNK <= agino)
+-              return -EINVAL;
++      if (irec->ir_startino + XFS_INODES_PER_CHUNK <= agino) {
++              *icount = 0;
++              return 0;
++      }
+       idx = agino - irec->ir_startino + 1;
+       if (idx < XFS_INODES_PER_CHUNK &&
+@@ -352,7 +354,6 @@ xfs_bulkstat(
+       xfs_inobt_rec_incore_t  *irbuf; /* start of irec buffer */
+       xfs_ino_t               lastino; /* last inode number returned */
+       int                     nirbuf; /* size of irbuf */
+-      int                     rval;   /* return value error code */
+       int                     ubcount; /* size of user's buffer */
+       struct xfs_bulkstat_agichunk ac;
+       int                     error = 0;
+@@ -388,7 +389,6 @@ xfs_bulkstat(
+        * Loop over the allocation groups, starting from the last
+        * inode returned; 0 means start of the allocation group.
+        */
+-      rval = 0;
+       while (agno < mp->m_sb.sb_agcount) {
+               struct xfs_inobt_rec_incore     *irbp = irbuf;
+               struct xfs_inobt_rec_incore     *irbufend = irbuf + nirbuf;
+@@ -491,13 +491,16 @@ del_cursor:
+                                       formatter, statstruct_size, &ac,
+                                       &lastino);
+                       if (error)
+-                              rval = error;
++                              break;
+                       cond_resched();
+               }
+-              /* If we've run out of space, we are done */
+-              if (ac.ac_ubleft < statstruct_size)
++              /*
++               * If we've run out of space or had a formatting error, we
++               * are now done
++               */
++              if (ac.ac_ubleft < statstruct_size || error)
+                       break;
+               if (end_of_ag) {
+@@ -511,11 +514,17 @@ del_cursor:
+        */
+       kmem_free(irbuf);
+       *ubcountp = ac.ac_ubelem;
++
+       /*
+-       * Found some inodes, return them now and return the error next time.
++       * We found some inodes, so clear the error status and return them.
++       * The lastino pointer will point directly at the inode that triggered
++       * any error that occurred, so on the next call the error will be
++       * triggered again and propagated to userspace as there will be no
++       * formatted inodes in the buffer.
+        */
+       if (ac.ac_ubelem)
+-              rval = 0;
++              error = 0;
++
+       if (agno >= mp->m_sb.sb_agcount) {
+               /*
+                * If we ran out of filesystem, mark lastino as off
+@@ -527,7 +536,7 @@ del_cursor:
+       } else
+               *lastinop = (xfs_ino_t)lastino;
+-      return rval;
++      return error;
+ }
+ int
diff --git a/queue-3.17/xfs-bulkstat-main-loop-logic-is-a-mess.patch b/queue-3.17/xfs-bulkstat-main-loop-logic-is-a-mess.patch
new file mode 100644 (file)
index 0000000..ad3f3e5
--- /dev/null
@@ -0,0 +1,150 @@
+From 6e57c542cb7e0e580eb53ae76a77875c7d92b4b1 Mon Sep 17 00:00:00 2001
+From: Dave Chinner <dchinner@redhat.com>
+Date: Fri, 7 Nov 2014 08:31:13 +1100
+Subject: xfs: bulkstat main loop logic is a mess
+
+From: Dave Chinner <dchinner@redhat.com>
+
+commit 6e57c542cb7e0e580eb53ae76a77875c7d92b4b1 upstream.
+
+There are a bunch of variables tha tare more wildy scoped than they
+need to be, obfuscated user buffer checks and tortured "next inode"
+tracking. This all needs cleaning up to expose the real issues that
+need fixing.
+
+Signed-off-by: Dave Chinner <dchinner@redhat.com>
+Reviewed-by: Brian Foster <bfoster@redhat.com>
+Signed-off-by: Dave Chinner <david@fromorbit.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ fs/xfs/xfs_itable.c |   56 ++++++++++++++++++++++------------------------------
+ 1 file changed, 24 insertions(+), 32 deletions(-)
+
+--- a/fs/xfs/xfs_itable.c
++++ b/fs/xfs/xfs_itable.c
+@@ -348,30 +348,23 @@ xfs_bulkstat(
+       xfs_agino_t             agino;  /* inode # in allocation group */
+       xfs_agnumber_t          agno;   /* allocation group number */
+       xfs_btree_cur_t         *cur;   /* btree cursor for ialloc btree */
+-      int                     end_of_ag; /* set if we've seen the ag end */
+-      int                     error;  /* error code */
+-      int                     icount; /* count of inodes good in irbuf */
+       size_t                  irbsize; /* size of irec buffer in bytes */
+-      xfs_ino_t               ino;    /* inode number (filesystem) */
+-      xfs_inobt_rec_incore_t  *irbp;  /* current irec buffer pointer */
+       xfs_inobt_rec_incore_t  *irbuf; /* start of irec buffer */
+-      xfs_inobt_rec_incore_t  *irbufend; /* end of good irec buffer entries */
+       xfs_ino_t               lastino; /* last inode number returned */
+       int                     nirbuf; /* size of irbuf */
+       int                     rval;   /* return value error code */
+       int                     ubcount; /* size of user's buffer */
+-      int                     stat;
+       struct xfs_bulkstat_agichunk ac;
++      int                     error = 0;
+       /*
+        * Get the last inode value, see if there's nothing to do.
+        */
+-      ino = (xfs_ino_t)*lastinop;
+-      lastino = ino;
+-      agno = XFS_INO_TO_AGNO(mp, ino);
+-      agino = XFS_INO_TO_AGINO(mp, ino);
++      lastino = *lastinop;
++      agno = XFS_INO_TO_AGNO(mp, lastino);
++      agino = XFS_INO_TO_AGINO(mp, lastino);
+       if (agno >= mp->m_sb.sb_agcount ||
+-          ino != XFS_AGINO_TO_INO(mp, agno, agino)) {
++          lastino != XFS_AGINO_TO_INO(mp, agno, agino)) {
+               *done = 1;
+               *ubcountp = 0;
+               return 0;
+@@ -396,8 +389,13 @@ xfs_bulkstat(
+        * inode returned; 0 means start of the allocation group.
+        */
+       rval = 0;
+-      while (XFS_BULKSTAT_UBLEFT(ac.ac_ubleft) && agno < mp->m_sb.sb_agcount) {
+-              cond_resched();
++      while (agno < mp->m_sb.sb_agcount) {
++              struct xfs_inobt_rec_incore     *irbp = irbuf;
++              struct xfs_inobt_rec_incore     *irbufend = irbuf + nirbuf;
++              bool                            end_of_ag = false;
++              int                             icount = 0;
++              int                             stat;
++
+               error = xfs_ialloc_read_agi(mp, NULL, agno, &agbp);
+               if (error)
+                       break;
+@@ -407,10 +405,6 @@ xfs_bulkstat(
+                */
+               cur = xfs_inobt_init_cursor(mp, NULL, agbp, agno,
+                                           XFS_BTNUM_INO);
+-              irbp = irbuf;
+-              irbufend = irbuf + nirbuf;
+-              end_of_ag = 0;
+-              icount = 0;
+               if (agino > 0) {
+                       /*
+                        * In the middle of an allocation group, we need to get
+@@ -435,7 +429,7 @@ xfs_bulkstat(
+                       error = xfs_inobt_lookup(cur, 0, XFS_LOOKUP_GE, &stat);
+               }
+               if (error || stat == 0) {
+-                      end_of_ag = 1;
++                      end_of_ag = true;
+                       goto del_cursor;
+               }
+@@ -448,7 +442,7 @@ xfs_bulkstat(
+                       error = xfs_inobt_get_rec(cur, &r, &stat);
+                       if (error || stat == 0) {
+-                              end_of_ag = 1;
++                              end_of_ag = true;
+                               goto del_cursor;
+                       }
+@@ -470,7 +464,7 @@ xfs_bulkstat(
+                       agino = r.ir_startino + XFS_INODES_PER_CHUNK;
+                       error = xfs_btree_increment(cur, 0, &stat);
+                       if (error || stat == 0) {
+-                              end_of_ag = 1;
++                              end_of_ag = true;
+                               goto del_cursor;
+                       }
+                       cond_resched();
+@@ -491,7 +485,7 @@ del_cursor:
+                */
+               irbufend = irbp;
+               for (irbp = irbuf;
+-                   irbp < irbufend && XFS_BULKSTAT_UBLEFT(ac.ac_ubleft);
++                   irbp < irbufend && ac.ac_ubleft >= statstruct_size;
+                    irbp++) {
+                       error = xfs_bulkstat_ag_ichunk(mp, agno, irbp,
+                                       formatter, statstruct_size, &ac,
+@@ -502,17 +496,15 @@ del_cursor:
+                       cond_resched();
+               }
+-              /*
+-               * Set up for the next loop iteration.
+-               */
+-              if (XFS_BULKSTAT_UBLEFT(ac.ac_ubleft)) {
+-                      if (end_of_ag) {
+-                              agno++;
+-                              agino = 0;
+-                      } else
+-                              agino = XFS_INO_TO_AGINO(mp, lastino);
+-              } else
++              /* If we've run out of space, we are done */
++              if (ac.ac_ubleft < statstruct_size)
+                       break;
++
++              if (end_of_ag) {
++                      agno++;
++                      agino = 0;
++              } else
++                      agino = XFS_INO_TO_AGINO(mp, lastino);
+       }
+       /*
+        * Done, we're either out of filesystem or space to put the data.
diff --git a/queue-3.17/xfs-check-error-during-inode-btree-iteration-in-xfs_bulkstat.patch b/queue-3.17/xfs-check-error-during-inode-btree-iteration-in-xfs_bulkstat.patch
new file mode 100644 (file)
index 0000000..f12c1dd
--- /dev/null
@@ -0,0 +1,38 @@
+From 7a19dee116c8fae7ba7a778043c245194289f5a2 Mon Sep 17 00:00:00 2001
+From: Jan Kara <jack@suse.cz>
+Date: Thu, 30 Oct 2014 10:34:52 +1100
+Subject: xfs: Check error during inode btree iteration in xfs_bulkstat()
+
+From: Jan Kara <jack@suse.cz>
+
+commit 7a19dee116c8fae7ba7a778043c245194289f5a2 upstream.
+
+xfs_bulkstat() doesn't check error return from xfs_btree_increment(). In
+case of specific fs corruption that could result in xfs_bulkstat()
+entering an infinite loop because we would be looping over the same
+chunk over and over again. Fix the problem by checking the return value
+and terminating the loop properly.
+
+Coverity-id: 1231338
+Signed-off-by: Jan Kara <jack@suse.cz>
+Reviewed-by: Jie Liu <jeff.u.liu@gmail.com>
+Signed-off-by: Dave Chinner <david@fromorbit.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ fs/xfs/xfs_itable.c |    4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/fs/xfs/xfs_itable.c
++++ b/fs/xfs/xfs_itable.c
+@@ -474,6 +474,10 @@ xfs_bulkstat(
+                        */
+                       agino = r.ir_startino + XFS_INODES_PER_CHUNK;
+                       error = xfs_btree_increment(cur, 0, &tmp);
++                      if (error) {
++                              end_of_ag = 1;
++                              goto del_cursor;
++                      }
+                       cond_resched();
+               }
diff --git a/queue-3.17/xfs-track-bulkstat-progress-by-agino.patch b/queue-3.17/xfs-track-bulkstat-progress-by-agino.patch
new file mode 100644 (file)
index 0000000..b2d7ec6
--- /dev/null
@@ -0,0 +1,198 @@
+From 002758992693ae63c04122603ea9261a0a58d728 Mon Sep 17 00:00:00 2001
+From: Dave Chinner <dchinner@redhat.com>
+Date: Fri, 7 Nov 2014 08:33:52 +1100
+Subject: xfs: track bulkstat progress by agino
+
+From: Dave Chinner <dchinner@redhat.com>
+
+commit 002758992693ae63c04122603ea9261a0a58d728 upstream.
+
+The bulkstat main loop progress is tracked by the "lastino"
+variable, which is a full 64 bit inode. However, the loop actually
+works on agno/agino pairs, and so there's a significant disconnect
+between the rest of the loop and the main cursor. Convert this to
+use the agino, and pass the agino into the chunk formatting function
+and convert it too.
+
+This gets rid of the inconsistency in the loop processing, and
+finally makes it simple for us to skip inodes at any point in the
+loop simply by incrementing the agino cursor.
+
+Signed-off-by: Dave Chinner <dchinner@redhat.com>
+Reviewed-by: Brian Foster <bfoster@redhat.com>
+Signed-off-by: Dave Chinner <david@fromorbit.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ fs/xfs/xfs_itable.c |   71 ++++++++++++++++++++++++----------------------------
+ 1 file changed, 34 insertions(+), 37 deletions(-)
+
+--- a/fs/xfs/xfs_itable.c
++++ b/fs/xfs/xfs_itable.c
+@@ -282,30 +282,31 @@ xfs_bulkstat_ag_ichunk(
+       bulkstat_one_pf                 formatter,
+       size_t                          statstruct_size,
+       struct xfs_bulkstat_agichunk    *acp,
+-      xfs_ino_t                       *lastino)
++      xfs_agino_t                     *last_agino)
+ {
+       char                            __user **ubufp = acp->ac_ubuffer;
+       int                             chunkidx;
+       int                             error = 0;
+-      xfs_agino_t                     agino;
++      xfs_agino_t                     agino = irbp->ir_startino;
+-      agino = irbp->ir_startino;
+       for (chunkidx = 0; chunkidx < XFS_INODES_PER_CHUNK;
+            chunkidx++, agino++) {
+               int             fmterror;
+               int             ubused;
+-              xfs_ino_t       ino = XFS_AGINO_TO_INO(mp, agno, agino);
++
++              /* inode won't fit in buffer, we are done */
++              if (acp->ac_ubleft < statstruct_size)
++                      break;
+               /* Skip if this inode is free */
+-              if (XFS_INOBT_MASK(chunkidx) & irbp->ir_free) {
+-                      *lastino = ino;
++              if (XFS_INOBT_MASK(chunkidx) & irbp->ir_free)
+                       continue;
+-              }
+               /* Get the inode and fill in a single buffer */
+               ubused = statstruct_size;
+-              error = formatter(mp, ino, *ubufp, acp->ac_ubleft,
+-                                &ubused, &fmterror);
++              error = formatter(mp, XFS_AGINO_TO_INO(mp, agno, agino),
++                                *ubufp, acp->ac_ubleft, &ubused, &fmterror);
++
+               if (fmterror == BULKSTAT_RV_GIVEUP ||
+                   (error && error != -ENOENT && error != -EINVAL)) {
+                       acp->ac_ubleft = 0;
+@@ -315,7 +316,6 @@ xfs_bulkstat_ag_ichunk(
+               /* be careful not to leak error if at end of chunk */
+               if (fmterror == BULKSTAT_RV_NOTHING || error) {
+-                      *lastino = ino;
+                       error = 0;
+                       continue;
+               }
+@@ -323,12 +323,18 @@ xfs_bulkstat_ag_ichunk(
+               *ubufp += ubused;
+               acp->ac_ubleft -= ubused;
+               acp->ac_ubelem++;
+-              *lastino = ino;
+-
+-              if (acp->ac_ubleft < statstruct_size)
+-                      break;
+       }
++      /*
++       * Post-update *last_agino. At this point, agino will always point one
++       * inode past the last inode we processed successfully. Hence we
++       * substract that inode when setting the *last_agino cursor so that we
++       * return the correct cookie to userspace. On the next bulkstat call,
++       * the inode under the lastino cookie will be skipped as we have already
++       * processed it here.
++       */
++      *last_agino = agino - 1;
++
+       return error;
+ }
+@@ -352,7 +358,6 @@ xfs_bulkstat(
+       xfs_btree_cur_t         *cur;   /* btree cursor for ialloc btree */
+       size_t                  irbsize; /* size of irec buffer in bytes */
+       xfs_inobt_rec_incore_t  *irbuf; /* start of irec buffer */
+-      xfs_ino_t               lastino; /* last inode number returned */
+       int                     nirbuf; /* size of irbuf */
+       int                     ubcount; /* size of user's buffer */
+       struct xfs_bulkstat_agichunk ac;
+@@ -361,11 +366,10 @@ xfs_bulkstat(
+       /*
+        * Get the last inode value, see if there's nothing to do.
+        */
+-      lastino = *lastinop;
+-      agno = XFS_INO_TO_AGNO(mp, lastino);
+-      agino = XFS_INO_TO_AGINO(mp, lastino);
++      agno = XFS_INO_TO_AGNO(mp, *lastinop);
++      agino = XFS_INO_TO_AGINO(mp, *lastinop);
+       if (agno >= mp->m_sb.sb_agcount ||
+-          lastino != XFS_AGINO_TO_INO(mp, agno, agino)) {
++          *lastinop != XFS_AGINO_TO_INO(mp, agno, agino)) {
+               *done = 1;
+               *ubcountp = 0;
+               return 0;
+@@ -420,7 +424,6 @@ xfs_bulkstat(
+                               irbp->ir_freecount = r.ir_freecount;
+                               irbp->ir_free = r.ir_free;
+                               irbp++;
+-                              agino = r.ir_startino + XFS_INODES_PER_CHUNK;
+                       }
+                       /* Increment to the next record */
+                       error = xfs_btree_increment(cur, 0, &stat);
+@@ -458,10 +461,6 @@ xfs_bulkstat(
+                               irbp++;
+                               icount += XFS_INODES_PER_CHUNK - r.ir_freecount;
+                       }
+-                      /*
+-                       * Set agino to after this chunk and bump the cursor.
+-                       */
+-                      agino = r.ir_startino + XFS_INODES_PER_CHUNK;
+                       error = xfs_btree_increment(cur, 0, &stat);
+                       if (error || stat == 0) {
+                               end_of_ag = true;
+@@ -481,7 +480,9 @@ del_cursor:
+               if (error)
+                       break;
+               /*
+-               * Now format all the good inodes into the user's buffer.
++               * Now format all the good inodes into the user's buffer. The
++               * call to xfs_bulkstat_ag_ichunk() sets up the agino pointer
++               * for the next loop iteration.
+                */
+               irbufend = irbp;
+               for (irbp = irbuf;
+@@ -489,7 +490,7 @@ del_cursor:
+                    irbp++) {
+                       error = xfs_bulkstat_ag_ichunk(mp, agno, irbp,
+                                       formatter, statstruct_size, &ac,
+-                                      &lastino);
++                                      &agino);
+                       if (error)
+                               break;
+@@ -506,8 +507,7 @@ del_cursor:
+               if (end_of_ag) {
+                       agno++;
+                       agino = 0;
+-              } else
+-                      agino = XFS_INO_TO_AGINO(mp, lastino);
++              }
+       }
+       /*
+        * Done, we're either out of filesystem or space to put the data.
+@@ -525,16 +525,13 @@ del_cursor:
+       if (ac.ac_ubelem)
+               error = 0;
+-      if (agno >= mp->m_sb.sb_agcount) {
+-              /*
+-               * If we ran out of filesystem, mark lastino as off
+-               * the end of the filesystem, so the next call
+-               * will return immediately.
+-               */
+-              *lastinop = (xfs_ino_t)XFS_AGINO_TO_INO(mp, agno, 0);
++      /*
++       * If we ran out of filesystem, lastino will point off the end of
++       * the filesystem so the next call will return immediately.
++       */
++      *lastinop = XFS_AGINO_TO_INO(mp, agno, agino);
++      if (agno >= mp->m_sb.sb_agcount)
+               *done = 1;
+-      } else
+-              *lastinop = (xfs_ino_t)lastino;
+       return error;
+ }