* [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 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 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 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(¤t->mm->mmap_sem);
> npages = get_user_page_nowait(addr, write_fault, page);
> up_read(¤t->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
* 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 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
* [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 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 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 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 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
* 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 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
* [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 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)
®_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)
®_window[pos],
- ®, sizeof(reg), 0)
+ ®, sizeof(reg),
+ FOLL_FORCE)
!= sizeof(reg))
return -EFAULT;
if (access_process_vm(target,
(unsigned long) u,
- ®, sizeof(reg), 1)
+ ®, 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)
®_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,
- ®, sizeof(reg), 0)
+ ®, sizeof(reg),
+ FOLL_FORCE)
!= sizeof(reg))
return -EFAULT;
if (access_process_vm(target,
(unsigned long)
®_window[pos],
- ®, sizeof(reg), 1)
+ ®, 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 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 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 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 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(¤t->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, >t->guptasks);
spin_unlock(>t->guptasklock);
- r = get_user_pages(userptr, num_pages, write, 0, p, NULL);
+ r = get_user_pages(userptr, num_pages, flags, p, NULL);
spin_lock(>t->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(¤t->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(¤t->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(¤t->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(¤t->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(¤t->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(¤t->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(¤t->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(¤t->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(¤t->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 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 04/10] mm: replace get_user_pages_locked() 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_locked()
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>
---
include/linux/mm.h | 2 +-
mm/frame_vector.c | 8 +++++++-
mm/gup.c | 12 +++---------
mm/nommu.c | 5 ++++-
4 files changed, 15 insertions(+), 12 deletions(-)
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 6adc4bc..27ab538 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -1282,7 +1282,7 @@ long get_user_pages(unsigned long start, unsigned long nr_pages,
int write, int force, struct page **pages,
struct vm_area_struct **vmas);
long get_user_pages_locked(unsigned long start, unsigned long nr_pages,
- int write, int force, struct page **pages, int *locked);
+ unsigned int gup_flags, 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,
struct page **pages, unsigned int gup_flags);
diff --git a/mm/frame_vector.c b/mm/frame_vector.c
index 381bb07..81b6749 100644
--- a/mm/frame_vector.c
+++ b/mm/frame_vector.c
@@ -41,10 +41,16 @@ int get_vaddr_frames(unsigned long start, unsigned int nr_frames,
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;
@@ -59,7 +65,7 @@ int get_vaddr_frames(unsigned long start, unsigned int nr_frames,
vec->got_ref = true;
vec->is_pfns = false;
ret = get_user_pages_locked(start, nr_frames,
- write, force, (struct page **)(vec->ptrs), &locked);
+ gup_flags, (struct page **)(vec->ptrs), &locked);
goto out;
}
diff --git a/mm/gup.c b/mm/gup.c
index cfcb014..7a0d033 100644
--- a/mm/gup.c
+++ b/mm/gup.c
@@ -838,18 +838,12 @@ static __always_inline long __get_user_pages_locked(struct task_struct *tsk,
* up_read(&mm->mmap_sem);
*/
long get_user_pages_locked(unsigned long start, unsigned long nr_pages,
- int write, int force, struct page **pages,
+ unsigned int gup_flags, struct page **pages,
int *locked)
{
- 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, NULL, locked, true, flags);
+ pages, NULL, locked, true,
+ gup_flags | FOLL_TOUCH);
}
EXPORT_SYMBOL(get_user_pages_locked);
diff --git a/mm/nommu.c b/mm/nommu.c
index 7e27add..842cfdd 100644
--- a/mm/nommu.c
+++ b/mm/nommu.c
@@ -176,9 +176,12 @@ long get_user_pages(unsigned long start, unsigned long nr_pages,
EXPORT_SYMBOL(get_user_pages);
long get_user_pages_locked(unsigned long start, unsigned long nr_pages,
- int write, int force, struct page **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);
}
EXPORT_SYMBOL(get_user_pages_locked);
--
2.10.0
^ permalink raw reply related
* [PATCH 03/10] mm: replace get_user_pages_unlocked() 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_unlocked()
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/mips/mm/gup.c | 2 +-
arch/s390/mm/gup.c | 3 ++-
arch/sh/mm/gup.c | 3 ++-
arch/sparc/mm/gup.c | 3 ++-
arch/x86/mm/gup.c | 2 +-
drivers/media/pci/ivtv/ivtv-udma.c | 4 ++--
drivers/media/pci/ivtv/ivtv-yuv.c | 5 +++--
drivers/scsi/st.c | 5 ++---
drivers/video/fbdev/pvr2fb.c | 4 ++--
include/linux/mm.h | 2 +-
mm/gup.c | 14 ++++----------
mm/nommu.c | 11 ++---------
mm/util.c | 3 ++-
net/ceph/pagevec.c | 2 +-
14 files changed, 27 insertions(+), 36 deletions(-)
diff --git a/arch/mips/mm/gup.c b/arch/mips/mm/gup.c
index 42d124f..d8c3c15 100644
--- a/arch/mips/mm/gup.c
+++ b/arch/mips/mm/gup.c
@@ -287,7 +287,7 @@ int get_user_pages_fast(unsigned long start, int nr_pages, int write,
pages += nr;
ret = get_user_pages_unlocked(start, (end - start) >> PAGE_SHIFT,
- write, 0, pages);
+ pages, write ? FOLL_WRITE : 0);
/* Have to be a bit careful with return values */
if (nr > 0) {
diff --git a/arch/s390/mm/gup.c b/arch/s390/mm/gup.c
index adb0c34..18d4107 100644
--- a/arch/s390/mm/gup.c
+++ b/arch/s390/mm/gup.c
@@ -266,7 +266,8 @@ int get_user_pages_fast(unsigned long start, int nr_pages, int write,
/* Try to get the remaining pages with get_user_pages */
start += nr << PAGE_SHIFT;
pages += nr;
- ret = get_user_pages_unlocked(start, nr_pages - nr, write, 0, pages);
+ ret = get_user_pages_unlocked(start, nr_pages - nr, pages,
+ write ? FOLL_WRITE : 0);
/* Have to be a bit careful with return values */
if (nr > 0)
ret = (ret < 0) ? nr : ret + nr;
diff --git a/arch/sh/mm/gup.c b/arch/sh/mm/gup.c
index 40fa6c8..063c298 100644
--- a/arch/sh/mm/gup.c
+++ b/arch/sh/mm/gup.c
@@ -258,7 +258,8 @@ int get_user_pages_fast(unsigned long start, int nr_pages, int write,
pages += nr;
ret = get_user_pages_unlocked(start,
- (end - start) >> PAGE_SHIFT, write, 0, pages);
+ (end - start) >> PAGE_SHIFT, pages,
+ write ? FOLL_WRITE : 0);
/* Have to be a bit careful with return values */
if (nr > 0) {
diff --git a/arch/sparc/mm/gup.c b/arch/sparc/mm/gup.c
index 4e06750..cd0e32b 100644
--- a/arch/sparc/mm/gup.c
+++ b/arch/sparc/mm/gup.c
@@ -238,7 +238,8 @@ int get_user_pages_fast(unsigned long start, int nr_pages, int write,
pages += nr;
ret = get_user_pages_unlocked(start,
- (end - start) >> PAGE_SHIFT, write, 0, pages);
+ (end - start) >> PAGE_SHIFT, pages,
+ write ? FOLL_WRITE : 0);
/* Have to be a bit careful with return values */
if (nr > 0) {
diff --git a/arch/x86/mm/gup.c b/arch/x86/mm/gup.c
index b8b6a60..0d4fb3e 100644
--- a/arch/x86/mm/gup.c
+++ b/arch/x86/mm/gup.c
@@ -435,7 +435,7 @@ int get_user_pages_fast(unsigned long start, int nr_pages, int write,
ret = get_user_pages_unlocked(start,
(end - start) >> PAGE_SHIFT,
- write, 0, pages);
+ pages, write ? FOLL_WRITE : 0);
/* Have to be a bit careful with return values */
if (nr > 0) {
diff --git a/drivers/media/pci/ivtv/ivtv-udma.c b/drivers/media/pci/ivtv/ivtv-udma.c
index 4769469..2c9232e 100644
--- a/drivers/media/pci/ivtv/ivtv-udma.c
+++ b/drivers/media/pci/ivtv/ivtv-udma.c
@@ -124,8 +124,8 @@ int ivtv_udma_setup(struct ivtv *itv, unsigned long ivtv_dest_addr,
}
/* Get user pages for DMA Xfer */
- err = get_user_pages_unlocked(user_dma.uaddr, user_dma.page_count, 0,
- 1, dma->map);
+ err = get_user_pages_unlocked(user_dma.uaddr, user_dma.page_count,
+ dma->map, FOLL_FORCE);
if (user_dma.page_count != err) {
IVTV_DEBUG_WARN("failed to map user pages, returned %d instead of %d\n",
diff --git a/drivers/media/pci/ivtv/ivtv-yuv.c b/drivers/media/pci/ivtv/ivtv-yuv.c
index b094054..f7299d3 100644
--- a/drivers/media/pci/ivtv/ivtv-yuv.c
+++ b/drivers/media/pci/ivtv/ivtv-yuv.c
@@ -76,11 +76,12 @@ static int ivtv_yuv_prep_user_dma(struct ivtv *itv, struct ivtv_user_dma *dma,
/* Get user pages for DMA Xfer */
y_pages = get_user_pages_unlocked(y_dma.uaddr,
- y_dma.page_count, 0, 1, &dma->map[0]);
+ y_dma.page_count, &dma->map[0], FOLL_FORCE);
uv_pages = 0; /* silence gcc. value is set and consumed only if: */
if (y_pages == y_dma.page_count) {
uv_pages = get_user_pages_unlocked(uv_dma.uaddr,
- uv_dma.page_count, 0, 1, &dma->map[y_pages]);
+ uv_dma.page_count, &dma->map[y_pages],
+ FOLL_FORCE);
}
if (y_pages != y_dma.page_count || uv_pages != uv_dma.page_count) {
diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c
index 7af5226..618422e 100644
--- a/drivers/scsi/st.c
+++ b/drivers/scsi/st.c
@@ -4922,9 +4922,8 @@ static int sgl_map_user_pages(struct st_buffer *STbp,
res = get_user_pages_unlocked(
uaddr,
nr_pages,
- rw == READ,
- 0, /* don't force */
- pages);
+ pages,
+ rw == READ ? FOLL_WRITE : 0); /* don't force */
/* Errors and no page mapped should return here */
if (res < nr_pages)
diff --git a/drivers/video/fbdev/pvr2fb.c b/drivers/video/fbdev/pvr2fb.c
index 3b1ca44..a2564ab 100644
--- a/drivers/video/fbdev/pvr2fb.c
+++ b/drivers/video/fbdev/pvr2fb.c
@@ -686,8 +686,8 @@ static ssize_t pvr2fb_write(struct fb_info *info, const char *buf,
if (!pages)
return -ENOMEM;
- ret = get_user_pages_unlocked((unsigned long)buf, nr_pages, WRITE,
- 0, pages);
+ ret = get_user_pages_unlocked((unsigned long)buf, nr_pages, pages,
+ FOLL_WRITE);
if (ret < nr_pages) {
nr_pages = ret;
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 2db98b6..6adc4bc 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -1287,7 +1287,7 @@ long __get_user_pages_unlocked(struct task_struct *tsk, struct mm_struct *mm,
unsigned long start, unsigned long nr_pages,
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);
+ struct page **pages, unsigned int gup_flags);
int get_user_pages_fast(unsigned long start, int nr_pages, int write,
struct page **pages);
diff --git a/mm/gup.c b/mm/gup.c
index 3d620dd..cfcb014 100644
--- a/mm/gup.c
+++ b/mm/gup.c
@@ -897,17 +897,10 @@ EXPORT_SYMBOL(__get_user_pages_unlocked);
* "force" parameter).
*/
long get_user_pages_unlocked(unsigned long start, unsigned long nr_pages,
- int write, int force, struct page **pages)
+ struct page **pages, unsigned int gup_flags)
{
- 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,
- pages, flags);
+ pages, gup_flags | FOLL_TOUCH);
}
EXPORT_SYMBOL(get_user_pages_unlocked);
@@ -1525,7 +1518,8 @@ int get_user_pages_fast(unsigned long start, int nr_pages, int write,
start += nr << PAGE_SHIFT;
pages += nr;
- ret = get_user_pages_unlocked(start, nr_pages - nr, write, 0, pages);
+ ret = get_user_pages_unlocked(start, nr_pages - nr, pages,
+ write ? FOLL_WRITE : 0);
/* Have to be a bit careful with return values */
if (nr > 0) {
diff --git a/mm/nommu.c b/mm/nommu.c
index 925dcc1..7e27add 100644
--- a/mm/nommu.c
+++ b/mm/nommu.c
@@ -197,17 +197,10 @@ long __get_user_pages_unlocked(struct task_struct *tsk, struct mm_struct *mm,
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)
+ struct page **pages, unsigned int gup_flags)
{
- 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,
- pages, flags);
+ pages, gup_flags);
}
EXPORT_SYMBOL(get_user_pages_unlocked);
diff --git a/mm/util.c b/mm/util.c
index 662cddf..4c685bd 100644
--- a/mm/util.c
+++ b/mm/util.c
@@ -283,7 +283,8 @@ EXPORT_SYMBOL_GPL(__get_user_pages_fast);
int __weak get_user_pages_fast(unsigned long start,
int nr_pages, int write, struct page **pages)
{
- return get_user_pages_unlocked(start, nr_pages, write, 0, pages);
+ return get_user_pages_unlocked(start, nr_pages, pages,
+ write ? FOLL_WRITE : 0);
}
EXPORT_SYMBOL_GPL(get_user_pages_fast);
diff --git a/net/ceph/pagevec.c b/net/ceph/pagevec.c
index 00d2601..1a7c9a7 100644
--- a/net/ceph/pagevec.c
+++ b/net/ceph/pagevec.c
@@ -26,7 +26,7 @@ struct page **ceph_get_direct_page_vector(const void __user *data,
while (got < num_pages) {
rc = get_user_pages_unlocked(
(unsigned long)data + ((unsigned long)got * PAGE_SIZE),
- num_pages - got, write_page, 0, pages + got);
+ num_pages - got, pages + got, write_page ? FOLL_WRITE : 0);
if (rc < 0)
break;
BUG_ON(rc == 0);
--
2.10.0
^ permalink raw reply related
* [PATCH 02/10] mm: remove write/force parameters from __get_user_pages_unlocked()
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_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(¤t->mm->mmap_sem);
npages = get_user_page_nowait(addr, write_fault, page);
up_read(¤t->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;
--
2.10.0
^ permalink raw reply related
* [PATCH 01/10] mm: remove write/force parameters from __get_user_pages_locked()
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_locked()
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>
---
mm/gup.c | 47 +++++++++++++++++++++++++++++++++--------------
1 file changed, 33 insertions(+), 14 deletions(-)
diff --git a/mm/gup.c b/mm/gup.c
index 96b2b2f..ba83942 100644
--- a/mm/gup.c
+++ b/mm/gup.c
@@ -729,7 +729,6 @@ static __always_inline long __get_user_pages_locked(struct task_struct *tsk,
struct mm_struct *mm,
unsigned long start,
unsigned long nr_pages,
- int write, int force,
struct page **pages,
struct vm_area_struct **vmas,
int *locked, bool notify_drop,
@@ -747,10 +746,6 @@ static __always_inline long __get_user_pages_locked(struct task_struct *tsk,
if (pages)
flags |= FOLL_GET;
- if (write)
- flags |= FOLL_WRITE;
- if (force)
- flags |= FOLL_FORCE;
pages_done = 0;
lock_dropped = false;
@@ -846,9 +841,15 @@ long get_user_pages_locked(unsigned long start, unsigned long nr_pages,
int write, int force, struct page **pages,
int *locked)
{
+ 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,
- write, force, pages, NULL, locked, true,
- FOLL_TOUCH);
+ pages, NULL, locked, true, flags);
}
EXPORT_SYMBOL(get_user_pages_locked);
@@ -869,9 +870,15 @@ __always_inline long __get_user_pages_unlocked(struct task_struct *tsk, struct m
{
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, write, force,
- pages, NULL, &locked, false, gup_flags);
+ ret = __get_user_pages_locked(tsk, mm, start, nr_pages, pages, NULL,
+ &locked, false, gup_flags);
if (locked)
up_read(&mm->mmap_sem);
return ret;
@@ -963,9 +970,15 @@ 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)
{
- return __get_user_pages_locked(tsk, mm, start, nr_pages, write, force,
- pages, vmas, NULL, false,
- FOLL_TOUCH | FOLL_REMOTE);
+ 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);
}
EXPORT_SYMBOL(get_user_pages_remote);
@@ -979,9 +992,15 @@ long get_user_pages(unsigned long start, unsigned long nr_pages,
int write, int force, 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,
- write, force, pages, vmas, NULL, false,
- FOLL_TOUCH);
+ pages, vmas, NULL, false, flags);
}
EXPORT_SYMBOL(get_user_pages);
--
2.10.0
^ permalink raw reply related
* [PATCH 00/10] mm: adjust get_user_pages* functions to explicitly pass FOLL_* flags
From: Lorenzo Stoakes @ 2016-10-13 0:20 UTC (permalink / raw)
To: linux-arm-kernel
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
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(-)
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox