diff mbox

tpm: add support for nonblocking operation

Message ID 152780934926.32219.7291994735609525171.stgit@tstruk-mobl1.jf.intel.com (mailing list archive)
State New, archived
Headers show

Commit Message

Tadeusz Struk May 31, 2018, 11:29 p.m. UTC
The TCG SAPI specification [1] defines a set of functions, which allows
applications to use the TPM device in either blocking or non-blocking fashion.
Each command defined by the specification has a corresponding
Tss2_Sys_<COMMAND>_Prepare() and Tss2_Sys_<COMMAND>_Complete() call, which
together with Tss2_Sys_ExecuteAsync() is designed to allow asynchronous
mode of operation. Currently the driver supports only blocking calls, which
doesn't allow asynchronous operation. This patch changes it and adds support
for nonblocking write and a new poll function to enable applications using
the API as designed by the spec.
The new functionality can be tested using standard TPM tools implemented
in [2], with modified TCTI from [3].

[1] https://trustedcomputinggroup.org/wp-content/uploads/TSS_SAPI_Version-1.1_Revision-22_review_030918.pdf
[2] https://github.com/tpm2-software/tpm2-tools
[3] https://github.com/tstruk/tpm2-tss/tree/async

Signed-off-by: Tadeusz Struk <tadeusz.struk@intel.com>
---
 drivers/char/tpm/tpm-dev-common.c |  170 +++++++++++++++++++++++++++++++------
 drivers/char/tpm/tpm-dev.c        |    1 
 drivers/char/tpm/tpm-dev.h        |    9 +-
 drivers/char/tpm/tpmrm-dev.c      |    1 
 4 files changed, 149 insertions(+), 32 deletions(-)

Comments

Jason Gunthorpe June 1, 2018, 2:37 p.m. UTC | #1
On Thu, May 31, 2018 at 04:29:09PM -0700, Tadeusz Struk wrote:
> The TCG SAPI specification [1] defines a set of functions, which allows
> applications to use the TPM device in either blocking or non-blocking fashion.
> Each command defined by the specification has a corresponding
> Tss2_Sys_<COMMAND>_Prepare() and Tss2_Sys_<COMMAND>_Complete() call, which
> together with Tss2_Sys_ExecuteAsync() is designed to allow asynchronous
> mode of operation. Currently the driver supports only blocking calls, which
> doesn't allow asynchronous operation. This patch changes it and adds support
> for nonblocking write and a new poll function to enable applications using
> the API as designed by the spec.
> The new functionality can be tested using standard TPM tools implemented
> in [2], with modified TCTI from [3].
> 
> [1] https://trustedcomputinggroup.org/wp-content/uploads/TSS_SAPI_Version-1.1_Revision-22_review_030918.pdf
> [2] https://github.com/tpm2-software/tpm2-tools
> [3] https://github.com/tstruk/tpm2-tss/tree/async
> 
> Signed-off-by: Tadeusz Struk <tadeusz.struk@intel.com>
>  drivers/char/tpm/tpm-dev-common.c |  170 +++++++++++++++++++++++++++++++------
>  drivers/char/tpm/tpm-dev.c        |    1 
>  drivers/char/tpm/tpm-dev.h        |    9 +-
>  drivers/char/tpm/tpmrm-dev.c      |    1 
>  4 files changed, 149 insertions(+), 32 deletions(-)
> 
> diff --git a/drivers/char/tpm/tpm-dev-common.c b/drivers/char/tpm/tpm-dev-common.c
> index e4a04b2d3c32..0f3378b5198a 100644
> +++ b/drivers/char/tpm/tpm-dev-common.c
> @@ -17,11 +17,46 @@
>   * License.
>   *
>   */
> +#include <linux/poll.h>
> +#include <linux/sched/signal.h>
>  #include <linux/slab.h>
>  #include <linux/uaccess.h>
> +#include <linux/workqueue.h>
>  #include "tpm.h"
>  #include "tpm-dev.h"
>  
> +static DECLARE_WAIT_QUEUE_HEAD(tpm_dev_wait);
> +static struct workqueue_struct *tpm_dev_wq;
> +
> +struct tpm_dev_work {
> +	struct work_struct work;
> +	struct file_priv *priv;
> +	struct tpm_space *space;
> +	ssize_t resp_size;
> +	bool nonblocking;
> +};

There can only be one operation going at once, so why not put this in
the file_priv? Which might be needed because..

