aboutsummaryrefslogtreecommitdiff
path: root/drivers/s390/net/qeth_main.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/s390/net/qeth_main.c')
-rw-r--r--drivers/s390/net/qeth_main.c8959
1 files changed, 0 insertions, 8959 deletions
diff --git a/drivers/s390/net/qeth_main.c b/drivers/s390/net/qeth_main.c
deleted file mode 100644
index d063e9ecf80..00000000000
--- a/drivers/s390/net/qeth_main.c
+++ /dev/null
@@ -1,8959 +0,0 @@
-/*
- * linux/drivers/s390/net/qeth_main.c
- *
- * Linux on zSeries OSA Express and HiperSockets support
- *
- * Copyright 2000,2003 IBM Corporation
- *
- * Author(s): Original Code written by
- * Utz Bacher (utz.bacher@de.ibm.com)
- * Rewritten by
- * Frank Pavlic (fpavlic@de.ibm.com) and
- * Thomas Spatzier <tspat@de.ibm.com>
- *
- * 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
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/mm.h>
-#include <linux/ip.h>
-#include <linux/inetdevice.h>
-#include <linux/netdevice.h>
-#include <linux/sched.h>
-#include <linux/workqueue.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/tcp.h>
-#include <linux/icmp.h>
-#include <linux/skbuff.h>
-#include <linux/in.h>
-#include <linux/igmp.h>
-#include <linux/init.h>
-#include <linux/reboot.h>
-#include <linux/mii.h>
-#include <linux/rcupdate.h>
-#include <linux/ethtool.h>
-
-#include <net/arp.h>
-#include <net/ip.h>
-#include <net/route.h>
-
-#include <asm/ebcdic.h>
-#include <asm/io.h>
-#include <asm/qeth.h>
-#include <asm/timex.h>
-#include <asm/semaphore.h>
-#include <asm/uaccess.h>
-#include <asm/s390_rdev.h>
-
-#include "qeth.h"
-#include "qeth_mpc.h"
-#include "qeth_fs.h"
-#include "qeth_eddp.h"
-#include "qeth_tso.h"
-
-static const char *version = "qeth S/390 OSA-Express driver";
-
-/**
- * Debug Facility Stuff
- */
-static debug_info_t *qeth_dbf_setup = NULL;
-static debug_info_t *qeth_dbf_data = NULL;
-static debug_info_t *qeth_dbf_misc = NULL;
-static debug_info_t *qeth_dbf_control = NULL;
-debug_info_t *qeth_dbf_trace = NULL;
-static debug_info_t *qeth_dbf_sense = NULL;
-static debug_info_t *qeth_dbf_qerr = NULL;
-
-DEFINE_PER_CPU(char[256], qeth_dbf_txt_buf);
-
-static struct lock_class_key qdio_out_skb_queue_key;
-
-/**
- * some more definitions and declarations
- */
-static unsigned int known_devices[][10] = QETH_MODELLIST_ARRAY;
-
-/* list of our cards */
-struct qeth_card_list_struct qeth_card_list;
-/*process list want to be notified*/
-spinlock_t qeth_notify_lock;
-struct list_head qeth_notify_list;
-
-static void qeth_send_control_data_cb(struct qeth_channel *,
- struct qeth_cmd_buffer *);
-
-/**
- * here we go with function implementation
- */
-static void
-qeth_init_qdio_info(struct qeth_card *card);
-
-static int
-qeth_init_qdio_queues(struct qeth_card *card);
-
-static int
-qeth_alloc_qdio_buffers(struct qeth_card *card);
-
-static void
-qeth_free_qdio_buffers(struct qeth_card *);
-
-static void
-qeth_clear_qdio_buffers(struct qeth_card *);
-
-static void
-qeth_clear_ip_list(struct qeth_card *, int, int);
-
-static void
-qeth_clear_ipacmd_list(struct qeth_card *);
-
-static int
-qeth_qdio_clear_card(struct qeth_card *, int);
-
-static void
-qeth_clear_working_pool_list(struct qeth_card *);
-
-static void
-qeth_clear_cmd_buffers(struct qeth_channel *);
-
-static int
-qeth_stop(struct net_device *);
-
-static void
-qeth_clear_ipato_list(struct qeth_card *);
-
-static int
-qeth_is_addr_covered_by_ipato(struct qeth_card *, struct qeth_ipaddr *);
-
-static void
-qeth_irq_tasklet(unsigned long);
-
-static int
-qeth_set_online(struct ccwgroup_device *);
-
-static int
-__qeth_set_online(struct ccwgroup_device *gdev, int recovery_mode);
-
-static struct qeth_ipaddr *
-qeth_get_addr_buffer(enum qeth_prot_versions);
-
-static void
-qeth_set_multicast_list(struct net_device *);
-
-static void
-qeth_setadp_promisc_mode(struct qeth_card *);
-
-static int
-qeth_hard_header_parse(const struct sk_buff *skb, unsigned char *haddr);
-
-static void
-qeth_notify_processes(void)
-{
- /*notify all registered processes */
- struct qeth_notify_list_struct *n_entry;
-
- QETH_DBF_TEXT(trace,3,"procnoti");
- spin_lock(&qeth_notify_lock);
- list_for_each_entry(n_entry, &qeth_notify_list, list) {
- send_sig(n_entry->signum, n_entry->task, 1);
- }
- spin_unlock(&qeth_notify_lock);
-
-}
-int
-qeth_notifier_unregister(struct task_struct *p)
-{
- struct qeth_notify_list_struct *n_entry, *tmp;
-
- QETH_DBF_TEXT(trace, 2, "notunreg");
- spin_lock(&qeth_notify_lock);
- list_for_each_entry_safe(n_entry, tmp, &qeth_notify_list, list) {
- if (n_entry->task == p) {
- list_del(&n_entry->list);
- kfree(n_entry);
- goto out;
- }
- }
-out:
- spin_unlock(&qeth_notify_lock);
- return 0;
-}
-int
-qeth_notifier_register(struct task_struct *p, int signum)
-{
- struct qeth_notify_list_struct *n_entry;
-
- /*check first if entry already exists*/
- spin_lock(&qeth_notify_lock);
- list_for_each_entry(n_entry, &qeth_notify_list, list) {
- if (n_entry->task == p) {
- n_entry->signum = signum;
- spin_unlock(&qeth_notify_lock);
- return 0;
- }
- }
- spin_unlock(&qeth_notify_lock);
-
- n_entry = (struct qeth_notify_list_struct *)
- kmalloc(sizeof(struct qeth_notify_list_struct),GFP_KERNEL);
- if (!n_entry)
- return -ENOMEM;
- n_entry->task = p;
- n_entry->signum = signum;
- spin_lock(&qeth_notify_lock);
- list_add(&n_entry->list,&qeth_notify_list);
- spin_unlock(&qeth_notify_lock);
- return 0;
-}
-
-
-/**
- * free channel command buffers
- */
-static void
-qeth_clean_channel(struct qeth_channel *channel)
-{
- int cnt;
-
- QETH_DBF_TEXT(setup, 2, "freech");
- for (cnt = 0; cnt < QETH_CMD_BUFFER_NO; cnt++)
- kfree(channel->iob[cnt].data);
-}
-
-/**
- * free card
- */
-static void
-qeth_free_card(struct qeth_card *card)
-{
-
- QETH_DBF_TEXT(setup, 2, "freecrd");
- QETH_DBF_HEX(setup, 2, &card, sizeof(void *));
- qeth_clean_channel(&card->read);
- qeth_clean_channel(&card->write);
- if (card->dev)
- free_netdev(card->dev);
- qeth_clear_ip_list(card, 0, 0);
- qeth_clear_ipato_list(card);
- kfree(card->ip_tbd_list);
- qeth_free_qdio_buffers(card);
- kfree(card);
-}
-
-/**
- * alloc memory for command buffer per channel
- */
-static int
-qeth_setup_channel(struct qeth_channel *channel)
-{
- int cnt;
-
- QETH_DBF_TEXT(setup, 2, "setupch");
- for (cnt=0; cnt < QETH_CMD_BUFFER_NO; cnt++) {
- channel->iob[cnt].data = (char *)
- kmalloc(QETH_BUFSIZE, GFP_DMA|GFP_KERNEL);
- if (channel->iob[cnt].data == NULL)
- break;
- channel->iob[cnt].state = BUF_STATE_FREE;
- channel->iob[cnt].channel = channel;
- channel->iob[cnt].callback = qeth_send_control_data_cb;
- channel->iob[cnt].rc = 0;
- }
- if (cnt < QETH_CMD_BUFFER_NO) {
- while (cnt-- > 0)
- kfree(channel->iob[cnt].data);
- return -ENOMEM;
- }
- channel->buf_no = 0;
- channel->io_buf_no = 0;
- atomic_set(&channel->irq_pending, 0);
- spin_lock_init(&channel->iob_lock);
-
- init_waitqueue_head(&channel->wait_q);
- channel->irq_tasklet.data = (unsigned long) channel;
- channel->irq_tasklet.func = qeth_irq_tasklet;
- return 0;
-}
-
-/**
- * alloc memory for card structure
- */
-static struct qeth_card *
-qeth_alloc_card(void)
-{
- struct qeth_card *card;
-
- QETH_DBF_TEXT(setup, 2, "alloccrd");
- card = kzalloc(sizeof(struct qeth_card), GFP_DMA|GFP_KERNEL);
- if (!card)
- return NULL;
- QETH_DBF_HEX(setup, 2, &card, sizeof(void *));
- if (qeth_setup_channel(&card->read)) {
- kfree(card);
- return NULL;
- }
- if (qeth_setup_channel(&card->write)) {
- qeth_clean_channel(&card->read);
- kfree(card);
- return NULL;
- }
- return card;
-}
-
-static long
-__qeth_check_irb_error(struct ccw_device *cdev, unsigned long intparm,
- struct irb *irb)
-{
- if (!IS_ERR(irb))
- return 0;
-
- switch (PTR_ERR(irb)) {
- case -EIO:
- PRINT_WARN("i/o-error on device %s\n", cdev->dev.bus_id);
- QETH_DBF_TEXT(trace, 2, "ckirberr");
- QETH_DBF_TEXT_(trace, 2, " rc%d", -EIO);
- break;
- case -ETIMEDOUT:
- PRINT_WARN("timeout on device %s\n", cdev->dev.bus_id);
- QETH_DBF_TEXT(trace, 2, "ckirberr");
- QETH_DBF_TEXT_(trace, 2, " rc%d", -ETIMEDOUT);
- if (intparm == QETH_RCD_PARM) {
- struct qeth_card *card = CARD_FROM_CDEV(cdev);
-
- if (card && (card->data.ccwdev == cdev)) {
- card->data.state = CH_STATE_DOWN;
- wake_up(&card->wait_q);
- }
- }
- break;
- default:
- PRINT_WARN("unknown error %ld on device %s\n", PTR_ERR(irb),
- cdev->dev.bus_id);
- QETH_DBF_TEXT(trace, 2, "ckirberr");
- QETH_DBF_TEXT(trace, 2, " rc???");
- }
- return PTR_ERR(irb);
-}
-
-static int
-qeth_get_problem(struct ccw_device *cdev, struct irb *irb)
-{
- int dstat,cstat;
- char *sense;
-
- sense = (char *) irb->ecw;
- cstat = irb->scsw.cstat;
- dstat = irb->scsw.dstat;
-
- if (cstat & (SCHN_STAT_CHN_CTRL_CHK | SCHN_STAT_INTF_CTRL_CHK |
- SCHN_STAT_CHN_DATA_CHK | SCHN_STAT_CHAIN_CHECK |
- SCHN_STAT_PROT_CHECK | SCHN_STAT_PROG_CHECK)) {
- QETH_DBF_TEXT(trace,2, "CGENCHK");
- PRINT_WARN("check on device %s, dstat=x%x, cstat=x%x ",
- cdev->dev.bus_id, dstat, cstat);
- HEXDUMP16(WARN, "irb: ", irb);
- HEXDUMP16(WARN, "irb: ", ((char *) irb) + 32);
- return 1;
- }
-
- if (dstat & DEV_STAT_UNIT_CHECK) {
- if (sense[SENSE_RESETTING_EVENT_BYTE] &
- SENSE_RESETTING_EVENT_FLAG) {
- QETH_DBF_TEXT(trace,2,"REVIND");
- return 1;
- }
- if (sense[SENSE_COMMAND_REJECT_BYTE] &
- SENSE_COMMAND_REJECT_FLAG) {
- QETH_DBF_TEXT(trace,2,"CMDREJi");
- return 0;
- }
- if ((sense[2] == 0xaf) && (sense[3] == 0xfe)) {
- QETH_DBF_TEXT(trace,2,"AFFE");
- return 1;
- }
- if ((!sense[0]) && (!sense[1]) && (!sense[2]) && (!sense[3])) {
- QETH_DBF_TEXT(trace,2,"ZEROSEN");
- return 0;
- }
- QETH_DBF_TEXT(trace,2,"DGENCHK");
- return 1;
- }
- return 0;
-}
-static int qeth_issue_next_read(struct qeth_card *);
-
-/**
- * interrupt handler
- */
-static void
-qeth_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
-{
- int rc;
- int cstat,dstat;
- struct qeth_cmd_buffer *buffer;
- struct qeth_channel *channel;
- struct qeth_card *card;
-
- QETH_DBF_TEXT(trace,5,"irq");
-
- if (__qeth_check_irb_error(cdev, intparm, irb))
- return;
- cstat = irb->scsw.cstat;
- dstat = irb->scsw.dstat;
-
- card = CARD_FROM_CDEV(cdev);
- if (!card)
- return;
-
- if (card->read.ccwdev == cdev){
- channel = &card->read;
- QETH_DBF_TEXT(trace,5,"read");
- } else if (card->write.ccwdev == cdev) {
- channel = &card->write;
- QETH_DBF_TEXT(trace,5,"write");
- } else {
- channel = &card->data;
- QETH_DBF_TEXT(trace,5,"data");
- }
- atomic_set(&channel->irq_pending, 0);
-
- if (irb->scsw.fctl & (SCSW_FCTL_CLEAR_FUNC))
- channel->state = CH_STATE_STOPPED;
-
- if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC))
- channel->state = CH_STATE_HALTED;
-
- /*let's wake up immediately on data channel*/
- if ((channel == &card->data) && (intparm != 0) &&
- (intparm != QETH_RCD_PARM))
- goto out;
-
- if (intparm == QETH_CLEAR_CHANNEL_PARM) {
- QETH_DBF_TEXT(trace, 6, "clrchpar");
- /* we don't have to handle this further */
- intparm = 0;
- }
- if (intparm == QETH_HALT_CHANNEL_PARM) {
- QETH_DBF_TEXT(trace, 6, "hltchpar");
- /* we don't have to handle this further */
- intparm = 0;
- }
- if ((dstat & DEV_STAT_UNIT_EXCEP) ||
- (dstat & DEV_STAT_UNIT_CHECK) ||
- (cstat)) {
- if (irb->esw.esw0.erw.cons) {
- /* TODO: we should make this s390dbf */
- PRINT_WARN("sense data available on channel %s.\n",
- CHANNEL_ID(channel));
- PRINT_WARN(" cstat 0x%X\n dstat 0x%X\n", cstat, dstat);
- HEXDUMP16(WARN,"irb: ",irb);
- HEXDUMP16(WARN,"sense data: ",irb->ecw);
- }
- if (intparm == QETH_RCD_PARM) {
- channel->state = CH_STATE_DOWN;
- goto out;
- }
- rc = qeth_get_problem(cdev,irb);
- if (rc) {
- qeth_schedule_recovery(card);
- goto out;
- }
- }
-
- if (intparm == QETH_RCD_PARM) {
- channel->state = CH_STATE_RCD_DONE;
- goto out;
- }
- if (intparm) {
- buffer = (struct qeth_cmd_buffer *) __va((addr_t)intparm);
- buffer->state = BUF_STATE_PROCESSED;
- }
- if (channel == &card->data)
- return;
-
- if (channel == &card->read &&
- channel->state == CH_STATE_UP)
- qeth_issue_next_read(card);
-
- qeth_irq_tasklet((unsigned long)channel);
- return;
-out:
- wake_up(&card->wait_q);
-}
-
-/**
- * tasklet function scheduled from irq handler
- */
-static void
-qeth_irq_tasklet(unsigned long data)
-{
- struct qeth_card *card;
- struct qeth_channel *channel;
- struct qeth_cmd_buffer *iob;
- __u8 index;
-
- QETH_DBF_TEXT(trace,5,"irqtlet");
- channel = (struct qeth_channel *) data;
- iob = channel->iob;
- index = channel->buf_no;
- card = CARD_FROM_CDEV(channel->ccwdev);
- while (iob[index].state == BUF_STATE_PROCESSED) {
- if (iob[index].callback !=NULL) {
- iob[index].callback(channel,iob + index);
- }
- index = (index + 1) % QETH_CMD_BUFFER_NO;
- }
- channel->buf_no = index;
- wake_up(&card->wait_q);
-}
-
-static int qeth_stop_card(struct qeth_card *, int);
-
-static int
-__qeth_set_offline(struct ccwgroup_device *cgdev, int recovery_mode)
-{
- struct qeth_card *card = (struct qeth_card *) cgdev->dev.driver_data;
- int rc = 0, rc2 = 0, rc3 = 0;
- enum qeth_card_states recover_flag;
-
- QETH_DBF_TEXT(setup, 3, "setoffl");
- QETH_DBF_HEX(setup, 3, &card, sizeof(void *));
-
- if (card->dev && netif_carrier_ok(card->dev))
- netif_carrier_off(card->dev);
- recover_flag = card->state;
- if (qeth_stop_card(card, recovery_mode) == -ERESTARTSYS){
- PRINT_WARN("Stopping card %s interrupted by user!\n",
- CARD_BUS_ID(card));
- return -ERESTARTSYS;
- }
- rc = ccw_device_set_offline(CARD_DDEV(card));
- rc2 = ccw_device_set_offline(CARD_WDEV(card));
- rc3 = ccw_device_set_offline(CARD_RDEV(card));
- if (!rc)
- rc = (rc2) ? rc2 : rc3;
- if (rc)
- QETH_DBF_TEXT_(setup, 2, "1err%d", rc);
- if (recover_flag == CARD_STATE_UP)
- card->state = CARD_STATE_RECOVER;
- qeth_notify_processes();
- return 0;
-}
-
-static int
-qeth_set_offline(struct ccwgroup_device *cgdev)
-{
- return __qeth_set_offline(cgdev, 0);
-}
-
-static int
-qeth_threads_running(struct qeth_card *card, unsigned long threads);
-
-
-static void
-qeth_remove_device(struct ccwgroup_device *cgdev)
-{
- struct qeth_card *card = (struct qeth_card *) cgdev->dev.driver_data;
- unsigned long flags;
-
- QETH_DBF_TEXT(setup, 3, "rmdev");
- QETH_DBF_HEX(setup, 3, &card, sizeof(void *));
-
- if (!card)
- return;
-
- wait_event(card->wait_q, qeth_threads_running(card, 0xffffffff) == 0);
-
- if (cgdev->state == CCWGROUP_ONLINE){
- card->use_hard_stop = 1;
- qeth_set_offline(cgdev);
- }
- /* remove form our internal list */
- write_lock_irqsave(&qeth_card_list.rwlock, flags);
- list_del(&card->list);
- write_unlock_irqrestore(&qeth_card_list.rwlock, flags);
- if (card->dev)
- unregister_netdev(card->dev);
- qeth_remove_device_attributes(&cgdev->dev);
- qeth_free_card(card);
- cgdev->dev.driver_data = NULL;
- put_device(&cgdev->dev);
-}
-
-static int
-qeth_register_addr_entry(struct qeth_card *, struct qeth_ipaddr *);
-static int
-qeth_deregister_addr_entry(struct qeth_card *, struct qeth_ipaddr *);
-
-/**
- * Add/remove address to/from card's ip list, i.e. try to add or remove
- * reference to/from an IP address that is already registered on the card.
- * Returns:
- * 0 address was on card and its reference count has been adjusted,
- * but is still > 0, so nothing has to be done
- * also returns 0 if card was not on card and the todo was to delete
- * the address -> there is also nothing to be done
- * 1 address was not on card and the todo is to add it to the card's ip
- * list
- * -1 address was on card and its reference count has been decremented
- * to <= 0 by the todo -> address must be removed from card
- */
-static int
-__qeth_ref_ip_on_card(struct qeth_card *card, struct qeth_ipaddr *todo,
- struct qeth_ipaddr **__addr)
-{
- struct qeth_ipaddr *addr;
- int found = 0;
-
- list_for_each_entry(addr, &card->ip_list, entry) {
- if (card->options.layer2) {
- if ((addr->type == todo->type) &&
- (memcmp(&addr->mac, &todo->mac,
- OSA_ADDR_LEN) == 0)) {
- found = 1;
- break;
- }
- continue;
- }
- if ((addr->proto == QETH_PROT_IPV4) &&
- (todo->proto == QETH_PROT_IPV4) &&
- (addr->type == todo->type) &&
- (addr->u.a4.addr == todo->u.a4.addr) &&
- (addr->u.a4.mask == todo->u.a4.mask)) {
- found = 1;
- break;
- }
- if ((addr->proto == QETH_PROT_IPV6) &&
- (todo->proto == QETH_PROT_IPV6) &&
- (addr->type == todo->type) &&
- (addr->u.a6.pfxlen == todo->u.a6.pfxlen) &&
- (memcmp(&addr->u.a6.addr, &todo->u.a6.addr,
- sizeof(struct in6_addr)) == 0)) {
- found = 1;
- break;
- }
- }
- if (found) {
- addr->users += todo->users;
- if (addr->users <= 0){
- *__addr = addr;
- return -1;
- } else {
- /* for VIPA and RXIP limit refcount to 1 */
- if (addr->type != QETH_IP_TYPE_NORMAL)
- addr->users = 1;
- return 0;
- }
- }
- if (todo->users > 0) {
- /* for VIPA and RXIP limit refcount to 1 */
- if (todo->type != QETH_IP_TYPE_NORMAL)
- todo->users = 1;
- return 1;
- } else
- return 0;
-}
-
-static int
-__qeth_address_exists_in_list(struct list_head *list, struct qeth_ipaddr *addr,
- int same_type)
-{
- struct qeth_ipaddr *tmp;
-
- list_for_each_entry(tmp, list, entry) {
- if ((tmp->proto == QETH_PROT_IPV4) &&
- (addr->proto == QETH_PROT_IPV4) &&
- ((same_type && (tmp->type == addr->type)) ||
- (!same_type && (tmp->type != addr->type)) ) &&
- (tmp->u.a4.addr == addr->u.a4.addr) ){
- return 1;
- }
- if ((tmp->proto == QETH_PROT_IPV6) &&
- (addr->proto == QETH_PROT_IPV6) &&
- ((same_type && (tmp->type == addr->type)) ||
- (!same_type && (tmp->type != addr->type)) ) &&
- (memcmp(&tmp->u.a6.addr, &addr->u.a6.addr,
- sizeof(struct in6_addr)) == 0) ) {
- return 1;
- }
- }
- return 0;
-}
-
-/*
- * Add IP to be added to todo list. If there is already an "add todo"
- * in this list we just incremenent the reference count.
- * Returns 0 if we just incremented reference count.
- */
-static int
-__qeth_insert_ip_todo(struct qeth_card *card, struct qeth_ipaddr *addr, int add)
-{
- struct qeth_ipaddr *tmp, *t;
- int found = 0;
-
- list_for_each_entry_safe(tmp, t, card->ip_tbd_list, entry) {
- if ((addr->type == QETH_IP_TYPE_DEL_ALL_MC) &&
- (tmp->type == QETH_IP_TYPE_DEL_ALL_MC))
- return 0;
- if (card->options.layer2) {
- if ((tmp->type == addr->type) &&
- (tmp->is_multicast == addr->is_multicast) &&
- (memcmp(&tmp->mac, &addr->mac,
- OSA_ADDR_LEN) == 0)) {
- found = 1;
- break;
- }
- continue;
- }
- if ((tmp->proto == QETH_PROT_IPV4) &&
- (addr->proto == QETH_PROT_IPV4) &&
- (tmp->type == addr->type) &&
- (tmp->is_multicast == addr->is_multicast) &&
- (tmp->u.a4.addr == addr->u.a4.addr) &&
- (tmp->u.a4.mask == addr->u.a4.mask)) {
- found = 1;
- break;
- }
- if ((tmp->proto == QETH_PROT_IPV6) &&
- (addr->proto == QETH_PROT_IPV6) &&
- (tmp->type == addr->type) &&
- (tmp->is_multicast == addr->is_multicast) &&
- (tmp->u.a6.pfxlen == addr->u.a6.pfxlen) &&
- (memcmp(&tmp->u.a6.addr, &addr->u.a6.addr,
- sizeof(struct in6_addr)) == 0)) {
- found = 1;
- break;
- }
- }
- if (found){
- if (addr->users != 0)
- tmp->users += addr->users;
- else
- tmp->users += add? 1:-1;
- if (tmp->users == 0) {
- list_del(&tmp->entry);
- kfree(tmp);
- }
- return 0;
- } else {
- if (addr->type == QETH_IP_TYPE_DEL_ALL_MC)
- list_add(&addr->entry, card->ip_tbd_list);
- else {
- if (addr->users == 0)
- addr->users += add? 1:-1;
- if (add && (addr->type == QETH_IP_TYPE_NORMAL) &&
- qeth_is_addr_covered_by_ipato(card, addr)){
- QETH_DBF_TEXT(trace, 2, "tkovaddr");
- addr->set_flags |= QETH_IPA_SETIP_TAKEOVER_FLAG;
- }
- list_add_tail(&addr->entry, card->ip_tbd_list);
- }
- return 1;
- }
-}
-
-/**
- * Remove IP address from list
- */
-static int
-qeth_delete_ip(struct qeth_card *card, struct qeth_ipaddr *addr)
-{
- unsigned long flags;
- int rc = 0;
-
- QETH_DBF_TEXT(trace, 4, "delip");
-
- if (card->options.layer2)
- QETH_DBF_HEX(trace, 4, &addr->mac, 6);
- else if (addr->proto == QETH_PROT_IPV4)
- QETH_DBF_HEX(trace, 4, &addr->u.a4.addr, 4);
- else {
- QETH_DBF_HEX(trace, 4, &addr->u.a6.addr, 8);
- QETH_DBF_HEX(trace, 4, ((char *)&addr->u.a6.addr) + 8, 8);
- }
- spin_lock_irqsave(&card->ip_lock, flags);
- rc = __qeth_insert_ip_todo(card, addr, 0);
- spin_unlock_irqrestore(&card->ip_lock, flags);
- return rc;
-}
-
-static int
-qeth_add_ip(struct qeth_card *card, struct qeth_ipaddr *addr)
-{
- unsigned long flags;
- int rc = 0;
-
- QETH_DBF_TEXT(trace, 4, "addip");
- if (card->options.layer2)
- QETH_DBF_HEX(trace, 4, &addr->mac, 6);
- else if (addr->proto == QETH_PROT_IPV4)
- QETH_DBF_HEX(trace, 4, &addr->u.a4.addr, 4);
- else {
- QETH_DBF_HEX(trace, 4, &addr->u.a6.addr, 8);
- QETH_DBF_HEX(trace, 4, ((char *)&addr->u.a6.addr) + 8, 8);
- }
- spin_lock_irqsave(&card->ip_lock, flags);
- rc = __qeth_insert_ip_todo(card, addr, 1);
- spin_unlock_irqrestore(&card->ip_lock, flags);
- return rc;
-}
-
-static void
-__qeth_delete_all_mc(struct qeth_card *card, unsigned long *flags)
-{
- struct qeth_ipaddr *addr, *tmp;
- int rc;
-again:
- list_for_each_entry_safe(addr, tmp, &card->ip_list, entry) {
- if (addr->is_multicast) {
- list_del(&addr->entry);
- spin_unlock_irqrestore(&card->ip_lock, *flags);
- rc = qeth_deregister_addr_entry(card, addr);
- spin_lock_irqsave(&card->ip_lock, *flags);
- if (!rc) {
- kfree(addr);
- goto again;
- } else
- list_add(&addr->entry, &card->ip_list);
- }
- }
-}
-
-static void
-qeth_set_ip_addr_list(struct qeth_card *card)
-{
- struct list_head *tbd_list;
- struct qeth_ipaddr *todo, *addr;
- unsigned long flags;
- int rc;
-
- QETH_DBF_TEXT(trace, 2, "sdiplist");
- QETH_DBF_HEX(trace, 2, &card, sizeof(void *));
-
- spin_lock_irqsave(&card->ip_lock, flags);
- tbd_list = card->ip_tbd_list;
- card->ip_tbd_list = kmalloc(sizeof(struct list_head), GFP_ATOMIC);
- if (!card->ip_tbd_list) {
- QETH_DBF_TEXT(trace, 0, "silnomem");
- card->ip_tbd_list = tbd_list;
- spin_unlock_irqrestore(&card->ip_lock, flags);
- return;
- } else
- INIT_LIST_HEAD(card->ip_tbd_list);
-
- while (!list_empty(tbd_list)){
- todo = list_entry(tbd_list->next, struct qeth_ipaddr, entry);
- list_del(&todo->entry);
- if (todo->type == QETH_IP_TYPE_DEL_ALL_MC){
- __qeth_delete_all_mc(card, &flags);
- kfree(todo);
- continue;
- }
- rc = __qeth_ref_ip_on_card(card, todo, &addr);
- if (rc == 0) {
- /* nothing to be done; only adjusted refcount */
- kfree(todo);
- } else if (rc == 1) {
- /* new entry to be added to on-card list */
- spin_unlock_irqrestore(&card->ip_lock, flags);
- rc = qeth_register_addr_entry(card, todo);
- spin_lock_irqsave(&card->ip_lock, flags);
- if (!rc)
- list_add_tail(&todo->entry, &card->ip_list);
- else
- kfree(todo);
- } else if (rc == -1) {
- /* on-card entry to be removed */
- list_del_init(&addr->entry);
- spin_unlock_irqrestore(&card->ip_lock, flags);
- rc = qeth_deregister_addr_entry(card, addr);
- spin_lock_irqsave(&card->ip_lock, flags);
- if (!rc)
- kfree(addr);
- else
- list_add_tail(&addr->entry, &card->ip_list);
- kfree(todo);
- }
- }
- spin_unlock_irqrestore(&card->ip_lock, flags);
- kfree(tbd_list);
-}
-
-static void qeth_delete_mc_addresses(struct qeth_card *);
-static void qeth_add_multicast_ipv4(struct qeth_card *);
-static void qeth_layer2_add_multicast(struct qeth_card *);
-#ifdef CONFIG_QETH_IPV6
-static void qeth_add_multicast_ipv6(struct qeth_card *);
-#endif
-
-static int
-qeth_set_thread_start_bit(struct qeth_card *card, unsigned long thread)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&card->thread_mask_lock, flags);
- if ( !(card->thread_allowed_mask & thread) ||
- (card->thread_start_mask & thread) ) {
- spin_unlock_irqrestore(&card->thread_mask_lock, flags);
- return -EPERM;
- }
- card->thread_start_mask |= thread;
- spin_unlock_irqrestore(&card->thread_mask_lock, flags);
- return 0;
-}
-
-static void
-qeth_clear_thread_start_bit(struct qeth_card *card, unsigned long thread)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&card->thread_mask_lock, flags);
- card->thread_start_mask &= ~thread;
- spin_unlock_irqrestore(&card->thread_mask_lock, flags);
- wake_up(&card->wait_q);
-}
-
-static void
-qeth_clear_thread_running_bit(struct qeth_card *card, unsigned long thread)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&card->thread_mask_lock, flags);
- card->thread_running_mask &= ~thread;
- spin_unlock_irqrestore(&card->thread_mask_lock, flags);
- wake_up(&card->wait_q);
-}
-
-static int
-__qeth_do_run_thread(struct qeth_card *card, unsigned long thread)
-{
- unsigned long flags;
- int rc = 0;
-
- spin_lock_irqsave(&card->thread_mask_lock, flags);
- if (card->thread_start_mask & thread){
- if ((card->thread_allowed_mask & thread) &&
- !(card->thread_running_mask & thread)){
- rc = 1;
- card->thread_start_mask &= ~thread;
- card->thread_running_mask |= thread;
- } else
- rc = -EPERM;
- }
- spin_unlock_irqrestore(&card->thread_mask_lock, flags);
- return rc;
-}
-
-static int
-qeth_do_run_thread(struct qeth_card *card, unsigned long thread)
-{
- int rc = 0;
-
- wait_event(card->wait_q,
- (rc = __qeth_do_run_thread(card, thread)) >= 0);
- return rc;
-}
-
-static int
-qeth_recover(void *ptr)
-{
- struct qeth_card *card;
- int rc = 0;
-
- card = (struct qeth_card *) ptr;
- daemonize("qeth_recover");
- QETH_DBF_TEXT(trace,2,"recover1");
- QETH_DBF_HEX(trace, 2, &card, sizeof(void *));
- if (!qeth_do_run_thread(card, QETH_RECOVER_THREAD))
- return 0;
- QETH_DBF_TEXT(trace,2,"recover2");
- PRINT_WARN("Recovery of device %s started ...\n",
- CARD_BUS_ID(card));
- card->use_hard_stop = 1;
- __qeth_set_offline(card->gdev,1);
- rc = __qeth_set_online(card->gdev,1);
- /* don't run another scheduled recovery */
- qeth_clear_thread_start_bit(card, QETH_RECOVER_THREAD);
- qeth_clear_thread_running_bit(card, QETH_RECOVER_THREAD);
- if (!rc)
- PRINT_INFO("Device %s successfully recovered!\n",
- CARD_BUS_ID(card));
- else
- PRINT_INFO("Device %s could not be recovered!\n",
- CARD_BUS_ID(card));
- return 0;
-}
-
-void
-qeth_schedule_recovery(struct qeth_card *card)
-{
- QETH_DBF_TEXT(trace,2,"startrec");
- if (qeth_set_thread_start_bit(card, QETH_RECOVER_THREAD) == 0)
- schedule_work(&card->kernel_thread_starter);
-}
-
-static int
-qeth_do_start_thread(struct qeth_card *card, unsigned long thread)
-{
- unsigned long flags;
- int rc = 0;
-
- spin_lock_irqsave(&card->thread_mask_lock, flags);
- QETH_DBF_TEXT_(trace, 4, " %02x%02x%02x",
- (u8) card->thread_start_mask,
- (u8) card->thread_allowed_mask,
- (u8) card->thread_running_mask);
- rc = (card->thread_start_mask & thread);
- spin_unlock_irqrestore(&card->thread_mask_lock, flags);
- return rc;
-}
-
-static void
-qeth_start_kernel_thread(struct work_struct *work)
-{
- struct qeth_card *card = container_of(work, struct qeth_card, kernel_thread_starter);
- QETH_DBF_TEXT(trace , 2, "strthrd");
-
- if (card->read.state != CH_STATE_UP &&
- card->write.state != CH_STATE_UP)
- return;
- if (qeth_do_start_thread(card, QETH_RECOVER_THREAD))
- kernel_thread(qeth_recover, (void *) card, SIGCHLD);
-}
-
-
-static void
-qeth_set_intial_options(struct qeth_card *card)
-{
- card->options.route4.type = NO_ROUTER;
-#ifdef CONFIG_QETH_IPV6
- card->options.route6.type = NO_ROUTER;
-#endif /* QETH_IPV6 */
- card->options.checksum_type = QETH_CHECKSUM_DEFAULT;
- card->options.broadcast_mode = QETH_TR_BROADCAST_ALLRINGS;
- card->options.macaddr_mode = QETH_TR_MACADDR_NONCANONICAL;
- card->options.fake_broadcast = 0;
- card->options.add_hhlen = DEFAULT_ADD_HHLEN;
- card->options.fake_ll = 0;
- if (card->info.type == QETH_CARD_TYPE_OSN)
- card->options.layer2 = 1;
- else
- card->options.layer2 = 0;
- card->options.performance_stats = 0;
- card->options.rx_sg_cb = QETH_RX_SG_CB;
-}
-
-/**
- * initialize channels ,card and all state machines
- */
-static int
-qeth_setup_card(struct qeth_card *card)
-{
-
- QETH_DBF_TEXT(setup, 2, "setupcrd");
- QETH_DBF_HEX(setup, 2, &card, sizeof(void *));
-
- card->read.state = CH_STATE_DOWN;
- card->write.state = CH_STATE_DOWN;
- card->data.state = CH_STATE_DOWN;
- card->state = CARD_STATE_DOWN;
- card->lan_online = 0;
- card->use_hard_stop = 0;
- card->dev = NULL;
-#ifdef CONFIG_QETH_VLAN
- spin_lock_init(&card->vlanlock);
- card->vlangrp = NULL;
-#endif
- spin_lock_init(&card->lock);
- spin_lock_init(&card->ip_lock);
- spin_lock_init(&card->thread_mask_lock);
- card->thread_start_mask = 0;
- card->thread_allowed_mask = 0;
- card->thread_running_mask = 0;
- INIT_WORK(&card->kernel_thread_starter, qeth_start_kernel_thread);
- INIT_LIST_HEAD(&card->ip_list);
- card->ip_tbd_list = kmalloc(sizeof(struct list_head), GFP_KERNEL);
- if (!card->ip_tbd_list) {
- QETH_DBF_TEXT(setup, 0, "iptbdnom");
- return -ENOMEM;
- }
- INIT_LIST_HEAD(card->ip_tbd_list);
- INIT_LIST_HEAD(&card->cmd_waiter_list);
- init_waitqueue_head(&card->wait_q);
- /* intial options */
- qeth_set_intial_options(card);
- /* IP address takeover */
- INIT_LIST_HEAD(&card->ipato.entries);
- card->ipato.enabled = 0;
- card->ipato.invert4 = 0;
- card->ipato.invert6 = 0;
- /* init QDIO stuff */
- qeth_init_qdio_info(card);
- return 0;
-}
-
-static int
-is_1920_device (struct qeth_card *card)
-{
- int single_queue = 0;
- struct ccw_device *ccwdev;
- struct channelPath_dsc {
- u8 flags;
- u8 lsn;
- u8 desc;
- u8 chpid;
- u8 swla;
- u8 zeroes;
- u8 chla;
- u8 chpp;
- } *chp_dsc;
-
- QETH_DBF_TEXT(setup, 2, "chk_1920");
-
- ccwdev = card->data.ccwdev;
- chp_dsc = (struct channelPath_dsc *)ccw_device_get_chp_desc(ccwdev, 0);
- if (chp_dsc != NULL) {
- /* CHPP field bit 6 == 1 -> single queue */
- single_queue = ((chp_dsc->chpp & 0x02) == 0x02);
- kfree(chp_dsc);
- }
- QETH_DBF_TEXT_(setup, 2, "rc:%x", single_queue);
- return single_queue;
-}
-
-static int
-qeth_determine_card_type(struct qeth_card *card)
-{
- int i = 0;
-
- QETH_DBF_TEXT(setup, 2, "detcdtyp");
-
- card->qdio.do_prio_queueing = QETH_PRIOQ_DEFAULT;
- card->qdio.default_out_queue = QETH_DEFAULT_QUEUE;
- while (known_devices[i][4]) {
- if ((CARD_RDEV(card)->id.dev_type == known_devices[i][2]) &&
- (CARD_RDEV(card)->id.dev_model == known_devices[i][3])) {
- card->info.type = known_devices[i][4];
- card->qdio.no_out_queues = known_devices[i][8];
- card->info.is_multicast_different = known_devices[i][9];
- if (is_1920_device(card)) {
- PRINT_INFO("Priority Queueing not able "
- "due to hardware limitations!\n");
- card->qdio.no_out_queues = 1;
- card->qdio.default_out_queue = 0;
- }
- return 0;
- }
- i++;
- }
- card->info.type = QETH_CARD_TYPE_UNKNOWN;
- PRINT_ERR("unknown card type on device %s\n", CARD_BUS_ID(card));
- return -ENOENT;
-}
-
-static int
-qeth_probe_device(struct ccwgroup_device *gdev)
-{
- struct qeth_card *card;
- struct device *dev;
- unsigned long flags;
- int rc;
-
- QETH_DBF_TEXT(setup, 2, "probedev");
-
- dev = &gdev->dev;
- if (!get_devi