* Re: [PATCH net-next 1/2] bpf: allow extended BPF programs access skb fields
From: Alexei Starovoitov @ 2015-03-13 17:09 UTC (permalink / raw)
To: Daniel Borkmann, David S. Miller
Cc: Thomas Graf, linux-api, netdev, linux-kernel
In-Reply-To: <550313B6.2080701@iogearbox.net>
On 3/13/15 9:43 AM, Daniel Borkmann wrote:
> On 03/13/2015 05:22 PM, Alexei Starovoitov wrote:
>> On 3/13/15 2:57 AM, Daniel Borkmann wrote:
>>> On 03/13/2015 03:21 AM, Alexei Starovoitov wrote:
>>>> introduce user accessible mirror of in-kernel 'struct sk_buff':
>>>
>>> For each member, I'd also add BUILD_BUG_ON()s similarly as we have in
>>> convert_bpf_extensions(). That way, people won't forget to adjust the
>>> code.
>>
>> I thought about it, but didn't add it, since we already have them
>> in the same file several lines above this spot.
>> I think one build error per .c file should be enough to attract
>> attention.
>> Though I'll add a comment to convert_bpf_extensions() that build_bug_on
>> errors should be addressed in two places.
>
> My concern would be that in case the code gets changed, this spot
> could easily be overlooked by someone possibly unfamiliar with the
> code, since no build error triggers there.
>
> So I guess it wouldn't hurt or cost us much to also adding the
> BUILD_BUG_ON()s there instead of a comment.
I think it's overkill, but fine, it's minor. Will add another set
of build_bug_ons and see how it looks.
^ permalink raw reply
* Re: [PATCH v4 4/9] selftests: Add install target
From: Shuah Khan @ 2015-03-13 17:08 UTC (permalink / raw)
To: Dave Jones, Michael Ellerman, linux-kernel, mmarek, linux-api,
Shuah Khan
In-Reply-To: <20150313163312.GA24666@codemonkey.org.uk>
On 03/13/2015 10:33 AM, Dave Jones wrote:
> On Thu, Mar 12, 2015 at 02:15:44PM +1100, Michael Ellerman wrote:
> > On Wed, 2015-03-11 at 07:18 -0600, Shuah Khan wrote:
> > > On 03/10/2015 10:06 PM, Michael Ellerman wrote:
> > > > This adds make install support to selftests. The basic usage is:
> > > >
> > > > v3: Rebase onto 4.0-rc2.
> > > > Rename all.sh to run_kselftest.sh.
> > > > Add --no-print-directory to emit_tests invocation.
> > > > v4: Rebase onto 4.0-rc3, add TEST_FILES to efivars and vm tests, remove
> > > > newlines from echoes.
> > >
> > > I don't see my comments addressed. If you want me to take
> > > this work, please address the following comments:
> > >
> > > - Name install directory kselftest. It should work with the
> > > the use-case.
> > >
> > > make INSTALL_PATH=/tmp make install
> > > The install directory should be /tmp/kselftest
> > >
> > > - Flatten the directory with all tests under /tmp/kselftest
> > >
> > > I am wasting lot of time because you don't fully address my
> > > comments and send patches that dont' work correctly. Please
> > > make sure your patches don't generate work for me.
> >
> > You're wasting a lot of time? You have got to be kidding me. You are wasting a
> > lot of *my* time.
> >
> > You have my patches, they're signed off, you can do what you wish with them.
> > Good luck.
>
> This is getting silly. We've spent three months arguing over shell
> scripts and makefiles for what should be a trivial feature.
> We make fundamental changes to the VM code with less contention.
I really don't think this process has to this contentious. I don't
believe the changes I am asking for are unreasonable.
>
> It's not helped that timezones are working against everyone here,
> so even minor changes end up taking a while to get reposted, re-reviewed
> etc, but the level of pushback because something isn't perfect is absurd,
> especially when we're talking about new features.
Directory structure might seem like a trivial problem, however it is
important to get the user interface right.
Since Michael doesn't have the bandwidth to make the suggested changes,
I working on applying his patches. There are some conflicts with the
selftests: Introduce minimal shared logic for running tests
selftests: Add install target
patches and timer patches. I can fix the directory hierarchy after
I get these two patches into next.
thanks,
-- Shuah
--
Shuah Khan
Sr. Linux Kernel Developer
Open Source Innovation Group
Samsung Research America (Silicon Valley)
shuahkh@osg.samsung.com | (970) 217-8978
^ permalink raw reply
* Re: [PATCH net-next 1/2] bpf: allow extended BPF programs access skb fields
From: Daniel Borkmann @ 2015-03-13 16:43 UTC (permalink / raw)
To: Alexei Starovoitov, David S. Miller
Cc: Thomas Graf, linux-api, netdev, linux-kernel
In-Reply-To: <55030ED8.4020209@plumgrid.com>
On 03/13/2015 05:22 PM, Alexei Starovoitov wrote:
> On 3/13/15 2:57 AM, Daniel Borkmann wrote:
>> On 03/13/2015 03:21 AM, Alexei Starovoitov wrote:
>>> introduce user accessible mirror of in-kernel 'struct sk_buff':
>>
>> For each member, I'd also add BUILD_BUG_ON()s similarly as we have in
>> convert_bpf_extensions(). That way, people won't forget to adjust the
>> code.
>
> I thought about it, but didn't add it, since we already have them
> in the same file several lines above this spot.
> I think one build error per .c file should be enough to attract
> attention.
> Though I'll add a comment to convert_bpf_extensions() that build_bug_on
> errors should be addressed in two places.
My concern would be that in case the code gets changed, this spot
could easily be overlooked by someone possibly unfamiliar with the
code, since no build error triggers there.
So I guess it wouldn't hurt or cost us much to also adding the
BUILD_BUG_ON()s there instead of a comment.
...
>> The remaining fields we export in classic BPF would be skb->hash,
>> skb->protocol, skb->vlan_tci, are we adding them as well to match
>> up functionality with classic BPF? For example, I can see hash being
>> useful as a key to be used with eBPF maps, etc.
>
> I want to do skb->protocol and skb->vlan_tci differently and hopefully
> more generically than classic did them, so they will come in the
> follow on patches. skb->hash also should be done differently.
> So yes. All of them are subjects of future patches/discussions.
Ok, sounds good.
Thanks,
Daniel
^ permalink raw reply
* Re: [PATCH v6 tip 3/8] tracing: allow BPF programs to call bpf_ktime_get_ns()
From: Alexei Starovoitov @ 2015-03-13 16:38 UTC (permalink / raw)
To: He Kuang, Ingo Molnar
Cc: Steven Rostedt, Namhyung Kim, Arnaldo Carvalho de Melo, Jiri Olsa,
Masami Hiramatsu, David S. Miller, Daniel Borkmann,
Peter Zijlstra, linux-api-u79uwXL29TY76Z2rM5mHXA,
netdev-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
wangnan0-hv44wF8Li93QT0dZR+AlfA
In-Reply-To: <5502C8EB.9070307-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
On 3/13/15 4:24 AM, He Kuang wrote:
> Hi, Alexei
>
> I've followed up your bpf version. In bpf filter, sometimes we need to
> get 'pid' and some other context informations to decide whether to
> filter or not.
>
> For example, to trace a vfs read procedure, we can insert bpf program to
> '__vfs_read(struct file *file, char __user *buf ...)', mark some of
> 'buf' addresses and only trace the read procedure of these 'buf's. But
> this parameter is a userspace pointer, the value is meaningless to other
> processes, so we should also record 'pid' to make sense.
>
> To a function like __vfs_read, 'pid' can't be extracted from function
> parameters directly. What's your opinion on this issue?
yes. it's the next thing on todo list after this set of patches.
There are several ways to let programs see 'pid'. We'll debate about
the best approach hopefully soon :)
^ permalink raw reply
* Re: [PATCH v4 4/9] selftests: Add install target
From: Dave Jones @ 2015-03-13 16:33 UTC (permalink / raw)
To: Michael Ellerman
Cc: Shuah Khan, linux-kernel-u79uwXL29TY76Z2rM5mHXA,
mmarek-AlSwsSmVLrQ, linux-api-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1426130144.25936.4.camel-Gsx/Oe8HsFggBc27wqDAHg@public.gmane.org>
On Thu, Mar 12, 2015 at 02:15:44PM +1100, Michael Ellerman wrote:
> On Wed, 2015-03-11 at 07:18 -0600, Shuah Khan wrote:
> > On 03/10/2015 10:06 PM, Michael Ellerman wrote:
> > > This adds make install support to selftests. The basic usage is:
> > >
> > > v3: Rebase onto 4.0-rc2.
> > > Rename all.sh to run_kselftest.sh.
> > > Add --no-print-directory to emit_tests invocation.
> > > v4: Rebase onto 4.0-rc3, add TEST_FILES to efivars and vm tests, remove
> > > newlines from echoes.
> >
> > I don't see my comments addressed. If you want me to take
> > this work, please address the following comments:
> >
> > - Name install directory kselftest. It should work with the
> > the use-case.
> >
> > make INSTALL_PATH=/tmp make install
> > The install directory should be /tmp/kselftest
> >
> > - Flatten the directory with all tests under /tmp/kselftest
> >
> > I am wasting lot of time because you don't fully address my
> > comments and send patches that dont' work correctly. Please
> > make sure your patches don't generate work for me.
>
> You're wasting a lot of time? You have got to be kidding me. You are wasting a
> lot of *my* time.
>
> You have my patches, they're signed off, you can do what you wish with them.
> Good luck.
This is getting silly. We've spent three months arguing over shell
scripts and makefiles for what should be a trivial feature.
We make fundamental changes to the VM code with less contention.
It's not helped that timezones are working against everyone here,
so even minor changes end up taking a while to get reposted, re-reviewed
etc, but the level of pushback because something isn't perfect is absurd,
especially when we're talking about new features.
If you want the in-kernel variant to be perfect every step of the way,
and move slowly and never introduce subtle breakage along the way that then
gets fixed up in subsequent changes, maybe a faster moving external project
that syncs up with the kernel tree is the way forward.
If kselftests is going to evolve into something useful, something has to change.
Dave
^ permalink raw reply
* Re: [PATCH net-next 1/2] bpf: allow extended BPF programs access skb fields
From: Alexei Starovoitov @ 2015-03-13 16:22 UTC (permalink / raw)
To: Daniel Borkmann, David S. Miller
Cc: Thomas Graf, linux-api, netdev, linux-kernel
In-Reply-To: <5502B48B.7040909@iogearbox.net>
On 3/13/15 2:57 AM, Daniel Borkmann wrote:
> On 03/13/2015 03:21 AM, Alexei Starovoitov wrote:
>> introduce user accessible mirror of in-kernel 'struct sk_buff':
>
> For each member, I'd also add BUILD_BUG_ON()s similarly as we have in
> convert_bpf_extensions(). That way, people won't forget to adjust the
> code.
I thought about it, but didn't add it, since we already have them
in the same file several lines above this spot.
I think one build error per .c file should be enough to attract
attention.
Though I'll add a comment to convert_bpf_extensions() that build_bug_on
errors should be addressed in two places.
btw I've tried to do a common converter, but since offsets are different
and the way instructions are stored are also different it was messy.
> General idea for this offset map looks good, imho. Well defined members
> that are already exported to uapi e.g. through classic socket filters or
> other socket api places could be used here.
yes. exactly.
>> +struct __sk_buff {
>> + __u32 len;
>> + __u32 pkt_type;
>> + __u32 mark;
>> + __u32 ifindex;
>> + __u32 queue_mapping;
>> +};
>
> I'd add a comment saying that fields may _only_ be safely added at
> the end of the structure. Rearranging or removing members here,
> naturally would break user space.
ok. will add a comment.
> The remaining fields we export in classic BPF would be skb->hash,
> skb->protocol, skb->vlan_tci, are we adding them as well to match
> up functionality with classic BPF? For example, I can see hash being
> useful as a key to be used with eBPF maps, etc.
I want to do skb->protocol and skb->vlan_tci differently and hopefully
more generically than classic did them, so they will come in the
follow on patches. skb->hash also should be done differently.
So yes. All of them are subjects of future patches/discussions.
>> + /* several new insns need to be inserted. Make room for them */
>> + insn_cnt += cnt - 1;
>> + new_prog = bpf_prog_realloc(env->prog,
>> + bpf_prog_size(insn_cnt),
>> + GFP_USER);
>> + if (!new_prog)
>> + return -ENOMEM;
>
> Seems a bit expensive, do you think we could speculatively allocate a
> bit more space in bpf_prog_load() when we detect that we have access
> to ctx that we need to convert?
we already have extra space thanks to your commit 60a3b2253c413 :)))
The size is rounded up to page size and whole thing is made read-only
after it passes verifier.
So 99% of the time we don't reallocate anything.
>> + case offsetof(struct __sk_buff, ifindex):
>> + *insn++ = BPF_LDX_MEM(BPF_W, dst_reg, src_reg,
>> + offsetof(struct sk_buff, skb_iif));
>> + break;
>
> This would only work for incoming skbs, but not outgoing ones
> f.e. in case of {cls,act}_bpf.
ahh, ok, will drop this field for now.
Thanks
^ permalink raw reply
* Re: [PATCH 6/6] clone4: Introduce new CLONE_FD flag to get task exit notification via fd
From: Oleg Nesterov @ 2015-03-13 16:21 UTC (permalink / raw)
To: Josh Triplett
Cc: Al Viro, Andrew Morton, Andy Lutomirski, Ingo Molnar, Kees Cook,
Paul E. McKenney, H. Peter Anvin, Rik van Riel, Thomas Gleixner,
Thiago Macieira, Michael Kerrisk, linux-kernel, linux-api,
linux-fsdevel, x86
In-Reply-To: <9c39c576e1d9a9912b4aec54d833a73a84d2f592.1426180120.git.josh@joshtriplett.org>
Josh,
I'll certainly try to read this series, but not before next week.
but a couple of nits right now.
On 03/12, Josh Triplett wrote:
>
> When passed CLONE_FD, clone4 will return a file descriptor rather than a
> PID. When the child process exits, it gets automatically reaped,
And even I have no idea what you are actually doing, this doesn't look
right, see below.
> +static unsigned int clonefd_poll(struct file *file, poll_table *wait)
> +{
> + struct task_struct *p = file->private_data;
> + poll_wait(file, &p->clonefd_wqh, wait);
> + return p->exit_state == EXIT_DEAD ? (POLLIN | POLLRDNORM) : 0;
> +}
> +
> +static ssize_t clonefd_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
> +{
> + struct task_struct *p = file->private_data;
> + int ret = 0;
> +
> + /* EOF after first read */
> + if (*ppos)
> + return 0;
> +
> + if (file->f_flags & O_NONBLOCK)
> + ret = -EAGAIN;
> + else
> + ret = wait_event_interruptible(p->clonefd_wqh, p->exit_state == EXIT_DEAD);
> +
> + if (p->exit_state == EXIT_DEAD) {
Again, I simply do not know what this code does at all. But I bet the usage
of EXIT_DEAD is wrong ;)
OK, OK, I can be wrong. But I simply do not see what protects this task_struct
if it is EXIT_DEAD (in fact even if it is EXIT_ZOMBIE).
> @@ -598,7 +600,9 @@ static void exit_notify(struct task_struct *tsk, int group_dead)
> if (group_dead)
> kill_orphaned_pgrp(tsk->group_leader, NULL);
>
> - if (unlikely(tsk->ptrace)) {
> + if (tsk->autoreap) {
> + autoreap = true;
Debuggers won't be happy. A ptraced task should not autoreap itself.
Oleg.
^ permalink raw reply
* Re: [RFC] capabilities: Ambient capabilities
From: Christoph Lameter @ 2015-03-13 16:06 UTC (permalink / raw)
To: Andrew G. Morgan
Cc: Andrew Lutomirski, Kees Cook, Serge Hallyn, Andy Lutomirski,
Jonathan Corbet, Aaron Jones, Ted Ts'o, linux-security-module,
LKML, Linux API, Andrew Morton, Mimi Zohar, Austin S Hemmelgarn,
Markku Savela, Jarkko Sakkinen, Michael Kerrisk
In-Reply-To: <CALQRfL6HUGMfPtsqLcSed8G14Yqg27R8tpDnMZOKtvQE+rqX0A@mail.gmail.com>
On Fri, 13 Mar 2015, Andrew G. Morgan wrote:
> > prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_SYS_ADMIN);
> > system("/bin/bash");
>
> Let's call the above two lines [a] and [b]. With this patch, you are
> encouraging folk to write programs that contain a line like [a]
> already. So, yes, I am saying that you are creating an exploitable
> path in these programs that says if someone can inject
> system("/bin/bash") into these programs they can get a new (because of
> this patch) privilege escalation.
Well this is what one naively expects capabilities to give you. An ability
to avoid full superuser binaries by segmenting off capabilities. Often
there is really no other choice. If you do not provide this mode then
the system will be even less secure since people run stuff as root.
This looks to many like the design of capabilties is inherent flawed since
it does not give you what you need. You experience a go around that leads
nowhere and just wastes your time.
> In the prevailing model, this kind of privilege escalation (resulting
> from naive inheritance) is designed out. I recognize that you want to
> add it back in, but I am concerned that you are not allowing for the
> possibility that some folk might still want to still be able to
> prevent it.
The functionalty here depends on CAP_SETPCAP. That was intended as some
point to be off by default? You can have distros/kernels with that being
off.
^ permalink raw reply
* Re: [PATCH 0/6] CLONE_FD: Task exit notification via file descriptor
From: David Drysdale @ 2015-03-13 16:05 UTC (permalink / raw)
To: Josh Triplett
Cc: Al Viro, Andrew Morton, Andy Lutomirski, Ingo Molnar, Kees Cook,
Oleg Nesterov, Paul E. McKenney, H. Peter Anvin, Rik van Riel,
Thomas Gleixner, Thiago Macieira, Michael Kerrisk,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Linux API,
linux-fsdevel-u79uwXL29TY76Z2rM5mHXA, X86 ML
In-Reply-To: <cover.1426180120.git.josh-iaAMLnmF4UmaiuxdJuQwMA@public.gmane.org>
On Fri, Mar 13, 2015 at 1:40 AM, Josh Triplett <josh-iaAMLnmF4UmaiuxdJuQwMA@public.gmane.org> wrote:
> This patch series introduces a new clone flag, CLONE_FD, which lets the caller
> handle child process exit notification via a file descriptor rather than
> SIGCHLD. CLONE_FD makes it possible for libraries to safely launch and manage
> child processes on behalf of their caller, *without* taking over process-wide
> SIGCHLD handling (either via signal handler or signalfd).
Hi Josh,
From the overall description (i.e. I haven't looked at the code yet)
this looks very interesting. However, it seems to cover a lot of the
same ground as the process descriptor feature that was added to FreeBSD
in 9.x/10.x:
https://www.freebsd.org/cgi/man.cgi?query=pdfork&sektion=2
I think it would ideally be nice for a userspace library developer to be
able to do subprocess management (without SIGCHLD) in a similar way
across both platforms, without lots of complicated autoconf shenanigans.
So could we look at the overlap and seeing if we can come up with
something that covers your requirements and also allows for something
that looks like FreeBSD's process descriptors?
(I've actually got some rough patches to add process descriptor
functionality on Linux, so I can look at how the two approaches compare
and contrast.)
> Note that signalfd for SIGCHLD does not suffice here, because that still
> receives notification for all child processes, and interferes with process-wide
> signal handling.
>
> The CLONE_FD file descriptor uniquely identifies a process on the system in a
> race-free way, by holding a reference to the task_struct. In the future, we
> may introduce APIs that support using process file descriptors instead of PIDs.
FreeBSD has pdkill(2) and (theoretically) pdwait4(2) along these lines.
I suspect we need either need pdkill(2) or a way to retrieve a PID from
a process file descriptor, so that there's a way to send signals to the
child.
> Introducing CLONE_FD required two additional bits of yak shaving: Since clone
> has no more usable flags (with the three currently unused flags unusable
> because old kernels ignore them without EINVAL), also introduce a new clone4
> system call with more flag bits and an extensible argument structure. And
> since the magic pt_regs-based syscall argument processing for clone's tls
> argument would otherwise prevent introducing a sane clone4 system call, fix
> that too.
>
> I tested the CLONE_SETTLS changes with a thread-local storage test program (two
> threads independently reading and writing a __thread variable), on both 32-bit
> and 64-bit, and I observed no issues there.
Worth preserving in tools/testing/selftests/ ?
> I tested clone4 and the new CLONE_FD call with several additional test
> programs, launching either a process or thread (in the former case using
> syscall(), in the latter case by calling clone4 via assembly and returning to
> C), sleeping in parent and child to test the case of either exiting first, and
> then printing the received clone4_info structure. Thiago also tested clone4
> with CLONE_FD with a modified version of libqt's process handling, which
> includes a test suite.
>
> I've also included the manpages patch at the end of this series. (Note that
> the manpage documents the behavior of the future glibc wrapper as well as the
> raw syscall.) Here's a formatted plain-text version of the manpage for
> reference:
FYI, I've added some comparisons with the FreeBSD equivalents below.
>
> CLONE4(2) Linux Programmer's Manual CLONE4(2)
>
>
>
> NAME
> clone4 - create a child process
>
> SYNOPSIS
> /* Prototype for the glibc wrapper function */
>
> #define _GNU_SOURCE
> #include <sched.h>
>
> int clone4(uint64_t flags,
> size_t args_size,
> struct clone4_args *args,
> int (*fn)(void *), void *arg);
>
> /* Prototype for the raw system call */
>
> int clone4(unsigned flags_high, unsigned flags_low,
> unsigned long args_size,
> struct clone4_args *args);
>
> struct clone4_args {
> pid_t *ptid;
> pid_t *ctid;
> unsigned long stack_start;
> unsigned long stack_size;
> unsigned long tls;
> };
>
>
> DESCRIPTION
> clone4() creates a new process, similar to clone(2) and fork(2).
> clone4() supports additional flags that clone(2) does not, and accepts
> arguments via an extensible structure.
>
> args points to a clone4_args structure, and args_size must contain the
> size of that structure, as understood by the caller. If the caller
> passes a shorter structure than the kernel expects, the remaining
> fields will default to 0. If the caller passes a larger structure than
> the kernel expects (such as one from a newer kernel), clone4() will
> return EINVAL. The clone4_args structure may gain additional fields at
> the end in the future, and callers must only pass a size that encom‐
> passes the number of fields they understand. If the caller passes 0
> for args_size, args is ignored and may be NULL.
>
> In the clone4_args structure, ptid, ctid, stack_start, stack_size, and
> tls have the same semantics as they do with clone(2) and clone2(2).
>
> In the glibc wrapper, fn and arg have the same semantics as they do
> with clone(2). As with clone(2), the underlying system call works more
> like fork(2), returning 0 in the child process; the glibc wrapper sim‐
> plifies thread execution by calling fn(arg) and exiting the child when
> that function exits.
>
> The 64-bit flags argument (split into the 32-bit flags_high and
> flags_low arguments in the kernel interface) accepts all the same flags
> as clone(2), with the exception of the obsolete CLONE_PID,
> CLONE_DETACHED, and CLONE_STOPPED. In addition, flags accepts the fol‐
> lowing flags:
>
>
> CLONE_FD
> Instead of returning a process ID, clone4() with the CLONE_FD
> flag returns a file descriptor associated with the new process.
> When the new process exits, the kernel will not send a signal to
> the parent process, and will not keep the new process around as
> a "zombie" process until a call to waitpid(2) or similar.
> Instead, the file descriptor will become available for reading,
> and the new process will be immediately reaped.
Just to confirm: presumably a waitpid(-1,...) call that's already in
progress won't return when one of these child processes exits?
>
> Unlike using signalfd(2) for the SIGCHLD signal, the file
> descriptor returned by clone4() with the CLONE_FD flag works
> even with SIGCHLD unblocked in one or more threads of the parent
> process, and allows the process to have different handlers for
> different child processes, such as those created by a library,
> without introducing race conditions around process-wide signal
> handling.
>
> clone4() will never return a file descriptor in the range 0-2 to
> the caller, to avoid ambiguity with the return of 0 in the child
> process. Only the calling process will have the new file
> descriptor open; the child process will not.
FreeBSD's pdfork(2) returns a PID but also takes an int *fdp argument to
return the file descriptor separately, which avoids the need for special
case processing for low FD values (and means that POSIX's "lowest file
descriptor not currently open" behaviour can be preserved if desired).
> Since the kernel does not send a termination signal when a child
> process created with CLONE_FD exits, the low byte of flags does
> not contain a signal number. Instead, the low byte of flags can
> contain the following additional flags for use with CLONE_FD:
>
>
> CLONEFD_CLOEXEC
> Set the O_CLOEXEC flag on the new open file descriptor.
> See the description of the O_CLOEXEC flag in open(2) for
> reasons why this may be useful.
>
>
> CLONEFD_NONBLOCK
> Set the O_NONBLOCK flag on the new open file descriptor.
> Using this flag saves extra calls to fcntl(2) to achieve
> the same result.
>
>
> clone4() with the CLONE_FD flag returns a file descriptor that
> supports the following operations:
>
> read(2) (and similar)
> When the new process exits, reading from the file
> descriptor produces a single clonefd_info structure:
>
> struct clonefd_info {
> uint32_t code; /* Signal code */
> uint32_t status; /* Exit status or signal */
> uint64_t utime; /* User CPU time */
> uint64_t stime; /* System CPU time */
> };
Presumably there is no way to get full rusage information for the exited
process?
[FreeBSD theoretically has pdwait4(2) to do wait4-like operations on a
process descriptor, including rusage retrieval. However, I don't think
they actually implemented it:
http://fxr.watson.org/fxr/source/kern/syscalls.master#L928]
>
> If the new process has not yet exited, read(2) either
> blocks until it does, or fails with the error EAGAIN if
> the file descriptor has been made nonblocking.
>
> Future kernels may extend clonefd_info by appending addi‐
> tional fields to the end. Callers should read as many
> bytes as they understand; unread data will be discarded,
> and subsequent reads after the first will return 0 to
> indicate end-of-file. Callers requesting more bytes than
> the kernel provides (such as callers expecting a newer
> clonefd_info structure) will receive a shorter structure
> from older kernels.
FreeBSD also implements fstat(2) for its process descriptors, although
only a few of the fields get filled in.
> poll(2), select(2), epoll(7) (and similar)
> The file descriptor is readable (the select(2) readfds
> argument; the poll(2) POLLIN flag) if the new process has
> exited.
FreeBSD uses POLLHUP here.
> close(2)
> When the file descriptor is no longer required it should
> be closed. If no process has a file descriptor open for
> the new process, no process will receive any notification
> when the new process exits. The new process will still
> be immediately reaped.
FreeBSD has two different behaviours for close(2), depending on a flag
value (PD_DAEMON). With the flag set it's roughly like this, but
without PD_DAEMON a close(2) operation on the (last open) file
descriptor terminates the child process.
This can be quite useful, particularly for the use case where some
userspace library has an FD-controlled subprocess -- if the application
using the library terminates, the process descriptor is closed and so
the subprocess is automatically terminated.
>
> C library/kernel ABI differences
> As with clone(2), the raw clone4() system call corresponds more closely
> to fork(2) in that execution in the child continues from the point of
> the call.
>
> Unlike clone(2), the raw system call interface for clone4() accepts
> arguments in the same order on all architectures.
>
> The raw system call accepts flags as two 32-bit arguments, flags_high
> and flags_low, to simplify portability across 32-bit and 64-bit archi‐
> tectures and calling conventions. The glibc wrapper accepts flags as a
> single 64-bit argument for convenience.
>
>
> RETURN VALUE
> For the glibc wrapper, on success, clone4() returns the file descriptor
> (with CLONE_FD) or new process ID (without CLONE_FD), and the child
> process begins running at the specified function.
>
> For the raw syscall, on success, clone4() returns the file descriptor
> or new process ID to the calling process, and returns 0 in the new
> child process.
>
> On failure, clone4() returns -1 and sets errno accordingly.
>
>
> ERRORS
> clone4() can return any error from clone(2), as well as the following
> additional errors:
>
> EINVAL flags contained an unknown flag.
>
> EINVAL flags included CLONE_FD, but the kernel configuration does not
> have the CONFIG_CLONEFD option enabled.
>
> EMFILE flags included CLONE_FD, but the new file descriptor would
> exceed the process limit on open file descriptors.
>
> ENFILE flags included CLONE_FD, but the new file descriptor would
> exceed the system-wide limit on open file descriptors.
>
> ENODEV flags included CLONE_FD, but clone4() could not mount the
> (internal) anonymous inode device.
>
>
> CONFORMING TO
> clone4() is Linux-specific and should not be used in programs intended
> to be portable.
>
>
> SEE ALSO
> clone(2), epoll(7), poll(2), pthreads(7), read(2), select(2)
>
>
>
> Linux 2015-03-01 CLONE4(2)
^ permalink raw reply
* Re: [PATCH v4 0/9] epoll: Introduce new syscalls, epoll_ctl_batch and epoll_pwait1
From: Paolo Bonzini @ 2015-03-13 14:56 UTC (permalink / raw)
To: Jason Baron, Fam Zheng
Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA, Thomas Gleixner, Ingo Molnar,
H. Peter Anvin, x86-DgEjT+Ai2ygdnm+yROfE0A, Alexander Viro,
Andrew Morton, Kees Cook, Andy Lutomirski, David Herrmann,
Alexei Starovoitov, Miklos Szeredi, David Drysdale, Oleg Nesterov,
David S. Miller, Vivek Goyal, Mike Frysinger, Theodore Ts'o,
Heiko Carstens, Rasmus Villemoes, Rashika Kheria, Hugh Dickins,
Mathieu Desnoyers, Peter Zijlstra <peter>
In-Reply-To: <5502F857.6050505-JqFfY2XvxFXQT0dZR+AlfA@public.gmane.org>
On 13/03/2015 15:46, Jason Baron wrote:
> > The throttling algorithm computes a duration for the next IO, which is used to
> > arm a timer in order to delay the request a bit. As timers are always rounded
> > *UP* to the effective granularity, the timeout being 1ms in epoll_pwait is just
> > too coarse and will lead to severe inaccuracy. With epoll_pwait1, we can avoid
> > the rounding-up.
>
> right, but we could use the timerfd here to get the desired precision.
Fam, didn't you see slowdowns with few file descriptors
epoll_ctl+epoll_wait+timerfd compared to ppoll?
Do they disappear or improve with epoll_ctl_batch and epoll_pwait1?
Paolo
^ permalink raw reply
* Re: [PATCH v4 0/9] epoll: Introduce new syscalls, epoll_ctl_batch and epoll_pwait1
From: Jason Baron @ 2015-03-13 14:46 UTC (permalink / raw)
To: Fam Zheng
Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA, Thomas Gleixner, Ingo Molnar,
H. Peter Anvin, x86-DgEjT+Ai2ygdnm+yROfE0A, Alexander Viro,
Andrew Morton, Kees Cook, Andy Lutomirski, David Herrmann,
Alexei Starovoitov, Miklos Szeredi, David Drysdale, Oleg Nesterov,
David S. Miller, Vivek Goyal, Mike Frysinger, Theodore Ts'o,
Heiko Carstens, Rasmus Villemoes, Rashika Kheria, Hugh Dickins,
Mathieu Desnoyers, Peter Zijlstra <peterz@
In-Reply-To: <20150313113122.GA7427-ZfWej9ACyHUXGNroddHbYwC/G2K4zDHf@public.gmane.org>
On 03/13/2015 07:31 AM, Fam Zheng wrote:
> On Thu, 03/12 11:02, Jason Baron wrote:
>> On 03/09/2015 09:49 PM, Fam Zheng wrote:
>>
>> Hi,
>>
>> So it sounds like you are comparing original qemu code (which was using
>> ppoll) vs. using epoll with these new syscalls. Curious if you have numbers
>> comparing the existing epoll (with say the timerfd in your epoll set), so
>> we can see the improvement relative to epoll.
> I did compare them, but they are too close to see differences. The improvements
> in epoll_pwait1 doesn't really help the hot path of guest IO, but it does
> affect the program timer precision, that are used in various device emulations
> in QEMU.
>
> Although it's kind of subtle and difficult to summarize here, I can give an
> example in the IO throttling implementation in QEMU, to show the significance:
>
> The throttling algorithm computes a duration for the next IO, which is used to
> arm a timer in order to delay the request a bit. As timers are always rounded
> *UP* to the effective granularity, the timeout being 1ms in epoll_pwait is just
> too coarse and will lead to severe inaccuracy. With epoll_pwait1, we can avoid
> the rounding-up.
right, but we could use the timerfd here to get the desired precision.
> I think this idea could be pertty generally desired by other applications, too.
>
> Regarding the epoll_ctl_batch improvement, again, it is not going to disrupt
> the numbers in the small workload I managed to test.
>
> Of course, if you have a specific application senario in mind, I will try it. :)
I want to understand what new functionality these syscalls offer over
what we have now. I mean we could show a micro-benchmark where
these matter, but is that enough to justify these new syscalls given that
I think we could implement library wrappers around what we have now
to do what you are proposing here.
Thanks,
-Jason
^ permalink raw reply
* Re: [PATCH v3 10/15] serial: stm32-usart: Add STM32 USART Driver
From: Andy Shevchenko @ 2015-03-13 14:19 UTC (permalink / raw)
To: Maxime Coquelin
Cc: u.kleine-koenig, afaerber, Geert Uytterhoeven, Rob Herring,
Philipp Zabel, Linus Walleij, Arnd Bergmann, stefan, pmeerw,
pebolle, Jonathan Corbet, Pawel Moll, Mark Rutland, Ian Campbell,
Kumar Gala, Russell King, Daniel Lezcano, Thomas Gleixner,
Greg Kroah-Hartman, Jiri Slaby, Andrew Morton, David S. Miller,
Mauro Carvalho Chehab, Joe Perches, Antti Palosaari
In-Reply-To: <1426197361-19290-11-git-send-email-maxime.coquelin@st.com>
On Thu, Mar 12, 2015 at 11:55 PM, Maxime Coquelin
<mcoquelin.stm32@gmail.com> wrote:
> From: Maxime Coquelin <mcoquelin.stm32@gmail.com>
>
> This drivers adds support to the STM32 USART controller, which is a
> standard serial driver.
My comment below.
>
> Signed-off-by: Maxime Coquelin <mcoquelin.stm32@gmail.com>
> ---
> drivers/tty/serial/Kconfig | 17 +
> drivers/tty/serial/Makefile | 1 +
> drivers/tty/serial/stm32-usart.c | 695 +++++++++++++++++++++++++++++++++++++++
> include/uapi/linux/serial_core.h | 3 +
> 4 files changed, 716 insertions(+)
> create mode 100644 drivers/tty/serial/stm32-usart.c
>
> diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
> index d2501f0..880cb4f 100644
> --- a/drivers/tty/serial/Kconfig
> +++ b/drivers/tty/serial/Kconfig
> @@ -1611,6 +1611,23 @@ config SERIAL_SPRD_CONSOLE
> with "earlycon" on the kernel command line. The console is
> enabled when early_param is processed.
>
> +config SERIAL_STM32
> + tristate "STMicroelectronics STM32 serial port support"
> + select SERIAL_CORE
> + depends on ARM || COMPILE_TEST
> + help
> + This driver is for the on-chip Serial Controller on
> + STMicroelectronics STM32 MCUs.
> + USART supports Rx & Tx functionality.
> + It support all industry standard baud rates.
> +
> + If unsure, say N.
> +
> +config SERIAL_STM32_CONSOLE
> + bool "Support for console on STM32"
> + depends on SERIAL_STM32=y
> + select SERIAL_CORE_CONSOLE
> +
> endmenu
>
> config SERIAL_MCTRL_GPIO
> diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
> index 599be4b..67c5023 100644
> --- a/drivers/tty/serial/Makefile
> +++ b/drivers/tty/serial/Makefile
> @@ -95,6 +95,7 @@ obj-$(CONFIG_SERIAL_FSL_LPUART) += fsl_lpuart.o
> obj-$(CONFIG_SERIAL_CONEXANT_DIGICOLOR) += digicolor-usart.o
> obj-$(CONFIG_SERIAL_MEN_Z135) += men_z135_uart.o
> obj-$(CONFIG_SERIAL_SPRD) += sprd_serial.o
> +obj-$(CONFIG_SERIAL_STM32) += stm32-usart.o
>
> # GPIOLIB helpers for modem control lines
> obj-$(CONFIG_SERIAL_MCTRL_GPIO) += serial_mctrl_gpio.o
> diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c
> new file mode 100644
> index 0000000..61bb065
> --- /dev/null
> +++ b/drivers/tty/serial/stm32-usart.c
> @@ -0,0 +1,695 @@
> +/*
> + * Copyright (C) Maxime Coquelin 2015
> + * Author: Maxime Coquelin <mcoquelin.stm32@gmail.com>
> + * License terms: GNU General Public License (GPL), version 2
> + *
> + * Inspired by st-asc.c from STMicroelectronics (c)
> + */
> +
> +#if defined(CONFIG_SERIAL_STM32_USART_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
> +#define SUPPORT_SYSRQ
> +#endif
> +
> +#include <linux/module.h>
> +#include <linux/serial.h>
> +#include <linux/console.h>
> +#include <linux/sysrq.h>
> +#include <linux/platform_device.h>
> +#include <linux/io.h>
> +#include <linux/irq.h>
> +#include <linux/tty.h>
> +#include <linux/tty_flip.h>
> +#include <linux/delay.h>
> +#include <linux/spinlock.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/serial_core.h>
> +#include <linux/clk.h>
> +
> +#define DRIVER_NAME "stm32-usart"
> +
> +/* Register offsets */
> +#define USART_SR 0x00
> +#define USART_DR 0x04
> +#define USART_BRR 0x08
> +#define USART_CR1 0x0c
> +#define USART_CR2 0x10
> +#define USART_CR3 0x14
> +#define USART_GTPR 0x18
> +
> +/* USART_SR */
> +#define USART_SR_PE BIT(0)
> +#define USART_SR_FE BIT(1)
> +#define USART_SR_NF BIT(2)
> +#define USART_SR_ORE BIT(3)
> +#define USART_SR_IDLE BIT(4)
> +#define USART_SR_RXNE BIT(5)
> +#define USART_SR_TC BIT(6)
> +#define USART_SR_TXE BIT(7)
> +#define USART_SR_LBD BIT(8)
> +#define USART_SR_CTS BIT(9)
> +#define USART_SR_ERR_MASK (USART_SR_LBD | USART_SR_ORE | \
> + USART_SR_FE | USART_SR_PE)
> +/* Dummy bits */
> +#define USART_SR_DUMMY_RX BIT(16)
> +
> +/* USART_DR */
> +#define USART_DR_MASK GENMASK(8, 0)
> +
> +/* USART_BRR */
> +#define USART_BRR_DIV_F_MASK GENMASK(3, 0)
> +#define USART_BRR_DIV_M_MASK GENMASK(15, 4)
> +#define USART_BRR_DIV_M_SHIFT 4
> +
> +/* USART_CR1 */
> +#define USART_CR1_SBK BIT(0)
> +#define USART_CR1_RWU BIT(1)
> +#define USART_CR1_RE BIT(2)
> +#define USART_CR1_TE BIT(3)
> +#define USART_CR1_IDLEIE BIT(4)
> +#define USART_CR1_RXNEIE BIT(5)
> +#define USART_CR1_TCIE BIT(6)
> +#define USART_CR1_TXEIE BIT(7)
> +#define USART_CR1_PEIE BIT(8)
> +#define USART_CR1_PS BIT(9)
> +#define USART_CR1_PCE BIT(10)
> +#define USART_CR1_WAKE BIT(11)
> +#define USART_CR1_M BIT(12)
> +#define USART_CR1_UE BIT(13)
> +#define USART_CR1_OVER8 BIT(15)
> +#define USART_CR1_IE_MASK GENMASK(8, 4)
> +
> +/* USART_CR2 */
> +#define USART_CR2_ADD_MASK GENMASK(3, 0)
> +#define USART_CR2_LBDL BIT(5)
> +#define USART_CR2_LBDIE BIT(6)
> +#define USART_CR2_LBCL BIT(8)
> +#define USART_CR2_CPHA BIT(9)
> +#define USART_CR2_CPOL BIT(10)
> +#define USART_CR2_CLKEN BIT(11)
> +#define USART_CR2_STOP_2B BIT(13)
> +#define USART_CR2_STOP_MASK GENMASK(13, 12)
> +#define USART_CR2_LINEN BIT(14)
> +
> +/* USART_CR3 */
> +#define USART_CR3_EIE BIT(0)
> +#define USART_CR3_IREN BIT(1)
> +#define USART_CR3_IRLP BIT(2)
> +#define USART_CR3_HDSEL BIT(3)
> +#define USART_CR3_NACK BIT(4)
> +#define USART_CR3_SCEN BIT(5)
> +#define USART_CR3_DMAR BIT(6)
> +#define USART_CR3_DMAT BIT(7)
> +#define USART_CR3_RTSE BIT(8)
> +#define USART_CR3_CTSE BIT(9)
> +#define USART_CR3_CTSIE BIT(10)
> +#define USART_CR3_ONEBIT BIT(11)
> +
> +/* USART_GTPR */
> +#define USART_GTPR_PSC_MASK GENMASK(7, 0)
> +#define USART_GTPR_GT_MASK GENMASK(15, 8)
> +
> +#define DRIVER_NAME "stm32-usart"
> +#define STM32_SERIAL_NAME "ttyS"
> +#define STM32_MAX_PORTS 6
> +
> +struct stm32_port {
> + struct uart_port port;
> + struct clk *clk;
> +};
> +
> +static struct stm32_port stm32_ports[STM32_MAX_PORTS];
> +static struct uart_driver stm32_usart_driver;
> +
> +static void stm32_stop_tx(struct uart_port *port);
> +
> +static void stm32_set_bits(struct uart_port *port, u32 reg, u32 bits)
> +{
> + u32 val;
> +
> + val = readl_relaxed(port->membase + reg);
> + val |= bits;
> + writel_relaxed(val, port->membase + reg);
> +}
> +
> +static void stm32_clr_bits(struct uart_port *port, u32 reg, u32 bits)
> +{
> + u32 val;
> +
> + val = readl_relaxed(port->membase + reg);
> + val &= ~bits;
> + writel_relaxed(val, port->membase + reg);
> +}
> +
> +static void stm32_receive_chars(struct uart_port *port)
> +{
> + struct tty_port *tport = &port->state->port;
> + unsigned long c;
> + u32 sr;
> + char flag;
> +
> + if (port->irq_wake)
> + pm_wakeup_event(tport->tty->dev, 0);
> +
> + while ((sr = readl_relaxed(port->membase + USART_SR)) & USART_SR_RXNE) {
> + sr |= USART_SR_DUMMY_RX;
> + c = readl_relaxed(port->membase + USART_DR);
> + flag = TTY_NORMAL;
> + port->icount.rx++;
> +
> + if (sr & USART_SR_ERR_MASK) {
> + if (sr & USART_SR_LBD) {
> + port->icount.brk++;
> + if (uart_handle_break(port))
> + continue;
> + } else if (sr & USART_SR_ORE) {
> + port->icount.overrun++;
> + } else if (sr & USART_SR_PE) {
> + port->icount.parity++;
> + } else if (sr & USART_SR_FE) {
> + port->icount.frame++;
> + }
> +
> + sr &= port->read_status_mask;
> +
> + if (sr & USART_SR_LBD)
> + flag = TTY_BREAK;
> + else if (sr & USART_SR_PE)
> + flag = TTY_PARITY;
> + else if (sr & USART_SR_FE)
> + flag = TTY_FRAME;
> + }
> +
> + if (uart_handle_sysrq_char(port, c))
> + continue;
> + uart_insert_char(port, sr, USART_SR_ORE, c, flag);
> + }
> +
> + spin_unlock(&port->lock);
> + tty_flip_buffer_push(tport);
> + spin_lock(&port->lock);
> +}
> +
> +static void stm32_transmit_chars(struct uart_port *port)
> +{
> + struct circ_buf *xmit = &port->state->xmit;
> +
> + if (port->x_char) {
> + writel_relaxed(port->x_char, port->membase + USART_DR);
> + port->x_char = 0;
> + port->icount.tx++;
> + return;
> + }
> +
> + if (uart_tx_stopped(port)) {
> + stm32_stop_tx(port);
> + return;
> + }
> +
> + if (uart_circ_empty(xmit)) {
> + stm32_stop_tx(port);
> + return;
> + }
> +
> + writel_relaxed(xmit->buf[xmit->tail], port->membase + USART_DR);
> + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
> + port->icount.tx++;
> +
> + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + uart_write_wakeup(port);
> +
> + if (uart_circ_empty(xmit))
> + stm32_stop_tx(port);
> +}
> +
> +static irqreturn_t stm32_interrupt(int irq, void *ptr)
> +{
> + struct uart_port *port = ptr;
> + u32 sr;
> +
> + spin_lock(&port->lock);
> +
> + sr = readl_relaxed(port->membase + USART_SR);
> +
> + if (sr & USART_SR_RXNE)
> + stm32_receive_chars(port);
> +
> + if (sr & USART_SR_TXE)
> + stm32_transmit_chars(port);
> +
> + spin_unlock(&port->lock);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static unsigned int stm32_tx_empty(struct uart_port *port)
> +{
> + return readl_relaxed(port->membase + USART_SR) & USART_SR_TXE;
> +}
> +
> +static void stm32_set_mctrl(struct uart_port *port, unsigned int mctrl)
> +{
> + /*
> + * This routine is used for seting signals of: DTR, DCD, CTS/RTS
> + * We use USART's hardware for CTS/RTS, so don't need any for that.
> + * Some boards have DTR and DCD implemented using PIO pins,
> + * code to do this should be hooked in here.
> + */
> +}
> +
> +static unsigned int stm32_get_mctrl(struct uart_port *port)
> +{
> + /*
> + * This routine is used for geting signals of: DTR, DCD, DSR, RI,
> + * and CTS/RTS
> + */
> + return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;
> +}
> +
> +/* There are probably characters waiting to be transmitted. */
> +static void stm32_start_tx(struct uart_port *port)
> +{
> + struct circ_buf *xmit = &port->state->xmit;
> +
> + if (uart_circ_empty(xmit))
> + return;
> +
> + stm32_set_bits(port, USART_CR1, USART_CR1_TXEIE | USART_CR1_TE);
> +}
> +
> +/* Transmit stop */
> +static void stm32_stop_tx(struct uart_port *port)
> +{
> + stm32_clr_bits(port, USART_CR1, USART_CR1_TXEIE);
> +}
> +
> +/* Receive stop */
> +static void stm32_stop_rx(struct uart_port *port)
> +{
> + stm32_clr_bits(port, USART_CR1, USART_CR1_RXNEIE);
> +}
> +
> +/* Handle breaks - ignored by us */
> +static void stm32_break_ctl(struct uart_port *port, int break_state)
> +{
> + /* Nothing here yet .. */
> +}
> +
> +static int stm32_startup(struct uart_port *port)
> +{
> + const char *name = to_platform_device(port->dev)->name;
> + u32 val;
> +
> + if (request_irq(port->irq, stm32_interrupt, IRQF_NO_SUSPEND,
> + name, port)) {
> + dev_err(port->dev, "cannot allocate irq %d\n", port->irq);
> + return -ENODEV;
> + }
> +
> + val = USART_CR1_RXNEIE | USART_CR1_TE | USART_CR1_RE;
> + stm32_set_bits(port, USART_CR1, val);
> +
> + return 0;
> +}
> +
> +static void stm32_shutdown(struct uart_port *port)
> +{
> + u32 val;
> +
> + val = USART_CR1_TXEIE | USART_CR1_RXNEIE | USART_CR1_TE | USART_CR1_RE;
> + stm32_set_bits(port, USART_CR1, val);
> +
> + free_irq(port->irq, port);
> +}
> +
> +static void stm32_set_termios(struct uart_port *port, struct ktermios *termios,
> + struct ktermios *old)
> +{
> + unsigned int baud;
> + u32 usardiv, mantissa, fraction;
> + tcflag_t cflag;
> + u32 cr1, cr2, cr3;
> + unsigned long flags;
> +
> + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16);
> + cflag = termios->c_cflag;
> +
> + spin_lock_irqsave(&port->lock, flags);
> +
> + /* Stop serial port and reset value */
> + writel_relaxed(0, port->membase + USART_CR1);
> +
> + cr1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE | USART_CR1_RXNEIE;
> +
> + if (cflag & CSTOPB)
> + cr2 = USART_CR2_STOP_2B;
> +
> + if (cflag & PARENB) {
> + cr1 |= USART_CR1_PCE;
> + if ((cflag & CSIZE) == CS8)
> + cr1 |= USART_CR1_M;
> + }
> +
> + if (cflag & PARODD)
> + cr1 |= USART_CR1_PS;
> +
> + if (cflag & CRTSCTS)
> + cr3 = USART_CR3_RTSE | USART_CR3_CTSE;
> +
> + usardiv = (port->uartclk * 25) / (baud * 4);
> + mantissa = (usardiv / 100) << USART_BRR_DIV_M_SHIFT;
> + fraction = DIV_ROUND_CLOSEST((usardiv % 100) * 16, 100);
> + if (fraction & ~USART_BRR_DIV_F_MASK) {
> + fraction = 0;
> + mantissa += (1 << USART_BRR_DIV_M_SHIFT);
> + }
So, it's a fractional divider. right? Could it be then fractional
divider clock in this first place (see
drivers/clk/clk-fractional-divider.c)?
> +
> + writel_relaxed(mantissa | fraction, port->membase + USART_BRR);
> +
> + uart_update_timeout(port, cflag, baud);
> +
> + port->read_status_mask = USART_SR_ORE;
> + if (termios->c_iflag & INPCK)
> + port->read_status_mask |= USART_SR_PE | USART_SR_FE;
> + if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
> + port->read_status_mask |= USART_SR_LBD;
> +
> + /* Characters to ignore */
> + port->ignore_status_mask = 0;
> + if (termios->c_iflag & IGNPAR)
> + port->ignore_status_mask = USART_SR_PE | USART_SR_FE;
> + if (termios->c_iflag & IGNBRK) {
> + port->ignore_status_mask |= USART_SR_LBD;
> + /*
> + * If we're ignoring parity and break indicators,
> + * ignore overruns too (for real raw support).
> + */
> + if (termios->c_iflag & IGNPAR)
> + port->ignore_status_mask |= USART_SR_ORE;
> + }
> +
> + /*
> + * Ignore all characters if CREAD is not set.
> + */
> + if ((termios->c_cflag & CREAD) == 0)
> + port->ignore_status_mask |= USART_SR_DUMMY_RX;
> +
> + writel_relaxed(cr3, port->membase + USART_CR3);
> + writel_relaxed(cr2, port->membase + USART_CR2);
> + writel_relaxed(cr1, port->membase + USART_CR1);
> +
> + spin_unlock_irqrestore(&port->lock, flags);
> +}
> +
> +static const char *stm32_type(struct uart_port *port)
> +{
> + return (port->type == PORT_STM32) ? DRIVER_NAME : NULL;
> +}
> +
> +static void stm32_release_port(struct uart_port *port)
> +{
> +}
> +
> +static int stm32_request_port(struct uart_port *port)
> +{
> + return 0;
> +}
> +
> +static void stm32_config_port(struct uart_port *port, int flags)
> +{
> + if ((flags & UART_CONFIG_TYPE))
> + port->type = PORT_STM32;
> +}
> +
> +static int
> +stm32_verify_port(struct uart_port *port, struct serial_struct *ser)
> +{
> + /* No user changeable parameters */
> + return -EINVAL;
> +}
> +
> +static void stm32_pm(struct uart_port *port, unsigned int state,
> + unsigned int oldstate)
> +{
> + struct stm32_port *stm32port = container_of(port,
> + struct stm32_port, port);
> + unsigned long flags = 0;
> +
> + switch (state) {
> + case UART_PM_STATE_ON:
> + clk_prepare_enable(stm32port->clk);
> + break;
> + case UART_PM_STATE_OFF:
> + spin_lock_irqsave(&port->lock, flags);
> + stm32_clr_bits(port, USART_CR1, USART_CR1_UE);
> + spin_unlock_irqrestore(&port->lock, flags);
> + clk_disable_unprepare(stm32port->clk);
> + break;
> + }
> +}
> +
> +static struct uart_ops stm32_uart_ops = {
> + .tx_empty = stm32_tx_empty,
> + .set_mctrl = stm32_set_mctrl,
> + .get_mctrl = stm32_get_mctrl,
> + .start_tx = stm32_start_tx,
> + .stop_tx = stm32_stop_tx,
> + .stop_rx = stm32_stop_rx,
> + .break_ctl = stm32_break_ctl,
> + .startup = stm32_startup,
> + .shutdown = stm32_shutdown,
> + .set_termios = stm32_set_termios,
> + .type = stm32_type,
> + .release_port = stm32_release_port,
> + .request_port = stm32_request_port,
> + .config_port = stm32_config_port,
> + .verify_port = stm32_verify_port,
> + .pm = stm32_pm,
> +};
> +
> +static int stm32_init_port(struct stm32_port *stm32port,
> + struct platform_device *pdev)
> +{
> + struct uart_port *port = &stm32port->port;
> + struct resource *res;
> +
> + port->iotype = UPIO_MEM;
> + port->flags = UPF_BOOT_AUTOCONF;
> + port->ops = &stm32_uart_ops;
> + port->dev = &pdev->dev;
> + port->irq = platform_get_irq(pdev, 0);
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + port->membase = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(port->membase))
> + return PTR_ERR(port->membase);
> + port->mapbase = res->start;
> +
> + spin_lock_init(&port->lock);
> +
> + stm32port->clk = devm_clk_get(&pdev->dev, NULL);
> +
> + if (WARN_ON(IS_ERR(stm32port->clk)))
> + return -EINVAL;
> +
> + /* ensure that clk rate is correct by enabling the clk */
> + clk_prepare_enable(stm32port->clk);
> + stm32port->port.uartclk = clk_get_rate(stm32port->clk);
> + WARN_ON(stm32port->port.uartclk == 0);
> + clk_disable_unprepare(stm32port->clk);
> +
> + return 0;
> +}
> +
> +static struct stm32_port *stm32_of_get_stm32_port(struct platform_device *pdev)
> +{
> + struct device_node *np = pdev->dev.of_node;
> + int id;
> +
> + if (!np)
> + return NULL;
> +
> + id = of_alias_get_id(np, STM32_SERIAL_NAME);
> +
> + if (id < 0)
> + id = 0;
> +
> + if (WARN_ON(id >= STM32_MAX_PORTS))
> + return NULL;
> +
> + stm32_ports[id].port.line = id;
> + return &stm32_ports[id];
> +}
> +
> +#ifdef CONFIG_OF
> +static const struct of_device_id stm32_match[] = {
> + { .compatible = "st,stm32-usart", },
> + {},
> +};
> +
> +MODULE_DEVICE_TABLE(of, stm32_match);
> +#endif
> +
> +static int stm32_serial_probe(struct platform_device *pdev)
> +{
> + int ret;
> + struct stm32_port *stm32port;
> +
> + stm32port = stm32_of_get_stm32_port(pdev);
> + if (!stm32port)
> + return -ENODEV;
> +
> + ret = stm32_init_port(stm32port, pdev);
> + if (ret)
> + return ret;
> +
> + ret = uart_add_one_port(&stm32_usart_driver, &stm32port->port);
> + if (ret)
> + return ret;
> +
> + platform_set_drvdata(pdev, &stm32port->port);
> +
> + return 0;
> +}
> +
> +static int stm32_serial_remove(struct platform_device *pdev)
> +{
> + struct uart_port *port = platform_get_drvdata(pdev);
> +
> + return uart_remove_one_port(&stm32_usart_driver, port);
> +}
> +
> +
> +#ifdef CONFIG_SERIAL_STM32_CONSOLE
> +static void stm32_console_putchar(struct uart_port *port, int ch)
> +{
> + while (!(readl_relaxed(port->membase + USART_SR) & USART_SR_TXE))
> + cpu_relax();
> +
> + writel_relaxed(ch, port->membase + USART_DR);
> +}
> +
> +static void stm32_console_write(struct console *co, const char *s, unsigned cnt)
> +{
> + struct uart_port *port = &stm32_ports[co->index].port;
> + unsigned long flags;
> + u32 old_cr1, new_cr1;
> + int locked = 1;
> +
> + if (oops_in_progress) {
> + locked = spin_trylock_irqsave(&port->lock, flags);
> + } else {
> + locked = 1;
> + spin_lock_irqsave(&port->lock, flags);
> + }
> +
> + /* Save and disable interrupts */
> + old_cr1 = readl_relaxed(port->membase + USART_CR1);
> + new_cr1 = old_cr1 & ~USART_CR1_IE_MASK;
> + writel_relaxed(new_cr1, port->membase + USART_CR1);
> +
> + uart_console_write(port, s, cnt, stm32_console_putchar);
> +
> + /* Restore interrupt state */
> + writel_relaxed(old_cr1, port->membase + USART_CR1);
> +
> + if (locked)
> + spin_unlock_irqrestore(&port->lock, flags);
> +}
> +
> +static int stm32_console_setup(struct console *co, char *options)
> +{
> + struct stm32_port *stm32port;
> + int baud = 9600;
> + int bits = 8;
> + int parity = 'n';
> + int flow = 'n';
> +
> + if (co->index >= STM32_MAX_PORTS)
> + return -ENODEV;
> +
> + stm32port = &stm32_ports[co->index];
> +
> + /*
> + * This driver does not support early console initialization
> + * (use ARM early printk support instead), so we only expect
> + * this to be called during the uart port registration when the
> + * driver gets probed and the port should be mapped at that point.
> + */
> + if (stm32port->port.mapbase == 0 || stm32port->port.membase == NULL)
> + return -ENXIO;
> +
> + if (options)
> + uart_parse_options(options, &baud, &parity, &bits, &flow);
> +
> + return uart_set_options(&stm32port->port, co, baud, parity, bits, flow);
> +}
> +
> +static struct console stm32_console = {
> + .name = STM32_SERIAL_NAME,
> + .device = uart_console_device,
> + .write = stm32_console_write,
> + .setup = stm32_console_setup,
> + .flags = CON_PRINTBUFFER,
> + .index = -1,
> + .data = &stm32_usart_driver,
> +};
> +
> +#define STM32_SERIAL_CONSOLE (&stm32_console)
> +
> +#else
> +#define STM32_SERIAL_CONSOLE NULL
> +#endif /* CONFIG_SERIAL_STM32_CONSOLE */
> +
> +static struct uart_driver stm32_usart_driver = {
> + .owner = THIS_MODULE,
> + .driver_name = DRIVER_NAME,
> + .dev_name = STM32_SERIAL_NAME,
> + .major = 0,
> + .minor = 0,
> + .nr = STM32_MAX_PORTS,
> + .cons = STM32_SERIAL_CONSOLE,
> +};
> +
> +static struct platform_driver stm32_serial_driver = {
> + .probe = stm32_serial_probe,
> + .remove = stm32_serial_remove,
> + .driver = {
> + .name = DRIVER_NAME,
> + .owner = THIS_MODULE,
> + .of_match_table = of_match_ptr(stm32_match),
> + },
> +};
> +
> +static int __init usart_init(void)
> +{
> + int ret;
> + static char banner[] __initdata =
> + KERN_INFO "STM32 USART driver initialized\n";
> +
> + printk(banner);
> +
> + ret = uart_register_driver(&stm32_usart_driver);
> + if (ret)
> + return ret;
> +
> + ret = platform_driver_register(&stm32_serial_driver);
> + if (ret)
> + uart_unregister_driver(&stm32_usart_driver);
> +
> + return ret;
> +}
> +
> +static void __exit usart_exit(void)
> +{
> + platform_driver_unregister(&stm32_serial_driver);
> + uart_unregister_driver(&stm32_usart_driver);
> +}
> +
> +module_init(usart_init);
> +module_exit(usart_exit);
> +
> +MODULE_ALIAS("platform:" DRIVER_NAME);
> +MODULE_DESCRIPTION("STMicroelectronics STM32 serial port driver");
> +MODULE_LICENSE("GPL");
> diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h
> index b212281..e22dee5 100644
> --- a/include/uapi/linux/serial_core.h
> +++ b/include/uapi/linux/serial_core.h
> @@ -258,4 +258,7 @@
> /* Cris v10 / v32 SoC */
> #define PORT_CRIS 112
>
> +/* STM32 USART */
> +#define PORT_STM32 110
> +
> #endif /* _UAPILINUX_SERIAL_CORE_H */
> --
> 1.9.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply
* Re: [RFC] capabilities: Ambient capabilities
From: Andrew G. Morgan @ 2015-03-13 13:24 UTC (permalink / raw)
To: Andrew Lutomirski
Cc: Kees Cook, Christoph Lameter, Serge Hallyn, Andy Lutomirski,
Jonathan Corbet, Aaron Jones, Ted Ts'o, linux-security-module,
LKML, Linux API, Andrew Morton, Mimi Zohar, Austin S Hemmelgarn,
Markku Savela, Jarkko Sakkinen, Michael Kerrisk
In-Reply-To: <CAObL_7HgU-ekb7mJS7C=0=idfrzV6=CSqTrG2LvUgdDtvbJx_w-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
> It's to preserve the invariant that pA is always a subset of pI.
But since a user can always raise a bit in pI if it is present in pP,
what does this invariant add to your model other than inconvenience?
>> I'm also unclear how you can turn off this new 'feature' for a process
>> tree? As it is, the code creates an exploit path for a capable (pP !=
>> 0) program with an exploitable flaw to create a privilege escalation
>> for an arbitrary child program.
>
> Huh? If you exploit the parent, you already win. Yes, if a kiddie
> injects shellcode that does system("/bin/bash") into some pP != 0
> program, they don't actually elevate their privileges. On the other
> hand, by the time an attacker injected shellcode for:
>
> prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_SYS_ADMIN);
> system("/bin/bash");
Let's call the above two lines [a] and [b]. With this patch, you are
encouraging folk to write programs that contain a line like [a]
already. So, yes, I am saying that you are creating an exploitable
path in these programs that says if someone can inject
system("/bin/bash") into these programs they can get a new (because of
this patch) privilege escalation.
In the prevailing model, this kind of privilege escalation (resulting
from naive inheritance) is designed out. I recognize that you want to
add it back in, but I am concerned that you are not allowing for the
possibility that some folk might still want to still be able to
prevent it.
> into a target, they can already do whatever they want.
>
>> While I understand that everyone
>> 'knows what they are doing' in implementing this change, I'm convinced
>> that folk that are up to no good also do... Why not provide a lockable
>> secure bit to selectively disable this support?
>
> Show me a legitimate use case and I'll gladly implement a secure bit.
Thanks. I was kind of hoping that you would add a lockable secure bit
that defaults this support to off, but can be used to turn it on with
or without a lock. That way, you can avoid disturbing the legacy
defaults (no surprises).
> In the mean time, I don't even believe that there's a legitimate use
> for any of the other secure bits (except keepcaps, and I don't know
> why that's a securebit in the first place).
Those bits currently make it possible to run a subsystem with no
[set]uid-0 support in its process tree.
> In the mean time, see CVE-2014-3215 for an example of why securebits
> are probably more trouble than they're worth.
I think it is safe to say that naive privilege inheritance has a fair
track record of being exploited orders of magnitude more frequently
than this. After all, these are the reasons LD_PRELOAD and shell
script setuid bits are suppressed.
Cheers
Andrew
^ permalink raw reply
* Re: [PATCH v4 0/9] epoll: Introduce new syscalls, epoll_ctl_batch and epoll_pwait1
From: Fam Zheng @ 2015-03-13 11:31 UTC (permalink / raw)
To: Jason Baron
Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA, Thomas Gleixner, Ingo Molnar,
H. Peter Anvin, x86-DgEjT+Ai2ygdnm+yROfE0A, Alexander Viro,
Andrew Morton, Kees Cook, Andy Lutomirski, David Herrmann,
Alexei Starovoitov, Miklos Szeredi, David Drysdale, Oleg Nesterov,
David S. Miller, Vivek Goyal, Mike Frysinger, Theodore Ts'o,
Heiko Carstens, Rasmus Villemoes, Rashika Kheria, Hugh Dickins,
Mathieu Desnoyers, Peter Zijlstra <peter>
In-Reply-To: <5501AA6B.2020209-JqFfY2XvxFXQT0dZR+AlfA@public.gmane.org>
On Thu, 03/12 11:02, Jason Baron wrote:
> On 03/09/2015 09:49 PM, Fam Zheng wrote:
> >
> > Benchmark for epoll_pwait1
> > ==========================
> >
> > By running fio tests inside VM with both original and modified QEMU, we can
> > compare their difference in performance.
> >
> > With a small VM setup [t1], the original QEMU (ppoll based) has an 4k read
> > latency overhead around 37 us. In this setup, the main loop polls 10~20 fds.
> >
> > With a slightly larger VM instance [t2] - attached a virtio-serial device so
> > that there are 80~90 fds in the main loop - the original QEMU has a latency
> > overhead around 49 us. By adding more such devices [t3], we can see the latency
> > go even higher - 83 us with ~200 FDs.
> >
> > Now modify QEMU to use epoll_pwait1 and test again, the latency numbers are
> > repectively 36us, 37us, 47us for t1, t2 and t3.
> >
> >
>
> Hi,
>
> So it sounds like you are comparing original qemu code (which was using
> ppoll) vs. using epoll with these new syscalls. Curious if you have numbers
> comparing the existing epoll (with say the timerfd in your epoll set), so
> we can see the improvement relative to epoll.
I did compare them, but they are too close to see differences. The improvements
in epoll_pwait1 doesn't really help the hot path of guest IO, but it does
affect the program timer precision, that are used in various device emulations
in QEMU.
Although it's kind of subtle and difficult to summarize here, I can give an
example in the IO throttling implementation in QEMU, to show the significance:
The throttling algorithm computes a duration for the next IO, which is used to
arm a timer in order to delay the request a bit. As timers are always rounded
*UP* to the effective granularity, the timeout being 1ms in epoll_pwait is just
too coarse and will lead to severe inaccuracy. With epoll_pwait1, we can avoid
the rounding-up.
I think this idea could be pertty generally desired by other applications, too.
Regarding the epoll_ctl_batch improvement, again, it is not going to disrupt
the numbers in the small workload I managed to test.
Of course, if you have a specific application senario in mind, I will try it. :)
Thanks,
Fam
^ permalink raw reply
* Re: [PATCH v6 tip 3/8] tracing: allow BPF programs to call bpf_ktime_get_ns()
From: He Kuang @ 2015-03-13 11:24 UTC (permalink / raw)
To: Alexei Starovoitov, Ingo Molnar
Cc: Steven Rostedt, Namhyung Kim, Arnaldo Carvalho de Melo, Jiri Olsa,
Masami Hiramatsu, David S. Miller, Daniel Borkmann,
Peter Zijlstra, linux-api-u79uwXL29TY76Z2rM5mHXA,
netdev-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
wangnan0-hv44wF8Li93QT0dZR+AlfA
In-Reply-To: <1426047534-8148-4-git-send-email-ast-uqk4Ao+rVK5Wk0Htik3J/w@public.gmane.org>
Hi, Alexei
I've followed up your bpf version. In bpf filter, sometimes we need to
get 'pid' and some other context informations to decide whether to
filter or not.
For example, to trace a vfs read procedure, we can insert bpf program to
'__vfs_read(struct file *file, char __user *buf ...)', mark some of
'buf' addresses and only trace the read procedure of these 'buf's. But
this parameter is a userspace pointer, the value is meaningless to other
processes, so we should also record 'pid' to make sense.
To a function like __vfs_read, 'pid' can't be extracted from function
parameters directly. What's your opinion on this issue?
Thanks!
On 2015/3/11 12:18, Alexei Starovoitov wrote:
> bpf_ktime_get_ns() is used by programs to compue time delta between events
> or as a timestamp
>
> Signed-off-by: Alexei Starovoitov <ast-uqk4Ao+rVK5Wk0Htik3J/w@public.gmane.org>
> ---
> include/uapi/linux/bpf.h | 1 +
> kernel/trace/bpf_trace.c | 11 +++++++++++
> 2 files changed, 12 insertions(+)
>
> diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
> index 4486d36d2e9e..101e509d1001 100644
> --- a/include/uapi/linux/bpf.h
> +++ b/include/uapi/linux/bpf.h
> @@ -165,6 +165,7 @@ enum bpf_func_id {
> BPF_FUNC_map_update_elem, /* int map_update_elem(&map, &key, &value, flags) */
> BPF_FUNC_map_delete_elem, /* int map_delete_elem(&map, &key) */
> BPF_FUNC_probe_read, /* int bpf_probe_read(void *dst, int size, void *src) */
> + BPF_FUNC_ktime_get_ns, /* u64 bpf_ktime_get_ns(void) */
> __BPF_FUNC_MAX_ID,
> };
>
> diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
> index 450ea93ac4ab..ee7c2c629e75 100644
> --- a/kernel/trace/bpf_trace.c
> +++ b/kernel/trace/bpf_trace.c
> @@ -56,6 +56,12 @@ static u64 bpf_probe_read(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5)
> return probe_kernel_read(dst, unsafe_ptr, size);
> }
>
> +static u64 bpf_ktime_get_ns(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5)
> +{
> + /* NMI safe access to clock monotonic */
> + return ktime_get_mono_fast_ns();
> +}
> +
> static struct bpf_func_proto kprobe_prog_funcs[] = {
> [BPF_FUNC_probe_read] = {
> .func = bpf_probe_read,
> @@ -65,6 +71,11 @@ static struct bpf_func_proto kprobe_prog_funcs[] = {
> .arg2_type = ARG_CONST_STACK_SIZE,
> .arg3_type = ARG_ANYTHING,
> },
> + [BPF_FUNC_ktime_get_ns] = {
> + .func = bpf_ktime_get_ns,
> + .gpl_only = true,
> + .ret_type = RET_INTEGER,
> + },
> };
>
> static const struct bpf_func_proto *kprobe_prog_func_proto(enum bpf_func_id func_id)
^ permalink raw reply
* Re: [PATCH net-next 0/2] bpf: allow extended BPF programs access skb fields
From: Daniel Borkmann @ 2015-03-13 10:40 UTC (permalink / raw)
To: Alexei Starovoitov, David S. Miller
Cc: Thomas Graf, linux-api-u79uwXL29TY76Z2rM5mHXA,
netdev-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1426213271-8363-1-git-send-email-ast-uqk4Ao+rVK5Wk0Htik3J/w@public.gmane.org>
On 03/13/2015 03:21 AM, Alexei Starovoitov wrote:
...
> Daniel,
> patch 1 includes a bit of code that does prog_realloc and branch adjustment
> to make room for new instructions. I think you'd need the same for your
> 'constant blinding' work. If indeed that would be the case, we'll make it
> into a helper function.
Yes, thanks will take care of that.
Cheers,
Daniel
^ permalink raw reply
* Re: [PATCH net-next 1/2] bpf: allow extended BPF programs access skb fields
From: Daniel Borkmann @ 2015-03-13 9:57 UTC (permalink / raw)
To: Alexei Starovoitov, David S. Miller
Cc: Thomas Graf, linux-api, netdev, linux-kernel
In-Reply-To: <1426213271-8363-2-git-send-email-ast@plumgrid.com>
On 03/13/2015 03:21 AM, Alexei Starovoitov wrote:
> introduce user accessible mirror of in-kernel 'struct sk_buff':
> struct __sk_buff {
> __u32 len;
> __u32 pkt_type;
> __u32 mark;
> __u32 ifindex;
> __u32 queue_mapping;
> };
>
> bpf programs can do:
> struct __sk_buff *ptr;
> var = ptr->pkt_type;
>
> which will be compiled to bpf assembler as:
> dst_reg = *(u32 *)(src_reg + 4) // 4 == offsetof(struct __sk_buff, pkt_type)
>
> bpf verifier will check validity of access and will convert it to:
> dst_reg = *(u8 *)(src_reg + offsetof(struct sk_buff, __pkt_type_offset))
> dst_reg &= 7
>
> since 'pkt_type' is a bitfield.
>
> When pkt_type field is moved around, goes into different structure, removed or
> its size changes, the function sk_filter_convert_ctx_access() would need to be
> updated. Just like the function convert_bpf_extensions() in case of classic bpf.
For each member, I'd also add BUILD_BUG_ON()s similarly as we have in
convert_bpf_extensions(). That way, people won't forget to adjust the
code.
General idea for this offset map looks good, imho. Well defined members
that are already exported to uapi e.g. through classic socket filters or
other socket api places could be used here.
> Signed-off-by: Alexei Starovoitov <ast@plumgrid.com>
...
> diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
> index 3fa1af8a58d7..66a82d6cd75b 100644
> --- a/include/uapi/linux/bpf.h
> +++ b/include/uapi/linux/bpf.h
> @@ -168,4 +168,12 @@ enum bpf_func_id {
> __BPF_FUNC_MAX_ID,
> };
>
> +struct __sk_buff {
> + __u32 len;
> + __u32 pkt_type;
> + __u32 mark;
> + __u32 ifindex;
> + __u32 queue_mapping;
> +};
I'd add a comment saying that fields may _only_ be safely added at
the end of the structure. Rearranging or removing members here,
naturally would break user space.
The remaining fields we export in classic BPF would be skb->hash,
skb->protocol, skb->vlan_tci, are we adding them as well to match
up functionality with classic BPF? For example, I can see hash being
useful as a key to be used with eBPF maps, etc.
...
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index e6b522496250..c22ebd36fa4b 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
...
> +/* convert load instructions that access fields of 'struct __sk_buff'
> + * into sequence of instructions that access fields of 'struct sk_buff'
> + */
> +static int convert_ctx_accesses(struct verifier_env *env)
> +{
> + struct bpf_insn *insn = env->prog->insnsi;
> + int insn_cnt = env->prog->len;
> + struct bpf_insn insn_buf[16];
> + struct bpf_prog *new_prog;
> + u32 cnt;
> + int i;
> +
> + if (!env->prog->aux->ops->convert_ctx_access)
> + return 0;
> +
> + for (i = 0; i < insn_cnt; i++, insn++) {
> + if (insn->code != (BPF_LDX | BPF_MEM | BPF_W))
> + continue;
> +
> + if (insn->imm != PTR_TO_CTX) {
> + /* clear internal mark */
> + insn->imm = 0;
> + continue;
> + }
> +
> + cnt = env->prog->aux->ops->
> + convert_ctx_access(insn->dst_reg, insn->src_reg,
> + insn->off, insn_buf);
> + if (cnt == 0 || cnt >= ARRAY_SIZE(insn_buf)) {
> + verbose("bpf verifier is misconfigured\n");
> + return -EINVAL;
> + }
> +
> + if (cnt == 1) {
> + memcpy(insn, insn_buf, sizeof(*insn));
> + continue;
> + }
> +
> + /* several new insns need to be inserted. Make room for them */
> + insn_cnt += cnt - 1;
> + new_prog = bpf_prog_realloc(env->prog,
> + bpf_prog_size(insn_cnt),
> + GFP_USER);
> + if (!new_prog)
> + return -ENOMEM;
Seems a bit expensive, do you think we could speculatively allocate a
bit more space in bpf_prog_load() when we detect that we have access
to ctx that we need to convert?
> + new_prog->len = insn_cnt;
> +
> + memmove(new_prog->insnsi + i + cnt, new_prog->insns + i + 1,
> + sizeof(*insn) * (insn_cnt - i - cnt));
> +
> + /* copy substitute insns in place of load instruction */
> + memcpy(new_prog->insnsi + i, insn_buf, sizeof(*insn) * cnt);
> +
> + /* adjust branches in the whole program */
> + adjust_branches(new_prog, i, cnt - 1);
> +
> + /* keep walking new program and skip insns we just inserted */
> + env->prog = new_prog;
> + insn = new_prog->insnsi + i + cnt - 1;
> + i += cnt - 1;
> + }
> +
> + return 0;
> +}
> +
> static void free_states(struct verifier_env *env)
> {
> struct verifier_state_list *sl, *sln;
...
> diff --git a/net/core/filter.c b/net/core/filter.c
> index 7a4eb7030dba..b5fcc7e2b608 100644
> --- a/net/core/filter.c
> +++ b/net/core/filter.c
...
> +
> +static u32 sk_filter_convert_ctx_access(int dst_reg, int src_reg, int ctx_off,
> + struct bpf_insn *insn_buf)
> +{
> + struct bpf_insn *insn = insn_buf;
> +
> + switch (ctx_off) {
> + case offsetof(struct __sk_buff, len):
> + *insn++ = BPF_LDX_MEM(BPF_W, dst_reg, src_reg,
> + offsetof(struct sk_buff, len));
> + break;
> +
> + case offsetof(struct __sk_buff, mark):
> + *insn++ = BPF_LDX_MEM(BPF_W, dst_reg, src_reg,
> + offsetof(struct sk_buff, mark));
> + break;
> +
> + case offsetof(struct __sk_buff, ifindex):
> + *insn++ = BPF_LDX_MEM(BPF_W, dst_reg, src_reg,
> + offsetof(struct sk_buff, skb_iif));
> + break;
This would only work for incoming skbs, but not outgoing ones
f.e. in case of {cls,act}_bpf.
> + case offsetof(struct __sk_buff, pkt_type):
> + *insn++ = BPF_LDX_MEM(BPF_B, dst_reg, src_reg, PKT_TYPE_OFFSET());
> + *insn++ = BPF_ALU32_IMM(BPF_AND, dst_reg, PKT_TYPE_MAX);
> +#ifdef __BIG_ENDIAN_BITFIELD
> + *insn++ = BPF_ALU32_IMM(BPF_RSH, dst_reg, 5);
> +#endif
> + break;
...
^ permalink raw reply
* [PATCH v2 7/7] eeprom: Add to MAINTAINERS for eeprom framework
From: Srinivas Kandagatla @ 2015-03-13 9:51 UTC (permalink / raw)
To: linux-arm-kernel
Cc: Maxime Ripard, Rob Herring, Pawel Moll, Kumar Gala, linux-api,
linux-kernel, devicetree, Stephen Boyd, Arnd Bergmann, broonie,
Greg Kroah-Hartman, linux-arm-msm, Srinivas Kandagatla
In-Reply-To: <1426240157-2383-1-git-send-email-srinivas.kandagatla@linaro.org>
This patch adds MAINTAINERS to eeprom framework.
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
---
MAINTAINERS | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index d66a97d..ee7ba92 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3657,6 +3657,15 @@ T: git git://git.alsa-project.org/alsa-kernel.git
S: Maintained
F: sound/usb/misc/ua101.c
+EEPROM FRAMEWORK
+M: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+M: Maxime Ripard <maxime.ripard@free-electrons.com>
+S: Maintained
+F: drivers/eeprom/
+F: Documentation/devicetree/bindings/eeprom/
+F: include/linux/eeprom-provider.h
+F: include/linux/eeprom-consumer.h
+
EXTENSIBLE FIRMWARE INTERFACE (EFI)
M: Matt Fleming <matt.fleming@intel.com>
L: linux-efi@vger.kernel.org
--
1.9.1
^ permalink raw reply related
* [PATCH v2 6/7] eeprom: qfprom: Add bindings for qfprom
From: Srinivas Kandagatla @ 2015-03-13 9:50 UTC (permalink / raw)
To: linux-arm-kernel
Cc: Maxime Ripard, Rob Herring, Pawel Moll, Kumar Gala, linux-api,
linux-kernel, devicetree, Stephen Boyd, Arnd Bergmann, broonie,
Greg Kroah-Hartman, linux-arm-msm, Srinivas Kandagatla
In-Reply-To: <1426240157-2383-1-git-send-email-srinivas.kandagatla@linaro.org>
This patch adds bindings for qfprom found in QCOM SOCs. QFPROM driver
is based on simple eeprom framework.
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
---
.../devicetree/bindings/eeprom/qfprom.txt | 23 ++++++++++++++++++++++
1 file changed, 23 insertions(+)
create mode 100644 Documentation/devicetree/bindings/eeprom/qfprom.txt
diff --git a/Documentation/devicetree/bindings/eeprom/qfprom.txt b/Documentation/devicetree/bindings/eeprom/qfprom.txt
new file mode 100644
index 0000000..d5baed6
--- /dev/null
+++ b/Documentation/devicetree/bindings/eeprom/qfprom.txt
@@ -0,0 +1,23 @@
+= Qualcomm QFPROM device tree bindings =
+
+This binding is intended to represent QFPROM which is found in most QCOM SOCs.
+
+Required properties:
+- compatible: should be "qcom,qfprom"
+- reg: Should contain registers location and length
+
+= Data cells =
+Are child nodes of qfprom, bindings of which as described in
+bindings/eeprom/eeprom.txt
+
+Example:
+
+ qfprom: qfprom@00700000 {
+ compatible = "qcom,qfprom";
+ reg = <0x00700000 0x1000>;
+ ...
+ /* Data cells */
+ tsens_calibration: calib@404 {
+ reg = <0x404 0x10>;
+ };
+ };
--
1.9.1
^ permalink raw reply related
* [PATCH v2 5/7] eeprom: qfprom: Add Qualcomm QFPROM support.
From: Srinivas Kandagatla @ 2015-03-13 9:50 UTC (permalink / raw)
To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
Cc: Maxime Ripard, Rob Herring, Pawel Moll, Kumar Gala,
linux-api-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, Stephen Boyd, Arnd Bergmann,
broonie-DgEjT+Ai2ygdnm+yROfE0A, Greg Kroah-Hartman,
linux-arm-msm-u79uwXL29TY76Z2rM5mHXA, Srinivas Kandagatla
In-Reply-To: <1426240157-2383-1-git-send-email-srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
This patch adds QFPROM support driver which is used by other drivers
like thermal sensor and cpufreq.
On MSM parts there are some efuses (called qfprom) these fuses store things like
calibration data, speed bins.. etc. Drivers like cpufreq, thermal sensors would
read out this data for configuring the driver.
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
---
drivers/eeprom/Kconfig | 9 ++++-
drivers/eeprom/Makefile | 1 +
drivers/eeprom/qfprom.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 96 insertions(+), 1 deletion(-)
create mode 100644 drivers/eeprom/qfprom.c
diff --git a/drivers/eeprom/Kconfig b/drivers/eeprom/Kconfig
index fd4fa2f..b77828b 100644
--- a/drivers/eeprom/Kconfig
+++ b/drivers/eeprom/Kconfig
@@ -23,4 +23,11 @@ config EEPROM_SUNXI_SID
This driver can also be built as a module. If so, the module
will be called eprom-sunxi-sid.
-endif
\ No newline at end of file
+config QCOM_QFPROM
+ tristate "QCOM QFPROM Support"
+ depends on EEPROM
+ select REGMAP_MMIO
+ help
+ Say y here to enable QFPROM support. The QFPROM provides access
+ functions for QFPROM data to rest of the drivers via eeprom interface.
+endif
diff --git a/drivers/eeprom/Makefile b/drivers/eeprom/Makefile
index 546a27c..9c1bb03 100644
--- a/drivers/eeprom/Makefile
+++ b/drivers/eeprom/Makefile
@@ -6,3 +6,4 @@ obj-$(CONFIG_EEPROM) += core.o
# Devices
obj-$(CONFIG_EEPROM_SUNXI_SID) += eeprom-sunxi-sid.o
+obj-$(CONFIG_QCOM_QFPROM) += qfprom.o
diff --git a/drivers/eeprom/qfprom.c b/drivers/eeprom/qfprom.c
new file mode 100644
index 0000000..7d9e5e9
--- /dev/null
+++ b/drivers/eeprom/qfprom.c
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2015 Srinivas Kandagatla <srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/eeprom-provider.h>
+
+static struct regmap_config qfprom_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 8,
+ .reg_stride = 1,
+};
+
+static struct eeprom_config econfig = {
+ .stride = 1,
+ .name = "qfprom",
+};
+
+static int qfprom_remove(struct platform_device *pdev)
+{
+ struct eeprom_device *eeprom = platform_get_drvdata(pdev);
+
+ return eeprom_unregister(eeprom);
+}
+
+static int qfprom_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ void __iomem *base;
+ struct device *dev = &pdev->dev;
+ struct eeprom_device *eeprom;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ qfprom_regmap_config.max_register = resource_size(res) - 1;
+
+ econfig.regmap = devm_regmap_init_mmio(dev, base,
+ &qfprom_regmap_config);
+ if (IS_ERR(econfig.regmap)) {
+ dev_err(dev, "regmap init failed\n");
+ return PTR_ERR(econfig.regmap);
+ }
+ econfig.owner = THIS_MODULE;
+ econfig.dev = dev;
+ econfig.size = resource_size(res) - 1;
+ eeprom = eeprom_register(&econfig);
+ if (IS_ERR(eeprom))
+ return PTR_ERR(eeprom);
+
+ platform_set_drvdata(pdev, eeprom);
+ return 0;
+}
+
+static const struct of_device_id qfprom_of_match[] = {
+ { .compatible = "qcom,qfprom"},
+ {/* sentinel */},
+};
+MODULE_DEVICE_TABLE(of, qfprom_of_match);
+
+static struct platform_driver qfprom_driver = {
+ .probe = qfprom_probe,
+ .remove = qfprom_remove,
+ .driver = {
+ .name = "qcom,qfprom",
+ .of_match_table = qfprom_of_match,
+ },
+};
+module_platform_driver(qfprom_driver);
+MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>");
+MODULE_DESCRIPTION("Qualcomm QFPROM driver");
+MODULE_LICENSE("GPL v2");
--
1.9.1
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v2 4/7] eeprom: sunxi: Move the SID driver to the eeprom framework
From: Srinivas Kandagatla @ 2015-03-13 9:50 UTC (permalink / raw)
To: linux-arm-kernel
Cc: Maxime Ripard, Rob Herring, Pawel Moll, Kumar Gala, linux-api,
linux-kernel, devicetree, Stephen Boyd, Arnd Bergmann, broonie,
Greg Kroah-Hartman, linux-arm-msm, Srinivas Kandagatla
In-Reply-To: <1426240157-2383-1-git-send-email-srinivas.kandagatla@linaro.org>
From: Maxime Ripard <maxime.ripard@free-electrons.com>
Now that we have the EEPROM framework, we can consolidate the common driver
code. Move the driver to the framework, and hopefully, it will fix the sysfs
file creation race.
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
[srinivas.kandagatla: Moved to regmap based EEPROM framework]
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
---
Documentation/ABI/testing/sysfs-driver-sunxi-sid | 22 ---
.../bindings/eeprom/allwinner,sunxi-sid.txt | 21 +++
.../bindings/misc/allwinner,sunxi-sid.txt | 17 ---
drivers/eeprom/Kconfig | 15 ++
drivers/eeprom/Makefile | 3 +
drivers/eeprom/eeprom-sunxi-sid.c | 136 ++++++++++++++++++
drivers/misc/eeprom/Kconfig | 13 --
drivers/misc/eeprom/Makefile | 1 -
drivers/misc/eeprom/sunxi_sid.c | 156 ---------------------
9 files changed, 175 insertions(+), 209 deletions(-)
delete mode 100644 Documentation/ABI/testing/sysfs-driver-sunxi-sid
create mode 100644 Documentation/devicetree/bindings/eeprom/allwinner,sunxi-sid.txt
delete mode 100644 Documentation/devicetree/bindings/misc/allwinner,sunxi-sid.txt
create mode 100644 drivers/eeprom/eeprom-sunxi-sid.c
delete mode 100644 drivers/misc/eeprom/sunxi_sid.c
diff --git a/Documentation/ABI/testing/sysfs-driver-sunxi-sid b/Documentation/ABI/testing/sysfs-driver-sunxi-sid
deleted file mode 100644
index ffb9536..0000000
--- a/Documentation/ABI/testing/sysfs-driver-sunxi-sid
+++ /dev/null
@@ -1,22 +0,0 @@
-What: /sys/devices/*/<our-device>/eeprom
-Date: August 2013
-Contact: Oliver Schinagl <oliver@schinagl.nl>
-Description: read-only access to the SID (Security-ID) on current
- A-series SoC's from Allwinner. Currently supports A10, A10s, A13
- and A20 CPU's. The earlier A1x series of SoCs exports 16 bytes,
- whereas the newer A20 SoC exposes 512 bytes split into sections.
- Besides the 16 bytes of SID, there's also an SJTAG area,
- HDMI-HDCP key and some custom keys. Below a quick overview, for
- details see the user manual:
- 0x000 128 bit root-key (sun[457]i)
- 0x010 128 bit boot-key (sun7i)
- 0x020 64 bit security-jtag-key (sun7i)
- 0x028 16 bit key configuration (sun7i)
- 0x02b 16 bit custom-vendor-key (sun7i)
- 0x02c 320 bit low general key (sun7i)
- 0x040 32 bit read-control access (sun7i)
- 0x064 224 bit low general key (sun7i)
- 0x080 2304 bit HDCP-key (sun7i)
- 0x1a0 768 bit high general key (sun7i)
-Users: any user space application which wants to read the SID on
- Allwinner's A-series of CPU's.
diff --git a/Documentation/devicetree/bindings/eeprom/allwinner,sunxi-sid.txt b/Documentation/devicetree/bindings/eeprom/allwinner,sunxi-sid.txt
new file mode 100644
index 0000000..cceaaf6
--- /dev/null
+++ b/Documentation/devicetree/bindings/eeprom/allwinner,sunxi-sid.txt
@@ -0,0 +1,21 @@
+Allwinner sunxi-sid
+
+Required properties:
+- compatible: "allwinner,sun4i-a10-sid" or "allwinner,sun7i-a20-sid"
+- reg: Should contain registers location and length
+
+= Data cells =
+Are child nodes of qfprom, bindings of which as described in
+bindings/eeprom/eeprom.txt
+
+Example for sun4i:
+ sid@01c23800 {
+ compatible = "allwinner,sun4i-a10-sid";
+ reg = <0x01c23800 0x10>
+ };
+
+Example for sun7i:
+ sid@01c23800 {
+ compatible = "allwinner,sun7i-a20-sid";
+ reg = <0x01c23800 0x200>
+ };
diff --git a/Documentation/devicetree/bindings/misc/allwinner,sunxi-sid.txt b/Documentation/devicetree/bindings/misc/allwinner,sunxi-sid.txt
deleted file mode 100644
index fabdf64..0000000
--- a/Documentation/devicetree/bindings/misc/allwinner,sunxi-sid.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-Allwinner sunxi-sid
-
-Required properties:
-- compatible: "allwinner,sun4i-a10-sid" or "allwinner,sun7i-a20-sid"
-- reg: Should contain registers location and length
-
-Example for sun4i:
- sid@01c23800 {
- compatible = "allwinner,sun4i-a10-sid";
- reg = <0x01c23800 0x10>
- };
-
-Example for sun7i:
- sid@01c23800 {
- compatible = "allwinner,sun7i-a20-sid";
- reg = <0x01c23800 0x200>
- };
diff --git a/drivers/eeprom/Kconfig b/drivers/eeprom/Kconfig
index 21e1847..fd4fa2f 100644
--- a/drivers/eeprom/Kconfig
+++ b/drivers/eeprom/Kconfig
@@ -9,3 +9,18 @@ menuconfig EEPROM
from both the Linux Kernel and the userspace.
If unsure, say no.
+
+if EEPROM
+
+config EEPROM_SUNXI_SID
+ tristate "Allwinner SoCs SID support"
+ depends on ARCH_SUNXI
+ select REGMAP_MMIO
+ help
+ This is a driver for the 'security ID' available on various Allwinner
+ devices.
+
+ This driver can also be built as a module. If so, the module
+ will be called eprom-sunxi-sid.
+
+endif
\ No newline at end of file
diff --git a/drivers/eeprom/Makefile b/drivers/eeprom/Makefile
index 250c95a..546a27c 100644
--- a/drivers/eeprom/Makefile
+++ b/drivers/eeprom/Makefile
@@ -3,3 +3,6 @@
#
obj-$(CONFIG_EEPROM) += core.o
+
+# Devices
+obj-$(CONFIG_EEPROM_SUNXI_SID) += eeprom-sunxi-sid.o
diff --git a/drivers/eeprom/eeprom-sunxi-sid.c b/drivers/eeprom/eeprom-sunxi-sid.c
new file mode 100644
index 0000000..58f7d24
--- /dev/null
+++ b/drivers/eeprom/eeprom-sunxi-sid.c
@@ -0,0 +1,136 @@
+/*
+ * Allwinner sunXi SoCs Security ID support.
+ *
+ * Copyright (c) 2013 Oliver Schinagl <oliver@schinagl.nl>
+ * Copyright (C) 2014 Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/eeprom-provider.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+struct eeprom_sid {
+ void __iomem *membase;
+ struct eeprom_device *eeprom;
+};
+
+static struct eeprom_config econfig = {
+ .stride = 1,
+ .name = "sunix-sid",
+};
+
+/* We read the entire key, due to a 32 bit read alignment requirement. Since we
+ * want to return the requested byte, this results in somewhat slower code and
+ * uses 4 times more reads as needed but keeps code simpler. Since the SID is
+ * only very rarely probed, this is not really an issue.
+ */
+static int sunxi_sid_reg_read(void *context,
+ unsigned int offset, unsigned int *val)
+{
+ struct eeprom_sid *sid = context;
+ u32 sid_key;
+
+ sid_key = ioread32be(sid->membase + round_down(offset, 4));
+ sid_key >>= (offset % 4) * 8;
+
+ *val = sid_key;
+
+ return 0;
+}
+
+static bool sunxi_sid_writeable_reg(struct device *dev, unsigned int reg)
+{
+ return false;
+}
+
+static const struct of_device_id sunxi_sid_of_match[] = {
+ { .compatible = "allwinner,sun4i-a10-sid", .data = (void *)16},
+ { .compatible = "allwinner,sun7i-a20-sid", .data = (void *)512},
+ {/* sentinel */},
+};
+MODULE_DEVICE_TABLE(of, sunxi_sid_of_match);
+
+static struct regmap_config sunxi_sid_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 8,
+ .reg_stride = 1,
+ .reg_read = sunxi_sid_reg_read,
+ .writeable_reg = sunxi_sid_writeable_reg,
+};
+
+static int sunxi_sid_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *device;
+ struct eeprom_sid *sid;
+ struct resource *res;
+ struct eeprom_device *eeprom;
+ struct device *dev = &pdev->dev;
+
+ sid = devm_kzalloc(dev, sizeof(*sid), GFP_KERNEL);
+ if (!sid)
+ return -ENOMEM;
+
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ sid->membase = devm_ioremap_resource(dev, res);
+ if (IS_ERR(sid->membase))
+ return PTR_ERR(sid->membase);
+
+ device = of_match_device(sunxi_sid_of_match, dev);
+ if (!device)
+ return -ENODEV;
+
+ sunxi_sid_regmap_config.max_register = (unsigned int)device->data - 1;
+
+ econfig.regmap = devm_regmap_init(dev, NULL,
+ sid, &sunxi_sid_regmap_config);
+ if (IS_ERR(econfig.regmap))
+ return PTR_ERR(econfig.regmap);
+
+ econfig.dev = dev;
+ econfig.owner = THIS_MODULE;
+ econfig.size = sunxi_sid_regmap_config.max_register;
+
+ eeprom = eeprom_register(&econfig);
+ if (IS_ERR(eeprom))
+ return PTR_ERR(eeprom);
+
+ platform_set_drvdata(pdev, eeprom);
+
+ return 0;
+}
+
+static int sunxi_sid_remove(struct platform_device *pdev)
+{
+ struct eeprom_device *eeprom = platform_get_drvdata(pdev);
+
+ return eeprom_unregister(eeprom);
+}
+
+static struct platform_driver sunxi_sid_driver = {
+ .probe = sunxi_sid_probe,
+ .remove = sunxi_sid_remove,
+ .driver = {
+ .name = "eeprom-sunxi-sid",
+ .of_match_table = sunxi_sid_of_match,
+ },
+};
+module_platform_driver(sunxi_sid_driver);
+
+MODULE_AUTHOR("Oliver Schinagl <oliver@schinagl.nl>");
+MODULE_DESCRIPTION("Allwinner sunxi security id driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig
index 9536852f..04f2e1f 100644
--- a/drivers/misc/eeprom/Kconfig
+++ b/drivers/misc/eeprom/Kconfig
@@ -96,17 +96,4 @@ config EEPROM_DIGSY_MTC_CFG
If unsure, say N.
-config EEPROM_SUNXI_SID
- tristate "Allwinner sunxi security ID support"
- depends on ARCH_SUNXI && SYSFS
- help
- This is a driver for the 'security ID' available on various Allwinner
- devices.
-
- Due to the potential risks involved with changing e-fuses,
- this driver is read-only.
-
- This driver can also be built as a module. If so, the module
- will be called sunxi_sid.
-
endmenu
diff --git a/drivers/misc/eeprom/Makefile b/drivers/misc/eeprom/Makefile
index 9507aec..fc1e81d 100644
--- a/drivers/misc/eeprom/Makefile
+++ b/drivers/misc/eeprom/Makefile
@@ -4,5 +4,4 @@ obj-$(CONFIG_EEPROM_LEGACY) += eeprom.o
obj-$(CONFIG_EEPROM_MAX6875) += max6875.o
obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o
obj-$(CONFIG_EEPROM_93XX46) += eeprom_93xx46.o
-obj-$(CONFIG_EEPROM_SUNXI_SID) += sunxi_sid.o
obj-$(CONFIG_EEPROM_DIGSY_MTC_CFG) += digsy_mtc_eeprom.o
diff --git a/drivers/misc/eeprom/sunxi_sid.c b/drivers/misc/eeprom/sunxi_sid.c
deleted file mode 100644
index 8385177..0000000
--- a/drivers/misc/eeprom/sunxi_sid.c
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright (c) 2013 Oliver Schinagl <oliver@schinagl.nl>
- * http://www.linux-sunxi.org
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * This driver exposes the Allwinner security ID, efuses exported in byte-
- * sized chunks.
- */
-
-#include <linux/compiler.h>
-#include <linux/device.h>
-#include <linux/err.h>
-#include <linux/export.h>
-#include <linux/fs.h>
-#include <linux/io.h>
-#include <linux/kernel.h>
-#include <linux/kobject.h>
-#include <linux/module.h>
-#include <linux/of_device.h>
-#include <linux/platform_device.h>
-#include <linux/random.h>
-#include <linux/slab.h>
-#include <linux/stat.h>
-#include <linux/sysfs.h>
-#include <linux/types.h>
-
-#define DRV_NAME "sunxi-sid"
-
-struct sunxi_sid_data {
- void __iomem *reg_base;
- unsigned int keysize;
-};
-
-/* We read the entire key, due to a 32 bit read alignment requirement. Since we
- * want to return the requested byte, this results in somewhat slower code and
- * uses 4 times more reads as needed but keeps code simpler. Since the SID is
- * only very rarely probed, this is not really an issue.
- */
-static u8 sunxi_sid_read_byte(const struct sunxi_sid_data *sid_data,
- const unsigned int offset)
-{
- u32 sid_key;
-
- if (offset >= sid_data->keysize)
- return 0;
-
- sid_key = ioread32be(sid_data->reg_base + round_down(offset, 4));
- sid_key >>= (offset % 4) * 8;
-
- return sid_key; /* Only return the last byte */
-}
-
-static ssize_t sid_read(struct file *fd, struct kobject *kobj,
- struct bin_attribute *attr, char *buf,
- loff_t pos, size_t size)
-{
- struct platform_device *pdev;
- struct sunxi_sid_data *sid_data;
- int i;
-
- pdev = to_platform_device(kobj_to_dev(kobj));
- sid_data = platform_get_drvdata(pdev);
-
- if (pos < 0 || pos >= sid_data->keysize)
- return 0;
- if (size > sid_data->keysize - pos)
- size = sid_data->keysize - pos;
-
- for (i = 0; i < size; i++)
- buf[i] = sunxi_sid_read_byte(sid_data, pos + i);
-
- return i;
-}
-
-static struct bin_attribute sid_bin_attr = {
- .attr = { .name = "eeprom", .mode = S_IRUGO, },
- .read = sid_read,
-};
-
-static int sunxi_sid_remove(struct platform_device *pdev)
-{
- device_remove_bin_file(&pdev->dev, &sid_bin_attr);
- dev_dbg(&pdev->dev, "driver unloaded\n");
-
- return 0;
-}
-
-static const struct of_device_id sunxi_sid_of_match[] = {
- { .compatible = "allwinner,sun4i-a10-sid", .data = (void *)16},
- { .compatible = "allwinner,sun7i-a20-sid", .data = (void *)512},
- {/* sentinel */},
-};
-MODULE_DEVICE_TABLE(of, sunxi_sid_of_match);
-
-static int sunxi_sid_probe(struct platform_device *pdev)
-{
- struct sunxi_sid_data *sid_data;
- struct resource *res;
- const struct of_device_id *of_dev_id;
- u8 *entropy;
- unsigned int i;
-
- sid_data = devm_kzalloc(&pdev->dev, sizeof(struct sunxi_sid_data),
- GFP_KERNEL);
- if (!sid_data)
- return -ENOMEM;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- sid_data->reg_base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(sid_data->reg_base))
- return PTR_ERR(sid_data->reg_base);
-
- of_dev_id = of_match_device(sunxi_sid_of_match, &pdev->dev);
- if (!of_dev_id)
- return -ENODEV;
- sid_data->keysize = (int)of_dev_id->data;
-
- platform_set_drvdata(pdev, sid_data);
-
- sid_bin_attr.size = sid_data->keysize;
- if (device_create_bin_file(&pdev->dev, &sid_bin_attr))
- return -ENODEV;
-
- entropy = kzalloc(sizeof(u8) * sid_data->keysize, GFP_KERNEL);
- for (i = 0; i < sid_data->keysize; i++)
- entropy[i] = sunxi_sid_read_byte(sid_data, i);
- add_device_randomness(entropy, sid_data->keysize);
- kfree(entropy);
-
- dev_dbg(&pdev->dev, "loaded\n");
-
- return 0;
-}
-
-static struct platform_driver sunxi_sid_driver = {
- .probe = sunxi_sid_probe,
- .remove = sunxi_sid_remove,
- .driver = {
- .name = DRV_NAME,
- .of_match_table = sunxi_sid_of_match,
- },
-};
-module_platform_driver(sunxi_sid_driver);
-
-MODULE_AUTHOR("Oliver Schinagl <oliver@schinagl.nl>");
-MODULE_DESCRIPTION("Allwinner sunxi security id driver");
-MODULE_LICENSE("GPL");
--
1.9.1
^ permalink raw reply related
* [PATCH v2 3/7] eeprom: Add bindings for simple eeprom framework
From: Srinivas Kandagatla @ 2015-03-13 9:50 UTC (permalink / raw)
To: linux-arm-kernel
Cc: Maxime Ripard, Rob Herring, Pawel Moll, Kumar Gala, linux-api,
linux-kernel, devicetree, Stephen Boyd, Arnd Bergmann, broonie,
Greg Kroah-Hartman, linux-arm-msm, Srinivas Kandagatla
In-Reply-To: <1426240157-2383-1-git-send-email-srinivas.kandagatla@linaro.org>
This patch adds bindings for simple eeprom framework which allows eeprom
consumers to talk to eeprom providers to get access to eeprom cell data.
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
[Maxime Ripard: intial version of eeprom framework]
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
---
.../devicetree/bindings/eeprom/eeprom.txt | 70 ++++++++++++++++++++++
1 file changed, 70 insertions(+)
create mode 100644 Documentation/devicetree/bindings/eeprom/eeprom.txt
diff --git a/Documentation/devicetree/bindings/eeprom/eeprom.txt b/Documentation/devicetree/bindings/eeprom/eeprom.txt
new file mode 100644
index 0000000..8348d18
--- /dev/null
+++ b/Documentation/devicetree/bindings/eeprom/eeprom.txt
@@ -0,0 +1,70 @@
+= EEPROM Data Device Tree Bindings =
+
+This binding is intended to represent the location of hardware
+configuration data stored in EEPROMs.
+
+On a significant proportion of boards, the manufacturer has stored
+some data on an EEPROM-like device, for the OS to be able to retrieve
+these information and act upon it. Obviously, the OS has to know
+about where to retrieve these data from, and where they are stored on
+the storage device.
+
+This document is here to document this.
+
+= Data providers =
+Contains bindings specific to provider drivers and data cells as children
+to this node.
+
+= Data cells =
+These are the child nodes of the provider which contain data cell
+information like offset and size in eeprom provider.
+
+Required properties:
+reg: specifies the offset in byte within that storage device, and the length
+ in bytes of the data we care about.
+ There could be more then one offset-length pairs in this property.
+
+Optional properties:
+As required by specific data parsers/interpreters.
+
+For example:
+
+ /* Provider */
+ qfprom: qfprom@00700000 {
+ compatible = "qcom,qfprom";
+ reg = <0x00700000 0x1000>;
+ ...
+
+ /* Data cells */
+ tsens_calibration: calib@404 {
+ reg = <0x404 0x10>;
+ };
+
+ serial_number: sn {
+ reg = <0x104 0x4>, <0x204 0x4>, <0x30c 0x4>;
+
+ };
+ ...
+ };
+
+= Data consumers =
+Are device nodes which consume eeprom data cells.
+
+Required properties:
+
+eeproms: List of phandle and data cell the device might be interested in.
+
+Optional properties:
+
+eeprom-names: List of data cell name strings sorted in the same order
+ as the eeproms property. Consumers drivers will use
+ eeprom-names to differentiate between multiple cells,
+ and hence being able to know what these cells are for.
+
+For example:
+
+ tsens {
+ ...
+ eeproms = <&tsens_calibration>;
+ eeprom-names = "calibration";
+ };
--
1.9.1
^ permalink raw reply related
* [PATCH v2 2/7] eeprom: Add a simple EEPROM framework for eeprom consumers
From: Srinivas Kandagatla @ 2015-03-13 9:50 UTC (permalink / raw)
To: linux-arm-kernel
Cc: Maxime Ripard, Rob Herring, Pawel Moll, Kumar Gala, linux-api,
linux-kernel, devicetree, Stephen Boyd, Arnd Bergmann, broonie,
Greg Kroah-Hartman, linux-arm-msm, Srinivas Kandagatla
In-Reply-To: <1426240157-2383-1-git-send-email-srinivas.kandagatla@linaro.org>
This patch adds just consumers part of the framework just to enable easy
review.
Up until now, EEPROM drivers were stored in drivers/misc, where they all had to
duplicate pretty much the same code to register a sysfs file, allow in-kernel
users to access the content of the devices they were driving, etc.
This was also a problem as far as other in-kernel users were involved, since
the solutions used were pretty much different from on driver to another, there
was a rather big abstraction leak.
This introduction of this framework aims at solving this. It also introduces DT
representation for consumer devices to go get the data they require (MAC
Addresses, SoC/Revision ID, part numbers, and so on) from the EEPROMs.
Having regmap interface to this framework would give much better
abstraction for eeproms on different buses.
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
[Maxime Ripard: intial version of the framework]
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
---
drivers/eeprom/core.c | 304 ++++++++++++++++++++++++++++++++++++++++
include/linux/eeprom-consumer.h | 67 +++++++++
2 files changed, 371 insertions(+)
create mode 100644 include/linux/eeprom-consumer.h
diff --git a/drivers/eeprom/core.c b/drivers/eeprom/core.c
index a9839de..b87e136 100644
--- a/drivers/eeprom/core.c
+++ b/drivers/eeprom/core.c
@@ -16,6 +16,7 @@
#include <linux/device.h>
#include <linux/eeprom-provider.h>
+#include <linux/eeprom-consumer.h>
#include <linux/export.h>
#include <linux/fs.h>
#include <linux/idr.h>
@@ -36,6 +37,13 @@ struct eeprom_device {
int users;
};
+struct eeprom_cell {
+ struct eeprom_device *eeprom;
+ int nblocks;
+ int size;
+ struct eeprom_block blocks[0];
+};
+
static DEFINE_MUTEX(eeprom_mutex);
static DEFINE_IDA(eeprom_ida);
@@ -121,6 +129,37 @@ static struct class eeprom_class = {
.dev_release = eeprom_release,
};
+static int of_eeprom_match(struct device *dev, const void *eeprom_np)
+{
+ return dev->of_node == eeprom_np;
+}
+
+static struct eeprom_device *of_eeprom_find(struct device_node *eeprom_np)
+{
+ struct device *d;
+
+ if (!eeprom_np)
+ return NULL;
+
+ d = class_find_device(&eeprom_class, NULL, eeprom_np, of_eeprom_match);
+
+ return d ? to_eeprom(d) : NULL;
+}
+
+static int eeprom_match(struct device *dev, const void *data)
+{
+ return !strcmp(dev_name(dev), (const char *)data);
+}
+
+static struct eeprom_device *eeprom_find(const char *name)
+{
+ struct device *d;
+
+ d = class_find_device(&eeprom_class, NULL, (void *)name, eeprom_match);
+
+ return d ? to_eeprom(d) : NULL;
+}
+
/**
* eeprom_register(): Register a eeprom device for given eeprom.
* Also creates an binary entry in /sys/class/eeprom/name-id/eeprom
@@ -194,6 +233,271 @@ int eeprom_unregister(struct eeprom_device *eeprom)
}
EXPORT_SYMBOL_GPL(eeprom_unregister);
+static int eeprom_cell_sanity_check(struct eeprom_cell *cell)
+{
+ struct eeprom_device *eeprom = cell->eeprom;
+ int i;
+
+ /* byte aligned, no need to check for stride sanity */
+ if (eeprom->stride == 1)
+ return 0;
+
+ for (i = 0; i < cell->nblocks; i++) {
+ if (!IS_ALIGNED(cell->blocks[i].offset, eeprom->stride) ||
+ !IS_ALIGNED(cell->blocks[i].count, eeprom->stride)) {
+ dev_err(&eeprom->dev,
+ "cell unaligned to eeprom stride %d\n",
+ eeprom->stride);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static struct eeprom_cell *__eeprom_cell_get(struct device_node *cell_np,
+ const char *ename,
+ struct eeprom_block *blocks,
+ int nblocks)
+{
+ struct eeprom_cell *cell;
+ struct eeprom_device *eeprom = NULL;
+ struct property *prop;
+ const __be32 *vp;
+ u32 pv;
+ int i, rval;
+
+ mutex_lock(&eeprom_mutex);
+
+ eeprom = cell_np ? of_eeprom_find(cell_np->parent) : eeprom_find(ename);
+ if (!eeprom) {
+ mutex_unlock(&eeprom_mutex);
+ return ERR_PTR(-EPROBE_DEFER);
+ }
+
+ eeprom->users++;
+ mutex_unlock(&eeprom_mutex);
+
+ if (!try_module_get(eeprom->owner)) {
+ dev_err(&eeprom->dev,
+ "could not increase module refcount for cell %s\n",
+ ename);
+ rval = -EINVAL;
+ goto err_mod;
+ }
+
+ if (cell_np)
+ nblocks = of_property_count_u32_elems(cell_np, "reg") / 2;
+
+ cell = kzalloc(sizeof(*cell) + nblocks * sizeof(*blocks), GFP_KERNEL);
+ if (!cell) {
+ rval = -ENOMEM;
+ goto err_mem;
+ }
+
+ cell->nblocks = nblocks;
+ cell->eeprom = eeprom;
+ cell->size = 0;
+ i = 0;
+
+ if (cell_np) {
+ of_property_for_each_u32(cell_np, "reg", prop, vp, pv) {
+ cell->blocks[i].offset = pv;
+ vp = of_prop_next_u32(prop, vp, &pv);
+ cell->blocks[i].count = pv;
+ cell->size += pv;
+ i++;
+ }
+ } else {
+ memcpy(cell->blocks, blocks, nblocks * sizeof(*blocks));
+ for (; i < nblocks; i++)
+ cell->size += blocks[i].count;
+ }
+
+ if (IS_ERR_VALUE(eeprom_cell_sanity_check(cell))) {
+ rval = -EINVAL;
+ goto err_sanity;
+ }
+
+ return cell;
+
+err_sanity:
+ kfree(cell);
+
+err_mem:
+ module_put(eeprom->owner);
+
+err_mod:
+ mutex_lock(&eeprom_mutex);
+ eeprom->users--;
+ mutex_unlock(&eeprom_mutex);
+
+ return ERR_PTR(rval);
+
+}
+
+/**
+ * eeprom_cell_get(): Get eeprom cell of device form a given eeprom name
+ * and blocks.
+ *
+ * @ename: eeprom device name that needs to be looked-up.
+ * @blocks: eeprom blocks containing offset and length information.
+ * @nblocks: number of eeprom blocks.
+ *
+ * The return value will be an ERR_PTR() on error or a valid pointer
+ * to a struct eeprom_cell. The eeprom_cell will be freed by the
+ * eeprom_cell_put().
+ */
+struct eeprom_cell *eeprom_cell_get(const char *ename,
+ struct eeprom_block *blocks, int nblocks)
+{
+ return __eeprom_cell_get(NULL, ename, blocks, nblocks);
+}
+EXPORT_SYMBOL_GPL(eeprom_cell_get);
+
+/**
+ * of_eeprom_cell_get(): Get eeprom cell of device form a given index
+ *
+ * @dev: Device that will be interacted with
+ * @index: eeprom index in eeproms property.
+ *
+ * The return value will be an ERR_PTR() on error or a valid pointer
+ * to a struct eeprom_cell. The eeprom_cell will be freed by the
+ * eeprom_cell_put().
+ */
+struct eeprom_cell *of_eeprom_cell_get(struct device *dev, int index)
+{
+ struct device_node *cell_np;
+
+ if (!dev || !dev->of_node)
+ return ERR_PTR(-EINVAL);
+
+ cell_np = of_parse_phandle(dev->of_node, "eeproms", index);
+ if (!cell_np)
+ return ERR_PTR(-EPROBE_DEFER);
+
+ return __eeprom_cell_get(cell_np, NULL, NULL, 0);
+}
+EXPORT_SYMBOL_GPL(of_eeprom_cell_get);
+
+/**
+ * of_eeprom_cell_get_byname(): Get eeprom cell of device form a given name
+ *
+ * @dev: Device that will be interacted with
+ * @name: eeprom name in eeprom-names property.
+ *
+ * The return value will be an ERR_PTR() on error or a valid pointer
+ * to a struct eeprom_cell. The eeprom_cell will be freed by the
+ * eeprom_cell_put().
+ */
+struct eeprom_cell *of_eeprom_cell_get_byname(struct device *dev,
+ const char *id)
+{
+ int index = 0;
+
+ if (!dev || !dev->of_node)
+ return ERR_PTR(-EINVAL);
+
+ if (id)
+ index = of_property_match_string(dev->of_node,
+ "eeprom-names",
+ id);
+ return of_eeprom_cell_get(dev, index);
+
+}
+EXPORT_SYMBOL_GPL(of_eeprom_cell_get_byname);
+
+/**
+ * eeprom_cell_put(): Release previously allocated eeprom cell.
+ *
+ * @cell: Previously allocated eeprom cell by eeprom_cell_get()
+ * or of_eeprom_cell_get() or of_eeprom_cell_get_byname().
+ */
+void eeprom_cell_put(struct eeprom_cell *cell)
+{
+ struct eeprom_device *eeprom = cell->eeprom;
+
+ mutex_lock(&eeprom_mutex);
+ eeprom->users--;
+ mutex_unlock(&eeprom_mutex);
+ module_put(eeprom->owner);
+ kfree(cell);
+}
+EXPORT_SYMBOL_GPL(eeprom_cell_put);
+
+/**
+ * eeprom_cell_read(): Read a given eeprom cell
+ *
+ * @cell: eeprom cell to be read.
+ * @len: pointer to length of cell which will be populated on successful read.
+ *
+ * The return value will be an ERR_PTR() on error or a valid pointer
+ * to a char * bufffer. The buffer should be freed by the consumer with a
+ * kfree().
+ */
+char *eeprom_cell_read(struct eeprom_cell *cell, ssize_t *len)
+{
+ struct eeprom_device *eeprom = cell->eeprom;
+ char *buf;
+ int rc, i, offset = 0;
+
+ if (!eeprom || !eeprom->regmap)
+ return ERR_PTR(-EINVAL);
+
+ buf = kzalloc(cell->size, GFP_KERNEL);
+ if (!buf)
+ return ERR_PTR(-ENOMEM);
+
+ for (i = 0; i < cell->nblocks; i++) {
+ rc = regmap_bulk_read(eeprom->regmap, cell->blocks[i].offset,
+ buf + offset,
+ cell->blocks[i].count);
+
+ if (IS_ERR_VALUE(rc)) {
+ kfree(buf);
+ return ERR_PTR(rc);
+ }
+ offset += cell->blocks[i].count;
+ }
+
+ *len = cell->size;
+
+ return buf;
+}
+EXPORT_SYMBOL_GPL(eeprom_cell_read);
+
+/**
+ * eeprom_cell_write(): Write to a given eeprom cell
+ *
+ * @cell: eeprom cell to be written.
+ * @buf: Buffer to be written.
+ * @len: length of buffer to be written to eeprom cell.
+ *
+ * The return value will be an non zero on error or a zero on successful write.
+ */
+int eeprom_cell_write(struct eeprom_cell *cell, const char *buf, ssize_t len)
+{
+ struct eeprom_device *eeprom = cell->eeprom;
+ int i, rc, offset = 0;
+
+ if (!eeprom || !eeprom->regmap || len != cell->size)
+ return -EINVAL;
+
+ for (i = 0; i < cell->nblocks; i++) {
+ rc = regmap_bulk_write(eeprom->regmap, cell->blocks[i].offset,
+ buf + offset,
+ cell->blocks[i].count);
+
+ if (IS_ERR_VALUE(rc))
+ return rc;
+
+ offset += cell->blocks[i].count;
+ }
+
+ return len;
+}
+EXPORT_SYMBOL_GPL(eeprom_cell_write);
+
static int eeprom_init(void)
{
return class_register(&eeprom_class);
diff --git a/include/linux/eeprom-consumer.h b/include/linux/eeprom-consumer.h
new file mode 100644
index 0000000..6d9d075
--- /dev/null
+++ b/include/linux/eeprom-consumer.h
@@ -0,0 +1,67 @@
+/*
+ * EEPROM framework consumer.
+ *
+ * Copyright (C) 2015 Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+ * Copyright (C) 2013 Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef _LINUX_EEPROM_CONSUMER_H
+#define _LINUX_EEPROM_CONSUMER_H
+
+struct eeprom_cell;
+
+struct eeprom_block {
+ loff_t offset;
+ size_t count;
+};
+#if IS_ENABLED(CONFIG_EEPROM)
+struct eeprom_cell *eeprom_cell_get(const char *ename,
+ struct eeprom_block *blocks, int nblocks);
+void eeprom_cell_put(struct eeprom_cell *cell);
+char *eeprom_cell_read(struct eeprom_cell *cell, ssize_t *len);
+int eeprom_cell_write(struct eeprom_cell *cell, const char *buf, ssize_t len);
+#else
+
+static inline struct eeprom_cell *eeprom_cell_get(const char *ename,
+ struct eeprom_block *blocks, int nblocks)
+{
+ return NULL;
+}
+
+static inline void eeprom_cell_put(struct eeprom_cell *cell)
+{
+}
+
+static inline char *eeprom_cell_read(struct eeprom_cell *cell, ssize_t *len)
+{
+ return NULL;
+}
+
+static inline int eeprom_cell_write(struct eeprom_cell *cell,
+ const char *buf, ssize_t len)
+{
+ return -ENOSYS;
+}
+#endif /* CONFIG_EEPROM */
+
+#if IS_ENABLED(CONFIG_EEPROM) && IS_ENABLED(CONFIG_OF)
+struct eeprom_cell *of_eeprom_cell_get(struct device *dev, int index);
+struct eeprom_cell *of_eeprom_cell_get_byname(struct device *dev,
+ const char *name);
+#else
+static inline struct eeprom_cell *of_eeprom_cell_get(
+ struct device *dev, int index)
+{
+ return NULL;
+}
+static inline struct eeprom_cell *of_eeprom_cell_get_byname(struct device *dev,
+ const char *name)
+{
+ return NULL;
+}
+#endif
+#endif /* ifndef _LINUX_EEPROM_CONSUMER_H */
--
1.9.1
^ permalink raw reply related
* [PATCH v2 1/7] eeprom: Add a simple EEPROM framework for eeprom providers
From: Srinivas Kandagatla @ 2015-03-13 9:50 UTC (permalink / raw)
To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
Cc: Maxime Ripard, Rob Herring, Pawel Moll, Kumar Gala,
linux-api-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, Stephen Boyd, Arnd Bergmann,
broonie-DgEjT+Ai2ygdnm+yROfE0A, Greg Kroah-Hartman,
linux-arm-msm-u79uwXL29TY76Z2rM5mHXA, Srinivas Kandagatla
In-Reply-To: <1426240157-2383-1-git-send-email-srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
This patch adds just providers part of the framework just to enable easy
review.
Up until now, EEPROM drivers were stored in drivers/misc, where they all had to
duplicate pretty much the same code to register a sysfs file, allow in-kernel
users to access the content of the devices they were driving, etc.
This was also a problem as far as other in-kernel users were involved, since
the solutions used were pretty much different from on driver to another, there
was a rather big abstraction leak.
This introduction of this framework aims at solving this. It also introduces DT
representation for consumer devices to go get the data they require (MAC
Addresses, SoC/Revision ID, part numbers, and so on) from the EEPROMs.
Having regmap interface to this framework would give much better
abstraction for eeproms on different buses.
Signed-off-by: Maxime Ripard <maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
[Maxime Ripard: intial version of eeprom framework]
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
---
drivers/Kconfig | 2 +
drivers/Makefile | 1 +
drivers/eeprom/Kconfig | 11 +++
drivers/eeprom/Makefile | 5 +
drivers/eeprom/core.c | 213 ++++++++++++++++++++++++++++++++++++++++
include/linux/eeprom-provider.h | 47 +++++++++
6 files changed, 279 insertions(+)
create mode 100644 drivers/eeprom/Kconfig
create mode 100644 drivers/eeprom/Makefile
create mode 100644 drivers/eeprom/core.c
create mode 100644 include/linux/eeprom-provider.h
diff --git a/drivers/Kconfig b/drivers/Kconfig
index c70d6e4..d7afc82 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -184,4 +184,6 @@ source "drivers/thunderbolt/Kconfig"
source "drivers/android/Kconfig"
+source "drivers/eeprom/Kconfig"
+
endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 527a6da..57eb5b0 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -165,3 +165,4 @@ obj-$(CONFIG_RAS) += ras/
obj-$(CONFIG_THUNDERBOLT) += thunderbolt/
obj-$(CONFIG_CORESIGHT) += coresight/
obj-$(CONFIG_ANDROID) += android/
+obj-$(CONFIG_EEPROM) += eeprom/
diff --git a/drivers/eeprom/Kconfig b/drivers/eeprom/Kconfig
new file mode 100644
index 0000000..21e1847
--- /dev/null
+++ b/drivers/eeprom/Kconfig
@@ -0,0 +1,11 @@
+menuconfig EEPROM
+ tristate "EEPROM Support"
+ depends on OF
+ select REGMAP
+ help
+ Support for EEPROM alike devices.
+
+ This framework is designed to provide a generic interface to EEPROM
+ from both the Linux Kernel and the userspace.
+
+ If unsure, say no.
diff --git a/drivers/eeprom/Makefile b/drivers/eeprom/Makefile
new file mode 100644
index 0000000..250c95a
--- /dev/null
+++ b/drivers/eeprom/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for eeprom drivers.
+#
+
+obj-$(CONFIG_EEPROM) += core.o
diff --git a/drivers/eeprom/core.c b/drivers/eeprom/core.c
new file mode 100644
index 0000000..a9839de
--- /dev/null
+++ b/drivers/eeprom/core.c
@@ -0,0 +1,213 @@
+/*
+ * EEPROM framework core.
+ *
+ * Copyright (C) 2015 Srinivas Kandagatla <srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
+ * Copyright (C) 2013 Maxime Ripard <maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/eeprom-provider.h>
+#include <linux/export.h>
+#include <linux/fs.h>
+#include <linux/idr.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+struct eeprom_device {
+ struct regmap *regmap;
+ int stride;
+ size_t size;
+
+ struct module *owner;
+ struct device dev;
+ int id;
+ int users;
+};
+
+static DEFINE_MUTEX(eeprom_mutex);
+static DEFINE_IDA(eeprom_ida);
+
+#define to_eeprom(d) container_of(d, struct eeprom_device, dev)
+
+static ssize_t bin_attr_eeprom_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr,
+ char *buf, loff_t offset, size_t count)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct eeprom_device *eeprom = to_eeprom(dev);
+ int rc;
+
+ if (offset > eeprom->size)
+ return -EINVAL;
+
+ if (offset + count > eeprom->size)
+ count = eeprom->size - offset;
+
+ rc = regmap_bulk_read(eeprom->regmap, offset,
+ buf, count/eeprom->stride);
+
+ if (IS_ERR_VALUE(rc))
+ return rc;
+
+ return count - count % eeprom->stride;
+}
+
+static ssize_t bin_attr_eeprom_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr,
+ char *buf, loff_t offset, size_t count)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct eeprom_device *eeprom = to_eeprom(dev);
+ int rc;
+
+ if (offset > eeprom->size)
+ return -EINVAL;
+
+ if (offset + count > eeprom->size)
+ count = eeprom->size - offset;
+
+ rc = regmap_bulk_write(eeprom->regmap, offset,
+ buf, count/eeprom->stride);
+
+ if (IS_ERR_VALUE(rc))
+ return rc;
+
+ return count - count % eeprom->stride;
+}
+
+static struct bin_attribute bin_attr_eeprom = {
+ .attr = {
+ .name = "eeprom",
+ .mode = S_IWUSR | S_IRUGO,
+ },
+ .read = bin_attr_eeprom_read,
+ .write = bin_attr_eeprom_write,
+};
+
+static struct bin_attribute *eeprom_bin_attributes[] = {
+ &bin_attr_eeprom,
+ NULL,
+};
+
+static const struct attribute_group eeprom_bin_group = {
+ .bin_attrs = eeprom_bin_attributes,
+};
+
+static const struct attribute_group *eeprom_dev_groups[] = {
+ &eeprom_bin_group,
+ NULL,
+};
+
+static void eeprom_release(struct device *dev)
+{
+ kfree(to_eeprom(dev));
+}
+
+static struct class eeprom_class = {
+ .name = "eeprom",
+ .dev_groups = eeprom_dev_groups,
+ .dev_release = eeprom_release,
+};
+
+/**
+ * eeprom_register(): Register a eeprom device for given eeprom.
+ * Also creates an binary entry in /sys/class/eeprom/name-id/eeprom
+ *
+ * @eeprom: eeprom device that needs to be created
+ *
+ * The return value will be an error code on error or a zero on success.
+ * The eeprom_device and sysfs entery will be freed by the eeprom_unregister().
+ */
+
+struct eeprom_device *eeprom_register(struct eeprom_config *config)
+{
+ struct eeprom_device *eeprom;
+ int rval;
+
+ if (!config->regmap || !config->size) {
+ dev_err(config->dev, "Regmap not found\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ eeprom = kzalloc(sizeof(*eeprom), GFP_KERNEL);
+ if (!eeprom)
+ return ERR_PTR(-ENOMEM);
+
+ eeprom->id = ida_simple_get(&eeprom_ida, 0, 0, GFP_KERNEL);
+ if (eeprom->id < 0)
+ return ERR_PTR(eeprom->id);
+
+ eeprom->owner = config->owner;
+ eeprom->regmap = config->regmap;
+ eeprom->stride = config->stride;
+ eeprom->size = config->size;
+ eeprom->dev.class = &eeprom_class;
+ eeprom->dev.parent = config->dev;
+ eeprom->dev.of_node = config->dev ? config->dev->of_node : NULL;
+ dev_set_name(&eeprom->dev, "%s%d",
+ config->name ? : "eeprom", config->id);
+
+ device_initialize(&eeprom->dev);
+
+ dev_dbg(&eeprom->dev, "Registering eeprom device %s\n",
+ dev_name(&eeprom->dev));
+
+ rval = device_add(&eeprom->dev);
+ if (rval)
+ return ERR_PTR(rval);
+
+ return eeprom;
+}
+EXPORT_SYMBOL_GPL(eeprom_register);
+
+/**
+ * eeprom_unregister(): Unregister previously registered eeprom device
+ *
+ * @eeprom: Pointer to previously registered eeprom device.
+ *
+ * The return value will be an non zero on error or a zero on success.
+ */
+int eeprom_unregister(struct eeprom_device *eeprom)
+{
+ mutex_lock(&eeprom_mutex);
+ if (eeprom->users) {
+ mutex_unlock(&eeprom_mutex);
+ return -EBUSY;
+ }
+ mutex_unlock(&eeprom_mutex);
+
+ device_del(&eeprom->dev);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(eeprom_unregister);
+
+static int eeprom_init(void)
+{
+ return class_register(&eeprom_class);
+}
+
+static void eeprom_exit(void)
+{
+ class_unregister(&eeprom_class);
+}
+
+subsys_initcall(eeprom_init);
+module_exit(eeprom_exit);
+
+MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org");
+MODULE_AUTHOR("Maxime Ripard <maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org");
+MODULE_DESCRIPTION("EEPROM Driver Core");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/eeprom-provider.h b/include/linux/eeprom-provider.h
new file mode 100644
index 0000000..21afdaa
--- /dev/null
+++ b/include/linux/eeprom-provider.h
@@ -0,0 +1,47 @@
+/*
+ * EEPROM framework provider.
+ *
+ * Copyright (C) 2015 Srinivas Kandagatla <srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
+ * Copyright (C) 2013 Maxime Ripard <maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef _LINUX_EEPROM_PROVIDER_H
+#define _LINUX_EEPROM_PROVIDER_H
+
+#include <linux/regmap.h>
+
+struct eeprom_device;
+
+struct eeprom_config {
+ struct device *dev;
+ struct regmap *regmap;
+ const char *name;
+ int id;
+ int stride;
+ size_t size;
+ struct module *owner;
+};
+
+#if IS_ENABLED(CONFIG_EEPROM)
+
+struct eeprom_device *eeprom_register(struct eeprom_config *cfg);
+int eeprom_unregister(struct eeprom_device *eeprom);
+
+#else
+
+static inline struct eeprom_device *eeprom_register(struct eeprom_config *cfg)
+{
+ return NULL;
+}
+static inline int eeprom_unregister(struct eeprom_device *eeprom)
+{
+ return -ENOSYS;
+}
+
+#endif /* CONFIG_EEPROM */
+
+#endif /* ifndef _LINUX_EEPROM_PROVIDER_H */
--
1.9.1
^ permalink raw reply related
* [PATCH v2 0/7] Add simple EEPROM Framework via regmap.
From: Srinivas Kandagatla @ 2015-03-13 9:49 UTC (permalink / raw)
To: linux-arm-kernel
Cc: Maxime Ripard, Rob Herring, Pawel Moll, Kumar Gala, linux-api,
linux-kernel, devicetree, Stephen Boyd, Arnd Bergmann, broonie,
Greg Kroah-Hartman, linux-arm-msm, Srinivas Kandagatla
In-Reply-To: <1425548685-12887-1-git-send-email-srinivas.kandagatla@linaro.org>
Thankyou all for providing inputs and comments on previous versions of this
patchset. Here is the v2 of the patchset addressing all the issues raised as
part of previous versions review.
This patchset adds a new simple EEPROM framework to kernel.
Up until now, EEPROM drivers were stored in drivers/misc, where they all had to
duplicate pretty much the same code to register a sysfs file, allow in-kernel
users to access the content of the devices they were driving, etc.
This was also a problem as far as other in-kernel users were involved, since
the solutions used were pretty much different from on driver to another, there
was a rather big abstraction leak.
This introduction of this framework aims at solving this. It also introduces DT
representation for consumer devices to go get the data they require (MAC
Addresses, SoC/Revision ID, part numbers, and so on) from the EEPROMs.
Having regmap interface to this framework would give much better
abstraction for eeproms on different buses.
patch 1-3 Introduces the EEPROM framework.
Patch 4 migrates an existing driver to eeprom framework.
Patch 5-6 Adds Qualcomm specific qfprom driver.
Patch 7 adds entry in MAINTAINERS.
Its also possible to migrate other eeprom drivers to this framework.
Patch 6 can also be made a generic mmio-eeprom driver.
Providers APIs:
eeprom_register/unregister();
Consumers APIs:
eeprom_cell_get()/of_eeprom_cell_get()/of_eeprom_cell_get_byname();
eeprom_cell_read()/eeprom_cell_write();
Device Tree:
/* Provider */
qfprom: qfprom@00700000 {
compatible = "qcom,qfprom";
reg = <0x00700000 0x1000>;
...
/* Data cells */
tsens_calibration: calib@404 {
reg = <0x404 0x10>;
};
serial_number: sn {
reg = <0x104 0x4>, <0x204 0x4>, <0x30c 0x4>;
};
...
};
/* Consumer node */
tsens: tsens {
...
eeproms = <&tsens_calibration>;
eeprom-names = "calib";
...
};
userspace interface:
hexdump /sys/class/eeprom/qfprom0/eeprom
0000000 0000 0000 0000 0000 0000 0000 0000 0000
*
00000a0 db10 2240 0000 e000 0c00 0c00 0000 0c00
0000000 0000 0000 0000 0000 0000 0000 0000 0000
...
*
0001000
Changes since v1(https://lkml.org/lkml/2015/3/5/153)
* Fix various Licencing issues spotted by Paul Bolle and Mark Brown
* Allow eeprom core to build as module spotted by Paul Bolle.
* Fix various kconfig issues spotted by Paul Bolle.
* remove unessary atomic varible spotted by Mark Brown.
* Few cleanups and common up some of the code in core.
* Add qfprom bindings.
Changes since RFC(https://lkml.org/lkml/2015/2/19/307)
* Fix documentation and error checks in read/write spotted by Andrew Lunn
* Kconfig fix suggested by Stephen Boyd.
* Add module owner suggested by Stephen Boyd and others.
* Fix unsafe handling of eeprom in unregister spotted by Russell and Mark Brown.
* seperate bindings patch as suggested by Rob.
* Add MAINTAINERS as suggested by Rob.
* Added support to allow reading eeprom for things like serial number which
can be scatters across.
* Added eeprom data using reg property suggested by Sascha and Stephen.
* Added non-DT support.
* Move kerneldoc to the src files spotted by Mark Brown.
* Remove local list and do eeprom lookup by using class_find_device()
Thanks,
srini
Maxime Ripard (1):
eeprom: sunxi: Move the SID driver to the eeprom framework
Srinivas Kandagatla (6):
eeprom: Add a simple EEPROM framework for eeprom providers
eeprom: Add a simple EEPROM framework for eeprom consumers
eeprom: Add bindings for simple eeprom framework
eeprom: qfprom: Add Qualcomm QFPROM support.
eeprom: qfprom: Add bindings for qfprom
eeprom: Add to MAINTAINERS for eeprom framework
Documentation/ABI/testing/sysfs-driver-sunxi-sid | 22 -
.../bindings/eeprom/allwinner,sunxi-sid.txt | 21 +
.../devicetree/bindings/eeprom/eeprom.txt | 70 +++
.../devicetree/bindings/eeprom/qfprom.txt | 23 +
.../bindings/misc/allwinner,sunxi-sid.txt | 17 -
MAINTAINERS | 9 +
drivers/Kconfig | 2 +
drivers/Makefile | 1 +
drivers/eeprom/Kconfig | 33 ++
drivers/eeprom/Makefile | 9 +
drivers/eeprom/core.c | 517 +++++++++++++++++++++
drivers/eeprom/eeprom-sunxi-sid.c | 136 ++++++
drivers/eeprom/qfprom.c | 87 ++++
drivers/misc/eeprom/Kconfig | 13 -
drivers/misc/eeprom/Makefile | 1 -
drivers/misc/eeprom/sunxi_sid.c | 156 -------
include/linux/eeprom-consumer.h | 67 +++
include/linux/eeprom-provider.h | 47 ++
18 files changed, 1022 insertions(+), 209 deletions(-)
delete mode 100644 Documentation/ABI/testing/sysfs-driver-sunxi-sid
create mode 100644 Documentation/devicetree/bindings/eeprom/allwinner,sunxi-sid.txt
create mode 100644 Documentation/devicetree/bindings/eeprom/eeprom.txt
create mode 100644 Documentation/devicetree/bindings/eeprom/qfprom.txt
delete mode 100644 Documentation/devicetree/bindings/misc/allwinner,sunxi-sid.txt
create mode 100644 drivers/eeprom/Kconfig
create mode 100644 drivers/eeprom/Makefile
create mode 100644 drivers/eeprom/core.c
create mode 100644 drivers/eeprom/eeprom-sunxi-sid.c
create mode 100644 drivers/eeprom/qfprom.c
delete mode 100644 drivers/misc/eeprom/sunxi_sid.c
create mode 100644 include/linux/eeprom-consumer.h
create mode 100644 include/linux/eeprom-provider.h
--
1.9.1
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox