]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
4.20-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 12 Mar 2019 12:57:41 +0000 (05:57 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 12 Mar 2019 12:57:41 +0000 (05:57 -0700)
added patches:
ath9k-avoid-of-no-eeprom-quirks-without-qca-no-eeprom.patch
gfs2-fix-missed-wakeups-in-find_insert_glock.patch
staging-erofs-keep-corrupted-fs-from-crashing-kernel-in-erofs_namei.patch

queue-4.20/ath9k-avoid-of-no-eeprom-quirks-without-qca-no-eeprom.patch [new file with mode: 0644]
queue-4.20/gfs2-fix-missed-wakeups-in-find_insert_glock.patch [new file with mode: 0644]
queue-4.20/series
queue-4.20/staging-erofs-keep-corrupted-fs-from-crashing-kernel-in-erofs_namei.patch [new file with mode: 0644]

diff --git a/queue-4.20/ath9k-avoid-of-no-eeprom-quirks-without-qca-no-eeprom.patch b/queue-4.20/ath9k-avoid-of-no-eeprom-quirks-without-qca-no-eeprom.patch
new file mode 100644 (file)
index 0000000..88a834c
--- /dev/null
@@ -0,0 +1,88 @@
+From ce938231bd3b1d7af3cbd8836f084801090470e1 Mon Sep 17 00:00:00 2001
+From: "Daniel F. Dickinson" <cshored@thecshore.com>
+Date: Sat, 22 Dec 2018 01:09:13 -0500
+Subject: ath9k: Avoid OF no-EEPROM quirks without qca,no-eeprom
+
+From: Daniel F. Dickinson <cshored@thecshore.com>
+
+commit ce938231bd3b1d7af3cbd8836f084801090470e1 upstream.
+
+ath9k_of_init() function[0] was initially written on the assumption that
+if someone had an explicit ath9k OF node that "there must be something
+wrong, why would someone add an OF node if everything is fine"[1]
+(Quoting Martin Blumenstingl <martin.blumenstingl@googlemail.com>)
+
+"it turns out it's not that simple. with your requirements I'm now aware
+of two use-cases where the current code in ath9k_of_init() doesn't work
+without modifications"[1]
+
+The "your requirements" Martin speaks of is the result of the fact that I
+have a device (PowerCloud Systems CR5000) has some kind of default - not
+unique mac address - set and requires to set the correct MAC address via
+mac-address devicetree property, however:
+
+"some cards come with a physical EEPROM chip [or OTP] so "qca,no-eeprom"
+should not be set (your use-case). in this case AH_USE_EEPROM should be
+set (which is the default when there is no OF node)"[1]
+
+The other use case is:
+
+the firmware on some PowerMac G5 seems to add a OF node for the ath9k
+card automatically. depending on the EEPROM on the card AH_NO_EEP_SWAP
+should be unset (which is the default when there is no OF node). see [3]
+
+After this patch to ath9k_of_init() the new behavior will be:
+
+    if there's no OF node then everything is the same as before
+    if there's an empty OF node then ath9k will use the hardware EEPROM
+      (before ath9k would fail to initialize because no EEPROM data was
+      provided by userspace)
+    if there's an OF node with only a MAC address then ath9k will use
+      the MAC address and the hardware EEPROM (see the case above)
+    with "qca,no-eeprom" EEPROM data from userspace will be requested.
+      the behavior here will not change
+[1]
+
+Martin provides additional background on EEPROM swapping[1].
+
+Thanks to Christian Lamparter <chunkeey@gmail.com> for all his help on
+troubleshooting this issue and the basis for this patch.
+
+[0]https://elixir.bootlin.com/linux/v4.20-rc7/source/drivers/net/wireless/ath/ath9k/init.c#L615
+[1]https://github.com/openwrt/openwrt/pull/1645#issuecomment-448027058
+[2]https://github.com/openwrt/openwrt/pull/1613
+[3]https://patchwork.kernel.org/patch/10241731/
+
+Fixes: 138b41253d9c ("ath9k: parse the device configuration from an OF node")
+Reviewed-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
+Tested-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
+Signed-off-by: Daniel F. Dickinson <cshored@thecshore.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+Cc: Christian Lamparter <chunkeey@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/net/wireless/ath/ath9k/init.c |    6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/wireless/ath/ath9k/init.c
++++ b/drivers/net/wireless/ath/ath9k/init.c
+@@ -636,15 +636,15 @@ static int ath9k_of_init(struct ath_soft
+               ret = ath9k_eeprom_request(sc, eeprom_name);
+               if (ret)
+                       return ret;
++
++              ah->ah_flags &= ~AH_USE_EEPROM;
++              ah->ah_flags |= AH_NO_EEP_SWAP;
+       }
+       mac = of_get_mac_address(np);
+       if (mac)
+               ether_addr_copy(common->macaddr, mac);
+-      ah->ah_flags &= ~AH_USE_EEPROM;
+-      ah->ah_flags |= AH_NO_EEP_SWAP;
+-
+       return 0;
+ }
diff --git a/queue-4.20/gfs2-fix-missed-wakeups-in-find_insert_glock.patch b/queue-4.20/gfs2-fix-missed-wakeups-in-find_insert_glock.patch
new file mode 100644 (file)
index 0000000..29404ca
--- /dev/null
@@ -0,0 +1,38 @@
+From 605b0487f0bc1ae9963bf52ece0f5c8055186f81 Mon Sep 17 00:00:00 2001
+From: Andreas Gruenbacher <agruenba@redhat.com>
+Date: Wed, 6 Mar 2019 15:41:57 +0100
+Subject: gfs2: Fix missed wakeups in find_insert_glock
+
+From: Andreas Gruenbacher <agruenba@redhat.com>
+
+commit 605b0487f0bc1ae9963bf52ece0f5c8055186f81 upstream.
+
+Mark Syms has reported seeing tasks that are stuck waiting in
+find_insert_glock.  It turns out that struct lm_lockname contains four padding
+bytes on 64-bit architectures that function glock_waitqueue doesn't skip when
+hashing the glock name.  As a result, we can end up waking up the wrong
+waitqueue, and the waiting tasks may be stuck forever.
+
+Fix that by using ht_parms.key_len instead of sizeof(struct lm_lockname) for
+the key length.
+
+Reported-by: Mark Syms <mark.syms@citrix.com>
+Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
+Signed-off-by: Bob Peterson <rpeterso@redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ fs/gfs2/glock.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/fs/gfs2/glock.c
++++ b/fs/gfs2/glock.c
+@@ -107,7 +107,7 @@ static int glock_wake_function(wait_queu
+ static wait_queue_head_t *glock_waitqueue(struct lm_lockname *name)
+ {
+-      u32 hash = jhash2((u32 *)name, sizeof(*name) / 4, 0);
++      u32 hash = jhash2((u32 *)name, ht_parms.key_len / 4, 0);
+       return glock_wait_table + hash_32(hash, GLOCK_WAIT_TABLE_BITS);
+ }
index c1749a08c9b6dfbbc794e0864ac298879b3b43ca..897ddf4fd4010bff3313a0e5853fa397d76fd9f7 100644 (file)
@@ -162,3 +162,6 @@ media-revert-media-rc-some-events-are-dropped-by-userspace.patch
 revert-pci-pme-implement-runtime-pm-callbacks.patch
 sk_msg-always-cancel-strp-work-before-freeing-the-psock.patch
 bpf-stop-the-psock-parser-before-canceling-its-work.patch
+gfs2-fix-missed-wakeups-in-find_insert_glock.patch
+staging-erofs-keep-corrupted-fs-from-crashing-kernel-in-erofs_namei.patch
+ath9k-avoid-of-no-eeprom-quirks-without-qca-no-eeprom.patch
diff --git a/queue-4.20/staging-erofs-keep-corrupted-fs-from-crashing-kernel-in-erofs_namei.patch b/queue-4.20/staging-erofs-keep-corrupted-fs-from-crashing-kernel-in-erofs_namei.patch
new file mode 100644 (file)
index 0000000..e17418a
--- /dev/null
@@ -0,0 +1,314 @@
+From 419d6efc50e94bcf5d6b35cd8c71f79edadec564 Mon Sep 17 00:00:00 2001
+From: Gao Xiang <gaoxiang25@huawei.com>
+Date: Fri, 1 Feb 2019 20:16:31 +0800
+Subject: staging: erofs: keep corrupted fs from crashing kernel in erofs_namei()
+
+From: Gao Xiang <gaoxiang25@huawei.com>
+
+commit 419d6efc50e94bcf5d6b35cd8c71f79edadec564 upstream.
+
+As Al pointed out, "
+... and while we are at it, what happens to
+       unsigned int nameoff = le16_to_cpu(de[mid].nameoff);
+       unsigned int matched = min(startprfx, endprfx);
+
+       struct qstr dname = QSTR_INIT(data + nameoff,
+               unlikely(mid >= ndirents - 1) ?
+                       maxsize - nameoff :
+                       le16_to_cpu(de[mid + 1].nameoff) - nameoff);
+
+       /* string comparison without already matched prefix */
+       int ret = dirnamecmp(name, &dname, &matched);
+if le16_to_cpu(de[...].nameoff) is not monotonically increasing?  I.e.
+what's to prevent e.g. (unsigned)-1 ending up in dname.len?
+
+Corrupted fs image shouldn't oops the kernel.. "
+
+Revisit the related lookup flow to address the issue.
+
+Fixes: d72d1ce60174 ("staging: erofs: add namei functions")
+Cc: <stable@vger.kernel.org> # 4.19+
+Suggested-by: Al Viro <viro@ZenIV.linux.org.uk>
+Signed-off-by: Gao Xiang <gaoxiang25@huawei.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+
+---
+ drivers/staging/erofs/namei.c |  183 ++++++++++++++++++++++--------------------
+ 1 file changed, 97 insertions(+), 86 deletions(-)
+
+--- a/drivers/staging/erofs/namei.c
++++ b/drivers/staging/erofs/namei.c
+@@ -15,74 +15,77 @@
+ #include <trace/events/erofs.h>
+-/* based on the value of qn->len is accurate */
+-static inline int dirnamecmp(struct qstr *qn,
+-      struct qstr *qd, unsigned int *matched)
++struct erofs_qstr {
++      const unsigned char *name;
++      const unsigned char *end;
++};
++
++/* based on the end of qn is accurate and it must have the trailing '\0' */
++static inline int dirnamecmp(const struct erofs_qstr *qn,
++                           const struct erofs_qstr *qd,
++                           unsigned int *matched)
+ {
+-      unsigned int i = *matched, len = min(qn->len, qd->len);
+-loop:
+-      if (unlikely(i >= len)) {
+-              *matched = i;
+-              if (qn->len < qd->len) {
+-                      /*
+-                       * actually (qn->len == qd->len)
+-                       * when qd->name[i] == '\0'
+-                       */
+-                      return qd->name[i] == '\0' ? 0 : -1;
+-              }
+-              return (qn->len > qd->len);
+-      }
++      unsigned int i = *matched;
+-      if (qn->name[i] != qd->name[i]) {
+-              *matched = i;
+-              return qn->name[i] > qd->name[i] ? 1 : -1;
++      /*
++       * on-disk error, let's only BUG_ON in the debugging mode.
++       * otherwise, it will return 1 to just skip the invalid name
++       * and go on (in consideration of the lookup performance).
++       */
++      DBG_BUGON(qd->name > qd->end);
++
++      /* qd could not have trailing '\0' */
++      /* However it is absolutely safe if < qd->end */
++      while (qd->name + i < qd->end && qd->name[i] != '\0') {
++              if (qn->name[i] != qd->name[i]) {
++                      *matched = i;
++                      return qn->name[i] > qd->name[i] ? 1 : -1;
++              }
++              ++i;
+       }
+-
+-      ++i;
+-      goto loop;
++      *matched = i;
++      /* See comments in __d_alloc on the terminating NUL character */
++      return qn->name[i] == '\0' ? 0 : 1;
+ }
+-static struct erofs_dirent *find_target_dirent(
+-      struct qstr *name,
+-      u8 *data, int maxsize)
++#define nameoff_from_disk(off, sz)    (le16_to_cpu(off) & ((sz) - 1))
++
++static struct erofs_dirent *find_target_dirent(struct erofs_qstr *name,
++                                             u8 *data,
++                                             unsigned int dirblksize,
++                                             const int ndirents)
+ {
+-      unsigned int ndirents, head, back;
++      int head, back;
+       unsigned int startprfx, endprfx;
+       struct erofs_dirent *const de = (struct erofs_dirent *)data;
+-      /* make sure that maxsize is valid */
+-      BUG_ON(maxsize < sizeof(struct erofs_dirent));
+-
+-      ndirents = le16_to_cpu(de->nameoff) / sizeof(*de);
+-
+-      /* corrupted dir (may be unnecessary...) */
+-      BUG_ON(!ndirents);
+-
+-      head = 0;
++      /* since the 1st dirent has been evaluated previously */
++      head = 1;
+       back = ndirents - 1;
+       startprfx = endprfx = 0;
+       while (head <= back) {
+-              unsigned int mid = head + (back - head) / 2;
+-              unsigned int nameoff = le16_to_cpu(de[mid].nameoff);
++              const int mid = head + (back - head) / 2;
++              const int nameoff = nameoff_from_disk(de[mid].nameoff,
++                                                    dirblksize);
+               unsigned int matched = min(startprfx, endprfx);
+-
+-              struct qstr dname = QSTR_INIT(data + nameoff,
+-                      unlikely(mid >= ndirents - 1) ?
+-                              maxsize - nameoff :
+-                              le16_to_cpu(de[mid + 1].nameoff) - nameoff);
++              struct erofs_qstr dname = {
++                      .name = data + nameoff,
++                      .end = unlikely(mid >= ndirents - 1) ?
++                              data + dirblksize :
++                              data + nameoff_from_disk(de[mid + 1].nameoff,
++                                                       dirblksize)
++              };
+               /* string comparison without already matched prefix */
+               int ret = dirnamecmp(name, &dname, &matched);
+-              if (unlikely(!ret))
++              if (unlikely(!ret)) {
+                       return de + mid;
+-              else if (ret > 0) {
++              } else if (ret > 0) {
+                       head = mid + 1;
+                       startprfx = matched;
+-              } else if (unlikely(mid < 1))   /* fix "mid" overflow */
+-                      break;
+-              else {
++              } else {
+                       back = mid - 1;
+                       endprfx = matched;
+               }
+@@ -91,12 +94,12 @@ static struct erofs_dirent *find_target_
+       return ERR_PTR(-ENOENT);
+ }
+-static struct page *find_target_block_classic(
+-      struct inode *dir,
+-      struct qstr *name, int *_diff)
++static struct page *find_target_block_classic(struct inode *dir,
++                                            struct erofs_qstr *name,
++                                            int *_ndirents)
+ {
+       unsigned int startprfx, endprfx;
+-      unsigned int head, back;
++      int head, back;
+       struct address_space *const mapping = dir->i_mapping;
+       struct page *candidate = ERR_PTR(-ENOENT);
+@@ -105,41 +108,43 @@ static struct page *find_target_block_cl
+       back = inode_datablocks(dir) - 1;
+       while (head <= back) {
+-              unsigned int mid = head + (back - head) / 2;
++              const int mid = head + (back - head) / 2;
+               struct page *page = read_mapping_page(mapping, mid, NULL);
+-              if (IS_ERR(page)) {
+-exact_out:
+-                      if (!IS_ERR(candidate)) /* valid candidate */
+-                              put_page(candidate);
+-                      return page;
+-              } else {
+-                      int diff;
+-                      unsigned int ndirents, matched;
+-                      struct qstr dname;
++              if (!IS_ERR(page)) {
+                       struct erofs_dirent *de = kmap_atomic(page);
+-                      unsigned int nameoff = le16_to_cpu(de->nameoff);
+-
+-                      ndirents = nameoff / sizeof(*de);
++                      const int nameoff = nameoff_from_disk(de->nameoff,
++                                                            EROFS_BLKSIZ);
++                      const int ndirents = nameoff / sizeof(*de);
++                      int diff;
++                      unsigned int matched;
++                      struct erofs_qstr dname;
+-                      /* corrupted dir (should have one entry at least) */
+-                      BUG_ON(!ndirents || nameoff > PAGE_SIZE);
++                      if (unlikely(!ndirents)) {
++                              DBG_BUGON(1);
++                              kunmap_atomic(de);
++                              put_page(page);
++                              page = ERR_PTR(-EIO);
++                              goto out;
++                      }
+                       matched = min(startprfx, endprfx);
+                       dname.name = (u8 *)de + nameoff;
+-                      dname.len = ndirents == 1 ?
+-                              /* since the rest of the last page is 0 */
+-                              EROFS_BLKSIZ - nameoff
+-                              : le16_to_cpu(de[1].nameoff) - nameoff;
++                      if (ndirents == 1)
++                              dname.end = (u8 *)de + EROFS_BLKSIZ;
++                      else
++                              dname.end = (u8 *)de +
++                                      nameoff_from_disk(de[1].nameoff,
++                                                        EROFS_BLKSIZ);
+                       /* string comparison without already matched prefix */
+                       diff = dirnamecmp(name, &dname, &matched);
+                       kunmap_atomic(de);
+                       if (unlikely(!diff)) {
+-                              *_diff = 0;
+-                              goto exact_out;
++                              *_ndirents = 0;
++                              goto out;
+                       } else if (diff > 0) {
+                               head = mid + 1;
+                               startprfx = matched;
+@@ -147,45 +152,51 @@ exact_out:
+                               if (likely(!IS_ERR(candidate)))
+                                       put_page(candidate);
+                               candidate = page;
++                              *_ndirents = ndirents;
+                       } else {
+                               put_page(page);
+-                              if (unlikely(mid < 1))  /* fix "mid" overflow */
+-                                      break;
+-
+                               back = mid - 1;
+                               endprfx = matched;
+                       }
++                      continue;
+               }
++out:          /* free if the candidate is valid */
++              if (!IS_ERR(candidate))
++                      put_page(candidate);
++              return page;
+       }
+-      *_diff = 1;
+       return candidate;
+ }
+ int erofs_namei(struct inode *dir,
+-      struct qstr *name,
+-      erofs_nid_t *nid, unsigned int *d_type)
++              struct qstr *name,
++              erofs_nid_t *nid, unsigned int *d_type)
+ {
+-      int diff;
++      int ndirents;
+       struct page *page;
+-      u8 *data;
++      void *data;
+       struct erofs_dirent *de;
++      struct erofs_qstr qn;
+       if (unlikely(!dir->i_size))
+               return -ENOENT;
+-      diff = 1;
+-      page = find_target_block_classic(dir, name, &diff);
++      qn.name = name->name;
++      qn.end = name->name + name->len;
++
++      ndirents = 0;
++      page = find_target_block_classic(dir, &qn, &ndirents);
+       if (unlikely(IS_ERR(page)))
+               return PTR_ERR(page);
+       data = kmap_atomic(page);
+       /* the target page has been mapped */
+-      de = likely(diff) ?
+-              /* since the rest of the last page is 0 */
+-              find_target_dirent(name, data, EROFS_BLKSIZ) :
+-              (struct erofs_dirent *)data;
++      if (ndirents)
++              de = find_target_dirent(&qn, data, EROFS_BLKSIZ, ndirents);
++      else
++              de = (struct erofs_dirent *)data;
+       if (likely(!IS_ERR(de))) {
+               *nid = le64_to_cpu(de->nid);