* Re: [PATCH v2] arm64:kgdb: Fix kernel single-stepping
From: Marc Zyngier @ 2020-02-20 15:06 UTC (permalink / raw)
To: minyard
Cc: Will Deacon, Catalin Marinas, linux-arm-kernel, Corey Minyard,
linux-kernel, Corey Minyard
In-Reply-To: <20200220145048.GH3704@minyard.net>
On 2020-02-20 14:50, Corey Minyard wrote:
> On Thu, Feb 20, 2020 at 02:21:36PM +0000, Marc Zyngier wrote:
>> On 2020-02-19 15:24, minyard@acm.org wrote:
>> > From: Corey Minyard <cminyard@mvista.com>
>>
>> [...]
>>
>> > After studying the EL0 handling for this, I realized an issue with using
>> > MDSCR to check if single step is enabled: it can be expensive on a VM.
>> > So check the task flag first to see if single step is enabled. Then
>> > check MDSCR if the task flag is set.
>>
>> Very tangential remark: I'd really like people *not* to try and
>> optimize
>> Linux based on the behaviour of a hypervisor. In general, reading a
>> system register is fast, and the fact that it traps on a given
>> hypervisor
>> at some point may not be true in the future, nor be a valid assumption
>> across hypervisors.
>
> Normally I would agree, but I based this upon git commit
> https://github.com/torvalds/linux/commit/2a2830703a2371b47f7b50b1d35cb15dc0e2b717
> which seemed to say that it was a significant enough factor to do in
> the
> EL0 case.
And that's a blast from a distant past. Hypervisors have changed
drastically
over these 6 years, and I'm still sitting on a bunch of patches that
*could*
change the way MDSCR_EL1 is handled.
M.
--
Jazz is not dead. It just smells funny...
^ permalink raw reply
* [MODERATED] Re: [PATCH 0/2] more sampling fun 0
From: Greg KH @ 2020-02-20 15:05 UTC (permalink / raw)
To: speck
In-Reply-To: <20200220145510.GE160988@tassilo.jf.intel.com>
On Thu, Feb 20, 2020 at 06:55:10AM -0800, speck for Andi Kleen wrote:
> > Then we need to stop using RDRAND internally for our "give me a random
> > number api" which has spread to more and more parts of the kernel.
>
> Only if that API is called frequently enough. AFAIK it is not.
It's called by all sorts of places in the kernel today.
> Normally it's used for rare rekeying of hash tables etc., which
> doesn't happen very often.
"normally" :)
> > Here's a patch that does so:
> > https://lore.kernel.org/lkml/20200216161836.1976-1-Jason@zx2c4.com/
> > which I'm going to advise get merged now and backported to the stable
> > branches.
>
> Don't see any reason at this point. Only do it if there's an actual
> indication of a problem.
It's slow, why wouldn't we stop using it as we already have a much
faster call.
thanks,
greg k-h
^ permalink raw reply
* Re: [PATCH v4 1/3] btrfs: backref: Introduce the skeleton of btrfs_backref_iter
From: Josef Bacik @ 2020-02-20 15:05 UTC (permalink / raw)
To: Qu Wenruo, linux-btrfs; +Cc: Johannes Thumshirn
In-Reply-To: <20200218065649.126255-2-wqu@suse.com>
On 2/18/20 1:56 AM, Qu Wenruo wrote:
> Due to the complex nature of btrfs extent tree, when we want to iterate
> all backrefs of one extent, it involves quite a lot of work, like
> searching the EXTENT_ITEM/METADATA_ITEM, iteration through inline and keyed
> backrefs.
>
> Normally this would result pretty complex code, something like:
> btrfs_search_slot()
> /* Ensure we are at EXTENT_ITEM/METADATA_ITEM */
> while (1) { /* Loop for extent tree items */
> while (ptr < end) { /* Loop for inlined items */
> /* REAL WORK HERE */
> }
> next:
> ret = btrfs_next_item()
> /* Ensure we're still at keyed item for specified bytenr */
> }
>
> The idea of btrfs_backref_iter is to avoid such complex and hard to
> read code structure, but something like the following:
>
> iter = btrfs_backref_iter_alloc();
> ret = btrfs_backref_iter_start(iter, bytenr);
> if (ret < 0)
> goto out;
> for (; ; ret = btrfs_backref_iter_next(iter)) {
> /* REAL WORK HERE */
> }
> out:
> btrfs_backref_iter_free(iter);
>
> This patch is just the skeleton + btrfs_backref_iter_start() code.
>
> Signed-off-by: Qu Wenruo <wqu@suse.com>
> Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Reviewed-by: Josef Bacik <josef@toxicpanda.com>
Thanks,
Josef
^ permalink raw reply
* Re: [PATCH v2 0/2] hw/arm/xilinx_zynq: Fix USB port instantiation
From: Peter Maydell @ 2020-02-20 15:05 UTC (permalink / raw)
To: Guenter Roeck; +Cc: Alistair Francis, Gerd Hoffmann, qemu-arm, QEMU Developers
In-Reply-To: <20200215122354.13706-1-linux@roeck-us.net>
On Sat, 15 Feb 2020 at 12:23, Guenter Roeck <linux@roeck-us.net> wrote:
>
> USB ports on Xilinx Zync must be instantiated as TYPE_CHIPIDEA to work.
> Linux expects and checks various chipidea registers, which do not exist
> with the basic ehci emulation. This patch series fixes the problem.
>
> The first patch in the series fixes the actual problem.
>
> The second patch removes the now obsolete explicit Xilinx
> support from the EHCI code.
>
> v2: Introduced summary
>
> ----------------------------------------------------------------
> Guenter Roeck (2):
> hw/arm/xilinx_zynq: Fix USB port instantiation
> hw/usb/hcd-ehci-sysbus: Remove obsolete xlnx,ps7-usb class
Xilinx folks -- could you provide a reviewed-by or acked-by
for this series, please?
thanks
-- PMM
^ permalink raw reply
* Re: [PATCH v3 4/5] sched/pelt: Add a new runnable average signal
From: Dietmar Eggemann @ 2020-02-20 15:04 UTC (permalink / raw)
To: Vincent Guittot, mingo, peterz, juri.lelli, rostedt, bsegall,
mgorman, linux-kernel
Cc: pauld, parth, valentin.schneider, hdanton
In-Reply-To: <20200219125513.8953-1-vincent.guittot@linaro.org>
On 19/02/2020 13:55, Vincent Guittot wrote:
> Now that runnable_load_avg has been removed, we can replace it by a new
> signal that will highlight the runnable pressure on a cfs_rq. This signal
> track the waiting time of tasks on rq and can help to better define the
> state of rqs.
>
> At now, only util_avg is used to define the state of a rq:
> A rq with more that around 80% of utilization and more than 1 tasks is
> considered as overloaded.
Don't we make the distinction of overutilized and overloaded?
update_sg_lb_stats(), SG_OVERUTILIZED and SG_OVERLOAD
[...]
> @@ -310,13 +325,14 @@ int __update_load_avg_cfs_rq(u64 now, struct cfs_rq *cfs_rq)
> * util_sum = cpu_scale * load_sum
> * runnable_sum = util_sum
> *
> - * load_avg is not supported and meaningless.
> + * load_avg and runnable_load_avg are not supported and meaningless.
Nit pick:
s/runnable_load_avg/runnable_avg
[...]
> + * load_avg and runnable_load_avg are not supported and meaningless.
> *
> */
>
> int update_dl_rq_load_avg(u64 now, struct rq *rq, int running)
[...]
> - * load_avg is not supported and meaningless.
> + * load_avg and runnable_load_avg are not supported and meaningless.
> *
> */
>
> @@ -389,9 +406,11 @@ int update_irq_load_avg(struct rq *rq, u64 running)
[...]
^ permalink raw reply
* [PATCH v6 04/16] x86/mm: Use helper fault_signal_pending()
From: Peter Xu @ 2020-02-20 15:02 UTC (permalink / raw)
To: linux-mm, linux-kernel
Cc: Peter Xu, Martin Cracauer, Mike Rapoport, Hugh Dickins,
Jerome Glisse, Kirill A . Shutemov, Matthew Wilcox,
Pavel Emelyanov, Brian Geffon, Maya Gokhale, Denis Plotnikov,
Andrea Arcangeli, Johannes Weiner, Dr . David Alan Gilbert,
Linus Torvalds, Mike Kravetz, Marty McFadden, David Hildenbrand,
Bobby Powers, Mel Gorman
In-Reply-To: <20200220145432.4561-1-peterx@redhat.com>
Let's move the fatal signal check even earlier so that we can directly
use the new fault_signal_pending() in x86 mm code.
Signed-off-by: Peter Xu <peterx@redhat.com>
---
arch/x86/mm/fault.c | 28 +++++++++++++---------------
1 file changed, 13 insertions(+), 15 deletions(-)
diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c
index fa4ea09593ab..6a00bc8d047f 100644
--- a/arch/x86/mm/fault.c
+++ b/arch/x86/mm/fault.c
@@ -1442,27 +1442,25 @@ void do_user_addr_fault(struct pt_regs *regs,
fault = handle_mm_fault(vma, address, flags);
major |= fault & VM_FAULT_MAJOR;
+ /* Quick path to respond to signals */
+ if (fault_signal_pending(fault, regs)) {
+ if (!user_mode(regs))
+ no_context(regs, hw_error_code, address, SIGBUS,
+ BUS_ADRERR);
+ return;
+ }
+
/*
* If we need to retry the mmap_sem has already been released,
* and if there is a fatal signal pending there is no guarantee
* that we made any progress. Handle this case first.
*/
- if (unlikely(fault & VM_FAULT_RETRY)) {
+ if (unlikely((fault & VM_FAULT_RETRY) &&
+ (flags & FAULT_FLAG_ALLOW_RETRY))) {
/* Retry at most once */
- if (flags & FAULT_FLAG_ALLOW_RETRY) {
- flags &= ~FAULT_FLAG_ALLOW_RETRY;
- flags |= FAULT_FLAG_TRIED;
- if (!fatal_signal_pending(tsk))
- goto retry;
- }
-
- /* User mode? Just return to handle the fatal exception */
- if (flags & FAULT_FLAG_USER)
- return;
-
- /* Not returning to user mode? Handle exceptions or die: */
- no_context(regs, hw_error_code, address, SIGBUS, BUS_ADRERR);
- return;
+ flags &= ~FAULT_FLAG_ALLOW_RETRY;
+ flags |= FAULT_FLAG_TRIED;
+ goto retry;
}
up_read(&mm->mmap_sem);
--
2.24.1
^ permalink raw reply related
* Re: [PATCH v3 0/3] Introduce per-task latency_nice for scheduler hints
From: Qais Yousef @ 2020-02-20 15:03 UTC (permalink / raw)
To: chris hyser
Cc: Parth Shah, vincent.guittot, patrick.bellasi, valentin.schneider,
dhaval.giani, dietmar.eggemann, linux-kernel, peterz, mingo,
pavel, qperret, David.Laight, pjt, tj
In-Reply-To: <a332d633-7826-b85d-5d9f-5e34f9de084a@oracle.com>
On 02/20/20 09:30, chris hyser wrote:
> > The below diff works out well enough in-order to align permission checks
> > with NICE.
> >
> > diff --git a/kernel/sched/core.c b/kernel/sched/core.c
> > index 2bfcff5623f9..ef4a397c9170 100644
> > --- a/kernel/sched/core.c
> > +++ b/kernel/sched/core.c
> > @@ -4878,6 +4878,10 @@ static int __sched_setscheduler(struct task_struct *p,
> > return -EINVAL;
> > if (attr->sched_latency_nice < MIN_LATENCY_NICE)
> > return -EINVAL;
> > + /* Use the same security checks as NICE */
> > + if (attr->sched_latency_nice < p->latency_nice &&
> > + !can_nice(p, attr->sched_latency_nice))
> > + return -EPERM;
> > }
> >
> > if (pi)
> >
> > With the above in effect,
> > A non-root user can only increase the value upto +19, and once increased
> > cannot be decreased. e.g., a user once sets the value latency_nice = 19,
> > the same user cannot set the value latency_nice = 18. This is the same
> > effect as with NICE.
> >
> > Is such permission checks required?
> >
> > Unlike NICE, we are going to use latency_nice for scheduler hints only, and
> > so won't it make more sense to allow a user to increase/decrease the values
> > of their owned tasks?
>
> Whether called a hint or not, it is a trade-off to reduce latency of select
> tasks at the expense of the throughput of the other tasks in the the system.
Does it actually affect the throughput of the other tasks? I thought this will
allow the scheduler to reduce latencies, for instance, when selecting which cpu
it should land on. I can't see how this could hurt other tasks.
Can you expand on the scenario you have in mind please?
> If any of the other tasks belong to other users, you would presumably
> require permission.
AFAIU security_task_setscheduler() will only allow a change if the task is
changing its own attribute value or has SYS_CAP_NICE.
If you're able to change the attribute of another task, then its not only
latency_nice that's broken here.
Thanks
--
Qais Yousef
^ permalink raw reply
* [PATCH] Set default CONNECTIVITY_CHECK_URIS to https://www.yoctoproject.org/
From: Nataliya Korovkina @ 2020-02-20 15:03 UTC (permalink / raw)
To: openembedded-core
Signed-off-by: Nataliya Korovkina <malus.brandywine@gmail.com>
---
meta/conf/distro/include/default-distrovars.inc | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/meta/conf/distro/include/default-distrovars.inc b/meta/conf/distro/include/default-distrovars.inc
index 433d4b6651..6c9155b2ae 100644
--- a/meta/conf/distro/include/default-distrovars.inc
+++ b/meta/conf/distro/include/default-distrovars.inc
@@ -48,4 +48,4 @@ KERNEL_IMAGETYPES ??= "${KERNEL_IMAGETYPE}"
# fetch from the network (and warn you if not). To disable the test set
# the variable to be empty.
# Git example url: git://git.yoctoproject.org/yocto-firewall-test;protocol=git;rev=master
-CONNECTIVITY_CHECK_URIS ?= "https://www.example.com/"
+CONNECTIVITY_CHECK_URIS ?= "https://www.yoctoproject.org/"
--
2.17.1
^ permalink raw reply related
* Re: [PATCH] xilinx_spips: Correct the number of dummy cycles for the FAST_READ_4 cmd
From: Peter Maydell @ 2020-02-20 15:02 UTC (permalink / raw)
To: Edgar E. Iglesias
Cc: Sai Pavan Boddu, Francisco Iglesias, Alistair Francis,
QEMU Developers, Edgar Iglesias, Cédric Le Goater,
Alistair Francis
In-Reply-To: <20200216160413.GF22292@toto>
On Wed, 19 Feb 2020 at 01:12, Edgar E. Iglesias
<edgar.iglesias@xilinx.com> wrote:
>
> On Tue, Feb 18, 2020 at 12:33:50PM +0100, Francisco Iglesias wrote:
> > From: Francisco Iglesias <francisco.iglesias@xilinx.com>
> >
> > Correct the number of dummy cycles required by the FAST_READ_4 command (to
> > be eight, one dummy byte).
> >
> > Fixes: ef06ca3946 ("xilinx_spips: Add support for RX discard and RX drain")
> > Suggested-by: Cédric Le Goater <clg@kaod.org>
> > Signed-off-by: Francisco Iglesias <frasse.iglesias@gmail.com>
>
> Reviewed-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com>
Applied to target-arm.next, thanks.
-- PMM
^ permalink raw reply
* Re: [Xen-devel] [PATCH] rwlock: allow recursive read locking when already locked in write mode
From: Jan Beulich @ 2020-02-20 15:02 UTC (permalink / raw)
To: Roger Pau Monné
Cc: Jürgen Groß, Stefano Stabellini, Julien Grall, Wei Liu,
Konrad Rzeszutek Wilk, George Dunlap, Andrew Cooper, Ian Jackson,
xen-devel
In-Reply-To: <20200220141117.GK4679@Air-de-Roger>
On 20.02.2020 15:11, Roger Pau Monné wrote:
> On Thu, Feb 20, 2020 at 01:48:54PM +0100, Jan Beulich wrote:
>> Another option is to use the recurse_cpu field of the
>> associated spin lock: The field is used for recursive locks
>> only, and hence the only conflict would be with
>> _spin_is_locked(), which we don't (and in the future then
>> also shouldn't) use on this lock.
>
> I looked into that also, but things get more complicated AFAICT, as it's
> not possible to atomically fetch the state of the lock and the owner
> CPU at the same time. Neither you could set the LOCKED bit and the CPU
> at the same time.
There's no need to atomically fetch both afaics: The field is
valid only if the LOCKED bit is set. And when reading the
field you only care about the value being equal to
smp_processor_id(), i.e. it is fine to set LOCKED before
updating the CPU field on lock, and to reset the CPU field to
SPINLOCK_NO_CPU (or whatever it's called) before clearing
LOCKED.
Jan
_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xenproject.org
https://lists.xenproject.org/mailman/listinfo/xen-devel
^ permalink raw reply
* Re: [RESEND PATCH v6 6/7] gpiolib: add new ioctl() for monitoring changes in line info
From: Linus Walleij @ 2020-02-20 15:03 UTC (permalink / raw)
To: Bartosz Golaszewski
Cc: Arnd Bergmann, Kent Gibson, Andy Shevchenko,
open list:GPIO SUBSYSTEM, linux-kernel@vger.kernel.org,
Bartosz Golaszewski
In-Reply-To: <CAMRc=MfkbJ=zTvgpaxFC7L7APEhfC7J_PcncGaQ_AQUA9uw2Fw@mail.gmail.com>
On Wed, Feb 12, 2020 at 12:00 PM Bartosz Golaszewski <brgl@bgdev.pl> wrote:
> > On Tue, Feb 11, 2020 at 10:19 AM Bartosz Golaszewski <brgl@bgdev.pl> wrote:
> > > From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
> > A question:
> >
> > Bartosz, since you know about possible impacts on userspace,
> > since this code use the preferred ktime_get_ns() rather than
> > ktime_get_ns_real(), what happens if we just patch the other
> > event timestamp to use ktime_get_ns() instead, so we use the
> > same everywhere?
> >
> > If it's fine I'd like to just toss in a patch for that as well.
> >
>
> Arnd pointed out it would be an incompatible ABI change[1].
Yeah, I was thinking more about this specific answer from Arnd:
> "It is an incompatible ABI change, the question here is whether anyone
> actually cares. If nothing relies on the timestamps being in
> CLOCK_REALTIME domain, then it can be changed, the question
> is just how you want to prove that this is the case."
So the question is if userspace really cares.
What happens with libgpiod or users of it? Are they assuming
the weirdness of CLOCK_REALTIME, or are they simply assuming
something that is monotonic increasing and just lucky that they
didn't run into anything jumping backwards in time even though
they *could*.
I think I'll propose a change and see what people say.
> However - I asked Khouloud who's working on v2 of the line event
> interface to use ktime_get_ns().
That's great!
Yours,
Linus Walleij
^ permalink raw reply
* [PATCH] gpio: Switch timestamps to ktime_get_ns()
From: Linus Walleij @ 2020-02-20 15:02 UTC (permalink / raw)
To: linux-gpio; +Cc: Bartosz Golaszewski, Linus Walleij, Arnd Bergmann
The existing use of ktime_get_real_ns() in the timestamps from
the GPIO events is dubious.
We have had several discussions about this timestamp, and it is
unclear whether userspace has ever taken into account that a
timestamp from ktime_get_real_ns() can actually move backwards
in time relative the previous timetamp, and userspace is more
likely to expect a monotonic counter.
Background:
https://lore.kernel.org/linux-gpio/CAK8P3a1Skvm48sje8FNDPLYqyz9Lf8q0qX1QETWtyZTxuX4k1g@mail.gmail.com/
https://marc.info/?l=linux-gpio&m=151661955709074&w=2
The change is ABI incompatible, but incompatible in a way that
is IMO more likely to fix future bugs rather than break current
userspace. To the best of my knowledge all userspace expects
a monotonic timestamp and users are just lucky that they very
seldom move backwards in time.
Cc: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
drivers/gpio/gpiolib.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 753283486037..5db16f69c13e 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -945,7 +945,7 @@ static irqreturn_t lineevent_irq_thread(int irq, void *p)
* we didn't get the timestamp from lineevent_irq_handler().
*/
if (!le->timestamp)
- ge.timestamp = ktime_get_real_ns();
+ ge.timestamp = ktime_get_ns();
else
ge.timestamp = le->timestamp;
@@ -983,7 +983,7 @@ static irqreturn_t lineevent_irq_handler(int irq, void *p)
* Just store the timestamp in hardirq context so we get it as
* close in time as possible to the actual event.
*/
- le->timestamp = ktime_get_real_ns();
+ le->timestamp = ktime_get_ns();
return IRQ_WAKE_THREAD;
}
--
2.24.1
^ permalink raw reply related
* Re: [PATCH] vsprintf: sanely handle NULL passed to %pe
From: Ilya Dryomov @ 2020-02-20 15:02 UTC (permalink / raw)
To: Petr Mladek
Cc: Rasmus Villemoes, Steven Rostedt, Sergey Senozhatsky,
Andy Shevchenko, Jonathan Corbet, Kees Cook, Tobin C . Harding,
Linus Torvalds, Linux Documentation List, LKML
In-Reply-To: <20200220125707.hbcox3xgevpezq4l@pathway.suse.cz>
On Thu, Feb 20, 2020 at 1:57 PM Petr Mladek <pmladek@suse.com> wrote:
>
> On Wed 2020-02-19 16:40:08, Rasmus Villemoes wrote:
> > On 19/02/2020 15.45, Petr Mladek wrote:
> > > On Wed 2020-02-19 14:56:32, Rasmus Villemoes wrote:
> > >> On 19/02/2020 14.48, Petr Mladek wrote:
> > >>> On Wed 2020-02-19 12:53:22, Rasmus Villemoes wrote:
> > >>>> --- a/lib/vsprintf.c
> > >>>> +++ b/lib/vsprintf.c
> > >>> The test should go into null_pointer() instead of errptr().
> > >>
> > >> Eh, no, the behaviour of %pe is tested by errptr(). I'll keep it that
> > >> way. But I should add a #else section that tests how %pe behaves without
> > >> CONFIG_SYMBOLIC_ERRNAME - though that's orthogonal to this patch.
> > >
> > > OK, we should agree on some structure first.
> > >
> > > We already have two top level functions that test how a particular
> > > pointer is printed using different pointer modifiers:
> > >
> > > null_pointer(); -> NULL with %p, %pX, %pE
> > > invalid_pointer(); -> random pointer with %p, %pX, %pE
> > >
> > > Following this logic, errptr() should test how a pointer from IS_ERR() range
> > > is printed using different pointer formats.
> >
> > Oh please. I wrote test_printf.c originally and structured it with one
> > helper for each %p<whatever>. How are your additions null_pointer and
> > invalid_pointer good examples for what the existing style is?
>
> I see, I was the one who broke the style. Please, find below a patch
> that tries to fix it. If you agree with the approach then I could
> split it into smaller steps.
>
> Also it would make sense to add checks for NULL and ERR pointer
> into each existing %p modifier check. It will make sure that
> check_pointer() is called in all handlers.
>
>
> > So yeah, I'm going to continue testing the behaviour of %pe in errptr, TYVM.
>
> OK.
>
> > >>>> BTW., your original patch for %p lacks corresponding update of
> > >>>> test_vsprintf.c. Please add appropriate test cases.
> > >>>
> > >>> diff --git a/lib/test_printf.c b/lib/test_printf.c
> > >>> index 2d9f520d2f27..1726a678bccd 100644
> > >>> --- a/lib/test_printf.c
> > >>> +++ b/lib/test_printf.c
> > >>> @@ -333,7 +333,7 @@ test_hashed(const char *fmt, const void *p)
> > >>> static void __init
> > >>> null_pointer(void)
> > >>> {
> > >>> - test_hashed("%p", NULL);
> > >>> + test(ZEROS "00000000", "%p", NULL);
> > >>
> > >> No, it most certainly also needs to check a few "%p", ERR_PTR(-4) cases
> > >> (where one of course has to use explicit integers and not E* constants).
> > >
> > > Yes, it would be great to add checks for %p, %px for IS_ERR() range.
> > > But it is different story. The above change is for the original patch
> > > and it was about NULL pointer handling.
> >
> > Wrong. The original patch (i.e. Ilya's) had subject "vsprintf: don't
> > obfuscate NULL and error pointers" and did
> >
> > + if (IS_ERR_OR_NULL(ptr))
> >
> > so the tests that should be part of that patch very much need to cover
> > both NULL and ERR_PTRs passed to plain %p.
>
> Grr, I see. I was too fast yesterday. OK, I suggest to fix the
> structure of the tests first. All these patches are for 5.7
> anyway.
My patch fixes a regression introduced by 3e5903eb9cff ("vsprintf:
Prevent crash when dereferencing invalid pointers" in 5.2, which
made debugging based on existing pr_debugs (used extensively in some
subsystems) very annoying.
I would like to see it in 5.6, so that it is backported to 5.4 and 5.5.
>
>
> Here is the proposed clean up:
>
> From 855909f2a1945d3a5bf490ddf4f2cca775ef967b Mon Sep 17 00:00:00 2001
> From: Petr Mladek <pmladek@suse.com>
> Date: Thu, 20 Feb 2020 12:53:43 +0100
> Subject: [PATCH] lib/test_printf: Clean up basic pointer testing
>
> The pointer testing has been originally split by the %p modifiers,
> for example, the function dentry() tested %pd and %pD handling.
>
> There were recently added tests that do not really fit into
> the existing structure, namely:
>
> + hashed pointers tested by a maze of functions
> + null and invalid pointer handling with various modifiers
>
> The hash pointer test is really special because the hash depends
> on a random key that is generated during boot. Though, it is
> still possible to check some aspects:
>
> + output string length
> + hash differs from the original pointer value
> + top half bites are zeroed on 64-bit systems
>
> Let's put all these checks into test_hashed() function that has
> the same behavior as the test() functions for well-defined output.
> It increments the number of tests and eventual failures. It prints
> warnings/errors when some aspects of the output are not as expected.
>
> Most of these checks were there even before. The only addition is
> the check whether hash differs from the original pointer value.
> There is a small chance of a false error. It might be reduced
> by checking more pointers but let's keep it simple for now.
>
> The existing null_pointer() and invalid_pointer() checks are
> newly split per-format modifier. And there is also fixed
> difference between invalid pointer in the IS_ERR() range
> and invalid pointer that looks like a valid one.
>
> The invalid pointer Oxdeaddead00000000 should work on most
> architectures. But I am not able to check it everywhere.
> So there is a small chance of a false error. It might get
> fixed when anyone reports a problem.
>
> Signed-off-by: Petr Mladek <pmladek@suse.com>
> ---
> lib/test_printf.c | 162 ++++++++++++++++++++----------------------------------
> 1 file changed, 59 insertions(+), 103 deletions(-)
>
> diff --git a/lib/test_printf.c b/lib/test_printf.c
> index 2d9f520d2f27..4e89b508def6 100644
> --- a/lib/test_printf.c
> +++ b/lib/test_printf.c
> @@ -206,146 +206,101 @@ test_string(void)
> }
>
> #define PLAIN_BUF_SIZE 64 /* leave some space so we don't oops */
> +#define PTR_ERROR ERR_PTR(-EFAULT)
> +#define PTR_VAL_ERROR "fffffff2"
>
> #if BITS_PER_LONG == 64
>
> #define PTR_WIDTH 16
> #define PTR ((void *)0xffff0123456789abUL)
> #define PTR_STR "ffff0123456789ab"
> +#define PTR_INVALID ((void *)0xdeaddead000000ab)
> +#define PTR_VAL_INVALID "deaddead000000ab"
> #define PTR_VAL_NO_CRNG "(____ptrval____)"
> +#define ONES "ffffffff" /* hex 32 one bits */
> #define ZEROS "00000000" /* hex 32 zero bits */
>
> -static int __init
> -plain_format(void)
> -{
> - char buf[PLAIN_BUF_SIZE];
> - int nchars;
> -
> - nchars = snprintf(buf, PLAIN_BUF_SIZE, "%p", PTR);
> -
> - if (nchars != PTR_WIDTH)
> - return -1;
> -
> - if (strncmp(buf, PTR_VAL_NO_CRNG, PTR_WIDTH) == 0) {
> - pr_warn("crng possibly not yet initialized. plain 'p' buffer contains \"%s\"",
> - PTR_VAL_NO_CRNG);
> - return 0;
> - }
> -
> - if (strncmp(buf, ZEROS, strlen(ZEROS)) != 0)
> - return -1;
> -
> - return 0;
> -}
> -
> #else
>
> #define PTR_WIDTH 8
> #define PTR ((void *)0x456789ab)
> #define PTR_STR "456789ab"
> +#define PTR_INVALID ((void *)0x000000ab)
> +#define PTR_VAL_INVALID "000000ab"
> #define PTR_VAL_NO_CRNG "(ptrval)"
> +#define ONES ""
> #define ZEROS ""
>
> -static int __init
> -plain_format(void)
> -{
> - /* Format is implicitly tested for 32 bit machines by plain_hash() */
> - return 0;
> -}
> -
> #endif /* BITS_PER_LONG == 64 */
>
> -static int __init
> -plain_hash_to_buffer(const void *p, char *buf, size_t len)
> +static void __init
> +test_hashed(const char *fmt, const void *p)
> {
> + char pointer[PLAIN_BUF_SIZE];
> + char hash[PLAIN_BUF_SIZE];
> int nchars;
>
> - nchars = snprintf(buf, len, "%p", p);
> -
> - if (nchars != PTR_WIDTH)
> - return -1;
> + total_tests++;
>
> - if (strncmp(buf, PTR_VAL_NO_CRNG, PTR_WIDTH) == 0) {
> - pr_warn("crng possibly not yet initialized. plain 'p' buffer contains \"%s\"",
> - PTR_VAL_NO_CRNG);
> - return 0;
> + nchars = snprintf(pointer, sizeof(pointer), "%px", p);
> + if (nchars != PTR_WIDTH) {
> + pr_err("error in test suite: vsprintf(\"%%px\", p) returned number of characters %d, expected %d\n",
> + nchars, PTR_WIDTH);
> + goto err;
> }
>
> - return 0;
> -}
> -
> -static int __init
> -plain_hash(void)
> -{
> - char buf[PLAIN_BUF_SIZE];
> - int ret;
> -
> - ret = plain_hash_to_buffer(PTR, buf, PLAIN_BUF_SIZE);
> - if (ret)
> - return ret;
> -
> - if (strncmp(buf, PTR_STR, PTR_WIDTH) == 0)
> - return -1;
> -
> - return 0;
> -}
> -
> -/*
> - * We can't use test() to test %p because we don't know what output to expect
> - * after an address is hashed.
> - */
> -static void __init
> -plain(void)
> -{
> - int err;
> -
> - err = plain_hash();
> - if (err) {
> - pr_warn("plain 'p' does not appear to be hashed\n");
> - failed_tests++;
> - return;
> + nchars = snprintf(hash, sizeof(hash), fmt, p);
> + if (nchars != PTR_WIDTH) {
> + pr_warn("vsprintf(\"%s\", p) returned number of characters %d, expected %d\n",
> + fmt, nchars, PTR_WIDTH);
> + goto err;
> }
>
> - err = plain_format();
> - if (err) {
> - pr_warn("hashing plain 'p' has unexpected format\n");
> - failed_tests++;
> + if (strncmp(hash, PTR_VAL_NO_CRNG, PTR_WIDTH) == 0) {
> + pr_warn_once("crng possibly not yet initialized. vsprinf(\"%s\", p) printed \"%s\"",
> + fmt, hash);
> + total_tests--;
> + return;
> }
> -}
> -
> -static void __init
> -test_hashed(const char *fmt, const void *p)
> -{
> - char buf[PLAIN_BUF_SIZE];
> - int ret;
>
> /*
> - * No need to increase failed test counter since this is assumed
> - * to be called after plain().
> + * There is a small chance of a false negative on 32-bit systems
> + * when the hash is the same as the pointer value.
> */
> - ret = plain_hash_to_buffer(p, buf, PLAIN_BUF_SIZE);
> - if (ret)
> - return;
> + if (strncmp(hash, pointer, PTR_WIDTH) == 0) {
> + pr_warn("vsprintf(\"%s\", p) returned %s, expected hashed pointer\n",
> + fmt, hash);
> + goto err;
> + }
> +
> +#if BITS_PER_LONG == 64
> + if (strncmp(hash, ZEROS, PTR_WIDTH / 2) != 0) {
> + pr_warn("vsprintf(\"%s\", p) returned %s, expected %s in the top half bits\n",
> + fmt, hash, ZEROS);
> + goto err;
> + }
> +#endif
> + return;
>
> - test(buf, fmt, p);
> +err:
> + failed_tests++;
> }
>
> static void __init
> -null_pointer(void)
> +plain_pointer(void)
> {
> test_hashed("%p", NULL);
> - test(ZEROS "00000000", "%px", NULL);
> - test("(null)", "%pE", NULL);
> + test_hashed("%p", PTR_ERROR);
> + test_hashed("%p", PTR_INVALID);
> }
>
> -#define PTR_INVALID ((void *)0x000000ab)
>
> static void __init
> -invalid_pointer(void)
> +real_pointer(void)
> {
> - test_hashed("%p", PTR_INVALID);
> - test(ZEROS "000000ab", "%px", PTR_INVALID);
> - test("(efault)", "%pE", PTR_INVALID);
> + test(ZEROS "00000000", "%px", NULL);
> + test(ONES PTR_VAL_ERROR, "%px", PTR_ERROR);
> + test(PTR_VAL_INVALID, "%px", PTR_INVALID);
> }
>
> static void __init
> @@ -372,6 +327,8 @@ addr(void)
> static void __init
> escaped_str(void)
> {
> + test("(null)", "%pE", NULL);
> + test("(efault)", "%pE", PTR_ERROR);
> }
>
> static void __init
> @@ -458,9 +415,9 @@ dentry(void)
> test("foo", "%pd2", &test_dentry[0]);
>
> test("(null)", "%pd", NULL);
> - test("(efault)", "%pd", PTR_INVALID);
> + test("(efault)", "%pd", PTR_ERROR);
> test("(null)", "%pD", NULL);
> - test("(efault)", "%pD", PTR_INVALID);
> + test("(efault)", "%pD", PTR_ERROR);
>
> test("romeo", "%pd", &test_dentry[3]);
> test("alfa/romeo", "%pd2", &test_dentry[3]);
> @@ -647,9 +604,8 @@ errptr(void)
> static void __init
> test_pointer(void)
> {
> - plain();
> - null_pointer();
> - invalid_pointer();
> + plain_pointer();
> + real_pointer();
> symbol_ptr();
> kernel_ptr();
> struct_resource();
Please note that I sent v2 of my patch ("[PATCH v2] vsprintf: don't
obfuscate NULL and error pointers"), fixing null_pointer() and adding
error_pointer() test cases, which conflicts with this restructure.
Thanks,
Ilya
^ permalink raw reply
* [PATCH] mm/page_alloc: increase default min_free_kbytes bound
From: Joel Savitz @ 2020-02-20 15:01 UTC (permalink / raw)
To: linux-kernel; +Cc: Joel Savitz, Andrew Morton, Rafael Aquini, linux-mm
Currently, the vm.min_free_kbytes sysctl value is capped at a hardcoded
64M in init_per_zone_wmark_min (unless it is overridden by khugepaged
initialization).
This value has not been modified since 2005, and enterprise-grade
systems now frequently have hundreds of GB of RAM and multiple 10, 40,
or even 100 GB NICs. We have seen page allocation failures on heavily
loaded systems related to NIC drivers. These issues were resolved by an
increase to vm.min_free_kbytes.
This patch increases the hardcoded value by a factor of 4 as a temporary
solution.
Further work to make the calculation of vm.min_free_kbytes more
consistent throughout the kernel would be desirable.
As an example of the inconsistency of the current method, this value is
recalculated by init_per_zone_wmark_min() in the case of memory hotplug
which will override the value set by set_recommended_min_free_kbytes()
called during khugepaged initialization even if khugepaged remains
enabled, however an on/off toggle of khugepaged will then recalculate
and set the value via set_recommended_min_free_kbytes().
Signed-off-by: Joel Savitz <jsavitz@redhat.com>
---
mm/page_alloc.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 3c4eb750a199..32cbfb13e958 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -7867,8 +7867,8 @@ int __meminit init_per_zone_wmark_min(void)
min_free_kbytes = new_min_free_kbytes;
if (min_free_kbytes < 128)
min_free_kbytes = 128;
- if (min_free_kbytes > 65536)
- min_free_kbytes = 65536;
+ if (min_free_kbytes > 262144)
+ min_free_kbytes = 262144;
} else {
pr_warn("min_free_kbytes is not updated to %d because user defined value %d is preferred\n",
new_min_free_kbytes, user_min_free_kbytes);
--
2.23.0
^ permalink raw reply related
* Re: [dpdk-dev] [PATCH] net/mlx5: fix metadata split with encap action
From: Slava Ovsiienko @ 2020-02-20 15:02 UTC (permalink / raw)
To: Matan Azrad, dev@dpdk.org; +Cc: stable@dpdk.org
In-Reply-To: <1582209810-20546-1-git-send-email-matan@mellanox.com>
> -----Original Message-----
> From: Matan Azrad <matan@mellanox.com>
> Sent: Thursday, February 20, 2020 16:44
> To: dev@dpdk.org
> Cc: Slava Ovsiienko <viacheslavo@mellanox.com>; stable@dpdk.org
> Subject: [PATCH] net/mlx5: fix metadata split with encap action
>
> In order to move the mbuf metadata from the WQE to the FDB steering
> domain, the PMD add for each NIC TX flow a new action to copy the metadata
> register REG_A to REG_C_0.
>
> This copy action is considered as modify header action from HW perspective.
>
> The HW doesn't support to do modify header action after ant encapsulation
> action.
>
> The split metadata function wrongly added the copy action in the end of the
> original actions list, hence, NIC egress flow with encapapsulation action failed
> when the PMD worked with dv_xmeta_en mode.
>
> Move the copy action to be before and back to back with the encapsulation
> action for the aforementioned case.
>
> Fixes: 71e254bc0294 ("net/mlx5: split Rx flows to provide metadata copy")
> Cc: stable@dpdk.org
>
> Signed-off-by: Matan Azrad <matan@mellanox.com>
Acked-by: Viacheslav Ovsiienko <viacheslavo@mellanox.com>
PS. Raslan - please, fix typos in commit message, thanks
> ---
> drivers/net/mlx5/mlx5_flow.c | 66 ++++++++++++++++++++++++++++++++++---
> -------
> 1 file changed, 51 insertions(+), 15 deletions(-)
>
> diff --git a/drivers/net/mlx5/mlx5_flow.c b/drivers/net/mlx5/mlx5_flow.c
> index fa58546..60aab16 100644
> --- a/drivers/net/mlx5/mlx5_flow.c
> +++ b/drivers/net/mlx5/mlx5_flow.c
> @@ -2744,7 +2744,7 @@ uint32_t mlx5_flow_adjust_priority(struct
> rte_eth_dev *dev, int32_t priority, }
>
> /**
> - * Get QUEUE/RSS action from the action list.
> + * Get metadata split action information.
> *
> * @param[in] actions
> * Pointer to the list of actions.
> @@ -2753,18 +2753,38 @@ uint32_t mlx5_flow_adjust_priority(struct
> rte_eth_dev *dev, int32_t priority,
> * @param[out] qrss_type
> * Pointer to the action type to return. RTE_FLOW_ACTION_TYPE_END is
> returned
> * if no QUEUE/RSS is found.
> + * @param[out] encap_idx
> + * Pointer to the index of the encap action if exists, otherwise the last
> + * action index.
> *
> * @return
> * Total number of actions.
> */
> static int
> -flow_parse_qrss_action(const struct rte_flow_action actions[],
> - const struct rte_flow_action **qrss)
> +flow_parse_metadata_split_actions_info(const struct rte_flow_action
> actions[],
> + const struct rte_flow_action **qrss,
> + int *encap_idx)
> {
> + const struct rte_flow_action_raw_encap *raw_encap;
> int actions_n = 0;
> + int raw_decap_idx = -1;
>
> + *encap_idx = -1;
> for (; actions->type != RTE_FLOW_ACTION_TYPE_END; actions++) {
> switch (actions->type) {
> + case RTE_FLOW_ACTION_TYPE_VXLAN_ENCAP:
> + case RTE_FLOW_ACTION_TYPE_NVGRE_ENCAP:
> + *encap_idx = actions_n;
> + break;
> + case RTE_FLOW_ACTION_TYPE_RAW_DECAP:
> + raw_decap_idx = actions_n;
> + break;
> + case RTE_FLOW_ACTION_TYPE_RAW_ENCAP:
> + raw_encap = actions->conf;
> + if (raw_encap->size >
> MLX5_ENCAPSULATION_DECISION_SIZE)
> + *encap_idx = raw_decap_idx != -1 ?
> + raw_decap_idx :
> actions_n;
> + break;
> case RTE_FLOW_ACTION_TYPE_QUEUE:
> case RTE_FLOW_ACTION_TYPE_RSS:
> *qrss = actions;
> @@ -2774,6 +2794,8 @@ uint32_t mlx5_flow_adjust_priority(struct
> rte_eth_dev *dev, int32_t priority,
> }
> actions_n++;
> }
> + if (*encap_idx == -1)
> + *encap_idx = actions_n;
> /* Count RTE_FLOW_ACTION_TYPE_END. */
> return actions_n + 1;
> }
> @@ -3739,6 +3761,8 @@ uint32_t mlx5_flow_adjust_priority(struct
> rte_eth_dev *dev, int32_t priority,
> * Number of actions in the list.
> * @param[out] error
> * Perform verbose error reporting if not NULL.
> + * @param[in] encap_idx
> + * The encap action inndex.
> *
> * @return
> * 0 on success, negative value otherwise
> @@ -3747,7 +3771,8 @@ uint32_t mlx5_flow_adjust_priority(struct
> rte_eth_dev *dev, int32_t priority, flow_mreg_tx_copy_prep(struct
> rte_eth_dev *dev,
> struct rte_flow_action *ext_actions,
> const struct rte_flow_action *actions,
> - int actions_n, struct rte_flow_error *error)
> + int actions_n, struct rte_flow_error *error,
> + int encap_idx)
> {
> struct mlx5_flow_action_copy_mreg *cp_mreg =
> (struct mlx5_flow_action_copy_mreg *) @@ -3762,15
> +3787,24 @@ uint32_t mlx5_flow_adjust_priority(struct rte_eth_dev *dev,
> int32_t priority,
> if (ret < 0)
> return ret;
> cp_mreg->src = ret;
> - memcpy(ext_actions, actions,
> - sizeof(*ext_actions) * actions_n);
> - ext_actions[actions_n - 1] = (struct rte_flow_action){
> - .type = MLX5_RTE_FLOW_ACTION_TYPE_COPY_MREG,
> - .conf = cp_mreg,
> - };
> - ext_actions[actions_n] = (struct rte_flow_action){
> - .type = RTE_FLOW_ACTION_TYPE_END,
> - };
> + if (encap_idx != 0)
> + memcpy(ext_actions, actions, sizeof(*ext_actions) *
> encap_idx);
> + if (encap_idx == actions_n - 1) {
> + ext_actions[actions_n - 1] = (struct rte_flow_action){
> + .type = MLX5_RTE_FLOW_ACTION_TYPE_COPY_MREG,
> + .conf = cp_mreg,
> + };
> + ext_actions[actions_n] = (struct rte_flow_action){
> + .type = RTE_FLOW_ACTION_TYPE_END,
> + };
> + } else {
> + ext_actions[encap_idx] = (struct rte_flow_action){
> + .type = MLX5_RTE_FLOW_ACTION_TYPE_COPY_MREG,
> + .conf = cp_mreg,
> + };
> + memcpy(ext_actions + encap_idx + 1, actions + encap_idx,
> + sizeof(*ext_actions) * (actions_n -
> encap_idx));
> + }
> return 0;
> }
>
> @@ -3821,6 +3855,7 @@ uint32_t mlx5_flow_adjust_priority(struct
> rte_eth_dev *dev, int32_t priority,
> int mtr_sfx = 0;
> size_t act_size;
> int actions_n;
> + int encap_idx;
> int ret;
>
> /* Check whether extensive metadata feature is engaged. */ @@ -
> 3830,7 +3865,8 @@ uint32_t mlx5_flow_adjust_priority(struct rte_eth_dev
> *dev, int32_t priority,
> return flow_create_split_inner(dev, flow, NULL, prefix_layers,
> attr, items, actions, external,
> error);
> - actions_n = flow_parse_qrss_action(actions, &qrss);
> + actions_n = flow_parse_metadata_split_actions_info(actions, &qrss,
> + &encap_idx);
> if (qrss) {
> /* Exclude hairpin flows from splitting. */
> if (qrss->type == RTE_FLOW_ACTION_TYPE_QUEUE) { @@ -
> 3905,7 +3941,7 @@ uint32_t mlx5_flow_adjust_priority(struct rte_eth_dev
> *dev, int32_t priority,
> "metadata flow");
> /* Create the action list appended with copy register. */
> ret = flow_mreg_tx_copy_prep(dev, ext_actions, actions,
> - actions_n, error);
> + actions_n, error, encap_idx);
> if (ret < 0)
> goto exit;
> }
> --
> 1.8.3.1
^ permalink raw reply
* Re: [RFC PATCH v3 03/27] qcow2: Process QCOW2_CLUSTER_ZERO_ALLOC clusters in handle_copied()
From: Max Reitz @ 2020-02-20 15:00 UTC (permalink / raw)
To: Alberto Garcia, qemu-devel
Cc: Kevin Wolf, Anton Nefedov, qemu-block,
Vladimir Sementsov-Ogievskiy, Denis V . Lunev
In-Reply-To: <e327f4c1ed2f9626ce018c1fd2b9db437721b30c.1577014346.git.berto@igalia.com>
[-- Attachment #1.1: Type: text/plain, Size: 1897 bytes --]
On 22.12.19 12:36, Alberto Garcia wrote:
> When writing to a qcow2 file there are two functions that take a
> virtual offset and return a host offset, possibly allocating new
> clusters if necessary:
>
> - handle_copied() looks for normal data clusters that are already
> allocated and have a reference count of 1. In those clusters we
> can simply write the data and there is no need to perform any
> copy-on-write.
>
> - handle_alloc() looks for clusters that do need copy-on-write,
> either because they haven't been allocated yet, because their
> reference count is != 1 or because they are ZERO_ALLOC clusters.
>
> The ZERO_ALLOC case is a bit special because those are clusters that
> are already allocated and they could perfectly be dealt with in
> handle_copied() (as long as copy-on-write is performed when required).
>
> In fact, there is extra code specifically for them in handle_alloc()
> that tries to reuse the existing allocation if possible and frees them
> otherwise.
>
> This patch changes the handling of ZERO_ALLOC clusters so the
> semantics of these two functions are now like this:
>
> - handle_copied() looks for clusters that are already allocated and
> which we can overwrite (NORMAL and ZERO_ALLOC clusters with a
> reference count of 1).
>
> - handle_alloc() looks for clusters for which we need a new
> allocation (all other cases).
>
> One importante difference after this change is that clusters found in
> handle_copied() may now require copy-on-write, but this will be anyway
> necessary once we add support for subclusters.
>
> Signed-off-by: Alberto Garcia <berto@igalia.com>
> ---
> block/qcow2-cluster.c | 226 +++++++++++++++++++++++-------------------
> 1 file changed, 126 insertions(+), 100 deletions(-)
Reviewed-by: Max Reitz <mreitz@redhat.com>
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply
* [PATCH v6 04/16] x86/mm: Use helper fault_signal_pending()
From: Peter Xu @ 2020-02-20 15:01 UTC (permalink / raw)
To: linux-mm, linux-kernel
Cc: Peter Xu, Martin Cracauer, Mike Rapoport, Hugh Dickins,
Jerome Glisse, Kirill A . Shutemov, Matthew Wilcox,
Pavel Emelyanov, Brian Geffon, Maya Gokhale, Denis Plotnikov,
Andrea Arcangeli, Johannes Weiner, Dr . David Alan Gilbert,
Linus Torvalds, Mike Kravetz, Marty McFadden, David Hildenbrand,
Bobby Powers, Mel Gorman
Let's move the fatal signal check even earlier so that we can directly
use the new fault_signal_pending() in x86 mm code.
Signed-off-by: Peter Xu <peterx@redhat.com>
---
arch/x86/mm/fault.c | 28 +++++++++++++---------------
1 file changed, 13 insertions(+), 15 deletions(-)
diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c
index fa4ea09593ab..6a00bc8d047f 100644
--- a/arch/x86/mm/fault.c
+++ b/arch/x86/mm/fault.c
@@ -1442,27 +1442,25 @@ void do_user_addr_fault(struct pt_regs *regs,
fault = handle_mm_fault(vma, address, flags);
major |= fault & VM_FAULT_MAJOR;
+ /* Quick path to respond to signals */
+ if (fault_signal_pending(fault, regs)) {
+ if (!user_mode(regs))
+ no_context(regs, hw_error_code, address, SIGBUS,
+ BUS_ADRERR);
+ return;
+ }
+
/*
* If we need to retry the mmap_sem has already been released,
* and if there is a fatal signal pending there is no guarantee
* that we made any progress. Handle this case first.
*/
- if (unlikely(fault & VM_FAULT_RETRY)) {
+ if (unlikely((fault & VM_FAULT_RETRY) &&
+ (flags & FAULT_FLAG_ALLOW_RETRY))) {
/* Retry at most once */
- if (flags & FAULT_FLAG_ALLOW_RETRY) {
- flags &= ~FAULT_FLAG_ALLOW_RETRY;
- flags |= FAULT_FLAG_TRIED;
- if (!fatal_signal_pending(tsk))
- goto retry;
- }
-
- /* User mode? Just return to handle the fatal exception */
- if (flags & FAULT_FLAG_USER)
- return;
-
- /* Not returning to user mode? Handle exceptions or die: */
- no_context(regs, hw_error_code, address, SIGBUS, BUS_ADRERR);
- return;
+ flags &= ~FAULT_FLAG_ALLOW_RETRY;
+ flags |= FAULT_FLAG_TRIED;
+ goto retry;
}
up_read(&mm->mmap_sem);
--
2.24.1
^ permalink raw reply related
* Re: [for-next][PATCH 12/26] Documentation: bootconfig: Add a doc for extended boot config
From: Markus Elfring @ 2020-02-20 15:00 UTC (permalink / raw)
To: Masami Hiramatsu, Steven Rostedt, linux-doc, linux-fsdevel
Cc: kernel-janitors, linux-kernel, Alexey Dobriyan, Andrew Morton,
Arnaldo Carvalho de Melo, Frank Rowand, Greg Kroah-Hartman,
Ingo Molnar, Jiri Olsa, Jonathan Corbet, Linus Torvalds,
Namhyung Kim, Randy Dunlap, Rob Herring, Thomas Gleixner,
Tim Bird, Tom Zanussi
In-Reply-To: <20200220221340.2b66fd2051a5da74775c474b@kernel.org>
>>> +Currently the maximum config size size is 32KB …
>>
>> Would you like to avoid a word duplication here?
>
> Oops, still exist.
Is there a need to separate the number from the following unit?
> Indeed, "node" is not well defined. What about this?
> ---
> Each key consists of words separated by dot, and value also consists of
> values separated by comma. Here, each word and each value is generally
> called a "node".
I have got still understanding difficulties with such an interpretation.
* Do other contributors find an other word also more appropriate for this use case?
* How will the influence evolve for naming these items?
* Is each element just a string (according to specific rules)?
>> Could an other wording be nicer than the abbreviation “a doc for … config”
>> in the commit subject?
>
> OK, I'll try next time.
Will words like “descriptions”and “configuration”be helpful?
Regards,
Markus
^ permalink raw reply
* [PATCH 4/5] iio: adc: ad9467: add support AD9467 ADC
From: Alexandru Ardelean @ 2020-02-20 15:03 UTC (permalink / raw)
To: linux-kernel, linux-iio, devicetree
Cc: robh+dt, jic23, Michael Hennerich, Lars-Peter Clausen,
Alexandru Ardelean
In-Reply-To: <20200220150317.1864-1-alexandru.ardelean@analog.com>
From: Michael Hennerich <michael.hennerich@analog.com>
The AD9467 is a 16-bit, monolithic, IF sampling analog-to-digital converter
(ADC). It is optimized for high performanceover wide bandwidths and ease of
use. The product operates at a 250 MSPS conversion rate and is designed for
wireless receivers, instrumentation, and test equipment that require a high
dynamic range. The ADC requires 1.8 V and 3.3 V power supplies and a low
voltage differential input clock for full performance operation. No
external reference or driver components are required for many applications.
Data outputs are LVDS compatible (ANSI-644 compatible) and include the
means to reduce the overall current needed for short trace distances.
Since the chip can operate at such high sample-rates (much higher than
classical interfaces), it requires that a DMA controller be used to
interface directly to the chip and push data into memory.
Typically, the AXI ADC IP core is used to interface with it.
Link: https://www.analog.com/media/en/technical-documentation/data-sheets/AD9467.pdf
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
---
drivers/iio/adc/Kconfig | 15 ++
drivers/iio/adc/Makefile | 1 +
drivers/iio/adc/ad9467.c | 447 +++++++++++++++++++++++++++++++++++++++
3 files changed, 463 insertions(+)
create mode 100644 drivers/iio/adc/ad9467.c
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 6cd48a256122..229b8bc6f9b6 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -246,6 +246,21 @@ config AD799X
To compile this driver as a module, choose M here: the module will be
called ad799x.
+config AD9467
+ tristate "Analog Devices AD9467 High Speed ADC driver"
+ depends on SPI
+ select AXI_ADC
+ help
+ Say yes here to build support for Analog Devices:
+ * AD9467 16-Bit, 200 MSPS/250 MSPS Analog-to-Digital Converter
+
+ The driver requires the assistance of the AXI ADC IP core to operate,
+ since SPI is used for configuration only, while data has to be
+ streamed into memory via DMA.
+
+ To compile this driver as a module, choose M here: the module will be
+ called ad9467.
+
config ASPEED_ADC
tristate "Aspeed ADC"
depends on ARCH_ASPEED || COMPILE_TEST
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index e14fabd53246..5018220b8ec7 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -26,6 +26,7 @@ obj-$(CONFIG_AD7793) += ad7793.o
obj-$(CONFIG_AD7887) += ad7887.o
obj-$(CONFIG_AD7949) += ad7949.o
obj-$(CONFIG_AD799X) += ad799x.o
+obj-$(CONFIG_AD9467) += ad9467.o
obj-$(CONFIG_ASPEED_ADC) += aspeed_adc.o
obj-$(CONFIG_AT91_ADC) += at91_adc.o
obj-$(CONFIG_AT91_SAMA5D2_ADC) += at91-sama5d2_adc.o
diff --git a/drivers/iio/adc/ad9467.c b/drivers/iio/adc/ad9467.c
new file mode 100644
index 000000000000..f268bbb6bcf6
--- /dev/null
+++ b/drivers/iio/adc/ad9467.c
@@ -0,0 +1,447 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Analog Devices AD9467 SPI ADC driver
+ *
+ * Copyright 2012-2020 Analog Devices Inc.
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#include <linux/clk.h>
+
+#include <linux/iio/adc/axi-adc.h>
+
+/*
+ * ADI High-Speed ADC common spi interface registers
+ * See Application-Note AN-877:
+ * https://www.analog.com/media/en/technical-documentation/application-notes/AN-877.pdf
+ */
+
+#define ADI_ADC_REG_CHIP_PORT_CONF 0x00
+#define ADI_ADC_REG_CHIP_ID 0x01
+#define ADI_ADC_REG_CHIP_GRADE 0x02
+#define ADI_ADC_REG_CHAN_INDEX 0x05
+#define ADI_ADC_REG_TRANSFER 0xFF
+#define ADI_ADC_REG_MODES 0x08
+#define ADI_ADC_REG_TEST_IO 0x0D
+#define ADI_ADC_REG_ADC_INPUT 0x0F
+#define ADI_ADC_REG_OFFSET 0x10
+#define ADI_ADC_REG_OUTPUT_MODE 0x14
+#define ADI_ADC_REG_OUTPUT_ADJUST 0x15
+#define ADI_ADC_REG_OUTPUT_PHASE 0x16
+#define ADI_ADC_REG_OUTPUT_DELAY 0x17
+#define ADI_ADC_REG_VREF 0x18
+#define ADI_ADC_REG_ANALOG_INPUT 0x2C
+
+/* ADI_ADC_REG_TEST_IO */
+#define ADI_ADC_TESTMODE_OFF 0x0
+#define ADI_ADC_TESTMODE_MIDSCALE_SHORT 0x1
+#define ADI_ADC_TESTMODE_POS_FULLSCALE 0x2
+#define ADI_ADC_TESTMODE_NEG_FULLSCALE 0x3
+#define ADI_ADC_TESTMODE_ALT_CHECKERBOARD 0x4
+#define ADI_ADC_TESTMODE_PN23_SEQ 0x5
+#define ADI_ADC_TESTMODE_PN9_SEQ 0x6
+#define ADI_ADC_TESTMODE_ONE_ZERO_TOGGLE 0x7
+#define ADI_ADC_TESTMODE_USER 0x8
+#define ADI_ADC_TESTMODE_BIT_TOGGLE 0x9
+#define ADI_ADC_TESTMODE_SYNC 0xA
+#define ADI_ADC_TESTMODE_ONE_BIT_HIGH 0xB
+#define ADI_ADC_TESTMODE_MIXED_BIT_FREQUENCY 0xC
+#define ADI_ADC_TESTMODE_RAMP 0xF
+
+/* ADI_ADC_REG_TRANSFER */
+#define ADI_ADC_TRANSFER_SYNC 0x1
+
+/* ADI_ADC_REG_OUTPUT_MODE */
+#define ADI_ADC_OUTPUT_MODE_OFFSET_BINARY 0x0
+#define ADI_ADC_OUTPUT_MODE_TWOS_COMPLEMENT 0x1
+#define ADI_ADC_OUTPUT_MODE_GRAY_CODE 0x2
+
+/* ADI_ADC_REG_OUTPUT_PHASE */
+#define ADI_ADC_OUTPUT_EVEN_ODD_MODE_EN 0x20
+#define ADI_ADC_INVERT_DCO_CLK 0x80
+
+/* ADI_ADC_REG_OUTPUT_DELAY */
+#define ADI_ADC_DCO_DELAY_ENABLE 0x80
+
+/*
+ * Analog Devices AD9467 16-Bit, 200/250 MSPS ADC
+ */
+
+#define CHIPID_AD9467 0x50
+#define AD9467_DEF_OUTPUT_MODE 0x08
+#define AD9467_REG_VREF_MASK 0x0F
+
+enum {
+ ID_AD9467,
+};
+
+struct ad9467_state {
+ struct spi_device *spi;
+ struct clk *clk;
+ unsigned int output_mode;
+
+ struct gpio_desc *pwrdown_gpio;
+ struct gpio_desc *reset_gpio;
+};
+
+static int ad9467_spi_read(struct spi_device *spi, unsigned int reg)
+{
+ unsigned char buf[3];
+ int ret;
+
+ buf[0] = 0x80 | (reg >> 8);
+ buf[1] = reg & 0xFF;
+
+ ret = spi_write_then_read(spi, &buf[0], 2, &buf[2], 1);
+
+ if (ret < 0)
+ return ret;
+
+ return buf[2];
+}
+
+static int ad9467_spi_write(struct spi_device *spi, unsigned int reg,
+ unsigned int val)
+{
+ unsigned char buf[3];
+
+ buf[0] = reg >> 8;
+ buf[1] = reg & 0xFF;
+ buf[2] = val;
+
+ return spi_write(spi, buf, ARRAY_SIZE(buf));
+}
+
+static int ad9467_reg_access(struct axi_adc_conv *conv, unsigned int reg,
+ unsigned int writeval, unsigned int *readval)
+{
+ struct ad9467_state *st = axi_adc_conv_priv(conv);
+ struct spi_device *spi = st->spi;
+ int ret;
+
+ if (readval == NULL) {
+ ret = ad9467_spi_write(spi, reg, writeval);
+ ad9467_spi_write(spi, ADI_ADC_REG_TRANSFER,
+ ADI_ADC_TRANSFER_SYNC);
+ return ret;
+ }
+
+ ret = ad9467_spi_read(spi, reg);
+ if (ret < 0)
+ return ret;
+ *readval = ret;
+
+ return 0;
+}
+
+static const unsigned int ad9467_scale_table[][2] = {
+ {2000, 0}, {2100, 6}, {2200, 7},
+ {2300, 8}, {2400, 9}, {2500, 10},
+};
+
+static void __ad9467_get_scale(struct axi_adc_conv *conv, int index,
+ unsigned int *val, unsigned int *val2)
+{
+ const struct axi_adc_chip_info *info = conv->chip_info;
+ const struct iio_chan_spec *chan = &info->channels[0].iio_chan;
+ unsigned int tmp;
+
+ tmp = (info->scale_table[index][0] * 1000000ULL) >>
+ chan->scan_type.realbits;
+ *val = tmp / 1000000;
+ *val2 = tmp % 1000000;
+}
+
+#define AD9467_CHAN(_chan, _si, _bits, _sign) \
+{ \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = _chan, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_CALIBSCALE) | \
+ BIT(IIO_CHAN_INFO_CALIBBIAS) | \
+ BIT(IIO_CHAN_INFO_CALIBPHASE) | \
+ BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .scan_index = _si, \
+ .scan_type = { \
+ .sign = _sign, \
+ .realbits = _bits, \
+ .storagebits = 16, \
+ .shift = 0, \
+ }, \
+}
+
+static const struct axi_adc_chan_spec ad9467_channels[] = {
+ {
+ .iio_chan = AD9467_CHAN(0, 0, 16, 'S'),
+ .num_lanes = 8,
+ },
+};
+
+static const struct axi_adc_chip_info ad9467_chip_info_tbl[] = {
+ [ID_AD9467] = {
+ .id = CHIPID_AD9467,
+ .max_rate = 250000000UL,
+ .scale_table = ad9467_scale_table,
+ .num_scales = ARRAY_SIZE(ad9467_scale_table),
+ .channels = ad9467_channels,
+ .num_channels = ARRAY_SIZE(ad9467_channels),
+ },
+};
+
+static int ad9467_get_scale(struct axi_adc_conv *conv, int *val, int *val2)
+{
+ const struct axi_adc_chip_info *info = conv->chip_info;
+ struct ad9467_state *st = axi_adc_conv_priv(conv);
+ unsigned int i, vref_val, vref_mask;
+
+ vref_val = ad9467_spi_read(st->spi, ADI_ADC_REG_VREF);
+
+ switch (info->id) {
+ case CHIPID_AD9467:
+ vref_mask = AD9467_REG_VREF_MASK;
+ break;
+ default:
+ vref_mask = 0xFFFF;
+ break;
+ }
+
+ vref_val &= vref_mask;
+
+ for (i = 0; i < info->num_scales; i++) {
+ if (vref_val == info->scale_table[i][1])
+ break;
+ }
+
+ if (i == info->num_scales)
+ return -ERANGE;
+
+ __ad9467_get_scale(conv, i, val, val2);
+
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static int ad9467_set_scale(struct axi_adc_conv *conv, int val, int val2)
+{
+ const struct axi_adc_chip_info *info = conv->chip_info;
+ struct ad9467_state *st = axi_adc_conv_priv(conv);
+ unsigned int scale_val[2];
+ unsigned int i;
+
+ if (val != 0)
+ return -EINVAL;
+
+ for (i = 0; i < info->num_scales; i++) {
+ __ad9467_get_scale(conv, i, &scale_val[0], &scale_val[1]);
+ if (scale_val[0] != val || scale_val[1] != val2)
+ continue;
+
+ ad9467_spi_write(st->spi, ADI_ADC_REG_VREF,
+ info->scale_table[i][1]);
+ ad9467_spi_write(st->spi, ADI_ADC_REG_TRANSFER,
+ ADI_ADC_TRANSFER_SYNC);
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int ad9467_read_raw(struct axi_adc_conv *conv,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long m)
+{
+ struct ad9467_state *st = axi_adc_conv_priv(conv);
+
+ switch (m) {
+ case IIO_CHAN_INFO_SCALE:
+ return ad9467_get_scale(conv, val, val2);
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ if (!st->clk)
+ return -ENODEV;
+
+ *val = clk_get_rate(st->clk);
+
+ return IIO_VAL_INT;
+
+ }
+ return -EINVAL;
+}
+
+static int ad9467_write_raw(struct axi_adc_conv *conv,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ const struct axi_adc_chip_info *info = conv->chip_info;
+ struct ad9467_state *st = axi_adc_conv_priv(conv);
+ unsigned long r_clk;
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ return ad9467_set_scale(conv, val, val2);
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ if (!st->clk)
+ return -ENODEV;
+
+ if (chan->extend_name)
+ return -ENODEV;
+
+ r_clk = clk_round_rate(st->clk, val);
+ if (r_clk < 0 || r_clk > info->max_rate) {
+ dev_warn(&st->spi->dev,
+ "Error setting ADC sample rate %ld", r_clk);
+ return -EINVAL;
+ }
+
+ ret = clk_set_rate(st->clk, r_clk);
+ if (ret < 0)
+ return ret;
+
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ad9467_outputmode_set(struct spi_device *spi, unsigned int mode)
+{
+ int ret;
+
+ ret = ad9467_spi_write(spi, ADI_ADC_REG_OUTPUT_MODE, mode);
+ if (ret < 0)
+ return ret;
+
+ return ad9467_spi_write(spi, ADI_ADC_REG_TRANSFER,
+ ADI_ADC_TRANSFER_SYNC);
+}
+
+static int ad9467_preenable_setup(struct axi_adc_conv *conv)
+{
+ struct ad9467_state *st = axi_adc_conv_priv(conv);
+
+ return ad9467_outputmode_set(st->spi, st->output_mode);
+}
+
+static int ad9467_setup(struct ad9467_state *st, unsigned int chip_id)
+{
+ switch (chip_id) {
+ case CHIPID_AD9467:
+ st->output_mode = AD9467_DEF_OUTPUT_MODE |
+ ADI_ADC_OUTPUT_MODE_TWOS_COMPLEMENT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void ad9467_clk_disable(void *data)
+{
+ struct ad9467_state *st = data;
+
+ clk_disable_unprepare(st->clk);
+}
+
+static int ad9467_probe(struct spi_device *spi)
+{
+ struct axi_adc_conv *conv;
+ struct ad9467_state *st;
+ unsigned int id;
+ int ret;
+
+ conv = devm_axi_adc_conv_register(&spi->dev, sizeof(*st));
+
+ if (IS_ERR(conv))
+ return PTR_ERR(conv);
+
+ st = axi_adc_conv_priv(conv);
+ st->spi = spi;
+
+ st->clk = devm_clk_get(&spi->dev, "sample-clock");
+ if (IS_ERR(st->clk))
+ return PTR_ERR(st->clk);
+
+ ret = clk_prepare_enable(st->clk);
+ if (ret < 0)
+ return ret;
+
+ ret = devm_add_action_or_reset(&spi->dev, ad9467_clk_disable, st);
+ if (ret)
+ return ret;
+
+ st->pwrdown_gpio = devm_gpiod_get_optional(&spi->dev, "powerdown",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(st->pwrdown_gpio))
+ return PTR_ERR(st->pwrdown_gpio);
+
+ st->reset_gpio = devm_gpiod_get_optional(&spi->dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(st->reset_gpio))
+ return PTR_ERR(st->reset_gpio);
+
+ if (st->reset_gpio) {
+ udelay(1);
+ ret = gpiod_direction_output(st->reset_gpio, 1);
+ mdelay(10);
+ }
+
+ spi_set_drvdata(spi, st);
+
+ id = spi_get_device_id(spi)->driver_data;
+ conv->chip_info = &ad9467_chip_info_tbl[id];
+
+ id = ad9467_spi_read(spi, ADI_ADC_REG_CHIP_ID);
+ if (id != conv->chip_info->id) {
+ dev_err(&spi->dev, "Unrecognized CHIP_ID 0x%X\n", id);
+ return -ENODEV;
+ }
+
+ conv->reg_access = ad9467_reg_access;
+ conv->write_raw = ad9467_write_raw;
+ conv->read_raw = ad9467_read_raw;
+ conv->preenable_setup = ad9467_preenable_setup;
+
+ return ad9467_setup(st, id);
+}
+
+static const struct spi_device_id ad9467_id[] = {
+ { "ad9467", ID_AD9467 },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad9467_id);
+
+static const struct of_device_id ad9467_of_match[] = {
+ { .compatible = "adi,ad9467" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, ad9467_of_match);
+
+static struct spi_driver ad9467_driver = {
+ .driver = {
+ .name = "ad9467",
+ .of_match_table = ad9467_of_match,
+ },
+ .probe = ad9467_probe,
+ .id_table = ad9467_id,
+};
+module_spi_driver(ad9467_driver);
+
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD9467 ADC driver");
+MODULE_LICENSE("GPL v2");
--
2.20.1
^ permalink raw reply related
* [PATCH 2/5] iio: adc: axi-adc: add support for AXI ADC IP core
From: Alexandru Ardelean @ 2020-02-20 15:03 UTC (permalink / raw)
To: linux-kernel, linux-iio, devicetree
Cc: robh+dt, jic23, Michael Hennerich, Lars-Peter Clausen,
Alexandru Ardelean
In-Reply-To: <20200220150317.1864-1-alexandru.ardelean@analog.com>
From: Michael Hennerich <michael.hennerich@analog.com>
This change adds support for the Analog Devices Generic AXI ADC IP core.
The IP core is used for interfacing with analog-to-digital (ADC) converters
that require either a high-speed serial interface (JESD204B/C) or a source
synchronous parallel interface (LVDS/CMOS).
Usually, some other interface type (i.e SPI) is used as a control interface
for the actual ADC, while the IP core (controlled via this driver), will
interface to the data-lines of the ADC and handle the streaming of data
into memory via DMA.
Because of this, the AXI ADC driver needs the other SPI-ADC driver to
register with it. The SPI-ADC needs to be register via the SPI framework,
while the AXI ADC registers as a platform driver. The two cannot be ordered
in a hierarchy as both drivers have their own registers, and trying to
organize this [in a hierarchy becomes] problematic when trying to map
memory/registers.
There are some modes where the AXI ADC can operate as standalone ADC, but
those will be implemented at a later point in time.
Link: https://wiki.analog.com/resources/fpga/docs/axi_adc_ip
Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
---
drivers/iio/adc/Kconfig | 20 +
drivers/iio/adc/Makefile | 1 +
drivers/iio/adc/axi-adc.c | 622 ++++++++++++++++++++++++++++++++
include/linux/iio/adc/axi-adc.h | 79 ++++
4 files changed, 722 insertions(+)
create mode 100644 drivers/iio/adc/axi-adc.c
create mode 100644 include/linux/iio/adc/axi-adc.h
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index f4da821c4022..6cd48a256122 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -282,6 +282,26 @@ config AT91_SAMA5D2_ADC
To compile this driver as a module, choose M here: the module will be
called at91-sama5d2_adc.
+config AXI_ADC
+ tristate "Analog Devices Generic AXI ADC IP core driver"
+ select IIO_BUFFER
+ select IIO_BUFFER_HW_CONSUMER
+ select IIO_BUFFER_DMAENGINE
+ help
+ Say yes here to build support for Analog Devices Generic
+ AXI ADC IP core. The IP core is used for interfacing with
+ analog-to-digital (ADC) converters that require either a high-speed
+ serial interface (JESD204B/C) or a source synchronous parallel
+ interface (LVDS/CMOS).
+ Typically (for such devices) SPI will be used for configuration only,
+ while this IP core handles the streaming of data into memory via DMA.
+
+ Link: https://wiki.analog.com/resources/fpga/docs/axi_adc_ip
+ If unsure, say N (but it's safe to say "Y").
+
+ To compile this driver as a module, choose M here: the
+ module will be called axi-adc.
+
config AXP20X_ADC
tristate "X-Powers AXP20X and AXP22X ADC driver"
depends on MFD_AXP20X
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 8462455b4228..e14fabd53246 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_AD799X) += ad799x.o
obj-$(CONFIG_ASPEED_ADC) += aspeed_adc.o
obj-$(CONFIG_AT91_ADC) += at91_adc.o
obj-$(CONFIG_AT91_SAMA5D2_ADC) += at91-sama5d2_adc.o
+obj-$(CONFIG_AXI_ADC) += axi-adc.o
obj-$(CONFIG_AXP20X_ADC) += axp20x_adc.o
obj-$(CONFIG_AXP288_ADC) += axp288_adc.o
obj-$(CONFIG_BCM_IPROC_ADC) += bcm_iproc_adc.o
diff --git a/drivers/iio/adc/axi-adc.c b/drivers/iio/adc/axi-adc.c
new file mode 100644
index 000000000000..9ddd64fdab2d
--- /dev/null
+++ b/drivers/iio/adc/axi-adc.c
@@ -0,0 +1,622 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Analog Devices Generic AXI ADC IP core
+ * Link: https://wiki.analog.com/resources/fpga/docs/axi_adc_ip
+ *
+ * Copyright 2012-2020 Analog Devices Inc.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/buffer-dmaengine.h>
+
+#include <linux/fpga/adi-axi-common.h>
+#include <linux/iio/adc/axi-adc.h>
+
+/**
+ * Register definitions:
+ * https://wiki.analog.com/resources/fpga/docs/axi_adc_ip#register_map
+ */
+
+#define AXI_ADC_UPPER16_MSK GENMASK(31, 16)
+#define AXI_ADC_UPPER16_SET(x) FIELD_PREP(AXI_ADC_UPPER16_MSK, x)
+#define AXI_ADC_UPPER16_GET(x) FIELD_GET(AXI_ADC_UPPER16_MSK, x)
+
+#define AXI_ADC_LOWER16_MSK GENMASK(15, 0)
+#define AXI_ADC_LOWER16_SET(x) FIELD_PREP(AXI_ADC_UPPER16_MSK, x)
+#define AXI_ADC_LOWER16_GET(x) FIELD_GET(AXI_ADC_LOWER16_MSK, x)
+
+/* ADC controls */
+
+#define AXI_ADC_REG_RSTN 0x0040
+#define AXI_ADC_MMCM_RSTN BIT(1)
+#define AXI_ADC_RSTN BIT(0)
+
+#define AXI_ADC_REG_CNTRL 0x0044
+#define AXI_ADC_R1_MODE BIT(2)
+#define AXI_ADC_DDR_EDGESEL BIT(1)
+#define AXI_ADC_PIN_MODE BIT(0)
+
+#define AXI_ADC_REG_CLK_FREQ 0x0054
+#define AXI_ADC_REG_CLK_RATIO 0x0058
+
+#define AXI_ADC_REG_STATUS 0x005C
+#define AXI_ADC_MUX_PN_ERR BIT(3)
+#define AXI_ADC_MUX_PN_OOS BIT(2)
+#define AXI_ADC_MUX_OVER_RANGE BIT(1)
+#define AXI_ADC_STATUS BIT(0)
+
+#define AXI_ADC_REG_DRP_CNTRL 0x0070
+#define AXI_ADC_DRP_SEL BIT(29)
+#define AXI_ADC_DRP_RWN BIT(28)
+#define AXI_ADC_DRP_ADDRESS_MSK GENMASK(27, 16)
+#define AXI_ADC_DRP_ADDRESS_SET(x) \
+ FIELD_PREP(AXI_ADC_DRP_ADDRESS_MSK, x)
+#define AXI_ADC_DRP_ADDRESS_GET(x) \
+ FIELD_GET(AXI_ADC_DRP_ADDRESS_MSK, x)
+#define AXI_ADC_DRP_WDATA_SET AXI_ADC_LOWER16_SET
+#define AXI_ADC_DRP_WDATA_GET AXI_ADC_LOWER16_GET
+
+#define AXI_REG_DRP_STATUS 0x0074
+#define AXI_ADC_DRP_STATUS BIT(16)
+#define AXI_ADC_DRP_RDATA_SET AXI_ADC_LOWER16_SET
+#define AXI_ADC_DRP_RDATA_GET AXI_ADC_LOWER16_GET
+
+#define AXI_ADC_REG_DMA_STATUS 0x0088
+#define AXI_ADC_DMA_OVF BIT(2)
+#define AXI_ADC_DMA_UNF BIT(1)
+#define AXI_ADC_DMA_STATUS BIT(0)
+
+#define ADI_REG_DMA_BUSWIDTH 0x008C
+#define AXI_ADC_REG_GP_CONTROL 0x00BC
+#define AXI_ADC_REG_ADC_DP_DISABLE 0x00C0
+
+/* ADC Channel controls */
+
+#define AXI_ADC_REG_CHAN_CNTRL(c) (0x0400 + (c) * 0x40)
+#define AXI_ADC_PN_SEL BIT(10)
+#define AXI_ADC_IQCOR_ENB BIT(9)
+#define AXI_ADC_DCFILT_ENB BIT(8)
+#define AXI_ADC_FORMAT_SIGNEXT BIT(6)
+#define AXI_ADC_FORMAT_TYPE BIT(5)
+#define AXI_ADC_FORMAT_ENABLE BIT(4)
+#define AXI_ADC_PN23_TYPE BIT(1)
+#define AXI_ADC_ENABLE BIT(0)
+
+#define AXI_ADC_REG_CHAN_STATUS(c) (0x0404 + (c) * 0x40)
+#define AXI_ADC_PN_ERR BIT(2)
+#define AXI_ADC_PN_OOS BIT(1)
+#define AXI_ADC_OVER_RANGE BIT(0)
+
+#define AXI_ADC_REG_CHAN_CNTRL_1(c) (0x0410 + (c) * 0x40)
+#define AXI_ADC_DCFILT_OFFSET_MSK AXI_ADC_UPPER16_MSK
+#define AXI_ADC_DCFILT_OFFSET_SET AXI_ADC_UPPER16_SET
+#define AXI_ADC_DCFILT_OFFSET_GET AXI_ADC_UPPER16_GET
+#define AXI_ADC_DCFILT_COEFF_MSK AXI_ADC_LOWER16_MSK
+#define AXI_ADC_DCFILT_COEFF_SET AXI_ADC_LOWER16_SET
+#define AXI_ADC_DCFILT_COEFF_GET AXI_ADC_LOWER16_GET
+
+#define AXI_ADC_REG_CHAN_CNTRL_2(c) (0x0414 + (c) * 0x40)
+#define AXI_ADC_IQCOR_COEFF_1_MSK AXI_ADC_UPPER16_MSK
+#define AXI_ADC_IQCOR_COEFF_1_SET AXI_ADC_UPPER16_SET
+#define AXI_ADC_IQCOR_COEFF_1_GET AXI_ADC_UPPER16_GET
+#define AXI_ADC_IQCOR_COEFF_2_MSK AXI_ADC_LOWER16_MSK
+#define AXI_ADC_IQCOR_COEFF_2_SET AXI_ADC_LOWER16_SET
+#define AXI_ADC_IQCOR_COEFF_2_GET AXI_ADC_LOWER16_GET
+
+/* format is 1.1.14 (sign, integer and fractional bits) */
+#define AXI_ADC_IQCOR_INT_1 0x4000UL
+#define AXI_ADC_IQCOR_SIGN_BIT BIT(15)
+/* The constant below is (2 * PI * 0x4000), where 0x4000 is AXI_ADC_IQCOR_INT_1 */
+#define AXI_ADC_2_X_PI_X_INT_1 102944ULL
+
+#define AXI_ADC_REG_CHAN_CNTRL_3(c) (0x0418 + (c) * 0x40)
+#define AXI_ADC_ADC_PN_SEL_MSK AXI_ADC_UPPER16_MSK
+#define AXI_ADC_ADC_PN_SEL_SET AXI_ADC_UPPER16_SET
+#define AXI_ADC_ADC_PN_SEL_GET AXI_ADC_UPPER16_GET
+#define AXI_ADC_ADC_DATA_SEL_MSK AXI_ADC_LOWER16_MSK
+#define AXI_ADC_ADC_DATA_SEL_SET AXI_ADC_LOWER16_SET
+#define AXI_ADC_ADC_DATA_SEL_GET AXI_ADC_LOWER16_GET
+
+#define AXI_ADC_REG_CHAN_USR_CNTRL_2(c) (0x0424 + (c) * 0x40)
+#define AXI_ADC_USR_DECIMATION_M_MSK AXI_ADC_UPPER16_MSK
+#define AXI_ADC_USR_DECIMATION_M_SET AXI_ADC_UPPER16_SET
+#define AXI_ADC_USR_DECIMATION_M_GET AXI_ADC_UPPER16_GET
+#define AXI_ADC_USR_DECIMATION_N_MSK AXI_ADC_LOWER16_MSK
+#define AXI_ADC_USR_DECIMATION_N_SET AXI_ADC_LOWER16_SET
+#define AXI_ADC_USR_DECIMATION_N_GET AXI_ADC_LOWER16_GET
+
+/* debugfs direct register access */
+#define DEBUGFS_DRA_PCORE_REG_MAGIC BIT(31)
+
+struct axi_adc_core_info {
+ unsigned int version;
+};
+
+struct axi_adc_state {
+ struct mutex lock;
+
+ struct axi_adc_client *client;
+ void __iomem *regs;
+ unsigned int regs_size;
+};
+
+struct axi_adc_client {
+ struct list_head entry;
+ struct axi_adc_conv conv;
+ struct axi_adc_state *state;
+ struct device *dev;
+ const struct axi_adc_core_info *info;
+};
+
+static LIST_HEAD(axi_adc_registered_clients);
+static DEFINE_MUTEX(axi_adc_registered_clients_lock);
+
+static struct axi_adc_client *axi_adc_conv_to_client(struct axi_adc_conv *conv)
+{
+ if (!conv)
+ return NULL;
+ return container_of(conv, struct axi_adc_client, conv);
+}
+
+void *axi_adc_conv_priv(struct axi_adc_conv *conv)
+{
+ struct axi_adc_client *cl = axi_adc_conv_to_client(conv);
+
+ if (!cl)
+ return NULL;
+
+ return (char *)cl + ALIGN(sizeof(struct axi_adc_client), IIO_ALIGN);
+}
+EXPORT_SYMBOL_GPL(axi_adc_conv_priv);
+
+static void axi_adc_write(struct axi_adc_state *st, unsigned int reg,
+ unsigned int val)
+{
+ iowrite32(val, st->regs + reg);
+}
+
+static unsigned int axi_adc_read(struct axi_adc_state *st, unsigned int reg)
+{
+ return ioread32(st->regs + reg);
+}
+
+static int axi_adc_config_dma_buffer(struct device *dev,
+ struct iio_dev *indio_dev)
+{
+ struct iio_buffer *buffer;
+ const char *dma_name;
+
+ if (!device_property_present(dev, "dmas"))
+ return 0;
+
+ if (device_property_read_string(dev, "dma-names", &dma_name))
+ dma_name = "rx";
+
+ buffer = devm_iio_dmaengine_buffer_alloc(indio_dev->dev.parent,
+ dma_name);
+ if (IS_ERR(buffer))
+ return PTR_ERR(buffer);
+
+ indio_dev->modes |= INDIO_BUFFER_HARDWARE;
+ iio_device_attach_buffer(indio_dev, buffer);
+
+ return 0;
+}
+
+static int axi_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct axi_adc_state *st = iio_priv(indio_dev);
+ struct axi_adc_conv *conv = &st->client->conv;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ /* fall-through */
+ default:
+ if (!conv->read_raw)
+ return -ENOSYS;
+
+ return conv->read_raw(conv, chan, val, val2, mask);
+ }
+
+ return -EINVAL;
+}
+
+static int axi_adc_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct axi_adc_state *st = iio_priv(indio_dev);
+ struct axi_adc_conv *conv = &st->client->conv;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ /* fall-through */
+ default:
+ if (!conv->write_raw)
+ return -ENOSYS;
+
+ return conv->write_raw(conv, chan, val, val2, mask);
+ }
+}
+
+static int axi_adc_update_scan_mode(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct axi_adc_state *st = iio_priv(indio_dev);
+ struct axi_adc_conv *conv = &st->client->conv;
+ unsigned int i, ctrl;
+
+ for (i = 0; i < conv->chip_info->num_channels; i++) {
+ ctrl = axi_adc_read(st, AXI_ADC_REG_CHAN_CNTRL(i));
+
+ if (test_bit(i, scan_mask))
+ ctrl |= AXI_ADC_ENABLE;
+ else
+ ctrl &= ~AXI_ADC_ENABLE;
+
+ axi_adc_write(st, AXI_ADC_REG_CHAN_CNTRL(i), ctrl);
+ }
+
+ return 0;
+}
+
+struct axi_adc_conv *axi_adc_conv_register(struct device *dev, int sizeof_priv)
+{
+ struct axi_adc_client *cl;
+ size_t alloc_size;
+
+ alloc_size = sizeof(struct axi_adc_client);
+ if (sizeof_priv) {
+ alloc_size = ALIGN(alloc_size, IIO_ALIGN);
+ alloc_size += sizeof_priv;
+ }
+ alloc_size += IIO_ALIGN - 1;
+
+ cl = kzalloc(alloc_size, GFP_KERNEL);
+ if (!cl)
+ return ERR_PTR(-ENOMEM);
+
+ mutex_lock(&axi_adc_registered_clients_lock);
+
+ get_device(dev);
+ cl->dev = dev;
+
+ list_add_tail(&cl->entry, &axi_adc_registered_clients);
+
+ mutex_unlock(&axi_adc_registered_clients_lock);
+
+ return &cl->conv;
+}
+EXPORT_SYMBOL_GPL(axi_adc_conv_register);
+
+void axi_adc_conv_unregister(struct axi_adc_conv *conv)
+{
+ struct axi_adc_client *cl = axi_adc_conv_to_client(conv);
+
+ if (!cl)
+ return;
+
+ mutex_lock(&axi_adc_registered_clients_lock);
+
+ put_device(cl->dev);
+ list_del(&cl->entry);
+ kfree(cl);
+
+ mutex_unlock(&axi_adc_registered_clients_lock);
+}
+EXPORT_SYMBOL(axi_adc_conv_unregister);
+
+static void devm_axi_adc_conv_release(struct device *dev, void *res)
+{
+ axi_adc_conv_unregister(*(struct axi_adc_conv **)res);
+}
+
+static int devm_axi_adc_conv_match(struct device *dev, void *res, void *data)
+{
+ struct axi_adc_conv **r = res;
+
+ return *r == data;
+}
+
+struct axi_adc_conv *devm_axi_adc_conv_register(struct device *dev,
+ int sizeof_priv)
+{
+ struct axi_adc_conv **ptr, *conv;
+
+ ptr = devres_alloc(devm_axi_adc_conv_release, sizeof(*ptr),
+ GFP_KERNEL);
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);
+
+ conv = axi_adc_conv_register(dev, sizeof_priv);
+ if (IS_ERR(conv)) {
+ devres_free(ptr);
+ return ERR_CAST(conv);
+ }
+
+ *ptr = conv;
+ devres_add(dev, ptr);
+
+ return conv;
+}
+EXPORT_SYMBOL_GPL(devm_axi_adc_conv_register);
+
+void devm_axi_adc_conv_unregister(struct device *dev,
+ struct axi_adc_conv *conv)
+{
+ int rc;
+
+ rc = devres_release(dev, devm_axi_adc_conv_release,
+ devm_axi_adc_conv_match, conv);
+ WARN_ON(rc);
+}
+EXPORT_SYMBOL_GPL(devm_axi_adc_conv_unregister);
+
+static ssize_t in_voltage_scale_available_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct axi_adc_state *st = iio_priv(indio_dev);
+ struct axi_adc_conv *conv = &st->client->conv;
+ size_t len = 0;
+ int i;
+
+ if (!conv->chip_info->num_scales) {
+ buf[0] = '\n';
+ return 1;
+ }
+
+ for (i = 0; i < conv->chip_info->num_scales; i++) {
+ const unsigned int *s = conv->chip_info->scale_table[i];
+
+ len += scnprintf(buf + len, PAGE_SIZE - len,
+ "%u.%06u ", s[0], s[1]);
+ }
+ buf[len - 1] = '\n';
+
+ return len;
+}
+
+static IIO_DEVICE_ATTR_RO(in_voltage_scale_available, 0);
+
+static struct attribute *axi_adc_attributes[] = {
+ &iio_dev_attr_in_voltage_scale_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group axi_adc_attribute_group = {
+ .attrs = axi_adc_attributes,
+};
+
+static const struct iio_info axi_adc_info = {
+ .read_raw = &axi_adc_read_raw,
+ .write_raw = &axi_adc_write_raw,
+ .attrs = &axi_adc_attribute_group,
+ .update_scan_mode = &axi_adc_update_scan_mode,
+};
+
+static const struct axi_adc_core_info axi_adc_10_0_a_info = {
+ .version = ADI_AXI_PCORE_VER(10, 0, 'a'),
+};
+
+/* Match table for of_platform binding */
+static const struct of_device_id axi_adc_of_match[] = {
+ { .compatible = "adi,axi-adc-10.0.a", .data = &axi_adc_10_0_a_info },
+ { /* end of list */ },
+};
+MODULE_DEVICE_TABLE(of, axi_adc_of_match);
+
+struct axi_adc_client *axi_adc_attach_client(struct device *dev)
+{
+ const struct of_device_id *id;
+ struct axi_adc_client *cl;
+ struct device_node *cln;
+
+ if (!dev->of_node) {
+ dev_err(dev, "DT node is null\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ id = of_match_node(axi_adc_of_match, dev->of_node);
+ if (!id)
+ return ERR_PTR(-ENODEV);
+
+ cln = of_parse_phandle(dev->of_node, "axi-adc-client", 0);
+ if (!cln) {
+ dev_err(dev, "No 'axi-adc-client' node defined\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ mutex_lock(&axi_adc_registered_clients_lock);
+
+ list_for_each_entry(cl, &axi_adc_registered_clients, entry) {
+ if (!cl->dev)
+ continue;
+ if (cl->dev->of_node == cln) {
+ if (!try_module_get(dev->driver->owner)) {
+ mutex_unlock(&axi_adc_registered_clients_lock);
+ return ERR_PTR(-ENODEV);
+ }
+ get_device(dev);
+ cl->info = id->data;
+ mutex_unlock(&axi_adc_registered_clients_lock);
+ return cl;
+ }
+ }
+
+ mutex_unlock(&axi_adc_registered_clients_lock);
+
+ return ERR_PTR(-EPROBE_DEFER);
+}
+
+static int axi_adc_setup_channels(struct device *dev, struct axi_adc_state *st)
+{
+ struct axi_adc_conv *conv = conv = &st->client->conv;
+ unsigned int val;
+ int i, ret;
+
+ if (conv->preenable_setup) {
+ ret = conv->preenable_setup(conv);
+ if (ret)
+ return ret;
+ }
+
+ for (i = 0; i < conv->chip_info->num_channels; i++) {
+ if (i & 1)
+ val = AXI_ADC_IQCOR_COEFF_2_SET(AXI_ADC_IQCOR_INT_1);
+ else
+ val = AXI_ADC_IQCOR_COEFF_1_SET(AXI_ADC_IQCOR_INT_1);
+ axi_adc_write(st, AXI_ADC_REG_CHAN_CNTRL_2(i), val);
+
+ axi_adc_write(st, AXI_ADC_REG_CHAN_CNTRL(i),
+ AXI_ADC_FORMAT_SIGNEXT | AXI_ADC_FORMAT_ENABLE |
+ AXI_ADC_IQCOR_ENB | AXI_ADC_ENABLE);
+ }
+
+ return 0;
+}
+
+static int axi_adc_alloc_channels(struct iio_dev *indio_dev,
+ struct axi_adc_conv *conv)
+{
+ unsigned int i, num = conv->chip_info->num_channels;
+ struct device *dev = indio_dev->dev.parent;
+ struct iio_chan_spec *channels;
+
+ channels = devm_kcalloc(dev, num, sizeof(*channels), GFP_KERNEL);
+ if (!channels)
+ return -ENOMEM;
+
+ for (i = 0; i < conv->chip_info->num_channels; i++)
+ channels[i] = conv->chip_info->channels->iio_chan;
+
+ indio_dev->num_channels = num;
+ indio_dev->channels = channels;
+
+ return 0;
+}
+
+struct axi_adc_cleanup_data {
+ struct axi_adc_state *st;
+ struct axi_adc_client *cl;
+};
+
+static void axi_adc_cleanup(void *data)
+{
+ struct axi_adc_client *cl = data;
+
+ put_device(cl->dev);
+ module_put(cl->dev->driver->owner);
+}
+
+static int axi_adc_probe(struct platform_device *pdev)
+{
+ struct axi_adc_conv *conv;
+ struct iio_dev *indio_dev;
+ struct axi_adc_client *cl;
+ struct axi_adc_state *st;
+ struct resource *mem;
+ unsigned int ver;
+ int ret;
+
+ cl = axi_adc_attach_client(&pdev->dev);
+ if (IS_ERR(cl))
+ return PTR_ERR(cl);
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*st));
+ if (indio_dev == NULL)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ st->client = cl;
+ cl->state = st;
+ mutex_init(&st->lock);
+
+ ret = devm_add_action_or_reset(&pdev->dev, axi_adc_cleanup, cl);
+ if (ret)
+ return ret;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ st->regs_size = resource_size(mem);
+ st->regs = devm_ioremap_resource(&pdev->dev, mem);
+ if (IS_ERR(st->regs))
+ return PTR_ERR(st->regs);
+
+ conv = &st->client->conv;
+
+ /* Reset HDL Core */
+ axi_adc_write(st, AXI_ADC_REG_RSTN, 0);
+ mdelay(10);
+ axi_adc_write(st, AXI_ADC_REG_RSTN, AXI_ADC_MMCM_RSTN);
+ mdelay(10);
+ axi_adc_write(st, AXI_ADC_REG_RSTN, AXI_ADC_RSTN | AXI_ADC_MMCM_RSTN);
+
+ ver = axi_adc_read(st, ADI_AXI_REG_VERSION);
+
+ if (cl->info->version > ver) {
+ dev_err(&pdev->dev,
+ "IP core version is too old. Expected %d.%.2d.%c, Reported %d.%.2d.%c\n",
+ ADI_AXI_PCORE_VER_MAJOR(cl->info->version),
+ ADI_AXI_PCORE_VER_MINOR(cl->info->version),
+ ADI_AXI_PCORE_VER_PATCH(cl->info->version),
+ ADI_AXI_PCORE_VER_MAJOR(ver),
+ ADI_AXI_PCORE_VER_MINOR(ver),
+ ADI_AXI_PCORE_VER_PATCH(ver));
+ return -ENODEV;
+ }
+
+ indio_dev->info = &axi_adc_info;
+ indio_dev->dev.parent = &pdev->dev;
+ indio_dev->name = pdev->dev.of_node->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = axi_adc_alloc_channels(indio_dev, conv);
+ if (ret)
+ return ret;
+
+ ret = axi_adc_config_dma_buffer(&pdev->dev, indio_dev);
+ if (ret)
+ return ret;
+
+ ret = axi_adc_setup_channels(&pdev->dev, st);
+ if (ret)
+ return ret;
+
+ ret = devm_iio_device_register(&pdev->dev, indio_dev);
+ if (ret)
+ return ret;
+
+ dev_info(&pdev->dev, "AXI ADC IP core (%d.%.2d.%c) probed\n",
+ ADI_AXI_PCORE_VER_MAJOR(ver),
+ ADI_AXI_PCORE_VER_MINOR(ver),
+ ADI_AXI_PCORE_VER_PATCH(ver));
+
+ return 0;
+}
+
+static struct platform_driver axi_adc_driver = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .of_match_table = axi_adc_of_match,
+ },
+ .probe = axi_adc_probe,
+};
+
+module_platform_driver(axi_adc_driver);
+
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
+MODULE_DESCRIPTION("Analog Devices Generic AXI ADC IP core driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/iio/adc/axi-adc.h b/include/linux/iio/adc/axi-adc.h
new file mode 100644
index 000000000000..d367c442dc52
--- /dev/null
+++ b/include/linux/iio/adc/axi-adc.h
@@ -0,0 +1,79 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Analog Devices Generic AXI ADC IP core driver/library
+ * Link: https://wiki.analog.com/resources/fpga/docs/axi_adc_ip
+ *
+ * Copyright 2012-2020 Analog Devices Inc.
+ */
+#ifndef __AXI_ADC_H__
+#define __AXI_ADC_H__
+
+struct device;
+
+/**
+ * struct axi_adc_chan_spec - AXI ADC channel wrapper
+ * maps IIO channel data with AXI ADC specifics
+ * @iio_chan IIO channel specification
+ * @num_lanes Number of lanes per channel
+ */
+struct axi_adc_chan_spec {
+ struct iio_chan_spec iio_chan;
+ unsigned int num_lanes;
+};
+
+/**
+ * struct axi_adc_chip_info - Chip specific information
+ * @name Chip name
+ * @id Chip ID (usually product ID)
+ * @channels Channel specifications of type @struct axi_adc_chan_spec
+ * @num_channels Number of @channels
+ * @scale_table Supported scales by the chip; tuples of 2 ints
+ * @num_scales Number of scales in the table
+ * @max_rate Maximum sampling rate supported by the device
+ */
+struct axi_adc_chip_info {
+ const char *name;
+ unsigned int id;
+
+ const struct axi_adc_chan_spec *channels;
+ unsigned int num_channels;
+
+ const unsigned int (*scale_table)[2];
+ int num_scales;
+
+ unsigned long max_rate;
+};
+
+/**
+ * struct axi_adc_conv - data of the ADC attached to the AXI ADC
+ * @chip_info chip info details for the client ADC
+ * @preenable_setup op to run in the client before enabling the AXI ADC
+ * @read_raw IIO read_raw hook for the client ADC
+ * @write_raw IIO write_raw hook for the client ADC
+ */
+struct axi_adc_conv {
+ const struct axi_adc_chip_info *chip_info;
+
+ int (*preenable_setup)(struct axi_adc_conv *conv);
+ int (*reg_access)(struct axi_adc_conv *conv, unsigned int reg,
+ unsigned int writeval, unsigned int *readval);
+ int (*read_raw)(struct axi_adc_conv *conv,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask);
+ int (*write_raw)(struct axi_adc_conv *conv,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask);
+};
+
+struct axi_adc_conv *axi_adc_conv_register(struct device *dev,
+ int sizeof_priv);
+void axi_adc_conv_unregister(struct axi_adc_conv *conv);
+
+struct axi_adc_conv *devm_axi_adc_conv_register(struct device *dev,
+ int sizeof_priv);
+void devm_axi_adc_conv_unregister(struct device *dev,
+ struct axi_adc_conv *conv);
+
+void *axi_adc_conv_priv(struct axi_adc_conv *conv);
+
+#endif
--
2.20.1
^ permalink raw reply related
* [PATCH 5/5] dt-bindings: iio: adc: add bindings doc for AD9467 ADC
From: Alexandru Ardelean @ 2020-02-20 15:03 UTC (permalink / raw)
To: linux-kernel, linux-iio, devicetree; +Cc: robh+dt, jic23, Alexandru Ardelean
In-Reply-To: <20200220150317.1864-1-alexandru.ardelean@analog.com>
This change adds the binding doc for the AD9467 ADC.
Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
---
.../bindings/iio/adc/adi,ad9467.yaml | 63 +++++++++++++++++++
1 file changed, 63 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/adc/adi,ad9467.yaml
diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad9467.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad9467.yaml
new file mode 100644
index 000000000000..e94d9ba294d8
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/adi,ad9467.yaml
@@ -0,0 +1,63 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/adc/adi,ad9467.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices AD9467 High-Speed ADC
+
+maintainers:
+ - Michael Hennerich <michael.hennerich@analog.com>
+ - Alexandru Ardelean <alexandru.ardelean@analog.com>
+
+description: |
+ The AD9467 is a 16-bit, monolithic, IF sampling analog-to-digital
+ converter (ADC).
+
+ https://www.analog.com/media/en/technical-documentation/data-sheets/AD9467.pdf
+
+properties:
+ compatible:
+ enum:
+ - adi,ad9467
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+
+ clocks:
+ items:
+ - const: sample-clock
+
+ powerdown-gpios:
+ description:
+ Pin that controls the powerdown mode of the device.
+ maxItems: 1
+
+ reset-gpios:
+ description:
+ Reset pin for the device.
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - clock-names
+
+additionalProperties: false
+
+examples:
+ - |
+ spi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ adc@0 {
+ compatible = "adi,ad9467";
+ reg = <0>;
+ };
+ };
+...
--
2.20.1
^ permalink raw reply related
* Re: [PATCH v7 10/24] mm: Add readahead address space operation
From: Zi Yan @ 2020-02-20 15:00 UTC (permalink / raw)
To: Matthew Wilcox
Cc: linux-xfs, linux-kernel, linux-f2fs-devel, cluster-devel,
linux-mm, ocfs2-devel, linux-fsdevel, linux-ext4, linux-erofs,
linux-btrfs
In-Reply-To: <20200219210103.32400-11-willy@infradead.org>
[-- Attachment #1: Type: text/plain, Size: 7352 bytes --]
On 19 Feb 2020, at 16:00, Matthew Wilcox wrote:
> From: "Matthew Wilcox (Oracle)" <willy@infradead.org>
>
> This replaces ->readpages with a saner interface:
> - Return void instead of an ignored error code.
> - Page cache is already populated with locked pages when ->readahead
> is called.
> - New arguments can be passed to the implementation without changing
> all the filesystems that use a common helper function like
> mpage_readahead().
>
> Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
> ---
> Documentation/filesystems/locking.rst | 6 +++++-
> Documentation/filesystems/vfs.rst | 15 +++++++++++++++
> include/linux/fs.h | 2 ++
> include/linux/pagemap.h | 18 ++++++++++++++++++
> mm/readahead.c | 12 ++++++++++--
> 5 files changed, 50 insertions(+), 3 deletions(-)
>
> diff --git a/Documentation/filesystems/locking.rst b/Documentation/filesystems/locking.rst
> index 5057e4d9dcd1..0af2e0e11461 100644
> --- a/Documentation/filesystems/locking.rst
> +++ b/Documentation/filesystems/locking.rst
> @@ -239,6 +239,7 @@ prototypes::
> int (*readpage)(struct file *, struct page *);
> int (*writepages)(struct address_space *, struct writeback_control *);
> int (*set_page_dirty)(struct page *page);
> + void (*readahead)(struct readahead_control *);
> int (*readpages)(struct file *filp, struct address_space *mapping,
> struct list_head *pages, unsigned nr_pages);
> int (*write_begin)(struct file *, struct address_space *mapping,
> @@ -271,7 +272,8 @@ writepage: yes, unlocks (see below)
> readpage: yes, unlocks
> writepages:
> set_page_dirty no
> -readpages:
> +readahead: yes, unlocks
> +readpages: no
> write_begin: locks the page exclusive
> write_end: yes, unlocks exclusive
> bmap:
> @@ -295,6 +297,8 @@ the request handler (/dev/loop).
> ->readpage() unlocks the page, either synchronously or via I/O
> completion.
>
> +->readahead() unlocks the pages that I/O is attempted on like ->readpage().
> +
> ->readpages() populates the pagecache with the passed pages and starts
> I/O against them. They come unlocked upon I/O completion.
>
> diff --git a/Documentation/filesystems/vfs.rst b/Documentation/filesystems/vfs.rst
> index 7d4d09dd5e6d..ed17771c212b 100644
> --- a/Documentation/filesystems/vfs.rst
> +++ b/Documentation/filesystems/vfs.rst
> @@ -706,6 +706,7 @@ cache in your filesystem. The following members are defined:
> int (*readpage)(struct file *, struct page *);
> int (*writepages)(struct address_space *, struct writeback_control *);
> int (*set_page_dirty)(struct page *page);
> + void (*readahead)(struct readahead_control *);
> int (*readpages)(struct file *filp, struct address_space *mapping,
> struct list_head *pages, unsigned nr_pages);
> int (*write_begin)(struct file *, struct address_space *mapping,
> @@ -781,12 +782,26 @@ cache in your filesystem. The following members are defined:
> If defined, it should set the PageDirty flag, and the
> PAGECACHE_TAG_DIRTY tag in the radix tree.
>
> +``readahead``
> + Called by the VM to read pages associated with the address_space
> + object. The pages are consecutive in the page cache and are
> + locked. The implementation should decrement the page refcount
> + after starting I/O on each page. Usually the page will be
> + unlocked by the I/O completion handler. If the filesystem decides
> + to stop attempting I/O before reaching the end of the readahead
> + window, it can simply return. The caller will decrement the page
> + refcount and unlock the remaining pages for you. Set PageUptodate
> + if the I/O completes successfully. Setting PageError on any page
> + will be ignored; simply unlock the page if an I/O error occurs.
> +
> ``readpages``
> called by the VM to read pages associated with the address_space
> object. This is essentially just a vector version of readpage.
> Instead of just one page, several pages are requested.
> readpages is only used for read-ahead, so read errors are
> ignored. If anything goes wrong, feel free to give up.
> + This interface is deprecated and will be removed by the end of
> + 2020; implement readahead instead.
>
> ``write_begin``
> Called by the generic buffered write code to ask the filesystem
> diff --git a/include/linux/fs.h b/include/linux/fs.h
> index 3cd4fe6b845e..d4e2d2964346 100644
> --- a/include/linux/fs.h
> +++ b/include/linux/fs.h
> @@ -292,6 +292,7 @@ enum positive_aop_returns {
> struct page;
> struct address_space;
> struct writeback_control;
> +struct readahead_control;
>
> /*
> * Write life time hint values.
> @@ -375,6 +376,7 @@ struct address_space_operations {
> */
> int (*readpages)(struct file *filp, struct address_space *mapping,
> struct list_head *pages, unsigned nr_pages);
> + void (*readahead)(struct readahead_control *);
>
> int (*write_begin)(struct file *, struct address_space *mapping,
> loff_t pos, unsigned len, unsigned flags,
> diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h
> index 4989d330fada..b3008605fd1b 100644
> --- a/include/linux/pagemap.h
> +++ b/include/linux/pagemap.h
> @@ -669,6 +669,24 @@ static inline struct page *readahead_page(struct readahead_control *rac)
> return page;
> }
>
> +/* The byte offset into the file of this readahead block */
> +static inline loff_t readahead_pos(struct readahead_control *rac)
> +{
> + return (loff_t)rac->_index * PAGE_SIZE;
> +}
> +
> +/* The number of bytes in this readahead block */
> +static inline loff_t readahead_length(struct readahead_control *rac)
> +{
> + return (loff_t)rac->_nr_pages * PAGE_SIZE;
> +}
> +
> +/* The index of the first page in this readahead block */
> +static inline unsigned int readahead_index(struct readahead_control *rac)
> +{
> + return rac->_index;
> +}
rac->_index is pgoff_t, so readahead_index() should return the same type, right?
BTW, pgoff_t is unsigned long.
> +
> /* The number of pages in this readahead block */
> static inline unsigned int readahead_count(struct readahead_control *rac)
> {
> diff --git a/mm/readahead.c b/mm/readahead.c
> index aaa209559ba2..07cdfbf00f4b 100644
> --- a/mm/readahead.c
> +++ b/mm/readahead.c
> @@ -124,7 +124,14 @@ static void read_pages(struct readahead_control *rac, struct list_head *pages)
>
> blk_start_plug(&plug);
>
> - if (aops->readpages) {
> + if (aops->readahead) {
> + aops->readahead(rac);
> + /* Clean up the remaining pages */
> + while ((page = readahead_page(rac))) {
> + unlock_page(page);
> + put_page(page);
> + }
> + } else if (aops->readpages) {
> aops->readpages(rac->file, rac->mapping, pages,
> readahead_count(rac));
> /* Clean up the remaining pages */
> @@ -234,7 +241,8 @@ void force_page_cache_readahead(struct address_space *mapping,
> struct file_ra_state *ra = &filp->f_ra;
> unsigned long max_pages;
>
> - if (unlikely(!mapping->a_ops->readpage && !mapping->a_ops->readpages))
> + if (unlikely(!mapping->a_ops->readpage && !mapping->a_ops->readpages &&
> + !mapping->a_ops->readahead))
> return;
>
> /*
> --
> 2.25.0
--
Best Regards,
Yan Zi
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 854 bytes --]
^ permalink raw reply
* [PATCH 3/5] dt-bindings: iio: adc: add bindings doc for AXI ADC driver
From: Alexandru Ardelean @ 2020-02-20 15:03 UTC (permalink / raw)
To: linux-kernel, linux-iio, devicetree; +Cc: robh+dt, jic23, Alexandru Ardelean
In-Reply-To: <20200220150317.1864-1-alexandru.ardelean@analog.com>
This change adds the bindings documentation for the AXI ADC driver.
Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
---
.../bindings/iio/adc/adi,axi-adc.yaml | 70 +++++++++++++++++++
1 file changed, 70 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/adc/adi,axi-adc.yaml
diff --git a/Documentation/devicetree/bindings/iio/adc/adi,axi-adc.yaml b/Documentation/devicetree/bindings/iio/adc/adi,axi-adc.yaml
new file mode 100644
index 000000000000..a1c2630c6840
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/adi,axi-adc.yaml
@@ -0,0 +1,70 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/adc/adi,axi-adc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices AXI ADC IP core
+
+maintainers:
+ - Michael Hennerich <michael.hennerich@analog.com>
+ - Alexandru Ardelean <alexandru.ardelean@analog.com>
+
+description: |
+ Analog Devices Generic AXI ADC IP core for interfacing an ADC device
+ with a high speed serial (JESD204B/C) or source synchronous parallel
+ interface (LVDS/CMOS).
+ Usually, some other interface type (i.e SPI) is used as a control
+ interface for the actual ADC, while this IP core will interface
+ to the data-lines of the ADC and handle the streaming of data into
+ memory via DMA.
+
+ https://wiki.analog.com/resources/fpga/docs/axi_adc_ip
+
+properties:
+ compatible:
+ enum:
+ - adi,axi-adc-10.0.a
+
+ reg:
+ maxItems: 1
+
+ dmas:
+ description:
+ A reference to a DMA channel channel specifier.
+ maxItems: 1
+
+ dmas-names:
+ description:
+ The name of the DMA channel.
+ maxItems: 1
+
+ axi-adc-client:
+ description:
+ A reference to a the actual ADC to which this FPGA ADC interfaces to.
+ maxItems: 1
+
+required:
+ - compatible
+ - dmas
+ - reg
+ - axi-adc-client
+
+additionalProperties: false
+
+examples:
+ - |
+ fpga_axi {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ axi-adc@44a00000 {
+ compatible = "adi,axi-adc-10.0.a";
+ reg = <0x44a00000 0x10000>;
+ dmas = <&rx_dma 0>;
+ dma-names = "rx";
+
+ axi-adc-client = <&spi_adc>;
+ };
+ };
+...
--
2.20.1
^ permalink raw reply related
* [PATCH 1/5] iio: buffer-dmaengine: add dev-managed calls for buffer alloc/free
From: Alexandru Ardelean @ 2020-02-20 15:03 UTC (permalink / raw)
To: linux-kernel, linux-iio, devicetree; +Cc: robh+dt, jic23, Alexandru Ardelean
Currently, when using a 'iio_dmaengine_buffer_alloc()', an matching call to
'iio_dmaengine_buffer_free()' must be made.
With this change, this can be avoided by using
'devm_iio_dmaengine_buffer_alloc()'. The buffer will get free'd via the
device's devres handling.
Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
---
.../buffer/industrialio-buffer-dmaengine.c | 70 +++++++++++++++++++
include/linux/iio/buffer-dmaengine.h | 5 ++
2 files changed, 75 insertions(+)
diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
index b129693af0fd..eff89037e3f5 100644
--- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c
+++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
@@ -229,6 +229,76 @@ void iio_dmaengine_buffer_free(struct iio_buffer *buffer)
}
EXPORT_SYMBOL_GPL(iio_dmaengine_buffer_free);
+static void __devm_iio_dmaengine_buffer_free(struct device *dev, void *res)
+{
+ iio_dmaengine_buffer_free(*(struct iio_buffer **)res);
+}
+
+/**
+ * devm_iio_dmaengine_buffer_alloc() - Resource-managed iio_dmaengine_buffer_alloc()
+ * @dev: Parent device for the buffer
+ * @channel: DMA channel name, typically "rx".
+ *
+ * This allocates a new IIO buffer which internally uses the DMAengine framework
+ * to perform its transfers. The parent device will be used to request the DMA
+ * channel.
+ *
+ * Once done using the buffer iio_dmaengine_buffer_free() should be used to
+ * release it.
+ */
+struct iio_buffer *devm_iio_dmaengine_buffer_alloc(struct device *dev,
+ const char *channel)
+{
+ struct iio_buffer **bufferp, *buffer;
+
+ bufferp = devres_alloc(__devm_iio_dmaengine_buffer_free,
+ sizeof(*bufferp), GFP_KERNEL);
+ if (!bufferp)
+ return ERR_PTR(-ENOMEM);
+
+ buffer = iio_dmaengine_buffer_alloc(dev, channel);
+ if (!IS_ERR(buffer)) {
+ *bufferp = buffer;
+ devres_add(dev, bufferp);
+ } else {
+ devres_free(bufferp);
+ }
+
+ return buffer;
+}
+EXPORT_SYMBOL_GPL(devm_iio_dmaengine_buffer_alloc);
+
+static int devm_iio_dmaengine_buffer_match(struct device *dev, void *res,
+ void *data)
+{
+ struct iio_buffer **r = res;
+
+ if (!r || !*r) {
+ WARN_ON(!r || !*r);
+ return 0;
+ }
+
+ return *r == data;
+}
+
+/**
+ * devm_iio_dmaengine_buffer_free - iio_dmaengine_buffer_free
+ * @dev: Device this iio_buffer belongs to
+ * @buffer: The iio_buffer associated with the device
+ *
+ * Free buffer allocated with devm_iio_dmaengine_buffer_alloc().
+ */
+void devm_iio_dmaengine_buffer_free(struct device *dev,
+ struct iio_buffer *buffer)
+{
+ int rc;
+
+ rc = devres_release(dev, __devm_iio_dmaengine_buffer_free,
+ devm_iio_dmaengine_buffer_match, buffer);
+ WARN_ON(rc);
+}
+EXPORT_SYMBOL_GPL(devm_iio_dmaengine_buffer_free);
+
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_DESCRIPTION("DMA buffer for the IIO framework");
MODULE_LICENSE("GPL");
diff --git a/include/linux/iio/buffer-dmaengine.h b/include/linux/iio/buffer-dmaengine.h
index b3a57444a886..8dcd973d76c1 100644
--- a/include/linux/iio/buffer-dmaengine.h
+++ b/include/linux/iio/buffer-dmaengine.h
@@ -14,4 +14,9 @@ struct iio_buffer *iio_dmaengine_buffer_alloc(struct device *dev,
const char *channel);
void iio_dmaengine_buffer_free(struct iio_buffer *buffer);
+struct iio_buffer *devm_iio_dmaengine_buffer_alloc(struct device *dev,
+ const char *channel);
+void devm_iio_dmaengine_buffer_free(struct device *dev,
+ struct iio_buffer *buffer);
+
#endif
--
2.20.1
^ permalink raw reply related
* Re: [PATCH v7 10/24] mm: Add readahead address space operation
From: Zi Yan @ 2020-02-20 15:00 UTC (permalink / raw)
To: Matthew Wilcox
Cc: linux-fsdevel, linux-mm, linux-kernel, linux-btrfs, linux-erofs,
linux-ext4, linux-f2fs-devel, cluster-devel, ocfs2-devel,
linux-xfs
In-Reply-To: <20200219210103.32400-11-willy@infradead.org>
[-- Attachment #1: Type: text/plain, Size: 7352 bytes --]
On 19 Feb 2020, at 16:00, Matthew Wilcox wrote:
> From: "Matthew Wilcox (Oracle)" <willy@infradead.org>
>
> This replaces ->readpages with a saner interface:
> - Return void instead of an ignored error code.
> - Page cache is already populated with locked pages when ->readahead
> is called.
> - New arguments can be passed to the implementation without changing
> all the filesystems that use a common helper function like
> mpage_readahead().
>
> Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
> ---
> Documentation/filesystems/locking.rst | 6 +++++-
> Documentation/filesystems/vfs.rst | 15 +++++++++++++++
> include/linux/fs.h | 2 ++
> include/linux/pagemap.h | 18 ++++++++++++++++++
> mm/readahead.c | 12 ++++++++++--
> 5 files changed, 50 insertions(+), 3 deletions(-)
>
> diff --git a/Documentation/filesystems/locking.rst b/Documentation/filesystems/locking.rst
> index 5057e4d9dcd1..0af2e0e11461 100644
> --- a/Documentation/filesystems/locking.rst
> +++ b/Documentation/filesystems/locking.rst
> @@ -239,6 +239,7 @@ prototypes::
> int (*readpage)(struct file *, struct page *);
> int (*writepages)(struct address_space *, struct writeback_control *);
> int (*set_page_dirty)(struct page *page);
> + void (*readahead)(struct readahead_control *);
> int (*readpages)(struct file *filp, struct address_space *mapping,
> struct list_head *pages, unsigned nr_pages);
> int (*write_begin)(struct file *, struct address_space *mapping,
> @@ -271,7 +272,8 @@ writepage: yes, unlocks (see below)
> readpage: yes, unlocks
> writepages:
> set_page_dirty no
> -readpages:
> +readahead: yes, unlocks
> +readpages: no
> write_begin: locks the page exclusive
> write_end: yes, unlocks exclusive
> bmap:
> @@ -295,6 +297,8 @@ the request handler (/dev/loop).
> ->readpage() unlocks the page, either synchronously or via I/O
> completion.
>
> +->readahead() unlocks the pages that I/O is attempted on like ->readpage().
> +
> ->readpages() populates the pagecache with the passed pages and starts
> I/O against them. They come unlocked upon I/O completion.
>
> diff --git a/Documentation/filesystems/vfs.rst b/Documentation/filesystems/vfs.rst
> index 7d4d09dd5e6d..ed17771c212b 100644
> --- a/Documentation/filesystems/vfs.rst
> +++ b/Documentation/filesystems/vfs.rst
> @@ -706,6 +706,7 @@ cache in your filesystem. The following members are defined:
> int (*readpage)(struct file *, struct page *);
> int (*writepages)(struct address_space *, struct writeback_control *);
> int (*set_page_dirty)(struct page *page);
> + void (*readahead)(struct readahead_control *);
> int (*readpages)(struct file *filp, struct address_space *mapping,
> struct list_head *pages, unsigned nr_pages);
> int (*write_begin)(struct file *, struct address_space *mapping,
> @@ -781,12 +782,26 @@ cache in your filesystem. The following members are defined:
> If defined, it should set the PageDirty flag, and the
> PAGECACHE_TAG_DIRTY tag in the radix tree.
>
> +``readahead``
> + Called by the VM to read pages associated with the address_space
> + object. The pages are consecutive in the page cache and are
> + locked. The implementation should decrement the page refcount
> + after starting I/O on each page. Usually the page will be
> + unlocked by the I/O completion handler. If the filesystem decides
> + to stop attempting I/O before reaching the end of the readahead
> + window, it can simply return. The caller will decrement the page
> + refcount and unlock the remaining pages for you. Set PageUptodate
> + if the I/O completes successfully. Setting PageError on any page
> + will be ignored; simply unlock the page if an I/O error occurs.
> +
> ``readpages``
> called by the VM to read pages associated with the address_space
> object. This is essentially just a vector version of readpage.
> Instead of just one page, several pages are requested.
> readpages is only used for read-ahead, so read errors are
> ignored. If anything goes wrong, feel free to give up.
> + This interface is deprecated and will be removed by the end of
> + 2020; implement readahead instead.
>
> ``write_begin``
> Called by the generic buffered write code to ask the filesystem
> diff --git a/include/linux/fs.h b/include/linux/fs.h
> index 3cd4fe6b845e..d4e2d2964346 100644
> --- a/include/linux/fs.h
> +++ b/include/linux/fs.h
> @@ -292,6 +292,7 @@ enum positive_aop_returns {
> struct page;
> struct address_space;
> struct writeback_control;
> +struct readahead_control;
>
> /*
> * Write life time hint values.
> @@ -375,6 +376,7 @@ struct address_space_operations {
> */
> int (*readpages)(struct file *filp, struct address_space *mapping,
> struct list_head *pages, unsigned nr_pages);
> + void (*readahead)(struct readahead_control *);
>
> int (*write_begin)(struct file *, struct address_space *mapping,
> loff_t pos, unsigned len, unsigned flags,
> diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h
> index 4989d330fada..b3008605fd1b 100644
> --- a/include/linux/pagemap.h
> +++ b/include/linux/pagemap.h
> @@ -669,6 +669,24 @@ static inline struct page *readahead_page(struct readahead_control *rac)
> return page;
> }
>
> +/* The byte offset into the file of this readahead block */
> +static inline loff_t readahead_pos(struct readahead_control *rac)
> +{
> + return (loff_t)rac->_index * PAGE_SIZE;
> +}
> +
> +/* The number of bytes in this readahead block */
> +static inline loff_t readahead_length(struct readahead_control *rac)
> +{
> + return (loff_t)rac->_nr_pages * PAGE_SIZE;
> +}
> +
> +/* The index of the first page in this readahead block */
> +static inline unsigned int readahead_index(struct readahead_control *rac)
> +{
> + return rac->_index;
> +}
rac->_index is pgoff_t, so readahead_index() should return the same type, right?
BTW, pgoff_t is unsigned long.
> +
> /* The number of pages in this readahead block */
> static inline unsigned int readahead_count(struct readahead_control *rac)
> {
> diff --git a/mm/readahead.c b/mm/readahead.c
> index aaa209559ba2..07cdfbf00f4b 100644
> --- a/mm/readahead.c
> +++ b/mm/readahead.c
> @@ -124,7 +124,14 @@ static void read_pages(struct readahead_control *rac, struct list_head *pages)
>
> blk_start_plug(&plug);
>
> - if (aops->readpages) {
> + if (aops->readahead) {
> + aops->readahead(rac);
> + /* Clean up the remaining pages */
> + while ((page = readahead_page(rac))) {
> + unlock_page(page);
> + put_page(page);
> + }
> + } else if (aops->readpages) {
> aops->readpages(rac->file, rac->mapping, pages,
> readahead_count(rac));
> /* Clean up the remaining pages */
> @@ -234,7 +241,8 @@ void force_page_cache_readahead(struct address_space *mapping,
> struct file_ra_state *ra = &filp->f_ra;
> unsigned long max_pages;
>
> - if (unlikely(!mapping->a_ops->readpage && !mapping->a_ops->readpages))
> + if (unlikely(!mapping->a_ops->readpage && !mapping->a_ops->readpages &&
> + !mapping->a_ops->readahead))
> return;
>
> /*
> --
> 2.25.0
--
Best Regards,
Yan Zi
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 854 bytes --]
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.