Skip to content

Commit 9e1b74a

Browse files
tstrukJarkko Sakkinen
authored andcommitted
tpm: add support for nonblocking operation
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. Tested-by: Philip Tricca <philip.b.tricca@intel.com> Signed-off-by: Tadeusz Struk <tadeusz.struk@intel.com> Reviewed-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com> Signed-off--by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
1 parent c3d477a commit 9e1b74a

File tree

6 files changed

+137
-45
lines changed

6 files changed

+137
-45
lines changed

drivers/char/tpm/tpm-dev-common.c

Lines changed: 105 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -17,29 +17,56 @@
1717
* License.
1818
*
1919
*/
20+
#include <linux/poll.h>
2021
#include <linux/slab.h>
2122
#include <linux/uaccess.h>
23+
#include <linux/workqueue.h>
2224
#include "tpm.h"
2325
#include "tpm-dev.h"
2426

27+
static struct workqueue_struct *tpm_dev_wq;
28+
static DEFINE_MUTEX(tpm_dev_wq_lock);
29+
30+
static void tpm_async_work(struct work_struct *work)
31+
{
32+
struct file_priv *priv =
33+
container_of(work, struct file_priv, async_work);
34+
ssize_t ret;
35+
36+
mutex_lock(&priv->buffer_mutex);
37+
priv->command_enqueued = false;
38+
ret = tpm_transmit(priv->chip, priv->space, priv->data_buffer,
39+
sizeof(priv->data_buffer), 0);
40+
41+
tpm_put_ops(priv->chip);
42+
if (ret > 0) {
43+
priv->data_pending = ret;
44+
mod_timer(&priv->user_read_timer, jiffies + (120 * HZ));
45+
}
46+
mutex_unlock(&priv->buffer_mutex);
47+
wake_up_interruptible(&priv->async_wait);
48+
}
49+
2550
static void user_reader_timeout(struct timer_list *t)
2651
{
2752
struct file_priv *priv = from_timer(priv, t, user_read_timer);
2853

2954
pr_warn("TPM user space timeout is deprecated (pid=%d)\n",
3055
task_tgid_nr(current));
3156

32-
schedule_work(&priv->work);
57+
schedule_work(&priv->timeout_work);
3358
}
3459

35-
static void timeout_work(struct work_struct *work)
60+
static void tpm_timeout_work(struct work_struct *work)
3661
{
37-
struct file_priv *priv = container_of(work, struct file_priv, work);
62+
struct file_priv *priv = container_of(work, struct file_priv,
63+
timeout_work);
3864

3965
mutex_lock(&priv->buffer_mutex);
4066
priv->data_pending = 0;
4167
memset(priv->data_buffer, 0, sizeof(priv->data_buffer));
4268
mutex_unlock(&priv->buffer_mutex);
69+
wake_up_interruptible(&priv->async_wait);
4370
}
4471

