From: Greg Kroah-Hartman Date: Tue, 12 Mar 2019 12:57:41 +0000 (-0700) Subject: 4.20-stable patches X-Git-Tag: v5.0.2~13 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=445cfb10b8550c989e3b1915818f99fa0dbff74f;p=thirdparty%2Fkernel%2Fstable-queue.git 4.20-stable patches 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 --- 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 index 00000000000..88a834cbf3f --- /dev/null +++ b/queue-4.20/ath9k-avoid-of-no-eeprom-quirks-without-qca-no-eeprom.patch @@ -0,0 +1,88 @@ +From ce938231bd3b1d7af3cbd8836f084801090470e1 Mon Sep 17 00:00:00 2001 +From: "Daniel F. Dickinson" +Date: Sat, 22 Dec 2018 01:09:13 -0500 +Subject: ath9k: Avoid OF no-EEPROM quirks without qca,no-eeprom + +From: Daniel F. Dickinson + +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 ) + +"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 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 +Tested-by: Martin Blumenstingl +Signed-off-by: Daniel F. Dickinson +Signed-off-by: Kalle Valo +Cc: Christian Lamparter +Signed-off-by: Greg Kroah-Hartman + +--- + 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 index 00000000000..29404caa7fe --- /dev/null +++ b/queue-4.20/gfs2-fix-missed-wakeups-in-find_insert_glock.patch @@ -0,0 +1,38 @@ +From 605b0487f0bc1ae9963bf52ece0f5c8055186f81 Mon Sep 17 00:00:00 2001 +From: Andreas Gruenbacher +Date: Wed, 6 Mar 2019 15:41:57 +0100 +Subject: gfs2: Fix missed wakeups in find_insert_glock + +From: Andreas Gruenbacher + +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 +Signed-off-by: Andreas Gruenbacher +Signed-off-by: Bob Peterson +Signed-off-by: Greg Kroah-Hartman + +--- + 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); + } diff --git a/queue-4.20/series b/queue-4.20/series index c1749a08c9b..897ddf4fd40 100644 --- a/queue-4.20/series +++ b/queue-4.20/series @@ -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 index 00000000000..e17418af0a5 --- /dev/null +++ b/queue-4.20/staging-erofs-keep-corrupted-fs-from-crashing-kernel-in-erofs_namei.patch @@ -0,0 +1,314 @@ +From 419d6efc50e94bcf5d6b35cd8c71f79edadec564 Mon Sep 17 00:00:00 2001 +From: Gao Xiang +Date: Fri, 1 Feb 2019 20:16:31 +0800 +Subject: staging: erofs: keep corrupted fs from crashing kernel in erofs_namei() + +From: Gao Xiang + +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: # 4.19+ +Suggested-by: Al Viro +Signed-off-by: Gao Xiang +Signed-off-by: Greg Kroah-Hartman + + +--- + 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 + +-/* 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);