Commit c63d11ba authored by David S. Miller's avatar David S. Miller

Merge branch 's390-next'

Julian Wiedmann says:

====================
s390/qeth: updates 2019-03-28

please apply the following patchset to net-next. This reworks the control
IO code in qeth so that we no longer need to poll for cmd completion,
and refactors the IDX setup code to also use this improved IO path.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 1571e2fd 2e873d10
......@@ -10,6 +10,7 @@
#ifndef __QETH_CORE_H__
#define __QETH_CORE_H__
#include <linux/completion.h>
#include <linux/if.h>
#include <linux/if_arp.h>
#include <linux/etherdevice.h>
......@@ -21,6 +22,7 @@
#include <linux/hashtable.h>
#include <linux/ip.h>
#include <linux/refcount.h>
#include <linux/wait.h>
#include <linux/workqueue.h>
#include <net/ipv6.h>
......@@ -538,7 +540,6 @@ struct qeth_qdio_info {
enum qeth_channel_states {
CH_STATE_UP,
CH_STATE_DOWN,
CH_STATE_ACTIVATING,
CH_STATE_HALTED,
CH_STATE_STOPPED,
CH_STATE_RCD,
......@@ -585,7 +586,10 @@ struct qeth_cmd_buffer {
enum qeth_cmd_buffer_state state;
struct qeth_channel *channel;
struct qeth_reply *reply;
long timeout;
unsigned char *data;
void (*finalize)(struct qeth_card *card, struct qeth_cmd_buffer *iob,
unsigned int length);
void (*callback)(struct qeth_card *card, struct qeth_channel *channel,
struct qeth_cmd_buffer *iob);
};
......@@ -610,6 +614,11 @@ struct qeth_channel {
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
*/
......@@ -636,12 +645,11 @@ struct qeth_seqno {
struct qeth_reply {
struct list_head list;
wait_queue_head_t wait_q;
struct completion received;
int (*callback)(struct qeth_card *, struct qeth_reply *,
unsigned long);
u32 seqno;
unsigned long offset;
atomic_t received;
int rc;
void *param;
refcount_t refcnt;
......@@ -774,18 +782,19 @@ struct qeth_card {
struct qeth_card_options options;
struct workqueue_struct *event_wq;
struct workqueue_struct *cmd_wq;
wait_queue_head_t wait_q;
spinlock_t mclock;
unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
DECLARE_HASHTABLE(mac_htable, 4);
DECLARE_HASHTABLE(ip_htable, 4);
struct mutex ip_lock;
DECLARE_HASHTABLE(ip_mc_htable, 4);
struct work_struct rx_mode_work;
struct work_struct kernel_thread_starter;
spinlock_t thread_mask_lock;
unsigned long thread_start_mask;
unsigned long thread_allowed_mask;
unsigned long thread_running_mask;
spinlock_t ip_lock;
struct qeth_ipato ipato;
struct list_head cmd_waiter_list;
/* QDIO buffer handling */
......@@ -983,8 +992,6 @@ void qeth_clear_qdio_buffers(struct qeth_card *);
void qeth_setadp_promisc_mode(struct qeth_card *);
int qeth_setadpparms_change_macaddr(struct qeth_card *);
void qeth_tx_timeout(struct net_device *);
void qeth_prepare_control_data(struct qeth_card *, int,
struct qeth_cmd_buffer *);
void qeth_release_buffer(struct qeth_channel *, struct qeth_cmd_buffer *);
void qeth_prepare_ipa_cmd(struct qeth_card *card, struct qeth_cmd_buffer *iob,
u16 cmd_length);
......
......@@ -61,9 +61,9 @@ static struct kmem_cache *qeth_qdio_outbuf_cache;
static struct device *qeth_core_root_dev;
static struct lock_class_key qdio_out_skb_queue_key;
static void qeth_send_control_data_cb(struct qeth_card *card,
struct qeth_channel *channel,
struct qeth_cmd_buffer *iob);
static void qeth_issue_next_read_cb(struct qeth_card *card,
struct qeth_channel *channel,
struct qeth_cmd_buffer *iob);
static struct qeth_cmd_buffer *qeth_get_buffer(struct qeth_channel *);
static void qeth_free_buffer_pool(struct qeth_card *);
static int qeth_qdio_establish(struct qeth_card *);
......@@ -511,7 +511,9 @@ static int __qeth_issue_next_read(struct qeth_card *card)
CARD_DEVID(card));
return -ENOMEM;
}
qeth_setup_ccw(channel->ccw, CCW_CMD_READ, QETH_BUFSIZE, iob->data);
iob->callback = qeth_issue_next_read_cb;
QETH_CARD_TEXT(card, 6, "noirqpnd");
rc = ccw_device_start(channel->ccwdev, channel->ccw,
(addr_t) iob, 0, 0);
......@@ -542,11 +544,10 @@ static struct qeth_reply *qeth_alloc_reply(struct qeth_card *card)
{
struct qeth_reply *reply;
reply = kzalloc(sizeof(struct qeth_reply), GFP_ATOMIC);
reply = kzalloc(sizeof(*reply), GFP_KERNEL);
if (reply) {
refcount_set(&reply->refcnt, 1);
atomic_set(&reply->received, 0);
init_waitqueue_head(&reply->wait_q);
init_completion(&reply->received);
}
return reply;
}
......@@ -576,10 +577,10 @@ static void qeth_dequeue_reply(struct qeth_card *card, struct qeth_reply *reply)
spin_unlock_irq(&card->lock);
}
static void qeth_notify_reply(struct qeth_reply *reply)
static void qeth_notify_reply(struct qeth_reply *reply, int reason)
{
atomic_inc(&reply->received);
wake_up(&reply->wait_q);
reply->rc = reason;
complete(&reply->received);
}
static void qeth_issue_ipa_msg(struct qeth_ipa_cmd *cmd, int rc,
......@@ -664,10 +665,8 @@ void qeth_clear_ipacmd_list(struct qeth_card *card)
QETH_CARD_TEXT(card, 4, "clipalst");
spin_lock_irqsave(&card->lock, flags);
list_for_each_entry(reply, &card->cmd_waiter_list, list) {
reply->rc = -EIO;
qeth_notify_reply(reply);
}
list_for_each_entry(reply, &card->cmd_waiter_list, list)
qeth_notify_reply(reply, -EIO);
spin_unlock_irqrestore(&card->lock, flags);
}
EXPORT_SYMBOL_GPL(qeth_clear_ipacmd_list);
......@@ -675,9 +674,6 @@ EXPORT_SYMBOL_GPL(qeth_clear_ipacmd_list);
static int qeth_check_idx_response(struct qeth_card *card,
unsigned char *buffer)
{
if (!buffer)
return 0;
QETH_DBF_HEX(CTRL, 2, buffer, QETH_DBF_CTRL_LEN);
if ((buffer[2] & 0xc0) == 0xc0) {
QETH_DBF_MESSAGE(2, "received an IDX TERMINATE with cause code %#04x\n",
......@@ -704,6 +700,7 @@ static struct qeth_cmd_buffer *__qeth_get_buffer(struct qeth_channel *channel)
do {
if (channel->iob[index].state == BUF_STATE_FREE) {
channel->iob[index].state = BUF_STATE_LOCKED;
channel->iob[index].timeout = QETH_TIMEOUT;
channel->io_buf_no = (channel->io_buf_no + 1) %
QETH_CMD_BUFFER_NO;
memset(channel->iob[index].data, 0, QETH_BUFSIZE);
......@@ -722,7 +719,7 @@ void qeth_release_buffer(struct qeth_channel *channel,
spin_lock_irqsave(&channel->iob_lock, flags);
iob->state = BUF_STATE_FREE;
iob->callback = qeth_send_control_data_cb;
iob->callback = NULL;
if (iob->reply) {
qeth_put_reply(iob->reply);
iob->reply = NULL;
......@@ -743,10 +740,8 @@ static void qeth_cancel_cmd(struct qeth_cmd_buffer *iob, int rc)
{
struct qeth_reply *reply = iob->reply;
if (reply) {
reply->rc = rc;
qeth_notify_reply(reply);
}
if (reply)
qeth_notify_reply(reply, rc);
qeth_release_buffer(iob->channel, iob);
}
......@@ -780,9 +775,9 @@ void qeth_clear_cmd_buffers(struct qeth_channel *channel)
}
EXPORT_SYMBOL_GPL(qeth_clear_cmd_buffers);
static void qeth_send_control_data_cb(struct qeth_card *card,
struct qeth_channel *channel,
struct qeth_cmd_buffer *iob)
static void qeth_issue_next_read_cb(struct qeth_card *card,
struct qeth_channel *channel,
struct qeth_cmd_buffer *iob)
{
struct qeth_ipa_cmd *cmd = NULL;
struct qeth_reply *reply = NULL;
......@@ -846,11 +841,8 @@ static void qeth_send_control_data_cb(struct qeth_card *card,
}
}
if (rc <= 0) {
reply->rc = rc;
qeth_notify_reply(reply);
}
if (rc <= 0)
qeth_notify_reply(reply, rc);
qeth_put_reply(reply);
out:
......@@ -1273,7 +1265,6 @@ static int qeth_setup_channel(struct qeth_channel *channel, bool alloc_buffers)
break;
channel->iob[cnt].state = BUF_STATE_FREE;
channel->iob[cnt].channel = channel;
channel->iob[cnt].callback = qeth_send_control_data_cb;
}
if (cnt < QETH_CMD_BUFFER_NO) {
qeth_clean_channel(channel);
......@@ -1409,9 +1400,7 @@ static void qeth_setup_card(struct qeth_card *card)
card->info.type = CARD_RDEV(card)->id.driver_info;
card->state = CARD_STATE_DOWN;
spin_lock_init(&card->mclock);
spin_lock_init(&card->lock);
spin_lock_init(&card->ip_lock);
spin_lock_init(&card->thread_mask_lock);
mutex_init(&card->conf_mutex);
mutex_init(&card->discipline_mutex);
......@@ -1451,7 +1440,8 @@ static struct qeth_card *qeth_alloc_card(struct ccwgroup_device *gdev)
CARD_WDEV(card) = gdev->cdev[1];
CARD_DDEV(card) = gdev->cdev[2];
card->event_wq = alloc_ordered_workqueue("%s", 0, dev_name(&gdev->dev));
card->event_wq = alloc_ordered_workqueue("%s_event", 0,
dev_name(&gdev->dev));
if (!card->event_wq)
goto out_wq;
if (qeth_setup_channel(&card->read, true))
......@@ -1771,121 +1761,16 @@ static void qeth_init_func_level(struct qeth_card *card)
}
}
static int qeth_idx_activate_get_answer(struct qeth_card *card,
struct qeth_channel *channel,
void (*reply_cb)(struct qeth_card *,
struct qeth_channel *,
struct qeth_cmd_buffer *))
{
struct qeth_cmd_buffer *iob;
int rc;
QETH_DBF_TEXT(SETUP, 2, "idxanswr");
iob = qeth_get_buffer(channel);
if (!iob)
return -ENOMEM;
iob->callback = reply_cb;
qeth_setup_ccw(channel->ccw, CCW_CMD_READ, QETH_BUFSIZE, iob->data);
wait_event(card->wait_q,
atomic_cmpxchg(&channel->irq_pending, 0, 1) == 0);
QETH_DBF_TEXT(SETUP, 6, "noirqpnd");
spin_lock_irq(get_ccwdev_lock(channel->ccwdev));
rc = ccw_device_start_timeout(channel->ccwdev, channel->ccw,
(addr_t) iob, 0, 0, QETH_TIMEOUT);
spin_unlock_irq(get_ccwdev_lock(channel->ccwdev));
if (rc) {
QETH_DBF_MESSAGE(2, "Error2 in activating channel rc=%d\n", rc);
QETH_DBF_TEXT_(SETUP, 2, "2err%d", rc);
atomic_set(&channel->irq_pending, 0);
qeth_release_buffer(channel, iob);
wake_up(&card->wait_q);
return rc;
}
rc = wait_event_interruptible_timeout(card->wait_q,
channel->state == CH_STATE_UP, QETH_TIMEOUT);
if (rc == -ERESTARTSYS)
return rc;
if (channel->state != CH_STATE_UP) {
rc = -ETIME;
QETH_DBF_TEXT_(SETUP, 2, "3err%d", rc);
} else
rc = 0;
return rc;
}
static int qeth_idx_activate_channel(struct qeth_card *card,
struct qeth_channel *channel,
void (*reply_cb)(struct qeth_card *,
struct qeth_channel *,
struct qeth_cmd_buffer *))
static void qeth_idx_finalize_cmd(struct qeth_card *card,
struct qeth_cmd_buffer *iob,
unsigned int length)
{
struct qeth_cmd_buffer *iob;
__u16 temp;
__u8 tmp;
int rc;
struct ccw_dev_id temp_devid;
QETH_DBF_TEXT(SETUP, 2, "idxactch");
qeth_setup_ccw(iob->channel->ccw, CCW_CMD_WRITE, length, iob->data);
iob = qeth_get_buffer(channel);
if (!iob)
return -ENOMEM;
iob->callback = reply_cb;
qeth_setup_ccw(channel->ccw, CCW_CMD_WRITE, IDX_ACTIVATE_SIZE,
iob->data);
if (channel == &card->write) {
memcpy(iob->data, IDX_ACTIVATE_WRITE, IDX_ACTIVATE_SIZE);
memcpy(QETH_TRANSPORT_HEADER_SEQ_NO(iob->data),
&card->seqno.trans_hdr, QETH_SEQ_NO_LENGTH);
memcpy(QETH_TRANSPORT_HEADER_SEQ_NO(iob->data), &card->seqno.trans_hdr,
QETH_SEQ_NO_LENGTH);
if (iob->channel == &card->write)
card->seqno.trans_hdr++;
} else {
memcpy(iob->data, IDX_ACTIVATE_READ, IDX_ACTIVATE_SIZE);
memcpy(QETH_TRANSPORT_HEADER_SEQ_NO(iob->data),
&card->seqno.trans_hdr, QETH_SEQ_NO_LENGTH);
}
tmp = ((u8)card->dev->dev_port) | 0x80;
memcpy(QETH_IDX_ACT_PNO(iob->data), &tmp, 1);
memcpy(QETH_IDX_ACT_ISSUER_RM_TOKEN(iob->data),
&card->token.issuer_rm_w, QETH_MPC_TOKEN_LENGTH);
memcpy(QETH_IDX_ACT_FUNC_LEVEL(iob->data),
&card->info.func_level, sizeof(__u16));
ccw_device_get_id(CARD_DDEV(card), &temp_devid);
memcpy(QETH_IDX_ACT_QDIO_DEV_CUA(iob->data), &temp_devid.devno, 2);
temp = (card->info.cula << 8) + card->info.unit_addr2;
memcpy(QETH_IDX_ACT_QDIO_DEV_REALADDR(iob->data), &temp, 2);
wait_event(card->wait_q,
atomic_cmpxchg(&channel->irq_pending, 0, 1) == 0);
QETH_DBF_TEXT(SETUP, 6, "noirqpnd");
spin_lock_irq(get_ccwdev_lock(channel->ccwdev));
rc = ccw_device_start_timeout(channel->ccwdev, channel->ccw,
(addr_t) iob, 0, 0, QETH_TIMEOUT);
spin_unlock_irq(get_ccwdev_lock(channel->ccwdev));
if (rc) {
QETH_DBF_MESSAGE(2, "Error1 in activating channel. rc=%d\n",
rc);
QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc);
atomic_set(&channel->irq_pending, 0);
qeth_release_buffer(channel, iob);
wake_up(&card->wait_q);
return rc;
}
rc = wait_event_interruptible_timeout(card->wait_q,
channel->state == CH_STATE_ACTIVATING, QETH_TIMEOUT);
if (rc == -ERESTARTSYS)
return rc;
if (channel->state != CH_STATE_ACTIVATING) {
dev_warn(&channel->ccwdev->dev, "The qeth device driver"
" failed to recover an error on the device\n");
QETH_DBF_MESSAGE(2, "IDX activate timed out on channel %x\n",
CCW_DEVID(channel->ccwdev));
QETH_DBF_TEXT_(SETUP, 2, "2err%d", -ETIME);
return -ETIME;
}
return qeth_idx_activate_get_answer(card, channel, reply_cb);
}
static int qeth_peer_func_level(int level)
......@@ -1897,112 +1782,21 @@ static int qeth_peer_func_level(int level)
return level;
}
static void qeth_idx_write_cb(struct qeth_card *card,
struct qeth_channel *channel,
struct qeth_cmd_buffer *iob)
{
__u16 temp;
QETH_DBF_TEXT(SETUP , 2, "idxwrcb");
if (channel->state == CH_STATE_DOWN) {
channel->state = CH_STATE_ACTIVATING;
goto out;
}
if (!(QETH_IS_IDX_ACT_POS_REPLY(iob->data))) {
if (QETH_IDX_ACT_CAUSE_CODE(iob->data) == QETH_IDX_ACT_ERR_EXCL)
dev_err(&channel->ccwdev->dev,
"The adapter is used exclusively by another "
"host\n");
else
QETH_DBF_MESSAGE(2, "IDX_ACTIVATE on channel %x: negative reply\n",
CCW_DEVID(channel->ccwdev));
goto out;
}
memcpy(&temp, QETH_IDX_ACT_FUNC_LEVEL(iob->data), 2);
if ((temp & ~0x0100) != qeth_peer_func_level(card->info.func_level)) {
QETH_DBF_MESSAGE(2, "IDX_ACTIVATE on channel %x: function level mismatch (sent: %#x, received: %#x)\n",
CCW_DEVID(channel->ccwdev),
card->info.func_level, temp);
goto out;
}
channel->state = CH_STATE_UP;
out:
qeth_release_buffer(channel, iob);
}
static void qeth_idx_read_cb(struct qeth_card *card,
struct qeth_channel *channel,
struct qeth_cmd_buffer *iob)
{
__u16 temp;
QETH_DBF_TEXT(SETUP , 2, "idxrdcb");
if (channel->state == CH_STATE_DOWN) {
channel->state = CH_STATE_ACTIVATING;
goto out;
}
if (qeth_check_idx_response(card, iob->data))
goto out;
if (!(QETH_IS_IDX_ACT_POS_REPLY(iob->data))) {
switch (QETH_IDX_ACT_CAUSE_CODE(iob->data)) {
case QETH_IDX_ACT_ERR_EXCL:
dev_err(&channel->ccwdev->dev,
"The adapter is used exclusively by another "
"host\n");
break;
case QETH_IDX_ACT_ERR_AUTH:
case QETH_IDX_ACT_ERR_AUTH_USER:
dev_err(&channel->ccwdev->dev,
"Setting the device online failed because of "
"insufficient authorization\n");
break;
default:
QETH_DBF_MESSAGE(2, "IDX_ACTIVATE on channel %x: negative reply\n",
CCW_DEVID(channel->ccwdev));
}
QETH_CARD_TEXT_(card, 2, "idxread%c",
QETH_IDX_ACT_CAUSE_CODE(iob->data));
goto out;
}
memcpy(&temp, QETH_IDX_ACT_FUNC_LEVEL(iob->data), 2);
if (temp != qeth_peer_func_level(card->info.func_level)) {
QETH_DBF_MESSAGE(2, "IDX_ACTIVATE on channel %x: function level mismatch (sent: %#x, received: %#x)\n",
CCW_DEVID(channel->ccwdev),
card->info.func_level, temp);
goto out;
}
memcpy(&card->token.issuer_rm_r,
QETH_IDX_ACT_ISSUER_RM_TOKEN(iob->data),
QETH_MPC_TOKEN_LENGTH);
memcpy(&card->info.mcl_level[0],
QETH_IDX_REPLY_LEVEL(iob->data), QETH_MCL_LENGTH);
channel->state = CH_STATE_UP;
out:
qeth_release_buffer(channel, iob);
}
void qeth_prepare_control_data(struct qeth_card *card, int len,
struct qeth_cmd_buffer *iob)
static void qeth_mpc_finalize_cmd(struct qeth_card *card,
struct qeth_cmd_buffer *iob,
unsigned int length)
{
qeth_setup_ccw(iob->channel->ccw, CCW_CMD_WRITE, len, iob->data);
iob->callback = qeth_release_buffer_cb;
qeth_idx_finalize_cmd(card, iob, length);
memcpy(QETH_TRANSPORT_HEADER_SEQ_NO(iob->data),
&card->seqno.trans_hdr, QETH_SEQ_NO_LENGTH);
card->seqno.trans_hdr++;
memcpy(QETH_PDU_HEADER_SEQ_NO(iob->data),
&card->seqno.pdu_hdr, QETH_SEQ_NO_LENGTH);
card->seqno.pdu_hdr++;
memcpy(QETH_PDU_HEADER_ACK_SEQ_NO(iob->data),
&card->seqno.pdu_hdr_ack, QETH_SEQ_NO_LENGTH);
QETH_DBF_HEX(CTRL, 2, iob->data, min(len, QETH_DBF_CTRL_LEN));
iob->reply->seqno = QETH_IDX_COMMAND_SEQNO;
iob->callback = qeth_release_buffer_cb;
}
EXPORT_SYMBOL_GPL(qeth_prepare_control_data);
/**
* qeth_send_control_data() - send control command to the card
......@@ -2035,17 +1829,12 @@ static int qeth_send_control_data(struct qeth_card *card, int len,
void *reply_param)
{
struct qeth_channel *channel = iob->channel;
long timeout = iob->timeout;
int rc;
struct qeth_reply *reply = NULL;
unsigned long timeout, event_timeout;
struct qeth_ipa_cmd *cmd = NULL;
QETH_CARD_TEXT(card, 2, "sendctl");
if (card->read_or_write_problem) {
qeth_release_buffer(channel, iob);
return -EIO;
}
reply = qeth_alloc_reply(card);
if (!reply) {
qeth_release_buffer(channel, iob);
......@@ -2058,27 +1847,24 @@ static int qeth_send_control_data(struct qeth_card *card, int len,
qeth_get_reply(reply);
iob->reply = reply;
while (atomic_cmpxchg(&channel->irq_pending, 0, 1)) ;
if (IS_IPA(iob->data)) {
cmd = __ipa_cmd(iob);
cmd->hdr.seqno = card->seqno.ipa++;
reply->seqno = cmd->hdr.seqno;
event_timeout = QETH_IPA_TIMEOUT;
} else {
reply->seqno = QETH_IDX_COMMAND_SEQNO;
event_timeout = QETH_TIMEOUT;
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;
}
qeth_prepare_control_data(card, len, iob);
qeth_enqueue_reply(card, reply);
iob->finalize(card, iob, len);
QETH_DBF_HEX(CTRL, 2, iob->data, min(len, QETH_DBF_CTRL_LEN));
timeout = jiffies + event_timeout;
qeth_enqueue_reply(card, reply);
QETH_CARD_TEXT(card, 6, "noirqpnd");
spin_lock_irq(get_ccwdev_lock(channel->ccwdev));
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));
if (rc) {
QETH_DBF_MESSAGE(2, "qeth_send_control_data on device %x: ccw_device_start rc = %i\n",
......@@ -2092,30 +1878,211 @@ static int qeth_send_control_data(struct qeth_card *card, int len,
return rc;
}
/* we have only one long running ipassist, since we can ensure
process context of this command we can sleep */
if (cmd && cmd->hdr.command == IPA_CMD_SETIP &&
cmd->hdr.prot_version == QETH_PROT_IPV4) {
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();
}
}
timeout = wait_for_completion_interruptible_timeout(&reply->received,
timeout);
if (timeout <= 0)
rc = (timeout == -ERESTARTSYS) ? -EINTR : -ETIME;
qeth_dequeue_reply(card, reply);
rc = reply->rc;
if (!rc)
rc = reply->rc;
qeth_put_reply(reply);
return rc;
}
time_err:
qeth_dequeue_reply(card, reply);
qeth_put_reply(reply);
return -ETIME;
static int qeth_idx_check_activate_response(struct qeth_card *card,
struct qeth_channel *channel,
struct qeth_cmd_buffer *iob)
{
int rc;
rc = qeth_check_idx_response(card, iob->data);
if (rc)
return rc;
if (QETH_IS_IDX_ACT_POS_REPLY(iob->data))
return 0;
/* negative reply: */
QETH_DBF_TEXT_(SETUP, 2, "idxneg%c",
QETH_IDX_ACT_CAUSE_CODE(iob->data));
switch (QETH_IDX_ACT_CAUSE_CODE(iob->data)) {
case QETH_IDX_ACT_ERR_EXCL:
dev_err(&channel->ccwdev->dev,
"The adapter is used exclusively by another host\n");
return -EBUSY;
case QETH_IDX_ACT_ERR_AUTH:
case QETH_IDX_ACT_ERR_AUTH_USER:
dev_err(&channel->ccwdev->dev,
"Setting the device online failed because of insufficient authorization\n");
return -EPERM;
default:
QETH_DBF_MESSAGE(2, "IDX_ACTIVATE on channel %x: negative reply\n",
CCW_DEVID(channel->ccwdev));
return -EIO;
}
}
static void qeth_idx_query_read_cb(struct qeth_card *card,
struct qeth_channel *channel,
struct qeth_cmd_buffer *iob)
{
u16 peer_level;
int rc;
QETH_DBF_TEXT(SETUP, 2, "idxrdcb");
rc = qeth_idx_check_activate_response(card, channel, iob);
if (rc)
goto out;
memcpy(&peer_level, QETH_IDX_ACT_FUNC_LEVEL(iob->data), 2);
if (peer_level != qeth_peer_func_level(card->info.func_level)) {
QETH_DBF_MESSAGE(2, "IDX_ACTIVATE on channel %x: function level mismatch (sent: %#x, received: %#x)\n",
CCW_DEVID(channel->ccwdev),
card->info.func_level, peer_level);
rc = -EINVAL;
goto out;
}
memcpy(&card->token.issuer_rm_r,
QETH_IDX_ACT_ISSUER_RM_TOKEN(iob->data),
QETH_MPC_TOKEN_LENGTH);
memcpy(&card->info.mcl_level[0],
QETH_IDX_REPLY_LEVEL(iob->data), QETH_MCL_LENGTH);
out:
qeth_notify_reply(iob->reply, rc);
qeth_release_buffer(channel, iob);
}
static void qeth_idx_query_write_cb(struct qeth_card *card,
struct qeth_channel *channel,
struct qeth_cmd_buffer *iob)
{
u16 peer_level;
int rc;
QETH_DBF_TEXT(SETUP, 2, "idxwrcb");
rc = qeth_idx_check_activate_response(card, channel, iob);
if (rc)
goto out;
memcpy(&peer_level, QETH_IDX_ACT_FUNC_LEVEL(iob->data), 2);
if ((peer_level & ~0x0100) !=
qeth_peer_func_level(card->info.func_level)) {
QETH_DBF_MESSAGE(2, "IDX_ACTIVATE on channel %x: function level mismatch (sent: %#x, received: %#x)\n",
CCW_DEVID(channel->ccwdev),
card->info.func_level, peer_level);
rc = -EINVAL;
}
out:
qeth_notify_reply(iob->reply, rc);
qeth_release_buffer(channel, iob);
}
static void qeth_idx_finalize_query_cmd(struct qeth_card *card,
struct qeth_cmd_buffer *iob,
unsigned int length)
{
qeth_setup_ccw(iob->channel->ccw, CCW_CMD_READ, length, iob->data);
}
static void qeth_idx_activate_cb(struct qeth_card *card,
struct qeth_channel *channel,
struct qeth_cmd_buffer *iob)
{
qeth_notify_reply(iob->reply, 0);
qeth_release_buffer(channel, iob);
}
static void qeth_idx_setup_activate_cmd(struct qeth_card *card,
struct qeth_cmd_buffer *iob)
{
u16 addr = (card->info.cula << 8) + card->info.unit_addr2;
u8 port = ((u8)card->dev->dev_port) | 0x80;
struct ccw_dev_id dev_id;
ccw_device_get_id(CARD_DDEV(card), &dev_id);
iob->finalize = qeth_idx_finalize_cmd;
iob->callback = qeth_idx_activate_cb;
memcpy(QETH_IDX_ACT_PNO(iob->data), &port, 1);
memcpy(QETH_IDX_ACT_ISSUER_RM_TOKEN(iob->data),
&card->token.issuer_rm_w, QETH_MPC_TOKEN_LENGTH);
memcpy(QETH_IDX_ACT_FUNC_LEVEL(iob->data),
&card->info.func_level, 2);
memcpy(QETH_IDX_ACT_QDIO_DEV_CUA(iob->data), &dev_id.devno, 2);
memcpy(QETH_IDX_ACT_QDIO_DEV_REALADDR(iob->data), &addr, 2);
}
static int qeth_idx_activate_read_channel(struct qeth_card *card)
{
struct qeth_channel *channel = &card->read;
struct qeth_cmd_buffer *iob;
int rc;
QETH_DBF_TEXT(SETUP, 2, "idxread");
iob = qeth_get_buffer(channel);
if (!iob)
return -ENOMEM;
memcpy(iob->data, IDX_ACTIVATE_READ, IDX_ACTIVATE_SIZE);
qeth_idx_setup_activate_cmd(card, iob);
rc = qeth_send_control_data(card, IDX_ACTIVATE_SIZE, iob, NULL, NULL);
if (rc)
return rc;
iob = qeth_get_buffer(channel);
if (!iob)
return -ENOMEM;
iob->finalize = qeth_idx_finalize_query_cmd;
iob->callback = qeth_idx_query_read_cb;
rc = qeth_send_control_data(card, QETH_BUFSIZE, iob, NULL, NULL);
if (rc)
return rc;
channel->state = CH_STATE_UP;
return 0;
}
static int qeth_idx_activate_write_channel(struct qeth_card *card)
{
struct qeth_channel *channel = &card->write;
struct qeth_cmd_buffer *iob;
int rc;
QETH_DBF_TEXT(SETUP, 2, "idxwrite");
iob = qeth_get_buffer(channel);
if (!iob)
return -ENOMEM;
memcpy(iob->data, IDX_ACTIVATE_WRITE, IDX_ACTIVATE_SIZE);
qeth_idx_setup_activate_cmd(card, iob);
rc = qeth_send_control_data(card, IDX_ACTIVATE_SIZE, iob, NULL, NULL);
if (rc)
return rc;
iob = qeth_get_buffer(channel);
if (!iob)
return -ENOMEM;
iob->finalize = qeth_idx_finalize_query_cmd;
iob->callback = qeth_idx_query_write_cb;
rc = qeth_send_control_data(card, QETH_BUFSIZE, iob, NULL, NULL);
if (rc)
return rc;
channel->state = CH_STATE_UP;
return 0;
}
static int qeth_cm_enable_cb(struct qeth_card *card, struct qeth_reply *reply,
......@@ -2140,7 +2107,9 @@ static int qeth_cm_enable(struct qeth_card *card)
QETH_DBF_TEXT(SETUP, 2, "cmenable");
iob = qeth_wait_for_buffer(&card->write);
iob->finalize = qeth_mpc_finalize_cmd;
memcpy(iob->data, CM_ENABLE, CM_ENABLE_SIZE);
memcpy(QETH_CM_ENABLE_ISSUER_RM_TOKEN(iob->data),
&card->token.issuer_rm_r, QETH_MPC_TOKEN_LENGTH);
memcpy(QETH_CM_ENABLE_FILTER_TOKEN(iob->data),
......@@ -2173,7 +2142,9 @@ static int qeth_cm_setup(struct qeth_card *card)
QETH_DBF_TEXT(SETUP, 2, "cmsetup");
iob = qeth_wait_for_buffer(&card->write);
iob->finalize = qeth_mpc_finalize_cmd;
memcpy(iob->data, CM_SETUP, CM_SETUP_SIZE);
memcpy(QETH_CM_SETUP_DEST_ADDR(iob->data),
&card->token.issuer_rm_r, QETH_MPC_TOKEN_LENGTH);
memcpy(QETH_CM_SETUP_CONNECTION_TOKEN(iob->data),
......@@ -2290,6 +2261,7 @@ static int qeth_ulp_enable(struct qeth_card *card)
QETH_DBF_TEXT(SETUP, 2, "ulpenabl");
iob = qeth_wait_for_buffer(&card->write);
iob->finalize = qeth_mpc_finalize_cmd;
memcpy(iob->data, ULP_ENABLE, ULP_ENABLE_SIZE);
*(QETH_ULP_ENABLE_LINKNUM(iob->data)) = (u8) card->dev->dev_port;
......@@ -2336,6 +2308,7 @@ static int qeth_ulp_setup(struct qeth_card *card)
QETH_DBF_TEXT(SETUP, 2, "ulpsetup");
iob = qeth_wait_for_buffer(&card->write);
iob->finalize = qeth_mpc_finalize_cmd;
memcpy(iob->data, ULP_SETUP, ULP_SETUP_SIZE);
memcpy(QETH_ULP_SETUP_DEST_ADDR(iob->data),
......@@ -2523,6 +2496,7 @@ static int qeth_dm_act(struct qeth_card *card)
QETH_DBF_TEXT(SETUP, 2, "dmact");
iob = qeth_wait_for_buffer(&card->write);
iob->finalize = qeth_mpc_finalize_cmd;
memcpy(iob->data, DM_ACT, DM_ACT_SIZE);
memcpy(QETH_DM_ACT_DEST_ADDR(iob->data),
......@@ -2805,12 +2779,26 @@ static void qeth_fill_ipacmd_header(struct qeth_card *card,
cmd->hdr.prot_version = prot;
}
static void qeth_ipa_finalize_cmd(struct qeth_card *card,
struct qeth_cmd_buffer *iob,
unsigned int length)
{
qeth_mpc_finalize_cmd(card, iob, length);
/* override with IPA-specific values: */
__ipa_cmd(iob)->hdr.seqno = card->seqno.ipa;
iob->reply->seqno = card->seqno.ipa++;
}
void qeth_prepare_ipa_cmd(struct qeth_card *card, struct qeth_cmd_buffer *iob,
u16 cmd_length)
{
u16 total_length = IPA_PDU_HEADER_SIZE + cmd_length;
u8 prot_type = qeth_mpc_select_prot_type(card);
iob->finalize = qeth_ipa_finalize_cmd;
iob->timeout = QETH_IPA_TIMEOUT;
memcpy(iob->data, IPA_PDU_HEADER, IPA_PDU_HEADER_SIZE);
memcpy(QETH_IPA_PDU_LEN_TOTAL(iob->data), &total_length, 2);
memcpy(QETH_IPA_CMD_PROT_TYPE(iob->data), &prot_type, 1);
......@@ -2866,6 +2854,11 @@ int qeth_send_ipa_cmd(struct qeth_card *card, struct qeth_cmd_buffer *iob,
QETH_CARD_TEXT(card, 4, "sendipa");
if (card->read_or_write_problem) {
qeth_release_buffer(iob->channel, iob);
return -EIO;
}
if (reply_cb == NULL)
reply_cb = qeth_send_ipa_cmd_cb;
memcpy(&length, QETH_IPA_PDU_LEN_TOTAL(iob->data), 2);
......@@ -5019,8 +5012,9 @@ int qeth_core_hardsetup_card(struct qeth_card *card, bool *carrier_ok)
qeth_determine_capabilities(card);
qeth_init_tokens(card);
qeth_init_func_level(card);
rc = qeth_idx_activate_channel(card, &card->read, qeth_idx_read_cb);
if (rc == -ERESTARTSYS) {
rc = qeth_idx_activate_read_channel(card);
if (rc == -EINTR) {
QETH_DBF_TEXT(SETUP, 2, "break2");
return rc;
} else if (rc) {
......@@ -5030,8 +5024,9 @@ int qeth_core_hardsetup_card(struct qeth_card *card, bool *carrier_ok)
else
goto retry;
}
rc = qeth_idx_activate_channel(card, &card->write, qeth_idx_write_cb);
if (rc == -ERESTARTSYS) {
rc = qeth_idx_activate_write_channel(card);
if (rc == -EINTR) {
QETH_DBF_TEXT(SETUP, 2, "break3");
return rc;
} else if (rc) {
......
......@@ -149,18 +149,16 @@ static int qeth_l2_remove_mac(struct qeth_card *card, u8 *mac)
return rc;
}
static void qeth_l2_del_all_macs(struct qeth_card *card)
static void qeth_l2_drain_rx_mode_cache(struct qeth_card *card)
{
struct qeth_mac *mac;
struct hlist_node *tmp;
int i;
spin_lock_bh(&card->mclock);
hash_for_each_safe(card->mac_htable, i, tmp, mac, hnode) {
hash_del(&mac->hnode);
kfree(mac);
}
spin_unlock_bh(&card->mclock);
}
static int qeth_l2_get_cast_type(struct qeth_card *card, struct sk_buff *skb)
......@@ -292,8 +290,10 @@ static void qeth_l2_stop_card(struct qeth_card *card)
qeth_set_allowed_threads(card, 0, 1);
cancel_work_sync(&card->rx_mode_work);
qeth_l2_drain_rx_mode_cache(card);
if (card->state == CARD_STATE_SOFTSETUP) {
qeth_l2_del_all_macs(card);
qeth_clear_ipacmd_list(card);
card->state = CARD_STATE_HARDSETUP;
}
......@@ -515,9 +515,11 @@ static void qeth_l2_add_mac(struct qeth_card *card, struct netdev_hw_addr *ha)
hash_add(card->mac_htable, &mac->hnode, mac_hash);
}
static void qeth_l2_set_rx_mode(struct net_device *dev)
static void qeth_l2_rx_mode_work(struct work_struct *work)
{
struct qeth_card *card = dev->ml_priv;
struct qeth_card *card = container_of(work, struct qeth_card,
rx_mode_work);
struct net_device *dev = card->dev;
struct netdev_hw_addr *ha;
struct qeth_mac *mac;
struct hlist_node *tmp;
......@@ -526,12 +528,12 @@ static void qeth_l2_set_rx_mode(struct net_device *dev)
QETH_CARD_TEXT(card, 3, "setmulti");
spin_lock_bh(&card->mclock);
netif_addr_lock_bh(dev);
netdev_for_each_mc_addr(ha, dev)
qeth_l2_add_mac(card, ha);
netdev_for_each_uc_addr(ha, dev)
qeth_l2_add_mac(card, ha);
netif_addr_unlock_bh(dev);
hash_for_each_safe(card->mac_htable, i, tmp, mac, hnode) {
switch (mac->disp_flag) {
......@@ -554,8 +556,6 @@ static void qeth_l2_set_rx_mode(struct net_device *dev)
}
}
spin_unlock_bh(&card->mclock);
if (qeth_adp_supported(card, IPA_SETADP_SET_PROMISC_MODE))
qeth_setadp_promisc_mode(card);
else
......@@ -653,6 +653,7 @@ static int qeth_l2_probe_device(struct ccwgroup_device *gdev)
}
hash_init(card->mac_htable);
INIT_WORK(&card->rx_mode_work, qeth_l2_rx_mode_work);
return 0;
}
......@@ -673,6 +674,13 @@ static void qeth_l2_remove_device(struct ccwgroup_device *cgdev)
unregister_netdev(card->dev);
}
static void qeth_l2_set_rx_mode(struct net_device *dev)
{
struct qeth_card *card = dev->ml_priv;
schedule_work(&card->rx_mode_work);
}
static const struct net_device_ops qeth_l2_netdev_ops = {
.ndo_open = qeth_open,
.ndo_stop = qeth_stop,
......@@ -1042,13 +1050,13 @@ static int qeth_osn_send_control_data(struct qeth_card *card, int len,
QETH_CARD_TEXT(card, 5, "osndctrd");
wait_event(card->wait_q,
atomic_cmpxchg(&channel->irq_pending, 0, 1) == 0);
qeth_prepare_control_data(card, len, iob);
wait_event(card->wait_q, qeth_trylock_channel(channel));
iob->finalize(card, iob, len);
QETH_DBF_HEX(CTRL, 2, iob->data, min(len, QETH_DBF_CTRL_LEN));
QETH_CARD_TEXT(card, 6, "osnoirqp");
spin_lock_irq(get_ccwdev_lock(channel->ccwdev));
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));
if (rc) {
QETH_DBF_MESSAGE(2, "qeth_osn_send_control_data: "
......
......@@ -246,9 +246,9 @@ static int qeth_l3_add_ip(struct qeth_card *card, struct qeth_ipaddr *tmp_addr)
*/
if (addr->proto == QETH_PROT_IPV4) {
addr->in_progress = 1;
spin_unlock_bh(&card->ip_lock);
mutex_unlock(&card->ip_lock);
rc = qeth_l3_register_addr_entry(card, addr);
spin_lock_bh(&card->ip_lock);
mutex_lock(&card->ip_lock);
addr->in_progress = 0;
} else
rc = qeth_l3_register_addr_entry(card, addr);
......@@ -268,6 +268,30 @@ static int qeth_l3_add_ip(struct qeth_card *card, struct qeth_ipaddr *tmp_addr)
return rc;
}
static int qeth_l3_modify_ip(struct qeth_card *card, struct qeth_ipaddr *addr,
bool add)
{
int rc;
mutex_lock(&card->ip_lock);
rc = add ? qeth_l3_add_ip(card, addr) : qeth_l3_delete_ip(card, addr);
mutex_unlock(&card->ip_lock);
return rc;
}
static void qeth_l3_drain_rx_mode_cache(struct qeth_card *card)
{
struct qeth_ipaddr *addr;
struct hlist_node *tmp;
int i;
hash_for_each_safe(card->ip_mc_htable, i, tmp, addr, hnode) {
hash_del(&addr->hnode);
kfree(addr);
}
}
static void qeth_l3_clear_ip_htable(struct qeth_card *card, int recover)
{
struct qeth_ipaddr *addr;
......@@ -276,7 +300,7 @@ static void qeth_l3_clear_ip_htable(struct qeth_card *card, int recover)
QETH_CARD_TEXT(card, 4, "clearip");
spin_lock_bh(&card->ip_lock);
mutex_lock(&card->ip_lock);
hash_for_each_safe(card->ip_htable, i, tmp, addr, hnode) {
if (!recover) {
......@@ -287,19 +311,9 @@ static void qeth_l3_clear_ip_htable(struct qeth_card *card, int recover)
addr->disp_flag = QETH_DISP_ADDR_ADD;
}
spin_unlock_bh(&card->ip_lock);
spin_lock_bh(&card->mclock);
hash_for_each_safe(card->ip_mc_htable, i, tmp, addr, hnode) {
hash_del(&addr->hnode);
kfree(addr);
}
spin_unlock_bh(&card->mclock);
mutex_unlock(&card->ip_lock);
}
static void qeth_l3_recover_ip(struct qeth_card *card)
{
struct qeth_ipaddr *addr;
......@@ -309,15 +323,15 @@ static void qeth_l3_recover_ip(struct qeth_card *card)
QETH_CARD_TEXT(card, 4, "recovrip");
spin_lock_bh(&card->ip_lock);
mutex_lock(&card->ip_lock);
hash_for_each_safe(card->ip_htable, i, tmp, addr, hnode) {
if (addr->disp_flag == QETH_DISP_ADDR_ADD) {
if (addr->proto == QETH_PROT_IPV4) {
addr->in_progress = 1;
spin_unlock_bh(&card->ip_lock);
mutex_unlock(&card->ip_lock);
rc = qeth_l3_register_addr_entry(card, addr);
spin_lock_bh(&card->ip_lock);
mutex_lock(&card->ip_lock);
addr->in_progress = 0;
} else
rc = qeth_l3_register_addr_entry(card, addr);
......@@ -333,8 +347,7 @@ static void qeth_l3_recover_ip(struct qeth_card *card)
}
}
spin_unlock_bh(&card->ip_lock);
mutex_unlock(&card->ip_lock);
}
static int qeth_l3_setdelip_cb(struct qeth_card *card, struct qeth_reply *reply,
......@@ -559,7 +572,7 @@ static void qeth_l3_clear_ipato_list(struct qeth_card *card)
{
struct qeth_ipato_entry *ipatoe, *tmp;
spin_lock_bh(&card->ip_lock);
mutex_lock(&card->ip_lock);
list_for_each_entry_safe(ipatoe, tmp, &card->ipato.entries, entry) {
list_del(&ipatoe->entry);
......@@ -567,7 +580,7 @@ static void qeth_l3_clear_ipato_list(struct qeth_card *card)
}
qeth_l3_update_ipato(card);
spin_unlock_bh(&card->ip_lock);
mutex_unlock(&card->ip_lock);
}
int qeth_l3_add_ipato_entry(struct qeth_card *card,
......@@ -578,7 +591,7 @@ int qeth_l3_add_ipato_entry(struct qeth_card *card,
QETH_CARD_TEXT(card, 2, "addipato");
spin_lock_bh(&card->ip_lock);
mutex_lock(&card->ip_lock);
list_for_each_entry(ipatoe, &card->ipato.entries, entry) {
if (ipatoe->proto != new->proto)
......@@ -596,7 +609,7 @@ int qeth_l3_add_ipato_entry(struct qeth_card *card,
qeth_l3_update_ipato(card);
}
spin_unlock_bh(&card->ip_lock);
mutex_unlock(&card->ip_lock);
return rc;
}
......@@ -610,7 +623,7 @@ int qeth_l3_del_ipato_entry(struct qeth_card *card,
QETH_CARD_TEXT(card, 2, "delipato");
spin_lock_bh(&card->ip_lock);
mutex_lock(&card->ip_lock);
list_for_each_entry_safe(ipatoe, tmp, &card->ipato.entries, entry) {
if (ipatoe->proto != proto)
......@@ -625,7 +638,7 @@ int qeth_l3_del_ipato_entry(struct qeth_card *card,
}
}
spin_unlock_bh(&card->ip_lock);
mutex_unlock(&card->ip_lock);
return rc;
}
......@@ -634,7 +647,6 @@ int qeth_l3_modify_rxip_vipa(struct qeth_card *card, bool add, const u8 *ip,
enum qeth_prot_versions proto)
{
struct qeth_ipaddr addr;
int rc;
qeth_l3_init_ipaddr(&addr, type, proto);
if (proto == QETH_PROT_IPV4)
......@@ -642,16 +654,13 @@ int qeth_l3_modify_rxip_vipa(struct qeth_card *card, bool add, const u8 *ip,
else
memcpy(&addr.u.a6.addr, ip, 16);
spin_lock_bh(&card->ip_lock);
rc = add ? qeth_l3_add_ip(card, &addr) : qeth_l3_delete_ip(card, &addr);
spin_unlock_bh(&card->ip_lock);
return rc;
return qeth_l3_modify_ip(card, &addr, add);
}
int qeth_l3_modify_hsuid(struct qeth_card *card, bool add)
{
struct qeth_ipaddr addr;
int rc, i;
unsigned int i;
qeth_l3_init_ipaddr(&addr, QETH_IP_TYPE_NORMAL, QETH_PROT_IPV6);
addr.u.a6.addr.s6_addr[0] = 0xfe;
......@@ -659,10 +668,7 @@ int qeth_l3_modify_hsuid(struct qeth_card *card, bool add)
for (i = 0; i < 8; i++)
addr.u.a6.addr.s6_addr[8+i] = card->options.hsuid[i];
spin_lock_bh(&card->ip_lock);
rc = add ? qeth_l3_add_ip(card, &addr) : qeth_l3_delete_ip(card, &addr);
spin_unlock_bh(&card->ip_lock);
return rc;
return qeth_l3_modify_ip(card, &addr, add);
}
static int qeth_l3_register_addr_entry(struct qeth_card *card,
......@@ -1413,6 +1419,9 @@ static void qeth_l3_stop_card(struct qeth_card *card)
qeth_set_allowed_threads(card, 0, 1);
cancel_work_sync(&card->rx_mode_work);
qeth_l3_drain_rx_mode_cache(card);
if (card->options.sniffer &&
(card->info.promisc_mode == SET_PROMISC_MODE_ON))
qeth_diags_trace(card, QETH_DIAGS_CMD_TRACE_DISABLE);
......@@ -1466,9 +1475,10 @@ qeth_l3_handle_promisc_mode(struct qeth_card *card)
}
}
static void qeth_l3_set_rx_mode(struct net_device *dev)
static void qeth_l3_rx_mode_work(struct work_struct *work)
{
struct qeth_card *card = dev->ml_priv;
struct qeth_card *card = container_of(work, struct qeth_card,
rx_mode_work);
struct qeth_ipaddr *addr;
struct hlist_node *tmp;
int i, rc;
......@@ -1476,8 +1486,6 @@ static void qeth_l3_set_rx_mode(struct net_device *dev)
QETH_CARD_TEXT(card, 3, "setmulti");
if (!card->options.sniffer) {
spin_lock_bh(&card->mclock);
qeth_l3_add_multicast_ipv4(card);
qeth_l3_add_multicast_ipv6(card);
......@@ -1505,8 +1513,6 @@ static void qeth_l3_set_rx_mode(struct net_device *dev)
}
}
spin_unlock_bh(&card->mclock);
if (!qeth_adp_supported(card, IPA_SETADP_SET_PROMISC_MODE))
return;
}
......@@ -2101,6 +2107,13 @@ static netdev_tx_t qeth_l3_hard_start_xmit(struct sk_buff *skb,
return NETDEV_TX_OK;
}
static void qeth_l3_set_rx_mode(struct net_device *dev)
{
struct qeth_card *card = dev->ml_priv;
schedule_work(&card->rx_mode_work);
}
/*
* we need NOARP for IPv4 but we want neighbor solicitation for IPv6. Setting
* NOARP on the netdevice is no option because it also turns off neighbor
......@@ -2253,14 +2266,22 @@ static int qeth_l3_probe_device(struct ccwgroup_device *gdev)
int rc;
hash_init(card->ip_htable);
mutex_init(&card->ip_lock);
card->cmd_wq = alloc_ordered_workqueue("%s_cmd", 0,
dev_name(&gdev->dev));
if (!card->cmd_wq)
return -ENOMEM;
if (gdev->dev.type == &qeth_generic_devtype) {
rc = qeth_l3_create_device_attributes(&gdev->dev);
if (rc)
if (rc) {
destroy_workqueue(card->cmd_wq);
return rc;
}
}
hash_init(card->ip_mc_htable);
INIT_WORK(&card->rx_mode_work, qeth_l3_rx_mode_work);
return 0;
}
......@@ -2280,6 +2301,9 @@ static void qeth_l3_remove_device(struct ccwgroup_device *cgdev)
cancel_work_sync(&card->close_dev_work);
if (qeth_netdev_is_registered(card->dev))
unregister_netdev(card->dev);
flush_workqueue(card->cmd_wq);
destroy_workqueue(card->cmd_wq);
qeth_l3_clear_ip_htable(card, 0);
qeth_l3_clear_ipato_list(card);
}
......@@ -2517,20 +2541,40 @@ static int qeth_l3_handle_ip_event(struct qeth_card *card,
{
switch (event) {
case NETDEV_UP:
spin_lock_bh(&card->ip_lock);
qeth_l3_add_ip(card, addr);
spin_unlock_bh(&card->ip_lock);
qeth_l3_modify_ip(card, addr, true);
return NOTIFY_OK;
case NETDEV_DOWN:
spin_lock_bh(&card->ip_lock);
qeth_l3_delete_ip(card, addr);
spin_unlock_bh(&card->ip_lock);
qeth_l3_modify_ip(card, addr, false);
return NOTIFY_OK;
default:
return NOTIFY_DONE;
}
}
struct qeth_l3_ip_event_work {
struct work_struct work;
struct qeth_card *card;
struct qeth_ipaddr addr;
};
#define to_ip_work(w) container_of((w), struct qeth_l3_ip_event_work, work)
static void qeth_l3_add_ip_worker(struct work_struct *work)
{
struct qeth_l3_ip_event_work *ip_work = to_ip_work(work);
qeth_l3_modify_ip(ip_work->card, &ip_work->addr, true);
kfree(work);
}
static void qeth_l3_delete_ip_worker(struct work_struct *work)
{
struct qeth_l3_ip_event_work *ip_work = to_ip_work(work);
qeth_l3_modify_ip(ip_work->card, &ip_work->addr, false);
kfree(work);
}
static struct qeth_card *qeth_l3_get_card_from_dev(struct net_device *dev)
{
if (is_vlan_dev(dev))
......@@ -2575,9 +2619,12 @@ static int qeth_l3_ip6_event(struct notifier_block *this,
{
struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)ptr;
struct net_device *dev = ifa->idev->dev;
struct qeth_ipaddr addr;
struct qeth_l3_ip_event_work *ip_work;
struct qeth_card *card;
if (event != NETDEV_UP && event != NETDEV_DOWN)
return NOTIFY_DONE;
card = qeth_l3_get_card_from_dev(dev);
if (!card)
return NOTIFY_DONE;
......@@ -2585,11 +2632,23 @@ static int qeth_l3_ip6_event(struct notifier_block *this,
if (!qeth_is_supported(card, IPA_IPV6))
return NOTIFY_DONE;
qeth_l3_init_ipaddr(&addr, QETH_IP_TYPE_NORMAL, QETH_PROT_IPV6);
addr.u.a6.addr = ifa->addr;
addr.u.a6.pfxlen = ifa->prefix_len;
ip_work = kmalloc(sizeof(*ip_work), GFP_ATOMIC);
if (!ip_work)
return NOTIFY_DONE;
return qeth_l3_handle_ip_event(card, &addr, event);
if (event == NETDEV_UP)
INIT_WORK(&ip_work->work, qeth_l3_add_ip_worker);
else
INIT_WORK(&ip_work->work, qeth_l3_delete_ip_worker);
ip_work->card = card;
qeth_l3_init_ipaddr(&ip_work->addr, QETH_IP_TYPE_NORMAL,
QETH_PROT_IPV6);
ip_work->addr.u.a6.addr = ifa->addr;
ip_work->addr.u.a6.pfxlen = ifa->prefix_len;
queue_work(card->cmd_wq, &ip_work->work);
return NOTIFY_OK;
}
static struct notifier_block qeth_l3_ip6_notifier = {
......
......@@ -367,9 +367,9 @@ static ssize_t qeth_l3_dev_ipato_enable_store(struct device *dev,
if (card->ipato.enabled != enable) {
card->ipato.enabled = enable;
spin_lock_bh(&card->ip_lock);
mutex_lock(&card->ip_lock);
qeth_l3_update_ipato(card);
spin_unlock_bh(&card->ip_lock);
mutex_unlock(&card->ip_lock);
}
out:
mutex_unlock(&card->conf_mutex);
......@@ -412,9 +412,9 @@ static ssize_t qeth_l3_dev_ipato_invert4_store(struct device *dev,
if (card->ipato.invert4 != invert) {
card->ipato.invert4 = invert;
spin_lock_bh(&card->ip_lock);
mutex_lock(&card->ip_lock);
qeth_l3_update_ipato(card);
spin_unlock_bh(&card->ip_lock);
mutex_unlock(&card->ip_lock);
}
out:
mutex_unlock(&card->conf_mutex);
......@@ -436,7 +436,7 @@ static ssize_t qeth_l3_dev_ipato_add_show(char *buf, struct qeth_card *card,
entry_len = (proto == QETH_PROT_IPV4)? 12 : 40;
/* add strlen for "/<mask>\n" */
entry_len += (proto == QETH_PROT_IPV4)? 5 : 6;
spin_lock_bh(&card->ip_lock);
mutex_lock(&card->ip_lock);
list_for_each_entry(ipatoe, &card->ipato.entries, entry) {
if (ipatoe->proto != proto)
continue;
......@@ -449,7 +449,7 @@ static ssize_t qeth_l3_dev_ipato_add_show(char *buf, struct qeth_card *card,
i += snprintf(buf + i, PAGE_SIZE - i,
"%s/%i\n", addr_str, ipatoe->mask_bits);
}
spin_unlock_bh(&card->ip_lock);
mutex_unlock(&card->ip_lock);
i += snprintf(buf + i, PAGE_SIZE - i, "\n");
return i;
......@@ -598,9 +598,9 @@ static ssize_t qeth_l3_dev_ipato_invert6_store(struct device *dev,
if (card->ipato.invert6 != invert) {
card->ipato.invert6 = invert;
spin_lock_bh(&card->ip_lock);
mutex_lock(&card->ip_lock);
qeth_l3_update_ipato(card);
spin_unlock_bh(&card->ip_lock);
mutex_unlock(&card->ip_lock);
}
out:
mutex_unlock(&card->conf_mutex);
......@@ -684,7 +684,7 @@ static ssize_t qeth_l3_dev_ip_add_show(struct device *dev, char *buf,
entry_len = (proto == QETH_PROT_IPV4)? 12 : 40;
entry_len += 2; /* \n + terminator */
spin_lock_bh(&card->ip_lock);
mutex_lock(&card->ip_lock);
hash_for_each(card->ip_htable, i, ipaddr, hnode) {
if (ipaddr->proto != proto || ipaddr->type != type)
continue;
......@@ -698,7 +698,7 @@ static ssize_t qeth_l3_dev_ip_add_show(struct device *dev, char *buf,
str_len += snprintf(buf + str_len, PAGE_SIZE - str_len, "%s\n",
addr_str);
}
spin_unlock_bh(&card->ip_lock);
mutex_unlock(&card->ip_lock);
str_len += snprintf(buf + str_len, PAGE_SIZE - str_len, "\n");
return str_len;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment