X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fs390%2Fnet%2Fqeth_main.c;h=ceab62acf37e39b325e806f308fa1834667b49e8;hb=4e4f43fe003969bdaa246374b90e16708a22ef79;hp=ba6ed9bee4b900f7dac8fb6d353f23e2599810a5;hpb=86090fcac5e27b630656fe3d963a6b80e26dac44;p=linux-2.6.git diff --git a/drivers/s390/net/qeth_main.c b/drivers/s390/net/qeth_main.c index ba6ed9bee..ceab62acf 100644 --- a/drivers/s390/net/qeth_main.c +++ b/drivers/s390/net/qeth_main.c @@ -1,6 +1,6 @@ /* * - * linux/drivers/s390/net/qeth_main.c ($Revision: 1.89 $) + * linux/drivers/s390/net/qeth_main.c ($Revision: 1.112 $) * * Linux on zSeries OSA Express and HiperSockets support * @@ -12,7 +12,7 @@ * Frank Pavlic (pavlic@de.ibm.com) and * Thomas Spatzier * - * $Revision: 1.89 $ $Date: 2004/04/27 16:27:26 $ + * $Revision: 1.112 $ $Date: 2004/05/19 09:28:21 $ * * 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 @@ -78,10 +78,9 @@ qeth_eyecatcher(void) #include "qeth_mpc.h" #include "qeth_fs.h" -#define VERSION_QETH_C "$Revision: 1.89 $" -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 ")"; +#define VERSION_QETH_C "$Revision: 1.112 $" +static const char *version = "qeth S/390 OSA-Express driver"; + /** * Debug Facility Stuff */ @@ -101,12 +100,13 @@ 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 *); -static atomic_t qeth_hsi_count; - /** * here we go with function implementation */ @@ -154,6 +154,72 @@ qeth_irq_tasklet(unsigned long); static int qeth_set_online(struct ccwgroup_device *); + +static struct qeth_ipaddr * +qeth_get_addr_buffer(enum qeth_prot_versions); + +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; + + QETH_DBF_TEXT(trace, 2, "notreg"); + /*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 */ @@ -459,6 +525,7 @@ qeth_set_offline(struct ccwgroup_device *cgdev) ccw_device_set_offline(CARD_RDEV(card)); if (recover_flag == CARD_STATE_UP) card->state = CARD_STATE_RECOVER; + qeth_notify_processes(); return 0; } @@ -478,8 +545,6 @@ qeth_remove_device(struct ccwgroup_device *cgdev) card->use_hard_stop = 1; qeth_set_offline(cgdev); } - if (card->info.type == QETH_CARD_TYPE_IQD) - atomic_dec(&qeth_hsi_count); /* remove form our internal list */ write_lock_irqsave(&qeth_card_list.rwlock, flags); list_del(&card->list); @@ -700,7 +765,7 @@ static void qeth_set_ip_addr_list(struct qeth_card *card) { struct list_head failed_todos; - struct qeth_ipaddr *todo, *addr, *tmp; + struct qeth_ipaddr *todo, *addr; unsigned long flags; int rc; @@ -709,9 +774,10 @@ qeth_set_ip_addr_list(struct qeth_card *card) INIT_LIST_HEAD(&failed_todos); -process_todos: spin_lock_irqsave(&card->ip_lock, flags); - list_for_each_entry_safe(todo, tmp, &card->ip_tbd_list, entry) { + while (!list_empty(&card->ip_tbd_list)) { + todo = list_entry(card->ip_tbd_list.next, + struct qeth_ipaddr, entry); list_del_init(&todo->entry); rc = __qeth_ref_ip_on_card(card, todo, &addr); if (rc == 0) { @@ -721,28 +787,24 @@ process_todos: /* new entry to be added to on-card list */ spin_unlock_irqrestore(&card->ip_lock, flags); rc = qeth_register_addr_entry(card, todo); - if (!rc){ - spin_lock_irqsave(&card->ip_lock, flags); + spin_lock_irqsave(&card->ip_lock, flags); + if (!rc) list_add_tail(&todo->entry, &card->ip_list); - spin_unlock_irqrestore(&card->ip_lock, flags); - } else + else list_add_tail(&todo->entry, &failed_todos); - goto process_todos; } 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); kfree(todo); } else { - spin_lock_irqsave(&card->ip_lock, flags); list_add_tail(&addr->entry, &card->ip_list); list_add_tail(&todo->entry, &failed_todos); - spin_unlock_irqrestore(&card->ip_lock, flags); } - goto process_todos; } } spin_unlock_irqrestore(&card->ip_lock, flags); @@ -937,7 +999,6 @@ qeth_set_intial_options(struct qeth_card *card) 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.enable_takeover = 1; card->options.fake_broadcast = 0; card->options.add_hhlen = DEFAULT_ADD_HHLEN; card->options.fake_ll = 0; @@ -998,10 +1059,6 @@ qeth_determine_card_type(struct qeth_card *card) 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]; - if (card->options.enable_takeover) - card->info.func_level = known_devices[i][6]; - else - card->info.func_level = known_devices[i][7]; card->qdio.no_out_queues = known_devices[i][8]; card->info.is_multicast_different = known_devices[i][9]; return 0; @@ -1456,7 +1513,7 @@ qeth_alloc_reply(struct qeth_card *card) { struct qeth_reply *reply; - reply = kmalloc(sizeof(struct qeth_reply), GFP_KERNEL|GFP_ATOMIC); + reply = kmalloc(sizeof(struct qeth_reply), GFP_ATOMIC); if (reply){ memset(reply, 0, sizeof(struct qeth_reply)); atomic_set(&reply->refcnt, 1); @@ -1627,17 +1684,20 @@ qeth_send_control_data_cb(struct qeth_channel *channel, spin_unlock_irqrestore(&card->lock, flags); keep_reply = 0; if (reply->callback != NULL) { - if (cmd) + if (cmd) { + reply->offset = (__u16)((char*)cmd - + (char *)iob->data); keep_reply = reply->callback(card, reply, (unsigned long)cmd); + } else keep_reply = reply->callback(card, reply, (unsigned long)iob); } if (cmd) - reply->rc = cmd->hdr.return_code; + reply->rc = (s16) cmd->hdr.return_code; else if (iob->rc) reply->rc = iob->rc; if (keep_reply) { @@ -1703,7 +1763,10 @@ qeth_send_control_data(struct qeth_card *card, int len, init_timer(&timer); timer.function = qeth_cmd_timeout; timer.data = (unsigned long) reply; - timer.expires = jiffies + QETH_TIMEOUT; + if (IS_IPA(iob->data)) + timer.expires = jiffies + QETH_IPA_TIMEOUT; + else + timer.expires = jiffies + QETH_TIMEOUT; init_waitqueue_head(&reply->wait_q); spin_lock_irqsave(&card->lock, flags); list_add_tail(&reply->list, &card->cmd_waiter_list); @@ -1743,12 +1806,10 @@ qeth_send_ipa_cmd(struct qeth_card *card, struct qeth_cmd_buffer *iob, (struct qeth_card *,struct qeth_reply*, unsigned long), void *reply_param) { - struct qeth_ipa_cmd *cmd; int rc; QETH_DBF_TEXT(trace,4,"sendipa"); - cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); memcpy(iob->data, IPA_PDU_HEADER, IPA_PDU_HEADER_SIZE); memcpy(QETH_IPA_CMD_DEST_ADDR(iob->data), &card->token.ulp_connection_r, QETH_MPC_TOKEN_LENGTH); @@ -2227,12 +2288,11 @@ qeth_process_inbound_buffer(struct qeth_card *card, while((skb = qeth_get_next_skb(card, buf->buffer, &element, &offset, &hdr))){ qeth_rebuild_skb(card, skb, hdr); - -#ifdef CONFIG_QETH_PERF_STATS - card->perf_stats.inbound_time += qeth_get_micros() - - card->perf_stats.inbound_start_time; - card->perf_stats.inbound_cnt++; -#endif + /* is device UP ? */ + if (!(card->dev->flags & IFF_UP)){ + dev_kfree_skb_irq(skb); + continue; + } skb->dev = card->dev; rxrc = netif_rx(skb); card->dev->last_rx = jiffies; @@ -2330,9 +2390,17 @@ qeth_queue_input_buffer(struct qeth_card *card, int index) * 'index') un-requeued -> this buffer is the first buffer that * will be requeued the next time */ +#ifdef CONFIG_QETH_PERF_STATS + card->perf_stats.inbound_do_qdio_cnt++; + card->perf_stats.inbound_do_qdio_start_time = qeth_get_micros(); +#endif rc = do_QDIO(CARD_DDEV(card), - QDIO_FLAG_SYNC_INPUT, + QDIO_FLAG_SYNC_INPUT | QDIO_FLAG_UNDER_INTERRUPT, 0, queue->next_buf_to_init, count, NULL); +#ifdef CONFIG_QETH_PERF_STATS + card->perf_stats.inbound_do_qdio_time += qeth_get_micros() - + card->perf_stats.inbound_do_qdio_start_time; +#endif if (rc){ PRINT_WARN("qeth_queue_input_buffer's do_QDIO " "return %i (device %s).\n", @@ -2369,6 +2437,7 @@ qeth_qdio_input_handler(struct ccw_device * ccwdev, unsigned int status, card = (struct qeth_card *) card_ptr; net_dev = card->dev; #ifdef CONFIG_QETH_PERF_STATS + card->perf_stats.inbound_cnt++; card->perf_stats.inbound_start_time = qeth_get_micros(); #endif if (status & QDIO_STATUS_LOOK_FOR_ERROR) { @@ -2391,6 +2460,10 @@ qeth_qdio_input_handler(struct ccw_device * ccwdev, unsigned int status, qeth_put_buffer_pool_entry(card, buffer->pool_entry); qeth_queue_input_buffer(card, index); } +#ifdef CONFIG_QETH_PERF_STATS + card->perf_stats.inbound_time += qeth_get_micros() - + card->perf_stats.inbound_start_time; +#endif } static inline int @@ -2449,6 +2522,9 @@ qeth_flush_buffers(struct qeth_qdio_out_q *queue, int under_int, buf->buffer->element[buf->next_element_to_fill - 1].flags |= SBAL_FLAGS_LAST_ENTRY; + if (queue->card->info.type == QETH_CARD_TYPE_IQD) + continue; + if (!queue->do_pack){ if ((atomic_read(&queue->used_buffers) >= (QETH_HIGH_WATERMARK_PACK - @@ -2479,6 +2555,10 @@ qeth_flush_buffers(struct qeth_qdio_out_q *queue, int under_int, } queue->card->dev->trans_start = jiffies; +#ifdef CONFIG_QETH_PERF_STATS + queue->card->perf_stats.outbound_do_qdio_cnt++; + queue->card->perf_stats.outbound_do_qdio_start_time = qeth_get_micros(); +#endif if (under_int) rc = do_QDIO(CARD_DDEV(queue->card), QDIO_FLAG_SYNC_OUTPUT | QDIO_FLAG_UNDER_INTERRUPT, @@ -2486,6 +2566,10 @@ qeth_flush_buffers(struct qeth_qdio_out_q *queue, int under_int, else rc = do_QDIO(CARD_DDEV(queue->card), QDIO_FLAG_SYNC_OUTPUT, queue->queue_no, index, count, NULL); +#ifdef CONFIG_QETH_PERF_STATS + queue->card->perf_stats.outbound_do_qdio_time += qeth_get_micros() - + queue->card->perf_stats.outbound_do_qdio_start_time; +#endif if (rc){ QETH_DBF_SPRINTF(trace, 0, "qeth_flush_buffers: do_QDIO " "returned error (%i) on device %s.", @@ -2497,7 +2581,6 @@ qeth_flush_buffers(struct qeth_qdio_out_q *queue, int under_int, } #ifdef CONFIG_QETH_PERF_STATS queue->card->perf_stats.bufs_sent += count; - queue->card->perf_stats.outbound_cnt++; #endif } @@ -2591,6 +2674,10 @@ qeth_qdio_output_handler(struct ccw_device * ccwdev, unsigned int status, } } +#ifdef CONFIG_QETH_PERF_STATS + card->perf_stats.outbound_handler_cnt++; + card->perf_stats.outbound_handler_start_time = qeth_get_micros(); +#endif for(i = first_element; i < (first_element + count); ++i){ buffer = &queue->bufs[i % QDIO_MAX_BUFFERS_PER_Q]; /*we only handle the KICK_IT error by doing a recovery */ @@ -2609,6 +2696,10 @@ qeth_qdio_output_handler(struct ccw_device * ccwdev, unsigned int status, } netif_wake_queue(card->dev); +#ifdef CONFIG_QETH_PERF_STATS + card->perf_stats.outbound_handler_time += qeth_get_micros() - + card->perf_stats.outbound_handler_start_time; +#endif } static char* @@ -3134,20 +3225,6 @@ out_qdio: return rc; } -static void -qeth_set_device_name(struct qeth_card *card) -{ - char buf[IF_NAME_LEN]; - - memset(buf, 0, IF_NAME_LEN); - if (card->info.type == QETH_CARD_TYPE_IQD) { - sprintf(buf,"hsi%d", atomic_read(&qeth_hsi_count)); - atomic_inc(&qeth_hsi_count); - memcpy(card->dev->name,buf,IF_NAME_LEN); - } - -} - static struct net_device * qeth_get_netdevice(enum qeth_card_types type, enum qeth_link_types linktype) { @@ -3167,6 +3244,8 @@ qeth_get_netdevice(enum qeth_card_types type, enum qeth_link_types linktype) } break; case QETH_CARD_TYPE_IQD: + dev = alloc_netdev(0, "hsi%d", ether_setup); + break; default: dev = alloc_etherdev(0); } @@ -3195,11 +3274,8 @@ qeth_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) card->stats.tx_carrier_errors++; return -EIO; } - if (netif_queue_stopped(dev) ) { - card->stats.tx_dropped++; - return -EBUSY; - } #ifdef CONFIG_QETH_PERF_STATS + card->perf_stats.outbound_cnt++; card->perf_stats.outbound_start_time = qeth_get_micros(); #endif /* @@ -3210,6 +3286,10 @@ qeth_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) if (!(rc = qeth_send_packet(card, skb))) netif_wake_queue(dev); +#ifdef CONFIG_QETH_PERF_STATS + card->perf_stats.outbound_time += qeth_get_micros() - + card->perf_stats.outbound_start_time; +#endif return rc; } @@ -3556,7 +3636,6 @@ qeth_fill_buffer(struct qeth_qdio_out_q *queue, struct qeth_qdio_out_buffer *buf int first_lap = 1; QETH_DBF_TEXT(trace, 6, "qdfillbf"); - buffer = buf->buffer; atomic_inc(&skb->users); skb_queue_tail(&buf->skb_list, skb); @@ -3610,33 +3689,59 @@ qeth_fill_buffer(struct qeth_qdio_out_q *queue, struct qeth_qdio_out_buffer *buf } static inline int -qeth_do_send_packet(struct qeth_card *card, struct sk_buff *skb, - struct qeth_qdio_out_q *queue, int ipv, - int cast_type) +qeth_do_send_packet_fast(struct qeth_card *card, struct qeth_qdio_out_q *queue, + struct sk_buff *skb, struct qeth_hdr *hdr, + int elements_needed) +{ + struct qeth_qdio_out_buffer *buffer; + int index; + int rc = 0; + + QETH_DBF_TEXT(trace, 6, "dosndpfa"); + + spin_lock(&queue->lock); + /* do we have empty buffers? */ + if (atomic_read(&queue->used_buffers) >= (QDIO_MAX_BUFFERS_PER_Q - 1)){ + card->stats.tx_dropped++; + rc = -EBUSY; + spin_unlock(&queue->lock); + goto out; + } + index = queue->next_buf_to_fill; + buffer = &queue->bufs[queue->next_buf_to_fill]; + BUG_ON(buffer->state == QETH_QDIO_BUF_PRIMED); + queue->next_buf_to_fill = (queue->next_buf_to_fill + 1) % + QDIO_MAX_BUFFERS_PER_Q; + atomic_inc(&queue->used_buffers); + spin_unlock(&queue->lock); + + /* go on sending ... */ + netif_wake_queue(skb->dev); + qeth_fill_buffer(queue, buffer, (char *)hdr, skb); + qeth_flush_buffers(queue, 0, index, 1); +out: + return rc; +} + +static inline int +qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue, + struct sk_buff *skb, struct qeth_hdr *hdr, + int elements_needed) { - struct qeth_hdr *hdr; struct qeth_qdio_out_buffer *buffer; - int elements_needed; int start_index; int flush_count = 0; - int rc; + int rc = 0; QETH_DBF_TEXT(trace, 6, "dosndpkt"); - if ((rc = qeth_prepare_skb(card, &skb, &hdr, ipv))){ - QETH_DBF_TEXT_(trace, 4, "1err%d", rc); - return rc; - } - qeth_fill_header(card, hdr, skb, ipv, cast_type); - elements_needed = 1 + (((((unsigned long) hdr) % PAGE_SIZE) + skb->len) - >> PAGE_SHIFT); - if (elements_needed > QETH_MAX_BUFFER_ELEMENTS(card)){ - PRINT_ERR("qeth_do_send_packet: invalid size of " - "IP packet. Discarded."); - return -EINVAL; - } - spin_lock(&queue->lock); + /* do we have empty buffers? */ + if (atomic_read(&queue->used_buffers) >= (QDIO_MAX_BUFFERS_PER_Q - 2)){ + card->stats.tx_dropped++; + rc = -EBUSY; + goto out; + } start_index = queue->next_buf_to_fill; buffer = &queue->bufs[queue->next_buf_to_fill]; BUG_ON(buffer->state == QETH_QDIO_BUF_PRIMED); @@ -3655,12 +3760,8 @@ qeth_do_send_packet(struct qeth_card *card, struct sk_buff *skb, } } - rc = qeth_fill_buffer(queue, buffer, (char *)hdr, skb); - if (rc) { - PRINT_WARN("qeth_do_send_packet: error during " - "qeth_fill_buffer."); - card->stats.tx_dropped++; - } else if (buffer->state == QETH_QDIO_BUF_PRIMED){ + qeth_fill_buffer(queue, buffer, (char *)hdr, skb); + if (buffer->state == QETH_QDIO_BUF_PRIMED){ /* next time fill the next buffer */ flush_count++; atomic_inc(&queue->used_buffers); @@ -3668,15 +3769,14 @@ qeth_do_send_packet(struct qeth_card *card, struct sk_buff *skb, QDIO_MAX_BUFFERS_PER_Q; } /* check if we need to switch packing state of this queue */ - if (card->info.type != QETH_CARD_TYPE_IQD) - flush_count += qeth_switch_packing_state(queue); + flush_count += qeth_switch_packing_state(queue); if (flush_count) qeth_flush_buffers(queue, 0, start_index, flush_count); if (!atomic_read(&queue->set_pci_flags_count)) qeth_flush_buffers_on_no_pci(queue, 0); - +out: spin_unlock(&queue->lock); return rc; @@ -3688,6 +3788,8 @@ qeth_send_packet(struct qeth_card *card, struct sk_buff *skb) int ipv; int cast_type; struct qeth_qdio_out_q *queue; + struct qeth_hdr *hdr; + int elements_needed; int rc; QETH_DBF_TEXT(trace, 6, "sendpkt"); @@ -3696,24 +3798,30 @@ qeth_send_packet(struct qeth_card *card, struct sk_buff *skb) cast_type = qeth_get_cast_type(card, skb); queue = card->qdio.out_qs [qeth_get_priority_queue(card, skb, ipv, cast_type)]; - /* do we have empty buffers? */ - rc = (atomic_read(&queue->used_buffers) >= - QDIO_MAX_BUFFERS_PER_Q - 1) ? -EBUSY : 0; - if (rc) { - card->stats.tx_dropped++; + + if ((rc = qeth_prepare_skb(card, &skb, &hdr, ipv))){ QETH_DBF_TEXT_(trace, 4, "1err%d", rc); return rc; } + qeth_fill_header(card, hdr, skb, ipv, cast_type); + elements_needed = 1 + (((((unsigned long) hdr) % PAGE_SIZE) + skb->len) + >> PAGE_SHIFT); + if (elements_needed > QETH_MAX_BUFFER_ELEMENTS(card)){ + PRINT_ERR("qeth_do_send_packet: invalid size of " + "IP packet. Discarded."); + return -EINVAL; + } - rc = qeth_do_send_packet(card, skb, queue, ipv, cast_type); + if (card->info.type != QETH_CARD_TYPE_IQD) + rc = qeth_do_send_packet(card, queue, skb, hdr, + elements_needed); + else + rc = qeth_do_send_packet_fast(card, queue, skb, hdr, + elements_needed); if (!rc){ card->stats.tx_packets++; card->stats.tx_bytes += skb->len; -#ifdef CONFIG_QETH_PERF_STATS - card->perf_stats.outbound_time += qeth_get_micros() - - card->perf_stats.outbound_start_time; -#endif } return rc; } @@ -3859,69 +3967,107 @@ qeth_arp_set_no_entries(struct qeth_card *card, int no_entries) if (rc) { tmp = rc; PRINT_WARN("Could not set number of ARP entries on %s: " - "%s (0x%x)\n", + "%s (0x%x/%d)\n", card->info.if_name, qeth_arp_get_error_cause(&rc), - tmp); + tmp, tmp); } return rc; } +static inline void +qeth_copy_arp_entries_stripped(struct qeth_arp_query_info *qinfo, + struct qeth_arp_query_data *qdata, + int entry_size, int uentry_size) +{ + char *entry_ptr; + char *uentry_ptr; + int i; + + entry_ptr = (char *)&qdata->data; + uentry_ptr = (char *)(qinfo->udata + qinfo->udata_offset); + for (i = 0; i < qdata->no_entries; ++i){ + /* strip off 32 bytes "media specific information" */ + memcpy(uentry_ptr, (entry_ptr + 32), entry_size - 32); + entry_ptr += entry_size; + uentry_ptr += uentry_size; + } +} + static int qeth_arp_query_cb(struct qeth_card *card, struct qeth_reply *reply, unsigned long data) { - struct qeth_ipa_arp_cmd *cmd; + struct qeth_ipa_cmd *cmd; struct qeth_arp_query_data *qdata; struct qeth_arp_query_info *qinfo; int entry_size; + int uentry_size; int i; QETH_DBF_TEXT(trace,4,"arpquecb"); qinfo = (struct qeth_arp_query_info *) reply->param; - cmd = (struct qeth_ipa_arp_cmd *) data; - if (cmd->ihdr.return_code) { - QETH_DBF_TEXT_(trace,4,"qaer1%i", cmd->ihdr.return_code); + cmd = (struct qeth_ipa_cmd *) data; + if (cmd->hdr.return_code) { + QETH_DBF_TEXT_(trace,4,"qaer1%i", cmd->hdr.return_code); return 0; } - if (cmd->shdr.return_code) { - cmd->ihdr.return_code = cmd->shdr.return_code; - QETH_DBF_TEXT_(trace,4,"qaer2%i", cmd->ihdr.return_code); + if (cmd->data.setassparms.hdr.return_code) { + cmd->hdr.return_code = cmd->data.setassparms.hdr.return_code; + QETH_DBF_TEXT_(trace,4,"qaer2%i", cmd->hdr.return_code); return 0; } - qdata = &cmd->data.query_arp; + qdata = &cmd->data.setassparms.data.query_arp; switch(qdata->reply_bits){ case 5: - entry_size = sizeof(struct qeth_arp_qi_entry5); + uentry_size = entry_size = sizeof(struct qeth_arp_qi_entry5); + if (qinfo->mask_bits & QETH_QARP_STRIP_ENTRIES) + uentry_size = sizeof(struct qeth_arp_qi_entry5_short); break; case 7: - entry_size = sizeof(struct qeth_arp_qi_entry7); - break; + /* fall through to default */ default: /* tr is the same as eth -> entry7 */ - entry_size = sizeof(struct qeth_arp_qi_entry7); + uentry_size = entry_size = sizeof(struct qeth_arp_qi_entry7); + if (qinfo->mask_bits & QETH_QARP_STRIP_ENTRIES) + uentry_size = sizeof(struct qeth_arp_qi_entry7_short); break; } /* check if there is enough room in userspace */ if ((qinfo->udata_len - qinfo->udata_offset) < - qdata->no_entries * entry_size){ + qdata->no_entries * uentry_size){ QETH_DBF_TEXT_(trace, 4, "qaer3%i", -ENOMEM); - cmd->ihdr.return_code = -ENOMEM; + cmd->hdr.return_code = -ENOMEM; + PRINT_WARN("query ARP user space buffer is too small for " + "the returned number of ARP entries. " + "Aborting query!\n"); goto out_error; } - QETH_DBF_TEXT_(trace, 4, "anore%i", cmd->shdr.number_of_replies); - QETH_DBF_TEXT_(trace, 4, "aseqn%i", cmd->shdr.seq_no); + QETH_DBF_TEXT_(trace, 4, "anore%i", + cmd->data.setassparms.hdr.number_of_replies); + QETH_DBF_TEXT_(trace, 4, "aseqn%i", cmd->data.setassparms.hdr.seq_no); QETH_DBF_TEXT_(trace, 4, "anoen%i", qdata->no_entries); - for (i = 0; i < qdata->no_entries; ++i){ + + if (qinfo->mask_bits & QETH_QARP_STRIP_ENTRIES) { + /* strip off "media specific information" */ + qeth_copy_arp_entries_stripped(qinfo, qdata, entry_size, + uentry_size); + } else + /*copy entries to user buffer*/ memcpy(qinfo->udata + qinfo->udata_offset, - qdata->data + i*entry_size, entry_size); - qinfo->no_entries++; - qinfo->udata_offset += entry_size; - } + (char *)&qdata->data, qdata->no_entries*uentry_size); + + qinfo->no_entries += qdata->no_entries; + qinfo->udata_offset += (qdata->no_entries*uentry_size); /* check if all replies received ... */ - if (cmd->shdr.seq_no < cmd->shdr.number_of_replies) + if (cmd->data.setassparms.hdr.seq_no < + cmd->data.setassparms.hdr.number_of_replies) return 1; memcpy(qinfo->udata, &qinfo->no_entries, 4); + /* keep STRIP_ENTRIES flag so the user program can distinguish + * stripped entries from normal ones */ + if (qinfo->mask_bits & QETH_QARP_STRIP_ENTRIES) + qdata->reply_bits |= QETH_QARP_STRIP_ENTRIES; memcpy(qinfo->udata + QETH_QARP_MASK_OFFSET,&qdata->reply_bits,2); return 0; out_error: @@ -3930,70 +4076,32 @@ out_error: return 0; } -static struct qeth_cmd_buffer * -qeth_get_ipacmd_buffer(struct qeth_card *, enum qeth_ipa_cmds, - enum qeth_prot_versions); - -struct qeth_cmd_buffer * -qeth_get_ipa_arp_cmd_buffer(struct qeth_card *card, u16 cmd_code, - u32 data_len, enum qeth_prot_versions proto) -{ - struct qeth_cmd_buffer *iob; - struct qeth_ipa_arp_cmd *cmd; - u16 s1, s2; - - QETH_DBF_TEXT(trace,4,"getarpcm"); - iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETASSPARMS, proto); - - memcpy(iob->data, IPA_PDU_HEADER, IPA_PDU_HEADER_SIZE); - - if ((IPA_PDU_HEADER_SIZE + QETH_ARP_CMD_BASE_LEN + data_len) > 256) { - /* adjust sizes in IPA_PDU_HEADER */ - s1 = (u32) IPA_PDU_HEADER_SIZE + QETH_ARP_CMD_BASE_LEN + - data_len; - s2 = (u32) QETH_ARP_CMD_BASE_LEN + data_len; - memcpy(QETH_IPA_PDU_LEN_TOTAL(iob->data), &s1, 2); - memcpy(QETH_IPA_PDU_LEN_PDU1(iob->data), &s2, 2); - memcpy(QETH_IPA_PDU_LEN_PDU2(iob->data), &s2, 2); - memcpy(QETH_IPA_PDU_LEN_PDU3(iob->data), &s2, 2); - } - - cmd = (struct qeth_ipa_arp_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); - cmd->shdr.assist_no = IPA_ARP_PROCESSING; - cmd->shdr.length = 8 + data_len; - cmd->shdr.command_code = cmd_code; - cmd->shdr.return_code = 0; - cmd->shdr.seq_no = 0; - - return iob; -} - static int qeth_send_ipa_arp_cmd(struct qeth_card *card, struct qeth_cmd_buffer *iob, - char *data, int data_len, - int (*reply_cb) - (struct qeth_card *,struct qeth_reply*, unsigned long), + int len, int (*reply_cb) + (struct qeth_card *, + struct qeth_reply*, unsigned long), void *reply_param) { int rc; QETH_DBF_TEXT(trace,4,"sendarp"); - memcpy(QETH_IPA_ARP_DATA_POS(iob->data), data, data_len); + memcpy(iob->data, IPA_PDU_HEADER, IPA_PDU_HEADER_SIZE); memcpy(QETH_IPA_CMD_DEST_ADDR(iob->data), &card->token.ulp_connection_r, QETH_MPC_TOKEN_LENGTH); - - rc = qeth_send_control_data(card, IPA_PDU_HEADER_SIZE + - QETH_ARP_CMD_BASE_LEN + data_len, iob, + rc = qeth_send_control_data(card, IPA_PDU_HEADER_SIZE + len, iob, reply_cb, reply_param); return rc; } +static struct qeth_cmd_buffer * +qeth_get_setassparms_cmd(struct qeth_card *, enum qeth_ipa_funcs, + __u16, __u16, enum qeth_prot_versions); static int qeth_arp_query(struct qeth_card *card, char *udata) { struct qeth_cmd_buffer *iob; - struct qeth_arp_query_data *qdata; struct qeth_arp_query_info qinfo = {0, }; int tmp; int rc; @@ -4008,40 +4116,33 @@ qeth_arp_query(struct qeth_card *card, char *udata) */ if (card->info.guestlan) return -EOPNOTSUPP; - if (!qeth_is_supported(card,IPA_ARP_PROCESSING)) { + if (!qeth_is_supported(card,/*IPA_QUERY_ARP_ADDR_INFO*/ + IPA_ARP_PROCESSING)) { PRINT_WARN("ARP processing not supported " "on %s!\n", card->info.if_name); return -EOPNOTSUPP; } - /* get size of userspace mem area */ - if (copy_from_user(&qinfo.udata_len, udata, 4)) + /* get size of userspace buffer and mask_bits -> 6 bytes */ + if (copy_from_user(&qinfo, udata, 6)) return -EFAULT; if (!(qinfo.udata = kmalloc(qinfo.udata_len, GFP_KERNEL))) return -ENOMEM; memset(qinfo.udata, 0, qinfo.udata_len); qinfo.udata_offset = QETH_QARP_ENTRIES_OFFSET; - /* alloc mem area for the actual query */ - if (!(qdata = kmalloc(sizeof(struct qeth_arp_query_data), - GFP_KERNEL))){ - kfree(qinfo.udata); - return -ENOMEM; - } - memset(qdata, 0, sizeof(struct qeth_arp_query_data)); - /* do not give sizeof(struct qeth_arp_query_data) to next command; - * this would cause the IPA PDU size to be set to a value of > 256 - * and this is to much for HiperSockets */ - iob = qeth_get_ipa_arp_cmd_buffer(card, IPA_CMD_ASS_ARP_QUERY_INFO, - 0, QETH_PROT_IPV4); + iob = qeth_get_setassparms_cmd(card, IPA_ARP_PROCESSING, + IPA_CMD_ASS_ARP_QUERY_INFO, + sizeof(int),QETH_PROT_IPV4); + rc = qeth_send_ipa_arp_cmd(card, iob, - (char *) qdata, - sizeof(struct qeth_arp_query_data), + QETH_SETASS_BASE_LEN+QETH_ARP_CMD_LEN, qeth_arp_query_cb, (void *)&qinfo); if (rc) { tmp = rc; - PRINT_WARN("Error while querying ARP cache on %s: %s (0x%x)\n", + PRINT_WARN("Error while querying ARP cache on %s: %s " + "(0x%x/%d)\n", card->info.if_name, qeth_arp_get_error_cause(&rc), - tmp); + tmp, tmp); copy_to_user(udata, qinfo.udata, 4); } else { copy_to_user(udata, qinfo.udata, qinfo.udata_len); @@ -4050,13 +4151,143 @@ qeth_arp_query(struct qeth_card *card, char *udata) return rc; } +/** + * SNMP command callback + */ static int -qeth_default_setassparms_cb(struct qeth_card *, struct qeth_reply *, - unsigned long); +qeth_snmp_command_cb(struct qeth_card *card, struct qeth_reply *reply, + unsigned long sdata) +{ + struct qeth_ipa_cmd *cmd; + struct qeth_arp_query_info *qinfo; + struct qeth_snmp_cmd *snmp; + unsigned char *data; + __u16 data_len; + + QETH_DBF_TEXT(trace,3,"snpcmdcb"); + + cmd = (struct qeth_ipa_cmd *) sdata; + data = (unsigned char *)((char *)cmd - reply->offset); + qinfo = (struct qeth_arp_query_info *) reply->param; + snmp = &cmd->data.setadapterparms.data.snmp; + + if (cmd->hdr.return_code) { + QETH_DBF_TEXT_(trace,4,"scer1%i", cmd->hdr.return_code); + return 0; + } + if (cmd->data.setadapterparms.hdr.return_code) { + cmd->hdr.return_code = cmd->data.setadapterparms.hdr.return_code; + QETH_DBF_TEXT_(trace,4,"scer2%i", cmd->hdr.return_code); + return 0; + } + data_len = *((__u16*)QETH_IPA_PDU_LEN_PDU1(data)); + if (cmd->data.setadapterparms.hdr.seq_no == 1) + data_len -= (__u16)((char*)&snmp->request - (char *)cmd); + else + data_len -= (__u16)((char *)&snmp->data - (char *)cmd); + /* check if there is enough room in userspace */ + if ((qinfo->udata_len - qinfo->udata_offset) < data_len) { + QETH_DBF_TEXT_(trace, 4, "scer3%i", -ENOMEM); + cmd->hdr.return_code = -ENOMEM; + return 0; + } + QETH_DBF_TEXT_(trace, 4, "snore%i", + cmd->data.setadapterparms.hdr.used_total); + QETH_DBF_TEXT_(trace, 4, "sseqn%i", cmd->data.setassparms.hdr.seq_no); + /*copy entries to user buffer*/ + if (cmd->data.setadapterparms.hdr.seq_no == 1) { + memcpy(qinfo->udata + qinfo->udata_offset, + (char *)snmp,offsetof(struct qeth_snmp_cmd,data)); + qinfo->udata_offset += offsetof(struct qeth_snmp_cmd, data); + } + memcpy(qinfo->udata + qinfo->udata_offset, + (char *)&snmp->data, data_len); + qinfo->udata_offset += data_len; + /* check if all replies received ... */ + QETH_DBF_TEXT_(trace, 4, "srtot%i", + cmd->data.setadapterparms.hdr.used_total); + QETH_DBF_TEXT_(trace, 4, "srseq%i", + cmd->data.setadapterparms.hdr.seq_no); + if (cmd->data.setadapterparms.hdr.seq_no < + cmd->data.setadapterparms.hdr.used_total) + return 1; + return 0; + +} static struct qeth_cmd_buffer * -qeth_get_setassparms_cmd(struct qeth_card *, enum qeth_ipa_funcs, - __u16, __u16, enum qeth_prot_versions); +qeth_get_ipacmd_buffer(struct qeth_card *, enum qeth_ipa_cmds, + enum qeth_prot_versions ); + +static struct qeth_cmd_buffer * +qeth_get_adapter_cmd(struct qeth_card *card, __u32 command, __u32 cmdlen) +{ + struct qeth_cmd_buffer *iob; + struct qeth_ipa_cmd *cmd; + + iob = qeth_get_ipacmd_buffer(card,IPA_CMD_SETADAPTERPARMS, + QETH_PROT_IPV4); + cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); + cmd->data.setadapterparms.hdr.cmdlength = cmdlen; + cmd->data.setadapterparms.hdr.command_code = command; + cmd->data.setadapterparms.hdr.used_total = 1; + cmd->data.setadapterparms.hdr.seq_no = 1; + + return iob; +} + +/** + * function to send SNMP commands to OSA-E card + */ +static int +qeth_snmp_command(struct qeth_card *card, char *udata) +{ + struct qeth_cmd_buffer *iob; + struct qeth_ipa_cmd *cmd; + struct qeth_snmp_ureq ureq; + struct qeth_arp_query_info qinfo = {0, }; + int rc = 0; + + QETH_DBF_TEXT(trace,3,"snmpcmd"); + + if (card->info.guestlan) + return -EOPNOTSUPP; + if (!qeth_adp_supported(card,IPA_SETADP_SET_SNMP_CONTROL)) { + PRINT_WARN("SNMP Query MIBS not supported " + "on %s!\n", card->info.if_name); + return -EOPNOTSUPP; + } + if (copy_from_user(&ureq, udata, sizeof(struct qeth_snmp_ureq))) + return -EFAULT; + qinfo.udata_len = ureq.hdr.data_len; + if (!(qinfo.udata = kmalloc(qinfo.udata_len, GFP_KERNEL))) + return -ENOMEM; + memset(qinfo.udata, 0, qinfo.udata_len); + qinfo.udata_offset = sizeof(struct qeth_snmp_ureq_hdr); + + iob = qeth_get_adapter_cmd(card, IPA_SETADP_SET_SNMP_CONTROL, + QETH_SNMP_SETADP_CMDLENGTH+ureq.hdr.req_len); + cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); + memcpy(&cmd->data.setadapterparms.data.snmp, &ureq.cmd, + sizeof(struct qeth_snmp_cmd)); + rc = qeth_send_ipa_arp_cmd(card, iob, + QETH_SETADP_BASE_LEN + QETH_ARP_DATA_SIZE + + ureq.hdr.req_len, qeth_snmp_command_cb, + (void *)&qinfo); + if (rc) + PRINT_WARN("SNMP command failed on %s: (0x%x)\n", + card->info.if_name, rc); + else + copy_to_user(udata, qinfo.udata, qinfo.udata_len); + + + kfree(qinfo.udata); + return rc; +} + +static int +qeth_default_setassparms_cb(struct qeth_card *, struct qeth_reply *, + unsigned long); static int qeth_send_setassparms(struct qeth_card *, struct qeth_cmd_buffer *, @@ -4101,9 +4332,9 @@ qeth_arp_add_entry(struct qeth_card *card, struct qeth_arp_cache_entry *entry) tmp = rc; qeth_ipaddr4_to_string((u8 *)entry->ipaddr, buf); PRINT_WARN("Could not add ARP entry for address %s on %s: " - "%s (0x%x)\n", + "%s (0x%x/%d)\n", buf, card->info.if_name, - qeth_arp_get_error_cause(&rc), tmp); + qeth_arp_get_error_cause(&rc), tmp, tmp); } return rc; } @@ -4144,9 +4375,9 @@ qeth_arp_remove_entry(struct qeth_card *card, struct qeth_arp_cache_entry *entry memset(buf, 0, 16); qeth_ipaddr4_to_string((u8 *)entry->ipaddr, buf); PRINT_WARN("Could not delete ARP entry for address %s on %s: " - "%s (0x%x)\n", + "%s (0x%x/%d)\n", buf, card->info.if_name, - qeth_arp_get_error_cause(&rc), tmp); + qeth_arp_get_error_cause(&rc), tmp, tmp); } return rc; } @@ -4165,7 +4396,7 @@ qeth_arp_flush_cache(struct qeth_card *card) * funcs flags); since all zeros is no valueable information, * we say EOPNOTSUPP for all ARP functions */ - if (card->info.guestlan) + if (card->info.guestlan || (card->info.type == QETH_CARD_TYPE_IQD)) return -EOPNOTSUPP; if (!qeth_is_supported(card,IPA_ARP_PROCESSING)) { PRINT_WARN("ARP processing not supported " @@ -4176,9 +4407,9 @@ qeth_arp_flush_cache(struct qeth_card *card) IPA_CMD_ASS_ARP_FLUSH_CACHE, 0); if (rc){ tmp = rc; - PRINT_WARN("Could not flush ARP cache on %s: %s (0x%x)\n", + PRINT_WARN("Could not flush ARP cache on %s: %s (0x%x/%d)\n", card->info.if_name, qeth_arp_get_error_cause(&rc), - tmp); + tmp, tmp); } return rc; } @@ -4198,7 +4429,6 @@ qeth_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) return -ENODEV; switch (cmd){ - case SIOCDEVPRIVATE: case SIOC_QETH_ARP_SET_NO_ENTRIES: if (!capable(CAP_NET_ADMIN)){ rc = -EPERM; @@ -4206,7 +4436,6 @@ qeth_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) } rc = qeth_arp_set_no_entries(card, rq->ifr_ifru.ifru_ivalue); break; - case SIOCDEVPRIVATE+1: case SIOC_QETH_ARP_QUERY_INFO: if (!capable(CAP_NET_ADMIN)){ rc = -EPERM; @@ -4214,7 +4443,6 @@ qeth_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) } rc = qeth_arp_query(card, rq->ifr_ifru.ifru_data); break; - case SIOCDEVPRIVATE+2: case SIOC_QETH_ARP_ADD_ENTRY: if (!capable(CAP_NET_ADMIN)){ rc = -EPERM; @@ -4226,7 +4454,6 @@ qeth_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) else rc = qeth_arp_add_entry(card, &arp_entry); break; - case SIOCDEVPRIVATE+3: case SIOC_QETH_ARP_REMOVE_ENTRY: if (!capable(CAP_NET_ADMIN)){ rc = -EPERM; @@ -4238,7 +4465,6 @@ qeth_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) else rc = qeth_arp_remove_entry(card, &arp_entry); break; - case SIOCDEVPRIVATE+4: case SIOC_QETH_ARP_FLUSH_CACHE: if (!capable(CAP_NET_ADMIN)){ rc = -EPERM; @@ -4246,10 +4472,9 @@ qeth_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) } rc = qeth_arp_flush_cache(card); break; - case SIOCDEVPRIVATE+5: case SIOC_QETH_ADP_SET_SNMP_CONTROL: + rc = qeth_snmp_command(card, rq->ifr_ifru.ifru_data); break; - case SIOCDEVPRIVATE+6: case SIOC_QETH_GET_CARD_TYPE: break; case SIOCGMIIPHY: @@ -4282,6 +4507,8 @@ qeth_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) default: rc = -EOPNOTSUPP; } + if (rc) + QETH_DBF_TEXT_(trace, 2, "ioce%d", rc); return rc; } @@ -4325,27 +4552,124 @@ static void qeth_vlan_rx_register(struct net_device *dev, struct vlan_group *grp) { struct qeth_card *card; + unsigned long flags; QETH_DBF_TEXT(trace,4,"vlanreg"); card = (struct qeth_card *) dev->priv; - spin_lock_irq(&card->vlanlock); + spin_lock_irqsave(&card->vlanlock, flags); card->vlangrp = grp; - spin_unlock_irq(&card->vlanlock); + spin_unlock_irqrestore(&card->vlanlock, flags); +} + +static inline void +qeth_free_vlan_buffer(struct qeth_card *card, struct qeth_qdio_out_buffer *buf, + unsigned short vid) +{ + int i; + struct sk_buff *skb; + struct sk_buff_head tmp_list; + + skb_queue_head_init(&tmp_list); + for(i = 0; i < QETH_MAX_BUFFER_ELEMENTS(card); ++i){ + while ((skb = skb_dequeue(&buf->skb_list))){ + if (vlan_tx_tag_present(skb) && + (vlan_tx_tag_get(skb) == vid)) { + atomic_dec(&skb->users); + dev_kfree_skb(skb); + } else + skb_queue_tail(&tmp_list, skb); + } + } + while ((skb = skb_dequeue(&tmp_list))) + skb_queue_tail(&buf->skb_list, skb); +} + +static void +qeth_free_vlan_skbs(struct qeth_card *card, unsigned short vid) +{ + int i, j; + + QETH_DBF_TEXT(trace, 4, "frvlskbs"); + for (i = 0; i < card->qdio.no_out_queues; ++i){ + for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j) + qeth_free_vlan_buffer(card, &card->qdio. + out_qs[i]->bufs[j], vid); + } +} + +static void +qeth_free_vlan_addresses4(struct qeth_card *card, unsigned short vid) +{ + struct in_device *in_dev; + struct in_ifaddr *ifa; + struct qeth_ipaddr *addr; + + QETH_DBF_TEXT(trace, 4, "frvaddr4"); + if (!card->vlangrp) + return; + in_dev = in_dev_get(card->vlangrp->vlan_devices[vid]); + if (!in_dev) + return; + for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next){ + addr = qeth_get_addr_buffer(QETH_PROT_IPV4); + if (addr){ + addr->u.a4.addr = ifa->ifa_address; + addr->u.a4.mask = ifa->ifa_mask; + addr->type = QETH_IP_TYPE_NORMAL; + if (!qeth_delete_ip(card, addr)) + kfree(addr); + } + } + in_dev_put(in_dev); +} + +static void +qeth_free_vlan_addresses6(struct qeth_card *card, unsigned short vid) +{ + struct inet6_dev *in6_dev; + struct inet6_ifaddr *ifa; + struct qeth_ipaddr *addr; + + QETH_DBF_TEXT(trace, 4, "frvaddr6"); + if (!card->vlangrp) + return; + in6_dev = in6_dev_get(card->vlangrp->vlan_devices[vid]); + if (!in6_dev) + return; + for (ifa = in6_dev->addr_list; ifa; ifa = ifa->lst_next){ + addr = qeth_get_addr_buffer(QETH_PROT_IPV6); + if (addr){ + memcpy(&addr->u.a6.addr, &ifa->addr, + sizeof(struct in6_addr)); + addr->u.a6.pfxlen = ifa->prefix_len; + addr->type = QETH_IP_TYPE_NORMAL; + if (!qeth_delete_ip(card, addr)) + kfree(addr); + } + } + in6_dev_put(in6_dev); } static void qeth_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) { struct qeth_card *card; + unsigned long flags; QETH_DBF_TEXT(trace,4,"vlkilvid"); card = (struct qeth_card *) dev->priv; - spin_lock_irq(&card->vlanlock); + /* free all skbs for the vlan device */ + qeth_free_vlan_skbs(card, vid); + spin_lock_irqsave(&card->vlanlock, flags); + /* unregister IP addresses of vlan device */ + qeth_free_vlan_addresses4(card, vid); + qeth_free_vlan_addresses6(card, vid); if (card->vlangrp) card->vlangrp->vlan_devices[vid] = NULL; - spin_unlock_irq(&card->vlanlock); + spin_unlock_irqrestore(&card->vlanlock, flags); + qeth_set_thread_start_bit(card, QETH_SET_IP_THREAD); /* delete mc addresses for this vlan dev */ qeth_set_thread_start_bit(card, QETH_SET_MC_THREAD); schedule_work(&card->kernel_thread_starter); @@ -4401,10 +4725,6 @@ qeth_get_addr_buffer(enum qeth_prot_versions prot) memset(addr,0,sizeof(struct qeth_ipaddr)); addr->type = QETH_IP_TYPE_NORMAL; addr->proto = prot; - addr->is_multicast = 0; - addr->users = 0; - addr->set_flags = 0; - addr->del_flags = 0; return addr; } @@ -4811,6 +5131,26 @@ qeth_netdev_init(struct net_device *dev) return 0; } +static void +qeth_init_func_level(struct qeth_card *card) +{ + if (card->ipato.enabled) { + if (card->info.type == QETH_CARD_TYPE_IQD) + card->info.func_level = + QETH_IDX_FUNC_LEVEL_IQD_ENA_IPAT; + else + card->info.func_level = + QETH_IDX_FUNC_LEVEL_OSAE_ENA_IPAT; + } else { + if (card->info.type == QETH_CARD_TYPE_IQD) + card->info.func_level = + QETH_IDX_FUNC_LEVEL_IQD_DIS_IPAT; + else + card->info.func_level = + QETH_IDX_FUNC_LEVEL_OSAE_DIS_IPAT; + } +} + /** * hardsetup card, initialize MPC and QDIO stuff */ @@ -4848,6 +5188,7 @@ retry: return rc; } qeth_init_tokens(card); + qeth_init_func_level(card); rc = qeth_idx_activate_channel(&card->read, qeth_idx_read_cb); if (rc == -ERESTARTSYS) { QETH_DBF_TEXT(setup, 2, "break2"); @@ -4885,7 +5226,6 @@ retry: QETH_DBF_TEXT_(setup, 2, "6err%d", rc); goto out; } - qeth_set_device_name(card); card->dev->priv = card; card->dev->type = qeth_get_arphdr_type(card->info.type, card->info.link_type); @@ -4897,24 +5237,6 @@ out: return rc; } -static struct qeth_cmd_buffer * -qeth_get_adapter_cmd(struct qeth_card *card, __u32 command) -{ - struct qeth_cmd_buffer *iob; - struct qeth_ipa_cmd *cmd; - - iob = qeth_get_ipacmd_buffer(card,IPA_CMD_SETADAPTERPARMS, - QETH_PROT_IPV4); - cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); - cmd->data.setadapterparms.cmdlength = - sizeof(struct qeth_ipacmd_setadpparms); - cmd->data.setadapterparms.command_code = command; - cmd->data.setadapterparms.frames_used_total = 1; - cmd->data.setadapterparms.frame_seq_no = 1; - - return iob; -} - static int qeth_default_setassparms_cb(struct qeth_card *card, struct qeth_reply *reply, unsigned long data) @@ -4952,7 +5274,7 @@ qeth_default_setadapterparms_cb(struct qeth_card *card, cmd = (struct qeth_ipa_cmd *) data; if (cmd->hdr.return_code == 0) - cmd->hdr.return_code = cmd->data.setadapterparms.return_code; + cmd->hdr.return_code = cmd->data.setadapterparms.hdr.return_code; return 0; } @@ -4980,7 +5302,8 @@ qeth_query_setadapterparms(struct qeth_card *card) struct qeth_cmd_buffer *iob; QETH_DBF_TEXT(trace,3,"queryadp"); - iob = qeth_get_adapter_cmd(card,IPA_SETADP_QUERY_COMMANDS_SUPPORTED); + iob = qeth_get_adapter_cmd(card, IPA_SETADP_QUERY_COMMANDS_SUPPORTED, + sizeof(struct qeth_ipacmd_setadpparms)); rc = qeth_send_ipa_cmd(card, iob, qeth_query_setadapterparms_cb, NULL); return rc; } @@ -5010,7 +5333,8 @@ qeth_setadpparms_change_macaddr(struct qeth_card *card) QETH_DBF_TEXT(trace,4,"chgmac"); - iob = qeth_get_adapter_cmd(card,IPA_SETADP_ALTER_MAC_ADDRESS); + iob = qeth_get_adapter_cmd(card,IPA_SETADP_ALTER_MAC_ADDRESS, + sizeof(struct qeth_ipacmd_setadpparms)); cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); cmd->data.setadapterparms.data.change_addr.cmd = CHANGE_ADDR_READ_MAC; cmd->data.setadapterparms.data.change_addr.addr_size = OSA_ADDR_LEN; @@ -5030,7 +5354,8 @@ qeth_send_setadp_mode(struct qeth_card *card, __u32 command, __u32 mode) QETH_DBF_TEXT(trace,4,"adpmode"); - iob = qeth_get_adapter_cmd(card, command); + iob = qeth_get_adapter_cmd(card, command, + sizeof(struct qeth_ipacmd_setadpparms)); cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); cmd->data.setadapterparms.data.mode = mode; rc = qeth_send_ipa_cmd(card, iob, qeth_default_setadapterparms_cb, @@ -5459,18 +5784,20 @@ qeth_start_ipa_broadcast(struct qeth_card *card) int rc; QETH_DBF_TEXT(trace,3,"stbrdcst"); + card->info.broadcast_capable = 0; if (!qeth_is_supported(card, IPA_FILTERING)) { PRINT_WARN("Broadcast not supported on %s\n", card->info.if_name); - return -EOPNOTSUPP; + rc = -EOPNOTSUPP; + goto out; } rc = qeth_send_simple_setassparms(card, IPA_FILTERING, IPA_CMD_ASS_START, 0); if (rc) { - PRINT_WARN("Could not enable broadcasting " + PRINT_WARN("Could not enable broadcasting filtering " "on %s: 0x%x\n", card->info.if_name, rc); - return rc; + goto out; } rc = qeth_send_simple_setassparms(card, IPA_FILTERING, @@ -5478,12 +5805,24 @@ qeth_start_ipa_broadcast(struct qeth_card *card) if (rc) { PRINT_WARN("Could not set up broadcast filtering on %s: 0x%x\n", card->info.if_name, rc); - return rc; + goto out; } + card->info.broadcast_capable = QETH_BROADCAST_WITH_ECHO; PRINT_INFO("Broadcast enabled \n"); - card->dev->flags |= IFF_BROADCAST; - card->info.broadcast_capable = 1; - return 0; + rc = qeth_send_simple_setassparms(card, IPA_FILTERING, + IPA_CMD_ASS_ENABLE, 1); + if (rc) { + PRINT_WARN("Could not set up broadcast echo filtering on " + "%s: 0x%x\n", card->info.if_name, rc); + goto out; + } + card->info.broadcast_capable = QETH_BROADCAST_WITHOUT_ECHO; +out: + if (card->info.broadcast_capable) + card->dev->flags |= IFF_BROADCAST; + else + card->dev->flags &= ~IFF_BROADCAST; + return rc; } static int @@ -5781,7 +6120,6 @@ static void qeth_clear_ip_list(struct qeth_card *card, int clean, int recover) { struct qeth_ipaddr *addr, *tmp; - int first_run = 1; unsigned long flags; QETH_DBF_TEXT(trace,4,"clearip"); @@ -5791,29 +6129,21 @@ qeth_clear_ip_list(struct qeth_card *card, int clean, int recover) list_del(&addr->entry); kfree(addr); } -again: - if (first_run) - first_run = 0; - else - spin_lock_irqsave(&card->ip_lock, flags); - list_for_each_entry_safe(addr, tmp, &card->ip_list, entry) { + while (!list_empty(&card->ip_list)) { + addr = list_entry(card->ip_list.next, + struct qeth_ipaddr, entry); list_del_init(&addr->entry); - if (clean){ + if (clean) { spin_unlock_irqrestore(&card->ip_lock, flags); qeth_deregister_addr_entry(card, addr); + spin_lock_irqsave(&card->ip_lock, flags); } - if (!recover || addr->is_multicast) + if (!recover || addr->is_multicast) { kfree(addr); - else { - if (clean) - spin_lock_irqsave(&card->ip_lock, flags); - list_add_tail(&addr->entry, &card->ip_tbd_list); - if (clean) { - spin_unlock_irqrestore(&card->ip_lock, flags); - goto again; - } + continue; } + list_add_tail(&addr->entry, &card->ip_tbd_list); } spin_unlock_irqrestore(&card->ip_lock, flags); } @@ -6109,7 +6439,7 @@ qeth_set_online(struct ccwgroup_device *gdev) qeth_set_allowed_threads(card, 0xffffffff, 0); if (recover_flag == CARD_STATE_RECOVER) qeth_start_again(card); - + qeth_notify_processes(); return 0; out_remove: card->use_hard_stop = 1; @@ -6775,12 +7105,18 @@ qeth_init(void) int rc=0; qeth_eyecatcher(); - printk(KERN_INFO "qeth: loading %s\n",version); + PRINT_INFO("loading %s (%s/%s/%s/%s/%s/%s/%s %s %s)\n", + version, VERSION_QETH_C, VERSION_QETH_H, + VERSION_QETH_MPC_H, VERSION_QETH_MPC_C, + VERSION_QETH_FS_H, VERSION_QETH_PROC_C, + VERSION_QETH_SYS_C, QETH_VERSION_IPV6, + QETH_VERSION_VLAN); INIT_LIST_HEAD(&qeth_card_list.list); + INIT_LIST_HEAD(&qeth_notify_list); + spin_lock_init(&qeth_notify_lock); rwlock_init(&qeth_card_list.rwlock); - atomic_set(&qeth_hsi_count, 0); if (qeth_register_dbf_views()) goto out_err; if (qeth_sysfs_register())