Merge remote-tracking branch 'common/android-4.9' into android-hikey-linaro-4.9-aosp
Change-Id: I602da1aa6334cd3c556a4ba4a670492ee4dd75d3
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
VERSION = 4
|
||||
PATCHLEVEL = 9
|
||||
SUBLEVEL = 48
|
||||
SUBLEVEL = 51
|
||||
EXTRAVERSION =
|
||||
NAME = Roaring Lionus
|
||||
|
||||
|
||||
+4
-1
@@ -314,8 +314,11 @@ retry:
|
||||
* signal first. We do not need to release the mmap_sem because
|
||||
* it would already be released in __lock_page_or_retry in
|
||||
* mm/filemap.c. */
|
||||
if ((fault & VM_FAULT_RETRY) && fatal_signal_pending(current))
|
||||
if ((fault & VM_FAULT_RETRY) && fatal_signal_pending(current)) {
|
||||
if (!user_mode(regs))
|
||||
goto no_context;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Major/minor page fault accounting is only done on the
|
||||
|
||||
@@ -170,6 +170,7 @@
|
||||
interrupt-controller;
|
||||
reg = <0x1d00000 0x10000>, /* GICD */
|
||||
<0x1d40000 0x40000>; /* GICR */
|
||||
interrupts = <GIC_PPI 9 IRQ_TYPE_LEVEL_HIGH>;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -480,7 +480,7 @@ static inline int mm_alloc_pgste(struct mm_struct *mm)
|
||||
* In the case that a guest uses storage keys
|
||||
* faults should no longer be backed by zero pages
|
||||
*/
|
||||
#define mm_forbids_zeropage mm_use_skey
|
||||
#define mm_forbids_zeropage mm_has_pgste
|
||||
static inline int mm_use_skey(struct mm_struct *mm)
|
||||
{
|
||||
#ifdef CONFIG_PGSTE
|
||||
|
||||
+32
-7
@@ -2124,6 +2124,37 @@ static inline void thp_split_mm(struct mm_struct *mm)
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove all empty zero pages from the mapping for lazy refaulting
|
||||
* - This must be called after mm->context.has_pgste is set, to avoid
|
||||
* future creation of zero pages
|
||||
* - This must be called after THP was enabled
|
||||
*/
|
||||
static int __zap_zero_pages(pmd_t *pmd, unsigned long start,
|
||||
unsigned long end, struct mm_walk *walk)
|
||||
{
|
||||
unsigned long addr;
|
||||
|
||||
for (addr = start; addr != end; addr += PAGE_SIZE) {
|
||||
pte_t *ptep;
|
||||
spinlock_t *ptl;
|
||||
|
||||
ptep = pte_offset_map_lock(walk->mm, pmd, addr, &ptl);
|
||||
if (is_zero_pfn(pte_pfn(*ptep)))
|
||||
ptep_xchg_direct(walk->mm, addr, ptep, __pte(_PAGE_INVALID));
|
||||
pte_unmap_unlock(ptep, ptl);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void zap_zero_pages(struct mm_struct *mm)
|
||||
{
|
||||
struct mm_walk walk = { .pmd_entry = __zap_zero_pages };
|
||||
|
||||
walk.mm = mm;
|
||||
walk_page_range(0, TASK_SIZE, &walk);
|
||||
}
|
||||
|
||||
/*
|
||||
* switch on pgstes for its userspace process (for kvm)
|
||||
*/
|
||||
@@ -2141,6 +2172,7 @@ int s390_enable_sie(void)
|
||||
mm->context.has_pgste = 1;
|
||||
/* split thp mappings and disable thp for future mappings */
|
||||
thp_split_mm(mm);
|
||||
zap_zero_pages(mm);
|
||||
up_write(&mm->mmap_sem);
|
||||
return 0;
|
||||
}
|
||||
@@ -2153,13 +2185,6 @@ EXPORT_SYMBOL_GPL(s390_enable_sie);
|
||||
static int __s390_enable_skey(pte_t *pte, unsigned long addr,
|
||||
unsigned long next, struct mm_walk *walk)
|
||||
{
|
||||
/*
|
||||
* Remove all zero page mappings,
|
||||
* after establishing a policy to forbid zero page mappings
|
||||
* following faults for that page will get fresh anonymous pages
|
||||
*/
|
||||
if (is_zero_pfn(pte_pfn(*pte)))
|
||||
ptep_xchg_direct(walk->mm, addr, pte, __pte(_PAGE_INVALID));
|
||||
/* Clear storage key */
|
||||
ptep_zap_key(walk->mm, addr, pte);
|
||||
return 0;
|
||||
|
||||
@@ -204,6 +204,7 @@ void set_personality_ia32(bool);
|
||||
|
||||
#define ELF_CORE_COPY_REGS(pr_reg, regs) \
|
||||
do { \
|
||||
unsigned long base; \
|
||||
unsigned v; \
|
||||
(pr_reg)[0] = (regs)->r15; \
|
||||
(pr_reg)[1] = (regs)->r14; \
|
||||
@@ -226,8 +227,8 @@ do { \
|
||||
(pr_reg)[18] = (regs)->flags; \
|
||||
(pr_reg)[19] = (regs)->sp; \
|
||||
(pr_reg)[20] = (regs)->ss; \
|
||||
(pr_reg)[21] = current->thread.fsbase; \
|
||||
(pr_reg)[22] = current->thread.gsbase; \
|
||||
rdmsrl(MSR_FS_BASE, base); (pr_reg)[21] = base; \
|
||||
rdmsrl(MSR_KERNEL_GS_BASE, base); (pr_reg)[22] = base; \
|
||||
asm("movl %%ds,%0" : "=r" (v)); (pr_reg)[23] = v; \
|
||||
asm("movl %%es,%0" : "=r" (v)); (pr_reg)[24] = v; \
|
||||
asm("movl %%fs,%0" : "=r" (v)); (pr_reg)[25] = v; \
|
||||
|
||||
+131
-105
@@ -136,6 +136,123 @@ void release_thread(struct task_struct *dead_task)
|
||||
}
|
||||
}
|
||||
|
||||
enum which_selector {
|
||||
FS,
|
||||
GS
|
||||
};
|
||||
|
||||
/*
|
||||
* Saves the FS or GS base for an outgoing thread if FSGSBASE extensions are
|
||||
* not available. The goal is to be reasonably fast on non-FSGSBASE systems.
|
||||
* It's forcibly inlined because it'll generate better code and this function
|
||||
* is hot.
|
||||
*/
|
||||
static __always_inline void save_base_legacy(struct task_struct *prev_p,
|
||||
unsigned short selector,
|
||||
enum which_selector which)
|
||||
{
|
||||
if (likely(selector == 0)) {
|
||||
/*
|
||||
* On Intel (without X86_BUG_NULL_SEG), the segment base could
|
||||
* be the pre-existing saved base or it could be zero. On AMD
|
||||
* (with X86_BUG_NULL_SEG), the segment base could be almost
|
||||
* anything.
|
||||
*
|
||||
* This branch is very hot (it's hit twice on almost every
|
||||
* context switch between 64-bit programs), and avoiding
|
||||
* the RDMSR helps a lot, so we just assume that whatever
|
||||
* value is already saved is correct. This matches historical
|
||||
* Linux behavior, so it won't break existing applications.
|
||||
*
|
||||
* To avoid leaking state, on non-X86_BUG_NULL_SEG CPUs, if we
|
||||
* report that the base is zero, it needs to actually be zero:
|
||||
* see the corresponding logic in load_seg_legacy.
|
||||
*/
|
||||
} else {
|
||||
/*
|
||||
* If the selector is 1, 2, or 3, then the base is zero on
|
||||
* !X86_BUG_NULL_SEG CPUs and could be anything on
|
||||
* X86_BUG_NULL_SEG CPUs. In the latter case, Linux
|
||||
* has never attempted to preserve the base across context
|
||||
* switches.
|
||||
*
|
||||
* If selector > 3, then it refers to a real segment, and
|
||||
* saving the base isn't necessary.
|
||||
*/
|
||||
if (which == FS)
|
||||
prev_p->thread.fsbase = 0;
|
||||
else
|
||||
prev_p->thread.gsbase = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static __always_inline void save_fsgs(struct task_struct *task)
|
||||
{
|
||||
savesegment(fs, task->thread.fsindex);
|
||||
savesegment(gs, task->thread.gsindex);
|
||||
save_base_legacy(task, task->thread.fsindex, FS);
|
||||
save_base_legacy(task, task->thread.gsindex, GS);
|
||||
}
|
||||
|
||||
static __always_inline void loadseg(enum which_selector which,
|
||||
unsigned short sel)
|
||||
{
|
||||
if (which == FS)
|
||||
loadsegment(fs, sel);
|
||||
else
|
||||
load_gs_index(sel);
|
||||
}
|
||||
|
||||
static __always_inline void load_seg_legacy(unsigned short prev_index,
|
||||
unsigned long prev_base,
|
||||
unsigned short next_index,
|
||||
unsigned long next_base,
|
||||
enum which_selector which)
|
||||
{
|
||||
if (likely(next_index <= 3)) {
|
||||
/*
|
||||
* The next task is using 64-bit TLS, is not using this
|
||||
* segment at all, or is having fun with arcane CPU features.
|
||||
*/
|
||||
if (next_base == 0) {
|
||||
/*
|
||||
* Nasty case: on AMD CPUs, we need to forcibly zero
|
||||
* the base.
|
||||
*/
|
||||
if (static_cpu_has_bug(X86_BUG_NULL_SEG)) {
|
||||
loadseg(which, __USER_DS);
|
||||
loadseg(which, next_index);
|
||||
} else {
|
||||
/*
|
||||
* We could try to exhaustively detect cases
|
||||
* under which we can skip the segment load,
|
||||
* but there's really only one case that matters
|
||||
* for performance: if both the previous and
|
||||
* next states are fully zeroed, we can skip
|
||||
* the load.
|
||||
*
|
||||
* (This assumes that prev_base == 0 has no
|
||||
* false positives. This is the case on
|
||||
* Intel-style CPUs.)
|
||||
*/
|
||||
if (likely(prev_index | next_index | prev_base))
|
||||
loadseg(which, next_index);
|
||||
}
|
||||
} else {
|
||||
if (prev_index != next_index)
|
||||
loadseg(which, next_index);
|
||||
wrmsrl(which == FS ? MSR_FS_BASE : MSR_KERNEL_GS_BASE,
|
||||
next_base);
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* The next task is using a real segment. Loading the selector
|
||||
* is sufficient.
|
||||
*/
|
||||
loadseg(which, next_index);
|
||||
}
|
||||
}
|
||||
|
||||
int copy_thread_tls(unsigned long clone_flags, unsigned long sp,
|
||||
unsigned long arg, struct task_struct *p, unsigned long tls)
|
||||
{
|
||||
@@ -216,10 +333,19 @@ start_thread_common(struct pt_regs *regs, unsigned long new_ip,
|
||||
unsigned long new_sp,
|
||||
unsigned int _cs, unsigned int _ss, unsigned int _ds)
|
||||
{
|
||||
WARN_ON_ONCE(regs != current_pt_regs());
|
||||
|
||||
if (static_cpu_has(X86_BUG_NULL_SEG)) {
|
||||
/* Loading zero below won't clear the base. */
|
||||
loadsegment(fs, __USER_DS);
|
||||
load_gs_index(__USER_DS);
|
||||
}
|
||||
|
||||
loadsegment(fs, 0);
|
||||
loadsegment(es, _ds);
|
||||
loadsegment(ds, _ds);
|
||||
load_gs_index(0);
|
||||
|
||||
regs->ip = new_ip;
|
||||
regs->sp = new_sp;
|
||||
regs->cs = _cs;
|
||||
@@ -264,7 +390,6 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p)
|
||||
struct fpu *next_fpu = &next->fpu;
|
||||
int cpu = smp_processor_id();
|
||||
struct tss_struct *tss = &per_cpu(cpu_tss, cpu);
|
||||
unsigned prev_fsindex, prev_gsindex;
|
||||
fpu_switch_t fpu_switch;
|
||||
|
||||
fpu_switch = switch_fpu_prepare(prev_fpu, next_fpu, cpu);
|
||||
@@ -274,8 +399,7 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p)
|
||||
*
|
||||
* (e.g. xen_load_tls())
|
||||
*/
|
||||
savesegment(fs, prev_fsindex);
|
||||
savesegment(gs, prev_gsindex);
|
||||
save_fsgs(prev_p);
|
||||
|
||||
/*
|
||||
* Load TLS before restoring any segments so that segment loads
|
||||
@@ -314,108 +438,10 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p)
|
||||
if (unlikely(next->ds | prev->ds))
|
||||
loadsegment(ds, next->ds);
|
||||
|
||||
/*
|
||||
* Switch FS and GS.
|
||||
*
|
||||
* These are even more complicated than DS and ES: they have
|
||||
* 64-bit bases are that controlled by arch_prctl. The bases
|
||||
* don't necessarily match the selectors, as user code can do
|
||||
* any number of things to cause them to be inconsistent.
|
||||
*
|
||||
* We don't promise to preserve the bases if the selectors are
|
||||
* nonzero. We also don't promise to preserve the base if the
|
||||
* selector is zero and the base doesn't match whatever was
|
||||
* most recently passed to ARCH_SET_FS/GS. (If/when the
|
||||
* FSGSBASE instructions are enabled, we'll need to offer
|
||||
* stronger guarantees.)
|
||||
*
|
||||
* As an invariant,
|
||||
* (fsbase != 0 && fsindex != 0) || (gsbase != 0 && gsindex != 0) is
|
||||
* impossible.
|
||||
*/
|
||||
if (next->fsindex) {
|
||||
/* Loading a nonzero value into FS sets the index and base. */
|
||||
loadsegment(fs, next->fsindex);
|
||||
} else {
|
||||
if (next->fsbase) {
|
||||
/* Next index is zero but next base is nonzero. */
|
||||
if (prev_fsindex)
|
||||
loadsegment(fs, 0);
|
||||
wrmsrl(MSR_FS_BASE, next->fsbase);
|
||||
} else {
|
||||
/* Next base and index are both zero. */
|
||||
if (static_cpu_has_bug(X86_BUG_NULL_SEG)) {
|
||||
/*
|
||||
* We don't know the previous base and can't
|
||||
* find out without RDMSR. Forcibly clear it.
|
||||
*/
|
||||
loadsegment(fs, __USER_DS);
|
||||
loadsegment(fs, 0);
|
||||
} else {
|
||||
/*
|
||||
* If the previous index is zero and ARCH_SET_FS
|
||||
* didn't change the base, then the base is
|
||||
* also zero and we don't need to do anything.
|
||||
*/
|
||||
if (prev->fsbase || prev_fsindex)
|
||||
loadsegment(fs, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Save the old state and preserve the invariant.
|
||||
* NB: if prev_fsindex == 0, then we can't reliably learn the base
|
||||
* without RDMSR because Intel user code can zero it without telling
|
||||
* us and AMD user code can program any 32-bit value without telling
|
||||
* us.
|
||||
*/
|
||||
if (prev_fsindex)
|
||||
prev->fsbase = 0;
|
||||
prev->fsindex = prev_fsindex;
|
||||
|
||||
if (next->gsindex) {
|
||||
/* Loading a nonzero value into GS sets the index and base. */
|
||||
load_gs_index(next->gsindex);
|
||||
} else {
|
||||
if (next->gsbase) {
|
||||
/* Next index is zero but next base is nonzero. */
|
||||
if (prev_gsindex)
|
||||
load_gs_index(0);
|
||||
wrmsrl(MSR_KERNEL_GS_BASE, next->gsbase);
|
||||
} else {
|
||||
/* Next base and index are both zero. */
|
||||
if (static_cpu_has_bug(X86_BUG_NULL_SEG)) {
|
||||
/*
|
||||
* We don't know the previous base and can't
|
||||
* find out without RDMSR. Forcibly clear it.
|
||||
*
|
||||
* This contains a pointless SWAPGS pair.
|
||||
* Fixing it would involve an explicit check
|
||||
* for Xen or a new pvop.
|
||||
*/
|
||||
load_gs_index(__USER_DS);
|
||||
load_gs_index(0);
|
||||
} else {
|
||||
/*
|
||||
* If the previous index is zero and ARCH_SET_GS
|
||||
* didn't change the base, then the base is
|
||||
* also zero and we don't need to do anything.
|
||||
*/
|
||||
if (prev->gsbase || prev_gsindex)
|
||||
load_gs_index(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Save the old state and preserve the invariant.
|
||||
* NB: if prev_gsindex == 0, then we can't reliably learn the base
|
||||
* without RDMSR because Intel user code can zero it without telling
|
||||
* us and AMD user code can program any 32-bit value without telling
|
||||
* us.
|
||||
*/
|
||||
if (prev_gsindex)
|
||||
prev->gsbase = 0;
|
||||
prev->gsindex = prev_gsindex;
|
||||
load_seg_legacy(prev->fsindex, prev->fsbase,
|
||||
next->fsindex, next->fsbase, FS);
|
||||
load_seg_legacy(prev->gsindex, prev->gsbase,
|
||||
next->gsindex, next->gsbase, GS);
|
||||
|
||||
switch_fpu_finish(next_fpu, fpu_switch);
|
||||
|
||||
|
||||
@@ -616,6 +616,7 @@ static const struct pci_device_id amd[] = {
|
||||
{ PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP73_IDE), 8 },
|
||||
{ PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP77_IDE), 8 },
|
||||
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_CS5536_IDE), 9 },
|
||||
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_CS5536_DEV_IDE), 9 },
|
||||
|
||||
{ },
|
||||
};
|
||||
|
||||
@@ -289,6 +289,7 @@ static int cs5536_init_one(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
|
||||
static const struct pci_device_id cs5536[] = {
|
||||
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_CS5536_IDE), },
|
||||
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_CS5536_DEV_IDE), },
|
||||
{ },
|
||||
};
|
||||
|
||||
|
||||
+1
-1
@@ -736,7 +736,7 @@ int bus_add_driver(struct device_driver *drv)
|
||||
|
||||
out_unregister:
|
||||
kobject_put(&priv->kobj);
|
||||
kfree(drv->p);
|
||||
/* drv->p is freed in driver_release() */
|
||||
drv->p = NULL;
|
||||
out_put_bus:
|
||||
bus_put(bus);
|
||||
|
||||
@@ -342,6 +342,7 @@ static const struct usb_device_id blacklist_table[] = {
|
||||
{ USB_DEVICE(0x13d3, 0x3410), .driver_info = BTUSB_REALTEK },
|
||||
{ USB_DEVICE(0x13d3, 0x3416), .driver_info = BTUSB_REALTEK },
|
||||
{ USB_DEVICE(0x13d3, 0x3459), .driver_info = BTUSB_REALTEK },
|
||||
{ USB_DEVICE(0x13d3, 0x3494), .driver_info = BTUSB_REALTEK },
|
||||
|
||||
/* Additional Realtek 8821AE Bluetooth devices */
|
||||
{ USB_DEVICE(0x0b05, 0x17dc), .driver_info = BTUSB_REALTEK },
|
||||
|
||||
@@ -424,10 +424,10 @@ static void adv7511_hpd_work(struct work_struct *work)
|
||||
else
|
||||
status = connector_status_disconnected;
|
||||
|
||||
if (adv7511->connector.status != status)
|
||||
if (adv7511->connector.status != status) {
|
||||
adv7511->connector.status = status;
|
||||
drm_kms_helper_hotplug_event(adv7511->connector.dev);
|
||||
|
||||
adv7511->connector.status = status;
|
||||
}
|
||||
}
|
||||
|
||||
static int adv7511_irq_process(struct adv7511 *adv7511, bool process_hpd)
|
||||
|
||||
@@ -192,6 +192,10 @@ nvkm_pci_new_(const struct nvkm_pci_func *func, struct nvkm_device *device,
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __BIG_ENDIAN
|
||||
pci->msi = false;
|
||||
#endif
|
||||
|
||||
pci->msi = nvkm_boolopt(device->cfgopt, "NvMSI", pci->msi);
|
||||
if (pci->msi && func->msi_rearm) {
|
||||
pci->msi = pci_enable_msi(pci->pdev) == 0;
|
||||
|
||||
@@ -85,6 +85,16 @@ static const struct pci_device_id intel_th_pci_id_table[] = {
|
||||
PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xa2a6),
|
||||
.driver_data = (kernel_ulong_t)0,
|
||||
},
|
||||
{
|
||||
/* Cannon Lake H */
|
||||
PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xa326),
|
||||
.driver_data = (kernel_ulong_t)0,
|
||||
},
|
||||
{
|
||||
/* Cannon Lake LP */
|
||||
PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x9da6),
|
||||
.driver_data = (kernel_ulong_t)0,
|
||||
},
|
||||
{ 0 },
|
||||
};
|
||||
|
||||
|
||||
@@ -80,18 +80,12 @@ static const unsigned int ads1115_data_rate[] = {
|
||||
8, 16, 32, 64, 128, 250, 475, 860
|
||||
};
|
||||
|
||||
static const struct {
|
||||
int scale;
|
||||
int uscale;
|
||||
} ads1015_scale[] = {
|
||||
{3, 0},
|
||||
{2, 0},
|
||||
{1, 0},
|
||||
{0, 500000},
|
||||
{0, 250000},
|
||||
{0, 125000},
|
||||
{0, 125000},
|
||||
{0, 125000},
|
||||
/*
|
||||
* Translation from PGA bits to full-scale positive and negative input voltage
|
||||
* range in mV
|
||||
*/
|
||||
static int ads1015_fullscale_range[] = {
|
||||
6144, 4096, 2048, 1024, 512, 256, 256, 256
|
||||
};
|
||||
|
||||
#define ADS1015_V_CHAN(_chan, _addr) { \
|
||||
@@ -182,6 +176,12 @@ struct ads1015_data {
|
||||
struct ads1015_channel_data channel_data[ADS1015_CHANNELS];
|
||||
|
||||
unsigned int *data_rate;
|
||||
/*
|
||||
* Set to true when the ADC is switched to the continuous-conversion
|
||||
* mode and exits from a power-down state. This flag is used to avoid
|
||||
* getting the stale result from the conversion register.
|
||||
*/
|
||||
bool conv_invalid;
|
||||
};
|
||||
|
||||
static bool ads1015_is_writeable_reg(struct device *dev, unsigned int reg)
|
||||
@@ -234,33 +234,43 @@ static int ads1015_set_power_state(struct ads1015_data *data, bool on)
|
||||
ret = pm_runtime_put_autosuspend(dev);
|
||||
}
|
||||
|
||||
return ret;
|
||||
return ret < 0 ? ret : 0;
|
||||
}
|
||||
|
||||
static
|
||||
int ads1015_get_adc_result(struct ads1015_data *data, int chan, int *val)
|
||||
{
|
||||
int ret, pga, dr, conv_time;
|
||||
bool change;
|
||||
unsigned int old, mask, cfg;
|
||||
|
||||
if (chan < 0 || chan >= ADS1015_CHANNELS)
|
||||
return -EINVAL;
|
||||
|
||||
pga = data->channel_data[chan].pga;
|
||||
dr = data->channel_data[chan].data_rate;
|
||||
|
||||
ret = regmap_update_bits_check(data->regmap, ADS1015_CFG_REG,
|
||||
ADS1015_CFG_MUX_MASK |
|
||||
ADS1015_CFG_PGA_MASK,
|
||||
chan << ADS1015_CFG_MUX_SHIFT |
|
||||
pga << ADS1015_CFG_PGA_SHIFT,
|
||||
&change);
|
||||
if (ret < 0)
|
||||
ret = regmap_read(data->regmap, ADS1015_CFG_REG, &old);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (change) {
|
||||
conv_time = DIV_ROUND_UP(USEC_PER_SEC, data->data_rate[dr]);
|
||||
pga = data->channel_data[chan].pga;
|
||||
dr = data->channel_data[chan].data_rate;
|
||||
mask = ADS1015_CFG_MUX_MASK | ADS1015_CFG_PGA_MASK |
|
||||
ADS1015_CFG_DR_MASK;
|
||||
cfg = chan << ADS1015_CFG_MUX_SHIFT | pga << ADS1015_CFG_PGA_SHIFT |
|
||||
dr << ADS1015_CFG_DR_SHIFT;
|
||||
|
||||
cfg = (old & ~mask) | (cfg & mask);
|
||||
|
||||
ret = regmap_write(data->regmap, ADS1015_CFG_REG, cfg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (old != cfg || data->conv_invalid) {
|
||||
int dr_old = (old & ADS1015_CFG_DR_MASK) >>
|
||||
ADS1015_CFG_DR_SHIFT;
|
||||
|
||||
conv_time = DIV_ROUND_UP(USEC_PER_SEC, data->data_rate[dr_old]);
|
||||
conv_time += DIV_ROUND_UP(USEC_PER_SEC, data->data_rate[dr]);
|
||||
usleep_range(conv_time, conv_time + 1);
|
||||
data->conv_invalid = false;
|
||||
}
|
||||
|
||||
return regmap_read(data->regmap, ADS1015_CONV_REG, val);
|
||||
@@ -297,17 +307,20 @@ err:
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int ads1015_set_scale(struct ads1015_data *data, int chan,
|
||||
static int ads1015_set_scale(struct ads1015_data *data,
|
||||
struct iio_chan_spec const *chan,
|
||||
int scale, int uscale)
|
||||
{
|
||||
int i, ret, rindex = -1;
|
||||
int fullscale = div_s64((scale * 1000000LL + uscale) <<
|
||||
(chan->scan_type.realbits - 1), 1000000);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ads1015_scale); i++)
|
||||
if (ads1015_scale[i].scale == scale &&
|
||||
ads1015_scale[i].uscale == uscale) {
|
||||
for (i = 0; i < ARRAY_SIZE(ads1015_fullscale_range); i++) {
|
||||
if (ads1015_fullscale_range[i] == fullscale) {
|
||||
rindex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (rindex < 0)
|
||||
return -EINVAL;
|
||||
|
||||
@@ -317,32 +330,23 @@ static int ads1015_set_scale(struct ads1015_data *data, int chan,
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
data->channel_data[chan].pga = rindex;
|
||||
data->channel_data[chan->address].pga = rindex;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ads1015_set_data_rate(struct ads1015_data *data, int chan, int rate)
|
||||
{
|
||||
int i, ret, rindex = -1;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ads1015_data_rate); i++)
|
||||
for (i = 0; i < ARRAY_SIZE(ads1015_data_rate); i++) {
|
||||
if (data->data_rate[i] == rate) {
|
||||
rindex = i;
|
||||
break;
|
||||
data->channel_data[chan].data_rate = i;
|
||||
return 0;
|
||||
}
|
||||
if (rindex < 0)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = regmap_update_bits(data->regmap, ADS1015_CFG_REG,
|
||||
ADS1015_CFG_DR_MASK,
|
||||
rindex << ADS1015_CFG_DR_SHIFT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
data->channel_data[chan].data_rate = rindex;
|
||||
|
||||
return 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ads1015_read_raw(struct iio_dev *indio_dev,
|
||||
@@ -384,9 +388,9 @@ static int ads1015_read_raw(struct iio_dev *indio_dev,
|
||||
}
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
idx = data->channel_data[chan->address].pga;
|
||||
*val = ads1015_scale[idx].scale;
|
||||
*val2 = ads1015_scale[idx].uscale;
|
||||
ret = IIO_VAL_INT_PLUS_MICRO;
|
||||
*val = ads1015_fullscale_range[idx];
|
||||
*val2 = chan->scan_type.realbits - 1;
|
||||
ret = IIO_VAL_FRACTIONAL_LOG2;
|
||||
break;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
idx = data->channel_data[chan->address].data_rate;
|
||||
@@ -413,7 +417,7 @@ static int ads1015_write_raw(struct iio_dev *indio_dev,
|
||||
mutex_lock(&data->lock);
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
ret = ads1015_set_scale(data, chan->address, val, val2);
|
||||
ret = ads1015_set_scale(data, chan, val, val2);
|
||||
break;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
ret = ads1015_set_data_rate(data, chan->address, val);
|
||||
@@ -445,7 +449,10 @@ static const struct iio_buffer_setup_ops ads1015_buffer_setup_ops = {
|
||||
.validate_scan_mask = &iio_validate_scan_mask_onehot,
|
||||
};
|
||||
|
||||
static IIO_CONST_ATTR(scale_available, "3 2 1 0.5 0.25 0.125");
|
||||
static IIO_CONST_ATTR_NAMED(ads1015_scale_available, scale_available,
|
||||
"3 2 1 0.5 0.25 0.125");
|
||||
static IIO_CONST_ATTR_NAMED(ads1115_scale_available, scale_available,
|
||||
"0.1875 0.125 0.0625 0.03125 0.015625 0.007813");
|
||||
|
||||
static IIO_CONST_ATTR_NAMED(ads1015_sampling_frequency_available,
|
||||
sampling_frequency_available, "128 250 490 920 1600 2400 3300");
|
||||
@@ -453,7 +460,7 @@ static IIO_CONST_ATTR_NAMED(ads1115_sampling_frequency_available,
|
||||
sampling_frequency_available, "8 16 32 64 128 250 475 860");
|
||||
|
||||
static struct attribute *ads1015_attributes[] = {
|
||||
&iio_const_attr_scale_available.dev_attr.attr,
|
||||
&iio_const_attr_ads1015_scale_available.dev_attr.attr,
|
||||
&iio_const_attr_ads1015_sampling_frequency_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
@@ -463,7 +470,7 @@ static const struct attribute_group ads1015_attribute_group = {
|
||||
};
|
||||
|
||||
static struct attribute *ads1115_attributes[] = {
|
||||
&iio_const_attr_scale_available.dev_attr.attr,
|
||||
&iio_const_attr_ads1115_scale_available.dev_attr.attr,
|
||||
&iio_const_attr_ads1115_sampling_frequency_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
@@ -624,6 +631,15 @@ static int ads1015_probe(struct i2c_client *client,
|
||||
dev_err(&client->dev, "iio triggered buffer setup failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_update_bits(data->regmap, ADS1015_CFG_REG,
|
||||
ADS1015_CFG_MOD_MASK,
|
||||
ADS1015_CONTINUOUS << ADS1015_CFG_MOD_SHIFT);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
data->conv_invalid = true;
|
||||
|
||||
ret = pm_runtime_set_active(&client->dev);
|
||||
if (ret)
|
||||
goto err_buffer_cleanup;
|
||||
@@ -679,10 +695,15 @@ static int ads1015_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct ads1015_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
return regmap_update_bits(data->regmap, ADS1015_CFG_REG,
|
||||
ret = regmap_update_bits(data->regmap, ADS1015_CFG_REG,
|
||||
ADS1015_CFG_MOD_MASK,
|
||||
ADS1015_CONTINUOUS << ADS1015_CFG_MOD_SHIFT);
|
||||
if (!ret)
|
||||
data->conv_invalid = true;
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -381,8 +381,8 @@ int trackpoint_detect(struct psmouse *psmouse, bool set_properties)
|
||||
return 0;
|
||||
|
||||
if (trackpoint_read(&psmouse->ps2dev, TP_EXT_BTN, &button_info)) {
|
||||
psmouse_warn(psmouse, "failed to get extended button data\n");
|
||||
button_info = 0;
|
||||
psmouse_warn(psmouse, "failed to get extended button data, assuming 3 buttons\n");
|
||||
button_info = 0x33;
|
||||
}
|
||||
|
||||
psmouse->private = kzalloc(sizeof(struct trackpoint_data), GFP_KERNEL);
|
||||
|
||||
@@ -114,6 +114,12 @@ static struct resource sc24_fpga_resource = {
|
||||
.flags = IORESOURCE_MEM,
|
||||
};
|
||||
|
||||
static struct resource sc31_fpga_resource = {
|
||||
.start = 0xf000e000,
|
||||
.end = 0xf000e000 + CHAM_HEADER_SIZE,
|
||||
.flags = IORESOURCE_MEM,
|
||||
};
|
||||
|
||||
static struct platform_driver mcb_lpc_driver = {
|
||||
.driver = {
|
||||
.name = "mcb-lpc",
|
||||
@@ -132,6 +138,15 @@ static const struct dmi_system_id mcb_lpc_dmi_table[] = {
|
||||
.driver_data = (void *)&sc24_fpga_resource,
|
||||
.callback = mcb_lpc_create_platform_device,
|
||||
},
|
||||
{
|
||||
.ident = "SC31",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "MEN"),
|
||||
DMI_MATCH(DMI_PRODUCT_VERSION, "14SC31"),
|
||||
},
|
||||
.driver_data = (void *)&sc31_fpga_resource,
|
||||
.callback = mcb_lpc_create_platform_device,
|
||||
},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(dmi, mcb_lpc_dmi_table);
|
||||
|
||||
@@ -5844,6 +5844,8 @@ static void raid5_do_work(struct work_struct *work)
|
||||
|
||||
spin_unlock_irq(&conf->device_lock);
|
||||
|
||||
r5l_flush_stripe_to_raid(conf->log);
|
||||
|
||||
async_tx_issue_pending_all();
|
||||
blk_finish_plug(&plug);
|
||||
|
||||
|
||||
@@ -877,6 +877,8 @@ static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr)
|
||||
}
|
||||
}
|
||||
|
||||
#define MXC_V1_ECCBYTES 5
|
||||
|
||||
static int mxc_v1_ooblayout_ecc(struct mtd_info *mtd, int section,
|
||||
struct mtd_oob_region *oobregion)
|
||||
{
|
||||
@@ -886,7 +888,7 @@ static int mxc_v1_ooblayout_ecc(struct mtd_info *mtd, int section,
|
||||
return -ERANGE;
|
||||
|
||||
oobregion->offset = (section * 16) + 6;
|
||||
oobregion->length = nand_chip->ecc.bytes;
|
||||
oobregion->length = MXC_V1_ECCBYTES;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -908,8 +910,7 @@ static int mxc_v1_ooblayout_free(struct mtd_info *mtd, int section,
|
||||
oobregion->length = 4;
|
||||
}
|
||||
} else {
|
||||
oobregion->offset = ((section - 1) * 16) +
|
||||
nand_chip->ecc.bytes + 6;
|
||||
oobregion->offset = ((section - 1) * 16) + MXC_V1_ECCBYTES + 6;
|
||||
if (section < nand_chip->ecc.steps)
|
||||
oobregion->length = (section * 16) + 6 -
|
||||
oobregion->offset;
|
||||
|
||||
@@ -109,7 +109,11 @@
|
||||
#define READ_ADDR 0
|
||||
|
||||
/* NAND_DEV_CMD_VLD bits */
|
||||
#define READ_START_VLD 0
|
||||
#define READ_START_VLD BIT(0)
|
||||
#define READ_STOP_VLD BIT(1)
|
||||
#define WRITE_START_VLD BIT(2)
|
||||
#define ERASE_START_VLD BIT(3)
|
||||
#define SEQ_READ_START_VLD BIT(4)
|
||||
|
||||
/* NAND_EBI2_ECC_BUF_CFG bits */
|
||||
#define NUM_STEPS 0
|
||||
@@ -148,6 +152,10 @@
|
||||
#define FETCH_ID 0xb
|
||||
#define RESET_DEVICE 0xd
|
||||
|
||||
/* Default Value for NAND_DEV_CMD_VLD */
|
||||
#define NAND_DEV_CMD_VLD_VAL (READ_START_VLD | WRITE_START_VLD | \
|
||||
ERASE_START_VLD | SEQ_READ_START_VLD)
|
||||
|
||||
/*
|
||||
* the NAND controller performs reads/writes with ECC in 516 byte chunks.
|
||||
* the driver calls the chunks 'step' or 'codeword' interchangeably
|
||||
@@ -672,8 +680,7 @@ static int nandc_param(struct qcom_nand_host *host)
|
||||
|
||||
/* configure CMD1 and VLD for ONFI param probing */
|
||||
nandc_set_reg(nandc, NAND_DEV_CMD_VLD,
|
||||
(nandc->vld & ~(1 << READ_START_VLD))
|
||||
| 0 << READ_START_VLD);
|
||||
(nandc->vld & ~READ_START_VLD));
|
||||
nandc_set_reg(nandc, NAND_DEV_CMD1,
|
||||
(nandc->cmd1 & ~(0xFF << READ_ADDR))
|
||||
| NAND_CMD_PARAM << READ_ADDR);
|
||||
@@ -1893,7 +1900,7 @@ static int qcom_nand_host_setup(struct qcom_nand_host *host)
|
||||
| wide_bus << WIDE_FLASH
|
||||
| 1 << DEV0_CFG1_ECC_DISABLE;
|
||||
|
||||
host->ecc_bch_cfg = host->bch_enabled << ECC_CFG_ECC_DISABLE
|
||||
host->ecc_bch_cfg = !host->bch_enabled << ECC_CFG_ECC_DISABLE
|
||||
| 0 << ECC_SW_RESET
|
||||
| host->cw_data << ECC_NUM_DATA_BYTES
|
||||
| 1 << ECC_FORCE_CLK_OPEN
|
||||
@@ -1972,13 +1979,14 @@ static int qcom_nandc_setup(struct qcom_nand_controller *nandc)
|
||||
{
|
||||
/* kill onenand */
|
||||
nandc_write(nandc, SFLASHC_BURST_CFG, 0);
|
||||
nandc_write(nandc, NAND_DEV_CMD_VLD, NAND_DEV_CMD_VLD_VAL);
|
||||
|
||||
/* enable ADM DMA */
|
||||
nandc_write(nandc, NAND_FLASH_CHIP_SELECT, DM_EN);
|
||||
|
||||
/* save the original values of these registers */
|
||||
nandc->cmd1 = nandc_read(nandc, NAND_DEV_CMD1);
|
||||
nandc->vld = nandc_read(nandc, NAND_DEV_CMD_VLD);
|
||||
nandc->vld = NAND_DEV_CMD_VLD_VAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -317,12 +317,12 @@ int t4_wr_mbox_meat_timeout(struct adapter *adap, int mbox, const void *cmd,
|
||||
|
||||
if (v != MBOX_OWNER_DRV) {
|
||||
ret = (v == MBOX_OWNER_FW) ? -EBUSY : -ETIMEDOUT;
|
||||
t4_record_mbox(adap, cmd, MBOX_LEN, access, ret);
|
||||
t4_record_mbox(adap, cmd, size, access, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Copy in the new mailbox command and send it on its way ... */
|
||||
t4_record_mbox(adap, cmd, MBOX_LEN, access, 0);
|
||||
t4_record_mbox(adap, cmd, size, access, 0);
|
||||
for (i = 0; i < size; i += 8)
|
||||
t4_write_reg64(adap, data_reg + i, be64_to_cpu(*p++));
|
||||
|
||||
@@ -371,7 +371,7 @@ int t4_wr_mbox_meat_timeout(struct adapter *adap, int mbox, const void *cmd,
|
||||
}
|
||||
|
||||
ret = (pcie_fw & PCIE_FW_ERR_F) ? -ENXIO : -ETIMEDOUT;
|
||||
t4_record_mbox(adap, cmd, MBOX_LEN, access, ret);
|
||||
t4_record_mbox(adap, cmd, size, access, ret);
|
||||
dev_err(adap->pdev_dev, "command %#x in mailbox %d timed out\n",
|
||||
*(const u8 *)cmd, mbox);
|
||||
t4_report_fw_error(adap);
|
||||
|
||||
@@ -622,6 +622,9 @@ static struct platform_device *dpaa_eth_add_device(int fman_id,
|
||||
goto no_mem;
|
||||
}
|
||||
|
||||
pdev->dev.of_node = node;
|
||||
pdev->dev.parent = priv->dev;
|
||||
|
||||
ret = platform_device_add_data(pdev, &data, sizeof(data));
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
@@ -3690,7 +3690,7 @@ static noinline void gfar_update_link_state(struct gfar_private *priv)
|
||||
u32 tempval1 = gfar_read(®s->maccfg1);
|
||||
u32 tempval = gfar_read(®s->maccfg2);
|
||||
u32 ecntrl = gfar_read(®s->ecntrl);
|
||||
u32 tx_flow_oldval = (tempval & MACCFG1_TX_FLOW);
|
||||
u32 tx_flow_oldval = (tempval1 & MACCFG1_TX_FLOW);
|
||||
|
||||
if (phydev->duplex != priv->oldduplex) {
|
||||
if (!(phydev->duplex))
|
||||
|
||||
@@ -4172,6 +4172,8 @@ static int mlxsw_sp_netdevice_port_upper_event(struct net_device *dev,
|
||||
return -EINVAL;
|
||||
if (!info->linking)
|
||||
break;
|
||||
if (netdev_has_any_upper_dev(upper_dev))
|
||||
return -EINVAL;
|
||||
/* HW limitation forbids to put ports to multiple bridges. */
|
||||
if (netif_is_bridge_master(upper_dev) &&
|
||||
!mlxsw_sp_master_bridge_check(mlxsw_sp, upper_dev))
|
||||
@@ -4185,6 +4187,10 @@ static int mlxsw_sp_netdevice_port_upper_event(struct net_device *dev,
|
||||
if (netif_is_lag_port(dev) && is_vlan_dev(upper_dev) &&
|
||||
!netif_is_lag_master(vlan_dev_real_dev(upper_dev)))
|
||||
return -EINVAL;
|
||||
if (!info->linking)
|
||||
break;
|
||||
if (netdev_has_any_upper_dev(upper_dev))
|
||||
return -EINVAL;
|
||||
break;
|
||||
case NETDEV_CHANGEUPPER:
|
||||
upper_dev = info->upper_dev;
|
||||
|
||||
@@ -724,7 +724,7 @@ static void ql_build_coredump_seg_header(
|
||||
seg_hdr->cookie = MPI_COREDUMP_COOKIE;
|
||||
seg_hdr->segNum = seg_number;
|
||||
seg_hdr->segSize = seg_size;
|
||||
memcpy(seg_hdr->description, desc, (sizeof(seg_hdr->description)) - 1);
|
||||
strncpy(seg_hdr->description, desc, (sizeof(seg_hdr->description)) - 1);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -1084,7 +1084,12 @@ static void netvsc_link_change(struct work_struct *w)
|
||||
bool notify = false, reschedule = false;
|
||||
unsigned long flags, next_reconfig, delay;
|
||||
|
||||
rtnl_lock();
|
||||
/* if changes are happening, comeback later */
|
||||
if (!rtnl_trylock()) {
|
||||
schedule_delayed_work(&ndev_ctx->dwork, LINKCHANGE_INT);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ndev_ctx->start_remove)
|
||||
goto out_unlock;
|
||||
|
||||
|
||||
@@ -3510,6 +3510,7 @@ module_init(macsec_init);
|
||||
module_exit(macsec_exit);
|
||||
|
||||
MODULE_ALIAS_RTNL_LINK("macsec");
|
||||
MODULE_ALIAS_GENL_FAMILY("macsec");
|
||||
|
||||
MODULE_DESCRIPTION("MACsec IEEE 802.1AE");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
@@ -674,9 +674,6 @@ void phy_stop_machine(struct phy_device *phydev)
|
||||
if (phydev->state > PHY_UP && phydev->state != PHY_HALTED)
|
||||
phydev->state = PHY_UP;
|
||||
mutex_unlock(&phydev->lock);
|
||||
|
||||
/* Now we can run the state machine synchronously */
|
||||
phy_state_machine(&phydev->state_queue.work);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1852,6 +1852,12 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode,
|
||||
goto err_wmi_detach;
|
||||
}
|
||||
|
||||
/* If firmware indicates Full Rx Reorder support it must be used in a
|
||||
* slightly different manner. Let HTT code know.
|
||||
*/
|
||||
ar->htt.rx_ring.in_ord_rx = !!(test_bit(WMI_SERVICE_RX_FULL_REORDER,
|
||||
ar->wmi.svc_map));
|
||||
|
||||
status = ath10k_htt_rx_alloc(&ar->htt);
|
||||
if (status) {
|
||||
ath10k_err(ar, "failed to alloc htt rx: %d\n", status);
|
||||
@@ -1964,12 +1970,6 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode,
|
||||
}
|
||||
}
|
||||
|
||||
/* If firmware indicates Full Rx Reorder support it must be used in a
|
||||
* slightly different manner. Let HTT code know.
|
||||
*/
|
||||
ar->htt.rx_ring.in_ord_rx = !!(test_bit(WMI_SERVICE_RX_FULL_REORDER,
|
||||
ar->wmi.svc_map));
|
||||
|
||||
status = ath10k_htt_rx_ring_refill(ar);
|
||||
if (status) {
|
||||
ath10k_err(ar, "failed to refill htt rx ring: %d\n", status);
|
||||
|
||||
@@ -429,6 +429,7 @@ static const struct pci_device_id iwl_hw_card_ids[] = {
|
||||
{IWL_PCI_DEVICE(0x095B, 0x520A, iwl7265_2ac_cfg)},
|
||||
{IWL_PCI_DEVICE(0x095A, 0x9000, iwl7265_2ac_cfg)},
|
||||
{IWL_PCI_DEVICE(0x095A, 0x9400, iwl7265_2ac_cfg)},
|
||||
{IWL_PCI_DEVICE(0x095A, 0x9E10, iwl7265_2ac_cfg)},
|
||||
|
||||
/* 8000 Series */
|
||||
{IWL_PCI_DEVICE(0x24F3, 0x0010, iwl8260_2ac_cfg)},
|
||||
|
||||
@@ -4188,7 +4188,7 @@ int mwifiex_init_channel_scan_gap(struct mwifiex_adapter *adapter)
|
||||
if (adapter->config_bands & BAND_A)
|
||||
n_channels_a = mwifiex_band_5ghz.n_channels;
|
||||
|
||||
adapter->num_in_chan_stats = max_t(u32, n_channels_bg, n_channels_a);
|
||||
adapter->num_in_chan_stats = n_channels_bg + n_channels_a;
|
||||
adapter->chan_stats = vmalloc(sizeof(*adapter->chan_stats) *
|
||||
adapter->num_in_chan_stats);
|
||||
|
||||
|
||||
@@ -2479,6 +2479,12 @@ mwifiex_update_chan_statistics(struct mwifiex_private *priv,
|
||||
sizeof(struct mwifiex_chan_stats);
|
||||
|
||||
for (i = 0 ; i < num_chan; i++) {
|
||||
if (adapter->survey_idx >= adapter->num_in_chan_stats) {
|
||||
mwifiex_dbg(adapter, WARN,
|
||||
"FW reported too many channel results (max %d)\n",
|
||||
adapter->num_in_chan_stats);
|
||||
return;
|
||||
}
|
||||
chan_stats.chan_num = fw_chan_stats->chan_num;
|
||||
chan_stats.bandcfg = fw_chan_stats->bandcfg;
|
||||
chan_stats.flags = fw_chan_stats->flags;
|
||||
|
||||
@@ -2269,7 +2269,7 @@ int rtl_pci_probe(struct pci_dev *pdev,
|
||||
/* find adapter */
|
||||
if (!_rtl_pci_find_adapter(pdev, hw)) {
|
||||
err = -ENODEV;
|
||||
goto fail3;
|
||||
goto fail2;
|
||||
}
|
||||
|
||||
/* Init IO handler */
|
||||
@@ -2339,10 +2339,10 @@ fail3:
|
||||
pci_set_drvdata(pdev, NULL);
|
||||
rtl_deinit_core(hw);
|
||||
|
||||
fail2:
|
||||
if (rtlpriv->io.pci_mem_start != 0)
|
||||
pci_iounmap(pdev, (void __iomem *)rtlpriv->io.pci_mem_start);
|
||||
|
||||
fail2:
|
||||
pci_release_regions(pdev);
|
||||
complete(&rtlpriv->firmware_loading_complete);
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ static struct nvmf_host *nvmf_host_default(void)
|
||||
kref_init(&host->ref);
|
||||
uuid_be_gen(&host->id);
|
||||
snprintf(host->nqn, NVMF_NQN_SIZE,
|
||||
"nqn.2014-08.org.nvmexpress:NVMf:uuid:%pUb", &host->id);
|
||||
"nqn.2014-08.org.nvmexpress:uuid:%pUb", &host->id);
|
||||
|
||||
mutex_lock(&nvmf_hosts_mutex);
|
||||
list_add_tail(&host->list, &nvmf_hosts);
|
||||
|
||||
+14
-5
@@ -1244,6 +1244,7 @@ sg_mmap(struct file *filp, struct vm_area_struct *vma)
|
||||
unsigned long req_sz, len, sa;
|
||||
Sg_scatter_hold *rsv_schp;
|
||||
int k, length;
|
||||
int ret = 0;
|
||||
|
||||
if ((!filp) || (!vma) || (!(sfp = (Sg_fd *) filp->private_data)))
|
||||
return -ENXIO;
|
||||
@@ -1254,8 +1255,11 @@ sg_mmap(struct file *filp, struct vm_area_struct *vma)
|
||||
if (vma->vm_pgoff)
|
||||
return -EINVAL; /* want no offset */
|
||||
rsv_schp = &sfp->reserve;
|
||||
if (req_sz > rsv_schp->bufflen)
|
||||
return -ENOMEM; /* cannot map more than reserved buffer */
|
||||
mutex_lock(&sfp->f_mutex);
|
||||
if (req_sz > rsv_schp->bufflen) {
|
||||
ret = -ENOMEM; /* cannot map more than reserved buffer */
|
||||
goto out;
|
||||
}
|
||||
|
||||
sa = vma->vm_start;
|
||||
length = 1 << (PAGE_SHIFT + rsv_schp->page_order);
|
||||
@@ -1269,7 +1273,9 @@ sg_mmap(struct file *filp, struct vm_area_struct *vma)
|
||||
vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP;
|
||||
vma->vm_private_data = sfp;
|
||||
vma->vm_ops = &sg_mmap_vm_ops;
|
||||
return 0;
|
||||
out:
|
||||
mutex_unlock(&sfp->f_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1741,9 +1747,12 @@ sg_start_req(Sg_request *srp, unsigned char *cmd)
|
||||
!sfp->res_in_use) {
|
||||
sfp->res_in_use = 1;
|
||||
sg_link_reserve(sfp, srp, dxfer_len);
|
||||
} else if ((hp->flags & SG_FLAG_MMAP_IO) && sfp->res_in_use) {
|
||||
} else if (hp->flags & SG_FLAG_MMAP_IO) {
|
||||
res = -EBUSY; /* sfp->res_in_use == 1 */
|
||||
if (dxfer_len > rsv_schp->bufflen)
|
||||
res = -ENOMEM;
|
||||
mutex_unlock(&sfp->f_mutex);
|
||||
return -EBUSY;
|
||||
return res;
|
||||
} else {
|
||||
res = sg_build_indirect(req_schp, sfp, dxfer_len);
|
||||
if (res) {
|
||||
|
||||
@@ -414,7 +414,7 @@ void set_sense_data(struct rtsx_chip *chip, unsigned int lun, u8 err_code,
|
||||
sense->ascq = ascq;
|
||||
if (sns_key_info0 != 0) {
|
||||
sense->sns_key_info[0] = SKSV | sns_key_info0;
|
||||
sense->sns_key_info[1] = (sns_key_info1 & 0xf0) >> 8;
|
||||
sense->sns_key_info[1] = (sns_key_info1 & 0xf0) >> 4;
|
||||
sense->sns_key_info[2] = sns_key_info1 & 0x0f;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -629,6 +629,8 @@ static void async_completed(struct urb *urb)
|
||||
if (as->status < 0 && as->bulk_addr && as->status != -ECONNRESET &&
|
||||
as->status != -ENOENT)
|
||||
cancel_bulk_urbs(ps, as->bulk_addr);
|
||||
|
||||
wake_up(&ps->wait);
|
||||
spin_unlock(&ps->lock);
|
||||
|
||||
if (signr) {
|
||||
@@ -636,8 +638,6 @@ static void async_completed(struct urb *urb)
|
||||
put_pid(pid);
|
||||
put_cred(cred);
|
||||
}
|
||||
|
||||
wake_up(&ps->wait);
|
||||
}
|
||||
|
||||
static void destroy_async(struct usb_dev_state *ps, struct list_head *list)
|
||||
|
||||
@@ -57,8 +57,9 @@ static const struct usb_device_id usb_quirk_list[] = {
|
||||
/* Microsoft LifeCam-VX700 v2.0 */
|
||||
{ USB_DEVICE(0x045e, 0x0770), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
/* Logitech HD Pro Webcams C920 and C930e */
|
||||
/* Logitech HD Pro Webcams C920, C920-C and C930e */
|
||||
{ USB_DEVICE(0x046d, 0x082d), .driver_info = USB_QUIRK_DELAY_INIT },
|
||||
{ USB_DEVICE(0x046d, 0x0841), .driver_info = USB_QUIRK_DELAY_INIT },
|
||||
{ USB_DEVICE(0x046d, 0x0843), .driver_info = USB_QUIRK_DELAY_INIT },
|
||||
|
||||
/* Logitech ConferenceCam CC3000e */
|
||||
@@ -217,6 +218,9 @@ static const struct usb_device_id usb_quirk_list[] = {
|
||||
{ USB_DEVICE(0x1a0a, 0x0200), .driver_info =
|
||||
USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL },
|
||||
|
||||
/* Corsair Strafe RGB */
|
||||
{ USB_DEVICE(0x1b1c, 0x1b20), .driver_info = USB_QUIRK_DELAY_INIT },
|
||||
|
||||
/* Acer C120 LED Projector */
|
||||
{ USB_DEVICE(0x1de1, 0xc102), .driver_info = USB_QUIRK_NO_LPM },
|
||||
|
||||
|
||||
@@ -825,7 +825,7 @@ static void receive_file_work(struct work_struct *data)
|
||||
struct usb_request *read_req = NULL, *write_req = NULL;
|
||||
struct file *filp;
|
||||
loff_t offset;
|
||||
int64_t count;
|
||||
int64_t count, len;
|
||||
int ret, cur_buf = 0;
|
||||
int r = 0;
|
||||
|
||||
@@ -843,8 +843,10 @@ static void receive_file_work(struct work_struct *data)
|
||||
read_req = dev->rx_req[cur_buf];
|
||||
cur_buf = (cur_buf + 1) % RX_REQ_MAX;
|
||||
|
||||
read_req->length = (count > MTP_BULK_BUFFER_SIZE
|
||||
? MTP_BULK_BUFFER_SIZE : count);
|
||||
len = usb_ep_align_maybe(cdev->gadget, dev->ep_out, count);
|
||||
if (len > MTP_BULK_BUFFER_SIZE)
|
||||
len = MTP_BULK_BUFFER_SIZE;
|
||||
read_req->length = len;
|
||||
dev->rx_done = 0;
|
||||
ret = usb_ep_queue(dev->ep_out, read_req, GFP_KERNEL);
|
||||
if (ret < 0) {
|
||||
|
||||
@@ -142,29 +142,30 @@ static int amd_chipset_sb_type_init(struct amd_chipset_info *pinfo)
|
||||
pinfo->sb_type.gen = AMD_CHIPSET_SB700;
|
||||
else if (rev >= 0x40 && rev <= 0x4f)
|
||||
pinfo->sb_type.gen = AMD_CHIPSET_SB800;
|
||||
}
|
||||
pinfo->smbus_dev = pci_get_device(PCI_VENDOR_ID_AMD,
|
||||
0x145c, NULL);
|
||||
if (pinfo->smbus_dev) {
|
||||
pinfo->sb_type.gen = AMD_CHIPSET_TAISHAN;
|
||||
} else {
|
||||
pinfo->smbus_dev = pci_get_device(PCI_VENDOR_ID_AMD,
|
||||
PCI_DEVICE_ID_AMD_HUDSON2_SMBUS, NULL);
|
||||
|
||||
if (!pinfo->smbus_dev) {
|
||||
pinfo->sb_type.gen = NOT_AMD_CHIPSET;
|
||||
return 0;
|
||||
if (pinfo->smbus_dev) {
|
||||
rev = pinfo->smbus_dev->revision;
|
||||
if (rev >= 0x11 && rev <= 0x14)
|
||||
pinfo->sb_type.gen = AMD_CHIPSET_HUDSON2;
|
||||
else if (rev >= 0x15 && rev <= 0x18)
|
||||
pinfo->sb_type.gen = AMD_CHIPSET_BOLTON;
|
||||
else if (rev >= 0x39 && rev <= 0x3a)
|
||||
pinfo->sb_type.gen = AMD_CHIPSET_YANGTZE;
|
||||
} else {
|
||||
pinfo->smbus_dev = pci_get_device(PCI_VENDOR_ID_AMD,
|
||||
0x145c, NULL);
|
||||
if (pinfo->smbus_dev) {
|
||||
rev = pinfo->smbus_dev->revision;
|
||||
pinfo->sb_type.gen = AMD_CHIPSET_TAISHAN;
|
||||
} else {
|
||||
pinfo->sb_type.gen = NOT_AMD_CHIPSET;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
rev = pinfo->smbus_dev->revision;
|
||||
if (rev >= 0x11 && rev <= 0x14)
|
||||
pinfo->sb_type.gen = AMD_CHIPSET_HUDSON2;
|
||||
else if (rev >= 0x15 && rev <= 0x18)
|
||||
pinfo->sb_type.gen = AMD_CHIPSET_BOLTON;
|
||||
else if (rev >= 0x39 && rev <= 0x3a)
|
||||
pinfo->sb_type.gen = AMD_CHIPSET_YANGTZE;
|
||||
}
|
||||
|
||||
pinfo->sb_type.rev = rev;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -2655,6 +2655,13 @@ static int musb_suspend(struct device *dev)
|
||||
{
|
||||
struct musb *musb = dev_to_musb(dev);
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret < 0) {
|
||||
pm_runtime_put_noidle(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
musb_platform_disable(musb);
|
||||
musb_generic_disable(musb);
|
||||
@@ -2703,14 +2710,6 @@ static int musb_resume(struct device *dev)
|
||||
if ((devctl & mask) != (musb->context.devctl & mask))
|
||||
musb->port1_status = 0;
|
||||
|
||||
/*
|
||||
* The USB HUB code expects the device to be in RPM_ACTIVE once it came
|
||||
* out of suspend
|
||||
*/
|
||||
pm_runtime_disable(dev);
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
musb_start(musb);
|
||||
|
||||
spin_lock_irqsave(&musb->lock, flags);
|
||||
@@ -2720,6 +2719,9 @@ static int musb_resume(struct device *dev)
|
||||
error);
|
||||
spin_unlock_irqrestore(&musb->lock, flags);
|
||||
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -2023,6 +2023,7 @@ static const struct usb_device_id option_ids[] = {
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x7d03, 0xff, 0x02, 0x01) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x7d03, 0xff, 0x00, 0x00) },
|
||||
{ USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7d04, 0xff) }, /* D-Link DWM-158 */
|
||||
{ USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7d0e, 0xff) }, /* D-Link DWM-157 C1 */
|
||||
{ USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7e19, 0xff), /* D-Link DWM-221 B1 */
|
||||
.driver_info = (kernel_ulong_t)&net_intf4_blacklist },
|
||||
{ USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7e35, 0xff), /* D-Link DWM-222 */
|
||||
|
||||
+6
-1
@@ -537,8 +537,13 @@ static int vhost_net_rx_peek_head_len(struct vhost_net *net, struct sock *sk)
|
||||
|
||||
preempt_enable();
|
||||
|
||||
if (vhost_enable_notify(&net->dev, vq))
|
||||
if (!vhost_vq_avail_empty(&net->dev, vq))
|
||||
vhost_poll_queue(&vq->poll);
|
||||
else if (unlikely(vhost_enable_notify(&net->dev, vq))) {
|
||||
vhost_disable_notify(&net->dev, vq);
|
||||
vhost_poll_queue(&vq->poll);
|
||||
}
|
||||
|
||||
mutex_unlock(&vq->mutex);
|
||||
|
||||
len = peek_head_len(sk);
|
||||
|
||||
@@ -1834,6 +1834,8 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data)
|
||||
goto restore;
|
||||
}
|
||||
|
||||
btrfs_qgroup_rescan_resume(fs_info);
|
||||
|
||||
if (!fs_info->uuid_root) {
|
||||
btrfs_info(fs_info, "creating UUID tree");
|
||||
ret = btrfs_create_uuid_tree(fs_info);
|
||||
|
||||
@@ -355,6 +355,10 @@ static int dlm_device_register(struct dlm_ls *ls, char *name)
|
||||
error = misc_register(&ls->ls_device);
|
||||
if (error) {
|
||||
kfree(ls->ls_device.name);
|
||||
/* this has to be set to NULL
|
||||
* to avoid a double-free in dlm_device_deregister
|
||||
*/
|
||||
ls->ls_device.name = NULL;
|
||||
}
|
||||
fail:
|
||||
return error;
|
||||
|
||||
+1
-3
@@ -316,7 +316,7 @@ static int check_index_in_prev_nodes(struct f2fs_sb_info *sbi,
|
||||
return 0;
|
||||
|
||||
/* Get the previous summary */
|
||||
for (i = CURSEG_WARM_DATA; i <= CURSEG_COLD_DATA; i++) {
|
||||
for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) {
|
||||
struct curseg_info *curseg = CURSEG_I(sbi, i);
|
||||
if (curseg->segno == segno) {
|
||||
sum = curseg->sum_blk->entries[blkoff];
|
||||
@@ -626,8 +626,6 @@ out:
|
||||
}
|
||||
|
||||
clear_sbi_flag(sbi, SBI_POR_DOING);
|
||||
if (err)
|
||||
set_ckpt_flags(sbi, CP_ERROR_FLAG);
|
||||
mutex_unlock(&sbi->cp_mutex);
|
||||
|
||||
/* let's drop all the directory inodes for clean checkpoint */
|
||||
|
||||
@@ -637,6 +637,7 @@ again:
|
||||
|
||||
dispose_list(&dispose);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(evict_inodes);
|
||||
|
||||
/**
|
||||
* invalidate_inodes - attempt to free all inodes on a superblock
|
||||
|
||||
@@ -138,7 +138,6 @@ extern bool atime_needs_update_rcu(const struct path *, struct inode *);
|
||||
extern void inode_io_list_del(struct inode *inode);
|
||||
|
||||
extern long get_nr_dirty_inodes(void);
|
||||
extern void evict_inodes(struct super_block *);
|
||||
extern int invalidate_inodes(struct super_block *, bool);
|
||||
|
||||
/*
|
||||
|
||||
+2
-2
@@ -281,7 +281,7 @@ iomap_dirty_actor(struct inode *inode, loff_t pos, loff_t length, void *data,
|
||||
unsigned long bytes; /* Bytes to write to page */
|
||||
|
||||
offset = (pos & (PAGE_SIZE - 1));
|
||||
bytes = min_t(unsigned long, PAGE_SIZE - offset, length);
|
||||
bytes = min_t(loff_t, PAGE_SIZE - offset, length);
|
||||
|
||||
rpage = __iomap_read_page(inode, pos);
|
||||
if (IS_ERR(rpage))
|
||||
@@ -376,7 +376,7 @@ iomap_zero_range_actor(struct inode *inode, loff_t pos, loff_t count,
|
||||
unsigned offset, bytes;
|
||||
|
||||
offset = pos & (PAGE_SIZE - 1); /* Within page */
|
||||
bytes = min_t(unsigned, PAGE_SIZE - offset, count);
|
||||
bytes = min_t(loff_t, PAGE_SIZE - offset, count);
|
||||
|
||||
if (IS_DAX(inode))
|
||||
status = iomap_dax_zero(pos, offset, bytes, iomap);
|
||||
|
||||
+1
-3
@@ -227,6 +227,7 @@ static struct mount *alloc_vfsmnt(const char *name)
|
||||
mnt->mnt_count = 1;
|
||||
mnt->mnt_writers = 0;
|
||||
#endif
|
||||
mnt->mnt.data = NULL;
|
||||
|
||||
INIT_HLIST_NODE(&mnt->mnt_hash);
|
||||
INIT_LIST_HEAD(&mnt->mnt_child);
|
||||
@@ -976,7 +977,6 @@ vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void
|
||||
if (!mnt)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
mnt->mnt.data = NULL;
|
||||
if (type->alloc_mnt_data) {
|
||||
mnt->mnt.data = type->alloc_mnt_data();
|
||||
if (!mnt->mnt.data) {
|
||||
@@ -990,7 +990,6 @@ vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void
|
||||
|
||||
root = mount_fs(type, flags, name, &mnt->mnt, data);
|
||||
if (IS_ERR(root)) {
|
||||
kfree(mnt->mnt.data);
|
||||
mnt_free_id(mnt);
|
||||
free_vfsmnt(mnt);
|
||||
return ERR_CAST(root);
|
||||
@@ -1109,7 +1108,6 @@ static struct mount *clone_mnt(struct mount *old, struct dentry *root,
|
||||
return mnt;
|
||||
|
||||
out_free:
|
||||
kfree(mnt->mnt.data);
|
||||
mnt_free_id(mnt);
|
||||
free_vfsmnt(mnt);
|
||||
return ERR_PTR(err);
|
||||
|
||||
+3
-3
@@ -636,11 +636,11 @@ ssize_t nfs_file_write(struct kiocb *iocb, struct iov_iter *from)
|
||||
if (result <= 0)
|
||||
goto out;
|
||||
|
||||
result = generic_write_sync(iocb, result);
|
||||
if (result < 0)
|
||||
goto out;
|
||||
written = result;
|
||||
iocb->ki_pos += written;
|
||||
result = generic_write_sync(iocb, written);
|
||||
if (result < 0)
|
||||
goto out;
|
||||
|
||||
/* Return error values */
|
||||
if (nfs_need_check_write(file, inode)) {
|
||||
|
||||
@@ -248,7 +248,6 @@ int nfs_iocounter_wait(struct nfs_lock_context *l_ctx);
|
||||
extern const struct nfs_pageio_ops nfs_pgio_rw_ops;
|
||||
struct nfs_pgio_header *nfs_pgio_header_alloc(const struct nfs_rw_ops *);
|
||||
void nfs_pgio_header_free(struct nfs_pgio_header *);
|
||||
void nfs_pgio_data_destroy(struct nfs_pgio_header *);
|
||||
int nfs_generic_pgio(struct nfs_pageio_descriptor *, struct nfs_pgio_header *);
|
||||
int nfs_initiate_pgio(struct rpc_clnt *clnt, struct nfs_pgio_header *hdr,
|
||||
struct rpc_cred *cred, const struct nfs_rpc_ops *rpc_ops,
|
||||
|
||||
+12
-14
@@ -497,16 +497,6 @@ struct nfs_pgio_header *nfs_pgio_header_alloc(const struct nfs_rw_ops *ops)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nfs_pgio_header_alloc);
|
||||
|
||||
/*
|
||||
* nfs_pgio_header_free - Free a read or write header
|
||||
* @hdr: The header to free
|
||||
*/
|
||||
void nfs_pgio_header_free(struct nfs_pgio_header *hdr)
|
||||
{
|
||||
hdr->rw_ops->rw_free_header(hdr);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nfs_pgio_header_free);
|
||||
|
||||
/**
|
||||
* nfs_pgio_data_destroy - make @hdr suitable for reuse
|
||||
*
|
||||
@@ -515,14 +505,24 @@ EXPORT_SYMBOL_GPL(nfs_pgio_header_free);
|
||||
*
|
||||
* @hdr: A header that has had nfs_generic_pgio called
|
||||
*/
|
||||
void nfs_pgio_data_destroy(struct nfs_pgio_header *hdr)
|
||||
static void nfs_pgio_data_destroy(struct nfs_pgio_header *hdr)
|
||||
{
|
||||
if (hdr->args.context)
|
||||
put_nfs_open_context(hdr->args.context);
|
||||
if (hdr->page_array.pagevec != hdr->page_array.page_array)
|
||||
kfree(hdr->page_array.pagevec);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nfs_pgio_data_destroy);
|
||||
|
||||
/*
|
||||
* nfs_pgio_header_free - Free a read or write header
|
||||
* @hdr: The header to free
|
||||
*/
|
||||
void nfs_pgio_header_free(struct nfs_pgio_header *hdr)
|
||||
{
|
||||
nfs_pgio_data_destroy(hdr);
|
||||
hdr->rw_ops->rw_free_header(hdr);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nfs_pgio_header_free);
|
||||
|
||||
/**
|
||||
* nfs_pgio_rpcsetup - Set up arguments for a pageio call
|
||||
@@ -636,7 +636,6 @@ EXPORT_SYMBOL_GPL(nfs_initiate_pgio);
|
||||
static void nfs_pgio_error(struct nfs_pgio_header *hdr)
|
||||
{
|
||||
set_bit(NFS_IOHDR_REDO, &hdr->flags);
|
||||
nfs_pgio_data_destroy(hdr);
|
||||
hdr->completion_ops->completion(hdr);
|
||||
}
|
||||
|
||||
@@ -647,7 +646,6 @@ static void nfs_pgio_error(struct nfs_pgio_header *hdr)
|
||||
static void nfs_pgio_release(void *calldata)
|
||||
{
|
||||
struct nfs_pgio_header *hdr = calldata;
|
||||
nfs_pgio_data_destroy(hdr);
|
||||
hdr->completion_ops->completion(hdr);
|
||||
}
|
||||
|
||||
|
||||
@@ -2145,7 +2145,6 @@ pnfs_write_through_mds(struct nfs_pageio_descriptor *desc,
|
||||
nfs_pageio_reset_write_mds(desc);
|
||||
mirror->pg_recoalesce = 1;
|
||||
}
|
||||
nfs_pgio_data_destroy(hdr);
|
||||
hdr->release(hdr);
|
||||
}
|
||||
|
||||
@@ -2257,7 +2256,6 @@ pnfs_read_through_mds(struct nfs_pageio_descriptor *desc,
|
||||
nfs_pageio_reset_read_mds(desc);
|
||||
mirror->pg_recoalesce = 1;
|
||||
}
|
||||
nfs_pgio_data_destroy(hdr);
|
||||
hdr->release(hdr);
|
||||
}
|
||||
|
||||
|
||||
@@ -176,6 +176,9 @@ void fixup_lower_ownership(struct dentry *dentry, const char *name)
|
||||
gid_t gid = sbi->options.fs_low_gid;
|
||||
struct iattr newattrs;
|
||||
|
||||
if (!sbi->options.gid_derivation)
|
||||
return;
|
||||
|
||||
info = SDCARDFS_I(d_inode(dentry));
|
||||
info_d = info->data;
|
||||
perm = info_d->perm;
|
||||
|
||||
+8
-4
@@ -34,10 +34,14 @@ const struct cred *override_fsids(struct sdcardfs_sb_info *sbi,
|
||||
if (!cred)
|
||||
return NULL;
|
||||
|
||||
if (data->under_obb)
|
||||
uid = AID_MEDIA_OBB;
|
||||
else
|
||||
uid = multiuser_get_uid(data->userid, sbi->options.fs_low_uid);
|
||||
if (sbi->options.gid_derivation) {
|
||||
if (data->under_obb)
|
||||
uid = AID_MEDIA_OBB;
|
||||
else
|
||||
uid = multiuser_get_uid(data->userid, sbi->options.fs_low_uid);
|
||||
} else {
|
||||
uid = sbi->options.fs_low_uid;
|
||||
}
|
||||
cred->fsuid = make_kuid(&init_user_ns, uid);
|
||||
cred->fsgid = make_kgid(&init_user_ns, sbi->options.fs_low_gid);
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ enum {
|
||||
Opt_multiuser,
|
||||
Opt_userid,
|
||||
Opt_reserved_mb,
|
||||
Opt_gid_derivation,
|
||||
Opt_err,
|
||||
};
|
||||
|
||||
@@ -43,6 +44,7 @@ static const match_table_t sdcardfs_tokens = {
|
||||
{Opt_mask, "mask=%u"},
|
||||
{Opt_userid, "userid=%d"},
|
||||
{Opt_multiuser, "multiuser"},
|
||||
{Opt_gid_derivation, "derive_gid"},
|
||||
{Opt_reserved_mb, "reserved_mb=%u"},
|
||||
{Opt_err, NULL}
|
||||
};
|
||||
@@ -64,6 +66,8 @@ static int parse_options(struct super_block *sb, char *options, int silent,
|
||||
vfsopts->gid = 0;
|
||||
/* by default, 0MB is reserved */
|
||||
opts->reserved_mb = 0;
|
||||
/* by default, gid derivation is off */
|
||||
opts->gid_derivation = false;
|
||||
|
||||
*debug = 0;
|
||||
|
||||
@@ -115,6 +119,9 @@ static int parse_options(struct super_block *sb, char *options, int silent,
|
||||
return 0;
|
||||
opts->reserved_mb = option;
|
||||
break;
|
||||
case Opt_gid_derivation:
|
||||
opts->gid_derivation = true;
|
||||
break;
|
||||
/* unknown option */
|
||||
default:
|
||||
if (!silent)
|
||||
|
||||
@@ -219,6 +219,7 @@ struct sdcardfs_mount_options {
|
||||
gid_t fs_low_gid;
|
||||
userid_t fs_user_id;
|
||||
bool multiuser;
|
||||
bool gid_derivation;
|
||||
unsigned int reserved_mb;
|
||||
};
|
||||
|
||||
|
||||
@@ -302,6 +302,8 @@ static int sdcardfs_show_options(struct vfsmount *mnt, struct seq_file *m,
|
||||
seq_printf(m, ",mask=%u", vfsopts->mask);
|
||||
if (opts->fs_user_id)
|
||||
seq_printf(m, ",userid=%u", opts->fs_user_id);
|
||||
if (opts->gid_derivation)
|
||||
seq_puts(m, ",derive_gid");
|
||||
if (opts->reserved_mb != 0)
|
||||
seq_printf(m, ",reserved=%uMB", opts->reserved_mb);
|
||||
|
||||
|
||||
@@ -351,7 +351,7 @@ xfs_attr3_leaf_read(
|
||||
|
||||
err = xfs_da_read_buf(tp, dp, bno, mappedbno, bpp,
|
||||
XFS_ATTR_FORK, &xfs_attr3_leaf_buf_ops);
|
||||
if (!err && tp)
|
||||
if (!err && tp && *bpp)
|
||||
xfs_trans_buf_set_type(tp, *bpp, XFS_BLFT_ATTR_LEAF_BUF);
|
||||
return err;
|
||||
}
|
||||
|
||||
+50
-10
@@ -579,7 +579,7 @@ xfs_bmap_validate_ret(
|
||||
|
||||
#else
|
||||
#define xfs_bmap_check_leaf_extents(cur, ip, whichfork) do { } while (0)
|
||||
#define xfs_bmap_validate_ret(bno,len,flags,mval,onmap,nmap)
|
||||
#define xfs_bmap_validate_ret(bno,len,flags,mval,onmap,nmap) do { } while (0)
|
||||
#endif /* DEBUG */
|
||||
|
||||
/*
|
||||
@@ -5555,6 +5555,8 @@ __xfs_bunmapi(
|
||||
int whichfork; /* data or attribute fork */
|
||||
xfs_fsblock_t sum;
|
||||
xfs_filblks_t len = *rlen; /* length to unmap in file */
|
||||
xfs_fileoff_t max_len;
|
||||
xfs_agnumber_t prev_agno = NULLAGNUMBER, agno;
|
||||
|
||||
trace_xfs_bunmap(ip, bno, len, flags, _RET_IP_);
|
||||
|
||||
@@ -5576,6 +5578,16 @@ __xfs_bunmapi(
|
||||
ASSERT(len > 0);
|
||||
ASSERT(nexts >= 0);
|
||||
|
||||
/*
|
||||
* Guesstimate how many blocks we can unmap without running the risk of
|
||||
* blowing out the transaction with a mix of EFIs and reflink
|
||||
* adjustments.
|
||||
*/
|
||||
if (xfs_is_reflink_inode(ip) && whichfork == XFS_DATA_FORK)
|
||||
max_len = min(len, xfs_refcount_max_unmap(tp->t_log_res));
|
||||
else
|
||||
max_len = len;
|
||||
|
||||
if (!(ifp->if_flags & XFS_IFEXTENTS) &&
|
||||
(error = xfs_iread_extents(tp, ip, whichfork)))
|
||||
return error;
|
||||
@@ -5621,7 +5633,7 @@ __xfs_bunmapi(
|
||||
|
||||
extno = 0;
|
||||
while (bno != (xfs_fileoff_t)-1 && bno >= start && lastx >= 0 &&
|
||||
(nexts == 0 || extno < nexts)) {
|
||||
(nexts == 0 || extno < nexts) && max_len > 0) {
|
||||
/*
|
||||
* Is the found extent after a hole in which bno lives?
|
||||
* Just back up to the previous extent, if so.
|
||||
@@ -5647,6 +5659,17 @@ __xfs_bunmapi(
|
||||
ASSERT(ep != NULL);
|
||||
del = got;
|
||||
wasdel = isnullstartblock(del.br_startblock);
|
||||
|
||||
/*
|
||||
* Make sure we don't touch multiple AGF headers out of order
|
||||
* in a single transaction, as that could cause AB-BA deadlocks.
|
||||
*/
|
||||
if (!wasdel) {
|
||||
agno = XFS_FSB_TO_AGNO(mp, del.br_startblock);
|
||||
if (prev_agno != NULLAGNUMBER && prev_agno > agno)
|
||||
break;
|
||||
prev_agno = agno;
|
||||
}
|
||||
if (got.br_startoff < start) {
|
||||
del.br_startoff = start;
|
||||
del.br_blockcount -= start - got.br_startoff;
|
||||
@@ -5655,6 +5678,15 @@ __xfs_bunmapi(
|
||||
}
|
||||
if (del.br_startoff + del.br_blockcount > bno + 1)
|
||||
del.br_blockcount = bno + 1 - del.br_startoff;
|
||||
|
||||
/* How much can we safely unmap? */
|
||||
if (max_len < del.br_blockcount) {
|
||||
del.br_startoff += del.br_blockcount - max_len;
|
||||
if (!wasdel)
|
||||
del.br_startblock += del.br_blockcount - max_len;
|
||||
del.br_blockcount = max_len;
|
||||
}
|
||||
|
||||
sum = del.br_startblock + del.br_blockcount;
|
||||
if (isrt &&
|
||||
(mod = do_mod(sum, mp->m_sb.sb_rextsize))) {
|
||||
@@ -5835,6 +5867,7 @@ __xfs_bunmapi(
|
||||
if (!isrt && wasdel)
|
||||
xfs_mod_fdblocks(mp, (int64_t)del.br_blockcount, false);
|
||||
|
||||
max_len -= del.br_blockcount;
|
||||
bno = del.br_startoff - 1;
|
||||
nodelete:
|
||||
/*
|
||||
@@ -6604,25 +6637,33 @@ xfs_bmap_finish_one(
|
||||
int whichfork,
|
||||
xfs_fileoff_t startoff,
|
||||
xfs_fsblock_t startblock,
|
||||
xfs_filblks_t blockcount,
|
||||
xfs_filblks_t *blockcount,
|
||||
xfs_exntst_t state)
|
||||
{
|
||||
struct xfs_bmbt_irec bmap;
|
||||
int nimaps = 1;
|
||||
xfs_fsblock_t firstfsb;
|
||||
int flags = XFS_BMAPI_REMAP;
|
||||
int done;
|
||||
int error = 0;
|
||||
|
||||
bmap.br_startblock = startblock;
|
||||
bmap.br_startoff = startoff;
|
||||
bmap.br_blockcount = blockcount;
|
||||
bmap.br_blockcount = *blockcount;
|
||||
bmap.br_state = state;
|
||||
|
||||
/*
|
||||
* firstfsb is tied to the transaction lifetime and is used to
|
||||
* ensure correct AG locking order and schedule work item
|
||||
* continuations. XFS_BUI_MAX_FAST_EXTENTS (== 1) restricts us
|
||||
* to only making one bmap call per transaction, so it should
|
||||
* be safe to have it as a local variable here.
|
||||
*/
|
||||
firstfsb = NULLFSBLOCK;
|
||||
|
||||
trace_xfs_bmap_deferred(tp->t_mountp,
|
||||
XFS_FSB_TO_AGNO(tp->t_mountp, startblock), type,
|
||||
XFS_FSB_TO_AGBNO(tp->t_mountp, startblock),
|
||||
ip->i_ino, whichfork, startoff, blockcount, state);
|
||||
ip->i_ino, whichfork, startoff, *blockcount, state);
|
||||
|
||||
if (whichfork != XFS_DATA_FORK && whichfork != XFS_ATTR_FORK)
|
||||
return -EFSCORRUPTED;
|
||||
@@ -6641,12 +6682,11 @@ xfs_bmap_finish_one(
|
||||
bmap.br_blockcount, flags, &firstfsb,
|
||||
bmap.br_blockcount, &bmap, &nimaps,
|
||||
dfops);
|
||||
*blockcount = 0;
|
||||
break;
|
||||
case XFS_BMAP_UNMAP:
|
||||
error = xfs_bunmapi(tp, ip, bmap.br_startoff,
|
||||
bmap.br_blockcount, flags, 1, &firstfsb,
|
||||
dfops, &done);
|
||||
ASSERT(done);
|
||||
error = __xfs_bunmapi(tp, ip, startoff, blockcount,
|
||||
XFS_BMAPI_REMAP, 1, &firstfsb, dfops);
|
||||
break;
|
||||
default:
|
||||
ASSERT(0);
|
||||
|
||||
@@ -265,7 +265,7 @@ struct xfs_bmap_intent {
|
||||
int xfs_bmap_finish_one(struct xfs_trans *tp, struct xfs_defer_ops *dfops,
|
||||
struct xfs_inode *ip, enum xfs_bmap_intent_type type,
|
||||
int whichfork, xfs_fileoff_t startoff, xfs_fsblock_t startblock,
|
||||
xfs_filblks_t blockcount, xfs_exntst_t state);
|
||||
xfs_filblks_t *blockcount, xfs_exntst_t state);
|
||||
int xfs_bmap_map_extent(struct xfs_mount *mp, struct xfs_defer_ops *dfops,
|
||||
struct xfs_inode *ip, struct xfs_bmbt_irec *imap);
|
||||
int xfs_bmap_unmap_extent(struct xfs_mount *mp, struct xfs_defer_ops *dfops,
|
||||
|
||||
@@ -888,6 +888,7 @@ xfs_bmbt_change_owner(
|
||||
cur = xfs_bmbt_init_cursor(ip->i_mount, tp, ip, whichfork);
|
||||
if (!cur)
|
||||
return -ENOMEM;
|
||||
cur->bc_private.b.flags |= XFS_BTCUR_BPRV_INVALID_OWNER;
|
||||
|
||||
error = xfs_btree_change_owner(cur, new_owner, buffer_list);
|
||||
xfs_btree_del_cursor(cur, error ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
|
||||
|
||||
+23
-12
@@ -714,7 +714,8 @@ xfs_btree_firstrec(
|
||||
* Get the block pointer for this level.
|
||||
*/
|
||||
block = xfs_btree_get_block(cur, level, &bp);
|
||||
xfs_btree_check_block(cur, block, level, bp);
|
||||
if (xfs_btree_check_block(cur, block, level, bp))
|
||||
return 0;
|
||||
/*
|
||||
* It's empty, there is no such record.
|
||||
*/
|
||||
@@ -743,7 +744,8 @@ xfs_btree_lastrec(
|
||||
* Get the block pointer for this level.
|
||||
*/
|
||||
block = xfs_btree_get_block(cur, level, &bp);
|
||||
xfs_btree_check_block(cur, block, level, bp);
|
||||
if (xfs_btree_check_block(cur, block, level, bp))
|
||||
return 0;
|
||||
/*
|
||||
* It's empty, there is no such record.
|
||||
*/
|
||||
@@ -1772,6 +1774,7 @@ xfs_btree_lookup_get_block(
|
||||
|
||||
/* Check the inode owner since the verifiers don't. */
|
||||
if (xfs_sb_version_hascrc(&cur->bc_mp->m_sb) &&
|
||||
!(cur->bc_private.b.flags & XFS_BTCUR_BPRV_INVALID_OWNER) &&
|
||||
(cur->bc_flags & XFS_BTREE_LONG_PTRS) &&
|
||||
be64_to_cpu((*blkp)->bb_u.l.bb_owner) !=
|
||||
cur->bc_private.b.ip->i_ino)
|
||||
@@ -4432,10 +4435,15 @@ xfs_btree_block_change_owner(
|
||||
|
||||
/* modify the owner */
|
||||
block = xfs_btree_get_block(cur, level, &bp);
|
||||
if (cur->bc_flags & XFS_BTREE_LONG_PTRS)
|
||||
if (cur->bc_flags & XFS_BTREE_LONG_PTRS) {
|
||||
if (block->bb_u.l.bb_owner == cpu_to_be64(bbcoi->new_owner))
|
||||
return 0;
|
||||
block->bb_u.l.bb_owner = cpu_to_be64(bbcoi->new_owner);
|
||||
else
|
||||
} else {
|
||||
if (block->bb_u.s.bb_owner == cpu_to_be32(bbcoi->new_owner))
|
||||
return 0;
|
||||
block->bb_u.s.bb_owner = cpu_to_be32(bbcoi->new_owner);
|
||||
}
|
||||
|
||||
/*
|
||||
* If the block is a root block hosted in an inode, we might not have a
|
||||
@@ -4444,16 +4452,19 @@ xfs_btree_block_change_owner(
|
||||
* block is formatted into the on-disk inode fork. We still change it,
|
||||
* though, so everything is consistent in memory.
|
||||
*/
|
||||
if (bp) {
|
||||
if (cur->bc_tp) {
|
||||
xfs_trans_ordered_buf(cur->bc_tp, bp);
|
||||
xfs_btree_log_block(cur, bp, XFS_BB_OWNER);
|
||||
} else {
|
||||
xfs_buf_delwri_queue(bp, bbcoi->buffer_list);
|
||||
}
|
||||
} else {
|
||||
if (!bp) {
|
||||
ASSERT(cur->bc_flags & XFS_BTREE_ROOT_IN_INODE);
|
||||
ASSERT(level == cur->bc_nlevels - 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cur->bc_tp) {
|
||||
if (!xfs_trans_ordered_buf(cur->bc_tp, bp)) {
|
||||
xfs_btree_log_block(cur, bp, XFS_BB_OWNER);
|
||||
return -EAGAIN;
|
||||
}
|
||||
} else {
|
||||
xfs_buf_delwri_queue(bp, bbcoi->buffer_list);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -268,7 +268,8 @@ typedef struct xfs_btree_cur
|
||||
short forksize; /* fork's inode space */
|
||||
char whichfork; /* data or attr fork */
|
||||
char flags; /* flags */
|
||||
#define XFS_BTCUR_BPRV_WASDEL 1 /* was delayed */
|
||||
#define XFS_BTCUR_BPRV_WASDEL (1<<0) /* was delayed */
|
||||
#define XFS_BTCUR_BPRV_INVALID_OWNER (1<<1) /* for ext swap */
|
||||
} b;
|
||||
} bc_private; /* per-btree type data */
|
||||
} xfs_btree_cur_t;
|
||||
|
||||
@@ -263,7 +263,7 @@ xfs_da3_node_read(
|
||||
|
||||
err = xfs_da_read_buf(tp, dp, bno, mappedbno, bpp,
|
||||
which_fork, &xfs_da3_node_buf_ops);
|
||||
if (!err && tp) {
|
||||
if (!err && tp && *bpp) {
|
||||
struct xfs_da_blkinfo *info = (*bpp)->b_addr;
|
||||
int type;
|
||||
|
||||
|
||||
@@ -139,7 +139,7 @@ xfs_dir3_block_read(
|
||||
|
||||
err = xfs_da_read_buf(tp, dp, mp->m_dir_geo->datablk, -1, bpp,
|
||||
XFS_DATA_FORK, &xfs_dir3_block_buf_ops);
|
||||
if (!err && tp)
|
||||
if (!err && tp && *bpp)
|
||||
xfs_trans_buf_set_type(tp, *bpp, XFS_BLFT_DIR_BLOCK_BUF);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -268,7 +268,7 @@ xfs_dir3_leaf_read(
|
||||
|
||||
err = xfs_da_read_buf(tp, dp, fbno, mappedbno, bpp,
|
||||
XFS_DATA_FORK, &xfs_dir3_leaf1_buf_ops);
|
||||
if (!err && tp)
|
||||
if (!err && tp && *bpp)
|
||||
xfs_trans_buf_set_type(tp, *bpp, XFS_BLFT_DIR_LEAF1_BUF);
|
||||
return err;
|
||||
}
|
||||
@@ -285,7 +285,7 @@ xfs_dir3_leafn_read(
|
||||
|
||||
err = xfs_da_read_buf(tp, dp, fbno, mappedbno, bpp,
|
||||
XFS_DATA_FORK, &xfs_dir3_leafn_buf_ops);
|
||||
if (!err && tp)
|
||||
if (!err && tp && *bpp)
|
||||
xfs_trans_buf_set_type(tp, *bpp, XFS_BLFT_DIR_LEAFN_BUF);
|
||||
return err;
|
||||
}
|
||||
|
||||
+28
-31
@@ -368,8 +368,6 @@ xfs_ialloc_inode_init(
|
||||
* transaction and pin the log appropriately.
|
||||
*/
|
||||
xfs_trans_ordered_buf(tp, fbuf);
|
||||
xfs_trans_log_buf(tp, fbuf, 0,
|
||||
BBTOB(fbuf->b_length) - 1);
|
||||
}
|
||||
} else {
|
||||
fbuf->b_flags |= XBF_DONE;
|
||||
@@ -1123,6 +1121,7 @@ xfs_dialloc_ag_inobt(
|
||||
int error;
|
||||
int offset;
|
||||
int i, j;
|
||||
int searchdistance = 10;
|
||||
|
||||
pag = xfs_perag_get(mp, agno);
|
||||
|
||||
@@ -1149,7 +1148,6 @@ xfs_dialloc_ag_inobt(
|
||||
if (pagno == agno) {
|
||||
int doneleft; /* done, to the left */
|
||||
int doneright; /* done, to the right */
|
||||
int searchdistance = 10;
|
||||
|
||||
error = xfs_inobt_lookup(cur, pagino, XFS_LOOKUP_LE, &i);
|
||||
if (error)
|
||||
@@ -1210,21 +1208,9 @@ xfs_dialloc_ag_inobt(
|
||||
/*
|
||||
* Loop until we find an inode chunk with a free inode.
|
||||
*/
|
||||
while (!doneleft || !doneright) {
|
||||
while (--searchdistance > 0 && (!doneleft || !doneright)) {
|
||||
int useleft; /* using left inode chunk this time */
|
||||
|
||||
if (!--searchdistance) {
|
||||
/*
|
||||
* Not in range - save last search
|
||||
* location and allocate a new inode
|
||||
*/
|
||||
xfs_btree_del_cursor(tcur, XFS_BTREE_NOERROR);
|
||||
pag->pagl_leftrec = trec.ir_startino;
|
||||
pag->pagl_rightrec = rec.ir_startino;
|
||||
pag->pagl_pagino = pagino;
|
||||
goto newino;
|
||||
}
|
||||
|
||||
/* figure out the closer block if both are valid. */
|
||||
if (!doneleft && !doneright) {
|
||||
useleft = pagino -
|
||||
@@ -1236,13 +1222,13 @@ xfs_dialloc_ag_inobt(
|
||||
|
||||
/* free inodes to the left? */
|
||||
if (useleft && trec.ir_freecount) {
|
||||
rec = trec;
|
||||
xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
|
||||
cur = tcur;
|
||||
|
||||
pag->pagl_leftrec = trec.ir_startino;
|
||||
pag->pagl_rightrec = rec.ir_startino;
|
||||
pag->pagl_pagino = pagino;
|
||||
rec = trec;
|
||||
goto alloc_inode;
|
||||
}
|
||||
|
||||
@@ -1268,26 +1254,37 @@ xfs_dialloc_ag_inobt(
|
||||
goto error1;
|
||||
}
|
||||
|
||||
/*
|
||||
* We've reached the end of the btree. because
|
||||
* we are only searching a small chunk of the
|
||||
* btree each search, there is obviously free
|
||||
* inodes closer to the parent inode than we
|
||||
* are now. restart the search again.
|
||||
*/
|
||||
pag->pagl_pagino = NULLAGINO;
|
||||
pag->pagl_leftrec = NULLAGINO;
|
||||
pag->pagl_rightrec = NULLAGINO;
|
||||
xfs_btree_del_cursor(tcur, XFS_BTREE_NOERROR);
|
||||
xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
|
||||
goto restart_pagno;
|
||||
if (searchdistance <= 0) {
|
||||
/*
|
||||
* Not in range - save last search
|
||||
* location and allocate a new inode
|
||||
*/
|
||||
xfs_btree_del_cursor(tcur, XFS_BTREE_NOERROR);
|
||||
pag->pagl_leftrec = trec.ir_startino;
|
||||
pag->pagl_rightrec = rec.ir_startino;
|
||||
pag->pagl_pagino = pagino;
|
||||
|
||||
} else {
|
||||
/*
|
||||
* We've reached the end of the btree. because
|
||||
* we are only searching a small chunk of the
|
||||
* btree each search, there is obviously free
|
||||
* inodes closer to the parent inode than we
|
||||
* are now. restart the search again.
|
||||
*/
|
||||
pag->pagl_pagino = NULLAGINO;
|
||||
pag->pagl_leftrec = NULLAGINO;
|
||||
pag->pagl_rightrec = NULLAGINO;
|
||||
xfs_btree_del_cursor(tcur, XFS_BTREE_NOERROR);
|
||||
xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
|
||||
goto restart_pagno;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* In a different AG from the parent.
|
||||
* See if the most recently allocated block has any free.
|
||||
*/
|
||||
newino:
|
||||
if (agi->agi_newino != cpu_to_be32(NULLAGINO)) {
|
||||
error = xfs_inobt_lookup(cur, be32_to_cpu(agi->agi_newino),
|
||||
XFS_LOOKUP_EQ, &i);
|
||||
|
||||
@@ -1539,14 +1539,11 @@ xfs_iext_realloc_indirect(
|
||||
xfs_ifork_t *ifp, /* inode fork pointer */
|
||||
int new_size) /* new indirection array size */
|
||||
{
|
||||
int nlists; /* number of irec's (ex lists) */
|
||||
int size; /* current indirection array size */
|
||||
|
||||
ASSERT(ifp->if_flags & XFS_IFEXTIREC);
|
||||
nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
|
||||
size = nlists * sizeof(xfs_ext_irec_t);
|
||||
ASSERT(ifp->if_real_bytes);
|
||||
ASSERT((new_size >= 0) && (new_size != size));
|
||||
ASSERT((new_size >= 0) &&
|
||||
(new_size != ((ifp->if_real_bytes / XFS_IEXT_BUFSZ) *
|
||||
sizeof(xfs_ext_irec_t))));
|
||||
if (new_size == 0) {
|
||||
xfs_iext_destroy(ifp);
|
||||
} else {
|
||||
|
||||
@@ -784,14 +784,6 @@ xfs_refcount_merge_extents(
|
||||
}
|
||||
|
||||
/*
|
||||
* While we're adjusting the refcounts records of an extent, we have
|
||||
* to keep an eye on the number of extents we're dirtying -- run too
|
||||
* many in a single transaction and we'll exceed the transaction's
|
||||
* reservation and crash the fs. Each record adds 12 bytes to the
|
||||
* log (plus any key updates) so we'll conservatively assume 24 bytes
|
||||
* per record. We must also leave space for btree splits on both ends
|
||||
* of the range and space for the CUD and a new CUI.
|
||||
*
|
||||
* XXX: This is a pretty hand-wavy estimate. The penalty for guessing
|
||||
* true incorrectly is a shutdown FS; the penalty for guessing false
|
||||
* incorrectly is more transaction rolls than might be necessary.
|
||||
@@ -822,7 +814,7 @@ xfs_refcount_still_have_space(
|
||||
else if (overhead > cur->bc_tp->t_log_res)
|
||||
return false;
|
||||
return cur->bc_tp->t_log_res - overhead >
|
||||
cur->bc_private.a.priv.refc.nr_ops * 32;
|
||||
cur->bc_private.a.priv.refc.nr_ops * XFS_REFCOUNT_ITEM_OVERHEAD;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1648,6 +1640,10 @@ xfs_refcount_recover_cow_leftovers(
|
||||
error = xfs_alloc_read_agf(mp, tp, agno, 0, &agbp);
|
||||
if (error)
|
||||
goto out_trans;
|
||||
if (!agbp) {
|
||||
error = -ENOMEM;
|
||||
goto out_trans;
|
||||
}
|
||||
cur = xfs_refcountbt_init_cursor(mp, tp, agbp, agno, NULL);
|
||||
|
||||
/* Find all the leftover CoW staging extents. */
|
||||
|
||||
@@ -67,4 +67,20 @@ extern int xfs_refcount_free_cow_extent(struct xfs_mount *mp,
|
||||
extern int xfs_refcount_recover_cow_leftovers(struct xfs_mount *mp,
|
||||
xfs_agnumber_t agno);
|
||||
|
||||
/*
|
||||
* While we're adjusting the refcounts records of an extent, we have
|
||||
* to keep an eye on the number of extents we're dirtying -- run too
|
||||
* many in a single transaction and we'll exceed the transaction's
|
||||
* reservation and crash the fs. Each record adds 12 bytes to the
|
||||
* log (plus any key updates) so we'll conservatively assume 32 bytes
|
||||
* per record. We must also leave space for btree splits on both ends
|
||||
* of the range and space for the CUD and a new CUI.
|
||||
*/
|
||||
#define XFS_REFCOUNT_ITEM_OVERHEAD 32
|
||||
|
||||
static inline xfs_fileoff_t xfs_refcount_max_unmap(int log_res)
|
||||
{
|
||||
return (log_res * 3 / 4) / XFS_REFCOUNT_ITEM_OVERHEAD;
|
||||
}
|
||||
|
||||
#endif /* __XFS_REFCOUNT_H__ */
|
||||
|
||||
+53
-26
@@ -90,11 +90,11 @@ xfs_find_bdev_for_inode(
|
||||
* associated buffer_heads, paying attention to the start and end offsets that
|
||||
* we need to process on the page.
|
||||
*
|
||||
* Landmine Warning: bh->b_end_io() will call end_page_writeback() on the last
|
||||
* buffer in the IO. Once it does this, it is unsafe to access the bufferhead or
|
||||
* the page at all, as we may be racing with memory reclaim and it can free both
|
||||
* the bufferhead chain and the page as it will see the page as clean and
|
||||
* unused.
|
||||
* Note that we open code the action in end_buffer_async_write here so that we
|
||||
* only have to iterate over the buffers attached to the page once. This is not
|
||||
* only more efficient, but also ensures that we only calls end_page_writeback
|
||||
* at the end of the iteration, and thus avoids the pitfall of having the page
|
||||
* and buffers potentially freed after every call to end_buffer_async_write.
|
||||
*/
|
||||
static void
|
||||
xfs_finish_page_writeback(
|
||||
@@ -102,29 +102,45 @@ xfs_finish_page_writeback(
|
||||
struct bio_vec *bvec,
|
||||
int error)
|
||||
{
|
||||
unsigned int end = bvec->bv_offset + bvec->bv_len - 1;
|
||||
struct buffer_head *head, *bh, *next;
|
||||
struct buffer_head *head = page_buffers(bvec->bv_page), *bh = head;
|
||||
bool busy = false;
|
||||
unsigned int off = 0;
|
||||
unsigned int bsize;
|
||||
unsigned long flags;
|
||||
|
||||
ASSERT(bvec->bv_offset < PAGE_SIZE);
|
||||
ASSERT((bvec->bv_offset & (i_blocksize(inode) - 1)) == 0);
|
||||
ASSERT(end < PAGE_SIZE);
|
||||
ASSERT(bvec->bv_offset + bvec->bv_len <= PAGE_SIZE);
|
||||
ASSERT((bvec->bv_len & (i_blocksize(inode) - 1)) == 0);
|
||||
|
||||
bh = head = page_buffers(bvec->bv_page);
|
||||
|
||||
bsize = bh->b_size;
|
||||
local_irq_save(flags);
|
||||
bit_spin_lock(BH_Uptodate_Lock, &head->b_state);
|
||||
do {
|
||||
if (off > end)
|
||||
break;
|
||||
next = bh->b_this_page;
|
||||
if (off < bvec->bv_offset)
|
||||
goto next_bh;
|
||||
bh->b_end_io(bh, !error);
|
||||
next_bh:
|
||||
off += bsize;
|
||||
} while ((bh = next) != head);
|
||||
if (off >= bvec->bv_offset &&
|
||||
off < bvec->bv_offset + bvec->bv_len) {
|
||||
ASSERT(buffer_async_write(bh));
|
||||
ASSERT(bh->b_end_io == NULL);
|
||||
|
||||
if (error) {
|
||||
mapping_set_error(bvec->bv_page->mapping, -EIO);
|
||||
set_buffer_write_io_error(bh);
|
||||
clear_buffer_uptodate(bh);
|
||||
SetPageError(bvec->bv_page);
|
||||
} else {
|
||||
set_buffer_uptodate(bh);
|
||||
}
|
||||
clear_buffer_async_write(bh);
|
||||
unlock_buffer(bh);
|
||||
} else if (buffer_async_write(bh)) {
|
||||
ASSERT(buffer_locked(bh));
|
||||
busy = true;
|
||||
}
|
||||
off += bh->b_size;
|
||||
} while ((bh = bh->b_this_page) != head);
|
||||
bit_spin_unlock(BH_Uptodate_Lock, &head->b_state);
|
||||
local_irq_restore(flags);
|
||||
|
||||
if (!busy)
|
||||
end_page_writeback(bvec->bv_page);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -138,8 +154,10 @@ xfs_destroy_ioend(
|
||||
int error)
|
||||
{
|
||||
struct inode *inode = ioend->io_inode;
|
||||
struct bio *last = ioend->io_bio;
|
||||
struct bio *bio, *next;
|
||||
struct bio *bio = &ioend->io_inline_bio;
|
||||
struct bio *last = ioend->io_bio, *next;
|
||||
u64 start = bio->bi_iter.bi_sector;
|
||||
bool quiet = bio_flagged(bio, BIO_QUIET);
|
||||
|
||||
for (bio = &ioend->io_inline_bio; bio; bio = next) {
|
||||
struct bio_vec *bvec;
|
||||
@@ -160,6 +178,11 @@ xfs_destroy_ioend(
|
||||
|
||||
bio_put(bio);
|
||||
}
|
||||
|
||||
if (unlikely(error && !quiet)) {
|
||||
xfs_err_ratelimited(XFS_I(inode)->i_mount,
|
||||
"writeback error on sector %llu", start);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -427,7 +450,8 @@ xfs_start_buffer_writeback(
|
||||
ASSERT(!buffer_delay(bh));
|
||||
ASSERT(!buffer_unwritten(bh));
|
||||
|
||||
mark_buffer_async_write(bh);
|
||||
bh->b_end_io = NULL;
|
||||
set_buffer_async_write(bh);
|
||||
set_buffer_uptodate(bh);
|
||||
clear_buffer_dirty(bh);
|
||||
}
|
||||
@@ -1566,9 +1590,12 @@ xfs_vm_bmap(
|
||||
* The swap code (ab-)uses ->bmap to get a block mapping and then
|
||||
* bypasseѕ the file system for actual I/O. We really can't allow
|
||||
* that on reflinks inodes, so we have to skip out here. And yes,
|
||||
* 0 is the magic code for a bmap error..
|
||||
* 0 is the magic code for a bmap error.
|
||||
*
|
||||
* Since we don't pass back blockdev info, we can't return bmap
|
||||
* information for rt files either.
|
||||
*/
|
||||
if (xfs_is_reflink_inode(ip)) {
|
||||
if (xfs_is_reflink_inode(ip) || XFS_IS_REALTIME_INODE(ip)) {
|
||||
xfs_iunlock(ip, XFS_IOLOCK_SHARED);
|
||||
return 0;
|
||||
}
|
||||
|
||||
+15
-2
@@ -395,6 +395,7 @@ xfs_bui_recover(
|
||||
struct xfs_map_extent *bmap;
|
||||
xfs_fsblock_t startblock_fsb;
|
||||
xfs_fsblock_t inode_fsb;
|
||||
xfs_filblks_t count;
|
||||
bool op_ok;
|
||||
struct xfs_bud_log_item *budp;
|
||||
enum xfs_bmap_intent_type type;
|
||||
@@ -403,6 +404,7 @@ xfs_bui_recover(
|
||||
struct xfs_trans *tp;
|
||||
struct xfs_inode *ip = NULL;
|
||||
struct xfs_defer_ops dfops;
|
||||
struct xfs_bmbt_irec irec;
|
||||
xfs_fsblock_t firstfsb;
|
||||
|
||||
ASSERT(!test_bit(XFS_BUI_RECOVERED, &buip->bui_flags));
|
||||
@@ -480,13 +482,24 @@ xfs_bui_recover(
|
||||
}
|
||||
xfs_trans_ijoin(tp, ip, 0);
|
||||
|
||||
count = bmap->me_len;
|
||||
error = xfs_trans_log_finish_bmap_update(tp, budp, &dfops, type,
|
||||
ip, whichfork, bmap->me_startoff,
|
||||
bmap->me_startblock, bmap->me_len,
|
||||
state);
|
||||
bmap->me_startblock, &count, state);
|
||||
if (error)
|
||||
goto err_dfops;
|
||||
|
||||
if (count > 0) {
|
||||
ASSERT(type == XFS_BMAP_UNMAP);
|
||||
irec.br_startblock = bmap->me_startblock;
|
||||
irec.br_blockcount = count;
|
||||
irec.br_startoff = bmap->me_startoff;
|
||||
irec.br_state = state;
|
||||
error = xfs_bmap_unmap_extent(tp->t_mountp, &dfops, ip, &irec);
|
||||
if (error)
|
||||
goto err_dfops;
|
||||
}
|
||||
|
||||
/* Finish transaction, free inodes. */
|
||||
error = xfs_defer_finish(&tp, &dfops, NULL);
|
||||
if (error)
|
||||
|
||||
+70
-25
@@ -1825,29 +1825,18 @@ xfs_swap_extent_forks(
|
||||
}
|
||||
|
||||
/*
|
||||
* Before we've swapped the forks, lets set the owners of the forks
|
||||
* appropriately. We have to do this as we are demand paging the btree
|
||||
* buffers, and so the validation done on read will expect the owner
|
||||
* field to be correctly set. Once we change the owners, we can swap the
|
||||
* inode forks.
|
||||
* Btree format (v3) inodes have the inode number stamped in the bmbt
|
||||
* block headers. We can't start changing the bmbt blocks until the
|
||||
* inode owner change is logged so recovery does the right thing in the
|
||||
* event of a crash. Set the owner change log flags now and leave the
|
||||
* bmbt scan as the last step.
|
||||
*/
|
||||
if (ip->i_d.di_version == 3 &&
|
||||
ip->i_d.di_format == XFS_DINODE_FMT_BTREE) {
|
||||
ip->i_d.di_format == XFS_DINODE_FMT_BTREE)
|
||||
(*target_log_flags) |= XFS_ILOG_DOWNER;
|
||||
error = xfs_bmbt_change_owner(tp, ip, XFS_DATA_FORK,
|
||||
tip->i_ino, NULL);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
if (tip->i_d.di_version == 3 &&
|
||||
tip->i_d.di_format == XFS_DINODE_FMT_BTREE) {
|
||||
tip->i_d.di_format == XFS_DINODE_FMT_BTREE)
|
||||
(*src_log_flags) |= XFS_ILOG_DOWNER;
|
||||
error = xfs_bmbt_change_owner(tp, tip, XFS_DATA_FORK,
|
||||
ip->i_ino, NULL);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Swap the data forks of the inodes
|
||||
@@ -1925,6 +1914,48 @@ xfs_swap_extent_forks(
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fix up the owners of the bmbt blocks to refer to the current inode. The
|
||||
* change owner scan attempts to order all modified buffers in the current
|
||||
* transaction. In the event of ordered buffer failure, the offending buffer is
|
||||
* physically logged as a fallback and the scan returns -EAGAIN. We must roll
|
||||
* the transaction in this case to replenish the fallback log reservation and
|
||||
* restart the scan. This process repeats until the scan completes.
|
||||
*/
|
||||
static int
|
||||
xfs_swap_change_owner(
|
||||
struct xfs_trans **tpp,
|
||||
struct xfs_inode *ip,
|
||||
struct xfs_inode *tmpip)
|
||||
{
|
||||
int error;
|
||||
struct xfs_trans *tp = *tpp;
|
||||
|
||||
do {
|
||||
error = xfs_bmbt_change_owner(tp, ip, XFS_DATA_FORK, ip->i_ino,
|
||||
NULL);
|
||||
/* success or fatal error */
|
||||
if (error != -EAGAIN)
|
||||
break;
|
||||
|
||||
error = xfs_trans_roll(tpp, NULL);
|
||||
if (error)
|
||||
break;
|
||||
tp = *tpp;
|
||||
|
||||
/*
|
||||
* Redirty both inodes so they can relog and keep the log tail
|
||||
* moving forward.
|
||||
*/
|
||||
xfs_trans_ijoin(tp, ip, 0);
|
||||
xfs_trans_ijoin(tp, tmpip, 0);
|
||||
xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
|
||||
xfs_trans_log_inode(tp, tmpip, XFS_ILOG_CORE);
|
||||
} while (true);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int
|
||||
xfs_swap_extents(
|
||||
struct xfs_inode *ip, /* target inode */
|
||||
@@ -1938,8 +1969,8 @@ xfs_swap_extents(
|
||||
int error = 0;
|
||||
int lock_flags;
|
||||
struct xfs_ifork *cowfp;
|
||||
__uint64_t f;
|
||||
int resblks;
|
||||
uint64_t f;
|
||||
int resblks = 0;
|
||||
|
||||
/*
|
||||
* Lock the inodes against other IO, page faults and truncate to
|
||||
@@ -1987,11 +2018,8 @@ xfs_swap_extents(
|
||||
XFS_SWAP_RMAP_SPACE_RES(mp,
|
||||
XFS_IFORK_NEXTENTS(tip, XFS_DATA_FORK),
|
||||
XFS_DATA_FORK);
|
||||
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, resblks,
|
||||
0, 0, &tp);
|
||||
} else
|
||||
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_ichange, 0,
|
||||
0, 0, &tp);
|
||||
}
|
||||
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, resblks, 0, 0, &tp);
|
||||
if (error)
|
||||
goto out_unlock;
|
||||
|
||||
@@ -2076,6 +2104,23 @@ xfs_swap_extents(
|
||||
xfs_trans_log_inode(tp, ip, src_log_flags);
|
||||
xfs_trans_log_inode(tp, tip, target_log_flags);
|
||||
|
||||
/*
|
||||
* The extent forks have been swapped, but crc=1,rmapbt=0 filesystems
|
||||
* have inode number owner values in the bmbt blocks that still refer to
|
||||
* the old inode. Scan each bmbt to fix up the owner values with the
|
||||
* inode number of the current inode.
|
||||
*/
|
||||
if (src_log_flags & XFS_ILOG_DOWNER) {
|
||||
error = xfs_swap_change_owner(&tp, ip, tip);
|
||||
if (error)
|
||||
goto out_trans_cancel;
|
||||
}
|
||||
if (target_log_flags & XFS_ILOG_DOWNER) {
|
||||
error = xfs_swap_change_owner(&tp, tip, ip);
|
||||
if (error)
|
||||
goto out_trans_cancel;
|
||||
}
|
||||
|
||||
/*
|
||||
* If this is a synchronous mount, make sure that the
|
||||
* transaction goes to disk before returning to the user.
|
||||
|
||||
+61
-1
@@ -116,7 +116,7 @@ static inline void
|
||||
__xfs_buf_ioacct_dec(
|
||||
struct xfs_buf *bp)
|
||||
{
|
||||
ASSERT(spin_is_locked(&bp->b_lock));
|
||||
lockdep_assert_held(&bp->b_lock);
|
||||
|
||||
if (bp->b_state & XFS_BSTATE_IN_FLIGHT) {
|
||||
bp->b_state &= ~XFS_BSTATE_IN_FLIGHT;
|
||||
@@ -2022,6 +2022,66 @@ xfs_buf_delwri_submit(
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Push a single buffer on a delwri queue.
|
||||
*
|
||||
* The purpose of this function is to submit a single buffer of a delwri queue
|
||||
* and return with the buffer still on the original queue. The waiting delwri
|
||||
* buffer submission infrastructure guarantees transfer of the delwri queue
|
||||
* buffer reference to a temporary wait list. We reuse this infrastructure to
|
||||
* transfer the buffer back to the original queue.
|
||||
*
|
||||
* Note the buffer transitions from the queued state, to the submitted and wait
|
||||
* listed state and back to the queued state during this call. The buffer
|
||||
* locking and queue management logic between _delwri_pushbuf() and
|
||||
* _delwri_queue() guarantee that the buffer cannot be queued to another list
|
||||
* before returning.
|
||||
*/
|
||||
int
|
||||
xfs_buf_delwri_pushbuf(
|
||||
struct xfs_buf *bp,
|
||||
struct list_head *buffer_list)
|
||||
{
|
||||
LIST_HEAD (submit_list);
|
||||
int error;
|
||||
|
||||
ASSERT(bp->b_flags & _XBF_DELWRI_Q);
|
||||
|
||||
trace_xfs_buf_delwri_pushbuf(bp, _RET_IP_);
|
||||
|
||||
/*
|
||||
* Isolate the buffer to a new local list so we can submit it for I/O
|
||||
* independently from the rest of the original list.
|
||||
*/
|
||||
xfs_buf_lock(bp);
|
||||
list_move(&bp->b_list, &submit_list);
|
||||
xfs_buf_unlock(bp);
|
||||
|
||||
/*
|
||||
* Delwri submission clears the DELWRI_Q buffer flag and returns with
|
||||
* the buffer on the wait list with an associated reference. Rather than
|
||||
* bounce the buffer from a local wait list back to the original list
|
||||
* after I/O completion, reuse the original list as the wait list.
|
||||
*/
|
||||
xfs_buf_delwri_submit_buffers(&submit_list, buffer_list);
|
||||
|
||||
/*
|
||||
* The buffer is now under I/O and wait listed as during typical delwri
|
||||
* submission. Lock the buffer to wait for I/O completion. Rather than
|
||||
* remove the buffer from the wait list and release the reference, we
|
||||
* want to return with the buffer queued to the original list. The
|
||||
* buffer already sits on the original list with a wait list reference,
|
||||
* however. If we let the queue inherit that wait list reference, all we
|
||||
* need to do is reset the DELWRI_Q flag.
|
||||
*/
|
||||
xfs_buf_lock(bp);
|
||||
error = bp->b_error;
|
||||
bp->b_flags |= _XBF_DELWRI_Q;
|
||||
xfs_buf_unlock(bp);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int __init
|
||||
xfs_buf_init(void)
|
||||
{
|
||||
|
||||
@@ -333,6 +333,7 @@ extern void xfs_buf_delwri_cancel(struct list_head *);
|
||||
extern bool xfs_buf_delwri_queue(struct xfs_buf *, struct list_head *);
|
||||
extern int xfs_buf_delwri_submit(struct list_head *);
|
||||
extern int xfs_buf_delwri_submit_nowait(struct list_head *);
|
||||
extern int xfs_buf_delwri_pushbuf(struct xfs_buf *, struct list_head *);
|
||||
|
||||
/* Buffer Daemon Setup Routines */
|
||||
extern int xfs_buf_init(void);
|
||||
|
||||
+101
-55
@@ -29,6 +29,7 @@
|
||||
#include "xfs_error.h"
|
||||
#include "xfs_trace.h"
|
||||
#include "xfs_log.h"
|
||||
#include "xfs_inode.h"
|
||||
|
||||
|
||||
kmem_zone_t *xfs_buf_item_zone;
|
||||
@@ -322,6 +323,8 @@ xfs_buf_item_format(
|
||||
ASSERT((bip->bli_flags & XFS_BLI_STALE) ||
|
||||
(xfs_blft_from_flags(&bip->__bli_format) > XFS_BLFT_UNKNOWN_BUF
|
||||
&& xfs_blft_from_flags(&bip->__bli_format) < XFS_BLFT_MAX_BUF));
|
||||
ASSERT(!(bip->bli_flags & XFS_BLI_ORDERED) ||
|
||||
(bip->bli_flags & XFS_BLI_STALE));
|
||||
|
||||
|
||||
/*
|
||||
@@ -346,16 +349,6 @@ xfs_buf_item_format(
|
||||
bip->bli_flags &= ~XFS_BLI_INODE_BUF;
|
||||
}
|
||||
|
||||
if ((bip->bli_flags & (XFS_BLI_ORDERED|XFS_BLI_STALE)) ==
|
||||
XFS_BLI_ORDERED) {
|
||||
/*
|
||||
* The buffer has been logged just to order it. It is not being
|
||||
* included in the transaction commit, so don't format it.
|
||||
*/
|
||||
trace_xfs_buf_item_format_ordered(bip);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < bip->bli_format_count; i++) {
|
||||
xfs_buf_item_format_segment(bip, lv, &vecp, offset,
|
||||
&bip->bli_formats[i]);
|
||||
@@ -574,26 +567,20 @@ xfs_buf_item_unlock(
|
||||
{
|
||||
struct xfs_buf_log_item *bip = BUF_ITEM(lip);
|
||||
struct xfs_buf *bp = bip->bli_buf;
|
||||
bool clean;
|
||||
bool aborted;
|
||||
int flags;
|
||||
bool aborted = !!(lip->li_flags & XFS_LI_ABORTED);
|
||||
bool hold = !!(bip->bli_flags & XFS_BLI_HOLD);
|
||||
bool dirty = !!(bip->bli_flags & XFS_BLI_DIRTY);
|
||||
#if defined(DEBUG) || defined(XFS_WARN)
|
||||
bool ordered = !!(bip->bli_flags & XFS_BLI_ORDERED);
|
||||
#endif
|
||||
|
||||
/* Clear the buffer's association with this transaction. */
|
||||
bp->b_transp = NULL;
|
||||
|
||||
/*
|
||||
* If this is a transaction abort, don't return early. Instead, allow
|
||||
* the brelse to happen. Normally it would be done for stale
|
||||
* (cancelled) buffers at unpin time, but we'll never go through the
|
||||
* pin/unpin cycle if we abort inside commit.
|
||||
* The per-transaction state has been copied above so clear it from the
|
||||
* bli.
|
||||
*/
|
||||
aborted = (lip->li_flags & XFS_LI_ABORTED) ? true : false;
|
||||
/*
|
||||
* Before possibly freeing the buf item, copy the per-transaction state
|
||||
* so we can reference it safely later after clearing it from the
|
||||
* buffer log item.
|
||||
*/
|
||||
flags = bip->bli_flags;
|
||||
bip->bli_flags &= ~(XFS_BLI_LOGGED | XFS_BLI_HOLD | XFS_BLI_ORDERED);
|
||||
|
||||
/*
|
||||
@@ -601,7 +588,7 @@ xfs_buf_item_unlock(
|
||||
* unlock the buffer and free the buf item when the buffer is unpinned
|
||||
* for the last time.
|
||||
*/
|
||||
if (flags & XFS_BLI_STALE) {
|
||||
if (bip->bli_flags & XFS_BLI_STALE) {
|
||||
trace_xfs_buf_item_unlock_stale(bip);
|
||||
ASSERT(bip->__bli_format.blf_flags & XFS_BLF_CANCEL);
|
||||
if (!aborted) {
|
||||
@@ -619,40 +606,34 @@ xfs_buf_item_unlock(
|
||||
* regardless of whether it is dirty or not. A dirty abort implies a
|
||||
* shutdown, anyway.
|
||||
*
|
||||
* Ordered buffers are dirty but may have no recorded changes, so ensure
|
||||
* we only release clean items here.
|
||||
* The bli dirty state should match whether the blf has logged segments
|
||||
* except for ordered buffers, where only the bli should be dirty.
|
||||
*/
|
||||
clean = (flags & XFS_BLI_DIRTY) ? false : true;
|
||||
if (clean) {
|
||||
int i;
|
||||
for (i = 0; i < bip->bli_format_count; i++) {
|
||||
if (!xfs_bitmap_empty(bip->bli_formats[i].blf_data_map,
|
||||
bip->bli_formats[i].blf_map_size)) {
|
||||
clean = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
ASSERT((!ordered && dirty == xfs_buf_item_dirty_format(bip)) ||
|
||||
(ordered && dirty && !xfs_buf_item_dirty_format(bip)));
|
||||
|
||||
/*
|
||||
* Clean buffers, by definition, cannot be in the AIL. However, aborted
|
||||
* buffers may be dirty and hence in the AIL. Therefore if we are
|
||||
* aborting a buffer and we've just taken the last refernce away, we
|
||||
* have to check if it is in the AIL before freeing it. We need to free
|
||||
* it in this case, because an aborted transaction has already shut the
|
||||
* filesystem down and this is the last chance we will have to do so.
|
||||
* buffers may be in the AIL regardless of dirty state. An aborted
|
||||
* transaction that invalidates a buffer already in the AIL may have
|
||||
* marked it stale and cleared the dirty state, for example.
|
||||
*
|
||||
* Therefore if we are aborting a buffer and we've just taken the last
|
||||
* reference away, we have to check if it is in the AIL before freeing
|
||||
* it. We need to free it in this case, because an aborted transaction
|
||||
* has already shut the filesystem down and this is the last chance we
|
||||
* will have to do so.
|
||||
*/
|
||||
if (atomic_dec_and_test(&bip->bli_refcount)) {
|
||||
if (clean)
|
||||
xfs_buf_item_relse(bp);
|
||||
else if (aborted) {
|
||||
if (aborted) {
|
||||
ASSERT(XFS_FORCED_SHUTDOWN(lip->li_mountp));
|
||||
xfs_trans_ail_remove(lip, SHUTDOWN_LOG_IO_ERROR);
|
||||
xfs_buf_item_relse(bp);
|
||||
}
|
||||
} else if (!dirty)
|
||||
xfs_buf_item_relse(bp);
|
||||
}
|
||||
|
||||
if (!(flags & XFS_BLI_HOLD))
|
||||
if (!hold)
|
||||
xfs_buf_relse(bp);
|
||||
}
|
||||
|
||||
@@ -942,14 +923,22 @@ xfs_buf_item_log(
|
||||
|
||||
|
||||
/*
|
||||
* Return 1 if the buffer has been logged or ordered in a transaction (at any
|
||||
* point, not just the current transaction) and 0 if not.
|
||||
* Return true if the buffer has any ranges logged/dirtied by a transaction,
|
||||
* false otherwise.
|
||||
*/
|
||||
uint
|
||||
xfs_buf_item_dirty(
|
||||
xfs_buf_log_item_t *bip)
|
||||
bool
|
||||
xfs_buf_item_dirty_format(
|
||||
struct xfs_buf_log_item *bip)
|
||||
{
|
||||
return (bip->bli_flags & XFS_BLI_DIRTY);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < bip->bli_format_count; i++) {
|
||||
if (!xfs_bitmap_empty(bip->bli_formats[i].blf_data_map,
|
||||
bip->bli_formats[i].blf_map_size))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
STATIC void
|
||||
@@ -1051,6 +1040,31 @@ xfs_buf_do_callbacks(
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Invoke the error state callback for each log item affected by the failed I/O.
|
||||
*
|
||||
* If a metadata buffer write fails with a non-permanent error, the buffer is
|
||||
* eventually resubmitted and so the completion callbacks are not run. The error
|
||||
* state may need to be propagated to the log items attached to the buffer,
|
||||
* however, so the next AIL push of the item knows hot to handle it correctly.
|
||||
*/
|
||||
STATIC void
|
||||
xfs_buf_do_callbacks_fail(
|
||||
struct xfs_buf *bp)
|
||||
{
|
||||
struct xfs_log_item *next;
|
||||
struct xfs_log_item *lip = bp->b_fspriv;
|
||||
struct xfs_ail *ailp = lip->li_ailp;
|
||||
|
||||
spin_lock(&ailp->xa_lock);
|
||||
for (; lip; lip = next) {
|
||||
next = lip->li_bio_list;
|
||||
if (lip->li_ops->iop_error)
|
||||
lip->li_ops->iop_error(lip, bp);
|
||||
}
|
||||
spin_unlock(&ailp->xa_lock);
|
||||
}
|
||||
|
||||
static bool
|
||||
xfs_buf_iodone_callback_error(
|
||||
struct xfs_buf *bp)
|
||||
@@ -1120,7 +1134,11 @@ xfs_buf_iodone_callback_error(
|
||||
if ((mp->m_flags & XFS_MOUNT_UNMOUNTING) && mp->m_fail_unmount)
|
||||
goto permanent_error;
|
||||
|
||||
/* still a transient error, higher layers will retry */
|
||||
/*
|
||||
* Still a transient error, run IO completion failure callbacks and let
|
||||
* the higher layers retry the buffer.
|
||||
*/
|
||||
xfs_buf_do_callbacks_fail(bp);
|
||||
xfs_buf_ioerror(bp, 0);
|
||||
xfs_buf_relse(bp);
|
||||
return true;
|
||||
@@ -1201,3 +1219,31 @@ xfs_buf_iodone(
|
||||
xfs_trans_ail_delete(ailp, lip, SHUTDOWN_CORRUPT_INCORE);
|
||||
xfs_buf_item_free(BUF_ITEM(lip));
|
||||
}
|
||||
|
||||
/*
|
||||
* Requeue a failed buffer for writeback
|
||||
*
|
||||
* Return true if the buffer has been re-queued properly, false otherwise
|
||||
*/
|
||||
bool
|
||||
xfs_buf_resubmit_failed_buffers(
|
||||
struct xfs_buf *bp,
|
||||
struct xfs_log_item *lip,
|
||||
struct list_head *buffer_list)
|
||||
{
|
||||
struct xfs_log_item *next;
|
||||
|
||||
/*
|
||||
* Clear XFS_LI_FAILED flag from all items before resubmit
|
||||
*
|
||||
* XFS_LI_FAILED set/clear is protected by xa_lock, caller this
|
||||
* function already have it acquired
|
||||
*/
|
||||
for (; lip; lip = next) {
|
||||
next = lip->li_bio_list;
|
||||
xfs_clear_li_failed(lip);
|
||||
}
|
||||
|
||||
/* Add this buffer back to the delayed write list */
|
||||
return xfs_buf_delwri_queue(bp, buffer_list);
|
||||
}
|
||||
|
||||
@@ -64,12 +64,15 @@ typedef struct xfs_buf_log_item {
|
||||
int xfs_buf_item_init(struct xfs_buf *, struct xfs_mount *);
|
||||
void xfs_buf_item_relse(struct xfs_buf *);
|
||||
void xfs_buf_item_log(xfs_buf_log_item_t *, uint, uint);
|
||||
uint xfs_buf_item_dirty(xfs_buf_log_item_t *);
|
||||
bool xfs_buf_item_dirty_format(struct xfs_buf_log_item *);
|
||||
void xfs_buf_attach_iodone(struct xfs_buf *,
|
||||
void(*)(struct xfs_buf *, xfs_log_item_t *),
|
||||
xfs_log_item_t *);
|
||||
void xfs_buf_iodone_callbacks(struct xfs_buf *);
|
||||
void xfs_buf_iodone(struct xfs_buf *, struct xfs_log_item *);
|
||||
bool xfs_buf_resubmit_failed_buffers(struct xfs_buf *,
|
||||
struct xfs_log_item *,
|
||||
struct list_head *);
|
||||
|
||||
extern kmem_zone_t *xfs_buf_item_zone;
|
||||
|
||||
|
||||
+9
-30
@@ -729,6 +729,7 @@ write_retry:
|
||||
xfs_rw_iunlock(ip, iolock);
|
||||
eofb.eof_flags = XFS_EOF_FLAGS_SYNC;
|
||||
xfs_icache_free_eofblocks(ip->i_mount, &eofb);
|
||||
xfs_icache_free_cowblocks(ip->i_mount, &eofb);
|
||||
goto write_retry;
|
||||
}
|
||||
|
||||
@@ -1139,29 +1140,8 @@ xfs_find_get_desired_pgoff(
|
||||
want = min_t(pgoff_t, end - index, PAGEVEC_SIZE - 1) + 1;
|
||||
nr_pages = pagevec_lookup(&pvec, inode->i_mapping, index,
|
||||
want);
|
||||
/*
|
||||
* No page mapped into given range. If we are searching holes
|
||||
* and if this is the first time we got into the loop, it means
|
||||
* that the given offset is landed in a hole, return it.
|
||||
*
|
||||
* If we have already stepped through some block buffers to find
|
||||
* holes but they all contains data. In this case, the last
|
||||
* offset is already updated and pointed to the end of the last
|
||||
* mapped page, if it does not reach the endpoint to search,
|
||||
* that means there should be a hole between them.
|
||||
*/
|
||||
if (nr_pages == 0) {
|
||||
/* Data search found nothing */
|
||||
if (type == DATA_OFF)
|
||||
break;
|
||||
|
||||
ASSERT(type == HOLE_OFF);
|
||||
if (lastoff == startoff || lastoff < endoff) {
|
||||
found = true;
|
||||
*offset = lastoff;
|
||||
}
|
||||
if (nr_pages == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < nr_pages; i++) {
|
||||
struct page *page = pvec.pages[i];
|
||||
@@ -1227,21 +1207,20 @@ xfs_find_get_desired_pgoff(
|
||||
|
||||
/*
|
||||
* The number of returned pages less than our desired, search
|
||||
* done. In this case, nothing was found for searching data,
|
||||
* but we found a hole behind the last offset.
|
||||
* done.
|
||||
*/
|
||||
if (nr_pages < want) {
|
||||
if (type == HOLE_OFF) {
|
||||
*offset = lastoff;
|
||||
found = true;
|
||||
}
|
||||
if (nr_pages < want)
|
||||
break;
|
||||
}
|
||||
|
||||
index = pvec.pages[i - 1]->index + 1;
|
||||
pagevec_release(&pvec);
|
||||
} while (index <= end);
|
||||
|
||||
/* No page at lastoff and we are not done - we found a hole. */
|
||||
if (type == HOLE_OFF && lastoff < endoff) {
|
||||
*offset = lastoff;
|
||||
found = true;
|
||||
}
|
||||
out:
|
||||
pagevec_release(&pvec);
|
||||
return found;
|
||||
|
||||
+7
-8
@@ -66,7 +66,6 @@ xfs_inode_alloc(
|
||||
|
||||
XFS_STATS_INC(mp, vn_active);
|
||||
ASSERT(atomic_read(&ip->i_pincount) == 0);
|
||||
ASSERT(!spin_is_locked(&ip->i_flags_lock));
|
||||
ASSERT(!xfs_isiflocked(ip));
|
||||
ASSERT(ip->i_ino == 0);
|
||||
|
||||
@@ -192,7 +191,7 @@ xfs_perag_set_reclaim_tag(
|
||||
{
|
||||
struct xfs_mount *mp = pag->pag_mount;
|
||||
|
||||
ASSERT(spin_is_locked(&pag->pag_ici_lock));
|
||||
lockdep_assert_held(&pag->pag_ici_lock);
|
||||
if (pag->pag_ici_reclaimable++)
|
||||
return;
|
||||
|
||||
@@ -214,7 +213,7 @@ xfs_perag_clear_reclaim_tag(
|
||||
{
|
||||
struct xfs_mount *mp = pag->pag_mount;
|
||||
|
||||
ASSERT(spin_is_locked(&pag->pag_ici_lock));
|
||||
lockdep_assert_held(&pag->pag_ici_lock);
|
||||
if (--pag->pag_ici_reclaimable)
|
||||
return;
|
||||
|
||||
@@ -1079,11 +1078,11 @@ reclaim:
|
||||
* Because we use RCU freeing we need to ensure the inode always appears
|
||||
* to be reclaimed with an invalid inode number when in the free state.
|
||||
* We do this as early as possible under the ILOCK so that
|
||||
* xfs_iflush_cluster() can be guaranteed to detect races with us here.
|
||||
* By doing this, we guarantee that once xfs_iflush_cluster has locked
|
||||
* XFS_ILOCK that it will see either a valid, flushable inode that will
|
||||
* serialise correctly, or it will see a clean (and invalid) inode that
|
||||
* it can skip.
|
||||
* xfs_iflush_cluster() and xfs_ifree_cluster() can be guaranteed to
|
||||
* detect races with us here. By doing this, we guarantee that once
|
||||
* xfs_iflush_cluster() or xfs_ifree_cluster() has locked XFS_ILOCK that
|
||||
* it will see either a valid inode that will serialise correctly, or it
|
||||
* will see an invalid inode that it can skip.
|
||||
*/
|
||||
spin_lock(&ip->i_flags_lock);
|
||||
ip->i_flags = XFS_IRECLAIM;
|
||||
|
||||
+25
-10
@@ -881,7 +881,6 @@ xfs_ialloc(
|
||||
case S_IFREG:
|
||||
case S_IFDIR:
|
||||
if (pip && (pip->i_d.di_flags & XFS_DIFLAG_ANY)) {
|
||||
uint64_t di_flags2 = 0;
|
||||
uint di_flags = 0;
|
||||
|
||||
if (S_ISDIR(mode)) {
|
||||
@@ -918,20 +917,23 @@ xfs_ialloc(
|
||||
di_flags |= XFS_DIFLAG_NODEFRAG;
|
||||
if (pip->i_d.di_flags & XFS_DIFLAG_FILESTREAM)
|
||||
di_flags |= XFS_DIFLAG_FILESTREAM;
|
||||
if (pip->i_d.di_flags2 & XFS_DIFLAG2_DAX)
|
||||
di_flags2 |= XFS_DIFLAG2_DAX;
|
||||
|
||||
ip->i_d.di_flags |= di_flags;
|
||||
ip->i_d.di_flags2 |= di_flags2;
|
||||
}
|
||||
if (pip &&
|
||||
(pip->i_d.di_flags2 & XFS_DIFLAG2_ANY) &&
|
||||
pip->i_d.di_version == 3 &&
|
||||
ip->i_d.di_version == 3) {
|
||||
uint64_t di_flags2 = 0;
|
||||
|
||||
if (pip->i_d.di_flags2 & XFS_DIFLAG2_COWEXTSIZE) {
|
||||
ip->i_d.di_flags2 |= XFS_DIFLAG2_COWEXTSIZE;
|
||||
di_flags2 |= XFS_DIFLAG2_COWEXTSIZE;
|
||||
ip->i_d.di_cowextsize = pip->i_d.di_cowextsize;
|
||||
}
|
||||
if (pip->i_d.di_flags2 & XFS_DIFLAG2_DAX)
|
||||
di_flags2 |= XFS_DIFLAG2_DAX;
|
||||
|
||||
ip->i_d.di_flags2 |= di_flags2;
|
||||
}
|
||||
/* FALLTHROUGH */
|
||||
case S_IFLNK:
|
||||
@@ -2366,11 +2368,24 @@ retry:
|
||||
* already marked stale. If we can't lock it, back off
|
||||
* and retry.
|
||||
*/
|
||||
if (ip != free_ip &&
|
||||
!xfs_ilock_nowait(ip, XFS_ILOCK_EXCL)) {
|
||||
rcu_read_unlock();
|
||||
delay(1);
|
||||
goto retry;
|
||||
if (ip != free_ip) {
|
||||
if (!xfs_ilock_nowait(ip, XFS_ILOCK_EXCL)) {
|
||||
rcu_read_unlock();
|
||||
delay(1);
|
||||
goto retry;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check the inode number again in case we're
|
||||
* racing with freeing in xfs_reclaim_inode().
|
||||
* See the comments in that function for more
|
||||
* information as to why the initial check is
|
||||
* not sufficient.
|
||||
*/
|
||||
if (ip->i_ino != inum + i) {
|
||||
xfs_iunlock(ip, XFS_ILOCK_EXCL);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
|
||||
+59
-15
@@ -27,6 +27,7 @@
|
||||
#include "xfs_error.h"
|
||||
#include "xfs_trace.h"
|
||||
#include "xfs_trans_priv.h"
|
||||
#include "xfs_buf_item.h"
|
||||
#include "xfs_log.h"
|
||||
|
||||
|
||||
@@ -475,6 +476,23 @@ xfs_inode_item_unpin(
|
||||
wake_up_bit(&ip->i_flags, __XFS_IPINNED_BIT);
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback used to mark a buffer with XFS_LI_FAILED when items in the buffer
|
||||
* have been failed during writeback
|
||||
*
|
||||
* This informs the AIL that the inode is already flush locked on the next push,
|
||||
* and acquires a hold on the buffer to ensure that it isn't reclaimed before
|
||||
* dirty data makes it to disk.
|
||||
*/
|
||||
STATIC void
|
||||
xfs_inode_item_error(
|
||||
struct xfs_log_item *lip,
|
||||
struct xfs_buf *bp)
|
||||
{
|
||||
ASSERT(xfs_isiflocked(INODE_ITEM(lip)->ili_inode));
|
||||
xfs_set_li_failed(lip, bp);
|
||||
}
|
||||
|
||||
STATIC uint
|
||||
xfs_inode_item_push(
|
||||
struct xfs_log_item *lip,
|
||||
@@ -484,13 +502,28 @@ xfs_inode_item_push(
|
||||
{
|
||||
struct xfs_inode_log_item *iip = INODE_ITEM(lip);
|
||||
struct xfs_inode *ip = iip->ili_inode;
|
||||
struct xfs_buf *bp = NULL;
|
||||
struct xfs_buf *bp = lip->li_buf;
|
||||
uint rval = XFS_ITEM_SUCCESS;
|
||||
int error;
|
||||
|
||||
if (xfs_ipincount(ip) > 0)
|
||||
return XFS_ITEM_PINNED;
|
||||
|
||||
/*
|
||||
* The buffer containing this item failed to be written back
|
||||
* previously. Resubmit the buffer for IO.
|
||||
*/
|
||||
if (lip->li_flags & XFS_LI_FAILED) {
|
||||
if (!xfs_buf_trylock(bp))
|
||||
return XFS_ITEM_LOCKED;
|
||||
|
||||
if (!xfs_buf_resubmit_failed_buffers(bp, lip, buffer_list))
|
||||
rval = XFS_ITEM_FLUSHING;
|
||||
|
||||
xfs_buf_unlock(bp);
|
||||
return rval;
|
||||
}
|
||||
|
||||
if (!xfs_ilock_nowait(ip, XFS_ILOCK_SHARED))
|
||||
return XFS_ITEM_LOCKED;
|
||||
|
||||
@@ -622,7 +655,8 @@ static const struct xfs_item_ops xfs_inode_item_ops = {
|
||||
.iop_unlock = xfs_inode_item_unlock,
|
||||
.iop_committed = xfs_inode_item_committed,
|
||||
.iop_push = xfs_inode_item_push,
|
||||
.iop_committing = xfs_inode_item_committing
|
||||
.iop_committing = xfs_inode_item_committing,
|
||||
.iop_error = xfs_inode_item_error
|
||||
};
|
||||
|
||||
|
||||
@@ -710,7 +744,8 @@ xfs_iflush_done(
|
||||
* the AIL lock.
|
||||
*/
|
||||
iip = INODE_ITEM(blip);
|
||||
if (iip->ili_logged && blip->li_lsn == iip->ili_flush_lsn)
|
||||
if ((iip->ili_logged && blip->li_lsn == iip->ili_flush_lsn) ||
|
||||
lip->li_flags & XFS_LI_FAILED)
|
||||
need_ail++;
|
||||
|
||||
blip = next;
|
||||
@@ -718,7 +753,8 @@ xfs_iflush_done(
|
||||
|
||||
/* make sure we capture the state of the initial inode. */
|
||||
iip = INODE_ITEM(lip);
|
||||
if (iip->ili_logged && lip->li_lsn == iip->ili_flush_lsn)
|
||||
if ((iip->ili_logged && lip->li_lsn == iip->ili_flush_lsn) ||
|
||||
lip->li_flags & XFS_LI_FAILED)
|
||||
need_ail++;
|
||||
|
||||
/*
|
||||
@@ -731,22 +767,30 @@ xfs_iflush_done(
|
||||
* holding the lock before removing the inode from the AIL.
|
||||
*/
|
||||
if (need_ail) {
|
||||
struct xfs_log_item *log_items[need_ail];
|
||||
int i = 0;
|
||||
bool mlip_changed = false;
|
||||
|
||||
/* this is an opencoded batch version of xfs_trans_ail_delete */
|
||||
spin_lock(&ailp->xa_lock);
|
||||
for (blip = lip; blip; blip = blip->li_bio_list) {
|
||||
iip = INODE_ITEM(blip);
|
||||
if (iip->ili_logged &&
|
||||
blip->li_lsn == iip->ili_flush_lsn) {
|
||||
log_items[i++] = blip;
|
||||
if (INODE_ITEM(blip)->ili_logged &&
|
||||
blip->li_lsn == INODE_ITEM(blip)->ili_flush_lsn)
|
||||
mlip_changed |= xfs_ail_delete_one(ailp, blip);
|
||||
else {
|
||||
xfs_clear_li_failed(blip);
|
||||
}
|
||||
ASSERT(i <= need_ail);
|
||||
}
|
||||
/* xfs_trans_ail_delete_bulk() drops the AIL lock. */
|
||||
xfs_trans_ail_delete_bulk(ailp, log_items, i,
|
||||
SHUTDOWN_CORRUPT_INCORE);
|
||||
}
|
||||
|
||||
if (mlip_changed) {
|
||||
if (!XFS_FORCED_SHUTDOWN(ailp->xa_mount))
|
||||
xlog_assign_tail_lsn_locked(ailp->xa_mount);
|
||||
if (list_empty(&ailp->xa_ail))
|
||||
wake_up_all(&ailp->xa_empty);
|
||||
}
|
||||
spin_unlock(&ailp->xa_lock);
|
||||
|
||||
if (mlip_changed)
|
||||
xfs_log_space_wake(ailp->xa_mount);
|
||||
}
|
||||
|
||||
/*
|
||||
* clean up and unlock the flush lock now we are done. We can clear the
|
||||
|
||||
+27
-14
@@ -928,16 +928,15 @@ xfs_ioc_fsgetxattr(
|
||||
return 0;
|
||||
}
|
||||
|
||||
STATIC void
|
||||
xfs_set_diflags(
|
||||
STATIC uint16_t
|
||||
xfs_flags2diflags(
|
||||
struct xfs_inode *ip,
|
||||
unsigned int xflags)
|
||||
{
|
||||
unsigned int di_flags;
|
||||
uint64_t di_flags2;
|
||||
|
||||
/* can't set PREALLOC this way, just preserve it */
|
||||
di_flags = (ip->i_d.di_flags & XFS_DIFLAG_PREALLOC);
|
||||
uint16_t di_flags =
|
||||
(ip->i_d.di_flags & XFS_DIFLAG_PREALLOC);
|
||||
|
||||
if (xflags & FS_XFLAG_IMMUTABLE)
|
||||
di_flags |= XFS_DIFLAG_IMMUTABLE;
|
||||
if (xflags & FS_XFLAG_APPEND)
|
||||
@@ -967,19 +966,24 @@ xfs_set_diflags(
|
||||
if (xflags & FS_XFLAG_EXTSIZE)
|
||||
di_flags |= XFS_DIFLAG_EXTSIZE;
|
||||
}
|
||||
ip->i_d.di_flags = di_flags;
|
||||
|
||||
/* diflags2 only valid for v3 inodes. */
|
||||
if (ip->i_d.di_version < 3)
|
||||
return;
|
||||
return di_flags;
|
||||
}
|
||||
|
||||
STATIC uint64_t
|
||||
xfs_flags2diflags2(
|
||||
struct xfs_inode *ip,
|
||||
unsigned int xflags)
|
||||
{
|
||||
uint64_t di_flags2 =
|
||||
(ip->i_d.di_flags2 & XFS_DIFLAG2_REFLINK);
|
||||
|
||||
di_flags2 = (ip->i_d.di_flags2 & XFS_DIFLAG2_REFLINK);
|
||||
if (xflags & FS_XFLAG_DAX)
|
||||
di_flags2 |= XFS_DIFLAG2_DAX;
|
||||
if (xflags & FS_XFLAG_COWEXTSIZE)
|
||||
di_flags2 |= XFS_DIFLAG2_COWEXTSIZE;
|
||||
|
||||
ip->i_d.di_flags2 = di_flags2;
|
||||
return di_flags2;
|
||||
}
|
||||
|
||||
STATIC void
|
||||
@@ -1005,11 +1009,12 @@ xfs_diflags_to_linux(
|
||||
inode->i_flags |= S_NOATIME;
|
||||
else
|
||||
inode->i_flags &= ~S_NOATIME;
|
||||
#if 0 /* disabled until the flag switching races are sorted out */
|
||||
if (xflags & FS_XFLAG_DAX)
|
||||
inode->i_flags |= S_DAX;
|
||||
else
|
||||
inode->i_flags &= ~S_DAX;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -1019,6 +1024,7 @@ xfs_ioctl_setattr_xflags(
|
||||
struct fsxattr *fa)
|
||||
{
|
||||
struct xfs_mount *mp = ip->i_mount;
|
||||
uint64_t di_flags2;
|
||||
|
||||
/* Can't change realtime flag if any extents are allocated. */
|
||||
if ((ip->i_d.di_nextents || ip->i_delayed_blks) &&
|
||||
@@ -1049,7 +1055,14 @@ xfs_ioctl_setattr_xflags(
|
||||
!capable(CAP_LINUX_IMMUTABLE))
|
||||
return -EPERM;
|
||||
|
||||
xfs_set_diflags(ip, fa->fsx_xflags);
|
||||
/* diflags2 only valid for v3 inodes. */
|
||||
di_flags2 = xfs_flags2diflags2(ip, fa->fsx_xflags);
|
||||
if (di_flags2 && ip->i_d.di_version < 3)
|
||||
return -EINVAL;
|
||||
|
||||
ip->i_d.di_flags = xfs_flags2diflags(ip, fa->fsx_xflags);
|
||||
ip->i_d.di_flags2 = di_flags2;
|
||||
|
||||
xfs_diflags_to_linux(ip);
|
||||
xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_CHG);
|
||||
xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
|
||||
|
||||
+1
-1
@@ -802,7 +802,7 @@ xfs_vn_setattr_nonsize(
|
||||
* Caution: The caller of this function is responsible for calling
|
||||
* setattr_prepare() or otherwise verifying the change is fine.
|
||||
*/
|
||||
int
|
||||
STATIC int
|
||||
xfs_setattr_size(
|
||||
struct xfs_inode *ip,
|
||||
struct iattr *iattr)
|
||||
|
||||
+8
-1
@@ -363,7 +363,14 @@ static inline __uint64_t howmany_64(__uint64_t x, __uint32_t y)
|
||||
#endif /* DEBUG */
|
||||
|
||||
#ifdef CONFIG_XFS_RT
|
||||
#define XFS_IS_REALTIME_INODE(ip) ((ip)->i_d.di_flags & XFS_DIFLAG_REALTIME)
|
||||
|
||||
/*
|
||||
* make sure we ignore the inode flag if the filesystem doesn't have a
|
||||
* configured realtime device.
|
||||
*/
|
||||
#define XFS_IS_REALTIME_INODE(ip) \
|
||||
(((ip)->i_d.di_flags & XFS_DIFLAG_REALTIME) && \
|
||||
(ip)->i_mount->m_rtdev_targp)
|
||||
#else
|
||||
#define XFS_IS_REALTIME_INODE(ip) (0)
|
||||
#endif
|
||||
|
||||
+35
-9
@@ -743,15 +743,45 @@ xfs_log_mount_finish(
|
||||
struct xfs_mount *mp)
|
||||
{
|
||||
int error = 0;
|
||||
bool readonly = (mp->m_flags & XFS_MOUNT_RDONLY);
|
||||
|
||||
if (mp->m_flags & XFS_MOUNT_NORECOVERY) {
|
||||
ASSERT(mp->m_flags & XFS_MOUNT_RDONLY);
|
||||
return 0;
|
||||
} else if (readonly) {
|
||||
/* Allow unlinked processing to proceed */
|
||||
mp->m_flags &= ~XFS_MOUNT_RDONLY;
|
||||
}
|
||||
|
||||
/*
|
||||
* During the second phase of log recovery, we need iget and
|
||||
* iput to behave like they do for an active filesystem.
|
||||
* xfs_fs_drop_inode needs to be able to prevent the deletion
|
||||
* of inodes before we're done replaying log items on those
|
||||
* inodes. Turn it off immediately after recovery finishes
|
||||
* so that we don't leak the quota inodes if subsequent mount
|
||||
* activities fail.
|
||||
*
|
||||
* We let all inodes involved in redo item processing end up on
|
||||
* the LRU instead of being evicted immediately so that if we do
|
||||
* something to an unlinked inode, the irele won't cause
|
||||
* premature truncation and freeing of the inode, which results
|
||||
* in log recovery failure. We have to evict the unreferenced
|
||||
* lru inodes after clearing MS_ACTIVE because we don't
|
||||
* otherwise clean up the lru if there's a subsequent failure in
|
||||
* xfs_mountfs, which leads to us leaking the inodes if nothing
|
||||
* else (e.g. quotacheck) references the inodes before the
|
||||
* mount failure occurs.
|
||||
*/
|
||||
mp->m_super->s_flags |= MS_ACTIVE;
|
||||
error = xlog_recover_finish(mp->m_log);
|
||||
if (!error)
|
||||
xfs_log_work_queue(mp);
|
||||
mp->m_super->s_flags &= ~MS_ACTIVE;
|
||||
evict_inodes(mp->m_super);
|
||||
|
||||
if (readonly)
|
||||
mp->m_flags |= XFS_MOUNT_RDONLY;
|
||||
|
||||
return error;
|
||||
}
|
||||
@@ -801,11 +831,14 @@ xfs_log_unmount_write(xfs_mount_t *mp)
|
||||
int error;
|
||||
|
||||
/*
|
||||
* Don't write out unmount record on read-only mounts.
|
||||
* Don't write out unmount record on norecovery mounts or ro devices.
|
||||
* Or, if we are doing a forced umount (typically because of IO errors).
|
||||
*/
|
||||
if (mp->m_flags & XFS_MOUNT_RDONLY)
|
||||
if (mp->m_flags & XFS_MOUNT_NORECOVERY ||
|
||||
xfs_readonly_buftarg(log->l_mp->m_logdev_targp)) {
|
||||
ASSERT(mp->m_flags & XFS_MOUNT_RDONLY);
|
||||
return 0;
|
||||
}
|
||||
|
||||
error = _xfs_log_force(mp, XFS_LOG_SYNC, NULL);
|
||||
ASSERT(error || !(XLOG_FORCED_SHUTDOWN(log)));
|
||||
@@ -3304,8 +3337,6 @@ maybe_sleep:
|
||||
*/
|
||||
if (iclog->ic_state & XLOG_STATE_IOERROR)
|
||||
return -EIO;
|
||||
if (log_flushed)
|
||||
*log_flushed = 1;
|
||||
} else {
|
||||
|
||||
no_sleep:
|
||||
@@ -3409,8 +3440,6 @@ try_again:
|
||||
|
||||
xlog_wait(&iclog->ic_prev->ic_write_wait,
|
||||
&log->l_icloglock);
|
||||
if (log_flushed)
|
||||
*log_flushed = 1;
|
||||
already_slept = 1;
|
||||
goto try_again;
|
||||
}
|
||||
@@ -3444,9 +3473,6 @@ try_again:
|
||||
*/
|
||||
if (iclog->ic_state & XLOG_STATE_IOERROR)
|
||||
return -EIO;
|
||||
|
||||
if (log_flushed)
|
||||
*log_flushed = 1;
|
||||
} else { /* just return */
|
||||
spin_unlock(&log->l_icloglock);
|
||||
}
|
||||
|
||||
+124
-64
@@ -1029,61 +1029,106 @@ out_error:
|
||||
}
|
||||
|
||||
/*
|
||||
* Check the log tail for torn writes. This is required when torn writes are
|
||||
* detected at the head and the head had to be walked back to a previous record.
|
||||
* The tail of the previous record must now be verified to ensure the torn
|
||||
* writes didn't corrupt the previous tail.
|
||||
* Calculate distance from head to tail (i.e., unused space in the log).
|
||||
*/
|
||||
static inline int
|
||||
xlog_tail_distance(
|
||||
struct xlog *log,
|
||||
xfs_daddr_t head_blk,
|
||||
xfs_daddr_t tail_blk)
|
||||
{
|
||||
if (head_blk < tail_blk)
|
||||
return tail_blk - head_blk;
|
||||
|
||||
return tail_blk + (log->l_logBBsize - head_blk);
|
||||
}
|
||||
|
||||
/*
|
||||
* Verify the log tail. This is particularly important when torn or incomplete
|
||||
* writes have been detected near the front of the log and the head has been
|
||||
* walked back accordingly.
|
||||
*
|
||||
* Return an error if CRC verification fails as recovery cannot proceed.
|
||||
* We also have to handle the case where the tail was pinned and the head
|
||||
* blocked behind the tail right before a crash. If the tail had been pushed
|
||||
* immediately prior to the crash and the subsequent checkpoint was only
|
||||
* partially written, it's possible it overwrote the last referenced tail in the
|
||||
* log with garbage. This is not a coherency problem because the tail must have
|
||||
* been pushed before it can be overwritten, but appears as log corruption to
|
||||
* recovery because we have no way to know the tail was updated if the
|
||||
* subsequent checkpoint didn't write successfully.
|
||||
*
|
||||
* Therefore, CRC check the log from tail to head. If a failure occurs and the
|
||||
* offending record is within max iclog bufs from the head, walk the tail
|
||||
* forward and retry until a valid tail is found or corruption is detected out
|
||||
* of the range of a possible overwrite.
|
||||
*/
|
||||
STATIC int
|
||||
xlog_verify_tail(
|
||||
struct xlog *log,
|
||||
xfs_daddr_t head_blk,
|
||||
xfs_daddr_t tail_blk)
|
||||
xfs_daddr_t *tail_blk,
|
||||
int hsize)
|
||||
{
|
||||
struct xlog_rec_header *thead;
|
||||
struct xfs_buf *bp;
|
||||
xfs_daddr_t first_bad;
|
||||
int count;
|
||||
int error = 0;
|
||||
bool wrapped;
|
||||
xfs_daddr_t tmp_head;
|
||||
xfs_daddr_t tmp_tail;
|
||||
xfs_daddr_t orig_tail = *tail_blk;
|
||||
|
||||
bp = xlog_get_bp(log, 1);
|
||||
if (!bp)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* Seek XLOG_MAX_ICLOGS + 1 records past the current tail record to get
|
||||
* a temporary head block that points after the last possible
|
||||
* concurrently written record of the tail.
|
||||
* Make sure the tail points to a record (returns positive count on
|
||||
* success).
|
||||
*/
|
||||
count = xlog_seek_logrec_hdr(log, head_blk, tail_blk,
|
||||
XLOG_MAX_ICLOGS + 1, bp, &tmp_head, &thead,
|
||||
&wrapped);
|
||||
if (count < 0) {
|
||||
error = count;
|
||||
error = xlog_seek_logrec_hdr(log, head_blk, *tail_blk, 1, bp,
|
||||
&tmp_tail, &thead, &wrapped);
|
||||
if (error < 0)
|
||||
goto out;
|
||||
if (*tail_blk != tmp_tail)
|
||||
*tail_blk = tmp_tail;
|
||||
|
||||
/*
|
||||
* Run a CRC check from the tail to the head. We can't just check
|
||||
* MAX_ICLOGS records past the tail because the tail may point to stale
|
||||
* blocks cleared during the search for the head/tail. These blocks are
|
||||
* overwritten with zero-length records and thus record count is not a
|
||||
* reliable indicator of the iclog state before a crash.
|
||||
*/
|
||||
first_bad = 0;
|
||||
error = xlog_do_recovery_pass(log, head_blk, *tail_blk,
|
||||
XLOG_RECOVER_CRCPASS, &first_bad);
|
||||
while ((error == -EFSBADCRC || error == -EFSCORRUPTED) && first_bad) {
|
||||
int tail_distance;
|
||||
|
||||
/*
|
||||
* Is corruption within range of the head? If so, retry from
|
||||
* the next record. Otherwise return an error.
|
||||
*/
|
||||
tail_distance = xlog_tail_distance(log, head_blk, first_bad);
|
||||
if (tail_distance > BTOBB(XLOG_MAX_ICLOGS * hsize))
|
||||
break;
|
||||
|
||||
/* skip to the next record; returns positive count on success */
|
||||
error = xlog_seek_logrec_hdr(log, head_blk, first_bad, 2, bp,
|
||||
&tmp_tail, &thead, &wrapped);
|
||||
if (error < 0)
|
||||
goto out;
|
||||
|
||||
*tail_blk = tmp_tail;
|
||||
first_bad = 0;
|
||||
error = xlog_do_recovery_pass(log, head_blk, *tail_blk,
|
||||
XLOG_RECOVER_CRCPASS, &first_bad);
|
||||
}
|
||||
|
||||
/*
|
||||
* If the call above didn't find XLOG_MAX_ICLOGS + 1 records, we ran
|
||||
* into the actual log head. tmp_head points to the start of the record
|
||||
* so update it to the actual head block.
|
||||
*/
|
||||
if (count < XLOG_MAX_ICLOGS + 1)
|
||||
tmp_head = head_blk;
|
||||
|
||||
/*
|
||||
* We now have a tail and temporary head block that covers at least
|
||||
* XLOG_MAX_ICLOGS records from the tail. We need to verify that these
|
||||
* records were completely written. Run a CRC verification pass from
|
||||
* tail to head and return the result.
|
||||
*/
|
||||
error = xlog_do_recovery_pass(log, tmp_head, tail_blk,
|
||||
XLOG_RECOVER_CRCPASS, &first_bad);
|
||||
|
||||
if (!error && *tail_blk != orig_tail)
|
||||
xfs_warn(log->l_mp,
|
||||
"Tail block (0x%llx) overwrite detected. Updated to 0x%llx",
|
||||
orig_tail, *tail_blk);
|
||||
out:
|
||||
xlog_put_bp(bp);
|
||||
return error;
|
||||
@@ -1143,7 +1188,7 @@ xlog_verify_head(
|
||||
*/
|
||||
error = xlog_do_recovery_pass(log, *head_blk, tmp_rhead_blk,
|
||||
XLOG_RECOVER_CRCPASS, &first_bad);
|
||||
if (error == -EFSBADCRC) {
|
||||
if ((error == -EFSBADCRC || error == -EFSCORRUPTED) && first_bad) {
|
||||
/*
|
||||
* We've hit a potential torn write. Reset the error and warn
|
||||
* about it.
|
||||
@@ -1183,31 +1228,12 @@ xlog_verify_head(
|
||||
ASSERT(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now verify the tail based on the updated head. This is
|
||||
* required because the torn writes trimmed from the head could
|
||||
* have been written over the tail of a previous record. Return
|
||||
* any errors since recovery cannot proceed if the tail is
|
||||
* corrupt.
|
||||
*
|
||||
* XXX: This leaves a gap in truly robust protection from torn
|
||||
* writes in the log. If the head is behind the tail, the tail
|
||||
* pushes forward to create some space and then a crash occurs
|
||||
* causing the writes into the previous record's tail region to
|
||||
* tear, log recovery isn't able to recover.
|
||||
*
|
||||
* How likely is this to occur? If possible, can we do something
|
||||
* more intelligent here? Is it safe to push the tail forward if
|
||||
* we can determine that the tail is within the range of the
|
||||
* torn write (e.g., the kernel can only overwrite the tail if
|
||||
* it has actually been pushed forward)? Alternatively, could we
|
||||
* somehow prevent this condition at runtime?
|
||||
*/
|
||||
error = xlog_verify_tail(log, *head_blk, *tail_blk);
|
||||
}
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
return error;
|
||||
return xlog_verify_tail(log, *head_blk, tail_blk,
|
||||
be32_to_cpu((*rhead)->h_size));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -4152,7 +4178,7 @@ xlog_recover_commit_trans(
|
||||
|
||||
#define XLOG_RECOVER_COMMIT_QUEUE_MAX 100
|
||||
|
||||
hlist_del(&trans->r_list);
|
||||
hlist_del_init(&trans->r_list);
|
||||
|
||||
error = xlog_recover_reorder_trans(log, trans, pass);
|
||||
if (error)
|
||||
@@ -4354,6 +4380,8 @@ xlog_recover_free_trans(
|
||||
xlog_recover_item_t *item, *n;
|
||||
int i;
|
||||
|
||||
hlist_del_init(&trans->r_list);
|
||||
|
||||
list_for_each_entry_safe(item, n, &trans->r_itemq, ri_list) {
|
||||
/* Free the regions in the item. */
|
||||
list_del(&item->ri_list);
|
||||
@@ -4799,12 +4827,16 @@ xlog_recover_process_intents(
|
||||
int error = 0;
|
||||
struct xfs_ail_cursor cur;
|
||||
struct xfs_ail *ailp;
|
||||
#if defined(DEBUG) || defined(XFS_WARN)
|
||||
xfs_lsn_t last_lsn;
|
||||
#endif
|
||||
|
||||
ailp = log->l_ailp;
|
||||
spin_lock(&ailp->xa_lock);
|
||||
lip = xfs_trans_ail_cursor_first(ailp, &cur, 0);
|
||||
#if defined(DEBUG) || defined(XFS_WARN)
|
||||
last_lsn = xlog_assign_lsn(log->l_curr_cycle, log->l_curr_block);
|
||||
#endif
|
||||
while (lip != NULL) {
|
||||
/*
|
||||
* We're done when we see something other than an intent.
|
||||
@@ -5214,7 +5246,7 @@ xlog_do_recovery_pass(
|
||||
xfs_daddr_t *first_bad) /* out: first bad log rec */
|
||||
{
|
||||
xlog_rec_header_t *rhead;
|
||||
xfs_daddr_t blk_no;
|
||||
xfs_daddr_t blk_no, rblk_no;
|
||||
xfs_daddr_t rhead_blk;
|
||||
char *offset;
|
||||
xfs_buf_t *hbp, *dbp;
|
||||
@@ -5222,11 +5254,15 @@ xlog_do_recovery_pass(
|
||||
int error2 = 0;
|
||||
int bblks, split_bblks;
|
||||
int hblks, split_hblks, wrapped_hblks;
|
||||
int i;
|
||||
struct hlist_head rhash[XLOG_RHASH_SIZE];
|
||||
LIST_HEAD (buffer_list);
|
||||
|
||||
ASSERT(head_blk != tail_blk);
|
||||
rhead_blk = 0;
|
||||
blk_no = rhead_blk = tail_blk;
|
||||
|
||||
for (i = 0; i < XLOG_RHASH_SIZE; i++)
|
||||
INIT_HLIST_HEAD(&rhash[i]);
|
||||
|
||||
/*
|
||||
* Read the header of the tail block and get the iclog buffer size from
|
||||
@@ -5301,7 +5337,6 @@ xlog_do_recovery_pass(
|
||||
}
|
||||
|
||||
memset(rhash, 0, sizeof(rhash));
|
||||
blk_no = rhead_blk = tail_blk;
|
||||
if (tail_blk > head_blk) {
|
||||
/*
|
||||
* Perform recovery around the end of the physical log.
|
||||
@@ -5363,9 +5398,19 @@ xlog_do_recovery_pass(
|
||||
bblks = (int)BTOBB(be32_to_cpu(rhead->h_len));
|
||||
blk_no += hblks;
|
||||
|
||||
/* Read in data for log record */
|
||||
if (blk_no + bblks <= log->l_logBBsize) {
|
||||
error = xlog_bread(log, blk_no, bblks, dbp,
|
||||
/*
|
||||
* Read the log record data in multiple reads if it
|
||||
* wraps around the end of the log. Note that if the
|
||||
* header already wrapped, blk_no could point past the
|
||||
* end of the log. The record data is contiguous in
|
||||
* that case.
|
||||
*/
|
||||
if (blk_no + bblks <= log->l_logBBsize ||
|
||||
blk_no >= log->l_logBBsize) {
|
||||
/* mod blk_no in case the header wrapped and
|
||||
* pushed it beyond the end of the log */
|
||||
rblk_no = do_mod(blk_no, log->l_logBBsize);
|
||||
error = xlog_bread(log, rblk_no, bblks, dbp,
|
||||
&offset);
|
||||
if (error)
|
||||
goto bread_err2;
|
||||
@@ -5464,6 +5509,19 @@ xlog_do_recovery_pass(
|
||||
if (error && first_bad)
|
||||
*first_bad = rhead_blk;
|
||||
|
||||
/*
|
||||
* Transactions are freed at commit time but transactions without commit
|
||||
* records on disk are never committed. Free any that may be left in the
|
||||
* hash table.
|
||||
*/
|
||||
for (i = 0; i < XLOG_RHASH_SIZE; i++) {
|
||||
struct hlist_node *tmp;
|
||||
struct xlog_recover *trans;
|
||||
|
||||
hlist_for_each_entry_safe(trans, tmp, &rhash[i], r_list)
|
||||
xlog_recover_free_trans(trans);
|
||||
}
|
||||
|
||||
return error ? error : error2;
|
||||
}
|
||||
|
||||
@@ -5542,6 +5600,8 @@ xlog_do_recover(
|
||||
xfs_buf_t *bp;
|
||||
xfs_sb_t *sbp;
|
||||
|
||||
trace_xfs_log_recover(log, head_blk, tail_blk);
|
||||
|
||||
/*
|
||||
* First replay the images in the log.
|
||||
*/
|
||||
|
||||
+2
-10
@@ -924,15 +924,6 @@ xfs_mountfs(
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* During the second phase of log recovery, we need iget and
|
||||
* iput to behave like they do for an active filesystem.
|
||||
* xfs_fs_drop_inode needs to be able to prevent the deletion
|
||||
* of inodes before we're done replaying log items on those
|
||||
* inodes.
|
||||
*/
|
||||
mp->m_super->s_flags |= MS_ACTIVE;
|
||||
|
||||
/*
|
||||
* Finish recovering the file system. This part needed to be delayed
|
||||
* until after the root and real-time bitmap inodes were consistently
|
||||
@@ -1008,12 +999,13 @@ xfs_mountfs(
|
||||
out_quota:
|
||||
xfs_qm_unmount_quotas(mp);
|
||||
out_rtunmount:
|
||||
mp->m_super->s_flags &= ~MS_ACTIVE;
|
||||
xfs_rtunmount_inodes(mp);
|
||||
out_rele_rip:
|
||||
IRELE(rip);
|
||||
cancel_delayed_work_sync(&mp->m_reclaim_work);
|
||||
xfs_reclaim_inodes(mp, SYNC_WAIT);
|
||||
/* Clean out dquots that might be in memory after quotacheck. */
|
||||
xfs_qm_unmount(mp);
|
||||
out_log_dealloc:
|
||||
mp->m_flags |= XFS_MOUNT_UNMOUNTING;
|
||||
xfs_log_mount_cancel(mp);
|
||||
|
||||
+30
-1
@@ -111,6 +111,9 @@ restart:
|
||||
skipped = 0;
|
||||
break;
|
||||
}
|
||||
/* we're done if id overflows back to zero */
|
||||
if (!next_index)
|
||||
break;
|
||||
}
|
||||
|
||||
if (skipped) {
|
||||
@@ -1247,6 +1250,7 @@ xfs_qm_flush_one(
|
||||
struct xfs_dquot *dqp,
|
||||
void *data)
|
||||
{
|
||||
struct xfs_mount *mp = dqp->q_mount;
|
||||
struct list_head *buffer_list = data;
|
||||
struct xfs_buf *bp = NULL;
|
||||
int error = 0;
|
||||
@@ -1257,7 +1261,32 @@ xfs_qm_flush_one(
|
||||
if (!XFS_DQ_IS_DIRTY(dqp))
|
||||
goto out_unlock;
|
||||
|
||||
xfs_dqflock(dqp);
|
||||
/*
|
||||
* The only way the dquot is already flush locked by the time quotacheck
|
||||
* gets here is if reclaim flushed it before the dqadjust walk dirtied
|
||||
* it for the final time. Quotacheck collects all dquot bufs in the
|
||||
* local delwri queue before dquots are dirtied, so reclaim can't have
|
||||
* possibly queued it for I/O. The only way out is to push the buffer to
|
||||
* cycle the flush lock.
|
||||
*/
|
||||
if (!xfs_dqflock_nowait(dqp)) {
|
||||
/* buf is pinned in-core by delwri list */
|
||||
DEFINE_SINGLE_BUF_MAP(map, dqp->q_blkno,
|
||||
mp->m_quotainfo->qi_dqchunklen);
|
||||
bp = _xfs_buf_find(mp->m_ddev_targp, &map, 1, 0, NULL);
|
||||
if (!bp) {
|
||||
error = -EINVAL;
|
||||
goto out_unlock;
|
||||
}
|
||||
xfs_buf_unlock(bp);
|
||||
|
||||
xfs_buf_delwri_pushbuf(bp, buffer_list);
|
||||
xfs_buf_rele(bp);
|
||||
|
||||
error = -EAGAIN;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
error = xfs_qm_dqflush(dqp, &bp);
|
||||
if (error)
|
||||
goto out_unlock;
|
||||
|
||||
@@ -169,6 +169,8 @@ xfs_reflink_find_shared(
|
||||
error = xfs_alloc_read_agf(mp, NULL, agno, 0, &agbp);
|
||||
if (error)
|
||||
return error;
|
||||
if (!agbp)
|
||||
return -ENOMEM;
|
||||
|
||||
cur = xfs_refcountbt_init_cursor(mp, NULL, agbp, agno, NULL);
|
||||
|
||||
@@ -333,7 +335,7 @@ xfs_reflink_convert_cow_extent(
|
||||
struct xfs_defer_ops *dfops)
|
||||
{
|
||||
struct xfs_bmbt_irec irec = *imap;
|
||||
xfs_fsblock_t first_block;
|
||||
xfs_fsblock_t first_block = NULLFSBLOCK;
|
||||
int nimaps = 1;
|
||||
|
||||
if (imap->br_state == XFS_EXT_NORM)
|
||||
|
||||
+1
-1
@@ -1214,7 +1214,7 @@ xfs_test_remount_options(
|
||||
tmp_mp->m_super = sb;
|
||||
error = xfs_parseargs(tmp_mp, options);
|
||||
xfs_free_fsname(tmp_mp);
|
||||
kfree(tmp_mp);
|
||||
kmem_free(tmp_mp);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
+19
-1
@@ -366,6 +366,7 @@ DEFINE_BUF_EVENT(xfs_buf_iowait_done);
|
||||
DEFINE_BUF_EVENT(xfs_buf_delwri_queue);
|
||||
DEFINE_BUF_EVENT(xfs_buf_delwri_queued);
|
||||
DEFINE_BUF_EVENT(xfs_buf_delwri_split);
|
||||
DEFINE_BUF_EVENT(xfs_buf_delwri_pushbuf);
|
||||
DEFINE_BUF_EVENT(xfs_buf_get_uncached);
|
||||
DEFINE_BUF_EVENT(xfs_bdstrat_shut);
|
||||
DEFINE_BUF_EVENT(xfs_buf_item_relse);
|
||||
@@ -519,7 +520,6 @@ DEFINE_BUF_ITEM_EVENT(xfs_buf_item_size);
|
||||
DEFINE_BUF_ITEM_EVENT(xfs_buf_item_size_ordered);
|
||||
DEFINE_BUF_ITEM_EVENT(xfs_buf_item_size_stale);
|
||||
DEFINE_BUF_ITEM_EVENT(xfs_buf_item_format);
|
||||
DEFINE_BUF_ITEM_EVENT(xfs_buf_item_format_ordered);
|
||||
DEFINE_BUF_ITEM_EVENT(xfs_buf_item_format_stale);
|
||||
DEFINE_BUF_ITEM_EVENT(xfs_buf_item_ordered);
|
||||
DEFINE_BUF_ITEM_EVENT(xfs_buf_item_pin);
|
||||
@@ -1990,6 +1990,24 @@ DEFINE_EVENT(xfs_swap_extent_class, name, \
|
||||
DEFINE_SWAPEXT_EVENT(xfs_swap_extent_before);
|
||||
DEFINE_SWAPEXT_EVENT(xfs_swap_extent_after);
|
||||
|
||||
TRACE_EVENT(xfs_log_recover,
|
||||
TP_PROTO(struct xlog *log, xfs_daddr_t headblk, xfs_daddr_t tailblk),
|
||||
TP_ARGS(log, headblk, tailblk),
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(xfs_daddr_t, headblk)
|
||||
__field(xfs_daddr_t, tailblk)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->dev = log->l_mp->m_super->s_dev;
|
||||
__entry->headblk = headblk;
|
||||
__entry->tailblk = tailblk;
|
||||
),
|
||||
TP_printk("dev %d:%d headblk 0x%llx tailblk 0x%llx",
|
||||
MAJOR(__entry->dev), MINOR(__entry->dev), __entry->headblk,
|
||||
__entry->tailblk)
|
||||
)
|
||||
|
||||
TRACE_EVENT(xfs_log_recover_record,
|
||||
TP_PROTO(struct xlog *log, struct xlog_rec_header *rhead, int pass),
|
||||
TP_ARGS(log, rhead, pass),
|
||||
|
||||
+11
-5
@@ -50,6 +50,7 @@ typedef struct xfs_log_item {
|
||||
struct xfs_ail *li_ailp; /* ptr to AIL */
|
||||
uint li_type; /* item type */
|
||||
uint li_flags; /* misc flags */
|
||||
struct xfs_buf *li_buf; /* real buffer pointer */
|
||||
struct xfs_log_item *li_bio_list; /* buffer item list */
|
||||
void (*li_cb)(struct xfs_buf *,
|
||||
struct xfs_log_item *);
|
||||
@@ -65,11 +66,13 @@ typedef struct xfs_log_item {
|
||||
} xfs_log_item_t;
|
||||
|
||||
#define XFS_LI_IN_AIL 0x1
|
||||
#define XFS_LI_ABORTED 0x2
|
||||
#define XFS_LI_ABORTED 0x2
|
||||
#define XFS_LI_FAILED 0x4
|
||||
|
||||
#define XFS_LI_FLAGS \
|
||||
{ XFS_LI_IN_AIL, "IN_AIL" }, \
|
||||
{ XFS_LI_ABORTED, "ABORTED" }
|
||||
{ XFS_LI_ABORTED, "ABORTED" }, \
|
||||
{ XFS_LI_FAILED, "FAILED" }
|
||||
|
||||
struct xfs_item_ops {
|
||||
void (*iop_size)(xfs_log_item_t *, int *, int *);
|
||||
@@ -80,6 +83,7 @@ struct xfs_item_ops {
|
||||
void (*iop_unlock)(xfs_log_item_t *);
|
||||
xfs_lsn_t (*iop_committed)(xfs_log_item_t *, xfs_lsn_t);
|
||||
void (*iop_committing)(xfs_log_item_t *, xfs_lsn_t);
|
||||
void (*iop_error)(xfs_log_item_t *, xfs_buf_t *);
|
||||
};
|
||||
|
||||
void xfs_log_item_init(struct xfs_mount *mp, struct xfs_log_item *item,
|
||||
@@ -213,12 +217,14 @@ void xfs_trans_bhold_release(xfs_trans_t *, struct xfs_buf *);
|
||||
void xfs_trans_binval(xfs_trans_t *, struct xfs_buf *);
|
||||
void xfs_trans_inode_buf(xfs_trans_t *, struct xfs_buf *);
|
||||
void xfs_trans_stale_inode_buf(xfs_trans_t *, struct xfs_buf *);
|
||||
void xfs_trans_ordered_buf(xfs_trans_t *, struct xfs_buf *);
|
||||
bool xfs_trans_ordered_buf(xfs_trans_t *, struct xfs_buf *);
|
||||
void xfs_trans_dquot_buf(xfs_trans_t *, struct xfs_buf *, uint);
|
||||
void xfs_trans_inode_alloc_buf(xfs_trans_t *, struct xfs_buf *);
|
||||
void xfs_trans_ichgtime(struct xfs_trans *, struct xfs_inode *, int);
|
||||
void xfs_trans_ijoin(struct xfs_trans *, struct xfs_inode *, uint);
|
||||
void xfs_trans_log_buf(xfs_trans_t *, struct xfs_buf *, uint, uint);
|
||||
void xfs_trans_log_buf(struct xfs_trans *, struct xfs_buf *, uint,
|
||||
uint);
|
||||
void xfs_trans_dirty_buf(struct xfs_trans *, struct xfs_buf *);
|
||||
void xfs_trans_log_inode(xfs_trans_t *, struct xfs_inode *, uint);
|
||||
|
||||
void xfs_extent_free_init_defer_op(void);
|
||||
@@ -277,6 +283,6 @@ int xfs_trans_log_finish_bmap_update(struct xfs_trans *tp,
|
||||
struct xfs_bud_log_item *rudp, struct xfs_defer_ops *dfops,
|
||||
enum xfs_bmap_intent_type type, struct xfs_inode *ip,
|
||||
int whichfork, xfs_fileoff_t startoff, xfs_fsblock_t startblock,
|
||||
xfs_filblks_t blockcount, xfs_exntst_t state);
|
||||
xfs_filblks_t *blockcount, xfs_exntst_t state);
|
||||
|
||||
#endif /* __XFS_TRANS_H__ */
|
||||
|
||||
+37
-37
@@ -684,8 +684,24 @@ xfs_trans_ail_update_bulk(
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* xfs_trans_ail_delete_bulk - remove multiple log items from the AIL
|
||||
bool
|
||||
xfs_ail_delete_one(
|
||||
struct xfs_ail *ailp,
|
||||
struct xfs_log_item *lip)
|
||||
{
|
||||
struct xfs_log_item *mlip = xfs_ail_min(ailp);
|
||||
|
||||
trace_xfs_ail_delete(lip, mlip->li_lsn, lip->li_lsn);
|
||||
xfs_ail_delete(ailp, lip);
|
||||
xfs_clear_li_failed(lip);
|
||||
lip->li_flags &= ~XFS_LI_IN_AIL;
|
||||
lip->li_lsn = 0;
|
||||
|
||||
return mlip == lip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a log items from the AIL
|
||||
*
|
||||
* @xfs_trans_ail_delete_bulk takes an array of log items that all need to
|
||||
* removed from the AIL. The caller is already holding the AIL lock, and done
|
||||
@@ -706,52 +722,36 @@ xfs_trans_ail_update_bulk(
|
||||
* before returning.
|
||||
*/
|
||||
void
|
||||
xfs_trans_ail_delete_bulk(
|
||||
xfs_trans_ail_delete(
|
||||
struct xfs_ail *ailp,
|
||||
struct xfs_log_item **log_items,
|
||||
int nr_items,
|
||||
struct xfs_log_item *lip,
|
||||
int shutdown_type) __releases(ailp->xa_lock)
|
||||
{
|
||||
xfs_log_item_t *mlip;
|
||||
int mlip_changed = 0;
|
||||
int i;
|
||||
struct xfs_mount *mp = ailp->xa_mount;
|
||||
bool mlip_changed;
|
||||
|
||||
mlip = xfs_ail_min(ailp);
|
||||
|
||||
for (i = 0; i < nr_items; i++) {
|
||||
struct xfs_log_item *lip = log_items[i];
|
||||
if (!(lip->li_flags & XFS_LI_IN_AIL)) {
|
||||
struct xfs_mount *mp = ailp->xa_mount;
|
||||
|
||||
spin_unlock(&ailp->xa_lock);
|
||||
if (!XFS_FORCED_SHUTDOWN(mp)) {
|
||||
xfs_alert_tag(mp, XFS_PTAG_AILDELETE,
|
||||
"%s: attempting to delete a log item that is not in the AIL",
|
||||
__func__);
|
||||
xfs_force_shutdown(mp, shutdown_type);
|
||||
}
|
||||
return;
|
||||
if (!(lip->li_flags & XFS_LI_IN_AIL)) {
|
||||
spin_unlock(&ailp->xa_lock);
|
||||
if (!XFS_FORCED_SHUTDOWN(mp)) {
|
||||
xfs_alert_tag(mp, XFS_PTAG_AILDELETE,
|
||||
"%s: attempting to delete a log item that is not in the AIL",
|
||||
__func__);
|
||||
xfs_force_shutdown(mp, shutdown_type);
|
||||
}
|
||||
|
||||
trace_xfs_ail_delete(lip, mlip->li_lsn, lip->li_lsn);
|
||||
xfs_ail_delete(ailp, lip);
|
||||
lip->li_flags &= ~XFS_LI_IN_AIL;
|
||||
lip->li_lsn = 0;
|
||||
if (mlip == lip)
|
||||
mlip_changed = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
mlip_changed = xfs_ail_delete_one(ailp, lip);
|
||||
if (mlip_changed) {
|
||||
if (!XFS_FORCED_SHUTDOWN(ailp->xa_mount))
|
||||
xlog_assign_tail_lsn_locked(ailp->xa_mount);
|
||||
if (!XFS_FORCED_SHUTDOWN(mp))
|
||||
xlog_assign_tail_lsn_locked(mp);
|
||||
if (list_empty(&ailp->xa_ail))
|
||||
wake_up_all(&ailp->xa_empty);
|
||||
spin_unlock(&ailp->xa_lock);
|
||||
|
||||
xfs_log_space_wake(ailp->xa_mount);
|
||||
} else {
|
||||
spin_unlock(&ailp->xa_lock);
|
||||
}
|
||||
|
||||
spin_unlock(&ailp->xa_lock);
|
||||
if (mlip_changed)
|
||||
xfs_log_space_wake(ailp->xa_mount);
|
||||
}
|
||||
|
||||
int
|
||||
|
||||
@@ -63,7 +63,7 @@ xfs_trans_log_finish_bmap_update(
|
||||
int whichfork,
|
||||
xfs_fileoff_t startoff,
|
||||
xfs_fsblock_t startblock,
|
||||
xfs_filblks_t blockcount,
|
||||
xfs_filblks_t *blockcount,
|
||||
xfs_exntst_t state)
|
||||
{
|
||||
int error;
|
||||
@@ -196,16 +196,23 @@ xfs_bmap_update_finish_item(
|
||||
void **state)
|
||||
{
|
||||
struct xfs_bmap_intent *bmap;
|
||||
xfs_filblks_t count;
|
||||
int error;
|
||||
|
||||
bmap = container_of(item, struct xfs_bmap_intent, bi_list);
|
||||
count = bmap->bi_bmap.br_blockcount;
|
||||
error = xfs_trans_log_finish_bmap_update(tp, done_item, dop,
|
||||
bmap->bi_type,
|
||||
bmap->bi_owner, bmap->bi_whichfork,
|
||||
bmap->bi_bmap.br_startoff,
|
||||
bmap->bi_bmap.br_startblock,
|
||||
bmap->bi_bmap.br_blockcount,
|
||||
&count,
|
||||
bmap->bi_bmap.br_state);
|
||||
if (!error && count > 0) {
|
||||
ASSERT(bmap->bi_type == XFS_BMAP_UNMAP);
|
||||
bmap->bi_bmap.br_blockcount = count;
|
||||
return -EAGAIN;
|
||||
}
|
||||
kmem_free(bmap);
|
||||
return error;
|
||||
}
|
||||
|
||||
+62
-36
@@ -356,6 +356,7 @@ xfs_trans_brelse(xfs_trans_t *tp,
|
||||
xfs_buf_t *bp)
|
||||
{
|
||||
xfs_buf_log_item_t *bip;
|
||||
int freed;
|
||||
|
||||
/*
|
||||
* Default to a normal brelse() call if the tp is NULL.
|
||||
@@ -419,16 +420,22 @@ xfs_trans_brelse(xfs_trans_t *tp,
|
||||
/*
|
||||
* Drop our reference to the buf log item.
|
||||
*/
|
||||
atomic_dec(&bip->bli_refcount);
|
||||
freed = atomic_dec_and_test(&bip->bli_refcount);
|
||||
|
||||
/*
|
||||
* If the buf item is not tracking data in the log, then
|
||||
* we must free it before releasing the buffer back to the
|
||||
* free pool. Before releasing the buffer to the free pool,
|
||||
* clear the transaction pointer in b_fsprivate2 to dissolve
|
||||
* its relation to this transaction.
|
||||
* If the buf item is not tracking data in the log, then we must free it
|
||||
* before releasing the buffer back to the free pool.
|
||||
*
|
||||
* If the fs has shutdown and we dropped the last reference, it may fall
|
||||
* on us to release a (possibly dirty) bli if it never made it to the
|
||||
* AIL (e.g., the aborted unpin already happened and didn't release it
|
||||
* due to our reference). Since we're already shutdown and need xa_lock,
|
||||
* just force remove from the AIL and release the bli here.
|
||||
*/
|
||||
if (!xfs_buf_item_dirty(bip)) {
|
||||
if (XFS_FORCED_SHUTDOWN(tp->t_mountp) && freed) {
|
||||
xfs_trans_ail_remove(&bip->bli_item, SHUTDOWN_LOG_IO_ERROR);
|
||||
xfs_buf_item_relse(bp);
|
||||
} else if (!(bip->bli_flags & XFS_BLI_DIRTY)) {
|
||||
/***
|
||||
ASSERT(bp->b_pincount == 0);
|
||||
***/
|
||||
@@ -486,25 +493,17 @@ xfs_trans_bhold_release(xfs_trans_t *tp,
|
||||
}
|
||||
|
||||
/*
|
||||
* This is called to mark bytes first through last inclusive of the given
|
||||
* buffer as needing to be logged when the transaction is committed.
|
||||
* The buffer must already be associated with the given transaction.
|
||||
*
|
||||
* First and last are numbers relative to the beginning of this buffer,
|
||||
* so the first byte in the buffer is numbered 0 regardless of the
|
||||
* value of b_blkno.
|
||||
* Mark a buffer dirty in the transaction.
|
||||
*/
|
||||
void
|
||||
xfs_trans_log_buf(xfs_trans_t *tp,
|
||||
xfs_buf_t *bp,
|
||||
uint first,
|
||||
uint last)
|
||||
xfs_trans_dirty_buf(
|
||||
struct xfs_trans *tp,
|
||||
struct xfs_buf *bp)
|
||||
{
|
||||
xfs_buf_log_item_t *bip = bp->b_fspriv;
|
||||
struct xfs_buf_log_item *bip = bp->b_fspriv;
|
||||
|
||||
ASSERT(bp->b_transp == tp);
|
||||
ASSERT(bip != NULL);
|
||||
ASSERT(first <= last && last < BBTOB(bp->b_length));
|
||||
ASSERT(bp->b_iodone == NULL ||
|
||||
bp->b_iodone == xfs_buf_iodone_callbacks);
|
||||
|
||||
@@ -524,8 +523,6 @@ xfs_trans_log_buf(xfs_trans_t *tp,
|
||||
bp->b_iodone = xfs_buf_iodone_callbacks;
|
||||
bip->bli_item.li_cb = xfs_buf_iodone;
|
||||
|
||||
trace_xfs_trans_log_buf(bip);
|
||||
|
||||
/*
|
||||
* If we invalidated the buffer within this transaction, then
|
||||
* cancel the invalidation now that we're dirtying the buffer
|
||||
@@ -538,17 +535,37 @@ xfs_trans_log_buf(xfs_trans_t *tp,
|
||||
bp->b_flags &= ~XBF_STALE;
|
||||
bip->__bli_format.blf_flags &= ~XFS_BLF_CANCEL;
|
||||
}
|
||||
bip->bli_flags |= XFS_BLI_DIRTY | XFS_BLI_LOGGED;
|
||||
|
||||
tp->t_flags |= XFS_TRANS_DIRTY;
|
||||
bip->bli_item.li_desc->lid_flags |= XFS_LID_DIRTY;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we have an ordered buffer we are not logging any dirty range but
|
||||
* it still needs to be marked dirty and that it has been logged.
|
||||
*/
|
||||
bip->bli_flags |= XFS_BLI_DIRTY | XFS_BLI_LOGGED;
|
||||
if (!(bip->bli_flags & XFS_BLI_ORDERED))
|
||||
xfs_buf_item_log(bip, first, last);
|
||||
/*
|
||||
* This is called to mark bytes first through last inclusive of the given
|
||||
* buffer as needing to be logged when the transaction is committed.
|
||||
* The buffer must already be associated with the given transaction.
|
||||
*
|
||||
* First and last are numbers relative to the beginning of this buffer,
|
||||
* so the first byte in the buffer is numbered 0 regardless of the
|
||||
* value of b_blkno.
|
||||
*/
|
||||
void
|
||||
xfs_trans_log_buf(
|
||||
struct xfs_trans *tp,
|
||||
struct xfs_buf *bp,
|
||||
uint first,
|
||||
uint last)
|
||||
{
|
||||
struct xfs_buf_log_item *bip = bp->b_fspriv;
|
||||
|
||||
ASSERT(first <= last && last < BBTOB(bp->b_length));
|
||||
ASSERT(!(bip->bli_flags & XFS_BLI_ORDERED));
|
||||
|
||||
xfs_trans_dirty_buf(tp, bp);
|
||||
|
||||
trace_xfs_trans_log_buf(bip);
|
||||
xfs_buf_item_log(bip, first, last);
|
||||
}
|
||||
|
||||
|
||||
@@ -701,14 +718,13 @@ xfs_trans_inode_alloc_buf(
|
||||
}
|
||||
|
||||
/*
|
||||
* Mark the buffer as ordered for this transaction. This means
|
||||
* that the contents of the buffer are not recorded in the transaction
|
||||
* but it is tracked in the AIL as though it was. This allows us
|
||||
* to record logical changes in transactions rather than the physical
|
||||
* changes we make to the buffer without changing writeback ordering
|
||||
* constraints of metadata buffers.
|
||||
* Mark the buffer as ordered for this transaction. This means that the contents
|
||||
* of the buffer are not recorded in the transaction but it is tracked in the
|
||||
* AIL as though it was. This allows us to record logical changes in
|
||||
* transactions rather than the physical changes we make to the buffer without
|
||||
* changing writeback ordering constraints of metadata buffers.
|
||||
*/
|
||||
void
|
||||
bool
|
||||
xfs_trans_ordered_buf(
|
||||
struct xfs_trans *tp,
|
||||
struct xfs_buf *bp)
|
||||
@@ -719,8 +735,18 @@ xfs_trans_ordered_buf(
|
||||
ASSERT(bip != NULL);
|
||||
ASSERT(atomic_read(&bip->bli_refcount) > 0);
|
||||
|
||||
if (xfs_buf_item_dirty_format(bip))
|
||||
return false;
|
||||
|
||||
bip->bli_flags |= XFS_BLI_ORDERED;
|
||||
trace_xfs_buf_item_ordered(bip);
|
||||
|
||||
/*
|
||||
* We don't log a dirty range of an ordered buffer but it still needs
|
||||
* to be marked dirty and that it has been logged.
|
||||
*/
|
||||
xfs_trans_dirty_buf(tp, bp);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+34
-12
@@ -106,18 +106,9 @@ xfs_trans_ail_update(
|
||||
xfs_trans_ail_update_bulk(ailp, NULL, &lip, 1, lsn);
|
||||
}
|
||||
|
||||
void xfs_trans_ail_delete_bulk(struct xfs_ail *ailp,
|
||||
struct xfs_log_item **log_items, int nr_items,
|
||||
int shutdown_type)
|
||||
__releases(ailp->xa_lock);
|
||||
static inline void
|
||||
xfs_trans_ail_delete(
|
||||
struct xfs_ail *ailp,
|
||||
xfs_log_item_t *lip,
|
||||
int shutdown_type) __releases(ailp->xa_lock)
|
||||
{
|
||||
xfs_trans_ail_delete_bulk(ailp, &lip, 1, shutdown_type);
|
||||
}
|
||||
bool xfs_ail_delete_one(struct xfs_ail *ailp, struct xfs_log_item *lip);
|
||||
void xfs_trans_ail_delete(struct xfs_ail *ailp, struct xfs_log_item *lip,
|
||||
int shutdown_type) __releases(ailp->xa_lock);
|
||||
|
||||
static inline void
|
||||
xfs_trans_ail_remove(
|
||||
@@ -173,4 +164,35 @@ xfs_trans_ail_copy_lsn(
|
||||
*dst = *src;
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline void
|
||||
xfs_clear_li_failed(
|
||||
struct xfs_log_item *lip)
|
||||
{
|
||||
struct xfs_buf *bp = lip->li_buf;
|
||||
|
||||
ASSERT(lip->li_flags & XFS_LI_IN_AIL);
|
||||
lockdep_assert_held(&lip->li_ailp->xa_lock);
|
||||
|
||||
if (lip->li_flags & XFS_LI_FAILED) {
|
||||
lip->li_flags &= ~XFS_LI_FAILED;
|
||||
lip->li_buf = NULL;
|
||||
xfs_buf_rele(bp);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
xfs_set_li_failed(
|
||||
struct xfs_log_item *lip,
|
||||
struct xfs_buf *bp)
|
||||
{
|
||||
lockdep_assert_held(&lip->li_ailp->xa_lock);
|
||||
|
||||
if (!(lip->li_flags & XFS_LI_FAILED)) {
|
||||
xfs_buf_hold(bp);
|
||||
lip->li_flags |= XFS_LI_FAILED;
|
||||
lip->li_buf = bp;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* __XFS_TRANS_PRIV_H__ */
|
||||
|
||||
@@ -2782,6 +2782,7 @@ static inline void lockdep_annotate_inode_mutex_key(struct inode *inode) { };
|
||||
#endif
|
||||
extern void unlock_new_inode(struct inode *);
|
||||
extern unsigned int get_next_ino(void);
|
||||
extern void evict_inodes(struct super_block *sb);
|
||||
|
||||
extern void __iget(struct inode * inode);
|
||||
extern void iget_failed(struct inode *);
|
||||
|
||||
@@ -3901,6 +3901,8 @@ struct net_device *netdev_all_upper_get_next_dev_rcu(struct net_device *dev,
|
||||
updev; \
|
||||
updev = netdev_all_upper_get_next_dev_rcu(dev, &(iter)))
|
||||
|
||||
bool netdev_has_any_upper_dev(struct net_device *dev);
|
||||
|
||||
void *netdev_lower_get_next_private(struct net_device *dev,
|
||||
struct list_head **iter);
|
||||
void *netdev_lower_get_next_private_rcu(struct net_device *dev,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user