--- /dev/null
+From 5ecc09ed8ee97b25ce54ed8b04f58ded5529bbfe Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 23 Aug 2023 20:40:51 +0900
+Subject: ALSA: hda/realtek: Add quirk for HP Victus 16-d1xxx to enable mute
+ LED
+
+From: SungHwan Jung <onenowy@gmail.com>
+
+[ Upstream commit 93dc18e11b1ab2d485b69f91c973e6b83e47ebd0 ]
+
+This quirk enables mute LED on HP Victus 16-d1xxx (8A25) laptops, which
+use ALC245 codec.
+
+Signed-off-by: SungHwan Jung <onenowy@gmail.com>
+Link: https://lore.kernel.org/r/20230823114051.3921-1-onenowy@gmail.com
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Stable-dep-of: 41b07476da38 ("ALSA: hda/realtek - ALC287 Realtek I2S speaker platform support")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ sound/pci/hda/patch_realtek.c | 22 ++++++++++++++++++++++
+ 1 file changed, 22 insertions(+)
+
+diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
+index 7d549229d0b95..e81bc0c026eba 100644
+--- a/sound/pci/hda/patch_realtek.c
++++ b/sound/pci/hda/patch_realtek.c
+@@ -4639,6 +4639,22 @@ static void alc236_fixup_hp_mute_led_coefbit2(struct hda_codec *codec,
+ }
+ }
+
++static void alc245_fixup_hp_mute_led_coefbit(struct hda_codec *codec,
++ const struct hda_fixup *fix,
++ int action)
++{
++ struct alc_spec *spec = codec->spec;
++
++ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
++ spec->mute_led_polarity = 0;
++ spec->mute_led_coef.idx = 0x0b;
++ spec->mute_led_coef.mask = 3 << 2;
++ spec->mute_led_coef.on = 2 << 2;
++ spec->mute_led_coef.off = 1 << 2;
++ snd_hda_gen_add_mute_led_cdev(codec, coef_mute_led_set);
++ }
++}
++
+ /* turn on/off mic-mute LED per capture hook by coef bit */
+ static int coef_micmute_led_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+@@ -7289,6 +7305,7 @@ enum {
+ ALC236_FIXUP_DELL_DUAL_CODECS,
+ ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI,
+ ALC287_FIXUP_TAS2781_I2C,
++ ALC245_FIXUP_HP_MUTE_LED_COEFBIT,
+ };
+
+ /* A special fixup for Lenovo C940 and Yoga Duet 7;
+@@ -9364,6 +9381,10 @@ static const struct hda_fixup alc269_fixups[] = {
+ .chained = true,
+ .chain_id = ALC269_FIXUP_THINKPAD_ACPI,
+ },
++ [ALC245_FIXUP_HP_MUTE_LED_COEFBIT] = {
++ .type = HDA_FIXUP_FUNC,
++ .v.func = alc245_fixup_hp_mute_led_coefbit,
++ },
+ };
+
+ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
+@@ -9630,6 +9651,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x103c, 0x89c6, "Zbook Fury 17 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x89ca, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x89d3, "HP EliteBook 645 G9 (MB 89D2)", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
++ SND_PCI_QUIRK(0x103c, 0x8a25, "HP Victus 16-d1xxx (MB 8A25)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT),
+ SND_PCI_QUIRK(0x103c, 0x8a78, "HP Dev One", ALC285_FIXUP_HP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x103c, 0x8aa0, "HP ProBook 440 G9 (MB 8A9E)", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8aa3, "HP ProBook 450 G9 (MB 8AA1)", ALC236_FIXUP_HP_GPIO_LED),
+--
+2.40.1
+
--- /dev/null
+From 7028ce65c3db5104a485063bfaf593e5c3d0e817 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 24 Aug 2023 20:39:48 +0200
+Subject: ALSA: hda/realtek: Add quirk for mute LEDs on HP ENVY x360 15-eu0xxx
+
+From: Fabian Vogt <fabian@ritter-vogt.de>
+
+[ Upstream commit c99c26b16c1544534ebd6a5f27a034f3e44d2597 ]
+
+The LED for the mic mute button is controlled by GPIO2.
+The mute button LED is slightly more complex, it's controlled by two bits
+in coeff 0x0b.
+
+Signed-off-by: Fabian Vogt <fabian@ritter-vogt.de>
+Link: https://lore.kernel.org/r/2693091.mvXUDI8C0e@fabians-envy
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Stable-dep-of: 41b07476da38 ("ALSA: hda/realtek - ALC287 Realtek I2S speaker platform support")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ sound/pci/hda/patch_realtek.c | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
+index e81bc0c026eba..e01af481e0d0d 100644
+--- a/sound/pci/hda/patch_realtek.c
++++ b/sound/pci/hda/patch_realtek.c
+@@ -7306,6 +7306,7 @@ enum {
+ ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI,
+ ALC287_FIXUP_TAS2781_I2C,
+ ALC245_FIXUP_HP_MUTE_LED_COEFBIT,
++ ALC245_FIXUP_HP_X360_MUTE_LEDS,
+ };
+
+ /* A special fixup for Lenovo C940 and Yoga Duet 7;
+@@ -9385,6 +9386,12 @@ static const struct hda_fixup alc269_fixups[] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc245_fixup_hp_mute_led_coefbit,
+ },
++ [ALC245_FIXUP_HP_X360_MUTE_LEDS] = {
++ .type = HDA_FIXUP_FUNC,
++ .v.func = alc245_fixup_hp_mute_led_coefbit,
++ .chained = true,
++ .chain_id = ALC245_FIXUP_HP_GPIO_LED
++ },
+ };
+
+ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
+@@ -9620,6 +9627,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x103c, 0x8870, "HP ZBook Fury 15.6 Inch G8 Mobile Workstation PC", ALC285_FIXUP_HP_GPIO_AMP_INIT),
+ SND_PCI_QUIRK(0x103c, 0x8873, "HP ZBook Studio 15.6 Inch G8 Mobile Workstation PC", ALC285_FIXUP_HP_GPIO_AMP_INIT),
+ SND_PCI_QUIRK(0x103c, 0x887a, "HP Laptop 15s-eq2xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
++ SND_PCI_QUIRK(0x103c, 0x888a, "HP ENVY x360 Convertible 15-eu0xxx", ALC245_FIXUP_HP_X360_MUTE_LEDS),
+ SND_PCI_QUIRK(0x103c, 0x888d, "HP ZBook Power 15.6 inch G8 Mobile Workstation PC", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8895, "HP EliteBook 855 G8 Notebook PC", ALC285_FIXUP_HP_SPEAKERS_MICMUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x8896, "HP EliteBook 855 G8 Notebook PC", ALC285_FIXUP_HP_MUTE_LED),
+--
+2.40.1
+
--- /dev/null
+From 180826e595afdaf93f61a7d8929efb5e586ee9b0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 Sep 2023 16:50:41 +0800
+Subject: ALSA: hda/realtek - ALC287 I2S speaker platform support
+
+From: Kailang Yang <kailang@realtek.com>
+
+[ Upstream commit e43252db7e207a2e194e6a4883a43a31a776a968 ]
+
+0x17 was only speaker pin, DAC assigned will be 0x03. Headphone
+assigned to 0x02.
+Playback via headphone will get EQ filter processing. So,it needs to
+swap DAC.
+
+Tested-by: Mark Pearson <mpearson@lenovo.com>
+Signed-off-by: Kailang Yang <kailang@realtek.com>
+Link: https://lore.kernel.org/r/4e4cfa1b3b4c46838aecafc6e8b6f876@realtek.com
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Stable-dep-of: 41b07476da38 ("ALSA: hda/realtek - ALC287 Realtek I2S speaker platform support")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ sound/pci/hda/patch_realtek.c | 30 ++++++++++++++++++++++++++++++
+ 1 file changed, 30 insertions(+)
+
+diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
+index e01af481e0d0d..62476b6fd248c 100644
+--- a/sound/pci/hda/patch_realtek.c
++++ b/sound/pci/hda/patch_realtek.c
+@@ -7046,6 +7046,27 @@ static void alc295_fixup_dell_inspiron_top_speakers(struct hda_codec *codec,
+ }
+ }
+
++/* Forcibly assign NID 0x03 to HP while NID 0x02 to SPK */
++static void alc287_fixup_bind_dacs(struct hda_codec *codec,
++ const struct hda_fixup *fix, int action)
++{
++ struct alc_spec *spec = codec->spec;
++ static const hda_nid_t conn[] = { 0x02, 0x03 }; /* exclude 0x06 */
++ static const hda_nid_t preferred_pairs[] = {
++ 0x17, 0x02, 0x21, 0x03, 0
++ };
++
++ if (action != HDA_FIXUP_ACT_PRE_PROBE)
++ return;
++
++ snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn);
++ spec->gen.preferred_dacs = preferred_pairs;
++ spec->gen.auto_mute_via_amp = 1;
++ snd_hda_codec_write_cache(codec, 0x14, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
++ 0x0); /* Make sure 0x14 was disable */
++}
++
++
+ enum {
+ ALC269_FIXUP_GPIO2,
+ ALC269_FIXUP_SONY_VAIO,
+@@ -7307,6 +7328,7 @@ enum {
+ ALC287_FIXUP_TAS2781_I2C,
+ ALC245_FIXUP_HP_MUTE_LED_COEFBIT,
+ ALC245_FIXUP_HP_X360_MUTE_LEDS,
++ ALC287_FIXUP_THINKPAD_I2S_SPK,
+ };
+
+ /* A special fixup for Lenovo C940 and Yoga Duet 7;
+@@ -9392,6 +9414,10 @@ static const struct hda_fixup alc269_fixups[] = {
+ .chained = true,
+ .chain_id = ALC245_FIXUP_HP_GPIO_LED
+ },
++ [ALC287_FIXUP_THINKPAD_I2S_SPK] = {
++ .type = HDA_FIXUP_FUNC,
++ .v.func = alc287_fixup_bind_dacs,
++ },
+ };
+
+ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
+@@ -10514,6 +10540,10 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
+ {0x17, 0x90170111},
+ {0x19, 0x03a11030},
+ {0x21, 0x03211020}),
++ SND_HDA_PIN_QUIRK(0x10ec0287, 0x17aa, "Lenovo", ALC287_FIXUP_THINKPAD_I2S_SPK,
++ {0x17, 0x90170110},
++ {0x19, 0x03a11030},
++ {0x21, 0x03211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0286, 0x1025, "Acer", ALC286_FIXUP_ACER_AIO_MIC_NO_PRESENCE,
+ {0x12, 0x90a60130},
+ {0x17, 0x90170110},
+--
+2.40.1
+
--- /dev/null
+From 948225e3347ea22b5e5ad668c64ed799bb47d8e4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 19 Sep 2023 16:27:16 +0800
+Subject: ALSA: hda/realtek - ALC287 Realtek I2S speaker platform support
+
+From: Kailang Yang <kailang@realtek.com>
+
+[ Upstream commit 41b07476da38ac2878a14e5b8fe0312c41ea36e3 ]
+
+New platform SSID:0x231f.
+
+0x17 was only speaker pin, DAC assigned will be 0x03. Headphone
+assigned to 0x02.
+Playback via headphone will get EQ filter processing.
+So, it needs to swap DAC.
+
+Signed-off-by: Kailang Yang <kailang@realtek.com>
+Cc: <stable@vger.kernel.org>
+Link: https://lore.kernel.org/r/8d63c6e360124e3ea2523753050e6f05@realtek.com
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ sound/pci/hda/patch_realtek.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
+index 62476b6fd248c..3bea49e772a1f 100644
+--- a/sound/pci/hda/patch_realtek.c
++++ b/sound/pci/hda/patch_realtek.c
+@@ -10544,6 +10544,10 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
+ {0x17, 0x90170110},
+ {0x19, 0x03a11030},
+ {0x21, 0x03211020}),
++ SND_HDA_PIN_QUIRK(0x10ec0287, 0x17aa, "Lenovo", ALC287_FIXUP_THINKPAD_I2S_SPK,
++ {0x17, 0x90170110}, /* 0x231f with RTK I2S AMP */
++ {0x19, 0x04a11040},
++ {0x21, 0x04211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0286, 0x1025, "Acer", ALC286_FIXUP_ACER_AIO_MIC_NO_PRESENCE,
+ {0x12, 0x90a60130},
+ {0x17, 0x90170110},
+--
+2.40.1
+
--- /dev/null
+From 07910699cda1dfec48856de36c7d2b2cf7f89f31 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 18 Aug 2023 16:58:35 +0800
+Subject: ALSA: hda/tas2781: Add tas2781 HDA driver
+
+From: Shenghao Ding <shenghao-ding@ti.com>
+
+[ Upstream commit 3babae915f4c15d76a5134e55806a1c1588e2865 ]
+
+Integrate tas2781 configs for Lenovo Laptops. All of the tas2781s in the
+laptop will be aggregated as one audio device. The code support realtek
+as the primary codec. Rename "struct cs35l41_dev_name" to
+"struct scodec_dev_name" for all other side codecs instead of the certain
+one.
+
+Signed-off-by: Shenghao Ding <shenghao-ding@ti.com>
+Link: https://lore.kernel.org/r/20230818085836.1442-1-shenghao-ding@ti.com
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Stable-dep-of: 41b07476da38 ("ALSA: hda/realtek - ALC287 Realtek I2S speaker platform support")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ sound/pci/hda/patch_realtek.c | 88 +++++++++++++++++++++++++++++++++--
+ 1 file changed, 85 insertions(+), 3 deletions(-)
+
+diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
+index 57e07aa4e136c..7d549229d0b95 100644
+--- a/sound/pci/hda/patch_realtek.c
++++ b/sound/pci/hda/patch_realtek.c
+@@ -6721,7 +6721,7 @@ static void comp_generic_playback_hook(struct hda_pcm_stream *hinfo, struct hda_
+ }
+ }
+
+-struct cs35l41_dev_name {
++struct scodec_dev_name {
+ const char *bus;
+ const char *hid;
+ int index;
+@@ -6730,7 +6730,7 @@ struct cs35l41_dev_name {
+ /* match the device name in a slightly relaxed manner */
+ static int comp_match_cs35l41_dev_name(struct device *dev, void *data)
+ {
+- struct cs35l41_dev_name *p = data;
++ struct scodec_dev_name *p = data;
+ const char *d = dev_name(dev);
+ int n = strlen(p->bus);
+ char tmp[32];
+@@ -6746,12 +6746,32 @@ static int comp_match_cs35l41_dev_name(struct device *dev, void *data)
+ return !strcmp(d + n, tmp);
+ }
+
++static int comp_match_tas2781_dev_name(struct device *dev,
++ void *data)
++{
++ struct scodec_dev_name *p = data;
++ const char *d = dev_name(dev);
++ int n = strlen(p->bus);
++ char tmp[32];
++
++ /* check the bus name */
++ if (strncmp(d, p->bus, n))
++ return 0;
++ /* skip the bus number */
++ if (isdigit(d[n]))
++ n++;
++ /* the rest must be exact matching */
++ snprintf(tmp, sizeof(tmp), "-%s:00", p->hid);
++
++ return !strcmp(d + n, tmp);
++}
++
+ static void cs35l41_generic_fixup(struct hda_codec *cdc, int action, const char *bus,
+ const char *hid, int count)
+ {
+ struct device *dev = hda_codec_dev(cdc);
+ struct alc_spec *spec = cdc->spec;
+- struct cs35l41_dev_name *rec;
++ struct scodec_dev_name *rec;
+ int ret, i;
+
+ switch (action) {
+@@ -6776,6 +6796,41 @@ static void cs35l41_generic_fixup(struct hda_codec *cdc, int action, const char
+ }
+ }
+
++static void tas2781_generic_fixup(struct hda_codec *cdc, int action,
++ const char *bus, const char *hid)
++{
++ struct device *dev = hda_codec_dev(cdc);
++ struct alc_spec *spec = cdc->spec;
++ struct scodec_dev_name *rec;
++ int ret;
++
++ switch (action) {
++ case HDA_FIXUP_ACT_PRE_PROBE:
++ rec = devm_kmalloc(dev, sizeof(*rec), GFP_KERNEL);
++ if (!rec)
++ return;
++ rec->bus = bus;
++ rec->hid = hid;
++ rec->index = 0;
++ spec->comps[0].codec = cdc;
++ component_match_add(dev, &spec->match,
++ comp_match_tas2781_dev_name, rec);
++ ret = component_master_add_with_match(dev, &comp_master_ops,
++ spec->match);
++ if (ret)
++ codec_err(cdc,
++ "Fail to register component aggregator %d\n",
++ ret);
++ else
++ spec->gen.pcm_playback_hook =
++ comp_generic_playback_hook;
++ break;
++ case HDA_FIXUP_ACT_FREE:
++ component_master_del(dev, &comp_master_ops);
++ break;
++ }
++}
++
+ static void cs35l41_fixup_i2c_two(struct hda_codec *cdc, const struct hda_fixup *fix, int action)
+ {
+ cs35l41_generic_fixup(cdc, action, "i2c", "CSC3551", 2);
+@@ -6803,6 +6858,12 @@ static void alc287_fixup_legion_16ithg6_speakers(struct hda_codec *cdc, const st
+ cs35l41_generic_fixup(cdc, action, "i2c", "CLSA0101", 2);
+ }
+
++static void tas2781_fixup_i2c(struct hda_codec *cdc,
++ const struct hda_fixup *fix, int action)
++{
++ tas2781_generic_fixup(cdc, action, "i2c", "TIAS2781");
++}
++
+ /* for alc295_fixup_hp_top_speakers */
+ #include "hp_x360_helper.c"
+
+@@ -7227,6 +7288,7 @@ enum {
+ ALC295_FIXUP_DELL_INSPIRON_TOP_SPEAKERS,
+ ALC236_FIXUP_DELL_DUAL_CODECS,
+ ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI,
++ ALC287_FIXUP_TAS2781_I2C,
+ };
+
+ /* A special fixup for Lenovo C940 and Yoga Duet 7;
+@@ -9296,6 +9358,12 @@ static const struct hda_fixup alc269_fixups[] = {
+ .chained = true,
+ .chain_id = ALC269_FIXUP_THINKPAD_ACPI,
+ },
++ [ALC287_FIXUP_TAS2781_I2C] = {
++ .type = HDA_FIXUP_FUNC,
++ .v.func = tas2781_fixup_i2c,
++ .chained = true,
++ .chain_id = ALC269_FIXUP_THINKPAD_ACPI,
++ },
+ };
+
+ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
+@@ -9867,6 +9935,20 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x17aa, 0x3853, "Lenovo Yoga 7 15ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS),
+ SND_PCI_QUIRK(0x17aa, 0x3855, "Legion 7 16ITHG6", ALC287_FIXUP_LEGION_16ITHG6),
+ SND_PCI_QUIRK(0x17aa, 0x3869, "Lenovo Yoga7 14IAL7", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN),
++ SND_PCI_QUIRK(0x17aa, 0x387d, "Yoga S780-16 pro Quad AAC", ALC287_FIXUP_TAS2781_I2C),
++ SND_PCI_QUIRK(0x17aa, 0x387e, "Yoga S780-16 pro Quad YC", ALC287_FIXUP_TAS2781_I2C),
++ SND_PCI_QUIRK(0x17aa, 0x3881, "YB9 dual powe mode2 YC", ALC287_FIXUP_TAS2781_I2C),
++ SND_PCI_QUIRK(0x17aa, 0x3884, "Y780 YG DUAL", ALC287_FIXUP_TAS2781_I2C),
++ SND_PCI_QUIRK(0x17aa, 0x3886, "Y780 VECO DUAL", ALC287_FIXUP_TAS2781_I2C),
++ SND_PCI_QUIRK(0x17aa, 0x38a7, "Y780P AMD YG dual", ALC287_FIXUP_TAS2781_I2C),
++ SND_PCI_QUIRK(0x17aa, 0x38a8, "Y780P AMD VECO dual", ALC287_FIXUP_TAS2781_I2C),
++ SND_PCI_QUIRK(0x17aa, 0x38ba, "Yoga S780-14.5 Air AMD quad YC", ALC287_FIXUP_TAS2781_I2C),
++ SND_PCI_QUIRK(0x17aa, 0x38bb, "Yoga S780-14.5 Air AMD quad AAC", ALC287_FIXUP_TAS2781_I2C),
++ SND_PCI_QUIRK(0x17aa, 0x38be, "Yoga S980-14.5 proX YC Dual", ALC287_FIXUP_TAS2781_I2C),
++ SND_PCI_QUIRK(0x17aa, 0x38bf, "Yoga S980-14.5 proX LX Dual", ALC287_FIXUP_TAS2781_I2C),
++ SND_PCI_QUIRK(0x17aa, 0x38c3, "Y980 DUAL", ALC287_FIXUP_TAS2781_I2C),
++ SND_PCI_QUIRK(0x17aa, 0x38cb, "Y790 YG DUAL", ALC287_FIXUP_TAS2781_I2C),
++ SND_PCI_QUIRK(0x17aa, 0x38cd, "Y790 VECO DUAL", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x3902, "Lenovo E50-80", ALC269_FIXUP_DMIC_THINKPAD_ACPI),
+ SND_PCI_QUIRK(0x17aa, 0x3977, "IdeaPad S210", ALC283_FIXUP_INT_MIC),
+ SND_PCI_QUIRK(0x17aa, 0x3978, "Lenovo B50-70", ALC269_FIXUP_DMIC_THINKPAD_ACPI),
+--
+2.40.1
+
--- /dev/null
+From 5542cbfe40b5adaa14b2ef882139a5058195903b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 9 Jan 2023 12:19:55 -0300
+Subject: arm64: Avoid repeated AA64MMFR1_EL1 register read on pagefault path
+
+From: Gabriel Krisman Bertazi <krisman@suse.de>
+
+[ Upstream commit a89c6bcdac22bec1bfbe6e64060b4cf5838d4f47 ]
+
+Accessing AA64MMFR1_EL1 is expensive in KVM guests, since it is emulated
+in the hypervisor. In fact, ARM documentation mentions some feature
+registers are not supposed to be accessed frequently by the OS, and
+therefore should be emulated for guests [1].
+
+Commit 0388f9c74330 ("arm64: mm: Implement
+arch_wants_old_prefaulted_pte()") introduced a read of this register in
+the page fault path. But, even when the feature of setting faultaround
+pages with the old flag is disabled for a given cpu, we are still paying
+the cost of checking the register on every pagefault. This results in an
+explosion of vmexit events in KVM guests, which directly impacts the
+performance of virtualized workloads. For instance, running kernbench
+yields a 15% increase in system time solely due to the increased vmexit
+cycles.
+
+This patch avoids the extra cost by using the sanitized cached value.
+It should be safe to do so, since this register mustn't change for a
+given cpu.
+
+[1] https://developer.arm.com/-/media/Arm%20Developer%20Community/PDF/Learn%20the%20Architecture/Armv8-A%20virtualization.pdf?revision=a765a7df-1a00-434d-b241-357bfda2dd31
+
+Signed-off-by: Gabriel Krisman Bertazi <krisman@suse.de>
+Acked-by: Will Deacon <will@kernel.org>
+Reviewed-by: Anshuman Khandual <anshuman.khandual@arm.com>
+Link: https://lore.kernel.org/r/20230109151955.8292-1-krisman@suse.de
+Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/arm64/include/asm/cpufeature.h | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h
+index f73f11b550425..5bf0f9aa46267 100644
+--- a/arch/arm64/include/asm/cpufeature.h
++++ b/arch/arm64/include/asm/cpufeature.h
+@@ -863,7 +863,11 @@ static inline bool cpu_has_hw_af(void)
+ if (!IS_ENABLED(CONFIG_ARM64_HW_AFDBM))
+ return false;
+
+- mmfr1 = read_cpuid(ID_AA64MMFR1_EL1);
++ /*
++ * Use cached version to avoid emulated msr operation on KVM
++ * guests.
++ */
++ mmfr1 = read_sanitised_ftr_reg(SYS_ID_AA64MMFR1_EL1);
+ return cpuid_feature_extract_unsigned_field(mmfr1,
+ ID_AA64MMFR1_EL1_HAFDBS_SHIFT);
+ }
+--
+2.40.1
+
--- /dev/null
+From bb5d98d6ac2702dae4a90e5e00540e700d4b378e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 7 Sep 2023 20:32:24 +0530
+Subject: ASoC: soc-utils: Export snd_soc_dai_is_dummy() symbol
+
+From: Sameer Pujar <spujar@nvidia.com>
+
+[ Upstream commit f101583fa9f8c3f372d4feb61d67da0ccbf4d9a5 ]
+
+Export symbol snd_soc_dai_is_dummy() for usage outside core driver
+modules. This is required by Tegra ASoC machine driver.
+
+Signed-off-by: Sameer Pujar <spujar@nvidia.com>
+Link: https://lore.kernel.org/r/1694098945-32760-2-git-send-email-spujar@nvidia.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ sound/soc/soc-utils.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c
+index a4dba0b751e76..1bbd1d077dfd9 100644
+--- a/sound/soc/soc-utils.c
++++ b/sound/soc/soc-utils.c
+@@ -217,6 +217,7 @@ int snd_soc_dai_is_dummy(struct snd_soc_dai *dai)
+ return 1;
+ return 0;
+ }
++EXPORT_SYMBOL_GPL(snd_soc_dai_is_dummy);
+
+ int snd_soc_component_is_dummy(struct snd_soc_component *component)
+ {
+--
+2.40.1
+
--- /dev/null
+From aacf2dd084dffdc963d1a0d15608150066363397 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 7 Sep 2023 20:32:25 +0530
+Subject: ASoC: tegra: Fix redundant PLLA and PLLA_OUT0 updates
+
+From: Sameer Pujar <spujar@nvidia.com>
+
+[ Upstream commit e765886249c533e1bb5cbc3cd741bad677417312 ]
+
+Tegra audio graph card has many DAI links which connects internal
+AHUB modules and external audio codecs. Since these are DPCM links,
+hw_params() call in the machine driver happens for each connected
+BE link and PLLA is updated every time. This is not really needed
+for all links as only I/O link DAIs derive respective clocks from
+PLLA_OUT0 and thus from PLLA. Hence add checks to limit the clock
+updates to DAIs over I/O links.
+
+This found to be fixing a DMIC clock discrepancy which is suspected
+to happen because of back to back quick PLLA and PLLA_OUT0 rate
+updates. This was observed on Jetson TX2 platform where DMIC clock
+ended up with unexpected value.
+
+Fixes: 202e2f774543 ("ASoC: tegra: Add audio graph based card driver")
+Cc: stable@vger.kernel.org
+Signed-off-by: Sameer Pujar <spujar@nvidia.com>
+Link: https://lore.kernel.org/r/1694098945-32760-3-git-send-email-spujar@nvidia.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ sound/soc/tegra/tegra_audio_graph_card.c | 30 ++++++++++++++----------
+ 1 file changed, 17 insertions(+), 13 deletions(-)
+
+diff --git a/sound/soc/tegra/tegra_audio_graph_card.c b/sound/soc/tegra/tegra_audio_graph_card.c
+index 1f2c5018bf5ac..4737e776d3837 100644
+--- a/sound/soc/tegra/tegra_audio_graph_card.c
++++ b/sound/soc/tegra/tegra_audio_graph_card.c
+@@ -10,6 +10,7 @@
+ #include <linux/platform_device.h>
+ #include <sound/graph_card.h>
+ #include <sound/pcm_params.h>
++#include <sound/soc-dai.h>
+
+ #define MAX_PLLA_OUT0_DIV 128
+
+@@ -44,6 +45,21 @@ struct tegra_audio_cdata {
+ unsigned int plla_out0_rates[NUM_RATE_TYPE];
+ };
+
++static bool need_clk_update(struct snd_soc_dai *dai)
++{
++ if (snd_soc_dai_is_dummy(dai) ||
++ !dai->driver->ops ||
++ !dai->driver->name)
++ return false;
++
++ if (strstr(dai->driver->name, "I2S") ||
++ strstr(dai->driver->name, "DMIC") ||
++ strstr(dai->driver->name, "DSPK"))
++ return true;
++
++ return false;
++}
++
+ /* Setup PLL clock as per the given sample rate */
+ static int tegra_audio_graph_update_pll(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+@@ -140,19 +156,7 @@ static int tegra_audio_graph_hw_params(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+ int err;
+
+- /*
+- * This gets called for each DAI link (FE or BE) when DPCM is used.
+- * We may not want to update PLLA rate for each call. So PLLA update
+- * must be restricted to external I/O links (I2S, DMIC or DSPK) since
+- * they actually depend on it. I/O modules update their clocks in
+- * hw_param() of their respective component driver and PLLA rate
+- * update here helps them to derive appropriate rates.
+- *
+- * TODO: When more HW accelerators get added (like sample rate
+- * converter, volume gain controller etc., which don't really
+- * depend on PLLA) we need a better way to filter here.
+- */
+- if (cpu_dai->driver->ops && rtd->dai_link->no_pcm) {
++ if (need_clk_update(cpu_dai)) {
+ err = tegra_audio_graph_update_pll(substream, params);
+ if (err)
+ return err;
+--
+2.40.1
+
--- /dev/null
+From 4d1fc727026b5029e2f304f3e50aedbc215eb4ba Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 5 Sep 2023 09:06:23 +0900
+Subject: ata: libata-scsi: Fix delayed scsi_rescan_device() execution
+
+From: Damien Le Moal <dlemoal@kernel.org>
+
+[ Upstream commit 8b4d9469d0b0e553208ee6f62f2807111fde18b9 ]
+
+Commit 6aa0365a3c85 ("ata: libata-scsi: Avoid deadlock on rescan after
+device resume") modified ata_scsi_dev_rescan() to check the scsi device
+"is_suspended" power field to ensure that the scsi device associated
+with an ATA device is fully resumed when scsi_rescan_device() is
+executed. However, this fix is problematic as:
+1) It relies on a PM internal field that should not be used without PM
+ device locking protection.
+2) The check for is_suspended and the call to scsi_rescan_device() are
+ not atomic and a suspend PM event may be triggered between them,
+ casuing scsi_rescan_device() to be called on a suspended device and
+ in that function blocking while holding the scsi device lock. This
+ would deadlock a following resume operation.
+These problems can trigger PM deadlocks on resume, especially with
+resume operations triggered quickly after or during suspend operations.
+E.g., a simple bash script like:
+
+for (( i=0; i<10; i++ )); do
+ echo "+2 > /sys/class/rtc/rtc0/wakealarm
+ echo mem > /sys/power/state
+done
+
+that triggers a resume 2 seconds after starting suspending a system can
+quickly lead to a PM deadlock preventing the system from correctly
+resuming.
+
+Fix this by replacing the check on is_suspended with a check on the
+return value given by scsi_rescan_device() as that function will fail if
+called against a suspended device. Also make sure rescan tasks already
+scheduled are first cancelled before suspending an ata port.
+
+Fixes: 6aa0365a3c85 ("ata: libata-scsi: Avoid deadlock on rescan after device resume")
+Cc: stable@vger.kernel.org
+Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
+Reviewed-by: Hannes Reinecke <hare@suse.de>
+Reviewed-by: Niklas Cassel <niklas.cassel@wdc.com>
+Tested-by: Geert Uytterhoeven <geert+renesas@glider.be>
+Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/ata/libata-core.c | 16 ++++++++++++++++
+ drivers/ata/libata-scsi.c | 33 +++++++++++++++------------------
+ 2 files changed, 31 insertions(+), 18 deletions(-)
+
+diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
+index 25b9bdf2fc380..6a053cd0cf410 100644
+--- a/drivers/ata/libata-core.c
++++ b/drivers/ata/libata-core.c
+@@ -5022,11 +5022,27 @@ static const unsigned int ata_port_suspend_ehi = ATA_EHI_QUIET
+
+ static void ata_port_suspend(struct ata_port *ap, pm_message_t mesg)
+ {
++ /*
++ * We are about to suspend the port, so we do not care about
++ * scsi_rescan_device() calls scheduled by previous resume operations.
++ * The next resume will schedule the rescan again. So cancel any rescan
++ * that is not done yet.
++ */
++ cancel_delayed_work_sync(&ap->scsi_rescan_task);
++
+ ata_port_request_pm(ap, mesg, 0, ata_port_suspend_ehi, false);
+ }
+
+ static void ata_port_suspend_async(struct ata_port *ap, pm_message_t mesg)
+ {
++ /*
++ * We are about to suspend the port, so we do not care about
++ * scsi_rescan_device() calls scheduled by previous resume operations.
++ * The next resume will schedule the rescan again. So cancel any rescan
++ * that is not done yet.
++ */
++ cancel_delayed_work_sync(&ap->scsi_rescan_task);
++
+ ata_port_request_pm(ap, mesg, 0, ata_port_suspend_ehi, true);
+ }
+
+diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
+index b348f77b91231..7b9c9264b9a72 100644
+--- a/drivers/ata/libata-scsi.c
++++ b/drivers/ata/libata-scsi.c
+@@ -4648,7 +4648,7 @@ void ata_scsi_dev_rescan(struct work_struct *work)
+ struct ata_link *link;
+ struct ata_device *dev;
+ unsigned long flags;
+- bool delay_rescan = false;
++ int ret = 0;
+
+ mutex_lock(&ap->scsi_scan_mutex);
+ spin_lock_irqsave(ap->lock, flags);
+@@ -4657,37 +4657,34 @@ void ata_scsi_dev_rescan(struct work_struct *work)
+ ata_for_each_dev(dev, link, ENABLED) {
+ struct scsi_device *sdev = dev->sdev;
+
++ /*
++ * If the port was suspended before this was scheduled,
++ * bail out.
++ */
++ if (ap->pflags & ATA_PFLAG_SUSPENDED)
++ goto unlock;
++
+ if (!sdev)
+ continue;
+ if (scsi_device_get(sdev))
+ continue;
+
+- /*
+- * If the rescan work was scheduled because of a resume
+- * event, the port is already fully resumed, but the
+- * SCSI device may not yet be fully resumed. In such
+- * case, executing scsi_rescan_device() may cause a
+- * deadlock with the PM code on device_lock(). Prevent
+- * this by giving up and retrying rescan after a short
+- * delay.
+- */
+- delay_rescan = sdev->sdev_gendev.power.is_suspended;
+- if (delay_rescan) {
+- scsi_device_put(sdev);
+- break;
+- }
+-
+ spin_unlock_irqrestore(ap->lock, flags);
+- scsi_rescan_device(sdev);
++ ret = scsi_rescan_device(sdev);
+ scsi_device_put(sdev);
+ spin_lock_irqsave(ap->lock, flags);
++
++ if (ret)
++ goto unlock;
+ }
+ }
+
++unlock:
+ spin_unlock_irqrestore(ap->lock, flags);
+ mutex_unlock(&ap->scsi_scan_mutex);
+
+- if (delay_rescan)
++ /* Reschedule with a delay if scsi_rescan_device() returned an error */
++ if (ret)
+ schedule_delayed_work(&ap->scsi_rescan_task,
+ msecs_to_jiffies(5));
+ }
+--
+2.40.1
+
--- /dev/null
+From 3bdffcb01d762ec932de44fbb950d6d3a828d08f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 24 Jul 2023 13:23:14 +0900
+Subject: ata,scsi: do not issue START STOP UNIT on resume
+
+From: Damien Le Moal <dlemoal@kernel.org>
+
+[ Upstream commit 0a8589055936d8feb56477123a8373ac634018fa ]
+
+During system resume, ata_port_pm_resume() triggers ata EH to
+1) Resume the controller
+2) Reset and rescan the ports
+3) Revalidate devices
+This EH execution is started asynchronously from ata_port_pm_resume(),
+which means that when sd_resume() is executed, none or only part of the
+above processing may have been executed. However, sd_resume() issues a
+START STOP UNIT to wake up the drive from sleep mode. This command is
+translated to ATA with ata_scsi_start_stop_xlat() and issued to the
+device. However, depending on the state of execution of the EH process
+and revalidation triggerred by ata_port_pm_resume(), two things may
+happen:
+1) The START STOP UNIT fails if it is received before the controller has
+ been reenabled at the beginning of the EH execution. This is visible
+ with error messages like:
+
+ata10.00: device reported invalid CHS sector 0
+sd 9:0:0:0: [sdc] Start/Stop Unit failed: Result: hostbyte=DID_OK driverbyte=DRIVER_OK
+sd 9:0:0:0: [sdc] Sense Key : Illegal Request [current]
+sd 9:0:0:0: [sdc] Add. Sense: Unaligned write command
+sd 9:0:0:0: PM: dpm_run_callback(): scsi_bus_resume+0x0/0x90 returns -5
+sd 9:0:0:0: PM: failed to resume async: error -5
+
+2) The START STOP UNIT command is received while the EH process is
+ on-going, which mean that it is stopped and must wait for its
+ completion, at which point the command is rather useless as the drive
+ is already fully spun up already. This case results also in a
+ significant delay in sd_resume() which is observable by users as
+ the entire system resume completion is delayed.
+
+Given that ATA devices will be woken up by libata activity on resume,
+sd_resume() has no need to issue a START STOP UNIT command, which solves
+the above mentioned problems. Do not issue this command by introducing
+the new scsi_device flag no_start_on_resume and setting this flag to 1
+in ata_scsi_dev_config(). sd_resume() is modified to issue a START STOP
+UNIT command only if this flag is not set.
+
+Reported-by: Paul Ausbeck <paula@soe.ucsc.edu>
+Closes: https://bugzilla.kernel.org/show_bug.cgi?id=215880
+Fixes: a19a93e4c6a9 ("scsi: core: pm: Rely on the device driver core for async power management")
+Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
+Tested-by: Tanner Watkins <dalzot@gmail.com>
+Tested-by: Paul Ausbeck <paula@soe.ucsc.edu>
+Reviewed-by: Hannes Reinecke <hare@suse.de>
+Reviewed-by: Bart Van Assche <bvanassche@acm.org>
+Stable-dep-of: 99398d2070ab ("scsi: sd: Do not issue commands to suspended disks on shutdown")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/ata/libata-scsi.c | 7 +++++++
+ drivers/scsi/sd.c | 9 ++++++---
+ include/scsi/scsi_device.h | 1 +
+ 3 files changed, 14 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
+index d28628b964e29..9c8dd9f86cbb3 100644
+--- a/drivers/ata/libata-scsi.c
++++ b/drivers/ata/libata-scsi.c
+@@ -1081,7 +1081,14 @@ int ata_scsi_dev_config(struct scsi_device *sdev, struct ata_device *dev)
+ }
+ } else {
+ sdev->sector_size = ata_id_logical_sector_size(dev->id);
++ /*
++ * Stop the drive on suspend but do not issue START STOP UNIT
++ * on resume as this is not necessary and may fail: the device
++ * will be woken up by ata_port_pm_resume() with a port reset
++ * and device revalidation.
++ */
+ sdev->manage_start_stop = 1;
++ sdev->no_start_on_resume = 1;
+ }
+
+ /*
+diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
+index e934779bf05c8..5bfca49415113 100644
+--- a/drivers/scsi/sd.c
++++ b/drivers/scsi/sd.c
+@@ -3718,7 +3718,7 @@ static int sd_suspend_runtime(struct device *dev)
+ static int sd_resume(struct device *dev)
+ {
+ struct scsi_disk *sdkp = dev_get_drvdata(dev);
+- int ret;
++ int ret = 0;
+
+ if (!sdkp) /* E.g.: runtime resume at the start of sd_probe() */
+ return 0;
+@@ -3726,8 +3726,11 @@ static int sd_resume(struct device *dev)
+ if (!sdkp->device->manage_start_stop)
+ return 0;
+
+- sd_printk(KERN_NOTICE, sdkp, "Starting disk\n");
+- ret = sd_start_stop_device(sdkp, 1);
++ if (!sdkp->device->no_start_on_resume) {
++ sd_printk(KERN_NOTICE, sdkp, "Starting disk\n");
++ ret = sd_start_stop_device(sdkp, 1);
++ }
++
+ if (!ret)
+ opal_unlock_from_suspend(sdkp->opal_dev);
+ return ret;
+diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h
+index 006858ed04e8c..9fdc77db3a2a8 100644
+--- a/include/scsi/scsi_device.h
++++ b/include/scsi/scsi_device.h
+@@ -193,6 +193,7 @@ struct scsi_device {
+ unsigned no_start_on_add:1; /* do not issue start on add */
+ unsigned allow_restart:1; /* issue START_UNIT in error handler */
+ unsigned manage_start_stop:1; /* Let HLD (sd) manage start/stop */
++ unsigned no_start_on_resume:1; /* Do not issue START_STOP_UNIT on resume */
+ unsigned start_stop_pwr_cond:1; /* Set power cond. in START_STOP_UNIT */
+ unsigned no_uld_attach:1; /* disable connecting to upper level drivers */
+ unsigned select_no_atn:1;
+--
+2.40.1
+
--- /dev/null
+From 22c78442fa7b92733ad1e22f57af18e0444b8a19 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 20 Oct 2022 12:58:26 -0400
+Subject: btrfs: setup qstr from dentrys using fscrypt helper
+
+From: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
+
+[ Upstream commit ab3c5c18e8fa3f8ea116016095d25adab466cd39 ]
+
+Most places where we get a struct qstr, we are doing so from a dentry.
+With fscrypt, the dentry's name may be encrypted on-disk, so fscrypt
+provides a helper to convert a dentry name to the appropriate disk name
+if necessary. Convert each of the dentry name accesses to use
+fscrypt_setup_filename(), then convert the resulting fscrypt_name back
+to an unencrypted qstr. This does not work for nokey names, but the
+specific locations that could spawn nokey names are noted.
+
+At present, since there are no encrypted directories, nothing goes down
+the filename encryption paths.
+
+Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
+Reviewed-by: David Sterba <dsterba@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Stable-dep-of: 9af86694fd5d ("btrfs: file_remove_privs needs an exclusive lock in direct io write")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/btrfs/ctree.h | 3 +
+ fs/btrfs/inode.c | 192 +++++++++++++++++++++++++++++++----------
+ fs/btrfs/transaction.c | 40 ++++++---
+ fs/btrfs/tree-log.c | 11 ++-
+ 4 files changed, 189 insertions(+), 57 deletions(-)
+
+diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
+index 6718cee57a94e..5120cea15b096 100644
+--- a/fs/btrfs/ctree.h
++++ b/fs/btrfs/ctree.h
+@@ -28,6 +28,7 @@
+ #include <linux/refcount.h>
+ #include <linux/crc32c.h>
+ #include <linux/iomap.h>
++#include <linux/fscrypt.h>
+ #include "extent-io-tree.h"
+ #include "extent_io.h"
+ #include "extent_map.h"
+@@ -3396,6 +3397,8 @@ struct btrfs_new_inode_args {
+ */
+ struct posix_acl *default_acl;
+ struct posix_acl *acl;
++ struct fscrypt_name fname;
++ struct qstr name;
+ };
+ int btrfs_new_inode_prepare(struct btrfs_new_inode_args *args,
+ unsigned int *trans_num_items);
+diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
+index a5e61ad2ba696..b5224dbaa4165 100644
+--- a/fs/btrfs/inode.c
++++ b/fs/btrfs/inode.c
+@@ -4415,28 +4415,41 @@ static int btrfs_unlink(struct inode *dir, struct dentry *dentry)
+ struct btrfs_trans_handle *trans;
+ struct inode *inode = d_inode(dentry);
+ int ret;
++ struct fscrypt_name fname;
++ struct qstr name;
++
++ ret = fscrypt_setup_filename(dir, &dentry->d_name, 1, &fname);
++ if (ret)
++ return ret;
++ name = (struct qstr)FSTR_TO_QSTR(&fname.disk_name);
++
++ /* This needs to handle no-key deletions later on */
+
+ trans = __unlink_start_trans(dir);
+- if (IS_ERR(trans))
+- return PTR_ERR(trans);
++ if (IS_ERR(trans)) {
++ ret = PTR_ERR(trans);
++ goto fscrypt_free;
++ }
+
+ btrfs_record_unlink_dir(trans, BTRFS_I(dir), BTRFS_I(d_inode(dentry)),
+ 0);
+
+ ret = btrfs_unlink_inode(trans, BTRFS_I(dir), BTRFS_I(d_inode(dentry)),
+- &dentry->d_name);
++ &name);
+ if (ret)
+- goto out;
++ goto end_trans;
+
+ if (inode->i_nlink == 0) {
+ ret = btrfs_orphan_add(trans, BTRFS_I(inode));
+ if (ret)
+- goto out;
++ goto end_trans;
+ }
+
+-out:
++end_trans:
+ btrfs_end_transaction(trans);
+ btrfs_btree_balance_dirty(BTRFS_I(dir)->root->fs_info);
++fscrypt_free:
++ fscrypt_free_filename(&fname);
+ return ret;
+ }
+
+@@ -4449,11 +4462,19 @@ static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans,
+ struct extent_buffer *leaf;
+ struct btrfs_dir_item *di;
+ struct btrfs_key key;
+- const struct qstr *name = &dentry->d_name;
++ struct qstr name;
+ u64 index;
+ int ret;
+ u64 objectid;
+ u64 dir_ino = btrfs_ino(BTRFS_I(dir));
++ struct fscrypt_name fname;
++
++ ret = fscrypt_setup_filename(dir, &dentry->d_name, 1, &fname);
++ if (ret)
++ return ret;
++ name = (struct qstr)FSTR_TO_QSTR(&fname.disk_name);
++
++ /* This needs to handle no-key deletions later on */
+
+ if (btrfs_ino(inode) == BTRFS_FIRST_FREE_OBJECTID) {
+ objectid = inode->root->root_key.objectid;
+@@ -4461,14 +4482,17 @@ static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans,
+ objectid = inode->location.objectid;
+ } else {
+ WARN_ON(1);
++ fscrypt_free_filename(&fname);
+ return -EINVAL;
+ }
+
+ path = btrfs_alloc_path();
+- if (!path)
+- return -ENOMEM;
++ if (!path) {
++ ret = -ENOMEM;
++ goto out;
++ }
+
+- di = btrfs_lookup_dir_item(trans, root, path, dir_ino, name, -1);
++ di = btrfs_lookup_dir_item(trans, root, path, dir_ino, &name, -1);
+ if (IS_ERR_OR_NULL(di)) {
+ ret = di ? PTR_ERR(di) : -ENOENT;
+ goto out;
+@@ -4494,7 +4518,7 @@ static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans,
+ * call btrfs_del_root_ref, and it _shouldn't_ fail.
+ */
+ if (btrfs_ino(inode) == BTRFS_EMPTY_SUBVOL_DIR_OBJECTID) {
+- di = btrfs_search_dir_index_item(root, path, dir_ino, name);
++ di = btrfs_search_dir_index_item(root, path, dir_ino, &name);
+ if (IS_ERR_OR_NULL(di)) {
+ if (!di)
+ ret = -ENOENT;
+@@ -4511,7 +4535,7 @@ static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans,
+ } else {
+ ret = btrfs_del_root_ref(trans, objectid,
+ root->root_key.objectid, dir_ino,
+- &index, name);
++ &index, &name);
+ if (ret) {
+ btrfs_abort_transaction(trans, ret);
+ goto out;
+@@ -4524,7 +4548,7 @@ static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans,
+ goto out;
+ }
+
+- btrfs_i_size_write(BTRFS_I(dir), dir->i_size - name->len * 2);
++ btrfs_i_size_write(BTRFS_I(dir), dir->i_size - name.len * 2);
+ inode_inc_iversion(dir);
+ dir->i_mtime = current_time(dir);
+ dir->i_ctime = dir->i_mtime;
+@@ -4533,6 +4557,7 @@ static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans,
+ btrfs_abort_transaction(trans, ret);
+ out:
+ btrfs_free_path(path);
++ fscrypt_free_filename(&fname);
+ return ret;
+ }
+
+@@ -4796,6 +4821,8 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry)
+ int err = 0;
+ struct btrfs_trans_handle *trans;
+ u64 last_unlink_trans;
++ struct fscrypt_name fname;
++ struct qstr name;
+
+ if (inode->i_size > BTRFS_EMPTY_DIR_SIZE)
+ return -ENOTEMPTY;
+@@ -4808,9 +4835,18 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry)
+ return btrfs_delete_subvolume(dir, dentry);
+ }
+
++ err = fscrypt_setup_filename(dir, &dentry->d_name, 1, &fname);
++ if (err)
++ return err;
++ name = (struct qstr)FSTR_TO_QSTR(&fname.disk_name);
++
++ /* This needs to handle no-key deletions later on */
++
+ trans = __unlink_start_trans(dir);
+- if (IS_ERR(trans))
+- return PTR_ERR(trans);
++ if (IS_ERR(trans)) {
++ err = PTR_ERR(trans);
++ goto out_notrans;
++ }
+
+ if (unlikely(btrfs_ino(BTRFS_I(inode)) == BTRFS_EMPTY_SUBVOL_DIR_OBJECTID)) {
+ err = btrfs_unlink_subvol(trans, dir, dentry);
+@@ -4825,7 +4861,7 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry)
+
+ /* now the directory is empty */
+ err = btrfs_unlink_inode(trans, BTRFS_I(dir), BTRFS_I(d_inode(dentry)),
+- &dentry->d_name);
++ &name);
+ if (!err) {
+ btrfs_i_size_write(BTRFS_I(inode), 0);
+ /*
+@@ -4844,7 +4880,9 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry)
+ }
+ out:
+ btrfs_end_transaction(trans);
++out_notrans:
+ btrfs_btree_balance_dirty(fs_info);
++ fscrypt_free_filename(&fname);
+
+ return err;
+ }
+@@ -5525,18 +5563,27 @@ void btrfs_evict_inode(struct inode *inode)
+ static int btrfs_inode_by_name(struct inode *dir, struct dentry *dentry,
+ struct btrfs_key *location, u8 *type)
+ {
+- const struct qstr *name = &dentry->d_name;
++ struct qstr name;
+ struct btrfs_dir_item *di;
+ struct btrfs_path *path;
+ struct btrfs_root *root = BTRFS_I(dir)->root;
+ int ret = 0;
++ struct fscrypt_name fname;
+
+ path = btrfs_alloc_path();
+ if (!path)
+ return -ENOMEM;
+
++ ret = fscrypt_setup_filename(dir, &dentry->d_name, 1, &fname);
++ if (ret)
++ goto out;
++
++ name = (struct qstr)FSTR_TO_QSTR(&fname.disk_name);
++
++ /* This needs to handle no-key deletions later on */
++
+ di = btrfs_lookup_dir_item(NULL, root, path, btrfs_ino(BTRFS_I(dir)),
+- name, 0);
++ &name, 0);
+ if (IS_ERR_OR_NULL(di)) {
+ ret = di ? PTR_ERR(di) : -ENOENT;
+ goto out;
+@@ -5548,12 +5595,13 @@ static int btrfs_inode_by_name(struct inode *dir, struct dentry *dentry,
+ ret = -EUCLEAN;
+ btrfs_warn(root->fs_info,
+ "%s gets something invalid in DIR_ITEM (name %s, directory ino %llu, location(%llu %u %llu))",
+- __func__, name->name, btrfs_ino(BTRFS_I(dir)),
++ __func__, name.name, btrfs_ino(BTRFS_I(dir)),
+ location->objectid, location->type, location->offset);
+ }
+ if (!ret)
+ *type = btrfs_dir_type(path->nodes[0], di);
+ out:
++ fscrypt_free_filename(&fname);
+ btrfs_free_path(path);
+ return ret;
+ }
+@@ -5576,6 +5624,14 @@ static int fixup_tree_root_location(struct btrfs_fs_info *fs_info,
+ struct btrfs_key key;
+ int ret;
+ int err = 0;
++ struct fscrypt_name fname;
++ struct qstr name;
++
++ ret = fscrypt_setup_filename(dir, &dentry->d_name, 0, &fname);
++ if (ret)
++ return ret;
++
++ name = (struct qstr)FSTR_TO_QSTR(&fname.disk_name);
+
+ path = btrfs_alloc_path();
+ if (!path) {
+@@ -5598,12 +5654,11 @@ static int fixup_tree_root_location(struct btrfs_fs_info *fs_info,
+ leaf = path->nodes[0];
+ ref = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_root_ref);
+ if (btrfs_root_ref_dirid(leaf, ref) != btrfs_ino(BTRFS_I(dir)) ||
+- btrfs_root_ref_name_len(leaf, ref) != dentry->d_name.len)
++ btrfs_root_ref_name_len(leaf, ref) != name.len)
+ goto out;
+
+- ret = memcmp_extent_buffer(leaf, dentry->d_name.name,
+- (unsigned long)(ref + 1),
+- dentry->d_name.len);
++ ret = memcmp_extent_buffer(leaf, name.name, (unsigned long)(ref + 1),
++ name.len);
+ if (ret)
+ goto out;
+
+@@ -5622,6 +5677,7 @@ static int fixup_tree_root_location(struct btrfs_fs_info *fs_info,
+ err = 0;
+ out:
+ btrfs_free_path(path);
++ fscrypt_free_filename(&fname);
+ return err;
+ }
+
+@@ -6230,9 +6286,19 @@ int btrfs_new_inode_prepare(struct btrfs_new_inode_args *args,
+ struct inode *inode = args->inode;
+ int ret;
+
++ if (!args->orphan) {
++ ret = fscrypt_setup_filename(dir, &args->dentry->d_name, 0,
++ &args->fname);
++ if (ret)
++ return ret;
++ args->name = (struct qstr)FSTR_TO_QSTR(&args->fname.disk_name);
++ }
++
+ ret = posix_acl_create(dir, &inode->i_mode, &args->default_acl, &args->acl);
+- if (ret)
++ if (ret) {
++ fscrypt_free_filename(&args->fname);
+ return ret;
++ }
+
+ /* 1 to add inode item */
+ *trans_num_items = 1;
+@@ -6272,6 +6338,7 @@ void btrfs_new_inode_args_destroy(struct btrfs_new_inode_args *args)
+ {
+ posix_acl_release(args->acl);
+ posix_acl_release(args->default_acl);
++ fscrypt_free_filename(&args->fname);
+ }
+
+ /*
+@@ -6697,6 +6764,8 @@ static int btrfs_link(struct dentry *old_dentry, struct inode *dir,
+ struct btrfs_root *root = BTRFS_I(dir)->root;
+ struct inode *inode = d_inode(old_dentry);
+ struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
++ struct fscrypt_name fname;
++ struct qstr name;
+ u64 index;
+ int err;
+ int drop_inode = 0;
+@@ -6708,6 +6777,12 @@ static int btrfs_link(struct dentry *old_dentry, struct inode *dir,
+ if (inode->i_nlink >= BTRFS_LINK_MAX)
+ return -EMLINK;
+
++ err = fscrypt_setup_filename(dir, &dentry->d_name, 0, &fname);
++ if (err)
++ goto fail;
++
++ name = (struct qstr)FSTR_TO_QSTR(&fname.disk_name);
++
+ err = btrfs_set_inode_index(BTRFS_I(dir), &index);
+ if (err)
+ goto fail;
+@@ -6734,7 +6809,7 @@ static int btrfs_link(struct dentry *old_dentry, struct inode *dir,
+ set_bit(BTRFS_INODE_COPY_EVERYTHING, &BTRFS_I(inode)->runtime_flags);
+
+ err = btrfs_add_link(trans, BTRFS_I(dir), BTRFS_I(inode),
+- &dentry->d_name, 1, index);
++ &name, 1, index);
+
+ if (err) {
+ drop_inode = 1;
+@@ -6758,6 +6833,7 @@ static int btrfs_link(struct dentry *old_dentry, struct inode *dir,
+ }
+
+ fail:
++ fscrypt_free_filename(&fname);
+ if (trans)
+ btrfs_end_transaction(trans);
+ if (drop_inode) {
+@@ -9030,6 +9106,8 @@ static int btrfs_rename_exchange(struct inode *old_dir,
+ int ret;
+ int ret2;
+ bool need_abort = false;
++ struct fscrypt_name old_fname, new_fname;
++ struct qstr old_name, new_name;
+
+ /*
+ * For non-subvolumes allow exchange only within one subvolume, in the
+@@ -9041,6 +9119,19 @@ static int btrfs_rename_exchange(struct inode *old_dir,
+ new_ino != BTRFS_FIRST_FREE_OBJECTID))
+ return -EXDEV;
+
++ ret = fscrypt_setup_filename(old_dir, &old_dentry->d_name, 0, &old_fname);
++ if (ret)
++ return ret;
++
++ ret = fscrypt_setup_filename(new_dir, &new_dentry->d_name, 0, &new_fname);
++ if (ret) {
++ fscrypt_free_filename(&old_fname);
++ return ret;
++ }
++
++ old_name = (struct qstr)FSTR_TO_QSTR(&old_fname.disk_name);
++ new_name = (struct qstr)FSTR_TO_QSTR(&new_fname.disk_name);
++
+ /* close the race window with snapshot create/destroy ioctl */
+ if (old_ino == BTRFS_FIRST_FREE_OBJECTID ||
+ new_ino == BTRFS_FIRST_FREE_OBJECTID)
+@@ -9108,8 +9199,7 @@ static int btrfs_rename_exchange(struct inode *old_dir,
+ /* force full log commit if subvolume involved. */
+ btrfs_set_log_full_commit(trans);
+ } else {
+- ret = btrfs_insert_inode_ref(trans, dest, &new_dentry->d_name,
+- old_ino,
++ ret = btrfs_insert_inode_ref(trans, dest, &new_name, old_ino,
+ btrfs_ino(BTRFS_I(new_dir)),
+ old_idx);
+ if (ret)
+@@ -9122,8 +9212,7 @@ static int btrfs_rename_exchange(struct inode *old_dir,
+ /* force full log commit if subvolume involved. */
+ btrfs_set_log_full_commit(trans);
+ } else {
+- ret = btrfs_insert_inode_ref(trans, root, &old_dentry->d_name,
+- new_ino,
++ ret = btrfs_insert_inode_ref(trans, root, &old_name, new_ino,
+ btrfs_ino(BTRFS_I(old_dir)),
+ new_idx);
+ if (ret) {
+@@ -9158,8 +9247,7 @@ static int btrfs_rename_exchange(struct inode *old_dir,
+ } else { /* src is an inode */
+ ret = __btrfs_unlink_inode(trans, BTRFS_I(old_dir),
+ BTRFS_I(old_dentry->d_inode),
+- &old_dentry->d_name,
+- &old_rename_ctx);
++ &old_name, &old_rename_ctx);
+ if (!ret)
+ ret = btrfs_update_inode(trans, root, BTRFS_I(old_inode));
+ }
+@@ -9174,8 +9262,7 @@ static int btrfs_rename_exchange(struct inode *old_dir,
+ } else { /* dest is an inode */
+ ret = __btrfs_unlink_inode(trans, BTRFS_I(new_dir),
+ BTRFS_I(new_dentry->d_inode),
+- &new_dentry->d_name,
+- &new_rename_ctx);
++ &new_name, &new_rename_ctx);
+ if (!ret)
+ ret = btrfs_update_inode(trans, dest, BTRFS_I(new_inode));
+ }
+@@ -9185,14 +9272,14 @@ static int btrfs_rename_exchange(struct inode *old_dir,
+ }
+
+ ret = btrfs_add_link(trans, BTRFS_I(new_dir), BTRFS_I(old_inode),
+- &new_dentry->d_name, 0, old_idx);
++ &new_name, 0, old_idx);
+ if (ret) {
+ btrfs_abort_transaction(trans, ret);
+ goto out_fail;
+ }
+
+ ret = btrfs_add_link(trans, BTRFS_I(old_dir), BTRFS_I(new_inode),
+- &old_dentry->d_name, 0, new_idx);
++ &old_name, 0, new_idx);
+ if (ret) {
+ btrfs_abort_transaction(trans, ret);
+ goto out_fail;
+@@ -9235,6 +9322,8 @@ static int btrfs_rename_exchange(struct inode *old_dir,
+ old_ino == BTRFS_FIRST_FREE_OBJECTID)
+ up_read(&fs_info->subvol_sem);
+
++ fscrypt_free_filename(&new_fname);
++ fscrypt_free_filename(&old_fname);
+ return ret;
+ }
+
+@@ -9274,6 +9363,8 @@ static int btrfs_rename(struct user_namespace *mnt_userns,
+ int ret;
+ int ret2;
+ u64 old_ino = btrfs_ino(BTRFS_I(old_inode));
++ struct fscrypt_name old_fname, new_fname;
++ struct qstr old_name, new_name;
+
+ if (btrfs_ino(BTRFS_I(new_dir)) == BTRFS_EMPTY_SUBVOL_DIR_OBJECTID)
+ return -EPERM;
+@@ -9290,21 +9381,32 @@ static int btrfs_rename(struct user_namespace *mnt_userns,
+ new_inode->i_size > BTRFS_EMPTY_DIR_SIZE)
+ return -ENOTEMPTY;
+
++ ret = fscrypt_setup_filename(old_dir, &old_dentry->d_name, 0, &old_fname);
++ if (ret)
++ return ret;
++
++ ret = fscrypt_setup_filename(new_dir, &new_dentry->d_name, 0, &new_fname);
++ if (ret) {
++ fscrypt_free_filename(&old_fname);
++ return ret;
++ }
++
++ old_name = (struct qstr)FSTR_TO_QSTR(&old_fname.disk_name);
++ new_name = (struct qstr)FSTR_TO_QSTR(&new_fname.disk_name);
+
+ /* check for collisions, even if the name isn't there */
+- ret = btrfs_check_dir_item_collision(dest, new_dir->i_ino,
+- &new_dentry->d_name);
++ ret = btrfs_check_dir_item_collision(dest, new_dir->i_ino, &new_name);
+
+ if (ret) {
+ if (ret == -EEXIST) {
+ /* we shouldn't get
+ * eexist without a new_inode */
+ if (WARN_ON(!new_inode)) {
+- return ret;
++ goto out_fscrypt_names;
+ }
+ } else {
+ /* maybe -EOVERFLOW */
+- return ret;
++ goto out_fscrypt_names;
+ }
+ }
+ ret = 0;
+@@ -9387,8 +9489,7 @@ static int btrfs_rename(struct user_namespace *mnt_userns,
+ /* force full log commit if subvolume involved. */
+ btrfs_set_log_full_commit(trans);
+ } else {
+- ret = btrfs_insert_inode_ref(trans, dest, &new_dentry->d_name,
+- old_ino,
++ ret = btrfs_insert_inode_ref(trans, dest, &new_name, old_ino,
+ btrfs_ino(BTRFS_I(new_dir)), index);
+ if (ret)
+ goto out_fail;
+@@ -9412,7 +9513,7 @@ static int btrfs_rename(struct user_namespace *mnt_userns,
+ } else {
+ ret = __btrfs_unlink_inode(trans, BTRFS_I(old_dir),
+ BTRFS_I(d_inode(old_dentry)),
+- &old_dentry->d_name, &rename_ctx);
++ &old_name, &rename_ctx);
+ if (!ret)
+ ret = btrfs_update_inode(trans, root, BTRFS_I(old_inode));
+ }
+@@ -9431,7 +9532,7 @@ static int btrfs_rename(struct user_namespace *mnt_userns,
+ } else {
+ ret = btrfs_unlink_inode(trans, BTRFS_I(new_dir),
+ BTRFS_I(d_inode(new_dentry)),
+- &new_dentry->d_name);
++ &new_name);
+ }
+ if (!ret && new_inode->i_nlink == 0)
+ ret = btrfs_orphan_add(trans,
+@@ -9443,7 +9544,7 @@ static int btrfs_rename(struct user_namespace *mnt_userns,
+ }
+
+ ret = btrfs_add_link(trans, BTRFS_I(new_dir), BTRFS_I(old_inode),
+- &new_dentry->d_name, 0, index);
++ &new_name, 0, index);
+ if (ret) {
+ btrfs_abort_transaction(trans, ret);
+ goto out_fail;
+@@ -9478,6 +9579,9 @@ static int btrfs_rename(struct user_namespace *mnt_userns,
+ out_whiteout_inode:
+ if (flags & RENAME_WHITEOUT)
+ iput(whiteout_args.inode);
++out_fscrypt_names:
++ fscrypt_free_filename(&old_fname);
++ fscrypt_free_filename(&new_fname);
+ return ret;
+ }
+
+diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c
+index b0fe054c9f401..c8918bdf15ccd 100644
+--- a/fs/btrfs/transaction.c
++++ b/fs/btrfs/transaction.c
+@@ -6,6 +6,7 @@
+ #include <linux/fs.h>
+ #include <linux/slab.h>
+ #include <linux/sched.h>
++#include <linux/sched/mm.h>
+ #include <linux/writeback.h>
+ #include <linux/pagemap.h>
+ #include <linux/blkdev.h>
+@@ -1627,10 +1628,9 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root = pending->root;
+ struct btrfs_root *parent_root;
+ struct btrfs_block_rsv *rsv;
+- struct inode *parent_inode;
++ struct inode *parent_inode = pending->dir;
+ struct btrfs_path *path;
+ struct btrfs_dir_item *dir_item;
+- struct dentry *dentry;
+ struct extent_buffer *tmp;
+ struct extent_buffer *old;
+ struct timespec64 cur_time;
+@@ -1639,6 +1639,9 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
+ u64 index = 0;
+ u64 objectid;
+ u64 root_flags;
++ unsigned int nofs_flags;
++ struct fscrypt_name fname;
++ struct qstr name;
+
+ ASSERT(pending->path);
+ path = pending->path;
+@@ -1646,9 +1649,23 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
+ ASSERT(pending->root_item);
+ new_root_item = pending->root_item;
+
++ /*
++ * We're inside a transaction and must make sure that any potential
++ * allocations with GFP_KERNEL in fscrypt won't recurse back to
++ * filesystem.
++ */
++ nofs_flags = memalloc_nofs_save();
++ pending->error = fscrypt_setup_filename(parent_inode,
++ &pending->dentry->d_name, 0,
++ &fname);
++ memalloc_nofs_restore(nofs_flags);
++ if (pending->error)
++ goto free_pending;
++ name = (struct qstr)FSTR_TO_QSTR(&fname.disk_name);
++
+ pending->error = btrfs_get_free_objectid(tree_root, &objectid);
+ if (pending->error)
+- goto no_free_objectid;
++ goto free_fname;
+
+ /*
+ * Make qgroup to skip current new snapshot's qgroupid, as it is
+@@ -1677,8 +1694,6 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
+ trace_btrfs_space_reservation(fs_info, "transaction",
+ trans->transid,
+ trans->bytes_reserved, 1);
+- dentry = pending->dentry;
+- parent_inode = pending->dir;
+ parent_root = BTRFS_I(parent_inode)->root;
+ ret = record_root_in_trans(trans, parent_root, 0);
+ if (ret)
+@@ -1694,7 +1709,7 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
+ /* check if there is a file/dir which has the same name. */
+ dir_item = btrfs_lookup_dir_item(NULL, parent_root, path,
+ btrfs_ino(BTRFS_I(parent_inode)),
+- &dentry->d_name, 0);
++ &name, 0);
+ if (dir_item != NULL && !IS_ERR(dir_item)) {
+ pending->error = -EEXIST;
+ goto dir_item_existed;
+@@ -1789,7 +1804,7 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
+ ret = btrfs_add_root_ref(trans, objectid,
+ parent_root->root_key.objectid,
+ btrfs_ino(BTRFS_I(parent_inode)), index,
+- &dentry->d_name);
++ &name);
+ if (ret) {
+ btrfs_abort_transaction(trans, ret);
+ goto fail;
+@@ -1821,9 +1836,8 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
+ if (ret < 0)
+ goto fail;
+
+- ret = btrfs_insert_dir_item(trans, &dentry->d_name,
+- BTRFS_I(parent_inode), &key, BTRFS_FT_DIR,
+- index);
++ ret = btrfs_insert_dir_item(trans, &name, BTRFS_I(parent_inode), &key,
++ BTRFS_FT_DIR, index);
+ /* We have check then name at the beginning, so it is impossible. */
+ BUG_ON(ret == -EEXIST || ret == -EOVERFLOW);
+ if (ret) {
+@@ -1832,7 +1846,7 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
+ }
+
+ btrfs_i_size_write(BTRFS_I(parent_inode), parent_inode->i_size +
+- dentry->d_name.len * 2);
++ name.len * 2);
+ parent_inode->i_mtime = current_time(parent_inode);
+ parent_inode->i_ctime = parent_inode->i_mtime;
+ ret = btrfs_update_inode_fallback(trans, parent_root, BTRFS_I(parent_inode));
+@@ -1864,7 +1878,9 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
+ trans->bytes_reserved = 0;
+ clear_skip_qgroup:
+ btrfs_clear_skip_qgroup(trans);
+-no_free_objectid:
++free_fname:
++ fscrypt_free_filename(&fname);
++free_pending:
+ kfree(new_root_item);
+ pending->root_item = NULL;
+ btrfs_free_path(path);
+diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
+index 9f55e81acc0ef..25fd3f34b8f21 100644
+--- a/fs/btrfs/tree-log.c
++++ b/fs/btrfs/tree-log.c
+@@ -7471,9 +7471,16 @@ void btrfs_log_new_name(struct btrfs_trans_handle *trans,
+ if (old_dir && old_dir->logged_trans == trans->transid) {
+ struct btrfs_root *log = old_dir->root->log_root;
+ struct btrfs_path *path;
++ struct fscrypt_name fname;
++ struct qstr name;
+
+ ASSERT(old_dir_index >= BTRFS_DIR_START_INDEX);
+
++ ret = fscrypt_setup_filename(&old_dir->vfs_inode,
++ &old_dentry->d_name, 0, &fname);
++ if (ret)
++ goto out;
++ name = (struct qstr)FSTR_TO_QSTR(&fname.disk_name);
+ /*
+ * We have two inodes to update in the log, the old directory and
+ * the inode that got renamed, so we must pin the log to prevent
+@@ -7493,6 +7500,7 @@ void btrfs_log_new_name(struct btrfs_trans_handle *trans,
+ path = btrfs_alloc_path();
+ if (!path) {
+ ret = -ENOMEM;
++ fscrypt_free_filename(&fname);
+ goto out;
+ }
+
+@@ -7508,7 +7516,7 @@ void btrfs_log_new_name(struct btrfs_trans_handle *trans,
+ */
+ mutex_lock(&old_dir->log_mutex);
+ ret = del_logged_dentry(trans, log, path, btrfs_ino(old_dir),
+- &old_dentry->d_name, old_dir_index);
++ &name, old_dir_index);
+ if (ret > 0) {
+ /*
+ * The dentry does not exist in the log, so record its
+@@ -7522,6 +7530,7 @@ void btrfs_log_new_name(struct btrfs_trans_handle *trans,
+ mutex_unlock(&old_dir->log_mutex);
+
+ btrfs_free_path(path);
++ fscrypt_free_filename(&fname);
+ if (ret < 0)
+ goto out;
+ }
+--
+2.40.1
+
--- /dev/null
+From a3bedc8e6ad79645b5ddb23c0d404f4e8ea95003 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 20 Oct 2022 12:58:27 -0400
+Subject: btrfs: use struct fscrypt_str instead of struct qstr
+
+From: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
+
+[ Upstream commit 6db75318823a169e836a478ca57d6a7c0a156b77 ]
+
+While struct qstr is more natural without fscrypt, since it's provided
+by dentries, struct fscrypt_str is provided by the fscrypt handlers
+processing dentries, and is thus more natural in the fscrypt world.
+Replace all of the struct qstr uses with struct fscrypt_str.
+
+Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
+Reviewed-by: David Sterba <dsterba@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Stable-dep-of: 9af86694fd5d ("btrfs: file_remove_privs needs an exclusive lock in direct io write")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/btrfs/ctree.h | 19 +++++----
+ fs/btrfs/dir-item.c | 10 ++---
+ fs/btrfs/inode-item.c | 14 +++----
+ fs/btrfs/inode-item.h | 10 ++---
+ fs/btrfs/inode.c | 87 +++++++++++++++++-------------------------
+ fs/btrfs/ioctl.c | 4 +-
+ fs/btrfs/root-tree.c | 4 +-
+ fs/btrfs/send.c | 4 +-
+ fs/btrfs/super.c | 2 +-
+ fs/btrfs/transaction.c | 13 +++----
+ fs/btrfs/tree-log.c | 42 ++++++++++----------
+ fs/btrfs/tree-log.h | 4 +-
+ 12 files changed, 95 insertions(+), 118 deletions(-)
+
+diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
+index 5120cea15b096..27d06bb5e5c05 100644
+--- a/fs/btrfs/ctree.h
++++ b/fs/btrfs/ctree.h
+@@ -3240,10 +3240,10 @@ static inline void btrfs_clear_sb_rdonly(struct super_block *sb)
+ /* root-item.c */
+ int btrfs_add_root_ref(struct btrfs_trans_handle *trans, u64 root_id,
+ u64 ref_id, u64 dirid, u64 sequence,
+- const struct qstr *name);
++ const struct fscrypt_str *name);
+ int btrfs_del_root_ref(struct btrfs_trans_handle *trans, u64 root_id,
+ u64 ref_id, u64 dirid, u64 *sequence,
+- const struct qstr *name);
++ const struct fscrypt_str *name);
+ int btrfs_del_root(struct btrfs_trans_handle *trans,
+ const struct btrfs_key *key);
+ int btrfs_insert_root(struct btrfs_trans_handle *trans, struct btrfs_root *root,
+@@ -3272,23 +3272,23 @@ int btrfs_uuid_tree_iterate(struct btrfs_fs_info *fs_info);
+
+ /* dir-item.c */
+ int btrfs_check_dir_item_collision(struct btrfs_root *root, u64 dir,
+- const struct qstr *name);
++ const struct fscrypt_str *name);
+ int btrfs_insert_dir_item(struct btrfs_trans_handle *trans,
+- const struct qstr *name, struct btrfs_inode *dir,
++ const struct fscrypt_str *name, struct btrfs_inode *dir,
+ struct btrfs_key *location, u8 type, u64 index);
+ struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct btrfs_path *path, u64 dir,
+- const struct qstr *name, int mod);
++ const struct fscrypt_str *name, int mod);
+ struct btrfs_dir_item *
+ btrfs_lookup_dir_index_item(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct btrfs_path *path, u64 dir,
+- u64 index, const struct qstr *name, int mod);
++ u64 index, const struct fscrypt_str *name, int mod);
+ struct btrfs_dir_item *
+ btrfs_search_dir_index_item(struct btrfs_root *root,
+ struct btrfs_path *path, u64 dirid,
+- const struct qstr *name);
++ const struct fscrypt_str *name);
+ int btrfs_delete_one_dir_name(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct btrfs_path *path,
+@@ -3369,10 +3369,10 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry);
+ int btrfs_set_inode_index(struct btrfs_inode *dir, u64 *index);
+ int btrfs_unlink_inode(struct btrfs_trans_handle *trans,
+ struct btrfs_inode *dir, struct btrfs_inode *inode,
+- const struct qstr *name);
++ const struct fscrypt_str *name);
+ int btrfs_add_link(struct btrfs_trans_handle *trans,
+ struct btrfs_inode *parent_inode, struct btrfs_inode *inode,
+- const struct qstr *name, int add_backref, u64 index);
++ const struct fscrypt_str *name, int add_backref, u64 index);
+ int btrfs_delete_subvolume(struct inode *dir, struct dentry *dentry);
+ int btrfs_truncate_block(struct btrfs_inode *inode, loff_t from, loff_t len,
+ int front);
+@@ -3398,7 +3398,6 @@ struct btrfs_new_inode_args {
+ struct posix_acl *default_acl;
+ struct posix_acl *acl;
+ struct fscrypt_name fname;
+- struct qstr name;
+ };
+ int btrfs_new_inode_prepare(struct btrfs_new_inode_args *args,
+ unsigned int *trans_num_items);
+diff --git a/fs/btrfs/dir-item.c b/fs/btrfs/dir-item.c
+index 8c60f37eb13fd..fdab48c1abb8a 100644
+--- a/fs/btrfs/dir-item.c
++++ b/fs/btrfs/dir-item.c
+@@ -104,7 +104,7 @@ int btrfs_insert_xattr_item(struct btrfs_trans_handle *trans,
+ * Will return 0 or -ENOMEM
+ */
+ int btrfs_insert_dir_item(struct btrfs_trans_handle *trans,
+- const struct qstr *name, struct btrfs_inode *dir,
++ const struct fscrypt_str *name, struct btrfs_inode *dir,
+ struct btrfs_key *location, u8 type, u64 index)
+ {
+ int ret = 0;
+@@ -206,7 +206,7 @@ static struct btrfs_dir_item *btrfs_lookup_match_dir(
+ struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct btrfs_path *path, u64 dir,
+- const struct qstr *name,
++ const struct fscrypt_str *name,
+ int mod)
+ {
+ struct btrfs_key key;
+@@ -225,7 +225,7 @@ struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans,
+ }
+
+ int btrfs_check_dir_item_collision(struct btrfs_root *root, u64 dir,
+- const struct qstr *name)
++ const struct fscrypt_str *name)
+ {
+ int ret;
+ struct btrfs_key key;
+@@ -302,7 +302,7 @@ struct btrfs_dir_item *
+ btrfs_lookup_dir_index_item(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct btrfs_path *path, u64 dir,
+- u64 index, const struct qstr *name, int mod)
++ u64 index, const struct fscrypt_str *name, int mod)
+ {
+ struct btrfs_dir_item *di;
+ struct btrfs_key key;
+@@ -321,7 +321,7 @@ btrfs_lookup_dir_index_item(struct btrfs_trans_handle *trans,
+
+ struct btrfs_dir_item *
+ btrfs_search_dir_index_item(struct btrfs_root *root, struct btrfs_path *path,
+- u64 dirid, const struct qstr *name)
++ u64 dirid, const struct fscrypt_str *name)
+ {
+ struct btrfs_dir_item *di;
+ struct btrfs_key key;
+diff --git a/fs/btrfs/inode-item.c b/fs/btrfs/inode-item.c
+index 61b323517a40b..5add022d3534f 100644
+--- a/fs/btrfs/inode-item.c
++++ b/fs/btrfs/inode-item.c
+@@ -11,7 +11,7 @@
+
+ struct btrfs_inode_ref *btrfs_find_name_in_backref(struct extent_buffer *leaf,
+ int slot,
+- const struct qstr *name)
++ const struct fscrypt_str *name)
+ {
+ struct btrfs_inode_ref *ref;
+ unsigned long ptr;
+@@ -38,7 +38,7 @@ struct btrfs_inode_ref *btrfs_find_name_in_backref(struct extent_buffer *leaf,
+
+ struct btrfs_inode_extref *btrfs_find_name_in_ext_backref(
+ struct extent_buffer *leaf, int slot, u64 ref_objectid,
+- const struct qstr *name)
++ const struct fscrypt_str *name)
+ {
+ struct btrfs_inode_extref *extref;
+ unsigned long ptr;
+@@ -77,7 +77,7 @@ struct btrfs_inode_extref *
+ btrfs_lookup_inode_extref(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct btrfs_path *path,
+- const struct qstr *name,
++ const struct fscrypt_str *name,
+ u64 inode_objectid, u64 ref_objectid, int ins_len,
+ int cow)
+ {
+@@ -100,7 +100,7 @@ btrfs_lookup_inode_extref(struct btrfs_trans_handle *trans,
+
+ static int btrfs_del_inode_extref(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+- const struct qstr *name,
++ const struct fscrypt_str *name,
+ u64 inode_objectid, u64 ref_objectid,
+ u64 *index)
+ {
+@@ -170,7 +170,7 @@ static int btrfs_del_inode_extref(struct btrfs_trans_handle *trans,
+ }
+
+ int btrfs_del_inode_ref(struct btrfs_trans_handle *trans,
+- struct btrfs_root *root, const struct qstr *name,
++ struct btrfs_root *root, const struct fscrypt_str *name,
+ u64 inode_objectid, u64 ref_objectid, u64 *index)
+ {
+ struct btrfs_path *path;
+@@ -247,7 +247,7 @@ int btrfs_del_inode_ref(struct btrfs_trans_handle *trans,
+ */
+ static int btrfs_insert_inode_extref(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+- const struct qstr *name,
++ const struct fscrypt_str *name,
+ u64 inode_objectid, u64 ref_objectid,
+ u64 index)
+ {
+@@ -302,7 +302,7 @@ static int btrfs_insert_inode_extref(struct btrfs_trans_handle *trans,
+
+ /* Will return 0, -ENOMEM, -EMLINK, or -EEXIST or anything from the CoW path */
+ int btrfs_insert_inode_ref(struct btrfs_trans_handle *trans,
+- struct btrfs_root *root, const struct qstr *name,
++ struct btrfs_root *root, const struct fscrypt_str *name,
+ u64 inode_objectid, u64 ref_objectid, u64 index)
+ {
+ struct btrfs_fs_info *fs_info = root->fs_info;
+diff --git a/fs/btrfs/inode-item.h b/fs/btrfs/inode-item.h
+index 3c657c670cfdf..b80aeb7157010 100644
+--- a/fs/btrfs/inode-item.h
++++ b/fs/btrfs/inode-item.h
+@@ -64,10 +64,10 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct btrfs_truncate_control *control);
+ int btrfs_insert_inode_ref(struct btrfs_trans_handle *trans,
+- struct btrfs_root *root, const struct qstr *name,
++ struct btrfs_root *root, const struct fscrypt_str *name,
+ u64 inode_objectid, u64 ref_objectid, u64 index);
+ int btrfs_del_inode_ref(struct btrfs_trans_handle *trans,
+- struct btrfs_root *root, const struct qstr *name,
++ struct btrfs_root *root, const struct fscrypt_str *name,
+ u64 inode_objectid, u64 ref_objectid, u64 *index);
+ int btrfs_insert_empty_inode(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+@@ -80,15 +80,15 @@ struct btrfs_inode_extref *btrfs_lookup_inode_extref(
+ struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct btrfs_path *path,
+- const struct qstr *name,
++ const struct fscrypt_str *name,
+ u64 inode_objectid, u64 ref_objectid, int ins_len,
+ int cow);
+
+ struct btrfs_inode_ref *btrfs_find_name_in_backref(struct extent_buffer *leaf,
+ int slot,
+- const struct qstr *name);
++ const struct fscrypt_str *name);
+ struct btrfs_inode_extref *btrfs_find_name_in_ext_backref(
+ struct extent_buffer *leaf, int slot, u64 ref_objectid,
+- const struct qstr *name);
++ const struct fscrypt_str *name);
+
+ #endif
+diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
+index b5224dbaa4165..47c5be597368b 100644
+--- a/fs/btrfs/inode.c
++++ b/fs/btrfs/inode.c
+@@ -4272,7 +4272,7 @@ int btrfs_update_inode_fallback(struct btrfs_trans_handle *trans,
+ static int __btrfs_unlink_inode(struct btrfs_trans_handle *trans,
+ struct btrfs_inode *dir,
+ struct btrfs_inode *inode,
+- const struct qstr *name,
++ const struct fscrypt_str *name,
+ struct btrfs_rename_ctx *rename_ctx)
+ {
+ struct btrfs_root *root = dir->root;
+@@ -4375,7 +4375,7 @@ static int __btrfs_unlink_inode(struct btrfs_trans_handle *trans,
+
+ int btrfs_unlink_inode(struct btrfs_trans_handle *trans,
+ struct btrfs_inode *dir, struct btrfs_inode *inode,
+- const struct qstr *name)
++ const struct fscrypt_str *name)
+ {
+ int ret;
+
+@@ -4416,12 +4416,10 @@ static int btrfs_unlink(struct inode *dir, struct dentry *dentry)
+ struct inode *inode = d_inode(dentry);
+ int ret;
+ struct fscrypt_name fname;
+- struct qstr name;
+
+ ret = fscrypt_setup_filename(dir, &dentry->d_name, 1, &fname);
+ if (ret)
+ return ret;
+- name = (struct qstr)FSTR_TO_QSTR(&fname.disk_name);
+
+ /* This needs to handle no-key deletions later on */
+
+@@ -4435,7 +4433,7 @@ static int btrfs_unlink(struct inode *dir, struct dentry *dentry)
+ 0);
+
+ ret = btrfs_unlink_inode(trans, BTRFS_I(dir), BTRFS_I(d_inode(dentry)),
+- &name);
++ &fname.disk_name);
+ if (ret)
+ goto end_trans;
+
+@@ -4462,7 +4460,6 @@ static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans,
+ struct extent_buffer *leaf;
+ struct btrfs_dir_item *di;
+ struct btrfs_key key;
+- struct qstr name;
+ u64 index;
+ int ret;
+ u64 objectid;
+@@ -4472,7 +4469,6 @@ static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans,
+ ret = fscrypt_setup_filename(dir, &dentry->d_name, 1, &fname);
+ if (ret)
+ return ret;
+- name = (struct qstr)FSTR_TO_QSTR(&fname.disk_name);
+
+ /* This needs to handle no-key deletions later on */
+
+@@ -4492,7 +4488,8 @@ static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans,
+ goto out;
+ }
+
+- di = btrfs_lookup_dir_item(trans, root, path, dir_ino, &name, -1);
++ di = btrfs_lookup_dir_item(trans, root, path, dir_ino,
++ &fname.disk_name, -1);
+ if (IS_ERR_OR_NULL(di)) {
+ ret = di ? PTR_ERR(di) : -ENOENT;
+ goto out;
+@@ -4518,7 +4515,7 @@ static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans,
+ * call btrfs_del_root_ref, and it _shouldn't_ fail.
+ */
+ if (btrfs_ino(inode) == BTRFS_EMPTY_SUBVOL_DIR_OBJECTID) {
+- di = btrfs_search_dir_index_item(root, path, dir_ino, &name);
++ di = btrfs_search_dir_index_item(root, path, dir_ino, &fname.disk_name);
+ if (IS_ERR_OR_NULL(di)) {
+ if (!di)
+ ret = -ENOENT;
+@@ -4535,7 +4532,7 @@ static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans,
+ } else {
+ ret = btrfs_del_root_ref(trans, objectid,
+ root->root_key.objectid, dir_ino,
+- &index, &name);
++ &index, &fname.disk_name);
+ if (ret) {
+ btrfs_abort_transaction(trans, ret);
+ goto out;
+@@ -4548,7 +4545,7 @@ static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans,
+ goto out;
+ }
+
+- btrfs_i_size_write(BTRFS_I(dir), dir->i_size - name.len * 2);
++ btrfs_i_size_write(BTRFS_I(dir), dir->i_size - fname.disk_name.len * 2);
+ inode_inc_iversion(dir);
+ dir->i_mtime = current_time(dir);
+ dir->i_ctime = dir->i_mtime;
+@@ -4571,7 +4568,7 @@ static noinline int may_destroy_subvol(struct btrfs_root *root)
+ struct btrfs_path *path;
+ struct btrfs_dir_item *di;
+ struct btrfs_key key;
+- struct qstr name = QSTR_INIT("default", 7);
++ struct fscrypt_str name = FSTR_INIT("default", 7);
+ u64 dir_id;
+ int ret;
+
+@@ -4822,7 +4819,6 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry)
+ struct btrfs_trans_handle *trans;
+ u64 last_unlink_trans;
+ struct fscrypt_name fname;
+- struct qstr name;
+
+ if (inode->i_size > BTRFS_EMPTY_DIR_SIZE)
+ return -ENOTEMPTY;
+@@ -4838,7 +4834,6 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry)
+ err = fscrypt_setup_filename(dir, &dentry->d_name, 1, &fname);
+ if (err)
+ return err;
+- name = (struct qstr)FSTR_TO_QSTR(&fname.disk_name);
+
+ /* This needs to handle no-key deletions later on */
+
+@@ -4861,7 +4856,7 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry)
+
+ /* now the directory is empty */
+ err = btrfs_unlink_inode(trans, BTRFS_I(dir), BTRFS_I(d_inode(dentry)),
+- &name);
++ &fname.disk_name);
+ if (!err) {
+ btrfs_i_size_write(BTRFS_I(inode), 0);
+ /*
+@@ -5563,7 +5558,6 @@ void btrfs_evict_inode(struct inode *inode)
+ static int btrfs_inode_by_name(struct inode *dir, struct dentry *dentry,
+ struct btrfs_key *location, u8 *type)
+ {
+- struct qstr name;
+ struct btrfs_dir_item *di;
+ struct btrfs_path *path;
+ struct btrfs_root *root = BTRFS_I(dir)->root;
+@@ -5578,12 +5572,10 @@ static int btrfs_inode_by_name(struct inode *dir, struct dentry *dentry,
+ if (ret)
+ goto out;
+
+- name = (struct qstr)FSTR_TO_QSTR(&fname.disk_name);
+-
+ /* This needs to handle no-key deletions later on */
+
+ di = btrfs_lookup_dir_item(NULL, root, path, btrfs_ino(BTRFS_I(dir)),
+- &name, 0);
++ &fname.disk_name, 0);
+ if (IS_ERR_OR_NULL(di)) {
+ ret = di ? PTR_ERR(di) : -ENOENT;
+ goto out;
+@@ -5595,7 +5587,7 @@ static int btrfs_inode_by_name(struct inode *dir, struct dentry *dentry,
+ ret = -EUCLEAN;
+ btrfs_warn(root->fs_info,
+ "%s gets something invalid in DIR_ITEM (name %s, directory ino %llu, location(%llu %u %llu))",
+- __func__, name.name, btrfs_ino(BTRFS_I(dir)),
++ __func__, fname.disk_name.name, btrfs_ino(BTRFS_I(dir)),
+ location->objectid, location->type, location->offset);
+ }
+ if (!ret)
+@@ -5625,14 +5617,11 @@ static int fixup_tree_root_location(struct btrfs_fs_info *fs_info,
+ int ret;
+ int err = 0;
+ struct fscrypt_name fname;
+- struct qstr name;
+
+ ret = fscrypt_setup_filename(dir, &dentry->d_name, 0, &fname);
+ if (ret)
+ return ret;
+
+- name = (struct qstr)FSTR_TO_QSTR(&fname.disk_name);
+-
+ path = btrfs_alloc_path();
+ if (!path) {
+ err = -ENOMEM;
+@@ -5654,11 +5643,11 @@ static int fixup_tree_root_location(struct btrfs_fs_info *fs_info,
+ leaf = path->nodes[0];
+ ref = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_root_ref);
+ if (btrfs_root_ref_dirid(leaf, ref) != btrfs_ino(BTRFS_I(dir)) ||
+- btrfs_root_ref_name_len(leaf, ref) != name.len)
++ btrfs_root_ref_name_len(leaf, ref) != fname.disk_name.len)
+ goto out;
+
+- ret = memcmp_extent_buffer(leaf, name.name, (unsigned long)(ref + 1),
+- name.len);
++ ret = memcmp_extent_buffer(leaf, fname.disk_name.name,
++ (unsigned long)(ref + 1), fname.disk_name.len);
+ if (ret)
+ goto out;
+
+@@ -6291,7 +6280,6 @@ int btrfs_new_inode_prepare(struct btrfs_new_inode_args *args,
+ &args->fname);
+ if (ret)
+ return ret;
+- args->name = (struct qstr)FSTR_TO_QSTR(&args->fname.disk_name);
+ }
+
+ ret = posix_acl_create(dir, &inode->i_mode, &args->default_acl, &args->acl);
+@@ -6374,7 +6362,7 @@ int btrfs_create_new_inode(struct btrfs_trans_handle *trans,
+ {
+ struct inode *dir = args->dir;
+ struct inode *inode = args->inode;
+- const struct qstr *name = args->orphan ? NULL : &args->dentry->d_name;
++ const struct fscrypt_str *name = args->orphan ? NULL : &args->fname.disk_name;
+ struct btrfs_fs_info *fs_info = btrfs_sb(dir->i_sb);
+ struct btrfs_root *root;
+ struct btrfs_inode_item *inode_item;
+@@ -6609,7 +6597,7 @@ int btrfs_create_new_inode(struct btrfs_trans_handle *trans,
+ */
+ int btrfs_add_link(struct btrfs_trans_handle *trans,
+ struct btrfs_inode *parent_inode, struct btrfs_inode *inode,
+- const struct qstr *name, int add_backref, u64 index)
++ const struct fscrypt_str *name, int add_backref, u64 index)
+ {
+ int ret = 0;
+ struct btrfs_key key;
+@@ -6765,7 +6753,6 @@ static int btrfs_link(struct dentry *old_dentry, struct inode *dir,
+ struct inode *inode = d_inode(old_dentry);
+ struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
+ struct fscrypt_name fname;
+- struct qstr name;
+ u64 index;
+ int err;
+ int drop_inode = 0;
+@@ -6781,8 +6768,6 @@ static int btrfs_link(struct dentry *old_dentry, struct inode *dir,
+ if (err)
+ goto fail;
+
+- name = (struct qstr)FSTR_TO_QSTR(&fname.disk_name);
+-
+ err = btrfs_set_inode_index(BTRFS_I(dir), &index);
+ if (err)
+ goto fail;
+@@ -6809,7 +6794,7 @@ static int btrfs_link(struct dentry *old_dentry, struct inode *dir,
+ set_bit(BTRFS_INODE_COPY_EVERYTHING, &BTRFS_I(inode)->runtime_flags);
+
+ err = btrfs_add_link(trans, BTRFS_I(dir), BTRFS_I(inode),
+- &name, 1, index);
++ &fname.disk_name, 1, index);
+
+ if (err) {
+ drop_inode = 1;
+@@ -9107,7 +9092,7 @@ static int btrfs_rename_exchange(struct inode *old_dir,
+ int ret2;
+ bool need_abort = false;
+ struct fscrypt_name old_fname, new_fname;
+- struct qstr old_name, new_name;
++ struct fscrypt_str *old_name, *new_name;
+
+ /*
+ * For non-subvolumes allow exchange only within one subvolume, in the
+@@ -9129,8 +9114,8 @@ static int btrfs_rename_exchange(struct inode *old_dir,
+ return ret;
+ }
+
+- old_name = (struct qstr)FSTR_TO_QSTR(&old_fname.disk_name);
+- new_name = (struct qstr)FSTR_TO_QSTR(&new_fname.disk_name);
++ old_name = &old_fname.disk_name;
++ new_name = &new_fname.disk_name;
+
+ /* close the race window with snapshot create/destroy ioctl */
+ if (old_ino == BTRFS_FIRST_FREE_OBJECTID ||
+@@ -9199,7 +9184,7 @@ static int btrfs_rename_exchange(struct inode *old_dir,
+ /* force full log commit if subvolume involved. */
+ btrfs_set_log_full_commit(trans);
+ } else {
+- ret = btrfs_insert_inode_ref(trans, dest, &new_name, old_ino,
++ ret = btrfs_insert_inode_ref(trans, dest, new_name, old_ino,
+ btrfs_ino(BTRFS_I(new_dir)),
+ old_idx);
+ if (ret)
+@@ -9212,7 +9197,7 @@ static int btrfs_rename_exchange(struct inode *old_dir,
+ /* force full log commit if subvolume involved. */
+ btrfs_set_log_full_commit(trans);
+ } else {
+- ret = btrfs_insert_inode_ref(trans, root, &old_name, new_ino,
++ ret = btrfs_insert_inode_ref(trans, root, old_name, new_ino,
+ btrfs_ino(BTRFS_I(old_dir)),
+ new_idx);
+ if (ret) {
+@@ -9247,7 +9232,7 @@ static int btrfs_rename_exchange(struct inode *old_dir,
+ } else { /* src is an inode */
+ ret = __btrfs_unlink_inode(trans, BTRFS_I(old_dir),
+ BTRFS_I(old_dentry->d_inode),
+- &old_name, &old_rename_ctx);
++ old_name, &old_rename_ctx);
+ if (!ret)
+ ret = btrfs_update_inode(trans, root, BTRFS_I(old_inode));
+ }
+@@ -9262,7 +9247,7 @@ static int btrfs_rename_exchange(struct inode *old_dir,
+ } else { /* dest is an inode */
+ ret = __btrfs_unlink_inode(trans, BTRFS_I(new_dir),
+ BTRFS_I(new_dentry->d_inode),
+- &new_name, &new_rename_ctx);
++ new_name, &new_rename_ctx);
+ if (!ret)
+ ret = btrfs_update_inode(trans, dest, BTRFS_I(new_inode));
+ }
+@@ -9272,14 +9257,14 @@ static int btrfs_rename_exchange(struct inode *old_dir,
+ }
+
+ ret = btrfs_add_link(trans, BTRFS_I(new_dir), BTRFS_I(old_inode),
+- &new_name, 0, old_idx);
++ new_name, 0, old_idx);
+ if (ret) {
+ btrfs_abort_transaction(trans, ret);
+ goto out_fail;
+ }
+
+ ret = btrfs_add_link(trans, BTRFS_I(old_dir), BTRFS_I(new_inode),
+- &old_name, 0, new_idx);
++ old_name, 0, new_idx);
+ if (ret) {
+ btrfs_abort_transaction(trans, ret);
+ goto out_fail;
+@@ -9364,7 +9349,6 @@ static int btrfs_rename(struct user_namespace *mnt_userns,
+ int ret2;
+ u64 old_ino = btrfs_ino(BTRFS_I(old_inode));
+ struct fscrypt_name old_fname, new_fname;
+- struct qstr old_name, new_name;
+
+ if (btrfs_ino(BTRFS_I(new_dir)) == BTRFS_EMPTY_SUBVOL_DIR_OBJECTID)
+ return -EPERM;
+@@ -9391,12 +9375,8 @@ static int btrfs_rename(struct user_namespace *mnt_userns,
+ return ret;
+ }
+
+- old_name = (struct qstr)FSTR_TO_QSTR(&old_fname.disk_name);
+- new_name = (struct qstr)FSTR_TO_QSTR(&new_fname.disk_name);
+-
+ /* check for collisions, even if the name isn't there */
+- ret = btrfs_check_dir_item_collision(dest, new_dir->i_ino, &new_name);
+-
++ ret = btrfs_check_dir_item_collision(dest, new_dir->i_ino, &new_fname.disk_name);
+ if (ret) {
+ if (ret == -EEXIST) {
+ /* we shouldn't get
+@@ -9489,8 +9469,9 @@ static int btrfs_rename(struct user_namespace *mnt_userns,
+ /* force full log commit if subvolume involved. */
+ btrfs_set_log_full_commit(trans);
+ } else {
+- ret = btrfs_insert_inode_ref(trans, dest, &new_name, old_ino,
+- btrfs_ino(BTRFS_I(new_dir)), index);
++ ret = btrfs_insert_inode_ref(trans, dest, &new_fname.disk_name,
++ old_ino, btrfs_ino(BTRFS_I(new_dir)),
++ index);
+ if (ret)
+ goto out_fail;
+ }
+@@ -9513,7 +9494,7 @@ static int btrfs_rename(struct user_namespace *mnt_userns,
+ } else {
+ ret = __btrfs_unlink_inode(trans, BTRFS_I(old_dir),
+ BTRFS_I(d_inode(old_dentry)),
+- &old_name, &rename_ctx);
++ &old_fname.disk_name, &rename_ctx);
+ if (!ret)
+ ret = btrfs_update_inode(trans, root, BTRFS_I(old_inode));
+ }
+@@ -9532,7 +9513,7 @@ static int btrfs_rename(struct user_namespace *mnt_userns,
+ } else {
+ ret = btrfs_unlink_inode(trans, BTRFS_I(new_dir),
+ BTRFS_I(d_inode(new_dentry)),
+- &new_name);
++ &new_fname.disk_name);
+ }
+ if (!ret && new_inode->i_nlink == 0)
+ ret = btrfs_orphan_add(trans,
+@@ -9544,7 +9525,7 @@ static int btrfs_rename(struct user_namespace *mnt_userns,
+ }
+
+ ret = btrfs_add_link(trans, BTRFS_I(new_dir), BTRFS_I(old_inode),
+- &new_name, 0, index);
++ &new_fname.disk_name, 0, index);
+ if (ret) {
+ btrfs_abort_transaction(trans, ret);
+ goto out_fail;
+diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
+index 5305d98905cea..9e323420c96d3 100644
+--- a/fs/btrfs/ioctl.c
++++ b/fs/btrfs/ioctl.c
+@@ -951,7 +951,7 @@ static noinline int btrfs_mksubvol(const struct path *parent,
+ struct inode *dir = d_inode(parent->dentry);
+ struct btrfs_fs_info *fs_info = btrfs_sb(dir->i_sb);
+ struct dentry *dentry;
+- struct qstr name_str = QSTR_INIT(name, namelen);
++ struct fscrypt_str name_str = FSTR_INIT((char *)name, namelen);
+ int error;
+
+ error = down_write_killable_nested(&dir->i_rwsem, I_MUTEX_PARENT);
+@@ -3782,7 +3782,7 @@ static long btrfs_ioctl_default_subvol(struct file *file, void __user *argp)
+ struct btrfs_trans_handle *trans;
+ struct btrfs_path *path = NULL;
+ struct btrfs_disk_key disk_key;
+- struct qstr name = QSTR_INIT("default", 7);
++ struct fscrypt_str name = FSTR_INIT("default", 7);
+ u64 objectid = 0;
+ u64 dir_id;
+ int ret;
+diff --git a/fs/btrfs/root-tree.c b/fs/btrfs/root-tree.c
+index cf29241b9b310..7d783f0943068 100644
+--- a/fs/btrfs/root-tree.c
++++ b/fs/btrfs/root-tree.c
+@@ -328,7 +328,7 @@ int btrfs_del_root(struct btrfs_trans_handle *trans,
+
+ int btrfs_del_root_ref(struct btrfs_trans_handle *trans, u64 root_id,
+ u64 ref_id, u64 dirid, u64 *sequence,
+- const struct qstr *name)
++ const struct fscrypt_str *name)
+ {
+ struct btrfs_root *tree_root = trans->fs_info->tree_root;
+ struct btrfs_path *path;
+@@ -400,7 +400,7 @@ int btrfs_del_root_ref(struct btrfs_trans_handle *trans, u64 root_id,
+ */
+ int btrfs_add_root_ref(struct btrfs_trans_handle *trans, u64 root_id,
+ u64 ref_id, u64 dirid, u64 sequence,
+- const struct qstr *name)
++ const struct fscrypt_str *name)
+ {
+ struct btrfs_root *tree_root = trans->fs_info->tree_root;
+ struct btrfs_key key;
+diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c
+index 833364527554c..547b5c2292186 100644
+--- a/fs/btrfs/send.c
++++ b/fs/btrfs/send.c
+@@ -1596,7 +1596,7 @@ static int gen_unique_name(struct send_ctx *sctx,
+ return -ENOMEM;
+
+ while (1) {
+- struct qstr tmp_name;
++ struct fscrypt_str tmp_name;
+
+ len = snprintf(tmp, sizeof(tmp), "o%llu-%llu-%llu",
+ ino, gen, idx);
+@@ -1756,7 +1756,7 @@ static int lookup_dir_item_inode(struct btrfs_root *root,
+ struct btrfs_dir_item *di;
+ struct btrfs_key key;
+ struct btrfs_path *path;
+- struct qstr name_str = QSTR_INIT(name, name_len);
++ struct fscrypt_str name_str = FSTR_INIT((char *)name, name_len);
+
+ path = alloc_path_for_send();
+ if (!path)
+diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
+index bf56e4d6b9f48..2c562febd801e 100644
+--- a/fs/btrfs/super.c
++++ b/fs/btrfs/super.c
+@@ -1398,7 +1398,7 @@ static int get_default_subvol_objectid(struct btrfs_fs_info *fs_info, u64 *objec
+ struct btrfs_dir_item *di;
+ struct btrfs_path *path;
+ struct btrfs_key location;
+- struct qstr name = QSTR_INIT("default", 7);
++ struct fscrypt_str name = FSTR_INIT("default", 7);
+ u64 dir_id;
+
+ path = btrfs_alloc_path();
+diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c
+index c8918bdf15ccd..1193214ba8c10 100644
+--- a/fs/btrfs/transaction.c
++++ b/fs/btrfs/transaction.c
+@@ -1641,7 +1641,6 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
+ u64 root_flags;
+ unsigned int nofs_flags;
+ struct fscrypt_name fname;
+- struct qstr name;
+
+ ASSERT(pending->path);
+ path = pending->path;
+@@ -1661,7 +1660,6 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
+ memalloc_nofs_restore(nofs_flags);
+ if (pending->error)
+ goto free_pending;
+- name = (struct qstr)FSTR_TO_QSTR(&fname.disk_name);
+
+ pending->error = btrfs_get_free_objectid(tree_root, &objectid);
+ if (pending->error)
+@@ -1709,7 +1707,7 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
+ /* check if there is a file/dir which has the same name. */
+ dir_item = btrfs_lookup_dir_item(NULL, parent_root, path,
+ btrfs_ino(BTRFS_I(parent_inode)),
+- &name, 0);
++ &fname.disk_name, 0);
+ if (dir_item != NULL && !IS_ERR(dir_item)) {
+ pending->error = -EEXIST;
+ goto dir_item_existed;
+@@ -1804,7 +1802,7 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
+ ret = btrfs_add_root_ref(trans, objectid,
+ parent_root->root_key.objectid,
+ btrfs_ino(BTRFS_I(parent_inode)), index,
+- &name);
++ &fname.disk_name);
+ if (ret) {
+ btrfs_abort_transaction(trans, ret);
+ goto fail;
+@@ -1836,8 +1834,9 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
+ if (ret < 0)
+ goto fail;
+
+- ret = btrfs_insert_dir_item(trans, &name, BTRFS_I(parent_inode), &key,
+- BTRFS_FT_DIR, index);
++ ret = btrfs_insert_dir_item(trans, &fname.disk_name,
++ BTRFS_I(parent_inode), &key, BTRFS_FT_DIR,
++ index);
+ /* We have check then name at the beginning, so it is impossible. */
+ BUG_ON(ret == -EEXIST || ret == -EOVERFLOW);
+ if (ret) {
+@@ -1846,7 +1845,7 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
+ }
+
+ btrfs_i_size_write(BTRFS_I(parent_inode), parent_inode->i_size +
+- name.len * 2);
++ fname.disk_name.len * 2);
+ parent_inode->i_mtime = current_time(parent_inode);
+ parent_inode->i_ctime = parent_inode->i_mtime;
+ ret = btrfs_update_inode_fallback(trans, parent_root, BTRFS_I(parent_inode));
+diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
+index 25fd3f34b8f21..ab7893debf07a 100644
+--- a/fs/btrfs/tree-log.c
++++ b/fs/btrfs/tree-log.c
+@@ -596,7 +596,7 @@ static int overwrite_item(struct btrfs_trans_handle *trans,
+ }
+
+ static int read_alloc_one_name(struct extent_buffer *eb, void *start, int len,
+- struct qstr *name)
++ struct fscrypt_str *name)
+ {
+ char *buf;
+
+@@ -916,7 +916,7 @@ static noinline int replay_one_extent(struct btrfs_trans_handle *trans,
+ static int unlink_inode_for_log_replay(struct btrfs_trans_handle *trans,
+ struct btrfs_inode *dir,
+ struct btrfs_inode *inode,
+- const struct qstr *name)
++ const struct fscrypt_str *name)
+ {
+ int ret;
+
+@@ -947,7 +947,7 @@ static noinline int drop_one_dir_item(struct btrfs_trans_handle *trans,
+ {
+ struct btrfs_root *root = dir->root;
+ struct inode *inode;
+- struct qstr name;
++ struct fscrypt_str name;
+ struct extent_buffer *leaf;
+ struct btrfs_key location;
+ int ret;
+@@ -988,7 +988,7 @@ static noinline int drop_one_dir_item(struct btrfs_trans_handle *trans,
+ static noinline int inode_in_dir(struct btrfs_root *root,
+ struct btrfs_path *path,
+ u64 dirid, u64 objectid, u64 index,
+- struct qstr *name)
++ struct fscrypt_str *name)
+ {
+ struct btrfs_dir_item *di;
+ struct btrfs_key location;
+@@ -1035,7 +1035,7 @@ static noinline int inode_in_dir(struct btrfs_root *root,
+ static noinline int backref_in_log(struct btrfs_root *log,
+ struct btrfs_key *key,
+ u64 ref_objectid,
+- const struct qstr *name)
++ const struct fscrypt_str *name)
+ {
+ struct btrfs_path *path;
+ int ret;
+@@ -1071,7 +1071,7 @@ static inline int __add_inode_ref(struct btrfs_trans_handle *trans,
+ struct btrfs_inode *dir,
+ struct btrfs_inode *inode,
+ u64 inode_objectid, u64 parent_objectid,
+- u64 ref_index, struct qstr *name)
++ u64 ref_index, struct fscrypt_str *name)
+ {
+ int ret;
+ struct extent_buffer *leaf;
+@@ -1105,7 +1105,7 @@ static inline int __add_inode_ref(struct btrfs_trans_handle *trans,
+ ptr = btrfs_item_ptr_offset(leaf, path->slots[0]);
+ ptr_end = ptr + btrfs_item_size(leaf, path->slots[0]);
+ while (ptr < ptr_end) {
+- struct qstr victim_name;
++ struct fscrypt_str victim_name;
+
+ victim_ref = (struct btrfs_inode_ref *)ptr;
+ ret = read_alloc_one_name(leaf, (victim_ref + 1),
+@@ -1155,7 +1155,7 @@ static inline int __add_inode_ref(struct btrfs_trans_handle *trans,
+ base = btrfs_item_ptr_offset(leaf, path->slots[0]);
+
+ while (cur_offset < item_size) {
+- struct qstr victim_name;
++ struct fscrypt_str victim_name;
+
+ extref = (struct btrfs_inode_extref *)(base + cur_offset);
+
+@@ -1230,7 +1230,7 @@ static inline int __add_inode_ref(struct btrfs_trans_handle *trans,
+ }
+
+ static int extref_get_fields(struct extent_buffer *eb, unsigned long ref_ptr,
+- struct qstr *name, u64 *index,
++ struct fscrypt_str *name, u64 *index,
+ u64 *parent_objectid)
+ {
+ struct btrfs_inode_extref *extref;
+@@ -1252,7 +1252,7 @@ static int extref_get_fields(struct extent_buffer *eb, unsigned long ref_ptr,
+ }
+
+ static int ref_get_fields(struct extent_buffer *eb, unsigned long ref_ptr,
+- struct qstr *name, u64 *index)
++ struct fscrypt_str *name, u64 *index)
+ {
+ struct btrfs_inode_ref *ref;
+ int ret;
+@@ -1304,7 +1304,7 @@ static int unlink_old_inode_refs(struct btrfs_trans_handle *trans,
+ ref_ptr = btrfs_item_ptr_offset(eb, path->slots[0]);
+ ref_end = ref_ptr + btrfs_item_size(eb, path->slots[0]);
+ while (ref_ptr < ref_end) {
+- struct qstr name;
++ struct fscrypt_str name;
+ u64 parent_id;
+
+ if (key->type == BTRFS_INODE_EXTREF_KEY) {
+@@ -1372,7 +1372,7 @@ static noinline int add_inode_ref(struct btrfs_trans_handle *trans,
+ struct inode *inode = NULL;
+ unsigned long ref_ptr;
+ unsigned long ref_end;
+- struct qstr name;
++ struct fscrypt_str name;
+ int ret;
+ int log_ref_ver = 0;
+ u64 parent_objectid;
+@@ -1766,7 +1766,7 @@ static noinline int link_to_fixup_dir(struct btrfs_trans_handle *trans,
+ static noinline int insert_one_name(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ u64 dirid, u64 index,
+- const struct qstr *name,
++ const struct fscrypt_str *name,
+ struct btrfs_key *location)
+ {
+ struct inode *inode;
+@@ -1844,7 +1844,7 @@ static noinline int replay_one_name(struct btrfs_trans_handle *trans,
+ struct btrfs_dir_item *di,
+ struct btrfs_key *key)
+ {
+- struct qstr name;
++ struct fscrypt_str name;
+ struct btrfs_dir_item *dir_dst_di;
+ struct btrfs_dir_item *index_dst_di;
+ bool dir_dst_matches = false;
+@@ -2124,7 +2124,7 @@ static noinline int check_item_in_log(struct btrfs_trans_handle *trans,
+ struct extent_buffer *eb;
+ int slot;
+ struct btrfs_dir_item *di;
+- struct qstr name;
++ struct fscrypt_str name;
+ struct inode *inode = NULL;
+ struct btrfs_key location;
+
+@@ -3417,7 +3417,7 @@ static int del_logged_dentry(struct btrfs_trans_handle *trans,
+ struct btrfs_root *log,
+ struct btrfs_path *path,
+ u64 dir_ino,
+- const struct qstr *name,
++ const struct fscrypt_str *name,
+ u64 index)
+ {
+ struct btrfs_dir_item *di;
+@@ -3464,7 +3464,7 @@ static int del_logged_dentry(struct btrfs_trans_handle *trans,
+ */
+ void btrfs_del_dir_entries_in_log(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+- const struct qstr *name,
++ const struct fscrypt_str *name,
+ struct btrfs_inode *dir, u64 index)
+ {
+ struct btrfs_path *path;
+@@ -3503,7 +3503,7 @@ void btrfs_del_dir_entries_in_log(struct btrfs_trans_handle *trans,
+ /* see comments for btrfs_del_dir_entries_in_log */
+ void btrfs_del_inode_ref_in_log(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+- const struct qstr *name,
++ const struct fscrypt_str *name,
+ struct btrfs_inode *inode, u64 dirid)
+ {
+ struct btrfs_root *log;
+@@ -5267,7 +5267,7 @@ static int btrfs_check_ref_name_override(struct extent_buffer *eb,
+ u32 this_len;
+ unsigned long name_ptr;
+ struct btrfs_dir_item *di;
+- struct qstr name_str;
++ struct fscrypt_str name_str;
+
+ if (key->type == BTRFS_INODE_REF_KEY) {
+ struct btrfs_inode_ref *iref;
+@@ -7472,7 +7472,6 @@ void btrfs_log_new_name(struct btrfs_trans_handle *trans,
+ struct btrfs_root *log = old_dir->root->log_root;
+ struct btrfs_path *path;
+ struct fscrypt_name fname;
+- struct qstr name;
+
+ ASSERT(old_dir_index >= BTRFS_DIR_START_INDEX);
+
+@@ -7480,7 +7479,6 @@ void btrfs_log_new_name(struct btrfs_trans_handle *trans,
+ &old_dentry->d_name, 0, &fname);
+ if (ret)
+ goto out;
+- name = (struct qstr)FSTR_TO_QSTR(&fname.disk_name);
+ /*
+ * We have two inodes to update in the log, the old directory and
+ * the inode that got renamed, so we must pin the log to prevent
+@@ -7516,7 +7514,7 @@ void btrfs_log_new_name(struct btrfs_trans_handle *trans,
+ */
+ mutex_lock(&old_dir->log_mutex);
+ ret = del_logged_dentry(trans, log, path, btrfs_ino(old_dir),
+- &name, old_dir_index);
++ &fname.disk_name, old_dir_index);
+ if (ret > 0) {
+ /*
+ * The dentry does not exist in the log, so record its
+diff --git a/fs/btrfs/tree-log.h b/fs/btrfs/tree-log.h
+index 6c0dc79787f05..8adebf4c9adaf 100644
+--- a/fs/btrfs/tree-log.h
++++ b/fs/btrfs/tree-log.h
+@@ -84,11 +84,11 @@ int btrfs_log_dentry_safe(struct btrfs_trans_handle *trans,
+ struct btrfs_log_ctx *ctx);
+ void btrfs_del_dir_entries_in_log(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+- const struct qstr *name,
++ const struct fscrypt_str *name,
+ struct btrfs_inode *dir, u64 index);
+ void btrfs_del_inode_ref_in_log(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+- const struct qstr *name,
++ const struct fscrypt_str *name,
+ struct btrfs_inode *inode, u64 dirid);
+ void btrfs_end_log_trans(struct btrfs_root *root);
+ void btrfs_pin_log_trans(struct btrfs_root *root);
+--
+2.40.1
+
--- /dev/null
+From 8c61a5386d7b8715600196b9b81b7371a9fd1017 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 20 Oct 2022 12:58:25 -0400
+Subject: btrfs: use struct qstr instead of name and namelen pairs
+
+From: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
+
+[ Upstream commit e43eec81c5167b655b72c781b0e75e62a05e415e ]
+
+Many functions throughout btrfs take name buffer and name length
+arguments. Most of these functions at the highest level are usually
+called with these arguments extracted from a supplied dentry's name.
+But the entire name can be passed instead, making each function a little
+more elegant.
+
+Each function whose arguments are currently the name and length
+extracted from a dentry is herein converted to instead take a pointer to
+the name in the dentry. The couple of calls to these calls without a
+struct dentry are converted to create an appropriate qstr to pass in.
+Additionally, every function which is only called with a name/len
+extracted directly from a qstr is also converted.
+
+This change has positive effect on stack consumption, frame of many
+functions is reduced but this will be used in the future for fscrypt
+related structures.
+
+Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
+Reviewed-by: David Sterba <dsterba@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Stable-dep-of: 9af86694fd5d ("btrfs: file_remove_privs needs an exclusive lock in direct io write")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/btrfs/ctree.h | 26 ++--
+ fs/btrfs/dir-item.c | 50 ++++----
+ fs/btrfs/inode-item.c | 73 ++++++-----
+ fs/btrfs/inode-item.h | 20 ++-
+ fs/btrfs/inode.c | 130 +++++++++-----------
+ fs/btrfs/ioctl.c | 7 +-
+ fs/btrfs/root-tree.c | 19 ++-
+ fs/btrfs/send.c | 12 +-
+ fs/btrfs/super.c | 3 +-
+ fs/btrfs/transaction.c | 11 +-
+ fs/btrfs/tree-log.c | 267 +++++++++++++++++++----------------------
+ fs/btrfs/tree-log.h | 4 +-
+ 12 files changed, 287 insertions(+), 335 deletions(-)
+
+diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
+index 3bcef0c4d6fc4..6718cee57a94e 100644
+--- a/fs/btrfs/ctree.h
++++ b/fs/btrfs/ctree.h
+@@ -3238,11 +3238,11 @@ static inline void btrfs_clear_sb_rdonly(struct super_block *sb)
+
+ /* root-item.c */
+ int btrfs_add_root_ref(struct btrfs_trans_handle *trans, u64 root_id,
+- u64 ref_id, u64 dirid, u64 sequence, const char *name,
+- int name_len);
++ u64 ref_id, u64 dirid, u64 sequence,
++ const struct qstr *name);
+ int btrfs_del_root_ref(struct btrfs_trans_handle *trans, u64 root_id,
+- u64 ref_id, u64 dirid, u64 *sequence, const char *name,
+- int name_len);
++ u64 ref_id, u64 dirid, u64 *sequence,
++ const struct qstr *name);
+ int btrfs_del_root(struct btrfs_trans_handle *trans,
+ const struct btrfs_key *key);
+ int btrfs_insert_root(struct btrfs_trans_handle *trans, struct btrfs_root *root,
+@@ -3271,25 +3271,23 @@ int btrfs_uuid_tree_iterate(struct btrfs_fs_info *fs_info);
+
+ /* dir-item.c */
+ int btrfs_check_dir_item_collision(struct btrfs_root *root, u64 dir,
+- const char *name, int name_len);
+-int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, const char *name,
+- int name_len, struct btrfs_inode *dir,
++ const struct qstr *name);
++int btrfs_insert_dir_item(struct btrfs_trans_handle *trans,
++ const struct qstr *name, struct btrfs_inode *dir,
+ struct btrfs_key *location, u8 type, u64 index);
+ struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct btrfs_path *path, u64 dir,
+- const char *name, int name_len,
+- int mod);
++ const struct qstr *name, int mod);
+ struct btrfs_dir_item *
+ btrfs_lookup_dir_index_item(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct btrfs_path *path, u64 dir,
+- u64 index, const char *name, int name_len,
+- int mod);
++ u64 index, const struct qstr *name, int mod);
+ struct btrfs_dir_item *
+ btrfs_search_dir_index_item(struct btrfs_root *root,
+ struct btrfs_path *path, u64 dirid,
+- const char *name, int name_len);
++ const struct qstr *name);
+ int btrfs_delete_one_dir_name(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct btrfs_path *path,
+@@ -3370,10 +3368,10 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry);
+ int btrfs_set_inode_index(struct btrfs_inode *dir, u64 *index);
+ int btrfs_unlink_inode(struct btrfs_trans_handle *trans,
+ struct btrfs_inode *dir, struct btrfs_inode *inode,
+- const char *name, int name_len);
++ const struct qstr *name);
+ int btrfs_add_link(struct btrfs_trans_handle *trans,
+ struct btrfs_inode *parent_inode, struct btrfs_inode *inode,
+- const char *name, int name_len, int add_backref, u64 index);
++ const struct qstr *name, int add_backref, u64 index);
+ int btrfs_delete_subvolume(struct inode *dir, struct dentry *dentry);
+ int btrfs_truncate_block(struct btrfs_inode *inode, loff_t from, loff_t len,
+ int front);
+diff --git a/fs/btrfs/dir-item.c b/fs/btrfs/dir-item.c
+index 72fb2c518a2b4..8c60f37eb13fd 100644
+--- a/fs/btrfs/dir-item.c
++++ b/fs/btrfs/dir-item.c
+@@ -103,8 +103,8 @@ int btrfs_insert_xattr_item(struct btrfs_trans_handle *trans,
+ * to use for the second index (if one is created).
+ * Will return 0 or -ENOMEM
+ */
+-int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, const char *name,
+- int name_len, struct btrfs_inode *dir,
++int btrfs_insert_dir_item(struct btrfs_trans_handle *trans,
++ const struct qstr *name, struct btrfs_inode *dir,
+ struct btrfs_key *location, u8 type, u64 index)
+ {
+ int ret = 0;
+@@ -120,7 +120,7 @@ int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, const char *name,
+
+ key.objectid = btrfs_ino(dir);
+ key.type = BTRFS_DIR_ITEM_KEY;
+- key.offset = btrfs_name_hash(name, name_len);
++ key.offset = btrfs_name_hash(name->name, name->len);
+
+ path = btrfs_alloc_path();
+ if (!path)
+@@ -128,9 +128,9 @@ int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, const char *name,
+
+ btrfs_cpu_key_to_disk(&disk_key, location);
+
+- data_size = sizeof(*dir_item) + name_len;
++ data_size = sizeof(*dir_item) + name->len;
+ dir_item = insert_with_overflow(trans, root, path, &key, data_size,
+- name, name_len);
++ name->name, name->len);
+ if (IS_ERR(dir_item)) {
+ ret = PTR_ERR(dir_item);
+ if (ret == -EEXIST)
+@@ -142,11 +142,11 @@ int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, const char *name,
+ btrfs_set_dir_item_key(leaf, dir_item, &disk_key);
+ btrfs_set_dir_type(leaf, dir_item, type);
+ btrfs_set_dir_data_len(leaf, dir_item, 0);
+- btrfs_set_dir_name_len(leaf, dir_item, name_len);
++ btrfs_set_dir_name_len(leaf, dir_item, name->len);
+ btrfs_set_dir_transid(leaf, dir_item, trans->transid);
+ name_ptr = (unsigned long)(dir_item + 1);
+
+- write_extent_buffer(leaf, name, name_ptr, name_len);
++ write_extent_buffer(leaf, name->name, name_ptr, name->len);
+ btrfs_mark_buffer_dirty(leaf);
+
+ second_insert:
+@@ -157,7 +157,7 @@ int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, const char *name,
+ }
+ btrfs_release_path(path);
+
+- ret2 = btrfs_insert_delayed_dir_index(trans, name, name_len, dir,
++ ret2 = btrfs_insert_delayed_dir_index(trans, name->name, name->len, dir,
+ &disk_key, type, index);
+ out_free:
+ btrfs_free_path(path);
+@@ -206,7 +206,7 @@ static struct btrfs_dir_item *btrfs_lookup_match_dir(
+ struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct btrfs_path *path, u64 dir,
+- const char *name, int name_len,
++ const struct qstr *name,
+ int mod)
+ {
+ struct btrfs_key key;
+@@ -214,9 +214,10 @@ struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans,
+
+ key.objectid = dir;
+ key.type = BTRFS_DIR_ITEM_KEY;
+- key.offset = btrfs_name_hash(name, name_len);
++ key.offset = btrfs_name_hash(name->name, name->len);
+
+- di = btrfs_lookup_match_dir(trans, root, path, &key, name, name_len, mod);
++ di = btrfs_lookup_match_dir(trans, root, path, &key, name->name,
++ name->len, mod);
+ if (IS_ERR(di) && PTR_ERR(di) == -ENOENT)
+ return NULL;
+
+@@ -224,7 +225,7 @@ struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans,
+ }
+
+ int btrfs_check_dir_item_collision(struct btrfs_root *root, u64 dir,
+- const char *name, int name_len)
++ const struct qstr *name)
+ {
+ int ret;
+ struct btrfs_key key;
+@@ -240,9 +241,10 @@ int btrfs_check_dir_item_collision(struct btrfs_root *root, u64 dir,
+
+ key.objectid = dir;
+ key.type = BTRFS_DIR_ITEM_KEY;
+- key.offset = btrfs_name_hash(name, name_len);
++ key.offset = btrfs_name_hash(name->name, name->len);
+
+- di = btrfs_lookup_match_dir(NULL, root, path, &key, name, name_len, 0);
++ di = btrfs_lookup_match_dir(NULL, root, path, &key, name->name,
++ name->len, 0);
+ if (IS_ERR(di)) {
+ ret = PTR_ERR(di);
+ /* Nothing found, we're safe */
+@@ -262,11 +264,8 @@ int btrfs_check_dir_item_collision(struct btrfs_root *root, u64 dir,
+ goto out;
+ }
+
+- /*
+- * see if there is room in the item to insert this
+- * name
+- */
+- data_size = sizeof(*di) + name_len;
++ /* See if there is room in the item to insert this name. */
++ data_size = sizeof(*di) + name->len;
+ leaf = path->nodes[0];
+ slot = path->slots[0];
+ if (data_size + btrfs_item_size(leaf, slot) +
+@@ -303,8 +302,7 @@ struct btrfs_dir_item *
+ btrfs_lookup_dir_index_item(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct btrfs_path *path, u64 dir,
+- u64 index, const char *name, int name_len,
+- int mod)
++ u64 index, const struct qstr *name, int mod)
+ {
+ struct btrfs_dir_item *di;
+ struct btrfs_key key;
+@@ -313,7 +311,8 @@ btrfs_lookup_dir_index_item(struct btrfs_trans_handle *trans,
+ key.type = BTRFS_DIR_INDEX_KEY;
+ key.offset = index;
+
+- di = btrfs_lookup_match_dir(trans, root, path, &key, name, name_len, mod);
++ di = btrfs_lookup_match_dir(trans, root, path, &key, name->name,
++ name->len, mod);
+ if (di == ERR_PTR(-ENOENT))
+ return NULL;
+
+@@ -321,9 +320,8 @@ btrfs_lookup_dir_index_item(struct btrfs_trans_handle *trans,
+ }
+
+ struct btrfs_dir_item *
+-btrfs_search_dir_index_item(struct btrfs_root *root,
+- struct btrfs_path *path, u64 dirid,
+- const char *name, int name_len)
++btrfs_search_dir_index_item(struct btrfs_root *root, struct btrfs_path *path,
++ u64 dirid, const struct qstr *name)
+ {
+ struct btrfs_dir_item *di;
+ struct btrfs_key key;
+@@ -338,7 +336,7 @@ btrfs_search_dir_index_item(struct btrfs_root *root,
+ break;
+
+ di = btrfs_match_dir_item_name(root->fs_info, path,
+- name, name_len);
++ name->name, name->len);
+ if (di)
+ return di;
+ }
+diff --git a/fs/btrfs/inode-item.c b/fs/btrfs/inode-item.c
+index 0eeb5ea878948..61b323517a40b 100644
+--- a/fs/btrfs/inode-item.c
++++ b/fs/btrfs/inode-item.c
+@@ -10,8 +10,8 @@
+ #include "print-tree.h"
+
+ struct btrfs_inode_ref *btrfs_find_name_in_backref(struct extent_buffer *leaf,
+- int slot, const char *name,
+- int name_len)
++ int slot,
++ const struct qstr *name)
+ {
+ struct btrfs_inode_ref *ref;
+ unsigned long ptr;
+@@ -27,9 +27,10 @@ struct btrfs_inode_ref *btrfs_find_name_in_backref(struct extent_buffer *leaf,
+ len = btrfs_inode_ref_name_len(leaf, ref);
+ name_ptr = (unsigned long)(ref + 1);
+ cur_offset += len + sizeof(*ref);
+- if (len != name_len)
++ if (len != name->len)
+ continue;
+- if (memcmp_extent_buffer(leaf, name, name_ptr, name_len) == 0)
++ if (memcmp_extent_buffer(leaf, name->name, name_ptr,
++ name->len) == 0)
+ return ref;
+ }
+ return NULL;
+@@ -37,7 +38,7 @@ struct btrfs_inode_ref *btrfs_find_name_in_backref(struct extent_buffer *leaf,
+
+ struct btrfs_inode_extref *btrfs_find_name_in_ext_backref(
+ struct extent_buffer *leaf, int slot, u64 ref_objectid,
+- const char *name, int name_len)
++ const struct qstr *name)
+ {
+ struct btrfs_inode_extref *extref;
+ unsigned long ptr;
+@@ -60,9 +61,10 @@ struct btrfs_inode_extref *btrfs_find_name_in_ext_backref(
+ name_ptr = (unsigned long)(&extref->name);
+ ref_name_len = btrfs_inode_extref_name_len(leaf, extref);
+
+- if (ref_name_len == name_len &&
++ if (ref_name_len == name->len &&
+ btrfs_inode_extref_parent(leaf, extref) == ref_objectid &&
+- (memcmp_extent_buffer(leaf, name, name_ptr, name_len) == 0))
++ (memcmp_extent_buffer(leaf, name->name, name_ptr,
++ name->len) == 0))
+ return extref;
+
+ cur_offset += ref_name_len + sizeof(*extref);
+@@ -75,7 +77,7 @@ struct btrfs_inode_extref *
+ btrfs_lookup_inode_extref(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct btrfs_path *path,
+- const char *name, int name_len,
++ const struct qstr *name,
+ u64 inode_objectid, u64 ref_objectid, int ins_len,
+ int cow)
+ {
+@@ -84,7 +86,7 @@ btrfs_lookup_inode_extref(struct btrfs_trans_handle *trans,
+
+ key.objectid = inode_objectid;
+ key.type = BTRFS_INODE_EXTREF_KEY;
+- key.offset = btrfs_extref_hash(ref_objectid, name, name_len);
++ key.offset = btrfs_extref_hash(ref_objectid, name->name, name->len);
+
+ ret = btrfs_search_slot(trans, root, &key, path, ins_len, cow);
+ if (ret < 0)
+@@ -92,13 +94,13 @@ btrfs_lookup_inode_extref(struct btrfs_trans_handle *trans,
+ if (ret > 0)
+ return NULL;
+ return btrfs_find_name_in_ext_backref(path->nodes[0], path->slots[0],
+- ref_objectid, name, name_len);
++ ref_objectid, name);
+
+ }
+
+ static int btrfs_del_inode_extref(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+- const char *name, int name_len,
++ const struct qstr *name,
+ u64 inode_objectid, u64 ref_objectid,
+ u64 *index)
+ {
+@@ -107,14 +109,14 @@ static int btrfs_del_inode_extref(struct btrfs_trans_handle *trans,
+ struct btrfs_inode_extref *extref;
+ struct extent_buffer *leaf;
+ int ret;
+- int del_len = name_len + sizeof(*extref);
++ int del_len = name->len + sizeof(*extref);
+ unsigned long ptr;
+ unsigned long item_start;
+ u32 item_size;
+
+ key.objectid = inode_objectid;
+ key.type = BTRFS_INODE_EXTREF_KEY;
+- key.offset = btrfs_extref_hash(ref_objectid, name, name_len);
++ key.offset = btrfs_extref_hash(ref_objectid, name->name, name->len);
+
+ path = btrfs_alloc_path();
+ if (!path)
+@@ -132,7 +134,7 @@ static int btrfs_del_inode_extref(struct btrfs_trans_handle *trans,
+ * readonly.
+ */
+ extref = btrfs_find_name_in_ext_backref(path->nodes[0], path->slots[0],
+- ref_objectid, name, name_len);
++ ref_objectid, name);
+ if (!extref) {
+ btrfs_handle_fs_error(root->fs_info, -ENOENT, NULL);
+ ret = -EROFS;
+@@ -168,8 +170,7 @@ static int btrfs_del_inode_extref(struct btrfs_trans_handle *trans,
+ }
+
+ int btrfs_del_inode_ref(struct btrfs_trans_handle *trans,
+- struct btrfs_root *root,
+- const char *name, int name_len,
++ struct btrfs_root *root, const struct qstr *name,
+ u64 inode_objectid, u64 ref_objectid, u64 *index)
+ {
+ struct btrfs_path *path;
+@@ -182,7 +183,7 @@ int btrfs_del_inode_ref(struct btrfs_trans_handle *trans,
+ u32 sub_item_len;
+ int ret;
+ int search_ext_refs = 0;
+- int del_len = name_len + sizeof(*ref);
++ int del_len = name->len + sizeof(*ref);
+
+ key.objectid = inode_objectid;
+ key.offset = ref_objectid;
+@@ -201,8 +202,7 @@ int btrfs_del_inode_ref(struct btrfs_trans_handle *trans,
+ goto out;
+ }
+
+- ref = btrfs_find_name_in_backref(path->nodes[0], path->slots[0], name,
+- name_len);
++ ref = btrfs_find_name_in_backref(path->nodes[0], path->slots[0], name);
+ if (!ref) {
+ ret = -ENOENT;
+ search_ext_refs = 1;
+@@ -219,7 +219,7 @@ int btrfs_del_inode_ref(struct btrfs_trans_handle *trans,
+ goto out;
+ }
+ ptr = (unsigned long)ref;
+- sub_item_len = name_len + sizeof(*ref);
++ sub_item_len = name->len + sizeof(*ref);
+ item_start = btrfs_item_ptr_offset(leaf, path->slots[0]);
+ memmove_extent_buffer(leaf, ptr, ptr + sub_item_len,
+ item_size - (ptr + sub_item_len - item_start));
+@@ -233,7 +233,7 @@ int btrfs_del_inode_ref(struct btrfs_trans_handle *trans,
+ * name in our ref array. Find and remove the extended
+ * inode ref then.
+ */
+- return btrfs_del_inode_extref(trans, root, name, name_len,
++ return btrfs_del_inode_extref(trans, root, name,
+ inode_objectid, ref_objectid, index);
+ }
+
+@@ -247,12 +247,13 @@ int btrfs_del_inode_ref(struct btrfs_trans_handle *trans,
+ */
+ static int btrfs_insert_inode_extref(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+- const char *name, int name_len,
+- u64 inode_objectid, u64 ref_objectid, u64 index)
++ const struct qstr *name,
++ u64 inode_objectid, u64 ref_objectid,
++ u64 index)
+ {
+ struct btrfs_inode_extref *extref;
+ int ret;
+- int ins_len = name_len + sizeof(*extref);
++ int ins_len = name->len + sizeof(*extref);
+ unsigned long ptr;
+ struct btrfs_path *path;
+ struct btrfs_key key;
+@@ -260,7 +261,7 @@ static int btrfs_insert_inode_extref(struct btrfs_trans_handle *trans,
+
+ key.objectid = inode_objectid;
+ key.type = BTRFS_INODE_EXTREF_KEY;
+- key.offset = btrfs_extref_hash(ref_objectid, name, name_len);
++ key.offset = btrfs_extref_hash(ref_objectid, name->name, name->len);
+
+ path = btrfs_alloc_path();
+ if (!path)
+@@ -272,7 +273,7 @@ static int btrfs_insert_inode_extref(struct btrfs_trans_handle *trans,
+ if (btrfs_find_name_in_ext_backref(path->nodes[0],
+ path->slots[0],
+ ref_objectid,
+- name, name_len))
++ name))
+ goto out;
+
+ btrfs_extend_item(path, ins_len);
+@@ -286,12 +287,12 @@ static int btrfs_insert_inode_extref(struct btrfs_trans_handle *trans,
+ ptr += btrfs_item_size(leaf, path->slots[0]) - ins_len;
+ extref = (struct btrfs_inode_extref *)ptr;
+
+- btrfs_set_inode_extref_name_len(path->nodes[0], extref, name_len);
++ btrfs_set_inode_extref_name_len(path->nodes[0], extref, name->len);
+ btrfs_set_inode_extref_index(path->nodes[0], extref, index);
+ btrfs_set_inode_extref_parent(path->nodes[0], extref, ref_objectid);
+
+ ptr = (unsigned long)&extref->name;
+- write_extent_buffer(path->nodes[0], name, ptr, name_len);
++ write_extent_buffer(path->nodes[0], name->name, ptr, name->len);
+ btrfs_mark_buffer_dirty(path->nodes[0]);
+
+ out:
+@@ -301,8 +302,7 @@ static int btrfs_insert_inode_extref(struct btrfs_trans_handle *trans,
+
+ /* Will return 0, -ENOMEM, -EMLINK, or -EEXIST or anything from the CoW path */
+ int btrfs_insert_inode_ref(struct btrfs_trans_handle *trans,
+- struct btrfs_root *root,
+- const char *name, int name_len,
++ struct btrfs_root *root, const struct qstr *name,
+ u64 inode_objectid, u64 ref_objectid, u64 index)
+ {
+ struct btrfs_fs_info *fs_info = root->fs_info;
+@@ -311,7 +311,7 @@ int btrfs_insert_inode_ref(struct btrfs_trans_handle *trans,
+ struct btrfs_inode_ref *ref;
+ unsigned long ptr;
+ int ret;
+- int ins_len = name_len + sizeof(*ref);
++ int ins_len = name->len + sizeof(*ref);
+
+ key.objectid = inode_objectid;
+ key.offset = ref_objectid;
+@@ -327,7 +327,7 @@ int btrfs_insert_inode_ref(struct btrfs_trans_handle *trans,
+ if (ret == -EEXIST) {
+ u32 old_size;
+ ref = btrfs_find_name_in_backref(path->nodes[0], path->slots[0],
+- name, name_len);
++ name);
+ if (ref)
+ goto out;
+
+@@ -336,7 +336,7 @@ int btrfs_insert_inode_ref(struct btrfs_trans_handle *trans,
+ ref = btrfs_item_ptr(path->nodes[0], path->slots[0],
+ struct btrfs_inode_ref);
+ ref = (struct btrfs_inode_ref *)((unsigned long)ref + old_size);
+- btrfs_set_inode_ref_name_len(path->nodes[0], ref, name_len);
++ btrfs_set_inode_ref_name_len(path->nodes[0], ref, name->len);
+ btrfs_set_inode_ref_index(path->nodes[0], ref, index);
+ ptr = (unsigned long)(ref + 1);
+ ret = 0;
+@@ -344,7 +344,7 @@ int btrfs_insert_inode_ref(struct btrfs_trans_handle *trans,
+ if (ret == -EOVERFLOW) {
+ if (btrfs_find_name_in_backref(path->nodes[0],
+ path->slots[0],
+- name, name_len))
++ name))
+ ret = -EEXIST;
+ else
+ ret = -EMLINK;
+@@ -353,11 +353,11 @@ int btrfs_insert_inode_ref(struct btrfs_trans_handle *trans,
+ } else {
+ ref = btrfs_item_ptr(path->nodes[0], path->slots[0],
+ struct btrfs_inode_ref);
+- btrfs_set_inode_ref_name_len(path->nodes[0], ref, name_len);
++ btrfs_set_inode_ref_name_len(path->nodes[0], ref, name->len);
+ btrfs_set_inode_ref_index(path->nodes[0], ref, index);
+ ptr = (unsigned long)(ref + 1);
+ }
+- write_extent_buffer(path->nodes[0], name, ptr, name_len);
++ write_extent_buffer(path->nodes[0], name->name, ptr, name->len);
+ btrfs_mark_buffer_dirty(path->nodes[0]);
+
+ out:
+@@ -370,7 +370,6 @@ int btrfs_insert_inode_ref(struct btrfs_trans_handle *trans,
+ if (btrfs_super_incompat_flags(disk_super)
+ & BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF)
+ ret = btrfs_insert_inode_extref(trans, root, name,
+- name_len,
+ inode_objectid,
+ ref_objectid, index);
+ }
+diff --git a/fs/btrfs/inode-item.h b/fs/btrfs/inode-item.h
+index a8fc16d0147f6..3c657c670cfdf 100644
+--- a/fs/btrfs/inode-item.h
++++ b/fs/btrfs/inode-item.h
+@@ -64,33 +64,31 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct btrfs_truncate_control *control);
+ int btrfs_insert_inode_ref(struct btrfs_trans_handle *trans,
+- struct btrfs_root *root,
+- const char *name, int name_len,
++ struct btrfs_root *root, const struct qstr *name,
+ u64 inode_objectid, u64 ref_objectid, u64 index);
+ int btrfs_del_inode_ref(struct btrfs_trans_handle *trans,
+- struct btrfs_root *root,
+- const char *name, int name_len,
+- u64 inode_objectid, u64 ref_objectid, u64 *index);
++ struct btrfs_root *root, const struct qstr *name,
++ u64 inode_objectid, u64 ref_objectid, u64 *index);
+ int btrfs_insert_empty_inode(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct btrfs_path *path, u64 objectid);
+-int btrfs_lookup_inode(struct btrfs_trans_handle *trans, struct btrfs_root
+- *root, struct btrfs_path *path,
++int btrfs_lookup_inode(struct btrfs_trans_handle *trans,
++ struct btrfs_root *root, struct btrfs_path *path,
+ struct btrfs_key *location, int mod);
+
+ struct btrfs_inode_extref *btrfs_lookup_inode_extref(
+ struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct btrfs_path *path,
+- const char *name, int name_len,
++ const struct qstr *name,
+ u64 inode_objectid, u64 ref_objectid, int ins_len,
+ int cow);
+
+ struct btrfs_inode_ref *btrfs_find_name_in_backref(struct extent_buffer *leaf,
+- int slot, const char *name,
+- int name_len);
++ int slot,
++ const struct qstr *name);
+ struct btrfs_inode_extref *btrfs_find_name_in_ext_backref(
+ struct extent_buffer *leaf, int slot, u64 ref_objectid,
+- const char *name, int name_len);
++ const struct qstr *name);
+
+ #endif
+diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
+index 222068bf80031..a5e61ad2ba696 100644
+--- a/fs/btrfs/inode.c
++++ b/fs/btrfs/inode.c
+@@ -3627,7 +3627,7 @@ void btrfs_run_delayed_iputs(struct btrfs_fs_info *fs_info)
+ spin_unlock(&fs_info->delayed_iput_lock);
+ }
+
+-/**
++/*
+ * Wait for flushing all delayed iputs
+ *
+ * @fs_info: the filesystem
+@@ -4272,7 +4272,7 @@ int btrfs_update_inode_fallback(struct btrfs_trans_handle *trans,
+ static int __btrfs_unlink_inode(struct btrfs_trans_handle *trans,
+ struct btrfs_inode *dir,
+ struct btrfs_inode *inode,
+- const char *name, int name_len,
++ const struct qstr *name,
+ struct btrfs_rename_ctx *rename_ctx)
+ {
+ struct btrfs_root *root = dir->root;
+@@ -4290,8 +4290,7 @@ static int __btrfs_unlink_inode(struct btrfs_trans_handle *trans,
+ goto out;
+ }
+
+- di = btrfs_lookup_dir_item(trans, root, path, dir_ino,
+- name, name_len, -1);
++ di = btrfs_lookup_dir_item(trans, root, path, dir_ino, name, -1);
+ if (IS_ERR_OR_NULL(di)) {
+ ret = di ? PTR_ERR(di) : -ENOENT;
+ goto err;
+@@ -4319,12 +4318,11 @@ static int __btrfs_unlink_inode(struct btrfs_trans_handle *trans,
+ }
+ }
+
+- ret = btrfs_del_inode_ref(trans, root, name, name_len, ino,
+- dir_ino, &index);
++ ret = btrfs_del_inode_ref(trans, root, name, ino, dir_ino, &index);
+ if (ret) {
+ btrfs_info(fs_info,
+ "failed to delete reference to %.*s, inode %llu parent %llu",
+- name_len, name, ino, dir_ino);
++ name->len, name->name, ino, dir_ino);
+ btrfs_abort_transaction(trans, ret);
+ goto err;
+ }
+@@ -4345,10 +4343,8 @@ static int __btrfs_unlink_inode(struct btrfs_trans_handle *trans,
+ * operations on the log tree, increasing latency for applications.
+ */
+ if (!rename_ctx) {
+- btrfs_del_inode_ref_in_log(trans, root, name, name_len, inode,
+- dir_ino);
+- btrfs_del_dir_entries_in_log(trans, root, name, name_len, dir,
+- index);
++ btrfs_del_inode_ref_in_log(trans, root, name, inode, dir_ino);
++ btrfs_del_dir_entries_in_log(trans, root, name, dir, index);
+ }
+
+ /*
+@@ -4366,7 +4362,7 @@ static int __btrfs_unlink_inode(struct btrfs_trans_handle *trans,
+ if (ret)
+ goto out;
+
+- btrfs_i_size_write(dir, dir->vfs_inode.i_size - name_len * 2);
++ btrfs_i_size_write(dir, dir->vfs_inode.i_size - name->len * 2);
+ inode_inc_iversion(&inode->vfs_inode);
+ inode_inc_iversion(&dir->vfs_inode);
+ inode->vfs_inode.i_ctime = current_time(&inode->vfs_inode);
+@@ -4379,10 +4375,11 @@ static int __btrfs_unlink_inode(struct btrfs_trans_handle *trans,
+
+ int btrfs_unlink_inode(struct btrfs_trans_handle *trans,
+ struct btrfs_inode *dir, struct btrfs_inode *inode,
+- const char *name, int name_len)
++ const struct qstr *name)
+ {
+ int ret;
+- ret = __btrfs_unlink_inode(trans, dir, inode, name, name_len, NULL);
++
++ ret = __btrfs_unlink_inode(trans, dir, inode, name, NULL);
+ if (!ret) {
+ drop_nlink(&inode->vfs_inode);
+ ret = btrfs_update_inode(trans, inode->root, inode);
+@@ -4426,9 +4423,8 @@ static int btrfs_unlink(struct inode *dir, struct dentry *dentry)
+ btrfs_record_unlink_dir(trans, BTRFS_I(dir), BTRFS_I(d_inode(dentry)),
+ 0);
+
+- ret = btrfs_unlink_inode(trans, BTRFS_I(dir),
+- BTRFS_I(d_inode(dentry)), dentry->d_name.name,
+- dentry->d_name.len);
++ ret = btrfs_unlink_inode(trans, BTRFS_I(dir), BTRFS_I(d_inode(dentry)),
++ &dentry->d_name);
+ if (ret)
+ goto out;
+
+@@ -4453,8 +4449,7 @@ static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans,
+ struct extent_buffer *leaf;
+ struct btrfs_dir_item *di;
+ struct btrfs_key key;
+- const char *name = dentry->d_name.name;
+- int name_len = dentry->d_name.len;
++ const struct qstr *name = &dentry->d_name;
+ u64 index;
+ int ret;
+ u64 objectid;
+@@ -4473,8 +4468,7 @@ static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans,
+ if (!path)
+ return -ENOMEM;
+
+- di = btrfs_lookup_dir_item(trans, root, path, dir_ino,
+- name, name_len, -1);
++ di = btrfs_lookup_dir_item(trans, root, path, dir_ino, name, -1);
+ if (IS_ERR_OR_NULL(di)) {
+ ret = di ? PTR_ERR(di) : -ENOENT;
+ goto out;
+@@ -4500,8 +4494,7 @@ static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans,
+ * call btrfs_del_root_ref, and it _shouldn't_ fail.
+ */
+ if (btrfs_ino(inode) == BTRFS_EMPTY_SUBVOL_DIR_OBJECTID) {
+- di = btrfs_search_dir_index_item(root, path, dir_ino,
+- name, name_len);
++ di = btrfs_search_dir_index_item(root, path, dir_ino, name);
+ if (IS_ERR_OR_NULL(di)) {
+ if (!di)
+ ret = -ENOENT;
+@@ -4518,7 +4511,7 @@ static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans,
+ } else {
+ ret = btrfs_del_root_ref(trans, objectid,
+ root->root_key.objectid, dir_ino,
+- &index, name, name_len);
++ &index, name);
+ if (ret) {
+ btrfs_abort_transaction(trans, ret);
+ goto out;
+@@ -4531,7 +4524,7 @@ static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans,
+ goto out;
+ }
+
+- btrfs_i_size_write(BTRFS_I(dir), dir->i_size - name_len * 2);
++ btrfs_i_size_write(BTRFS_I(dir), dir->i_size - name->len * 2);
+ inode_inc_iversion(dir);
+ dir->i_mtime = current_time(dir);
+ dir->i_ctime = dir->i_mtime;
+@@ -4553,6 +4546,7 @@ static noinline int may_destroy_subvol(struct btrfs_root *root)
+ struct btrfs_path *path;
+ struct btrfs_dir_item *di;
+ struct btrfs_key key;
++ struct qstr name = QSTR_INIT("default", 7);
+ u64 dir_id;
+ int ret;
+
+@@ -4563,7 +4557,7 @@ static noinline int may_destroy_subvol(struct btrfs_root *root)
+ /* Make sure this root isn't set as the default subvol */
+ dir_id = btrfs_super_root_dir(fs_info->super_copy);
+ di = btrfs_lookup_dir_item(NULL, fs_info->tree_root, path,
+- dir_id, "default", 7, 0);
++ dir_id, &name, 0);
+ if (di && !IS_ERR(di)) {
+ btrfs_dir_item_key_to_cpu(path->nodes[0], di, &key);
+ if (key.objectid == root->root_key.objectid) {
+@@ -4830,9 +4824,8 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry)
+ last_unlink_trans = BTRFS_I(inode)->last_unlink_trans;
+
+ /* now the directory is empty */
+- err = btrfs_unlink_inode(trans, BTRFS_I(dir),
+- BTRFS_I(d_inode(dentry)), dentry->d_name.name,
+- dentry->d_name.len);
++ err = btrfs_unlink_inode(trans, BTRFS_I(dir), BTRFS_I(d_inode(dentry)),
++ &dentry->d_name);
+ if (!err) {
+ btrfs_i_size_write(BTRFS_I(inode), 0);
+ /*
+@@ -5532,8 +5525,7 @@ void btrfs_evict_inode(struct inode *inode)
+ static int btrfs_inode_by_name(struct inode *dir, struct dentry *dentry,
+ struct btrfs_key *location, u8 *type)
+ {
+- const char *name = dentry->d_name.name;
+- int namelen = dentry->d_name.len;
++ const struct qstr *name = &dentry->d_name;
+ struct btrfs_dir_item *di;
+ struct btrfs_path *path;
+ struct btrfs_root *root = BTRFS_I(dir)->root;
+@@ -5544,7 +5536,7 @@ static int btrfs_inode_by_name(struct inode *dir, struct dentry *dentry,
+ return -ENOMEM;
+
+ di = btrfs_lookup_dir_item(NULL, root, path, btrfs_ino(BTRFS_I(dir)),
+- name, namelen, 0);
++ name, 0);
+ if (IS_ERR_OR_NULL(di)) {
+ ret = di ? PTR_ERR(di) : -ENOENT;
+ goto out;
+@@ -5556,7 +5548,7 @@ static int btrfs_inode_by_name(struct inode *dir, struct dentry *dentry,
+ ret = -EUCLEAN;
+ btrfs_warn(root->fs_info,
+ "%s gets something invalid in DIR_ITEM (name %s, directory ino %llu, location(%llu %u %llu))",
+- __func__, name, btrfs_ino(BTRFS_I(dir)),
++ __func__, name->name, btrfs_ino(BTRFS_I(dir)),
+ location->objectid, location->type, location->offset);
+ }
+ if (!ret)
+@@ -6315,8 +6307,7 @@ int btrfs_create_new_inode(struct btrfs_trans_handle *trans,
+ {
+ struct inode *dir = args->dir;
+ struct inode *inode = args->inode;
+- const char *name = args->orphan ? NULL : args->dentry->d_name.name;
+- int name_len = args->orphan ? 0 : args->dentry->d_name.len;
++ const struct qstr *name = args->orphan ? NULL : &args->dentry->d_name;
+ struct btrfs_fs_info *fs_info = btrfs_sb(dir->i_sb);
+ struct btrfs_root *root;
+ struct btrfs_inode_item *inode_item;
+@@ -6417,7 +6408,7 @@ int btrfs_create_new_inode(struct btrfs_trans_handle *trans,
+ sizes[1] = 2 + sizeof(*ref);
+ } else {
+ key[1].offset = btrfs_ino(BTRFS_I(dir));
+- sizes[1] = name_len + sizeof(*ref);
++ sizes[1] = name->len + sizeof(*ref);
+ }
+ }
+
+@@ -6456,10 +6447,12 @@ int btrfs_create_new_inode(struct btrfs_trans_handle *trans,
+ btrfs_set_inode_ref_index(path->nodes[0], ref, 0);
+ write_extent_buffer(path->nodes[0], "..", ptr, 2);
+ } else {
+- btrfs_set_inode_ref_name_len(path->nodes[0], ref, name_len);
++ btrfs_set_inode_ref_name_len(path->nodes[0], ref,
++ name->len);
+ btrfs_set_inode_ref_index(path->nodes[0], ref,
+ BTRFS_I(inode)->dir_index);
+- write_extent_buffer(path->nodes[0], name, ptr, name_len);
++ write_extent_buffer(path->nodes[0], name->name, ptr,
++ name->len);
+ }
+ }
+
+@@ -6520,7 +6513,7 @@ int btrfs_create_new_inode(struct btrfs_trans_handle *trans,
+ ret = btrfs_orphan_add(trans, BTRFS_I(inode));
+ } else {
+ ret = btrfs_add_link(trans, BTRFS_I(dir), BTRFS_I(inode), name,
+- name_len, 0, BTRFS_I(inode)->dir_index);
++ 0, BTRFS_I(inode)->dir_index);
+ }
+ if (ret) {
+ btrfs_abort_transaction(trans, ret);
+@@ -6549,7 +6542,7 @@ int btrfs_create_new_inode(struct btrfs_trans_handle *trans,
+ */
+ int btrfs_add_link(struct btrfs_trans_handle *trans,
+ struct btrfs_inode *parent_inode, struct btrfs_inode *inode,
+- const char *name, int name_len, int add_backref, u64 index)
++ const struct qstr *name, int add_backref, u64 index)
+ {
+ int ret = 0;
+ struct btrfs_key key;
+@@ -6568,17 +6561,17 @@ int btrfs_add_link(struct btrfs_trans_handle *trans,
+ if (unlikely(ino == BTRFS_FIRST_FREE_OBJECTID)) {
+ ret = btrfs_add_root_ref(trans, key.objectid,
+ root->root_key.objectid, parent_ino,
+- index, name, name_len);
++ index, name);
+ } else if (add_backref) {
+- ret = btrfs_insert_inode_ref(trans, root, name, name_len, ino,
+- parent_ino, index);
++ ret = btrfs_insert_inode_ref(trans, root, name,
++ ino, parent_ino, index);
+ }
+
+ /* Nothing to clean up yet */
+ if (ret)
+ return ret;
+
+- ret = btrfs_insert_dir_item(trans, name, name_len, parent_inode, &key,
++ ret = btrfs_insert_dir_item(trans, name, parent_inode, &key,
+ btrfs_inode_type(&inode->vfs_inode), index);
+ if (ret == -EEXIST || ret == -EOVERFLOW)
+ goto fail_dir_item;
+@@ -6588,7 +6581,7 @@ int btrfs_add_link(struct btrfs_trans_handle *trans,
+ }
+
+ btrfs_i_size_write(parent_inode, parent_inode->vfs_inode.i_size +
+- name_len * 2);
++ name->len * 2);
+ inode_inc_iversion(&parent_inode->vfs_inode);
+ /*
+ * If we are replaying a log tree, we do not want to update the mtime
+@@ -6613,15 +6606,15 @@ int btrfs_add_link(struct btrfs_trans_handle *trans,
+ int err;
+ err = btrfs_del_root_ref(trans, key.objectid,
+ root->root_key.objectid, parent_ino,
+- &local_index, name, name_len);
++ &local_index, name);
+ if (err)
+ btrfs_abort_transaction(trans, err);
+ } else if (add_backref) {
+ u64 local_index;
+ int err;
+
+- err = btrfs_del_inode_ref(trans, root, name, name_len,
+- ino, parent_ino, &local_index);
++ err = btrfs_del_inode_ref(trans, root, name, ino, parent_ino,
++ &local_index);
+ if (err)
+ btrfs_abort_transaction(trans, err);
+ }
+@@ -6741,7 +6734,7 @@ static int btrfs_link(struct dentry *old_dentry, struct inode *dir,
+ set_bit(BTRFS_INODE_COPY_EVERYTHING, &BTRFS_I(inode)->runtime_flags);
+
+ err = btrfs_add_link(trans, BTRFS_I(dir), BTRFS_I(inode),
+- dentry->d_name.name, dentry->d_name.len, 1, index);
++ &dentry->d_name, 1, index);
+
+ if (err) {
+ drop_inode = 1;
+@@ -9115,9 +9108,7 @@ static int btrfs_rename_exchange(struct inode *old_dir,
+ /* force full log commit if subvolume involved. */
+ btrfs_set_log_full_commit(trans);
+ } else {
+- ret = btrfs_insert_inode_ref(trans, dest,
+- new_dentry->d_name.name,
+- new_dentry->d_name.len,
++ ret = btrfs_insert_inode_ref(trans, dest, &new_dentry->d_name,
+ old_ino,
+ btrfs_ino(BTRFS_I(new_dir)),
+ old_idx);
+@@ -9131,9 +9122,7 @@ static int btrfs_rename_exchange(struct inode *old_dir,
+ /* force full log commit if subvolume involved. */
+ btrfs_set_log_full_commit(trans);
+ } else {
+- ret = btrfs_insert_inode_ref(trans, root,
+- old_dentry->d_name.name,
+- old_dentry->d_name.len,
++ ret = btrfs_insert_inode_ref(trans, root, &old_dentry->d_name,
+ new_ino,
+ btrfs_ino(BTRFS_I(old_dir)),
+ new_idx);
+@@ -9169,8 +9158,7 @@ static int btrfs_rename_exchange(struct inode *old_dir,
+ } else { /* src is an inode */
+ ret = __btrfs_unlink_inode(trans, BTRFS_I(old_dir),
+ BTRFS_I(old_dentry->d_inode),
+- old_dentry->d_name.name,
+- old_dentry->d_name.len,
++ &old_dentry->d_name,
+ &old_rename_ctx);
+ if (!ret)
+ ret = btrfs_update_inode(trans, root, BTRFS_I(old_inode));
+@@ -9186,8 +9174,7 @@ static int btrfs_rename_exchange(struct inode *old_dir,
+ } else { /* dest is an inode */
+ ret = __btrfs_unlink_inode(trans, BTRFS_I(new_dir),
+ BTRFS_I(new_dentry->d_inode),
+- new_dentry->d_name.name,
+- new_dentry->d_name.len,
++ &new_dentry->d_name,
+ &new_rename_ctx);
+ if (!ret)
+ ret = btrfs_update_inode(trans, dest, BTRFS_I(new_inode));
+@@ -9198,16 +9185,14 @@ static int btrfs_rename_exchange(struct inode *old_dir,
+ }
+
+ ret = btrfs_add_link(trans, BTRFS_I(new_dir), BTRFS_I(old_inode),
+- new_dentry->d_name.name,
+- new_dentry->d_name.len, 0, old_idx);
++ &new_dentry->d_name, 0, old_idx);
+ if (ret) {
+ btrfs_abort_transaction(trans, ret);
+ goto out_fail;
+ }
+
+ ret = btrfs_add_link(trans, BTRFS_I(old_dir), BTRFS_I(new_inode),
+- old_dentry->d_name.name,
+- old_dentry->d_name.len, 0, new_idx);
++ &old_dentry->d_name, 0, new_idx);
+ if (ret) {
+ btrfs_abort_transaction(trans, ret);
+ goto out_fail;
+@@ -9308,8 +9293,7 @@ static int btrfs_rename(struct user_namespace *mnt_userns,
+
+ /* check for collisions, even if the name isn't there */
+ ret = btrfs_check_dir_item_collision(dest, new_dir->i_ino,
+- new_dentry->d_name.name,
+- new_dentry->d_name.len);
++ &new_dentry->d_name);
+
+ if (ret) {
+ if (ret == -EEXIST) {
+@@ -9403,9 +9387,7 @@ static int btrfs_rename(struct user_namespace *mnt_userns,
+ /* force full log commit if subvolume involved. */
+ btrfs_set_log_full_commit(trans);
+ } else {
+- ret = btrfs_insert_inode_ref(trans, dest,
+- new_dentry->d_name.name,
+- new_dentry->d_name.len,
++ ret = btrfs_insert_inode_ref(trans, dest, &new_dentry->d_name,
+ old_ino,
+ btrfs_ino(BTRFS_I(new_dir)), index);
+ if (ret)
+@@ -9429,10 +9411,8 @@ static int btrfs_rename(struct user_namespace *mnt_userns,
+ ret = btrfs_unlink_subvol(trans, old_dir, old_dentry);
+ } else {
+ ret = __btrfs_unlink_inode(trans, BTRFS_I(old_dir),
+- BTRFS_I(d_inode(old_dentry)),
+- old_dentry->d_name.name,
+- old_dentry->d_name.len,
+- &rename_ctx);
++ BTRFS_I(d_inode(old_dentry)),
++ &old_dentry->d_name, &rename_ctx);
+ if (!ret)
+ ret = btrfs_update_inode(trans, root, BTRFS_I(old_inode));
+ }
+@@ -9451,8 +9431,7 @@ static int btrfs_rename(struct user_namespace *mnt_userns,
+ } else {
+ ret = btrfs_unlink_inode(trans, BTRFS_I(new_dir),
+ BTRFS_I(d_inode(new_dentry)),
+- new_dentry->d_name.name,
+- new_dentry->d_name.len);
++ &new_dentry->d_name);
+ }
+ if (!ret && new_inode->i_nlink == 0)
+ ret = btrfs_orphan_add(trans,
+@@ -9464,8 +9443,7 @@ static int btrfs_rename(struct user_namespace *mnt_userns,
+ }
+
+ ret = btrfs_add_link(trans, BTRFS_I(new_dir), BTRFS_I(old_inode),
+- new_dentry->d_name.name,
+- new_dentry->d_name.len, 0, index);
++ &new_dentry->d_name, 0, index);
+ if (ret) {
+ btrfs_abort_transaction(trans, ret);
+ goto out_fail;
+diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
+index 2e29fafe0e7d9..5305d98905cea 100644
+--- a/fs/btrfs/ioctl.c
++++ b/fs/btrfs/ioctl.c
+@@ -951,6 +951,7 @@ static noinline int btrfs_mksubvol(const struct path *parent,
+ struct inode *dir = d_inode(parent->dentry);
+ struct btrfs_fs_info *fs_info = btrfs_sb(dir->i_sb);
+ struct dentry *dentry;
++ struct qstr name_str = QSTR_INIT(name, namelen);
+ int error;
+
+ error = down_write_killable_nested(&dir->i_rwsem, I_MUTEX_PARENT);
+@@ -971,8 +972,7 @@ static noinline int btrfs_mksubvol(const struct path *parent,
+ * check for them now when we can safely fail
+ */
+ error = btrfs_check_dir_item_collision(BTRFS_I(dir)->root,
+- dir->i_ino, name,
+- namelen);
++ dir->i_ino, &name_str);
+ if (error)
+ goto out_dput;
+
+@@ -3782,6 +3782,7 @@ static long btrfs_ioctl_default_subvol(struct file *file, void __user *argp)
+ struct btrfs_trans_handle *trans;
+ struct btrfs_path *path = NULL;
+ struct btrfs_disk_key disk_key;
++ struct qstr name = QSTR_INIT("default", 7);
+ u64 objectid = 0;
+ u64 dir_id;
+ int ret;
+@@ -3825,7 +3826,7 @@ static long btrfs_ioctl_default_subvol(struct file *file, void __user *argp)
+
+ dir_id = btrfs_super_root_dir(fs_info->super_copy);
+ di = btrfs_lookup_dir_item(trans, fs_info->tree_root, path,
+- dir_id, "default", 7, 1);
++ dir_id, &name, 1);
+ if (IS_ERR_OR_NULL(di)) {
+ btrfs_release_path(path);
+ btrfs_end_transaction(trans);
+diff --git a/fs/btrfs/root-tree.c b/fs/btrfs/root-tree.c
+index e1f599d7a9164..cf29241b9b310 100644
+--- a/fs/btrfs/root-tree.c
++++ b/fs/btrfs/root-tree.c
+@@ -327,9 +327,8 @@ int btrfs_del_root(struct btrfs_trans_handle *trans,
+ }
+
+ int btrfs_del_root_ref(struct btrfs_trans_handle *trans, u64 root_id,
+- u64 ref_id, u64 dirid, u64 *sequence, const char *name,
+- int name_len)
+-
++ u64 ref_id, u64 dirid, u64 *sequence,
++ const struct qstr *name)
+ {
+ struct btrfs_root *tree_root = trans->fs_info->tree_root;
+ struct btrfs_path *path;
+@@ -356,8 +355,8 @@ int btrfs_del_root_ref(struct btrfs_trans_handle *trans, u64 root_id,
+ struct btrfs_root_ref);
+ ptr = (unsigned long)(ref + 1);
+ if ((btrfs_root_ref_dirid(leaf, ref) != dirid) ||
+- (btrfs_root_ref_name_len(leaf, ref) != name_len) ||
+- memcmp_extent_buffer(leaf, name, ptr, name_len)) {
++ (btrfs_root_ref_name_len(leaf, ref) != name->len) ||
++ memcmp_extent_buffer(leaf, name->name, ptr, name->len)) {
+ ret = -ENOENT;
+ goto out;
+ }
+@@ -400,8 +399,8 @@ int btrfs_del_root_ref(struct btrfs_trans_handle *trans, u64 root_id,
+ * Will return 0, -ENOMEM, or anything from the CoW path
+ */
+ int btrfs_add_root_ref(struct btrfs_trans_handle *trans, u64 root_id,
+- u64 ref_id, u64 dirid, u64 sequence, const char *name,
+- int name_len)
++ u64 ref_id, u64 dirid, u64 sequence,
++ const struct qstr *name)
+ {
+ struct btrfs_root *tree_root = trans->fs_info->tree_root;
+ struct btrfs_key key;
+@@ -420,7 +419,7 @@ int btrfs_add_root_ref(struct btrfs_trans_handle *trans, u64 root_id,
+ key.offset = ref_id;
+ again:
+ ret = btrfs_insert_empty_item(trans, tree_root, path, &key,
+- sizeof(*ref) + name_len);
++ sizeof(*ref) + name->len);
+ if (ret) {
+ btrfs_abort_transaction(trans, ret);
+ btrfs_free_path(path);
+@@ -431,9 +430,9 @@ int btrfs_add_root_ref(struct btrfs_trans_handle *trans, u64 root_id,
+ ref = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_root_ref);
+ btrfs_set_root_ref_dirid(leaf, ref, dirid);
+ btrfs_set_root_ref_sequence(leaf, ref, sequence);
+- btrfs_set_root_ref_name_len(leaf, ref, name_len);
++ btrfs_set_root_ref_name_len(leaf, ref, name->len);
+ ptr = (unsigned long)(ref + 1);
+- write_extent_buffer(leaf, name, ptr, name_len);
++ write_extent_buffer(leaf, name->name, ptr, name->len);
+ btrfs_mark_buffer_dirty(leaf);
+
+ if (key.type == BTRFS_ROOT_BACKREF_KEY) {
+diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c
+index 35e889fe2a95d..833364527554c 100644
+--- a/fs/btrfs/send.c
++++ b/fs/btrfs/send.c
+@@ -1596,13 +1596,17 @@ static int gen_unique_name(struct send_ctx *sctx,
+ return -ENOMEM;
+
+ while (1) {
++ struct qstr tmp_name;
++
+ len = snprintf(tmp, sizeof(tmp), "o%llu-%llu-%llu",
+ ino, gen, idx);
+ ASSERT(len < sizeof(tmp));
++ tmp_name.name = tmp;
++ tmp_name.len = strlen(tmp);
+
+ di = btrfs_lookup_dir_item(NULL, sctx->send_root,
+ path, BTRFS_FIRST_FREE_OBJECTID,
+- tmp, strlen(tmp), 0);
++ &tmp_name, 0);
+ btrfs_release_path(path);
+ if (IS_ERR(di)) {
+ ret = PTR_ERR(di);
+@@ -1622,7 +1626,7 @@ static int gen_unique_name(struct send_ctx *sctx,
+
+ di = btrfs_lookup_dir_item(NULL, sctx->parent_root,
+ path, BTRFS_FIRST_FREE_OBJECTID,
+- tmp, strlen(tmp), 0);
++ &tmp_name, 0);
+ btrfs_release_path(path);
+ if (IS_ERR(di)) {
+ ret = PTR_ERR(di);
+@@ -1752,13 +1756,13 @@ static int lookup_dir_item_inode(struct btrfs_root *root,
+ struct btrfs_dir_item *di;
+ struct btrfs_key key;
+ struct btrfs_path *path;
++ struct qstr name_str = QSTR_INIT(name, name_len);
+
+ path = alloc_path_for_send();
+ if (!path)
+ return -ENOMEM;
+
+- di = btrfs_lookup_dir_item(NULL, root, path,
+- dir, name, name_len, 0);
++ di = btrfs_lookup_dir_item(NULL, root, path, dir, &name_str, 0);
+ if (IS_ERR_OR_NULL(di)) {
+ ret = di ? PTR_ERR(di) : -ENOENT;
+ goto out;
+diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
+index 582b71b7fa779..bf56e4d6b9f48 100644
+--- a/fs/btrfs/super.c
++++ b/fs/btrfs/super.c
+@@ -1398,6 +1398,7 @@ static int get_default_subvol_objectid(struct btrfs_fs_info *fs_info, u64 *objec
+ struct btrfs_dir_item *di;
+ struct btrfs_path *path;
+ struct btrfs_key location;
++ struct qstr name = QSTR_INIT("default", 7);
+ u64 dir_id;
+
+ path = btrfs_alloc_path();
+@@ -1410,7 +1411,7 @@ static int get_default_subvol_objectid(struct btrfs_fs_info *fs_info, u64 *objec
+ * to mount.
+ */
+ dir_id = btrfs_super_root_dir(fs_info->super_copy);
+- di = btrfs_lookup_dir_item(NULL, root, path, dir_id, "default", 7, 0);
++ di = btrfs_lookup_dir_item(NULL, root, path, dir_id, &name, 0);
+ if (IS_ERR(di)) {
+ btrfs_free_path(path);
+ return PTR_ERR(di);
+diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c
+index a555567594418..b0fe054c9f401 100644
+--- a/fs/btrfs/transaction.c
++++ b/fs/btrfs/transaction.c
+@@ -1694,8 +1694,7 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
+ /* check if there is a file/dir which has the same name. */
+ dir_item = btrfs_lookup_dir_item(NULL, parent_root, path,
+ btrfs_ino(BTRFS_I(parent_inode)),
+- dentry->d_name.name,
+- dentry->d_name.len, 0);
++ &dentry->d_name, 0);
+ if (dir_item != NULL && !IS_ERR(dir_item)) {
+ pending->error = -EEXIST;
+ goto dir_item_existed;
+@@ -1790,7 +1789,7 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
+ ret = btrfs_add_root_ref(trans, objectid,
+ parent_root->root_key.objectid,
+ btrfs_ino(BTRFS_I(parent_inode)), index,
+- dentry->d_name.name, dentry->d_name.len);
++ &dentry->d_name);
+ if (ret) {
+ btrfs_abort_transaction(trans, ret);
+ goto fail;
+@@ -1822,9 +1821,9 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
+ if (ret < 0)
+ goto fail;
+
+- ret = btrfs_insert_dir_item(trans, dentry->d_name.name,
+- dentry->d_name.len, BTRFS_I(parent_inode),
+- &key, BTRFS_FT_DIR, index);
++ ret = btrfs_insert_dir_item(trans, &dentry->d_name,
++ BTRFS_I(parent_inode), &key, BTRFS_FT_DIR,
++ index);
+ /* We have check then name at the beginning, so it is impossible. */
+ BUG_ON(ret == -EEXIST || ret == -EOVERFLOW);
+ if (ret) {
+diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
+index 00be69ce7b90f..9f55e81acc0ef 100644
+--- a/fs/btrfs/tree-log.c
++++ b/fs/btrfs/tree-log.c
+@@ -595,6 +595,21 @@ static int overwrite_item(struct btrfs_trans_handle *trans,
+ return do_overwrite_item(trans, root, path, eb, slot, key);
+ }
+
++static int read_alloc_one_name(struct extent_buffer *eb, void *start, int len,
++ struct qstr *name)
++{
++ char *buf;
++
++ buf = kmalloc(len, GFP_NOFS);
++ if (!buf)
++ return -ENOMEM;
++
++ read_extent_buffer(eb, buf, (unsigned long)start, len);
++ name->name = buf;
++ name->len = len;
++ return 0;
++}
++
+ /*
+ * simple helper to read an inode off the disk from a given root
+ * This can only be called for subvolume roots and not for the log
+@@ -901,12 +916,11 @@ static noinline int replay_one_extent(struct btrfs_trans_handle *trans,
+ static int unlink_inode_for_log_replay(struct btrfs_trans_handle *trans,
+ struct btrfs_inode *dir,
+ struct btrfs_inode *inode,
+- const char *name,
+- int name_len)
++ const struct qstr *name)
+ {
+ int ret;
+
+- ret = btrfs_unlink_inode(trans, dir, inode, name, name_len);
++ ret = btrfs_unlink_inode(trans, dir, inode, name);
+ if (ret)
+ return ret;
+ /*
+@@ -933,8 +947,7 @@ static noinline int drop_one_dir_item(struct btrfs_trans_handle *trans,
+ {
+ struct btrfs_root *root = dir->root;
+ struct inode *inode;
+- char *name;
+- int name_len;
++ struct qstr name;
+ struct extent_buffer *leaf;
+ struct btrfs_key location;
+ int ret;
+@@ -942,12 +955,10 @@ static noinline int drop_one_dir_item(struct btrfs_trans_handle *trans,
+ leaf = path->nodes[0];
+
+ btrfs_dir_item_key_to_cpu(leaf, di, &location);
+- name_len = btrfs_dir_name_len(leaf, di);
+- name = kmalloc(name_len, GFP_NOFS);
+- if (!name)
++ ret = read_alloc_one_name(leaf, di + 1, btrfs_dir_name_len(leaf, di), &name);
++ if (ret)
+ return -ENOMEM;
+
+- read_extent_buffer(leaf, name, (unsigned long)(di + 1), name_len);
+ btrfs_release_path(path);
+
+ inode = read_one_inode(root, location.objectid);
+@@ -960,10 +971,9 @@ static noinline int drop_one_dir_item(struct btrfs_trans_handle *trans,
+ if (ret)
+ goto out;
+
+- ret = unlink_inode_for_log_replay(trans, dir, BTRFS_I(inode), name,
+- name_len);
++ ret = unlink_inode_for_log_replay(trans, dir, BTRFS_I(inode), &name);
+ out:
+- kfree(name);
++ kfree(name.name);
+ iput(inode);
+ return ret;
+ }
+@@ -978,14 +988,14 @@ static noinline int drop_one_dir_item(struct btrfs_trans_handle *trans,
+ static noinline int inode_in_dir(struct btrfs_root *root,
+ struct btrfs_path *path,
+ u64 dirid, u64 objectid, u64 index,
+- const char *name, int name_len)
++ struct qstr *name)
+ {
+ struct btrfs_dir_item *di;
+ struct btrfs_key location;
+ int ret = 0;
+
+ di = btrfs_lookup_dir_index_item(NULL, root, path, dirid,
+- index, name, name_len, 0);
++ index, name, 0);
+ if (IS_ERR(di)) {
+ ret = PTR_ERR(di);
+ goto out;
+@@ -998,7 +1008,7 @@ static noinline int inode_in_dir(struct btrfs_root *root,
+ }
+
+ btrfs_release_path(path);
+- di = btrfs_lookup_dir_item(NULL, root, path, dirid, name, name_len, 0);
++ di = btrfs_lookup_dir_item(NULL, root, path, dirid, name, 0);
+ if (IS_ERR(di)) {
+ ret = PTR_ERR(di);
+ goto out;
+@@ -1025,7 +1035,7 @@ static noinline int inode_in_dir(struct btrfs_root *root,
+ static noinline int backref_in_log(struct btrfs_root *log,
+ struct btrfs_key *key,
+ u64 ref_objectid,
+- const char *name, int namelen)
++ const struct qstr *name)
+ {
+ struct btrfs_path *path;
+ int ret;
+@@ -1045,12 +1055,10 @@ static noinline int backref_in_log(struct btrfs_root *log,
+ if (key->type == BTRFS_INODE_EXTREF_KEY)
+ ret = !!btrfs_find_name_in_ext_backref(path->nodes[0],
+ path->slots[0],
+- ref_objectid,
+- name, namelen);
++ ref_objectid, name);
+ else
+ ret = !!btrfs_find_name_in_backref(path->nodes[0],
+- path->slots[0],
+- name, namelen);
++ path->slots[0], name);
+ out:
+ btrfs_free_path(path);
+ return ret;
+@@ -1063,11 +1071,9 @@ static inline int __add_inode_ref(struct btrfs_trans_handle *trans,
+ struct btrfs_inode *dir,
+ struct btrfs_inode *inode,
+ u64 inode_objectid, u64 parent_objectid,
+- u64 ref_index, char *name, int namelen)
++ u64 ref_index, struct qstr *name)
+ {
+ int ret;
+- char *victim_name;
+- int victim_name_len;
+ struct extent_buffer *leaf;
+ struct btrfs_dir_item *di;
+ struct btrfs_key search_key;
+@@ -1099,43 +1105,40 @@ static inline int __add_inode_ref(struct btrfs_trans_handle *trans,
+ ptr = btrfs_item_ptr_offset(leaf, path->slots[0]);
+ ptr_end = ptr + btrfs_item_size(leaf, path->slots[0]);
+ while (ptr < ptr_end) {
+- victim_ref = (struct btrfs_inode_ref *)ptr;
+- victim_name_len = btrfs_inode_ref_name_len(leaf,
+- victim_ref);
+- victim_name = kmalloc(victim_name_len, GFP_NOFS);
+- if (!victim_name)
+- return -ENOMEM;
++ struct qstr victim_name;
+
+- read_extent_buffer(leaf, victim_name,
+- (unsigned long)(victim_ref + 1),
+- victim_name_len);
++ victim_ref = (struct btrfs_inode_ref *)ptr;
++ ret = read_alloc_one_name(leaf, (victim_ref + 1),
++ btrfs_inode_ref_name_len(leaf, victim_ref),
++ &victim_name);
++ if (ret)
++ return ret;
+
+ ret = backref_in_log(log_root, &search_key,
+- parent_objectid, victim_name,
+- victim_name_len);
++ parent_objectid, &victim_name);
+ if (ret < 0) {
+- kfree(victim_name);
++ kfree(victim_name.name);
+ return ret;
+ } else if (!ret) {
+ inc_nlink(&inode->vfs_inode);
+ btrfs_release_path(path);
+
+ ret = unlink_inode_for_log_replay(trans, dir, inode,
+- victim_name, victim_name_len);
+- kfree(victim_name);
++ &victim_name);
++ kfree(victim_name.name);
+ if (ret)
+ return ret;
+ goto again;
+ }
+- kfree(victim_name);
++ kfree(victim_name.name);
+
+- ptr = (unsigned long)(victim_ref + 1) + victim_name_len;
++ ptr = (unsigned long)(victim_ref + 1) + victim_name.len;
+ }
+ }
+ btrfs_release_path(path);
+
+ /* Same search but for extended refs */
+- extref = btrfs_lookup_inode_extref(NULL, root, path, name, namelen,
++ extref = btrfs_lookup_inode_extref(NULL, root, path, name,
+ inode_objectid, parent_objectid, 0,
+ 0);
+ if (IS_ERR(extref)) {
+@@ -1152,29 +1155,28 @@ static inline int __add_inode_ref(struct btrfs_trans_handle *trans,
+ base = btrfs_item_ptr_offset(leaf, path->slots[0]);
+
+ while (cur_offset < item_size) {
+- extref = (struct btrfs_inode_extref *)(base + cur_offset);
++ struct qstr victim_name;
+
+- victim_name_len = btrfs_inode_extref_name_len(leaf, extref);
++ extref = (struct btrfs_inode_extref *)(base + cur_offset);
+
+ if (btrfs_inode_extref_parent(leaf, extref) != parent_objectid)
+ goto next;
+
+- victim_name = kmalloc(victim_name_len, GFP_NOFS);
+- if (!victim_name)
+- return -ENOMEM;
+- read_extent_buffer(leaf, victim_name, (unsigned long)&extref->name,
+- victim_name_len);
++ ret = read_alloc_one_name(leaf, &extref->name,
++ btrfs_inode_extref_name_len(leaf, extref),
++ &victim_name);
++ if (ret)
++ return ret;
+
+ search_key.objectid = inode_objectid;
+ search_key.type = BTRFS_INODE_EXTREF_KEY;
+ search_key.offset = btrfs_extref_hash(parent_objectid,
+- victim_name,
+- victim_name_len);
++ victim_name.name,
++ victim_name.len);
+ ret = backref_in_log(log_root, &search_key,
+- parent_objectid, victim_name,
+- victim_name_len);
++ parent_objectid, &victim_name);
+ if (ret < 0) {
+- kfree(victim_name);
++ kfree(victim_name.name);
+ return ret;
+ } else if (!ret) {
+ ret = -ENOENT;
+@@ -1186,26 +1188,24 @@ static inline int __add_inode_ref(struct btrfs_trans_handle *trans,
+
+ ret = unlink_inode_for_log_replay(trans,
+ BTRFS_I(victim_parent),
+- inode,
+- victim_name,
+- victim_name_len);
++ inode, &victim_name);
+ }
+ iput(victim_parent);
+- kfree(victim_name);
++ kfree(victim_name.name);
+ if (ret)
+ return ret;
+ goto again;
+ }
+- kfree(victim_name);
++ kfree(victim_name.name);
+ next:
+- cur_offset += victim_name_len + sizeof(*extref);
++ cur_offset += victim_name.len + sizeof(*extref);
+ }
+ }
+ btrfs_release_path(path);
+
+ /* look for a conflicting sequence number */
+ di = btrfs_lookup_dir_index_item(trans, root, path, btrfs_ino(dir),
+- ref_index, name, namelen, 0);
++ ref_index, name, 0);
+ if (IS_ERR(di)) {
+ return PTR_ERR(di);
+ } else if (di) {
+@@ -1216,8 +1216,7 @@ static inline int __add_inode_ref(struct btrfs_trans_handle *trans,
+ btrfs_release_path(path);
+
+ /* look for a conflicting name */
+- di = btrfs_lookup_dir_item(trans, root, path, btrfs_ino(dir),
+- name, namelen, 0);
++ di = btrfs_lookup_dir_item(trans, root, path, btrfs_ino(dir), name, 0);
+ if (IS_ERR(di)) {
+ return PTR_ERR(di);
+ } else if (di) {
+@@ -1231,20 +1230,18 @@ static inline int __add_inode_ref(struct btrfs_trans_handle *trans,
+ }
+
+ static int extref_get_fields(struct extent_buffer *eb, unsigned long ref_ptr,
+- u32 *namelen, char **name, u64 *index,
++ struct qstr *name, u64 *index,
+ u64 *parent_objectid)
+ {
+ struct btrfs_inode_extref *extref;
++ int ret;
+
+ extref = (struct btrfs_inode_extref *)ref_ptr;
+
+- *namelen = btrfs_inode_extref_name_len(eb, extref);
+- *name = kmalloc(*namelen, GFP_NOFS);
+- if (*name == NULL)
+- return -ENOMEM;
+-
+- read_extent_buffer(eb, *name, (unsigned long)&extref->name,
+- *namelen);
++ ret = read_alloc_one_name(eb, &extref->name,
++ btrfs_inode_extref_name_len(eb, extref), name);
++ if (ret)
++ return ret;
+
+ if (index)
+ *index = btrfs_inode_extref_index(eb, extref);
+@@ -1255,18 +1252,17 @@ static int extref_get_fields(struct extent_buffer *eb, unsigned long ref_ptr,
+ }
+
+ static int ref_get_fields(struct extent_buffer *eb, unsigned long ref_ptr,
+- u32 *namelen, char **name, u64 *index)
++ struct qstr *name, u64 *index)
+ {
+ struct btrfs_inode_ref *ref;
++ int ret;
+
+ ref = (struct btrfs_inode_ref *)ref_ptr;
+
+- *namelen = btrfs_inode_ref_name_len(eb, ref);
+- *name = kmalloc(*namelen, GFP_NOFS);
+- if (*name == NULL)
+- return -ENOMEM;
+-
+- read_extent_buffer(eb, *name, (unsigned long)(ref + 1), *namelen);
++ ret = read_alloc_one_name(eb, ref + 1, btrfs_inode_ref_name_len(eb, ref),
++ name);
++ if (ret)
++ return ret;
+
+ if (index)
+ *index = btrfs_inode_ref_index(eb, ref);
+@@ -1308,28 +1304,24 @@ static int unlink_old_inode_refs(struct btrfs_trans_handle *trans,
+ ref_ptr = btrfs_item_ptr_offset(eb, path->slots[0]);
+ ref_end = ref_ptr + btrfs_item_size(eb, path->slots[0]);
+ while (ref_ptr < ref_end) {
+- char *name = NULL;
+- int namelen;
++ struct qstr name;
+ u64 parent_id;
+
+ if (key->type == BTRFS_INODE_EXTREF_KEY) {
+- ret = extref_get_fields(eb, ref_ptr, &namelen, &name,
++ ret = extref_get_fields(eb, ref_ptr, &name,
+ NULL, &parent_id);
+ } else {
+ parent_id = key->offset;
+- ret = ref_get_fields(eb, ref_ptr, &namelen, &name,
+- NULL);
++ ret = ref_get_fields(eb, ref_ptr, &name, NULL);
+ }
+ if (ret)
+ goto out;
+
+ if (key->type == BTRFS_INODE_EXTREF_KEY)
+ ret = !!btrfs_find_name_in_ext_backref(log_eb, log_slot,
+- parent_id, name,
+- namelen);
++ parent_id, &name);
+ else
+- ret = !!btrfs_find_name_in_backref(log_eb, log_slot,
+- name, namelen);
++ ret = !!btrfs_find_name_in_backref(log_eb, log_slot, &name);
+
+ if (!ret) {
+ struct inode *dir;
+@@ -1338,20 +1330,20 @@ static int unlink_old_inode_refs(struct btrfs_trans_handle *trans,
+ dir = read_one_inode(root, parent_id);
+ if (!dir) {
+ ret = -ENOENT;
+- kfree(name);
++ kfree(name.name);
+ goto out;
+ }
+ ret = unlink_inode_for_log_replay(trans, BTRFS_I(dir),
+- inode, name, namelen);
+- kfree(name);
++ inode, &name);
++ kfree(name.name);
+ iput(dir);
+ if (ret)
+ goto out;
+ goto again;
+ }
+
+- kfree(name);
+- ref_ptr += namelen;
++ kfree(name.name);
++ ref_ptr += name.len;
+ if (key->type == BTRFS_INODE_EXTREF_KEY)
+ ref_ptr += sizeof(struct btrfs_inode_extref);
+ else
+@@ -1380,8 +1372,7 @@ static noinline int add_inode_ref(struct btrfs_trans_handle *trans,
+ struct inode *inode = NULL;
+ unsigned long ref_ptr;
+ unsigned long ref_end;
+- char *name = NULL;
+- int namelen;
++ struct qstr name;
+ int ret;
+ int log_ref_ver = 0;
+ u64 parent_objectid;
+@@ -1425,7 +1416,7 @@ static noinline int add_inode_ref(struct btrfs_trans_handle *trans,
+
+ while (ref_ptr < ref_end) {
+ if (log_ref_ver) {
+- ret = extref_get_fields(eb, ref_ptr, &namelen, &name,
++ ret = extref_get_fields(eb, ref_ptr, &name,
+ &ref_index, &parent_objectid);
+ /*
+ * parent object can change from one array
+@@ -1438,15 +1429,13 @@ static noinline int add_inode_ref(struct btrfs_trans_handle *trans,
+ goto out;
+ }
+ } else {
+- ret = ref_get_fields(eb, ref_ptr, &namelen, &name,
+- &ref_index);
++ ret = ref_get_fields(eb, ref_ptr, &name, &ref_index);
+ }
+ if (ret)
+ goto out;
+
+ ret = inode_in_dir(root, path, btrfs_ino(BTRFS_I(dir)),
+- btrfs_ino(BTRFS_I(inode)), ref_index,
+- name, namelen);
++ btrfs_ino(BTRFS_I(inode)), ref_index, &name);
+ if (ret < 0) {
+ goto out;
+ } else if (ret == 0) {
+@@ -1460,7 +1449,7 @@ static noinline int add_inode_ref(struct btrfs_trans_handle *trans,
+ ret = __add_inode_ref(trans, root, path, log,
+ BTRFS_I(dir), BTRFS_I(inode),
+ inode_objectid, parent_objectid,
+- ref_index, name, namelen);
++ ref_index, &name);
+ if (ret) {
+ if (ret == 1)
+ ret = 0;
+@@ -1469,7 +1458,7 @@ static noinline int add_inode_ref(struct btrfs_trans_handle *trans,
+
+ /* insert our name */
+ ret = btrfs_add_link(trans, BTRFS_I(dir), BTRFS_I(inode),
+- name, namelen, 0, ref_index);
++ &name, 0, ref_index);
+ if (ret)
+ goto out;
+
+@@ -1479,9 +1468,9 @@ static noinline int add_inode_ref(struct btrfs_trans_handle *trans,
+ }
+ /* Else, ret == 1, we already have a perfect match, we're done. */
+
+- ref_ptr = (unsigned long)(ref_ptr + ref_struct_size) + namelen;
+- kfree(name);
+- name = NULL;
++ ref_ptr = (unsigned long)(ref_ptr + ref_struct_size) + name.len;
++ kfree(name.name);
++ name.name = NULL;
+ if (log_ref_ver) {
+ iput(dir);
+ dir = NULL;
+@@ -1505,7 +1494,7 @@ static noinline int add_inode_ref(struct btrfs_trans_handle *trans,
+ ret = overwrite_item(trans, root, path, eb, slot, key);
+ out:
+ btrfs_release_path(path);
+- kfree(name);
++ kfree(name.name);
+ iput(dir);
+ iput(inode);
+ return ret;
+@@ -1777,7 +1766,7 @@ static noinline int link_to_fixup_dir(struct btrfs_trans_handle *trans,
+ static noinline int insert_one_name(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ u64 dirid, u64 index,
+- char *name, int name_len,
++ const struct qstr *name,
+ struct btrfs_key *location)
+ {
+ struct inode *inode;
+@@ -1795,7 +1784,7 @@ static noinline int insert_one_name(struct btrfs_trans_handle *trans,
+ }
+
+ ret = btrfs_add_link(trans, BTRFS_I(dir), BTRFS_I(inode), name,
+- name_len, 1, index);
++ 1, index);
+
+ /* FIXME, put inode into FIXUP list */
+
+@@ -1855,8 +1844,7 @@ static noinline int replay_one_name(struct btrfs_trans_handle *trans,
+ struct btrfs_dir_item *di,
+ struct btrfs_key *key)
+ {
+- char *name;
+- int name_len;
++ struct qstr name;
+ struct btrfs_dir_item *dir_dst_di;
+ struct btrfs_dir_item *index_dst_di;
+ bool dir_dst_matches = false;
+@@ -1874,17 +1862,11 @@ static noinline int replay_one_name(struct btrfs_trans_handle *trans,
+ if (!dir)
+ return -EIO;
+
+- name_len = btrfs_dir_name_len(eb, di);
+- name = kmalloc(name_len, GFP_NOFS);
+- if (!name) {
+- ret = -ENOMEM;
++ ret = read_alloc_one_name(eb, di + 1, btrfs_dir_name_len(eb, di), &name);
++ if (ret)
+ goto out;
+- }
+
+ log_type = btrfs_dir_type(eb, di);
+- read_extent_buffer(eb, name, (unsigned long)(di + 1),
+- name_len);
+-
+ btrfs_dir_item_key_to_cpu(eb, di, &log_key);
+ ret = btrfs_lookup_inode(trans, root, path, &log_key, 0);
+ btrfs_release_path(path);
+@@ -1894,7 +1876,7 @@ static noinline int replay_one_name(struct btrfs_trans_handle *trans,
+ ret = 0;
+
+ dir_dst_di = btrfs_lookup_dir_item(trans, root, path, key->objectid,
+- name, name_len, 1);
++ &name, 1);
+ if (IS_ERR(dir_dst_di)) {
+ ret = PTR_ERR(dir_dst_di);
+ goto out;
+@@ -1911,7 +1893,7 @@ static noinline int replay_one_name(struct btrfs_trans_handle *trans,
+
+ index_dst_di = btrfs_lookup_dir_index_item(trans, root, path,
+ key->objectid, key->offset,
+- name, name_len, 1);
++ &name, 1);
+ if (IS_ERR(index_dst_di)) {
+ ret = PTR_ERR(index_dst_di);
+ goto out;
+@@ -1939,7 +1921,7 @@ static noinline int replay_one_name(struct btrfs_trans_handle *trans,
+ search_key.objectid = log_key.objectid;
+ search_key.type = BTRFS_INODE_REF_KEY;
+ search_key.offset = key->objectid;
+- ret = backref_in_log(root->log_root, &search_key, 0, name, name_len);
++ ret = backref_in_log(root->log_root, &search_key, 0, &name);
+ if (ret < 0) {
+ goto out;
+ } else if (ret) {
+@@ -1952,8 +1934,7 @@ static noinline int replay_one_name(struct btrfs_trans_handle *trans,
+ search_key.objectid = log_key.objectid;
+ search_key.type = BTRFS_INODE_EXTREF_KEY;
+ search_key.offset = key->objectid;
+- ret = backref_in_log(root->log_root, &search_key, key->objectid, name,
+- name_len);
++ ret = backref_in_log(root->log_root, &search_key, key->objectid, &name);
+ if (ret < 0) {
+ goto out;
+ } else if (ret) {
+@@ -1964,7 +1945,7 @@ static noinline int replay_one_name(struct btrfs_trans_handle *trans,
+ }
+ btrfs_release_path(path);
+ ret = insert_one_name(trans, root, key->objectid, key->offset,
+- name, name_len, &log_key);
++ &name, &log_key);
+ if (ret && ret != -ENOENT && ret != -EEXIST)
+ goto out;
+ if (!ret)
+@@ -1974,10 +1955,10 @@ static noinline int replay_one_name(struct btrfs_trans_handle *trans,
+
+ out:
+ if (!ret && update_size) {
+- btrfs_i_size_write(BTRFS_I(dir), dir->i_size + name_len * 2);
++ btrfs_i_size_write(BTRFS_I(dir), dir->i_size + name.len * 2);
+ ret = btrfs_update_inode(trans, root, BTRFS_I(dir));
+ }
+- kfree(name);
++ kfree(name.name);
+ iput(dir);
+ if (!ret && name_added)
+ ret = 1;
+@@ -2143,8 +2124,7 @@ static noinline int check_item_in_log(struct btrfs_trans_handle *trans,
+ struct extent_buffer *eb;
+ int slot;
+ struct btrfs_dir_item *di;
+- int name_len;
+- char *name;
++ struct qstr name;
+ struct inode *inode = NULL;
+ struct btrfs_key location;
+
+@@ -2159,22 +2139,16 @@ static noinline int check_item_in_log(struct btrfs_trans_handle *trans,
+ eb = path->nodes[0];
+ slot = path->slots[0];
+ di = btrfs_item_ptr(eb, slot, struct btrfs_dir_item);
+- name_len = btrfs_dir_name_len(eb, di);
+- name = kmalloc(name_len, GFP_NOFS);
+- if (!name) {
+- ret = -ENOMEM;
++ ret = read_alloc_one_name(eb, di + 1, btrfs_dir_name_len(eb, di), &name);
++ if (ret)
+ goto out;
+- }
+-
+- read_extent_buffer(eb, name, (unsigned long)(di + 1), name_len);
+
+ if (log) {
+ struct btrfs_dir_item *log_di;
+
+ log_di = btrfs_lookup_dir_index_item(trans, log, log_path,
+ dir_key->objectid,
+- dir_key->offset,
+- name, name_len, 0);
++ dir_key->offset, &name, 0);
+ if (IS_ERR(log_di)) {
+ ret = PTR_ERR(log_di);
+ goto out;
+@@ -2200,7 +2174,7 @@ static noinline int check_item_in_log(struct btrfs_trans_handle *trans,
+
+ inc_nlink(inode);
+ ret = unlink_inode_for_log_replay(trans, BTRFS_I(dir), BTRFS_I(inode),
+- name, name_len);
++ &name);
+ /*
+ * Unlike dir item keys, dir index keys can only have one name (entry) in
+ * them, as there are no key collisions since each key has a unique offset
+@@ -2209,7 +2183,7 @@ static noinline int check_item_in_log(struct btrfs_trans_handle *trans,
+ out:
+ btrfs_release_path(path);
+ btrfs_release_path(log_path);
+- kfree(name);
++ kfree(name.name);
+ iput(inode);
+ return ret;
+ }
+@@ -3443,7 +3417,7 @@ static int del_logged_dentry(struct btrfs_trans_handle *trans,
+ struct btrfs_root *log,
+ struct btrfs_path *path,
+ u64 dir_ino,
+- const char *name, int name_len,
++ const struct qstr *name,
+ u64 index)
+ {
+ struct btrfs_dir_item *di;
+@@ -3453,7 +3427,7 @@ static int del_logged_dentry(struct btrfs_trans_handle *trans,
+ * for dir item keys.
+ */
+ di = btrfs_lookup_dir_index_item(trans, log, path, dir_ino,
+- index, name, name_len, -1);
++ index, name, -1);
+ if (IS_ERR(di))
+ return PTR_ERR(di);
+ else if (!di)
+@@ -3490,7 +3464,7 @@ static int del_logged_dentry(struct btrfs_trans_handle *trans,
+ */
+ void btrfs_del_dir_entries_in_log(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+- const char *name, int name_len,
++ const struct qstr *name,
+ struct btrfs_inode *dir, u64 index)
+ {
+ struct btrfs_path *path;
+@@ -3517,7 +3491,7 @@ void btrfs_del_dir_entries_in_log(struct btrfs_trans_handle *trans,
+ }
+
+ ret = del_logged_dentry(trans, root->log_root, path, btrfs_ino(dir),
+- name, name_len, index);
++ name, index);
+ btrfs_free_path(path);
+ out_unlock:
+ mutex_unlock(&dir->log_mutex);
+@@ -3529,7 +3503,7 @@ void btrfs_del_dir_entries_in_log(struct btrfs_trans_handle *trans,
+ /* see comments for btrfs_del_dir_entries_in_log */
+ void btrfs_del_inode_ref_in_log(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+- const char *name, int name_len,
++ const struct qstr *name,
+ struct btrfs_inode *inode, u64 dirid)
+ {
+ struct btrfs_root *log;
+@@ -3550,7 +3524,7 @@ void btrfs_del_inode_ref_in_log(struct btrfs_trans_handle *trans,
+ log = root->log_root;
+ mutex_lock(&inode->log_mutex);
+
+- ret = btrfs_del_inode_ref(trans, log, name, name_len, btrfs_ino(inode),
++ ret = btrfs_del_inode_ref(trans, log, name, btrfs_ino(inode),
+ dirid, &index);
+ mutex_unlock(&inode->log_mutex);
+ if (ret < 0 && ret != -ENOENT)
+@@ -5293,6 +5267,7 @@ static int btrfs_check_ref_name_override(struct extent_buffer *eb,
+ u32 this_len;
+ unsigned long name_ptr;
+ struct btrfs_dir_item *di;
++ struct qstr name_str;
+
+ if (key->type == BTRFS_INODE_REF_KEY) {
+ struct btrfs_inode_ref *iref;
+@@ -5326,8 +5301,11 @@ static int btrfs_check_ref_name_override(struct extent_buffer *eb,
+ }
+
+ read_extent_buffer(eb, name, name_ptr, this_name_len);
++
++ name_str.name = name;
++ name_str.len = this_name_len;
+ di = btrfs_lookup_dir_item(NULL, inode->root, search_path,
+- parent, name, this_name_len, 0);
++ parent, &name_str, 0);
+ if (di && !IS_ERR(di)) {
+ struct btrfs_key di_key;
+
+@@ -7530,8 +7508,7 @@ void btrfs_log_new_name(struct btrfs_trans_handle *trans,
+ */
+ mutex_lock(&old_dir->log_mutex);
+ ret = del_logged_dentry(trans, log, path, btrfs_ino(old_dir),
+- old_dentry->d_name.name,
+- old_dentry->d_name.len, old_dir_index);
++ &old_dentry->d_name, old_dir_index);
+ if (ret > 0) {
+ /*
+ * The dentry does not exist in the log, so record its
+diff --git a/fs/btrfs/tree-log.h b/fs/btrfs/tree-log.h
+index bcca74128c3bb..6c0dc79787f05 100644
+--- a/fs/btrfs/tree-log.h
++++ b/fs/btrfs/tree-log.h
+@@ -84,11 +84,11 @@ int btrfs_log_dentry_safe(struct btrfs_trans_handle *trans,
+ struct btrfs_log_ctx *ctx);
+ void btrfs_del_dir_entries_in_log(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+- const char *name, int name_len,
++ const struct qstr *name,
+ struct btrfs_inode *dir, u64 index);
+ void btrfs_del_inode_ref_in_log(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+- const char *name, int name_len,
++ const struct qstr *name,
+ struct btrfs_inode *inode, u64 dirid);
+ void btrfs_end_log_trans(struct btrfs_root *root);
+ void btrfs_pin_log_trans(struct btrfs_root *root);
+--
+2.40.1
+
--- /dev/null
+From 562b1f633915512df826243cd52eeb774147186c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 21 Sep 2023 14:12:35 -0400
+Subject: maple_tree: add mas_is_active() to detect in-tree walks
+
+From: Liam R. Howlett <Liam.Howlett@oracle.com>
+
+[ Upstream commit 5c590804b6b0ff933ed4e5cee5d76de3a5048d9f ]
+
+Patch series "maple_tree: Fix mas_prev() state regression".
+
+Pedro Falcato retported an mprotect regression [1] which was bisected back
+to the iterator changes for maple tree. Root cause analysis showed the
+mas_prev() running off the end of the VMA space (previous from 0) followed
+by mas_find(), would skip the first value.
+
+This patchset introduces maple state underflow/overflow so the sequence of
+calls on the maple state will return what the user expects.
+
+Users who encounter this bug may see mprotect(), userfaultfd_register(),
+and mlock() fail on VMAs mapped with address 0.
+
+This patch (of 2):
+
+Instead of constantly checking each possibility of the maple state,
+create a fast path that will skip over checking unlikely states.
+
+Link: https://lkml.kernel.org/r/20230921181236.509072-1-Liam.Howlett@oracle.com
+Link: https://lkml.kernel.org/r/20230921181236.509072-2-Liam.Howlett@oracle.com
+Signed-off-by: Liam R. Howlett <Liam.Howlett@oracle.com>
+Cc: Pedro Falcato <pedro.falcato@gmail.com>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/linux/maple_tree.h | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+diff --git a/include/linux/maple_tree.h b/include/linux/maple_tree.h
+index 443dec917ec64..27864178d1918 100644
+--- a/include/linux/maple_tree.h
++++ b/include/linux/maple_tree.h
+@@ -488,6 +488,15 @@ static inline bool mas_is_paused(struct ma_state *mas)
+ return mas->node == MAS_PAUSE;
+ }
+
++/* Check if the mas is pointing to a node or not */
++static inline bool mas_is_active(struct ma_state *mas)
++{
++ if ((unsigned long)mas->node >= MAPLE_RESERVED_RANGE)
++ return true;
++
++ return false;
++}
++
+ /**
+ * mas_reset() - Reset a Maple Tree operation state.
+ * @mas: Maple Tree operation state.
+--
+2.40.1
+
--- /dev/null
+From 2c6b47348d1fd76c15dc3f4068829747a550419a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 24 May 2023 11:12:47 +0800
+Subject: maple_tree: relocate the declaration of mas_empty_area_rev().
+
+From: Peng Zhang <zhangpeng.00@bytedance.com>
+
+[ Upstream commit 06b27ce36a1a3dc5ea6f8314d0c7d1baa9f8ece7 ]
+
+Relocate the declaration of mas_empty_area_rev() so that mas_empty_area()
+and mas_empty_area_rev() are together.
+
+Link: https://lkml.kernel.org/r/20230524031247.65949-11-zhangpeng.00@bytedance.com
+Signed-off-by: Peng Zhang <zhangpeng.00@bytedance.com>
+Reviewed-by: Liam R. Howlett <Liam.Howlett@oracle.com>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Stable-dep-of: 5c590804b6b0 ("maple_tree: add mas_is_active() to detect in-tree walks")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/linux/maple_tree.h | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+diff --git a/include/linux/maple_tree.h b/include/linux/maple_tree.h
+index 1a424edb71a65..443dec917ec64 100644
+--- a/include/linux/maple_tree.h
++++ b/include/linux/maple_tree.h
+@@ -469,6 +469,12 @@ void *mas_next(struct ma_state *mas, unsigned long max);
+
+ int mas_empty_area(struct ma_state *mas, unsigned long min, unsigned long max,
+ unsigned long size);
++/*
++ * This finds an empty area from the highest address to the lowest.
++ * AKA "Topdown" version,
++ */
++int mas_empty_area_rev(struct ma_state *mas, unsigned long min,
++ unsigned long max, unsigned long size);
+
+ /* Checks if a mas has not found anything */
+ static inline bool mas_is_none(struct ma_state *mas)
+@@ -482,12 +488,6 @@ static inline bool mas_is_paused(struct ma_state *mas)
+ return mas->node == MAS_PAUSE;
+ }
+
+-/*
+- * This finds an empty area from the highest address to the lowest.
+- * AKA "Topdown" version,
+- */
+-int mas_empty_area_rev(struct ma_state *mas, unsigned long min,
+- unsigned long max, unsigned long size);
+ /**
+ * mas_reset() - Reset a Maple Tree operation state.
+ * @mas: Maple Tree operation state.
+--
+2.40.1
+
--- /dev/null
+From 8115a697c47c8d0d1e35d67364eb7fb1b5fcf922 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 21 Dec 2022 14:00:56 +0800
+Subject: maple_tree: remove the redundant code
+
+From: Vernon Yang <vernon2gm@gmail.com>
+
+[ Upstream commit eabb305293835b191ffe60234587ae8bf5e4e9fd ]
+
+The macros CONFIG_DEBUG_MAPLE_TREE_VERBOSE no one uses, functions
+mas_dup_tree() and mas_dup_store() are not implemented, just function
+declaration, so drop it.
+
+Link: https://lkml.kernel.org/r/20221221060058.609003-6-vernon2gm@gmail.com
+Signed-off-by: Vernon Yang <vernon2gm@gmail.com>
+Reviewed-by: Liam R. Howlett <Liam.Howlett@oracle.com>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Stable-dep-of: 5c590804b6b0 ("maple_tree: add mas_is_active() to detect in-tree walks")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/linux/maple_tree.h | 4 ----
+ 1 file changed, 4 deletions(-)
+
+diff --git a/include/linux/maple_tree.h b/include/linux/maple_tree.h
+index e594db58a0f14..1a424edb71a65 100644
+--- a/include/linux/maple_tree.h
++++ b/include/linux/maple_tree.h
+@@ -12,7 +12,6 @@
+ #include <linux/rcupdate.h>
+ #include <linux/spinlock.h>
+ /* #define CONFIG_MAPLE_RCU_DISABLED */
+-/* #define CONFIG_DEBUG_MAPLE_TREE_VERBOSE */
+
+ /*
+ * Allocated nodes are mutable until they have been inserted into the tree,
+@@ -483,9 +482,6 @@ static inline bool mas_is_paused(struct ma_state *mas)
+ return mas->node == MAS_PAUSE;
+ }
+
+-void mas_dup_tree(struct ma_state *oldmas, struct ma_state *mas);
+-void mas_dup_store(struct ma_state *mas, void *entry);
+-
+ /*
+ * This finds an empty area from the highest address to the lowest.
+ * AKA "Topdown" version,
+--
+2.40.1
+
--- /dev/null
+From 4dd74e82b56a3b09dafbbfc7ff1d61da63c67d27 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 21 Dec 2022 10:08:45 -0800
+Subject: mm/memory: add vm_normal_folio()
+
+From: Vishal Moola (Oracle) <vishal.moola@gmail.com>
+
+[ Upstream commit 318e9342fbbb6888d903d86e83865609901a1c65 ]
+
+Patch series "Convert deactivate_page() to folio_deactivate()", v4.
+
+Deactivate_page() has already been converted to use folios. This patch
+series modifies the callers of deactivate_page() to use folios. It also
+introduces vm_normal_folio() to assist with folio conversions, and
+converts deactivate_page() to folio_deactivate() which takes in a folio.
+
+This patch (of 4):
+
+Introduce a wrapper function called vm_normal_folio(). This function
+calls vm_normal_page() and returns the folio of the page found, or null if
+no page is found.
+
+This function allows callers to get a folio from a pte, which will
+eventually allow them to completely replace their struct page variables
+with struct folio instead.
+
+Link: https://lkml.kernel.org/r/20221221180848.20774-1-vishal.moola@gmail.com
+Link: https://lkml.kernel.org/r/20221221180848.20774-2-vishal.moola@gmail.com
+Signed-off-by: Vishal Moola (Oracle) <vishal.moola@gmail.com>
+Reviewed-by: Matthew Wilcox (Oracle) <willy@infradead.org>
+Cc: SeongJae Park <sj@kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Stable-dep-of: 24526268f4e3 ("mm: mempolicy: keep VMA walk if both MPOL_MF_STRICT and MPOL_MF_MOVE are specified")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/linux/mm.h | 2 ++
+ mm/memory.c | 10 ++++++++++
+ 2 files changed, 12 insertions(+)
+
+diff --git a/include/linux/mm.h b/include/linux/mm.h
+index 104ec00823da8..eefb0948110ae 100644
+--- a/include/linux/mm.h
++++ b/include/linux/mm.h
+@@ -1906,6 +1906,8 @@ static inline bool can_do_mlock(void) { return false; }
+ extern int user_shm_lock(size_t, struct ucounts *);
+ extern void user_shm_unlock(size_t, struct ucounts *);
+
++struct folio *vm_normal_folio(struct vm_area_struct *vma, unsigned long addr,
++ pte_t pte);
+ struct page *vm_normal_page(struct vm_area_struct *vma, unsigned long addr,
+ pte_t pte);
+ struct page *vm_normal_page_pmd(struct vm_area_struct *vma, unsigned long addr,
+diff --git a/mm/memory.c b/mm/memory.c
+index 2083078cd0615..0d1b3ee8fcd7a 100644
+--- a/mm/memory.c
++++ b/mm/memory.c
+@@ -672,6 +672,16 @@ struct page *vm_normal_page(struct vm_area_struct *vma, unsigned long addr,
+ return pfn_to_page(pfn);
+ }
+
++struct folio *vm_normal_folio(struct vm_area_struct *vma, unsigned long addr,
++ pte_t pte)
++{
++ struct page *page = vm_normal_page(vma, addr, pte);
++
++ if (page)
++ return page_folio(page);
++ return NULL;
++}
++
+ #ifdef CONFIG_TRANSPARENT_HUGEPAGE
+ struct page *vm_normal_page_pmd(struct vm_area_struct *vma, unsigned long addr,
+ pmd_t pmd)
+--
+2.40.1
+
--- /dev/null
+From 8af17b9c7c656fa77bc58151f3bdf3a5b7283303 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 30 Jan 2023 12:18:33 -0800
+Subject: mm/mempolicy: convert migrate_page_add() to migrate_folio_add()
+
+From: Vishal Moola (Oracle) <vishal.moola@gmail.com>
+
+[ Upstream commit 4a64981dfee9119aa2c1f243b48f34cbbd67779c ]
+
+Replace migrate_page_add() with migrate_folio_add(). migrate_folio_add()
+does the same a migrate_page_add() but takes in a folio instead of a page.
+This removes a couple of calls to compound_head().
+
+Link: https://lkml.kernel.org/r/20230130201833.27042-7-vishal.moola@gmail.com
+Signed-off-by: Vishal Moola (Oracle) <vishal.moola@gmail.com>
+Reviewed-by: Yin Fengwei <fengwei.yin@intel.com>
+Cc: David Hildenbrand <david@redhat.com>
+Cc: Jane Chu <jane.chu@oracle.com>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Stable-dep-of: 24526268f4e3 ("mm: mempolicy: keep VMA walk if both MPOL_MF_STRICT and MPOL_MF_MOVE are specified")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ mm/mempolicy.c | 39 ++++++++++++++++++++-------------------
+ 1 file changed, 20 insertions(+), 19 deletions(-)
+
+diff --git a/mm/mempolicy.c b/mm/mempolicy.c
+index 2ae6c8f18aba1..158b0bcd12fd7 100644
+--- a/mm/mempolicy.c
++++ b/mm/mempolicy.c
+@@ -414,7 +414,7 @@ static const struct mempolicy_operations mpol_ops[MPOL_MAX] = {
+ },
+ };
+
+-static int migrate_page_add(struct page *page, struct list_head *pagelist,
++static int migrate_folio_add(struct folio *folio, struct list_head *foliolist,
+ unsigned long flags);
+
+ struct queue_pages {
+@@ -476,7 +476,7 @@ static int queue_folios_pmd(pmd_t *pmd, spinlock_t *ptl, unsigned long addr,
+ /* go to folio migration */
+ if (flags & (MPOL_MF_MOVE | MPOL_MF_MOVE_ALL)) {
+ if (!vma_migratable(walk->vma) ||
+- migrate_page_add(&folio->page, qp->pagelist, flags)) {
++ migrate_folio_add(folio, qp->pagelist, flags)) {
+ ret = 1;
+ goto unlock;
+ }
+@@ -544,7 +544,7 @@ static int queue_folios_pte_range(pmd_t *pmd, unsigned long addr,
+ * temporary off LRU pages in the range. Still
+ * need migrate other LRU pages.
+ */
+- if (migrate_page_add(&folio->page, qp->pagelist, flags))
++ if (migrate_folio_add(folio, qp->pagelist, flags))
+ has_unmovable = true;
+ } else
+ break;
+@@ -1012,27 +1012,28 @@ static long do_get_mempolicy(int *policy, nodemask_t *nmask,
+ }
+
+ #ifdef CONFIG_MIGRATION
+-/*
+- * page migration, thp tail pages can be passed.
+- */
+-static int migrate_page_add(struct page *page, struct list_head *pagelist,
++static int migrate_folio_add(struct folio *folio, struct list_head *foliolist,
+ unsigned long flags)
+ {
+- struct page *head = compound_head(page);
+ /*
+- * Avoid migrating a page that is shared with others.
++ * We try to migrate only unshared folios. If it is shared it
++ * is likely not worth migrating.
++ *
++ * To check if the folio is shared, ideally we want to make sure
++ * every page is mapped to the same process. Doing that is very
++ * expensive, so check the estimated mapcount of the folio instead.
+ */
+- if ((flags & MPOL_MF_MOVE_ALL) || page_mapcount(head) == 1) {
+- if (!isolate_lru_page(head)) {
+- list_add_tail(&head->lru, pagelist);
+- mod_node_page_state(page_pgdat(head),
+- NR_ISOLATED_ANON + page_is_file_lru(head),
+- thp_nr_pages(head));
++ if ((flags & MPOL_MF_MOVE_ALL) || folio_estimated_sharers(folio) == 1) {
++ if (!folio_isolate_lru(folio)) {
++ list_add_tail(&folio->lru, foliolist);
++ node_stat_mod_folio(folio,
++ NR_ISOLATED_ANON + folio_is_file_lru(folio),
++ folio_nr_pages(folio));
+ } else if (flags & MPOL_MF_STRICT) {
+ /*
+- * Non-movable page may reach here. And, there may be
+- * temporary off LRU pages or non-LRU movable pages.
+- * Treat them as unmovable pages since they can't be
++ * Non-movable folio may reach here. And, there may be
++ * temporary off LRU folios or non-LRU movable folios.
++ * Treat them as unmovable folios since they can't be
+ * isolated, so they can't be moved at the moment. It
+ * should return -EIO for this case too.
+ */
+@@ -1224,7 +1225,7 @@ static struct page *new_page(struct page *page, unsigned long start)
+ }
+ #else
+
+-static int migrate_page_add(struct page *page, struct list_head *pagelist,
++static int migrate_folio_add(struct folio *folio, struct list_head *foliolist,
+ unsigned long flags)
+ {
+ return -EIO;
+--
+2.40.1
+
--- /dev/null
+From 299e39e2131d8ec58eec46daaa826d503335bada Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 30 Jan 2023 12:18:29 -0800
+Subject: mm/mempolicy: convert queue_pages_pmd() to queue_folios_pmd()
+
+From: Vishal Moola (Oracle) <vishal.moola@gmail.com>
+
+[ Upstream commit de1f5055523e9a035b38533f25a56df03d45034a ]
+
+The function now operates on a folio instead of the page associated with a
+pmd.
+
+This change is in preparation for the conversion of queue_pages_required()
+to queue_folio_required() and migrate_page_add() to migrate_folio_add().
+
+Link: https://lkml.kernel.org/r/20230130201833.27042-3-vishal.moola@gmail.com
+Signed-off-by: Vishal Moola (Oracle) <vishal.moola@gmail.com>
+Cc: David Hildenbrand <david@redhat.com>
+Cc: Jane Chu <jane.chu@oracle.com>
+Cc: "Yin, Fengwei" <fengwei.yin@intel.com>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Stable-dep-of: 24526268f4e3 ("mm: mempolicy: keep VMA walk if both MPOL_MF_STRICT and MPOL_MF_MOVE are specified")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ mm/mempolicy.c | 24 ++++++++++++------------
+ 1 file changed, 12 insertions(+), 12 deletions(-)
+
+diff --git a/mm/mempolicy.c b/mm/mempolicy.c
+index 7d36dd95d1fff..3a291026e1896 100644
+--- a/mm/mempolicy.c
++++ b/mm/mempolicy.c
+@@ -442,21 +442,21 @@ static inline bool queue_pages_required(struct page *page,
+ }
+
+ /*
+- * queue_pages_pmd() has three possible return values:
+- * 0 - pages are placed on the right node or queued successfully, or
++ * queue_folios_pmd() has three possible return values:
++ * 0 - folios are placed on the right node or queued successfully, or
+ * special page is met, i.e. huge zero page.
+- * 1 - there is unmovable page, and MPOL_MF_MOVE* & MPOL_MF_STRICT were
++ * 1 - there is unmovable folio, and MPOL_MF_MOVE* & MPOL_MF_STRICT were
+ * specified.
+ * -EIO - is migration entry or only MPOL_MF_STRICT was specified and an
+- * existing page was already on a node that does not follow the
++ * existing folio was already on a node that does not follow the
+ * policy.
+ */
+-static int queue_pages_pmd(pmd_t *pmd, spinlock_t *ptl, unsigned long addr,
++static int queue_folios_pmd(pmd_t *pmd, spinlock_t *ptl, unsigned long addr,
+ unsigned long end, struct mm_walk *walk)
+ __releases(ptl)
+ {
+ int ret = 0;
+- struct page *page;
++ struct folio *folio;
+ struct queue_pages *qp = walk->private;
+ unsigned long flags;
+
+@@ -464,19 +464,19 @@ static int queue_pages_pmd(pmd_t *pmd, spinlock_t *ptl, unsigned long addr,
+ ret = -EIO;
+ goto unlock;
+ }
+- page = pmd_page(*pmd);
+- if (is_huge_zero_page(page)) {
++ folio = pfn_folio(pmd_pfn(*pmd));
++ if (is_huge_zero_page(&folio->page)) {
+ walk->action = ACTION_CONTINUE;
+ goto unlock;
+ }
+- if (!queue_pages_required(page, qp))
++ if (!queue_pages_required(&folio->page, qp))
+ goto unlock;
+
+ flags = qp->flags;
+- /* go to thp migration */
++ /* go to folio migration */
+ if (flags & (MPOL_MF_MOVE | MPOL_MF_MOVE_ALL)) {
+ if (!vma_migratable(walk->vma) ||
+- migrate_page_add(page, qp->pagelist, flags)) {
++ migrate_page_add(&folio->page, qp->pagelist, flags)) {
+ ret = 1;
+ goto unlock;
+ }
+@@ -512,7 +512,7 @@ static int queue_pages_pte_range(pmd_t *pmd, unsigned long addr,
+
+ ptl = pmd_trans_huge_lock(pmd, vma);
+ if (ptl)
+- return queue_pages_pmd(pmd, ptl, addr, end, walk);
++ return queue_folios_pmd(pmd, ptl, addr, end, walk);
+
+ if (pmd_trans_unstable(pmd))
+ return 0;
+--
+2.40.1
+
--- /dev/null
+From 35c8cbe38cb84466882c63f46dd0129bdba26456 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 30 Jan 2023 12:18:30 -0800
+Subject: mm/mempolicy: convert queue_pages_pte_range() to
+ queue_folios_pte_range()
+
+From: Vishal Moola (Oracle) <vishal.moola@gmail.com>
+
+[ Upstream commit 3dae02bbd07f40e37bbfec2d77119628db461eaa ]
+
+This function now operates on folios associated with ptes instead of
+pages.
+
+This change is in preparation for the conversion of queue_pages_required()
+to queue_folio_required() and migrate_page_add() to migrate_folio_add().
+
+Link: https://lkml.kernel.org/r/20230130201833.27042-4-vishal.moola@gmail.com
+Signed-off-by: Vishal Moola (Oracle) <vishal.moola@gmail.com>
+Cc: David Hildenbrand <david@redhat.com>
+Cc: Jane Chu <jane.chu@oracle.com>
+Cc: "Yin, Fengwei" <fengwei.yin@intel.com>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Stable-dep-of: 24526268f4e3 ("mm: mempolicy: keep VMA walk if both MPOL_MF_STRICT and MPOL_MF_MOVE are specified")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ mm/mempolicy.c | 28 ++++++++++++++--------------
+ 1 file changed, 14 insertions(+), 14 deletions(-)
+
+diff --git a/mm/mempolicy.c b/mm/mempolicy.c
+index 3a291026e1896..2ae6c8f18aba1 100644
+--- a/mm/mempolicy.c
++++ b/mm/mempolicy.c
+@@ -491,19 +491,19 @@ static int queue_folios_pmd(pmd_t *pmd, spinlock_t *ptl, unsigned long addr,
+ * Scan through pages checking if pages follow certain conditions,
+ * and move them to the pagelist if they do.
+ *
+- * queue_pages_pte_range() has three possible return values:
+- * 0 - pages are placed on the right node or queued successfully, or
++ * queue_folios_pte_range() has three possible return values:
++ * 0 - folios are placed on the right node or queued successfully, or
+ * special page is met, i.e. zero page.
+- * 1 - there is unmovable page, and MPOL_MF_MOVE* & MPOL_MF_STRICT were
++ * 1 - there is unmovable folio, and MPOL_MF_MOVE* & MPOL_MF_STRICT were
+ * specified.
+- * -EIO - only MPOL_MF_STRICT was specified and an existing page was already
++ * -EIO - only MPOL_MF_STRICT was specified and an existing folio was already
+ * on a node that does not follow the policy.
+ */
+-static int queue_pages_pte_range(pmd_t *pmd, unsigned long addr,
++static int queue_folios_pte_range(pmd_t *pmd, unsigned long addr,
+ unsigned long end, struct mm_walk *walk)
+ {
+ struct vm_area_struct *vma = walk->vma;
+- struct page *page;
++ struct folio *folio;
+ struct queue_pages *qp = walk->private;
+ unsigned long flags = qp->flags;
+ bool has_unmovable = false;
+@@ -521,16 +521,16 @@ static int queue_pages_pte_range(pmd_t *pmd, unsigned long addr,
+ for (; addr != end; pte++, addr += PAGE_SIZE) {
+ if (!pte_present(*pte))
+ continue;
+- page = vm_normal_page(vma, addr, *pte);
+- if (!page || is_zone_device_page(page))
++ folio = vm_normal_folio(vma, addr, *pte);
++ if (!folio || folio_is_zone_device(folio))
+ continue;
+ /*
+- * vm_normal_page() filters out zero pages, but there might
+- * still be PageReserved pages to skip, perhaps in a VDSO.
++ * vm_normal_folio() filters out zero pages, but there might
++ * still be reserved folios to skip, perhaps in a VDSO.
+ */
+- if (PageReserved(page))
++ if (folio_test_reserved(folio))
+ continue;
+- if (!queue_pages_required(page, qp))
++ if (!queue_pages_required(&folio->page, qp))
+ continue;
+ if (flags & (MPOL_MF_MOVE | MPOL_MF_MOVE_ALL)) {
+ /* MPOL_MF_STRICT must be specified if we get here */
+@@ -544,7 +544,7 @@ static int queue_pages_pte_range(pmd_t *pmd, unsigned long addr,
+ * temporary off LRU pages in the range. Still
+ * need migrate other LRU pages.
+ */
+- if (migrate_page_add(page, qp->pagelist, flags))
++ if (migrate_page_add(&folio->page, qp->pagelist, flags))
+ has_unmovable = true;
+ } else
+ break;
+@@ -705,7 +705,7 @@ static int queue_pages_test_walk(unsigned long start, unsigned long end,
+
+ static const struct mm_walk_ops queue_pages_walk_ops = {
+ .hugetlb_entry = queue_pages_hugetlb,
+- .pmd_entry = queue_pages_pte_range,
++ .pmd_entry = queue_folios_pte_range,
+ .test_walk = queue_pages_test_walk,
+ };
+
+--
+2.40.1
+
--- /dev/null
+From 03117032d2162841bbbe9c4bafd56777cab40033 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 20 Sep 2023 15:32:42 -0700
+Subject: mm: mempolicy: keep VMA walk if both MPOL_MF_STRICT and MPOL_MF_MOVE
+ are specified
+
+From: Yang Shi <yang@os.amperecomputing.com>
+
+[ Upstream commit 24526268f4e38c9ec0c4a30de4f37ad2a2a84e47 ]
+
+When calling mbind() with MPOL_MF_{MOVE|MOVEALL} | MPOL_MF_STRICT, kernel
+should attempt to migrate all existing pages, and return -EIO if there is
+misplaced or unmovable page. Then commit 6f4576e3687b ("mempolicy: apply
+page table walker on queue_pages_range()") messed up the return value and
+didn't break VMA scan early ianymore when MPOL_MF_STRICT alone. The
+return value problem was fixed by commit a7f40cfe3b7a ("mm: mempolicy:
+make mbind() return -EIO when MPOL_MF_STRICT is specified"), but it broke
+the VMA walk early if unmovable page is met, it may cause some pages are
+not migrated as expected.
+
+The code should conceptually do:
+
+ if (MPOL_MF_MOVE|MOVEALL)
+ scan all vmas
+ try to migrate the existing pages
+ return success
+ else if (MPOL_MF_MOVE* | MPOL_MF_STRICT)
+ scan all vmas
+ try to migrate the existing pages
+ return -EIO if unmovable or migration failed
+ else /* MPOL_MF_STRICT alone */
+ break early if meets unmovable and don't call mbind_range() at all
+ else /* none of those flags */
+ check the ranges in test_walk, EFAULT without mbind_range() if discontig.
+
+Fixed the behavior.
+
+Link: https://lkml.kernel.org/r/20230920223242.3425775-1-yang@os.amperecomputing.com
+Fixes: a7f40cfe3b7a ("mm: mempolicy: make mbind() return -EIO when MPOL_MF_STRICT is specified")
+Signed-off-by: Yang Shi <yang@os.amperecomputing.com>
+Cc: Hugh Dickins <hughd@google.com>
+Cc: Suren Baghdasaryan <surenb@google.com>
+Cc: Matthew Wilcox <willy@infradead.org>
+Cc: Michal Hocko <mhocko@suse.com>
+Cc: Vlastimil Babka <vbabka@suse.cz>
+Cc: Oscar Salvador <osalvador@suse.de>
+Cc: Rafael Aquini <aquini@redhat.com>
+Cc: Kirill A. Shutemov <kirill@shutemov.name>
+Cc: David Rientjes <rientjes@google.com>
+Cc: <stable@vger.kernel.org> [4.9+]
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ mm/mempolicy.c | 39 +++++++++++++++++++--------------------
+ 1 file changed, 19 insertions(+), 20 deletions(-)
+
+diff --git a/mm/mempolicy.c b/mm/mempolicy.c
+index 158b0bcd12fd7..bfe2d1d50fbee 100644
+--- a/mm/mempolicy.c
++++ b/mm/mempolicy.c
+@@ -424,6 +424,7 @@ struct queue_pages {
+ unsigned long start;
+ unsigned long end;
+ struct vm_area_struct *first;
++ bool has_unmovable;
+ };
+
+ /*
+@@ -444,9 +445,8 @@ static inline bool queue_pages_required(struct page *page,
+ /*
+ * queue_folios_pmd() has three possible return values:
+ * 0 - folios are placed on the right node or queued successfully, or
+- * special page is met, i.e. huge zero page.
+- * 1 - there is unmovable folio, and MPOL_MF_MOVE* & MPOL_MF_STRICT were
+- * specified.
++ * special page is met, i.e. zero page, or unmovable page is found
++ * but continue walking (indicated by queue_pages.has_unmovable).
+ * -EIO - is migration entry or only MPOL_MF_STRICT was specified and an
+ * existing folio was already on a node that does not follow the
+ * policy.
+@@ -477,7 +477,7 @@ static int queue_folios_pmd(pmd_t *pmd, spinlock_t *ptl, unsigned long addr,
+ if (flags & (MPOL_MF_MOVE | MPOL_MF_MOVE_ALL)) {
+ if (!vma_migratable(walk->vma) ||
+ migrate_folio_add(folio, qp->pagelist, flags)) {
+- ret = 1;
++ qp->has_unmovable = true;
+ goto unlock;
+ }
+ } else
+@@ -493,9 +493,8 @@ static int queue_folios_pmd(pmd_t *pmd, spinlock_t *ptl, unsigned long addr,
+ *
+ * queue_folios_pte_range() has three possible return values:
+ * 0 - folios are placed on the right node or queued successfully, or
+- * special page is met, i.e. zero page.
+- * 1 - there is unmovable folio, and MPOL_MF_MOVE* & MPOL_MF_STRICT were
+- * specified.
++ * special page is met, i.e. zero page, or unmovable page is found
++ * but continue walking (indicated by queue_pages.has_unmovable).
+ * -EIO - only MPOL_MF_STRICT was specified and an existing folio was already
+ * on a node that does not follow the policy.
+ */
+@@ -506,7 +505,6 @@ static int queue_folios_pte_range(pmd_t *pmd, unsigned long addr,
+ struct folio *folio;
+ struct queue_pages *qp = walk->private;
+ unsigned long flags = qp->flags;
+- bool has_unmovable = false;
+ pte_t *pte, *mapped_pte;
+ spinlock_t *ptl;
+
+@@ -533,11 +531,12 @@ static int queue_folios_pte_range(pmd_t *pmd, unsigned long addr,
+ if (!queue_pages_required(&folio->page, qp))
+ continue;
+ if (flags & (MPOL_MF_MOVE | MPOL_MF_MOVE_ALL)) {
+- /* MPOL_MF_STRICT must be specified if we get here */
+- if (!vma_migratable(vma)) {
+- has_unmovable = true;
+- break;
+- }
++ /*
++ * MPOL_MF_STRICT must be specified if we get here.
++ * Continue walking vmas due to MPOL_MF_MOVE* flags.
++ */
++ if (!vma_migratable(vma))
++ qp->has_unmovable = true;
+
+ /*
+ * Do not abort immediately since there may be
+@@ -545,16 +544,13 @@ static int queue_folios_pte_range(pmd_t *pmd, unsigned long addr,
+ * need migrate other LRU pages.
+ */
+ if (migrate_folio_add(folio, qp->pagelist, flags))
+- has_unmovable = true;
++ qp->has_unmovable = true;
+ } else
+ break;
+ }
+ pte_unmap_unlock(mapped_pte, ptl);
+ cond_resched();
+
+- if (has_unmovable)
+- return 1;
+-
+ return addr != end ? -EIO : 0;
+ }
+
+@@ -594,7 +590,7 @@ static int queue_pages_hugetlb(pte_t *pte, unsigned long hmask,
+ * Detecting misplaced page but allow migrating pages which
+ * have been queued.
+ */
+- ret = 1;
++ qp->has_unmovable = true;
+ goto unlock;
+ }
+
+@@ -608,7 +604,7 @@ static int queue_pages_hugetlb(pte_t *pte, unsigned long hmask,
+ * Failed to isolate page but allow migrating pages
+ * which have been queued.
+ */
+- ret = 1;
++ qp->has_unmovable = true;
+ }
+ unlock:
+ spin_unlock(ptl);
+@@ -737,10 +733,13 @@ queue_pages_range(struct mm_struct *mm, unsigned long start, unsigned long end,
+ .start = start,
+ .end = end,
+ .first = NULL,
++ .has_unmovable = false,
+ };
+
+ err = walk_page_range(mm, start, end, &queue_pages_walk_ops, &qp);
+
++ if (qp.has_unmovable)
++ err = 1;
+ if (!qp.first)
+ /* whole range in hole */
+ err = -EFAULT;
+@@ -1338,7 +1337,7 @@ static long do_mbind(unsigned long start, unsigned long len,
+ putback_movable_pages(&pagelist);
+ }
+
+- if ((ret > 0) || (nr_failed && (flags & MPOL_MF_STRICT)))
++ if (((ret > 0) || nr_failed) && (flags & MPOL_MF_STRICT))
+ err = -EIO;
+ } else {
+ up_out:
+--
+2.40.1
+
--- /dev/null
+From 9d8e793d000a5f5b70e270f999cc52c2dd78b5cd Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 18 Nov 2022 10:17:13 +0000
+Subject: mm/page_alloc: always remove pages from temporary list
+
+From: Mel Gorman <mgorman@techsingularity.net>
+
+[ Upstream commit c3e58a70425ac6ddaae1529c8146e88b4f7252bb ]
+
+Patch series "Leave IRQs enabled for per-cpu page allocations", v3.
+
+This patch (of 2):
+
+free_unref_page_list() has neglected to remove pages properly from the
+list of pages to free since forever. It works by coincidence because
+list_add happened to do the right thing adding the pages to just the PCP
+lists. However, a later patch added pages to either the PCP list or the
+zone list but only properly deleted the page from the list in one path
+leading to list corruption and a subsequent failure. As a preparation
+patch, always delete the pages from one list properly before adding to
+another. On its own, this fixes nothing although it adds a fractional
+amount of overhead but is critical to the next patch.
+
+Link: https://lkml.kernel.org/r/20221118101714.19590-1-mgorman@techsingularity.net
+Link: https://lkml.kernel.org/r/20221118101714.19590-2-mgorman@techsingularity.net
+Signed-off-by: Mel Gorman <mgorman@techsingularity.net>
+Reported-by: Hugh Dickins <hughd@google.com>
+Reviewed-by: Vlastimil Babka <vbabka@suse.cz>
+Cc: Marcelo Tosatti <mtosatti@redhat.com>
+Cc: Marek Szyprowski <m.szyprowski@samsung.com>
+Cc: Michal Hocko <mhocko@kernel.org>
+Cc: Yu Zhao <yuzhao@google.com>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Stable-dep-of: 7b086755fb8c ("mm: page_alloc: fix CMA and HIGHATOMIC landing on the wrong buddy list")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ mm/page_alloc.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/mm/page_alloc.c b/mm/page_alloc.c
+index 69668817fed37..d94ac6d87bc97 100644
+--- a/mm/page_alloc.c
++++ b/mm/page_alloc.c
+@@ -3547,6 +3547,8 @@ void free_unref_page_list(struct list_head *list)
+ list_for_each_entry_safe(page, next, list, lru) {
+ struct zone *zone = page_zone(page);
+
++ list_del(&page->lru);
++
+ /* Different zone, different pcp lock. */
+ if (zone != locked_zone) {
+ if (pcp)
+--
+2.40.1
+
--- /dev/null
+From 65647c5732d410e694b93da2cffed9f76a5e599b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 Sep 2023 14:11:08 -0400
+Subject: mm: page_alloc: fix CMA and HIGHATOMIC landing on the wrong buddy
+ list
+
+From: Johannes Weiner <hannes@cmpxchg.org>
+
+[ Upstream commit 7b086755fb8cdbb6b3e45a1bbddc00e7f9b1dc03 ]
+
+Commit 4b23a68f9536 ("mm/page_alloc: protect PCP lists with a spinlock")
+bypasses the pcplist on lock contention and returns the page directly to
+the buddy list of the page's migratetype.
+
+For pages that don't have their own pcplist, such as CMA and HIGHATOMIC,
+the migratetype is temporarily updated such that the page can hitch a ride
+on the MOVABLE pcplist. Their true type is later reassessed when flushing
+in free_pcppages_bulk(). However, when lock contention is detected after
+the type was already overridden, the bypass will then put the page on the
+wrong buddy list.
+
+Once on the MOVABLE buddy list, the page becomes eligible for fallbacks
+and even stealing. In the case of HIGHATOMIC, otherwise ineligible
+allocations can dip into the highatomic reserves. In the case of CMA, the
+page can be lost from the CMA region permanently.
+
+Use a separate pcpmigratetype variable for the pcplist override. Use the
+original migratetype when going directly to the buddy. This fixes the bug
+and should make the intentions more obvious in the code.
+
+Originally sent here to address the HIGHATOMIC case:
+https://lore.kernel.org/lkml/20230821183733.106619-4-hannes@cmpxchg.org/
+
+Changelog updated in response to the CMA-specific bug report.
+
+[mgorman@techsingularity.net: updated changelog]
+Link: https://lkml.kernel.org/r/20230911181108.GA104295@cmpxchg.org
+Fixes: 4b23a68f9536 ("mm/page_alloc: protect PCP lists with a spinlock")
+Signed-off-by: Johannes Weiner <hannes@cmpxchg.org>
+Reported-by: Joe Liu <joe.liu@mediatek.com>
+Reviewed-by: Vlastimil Babka <vbabka@suse.cz>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ mm/page_alloc.c | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+diff --git a/mm/page_alloc.c b/mm/page_alloc.c
+index 90082f75660f2..ca017c6008b7c 100644
+--- a/mm/page_alloc.c
++++ b/mm/page_alloc.c
+@@ -3448,7 +3448,7 @@ void free_unref_page(struct page *page, unsigned int order)
+ struct per_cpu_pages *pcp;
+ struct zone *zone;
+ unsigned long pfn = page_to_pfn(page);
+- int migratetype;
++ int migratetype, pcpmigratetype;
+
+ if (!free_unref_page_prepare(page, pfn, order))
+ return;
+@@ -3456,24 +3456,24 @@ void free_unref_page(struct page *page, unsigned int order)
+ /*
+ * We only track unmovable, reclaimable and movable on pcp lists.
+ * Place ISOLATE pages on the isolated list because they are being
+- * offlined but treat HIGHATOMIC as movable pages so we can get those
+- * areas back if necessary. Otherwise, we may have to free
++ * offlined but treat HIGHATOMIC and CMA as movable pages so we can
++ * get those areas back if necessary. Otherwise, we may have to free
+ * excessively into the page allocator
+ */
+- migratetype = get_pcppage_migratetype(page);
++ migratetype = pcpmigratetype = get_pcppage_migratetype(page);
+ if (unlikely(migratetype >= MIGRATE_PCPTYPES)) {
+ if (unlikely(is_migrate_isolate(migratetype))) {
+ free_one_page(page_zone(page), page, pfn, order, migratetype, FPI_NONE);
+ return;
+ }
+- migratetype = MIGRATE_MOVABLE;
++ pcpmigratetype = MIGRATE_MOVABLE;
+ }
+
+ zone = page_zone(page);
+ pcp_trylock_prepare(UP_flags);
+ pcp = pcp_spin_trylock(zone->per_cpu_pageset);
+ if (pcp) {
+- free_unref_page_commit(zone, pcp, page, migratetype, order);
++ free_unref_page_commit(zone, pcp, page, pcpmigratetype, order);
+ pcp_spin_unlock(pcp);
+ } else {
+ free_one_page(zone, page, pfn, order, migratetype, FPI_NONE);
+--
+2.40.1
+
--- /dev/null
+From a7473a18b6b2c4fd29dbf3399db70ba026294e3f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 18 Nov 2022 10:17:14 +0000
+Subject: mm/page_alloc: leave IRQs enabled for per-cpu page allocations
+
+From: Mel Gorman <mgorman@techsingularity.net>
+
+[ Upstream commit 5749077415994eb02d660b2559b9d8278521e73d ]
+
+The pcp_spin_lock_irqsave protecting the PCP lists is IRQ-safe as a task
+allocating from the PCP must not re-enter the allocator from IRQ context.
+In each instance where IRQ-reentrancy is possible, the lock is acquired
+using pcp_spin_trylock_irqsave() even though IRQs are disabled and
+re-entrancy is impossible.
+
+Demote the lock to pcp_spin_lock avoids an IRQ disable/enable in the
+common case at the cost of some IRQ allocations taking a slower path. If
+the PCP lists need to be refilled, the zone lock still needs to disable
+IRQs but that will only happen on PCP refill and drain. If an IRQ is
+raised when a PCP allocation is in progress, the trylock will fail and
+fallback to using the buddy lists directly. Note that this may not be a
+universal win if an interrupt-intensive workload also allocates heavily
+from interrupt context and contends heavily on the zone->lock as a result.
+
+[mgorman@techsingularity.net: migratetype might be wrong if a PCP was locked]
+ Link: https://lkml.kernel.org/r/20221122131229.5263-2-mgorman@techsingularity.net
+[yuzhao@google.com: reported lockdep issue on IO completion from softirq]
+[hughd@google.com: fix list corruption, lock improvements, micro-optimsations]
+Link: https://lkml.kernel.org/r/20221118101714.19590-3-mgorman@techsingularity.net
+Signed-off-by: Mel Gorman <mgorman@techsingularity.net>
+Reviewed-by: Vlastimil Babka <vbabka@suse.cz>
+Cc: Marcelo Tosatti <mtosatti@redhat.com>
+Cc: Marek Szyprowski <m.szyprowski@samsung.com>
+Cc: Michal Hocko <mhocko@kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Stable-dep-of: 7b086755fb8c ("mm: page_alloc: fix CMA and HIGHATOMIC landing on the wrong buddy list")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ mm/page_alloc.c | 124 +++++++++++++++++++++---------------------------
+ 1 file changed, 54 insertions(+), 70 deletions(-)
+
+diff --git a/mm/page_alloc.c b/mm/page_alloc.c
+index d94ac6d87bc97..90082f75660f2 100644
+--- a/mm/page_alloc.c
++++ b/mm/page_alloc.c
+@@ -170,21 +170,12 @@ static DEFINE_MUTEX(pcp_batch_high_lock);
+ _ret; \
+ })
+
+-#define pcpu_spin_lock_irqsave(type, member, ptr, flags) \
++#define pcpu_spin_trylock(type, member, ptr) \
+ ({ \
+ type *_ret; \
+ pcpu_task_pin(); \
+ _ret = this_cpu_ptr(ptr); \
+- spin_lock_irqsave(&_ret->member, flags); \
+- _ret; \
+-})
+-
+-#define pcpu_spin_trylock_irqsave(type, member, ptr, flags) \
+-({ \
+- type *_ret; \
+- pcpu_task_pin(); \
+- _ret = this_cpu_ptr(ptr); \
+- if (!spin_trylock_irqsave(&_ret->member, flags)) { \
++ if (!spin_trylock(&_ret->member)) { \
+ pcpu_task_unpin(); \
+ _ret = NULL; \
+ } \
+@@ -197,27 +188,16 @@ static DEFINE_MUTEX(pcp_batch_high_lock);
+ pcpu_task_unpin(); \
+ })
+
+-#define pcpu_spin_unlock_irqrestore(member, ptr, flags) \
+-({ \
+- spin_unlock_irqrestore(&ptr->member, flags); \
+- pcpu_task_unpin(); \
+-})
+-
+ /* struct per_cpu_pages specific helpers. */
+ #define pcp_spin_lock(ptr) \
+ pcpu_spin_lock(struct per_cpu_pages, lock, ptr)
+
+-#define pcp_spin_lock_irqsave(ptr, flags) \
+- pcpu_spin_lock_irqsave(struct per_cpu_pages, lock, ptr, flags)
+-
+-#define pcp_spin_trylock_irqsave(ptr, flags) \
+- pcpu_spin_trylock_irqsave(struct per_cpu_pages, lock, ptr, flags)
++#define pcp_spin_trylock(ptr) \
++ pcpu_spin_trylock(struct per_cpu_pages, lock, ptr)
+
+ #define pcp_spin_unlock(ptr) \
+ pcpu_spin_unlock(lock, ptr)
+
+-#define pcp_spin_unlock_irqrestore(ptr, flags) \
+- pcpu_spin_unlock_irqrestore(lock, ptr, flags)
+ #ifdef CONFIG_USE_PERCPU_NUMA_NODE_ID
+ DEFINE_PER_CPU(int, numa_node);
+ EXPORT_PER_CPU_SYMBOL(numa_node);
+@@ -1548,6 +1528,7 @@ static void free_pcppages_bulk(struct zone *zone, int count,
+ struct per_cpu_pages *pcp,
+ int pindex)
+ {
++ unsigned long flags;
+ int min_pindex = 0;
+ int max_pindex = NR_PCP_LISTS - 1;
+ unsigned int order;
+@@ -1563,8 +1544,7 @@ static void free_pcppages_bulk(struct zone *zone, int count,
+ /* Ensure requested pindex is drained first. */
+ pindex = pindex - 1;
+
+- /* Caller must hold IRQ-safe pcp->lock so IRQs are disabled. */
+- spin_lock(&zone->lock);
++ spin_lock_irqsave(&zone->lock, flags);
+ isolated_pageblocks = has_isolate_pageblock(zone);
+
+ while (count > 0) {
+@@ -1612,7 +1592,7 @@ static void free_pcppages_bulk(struct zone *zone, int count,
+ } while (count > 0 && !list_empty(list));
+ }
+
+- spin_unlock(&zone->lock);
++ spin_unlock_irqrestore(&zone->lock, flags);
+ }
+
+ static void free_one_page(struct zone *zone,
+@@ -3126,10 +3106,10 @@ static int rmqueue_bulk(struct zone *zone, unsigned int order,
+ unsigned long count, struct list_head *list,
+ int migratetype, unsigned int alloc_flags)
+ {
++ unsigned long flags;
+ int i, allocated = 0;
+
+- /* Caller must hold IRQ-safe pcp->lock so IRQs are disabled. */
+- spin_lock(&zone->lock);
++ spin_lock_irqsave(&zone->lock, flags);
+ for (i = 0; i < count; ++i) {
+ struct page *page = __rmqueue(zone, order, migratetype,
+ alloc_flags);
+@@ -3163,7 +3143,7 @@ static int rmqueue_bulk(struct zone *zone, unsigned int order,
+ * pages added to the pcp list.
+ */
+ __mod_zone_page_state(zone, NR_FREE_PAGES, -(i << order));
+- spin_unlock(&zone->lock);
++ spin_unlock_irqrestore(&zone->lock, flags);
+ return allocated;
+ }
+
+@@ -3180,16 +3160,9 @@ void drain_zone_pages(struct zone *zone, struct per_cpu_pages *pcp)
+ batch = READ_ONCE(pcp->batch);
+ to_drain = min(pcp->count, batch);
+ if (to_drain > 0) {
+- unsigned long flags;
+-
+- /*
+- * free_pcppages_bulk expects IRQs disabled for zone->lock
+- * so even though pcp->lock is not intended to be IRQ-safe,
+- * it's needed in this context.
+- */
+- spin_lock_irqsave(&pcp->lock, flags);
++ spin_lock(&pcp->lock);
+ free_pcppages_bulk(zone, to_drain, pcp, 0);
+- spin_unlock_irqrestore(&pcp->lock, flags);
++ spin_unlock(&pcp->lock);
+ }
+ }
+ #endif
+@@ -3203,12 +3176,9 @@ static void drain_pages_zone(unsigned int cpu, struct zone *zone)
+
+ pcp = per_cpu_ptr(zone->per_cpu_pageset, cpu);
+ if (pcp->count) {
+- unsigned long flags;
+-
+- /* See drain_zone_pages on why this is disabling IRQs */
+- spin_lock_irqsave(&pcp->lock, flags);
++ spin_lock(&pcp->lock);
+ free_pcppages_bulk(zone, pcp->count, pcp, 0);
+- spin_unlock_irqrestore(&pcp->lock, flags);
++ spin_unlock(&pcp->lock);
+ }
+ }
+
+@@ -3474,7 +3444,6 @@ static void free_unref_page_commit(struct zone *zone, struct per_cpu_pages *pcp,
+ */
+ void free_unref_page(struct page *page, unsigned int order)
+ {
+- unsigned long flags;
+ unsigned long __maybe_unused UP_flags;
+ struct per_cpu_pages *pcp;
+ struct zone *zone;
+@@ -3502,10 +3471,10 @@ void free_unref_page(struct page *page, unsigned int order)
+
+ zone = page_zone(page);
+ pcp_trylock_prepare(UP_flags);
+- pcp = pcp_spin_trylock_irqsave(zone->per_cpu_pageset, flags);
++ pcp = pcp_spin_trylock(zone->per_cpu_pageset);
+ if (pcp) {
+ free_unref_page_commit(zone, pcp, page, migratetype, order);
+- pcp_spin_unlock_irqrestore(pcp, flags);
++ pcp_spin_unlock(pcp);
+ } else {
+ free_one_page(zone, page, pfn, order, migratetype, FPI_NONE);
+ }
+@@ -3517,10 +3486,10 @@ void free_unref_page(struct page *page, unsigned int order)
+ */
+ void free_unref_page_list(struct list_head *list)
+ {
++ unsigned long __maybe_unused UP_flags;
+ struct page *page, *next;
+ struct per_cpu_pages *pcp = NULL;
+ struct zone *locked_zone = NULL;
+- unsigned long flags;
+ int batch_count = 0;
+ int migratetype;
+
+@@ -3548,21 +3517,36 @@ void free_unref_page_list(struct list_head *list)
+ struct zone *zone = page_zone(page);
+
+ list_del(&page->lru);
++ migratetype = get_pcppage_migratetype(page);
+
+ /* Different zone, different pcp lock. */
+ if (zone != locked_zone) {
+- if (pcp)
+- pcp_spin_unlock_irqrestore(pcp, flags);
++ if (pcp) {
++ pcp_spin_unlock(pcp);
++ pcp_trylock_finish(UP_flags);
++ }
+
++ /*
++ * trylock is necessary as pages may be getting freed
++ * from IRQ or SoftIRQ context after an IO completion.
++ */
++ pcp_trylock_prepare(UP_flags);
++ pcp = pcp_spin_trylock(zone->per_cpu_pageset);
++ if (unlikely(!pcp)) {
++ pcp_trylock_finish(UP_flags);
++ free_one_page(zone, page, page_to_pfn(page),
++ 0, migratetype, FPI_NONE);
++ locked_zone = NULL;
++ continue;
++ }
+ locked_zone = zone;
+- pcp = pcp_spin_lock_irqsave(locked_zone->per_cpu_pageset, flags);
++ batch_count = 0;
+ }
+
+ /*
+ * Non-isolated types over MIGRATE_PCPTYPES get added
+ * to the MIGRATE_MOVABLE pcp list.
+ */
+- migratetype = get_pcppage_migratetype(page);
+ if (unlikely(migratetype >= MIGRATE_PCPTYPES))
+ migratetype = MIGRATE_MOVABLE;
+
+@@ -3570,18 +3554,23 @@ void free_unref_page_list(struct list_head *list)
+ free_unref_page_commit(zone, pcp, page, migratetype, 0);
+
+ /*
+- * Guard against excessive IRQ disabled times when we get
+- * a large list of pages to free.
++ * Guard against excessive lock hold times when freeing
++ * a large list of pages. Lock will be reacquired if
++ * necessary on the next iteration.
+ */
+ if (++batch_count == SWAP_CLUSTER_MAX) {
+- pcp_spin_unlock_irqrestore(pcp, flags);
++ pcp_spin_unlock(pcp);
++ pcp_trylock_finish(UP_flags);
+ batch_count = 0;
+- pcp = pcp_spin_lock_irqsave(locked_zone->per_cpu_pageset, flags);
++ pcp = NULL;
++ locked_zone = NULL;
+ }
+ }
+
+- if (pcp)
+- pcp_spin_unlock_irqrestore(pcp, flags);
++ if (pcp) {
++ pcp_spin_unlock(pcp);
++ pcp_trylock_finish(UP_flags);
++ }
+ }
+
+ /*
+@@ -3782,15 +3771,11 @@ static struct page *rmqueue_pcplist(struct zone *preferred_zone,
+ struct per_cpu_pages *pcp;
+ struct list_head *list;
+ struct page *page;
+- unsigned long flags;
+ unsigned long __maybe_unused UP_flags;
+
+- /*
+- * spin_trylock may fail due to a parallel drain. In the future, the
+- * trylock will also protect against IRQ reentrancy.
+- */
++ /* spin_trylock may fail due to a parallel drain or IRQ reentrancy. */
+ pcp_trylock_prepare(UP_flags);
+- pcp = pcp_spin_trylock_irqsave(zone->per_cpu_pageset, flags);
++ pcp = pcp_spin_trylock(zone->per_cpu_pageset);
+ if (!pcp) {
+ pcp_trylock_finish(UP_flags);
+ return NULL;
+@@ -3804,7 +3789,7 @@ static struct page *rmqueue_pcplist(struct zone *preferred_zone,
+ pcp->free_factor >>= 1;
+ list = &pcp->lists[order_to_pindex(migratetype, order)];
+ page = __rmqueue_pcplist(zone, order, migratetype, alloc_flags, pcp, list);
+- pcp_spin_unlock_irqrestore(pcp, flags);
++ pcp_spin_unlock(pcp);
+ pcp_trylock_finish(UP_flags);
+ if (page) {
+ __count_zid_vm_events(PGALLOC, page_zonenum(page), 1 << order);
+@@ -5375,7 +5360,6 @@ unsigned long __alloc_pages_bulk(gfp_t gfp, int preferred_nid,
+ struct page **page_array)
+ {
+ struct page *page;
+- unsigned long flags;
+ unsigned long __maybe_unused UP_flags;
+ struct zone *zone;
+ struct zoneref *z;
+@@ -5457,9 +5441,9 @@ unsigned long __alloc_pages_bulk(gfp_t gfp, int preferred_nid,
+ if (unlikely(!zone))
+ goto failed;
+
+- /* Is a parallel drain in progress? */
++ /* spin_trylock may fail due to a parallel drain or IRQ reentrancy. */
+ pcp_trylock_prepare(UP_flags);
+- pcp = pcp_spin_trylock_irqsave(zone->per_cpu_pageset, flags);
++ pcp = pcp_spin_trylock(zone->per_cpu_pageset);
+ if (!pcp)
+ goto failed_irq;
+
+@@ -5478,7 +5462,7 @@ unsigned long __alloc_pages_bulk(gfp_t gfp, int preferred_nid,
+ if (unlikely(!page)) {
+ /* Try and allocate at least one page */
+ if (!nr_account) {
+- pcp_spin_unlock_irqrestore(pcp, flags);
++ pcp_spin_unlock(pcp);
+ goto failed_irq;
+ }
+ break;
+@@ -5493,7 +5477,7 @@ unsigned long __alloc_pages_bulk(gfp_t gfp, int preferred_nid,
+ nr_populated++;
+ }
+
+- pcp_spin_unlock_irqrestore(pcp, flags);
++ pcp_spin_unlock(pcp);
+ pcp_trylock_finish(UP_flags);
+
+ __count_zid_vm_events(PGALLOC, zone_idx(zone), nr_account);
+--
+2.40.1
+
--- /dev/null
+From b8e7f84416f19aecd3af15f048fee98e03e1aacb Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 15 Mar 2023 20:57:45 +0000
+Subject: mptcp: annotate lockless accesses to sk->sk_err
+
+From: Eric Dumazet <edumazet@google.com>
+
+[ Upstream commit 9ae8e5ad99b8ebcd3d3dd46075f3825e6f08f063 ]
+
+mptcp_poll() reads sk->sk_err without socket lock held/owned.
+
+Add READ_ONCE() and WRITE_ONCE() to avoid load/store tearing.
+
+Signed-off-by: Eric Dumazet <edumazet@google.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Stable-dep-of: d5fbeff1ab81 ("mptcp: move __mptcp_error_report in protocol.c")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/mptcp/pm_netlink.c | 2 +-
+ net/mptcp/protocol.c | 8 ++++----
+ net/mptcp/subflow.c | 4 ++--
+ 3 files changed, 7 insertions(+), 7 deletions(-)
+
+diff --git a/net/mptcp/pm_netlink.c b/net/mptcp/pm_netlink.c
+index 9127a7fd5269c..5d845fcf3d09e 100644
+--- a/net/mptcp/pm_netlink.c
++++ b/net/mptcp/pm_netlink.c
+@@ -2047,7 +2047,7 @@ static int mptcp_event_put_token_and_ssk(struct sk_buff *skb,
+ nla_put_s32(skb, MPTCP_ATTR_IF_IDX, ssk->sk_bound_dev_if))
+ return -EMSGSIZE;
+
+- sk_err = ssk->sk_err;
++ sk_err = READ_ONCE(ssk->sk_err);
+ if (sk_err && sk->sk_state == TCP_ESTABLISHED &&
+ nla_put_u8(skb, MPTCP_ATTR_ERROR, sk_err))
+ return -EMSGSIZE;
+diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
+index 60e65f6325c3c..84f107854eac9 100644
+--- a/net/mptcp/protocol.c
++++ b/net/mptcp/protocol.c
+@@ -2517,15 +2517,15 @@ static void mptcp_check_fastclose(struct mptcp_sock *msk)
+ /* Mirror the tcp_reset() error propagation */
+ switch (sk->sk_state) {
+ case TCP_SYN_SENT:
+- sk->sk_err = ECONNREFUSED;
++ WRITE_ONCE(sk->sk_err, ECONNREFUSED);
+ break;
+ case TCP_CLOSE_WAIT:
+- sk->sk_err = EPIPE;
++ WRITE_ONCE(sk->sk_err, EPIPE);
+ break;
+ case TCP_CLOSE:
+ return;
+ default:
+- sk->sk_err = ECONNRESET;
++ WRITE_ONCE(sk->sk_err, ECONNRESET);
+ }
+
+ inet_sk_state_store(sk, TCP_CLOSE);
+@@ -3893,7 +3893,7 @@ static __poll_t mptcp_poll(struct file *file, struct socket *sock,
+
+ /* This barrier is coupled with smp_wmb() in __mptcp_error_report() */
+ smp_rmb();
+- if (sk->sk_err)
++ if (READ_ONCE(sk->sk_err))
+ mask |= EPOLLERR;
+
+ return mask;
+diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c
+index 168dced2434b3..032661c8273f2 100644
+--- a/net/mptcp/subflow.c
++++ b/net/mptcp/subflow.c
+@@ -1248,7 +1248,7 @@ static bool subflow_check_data_avail(struct sock *ssk)
+ subflow->reset_reason = MPTCP_RST_EMPTCP;
+
+ reset:
+- ssk->sk_err = EBADMSG;
++ WRITE_ONCE(ssk->sk_err, EBADMSG);
+ tcp_set_state(ssk, TCP_CLOSE);
+ while ((skb = skb_peek(&ssk->sk_receive_queue)))
+ sk_eat_skb(ssk, skb);
+@@ -1332,7 +1332,7 @@ void __mptcp_error_report(struct sock *sk)
+ ssk_state = inet_sk_state_load(ssk);
+ if (ssk_state == TCP_CLOSE && !sock_flag(sk, SOCK_DEAD))
+ inet_sk_state_store(sk, ssk_state);
+- sk->sk_err = -err;
++ WRITE_ONCE(sk->sk_err, -err);
+
+ /* This barrier is coupled with smp_rmb() in mptcp_poll() */
+ smp_wmb();
+--
+2.40.1
+
--- /dev/null
+From 508f8535214e8e82ecb63fcf8f6a69c18230a889 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 16 Sep 2023 12:52:49 +0200
+Subject: mptcp: fix dangling connection hang-up
+
+From: Paolo Abeni <pabeni@redhat.com>
+
+[ Upstream commit 27e5ccc2d5a50ed61bb73153edb1066104b108b3 ]
+
+According to RFC 8684 section 3.3:
+
+ A connection is not closed unless [...] or an implementation-specific
+ connection-level send timeout.
+
+Currently the MPTCP protocol does not implement such timeout, and
+connection timing-out at the TCP-level never move to close state.
+
+Introduces a catch-up condition at subflow close time to move the
+MPTCP socket to close, too.
+
+That additionally allows removing similar existing inside the worker.
+
+Finally, allow some additional timeout for plain ESTABLISHED mptcp
+sockets, as the protocol allows creating new subflows even at that
+point and making the connection functional again.
+
+This issue is actually present since the beginning, but it is basically
+impossible to solve without a long chain of functional pre-requisites
+topped by commit bbd49d114d57 ("mptcp: consolidate transition to
+TCP_CLOSE in mptcp_do_fastclose()"). When backporting this current
+patch, please also backport this other commit as well.
+
+Closes: https://github.com/multipath-tcp/mptcp_net-next/issues/430
+Fixes: e16163b6e2b7 ("mptcp: refactor shutdown and close")
+Cc: stable@vger.kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Reviewed-by: Matthieu Baerts <matthieu.baerts@tessares.net>
+Reviewed-by: Mat Martineau <martineau@kernel.org>
+Signed-off-by: Matthieu Baerts <matthieu.baerts@tessares.net>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/mptcp/protocol.c | 90 ++++++++++++++++++++++----------------------
+ net/mptcp/protocol.h | 22 +++++++++++
+ net/mptcp/subflow.c | 1 +
+ 3 files changed, 67 insertions(+), 46 deletions(-)
+
+diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
+index 93b60b049be27..60e65f6325c3c 100644
+--- a/net/mptcp/protocol.c
++++ b/net/mptcp/protocol.c
+@@ -846,6 +846,7 @@ static bool __mptcp_finish_join(struct mptcp_sock *msk, struct sock *ssk)
+
+ mptcp_sockopt_sync_locked(msk, ssk);
+ mptcp_subflow_joined(msk, ssk);
++ mptcp_stop_tout_timer(sk);
+ return true;
+ }
+
+@@ -2349,18 +2350,14 @@ static void __mptcp_close_ssk(struct sock *sk, struct sock *ssk,
+ bool dispose_it, need_push = false;
+
+ /* If the first subflow moved to a close state before accept, e.g. due
+- * to an incoming reset, mptcp either:
+- * - if either the subflow or the msk are dead, destroy the context
+- * (the subflow socket is deleted by inet_child_forget) and the msk
+- * - otherwise do nothing at the moment and take action at accept and/or
+- * listener shutdown - user-space must be able to accept() the closed
+- * socket.
++ * to an incoming reset or listener shutdown, the subflow socket is
++ * already deleted by inet_child_forget() and the mptcp socket can't
++ * survive too.
+ */
+- if (msk->in_accept_queue && msk->first == ssk) {
+- if (!sock_flag(sk, SOCK_DEAD) && !sock_flag(ssk, SOCK_DEAD))
+- return;
+-
++ if (msk->in_accept_queue && msk->first == ssk &&
++ (sock_flag(sk, SOCK_DEAD) || sock_flag(ssk, SOCK_DEAD))) {
+ /* ensure later check in mptcp_worker() will dispose the msk */
++ mptcp_set_close_tout(sk, tcp_jiffies32 - (TCP_TIMEWAIT_LEN + 1));
+ sock_set_flag(sk, SOCK_DEAD);
+ lock_sock_nested(ssk, SINGLE_DEPTH_NESTING);
+ mptcp_subflow_drop_ctx(ssk);
+@@ -2426,6 +2423,22 @@ static void __mptcp_close_ssk(struct sock *sk, struct sock *ssk,
+
+ if (need_push)
+ __mptcp_push_pending(sk, 0);
++
++ /* Catch every 'all subflows closed' scenario, including peers silently
++ * closing them, e.g. due to timeout.
++ * For established sockets, allow an additional timeout before closing,
++ * as the protocol can still create more subflows.
++ */
++ if (list_is_singular(&msk->conn_list) && msk->first &&
++ inet_sk_state_load(msk->first) == TCP_CLOSE) {
++ if (sk->sk_state != TCP_ESTABLISHED ||
++ msk->in_accept_queue || sock_flag(sk, SOCK_DEAD)) {
++ inet_sk_state_store(sk, TCP_CLOSE);
++ mptcp_close_wake_up(sk);
++ } else {
++ mptcp_start_tout_timer(sk);
++ }
++ }
+ }
+
+ void mptcp_close_ssk(struct sock *sk, struct sock *ssk,
+@@ -2469,23 +2482,14 @@ static void __mptcp_close_subflow(struct sock *sk)
+
+ }
+
+-static bool mptcp_should_close(const struct sock *sk)
++static bool mptcp_close_tout_expired(const struct sock *sk)
+ {
+- s32 delta = tcp_jiffies32 - inet_csk(sk)->icsk_mtup.probe_timestamp;
+- struct mptcp_subflow_context *subflow;
+-
+- if (delta >= TCP_TIMEWAIT_LEN || mptcp_sk(sk)->in_accept_queue)
+- return true;
++ if (!inet_csk(sk)->icsk_mtup.probe_timestamp ||
++ sk->sk_state == TCP_CLOSE)
++ return false;
+
+- /* if all subflows are in closed status don't bother with additional
+- * timeout
+- */
+- mptcp_for_each_subflow(mptcp_sk(sk), subflow) {
+- if (inet_sk_state_load(mptcp_subflow_tcp_sock(subflow)) !=
+- TCP_CLOSE)
+- return false;
+- }
+- return true;
++ return time_after32(tcp_jiffies32,
++ inet_csk(sk)->icsk_mtup.probe_timestamp + TCP_TIMEWAIT_LEN);
+ }
+
+ static void mptcp_check_fastclose(struct mptcp_sock *msk)
+@@ -2609,15 +2613,16 @@ void mptcp_reset_tout_timer(struct mptcp_sock *msk, unsigned long fail_tout)
+ struct sock *sk = (struct sock *)msk;
+ unsigned long timeout, close_timeout;
+
+- if (!fail_tout && !sock_flag(sk, SOCK_DEAD))
++ if (!fail_tout && !inet_csk(sk)->icsk_mtup.probe_timestamp)
+ return;
+
+- close_timeout = inet_csk(sk)->icsk_mtup.probe_timestamp - tcp_jiffies32 + jiffies + TCP_TIMEWAIT_LEN;
++ close_timeout = inet_csk(sk)->icsk_mtup.probe_timestamp - tcp_jiffies32 + jiffies +
++ TCP_TIMEWAIT_LEN;
+
+ /* the close timeout takes precedence on the fail one, and here at least one of
+ * them is active
+ */
+- timeout = sock_flag(sk, SOCK_DEAD) ? close_timeout : fail_tout;
++ timeout = inet_csk(sk)->icsk_mtup.probe_timestamp ? close_timeout : fail_tout;
+
+ sk_reset_timer(sk, &sk->sk_timer, timeout);
+ }
+@@ -2636,8 +2641,6 @@ static void mptcp_mp_fail_no_response(struct mptcp_sock *msk)
+ mptcp_subflow_reset(ssk);
+ WRITE_ONCE(mptcp_subflow_ctx(ssk)->fail_tout, 0);
+ unlock_sock_fast(ssk, slow);
+-
+- mptcp_reset_tout_timer(msk, 0);
+ }
+
+ static void mptcp_do_fastclose(struct sock *sk)
+@@ -2676,19 +2679,15 @@ static void mptcp_worker(struct work_struct *work)
+ if (test_and_clear_bit(MPTCP_WORK_CLOSE_SUBFLOW, &msk->flags))
+ __mptcp_close_subflow(sk);
+
+- /* There is no point in keeping around an orphaned sk timedout or
+- * closed, but we need the msk around to reply to incoming DATA_FIN,
+- * even if it is orphaned and in FIN_WAIT2 state
+- */
+- if (sock_flag(sk, SOCK_DEAD)) {
+- if (mptcp_should_close(sk)) {
+- inet_sk_state_store(sk, TCP_CLOSE);
+- mptcp_do_fastclose(sk);
+- }
+- if (sk->sk_state == TCP_CLOSE) {
+- __mptcp_destroy_sock(sk);
+- goto unlock;
+- }
++ if (mptcp_close_tout_expired(sk)) {
++ inet_sk_state_store(sk, TCP_CLOSE);
++ mptcp_do_fastclose(sk);
++ mptcp_close_wake_up(sk);
++ }
++
++ if (sock_flag(sk, SOCK_DEAD) && sk->sk_state == TCP_CLOSE) {
++ __mptcp_destroy_sock(sk);
++ goto unlock;
+ }
+
+ if (test_and_clear_bit(MPTCP_WORK_RTX, &msk->flags))
+@@ -2984,7 +2983,6 @@ bool __mptcp_close(struct sock *sk, long timeout)
+
+ cleanup:
+ /* orphan all the subflows */
+- inet_csk(sk)->icsk_mtup.probe_timestamp = tcp_jiffies32;
+ mptcp_for_each_subflow(msk, subflow) {
+ struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
+ bool slow = lock_sock_fast_nested(ssk);
+@@ -3021,7 +3019,7 @@ bool __mptcp_close(struct sock *sk, long timeout)
+ __mptcp_destroy_sock(sk);
+ do_cancel_work = true;
+ } else {
+- mptcp_reset_tout_timer(msk, 0);
++ mptcp_start_tout_timer(sk);
+ }
+
+ return do_cancel_work;
+@@ -3085,7 +3083,7 @@ static int mptcp_disconnect(struct sock *sk, int flags)
+ inet_sk_state_store(sk, TCP_CLOSE);
+
+ mptcp_stop_rtx_timer(sk);
+- sk_stop_timer(sk, &sk->sk_timer);
++ mptcp_stop_tout_timer(sk);
+
+ if (mptcp_sk(sk)->token)
+ mptcp_event(MPTCP_EVENT_CLOSED, mptcp_sk(sk), NULL, GFP_KERNEL);
+diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
+index b73160c5e2cf8..91d89a0aeb586 100644
+--- a/net/mptcp/protocol.h
++++ b/net/mptcp/protocol.h
+@@ -682,6 +682,28 @@ void mptcp_get_options(const struct sk_buff *skb,
+ void mptcp_finish_connect(struct sock *sk);
+ void __mptcp_set_connected(struct sock *sk);
+ void mptcp_reset_tout_timer(struct mptcp_sock *msk, unsigned long fail_tout);
++
++static inline void mptcp_stop_tout_timer(struct sock *sk)
++{
++ if (!inet_csk(sk)->icsk_mtup.probe_timestamp)
++ return;
++
++ sk_stop_timer(sk, &sk->sk_timer);
++ inet_csk(sk)->icsk_mtup.probe_timestamp = 0;
++}
++
++static inline void mptcp_set_close_tout(struct sock *sk, unsigned long tout)
++{
++ /* avoid 0 timestamp, as that means no close timeout */
++ inet_csk(sk)->icsk_mtup.probe_timestamp = tout ? : 1;
++}
++
++static inline void mptcp_start_tout_timer(struct sock *sk)
++{
++ mptcp_set_close_tout(sk, tcp_jiffies32);
++ mptcp_reset_tout_timer(mptcp_sk(sk), 0);
++}
++
+ static inline bool mptcp_is_fully_established(struct sock *sk)
+ {
+ return inet_sk_state_load(sk) == TCP_ESTABLISHED &&
+diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c
+index 6c8148c6e7710..168dced2434b3 100644
+--- a/net/mptcp/subflow.c
++++ b/net/mptcp/subflow.c
+@@ -1527,6 +1527,7 @@ int __mptcp_subflow_connect(struct sock *sk, const struct mptcp_addr_info *loc,
+ mptcp_sock_graft(ssk, sk->sk_socket);
+ iput(SOCK_INODE(sf));
+ WRITE_ONCE(msk->allow_infinite_fallback, false);
++ mptcp_stop_tout_timer(sk);
+ return 0;
+
+ failed_unlink:
+--
+2.40.1
+
--- /dev/null
+From 9b949b2bfa5363c42d1b085bfad979a21c50d114 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 16 Sep 2023 12:52:46 +0200
+Subject: mptcp: move __mptcp_error_report in protocol.c
+
+From: Paolo Abeni <pabeni@redhat.com>
+
+[ Upstream commit d5fbeff1ab812b6c473b6924bee8748469462e2c ]
+
+This will simplify the next patch ("mptcp: process pending subflow error
+on close").
+
+No functional change intended.
+
+Cc: stable@vger.kernel.org # v5.12+
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Reviewed-by: Mat Martineau <martineau@kernel.org>
+Signed-off-by: Matthieu Baerts <matthieu.baerts@tessares.net>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/mptcp/protocol.c | 36 ++++++++++++++++++++++++++++++++++++
+ net/mptcp/subflow.c | 36 ------------------------------------
+ 2 files changed, 36 insertions(+), 36 deletions(-)
+
+diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
+index 84f107854eac9..193f2bdc8fe1b 100644
+--- a/net/mptcp/protocol.c
++++ b/net/mptcp/protocol.c
+@@ -765,6 +765,42 @@ static bool __mptcp_ofo_queue(struct mptcp_sock *msk)
+ return moved;
+ }
+
++void __mptcp_error_report(struct sock *sk)
++{
++ struct mptcp_subflow_context *subflow;
++ struct mptcp_sock *msk = mptcp_sk(sk);
++
++ mptcp_for_each_subflow(msk, subflow) {
++ struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
++ int err = sock_error(ssk);
++ int ssk_state;
++
++ if (!err)
++ continue;
++
++ /* only propagate errors on fallen-back sockets or
++ * on MPC connect
++ */
++ if (sk->sk_state != TCP_SYN_SENT && !__mptcp_check_fallback(msk))
++ continue;
++
++ /* We need to propagate only transition to CLOSE state.
++ * Orphaned socket will see such state change via
++ * subflow_sched_work_if_closed() and that path will properly
++ * destroy the msk as needed.
++ */
++ ssk_state = inet_sk_state_load(ssk);
++ if (ssk_state == TCP_CLOSE && !sock_flag(sk, SOCK_DEAD))
++ inet_sk_state_store(sk, ssk_state);
++ WRITE_ONCE(sk->sk_err, -err);
++
++ /* This barrier is coupled with smp_rmb() in mptcp_poll() */
++ smp_wmb();
++ sk_error_report(sk);
++ break;
++ }
++}
++
+ /* In most cases we will be able to lock the mptcp socket. If its already
+ * owned, we need to defer to the work queue to avoid ABBA deadlock.
+ */
+diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c
+index 032661c8273f2..b93b08a75017b 100644
+--- a/net/mptcp/subflow.c
++++ b/net/mptcp/subflow.c
+@@ -1305,42 +1305,6 @@ void mptcp_space(const struct sock *ssk, int *space, int *full_space)
+ *full_space = tcp_full_space(sk);
+ }
+
+-void __mptcp_error_report(struct sock *sk)
+-{
+- struct mptcp_subflow_context *subflow;
+- struct mptcp_sock *msk = mptcp_sk(sk);
+-
+- mptcp_for_each_subflow(msk, subflow) {
+- struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
+- int err = sock_error(ssk);
+- int ssk_state;
+-
+- if (!err)
+- continue;
+-
+- /* only propagate errors on fallen-back sockets or
+- * on MPC connect
+- */
+- if (sk->sk_state != TCP_SYN_SENT && !__mptcp_check_fallback(msk))
+- continue;
+-
+- /* We need to propagate only transition to CLOSE state.
+- * Orphaned socket will see such state change via
+- * subflow_sched_work_if_closed() and that path will properly
+- * destroy the msk as needed.
+- */
+- ssk_state = inet_sk_state_load(ssk);
+- if (ssk_state == TCP_CLOSE && !sock_flag(sk, SOCK_DEAD))
+- inet_sk_state_store(sk, ssk_state);
+- WRITE_ONCE(sk->sk_err, -err);
+-
+- /* This barrier is coupled with smp_rmb() in mptcp_poll() */
+- smp_wmb();
+- sk_error_report(sk);
+- break;
+- }
+-}
+-
+ static void subflow_error_report(struct sock *ssk)
+ {
+ struct sock *sk = mptcp_subflow_ctx(ssk)->conn;
+--
+2.40.1
+
--- /dev/null
+From 25532afde428aef9d85923b345150355c5ff2c0e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 16 Sep 2023 12:52:47 +0200
+Subject: mptcp: process pending subflow error on close
+
+From: Paolo Abeni <pabeni@redhat.com>
+
+[ Upstream commit 9f1a98813b4b686482e5ef3c9d998581cace0ba6 ]
+
+On incoming TCP reset, subflow closing could happen before error
+propagation. That in turn could cause the socket error being ignored,
+and a missing socket state transition, as reported by Daire-Byrne.
+
+Address the issues explicitly checking for subflow socket error at
+close time. To avoid code duplication, factor-out of __mptcp_error_report()
+a new helper implementing the relevant bits.
+
+Closes: https://github.com/multipath-tcp/mptcp_net-next/issues/429
+Fixes: 15cc10453398 ("mptcp: deliver ssk errors to msk")
+Cc: stable@vger.kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Reviewed-by: Mat Martineau <martineau@kernel.org>
+Signed-off-by: Matthieu Baerts <matthieu.baerts@tessares.net>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/mptcp/protocol.c | 63 ++++++++++++++++++++++++--------------------
+ 1 file changed, 34 insertions(+), 29 deletions(-)
+
+diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
+index 193f2bdc8fe1b..b6e0579e72644 100644
+--- a/net/mptcp/protocol.c
++++ b/net/mptcp/protocol.c
+@@ -765,40 +765,44 @@ static bool __mptcp_ofo_queue(struct mptcp_sock *msk)
+ return moved;
+ }
+
+-void __mptcp_error_report(struct sock *sk)
++static bool __mptcp_subflow_error_report(struct sock *sk, struct sock *ssk)
+ {
+- struct mptcp_subflow_context *subflow;
+- struct mptcp_sock *msk = mptcp_sk(sk);
++ int err = sock_error(ssk);
++ int ssk_state;
+
+- mptcp_for_each_subflow(msk, subflow) {
+- struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
+- int err = sock_error(ssk);
+- int ssk_state;
++ if (!err)
++ return false;
+
+- if (!err)
+- continue;
++ /* only propagate errors on fallen-back sockets or
++ * on MPC connect
++ */
++ if (sk->sk_state != TCP_SYN_SENT && !__mptcp_check_fallback(mptcp_sk(sk)))
++ return false;
+
+- /* only propagate errors on fallen-back sockets or
+- * on MPC connect
+- */
+- if (sk->sk_state != TCP_SYN_SENT && !__mptcp_check_fallback(msk))
+- continue;
++ /* We need to propagate only transition to CLOSE state.
++ * Orphaned socket will see such state change via
++ * subflow_sched_work_if_closed() and that path will properly
++ * destroy the msk as needed.
++ */
++ ssk_state = inet_sk_state_load(ssk);
++ if (ssk_state == TCP_CLOSE && !sock_flag(sk, SOCK_DEAD))
++ inet_sk_state_store(sk, ssk_state);
++ WRITE_ONCE(sk->sk_err, -err);
+
+- /* We need to propagate only transition to CLOSE state.
+- * Orphaned socket will see such state change via
+- * subflow_sched_work_if_closed() and that path will properly
+- * destroy the msk as needed.
+- */
+- ssk_state = inet_sk_state_load(ssk);
+- if (ssk_state == TCP_CLOSE && !sock_flag(sk, SOCK_DEAD))
+- inet_sk_state_store(sk, ssk_state);
+- WRITE_ONCE(sk->sk_err, -err);
+-
+- /* This barrier is coupled with smp_rmb() in mptcp_poll() */
+- smp_wmb();
+- sk_error_report(sk);
+- break;
+- }
++ /* This barrier is coupled with smp_rmb() in mptcp_poll() */
++ smp_wmb();
++ sk_error_report(sk);
++ return true;
++}
++
++void __mptcp_error_report(struct sock *sk)
++{
++ struct mptcp_subflow_context *subflow;
++ struct mptcp_sock *msk = mptcp_sk(sk);
++
++ mptcp_for_each_subflow(msk, subflow)
++ if (__mptcp_subflow_error_report(sk, mptcp_subflow_tcp_sock(subflow)))
++ break;
+ }
+
+ /* In most cases we will be able to lock the mptcp socket. If its already
+@@ -2446,6 +2450,7 @@ static void __mptcp_close_ssk(struct sock *sk, struct sock *ssk,
+ }
+
+ out_release:
++ __mptcp_subflow_error_report(sk, ssk);
+ release_sock(ssk);
+
+ sock_put(ssk);
+--
+2.40.1
+
--- /dev/null
+From f49a0d87f8120207dbf164907a56445911a28011 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 16 Sep 2023 12:52:48 +0200
+Subject: mptcp: rename timer related helper to less confusing names
+
+From: Paolo Abeni <pabeni@redhat.com>
+
+[ Upstream commit f6909dc1c1f4452879278128012da6c76bc186a5 ]
+
+The msk socket uses to different timeout to track close related
+events and retransmissions. The existing helpers do not indicate
+clearly which timer they actually touch, making the related code
+quite confusing.
+
+Change the existing helpers name to avoid such confusion. No
+functional change intended.
+
+This patch is linked to the next one ("mptcp: fix dangling connection
+hang-up"). The two patches are supposed to be backported together.
+
+Cc: stable@vger.kernel.org # v5.11+
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Reviewed-by: Matthieu Baerts <matthieu.baerts@tessares.net>
+Reviewed-by: Mat Martineau <martineau@kernel.org>
+Signed-off-by: Matthieu Baerts <matthieu.baerts@tessares.net>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Stable-dep-of: 27e5ccc2d5a5 ("mptcp: fix dangling connection hang-up")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/mptcp/protocol.c | 42 +++++++++++++++++++++---------------------
+ net/mptcp/protocol.h | 2 +-
+ net/mptcp/subflow.c | 2 +-
+ 3 files changed, 23 insertions(+), 23 deletions(-)
+
+diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
+index 6dd880d6b0518..93b60b049be27 100644
+--- a/net/mptcp/protocol.c
++++ b/net/mptcp/protocol.c
+@@ -401,7 +401,7 @@ static bool __mptcp_move_skb(struct mptcp_sock *msk, struct sock *ssk,
+ return false;
+ }
+
+-static void mptcp_stop_timer(struct sock *sk)
++static void mptcp_stop_rtx_timer(struct sock *sk)
+ {
+ struct inet_connection_sock *icsk = inet_csk(sk);
+
+@@ -865,12 +865,12 @@ static void __mptcp_flush_join_list(struct sock *sk, struct list_head *join_list
+ }
+ }
+
+-static bool mptcp_timer_pending(struct sock *sk)
++static bool mptcp_rtx_timer_pending(struct sock *sk)
+ {
+ return timer_pending(&inet_csk(sk)->icsk_retransmit_timer);
+ }
+
+-static void mptcp_reset_timer(struct sock *sk)
++static void mptcp_reset_rtx_timer(struct sock *sk)
+ {
+ struct inet_connection_sock *icsk = inet_csk(sk);
+ unsigned long tout;
+@@ -1054,10 +1054,10 @@ static void __mptcp_clean_una(struct sock *sk)
+ out:
+ if (snd_una == READ_ONCE(msk->snd_nxt) &&
+ snd_una == READ_ONCE(msk->write_seq)) {
+- if (mptcp_timer_pending(sk) && !mptcp_data_fin_enabled(msk))
+- mptcp_stop_timer(sk);
++ if (mptcp_rtx_timer_pending(sk) && !mptcp_data_fin_enabled(msk))
++ mptcp_stop_rtx_timer(sk);
+ } else {
+- mptcp_reset_timer(sk);
++ mptcp_reset_rtx_timer(sk);
+ }
+ }
+
+@@ -1606,8 +1606,8 @@ void __mptcp_push_pending(struct sock *sk, unsigned int flags)
+
+ out:
+ /* ensure the rtx timer is running */
+- if (!mptcp_timer_pending(sk))
+- mptcp_reset_timer(sk);
++ if (!mptcp_rtx_timer_pending(sk))
++ mptcp_reset_rtx_timer(sk);
+ if (do_check_data_fin)
+ mptcp_check_send_data_fin(sk);
+ }
+@@ -1665,8 +1665,8 @@ static void __mptcp_subflow_push_pending(struct sock *sk, struct sock *ssk)
+ if (copied) {
+ tcp_push(ssk, 0, info.mss_now, tcp_sk(ssk)->nonagle,
+ info.size_goal);
+- if (!mptcp_timer_pending(sk))
+- mptcp_reset_timer(sk);
++ if (!mptcp_rtx_timer_pending(sk))
++ mptcp_reset_rtx_timer(sk);
+
+ if (msk->snd_data_fin_enable &&
+ msk->snd_nxt + 1 == msk->write_seq)
+@@ -2227,7 +2227,7 @@ static void mptcp_retransmit_timer(struct timer_list *t)
+ sock_put(sk);
+ }
+
+-static void mptcp_timeout_timer(struct timer_list *t)
++static void mptcp_tout_timer(struct timer_list *t)
+ {
+ struct sock *sk = from_timer(sk, t, sk_timer);
+
+@@ -2597,14 +2597,14 @@ static void __mptcp_retrans(struct sock *sk)
+ reset_timer:
+ mptcp_check_and_set_pending(sk);
+
+- if (!mptcp_timer_pending(sk))
+- mptcp_reset_timer(sk);
++ if (!mptcp_rtx_timer_pending(sk))
++ mptcp_reset_rtx_timer(sk);
+ }
+
+ /* schedule the timeout timer for the relevant event: either close timeout
+ * or mp_fail timeout. The close timeout takes precedence on the mp_fail one
+ */
+-void mptcp_reset_timeout(struct mptcp_sock *msk, unsigned long fail_tout)
++void mptcp_reset_tout_timer(struct mptcp_sock *msk, unsigned long fail_tout)
+ {
+ struct sock *sk = (struct sock *)msk;
+ unsigned long timeout, close_timeout;
+@@ -2637,7 +2637,7 @@ static void mptcp_mp_fail_no_response(struct mptcp_sock *msk)
+ WRITE_ONCE(mptcp_subflow_ctx(ssk)->fail_tout, 0);
+ unlock_sock_fast(ssk, slow);
+
+- mptcp_reset_timeout(msk, 0);
++ mptcp_reset_tout_timer(msk, 0);
+ }
+
+ static void mptcp_do_fastclose(struct sock *sk)
+@@ -2728,7 +2728,7 @@ static int __mptcp_init_sock(struct sock *sk)
+
+ /* re-use the csk retrans timer for MPTCP-level retrans */
+ timer_setup(&msk->sk.icsk_retransmit_timer, mptcp_retransmit_timer, 0);
+- timer_setup(&sk->sk_timer, mptcp_timeout_timer, 0);
++ timer_setup(&sk->sk_timer, mptcp_tout_timer, 0);
+
+ return 0;
+ }
+@@ -2820,8 +2820,8 @@ void mptcp_subflow_shutdown(struct sock *sk, struct sock *ssk, int how)
+ } else {
+ pr_debug("Sending DATA_FIN on subflow %p", ssk);
+ tcp_send_ack(ssk);
+- if (!mptcp_timer_pending(sk))
+- mptcp_reset_timer(sk);
++ if (!mptcp_rtx_timer_pending(sk))
++ mptcp_reset_rtx_timer(sk);
+ }
+ break;
+ }
+@@ -2904,7 +2904,7 @@ static void __mptcp_destroy_sock(struct sock *sk)
+
+ might_sleep();
+
+- mptcp_stop_timer(sk);
++ mptcp_stop_rtx_timer(sk);
+ sk_stop_timer(sk, &sk->sk_timer);
+ msk->pm.status = 0;
+
+@@ -3021,7 +3021,7 @@ bool __mptcp_close(struct sock *sk, long timeout)
+ __mptcp_destroy_sock(sk);
+ do_cancel_work = true;
+ } else {
+- mptcp_reset_timeout(msk, 0);
++ mptcp_reset_tout_timer(msk, 0);
+ }
+
+ return do_cancel_work;
+@@ -3084,7 +3084,7 @@ static int mptcp_disconnect(struct sock *sk, int flags)
+ mptcp_check_listen_stop(sk);
+ inet_sk_state_store(sk, TCP_CLOSE);
+
+- mptcp_stop_timer(sk);
++ mptcp_stop_rtx_timer(sk);
+ sk_stop_timer(sk, &sk->sk_timer);
+
+ if (mptcp_sk(sk)->token)
+diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
+index d77b25636125b..b73160c5e2cf8 100644
+--- a/net/mptcp/protocol.h
++++ b/net/mptcp/protocol.h
+@@ -681,7 +681,7 @@ void mptcp_get_options(const struct sk_buff *skb,
+
+ void mptcp_finish_connect(struct sock *sk);
+ void __mptcp_set_connected(struct sock *sk);
+-void mptcp_reset_timeout(struct mptcp_sock *msk, unsigned long fail_tout);
++void mptcp_reset_tout_timer(struct mptcp_sock *msk, unsigned long fail_tout);
+ static inline bool mptcp_is_fully_established(struct sock *sk)
+ {
+ return inet_sk_state_load(sk) == TCP_ESTABLISHED &&
+diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c
+index 52a747a80e88e..6c8148c6e7710 100644
+--- a/net/mptcp/subflow.c
++++ b/net/mptcp/subflow.c
+@@ -1161,7 +1161,7 @@ static void mptcp_subflow_fail(struct mptcp_sock *msk, struct sock *ssk)
+ WRITE_ONCE(subflow->fail_tout, fail_tout);
+ tcp_send_ack(ssk);
+
+- mptcp_reset_timeout(msk, subflow->fail_tout);
++ mptcp_reset_tout_timer(msk, subflow->fail_tout);
+ }
+
+ static bool subflow_check_data_avail(struct sock *ssk)
+--
+2.40.1
+
--- /dev/null
+From 9aa0b2eda8095073eadf5292d4ea37420997d704 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 20 Apr 2023 12:17:35 -0400
+Subject: NFS: Cleanup unused rpc_clnt variable
+
+From: Benjamin Coddington <bcodding@redhat.com>
+
+[ Upstream commit e025f0a73f6acb920d86549b2177a5883535421d ]
+
+The root rpc_clnt is not used here, clean it up.
+
+Fixes: 4dc73c679114 ("NFSv4: keep state manager thread active if swap is enabled")
+Signed-off-by: Benjamin Coddington <bcodding@redhat.com>
+Reviewed-by: NeilBrown <neilb@suse.de>
+Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
+Stable-dep-of: 956fd46f97d2 ("NFSv4: Fix a state manager thread deadlock regression")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/nfs/nfs4state.c | 4 ----
+ 1 file changed, 4 deletions(-)
+
+diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c
+index 5b49e5365bb30..1b707573fbf8d 100644
+--- a/fs/nfs/nfs4state.c
++++ b/fs/nfs/nfs4state.c
+@@ -1209,10 +1209,6 @@ void nfs4_schedule_state_manager(struct nfs_client *clp)
+ {
+ struct task_struct *task;
+ char buf[INET6_ADDRSTRLEN + sizeof("-manager") + 1];
+- struct rpc_clnt *cl = clp->cl_rpcclient;
+-
+- while (cl != cl->cl_parent)
+- cl = cl->cl_parent;
+
+ set_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state);
+ if (test_and_set_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state) != 0) {
+--
+2.40.1
+
--- /dev/null
+From 307a9be3b1a34bf9bd085bb0bb1088324672d52d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 15 Jun 2023 14:07:22 -0400
+Subject: NFS: rename nfs_client_kset to nfs_kset
+
+From: Benjamin Coddington <bcodding@redhat.com>
+
+[ Upstream commit 8b18a2edecc0741b0eecf8b18fdb356a0f8682de ]
+
+Be brief and match the subsystem name. There's no need to distinguish this
+kset variable from the server.
+
+Signed-off-by: Benjamin Coddington <bcodding@redhat.com>
+Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
+Stable-dep-of: 956fd46f97d2 ("NFSv4: Fix a state manager thread deadlock regression")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/nfs/sysfs.c | 16 ++++++++--------
+ 1 file changed, 8 insertions(+), 8 deletions(-)
+
+diff --git a/fs/nfs/sysfs.c b/fs/nfs/sysfs.c
+index a6f7403669631..edb535a0ff973 100644
+--- a/fs/nfs/sysfs.c
++++ b/fs/nfs/sysfs.c
+@@ -18,7 +18,7 @@
+ #include "sysfs.h"
+
+ struct kobject *nfs_client_kobj;
+-static struct kset *nfs_client_kset;
++static struct kset *nfs_kset;
+
+ static void nfs_netns_object_release(struct kobject *kobj)
+ {
+@@ -55,13 +55,13 @@ static struct kobject *nfs_netns_object_alloc(const char *name,
+
+ int nfs_sysfs_init(void)
+ {
+- nfs_client_kset = kset_create_and_add("nfs", NULL, fs_kobj);
+- if (!nfs_client_kset)
++ nfs_kset = kset_create_and_add("nfs", NULL, fs_kobj);
++ if (!nfs_kset)
+ return -ENOMEM;
+- nfs_client_kobj = nfs_netns_object_alloc("net", nfs_client_kset, NULL);
++ nfs_client_kobj = nfs_netns_object_alloc("net", nfs_kset, NULL);
+ if (!nfs_client_kobj) {
+- kset_unregister(nfs_client_kset);
+- nfs_client_kset = NULL;
++ kset_unregister(nfs_kset);
++ nfs_kset = NULL;
+ return -ENOMEM;
+ }
+ return 0;
+@@ -70,7 +70,7 @@ int nfs_sysfs_init(void)
+ void nfs_sysfs_exit(void)
+ {
+ kobject_put(nfs_client_kobj);
+- kset_unregister(nfs_client_kset);
++ kset_unregister(nfs_kset);
+ }
+
+ static ssize_t nfs_netns_identifier_show(struct kobject *kobj,
+@@ -159,7 +159,7 @@ static struct nfs_netns_client *nfs_netns_client_alloc(struct kobject *parent,
+ p = kzalloc(sizeof(*p), GFP_KERNEL);
+ if (p) {
+ p->net = net;
+- p->kobject.kset = nfs_client_kset;
++ p->kobject.kset = nfs_kset;
+ if (kobject_init_and_add(&p->kobject, &nfs_netns_client_type,
+ parent, "nfs_client") == 0)
+ return p;
+--
+2.40.1
+
--- /dev/null
+From 7eca5fbc29ba9fd59b57061e5611643168a6bb58 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 24 Sep 2023 13:14:15 -0400
+Subject: NFSv4: Fix a state manager thread deadlock regression
+
+From: Trond Myklebust <trond.myklebust@hammerspace.com>
+
+[ Upstream commit 956fd46f97d238032cb5fa4771cdaccc6e760f9a ]
+
+Commit 4dc73c679114 reintroduces the deadlock that was fixed by commit
+aeabb3c96186 ("NFSv4: Fix a NFSv4 state manager deadlock") because it
+prevents the setup of new threads to handle reboot recovery, while the
+older recovery thread is stuck returning delegations.
+
+Fixes: 4dc73c679114 ("NFSv4: keep state manager thread active if swap is enabled")
+Cc: stable@vger.kernel.org
+Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
+Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/nfs/nfs4proc.c | 4 +++-
+ fs/nfs/nfs4state.c | 36 +++++++++++++++++++++++++-----------
+ 2 files changed, 28 insertions(+), 12 deletions(-)
+
+diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
+index be570c65ae154..b927a7d1b46d4 100644
+--- a/fs/nfs/nfs4proc.c
++++ b/fs/nfs/nfs4proc.c
+@@ -10629,7 +10629,9 @@ static void nfs4_disable_swap(struct inode *inode)
+ */
+ struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
+
+- nfs4_schedule_state_manager(clp);
++ set_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state);
++ clear_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state);
++ wake_up_var(&clp->cl_state);
+ }
+
+ static const struct inode_operations nfs4_dir_inode_operations = {
+diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c
+index 1b707573fbf8d..ed789e0cb9431 100644
+--- a/fs/nfs/nfs4state.c
++++ b/fs/nfs/nfs4state.c
+@@ -1209,13 +1209,23 @@ void nfs4_schedule_state_manager(struct nfs_client *clp)
+ {
+ struct task_struct *task;
+ char buf[INET6_ADDRSTRLEN + sizeof("-manager") + 1];
++ struct rpc_clnt *clnt = clp->cl_rpcclient;
++ bool swapon = false;
+
+ set_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state);
+- if (test_and_set_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state) != 0) {
+- wake_up_var(&clp->cl_state);
+- return;
++
++ if (atomic_read(&clnt->cl_swapper)) {
++ swapon = !test_and_set_bit(NFS4CLNT_MANAGER_AVAILABLE,
++ &clp->cl_state);
++ if (!swapon) {
++ wake_up_var(&clp->cl_state);
++ return;
++ }
+ }
+- set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state);
++
++ if (test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) != 0)
++ return;
++
+ __module_get(THIS_MODULE);
+ refcount_inc(&clp->cl_count);
+
+@@ -1232,8 +1242,9 @@ void nfs4_schedule_state_manager(struct nfs_client *clp)
+ __func__, PTR_ERR(task));
+ if (!nfs_client_init_is_complete(clp))
+ nfs_mark_client_ready(clp, PTR_ERR(task));
++ if (swapon)
++ clear_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state);
+ nfs4_clear_state_manager_bit(clp);
+- clear_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state);
+ nfs_put_client(clp);
+ module_put(THIS_MODULE);
+ }
+@@ -2737,22 +2748,25 @@ static int nfs4_run_state_manager(void *ptr)
+
+ allow_signal(SIGKILL);
+ again:
+- set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state);
+ nfs4_state_manager(clp);
+- if (atomic_read(&cl->cl_swapper)) {
++
++ if (test_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state) &&
++ !test_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state)) {
+ wait_var_event_interruptible(&clp->cl_state,
+ test_bit(NFS4CLNT_RUN_MANAGER,
+ &clp->cl_state));
+- if (atomic_read(&cl->cl_swapper) &&
+- test_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state))
++ if (!atomic_read(&cl->cl_swapper))
++ clear_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state);
++ if (refcount_read(&clp->cl_count) > 1 && !signalled() &&
++ !test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state))
+ goto again;
+ /* Either no longer a swapper, or were signalled */
++ clear_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state);
+ }
+- clear_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state);
+
+ if (refcount_read(&clp->cl_count) > 1 && !signalled() &&
+ test_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state) &&
+- !test_and_set_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state))
++ !test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state))
+ goto again;
+
+ nfs_put_client(clp);
+--
+2.40.1
+
--- /dev/null
+From 878cf5ac73826139a016ebb3154ba1e8b0130bd3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 27 Jun 2023 14:31:49 -0400
+Subject: Revert "NFSv4: Retry LOCK on OLD_STATEID during delegation return"
+
+From: Benjamin Coddington <bcodding@redhat.com>
+
+[ Upstream commit 5b4a82a0724af1dfd1320826e0266117b6a57fbd ]
+
+Olga Kornievskaia reports that this patch breaks NFSv4.0 state recovery.
+It also introduces additional complexity in the error paths for cases not
+related to the original problem. Let's revert it for now, and address the
+original problem in another manner.
+
+This reverts commit f5ea16137a3fa2858620dc9084466491c128535f.
+
+Fixes: f5ea16137a3f ("NFSv4: Retry LOCK on OLD_STATEID during delegation return")
+Reported-by: Kornievskaia, Olga <Olga.Kornievskaia@netapp.com>
+Signed-off-by: Benjamin Coddington <bcodding@redhat.com>
+Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/nfs/nfs4proc.c | 6 ++----
+ 1 file changed, 2 insertions(+), 4 deletions(-)
+
+diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
+index b927a7d1b46d4..e1297c6bcfbe2 100644
+--- a/fs/nfs/nfs4proc.c
++++ b/fs/nfs/nfs4proc.c
+@@ -7157,7 +7157,6 @@ static void nfs4_lock_done(struct rpc_task *task, void *calldata)
+ {
+ struct nfs4_lockdata *data = calldata;
+ struct nfs4_lock_state *lsp = data->lsp;
+- struct nfs_server *server = NFS_SERVER(d_inode(data->ctx->dentry));
+
+ if (!nfs4_sequence_done(task, &data->res.seq_res))
+ return;
+@@ -7165,7 +7164,8 @@ static void nfs4_lock_done(struct rpc_task *task, void *calldata)
+ data->rpc_status = task->tk_status;
+ switch (task->tk_status) {
+ case 0:
+- renew_lease(server, data->timestamp);
++ renew_lease(NFS_SERVER(d_inode(data->ctx->dentry)),
++ data->timestamp);
+ if (data->arg.new_lock && !data->cancelled) {
+ data->fl.fl_flags &= ~(FL_SLEEP | FL_ACCESS);
+ if (locks_lock_inode_wait(lsp->ls_state->inode, &data->fl) < 0)
+@@ -7193,8 +7193,6 @@ static void nfs4_lock_done(struct rpc_task *task, void *calldata)
+ if (!nfs4_stateid_match(&data->arg.open_stateid,
+ &lsp->ls_state->open_stateid))
+ goto out_restart;
+- else if (nfs4_async_handle_error(task, server, lsp->ls_state, NULL) == -EAGAIN)
+- goto out_restart;
+ } else if (!nfs4_stateid_match(&data->arg.lock_stateid,
+ &lsp->ls_stateid))
+ goto out_restart;
+--
+2.40.1
+
--- /dev/null
+From 3ba4e64542bd7ce2b45b115607a32a814492eb6f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 21 Sep 2023 20:54:25 +0800
+Subject: ring-buffer: Fix bytes info in per_cpu buffer stats
+
+From: Zheng Yejian <zhengyejian1@huawei.com>
+
+[ Upstream commit 45d99ea451d0c30bfd4864f0fe485d7dac014902 ]
+
+The 'bytes' info in file 'per_cpu/cpu<X>/stats' means the number of
+bytes in cpu buffer that have not been consumed. However, currently
+after consuming data by reading file 'trace_pipe', the 'bytes' info
+was not changed as expected.
+
+ # cat per_cpu/cpu0/stats
+ entries: 0
+ overrun: 0
+ commit overrun: 0
+ bytes: 568 <--- 'bytes' is problematical !!!
+ oldest event ts: 8651.371479
+ now ts: 8653.912224
+ dropped events: 0
+ read events: 8
+
+The root cause is incorrect stat on cpu_buffer->read_bytes. To fix it:
+ 1. When stat 'read_bytes', account consumed event in rb_advance_reader();
+ 2. When stat 'entries_bytes', exclude the discarded padding event which
+ is smaller than minimum size because it is invisible to reader. Then
+ use rb_page_commit() instead of BUF_PAGE_SIZE at where accounting for
+ page-based read/remove/overrun.
+
+Also correct the comments of ring_buffer_bytes_cpu() in this patch.
+
+Link: https://lore.kernel.org/linux-trace-kernel/20230921125425.1708423-1-zhengyejian1@huawei.com
+
+Cc: stable@vger.kernel.org
+Fixes: c64e148a3be3 ("trace: Add ring buffer stats to measure rate of events")
+Signed-off-by: Zheng Yejian <zhengyejian1@huawei.com>
+Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ kernel/trace/ring_buffer.c | 28 +++++++++++++++-------------
+ 1 file changed, 15 insertions(+), 13 deletions(-)
+
+diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
+index 51737b3d54b35..b7383358c4ea1 100644
+--- a/kernel/trace/ring_buffer.c
++++ b/kernel/trace/ring_buffer.c
+@@ -354,6 +354,11 @@ static void rb_init_page(struct buffer_data_page *bpage)
+ local_set(&bpage->commit, 0);
+ }
+
++static __always_inline unsigned int rb_page_commit(struct buffer_page *bpage)
++{
++ return local_read(&bpage->page->commit);
++}
++
+ static void free_buffer_page(struct buffer_page *bpage)
+ {
+ free_page((unsigned long)bpage->page);
+@@ -2020,7 +2025,7 @@ rb_remove_pages(struct ring_buffer_per_cpu *cpu_buffer, unsigned long nr_pages)
+ * Increment overrun to account for the lost events.
+ */
+ local_add(page_entries, &cpu_buffer->overrun);
+- local_sub(BUF_PAGE_SIZE, &cpu_buffer->entries_bytes);
++ local_sub(rb_page_commit(to_remove_page), &cpu_buffer->entries_bytes);
+ local_inc(&cpu_buffer->pages_lost);
+ }
+
+@@ -2364,11 +2369,6 @@ rb_reader_event(struct ring_buffer_per_cpu *cpu_buffer)
+ cpu_buffer->reader_page->read);
+ }
+
+-static __always_inline unsigned rb_page_commit(struct buffer_page *bpage)
+-{
+- return local_read(&bpage->page->commit);
+-}
+-
+ static struct ring_buffer_event *
+ rb_iter_head_event(struct ring_buffer_iter *iter)
+ {
+@@ -2514,7 +2514,7 @@ rb_handle_head_page(struct ring_buffer_per_cpu *cpu_buffer,
+ * the counters.
+ */
+ local_add(entries, &cpu_buffer->overrun);
+- local_sub(BUF_PAGE_SIZE, &cpu_buffer->entries_bytes);
++ local_sub(rb_page_commit(next_page), &cpu_buffer->entries_bytes);
+ local_inc(&cpu_buffer->pages_lost);
+
+ /*
+@@ -2657,9 +2657,6 @@ rb_reset_tail(struct ring_buffer_per_cpu *cpu_buffer,
+
+ event = __rb_page_index(tail_page, tail);
+
+- /* account for padding bytes */
+- local_add(BUF_PAGE_SIZE - tail, &cpu_buffer->entries_bytes);
+-
+ /*
+ * Save the original length to the meta data.
+ * This will be used by the reader to add lost event
+@@ -2673,7 +2670,8 @@ rb_reset_tail(struct ring_buffer_per_cpu *cpu_buffer,
+ * write counter enough to allow another writer to slip
+ * in on this page.
+ * We put in a discarded commit instead, to make sure
+- * that this space is not used again.
++ * that this space is not used again, and this space will
++ * not be accounted into 'entries_bytes'.
+ *
+ * If we are less than the minimum size, we don't need to
+ * worry about it.
+@@ -2698,6 +2696,9 @@ rb_reset_tail(struct ring_buffer_per_cpu *cpu_buffer,
+ /* time delta must be non zero */
+ event->time_delta = 1;
+
++ /* account for padding bytes */
++ local_add(BUF_PAGE_SIZE - tail, &cpu_buffer->entries_bytes);
++
+ /* Make sure the padding is visible before the tail_page->write update */
+ smp_wmb();
+
+@@ -4215,7 +4216,7 @@ u64 ring_buffer_oldest_event_ts(struct trace_buffer *buffer, int cpu)
+ EXPORT_SYMBOL_GPL(ring_buffer_oldest_event_ts);
+
+ /**
+- * ring_buffer_bytes_cpu - get the number of bytes consumed in a cpu buffer
++ * ring_buffer_bytes_cpu - get the number of bytes unconsumed in a cpu buffer
+ * @buffer: The ring buffer
+ * @cpu: The per CPU buffer to read from.
+ */
+@@ -4725,6 +4726,7 @@ static void rb_advance_reader(struct ring_buffer_per_cpu *cpu_buffer)
+
+ length = rb_event_length(event);
+ cpu_buffer->reader_page->read += length;
++ cpu_buffer->read_bytes += length;
+ }
+
+ static void rb_advance_iter(struct ring_buffer_iter *iter)
+@@ -5820,7 +5822,7 @@ int ring_buffer_read_page(struct trace_buffer *buffer,
+ } else {
+ /* update the entry counter */
+ cpu_buffer->read += rb_page_entries(reader);
+- cpu_buffer->read_bytes += BUF_PAGE_SIZE;
++ cpu_buffer->read_bytes += rb_page_commit(reader);
+
+ /* swap the pages */
+ rb_init_page(bpage);
+--
+2.40.1
+
--- /dev/null
+From 54e23956944147b55c7e5a2e07d96653611e0832 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 15 Mar 2023 15:24:46 +0100
+Subject: ring-buffer: remove obsolete comment for free_buffer_page()
+
+From: Vlastimil Babka <vbabka@suse.cz>
+
+[ Upstream commit a98151ad53b53f010ee364ec2fd06445b328578b ]
+
+The comment refers to mm/slob.c which is being removed. It comes from
+commit ed56829cb319 ("ring_buffer: reset buffer page when freeing") and
+according to Steven the borrowed code was a page mapcount and mapping
+reset, which was later removed by commit e4c2ce82ca27 ("ring_buffer:
+allocate buffer page pointer"). Thus the comment is not accurate anyway,
+remove it.
+
+Link: https://lore.kernel.org/linux-trace-kernel/20230315142446.27040-1-vbabka@suse.cz
+
+Cc: Masami Hiramatsu <mhiramat@kernel.org>
+Cc: Ingo Molnar <mingo@elte.hu>
+Reported-by: Mike Rapoport <mike.rapoport@gmail.com>
+Suggested-by: Steven Rostedt (Google) <rostedt@goodmis.org>
+Fixes: e4c2ce82ca27 ("ring_buffer: allocate buffer page pointer")
+Signed-off-by: Vlastimil Babka <vbabka@suse.cz>
+Reviewed-by: Mukesh Ojha <quic_mojha@quicinc.com>
+Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
+Stable-dep-of: 45d99ea451d0 ("ring-buffer: Fix bytes info in per_cpu buffer stats")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ kernel/trace/ring_buffer.c | 4 ----
+ 1 file changed, 4 deletions(-)
+
+diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
+index 2f562cf961e0a..51737b3d54b35 100644
+--- a/kernel/trace/ring_buffer.c
++++ b/kernel/trace/ring_buffer.c
+@@ -354,10 +354,6 @@ static void rb_init_page(struct buffer_data_page *bpage)
+ local_set(&bpage->commit, 0);
+ }
+
+-/*
+- * Also stolen from mm/slob.c. Thanks to Mathieu Desnoyers for pointing
+- * this issue out.
+- */
+ static void free_buffer_page(struct buffer_page *bpage)
+ {
+ free_page((unsigned long)bpage->page);
+--
+2.40.1
+
--- /dev/null
+From 40d723c10314e9132382eed0470564cce942d865 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 22 Aug 2023 08:30:41 -0700
+Subject: scsi: core: Improve type safety of scsi_rescan_device()
+
+From: Bart Van Assche <bvanassche@acm.org>
+
+[ Upstream commit 79519528a180c64a90863db2ce70887de6c49d16 ]
+
+Most callers of scsi_rescan_device() have the scsi_device pointer readily
+available. Pass a struct scsi_device pointer to scsi_rescan_device()
+instead of a struct device pointer. This change prevents that a pointer to
+another struct device would be passed accidentally to scsi_rescan_device().
+
+Remove the scsi_rescan_device() declaration from the scsi_priv.h header
+file since it duplicates the declaration in <scsi/scsi_host.h>.
+
+Reviewed-by: Hannes Reinecke <hare@suse.de>
+Reviewed-by: Damien Le Moal <damien.lemoal@opensource.wdc.com>
+Reviewed-by: John Garry <john.g.garry@oracle.com>
+Cc: Mike Christie <michael.christie@oracle.com>
+Cc: Ming Lei <ming.lei@redhat.com>
+Signed-off-by: Bart Van Assche <bvanassche@acm.org>
+Link: https://lore.kernel.org/r/20230822153043.4046244-1-bvanassche@acm.org
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Stable-dep-of: 8b4d9469d0b0 ("ata: libata-scsi: Fix delayed scsi_rescan_device() execution")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/ata/libata-scsi.c | 2 +-
+ drivers/scsi/aacraid/commsup.c | 2 +-
+ drivers/scsi/mvumi.c | 2 +-
+ drivers/scsi/scsi_lib.c | 2 +-
+ drivers/scsi/scsi_priv.h | 1 -
+ drivers/scsi/scsi_scan.c | 4 ++--
+ drivers/scsi/scsi_sysfs.c | 4 ++--
+ drivers/scsi/smartpqi/smartpqi_init.c | 2 +-
+ drivers/scsi/storvsc_drv.c | 2 +-
+ drivers/scsi/virtio_scsi.c | 2 +-
+ include/scsi/scsi_host.h | 2 +-
+ 11 files changed, 12 insertions(+), 13 deletions(-)
+
+diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
+index 8cc8268327f0c..b348f77b91231 100644
+--- a/drivers/ata/libata-scsi.c
++++ b/drivers/ata/libata-scsi.c
+@@ -4678,7 +4678,7 @@ void ata_scsi_dev_rescan(struct work_struct *work)
+ }
+
+ spin_unlock_irqrestore(ap->lock, flags);
+- scsi_rescan_device(&(sdev->sdev_gendev));
++ scsi_rescan_device(sdev);
+ scsi_device_put(sdev);
+ spin_lock_irqsave(ap->lock, flags);
+ }
+diff --git a/drivers/scsi/aacraid/commsup.c b/drivers/scsi/aacraid/commsup.c
+index 3f062e4013ab6..013a9a334972e 100644
+--- a/drivers/scsi/aacraid/commsup.c
++++ b/drivers/scsi/aacraid/commsup.c
+@@ -1451,7 +1451,7 @@ static void aac_handle_aif(struct aac_dev * dev, struct fib * fibptr)
+ #endif
+ break;
+ }
+- scsi_rescan_device(&device->sdev_gendev);
++ scsi_rescan_device(device);
+ break;
+
+ default:
+diff --git a/drivers/scsi/mvumi.c b/drivers/scsi/mvumi.c
+index 05d3ce9b72dba..c4acf65379d20 100644
+--- a/drivers/scsi/mvumi.c
++++ b/drivers/scsi/mvumi.c
+@@ -1500,7 +1500,7 @@ static void mvumi_rescan_devices(struct mvumi_hba *mhba, int id)
+
+ sdev = scsi_device_lookup(mhba->shost, 0, id, 0);
+ if (sdev) {
+- scsi_rescan_device(&sdev->sdev_gendev);
++ scsi_rescan_device(sdev);
+ scsi_device_put(sdev);
+ }
+ }
+diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
+index fb6e9a7a7f58b..d25e1c2472538 100644
+--- a/drivers/scsi/scsi_lib.c
++++ b/drivers/scsi/scsi_lib.c
+@@ -2445,7 +2445,7 @@ static void scsi_evt_emit(struct scsi_device *sdev, struct scsi_event *evt)
+ envp[idx++] = "SDEV_MEDIA_CHANGE=1";
+ break;
+ case SDEV_EVT_INQUIRY_CHANGE_REPORTED:
+- scsi_rescan_device(&sdev->sdev_gendev);
++ scsi_rescan_device(sdev);
+ envp[idx++] = "SDEV_UA=INQUIRY_DATA_HAS_CHANGED";
+ break;
+ case SDEV_EVT_CAPACITY_CHANGE_REPORTED:
+diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h
+index c52de9a973e46..b14545acb40f5 100644
+--- a/drivers/scsi/scsi_priv.h
++++ b/drivers/scsi/scsi_priv.h
+@@ -132,7 +132,6 @@ extern int scsi_complete_async_scans(void);
+ extern int scsi_scan_host_selected(struct Scsi_Host *, unsigned int,
+ unsigned int, u64, enum scsi_scan_mode);
+ extern void scsi_forget_host(struct Scsi_Host *);
+-extern void scsi_rescan_device(struct device *);
+
+ /* scsi_sysctl.c */
+ #ifdef CONFIG_SYSCTL
+diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
+index d12f2dcb4040a..445989f44d3f2 100644
+--- a/drivers/scsi/scsi_scan.c
++++ b/drivers/scsi/scsi_scan.c
+@@ -1611,9 +1611,9 @@ int scsi_add_device(struct Scsi_Host *host, uint channel,
+ }
+ EXPORT_SYMBOL(scsi_add_device);
+
+-void scsi_rescan_device(struct device *dev)
++void scsi_rescan_device(struct scsi_device *sdev)
+ {
+- struct scsi_device *sdev = to_scsi_device(dev);
++ struct device *dev = &sdev->sdev_gendev;
+
+ device_lock(dev);
+
+diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c
+index cac7c902cf70a..1f531063d6331 100644
+--- a/drivers/scsi/scsi_sysfs.c
++++ b/drivers/scsi/scsi_sysfs.c
+@@ -762,7 +762,7 @@ static ssize_t
+ store_rescan_field (struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+ {
+- scsi_rescan_device(dev);
++ scsi_rescan_device(to_scsi_device(dev));
+ return count;
+ }
+ static DEVICE_ATTR(rescan, S_IWUSR, NULL, store_rescan_field);
+@@ -855,7 +855,7 @@ store_state_field(struct device *dev, struct device_attribute *attr,
+ * waiting for pending I/O to finish.
+ */
+ blk_mq_run_hw_queues(sdev->request_queue, true);
+- scsi_rescan_device(dev);
++ scsi_rescan_device(sdev);
+ }
+
+ return ret == 0 ? count : -EINVAL;
+diff --git a/drivers/scsi/smartpqi/smartpqi_init.c b/drivers/scsi/smartpqi/smartpqi_init.c
+index 9f0f69c1ed665..47d487729635c 100644
+--- a/drivers/scsi/smartpqi/smartpqi_init.c
++++ b/drivers/scsi/smartpqi/smartpqi_init.c
+@@ -2278,7 +2278,7 @@ static void pqi_update_device_list(struct pqi_ctrl_info *ctrl_info,
+ device->advertised_queue_depth = device->queue_depth;
+ scsi_change_queue_depth(device->sdev, device->advertised_queue_depth);
+ if (device->rescan) {
+- scsi_rescan_device(&device->sdev->sdev_gendev);
++ scsi_rescan_device(device->sdev);
+ device->rescan = false;
+ }
+ }
+diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c
+index 7a1dc5c7c49ee..c2d981d5a2dd5 100644
+--- a/drivers/scsi/storvsc_drv.c
++++ b/drivers/scsi/storvsc_drv.c
+@@ -471,7 +471,7 @@ static void storvsc_device_scan(struct work_struct *work)
+ sdev = scsi_device_lookup(wrk->host, 0, wrk->tgt_id, wrk->lun);
+ if (!sdev)
+ goto done;
+- scsi_rescan_device(&sdev->sdev_gendev);
++ scsi_rescan_device(sdev);
+ scsi_device_put(sdev);
+
+ done:
+diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c
+index 2a79ab16134b1..3f8c553f3d91e 100644
+--- a/drivers/scsi/virtio_scsi.c
++++ b/drivers/scsi/virtio_scsi.c
+@@ -325,7 +325,7 @@ static void virtscsi_handle_param_change(struct virtio_scsi *vscsi,
+ /* Handle "Parameters changed", "Mode parameters changed", and
+ "Capacity data has changed". */
+ if (asc == 0x2a && (ascq == 0x00 || ascq == 0x01 || ascq == 0x09))
+- scsi_rescan_device(&sdev->sdev_gendev);
++ scsi_rescan_device(sdev);
+
+ scsi_device_put(sdev);
+ }
+diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h
+index d27d9fb7174c8..16848def47a1d 100644
+--- a/include/scsi/scsi_host.h
++++ b/include/scsi/scsi_host.h
+@@ -752,7 +752,7 @@ extern int __must_check scsi_add_host_with_dma(struct Scsi_Host *,
+ struct device *,
+ struct device *);
+ extern void scsi_scan_host(struct Scsi_Host *);
+-extern void scsi_rescan_device(struct device *);
++extern void scsi_rescan_device(struct scsi_device *);
+ extern void scsi_remove_host(struct Scsi_Host *);
+ extern struct Scsi_Host *scsi_host_get(struct Scsi_Host *);
+ extern int scsi_host_busy(struct Scsi_Host *shost);
+--
+2.40.1
+
--- /dev/null
+From bc1853103b803f47e566a48893bfdac0dcd86f99 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 15 Sep 2023 15:00:13 +0900
+Subject: scsi: Do not attempt to rescan suspended devices
+
+From: Damien Le Moal <dlemoal@kernel.org>
+
+[ Upstream commit ff48b37802e5c134e2dfc4d091f10b2eb5065a72 ]
+
+scsi_rescan_device() takes a scsi device lock before executing a device
+handler and device driver rescan methods. Waiting for the completion of
+any command issued to the device by these methods will thus be done with
+the device lock held. As a result, there is a risk of deadlocking within
+the power management code if scsi_rescan_device() is called to handle a
+device resume with the associated scsi device not yet resumed.
+
+Avoid such situation by checking that the target scsi device is in the
+running state, that is, fully capable of executing commands, before
+proceeding with the rescan and bailout returning -EWOULDBLOCK otherwise.
+With this error return, the caller can retry rescaning the device after
+a delay.
+
+The state check is done with the device lock held and is thus safe
+against incoming suspend power management operations.
+
+Fixes: 6aa0365a3c85 ("ata: libata-scsi: Avoid deadlock on rescan after device resume")
+Cc: stable@vger.kernel.org
+Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
+Reviewed-by: Hannes Reinecke <hare@suse.de>
+Reviewed-by: Niklas Cassel <niklas.cassel@wdc.com>
+Tested-by: Geert Uytterhoeven <geert+renesas@glider.be>
+Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com>
+Reviewed-by: Bart Van Assche <bvanassche@acm.org>
+Stable-dep-of: 8b4d9469d0b0 ("ata: libata-scsi: Fix delayed scsi_rescan_device() execution")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/scsi/scsi_scan.c | 18 +++++++++++++++++-
+ include/scsi/scsi_host.h | 2 +-
+ 2 files changed, 18 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
+index 445989f44d3f2..ed26c52ed8474 100644
+--- a/drivers/scsi/scsi_scan.c
++++ b/drivers/scsi/scsi_scan.c
+@@ -1611,12 +1611,24 @@ int scsi_add_device(struct Scsi_Host *host, uint channel,
+ }
+ EXPORT_SYMBOL(scsi_add_device);
+
+-void scsi_rescan_device(struct scsi_device *sdev)
++int scsi_rescan_device(struct scsi_device *sdev)
+ {
+ struct device *dev = &sdev->sdev_gendev;
++ int ret = 0;
+
+ device_lock(dev);
+
++ /*
++ * Bail out if the device is not running. Otherwise, the rescan may
++ * block waiting for commands to be executed, with us holding the
++ * device lock. This can result in a potential deadlock in the power
++ * management core code when system resume is on-going.
++ */
++ if (sdev->sdev_state != SDEV_RUNNING) {
++ ret = -EWOULDBLOCK;
++ goto unlock;
++ }
++
+ scsi_attach_vpd(sdev);
+
+ if (sdev->handler && sdev->handler->rescan)
+@@ -1629,7 +1641,11 @@ void scsi_rescan_device(struct scsi_device *sdev)
+ drv->rescan(dev);
+ module_put(dev->driver->owner);
+ }
++
++unlock:
+ device_unlock(dev);
++
++ return ret;
+ }
+ EXPORT_SYMBOL(scsi_rescan_device);
+
+diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h
+index 16848def47a1d..71def41b1ad78 100644
+--- a/include/scsi/scsi_host.h
++++ b/include/scsi/scsi_host.h
+@@ -752,7 +752,7 @@ extern int __must_check scsi_add_host_with_dma(struct Scsi_Host *,
+ struct device *,
+ struct device *);
+ extern void scsi_scan_host(struct Scsi_Host *);
+-extern void scsi_rescan_device(struct scsi_device *);
++extern int scsi_rescan_device(struct scsi_device *sdev);
+ extern void scsi_remove_host(struct Scsi_Host *);
+ extern struct Scsi_Host *scsi_host_get(struct Scsi_Host *);
+ extern int scsi_host_busy(struct Scsi_Host *shost);
+--
+2.40.1
+
--- /dev/null
+From 3b3411e173405c039b240cffc037812620e3b00b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 15 Sep 2023 10:02:41 +0900
+Subject: scsi: sd: Differentiate system and runtime start/stop management
+
+From: Damien Le Moal <dlemoal@kernel.org>
+
+[ Upstream commit 3cc2ffe5c16dc65dfac354bc5b5bc98d3b397567 ]
+
+The underlying device and driver of a SCSI disk may have different
+system and runtime power mode control requirements. This is because
+runtime power management affects only the SCSI disk, while system level
+power management affects all devices, including the controller for the
+SCSI disk.
+
+For instance, issuing a START STOP UNIT command when a SCSI disk is
+runtime suspended and resumed is fine: the command is translated to a
+STANDBY IMMEDIATE command to spin down the ATA disk and to a VERIFY
+command to wake it up. The SCSI disk runtime operations have no effect
+on the ata port device used to connect the ATA disk. However, for
+system suspend/resume operations, the ATA port used to connect the
+device will also be suspended and resumed, with the resume operation
+requiring re-validating the device link and the device itself. In this
+case, issuing a VERIFY command to spinup the disk must be done before
+starting to revalidate the device, when the ata port is being resumed.
+In such case, we must not allow the SCSI disk driver to issue START STOP
+UNIT commands.
+
+Allow a low level driver to refine the SCSI disk start/stop management
+by differentiating system and runtime cases with two new SCSI device
+flags: manage_system_start_stop and manage_runtime_start_stop. These new
+flags replace the current manage_start_stop flag. Drivers setting the
+manage_start_stop are modifed to set both new flags, thus preserving the
+existing start/stop management behavior. For backward compatibility, the
+old manage_start_stop sysfs device attribute is kept as a read-only
+attribute showing a value of 1 for devices enabling both new flags and 0
+otherwise.
+
+Fixes: 0a8589055936 ("ata,scsi: do not issue START STOP UNIT on resume")
+Cc: stable@vger.kernel.org
+Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
+Reviewed-by: Hannes Reinecke <hare@suse.de>
+Tested-by: Geert Uytterhoeven <geert+renesas@glider.be>
+Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com>
+Stable-dep-of: 99398d2070ab ("scsi: sd: Do not issue commands to suspended disks on shutdown")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/ata/libata-scsi.c | 3 +-
+ drivers/firewire/sbp2.c | 9 ++--
+ drivers/scsi/sd.c | 90 ++++++++++++++++++++++++++++++--------
+ include/scsi/scsi_device.h | 5 ++-
+ 4 files changed, 84 insertions(+), 23 deletions(-)
+
+diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
+index 9c8dd9f86cbb3..8cc8268327f0c 100644
+--- a/drivers/ata/libata-scsi.c
++++ b/drivers/ata/libata-scsi.c
+@@ -1087,7 +1087,8 @@ int ata_scsi_dev_config(struct scsi_device *sdev, struct ata_device *dev)
+ * will be woken up by ata_port_pm_resume() with a port reset
+ * and device revalidation.
+ */
+- sdev->manage_start_stop = 1;
++ sdev->manage_system_start_stop = true;
++ sdev->manage_runtime_start_stop = true;
+ sdev->no_start_on_resume = 1;
+ }
+
+diff --git a/drivers/firewire/sbp2.c b/drivers/firewire/sbp2.c
+index 60051c0cabeaa..e322a326546b5 100644
+--- a/drivers/firewire/sbp2.c
++++ b/drivers/firewire/sbp2.c
+@@ -81,7 +81,8 @@ MODULE_PARM_DESC(exclusive_login, "Exclusive login to sbp2 device "
+ *
+ * - power condition
+ * Set the power condition field in the START STOP UNIT commands sent by
+- * sd_mod on suspend, resume, and shutdown (if manage_start_stop is on).
++ * sd_mod on suspend, resume, and shutdown (if manage_system_start_stop or
++ * manage_runtime_start_stop is on).
+ * Some disks need this to spin down or to resume properly.
+ *
+ * - override internal blacklist
+@@ -1517,8 +1518,10 @@ static int sbp2_scsi_slave_configure(struct scsi_device *sdev)
+
+ sdev->use_10_for_rw = 1;
+
+- if (sbp2_param_exclusive_login)
+- sdev->manage_start_stop = 1;
++ if (sbp2_param_exclusive_login) {
++ sdev->manage_system_start_stop = true;
++ sdev->manage_runtime_start_stop = true;
++ }
+
+ if (sdev->type == TYPE_ROM)
+ sdev->use_10_for_ms = 1;
+diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
+index 5bfca49415113..2ed57dfaf9ee0 100644
+--- a/drivers/scsi/sd.c
++++ b/drivers/scsi/sd.c
+@@ -213,18 +213,32 @@ cache_type_store(struct device *dev, struct device_attribute *attr,
+ }
+
+ static ssize_t
+-manage_start_stop_show(struct device *dev, struct device_attribute *attr,
+- char *buf)
++manage_start_stop_show(struct device *dev,
++ struct device_attribute *attr, char *buf)
+ {
+ struct scsi_disk *sdkp = to_scsi_disk(dev);
+ struct scsi_device *sdp = sdkp->device;
+
+- return sprintf(buf, "%u\n", sdp->manage_start_stop);
++ return sysfs_emit(buf, "%u\n",
++ sdp->manage_system_start_stop &&
++ sdp->manage_runtime_start_stop);
+ }
++static DEVICE_ATTR_RO(manage_start_stop);
+
+ static ssize_t
+-manage_start_stop_store(struct device *dev, struct device_attribute *attr,
+- const char *buf, size_t count)
++manage_system_start_stop_show(struct device *dev,
++ struct device_attribute *attr, char *buf)
++{
++ struct scsi_disk *sdkp = to_scsi_disk(dev);
++ struct scsi_device *sdp = sdkp->device;
++
++ return sysfs_emit(buf, "%u\n", sdp->manage_system_start_stop);
++}
++
++static ssize_t
++manage_system_start_stop_store(struct device *dev,
++ struct device_attribute *attr,
++ const char *buf, size_t count)
+ {
+ struct scsi_disk *sdkp = to_scsi_disk(dev);
+ struct scsi_device *sdp = sdkp->device;
+@@ -236,11 +250,42 @@ manage_start_stop_store(struct device *dev, struct device_attribute *attr,
+ if (kstrtobool(buf, &v))
+ return -EINVAL;
+
+- sdp->manage_start_stop = v;
++ sdp->manage_system_start_stop = v;
+
+ return count;
+ }
+-static DEVICE_ATTR_RW(manage_start_stop);
++static DEVICE_ATTR_RW(manage_system_start_stop);
++
++static ssize_t
++manage_runtime_start_stop_show(struct device *dev,
++ struct device_attribute *attr, char *buf)
++{
++ struct scsi_disk *sdkp = to_scsi_disk(dev);
++ struct scsi_device *sdp = sdkp->device;
++
++ return sysfs_emit(buf, "%u\n", sdp->manage_runtime_start_stop);
++}
++
++static ssize_t
++manage_runtime_start_stop_store(struct device *dev,
++ struct device_attribute *attr,
++ const char *buf, size_t count)
++{
++ struct scsi_disk *sdkp = to_scsi_disk(dev);
++ struct scsi_device *sdp = sdkp->device;
++ bool v;
++
++ if (!capable(CAP_SYS_ADMIN))
++ return -EACCES;
++
++ if (kstrtobool(buf, &v))
++ return -EINVAL;
++
++ sdp->manage_runtime_start_stop = v;
++
++ return count;
++}
++static DEVICE_ATTR_RW(manage_runtime_start_stop);
+
+ static ssize_t
+ allow_restart_show(struct device *dev, struct device_attribute *attr, char *buf)
+@@ -572,6 +617,8 @@ static struct attribute *sd_disk_attrs[] = {
+ &dev_attr_FUA.attr,
+ &dev_attr_allow_restart.attr,
+ &dev_attr_manage_start_stop.attr,
++ &dev_attr_manage_system_start_stop.attr,
++ &dev_attr_manage_runtime_start_stop.attr,
+ &dev_attr_protection_type.attr,
+ &dev_attr_protection_mode.attr,
+ &dev_attr_app_tag_own.attr,
+@@ -3652,13 +3699,20 @@ static void sd_shutdown(struct device *dev)
+ sd_sync_cache(sdkp, NULL);
+ }
+
+- if (system_state != SYSTEM_RESTART && sdkp->device->manage_start_stop) {
++ if (system_state != SYSTEM_RESTART &&
++ sdkp->device->manage_system_start_stop) {
+ sd_printk(KERN_NOTICE, sdkp, "Stopping disk\n");
+ sd_start_stop_device(sdkp, 0);
+ }
+ }
+
+-static int sd_suspend_common(struct device *dev, bool ignore_stop_errors)
++static inline bool sd_do_start_stop(struct scsi_device *sdev, bool runtime)
++{
++ return (sdev->manage_system_start_stop && !runtime) ||
++ (sdev->manage_runtime_start_stop && runtime);
++}
++
++static int sd_suspend_common(struct device *dev, bool runtime)
+ {
+ struct scsi_disk *sdkp = dev_get_drvdata(dev);
+ struct scsi_sense_hdr sshdr;
+@@ -3690,12 +3744,12 @@ static int sd_suspend_common(struct device *dev, bool ignore_stop_errors)
+ }
+ }
+
+- if (sdkp->device->manage_start_stop) {
++ if (sd_do_start_stop(sdkp->device, runtime)) {
+ if (!sdkp->device->silence_suspend)
+ sd_printk(KERN_NOTICE, sdkp, "Stopping disk\n");
+ /* an error is not worth aborting a system sleep */
+ ret = sd_start_stop_device(sdkp, 0);
+- if (ignore_stop_errors)
++ if (!runtime)
+ ret = 0;
+ }
+
+@@ -3707,23 +3761,23 @@ static int sd_suspend_system(struct device *dev)
+ if (pm_runtime_suspended(dev))
+ return 0;
+
+- return sd_suspend_common(dev, true);
++ return sd_suspend_common(dev, false);
+ }
+
+ static int sd_suspend_runtime(struct device *dev)
+ {
+- return sd_suspend_common(dev, false);
++ return sd_suspend_common(dev, true);
+ }
+
+-static int sd_resume(struct device *dev)
++static int sd_resume(struct device *dev, bool runtime)
+ {
+ struct scsi_disk *sdkp = dev_get_drvdata(dev);
+- int ret = 0;
++ int ret;
+
+ if (!sdkp) /* E.g.: runtime resume at the start of sd_probe() */
+ return 0;
+
+- if (!sdkp->device->manage_start_stop)
++ if (!sd_do_start_stop(sdkp->device, runtime))
+ return 0;
+
+ if (!sdkp->device->no_start_on_resume) {
+@@ -3741,7 +3795,7 @@ static int sd_resume_system(struct device *dev)
+ if (pm_runtime_suspended(dev))
+ return 0;
+
+- return sd_resume(dev);
++ return sd_resume(dev, false);
+ }
+
+ static int sd_resume_runtime(struct device *dev)
+@@ -3765,7 +3819,7 @@ static int sd_resume_runtime(struct device *dev)
+ "Failed to clear sense data\n");
+ }
+
+- return sd_resume(dev);
++ return sd_resume(dev, true);
+ }
+
+ /**
+diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h
+index 9fdc77db3a2a8..dc2cff18b68bd 100644
+--- a/include/scsi/scsi_device.h
++++ b/include/scsi/scsi_device.h
+@@ -161,6 +161,10 @@ struct scsi_device {
+ * pass settings from slave_alloc to scsi
+ * core. */
+ unsigned int eh_timeout; /* Error handling timeout */
++
++ bool manage_system_start_stop; /* Let HLD (sd) manage system start/stop */
++ bool manage_runtime_start_stop; /* Let HLD (sd) manage runtime start/stop */
++
+ unsigned removable:1;
+ unsigned changed:1; /* Data invalid due to media change */
+ unsigned busy:1; /* Used to prevent races */
+@@ -192,7 +196,6 @@ struct scsi_device {
+ unsigned use_192_bytes_for_3f:1; /* ask for 192 bytes from page 0x3f */
+ unsigned no_start_on_add:1; /* do not issue start on add */
+ unsigned allow_restart:1; /* issue START_UNIT in error handler */
+- unsigned manage_start_stop:1; /* Let HLD (sd) manage start/stop */
+ unsigned no_start_on_resume:1; /* Do not issue START_STOP_UNIT on resume */
+ unsigned start_stop_pwr_cond:1; /* Set power cond. in START_STOP_UNIT */
+ unsigned no_uld_attach:1; /* disable connecting to upper level drivers */
+--
+2.40.1
+
--- /dev/null
+From 29c60a5e17517ecf23bd2258340ac055e6ef1832 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 8 Sep 2023 17:03:15 +0900
+Subject: scsi: sd: Do not issue commands to suspended disks on shutdown
+
+From: Damien Le Moal <dlemoal@kernel.org>
+
+[ Upstream commit 99398d2070ab03d13f90b758ad397e19a65fffb0 ]
+
+If an error occurs when resuming a host adapter before the devices
+attached to the adapter are resumed, the adapter low level driver may
+remove the scsi host, resulting in a call to sd_remove() for the
+disks of the host. This in turn results in a call to sd_shutdown() which
+will issue a synchronize cache command and a start stop unit command to
+spindown the disk. sd_shutdown() issues the commands only if the device
+is not already runtime suspended but does not check the power state for
+system-wide suspend/resume. That is, the commands may be issued with the
+device in a suspended state, which causes PM resume to hang, forcing a
+reset of the machine to recover.
+
+Fix this by tracking the suspended state of a disk by introducing the
+suspended boolean field in the scsi_disk structure. This flag is set to
+true when the disk is suspended is sd_suspend_common() and resumed with
+sd_resume(). When suspended is true, sd_shutdown() is not executed from
+sd_remove().
+
+Cc: stable@vger.kernel.org
+Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
+Reviewed-by: Hannes Reinecke <hare@suse.de>
+Reviewed-by: Bart Van Assche <bvanassche@acm.org>
+Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/scsi/sd.c | 17 +++++++++++++----
+ drivers/scsi/sd.h | 1 +
+ 2 files changed, 14 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
+index 2ed57dfaf9ee0..30184f7b762c1 100644
+--- a/drivers/scsi/sd.c
++++ b/drivers/scsi/sd.c
+@@ -3626,7 +3626,8 @@ static int sd_remove(struct device *dev)
+
+ device_del(&sdkp->disk_dev);
+ del_gendisk(sdkp->disk);
+- sd_shutdown(dev);
++ if (!sdkp->suspended)
++ sd_shutdown(dev);
+
+ put_disk(sdkp->disk);
+ return 0;
+@@ -3753,6 +3754,9 @@ static int sd_suspend_common(struct device *dev, bool runtime)
+ ret = 0;
+ }
+
++ if (!ret)
++ sdkp->suspended = true;
++
+ return ret;
+ }
+
+@@ -3772,21 +3776,26 @@ static int sd_suspend_runtime(struct device *dev)
+ static int sd_resume(struct device *dev, bool runtime)
+ {
+ struct scsi_disk *sdkp = dev_get_drvdata(dev);
+- int ret;
++ int ret = 0;
+
+ if (!sdkp) /* E.g.: runtime resume at the start of sd_probe() */
+ return 0;
+
+- if (!sd_do_start_stop(sdkp->device, runtime))
++ if (!sd_do_start_stop(sdkp->device, runtime)) {
++ sdkp->suspended = false;
+ return 0;
++ }
+
+ if (!sdkp->device->no_start_on_resume) {
+ sd_printk(KERN_NOTICE, sdkp, "Starting disk\n");
+ ret = sd_start_stop_device(sdkp, 1);
+ }
+
+- if (!ret)
++ if (!ret) {
+ opal_unlock_from_suspend(sdkp->opal_dev);
++ sdkp->suspended = false;
++ }
++
+ return ret;
+ }
+
+diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h
+index 5eea762f84d18..409dda5350d10 100644
+--- a/drivers/scsi/sd.h
++++ b/drivers/scsi/sd.h
+@@ -131,6 +131,7 @@ struct scsi_disk {
+ u8 provisioning_mode;
+ u8 zeroing_mode;
+ u8 nr_actuators; /* Number of actuators */
++ bool suspended; /* Disk is suspended (stopped) */
+ unsigned ATO : 1; /* state of disk ATO bit */
+ unsigned cache_override : 1; /* temp override of WCE,RCD */
+ unsigned WCE : 1; /* state of disk WCE bit */
+--
+2.40.1
+
--- /dev/null
+spi-zynqmp-gqspi-convert-to-platform-remove-callback.patch
+spi-zynqmp-gqspi-fix-clock-imbalance-on-probe-failur.patch
+alsa-hda-tas2781-add-tas2781-hda-driver.patch
+alsa-hda-realtek-add-quirk-for-hp-victus-16-d1xxx-to.patch
+alsa-hda-realtek-add-quirk-for-mute-leds-on-hp-envy-.patch
+alsa-hda-realtek-alc287-i2s-speaker-platform-support.patch
+alsa-hda-realtek-alc287-realtek-i2s-speaker-platform.patch
+asoc-soc-utils-export-snd_soc_dai_is_dummy-symbol.patch
+asoc-tegra-fix-redundant-plla-and-plla_out0-updates.patch
+maple_tree-remove-the-redundant-code.patch
+maple_tree-relocate-the-declaration-of-mas_empty_are.patch
+maple_tree-add-mas_is_active-to-detect-in-tree-walks.patch
+mptcp-rename-timer-related-helper-to-less-confusing-.patch
+mptcp-fix-dangling-connection-hang-up.patch
+mptcp-annotate-lockless-accesses-to-sk-sk_err.patch
+mptcp-move-__mptcp_error_report-in-protocol.c.patch
+mptcp-process-pending-subflow-error-on-close.patch
+ata-scsi-do-not-issue-start-stop-unit-on-resume.patch
+scsi-sd-differentiate-system-and-runtime-start-stop-.patch
+scsi-sd-do-not-issue-commands-to-suspended-disks-on-.patch
+scsi-core-improve-type-safety-of-scsi_rescan_device.patch
+scsi-do-not-attempt-to-rescan-suspended-devices.patch
+ata-libata-scsi-fix-delayed-scsi_rescan_device-execu.patch
+nfs-cleanup-unused-rpc_clnt-variable.patch
+nfs-rename-nfs_client_kset-to-nfs_kset.patch
+nfsv4-fix-a-state-manager-thread-deadlock-regression.patch
+mm-memory-add-vm_normal_folio.patch
+mm-mempolicy-convert-queue_pages_pmd-to-queue_folios.patch
+mm-mempolicy-convert-queue_pages_pte_range-to-queue_.patch
+mm-mempolicy-convert-migrate_page_add-to-migrate_fol.patch
+mm-mempolicy-keep-vma-walk-if-both-mpol_mf_strict-an.patch
+mm-page_alloc-always-remove-pages-from-temporary-lis.patch
+mm-page_alloc-leave-irqs-enabled-for-per-cpu-page-al.patch
+mm-page_alloc-fix-cma-and-highatomic-landing-on-the-.patch
+ring-buffer-remove-obsolete-comment-for-free_buffer_.patch
+ring-buffer-fix-bytes-info-in-per_cpu-buffer-stats.patch
+btrfs-use-struct-qstr-instead-of-name-and-namelen-pa.patch
+btrfs-setup-qstr-from-dentrys-using-fscrypt-helper.patch
+btrfs-use-struct-fscrypt_str-instead-of-struct-qstr.patch
+revert-nfsv4-retry-lock-on-old_stateid-during-delega.patch
+arm64-avoid-repeated-aa64mmfr1_el1-register-read-on-.patch
--- /dev/null
+From a0dd03214a34d747df5a47cacec49c4693890454 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 3 Mar 2023 18:20:41 +0100
+Subject: spi: zynqmp-gqspi: Convert to platform remove callback returning void
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
+
+[ Upstream commit 3ffefa1d9c9eba60c7f8b4a9ce2df3e4c7f4a88e ]
+
+The .remove() callback for a platform driver returns an int which makes
+many driver authors wrongly assume it's possible to do error handling by
+returning an error code. However the value returned is (mostly) ignored
+and this typically results in resource leaks. To improve here there is a
+quest to make the remove callback return void. In the first step of this
+quest all drivers are converted to .remove_new() which already returns
+void.
+
+Trivially convert this driver from always returning zero in the remove
+callback to the void returning variant.
+
+Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
+Link: https://lore.kernel.org/r/20230303172041.2103336-88-u.kleine-koenig@pengutronix.de
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Stable-dep-of: 1527b076ae2c ("spi: zynqmp-gqspi: fix clock imbalance on probe failure")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/spi/spi-zynqmp-gqspi.c | 6 ++----
+ 1 file changed, 2 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/spi/spi-zynqmp-gqspi.c b/drivers/spi/spi-zynqmp-gqspi.c
+index c760aac070e54..876a41c5d1664 100644
+--- a/drivers/spi/spi-zynqmp-gqspi.c
++++ b/drivers/spi/spi-zynqmp-gqspi.c
+@@ -1240,7 +1240,7 @@ static int zynqmp_qspi_probe(struct platform_device *pdev)
+ *
+ * Return: 0 Always
+ */
+-static int zynqmp_qspi_remove(struct platform_device *pdev)
++static void zynqmp_qspi_remove(struct platform_device *pdev)
+ {
+ struct zynqmp_qspi *xqspi = platform_get_drvdata(pdev);
+
+@@ -1249,8 +1249,6 @@ static int zynqmp_qspi_remove(struct platform_device *pdev)
+ clk_disable_unprepare(xqspi->pclk);
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+-
+- return 0;
+ }
+
+ static const struct of_device_id zynqmp_qspi_of_match[] = {
+@@ -1262,7 +1260,7 @@ MODULE_DEVICE_TABLE(of, zynqmp_qspi_of_match);
+
+ static struct platform_driver zynqmp_qspi_driver = {
+ .probe = zynqmp_qspi_probe,
+- .remove = zynqmp_qspi_remove,
++ .remove_new = zynqmp_qspi_remove,
+ .driver = {
+ .name = "zynqmp-qspi",
+ .of_match_table = zynqmp_qspi_of_match,
+--
+2.40.1
+
--- /dev/null
+From bc5eee6846668dfa31bf34227ce977ed041fd14d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 22 Jun 2023 10:24:35 +0200
+Subject: spi: zynqmp-gqspi: fix clock imbalance on probe failure
+
+From: Johan Hovold <johan+linaro@kernel.org>
+
+[ Upstream commit 1527b076ae2cb6a9c590a02725ed39399fcad1cf ]
+
+Make sure that the device is not runtime suspended before explicitly
+disabling the clocks on probe failure and on driver unbind to avoid a
+clock enable-count imbalance.
+
+Fixes: 9e3a000362ae ("spi: zynqmp: Add pm runtime support")
+Cc: stable@vger.kernel.org # 4.19
+Cc: Naga Sureshkumar Relli <naga.sureshkumar.relli@xilinx.com>
+Cc: Shubhrajyoti Datta <shubhrajyoti.datta@xilinx.com>
+Signed-off-by: Johan Hovold <johan+linaro@kernel.org>
+Link: https://lore.kernel.org/r/Message-Id: <20230622082435.7873-1-johan+linaro@kernel.org>
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/spi/spi-zynqmp-gqspi.c | 12 ++++++++----
+ 1 file changed, 8 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/spi/spi-zynqmp-gqspi.c b/drivers/spi/spi-zynqmp-gqspi.c
+index 876a41c5d1664..f2dcd1ae77c7d 100644
+--- a/drivers/spi/spi-zynqmp-gqspi.c
++++ b/drivers/spi/spi-zynqmp-gqspi.c
+@@ -1218,9 +1218,9 @@ static int zynqmp_qspi_probe(struct platform_device *pdev)
+ return 0;
+
+ clk_dis_all:
+- pm_runtime_put_sync(&pdev->dev);
+- pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
++ pm_runtime_put_noidle(&pdev->dev);
++ pm_runtime_set_suspended(&pdev->dev);
+ clk_disable_unprepare(xqspi->refclk);
+ clk_dis_pclk:
+ clk_disable_unprepare(xqspi->pclk);
+@@ -1244,11 +1244,15 @@ static void zynqmp_qspi_remove(struct platform_device *pdev)
+ {
+ struct zynqmp_qspi *xqspi = platform_get_drvdata(pdev);
+
++ pm_runtime_get_sync(&pdev->dev);
++
+ zynqmp_gqspi_write(xqspi, GQSPI_EN_OFST, 0x0);
++
++ pm_runtime_disable(&pdev->dev);
++ pm_runtime_put_noidle(&pdev->dev);
++ pm_runtime_set_suspended(&pdev->dev);
+ clk_disable_unprepare(xqspi->refclk);
+ clk_disable_unprepare(xqspi->pclk);
+- pm_runtime_set_suspended(&pdev->dev);
+- pm_runtime_disable(&pdev->dev);
+ }
+
+ static const struct of_device_id zynqmp_qspi_of_match[] = {
+--
+2.40.1
+