Commit b3332930 authored by Frank Blaschka's avatar Frank Blaschka Committed by David S. Miller

qeth: add support for af_iucv HiperSockets transport

This patch extends the HiperSockets device driver to send and receive
af_iucv traffic over HiperSockets transport.
TX: Driver uses new asynchronous delivery of storage blocks to pass
    flow control/congestion information from the HiperSockets microcode
    to the af_iucv socket.
RX: Memory for incoming traffic is preallocated and passed to
    HiperSockets layer. If receiver is not capable to clean its buffers
    shared with HiperSockets and pass new memory to the HiperSockets
    layer this will cause flow control/congestion events on the
    sender.
Signed-off-by: default avatarFrank Blaschka <frank.blaschka@de.ibm.com>
Signed-off-by: default avatarEinar Lueck <elelueck@de.ibm.com>
Signed-off-by: default avatarUrsula Braun <ursula.braun@de.ibm.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 0da9581d
......@@ -217,6 +217,7 @@ static inline int qeth_is_ipa_enabled(struct qeth_ipa_info *ipa,
*/
#define QETH_TX_TIMEOUT 100 * HZ
#define QETH_RCD_TIMEOUT 60 * HZ
#define QETH_RECLAIM_WORK_TIME HZ
#define QETH_HEADER_SIZE 32
#define QETH_MAX_PORTNO 15
......@@ -265,6 +266,7 @@ static inline int qeth_is_ipa_enabled(struct qeth_ipa_info *ipa,
/* large receive scatter gather copy break */
#define QETH_RX_SG_CB (PAGE_SIZE >> 1)
#define QETH_RX_PULL_LEN 256
struct qeth_hdr_layer3 {
__u8 id;
......@@ -380,6 +382,16 @@ enum qeth_qdio_buffer_states {
* outbound: filled by driver; owned by hardware in order to be sent
*/
QETH_QDIO_BUF_PRIMED,
/*
* inbound: not applicable
* outbound: identified to be pending in TPQ
*/
QETH_QDIO_BUF_PENDING,
/*
* inbound: not applicable
* outbound: found in completion queue
*/
QETH_QDIO_BUF_IN_CQ,
/*
* inbound: not applicable
* outbound: handled via transfer pending / completion queue
......@@ -409,6 +421,7 @@ struct qeth_qdio_buffer {
struct qdio_buffer *buffer;
/* the buffer pool entry currently associated to this buffer */
struct qeth_buffer_pool_entry *pool_entry;
struct sk_buff *rx_skb;
};
struct qeth_qdio_q {
......@@ -674,6 +687,7 @@ struct qeth_card_options {
enum qeth_ipa_isolation_modes isolation;
int sniffer;
enum qeth_cq cq;
char hsuid[9];
};
/*
......@@ -771,6 +785,8 @@ struct qeth_card {
struct mutex discipline_mutex;
struct napi_struct napi;
struct qeth_rx rx;
struct delayed_work buffer_reclaim_work;
int reclaim_index;
};
struct qeth_card_list_struct {
......@@ -836,6 +852,7 @@ int qeth_core_create_device_attributes(struct device *);
void qeth_core_remove_device_attributes(struct device *);
int qeth_core_create_osn_attributes(struct device *);
void qeth_core_remove_osn_attributes(struct device *);
void qeth_buffer_reclaim_work(struct work_struct *);
/* exports for qeth discipline device drivers */
extern struct qeth_card_list_struct qeth_core_card_list;
......@@ -864,7 +881,7 @@ int qeth_check_qdio_errors(struct qeth_card *, struct qdio_buffer *,
unsigned int, const char *);
void qeth_queue_input_buffer(struct qeth_card *, int);
struct sk_buff *qeth_core_get_next_skb(struct qeth_card *,
struct qdio_buffer *, struct qdio_buffer_element **, int *,
struct qeth_qdio_buffer *, struct qdio_buffer_element **, int *,
struct qeth_hdr **);
void qeth_schedule_recovery(struct qeth_card *);
void qeth_qdio_start_poll(struct ccw_device *, int, unsigned long);
......
This diff is collapsed.
......@@ -409,7 +409,7 @@ static int qeth_l2_process_inbound_buffer(struct qeth_card *card,
BUG_ON(!budget);
while (budget) {
skb = qeth_core_get_next_skb(card,
card->qdio.in_q->bufs[card->rx.b_index].buffer,
&card->qdio.in_q->bufs[card->rx.b_index],
&card->rx.b_element, &card->rx.e_offset, &hdr);
if (!skb) {
*done = 1;
......
......@@ -63,5 +63,9 @@ int qeth_l3_add_rxip(struct qeth_card *, enum qeth_prot_versions, const u8 *);
void qeth_l3_del_rxip(struct qeth_card *card, enum qeth_prot_versions,
const u8 *);
int qeth_l3_is_addr_covered_by_ipato(struct qeth_card *, struct qeth_ipaddr *);
struct qeth_ipaddr *qeth_l3_get_addr_buffer(enum qeth_prot_versions);
int qeth_l3_add_ip(struct qeth_card *, struct qeth_ipaddr *);
int qeth_l3_delete_ip(struct qeth_card *, struct qeth_ipaddr *);
void qeth_l3_set_ip_addr_list(struct qeth_card *);
#endif /* __QETH_L3_H__ */
......@@ -29,6 +29,7 @@
#include <net/ip.h>
#include <net/arp.h>
#include <net/ip6_checksum.h>
#include <net/iucv/af_iucv.h>
#include "qeth_l3.h"
......@@ -267,7 +268,7 @@ static int __qeth_l3_insert_ip_todo(struct qeth_card *card,
}
}
static int qeth_l3_delete_ip(struct qeth_card *card, struct qeth_ipaddr *addr)
int qeth_l3_delete_ip(struct qeth_card *card, struct qeth_ipaddr *addr)
{
unsigned long flags;
int rc = 0;
......@@ -286,7 +287,7 @@ static int qeth_l3_delete_ip(struct qeth_card *card, struct qeth_ipaddr *addr)
return rc;
}
static int qeth_l3_add_ip(struct qeth_card *card, struct qeth_ipaddr *addr)
int qeth_l3_add_ip(struct qeth_card *card, struct qeth_ipaddr *addr)
{
unsigned long flags;
int rc = 0;
......@@ -305,7 +306,7 @@ static int qeth_l3_add_ip(struct qeth_card *card, struct qeth_ipaddr *addr)
}
static struct qeth_ipaddr *qeth_l3_get_addr_buffer(
struct qeth_ipaddr *qeth_l3_get_addr_buffer(
enum qeth_prot_versions prot)
{
struct qeth_ipaddr *addr;
......@@ -421,7 +422,7 @@ static void __qeth_l3_delete_all_mc(struct qeth_card *card,
list_splice(&fail_list, &card->ip_list);
}
static void qeth_l3_set_ip_addr_list(struct qeth_card *card)
void qeth_l3_set_ip_addr_list(struct qeth_card *card)
{
struct list_head *tbd_list;
struct qeth_ipaddr *todo, *addr;
......@@ -438,7 +439,7 @@ static void qeth_l3_set_ip_addr_list(struct qeth_card *card)
spin_lock_irqsave(&card->ip_lock, flags);
tbd_list = card->ip_tbd_list;
card->ip_tbd_list = kmalloc(sizeof(struct list_head), GFP_ATOMIC);
card->ip_tbd_list = kzalloc(sizeof(struct list_head), GFP_ATOMIC);
if (!card->ip_tbd_list) {
QETH_CARD_TEXT(card, 0, "silnomem");
card->ip_tbd_list = tbd_list;
......@@ -1993,12 +1994,13 @@ static int qeth_l3_process_inbound_buffer(struct qeth_card *card,
__u16 vlan_tag = 0;
int is_vlan;
unsigned int len;
__u16 magic;
*done = 0;
BUG_ON(!budget);
while (budget) {
skb = qeth_core_get_next_skb(card,
card->qdio.in_q->bufs[card->rx.b_index].buffer,
&card->qdio.in_q->bufs[card->rx.b_index],
&card->rx.b_element, &card->rx.e_offset, &hdr);
if (!skb) {
*done = 1;
......@@ -2007,12 +2009,26 @@ static int qeth_l3_process_inbound_buffer(struct qeth_card *card,
skb->dev = card->dev;
switch (hdr->hdr.l3.id) {
case QETH_HEADER_TYPE_LAYER3:
is_vlan = qeth_l3_rebuild_skb(card, skb, hdr,
magic = *(__u16 *)skb->data;
if ((card->info.type == QETH_CARD_TYPE_IQD) &&
(magic == ETH_P_AF_IUCV)) {
skb->protocol = ETH_P_AF_IUCV;
skb->pkt_type = PACKET_HOST;
skb->mac_header = NET_SKB_PAD;
skb->dev = card->dev;
len = skb->len;
card->dev->header_ops->create(skb, card->dev, 0,
card->dev->dev_addr, "FAKELL",
card->dev->addr_len);
netif_receive_skb(skb);
} else {
is_vlan = qeth_l3_rebuild_skb(card, skb, hdr,
&vlan_tag);
len = skb->len;
if (is_vlan && !card->options.sniffer)
__vlan_hwaccel_put_tag(skb, vlan_tag);
napi_gro_receive(&card->napi, skb);
len = skb->len;
if (is_vlan && !card->options.sniffer)
__vlan_hwaccel_put_tag(skb, vlan_tag);
napi_gro_receive(&card->napi, skb);
}
break;
case QETH_HEADER_TYPE_LAYER2: /* for HiperSockets sniffer */
skb->pkt_type = PACKET_HOST;
......@@ -2784,6 +2800,30 @@ int inline qeth_l3_get_cast_type(struct qeth_card *card, struct sk_buff *skb)
return cast_type;
}
static void qeth_l3_fill_af_iucv_hdr(struct qeth_card *card,
struct qeth_hdr *hdr, struct sk_buff *skb)
{
char daddr[16];
struct af_iucv_trans_hdr *iucv_hdr;
skb_pull(skb, 14);
card->dev->header_ops->create(skb, card->dev, 0,
card->dev->dev_addr, card->dev->dev_addr,
card->dev->addr_len);
skb_pull(skb, 14);
iucv_hdr = (struct af_iucv_trans_hdr *)skb->data;
memset(hdr, 0, sizeof(struct qeth_hdr));
hdr->hdr.l3.id = QETH_HEADER_TYPE_LAYER3;
hdr->hdr.l3.ext_flags = 0;
hdr->hdr.l3.length = skb->len;
hdr->hdr.l3.flags = QETH_HDR_IPV6 | QETH_CAST_UNICAST;
memset(daddr, 0, sizeof(daddr));
daddr[0] = 0xfe;
daddr[1] = 0x80;
memcpy(&daddr[8], iucv_hdr->destUserID, 8);
memcpy(hdr->hdr.l3.dest_addr, daddr, 16);
}
static void qeth_l3_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
struct sk_buff *skb, int ipv, int cast_type)
{
......@@ -2936,8 +2976,11 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
int data_offset = -1;
int nr_frags;
if (((card->info.type == QETH_CARD_TYPE_IQD) && (!ipv)) ||
card->options.sniffer)
if (((card->info.type == QETH_CARD_TYPE_IQD) &&
(((card->options.cq != QETH_CQ_ENABLED) && !ipv) ||
((card->options.cq == QETH_CQ_ENABLED) &&
(skb->protocol != ETH_P_AF_IUCV)))) ||
card->options.sniffer)
goto tx_drop;
if ((card->state != CARD_STATE_UP) || !card->lan_online) {
......@@ -2959,7 +3002,10 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
if ((card->info.type == QETH_CARD_TYPE_IQD) && (!large_send) &&
(skb_shinfo(skb)->nr_frags == 0)) {
new_skb = skb;
data_offset = ETH_HLEN;
if (new_skb->protocol == ETH_P_AF_IUCV)
data_offset = 0;
else
data_offset = ETH_HLEN;
hdr = kmem_cache_alloc(qeth_core_header_cache, GFP_ATOMIC);
if (!hdr)
goto tx_drop;
......@@ -3024,9 +3070,13 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
qeth_l3_fill_header(card, hdr, new_skb, ipv,
cast_type);
} else {
qeth_l3_fill_header(card, hdr, new_skb, ipv,
cast_type);
hdr->hdr.l3.length = new_skb->len - data_offset;
if (new_skb->protocol == ETH_P_AF_IUCV)
qeth_l3_fill_af_iucv_hdr(card, hdr, new_skb);
else {
qeth_l3_fill_header(card, hdr, new_skb, ipv,
cast_type);
hdr->hdr.l3.length = new_skb->len - data_offset;
}
}
if (skb->ip_summed == CHECKSUM_PARTIAL)
......@@ -3289,6 +3339,8 @@ static int qeth_l3_setup_netdev(struct qeth_card *card)
card->dev->flags |= IFF_NOARP;
card->dev->netdev_ops = &qeth_l3_netdev_ops;
qeth_l3_iqd_read_initial_mac(card);
if (card->options.hsuid[0])
memcpy(card->dev->perm_addr, card->options.hsuid, 9);
} else
return -ENODEV;
......@@ -3659,7 +3711,6 @@ static int qeth_l3_ip6_event(struct notifier_block *this,
struct qeth_ipaddr *addr;
struct qeth_card *card;
card = qeth_l3_get_card_from_dev(dev);
if (!card)
return NOTIFY_DONE;
......
......@@ -9,7 +9,7 @@
*/
#include <linux/slab.h>
#include <asm/ebcdic.h>
#include "qeth_l3.h"
#define QETH_DEVICE_ATTR(_id, _name, _mode, _show, _store) \
......@@ -308,6 +308,8 @@ static ssize_t qeth_l3_dev_sniffer_store(struct device *dev,
if (card->info.type != QETH_CARD_TYPE_IQD)
return -EPERM;
if (card->options.cq == QETH_CQ_ENABLED)
return -EPERM;
mutex_lock(&card->conf_mutex);
if ((card->state != CARD_STATE_DOWN) &&
......@@ -347,6 +349,111 @@ static ssize_t qeth_l3_dev_sniffer_store(struct device *dev,
static DEVICE_ATTR(sniffer, 0644, qeth_l3_dev_sniffer_show,
qeth_l3_dev_sniffer_store);
static ssize_t qeth_l3_dev_hsuid_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct qeth_card *card = dev_get_drvdata(dev);
char tmp_hsuid[9];
if (!card)
return -EINVAL;
if (card->info.type != QETH_CARD_TYPE_IQD)
return -EPERM;
if (card->state == CARD_STATE_DOWN)
return -EPERM;
memcpy(tmp_hsuid, card->options.hsuid, sizeof(tmp_hsuid));
EBCASC(tmp_hsuid, 8);
return sprintf(buf, "%s\n", tmp_hsuid);
}
static ssize_t qeth_l3_dev_hsuid_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct qeth_card *card = dev_get_drvdata(dev);
struct qeth_ipaddr *addr;
char *tmp;
int i;
if (!card)
return -EINVAL;
if (card->info.type != QETH_CARD_TYPE_IQD)
return -EPERM;
if (card->state != CARD_STATE_DOWN &&
card->state != CARD_STATE_RECOVER)
return -EPERM;
if (card->options.sniffer)
return -EPERM;
if (card->options.cq == QETH_CQ_NOTAVAILABLE)
return -EPERM;
tmp = strsep((char **)&buf, "\n");
if (strlen(tmp) > 8)
return -EINVAL;
if (card->options.hsuid[0]) {
/* delete old ip address */
addr = qeth_l3_get_addr_buffer(QETH_PROT_IPV6);
if (addr != NULL) {
addr->u.a6.addr.s6_addr32[0] = 0xfe800000;
addr->u.a6.addr.s6_addr32[1] = 0x00000000;
for (i = 8; i < 16; i++)
addr->u.a6.addr.s6_addr[i] =
card->options.hsuid[i - 8];
addr->u.a6.pfxlen = 0;
addr->type = QETH_IP_TYPE_NORMAL;
} else
return -ENOMEM;
if (!qeth_l3_delete_ip(card, addr))
kfree(addr);
qeth_l3_set_ip_addr_list(card);
}
if (strlen(tmp) == 0) {
/* delete ip address only */
card->options.hsuid[0] = '\0';
if (card->dev)
memcpy(card->dev->perm_addr, card->options.hsuid, 9);
qeth_configure_cq(card, QETH_CQ_DISABLED);
return count;
}
if (qeth_configure_cq(card, QETH_CQ_ENABLED))
return -EPERM;
for (i = 0; i < 8; i++)
card->options.hsuid[i] = ' ';
card->options.hsuid[8] = '\0';
strncpy(card->options.hsuid, tmp, strlen(tmp));
ASCEBC(card->options.hsuid, 8);
if (card->dev)
memcpy(card->dev->perm_addr, card->options.hsuid, 9);
addr = qeth_l3_get_addr_buffer(QETH_PROT_IPV6);
if (addr != NULL) {
addr->u.a6.addr.s6_addr32[0] = 0xfe800000;
addr->u.a6.addr.s6_addr32[1] = 0x00000000;
for (i = 8; i < 16; i++)
addr->u.a6.addr.s6_addr[i] = card->options.hsuid[i - 8];
addr->u.a6.pfxlen = 0;
addr->type = QETH_IP_TYPE_NORMAL;
} else
return -ENOMEM;
if (!qeth_l3_add_ip(card, addr))
kfree(addr);
qeth_l3_set_ip_addr_list(card);
return count;
}
static DEVICE_ATTR(hsuid, 0644, qeth_l3_dev_hsuid_show,
qeth_l3_dev_hsuid_store);
static struct attribute *qeth_l3_device_attrs[] = {
&dev_attr_route4.attr,
&dev_attr_route6.attr,
......@@ -354,6 +461,7 @@ static struct attribute *qeth_l3_device_attrs[] = {
&dev_attr_broadcast_mode.attr,
&dev_attr_canonical_macaddr.attr,
&dev_attr_sniffer.attr,
&dev_attr_hsuid.attr,
NULL,
};
......
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