-
Notifications
You must be signed in to change notification settings - Fork 10
Make a few adjustments #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
include/linux/blkdev.h
Outdated
| (1 << QUEUE_FLAG_STACKABLE) | \ | ||
| (1 << QUEUE_FLAG_SAME_COMP) | \ | ||
| (1 << QUEUE_FLAG_ADD_RANDOM)) | ||
| (0 << QUEUE_FLAG_ADD_RANDOM)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's go for the upstream solution: https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=b277da0a8a594308e17881f4926879bd5fca2a2d
drivers/thermal/Kconfig
Outdated
| absence of a system wide thermal monitoring entity. Monitors temperature | ||
| via a specified ADC channel on the VADC device, and limits the max frequency | ||
| of the CPU cores accordingly. The code is simple, allows for up to 8 throttling | ||
| of the CPU cores accordingly. The code is simple, allows for multiple throttling |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The extra zones aren't really useful on the OPX. My last throttle zone already forces the maxfreq down to 729 MHz, so I don't think we need more throttling steps after that.
drivers/mmc/core/core.c
Outdated
| * So we allow it it to be disabled. | ||
| */ | ||
| bool use_spi_crc = 1; | ||
| bool use_spi_crc = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SPI mode isn't used for both our eMMC and SD card, so this is a no-op.
drivers/cpufreq/cpu_input_boost.c
Outdated
| enum boost_status ib_status; | ||
| bool do_boost; | ||
|
|
||
| /* Anticipate device wake-up and boost early */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Boosting while the screen is off caused panics and cpufreq violations for me with MSM8974 kernels, probably because off-screen boosts can be active when the system suspends. This didn't crash my MSM8996 kernel, but it still resulted in cpufreq violations, which is why I've stopped using it entirely.
drivers/cpufreq/Makefile
Outdated
| obj-$(CONFIG_CPU_FREQ_GOV_ONDEMAND) += cpufreq_ondemand.o | ||
| obj-$(CONFIG_CPU_FREQ_GOV_CONSERVATIVE) += cpufreq_conservative.o | ||
| obj-$(CONFIG_CPU_FREQ_GOV_INTERACTIVE) += cpufreq_interactive.o | ||
| obj-$(CONFIG_CPU_FREQ_GOV_SMARTMAX) += cpufreq_smartmax.o |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Performance and stability with Interactive are solid right now; I don't want to fix something that isn't broken. Tweaking the CPU governor is a nightmare, so I'd rather not touch this again :P
| msm_clock_init(&msm8974_rumi_clock_init_data); | ||
| else | ||
| msm_clock_init(&msm8974_clock_init_data); | ||
| tsens_tm_init_driver(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Although it's true that TSENS' readings are totally ignored by both userspace and the kernel with my setup, it's not doing anything bad. Who knows, some users might actually want to view TSENS readings for fun. I only disabled the TSENS driver in my MSM8996 kernel because GCC's stack protector detected stack corruption from inside one of its IRQ handlers.
| #define QUEUE_FLAG_SANITIZE 19 /* supports SANITIZE */ | ||
|
|
||
| #define QUEUE_FLAG_DEFAULT ((1 << QUEUE_FLAG_IO_STAT) | \ | ||
| #define QUEUE_FLAG_DEFAULT ((0 << QUEUE_FLAG_IO_STAT) | \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I need to investigate this a bit more to find out exactly what it does in order to feel comfortable disabling it.
CPU boosts should come before anything else in the framebuffer notifier chain in order to speed up system resume. Signed-off-by: Sultanxda <sultanxda@gmail.com>
|
Okay, so I removed changes that were questionable. I do believe though that CPU input boost should have boost's with the maximum priority so as to avoid conflict, and while we can leave TSENS enabled, it does not need to be a dependency of your simple thermal driver. I'm keeping the IO stats change as we really only need this for storage benchmarks, otherwise it is incredibly pointless. I replaced the commit where add random was disabled with the upstream implementation as well |
|
Another thing to consider... I have CFQ working perfectly on my implementation of your kernel without the random reboots or any of that, and it has been doing well for the past day or so. The only issue is that my MMC implementation requires backporting changes from 3.10 which I believe is something that you wouldn't want to do on your part. It works fine, but I just wanted to let you know that it is still a possibility if you don't mind the potential offsets ensued by using the MMC implementation from 3.10 |
VADC seems to very buggy on this device and causes a great mess with thermal driver. Switch to TSENS for now and add a future switcher for this. Signed-off-by: Ícaro Hoff <icarohoff@gmail.com>
XO_THERM_PU2 is what thermal-engine wants for MSM8974 devices, although TSENS is a great option, it's way too much erratic and forces the driver to act a lot when it is not even needed. Now, this simple (poor code) switcher can allow boths modes in the same driver at user will to choose one over the other. Signed-off-by: Ícaro Hoff <icarohoff@gmail.com>
…st to verify that it is not needed. Signed-off-by: DerRomtester <dieb.stefan.96@gmail.com>
Clear QUEUE_FLAG_ADD_RANDOM in all block drivers that set QUEUE_FLAG_NONROT. Historically, all block devices have automatically made entropy contributions. But as previously stated in commit e2e1a14 ("block: add sysfs knob for turning off disk entropy contributions"): - On SSD disks, the completion times aren't as random as they are for rotational drives. So it's questionable whether they should contribute to the random pool in the first place. - Calling add_disk_randomness() has a lot of overhead. There are more reliable sources for randomness than non-rotational block devices. From a security perspective it is better to err on the side of caution than to allow entropy contributions from unreliable "random" sources. Signed-off-by: Mike Snitzer <snitzer@redhat.com> Signed-off-by: Jens Axboe <axboe@fb.com> Conflicts: drivers/block/nbd.c drivers/block/null_blk.c drivers/block/nvme-core.c drivers/block/rsxx/dev.c drivers/block/skd_main.c drivers/block/zram/zram_drv.c drivers/md/bcache/super.c drivers/s390/block/scm_blk.c drivers/s390/block/xpram.c Change-Id: I733f051d20a48f2afce68b7113fd9f1261be430b
|
Made a few more changes... Let's keep the TSENS dependency and give the user the ability to decide between VADC or TSENS for the thermal driver :) Changes to IO stats and entropy contributions still persist |
|
Forget what I said about CFQ... I now see what you mean. It took a lot of time, but I see what you mean :). After taking a log, it appears to still be correlated with our MMC device... how unfortunate. Back to deadline or zen it is... |
|
Meh. Two different mindsets with two different visions. Thank you for your time Sultan. I appreciate it |
On a KVM guest, when a CPU is taken offline and brought back online, we hit the following NULL pointer dereference: [ 45.400843] Unregister pv shared memory for cpu 1 [ 45.412331] smpboot: CPU 1 is now offline [ 45.529894] SMP alternatives: lockdep: fixing up alternatives [ 45.533472] smpboot: Booting Node 0 Processor 1 APIC 0x1 [ 45.411526] kvm-clock: cpu 1, msr 0:7d14601, secondary cpu clock [ 45.571370] KVM setup async PF for cpu 1 [ 45.572331] kvm-stealtime: cpu 1, msr 7d0e040 [ 45.575031] BUG: unable to handle kernel NULL pointer dereference at (null) [ 45.576017] IP: [<ffffffff81519f98>] cpuidle_disable_device+0x18/0x80 [ 45.576017] PGD 5dfb067 PUD 5da8067 PMD 0 [ 45.576017] Oops: 0000 [kerneltoast#1] SMP [ 45.576017] Modules linked in: [ 45.576017] CPU 0 [ 45.576017] Pid: 607, comm: stress_cpu_hotp Not tainted 3.6.0-padata-tp-debug #3 Bochs Bochs [ 45.576017] RIP: 0010:[<ffffffff81519f98>] [<ffffffff81519f98>] cpuidle_disable_device+0x18/0x80 [ 45.576017] RSP: 0018:ffff880005d93ce8 EFLAGS: 00010286 [ 45.576017] RAX: ffff880005d93fd8 RBX: 0000000000000000 RCX: 0000000000000006 [ 45.576017] RDX: 0000000000000006 RSI: 2222222222222222 RDI: 0000000000000000 [ 45.576017] RBP: ffff880005d93cf8 R08: 2222222222222222 R09: 2222222222222222 [ 45.576017] R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000000 [ 45.576017] R13: 0000000000000000 R14: ffffffff81c8cca0 R15: 0000000000000001 [ 45.576017] FS: 00007f91936ae700(0000) GS:ffff880007c00000(0000) knlGS:0000000000000000 [ 45.576017] CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b [ 45.576017] CR2: 0000000000000000 CR3: 0000000005db3000 CR4: 00000000000006f0 [ 45.576017] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 45.576017] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 [ 45.576017] Process stress_cpu_hotp (pid: 607, threadinfo ffff880005d92000, task ffff8800066bbf40) [ 45.576017] Stack: [ 45.576017] ffff880007a96400 0000000000000000 ffff880005d93d28 ffffffff813ac689 [ 45.576017] ffff880007a96400 ffff880007a96400 0000000000000002 ffffffff81cd8d01 [ 45.576017] ffff880005d93d58 ffffffff813aa498 0000000000000001 00000000ffffffdd [ 45.576017] Call Trace: [ 45.576017] [<ffffffff813ac689>] acpi_processor_hotplug+0x55/0x97 [ 45.576017] [<ffffffff813aa498>] acpi_cpu_soft_notify+0x93/0xce [ 45.576017] [<ffffffff816ae47d>] notifier_call_chain+0x5d/0x110 [ 45.576017] [<ffffffff8109730e>] __raw_notifier_call_chain+0xe/0x10 [ 45.576017] [<ffffffff81069050>] __cpu_notify+0x20/0x40 [ 45.576017] [<ffffffff81069085>] cpu_notify+0x15/0x20 [ 45.576017] [<ffffffff816978f1>] _cpu_up+0xee/0x137 [ 45.576017] [<ffffffff81697983>] cpu_up+0x49/0x59 [ 45.576017] [<ffffffff8168758d>] store_online+0x9d/0xe0 [ 45.576017] [<ffffffff8140a9f8>] dev_attr_store+0x18/0x30 [ 45.576017] [<ffffffff812322c0>] sysfs_write_file+0xe0/0x150 [ 45.576017] [<ffffffff811b389c>] vfs_write+0xac/0x180 [ 45.576017] [<ffffffff811b3be2>] sys_write+0x52/0xa0 [ 45.576017] [<ffffffff816b31e9>] system_call_fastpath+0x16/0x1b [ 45.576017] Code: 48 c7 c7 40 e5 ca 81 e8 07 d0 18 00 5d c3 0f 1f 44 00 00 0f 1f 44 00 00 55 48 89 e5 48 83 ec 10 48 89 5d f0 4c 89 65 f8 48 89 fb <f6> 07 02 75 13 48 8b 5d f0 4c 8b 65 f8 c9 c3 66 0f 1f 84 00 00 [ 45.576017] RIP [<ffffffff81519f98>] cpuidle_disable_device+0x18/0x80 [ 45.576017] RSP <ffff880005d93ce8> [ 45.576017] CR2: 0000000000000000 [ 45.656079] ---[ end trace 433d6c9ac0b02cef ]--- Analysis: Commit 3d339dc (cpuidle / ACPI : move cpuidle_device field out of the acpi_processor_power structure()) made the allocation of the dev structure (struct cpuidle) of a CPU dynamic, whereas previously it was statically allocated. And this dynamic allocation occurs in acpi_processor_power_init() if pr->flags.power evaluates to non-zero. On KVM guests, pr->flags.power evaluates to zero, hence dev is never allocated. This causes the NULL pointer (dev) dereference in cpuidle_disable_device() during a subsequent CPU online operation. Fix this by ensuring that dev is non-NULL before dereferencing. Signed-off-by: Srivatsa S. Bhat <srivatsa.bhat@linux.vnet.ibm.com> Signed-off-by: Len Brown <len.brown@intel.com> Signed-off-by: Pranav Vashi <neobuddy89@gmail.com> Signed-off-by: Chet Kener <Cl3Kener@gmail.com> Signed-off-by: engstk <eng.stk@sapo.pt>
kswapd_stop() is called to destroy the kswapd work thread when all memory of a NUMA node has been offlined. But kswapd_stop() only terminates the work thread without resetting NODE_DATA(nid)->kswapd to NULL. The stale pointer will prevent kswapd_run() from creating a new work thread when adding memory to the memory-less NUMA node again. Eventually the stale pointer may cause invalid memory access. An example stack dump as below. It's reproduced with 2.6.32, but latest kernel has the same issue. BUG: unable to handle kernel NULL pointer dereference at (null) IP: [<ffffffff81051a94>] exit_creds+0x12/0x78 PGD 0 Oops: 0000 [kerneltoast#1] SMP last sysfs file: /sys/devices/system/memory/memory391/state CPU 11 Modules linked in: cpufreq_conservative cpufreq_userspace cpufreq_powersave acpi_cpufreq microcode fuse loop dm_mod tpm_tis rtc_cmos i2c_i801 rtc_core tpm serio_raw pcspkr sg tpm_bios igb i2c_core iTCO_wdt rtc_lib mptctl iTCO_vendor_support button dca bnx2 usbhid hid uhci_hcd ehci_hcd usbcore sd_mod crc_t10dif edd ext3 mbcache jbd fan ide_pci_generic ide_core ata_generic ata_piix libata thermal processor thermal_sys hwmon mptsas mptscsih mptbase scsi_transport_sas scsi_mod Pid: 7949, comm: sh Not tainted 2.6.32.12-qiuxishi-5-default #92 Tecal RH2285 RIP: 0010:exit_creds+0x12/0x78 RSP: 0018:ffff8806044f1d78 EFLAGS: 00010202 RAX: 0000000000000000 RBX: ffff880604f22140 RCX: 0000000000019502 RDX: 0000000000000000 RSI: 0000000000000202 RDI: 0000000000000000 RBP: ffff880604f22150 R08: 0000000000000000 R09: ffffffff81a4dc10 R10: 00000000000032a0 R11: ffff880006202500 R12: 0000000000000000 R13: 0000000000c40000 R14: 0000000000008000 R15: 0000000000000001 FS: 00007fbc03d066f0(0000) GS:ffff8800282e0000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b CR2: 0000000000000000 CR3: 000000060f029000 CR4: 00000000000006e0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 Process sh (pid: 7949, threadinfo ffff8806044f0000, task ffff880603d7c600) Stack: ffff880604f22140 ffffffff8103aac5 ffff880604f22140 ffffffff8104d21e ffff880006202500 0000000000008000 0000000000c38000 ffffffff810bd5b1 0000000000000000 ffff880603d7c600 00000000ffffdd29 0000000000000003 Call Trace: __put_task_struct+0x5d/0x97 kthread_stop+0x50/0x58 offline_pages+0x324/0x3da memory_block_change_state+0x179/0x1db store_mem_state+0x9e/0xbb sysfs_write_file+0xd0/0x107 vfs_write+0xad/0x169 sys_write+0x45/0x6e system_call_fastpath+0x16/0x1b Code: ff 4d 00 0f 94 c0 84 c0 74 08 48 89 ef e8 1f fd ff ff 5b 5d 31 c0 41 5c c3 53 48 8b 87 20 06 00 00 48 89 fb 48 8b bf 18 06 00 00 <8b> 00 48 c7 83 18 06 00 00 00 00 00 00 f0 ff 0f 0f 94 c0 84 c0 RIP exit_creds+0x12/0x78 RSP <ffff8806044f1d78> CR2: 0000000000000000 [akpm@linux-foundation.org: add pglist_data.kswapd locking comments] Signed-off-by: Xishi Qiu <qiuxishi@huawei.com> Signed-off-by: Jiang Liu <jiang.liu@huawei.com> Acked-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com> Acked-by: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com> Acked-by: Mel Gorman <mgorman@suse.de> Acked-by: David Rientjes <rientjes@google.com> Reviewed-by: Minchan Kim <minchan@kernel.org> Cc: <stable@vger.kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> Signed-off-by: franciscofranco <franciscofranco.1990@gmail.com> Signed-off-by: engstk <eng.stk@sapo.pt>
commit 412d32e6c98527078779e5b515823b2810e40324 upstream. A rescue thread exiting TASK_INTERRUPTIBLE can lead to a task scheduling off, never to be seen again. In the case where this occurred, an exiting thread hit reiserfs homebrew conditional resched while holding a mutex, bringing the box to its knees. PID: 18105 TASK: ffff8807fd412180 CPU: 5 COMMAND: "kdmflush" #0 [ffff8808157e7670] schedule at ffffffff8143f489 kerneltoast#1 [ffff8808157e77b8] reiserfs_get_block at ffffffffa038ab2d [reiserfs] kerneltoast#2 [ffff8808157e79a8] __block_write_begin at ffffffff8117fb14 #3 [ffff8808157e7a98] reiserfs_write_begin at ffffffffa0388695 [reiserfs] #4 [ffff8808157e7ad8] generic_perform_write at ffffffff810ee9e2 #5 [ffff8808157e7b58] generic_file_buffered_write at ffffffff810eeb41 #6 [ffff8808157e7ba8] __generic_file_aio_write at ffffffff810f1a3a #7 [ffff8808157e7c58] generic_file_aio_write at ffffffff810f1c88 #8 [ffff8808157e7cc8] do_sync_write at ffffffff8114f850 #9 [ffff8808157e7dd8] do_acct_process at ffffffff810a268f [exception RIP: kernel_thread_helper] RIP: ffffffff8144a5c0 RSP: ffff8808157e7f58 RFLAGS: 00000202 RAX: 0000000000000000 RBX: 0000000000000000 RCX: 0000000000000000 RDX: 0000000000000000 RSI: ffffffff8107af60 RDI: ffff8803ee491d18 RBP: 0000000000000000 R8: 0000000000000000 R9: 0000000000000000 R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000000 R13: 0000000000000000 R14: 0000000000000000 R15: 0000000000000000 ORIG_RAX: ffffffffffffffff CS: 0010 SS: 0018 Signed-off-by: Mike Galbraith <mgalbraith@suse.de> Signed-off-by: Tejun Heo <tj@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: franciscofranco <franciscofranco.1990@gmail.com> Signed-off-by: engstk <eng.stk@sapo.pt>
If a user key gets negatively instantiated, an error code is cached in the
payload area. A negatively instantiated key may be then be positively
instantiated by updating it with valid data. However, the ->update key
type method must be aware that the error code may be there.
The following may be used to trigger the bug in the user key type:
keyctl request2 user user "" @U
keyctl add user user "a" @U
which manifests itself as:
BUG: unable to handle kernel paging request at 00000000ffffff8a
IP: [<ffffffff810a376f>] __call_rcu.constprop.76+0x1f/0x280 kernel/rcu/tree.c:3046
PGD 7cc30067 PUD 0
Oops: 0002 [kerneltoast#1] SMP
Modules linked in:
CPU: 3 PID: 2644 Comm: a.out Not tainted 4.3.0+ #49
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011
task: ffff88003ddea700 ti: ffff88003dd88000 task.ti: ffff88003dd88000
RIP: 0010:[<ffffffff810a376f>] [<ffffffff810a376f>] __call_rcu.constprop.76+0x1f/0x280
[<ffffffff810a376f>] __call_rcu.constprop.76+0x1f/0x280 kernel/rcu/tree.c:3046
RSP: 0018:ffff88003dd8bdb0 EFLAGS: 00010246
RAX: 00000000ffffff82 RBX: 0000000000000000 RCX: 0000000000000001
RDX: ffffffff81e3fe40 RSI: 0000000000000000 RDI: 00000000ffffff82
RBP: ffff88003dd8bde0 R08: ffff88007d2d2da0 R09: 0000000000000000
R10: 0000000000000000 R11: ffff88003e8073c0 R12: 00000000ffffff82
R13: ffff88003dd8be68 R14: ffff88007d027600 R15: ffff88003ddea700
FS: 0000000000b92880(0063) GS:ffff88007fd00000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b
CR2: 00000000ffffff8a CR3: 000000007cc5f000 CR4: 00000000000006e0
Stack:
ffff88003dd8bdf0 ffffffff81160a8a 0000000000000000 00000000ffffff82
ffff88003dd8be68 ffff88007d027600 ffff88003dd8bdf0 ffffffff810a39e5
ffff88003dd8be20 ffffffff812a31ab ffff88007d027600 ffff88007d027620
Call Trace:
[<ffffffff810a39e5>] kfree_call_rcu+0x15/0x20 kernel/rcu/tree.c:3136
[<ffffffff812a31ab>] user_update+0x8b/0xb0 security/keys/user_defined.c:129
[< inline >] __key_update security/keys/key.c:730
[<ffffffff8129e5c1>] key_create_or_update+0x291/0x440 security/keys/key.c:908
[< inline >] SYSC_add_key security/keys/keyctl.c:125
[<ffffffff8129fc21>] SyS_add_key+0x101/0x1e0 security/keys/keyctl.c:60
[<ffffffff8185f617>] entry_SYSCALL_64_fastpath+0x12/0x6a arch/x86/entry/entry_64.S:185
Note the error code (-ENOKEY) in EDX.
A similar bug can be tripped by:
keyctl request2 trusted user "" @U
keyctl add trusted user "a" @U
This should also affect encrypted keys - but that has to be correctly
parameterised or it will fail with EINVAL before getting to the bit that
will crashes.
Change-Id: I171d566f431c56208e1fe279f466d2d399a9ac7c
Reported-by: Dmitry Vyukov <dvyukov@google.com>
Signed-off-by: David Howells <dhowells@redhat.com>
Acked-by: Mimi Zohar <zohar@linux.vnet.ibm.com>
Signed-off-by: James Morris <james.l.morris@oracle.com>
Signed-off-by: engstk <eng.stk@sapo.pt>
The CPU input boost and simple thermal commits were picked from your kernel for the OnePlus 3 as they appear to work and to be applicable here. Since TSENS is not used by your thermal driver, there is no reason to call it anymore so I removed it's initialization. Furthermore, I disabled IO stats and add random as they are not really beneficial on MMC devices, and IO stats only serves the purpose of monitoring transfer speeds which is just using up resources that we do not need in a kernel intended for daily use. Disabling the CRC check also lead to higher speeds when testing with my modified version of your kernel as well. Finally, I added back Smartmax. It appeared as though you had tried to get it working right for Bacon at an earlier stage, but never could. DerRomtester seems to have successfully fixed it for use with your kernel and it has worked flawlessly for me over the months
Merry Christmas ;)