4504 lines
134 KiB
Diff
4504 lines
134 KiB
Diff
diff --git a/Makefile b/Makefile
|
|
index 80bb4fd..e13e4e7 100644
|
|
--- a/Makefile
|
|
+++ b/Makefile
|
|
@@ -1,6 +1,6 @@
|
|
VERSION = 3
|
|
PATCHLEVEL = 2
|
|
-SUBLEVEL = 24
|
|
+SUBLEVEL = 25
|
|
EXTRAVERSION =
|
|
NAME = Saber-toothed Squirrel
|
|
|
|
diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h
|
|
index 559da19..578e5a0 100644
|
|
--- a/arch/powerpc/include/asm/reg.h
|
|
+++ b/arch/powerpc/include/asm/reg.h
|
|
@@ -1016,7 +1016,8 @@
|
|
/* Macros for setting and retrieving special purpose registers */
|
|
#ifndef __ASSEMBLY__
|
|
#define mfmsr() ({unsigned long rval; \
|
|
- asm volatile("mfmsr %0" : "=r" (rval)); rval;})
|
|
+ asm volatile("mfmsr %0" : "=r" (rval) : \
|
|
+ : "memory"); rval;})
|
|
#ifdef CONFIG_PPC_BOOK3S_64
|
|
#define __mtmsrd(v, l) asm volatile("mtmsrd %0," __stringify(l) \
|
|
: : "r" (v) : "memory")
|
|
diff --git a/arch/powerpc/kernel/ftrace.c b/arch/powerpc/kernel/ftrace.c
|
|
index bf99cfa..6324008 100644
|
|
--- a/arch/powerpc/kernel/ftrace.c
|
|
+++ b/arch/powerpc/kernel/ftrace.c
|
|
@@ -245,9 +245,9 @@ __ftrace_make_nop(struct module *mod,
|
|
|
|
/*
|
|
* On PPC32 the trampoline looks like:
|
|
- * 0x3d, 0x60, 0x00, 0x00 lis r11,sym@ha
|
|
- * 0x39, 0x6b, 0x00, 0x00 addi r11,r11,sym@l
|
|
- * 0x7d, 0x69, 0x03, 0xa6 mtctr r11
|
|
+ * 0x3d, 0x80, 0x00, 0x00 lis r12,sym@ha
|
|
+ * 0x39, 0x8c, 0x00, 0x00 addi r12,r12,sym@l
|
|
+ * 0x7d, 0x89, 0x03, 0xa6 mtctr r12
|
|
* 0x4e, 0x80, 0x04, 0x20 bctr
|
|
*/
|
|
|
|
@@ -262,9 +262,9 @@ __ftrace_make_nop(struct module *mod,
|
|
pr_devel(" %08x %08x ", jmp[0], jmp[1]);
|
|
|
|
/* verify that this is what we expect it to be */
|
|
- if (((jmp[0] & 0xffff0000) != 0x3d600000) ||
|
|
- ((jmp[1] & 0xffff0000) != 0x396b0000) ||
|
|
- (jmp[2] != 0x7d6903a6) ||
|
|
+ if (((jmp[0] & 0xffff0000) != 0x3d800000) ||
|
|
+ ((jmp[1] & 0xffff0000) != 0x398c0000) ||
|
|
+ (jmp[2] != 0x7d8903a6) ||
|
|
(jmp[3] != 0x4e800420)) {
|
|
printk(KERN_ERR "Not a trampoline\n");
|
|
return -EINVAL;
|
|
diff --git a/arch/s390/kernel/processor.c b/arch/s390/kernel/processor.c
|
|
index 6e0073e..07c7bf4 100644
|
|
--- a/arch/s390/kernel/processor.c
|
|
+++ b/arch/s390/kernel/processor.c
|
|
@@ -26,12 +26,14 @@ static DEFINE_PER_CPU(struct cpuid, cpu_id);
|
|
void __cpuinit cpu_init(void)
|
|
{
|
|
struct cpuid *id = &per_cpu(cpu_id, smp_processor_id());
|
|
+ struct s390_idle_data *idle = &__get_cpu_var(s390_idle);
|
|
|
|
get_cpu_id(id);
|
|
atomic_inc(&init_mm.mm_count);
|
|
current->active_mm = &init_mm;
|
|
BUG_ON(current->mm);
|
|
enter_lazy_tlb(&init_mm, current);
|
|
+ memset(idle, 0, sizeof(*idle));
|
|
}
|
|
|
|
/*
|
|
diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c
|
|
index 3ea8728..1df64a8 100644
|
|
--- a/arch/s390/kernel/smp.c
|
|
+++ b/arch/s390/kernel/smp.c
|
|
@@ -1020,14 +1020,11 @@ static int __cpuinit smp_cpu_notify(struct notifier_block *self,
|
|
unsigned int cpu = (unsigned int)(long)hcpu;
|
|
struct cpu *c = &per_cpu(cpu_devices, cpu);
|
|
struct sys_device *s = &c->sysdev;
|
|
- struct s390_idle_data *idle;
|
|
int err = 0;
|
|
|
|
switch (action) {
|
|
case CPU_ONLINE:
|
|
case CPU_ONLINE_FROZEN:
|
|
- idle = &per_cpu(s390_idle, cpu);
|
|
- memset(idle, 0, sizeof(struct s390_idle_data));
|
|
err = sysfs_create_group(&s->kobj, &cpu_online_attr_group);
|
|
break;
|
|
case CPU_DEAD:
|
|
diff --git a/arch/x86/kernel/microcode_core.c b/arch/x86/kernel/microcode_core.c
|
|
index 563a09d..29c95d7 100644
|
|
--- a/arch/x86/kernel/microcode_core.c
|
|
+++ b/arch/x86/kernel/microcode_core.c
|
|
@@ -297,20 +297,31 @@ static ssize_t reload_store(struct sys_device *dev,
|
|
const char *buf, size_t size)
|
|
{
|
|
unsigned long val;
|
|
- int cpu = dev->id;
|
|
- int ret = 0;
|
|
- char *end;
|
|
+ int cpu;
|
|
+ ssize_t ret = 0, tmp_ret;
|
|
|
|
- val = simple_strtoul(buf, &end, 0);
|
|
- if (end == buf)
|
|
+ /* allow reload only from the BSP */
|
|
+ if (boot_cpu_data.cpu_index != dev->id)
|
|
return -EINVAL;
|
|
|
|
- if (val == 1) {
|
|
- get_online_cpus();
|
|
- if (cpu_online(cpu))
|
|
- ret = reload_for_cpu(cpu);
|
|
- put_online_cpus();
|
|
+ ret = kstrtoul(buf, 0, &val);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ if (val != 1)
|
|
+ return size;
|
|
+
|
|
+ get_online_cpus();
|
|
+ for_each_online_cpu(cpu) {
|
|
+ tmp_ret = reload_for_cpu(cpu);
|
|
+ if (tmp_ret != 0)
|
|
+ pr_warn("Error reloading microcode on CPU %d\n", cpu);
|
|
+
|
|
+ /* save retval of the first encountered reload error */
|
|
+ if (!ret)
|
|
+ ret = tmp_ret;
|
|
}
|
|
+ put_online_cpus();
|
|
|
|
if (!ret)
|
|
ret = size;
|
|
diff --git a/arch/x86/pci/fixup.c b/arch/x86/pci/fixup.c
|
|
index 6dd8955..0951b81 100644
|
|
--- a/arch/x86/pci/fixup.c
|
|
+++ b/arch/x86/pci/fixup.c
|
|
@@ -521,3 +521,20 @@ static void sb600_disable_hpet_bar(struct pci_dev *dev)
|
|
}
|
|
}
|
|
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_ATI, 0x4385, sb600_disable_hpet_bar);
|
|
+
|
|
+/*
|
|
+ * Twinhead H12Y needs us to block out a region otherwise we map devices
|
|
+ * there and any access kills the box.
|
|
+ *
|
|
+ * See: https://bugzilla.kernel.org/show_bug.cgi?id=10231
|
|
+ *
|
|
+ * Match off the LPC and svid/sdid (older kernels lose the bridge subvendor)
|
|
+ */
|
|
+static void __devinit twinhead_reserve_killing_zone(struct pci_dev *dev)
|
|
+{
|
|
+ if (dev->subsystem_vendor == 0x14FF && dev->subsystem_device == 0xA003) {
|
|
+ pr_info("Reserving memory on Twinhead H12Y\n");
|
|
+ request_mem_region(0xFFB00000, 0x100000, "twinhead");
|
|
+ }
|
|
+}
|
|
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x27B9, twinhead_reserve_killing_zone);
|
|
diff --git a/block/blk-core.c b/block/blk-core.c
|
|
index 15de223..49d9e91 100644
|
|
--- a/block/blk-core.c
|
|
+++ b/block/blk-core.c
|
|
@@ -607,7 +607,7 @@ EXPORT_SYMBOL(blk_init_allocated_queue);
|
|
|
|
int blk_get_queue(struct request_queue *q)
|
|
{
|
|
- if (likely(!test_bit(QUEUE_FLAG_DEAD, &q->queue_flags))) {
|
|
+ if (likely(!blk_queue_dead(q))) {
|
|
kobject_get(&q->kobj);
|
|
return 0;
|
|
}
|
|
@@ -754,7 +754,7 @@ static struct request *get_request(struct request_queue *q, int rw_flags,
|
|
const bool is_sync = rw_is_sync(rw_flags) != 0;
|
|
int may_queue;
|
|
|
|
- if (unlikely(test_bit(QUEUE_FLAG_DEAD, &q->queue_flags)))
|
|
+ if (unlikely(blk_queue_dead(q)))
|
|
return NULL;
|
|
|
|
may_queue = elv_may_queue(q, rw_flags);
|
|
@@ -874,7 +874,7 @@ static struct request *get_request_wait(struct request_queue *q, int rw_flags,
|
|
struct io_context *ioc;
|
|
struct request_list *rl = &q->rq;
|
|
|
|
- if (unlikely(test_bit(QUEUE_FLAG_DEAD, &q->queue_flags)))
|
|
+ if (unlikely(blk_queue_dead(q)))
|
|
return NULL;
|
|
|
|
prepare_to_wait_exclusive(&rl->wait[is_sync], &wait,
|
|
diff --git a/block/blk-exec.c b/block/blk-exec.c
|
|
index a1ebceb..6053285 100644
|
|
--- a/block/blk-exec.c
|
|
+++ b/block/blk-exec.c
|
|
@@ -50,7 +50,7 @@ void blk_execute_rq_nowait(struct request_queue *q, struct gendisk *bd_disk,
|
|
{
|
|
int where = at_head ? ELEVATOR_INSERT_FRONT : ELEVATOR_INSERT_BACK;
|
|
|
|
- if (unlikely(test_bit(QUEUE_FLAG_DEAD, &q->queue_flags))) {
|
|
+ if (unlikely(blk_queue_dead(q))) {
|
|
rq->errors = -ENXIO;
|
|
if (rq->end_io)
|
|
rq->end_io(rq, rq->errors);
|
|
diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c
|
|
index e7f9f65..f0b2ca8 100644
|
|
--- a/block/blk-sysfs.c
|
|
+++ b/block/blk-sysfs.c
|
|
@@ -425,7 +425,7 @@ queue_attr_show(struct kobject *kobj, struct attribute *attr, char *page)
|
|
if (!entry->show)
|
|
return -EIO;
|
|
mutex_lock(&q->sysfs_lock);
|
|
- if (test_bit(QUEUE_FLAG_DEAD, &q->queue_flags)) {
|
|
+ if (blk_queue_dead(q)) {
|
|
mutex_unlock(&q->sysfs_lock);
|
|
return -ENOENT;
|
|
}
|
|
@@ -447,7 +447,7 @@ queue_attr_store(struct kobject *kobj, struct attribute *attr,
|
|
|
|
q = container_of(kobj, struct request_queue, kobj);
|
|
mutex_lock(&q->sysfs_lock);
|
|
- if (test_bit(QUEUE_FLAG_DEAD, &q->queue_flags)) {
|
|
+ if (blk_queue_dead(q)) {
|
|
mutex_unlock(&q->sysfs_lock);
|
|
return -ENOENT;
|
|
}
|
|
diff --git a/block/blk-throttle.c b/block/blk-throttle.c
|
|
index 4553245..5eed6a7 100644
|
|
--- a/block/blk-throttle.c
|
|
+++ b/block/blk-throttle.c
|
|
@@ -310,7 +310,7 @@ static struct throtl_grp * throtl_get_tg(struct throtl_data *td)
|
|
struct request_queue *q = td->queue;
|
|
|
|
/* no throttling for dead queue */
|
|
- if (unlikely(test_bit(QUEUE_FLAG_DEAD, &q->queue_flags)))
|
|
+ if (unlikely(blk_queue_dead(q)))
|
|
return NULL;
|
|
|
|
rcu_read_lock();
|
|
@@ -335,7 +335,7 @@ static struct throtl_grp * throtl_get_tg(struct throtl_data *td)
|
|
spin_lock_irq(q->queue_lock);
|
|
|
|
/* Make sure @q is still alive */
|
|
- if (unlikely(test_bit(QUEUE_FLAG_DEAD, &q->queue_flags))) {
|
|
+ if (unlikely(blk_queue_dead(q))) {
|
|
kfree(tg);
|
|
return NULL;
|
|
}
|
|
diff --git a/block/blk.h b/block/blk.h
|
|
index 3f6551b..e38691d 100644
|
|
--- a/block/blk.h
|
|
+++ b/block/blk.h
|
|
@@ -85,7 +85,7 @@ static inline struct request *__elv_next_request(struct request_queue *q)
|
|
q->flush_queue_delayed = 1;
|
|
return NULL;
|
|
}
|
|
- if (test_bit(QUEUE_FLAG_DEAD, &q->queue_flags) ||
|
|
+ if (unlikely(blk_queue_dead(q)) ||
|
|
!q->elevator->ops->elevator_dispatch_fn(q, 0))
|
|
return NULL;
|
|
}
|
|
diff --git a/drivers/acpi/ac.c b/drivers/acpi/ac.c
|
|
index 6512b20..d1fcbc0 100644
|
|
--- a/drivers/acpi/ac.c
|
|
+++ b/drivers/acpi/ac.c
|
|
@@ -292,7 +292,9 @@ static int acpi_ac_add(struct acpi_device *device)
|
|
ac->charger.properties = ac_props;
|
|
ac->charger.num_properties = ARRAY_SIZE(ac_props);
|
|
ac->charger.get_property = get_ac_property;
|
|
- power_supply_register(&ac->device->dev, &ac->charger);
|
|
+ result = power_supply_register(&ac->device->dev, &ac->charger);
|
|
+ if (result)
|
|
+ goto end;
|
|
|
|
printk(KERN_INFO PREFIX "%s [%s] (%s)\n",
|
|
acpi_device_name(device), acpi_device_bid(device),
|
|
diff --git a/drivers/gpu/drm/nouveau/nva3_copy.fuc b/drivers/gpu/drm/nouveau/nva3_copy.fuc
|
|
index eaf35f8..d894731 100644
|
|
--- a/drivers/gpu/drm/nouveau/nva3_copy.fuc
|
|
+++ b/drivers/gpu/drm/nouveau/nva3_copy.fuc
|
|
@@ -118,9 +118,9 @@ dispatch_dma:
|
|
// mthd 0x030c-0x0340, various stuff
|
|
.b16 0xc3 14
|
|
.b32 ctx_src_address_high ~0x000000ff
|
|
-.b32 ctx_src_address_low ~0xfffffff0
|
|
+.b32 ctx_src_address_low ~0xffffffff
|
|
.b32 ctx_dst_address_high ~0x000000ff
|
|
-.b32 ctx_dst_address_low ~0xfffffff0
|
|
+.b32 ctx_dst_address_low ~0xffffffff
|
|
.b32 ctx_src_pitch ~0x0007ffff
|
|
.b32 ctx_dst_pitch ~0x0007ffff
|
|
.b32 ctx_xcnt ~0x0000ffff
|
|
diff --git a/drivers/gpu/drm/nouveau/nva3_copy.fuc.h b/drivers/gpu/drm/nouveau/nva3_copy.fuc.h
|
|
index 2731de2..e2a0e88 100644
|
|
--- a/drivers/gpu/drm/nouveau/nva3_copy.fuc.h
|
|
+++ b/drivers/gpu/drm/nouveau/nva3_copy.fuc.h
|
|
@@ -1,37 +1,72 @@
|
|
-uint32_t nva3_pcopy_data[] = {
|
|
+u32 nva3_pcopy_data[] = {
|
|
+/* 0x0000: ctx_object */
|
|
0x00000000,
|
|
+/* 0x0004: ctx_dma */
|
|
+/* 0x0004: ctx_dma_query */
|
|
0x00000000,
|
|
+/* 0x0008: ctx_dma_src */
|
|
0x00000000,
|
|
+/* 0x000c: ctx_dma_dst */
|
|
0x00000000,
|
|
+/* 0x0010: ctx_query_address_high */
|
|
0x00000000,
|
|
+/* 0x0014: ctx_query_address_low */
|
|
0x00000000,
|
|
+/* 0x0018: ctx_query_counter */
|
|
0x00000000,
|
|
+/* 0x001c: ctx_src_address_high */
|
|
0x00000000,
|
|
+/* 0x0020: ctx_src_address_low */
|
|
0x00000000,
|
|
+/* 0x0024: ctx_src_pitch */
|
|
0x00000000,
|
|
+/* 0x0028: ctx_src_tile_mode */
|
|
0x00000000,
|
|
+/* 0x002c: ctx_src_xsize */
|
|
0x00000000,
|
|
+/* 0x0030: ctx_src_ysize */
|
|
0x00000000,
|
|
+/* 0x0034: ctx_src_zsize */
|
|
0x00000000,
|
|
+/* 0x0038: ctx_src_zoff */
|
|
0x00000000,
|
|
+/* 0x003c: ctx_src_xoff */
|
|
0x00000000,
|
|
+/* 0x0040: ctx_src_yoff */
|
|
0x00000000,
|
|
+/* 0x0044: ctx_src_cpp */
|
|
0x00000000,
|
|
+/* 0x0048: ctx_dst_address_high */
|
|
0x00000000,
|
|
+/* 0x004c: ctx_dst_address_low */
|
|
0x00000000,
|
|
+/* 0x0050: ctx_dst_pitch */
|
|
0x00000000,
|
|
+/* 0x0054: ctx_dst_tile_mode */
|
|
0x00000000,
|
|
+/* 0x0058: ctx_dst_xsize */
|
|
0x00000000,
|
|
+/* 0x005c: ctx_dst_ysize */
|
|
0x00000000,
|
|
+/* 0x0060: ctx_dst_zsize */
|
|
0x00000000,
|
|
+/* 0x0064: ctx_dst_zoff */
|
|
0x00000000,
|
|
+/* 0x0068: ctx_dst_xoff */
|
|
0x00000000,
|
|
+/* 0x006c: ctx_dst_yoff */
|
|
0x00000000,
|
|
+/* 0x0070: ctx_dst_cpp */
|
|
0x00000000,
|
|
+/* 0x0074: ctx_format */
|
|
0x00000000,
|
|
+/* 0x0078: ctx_swz_const0 */
|
|
0x00000000,
|
|
+/* 0x007c: ctx_swz_const1 */
|
|
0x00000000,
|
|
+/* 0x0080: ctx_xcnt */
|
|
0x00000000,
|
|
+/* 0x0084: ctx_ycnt */
|
|
0x00000000,
|
|
0x00000000,
|
|
0x00000000,
|
|
@@ -63,6 +98,7 @@ uint32_t nva3_pcopy_data[] = {
|
|
0x00000000,
|
|
0x00000000,
|
|
0x00000000,
|
|
+/* 0x0100: dispatch_table */
|
|
0x00010000,
|
|
0x00000000,
|
|
0x00000000,
|
|
@@ -73,6 +109,7 @@ uint32_t nva3_pcopy_data[] = {
|
|
0x00010162,
|
|
0x00000000,
|
|
0x00030060,
|
|
+/* 0x0128: dispatch_dma */
|
|
0x00010170,
|
|
0x00000000,
|
|
0x00010170,
|
|
@@ -118,11 +155,11 @@ uint32_t nva3_pcopy_data[] = {
|
|
0x0000001c,
|
|
0xffffff00,
|
|
0x00000020,
|
|
- 0x0000000f,
|
|
+ 0x00000000,
|
|
0x00000048,
|
|
0xffffff00,
|
|
0x0000004c,
|
|
- 0x0000000f,
|
|
+ 0x00000000,
|
|
0x00000024,
|
|
0xfff80000,
|
|
0x00000050,
|
|
@@ -146,7 +183,8 @@ uint32_t nva3_pcopy_data[] = {
|
|
0x00000800,
|
|
};
|
|
|
|
-uint32_t nva3_pcopy_code[] = {
|
|
+u32 nva3_pcopy_code[] = {
|
|
+/* 0x0000: main */
|
|
0x04fe04bd,
|
|
0x3517f000,
|
|
0xf10010fe,
|
|
@@ -158,23 +196,31 @@ uint32_t nva3_pcopy_code[] = {
|
|
0x17f11031,
|
|
0x27f01200,
|
|
0x0012d003,
|
|
+/* 0x002f: spin */
|
|
0xf40031f4,
|
|
0x0ef40028,
|
|
+/* 0x0035: ih */
|
|
0x8001cffd,
|
|
0xf40812c4,
|
|
0x21f4060b,
|
|
+/* 0x0041: ih_no_chsw */
|
|
0x0412c472,
|
|
0xf4060bf4,
|
|
+/* 0x004a: ih_no_cmd */
|
|
0x11c4c321,
|
|
0x4001d00c,
|
|
+/* 0x0052: swctx */
|
|
0x47f101f8,
|
|
0x4bfe7700,
|
|
0x0007fe00,
|
|
0xf00204b9,
|
|
0x01f40643,
|
|
0x0604fa09,
|
|
+/* 0x006b: swctx_load */
|
|
0xfa060ef4,
|
|
+/* 0x006e: swctx_done */
|
|
0x03f80504,
|
|
+/* 0x0072: chsw */
|
|
0x27f100f8,
|
|
0x23cf1400,
|
|
0x1e3fc800,
|
|
@@ -183,18 +229,22 @@ uint32_t nva3_pcopy_code[] = {
|
|
0x1e3af052,
|
|
0xf00023d0,
|
|
0x24d00147,
|
|
+/* 0x0093: chsw_no_unload */
|
|
0xcf00f880,
|
|
0x3dc84023,
|
|
0x220bf41e,
|
|
0xf40131f4,
|
|
0x57f05221,
|
|
0x0367f004,
|
|
+/* 0x00a8: chsw_load_ctx_dma */
|
|
0xa07856bc,
|
|
0xb6018068,
|
|
0x87d00884,
|
|
0x0162b600,
|
|
+/* 0x00bb: chsw_finish_load */
|
|
0xf0f018f4,
|
|
0x23d00237,
|
|
+/* 0x00c3: dispatch */
|
|
0xf100f880,
|
|
0xcf190037,
|
|
0x33cf4032,
|
|
@@ -202,6 +252,7 @@ uint32_t nva3_pcopy_code[] = {
|
|
0x1024b607,
|
|
0x010057f1,
|
|
0x74bd64bd,
|
|
+/* 0x00dc: dispatch_loop */
|
|
0x58005658,
|
|
0x50b60157,
|
|
0x0446b804,
|
|
@@ -211,6 +262,7 @@ uint32_t nva3_pcopy_code[] = {
|
|
0xb60276bb,
|
|
0x57bb0374,
|
|
0xdf0ef400,
|
|
+/* 0x0100: dispatch_valid_mthd */
|
|
0xb60246bb,
|
|
0x45bb0344,
|
|
0x01459800,
|
|
@@ -220,31 +272,41 @@ uint32_t nva3_pcopy_code[] = {
|
|
0xb0014658,
|
|
0x1bf40064,
|
|
0x00538009,
|
|
+/* 0x0127: dispatch_cmd */
|
|
0xf4300ef4,
|
|
0x55f90132,
|
|
0xf40c01f4,
|
|
+/* 0x0132: dispatch_invalid_bitfield */
|
|
0x25f0250e,
|
|
+/* 0x0135: dispatch_illegal_mthd */
|
|
0x0125f002,
|
|
+/* 0x0138: dispatch_error */
|
|
0x100047f1,
|
|
0xd00042d0,
|
|
0x27f04043,
|
|
0x0002d040,
|
|
+/* 0x0148: hostirq_wait */
|
|
0xf08002cf,
|
|
0x24b04024,
|
|
0xf71bf400,
|
|
+/* 0x0154: dispatch_done */
|
|
0x1d0027f1,
|
|
0xd00137f0,
|
|
0x00f80023,
|
|
+/* 0x0160: cmd_nop */
|
|
+/* 0x0162: cmd_pm_trigger */
|
|
0x27f100f8,
|
|
0x34bd2200,
|
|
0xd00233f0,
|
|
0x00f80023,
|
|
+/* 0x0170: cmd_dma */
|
|
0x012842b7,
|
|
0xf00145b6,
|
|
0x43801e39,
|
|
0x0040b701,
|
|
0x0644b606,
|
|
0xf80043d0,
|
|
+/* 0x0189: cmd_exec_set_format */
|
|
0xf030f400,
|
|
0xb00001b0,
|
|
0x01b00101,
|
|
@@ -256,20 +318,26 @@ uint32_t nva3_pcopy_code[] = {
|
|
0x70b63847,
|
|
0x0232f401,
|
|
0x94bd84bd,
|
|
+/* 0x01b4: ncomp_loop */
|
|
0xb60f4ac4,
|
|
0xb4bd0445,
|
|
+/* 0x01bc: bpc_loop */
|
|
0xf404a430,
|
|
0xa5ff0f18,
|
|
0x00cbbbc0,
|
|
0xf40231f4,
|
|
+/* 0x01ce: cmp_c0 */
|
|
0x1bf4220e,
|
|
0x10c7f00c,
|
|
0xf400cbbb,
|
|
+/* 0x01da: cmp_c1 */
|
|
0xa430160e,
|
|
0x0c18f406,
|
|
0xbb14c7f0,
|
|
0x0ef400cb,
|
|
+/* 0x01e9: cmp_zero */
|
|
0x80c7f107,
|
|
+/* 0x01ed: bpc_next */
|
|
0x01c83800,
|
|
0xb60180b6,
|
|
0xb5b801b0,
|
|
@@ -280,6 +348,7 @@ uint32_t nva3_pcopy_code[] = {
|
|
0x98110680,
|
|
0x68fd2008,
|
|
0x0502f400,
|
|
+/* 0x0216: dst_xcnt */
|
|
0x75fd64bd,
|
|
0x1c078000,
|
|
0xf10078fd,
|
|
@@ -304,6 +373,7 @@ uint32_t nva3_pcopy_code[] = {
|
|
0x980056d0,
|
|
0x56d01f06,
|
|
0x1030f440,
|
|
+/* 0x0276: cmd_exec_set_surface_tiled */
|
|
0x579800f8,
|
|
0x6879c70a,
|
|
0xb66478c7,
|
|
@@ -311,9 +381,11 @@ uint32_t nva3_pcopy_code[] = {
|
|
0x0e76b060,
|
|
0xf0091bf4,
|
|
0x0ef40477,
|
|
+/* 0x0291: xtile64 */
|
|
0x027cf00f,
|
|
0xfd1170b6,
|
|
0x77f00947,
|
|
+/* 0x029d: xtileok */
|
|
0x0f5a9806,
|
|
0xfd115b98,
|
|
0xb7f000ab,
|
|
@@ -371,6 +443,7 @@ uint32_t nva3_pcopy_code[] = {
|
|
0x67d00600,
|
|
0x0060b700,
|
|
0x0068d004,
|
|
+/* 0x0382: cmd_exec_set_surface_linear */
|
|
0x6cf000f8,
|
|
0x0260b702,
|
|
0x0864b602,
|
|
@@ -381,13 +454,16 @@ uint32_t nva3_pcopy_code[] = {
|
|
0xb70067d0,
|
|
0x98040060,
|
|
0x67d00957,
|
|
+/* 0x03ab: cmd_exec_wait */
|
|
0xf900f800,
|
|
0xf110f900,
|
|
0xb6080007,
|
|
+/* 0x03b6: loop */
|
|
0x01cf0604,
|
|
0x0114f000,
|
|
0xfcfa1bf4,
|
|
0xf800fc10,
|
|
+/* 0x03c5: cmd_exec_query */
|
|
0x0d34c800,
|
|
0xf5701bf4,
|
|
0xf103ab21,
|
|
@@ -417,6 +493,7 @@ uint32_t nva3_pcopy_code[] = {
|
|
0x47f10153,
|
|
0x44b60800,
|
|
0x0045d006,
|
|
+/* 0x0438: query_counter */
|
|
0x03ab21f5,
|
|
0x080c47f1,
|
|
0x980644b6,
|
|
@@ -439,11 +516,13 @@ uint32_t nva3_pcopy_code[] = {
|
|
0x47f10153,
|
|
0x44b60800,
|
|
0x0045d006,
|
|
+/* 0x0492: cmd_exec */
|
|
0x21f500f8,
|
|
0x3fc803ab,
|
|
0x0e0bf400,
|
|
0x018921f5,
|
|
0x020047f1,
|
|
+/* 0x04a7: cmd_exec_no_format */
|
|
0xf11e0ef4,
|
|
0xb6081067,
|
|
0x77f00664,
|
|
@@ -451,19 +530,24 @@ uint32_t nva3_pcopy_code[] = {
|
|
0x981c0780,
|
|
0x67d02007,
|
|
0x4067d000,
|
|
+/* 0x04c2: cmd_exec_init_src_surface */
|
|
0x32f444bd,
|
|
0xc854bd02,
|
|
0x0bf4043f,
|
|
0x8221f50a,
|
|
0x0a0ef403,
|
|
+/* 0x04d4: src_tiled */
|
|
0x027621f5,
|
|
+/* 0x04db: cmd_exec_init_dst_surface */
|
|
0xf40749f0,
|
|
0x57f00231,
|
|
0x083fc82c,
|
|
0xf50a0bf4,
|
|
0xf4038221,
|
|
+/* 0x04ee: dst_tiled */
|
|
0x21f50a0e,
|
|
0x49f00276,
|
|
+/* 0x04f5: cmd_exec_kick */
|
|
0x0057f108,
|
|
0x0654b608,
|
|
0xd0210698,
|
|
@@ -473,6 +557,8 @@ uint32_t nva3_pcopy_code[] = {
|
|
0xc80054d0,
|
|
0x0bf40c3f,
|
|
0xc521f507,
|
|
+/* 0x0519: cmd_exec_done */
|
|
+/* 0x051b: cmd_wrcache_flush */
|
|
0xf100f803,
|
|
0xbd220027,
|
|
0x0133f034,
|
|
diff --git a/drivers/gpu/drm/nouveau/nvc0_copy.fuc.h b/drivers/gpu/drm/nouveau/nvc0_copy.fuc.h
|
|
index 4199038..9e87036 100644
|
|
--- a/drivers/gpu/drm/nouveau/nvc0_copy.fuc.h
|
|
+++ b/drivers/gpu/drm/nouveau/nvc0_copy.fuc.h
|
|
@@ -1,34 +1,65 @@
|
|
-uint32_t nvc0_pcopy_data[] = {
|
|
+u32 nvc0_pcopy_data[] = {
|
|
+/* 0x0000: ctx_object */
|
|
0x00000000,
|
|
+/* 0x0004: ctx_query_address_high */
|
|
0x00000000,
|
|
+/* 0x0008: ctx_query_address_low */
|
|
0x00000000,
|
|
+/* 0x000c: ctx_query_counter */
|
|
0x00000000,
|
|
+/* 0x0010: ctx_src_address_high */
|
|
0x00000000,
|
|
+/* 0x0014: ctx_src_address_low */
|
|
0x00000000,
|
|
+/* 0x0018: ctx_src_pitch */
|
|
0x00000000,
|
|
+/* 0x001c: ctx_src_tile_mode */
|
|
0x00000000,
|
|
+/* 0x0020: ctx_src_xsize */
|
|
0x00000000,
|
|
+/* 0x0024: ctx_src_ysize */
|
|
0x00000000,
|
|
+/* 0x0028: ctx_src_zsize */
|
|
0x00000000,
|
|
+/* 0x002c: ctx_src_zoff */
|
|
0x00000000,
|
|
+/* 0x0030: ctx_src_xoff */
|
|
0x00000000,
|
|
+/* 0x0034: ctx_src_yoff */
|
|
0x00000000,
|
|
+/* 0x0038: ctx_src_cpp */
|
|
0x00000000,
|
|
+/* 0x003c: ctx_dst_address_high */
|
|
0x00000000,
|
|
+/* 0x0040: ctx_dst_address_low */
|
|
0x00000000,
|
|
+/* 0x0044: ctx_dst_pitch */
|
|
0x00000000,
|
|
+/* 0x0048: ctx_dst_tile_mode */
|
|
0x00000000,
|
|
+/* 0x004c: ctx_dst_xsize */
|
|
0x00000000,
|
|
+/* 0x0050: ctx_dst_ysize */
|
|
0x00000000,
|
|
+/* 0x0054: ctx_dst_zsize */
|
|
0x00000000,
|
|
+/* 0x0058: ctx_dst_zoff */
|
|
0x00000000,
|
|
+/* 0x005c: ctx_dst_xoff */
|
|
0x00000000,
|
|
+/* 0x0060: ctx_dst_yoff */
|
|
0x00000000,
|
|
+/* 0x0064: ctx_dst_cpp */
|
|
0x00000000,
|
|
+/* 0x0068: ctx_format */
|
|
0x00000000,
|
|
+/* 0x006c: ctx_swz_const0 */
|
|
0x00000000,
|
|
+/* 0x0070: ctx_swz_const1 */
|
|
0x00000000,
|
|
+/* 0x0074: ctx_xcnt */
|
|
0x00000000,
|
|
+/* 0x0078: ctx_ycnt */
|
|
0x00000000,
|
|
0x00000000,
|
|
0x00000000,
|
|
@@ -63,6 +94,7 @@ uint32_t nvc0_pcopy_data[] = {
|
|
0x00000000,
|
|
0x00000000,
|
|
0x00000000,
|
|
+/* 0x0100: dispatch_table */
|
|
0x00010000,
|
|
0x00000000,
|
|
0x00000000,
|
|
@@ -111,11 +143,11 @@ uint32_t nvc0_pcopy_data[] = {
|
|
0x00000010,
|
|
0xffffff00,
|
|
0x00000014,
|
|
- 0x0000000f,
|
|
+ 0x00000000,
|
|
0x0000003c,
|
|
0xffffff00,
|
|
0x00000040,
|
|
- 0x0000000f,
|
|
+ 0x00000000,
|
|
0x00000018,
|
|
0xfff80000,
|
|
0x00000044,
|
|
@@ -139,7 +171,8 @@ uint32_t nvc0_pcopy_data[] = {
|
|
0x00000800,
|
|
};
|
|
|
|
-uint32_t nvc0_pcopy_code[] = {
|
|
+u32 nvc0_pcopy_code[] = {
|
|
+/* 0x0000: main */
|
|
0x04fe04bd,
|
|
0x3517f000,
|
|
0xf10010fe,
|
|
@@ -151,15 +184,20 @@ uint32_t nvc0_pcopy_code[] = {
|
|
0x17f11031,
|
|
0x27f01200,
|
|
0x0012d003,
|
|
+/* 0x002f: spin */
|
|
0xf40031f4,
|
|
0x0ef40028,
|
|
+/* 0x0035: ih */
|
|
0x8001cffd,
|
|
0xf40812c4,
|
|
0x21f4060b,
|
|
+/* 0x0041: ih_no_chsw */
|
|
0x0412c4ca,
|
|
0xf5070bf4,
|
|
+/* 0x004b: ih_no_cmd */
|
|
0xc4010221,
|
|
0x01d00c11,
|
|
+/* 0x0053: swctx */
|
|
0xf101f840,
|
|
0xfe770047,
|
|
0x47f1004b,
|
|
@@ -188,8 +226,11 @@ uint32_t nvc0_pcopy_code[] = {
|
|
0xf00204b9,
|
|
0x01f40643,
|
|
0x0604fa09,
|
|
+/* 0x00c3: swctx_load */
|
|
0xfa060ef4,
|
|
+/* 0x00c6: swctx_done */
|
|
0x03f80504,
|
|
+/* 0x00ca: chsw */
|
|
0x27f100f8,
|
|
0x23cf1400,
|
|
0x1e3fc800,
|
|
@@ -198,18 +239,22 @@ uint32_t nvc0_pcopy_code[] = {
|
|
0x1e3af053,
|
|
0xf00023d0,
|
|
0x24d00147,
|
|
+/* 0x00eb: chsw_no_unload */
|
|
0xcf00f880,
|
|
0x3dc84023,
|
|
0x090bf41e,
|
|
0xf40131f4,
|
|
+/* 0x00fa: chsw_finish_load */
|
|
0x37f05321,
|
|
0x8023d002,
|
|
+/* 0x0102: dispatch */
|
|
0x37f100f8,
|
|
0x32cf1900,
|
|
0x0033cf40,
|
|
0x07ff24e4,
|
|
0xf11024b6,
|
|
0xbd010057,
|
|
+/* 0x011b: dispatch_loop */
|
|
0x5874bd64,
|
|
0x57580056,
|
|
0x0450b601,
|
|
@@ -219,6 +264,7 @@ uint32_t nvc0_pcopy_code[] = {
|
|
0xbb0f08f4,
|
|
0x74b60276,
|
|
0x0057bb03,
|
|
+/* 0x013f: dispatch_valid_mthd */
|
|
0xbbdf0ef4,
|
|
0x44b60246,
|
|
0x0045bb03,
|
|
@@ -229,24 +275,33 @@ uint32_t nvc0_pcopy_code[] = {
|
|
0x64b00146,
|
|
0x091bf400,
|
|
0xf4005380,
|
|
+/* 0x0166: dispatch_cmd */
|
|
0x32f4300e,
|
|
0xf455f901,
|
|
0x0ef40c01,
|
|
+/* 0x0171: dispatch_invalid_bitfield */
|
|
0x0225f025,
|
|
+/* 0x0174: dispatch_illegal_mthd */
|
|
+/* 0x0177: dispatch_error */
|
|
0xf10125f0,
|
|
0xd0100047,
|
|
0x43d00042,
|
|
0x4027f040,
|
|
+/* 0x0187: hostirq_wait */
|
|
0xcf0002d0,
|
|
0x24f08002,
|
|
0x0024b040,
|
|
+/* 0x0193: dispatch_done */
|
|
0xf1f71bf4,
|
|
0xf01d0027,
|
|
0x23d00137,
|
|
+/* 0x019f: cmd_nop */
|
|
0xf800f800,
|
|
+/* 0x01a1: cmd_pm_trigger */
|
|
0x0027f100,
|
|
0xf034bd22,
|
|
0x23d00233,
|
|
+/* 0x01af: cmd_exec_set_format */
|
|
0xf400f800,
|
|
0x01b0f030,
|
|
0x0101b000,
|
|
@@ -258,20 +313,26 @@ uint32_t nvc0_pcopy_code[] = {
|
|
0x3847c701,
|
|
0xf40170b6,
|
|
0x84bd0232,
|
|
+/* 0x01da: ncomp_loop */
|
|
0x4ac494bd,
|
|
0x0445b60f,
|
|
+/* 0x01e2: bpc_loop */
|
|
0xa430b4bd,
|
|
0x0f18f404,
|
|
0xbbc0a5ff,
|
|
0x31f400cb,
|
|
0x220ef402,
|
|
+/* 0x01f4: cmp_c0 */
|
|
0xf00c1bf4,
|
|
0xcbbb10c7,
|
|
0x160ef400,
|
|
+/* 0x0200: cmp_c1 */
|
|
0xf406a430,
|
|
0xc7f00c18,
|
|
0x00cbbb14,
|
|
+/* 0x020f: cmp_zero */
|
|
0xf1070ef4,
|
|
+/* 0x0213: bpc_next */
|
|
0x380080c7,
|
|
0x80b601c8,
|
|
0x01b0b601,
|
|
@@ -283,6 +344,7 @@ uint32_t nvc0_pcopy_code[] = {
|
|
0x1d08980e,
|
|
0xf40068fd,
|
|
0x64bd0502,
|
|
+/* 0x023c: dst_xcnt */
|
|
0x800075fd,
|
|
0x78fd1907,
|
|
0x1057f100,
|
|
@@ -307,15 +369,18 @@ uint32_t nvc0_pcopy_code[] = {
|
|
0x1c069800,
|
|
0xf44056d0,
|
|
0x00f81030,
|
|
+/* 0x029c: cmd_exec_set_surface_tiled */
|
|
0xc7075798,
|
|
0x78c76879,
|
|
0x0380b664,
|
|
0xb06077c7,
|
|
0x1bf40e76,
|
|
0x0477f009,
|
|
+/* 0x02b7: xtile64 */
|
|
0xf00f0ef4,
|
|
0x70b6027c,
|
|
0x0947fd11,
|
|
+/* 0x02c3: xtileok */
|
|
0x980677f0,
|
|
0x5b980c5a,
|
|
0x00abfd0e,
|
|
@@ -374,6 +439,7 @@ uint32_t nvc0_pcopy_code[] = {
|
|
0xb70067d0,
|
|
0xd0040060,
|
|
0x00f80068,
|
|
+/* 0x03a8: cmd_exec_set_surface_linear */
|
|
0xb7026cf0,
|
|
0xb6020260,
|
|
0x57980864,
|
|
@@ -384,12 +450,15 @@ uint32_t nvc0_pcopy_code[] = {
|
|
0x0060b700,
|
|
0x06579804,
|
|
0xf80067d0,
|
|
+/* 0x03d1: cmd_exec_wait */
|
|
0xf900f900,
|
|
0x0007f110,
|
|
0x0604b608,
|
|
+/* 0x03dc: loop */
|
|
0xf00001cf,
|
|
0x1bf40114,
|
|
0xfc10fcfa,
|
|
+/* 0x03eb: cmd_exec_query */
|
|
0xc800f800,
|
|
0x1bf40d34,
|
|
0xd121f570,
|
|
@@ -419,6 +488,7 @@ uint32_t nvc0_pcopy_code[] = {
|
|
0x0153f026,
|
|
0x080047f1,
|
|
0xd00644b6,
|
|
+/* 0x045e: query_counter */
|
|
0x21f50045,
|
|
0x47f103d1,
|
|
0x44b6080c,
|
|
@@ -442,11 +512,13 @@ uint32_t nvc0_pcopy_code[] = {
|
|
0x080047f1,
|
|
0xd00644b6,
|
|
0x00f80045,
|
|
+/* 0x04b8: cmd_exec */
|
|
0x03d121f5,
|
|
0xf4003fc8,
|
|
0x21f50e0b,
|
|
0x47f101af,
|
|
0x0ef40200,
|
|
+/* 0x04cd: cmd_exec_no_format */
|
|
0x1067f11e,
|
|
0x0664b608,
|
|
0x800177f0,
|
|
@@ -454,18 +526,23 @@ uint32_t nvc0_pcopy_code[] = {
|
|
0x1d079819,
|
|
0xd00067d0,
|
|
0x44bd4067,
|
|
+/* 0x04e8: cmd_exec_init_src_surface */
|
|
0xbd0232f4,
|
|
0x043fc854,
|
|
0xf50a0bf4,
|
|
0xf403a821,
|
|
+/* 0x04fa: src_tiled */
|
|
0x21f50a0e,
|
|
0x49f0029c,
|
|
+/* 0x0501: cmd_exec_init_dst_surface */
|
|
0x0231f407,
|
|
0xc82c57f0,
|
|
0x0bf4083f,
|
|
0xa821f50a,
|
|
0x0a0ef403,
|
|
+/* 0x0514: dst_tiled */
|
|
0x029c21f5,
|
|
+/* 0x051b: cmd_exec_kick */
|
|
0xf10849f0,
|
|
0xb6080057,
|
|
0x06980654,
|
|
@@ -475,7 +552,9 @@ uint32_t nvc0_pcopy_code[] = {
|
|
0x54d00546,
|
|
0x0c3fc800,
|
|
0xf5070bf4,
|
|
+/* 0x053f: cmd_exec_done */
|
|
0xf803eb21,
|
|
+/* 0x0541: cmd_wrcache_flush */
|
|
0x0027f100,
|
|
0xf034bd22,
|
|
0x23d00133,
|
|
diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c
|
|
index 552b436..3254d51 100644
|
|
--- a/drivers/gpu/drm/radeon/atombios_dp.c
|
|
+++ b/drivers/gpu/drm/radeon/atombios_dp.c
|
|
@@ -22,6 +22,7 @@
|
|
*
|
|
* Authors: Dave Airlie
|
|
* Alex Deucher
|
|
+ * Jerome Glisse
|
|
*/
|
|
#include "drmP.h"
|
|
#include "radeon_drm.h"
|
|
@@ -634,7 +635,6 @@ static bool radeon_dp_get_link_status(struct radeon_connector *radeon_connector,
|
|
ret = radeon_dp_aux_native_read(radeon_connector, DP_LANE0_1_STATUS,
|
|
link_status, DP_LINK_STATUS_SIZE, 100);
|
|
if (ret <= 0) {
|
|
- DRM_ERROR("displayport link status failed\n");
|
|
return false;
|
|
}
|
|
|
|
@@ -812,8 +812,10 @@ static int radeon_dp_link_train_cr(struct radeon_dp_link_train_info *dp_info)
|
|
else
|
|
mdelay(dp_info->rd_interval * 4);
|
|
|
|
- if (!radeon_dp_get_link_status(dp_info->radeon_connector, dp_info->link_status))
|
|
+ if (!radeon_dp_get_link_status(dp_info->radeon_connector, dp_info->link_status)) {
|
|
+ DRM_ERROR("displayport link status failed\n");
|
|
break;
|
|
+ }
|
|
|
|
if (dp_clock_recovery_ok(dp_info->link_status, dp_info->dp_lane_count)) {
|
|
clock_recovery = true;
|
|
@@ -875,8 +877,10 @@ static int radeon_dp_link_train_ce(struct radeon_dp_link_train_info *dp_info)
|
|
else
|
|
mdelay(dp_info->rd_interval * 4);
|
|
|
|
- if (!radeon_dp_get_link_status(dp_info->radeon_connector, dp_info->link_status))
|
|
+ if (!radeon_dp_get_link_status(dp_info->radeon_connector, dp_info->link_status)) {
|
|
+ DRM_ERROR("displayport link status failed\n");
|
|
break;
|
|
+ }
|
|
|
|
if (dp_channel_eq_ok(dp_info->link_status, dp_info->dp_lane_count)) {
|
|
channel_eq = true;
|
|
diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c
|
|
index 4a4493f..87d494d 100644
|
|
--- a/drivers/gpu/drm/radeon/radeon_connectors.c
|
|
+++ b/drivers/gpu/drm/radeon/radeon_connectors.c
|
|
@@ -64,14 +64,33 @@ void radeon_connector_hotplug(struct drm_connector *connector)
|
|
|
|
/* just deal with DP (not eDP) here. */
|
|
if (connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort) {
|
|
- int saved_dpms = connector->dpms;
|
|
-
|
|
- /* Only turn off the display it it's physically disconnected */
|
|
- if (!radeon_hpd_sense(rdev, radeon_connector->hpd.hpd))
|
|
- drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
|
|
- else if (radeon_dp_needs_link_train(radeon_connector))
|
|
- drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON);
|
|
- connector->dpms = saved_dpms;
|
|
+ struct radeon_connector_atom_dig *dig_connector =
|
|
+ radeon_connector->con_priv;
|
|
+
|
|
+ /* if existing sink type was not DP no need to retrain */
|
|
+ if (dig_connector->dp_sink_type != CONNECTOR_OBJECT_ID_DISPLAYPORT)
|
|
+ return;
|
|
+
|
|
+ /* first get sink type as it may be reset after (un)plug */
|
|
+ dig_connector->dp_sink_type = radeon_dp_getsinktype(radeon_connector);
|
|
+ /* don't do anything if sink is not display port, i.e.,
|
|
+ * passive dp->(dvi|hdmi) adaptor
|
|
+ */
|
|
+ if (dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) {
|
|
+ int saved_dpms = connector->dpms;
|
|
+ /* Only turn off the display if it's physically disconnected */
|
|
+ if (!radeon_hpd_sense(rdev, radeon_connector->hpd.hpd)) {
|
|
+ drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
|
|
+ } else if (radeon_dp_needs_link_train(radeon_connector)) {
|
|
+ /* set it to OFF so that drm_helper_connector_dpms()
|
|
+ * won't return immediately since the current state
|
|
+ * is ON at this point.
|
|
+ */
|
|
+ connector->dpms = DRM_MODE_DPMS_OFF;
|
|
+ drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON);
|
|
+ }
|
|
+ connector->dpms = saved_dpms;
|
|
+ }
|
|
}
|
|
}
|
|
|
|
diff --git a/drivers/gpu/drm/radeon/radeon_cursor.c b/drivers/gpu/drm/radeon/radeon_cursor.c
|
|
index 986d608..2132109 100644
|
|
--- a/drivers/gpu/drm/radeon/radeon_cursor.c
|
|
+++ b/drivers/gpu/drm/radeon/radeon_cursor.c
|
|
@@ -257,8 +257,14 @@ int radeon_crtc_cursor_move(struct drm_crtc *crtc,
|
|
if (!(cursor_end & 0x7f))
|
|
w--;
|
|
}
|
|
- if (w <= 0)
|
|
+ if (w <= 0) {
|
|
w = 1;
|
|
+ cursor_end = x - xorigin + w;
|
|
+ if (!(cursor_end & 0x7f)) {
|
|
+ x--;
|
|
+ WARN_ON_ONCE(x < 0);
|
|
+ }
|
|
+ }
|
|
}
|
|
}
|
|
|
|
diff --git a/drivers/gpu/drm/radeon/radeon_object.c b/drivers/gpu/drm/radeon/radeon_object.c
|
|
index f3ae607..39497c7 100644
|
|
--- a/drivers/gpu/drm/radeon/radeon_object.c
|
|
+++ b/drivers/gpu/drm/radeon/radeon_object.c
|
|
@@ -117,7 +117,6 @@ int radeon_bo_create(struct radeon_device *rdev,
|
|
return -ENOMEM;
|
|
}
|
|
|
|
-retry:
|
|
bo = kzalloc(sizeof(struct radeon_bo), GFP_KERNEL);
|
|
if (bo == NULL)
|
|
return -ENOMEM;
|
|
@@ -130,6 +129,8 @@ retry:
|
|
bo->gem_base.driver_private = NULL;
|
|
bo->surface_reg = -1;
|
|
INIT_LIST_HEAD(&bo->list);
|
|
+
|
|
+retry:
|
|
radeon_ttm_placement_from_domain(bo, domain);
|
|
/* Kernel allocation are uninterruptible */
|
|
mutex_lock(&rdev->vram_mutex);
|
|
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
|
|
index a1b8caa..0f074e0 100644
|
|
--- a/drivers/iommu/amd_iommu.c
|
|
+++ b/drivers/iommu/amd_iommu.c
|
|
@@ -1865,6 +1865,11 @@ static int device_change_notifier(struct notifier_block *nb,
|
|
|
|
iommu_init_device(dev);
|
|
|
|
+ if (iommu_pass_through) {
|
|
+ attach_device(dev, pt_domain);
|
|
+ break;
|
|
+ }
|
|
+
|
|
domain = domain_for_device(dev);
|
|
|
|
/* allocate a protection domain if a device is added */
|
|
@@ -1880,10 +1885,7 @@ static int device_change_notifier(struct notifier_block *nb,
|
|
list_add_tail(&dma_domain->list, &iommu_pd_list);
|
|
spin_unlock_irqrestore(&iommu_pd_list_lock, flags);
|
|
|
|
- if (!iommu_pass_through)
|
|
- dev->archdata.dma_ops = &amd_iommu_dma_ops;
|
|
- else
|
|
- dev->archdata.dma_ops = &nommu_dma_ops;
|
|
+ dev->archdata.dma_ops = &amd_iommu_dma_ops;
|
|
|
|
break;
|
|
case BUS_NOTIFY_DEL_DEVICE:
|
|
diff --git a/drivers/media/video/cx25821/cx25821-core.c b/drivers/media/video/cx25821/cx25821-core.c
|
|
index a7fa38f..e572ce5 100644
|
|
--- a/drivers/media/video/cx25821/cx25821-core.c
|
|
+++ b/drivers/media/video/cx25821/cx25821-core.c
|
|
@@ -914,9 +914,6 @@ static int cx25821_dev_setup(struct cx25821_dev *dev)
|
|
list_add_tail(&dev->devlist, &cx25821_devlist);
|
|
mutex_unlock(&cx25821_devlist_mutex);
|
|
|
|
- strcpy(cx25821_boards[UNKNOWN_BOARD].name, "unknown");
|
|
- strcpy(cx25821_boards[CX25821_BOARD].name, "cx25821");
|
|
-
|
|
if (dev->pci->device != 0x8210) {
|
|
pr_info("%s(): Exiting. Incorrect Hardware device = 0x%02x\n",
|
|
__func__, dev->pci->device);
|
|
diff --git a/drivers/media/video/cx25821/cx25821.h b/drivers/media/video/cx25821/cx25821.h
|
|
index 2d2d009..bf54360 100644
|
|
--- a/drivers/media/video/cx25821/cx25821.h
|
|
+++ b/drivers/media/video/cx25821/cx25821.h
|
|
@@ -187,7 +187,7 @@ enum port {
|
|
};
|
|
|
|
struct cx25821_board {
|
|
- char *name;
|
|
+ const char *name;
|
|
enum port porta;
|
|
enum port portb;
|
|
enum port portc;
|
|
diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c
|
|
index 6878a94..83b51b5 100644
|
|
--- a/drivers/mmc/host/sdhci-pci.c
|
|
+++ b/drivers/mmc/host/sdhci-pci.c
|
|
@@ -148,6 +148,7 @@ static const struct sdhci_pci_fixes sdhci_ene_714 = {
|
|
static const struct sdhci_pci_fixes sdhci_cafe = {
|
|
.quirks = SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER |
|
|
SDHCI_QUIRK_NO_BUSY_IRQ |
|
|
+ SDHCI_QUIRK_BROKEN_CARD_DETECTION |
|
|
SDHCI_QUIRK_BROKEN_TIMEOUT_VAL,
|
|
};
|
|
|
|
diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c
|
|
index 9e61d6b..ed1be8a 100644
|
|
--- a/drivers/net/ethernet/realtek/r8169.c
|
|
+++ b/drivers/net/ethernet/realtek/r8169.c
|
|
@@ -3770,6 +3770,7 @@ static void rtl_init_rxcfg(struct rtl8169_private *tp)
|
|
case RTL_GIGA_MAC_VER_22:
|
|
case RTL_GIGA_MAC_VER_23:
|
|
case RTL_GIGA_MAC_VER_24:
|
|
+ case RTL_GIGA_MAC_VER_34:
|
|
RTL_W32(RxConfig, RX128_INT_EN | RX_MULTI_EN | RX_DMA_BURST);
|
|
break;
|
|
default:
|
|
diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c
|
|
index 01dcb1a..727c129 100644
|
|
--- a/drivers/net/wireless/mwifiex/cfg80211.c
|
|
+++ b/drivers/net/wireless/mwifiex/cfg80211.c
|
|
@@ -545,9 +545,9 @@ mwifiex_dump_station_info(struct mwifiex_private *priv,
|
|
|
|
/*
|
|
* Bit 0 in tx_htinfo indicates that current Tx rate is 11n rate. Valid
|
|
- * MCS index values for us are 0 to 7.
|
|
+ * MCS index values for us are 0 to 15.
|
|
*/
|
|
- if ((priv->tx_htinfo & BIT(0)) && (priv->tx_rate < 8)) {
|
|
+ if ((priv->tx_htinfo & BIT(0)) && (priv->tx_rate < 16)) {
|
|
sinfo->txrate.mcs = priv->tx_rate;
|
|
sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
|
|
/* 40MHz rate */
|
|
diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c
|
|
index 0ffa111..bdf960b 100644
|
|
--- a/drivers/net/wireless/rt2x00/rt2800usb.c
|
|
+++ b/drivers/net/wireless/rt2x00/rt2800usb.c
|
|
@@ -876,6 +876,7 @@ static struct usb_device_id rt2800usb_device_table[] = {
|
|
{ USB_DEVICE(0x1482, 0x3c09) },
|
|
/* AirTies */
|
|
{ USB_DEVICE(0x1eda, 0x2012) },
|
|
+ { USB_DEVICE(0x1eda, 0x2210) },
|
|
{ USB_DEVICE(0x1eda, 0x2310) },
|
|
/* Allwin */
|
|
{ USB_DEVICE(0x8516, 0x2070) },
|
|
@@ -945,6 +946,7 @@ static struct usb_device_id rt2800usb_device_table[] = {
|
|
/* DVICO */
|
|
{ USB_DEVICE(0x0fe9, 0xb307) },
|
|
/* Edimax */
|
|
+ { USB_DEVICE(0x7392, 0x4085) },
|
|
{ USB_DEVICE(0x7392, 0x7711) },
|
|
{ USB_DEVICE(0x7392, 0x7717) },
|
|
{ USB_DEVICE(0x7392, 0x7718) },
|
|
@@ -1020,6 +1022,7 @@ static struct usb_device_id rt2800usb_device_table[] = {
|
|
/* Philips */
|
|
{ USB_DEVICE(0x0471, 0x200f) },
|
|
/* Planex */
|
|
+ { USB_DEVICE(0x2019, 0x5201) },
|
|
{ USB_DEVICE(0x2019, 0xab25) },
|
|
{ USB_DEVICE(0x2019, 0xed06) },
|
|
/* Quanta */
|
|
@@ -1088,6 +1091,12 @@ static struct usb_device_id rt2800usb_device_table[] = {
|
|
#ifdef CONFIG_RT2800USB_RT33XX
|
|
/* Belkin */
|
|
{ USB_DEVICE(0x050d, 0x945b) },
|
|
+ /* D-Link */
|
|
+ { USB_DEVICE(0x2001, 0x3c17) },
|
|
+ /* Panasonic */
|
|
+ { USB_DEVICE(0x083a, 0xb511) },
|
|
+ /* Philips */
|
|
+ { USB_DEVICE(0x0471, 0x20dd) },
|
|
/* Ralink */
|
|
{ USB_DEVICE(0x148f, 0x3370) },
|
|
{ USB_DEVICE(0x148f, 0x8070) },
|
|
@@ -1099,6 +1108,8 @@ static struct usb_device_id rt2800usb_device_table[] = {
|
|
{ USB_DEVICE(0x8516, 0x3572) },
|
|
/* Askey */
|
|
{ USB_DEVICE(0x1690, 0x0744) },
|
|
+ { USB_DEVICE(0x1690, 0x0761) },
|
|
+ { USB_DEVICE(0x1690, 0x0764) },
|
|
/* Cisco */
|
|
{ USB_DEVICE(0x167b, 0x4001) },
|
|
/* EnGenius */
|
|
@@ -1113,6 +1124,9 @@ static struct usb_device_id rt2800usb_device_table[] = {
|
|
/* Sitecom */
|
|
{ USB_DEVICE(0x0df6, 0x0041) },
|
|
{ USB_DEVICE(0x0df6, 0x0062) },
|
|
+ { USB_DEVICE(0x0df6, 0x0065) },
|
|
+ { USB_DEVICE(0x0df6, 0x0066) },
|
|
+ { USB_DEVICE(0x0df6, 0x0068) },
|
|
/* Toshiba */
|
|
{ USB_DEVICE(0x0930, 0x0a07) },
|
|
/* Zinwell */
|
|
@@ -1122,6 +1136,9 @@ static struct usb_device_id rt2800usb_device_table[] = {
|
|
/* Azurewave */
|
|
{ USB_DEVICE(0x13d3, 0x3329) },
|
|
{ USB_DEVICE(0x13d3, 0x3365) },
|
|
+ /* D-Link */
|
|
+ { USB_DEVICE(0x2001, 0x3c1c) },
|
|
+ { USB_DEVICE(0x2001, 0x3c1d) },
|
|
/* Ralink */
|
|
{ USB_DEVICE(0x148f, 0x5370) },
|
|
{ USB_DEVICE(0x148f, 0x5372) },
|
|
@@ -1163,13 +1180,8 @@ static struct usb_device_id rt2800usb_device_table[] = {
|
|
/* D-Link */
|
|
{ USB_DEVICE(0x07d1, 0x3c0b) },
|
|
{ USB_DEVICE(0x07d1, 0x3c17) },
|
|
- { USB_DEVICE(0x2001, 0x3c17) },
|
|
- /* Edimax */
|
|
- { USB_DEVICE(0x7392, 0x4085) },
|
|
/* Encore */
|
|
{ USB_DEVICE(0x203d, 0x14a1) },
|
|
- /* Fujitsu Stylistic 550 */
|
|
- { USB_DEVICE(0x1690, 0x0761) },
|
|
/* Gemtek */
|
|
{ USB_DEVICE(0x15a9, 0x0010) },
|
|
/* Gigabyte */
|
|
@@ -1190,7 +1202,6 @@ static struct usb_device_id rt2800usb_device_table[] = {
|
|
{ USB_DEVICE(0x05a6, 0x0101) },
|
|
{ USB_DEVICE(0x1d4d, 0x0010) },
|
|
/* Planex */
|
|
- { USB_DEVICE(0x2019, 0x5201) },
|
|
{ USB_DEVICE(0x2019, 0xab24) },
|
|
/* Qcom */
|
|
{ USB_DEVICE(0x18e8, 0x6259) },
|
|
diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/phy.c b/drivers/net/wireless/rtlwifi/rtl8192de/phy.c
|
|
index 2cf4c5f..de9faa9 100644
|
|
--- a/drivers/net/wireless/rtlwifi/rtl8192de/phy.c
|
|
+++ b/drivers/net/wireless/rtlwifi/rtl8192de/phy.c
|
|
@@ -3462,21 +3462,21 @@ void rtl92d_phy_config_macphymode_info(struct ieee80211_hw *hw)
|
|
switch (rtlhal->macphymode) {
|
|
case DUALMAC_SINGLEPHY:
|
|
rtlphy->rf_type = RF_2T2R;
|
|
- rtlhal->version |= CHIP_92D_SINGLEPHY;
|
|
+ rtlhal->version |= RF_TYPE_2T2R;
|
|
rtlhal->bandset = BAND_ON_BOTH;
|
|
rtlhal->current_bandtype = BAND_ON_2_4G;
|
|
break;
|
|
|
|
case SINGLEMAC_SINGLEPHY:
|
|
rtlphy->rf_type = RF_2T2R;
|
|
- rtlhal->version |= CHIP_92D_SINGLEPHY;
|
|
+ rtlhal->version |= RF_TYPE_2T2R;
|
|
rtlhal->bandset = BAND_ON_BOTH;
|
|
rtlhal->current_bandtype = BAND_ON_2_4G;
|
|
break;
|
|
|
|
case DUALMAC_DUALPHY:
|
|
rtlphy->rf_type = RF_1T1R;
|
|
- rtlhal->version &= (~CHIP_92D_SINGLEPHY);
|
|
+ rtlhal->version &= RF_TYPE_1T1R;
|
|
/* Now we let MAC0 run on 5G band. */
|
|
if (rtlhal->interfaceindex == 0) {
|
|
rtlhal->bandset = BAND_ON_5G;
|
|
diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c
|
|
index 351dc0b..ee77a58 100644
|
|
--- a/drivers/scsi/hosts.c
|
|
+++ b/drivers/scsi/hosts.c
|
|
@@ -287,6 +287,7 @@ static void scsi_host_dev_release(struct device *dev)
|
|
struct Scsi_Host *shost = dev_to_shost(dev);
|
|
struct device *parent = dev->parent;
|
|
struct request_queue *q;
|
|
+ void *queuedata;
|
|
|
|
scsi_proc_hostdir_rm(shost->hostt);
|
|
|
|
@@ -296,9 +297,9 @@ static void scsi_host_dev_release(struct device *dev)
|
|
destroy_workqueue(shost->work_q);
|
|
q = shost->uspace_req_q;
|
|
if (q) {
|
|
- kfree(q->queuedata);
|
|
- q->queuedata = NULL;
|
|
- scsi_free_queue(q);
|
|
+ queuedata = q->queuedata;
|
|
+ blk_cleanup_queue(q);
|
|
+ kfree(queuedata);
|
|
}
|
|
|
|
scsi_destroy_command_freelist(shost);
|
|
diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c
|
|
index e48ba4b..dbe3568 100644
|
|
--- a/drivers/scsi/libsas/sas_expander.c
|
|
+++ b/drivers/scsi/libsas/sas_expander.c
|
|
@@ -774,7 +774,7 @@ static struct domain_device *sas_ex_discover_end_dev(
|
|
}
|
|
|
|
/* See if this phy is part of a wide port */
|
|
-static int sas_ex_join_wide_port(struct domain_device *parent, int phy_id)
|
|
+static bool sas_ex_join_wide_port(struct domain_device *parent, int phy_id)
|
|
{
|
|
struct ex_phy *phy = &parent->ex_dev.ex_phy[phy_id];
|
|
int i;
|
|
@@ -790,11 +790,11 @@ static int sas_ex_join_wide_port(struct domain_device *parent, int phy_id)
|
|
sas_port_add_phy(ephy->port, phy->phy);
|
|
phy->port = ephy->port;
|
|
phy->phy_state = PHY_DEVICE_DISCOVERED;
|
|
- return 0;
|
|
+ return true;
|
|
}
|
|
}
|
|
|
|
- return -ENODEV;
|
|
+ return false;
|
|
}
|
|
|
|
static struct domain_device *sas_ex_discover_expander(
|
|
@@ -932,8 +932,7 @@ static int sas_ex_discover_dev(struct domain_device *dev, int phy_id)
|
|
return res;
|
|
}
|
|
|
|
- res = sas_ex_join_wide_port(dev, phy_id);
|
|
- if (!res) {
|
|
+ if (sas_ex_join_wide_port(dev, phy_id)) {
|
|
SAS_DPRINTK("Attaching ex phy%d to wide port %016llx\n",
|
|
phy_id, SAS_ADDR(ex_phy->attached_sas_addr));
|
|
return res;
|
|
@@ -978,8 +977,7 @@ static int sas_ex_discover_dev(struct domain_device *dev, int phy_id)
|
|
if (SAS_ADDR(ex->ex_phy[i].attached_sas_addr) ==
|
|
SAS_ADDR(child->sas_addr)) {
|
|
ex->ex_phy[i].phy_state= PHY_DEVICE_DISCOVERED;
|
|
- res = sas_ex_join_wide_port(dev, i);
|
|
- if (!res)
|
|
+ if (sas_ex_join_wide_port(dev, i))
|
|
SAS_DPRINTK("Attaching ex phy%d to wide port %016llx\n",
|
|
i, SAS_ADDR(ex->ex_phy[i].attached_sas_addr));
|
|
|
|
@@ -1849,32 +1847,20 @@ static int sas_discover_new(struct domain_device *dev, int phy_id)
|
|
{
|
|
struct ex_phy *ex_phy = &dev->ex_dev.ex_phy[phy_id];
|
|
struct domain_device *child;
|
|
- bool found = false;
|
|
- int res, i;
|
|
+ int res;
|
|
|
|
SAS_DPRINTK("ex %016llx phy%d new device attached\n",
|
|
SAS_ADDR(dev->sas_addr), phy_id);
|
|
res = sas_ex_phy_discover(dev, phy_id);
|
|
if (res)
|
|
- goto out;
|
|
- /* to support the wide port inserted */
|
|
- for (i = 0; i < dev->ex_dev.num_phys; i++) {
|
|
- struct ex_phy *ex_phy_temp = &dev->ex_dev.ex_phy[i];
|
|
- if (i == phy_id)
|
|
- continue;
|
|
- if (SAS_ADDR(ex_phy_temp->attached_sas_addr) ==
|
|
- SAS_ADDR(ex_phy->attached_sas_addr)) {
|
|
- found = true;
|
|
- break;
|
|
- }
|
|
- }
|
|
- if (found) {
|
|
- sas_ex_join_wide_port(dev, phy_id);
|
|
+ return res;
|
|
+
|
|
+ if (sas_ex_join_wide_port(dev, phy_id))
|
|
return 0;
|
|
- }
|
|
+
|
|
res = sas_ex_discover_devices(dev, phy_id);
|
|
- if (!res)
|
|
- goto out;
|
|
+ if (res)
|
|
+ return res;
|
|
list_for_each_entry(child, &dev->ex_dev.children, siblings) {
|
|
if (SAS_ADDR(child->sas_addr) ==
|
|
SAS_ADDR(ex_phy->attached_sas_addr)) {
|
|
@@ -1884,7 +1870,6 @@ static int sas_discover_new(struct domain_device *dev, int phy_id)
|
|
break;
|
|
}
|
|
}
|
|
-out:
|
|
return res;
|
|
}
|
|
|
|
@@ -1983,9 +1968,7 @@ int sas_ex_revalidate_domain(struct domain_device *port_dev)
|
|
struct domain_device *dev = NULL;
|
|
|
|
res = sas_find_bcast_dev(port_dev, &dev);
|
|
- if (res)
|
|
- goto out;
|
|
- if (dev) {
|
|
+ while (res == 0 && dev) {
|
|
struct expander_device *ex = &dev->ex_dev;
|
|
int i = 0, phy_id;
|
|
|
|
@@ -1997,8 +1980,10 @@ int sas_ex_revalidate_domain(struct domain_device *port_dev)
|
|
res = sas_rediscover(dev, phy_id);
|
|
i = phy_id + 1;
|
|
} while (i < ex->num_phys);
|
|
+
|
|
+ dev = NULL;
|
|
+ res = sas_find_bcast_dev(port_dev, &dev);
|
|
}
|
|
-out:
|
|
return res;
|
|
}
|
|
|
|
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
|
|
index 2aeb2e9..831db24 100644
|
|
--- a/drivers/scsi/scsi.c
|
|
+++ b/drivers/scsi/scsi.c
|
|
@@ -785,7 +785,13 @@ static void scsi_done(struct scsi_cmnd *cmd)
|
|
/* Move this to a header if it becomes more generally useful */
|
|
static struct scsi_driver *scsi_cmd_to_driver(struct scsi_cmnd *cmd)
|
|
{
|
|
- return *(struct scsi_driver **)cmd->request->rq_disk->private_data;
|
|
+ struct scsi_driver **sdp;
|
|
+
|
|
+ sdp = (struct scsi_driver **)cmd->request->rq_disk->private_data;
|
|
+ if (!sdp)
|
|
+ return NULL;
|
|
+
|
|
+ return *sdp;
|
|
}
|
|
|
|
/**
|
|
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
|
|
index dc6131e..456b131 100644
|
|
--- a/drivers/scsi/scsi_error.c
|
|
+++ b/drivers/scsi/scsi_error.c
|
|
@@ -1673,6 +1673,20 @@ static void scsi_restart_operations(struct Scsi_Host *shost)
|
|
* requests are started.
|
|
*/
|
|
scsi_run_host_queues(shost);
|
|
+
|
|
+ /*
|
|
+ * if eh is active and host_eh_scheduled is pending we need to re-run
|
|
+ * recovery. we do this check after scsi_run_host_queues() to allow
|
|
+ * everything pent up since the last eh run a chance to make forward
|
|
+ * progress before we sync again. Either we'll immediately re-run
|
|
+ * recovery or scsi_device_unbusy() will wake us again when these
|
|
+ * pending commands complete.
|
|
+ */
|
|
+ spin_lock_irqsave(shost->host_lock, flags);
|
|
+ if (shost->host_eh_scheduled)
|
|
+ if (scsi_host_set_state(shost, SHOST_RECOVERY))
|
|
+ WARN_ON(scsi_host_set_state(shost, SHOST_CANCEL_RECOVERY));
|
|
+ spin_unlock_irqrestore(shost->host_lock, flags);
|
|
}
|
|
|
|
/**
|
|
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
|
|
index f0ab58e..6c4b620 100644
|
|
--- a/drivers/scsi/scsi_lib.c
|
|
+++ b/drivers/scsi/scsi_lib.c
|
|
@@ -406,10 +406,6 @@ static void scsi_run_queue(struct request_queue *q)
|
|
LIST_HEAD(starved_list);
|
|
unsigned long flags;
|
|
|
|
- /* if the device is dead, sdev will be NULL, so no queue to run */
|
|
- if (!sdev)
|
|
- return;
|
|
-
|
|
shost = sdev->host;
|
|
if (scsi_target(sdev)->single_lun)
|
|
scsi_single_lun_run(sdev);
|
|
@@ -483,15 +479,26 @@ void scsi_requeue_run_queue(struct work_struct *work)
|
|
*/
|
|
static void scsi_requeue_command(struct request_queue *q, struct scsi_cmnd *cmd)
|
|
{
|
|
+ struct scsi_device *sdev = cmd->device;
|
|
struct request *req = cmd->request;
|
|
unsigned long flags;
|
|
|
|
+ /*
|
|
+ * We need to hold a reference on the device to avoid the queue being
|
|
+ * killed after the unlock and before scsi_run_queue is invoked which
|
|
+ * may happen because scsi_unprep_request() puts the command which
|
|
+ * releases its reference on the device.
|
|
+ */
|
|
+ get_device(&sdev->sdev_gendev);
|
|
+
|
|
spin_lock_irqsave(q->queue_lock, flags);
|
|
scsi_unprep_request(req);
|
|
blk_requeue_request(q, req);
|
|
spin_unlock_irqrestore(q->queue_lock, flags);
|
|
|
|
scsi_run_queue(q);
|
|
+
|
|
+ put_device(&sdev->sdev_gendev);
|
|
}
|
|
|
|
void scsi_next_command(struct scsi_cmnd *cmd)
|
|
@@ -1374,16 +1381,16 @@ static inline int scsi_host_queue_ready(struct request_queue *q,
|
|
* may be changed after request stacking drivers call the function,
|
|
* regardless of taking lock or not.
|
|
*
|
|
- * When scsi can't dispatch I/Os anymore and needs to kill I/Os
|
|
- * (e.g. !sdev), scsi needs to return 'not busy'.
|
|
- * Otherwise, request stacking drivers may hold requests forever.
|
|
+ * When scsi can't dispatch I/Os anymore and needs to kill I/Os scsi
|
|
+ * needs to return 'not busy'. Otherwise, request stacking drivers
|
|
+ * may hold requests forever.
|
|
*/
|
|
static int scsi_lld_busy(struct request_queue *q)
|
|
{
|
|
struct scsi_device *sdev = q->queuedata;
|
|
struct Scsi_Host *shost;
|
|
|
|
- if (!sdev)
|
|
+ if (blk_queue_dead(q))
|
|
return 0;
|
|
|
|
shost = sdev->host;
|
|
@@ -1494,12 +1501,6 @@ static void scsi_request_fn(struct request_queue *q)
|
|
struct scsi_cmnd *cmd;
|
|
struct request *req;
|
|
|
|
- if (!sdev) {
|
|
- while ((req = blk_peek_request(q)) != NULL)
|
|
- scsi_kill_request(req, q);
|
|
- return;
|
|
- }
|
|
-
|
|
if(!get_device(&sdev->sdev_gendev))
|
|
/* We must be tearing the block queue down already */
|
|
return;
|
|
@@ -1701,20 +1702,6 @@ struct request_queue *scsi_alloc_queue(struct scsi_device *sdev)
|
|
return q;
|
|
}
|
|
|
|
-void scsi_free_queue(struct request_queue *q)
|
|
-{
|
|
- unsigned long flags;
|
|
-
|
|
- WARN_ON(q->queuedata);
|
|
-
|
|
- /* cause scsi_request_fn() to kill all non-finished requests */
|
|
- spin_lock_irqsave(q->queue_lock, flags);
|
|
- q->request_fn(q);
|
|
- spin_unlock_irqrestore(q->queue_lock, flags);
|
|
-
|
|
- blk_cleanup_queue(q);
|
|
-}
|
|
-
|
|
/*
|
|
* Function: scsi_block_requests()
|
|
*
|
|
diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h
|
|
index 5b475d0..d58adca 100644
|
|
--- a/drivers/scsi/scsi_priv.h
|
|
+++ b/drivers/scsi/scsi_priv.h
|
|
@@ -85,7 +85,6 @@ extern void scsi_next_command(struct scsi_cmnd *cmd);
|
|
extern void scsi_io_completion(struct scsi_cmnd *, unsigned int);
|
|
extern void scsi_run_host_queues(struct Scsi_Host *shost);
|
|
extern struct request_queue *scsi_alloc_queue(struct scsi_device *sdev);
|
|
-extern void scsi_free_queue(struct request_queue *q);
|
|
extern int scsi_init_queue(void);
|
|
extern void scsi_exit_queue(void);
|
|
struct request_queue;
|
|
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
|
|
index 6e7ea4a..a48b59c 100644
|
|
--- a/drivers/scsi/scsi_scan.c
|
|
+++ b/drivers/scsi/scsi_scan.c
|
|
@@ -1710,6 +1710,9 @@ static void scsi_sysfs_add_devices(struct Scsi_Host *shost)
|
|
{
|
|
struct scsi_device *sdev;
|
|
shost_for_each_device(sdev, shost) {
|
|
+ /* target removed before the device could be added */
|
|
+ if (sdev->sdev_state == SDEV_DEL)
|
|
+ continue;
|
|
if (!scsi_host_scan_allowed(shost) ||
|
|
scsi_sysfs_add_sdev(sdev) != 0)
|
|
__scsi_remove_device(sdev);
|
|
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c
|
|
index 04c2a27..bb7c482 100644
|
|
--- a/drivers/scsi/scsi_sysfs.c
|
|
+++ b/drivers/scsi/scsi_sysfs.c
|
|
@@ -971,11 +971,8 @@ void __scsi_remove_device(struct scsi_device *sdev)
|
|
sdev->host->hostt->slave_destroy(sdev);
|
|
transport_destroy_device(dev);
|
|
|
|
- /* cause the request function to reject all I/O requests */
|
|
- sdev->request_queue->queuedata = NULL;
|
|
-
|
|
/* Freeing the queue signals to block that we're done */
|
|
- scsi_free_queue(sdev->request_queue);
|
|
+ blk_cleanup_queue(sdev->request_queue);
|
|
put_device(dev);
|
|
}
|
|
|
|
@@ -1000,7 +997,6 @@ static void __scsi_remove_target(struct scsi_target *starget)
|
|
struct scsi_device *sdev;
|
|
|
|
spin_lock_irqsave(shost->host_lock, flags);
|
|
- starget->reap_ref++;
|
|
restart:
|
|
list_for_each_entry(sdev, &shost->__devices, siblings) {
|
|
if (sdev->channel != starget->channel ||
|
|
@@ -1014,14 +1010,6 @@ static void __scsi_remove_target(struct scsi_target *starget)
|
|
goto restart;
|
|
}
|
|
spin_unlock_irqrestore(shost->host_lock, flags);
|
|
- scsi_target_reap(starget);
|
|
-}
|
|
-
|
|
-static int __remove_child (struct device * dev, void * data)
|
|
-{
|
|
- if (scsi_is_target_device(dev))
|
|
- __scsi_remove_target(to_scsi_target(dev));
|
|
- return 0;
|
|
}
|
|
|
|
/**
|
|
@@ -1034,14 +1022,34 @@ static int __remove_child (struct device * dev, void * data)
|
|
*/
|
|
void scsi_remove_target(struct device *dev)
|
|
{
|
|
- if (scsi_is_target_device(dev)) {
|
|
- __scsi_remove_target(to_scsi_target(dev));
|
|
- return;
|
|
+ struct Scsi_Host *shost = dev_to_shost(dev->parent);
|
|
+ struct scsi_target *starget, *found;
|
|
+ unsigned long flags;
|
|
+
|
|
+ restart:
|
|
+ found = NULL;
|
|
+ spin_lock_irqsave(shost->host_lock, flags);
|
|
+ list_for_each_entry(starget, &shost->__targets, siblings) {
|
|
+ if (starget->state == STARGET_DEL)
|
|
+ continue;
|
|
+ if (starget->dev.parent == dev || &starget->dev == dev) {
|
|
+ found = starget;
|
|
+ found->reap_ref++;
|
|
+ break;
|
|
+ }
|
|
}
|
|
+ spin_unlock_irqrestore(shost->host_lock, flags);
|
|
|
|
- get_device(dev);
|
|
- device_for_each_child(dev, NULL, __remove_child);
|
|
- put_device(dev);
|
|
+ if (found) {
|
|
+ __scsi_remove_target(found);
|
|
+ scsi_target_reap(found);
|
|
+ /* in the case where @dev has multiple starget children,
|
|
+ * continue removing.
|
|
+ *
|
|
+ * FIXME: does such a case exist?
|
|
+ */
|
|
+ goto restart;
|
|
+ }
|
|
}
|
|
EXPORT_SYMBOL(scsi_remove_target);
|
|
|
|
diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c
|
|
index 0842cc7..2ff1255 100644
|
|
--- a/drivers/target/iscsi/iscsi_target.c
|
|
+++ b/drivers/target/iscsi/iscsi_target.c
|
|
@@ -427,19 +427,8 @@ int iscsit_reset_np_thread(
|
|
|
|
int iscsit_del_np_comm(struct iscsi_np *np)
|
|
{
|
|
- if (!np->np_socket)
|
|
- return 0;
|
|
-
|
|
- /*
|
|
- * Some network transports allocate their own struct sock->file,
|
|
- * see if we need to free any additional allocated resources.
|
|
- */
|
|
- if (np->np_flags & NPF_SCTP_STRUCT_FILE) {
|
|
- kfree(np->np_socket->file);
|
|
- np->np_socket->file = NULL;
|
|
- }
|
|
-
|
|
- sock_release(np->np_socket);
|
|
+ if (np->np_socket)
|
|
+ sock_release(np->np_socket);
|
|
return 0;
|
|
}
|
|
|
|
@@ -4105,13 +4094,8 @@ int iscsit_close_connection(
|
|
kfree(conn->conn_ops);
|
|
conn->conn_ops = NULL;
|
|
|
|
- if (conn->sock) {
|
|
- if (conn->conn_flags & CONNFLAG_SCTP_STRUCT_FILE) {
|
|
- kfree(conn->sock->file);
|
|
- conn->sock->file = NULL;
|
|
- }
|
|
+ if (conn->sock)
|
|
sock_release(conn->sock);
|
|
- }
|
|
conn->thread_set = NULL;
|
|
|
|
pr_debug("Moving to TARG_CONN_STATE_FREE.\n");
|
|
diff --git a/drivers/target/iscsi/iscsi_target_core.h b/drivers/target/iscsi/iscsi_target_core.h
|
|
index 7da2d6a..0f68197 100644
|
|
--- a/drivers/target/iscsi/iscsi_target_core.h
|
|
+++ b/drivers/target/iscsi/iscsi_target_core.h
|
|
@@ -224,7 +224,6 @@ enum iscsi_timer_flags_table {
|
|
/* Used for struct iscsi_np->np_flags */
|
|
enum np_flags_table {
|
|
NPF_IP_NETWORK = 0x00,
|
|
- NPF_SCTP_STRUCT_FILE = 0x01 /* Bugfix */
|
|
};
|
|
|
|
/* Used for struct iscsi_np->np_thread_state */
|
|
@@ -511,7 +510,6 @@ struct iscsi_conn {
|
|
u16 local_port;
|
|
int net_size;
|
|
u32 auth_id;
|
|
-#define CONNFLAG_SCTP_STRUCT_FILE 0x01
|
|
u32 conn_flags;
|
|
/* Used for iscsi_tx_login_rsp() */
|
|
u32 login_itt;
|
|
diff --git a/drivers/target/iscsi/iscsi_target_login.c b/drivers/target/iscsi/iscsi_target_login.c
|
|
index bd2adec..2ec5339 100644
|
|
--- a/drivers/target/iscsi/iscsi_target_login.c
|
|
+++ b/drivers/target/iscsi/iscsi_target_login.c
|
|
@@ -793,22 +793,6 @@ int iscsi_target_setup_login_socket(
|
|
}
|
|
np->np_socket = sock;
|
|
/*
|
|
- * The SCTP stack needs struct socket->file.
|
|
- */
|
|
- if ((np->np_network_transport == ISCSI_SCTP_TCP) ||
|
|
- (np->np_network_transport == ISCSI_SCTP_UDP)) {
|
|
- if (!sock->file) {
|
|
- sock->file = kzalloc(sizeof(struct file), GFP_KERNEL);
|
|
- if (!sock->file) {
|
|
- pr_err("Unable to allocate struct"
|
|
- " file for SCTP\n");
|
|
- ret = -ENOMEM;
|
|
- goto fail;
|
|
- }
|
|
- np->np_flags |= NPF_SCTP_STRUCT_FILE;
|
|
- }
|
|
- }
|
|
- /*
|
|
* Setup the np->np_sockaddr from the passed sockaddr setup
|
|
* in iscsi_target_configfs.c code..
|
|
*/
|
|
@@ -857,21 +841,15 @@ int iscsi_target_setup_login_socket(
|
|
|
|
fail:
|
|
np->np_socket = NULL;
|
|
- if (sock) {
|
|
- if (np->np_flags & NPF_SCTP_STRUCT_FILE) {
|
|
- kfree(sock->file);
|
|
- sock->file = NULL;
|
|
- }
|
|
-
|
|
+ if (sock)
|
|
sock_release(sock);
|
|
- }
|
|
return ret;
|
|
}
|
|
|
|
static int __iscsi_target_login_thread(struct iscsi_np *np)
|
|
{
|
|
u8 buffer[ISCSI_HDR_LEN], iscsi_opcode, zero_tsih = 0;
|
|
- int err, ret = 0, ip_proto, sock_type, set_sctp_conn_flag, stop;
|
|
+ int err, ret = 0, ip_proto, sock_type, stop;
|
|
struct iscsi_conn *conn = NULL;
|
|
struct iscsi_login *login;
|
|
struct iscsi_portal_group *tpg = NULL;
|
|
@@ -882,7 +860,6 @@ static int __iscsi_target_login_thread(struct iscsi_np *np)
|
|
struct sockaddr_in6 sock_in6;
|
|
|
|
flush_signals(current);
|
|
- set_sctp_conn_flag = 0;
|
|
sock = np->np_socket;
|
|
ip_proto = np->np_ip_proto;
|
|
sock_type = np->np_sock_type;
|
|
@@ -907,35 +884,12 @@ static int __iscsi_target_login_thread(struct iscsi_np *np)
|
|
spin_unlock_bh(&np->np_thread_lock);
|
|
goto out;
|
|
}
|
|
- /*
|
|
- * The SCTP stack needs struct socket->file.
|
|
- */
|
|
- if ((np->np_network_transport == ISCSI_SCTP_TCP) ||
|
|
- (np->np_network_transport == ISCSI_SCTP_UDP)) {
|
|
- if (!new_sock->file) {
|
|
- new_sock->file = kzalloc(
|
|
- sizeof(struct file), GFP_KERNEL);
|
|
- if (!new_sock->file) {
|
|
- pr_err("Unable to allocate struct"
|
|
- " file for SCTP\n");
|
|
- sock_release(new_sock);
|
|
- /* Get another socket */
|
|
- return 1;
|
|
- }
|
|
- set_sctp_conn_flag = 1;
|
|
- }
|
|
- }
|
|
-
|
|
iscsi_start_login_thread_timer(np);
|
|
|
|
conn = kzalloc(sizeof(struct iscsi_conn), GFP_KERNEL);
|
|
if (!conn) {
|
|
pr_err("Could not allocate memory for"
|
|
" new connection\n");
|
|
- if (set_sctp_conn_flag) {
|
|
- kfree(new_sock->file);
|
|
- new_sock->file = NULL;
|
|
- }
|
|
sock_release(new_sock);
|
|
/* Get another socket */
|
|
return 1;
|
|
@@ -945,9 +899,6 @@ static int __iscsi_target_login_thread(struct iscsi_np *np)
|
|
conn->conn_state = TARG_CONN_STATE_FREE;
|
|
conn->sock = new_sock;
|
|
|
|
- if (set_sctp_conn_flag)
|
|
- conn->conn_flags |= CONNFLAG_SCTP_STRUCT_FILE;
|
|
-
|
|
pr_debug("Moving to TARG_CONN_STATE_XPT_UP.\n");
|
|
conn->conn_state = TARG_CONN_STATE_XPT_UP;
|
|
|
|
@@ -1195,13 +1146,8 @@ old_sess_out:
|
|
iscsi_release_param_list(conn->param_list);
|
|
conn->param_list = NULL;
|
|
}
|
|
- if (conn->sock) {
|
|
- if (conn->conn_flags & CONNFLAG_SCTP_STRUCT_FILE) {
|
|
- kfree(conn->sock->file);
|
|
- conn->sock->file = NULL;
|
|
- }
|
|
+ if (conn->sock)
|
|
sock_release(conn->sock);
|
|
- }
|
|
kfree(conn);
|
|
|
|
if (tpg) {
|
|
diff --git a/drivers/target/target_core_cdb.c b/drivers/target/target_core_cdb.c
|
|
index 93b9406..717a8d4 100644
|
|
--- a/drivers/target/target_core_cdb.c
|
|
+++ b/drivers/target/target_core_cdb.c
|
|
@@ -1114,11 +1114,11 @@ int target_emulate_unmap(struct se_task *task)
|
|
struct se_cmd *cmd = task->task_se_cmd;
|
|
struct se_device *dev = cmd->se_dev;
|
|
unsigned char *buf, *ptr = NULL;
|
|
- unsigned char *cdb = &cmd->t_task_cdb[0];
|
|
sector_t lba;
|
|
- unsigned int size = cmd->data_length, range;
|
|
- int ret = 0, offset;
|
|
- unsigned short dl, bd_dl;
|
|
+ int size = cmd->data_length;
|
|
+ u32 range;
|
|
+ int ret = 0;
|
|
+ int dl, bd_dl;
|
|
|
|
if (!dev->transport->do_discard) {
|
|
pr_err("UNMAP emulation not supported for: %s\n",
|
|
@@ -1127,24 +1127,41 @@ int target_emulate_unmap(struct se_task *task)
|
|
return -ENOSYS;
|
|
}
|
|
|
|
- /* First UNMAP block descriptor starts at 8 byte offset */
|
|
- offset = 8;
|
|
- size -= 8;
|
|
- dl = get_unaligned_be16(&cdb[0]);
|
|
- bd_dl = get_unaligned_be16(&cdb[2]);
|
|
-
|
|
buf = transport_kmap_data_sg(cmd);
|
|
|
|
- ptr = &buf[offset];
|
|
- pr_debug("UNMAP: Sub: %s Using dl: %hu bd_dl: %hu size: %hu"
|
|
+ dl = get_unaligned_be16(&buf[0]);
|
|
+ bd_dl = get_unaligned_be16(&buf[2]);
|
|
+
|
|
+ size = min(size - 8, bd_dl);
|
|
+ if (size / 16 > dev->se_sub_dev->se_dev_attrib.max_unmap_block_desc_count) {
|
|
+ cmd->scsi_sense_reason = TCM_INVALID_PARAMETER_LIST;
|
|
+ ret = -EINVAL;
|
|
+ goto err;
|
|
+ }
|
|
+
|
|
+ /* First UNMAP block descriptor starts at 8 byte offset */
|
|
+ ptr = &buf[8];
|
|
+ pr_debug("UNMAP: Sub: %s Using dl: %u bd_dl: %u size: %u"
|
|
" ptr: %p\n", dev->transport->name, dl, bd_dl, size, ptr);
|
|
|
|
- while (size) {
|
|
+ while (size >= 16) {
|
|
lba = get_unaligned_be64(&ptr[0]);
|
|
range = get_unaligned_be32(&ptr[8]);
|
|
pr_debug("UNMAP: Using lba: %llu and range: %u\n",
|
|
(unsigned long long)lba, range);
|
|
|
|
+ if (range > dev->se_sub_dev->se_dev_attrib.max_unmap_lba_count) {
|
|
+ cmd->scsi_sense_reason = TCM_INVALID_PARAMETER_LIST;
|
|
+ ret = -EINVAL;
|
|
+ goto err;
|
|
+ }
|
|
+
|
|
+ if (lba + range > dev->transport->get_blocks(dev) + 1) {
|
|
+ cmd->scsi_sense_reason = TCM_ADDRESS_OUT_OF_RANGE;
|
|
+ ret = -EINVAL;
|
|
+ goto err;
|
|
+ }
|
|
+
|
|
ret = dev->transport->do_discard(dev, lba, range);
|
|
if (ret < 0) {
|
|
pr_err("blkdev_issue_discard() failed: %d\n",
|
|
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
|
|
index 5660916..94c03d2 100644
|
|
--- a/drivers/target/target_core_transport.c
|
|
+++ b/drivers/target/target_core_transport.c
|
|
@@ -1820,6 +1820,7 @@ static void transport_generic_request_failure(struct se_cmd *cmd)
|
|
case TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE:
|
|
case TCM_UNKNOWN_MODE_PAGE:
|
|
case TCM_WRITE_PROTECTED:
|
|
+ case TCM_ADDRESS_OUT_OF_RANGE:
|
|
case TCM_CHECK_CONDITION_ABORT_CMD:
|
|
case TCM_CHECK_CONDITION_UNIT_ATTENTION:
|
|
case TCM_CHECK_CONDITION_NOT_READY:
|
|
@@ -4496,6 +4497,15 @@ int transport_send_check_condition_and_sense(
|
|
/* WRITE PROTECTED */
|
|
buffer[offset+SPC_ASC_KEY_OFFSET] = 0x27;
|
|
break;
|
|
+ case TCM_ADDRESS_OUT_OF_RANGE:
|
|
+ /* CURRENT ERROR */
|
|
+ buffer[offset] = 0x70;
|
|
+ buffer[offset+SPC_ADD_SENSE_LEN_OFFSET] = 10;
|
|
+ /* ILLEGAL REQUEST */
|
|
+ buffer[offset+SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST;
|
|
+ /* LOGICAL BLOCK ADDRESS OUT OF RANGE */
|
|
+ buffer[offset+SPC_ASC_KEY_OFFSET] = 0x21;
|
|
+ break;
|
|
case TCM_CHECK_CONDITION_UNIT_ATTENTION:
|
|
/* CURRENT ERROR */
|
|
buffer[offset] = 0x70;
|
|
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
|
|
index f6ff837..a9df218 100644
|
|
--- a/drivers/usb/core/devio.c
|
|
+++ b/drivers/usb/core/devio.c
|
|
@@ -1555,10 +1555,14 @@ static int processcompl_compat(struct async *as, void __user * __user *arg)
|
|
void __user *addr = as->userurb;
|
|
unsigned int i;
|
|
|
|
- if (as->userbuffer && urb->actual_length)
|
|
- if (copy_to_user(as->userbuffer, urb->transfer_buffer,
|
|
- urb->actual_length))
|
|
+ if (as->userbuffer && urb->actual_length) {
|
|
+ if (urb->number_of_packets > 0) /* Isochronous */
|
|
+ i = urb->transfer_buffer_length;
|
|
+ else /* Non-Isoc */
|
|
+ i = urb->actual_length;
|
|
+ if (copy_to_user(as->userbuffer, urb->transfer_buffer, i))
|
|
return -EFAULT;
|
|
+ }
|
|
if (put_user(as->status, &userurb->status))
|
|
return -EFAULT;
|
|
if (put_user(urb->actual_length, &userurb->actual_length))
|
|
diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c
|
|
index 29c854b..4e1f0aa 100644
|
|
--- a/drivers/usb/gadget/u_ether.c
|
|
+++ b/drivers/usb/gadget/u_ether.c
|
|
@@ -796,12 +796,6 @@ int gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN])
|
|
|
|
SET_ETHTOOL_OPS(net, &ops);
|
|
|
|
- /* two kinds of host-initiated state changes:
|
|
- * - iff DATA transfer is active, carrier is "on"
|
|
- * - tx queueing enabled if open *and* carrier is "on"
|
|
- */
|
|
- netif_carrier_off(net);
|
|
-
|
|
dev->gadget = g;
|
|
SET_NETDEV_DEV(net, &g->dev);
|
|
SET_NETDEV_DEVTYPE(net, &gadget_type);
|
|
@@ -815,6 +809,12 @@ int gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN])
|
|
INFO(dev, "HOST MAC %pM\n", dev->host_mac);
|
|
|
|
the_dev = dev;
|
|
+
|
|
+ /* two kinds of host-initiated state changes:
|
|
+ * - iff DATA transfer is active, carrier is "on"
|
|
+ * - tx queueing enabled if open *and* carrier is "on"
|
|
+ */
|
|
+ netif_carrier_off(net);
|
|
}
|
|
|
|
return status;
|
|
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
|
|
index 5971c95..d89aac1 100644
|
|
--- a/drivers/usb/serial/option.c
|
|
+++ b/drivers/usb/serial/option.c
|
|
@@ -932,8 +932,12 @@ static const struct usb_device_id option_ids[] = {
|
|
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0165, 0xff, 0xff, 0xff) },
|
|
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0167, 0xff, 0xff, 0xff),
|
|
.driver_info = (kernel_ulong_t)&net_intf4_blacklist },
|
|
- { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1008, 0xff, 0xff, 0xff) },
|
|
- { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1010, 0xff, 0xff, 0xff) },
|
|
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0326, 0xff, 0xff, 0xff),
|
|
+ .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
|
|
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1008, 0xff, 0xff, 0xff),
|
|
+ .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
|
|
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1010, 0xff, 0xff, 0xff),
|
|
+ .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
|
|
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1012, 0xff, 0xff, 0xff) },
|
|
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1057, 0xff, 0xff, 0xff) },
|
|
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1058, 0xff, 0xff, 0xff) },
|
|
diff --git a/fs/btrfs/async-thread.c b/fs/btrfs/async-thread.c
|
|
index 0b39458..03321e5 100644
|
|
--- a/fs/btrfs/async-thread.c
|
|
+++ b/fs/btrfs/async-thread.c
|
|
@@ -206,10 +206,17 @@ static noinline int run_ordered_completions(struct btrfs_workers *workers,
|
|
|
|
work->ordered_func(work);
|
|
|
|
- /* now take the lock again and call the freeing code */
|
|
+ /* now take the lock again and drop our item from the list */
|
|
spin_lock(&workers->order_lock);
|
|
list_del(&work->order_list);
|
|
+ spin_unlock(&workers->order_lock);
|
|
+
|
|
+ /*
|
|
+ * we don't want to call the ordered free functions
|
|
+ * with the lock held though
|
|
+ */
|
|
work->ordered_free(work);
|
|
+ spin_lock(&workers->order_lock);
|
|
}
|
|
|
|
spin_unlock(&workers->order_lock);
|
|
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
|
|
index f44b392..6b2a724 100644
|
|
--- a/fs/btrfs/disk-io.c
|
|
+++ b/fs/btrfs/disk-io.c
|
|
@@ -872,7 +872,8 @@ static int btree_submit_bio_hook(struct inode *inode, int rw, struct bio *bio,
|
|
|
|
#ifdef CONFIG_MIGRATION
|
|
static int btree_migratepage(struct address_space *mapping,
|
|
- struct page *newpage, struct page *page)
|
|
+ struct page *newpage, struct page *page,
|
|
+ enum migrate_mode mode)
|
|
{
|
|
/*
|
|
* we can't safely write a btree page from here,
|
|
@@ -887,7 +888,7 @@ static int btree_migratepage(struct address_space *mapping,
|
|
if (page_has_private(page) &&
|
|
!try_to_release_page(page, GFP_KERNEL))
|
|
return -EAGAIN;
|
|
- return migrate_page(mapping, newpage, page);
|
|
+ return migrate_page(mapping, newpage, page, mode);
|
|
}
|
|
#endif
|
|
|
|
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
|
|
index 6aa7457..c858a29 100644
|
|
--- a/fs/cifs/cifssmb.c
|
|
+++ b/fs/cifs/cifssmb.c
|
|
@@ -89,6 +89,32 @@ static struct {
|
|
/* Forward declarations */
|
|
static void cifs_readv_complete(struct work_struct *work);
|
|
|
|
+#ifdef CONFIG_HIGHMEM
|
|
+/*
|
|
+ * On arches that have high memory, kmap address space is limited. By
|
|
+ * serializing the kmap operations on those arches, we ensure that we don't
|
|
+ * end up with a bunch of threads in writeback with partially mapped page
|
|
+ * arrays, stuck waiting for kmap to come back. That situation prevents
|
|
+ * progress and can deadlock.
|
|
+ */
|
|
+static DEFINE_MUTEX(cifs_kmap_mutex);
|
|
+
|
|
+static inline void
|
|
+cifs_kmap_lock(void)
|
|
+{
|
|
+ mutex_lock(&cifs_kmap_mutex);
|
|
+}
|
|
+
|
|
+static inline void
|
|
+cifs_kmap_unlock(void)
|
|
+{
|
|
+ mutex_unlock(&cifs_kmap_mutex);
|
|
+}
|
|
+#else /* !CONFIG_HIGHMEM */
|
|
+#define cifs_kmap_lock() do { ; } while(0)
|
|
+#define cifs_kmap_unlock() do { ; } while(0)
|
|
+#endif /* CONFIG_HIGHMEM */
|
|
+
|
|
/* Mark as invalid, all open files on tree connections since they
|
|
were closed when session to server was lost */
|
|
static void mark_open_files_invalid(struct cifs_tcon *pTcon)
|
|
@@ -1540,6 +1566,7 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
|
|
eof_index = eof ? (eof - 1) >> PAGE_CACHE_SHIFT : 0;
|
|
cFYI(1, "eof=%llu eof_index=%lu", eof, eof_index);
|
|
|
|
+ cifs_kmap_lock();
|
|
list_for_each_entry_safe(page, tpage, &rdata->pages, lru) {
|
|
if (remaining >= PAGE_CACHE_SIZE) {
|
|
/* enough data to fill the page */
|
|
@@ -1589,6 +1616,7 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
|
|
page_cache_release(page);
|
|
}
|
|
}
|
|
+ cifs_kmap_unlock();
|
|
|
|
/* issue the read if we have any iovecs left to fill */
|
|
if (rdata->nr_iov > 1) {
|
|
@@ -2171,6 +2199,7 @@ cifs_async_writev(struct cifs_writedata *wdata)
|
|
iov[0].iov_base = smb;
|
|
|
|
/* marshal up the pages into iov array */
|
|
+ cifs_kmap_lock();
|
|
wdata->bytes = 0;
|
|
for (i = 0; i < wdata->nr_pages; i++) {
|
|
iov[i + 1].iov_len = min(inode->i_size -
|
|
@@ -2179,6 +2208,7 @@ cifs_async_writev(struct cifs_writedata *wdata)
|
|
iov[i + 1].iov_base = kmap(wdata->pages[i]);
|
|
wdata->bytes += iov[i + 1].iov_len;
|
|
}
|
|
+ cifs_kmap_unlock();
|
|
|
|
cFYI(1, "async write at %llu %u bytes", wdata->offset, wdata->bytes);
|
|
|
|
diff --git a/fs/ext4/balloc.c b/fs/ext4/balloc.c
|
|
index 914bf9e..d6970f7 100644
|
|
--- a/fs/ext4/balloc.c
|
|
+++ b/fs/ext4/balloc.c
|
|
@@ -557,7 +557,8 @@ ext4_fsblk_t ext4_count_free_clusters(struct super_block *sb)
|
|
if (bitmap_bh == NULL)
|
|
continue;
|
|
|
|
- x = ext4_count_free(bitmap_bh, sb->s_blocksize);
|
|
+ x = ext4_count_free(bitmap_bh->b_data,
|
|
+ EXT4_BLOCKS_PER_GROUP(sb) / 8);
|
|
printk(KERN_DEBUG "group %u: stored = %d, counted = %u\n",
|
|
i, ext4_free_group_clusters(sb, gdp), x);
|
|
bitmap_count += x;
|
|
diff --git a/fs/ext4/bitmap.c b/fs/ext4/bitmap.c
|
|
index fa3af81..bbde5d5 100644
|
|
--- a/fs/ext4/bitmap.c
|
|
+++ b/fs/ext4/bitmap.c
|
|
@@ -11,21 +11,15 @@
|
|
#include <linux/jbd2.h>
|
|
#include "ext4.h"
|
|
|
|
-#ifdef EXT4FS_DEBUG
|
|
-
|
|
static const int nibblemap[] = {4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0};
|
|
|
|
-unsigned int ext4_count_free(struct buffer_head *map, unsigned int numchars)
|
|
+unsigned int ext4_count_free(char *bitmap, unsigned int numchars)
|
|
{
|
|
unsigned int i, sum = 0;
|
|
|
|
- if (!map)
|
|
- return 0;
|
|
for (i = 0; i < numchars; i++)
|
|
- sum += nibblemap[map->b_data[i] & 0xf] +
|
|
- nibblemap[(map->b_data[i] >> 4) & 0xf];
|
|
+ sum += nibblemap[bitmap[i] & 0xf] +
|
|
+ nibblemap[(bitmap[i] >> 4) & 0xf];
|
|
return sum;
|
|
}
|
|
|
|
-#endif /* EXT4FS_DEBUG */
|
|
-
|
|
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
|
|
index 7b1cd5c..8cb184c 100644
|
|
--- a/fs/ext4/ext4.h
|
|
+++ b/fs/ext4/ext4.h
|
|
@@ -1123,8 +1123,7 @@ struct ext4_sb_info {
|
|
unsigned long s_desc_per_block; /* Number of group descriptors per block */
|
|
ext4_group_t s_groups_count; /* Number of groups in the fs */
|
|
ext4_group_t s_blockfile_groups;/* Groups acceptable for non-extent files */
|
|
- unsigned long s_overhead_last; /* Last calculated overhead */
|
|
- unsigned long s_blocks_last; /* Last seen block count */
|
|
+ unsigned long s_overhead; /* # of fs overhead clusters */
|
|
unsigned int s_cluster_ratio; /* Number of blocks per cluster */
|
|
unsigned int s_cluster_bits; /* log2 of s_cluster_ratio */
|
|
loff_t s_bitmap_maxbytes; /* max bytes for bitmap files */
|
|
@@ -1757,7 +1756,7 @@ struct mmpd_data {
|
|
# define NORET_AND noreturn,
|
|
|
|
/* bitmap.c */
|
|
-extern unsigned int ext4_count_free(struct buffer_head *, unsigned);
|
|
+extern unsigned int ext4_count_free(char *bitmap, unsigned numchars);
|
|
|
|
/* balloc.c */
|
|
extern unsigned int ext4_block_group(struct super_block *sb,
|
|
@@ -1925,6 +1924,7 @@ extern int ext4_group_extend(struct super_block *sb,
|
|
ext4_fsblk_t n_blocks_count);
|
|
|
|
/* super.c */
|
|
+extern int ext4_calculate_overhead(struct super_block *sb);
|
|
extern void *ext4_kvmalloc(size_t size, gfp_t flags);
|
|
extern void *ext4_kvzalloc(size_t size, gfp_t flags);
|
|
extern void ext4_kvfree(void *ptr);
|
|
diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
|
|
index 8fb6844..6266799 100644
|
|
--- a/fs/ext4/ialloc.c
|
|
+++ b/fs/ext4/ialloc.c
|
|
@@ -1057,7 +1057,8 @@ unsigned long ext4_count_free_inodes(struct super_block *sb)
|
|
if (!bitmap_bh)
|
|
continue;
|
|
|
|
- x = ext4_count_free(bitmap_bh, EXT4_INODES_PER_GROUP(sb) / 8);
|
|
+ x = ext4_count_free(bitmap_bh->b_data,
|
|
+ EXT4_INODES_PER_GROUP(sb) / 8);
|
|
printk(KERN_DEBUG "group %lu: stored = %d, counted = %lu\n",
|
|
(unsigned long) i, ext4_free_inodes_count(sb, gdp), x);
|
|
bitmap_count += x;
|
|
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
|
|
index 3ce7613..8b01f9f 100644
|
|
--- a/fs/ext4/inode.c
|
|
+++ b/fs/ext4/inode.c
|
|
@@ -277,6 +277,15 @@ void ext4_da_update_reserve_space(struct inode *inode,
|
|
used = ei->i_reserved_data_blocks;
|
|
}
|
|
|
|
+ if (unlikely(ei->i_allocated_meta_blocks > ei->i_reserved_meta_blocks)) {
|
|
+ ext4_msg(inode->i_sb, KERN_NOTICE, "%s: ino %lu, allocated %d "
|
|
+ "with only %d reserved metadata blocks\n", __func__,
|
|
+ inode->i_ino, ei->i_allocated_meta_blocks,
|
|
+ ei->i_reserved_meta_blocks);
|
|
+ WARN_ON(1);
|
|
+ ei->i_allocated_meta_blocks = ei->i_reserved_meta_blocks;
|
|
+ }
|
|
+
|
|
/* Update per-inode reservations */
|
|
ei->i_reserved_data_blocks -= used;
|
|
ei->i_reserved_meta_blocks -= ei->i_allocated_meta_blocks;
|
|
@@ -1102,6 +1111,17 @@ static int ext4_da_reserve_space(struct inode *inode, ext4_lblk_t lblock)
|
|
struct ext4_inode_info *ei = EXT4_I(inode);
|
|
unsigned int md_needed;
|
|
int ret;
|
|
+ ext4_lblk_t save_last_lblock;
|
|
+ int save_len;
|
|
+
|
|
+ /*
|
|
+ * We will charge metadata quota at writeout time; this saves
|
|
+ * us from metadata over-estimation, though we may go over by
|
|
+ * a small amount in the end. Here we just reserve for data.
|
|
+ */
|
|
+ ret = dquot_reserve_block(inode, EXT4_C2B(sbi, 1));
|
|
+ if (ret)
|
|
+ return ret;
|
|
|
|
/*
|
|
* recalculate the amount of metadata blocks to reserve
|
|
@@ -1110,32 +1130,31 @@ static int ext4_da_reserve_space(struct inode *inode, ext4_lblk_t lblock)
|
|
*/
|
|
repeat:
|
|
spin_lock(&ei->i_block_reservation_lock);
|
|
+ /*
|
|
+ * ext4_calc_metadata_amount() has side effects, which we have
|
|
+ * to be prepared undo if we fail to claim space.
|
|
+ */
|
|
+ save_len = ei->i_da_metadata_calc_len;
|
|
+ save_last_lblock = ei->i_da_metadata_calc_last_lblock;
|
|
md_needed = EXT4_NUM_B2C(sbi,
|
|
ext4_calc_metadata_amount(inode, lblock));
|
|
trace_ext4_da_reserve_space(inode, md_needed);
|
|
- spin_unlock(&ei->i_block_reservation_lock);
|
|
|
|
/*
|
|
- * We will charge metadata quota at writeout time; this saves
|
|
- * us from metadata over-estimation, though we may go over by
|
|
- * a small amount in the end. Here we just reserve for data.
|
|
- */
|
|
- ret = dquot_reserve_block(inode, EXT4_C2B(sbi, 1));
|
|
- if (ret)
|
|
- return ret;
|
|
- /*
|
|
* We do still charge estimated metadata to the sb though;
|
|
* we cannot afford to run out of free blocks.
|
|
*/
|
|
if (ext4_claim_free_clusters(sbi, md_needed + 1, 0)) {
|
|
- dquot_release_reservation_block(inode, EXT4_C2B(sbi, 1));
|
|
+ ei->i_da_metadata_calc_len = save_len;
|
|
+ ei->i_da_metadata_calc_last_lblock = save_last_lblock;
|
|
+ spin_unlock(&ei->i_block_reservation_lock);
|
|
if (ext4_should_retry_alloc(inode->i_sb, &retries)) {
|
|
yield();
|
|
goto repeat;
|
|
}
|
|
+ dquot_release_reservation_block(inode, EXT4_C2B(sbi, 1));
|
|
return -ENOSPC;
|
|
}
|
|
- spin_lock(&ei->i_block_reservation_lock);
|
|
ei->i_reserved_data_blocks++;
|
|
ei->i_reserved_meta_blocks += md_needed;
|
|
spin_unlock(&ei->i_block_reservation_lock);
|
|
diff --git a/fs/ext4/resize.c b/fs/ext4/resize.c
|
|
index 996780a..4eac337 100644
|
|
--- a/fs/ext4/resize.c
|
|
+++ b/fs/ext4/resize.c
|
|
@@ -952,6 +952,11 @@ int ext4_group_add(struct super_block *sb, struct ext4_new_group_data *input)
|
|
&sbi->s_flex_groups[flex_group].free_inodes);
|
|
}
|
|
|
|
+ /*
|
|
+ * Update the fs overhead information
|
|
+ */
|
|
+ ext4_calculate_overhead(sb);
|
|
+
|
|
ext4_handle_dirty_super(handle, sb);
|
|
|
|
exit_journal:
|
|
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
|
|
index a93486e..a071348 100644
|
|
--- a/fs/ext4/super.c
|
|
+++ b/fs/ext4/super.c
|
|
@@ -3083,6 +3083,114 @@ static void ext4_destroy_lazyinit_thread(void)
|
|
kthread_stop(ext4_lazyinit_task);
|
|
}
|
|
|
|
+/*
|
|
+ * Note: calculating the overhead so we can be compatible with
|
|
+ * historical BSD practice is quite difficult in the face of
|
|
+ * clusters/bigalloc. This is because multiple metadata blocks from
|
|
+ * different block group can end up in the same allocation cluster.
|
|
+ * Calculating the exact overhead in the face of clustered allocation
|
|
+ * requires either O(all block bitmaps) in memory or O(number of block
|
|
+ * groups**2) in time. We will still calculate the superblock for
|
|
+ * older file systems --- and if we come across with a bigalloc file
|
|
+ * system with zero in s_overhead_clusters the estimate will be close to
|
|
+ * correct especially for very large cluster sizes --- but for newer
|
|
+ * file systems, it's better to calculate this figure once at mkfs
|
|
+ * time, and store it in the superblock. If the superblock value is
|
|
+ * present (even for non-bigalloc file systems), we will use it.
|
|
+ */
|
|
+static int count_overhead(struct super_block *sb, ext4_group_t grp,
|
|
+ char *buf)
|
|
+{
|
|
+ struct ext4_sb_info *sbi = EXT4_SB(sb);
|
|
+ struct ext4_group_desc *gdp;
|
|
+ ext4_fsblk_t first_block, last_block, b;
|
|
+ ext4_group_t i, ngroups = ext4_get_groups_count(sb);
|
|
+ int s, j, count = 0;
|
|
+
|
|
+ first_block = le32_to_cpu(sbi->s_es->s_first_data_block) +
|
|
+ (grp * EXT4_BLOCKS_PER_GROUP(sb));
|
|
+ last_block = first_block + EXT4_BLOCKS_PER_GROUP(sb) - 1;
|
|
+ for (i = 0; i < ngroups; i++) {
|
|
+ gdp = ext4_get_group_desc(sb, i, NULL);
|
|
+ b = ext4_block_bitmap(sb, gdp);
|
|
+ if (b >= first_block && b <= last_block) {
|
|
+ ext4_set_bit(EXT4_B2C(sbi, b - first_block), buf);
|
|
+ count++;
|
|
+ }
|
|
+ b = ext4_inode_bitmap(sb, gdp);
|
|
+ if (b >= first_block && b <= last_block) {
|
|
+ ext4_set_bit(EXT4_B2C(sbi, b - first_block), buf);
|
|
+ count++;
|
|
+ }
|
|
+ b = ext4_inode_table(sb, gdp);
|
|
+ if (b >= first_block && b + sbi->s_itb_per_group <= last_block)
|
|
+ for (j = 0; j < sbi->s_itb_per_group; j++, b++) {
|
|
+ int c = EXT4_B2C(sbi, b - first_block);
|
|
+ ext4_set_bit(c, buf);
|
|
+ count++;
|
|
+ }
|
|
+ if (i != grp)
|
|
+ continue;
|
|
+ s = 0;
|
|
+ if (ext4_bg_has_super(sb, grp)) {
|
|
+ ext4_set_bit(s++, buf);
|
|
+ count++;
|
|
+ }
|
|
+ for (j = ext4_bg_num_gdb(sb, grp); j > 0; j--) {
|
|
+ ext4_set_bit(EXT4_B2C(sbi, s++), buf);
|
|
+ count++;
|
|
+ }
|
|
+ }
|
|
+ if (!count)
|
|
+ return 0;
|
|
+ return EXT4_CLUSTERS_PER_GROUP(sb) -
|
|
+ ext4_count_free(buf, EXT4_CLUSTERS_PER_GROUP(sb) / 8);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Compute the overhead and stash it in sbi->s_overhead
|
|
+ */
|
|
+int ext4_calculate_overhead(struct super_block *sb)
|
|
+{
|
|
+ struct ext4_sb_info *sbi = EXT4_SB(sb);
|
|
+ struct ext4_super_block *es = sbi->s_es;
|
|
+ ext4_group_t i, ngroups = ext4_get_groups_count(sb);
|
|
+ ext4_fsblk_t overhead = 0;
|
|
+ char *buf = (char *) get_zeroed_page(GFP_KERNEL);
|
|
+
|
|
+ memset(buf, 0, PAGE_SIZE);
|
|
+ if (!buf)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ /*
|
|
+ * Compute the overhead (FS structures). This is constant
|
|
+ * for a given filesystem unless the number of block groups
|
|
+ * changes so we cache the previous value until it does.
|
|
+ */
|
|
+
|
|
+ /*
|
|
+ * All of the blocks before first_data_block are overhead
|
|
+ */
|
|
+ overhead = EXT4_B2C(sbi, le32_to_cpu(es->s_first_data_block));
|
|
+
|
|
+ /*
|
|
+ * Add the overhead found in each block group
|
|
+ */
|
|
+ for (i = 0; i < ngroups; i++) {
|
|
+ int blks;
|
|
+
|
|
+ blks = count_overhead(sb, i, buf);
|
|
+ overhead += blks;
|
|
+ if (blks)
|
|
+ memset(buf, 0, PAGE_SIZE);
|
|
+ cond_resched();
|
|
+ }
|
|
+ sbi->s_overhead = overhead;
|
|
+ smp_wmb();
|
|
+ free_page((unsigned long) buf);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static int ext4_fill_super(struct super_block *sb, void *data, int silent)
|
|
{
|
|
char *orig_data = kstrdup(data, GFP_KERNEL);
|
|
@@ -3695,6 +3803,18 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
|
|
|
|
no_journal:
|
|
/*
|
|
+ * Get the # of file system overhead blocks from the
|
|
+ * superblock if present.
|
|
+ */
|
|
+ if (es->s_overhead_clusters)
|
|
+ sbi->s_overhead = le32_to_cpu(es->s_overhead_clusters);
|
|
+ else {
|
|
+ ret = ext4_calculate_overhead(sb);
|
|
+ if (ret)
|
|
+ goto failed_mount_wq;
|
|
+ }
|
|
+
|
|
+ /*
|
|
* The maximum number of concurrent works can be high and
|
|
* concurrency isn't really necessary. Limit it to 1.
|
|
*/
|
|
@@ -4568,67 +4688,21 @@ restore_opts:
|
|
return err;
|
|
}
|
|
|
|
-/*
|
|
- * Note: calculating the overhead so we can be compatible with
|
|
- * historical BSD practice is quite difficult in the face of
|
|
- * clusters/bigalloc. This is because multiple metadata blocks from
|
|
- * different block group can end up in the same allocation cluster.
|
|
- * Calculating the exact overhead in the face of clustered allocation
|
|
- * requires either O(all block bitmaps) in memory or O(number of block
|
|
- * groups**2) in time. We will still calculate the superblock for
|
|
- * older file systems --- and if we come across with a bigalloc file
|
|
- * system with zero in s_overhead_clusters the estimate will be close to
|
|
- * correct especially for very large cluster sizes --- but for newer
|
|
- * file systems, it's better to calculate this figure once at mkfs
|
|
- * time, and store it in the superblock. If the superblock value is
|
|
- * present (even for non-bigalloc file systems), we will use it.
|
|
- */
|
|
static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf)
|
|
{
|
|
struct super_block *sb = dentry->d_sb;
|
|
struct ext4_sb_info *sbi = EXT4_SB(sb);
|
|
struct ext4_super_block *es = sbi->s_es;
|
|
- struct ext4_group_desc *gdp;
|
|
+ ext4_fsblk_t overhead = 0;
|
|
u64 fsid;
|
|
s64 bfree;
|
|
|
|
- if (test_opt(sb, MINIX_DF)) {
|
|
- sbi->s_overhead_last = 0;
|
|
- } else if (es->s_overhead_clusters) {
|
|
- sbi->s_overhead_last = le32_to_cpu(es->s_overhead_clusters);
|
|
- } else if (sbi->s_blocks_last != ext4_blocks_count(es)) {
|
|
- ext4_group_t i, ngroups = ext4_get_groups_count(sb);
|
|
- ext4_fsblk_t overhead = 0;
|
|
-
|
|
- /*
|
|
- * Compute the overhead (FS structures). This is constant
|
|
- * for a given filesystem unless the number of block groups
|
|
- * changes so we cache the previous value until it does.
|
|
- */
|
|
-
|
|
- /*
|
|
- * All of the blocks before first_data_block are
|
|
- * overhead
|
|
- */
|
|
- overhead = EXT4_B2C(sbi, le32_to_cpu(es->s_first_data_block));
|
|
-
|
|
- /*
|
|
- * Add the overhead found in each block group
|
|
- */
|
|
- for (i = 0; i < ngroups; i++) {
|
|
- gdp = ext4_get_group_desc(sb, i, NULL);
|
|
- overhead += ext4_num_overhead_clusters(sb, i, gdp);
|
|
- cond_resched();
|
|
- }
|
|
- sbi->s_overhead_last = overhead;
|
|
- smp_wmb();
|
|
- sbi->s_blocks_last = ext4_blocks_count(es);
|
|
- }
|
|
+ if (!test_opt(sb, MINIX_DF))
|
|
+ overhead = sbi->s_overhead;
|
|
|
|
buf->f_type = EXT4_SUPER_MAGIC;
|
|
buf->f_bsize = sb->s_blocksize;
|
|
- buf->f_blocks = (ext4_blocks_count(es) -
|
|
- EXT4_C2B(sbi, sbi->s_overhead_last));
|
|
+ buf->f_blocks = ext4_blocks_count(es) - EXT4_C2B(sbi, sbi->s_overhead);
|
|
bfree = percpu_counter_sum_positive(&sbi->s_freeclusters_counter) -
|
|
percpu_counter_sum_positive(&sbi->s_dirtyclusters_counter);
|
|
/* prevent underflow in case that few free space is available */
|
|
diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c
|
|
index ebc2f4d..0aa424a 100644
|
|
--- a/fs/hugetlbfs/inode.c
|
|
+++ b/fs/hugetlbfs/inode.c
|
|
@@ -569,7 +569,8 @@ static int hugetlbfs_set_page_dirty(struct page *page)
|
|
}
|
|
|
|
static int hugetlbfs_migrate_page(struct address_space *mapping,
|
|
- struct page *newpage, struct page *page)
|
|
+ struct page *newpage, struct page *page,
|
|
+ enum migrate_mode mode)
|
|
{
|
|
int rc;
|
|
|
|
diff --git a/fs/locks.c b/fs/locks.c
|
|
index 6a64f15..fcc50ab 100644
|
|
--- a/fs/locks.c
|
|
+++ b/fs/locks.c
|
|
@@ -308,7 +308,7 @@ static int flock_make_lock(struct file *filp, struct file_lock **lock,
|
|
return 0;
|
|
}
|
|
|
|
-static int assign_type(struct file_lock *fl, int type)
|
|
+static int assign_type(struct file_lock *fl, long type)
|
|
{
|
|
switch (type) {
|
|
case F_RDLCK:
|
|
@@ -445,7 +445,7 @@ static const struct lock_manager_operations lease_manager_ops = {
|
|
/*
|
|
* Initialize a lease, use the default lock manager operations
|
|
*/
|
|
-static int lease_init(struct file *filp, int type, struct file_lock *fl)
|
|
+static int lease_init(struct file *filp, long type, struct file_lock *fl)
|
|
{
|
|
if (assign_type(fl, type) != 0)
|
|
return -EINVAL;
|
|
@@ -463,7 +463,7 @@ static int lease_init(struct file *filp, int type, struct file_lock *fl)
|
|
}
|
|
|
|
/* Allocate a file_lock initialised to this type of lease */
|
|
-static struct file_lock *lease_alloc(struct file *filp, int type)
|
|
+static struct file_lock *lease_alloc(struct file *filp, long type)
|
|
{
|
|
struct file_lock *fl = locks_alloc_lock();
|
|
int error = -ENOMEM;
|
|
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
|
|
index 3f4d957..68b3f20 100644
|
|
--- a/fs/nfs/internal.h
|
|
+++ b/fs/nfs/internal.h
|
|
@@ -330,7 +330,7 @@ void nfs_commit_release_pages(struct nfs_write_data *data);
|
|
|
|
#ifdef CONFIG_MIGRATION
|
|
extern int nfs_migrate_page(struct address_space *,
|
|
- struct page *, struct page *);
|
|
+ struct page *, struct page *, enum migrate_mode);
|
|
#else
|
|
#define nfs_migrate_page NULL
|
|
#endif
|
|
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
|
|
index 4efd421..c6e523a 100644
|
|
--- a/fs/nfs/write.c
|
|
+++ b/fs/nfs/write.c
|
|
@@ -1711,7 +1711,7 @@ out_error:
|
|
|
|
#ifdef CONFIG_MIGRATION
|
|
int nfs_migrate_page(struct address_space *mapping, struct page *newpage,
|
|
- struct page *page)
|
|
+ struct page *page, enum migrate_mode mode)
|
|
{
|
|
/*
|
|
* If PagePrivate is set, then the page is currently associated with
|
|
@@ -1726,7 +1726,7 @@ int nfs_migrate_page(struct address_space *mapping, struct page *newpage,
|
|
|
|
nfs_fscache_release_page(page, GFP_KERNEL);
|
|
|
|
- return migrate_page(mapping, newpage, page);
|
|
+ return migrate_page(mapping, newpage, page, mode);
|
|
}
|
|
#endif
|
|
|
|
diff --git a/fs/udf/super.c b/fs/udf/super.c
|
|
index 270e135..516b7f0 100644
|
|
--- a/fs/udf/super.c
|
|
+++ b/fs/udf/super.c
|
|
@@ -1285,7 +1285,7 @@ static int udf_load_logicalvol(struct super_block *sb, sector_t block,
|
|
BUG_ON(ident != TAG_IDENT_LVD);
|
|
lvd = (struct logicalVolDesc *)bh->b_data;
|
|
table_len = le32_to_cpu(lvd->mapTableLength);
|
|
- if (sizeof(*lvd) + table_len > sb->s_blocksize) {
|
|
+ if (table_len > sb->s_blocksize - sizeof(*lvd)) {
|
|
udf_err(sb, "error loading logical volume descriptor: "
|
|
"Partition table too long (%u > %lu)\n", table_len,
|
|
sb->s_blocksize - sizeof(*lvd));
|
|
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
|
|
index 0ed1eb0..ff039f0 100644
|
|
--- a/include/linux/blkdev.h
|
|
+++ b/include/linux/blkdev.h
|
|
@@ -481,6 +481,7 @@ static inline void queue_flag_clear(unsigned int flag, struct request_queue *q)
|
|
|
|
#define blk_queue_tagged(q) test_bit(QUEUE_FLAG_QUEUED, &(q)->queue_flags)
|
|
#define blk_queue_stopped(q) test_bit(QUEUE_FLAG_STOPPED, &(q)->queue_flags)
|
|
+#define blk_queue_dead(q) test_bit(QUEUE_FLAG_DEAD, &(q)->queue_flags)
|
|
#define blk_queue_nomerges(q) test_bit(QUEUE_FLAG_NOMERGES, &(q)->queue_flags)
|
|
#define blk_queue_noxmerges(q) \
|
|
test_bit(QUEUE_FLAG_NOXMERGES, &(q)->queue_flags)
|
|
diff --git a/include/linux/cpu.h b/include/linux/cpu.h
|
|
index 6cb60fd..c692acc 100644
|
|
--- a/include/linux/cpu.h
|
|
+++ b/include/linux/cpu.h
|
|
@@ -66,8 +66,9 @@ enum {
|
|
/* migration should happen before other stuff but after perf */
|
|
CPU_PRI_PERF = 20,
|
|
CPU_PRI_MIGRATION = 10,
|
|
- /* prepare workqueues for other notifiers */
|
|
- CPU_PRI_WORKQUEUE = 5,
|
|
+ /* bring up workqueues before normal notifiers and down after */
|
|
+ CPU_PRI_WORKQUEUE_UP = 5,
|
|
+ CPU_PRI_WORKQUEUE_DOWN = -5,
|
|
};
|
|
|
|
#define CPU_ONLINE 0x0002 /* CPU (unsigned)v is up */
|
|
diff --git a/include/linux/cpuset.h b/include/linux/cpuset.h
|
|
index e9eaec5..7a7e5fd 100644
|
|
--- a/include/linux/cpuset.h
|
|
+++ b/include/linux/cpuset.h
|
|
@@ -89,42 +89,33 @@ extern void rebuild_sched_domains(void);
|
|
extern void cpuset_print_task_mems_allowed(struct task_struct *p);
|
|
|
|
/*
|
|
- * reading current mems_allowed and mempolicy in the fastpath must protected
|
|
- * by get_mems_allowed()
|
|
+ * get_mems_allowed is required when making decisions involving mems_allowed
|
|
+ * such as during page allocation. mems_allowed can be updated in parallel
|
|
+ * and depending on the new value an operation can fail potentially causing
|
|
+ * process failure. A retry loop with get_mems_allowed and put_mems_allowed
|
|
+ * prevents these artificial failures.
|
|
*/
|
|
-static inline void get_mems_allowed(void)
|
|
+static inline unsigned int get_mems_allowed(void)
|
|
{
|
|
- current->mems_allowed_change_disable++;
|
|
-
|
|
- /*
|
|
- * ensure that reading mems_allowed and mempolicy happens after the
|
|
- * update of ->mems_allowed_change_disable.
|
|
- *
|
|
- * the write-side task finds ->mems_allowed_change_disable is not 0,
|
|
- * and knows the read-side task is reading mems_allowed or mempolicy,
|
|
- * so it will clear old bits lazily.
|
|
- */
|
|
- smp_mb();
|
|
+ return read_seqcount_begin(¤t->mems_allowed_seq);
|
|
}
|
|
|
|
-static inline void put_mems_allowed(void)
|
|
+/*
|
|
+ * If this returns false, the operation that took place after get_mems_allowed
|
|
+ * may have failed. It is up to the caller to retry the operation if
|
|
+ * appropriate.
|
|
+ */
|
|
+static inline bool put_mems_allowed(unsigned int seq)
|
|
{
|
|
- /*
|
|
- * ensure that reading mems_allowed and mempolicy before reducing
|
|
- * mems_allowed_change_disable.
|
|
- *
|
|
- * the write-side task will know that the read-side task is still
|
|
- * reading mems_allowed or mempolicy, don't clears old bits in the
|
|
- * nodemask.
|
|
- */
|
|
- smp_mb();
|
|
- --ACCESS_ONCE(current->mems_allowed_change_disable);
|
|
+ return !read_seqcount_retry(¤t->mems_allowed_seq, seq);
|
|
}
|
|
|
|
static inline void set_mems_allowed(nodemask_t nodemask)
|
|
{
|
|
task_lock(current);
|
|
+ write_seqcount_begin(¤t->mems_allowed_seq);
|
|
current->mems_allowed = nodemask;
|
|
+ write_seqcount_end(¤t->mems_allowed_seq);
|
|
task_unlock(current);
|
|
}
|
|
|
|
@@ -234,12 +225,14 @@ static inline void set_mems_allowed(nodemask_t nodemask)
|
|
{
|
|
}
|
|
|
|
-static inline void get_mems_allowed(void)
|
|
+static inline unsigned int get_mems_allowed(void)
|
|
{
|
|
+ return 0;
|
|
}
|
|
|
|
-static inline void put_mems_allowed(void)
|
|
+static inline bool put_mems_allowed(unsigned int seq)
|
|
{
|
|
+ return true;
|
|
}
|
|
|
|
#endif /* !CONFIG_CPUSETS */
|
|
diff --git a/include/linux/fs.h b/include/linux/fs.h
|
|
index 43d36b7..29b6353 100644
|
|
--- a/include/linux/fs.h
|
|
+++ b/include/linux/fs.h
|
|
@@ -525,6 +525,7 @@ enum positive_aop_returns {
|
|
struct page;
|
|
struct address_space;
|
|
struct writeback_control;
|
|
+enum migrate_mode;
|
|
|
|
struct iov_iter {
|
|
const struct iovec *iov;
|
|
@@ -609,9 +610,12 @@ struct address_space_operations {
|
|
loff_t offset, unsigned long nr_segs);
|
|
int (*get_xip_mem)(struct address_space *, pgoff_t, int,
|
|
void **, unsigned long *);
|
|
- /* migrate the contents of a page to the specified target */
|
|
+ /*
|
|
+ * migrate the contents of a page to the specified target. If sync
|
|
+ * is false, it must not block.
|
|
+ */
|
|
int (*migratepage) (struct address_space *,
|
|
- struct page *, struct page *);
|
|
+ struct page *, struct page *, enum migrate_mode);
|
|
int (*launder_page) (struct page *);
|
|
int (*is_partially_uptodate) (struct page *, read_descriptor_t *,
|
|
unsigned long);
|
|
@@ -2586,7 +2590,8 @@ extern int generic_check_addressable(unsigned, u64);
|
|
|
|
#ifdef CONFIG_MIGRATION
|
|
extern int buffer_migrate_page(struct address_space *,
|
|
- struct page *, struct page *);
|
|
+ struct page *, struct page *,
|
|
+ enum migrate_mode);
|
|
#else
|
|
#define buffer_migrate_page NULL
|
|
#endif
|
|
diff --git a/include/linux/init_task.h b/include/linux/init_task.h
|
|
index 32574ee..df53fdf 100644
|
|
--- a/include/linux/init_task.h
|
|
+++ b/include/linux/init_task.h
|
|
@@ -30,6 +30,13 @@ extern struct fs_struct init_fs;
|
|
#define INIT_THREADGROUP_FORK_LOCK(sig)
|
|
#endif
|
|
|
|
+#ifdef CONFIG_CPUSETS
|
|
+#define INIT_CPUSET_SEQ \
|
|
+ .mems_allowed_seq = SEQCNT_ZERO,
|
|
+#else
|
|
+#define INIT_CPUSET_SEQ
|
|
+#endif
|
|
+
|
|
#define INIT_SIGNALS(sig) { \
|
|
.nr_threads = 1, \
|
|
.wait_chldexit = __WAIT_QUEUE_HEAD_INITIALIZER(sig.wait_chldexit),\
|
|
@@ -193,6 +200,7 @@ extern struct cred init_cred;
|
|
INIT_FTRACE_GRAPH \
|
|
INIT_TRACE_RECURSION \
|
|
INIT_TASK_RCU_PREEMPT(tsk) \
|
|
+ INIT_CPUSET_SEQ \
|
|
}
|
|
|
|
|
|
diff --git a/include/linux/migrate.h b/include/linux/migrate.h
|
|
index e39aeec..eaf8674 100644
|
|
--- a/include/linux/migrate.h
|
|
+++ b/include/linux/migrate.h
|
|
@@ -6,18 +6,31 @@
|
|
|
|
typedef struct page *new_page_t(struct page *, unsigned long private, int **);
|
|
|
|
+/*
|
|
+ * MIGRATE_ASYNC means never block
|
|
+ * MIGRATE_SYNC_LIGHT in the current implementation means to allow blocking
|
|
+ * on most operations but not ->writepage as the potential stall time
|
|
+ * is too significant
|
|
+ * MIGRATE_SYNC will block when migrating pages
|
|
+ */
|
|
+enum migrate_mode {
|
|
+ MIGRATE_ASYNC,
|
|
+ MIGRATE_SYNC_LIGHT,
|
|
+ MIGRATE_SYNC,
|
|
+};
|
|
+
|
|
#ifdef CONFIG_MIGRATION
|
|
#define PAGE_MIGRATION 1
|
|
|
|
extern void putback_lru_pages(struct list_head *l);
|
|
extern int migrate_page(struct address_space *,
|
|
- struct page *, struct page *);
|
|
+ struct page *, struct page *, enum migrate_mode);
|
|
extern int migrate_pages(struct list_head *l, new_page_t x,
|
|
unsigned long private, bool offlining,
|
|
- bool sync);
|
|
+ enum migrate_mode mode);
|
|
extern int migrate_huge_pages(struct list_head *l, new_page_t x,
|
|
unsigned long private, bool offlining,
|
|
- bool sync);
|
|
+ enum migrate_mode mode);
|
|
|
|
extern int fail_migrate_page(struct address_space *,
|
|
struct page *, struct page *);
|
|
@@ -36,10 +49,10 @@ extern int migrate_huge_page_move_mapping(struct address_space *mapping,
|
|
static inline void putback_lru_pages(struct list_head *l) {}
|
|
static inline int migrate_pages(struct list_head *l, new_page_t x,
|
|
unsigned long private, bool offlining,
|
|
- bool sync) { return -ENOSYS; }
|
|
+ enum migrate_mode mode) { return -ENOSYS; }
|
|
static inline int migrate_huge_pages(struct list_head *l, new_page_t x,
|
|
unsigned long private, bool offlining,
|
|
- bool sync) { return -ENOSYS; }
|
|
+ enum migrate_mode mode) { return -ENOSYS; }
|
|
|
|
static inline int migrate_prep(void) { return -ENOSYS; }
|
|
static inline int migrate_prep_local(void) { return -ENOSYS; }
|
|
diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
|
|
index 905b1e1..25842b6 100644
|
|
--- a/include/linux/mmzone.h
|
|
+++ b/include/linux/mmzone.h
|
|
@@ -173,6 +173,8 @@ static inline int is_unevictable_lru(enum lru_list l)
|
|
#define ISOLATE_CLEAN ((__force isolate_mode_t)0x4)
|
|
/* Isolate unmapped file */
|
|
#define ISOLATE_UNMAPPED ((__force isolate_mode_t)0x8)
|
|
+/* Isolate for asynchronous migration */
|
|
+#define ISOLATE_ASYNC_MIGRATE ((__force isolate_mode_t)0x10)
|
|
|
|
/* LRU Isolation modes. */
|
|
typedef unsigned __bitwise__ isolate_mode_t;
|
|
diff --git a/include/linux/sched.h b/include/linux/sched.h
|
|
index 5afa2a3..d336c35 100644
|
|
--- a/include/linux/sched.h
|
|
+++ b/include/linux/sched.h
|
|
@@ -145,6 +145,7 @@ extern unsigned long this_cpu_load(void);
|
|
|
|
|
|
extern void calc_global_load(unsigned long ticks);
|
|
+extern void update_cpu_load_nohz(void);
|
|
|
|
extern unsigned long get_parent_ip(unsigned long addr);
|
|
|
|
@@ -1481,7 +1482,7 @@ struct task_struct {
|
|
#endif
|
|
#ifdef CONFIG_CPUSETS
|
|
nodemask_t mems_allowed; /* Protected by alloc_lock */
|
|
- int mems_allowed_change_disable;
|
|
+ seqcount_t mems_allowed_seq; /* Seqence no to catch updates */
|
|
int cpuset_mem_spread_rotor;
|
|
int cpuset_slab_spread_rotor;
|
|
#endif
|
|
diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h
|
|
index 94bbec3..6ee550e 100644
|
|
--- a/include/target/target_core_base.h
|
|
+++ b/include/target/target_core_base.h
|
|
@@ -157,6 +157,7 @@ enum tcm_sense_reason_table {
|
|
TCM_CHECK_CONDITION_UNIT_ATTENTION = 0x0e,
|
|
TCM_CHECK_CONDITION_NOT_READY = 0x0f,
|
|
TCM_RESERVATION_CONFLICT = 0x10,
|
|
+ TCM_ADDRESS_OUT_OF_RANGE = 0x11,
|
|
};
|
|
|
|
struct se_obj {
|
|
diff --git a/kernel/cpuset.c b/kernel/cpuset.c
|
|
index 0b1712d..46a1d3c 100644
|
|
--- a/kernel/cpuset.c
|
|
+++ b/kernel/cpuset.c
|
|
@@ -964,7 +964,6 @@ static void cpuset_change_task_nodemask(struct task_struct *tsk,
|
|
{
|
|
bool need_loop;
|
|
|
|
-repeat:
|
|
/*
|
|
* Allow tasks that have access to memory reserves because they have
|
|
* been OOM killed to get memory anywhere.
|
|
@@ -983,45 +982,19 @@ repeat:
|
|
*/
|
|
need_loop = task_has_mempolicy(tsk) ||
|
|
!nodes_intersects(*newmems, tsk->mems_allowed);
|
|
- nodes_or(tsk->mems_allowed, tsk->mems_allowed, *newmems);
|
|
- mpol_rebind_task(tsk, newmems, MPOL_REBIND_STEP1);
|
|
|
|
- /*
|
|
- * ensure checking ->mems_allowed_change_disable after setting all new
|
|
- * allowed nodes.
|
|
- *
|
|
- * the read-side task can see an nodemask with new allowed nodes and
|
|
- * old allowed nodes. and if it allocates page when cpuset clears newly
|
|
- * disallowed ones continuous, it can see the new allowed bits.
|
|
- *
|
|
- * And if setting all new allowed nodes is after the checking, setting
|
|
- * all new allowed nodes and clearing newly disallowed ones will be done
|
|
- * continuous, and the read-side task may find no node to alloc page.
|
|
- */
|
|
- smp_mb();
|
|
+ if (need_loop)
|
|
+ write_seqcount_begin(&tsk->mems_allowed_seq);
|
|
|
|
- /*
|
|
- * Allocation of memory is very fast, we needn't sleep when waiting
|
|
- * for the read-side.
|
|
- */
|
|
- while (need_loop && ACCESS_ONCE(tsk->mems_allowed_change_disable)) {
|
|
- task_unlock(tsk);
|
|
- if (!task_curr(tsk))
|
|
- yield();
|
|
- goto repeat;
|
|
- }
|
|
-
|
|
- /*
|
|
- * ensure checking ->mems_allowed_change_disable before clearing all new
|
|
- * disallowed nodes.
|
|
- *
|
|
- * if clearing newly disallowed bits before the checking, the read-side
|
|
- * task may find no node to alloc page.
|
|
- */
|
|
- smp_mb();
|
|
+ nodes_or(tsk->mems_allowed, tsk->mems_allowed, *newmems);
|
|
+ mpol_rebind_task(tsk, newmems, MPOL_REBIND_STEP1);
|
|
|
|
mpol_rebind_task(tsk, newmems, MPOL_REBIND_STEP2);
|
|
tsk->mems_allowed = *newmems;
|
|
+
|
|
+ if (need_loop)
|
|
+ write_seqcount_end(&tsk->mems_allowed_seq);
|
|
+
|
|
task_unlock(tsk);
|
|
}
|
|
|
|
diff --git a/kernel/fork.c b/kernel/fork.c
|
|
index 79ee71f..222457a 100644
|
|
--- a/kernel/fork.c
|
|
+++ b/kernel/fork.c
|
|
@@ -979,6 +979,9 @@ static int copy_signal(unsigned long clone_flags, struct task_struct *tsk)
|
|
#ifdef CONFIG_CGROUPS
|
|
init_rwsem(&sig->threadgroup_fork_lock);
|
|
#endif
|
|
+#ifdef CONFIG_CPUSETS
|
|
+ seqcount_init(&tsk->mems_allowed_seq);
|
|
+#endif
|
|
|
|
sig->oom_adj = current->signal->oom_adj;
|
|
sig->oom_score_adj = current->signal->oom_score_adj;
|
|
diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c
|
|
index 7c0d578..013bd2e 100644
|
|
--- a/kernel/power/hibernate.c
|
|
+++ b/kernel/power/hibernate.c
|
|
@@ -367,6 +367,7 @@ int hibernation_snapshot(int platform_mode)
|
|
}
|
|
|
|
suspend_console();
|
|
+ ftrace_stop();
|
|
pm_restrict_gfp_mask();
|
|
error = dpm_suspend(PMSG_FREEZE);
|
|
if (error)
|
|
@@ -392,6 +393,7 @@ int hibernation_snapshot(int platform_mode)
|
|
if (error || !in_suspend)
|
|
pm_restore_gfp_mask();
|
|
|
|
+ ftrace_start();
|
|
resume_console();
|
|
dpm_complete(msg);
|
|
|
|
@@ -496,6 +498,7 @@ int hibernation_restore(int platform_mode)
|
|
|
|
pm_prepare_console();
|
|
suspend_console();
|
|
+ ftrace_stop();
|
|
pm_restrict_gfp_mask();
|
|
error = dpm_suspend_start(PMSG_QUIESCE);
|
|
if (!error) {
|
|
@@ -503,6 +506,7 @@ int hibernation_restore(int platform_mode)
|
|
dpm_resume_end(PMSG_RECOVER);
|
|
}
|
|
pm_restore_gfp_mask();
|
|
+ ftrace_start();
|
|
resume_console();
|
|
pm_restore_console();
|
|
return error;
|
|
@@ -529,6 +533,7 @@ int hibernation_platform_enter(void)
|
|
|
|
entering_platform_hibernation = true;
|
|
suspend_console();
|
|
+ ftrace_stop();
|
|
error = dpm_suspend_start(PMSG_HIBERNATE);
|
|
if (error) {
|
|
if (hibernation_ops->recover)
|
|
@@ -572,6 +577,7 @@ int hibernation_platform_enter(void)
|
|
Resume_devices:
|
|
entering_platform_hibernation = false;
|
|
dpm_resume_end(PMSG_RESTORE);
|
|
+ ftrace_start();
|
|
resume_console();
|
|
|
|
Close:
|
|
diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c
|
|
index 4953dc0..af48faa 100644
|
|
--- a/kernel/power/suspend.c
|
|
+++ b/kernel/power/suspend.c
|
|
@@ -25,6 +25,7 @@
|
|
#include <linux/export.h>
|
|
#include <linux/suspend.h>
|
|
#include <linux/syscore_ops.h>
|
|
+#include <linux/ftrace.h>
|
|
#include <trace/events/power.h>
|
|
|
|
#include "power.h"
|
|
@@ -220,6 +221,7 @@ int suspend_devices_and_enter(suspend_state_t state)
|
|
goto Close;
|
|
}
|
|
suspend_console();
|
|
+ ftrace_stop();
|
|
suspend_test_start();
|
|
error = dpm_suspend_start(PMSG_SUSPEND);
|
|
if (error) {
|
|
@@ -239,6 +241,7 @@ int suspend_devices_and_enter(suspend_state_t state)
|
|
suspend_test_start();
|
|
dpm_resume_end(PMSG_RESUME);
|
|
suspend_test_finish("resume devices");
|
|
+ ftrace_start();
|
|
resume_console();
|
|
Close:
|
|
if (suspend_ops->end)
|
|
diff --git a/kernel/sched.c b/kernel/sched.c
|
|
index 52ac69b..9cd8ca7 100644
|
|
--- a/kernel/sched.c
|
|
+++ b/kernel/sched.c
|
|
@@ -1887,7 +1887,7 @@ static void double_rq_unlock(struct rq *rq1, struct rq *rq2)
|
|
|
|
static void update_sysctl(void);
|
|
static int get_update_sysctl_factor(void);
|
|
-static void update_cpu_load(struct rq *this_rq);
|
|
+static void update_idle_cpu_load(struct rq *this_rq);
|
|
|
|
static inline void __set_task_cpu(struct task_struct *p, unsigned int cpu)
|
|
{
|
|
@@ -3855,22 +3855,13 @@ decay_load_missed(unsigned long load, unsigned long missed_updates, int idx)
|
|
* scheduler tick (TICK_NSEC). With tickless idle this will not be called
|
|
* every tick. We fix it up based on jiffies.
|
|
*/
|
|
-static void update_cpu_load(struct rq *this_rq)
|
|
+static void __update_cpu_load(struct rq *this_rq, unsigned long this_load,
|
|
+ unsigned long pending_updates)
|
|
{
|
|
- unsigned long this_load = this_rq->load.weight;
|
|
- unsigned long curr_jiffies = jiffies;
|
|
- unsigned long pending_updates;
|
|
int i, scale;
|
|
|
|
this_rq->nr_load_updates++;
|
|
|
|
- /* Avoid repeated calls on same jiffy, when moving in and out of idle */
|
|
- if (curr_jiffies == this_rq->last_load_update_tick)
|
|
- return;
|
|
-
|
|
- pending_updates = curr_jiffies - this_rq->last_load_update_tick;
|
|
- this_rq->last_load_update_tick = curr_jiffies;
|
|
-
|
|
/* Update our load: */
|
|
this_rq->cpu_load[0] = this_load; /* Fasttrack for idx 0 */
|
|
for (i = 1, scale = 2; i < CPU_LOAD_IDX_MAX; i++, scale += scale) {
|
|
@@ -3895,9 +3886,78 @@ static void update_cpu_load(struct rq *this_rq)
|
|
sched_avg_update(this_rq);
|
|
}
|
|
|
|
+#ifdef CONFIG_NO_HZ
|
|
+/*
|
|
+ * There is no sane way to deal with nohz on smp when using jiffies because the
|
|
+ * cpu doing the jiffies update might drift wrt the cpu doing the jiffy reading
|
|
+ * causing off-by-one errors in observed deltas; {0,2} instead of {1,1}.
|
|
+ *
|
|
+ * Therefore we cannot use the delta approach from the regular tick since that
|
|
+ * would seriously skew the load calculation. However we'll make do for those
|
|
+ * updates happening while idle (nohz_idle_balance) or coming out of idle
|
|
+ * (tick_nohz_idle_exit).
|
|
+ *
|
|
+ * This means we might still be one tick off for nohz periods.
|
|
+ */
|
|
+
|
|
+/*
|
|
+ * Called from nohz_idle_balance() to update the load ratings before doing the
|
|
+ * idle balance.
|
|
+ */
|
|
+static void update_idle_cpu_load(struct rq *this_rq)
|
|
+{
|
|
+ unsigned long curr_jiffies = ACCESS_ONCE(jiffies);
|
|
+ unsigned long load = this_rq->load.weight;
|
|
+ unsigned long pending_updates;
|
|
+
|
|
+ /*
|
|
+ * bail if there's load or we're actually up-to-date.
|
|
+ */
|
|
+ if (load || curr_jiffies == this_rq->last_load_update_tick)
|
|
+ return;
|
|
+
|
|
+ pending_updates = curr_jiffies - this_rq->last_load_update_tick;
|
|
+ this_rq->last_load_update_tick = curr_jiffies;
|
|
+
|
|
+ __update_cpu_load(this_rq, load, pending_updates);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Called from tick_nohz_idle_exit() -- try and fix up the ticks we missed.
|
|
+ */
|
|
+void update_cpu_load_nohz(void)
|
|
+{
|
|
+ struct rq *this_rq = this_rq();
|
|
+ unsigned long curr_jiffies = ACCESS_ONCE(jiffies);
|
|
+ unsigned long pending_updates;
|
|
+
|
|
+ if (curr_jiffies == this_rq->last_load_update_tick)
|
|
+ return;
|
|
+
|
|
+ raw_spin_lock(&this_rq->lock);
|
|
+ pending_updates = curr_jiffies - this_rq->last_load_update_tick;
|
|
+ if (pending_updates) {
|
|
+ this_rq->last_load_update_tick = curr_jiffies;
|
|
+ /*
|
|
+ * We were idle, this means load 0, the current load might be
|
|
+ * !0 due to remote wakeups and the sort.
|
|
+ */
|
|
+ __update_cpu_load(this_rq, 0, pending_updates);
|
|
+ }
|
|
+ raw_spin_unlock(&this_rq->lock);
|
|
+}
|
|
+#endif /* CONFIG_NO_HZ */
|
|
+
|
|
+/*
|
|
+ * Called from scheduler_tick()
|
|
+ */
|
|
static void update_cpu_load_active(struct rq *this_rq)
|
|
{
|
|
- update_cpu_load(this_rq);
|
|
+ /*
|
|
+ * See the mess around update_idle_cpu_load() / update_cpu_load_nohz().
|
|
+ */
|
|
+ this_rq->last_load_update_tick = jiffies;
|
|
+ __update_cpu_load(this_rq, this_rq->load.weight, 1);
|
|
|
|
calc_load_account_active(this_rq);
|
|
}
|
|
diff --git a/kernel/sched_fair.c b/kernel/sched_fair.c
|
|
index 8a39fa3..66e4576 100644
|
|
--- a/kernel/sched_fair.c
|
|
+++ b/kernel/sched_fair.c
|
|
@@ -4735,7 +4735,7 @@ static void nohz_idle_balance(int this_cpu, enum cpu_idle_type idle)
|
|
|
|
raw_spin_lock_irq(&this_rq->lock);
|
|
update_rq_clock(this_rq);
|
|
- update_cpu_load(this_rq);
|
|
+ update_idle_cpu_load(this_rq);
|
|
raw_spin_unlock_irq(&this_rq->lock);
|
|
|
|
rebalance_domains(balance_cpu, CPU_IDLE);
|
|
diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c
|
|
index 9955ebd..793548c 100644
|
|
--- a/kernel/time/tick-sched.c
|
|
+++ b/kernel/time/tick-sched.c
|
|
@@ -549,6 +549,7 @@ void tick_nohz_restart_sched_tick(void)
|
|
/* Update jiffies first */
|
|
select_nohz_load_balancer(0);
|
|
tick_do_update_jiffies64(now);
|
|
+ update_cpu_load_nohz();
|
|
|
|
#ifndef CONFIG_VIRT_CPU_ACCOUNTING
|
|
/*
|
|
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
|
|
index 7947e16..a650bee 100644
|
|
--- a/kernel/workqueue.c
|
|
+++ b/kernel/workqueue.c
|
|
@@ -3586,6 +3586,41 @@ static int __devinit workqueue_cpu_callback(struct notifier_block *nfb,
|
|
return notifier_from_errno(0);
|
|
}
|
|
|
|
+/*
|
|
+ * Workqueues should be brought up before normal priority CPU notifiers.
|
|
+ * This will be registered high priority CPU notifier.
|
|
+ */
|
|
+static int __devinit workqueue_cpu_up_callback(struct notifier_block *nfb,
|
|
+ unsigned long action,
|
|
+ void *hcpu)
|
|
+{
|
|
+ switch (action & ~CPU_TASKS_FROZEN) {
|
|
+ case CPU_UP_PREPARE:
|
|
+ case CPU_UP_CANCELED:
|
|
+ case CPU_DOWN_FAILED:
|
|
+ case CPU_ONLINE:
|
|
+ return workqueue_cpu_callback(nfb, action, hcpu);
|
|
+ }
|
|
+ return NOTIFY_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Workqueues should be brought down after normal priority CPU notifiers.
|
|
+ * This will be registered as low priority CPU notifier.
|
|
+ */
|
|
+static int __devinit workqueue_cpu_down_callback(struct notifier_block *nfb,
|
|
+ unsigned long action,
|
|
+ void *hcpu)
|
|
+{
|
|
+ switch (action & ~CPU_TASKS_FROZEN) {
|
|
+ case CPU_DOWN_PREPARE:
|
|
+ case CPU_DYING:
|
|
+ case CPU_POST_DEAD:
|
|
+ return workqueue_cpu_callback(nfb, action, hcpu);
|
|
+ }
|
|
+ return NOTIFY_OK;
|
|
+}
|
|
+
|
|
#ifdef CONFIG_SMP
|
|
|
|
struct work_for_cpu {
|
|
@@ -3779,7 +3814,8 @@ static int __init init_workqueues(void)
|
|
unsigned int cpu;
|
|
int i;
|
|
|
|
- cpu_notifier(workqueue_cpu_callback, CPU_PRI_WORKQUEUE);
|
|
+ cpu_notifier(workqueue_cpu_up_callback, CPU_PRI_WORKQUEUE_UP);
|
|
+ cpu_notifier(workqueue_cpu_down_callback, CPU_PRI_WORKQUEUE_DOWN);
|
|
|
|
/* initialize gcwqs */
|
|
for_each_gcwq_cpu(cpu) {
|
|
diff --git a/mm/compaction.c b/mm/compaction.c
|
|
index 50f1c60..46973fb 100644
|
|
--- a/mm/compaction.c
|
|
+++ b/mm/compaction.c
|
|
@@ -372,7 +372,7 @@ static isolate_migrate_t isolate_migratepages(struct zone *zone,
|
|
}
|
|
|
|
if (!cc->sync)
|
|
- mode |= ISOLATE_CLEAN;
|
|
+ mode |= ISOLATE_ASYNC_MIGRATE;
|
|
|
|
/* Try isolate the page */
|
|
if (__isolate_lru_page(page, mode, 0) != 0)
|
|
@@ -577,7 +577,7 @@ static int compact_zone(struct zone *zone, struct compact_control *cc)
|
|
nr_migrate = cc->nr_migratepages;
|
|
err = migrate_pages(&cc->migratepages, compaction_alloc,
|
|
(unsigned long)cc, false,
|
|
- cc->sync);
|
|
+ cc->sync ? MIGRATE_SYNC_LIGHT : MIGRATE_ASYNC);
|
|
update_nr_listpages(cc);
|
|
nr_remaining = cc->nr_migratepages;
|
|
|
|
diff --git a/mm/filemap.c b/mm/filemap.c
|
|
index 03c5b0e..556858c 100644
|
|
--- a/mm/filemap.c
|
|
+++ b/mm/filemap.c
|
|
@@ -500,10 +500,13 @@ struct page *__page_cache_alloc(gfp_t gfp)
|
|
struct page *page;
|
|
|
|
if (cpuset_do_page_mem_spread()) {
|
|
- get_mems_allowed();
|
|
- n = cpuset_mem_spread_node();
|
|
- page = alloc_pages_exact_node(n, gfp, 0);
|
|
- put_mems_allowed();
|
|
+ unsigned int cpuset_mems_cookie;
|
|
+ do {
|
|
+ cpuset_mems_cookie = get_mems_allowed();
|
|
+ n = cpuset_mem_spread_node();
|
|
+ page = alloc_pages_exact_node(n, gfp, 0);
|
|
+ } while (!put_mems_allowed(cpuset_mems_cookie) && !page);
|
|
+
|
|
return page;
|
|
}
|
|
return alloc_pages(gfp, 0);
|
|
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
|
|
index 7c535b0..b1e1bad 100644
|
|
--- a/mm/hugetlb.c
|
|
+++ b/mm/hugetlb.c
|
|
@@ -538,8 +538,10 @@ static struct page *dequeue_huge_page_vma(struct hstate *h,
|
|
struct zonelist *zonelist;
|
|
struct zone *zone;
|
|
struct zoneref *z;
|
|
+ unsigned int cpuset_mems_cookie;
|
|
|
|
- get_mems_allowed();
|
|
+retry_cpuset:
|
|
+ cpuset_mems_cookie = get_mems_allowed();
|
|
zonelist = huge_zonelist(vma, address,
|
|
htlb_alloc_mask, &mpol, &nodemask);
|
|
/*
|
|
@@ -566,10 +568,15 @@ static struct page *dequeue_huge_page_vma(struct hstate *h,
|
|
}
|
|
}
|
|
}
|
|
-err:
|
|
+
|
|
mpol_cond_put(mpol);
|
|
- put_mems_allowed();
|
|
+ if (unlikely(!put_mems_allowed(cpuset_mems_cookie) && !page))
|
|
+ goto retry_cpuset;
|
|
return page;
|
|
+
|
|
+err:
|
|
+ mpol_cond_put(mpol);
|
|
+ return NULL;
|
|
}
|
|
|
|
static void update_and_free_page(struct hstate *h, struct page *page)
|
|
diff --git a/mm/memory-failure.c b/mm/memory-failure.c
|
|
index 06d3479..5bd5bb1 100644
|
|
--- a/mm/memory-failure.c
|
|
+++ b/mm/memory-failure.c
|
|
@@ -1427,8 +1427,8 @@ static int soft_offline_huge_page(struct page *page, int flags)
|
|
/* Keep page count to indicate a given hugepage is isolated. */
|
|
|
|
list_add(&hpage->lru, &pagelist);
|
|
- ret = migrate_huge_pages(&pagelist, new_page, MPOL_MF_MOVE_ALL, 0,
|
|
- true);
|
|
+ ret = migrate_huge_pages(&pagelist, new_page, MPOL_MF_MOVE_ALL, false,
|
|
+ MIGRATE_SYNC);
|
|
if (ret) {
|
|
struct page *page1, *page2;
|
|
list_for_each_entry_safe(page1, page2, &pagelist, lru)
|
|
@@ -1557,7 +1557,7 @@ int soft_offline_page(struct page *page, int flags)
|
|
page_is_file_cache(page));
|
|
list_add(&page->lru, &pagelist);
|
|
ret = migrate_pages(&pagelist, new_page, MPOL_MF_MOVE_ALL,
|
|
- 0, true);
|
|
+ false, MIGRATE_SYNC);
|
|
if (ret) {
|
|
putback_lru_pages(&pagelist);
|
|
pr_info("soft offline: %#lx: migration failed %d, type %lx\n",
|
|
diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
|
|
index 2168489..6629faf 100644
|
|
--- a/mm/memory_hotplug.c
|
|
+++ b/mm/memory_hotplug.c
|
|
@@ -809,7 +809,7 @@ do_migrate_range(unsigned long start_pfn, unsigned long end_pfn)
|
|
}
|
|
/* this function returns # of failed pages */
|
|
ret = migrate_pages(&source, hotremove_migrate_alloc, 0,
|
|
- true, true);
|
|
+ true, MIGRATE_SYNC);
|
|
if (ret)
|
|
putback_lru_pages(&source);
|
|
}
|
|
diff --git a/mm/mempolicy.c b/mm/mempolicy.c
|
|
index b26aae2..c0007f9 100644
|
|
--- a/mm/mempolicy.c
|
|
+++ b/mm/mempolicy.c
|
|
@@ -942,7 +942,7 @@ static int migrate_to_node(struct mm_struct *mm, int source, int dest,
|
|
|
|
if (!list_empty(&pagelist)) {
|
|
err = migrate_pages(&pagelist, new_node_page, dest,
|
|
- false, true);
|
|
+ false, MIGRATE_SYNC);
|
|
if (err)
|
|
putback_lru_pages(&pagelist);
|
|
}
|
|
@@ -1843,18 +1843,24 @@ struct page *
|
|
alloc_pages_vma(gfp_t gfp, int order, struct vm_area_struct *vma,
|
|
unsigned long addr, int node)
|
|
{
|
|
- struct mempolicy *pol = get_vma_policy(current, vma, addr);
|
|
+ struct mempolicy *pol;
|
|
struct zonelist *zl;
|
|
struct page *page;
|
|
+ unsigned int cpuset_mems_cookie;
|
|
+
|
|
+retry_cpuset:
|
|
+ pol = get_vma_policy(current, vma, addr);
|
|
+ cpuset_mems_cookie = get_mems_allowed();
|
|
|
|
- get_mems_allowed();
|
|
if (unlikely(pol->mode == MPOL_INTERLEAVE)) {
|
|
unsigned nid;
|
|
|
|
nid = interleave_nid(pol, vma, addr, PAGE_SHIFT + order);
|
|
mpol_cond_put(pol);
|
|
page = alloc_page_interleave(gfp, order, nid);
|
|
- put_mems_allowed();
|
|
+ if (unlikely(!put_mems_allowed(cpuset_mems_cookie) && !page))
|
|
+ goto retry_cpuset;
|
|
+
|
|
return page;
|
|
}
|
|
zl = policy_zonelist(gfp, pol, node);
|
|
@@ -1865,7 +1871,8 @@ alloc_pages_vma(gfp_t gfp, int order, struct vm_area_struct *vma,
|
|
struct page *page = __alloc_pages_nodemask(gfp, order,
|
|
zl, policy_nodemask(gfp, pol));
|
|
__mpol_put(pol);
|
|
- put_mems_allowed();
|
|
+ if (unlikely(!put_mems_allowed(cpuset_mems_cookie) && !page))
|
|
+ goto retry_cpuset;
|
|
return page;
|
|
}
|
|
/*
|
|
@@ -1873,7 +1880,8 @@ alloc_pages_vma(gfp_t gfp, int order, struct vm_area_struct *vma,
|
|
*/
|
|
page = __alloc_pages_nodemask(gfp, order, zl,
|
|
policy_nodemask(gfp, pol));
|
|
- put_mems_allowed();
|
|
+ if (unlikely(!put_mems_allowed(cpuset_mems_cookie) && !page))
|
|
+ goto retry_cpuset;
|
|
return page;
|
|
}
|
|
|
|
@@ -1900,11 +1908,14 @@ struct page *alloc_pages_current(gfp_t gfp, unsigned order)
|
|
{
|
|
struct mempolicy *pol = current->mempolicy;
|
|
struct page *page;
|
|
+ unsigned int cpuset_mems_cookie;
|
|
|
|
if (!pol || in_interrupt() || (gfp & __GFP_THISNODE))
|
|
pol = &default_policy;
|
|
|
|
- get_mems_allowed();
|
|
+retry_cpuset:
|
|
+ cpuset_mems_cookie = get_mems_allowed();
|
|
+
|
|
/*
|
|
* No reference counting needed for current->mempolicy
|
|
* nor system default_policy
|
|
@@ -1915,7 +1926,10 @@ struct page *alloc_pages_current(gfp_t gfp, unsigned order)
|
|
page = __alloc_pages_nodemask(gfp, order,
|
|
policy_zonelist(gfp, pol, numa_node_id()),
|
|
policy_nodemask(gfp, pol));
|
|
- put_mems_allowed();
|
|
+
|
|
+ if (unlikely(!put_mems_allowed(cpuset_mems_cookie) && !page))
|
|
+ goto retry_cpuset;
|
|
+
|
|
return page;
|
|
}
|
|
EXPORT_SYMBOL(alloc_pages_current);
|
|
diff --git a/mm/migrate.c b/mm/migrate.c
|
|
index 177aca4..180d97f 100644
|
|
--- a/mm/migrate.c
|
|
+++ b/mm/migrate.c
|
|
@@ -220,6 +220,56 @@ out:
|
|
pte_unmap_unlock(ptep, ptl);
|
|
}
|
|
|
|
+#ifdef CONFIG_BLOCK
|
|
+/* Returns true if all buffers are successfully locked */
|
|
+static bool buffer_migrate_lock_buffers(struct buffer_head *head,
|
|
+ enum migrate_mode mode)
|
|
+{
|
|
+ struct buffer_head *bh = head;
|
|
+
|
|
+ /* Simple case, sync compaction */
|
|
+ if (mode != MIGRATE_ASYNC) {
|
|
+ do {
|
|
+ get_bh(bh);
|
|
+ lock_buffer(bh);
|
|
+ bh = bh->b_this_page;
|
|
+
|
|
+ } while (bh != head);
|
|
+
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ /* async case, we cannot block on lock_buffer so use trylock_buffer */
|
|
+ do {
|
|
+ get_bh(bh);
|
|
+ if (!trylock_buffer(bh)) {
|
|
+ /*
|
|
+ * We failed to lock the buffer and cannot stall in
|
|
+ * async migration. Release the taken locks
|
|
+ */
|
|
+ struct buffer_head *failed_bh = bh;
|
|
+ put_bh(failed_bh);
|
|
+ bh = head;
|
|
+ while (bh != failed_bh) {
|
|
+ unlock_buffer(bh);
|
|
+ put_bh(bh);
|
|
+ bh = bh->b_this_page;
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ bh = bh->b_this_page;
|
|
+ } while (bh != head);
|
|
+ return true;
|
|
+}
|
|
+#else
|
|
+static inline bool buffer_migrate_lock_buffers(struct buffer_head *head,
|
|
+ enum migrate_mode mode)
|
|
+{
|
|
+ return true;
|
|
+}
|
|
+#endif /* CONFIG_BLOCK */
|
|
+
|
|
/*
|
|
* Replace the page in the mapping.
|
|
*
|
|
@@ -229,7 +279,8 @@ out:
|
|
* 3 for pages with a mapping and PagePrivate/PagePrivate2 set.
|
|
*/
|
|
static int migrate_page_move_mapping(struct address_space *mapping,
|
|
- struct page *newpage, struct page *page)
|
|
+ struct page *newpage, struct page *page,
|
|
+ struct buffer_head *head, enum migrate_mode mode)
|
|
{
|
|
int expected_count;
|
|
void **pslot;
|
|
@@ -259,6 +310,20 @@ static int migrate_page_move_mapping(struct address_space *mapping,
|
|
}
|
|
|
|
/*
|
|
+ * In the async migration case of moving a page with buffers, lock the
|
|
+ * buffers using trylock before the mapping is moved. If the mapping
|
|
+ * was moved, we later failed to lock the buffers and could not move
|
|
+ * the mapping back due to an elevated page count, we would have to
|
|
+ * block waiting on other references to be dropped.
|
|
+ */
|
|
+ if (mode == MIGRATE_ASYNC && head &&
|
|
+ !buffer_migrate_lock_buffers(head, mode)) {
|
|
+ page_unfreeze_refs(page, expected_count);
|
|
+ spin_unlock_irq(&mapping->tree_lock);
|
|
+ return -EAGAIN;
|
|
+ }
|
|
+
|
|
+ /*
|
|
* Now we know that no one else is looking at the page.
|
|
*/
|
|
get_page(newpage); /* add cache reference */
|
|
@@ -415,13 +480,14 @@ EXPORT_SYMBOL(fail_migrate_page);
|
|
* Pages are locked upon entry and exit.
|
|
*/
|
|
int migrate_page(struct address_space *mapping,
|
|
- struct page *newpage, struct page *page)
|
|
+ struct page *newpage, struct page *page,
|
|
+ enum migrate_mode mode)
|
|
{
|
|
int rc;
|
|
|
|
BUG_ON(PageWriteback(page)); /* Writeback must be complete */
|
|
|
|
- rc = migrate_page_move_mapping(mapping, newpage, page);
|
|
+ rc = migrate_page_move_mapping(mapping, newpage, page, NULL, mode);
|
|
|
|
if (rc)
|
|
return rc;
|
|
@@ -438,28 +504,28 @@ EXPORT_SYMBOL(migrate_page);
|
|
* exist.
|
|
*/
|
|
int buffer_migrate_page(struct address_space *mapping,
|
|
- struct page *newpage, struct page *page)
|
|
+ struct page *newpage, struct page *page, enum migrate_mode mode)
|
|
{
|
|
struct buffer_head *bh, *head;
|
|
int rc;
|
|
|
|
if (!page_has_buffers(page))
|
|
- return migrate_page(mapping, newpage, page);
|
|
+ return migrate_page(mapping, newpage, page, mode);
|
|
|
|
head = page_buffers(page);
|
|
|
|
- rc = migrate_page_move_mapping(mapping, newpage, page);
|
|
+ rc = migrate_page_move_mapping(mapping, newpage, page, head, mode);
|
|
|
|
if (rc)
|
|
return rc;
|
|
|
|
- bh = head;
|
|
- do {
|
|
- get_bh(bh);
|
|
- lock_buffer(bh);
|
|
- bh = bh->b_this_page;
|
|
-
|
|
- } while (bh != head);
|
|
+ /*
|
|
+ * In the async case, migrate_page_move_mapping locked the buffers
|
|
+ * with an IRQ-safe spinlock held. In the sync case, the buffers
|
|
+ * need to be locked now
|
|
+ */
|
|
+ if (mode != MIGRATE_ASYNC)
|
|
+ BUG_ON(!buffer_migrate_lock_buffers(head, mode));
|
|
|
|
ClearPagePrivate(page);
|
|
set_page_private(newpage, page_private(page));
|
|
@@ -536,10 +602,14 @@ static int writeout(struct address_space *mapping, struct page *page)
|
|
* Default handling if a filesystem does not provide a migration function.
|
|
*/
|
|
static int fallback_migrate_page(struct address_space *mapping,
|
|
- struct page *newpage, struct page *page)
|
|
+ struct page *newpage, struct page *page, enum migrate_mode mode)
|
|
{
|
|
- if (PageDirty(page))
|
|
+ if (PageDirty(page)) {
|
|
+ /* Only writeback pages in full synchronous migration */
|
|
+ if (mode != MIGRATE_SYNC)
|
|
+ return -EBUSY;
|
|
return writeout(mapping, page);
|
|
+ }
|
|
|
|
/*
|
|
* Buffers may be managed in a filesystem specific way.
|
|
@@ -549,7 +619,7 @@ static int fallback_migrate_page(struct address_space *mapping,
|
|
!try_to_release_page(page, GFP_KERNEL))
|
|
return -EAGAIN;
|
|
|
|
- return migrate_page(mapping, newpage, page);
|
|
+ return migrate_page(mapping, newpage, page, mode);
|
|
}
|
|
|
|
/*
|
|
@@ -564,7 +634,7 @@ static int fallback_migrate_page(struct address_space *mapping,
|
|
* == 0 - success
|
|
*/
|
|
static int move_to_new_page(struct page *newpage, struct page *page,
|
|
- int remap_swapcache, bool sync)
|
|
+ int remap_swapcache, enum migrate_mode mode)
|
|
{
|
|
struct address_space *mapping;
|
|
int rc;
|
|
@@ -585,29 +655,18 @@ static int move_to_new_page(struct page *newpage, struct page *page,
|
|
|
|
mapping = page_mapping(page);
|
|
if (!mapping)
|
|
- rc = migrate_page(mapping, newpage, page);
|
|
- else {
|
|
+ rc = migrate_page(mapping, newpage, page, mode);
|
|
+ else if (mapping->a_ops->migratepage)
|
|
/*
|
|
- * Do not writeback pages if !sync and migratepage is
|
|
- * not pointing to migrate_page() which is nonblocking
|
|
- * (swapcache/tmpfs uses migratepage = migrate_page).
|
|
+ * Most pages have a mapping and most filesystems provide a
|
|
+ * migratepage callback. Anonymous pages are part of swap
|
|
+ * space which also has its own migratepage callback. This
|
|
+ * is the most common path for page migration.
|
|
*/
|
|
- if (PageDirty(page) && !sync &&
|
|
- mapping->a_ops->migratepage != migrate_page)
|
|
- rc = -EBUSY;
|
|
- else if (mapping->a_ops->migratepage)
|
|
- /*
|
|
- * Most pages have a mapping and most filesystems
|
|
- * should provide a migration function. Anonymous
|
|
- * pages are part of swap space which also has its
|
|
- * own migration function. This is the most common
|
|
- * path for page migration.
|
|
- */
|
|
- rc = mapping->a_ops->migratepage(mapping,
|
|
- newpage, page);
|
|
- else
|
|
- rc = fallback_migrate_page(mapping, newpage, page);
|
|
- }
|
|
+ rc = mapping->a_ops->migratepage(mapping,
|
|
+ newpage, page, mode);
|
|
+ else
|
|
+ rc = fallback_migrate_page(mapping, newpage, page, mode);
|
|
|
|
if (rc) {
|
|
newpage->mapping = NULL;
|
|
@@ -622,7 +681,7 @@ static int move_to_new_page(struct page *newpage, struct page *page,
|
|
}
|
|
|
|
static int __unmap_and_move(struct page *page, struct page *newpage,
|
|
- int force, bool offlining, bool sync)
|
|
+ int force, bool offlining, enum migrate_mode mode)
|
|
{
|
|
int rc = -EAGAIN;
|
|
int remap_swapcache = 1;
|
|
@@ -631,7 +690,7 @@ static int __unmap_and_move(struct page *page, struct page *newpage,
|
|
struct anon_vma *anon_vma = NULL;
|
|
|
|
if (!trylock_page(page)) {
|
|
- if (!force || !sync)
|
|
+ if (!force || mode == MIGRATE_ASYNC)
|
|
goto out;
|
|
|
|
/*
|
|
@@ -677,10 +736,12 @@ static int __unmap_and_move(struct page *page, struct page *newpage,
|
|
|
|
if (PageWriteback(page)) {
|
|
/*
|
|
- * For !sync, there is no point retrying as the retry loop
|
|
- * is expected to be too short for PageWriteback to be cleared
|
|
+ * Only in the case of a full syncronous migration is it
|
|
+ * necessary to wait for PageWriteback. In the async case,
|
|
+ * the retry loop is too short and in the sync-light case,
|
|
+ * the overhead of stalling is too much
|
|
*/
|
|
- if (!sync) {
|
|
+ if (mode != MIGRATE_SYNC) {
|
|
rc = -EBUSY;
|
|
goto uncharge;
|
|
}
|
|
@@ -751,7 +812,7 @@ static int __unmap_and_move(struct page *page, struct page *newpage,
|
|
|
|
skip_unmap:
|
|
if (!page_mapped(page))
|
|
- rc = move_to_new_page(newpage, page, remap_swapcache, sync);
|
|
+ rc = move_to_new_page(newpage, page, remap_swapcache, mode);
|
|
|
|
if (rc && remap_swapcache)
|
|
remove_migration_ptes(page, page);
|
|
@@ -774,7 +835,8 @@ out:
|
|
* to the newly allocated page in newpage.
|
|
*/
|
|
static int unmap_and_move(new_page_t get_new_page, unsigned long private,
|
|
- struct page *page, int force, bool offlining, bool sync)
|
|
+ struct page *page, int force, bool offlining,
|
|
+ enum migrate_mode mode)
|
|
{
|
|
int rc = 0;
|
|
int *result = NULL;
|
|
@@ -792,7 +854,7 @@ static int unmap_and_move(new_page_t get_new_page, unsigned long private,
|
|
if (unlikely(split_huge_page(page)))
|
|
goto out;
|
|
|
|
- rc = __unmap_and_move(page, newpage, force, offlining, sync);
|
|
+ rc = __unmap_and_move(page, newpage, force, offlining, mode);
|
|
out:
|
|
if (rc != -EAGAIN) {
|
|
/*
|
|
@@ -840,7 +902,8 @@ out:
|
|
*/
|
|
static int unmap_and_move_huge_page(new_page_t get_new_page,
|
|
unsigned long private, struct page *hpage,
|
|
- int force, bool offlining, bool sync)
|
|
+ int force, bool offlining,
|
|
+ enum migrate_mode mode)
|
|
{
|
|
int rc = 0;
|
|
int *result = NULL;
|
|
@@ -853,7 +916,7 @@ static int unmap_and_move_huge_page(new_page_t get_new_page,
|
|
rc = -EAGAIN;
|
|
|
|
if (!trylock_page(hpage)) {
|
|
- if (!force || !sync)
|
|
+ if (!force || mode != MIGRATE_SYNC)
|
|
goto out;
|
|
lock_page(hpage);
|
|
}
|
|
@@ -864,7 +927,7 @@ static int unmap_and_move_huge_page(new_page_t get_new_page,
|
|
try_to_unmap(hpage, TTU_MIGRATION|TTU_IGNORE_MLOCK|TTU_IGNORE_ACCESS);
|
|
|
|
if (!page_mapped(hpage))
|
|
- rc = move_to_new_page(new_hpage, hpage, 1, sync);
|
|
+ rc = move_to_new_page(new_hpage, hpage, 1, mode);
|
|
|
|
if (rc)
|
|
remove_migration_ptes(hpage, hpage);
|
|
@@ -907,7 +970,7 @@ out:
|
|
*/
|
|
int migrate_pages(struct list_head *from,
|
|
new_page_t get_new_page, unsigned long private, bool offlining,
|
|
- bool sync)
|
|
+ enum migrate_mode mode)
|
|
{
|
|
int retry = 1;
|
|
int nr_failed = 0;
|
|
@@ -928,7 +991,7 @@ int migrate_pages(struct list_head *from,
|
|
|
|
rc = unmap_and_move(get_new_page, private,
|
|
page, pass > 2, offlining,
|
|
- sync);
|
|
+ mode);
|
|
|
|
switch(rc) {
|
|
case -ENOMEM:
|
|
@@ -958,7 +1021,7 @@ out:
|
|
|
|
int migrate_huge_pages(struct list_head *from,
|
|
new_page_t get_new_page, unsigned long private, bool offlining,
|
|
- bool sync)
|
|
+ enum migrate_mode mode)
|
|
{
|
|
int retry = 1;
|
|
int nr_failed = 0;
|
|
@@ -975,7 +1038,7 @@ int migrate_huge_pages(struct list_head *from,
|
|
|
|
rc = unmap_and_move_huge_page(get_new_page,
|
|
private, page, pass > 2, offlining,
|
|
- sync);
|
|
+ mode);
|
|
|
|
switch(rc) {
|
|
case -ENOMEM:
|
|
@@ -1104,7 +1167,7 @@ set_status:
|
|
err = 0;
|
|
if (!list_empty(&pagelist)) {
|
|
err = migrate_pages(&pagelist, new_page_node,
|
|
- (unsigned long)pm, 0, true);
|
|
+ (unsigned long)pm, 0, MIGRATE_SYNC);
|
|
if (err)
|
|
putback_lru_pages(&pagelist);
|
|
}
|
|
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
|
|
index 485be89..065dbe8 100644
|
|
--- a/mm/page_alloc.c
|
|
+++ b/mm/page_alloc.c
|
|
@@ -1886,14 +1886,20 @@ static struct page *
|
|
__alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order,
|
|
struct zonelist *zonelist, enum zone_type high_zoneidx,
|
|
nodemask_t *nodemask, int alloc_flags, struct zone *preferred_zone,
|
|
- int migratetype, unsigned long *did_some_progress,
|
|
- bool sync_migration)
|
|
+ int migratetype, bool sync_migration,
|
|
+ bool *deferred_compaction,
|
|
+ unsigned long *did_some_progress)
|
|
{
|
|
struct page *page;
|
|
|
|
- if (!order || compaction_deferred(preferred_zone))
|
|
+ if (!order)
|
|
return NULL;
|
|
|
|
+ if (compaction_deferred(preferred_zone)) {
|
|
+ *deferred_compaction = true;
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
current->flags |= PF_MEMALLOC;
|
|
*did_some_progress = try_to_compact_pages(zonelist, order, gfp_mask,
|
|
nodemask, sync_migration);
|
|
@@ -1921,7 +1927,13 @@ __alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order,
|
|
* but not enough to satisfy watermarks.
|
|
*/
|
|
count_vm_event(COMPACTFAIL);
|
|
- defer_compaction(preferred_zone);
|
|
+
|
|
+ /*
|
|
+ * As async compaction considers a subset of pageblocks, only
|
|
+ * defer if the failure was a sync compaction failure.
|
|
+ */
|
|
+ if (sync_migration)
|
|
+ defer_compaction(preferred_zone);
|
|
|
|
cond_resched();
|
|
}
|
|
@@ -1933,8 +1945,9 @@ static inline struct page *
|
|
__alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order,
|
|
struct zonelist *zonelist, enum zone_type high_zoneidx,
|
|
nodemask_t *nodemask, int alloc_flags, struct zone *preferred_zone,
|
|
- int migratetype, unsigned long *did_some_progress,
|
|
- bool sync_migration)
|
|
+ int migratetype, bool sync_migration,
|
|
+ bool *deferred_compaction,
|
|
+ unsigned long *did_some_progress)
|
|
{
|
|
return NULL;
|
|
}
|
|
@@ -2084,6 +2097,7 @@ __alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order,
|
|
unsigned long pages_reclaimed = 0;
|
|
unsigned long did_some_progress;
|
|
bool sync_migration = false;
|
|
+ bool deferred_compaction = false;
|
|
|
|
/*
|
|
* In the slowpath, we sanity check order to avoid ever trying to
|
|
@@ -2164,12 +2178,22 @@ rebalance:
|
|
zonelist, high_zoneidx,
|
|
nodemask,
|
|
alloc_flags, preferred_zone,
|
|
- migratetype, &did_some_progress,
|
|
- sync_migration);
|
|
+ migratetype, sync_migration,
|
|
+ &deferred_compaction,
|
|
+ &did_some_progress);
|
|
if (page)
|
|
goto got_pg;
|
|
sync_migration = true;
|
|
|
|
+ /*
|
|
+ * If compaction is deferred for high-order allocations, it is because
|
|
+ * sync compaction recently failed. In this is the case and the caller
|
|
+ * has requested the system not be heavily disrupted, fail the
|
|
+ * allocation now instead of entering direct reclaim
|
|
+ */
|
|
+ if (deferred_compaction && (gfp_mask & __GFP_NO_KSWAPD))
|
|
+ goto nopage;
|
|
+
|
|
/* Try direct reclaim and then allocating */
|
|
page = __alloc_pages_direct_reclaim(gfp_mask, order,
|
|
zonelist, high_zoneidx,
|
|
@@ -2232,8 +2256,9 @@ rebalance:
|
|
zonelist, high_zoneidx,
|
|
nodemask,
|
|
alloc_flags, preferred_zone,
|
|
- migratetype, &did_some_progress,
|
|
- sync_migration);
|
|
+ migratetype, sync_migration,
|
|
+ &deferred_compaction,
|
|
+ &did_some_progress);
|
|
if (page)
|
|
goto got_pg;
|
|
}
|
|
@@ -2257,8 +2282,9 @@ __alloc_pages_nodemask(gfp_t gfp_mask, unsigned int order,
|
|
{
|
|
enum zone_type high_zoneidx = gfp_zone(gfp_mask);
|
|
struct zone *preferred_zone;
|
|
- struct page *page;
|
|
+ struct page *page = NULL;
|
|
int migratetype = allocflags_to_migratetype(gfp_mask);
|
|
+ unsigned int cpuset_mems_cookie;
|
|
|
|
gfp_mask &= gfp_allowed_mask;
|
|
|
|
@@ -2277,15 +2303,15 @@ __alloc_pages_nodemask(gfp_t gfp_mask, unsigned int order,
|
|
if (unlikely(!zonelist->_zonerefs->zone))
|
|
return NULL;
|
|
|
|
- get_mems_allowed();
|
|
+retry_cpuset:
|
|
+ cpuset_mems_cookie = get_mems_allowed();
|
|
+
|
|
/* The preferred zone is used for statistics later */
|
|
first_zones_zonelist(zonelist, high_zoneidx,
|
|
nodemask ? : &cpuset_current_mems_allowed,
|
|
&preferred_zone);
|
|
- if (!preferred_zone) {
|
|
- put_mems_allowed();
|
|
- return NULL;
|
|
- }
|
|
+ if (!preferred_zone)
|
|
+ goto out;
|
|
|
|
/* First allocation attempt */
|
|
page = get_page_from_freelist(gfp_mask|__GFP_HARDWALL, nodemask, order,
|
|
@@ -2295,9 +2321,19 @@ __alloc_pages_nodemask(gfp_t gfp_mask, unsigned int order,
|
|
page = __alloc_pages_slowpath(gfp_mask, order,
|
|
zonelist, high_zoneidx, nodemask,
|
|
preferred_zone, migratetype);
|
|
- put_mems_allowed();
|
|
|
|
trace_mm_page_alloc(page, order, gfp_mask, migratetype);
|
|
+
|
|
+out:
|
|
+ /*
|
|
+ * When updating a task's mems_allowed, it is possible to race with
|
|
+ * parallel threads in such a way that an allocation can fail while
|
|
+ * the mask is being updated. If a page allocation is about to fail,
|
|
+ * check if the cpuset changed during allocation and if so, retry.
|
|
+ */
|
|
+ if (unlikely(!put_mems_allowed(cpuset_mems_cookie) && !page))
|
|
+ goto retry_cpuset;
|
|
+
|
|
return page;
|
|
}
|
|
EXPORT_SYMBOL(__alloc_pages_nodemask);
|
|
@@ -2521,13 +2557,15 @@ void si_meminfo_node(struct sysinfo *val, int nid)
|
|
bool skip_free_areas_node(unsigned int flags, int nid)
|
|
{
|
|
bool ret = false;
|
|
+ unsigned int cpuset_mems_cookie;
|
|
|
|
if (!(flags & SHOW_MEM_FILTER_NODES))
|
|
goto out;
|
|
|
|
- get_mems_allowed();
|
|
- ret = !node_isset(nid, cpuset_current_mems_allowed);
|
|
- put_mems_allowed();
|
|
+ do {
|
|
+ cpuset_mems_cookie = get_mems_allowed();
|
|
+ ret = !node_isset(nid, cpuset_current_mems_allowed);
|
|
+ } while (!put_mems_allowed(cpuset_mems_cookie));
|
|
out:
|
|
return ret;
|
|
}
|
|
@@ -3407,25 +3445,33 @@ static void setup_zone_migrate_reserve(struct zone *zone)
|
|
if (page_to_nid(page) != zone_to_nid(zone))
|
|
continue;
|
|
|
|
- /* Blocks with reserved pages will never free, skip them. */
|
|
- block_end_pfn = min(pfn + pageblock_nr_pages, end_pfn);
|
|
- if (pageblock_is_reserved(pfn, block_end_pfn))
|
|
- continue;
|
|
-
|
|
block_migratetype = get_pageblock_migratetype(page);
|
|
|
|
- /* If this block is reserved, account for it */
|
|
- if (reserve > 0 && block_migratetype == MIGRATE_RESERVE) {
|
|
- reserve--;
|
|
- continue;
|
|
- }
|
|
+ /* Only test what is necessary when the reserves are not met */
|
|
+ if (reserve > 0) {
|
|
+ /*
|
|
+ * Blocks with reserved pages will never free, skip
|
|
+ * them.
|
|
+ */
|
|
+ block_end_pfn = min(pfn + pageblock_nr_pages, end_pfn);
|
|
+ if (pageblock_is_reserved(pfn, block_end_pfn))
|
|
+ continue;
|
|
|
|
- /* Suitable for reserving if this block is movable */
|
|
- if (reserve > 0 && block_migratetype == MIGRATE_MOVABLE) {
|
|
- set_pageblock_migratetype(page, MIGRATE_RESERVE);
|
|
- move_freepages_block(zone, page, MIGRATE_RESERVE);
|
|
- reserve--;
|
|
- continue;
|
|
+ /* If this block is reserved, account for it */
|
|
+ if (block_migratetype == MIGRATE_RESERVE) {
|
|
+ reserve--;
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ /* Suitable for reserving if this block is movable */
|
|
+ if (block_migratetype == MIGRATE_MOVABLE) {
|
|
+ set_pageblock_migratetype(page,
|
|
+ MIGRATE_RESERVE);
|
|
+ move_freepages_block(zone, page,
|
|
+ MIGRATE_RESERVE);
|
|
+ reserve--;
|
|
+ continue;
|
|
+ }
|
|
}
|
|
|
|
/*
|
|
diff --git a/mm/slab.c b/mm/slab.c
|
|
index 83311c9a..cd3ab93 100644
|
|
--- a/mm/slab.c
|
|
+++ b/mm/slab.c
|
|
@@ -3267,12 +3267,10 @@ static void *alternate_node_alloc(struct kmem_cache *cachep, gfp_t flags)
|
|
if (in_interrupt() || (flags & __GFP_THISNODE))
|
|
return NULL;
|
|
nid_alloc = nid_here = numa_mem_id();
|
|
- get_mems_allowed();
|
|
if (cpuset_do_slab_mem_spread() && (cachep->flags & SLAB_MEM_SPREAD))
|
|
nid_alloc = cpuset_slab_spread_node();
|
|
else if (current->mempolicy)
|
|
nid_alloc = slab_node(current->mempolicy);
|
|
- put_mems_allowed();
|
|
if (nid_alloc != nid_here)
|
|
return ____cache_alloc_node(cachep, flags, nid_alloc);
|
|
return NULL;
|
|
@@ -3295,14 +3293,17 @@ static void *fallback_alloc(struct kmem_cache *cache, gfp_t flags)
|
|
enum zone_type high_zoneidx = gfp_zone(flags);
|
|
void *obj = NULL;
|
|
int nid;
|
|
+ unsigned int cpuset_mems_cookie;
|
|
|
|
if (flags & __GFP_THISNODE)
|
|
return NULL;
|
|
|
|
- get_mems_allowed();
|
|
- zonelist = node_zonelist(slab_node(current->mempolicy), flags);
|
|
local_flags = flags & (GFP_CONSTRAINT_MASK|GFP_RECLAIM_MASK);
|
|
|
|
+retry_cpuset:
|
|
+ cpuset_mems_cookie = get_mems_allowed();
|
|
+ zonelist = node_zonelist(slab_node(current->mempolicy), flags);
|
|
+
|
|
retry:
|
|
/*
|
|
* Look through allowed nodes for objects available
|
|
@@ -3355,7 +3356,9 @@ retry:
|
|
}
|
|
}
|
|
}
|
|
- put_mems_allowed();
|
|
+
|
|
+ if (unlikely(!put_mems_allowed(cpuset_mems_cookie) && !obj))
|
|
+ goto retry_cpuset;
|
|
return obj;
|
|
}
|
|
|
|
diff --git a/mm/slub.c b/mm/slub.c
|
|
index af47188..5710788 100644
|
|
--- a/mm/slub.c
|
|
+++ b/mm/slub.c
|
|
@@ -1582,6 +1582,7 @@ static struct page *get_any_partial(struct kmem_cache *s, gfp_t flags,
|
|
struct zone *zone;
|
|
enum zone_type high_zoneidx = gfp_zone(flags);
|
|
void *object;
|
|
+ unsigned int cpuset_mems_cookie;
|
|
|
|
/*
|
|
* The defrag ratio allows a configuration of the tradeoffs between
|
|
@@ -1605,23 +1606,32 @@ static struct page *get_any_partial(struct kmem_cache *s, gfp_t flags,
|
|
get_cycles() % 1024 > s->remote_node_defrag_ratio)
|
|
return NULL;
|
|
|
|
- get_mems_allowed();
|
|
- zonelist = node_zonelist(slab_node(current->mempolicy), flags);
|
|
- for_each_zone_zonelist(zone, z, zonelist, high_zoneidx) {
|
|
- struct kmem_cache_node *n;
|
|
-
|
|
- n = get_node(s, zone_to_nid(zone));
|
|
-
|
|
- if (n && cpuset_zone_allowed_hardwall(zone, flags) &&
|
|
- n->nr_partial > s->min_partial) {
|
|
- object = get_partial_node(s, n, c);
|
|
- if (object) {
|
|
- put_mems_allowed();
|
|
- return object;
|
|
+ do {
|
|
+ cpuset_mems_cookie = get_mems_allowed();
|
|
+ zonelist = node_zonelist(slab_node(current->mempolicy), flags);
|
|
+ for_each_zone_zonelist(zone, z, zonelist, high_zoneidx) {
|
|
+ struct kmem_cache_node *n;
|
|
+
|
|
+ n = get_node(s, zone_to_nid(zone));
|
|
+
|
|
+ if (n && cpuset_zone_allowed_hardwall(zone, flags) &&
|
|
+ n->nr_partial > s->min_partial) {
|
|
+ object = get_partial_node(s, n, c);
|
|
+ if (object) {
|
|
+ /*
|
|
+ * Return the object even if
|
|
+ * put_mems_allowed indicated that
|
|
+ * the cpuset mems_allowed was
|
|
+ * updated in parallel. It's a
|
|
+ * harmless race between the alloc
|
|
+ * and the cpuset update.
|
|
+ */
|
|
+ put_mems_allowed(cpuset_mems_cookie);
|
|
+ return object;
|
|
+ }
|
|
}
|
|
}
|
|
- }
|
|
- put_mems_allowed();
|
|
+ } while (!put_mems_allowed(cpuset_mems_cookie));
|
|
#endif
|
|
return NULL;
|
|
}
|
|
diff --git a/mm/vmscan.c b/mm/vmscan.c
|
|
index 8342119..48febd7 100644
|
|
--- a/mm/vmscan.c
|
|
+++ b/mm/vmscan.c
|
|
@@ -715,7 +715,13 @@ static enum page_references page_check_references(struct page *page,
|
|
*/
|
|
SetPageReferenced(page);
|
|
|
|
- if (referenced_page)
|
|
+ if (referenced_page || referenced_ptes > 1)
|
|
+ return PAGEREF_ACTIVATE;
|
|
+
|
|
+ /*
|
|
+ * Activate file-backed executable pages after first usage.
|
|
+ */
|
|
+ if (vm_flags & VM_EXEC)
|
|
return PAGEREF_ACTIVATE;
|
|
|
|
return PAGEREF_KEEP;
|
|
@@ -1061,8 +1067,39 @@ int __isolate_lru_page(struct page *page, isolate_mode_t mode, int file)
|
|
|
|
ret = -EBUSY;
|
|
|
|
- if ((mode & ISOLATE_CLEAN) && (PageDirty(page) || PageWriteback(page)))
|
|
- return ret;
|
|
+ /*
|
|
+ * To minimise LRU disruption, the caller can indicate that it only
|
|
+ * wants to isolate pages it will be able to operate on without
|
|
+ * blocking - clean pages for the most part.
|
|
+ *
|
|
+ * ISOLATE_CLEAN means that only clean pages should be isolated. This
|
|
+ * is used by reclaim when it is cannot write to backing storage
|
|
+ *
|
|
+ * ISOLATE_ASYNC_MIGRATE is used to indicate that it only wants to pages
|
|
+ * that it is possible to migrate without blocking
|
|
+ */
|
|
+ if (mode & (ISOLATE_CLEAN|ISOLATE_ASYNC_MIGRATE)) {
|
|
+ /* All the caller can do on PageWriteback is block */
|
|
+ if (PageWriteback(page))
|
|
+ return ret;
|
|
+
|
|
+ if (PageDirty(page)) {
|
|
+ struct address_space *mapping;
|
|
+
|
|
+ /* ISOLATE_CLEAN means only clean pages */
|
|
+ if (mode & ISOLATE_CLEAN)
|
|
+ return ret;
|
|
+
|
|
+ /*
|
|
+ * Only pages without mappings or that have a
|
|
+ * ->migratepage callback are possible to migrate
|
|
+ * without blocking
|
|
+ */
|
|
+ mapping = page_mapping(page);
|
|
+ if (mapping && !mapping->a_ops->migratepage)
|
|
+ return ret;
|
|
+ }
|
|
+ }
|
|
|
|
if ((mode & ISOLATE_UNMAPPED) && page_mapped(page))
|
|
return ret;
|
|
@@ -1178,7 +1215,7 @@ static unsigned long isolate_lru_pages(unsigned long nr_to_scan,
|
|
* anon page which don't already have a swap slot is
|
|
* pointless.
|
|
*/
|
|
- if (nr_swap_pages <= 0 && PageAnon(cursor_page) &&
|
|
+ if (nr_swap_pages <= 0 && PageSwapBacked(cursor_page) &&
|
|
!PageSwapCache(cursor_page))
|
|
break;
|
|
|
|
@@ -1874,7 +1911,8 @@ static void get_scan_count(struct zone *zone, struct scan_control *sc,
|
|
* latencies, so it's better to scan a minimum amount there as
|
|
* well.
|
|
*/
|
|
- if (scanning_global_lru(sc) && current_is_kswapd())
|
|
+ if (scanning_global_lru(sc) && current_is_kswapd() &&
|
|
+ zone->all_unreclaimable)
|
|
force_scan = true;
|
|
if (!scanning_global_lru(sc))
|
|
force_scan = true;
|
|
@@ -2012,8 +2050,9 @@ static inline bool should_continue_reclaim(struct zone *zone,
|
|
* inactive lists are large enough, continue reclaiming
|
|
*/
|
|
pages_for_compaction = (2UL << sc->order);
|
|
- inactive_lru_pages = zone_nr_lru_pages(zone, sc, LRU_INACTIVE_ANON) +
|
|
- zone_nr_lru_pages(zone, sc, LRU_INACTIVE_FILE);
|
|
+ inactive_lru_pages = zone_nr_lru_pages(zone, sc, LRU_INACTIVE_FILE);
|
|
+ if (nr_swap_pages > 0)
|
|
+ inactive_lru_pages += zone_nr_lru_pages(zone, sc, LRU_INACTIVE_ANON);
|
|
if (sc->nr_reclaimed < pages_for_compaction &&
|
|
inactive_lru_pages > pages_for_compaction)
|
|
return true;
|
|
@@ -2088,6 +2127,42 @@ restart:
|
|
throttle_vm_writeout(sc->gfp_mask);
|
|
}
|
|
|
|
+/* Returns true if compaction should go ahead for a high-order request */
|
|
+static inline bool compaction_ready(struct zone *zone, struct scan_control *sc)
|
|
+{
|
|
+ unsigned long balance_gap, watermark;
|
|
+ bool watermark_ok;
|
|
+
|
|
+ /* Do not consider compaction for orders reclaim is meant to satisfy */
|
|
+ if (sc->order <= PAGE_ALLOC_COSTLY_ORDER)
|
|
+ return false;
|
|
+
|
|
+ /*
|
|
+ * Compaction takes time to run and there are potentially other
|
|
+ * callers using the pages just freed. Continue reclaiming until
|
|
+ * there is a buffer of free pages available to give compaction
|
|
+ * a reasonable chance of completing and allocating the page
|
|
+ */
|
|
+ balance_gap = min(low_wmark_pages(zone),
|
|
+ (zone->present_pages + KSWAPD_ZONE_BALANCE_GAP_RATIO-1) /
|
|
+ KSWAPD_ZONE_BALANCE_GAP_RATIO);
|
|
+ watermark = high_wmark_pages(zone) + balance_gap + (2UL << sc->order);
|
|
+ watermark_ok = zone_watermark_ok_safe(zone, 0, watermark, 0, 0);
|
|
+
|
|
+ /*
|
|
+ * If compaction is deferred, reclaim up to a point where
|
|
+ * compaction will have a chance of success when re-enabled
|
|
+ */
|
|
+ if (compaction_deferred(zone))
|
|
+ return watermark_ok;
|
|
+
|
|
+ /* If compaction is not ready to start, keep reclaiming */
|
|
+ if (!compaction_suitable(zone, sc->order))
|
|
+ return false;
|
|
+
|
|
+ return watermark_ok;
|
|
+}
|
|
+
|
|
/*
|
|
* This is the direct reclaim path, for page-allocating processes. We only
|
|
* try to reclaim pages from zones which will satisfy the caller's allocation
|
|
@@ -2105,8 +2180,9 @@ restart:
|
|
* scan then give up on it.
|
|
*
|
|
* This function returns true if a zone is being reclaimed for a costly
|
|
- * high-order allocation and compaction is either ready to begin or deferred.
|
|
- * This indicates to the caller that it should retry the allocation or fail.
|
|
+ * high-order allocation and compaction is ready to begin. This indicates to
|
|
+ * the caller that it should consider retrying the allocation instead of
|
|
+ * further reclaim.
|
|
*/
|
|
static bool shrink_zones(int priority, struct zonelist *zonelist,
|
|
struct scan_control *sc)
|
|
@@ -2115,7 +2191,7 @@ static bool shrink_zones(int priority, struct zonelist *zonelist,
|
|
struct zone *zone;
|
|
unsigned long nr_soft_reclaimed;
|
|
unsigned long nr_soft_scanned;
|
|
- bool should_abort_reclaim = false;
|
|
+ bool aborted_reclaim = false;
|
|
|
|
for_each_zone_zonelist_nodemask(zone, z, zonelist,
|
|
gfp_zone(sc->gfp_mask), sc->nodemask) {
|
|
@@ -2140,10 +2216,8 @@ static bool shrink_zones(int priority, struct zonelist *zonelist,
|
|
* noticable problem, like transparent huge page
|
|
* allocations.
|
|
*/
|
|
- if (sc->order > PAGE_ALLOC_COSTLY_ORDER &&
|
|
- (compaction_suitable(zone, sc->order) ||
|
|
- compaction_deferred(zone))) {
|
|
- should_abort_reclaim = true;
|
|
+ if (compaction_ready(zone, sc)) {
|
|
+ aborted_reclaim = true;
|
|
continue;
|
|
}
|
|
}
|
|
@@ -2165,7 +2239,7 @@ static bool shrink_zones(int priority, struct zonelist *zonelist,
|
|
shrink_zone(priority, zone, sc);
|
|
}
|
|
|
|
- return should_abort_reclaim;
|
|
+ return aborted_reclaim;
|
|
}
|
|
|
|
static bool zone_reclaimable(struct zone *zone)
|
|
@@ -2219,8 +2293,8 @@ static unsigned long do_try_to_free_pages(struct zonelist *zonelist,
|
|
struct zoneref *z;
|
|
struct zone *zone;
|
|
unsigned long writeback_threshold;
|
|
+ bool aborted_reclaim;
|
|
|
|
- get_mems_allowed();
|
|
delayacct_freepages_start();
|
|
|
|
if (scanning_global_lru(sc))
|
|
@@ -2230,8 +2304,7 @@ static unsigned long do_try_to_free_pages(struct zonelist *zonelist,
|
|
sc->nr_scanned = 0;
|
|
if (!priority)
|
|
disable_swap_token(sc->mem_cgroup);
|
|
- if (shrink_zones(priority, zonelist, sc))
|
|
- break;
|
|
+ aborted_reclaim = shrink_zones(priority, zonelist, sc);
|
|
|
|
/*
|
|
* Don't shrink slabs when reclaiming memory from
|
|
@@ -2285,7 +2358,6 @@ static unsigned long do_try_to_free_pages(struct zonelist *zonelist,
|
|
|
|
out:
|
|
delayacct_freepages_end();
|
|
- put_mems_allowed();
|
|
|
|
if (sc->nr_reclaimed)
|
|
return sc->nr_reclaimed;
|
|
@@ -2298,6 +2370,10 @@ out:
|
|
if (oom_killer_disabled)
|
|
return 0;
|
|
|
|
+ /* Aborted reclaim to try compaction? don't OOM, then */
|
|
+ if (aborted_reclaim)
|
|
+ return 1;
|
|
+
|
|
/* top priority shrink_zones still had more to do? don't OOM, then */
|
|
if (scanning_global_lru(sc) && !all_unreclaimable(zonelist, sc))
|
|
return 1;
|
|
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
|
|
index c505fd5..c119f33 100644
|
|
--- a/sound/pci/hda/patch_hdmi.c
|
|
+++ b/sound/pci/hda/patch_hdmi.c
|
|
@@ -868,7 +868,6 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
|
|
struct hdmi_spec_per_pin *per_pin;
|
|
struct hdmi_eld *eld;
|
|
struct hdmi_spec_per_cvt *per_cvt = NULL;
|
|
- int pinctl;
|
|
|
|
/* Validate hinfo */
|
|
pin_idx = hinfo_to_pin_index(spec, hinfo);
|
|
@@ -904,11 +903,6 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
|
|
snd_hda_codec_write(codec, per_pin->pin_nid, 0,
|
|
AC_VERB_SET_CONNECT_SEL,
|
|
mux_idx);
|
|
- pinctl = snd_hda_codec_read(codec, per_pin->pin_nid, 0,
|
|
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
|
|
- snd_hda_codec_write(codec, per_pin->pin_nid, 0,
|
|
- AC_VERB_SET_PIN_WIDGET_CONTROL,
|
|
- pinctl | PIN_OUT);
|
|
snd_hda_spdif_ctls_assign(codec, pin_idx, per_cvt->cvt_nid);
|
|
|
|
/* Initially set the converter's capabilities */
|
|
@@ -1147,11 +1141,17 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
|
|
struct hdmi_spec *spec = codec->spec;
|
|
int pin_idx = hinfo_to_pin_index(spec, hinfo);
|
|
hda_nid_t pin_nid = spec->pins[pin_idx].pin_nid;
|
|
+ int pinctl;
|
|
|
|
hdmi_set_channel_count(codec, cvt_nid, substream->runtime->channels);
|
|
|
|
hdmi_setup_audio_infoframe(codec, pin_idx, substream);
|
|
|
|
+ pinctl = snd_hda_codec_read(codec, pin_nid, 0,
|
|
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
|
|
+ snd_hda_codec_write(codec, pin_nid, 0,
|
|
+ AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl | PIN_OUT);
|
|
+
|
|
return hdmi_setup_stream(codec, cvt_nid, pin_nid, stream_tag, format);
|
|
}
|
|
|
|
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
|
|
index 5f096a5..191fd78 100644
|
|
--- a/sound/pci/hda/patch_realtek.c
|
|
+++ b/sound/pci/hda/patch_realtek.c
|
|
@@ -5989,6 +5989,7 @@ static const struct hda_codec_preset snd_hda_preset_realtek[] = {
|
|
{ .id = 0x10ec0275, .name = "ALC275", .patch = patch_alc269 },
|
|
{ .id = 0x10ec0276, .name = "ALC276", .patch = patch_alc269 },
|
|
{ .id = 0x10ec0280, .name = "ALC280", .patch = patch_alc269 },
|
|
+ { .id = 0x10ec0282, .name = "ALC282", .patch = patch_alc269 },
|
|
{ .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660",
|
|
.patch = patch_alc861 },
|
|
{ .id = 0x10ec0660, .name = "ALC660-VD", .patch = patch_alc861vd },
|
|
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
|
|
index 90e93bf..0dc441c 100644
|
|
--- a/sound/soc/soc-dapm.c
|
|
+++ b/sound/soc/soc-dapm.c
|
|
@@ -1381,7 +1381,15 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
|
|
}
|
|
|
|
list_for_each_entry(w, &card->widgets, list) {
|
|
- list_del_init(&w->dirty);
|
|
+ switch (w->id) {
|
|
+ case snd_soc_dapm_pre:
|
|
+ case snd_soc_dapm_post:
|
|
+ /* These widgets always need to be powered */
|
|
+ break;
|
|
+ default:
|
|
+ list_del_init(&w->dirty);
|
|
+ break;
|
|
+ }
|
|
|
|
if (w->power) {
|
|
d = w->dapm;
|