s390/qeth: don't poll for cmd IO completion
All callers are running in process context now, so we can safely sleep in qeth_send_control_data() while waiting for a cmd to complete. Signed-off-by: Julian Wiedmann <jwi@linux.ibm.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
df2a2a5225
commit
782e4a7921
@ -10,6 +10,7 @@
|
|||||||
#ifndef __QETH_CORE_H__
|
#ifndef __QETH_CORE_H__
|
||||||
#define __QETH_CORE_H__
|
#define __QETH_CORE_H__
|
||||||
|
|
||||||
|
#include <linux/completion.h>
|
||||||
#include <linux/if.h>
|
#include <linux/if.h>
|
||||||
#include <linux/if_arp.h>
|
#include <linux/if_arp.h>
|
||||||
#include <linux/etherdevice.h>
|
#include <linux/etherdevice.h>
|
||||||
@ -21,6 +22,7 @@
|
|||||||
#include <linux/hashtable.h>
|
#include <linux/hashtable.h>
|
||||||
#include <linux/ip.h>
|
#include <linux/ip.h>
|
||||||
#include <linux/refcount.h>
|
#include <linux/refcount.h>
|
||||||
|
#include <linux/wait.h>
|
||||||
#include <linux/workqueue.h>
|
#include <linux/workqueue.h>
|
||||||
|
|
||||||
#include <net/ipv6.h>
|
#include <net/ipv6.h>
|
||||||
@ -585,6 +587,7 @@ struct qeth_cmd_buffer {
|
|||||||
enum qeth_cmd_buffer_state state;
|
enum qeth_cmd_buffer_state state;
|
||||||
struct qeth_channel *channel;
|
struct qeth_channel *channel;
|
||||||
struct qeth_reply *reply;
|
struct qeth_reply *reply;
|
||||||
|
long timeout;
|
||||||
unsigned char *data;
|
unsigned char *data;
|
||||||
void (*callback)(struct qeth_card *card, struct qeth_channel *channel,
|
void (*callback)(struct qeth_card *card, struct qeth_channel *channel,
|
||||||
struct qeth_cmd_buffer *iob);
|
struct qeth_cmd_buffer *iob);
|
||||||
@ -610,6 +613,11 @@ struct qeth_channel {
|
|||||||
int io_buf_no;
|
int io_buf_no;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static inline bool qeth_trylock_channel(struct qeth_channel *channel)
|
||||||
|
{
|
||||||
|
return atomic_cmpxchg(&channel->irq_pending, 0, 1) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* OSA card related definitions
|
* OSA card related definitions
|
||||||
*/
|
*/
|
||||||
@ -636,12 +644,11 @@ struct qeth_seqno {
|
|||||||
|
|
||||||
struct qeth_reply {
|
struct qeth_reply {
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
wait_queue_head_t wait_q;
|
struct completion received;
|
||||||
int (*callback)(struct qeth_card *, struct qeth_reply *,
|
int (*callback)(struct qeth_card *, struct qeth_reply *,
|
||||||
unsigned long);
|
unsigned long);
|
||||||
u32 seqno;
|
u32 seqno;
|
||||||
unsigned long offset;
|
unsigned long offset;
|
||||||
atomic_t received;
|
|
||||||
int rc;
|
int rc;
|
||||||
void *param;
|
void *param;
|
||||||
refcount_t refcnt;
|
refcount_t refcnt;
|
||||||
|
@ -542,11 +542,10 @@ static struct qeth_reply *qeth_alloc_reply(struct qeth_card *card)
|
|||||||
{
|
{
|
||||||
struct qeth_reply *reply;
|
struct qeth_reply *reply;
|
||||||
|
|
||||||
reply = kzalloc(sizeof(struct qeth_reply), GFP_ATOMIC);
|
reply = kzalloc(sizeof(*reply), GFP_KERNEL);
|
||||||
if (reply) {
|
if (reply) {
|
||||||
refcount_set(&reply->refcnt, 1);
|
refcount_set(&reply->refcnt, 1);
|
||||||
atomic_set(&reply->received, 0);
|
init_completion(&reply->received);
|
||||||
init_waitqueue_head(&reply->wait_q);
|
|
||||||
}
|
}
|
||||||
return reply;
|
return reply;
|
||||||
}
|
}
|
||||||
@ -578,8 +577,7 @@ static void qeth_dequeue_reply(struct qeth_card *card, struct qeth_reply *reply)
|
|||||||
|
|
||||||
static void qeth_notify_reply(struct qeth_reply *reply)
|
static void qeth_notify_reply(struct qeth_reply *reply)
|
||||||
{
|
{
|
||||||
atomic_inc(&reply->received);
|
complete(&reply->received);
|
||||||
wake_up(&reply->wait_q);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qeth_issue_ipa_msg(struct qeth_ipa_cmd *cmd, int rc,
|
static void qeth_issue_ipa_msg(struct qeth_ipa_cmd *cmd, int rc,
|
||||||
@ -704,6 +702,7 @@ static struct qeth_cmd_buffer *__qeth_get_buffer(struct qeth_channel *channel)
|
|||||||
do {
|
do {
|
||||||
if (channel->iob[index].state == BUF_STATE_FREE) {
|
if (channel->iob[index].state == BUF_STATE_FREE) {
|
||||||
channel->iob[index].state = BUF_STATE_LOCKED;
|
channel->iob[index].state = BUF_STATE_LOCKED;
|
||||||
|
channel->iob[index].timeout = QETH_TIMEOUT;
|
||||||
channel->io_buf_no = (channel->io_buf_no + 1) %
|
channel->io_buf_no = (channel->io_buf_no + 1) %
|
||||||
QETH_CMD_BUFFER_NO;
|
QETH_CMD_BUFFER_NO;
|
||||||
memset(channel->iob[index].data, 0, QETH_BUFSIZE);
|
memset(channel->iob[index].data, 0, QETH_BUFSIZE);
|
||||||
@ -1786,8 +1785,7 @@ static int qeth_idx_activate_get_answer(struct qeth_card *card,
|
|||||||
iob->callback = reply_cb;
|
iob->callback = reply_cb;
|
||||||
qeth_setup_ccw(channel->ccw, CCW_CMD_READ, QETH_BUFSIZE, iob->data);
|
qeth_setup_ccw(channel->ccw, CCW_CMD_READ, QETH_BUFSIZE, iob->data);
|
||||||
|
|
||||||
wait_event(card->wait_q,
|
wait_event(card->wait_q, qeth_trylock_channel(channel));
|
||||||
atomic_cmpxchg(&channel->irq_pending, 0, 1) == 0);
|
|
||||||
QETH_DBF_TEXT(SETUP, 6, "noirqpnd");
|
QETH_DBF_TEXT(SETUP, 6, "noirqpnd");
|
||||||
spin_lock_irq(get_ccwdev_lock(channel->ccwdev));
|
spin_lock_irq(get_ccwdev_lock(channel->ccwdev));
|
||||||
rc = ccw_device_start_timeout(channel->ccwdev, channel->ccw,
|
rc = ccw_device_start_timeout(channel->ccwdev, channel->ccw,
|
||||||
@ -1855,8 +1853,7 @@ static int qeth_idx_activate_channel(struct qeth_card *card,
|
|||||||
temp = (card->info.cula << 8) + card->info.unit_addr2;
|
temp = (card->info.cula << 8) + card->info.unit_addr2;
|
||||||
memcpy(QETH_IDX_ACT_QDIO_DEV_REALADDR(iob->data), &temp, 2);
|
memcpy(QETH_IDX_ACT_QDIO_DEV_REALADDR(iob->data), &temp, 2);
|
||||||
|
|
||||||
wait_event(card->wait_q,
|
wait_event(card->wait_q, qeth_trylock_channel(channel));
|
||||||
atomic_cmpxchg(&channel->irq_pending, 0, 1) == 0);
|
|
||||||
QETH_DBF_TEXT(SETUP, 6, "noirqpnd");
|
QETH_DBF_TEXT(SETUP, 6, "noirqpnd");
|
||||||
spin_lock_irq(get_ccwdev_lock(channel->ccwdev));
|
spin_lock_irq(get_ccwdev_lock(channel->ccwdev));
|
||||||
rc = ccw_device_start_timeout(channel->ccwdev, channel->ccw,
|
rc = ccw_device_start_timeout(channel->ccwdev, channel->ccw,
|
||||||
@ -2034,9 +2031,9 @@ static int qeth_send_control_data(struct qeth_card *card, int len,
|
|||||||
void *reply_param)
|
void *reply_param)
|
||||||
{
|
{
|
||||||
struct qeth_channel *channel = iob->channel;
|
struct qeth_channel *channel = iob->channel;
|
||||||
|
long timeout = iob->timeout;
|
||||||
int rc;
|
int rc;
|
||||||
struct qeth_reply *reply = NULL;
|
struct qeth_reply *reply = NULL;
|
||||||
unsigned long timeout, event_timeout;
|
|
||||||
struct qeth_ipa_cmd *cmd = NULL;
|
struct qeth_ipa_cmd *cmd = NULL;
|
||||||
|
|
||||||
QETH_CARD_TEXT(card, 2, "sendctl");
|
QETH_CARD_TEXT(card, 2, "sendctl");
|
||||||
@ -2057,27 +2054,30 @@ static int qeth_send_control_data(struct qeth_card *card, int len,
|
|||||||
qeth_get_reply(reply);
|
qeth_get_reply(reply);
|
||||||
iob->reply = reply;
|
iob->reply = reply;
|
||||||
|
|
||||||
while (atomic_cmpxchg(&channel->irq_pending, 0, 1)) ;
|
timeout = wait_event_interruptible_timeout(card->wait_q,
|
||||||
|
qeth_trylock_channel(channel),
|
||||||
|
timeout);
|
||||||
|
if (timeout <= 0) {
|
||||||
|
qeth_put_reply(reply);
|
||||||
|
qeth_release_buffer(channel, iob);
|
||||||
|
return (timeout == -ERESTARTSYS) ? -EINTR : -ETIME;
|
||||||
|
}
|
||||||
|
|
||||||
if (IS_IPA(iob->data)) {
|
if (IS_IPA(iob->data)) {
|
||||||
cmd = __ipa_cmd(iob);
|
cmd = __ipa_cmd(iob);
|
||||||
cmd->hdr.seqno = card->seqno.ipa++;
|
cmd->hdr.seqno = card->seqno.ipa++;
|
||||||
reply->seqno = cmd->hdr.seqno;
|
reply->seqno = cmd->hdr.seqno;
|
||||||
event_timeout = QETH_IPA_TIMEOUT;
|
|
||||||
} else {
|
} else {
|
||||||
reply->seqno = QETH_IDX_COMMAND_SEQNO;
|
reply->seqno = QETH_IDX_COMMAND_SEQNO;
|
||||||
event_timeout = QETH_TIMEOUT;
|
|
||||||
}
|
}
|
||||||
qeth_prepare_control_data(card, len, iob);
|
qeth_prepare_control_data(card, len, iob);
|
||||||
|
|
||||||
qeth_enqueue_reply(card, reply);
|
qeth_enqueue_reply(card, reply);
|
||||||
|
|
||||||
timeout = jiffies + event_timeout;
|
|
||||||
|
|
||||||
QETH_CARD_TEXT(card, 6, "noirqpnd");
|
QETH_CARD_TEXT(card, 6, "noirqpnd");
|
||||||
spin_lock_irq(get_ccwdev_lock(channel->ccwdev));
|
spin_lock_irq(get_ccwdev_lock(channel->ccwdev));
|
||||||
rc = ccw_device_start_timeout(channel->ccwdev, channel->ccw,
|
rc = ccw_device_start_timeout(channel->ccwdev, channel->ccw,
|
||||||
(addr_t) iob, 0, 0, event_timeout);
|
(addr_t) iob, 0, 0, timeout);
|
||||||
spin_unlock_irq(get_ccwdev_lock(channel->ccwdev));
|
spin_unlock_irq(get_ccwdev_lock(channel->ccwdev));
|
||||||
if (rc) {
|
if (rc) {
|
||||||
QETH_DBF_MESSAGE(2, "qeth_send_control_data on device %x: ccw_device_start rc = %i\n",
|
QETH_DBF_MESSAGE(2, "qeth_send_control_data on device %x: ccw_device_start rc = %i\n",
|
||||||
@ -2091,30 +2091,16 @@ static int qeth_send_control_data(struct qeth_card *card, int len,
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* we have only one long running ipassist, since we can ensure
|
timeout = wait_for_completion_interruptible_timeout(&reply->received,
|
||||||
process context of this command we can sleep */
|
timeout);
|
||||||
if (cmd && cmd->hdr.command == IPA_CMD_SETIP &&
|
if (timeout <= 0)
|
||||||
cmd->hdr.prot_version == QETH_PROT_IPV4) {
|
rc = (timeout == -ERESTARTSYS) ? -EINTR : -ETIME;
|
||||||
if (!wait_event_timeout(reply->wait_q,
|
|
||||||
atomic_read(&reply->received), event_timeout))
|
|
||||||
goto time_err;
|
|
||||||
} else {
|
|
||||||
while (!atomic_read(&reply->received)) {
|
|
||||||
if (time_after(jiffies, timeout))
|
|
||||||
goto time_err;
|
|
||||||
cpu_relax();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
qeth_dequeue_reply(card, reply);
|
qeth_dequeue_reply(card, reply);
|
||||||
rc = reply->rc;
|
if (!rc)
|
||||||
|
rc = reply->rc;
|
||||||
qeth_put_reply(reply);
|
qeth_put_reply(reply);
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
time_err:
|
|
||||||
qeth_dequeue_reply(card, reply);
|
|
||||||
qeth_put_reply(reply);
|
|
||||||
return -ETIME;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int qeth_cm_enable_cb(struct qeth_card *card, struct qeth_reply *reply,
|
static int qeth_cm_enable_cb(struct qeth_card *card, struct qeth_reply *reply,
|
||||||
@ -2810,6 +2796,8 @@ void qeth_prepare_ipa_cmd(struct qeth_card *card, struct qeth_cmd_buffer *iob,
|
|||||||
u16 total_length = IPA_PDU_HEADER_SIZE + cmd_length;
|
u16 total_length = IPA_PDU_HEADER_SIZE + cmd_length;
|
||||||
u8 prot_type = qeth_mpc_select_prot_type(card);
|
u8 prot_type = qeth_mpc_select_prot_type(card);
|
||||||
|
|
||||||
|
iob->timeout = QETH_IPA_TIMEOUT;
|
||||||
|
|
||||||
memcpy(iob->data, IPA_PDU_HEADER, IPA_PDU_HEADER_SIZE);
|
memcpy(iob->data, IPA_PDU_HEADER, IPA_PDU_HEADER_SIZE);
|
||||||
memcpy(QETH_IPA_PDU_LEN_TOTAL(iob->data), &total_length, 2);
|
memcpy(QETH_IPA_PDU_LEN_TOTAL(iob->data), &total_length, 2);
|
||||||
memcpy(QETH_IPA_CMD_PROT_TYPE(iob->data), &prot_type, 1);
|
memcpy(QETH_IPA_CMD_PROT_TYPE(iob->data), &prot_type, 1);
|
||||||
|
@ -1050,13 +1050,12 @@ static int qeth_osn_send_control_data(struct qeth_card *card, int len,
|
|||||||
|
|
||||||
QETH_CARD_TEXT(card, 5, "osndctrd");
|
QETH_CARD_TEXT(card, 5, "osndctrd");
|
||||||
|
|
||||||
wait_event(card->wait_q,
|
wait_event(card->wait_q, qeth_trylock_channel(channel));
|
||||||
atomic_cmpxchg(&channel->irq_pending, 0, 1) == 0);
|
|
||||||
qeth_prepare_control_data(card, len, iob);
|
qeth_prepare_control_data(card, len, iob);
|
||||||
QETH_CARD_TEXT(card, 6, "osnoirqp");
|
QETH_CARD_TEXT(card, 6, "osnoirqp");
|
||||||
spin_lock_irq(get_ccwdev_lock(channel->ccwdev));
|
spin_lock_irq(get_ccwdev_lock(channel->ccwdev));
|
||||||
rc = ccw_device_start_timeout(channel->ccwdev, channel->ccw,
|
rc = ccw_device_start_timeout(channel->ccwdev, channel->ccw,
|
||||||
(addr_t) iob, 0, 0, QETH_IPA_TIMEOUT);
|
(addr_t) iob, 0, 0, iob->timeout);
|
||||||
spin_unlock_irq(get_ccwdev_lock(channel->ccwdev));
|
spin_unlock_irq(get_ccwdev_lock(channel->ccwdev));
|
||||||
if (rc) {
|
if (rc) {
|
||||||
QETH_DBF_MESSAGE(2, "qeth_osn_send_control_data: "
|
QETH_DBF_MESSAGE(2, "qeth_osn_send_control_data: "
|
||||||
|
Loading…
Reference in New Issue
Block a user