|  | From: Sebastian Andrzej Siewior <bigeasy@linutronix.de> | 
|  | Date: Wed, 4 Oct 2017 10:24:23 +0200 | 
|  | Subject: [PATCH] pci/switchtec: Don't use completion's wait queue | 
|  |  | 
|  | The poll callback is using completion's wait_queue_head_t member and | 
|  | puts it in poll_wait() so the poll() caller gets a wakeup after command | 
|  | completed. This does not work on RT because we don't have a | 
|  | wait_queue_head_t in our completion implementation. Nobody in tree does | 
|  | like that in tree so this is the only driver that breaks. | 
|  |  | 
|  | Instead of using the completion here is waitqueue with a status flag as | 
|  | suggested by Logan. | 
|  |  | 
|  | I don't have the HW so I have no idea if it works as expected, so please | 
|  | test it. | 
|  |  | 
|  | Cc: Kurt Schwemmer <kurt.schwemmer@microsemi.com> | 
|  | Cc: Logan Gunthorpe <logang@deltatee.com> | 
|  | Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> | 
|  | --- | 
|  | drivers/pci/switch/switchtec.c |   22 +++++++++++++--------- | 
|  | 1 file changed, 13 insertions(+), 9 deletions(-) | 
|  |  | 
|  | --- a/drivers/pci/switch/switchtec.c | 
|  | +++ b/drivers/pci/switch/switchtec.c | 
|  | @@ -41,10 +41,11 @@ struct switchtec_user { | 
|  |  | 
|  | enum mrpc_state state; | 
|  |  | 
|  | -	struct completion comp; | 
|  | +	wait_queue_head_t cmd_comp; | 
|  | struct kref kref; | 
|  | struct list_head list; | 
|  |  | 
|  | +	bool cmd_done; | 
|  | u32 cmd; | 
|  | u32 status; | 
|  | u32 return_code; | 
|  | @@ -66,7 +67,7 @@ static struct switchtec_user *stuser_cre | 
|  | stuser->stdev = stdev; | 
|  | kref_init(&stuser->kref); | 
|  | INIT_LIST_HEAD(&stuser->list); | 
|  | -	init_completion(&stuser->comp); | 
|  | +	init_waitqueue_head(&stuser->cmd_comp); | 
|  | stuser->event_cnt = atomic_read(&stdev->event_cnt); | 
|  |  | 
|  | dev_dbg(&stdev->dev, "%s: %p\n", __func__, stuser); | 
|  | @@ -149,7 +150,7 @@ static int mrpc_queue_cmd(struct switcht | 
|  | kref_get(&stuser->kref); | 
|  | stuser->read_len = sizeof(stuser->data); | 
|  | stuser_set_state(stuser, MRPC_QUEUED); | 
|  | -	init_completion(&stuser->comp); | 
|  | +	stuser->cmd_done = false; | 
|  | list_add_tail(&stuser->list, &stdev->mrpc_queue); | 
|  |  | 
|  | mrpc_cmd_submit(stdev); | 
|  | @@ -186,7 +187,8 @@ static void mrpc_complete_cmd(struct swi | 
|  | stuser->read_len); | 
|  |  | 
|  | out: | 
|  | -	complete_all(&stuser->comp); | 
|  | +	stuser->cmd_done = true; | 
|  | +	wake_up_interruptible(&stuser->cmd_comp); | 
|  | list_del_init(&stuser->list); | 
|  | stuser_put(stuser); | 
|  | stdev->mrpc_busy = 0; | 
|  | @@ -456,10 +458,11 @@ static ssize_t switchtec_dev_read(struct | 
|  | mutex_unlock(&stdev->mrpc_mutex); | 
|  |  | 
|  | if (filp->f_flags & O_NONBLOCK) { | 
|  | -		if (!try_wait_for_completion(&stuser->comp)) | 
|  | +		if (!READ_ONCE(stuser->cmd_done)) | 
|  | return -EAGAIN; | 
|  | } else { | 
|  | -		rc = wait_for_completion_interruptible(&stuser->comp); | 
|  | +		rc = wait_event_interruptible(stuser->cmd_comp, | 
|  | +					      stuser->cmd_done); | 
|  | if (rc < 0) | 
|  | return rc; | 
|  | } | 
|  | @@ -507,7 +510,7 @@ static __poll_t switchtec_dev_poll(struc | 
|  | struct switchtec_dev *stdev = stuser->stdev; | 
|  | __poll_t ret = 0; | 
|  |  | 
|  | -	poll_wait(filp, &stuser->comp.wait, wait); | 
|  | +	poll_wait(filp, &stuser->cmd_comp, wait); | 
|  | poll_wait(filp, &stdev->event_wq, wait); | 
|  |  | 
|  | if (lock_mutex_and_test_alive(stdev)) | 
|  | @@ -515,7 +518,7 @@ static __poll_t switchtec_dev_poll(struc | 
|  |  | 
|  | mutex_unlock(&stdev->mrpc_mutex); | 
|  |  | 
|  | -	if (try_wait_for_completion(&stuser->comp)) | 
|  | +	if (READ_ONCE(stuser->cmd_done)) | 
|  | ret |= EPOLLIN | EPOLLRDNORM; | 
|  |  | 
|  | if (stuser->event_cnt != atomic_read(&stdev->event_cnt)) | 
|  | @@ -1034,7 +1037,8 @@ static void stdev_kill(struct switchtec_ | 
|  |  | 
|  | /* Wake up and kill any users waiting on an MRPC request */ | 
|  | list_for_each_entry_safe(stuser, tmpuser, &stdev->mrpc_queue, list) { | 
|  | -		complete_all(&stuser->comp); | 
|  | +		stuser->cmd_done = true; | 
|  | +		wake_up_interruptible(&stuser->cmd_comp); | 
|  | list_del_init(&stuser->list); | 
|  | stuser_put(stuser); | 
|  | } |