2651 lines
85 KiB
Diff
2651 lines
85 KiB
Diff
diff --git a/Makefile b/Makefile
|
|
index bc58f206c0da..c6b680faedd8 100644
|
|
--- a/Makefile
|
|
+++ b/Makefile
|
|
@@ -1,6 +1,6 @@
|
|
VERSION = 4
|
|
PATCHLEVEL = 4
|
|
-SUBLEVEL = 170
|
|
+SUBLEVEL = 171
|
|
EXTRAVERSION =
|
|
NAME = Blurry Fish Butt
|
|
|
|
diff --git a/crypto/cts.c b/crypto/cts.c
|
|
index e467ec0acf9f..e65688d6a4ca 100644
|
|
--- a/crypto/cts.c
|
|
+++ b/crypto/cts.c
|
|
@@ -137,8 +137,8 @@ static int crypto_cts_encrypt(struct blkcipher_desc *desc,
|
|
lcldesc.info = desc->info;
|
|
lcldesc.flags = desc->flags;
|
|
|
|
- if (tot_blocks == 1) {
|
|
- err = crypto_blkcipher_encrypt_iv(&lcldesc, dst, src, bsize);
|
|
+ if (tot_blocks <= 1) {
|
|
+ err = crypto_blkcipher_encrypt_iv(&lcldesc, dst, src, nbytes);
|
|
} else if (nbytes <= bsize * 2) {
|
|
err = cts_cbc_encrypt(ctx, desc, dst, src, 0, nbytes);
|
|
} else {
|
|
@@ -232,8 +232,8 @@ static int crypto_cts_decrypt(struct blkcipher_desc *desc,
|
|
lcldesc.info = desc->info;
|
|
lcldesc.flags = desc->flags;
|
|
|
|
- if (tot_blocks == 1) {
|
|
- err = crypto_blkcipher_decrypt_iv(&lcldesc, dst, src, bsize);
|
|
+ if (tot_blocks <= 1) {
|
|
+ err = crypto_blkcipher_decrypt_iv(&lcldesc, dst, src, nbytes);
|
|
} else if (nbytes <= bsize * 2) {
|
|
err = cts_cbc_decrypt(ctx, desc, dst, src, 0, nbytes);
|
|
} else {
|
|
diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c
|
|
index 1c2b846c5776..f28b4949cb9d 100644
|
|
--- a/drivers/acpi/power.c
|
|
+++ b/drivers/acpi/power.c
|
|
@@ -131,6 +131,23 @@ void acpi_power_resources_list_free(struct list_head *list)
|
|
}
|
|
}
|
|
|
|
+static bool acpi_power_resource_is_dup(union acpi_object *package,
|
|
+ unsigned int start, unsigned int i)
|
|
+{
|
|
+ acpi_handle rhandle, dup;
|
|
+ unsigned int j;
|
|
+
|
|
+ /* The caller is expected to check the package element types */
|
|
+ rhandle = package->package.elements[i].reference.handle;
|
|
+ for (j = start; j < i; j++) {
|
|
+ dup = package->package.elements[j].reference.handle;
|
|
+ if (dup == rhandle)
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ return false;
|
|
+}
|
|
+
|
|
int acpi_extract_power_resources(union acpi_object *package, unsigned int start,
|
|
struct list_head *list)
|
|
{
|
|
@@ -150,6 +167,11 @@ int acpi_extract_power_resources(union acpi_object *package, unsigned int start,
|
|
err = -ENODEV;
|
|
break;
|
|
}
|
|
+
|
|
+ /* Some ACPI tables contain duplicate power resource references */
|
|
+ if (acpi_power_resource_is_dup(package, start, i))
|
|
+ continue;
|
|
+
|
|
err = acpi_add_power_resource(rhandle);
|
|
if (err)
|
|
break;
|
|
diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c
|
|
index 94c837046786..57e3790c87b1 100644
|
|
--- a/drivers/i2c/i2c-dev.c
|
|
+++ b/drivers/i2c/i2c-dev.c
|
|
@@ -459,9 +459,15 @@ static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|
return i2cdev_ioctl_smbus(client, arg);
|
|
|
|
case I2C_RETRIES:
|
|
+ if (arg > INT_MAX)
|
|
+ return -EINVAL;
|
|
+
|
|
client->adapter->retries = arg;
|
|
break;
|
|
case I2C_TIMEOUT:
|
|
+ if (arg > INT_MAX)
|
|
+ return -EINVAL;
|
|
+
|
|
/* For historical reasons, user-space sets the timeout
|
|
* value in units of 10 ms.
|
|
*/
|
|
diff --git a/drivers/pci/host/pcie-altera.c b/drivers/pci/host/pcie-altera.c
|
|
index 99da549d5d06..0118287a8a10 100644
|
|
--- a/drivers/pci/host/pcie-altera.c
|
|
+++ b/drivers/pci/host/pcie-altera.c
|
|
@@ -40,8 +40,10 @@
|
|
#define P2A_INT_ENABLE 0x3070
|
|
#define P2A_INT_ENA_ALL 0xf
|
|
#define RP_LTSSM 0x3c64
|
|
+#define RP_LTSSM_MASK 0x1f
|
|
#define LTSSM_L0 0xf
|
|
|
|
+#define PCIE_CAP_OFFSET 0x80
|
|
/* TLP configuration type 0 and 1 */
|
|
#define TLP_FMTTYPE_CFGRD0 0x04 /* Configuration Read Type 0 */
|
|
#define TLP_FMTTYPE_CFGWR0 0x44 /* Configuration Write Type 0 */
|
|
@@ -60,6 +62,9 @@
|
|
#define TLP_LOOP 500
|
|
#define RP_DEVFN 0
|
|
|
|
+#define LINK_UP_TIMEOUT HZ
|
|
+#define LINK_RETRAIN_TIMEOUT HZ
|
|
+
|
|
#define INTX_NUM 4
|
|
|
|
#define DWORD_MASK 3
|
|
@@ -80,25 +85,21 @@ struct tlp_rp_regpair_t {
|
|
u32 reg1;
|
|
};
|
|
|
|
-static void altera_pcie_retrain(struct pci_dev *dev)
|
|
+static inline void cra_writel(struct altera_pcie *pcie, const u32 value,
|
|
+ const u32 reg)
|
|
{
|
|
- u16 linkcap, linkstat;
|
|
-
|
|
- /*
|
|
- * Set the retrain bit if the PCIe rootport support > 2.5GB/s, but
|
|
- * current speed is 2.5 GB/s.
|
|
- */
|
|
- pcie_capability_read_word(dev, PCI_EXP_LNKCAP, &linkcap);
|
|
+ writel_relaxed(value, pcie->cra_base + reg);
|
|
+}
|
|
|
|
- if ((linkcap & PCI_EXP_LNKCAP_SLS) <= PCI_EXP_LNKCAP_SLS_2_5GB)
|
|
- return;
|
|
+static inline u32 cra_readl(struct altera_pcie *pcie, const u32 reg)
|
|
+{
|
|
+ return readl_relaxed(pcie->cra_base + reg);
|
|
+}
|
|
|
|
- pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &linkstat);
|
|
- if ((linkstat & PCI_EXP_LNKSTA_CLS) == PCI_EXP_LNKSTA_CLS_2_5GB)
|
|
- pcie_capability_set_word(dev, PCI_EXP_LNKCTL,
|
|
- PCI_EXP_LNKCTL_RL);
|
|
+static bool altera_pcie_link_is_up(struct altera_pcie *pcie)
|
|
+{
|
|
+ return !!((cra_readl(pcie, RP_LTSSM) & RP_LTSSM_MASK) == LTSSM_L0);
|
|
}
|
|
-DECLARE_PCI_FIXUP_EARLY(0x1172, PCI_ANY_ID, altera_pcie_retrain);
|
|
|
|
/*
|
|
* Altera PCIe port uses BAR0 of RC's configuration space as the translation
|
|
@@ -119,17 +120,6 @@ static bool altera_pcie_hide_rc_bar(struct pci_bus *bus, unsigned int devfn,
|
|
return false;
|
|
}
|
|
|
|
-static inline void cra_writel(struct altera_pcie *pcie, const u32 value,
|
|
- const u32 reg)
|
|
-{
|
|
- writel_relaxed(value, pcie->cra_base + reg);
|
|
-}
|
|
-
|
|
-static inline u32 cra_readl(struct altera_pcie *pcie, const u32 reg)
|
|
-{
|
|
- return readl_relaxed(pcie->cra_base + reg);
|
|
-}
|
|
-
|
|
static void tlp_write_tx(struct altera_pcie *pcie,
|
|
struct tlp_rp_regpair_t *tlp_rp_regdata)
|
|
{
|
|
@@ -138,11 +128,6 @@ static void tlp_write_tx(struct altera_pcie *pcie,
|
|
cra_writel(pcie, tlp_rp_regdata->ctrl, RP_TX_CNTRL);
|
|
}
|
|
|
|
-static bool altera_pcie_link_is_up(struct altera_pcie *pcie)
|
|
-{
|
|
- return !!(cra_readl(pcie, RP_LTSSM) & LTSSM_L0);
|
|
-}
|
|
-
|
|
static bool altera_pcie_valid_config(struct altera_pcie *pcie,
|
|
struct pci_bus *bus, int dev)
|
|
{
|
|
@@ -286,22 +271,14 @@ static int tlp_cfg_dword_write(struct altera_pcie *pcie, u8 bus, u32 devfn,
|
|
return PCIBIOS_SUCCESSFUL;
|
|
}
|
|
|
|
-static int altera_pcie_cfg_read(struct pci_bus *bus, unsigned int devfn,
|
|
- int where, int size, u32 *value)
|
|
+static int _altera_pcie_cfg_read(struct altera_pcie *pcie, u8 busno,
|
|
+ unsigned int devfn, int where, int size,
|
|
+ u32 *value)
|
|
{
|
|
- struct altera_pcie *pcie = bus->sysdata;
|
|
int ret;
|
|
u32 data;
|
|
u8 byte_en;
|
|
|
|
- if (altera_pcie_hide_rc_bar(bus, devfn, where))
|
|
- return PCIBIOS_BAD_REGISTER_NUMBER;
|
|
-
|
|
- if (!altera_pcie_valid_config(pcie, bus, PCI_SLOT(devfn))) {
|
|
- *value = 0xffffffff;
|
|
- return PCIBIOS_DEVICE_NOT_FOUND;
|
|
- }
|
|
-
|
|
switch (size) {
|
|
case 1:
|
|
byte_en = 1 << (where & 3);
|
|
@@ -314,7 +291,7 @@ static int altera_pcie_cfg_read(struct pci_bus *bus, unsigned int devfn,
|
|
break;
|
|
}
|
|
|
|
- ret = tlp_cfg_dword_read(pcie, bus->number, devfn,
|
|
+ ret = tlp_cfg_dword_read(pcie, busno, devfn,
|
|
(where & ~DWORD_MASK), byte_en, &data);
|
|
if (ret != PCIBIOS_SUCCESSFUL)
|
|
return ret;
|
|
@@ -334,20 +311,14 @@ static int altera_pcie_cfg_read(struct pci_bus *bus, unsigned int devfn,
|
|
return PCIBIOS_SUCCESSFUL;
|
|
}
|
|
|
|
-static int altera_pcie_cfg_write(struct pci_bus *bus, unsigned int devfn,
|
|
- int where, int size, u32 value)
|
|
+static int _altera_pcie_cfg_write(struct altera_pcie *pcie, u8 busno,
|
|
+ unsigned int devfn, int where, int size,
|
|
+ u32 value)
|
|
{
|
|
- struct altera_pcie *pcie = bus->sysdata;
|
|
u32 data32;
|
|
u32 shift = 8 * (where & 3);
|
|
u8 byte_en;
|
|
|
|
- if (altera_pcie_hide_rc_bar(bus, devfn, where))
|
|
- return PCIBIOS_BAD_REGISTER_NUMBER;
|
|
-
|
|
- if (!altera_pcie_valid_config(pcie, bus, PCI_SLOT(devfn)))
|
|
- return PCIBIOS_DEVICE_NOT_FOUND;
|
|
-
|
|
switch (size) {
|
|
case 1:
|
|
data32 = (value & 0xff) << shift;
|
|
@@ -363,8 +334,40 @@ static int altera_pcie_cfg_write(struct pci_bus *bus, unsigned int devfn,
|
|
break;
|
|
}
|
|
|
|
- return tlp_cfg_dword_write(pcie, bus->number, devfn,
|
|
- (where & ~DWORD_MASK), byte_en, data32);
|
|
+ return tlp_cfg_dword_write(pcie, busno, devfn, (where & ~DWORD_MASK),
|
|
+ byte_en, data32);
|
|
+}
|
|
+
|
|
+static int altera_pcie_cfg_read(struct pci_bus *bus, unsigned int devfn,
|
|
+ int where, int size, u32 *value)
|
|
+{
|
|
+ struct altera_pcie *pcie = bus->sysdata;
|
|
+
|
|
+ if (altera_pcie_hide_rc_bar(bus, devfn, where))
|
|
+ return PCIBIOS_BAD_REGISTER_NUMBER;
|
|
+
|
|
+ if (!altera_pcie_valid_config(pcie, bus, PCI_SLOT(devfn))) {
|
|
+ *value = 0xffffffff;
|
|
+ return PCIBIOS_DEVICE_NOT_FOUND;
|
|
+ }
|
|
+
|
|
+ return _altera_pcie_cfg_read(pcie, bus->number, devfn, where, size,
|
|
+ value);
|
|
+}
|
|
+
|
|
+static int altera_pcie_cfg_write(struct pci_bus *bus, unsigned int devfn,
|
|
+ int where, int size, u32 value)
|
|
+{
|
|
+ struct altera_pcie *pcie = bus->sysdata;
|
|
+
|
|
+ if (altera_pcie_hide_rc_bar(bus, devfn, where))
|
|
+ return PCIBIOS_BAD_REGISTER_NUMBER;
|
|
+
|
|
+ if (!altera_pcie_valid_config(pcie, bus, PCI_SLOT(devfn)))
|
|
+ return PCIBIOS_DEVICE_NOT_FOUND;
|
|
+
|
|
+ return _altera_pcie_cfg_write(pcie, bus->number, devfn, where, size,
|
|
+ value);
|
|
}
|
|
|
|
static struct pci_ops altera_pcie_ops = {
|
|
@@ -372,6 +375,90 @@ static struct pci_ops altera_pcie_ops = {
|
|
.write = altera_pcie_cfg_write,
|
|
};
|
|
|
|
+static int altera_read_cap_word(struct altera_pcie *pcie, u8 busno,
|
|
+ unsigned int devfn, int offset, u16 *value)
|
|
+{
|
|
+ u32 data;
|
|
+ int ret;
|
|
+
|
|
+ ret = _altera_pcie_cfg_read(pcie, busno, devfn,
|
|
+ PCIE_CAP_OFFSET + offset, sizeof(*value),
|
|
+ &data);
|
|
+ *value = data;
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int altera_write_cap_word(struct altera_pcie *pcie, u8 busno,
|
|
+ unsigned int devfn, int offset, u16 value)
|
|
+{
|
|
+ return _altera_pcie_cfg_write(pcie, busno, devfn,
|
|
+ PCIE_CAP_OFFSET + offset, sizeof(value),
|
|
+ value);
|
|
+}
|
|
+
|
|
+static void altera_wait_link_retrain(struct altera_pcie *pcie)
|
|
+{
|
|
+ u16 reg16;
|
|
+ unsigned long start_jiffies;
|
|
+
|
|
+ /* Wait for link training end. */
|
|
+ start_jiffies = jiffies;
|
|
+ for (;;) {
|
|
+ altera_read_cap_word(pcie, pcie->root_bus_nr, RP_DEVFN,
|
|
+ PCI_EXP_LNKSTA, ®16);
|
|
+ if (!(reg16 & PCI_EXP_LNKSTA_LT))
|
|
+ break;
|
|
+
|
|
+ if (time_after(jiffies, start_jiffies + LINK_RETRAIN_TIMEOUT)) {
|
|
+ dev_err(&pcie->pdev->dev, "link retrain timeout\n");
|
|
+ break;
|
|
+ }
|
|
+ udelay(100);
|
|
+ }
|
|
+
|
|
+ /* Wait for link is up */
|
|
+ start_jiffies = jiffies;
|
|
+ for (;;) {
|
|
+ if (altera_pcie_link_is_up(pcie))
|
|
+ break;
|
|
+
|
|
+ if (time_after(jiffies, start_jiffies + LINK_UP_TIMEOUT)) {
|
|
+ dev_err(&pcie->pdev->dev, "link up timeout\n");
|
|
+ break;
|
|
+ }
|
|
+ udelay(100);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void altera_pcie_retrain(struct altera_pcie *pcie)
|
|
+{
|
|
+ u16 linkcap, linkstat, linkctl;
|
|
+
|
|
+ if (!altera_pcie_link_is_up(pcie))
|
|
+ return;
|
|
+
|
|
+ /*
|
|
+ * Set the retrain bit if the PCIe rootport support > 2.5GB/s, but
|
|
+ * current speed is 2.5 GB/s.
|
|
+ */
|
|
+ altera_read_cap_word(pcie, pcie->root_bus_nr, RP_DEVFN, PCI_EXP_LNKCAP,
|
|
+ &linkcap);
|
|
+ if ((linkcap & PCI_EXP_LNKCAP_SLS) <= PCI_EXP_LNKCAP_SLS_2_5GB)
|
|
+ return;
|
|
+
|
|
+ altera_read_cap_word(pcie, pcie->root_bus_nr, RP_DEVFN, PCI_EXP_LNKSTA,
|
|
+ &linkstat);
|
|
+ if ((linkstat & PCI_EXP_LNKSTA_CLS) == PCI_EXP_LNKSTA_CLS_2_5GB) {
|
|
+ altera_read_cap_word(pcie, pcie->root_bus_nr, RP_DEVFN,
|
|
+ PCI_EXP_LNKCTL, &linkctl);
|
|
+ linkctl |= PCI_EXP_LNKCTL_RL;
|
|
+ altera_write_cap_word(pcie, pcie->root_bus_nr, RP_DEVFN,
|
|
+ PCI_EXP_LNKCTL, linkctl);
|
|
+
|
|
+ altera_wait_link_retrain(pcie);
|
|
+ }
|
|
+}
|
|
+
|
|
static int altera_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
|
|
irq_hw_number_t hwirq)
|
|
{
|
|
@@ -506,6 +593,11 @@ static int altera_pcie_parse_dt(struct altera_pcie *pcie)
|
|
return 0;
|
|
}
|
|
|
|
+static void altera_pcie_host_init(struct altera_pcie *pcie)
|
|
+{
|
|
+ altera_pcie_retrain(pcie);
|
|
+}
|
|
+
|
|
static int altera_pcie_probe(struct platform_device *pdev)
|
|
{
|
|
struct altera_pcie *pcie;
|
|
@@ -543,6 +635,7 @@ static int altera_pcie_probe(struct platform_device *pdev)
|
|
cra_writel(pcie, P2A_INT_STS_ALL, P2A_INT_STATUS);
|
|
/* enable all interrupts */
|
|
cra_writel(pcie, P2A_INT_ENA_ALL, P2A_INT_ENABLE);
|
|
+ altera_pcie_host_init(pcie);
|
|
|
|
bus = pci_scan_root_bus(&pdev->dev, pcie->root_bus_nr, &altera_pcie_ops,
|
|
pcie, &pcie->resources);
|
|
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
|
|
index 3919ea066bf9..736de1021d8b 100644
|
|
--- a/drivers/usb/class/cdc-acm.c
|
|
+++ b/drivers/usb/class/cdc-acm.c
|
|
@@ -1885,6 +1885,13 @@ static const struct usb_device_id acm_ids[] = {
|
|
.driver_info = IGNORE_DEVICE,
|
|
},
|
|
|
|
+ { USB_DEVICE(0x1bc7, 0x0021), /* Telit 3G ACM only composition */
|
|
+ .driver_info = SEND_ZERO_PACKET,
|
|
+ },
|
|
+ { USB_DEVICE(0x1bc7, 0x0023), /* Telit 3G ACM + ECM composition */
|
|
+ .driver_info = SEND_ZERO_PACKET,
|
|
+ },
|
|
+
|
|
/* control interfaces without any protocol set */
|
|
{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
|
|
USB_CDC_PROTO_NONE) },
|
|
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
|
|
index cf378b1ed373..733479ddf8a7 100644
|
|
--- a/drivers/usb/core/quirks.c
|
|
+++ b/drivers/usb/core/quirks.c
|
|
@@ -240,7 +240,8 @@ static const struct usb_device_id usb_quirk_list[] = {
|
|
USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL },
|
|
|
|
/* Corsair K70 RGB */
|
|
- { USB_DEVICE(0x1b1c, 0x1b13), .driver_info = USB_QUIRK_DELAY_INIT },
|
|
+ { USB_DEVICE(0x1b1c, 0x1b13), .driver_info = USB_QUIRK_DELAY_INIT |
|
|
+ USB_QUIRK_DELAY_CTRL_MSG },
|
|
|
|
/* Corsair Strafe */
|
|
{ USB_DEVICE(0x1b1c, 0x1b15), .driver_info = USB_QUIRK_DELAY_INIT |
|
|
diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c
|
|
index 6c186b4df94a..b3344a77dcce 100644
|
|
--- a/drivers/usb/storage/scsiglue.c
|
|
+++ b/drivers/usb/storage/scsiglue.c
|
|
@@ -223,8 +223,12 @@ static int slave_configure(struct scsi_device *sdev)
|
|
if (!(us->fflags & US_FL_NEEDS_CAP16))
|
|
sdev->try_rc_10_first = 1;
|
|
|
|
- /* assume SPC3 or latter devices support sense size > 18 */
|
|
- if (sdev->scsi_level > SCSI_SPC_2)
|
|
+ /*
|
|
+ * assume SPC3 or latter devices support sense size > 18
|
|
+ * unless US_FL_BAD_SENSE quirk is specified.
|
|
+ */
|
|
+ if (sdev->scsi_level > SCSI_SPC_2 &&
|
|
+ !(us->fflags & US_FL_BAD_SENSE))
|
|
us->fflags |= US_FL_SANE_SENSE;
|
|
|
|
/* USB-IDE bridges tend to report SK = 0x04 (Non-recoverable
|
|
diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h
|
|
index 898215cad351..d92b974f0635 100644
|
|
--- a/drivers/usb/storage/unusual_devs.h
|
|
+++ b/drivers/usb/storage/unusual_devs.h
|
|
@@ -1392,6 +1392,18 @@ UNUSUAL_DEV( 0x0d49, 0x7310, 0x0000, 0x9999,
|
|
USB_SC_DEVICE, USB_PR_DEVICE, NULL,
|
|
US_FL_SANE_SENSE),
|
|
|
|
+/*
|
|
+ * Reported by Icenowy Zheng <icenowy@aosc.io>
|
|
+ * The SMI SM3350 USB-UFS bridge controller will enter a wrong state
|
|
+ * that do not process read/write command if a long sense is requested,
|
|
+ * so force to use 18-byte sense.
|
|
+ */
|
|
+UNUSUAL_DEV( 0x090c, 0x3350, 0x0000, 0xffff,
|
|
+ "SMI",
|
|
+ "SM3350 UFS-to-USB-Mass-Storage bridge",
|
|
+ USB_SC_DEVICE, USB_PR_DEVICE, NULL,
|
|
+ US_FL_BAD_SENSE ),
|
|
+
|
|
/*
|
|
* Pete Zaitcev <zaitcev@yahoo.com>, bz#164688.
|
|
* The device blatantly ignores LUN and returns 1 in GetMaxLUN.
|
|
diff --git a/fs/btrfs/Makefile b/fs/btrfs/Makefile
|
|
index 6d1d0b93b1aa..c792df826e12 100644
|
|
--- a/fs/btrfs/Makefile
|
|
+++ b/fs/btrfs/Makefile
|
|
@@ -9,7 +9,7 @@ btrfs-y += super.o ctree.o extent-tree.o print-tree.o root-tree.o dir-item.o \
|
|
export.o tree-log.o free-space-cache.o zlib.o lzo.o \
|
|
compression.o delayed-ref.o relocation.o delayed-inode.o scrub.o \
|
|
reada.o backref.o ulist.o qgroup.o send.o dev-replace.o raid56.o \
|
|
- uuid-tree.o props.o hash.o
|
|
+ uuid-tree.o props.o hash.o tree-checker.o
|
|
|
|
btrfs-$(CONFIG_BTRFS_FS_POSIX_ACL) += acl.o
|
|
btrfs-$(CONFIG_BTRFS_FS_CHECK_INTEGRITY) += check-integrity.o
|
|
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c
|
|
index 38ee08675468..8f4baa3cb992 100644
|
|
--- a/fs/btrfs/ctree.c
|
|
+++ b/fs/btrfs/ctree.c
|
|
@@ -1726,20 +1726,6 @@ int btrfs_realloc_node(struct btrfs_trans_handle *trans,
|
|
return err;
|
|
}
|
|
|
|
-/*
|
|
- * The leaf data grows from end-to-front in the node.
|
|
- * this returns the address of the start of the last item,
|
|
- * which is the stop of the leaf data stack
|
|
- */
|
|
-static inline unsigned int leaf_data_end(struct btrfs_root *root,
|
|
- struct extent_buffer *leaf)
|
|
-{
|
|
- u32 nr = btrfs_header_nritems(leaf);
|
|
- if (nr == 0)
|
|
- return BTRFS_LEAF_DATA_SIZE(root);
|
|
- return btrfs_item_offset_nr(leaf, nr - 1);
|
|
-}
|
|
-
|
|
|
|
/*
|
|
* search for key in the extent_buffer. The items start at offset p,
|
|
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
|
|
index e847573c6db0..4a91d3119e59 100644
|
|
--- a/fs/btrfs/ctree.h
|
|
+++ b/fs/btrfs/ctree.h
|
|
@@ -35,6 +35,7 @@
|
|
#include <linux/btrfs.h>
|
|
#include <linux/workqueue.h>
|
|
#include <linux/security.h>
|
|
+#include <linux/sizes.h>
|
|
#include "extent_io.h"
|
|
#include "extent_map.h"
|
|
#include "async-thread.h"
|
|
@@ -897,6 +898,7 @@ struct btrfs_balance_item {
|
|
#define BTRFS_FILE_EXTENT_INLINE 0
|
|
#define BTRFS_FILE_EXTENT_REG 1
|
|
#define BTRFS_FILE_EXTENT_PREALLOC 2
|
|
+#define BTRFS_FILE_EXTENT_TYPES 2
|
|
|
|
struct btrfs_file_extent_item {
|
|
/*
|
|
@@ -2283,7 +2285,7 @@ do { \
|
|
#define BTRFS_INODE_ROOT_ITEM_INIT (1 << 31)
|
|
|
|
struct btrfs_map_token {
|
|
- struct extent_buffer *eb;
|
|
+ const struct extent_buffer *eb;
|
|
char *kaddr;
|
|
unsigned long offset;
|
|
};
|
|
@@ -2314,18 +2316,19 @@ static inline void btrfs_init_map_token (struct btrfs_map_token *token)
|
|
sizeof(((type *)0)->member)))
|
|
|
|
#define DECLARE_BTRFS_SETGET_BITS(bits) \
|
|
-u##bits btrfs_get_token_##bits(struct extent_buffer *eb, void *ptr, \
|
|
- unsigned long off, \
|
|
- struct btrfs_map_token *token); \
|
|
-void btrfs_set_token_##bits(struct extent_buffer *eb, void *ptr, \
|
|
+u##bits btrfs_get_token_##bits(const struct extent_buffer *eb, \
|
|
+ const void *ptr, unsigned long off, \
|
|
+ struct btrfs_map_token *token); \
|
|
+void btrfs_set_token_##bits(struct extent_buffer *eb, const void *ptr, \
|
|
unsigned long off, u##bits val, \
|
|
struct btrfs_map_token *token); \
|
|
-static inline u##bits btrfs_get_##bits(struct extent_buffer *eb, void *ptr, \
|
|
+static inline u##bits btrfs_get_##bits(const struct extent_buffer *eb, \
|
|
+ const void *ptr, \
|
|
unsigned long off) \
|
|
{ \
|
|
return btrfs_get_token_##bits(eb, ptr, off, NULL); \
|
|
} \
|
|
-static inline void btrfs_set_##bits(struct extent_buffer *eb, void *ptr, \
|
|
+static inline void btrfs_set_##bits(struct extent_buffer *eb, void *ptr,\
|
|
unsigned long off, u##bits val) \
|
|
{ \
|
|
btrfs_set_token_##bits(eb, ptr, off, val, NULL); \
|
|
@@ -2337,7 +2340,8 @@ DECLARE_BTRFS_SETGET_BITS(32)
|
|
DECLARE_BTRFS_SETGET_BITS(64)
|
|
|
|
#define BTRFS_SETGET_FUNCS(name, type, member, bits) \
|
|
-static inline u##bits btrfs_##name(struct extent_buffer *eb, type *s) \
|
|
+static inline u##bits btrfs_##name(const struct extent_buffer *eb, \
|
|
+ const type *s) \
|
|
{ \
|
|
BUILD_BUG_ON(sizeof(u##bits) != sizeof(((type *)0))->member); \
|
|
return btrfs_get_##bits(eb, s, offsetof(type, member)); \
|
|
@@ -2348,7 +2352,8 @@ static inline void btrfs_set_##name(struct extent_buffer *eb, type *s, \
|
|
BUILD_BUG_ON(sizeof(u##bits) != sizeof(((type *)0))->member); \
|
|
btrfs_set_##bits(eb, s, offsetof(type, member), val); \
|
|
} \
|
|
-static inline u##bits btrfs_token_##name(struct extent_buffer *eb, type *s, \
|
|
+static inline u##bits btrfs_token_##name(const struct extent_buffer *eb,\
|
|
+ const type *s, \
|
|
struct btrfs_map_token *token) \
|
|
{ \
|
|
BUILD_BUG_ON(sizeof(u##bits) != sizeof(((type *)0))->member); \
|
|
@@ -2363,9 +2368,9 @@ static inline void btrfs_set_token_##name(struct extent_buffer *eb, \
|
|
}
|
|
|
|
#define BTRFS_SETGET_HEADER_FUNCS(name, type, member, bits) \
|
|
-static inline u##bits btrfs_##name(struct extent_buffer *eb) \
|
|
+static inline u##bits btrfs_##name(const struct extent_buffer *eb) \
|
|
{ \
|
|
- type *p = page_address(eb->pages[0]); \
|
|
+ const type *p = page_address(eb->pages[0]); \
|
|
u##bits res = le##bits##_to_cpu(p->member); \
|
|
return res; \
|
|
} \
|
|
@@ -2377,7 +2382,7 @@ static inline void btrfs_set_##name(struct extent_buffer *eb, \
|
|
}
|
|
|
|
#define BTRFS_SETGET_STACK_FUNCS(name, type, member, bits) \
|
|
-static inline u##bits btrfs_##name(type *s) \
|
|
+static inline u##bits btrfs_##name(const type *s) \
|
|
{ \
|
|
return le##bits##_to_cpu(s->member); \
|
|
} \
|
|
@@ -2678,7 +2683,7 @@ static inline unsigned long btrfs_node_key_ptr_offset(int nr)
|
|
sizeof(struct btrfs_key_ptr) * nr;
|
|
}
|
|
|
|
-void btrfs_node_key(struct extent_buffer *eb,
|
|
+void btrfs_node_key(const struct extent_buffer *eb,
|
|
struct btrfs_disk_key *disk_key, int nr);
|
|
|
|
static inline void btrfs_set_node_key(struct extent_buffer *eb,
|
|
@@ -2707,28 +2712,28 @@ static inline struct btrfs_item *btrfs_item_nr(int nr)
|
|
return (struct btrfs_item *)btrfs_item_nr_offset(nr);
|
|
}
|
|
|
|
-static inline u32 btrfs_item_end(struct extent_buffer *eb,
|
|
+static inline u32 btrfs_item_end(const struct extent_buffer *eb,
|
|
struct btrfs_item *item)
|
|
{
|
|
return btrfs_item_offset(eb, item) + btrfs_item_size(eb, item);
|
|
}
|
|
|
|
-static inline u32 btrfs_item_end_nr(struct extent_buffer *eb, int nr)
|
|
+static inline u32 btrfs_item_end_nr(const struct extent_buffer *eb, int nr)
|
|
{
|
|
return btrfs_item_end(eb, btrfs_item_nr(nr));
|
|
}
|
|
|
|
-static inline u32 btrfs_item_offset_nr(struct extent_buffer *eb, int nr)
|
|
+static inline u32 btrfs_item_offset_nr(const struct extent_buffer *eb, int nr)
|
|
{
|
|
return btrfs_item_offset(eb, btrfs_item_nr(nr));
|
|
}
|
|
|
|
-static inline u32 btrfs_item_size_nr(struct extent_buffer *eb, int nr)
|
|
+static inline u32 btrfs_item_size_nr(const struct extent_buffer *eb, int nr)
|
|
{
|
|
return btrfs_item_size(eb, btrfs_item_nr(nr));
|
|
}
|
|
|
|
-static inline void btrfs_item_key(struct extent_buffer *eb,
|
|
+static inline void btrfs_item_key(const struct extent_buffer *eb,
|
|
struct btrfs_disk_key *disk_key, int nr)
|
|
{
|
|
struct btrfs_item *item = btrfs_item_nr(nr);
|
|
@@ -2764,8 +2769,8 @@ BTRFS_SETGET_STACK_FUNCS(stack_dir_name_len, struct btrfs_dir_item,
|
|
BTRFS_SETGET_STACK_FUNCS(stack_dir_transid, struct btrfs_dir_item,
|
|
transid, 64);
|
|
|
|
-static inline void btrfs_dir_item_key(struct extent_buffer *eb,
|
|
- struct btrfs_dir_item *item,
|
|
+static inline void btrfs_dir_item_key(const struct extent_buffer *eb,
|
|
+ const struct btrfs_dir_item *item,
|
|
struct btrfs_disk_key *key)
|
|
{
|
|
read_eb_member(eb, item, struct btrfs_dir_item, location, key);
|
|
@@ -2773,7 +2778,7 @@ static inline void btrfs_dir_item_key(struct extent_buffer *eb,
|
|
|
|
static inline void btrfs_set_dir_item_key(struct extent_buffer *eb,
|
|
struct btrfs_dir_item *item,
|
|
- struct btrfs_disk_key *key)
|
|
+ const struct btrfs_disk_key *key)
|
|
{
|
|
write_eb_member(eb, item, struct btrfs_dir_item, location, key);
|
|
}
|
|
@@ -2785,8 +2790,8 @@ BTRFS_SETGET_FUNCS(free_space_bitmaps, struct btrfs_free_space_header,
|
|
BTRFS_SETGET_FUNCS(free_space_generation, struct btrfs_free_space_header,
|
|
generation, 64);
|
|
|
|
-static inline void btrfs_free_space_key(struct extent_buffer *eb,
|
|
- struct btrfs_free_space_header *h,
|
|
+static inline void btrfs_free_space_key(const struct extent_buffer *eb,
|
|
+ const struct btrfs_free_space_header *h,
|
|
struct btrfs_disk_key *key)
|
|
{
|
|
read_eb_member(eb, h, struct btrfs_free_space_header, location, key);
|
|
@@ -2794,7 +2799,7 @@ static inline void btrfs_free_space_key(struct extent_buffer *eb,
|
|
|
|
static inline void btrfs_set_free_space_key(struct extent_buffer *eb,
|
|
struct btrfs_free_space_header *h,
|
|
- struct btrfs_disk_key *key)
|
|
+ const struct btrfs_disk_key *key)
|
|
{
|
|
write_eb_member(eb, h, struct btrfs_free_space_header, location, key);
|
|
}
|
|
@@ -2821,25 +2826,25 @@ static inline void btrfs_cpu_key_to_disk(struct btrfs_disk_key *disk,
|
|
disk->objectid = cpu_to_le64(cpu->objectid);
|
|
}
|
|
|
|
-static inline void btrfs_node_key_to_cpu(struct extent_buffer *eb,
|
|
- struct btrfs_key *key, int nr)
|
|
+static inline void btrfs_node_key_to_cpu(const struct extent_buffer *eb,
|
|
+ struct btrfs_key *key, int nr)
|
|
{
|
|
struct btrfs_disk_key disk_key;
|
|
btrfs_node_key(eb, &disk_key, nr);
|
|
btrfs_disk_key_to_cpu(key, &disk_key);
|
|
}
|
|
|
|
-static inline void btrfs_item_key_to_cpu(struct extent_buffer *eb,
|
|
- struct btrfs_key *key, int nr)
|
|
+static inline void btrfs_item_key_to_cpu(const struct extent_buffer *eb,
|
|
+ struct btrfs_key *key, int nr)
|
|
{
|
|
struct btrfs_disk_key disk_key;
|
|
btrfs_item_key(eb, &disk_key, nr);
|
|
btrfs_disk_key_to_cpu(key, &disk_key);
|
|
}
|
|
|
|
-static inline void btrfs_dir_item_key_to_cpu(struct extent_buffer *eb,
|
|
- struct btrfs_dir_item *item,
|
|
- struct btrfs_key *key)
|
|
+static inline void btrfs_dir_item_key_to_cpu(const struct extent_buffer *eb,
|
|
+ const struct btrfs_dir_item *item,
|
|
+ struct btrfs_key *key)
|
|
{
|
|
struct btrfs_disk_key disk_key;
|
|
btrfs_dir_item_key(eb, item, &disk_key);
|
|
@@ -2872,7 +2877,7 @@ BTRFS_SETGET_STACK_FUNCS(stack_header_nritems, struct btrfs_header,
|
|
nritems, 32);
|
|
BTRFS_SETGET_STACK_FUNCS(stack_header_bytenr, struct btrfs_header, bytenr, 64);
|
|
|
|
-static inline int btrfs_header_flag(struct extent_buffer *eb, u64 flag)
|
|
+static inline int btrfs_header_flag(const struct extent_buffer *eb, u64 flag)
|
|
{
|
|
return (btrfs_header_flags(eb) & flag) == flag;
|
|
}
|
|
@@ -2891,7 +2896,7 @@ static inline int btrfs_clear_header_flag(struct extent_buffer *eb, u64 flag)
|
|
return (flags & flag) == flag;
|
|
}
|
|
|
|
-static inline int btrfs_header_backref_rev(struct extent_buffer *eb)
|
|
+static inline int btrfs_header_backref_rev(const struct extent_buffer *eb)
|
|
{
|
|
u64 flags = btrfs_header_flags(eb);
|
|
return flags >> BTRFS_BACKREF_REV_SHIFT;
|
|
@@ -2911,12 +2916,12 @@ static inline unsigned long btrfs_header_fsid(void)
|
|
return offsetof(struct btrfs_header, fsid);
|
|
}
|
|
|
|
-static inline unsigned long btrfs_header_chunk_tree_uuid(struct extent_buffer *eb)
|
|
+static inline unsigned long btrfs_header_chunk_tree_uuid(const struct extent_buffer *eb)
|
|
{
|
|
return offsetof(struct btrfs_header, chunk_tree_uuid);
|
|
}
|
|
|
|
-static inline int btrfs_is_leaf(struct extent_buffer *eb)
|
|
+static inline int btrfs_is_leaf(const struct extent_buffer *eb)
|
|
{
|
|
return btrfs_header_level(eb) == 0;
|
|
}
|
|
@@ -2950,12 +2955,12 @@ BTRFS_SETGET_STACK_FUNCS(root_stransid, struct btrfs_root_item,
|
|
BTRFS_SETGET_STACK_FUNCS(root_rtransid, struct btrfs_root_item,
|
|
rtransid, 64);
|
|
|
|
-static inline bool btrfs_root_readonly(struct btrfs_root *root)
|
|
+static inline bool btrfs_root_readonly(const struct btrfs_root *root)
|
|
{
|
|
return (root->root_item.flags & cpu_to_le64(BTRFS_ROOT_SUBVOL_RDONLY)) != 0;
|
|
}
|
|
|
|
-static inline bool btrfs_root_dead(struct btrfs_root *root)
|
|
+static inline bool btrfs_root_dead(const struct btrfs_root *root)
|
|
{
|
|
return (root->root_item.flags & cpu_to_le64(BTRFS_ROOT_SUBVOL_DEAD)) != 0;
|
|
}
|
|
@@ -3012,51 +3017,51 @@ BTRFS_SETGET_STACK_FUNCS(backup_num_devices, struct btrfs_root_backup,
|
|
/* struct btrfs_balance_item */
|
|
BTRFS_SETGET_FUNCS(balance_flags, struct btrfs_balance_item, flags, 64);
|
|
|
|
-static inline void btrfs_balance_data(struct extent_buffer *eb,
|
|
- struct btrfs_balance_item *bi,
|
|
+static inline void btrfs_balance_data(const struct extent_buffer *eb,
|
|
+ const struct btrfs_balance_item *bi,
|
|
struct btrfs_disk_balance_args *ba)
|
|
{
|
|
read_eb_member(eb, bi, struct btrfs_balance_item, data, ba);
|
|
}
|
|
|
|
static inline void btrfs_set_balance_data(struct extent_buffer *eb,
|
|
- struct btrfs_balance_item *bi,
|
|
- struct btrfs_disk_balance_args *ba)
|
|
+ struct btrfs_balance_item *bi,
|
|
+ const struct btrfs_disk_balance_args *ba)
|
|
{
|
|
write_eb_member(eb, bi, struct btrfs_balance_item, data, ba);
|
|
}
|
|
|
|
-static inline void btrfs_balance_meta(struct extent_buffer *eb,
|
|
- struct btrfs_balance_item *bi,
|
|
+static inline void btrfs_balance_meta(const struct extent_buffer *eb,
|
|
+ const struct btrfs_balance_item *bi,
|
|
struct btrfs_disk_balance_args *ba)
|
|
{
|
|
read_eb_member(eb, bi, struct btrfs_balance_item, meta, ba);
|
|
}
|
|
|
|
static inline void btrfs_set_balance_meta(struct extent_buffer *eb,
|
|
- struct btrfs_balance_item *bi,
|
|
- struct btrfs_disk_balance_args *ba)
|
|
+ struct btrfs_balance_item *bi,
|
|
+ const struct btrfs_disk_balance_args *ba)
|
|
{
|
|
write_eb_member(eb, bi, struct btrfs_balance_item, meta, ba);
|
|
}
|
|
|
|
-static inline void btrfs_balance_sys(struct extent_buffer *eb,
|
|
- struct btrfs_balance_item *bi,
|
|
+static inline void btrfs_balance_sys(const struct extent_buffer *eb,
|
|
+ const struct btrfs_balance_item *bi,
|
|
struct btrfs_disk_balance_args *ba)
|
|
{
|
|
read_eb_member(eb, bi, struct btrfs_balance_item, sys, ba);
|
|
}
|
|
|
|
static inline void btrfs_set_balance_sys(struct extent_buffer *eb,
|
|
- struct btrfs_balance_item *bi,
|
|
- struct btrfs_disk_balance_args *ba)
|
|
+ struct btrfs_balance_item *bi,
|
|
+ const struct btrfs_disk_balance_args *ba)
|
|
{
|
|
write_eb_member(eb, bi, struct btrfs_balance_item, sys, ba);
|
|
}
|
|
|
|
static inline void
|
|
btrfs_disk_balance_args_to_cpu(struct btrfs_balance_args *cpu,
|
|
- struct btrfs_disk_balance_args *disk)
|
|
+ const struct btrfs_disk_balance_args *disk)
|
|
{
|
|
memset(cpu, 0, sizeof(*cpu));
|
|
|
|
@@ -3076,7 +3081,7 @@ btrfs_disk_balance_args_to_cpu(struct btrfs_balance_args *cpu,
|
|
|
|
static inline void
|
|
btrfs_cpu_balance_args_to_disk(struct btrfs_disk_balance_args *disk,
|
|
- struct btrfs_balance_args *cpu)
|
|
+ const struct btrfs_balance_args *cpu)
|
|
{
|
|
memset(disk, 0, sizeof(*disk));
|
|
|
|
@@ -3144,7 +3149,7 @@ BTRFS_SETGET_STACK_FUNCS(super_magic, struct btrfs_super_block, magic, 64);
|
|
BTRFS_SETGET_STACK_FUNCS(super_uuid_tree_generation, struct btrfs_super_block,
|
|
uuid_tree_generation, 64);
|
|
|
|
-static inline int btrfs_super_csum_size(struct btrfs_super_block *s)
|
|
+static inline int btrfs_super_csum_size(const struct btrfs_super_block *s)
|
|
{
|
|
u16 t = btrfs_super_csum_type(s);
|
|
/*
|
|
@@ -3158,6 +3163,21 @@ static inline unsigned long btrfs_leaf_data(struct extent_buffer *l)
|
|
return offsetof(struct btrfs_leaf, items);
|
|
}
|
|
|
|
+/*
|
|
+ * The leaf data grows from end-to-front in the node.
|
|
+ * this returns the address of the start of the last item,
|
|
+ * which is the stop of the leaf data stack
|
|
+ */
|
|
+static inline unsigned int leaf_data_end(const struct btrfs_root *root,
|
|
+ const struct extent_buffer *leaf)
|
|
+{
|
|
+ u32 nr = btrfs_header_nritems(leaf);
|
|
+
|
|
+ if (nr == 0)
|
|
+ return BTRFS_LEAF_DATA_SIZE(root);
|
|
+ return btrfs_item_offset_nr(leaf, nr - 1);
|
|
+}
|
|
+
|
|
/* struct btrfs_file_extent_item */
|
|
BTRFS_SETGET_FUNCS(file_extent_type, struct btrfs_file_extent_item, type, 8);
|
|
BTRFS_SETGET_STACK_FUNCS(stack_file_extent_disk_bytenr,
|
|
@@ -3174,7 +3194,7 @@ BTRFS_SETGET_STACK_FUNCS(stack_file_extent_compression,
|
|
struct btrfs_file_extent_item, compression, 8);
|
|
|
|
static inline unsigned long
|
|
-btrfs_file_extent_inline_start(struct btrfs_file_extent_item *e)
|
|
+btrfs_file_extent_inline_start(const struct btrfs_file_extent_item *e)
|
|
{
|
|
return (unsigned long)e + BTRFS_FILE_EXTENT_INLINE_DATA_START;
|
|
}
|
|
@@ -3208,8 +3228,9 @@ BTRFS_SETGET_FUNCS(file_extent_other_encoding, struct btrfs_file_extent_item,
|
|
* size of any extent headers. If a file is compressed on disk, this is
|
|
* the compressed size
|
|
*/
|
|
-static inline u32 btrfs_file_extent_inline_item_len(struct extent_buffer *eb,
|
|
- struct btrfs_item *e)
|
|
+static inline u32 btrfs_file_extent_inline_item_len(
|
|
+ const struct extent_buffer *eb,
|
|
+ struct btrfs_item *e)
|
|
{
|
|
return btrfs_item_size(eb, e) - BTRFS_FILE_EXTENT_INLINE_DATA_START;
|
|
}
|
|
@@ -3217,9 +3238,9 @@ static inline u32 btrfs_file_extent_inline_item_len(struct extent_buffer *eb,
|
|
/* this returns the number of file bytes represented by the inline item.
|
|
* If an item is compressed, this is the uncompressed size
|
|
*/
|
|
-static inline u32 btrfs_file_extent_inline_len(struct extent_buffer *eb,
|
|
- int slot,
|
|
- struct btrfs_file_extent_item *fi)
|
|
+static inline u32 btrfs_file_extent_inline_len(const struct extent_buffer *eb,
|
|
+ int slot,
|
|
+ const struct btrfs_file_extent_item *fi)
|
|
{
|
|
struct btrfs_map_token token;
|
|
|
|
@@ -3241,8 +3262,8 @@ static inline u32 btrfs_file_extent_inline_len(struct extent_buffer *eb,
|
|
|
|
|
|
/* btrfs_dev_stats_item */
|
|
-static inline u64 btrfs_dev_stats_value(struct extent_buffer *eb,
|
|
- struct btrfs_dev_stats_item *ptr,
|
|
+static inline u64 btrfs_dev_stats_value(const struct extent_buffer *eb,
|
|
+ const struct btrfs_dev_stats_item *ptr,
|
|
int index)
|
|
{
|
|
u64 val;
|
|
diff --git a/fs/btrfs/dev-replace.c b/fs/btrfs/dev-replace.c
|
|
index 176a27bc63aa..81e5bc62e8e3 100644
|
|
--- a/fs/btrfs/dev-replace.c
|
|
+++ b/fs/btrfs/dev-replace.c
|
|
@@ -620,7 +620,7 @@ static void btrfs_dev_replace_update_device_in_mapping_tree(
|
|
em = lookup_extent_mapping(em_tree, start, (u64)-1);
|
|
if (!em)
|
|
break;
|
|
- map = (struct map_lookup *)em->bdev;
|
|
+ map = em->map_lookup;
|
|
for (i = 0; i < map->num_stripes; i++)
|
|
if (srcdev == map->stripes[i].dev)
|
|
map->stripes[i].dev = tgtdev;
|
|
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
|
|
index 1f21c6c33228..f80a0af68736 100644
|
|
--- a/fs/btrfs/disk-io.c
|
|
+++ b/fs/btrfs/disk-io.c
|
|
@@ -49,6 +49,7 @@
|
|
#include "raid56.h"
|
|
#include "sysfs.h"
|
|
#include "qgroup.h"
|
|
+#include "tree-checker.h"
|
|
|
|
#ifdef CONFIG_X86
|
|
#include <asm/cpufeature.h>
|
|
@@ -522,72 +523,6 @@ static int check_tree_block_fsid(struct btrfs_fs_info *fs_info,
|
|
return ret;
|
|
}
|
|
|
|
-#define CORRUPT(reason, eb, root, slot) \
|
|
- btrfs_crit(root->fs_info, "corrupt leaf, %s: block=%llu," \
|
|
- "root=%llu, slot=%d", reason, \
|
|
- btrfs_header_bytenr(eb), root->objectid, slot)
|
|
-
|
|
-static noinline int check_leaf(struct btrfs_root *root,
|
|
- struct extent_buffer *leaf)
|
|
-{
|
|
- struct btrfs_key key;
|
|
- struct btrfs_key leaf_key;
|
|
- u32 nritems = btrfs_header_nritems(leaf);
|
|
- int slot;
|
|
-
|
|
- if (nritems == 0)
|
|
- return 0;
|
|
-
|
|
- /* Check the 0 item */
|
|
- if (btrfs_item_offset_nr(leaf, 0) + btrfs_item_size_nr(leaf, 0) !=
|
|
- BTRFS_LEAF_DATA_SIZE(root)) {
|
|
- CORRUPT("invalid item offset size pair", leaf, root, 0);
|
|
- return -EIO;
|
|
- }
|
|
-
|
|
- /*
|
|
- * Check to make sure each items keys are in the correct order and their
|
|
- * offsets make sense. We only have to loop through nritems-1 because
|
|
- * we check the current slot against the next slot, which verifies the
|
|
- * next slot's offset+size makes sense and that the current's slot
|
|
- * offset is correct.
|
|
- */
|
|
- for (slot = 0; slot < nritems - 1; slot++) {
|
|
- btrfs_item_key_to_cpu(leaf, &leaf_key, slot);
|
|
- btrfs_item_key_to_cpu(leaf, &key, slot + 1);
|
|
-
|
|
- /* Make sure the keys are in the right order */
|
|
- if (btrfs_comp_cpu_keys(&leaf_key, &key) >= 0) {
|
|
- CORRUPT("bad key order", leaf, root, slot);
|
|
- return -EIO;
|
|
- }
|
|
-
|
|
- /*
|
|
- * Make sure the offset and ends are right, remember that the
|
|
- * item data starts at the end of the leaf and grows towards the
|
|
- * front.
|
|
- */
|
|
- if (btrfs_item_offset_nr(leaf, slot) !=
|
|
- btrfs_item_end_nr(leaf, slot + 1)) {
|
|
- CORRUPT("slot offset bad", leaf, root, slot);
|
|
- return -EIO;
|
|
- }
|
|
-
|
|
- /*
|
|
- * Check to make sure that we don't point outside of the leaf,
|
|
- * just incase all the items are consistent to eachother, but
|
|
- * all point outside of the leaf.
|
|
- */
|
|
- if (btrfs_item_end_nr(leaf, slot) >
|
|
- BTRFS_LEAF_DATA_SIZE(root)) {
|
|
- CORRUPT("slot end outside of leaf", leaf, root, slot);
|
|
- return -EIO;
|
|
- }
|
|
- }
|
|
-
|
|
- return 0;
|
|
-}
|
|
-
|
|
static int btree_readpage_end_io_hook(struct btrfs_io_bio *io_bio,
|
|
u64 phy_offset, struct page *page,
|
|
u64 start, u64 end, int mirror)
|
|
@@ -654,11 +589,14 @@ static int btree_readpage_end_io_hook(struct btrfs_io_bio *io_bio,
|
|
* that we don't try and read the other copies of this block, just
|
|
* return -EIO.
|
|
*/
|
|
- if (found_level == 0 && check_leaf(root, eb)) {
|
|
+ if (found_level == 0 && btrfs_check_leaf_full(root, eb)) {
|
|
set_bit(EXTENT_BUFFER_CORRUPT, &eb->bflags);
|
|
ret = -EIO;
|
|
}
|
|
|
|
+ if (found_level > 0 && btrfs_check_node(root, eb))
|
|
+ ret = -EIO;
|
|
+
|
|
if (!ret)
|
|
set_extent_buffer_uptodate(eb);
|
|
err:
|
|
@@ -3958,7 +3896,13 @@ void btrfs_mark_buffer_dirty(struct extent_buffer *buf)
|
|
buf->len,
|
|
root->fs_info->dirty_metadata_batch);
|
|
#ifdef CONFIG_BTRFS_FS_CHECK_INTEGRITY
|
|
- if (btrfs_header_level(buf) == 0 && check_leaf(root, buf)) {
|
|
+ /*
|
|
+ * Since btrfs_mark_buffer_dirty() can be called with item pointer set
|
|
+ * but item data not updated.
|
|
+ * So here we should only check item pointers, not item data.
|
|
+ */
|
|
+ if (btrfs_header_level(buf) == 0 &&
|
|
+ btrfs_check_leaf_relaxed(root, buf)) {
|
|
btrfs_print_leaf(root, buf);
|
|
ASSERT(0);
|
|
}
|
|
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
|
|
index 13ff0fdae03e..978bbfed5a2c 100644
|
|
--- a/fs/btrfs/extent-tree.c
|
|
+++ b/fs/btrfs/extent-tree.c
|
|
@@ -2342,7 +2342,13 @@ static int run_delayed_tree_ref(struct btrfs_trans_handle *trans,
|
|
ins.type = BTRFS_EXTENT_ITEM_KEY;
|
|
}
|
|
|
|
- BUG_ON(node->ref_mod != 1);
|
|
+ if (node->ref_mod != 1) {
|
|
+ btrfs_err(root->fs_info,
|
|
+ "btree block(%llu) has %d references rather than 1: action %d ref_root %llu parent %llu",
|
|
+ node->bytenr, node->ref_mod, node->action, ref_root,
|
|
+ parent);
|
|
+ return -EIO;
|
|
+ }
|
|
if (node->action == BTRFS_ADD_DELAYED_REF && insert_reserved) {
|
|
BUG_ON(!extent_op || !extent_op->update_flags);
|
|
ret = alloc_reserved_tree_block(trans, root,
|
|
@@ -9481,6 +9487,8 @@ static int find_first_block_group(struct btrfs_root *root,
|
|
int ret = 0;
|
|
struct btrfs_key found_key;
|
|
struct extent_buffer *leaf;
|
|
+ struct btrfs_block_group_item bg;
|
|
+ u64 flags;
|
|
int slot;
|
|
|
|
ret = btrfs_search_slot(NULL, root, key, path, 0, 0);
|
|
@@ -9502,7 +9510,47 @@ static int find_first_block_group(struct btrfs_root *root,
|
|
|
|
if (found_key.objectid >= key->objectid &&
|
|
found_key.type == BTRFS_BLOCK_GROUP_ITEM_KEY) {
|
|
- ret = 0;
|
|
+ struct extent_map_tree *em_tree;
|
|
+ struct extent_map *em;
|
|
+
|
|
+ em_tree = &root->fs_info->mapping_tree.map_tree;
|
|
+ read_lock(&em_tree->lock);
|
|
+ em = lookup_extent_mapping(em_tree, found_key.objectid,
|
|
+ found_key.offset);
|
|
+ read_unlock(&em_tree->lock);
|
|
+ if (!em) {
|
|
+ btrfs_err(root->fs_info,
|
|
+ "logical %llu len %llu found bg but no related chunk",
|
|
+ found_key.objectid, found_key.offset);
|
|
+ ret = -ENOENT;
|
|
+ } else if (em->start != found_key.objectid ||
|
|
+ em->len != found_key.offset) {
|
|
+ btrfs_err(root->fs_info,
|
|
+ "block group %llu len %llu mismatch with chunk %llu len %llu",
|
|
+ found_key.objectid, found_key.offset,
|
|
+ em->start, em->len);
|
|
+ ret = -EUCLEAN;
|
|
+ } else {
|
|
+ read_extent_buffer(leaf, &bg,
|
|
+ btrfs_item_ptr_offset(leaf, slot),
|
|
+ sizeof(bg));
|
|
+ flags = btrfs_block_group_flags(&bg) &
|
|
+ BTRFS_BLOCK_GROUP_TYPE_MASK;
|
|
+
|
|
+ if (flags != (em->map_lookup->type &
|
|
+ BTRFS_BLOCK_GROUP_TYPE_MASK)) {
|
|
+ btrfs_err(root->fs_info,
|
|
+"block group %llu len %llu type flags 0x%llx mismatch with chunk type flags 0x%llx",
|
|
+ found_key.objectid,
|
|
+ found_key.offset, flags,
|
|
+ (BTRFS_BLOCK_GROUP_TYPE_MASK &
|
|
+ em->map_lookup->type));
|
|
+ ret = -EUCLEAN;
|
|
+ } else {
|
|
+ ret = 0;
|
|
+ }
|
|
+ }
|
|
+ free_extent_map(em);
|
|
goto out;
|
|
}
|
|
path->slots[0]++;
|
|
@@ -9717,6 +9765,62 @@ btrfs_create_block_group_cache(struct btrfs_root *root, u64 start, u64 size)
|
|
return cache;
|
|
}
|
|
|
|
+
|
|
+/*
|
|
+ * Iterate all chunks and verify that each of them has the corresponding block
|
|
+ * group
|
|
+ */
|
|
+static int check_chunk_block_group_mappings(struct btrfs_fs_info *fs_info)
|
|
+{
|
|
+ struct btrfs_mapping_tree *map_tree = &fs_info->mapping_tree;
|
|
+ struct extent_map *em;
|
|
+ struct btrfs_block_group_cache *bg;
|
|
+ u64 start = 0;
|
|
+ int ret = 0;
|
|
+
|
|
+ while (1) {
|
|
+ read_lock(&map_tree->map_tree.lock);
|
|
+ /*
|
|
+ * lookup_extent_mapping will return the first extent map
|
|
+ * intersecting the range, so setting @len to 1 is enough to
|
|
+ * get the first chunk.
|
|
+ */
|
|
+ em = lookup_extent_mapping(&map_tree->map_tree, start, 1);
|
|
+ read_unlock(&map_tree->map_tree.lock);
|
|
+ if (!em)
|
|
+ break;
|
|
+
|
|
+ bg = btrfs_lookup_block_group(fs_info, em->start);
|
|
+ if (!bg) {
|
|
+ btrfs_err(fs_info,
|
|
+ "chunk start=%llu len=%llu doesn't have corresponding block group",
|
|
+ em->start, em->len);
|
|
+ ret = -EUCLEAN;
|
|
+ free_extent_map(em);
|
|
+ break;
|
|
+ }
|
|
+ if (bg->key.objectid != em->start ||
|
|
+ bg->key.offset != em->len ||
|
|
+ (bg->flags & BTRFS_BLOCK_GROUP_TYPE_MASK) !=
|
|
+ (em->map_lookup->type & BTRFS_BLOCK_GROUP_TYPE_MASK)) {
|
|
+ btrfs_err(fs_info,
|
|
+"chunk start=%llu len=%llu flags=0x%llx doesn't match block group start=%llu len=%llu flags=0x%llx",
|
|
+ em->start, em->len,
|
|
+ em->map_lookup->type & BTRFS_BLOCK_GROUP_TYPE_MASK,
|
|
+ bg->key.objectid, bg->key.offset,
|
|
+ bg->flags & BTRFS_BLOCK_GROUP_TYPE_MASK);
|
|
+ ret = -EUCLEAN;
|
|
+ free_extent_map(em);
|
|
+ btrfs_put_block_group(bg);
|
|
+ break;
|
|
+ }
|
|
+ start = em->start + em->len;
|
|
+ free_extent_map(em);
|
|
+ btrfs_put_block_group(bg);
|
|
+ }
|
|
+ return ret;
|
|
+}
|
|
+
|
|
int btrfs_read_block_groups(struct btrfs_root *root)
|
|
{
|
|
struct btrfs_path *path;
|
|
@@ -9903,7 +10007,7 @@ int btrfs_read_block_groups(struct btrfs_root *root)
|
|
}
|
|
|
|
init_global_block_rsv(info);
|
|
- ret = 0;
|
|
+ ret = check_chunk_block_group_mappings(info);
|
|
error:
|
|
btrfs_free_path(path);
|
|
return ret;
|
|
@@ -10388,7 +10492,7 @@ btrfs_start_trans_remove_block_group(struct btrfs_fs_info *fs_info,
|
|
* more device items and remove one chunk item), but this is done at
|
|
* btrfs_remove_chunk() through a call to check_system_chunk().
|
|
*/
|
|
- map = (struct map_lookup *)em->bdev;
|
|
+ map = em->map_lookup;
|
|
num_items = 3 + map->num_stripes;
|
|
free_extent_map(em);
|
|
|
|
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
|
|
index 88bee6703cc0..42e7f6a8f91d 100644
|
|
--- a/fs/btrfs/extent_io.c
|
|
+++ b/fs/btrfs/extent_io.c
|
|
@@ -3847,8 +3847,10 @@ static noinline_for_stack int write_one_eb(struct extent_buffer *eb,
|
|
struct block_device *bdev = fs_info->fs_devices->latest_bdev;
|
|
struct extent_io_tree *tree = &BTRFS_I(fs_info->btree_inode)->io_tree;
|
|
u64 offset = eb->start;
|
|
+ u32 nritems;
|
|
unsigned long i, num_pages;
|
|
unsigned long bio_flags = 0;
|
|
+ unsigned long start, end;
|
|
int rw = (epd->sync_io ? WRITE_SYNC : WRITE) | REQ_META;
|
|
int ret = 0;
|
|
|
|
@@ -3858,6 +3860,23 @@ static noinline_for_stack int write_one_eb(struct extent_buffer *eb,
|
|
if (btrfs_header_owner(eb) == BTRFS_TREE_LOG_OBJECTID)
|
|
bio_flags = EXTENT_BIO_TREE_LOG;
|
|
|
|
+ /* set btree blocks beyond nritems with 0 to avoid stale content. */
|
|
+ nritems = btrfs_header_nritems(eb);
|
|
+ if (btrfs_header_level(eb) > 0) {
|
|
+ end = btrfs_node_key_ptr_offset(nritems);
|
|
+
|
|
+ memset_extent_buffer(eb, 0, end, eb->len - end);
|
|
+ } else {
|
|
+ /*
|
|
+ * leaf:
|
|
+ * header 0 1 2 .. N ... data_N .. data_2 data_1 data_0
|
|
+ */
|
|
+ start = btrfs_item_nr_offset(nritems);
|
|
+ end = btrfs_leaf_data(eb) +
|
|
+ leaf_data_end(fs_info->tree_root, eb);
|
|
+ memset_extent_buffer(eb, 0, start, end - start);
|
|
+ }
|
|
+
|
|
for (i = 0; i < num_pages; i++) {
|
|
struct page *p = eb->pages[i];
|
|
|
|
@@ -5362,9 +5381,8 @@ unlock_exit:
|
|
return ret;
|
|
}
|
|
|
|
-void read_extent_buffer(struct extent_buffer *eb, void *dstv,
|
|
- unsigned long start,
|
|
- unsigned long len)
|
|
+void read_extent_buffer(const struct extent_buffer *eb, void *dstv,
|
|
+ unsigned long start, unsigned long len)
|
|
{
|
|
size_t cur;
|
|
size_t offset;
|
|
@@ -5393,9 +5411,9 @@ void read_extent_buffer(struct extent_buffer *eb, void *dstv,
|
|
}
|
|
}
|
|
|
|
-int read_extent_buffer_to_user(struct extent_buffer *eb, void __user *dstv,
|
|
- unsigned long start,
|
|
- unsigned long len)
|
|
+int read_extent_buffer_to_user(const struct extent_buffer *eb,
|
|
+ void __user *dstv,
|
|
+ unsigned long start, unsigned long len)
|
|
{
|
|
size_t cur;
|
|
size_t offset;
|
|
@@ -5430,10 +5448,10 @@ int read_extent_buffer_to_user(struct extent_buffer *eb, void __user *dstv,
|
|
return ret;
|
|
}
|
|
|
|
-int map_private_extent_buffer(struct extent_buffer *eb, unsigned long start,
|
|
- unsigned long min_len, char **map,
|
|
- unsigned long *map_start,
|
|
- unsigned long *map_len)
|
|
+int map_private_extent_buffer(const struct extent_buffer *eb,
|
|
+ unsigned long start, unsigned long min_len,
|
|
+ char **map, unsigned long *map_start,
|
|
+ unsigned long *map_len)
|
|
{
|
|
size_t offset = start & (PAGE_CACHE_SIZE - 1);
|
|
char *kaddr;
|
|
@@ -5468,9 +5486,8 @@ int map_private_extent_buffer(struct extent_buffer *eb, unsigned long start,
|
|
return 0;
|
|
}
|
|
|
|
-int memcmp_extent_buffer(struct extent_buffer *eb, const void *ptrv,
|
|
- unsigned long start,
|
|
- unsigned long len)
|
|
+int memcmp_extent_buffer(const struct extent_buffer *eb, const void *ptrv,
|
|
+ unsigned long start, unsigned long len)
|
|
{
|
|
size_t cur;
|
|
size_t offset;
|
|
diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h
|
|
index f4c1ae11855f..751435967724 100644
|
|
--- a/fs/btrfs/extent_io.h
|
|
+++ b/fs/btrfs/extent_io.h
|
|
@@ -308,14 +308,13 @@ static inline void extent_buffer_get(struct extent_buffer *eb)
|
|
atomic_inc(&eb->refs);
|
|
}
|
|
|
|
-int memcmp_extent_buffer(struct extent_buffer *eb, const void *ptrv,
|
|
- unsigned long start,
|
|
- unsigned long len);
|
|
-void read_extent_buffer(struct extent_buffer *eb, void *dst,
|
|
+int memcmp_extent_buffer(const struct extent_buffer *eb, const void *ptrv,
|
|
+ unsigned long start, unsigned long len);
|
|
+void read_extent_buffer(const struct extent_buffer *eb, void *dst,
|
|
unsigned long start,
|
|
unsigned long len);
|
|
-int read_extent_buffer_to_user(struct extent_buffer *eb, void __user *dst,
|
|
- unsigned long start,
|
|
+int read_extent_buffer_to_user(const struct extent_buffer *eb,
|
|
+ void __user *dst, unsigned long start,
|
|
unsigned long len);
|
|
void write_extent_buffer(struct extent_buffer *eb, const void *src,
|
|
unsigned long start, unsigned long len);
|
|
@@ -334,10 +333,10 @@ int set_extent_buffer_uptodate(struct extent_buffer *eb);
|
|
int clear_extent_buffer_uptodate(struct extent_buffer *eb);
|
|
int extent_buffer_uptodate(struct extent_buffer *eb);
|
|
int extent_buffer_under_io(struct extent_buffer *eb);
|
|
-int map_private_extent_buffer(struct extent_buffer *eb, unsigned long offset,
|
|
- unsigned long min_len, char **map,
|
|
- unsigned long *map_start,
|
|
- unsigned long *map_len);
|
|
+int map_private_extent_buffer(const struct extent_buffer *eb,
|
|
+ unsigned long offset, unsigned long min_len,
|
|
+ char **map, unsigned long *map_start,
|
|
+ unsigned long *map_len);
|
|
int extent_range_clear_dirty_for_io(struct inode *inode, u64 start, u64 end);
|
|
int extent_range_redirty_for_io(struct inode *inode, u64 start, u64 end);
|
|
int extent_clear_unlock_delalloc(struct inode *inode, u64 start, u64 end,
|
|
diff --git a/fs/btrfs/extent_map.c b/fs/btrfs/extent_map.c
|
|
index 6a98bddd8f33..84fb56d5c018 100644
|
|
--- a/fs/btrfs/extent_map.c
|
|
+++ b/fs/btrfs/extent_map.c
|
|
@@ -76,7 +76,7 @@ void free_extent_map(struct extent_map *em)
|
|
WARN_ON(extent_map_in_tree(em));
|
|
WARN_ON(!list_empty(&em->list));
|
|
if (test_bit(EXTENT_FLAG_FS_MAPPING, &em->flags))
|
|
- kfree(em->bdev);
|
|
+ kfree(em->map_lookup);
|
|
kmem_cache_free(extent_map_cache, em);
|
|
}
|
|
}
|
|
diff --git a/fs/btrfs/extent_map.h b/fs/btrfs/extent_map.h
|
|
index b2991fd8583e..eb8b8fae036b 100644
|
|
--- a/fs/btrfs/extent_map.h
|
|
+++ b/fs/btrfs/extent_map.h
|
|
@@ -32,7 +32,15 @@ struct extent_map {
|
|
u64 block_len;
|
|
u64 generation;
|
|
unsigned long flags;
|
|
- struct block_device *bdev;
|
|
+ union {
|
|
+ struct block_device *bdev;
|
|
+
|
|
+ /*
|
|
+ * used for chunk mappings
|
|
+ * flags & EXTENT_FLAG_FS_MAPPING must be set
|
|
+ */
|
|
+ struct map_lookup *map_lookup;
|
|
+ };
|
|
atomic_t refs;
|
|
unsigned int compress_type;
|
|
struct list_head list;
|
|
diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c
|
|
index 6dca9f937bf6..cc9ccc42f469 100644
|
|
--- a/fs/btrfs/scrub.c
|
|
+++ b/fs/btrfs/scrub.c
|
|
@@ -3460,7 +3460,7 @@ static noinline_for_stack int scrub_chunk(struct scrub_ctx *sctx,
|
|
return ret;
|
|
}
|
|
|
|
- map = (struct map_lookup *)em->bdev;
|
|
+ map = em->map_lookup;
|
|
if (em->start != chunk_offset)
|
|
goto out;
|
|
|
|
diff --git a/fs/btrfs/struct-funcs.c b/fs/btrfs/struct-funcs.c
|
|
index b976597b0721..63ffd213b0b7 100644
|
|
--- a/fs/btrfs/struct-funcs.c
|
|
+++ b/fs/btrfs/struct-funcs.c
|
|
@@ -50,8 +50,8 @@ static inline void put_unaligned_le8(u8 val, void *p)
|
|
*/
|
|
|
|
#define DEFINE_BTRFS_SETGET_BITS(bits) \
|
|
-u##bits btrfs_get_token_##bits(struct extent_buffer *eb, void *ptr, \
|
|
- unsigned long off, \
|
|
+u##bits btrfs_get_token_##bits(const struct extent_buffer *eb, \
|
|
+ const void *ptr, unsigned long off, \
|
|
struct btrfs_map_token *token) \
|
|
{ \
|
|
unsigned long part_offset = (unsigned long)ptr; \
|
|
@@ -90,7 +90,8 @@ u##bits btrfs_get_token_##bits(struct extent_buffer *eb, void *ptr, \
|
|
return res; \
|
|
} \
|
|
void btrfs_set_token_##bits(struct extent_buffer *eb, \
|
|
- void *ptr, unsigned long off, u##bits val, \
|
|
+ const void *ptr, unsigned long off, \
|
|
+ u##bits val, \
|
|
struct btrfs_map_token *token) \
|
|
{ \
|
|
unsigned long part_offset = (unsigned long)ptr; \
|
|
@@ -133,7 +134,7 @@ DEFINE_BTRFS_SETGET_BITS(16)
|
|
DEFINE_BTRFS_SETGET_BITS(32)
|
|
DEFINE_BTRFS_SETGET_BITS(64)
|
|
|
|
-void btrfs_node_key(struct extent_buffer *eb,
|
|
+void btrfs_node_key(const struct extent_buffer *eb,
|
|
struct btrfs_disk_key *disk_key, int nr)
|
|
{
|
|
unsigned long ptr = btrfs_node_key_ptr_offset(nr);
|
|
diff --git a/fs/btrfs/tree-checker.c b/fs/btrfs/tree-checker.c
|
|
new file mode 100644
|
|
index 000000000000..5b98f3c76ce4
|
|
--- /dev/null
|
|
+++ b/fs/btrfs/tree-checker.c
|
|
@@ -0,0 +1,649 @@
|
|
+/*
|
|
+ * Copyright (C) Qu Wenruo 2017. All rights reserved.
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU General Public
|
|
+ * License v2 as published by the Free Software Foundation.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
+ * General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public
|
|
+ * License along with this program.
|
|
+ */
|
|
+
|
|
+/*
|
|
+ * The module is used to catch unexpected/corrupted tree block data.
|
|
+ * Such behavior can be caused either by a fuzzed image or bugs.
|
|
+ *
|
|
+ * The objective is to do leaf/node validation checks when tree block is read
|
|
+ * from disk, and check *every* possible member, so other code won't
|
|
+ * need to checking them again.
|
|
+ *
|
|
+ * Due to the potential and unwanted damage, every checker needs to be
|
|
+ * carefully reviewed otherwise so it does not prevent mount of valid images.
|
|
+ */
|
|
+
|
|
+#include "ctree.h"
|
|
+#include "tree-checker.h"
|
|
+#include "disk-io.h"
|
|
+#include "compression.h"
|
|
+#include "hash.h"
|
|
+#include "volumes.h"
|
|
+
|
|
+#define CORRUPT(reason, eb, root, slot) \
|
|
+ btrfs_crit(root->fs_info, \
|
|
+ "corrupt %s, %s: block=%llu, root=%llu, slot=%d", \
|
|
+ btrfs_header_level(eb) == 0 ? "leaf" : "node", \
|
|
+ reason, btrfs_header_bytenr(eb), root->objectid, slot)
|
|
+
|
|
+/*
|
|
+ * Error message should follow the following format:
|
|
+ * corrupt <type>: <identifier>, <reason>[, <bad_value>]
|
|
+ *
|
|
+ * @type: leaf or node
|
|
+ * @identifier: the necessary info to locate the leaf/node.
|
|
+ * It's recommened to decode key.objecitd/offset if it's
|
|
+ * meaningful.
|
|
+ * @reason: describe the error
|
|
+ * @bad_value: optional, it's recommened to output bad value and its
|
|
+ * expected value (range).
|
|
+ *
|
|
+ * Since comma is used to separate the components, only space is allowed
|
|
+ * inside each component.
|
|
+ */
|
|
+
|
|
+/*
|
|
+ * Append generic "corrupt leaf/node root=%llu block=%llu slot=%d: " to @fmt.
|
|
+ * Allows callers to customize the output.
|
|
+ */
|
|
+__printf(4, 5)
|
|
+static void generic_err(const struct btrfs_root *root,
|
|
+ const struct extent_buffer *eb, int slot,
|
|
+ const char *fmt, ...)
|
|
+{
|
|
+ struct va_format vaf;
|
|
+ va_list args;
|
|
+
|
|
+ va_start(args, fmt);
|
|
+
|
|
+ vaf.fmt = fmt;
|
|
+ vaf.va = &args;
|
|
+
|
|
+ btrfs_crit(root->fs_info,
|
|
+ "corrupt %s: root=%llu block=%llu slot=%d, %pV",
|
|
+ btrfs_header_level(eb) == 0 ? "leaf" : "node",
|
|
+ root->objectid, btrfs_header_bytenr(eb), slot, &vaf);
|
|
+ va_end(args);
|
|
+}
|
|
+
|
|
+static int check_extent_data_item(struct btrfs_root *root,
|
|
+ struct extent_buffer *leaf,
|
|
+ struct btrfs_key *key, int slot)
|
|
+{
|
|
+ struct btrfs_file_extent_item *fi;
|
|
+ u32 sectorsize = root->sectorsize;
|
|
+ u32 item_size = btrfs_item_size_nr(leaf, slot);
|
|
+
|
|
+ if (!IS_ALIGNED(key->offset, sectorsize)) {
|
|
+ CORRUPT("unaligned key offset for file extent",
|
|
+ leaf, root, slot);
|
|
+ return -EUCLEAN;
|
|
+ }
|
|
+
|
|
+ fi = btrfs_item_ptr(leaf, slot, struct btrfs_file_extent_item);
|
|
+
|
|
+ if (btrfs_file_extent_type(leaf, fi) > BTRFS_FILE_EXTENT_TYPES) {
|
|
+ CORRUPT("invalid file extent type", leaf, root, slot);
|
|
+ return -EUCLEAN;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Support for new compression/encrption must introduce incompat flag,
|
|
+ * and must be caught in open_ctree().
|
|
+ */
|
|
+ if (btrfs_file_extent_compression(leaf, fi) > BTRFS_COMPRESS_TYPES) {
|
|
+ CORRUPT("invalid file extent compression", leaf, root, slot);
|
|
+ return -EUCLEAN;
|
|
+ }
|
|
+ if (btrfs_file_extent_encryption(leaf, fi)) {
|
|
+ CORRUPT("invalid file extent encryption", leaf, root, slot);
|
|
+ return -EUCLEAN;
|
|
+ }
|
|
+ if (btrfs_file_extent_type(leaf, fi) == BTRFS_FILE_EXTENT_INLINE) {
|
|
+ /* Inline extent must have 0 as key offset */
|
|
+ if (key->offset) {
|
|
+ CORRUPT("inline extent has non-zero key offset",
|
|
+ leaf, root, slot);
|
|
+ return -EUCLEAN;
|
|
+ }
|
|
+
|
|
+ /* Compressed inline extent has no on-disk size, skip it */
|
|
+ if (btrfs_file_extent_compression(leaf, fi) !=
|
|
+ BTRFS_COMPRESS_NONE)
|
|
+ return 0;
|
|
+
|
|
+ /* Uncompressed inline extent size must match item size */
|
|
+ if (item_size != BTRFS_FILE_EXTENT_INLINE_DATA_START +
|
|
+ btrfs_file_extent_ram_bytes(leaf, fi)) {
|
|
+ CORRUPT("plaintext inline extent has invalid size",
|
|
+ leaf, root, slot);
|
|
+ return -EUCLEAN;
|
|
+ }
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ /* Regular or preallocated extent has fixed item size */
|
|
+ if (item_size != sizeof(*fi)) {
|
|
+ CORRUPT(
|
|
+ "regluar or preallocated extent data item size is invalid",
|
|
+ leaf, root, slot);
|
|
+ return -EUCLEAN;
|
|
+ }
|
|
+ if (!IS_ALIGNED(btrfs_file_extent_ram_bytes(leaf, fi), sectorsize) ||
|
|
+ !IS_ALIGNED(btrfs_file_extent_disk_bytenr(leaf, fi), sectorsize) ||
|
|
+ !IS_ALIGNED(btrfs_file_extent_disk_num_bytes(leaf, fi), sectorsize) ||
|
|
+ !IS_ALIGNED(btrfs_file_extent_offset(leaf, fi), sectorsize) ||
|
|
+ !IS_ALIGNED(btrfs_file_extent_num_bytes(leaf, fi), sectorsize)) {
|
|
+ CORRUPT(
|
|
+ "regular or preallocated extent data item has unaligned value",
|
|
+ leaf, root, slot);
|
|
+ return -EUCLEAN;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int check_csum_item(struct btrfs_root *root, struct extent_buffer *leaf,
|
|
+ struct btrfs_key *key, int slot)
|
|
+{
|
|
+ u32 sectorsize = root->sectorsize;
|
|
+ u32 csumsize = btrfs_super_csum_size(root->fs_info->super_copy);
|
|
+
|
|
+ if (key->objectid != BTRFS_EXTENT_CSUM_OBJECTID) {
|
|
+ CORRUPT("invalid objectid for csum item", leaf, root, slot);
|
|
+ return -EUCLEAN;
|
|
+ }
|
|
+ if (!IS_ALIGNED(key->offset, sectorsize)) {
|
|
+ CORRUPT("unaligned key offset for csum item", leaf, root, slot);
|
|
+ return -EUCLEAN;
|
|
+ }
|
|
+ if (!IS_ALIGNED(btrfs_item_size_nr(leaf, slot), csumsize)) {
|
|
+ CORRUPT("unaligned csum item size", leaf, root, slot);
|
|
+ return -EUCLEAN;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Customized reported for dir_item, only important new info is key->objectid,
|
|
+ * which represents inode number
|
|
+ */
|
|
+__printf(4, 5)
|
|
+static void dir_item_err(const struct btrfs_root *root,
|
|
+ const struct extent_buffer *eb, int slot,
|
|
+ const char *fmt, ...)
|
|
+{
|
|
+ struct btrfs_key key;
|
|
+ struct va_format vaf;
|
|
+ va_list args;
|
|
+
|
|
+ btrfs_item_key_to_cpu(eb, &key, slot);
|
|
+ va_start(args, fmt);
|
|
+
|
|
+ vaf.fmt = fmt;
|
|
+ vaf.va = &args;
|
|
+
|
|
+ btrfs_crit(root->fs_info,
|
|
+ "corrupt %s: root=%llu block=%llu slot=%d ino=%llu, %pV",
|
|
+ btrfs_header_level(eb) == 0 ? "leaf" : "node", root->objectid,
|
|
+ btrfs_header_bytenr(eb), slot, key.objectid, &vaf);
|
|
+ va_end(args);
|
|
+}
|
|
+
|
|
+static int check_dir_item(struct btrfs_root *root,
|
|
+ struct extent_buffer *leaf,
|
|
+ struct btrfs_key *key, int slot)
|
|
+{
|
|
+ struct btrfs_dir_item *di;
|
|
+ u32 item_size = btrfs_item_size_nr(leaf, slot);
|
|
+ u32 cur = 0;
|
|
+
|
|
+ di = btrfs_item_ptr(leaf, slot, struct btrfs_dir_item);
|
|
+ while (cur < item_size) {
|
|
+ u32 name_len;
|
|
+ u32 data_len;
|
|
+ u32 max_name_len;
|
|
+ u32 total_size;
|
|
+ u32 name_hash;
|
|
+ u8 dir_type;
|
|
+
|
|
+ /* header itself should not cross item boundary */
|
|
+ if (cur + sizeof(*di) > item_size) {
|
|
+ dir_item_err(root, leaf, slot,
|
|
+ "dir item header crosses item boundary, have %zu boundary %u",
|
|
+ cur + sizeof(*di), item_size);
|
|
+ return -EUCLEAN;
|
|
+ }
|
|
+
|
|
+ /* dir type check */
|
|
+ dir_type = btrfs_dir_type(leaf, di);
|
|
+ if (dir_type >= BTRFS_FT_MAX) {
|
|
+ dir_item_err(root, leaf, slot,
|
|
+ "invalid dir item type, have %u expect [0, %u)",
|
|
+ dir_type, BTRFS_FT_MAX);
|
|
+ return -EUCLEAN;
|
|
+ }
|
|
+
|
|
+ if (key->type == BTRFS_XATTR_ITEM_KEY &&
|
|
+ dir_type != BTRFS_FT_XATTR) {
|
|
+ dir_item_err(root, leaf, slot,
|
|
+ "invalid dir item type for XATTR key, have %u expect %u",
|
|
+ dir_type, BTRFS_FT_XATTR);
|
|
+ return -EUCLEAN;
|
|
+ }
|
|
+ if (dir_type == BTRFS_FT_XATTR &&
|
|
+ key->type != BTRFS_XATTR_ITEM_KEY) {
|
|
+ dir_item_err(root, leaf, slot,
|
|
+ "xattr dir type found for non-XATTR key");
|
|
+ return -EUCLEAN;
|
|
+ }
|
|
+ if (dir_type == BTRFS_FT_XATTR)
|
|
+ max_name_len = XATTR_NAME_MAX;
|
|
+ else
|
|
+ max_name_len = BTRFS_NAME_LEN;
|
|
+
|
|
+ /* Name/data length check */
|
|
+ name_len = btrfs_dir_name_len(leaf, di);
|
|
+ data_len = btrfs_dir_data_len(leaf, di);
|
|
+ if (name_len > max_name_len) {
|
|
+ dir_item_err(root, leaf, slot,
|
|
+ "dir item name len too long, have %u max %u",
|
|
+ name_len, max_name_len);
|
|
+ return -EUCLEAN;
|
|
+ }
|
|
+ if (name_len + data_len > BTRFS_MAX_XATTR_SIZE(root)) {
|
|
+ dir_item_err(root, leaf, slot,
|
|
+ "dir item name and data len too long, have %u max %zu",
|
|
+ name_len + data_len,
|
|
+ BTRFS_MAX_XATTR_SIZE(root));
|
|
+ return -EUCLEAN;
|
|
+ }
|
|
+
|
|
+ if (data_len && dir_type != BTRFS_FT_XATTR) {
|
|
+ dir_item_err(root, leaf, slot,
|
|
+ "dir item with invalid data len, have %u expect 0",
|
|
+ data_len);
|
|
+ return -EUCLEAN;
|
|
+ }
|
|
+
|
|
+ total_size = sizeof(*di) + name_len + data_len;
|
|
+
|
|
+ /* header and name/data should not cross item boundary */
|
|
+ if (cur + total_size > item_size) {
|
|
+ dir_item_err(root, leaf, slot,
|
|
+ "dir item data crosses item boundary, have %u boundary %u",
|
|
+ cur + total_size, item_size);
|
|
+ return -EUCLEAN;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Special check for XATTR/DIR_ITEM, as key->offset is name
|
|
+ * hash, should match its name
|
|
+ */
|
|
+ if (key->type == BTRFS_DIR_ITEM_KEY ||
|
|
+ key->type == BTRFS_XATTR_ITEM_KEY) {
|
|
+ char namebuf[max(BTRFS_NAME_LEN, XATTR_NAME_MAX)];
|
|
+
|
|
+ read_extent_buffer(leaf, namebuf,
|
|
+ (unsigned long)(di + 1), name_len);
|
|
+ name_hash = btrfs_name_hash(namebuf, name_len);
|
|
+ if (key->offset != name_hash) {
|
|
+ dir_item_err(root, leaf, slot,
|
|
+ "name hash mismatch with key, have 0x%016x expect 0x%016llx",
|
|
+ name_hash, key->offset);
|
|
+ return -EUCLEAN;
|
|
+ }
|
|
+ }
|
|
+ cur += total_size;
|
|
+ di = (struct btrfs_dir_item *)((void *)di + total_size);
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+__printf(4, 5)
|
|
+__cold
|
|
+static void block_group_err(const struct btrfs_fs_info *fs_info,
|
|
+ const struct extent_buffer *eb, int slot,
|
|
+ const char *fmt, ...)
|
|
+{
|
|
+ struct btrfs_key key;
|
|
+ struct va_format vaf;
|
|
+ va_list args;
|
|
+
|
|
+ btrfs_item_key_to_cpu(eb, &key, slot);
|
|
+ va_start(args, fmt);
|
|
+
|
|
+ vaf.fmt = fmt;
|
|
+ vaf.va = &args;
|
|
+
|
|
+ btrfs_crit(fs_info,
|
|
+ "corrupt %s: root=%llu block=%llu slot=%d bg_start=%llu bg_len=%llu, %pV",
|
|
+ btrfs_header_level(eb) == 0 ? "leaf" : "node",
|
|
+ btrfs_header_owner(eb), btrfs_header_bytenr(eb), slot,
|
|
+ key.objectid, key.offset, &vaf);
|
|
+ va_end(args);
|
|
+}
|
|
+
|
|
+static int check_block_group_item(struct btrfs_fs_info *fs_info,
|
|
+ struct extent_buffer *leaf,
|
|
+ struct btrfs_key *key, int slot)
|
|
+{
|
|
+ struct btrfs_block_group_item bgi;
|
|
+ u32 item_size = btrfs_item_size_nr(leaf, slot);
|
|
+ u64 flags;
|
|
+ u64 type;
|
|
+
|
|
+ /*
|
|
+ * Here we don't really care about alignment since extent allocator can
|
|
+ * handle it. We care more about the size, as if one block group is
|
|
+ * larger than maximum size, it's must be some obvious corruption.
|
|
+ */
|
|
+ if (key->offset > BTRFS_MAX_DATA_CHUNK_SIZE || key->offset == 0) {
|
|
+ block_group_err(fs_info, leaf, slot,
|
|
+ "invalid block group size, have %llu expect (0, %llu]",
|
|
+ key->offset, BTRFS_MAX_DATA_CHUNK_SIZE);
|
|
+ return -EUCLEAN;
|
|
+ }
|
|
+
|
|
+ if (item_size != sizeof(bgi)) {
|
|
+ block_group_err(fs_info, leaf, slot,
|
|
+ "invalid item size, have %u expect %zu",
|
|
+ item_size, sizeof(bgi));
|
|
+ return -EUCLEAN;
|
|
+ }
|
|
+
|
|
+ read_extent_buffer(leaf, &bgi, btrfs_item_ptr_offset(leaf, slot),
|
|
+ sizeof(bgi));
|
|
+ if (btrfs_block_group_chunk_objectid(&bgi) !=
|
|
+ BTRFS_FIRST_CHUNK_TREE_OBJECTID) {
|
|
+ block_group_err(fs_info, leaf, slot,
|
|
+ "invalid block group chunk objectid, have %llu expect %llu",
|
|
+ btrfs_block_group_chunk_objectid(&bgi),
|
|
+ BTRFS_FIRST_CHUNK_TREE_OBJECTID);
|
|
+ return -EUCLEAN;
|
|
+ }
|
|
+
|
|
+ if (btrfs_block_group_used(&bgi) > key->offset) {
|
|
+ block_group_err(fs_info, leaf, slot,
|
|
+ "invalid block group used, have %llu expect [0, %llu)",
|
|
+ btrfs_block_group_used(&bgi), key->offset);
|
|
+ return -EUCLEAN;
|
|
+ }
|
|
+
|
|
+ flags = btrfs_block_group_flags(&bgi);
|
|
+ if (hweight64(flags & BTRFS_BLOCK_GROUP_PROFILE_MASK) > 1) {
|
|
+ block_group_err(fs_info, leaf, slot,
|
|
+"invalid profile flags, have 0x%llx (%lu bits set) expect no more than 1 bit set",
|
|
+ flags & BTRFS_BLOCK_GROUP_PROFILE_MASK,
|
|
+ hweight64(flags & BTRFS_BLOCK_GROUP_PROFILE_MASK));
|
|
+ return -EUCLEAN;
|
|
+ }
|
|
+
|
|
+ type = flags & BTRFS_BLOCK_GROUP_TYPE_MASK;
|
|
+ if (type != BTRFS_BLOCK_GROUP_DATA &&
|
|
+ type != BTRFS_BLOCK_GROUP_METADATA &&
|
|
+ type != BTRFS_BLOCK_GROUP_SYSTEM &&
|
|
+ type != (BTRFS_BLOCK_GROUP_METADATA |
|
|
+ BTRFS_BLOCK_GROUP_DATA)) {
|
|
+ block_group_err(fs_info, leaf, slot,
|
|
+"invalid type, have 0x%llx (%lu bits set) expect either 0x%llx, 0x%llx, 0x%llx or 0x%llx",
|
|
+ type, hweight64(type),
|
|
+ BTRFS_BLOCK_GROUP_DATA, BTRFS_BLOCK_GROUP_METADATA,
|
|
+ BTRFS_BLOCK_GROUP_SYSTEM,
|
|
+ BTRFS_BLOCK_GROUP_METADATA | BTRFS_BLOCK_GROUP_DATA);
|
|
+ return -EUCLEAN;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Common point to switch the item-specific validation.
|
|
+ */
|
|
+static int check_leaf_item(struct btrfs_root *root,
|
|
+ struct extent_buffer *leaf,
|
|
+ struct btrfs_key *key, int slot)
|
|
+{
|
|
+ int ret = 0;
|
|
+
|
|
+ switch (key->type) {
|
|
+ case BTRFS_EXTENT_DATA_KEY:
|
|
+ ret = check_extent_data_item(root, leaf, key, slot);
|
|
+ break;
|
|
+ case BTRFS_EXTENT_CSUM_KEY:
|
|
+ ret = check_csum_item(root, leaf, key, slot);
|
|
+ break;
|
|
+ case BTRFS_DIR_ITEM_KEY:
|
|
+ case BTRFS_DIR_INDEX_KEY:
|
|
+ case BTRFS_XATTR_ITEM_KEY:
|
|
+ ret = check_dir_item(root, leaf, key, slot);
|
|
+ break;
|
|
+ case BTRFS_BLOCK_GROUP_ITEM_KEY:
|
|
+ ret = check_block_group_item(root->fs_info, leaf, key, slot);
|
|
+ break;
|
|
+ }
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int check_leaf(struct btrfs_root *root, struct extent_buffer *leaf,
|
|
+ bool check_item_data)
|
|
+{
|
|
+ struct btrfs_fs_info *fs_info = root->fs_info;
|
|
+ /* No valid key type is 0, so all key should be larger than this key */
|
|
+ struct btrfs_key prev_key = {0, 0, 0};
|
|
+ struct btrfs_key key;
|
|
+ u32 nritems = btrfs_header_nritems(leaf);
|
|
+ int slot;
|
|
+
|
|
+ if (btrfs_header_level(leaf) != 0) {
|
|
+ generic_err(root, leaf, 0,
|
|
+ "invalid level for leaf, have %d expect 0",
|
|
+ btrfs_header_level(leaf));
|
|
+ return -EUCLEAN;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Extent buffers from a relocation tree have a owner field that
|
|
+ * corresponds to the subvolume tree they are based on. So just from an
|
|
+ * extent buffer alone we can not find out what is the id of the
|
|
+ * corresponding subvolume tree, so we can not figure out if the extent
|
|
+ * buffer corresponds to the root of the relocation tree or not. So
|
|
+ * skip this check for relocation trees.
|
|
+ */
|
|
+ if (nritems == 0 && !btrfs_header_flag(leaf, BTRFS_HEADER_FLAG_RELOC)) {
|
|
+ u64 owner = btrfs_header_owner(leaf);
|
|
+ struct btrfs_root *check_root;
|
|
+
|
|
+ /* These trees must never be empty */
|
|
+ if (owner == BTRFS_ROOT_TREE_OBJECTID ||
|
|
+ owner == BTRFS_CHUNK_TREE_OBJECTID ||
|
|
+ owner == BTRFS_EXTENT_TREE_OBJECTID ||
|
|
+ owner == BTRFS_DEV_TREE_OBJECTID ||
|
|
+ owner == BTRFS_FS_TREE_OBJECTID ||
|
|
+ owner == BTRFS_DATA_RELOC_TREE_OBJECTID) {
|
|
+ generic_err(root, leaf, 0,
|
|
+ "invalid root, root %llu must never be empty",
|
|
+ owner);
|
|
+ return -EUCLEAN;
|
|
+ }
|
|
+ key.objectid = owner;
|
|
+ key.type = BTRFS_ROOT_ITEM_KEY;
|
|
+ key.offset = (u64)-1;
|
|
+
|
|
+ check_root = btrfs_get_fs_root(fs_info, &key, false);
|
|
+ /*
|
|
+ * The only reason we also check NULL here is that during
|
|
+ * open_ctree() some roots has not yet been set up.
|
|
+ */
|
|
+ if (!IS_ERR_OR_NULL(check_root)) {
|
|
+ struct extent_buffer *eb;
|
|
+
|
|
+ eb = btrfs_root_node(check_root);
|
|
+ /* if leaf is the root, then it's fine */
|
|
+ if (leaf != eb) {
|
|
+ CORRUPT("non-root leaf's nritems is 0",
|
|
+ leaf, check_root, 0);
|
|
+ free_extent_buffer(eb);
|
|
+ return -EUCLEAN;
|
|
+ }
|
|
+ free_extent_buffer(eb);
|
|
+ }
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if (nritems == 0)
|
|
+ return 0;
|
|
+
|
|
+ /*
|
|
+ * Check the following things to make sure this is a good leaf, and
|
|
+ * leaf users won't need to bother with similar sanity checks:
|
|
+ *
|
|
+ * 1) key ordering
|
|
+ * 2) item offset and size
|
|
+ * No overlap, no hole, all inside the leaf.
|
|
+ * 3) item content
|
|
+ * If possible, do comprehensive sanity check.
|
|
+ * NOTE: All checks must only rely on the item data itself.
|
|
+ */
|
|
+ for (slot = 0; slot < nritems; slot++) {
|
|
+ u32 item_end_expected;
|
|
+ int ret;
|
|
+
|
|
+ btrfs_item_key_to_cpu(leaf, &key, slot);
|
|
+
|
|
+ /* Make sure the keys are in the right order */
|
|
+ if (btrfs_comp_cpu_keys(&prev_key, &key) >= 0) {
|
|
+ CORRUPT("bad key order", leaf, root, slot);
|
|
+ return -EUCLEAN;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Make sure the offset and ends are right, remember that the
|
|
+ * item data starts at the end of the leaf and grows towards the
|
|
+ * front.
|
|
+ */
|
|
+ if (slot == 0)
|
|
+ item_end_expected = BTRFS_LEAF_DATA_SIZE(root);
|
|
+ else
|
|
+ item_end_expected = btrfs_item_offset_nr(leaf,
|
|
+ slot - 1);
|
|
+ if (btrfs_item_end_nr(leaf, slot) != item_end_expected) {
|
|
+ CORRUPT("slot offset bad", leaf, root, slot);
|
|
+ return -EUCLEAN;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Check to make sure that we don't point outside of the leaf,
|
|
+ * just in case all the items are consistent to each other, but
|
|
+ * all point outside of the leaf.
|
|
+ */
|
|
+ if (btrfs_item_end_nr(leaf, slot) >
|
|
+ BTRFS_LEAF_DATA_SIZE(root)) {
|
|
+ CORRUPT("slot end outside of leaf", leaf, root, slot);
|
|
+ return -EUCLEAN;
|
|
+ }
|
|
+
|
|
+ /* Also check if the item pointer overlaps with btrfs item. */
|
|
+ if (btrfs_item_nr_offset(slot) + sizeof(struct btrfs_item) >
|
|
+ btrfs_item_ptr_offset(leaf, slot)) {
|
|
+ CORRUPT("slot overlap with its data", leaf, root, slot);
|
|
+ return -EUCLEAN;
|
|
+ }
|
|
+
|
|
+ if (check_item_data) {
|
|
+ /*
|
|
+ * Check if the item size and content meet other
|
|
+ * criteria
|
|
+ */
|
|
+ ret = check_leaf_item(root, leaf, &key, slot);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ prev_key.objectid = key.objectid;
|
|
+ prev_key.type = key.type;
|
|
+ prev_key.offset = key.offset;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int btrfs_check_leaf_full(struct btrfs_root *root, struct extent_buffer *leaf)
|
|
+{
|
|
+ return check_leaf(root, leaf, true);
|
|
+}
|
|
+
|
|
+int btrfs_check_leaf_relaxed(struct btrfs_root *root,
|
|
+ struct extent_buffer *leaf)
|
|
+{
|
|
+ return check_leaf(root, leaf, false);
|
|
+}
|
|
+
|
|
+int btrfs_check_node(struct btrfs_root *root, struct extent_buffer *node)
|
|
+{
|
|
+ unsigned long nr = btrfs_header_nritems(node);
|
|
+ struct btrfs_key key, next_key;
|
|
+ int slot;
|
|
+ int level = btrfs_header_level(node);
|
|
+ u64 bytenr;
|
|
+ int ret = 0;
|
|
+
|
|
+ if (level <= 0 || level >= BTRFS_MAX_LEVEL) {
|
|
+ generic_err(root, node, 0,
|
|
+ "invalid level for node, have %d expect [1, %d]",
|
|
+ level, BTRFS_MAX_LEVEL - 1);
|
|
+ return -EUCLEAN;
|
|
+ }
|
|
+ if (nr == 0 || nr > BTRFS_NODEPTRS_PER_BLOCK(root)) {
|
|
+ btrfs_crit(root->fs_info,
|
|
+"corrupt node: root=%llu block=%llu, nritems too %s, have %lu expect range [1,%zu]",
|
|
+ root->objectid, node->start,
|
|
+ nr == 0 ? "small" : "large", nr,
|
|
+ BTRFS_NODEPTRS_PER_BLOCK(root));
|
|
+ return -EUCLEAN;
|
|
+ }
|
|
+
|
|
+ for (slot = 0; slot < nr - 1; slot++) {
|
|
+ bytenr = btrfs_node_blockptr(node, slot);
|
|
+ btrfs_node_key_to_cpu(node, &key, slot);
|
|
+ btrfs_node_key_to_cpu(node, &next_key, slot + 1);
|
|
+
|
|
+ if (!bytenr) {
|
|
+ generic_err(root, node, slot,
|
|
+ "invalid NULL node pointer");
|
|
+ ret = -EUCLEAN;
|
|
+ goto out;
|
|
+ }
|
|
+ if (!IS_ALIGNED(bytenr, root->sectorsize)) {
|
|
+ generic_err(root, node, slot,
|
|
+ "unaligned pointer, have %llu should be aligned to %u",
|
|
+ bytenr, root->sectorsize);
|
|
+ ret = -EUCLEAN;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ if (btrfs_comp_cpu_keys(&key, &next_key) >= 0) {
|
|
+ generic_err(root, node, slot,
|
|
+ "bad key order, current (%llu %u %llu) next (%llu %u %llu)",
|
|
+ key.objectid, key.type, key.offset,
|
|
+ next_key.objectid, next_key.type,
|
|
+ next_key.offset);
|
|
+ ret = -EUCLEAN;
|
|
+ goto out;
|
|
+ }
|
|
+ }
|
|
+out:
|
|
+ return ret;
|
|
+}
|
|
diff --git a/fs/btrfs/tree-checker.h b/fs/btrfs/tree-checker.h
|
|
new file mode 100644
|
|
index 000000000000..3d53e8d6fda0
|
|
--- /dev/null
|
|
+++ b/fs/btrfs/tree-checker.h
|
|
@@ -0,0 +1,38 @@
|
|
+/*
|
|
+ * Copyright (C) Qu Wenruo 2017. All rights reserved.
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU General Public
|
|
+ * License v2 as published by the Free Software Foundation.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
+ * General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public
|
|
+ * License along with this program.
|
|
+ */
|
|
+
|
|
+#ifndef __BTRFS_TREE_CHECKER__
|
|
+#define __BTRFS_TREE_CHECKER__
|
|
+
|
|
+#include "ctree.h"
|
|
+#include "extent_io.h"
|
|
+
|
|
+/*
|
|
+ * Comprehensive leaf checker.
|
|
+ * Will check not only the item pointers, but also every possible member
|
|
+ * in item data.
|
|
+ */
|
|
+int btrfs_check_leaf_full(struct btrfs_root *root, struct extent_buffer *leaf);
|
|
+
|
|
+/*
|
|
+ * Less strict leaf checker.
|
|
+ * Will only check item pointers, not reading item data.
|
|
+ */
|
|
+int btrfs_check_leaf_relaxed(struct btrfs_root *root,
|
|
+ struct extent_buffer *leaf);
|
|
+int btrfs_check_node(struct btrfs_root *root, struct extent_buffer *node);
|
|
+
|
|
+#endif
|
|
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
|
|
index b4d63a9842fa..5e8fe8f3942d 100644
|
|
--- a/fs/btrfs/volumes.c
|
|
+++ b/fs/btrfs/volumes.c
|
|
@@ -1184,7 +1184,7 @@ again:
|
|
struct map_lookup *map;
|
|
int i;
|
|
|
|
- map = (struct map_lookup *)em->bdev;
|
|
+ map = em->map_lookup;
|
|
for (i = 0; i < map->num_stripes; i++) {
|
|
u64 end;
|
|
|
|
@@ -2757,7 +2757,7 @@ int btrfs_remove_chunk(struct btrfs_trans_handle *trans,
|
|
free_extent_map(em);
|
|
return -EINVAL;
|
|
}
|
|
- map = (struct map_lookup *)em->bdev;
|
|
+ map = em->map_lookup;
|
|
lock_chunks(root->fs_info->chunk_root);
|
|
check_system_chunk(trans, extent_root, map->type);
|
|
unlock_chunks(root->fs_info->chunk_root);
|
|
@@ -4540,7 +4540,7 @@ static int __btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
|
|
|
|
if (type & BTRFS_BLOCK_GROUP_DATA) {
|
|
max_stripe_size = 1024 * 1024 * 1024;
|
|
- max_chunk_size = 10 * max_stripe_size;
|
|
+ max_chunk_size = BTRFS_MAX_DATA_CHUNK_SIZE;
|
|
if (!devs_max)
|
|
devs_max = BTRFS_MAX_DEVS(info->chunk_root);
|
|
} else if (type & BTRFS_BLOCK_GROUP_METADATA) {
|
|
@@ -4731,7 +4731,7 @@ static int __btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
|
|
goto error;
|
|
}
|
|
set_bit(EXTENT_FLAG_FS_MAPPING, &em->flags);
|
|
- em->bdev = (struct block_device *)map;
|
|
+ em->map_lookup = map;
|
|
em->start = start;
|
|
em->len = num_bytes;
|
|
em->block_start = 0;
|
|
@@ -4826,7 +4826,7 @@ int btrfs_finish_chunk_alloc(struct btrfs_trans_handle *trans,
|
|
return -EINVAL;
|
|
}
|
|
|
|
- map = (struct map_lookup *)em->bdev;
|
|
+ map = em->map_lookup;
|
|
item_size = btrfs_chunk_item_size(map->num_stripes);
|
|
stripe_size = em->orig_block_len;
|
|
|
|
@@ -4968,7 +4968,7 @@ int btrfs_chunk_readonly(struct btrfs_root *root, u64 chunk_offset)
|
|
if (!em)
|
|
return 1;
|
|
|
|
- map = (struct map_lookup *)em->bdev;
|
|
+ map = em->map_lookup;
|
|
for (i = 0; i < map->num_stripes; i++) {
|
|
if (map->stripes[i].dev->missing) {
|
|
miss_ndevs++;
|
|
@@ -5048,7 +5048,7 @@ int btrfs_num_copies(struct btrfs_fs_info *fs_info, u64 logical, u64 len)
|
|
return 1;
|
|
}
|
|
|
|
- map = (struct map_lookup *)em->bdev;
|
|
+ map = em->map_lookup;
|
|
if (map->type & (BTRFS_BLOCK_GROUP_DUP | BTRFS_BLOCK_GROUP_RAID1))
|
|
ret = map->num_stripes;
|
|
else if (map->type & BTRFS_BLOCK_GROUP_RAID10)
|
|
@@ -5091,7 +5091,7 @@ unsigned long btrfs_full_stripe_len(struct btrfs_root *root,
|
|
BUG_ON(!em);
|
|
|
|
BUG_ON(em->start > logical || em->start + em->len < logical);
|
|
- map = (struct map_lookup *)em->bdev;
|
|
+ map = em->map_lookup;
|
|
if (map->type & BTRFS_BLOCK_GROUP_RAID56_MASK)
|
|
len = map->stripe_len * nr_data_stripes(map);
|
|
free_extent_map(em);
|
|
@@ -5112,7 +5112,7 @@ int btrfs_is_parity_mirror(struct btrfs_mapping_tree *map_tree,
|
|
BUG_ON(!em);
|
|
|
|
BUG_ON(em->start > logical || em->start + em->len < logical);
|
|
- map = (struct map_lookup *)em->bdev;
|
|
+ map = em->map_lookup;
|
|
if (map->type & BTRFS_BLOCK_GROUP_RAID56_MASK)
|
|
ret = 1;
|
|
free_extent_map(em);
|
|
@@ -5271,7 +5271,7 @@ static int __btrfs_map_block(struct btrfs_fs_info *fs_info, int rw,
|
|
return -EINVAL;
|
|
}
|
|
|
|
- map = (struct map_lookup *)em->bdev;
|
|
+ map = em->map_lookup;
|
|
offset = logical - em->start;
|
|
|
|
stripe_len = map->stripe_len;
|
|
@@ -5813,7 +5813,7 @@ int btrfs_rmap_block(struct btrfs_mapping_tree *map_tree,
|
|
free_extent_map(em);
|
|
return -EIO;
|
|
}
|
|
- map = (struct map_lookup *)em->bdev;
|
|
+ map = em->map_lookup;
|
|
|
|
length = em->len;
|
|
rmap_len = map->stripe_len;
|
|
@@ -6208,6 +6208,101 @@ struct btrfs_device *btrfs_alloc_device(struct btrfs_fs_info *fs_info,
|
|
return dev;
|
|
}
|
|
|
|
+/* Return -EIO if any error, otherwise return 0. */
|
|
+static int btrfs_check_chunk_valid(struct btrfs_root *root,
|
|
+ struct extent_buffer *leaf,
|
|
+ struct btrfs_chunk *chunk, u64 logical)
|
|
+{
|
|
+ u64 length;
|
|
+ u64 stripe_len;
|
|
+ u16 num_stripes;
|
|
+ u16 sub_stripes;
|
|
+ u64 type;
|
|
+ u64 features;
|
|
+ bool mixed = false;
|
|
+
|
|
+ length = btrfs_chunk_length(leaf, chunk);
|
|
+ stripe_len = btrfs_chunk_stripe_len(leaf, chunk);
|
|
+ num_stripes = btrfs_chunk_num_stripes(leaf, chunk);
|
|
+ sub_stripes = btrfs_chunk_sub_stripes(leaf, chunk);
|
|
+ type = btrfs_chunk_type(leaf, chunk);
|
|
+
|
|
+ if (!num_stripes) {
|
|
+ btrfs_err(root->fs_info, "invalid chunk num_stripes: %u",
|
|
+ num_stripes);
|
|
+ return -EIO;
|
|
+ }
|
|
+ if (!IS_ALIGNED(logical, root->sectorsize)) {
|
|
+ btrfs_err(root->fs_info,
|
|
+ "invalid chunk logical %llu", logical);
|
|
+ return -EIO;
|
|
+ }
|
|
+ if (btrfs_chunk_sector_size(leaf, chunk) != root->sectorsize) {
|
|
+ btrfs_err(root->fs_info, "invalid chunk sectorsize %u",
|
|
+ btrfs_chunk_sector_size(leaf, chunk));
|
|
+ return -EIO;
|
|
+ }
|
|
+ if (!length || !IS_ALIGNED(length, root->sectorsize)) {
|
|
+ btrfs_err(root->fs_info,
|
|
+ "invalid chunk length %llu", length);
|
|
+ return -EIO;
|
|
+ }
|
|
+ if (!is_power_of_2(stripe_len)) {
|
|
+ btrfs_err(root->fs_info, "invalid chunk stripe length: %llu",
|
|
+ stripe_len);
|
|
+ return -EIO;
|
|
+ }
|
|
+ if (~(BTRFS_BLOCK_GROUP_TYPE_MASK | BTRFS_BLOCK_GROUP_PROFILE_MASK) &
|
|
+ type) {
|
|
+ btrfs_err(root->fs_info, "unrecognized chunk type: %llu",
|
|
+ ~(BTRFS_BLOCK_GROUP_TYPE_MASK |
|
|
+ BTRFS_BLOCK_GROUP_PROFILE_MASK) &
|
|
+ btrfs_chunk_type(leaf, chunk));
|
|
+ return -EIO;
|
|
+ }
|
|
+
|
|
+ if ((type & BTRFS_BLOCK_GROUP_TYPE_MASK) == 0) {
|
|
+ btrfs_err(root->fs_info, "missing chunk type flag: 0x%llx", type);
|
|
+ return -EIO;
|
|
+ }
|
|
+
|
|
+ if ((type & BTRFS_BLOCK_GROUP_SYSTEM) &&
|
|
+ (type & (BTRFS_BLOCK_GROUP_METADATA | BTRFS_BLOCK_GROUP_DATA))) {
|
|
+ btrfs_err(root->fs_info,
|
|
+ "system chunk with data or metadata type: 0x%llx", type);
|
|
+ return -EIO;
|
|
+ }
|
|
+
|
|
+ features = btrfs_super_incompat_flags(root->fs_info->super_copy);
|
|
+ if (features & BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS)
|
|
+ mixed = true;
|
|
+
|
|
+ if (!mixed) {
|
|
+ if ((type & BTRFS_BLOCK_GROUP_METADATA) &&
|
|
+ (type & BTRFS_BLOCK_GROUP_DATA)) {
|
|
+ btrfs_err(root->fs_info,
|
|
+ "mixed chunk type in non-mixed mode: 0x%llx", type);
|
|
+ return -EIO;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if ((type & BTRFS_BLOCK_GROUP_RAID10 && sub_stripes != 2) ||
|
|
+ (type & BTRFS_BLOCK_GROUP_RAID1 && num_stripes < 1) ||
|
|
+ (type & BTRFS_BLOCK_GROUP_RAID5 && num_stripes < 2) ||
|
|
+ (type & BTRFS_BLOCK_GROUP_RAID6 && num_stripes < 3) ||
|
|
+ (type & BTRFS_BLOCK_GROUP_DUP && num_stripes > 2) ||
|
|
+ ((type & BTRFS_BLOCK_GROUP_PROFILE_MASK) == 0 &&
|
|
+ num_stripes != 1)) {
|
|
+ btrfs_err(root->fs_info,
|
|
+ "invalid num_stripes:sub_stripes %u:%u for profile %llu",
|
|
+ num_stripes, sub_stripes,
|
|
+ type & BTRFS_BLOCK_GROUP_PROFILE_MASK);
|
|
+ return -EIO;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static int read_one_chunk(struct btrfs_root *root, struct btrfs_key *key,
|
|
struct extent_buffer *leaf,
|
|
struct btrfs_chunk *chunk)
|
|
@@ -6217,6 +6312,7 @@ static int read_one_chunk(struct btrfs_root *root, struct btrfs_key *key,
|
|
struct extent_map *em;
|
|
u64 logical;
|
|
u64 length;
|
|
+ u64 stripe_len;
|
|
u64 devid;
|
|
u8 uuid[BTRFS_UUID_SIZE];
|
|
int num_stripes;
|
|
@@ -6225,6 +6321,12 @@ static int read_one_chunk(struct btrfs_root *root, struct btrfs_key *key,
|
|
|
|
logical = key->offset;
|
|
length = btrfs_chunk_length(leaf, chunk);
|
|
+ stripe_len = btrfs_chunk_stripe_len(leaf, chunk);
|
|
+ num_stripes = btrfs_chunk_num_stripes(leaf, chunk);
|
|
+
|
|
+ ret = btrfs_check_chunk_valid(root, leaf, chunk, logical);
|
|
+ if (ret)
|
|
+ return ret;
|
|
|
|
read_lock(&map_tree->map_tree.lock);
|
|
em = lookup_extent_mapping(&map_tree->map_tree, logical, 1);
|
|
@@ -6241,7 +6343,6 @@ static int read_one_chunk(struct btrfs_root *root, struct btrfs_key *key,
|
|
em = alloc_extent_map();
|
|
if (!em)
|
|
return -ENOMEM;
|
|
- num_stripes = btrfs_chunk_num_stripes(leaf, chunk);
|
|
map = kmalloc(map_lookup_size(num_stripes), GFP_NOFS);
|
|
if (!map) {
|
|
free_extent_map(em);
|
|
@@ -6249,7 +6350,7 @@ static int read_one_chunk(struct btrfs_root *root, struct btrfs_key *key,
|
|
}
|
|
|
|
set_bit(EXTENT_FLAG_FS_MAPPING, &em->flags);
|
|
- em->bdev = (struct block_device *)map;
|
|
+ em->map_lookup = map;
|
|
em->start = logical;
|
|
em->len = length;
|
|
em->orig_start = 0;
|
|
@@ -6473,6 +6574,7 @@ int btrfs_read_sys_array(struct btrfs_root *root)
|
|
u32 array_size;
|
|
u32 len = 0;
|
|
u32 cur_offset;
|
|
+ u64 type;
|
|
struct btrfs_key key;
|
|
|
|
ASSERT(BTRFS_SUPER_INFO_SIZE <= root->nodesize);
|
|
@@ -6539,6 +6641,15 @@ int btrfs_read_sys_array(struct btrfs_root *root)
|
|
break;
|
|
}
|
|
|
|
+ type = btrfs_chunk_type(sb, chunk);
|
|
+ if ((type & BTRFS_BLOCK_GROUP_SYSTEM) == 0) {
|
|
+ btrfs_err(root->fs_info,
|
|
+ "invalid chunk type %llu in sys_array at offset %u",
|
|
+ type, cur_offset);
|
|
+ ret = -EIO;
|
|
+ break;
|
|
+ }
|
|
+
|
|
len = btrfs_chunk_item_size(num_stripes);
|
|
if (cur_offset + len > array_size)
|
|
goto out_short_read;
|
|
@@ -6948,7 +7059,7 @@ void btrfs_update_commit_device_bytes_used(struct btrfs_root *root,
|
|
/* In order to kick the device replace finish process */
|
|
lock_chunks(root);
|
|
list_for_each_entry(em, &transaction->pending_chunks, list) {
|
|
- map = (struct map_lookup *)em->bdev;
|
|
+ map = em->map_lookup;
|
|
|
|
for (i = 0; i < map->num_stripes; i++) {
|
|
dev = map->stripes[i].dev;
|
|
diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h
|
|
index d5c84f6b1353..3c651df420be 100644
|
|
--- a/fs/btrfs/volumes.h
|
|
+++ b/fs/btrfs/volumes.h
|
|
@@ -24,6 +24,8 @@
|
|
#include <linux/btrfs.h>
|
|
#include "async-thread.h"
|
|
|
|
+#define BTRFS_MAX_DATA_CHUNK_SIZE (10ULL * SZ_1G)
|
|
+
|
|
extern struct mutex uuid_mutex;
|
|
|
|
#define BTRFS_STRIPE_LEN (64 * 1024)
|
|
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
|
|
index 0141aba9eca6..026b399af215 100644
|
|
--- a/fs/cifs/file.c
|
|
+++ b/fs/cifs/file.c
|
|
@@ -1073,10 +1073,10 @@ cifs_push_mandatory_locks(struct cifsFileInfo *cfile)
|
|
|
|
/*
|
|
* Accessing maxBuf is racy with cifs_reconnect - need to store value
|
|
- * and check it for zero before using.
|
|
+ * and check it before using.
|
|
*/
|
|
max_buf = tcon->ses->server->maxBuf;
|
|
- if (!max_buf) {
|
|
+ if (max_buf < (sizeof(struct smb_hdr) + sizeof(LOCKING_ANDX_RANGE))) {
|
|
free_xid(xid);
|
|
return -EINVAL;
|
|
}
|
|
@@ -1404,10 +1404,10 @@ cifs_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock,
|
|
|
|
/*
|
|
* Accessing maxBuf is racy with cifs_reconnect - need to store value
|
|
- * and check it for zero before using.
|
|
+ * and check it before using.
|
|
*/
|
|
max_buf = tcon->ses->server->maxBuf;
|
|
- if (!max_buf)
|
|
+ if (max_buf < (sizeof(struct smb_hdr) + sizeof(LOCKING_ANDX_RANGE)))
|
|
return -EINVAL;
|
|
|
|
max_num = (max_buf - sizeof(struct smb_hdr)) /
|
|
diff --git a/fs/cifs/smb2file.c b/fs/cifs/smb2file.c
|
|
index b2aff0c6f22c..b7885dc0d9bb 100644
|
|
--- a/fs/cifs/smb2file.c
|
|
+++ b/fs/cifs/smb2file.c
|
|
@@ -123,10 +123,10 @@ smb2_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock,
|
|
|
|
/*
|
|
* Accessing maxBuf is racy with cifs_reconnect - need to store value
|
|
- * and check it for zero before using.
|
|
+ * and check it before using.
|
|
*/
|
|
max_buf = tcon->ses->server->maxBuf;
|
|
- if (!max_buf)
|
|
+ if (max_buf < sizeof(struct smb2_lock_element))
|
|
return -EINVAL;
|
|
|
|
max_num = max_buf / sizeof(struct smb2_lock_element);
|
|
diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c
|
|
index 54af10204e83..1cf0a336ec06 100644
|
|
--- a/fs/cifs/transport.c
|
|
+++ b/fs/cifs/transport.c
|
|
@@ -360,7 +360,7 @@ uncork:
|
|
if (rc < 0 && rc != -EINTR)
|
|
cifs_dbg(VFS, "Error %d sending data on socket to server\n",
|
|
rc);
|
|
- else
|
|
+ else if (rc > 0)
|
|
rc = 0;
|
|
|
|
return rc;
|
|
diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
|
|
index 46d4fac48cf4..0dcd33f62637 100644
|
|
--- a/fs/ext4/inline.c
|
|
+++ b/fs/ext4/inline.c
|
|
@@ -1861,12 +1861,12 @@ int ext4_inline_data_fiemap(struct inode *inode,
|
|
physical += (char *)ext4_raw_inode(&iloc) - iloc.bh->b_data;
|
|
physical += offsetof(struct ext4_inode, i_block);
|
|
|
|
- if (physical)
|
|
- error = fiemap_fill_next_extent(fieinfo, start, physical,
|
|
- inline_len, flags);
|
|
brelse(iloc.bh);
|
|
out:
|
|
up_read(&EXT4_I(inode)->xattr_sem);
|
|
+ if (physical)
|
|
+ error = fiemap_fill_next_extent(fieinfo, start, physical,
|
|
+ inline_len, flags);
|
|
return (error < 0 ? error : 0);
|
|
}
|
|
|
|
diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h
|
|
index cc0fc712bb82..a8ac3f25b4ec 100644
|
|
--- a/include/linux/sunrpc/svc.h
|
|
+++ b/include/linux/sunrpc/svc.h
|
|
@@ -290,9 +290,12 @@ struct svc_rqst {
|
|
struct svc_cacherep * rq_cacherep; /* cache info */
|
|
struct task_struct *rq_task; /* service thread */
|
|
spinlock_t rq_lock; /* per-request lock */
|
|
+ struct net *rq_bc_net; /* pointer to backchannel's
|
|
+ * net namespace
|
|
+ */
|
|
};
|
|
|
|
-#define SVC_NET(svc_rqst) (svc_rqst->rq_xprt->xpt_net)
|
|
+#define SVC_NET(rqst) (rqst->rq_xprt ? rqst->rq_xprt->xpt_net : rqst->rq_bc_net)
|
|
|
|
/*
|
|
* Rigorous type checking on sockaddr type conversions
|
|
diff --git a/mm/slab.c b/mm/slab.c
|
|
index fa49c01225a7..92df044f5e00 100644
|
|
--- a/mm/slab.c
|
|
+++ b/mm/slab.c
|
|
@@ -875,8 +875,10 @@ static struct alien_cache *__alloc_alien_cache(int node, int entries,
|
|
struct alien_cache *alc = NULL;
|
|
|
|
alc = kmalloc_node(memsize, gfp, node);
|
|
- init_arraycache(&alc->ac, entries, batch);
|
|
- spin_lock_init(&alc->lock);
|
|
+ if (alc) {
|
|
+ init_arraycache(&alc->ac, entries, batch);
|
|
+ spin_lock_init(&alc->lock);
|
|
+ }
|
|
return alc;
|
|
}
|
|
|
|
diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c
|
|
index c5b0cb4f4056..41f6e964fe91 100644
|
|
--- a/net/sunrpc/svc.c
|
|
+++ b/net/sunrpc/svc.c
|
|
@@ -1062,6 +1062,8 @@ void svc_printk(struct svc_rqst *rqstp, const char *fmt, ...)
|
|
static __printf(2,3) void svc_printk(struct svc_rqst *rqstp, const char *fmt, ...) {}
|
|
#endif
|
|
|
|
+extern void svc_tcp_prep_reply_hdr(struct svc_rqst *);
|
|
+
|
|
/*
|
|
* Common routine for processing the RPC request.
|
|
*/
|
|
@@ -1091,7 +1093,8 @@ svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)
|
|
clear_bit(RQ_DROPME, &rqstp->rq_flags);
|
|
|
|
/* Setup reply header */
|
|
- rqstp->rq_xprt->xpt_ops->xpo_prep_reply_hdr(rqstp);
|
|
+ if (rqstp->rq_prot == IPPROTO_TCP)
|
|
+ svc_tcp_prep_reply_hdr(rqstp);
|
|
|
|
svc_putu32(resv, rqstp->rq_xid);
|
|
|
|
@@ -1138,7 +1141,8 @@ svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)
|
|
case SVC_DENIED:
|
|
goto err_bad_auth;
|
|
case SVC_CLOSE:
|
|
- if (test_bit(XPT_TEMP, &rqstp->rq_xprt->xpt_flags))
|
|
+ if (rqstp->rq_xprt &&
|
|
+ test_bit(XPT_TEMP, &rqstp->rq_xprt->xpt_flags))
|
|
svc_close_xprt(rqstp->rq_xprt);
|
|
case SVC_DROP:
|
|
goto dropit;
|
|
@@ -1360,10 +1364,10 @@ bc_svc_process(struct svc_serv *serv, struct rpc_rqst *req,
|
|
dprintk("svc: %s(%p)\n", __func__, req);
|
|
|
|
/* Build the svc_rqst used by the common processing routine */
|
|
- rqstp->rq_xprt = serv->sv_bc_xprt;
|
|
rqstp->rq_xid = req->rq_xid;
|
|
rqstp->rq_prot = req->rq_xprt->prot;
|
|
rqstp->rq_server = serv;
|
|
+ rqstp->rq_bc_net = req->rq_xprt->xprt_net;
|
|
|
|
rqstp->rq_addrlen = sizeof(req->rq_xprt->addr);
|
|
memcpy(&rqstp->rq_addr, &req->rq_xprt->addr, rqstp->rq_addrlen);
|
|
diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c
|
|
index 71f15da72f02..2b8e80c721db 100644
|
|
--- a/net/sunrpc/svc_xprt.c
|
|
+++ b/net/sunrpc/svc_xprt.c
|
|
@@ -454,10 +454,11 @@ out:
|
|
*/
|
|
void svc_reserve(struct svc_rqst *rqstp, int space)
|
|
{
|
|
+ struct svc_xprt *xprt = rqstp->rq_xprt;
|
|
+
|
|
space += rqstp->rq_res.head[0].iov_len;
|
|
|
|
- if (space < rqstp->rq_reserved) {
|
|
- struct svc_xprt *xprt = rqstp->rq_xprt;
|
|
+ if (xprt && space < rqstp->rq_reserved) {
|
|
atomic_sub((rqstp->rq_reserved - space), &xprt->xpt_reserved);
|
|
rqstp->rq_reserved = space;
|
|
|
|
diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c
|
|
index 9701fcca002c..0a9fe033132c 100644
|
|
--- a/net/sunrpc/svcsock.c
|
|
+++ b/net/sunrpc/svcsock.c
|
|
@@ -1240,7 +1240,7 @@ static int svc_tcp_sendto(struct svc_rqst *rqstp)
|
|
/*
|
|
* Setup response header. TCP has a 4B record length field.
|
|
*/
|
|
-static void svc_tcp_prep_reply_hdr(struct svc_rqst *rqstp)
|
|
+void svc_tcp_prep_reply_hdr(struct svc_rqst *rqstp)
|
|
{
|
|
struct kvec *resv = &rqstp->rq_res.head[0];
|
|
|
|
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
|
|
index 0467e5ba82e0..5d8ac2d798df 100644
|
|
--- a/sound/pci/hda/patch_realtek.c
|
|
+++ b/sound/pci/hda/patch_realtek.c
|
|
@@ -4792,6 +4792,13 @@ static void alc280_fixup_hp_9480m(struct hda_codec *codec,
|
|
}
|
|
}
|
|
|
|
+static void alc_fixup_disable_mic_vref(struct hda_codec *codec,
|
|
+ const struct hda_fixup *fix, int action)
|
|
+{
|
|
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
|
|
+ snd_hda_codec_set_pin_target(codec, 0x19, PIN_VREFHIZ);
|
|
+}
|
|
+
|
|
/* for hda_fixup_thinkpad_acpi() */
|
|
#include "thinkpad_helper.c"
|
|
|
|
@@ -4891,6 +4898,7 @@ enum {
|
|
ALC293_FIXUP_LENOVO_SPK_NOISE,
|
|
ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY,
|
|
ALC255_FIXUP_DELL_SPK_NOISE,
|
|
+ ALC225_FIXUP_DISABLE_MIC_VREF,
|
|
ALC225_FIXUP_DELL1_MIC_NO_PRESENCE,
|
|
ALC295_FIXUP_DISABLE_DAC3,
|
|
ALC280_FIXUP_HP_HEADSET_MIC,
|
|
@@ -5546,6 +5554,12 @@ static const struct hda_fixup alc269_fixups[] = {
|
|
.chained = true,
|
|
.chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE
|
|
},
|
|
+ [ALC225_FIXUP_DISABLE_MIC_VREF] = {
|
|
+ .type = HDA_FIXUP_FUNC,
|
|
+ .v.func = alc_fixup_disable_mic_vref,
|
|
+ .chained = true,
|
|
+ .chain_id = ALC269_FIXUP_DELL1_MIC_NO_PRESENCE
|
|
+ },
|
|
[ALC225_FIXUP_DELL1_MIC_NO_PRESENCE] = {
|
|
.type = HDA_FIXUP_VERBS,
|
|
.v.verbs = (const struct hda_verb[]) {
|
|
@@ -5555,7 +5569,7 @@ static const struct hda_fixup alc269_fixups[] = {
|
|
{}
|
|
},
|
|
.chained = true,
|
|
- .chain_id = ALC269_FIXUP_DELL1_MIC_NO_PRESENCE
|
|
+ .chain_id = ALC225_FIXUP_DISABLE_MIC_VREF
|
|
},
|
|
[ALC280_FIXUP_HP_HEADSET_MIC] = {
|
|
.type = HDA_FIXUP_FUNC,
|