/*
*
- * linux/drivers/s390/net/qeth.c ($Revision: 1.160 $)
+ * linux/drivers/s390/net/qeth.c ($Revision: 1.177 $)
*
* Linux on zSeries OSA Express and HiperSockets support
*
* numerous bugfixes)
* Frank Pavlic <pavlic@de.ibm.com> (query/purge ARP, SNMP, fixes)
* Andreas Herrmann <aherrman@de.ibm.com> (bugfixes)
+ * Thomas Spatzier <tspat@de.ibm.com> (bugfixes)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
"reserved for low memory situations");
/****************** MODULE STUFF **********************************/
-#define VERSION_QETH_C "$Revision: 1.160 $"
+#define VERSION_QETH_C "$Revision: 1.177 $"
static const char *version = "qeth S/390 OSA-Express driver ("
VERSION_QETH_C "/" VERSION_QETH_H "/" VERSION_QETH_MPC_H
QETH_VERSION_IPV6 QETH_VERSION_VLAN ")";
}
}
+static int inline
+my_down_trylock_nonbusy(struct qeth_card *card, struct semaphore *sema)
+{
+ for (;;) {
+ if (card) {
+ if (atomic_read(&card->shutdown_phase))
+ return -1;
+ }
+ if (down_trylock(sema))
+ return 0;
+ qeth_wait_nonbusy(QETH_IDLE_WAIT_TIME);
+ }
+}
+
+
#ifdef CONFIG_ARCH_S390X
#define QETH_GET_ADDR(x) ((__u32)(unsigned long)x)
#else /* CONFIG_ARCH_S390X */
qeth_is_multicast_skb_at_all(struct sk_buff *skb, int version)
{
int i;
+ struct qeth_card *card;
+
+ i = RTN_UNSPEC;
+ card = (struct qeth_card *)skb->dev->priv;
if (skb->dst && skb->dst->neighbour) {
i = skb->dst->neighbour->type;
return ((i == RTN_BROADCAST) ||
return ((skb->nh.raw[16] & 0xf0) == 0xe0) ? RTN_MULTICAST : 0;
} else if (version == 6) {
return (skb->nh.raw[24] == 0xff) ? RTN_MULTICAST : 0;
- } else {
- PRINT_STUPID("QETH_IP_VERSION is %x\n", version);
- PRINT_STUPID("skb->protocol=x%x=%i\n",
- skb->protocol, skb->protocol);
- HEXDUMP16(STUPID, "skb:", skb->data);
}
- return 0;
+ if (!memcmp(skb->nh.raw, skb->dev->broadcast, 6)) {
+ i = RTN_BROADCAST;
+ } else {
+ __u16 hdr_mac;
+
+ hdr_mac = *((__u16*)skb->nh.raw);
+ /* tr multicast? */
+ switch (card->link_type) {
+ case QETH_MPC_LINK_TYPE_HSTR:
+ case QETH_MPC_LINK_TYPE_LANE_TR:
+ if ((hdr_mac == QETH_TR_MAC_NC) ||
+ (hdr_mac == QETH_TR_MAC_C))
+ i = RTN_MULTICAST;
+ break;
+ /* eth or so multicast? */
+ default:
+ if ((hdr_mac == QETH_ETH_MAC_V4) ||
+ (hdr_mac == QETH_ETH_MAC_V6))
+ i = RTN_MULTICAST;
+ }
+ }
+ return ((i == RTN_BROADCAST)||
+ (i == RTN_MULTICAST)||
+ (i == RTN_ANYCAST)) ? i : 0;
}
static int
qeth_get_prioqueue(struct qeth_card *card, struct sk_buff *skb,
int multicast, int version)
{
- if (!version)
+ if (!version && (card->type == QETH_CARD_TYPE_OSAE))
return QETH_DEFAULT_QUEUE;
switch (card->no_queues) {
case 1:
QETH_FAKE_LL_ADDR_LEN);
} else {
/* clear source MAC for security reasons */
- memset(skb->mac.raw + QETH_FAKE_LL_DEST_MAC_POS,
+ memset(skb->mac.raw + QETH_FAKE_LL_SRC_MAC_POS,
0, QETH_FAKE_LL_ADDR_LEN);
}
memcpy(skb->mac.raw + QETH_FAKE_LL_PROT_POS,
case ERROR_LINK_FAILURE:
case ERROR_KICK_THAT_PUPPY:
QETH_DBF_TEXT4(0, trace, "endeglnd");
- dst_link_failure(skb);
atomic_dec(&skb->users);
dev_kfree_skb_irq(skb);
break;
if (!card) {
QETH_DBF_TEXT2(0, trace, "XMNSNOCD");
- dst_link_failure(skb);
dev_kfree_skb_irq(skb);
return 0;
}
if (!atomic_read(&card->is_startlaned)) {
card->stats->tx_carrier_errors++;
QETH_DBF_CARD2(0, trace, "XMNS", card);
- dst_link_failure(skb);
dev_kfree_skb_irq(skb);
return 0;
}
qeth_wakeup_procfile(void)
{
QETH_DBF_TEXT5(0, trace, "procwkup");
- if (atomic_read(&qeth_procfile_ioctl_sem.count) <
+ /* is this if statement correct? */
+ if (atomic_read(&qeth_procfile_ioctl_sem.count) <=
PROCFILE_SLEEP_SEM_MAX_VALUE)
up(&qeth_procfile_ioctl_sem);
}
{
QETH_DBF_TEXT5(0, trace, "procslp");
if (down_interruptible(&qeth_procfile_ioctl_sem)) {
- up(&qeth_procfile_ioctl_sem);
return -ERESTARTSYS;
}
return 0;
QETH_DBF_TEXT2(0, trace, "scd:doio");
sprintf(dbf_text, "%4x", (__s16) result);
QETH_DBF_TEXT2(0, trace, dbf_text);
+ /* re-enable qeth_send_control_data again */
+ atomic_set(&card->write_busy,0);
return NULL;
}
atomic_set(&card->write_busy, 0);
return NULL;
}
- rec_buf = card->ipa_buf;
+ rec_buf = card->dma_stuff->recbuf;
QETH_DBF_CARD2(0, trace, "scro", card);
} else {
if (qeth_sleepon(card, (setip) ? QETH_IPA_TIMEOUT :
(result==0xe00e)?"unsupported arp assist cmd": \
(result==0xe00f)?"arp assist not enabled": \
(result==0xe080)?"startlan disabled": \
+ (result==0xf012)?"unicast IP address invalid": \
+ (result==0xf013)?"multicast router limit reached": \
+ (result==0xf014)?"stop assist not supported": \
+ (result==0xf015)?"multicast assist not set": \
+ (result==0xf080)?"VM: startlan disabled": \
(result==-1)?"IPA communication timeout": \
"unknown return code")
QETH_DBF_TEXT2(0, trace, dbf_text);
}
- if (((result == -1) || (result == 0xe080)) && (retries--)) {
+ if (((result == -1) || (result == 0xe080) ||(result==0xf080)) &&
+ (retries--)) {
QETH_DBF_CARD2(0, trace, "sipr", card);
if (ip_vers == 4) {
*((__u32 *) (&dbf_text[0])) = *((__u32 *) ip);
le is last entry */
char dbf_text[15];
int result;
- __u8 netmask[16] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+ __u8 netmask[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
struct qeth_vipa_entry *priv_add_list = NULL;
struct qeth_vipa_entry *priv_del_list = NULL;
* so we clone the entry */
ne = (struct qeth_vipa_entry *)
kmalloc(sizeof (struct qeth_vipa_entry),
- GFP_KERNEL);
+ GFP_ATOMIC);
if (ne) {
ne->version = e->version;
+ ne->flag = e->flag;
memcpy(ne->ip, e->ip, 16);
ne->next = priv_add_list;
priv_add_list = ne;
GFP_KERNEL);
if (ne) {
ne->version = e->version;
+ ne->flag = e->flag;
memcpy(ne->ip, e->ip, 16);
ne->next = priv_del_list;
priv_del_list = ne;
sprintf(dbf_text, "%4x", result);
QETH_DBF_TEXT2(0, trace, dbf_text);
if (priv_add_list->version == 4) {
- PRINT_ERR("going to leave vipa/rxip %08x"
+ PRINT_ERR("going to leave vipa/rxip x%08x"
"unset...\n",
*((__u32 *) & priv_add_list->ip[0]));
sprintf(dbf_text, "%08x",
while (addr) {
if (qeth_is_ipma_in_list(addr,
card->ip_mc_current_state.ipm6_ifa)) {
+ qeth_remove_mc_ifa_from_list(
+ &card->ip_mc_new_state.ipm6_ifa,
+ addr);
addr = addr->next;
continue;
}
CARD_BUS_ID(card), result);
sprintf(dbf_text, "sms6%4x", result);
QETH_DBF_TEXT3(0, trace, dbf_text);
- qeth_remove_mc_ifa_from_list
- (&card->ip_mc_current_state.ipm6_ifa, addr);
+ } else {
+ qeth_remove_mc_ifa_from_list(
+ &card->ip_mc_new_state.ipm6_ifa,
+ addr);
+ qeth_add_mc_ifa_to_list(
+ &card->ip_mc_current_state.ipm6_ifa,
+ addr);
}
addr = addr->next;
}
ndisc_mc_map(&im6->mca_addr, buf, card->dev, 0);
ipmanew =
(struct qeth_ipm_mac *)
- kmalloc(sizeof (struct qeth_ipm_mac), GFP_KERNEL);
+ kmalloc(sizeof (struct qeth_ipm_mac), GFP_ATOMIC);
if (!ipmanew) {
PRINT_WARN("No memory for IPM address "
"handling. Multicast IP "
while (ifa) {
ifanew =
- kmalloc(sizeof (struct inet6_ifaddr), GFP_KERNEL);
+ kmalloc(sizeof (struct inet6_ifaddr), GFP_ATOMIC);
if (!ifanew) {
PRINT_WARN("No memory for IP address "
"handling. Some of the IPs "
qeth_get_mac_for_ipm(im4->multiaddr, buf, in4_dev->dev);
ipmanew =
(struct qeth_ipm_mac *)
- kmalloc(sizeof (struct qeth_ipm_mac), GFP_KERNEL);
+ kmalloc(sizeof (struct qeth_ipm_mac), GFP_ATOMIC);
if (!ipmanew) {
PRINT_WARN("No memory for IPM address "
"handling. Multicast IP %08x"
ifa = ((struct in_device *) card->dev->ip_ptr)->ifa_list;
while (ifa) {
- ifanew = kmalloc(sizeof (struct in_ifaddr), GFP_KERNEL);
+ ifanew = kmalloc(sizeof (struct in_ifaddr), GFP_ATOMIC);
if (!ifanew) {
PRINT_WARN("No memory for IP address "
"handling. Some of the IPs "
QETH_DBF_TEXT2(0, trace, dbf_text);
atomic_set(&card->is_softsetup, 0);
/* do not return an error */
- if (result == 0xe080)
+ if ((result == 0xe080) || (result == 0xf080))
result = 0;
return result;
}
"failure -- please check the "
"network, plug in the cable or "
"enable the OSA port" :
+ (result==0xf080) ?
+ "startlan disabled (VM: LAN " \
+ "is offline for functions " \
+ "requiring LAN access.":
"unknown return code");
sprintf(dbf_text, "stln%4x", result);
QETH_DBF_TEXT2(0, trace, dbf_text);
atomic_set(&card->is_softsetup, 0);
atomic_set(&card->is_startlaned, 0);
/* do not return an error */
- if (result == 0xe080) {
+ if ((result == 0xe080) || (result == 0xf080)) {
result = 0;
}
return result;
if (!atomic_read(&card->enable_routing_attempts6))
return;
- if (!card->options.routing_type6) {
+ if (!card->options.routing_type6 ||
+ ((card->type == QETH_CARD_TYPE_OSAE) &&
+ ((card->options.routing_type6&ROUTER_MASK) == MULTICAST_ROUTER) &&
+ !qeth_is_supported6(IPA_OSA_MC_ROUTER_AVAIL))) {
atomic_set(&card->enable_routing_attempts6, 0);
atomic_set(&card->rt6fld, 0);
return;
int use_setip_retries = 1;
if (wait_for_lock == QETH_WAIT_FOR_LOCK) {
- spin_lock(&card->softsetup_lock);
+ down(&card->softsetup_sema);
} else if (wait_for_lock == QETH_DONT_WAIT_FOR_LOCK) {
- if (!spin_trylock(&card->softsetup_lock)) {
+ if (!down_trylock(&card->softsetup_sema)) {
return -EAGAIN;
}
} else if (wait_for_lock == QETH_LOCK_ALREADY_HELD) {
netif_wake_queue(card->dev);
}
if (wait_for_lock != QETH_LOCK_ALREADY_HELD)
- spin_unlock(&card->softsetup_lock);
+ up(&card->softsetup_sema);
return result;
}
cleanup_qdio = in_recovery; /* if we are in recovery, we clean
the qdio stuff up */
- spin_lock(&card->hardsetup_lock);
+ down(&card->hardsetup_sema);
atomic_set(&card->write_busy, 0);
do {
}
exit:
- spin_unlock(&card->hardsetup_lock);
+ up(&card->hardsetup_sema);
return result;
}
atomic_set(&card->escape_softsetup, 1);
- if (-1 == my_spin_lock_nonbusy(card, &card->softsetup_lock)) {
+ if (-1 == my_down_trylock_nonbusy(card, &card->softsetup_sema)) {
atomic_set(&card->escape_softsetup, 0);
goto out;
}
atomic_set(&card->escape_softsetup, 0);
if (atomic_read(&card->shutdown_phase)) {
- spin_unlock(&card->softsetup_lock);
+ up(&card->softsetup_sema);
goto out_wakeup;
}
if (!qeth_verify_card(card))
} else {
QETH_DBF_TEXT1(0, trace, "ri-sftst");
qeth_softsetup_card(card, QETH_LOCK_ALREADY_HELD);
- spin_unlock(&card->softsetup_lock);
+ up(&card->softsetup_sema);
if (!already_registered) {
QETH_DBF_TEXT1(0, trace, "ri-regcd");
qeth_fill_qeth_card_options(card);
- spin_lock_init(&card->softsetup_lock);
- spin_lock_init(&card->hardsetup_lock);
+ init_MUTEX(&card->softsetup_sema);
+ init_MUTEX(&card->hardsetup_sema);
spin_lock_init(&card->ioctl_lock);
#ifdef QETH_VLAN
spin_lock_init(&card->vlan_lock);
card->options.do_prio_queueing = NO_PRIO_QUEUEING;
} else {
/* if it's a mc router, it's no router */
- if ((card->options.routing_type4 == MULTICAST_ROUTER) ||
+ if ((!qeth_is_supported(IPA_OSA_MC_ROUTER_AVAIL) &&
+ (card->options.routing_type4 == MULTICAST_ROUTER)) ||
(card->options.routing_type4 == PRIMARY_CONNECTOR) ||
(card->options.routing_type4 == SECONDARY_CONNECTOR)) {
PRINT_WARN("routing not applicable, reset "
card->options.do_prio_queueing = NO_PRIO_QUEUEING;
} else {
/* if it's a mc router, it's no router */
- if ((card->options.routing_type6 == MULTICAST_ROUTER) ||
+ if ((!qeth_is_supported(IPA_OSA_MC_ROUTER_AVAIL) &&
+ (card->options.routing_type6 == MULTICAST_ROUTER)) ||
(card->options.routing_type6 == PRIMARY_CONNECTOR) ||
(card->options.routing_type6 == SECONDARY_CONNECTOR)) {
PRINT_WARN("routing not applicable, reset "
QETH_DBF_TEXT2(0, trace, "procread");
length += sprintf(buffer + length,
- "devices CHPID "
+ "devices CHPID "
"device cardtype port chksum prio-q'ing "
"rtr fsz cnt\n");
length += sprintf(buffer + length,
- "-------------------- --- ----"
+ "-------------------------- --- ----"
"------ -------------- -- -- ---------- "
"--- --- ---\n");
card = firstcard;
{
int result;
- down_interruptible(&qeth_procfile_ioctl_lock);
- switch (cmd) {
-
- case QETH_IOCPROC_OSAEINTERFACES:
- result = qeth_procfile_getinterfaces(arg);
- break;
- case QETH_IOCPROC_INTERFACECHANGES:
- result = qeth_procfile_interfacechanges(arg);
- break;
- default:
- result = -EOPNOTSUPP;
- }
- up(&qeth_procfile_ioctl_lock);
+ if (!down_interruptible(&qeth_procfile_ioctl_lock)) {
+ switch (cmd) {
+ case QETH_IOCPROC_OSAEINTERFACES:
+ result = qeth_procfile_getinterfaces(arg);
+ break;
+ case QETH_IOCPROC_INTERFACECHANGES:
+ result = qeth_procfile_interfacechanges(arg);
+ break;
+ default:
+ result = -EOPNOTSUPP;
+ }
+ up(&qeth_procfile_ioctl_lock);
+ } else
+ result = -ERESTARTSYS;
return result;
};
.remove = ccwgroup_remove_ccwdev,
};
-static void
-qeth_root_dev_release (struct device *dev)
-{
-}
-
-static struct device qeth_root_dev = {
- .bus_id = "qeth",
- .release = qeth_root_dev_release,
-};
+static struct device *qeth_root_dev;
static struct ccwgroup_driver qeth_ccwgroup_driver;
static ssize_t
}
pr_debug("creating qeth group device from '%s', '%s' and '%s'\n",
bus_ids[0], bus_ids[1], bus_ids[2]);
- ccwgroup_create(&qeth_root_dev, qeth_ccwgroup_driver.driver_id,
+ ccwgroup_create(qeth_root_dev, qeth_ccwgroup_driver.driver_id,
&qeth_ccw_driver, 3, argv);
return count;
}
return -ENODEV;
}
-static int
-qeth_remove_device(struct ccwgroup_device *gdev)
-{
- struct qeth_card *card = gdev->dev.driver_data;
-
- __qeth_remove_attributes(&gdev->dev);
- gdev->dev.driver_data = NULL;
- if (card)
- qeth_free_card(card);
- put_device(&gdev->dev);
- return 0;
-}
-
static int
qeth_set_online(struct ccwgroup_device *gdev)
{
return 0;
}
+static void
+qeth_remove_device(struct ccwgroup_device *gdev)
+{
+ struct qeth_card *card = gdev->dev.driver_data;
+
+ if (card && qeth_does_card_exist(card))
+ /* Means that card is already in list. */
+ qeth_set_offline(gdev);
+ __qeth_remove_attributes(&gdev->dev);
+ gdev->dev.driver_data = NULL;
+ if (card)
+ qeth_free_card(card);
+ put_device(&gdev->dev);
+}
+
static struct ccwgroup_driver qeth_ccwgroup_driver = {
.name = "qeth",
.driver_id = 0xD8C5E3C8,
if (result)
goto out_cdrv;
- result = device_register(&qeth_root_dev);
- if (result)
+ qeth_root_dev = s390_root_dev_register("qeth");
+ if (IS_ERR(qeth_root_dev)) {
+ result = PTR_ERR(qeth_root_dev);
goto out_file;
-
+ }
qeth_register_notifiers();
qeth_add_procfs_entries();
driver_remove_file(&qeth_ccwgroup_driver.driver, &driver_attr_group);
ccw_driver_unregister(&qeth_ccw_driver);
ccwgroup_driver_unregister(&qeth_ccwgroup_driver);
- device_unregister(&qeth_root_dev);
+ s390_root_dev_unregister(qeth_root_dev);
while (firstcard) {
struct qeth_card *card = firstcard;