X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fs390%2Fnet%2Flcs.c;h=e5665b6743a1379d37e949cb549fab52dbb40021;hb=97bf2856c6014879bd04983a3e9dfcdac1e7fe85;hp=cac57951ae28f956690fa8bad659128328af3784;hpb=9bf4aaab3e101692164d49b7ca357651eb691cb6;p=linux-2.6.git diff --git a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c index cac57951a..e5665b674 100644 --- a/drivers/s390/net/lcs.c +++ b/drivers/s390/net/lcs.c @@ -8,11 +8,9 @@ * Author(s): Original Code written by * DJ Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com) * Rewritten by - * Frank Pavlic (pavlic@de.ibm.com) and + * Frank Pavlic (fpavlic@de.ibm.com) and * Martin Schwidefsky * - * $Revision: 1.85 $ $Date: 2004/08/04 11:05:43 $ - * * 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) @@ -56,20 +54,23 @@ #error Cannot compile lcs.c without some net devices switched on. #endif +#define PRINTK_HEADER " lcs: " + /** * initialization string for output */ -#define VERSION_LCS_C "$Revision: 1.85 $" -static char version[] __initdata = "LCS driver ("VERSION_LCS_C "/" VERSION_LCS_H ")"; +static char version[] __initdata = "LCS driver"; static char debug_buffer[255]; /** * Some prototypes. */ static void lcs_tasklet(unsigned long); -static void lcs_start_kernel_thread(struct lcs_card *card); +static void lcs_start_kernel_thread(struct work_struct *); static void lcs_get_frames_cb(struct lcs_channel *, struct lcs_buffer *); +static int lcs_send_delipm(struct lcs_card *, struct lcs_ipm_list *); +static int lcs_recovery(void *ptr); /** * Debug Facility Stuff @@ -92,8 +93,8 @@ lcs_unregister_debug_facility(void) static int lcs_register_debug_facility(void) { - lcs_dbf_setup = debug_register("lcs_setup", 1, 1, 8); - lcs_dbf_trace = debug_register("lcs_trace", 1, 2, 8); + lcs_dbf_setup = debug_register("lcs_setup", 2, 1, 8); + lcs_dbf_trace = debug_register("lcs_trace", 2, 2, 8); if (lcs_dbf_setup == NULL || lcs_dbf_trace == NULL) { PRINT_ERR("Not enough memory for debug facility.\n"); lcs_unregister_debug_facility(); @@ -117,12 +118,11 @@ lcs_alloc_channel(struct lcs_channel *channel) LCS_DBF_TEXT(2, setup, "ichalloc"); for (cnt = 0; cnt < LCS_NUM_BUFFS; cnt++) { /* alloc memory fo iobuffer */ - channel->iob[cnt].data = (void *) - kmalloc(LCS_IOBUFFERSIZE, GFP_DMA | GFP_KERNEL); + channel->iob[cnt].data = + kzalloc(LCS_IOBUFFERSIZE, GFP_DMA | GFP_KERNEL); if (channel->iob[cnt].data == NULL) break; - memset(channel->iob[cnt].data, 0, LCS_IOBUFFERSIZE); - channel->iob[cnt].state = BUF_STATE_EMPTY; + channel->iob[cnt].state = LCS_BUF_STATE_EMPTY; } if (cnt < LCS_NUM_BUFFS) { /* Not all io buffers could be allocated. */ @@ -144,8 +144,7 @@ lcs_free_channel(struct lcs_channel *channel) LCS_DBF_TEXT(2, setup, "ichfree"); for (cnt = 0; cnt < LCS_NUM_BUFFS; cnt++) { - if (channel->iob[cnt].data != NULL) - kfree(channel->iob[cnt].data); + kfree(channel->iob[cnt].data); channel->iob[cnt].data = NULL; } } @@ -185,11 +184,11 @@ lcs_alloc_card(void) LCS_DBF_TEXT(2, setup, "alloclcs"); - card = kmalloc(sizeof(struct lcs_card), GFP_KERNEL | GFP_DMA); + card = kzalloc(sizeof(struct lcs_card), GFP_KERNEL | GFP_DMA); if (card == NULL) return NULL; - memset(card, 0, sizeof(struct lcs_card)); card->lan_type = LCS_FRAME_TYPE_AUTO; + card->pkt_seq = 0; card->lancmd_timeout = LCS_LANCMD_TIMEOUT_DEFAULT; /* Allocate io buffers for the read channel. */ rc = lcs_alloc_channel(&card->read); @@ -239,7 +238,7 @@ lcs_setup_read_ccws(struct lcs_card *card) ((struct lcs_header *) card->read.iob[cnt].data)->offset = LCS_ILLEGAL_OFFSET; card->read.iob[cnt].callback = lcs_get_frames_cb; - card->read.iob[cnt].state = BUF_STATE_READY; + card->read.iob[cnt].state = LCS_BUF_STATE_READY; card->read.iob[cnt].count = LCS_IOBUFFERSIZE; } card->read.ccws[0].flags &= ~CCW_FLAG_PCI; @@ -250,7 +249,7 @@ lcs_setup_read_ccws(struct lcs_card *card) card->read.ccws[LCS_NUM_BUFFS].cda = (__u32) __pa(card->read.ccws); /* Setg initial state of the read channel. */ - card->read.state = CH_STATE_INIT; + card->read.state = LCS_CH_STATE_INIT; card->read.io_idx = 0; card->read.buf_idx = 0; @@ -297,7 +296,7 @@ lcs_setup_write_ccws(struct lcs_card *card) card->write.ccws[LCS_NUM_BUFFS].cda = (__u32) __pa(card->write.ccws); /* Set initial state of the write channel. */ - card->read.state = CH_STATE_INIT; + card->read.state = LCS_CH_STATE_INIT; card->write.io_idx = 0; card->write.buf_idx = 0; @@ -316,7 +315,106 @@ lcs_setup_write(struct lcs_card *card) init_waitqueue_head(&card->write.wait_q); } +static void +lcs_set_allowed_threads(struct lcs_card *card, unsigned long threads) +{ + unsigned long flags; + + spin_lock_irqsave(&card->mask_lock, flags); + card->thread_allowed_mask = threads; + spin_unlock_irqrestore(&card->mask_lock, flags); + wake_up(&card->wait_q); +} +static inline int +lcs_threads_running(struct lcs_card *card, unsigned long threads) +{ + unsigned long flags; + int rc = 0; + + spin_lock_irqsave(&card->mask_lock, flags); + rc = (card->thread_running_mask & threads); + spin_unlock_irqrestore(&card->mask_lock, flags); + return rc; +} + +static int +lcs_wait_for_threads(struct lcs_card *card, unsigned long threads) +{ + return wait_event_interruptible(card->wait_q, + lcs_threads_running(card, threads) == 0); +} + +static inline int +lcs_set_thread_start_bit(struct lcs_card *card, unsigned long thread) +{ + unsigned long flags; + + spin_lock_irqsave(&card->mask_lock, flags); + if ( !(card->thread_allowed_mask & thread) || + (card->thread_start_mask & thread) ) { + spin_unlock_irqrestore(&card->mask_lock, flags); + return -EPERM; + } + card->thread_start_mask |= thread; + spin_unlock_irqrestore(&card->mask_lock, flags); + return 0; +} + +static void +lcs_clear_thread_running_bit(struct lcs_card *card, unsigned long thread) +{ + unsigned long flags; + + spin_lock_irqsave(&card->mask_lock, flags); + card->thread_running_mask &= ~thread; + spin_unlock_irqrestore(&card->mask_lock, flags); + wake_up(&card->wait_q); +} +static inline int +__lcs_do_run_thread(struct lcs_card *card, unsigned long thread) +{ + unsigned long flags; + int rc = 0; + + spin_lock_irqsave(&card->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->mask_lock, flags); + return rc; +} + +static int +lcs_do_run_thread(struct lcs_card *card, unsigned long thread) +{ + int rc = 0; + wait_event(card->wait_q, + (rc = __lcs_do_run_thread(card, thread)) >= 0); + return rc; +} + +static int +lcs_do_start_thread(struct lcs_card *card, unsigned long thread) +{ + unsigned long flags; + int rc = 0; + + spin_lock_irqsave(&card->mask_lock, flags); + LCS_DBF_TEXT_(4, trace, " %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->mask_lock, flags); + return rc; +} /** * Initialize channels,card and state machines. @@ -334,37 +432,50 @@ lcs_setup_card(struct lcs_card *card) card->tx_buffer = NULL; card->tx_emitted = 0; - /* Initialize kernel thread task used for LGW commands. */ - INIT_WORK(&card->kernel_thread_starter, - (void *)lcs_start_kernel_thread,card); - card->thread_mask = 0; + init_waitqueue_head(&card->wait_q); spin_lock_init(&card->lock); spin_lock_init(&card->ipm_lock); + spin_lock_init(&card->mask_lock); #ifdef CONFIG_IP_MULTICAST INIT_LIST_HEAD(&card->ipm_list); #endif INIT_LIST_HEAD(&card->lancmd_waiters); } +static inline void +lcs_clear_multicast_list(struct lcs_card *card) +{ +#ifdef CONFIG_IP_MULTICAST + struct lcs_ipm_list *ipm; + unsigned long flags; + + /* Free multicast list. */ + LCS_DBF_TEXT(3, setup, "clmclist"); + spin_lock_irqsave(&card->ipm_lock, flags); + while (!list_empty(&card->ipm_list)){ + ipm = list_entry(card->ipm_list.next, + struct lcs_ipm_list, list); + list_del(&ipm->list); + if (ipm->ipm_state != LCS_IPM_STATE_SET_REQUIRED){ + spin_unlock_irqrestore(&card->ipm_lock, flags); + lcs_send_delipm(card, ipm); + spin_lock_irqsave(&card->ipm_lock, flags); + } + kfree(ipm); + } + spin_unlock_irqrestore(&card->ipm_lock, flags); +#endif +} /** * Cleanup channels,card and state machines. */ static void lcs_cleanup_card(struct lcs_card *card) { - struct list_head *l, *n; - struct lcs_ipm_list *ipm_list; LCS_DBF_TEXT(3, setup, "cleancrd"); LCS_DBF_HEX(2,setup,&card,sizeof(void*)); -#ifdef CONFIG_IP_MULTICAST - /* Free multicast list. */ - list_for_each_safe(l, n, &card->ipm_list) { - ipm_list = list_entry(l, struct lcs_ipm_list, list); - list_del(&ipm_list->list); - kfree(ipm_list); - } -#endif + if (card->dev != NULL) free_netdev(card->dev); /* Cleanup channels. */ @@ -387,7 +498,7 @@ lcs_start_channel(struct lcs_channel *channel) channel->ccws + channel->io_idx, 0, 0, DOIO_DENY_PREFETCH | DOIO_ALLOW_SUSPEND); if (rc == 0) - channel->state = CH_STATE_RUNNING; + channel->state = LCS_CH_STATE_RUNNING; spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags); if (rc) { LCS_DBF_TEXT_(4,trace,"essh%s", channel->ccwdev->dev.bus_id); @@ -411,8 +522,8 @@ lcs_clear_channel(struct lcs_channel *channel) LCS_DBF_TEXT_(4,trace,"ecsc%s", channel->ccwdev->dev.bus_id); return rc; } - wait_event(channel->wait_q, (channel->state == CH_STATE_CLEARED)); - channel->state = CH_STATE_STOPPED; + wait_event(channel->wait_q, (channel->state == LCS_CH_STATE_CLEARED)); + channel->state = LCS_CH_STATE_STOPPED; return rc; } @@ -426,10 +537,11 @@ lcs_stop_channel(struct lcs_channel *channel) unsigned long flags; int rc; - if (channel->state == CH_STATE_STOPPED) + if (channel->state == LCS_CH_STATE_STOPPED) return 0; LCS_DBF_TEXT(4,trace,"haltsch"); LCS_DBF_TEXT_(4,trace,"%s", channel->ccwdev->dev.bus_id); + channel->state = LCS_CH_STATE_INIT; spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags); rc = ccw_device_halt(channel->ccwdev, (addr_t) channel); spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags); @@ -438,7 +550,7 @@ lcs_stop_channel(struct lcs_channel *channel) return rc; } /* Asynchronous halt initialted. Wait for its completion. */ - wait_event(channel->wait_q, (channel->state == CH_STATE_HALTED)); + wait_event(channel->wait_q, (channel->state == LCS_CH_STATE_HALTED)); lcs_clear_channel(channel); return 0; } @@ -486,8 +598,8 @@ __lcs_get_buffer(struct lcs_channel *channel) LCS_DBF_TEXT(5, trace, "_getbuff"); index = channel->io_idx; do { - if (channel->iob[index].state == BUF_STATE_EMPTY) { - channel->iob[index].state = BUF_STATE_LOCKED; + if (channel->iob[index].state == LCS_BUF_STATE_EMPTY) { + channel->iob[index].state = LCS_BUF_STATE_LOCKED; return channel->iob + index; } index = (index + 1) & (LCS_NUM_BUFFS - 1); @@ -516,7 +628,7 @@ __lcs_resume_channel(struct lcs_channel *channel) { int rc; - if (channel->state != CH_STATE_SUSPENDED) + if (channel->state != LCS_CH_STATE_SUSPENDED) return 0; if (channel->ccws[channel->io_idx].flags & CCW_FLAG_SUSPEND) return 0; @@ -526,7 +638,7 @@ __lcs_resume_channel(struct lcs_channel *channel) LCS_DBF_TEXT_(4, trace, "ersc%s", channel->ccwdev->dev.bus_id); PRINT_ERR("Error in lcs_resume_channel: rc=%d\n",rc); } else - channel->state = CH_STATE_RUNNING; + channel->state = LCS_CH_STATE_RUNNING; return rc; } @@ -560,11 +672,10 @@ lcs_ready_buffer(struct lcs_channel *channel, struct lcs_buffer *buffer) int index, rc; LCS_DBF_TEXT(5, trace, "rdybuff"); - if (buffer->state != BUF_STATE_LOCKED && - buffer->state != BUF_STATE_PROCESSED) - BUG(); + BUG_ON(buffer->state != LCS_BUF_STATE_LOCKED && + buffer->state != LCS_BUF_STATE_PROCESSED); spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags); - buffer->state = BUF_STATE_READY; + buffer->state = LCS_BUF_STATE_READY; index = buffer - channel->iob; /* Set length. */ channel->ccws[index].count = buffer->count; @@ -586,9 +697,8 @@ __lcs_processed_buffer(struct lcs_channel *channel, struct lcs_buffer *buffer) int index, prev, next; LCS_DBF_TEXT(5, trace, "prcsbuff"); - if (buffer->state != BUF_STATE_READY) - BUG(); - buffer->state = BUF_STATE_PROCESSED; + BUG_ON(buffer->state != LCS_BUF_STATE_READY); + buffer->state = LCS_BUF_STATE_PROCESSED; index = buffer - channel->iob; prev = (index - 1) & (LCS_NUM_BUFFS - 1); next = (index + 1) & (LCS_NUM_BUFFS - 1); @@ -596,7 +706,7 @@ __lcs_processed_buffer(struct lcs_channel *channel, struct lcs_buffer *buffer) channel->ccws[index].flags |= CCW_FLAG_SUSPEND; channel->ccws[index].flags &= ~CCW_FLAG_PCI; /* Check the suspend bit of the previous buffer. */ - if (channel->iob[prev].state == BUF_STATE_READY) { + if (channel->iob[prev].state == LCS_BUF_STATE_READY) { /* * Previous buffer is in state ready. It might have * happened in lcs_ready_buffer that the suspend bit @@ -619,11 +729,10 @@ lcs_release_buffer(struct lcs_channel *channel, struct lcs_buffer *buffer) unsigned long flags; LCS_DBF_TEXT(5, trace, "relbuff"); - if (buffer->state != BUF_STATE_LOCKED && - buffer->state != BUF_STATE_PROCESSED) - BUG(); + BUG_ON(buffer->state != LCS_BUF_STATE_LOCKED && + buffer->state != LCS_BUF_STATE_PROCESSED); spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags); - buffer->state = BUF_STATE_EMPTY; + buffer->state = LCS_BUF_STATE_EMPTY; spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags); } @@ -651,6 +760,43 @@ lcs_get_lancmd(struct lcs_card *card, int count) return buffer; } + +static void +lcs_get_reply(struct lcs_reply *reply) +{ + WARN_ON(atomic_read(&reply->refcnt) <= 0); + atomic_inc(&reply->refcnt); +} + +static void +lcs_put_reply(struct lcs_reply *reply) +{ + WARN_ON(atomic_read(&reply->refcnt) <= 0); + if (atomic_dec_and_test(&reply->refcnt)) { + kfree(reply); + } + +} + +static struct lcs_reply * +lcs_alloc_reply(struct lcs_cmd *cmd) +{ + struct lcs_reply *reply; + + LCS_DBF_TEXT(4, trace, "getreply"); + + reply = kzalloc(sizeof(struct lcs_reply), GFP_ATOMIC); + if (!reply) + return NULL; + atomic_set(&reply->refcnt,1); + reply->sequence_no = cmd->sequence_no; + reply->received = 0; + reply->rc = 0; + init_waitqueue_head(&reply->wait_q); + + return reply; +} + /** * Notifier function for lancmd replies. Called from read irq. */ @@ -665,12 +811,14 @@ lcs_notify_lancmd_waiters(struct lcs_card *card, struct lcs_cmd *cmd) list_for_each_safe(l, n, &card->lancmd_waiters) { reply = list_entry(l, struct lcs_reply, list); if (reply->sequence_no == cmd->sequence_no) { - list_del(&reply->list); + lcs_get_reply(reply); + list_del_init(&reply->list); if (reply->callback != NULL) reply->callback(card, cmd); reply->received = 1; reply->rc = cmd->return_code; wake_up(&reply->wait_q); + lcs_put_reply(reply); break; } } @@ -683,50 +831,66 @@ lcs_notify_lancmd_waiters(struct lcs_card *card, struct lcs_cmd *cmd) void lcs_lancmd_timeout(unsigned long data) { - struct lcs_reply *reply; + struct lcs_reply *reply, *list_reply, *r; + unsigned long flags; LCS_DBF_TEXT(4, trace, "timeout"); reply = (struct lcs_reply *) data; - list_del(&reply->list); - reply->received = 1; - reply->rc = -ETIME; - wake_up(&reply->wait_q); + spin_lock_irqsave(&reply->card->lock, flags); + list_for_each_entry_safe(list_reply, r, + &reply->card->lancmd_waiters,list) { + if (reply == list_reply) { + lcs_get_reply(reply); + list_del_init(&reply->list); + spin_unlock_irqrestore(&reply->card->lock, flags); + reply->received = 1; + reply->rc = -ETIME; + wake_up(&reply->wait_q); + lcs_put_reply(reply); + return; + } + } + spin_unlock_irqrestore(&reply->card->lock, flags); } static int lcs_send_lancmd(struct lcs_card *card, struct lcs_buffer *buffer, void (*reply_callback)(struct lcs_card *, struct lcs_cmd *)) { - struct lcs_reply reply; + struct lcs_reply *reply; struct lcs_cmd *cmd; struct timer_list timer; + unsigned long flags; int rc; LCS_DBF_TEXT(4, trace, "sendcmd"); cmd = (struct lcs_cmd *) buffer->data; - cmd->sequence_no = ++card->sequence_no; cmd->return_code = 0; - reply.sequence_no = cmd->sequence_no; - reply.callback = reply_callback; - reply.received = 0; - reply.rc = 0; - init_waitqueue_head(&reply.wait_q); - spin_lock(&card->lock); - list_add_tail(&reply.list, &card->lancmd_waiters); - spin_unlock(&card->lock); + cmd->sequence_no = card->sequence_no++; + reply = lcs_alloc_reply(cmd); + if (!reply) + return -ENOMEM; + reply->callback = reply_callback; + reply->card = card; + spin_lock_irqsave(&card->lock, flags); + list_add_tail(&reply->list, &card->lancmd_waiters); + spin_unlock_irqrestore(&card->lock, flags); + buffer->callback = lcs_release_buffer; rc = lcs_ready_buffer(&card->write, buffer); if (rc) return rc; init_timer(&timer); timer.function = lcs_lancmd_timeout; - timer.data = (unsigned long) &reply; + timer.data = (unsigned long) reply; timer.expires = jiffies + HZ*card->lancmd_timeout; add_timer(&timer); - wait_event(reply.wait_q, reply.received); - del_timer(&timer); - LCS_DBF_TEXT_(4, trace, "rc:%d",reply.rc); - return reply.rc ? -EIO : 0; + wait_event(reply->wait_q, reply->received); + del_timer_sync(&timer); + LCS_DBF_TEXT_(4, trace, "rc:%d",reply->rc); + rc = reply->rc; + lcs_put_reply(reply); + return rc ? -EIO : 0; } /** @@ -921,14 +1085,6 @@ lcs_check_multicast_support(struct lcs_card *card) PRINT_ERR("Query IPAssist failed. Assuming unsupported!\n"); return -EOPNOTSUPP; } - /* Print out supported assists: IPv6 */ - PRINT_INFO("LCS device %s %s IPv6 support\n", card->dev->name, - (card->ip_assists_supported & LCS_IPASS_IPV6_SUPPORT) ? - "with" : "without"); - /* Print out supported assist: Multicast */ - PRINT_INFO("LCS device %s %s Multicast support\n", card->dev->name, - (card->ip_assists_supported & LCS_IPASS_MULTICAST_SUPPORT) ? - "with" : "without"); if (card->ip_assists_supported & LCS_IPASS_MULTICAST_SUPPORT) return 0; return -EOPNOTSUPP; @@ -940,40 +1096,60 @@ lcs_check_multicast_support(struct lcs_card *card) static void lcs_fix_multicast_list(struct lcs_card *card) { - struct list_head *l, *n; - struct lcs_ipm_list *ipm; + struct list_head failed_list; + struct lcs_ipm_list *ipm, *tmp; + unsigned long flags; + int rc; LCS_DBF_TEXT(4,trace, "fixipm"); - spin_lock(&card->ipm_lock); - list_for_each_safe(l, n, &card->ipm_list) { - ipm = list_entry(l, struct lcs_ipm_list, list); + INIT_LIST_HEAD(&failed_list); + spin_lock_irqsave(&card->ipm_lock, flags); +list_modified: + list_for_each_entry_safe(ipm, tmp, &card->ipm_list, list){ switch (ipm->ipm_state) { case LCS_IPM_STATE_SET_REQUIRED: - if (lcs_send_setipm(card, ipm)) + /* del from ipm_list so noone else can tamper with + * this entry */ + list_del_init(&ipm->list); + spin_unlock_irqrestore(&card->ipm_lock, flags); + rc = lcs_send_setipm(card, ipm); + spin_lock_irqsave(&card->ipm_lock, flags); + if (rc) { PRINT_INFO("Adding multicast address failed." "Table possibly full!\n"); - else + /* store ipm in failed list -> will be added + * to ipm_list again, so a retry will be done + * during the next call of this function */ + list_add_tail(&ipm->list, &failed_list); + } else { ipm->ipm_state = LCS_IPM_STATE_ON_CARD; - break; + /* re-insert into ipm_list */ + list_add_tail(&ipm->list, &card->ipm_list); + } + goto list_modified; case LCS_IPM_STATE_DEL_REQUIRED: - lcs_send_delipm(card, ipm); list_del(&ipm->list); + spin_unlock_irqrestore(&card->ipm_lock, flags); + lcs_send_delipm(card, ipm); + spin_lock_irqsave(&card->ipm_lock, flags); kfree(ipm); - break; + goto list_modified; case LCS_IPM_STATE_ON_CARD: break; } } - if (card->state == DEV_STATE_UP) - netif_wake_queue(card->dev); - spin_unlock(&card->ipm_lock); + /* re-insert all entries from the failed_list into ipm_list */ + list_for_each_entry_safe(ipm, tmp, &failed_list, list) + list_move_tail(&ipm->list, &card->ipm_list); + + spin_unlock_irqrestore(&card->ipm_lock, flags); } /** * get mac address for the relevant Multicast address */ static void -lcs_get_mac_for_ipm(__u32 ipm, char *mac, struct net_device *dev) +lcs_get_mac_for_ipm(__be32 ipm, char *mac, struct net_device *dev) { LCS_DBF_TEXT(4,trace, "getmac"); if (dev->type == ARPHRD_IEEE802_TR) @@ -985,70 +1161,119 @@ lcs_get_mac_for_ipm(__u32 ipm, char *mac, struct net_device *dev) /** * function called by net device to handle multicast address relevant things */ -static int -lcs_register_mc_addresses(void *data) +static inline void +lcs_remove_mc_addresses(struct lcs_card *card, struct in_device *in4_dev) { - struct lcs_card *card; - char buf[MAX_ADDR_LEN]; - struct list_head *l; struct ip_mc_list *im4; - struct in_device *in4_dev; - struct lcs_ipm_list *ipm, *tmp; - - daemonize("regipm"); - LCS_DBF_TEXT(4, trace, "regmulti"); + struct list_head *l; + struct lcs_ipm_list *ipm; + unsigned long flags; + char buf[MAX_ADDR_LEN]; - card = (struct lcs_card *) data; - in4_dev = in_dev_get(card->dev); - if (in4_dev == NULL) - return 0; - read_lock(&in4_dev->lock); - spin_lock(&card->ipm_lock); - /* Check for multicast addresses to be removed. */ + LCS_DBF_TEXT(4, trace, "remmclst"); + spin_lock_irqsave(&card->ipm_lock, flags); list_for_each(l, &card->ipm_list) { ipm = list_entry(l, struct lcs_ipm_list, list); for (im4 = in4_dev->mc_list; im4 != NULL; im4 = im4->next) { lcs_get_mac_for_ipm(im4->multiaddr, buf, card->dev); - if (memcmp(buf, &ipm->ipm.mac_addr, - LCS_MAC_LENGTH) == 0 && - ipm->ipm.ip_addr == im4->multiaddr) + if ( (ipm->ipm.ip_addr == im4->multiaddr) && + (memcmp(buf, &ipm->ipm.mac_addr, + LCS_MAC_LENGTH) == 0) ) break; } if (im4 == NULL) ipm->ipm_state = LCS_IPM_STATE_DEL_REQUIRED; } - /* Check for multicast addresses to be added. */ + spin_unlock_irqrestore(&card->ipm_lock, flags); +} + +static inline struct lcs_ipm_list * +lcs_check_addr_entry(struct lcs_card *card, struct ip_mc_list *im4, char *buf) +{ + struct lcs_ipm_list *tmp, *ipm = NULL; + struct list_head *l; + unsigned long flags; + + LCS_DBF_TEXT(4, trace, "chkmcent"); + spin_lock_irqsave(&card->ipm_lock, flags); + list_for_each(l, &card->ipm_list) { + tmp = list_entry(l, struct lcs_ipm_list, list); + if ( (tmp->ipm.ip_addr == im4->multiaddr) && + (memcmp(buf, &tmp->ipm.mac_addr, + LCS_MAC_LENGTH) == 0) ) { + ipm = tmp; + break; + } + } + spin_unlock_irqrestore(&card->ipm_lock, flags); + return ipm; +} + +static inline void +lcs_set_mc_addresses(struct lcs_card *card, struct in_device *in4_dev) +{ + + struct ip_mc_list *im4; + struct lcs_ipm_list *ipm; + char buf[MAX_ADDR_LEN]; + unsigned long flags; + + LCS_DBF_TEXT(4, trace, "setmclst"); for (im4 = in4_dev->mc_list; im4; im4 = im4->next) { lcs_get_mac_for_ipm(im4->multiaddr, buf, card->dev); - ipm = NULL; - list_for_each(l, &card->ipm_list) { - tmp = list_entry(l, struct lcs_ipm_list, list); - if (memcmp(buf, &tmp->ipm.mac_addr, - LCS_MAC_LENGTH) == 0 && - tmp->ipm.ip_addr == im4->multiaddr) { - ipm = tmp; - break; - } - } + ipm = lcs_check_addr_entry(card, im4, buf); if (ipm != NULL) continue; /* Address already in list. */ ipm = (struct lcs_ipm_list *) - kmalloc(sizeof(struct lcs_ipm_list), GFP_ATOMIC); + kzalloc(sizeof(struct lcs_ipm_list), GFP_ATOMIC); if (ipm == NULL) { PRINT_INFO("Not enough memory to add " "new multicast entry!\n"); break; } - memset(ipm, 0, sizeof(struct lcs_ipm_list)); memcpy(&ipm->ipm.mac_addr, buf, LCS_MAC_LENGTH); ipm->ipm.ip_addr = im4->multiaddr; ipm->ipm_state = LCS_IPM_STATE_SET_REQUIRED; + spin_lock_irqsave(&card->ipm_lock, flags); + LCS_DBF_HEX(2,trace,&ipm->ipm.ip_addr,4); list_add(&ipm->list, &card->ipm_list); + spin_unlock_irqrestore(&card->ipm_lock, flags); } - spin_unlock(&card->ipm_lock); - read_unlock(&in4_dev->lock); +} + +static int +lcs_register_mc_addresses(void *data) +{ + struct lcs_card *card; + struct in_device *in4_dev; + + card = (struct lcs_card *) data; + daemonize("regipm"); + + if (!lcs_do_run_thread(card, LCS_SET_MC_THREAD)) + return 0; + LCS_DBF_TEXT(4, trace, "regmulti"); + + in4_dev = in_dev_get(card->dev); + if (in4_dev == NULL) + goto out; + read_lock(&in4_dev->mc_list_lock); + lcs_remove_mc_addresses(card,in4_dev); + lcs_set_mc_addresses(card, in4_dev); + read_unlock(&in4_dev->mc_list_lock); in_dev_put(in4_dev); + + netif_carrier_off(card->dev); + netif_tx_disable(card->dev); + wait_event(card->write.wait_q, + (card->write.state != LCS_CH_STATE_RUNNING)); lcs_fix_multicast_list(card); + if (card->state == DEV_STATE_UP) { + netif_carrier_on(card->dev); + netif_wake_queue(card->dev); + } +out: + lcs_clear_thread_running_bit(card, LCS_SET_MC_THREAD); return 0; } /** @@ -1062,8 +1287,9 @@ lcs_set_multicast_list(struct net_device *dev) LCS_DBF_TEXT(4, trace, "setmulti"); card = (struct lcs_card *) dev->priv; - set_bit(3, &card->thread_mask); - schedule_work(&card->kernel_thread_starter); + + if (!lcs_set_thread_start_bit(card, LCS_SET_MC_THREAD)) + schedule_work(&card->kernel_thread_starter); } #endif /* CONFIG_IP_MULTICAST */ @@ -1094,6 +1320,53 @@ lcs_check_irb_error(struct ccw_device *cdev, struct irb *irb) return PTR_ERR(irb); } +static int +lcs_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)) { + LCS_DBF_TEXT(2, trace, "CGENCHK"); + return 1; + } + if (dstat & DEV_STAT_UNIT_CHECK) { + if (sense[LCS_SENSE_BYTE_1] & + LCS_SENSE_RESETTING_EVENT) { + LCS_DBF_TEXT(2, trace, "REVIND"); + return 1; + } + if (sense[LCS_SENSE_BYTE_0] & + LCS_SENSE_CMD_REJECT) { + LCS_DBF_TEXT(2, trace, "CMDREJ"); + return 0; + } + if ((!sense[LCS_SENSE_BYTE_0]) && + (!sense[LCS_SENSE_BYTE_1]) && + (!sense[LCS_SENSE_BYTE_2]) && + (!sense[LCS_SENSE_BYTE_3])) { + LCS_DBF_TEXT(2, trace, "ZEROSEN"); + return 0; + } + LCS_DBF_TEXT(2, trace, "DGENCHK"); + return 1; + } + return 0; +} + +void +lcs_schedule_recovery(struct lcs_card *card) +{ + LCS_DBF_TEXT(2, trace, "startrec"); + if (!lcs_set_thread_start_bit(card, LCS_RECOVERY_THREAD)) + schedule_work(&card->kernel_thread_starter); +} /** * IRQ Handler for LCS channels @@ -1103,7 +1376,8 @@ lcs_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb) { struct lcs_card *card; struct lcs_channel *channel; - int index; + int rc, index; + int cstat, dstat; if (lcs_check_irb_error(cdev, irb)) return; @@ -1114,16 +1388,30 @@ lcs_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb) else channel = &card->write; + cstat = irb->scsw.cstat; + dstat = irb->scsw.dstat; LCS_DBF_TEXT_(5, trace, "Rint%s",cdev->dev.bus_id); LCS_DBF_TEXT_(5, trace, "%4x%4x",irb->scsw.cstat, irb->scsw.dstat); - + LCS_DBF_TEXT_(5, trace, "%4x%4x",irb->scsw.fctl, irb->scsw.actl); + + /* Check for channel and device errors presented */ + rc = lcs_get_problem(cdev, irb); + if (rc || (dstat & DEV_STAT_UNIT_EXCEP)) { + PRINT_WARN("check on device %s, dstat=0x%X, cstat=0x%X \n", + cdev->dev.bus_id, dstat, cstat); + if (rc) { + lcs_schedule_recovery(card); + wake_up(&card->wait_q); + return; + } + } /* How far in the ccw chain have we processed? */ - if ((channel->state != CH_STATE_INIT) && + if ((channel->state != LCS_CH_STATE_INIT) && (irb->scsw.fctl & SCSW_FCTL_START_FUNC)) { - index = (struct ccw1 *) __va((addr_t) irb->scsw.cpa) + index = (struct ccw1 *) __va((addr_t) irb->scsw.cpa) - channel->ccws; if ((irb->scsw.actl & SCSW_ACTL_SUSPENDED) || - (irb->scsw.cstat | SCHN_STAT_PCI)) + (irb->scsw.cstat & SCHN_STAT_PCI)) /* Bloody io subsystem tells us lies about cpa... */ index = (index - 1) & (LCS_NUM_BUFFS - 1); while (channel->io_idx != index) { @@ -1138,22 +1426,20 @@ lcs_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb) (irb->scsw.dstat & DEV_STAT_CHN_END) || (irb->scsw.dstat & DEV_STAT_UNIT_CHECK)) /* Mark channel as stopped. */ - channel->state = CH_STATE_STOPPED; + channel->state = LCS_CH_STATE_STOPPED; else if (irb->scsw.actl & SCSW_ACTL_SUSPENDED) /* CCW execution stopped on a suspend bit. */ - channel->state = CH_STATE_SUSPENDED; - + channel->state = LCS_CH_STATE_SUSPENDED; if (irb->scsw.fctl & SCSW_FCTL_HALT_FUNC) { if (irb->scsw.cc != 0) { ccw_device_halt(channel->ccwdev, (addr_t) channel); return; } /* The channel has been stopped by halt_IO. */ - channel->state = CH_STATE_HALTED; + channel->state = LCS_CH_STATE_HALTED; } - if (irb->scsw.fctl & SCSW_FCTL_CLEAR_FUNC) { - channel->state = CH_STATE_CLEARED; + channel->state = LCS_CH_STATE_CLEARED; } /* Do the rest in the tasklet. */ tasklet_schedule(&channel->irq_tasklet); @@ -1177,7 +1463,7 @@ lcs_tasklet(unsigned long data) /* Check for processed buffers. */ iob = channel->iob; buf_idx = channel->buf_idx; - while (iob[buf_idx].state == BUF_STATE_PROCESSED) { + while (iob[buf_idx].state == LCS_BUF_STATE_PROCESSED) { /* Do the callback thing. */ if (iob[buf_idx].callback != NULL) iob[buf_idx].callback(channel, iob + buf_idx); @@ -1185,12 +1471,12 @@ lcs_tasklet(unsigned long data) } channel->buf_idx = buf_idx; - if (channel->state == CH_STATE_STOPPED) + if (channel->state == LCS_CH_STATE_STOPPED) // FIXME: what if rc != 0 ?? rc = lcs_start_channel(channel); spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags); - if (channel->state == CH_STATE_SUSPENDED && - channel->iob[channel->io_idx].state == BUF_STATE_READY) { + if (channel->state == LCS_CH_STATE_SUSPENDED && + channel->iob[channel->io_idx].state == LCS_BUF_STATE_READY) { // FIXME: what if rc != 0 ?? rc = __lcs_resume_channel(channel); } @@ -1227,6 +1513,8 @@ lcs_txbuffer_cb(struct lcs_channel *channel, struct lcs_buffer *buffer) lcs_release_buffer(channel, buffer); card = (struct lcs_card *) ((char *) channel - offsetof(struct lcs_card, write)); + if (netif_queue_stopped(card->dev) && netif_carrier_ok(card->dev)) + netif_wake_queue(card->dev); spin_lock(&card->lock); card->tx_emitted--; if (card->tx_emitted <= 0 && card->tx_buffer != NULL) @@ -1246,6 +1534,7 @@ __lcs_start_xmit(struct lcs_card *card, struct sk_buff *skb, struct net_device *dev) { struct lcs_header *header; + int rc = 0; LCS_DBF_TEXT(5, trace, "hardxmit"); if (skb == NULL) { @@ -1260,10 +1549,12 @@ __lcs_start_xmit(struct lcs_card *card, struct sk_buff *skb, card->stats.tx_carrier_errors++; return 0; } - if (netif_queue_stopped(dev) ) { - card->stats.tx_dropped++; - return -EBUSY; + if (skb->protocol == htons(ETH_P_IPV6)) { + dev_kfree_skb(skb); + return 0; } + netif_stop_queue(card->dev); + spin_lock(&card->lock); if (card->tx_buffer != NULL && card->tx_buffer->count + sizeof(struct lcs_header) + skb->len + sizeof(u16) > LCS_IOBUFFERSIZE) @@ -1274,7 +1565,8 @@ __lcs_start_xmit(struct lcs_card *card, struct sk_buff *skb, card->tx_buffer = lcs_get_buffer(&card->write); if (card->tx_buffer == NULL) { card->stats.tx_dropped++; - return -EBUSY; + rc = -EBUSY; + goto out; } card->tx_buffer->callback = lcs_txbuffer_cb; card->tx_buffer->count = 0; @@ -1286,13 +1578,18 @@ __lcs_start_xmit(struct lcs_card *card, struct sk_buff *skb, header->type = card->lan_type; header->slot = card->portno; memcpy(header + 1, skb->data, skb->len); + spin_unlock(&card->lock); card->stats.tx_bytes += skb->len; card->stats.tx_packets++; dev_kfree_skb(skb); - if (card->tx_emitted <= 0) + netif_wake_queue(card->dev); + spin_lock(&card->lock); + if (card->tx_emitted <= 0 && card->tx_buffer != NULL) /* If this is the first tx buffer emit it immediately. */ __lcs_emit_txbuffer(card); - return 0; +out: + spin_unlock(&card->lock); + return rc; } static int @@ -1303,9 +1600,7 @@ lcs_start_xmit(struct sk_buff *skb, struct net_device *dev) LCS_DBF_TEXT(5, trace, "pktxmit"); card = (struct lcs_card *) dev->priv; - spin_lock(&card->lock); rc = __lcs_start_xmit(card, skb, dev); - spin_unlock(&card->lock); return rc; } @@ -1383,8 +1678,6 @@ lcs_detect(struct lcs_card *card) /* start/reset card */ if (card->dev) netif_stop_queue(card->dev); - card->write.state = CH_STATE_INIT; - card->read.state = CH_STATE_INIT; rc = lcs_stop_channels(card); if (rc == 0) { rc = lcs_start_channels(card); @@ -1398,35 +1691,12 @@ lcs_detect(struct lcs_card *card) card->state = DEV_STATE_UP; } else { card->state = DEV_STATE_DOWN; - card->write.state = CH_STATE_INIT; - card->read.state = CH_STATE_INIT; + card->write.state = LCS_CH_STATE_INIT; + card->read.state = LCS_CH_STATE_INIT; } return rc; } -/** - * reset card - */ -static int -lcs_resetcard(struct lcs_card *card) -{ - int retries; - - LCS_DBF_TEXT(2, trace, "rescard"); - for (retries = 0; retries < 10; retries++) { - if (lcs_detect(card) == 0) { - netif_wake_queue(card->dev); - card->state = DEV_STATE_UP; - PRINT_INFO("LCS device %s successfully restarted!\n", - card->dev->name); - return 0; - } - msleep(3000); - } - PRINT_ERR("Error in Reseting LCS card!\n"); - return -EIO; -} - /** * LCS Stop card */ @@ -1437,9 +1707,10 @@ lcs_stopcard(struct lcs_card *card) LCS_DBF_TEXT(3, setup, "stopcard"); - if (card->read.state != CH_STATE_STOPPED && - card->write.state != CH_STATE_STOPPED && + if (card->read.state != LCS_CH_STATE_STOPPED && + card->write.state != LCS_CH_STATE_STOPPED && card->state == DEV_STATE_UP) { + lcs_clear_multicast_list(card); rc = lcs_send_stoplan(card,LCS_INITIATOR_TCPIP); rc = lcs_send_shutdown(card); } @@ -1449,115 +1720,20 @@ lcs_stopcard(struct lcs_card *card) return rc; } -/** - * LGW initiated commands - */ -static int -lcs_lgw_startlan_thread(void *data) -{ - struct lcs_card *card; - - card = (struct lcs_card *) data; - daemonize("lgwstpln"); - LCS_DBF_TEXT(4, trace, "lgwstpln"); - if (card->dev) - netif_stop_queue(card->dev); - if (lcs_startlan(card) == 0) { - netif_wake_queue(card->dev); - card->state = DEV_STATE_UP; - PRINT_INFO("LCS Startlan for device %s succeeded!\n", - card->dev->name); - - } else - PRINT_ERR("LCS Startlan for device %s failed!\n", - card->dev->name); - return 0; -} - -/** - * Send startup command initiated by Lan Gateway - */ -static int -lcs_lgw_startup_thread(void *data) -{ - int rc; - - struct lcs_card *card; - - card = (struct lcs_card *) data; - daemonize("lgwstpln"); - LCS_DBF_TEXT(4, trace, "lgwstpln"); - if (card->dev) - netif_stop_queue(card->dev); - rc = lcs_send_startup(card, LCS_INITIATOR_LGW); - if (rc != 0) { - PRINT_ERR("Startup for LCS device %s initiated " \ - "by LGW failed!\nReseting card ...\n", - card->dev->name); - /* do a card reset */ - rc = lcs_resetcard(card); - if (rc == 0) - goto Done; - } - rc = lcs_startlan(card); - if (rc == 0) { - netif_wake_queue(card->dev); - card->state = DEV_STATE_UP; - } -Done: - if (rc == 0) - PRINT_INFO("LCS Startup for device %s succeeded!\n", - card->dev->name); - else - PRINT_ERR("LCS Startup for device %s failed!\n", - card->dev->name); - return 0; -} - - -/** - * send stoplan command initiated by Lan Gateway - */ -static int -lcs_lgw_stoplan_thread(void *data) -{ - struct lcs_card *card; - int rc; - - card = (struct lcs_card *) data; - daemonize("lgwstop"); - LCS_DBF_TEXT(4, trace, "lgwstop"); - if (card->dev) - netif_stop_queue(card->dev); - if (lcs_send_stoplan(card, LCS_INITIATOR_LGW) == 0) - PRINT_INFO("Stoplan for %s initiated by LGW succeeded!\n", - card->dev->name); - else - PRINT_ERR("Stoplan %s initiated by LGW failed!\n", - card->dev->name); - /*Try to reset the card, stop it on failure */ - rc = lcs_resetcard(card); - if (rc != 0) - rc = lcs_stopcard(card); - return rc; -} - /** * Kernel Thread helper functions for LGW initiated commands */ static void -lcs_start_kernel_thread(struct lcs_card *card) +lcs_start_kernel_thread(struct work_struct *work) { + struct lcs_card *card = container_of(work, struct lcs_card, kernel_thread_starter); LCS_DBF_TEXT(5, trace, "krnthrd"); - if (test_and_clear_bit(0, &card->thread_mask)) - kernel_thread(lcs_lgw_startup_thread, (void *) card, SIGCHLD); - if (test_and_clear_bit(1, &card->thread_mask)) - kernel_thread(lcs_lgw_startlan_thread, (void *) card, SIGCHLD); - if (test_and_clear_bit(2, &card->thread_mask)) - kernel_thread(lcs_lgw_stoplan_thread, (void *) card, SIGCHLD); + if (lcs_do_start_thread(card, LCS_RECOVERY_THREAD)) + kernel_thread(lcs_recovery, (void *) card, SIGCHLD); #ifdef CONFIG_IP_MULTICAST - if (test_and_clear_bit(3, &card->thread_mask)) - kernel_thread(lcs_register_mc_addresses, (void *) card, SIGCHLD); + if (lcs_do_start_thread(card, LCS_SET_MC_THREAD)) + kernel_thread(lcs_register_mc_addresses, + (void *) card, SIGCHLD); #endif } @@ -1571,16 +1747,14 @@ lcs_get_control(struct lcs_card *card, struct lcs_cmd *cmd) if (cmd->initiator == LCS_INITIATOR_LGW) { switch(cmd->cmd_code) { case LCS_CMD_STARTUP: - set_bit(0, &card->thread_mask); - schedule_work(&card->kernel_thread_starter); - break; case LCS_CMD_STARTLAN: - set_bit(1, &card->thread_mask); - schedule_work(&card->kernel_thread_starter); + lcs_schedule_recovery(card); break; case LCS_CMD_STOPLAN: - set_bit(2, &card->thread_mask); - schedule_work(&card->kernel_thread_starter); + PRINT_WARN("Stoplan for %s initiated by LGW.\n", + card->dev->name); + if (card->dev) + netif_carrier_off(card->dev); break; default: PRINT_INFO("UNRECOGNIZED LGW COMMAND\n"); @@ -1616,6 +1790,7 @@ lcs_get_skb(struct lcs_card *card, char *skb_data, unsigned int skb_len) skb->protocol = card->lan_type_trans(skb, card->dev); card->stats.rx_bytes += skb_len; card->stats.rx_packets++; + *((__u32 *)skb->cb) = ++card->pkt_seq; netif_rx(skb); } @@ -1695,8 +1870,11 @@ lcs_stop_device(struct net_device *dev) LCS_DBF_TEXT(2, trace, "stopdev"); card = (struct lcs_card *) dev->priv; - netif_stop_queue(dev); + netif_carrier_off(dev); + netif_tx_disable(dev); dev->flags &= ~IFF_UP; + wait_event(card->write.wait_q, + (card->write.state != LCS_CH_STATE_RUNNING)); rc = lcs_stopcard(card); if (rc) PRINT_ERR("Try it again!\n "); @@ -1722,6 +1900,7 @@ lcs_open_device(struct net_device *dev) } else { dev->flags |= IFF_UP; + netif_carrier_on(dev); netif_wake_queue(dev); card->state = DEV_STATE_UP; } @@ -1732,7 +1911,7 @@ lcs_open_device(struct net_device *dev) * show function for portno called by cat or similar things */ static ssize_t -lcs_portno_show (struct device *dev, char *buf) +lcs_portno_show (struct device *dev, struct device_attribute *attr, char *buf) { struct lcs_card *card; @@ -1748,7 +1927,7 @@ lcs_portno_show (struct device *dev, char *buf) * store the value which is piped to file portno */ static ssize_t -lcs_portno_store (struct device *dev, const char *buf, size_t count) +lcs_portno_store (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct lcs_card *card; int value; @@ -1769,7 +1948,7 @@ lcs_portno_store (struct device *dev, const char *buf, size_t count) static DEVICE_ATTR(portno, 0644, lcs_portno_show, lcs_portno_store); static ssize_t -lcs_type_show(struct device *dev, char *buf) +lcs_type_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ccwgroup_device *cgdev; @@ -1783,7 +1962,7 @@ lcs_type_show(struct device *dev, char *buf) static DEVICE_ATTR(type, 0444, lcs_type_show, NULL); static ssize_t -lcs_timeout_show(struct device *dev, char *buf) +lcs_timeout_show(struct device *dev, struct device_attribute *attr, char *buf) { struct lcs_card *card; @@ -1793,7 +1972,7 @@ lcs_timeout_show(struct device *dev, char *buf) } static ssize_t -lcs_timeout_store (struct device *dev, const char *buf, size_t count) +lcs_timeout_store (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct lcs_card *card; int value; @@ -1813,10 +1992,31 @@ lcs_timeout_store (struct device *dev, const char *buf, size_t count) DEVICE_ATTR(lancmd_timeout, 0644, lcs_timeout_show, lcs_timeout_store); +static ssize_t +lcs_dev_recover_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct lcs_card *card = dev->driver_data; + char *tmp; + int i; + + if (!card) + return -EINVAL; + if (card->state != DEV_STATE_UP) + return -EPERM; + i = simple_strtoul(buf, &tmp, 16); + if (i == 1) + lcs_schedule_recovery(card); + return count; +} + +static DEVICE_ATTR(recover, 0200, NULL, lcs_dev_recover_store); + static struct attribute * lcs_attrs[] = { &dev_attr_portno.attr, &dev_attr_type.attr, &dev_attr_lancmd_timeout.attr, + &dev_attr_recover.attr, NULL, }; @@ -1853,6 +2053,11 @@ lcs_probe_device(struct ccwgroup_device *ccwgdev) ccwgdev->dev.driver_data = card; ccwgdev->cdev[0]->handler = lcs_irq; ccwgdev->cdev[1]->handler = lcs_irq; + card->gdev = ccwgdev; + INIT_WORK(&card->kernel_thread_starter, lcs_start_kernel_thread); + card->thread_start_mask = 0; + card->thread_allowed_mask = 0; + card->thread_running_mask = 0; return 0; } @@ -1938,28 +2143,40 @@ lcs_new_device(struct ccwgroup_device *ccwgdev) if (!dev) goto out; card->dev = dev; -netdev_out: card->dev->priv = card; card->dev->open = lcs_open_device; card->dev->stop = lcs_stop_device; card->dev->hard_start_xmit = lcs_start_xmit; card->dev->get_stats = lcs_getstats; SET_MODULE_OWNER(dev); - if (lcs_register_netdev(ccwgdev) != 0) - goto out; memcpy(card->dev->dev_addr, card->mac, LCS_MAC_LENGTH); #ifdef CONFIG_IP_MULTICAST if (!lcs_check_multicast_support(card)) card->dev->set_multicast_list = lcs_set_multicast_list; #endif - netif_stop_queue(card->dev); +netdev_out: + lcs_set_allowed_threads(card,0xffffffff); if (recover_state == DEV_STATE_RECOVER) { + lcs_set_multicast_list(card->dev); card->dev->flags |= IFF_UP; + netif_carrier_on(card->dev); netif_wake_queue(card->dev); card->state = DEV_STATE_UP; - } else + } else { lcs_stopcard(card); + } + if (lcs_register_netdev(ccwgdev) != 0) + goto out; + + /* Print out supported assists: IPv6 */ + PRINT_INFO("LCS device %s %s IPv6 support\n", card->dev->name, + (card->ip_assists_supported & LCS_IPASS_IPV6_SUPPORT) ? + "with" : "without"); + /* Print out supported assist: Multicast */ + PRINT_INFO("LCS device %s %s Multicast support\n", card->dev->name, + (card->ip_assists_supported & LCS_IPASS_MULTICAST_SUPPORT) ? + "with" : "without"); return 0; out: @@ -1972,7 +2189,7 @@ out: * lcs_shutdown_device, called when setting the group device offline. */ static int -lcs_shutdown_device(struct ccwgroup_device *ccwgdev) +__lcs_shutdown_device(struct ccwgroup_device *ccwgdev, int recovery_mode) { struct lcs_card *card; enum lcs_dev_states recover_state; @@ -1982,7 +2199,11 @@ lcs_shutdown_device(struct ccwgroup_device *ccwgdev) card = (struct lcs_card *)ccwgdev->dev.driver_data; if (!card) return -ENODEV; - + if (recovery_mode == 0) { + lcs_set_allowed_threads(card, 0); + if (lcs_wait_for_threads(card, LCS_SET_MC_THREAD)) + return -ERESTARTSYS; + } LCS_DBF_HEX(3, setup, &card, sizeof(void*)); recover_state = card->state; @@ -1997,6 +2218,43 @@ lcs_shutdown_device(struct ccwgroup_device *ccwgdev) return 0; } +static int +lcs_shutdown_device(struct ccwgroup_device *ccwgdev) +{ + return __lcs_shutdown_device(ccwgdev, 0); +} + +/** + * drive lcs recovery after startup and startlan initiated by Lan Gateway + */ +static int +lcs_recovery(void *ptr) +{ + struct lcs_card *card; + struct ccwgroup_device *gdev; + int rc; + + card = (struct lcs_card *) ptr; + daemonize("lcs_recover"); + + LCS_DBF_TEXT(4, trace, "recover1"); + if (!lcs_do_run_thread(card, LCS_RECOVERY_THREAD)) + return 0; + LCS_DBF_TEXT(4, trace, "recover2"); + gdev = card->gdev; + PRINT_WARN("Recovery of device %s started...\n", gdev->dev.bus_id); + rc = __lcs_shutdown_device(gdev, 1); + rc = lcs_new_device(gdev); + if (!rc) + PRINT_INFO("Device %s successfully recovered!\n", + card->dev->name); + else + PRINT_INFO("Device %s could not be recovered!\n", + card->dev->name); + lcs_clear_thread_running_bit(card, LCS_RECOVERY_THREAD); + return 0; +} + /** * lcs_remove_device, free buffers and card */ @@ -2058,7 +2316,6 @@ __init lcs_init_module(void) PRINT_ERR("Initialization failed\n"); return rc; } - return 0; } @@ -2078,6 +2335,6 @@ __exit lcs_cleanup_module(void) module_init(lcs_init_module); module_exit(lcs_cleanup_module); -MODULE_AUTHOR("Frank Pavlic "); +MODULE_AUTHOR("Frank Pavlic "); MODULE_LICENSE("GPL");