> +static void tpm_async_work(struct work_struct *work)
> +{
> +	struct tpm_dev_work *tpm_work =
> +		container_of(work, struct tpm_dev_work, work);
> +	struct file_priv *priv = tpm_work->priv;

What prevents priv from being freed at this point?

> @@ -82,10 +120,11 @@ ssize_t tpm_common_write(struct file *file, const char __user *buf,
>  			 size_t size, loff_t *off, struct tpm_space *space)
>  {
>  	struct file_priv *priv = file->private_data;
> -	size_t in_size = size;
> -	ssize_t out_size;
> +	struct tpm_dev_work *work;
> +	DECLARE_WAITQUEUE(wait, current);
> +	int ret = 0;

There is not really any reason to change the flow for the blocking
case, maybe just call the work function directly?

Also, it is getting confusing to have two things called 'work' (the
timer and the async executor)

> +__poll_t tpm_common_poll(struct file *file, poll_table *wait)
> +{
> +	struct file_priv *priv = file->private_data;
> +	__poll_t mask = 0;
> +
> +	poll_wait(file, &tpm_dev_wait, wait);

Why a global wait queue? Should be in file_priv I'd think.

> +static int __init tpm_dev_common_init(void)
> +{
> +	tpm_dev_wq = alloc_workqueue("tpm_dev_wq", WQ_MEM_RECLAIM, 0);
> +
> +	return !tpm_dev_wq ? -ENOMEM : 0;
> +}
> +
> +late_initcall(tpm_dev_common_init);
> +
> +static void __exit tpm_dev_common_exit(void)
> +{
> +	destroy_workqueue(tpm_dev_wq);
> +}

I wonder if it is worth creating this when the first file is
opened.. Lots of systems have TPMs but few use the userspace..

Jason
Tadeusz Struk June 1, 2018, 4:55 p.m. UTC | #2
On 06/01/2018 07:37 AM, Jason Gunthorpe wrote:
>> +struct tpm_dev_work {
>> +	struct work_struct work;
>> +	struct file_priv *priv;
>> +	struct tpm_space *space;
>> +	ssize_t resp_size;
>> +	bool nonblocking;
>> +};
> There can only be one operation going at once, so why not put this in
> the file_priv? Which might be needed because..

I need the *space as well, which doesn't really belong to file_priv
that's why I added this new struct. You are right the file_priv
isn't really needed here. I used it to have the priv->data_pending
but I can have just a ptr to data_pending in tpm_dev_work.

> 
>> +static void tpm_async_work(struct work_struct *work)
>> +{
>> +	struct tpm_dev_work *tpm_work =
>> +		container_of(work, struct tpm_dev_work, work);
>> +	struct file_priv *priv = tpm_work->priv;
> What prevents priv from being freed at this point?

The flush_work() that is currently missing from tpm_common_release() :)

> 
>> @@ -82,10 +120,11 @@ ssize_t tpm_common_write(struct file *file, const char __user *buf,
>>  			 size_t size, loff_t *off, struct tpm_space *space)
>>  {
>>  	struct file_priv *priv = file->private_data;
>> -	size_t in_size = size;
>> -	ssize_t out_size;
>> +	struct tpm_dev_work *work;
>> +	DECLARE_WAITQUEUE(wait, current);
>> +	int ret = 0;
> There is not really any reason to change the flow for the blocking
> case, maybe just call the work function directly?

That was my first idea, but then I thought it mighty be better to
have the same flow for both blocking and non-blocking.
I'm fine with doing it either way.

> 
> Also, it is getting confusing to have two things called 'work' (the
> timer and the async executor)
> 
>> +__poll_t tpm_common_poll(struct file *file, poll_table *wait)
>> +{
>> +	struct file_priv *priv = file->private_data;
>> +	__poll_t mask = 0;
>> +
>> +	poll_wait(file, &tpm_dev_wait, wait);
> Why a global wait queue? Should be in file_priv I'd think.

Right, I will move it to file_priv.

> 
>> +static int __init tpm_dev_common_init(void)
>> +{
>> +	tpm_dev_wq = alloc_workqueue("tpm_dev_wq", WQ_MEM_RECLAIM, 0);
>> +
>> +	return !tpm_dev_wq ? -ENOMEM : 0;
>> +}
>> +
>> +late_initcall(tpm_dev_common_init);
>> +
>> +static void __exit tpm_dev_common_exit(void)
>> +{
>> +	destroy_workqueue(tpm_dev_wq);
>> +}
> I wonder if it is worth creating this when the first file is
> opened.. Lots of systems have TPMs but few use the userspace.

I can do that.

Thanks for review Jason. I will send a v2 soon.
Jason Gunthorpe June 1, 2018, 5:17 p.m. UTC | #3
On Fri, Jun 01, 2018 at 09:55:52AM -0700, Tadeusz Struk wrote:
> On 06/01/2018 07:37 AM, Jason Gunthorpe wrote:
> >> +struct tpm_dev_work {
> >> +	struct work_struct work;
> >> +	struct file_priv *priv;
> >> +	struct tpm_space *space;
> >> +	ssize_t resp_size;
> >> +	bool nonblocking;
> >> +};
> > There can only be one operation going at once, so why not put this in
> > the file_priv? Which might be needed because..
> 
> I need the *space as well, which doesn't really belong to file_priv
> that's why I added this new struct. You are right the file_priv
> isn't really needed here. I used it to have the priv->data_pending
> but I can have just a ptr to data_pending in tpm_dev_work.

I mean in the sense that each file_priv can have exactly one async
work outstanding, so you can just add this struct to file_priv instead
of allocing it everytime..

Jason
Jarkko Sakkinen June 4, 2018, 7:55 p.m. UTC | #4
On Thu, May 31, 2018 at 04:29:09PM -0700, Tadeusz Struk wrote:
> The TCG SAPI specification [1] defines a set of functions, which allows
> applications to use the TPM device in either blocking or non-blocking fashion.
> Each command defined by the specification has a corresponding
> Tss2_Sys_<COMMAND>_Prepare() and Tss2_Sys_<COMMAND>_Complete() call, which
> together with Tss2_Sys_ExecuteAsync() is designed to allow asynchronous
> mode of operation. Currently the driver supports only blocking calls, which
> doesn't allow asynchronous operation. This patch changes it and adds support
> for nonblocking write and a new poll function to enable applications using
> the API as designed by the spec.
> The new functionality can be tested using standard TPM tools implemented
> in [2], with modified TCTI from [3].

I would need some statistics before I have interest to take these
changes in any form eg use case where this matters in the end.

/Jarkko
Jarkko Sakkinen June 4, 2018, 7:56 p.m. UTC | #5
On Mon, Jun 04, 2018 at 10:55:54PM +0300, Jarkko Sakkinen wrote:
> On Thu, May 31, 2018 at 04:29:09PM -0700, Tadeusz Struk wrote:
> > The TCG SAPI specification [1] defines a set of functions, which allows
> > applications to use the TPM device in either blocking or non-blocking fashion.
> > Each command defined by the specification has a corresponding
> > Tss2_Sys_<COMMAND>_Prepare() and Tss2_Sys_<COMMAND>_Complete() call, which
> > together with Tss2_Sys_ExecuteAsync() is designed to allow asynchronous
> > mode of operation. Currently the driver supports only blocking calls, which
> > doesn't allow asynchronous operation. This patch changes it and adds support
> > for nonblocking write and a new poll function to enable applications using
> > the API as designed by the spec.
> > The new functionality can be tested using standard TPM tools implemented
> > in [2], with modified TCTI from [3].
> 
> I would need some statistics before I have interest to take these
> changes in any form eg use case where this matters in the end.

And please CC linux-kernel and linux-security-module.

/Jarkko
flihp June 8, 2018, 7:36 p.m. UTC | #6
On 06/04/2018 12:55 PM, Jarkko Sakkinen wrote:
> On Thu, May 31, 2018 at 04:29:09PM -0700, Tadeusz Struk wrote:
>> The TCG SAPI specification [1] defines a set of functions, which allows
>> applications to use the TPM device in either blocking or non-blocking fashion.
>> Each command defined by the specification has a corresponding
>> Tss2_Sys_<COMMAND>_Prepare() and Tss2_Sys_<COMMAND>_Complete() call, which
>> together with Tss2_Sys_ExecuteAsync() is designed to allow asynchronous
>> mode of operation. Currently the driver supports only blocking calls, which
>> doesn't allow asynchronous operation. This patch changes it and adds support
>> for nonblocking write and a new poll function to enable applications using
>> the API as designed by the spec.
>> The new functionality can be tested using standard TPM tools implemented
>> in [2], with modified TCTI from [3].
> 
> I would need some statistics before I have interest to take these
> changes in any form eg use case where this matters in the end.

The use cases motivating this feature are the same ones that motivated
the non-blocking behavior of other kernel interfaces (files, sockets and
other hardware) that has the potential to block threads in a process. By
implementing this same behavior in the TPM driver our goal is to enable
use of the TPM in programming languages / frameworks implementing an
"event-driven" model. There are a lot of them out there but since the
TSS2 APIs are currently limited to C our example code is in glib / GSource.

Hopefully this is sufficient but if it isn't it would help us to get
additional details on what you're looking for.

Thanks,
Philip
Tadeusz Struk June 12, 2018, 12:13 a.m. UTC | #7
On 06/08/2018 12:36 PM, flihp wrote:
> On 06/04/2018 12:55 PM, Jarkko Sakkinen wrote:
>> On Thu, May 31, 2018 at 04:29:09PM -0700, Tadeusz Struk wrote:
>>> The TCG SAPI specification [1] defines a set of functions, which allows
>>> applications to use the TPM device in either blocking or non-blocking fashion.
>>> Each command defined by the specification has a corresponding
>>> Tss2_Sys_<COMMAND>_Prepare() and Tss2_Sys_<COMMAND>_Complete() call, which
>>> together with Tss2_Sys_ExecuteAsync() is designed to allow asynchronous
>>> mode of operation. Currently the driver supports only blocking calls, which
>>> doesn't allow asynchronous operation. This patch changes it and adds support
>>> for nonblocking write and a new poll function to enable applications using
>>> the API as designed by the spec.
>>> The new functionality can be tested using standard TPM tools implemented
>>> in [2], with modified TCTI from [3].
>>
>> I would need some statistics before I have interest to take these
>> changes in any form eg use case where this matters in the end.
> 
> The use cases motivating this feature are the same ones that motivated
> the non-blocking behavior of other kernel interfaces (files, sockets and
> other hardware) that has the potential to block threads in a process. By
> implementing this same behavior in the TPM driver our goal is to enable
> use of the TPM in programming languages / frameworks implementing an
> "event-driven" model. There are a lot of them out there but since the
> TSS2 APIs are currently limited to C our example code is in glib / GSource.
> 
> Hopefully this is sufficient but if it isn't it would help us to get
> additional details on what you're looking for.

I tried to run some tests w/r/t blocking to non-blocking.
and for example for tpm2_createprimary (type RSA) it looks more less like this:
blocking:
real	0m3.194s
user	0m0.052s
sys	0m0.094s

vs nonblocking:
real	0m1.031s
user	0m0.043s
sys	0m0.162s

that's on Minnowboard Turbot D0 PLATFORM
running FW Version: MNW2MAX1.X64.0097.R01.1709211204

The number are different every time I run it though.
Is this what you wanted to see?

Thanks,
Tadeusz Struk June 12, 2018, 12:20 a.m. UTC | #8
The TCG SAPI specification [1] defines a set of functions, which allows
applications to use the TPM device in either blocking or non-blocking fashion.
Each command defined by the specification has a corresponding
Tss2_Sys_<COMMAND>_Prepare() and Tss2_Sys_<COMMAND>_Complete() call, which
together with Tss2_Sys_ExecuteAsync() is designed to allow asynchronous
mode of operation. Currently the TPM driver supports only blocking calls,
which doesn't allow asynchronous IO operations.
This patch changes it and adds support for nonblocking write and a new poll
function to enable applications, which want to take advantage of this feature.
The new functionality can be tested using standard TPM tools implemented
in [2], together with modified TCTI from [3].

[1] https://trustedcomputinggroup.org/wp-content/uploads/TSS_SAPI_Version-1.1_Revision-22_review_030918.pdf
[2] https://github.com/tpm2-software/tpm2-tools
[3] https://github.com/tstruk/tpm2-tss/tree/async

---
Changes in v2:
- Split the change into two separate patches. First patch adds a pointer
  to the space to the struct file_priv to have access to it from the async job.
  This is to avoid memory allocations on every write call. Now everything
  what's needed is in the file_priv struct.
- Renamed the 'work' member of the timer to avoid confusion.
  Now there are 'timeout_work' and 'async_work'.
- Removed the global wait queue and moved it to file_priv.
- Only creating the work queue when the first file is opened.

Tadeusz Struk (2):
      tpm: add ptr to the tpm_space struct to file_priv
      tpm: add support for nonblocking operation

 drivers/char/tpm/tpm-dev-common.c |  152 ++++++++++++++++++++++++++++---------
 drivers/char/tpm/tpm-dev.c        |   22 +++--
 drivers/char/tpm/tpm-dev.h        |   19 +++--
 drivers/char/tpm/tpmrm-dev.c      |   31 ++++----
 4 files changed, 152 insertions(+), 72 deletions(-)

--
TS
James Bottomley June 12, 2018, 1:43 a.m. UTC | #9
On Fri, 2018-06-08 at 12:36 -0700, flihp wrote:
> On 06/04/2018 12:55 PM, Jarkko Sakkinen wrote:
> > On Thu, May 31, 2018 at 04:29:09PM -0700, Tadeusz Struk wrote:
> > > The TCG SAPI specification [1] defines a set of functions, which
> > > allows applications to use the TPM device in either blocking or
> > > non-blocking fashion. Each command defined by the specification
> > > has a corresponding Tss2_Sys_<COMMAND>_Prepare() and
> > > Tss2_Sys_<COMMAND>_Complete() call, which together with
> > > Tss2_Sys_ExecuteAsync() is designed to allow asynchronous
> > > mode of operation. Currently the driver supports only blocking
> > > calls, which doesn't allow asynchronous operation. This patch
> > > changes it and adds support for nonblocking write and a new poll
> > > function to enable applications using the API as designed by the
> > > spec. The new functionality can be tested using standard TPM
> > > tools implemented in [2], with modified TCTI from [3].
> > 
> > I would need some statistics before I have interest to take these
> > changes in any form eg use case where this matters in the end.
> 
> The use cases motivating this feature are the same ones that
> motivated the non-blocking behavior of other kernel interfaces
> (files, sockets and other hardware) that has the potential to block
> threads in a process. By implementing this same behavior in the TPM
> driver our goal is to enable use of the TPM in programming languages
> / frameworks implementing an "event-driven" model. There are a lot of
> them out there but since the TSS2 APIs are currently limited to C our
> example code is in glib / GSource.

But the TPM isn't like any of those resources, which can handle
multiple outstanding requests at once and get more efficient
utilisation doing so: it *is* single threaded in operation.  Meaning if
it's occupied doing something when you make your request, you wait
until it's finished up before your request goes through.  This means
that even for modern webby languages, the single worker thread
producer/consumer queue model is the best way of handling it because
it's the model that most closely corresponds to actual operation.  That
model just works today with the worker thread owning the open tpmrm
device.

I mean some of the actual devices are polled rather than interrupt
driven to give those watching some idea of how primitive our control
bus is.

> Hopefully this is sufficient but if it isn't it would help us to get
> additional details on what you're looking for.

OK, so I still don't see an actual use case for having poll, but on the
other hand the patches look simple enough so I don't see them adding
unmaintainable complexity either; on that measure, I'm OK with this as
long as you support it.

James
Jarkko Sakkinen June 18, 2018, 6:25 p.m. UTC | #10
On Fri, Jun 08, 2018 at 12:36:18PM -0700, flihp wrote:
> On 06/04/2018 12:55 PM, Jarkko Sakkinen wrote:
> > On Thu, May 31, 2018 at 04:29:09PM -0700, Tadeusz Struk wrote:
> >> The TCG SAPI specification [1] defines a set of functions, which allows
> >> applications to use the TPM device in either blocking or non-blocking fashion.
> >> Each command defined by the specification has a corresponding
> >> Tss2_Sys_<COMMAND>_Prepare() and Tss2_Sys_<COMMAND>_Complete() call, which
> >> together with Tss2_Sys_ExecuteAsync() is designed to allow asynchronous
> >> mode of operation. Currently the driver supports only blocking calls, which
> >> doesn't allow asynchronous operation. This patch changes it and adds support
> >> for nonblocking write and a new poll function to enable applications using
> >> the API as designed by the spec.
> >> The new functionality can be tested using standard TPM tools implemented
> >> in [2], with modified TCTI from [3].
> > 
> > I would need some statistics before I have interest to take these
> > changes in any form eg use case where this matters in the end.
> 
> The use cases motivating this feature are the same ones that motivated
> the non-blocking behavior of other kernel interfaces (files, sockets and
> other hardware) that has the potential to block threads in a process. By
> implementing this same behavior in the TPM driver our goal is to enable
> use of the TPM in programming languages / frameworks implementing an
> "event-driven" model. There are a lot of them out there but since the
> TSS2 APIs are currently limited to C our example code is in glib / GSource.
> 
> Hopefully this is sufficient but if it isn't it would help us to get
> additional details on what you're looking for.

Thanks Philip. I'll look into the patch itself.

/Jarkko
Jarkko Sakkinen June 18, 2018, 6:26 p.m. UTC | #11
On Mon, Jun 11, 2018 at 05:13:34PM -0700, Tadeusz Struk wrote:
> On 06/08/2018 12:36 PM, flihp wrote:
> > On 06/04/2018 12:55 PM, Jarkko Sakkinen wrote:
> >> On Thu, May 31, 2018 at 04:29:09PM -0700, Tadeusz Struk wrote:
> >>> The TCG SAPI specification [1] defines a set of functions, which allows
> >>> applications to use the TPM device in either blocking or non-blocking fashion.
> >>> Each command defined by the specification has a corresponding
> >>> Tss2_Sys_<COMMAND>_Prepare() and Tss2_Sys_<COMMAND>_Complete() call, which
> >>> together with Tss2_Sys_ExecuteAsync() is designed to allow asynchronous
> >>> mode of operation. Currently the driver supports only blocking calls, which
> >>> doesn't allow asynchronous operation. This patch changes it and adds support
> >>> for nonblocking write and a new poll function to enable applications using
> >>> the API as designed by the spec.
> >>> The new functionality can be tested using standard TPM tools implemented
> >>> in [2], with modified TCTI from [3].
> >>
> >> I would need some statistics before I have interest to take these
> >> changes in any form eg use case where this matters in the end.
> > 
> > The use cases motivating this feature are the same ones that motivated
> > the non-blocking behavior of other kernel interfaces (files, sockets and
> > other hardware) that has the potential to block threads in a process. By
> > implementing this same behavior in the TPM driver our goal is to enable
> > use of the TPM in programming languages / frameworks implementing an
> > "event-driven" model. There are a lot of them out there but since the
> > TSS2 APIs are currently limited to C our example code is in glib / GSource.
> > 
> > Hopefully this is sufficient but if it isn't it would help us to get
> > additional details on what you're looking for.
> 
> I tried to run some tests w/r/t blocking to non-blocking.
> and for example for tpm2_createprimary (type RSA) it looks more less like this:
> blocking:
> real	0m3.194s
> user	0m0.052s
> sys	0m0.094s
> 
> vs nonblocking:
> real	0m1.031s
> user	0m0.043s
> sys	0m0.162s
> 
> that's on Minnowboard Turbot D0 PLATFORM
> running FW Version: MNW2MAX1.X64.0097.R01.1709211204
> 
> The number are different every time I run it though.
> Is this what you wanted to see?

Thanks, this and Philips explanation make it worth of looking into!

/Jarkko
flihp July 5, 2018, 11:15 p.m. UTC | #12
On 06/18/2018 11:25 AM, Jarkko Sakkinen wrote:
> On Fri, Jun 08, 2018 at 12:36:18PM -0700, flihp wrote:
>> On 06/04/2018 12:55 PM, Jarkko Sakkinen wrote:
>>> On Thu, May 31, 2018 at 04:29:09PM -0700, Tadeusz Struk wrote:
>>>> The TCG SAPI specification [1] defines a set of functions, which allows
>>>> applications to use the TPM device in either blocking or non-blocking fashion.
>>>> Each command defined by the specification has a corresponding
>>>> Tss2_Sys_<COMMAND>_Prepare() and Tss2_Sys_<COMMAND>_Complete() call, which
>>>> together with Tss2_Sys_ExecuteAsync() is designed to allow asynchronous
>>>> mode of operation. Currently the driver supports only blocking calls, which
>>>> doesn't allow asynchronous operation. This patch changes it and adds support
>>>> for nonblocking write and a new poll function to enable applications using
>>>> the API as designed by the spec.
>>>> The new functionality can be tested using standard TPM tools implemented
>>>> in [2], with modified TCTI from [3].
>>>
>>> I would need some statistics before I have interest to take these
>>> changes in any form eg use case where this matters in the end.
>>
>> The use cases motivating this feature are the same ones that motivated
>> the non-blocking behavior of other kernel interfaces (files, sockets and
>> other hardware) that has the potential to block threads in a process. By
>> implementing this same behavior in the TPM driver our goal is to enable
>> use of the TPM in programming languages / frameworks implementing an
>> "event-driven" model. There are a lot of them out there but since the
>> TSS2 APIs are currently limited to C our example code is in glib / GSource.
>>
>> Hopefully this is sufficient but if it isn't it would help us to get
>> additional details on what you're looking for.
> 
> Thanks Philip. I'll look into the patch itself.

Thanks for reviewing the patch Jarkko. While you're doing that I took
some time to hack up code to demonstrate the utility of supporting this
feature. The code can be found here:

https://github.com/flihp/glib-tss2-async-example

In short, the example application `glib-tss2-event` uses a glib main
event loop to create an RSA 2048 primary key in the TPM2 NULL hierarchy
while using a glib timer event to time the operation. A GSource object
is used to generate an event when the FD underlying the tss2 function
call has data ready. While the application waits for an event indicating
that the CreatePrimary operation is complete, it counts timer events
that occur every 100ms. Once the CreatePrimary operation completes the
number of timer events that occurred is used to make a rough calculation
of the elapsed time. This value is then printed to the console.

This takes ~300 lines of C code and requires no management or
synchronization of threads. The glib GMainContext is "just a poll()
loop" according to the glib documentation here:

https://developer.gnome.org/programming-guidelines/stable/main-contexts.html.en

and so supporting 'poll' is the easiest way to integrate with glib /
gtk+. This is true of any other event system that relies on 'poll'
instead of worker threads.

I've tested this against the userspace resource management daemon (which
supports 'poll') as well as the kernel interface using Tadeusz's patch
currently under review here. If / when this gets merged feel free to add
a "tested-by" line for myself:
Tested-by: Philip Tricca <philip.b.tricca@intel.com>

Thanks,
Philip
Jarkko Sakkinen July 16, 2018, 5:34 p.m. UTC | #13
On Thu, Jul 05, 2018 at 04:15:06PM -0700, flihp wrote:
> Thanks for reviewing the patch Jarkko. While you're doing that I took
> some time to hack up code to demonstrate the utility of supporting this
> feature. The code can be found here:
> 
> https://github.com/flihp/glib-tss2-async-example
> 
> In short, the example application `glib-tss2-event` uses a glib main
> event loop to create an RSA 2048 primary key in the TPM2 NULL hierarchy
> while using a glib timer event to time the operation. A GSource object
> is used to generate an event when the FD underlying the tss2 function
> call has data ready. While the application waits for an event indicating
> that the CreatePrimary operation is complete, it counts timer events
> that occur every 100ms. Once the CreatePrimary operation completes the
> number of timer events that occurred is used to make a rough calculation
> of the elapsed time. This value is then printed to the console.
> 
> This takes ~300 lines of C code and requires no management or
> synchronization of threads. The glib GMainContext is "just a poll()
> loop" according to the glib documentation here:
> 
> https://developer.gnome.org/programming-guidelines/stable/main-contexts.html.en
> 
> and so supporting 'poll' is the easiest way to integrate with glib /
> gtk+. This is true of any other event system that relies on 'poll'
> instead of worker threads.
> 
> I've tested this against the userspace resource management daemon (which
> supports 'poll') as well as the kernel interface using Tadeusz's patch
> currently under review here. If / when this gets merged feel free to add
> a "tested-by" line for myself:
> Tested-by: Philip Tricca <philip.b.tricca@intel.com>

I see the value now. I'll test this at early August when I'm back from
my leave.

Noticed now that the whole patch should be reposted because CC is missing
linux-kernel and linux-security-module (maybe with a link to your test
application in the cover letter).

/Jarkko
Tadeusz Struk July 16, 2018, 8:10 p.m. UTC | #14
On 07/16/2018 10:34 AM, Jarkko Sakkinen wrote:
> On Thu, Jul 05, 2018 at 04:15:06PM -0700, flihp wrote:
>> Thanks for reviewing the patch Jarkko. While you're doing that I took
>> some time to hack up code to demonstrate the utility of supporting this
>> feature. The code can be found here:
>>
>> https://github.com/flihp/glib-tss2-async-example
>>
>> In short, the example application `glib-tss2-event` uses a glib main
>> event loop to create an RSA 2048 primary key in the TPM2 NULL hierarchy
>> while using a glib timer event to time the operation. A GSource object
>> is used to generate an event when the FD underlying the tss2 function
>> call has data ready. While the application waits for an event indicating
>> that the CreatePrimary operation is complete, it counts timer events
>> that occur every 100ms. Once the CreatePrimary operation completes the
>> number of timer events that occurred is used to make a rough calculation
>> of the elapsed time. This value is then printed to the console.
>>
>> This takes ~300 lines of C code and requires no management or
>> synchronization of threads. The glib GMainContext is "just a poll()
>> loop" according to the glib documentation here:
>>
>> https://developer.gnome.org/programming-guidelines/stable/main-contexts.html.en
>>
>> and so supporting 'poll' is the easiest way to integrate with glib /
>> gtk+. This is true of any other event system that relies on 'poll'
>> instead of worker threads.
>>
>> I've tested this against the userspace resource management daemon (which
>> supports 'poll') as well as the kernel interface using Tadeusz's patch
>> currently under review here. If / when this gets merged feel free to add
>> a "tested-by" line for myself:
>> Tested-by: Philip Tricca <philip.b.tricca@intel.com>
> 
> I see the value now. I'll test this at early August when I'm back from
> my leave.
> 
> Noticed now that the whole patch should be reposted because CC is missing
> linux-kernel and linux-security-module (maybe with a link to your test
> application in the cover letter).

I'll rebase and resend it after you will be back from vacation.
Thanks,
Jarkko Sakkinen July 23, 2018, 5:38 p.m. UTC | #15
On Mon, Jul 16, 2018 at 01:10:42PM -0700, Tadeusz Struk wrote:
> On 07/16/2018 10:34 AM, Jarkko Sakkinen wrote:
> > On Thu, Jul 05, 2018 at 04:15:06PM -0700, flihp wrote:
> >> Thanks for reviewing the patch Jarkko. While you're doing that I took
> >> some time to hack up code to demonstrate the utility of supporting this
> >> feature. The code can be found here:
> >>
> >> https://github.com/flihp/glib-tss2-async-example
> >>
> >> In short, the example application `glib-tss2-event` uses a glib main
> >> event loop to create an RSA 2048 primary key in the TPM2 NULL hierarchy
> >> while using a glib timer event to time the operation. A GSource object
> >> is used to generate an event when the FD underlying the tss2 function
> >> call has data ready. While the application waits for an event indicating
> >> that the CreatePrimary operation is complete, it counts timer events
> >> that occur every 100ms. Once the CreatePrimary operation completes the
> >> number of timer events that occurred is used to make a rough calculation
> >> of the elapsed time. This value is then printed to the console.
> >>
> >> This takes ~300 lines of C code and requires no management or
> >> synchronization of threads. The glib GMainContext is "just a poll()
> >> loop" according to the glib documentation here:
> >>
> >> https://developer.gnome.org/programming-guidelines/stable/main-contexts.html.en
> >>
> >> and so supporting 'poll' is the easiest way to integrate with glib /
> >> gtk+. This is true of any other event system that relies on 'poll'
> >> instead of worker threads.
> >>
> >> I've tested this against the userspace resource management daemon (which
> >> supports 'poll') as well as the kernel interface using Tadeusz's patch
> >> currently under review here. If / when this gets merged feel free to add
> >> a "tested-by" line for myself:
> >> Tested-by: Philip Tricca <philip.b.tricca@intel.com>
> > 
> > I see the value now. I'll test this at early August when I'm back from
> > my leave.
> > 
> > Noticed now that the whole patch should be reposted because CC is missing
> > linux-kernel and linux-security-module (maybe with a link to your test
> > application in the cover letter).
> 
> I'll rebase and resend it after you will be back from vacation.
> Thanks,
> -- 
> Tadeusz

Please add also the requested CC's. Thank you.

/Jarkko
diff mbox

Patch

diff --git a/drivers/char/tpm/tpm-dev-common.c b/drivers/char/tpm/tpm-dev-common.c
index e4a04b2d3c32..0f3378b5198a 100644
--- a/drivers/char/tpm/tpm-dev-common.c
+++ b/drivers/char/tpm/tpm-dev-common.c
@@ -17,11 +17,46 @@ 
  * License.
  *
  */
+#include <linux/poll.h>
+#include <linux/sched/signal.h>
 #include <linux/slab.h>
 #include <linux/uaccess.h>
+#include <linux/workqueue.h>
 #include "tpm.h"
 #include "tpm-dev.h"
 
+static DECLARE_WAIT_QUEUE_HEAD(tpm_dev_wait);
+static struct workqueue_struct *tpm_dev_wq;
+
+struct tpm_dev_work {
+	struct work_struct work;
+	struct file_priv *priv;
+	struct tpm_space *space;
+	ssize_t resp_size;
+	bool nonblocking;
+};
+
+static void tpm_async_work(struct work_struct *work)
+{
+	struct tpm_dev_work *tpm_work =
+		container_of(work, struct tpm_dev_work, work);
+	struct file_priv *priv = tpm_work->priv;
+
+	tpm_work->resp_size = tpm_transmit(priv->chip, tpm_work->space,
+					   priv->data_buffer,
+					   sizeof(priv->data_buffer), 0);
+	if (!tpm_work->nonblocking) {
+		wake_up_interruptible(&tpm_dev_wait);
+		return;
+	}
+
+	tpm_put_ops(priv->chip);
+	priv->data_pending = tpm_work->resp_size;
+	mutex_unlock(&priv->buffer_mutex);
+	mod_timer(&priv->user_read_timer, jiffies + (120 * HZ));
+	kfree(tpm_work);
+}
+
 static void user_reader_timeout(struct timer_list *t)
 {
 	struct file_priv *priv = from_timer(priv, t, user_read_timer);
@@ -40,6 +75,7 @@  static void timeout_work(struct work_struct *work)
 	priv->data_pending = 0;
 	memset(priv->data_buffer, 0, sizeof(priv->data_buffer));
 	mutex_unlock(&priv->buffer_mutex);
+	wake_up_interruptible(&tpm_dev_wait);
 }
 
 void tpm_common_open(struct file *file, struct tpm_chip *chip,
@@ -66,10 +102,12 @@  ssize_t tpm_common_read(struct file *file, char __user *buf,
 
 	if (priv->data_pending) {
 		ret_size = min_t(ssize_t, size, priv->data_pending);
-		rc = copy_to_user(buf, priv->data_buffer, ret_size);
-		memset(priv->data_buffer, 0, priv->data_pending);
-		if (rc)
-			ret_size = -EFAULT;
+		if (ret_size > 0) {
+			rc = copy_to_user(buf, priv->data_buffer, ret_size);
+			memset(priv->data_buffer, 0, priv->data_pending);
+			if (rc)
+				ret_size = -EFAULT;
+		}
 
 		priv->data_pending = 0;
 	}
@@ -82,10 +120,11 @@  ssize_t tpm_common_write(struct file *file, const char __user *buf,
 			 size_t size, loff_t *off, struct tpm_space *space)
 {
 	struct file_priv *priv = file->private_data;
-	size_t in_size = size;
-	ssize_t out_size;
+	struct tpm_dev_work *work;
+	DECLARE_WAITQUEUE(wait, current);
+	int ret = 0;
 
-	if (in_size > TPM_BUFSIZE)
+	if (size > TPM_BUFSIZE)
 		return -E2BIG;
 
 	mutex_lock(&priv->buffer_mutex);
@@ -95,20 +134,19 @@  ssize_t tpm_common_write(struct file *file, const char __user *buf,
 	 * buffered writes from blocking here.
 	 */
 	if (priv->data_pending != 0) {
-		mutex_unlock(&priv->buffer_mutex);
-		return -EBUSY;
+		ret = -EBUSY;
+		goto out;
 	}
 
-	if (copy_from_user
-	    (priv->data_buffer, (void __user *) buf, in_size)) {
-		mutex_unlock(&priv->buffer_mutex);
-		return -EFAULT;
+	if (copy_from_user(priv->data_buffer, buf, size)) {
+		ret = -EFAULT;
+		goto out;
 	}
 
-	if (in_size < 6 ||
-	    in_size < be32_to_cpu(*((__be32 *) (priv->data_buffer + 2)))) {
-		mutex_unlock(&priv->buffer_mutex);
-		return -EINVAL;
+	if (size < 6 ||
+	    size < be32_to_cpu(*((__be32 *)(priv->data_buffer + 2)))) {
+		ret = -EINVAL;
+		goto out;
 	}
 
 	/* atomic tpm command send and result receive. We only hold the ops
@@ -116,25 +154,85 @@  ssize_t tpm_common_write(struct file *file, const char __user *buf,
 	 * the char dev is held open.
 	 */
 	if (tpm_try_get_ops(priv->chip)) {
-		mutex_unlock(&priv->buffer_mutex);
-		return -EPIPE;
+		ret = -EPIPE;
+		goto out;
 	}
-	out_size = tpm_transmit(priv->chip, space, priv->data_buffer,
-				sizeof(priv->data_buffer), 0);
 
+	/*
+	 * Schedule an async job to send the command
+	 */
+	work = kzalloc(sizeof(*work), GFP_KERNEL);
+	if (!work) {
+		tpm_put_ops(priv->chip);
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	work->priv = priv;
+	work->space = space;
+	work->nonblocking = file->f_flags & O_NONBLOCK;
+	INIT_WORK(&work->work, tpm_async_work);
+	queue_work(tpm_dev_wq, &work->work);
+
+	/*
+	 * If in nonblocking mode, return the size.
+	 * In case of an error the error code will be
+	 * returned in the subsequent read call.
+	 */
+	if (work->nonblocking)
+		return size;
+
+	add_wait_queue(&tpm_dev_wait, &wait);
+	current->state = TASK_INTERRUPTIBLE;
+	for (;;) {
+		if (work->resp_size)
+			break;
+
+		if (signal_pending(current)) {
+			ret = -ERESTARTSYS;
+			break;
+		}
+		schedule();
+	}
+	current->state = TASK_RUNNING;
+	remove_wait_queue(&tpm_dev_wait, &wait);
 	tpm_put_ops(priv->chip);
-	if (out_size < 0) {
-		mutex_unlock(&priv->buffer_mutex);
-		return out_size;
+
+	if (ret)
+		goto out_free;
+
+	if (work->resp_size < 0) {
+		ret = work->resp_size;
+		goto out_free;
 	}
 
-	priv->data_pending = out_size;
+	priv->data_pending = work->resp_size;
+	kfree(work);
 	mutex_unlock(&priv->buffer_mutex);
-
-	/* Set a timeout by which the reader must come claim the result */
+	wake_up_interruptible(&tpm_dev_wait);
 	mod_timer(&priv->user_read_timer, jiffies + (120 * HZ));
+	return size;
+
+out_free:
+	kfree(work);
+out:
+	mutex_unlock(&priv->buffer_mutex);
+	return ret;
+}
+
+__poll_t tpm_common_poll(struct file *file, poll_table *wait)
+{
+	struct file_priv *priv = file->private_data;
+	__poll_t mask = 0;
+
+	poll_wait(file, &tpm_dev_wait, wait);
+
+	if (priv->data_pending)
+		mask = EPOLLIN | EPOLLRDNORM;
+	else
+		mask = EPOLLOUT | EPOLLWRNORM;
 
-	return in_size;
+	return mask;
 }
 
 /*
@@ -147,3 +245,19 @@  void tpm_common_release(struct file *file, struct file_priv *priv)
 	file->private_data = NULL;
 	priv->data_pending = 0;
 }
+
+static int __init tpm_dev_common_init(void)
+{
+	tpm_dev_wq = alloc_workqueue("tpm_dev_wq", WQ_MEM_RECLAIM, 0);
+
+	return !tpm_dev_wq ? -ENOMEM : 0;
+}
+
+late_initcall(tpm_dev_common_init);
+
+static void __exit tpm_dev_common_exit(void)
+{
+	destroy_workqueue(tpm_dev_wq);
+}
+
+__exitcall(tpm_dev_common_exit);
diff --git a/drivers/char/tpm/tpm-dev.c b/drivers/char/tpm/tpm-dev.c
index ebd74ab5abef..dc0fdfdf24c4 100644
--- a/drivers/char/tpm/tpm-dev.c
+++ b/drivers/char/tpm/tpm-dev.c
@@ -74,5 +74,6 @@  const struct file_operations tpm_fops = {
 	.open = tpm_open,
 	.read = tpm_common_read,
 	.write = tpm_write,
+	.poll = tpm_common_poll,
 	.release = tpm_release,
 };
diff --git a/drivers/char/tpm/tpm-dev.h b/drivers/char/tpm/tpm-dev.h
index b24cfb4d3ee1..61a3ee0024e4 100644
--- a/drivers/char/tpm/tpm-dev.h
+++ b/drivers/char/tpm/tpm-dev.h
@@ -2,13 +2,13 @@ 
 #ifndef _TPM_DEV_H
 #define _TPM_DEV_H
 
+#include <linux/poll.h>
 #include "tpm.h"
 
 struct file_priv {
 	struct tpm_chip *chip;
-
-	/* Data passed to and from the tpm via the read/write calls */
-	size_t data_pending;
+	/* Holds the amount of data passed or an error code from async op */
+	ssize_t data_pending;
 	struct mutex buffer_mutex;
 
 	struct timer_list user_read_timer;      /* user needs to claim result */
@@ -23,6 +23,7 @@  ssize_t tpm_common_read(struct file *file, char __user *buf,
 			size_t size, loff_t *off);
 ssize_t tpm_common_write(struct file *file, const char __user *buf,
 			 size_t size, loff_t *off, struct tpm_space *space);
-void tpm_common_release(struct file *file, struct file_priv *priv);
+__poll_t tpm_common_poll(struct file *file, poll_table *wait);
 
+void tpm_common_release(struct file *file, struct file_priv *priv);
 #endif
diff --git a/drivers/char/tpm/tpmrm-dev.c b/drivers/char/tpm/tpmrm-dev.c
index 1a0e97a5da5a..bbfd384b67b2 100644
--- a/drivers/char/tpm/tpmrm-dev.c
+++ b/drivers/char/tpm/tpmrm-dev.c
@@ -60,6 +60,7 @@  const struct file_operations tpmrm_fops = {
 	.open = tpmrm_open,
 	.read = tpm_common_read,
 	.write = tpmrm_write,
+	.poll = tpm_common_poll,
 	.release = tpmrm_release,
 };