Merge "msm: mdss: hdmi: add hdmi_ahb clock to HPD"
diff --git a/arch/arm/boot/dts/msm8974-v1.dtsi b/arch/arm/boot/dts/msm8974-v1.dtsi
index 6146454..6b3e945 100644
--- a/arch/arm/boot/dts/msm8974-v1.dtsi
+++ b/arch/arm/boot/dts/msm8974-v1.dtsi
@@ -41,6 +41,7 @@
compatible = "qcom,android-usb";
reg = <0xfc42b0c8 0xc8>;
qcom,android-usb-swfi-latency = <1>;
+ qcom,android-usb-uicc-nluns = /bits/ 8 <1>;
};
qcom,msm-imem@fc42b000 {
diff --git a/arch/arm/boot/dts/msm8974-v2.dtsi b/arch/arm/boot/dts/msm8974-v2.dtsi
index fd4221f..91844c3 100644
--- a/arch/arm/boot/dts/msm8974-v2.dtsi
+++ b/arch/arm/boot/dts/msm8974-v2.dtsi
@@ -41,6 +41,7 @@
compatible = "qcom,android-usb";
reg = <0xfe8050c8 0xc8>;
qcom,android-usb-swfi-latency = <1>;
+ qcom,android-usb-uicc-nluns = /bits/ 8 <1>;
};
qcom,msm-imem@fe805000 {
diff --git a/arch/arm/boot/dts/msm8974pro.dtsi b/arch/arm/boot/dts/msm8974pro.dtsi
index 3e00a46..7a08ca6 100644
--- a/arch/arm/boot/dts/msm8974pro.dtsi
+++ b/arch/arm/boot/dts/msm8974pro.dtsi
@@ -27,6 +27,7 @@
compatible = "qcom,android-usb";
reg = <0xfe8050c8 0xc8>;
qcom,android-usb-swfi-latency = <1>;
+ qcom,android-usb-uicc-nluns = /bits/ 8 <1>;
};
qcom,msm-imem@fe805000 {
diff --git a/arch/arm/configs/msm8974-perf_defconfig b/arch/arm/configs/msm8974-perf_defconfig
index 414a4df..913d798 100755
--- a/arch/arm/configs/msm8974-perf_defconfig
+++ b/arch/arm/configs/msm8974-perf_defconfig
@@ -396,6 +396,7 @@
CONFIG_USB_EHCI_MSM=y
CONFIG_USB_EHCI_MSM_HSIC=y
CONFIG_USB_ACM=y
+CONFIG_USB_MON=y
CONFIG_USB_CCID_BRIDGE=y
CONFIG_USB_STORAGE=y
CONFIG_USB_STORAGE_DATAFAB=y
diff --git a/arch/arm/configs/msm8974_defconfig b/arch/arm/configs/msm8974_defconfig
index 3b2f2b5..d588fc6 100755
--- a/arch/arm/configs/msm8974_defconfig
+++ b/arch/arm/configs/msm8974_defconfig
@@ -420,6 +420,7 @@
CONFIG_USB_EHCI_MSM=y
CONFIG_USB_EHCI_MSM_HSIC=y
CONFIG_USB_ACM=y
+CONFIG_USB_MON=y
CONFIG_USB_CCID_BRIDGE=y
CONFIG_USB_STORAGE=y
CONFIG_USB_STORAGE_DATAFAB=y
diff --git a/arch/arm/mach-msm/qdsp6v2/voice_svc.c b/arch/arm/mach-msm/qdsp6v2/voice_svc.c
old mode 100644
new mode 100755
index 92b3003..5bf86dc
--- a/arch/arm/mach-msm/qdsp6v2/voice_svc.c
+++ b/arch/arm/mach-msm/qdsp6v2/voice_svc.c
@@ -56,8 +56,15 @@
static struct voice_svc_device *voice_svc_dev;
static struct class *voice_svc_class;
+static bool reg_dummy_sess;
+static void *dummy_q6_mvm;
+static void *dummy_q6_cvs;
dev_t device_num;
+static int voice_svc_dummy_reg(void);
+static int32_t qdsp_dummy_apr_callback(struct apr_client_data *data,
+ void *priv);
+
static int32_t qdsp_apr_callback(struct apr_client_data *data, void *priv)
{
struct voice_svc_prvt *prtd;
@@ -127,6 +134,12 @@
return 0;
}
+static int32_t qdsp_dummy_apr_callback(struct apr_client_data *data, void *priv)
+{
+ /* Do Nothing */
+ return 0;
+}
+
static void voice_svc_update_hdr(struct voice_svc_cmd_request* apr_req_data,
struct apr_data *aprdata,
struct voice_svc_prvt *prtd)
@@ -223,6 +236,13 @@
goto done;
}
+ if (src_port == (APR_MAX_PORTS - 1)) {
+ pr_err("%s: SRC port reserved for dummy session\n", __func__);
+ pr_err("%s: Unable to register %s\n", __func__, svc);
+ ret = -EINVAL;
+ goto done;
+ }
+
*handle = apr_register("ADSP",
svc, qdsp_apr_callback,
((src_port) << 8 | 0x0001),
@@ -449,6 +469,37 @@
return ret;
}
+static int voice_svc_dummy_reg()
+{
+ uint32_t src_port = APR_MAX_PORTS - 1;
+
+ pr_debug("%s\n", __func__);
+ dummy_q6_mvm = apr_register("ADSP", "MVM",
+ qdsp_dummy_apr_callback,
+ src_port,
+ NULL);
+ if (dummy_q6_mvm == NULL) {
+ pr_err("%s: Unable to register dummy MVM\n", __func__);
+ goto err;
+ }
+
+ dummy_q6_cvs = apr_register("ADSP", "CVS",
+ qdsp_dummy_apr_callback,
+ src_port,
+ NULL);
+ if (dummy_q6_cvs == NULL) {
+ pr_err("%s: Unable to register dummy CVS\n", __func__);
+ goto err;
+ }
+ return 0;
+err:
+ if (dummy_q6_mvm != NULL) {
+ apr_deregister(dummy_q6_mvm);
+ dummy_q6_mvm = NULL;
+ }
+ return -EINVAL;
+}
+
static int voice_svc_open(struct inode *inode, struct file *file)
{
struct voice_svc_prvt *prtd = NULL;
@@ -472,6 +523,16 @@
file->private_data = (void*)prtd;
+ /* Current APR implementation doesn't support session based
+ * multiple service registrations. The apr_deregister()
+ * function sets the destination and client IDs to zero, if
+ * deregister is called for a single service instance.
+ * To avoid this, register for additional services.
+ */
+ if (!reg_dummy_sess) {
+ voice_svc_dummy_reg();
+ reg_dummy_sess = 1;
+ }
return 0;
}
diff --git a/drivers/crypto/msm/qcedev.c b/drivers/crypto/msm/qcedev.c
index 4845f11..d1e35ec 100644
--- a/drivers/crypto/msm/qcedev.c
+++ b/drivers/crypto/msm/qcedev.c
@@ -2111,6 +2111,75 @@
return 0;
};
+static int qcedev_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct qcedev_control *podev;
+ int ret;
+ podev = platform_get_drvdata(pdev);
+
+ if (!podev || !podev->platform_support.bus_scale_table)
+ return 0;
+
+ mutex_lock(&qcedev_sent_bw_req);
+ if (podev->high_bw_req_count) {
+ ret = msm_bus_scale_client_update_request(
+ podev->bus_scale_handle, 0);
+ if (ret) {
+ pr_err("%s Unable to set to low bandwidth\n",
+ __func__);
+ goto suspend_exit;
+ }
+ ret = qce_disable_clk(podev->qce);
+ if (ret) {
+ pr_err("%s Unable disable clk\n", __func__);
+ ret = msm_bus_scale_client_update_request(
+ podev->bus_scale_handle, 1);
+ if (ret)
+ pr_err("%s Unable to set to high bandwidth\n",
+ __func__);
+ goto suspend_exit;
+ }
+ }
+
+suspend_exit:
+ mutex_unlock(&qcedev_sent_bw_req);
+ return 0;
+}
+
+static int qcedev_resume(struct platform_device *pdev)
+{
+ struct qcedev_control *podev;
+ int ret;
+ podev = platform_get_drvdata(pdev);
+
+ if (!podev || !podev->platform_support.bus_scale_table)
+ return 0;
+
+ mutex_lock(&qcedev_sent_bw_req);
+ if (podev->high_bw_req_count) {
+ ret = qce_enable_clk(podev->qce);
+ if (ret) {
+ pr_err("%s Unable enable clk\n", __func__);
+ goto resume_exit;
+ }
+ ret = msm_bus_scale_client_update_request(
+ podev->bus_scale_handle, 1);
+ if (ret) {
+ pr_err("%s Unable to set to high bandwidth\n",
+ __func__);
+ ret = qce_disable_clk(podev->qce);
+ if (ret)
+ pr_err("%s Unable enable clk\n",
+ __func__);
+ goto resume_exit;
+ }
+ }
+
+resume_exit:
+ mutex_unlock(&qcedev_sent_bw_req);
+ return 0;
+}
+
static struct of_device_id qcedev_match[] = {
{ .compatible = "qcom,qcedev",
},
@@ -2120,6 +2189,8 @@
static struct platform_driver qcedev_plat_driver = {
.probe = qcedev_probe,
.remove = qcedev_remove,
+ .suspend = qcedev_suspend,
+ .resume = qcedev_resume,
.driver = {
.name = "qce",
.owner = THIS_MODULE,
diff --git a/drivers/gpu/ion/ion_heap.c b/drivers/gpu/ion/ion_heap.c
index aa2551a..0b193a0 100644
--- a/drivers/gpu/ion/ion_heap.c
+++ b/drivers/gpu/ion/ion_heap.c
@@ -2,7 +2,7 @@
* drivers/gpu/ion/ion_heap.c
*
* Copyright (C) 2011 Google, Inc.
- * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2014, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -26,6 +26,7 @@
#include <linux/vmalloc.h>
#include <linux/slab.h>
#include <linux/highmem.h>
+#include <linux/dma-mapping.h>
#include "ion_priv.h"
void *ion_heap_map_kernel(struct ion_heap *heap,
@@ -107,16 +108,15 @@
* chunks to minimize the number of memsets and vmaps/vunmaps.
*
* Note that the `pages' array should be composed of all 4K pages.
+ *
+ * NOTE: This function does not guarantee synchronization of the caches
+ * and thus caller is responsible for handling any cache maintenance
+ * operations needed.
*/
int ion_heap_pages_zero(struct page **pages, int num_pages)
{
- int i, j, k, npages_to_vmap;
+ int i, j, npages_to_vmap;
void *ptr = NULL;
- /*
- * It's cheaper just to use writecombine memory and skip the
- * cache vs. using a cache memory and trying to flush it afterwards
- */
- pgprot_t pgprot = pgprot_writecombine(pgprot_kernel);
/*
* As an optimization, we manually zero out all of the pages
@@ -132,7 +132,7 @@
for (j = 0; j < MAX_VMAP_RETRIES && npages_to_vmap;
++j) {
ptr = vmap(&pages[i], npages_to_vmap,
- VM_IOREMAP, pgprot);
+ VM_IOREMAP, PAGE_KERNEL);
if (ptr)
break;
else
@@ -141,21 +141,6 @@
if (!ptr)
return -ENOMEM;
- /*
- * We have to invalidate the cache here because there
- * might be dirty lines to these physical pages (which
- * we don't care about) that could get written out at
- * any moment.
- */
- for (k = 0; k < npages_to_vmap; k++) {
- void *p = kmap_atomic(pages[i + k]);
- phys_addr_t phys = page_to_phys(
- pages[i + k]);
-
- dmac_inv_range(p, p + PAGE_SIZE);
- outer_inv_range(phys, phys + PAGE_SIZE);
- kunmap_atomic(p);
- }
memset(ptr, 0, npages_to_vmap * PAGE_SIZE);
vunmap(ptr);
}
@@ -163,11 +148,12 @@
return 0;
}
-static int ion_heap_alloc_pages_mem(int page_tbl_size,
- struct pages_mem *pages_mem)
+int ion_heap_alloc_pages_mem(struct pages_mem *pages_mem)
{
struct page **pages;
+ unsigned int page_tbl_size;
pages_mem->free_fn = kfree;
+ page_tbl_size = sizeof(struct page *) * (pages_mem->size >> PAGE_SHIFT);
if (page_tbl_size > SZ_8K) {
/*
* Do fallback to ensure we have a balance between
@@ -191,7 +177,7 @@
return 0;
}
-static void ion_heap_free_pages_mem(struct pages_mem *pages_mem)
+void ion_heap_free_pages_mem(struct pages_mem *pages_mem)
{
pages_mem->free_fn(pages_mem->pages);
}
@@ -201,15 +187,17 @@
int i, ret;
struct pages_mem pages_mem;
int npages = 1 << order;
- int page_tbl_size = sizeof(struct page *) * npages;
+ pages_mem.size = npages * PAGE_SIZE;
- if (ion_heap_alloc_pages_mem(page_tbl_size, &pages_mem))
+ if (ion_heap_alloc_pages_mem(&pages_mem))
return -ENOMEM;
for (i = 0; i < (1 << order); ++i)
pages_mem.pages[i] = page + i;
ret = ion_heap_pages_zero(pages_mem.pages, npages);
+ dma_sync_single_for_device(NULL, page_to_phys(page), pages_mem.size,
+ DMA_BIDIRECTIONAL);
ion_heap_free_pages_mem(&pages_mem);
return ret;
}
@@ -218,16 +206,12 @@
{
struct sg_table *table = buffer->sg_table;
struct scatterlist *sg;
- int i, j, ret = 0, npages = 0, page_tbl_size = 0;
+ int i, j, ret = 0, npages = 0;
struct pages_mem pages_mem;
- for_each_sg(table->sgl, sg, table->nents, i) {
- unsigned long len = sg_dma_len(sg);
- int nrpages = len >> PAGE_SHIFT;
- page_tbl_size += sizeof(struct page *) * nrpages;
- }
+ pages_mem.size = PAGE_ALIGN(buffer->size);
- if (ion_heap_alloc_pages_mem(page_tbl_size, &pages_mem))
+ if (ion_heap_alloc_pages_mem(&pages_mem))
return -ENOMEM;
for_each_sg(table->sgl, sg, table->nents, i) {
@@ -239,6 +223,8 @@
}
ret = ion_heap_pages_zero(pages_mem.pages, npages);
+ dma_sync_sg_for_device(NULL, table->sgl, table->nents,
+ DMA_BIDIRECTIONAL);
ion_heap_free_pages_mem(&pages_mem);
return ret;
}
diff --git a/drivers/gpu/ion/ion_page_pool.c b/drivers/gpu/ion/ion_page_pool.c
index cc2a36d..a1845de 100644
--- a/drivers/gpu/ion/ion_page_pool.c
+++ b/drivers/gpu/ion/ion_page_pool.c
@@ -32,7 +32,6 @@
static void *ion_page_pool_alloc_pages(struct ion_page_pool *pool)
{
struct page *page;
- struct scatterlist sg;
page = alloc_pages(pool->gfp_mask & ~__GFP_ZERO, pool->order);
@@ -43,11 +42,6 @@
if (ion_heap_high_order_page_zero(page, pool->order))
goto error_free_pages;
- sg_init_table(&sg, 1);
- sg_set_page(&sg, page, PAGE_SIZE << pool->order, 0);
- sg_dma_address(&sg) = sg_phys(&sg);
- dma_sync_sg_for_device(NULL, &sg, 1, DMA_BIDIRECTIONAL);
-
return page;
error_free_pages:
__free_pages(page, pool->order);
@@ -104,22 +98,25 @@
return page;
}
-void *ion_page_pool_alloc(struct ion_page_pool *pool)
+void *ion_page_pool_alloc(struct ion_page_pool *pool, bool *from_pool)
{
struct page *page = NULL;
BUG_ON(!pool);
- mutex_lock(&pool->mutex);
- if (pool->high_count)
- page = ion_page_pool_remove(pool, true);
- else if (pool->low_count)
- page = ion_page_pool_remove(pool, false);
- mutex_unlock(&pool->mutex);
+ *from_pool = true;
- if (!page)
+ if (mutex_trylock(&pool->mutex)) {
+ if (pool->high_count)
+ page = ion_page_pool_remove(pool, true);
+ else if (pool->low_count)
+ page = ion_page_pool_remove(pool, false);
+ mutex_unlock(&pool->mutex);
+ }
+ if (!page) {
page = ion_page_pool_alloc_pages(pool);
-
+ *from_pool = false;
+ }
return page;
}
diff --git a/drivers/gpu/ion/ion_priv.h b/drivers/gpu/ion/ion_priv.h
index c57efc1..1f78cb1 100644
--- a/drivers/gpu/ion/ion_priv.h
+++ b/drivers/gpu/ion/ion_priv.h
@@ -2,7 +2,7 @@
* drivers/gpu/ion/ion_priv.h
*
* Copyright (C) 2011 Google, Inc.
- * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2014, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -223,6 +223,7 @@
struct pages_mem {
struct page **pages;
+ u32 size;
void (*free_fn) (const void *);
};
@@ -237,6 +238,8 @@
int ion_heap_pages_zero(struct page **pages, int num_pages);
int ion_heap_buffer_zero(struct ion_buffer *buffer);
int ion_heap_high_order_page_zero(struct page *page, int order);
+int ion_heap_alloc_pages_mem(struct pages_mem *pages_mem);
+void ion_heap_free_pages_mem(struct pages_mem *pages_mem);
/**
* ion_heap_init_deferred_free -- initialize deferred free functionality
@@ -374,7 +377,7 @@
struct ion_page_pool *ion_page_pool_create(gfp_t gfp_mask, unsigned int order);
void ion_page_pool_destroy(struct ion_page_pool *);
-void *ion_page_pool_alloc(struct ion_page_pool *);
+void *ion_page_pool_alloc(struct ion_page_pool *, bool *from_pool);
void ion_page_pool_free(struct ion_page_pool *, struct page *);
/** ion_page_pool_shrink - shrinks the size of the memory cached in the pool
diff --git a/drivers/gpu/ion/ion_system_heap.c b/drivers/gpu/ion/ion_system_heap.c
index cfdd5f4..b7ad01f 100644
--- a/drivers/gpu/ion/ion_system_heap.c
+++ b/drivers/gpu/ion/ion_system_heap.c
@@ -29,10 +29,10 @@
#include <linux/dma-mapping.h>
#include <trace/events/kmem.h>
-static unsigned int high_order_gfp_flags = (GFP_HIGHUSER | __GFP_ZERO |
+static unsigned int high_order_gfp_flags = (GFP_HIGHUSER |
__GFP_NOWARN | __GFP_NORETRY |
__GFP_NO_KSWAPD) & ~__GFP_WAIT;
-static unsigned int low_order_gfp_flags = (GFP_HIGHUSER | __GFP_ZERO |
+static unsigned int low_order_gfp_flags = (GFP_HIGHUSER |
__GFP_NOWARN);
static const unsigned int orders[] = {9, 8, 4, 0};
static const int num_orders = ARRAY_SIZE(orders);
@@ -59,13 +59,15 @@
struct page_info {
struct page *page;
+ bool from_pool;
unsigned int order;
struct list_head list;
};
static struct page *alloc_buffer_page(struct ion_system_heap *heap,
struct ion_buffer *buffer,
- unsigned long order)
+ unsigned long order,
+ bool *from_pool)
{
bool cached = ion_buffer_cached(buffer);
bool split_pages = ion_buffer_fault_user_mappings(buffer);
@@ -76,7 +78,7 @@
pool = heap->uncached_pools[order_to_index(order)];
else
pool = heap->cached_pools[order_to_index(order)];
- page = ion_page_pool_alloc(pool);
+ page = ion_page_pool_alloc(pool, from_pool);
if (!page)
return 0;
@@ -119,14 +121,14 @@
struct page *page;
struct page_info *info;
int i;
-
+ bool from_pool;
for (i = 0; i < num_orders; i++) {
if (size < order_to_size(orders[i]))
continue;
if (max_order < orders[i])
continue;
- page = alloc_buffer_page(heap, buffer, orders[i]);
+ page = alloc_buffer_page(heap, buffer, orders[i], &from_pool);
if (!page)
continue;
@@ -134,11 +136,39 @@
if (info) {
info->page = page;
info->order = orders[i];
+ info->from_pool = from_pool;
}
return info;
}
return NULL;
}
+static unsigned int process_info(struct page_info *info,
+ struct scatterlist *sg,
+ struct scatterlist *sg_sync,
+ struct pages_mem *data, unsigned int i)
+{
+ struct page *page = info->page;
+ unsigned int j;
+
+ if (sg_sync) {
+ sg_set_page(sg_sync, page, (1 << info->order) * PAGE_SIZE, 0);
+ sg_dma_address(sg_sync) = page_to_phys(page);
+ }
+ sg_set_page(sg, page, (1 << info->order) * PAGE_SIZE, 0);
+ /*
+ * This is not correct - sg_dma_address needs a dma_addr_t
+ * that is valid for the the targeted device, but this works
+ * on the currently targeted hardware.
+ */
+ sg_dma_address(sg) = page_to_phys(page);
+ if (data) {
+ for (j = 0; j < (1 << info->order); ++j)
+ data->pages[i++] = nth_page(page, j);
+ }
+ list_del(&info->list);
+ kfree(info);
+ return i;
+}
static int ion_system_heap_allocate(struct ion_heap *heap,
struct ion_buffer *buffer,
@@ -149,29 +179,51 @@
struct ion_system_heap,
heap);
struct sg_table *table;
+ struct sg_table table_sync;
struct scatterlist *sg;
+ struct scatterlist *sg_sync;
int ret;
struct list_head pages;
+ struct list_head pages_from_pool;
struct page_info *info, *tmp_info;
int i = 0;
+ unsigned int nents_sync = 0;
unsigned long size_remaining = PAGE_ALIGN(size);
unsigned int max_order = orders[0];
+ struct pages_mem data;
+ unsigned int sz;
bool split_pages = ion_buffer_fault_user_mappings(buffer);
+ data.size = 0;
INIT_LIST_HEAD(&pages);
+ INIT_LIST_HEAD(&pages_from_pool);
while (size_remaining > 0) {
info = alloc_largest_available(sys_heap, buffer, size_remaining, max_order);
if (!info)
goto err;
- list_add_tail(&info->list, &pages);
- size_remaining -= (1 << info->order) * PAGE_SIZE;
+
+ sz = (1 << info->order) * PAGE_SIZE;
+
+ if (info->from_pool) {
+ list_add_tail(&info->list, &pages_from_pool);
+ } else {
+ list_add_tail(&info->list, &pages);
+ data.size += sz;
+ ++nents_sync;
+ }
+ size_remaining -= sz;
max_order = info->order;
i++;
}
+ ret = ion_heap_alloc_pages_mem(&data);
+
+ if (ret)
+ goto err;
+
table = kmalloc(sizeof(struct sg_table), GFP_KERNEL);
if (!table)
- goto err;
+ goto err_free_data_pages;
if (split_pages)
ret = sg_alloc_table(table, PAGE_ALIGN(size) / PAGE_SIZE,
@@ -182,32 +234,91 @@
if (ret)
goto err1;
- sg = table->sgl;
- list_for_each_entry_safe(info, tmp_info, &pages, list) {
- struct page *page = info->page;
- if (split_pages) {
- for (i = 0; i < (1 << info->order); i++) {
- sg_set_page(sg, page + i, PAGE_SIZE, 0);
- sg = sg_next(sg);
- }
- } else {
- sg_set_page(sg, page, (1 << info->order) * PAGE_SIZE,
- 0);
- sg = sg_next(sg);
- }
- list_del(&info->list);
- kfree(info);
+ if (nents_sync) {
+ ret = sg_alloc_table(&table_sync, nents_sync, GFP_KERNEL);
+ if (ret)
+ goto err_free_sg;
}
+ i = 0;
+ sg = table->sgl;
+ sg_sync = table_sync.sgl;
+
+ /*
+ * We now have two separate lists. One list contains pages from the
+ * pool and the other pages from buddy. We want to merge these
+ * together while preserving the ordering of the pages (higher order
+ * first).
+ */
+ do {
+ if (!list_empty(&pages))
+ info = list_first_entry(&pages, struct page_info, list);
+ else
+ info = NULL;
+ if (!list_empty(&pages_from_pool))
+ tmp_info = list_first_entry(&pages_from_pool,
+ struct page_info, list);
+ else
+ tmp_info = NULL;
+
+ if (info && tmp_info) {
+ if (info->order >= tmp_info->order) {
+ i = process_info(info, sg, sg_sync, &data, i);
+ sg_sync = sg_next(sg_sync);
+ } else {
+ i = process_info(tmp_info, sg, 0, 0, i);
+ }
+ } else if (info) {
+ i = process_info(info, sg, sg_sync, &data, i);
+ sg_sync = sg_next(sg_sync);
+ } else if (tmp_info) {
+ i = process_info(tmp_info, sg, 0, 0, i);
+ } else {
+ BUG();
+ }
+ sg = sg_next(sg);
+
+ } while (sg);
+
+ ret = ion_heap_pages_zero(data.pages, data.size >> PAGE_SHIFT);
+ if (ret) {
+ pr_err("Unable to zero pages\n");
+ goto err_free_sg2;
+ }
+
+ if (nents_sync)
+ dma_sync_sg_for_device(NULL, table_sync.sgl, table_sync.nents,
+ DMA_BIDIRECTIONAL);
+
buffer->priv_virt = table;
+ if (nents_sync)
+ sg_free_table(&table_sync);
+ ion_heap_free_pages_mem(&data);
return 0;
+err_free_sg2:
+ /* We failed to zero buffers. Bypass pool */
+ buffer->flags |= ION_FLAG_FREED_FROM_SHRINKER;
+
+ for_each_sg(table->sgl, sg, table->nents, i)
+ free_buffer_page(sys_heap, buffer, sg_page(sg),
+ get_order(sg->length));
+ if (nents_sync)
+ sg_free_table(&table_sync);
+err_free_sg:
+ sg_free_table(table);
err1:
kfree(table);
+err_free_data_pages:
+ ion_heap_free_pages_mem(&data);
err:
list_for_each_entry_safe(info, tmp_info, &pages, list) {
free_buffer_page(sys_heap, buffer, info->page, info->order);
kfree(info);
}
+ list_for_each_entry_safe(info, tmp_info, &pages_from_pool, list) {
+ free_buffer_page(sys_heap, buffer, info->page, info->order);
+ kfree(info);
+ }
return -ENOMEM;
}
diff --git a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c
index d3a848a..b4196e9 100644
--- a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c
+++ b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c
@@ -56,6 +56,16 @@
#define MSM_CPP_NOMINAL_CLOCK 266670000
#define MSM_CPP_TURBO_CLOCK 320000000
+#define CPP_FW_VERSION_1_2_0 0x10020000
+#define CPP_FW_VERSION_1_4_0 0x10040000
+#define CPP_FW_VERSION_1_6_0 0x10060000
+#define CPP_FW_VERSION_1_8_0 0x10080000
+
+/* stripe information offsets in frame command */
+#define STRIPE_BASE_FW_1_2_0 130
+#define STRIPE_BASE_FW_1_4_0 140
+#define STRIPE_BASE_FW_1_6_0 464
+
struct msm_cpp_timer_data_t {
struct cpp_device *cpp_dev;
struct msm_cpp_frame_info_t *processed_frame;
@@ -918,7 +928,8 @@
msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_CMD);
msm_cpp_poll(cpp_dev->base, 0x2);
msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_FW_VER);
- pr_info("CPP FW Version: 0x%x\n", msm_cpp_read(cpp_dev->base));
+ cpp_dev->fw_version = msm_cpp_read(cpp_dev->base);
+ pr_info("CPP FW Version: 0x%08x\n", cpp_dev->fw_version);
msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_TRAILER);
/*Disable MC clock*/
@@ -1284,12 +1295,9 @@
unsigned long in_phyaddr, out_phyaddr0, out_phyaddr1;
uint16_t num_stripes = 0;
struct msm_buf_mngr_info buff_mgr_info, dup_buff_mgr_info;
- struct msm_cpp_frame_info_t *u_frame_info =
- (struct msm_cpp_frame_info_t *)ioctl_ptr->ioctl_ptr;
int32_t status = 0;
- uint8_t fw_version_1_2_x = 0;
int in_fd;
-
+ int32_t stripe_base = 0;
int i = 0;
if (!new_frame) {
pr_err("Insufficient memory. return\n");
@@ -1330,7 +1338,16 @@
}
new_frame->cpp_cmd_msg = cpp_frame_msg;
-
+ if (cpp_frame_msg == NULL ||
+ (new_frame->msg_len < MSM_CPP_MIN_FRAME_LENGTH)) {
+ pr_err("%s %d Length is not correct or frame message is missing\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ if (cpp_frame_msg[new_frame->msg_len - 1] != MSM_CPP_MSG_ID_TRAILER) {
+ pr_err("%s %d Invalid frame message\n", __func__, __LINE__);
+ return -EINVAL;
+ }
in_phyaddr = msm_cpp_fetch_buffer_info(cpp_dev,
&new_frame->input_buffer_info,
((new_frame->input_buffer_info.identity >> 16) & 0xFFFF),
@@ -1404,22 +1421,36 @@
((cpp_frame_msg[12] >> 10) & 0x3FF) +
(cpp_frame_msg[12] & 0x3FF);
- fw_version_1_2_x = 0;
- if ((cpp_dev->hw_info.cpp_hw_version == CPP_HW_VERSION_1_1_0) ||
- (cpp_dev->hw_info.cpp_hw_version == CPP_HW_VERSION_1_1_1) ||
- (cpp_dev->hw_info.cpp_hw_version == CPP_HW_VERSION_2_0_0))
- fw_version_1_2_x = 2;
+ if ((cpp_dev->fw_version & 0xffff0000) ==
+ CPP_FW_VERSION_1_2_0) {
+ stripe_base = STRIPE_BASE_FW_1_2_0;
+ } else if ((cpp_dev->fw_version & 0xffff0000) ==
+ CPP_FW_VERSION_1_4_0) {
+ stripe_base = STRIPE_BASE_FW_1_4_0;
+ } else if ((cpp_dev->fw_version & 0xffff0000) ==
+ CPP_FW_VERSION_1_6_0) {
+ stripe_base = STRIPE_BASE_FW_1_6_0;
+ } else {
+ pr_err("invalid fw version %08x", cpp_dev->fw_version);
+ }
+
+ if ((stripe_base + num_stripes*27 + 1) != new_frame->msg_len) {
+ pr_err("Invalid frame message\n");
+ rc = -EINVAL;
+ goto ERROR3;
+ }
+
for (i = 0; i < num_stripes; i++) {
- cpp_frame_msg[(133 + fw_version_1_2_x) + i * 27] +=
+ cpp_frame_msg[stripe_base + 5 + i*27] +=
(uint32_t) in_phyaddr;
- cpp_frame_msg[(139 + fw_version_1_2_x) + i * 27] +=
+ cpp_frame_msg[stripe_base + 11 + i * 27] +=
(uint32_t) out_phyaddr0;
- cpp_frame_msg[(140 + fw_version_1_2_x) + i * 27] +=
+ cpp_frame_msg[stripe_base + 12 + i * 27] +=
(uint32_t) out_phyaddr1;
- cpp_frame_msg[(141 + fw_version_1_2_x) + i * 27] +=
+ cpp_frame_msg[stripe_base + 13 + i * 27] +=
(uint32_t) out_phyaddr0;
- cpp_frame_msg[(142 + fw_version_1_2_x) + i * 27] +=
+ cpp_frame_msg[stripe_base + 14 + i * 27] +=
(uint32_t) out_phyaddr1;
}
@@ -1441,7 +1472,7 @@
ioctl_ptr->trans_code = rc;
status = rc;
- rc = (copy_to_user((void __user *)u_frame_info->status, &status,
+ rc = (copy_to_user((void __user *)new_frame->status, &status,
sizeof(int32_t)) ? -EFAULT : 0);
if (rc) {
ERR_COPY_FROM_USER();
@@ -1457,12 +1488,12 @@
ERROR2:
kfree(cpp_frame_msg);
ERROR1:
- kfree(new_frame);
ioctl_ptr->trans_code = rc;
status = rc;
- if (copy_to_user((void __user *)u_frame_info->status, &status,
+ if (copy_to_user((void __user *)new_frame->status, &status,
sizeof(int32_t)))
pr_err("error cannot copy error\n");
+ kfree(new_frame);
return rc;
}
diff --git a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h
index bd73ab2..af1af2d 100644
--- a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h
+++ b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h
@@ -189,6 +189,7 @@
char *fw_name_bin;
struct workqueue_struct *timer_wq;
struct msm_cpp_work_t *work;
+ uint32_t fw_version;
uint8_t stream_cnt;
uint8_t timeout_trial_cnt;
diff --git a/drivers/media/platform/msm/vidc/hfi_packetization.c b/drivers/media/platform/msm/vidc/hfi_packetization.c
index a563f68..0e38b18 100644
--- a/drivers/media/platform/msm/vidc/hfi_packetization.c
+++ b/drivers/media/platform/msm/vidc/hfi_packetization.c
@@ -1447,7 +1447,6 @@
hfi->ltrcount = hal->ltrcount;
hfi->trustmode = hal->trustmode;
pkt->size += sizeof(u32) + sizeof(struct hfi_ltrmode);
- pr_err("SET LTR\n");
break;
}
case HAL_CONFIG_VENC_USELTRFRAME:
@@ -1461,7 +1460,6 @@
hfi->refltr = hal->refltr;
hfi->useconstrnt = hal->useconstrnt;
pkt->size += sizeof(u32) + sizeof(struct hfi_ltruse);
- pr_err("USE LTR\n");
break;
}
case HAL_CONFIG_VENC_MARKLTRFRAME:
@@ -1473,7 +1471,6 @@
hfi = (struct hfi_ltrmark *) &pkt->rg_property_data[1];
hfi->markframe = hal->markframe;
pkt->size += sizeof(u32) + sizeof(struct hfi_ltrmark);
- pr_err("MARK LTR\n");
break;
}
case HAL_PARAM_VENC_HIER_P_MAX_ENH_LAYERS:
diff --git a/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
index c638415..aaed0e9 100644
--- a/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
+++ b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
@@ -543,6 +543,35 @@
{}
};
+static int msm_vidc_pm_suspend(struct device *pdev)
+{
+ struct msm_vidc_core *core;
+
+ if (!pdev) {
+ dprintk(VIDC_ERR, "%s invalid device\n", __func__);
+ return -EINVAL;
+ }
+
+ core = (struct msm_vidc_core *)pdev->platform_data;
+ if (!core) {
+ dprintk(VIDC_ERR, "%s invalid core\n", __func__);
+ return -EINVAL;
+ }
+ dprintk(VIDC_INFO, "%s\n", __func__);
+
+ return msm_vidc_suspend(core->id);
+}
+
+static int msm_vidc_pm_resume(struct device *dev)
+{
+ dprintk(VIDC_INFO, "%s\n", __func__);
+ return 0;
+}
+
+static const struct dev_pm_ops msm_vidc_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(msm_vidc_pm_suspend, msm_vidc_pm_resume)
+};
+
MODULE_DEVICE_TABLE(of, msm_vidc_dt_match);
static struct platform_driver msm_vidc_driver = {
@@ -552,6 +581,7 @@
.name = "msm_vidc_v4l2",
.owner = THIS_MODULE,
.of_match_table = msm_vidc_dt_match,
+ .pm = &msm_vidc_pm_ops,
},
};
diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c
index bcd13b8..701a8cc 100644
--- a/drivers/media/platform/msm/vidc/msm_venc.c
+++ b/drivers/media/platform/msm/vidc/msm_venc.c
@@ -723,7 +723,7 @@
{
.id = V4L2_CID_MPEG_VIDC_VIDEO_USELTRFRAME,
.name = "H264 Use LTR",
- .type = V4L2_CTRL_TYPE_BUTTON,
+ .type = V4L2_CTRL_TYPE_INTEGER,
.minimum = 0,
.maximum = (MAX_LTR_FRAME_COUNT - 1),
.default_value = 0,
@@ -753,7 +753,7 @@
{
.id = V4L2_CID_MPEG_VIDC_VIDEO_MARKLTRFRAME,
.name = "H264 Mark LTR",
- .type = V4L2_CTRL_TYPE_BUTTON,
+ .type = V4L2_CTRL_TYPE_INTEGER,
.minimum = 0,
.maximum = (MAX_LTR_FRAME_COUNT - 1),
.default_value = 0,
@@ -2179,7 +2179,7 @@
}
case V4L2_CID_MPEG_VIDC_VIDEO_USELTRFRAME:
property_id = HAL_CONFIG_VENC_USELTRFRAME;
- useltr.refltr = ctrl->val;
+ useltr.refltr = (1 << ctrl->val);
useltr.useconstrnt = false;
useltr.frames = 0;
pdata = &useltr;
@@ -2302,7 +2302,6 @@
rc = call_hfi_op(hdev, session_set_property,
(void *)inst->session, property_id, pdata);
}
- pr_err("Returning from %s\n", __func__);
return rc;
}
diff --git a/drivers/media/platform/msm/vidc/msm_vidc.c b/drivers/media/platform/msm/vidc/msm_vidc.c
index cfc2eb8..c5d038f 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc.c
@@ -1404,3 +1404,8 @@
return 0;
}
+
+int msm_vidc_suspend(int core_id)
+{
+ return msm_comm_suspend(core_id);
+}
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c
index 9f0dac4..84b0780 100755
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c
@@ -2112,6 +2112,33 @@
return rc;
}
+int msm_comm_suspend(int core_id)
+{
+ struct hfi_device *hdev;
+ struct msm_vidc_core *core;
+ int rc = 0;
+
+ core = get_vidc_core(core_id);
+ if (!core) {
+ dprintk(VIDC_ERR,
+ "%s: Failed to find core for core_id = %d\n",
+ __func__, core_id);
+ return -EINVAL;
+ }
+
+ hdev = (struct hfi_device *)core->device;
+ if (!hdev) {
+ dprintk(VIDC_ERR, "%s Invalid device handle\n", __func__);
+ return -EINVAL;
+ }
+
+ rc = call_hfi_op(hdev, suspend, hdev->hfi_device_data);
+ if (rc)
+ dprintk(VIDC_WARN, "Failed to suspend\n");
+
+ return rc;
+}
+
static int get_flipped_state(int present_state,
int desired_state)
{
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.h b/drivers/media/platform/msm/vidc/msm_vidc_common.h
index da71424..f4e2cd5 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.h
@@ -40,6 +40,7 @@
int msm_comm_release_persist_buffers(struct msm_vidc_inst *inst);
int msm_comm_release_output_buffers(struct msm_vidc_inst *inst);
int msm_comm_force_cleanup(struct msm_vidc_inst *inst);
+int msm_comm_suspend(int core_id);
enum hal_extradata_id msm_comm_get_hal_extradata_index(
enum v4l2_mpeg_vidc_extradata index);
int msm_comm_get_domain_partition(struct msm_vidc_inst *inst, u32 flags,
diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c
index 448fe3b..a46dc95 100755
--- a/drivers/media/platform/msm/vidc/venus_hfi.c
+++ b/drivers/media/platform/msm/vidc/venus_hfi.c
@@ -3518,6 +3518,25 @@
return rc;
}
+static int venus_hfi_suspend(void *dev)
+{
+ int rc = 0;
+ struct venus_hfi_device *device = (struct venus_hfi_device *) dev;
+
+ if (!device) {
+ dprintk(VIDC_ERR, "%s invalid device\n", __func__);
+ return -EINVAL;
+ }
+ dprintk(VIDC_INFO, "%s\n", __func__);
+
+ if (device->power_enabled) {
+ venus_hfi_try_clk_gating(device);
+ rc = flush_delayed_work(&venus_hfi_pm_work);
+ dprintk(VIDC_INFO, "%s flush delayed work %d\n", __func__, rc);
+ }
+ return 0;
+}
+
static int venus_hfi_load_fw(void *dev)
{
int rc = 0;
@@ -3910,6 +3929,7 @@
hdev->get_stride_scanline = venus_hfi_get_stride_scanline;
hdev->get_core_capabilities = venus_hfi_get_core_capabilities;
hdev->power_enable = venus_hfi_power_enable;
+ hdev->suspend = venus_hfi_suspend;
}
int venus_hfi_initialize(struct hfi_device *hdev, u32 device_id,
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_api.h b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
index 38c5bdb..8484bb2 100755
--- a/drivers/media/platform/msm/vidc/vidc_hfi_api.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
@@ -1171,6 +1171,7 @@
int (*session_clean)(void *sess);
int (*get_core_capabilities)(void);
int (*power_enable)(void *dev);
+ int (*suspend)(void *dev);
};
typedef void (*hfi_cmd_response_callback) (enum command_response cmd,
diff --git a/drivers/net/wireless/wcnss/wcnss_wlan.c b/drivers/net/wireless/wcnss/wcnss_wlan.c
index d06ec85..2d260be 100644
--- a/drivers/net/wireless/wcnss/wcnss_wlan.c
+++ b/drivers/net/wireless/wcnss/wcnss_wlan.c
@@ -207,7 +207,7 @@
/* max 20mhz channel count */
#define WCNSS_MAX_CH_NUM 45
-#define WCNSS_MAX_PIL_RETRY 3
+#define WCNSS_MAX_PIL_RETRY 2
#define VALID_VERSION(version) \
((strncmp(version, "INVALID", WCNSS_VERSION_LEN)) ? 1 : 0)
diff --git a/drivers/power/qpnp-charger.c b/drivers/power/qpnp-charger.c
index 94d48e6..7963ed4 100644
--- a/drivers/power/qpnp-charger.c
+++ b/drivers/power/qpnp-charger.c
@@ -2465,11 +2465,6 @@
int rc;
u8 chgr_sts, bat_if_sts;
- if ((qpnp_chg_is_usb_chg_plugged_in(chip) ||
- qpnp_chg_is_dc_chg_plugged_in(chip)) && chip->chg_done) {
- return POWER_SUPPLY_STATUS_FULL;
- }
-
rc = qpnp_chg_read(chip, &chgr_sts, INT_RT_STS(chip->chgr_base), 1);
if (rc) {
pr_err("failed to read interrupt sts %d\n", rc);
@@ -2487,10 +2482,14 @@
if (chgr_sts & FAST_CHG_ON_IRQ && bat_if_sts & BAT_FET_ON_IRQ)
return POWER_SUPPLY_STATUS_CHARGING;
- /* report full if state of charge is 100 and a charger is connected */
+ /*
+ * Report full if state of charge is 100 or chg_done is true
+ * when a charger is connected and boost is disabled
+ */
if ((qpnp_chg_is_usb_chg_plugged_in(chip) ||
- qpnp_chg_is_dc_chg_plugged_in(chip))
- && get_batt_capacity(chip) == 100) {
+ qpnp_chg_is_dc_chg_plugged_in(chip)) &&
+ (chip->chg_done || get_batt_capacity(chip) == 100)
+ && qpnp_chg_is_boost_en_set(chip) == 0) {
return POWER_SUPPLY_STATUS_FULL;
}
@@ -3242,10 +3241,10 @@
qpnp_chg_regulator_boost_enable(struct regulator_dev *rdev)
{
struct qpnp_chg_chip *chip = rdev_get_drvdata(rdev);
+ int usb_present = qpnp_chg_is_usb_chg_plugged_in(chip);
int rc;
- if (qpnp_chg_is_usb_chg_plugged_in(chip) &&
- (chip->flags & BOOST_FLASH_WA)) {
+ if (usb_present && (chip->flags & BOOST_FLASH_WA)) {
if (ext_ovp_isns_present && chip->ext_ovp_ic_gpio_enabled) {
pr_debug("EXT OVP IC ISNS disabled\n");
@@ -3273,10 +3272,24 @@
}
}
- return qpnp_chg_masked_write(chip,
+ rc = qpnp_chg_masked_write(chip,
chip->boost_base + BOOST_ENABLE_CONTROL,
BOOST_PWR_EN,
BOOST_PWR_EN, 1);
+ if (rc) {
+ pr_err("failed to enable boost rc = %d\n", rc);
+ return rc;
+ }
+ /*
+ * update battery status when charger is connected and state is full
+ */
+ if (usb_present && (chip->chg_done
+ || (get_batt_capacity(chip) == 100)
+ || (get_prop_batt_status(chip) ==
+ POWER_SUPPLY_STATUS_FULL)))
+ power_supply_changed(&chip->batt_psy);
+
+ return rc;
}
/* Boost regulator operations */
@@ -3369,6 +3382,20 @@
qpnp_chg_usb_suspend_enable(chip, 0);
}
+ /*
+ * When a charger is connected,if state of charge is not full
+ * resumeing charging else update battery status
+ */
+ if (qpnp_chg_is_usb_chg_plugged_in(chip)) {
+ if (get_batt_capacity(chip) < 100 || !chip->chg_done) {
+ chip->chg_done = false;
+ chip->resuming_charging = true;
+ qpnp_chg_set_appropriate_vbatdet(chip);
+ } else if (chip->chg_done) {
+ power_supply_changed(&chip->batt_psy);
+ }
+ }
+
if (ext_ovp_isns_present && chip->ext_ovp_ic_gpio_enabled) {
pr_debug("EXT OVP IC ISNS enable\n");
gpio_direction_output(chip->ext_ovp_isns_gpio, 1);
diff --git a/drivers/spi/spi_qsd.c b/drivers/spi/spi_qsd.c
index 5686107..b570937 100644
--- a/drivers/spi/spi_qsd.c
+++ b/drivers/spi/spi_qsd.c
@@ -3102,6 +3102,7 @@
spin_lock_init(&dd->queue_lock);
mutex_init(&dd->core_lock);
+ init_waitqueue_head(&dd->continue_suspend);
if (!devm_request_mem_region(&pdev->dev, dd->mem_phys_addr,
dd->mem_size, SPI_DRV_NAME)) {
diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c
index 05292f9..669fca9 100644
--- a/drivers/usb/storage/scsiglue.c
+++ b/drivers/usb/storage/scsiglue.c
@@ -235,15 +235,6 @@
sdev->use_rpm_auto = 1;
sdev->autosuspend_delay = us->sdev_autosuspend_delay;
}
-
- /*
- * This quirk enables sending consecutive TEST_UNIT_READY
- * commands in WRITE(10) command processing context. Increase
- * the timeout to 60 seconds.
- */
- if (us->fflags & US_FL_TUR_AFTER_WRITE)
- blk_queue_rq_timeout(sdev->request_queue, (60 * HZ));
-
} else {
/* Non-disk-type devices don't need to blacklist any pages
diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c
index a710d9f..c70109e 100644
--- a/drivers/usb/storage/transport.c
+++ b/drivers/usb/storage/transport.c
@@ -859,42 +859,6 @@
srb->result = DID_ERROR << 16;
last_sector_hacks(us, srb);
-
- /*
- * TMC UICC cards expect 5 TEST_UNIT_READY commands after
- * writing some data. The card performs the flash related
- * house keeping operations after receiving these commands.
- * Send 5 TEST_UNIT_READY commands for every 8 WRITE_10
- * commands.
- */
- if (unlikely((us->fflags & US_FL_TUR_AFTER_WRITE) &&
- srb->cmnd[0] == WRITE_10)) {
- int i;
- int temp_result;
- struct scsi_eh_save ses;
- unsigned char cmd[] = {
- TEST_UNIT_READY, 0, 0, 0, 0, 0,
- };
-
- us->tur_count[srb->device->lun]++;
-
- if (++us->tur_count[srb->device->lun] == 8) {
-
- us->tur_count[srb->device->lun] = 0;
-
- scsi_eh_prep_cmnd(srb, &ses, cmd, 6, 0);
- for (i = 0; i < 5; i++) {
- temp_result = us->transport(us->srb, us);
- if (temp_result != USB_STOR_TRANSPORT_GOOD) {
- US_DEBUGP("TUR failed %d %d\n",
- i, temp_result);
- break;
- }
- }
- scsi_eh_restore_cmnd(srb, &ses);
- }
- }
-
return;
/* Error and abort processing: try to resynchronize with the device
diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h
index 901f6fb..856ad92 100644
--- a/drivers/usb/storage/unusual_devs.h
+++ b/drivers/usb/storage/unusual_devs.h
@@ -2018,12 +2018,6 @@
"Digital MP3 Audio Player",
USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_NOT_LOCKABLE ),
-/* Reported by Pavankumar Kondeti <pkondeti@codeaurora.org> */
-UNUSUAL_DEV(0x0925, 0x9011, 0x0100, 0x0100,
- "TMC",
- "USB DISK",
- USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_TUR_AFTER_WRITE),
-
/* Control/Bulk transport for all SubClass values */
USUAL_DEV(USB_SC_RBC, USB_PR_CB, USB_US_TYPE_STOR),
USUAL_DEV(USB_SC_8020, USB_PR_CB, USB_US_TYPE_STOR),
diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h
index b079984..db75080 100644
--- a/drivers/usb/storage/usb.h
+++ b/drivers/usb/storage/usb.h
@@ -162,8 +162,6 @@
int use_last_sector_hacks;
int last_sector_retries;
int sdev_autosuspend_delay;
- /* consecutive TEST_UNIT_READY commands during write */
- int tur_count[16];
};
/* Convert between us_data and the corresponding Scsi_Host */
diff --git a/drivers/video/msm/mdss/dsi_v2.c b/drivers/video/msm/mdss/dsi_v2.c
index f12d03a..4fc3909 100644
--- a/drivers/video/msm/mdss/dsi_v2.c
+++ b/drivers/video/msm/mdss/dsi_v2.c
@@ -133,8 +133,8 @@
pdata->panel_info.panel_power_on = 0;
if (!pdata->panel_info.dynamic_switch_pending) {
if (pdata->panel_info.type == MIPI_CMD_PANEL)
- mdss_dsi_panel_reset(pdata, 0);
- dsi_ctrl_gpio_free(ctrl_pdata);
+ dsi_ctrl_gpio_free(ctrl_pdata);
+ mdss_dsi_panel_reset(pdata, 0);
}
}
return rc;
@@ -296,7 +296,7 @@
if (!dev || !mp) {
pr_err("%s: invalid input\n", __func__);
rc = -EINVAL;
- goto error;
+ return rc;
}
np = dev->of_node;
diff --git a/drivers/video/msm/mdss/mdp3.c b/drivers/video/msm/mdss/mdp3.c
index 37134b4..d4d913f 100644
--- a/drivers/video/msm/mdss/mdp3.c
+++ b/drivers/video/msm/mdss/mdp3.c
@@ -2373,7 +2373,7 @@
if (!mdp3_res || !mdp3_res->pan_cfg.init_done)
rc = -EPROBE_DEFER;
- if (mdp3_res->pan_cfg.lk_cfg)
+ else if (mdp3_res->pan_cfg.lk_cfg)
rc = 1;
else
rc = 0;
diff --git a/drivers/video/msm/mdss/mdp3_ctrl.c b/drivers/video/msm/mdss/mdp3_ctrl.c
index f448c46..cd0af6d 100644
--- a/drivers/video/msm/mdss/mdp3_ctrl.c
+++ b/drivers/video/msm/mdss/mdp3_ctrl.c
@@ -49,7 +49,7 @@
if (!count)
return;
- while (count--) {
+ while (count-- && (bufq->pop_idx >= 0)) {
struct mdp3_img_data *data = &bufq->img_data[bufq->pop_idx];
bufq->pop_idx = (bufq->pop_idx + 1) % MDP3_MAX_BUF_QUEUE;
mdp3_put_img(data, MDP3_CLIENT_DMA_P);
@@ -986,7 +986,7 @@
{
struct mdp3_session_data *mdp3_session;
struct mdp3_img_data *data;
- struct mdss_panel_info *panel_info = mfd->panel_info;
+ struct mdss_panel_info *panel_info;
int rc = 0;
bool reset_done = false;
struct mdss_panel_data *panel;
@@ -994,6 +994,7 @@
if (!mfd || !mfd->mdp.private1)
return -EINVAL;
+ panel_info = mfd->panel_info;
mdp3_session = mfd->mdp.private1;
if (!mdp3_session || !mdp3_session->dma)
return -EINVAL;
@@ -1050,7 +1051,8 @@
if (mdp3_bufq_count(&mdp3_session->bufq_out) > 1) {
mdp3_release_splash_memory(mfd);
data = mdp3_bufq_pop(&mdp3_session->bufq_out);
- mdp3_put_img(data, MDP3_CLIENT_DMA_P);
+ if (data)
+ mdp3_put_img(data, MDP3_CLIENT_DMA_P);
}
if (mdp3_session->first_commit) {
@@ -1076,13 +1078,14 @@
struct mdp3_session_data *mdp3_session;
u32 offset;
int bpp;
- struct mdss_panel_info *panel_info = mfd->panel_info;
+ struct mdss_panel_info *panel_info;
int rc;
pr_debug("mdp3_ctrl_pan_display\n");
if (!mfd || !mfd->mdp.private1)
return;
+ panel_info = mfd->panel_info;
mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1;
if (!mdp3_session || !mdp3_session->dma)
return;
diff --git a/drivers/video/msm/mdss/mdss_dsi.c b/drivers/video/msm/mdss/mdss_dsi.c
index 993645a..99fd195 100644
--- a/drivers/video/msm/mdss/mdss_dsi.c
+++ b/drivers/video/msm/mdss/mdss_dsi.c
@@ -131,7 +131,7 @@
if (!dev || !mp) {
pr_err("%s: invalid input\n", __func__);
rc = -EINVAL;
- goto error;
+ return rc;
}
of_node = dev->of_node;
diff --git a/drivers/video/msm/mdss/mdss_dsi_host.c b/drivers/video/msm/mdss/mdss_dsi_host.c
index d8cd22e..d8a713e 100644
--- a/drivers/video/msm/mdss/mdss_dsi_host.c
+++ b/drivers/video/msm/mdss/mdss_dsi_host.c
@@ -1534,9 +1534,11 @@
(struct mdss_dsi_ctrl_pdata *)ptr;
struct mdss_dsi_ctrl_pdata *mctrl = NULL;
- if (!ctrl->ctrl_base)
+ if (!ctrl->ctrl_base) {
pr_err("%s:%d DSI base adr no Initialized",
- __func__, __LINE__);
+ __func__, __LINE__);
+ return IRQ_HANDLED;
+ }
isr = MIPI_INP(ctrl->ctrl_base + 0x0110);/* DSI_INTR_CTRL */
MIPI_OUTP(ctrl->ctrl_base + 0x0110, isr);
diff --git a/drivers/video/msm/mdss/mdss_dsi_panel.c b/drivers/video/msm/mdss/mdss_dsi_panel.c
index 1ac37dc..4fa995f 100644
--- a/drivers/video/msm/mdss/mdss_dsi_panel.c
+++ b/drivers/video/msm/mdss/mdss_dsi_panel.c
@@ -924,13 +924,14 @@
else if (!strcmp(pdest, "display_2"))
pinfo->pdest = DISPLAY_2;
else {
- pr_debug("%s: pdest not specified. Set Default\n",
- __func__);
+ pr_debug("%s: incorrect pdest. Set Default\n",
+ __func__);
pinfo->pdest = DISPLAY_1;
}
} else {
- pr_err("%s: pdest not specified\n", __func__);
- return -EINVAL;
+ pr_debug("%s: pdest not specified. Set Default\n",
+ __func__);
+ pinfo->pdest = DISPLAY_1;
}
rc = of_property_read_u32(np, "qcom,mdss-dsi-h-front-porch", &tmp);
pinfo->lcdc.h_front_porch = (!rc ? tmp : 6);
diff --git a/drivers/video/msm/mdss/mdss_fb.c b/drivers/video/msm/mdss/mdss_fb.c
index 511fa00..84ee426 100644
--- a/drivers/video/msm/mdss/mdss_fb.c
+++ b/drivers/video/msm/mdss/mdss_fb.c
@@ -661,13 +661,13 @@
mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
+ if (!mfd)
+ return -ENODEV;
+
mdss_fb_remove_sysfs(mfd);
pm_runtime_disable(mfd->fbi->dev);
- if (!mfd)
- return -ENODEV;
-
if (mfd->key != MFD_KEY)
return -EINVAL;
@@ -2653,7 +2653,8 @@
pr_info("adding framebuffer device %s\n", dev_name(&pdev->dev));
fb_pdev = of_platform_device_create(node, NULL,
&mdss_pdev->dev);
- fb_pdev->dev.platform_data = pdata;
+ if (fb_pdev)
+ fb_pdev->dev.platform_data = pdata;
}
if (master_panel && mdp_instance->panel_register_done)
@@ -2682,7 +2683,7 @@
struct fb_info *info;
struct msm_fb_data_type *mfd;
- if (fb_num > MAX_FBI_LIST)
+ if (fb_num >= MAX_FBI_LIST)
return -EINVAL;
info = fbi_list[fb_num];
diff --git a/drivers/video/msm/mdss/mdss_mdp_intf_cmd.c b/drivers/video/msm/mdss/mdss_mdp_intf_cmd.c
index e619e6b..6808313 100644
--- a/drivers/video/msm/mdss/mdss_mdp_intf_cmd.c
+++ b/drivers/video/msm/mdss/mdss_mdp_intf_cmd.c
@@ -405,14 +405,6 @@
return;
}
- mutex_lock(&ctx->clk_mtx);
- if (ctx->clk_enabled) {
- mutex_unlock(&ctx->clk_mtx);
- pr_warn("Cannot enter ulps mode if DSI clocks are on\n");
- return;
- }
- mutex_unlock(&ctx->clk_mtx);
-
if (!ctx->panel_on) {
pr_err("Panel is off. skipping ULPS configuration\n");
return;
diff --git a/drivers/video/msm/mdss/mdss_mdp_overlay.c b/drivers/video/msm/mdss/mdss_mdp_overlay.c
index f371f9c..41a7d9d 100644
--- a/drivers/video/msm/mdss/mdss_mdp_overlay.c
+++ b/drivers/video/msm/mdss/mdss_mdp_overlay.c
@@ -56,6 +56,7 @@
static int mdss_mdp_overlay_fb_parse_dt(struct msm_fb_data_type *mfd);
static int mdss_mdp_overlay_off(struct msm_fb_data_type *mfd);
static void __overlay_kickoff_requeue(struct msm_fb_data_type *mfd);
+static void __vsync_retire_signal(struct msm_fb_data_type *mfd, int val);
static int mdss_mdp_overlay_sd_ctrl(struct msm_fb_data_type *mfd,
unsigned int enable)
@@ -1995,6 +1996,8 @@
mixer = mdss_mdp_mixer_get(mdp5_data->ctl, MDSS_MDP_MIXER_MUX_DEFAULT);
off = MDSS_MDP_REG_LM_OFFSET(mixer->num);
+ if (!mixer)
+ return -ENODEV;
if ((img->width > MDSS_MDP_CURSOR_WIDTH) ||
(img->height > MDSS_MDP_CURSOR_HEIGHT) ||
@@ -2788,6 +2791,20 @@
mdss_mdp_overlay_kickoff(mfd, NULL);
}
+ /*
+ * If retire fences are still active wait for a vsync time
+ * for retire fence to be updated.
+ * As a last resort signal the timeline if vsync doesn't arrive.
+ */
+ if (mdp5_data->retire_cnt) {
+ u32 fps = mdss_panel_get_framerate(mfd->panel_info);
+ u32 vsync_time = 1000 / (fps ? : DEFAULT_FRAME_RATE);
+
+ msleep(vsync_time);
+
+ __vsync_retire_signal(mfd, mdp5_data->retire_cnt);
+ }
+
rc = mdss_mdp_ctl_stop(mdp5_data->ctl);
if (rc == 0) {
__mdss_mdp_overlay_free_list_purge(mfd);
@@ -2966,7 +2983,6 @@
{
struct mdss_overlay_private *mdp5_data =
container_of(work, typeof(*mdp5_data), retire_work);
- struct msm_sync_pt_data *sync_pt_data;
if (!mdp5_data->ctl || !mdp5_data->ctl->mfd)
return;
@@ -2974,12 +2990,18 @@
if (!mdp5_data->ctl->remove_vsync_handler)
return;
- sync_pt_data = &mdp5_data->ctl->mfd->mdp_sync_pt_data;
- mutex_lock(&sync_pt_data->sync_mutex);
- if (mdp5_data->retire_cnt > 0) {
- sw_sync_timeline_inc(mdp5_data->vsync_timeline, 1);
+ __vsync_retire_signal(mdp5_data->ctl->mfd, 1);
+}
- mdp5_data->retire_cnt--;
+static void __vsync_retire_signal(struct msm_fb_data_type *mfd, int val)
+{
+ struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
+
+ mutex_lock(&mfd->mdp_sync_pt_data.sync_mutex);
+ if (mdp5_data->retire_cnt > 0) {
+ sw_sync_timeline_inc(mdp5_data->vsync_timeline, val);
+
+ mdp5_data->retire_cnt -= min(val, mdp5_data->retire_cnt);
if (mdp5_data->retire_cnt == 0) {
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
mdp5_data->ctl->remove_vsync_handler(mdp5_data->ctl,
@@ -2987,7 +3009,7 @@
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
}
}
- mutex_unlock(&sync_pt_data->sync_mutex);
+ mutex_unlock(&mfd->mdp_sync_pt_data.sync_mutex);
}
static struct sync_fence *
@@ -3013,7 +3035,7 @@
return ERR_PTR(-EPERM);
}
- if (mdp5_data->retire_cnt == 0) {
+ if (!mdp5_data->vsync_retire_handler.enabled) {
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
rc = ctl->add_vsync_handler(ctl,
&mdp5_data->vsync_retire_handler);
diff --git a/drivers/video/msm/mdss/mdss_mdp_pipe.c b/drivers/video/msm/mdss/mdss_mdp_pipe.c
index 70544c0..ccd60a6 100644
--- a/drivers/video/msm/mdss/mdss_mdp_pipe.c
+++ b/drivers/video/msm/mdss/mdss_mdp_pipe.c
@@ -276,7 +276,7 @@
if (pipe->mixer && pipe->mixer->rotator_mode) {
rot_mode = 1;
- } else if (ps.num_planes == 1) {
+ } else if (pipe->mixer && (ps.num_planes == 1)) {
ps.ystride[0] = MAX_BPP *
max(pipe->mixer->width, width);
} else if (mdata->has_decimation) {
diff --git a/drivers/video/msm/mdss/mdss_mdp_pp.c b/drivers/video/msm/mdss/mdss_mdp_pp.c
index 7159a0a..ebda2ee 100644
--- a/drivers/video/msm/mdss/mdss_mdp_pp.c
+++ b/drivers/video/msm/mdss/mdss_mdp_pp.c
@@ -1174,15 +1174,17 @@
struct pp_hist_col_info *hist_info;
char __iomem *ctl_base;
- if (!pipe && pipe->pp_res.hist.col_en) {
- done_bit = 3 << (pipe->num * 4);
- hist_info = &pipe->pp_res.hist;
- ctl_base = pipe->base +
- MDSS_MDP_REG_VIG_HIST_CTL_BASE;
- pp_histogram_disable(hist_info, done_bit, ctl_base);
+ if (pipe) {
+ if (pipe->pp_res.hist.col_en) {
+ done_bit = 3 << (pipe->num * 4);
+ hist_info = &pipe->pp_res.hist;
+ ctl_base = pipe->base +
+ MDSS_MDP_REG_VIG_HIST_CTL_BASE;
+ pp_histogram_disable(hist_info, done_bit, ctl_base);
+ }
+ memset(&pipe->pp_cfg, 0, sizeof(struct mdp_overlay_pp_params));
+ memset(&pipe->pp_res, 0, sizeof(struct mdss_pipe_pp_res));
}
- memset(&pipe->pp_cfg, 0, sizeof(struct mdp_overlay_pp_params));
- memset(&pipe->pp_res, 0, sizeof(struct mdss_pipe_pp_res));
}
int mdss_mdp_pipe_sspp_setup(struct mdss_mdp_pipe *pipe, u32 *op)
@@ -1251,10 +1253,10 @@
struct pp_sts_type *pp_sts;
struct mdss_mdp_ctl *ctl;
char __iomem *addr;
- dspp_num = mixer->num;
if (!mixer || !mixer->ctl)
return -EINVAL;
+ dspp_num = mixer->num;
ctl = mixer->ctl;
/* no corresponding dspp */
@@ -1815,7 +1817,7 @@
}
}
}
- if (mdata) {
+ if (mdata && mdata->vig_pipes) {
vig = mdata->vig_pipes;
for (i = 0; i < mdata->nvig_pipes; i++) {
mutex_init(&vig[i].pp_res.hist.hist_mutex);
@@ -1844,6 +1846,7 @@
mutex_unlock(&mdss_pp_mutex);
return ret;
}
+
void mdss_mdp_pp_term(struct device *dev)
{
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
@@ -3763,8 +3766,7 @@
hist_info = &pipe->pp_res.hist;
}
/* Histogram Done Interrupt */
- if (hist_info && (isr_blk & 0x1) &&
- (hist_info->col_en)) {
+ if (hist_info && (isr_blk & 0x1) && (hist_info->col_en)) {
spin_lock(&hist_info->hist_lock);
hist_info->col_state = HIST_READY;
spin_unlock(&hist_info->hist_lock);
@@ -3774,8 +3776,7 @@
}
}
/* Histogram Reset Done Interrupt */
- if ((isr_blk & 0x2) &&
- (hist_info->col_en)) {
+ if (hist_info && (isr_blk & 0x2) && (hist_info->col_en)) {
spin_lock(&hist_info->hist_lock);
hist_info->col_state = HIST_IDLE;
spin_unlock(&hist_info->hist_lock);
diff --git a/drivers/video/msm/mdss/mdss_mdp_util.c b/drivers/video/msm/mdss/mdss_mdp_util.c
index 9336582..01745fd 100644
--- a/drivers/video/msm/mdss/mdss_mdp_util.c
+++ b/drivers/video/msm/mdss/mdss_mdp_util.c
@@ -498,24 +498,28 @@
data->srcp_file = NULL;
} else if (!IS_ERR_OR_NULL(data->srcp_ihdl)) {
pr_debug("ion hdl=%p buf=0x%x\n", data->srcp_ihdl, data->addr);
-
- if (is_mdss_iommu_attached()) {
- int domain;
- if (data->flags & MDP_SECURE_OVERLAY_SESSION)
- domain = MDSS_IOMMU_DOMAIN_SECURE;
- else
- domain = MDSS_IOMMU_DOMAIN_UNSECURE;
- ion_unmap_iommu(iclient, data->srcp_ihdl,
+ if (!iclient) {
+ pr_err("invalid ion client\n");
+ return -ENOMEM;
+ } else {
+ if (is_mdss_iommu_attached()) {
+ int domain;
+ if (data->flags & MDP_SECURE_OVERLAY_SESSION)
+ domain = MDSS_IOMMU_DOMAIN_SECURE;
+ else
+ domain = MDSS_IOMMU_DOMAIN_UNSECURE;
+ ion_unmap_iommu(iclient, data->srcp_ihdl,
mdss_get_iommu_domain(domain), 0);
- if (domain == MDSS_IOMMU_DOMAIN_SECURE) {
- msm_ion_unsecure_buffer(iclient,
- data->srcp_ihdl);
+ if (domain == MDSS_IOMMU_DOMAIN_SECURE) {
+ msm_ion_unsecure_buffer(iclient,
+ data->srcp_ihdl);
+ }
}
+ ion_free(iclient, data->srcp_ihdl);
+ data->srcp_ihdl = NULL;
}
- ion_free(iclient, data->srcp_ihdl);
- data->srcp_ihdl = NULL;
} else {
return -ENOMEM;
}
diff --git a/drivers/video/msm/mdss/mdss_mdp_wb.c b/drivers/video/msm/mdss/mdss_mdp_wb.c
index 6086e28..3b30b2b 100644
--- a/drivers/video/msm/mdss/mdss_mdp_wb.c
+++ b/drivers/video/msm/mdss/mdss_mdp_wb.c
@@ -343,12 +343,12 @@
static int mdss_mdp_wb_register_node(struct mdss_mdp_wb *wb,
struct mdss_mdp_wb_data *node)
{
- node->state = REGISTERED;
- list_add_tail(&node->registered_entry, &wb->register_queue);
if (!node) {
pr_err("Invalid wb node\n");
return -EINVAL;
}
+ node->state = REGISTERED;
+ list_add_tail(&node->registered_entry, &wb->register_queue);
return 0;
}
diff --git a/drivers/video/msm/mdss/msm_mdss_io_8974.c b/drivers/video/msm/mdss/msm_mdss_io_8974.c
index 584ffe7..671fb50 100644
--- a/drivers/video/msm/mdss/msm_mdss_io_8974.c
+++ b/drivers/video/msm/mdss/msm_mdss_io_8974.c
@@ -217,7 +217,7 @@
}
/* find the mnd settings from mnd_table entry */
- for (; mnd_entry != mnd_table + ARRAY_SIZE(mnd_table); ++mnd_entry) {
+ for (; mnd_entry < mnd_table + ARRAY_SIZE(mnd_table); ++mnd_entry) {
if (((mnd_entry->lanes) == lanes) &&
((mnd_entry->bpp) == bpp))
break;
diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h
index 20fb776..3ba696b 100644
--- a/include/linux/rcupdate.h
+++ b/include/linux/rcupdate.h
@@ -190,6 +190,7 @@
extern void rcu_idle_exit(void);
extern void rcu_irq_enter(void);
extern void rcu_irq_exit(void);
+extern void exit_rcu(void);
/**
* RCU_NONIDLE - Indicate idle-loop code that needs RCU readers
diff --git a/include/linux/rcutiny.h b/include/linux/rcutiny.h
index e93df77..4e56a9c 100644
--- a/include/linux/rcutiny.h
+++ b/include/linux/rcutiny.h
@@ -91,23 +91,20 @@
{
}
-static inline void exit_rcu(void)
+static inline int rcu_needs_cpu(int cpu, unsigned long *delta_jiffies)
{
-}
-
-static inline int rcu_needs_cpu(int cpu)
-{
+ *delta_jiffies = ULONG_MAX;
return 0;
}
#else /* #ifdef CONFIG_TINY_RCU */
void rcu_preempt_note_context_switch(void);
-extern void exit_rcu(void);
int rcu_preempt_needs_cpu(void);
-static inline int rcu_needs_cpu(int cpu)
+static inline int rcu_needs_cpu(int cpu, unsigned long *delta_jiffies)
{
+ *delta_jiffies = ULONG_MAX;
return rcu_preempt_needs_cpu();
}
diff --git a/include/linux/rcutree.h b/include/linux/rcutree.h
index e8ee5dd..624e6e9 100644
--- a/include/linux/rcutree.h
+++ b/include/linux/rcutree.h
@@ -32,7 +32,7 @@
extern void rcu_init(void);
extern void rcu_note_context_switch(int cpu);
-extern int rcu_needs_cpu(int cpu);
+extern int rcu_needs_cpu(int cpu, unsigned long *delta_jiffies);
extern void rcu_cpu_stall_reset(void);
/*
@@ -45,18 +45,6 @@
rcu_note_context_switch(cpu);
}
-#ifdef CONFIG_TREE_PREEMPT_RCU
-
-extern void exit_rcu(void);
-
-#else /* #ifdef CONFIG_TREE_PREEMPT_RCU */
-
-static inline void exit_rcu(void)
-{
-}
-
-#endif /* #else #ifdef CONFIG_TREE_PREEMPT_RCU */
-
extern void synchronize_rcu_bh(void);
extern void synchronize_sched_expedited(void);
extern void synchronize_rcu_expedited(void);
diff --git a/include/linux/usb_usual.h b/include/linux/usb_usual.h
index ff30988..17df360 100644
--- a/include/linux/usb_usual.h
+++ b/include/linux/usb_usual.h
@@ -64,9 +64,7 @@
US_FLAG(NO_READ_CAPACITY_16, 0x00080000) \
/* cannot handle READ_CAPACITY_16 */ \
US_FLAG(INITIAL_READ10, 0x00100000) \
- /* Initial READ(10) (and others) must be retried */ \
- US_FLAG(TUR_AFTER_WRITE, 0x00200000) \
- /* 5 TEST_UNIT_READY after 8 WRITE(10) */ \
+ /* Initial READ(10) (and others) must be retried */
#define US_FLAG(name, value) US_FL_##name = value ,
enum { US_DO_ALL_FLAGS };
diff --git a/include/media/msm_vidc.h b/include/media/msm_vidc.h
index bbde6ef..e48c7da 100644
--- a/include/media/msm_vidc.h
+++ b/include/media/msm_vidc.h
@@ -55,6 +55,7 @@
void *msm_vidc_open(int core_id, int session_type);
int msm_vidc_close(void *instance);
+int msm_vidc_suspend(int core_id);
int msm_vidc_querycap(void *instance, struct v4l2_capability *cap);
int msm_vidc_enum_fmt(void *instance, struct v4l2_fmtdesc *f);
int msm_vidc_s_fmt(void *instance, struct v4l2_format *f);
diff --git a/include/media/msmb_pproc.h b/include/media/msmb_pproc.h
index 59dcca9..f5a53a8 100644
--- a/include/media/msmb_pproc.h
+++ b/include/media/msmb_pproc.h
@@ -13,7 +13,8 @@
#define MAX_NUM_CPP_STRIPS 8
#define MSM_CPP_MAX_NUM_PLANES 3
-#define MSM_CPP_MAX_FRAME_LENGTH 1024
+#define MSM_CPP_MIN_FRAME_LENGTH 13
+#define MSM_CPP_MAX_FRAME_LENGTH 2048
#define MSM_CPP_MAX_FW_NAME_LEN 32
#define MAX_FREQ_TBL 10
diff --git a/include/trace/events/rcu.h b/include/trace/events/rcu.h
index 3370997..d274734 100644
--- a/include/trace/events/rcu.h
+++ b/include/trace/events/rcu.h
@@ -289,9 +289,12 @@
* "In holdoff": Nothing to do, holding off after unsuccessful attempt.
* "Begin holdoff": Attempt failed, don't retry until next jiffy.
* "Dyntick with callbacks": Entering dyntick-idle despite callbacks.
+ * "Dyntick with lazy callbacks": Entering dyntick-idle w/lazy callbacks.
* "More callbacks": Still more callbacks, try again to clear them out.
* "Callbacks drained": All callbacks processed, off to dyntick idle!
* "Timer": Timer fired to cause CPU to continue processing callbacks.
+ * "Demigrate": Timer fired on wrong CPU, woke up correct CPU.
+ * "Cleanup after idle": Idle exited, timer canceled.
*/
TRACE_EVENT(rcu_prep_idle,
diff --git a/kernel/cpu.c b/kernel/cpu.c
index 2db91f9..745a600 100644
--- a/kernel/cpu.c
+++ b/kernel/cpu.c
@@ -648,10 +648,12 @@
void set_cpu_online(unsigned int cpu, bool online)
{
- if (online)
+ if (online) {
cpumask_set_cpu(cpu, to_cpumask(cpu_online_bits));
- else
+ cpumask_set_cpu(cpu, to_cpumask(cpu_active_bits));
+ } else {
cpumask_clear_cpu(cpu, to_cpumask(cpu_online_bits));
+ }
}
void set_cpu_active(unsigned int cpu, bool active)
diff --git a/kernel/futex.c b/kernel/futex.c
index e2b0fb9..9536918 100644
--- a/kernel/futex.c
+++ b/kernel/futex.c
@@ -1256,6 +1256,13 @@
if (requeue_pi) {
/*
+ * Requeue PI only works on two distinct uaddrs. This
+ * check is only valid for private futexes. See below.
+ */
+ if (uaddr1 == uaddr2)
+ return -EINVAL;
+
+ /*
* requeue_pi requires a pi_state, try to allocate it now
* without any locks in case it fails.
*/
@@ -1293,6 +1300,15 @@
if (unlikely(ret != 0))
goto out_put_key1;
+ /*
+ * The check above which compares uaddrs is not sufficient for
+ * shared futexes. We need to compare the keys:
+ */
+ if (requeue_pi && match_futex(&key1, &key2)) {
+ ret = -EINVAL;
+ goto out_put_keys;
+ }
+
hb1 = hash_futex(&key1);
hb2 = hash_futex(&key2);
@@ -2308,6 +2324,15 @@
if (ret)
goto out_key2;
+ /*
+ * The check above which compares uaddrs is not sufficient for
+ * shared futexes. We need to compare the keys:
+ */
+ if (match_futex(&q.key, &key2)) {
+ ret = -EINVAL;
+ goto out_put_keys;
+ }
+
/* Queue the futex_q, drop the hb lock, wait for wakeup. */
futex_wait_queue_me(hb, &q, to);
diff --git a/kernel/rcupdate.c b/kernel/rcupdate.c
index 5746f18..c1a835c 100644
--- a/kernel/rcupdate.c
+++ b/kernel/rcupdate.c
@@ -53,6 +53,33 @@
#include "rcu.h"
module_param(rcu_expedited, int, 0);
+#ifdef CONFIG_PREEMPT_RCU
+
+/*
+ * Check for a task exiting while in a preemptible-RCU read-side
+ * critical section, clean up if so. No need to issue warnings,
+ * as debug_check_no_locks_held() already does this if lockdep
+ * is enabled.
+ */
+void exit_rcu(void)
+{
+ struct task_struct *t = current;
+
+ if (likely(list_empty(¤t->rcu_node_entry)))
+ return;
+ t->rcu_read_lock_nesting = 1;
+ barrier();
+ t->rcu_read_unlock_special = RCU_READ_UNLOCK_BLOCKED;
+ __rcu_read_unlock();
+}
+
+#else /* #ifdef CONFIG_PREEMPT_RCU */
+
+void exit_rcu(void)
+{
+}
+
+#endif /* #else #ifdef CONFIG_PREEMPT_RCU */
#ifdef CONFIG_DEBUG_LOCK_ALLOC
static struct lock_class_key rcu_lock_key;
diff --git a/kernel/rcutiny_plugin.h b/kernel/rcutiny_plugin.h
index 5a0f324..977250f 100644
--- a/kernel/rcutiny_plugin.h
+++ b/kernel/rcutiny_plugin.h
@@ -854,22 +854,6 @@
return rcu_preempt_ctrlblk.rcb.rcucblist != NULL;
}
-/*
- * Check for a task exiting while in a preemptible -RCU read-side
- * critical section, clean up if so. No need to issue warnings,
- * as debug_check_no_locks_held() already does this if lockdep
- * is enabled.
- */
-void exit_rcu(void)
-{
- struct task_struct *t = current;
-
- if (t->rcu_read_lock_nesting == 0)
- return;
- t->rcu_read_lock_nesting = 1;
- __rcu_read_unlock();
-}
-
#else /* #ifdef CONFIG_TINY_PREEMPT_RCU */
#ifdef CONFIG_RCU_TRACE
diff --git a/kernel/rcutree.c b/kernel/rcutree.c
index e269782..aaf798b 100644
--- a/kernel/rcutree.c
+++ b/kernel/rcutree.c
@@ -75,6 +75,8 @@
.gpnum = -300, \
.completed = -300, \
.onofflock = __RAW_SPIN_LOCK_UNLOCKED(&structname##_state.onofflock), \
+ .orphan_nxttail = &structname##_state.orphan_nxtlist, \
+ .orphan_donetail = &structname##_state.orphan_donelist, \
.fqslock = __RAW_SPIN_LOCK_UNLOCKED(&structname##_state.fqslock), \
.n_force_qs = 0, \
.n_force_qs_ngp = 0, \
@@ -145,6 +147,13 @@
unsigned long rcutorture_testseq;
unsigned long rcutorture_vernum;
+/* State information for rcu_barrier() and friends. */
+
+static DEFINE_PER_CPU(struct rcu_head, rcu_barrier_head) = {NULL};
+static atomic_t rcu_barrier_cpu_count;
+static DEFINE_MUTEX(rcu_barrier_mutex);
+static struct completion rcu_barrier_completion;
+
/*
* Return true if an RCU grace period is in progress. The ACCESS_ONCE()s
* permit this function to be invoked without holding the root rcu_node
@@ -1313,95 +1322,133 @@
#ifdef CONFIG_HOTPLUG_CPU
/*
- * Move a dying CPU's RCU callbacks to online CPU's callback list.
- * Also record a quiescent state for this CPU for the current grace period.
- * Synchronization and interrupt disabling are not required because
- * this function executes in stop_machine() context. Therefore, cleanup
- * operations that might block must be done later from the CPU_DEAD
- * notifier.
- *
- * Note that the outgoing CPU's bit has already been cleared in the
- * cpu_online_mask. This allows us to randomly pick a callback
- * destination from the bits set in that mask.
+ * Send the specified CPU's RCU callbacks to the orphanage. The
+ * specified CPU must be offline, and the caller must hold the
+ * ->onofflock.
*/
-static void rcu_cleanup_dying_cpu(struct rcu_state *rsp)
+static void
+rcu_send_cbs_to_orphanage(int cpu, struct rcu_state *rsp,
+ struct rcu_node *rnp, struct rcu_data *rdp)
{
int i;
- unsigned long mask;
- int receive_cpu = cpumask_any(cpu_online_mask);
- struct rcu_data *rdp = this_cpu_ptr(rsp->rda);
- struct rcu_data *receive_rdp = per_cpu_ptr(rsp->rda, receive_cpu);
- RCU_TRACE(struct rcu_node *rnp = rdp->mynode); /* For dying CPU. */
- /* First, adjust the counts. */
+ /*
+ * Orphan the callbacks. First adjust the counts. This is safe
+ * because ->onofflock excludes _rcu_barrier()'s adoption of
+ * the callbacks, thus no memory barrier is required.
+ */
if (rdp->nxtlist != NULL) {
- receive_rdp->qlen_lazy += rdp->qlen_lazy;
- receive_rdp->qlen += rdp->qlen;
+ rsp->qlen_lazy += rdp->qlen_lazy;
+ rsp->qlen += rdp->qlen;
+ rdp->n_cbs_orphaned += rdp->qlen;
rdp->qlen_lazy = 0;
rdp->qlen = 0;
}
/*
- * Next, move ready-to-invoke callbacks to be invoked on some
- * other CPU. These will not be required to pass through another
- * grace period: They are done, regardless of CPU.
+ * Next, move those callbacks still needing a grace period to
+ * the orphanage, where some other CPU will pick them up.
+ * Some of the callbacks might have gone partway through a grace
+ * period, but that is too bad. They get to start over because we
+ * cannot assume that grace periods are synchronized across CPUs.
+ * We don't bother updating the ->nxttail[] array yet, instead
+ * we just reset the whole thing later on.
*/
- if (rdp->nxtlist != NULL &&
- rdp->nxttail[RCU_DONE_TAIL] != &rdp->nxtlist) {
- struct rcu_head *oldhead;
- struct rcu_head **oldtail;
- struct rcu_head **newtail;
-
- oldhead = rdp->nxtlist;
- oldtail = receive_rdp->nxttail[RCU_DONE_TAIL];
- rdp->nxtlist = *rdp->nxttail[RCU_DONE_TAIL];
- *rdp->nxttail[RCU_DONE_TAIL] = *oldtail;
- *receive_rdp->nxttail[RCU_DONE_TAIL] = oldhead;
- newtail = rdp->nxttail[RCU_DONE_TAIL];
- for (i = RCU_DONE_TAIL; i < RCU_NEXT_SIZE; i++) {
- if (receive_rdp->nxttail[i] == oldtail)
- receive_rdp->nxttail[i] = newtail;
- if (rdp->nxttail[i] == newtail)
- rdp->nxttail[i] = &rdp->nxtlist;
- }
+ if (*rdp->nxttail[RCU_DONE_TAIL] != NULL) {
+ *rsp->orphan_nxttail = *rdp->nxttail[RCU_DONE_TAIL];
+ rsp->orphan_nxttail = rdp->nxttail[RCU_NEXT_TAIL];
+ *rdp->nxttail[RCU_DONE_TAIL] = NULL;
}
/*
- * Finally, put the rest of the callbacks at the end of the list.
- * The ones that made it partway through get to start over: We
- * cannot assume that grace periods are synchronized across CPUs.
- * (We could splice RCU_WAIT_TAIL into RCU_NEXT_READY_TAIL, but
- * this does not seem compelling. Not yet, anyway.)
+ * Then move the ready-to-invoke callbacks to the orphanage,
+ * where some other CPU will pick them up. These will not be
+ * required to pass though another grace period: They are done.
*/
if (rdp->nxtlist != NULL) {
- *receive_rdp->nxttail[RCU_NEXT_TAIL] = rdp->nxtlist;
- receive_rdp->nxttail[RCU_NEXT_TAIL] =
- rdp->nxttail[RCU_NEXT_TAIL];
- receive_rdp->n_cbs_adopted += rdp->qlen;
- rdp->n_cbs_orphaned += rdp->qlen;
-
- rdp->nxtlist = NULL;
- for (i = 0; i < RCU_NEXT_SIZE; i++)
- rdp->nxttail[i] = &rdp->nxtlist;
+ *rsp->orphan_donetail = rdp->nxtlist;
+ rsp->orphan_donetail = rdp->nxttail[RCU_DONE_TAIL];
}
+ /* Finally, initialize the rcu_data structure's list to empty. */
+ rdp->nxtlist = NULL;
+ for (i = 0; i < RCU_NEXT_SIZE; i++)
+ rdp->nxttail[i] = &rdp->nxtlist;
+}
+
+/*
+ * Adopt the RCU callbacks from the specified rcu_state structure's
+ * orphanage. The caller must hold the ->onofflock.
+ */
+static void rcu_adopt_orphan_cbs(struct rcu_state *rsp)
+{
+ int i;
+ struct rcu_data *rdp = __this_cpu_ptr(rsp->rda);
+
/*
- * Record a quiescent state for the dying CPU. This is safe
- * only because we have already cleared out the callbacks.
- * (Otherwise, the RCU core might try to schedule the invocation
- * of callbacks on this now-offline CPU, which would be bad.)
+ * If there is an rcu_barrier() operation in progress, then
+ * only the task doing that operation is permitted to adopt
+ * callbacks. To do otherwise breaks rcu_barrier() and friends
+ * by causing them to fail to wait for the callbacks in the
+ * orphanage.
*/
- mask = rdp->grpmask; /* rnp->grplo is constant. */
+ if (rsp->rcu_barrier_in_progress &&
+ rsp->rcu_barrier_in_progress != current)
+ return;
+
+ /* Do the accounting first. */
+ rdp->qlen_lazy += rsp->qlen_lazy;
+ rdp->qlen += rsp->qlen;
+ rdp->n_cbs_adopted += rsp->qlen;
+ rsp->qlen_lazy = 0;
+ rsp->qlen = 0;
+
+ /*
+ * We do not need a memory barrier here because the only way we
+ * can get here if there is an rcu_barrier() in flight is if
+ * we are the task doing the rcu_barrier().
+ */
+
+ /* First adopt the ready-to-invoke callbacks. */
+ if (rsp->orphan_donelist != NULL) {
+ *rsp->orphan_donetail = *rdp->nxttail[RCU_DONE_TAIL];
+ *rdp->nxttail[RCU_DONE_TAIL] = rsp->orphan_donelist;
+ for (i = RCU_NEXT_SIZE - 1; i >= RCU_DONE_TAIL; i--)
+ if (rdp->nxttail[i] == rdp->nxttail[RCU_DONE_TAIL])
+ rdp->nxttail[i] = rsp->orphan_donetail;
+ rsp->orphan_donelist = NULL;
+ rsp->orphan_donetail = &rsp->orphan_donelist;
+ }
+
+ /* And then adopt the callbacks that still need a grace period. */
+ if (rsp->orphan_nxtlist != NULL) {
+ *rdp->nxttail[RCU_NEXT_TAIL] = rsp->orphan_nxtlist;
+ rdp->nxttail[RCU_NEXT_TAIL] = rsp->orphan_nxttail;
+ rsp->orphan_nxtlist = NULL;
+ rsp->orphan_nxttail = &rsp->orphan_nxtlist;
+ }
+}
+
+/*
+ * Trace the fact that this CPU is going offline.
+ */
+static void rcu_cleanup_dying_cpu(struct rcu_state *rsp)
+{
+ RCU_TRACE(unsigned long mask);
+ RCU_TRACE(struct rcu_data *rdp = this_cpu_ptr(rsp->rda));
+ RCU_TRACE(struct rcu_node *rnp = rdp->mynode);
+
+ RCU_TRACE(mask = rdp->grpmask);
trace_rcu_grace_period(rsp->name,
rnp->gpnum + 1 - !!(rnp->qsmask & mask),
"cpuofl");
- rcu_report_qs_rdp(smp_processor_id(), rsp, rdp, rsp->gpnum);
- /* Note that rcu_report_qs_rdp() might call trace_rcu_grace_period(). */
}
/*
* The CPU has been completely removed, and some other CPU is reporting
- * this fact from process context. Do the remainder of the cleanup.
+ * this fact from process context. Do the remainder of the cleanup,
+ * including orphaning the outgoing CPU's RCU callbacks, and also
+ * adopting them, if there is no _rcu_barrier() instance running.
* There can only be one CPU hotplug operation at a time, so no other
* CPU can be attempting to update rcu_cpu_kthread_task.
*/
@@ -1411,17 +1458,21 @@
unsigned long mask;
int need_report = 0;
struct rcu_data *rdp = per_cpu_ptr(rsp->rda, cpu);
- struct rcu_node *rnp = rdp->mynode; /* Outgoing CPU's rnp. */
+ struct rcu_node *rnp = rdp->mynode; /* Outgoing CPU's rdp & rnp. */
/* Adjust any no-longer-needed kthreads. */
rcu_stop_cpu_kthread(cpu);
rcu_node_kthread_setaffinity(rnp, -1);
- /* Remove the dying CPU from the bitmasks in the rcu_node hierarchy. */
+ /* Remove the dead CPU from the bitmasks in the rcu_node hierarchy. */
/* Exclude any attempts to start a new grace period. */
raw_spin_lock_irqsave(&rsp->onofflock, flags);
+ /* Orphan the dead CPU's callbacks, and adopt them if appropriate. */
+ rcu_send_cbs_to_orphanage(cpu, rsp, rnp, rdp);
+ rcu_adopt_orphan_cbs(rsp);
+
/* Remove the outgoing CPU from the masks in the rcu_node hierarchy. */
mask = rdp->grpmask; /* rnp->grplo is constant. */
do {
@@ -1458,6 +1509,10 @@
#else /* #ifdef CONFIG_HOTPLUG_CPU */
+static void rcu_adopt_orphan_cbs(struct rcu_state *rsp)
+{
+}
+
static void rcu_cleanup_dying_cpu(struct rcu_state *rsp)
{
}
@@ -1476,7 +1531,7 @@
{
unsigned long flags;
struct rcu_head *next, *list, **tail;
- int bl, count, count_lazy;
+ int bl, count, count_lazy, i;
/* If no callbacks are ready, just return.*/
if (!cpu_has_callbacks_ready_to_invoke(rdp)) {
@@ -1499,9 +1554,9 @@
rdp->nxtlist = *rdp->nxttail[RCU_DONE_TAIL];
*rdp->nxttail[RCU_DONE_TAIL] = NULL;
tail = rdp->nxttail[RCU_DONE_TAIL];
- for (count = RCU_NEXT_SIZE - 1; count >= 0; count--)
- if (rdp->nxttail[count] == rdp->nxttail[RCU_DONE_TAIL])
- rdp->nxttail[count] = &rdp->nxtlist;
+ for (i = RCU_NEXT_SIZE - 1; i >= 0; i--)
+ if (rdp->nxttail[i] == rdp->nxttail[RCU_DONE_TAIL])
+ rdp->nxttail[i] = &rdp->nxtlist;
local_irq_restore(flags);
/* Invoke callbacks. */
@@ -1526,18 +1581,19 @@
rcu_is_callbacks_kthread());
/* Update count, and requeue any remaining callbacks. */
- rdp->qlen_lazy -= count_lazy;
- rdp->qlen -= count;
- rdp->n_cbs_invoked += count;
if (list != NULL) {
*tail = rdp->nxtlist;
rdp->nxtlist = list;
- for (count = 0; count < RCU_NEXT_SIZE; count++)
- if (&rdp->nxtlist == rdp->nxttail[count])
- rdp->nxttail[count] = tail;
+ for (i = 0; i < RCU_NEXT_SIZE; i++)
+ if (&rdp->nxtlist == rdp->nxttail[i])
+ rdp->nxttail[i] = tail;
else
break;
}
+ smp_mb(); /* List handling before counting for rcu_barrier(). */
+ rdp->qlen_lazy -= count_lazy;
+ rdp->qlen -= count;
+ rdp->n_cbs_invoked += count;
/* Reinstate batch limit if we have worked down the excess. */
if (rdp->blimit == LONG_MAX && rdp->qlen <= qlowmark)
@@ -1825,11 +1881,14 @@
rdp = this_cpu_ptr(rsp->rda);
/* Add the callback to our list. */
- *rdp->nxttail[RCU_NEXT_TAIL] = head;
- rdp->nxttail[RCU_NEXT_TAIL] = &head->next;
rdp->qlen++;
if (lazy)
rdp->qlen_lazy++;
+ else
+ rcu_idle_count_callbacks_posted();
+ smp_mb(); /* Count before adding callback for rcu_barrier(). */
+ *rdp->nxttail[RCU_NEXT_TAIL] = head;
+ rdp->nxttail[RCU_NEXT_TAIL] = &head->next;
if (__is_kfree_rcu_offset((unsigned long)func))
trace_rcu_kfree_callback(rsp->name, head, (unsigned long)func,
@@ -2174,11 +2233,10 @@
rcu_preempt_cpu_has_callbacks(cpu);
}
-static DEFINE_PER_CPU(struct rcu_head, rcu_barrier_head) = {NULL};
-static atomic_t rcu_barrier_cpu_count;
-static DEFINE_MUTEX(rcu_barrier_mutex);
-static struct completion rcu_barrier_completion;
-
+/*
+ * RCU callback function for _rcu_barrier(). If we are last, wake
+ * up the task executing _rcu_barrier().
+ */
static void rcu_barrier_callback(struct rcu_head *notused)
{
if (atomic_dec_and_test(&rcu_barrier_cpu_count))
@@ -2208,27 +2266,94 @@
void (*call_rcu_func)(struct rcu_head *head,
void (*func)(struct rcu_head *head)))
{
- BUG_ON(in_interrupt());
+ int cpu;
+ unsigned long flags;
+ struct rcu_data *rdp;
+ struct rcu_head rh;
+
+ init_rcu_head_on_stack(&rh);
+
/* Take mutex to serialize concurrent rcu_barrier() requests. */
mutex_lock(&rcu_barrier_mutex);
- init_completion(&rcu_barrier_completion);
+
+ smp_mb(); /* Prevent any prior operations from leaking in. */
+
/*
- * Initialize rcu_barrier_cpu_count to 1, then invoke
- * rcu_barrier_func() on each CPU, so that each CPU also has
- * incremented rcu_barrier_cpu_count. Only then is it safe to
- * decrement rcu_barrier_cpu_count -- otherwise the first CPU
- * might complete its grace period before all of the other CPUs
- * did their increment, causing this function to return too
- * early. Note that on_each_cpu() disables irqs, which prevents
- * any CPUs from coming online or going offline until each online
- * CPU has queued its RCU-barrier callback.
+ * Initialize the count to one rather than to zero in order to
+ * avoid a too-soon return to zero in case of a short grace period
+ * (or preemption of this task). Also flag this task as doing
+ * an rcu_barrier(). This will prevent anyone else from adopting
+ * orphaned callbacks, which could cause otherwise failure if a
+ * CPU went offline and quickly came back online. To see this,
+ * consider the following sequence of events:
+ *
+ * 1. We cause CPU 0 to post an rcu_barrier_callback() callback.
+ * 2. CPU 1 goes offline, orphaning its callbacks.
+ * 3. CPU 0 adopts CPU 1's orphaned callbacks.
+ * 4. CPU 1 comes back online.
+ * 5. We cause CPU 1 to post an rcu_barrier_callback() callback.
+ * 6. Both rcu_barrier_callback() callbacks are invoked, awakening
+ * us -- but before CPU 1's orphaned callbacks are invoked!!!
*/
+ init_completion(&rcu_barrier_completion);
atomic_set(&rcu_barrier_cpu_count, 1);
- on_each_cpu(rcu_barrier_func, (void *)call_rcu_func, 1);
+ raw_spin_lock_irqsave(&rsp->onofflock, flags);
+ rsp->rcu_barrier_in_progress = current;
+ raw_spin_unlock_irqrestore(&rsp->onofflock, flags);
+
+ /*
+ * Force every CPU with callbacks to register a new callback
+ * that will tell us when all the preceding callbacks have
+ * been invoked. If an offline CPU has callbacks, wait for
+ * it to either come back online or to finish orphaning those
+ * callbacks.
+ */
+ for_each_possible_cpu(cpu) {
+ preempt_disable();
+ rdp = per_cpu_ptr(rsp->rda, cpu);
+ if (cpu_is_offline(cpu)) {
+ preempt_enable();
+ while (cpu_is_offline(cpu) && ACCESS_ONCE(rdp->qlen))
+ schedule_timeout_interruptible(1);
+ } else if (ACCESS_ONCE(rdp->qlen)) {
+ smp_call_function_single(cpu, rcu_barrier_func,
+ (void *)call_rcu_func, 1);
+ preempt_enable();
+ } else {
+ preempt_enable();
+ }
+ }
+
+ /*
+ * Now that all online CPUs have rcu_barrier_callback() callbacks
+ * posted, we can adopt all of the orphaned callbacks and place
+ * an rcu_barrier_callback() callback after them. When that is done,
+ * we are guaranteed to have an rcu_barrier_callback() callback
+ * following every callback that could possibly have been
+ * registered before _rcu_barrier() was called.
+ */
+ raw_spin_lock_irqsave(&rsp->onofflock, flags);
+ rcu_adopt_orphan_cbs(rsp);
+ rsp->rcu_barrier_in_progress = NULL;
+ raw_spin_unlock_irqrestore(&rsp->onofflock, flags);
+ atomic_inc(&rcu_barrier_cpu_count);
+ smp_mb__after_atomic_inc(); /* Ensure atomic_inc() before callback. */
+ call_rcu_func(&rh, rcu_barrier_callback);
+
+ /*
+ * Now that we have an rcu_barrier_callback() callback on each
+ * CPU, and thus each counted, remove the initial count.
+ */
if (atomic_dec_and_test(&rcu_barrier_cpu_count))
complete(&rcu_barrier_completion);
+
+ /* Wait for all rcu_barrier_callback() callbacks to be invoked. */
wait_for_completion(&rcu_barrier_completion);
+
+ /* Other rcu_barrier() invocations can now safely proceed. */
mutex_unlock(&rcu_barrier_mutex);
+
+ destroy_rcu_head_on_stack(&rh);
}
/**
diff --git a/kernel/rcutree.h b/kernel/rcutree.h
index cdd1be0..aa5676b 100644
--- a/kernel/rcutree.h
+++ b/kernel/rcutree.h
@@ -88,6 +88,20 @@
/* Process level is worth LLONG_MAX/2. */
int dynticks_nmi_nesting; /* Track NMI nesting level. */
atomic_t dynticks; /* Even value for idle, else odd. */
+#ifdef CONFIG_RCU_FAST_NO_HZ
+ int dyntick_drain; /* Prepare-for-idle state variable. */
+ unsigned long dyntick_holdoff;
+ /* No retries for the jiffy of failure. */
+ struct timer_list idle_gp_timer;
+ /* Wake up CPU sleeping with callbacks. */
+ unsigned long idle_gp_timer_expires;
+ /* When to wake up CPU (for repost). */
+ bool idle_first_pass; /* First pass of attempt to go idle? */
+ unsigned long nonlazy_posted;
+ /* # times non-lazy CBs posted to CPU. */
+ unsigned long nonlazy_posted_snap;
+ /* idle-period nonlazy_posted snapshot. */
+#endif /* #ifdef CONFIG_RCU_FAST_NO_HZ */
};
/* RCU's kthread states for tracing. */
@@ -371,6 +385,17 @@
raw_spinlock_t onofflock; /* exclude on/offline and */
/* starting new GP. */
+ struct rcu_head *orphan_nxtlist; /* Orphaned callbacks that */
+ /* need a grace period. */
+ struct rcu_head **orphan_nxttail; /* Tail of above. */
+ struct rcu_head *orphan_donelist; /* Orphaned callbacks that */
+ /* are ready to invoke. */
+ struct rcu_head **orphan_donetail; /* Tail of above. */
+ long qlen_lazy; /* Number of lazy callbacks. */
+ long qlen; /* Total number of callbacks. */
+ struct task_struct *rcu_barrier_in_progress;
+ /* Task doing rcu_barrier(), */
+ /* or NULL if no barrier. */
raw_spinlock_t fqslock; /* Only one task forcing */
/* quiescent states. */
unsigned long jiffies_force_qs; /* Time at which to invoke */
@@ -471,6 +496,7 @@
static void rcu_prepare_for_idle_init(int cpu);
static void rcu_cleanup_after_idle(int cpu);
static void rcu_prepare_for_idle(int cpu);
+static void rcu_idle_count_callbacks_posted(void);
static void print_cpu_stall_info_begin(void);
static void print_cpu_stall_info(struct rcu_state *rsp, int cpu);
static void print_cpu_stall_info_end(void);
diff --git a/kernel/rcutree_plugin.h b/kernel/rcutree_plugin.h
index beafb9c..9e501bd 100644
--- a/kernel/rcutree_plugin.h
+++ b/kernel/rcutree_plugin.h
@@ -972,22 +972,6 @@
rcu_init_one(&rcu_preempt_state, &rcu_preempt_data);
}
-/*
- * Check for a task exiting while in a preemptible-RCU read-side
- * critical section, clean up if so. No need to issue warnings,
- * as debug_check_no_locks_held() already does this if lockdep
- * is enabled.
- */
-void exit_rcu(void)
-{
- struct task_struct *t = current;
-
- if (t->rcu_read_lock_nesting == 0)
- return;
- t->rcu_read_lock_nesting = 1;
- __rcu_read_unlock();
-}
-
#else /* #ifdef CONFIG_TREE_PREEMPT_RCU */
static struct rcu_state *rcu_state = &rcu_sched_state;
@@ -1913,8 +1897,9 @@
* Because we not have RCU_FAST_NO_HZ, just check whether this CPU needs
* any flavor of RCU.
*/
-int rcu_needs_cpu(int cpu)
+int rcu_needs_cpu(int cpu, unsigned long *delta_jiffies)
{
+ *delta_jiffies = ULONG_MAX;
return rcu_cpu_has_callbacks(cpu);
}
@@ -1941,6 +1926,14 @@
{
}
+/*
+ * Don't bother keeping a running count of the number of RCU callbacks
+ * posted because CONFIG_RCU_FAST_NO_HZ=n.
+ */
+static void rcu_idle_count_callbacks_posted(void)
+{
+}
+
#else /* #if !defined(CONFIG_RCU_FAST_NO_HZ) */
/*
@@ -1981,30 +1974,6 @@
#define RCU_IDLE_GP_DELAY 6 /* Roughly one grace period. */
#define RCU_IDLE_LAZY_GP_DELAY (6 * HZ) /* Roughly six seconds. */
-static DEFINE_PER_CPU(int, rcu_dyntick_drain);
-static DEFINE_PER_CPU(unsigned long, rcu_dyntick_holdoff);
-static DEFINE_PER_CPU(struct hrtimer, rcu_idle_gp_timer);
-static ktime_t rcu_idle_gp_wait; /* If some non-lazy callbacks. */
-static ktime_t rcu_idle_lazy_gp_wait; /* If only lazy callbacks. */
-
-/*
- * Allow the CPU to enter dyntick-idle mode if either: (1) There are no
- * callbacks on this CPU, (2) this CPU has not yet attempted to enter
- * dyntick-idle mode, or (3) this CPU is in the process of attempting to
- * enter dyntick-idle mode. Otherwise, if we have recently tried and failed
- * to enter dyntick-idle mode, we refuse to try to enter it. After all,
- * it is better to incur scheduling-clock interrupts than to spin
- * continuously for the same time duration!
- */
-int rcu_needs_cpu(int cpu)
-{
- /* If no callbacks, RCU doesn't need the CPU. */
- if (!rcu_cpu_has_callbacks(cpu))
- return 0;
- /* Otherwise, RCU needs the CPU only if it recently tried and failed. */
- return per_cpu(rcu_dyntick_holdoff, cpu) == jiffies;
-}
-
/*
* Does the specified flavor of RCU have non-lazy callbacks pending on
* the specified CPU? Both RCU flavor and CPU are specified by the
@@ -2048,16 +2017,75 @@
}
/*
+ * Allow the CPU to enter dyntick-idle mode if either: (1) There are no
+ * callbacks on this CPU, (2) this CPU has not yet attempted to enter
+ * dyntick-idle mode, or (3) this CPU is in the process of attempting to
+ * enter dyntick-idle mode. Otherwise, if we have recently tried and failed
+ * to enter dyntick-idle mode, we refuse to try to enter it. After all,
+ * it is better to incur scheduling-clock interrupts than to spin
+ * continuously for the same time duration!
+ *
+ * The delta_jiffies argument is used to store the time when RCU is
+ * going to need the CPU again if it still has callbacks. The reason
+ * for this is that rcu_prepare_for_idle() might need to post a timer,
+ * but if so, it will do so after tick_nohz_stop_sched_tick() has set
+ * the wakeup time for this CPU. This means that RCU's timer can be
+ * delayed until the wakeup time, which defeats the purpose of posting
+ * a timer.
+ */
+int rcu_needs_cpu(int cpu, unsigned long *delta_jiffies)
+{
+ struct rcu_dynticks *rdtp = &per_cpu(rcu_dynticks, cpu);
+
+ /* Flag a new idle sojourn to the idle-entry state machine. */
+ rdtp->idle_first_pass = 1;
+ /* If no callbacks, RCU doesn't need the CPU. */
+ if (!rcu_cpu_has_callbacks(cpu)) {
+ *delta_jiffies = ULONG_MAX;
+ return 0;
+ }
+ if (rdtp->dyntick_holdoff == jiffies) {
+ /* RCU recently tried and failed, so don't try again. */
+ *delta_jiffies = 1;
+ return 1;
+ }
+ /* Set up for the possibility that RCU will post a timer. */
+ if (rcu_cpu_has_nonlazy_callbacks(cpu))
+ *delta_jiffies = RCU_IDLE_GP_DELAY;
+ else
+ *delta_jiffies = RCU_IDLE_LAZY_GP_DELAY;
+ return 0;
+}
+
+/*
+ * Handler for smp_call_function_single(). The only point of this
+ * handler is to wake the CPU up, so the handler does only tracing.
+ */
+void rcu_idle_demigrate(void *unused)
+{
+ trace_rcu_prep_idle("Demigrate");
+}
+
+/*
* Timer handler used to force CPU to start pushing its remaining RCU
* callbacks in the case where it entered dyntick-idle mode with callbacks
* pending. The hander doesn't really need to do anything because the
* real work is done upon re-entry to idle, or by the next scheduling-clock
* interrupt should idle not be re-entered.
+ *
+ * One special case: the timer gets migrated without awakening the CPU
+ * on which the timer was scheduled on. In this case, we must wake up
+ * that CPU. We do so with smp_call_function_single().
*/
-static enum hrtimer_restart rcu_idle_gp_timer_func(struct hrtimer *hrtp)
+static void rcu_idle_gp_timer_func(unsigned long cpu_in)
{
+ int cpu = (int)cpu_in;
+
trace_rcu_prep_idle("Timer");
- return HRTIMER_NORESTART;
+ if (cpu != smp_processor_id())
+ smp_call_function_single(cpu, rcu_idle_demigrate, NULL, 0);
+ else
+ WARN_ON_ONCE(1); /* Getting here can hang the system... */
}
/*
@@ -2065,29 +2093,25 @@
*/
static void rcu_prepare_for_idle_init(int cpu)
{
- static int firsttime = 1;
- struct hrtimer *hrtp = &per_cpu(rcu_idle_gp_timer, cpu);
+ struct rcu_dynticks *rdtp = &per_cpu(rcu_dynticks, cpu);
- hrtimer_init(hrtp, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- hrtp->function = rcu_idle_gp_timer_func;
- if (firsttime) {
- unsigned int upj = jiffies_to_usecs(RCU_IDLE_GP_DELAY);
-
- rcu_idle_gp_wait = ns_to_ktime(upj * (u64)1000);
- upj = jiffies_to_usecs(RCU_IDLE_LAZY_GP_DELAY);
- rcu_idle_lazy_gp_wait = ns_to_ktime(upj * (u64)1000);
- firsttime = 0;
- }
+ rdtp->dyntick_holdoff = jiffies - 1;
+ setup_timer(&rdtp->idle_gp_timer, rcu_idle_gp_timer_func, cpu);
+ rdtp->idle_gp_timer_expires = jiffies - 1;
+ rdtp->idle_first_pass = 1;
}
/*
* Clean up for exit from idle. Because we are exiting from idle, there
- * is no longer any point to rcu_idle_gp_timer, so cancel it. This will
+ * is no longer any point to ->idle_gp_timer, so cancel it. This will
* do nothing if this timer is not active, so just cancel it unconditionally.
*/
static void rcu_cleanup_after_idle(int cpu)
{
- hrtimer_cancel(&per_cpu(rcu_idle_gp_timer, cpu));
+ struct rcu_dynticks *rdtp = &per_cpu(rcu_dynticks, cpu);
+
+ del_timer(&rdtp->idle_gp_timer);
+ trace_rcu_prep_idle("Cleanup after idle");
}
/*
@@ -2105,19 +2129,41 @@
* Because it is not legal to invoke rcu_process_callbacks() with irqs
* disabled, we do one pass of force_quiescent_state(), then do a
* invoke_rcu_core() to cause rcu_process_callbacks() to be invoked
- * later. The per-cpu rcu_dyntick_drain variable controls the sequencing.
+ * later. The ->dyntick_drain field controls the sequencing.
*
* The caller must have disabled interrupts.
*/
static void rcu_prepare_for_idle(int cpu)
{
+ struct timer_list *tp;
+ struct rcu_dynticks *rdtp = &per_cpu(rcu_dynticks, cpu);
+
+ /*
+ * If this is an idle re-entry, for example, due to use of
+ * RCU_NONIDLE() or the new idle-loop tracing API within the idle
+ * loop, then don't take any state-machine actions, unless the
+ * momentary exit from idle queued additional non-lazy callbacks.
+ * Instead, repost the ->idle_gp_timer if this CPU has callbacks
+ * pending.
+ */
+ if (!rdtp->idle_first_pass &&
+ (rdtp->nonlazy_posted == rdtp->nonlazy_posted_snap)) {
+ if (rcu_cpu_has_callbacks(cpu)) {
+ tp = &rdtp->idle_gp_timer;
+ mod_timer_pinned(tp, rdtp->idle_gp_timer_expires);
+ }
+ return;
+ }
+ rdtp->idle_first_pass = 0;
+ rdtp->nonlazy_posted_snap = rdtp->nonlazy_posted - 1;
+
/*
* If there are no callbacks on this CPU, enter dyntick-idle mode.
* Also reset state to avoid prejudicing later attempts.
*/
if (!rcu_cpu_has_callbacks(cpu)) {
- per_cpu(rcu_dyntick_holdoff, cpu) = jiffies - 1;
- per_cpu(rcu_dyntick_drain, cpu) = 0;
+ rdtp->dyntick_holdoff = jiffies - 1;
+ rdtp->dyntick_drain = 0;
trace_rcu_prep_idle("No callbacks");
return;
}
@@ -2126,32 +2172,37 @@
* If in holdoff mode, just return. We will presumably have
* refrained from disabling the scheduling-clock tick.
*/
- if (per_cpu(rcu_dyntick_holdoff, cpu) == jiffies) {
+ if (rdtp->dyntick_holdoff == jiffies) {
trace_rcu_prep_idle("In holdoff");
return;
}
- /* Check and update the rcu_dyntick_drain sequencing. */
- if (per_cpu(rcu_dyntick_drain, cpu) <= 0) {
+ /* Check and update the ->dyntick_drain sequencing. */
+ if (rdtp->dyntick_drain <= 0) {
/* First time through, initialize the counter. */
- per_cpu(rcu_dyntick_drain, cpu) = RCU_IDLE_FLUSHES;
- } else if (per_cpu(rcu_dyntick_drain, cpu) <= RCU_IDLE_OPT_FLUSHES &&
+ rdtp->dyntick_drain = RCU_IDLE_FLUSHES;
+ } else if (rdtp->dyntick_drain <= RCU_IDLE_OPT_FLUSHES &&
!rcu_pending(cpu) &&
!local_softirq_pending()) {
/* Can we go dyntick-idle despite still having callbacks? */
- trace_rcu_prep_idle("Dyntick with callbacks");
- per_cpu(rcu_dyntick_drain, cpu) = 0;
- per_cpu(rcu_dyntick_holdoff, cpu) = jiffies;
- if (rcu_cpu_has_nonlazy_callbacks(cpu))
- hrtimer_start(&per_cpu(rcu_idle_gp_timer, cpu),
- rcu_idle_gp_wait, HRTIMER_MODE_REL);
- else
- hrtimer_start(&per_cpu(rcu_idle_gp_timer, cpu),
- rcu_idle_lazy_gp_wait, HRTIMER_MODE_REL);
+ rdtp->dyntick_drain = 0;
+ rdtp->dyntick_holdoff = jiffies;
+ if (rcu_cpu_has_nonlazy_callbacks(cpu)) {
+ trace_rcu_prep_idle("Dyntick with callbacks");
+ rdtp->idle_gp_timer_expires =
+ jiffies + RCU_IDLE_GP_DELAY;
+ } else {
+ rdtp->idle_gp_timer_expires =
+ jiffies + RCU_IDLE_LAZY_GP_DELAY;
+ trace_rcu_prep_idle("Dyntick with lazy callbacks");
+ }
+ tp = &rdtp->idle_gp_timer;
+ mod_timer_pinned(tp, rdtp->idle_gp_timer_expires);
+ rdtp->nonlazy_posted_snap = rdtp->nonlazy_posted;
return; /* Nothing more to do immediately. */
- } else if (--per_cpu(rcu_dyntick_drain, cpu) <= 0) {
+ } else if (--(rdtp->dyntick_drain) <= 0) {
/* We have hit the limit, so time to give up. */
- per_cpu(rcu_dyntick_holdoff, cpu) = jiffies;
+ rdtp->dyntick_holdoff = jiffies;
trace_rcu_prep_idle("Begin holdoff");
invoke_rcu_core(); /* Force the CPU out of dyntick-idle. */
return;
@@ -2187,6 +2238,19 @@
trace_rcu_prep_idle("Callbacks drained");
}
+/*
+ * Keep a running count of the number of non-lazy callbacks posted
+ * on this CPU. This running counter (which is never decremented) allows
+ * rcu_prepare_for_idle() to detect when something out of the idle loop
+ * posts a callback, even if an equal number of callbacks are invoked.
+ * Of course, callbacks should only be posted from within a trace event
+ * designed to be called from idle or from within RCU_NONIDLE().
+ */
+static void rcu_idle_count_callbacks_posted(void)
+{
+ __this_cpu_add(rcu_dynticks.nonlazy_posted, 1);
+}
+
#endif /* #else #if !defined(CONFIG_RCU_FAST_NO_HZ) */
#ifdef CONFIG_RCU_CPU_STALL_INFO
@@ -2195,14 +2259,13 @@
static void print_cpu_stall_fast_no_hz(char *cp, int cpu)
{
- struct hrtimer *hrtp = &per_cpu(rcu_idle_gp_timer, cpu);
+ struct rcu_dynticks *rdtp = &per_cpu(rcu_dynticks, cpu);
+ struct timer_list *tltp = &rdtp->idle_gp_timer;
- sprintf(cp, "drain=%d %c timer=%lld",
- per_cpu(rcu_dyntick_drain, cpu),
- per_cpu(rcu_dyntick_holdoff, cpu) == jiffies ? 'H' : '.',
- hrtimer_active(hrtp)
- ? ktime_to_us(hrtimer_get_remaining(hrtp))
- : -1);
+ sprintf(cp, "drain=%d %c timer=%lu",
+ rdtp->dyntick_drain,
+ rdtp->dyntick_holdoff == jiffies ? 'H' : '.',
+ timer_pending(tltp) ? tltp->expires - jiffies : -1);
}
#else /* #ifdef CONFIG_RCU_FAST_NO_HZ */
diff --git a/kernel/rcutree_trace.c b/kernel/rcutree_trace.c
index ed459ed..d4bc16d 100644
--- a/kernel/rcutree_trace.c
+++ b/kernel/rcutree_trace.c
@@ -271,13 +271,13 @@
gpnum = rsp->gpnum;
seq_printf(m, "c=%lu g=%lu s=%d jfq=%ld j=%x "
- "nfqs=%lu/nfqsng=%lu(%lu) fqlh=%lu\n",
+ "nfqs=%lu/nfqsng=%lu(%lu) fqlh=%lu oqlen=%ld/%ld\n",
rsp->completed, gpnum, rsp->fqs_state,
(long)(rsp->jiffies_force_qs - jiffies),
(int)(jiffies & 0xffff),
rsp->n_force_qs, rsp->n_force_qs_ngp,
rsp->n_force_qs - rsp->n_force_qs_ngp,
- rsp->n_force_qs_lh);
+ rsp->n_force_qs_lh, rsp->qlen_lazy, rsp->qlen);
for (rnp = &rsp->node[0]; rnp - &rsp->node[0] < NUM_RCU_NODES; rnp++) {
if (rnp->level != level) {
seq_puts(m, "\n");
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 56b2b86..faeb4b7 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -5507,7 +5507,6 @@
unsigned long action, void *hcpu)
{
switch (action & ~CPU_TASKS_FROZEN) {
- case CPU_STARTING:
case CPU_DOWN_FAILED:
set_cpu_active((long)hcpu, true);
return NOTIFY_OK;
diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c
index c6cd85b..0f30374 100644
--- a/kernel/time/tick-sched.c
+++ b/kernel/time/tick-sched.c
@@ -280,6 +280,7 @@
static void tick_nohz_stop_sched_tick(struct tick_sched *ts)
{
unsigned long seq, last_jiffies, next_jiffies, delta_jiffies;
+ unsigned long rcu_delta_jiffies;
ktime_t last_update, expires, now;
struct clock_event_device *dev = __get_cpu_var(tick_cpu_device).evtdev;
u64 time_delta;
@@ -328,7 +329,7 @@
time_delta = timekeeping_max_deferment();
} while (read_seqretry(&xtime_lock, seq));
- if (rcu_needs_cpu(cpu) || printk_needs_cpu(cpu) ||
+ if (rcu_needs_cpu(cpu, &rcu_delta_jiffies) || printk_needs_cpu(cpu) ||
arch_needs_cpu(cpu)) {
next_jiffies = last_jiffies + 1;
delta_jiffies = 1;
@@ -336,6 +337,10 @@
/* Get the next timer wheel timer */
next_jiffies = get_next_timer_interrupt(last_jiffies);
delta_jiffies = next_jiffies - last_jiffies;
+ if (rcu_delta_jiffies < delta_jiffies) {
+ next_jiffies = last_jiffies + rcu_delta_jiffies;
+ delta_jiffies = rcu_delta_jiffies;
+ }
}
/*
* Do not stop the tick, if we are only one off
diff --git a/kernel/timer.c b/kernel/timer.c
index cf7217a..a851025 100644
--- a/kernel/timer.c
+++ b/kernel/timer.c
@@ -862,7 +862,13 @@
*
* mod_timer_pinned() is a way to update the expire field of an
* active timer (if the timer is inactive it will be activated)
- * and not allow the timer to be migrated to a different CPU.
+ * and to ensure that the timer is scheduled on the current CPU.
+ *
+ * Note that this does not prevent the timer from being migrated
+ * when the current CPU goes offline. If this is a problem for
+ * you, use CPU-hotplug notifiers to handle it correctly, for
+ * example, cancelling the timer when the corresponding CPU goes
+ * offline.
*
* mod_timer_pinned(timer, expires) is equivalent to:
*
diff --git a/net/core/ethtool.c b/net/core/ethtool.c
index 6d6d7d2..452fa86 100644
--- a/net/core/ethtool.c
+++ b/net/core/ethtool.c
@@ -691,11 +691,13 @@
static int ethtool_get_wol(struct net_device *dev, char __user *useraddr)
{
- struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL };
+ struct ethtool_wolinfo wol;
if (!dev->ethtool_ops->get_wol)
return -EOPNOTSUPP;
+ memset(&wol, 0, sizeof(struct ethtool_wolinfo));
+ wol.cmd = ETHTOOL_GWOL;
dev->ethtool_ops->get_wol(dev, &wol);
if (copy_to_user(useraddr, &wol, sizeof(wol)))
diff --git a/sound/soc/codecs/wcd9xxx-mbhc.c b/sound/soc/codecs/wcd9xxx-mbhc.c
index 9bd1652..a49866c 100644
--- a/sound/soc/codecs/wcd9xxx-mbhc.c
+++ b/sound/soc/codecs/wcd9xxx-mbhc.c
@@ -845,9 +845,9 @@
if (mbhc->micbias_enable && mbhc->micbias_enable_cb) {
pr_debug("%s: Disabling micbias\n", __func__);
+ mbhc->micbias_enable = false;
mbhc->micbias_enable_cb(mbhc->codec, false,
mbhc->mbhc_cfg->micbias);
- mbhc->micbias_enable = false;
}
mbhc->zl = mbhc->zr = 0;
pr_debug("%s: Reporting removal %d(%x)\n", __func__,
@@ -872,9 +872,9 @@
if (mbhc->micbias_enable && mbhc->micbias_enable_cb &&
mbhc->hph_status == SND_JACK_HEADSET) {
pr_debug("%s: Disabling micbias\n", __func__);
+ mbhc->micbias_enable = false;
mbhc->micbias_enable_cb(mbhc->codec, false,
mbhc->mbhc_cfg->micbias);
- mbhc->micbias_enable = false;
}
pr_debug("%s: Reporting removal (%x)\n",