Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 05/10] mm: replace get_vaddr_frames() write/force parameters with gup_flags
From: Lorenzo Stoakes @ 2016-10-13  0:20 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161013002020.3062-1-lstoakes@gmail.com>

This patch removes the write and force parameters from get_vaddr_frames() and
replaces them with a gup_flags parameter to make the use of FOLL_FORCE explicit
in callers as use of this flag can result in surprising behaviour (and hence
bugs) within the mm subsystem.

Signed-off-by: Lorenzo Stoakes <lstoakes@gmail.com>
---
 drivers/gpu/drm/exynos/exynos_drm_g2d.c    |  3 ++-
 drivers/media/platform/omap/omap_vout.c    |  2 +-
 drivers/media/v4l2-core/videobuf2-memops.c |  6 +++++-
 include/linux/mm.h                         |  2 +-
 mm/frame_vector.c                          | 13 ++-----------
 5 files changed, 11 insertions(+), 15 deletions(-)

diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.c b/drivers/gpu/drm/exynos/exynos_drm_g2d.c
index aa92dec..fbd13fa 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_g2d.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.c
@@ -488,7 +488,8 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev,
 		goto err_free;
 	}
 
-	ret = get_vaddr_frames(start, npages, true, true, g2d_userptr->vec);
+	ret = get_vaddr_frames(start, npages, FOLL_FORCE | FOLL_WRITE,
+		g2d_userptr->vec);
 	if (ret != npages) {
 		DRM_ERROR("failed to get user pages from userptr.\n");
 		if (ret < 0)
diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c
index e668dde..a31b95c 100644
--- a/drivers/media/platform/omap/omap_vout.c
+++ b/drivers/media/platform/omap/omap_vout.c
@@ -214,7 +214,7 @@ static int omap_vout_get_userptr(struct videobuf_buffer *vb, u32 virtp,
 	if (!vec)
 		return -ENOMEM;
 
-	ret = get_vaddr_frames(virtp, 1, true, false, vec);
+	ret = get_vaddr_frames(virtp, 1, FOLL_WRITE, vec);
 	if (ret != 1) {
 		frame_vector_destroy(vec);
 		return -EINVAL;
diff --git a/drivers/media/v4l2-core/videobuf2-memops.c b/drivers/media/v4l2-core/videobuf2-memops.c
index 3c3b517..1cd322e 100644
--- a/drivers/media/v4l2-core/videobuf2-memops.c
+++ b/drivers/media/v4l2-core/videobuf2-memops.c
@@ -42,6 +42,10 @@ struct frame_vector *vb2_create_framevec(unsigned long start,
 	unsigned long first, last;
 	unsigned long nr;
 	struct frame_vector *vec;
+	unsigned int flags = FOLL_FORCE;
+
+	if (write)
+		flags |= FOLL_WRITE;
 
 	first = start >> PAGE_SHIFT;
 	last = (start + length - 1) >> PAGE_SHIFT;
@@ -49,7 +53,7 @@ struct frame_vector *vb2_create_framevec(unsigned long start,
 	vec = frame_vector_create(nr);
 	if (!vec)
 		return ERR_PTR(-ENOMEM);
-	ret = get_vaddr_frames(start & PAGE_MASK, nr, write, true, vec);
+	ret = get_vaddr_frames(start & PAGE_MASK, nr, flags, vec);
 	if (ret < 0)
 		goto out_destroy;
 	/* We accept only complete set of PFNs */
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 27ab538..5ff084f6 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -1305,7 +1305,7 @@ struct frame_vector {
 struct frame_vector *frame_vector_create(unsigned int nr_frames);
 void frame_vector_destroy(struct frame_vector *vec);
 int get_vaddr_frames(unsigned long start, unsigned int nr_pfns,
-		     bool write, bool force, struct frame_vector *vec);
+		     unsigned int gup_flags, struct frame_vector *vec);
 void put_vaddr_frames(struct frame_vector *vec);
 int frame_vector_to_pages(struct frame_vector *vec);
 void frame_vector_to_pfns(struct frame_vector *vec);
diff --git a/mm/frame_vector.c b/mm/frame_vector.c
index 81b6749..db77dcb 100644
--- a/mm/frame_vector.c
+++ b/mm/frame_vector.c
@@ -11,10 +11,7 @@
  * get_vaddr_frames() - map virtual addresses to pfns
  * @start:	starting user address
  * @nr_frames:	number of pages / pfns from start to map
- * @write:	whether pages will be written to by the caller
- * @force:	whether to force write access even if user mapping is
- *		readonly. See description of the same argument of
-		get_user_pages().
+ * @gup_flags:	flags modifying lookup behaviour
  * @vec:	structure which receives pages / pfns of the addresses mapped.
  *		It should have space for@least nr_frames entries.
  *
@@ -34,23 +31,17 @@
  * This function takes care of grabbing mmap_sem as necessary.
  */
 int get_vaddr_frames(unsigned long start, unsigned int nr_frames,
-		     bool write, bool force, struct frame_vector *vec)
+		     unsigned int gup_flags, struct frame_vector *vec)
 {
 	struct mm_struct *mm = current->mm;
 	struct vm_area_struct *vma;
 	int ret = 0;
 	int err;
 	int locked;
-	unsigned int gup_flags = 0;
 
 	if (nr_frames == 0)
 		return 0;
 
-	if (write)
-		gup_flags |= FOLL_WRITE;
-	if (force)
-		gup_flags |= FOLL_FORCE;
-
 	if (WARN_ON_ONCE(nr_frames > vec->nr_allocated))
 		nr_frames = vec->nr_allocated;
 
-- 
2.10.0

^ permalink raw reply related

* [PATCH 06/10] mm: replace get_user_pages() write/force parameters with gup_flags
From: Lorenzo Stoakes @ 2016-10-13  0:20 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161013002020.3062-1-lstoakes@gmail.com>

This patch removes the write and force parameters from get_user_pages() and
replaces them with a gup_flags parameter to make the use of FOLL_FORCE explicit
in callers as use of this flag can result in surprising behaviour (and hence
bugs) within the mm subsystem.

Signed-off-by: Lorenzo Stoakes <lstoakes@gmail.com>
---
 arch/cris/arch-v32/drivers/cryptocop.c                 |  4 +---
 arch/ia64/kernel/err_inject.c                          |  2 +-
 arch/x86/mm/mpx.c                                      |  5 ++---
 drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c                |  7 +++++--
 drivers/gpu/drm/radeon/radeon_ttm.c                    |  3 ++-
 drivers/gpu/drm/via/via_dmablit.c                      |  4 ++--
 drivers/infiniband/core/umem.c                         |  6 +++++-
 drivers/infiniband/hw/mthca/mthca_memfree.c            |  2 +-
 drivers/infiniband/hw/qib/qib_user_pages.c             |  3 ++-
 drivers/infiniband/hw/usnic/usnic_uiom.c               |  5 ++++-
 drivers/media/v4l2-core/videobuf-dma-sg.c              |  7 +++++--
 drivers/misc/mic/scif/scif_rma.c                       |  3 +--
 drivers/misc/sgi-gru/grufault.c                        |  2 +-
 drivers/platform/goldfish/goldfish_pipe.c              |  3 ++-
 drivers/rapidio/devices/rio_mport_cdev.c               |  3 ++-
 .../vc04_services/interface/vchiq_arm/vchiq_2835_arm.c |  3 +--
 .../vc04_services/interface/vchiq_arm/vchiq_arm.c      |  3 +--
 drivers/virt/fsl_hypervisor.c                          |  4 ++--
 include/linux/mm.h                                     |  2 +-
 mm/gup.c                                               | 12 +++---------
 mm/mempolicy.c                                         |  2 +-
 mm/nommu.c                                             | 18 ++++--------------
 22 files changed, 49 insertions(+), 54 deletions(-)

diff --git a/arch/cris/arch-v32/drivers/cryptocop.c b/arch/cris/arch-v32/drivers/cryptocop.c
index b5698c8..099e170 100644
--- a/arch/cris/arch-v32/drivers/cryptocop.c
+++ b/arch/cris/arch-v32/drivers/cryptocop.c
@@ -2722,7 +2722,6 @@ static int cryptocop_ioctl_process(struct inode *inode, struct file *filp, unsig
 	err = get_user_pages((unsigned long int)(oper.indata + prev_ix),
 			     noinpages,
 			     0,  /* read access only for in data */
-			     0, /* no force */
 			     inpages,
 			     NULL);
 
@@ -2736,8 +2735,7 @@ static int cryptocop_ioctl_process(struct inode *inode, struct file *filp, unsig
 	if (oper.do_cipher){
 		err = get_user_pages((unsigned long int)oper.cipher_outdata,
 				     nooutpages,
-				     1, /* write access for out data */
-				     0, /* no force */
+				     FOLL_WRITE, /* write access for out data */
 				     outpages,
 				     NULL);
 		up_read(&current->mm->mmap_sem);
diff --git a/arch/ia64/kernel/err_inject.c b/arch/ia64/kernel/err_inject.c
index 09f8457..5ed0ea9 100644
--- a/arch/ia64/kernel/err_inject.c
+++ b/arch/ia64/kernel/err_inject.c
@@ -142,7 +142,7 @@ store_virtual_to_phys(struct device *dev, struct device_attribute *attr,
 	u64 virt_addr=simple_strtoull(buf, NULL, 16);
 	int ret;
 
-	ret = get_user_pages(virt_addr, 1, VM_READ, 0, NULL, NULL);
+	ret = get_user_pages(virt_addr, 1, FOLL_WRITE, NULL, NULL);
 	if (ret<=0) {
 #ifdef ERR_INJ_DEBUG
 		printk("Virtual address %lx is not existing.\n",virt_addr);
diff --git a/arch/x86/mm/mpx.c b/arch/x86/mm/mpx.c
index 8047687..e4f8009 100644
--- a/arch/x86/mm/mpx.c
+++ b/arch/x86/mm/mpx.c
@@ -544,10 +544,9 @@ static int mpx_resolve_fault(long __user *addr, int write)
 {
 	long gup_ret;
 	int nr_pages = 1;
-	int force = 0;
 
-	gup_ret = get_user_pages((unsigned long)addr, nr_pages, write,
-			force, NULL, NULL);
+	gup_ret = get_user_pages((unsigned long)addr, nr_pages,
+			write ? FOLL_WRITE : 0,	NULL, NULL);
 	/*
 	 * get_user_pages() returns number of pages gotten.
 	 * 0 means we failed to fault in and get anything,
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
index 887483b..dcaf691 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
@@ -555,10 +555,13 @@ struct amdgpu_ttm_tt {
 int amdgpu_ttm_tt_get_user_pages(struct ttm_tt *ttm, struct page **pages)
 {
 	struct amdgpu_ttm_tt *gtt = (void *)ttm;
-	int write = !(gtt->userflags & AMDGPU_GEM_USERPTR_READONLY);
+	unsigned int flags = 0;
 	unsigned pinned = 0;
 	int r;
 
+	if (!(gtt->userflags & AMDGPU_GEM_USERPTR_READONLY))
+		flags |= FOLL_WRITE;
+
 	if (gtt->userflags & AMDGPU_GEM_USERPTR_ANONONLY) {
 		/* check that we only use anonymous memory
 		   to prevent problems with writeback */
@@ -581,7 +584,7 @@ int amdgpu_ttm_tt_get_user_pages(struct ttm_tt *ttm, struct page **pages)
 		list_add(&guptask.list, &gtt->guptasks);
 		spin_unlock(&gtt->guptasklock);
 
-		r = get_user_pages(userptr, num_pages, write, 0, p, NULL);
+		r = get_user_pages(userptr, num_pages, flags, p, NULL);
 
 		spin_lock(&gtt->guptasklock);
 		list_del(&guptask.list);
diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c
index 4552682..3de5e6e 100644
--- a/drivers/gpu/drm/radeon/radeon_ttm.c
+++ b/drivers/gpu/drm/radeon/radeon_ttm.c
@@ -566,7 +566,8 @@ static int radeon_ttm_tt_pin_userptr(struct ttm_tt *ttm)
 		uint64_t userptr = gtt->userptr + pinned * PAGE_SIZE;
 		struct page **pages = ttm->pages + pinned;
 
-		r = get_user_pages(userptr, num_pages, write, 0, pages, NULL);
+		r = get_user_pages(userptr, num_pages, write ? FOLL_WRITE : 0,
+				   pages, NULL);
 		if (r < 0)
 			goto release_pages;
 
diff --git a/drivers/gpu/drm/via/via_dmablit.c b/drivers/gpu/drm/via/via_dmablit.c
index 7e2a12c..1a3ad76 100644
--- a/drivers/gpu/drm/via/via_dmablit.c
+++ b/drivers/gpu/drm/via/via_dmablit.c
@@ -241,8 +241,8 @@ via_lock_all_dma_pages(drm_via_sg_info_t *vsg,  drm_via_dmablit_t *xfer)
 	down_read(&current->mm->mmap_sem);
 	ret = get_user_pages((unsigned long)xfer->mem_addr,
 			     vsg->num_pages,
-			     (vsg->direction == DMA_FROM_DEVICE),
-			     0, vsg->pages, NULL);
+			     (vsg->direction == DMA_FROM_DEVICE) ? FOLL_WRITE : 0,
+			     vsg->pages, NULL);
 
 	up_read(&current->mm->mmap_sem);
 	if (ret != vsg->num_pages) {
diff --git a/drivers/infiniband/core/umem.c b/drivers/infiniband/core/umem.c
index c68746c..224ad27 100644
--- a/drivers/infiniband/core/umem.c
+++ b/drivers/infiniband/core/umem.c
@@ -94,6 +94,7 @@ struct ib_umem *ib_umem_get(struct ib_ucontext *context, unsigned long addr,
 	unsigned long dma_attrs = 0;
 	struct scatterlist *sg, *sg_list_start;
 	int need_release = 0;
+	unsigned int gup_flags = FOLL_WRITE;
 
 	if (dmasync)
 		dma_attrs |= DMA_ATTR_WRITE_BARRIER;
@@ -183,6 +184,9 @@ struct ib_umem *ib_umem_get(struct ib_ucontext *context, unsigned long addr,
 	if (ret)
 		goto out;
 
+	if (!umem->writable)
+		gup_flags |= FOLL_FORCE;
+
 	need_release = 1;
 	sg_list_start = umem->sg_head.sgl;
 
@@ -190,7 +194,7 @@ struct ib_umem *ib_umem_get(struct ib_ucontext *context, unsigned long addr,
 		ret = get_user_pages(cur_base,
 				     min_t(unsigned long, npages,
 					   PAGE_SIZE / sizeof (struct page *)),
-				     1, !umem->writable, page_list, vma_list);
+				     gup_flags, page_list, vma_list);
 
 		if (ret < 0)
 			goto out;
diff --git a/drivers/infiniband/hw/mthca/mthca_memfree.c b/drivers/infiniband/hw/mthca/mthca_memfree.c
index 6c00d04..c6fe89d 100644
--- a/drivers/infiniband/hw/mthca/mthca_memfree.c
+++ b/drivers/infiniband/hw/mthca/mthca_memfree.c
@@ -472,7 +472,7 @@ int mthca_map_user_db(struct mthca_dev *dev, struct mthca_uar *uar,
 		goto out;
 	}
 
-	ret = get_user_pages(uaddr & PAGE_MASK, 1, 1, 0, pages, NULL);
+	ret = get_user_pages(uaddr & PAGE_MASK, 1, FOLL_WRITE, pages, NULL);
 	if (ret < 0)
 		goto out;
 
diff --git a/drivers/infiniband/hw/qib/qib_user_pages.c b/drivers/infiniband/hw/qib/qib_user_pages.c
index 2d2b94f..75f0862 100644
--- a/drivers/infiniband/hw/qib/qib_user_pages.c
+++ b/drivers/infiniband/hw/qib/qib_user_pages.c
@@ -67,7 +67,8 @@ static int __qib_get_user_pages(unsigned long start_page, size_t num_pages,
 
 	for (got = 0; got < num_pages; got += ret) {
 		ret = get_user_pages(start_page + got * PAGE_SIZE,
-				     num_pages - got, 1, 1,
+				     num_pages - got,
+				     FOLL_WRITE | FOLL_FORCE,
 				     p + got, NULL);
 		if (ret < 0)
 			goto bail_release;
diff --git a/drivers/infiniband/hw/usnic/usnic_uiom.c b/drivers/infiniband/hw/usnic/usnic_uiom.c
index a0b6ebe..1ccee6e 100644
--- a/drivers/infiniband/hw/usnic/usnic_uiom.c
+++ b/drivers/infiniband/hw/usnic/usnic_uiom.c
@@ -111,6 +111,7 @@ static int usnic_uiom_get_pages(unsigned long addr, size_t size, int writable,
 	int i;
 	int flags;
 	dma_addr_t pa;
+	unsigned int gup_flags;
 
 	if (!can_do_mlock())
 		return -EPERM;
@@ -135,6 +136,8 @@ static int usnic_uiom_get_pages(unsigned long addr, size_t size, int writable,
 
 	flags = IOMMU_READ | IOMMU_CACHE;
 	flags |= (writable) ? IOMMU_WRITE : 0;
+	gup_flags = FOLL_WRITE;
+	gup_flags |= (writable) ? 0 : FOLL_FORCE;
 	cur_base = addr & PAGE_MASK;
 	ret = 0;
 
@@ -142,7 +145,7 @@ static int usnic_uiom_get_pages(unsigned long addr, size_t size, int writable,
 		ret = get_user_pages(cur_base,
 					min_t(unsigned long, npages,
 					PAGE_SIZE / sizeof(struct page *)),
-					1, !writable, page_list, NULL);
+					gup_flags, page_list, NULL);
 
 		if (ret < 0)
 			goto out;
diff --git a/drivers/media/v4l2-core/videobuf-dma-sg.c b/drivers/media/v4l2-core/videobuf-dma-sg.c
index f300f06..1db0af6 100644
--- a/drivers/media/v4l2-core/videobuf-dma-sg.c
+++ b/drivers/media/v4l2-core/videobuf-dma-sg.c
@@ -156,6 +156,7 @@ static int videobuf_dma_init_user_locked(struct videobuf_dmabuf *dma,
 {
 	unsigned long first, last;
 	int err, rw = 0;
+	unsigned int flags = FOLL_FORCE;
 
 	dma->direction = direction;
 	switch (dma->direction) {
@@ -178,12 +179,14 @@ static int videobuf_dma_init_user_locked(struct videobuf_dmabuf *dma,
 	if (NULL == dma->pages)
 		return -ENOMEM;
 
+	if (rw == READ)
+		flags |= FOLL_WRITE;
+
 	dprintk(1, "init user [0x%lx+0x%lx => %d pages]\n",
 		data, size, dma->nr_pages);
 
 	err = get_user_pages(data & PAGE_MASK, dma->nr_pages,
-			     rw == READ, 1, /* force */
-			     dma->pages, NULL);
+			     flags, dma->pages, NULL);
 
 	if (err != dma->nr_pages) {
 		dma->nr_pages = (err >= 0) ? err : 0;
diff --git a/drivers/misc/mic/scif/scif_rma.c b/drivers/misc/mic/scif/scif_rma.c
index e0203b1..f806a44 100644
--- a/drivers/misc/mic/scif/scif_rma.c
+++ b/drivers/misc/mic/scif/scif_rma.c
@@ -1396,8 +1396,7 @@ int __scif_pin_pages(void *addr, size_t len, int *out_prot,
 		pinned_pages->nr_pages = get_user_pages(
 				(u64)addr,
 				nr_pages,
-				!!(prot & SCIF_PROT_WRITE),
-				0,
+				(prot & SCIF_PROT_WRITE) ? FOLL_WRITE : 0,
 				pinned_pages->pages,
 				NULL);
 		up_write(&mm->mmap_sem);
diff --git a/drivers/misc/sgi-gru/grufault.c b/drivers/misc/sgi-gru/grufault.c
index a2d97b9..6fb773d 100644
--- a/drivers/misc/sgi-gru/grufault.c
+++ b/drivers/misc/sgi-gru/grufault.c
@@ -198,7 +198,7 @@ static int non_atomic_pte_lookup(struct vm_area_struct *vma,
 #else
 	*pageshift = PAGE_SHIFT;
 #endif
-	if (get_user_pages(vaddr, 1, write, 0, &page, NULL) <= 0)
+	if (get_user_pages(vaddr, 1, write ? FOLL_WRITE : 0, &page, NULL) <= 0)
 		return -EFAULT;
 	*paddr = page_to_phys(page);
 	put_page(page);
diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c
index 07462d7..1aba2c7 100644
--- a/drivers/platform/goldfish/goldfish_pipe.c
+++ b/drivers/platform/goldfish/goldfish_pipe.c
@@ -309,7 +309,8 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, char __user *buffer,
 		 * much memory to the process.
 		 */
 		down_read(&current->mm->mmap_sem);
-		ret = get_user_pages(address, 1, !is_write, 0, &page, NULL);
+		ret = get_user_pages(address, 1, is_write ? 0 : FOLL_WRITE,
+				&page, NULL);
 		up_read(&current->mm->mmap_sem);
 		if (ret < 0)
 			break;
diff --git a/drivers/rapidio/devices/rio_mport_cdev.c b/drivers/rapidio/devices/rio_mport_cdev.c
index 436dfe8..9013a58 100644
--- a/drivers/rapidio/devices/rio_mport_cdev.c
+++ b/drivers/rapidio/devices/rio_mport_cdev.c
@@ -892,7 +892,8 @@ rio_dma_transfer(struct file *filp, u32 transfer_mode,
 		down_read(&current->mm->mmap_sem);
 		pinned = get_user_pages(
 				(unsigned long)xfer->loc_addr & PAGE_MASK,
-				nr_pages, dir == DMA_FROM_DEVICE, 0,
+				nr_pages,
+				dir == DMA_FROM_DEVICE ? FOLL_WRITE : 0,
 				page_list, NULL);
 		up_read(&current->mm->mmap_sem);
 
diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c
index c29040f..1091b9f 100644
--- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c
+++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c
@@ -423,8 +423,7 @@ create_pagelist(char __user *buf, size_t count, unsigned short type,
 		actual_pages = get_user_pages(task, task->mm,
 				          (unsigned long)buf & ~(PAGE_SIZE - 1),
 					  num_pages,
-					  (type == PAGELIST_READ) /*Write */ ,
-					  0 /*Force */ ,
+					  (type == PAGELIST_READ) ? FOLL_WRITE : 0,
 					  pages,
 					  NULL /*vmas */);
 		up_read(&task->mm->mmap_sem);
diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c
index e11c0e0..7b6cd4d 100644
--- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c
+++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c
@@ -1477,8 +1477,7 @@ dump_phys_mem(void *virt_addr, uint32_t num_bytes)
 		current->mm,              /* mm */
 		(unsigned long)virt_addr, /* start */
 		num_pages,                /* len */
-		0,                        /* write */
-		0,                        /* force */
+		0,                        /* gup_flags */
 		pages,                    /* pages (array of page pointers) */
 		NULL);                    /* vmas */
 	up_read(&current->mm->mmap_sem);
diff --git a/drivers/virt/fsl_hypervisor.c b/drivers/virt/fsl_hypervisor.c
index 60bdad3..150ce2a 100644
--- a/drivers/virt/fsl_hypervisor.c
+++ b/drivers/virt/fsl_hypervisor.c
@@ -245,8 +245,8 @@ static long ioctl_memcpy(struct fsl_hv_ioctl_memcpy __user *p)
 	/* Get the physical addresses of the source buffer */
 	down_read(&current->mm->mmap_sem);
 	num_pinned = get_user_pages(param.local_vaddr - lb_offset,
-		num_pages, (param.source == -1) ? READ : WRITE,
-		0, pages, NULL);
+		num_pages, (param.source == -1) ? 0 : FOLL_WRITE,
+		pages, NULL);
 	up_read(&current->mm->mmap_sem);
 
 	if (num_pinned != num_pages) {
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 5ff084f6..686a477 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -1279,7 +1279,7 @@ long get_user_pages_remote(struct task_struct *tsk, struct mm_struct *mm,
 			    int write, int force, struct page **pages,
 			    struct vm_area_struct **vmas);
 long get_user_pages(unsigned long start, unsigned long nr_pages,
-			    int write, int force, struct page **pages,
+			    unsigned int gup_flags, struct page **pages,
 			    struct vm_area_struct **vmas);
 long get_user_pages_locked(unsigned long start, unsigned long nr_pages,
 		    unsigned int gup_flags, struct page **pages, int *locked);
diff --git a/mm/gup.c b/mm/gup.c
index 7a0d033..dc91303 100644
--- a/mm/gup.c
+++ b/mm/gup.c
@@ -977,18 +977,12 @@ EXPORT_SYMBOL(get_user_pages_remote);
  * obviously don't pass FOLL_REMOTE in here.
  */
 long get_user_pages(unsigned long start, unsigned long nr_pages,
-		int write, int force, struct page **pages,
+		unsigned int gup_flags, struct page **pages,
 		struct vm_area_struct **vmas)
 {
-	unsigned int flags = FOLL_TOUCH;
-
-	if (write)
-		flags |= FOLL_WRITE;
-	if (force)
-		flags |= FOLL_FORCE;
-
 	return __get_user_pages_locked(current, current->mm, start, nr_pages,
-				       pages, vmas, NULL, false, flags);
+				       pages, vmas, NULL, false,
+				       gup_flags | FOLL_TOUCH);
 }
 EXPORT_SYMBOL(get_user_pages);
 
diff --git a/mm/mempolicy.c b/mm/mempolicy.c
index ad1c96a..0b859af 100644
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -850,7 +850,7 @@ static int lookup_node(unsigned long addr)
 	struct page *p;
 	int err;
 
-	err = get_user_pages(addr & PAGE_MASK, 1, 0, 0, &p, NULL);
+	err = get_user_pages(addr & PAGE_MASK, 1, 0, &p, NULL);
 	if (err >= 0) {
 		err = page_to_nid(p);
 		put_page(p);
diff --git a/mm/nommu.c b/mm/nommu.c
index 842cfdd..70cb844 100644
--- a/mm/nommu.c
+++ b/mm/nommu.c
@@ -160,18 +160,11 @@ long __get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
  * - don't permit access to VMAs that don't support it, such as I/O mappings
  */
 long get_user_pages(unsigned long start, unsigned long nr_pages,
-		    int write, int force, struct page **pages,
+		    unsigned int gup_flags, struct page **pages,
 		    struct vm_area_struct **vmas)
 {
-	int flags = 0;
-
-	if (write)
-		flags |= FOLL_WRITE;
-	if (force)
-		flags |= FOLL_FORCE;
-
-	return __get_user_pages(current, current->mm, start, nr_pages, flags,
-				pages, vmas, NULL);
+	return __get_user_pages(current, current->mm, start, nr_pages,
+				gup_flags, pages, vmas, NULL);
 }
 EXPORT_SYMBOL(get_user_pages);
 
@@ -179,10 +172,7 @@ long get_user_pages_locked(unsigned long start, unsigned long nr_pages,
 			    unsigned int gup_flags, struct page **pages,
 			    int *locked)
 {
-	int write = gup_flags & FOLL_WRITE;
-	int force = gup_flags & FOLL_FORCE;
-
-	return get_user_pages(start, nr_pages, write, force, pages, NULL);
+	return get_user_pages(start, nr_pages, gup_flags, pages, NULL);
 }
 EXPORT_SYMBOL(get_user_pages_locked);
 
-- 
2.10.0

^ permalink raw reply related

* [PATCH 07/10] mm: replace get_user_pages_remote() write/force parameters with gup_flags
From: Lorenzo Stoakes @ 2016-10-13  0:20 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161013002020.3062-1-lstoakes@gmail.com>

This patch removes the write and force parameters from get_user_pages_remote()
and replaces them with a gup_flags parameter to make the use of FOLL_FORCE
explicit in callers as use of this flag can result in surprising behaviour (and
hence bugs) within the mm subsystem.

Signed-off-by: Lorenzo Stoakes <lstoakes@gmail.com>
---
 drivers/gpu/drm/etnaviv/etnaviv_gem.c   |  7 +++++--
 drivers/gpu/drm/i915/i915_gem_userptr.c |  6 +++++-
 drivers/infiniband/core/umem_odp.c      |  7 +++++--
 fs/exec.c                               |  9 +++++++--
 include/linux/mm.h                      |  2 +-
 kernel/events/uprobes.c                 |  6 ++++--
 mm/gup.c                                | 22 +++++++---------------
 mm/memory.c                             |  6 +++++-
 security/tomoyo/domain.c                |  2 +-
 9 files changed, 40 insertions(+), 27 deletions(-)

diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.c b/drivers/gpu/drm/etnaviv/etnaviv_gem.c
index 5ce3603..0370b84 100644
--- a/drivers/gpu/drm/etnaviv/etnaviv_gem.c
+++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.c
@@ -748,19 +748,22 @@ static struct page **etnaviv_gem_userptr_do_get_pages(
 	int ret = 0, pinned, npages = etnaviv_obj->base.size >> PAGE_SHIFT;
 	struct page **pvec;
 	uintptr_t ptr;
+	unsigned int flags = 0;
 
 	pvec = drm_malloc_ab(npages, sizeof(struct page *));
 	if (!pvec)
 		return ERR_PTR(-ENOMEM);
 
+	if (!etnaviv_obj->userptr.ro)
+		flags |= FOLL_WRITE;
+
 	pinned = 0;
 	ptr = etnaviv_obj->userptr.ptr;
 
 	down_read(&mm->mmap_sem);
 	while (pinned < npages) {
 		ret = get_user_pages_remote(task, mm, ptr, npages - pinned,
-					    !etnaviv_obj->userptr.ro, 0,
-					    pvec + pinned, NULL);
+					    flags, pvec + pinned, NULL);
 		if (ret < 0)
 			break;
 
diff --git a/drivers/gpu/drm/i915/i915_gem_userptr.c b/drivers/gpu/drm/i915/i915_gem_userptr.c
index e537930..c6f780f 100644
--- a/drivers/gpu/drm/i915/i915_gem_userptr.c
+++ b/drivers/gpu/drm/i915/i915_gem_userptr.c
@@ -508,6 +508,10 @@ __i915_gem_userptr_get_pages_worker(struct work_struct *_work)
 	pvec = drm_malloc_gfp(npages, sizeof(struct page *), GFP_TEMPORARY);
 	if (pvec != NULL) {
 		struct mm_struct *mm = obj->userptr.mm->mm;
+		unsigned int flags = 0;
+
+		if (!obj->userptr.read_only)
+			flags |= FOLL_WRITE;
 
 		ret = -EFAULT;
 		if (atomic_inc_not_zero(&mm->mm_users)) {
@@ -517,7 +521,7 @@ __i915_gem_userptr_get_pages_worker(struct work_struct *_work)
 					(work->task, mm,
 					 obj->userptr.ptr + pinned * PAGE_SIZE,
 					 npages - pinned,
-					 !obj->userptr.read_only, 0,
+					 flags,
 					 pvec + pinned, NULL);
 				if (ret < 0)
 					break;
diff --git a/drivers/infiniband/core/umem_odp.c b/drivers/infiniband/core/umem_odp.c
index 75077a0..1f0fe32 100644
--- a/drivers/infiniband/core/umem_odp.c
+++ b/drivers/infiniband/core/umem_odp.c
@@ -527,6 +527,7 @@ int ib_umem_odp_map_dma_pages(struct ib_umem *umem, u64 user_virt, u64 bcnt,
 	u64 off;
 	int j, k, ret = 0, start_idx, npages = 0;
 	u64 base_virt_addr;
+	unsigned int flags = 0;
 
 	if (access_mask == 0)
 		return -EINVAL;
@@ -556,6 +557,9 @@ int ib_umem_odp_map_dma_pages(struct ib_umem *umem, u64 user_virt, u64 bcnt,
 		goto out_put_task;
 	}
 
+	if (access_mask & ODP_WRITE_ALLOWED_BIT)
+		flags |= FOLL_WRITE;
+
 	start_idx = (user_virt - ib_umem_start(umem)) >> PAGE_SHIFT;
 	k = start_idx;
 
@@ -574,8 +578,7 @@ int ib_umem_odp_map_dma_pages(struct ib_umem *umem, u64 user_virt, u64 bcnt,
 		 */
 		npages = get_user_pages_remote(owning_process, owning_mm,
 				user_virt, gup_num_pages,
-				access_mask & ODP_WRITE_ALLOWED_BIT,
-				0, local_page_list, NULL);
+				flags, local_page_list, NULL);
 		up_read(&owning_mm->mmap_sem);
 
 		if (npages < 0)
diff --git a/fs/exec.c b/fs/exec.c
index 6fcfb3f..4e497b9 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -191,6 +191,7 @@ static struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos,
 {
 	struct page *page;
 	int ret;
+	unsigned int gup_flags = FOLL_FORCE;
 
 #ifdef CONFIG_STACK_GROWSUP
 	if (write) {
@@ -199,12 +200,16 @@ static struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos,
 			return NULL;
 	}
 #endif
+
+	if (write)
+		gup_flags |= FOLL_WRITE;
+
 	/*
 	 * We are doing an exec().  'current' is the process
 	 * doing the exec and bprm->mm is the new process's mm.
 	 */
-	ret = get_user_pages_remote(current, bprm->mm, pos, 1, write,
-			1, &page, NULL);
+	ret = get_user_pages_remote(current, bprm->mm, pos, 1, gup_flags,
+			&page, NULL);
 	if (ret <= 0)
 		return NULL;
 
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 686a477..2a481d3 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -1276,7 +1276,7 @@ long __get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
 		      struct vm_area_struct **vmas, int *nonblocking);
 long get_user_pages_remote(struct task_struct *tsk, struct mm_struct *mm,
 			    unsigned long start, unsigned long nr_pages,
-			    int write, int force, struct page **pages,
+			    unsigned int gup_flags, struct page **pages,
 			    struct vm_area_struct **vmas);
 long get_user_pages(unsigned long start, unsigned long nr_pages,
 			    unsigned int gup_flags, struct page **pages,
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index d4129bb..f9ec9ad 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -300,7 +300,8 @@ int uprobe_write_opcode(struct mm_struct *mm, unsigned long vaddr,
 
 retry:
 	/* Read the page with vaddr into memory */
-	ret = get_user_pages_remote(NULL, mm, vaddr, 1, 0, 1, &old_page, &vma);
+	ret = get_user_pages_remote(NULL, mm, vaddr, 1, FOLL_FORCE, &old_page,
+			&vma);
 	if (ret <= 0)
 		return ret;
 
@@ -1710,7 +1711,8 @@ static int is_trap_at_addr(struct mm_struct *mm, unsigned long vaddr)
 	 * but we treat this as a 'remote' access since it is
 	 * essentially a kernel access to the memory.
 	 */
-	result = get_user_pages_remote(NULL, mm, vaddr, 1, 0, 1, &page, NULL);
+	result = get_user_pages_remote(NULL, mm, vaddr, 1, FOLL_FORCE, &page,
+			NULL);
 	if (result < 0)
 		return result;
 
diff --git a/mm/gup.c b/mm/gup.c
index dc91303..0deecf3 100644
--- a/mm/gup.c
+++ b/mm/gup.c
@@ -905,9 +905,7 @@ EXPORT_SYMBOL(get_user_pages_unlocked);
  * @mm:		mm_struct of target mm
  * @start:	starting user address
  * @nr_pages:	number of pages from start to pin
- * @write:	whether pages will be written to by the caller
- * @force:	whether to force access even when user mapping is currently
- *		protected (but never forces write access to shared mapping).
+ * @gup_flags:	flags modifying lookup behaviour
  * @pages:	array that receives pointers to the pages pinned.
  *		Should be@least nr_pages long. Or NULL, if caller
  *		only intends to ensure the pages are faulted in.
@@ -936,9 +934,9 @@ EXPORT_SYMBOL(get_user_pages_unlocked);
  * or similar operation cannot guarantee anything stronger anyway because
  * locks can't be held over the syscall boundary.
  *
- * If write=0, the page must not be written to. If the page is written to,
- * set_page_dirty (or set_page_dirty_lock, as appropriate) must be called
- * after the page is finished with, and before put_page is called.
+ * If gup_flags & FOLL_WRITE == 0, the page must not be written to. If the page
+ * is written to, set_page_dirty (or set_page_dirty_lock, as appropriate) must
+ * be called after the page is finished with, and before put_page is called.
  *
  * get_user_pages is typically used for fewer-copy IO operations, to get a
  * handle on the memory by some means other than accesses via the user virtual
@@ -955,18 +953,12 @@ EXPORT_SYMBOL(get_user_pages_unlocked);
  */
 long get_user_pages_remote(struct task_struct *tsk, struct mm_struct *mm,
 		unsigned long start, unsigned long nr_pages,
-		int write, int force, struct page **pages,
+		unsigned int gup_flags, struct page **pages,
 		struct vm_area_struct **vmas)
 {
-	unsigned int flags = FOLL_TOUCH | FOLL_REMOTE;
-
-	if (write)
-		flags |= FOLL_WRITE;
-	if (force)
-		flags |= FOLL_FORCE;
-
 	return __get_user_pages_locked(tsk, mm, start, nr_pages, pages, vmas,
-				       NULL, false, flags);
+				       NULL, false,
+				       gup_flags | FOLL_TOUCH | FOLL_REMOTE);
 }
 EXPORT_SYMBOL(get_user_pages_remote);
 
diff --git a/mm/memory.c b/mm/memory.c
index fc1987d..20a9adb 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -3873,6 +3873,10 @@ static int __access_remote_vm(struct task_struct *tsk, struct mm_struct *mm,
 {
 	struct vm_area_struct *vma;
 	void *old_buf = buf;
+	unsigned int flags = FOLL_FORCE;
+
+	if (write)
+		flags |= FOLL_WRITE;
 
 	down_read(&mm->mmap_sem);
 	/* ignore errors, just check how much was successfully transferred */
@@ -3882,7 +3886,7 @@ static int __access_remote_vm(struct task_struct *tsk, struct mm_struct *mm,
 		struct page *page = NULL;
 
 		ret = get_user_pages_remote(tsk, mm, addr, 1,
-				write, 1, &page, &vma);
+				flags, &page, &vma);
 		if (ret <= 0) {
 #ifndef CONFIG_HAVE_IOREMAP_PROT
 			break;
diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c
index ade7c6c..682b73a 100644
--- a/security/tomoyo/domain.c
+++ b/security/tomoyo/domain.c
@@ -881,7 +881,7 @@ bool tomoyo_dump_page(struct linux_binprm *bprm, unsigned long pos,
 	 * the execve().
 	 */
 	if (get_user_pages_remote(current, bprm->mm, pos, 1,
-				0, 1, &page, NULL) <= 0)
+				FOLL_FORCE, &page, NULL) <= 0)
 		return false;
 #else
 	page = bprm->page[pos / PAGE_SIZE];
-- 
2.10.0

^ permalink raw reply related

* [PATCH 08/10] mm: replace __access_remote_vm() write parameter with gup_flags
From: Lorenzo Stoakes @ 2016-10-13  0:20 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161013002020.3062-1-lstoakes@gmail.com>

This patch removes the write parameter from __access_remote_vm() and replaces it
with a gup_flags parameter as use of this function previously _implied_
FOLL_FORCE, whereas after this patch callers explicitly pass this flag.

We make this explicit as use of FOLL_FORCE can result in surprising behaviour
(and hence bugs) within the mm subsystem.

Signed-off-by: Lorenzo Stoakes <lstoakes@gmail.com>
---
 mm/memory.c | 23 +++++++++++++++--------
 mm/nommu.c  |  9 ++++++---
 2 files changed, 21 insertions(+), 11 deletions(-)

diff --git a/mm/memory.c b/mm/memory.c
index 20a9adb..79ebed3 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -3869,14 +3869,11 @@ EXPORT_SYMBOL_GPL(generic_access_phys);
  * given task for page fault accounting.
  */
 static int __access_remote_vm(struct task_struct *tsk, struct mm_struct *mm,
-		unsigned long addr, void *buf, int len, int write)
+		unsigned long addr, void *buf, int len, unsigned int gup_flags)
 {
 	struct vm_area_struct *vma;
 	void *old_buf = buf;
-	unsigned int flags = FOLL_FORCE;
-
-	if (write)
-		flags |= FOLL_WRITE;
+	int write = gup_flags & FOLL_WRITE;
 
 	down_read(&mm->mmap_sem);
 	/* ignore errors, just check how much was successfully transferred */
@@ -3886,7 +3883,7 @@ static int __access_remote_vm(struct task_struct *tsk, struct mm_struct *mm,
 		struct page *page = NULL;
 
 		ret = get_user_pages_remote(tsk, mm, addr, 1,
-				flags, &page, &vma);
+				gup_flags, &page, &vma);
 		if (ret <= 0) {
 #ifndef CONFIG_HAVE_IOREMAP_PROT
 			break;
@@ -3945,7 +3942,12 @@ static int __access_remote_vm(struct task_struct *tsk, struct mm_struct *mm,
 int access_remote_vm(struct mm_struct *mm, unsigned long addr,
 		void *buf, int len, int write)
 {
-	return __access_remote_vm(NULL, mm, addr, buf, len, write);
+	unsigned int flags = FOLL_FORCE;
+
+	if (write)
+		flags |= FOLL_WRITE;
+
+	return __access_remote_vm(NULL, mm, addr, buf, len, flags);
 }
 
 /*
@@ -3958,12 +3960,17 @@ int access_process_vm(struct task_struct *tsk, unsigned long addr,
 {
 	struct mm_struct *mm;
 	int ret;
+	unsigned int flags = FOLL_FORCE;
 
 	mm = get_task_mm(tsk);
 	if (!mm)
 		return 0;
 
-	ret = __access_remote_vm(tsk, mm, addr, buf, len, write);
+	if (write)
+		flags |= FOLL_WRITE;
+
+	ret = __access_remote_vm(tsk, mm, addr, buf, len, flags);
+
 	mmput(mm);
 
 	return ret;
diff --git a/mm/nommu.c b/mm/nommu.c
index 70cb844..bde7df3 100644
--- a/mm/nommu.c
+++ b/mm/nommu.c
@@ -1809,9 +1809,10 @@ void filemap_map_pages(struct fault_env *fe,
 EXPORT_SYMBOL(filemap_map_pages);
 
 static int __access_remote_vm(struct task_struct *tsk, struct mm_struct *mm,
-		unsigned long addr, void *buf, int len, int write)
+		unsigned long addr, void *buf, int len, unsigned int gup_flags)
 {
 	struct vm_area_struct *vma;
+	int write = gup_flags & FOLL_WRITE;
 
 	down_read(&mm->mmap_sem);
 
@@ -1853,7 +1854,8 @@ static int __access_remote_vm(struct task_struct *tsk, struct mm_struct *mm,
 int access_remote_vm(struct mm_struct *mm, unsigned long addr,
 		void *buf, int len, int write)
 {
-	return __access_remote_vm(NULL, mm, addr, buf, len, write);
+	return __access_remote_vm(NULL, mm, addr, buf, len,
+			write ? FOLL_WRITE : 0);
 }
 
 /*
@@ -1871,7 +1873,8 @@ int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, in
 	if (!mm)
 		return 0;
 
-	len = __access_remote_vm(tsk, mm, addr, buf, len, write);
+	len = __access_remote_vm(tsk, mm, addr, buf, len,
+			write ? FOLL_WRITE : 0);
 
 	mmput(mm);
 	return len;
-- 
2.10.0

^ permalink raw reply related

* [PATCH 09/10] mm: replace access_remote_vm() write parameter with gup_flags
From: Lorenzo Stoakes @ 2016-10-13  0:20 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161013002020.3062-1-lstoakes@gmail.com>

This patch removes the write parameter from access_remote_vm() and replaces it
with a gup_flags parameter as use of this function previously _implied_
FOLL_FORCE, whereas after this patch callers explicitly pass this flag.

We make this explicit as use of FOLL_FORCE can result in surprising behaviour
(and hence bugs) within the mm subsystem.

Signed-off-by: Lorenzo Stoakes <lstoakes@gmail.com>
---
 fs/proc/base.c     | 19 +++++++++++++------
 include/linux/mm.h |  2 +-
 mm/memory.c        | 11 +++--------
 mm/nommu.c         |  7 +++----
 4 files changed, 20 insertions(+), 19 deletions(-)

diff --git a/fs/proc/base.c b/fs/proc/base.c
index c2964d8..8e65446 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -252,7 +252,7 @@ static ssize_t proc_pid_cmdline_read(struct file *file, char __user *buf,
 	 * Inherently racy -- command line shares address space
 	 * with code and data.
 	 */
-	rv = access_remote_vm(mm, arg_end - 1, &c, 1, 0);
+	rv = access_remote_vm(mm, arg_end - 1, &c, 1, FOLL_FORCE);
 	if (rv <= 0)
 		goto out_free_page;
 
@@ -270,7 +270,8 @@ static ssize_t proc_pid_cmdline_read(struct file *file, char __user *buf,
 			int nr_read;
 
 			_count = min3(count, len, PAGE_SIZE);
-			nr_read = access_remote_vm(mm, p, page, _count, 0);
+			nr_read = access_remote_vm(mm, p, page, _count,
+					FOLL_FORCE);
 			if (nr_read < 0)
 				rv = nr_read;
 			if (nr_read <= 0)
@@ -305,7 +306,8 @@ static ssize_t proc_pid_cmdline_read(struct file *file, char __user *buf,
 			bool final;
 
 			_count = min3(count, len, PAGE_SIZE);
-			nr_read = access_remote_vm(mm, p, page, _count, 0);
+			nr_read = access_remote_vm(mm, p, page, _count,
+					FOLL_FORCE);
 			if (nr_read < 0)
 				rv = nr_read;
 			if (nr_read <= 0)
@@ -354,7 +356,8 @@ static ssize_t proc_pid_cmdline_read(struct file *file, char __user *buf,
 			bool final;
 
 			_count = min3(count, len, PAGE_SIZE);
-			nr_read = access_remote_vm(mm, p, page, _count, 0);
+			nr_read = access_remote_vm(mm, p, page, _count,
+					FOLL_FORCE);
 			if (nr_read < 0)
 				rv = nr_read;
 			if (nr_read <= 0)
@@ -832,6 +835,7 @@ static ssize_t mem_rw(struct file *file, char __user *buf,
 	unsigned long addr = *ppos;
 	ssize_t copied;
 	char *page;
+	unsigned int flags = FOLL_FORCE;
 
 	if (!mm)
 		return 0;
@@ -844,6 +848,9 @@ static ssize_t mem_rw(struct file *file, char __user *buf,
 	if (!atomic_inc_not_zero(&mm->mm_users))
 		goto free;
 
+	if (write)
+		flags |= FOLL_WRITE;
+
 	while (count > 0) {
 		int this_len = min_t(int, count, PAGE_SIZE);
 
@@ -852,7 +859,7 @@ static ssize_t mem_rw(struct file *file, char __user *buf,
 			break;
 		}
 
-		this_len = access_remote_vm(mm, addr, page, this_len, write);
+		this_len = access_remote_vm(mm, addr, page, this_len, flags);
 		if (!this_len) {
 			if (!copied)
 				copied = -EIO;
@@ -965,7 +972,7 @@ static ssize_t environ_read(struct file *file, char __user *buf,
 		this_len = min(max_len, this_len);
 
 		retval = access_remote_vm(mm, (env_start + src),
-			page, this_len, 0);
+			page, this_len, FOLL_FORCE);
 
 		if (retval <= 0) {
 			ret = retval;
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 2a481d3..3e5234e 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -1268,7 +1268,7 @@ static inline int fixup_user_fault(struct task_struct *tsk,
 
 extern int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, int len, int write);
 extern int access_remote_vm(struct mm_struct *mm, unsigned long addr,
-		void *buf, int len, int write);
+		void *buf, int len, unsigned int gup_flags);
 
 long __get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
 		      unsigned long start, unsigned long nr_pages,
diff --git a/mm/memory.c b/mm/memory.c
index 79ebed3..bac2d99 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -3935,19 +3935,14 @@ static int __access_remote_vm(struct task_struct *tsk, struct mm_struct *mm,
  * @addr:	start address to access
  * @buf:	source or destination buffer
  * @len:	number of bytes to transfer
- * @write:	whether the access is a write
+ * @gup_flags:	flags modifying lookup behaviour
  *
  * The caller must hold a reference on @mm.
  */
 int access_remote_vm(struct mm_struct *mm, unsigned long addr,
-		void *buf, int len, int write)
+		void *buf, int len, unsigned int gup_flags)
 {
-	unsigned int flags = FOLL_FORCE;
-
-	if (write)
-		flags |= FOLL_WRITE;
-
-	return __access_remote_vm(NULL, mm, addr, buf, len, flags);
+	return __access_remote_vm(NULL, mm, addr, buf, len, gup_flags);
 }
 
 /*
diff --git a/mm/nommu.c b/mm/nommu.c
index bde7df3..93d5bb5 100644
--- a/mm/nommu.c
+++ b/mm/nommu.c
@@ -1847,15 +1847,14 @@ static int __access_remote_vm(struct task_struct *tsk, struct mm_struct *mm,
  * @addr:	start address to access
  * @buf:	source or destination buffer
  * @len:	number of bytes to transfer
- * @write:	whether the access is a write
+ * @gup_flags:	flags modifying lookup behaviour
  *
  * The caller must hold a reference on @mm.
  */
 int access_remote_vm(struct mm_struct *mm, unsigned long addr,
-		void *buf, int len, int write)
+		void *buf, int len, unsigned int gup_flags)
 {
-	return __access_remote_vm(NULL, mm, addr, buf, len,
-			write ? FOLL_WRITE : 0);
+	return __access_remote_vm(NULL, mm, addr, buf, len, gup_flags);
 }
 
 /*
-- 
2.10.0

^ permalink raw reply related

* [PATCH 10/10] mm: replace access_process_vm() write parameter with gup_flags
From: Lorenzo Stoakes @ 2016-10-13  0:20 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161013002020.3062-1-lstoakes@gmail.com>

This patch removes the write parameter from access_process_vm() and replaces it
with a gup_flags parameter as use of this function previously _implied_
FOLL_FORCE, whereas after this patch callers explicitly pass this flag.

We make this explicit as use of FOLL_FORCE can result in surprising behaviour
(and hence bugs) within the mm subsystem.

Signed-off-by: Lorenzo Stoakes <lstoakes@gmail.com>
---
 arch/alpha/kernel/ptrace.c         |  9 ++++++---
 arch/blackfin/kernel/ptrace.c      |  5 +++--
 arch/cris/arch-v32/kernel/ptrace.c |  4 ++--
 arch/ia64/kernel/ptrace.c          | 14 +++++++++-----
 arch/m32r/kernel/ptrace.c          | 15 ++++++++++-----
 arch/mips/kernel/ptrace32.c        |  5 +++--
 arch/powerpc/kernel/ptrace32.c     |  5 +++--
 arch/score/kernel/ptrace.c         | 10 ++++++----
 arch/sparc/kernel/ptrace_64.c      | 24 ++++++++++++++++--------
 arch/x86/kernel/step.c             |  3 ++-
 arch/x86/um/ptrace_32.c            |  3 ++-
 arch/x86/um/ptrace_64.c            |  3 ++-
 include/linux/mm.h                 |  3 ++-
 kernel/ptrace.c                    | 16 ++++++++++------
 mm/memory.c                        |  8 ++------
 mm/nommu.c                         |  6 +++---
 mm/util.c                          |  5 +++--
 17 files changed, 84 insertions(+), 54 deletions(-)

diff --git a/arch/alpha/kernel/ptrace.c b/arch/alpha/kernel/ptrace.c
index d9ee817..940dfb4 100644
--- a/arch/alpha/kernel/ptrace.c
+++ b/arch/alpha/kernel/ptrace.c
@@ -157,14 +157,16 @@ put_reg(struct task_struct *task, unsigned long regno, unsigned long data)
 static inline int
 read_int(struct task_struct *task, unsigned long addr, int * data)
 {
-	int copied = access_process_vm(task, addr, data, sizeof(int), 0);
+	int copied = access_process_vm(task, addr, data, sizeof(int),
+			FOLL_FORCE);
 	return (copied == sizeof(int)) ? 0 : -EIO;
 }
 
 static inline int
 write_int(struct task_struct *task, unsigned long addr, int data)
 {
-	int copied = access_process_vm(task, addr, &data, sizeof(int), 1);
+	int copied = access_process_vm(task, addr, &data, sizeof(int),
+			FOLL_FORCE | FOLL_WRITE);
 	return (copied == sizeof(int)) ? 0 : -EIO;
 }
 
@@ -281,7 +283,8 @@ long arch_ptrace(struct task_struct *child, long request,
 	/* When I and D space are separate, these will need to be fixed.  */
 	case PTRACE_PEEKTEXT: /* read word at location addr. */
 	case PTRACE_PEEKDATA:
-		copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
+		copied = access_process_vm(child, addr, &tmp, sizeof(tmp),
+				FOLL_FORCE);
 		ret = -EIO;
 		if (copied != sizeof(tmp))
 			break;
diff --git a/arch/blackfin/kernel/ptrace.c b/arch/blackfin/kernel/ptrace.c
index 8b8fe67..8d79286 100644
--- a/arch/blackfin/kernel/ptrace.c
+++ b/arch/blackfin/kernel/ptrace.c
@@ -271,7 +271,7 @@ long arch_ptrace(struct task_struct *child, long request,
 			case BFIN_MEM_ACCESS_CORE:
 			case BFIN_MEM_ACCESS_CORE_ONLY:
 				copied = access_process_vm(child, addr, &tmp,
-				                           to_copy, 0);
+							   to_copy, FOLL_FORCE);
 				if (copied)
 					break;
 
@@ -324,7 +324,8 @@ long arch_ptrace(struct task_struct *child, long request,
 			case BFIN_MEM_ACCESS_CORE:
 			case BFIN_MEM_ACCESS_CORE_ONLY:
 				copied = access_process_vm(child, addr, &data,
-				                           to_copy, 1);
+				                           to_copy,
+							   FOLL_FORCE | FOLL_WRITE);
 				break;
 			case BFIN_MEM_ACCESS_DMA:
 				if (safe_dma_memcpy(paddr, &data, to_copy))
diff --git a/arch/cris/arch-v32/kernel/ptrace.c b/arch/cris/arch-v32/kernel/ptrace.c
index f085229..f0df654 100644
--- a/arch/cris/arch-v32/kernel/ptrace.c
+++ b/arch/cris/arch-v32/kernel/ptrace.c
@@ -147,7 +147,7 @@ long arch_ptrace(struct task_struct *child, long request,
 				/* The trampoline page is globally mapped, no page table to traverse.*/
 				tmp = *(unsigned long*)addr;
 			} else {
-				copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
+				copied = access_process_vm(child, addr, &tmp, sizeof(tmp), FOLL_FORCE);
 
 				if (copied != sizeof(tmp))
 					break;
@@ -279,7 +279,7 @@ static int insn_size(struct task_struct *child, unsigned long pc)
   int opsize = 0;
 
   /* Read the opcode at pc (do what PTRACE_PEEKTEXT would do). */
-  copied = access_process_vm(child, pc, &opcode, sizeof(opcode), 0);
+  copied = access_process_vm(child, pc, &opcode, sizeof(opcode), FOLL_FORCE);
   if (copied != sizeof(opcode))
     return 0;
 
diff --git a/arch/ia64/kernel/ptrace.c b/arch/ia64/kernel/ptrace.c
index 6f54d51..31aa8c0 100644
--- a/arch/ia64/kernel/ptrace.c
+++ b/arch/ia64/kernel/ptrace.c
@@ -453,7 +453,7 @@ ia64_peek (struct task_struct *child, struct switch_stack *child_stack,
 			return 0;
 		}
 	}
-	copied = access_process_vm(child, addr, &ret, sizeof(ret), 0);
+	copied = access_process_vm(child, addr, &ret, sizeof(ret), FOLL_FORCE);
 	if (copied != sizeof(ret))
 		return -EIO;
 	*val = ret;
@@ -489,7 +489,8 @@ ia64_poke (struct task_struct *child, struct switch_stack *child_stack,
 				*ia64_rse_skip_regs(krbs, regnum) = val;
 			}
 		}
-	} else if (access_process_vm(child, addr, &val, sizeof(val), 1)
+	} else if (access_process_vm(child, addr, &val, sizeof(val),
+				FOLL_FORCE | FOLL_WRITE)
 		   != sizeof(val))
 		return -EIO;
 	return 0;
@@ -543,7 +544,8 @@ ia64_sync_user_rbs (struct task_struct *child, struct switch_stack *sw,
 		ret = ia64_peek(child, sw, user_rbs_end, addr, &val);
 		if (ret < 0)
 			return ret;
-		if (access_process_vm(child, addr, &val, sizeof(val), 1)
+		if (access_process_vm(child, addr, &val, sizeof(val),
+				FOLL_FORCE | FOLL_WRITE)
 		    != sizeof(val))
 			return -EIO;
 	}
@@ -559,7 +561,8 @@ ia64_sync_kernel_rbs (struct task_struct *child, struct switch_stack *sw,
 
 	/* now copy word for word from user rbs to kernel rbs: */
 	for (addr = user_rbs_start; addr < user_rbs_end; addr += 8) {
-		if (access_process_vm(child, addr, &val, sizeof(val), 0)
+		if (access_process_vm(child, addr, &val, sizeof(val),
+				FOLL_FORCE)
 				!= sizeof(val))
 			return -EIO;
 
@@ -1156,7 +1159,8 @@ arch_ptrace (struct task_struct *child, long request,
 	case PTRACE_PEEKTEXT:
 	case PTRACE_PEEKDATA:
 		/* read word@location addr */
-		if (access_process_vm(child, addr, &data, sizeof(data), 0)
+		if (access_process_vm(child, addr, &data, sizeof(data),
+				FOLL_FORCE)
 		    != sizeof(data))
 			return -EIO;
 		/* ensure return value is not mistaken for error code */
diff --git a/arch/m32r/kernel/ptrace.c b/arch/m32r/kernel/ptrace.c
index 51f5e9a..c145605 100644
--- a/arch/m32r/kernel/ptrace.c
+++ b/arch/m32r/kernel/ptrace.c
@@ -493,7 +493,8 @@ unregister_all_debug_traps(struct task_struct *child)
 	int i;
 
 	for (i = 0; i < p->nr_trap; i++)
-		access_process_vm(child, p->addr[i], &p->insn[i], sizeof(p->insn[i]), 1);
+		access_process_vm(child, p->addr[i], &p->insn[i], sizeof(p->insn[i]),
+				FOLL_FORCE | FOLL_WRITE);
 	p->nr_trap = 0;
 }
 
@@ -537,7 +538,8 @@ embed_debug_trap(struct task_struct *child, unsigned long next_pc)
 	unsigned long next_insn, code;
 	unsigned long addr = next_pc & ~3;
 
-	if (access_process_vm(child, addr, &next_insn, sizeof(next_insn), 0)
+	if (access_process_vm(child, addr, &next_insn, sizeof(next_insn),
+			FOLL_FORCE)
 	    != sizeof(next_insn)) {
 		return -1; /* error */
 	}
@@ -546,7 +548,8 @@ embed_debug_trap(struct task_struct *child, unsigned long next_pc)
 	if (register_debug_trap(child, next_pc, next_insn, &code)) {
 		return -1; /* error */
 	}
-	if (access_process_vm(child, addr, &code, sizeof(code), 1)
+	if (access_process_vm(child, addr, &code, sizeof(code),
+			FOLL_FORCE | FOLL_WRITE)
 	    != sizeof(code)) {
 		return -1; /* error */
 	}
@@ -562,7 +565,8 @@ withdraw_debug_trap(struct pt_regs *regs)
  	addr = (regs->bpc - 2) & ~3;
 	regs->bpc -= 2;
 	if (unregister_debug_trap(current, addr, &code)) {
-	    access_process_vm(current, addr, &code, sizeof(code), 1);
+	    access_process_vm(current, addr, &code, sizeof(code),
+		    FOLL_FORCE | FOLL_WRITE);
 	    invalidate_cache();
 	}
 }
@@ -589,7 +593,8 @@ void user_enable_single_step(struct task_struct *child)
 	/* Compute next pc.  */
 	pc = get_stack_long(child, PT_BPC);
 
-	if (access_process_vm(child, pc&~3, &insn, sizeof(insn), 0)
+	if (access_process_vm(child, pc&~3, &insn, sizeof(insn),
+			FOLL_FORCE)
 	    != sizeof(insn))
 		return;
 
diff --git a/arch/mips/kernel/ptrace32.c b/arch/mips/kernel/ptrace32.c
index 283b5a1..7e71a4e 100644
--- a/arch/mips/kernel/ptrace32.c
+++ b/arch/mips/kernel/ptrace32.c
@@ -70,7 +70,7 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
 			break;
 
 		copied = access_process_vm(child, (u64)addrOthers, &tmp,
-				sizeof(tmp), 0);
+				sizeof(tmp), FOLL_FORCE);
 		if (copied != sizeof(tmp))
 			break;
 		ret = put_user(tmp, (u32 __user *) (unsigned long) data);
@@ -179,7 +179,8 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
 			break;
 		ret = 0;
 		if (access_process_vm(child, (u64)addrOthers, &data,
-					sizeof(data), 1) == sizeof(data))
+					sizeof(data),
+					FOLL_FORCE | FOLL_WRITE) == sizeof(data))
 			break;
 		ret = -EIO;
 		break;
diff --git a/arch/powerpc/kernel/ptrace32.c b/arch/powerpc/kernel/ptrace32.c
index f52b7db3..010b7b3 100644
--- a/arch/powerpc/kernel/ptrace32.c
+++ b/arch/powerpc/kernel/ptrace32.c
@@ -74,7 +74,7 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
 			break;
 
 		copied = access_process_vm(child, (u64)addrOthers, &tmp,
-				sizeof(tmp), 0);
+				sizeof(tmp), FOLL_FORCE);
 		if (copied != sizeof(tmp))
 			break;
 		ret = put_user(tmp, (u32 __user *)data);
@@ -179,7 +179,8 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
 			break;
 		ret = 0;
 		if (access_process_vm(child, (u64)addrOthers, &tmp,
-					sizeof(tmp), 1) == sizeof(tmp))
+					sizeof(tmp),
+					FOLL_FORCE | FOLL_WRITE) == sizeof(tmp))
 			break;
 		ret = -EIO;
 		break;
diff --git a/arch/score/kernel/ptrace.c b/arch/score/kernel/ptrace.c
index 5583618..4f7314d 100644
--- a/arch/score/kernel/ptrace.c
+++ b/arch/score/kernel/ptrace.c
@@ -131,7 +131,7 @@ read_tsk_long(struct task_struct *child,
 {
 	int copied;
 
-	copied = access_process_vm(child, addr, res, sizeof(*res), 0);
+	copied = access_process_vm(child, addr, res, sizeof(*res), FOLL_FORCE);
 
 	return copied != sizeof(*res) ? -EIO : 0;
 }
@@ -142,7 +142,7 @@ read_tsk_short(struct task_struct *child,
 {
 	int copied;
 
-	copied = access_process_vm(child, addr, res, sizeof(*res), 0);
+	copied = access_process_vm(child, addr, res, sizeof(*res), FOLL_FORCE);
 
 	return copied != sizeof(*res) ? -EIO : 0;
 }
@@ -153,7 +153,8 @@ write_tsk_short(struct task_struct *child,
 {
 	int copied;
 
-	copied = access_process_vm(child, addr, &val, sizeof(val), 1);
+	copied = access_process_vm(child, addr, &val, sizeof(val),
+			FOLL_FORCE | FOLL_WRITE);
 
 	return copied != sizeof(val) ? -EIO : 0;
 }
@@ -164,7 +165,8 @@ write_tsk_long(struct task_struct *child,
 {
 	int copied;
 
-	copied = access_process_vm(child, addr, &val, sizeof(val), 1);
+	copied = access_process_vm(child, addr, &val, sizeof(val),
+			FOLL_FORCE | FOLL_WRITE);
 
 	return copied != sizeof(val) ? -EIO : 0;
 }
diff --git a/arch/sparc/kernel/ptrace_64.c b/arch/sparc/kernel/ptrace_64.c
index 9ddc492..ac082dd 100644
--- a/arch/sparc/kernel/ptrace_64.c
+++ b/arch/sparc/kernel/ptrace_64.c
@@ -127,7 +127,8 @@ static int get_from_target(struct task_struct *target, unsigned long uaddr,
 		if (copy_from_user(kbuf, (void __user *) uaddr, len))
 			return -EFAULT;
 	} else {
-		int len2 = access_process_vm(target, uaddr, kbuf, len, 0);
+		int len2 = access_process_vm(target, uaddr, kbuf, len,
+				FOLL_FORCE);
 		if (len2 != len)
 			return -EFAULT;
 	}
@@ -141,7 +142,8 @@ static int set_to_target(struct task_struct *target, unsigned long uaddr,
 		if (copy_to_user((void __user *) uaddr, kbuf, len))
 			return -EFAULT;
 	} else {
-		int len2 = access_process_vm(target, uaddr, kbuf, len, 1);
+		int len2 = access_process_vm(target, uaddr, kbuf, len,
+				FOLL_FORCE | FOLL_WRITE);
 		if (len2 != len)
 			return -EFAULT;
 	}
@@ -505,7 +507,8 @@ static int genregs32_get(struct task_struct *target,
 				if (access_process_vm(target,
 						      (unsigned long)
 						      &reg_window[pos],
-						      k, sizeof(*k), 0)
+						      k, sizeof(*k),
+						      FOLL_FORCE)
 				    != sizeof(*k))
 					return -EFAULT;
 				k++;
@@ -531,12 +534,14 @@ static int genregs32_get(struct task_struct *target,
 				if (access_process_vm(target,
 						      (unsigned long)
 						      &reg_window[pos],
-						      &reg, sizeof(reg), 0)
+						      &reg, sizeof(reg),
+						      FOLL_FORCE)
 				    != sizeof(reg))
 					return -EFAULT;
 				if (access_process_vm(target,
 						      (unsigned long) u,
-						      &reg, sizeof(reg), 1)
+						      &reg, sizeof(reg),
+						      FOLL_FORCE | FOLL_WRITE)
 				    != sizeof(reg))
 					return -EFAULT;
 				pos++;
@@ -615,7 +620,8 @@ static int genregs32_set(struct task_struct *target,
 						      (unsigned long)
 						      &reg_window[pos],
 						      (void *) k,
-						      sizeof(*k), 1)
+						      sizeof(*k),
+						      FOLL_FORCE | FOLL_WRITE)
 				    != sizeof(*k))
 					return -EFAULT;
 				k++;
@@ -642,13 +648,15 @@ static int genregs32_set(struct task_struct *target,
 				if (access_process_vm(target,
 						      (unsigned long)
 						      u,
-						      &reg, sizeof(reg), 0)
+						      &reg, sizeof(reg),
+						      FOLL_FORCE)
 				    != sizeof(reg))
 					return -EFAULT;
 				if (access_process_vm(target,
 						      (unsigned long)
 						      &reg_window[pos],
-						      &reg, sizeof(reg), 1)
+						      &reg, sizeof(reg),
+						      FOLL_FORCE | FOLL_WRITE)
 				    != sizeof(reg))
 					return -EFAULT;
 				pos++;
diff --git a/arch/x86/kernel/step.c b/arch/x86/kernel/step.c
index c9a0738..a23ce84 100644
--- a/arch/x86/kernel/step.c
+++ b/arch/x86/kernel/step.c
@@ -57,7 +57,8 @@ static int is_setting_trap_flag(struct task_struct *child, struct pt_regs *regs)
 	unsigned char opcode[15];
 	unsigned long addr = convert_ip_to_linear(child, regs);
 
-	copied = access_process_vm(child, addr, opcode, sizeof(opcode), 0);
+	copied = access_process_vm(child, addr, opcode, sizeof(opcode),
+			FOLL_FORCE);
 	for (i = 0; i < copied; i++) {
 		switch (opcode[i]) {
 		/* popf and iret */
diff --git a/arch/x86/um/ptrace_32.c b/arch/x86/um/ptrace_32.c
index 5766ead..60a5a5a 100644
--- a/arch/x86/um/ptrace_32.c
+++ b/arch/x86/um/ptrace_32.c
@@ -36,7 +36,8 @@ int is_syscall(unsigned long addr)
 		 * slow, but that doesn't matter, since it will be called only
 		 * in case of singlestepping, if copy_from_user failed.
 		 */
-		n = access_process_vm(current, addr, &instr, sizeof(instr), 0);
+		n = access_process_vm(current, addr, &instr, sizeof(instr),
+				FOLL_FORCE);
 		if (n != sizeof(instr)) {
 			printk(KERN_ERR "is_syscall : failed to read "
 			       "instruction from 0x%lx\n", addr);
diff --git a/arch/x86/um/ptrace_64.c b/arch/x86/um/ptrace_64.c
index 0b5c184..e30202b 100644
--- a/arch/x86/um/ptrace_64.c
+++ b/arch/x86/um/ptrace_64.c
@@ -212,7 +212,8 @@ int is_syscall(unsigned long addr)
 		 * slow, but that doesn't matter, since it will be called only
 		 * in case of singlestepping, if copy_from_user failed.
 		 */
-		n = access_process_vm(current, addr, &instr, sizeof(instr), 0);
+		n = access_process_vm(current, addr, &instr, sizeof(instr),
+				FOLL_FORCE);
 		if (n != sizeof(instr)) {
 			printk("is_syscall : failed to read instruction from "
 			       "0x%lx\n", addr);
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 3e5234e..7beda79 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -1266,7 +1266,8 @@ static inline int fixup_user_fault(struct task_struct *tsk,
 }
 #endif
 
-extern int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, int len, int write);
+extern int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, int len,
+		unsigned int gup_flags);
 extern int access_remote_vm(struct mm_struct *mm, unsigned long addr,
 		void *buf, int len, unsigned int gup_flags);
 
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index 2a99027..e6474f7 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -537,7 +537,7 @@ int ptrace_readdata(struct task_struct *tsk, unsigned long src, char __user *dst
 		int this_len, retval;
 
 		this_len = (len > sizeof(buf)) ? sizeof(buf) : len;
-		retval = access_process_vm(tsk, src, buf, this_len, 0);
+		retval = access_process_vm(tsk, src, buf, this_len, FOLL_FORCE);
 		if (!retval) {
 			if (copied)
 				break;
@@ -564,7 +564,8 @@ int ptrace_writedata(struct task_struct *tsk, char __user *src, unsigned long ds
 		this_len = (len > sizeof(buf)) ? sizeof(buf) : len;
 		if (copy_from_user(buf, src, this_len))
 			return -EFAULT;
-		retval = access_process_vm(tsk, dst, buf, this_len, 1);
+		retval = access_process_vm(tsk, dst, buf, this_len,
+				FOLL_FORCE | FOLL_WRITE);
 		if (!retval) {
 			if (copied)
 				break;
@@ -1127,7 +1128,7 @@ int generic_ptrace_peekdata(struct task_struct *tsk, unsigned long addr,
 	unsigned long tmp;
 	int copied;
 
-	copied = access_process_vm(tsk, addr, &tmp, sizeof(tmp), 0);
+	copied = access_process_vm(tsk, addr, &tmp, sizeof(tmp), FOLL_FORCE);
 	if (copied != sizeof(tmp))
 		return -EIO;
 	return put_user(tmp, (unsigned long __user *)data);
@@ -1138,7 +1139,8 @@ int generic_ptrace_pokedata(struct task_struct *tsk, unsigned long addr,
 {
 	int copied;
 
-	copied = access_process_vm(tsk, addr, &data, sizeof(data), 1);
+	copied = access_process_vm(tsk, addr, &data, sizeof(data),
+			FOLL_FORCE | FOLL_WRITE);
 	return (copied == sizeof(data)) ? 0 : -EIO;
 }
 
@@ -1155,7 +1157,8 @@ int compat_ptrace_request(struct task_struct *child, compat_long_t request,
 	switch (request) {
 	case PTRACE_PEEKTEXT:
 	case PTRACE_PEEKDATA:
-		ret = access_process_vm(child, addr, &word, sizeof(word), 0);
+		ret = access_process_vm(child, addr, &word, sizeof(word),
+				FOLL_FORCE);
 		if (ret != sizeof(word))
 			ret = -EIO;
 		else
@@ -1164,7 +1167,8 @@ int compat_ptrace_request(struct task_struct *child, compat_long_t request,
 
 	case PTRACE_POKETEXT:
 	case PTRACE_POKEDATA:
-		ret = access_process_vm(child, addr, &data, sizeof(data), 1);
+		ret = access_process_vm(child, addr, &data, sizeof(data),
+				FOLL_FORCE | FOLL_WRITE);
 		ret = (ret != sizeof(data) ? -EIO : 0);
 		break;
 
diff --git a/mm/memory.c b/mm/memory.c
index bac2d99..e18c57b 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -3951,20 +3951,16 @@ int access_remote_vm(struct mm_struct *mm, unsigned long addr,
  * Do not walk the page table directly, use get_user_pages
  */
 int access_process_vm(struct task_struct *tsk, unsigned long addr,
-		void *buf, int len, int write)
+		void *buf, int len, unsigned int gup_flags)
 {
 	struct mm_struct *mm;
 	int ret;
-	unsigned int flags = FOLL_FORCE;
 
 	mm = get_task_mm(tsk);
 	if (!mm)
 		return 0;
 
-	if (write)
-		flags |= FOLL_WRITE;
-
-	ret = __access_remote_vm(tsk, mm, addr, buf, len, flags);
+	ret = __access_remote_vm(tsk, mm, addr, buf, len, gup_flags);
 
 	mmput(mm);
 
diff --git a/mm/nommu.c b/mm/nommu.c
index 93d5bb5..db5fd17 100644
--- a/mm/nommu.c
+++ b/mm/nommu.c
@@ -1861,7 +1861,8 @@ int access_remote_vm(struct mm_struct *mm, unsigned long addr,
  * Access another process' address space.
  * - source/target buffer must be kernel space
  */
-int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, int len, int write)
+int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, int len,
+		unsigned int gup_flags)
 {
 	struct mm_struct *mm;
 
@@ -1872,8 +1873,7 @@ int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, in
 	if (!mm)
 		return 0;
 
-	len = __access_remote_vm(tsk, mm, addr, buf, len,
-			write ? FOLL_WRITE : 0);
+	len = __access_remote_vm(tsk, mm, addr, buf, len, gup_flags);
 
 	mmput(mm);
 	return len;
diff --git a/mm/util.c b/mm/util.c
index 4c685bd..952cbe7 100644
--- a/mm/util.c
+++ b/mm/util.c
@@ -624,7 +624,7 @@ int get_cmdline(struct task_struct *task, char *buffer, int buflen)
 	if (len > buflen)
 		len = buflen;
 
-	res = access_process_vm(task, arg_start, buffer, len, 0);
+	res = access_process_vm(task, arg_start, buffer, len, FOLL_FORCE);
 
 	/*
 	 * If the nul at the end of args has been overwritten, then
@@ -639,7 +639,8 @@ int get_cmdline(struct task_struct *task, char *buffer, int buflen)
 			if (len > buflen - res)
 				len = buflen - res;
 			res += access_process_vm(task, env_start,
-						 buffer+res, len, 0);
+						 buffer+res, len,
+						 FOLL_FORCE);
 			res = strnlen(buffer, res);
 		}
 	}
-- 
2.10.0

^ permalink raw reply related

* [PATCH v7 2/8] power: add power sequence library
From: Peter Chen @ 2016-10-13  1:22 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <4691591.qnCGxrEkrZ@phil>

On Wed, Oct 12, 2016 at 12:30:29PM +0200, Heiko Stuebner wrote:
> Hi,
> 
> Am Dienstag, 20. September 2016, 11:36:41 CEST schrieb Peter Chen:
> > We have an well-known problem that the device needs to do some power
> > sequence before it can be recognized by related host, the typical
> > example like hard-wired mmc devices and usb devices.
> > 
> > This power sequence is hard to be described at device tree and handled by
> > related host driver, so we have created a common power sequence
> > library to cover this requirement. The core code has supplied
> > some common helpers for host driver, and individual power sequence
> > libraries handle kinds of power sequence for devices.
> > 
> > pwrseq_generic is intended for general purpose of power sequence, which
> > handles gpios and clocks currently, and can cover regulator and pinctrl
> > in future. The host driver just needs to call of_pwrseq_on/of_pwrseq_off
> > if only one power sequence is needed, else call of_pwrseq_on_list
> > /of_pwrseq_off_list instead (eg, USB hub driver).
> >
> > Signed-off-by: Peter Chen <peter.chen@nxp.com>
> > Tested-by Joshua Clayton <stillcompiling@gmail.com>
> > Reviewed-by: Matthias Kaehlcke <mka@chromium.org>
> > Tested-by: Matthias Kaehlcke <mka@chromium.org>
> 
> first of all, glad to see this move forward. I've only some qualms with the 
> static number of allocated power sequences below.
> 

Thanks for commenting it, the preallocate way is not a good way, but I
can't find suitable way. See below comments.

> > +static int __init pwrseq_generic_register(void)
> > +{
> > +	struct pwrseq_generic *pwrseq_gen;
> > +	int i;
> > +
> > +	for (i = 0; i < CONFIG_PWRSEQ_GENERIC_INSTANCE_NUMBER; i++) {
> > +		pwrseq_gen = kzalloc(sizeof(*pwrseq_gen), GFP_KERNEL);
> > +		if (!pwrseq_gen)
> > +			return -ENOMEM;
> > +
> > +		pwrseq_gen->pwrseq.pwrseq_of_match_table = generic_id_table;
> > +		pwrseq_gen->pwrseq.get = pwrseq_generic_get;
> > +		pwrseq_gen->pwrseq.on = pwrseq_generic_on;
> > +		pwrseq_gen->pwrseq.off = pwrseq_generic_off;
> > +		pwrseq_gen->pwrseq.put = pwrseq_generic_put;
> > +		pwrseq_gen->pwrseq.free = pwrseq_generic_free;
> > +
> > +		pwrseq_register(&pwrseq_gen->pwrseq);
> > +	}
> > +
> > +	return 0;
> > +}
> > +postcore_initcall(pwrseq_generic_register)
> 
> I see that you need to have it preallocated for the compatible matching, but 
> wouldn't it also work to either just register the type and allocate 
> dynamically or otherwise just allocate a new spare everytime 
> pwrseq_generic_get() picks up the previous spare?

Before compatible matching, the host driver doesn't know which pwrseq type
for its child node, then doesn't know which pwrseq instance needs to be
allocated. From dts, we don't know which pwrseq type for the node.

-- 

Best Regards,
Peter Chen

^ permalink raw reply

* [PATCH v2] drm/bridge: analogix: protect power when get_modes or detect
From: Mark Yao @ 2016-10-13  1:50 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1476266442-29803-1-git-send-email-mark.yao@rock-chips.com>

The drm callback ->detect and ->get_modes seems is not power safe,
they may be called when device is power off, do register access on
detect or get_modes will cause system die.

Here is the path call ->detect before analogix_dp power on
[<ffffff800843babc>] analogix_dp_detect+0x44/0xdc
[<ffffff80083fd840>] drm_helper_probe_single_connector_modes_merge_bits+0xe8/0x41c
[<ffffff80083fdb84>] drm_helper_probe_single_connector_modes+0x10/0x18
[<ffffff8008418d24>] drm_mode_getconnector+0xf4/0x304
[<ffffff800840cff0>] drm_ioctl+0x23c/0x390
[<ffffff80081a8adc>] do_vfs_ioctl+0x4b8/0x58c
[<ffffff80081a8c10>] SyS_ioctl+0x60/0x88

Cc: Inki Dae <inki.dae@samsung.com>
Cc: Sean Paul <seanpaul@chromium.org>
Cc: Gustavo Padovan <gustavo.padovan@collabora.co.uk>
Cc: "Ville Syrj?l?" <ville.syrjala@linux.intel.com>

Signed-off-by: Mark Yao <mark.yao@rock-chips.com>
---
Changes in v2:
- remove sub device power on/off callback, use pm_runtime_get/put is enough
to fix my problem, so will can avoid race to dpms.

 drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
index efac8ab..ff2d328 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
@@ -1062,11 +1062,15 @@ int analogix_dp_get_modes(struct drm_connector *connector)
 		return 0;
 	}
 
+	pm_runtime_get_sync(dp->dev);
+
 	if (analogix_dp_handle_edid(dp) == 0) {
 		drm_mode_connector_update_edid_property(&dp->connector, edid);
 		num_modes += drm_add_edid_modes(&dp->connector, edid);
 	}
 
+	pm_runtime_put(dp->dev);
+
 	if (dp->plat_data->panel)
 		num_modes += drm_panel_get_modes(dp->plat_data->panel);
 
@@ -1106,9 +1110,13 @@ analogix_dp_detect(struct drm_connector *connector, bool force)
 		return connector_status_disconnected;
 	}
 
+	pm_runtime_get_sync(dp->dev);
+
 	if (!analogix_dp_detect_hpd(dp))
 		status = connector_status_connected;
 
+	pm_runtime_put(dp->dev);
+
 	ret = analogix_dp_prepare_panel(dp, false, false);
 	if (ret)
 		DRM_ERROR("Failed to unprepare panel (%d)\n", ret);
-- 
1.9.1

^ permalink raw reply related

* Question: Re: [PATCH] drm/bridge: analogix: protect power when get_modes or detect
From: Mark yao @ 2016-10-13  1:56 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAOw6vbKe0d66O1MymP24UQ-M3EmdR+=pZVWmgZG9iVm5x1Arrg@mail.gmail.com>

On 2016?10?12? 22:51, Sean Paul wrote:
> On Wed, Oct 12, 2016 at 6:22 AM, Mark yao <mark.yao@rock-chips.com> wrote:
>> I'm not familiar with the analogix driver, maybe use a power reference count
>> would better then direct power on/off analogix_dp.
>>
>> Does anyone has the idea to protect detect and get_modes context?
>>
> I'm not sure a reference count is going to help here. The common
> pattern is to call detect() followed by get_modes() and then modeset.
> However, it's not guaranteed that any one of those functions will be
> called after the other. So, if you leave things on after detect or
> get_modes, you might be wasting power (or worse).
>
> I recently ran into this exact problem with a panel we're using. Check
> out "0b8b059a7: drm/bridge: analogix_dp: Ensure the panel is properly
> prepared/unprepared". Perhaps you can piggyback on that function to
> add your pm_runtime and plat_data callbacks (since using dpms_mode
> might be racey).
>
> Sean
Hi Sean

Thanks for your advice.

I re-test the detect and get_modes, only use pm_runtime_get/put also can 
fix my problem.
without plat_data callbacks can avoid race to dpms_mode.

I had send v2 patch,  you can review it.

Thanks.

>
>> I found many other connector driver also direct access register on detect or
>> get_modes, no problem for it?
>>
>> On 2016?10?12? 18:00, Mark Yao wrote:
>>
>> The drm callback ->detect and ->get_modes seems is not power safe,
>> they may be called when device is power off, do register access on
>> detect or get_modes will cause system die.
>>
>> Here is the path call ->detect before analogix_dp power on
>> [<ffffff800843babc>] analogix_dp_detect+0x44/0xdc
>> [<ffffff80083fd840>]
>> drm_helper_probe_single_connector_modes_merge_bits+0xe8/0x41c
>> [<ffffff80083fdb84>] drm_helper_probe_single_connector_modes+0x10/0x18
>> [<ffffff8008418d24>] drm_mode_getconnector+0xf4/0x304
>> [<ffffff800840cff0>] drm_ioctl+0x23c/0x390
>> [<ffffff80081a8adc>] do_vfs_ioctl+0x4b8/0x58c
>> [<ffffff80081a8c10>] SyS_ioctl+0x60/0x88
>>
>> Cc: Inki Dae <inki.dae@samsung.com>
>> Cc: Sean Paul <seanpaul@chromium.org>
>> Cc: Gustavo Padovan <gustavo.padovan@collabora.co.uk>
>> Cc: "Ville Syrj?l?" <ville.syrjala@linux.intel.com>
>>
>> Signed-off-by: Mark Yao <mark.yao@rock-chips.com>
>> ---
>>   drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 28
>> ++++++++++++++++++++++
>>   1 file changed, 28 insertions(+)
>>
>> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
>> b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
>> index efac8ab..09dece2 100644
>> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
>> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
>> @@ -1062,6 +1062,13 @@ int analogix_dp_get_modes(struct drm_connector
>> *connector)
>>    return 0;
>>    }
>>
>> + if (dp->dpms_mode != DRM_MODE_DPMS_ON) {
>> + pm_runtime_get_sync(dp->dev);
>> +
>> + if (dp->plat_data->power_on)
>> + dp->plat_data->power_on(dp->plat_data);
>> + }
>> +
>>    if (analogix_dp_handle_edid(dp) == 0) {
>>    drm_mode_connector_update_edid_property(&dp->connector, edid);
>>    num_modes += drm_add_edid_modes(&dp->connector, edid);
>> @@ -1073,6 +1080,13 @@ int analogix_dp_get_modes(struct drm_connector
>> *connector)
>>    if (dp->plat_data->get_modes)
>>    num_modes += dp->plat_data->get_modes(dp->plat_data, connector);
>>
>> + if (dp->dpms_mode != DRM_MODE_DPMS_ON) {
>> + if (dp->plat_data->power_off)
>> + dp->plat_data->power_off(dp->plat_data);
>> +
>> + pm_runtime_put_sync(dp->dev);
>> + }
>> +
>>    ret = analogix_dp_prepare_panel(dp, false, false);
>>    if (ret)
>>    DRM_ERROR("Failed to unprepare panel (%d)\n", ret);
>> @@ -1106,9 +1120,23 @@ analogix_dp_detect(struct drm_connector *connector,
>> bool force)
>>    return connector_status_disconnected;
>>    }
>>
>> + if (dp->dpms_mode != DRM_MODE_DPMS_ON) {
>> + pm_runtime_get_sync(dp->dev);
>> +
>> + if (dp->plat_data->power_on)
>> + dp->plat_data->power_on(dp->plat_data);
>> + }
>> +
>>    if (!analogix_dp_detect_hpd(dp))
>>    status = connector_status_connected;
>>
>> + if (dp->dpms_mode != DRM_MODE_DPMS_ON) {
>> + if (dp->plat_data->power_off)
>> + dp->plat_data->power_off(dp->plat_data);
>> +
>> + pm_runtime_put_sync(dp->dev);
>> + }
>> +
>>    ret = analogix_dp_prepare_panel(dp, false, false);
>>    if (ret)
>>    DRM_ERROR("Failed to unprepare panel (%d)\n", ret);
>>
>>
>>
>> --
>> ?ark Yao
>
>


-- 
?ark Yao

^ permalink raw reply

* [PATCH v3 3/6] pwm: imx: support output polarity inversion
From: Lukasz Majewski @ 2016-10-13  4:58 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <ff9ba34df2729204c617685dd26ea625@agner.ch>

Hi Stefan,

> On 2016-10-12 15:15, Lukasz Majewski wrote:
> > Hi Stefan,
> > 
> >> On 2016-10-07 08:11, Bhuvanchandra DV wrote:
> >> > From: Lothar Wassmann <LW@KARO-electronics.de>
> >> >
> >> > The i.MX pwm unit on i.MX27 and newer SoCs provides a
> >> > configurable output polarity. This patch adds support to utilize
> >> > this feature where available.
> >> >
> >> > Signed-off-by: Lothar Wa?mann <LW@KARO-electronics.de>
> >> > Signed-off-by: Lukasz Majewski <l.majewski@samsung.com>
> >> > Signed-off-by: Bhuvanchandra DV <bhuvanchandra.dv@toradex.com>
> >> > Acked-by: Shawn Guo <shawn.guo@linaro.org>
> >> > Reviewed-by: Sascha Hauer <s.hauer@pengutronix.de>
> >> > ---
> >> >  Documentation/devicetree/bindings/pwm/imx-pwm.txt |  6 +--
> >> >  drivers/pwm/pwm-imx.c                             | 51
> >> > +++++++++++++++++++++-- 2 files changed, 51 insertions(+), 6
> >> > deletions(-)
> >> >
> >> > diff --git a/Documentation/devicetree/bindings/pwm/imx-pwm.txt
> >> > b/Documentation/devicetree/bindings/pwm/imx-pwm.txt
> >> > index e00c2e9..c61bdf8 100644
> >> > --- a/Documentation/devicetree/bindings/pwm/imx-pwm.txt
> >> > +++ b/Documentation/devicetree/bindings/pwm/imx-pwm.txt
> >> > @@ -6,8 +6,8 @@ Required properties:
> >> >    - "fsl,imx1-pwm" for PWM compatible with the one integrated on
> >> > i.MX1
> >> >    - "fsl,imx27-pwm" for PWM compatible with the one integrated
> >> > on i.MX27
> >> >  - reg: physical base address and length of the controller's
> >> > registers -- #pwm-cells: should be 2. See pwm.txt in this
> >> > directory for a description of
> >> > -  the cells format.
> >> > +- #pwm-cells: 2 for i.MX1 and 3 for i.MX27 and newer SoCs. See
> >> > pwm.txt
> >> > +  in this directory for a description of the cells format.
> >> >  - clocks : Clock specifiers for both ipg and per clocks.
> >> >  - clock-names : Clock names should include both "ipg" and "per"
> >> >  See the clock consumer binding,
> >> > @@ -17,7 +17,7 @@ See the clock consumer binding,
> >> >  Example:
> >> >
> >> >  pwm1: pwm at 53fb4000 {
> >> > -	#pwm-cells = <2>;
> >> > +	#pwm-cells = <3>;
> >> >  	compatible = "fsl,imx53-pwm", "fsl,imx27-pwm";
> >> >  	reg = <0x53fb4000 0x4000>;
> >> >  	clocks = <&clks IMX5_CLK_PWM1_IPG_GATE>,
> >> > diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c
> >> > index d600fd5..c37d223 100644
> >> > --- a/drivers/pwm/pwm-imx.c
> >> > +++ b/drivers/pwm/pwm-imx.c
> >> > @@ -38,6 +38,7 @@
> >> >  #define MX3_PWMCR_DOZEEN		(1 << 24)
> >> >  #define MX3_PWMCR_WAITEN		(1 << 23)
> >> >  #define MX3_PWMCR_DBGEN			(1 << 22)
> >> > +#define MX3_PWMCR_POUTC			(1 << 18)
> >> >  #define MX3_PWMCR_CLKSRC_IPG_HIGH	(2 << 16)
> >> >  #define MX3_PWMCR_CLKSRC_IPG		(1 << 16)
> >> >  #define MX3_PWMCR_SWR			(1 << 3)
> >> > @@ -180,6 +181,9 @@ static int imx_pwm_config_v2(struct pwm_chip
> >> > *chip, if (enable)
> >> >  		cr |= MX3_PWMCR_EN;
> >> >
> >> > +	if (pwm->args.polarity == PWM_POLARITY_INVERSED)
> >> > +		cr |= MX3_PWMCR_POUTC;
> >> > +
> >>
> >> This seems wrong to me, the config callback is meant for
> >> period/duty cycle only.
> > 
> > If it is meant only for that, then the polarity should be removed
> > from it.
> > 
> > However after very quick testing, at least on my setup, it turns out
> > that removing this lines causes polarity to _not_ being set (and the
> > polarity is not inverted).
> > 
> > I will investigate this further on my setup and hopefully sent
> > proper patch.
> > 
> >> The set_polarity callback should get called in case a
> >> different polarity is requested.
> > 
> > On my setup the pwm2 is set from DT and pwm_backlight_probe() calls
> > pwm_apply_args(), so everything should work. However, as I mentioned
> > above there still is some problem with inversion setting.
> > 
> >>
> >>
> >> >  	writel(cr, imx->mmio_base + MX3_PWMCR);
> >> >
> >> >  	return 0;
> >> > @@ -240,27 +244,62 @@ static void imx_pwm_disable(struct pwm_chip
> >> > *chip, struct pwm_device *pwm)
> >> >  	clk_disable_unprepare(imx->clk_per);
> >> >  }
> >> >
> >> > -static struct pwm_ops imx_pwm_ops = {
> >> > +static int imx_pwm_set_polarity(struct pwm_chip *chip, struct
> >> > pwm_device *pwm,
> >> > +				enum pwm_polarity polarity)
> >> > +{
> >> > +	struct imx_chip *imx = to_imx_chip(chip);
> >> > +	u32 val;
> >> > +
> >> > +	if (polarity == pwm->args.polarity)
> >> > +		return 0;
> >>
> >> I don't think that this is right. Today, pwm_apply_args (in
> >> include/linux/pwm.h) copies the polarity from args to
> >> state.polarity, which is then passed as polarity argument to this
> >> function. So this will always return 0 afaict.
> > 
> > Yes, I've overlooked it (that the state is copied).
> > 
> > It can be dropped.
> 
> Did you do the above test with that line dropped?

Yes. The above code has been also removed.

Best regards,
?ukasz Majewski

> 
> > 
> >>
> >> I would just drop that.
> >>
> >> There is probably one little problem in the current state of
> >> affairs: If the bootloader makes use of a PWM channel with
> >> inverted state, then the kernel would not know about that and
> >> currently assume a wrong initial state... I guess at one point in
> >> time we should implement the state retrieval callback and move to
> >> the new atomic PWM API, which would mean to implement apply
> >> callback.
> > 
> > Are there any patches on the horizon?
> > 
> 
> Not that I know of...
> 
> --
> Stefan
> 
> >>
> >> --
> >> Stefan
> >>
> >>
> >> > +
> >> > +	val = readl(imx->mmio_base + MX3_PWMCR);
> >> > +
> >> > +	if (polarity == PWM_POLARITY_INVERSED)
> >> > +		val |= MX3_PWMCR_POUTC;
> >> > +	else
> >> > +		val &= ~MX3_PWMCR_POUTC;
> >> > +
> >> > +	writel(val, imx->mmio_base + MX3_PWMCR);
> >> > +
> >> > +	dev_dbg(imx->chip.dev, "%s: polarity set to %s\n",
> >> > __func__,
> >> > +		polarity == PWM_POLARITY_INVERSED ? "inverted" :
> >> > "normal"); +
> >> > +	return 0;
> >> > +}
> >> > +
> >> > +static struct pwm_ops imx_pwm_ops_v1 = {
> >> >  	.enable = imx_pwm_enable,
> >> >  	.disable = imx_pwm_disable,
> >> >  	.config = imx_pwm_config,
> >> >  	.owner = THIS_MODULE,
> >> >  };
> >> >
> >> > +static struct pwm_ops imx_pwm_ops_v2 = {
> >> > +	.enable = imx_pwm_enable,
> >> > +	.disable = imx_pwm_disable,
> >> > +	.set_polarity = imx_pwm_set_polarity,
> >> > +	.config = imx_pwm_config,
> >> > +	.owner = THIS_MODULE,
> >> > +};
> >> > +
> >> >  struct imx_pwm_data {
> >> >  	int (*config)(struct pwm_chip *chip,
> >> >  		struct pwm_device *pwm, int duty_ns, int
> >> > period_ns); void (*set_enable)(struct pwm_chip *chip, bool
> >> > enable);
> >> > +	struct pwm_ops *pwm_ops;
> >> >  };
> >> >
> >> >  static struct imx_pwm_data imx_pwm_data_v1 = {
> >> >  	.config = imx_pwm_config_v1,
> >> >  	.set_enable = imx_pwm_set_enable_v1,
> >> > +	.pwm_ops = &imx_pwm_ops_v1,
> >> >  };
> >> >
> >> >  static struct imx_pwm_data imx_pwm_data_v2 = {
> >> >  	.config = imx_pwm_config_v2,
> >> >  	.set_enable = imx_pwm_set_enable_v2,
> >> > +	.pwm_ops = &imx_pwm_ops_v2,
> >> >  };
> >> >
> >> >  static const struct of_device_id imx_pwm_dt_ids[] = {
> >> > @@ -282,6 +321,8 @@ static int imx_pwm_probe(struct
> >> > platform_device *pdev) if (!of_id)
> >> >  		return -ENODEV;
> >> >
> >> > +	data = of_id->data;
> >> > +
> >> >  	imx = devm_kzalloc(&pdev->dev, sizeof(*imx),
> >> > GFP_KERNEL); if (imx == NULL)
> >> >  		return -ENOMEM;
> >> > @@ -300,18 +341,22 @@ static int imx_pwm_probe(struct
> >> > platform_device *pdev) return PTR_ERR(imx->clk_ipg);
> >> >  	}
> >> >
> >> > -	imx->chip.ops = &imx_pwm_ops;
> >> > +	imx->chip.ops = data->pwm_ops;
> >> >  	imx->chip.dev = &pdev->dev;
> >> >  	imx->chip.base = -1;
> >> >  	imx->chip.npwm = 1;
> >> >  	imx->chip.can_sleep = true;
> >> > +	if (data->pwm_ops->set_polarity) {
> >> > +		dev_dbg(&pdev->dev, "PWM supports output
> >> > inversion\n");
> >> > +		imx->chip.of_xlate = of_pwm_xlate_with_flags;
> >> > +		imx->chip.of_pwm_n_cells = 3;
> >> > +	}
> >> >
> >> >  	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> >> >  	imx->mmio_base = devm_ioremap_resource(&pdev->dev, r);
> >> >  	if (IS_ERR(imx->mmio_base))
> >> >  		return PTR_ERR(imx->mmio_base);
> >> >
> >> > -	data = of_id->data;
> >> >  	imx->config = data->config;
> >> >  	imx->set_enable = data->set_enable;
> >>
> > 
> > Best regards,
> > 
> > ?ukasz Majewski

-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 181 bytes
Desc: OpenPGP digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161013/03447ea2/attachment-0001.sig>

^ permalink raw reply

* [PATCH 0/3] Add Mediatek CIRQ interrupt controller
From: Youlin Pei @ 2016-10-13  5:06 UTC (permalink / raw)
  To: linux-arm-kernel

In Mediatek SOCs, the CIRQ is a low power interrupt controller designed to
works outside MCUSYS which comprises with Cortex-Ax cores,CCI and GIC.

The CIRQ controller is integrated in between MCUSYS and interrupt sources
as the second level interrupt controller. The external interrupts which 
outside MCUSYS will feed through CIRQ then bypass to GIC.

In normal mode(where MCUSYS is active), CIRQ is disabled and interrupts
will directly issue to MCUSYS. When MCUSYS enters sleep mode, where GIC
is power downed. CIRQ will be enabled and monitor all edge trigger
interrupts(only edge trigger interrupts will be lost in this scenario).
When an edge interrupt is triggered, CIRQ will record the status and
generated a pulse signal to GIC when flush command is executed. 

With CIRQ, MCUSYS can be completely turned off to improve the system 
power consumption without losing interrupts.

Youlin Pei (3):
  binding: irqchip: mtk-cirq: Add binding document
  irqchip: mtk-cirq: Add mediatek mtk-cirq implement
  ARM: dts: mt2701: Add mtk-cirq node for mt2701

 .../interrupt-controller/mediatek,cirq.txt         |  30 +++
 arch/arm/boot/dts/mt2701.dtsi                      |  11 +-
 drivers/irqchip/Makefile                           |   2 +-
 drivers/irqchip/irq-mtk-cirq.c                     | 257 +++++++++++++++++++++
 4 files changed, 298 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/interrupt-controller/mediatek,cirq.txt
 create mode 100644 drivers/irqchip/irq-mtk-cirq.c

-- 
1.9.1 

^ permalink raw reply

* [PATCH 1/3] binding: irqchip: mtk-cirq: Add binding document
From: Youlin Pei @ 2016-10-13  5:06 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1476335194-26604-1-git-send-email-youlin.pei@mediatek.com>

This commit adds the device tree binding document for
the mediatek cirq.

Signed-off-by: Youlin Pei <youlin.pei@mediatek.com>

---
base on v4.8-rc1
---
 .../interrupt-controller/mediatek,cirq.txt         |   30 ++++++++++++++++++++
 1 file changed, 30 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/interrupt-controller/mediatek,cirq.txt

diff --git a/Documentation/devicetree/bindings/interrupt-controller/mediatek,cirq.txt b/Documentation/devicetree/bindings/interrupt-controller/mediatek,cirq.txt
new file mode 100644
index 0000000..ad16953
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/mediatek,cirq.txt
@@ -0,0 +1,30 @@
+* Mediatek 27xx cirq
+
+In Mediatek SOCs, the CIRQ is a low power interrupt controller designed to
+works outside MCUSYS which comprises with Cortex-Ax cores,CCI and GIC.
+The external interrupts (outside MCUSYS) will feed through CIRQ and connect
+to GIC in MCUSYS. When CIRQ is enabled, it will record the edge-sensitive
+interrupts and generated a pulse signal to parent interrupt controller when
+flush command is executed. With CIRQ, MCUSYS can be completely turned off
+to improve the system power consumption without losing interrupts.
+
+Required properties:
+- compatible: should be: "mediatek,mt2701-cirq".
+- interrupt-controller : Identifies the node as an interrupt controller.
+- #interrupt-cells : Use the same format as specified by GIC in arm,gic.txt.
+- interrupt-parent: phandle of irq parent for cirq. The parent must
+  use the same interrupt-cells format as GIC.
+- reg: Physical base address of the cirq registers and length of memory
+  mapped region.
+- mediatek,ext-irq-start: Identifies external irq start number in different
+  SOCs.
+
+Example:
+	cirq: interrupt-controller at 10204000 {
+		compatible = "mediatek,mt2701-cirq";
+		interrupt-controller;
+		#interrupt-cells = <3>;
+		interrupt-parent = <&sysirq>;
+		reg = <0 0x10204000 0 0x4000>;
+		mediatek,ext-irq-start = <32>;
+	};
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 2/3] irqchip: mtk-cirq: Add mediatek mtk-cirq implement
From: Youlin Pei @ 2016-10-13  5:06 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1476335194-26604-1-git-send-email-youlin.pei@mediatek.com>

This commit add the mtk-cirq implement for mt2701.

Signed-off-by: Youlin Pei <youlin.pei@mediatek.com>
---
 drivers/irqchip/Makefile       |    2 +-
 drivers/irqchip/irq-mtk-cirq.c |  257 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 258 insertions(+), 1 deletion(-)
 create mode 100644 drivers/irqchip/irq-mtk-cirq.c

diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 4c203b6..eee95c6 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -59,7 +59,7 @@ obj-$(CONFIG_BCM7120_L2_IRQ)		+= irq-bcm7120-l2.o
 obj-$(CONFIG_BRCMSTB_L2_IRQ)		+= irq-brcmstb-l2.o
 obj-$(CONFIG_KEYSTONE_IRQ)		+= irq-keystone.o
 obj-$(CONFIG_MIPS_GIC)			+= irq-mips-gic.o
-obj-$(CONFIG_ARCH_MEDIATEK)		+= irq-mtk-sysirq.o
+obj-$(CONFIG_ARCH_MEDIATEK)		+= irq-mtk-sysirq.o irq-mtk-cirq.o
 obj-$(CONFIG_ARCH_DIGICOLOR)		+= irq-digicolor.o
 obj-$(CONFIG_RENESAS_H8300H_INTC)	+= irq-renesas-h8300h.o
 obj-$(CONFIG_RENESAS_H8S_INTC)		+= irq-renesas-h8s.o
diff --git a/drivers/irqchip/irq-mtk-cirq.c b/drivers/irqchip/irq-mtk-cirq.c
new file mode 100644
index 0000000..544767d
--- /dev/null
+++ b/drivers/irqchip/irq-mtk-cirq.c
@@ -0,0 +1,257 @@
+/*
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Youlin.Pei <youlin.pei@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/irqdomain.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/syscore_ops.h>
+
+#define CIRQ_MASK_SET	0xC0
+#define CIRQ_MASK_CLR	0x100
+#define CIRQ_SENS_SET	0x180
+#define CIRQ_SENS_CLR	0x1C0
+#define CIRQ_POL_SET	0x240
+#define CIRQ_POL_CLR	0x280
+#define CIRQ_CONTROL	0x300
+
+#define CIRQ_EN		0x1
+#define CIRQ_EDGE	0x2
+#define CIRQ_FLUSH	0x4
+
+#define CIRQ_IRQ_NUM    0x200
+
+struct mtk_cirq_chip_data {
+	void __iomem *base;
+	unsigned int ext_irq_start;
+};
+
+struct mtk_cirq_chip_data *cirq_data;
+
+static void mtk_cirq_write_mask(struct irq_data *data, unsigned int offset)
+{
+	struct mtk_cirq_chip_data *chip_data = data->chip_data;
+	unsigned int cirq_num = data->hwirq - chip_data->ext_irq_start;
+	u32 mask = 1 << (cirq_num % 32);
+
+	writel(mask, chip_data->base + offset + (cirq_num / 32) * 4);
+}
+
+static void mtk_cirq_mask(struct irq_data *data)
+{
+	mtk_cirq_write_mask(data, CIRQ_MASK_SET);
+	irq_chip_mask_parent(data);
+}
+
+static void mtk_cirq_unmask(struct irq_data *data)
+{
+	mtk_cirq_write_mask(data, CIRQ_MASK_CLR);
+	irq_chip_unmask_parent(data);
+}
+
+static int mtk_cirq_set_type(struct irq_data *data, unsigned int type)
+{
+	int ret;
+
+	switch (type & IRQ_TYPE_SENSE_MASK) {
+	case IRQ_TYPE_EDGE_FALLING:
+		mtk_cirq_write_mask(data, CIRQ_POL_CLR);
+		mtk_cirq_write_mask(data, CIRQ_SENS_CLR);
+		break;
+	case IRQ_TYPE_EDGE_RISING:
+		mtk_cirq_write_mask(data, CIRQ_POL_SET);
+		mtk_cirq_write_mask(data, CIRQ_SENS_CLR);
+		break;
+	case IRQ_TYPE_LEVEL_LOW:
+		mtk_cirq_write_mask(data, CIRQ_POL_CLR);
+		mtk_cirq_write_mask(data, CIRQ_SENS_SET);
+		break;
+	case IRQ_TYPE_LEVEL_HIGH:
+		mtk_cirq_write_mask(data, CIRQ_POL_SET);
+		mtk_cirq_write_mask(data, CIRQ_SENS_SET);
+		break;
+	default:
+		break;
+	}
+
+	data = data->parent_data;
+	ret = data->chip->irq_set_type(data, type);
+	return ret;
+}
+
+static struct irq_chip mtk_cirq_chip = {
+	.name			= "MT_CIRQ",
+	.irq_mask		= mtk_cirq_mask,
+	.irq_unmask		= mtk_cirq_unmask,
+	.irq_eoi		= irq_chip_eoi_parent,
+	.irq_set_type		= mtk_cirq_set_type,
+	.irq_retrigger		= irq_chip_retrigger_hierarchy,
+#ifdef CONFIG_SMP
+	.irq_set_affinity	= irq_chip_set_affinity_parent,
+#endif
+};
+
+static int mtk_cirq_domain_translate(struct irq_domain *d,
+				     struct irq_fwspec *fwspec,
+				     unsigned long *hwirq,
+				     unsigned int *type)
+{
+	if (is_of_node(fwspec->fwnode)) {
+		if (fwspec->param_count != 3)
+			return -EINVAL;
+
+		/* No PPI should point to this domain */
+		if (fwspec->param[0] != 0)
+			return -EINVAL;
+
+		/* cirq support irq number check */
+		if (fwspec->param[1] < cirq_data->ext_irq_start)
+			return -EINVAL;
+
+		*hwirq = fwspec->param[1];
+		*type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int mtk_cirq_domain_alloc(struct irq_domain *domain, unsigned int virq,
+				 unsigned int nr_irqs, void *arg)
+{
+	int i;
+	irq_hw_number_t hwirq;
+	struct irq_fwspec *fwspec = arg;
+	struct irq_fwspec parent_fwspec = *fwspec;
+
+	if (fwspec->param_count != 3)
+		return -EINVAL;
+
+	/* cirq doesn't support PPI */
+	if (fwspec->param[0])
+		return -EINVAL;
+
+	if (fwspec->param[1] < cirq_data->ext_irq_start)
+		return -EINVAL;
+
+	hwirq = fwspec->param[1];
+	for (i = 0; i < nr_irqs; i++)
+		irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
+					      &mtk_cirq_chip,
+					      domain->host_data);
+
+	parent_fwspec.fwnode = domain->parent->fwnode;
+	return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs,
+					    &parent_fwspec);
+}
+
+static const struct irq_domain_ops cirq_domain_ops = {
+	.translate	= mtk_cirq_domain_translate,
+	.alloc		= mtk_cirq_domain_alloc,
+	.free		= irq_domain_free_irqs_common,
+};
+
+#ifdef CONFIG_PM_SLEEP
+static int mtk_cirq_suspend(void)
+{
+	u32 value;
+
+	/* set edge_only mode, record edge-triggerd interrupts */
+	/* enable cirq */
+	value = readl_relaxed(cirq_data->base + CIRQ_CONTROL);
+	value |= (CIRQ_EDGE | CIRQ_EN);
+	writel(value, cirq_data->base + CIRQ_CONTROL);
+	return 0;
+}
+
+static void mtk_cirq_resume(void)
+{
+	u32 value;
+
+	/* flush recored interrupts, will send signals to parent controller */
+	value = readl_relaxed(cirq_data->base + CIRQ_CONTROL);
+	writel(value | CIRQ_FLUSH, cirq_data->base + CIRQ_CONTROL);
+
+	/* disable cirq */
+	value = readl_relaxed(cirq_data->base + CIRQ_CONTROL);
+	value &= ~(CIRQ_EDGE | CIRQ_EN);
+	writel(value, cirq_data->base + CIRQ_CONTROL);
+}
+
+static struct syscore_ops mtk_cirq_syscore_ops = {
+	.suspend	= mtk_cirq_suspend,
+	.resume		= mtk_cirq_resume,
+};
+
+static void mtk_cirq_syscore_init(void)
+{
+	register_syscore_ops(&mtk_cirq_syscore_ops);
+}
+#else
+static inline void mtk_cirq_syscore_init(void) {}
+#endif
+
+static int __init mtk_cirq_of_init(struct device_node *node,
+				   struct device_node *parent)
+{
+	struct irq_domain *domain, *domain_parent;
+	int ret;
+
+	domain_parent = irq_find_host(parent);
+	if (!domain_parent) {
+		pr_err("mtk_cirq: interrupt-parent not found\n");
+		return -EINVAL;
+	}
+
+	cirq_data = kzalloc(sizeof(*cirq_data), GFP_KERNEL);
+	if (!cirq_data)
+		return -ENOMEM;
+
+	cirq_data->base = of_iomap(node, 0);
+	if (!cirq_data->base) {
+		pr_err("mtk_cirq: unable to map cirq register\n");
+		ret = -ENXIO;
+		goto out_free;
+	}
+
+	if (of_property_read_u32(node, "mediatek,ext-irq-start",
+				 &cirq_data->ext_irq_start)) {
+		ret = -EINVAL;
+		goto out_free;
+	}
+
+	domain = irq_domain_add_hierarchy(domain_parent, 0, CIRQ_IRQ_NUM, node,
+					  &cirq_domain_ops, cirq_data);
+	if (!domain) {
+		ret = -ENOMEM;
+		goto out_unmap;
+	}
+
+	mtk_cirq_syscore_init();
+
+	return 0;
+
+out_unmap:
+	iounmap(cirq_data->base);
+out_free:
+	kfree(cirq_data);
+	return ret;
+}
+
+IRQCHIP_DECLARE(mtk_cirq, "mediatek,mt2701-cirq", mtk_cirq_of_init);
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 3/3] ARM: dts: mt2701: Add mtk-cirq node for mt2701
From: Youlin Pei @ 2016-10-13  5:06 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1476335194-26604-1-git-send-email-youlin.pei@mediatek.com>

This commit add mtk-cirq node to mt2701 dtsi.

Signed-off-by: Youlin Pei <youlin.pei@mediatek.com>
---
 arch/arm/boot/dts/mt2701.dtsi |   11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/mt2701.dtsi b/arch/arm/boot/dts/mt2701.dtsi
index 7eab6f4..a314df9 100644
--- a/arch/arm/boot/dts/mt2701.dtsi
+++ b/arch/arm/boot/dts/mt2701.dtsi
@@ -21,7 +21,7 @@
 
 / {
 	compatible = "mediatek,mt2701";
-	interrupt-parent = <&sysirq>;
+	interrupt-parent = <&cirq>;
 
 	cpus {
 		#address-cells = <1>;
@@ -158,6 +158,15 @@
 		reg = <0 0x10200100 0 0x1c>;
 	};
 
+	cirq: interrupt-controller at 10204000 {
+		compatible = "mediatek,mt2701-cirq";
+		interrupt-controller;
+		#interrupt-cells = <3>;
+		interrupt-parent = <&sysirq>;
+		reg = <0 0x10204000 0 0x4000>;
+		mediatek,ext-irq-start = <32>;
+	};
+
 	apmixedsys: syscon at 10209000 {
 		compatible = "mediatek,mt2701-apmixedsys", "syscon";
 		reg = <0 0x10209000 0 0x1000>;
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 6/10] mmc: sdhci-xenon: Add Marvell Xenon SDHC core functionality
From: Ziji Hu @ 2016-10-13  5:38 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <7939a923-eb97-ca44-ac4c-f0a3cd4f45bc@intel.com>

Hi Adrian,

On 2016/10/12 21:07, Adrian Hunter wrote:
> On 12/10/16 14:58, Ziji Hu wrote:
>> Hi Adrian,
>>
>> 	Thank you very much for your review.
>> 	I will firstly fix the typo.
>>
>> On 2016/10/11 20:37, Adrian Hunter wrote:
<snip>
>>>> +
>>>> +static int xenon_start_signal_voltage_switch(struct mmc_host *mmc,
>>>> +					     struct mmc_ios *ios)
>>>> +{
>>>> +	struct sdhci_host *host = mmc_priv(mmc);
>>>> +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
>>>> +	struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
>>>> +
>>>> +	/*
>>>> +	 * Before SD/SDIO set signal voltage, SD bus clock should be
>>>> +	 * disabled. However, sdhci_set_clock will also disable the Internal
>>>> +	 * clock in mmc_set_signal_voltage().
>>>> +	 * If Internal clock is disabled, the 3.3V/1.8V bit can not be updated.
>>>> +	 * Thus here manually enable internal clock.
>>>> +	 *
>>>> +	 * After switch completes, it is unnecessary to disable internal clock,
>>>> +	 * since keeping internal clock active obeys SD spec.
>>>> +	 */
>>>> +	enable_xenon_internal_clk(host);
>>>> +
>>>> +	if (priv->card_candidate) {
>>>
>>> mmc_power_up() calls __mmc_set_signal_voltage() calls
>>> host->ops->start_signal_voltage_switch so priv->card_candidate could be an
>>> invalid reference to an old card.
>>>
>>> So that's not going to work if the card changes - not only for removable
>>> cards but even for eMMC if init fails and retries.
>>>
>> 	As you point out, this piece of code have defects, even though it actually works on Marvell multiple platforms, unless eMMC card is removable.
>>
>> 	I can add a property to explicitly indicate eMMC type in DTS.
>> 	Then card_candidate access can be removed here.
>> 	Does it sounds more reasonable to you?
> 
> Sure
> 
>>
>>>> +		if (mmc_card_mmc(priv->card_candidate))
>>>> +			return xenon_emmc_signal_voltage_switch(mmc, ios);
>>>
>>> So if all you need to know is whether it is a eMMC, why can't DT tell you?
>>>
>> 	I can add an eMMC type property in DTS, to remove the card_candidate access here.
>>
>>>> +	}
>>>> +
>>>> +	return sdhci_start_signal_voltage_switch(mmc, ios);
>>>> +}
>>>> +
>>>> +/*
>>>> + * After determining which slot is used for SDIO,
>>>> + * some additional task is required.
>>>> + */
>>>> +static void xenon_init_card(struct mmc_host *mmc, struct mmc_card *card)
>>>> +{
>>>> +	struct sdhci_host *host = mmc_priv(mmc);
>>>> +	u32 reg;
>>>> +	u8 slot_idx;
>>>> +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
>>>> +	struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
>>>> +
>>>> +	/* Link the card for delay adjustment */
>>>> +	priv->card_candidate = card;
>>>
>>> You really need a better way to get the card.  I suggest you take up the
>>> issue with Ulf.  One possibility is to have mmc core set host->card = card
>>> much earlier.
>>>
>> 	Could you please tell me if any issue related to card_candidate still exists, after the card_candidate is removed from xenon_start_signal_voltage_switch() in above?
>> 	It seems that when init_card is called, the structure card has already been updated and stable in MMC/SD/SDIO initialization sequence.
>> 	May I keep it here?
> 
> It works by accident rather than by design.  We can do better.
> 
	Could you please tell me some details which are satisfied about card_candidate?

	I must admit that card_candidate in xenon_start_signal_voltage_switch() is imperfect.
	But card_candidate in init_card() and later in set_ios() work by design, rather than by accident. We did a lot of tests on several platforms.
	
	The structure mmc_card passed in here is a stable one. Thus in my very own opinion, it is safe and stable to use mmc_card here.
	card_candidate is used only in card initialization. It is not active in later transfers after initialization is done.
	It will always updated with mmc_card in next card initialization.

>>
>>>> +	/* Set tuning functionality of this slot */
>>>> +	xenon_slot_tuning_setup(host);
>>>> +
>>>> +	slot_idx = priv->slot_idx;
>>>> +	if (!mmc_card_sdio(card)) {
>>>> +		/* Re-enable the Auto-CMD12 cap flag. */
>>>> +		host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12;
>>>> +		host->flags |= SDHCI_AUTO_CMD12;
>>>> +
>>>> +		/* Clear SDIO Card Inserted indication */
>>>> +		reg = sdhci_readl(host, SDHC_SYS_CFG_INFO);
>>>> +		reg &= ~(1 << (slot_idx + SLOT_TYPE_SDIO_SHIFT));
>>>> +		sdhci_writel(host, reg, SDHC_SYS_CFG_INFO);
>>>> +
>>>> +		if (mmc_card_mmc(card)) {
>>>> +			mmc->caps |= MMC_CAP_NONREMOVABLE;
>>>> +			if (!(host->quirks2 & SDHCI_QUIRK2_NO_1_8_V))
>>>> +				mmc->caps |= MMC_CAP_1_8V_DDR;
>>>> +			/*
>>>> +			 * Force to clear BUS_TEST to
>>>> +			 * skip bus_test_pre and bus_test_post
>>>> +			 */
>>>> +			mmc->caps &= ~MMC_CAP_BUS_WIDTH_TEST;
>>>> +			mmc->caps2 |= MMC_CAP2_HC_ERASE_SZ |
>>>> +				      MMC_CAP2_PACKED_CMD;
>>>> +			if (mmc->caps & MMC_CAP_8_BIT_DATA)
>>>> +				mmc->caps2 |= MMC_CAP2_HS400_1_8V;
>>>> +		}
>>>> +	} else {
>>>> +		/*
>>>> +		 * Delete the Auto-CMD12 cap flag.
>>>> +		 * Otherwise, when sending multi-block CMD53,
>>>> +		 * Driver will set Transfer Mode Register to enable Auto CMD12.
>>>> +		 * However, SDIO device cannot recognize CMD12.
>>>> +		 * Thus SDHC will time-out for waiting for CMD12 response.
>>>> +		 */
>>>> +		host->quirks &= ~SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12;
>>>> +		host->flags &= ~SDHCI_AUTO_CMD12;
>>>
>>> sdhci_set_transfer_mode() won't enable auto-CMD12 for CMD53 anyway, so is
>>> this needed?
>>>
>> 	In Xenon driver, Auto-CMD12 flag is set to enable full support to Auto-CMD feature, both Auto-CMD12 and Auto-CMD23.
>> 	As a result, when Xenon SDHC slot can both support SD and SDIO, Auto-CMD12 is disabled when SDIO card is inserted, and renabled when SD is inserted.
>>
>> 	I recheck the sdhci code to set Auto-CMD bit in Transfer Mode register, in sdhci_set_transfer_mode():
>> 	if (mmc_op_multi(cmd->opcode) || data->blocks > 1)
>> 	As you can see, as long as it is CMD18/CMD25 OR there are multiple data blocks, Auto-CMD field will be set.
>> 	CMD53 doesn't have CMD23. Thus Auto-CMD12 is selected since Auto-CMD12 flag is set.
>> 	Thus I have to clear Auto-CMD12 to avoid issuing Auto-CMD12 in SDIO transfer.
> 
> 
> The code is:
> 
> 	if (mmc_op_multi(cmd->opcode) || data->blocks > 1) {
> 		mode = SDHCI_TRNS_BLK_CNT_EN | SDHCI_TRNS_MULTI;
> 		/*
> 		 * If we are sending CMD23, CMD12 never gets sent
> 		 * on successful completion (so no Auto-CMD12).
> 		 */
> 		if (sdhci_auto_cmd12(host, cmd->mrq) &&
> 		    (cmd->opcode != SD_IO_RW_EXTENDED))
> 			mode |= SDHCI_TRNS_AUTO_CMD12;
> 		else if (cmd->mrq->sbc && (host->flags & SDHCI_AUTO_CMD23)) {
> 			mode |= SDHCI_TRNS_AUTO_CMD23;
> 			sdhci_writel(host, cmd->mrq->sbc->arg, SDHCI_ARGUMENT2);
> 		}
> 	}
> 
> You can see the check for SD_IO_RW_EXTENDED which is CMD53.
> 
	Sorry. I didn't notice CMD53 check was added.
	I introduced this Auto-CMD12 hack since kernel 3.8. It seems that this check is not added in kernel 3.8.
	Thanks for the information. I will remove the Auto-CMD12 hack.

>>
>> 	I just meet a similar issue in RPMB.
>> 	When Auto-CMD12 flag is set, eMMC RPMB access will trigger Auto-CMD12, since CMD25 is in use.
>> 	It will cause RPMB access failed.
> 
> Can you explain more about the RPMB issue.  Doesn't it use CMD23, so CMD12
> wouldn't be used - auto or manually.
> 
	RPMB go through the MMC ioctl routine.
	Unlike normal data transfer, MMC ioctl for RPMB explicitly issues CMD23. When CMD25 is issued, there is neither data->sbc nor Auto-CMD23.
	As a result, sdhci driver will automatically enable Auto-CMD12 for RPMB CMD25 if Auto-CMD12 flag is set.

>>
>> 	One possible solution is to drop Auto-CMD12 support and use Auto-CMD23 only, in Xenon driver.
>> 	May I know you opinion, please?
> 
> I don't use auto-CMD12 because I don't know if it provides any benefit and
> sdhci does not seem to have implemented Auto CMD12 Error Recovery, although
> I have never looked at it closely.
>
	Actually, Auto-CMD23 is always used on our Xenon. Auto-CMD12 is not used at all.
	But since this driver is a general one for all Marvell products, Auto-CMD12 is also supported in case that Auto-CMD23 is not available.
 

^ permalink raw reply

* Low network throughput on i.MX28
From: Lothar Waßmann @ 2016-10-13  6:48 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1476313753.2065.11.camel@embedded.rocks>

Hi,

On Thu, 13 Oct 2016 01:09:13 +0200 J?rg Krause wrote:
> Hi,
> 
> I am using a custom i.MX28 board similar to the i.MX28-EVK. For Wi-Fi
> the board assembles a BCM43362 from Broadcom and for Ethernet a
> LAN8720A from Microchip. The board is running mainline Linux 4.7.
> 
> While both, wireless and wired network interfaces work, the TCP
> throughput measured with iperf is low.
> 
> The bandwith for Ethernet is between 20-30 MBits/sec and for WLAN is
> about 4-5 MBits/sec.
> 
> There exists an Application Note "i.MX28 Ethernet Performance on
> Linux" [1] which shows a bandwith of > 60 MBits/sec. A user an the NXP
> forum [2] told he achieved 20 MBits/sec with some Qualcom chip.
> 
> Note, that these values are most probably measured with the legacy
> Linux Kernel 2.6.35 from NXP.
> 
> Does anybody has done throughput tests on i.MX28 with mainline Kernel?
> If so, what are the results? What might be the bottleneck?
>

This is the iperf output on a TX28 with current mainline kernel
(4.8.0-rc5):
------------------------------------------------------------
Client connecting to 192.168.100.1, TCP port 5001
TCP window size: 43.8 KByte (default)
------------------------------------------------------------
[  3] local 192.168.100.56 port 60325 connected with 192.168.100.1 port 5001
[ ID] Interval       Transfer     Bandwidth
[  3]  0.0-10.0 sec  57.5 MBytes  48.2 Mbits/sec

You might check your kernel DEBUG configs (especially
CONFIG_DEBUG_PAGEALLOC).


Lothar Wa?mann

^ permalink raw reply

* [PATCH 02/10] mm: remove write/force parameters from __get_user_pages_unlocked()
From: Paolo Bonzini @ 2016-10-13  6:54 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161013002020.3062-3-lstoakes@gmail.com>



On 13/10/2016 02:20, Lorenzo Stoakes wrote:
> This patch removes the write and force parameters from
> __get_user_pages_unlocked() to make the use of FOLL_FORCE explicit in callers as
> use of this flag can result in surprising behaviour (and hence bugs) within the
> mm subsystem.
> 
> Signed-off-by: Lorenzo Stoakes <lstoakes@gmail.com>
> ---
>  include/linux/mm.h     |  3 +--
>  mm/gup.c               | 17 +++++++++--------
>  mm/nommu.c             | 12 +++++++++---
>  mm/process_vm_access.c |  7 +++++--
>  virt/kvm/async_pf.c    |  3 ++-
>  virt/kvm/kvm_main.c    | 11 ++++++++---
>  6 files changed, 34 insertions(+), 19 deletions(-)
> 
> diff --git a/include/linux/mm.h b/include/linux/mm.h
> index e9caec6..2db98b6 100644
> --- a/include/linux/mm.h
> +++ b/include/linux/mm.h
> @@ -1285,8 +1285,7 @@ long get_user_pages_locked(unsigned long start, unsigned long nr_pages,
>  		    int write, int force, struct page **pages, int *locked);
>  long __get_user_pages_unlocked(struct task_struct *tsk, struct mm_struct *mm,
>  			       unsigned long start, unsigned long nr_pages,
> -			       int write, int force, struct page **pages,
> -			       unsigned int gup_flags);
> +			       struct page **pages, unsigned int gup_flags);
>  long get_user_pages_unlocked(unsigned long start, unsigned long nr_pages,
>  		    int write, int force, struct page **pages);
>  int get_user_pages_fast(unsigned long start, int nr_pages, int write,
> diff --git a/mm/gup.c b/mm/gup.c
> index ba83942..3d620dd 100644
> --- a/mm/gup.c
> +++ b/mm/gup.c
> @@ -865,17 +865,11 @@ EXPORT_SYMBOL(get_user_pages_locked);
>   */
>  __always_inline long __get_user_pages_unlocked(struct task_struct *tsk, struct mm_struct *mm,
>  					       unsigned long start, unsigned long nr_pages,
> -					       int write, int force, struct page **pages,
> -					       unsigned int gup_flags)
> +					       struct page **pages, unsigned int gup_flags)
>  {
>  	long ret;
>  	int locked = 1;
>  
> -	if (write)
> -		gup_flags |= FOLL_WRITE;
> -	if (force)
> -		gup_flags |= FOLL_FORCE;
> -
>  	down_read(&mm->mmap_sem);
>  	ret = __get_user_pages_locked(tsk, mm, start, nr_pages, pages, NULL,
>  				      &locked, false, gup_flags);
> @@ -905,8 +899,15 @@ EXPORT_SYMBOL(__get_user_pages_unlocked);
>  long get_user_pages_unlocked(unsigned long start, unsigned long nr_pages,
>  			     int write, int force, struct page **pages)
>  {
> +	unsigned int flags = FOLL_TOUCH;
> +
> +	if (write)
> +		flags |= FOLL_WRITE;
> +	if (force)
> +		flags |= FOLL_FORCE;
> +
>  	return __get_user_pages_unlocked(current, current->mm, start, nr_pages,
> -					 write, force, pages, FOLL_TOUCH);
> +					 pages, flags);
>  }
>  EXPORT_SYMBOL(get_user_pages_unlocked);
>  
> diff --git a/mm/nommu.c b/mm/nommu.c
> index 95daf81..925dcc1 100644
> --- a/mm/nommu.c
> +++ b/mm/nommu.c
> @@ -185,8 +185,7 @@ EXPORT_SYMBOL(get_user_pages_locked);
>  
>  long __get_user_pages_unlocked(struct task_struct *tsk, struct mm_struct *mm,
>  			       unsigned long start, unsigned long nr_pages,
> -			       int write, int force, struct page **pages,
> -			       unsigned int gup_flags)
> +			       struct page **pages, unsigned int gup_flags)
>  {
>  	long ret;
>  	down_read(&mm->mmap_sem);
> @@ -200,8 +199,15 @@ EXPORT_SYMBOL(__get_user_pages_unlocked);
>  long get_user_pages_unlocked(unsigned long start, unsigned long nr_pages,
>  			     int write, int force, struct page **pages)
>  {
> +	unsigned int flags = 0;
> +
> +	if (write)
> +		flags |= FOLL_WRITE;
> +	if (force)
> +		flags |= FOLL_FORCE;
> +
>  	return __get_user_pages_unlocked(current, current->mm, start, nr_pages,
> -					 write, force, pages, 0);
> +					 pages, flags);
>  }
>  EXPORT_SYMBOL(get_user_pages_unlocked);
>  
> diff --git a/mm/process_vm_access.c b/mm/process_vm_access.c
> index 07514d4..be8dc8d 100644
> --- a/mm/process_vm_access.c
> +++ b/mm/process_vm_access.c
> @@ -88,12 +88,16 @@ static int process_vm_rw_single_vec(unsigned long addr,
>  	ssize_t rc = 0;
>  	unsigned long max_pages_per_loop = PVM_MAX_KMALLOC_PAGES
>  		/ sizeof(struct pages *);
> +	unsigned int flags = FOLL_REMOTE;
>  
>  	/* Work out address and page range required */
>  	if (len == 0)
>  		return 0;
>  	nr_pages = (addr + len - 1) / PAGE_SIZE - addr / PAGE_SIZE + 1;
>  
> +	if (vm_write)
> +		flags |= FOLL_WRITE;
> +
>  	while (!rc && nr_pages && iov_iter_count(iter)) {
>  		int pages = min(nr_pages, max_pages_per_loop);
>  		size_t bytes;
> @@ -104,8 +108,7 @@ static int process_vm_rw_single_vec(unsigned long addr,
>  		 * current/current->mm
>  		 */
>  		pages = __get_user_pages_unlocked(task, mm, pa, pages,
> -						  vm_write, 0, process_pages,
> -						  FOLL_REMOTE);
> +						  process_pages, flags);
>  		if (pages <= 0)
>  			return -EFAULT;
>  
> diff --git a/virt/kvm/async_pf.c b/virt/kvm/async_pf.c
> index db96688..8035cc1 100644
> --- a/virt/kvm/async_pf.c
> +++ b/virt/kvm/async_pf.c
> @@ -84,7 +84,8 @@ static void async_pf_execute(struct work_struct *work)
>  	 * mm and might be done in another context, so we must
>  	 * use FOLL_REMOTE.
>  	 */
> -	__get_user_pages_unlocked(NULL, mm, addr, 1, 1, 0, NULL, FOLL_REMOTE);
> +	__get_user_pages_unlocked(NULL, mm, addr, 1, NULL,
> +			FOLL_WRITE | FOLL_REMOTE);
>  
>  	kvm_async_page_present_sync(vcpu, apf);
>  
> diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
> index 81dfc73..28510e7 100644
> --- a/virt/kvm/kvm_main.c
> +++ b/virt/kvm/kvm_main.c
> @@ -1416,10 +1416,15 @@ static int hva_to_pfn_slow(unsigned long addr, bool *async, bool write_fault,
>  		down_read(&current->mm->mmap_sem);
>  		npages = get_user_page_nowait(addr, write_fault, page);
>  		up_read(&current->mm->mmap_sem);
> -	} else
> +	} else {
> +		unsigned int flags = FOLL_TOUCH | FOLL_HWPOISON;
> +
> +		if (write_fault)
> +			flags |= FOLL_WRITE;
> +
>  		npages = __get_user_pages_unlocked(current, current->mm, addr, 1,
> -						   write_fault, 0, page,
> -						   FOLL_TOUCH|FOLL_HWPOISON);
> +						   page, flags);
> +	}
>  	if (npages != 1)
>  		return npages;
>  
> 

Acked-by: Paolo Bonzini <pbonzini@redhat.com>

^ permalink raw reply

* [PATCH v2] sdhci-esdhc-imx: Correct two register accesses
From: Ulf Hansson @ 2016-10-13  7:01 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1476124792-18441-1-git-send-email-aaron.brice@datasoft.com>

On 10 October 2016 at 20:39, Aaron Brice <aaron.brice@datasoft.com> wrote:
>
>  - The DMA error interrupt bit is in a different position as
>    compared to the sdhci standard.  This is accounted for in
>    many cases, but not handled in the case of clearing the
>    INT_STATUS register by writing a 1 to that location.
>  - The HOST_CONTROL register is very different as compared to
>    the sdhci standard.  This is accounted for in the write
>    case, but not when read back out (which it is in the sdhci
>    code).
>
> Signed-off-by: Dave Russell <david.russell@datasoft.com>
> Signed-off-by: Aaron Brice <aaron.brice@datasoft.com>
> Acked-by: Dong Aisheng <aisheng.dong@nxp.com>
> ---

Thanks, applied for fixes!

Kind regards
Uffe

>
> v1 -> v2:
> - rename long_val to val
>
>  drivers/mmc/host/sdhci-esdhc-imx.c | 23 ++++++++++++++++++++++-
>  1 file changed, 22 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c
> index 1f54fd8..7123ef9 100644
> --- a/drivers/mmc/host/sdhci-esdhc-imx.c
> +++ b/drivers/mmc/host/sdhci-esdhc-imx.c
> @@ -346,7 +346,8 @@ static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg)
>         struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
>         u32 data;
>
> -       if (unlikely(reg == SDHCI_INT_ENABLE || reg == SDHCI_SIGNAL_ENABLE)) {
> +       if (unlikely(reg == SDHCI_INT_ENABLE || reg == SDHCI_SIGNAL_ENABLE ||
> +                       reg == SDHCI_INT_STATUS)) {
>                 if ((val & SDHCI_INT_CARD_INT) && !esdhc_is_usdhc(imx_data)) {
>                         /*
>                          * Clear and then set D3CD bit to avoid missing the
> @@ -555,6 +556,25 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
>         esdhc_clrset_le(host, 0xffff, val, reg);
>  }
>
> +static u8 esdhc_readb_le(struct sdhci_host *host, int reg)
> +{
> +       u8 ret;
> +       u32 val;
> +
> +       switch (reg) {
> +       case SDHCI_HOST_CONTROL:
> +               val = readl(host->ioaddr + reg);
> +
> +               ret = val & SDHCI_CTRL_LED;
> +               ret |= (val >> 5) & SDHCI_CTRL_DMA_MASK;
> +               ret |= (val & ESDHC_CTRL_4BITBUS);
> +               ret |= (val & ESDHC_CTRL_8BITBUS) << 3;
> +               return ret;
> +       }
> +
> +       return readb(host->ioaddr + reg);
> +}
> +
>  static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg)
>  {
>         struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> @@ -947,6 +967,7 @@ static void esdhc_set_timeout(struct sdhci_host *host, struct mmc_command *cmd)
>  static struct sdhci_ops sdhci_esdhc_ops = {
>         .read_l = esdhc_readl_le,
>         .read_w = esdhc_readw_le,
> +       .read_b = esdhc_readb_le,
>         .write_l = esdhc_writel_le,
>         .write_w = esdhc_writew_le,
>         .write_b = esdhc_writeb_le,
> --
> 2.7.4
>

^ permalink raw reply

* [PATCH v7 2/8] power: add power sequence library
From: Heiko Stuebner @ 2016-10-13  7:04 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161013012216.GA8114@b29397-desktop>

Am Donnerstag, 13. Oktober 2016, 09:22:16 CEST schrieb Peter Chen:
> On Wed, Oct 12, 2016 at 12:30:29PM +0200, Heiko Stuebner wrote:
> > Hi,
> > 
> > Am Dienstag, 20. September 2016, 11:36:41 CEST schrieb Peter Chen:
> > > We have an well-known problem that the device needs to do some power
> > > sequence before it can be recognized by related host, the typical
> > > example like hard-wired mmc devices and usb devices.
> > > 
> > > This power sequence is hard to be described at device tree and handled
> > > by
> > > related host driver, so we have created a common power sequence
> > > library to cover this requirement. The core code has supplied
> > > some common helpers for host driver, and individual power sequence
> > > libraries handle kinds of power sequence for devices.
> > > 
> > > pwrseq_generic is intended for general purpose of power sequence, which
> > > handles gpios and clocks currently, and can cover regulator and pinctrl
> > > in future. The host driver just needs to call of_pwrseq_on/of_pwrseq_off
> > > if only one power sequence is needed, else call of_pwrseq_on_list
> > > /of_pwrseq_off_list instead (eg, USB hub driver).
> > > 
> > > Signed-off-by: Peter Chen <peter.chen@nxp.com>
> > > Tested-by Joshua Clayton <stillcompiling@gmail.com>
> > > Reviewed-by: Matthias Kaehlcke <mka@chromium.org>
> > > Tested-by: Matthias Kaehlcke <mka@chromium.org>
> > 
> > first of all, glad to see this move forward. I've only some qualms with
> > the
> > static number of allocated power sequences below.
> 
> Thanks for commenting it, the preallocate way is not a good way, but I
> can't find suitable way. See below comments.
> 
> > > +static int __init pwrseq_generic_register(void)
> > > +{
> > > +	struct pwrseq_generic *pwrseq_gen;
> > > +	int i;
> > > +
> > > +	for (i = 0; i < CONFIG_PWRSEQ_GENERIC_INSTANCE_NUMBER; i++) {
> > > +		pwrseq_gen = kzalloc(sizeof(*pwrseq_gen), GFP_KERNEL);
> > > +		if (!pwrseq_gen)
> > > +			return -ENOMEM;
> > > +
> > > +		pwrseq_gen->pwrseq.pwrseq_of_match_table = generic_id_table;
> > > +		pwrseq_gen->pwrseq.get = pwrseq_generic_get;
> > > +		pwrseq_gen->pwrseq.on = pwrseq_generic_on;
> > > +		pwrseq_gen->pwrseq.off = pwrseq_generic_off;
> > > +		pwrseq_gen->pwrseq.put = pwrseq_generic_put;
> > > +		pwrseq_gen->pwrseq.free = pwrseq_generic_free;
> > > +
> > > +		pwrseq_register(&pwrseq_gen->pwrseq);
> > > +	}
> > > +
> > > +	return 0;
> > > +}
> > > +postcore_initcall(pwrseq_generic_register)
> > 
> > I see that you need to have it preallocated for the compatible matching,
> > but wouldn't it also work to either just register the type and allocate
> > dynamically or otherwise just allocate a new spare everytime
> > pwrseq_generic_get() picks up the previous spare?
> 
> Before compatible matching, the host driver doesn't know which pwrseq type
> for its child node, then doesn't know which pwrseq instance needs to be
> allocated. From dts, we don't know which pwrseq type for the node.

yes, that is why I was suggesting allocating one (or two) here in 
pwrseq_generic_register() and every time pwrseq_generic_get() grabs the last 
free sequence instance, you allocate a new free spare one in that function.

^ permalink raw reply

* [PATCH 4/4] staging/vchi: Remove dependency on CONFIG_BROKEN.
From: Greg Kroah-Hartman @ 2016-10-13  7:05 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161003185209.27733-5-eric@anholt.net>

On Mon, Oct 03, 2016 at 11:52:09AM -0700, Eric Anholt wrote:
> The driver builds now.
> 
> Signed-off-by: Eric Anholt <eric@anholt.net>
> ---
>  drivers/staging/vc04_services/Kconfig | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/staging/vc04_services/Kconfig b/drivers/staging/vc04_services/Kconfig
> index 9676fb29075a..db8e1beb89f9 100644
> --- a/drivers/staging/vc04_services/Kconfig
> +++ b/drivers/staging/vc04_services/Kconfig
> @@ -1,6 +1,6 @@
>  config BCM2708_VCHIQ
>  	tristate "Videocore VCHIQ"
> -	depends on RASPBERRYPI_FIRMWARE && BROKEN
> +	depends on RASPBERRYPI_FIRMWARE
>  	default y
>  	help
>  		Kernel to VideoCore communication interface for the

I've dropped this patch from my branch as there are build errors on
arm64 systems still, and we don't want regressions like that.

I've forwarded you the error messages, and I'll be glad to add this
patch back once these issues are fixed.

thanks,

greg k-h

^ permalink raw reply

* [PATCH] ARM: dts: rockchip: Reserve unusable memory region on rk3066
From: Huang, Tao @ 2016-10-13  7:12 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <149df5ee-3478-6d15-bb76-47becac146e9@gmail.com>

Hi, Pawe?:
On 2016?10?10? 17:11, Pawe? Jarosz wrote:
> W dniu 10.10.2016 o 09:18, Huang, Tao pisze:
>> Our IC guy need us tell them which master can not access such area, DMA
>> or EMMC Controller or GPU, etc? Could you tell me how to reproduce such
>> issue?
>> And we can confirm CPU core can access this memory through /dev/mem and
>> the test board is 1GB too. Personally, I don't think RK3066 has such
>> limit because when we verify this chip, we don't found such limit at all.
>>
>> Thanks,
>> Huang, Tao
> I'm getting this on Ubuntu 16.04 with mainline kernel.
> My board always freezes when i type: "memtester 800M"
>
We try run memtest 800M with Linux kernel 4.8, which killed by OOM.
But if we run:
# memtester -p 0x9F000000 16K 1
memtester version 4.3.0 (32-bit)
Copyright (C) 2001-2012 Charles Cazabon.
Licensed under the GNU General Public License version 2 (only).

pagesize is 4096
pagesizemask is 0xfffff000
want 0MB (16384 bytes)
Loop 1/1:
   Stuck Address       : ok
   Random Value        : ok
   Compare XOR         : ok
   Compare SUB         : ok
   Compare MUL         : ok
   Compare DIV         : ok
   Compare OR          : ok
   Compare AND         : ok
   Sequential Increment: ok
   Solid Bits          : ok
   Block Sequential    : ok
   Checkerboard        : ok
   Bit Spread          : ok
   Bit Flip            : ok
   Walking Ones        : ok
   Walking Zeroes      : ok
   8-bit Writes        : ok
   16-bit Writes       : ok

So these memory should be fine to CPU core. Maybe your system just
freeze because out of memory.

^ permalink raw reply

* [PATCH] ARM: dts: da850: Add basic DTS for the L138/C6748 Dev Kit
From: Karl Beldan @ 2016-10-13  7:14 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20160804234349.GA4391@gobelin>

On Thu, Aug 04, 2016 at 11:43:49PM +0000, Karl Beldan wrote:
> On Thu, Aug 04, 2016 at 04:13:10PM -0500, Rob Herring wrote:
> > +Peter H
> > 
> > On Thu, Aug 4, 2016 at 4:03 PM, Karl Beldan <kbeldan@baylibre.com> wrote:
> > > On Thu, Aug 04, 2016 at 01:29:02PM -0500, Rob Herring wrote:
> > >> On Thu, Aug 04, 2016 at 02:43:32PM +0000, Karl Beldan wrote:
> > >> > The LCDK is the successor to the late Hawkboard and has the same machine
> > >> > number.
> > >> > Among the differences are the flash (16bits vs 8bits) and some pins
> > >> > (MMC, LEDs, buttons, some external connectors), however the main
> > >> > components remain the same (eth. PHY, Audio Codec, Video decoder and
> > >> > DAC) except for the main PMIC, different and hard-wired on the LCDK (the
> > >> > LDOs and DCDCs are always ON).
> > >> > A DT-only boot with this addition gives functional uart, reboot via
> > >> > watchdog, rtc, ethernet and MMC (I added the CD gpio for the MMC
> > >> > although davinci_mmc doesn't call yet the mmc core OF facilities).
> > 
> > [...]
> > 
> > >> > +   aliases {
> > >> > +           serial2 = &serial2;
> > >> > +   };
> > >> > +
> > >> > +   chosen {
> > >> > +           bootargs = "console=ttyS2,115200n8 earlycon";
> > >>
> > >> You don't need console with stdout-path set. And really, earlycon should
> > >> not be the default.
> > >>
> > > Ok for earlycon.
> > >
> > > For the console, I thought likewise .. until I _struggled_ to understand
> > > why the console was disappearing under my feet.
> > > It seemed even stranger when I started looking at the codepath because
> > > drivers/of/base.c would properly add_preferred_console as expected after
> > > the corresponding UART was added. But the reality was there, without it
> > > the console was not like stdout-path.
> > > Among the commits that could instill doubt as to whether stdout-path
> > > rules is f64255b5072d:
> > > "The assumption that at least 1 preferred console will be registered
> > > when the stdout-path property is set is invalid, which can result in
> > > _no_ consoles.", though it didn't answer my question.
> > > I started looking whether tty0 was not getting in the way and in the end
> > > I came up with setting console.
> > > But you seem affirmative.
> > 
> > There have been some issues in this area, but I thought they were in
> > conjunction with earlycon and solved now. This is a fairly common
> > review request for dts files, so I'm surprised if it doesn't work
> > right. Maybe having tty0 or not has some influence. Or people just
> > accept review comments without testing or caring...
> > 
> I can assure you I tested without early{con,printk} ;).
> 
> Now I have just rechecked the codepaths and something caught my
> attention in kernel/printk/printk.c:__add_preferred_console(): if
> console_cmdline is empty selected_console gets unconditionally set to
> zero ! Knowing this I confirmed by replacing with idx (works), and also
> by using ttyS0 (i.e. alias serial0 = &uart2) (works), this would not
> affect DTs using serial0. It's late here so I'll stop there.
>  

The stdout-path bug was fixed last week by Paul Burton in 05fd007e4629 
("console: don't prefer first registered if DT specifies stdout-path").
 
Karl

^ permalink raw reply

* [PATCH 00/10] mm: adjust get_user_pages* functions to explicitly pass FOLL_* flags
From: Christian König @ 2016-10-13  7:32 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161013002020.3062-1-lstoakes@gmail.com>

Am 13.10.2016 um 02:20 schrieb Lorenzo Stoakes:
> This patch series adjusts functions in the get_user_pages* family such that
> desired FOLL_* flags are passed as an argument rather than implied by flags.
>
> The purpose of this change is to make the use of FOLL_FORCE explicit so it is
> easier to grep for and clearer to callers that this flag is being used. The use
> of FOLL_FORCE is an issue as it overrides missing VM_READ/VM_WRITE flags for the
> VMA whose pages we are reading from/writing to, which can result in surprising
> behaviour.
>
> The patch series came out of the discussion around commit 38e0885, which
> addressed a BUG_ON() being triggered when a page was faulted in with PROT_NONE
> set but having been overridden by FOLL_FORCE. do_numa_page() was run on the
> assumption the page _must_ be one marked for NUMA node migration as an actual
> PROT_NONE page would have been dealt with prior to this code path, however
> FOLL_FORCE introduced a situation where this assumption did not hold.
>
> See https://marc.info/?l=linux-mm&m=147585445805166 for the patch proposal.
>
> Lorenzo Stoakes (10):
>    mm: remove write/force parameters from __get_user_pages_locked()
>    mm: remove write/force parameters from __get_user_pages_unlocked()
>    mm: replace get_user_pages_unlocked() write/force parameters with gup_flags
>    mm: replace get_user_pages_locked() write/force parameters with gup_flags
>    mm: replace get_vaddr_frames() write/force parameters with gup_flags
>    mm: replace get_user_pages() write/force parameters with gup_flags
>    mm: replace get_user_pages_remote() write/force parameters with gup_flags
>    mm: replace __access_remote_vm() write parameter with gup_flags
>    mm: replace access_remote_vm() write parameter with gup_flags
>    mm: replace access_process_vm() write parameter with gup_flags

Patch number 6 in this series (which touches drivers I co-maintain) is 
Acked-by: Christian K?nig <christian.koenig@amd.com>.

In general looks like a very nice cleanup to me, but I'm not enlightened 
enough to full judge.

Regards,
Christian.

>
>   arch/alpha/kernel/ptrace.c                         |  9 ++--
>   arch/blackfin/kernel/ptrace.c                      |  5 ++-
>   arch/cris/arch-v32/drivers/cryptocop.c             |  4 +-
>   arch/cris/arch-v32/kernel/ptrace.c                 |  4 +-
>   arch/ia64/kernel/err_inject.c                      |  2 +-
>   arch/ia64/kernel/ptrace.c                          | 14 +++---
>   arch/m32r/kernel/ptrace.c                          | 15 ++++---
>   arch/mips/kernel/ptrace32.c                        |  5 ++-
>   arch/mips/mm/gup.c                                 |  2 +-
>   arch/powerpc/kernel/ptrace32.c                     |  5 ++-
>   arch/s390/mm/gup.c                                 |  3 +-
>   arch/score/kernel/ptrace.c                         | 10 +++--
>   arch/sh/mm/gup.c                                   |  3 +-
>   arch/sparc/kernel/ptrace_64.c                      | 24 +++++++----
>   arch/sparc/mm/gup.c                                |  3 +-
>   arch/x86/kernel/step.c                             |  3 +-
>   arch/x86/mm/gup.c                                  |  2 +-
>   arch/x86/mm/mpx.c                                  |  5 +--
>   arch/x86/um/ptrace_32.c                            |  3 +-
>   arch/x86/um/ptrace_64.c                            |  3 +-
>   drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c            |  7 ++-
>   drivers/gpu/drm/etnaviv/etnaviv_gem.c              |  7 ++-
>   drivers/gpu/drm/exynos/exynos_drm_g2d.c            |  3 +-
>   drivers/gpu/drm/i915/i915_gem_userptr.c            |  6 ++-
>   drivers/gpu/drm/radeon/radeon_ttm.c                |  3 +-
>   drivers/gpu/drm/via/via_dmablit.c                  |  4 +-
>   drivers/infiniband/core/umem.c                     |  6 ++-
>   drivers/infiniband/core/umem_odp.c                 |  7 ++-
>   drivers/infiniband/hw/mthca/mthca_memfree.c        |  2 +-
>   drivers/infiniband/hw/qib/qib_user_pages.c         |  3 +-
>   drivers/infiniband/hw/usnic/usnic_uiom.c           |  5 ++-
>   drivers/media/pci/ivtv/ivtv-udma.c                 |  4 +-
>   drivers/media/pci/ivtv/ivtv-yuv.c                  |  5 ++-
>   drivers/media/platform/omap/omap_vout.c            |  2 +-
>   drivers/media/v4l2-core/videobuf-dma-sg.c          |  7 ++-
>   drivers/media/v4l2-core/videobuf2-memops.c         |  6 ++-
>   drivers/misc/mic/scif/scif_rma.c                   |  3 +-
>   drivers/misc/sgi-gru/grufault.c                    |  2 +-
>   drivers/platform/goldfish/goldfish_pipe.c          |  3 +-
>   drivers/rapidio/devices/rio_mport_cdev.c           |  3 +-
>   drivers/scsi/st.c                                  |  5 +--
>   .../interface/vchiq_arm/vchiq_2835_arm.c           |  3 +-
>   .../vc04_services/interface/vchiq_arm/vchiq_arm.c  |  3 +-
>   drivers/video/fbdev/pvr2fb.c                       |  4 +-
>   drivers/virt/fsl_hypervisor.c                      |  4 +-
>   fs/exec.c                                          |  9 +++-
>   fs/proc/base.c                                     | 19 +++++---
>   include/linux/mm.h                                 | 18 ++++----
>   kernel/events/uprobes.c                            |  6 ++-
>   kernel/ptrace.c                                    | 16 ++++---
>   mm/frame_vector.c                                  |  9 ++--
>   mm/gup.c                                           | 50 ++++++++++------------
>   mm/memory.c                                        | 16 ++++---
>   mm/mempolicy.c                                     |  2 +-
>   mm/nommu.c                                         | 38 +++++++---------
>   mm/process_vm_access.c                             |  7 ++-
>   mm/util.c                                          |  8 ++--
>   net/ceph/pagevec.c                                 |  2 +-
>   security/tomoyo/domain.c                           |  2 +-
>   virt/kvm/async_pf.c                                |  3 +-
>   virt/kvm/kvm_main.c                                | 11 +++--
>   61 files changed, 260 insertions(+), 187 deletions(-)
> _______________________________________________
> dri-devel mailing list
> dri-devel at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply

* [PATCH 2/3] irqchip: mtk-cirq: Add mediatek mtk-cirq implement
From: Marc Zyngier @ 2016-10-13  8:28 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1476335194-26604-3-git-send-email-youlin.pei@mediatek.com>

On Thu, 13 Oct 2016 13:06:33 +0800
Youlin Pei <youlin.pei@mediatek.com> wrote:

> This commit add the mtk-cirq implement for mt2701.

Can you please expand this a bit?

> 
> Signed-off-by: Youlin Pei <youlin.pei@mediatek.com>
> ---
>  drivers/irqchip/Makefile       |    2 +-
>  drivers/irqchip/irq-mtk-cirq.c |  257 ++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 258 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/irqchip/irq-mtk-cirq.c
> 
> diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
> index 4c203b6..eee95c6 100644
> --- a/drivers/irqchip/Makefile
> +++ b/drivers/irqchip/Makefile
> @@ -59,7 +59,7 @@ obj-$(CONFIG_BCM7120_L2_IRQ)		+= irq-bcm7120-l2.o
>  obj-$(CONFIG_BRCMSTB_L2_IRQ)		+= irq-brcmstb-l2.o
>  obj-$(CONFIG_KEYSTONE_IRQ)		+= irq-keystone.o
>  obj-$(CONFIG_MIPS_GIC)			+= irq-mips-gic.o
> -obj-$(CONFIG_ARCH_MEDIATEK)		+= irq-mtk-sysirq.o
> +obj-$(CONFIG_ARCH_MEDIATEK)		+= irq-mtk-sysirq.o irq-mtk-cirq.o
>  obj-$(CONFIG_ARCH_DIGICOLOR)		+= irq-digicolor.o
>  obj-$(CONFIG_RENESAS_H8300H_INTC)	+= irq-renesas-h8300h.o
>  obj-$(CONFIG_RENESAS_H8S_INTC)		+= irq-renesas-h8s.o
> diff --git a/drivers/irqchip/irq-mtk-cirq.c b/drivers/irqchip/irq-mtk-cirq.c
> new file mode 100644
> index 0000000..544767d
> --- /dev/null
> +++ b/drivers/irqchip/irq-mtk-cirq.c
> @@ -0,0 +1,257 @@
> +/*
> + * Copyright (c) 2016 MediaTek Inc.
> + * Author: Youlin.Pei <youlin.pei@mediatek.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/irq.h>
> +#include <linux/irqchip.h>
> +#include <linux/irqdomain.h>
> +#include <linux/of.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_address.h>
> +#include <linux/io.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +#include <linux/syscore_ops.h>
> +
> +#define CIRQ_MASK_SET	0xC0
> +#define CIRQ_MASK_CLR	0x100
> +#define CIRQ_SENS_SET	0x180
> +#define CIRQ_SENS_CLR	0x1C0
> +#define CIRQ_POL_SET	0x240
> +#define CIRQ_POL_CLR	0x280
> +#define CIRQ_CONTROL	0x300
> +
> +#define CIRQ_EN		0x1
> +#define CIRQ_EDGE	0x2
> +#define CIRQ_FLUSH	0x4
> +
> +#define CIRQ_IRQ_NUM    0x200
> +
> +struct mtk_cirq_chip_data {
> +	void __iomem *base;
> +	unsigned int ext_irq_start;
> +};
> +
> +struct mtk_cirq_chip_data *cirq_data;

Any reason why this is not static?

> +
> +static void mtk_cirq_write_mask(struct irq_data *data, unsigned int offset)
> +{
> +	struct mtk_cirq_chip_data *chip_data = data->chip_data;
> +	unsigned int cirq_num = data->hwirq - chip_data->ext_irq_start;

I wonder why you have to compute this offset. Ideally, data->hwirq
should be the bit position (starting at zero). The fact that you have
an offset between the pin number in this driver and the IRQ number in
the GIC or sysirq should be resolved at alloc time.

> +	u32 mask = 1 << (cirq_num % 32);
> +
> +	writel(mask, chip_data->base + offset + (cirq_num / 32) * 4);
> +}
> +
> +static void mtk_cirq_mask(struct irq_data *data)
> +{
> +	mtk_cirq_write_mask(data, CIRQ_MASK_SET);
> +	irq_chip_mask_parent(data);
> +}
> +
> +static void mtk_cirq_unmask(struct irq_data *data)
> +{
> +	mtk_cirq_write_mask(data, CIRQ_MASK_CLR);
> +	irq_chip_unmask_parent(data);
> +}
> +
> +static int mtk_cirq_set_type(struct irq_data *data, unsigned int type)
> +{
> +	int ret;
> +
> +	switch (type & IRQ_TYPE_SENSE_MASK) {
> +	case IRQ_TYPE_EDGE_FALLING:
> +		mtk_cirq_write_mask(data, CIRQ_POL_CLR);
> +		mtk_cirq_write_mask(data, CIRQ_SENS_CLR);
> +		break;
> +	case IRQ_TYPE_EDGE_RISING:
> +		mtk_cirq_write_mask(data, CIRQ_POL_SET);
> +		mtk_cirq_write_mask(data, CIRQ_SENS_CLR);
> +		break;
> +	case IRQ_TYPE_LEVEL_LOW:
> +		mtk_cirq_write_mask(data, CIRQ_POL_CLR);
> +		mtk_cirq_write_mask(data, CIRQ_SENS_SET);
> +		break;
> +	case IRQ_TYPE_LEVEL_HIGH:
> +		mtk_cirq_write_mask(data, CIRQ_POL_SET);
> +		mtk_cirq_write_mask(data, CIRQ_SENS_SET);
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	data = data->parent_data;
> +	ret = data->chip->irq_set_type(data, type);
> +	return ret;
> +}
> +
> +static struct irq_chip mtk_cirq_chip = {
> +	.name			= "MT_CIRQ",
> +	.irq_mask		= mtk_cirq_mask,
> +	.irq_unmask		= mtk_cirq_unmask,
> +	.irq_eoi		= irq_chip_eoi_parent,
> +	.irq_set_type		= mtk_cirq_set_type,
> +	.irq_retrigger		= irq_chip_retrigger_hierarchy,
> +#ifdef CONFIG_SMP
> +	.irq_set_affinity	= irq_chip_set_affinity_parent,
> +#endif
> +};

I'm surprised that you don't implement irq_set_wake. Do you wake-up on
*any* interrupt?

> +
> +static int mtk_cirq_domain_translate(struct irq_domain *d,
> +				     struct irq_fwspec *fwspec,
> +				     unsigned long *hwirq,
> +				     unsigned int *type)
> +{
> +	if (is_of_node(fwspec->fwnode)) {
> +		if (fwspec->param_count != 3)
> +			return -EINVAL;
> +
> +		/* No PPI should point to this domain */
> +		if (fwspec->param[0] != 0)
> +			return -EINVAL;
> +
> +		/* cirq support irq number check */
> +		if (fwspec->param[1] < ;)
> +			return -EINVAL;
> +
> +		*hwirq = fwspec->param[1];

So if you turn this into:

		*hwirq = fwspec->param[1] - cirq_data->ext_irq_start;

and drop the above offset computing in your write function, you'd have
something that'd make a bit more sense.

> +		*type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
> +		return 0;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static int mtk_cirq_domain_alloc(struct irq_domain *domain, unsigned int virq,
> +				 unsigned int nr_irqs, void *arg)
> +{
> +	int i;
> +	irq_hw_number_t hwirq;
> +	struct irq_fwspec *fwspec = arg;
> +	struct irq_fwspec parent_fwspec = *fwspec;
> +
> +	if (fwspec->param_count != 3)
> +		return -EINVAL;
> +
> +	/* cirq doesn't support PPI */
> +	if (fwspec->param[0])
> +		return -EINVAL;
> +
> +	if (fwspec->param[1] < cirq_data->ext_irq_start)
> +		return -EINVAL;
> +
> +	hwirq = fwspec->param[1];
> +	for (i = 0; i < nr_irqs; i++)
> +		irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
> +					      &mtk_cirq_chip,
> +					      domain->host_data);
> +
> +	parent_fwspec.fwnode = domain->parent->fwnode;
> +	return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs,
> +					    &parent_fwspec);
> +}
> +
> +static const struct irq_domain_ops cirq_domain_ops = {
> +	.translate	= mtk_cirq_domain_translate,
> +	.alloc		= mtk_cirq_domain_alloc,
> +	.free		= irq_domain_free_irqs_common,
> +};
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int mtk_cirq_suspend(void)
> +{
> +	u32 value;
> +
> +	/* set edge_only mode, record edge-triggerd interrupts */
> +	/* enable cirq */
> +	value = readl_relaxed(cirq_data->base + CIRQ_CONTROL);
> +	value |= (CIRQ_EDGE | CIRQ_EN);
> +	writel(value, cirq_data->base + CIRQ_CONTROL);
> +	return 0;
> +}
> +
> +static void mtk_cirq_resume(void)
> +{
> +	u32 value;
> +
> +	/* flush recored interrupts, will send signals to parent controller */
> +	value = readl_relaxed(cirq_data->base + CIRQ_CONTROL);
> +	writel(value | CIRQ_FLUSH, cirq_data->base + CIRQ_CONTROL);
> +
> +	/* disable cirq */
> +	value = readl_relaxed(cirq_data->base + CIRQ_CONTROL);
> +	value &= ~(CIRQ_EDGE | CIRQ_EN);
> +	writel(value, cirq_data->base + CIRQ_CONTROL);
> +}
> +
> +static struct syscore_ops mtk_cirq_syscore_ops = {
> +	.suspend	= mtk_cirq_suspend,
> +	.resume		= mtk_cirq_resume,
> +};
> +
> +static void mtk_cirq_syscore_init(void)
> +{
> +	register_syscore_ops(&mtk_cirq_syscore_ops);
> +}
> +#else
> +static inline void mtk_cirq_syscore_init(void) {}
> +#endif
> +
> +static int __init mtk_cirq_of_init(struct device_node *node,
> +				   struct device_node *parent)
> +{
> +	struct irq_domain *domain, *domain_parent;
> +	int ret;
> +
> +	domain_parent = irq_find_host(parent);
> +	if (!domain_parent) {
> +		pr_err("mtk_cirq: interrupt-parent not found\n");
> +		return -EINVAL;
> +	}
> +
> +	cirq_data = kzalloc(sizeof(*cirq_data), GFP_KERNEL);
> +	if (!cirq_data)
> +		return -ENOMEM;
> +
> +	cirq_data->base = of_iomap(node, 0);
> +	if (!cirq_data->base) {
> +		pr_err("mtk_cirq: unable to map cirq register\n");
> +		ret = -ENXIO;
> +		goto out_free;
> +	}
> +
> +	if (of_property_read_u32(node, "mediatek,ext-irq-start",
> +				 &cirq_data->ext_irq_start)) {
> +		ret = -EINVAL;
> +		goto out_free;
> +	}
> +
> +	domain = irq_domain_add_hierarchy(domain_parent, 0, CIRQ_IRQ_NUM, node,
> +					  &cirq_domain_ops, cirq_data);
> +	if (!domain) {
> +		ret = -ENOMEM;
> +		goto out_unmap;
> +	}
> +
> +	mtk_cirq_syscore_init();
> +
> +	return 0;
> +
> +out_unmap:
> +	iounmap(cirq_data->base);
> +out_free:
> +	kfree(cirq_data);
> +	return ret;
> +}
> +
> +IRQCHIP_DECLARE(mtk_cirq, "mediatek,mt2701-cirq", mtk_cirq_of_init);

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny.

^ permalink raw reply

* [PATCH V3 02/10] ras: acpi/apei: cper: generic error data entry v3 per ACPI 6.1
From: Suzuki K Poulose @ 2016-10-13  8:50 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <c17a0c03-5037-d88d-f096-4cc72dbfcb99@codeaurora.org>

On 12/10/16 23:10, Baicar, Tyler wrote:
> Hello Suzuki,
>
> Thank you for the feedback! Responses below.
>
>
> On 10/11/2016 11:28 AM, Suzuki K Poulose wrote:
>> On 07/10/16 22:31, Tyler Baicar wrote:
>>> Currently when a RAS error is reported it is not timestamped.
>>> The ACPI 6.1 spec adds the timestamp field to the generic error
>>> data entry v3 structure. The timestamp of when the firmware
>>> generated the error is now being reported.
>>>
>>> Signed-off-by: Jonathan (Zhixiong) Zhang <zjzhang@codeaurora.org>
>>> Signed-off-by: Richard Ruigrok <rruigrok@codeaurora.org>
>>> Signed-off-by: Tyler Baicar <tbaicar@codeaurora.org>
>>> Signed-off-by: Naveen Kaje <nkaje@codeaurora.org>
>>
>> Please could you keep the people who reviewed/commented on your series in the past,
>> whenever you post a new version ?
> Do you mean to just send the new version to their e-mail directly in addition to the lists? If so, I will do that next time.

If you send a new version of a series to the list, it is a good idea to keep
the people who commented (significantly) on your previous version in Cc, especially
when you have addressed their feedback. That will help them to keep track of the
series. People can always see the new version in the list, but then it is so easy
to miss something in the 100s of emails you get each day. I am sure people have
special filters for the emails based on if they are in Cc/To etc.

>
> I know you provided good feedback on the previous patchset, but I did not have anyone specifically respond to add "reviewed-by:...". I don't think I should add reviewed-by for someone unless they specifically add it in a response :)

No, I haven't yet "Reviewed-by" your patches. I had some comments on it, which means
I expected it to be addressed as you committed in your response.

>>> diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
>>> index 3021f0e..c8488f1 100644
>>> --- a/drivers/acpi/apei/ghes.c
>>> +++ b/drivers/acpi/apei/ghes.c
>>> @@ -80,6 +80,10 @@

> I think that should work to avoid duplication. I will move them to a header file in the next patchset.
>>> +
>>> +static void cper_estatus_print_section_v300(const char *pfx,
>>> +    const struct acpi_hest_generic_data_v300 *gdata)
>>> +{
>>> +    __u8 hour, min, sec, day, mon, year, century, *timestamp;
>>> +
>>> +    if (gdata->validation_bits & ACPI_HEST_GEN_VALID_TIMESTAMP) {
>>> +        timestamp = (__u8 *)&(gdata->time_stamp);
>>> +        memcpy(&sec, timestamp, 1);
>>> +        memcpy(&min, timestamp + 1, 1);
>>> +        memcpy(&hour, timestamp + 2, 1);
>>> +        memcpy(&day, timestamp + 4, 1);
>>> +        memcpy(&mon, timestamp + 5, 1);
>>> +        memcpy(&year, timestamp + 6, 1);
>>> +        memcpy(&century, timestamp + 7, 1);
>>> +        printk("%stime: ", pfx);
>>> +        printk("%7s", 0x01 & *(timestamp + 3) ? "precise" : "");
>>
>> What format is the (timestamp + 3) stored in ? Does it need conversion ?
> The third byte of the timestamp is currently only used to determine if the time is precise or not. Bit 0 is used to specify that and the other bits in this byte are marked as reserved. This is shown in table 247 of the UEFI spec 2.6:
>
> Byte 3:
>   Bit 0 ? Timestamp is precise if this bit is set and correlates to the time of the error event.
>   Bit 7:1 ? Reserved

Is it always the same endianness as that of the CPU ?

Cheers
Suzuki

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox