From patchwork Tue Jun 12 00:20:24 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tadeusz Struk X-Patchwork-Id: 10458971 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id AD7D260532 for ; Tue, 12 Jun 2018 00:20:30 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 98704285EE for ; Tue, 12 Jun 2018 00:20:30 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 869B8285E8; Tue, 12 Jun 2018 00:20:30 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id DE904285E5 for ; Tue, 12 Jun 2018 00:20:29 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S934374AbeFLAU2 (ORCPT ); Mon, 11 Jun 2018 20:20:28 -0400 Received: from mga07.intel.com ([134.134.136.100]:44496 "EHLO mga07.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S934366AbeFLAUZ (ORCPT ); Mon, 11 Jun 2018 20:20:25 -0400 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga005.jf.intel.com ([10.7.209.41]) by orsmga105.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 11 Jun 2018 17:20:24 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.51,212,1526367600"; d="scan'208";a="231780806" Received: from tstruk-mobl1.jf.intel.com ([10.7.196.162]) by orsmga005.jf.intel.com with ESMTP; 11 Jun 2018 17:20:24 -0700 Subject: [PATCH v2 2/2] tpm: add support for nonblocking operation From: Tadeusz Struk To: jarkko.sakkinen@linux.intel.com Cc: jgg@ziepe.ca, linux-integrity@vger.kernel.org, tadeusz.struk@intel.com, linux-security-module@vger.kernel.org, linux-kernel@vger.kernel.org Date: Mon, 11 Jun 2018 17:20:24 -0700 Message-ID: <152876282414.19591.7972634639749417028.stgit@tstruk-mobl1.jf.intel.com> In-Reply-To: <152876281395.19591.3681365717212126222.stgit@tstruk-mobl1.jf.intel.com> References: <152876281395.19591.3681365717212126222.stgit@tstruk-mobl1.jf.intel.com> User-Agent: StGit/0.17.1-dirty MIME-Version: 1.0 Sender: owner-linux-security-module@vger.kernel.org Precedence: bulk List-ID: X-Virus-Scanned: ClamAV using ClamSMTP Currently the TPM driver only supports blocking calls, which doesn't allow asynchronous IO operations to the TPM hardware. 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. Signed-off-by: Tadeusz Struk --- drivers/char/tpm/tpm-dev-common.c | 148 ++++++++++++++++++++++++++++--------- drivers/char/tpm/tpm-dev.c | 16 +++- drivers/char/tpm/tpm-dev.h | 16 +++- drivers/char/tpm/tpmrm-dev.c | 19 ++++- 4 files changed, 146 insertions(+), 53 deletions(-) -- To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html diff --git a/drivers/char/tpm/tpm-dev-common.c b/drivers/char/tpm/tpm-dev-common.c index f0c033b69b62..97625639e20f 100644 --- a/drivers/char/tpm/tpm-dev-common.c +++ b/drivers/char/tpm/tpm-dev-common.c @@ -17,11 +17,35 @@ * License. * */ +#include +#include #include #include +#include #include "tpm.h" #include "tpm-dev.h" +static struct workqueue_struct *tpm_dev_wq; +static DEFINE_MUTEX(tpm_dev_wq_lock); + +static void tpm_async_work(struct work_struct *work) +{ + struct file_priv *priv = + container_of(work, struct file_priv, async_work); + ssize_t ret; + + ret = tpm_transmit(priv->chip, priv->space, priv->data_buffer, + sizeof(priv->data_buffer), 0); + + tpm_put_ops(priv->chip); + if (ret > 0) { + priv->data_pending = ret; + mod_timer(&priv->user_read_timer, jiffies + (120 * HZ)); + } + mutex_unlock(&priv->buffer_mutex); + wake_up_interruptible(&priv->async_wait); +} + static void user_reader_timeout(struct timer_list *t) { struct file_priv *priv = from_timer(priv, t, user_read_timer); @@ -29,30 +53,44 @@ static void user_reader_timeout(struct timer_list *t) pr_warn("TPM user space timeout is deprecated (pid=%d)\n", task_tgid_nr(current)); - schedule_work(&priv->work); + schedule_work(&priv->timeout_work); } -static void timeout_work(struct work_struct *work) +static void tpm_timeout_work(struct work_struct *work) { - struct file_priv *priv = container_of(work, struct file_priv, work); + struct file_priv *priv = container_of(work, struct file_priv, + timeout_work); mutex_lock(&priv->buffer_mutex); priv->data_pending = 0; memset(priv->data_buffer, 0, sizeof(priv->data_buffer)); mutex_unlock(&priv->buffer_mutex); + wake_up_interruptible(&priv->async_wait); } -void tpm_common_open(struct file *file, struct tpm_chip *chip, - struct file_priv *priv, struct tpm_space *space) +int tpm_common_open(struct file *file, struct tpm_chip *chip, + struct file_priv *priv, struct tpm_space *space) { priv->chip = chip; priv->space = space; mutex_init(&priv->buffer_mutex); timer_setup(&priv->user_read_timer, user_reader_timeout, 0); - INIT_WORK(&priv->work, timeout_work); - + INIT_WORK(&priv->timeout_work, tpm_timeout_work); + INIT_WORK(&priv->async_work, tpm_async_work); + init_waitqueue_head(&priv->async_wait); file->private_data = priv; + mutex_lock(&tpm_dev_wq_lock); + if (!tpm_dev_wq) { + tpm_dev_wq = alloc_workqueue("tpm_dev_wq", WQ_MEM_RECLAIM, 0); + if (!tpm_dev_wq) { + mutex_unlock(&tpm_dev_wq_lock); + return -ENOMEM; + } + } + + mutex_unlock(&tpm_dev_wq_lock); + return 0; } ssize_t tpm_common_read(struct file *file, char __user *buf, @@ -63,15 +101,17 @@ ssize_t tpm_common_read(struct file *file, char __user *buf, int rc; del_singleshot_timer_sync(&priv->user_read_timer); - flush_work(&priv->work); + flush_work(&priv->timeout_work); mutex_lock(&priv->buffer_mutex); 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; } @@ -84,10 +124,9 @@ ssize_t tpm_common_write(struct file *file, const char __user *buf, size_t size, loff_t *off) { struct file_priv *priv = file->private_data; - size_t in_size = size; - ssize_t out_size; + int ret = 0; - if (in_size > TPM_BUFSIZE) + if (size > TPM_BUFSIZE) return -E2BIG; mutex_lock(&priv->buffer_mutex); @@ -97,20 +136,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 @@ -118,25 +156,48 @@ 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, priv->space, priv->data_buffer, - sizeof(priv->data_buffer), 0); - tpm_put_ops(priv->chip); - if (out_size < 0) { - mutex_unlock(&priv->buffer_mutex); - return out_size; + /* + * If in nonblocking mode schedule an async job to send + * the command return the size. + * In case of error the err code will be returned in + * the subsequent read call. + */ + if (file->f_flags & O_NONBLOCK) { + queue_work(tpm_dev_wq, &priv->async_work); + return size; } - priv->data_pending = out_size; + ret = tpm_transmit(priv->chip, priv->space, priv->data_buffer, + sizeof(priv->data_buffer), 0); + tpm_put_ops(priv->chip); + + if (ret > 0) { + priv->data_pending = ret; + mod_timer(&priv->user_read_timer, jiffies + (120 * HZ)); + ret = size; + } +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; - /* Set a timeout by which the reader must come claim the result */ - mod_timer(&priv->user_read_timer, jiffies + (120 * HZ)); + poll_wait(file, &priv->async_wait, wait); - return in_size; + if (priv->data_pending) + mask = EPOLLIN | EPOLLRDNORM; + else + mask = EPOLLOUT | EPOLLWRNORM; + + return mask; } /* @@ -144,8 +205,19 @@ ssize_t tpm_common_write(struct file *file, const char __user *buf, */ void tpm_common_release(struct file *file, struct file_priv *priv) { + flush_work(&priv->async_work); del_singleshot_timer_sync(&priv->user_read_timer); - flush_work(&priv->work); + flush_work(&priv->timeout_work); file->private_data = NULL; priv->data_pending = 0; } + +static void __exit tpm_dev_common_exit(void) +{ + if (tpm_dev_wq) { + destroy_workqueue(tpm_dev_wq); + tpm_dev_wq = NULL; + } +} + +__exitcall(tpm_dev_common_exit); diff --git a/drivers/char/tpm/tpm-dev.c b/drivers/char/tpm/tpm-dev.c index 98b9630c3a36..1e353a4f4b7e 100644 --- a/drivers/char/tpm/tpm-dev.c +++ b/drivers/char/tpm/tpm-dev.c @@ -24,6 +24,7 @@ static int tpm_open(struct inode *inode, struct file *file) { struct tpm_chip *chip; struct file_priv *priv; + int rc = -ENOMEM; chip = container_of(inode->i_cdev, struct tpm_chip, cdev); @@ -37,15 +38,21 @@ static int tpm_open(struct inode *inode, struct file *file) priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (priv == NULL) - goto out; + goto out_clear; - tpm_common_open(file, chip, priv, NULL); + rc = tpm_common_open(file, chip, priv, NULL); + if (rc) + goto out_free; return 0; - out: +out_free: + kfree(priv); + +out_clear: clear_bit(0, &chip->is_open); - return -ENOMEM; + + return rc; } /* @@ -68,5 +75,6 @@ const struct file_operations tpm_fops = { .open = tpm_open, .read = tpm_common_read, .write = tpm_common_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 4048677bbd78..fa49c885a2cb 100644 --- a/drivers/char/tpm/tpm-dev.h +++ b/drivers/char/tpm/tpm-dev.h @@ -2,28 +2,32 @@ #ifndef _TPM_DEV_H #define _TPM_DEV_H +#include #include "tpm.h" struct file_priv { struct tpm_chip *chip; struct tpm_space *space; - /* 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 */ - struct work_struct work; + struct work_struct timeout_work; + struct work_struct async_work; + wait_queue_head_t async_wait; u8 data_buffer[TPM_BUFSIZE]; }; -void tpm_common_open(struct file *file, struct tpm_chip *chip, - struct file_priv *priv, struct tpm_space *space); +int tpm_common_open(struct file *file, struct tpm_chip *chip, + struct file_priv *priv, struct tpm_space *space); 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); -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 96006c6b9696..f131c97c1577 100644 --- a/drivers/char/tpm/tpmrm-dev.c +++ b/drivers/char/tpm/tpmrm-dev.c @@ -23,14 +23,22 @@ static int tpmrm_open(struct inode *inode, struct file *file) return -ENOMEM; rc = tpm2_init_space(&priv->space); - if (rc) { - kfree(priv); - return -ENOMEM; - } + if (rc) + goto out_free; - tpm_common_open(file, chip, &priv->priv, &priv->space); + rc = tpm_common_open(file, chip, &priv->priv, &priv->space); + if (rc) + goto out_del_space; return 0; + +out_del_space: + tpm2_del_space(chip, &priv->space); + +out_free: + kfree(priv); + + return rc; } static int tpmrm_release(struct inode *inode, struct file *file) @@ -51,5 +59,6 @@ const struct file_operations tpmrm_fops = { .open = tpmrm_open, .read = tpm_common_read, .write = tpm_common_write, + .poll = tpm_common_poll, .release = tpmrm_release, };