4572
void tpm_common_open(struct file *file, struct tpm_chip *chip,
@@ -50,8 +77,9 @@ void tpm_common_open(struct file *file, struct tpm_chip *chip,
5077

5178
mutex_init(&priv->buffer_mutex);
5279
timer_setup(&priv->user_read_timer, user_reader_timeout, 0);
53-
INIT_WORK(&priv->work, timeout_work);
54-
80+
INIT_WORK(&priv->timeout_work, tpm_timeout_work);
81+
INIT_WORK(&priv->async_work, tpm_async_work);
82+
init_waitqueue_head(&priv->async_wait);
5583
file->private_data = priv;
5684
}
5785

@@ -63,15 +91,17 @@ ssize_t tpm_common_read(struct file *file, char __user *buf,
6391
int rc;
6492

6593
del_singleshot_timer_sync(&priv->user_read_timer);
66-
flush_work(&priv->work);
94+
flush_work(&priv->timeout_work);
6795
mutex_lock(&priv->buffer_mutex);
6896

6997
if (priv->data_pending) {
7098
ret_size = min_t(ssize_t, size, priv->data_pending);
71-
rc = copy_to_user(buf, priv->data_buffer, ret_size);
72-
memset(priv->data_buffer, 0, priv->data_pending);
73-
if (rc)
74-
ret_size = -EFAULT;
99+
if (ret_size > 0) {
100+
rc = copy_to_user(buf, priv->data_buffer, ret_size);
101+
memset(priv->data_buffer, 0, priv->data_pending);
102+
if (rc)
103+
ret_size = -EFAULT;
104+
}
75105

76106
priv->data_pending = 0;
77107
}
@@ -84,10 +114,9 @@ ssize_t tpm_common_write(struct file *file, const char __user *buf,
84114
size_t size, loff_t *off)
85115
{
86116
struct file_priv *priv = file->private_data;
87-
size_t in_size = size;
88-
ssize_t out_size;
117+
int ret = 0;
89118

90-
if (in_size > TPM_BUFSIZE)
119+
if (size > TPM_BUFSIZE)
91120
return -E2BIG;
92121

93122
mutex_lock(&priv->buffer_mutex);
@@ -96,56 +125,96 @@ ssize_t tpm_common_write(struct file *file, const char __user *buf,
96125
* tpm_read or a user_read_timer timeout. This also prevents split
97126
* buffered writes from blocking here.
98127
*/
99-
if (priv->data_pending != 0) {
100-
mutex_unlock(&priv->buffer_mutex);
101-
return -EBUSY;
128+
if (priv->data_pending != 0 || priv->command_enqueued) {
129+
ret = -EBUSY;
130+
goto out;
102131
}
103132

104-
if (copy_from_user
105-
(priv->data_buffer, (void __user *) buf, in_size)) {
106-
mutex_unlock(&priv->buffer_mutex);
107-
return -EFAULT;
133+
if (copy_from_user(priv->data_buffer, buf, size)) {
134+
ret = -EFAULT;
135+
goto out;
108136
}
109137

110-
if (in_size < 6 ||
111-
in_size < be32_to_cpu(*((__be32 *) (priv->data_buffer + 2)))) {
112-
mutex_unlock(&priv->buffer_mutex);
113-
return -EINVAL;
138+
if (size < 6 ||
139+
size < be32_to_cpu(*((__be32 *)(priv->data_buffer + 2)))) {
140+
ret = -EINVAL;
141+
goto out;
114142
}
115143

116144
/* atomic tpm command send and result receive. We only hold the ops
117145
* lock during this period so that the tpm can be unregistered even if
118146
* the char dev is held open.
119147
*/
120148
if (tpm_try_get_ops(priv->chip)) {
121-
mutex_unlock(&priv->buffer_mutex);
122-
return -EPIPE;
149+
ret = -EPIPE;
150+
goto out;
123151
}
124-
out_size = tpm_transmit(priv->chip, priv->space, priv->data_buffer,
125-
sizeof(priv->data_buffer), 0);
126152

127-
tpm_put_ops(priv->chip);
128-
if (out_size < 0) {
153+
/*
154+
* If in nonblocking mode schedule an async job to send
155+
* the command return the size.
156+
* In case of error the err code will be returned in
157+
* the subsequent read call.
158+
*/
159+
if (file->f_flags & O_NONBLOCK) {
160+
priv->command_enqueued = true;
161+
queue_work(tpm_dev_wq, &priv->async_work);
129162
mutex_unlock(&priv->buffer_mutex);
130-
return out_size;
163+
return size;
131164
}
132165

133-
priv->data_pending = out_size;
166+
ret = tpm_transmit(priv->chip, priv->space, priv->data_buffer,
167+
sizeof(priv->data_buffer), 0);
168+
tpm_put_ops(priv->chip);
169+
170+
if (ret > 0) {
171+
priv->data_pending = ret;
172+
mod_timer(&priv->user_read_timer, jiffies + (120 * HZ));
173+
ret = size;
174+
}
175+
out:
134176
mutex_unlock(&priv->buffer_mutex);
177+
return ret;
178+
}
179+
180+
__poll_t tpm_common_poll(struct file *file, poll_table *wait)
181+
{
182+
struct file_priv *priv = file->private_data;
183+
__poll_t mask = 0;
184+
185+
poll_wait(file, &priv->async_wait, wait);
135186

136-
/* Set a timeout by which the reader must come claim the result */
137-
mod_timer(&priv->user_read_timer, jiffies + (120 * HZ));
187+
if (priv->data_pending)
188+
mask = EPOLLIN | EPOLLRDNORM;
189+
else
190+
mask = EPOLLOUT | EPOLLWRNORM;
138191

139-
return in_size;
192+
return mask;
140193
}
141194

142195
/*
143196
* Called on file close
144197
*/
145198
void tpm_common_release(struct file *file, struct file_priv *priv)
146199
{
200+
flush_work(&priv->async_work);
147201
del_singleshot_timer_sync(&priv->user_read_timer);
148-
flush_work(&priv->work);
202+
flush_work(&priv->timeout_work);
149203
file->private_data = NULL;
150204
priv->data_pending = 0;
151205
}
206+
207+
int __init tpm_dev_common_init(void)
208+
{
209+
tpm_dev_wq = alloc_workqueue("tpm_dev_wq", WQ_MEM_RECLAIM, 0);
210+
211+
return !tpm_dev_wq ? -ENOMEM : 0;
212+
}
213+
214+
void __exit tpm_dev_common_exit(void)
215+
{
216+
if (tpm_dev_wq) {
217+
destroy_workqueue(tpm_dev_wq);
218+
tpm_dev_wq = NULL;
219+
}
220+
}

drivers/char/tpm/tpm-dev.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,5 +68,6 @@ const struct file_operations tpm_fops = {
6868
.open = tpm_open,
6969
.read = tpm_common_read,
7070
.write = tpm_common_write,
71+
.poll = tpm_common_poll,
7172
.release = tpm_release,
7273
};

drivers/char/tpm/tpm-dev.h

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,22 @@
22
#ifndef _TPM_DEV_H
33
#define _TPM_DEV_H
44

5+
#include <linux/poll.h>
56
#include "tpm.h"
67

78
struct file_priv {
89
struct tpm_chip *chip;
910
struct tpm_space *space;
1011

11-
/* Data passed to and from the tpm via the read/write calls */
12-
size_t data_pending;
12+
/* Holds the amount of data passed or an error code from async op */
13+
ssize_t data_pending;
1314
struct mutex buffer_mutex;
1415

1516
struct timer_list user_read_timer; /* user needs to claim result */
16-
struct work_struct work;
17+
struct work_struct timeout_work;
18+
struct work_struct async_work;
19+
wait_queue_head_t async_wait;
20+
bool command_enqueued;
1721

1822
u8 data_buffer[TPM_BUFSIZE];
1923
};
@@ -24,6 +28,7 @@ ssize_t tpm_common_read(struct file *file, char __user *buf,
2428
size_t size, loff_t *off);
2529
ssize_t tpm_common_write(struct file *file, const char __user *buf,
2630
size_t size, loff_t *off);
27-
void tpm_common_release(struct file *file, struct file_priv *priv);
31+
__poll_t tpm_common_poll(struct file *file, poll_table *wait);
2832

33+
void tpm_common_release(struct file *file, struct file_priv *priv);
2934
#endif

drivers/char/tpm/tpm-interface.c

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1409,19 +1409,32 @@ static int __init tpm_init(void)
14091409
tpmrm_class = class_create(THIS_MODULE, "tpmrm");
14101410
if (IS_ERR(tpmrm_class)) {
14111411
pr_err("couldn't create tpmrm class\n");
1412-
class_destroy(tpm_class);
1413-
return PTR_ERR(tpmrm_class);
1412+
rc = PTR_ERR(tpmrm_class);
1413+
goto out_destroy_tpm_class;
14141414
}
14151415

14161416
rc = alloc_chrdev_region(&tpm_devt, 0, 2*TPM_NUM_DEVICES, "tpm");
14171417
if (rc < 0) {
14181418
pr_err("tpm: failed to allocate char dev region\n");
1419-
class_destroy(tpmrm_class);
1420-
class_destroy(tpm_class);
1421-
return rc;
1419+
goto out_destroy_tpmrm_class;
1420+
}
1421+
1422+
rc = tpm_dev_common_init();
1423+
if (rc) {
1424+
pr_err("tpm: failed to allocate char dev region\n");
1425+
goto out_unreg_chrdev;
14221426
}
14231427

14241428
return 0;
1429+
1430+
out_unreg_chrdev:
1431+
unregister_chrdev_region(tpm_devt, 2 * TPM_NUM_DEVICES);
1432+
out_destroy_tpmrm_class:
1433+
class_destroy(tpmrm_class);
1434+
out_destroy_tpm_class:
1435+
class_destroy(tpm_class);
1436+
1437+
return rc;
14251438
}
14261439

14271440
static void __exit tpm_exit(void)
@@ -1430,6 +1443,7 @@ static void __exit tpm_exit(void)
14301443
class_destroy(tpm_class);
14311444
class_destroy(tpmrm_class);
14321445
unregister_chrdev_region(tpm_devt, 2*TPM_NUM_DEVICES);
1446+
tpm_dev_common_exit();
14331447
}
14341448

14351449
subsys_initcall(tpm_init);

drivers/char/tpm/tpm.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -604,4 +604,6 @@ int tpm2_commit_space(struct tpm_chip *chip, struct tpm_space *space,
604604

605605
int tpm_bios_log_setup(struct tpm_chip *chip);
606606
void tpm_bios_log_teardown(struct tpm_chip *chip);
607+
int tpm_dev_common_init(void);
608+
void tpm_dev_common_exit(void);
607609
#endif

drivers/char/tpm/tpmrm-dev.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,5 +51,6 @@ const struct file_operations tpmrm_fops = {
5151
.open = tpmrm_open,
5252
.read = tpm_common_read,
5353
.write = tpm_common_write,
54+
.poll = tpm_common_poll,
5455
.release = tpmrm_release,
5556
};

0 commit comments

Comments
 (0)