diff options
Diffstat (limited to 'drivers/s390/net/qeth_core_main.c')
| -rw-r--r-- | drivers/s390/net/qeth_core_main.c | 1141 |
1 files changed, 761 insertions, 380 deletions
diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 9c3f38da4c0..f54bec54d67 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -1,6 +1,4 @@ /* - * drivers/s390/net/qeth_core_main.c - * * Copyright IBM Corp. 2007, 2009 * Author(s): Utz Bacher <utz.bacher@de.ibm.com>, * Frank Pavlic <fpavlic@de.ibm.com>, @@ -22,10 +20,13 @@ #include <linux/kthread.h> #include <linux/slab.h> #include <net/iucv/af_iucv.h> +#include <net/dsfield.h> #include <asm/ebcdic.h> +#include <asm/chpid.h> #include <asm/io.h> #include <asm/sysinfo.h> +#include <asm/compat.h> #include "qeth_core.h" @@ -34,8 +35,8 @@ struct qeth_dbf_info qeth_dbf[QETH_DBF_INFOS] = { /* N P A M L V H */ [QETH_DBF_SETUP] = {"qeth_setup", 8, 1, 8, 5, &debug_hex_ascii_view, NULL}, - [QETH_DBF_MSG] = {"qeth_msg", - 8, 1, 128, 3, &debug_sprintf_view, NULL}, + [QETH_DBF_MSG] = {"qeth_msg", 8, 1, 11 * sizeof(long), 3, + &debug_sprintf_view, NULL}, [QETH_DBF_CTRL] = {"qeth_control", 8, 1, QETH_DBF_CTRL_LEN, 5, &debug_hex_ascii_view, NULL}, }; @@ -50,6 +51,7 @@ static struct kmem_cache *qeth_qdio_outbuf_cache; static struct device *qeth_core_root_dev; static unsigned int known_devices[][6] = QETH_MODELLIST_ARRAY; static struct lock_class_key qdio_out_skb_queue_key; +static struct mutex qeth_mod_mutex; static void qeth_send_control_data_cb(struct qeth_channel *, struct qeth_cmd_buffer *); @@ -68,18 +70,40 @@ static void qeth_clear_output_buffer(struct qeth_qdio_out_q *queue, enum qeth_qdio_buffer_states newbufstate); static int qeth_init_qdio_out_buf(struct qeth_qdio_out_q *, int); +struct workqueue_struct *qeth_wq; +EXPORT_SYMBOL_GPL(qeth_wq); + +static void qeth_close_dev_handler(struct work_struct *work) +{ + struct qeth_card *card; + + card = container_of(work, struct qeth_card, close_dev_work); + QETH_CARD_TEXT(card, 2, "cldevhdl"); + rtnl_lock(); + dev_close(card->dev); + rtnl_unlock(); + ccwgroup_set_offline(card->gdev); +} + +void qeth_close_dev(struct qeth_card *card) +{ + QETH_CARD_TEXT(card, 2, "cldevsubm"); + queue_work(qeth_wq, &card->close_dev_work); +} +EXPORT_SYMBOL_GPL(qeth_close_dev); + static inline const char *qeth_get_cardname(struct qeth_card *card) { if (card->info.guestlan) { switch (card->info.type) { case QETH_CARD_TYPE_OSD: - return " Guest LAN QDIO"; + return " Virtual NIC QDIO"; case QETH_CARD_TYPE_IQD: - return " Guest LAN Hiper"; + return " Virtual NIC Hiper"; case QETH_CARD_TYPE_OSM: - return " Guest LAN QDIO - OSM"; + return " Virtual NIC QDIO - OSM"; case QETH_CARD_TYPE_OSX: - return " Guest LAN QDIO - OSX"; + return " Virtual NIC QDIO - OSX"; default: return " unknown"; } @@ -108,13 +132,13 @@ const char *qeth_get_cardname_short(struct qeth_card *card) if (card->info.guestlan) { switch (card->info.type) { case QETH_CARD_TYPE_OSD: - return "GuestLAN QDIO"; + return "Virt.NIC QDIO"; case QETH_CARD_TYPE_IQD: - return "GuestLAN Hiper"; + return "Virt.NIC Hiper"; case QETH_CARD_TYPE_OSM: - return "GuestLAN OSM"; + return "Virt.NIC OSM"; case QETH_CARD_TYPE_OSX: - return "GuestLAN OSX"; + return "Virt.NIC OSX"; default: return "unknown"; } @@ -156,6 +180,23 @@ const char *qeth_get_cardname_short(struct qeth_card *card) return "n/a"; } +void qeth_set_recovery_task(struct qeth_card *card) +{ + card->recovery_task = current; +} +EXPORT_SYMBOL_GPL(qeth_set_recovery_task); + +void qeth_clear_recovery_task(struct qeth_card *card) +{ + card->recovery_task = NULL; +} +EXPORT_SYMBOL_GPL(qeth_clear_recovery_task); + +static bool qeth_is_recovery_task(const struct qeth_card *card) +{ + return card->recovery_task == current; +} + void qeth_set_allowed_threads(struct qeth_card *card, unsigned long threads, int clear_start_mask) { @@ -184,6 +225,8 @@ EXPORT_SYMBOL_GPL(qeth_threads_running); int qeth_wait_for_threads(struct qeth_card *card, unsigned long threads) { + if (qeth_is_recovery_task(card)) + return 0; return wait_event_interruptible(card->wait_q, qeth_threads_running(card, threads) == 0); } @@ -295,7 +338,7 @@ static inline int qeth_alloc_cq(struct qeth_card *card) card->qdio.no_in_queues = 2; - card->qdio.out_bufstates = (struct qdio_outbuf_state *) + card->qdio.out_bufstates = kzalloc(card->qdio.no_out_queues * QDIO_MAX_BUFFERS_PER_Q * sizeof(struct qdio_outbuf_state), GFP_KERNEL); @@ -383,7 +426,7 @@ static inline void qeth_cleanup_handled_pending(struct qeth_qdio_out_q *q, qeth_release_skbs(c); c = f->next_pending; - BUG_ON(head->next_pending != f); + WARN_ON_ONCE(head->next_pending != f); head->next_pending = c; kmem_cache_free(qeth_qdio_outbuf_cache, f); } else { @@ -415,13 +458,12 @@ static inline void qeth_qdio_handle_aob(struct qeth_card *card, buffer = (struct qeth_qdio_out_buffer *) aob->user1; QETH_CARD_TEXT_(card, 5, "%lx", aob->user1); - BUG_ON(buffer == NULL); - if (atomic_cmpxchg(&buffer->state, QETH_QDIO_BUF_PRIMED, QETH_QDIO_BUF_IN_CQ) == QETH_QDIO_BUF_PRIMED) { notification = TX_NOTIFY_OK; } else { - BUG_ON(atomic_read(&buffer->state) != QETH_QDIO_BUF_PENDING); + WARN_ON_ONCE(atomic_read(&buffer->state) != + QETH_QDIO_BUF_PENDING); atomic_set(&buffer->state, QETH_QDIO_BUF_IN_CQ); notification = TX_NOTIFY_DELAYED_OK; } @@ -489,7 +531,7 @@ static struct qeth_reply *qeth_alloc_reply(struct qeth_card *card) atomic_set(&reply->refcnt, 1); atomic_set(&reply->received, 0); reply->card = card; - }; + } return reply; } @@ -543,11 +585,23 @@ static struct qeth_ipa_cmd *qeth_check_ipa_data(struct qeth_card *card, } else { switch (cmd->hdr.command) { case IPA_CMD_STOPLAN: - dev_warn(&card->gdev->dev, + if (cmd->hdr.return_code == + IPA_RC_VEPA_TO_VEB_TRANSITION) { + dev_err(&card->gdev->dev, + "Interface %s is down because the " + "adjacent port is no longer in " + "reflective relay mode\n", + QETH_CARD_IFNAME(card)); + qeth_close_dev(card); + } else { + dev_warn(&card->gdev->dev, "The link for interface %s on CHPID" " 0x%X failed\n", QETH_CARD_IFNAME(card), card->info.chpid); + qeth_issue_ipa_msg(cmd, + cmd->hdr.return_code, card); + } card->lan_online = 0; if (card->dev && netif_carrier_ok(card->dev)) netif_carrier_off(card->dev); @@ -564,6 +618,13 @@ static struct qeth_ipa_cmd *qeth_check_ipa_data(struct qeth_card *card, card->info.hwtrap = 2; qeth_schedule_recovery(card); return NULL; + case IPA_CMD_SETBRIDGEPORT: + case IPA_CMD_ADDRESS_CHANGE_NOTIF: + if (card->discipline->control_event_handler + (card, cmd)) + return cmd; + else + return NULL; case IPA_CMD_MODCCID: return cmd; case IPA_CMD_REGISTER_LOCAL_ADDR: @@ -677,6 +738,7 @@ void qeth_release_buffer(struct qeth_channel *channel, iob->callback = qeth_send_control_data_cb; iob->rc = 0; spin_unlock_irqrestore(&channel->iob_lock, flags); + wake_up(&channel->wait_q); } EXPORT_SYMBOL_GPL(qeth_release_buffer); @@ -952,7 +1014,7 @@ static long __qeth_check_irb_error(struct ccw_device *cdev, card = CARD_FROM_CDEV(cdev); - if (!IS_ERR(irb)) + if (!card || !IS_ERR(irb)) return 0; switch (PTR_ERR(irb)) { @@ -968,7 +1030,7 @@ static long __qeth_check_irb_error(struct ccw_device *cdev, QETH_CARD_TEXT(card, 2, "ckirberr"); QETH_CARD_TEXT_(card, 2, " rc%d", -ETIMEDOUT); if (intparm == QETH_RCD_PARM) { - if (card && (card->data.ccwdev == cdev)) { + if (card->data.ccwdev == cdev) { card->data.state = CH_STATE_DOWN; wake_up(&card->wait_q); } @@ -1130,7 +1192,7 @@ static void qeth_release_skbs(struct qeth_qdio_out_buffer *buf) notify_general_error = 1; /* release may never happen from within CQ tasklet scope */ - BUG_ON(atomic_read(&buf->state) == QETH_QDIO_BUF_IN_CQ); + WARN_ON_ONCE(atomic_read(&buf->state) == QETH_QDIO_BUF_IN_CQ); skb = skb_dequeue(&buf->skb_list); while (skb) { @@ -1230,8 +1292,10 @@ static void qeth_free_qdio_buffers(struct qeth_card *card) qeth_free_cq(card); cancel_delayed_work_sync(&card->buffer_reclaim_work); - for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j) - dev_kfree_skb_any(card->qdio.in_q->bufs[j].rx_skb); + for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j) { + if (card->qdio.in_q->bufs[j].rx_skb) + dev_kfree_skb_any(card->qdio.in_q->bufs[j].rx_skb); + } kfree(card->qdio.in_q); card->qdio.in_q = NULL; /* inbound buffer pool */ @@ -1256,55 +1320,54 @@ static void qeth_clean_channel(struct qeth_channel *channel) kfree(channel->iob[cnt].data); } -static void qeth_get_channel_path_desc(struct qeth_card *card) +static void qeth_set_single_write_queues(struct qeth_card *card) +{ + if ((atomic_read(&card->qdio.state) != QETH_QDIO_UNINITIALIZED) && + (card->qdio.no_out_queues == 4)) + qeth_free_qdio_buffers(card); + + card->qdio.no_out_queues = 1; + if (card->qdio.default_out_queue != 0) + dev_info(&card->gdev->dev, "Priority Queueing not supported\n"); + + card->qdio.default_out_queue = 0; +} + +static void qeth_set_multiple_write_queues(struct qeth_card *card) +{ + if ((atomic_read(&card->qdio.state) != QETH_QDIO_UNINITIALIZED) && + (card->qdio.no_out_queues == 1)) { + qeth_free_qdio_buffers(card); + card->qdio.default_out_queue = 2; + } + card->qdio.no_out_queues = 4; +} + +static void qeth_update_from_chp_desc(struct qeth_card *card) { 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; + struct channel_path_desc *chp_dsc; QETH_DBF_TEXT(SETUP, 2, "chp_desc"); ccwdev = card->data.ccwdev; - chp_dsc = (struct channelPath_dsc *)ccw_device_get_chp_desc(ccwdev, 0); - if (chp_dsc != NULL) { - if (card->info.type != QETH_CARD_TYPE_IQD) { - /* CHPP field bit 6 == 1 -> single queue */ - if ((chp_dsc->chpp & 0x02) == 0x02) { - if ((atomic_read(&card->qdio.state) != - QETH_QDIO_UNINITIALIZED) && - (card->qdio.no_out_queues == 4)) - /* change from 4 to 1 outbound queues */ - qeth_free_qdio_buffers(card); - card->qdio.no_out_queues = 1; - if (card->qdio.default_out_queue != 0) - dev_info(&card->gdev->dev, - "Priority Queueing not supported\n"); - card->qdio.default_out_queue = 0; - } else { - if ((atomic_read(&card->qdio.state) != - QETH_QDIO_UNINITIALIZED) && - (card->qdio.no_out_queues == 1)) { - /* change from 1 to 4 outbound queues */ - qeth_free_qdio_buffers(card); - card->qdio.default_out_queue = 2; - } - card->qdio.no_out_queues = 4; - } - } - card->info.func_level = 0x4100 + chp_dsc->desc; - kfree(chp_dsc); - } + chp_dsc = ccw_device_get_chp_desc(ccwdev, 0); + if (!chp_dsc) + goto out; + + card->info.func_level = 0x4100 + chp_dsc->desc; + if (card->info.type == QETH_CARD_TYPE_IQD) + goto out; + + /* CHPP field bit 6 == 1 -> single queue */ + if ((chp_dsc->chpp & 0x02) == 0x02) + qeth_set_single_write_queues(card); + else + qeth_set_multiple_write_queues(card); +out: + kfree(chp_dsc); QETH_DBF_TEXT_(SETUP, 2, "nr:%x", card->qdio.no_out_queues); QETH_DBF_TEXT_(SETUP, 2, "lvl:%02x", card->info.func_level); - return; } static void qeth_init_qdio_info(struct qeth_card *card) @@ -1326,8 +1389,6 @@ static void qeth_set_intial_options(struct qeth_card *card) { card->options.route4.type = NO_ROUTER; card->options.route6.type = NO_ROUTER; - 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.performance_stats = 0; @@ -1362,7 +1423,7 @@ static void qeth_start_kernel_thread(struct work_struct *work) card->write.state != CH_STATE_UP) return; if (qeth_do_start_thread(card, QETH_RECOVER_THREAD)) { - ts = kthread_run(card->discipline.recover, (void *)card, + ts = kthread_run(card->discipline->recover, (void *)card, "qeth_recover"); if (IS_ERR(ts)) { qeth_clear_thread_start_bit(card, QETH_RECOVER_THREAD); @@ -1410,6 +1471,7 @@ static int qeth_setup_card(struct qeth_card *card) /* init QDIO stuff */ qeth_init_qdio_info(card); INIT_DELAYED_WORK(&card->buffer_reclaim_work, qeth_buffer_reclaim_work); + INIT_WORK(&card->close_dev_work, qeth_close_dev_handler); return 0; } @@ -1474,7 +1536,7 @@ static int qeth_determine_card_type(struct qeth_card *card) card->qdio.no_in_queues = 1; card->info.is_multicast_different = known_devices[i][QETH_MULTICAST_IND]; - qeth_get_channel_path_desc(card); + qeth_update_from_chp_desc(card); return 0; } i++; @@ -1591,7 +1653,6 @@ int qeth_qdio_clear_card(struct qeth_card *card, int use_halt) QDIO_FLAG_CLEANUP_USING_CLEAR); if (rc) QETH_CARD_TEXT_(card, 3, "1err%d", rc); - qdio_free(CARD_DDEV(card)); atomic_set(&card->qdio.state, QETH_QDIO_ALLOCATED); break; case QETH_QDIO_CLEANING: @@ -1669,14 +1730,15 @@ static void qeth_configure_blkt_default(struct qeth_card *card, char *prcd) { QETH_DBF_TEXT(SETUP, 2, "cfgblkt"); - if (prcd[74] == 0xF0 && prcd[75] == 0xF0 && prcd[76] == 0xF5) { - card->info.blkt.time_total = 250; - card->info.blkt.inter_packet = 5; - card->info.blkt.inter_packet_jumbo = 15; - } else { + if (prcd[74] == 0xF0 && prcd[75] == 0xF0 && + prcd[76] >= 0xF1 && prcd[76] <= 0xF4) { card->info.blkt.time_total = 0; card->info.blkt.inter_packet = 0; card->info.blkt.inter_packet_jumbo = 0; + } else { + card->info.blkt.time_total = 250; + card->info.blkt.inter_packet = 5; + card->info.blkt.inter_packet_jumbo = 15; } } @@ -2029,7 +2091,7 @@ int qeth_send_control_data(struct qeth_card *card, int len, if (time_after(jiffies, timeout)) goto time_err; cpu_relax(); - }; + } } if (reply->rc == -EIO) @@ -2138,11 +2200,11 @@ static inline int qeth_get_initial_mtu_for_card(struct qeth_card *card) case QETH_LINK_TYPE_LANE_TR: return 2000; default: - return 1492; + return card->options.layer2 ? 1500 : 1492; } case QETH_CARD_TYPE_OSM: case QETH_CARD_TYPE_OSX: - return 1492; + return card->options.layer2 ? 1500 : 1492; default: return 1500; } @@ -2215,9 +2277,10 @@ static int qeth_ulp_enable_cb(struct qeth_card *card, struct qeth_reply *reply, card->info.max_mtu = mtu; card->qdio.in_buf_size = mtu + 2 * PAGE_SIZE; } else { - card->info.initial_mtu = qeth_get_initial_mtu_for_card(card); card->info.max_mtu = *(__u16 *)QETH_ULP_ENABLE_RESP_MAX_MTU( iob->data); + card->info.initial_mtu = min(card->info.max_mtu, + qeth_get_initial_mtu_for_card(card)); card->qdio.in_buf_size = QETH_IN_BUF_SIZE_DEFAULT; } @@ -2272,7 +2335,6 @@ static int qeth_ulp_setup_cb(struct qeth_card *card, struct qeth_reply *reply, unsigned long data) { struct qeth_cmd_buffer *iob; - int rc = 0; QETH_DBF_TEXT(SETUP, 2, "ulpstpcb"); @@ -2288,7 +2350,7 @@ static int qeth_ulp_setup_cb(struct qeth_card *card, struct qeth_reply *reply, iob->rc = -EMLINK; } QETH_DBF_TEXT_(SETUP, 2, " rc%d", iob->rc); - return rc; + return 0; } static int qeth_ulp_setup(struct qeth_card *card) @@ -2393,7 +2455,7 @@ static int qeth_alloc_qdio_buffers(struct qeth_card *card) card->qdio.out_qs[i]->queue_no = i; /* give outbound qeth_qdio_buffers their qdio_buffers */ for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j) { - BUG_ON(card->qdio.out_qs[i]->bufs[j] != NULL); + WARN_ON(card->qdio.out_qs[i]->bufs[j] != NULL); if (qeth_init_qdio_out_buf(card->qdio.out_qs[i], j)) goto out_freeoutqbufs; } @@ -2535,6 +2597,7 @@ static int qeth_mpc_initialize(struct qeth_card *card) return 0; out_qdio: qeth_qdio_clear_card(card, card->info.type != QETH_CARD_TYPE_IQD); + qdio_free(CARD_DDEV(card)); return rc; } @@ -2862,7 +2925,7 @@ int qeth_send_startlan(struct qeth_card *card) } EXPORT_SYMBOL_GPL(qeth_send_startlan); -int qeth_default_setadapterparms_cb(struct qeth_card *card, +static int qeth_default_setadapterparms_cb(struct qeth_card *card, struct qeth_reply *reply, unsigned long data) { struct qeth_ipa_cmd *cmd; @@ -2875,7 +2938,6 @@ int qeth_default_setadapterparms_cb(struct qeth_card *card, cmd->data.setadapterparms.hdr.return_code; return 0; } -EXPORT_SYMBOL_GPL(qeth_default_setadapterparms_cb); static int qeth_query_setadapterparms_cb(struct qeth_card *card, struct qeth_reply *reply, unsigned long data) @@ -2895,7 +2957,7 @@ static int qeth_query_setadapterparms_cb(struct qeth_card *card, return qeth_default_setadapterparms_cb(card, reply, (unsigned long)cmd); } -struct qeth_cmd_buffer *qeth_get_adapter_cmd(struct qeth_card *card, +static struct qeth_cmd_buffer *qeth_get_adapter_cmd(struct qeth_card *card, __u32 command, __u32 cmdlen) { struct qeth_cmd_buffer *iob; @@ -2911,7 +2973,6 @@ struct qeth_cmd_buffer *qeth_get_adapter_cmd(struct qeth_card *card, return iob; } -EXPORT_SYMBOL_GPL(qeth_get_adapter_cmd); int qeth_query_setadapterparms(struct qeth_card *card) { @@ -2934,16 +2995,33 @@ static int qeth_query_ipassists_cb(struct qeth_card *card, QETH_DBF_TEXT(SETUP, 2, "qipasscb"); cmd = (struct qeth_ipa_cmd *) data; + + switch (cmd->hdr.return_code) { + case IPA_RC_NOTSUPP: + case IPA_RC_L2_UNSUPPORTED_CMD: + QETH_DBF_TEXT(SETUP, 2, "ipaunsup"); + card->options.ipa4.supported_funcs |= IPA_SETADAPTERPARMS; + card->options.ipa6.supported_funcs |= IPA_SETADAPTERPARMS; + return -0; + default: + if (cmd->hdr.return_code) { + QETH_DBF_MESSAGE(1, "%s IPA_CMD_QIPASSIST: Unhandled " + "rc=%d\n", + dev_name(&card->gdev->dev), + cmd->hdr.return_code); + return 0; + } + } + if (cmd->hdr.prot_version == QETH_PROT_IPV4) { card->options.ipa4.supported_funcs = cmd->hdr.ipa_supported; card->options.ipa4.enabled_funcs = cmd->hdr.ipa_enabled; - } else { + } else if (cmd->hdr.prot_version == QETH_PROT_IPV6) { card->options.ipa6.supported_funcs = cmd->hdr.ipa_supported; card->options.ipa6.enabled_funcs = cmd->hdr.ipa_enabled; - } - QETH_DBF_TEXT(SETUP, 2, "suppenbl"); - QETH_DBF_TEXT_(SETUP, 2, "%x", cmd->hdr.ipa_supported); - QETH_DBF_TEXT_(SETUP, 2, "%x", cmd->hdr.ipa_enabled); + } else + QETH_DBF_MESSAGE(1, "%s IPA_CMD_QIPASSIST: Flawed LIC detected" + "\n", dev_name(&card->gdev->dev)); return 0; } @@ -2993,7 +3071,7 @@ static void qeth_get_trap_id(struct qeth_card *card, struct qeth_trap_id *tid) struct sysinfo_2_2_2 *info222 = (struct sysinfo_2_2_2 *)info; struct sysinfo_3_2_2 *info322 = (struct sysinfo_3_2_2 *)info; struct ccw_dev_id ccwid; - int level, rc; + int level; tid->chpid = card->info.chpid; ccw_device_get_id(CARD_RDEV(card), &ccwid); @@ -3001,17 +3079,10 @@ static void qeth_get_trap_id(struct qeth_card *card, struct qeth_trap_id *tid) tid->devno = ccwid.devno; if (!info) return; - - rc = stsi(NULL, 0, 0, 0); - if (rc == -ENOSYS) - level = rc; - else - level = (((unsigned int) rc) >> 28); - - if ((level >= 2) && (stsi(info222, 2, 2, 2) != -ENOSYS)) + level = stsi(NULL, 0, 0, 0); + if ((level >= 2) && (stsi(info222, 2, 2, 2) == 0)) tid->lparnr = info222->lpar_number; - - if ((level >= 3) && (stsi(info322, 3, 2, 2) != -ENOSYS)) { + if ((level >= 3) && (stsi(info322, 3, 2, 2) == 0)) { EBCASC(info322->vm[0].name, sizeof(info322->vm[0].name)); memcpy(tid->vmname, info322->vm[0].name, sizeof(tid->vmname)); } @@ -3335,7 +3406,7 @@ static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int index, if (rc) { queue->card->stats.tx_errors += count; /* ignore temporary SIGA errors without busy condition */ - if (rc == QDIO_ERROR_SIGA_TARGET) + if (rc == -ENOBUFS) return; QETH_CARD_TEXT(queue->card, 2, "flushbuf"); QETH_CARD_TEXT_(queue->card, 2, " q%d", queue->queue_no); @@ -3529,7 +3600,7 @@ void qeth_qdio_output_handler(struct ccw_device *ccwdev, int i; QETH_CARD_TEXT(card, 6, "qdouhdl"); - if (qdio_error & QDIO_ERROR_ACTIVATE_CHECK_CONDITION) { + if (qdio_error & QDIO_ERROR_FATAL) { QETH_CARD_TEXT(card, 2, "achkcond"); netif_stop_queue(card->dev); qeth_schedule_recovery(card); @@ -3548,7 +3619,7 @@ void qeth_qdio_output_handler(struct ccw_device *ccwdev, if (queue->bufstates && (queue->bufstates[bidx].flags & QDIO_OUTBUF_STATE_FLAG_PENDING) != 0) { - BUG_ON(card->options.cq != QETH_CQ_ENABLED); + WARN_ON_ONCE(card->options.cq != QETH_CQ_ENABLED); if (atomic_cmpxchg(&buffer->state, QETH_QDIO_BUF_PRIMED, @@ -3562,7 +3633,6 @@ void qeth_qdio_output_handler(struct ccw_device *ccwdev, QETH_CARD_TEXT(queue->card, 5, "aob"); QETH_CARD_TEXT_(queue->card, 5, "%lx", virt_to_phys(buffer->aob)); - BUG_ON(bidx < 0 || bidx >= QDIO_MAX_BUFFERS_PER_Q); if (qeth_init_qdio_out_buf(queue, bidx)) { QETH_CARD_TEXT(card, 2, "outofbuf"); qeth_schedule_recovery(card); @@ -3593,53 +3663,87 @@ void qeth_qdio_output_handler(struct ccw_device *ccwdev, } EXPORT_SYMBOL_GPL(qeth_qdio_output_handler); +/** + * Note: Function assumes that we have 4 outbound queues. + */ int qeth_get_priority_queue(struct qeth_card *card, struct sk_buff *skb, int ipv, int cast_type) { - if (!ipv && (card->info.type == QETH_CARD_TYPE_OSD || - card->info.type == QETH_CARD_TYPE_OSX)) - return card->qdio.default_out_queue; - switch (card->qdio.no_out_queues) { - case 4: - if (cast_type && card->info.is_multicast_different) - return card->info.is_multicast_different & - (card->qdio.no_out_queues - 1); - if (card->qdio.do_prio_queueing && (ipv == 4)) { - const u8 tos = ip_hdr(skb)->tos; - - if (card->qdio.do_prio_queueing == - QETH_PRIO_Q_ING_TOS) { - if (tos & IP_TOS_NOTIMPORTANT) - return 3; - if (tos & IP_TOS_HIGHRELIABILITY) - return 2; - if (tos & IP_TOS_HIGHTHROUGHPUT) - return 1; - if (tos & IP_TOS_LOWDELAY) - return 0; - } - if (card->qdio.do_prio_queueing == - QETH_PRIO_Q_ING_PREC) - return 3 - (tos >> 6); - } else if (card->qdio.do_prio_queueing && (ipv == 6)) { - /* TODO: IPv6!!! */ + __be16 *tci; + u8 tos; + + if (cast_type && card->info.is_multicast_different) + return card->info.is_multicast_different & + (card->qdio.no_out_queues - 1); + + switch (card->qdio.do_prio_queueing) { + case QETH_PRIO_Q_ING_TOS: + case QETH_PRIO_Q_ING_PREC: + switch (ipv) { + case 4: + tos = ipv4_get_dsfield(ip_hdr(skb)); + break; + case 6: + tos = ipv6_get_dsfield(ipv6_hdr(skb)); + break; + default: + return card->qdio.default_out_queue; } - return card->qdio.default_out_queue; - case 1: /* fallthrough for single-out-queue 1920-device */ + if (card->qdio.do_prio_queueing == QETH_PRIO_Q_ING_PREC) + return ~tos >> 6 & 3; + if (tos & IPTOS_MINCOST) + return 3; + if (tos & IPTOS_RELIABILITY) + return 2; + if (tos & IPTOS_THROUGHPUT) + return 1; + if (tos & IPTOS_LOWDELAY) + return 0; + break; + case QETH_PRIO_Q_ING_SKB: + if (skb->priority > 5) + return 0; + return ~skb->priority >> 1 & 3; + case QETH_PRIO_Q_ING_VLAN: + tci = &((struct ethhdr *)skb->data)->h_proto; + if (*tci == ETH_P_8021Q) + return ~*(tci + 1) >> (VLAN_PRIO_SHIFT + 1) & 3; + break; default: - return card->qdio.default_out_queue; + break; } + return card->qdio.default_out_queue; } EXPORT_SYMBOL_GPL(qeth_get_priority_queue); -int qeth_get_elements_no(struct qeth_card *card, void *hdr, +int qeth_get_elements_for_frags(struct sk_buff *skb) +{ + int cnt, length, e, elements = 0; + struct skb_frag_struct *frag; + char *data; + + for (cnt = 0; cnt < skb_shinfo(skb)->nr_frags; cnt++) { + frag = &skb_shinfo(skb)->frags[cnt]; + data = (char *)page_to_phys(skb_frag_page(frag)) + + frag->page_offset; + length = frag->size; + e = PFN_UP((unsigned long)data + length - 1) - + PFN_DOWN((unsigned long)data); + elements += e; + } + return elements; +} +EXPORT_SYMBOL_GPL(qeth_get_elements_for_frags); + +int qeth_get_elements_no(struct qeth_card *card, struct sk_buff *skb, int elems) { int dlen = skb->len - skb->data_len; int elements_needed = PFN_UP((unsigned long)skb->data + dlen - 1) - PFN_DOWN((unsigned long)skb->data); - elements_needed += skb_shinfo(skb)->nr_frags; + elements_needed += qeth_get_elements_for_frags(skb); + if ((elements_needed + elems) > QETH_MAX_BUFFER_ELEMENTS(card)) { QETH_DBF_MESSAGE(2, "Invalid size of IP packet " "(Number=%d / Length=%d). Discarded.\n", @@ -3650,7 +3754,7 @@ int qeth_get_elements_no(struct qeth_card *card, void *hdr, } EXPORT_SYMBOL_GPL(qeth_get_elements_no); -int qeth_hdr_chk_and_bounce(struct sk_buff *skb, int len) +int qeth_hdr_chk_and_bounce(struct sk_buff *skb, struct qeth_hdr **hdr, int len) { int hroom, inpage, rest; @@ -3663,6 +3767,8 @@ int qeth_hdr_chk_and_bounce(struct sk_buff *skb, int len) return 1; memmove(skb->data - rest, skb->data, skb->len - skb->data_len); skb->data -= rest; + skb->tail -= rest; + *hdr = (struct qeth_hdr *)skb->data; QETH_DBF_MESSAGE(2, "skb bounce len: %d rest: %d\n", len, rest); } return 0; @@ -3724,12 +3830,23 @@ static inline void __qeth_fill_buffer(struct sk_buff *skb, for (cnt = 0; cnt < skb_shinfo(skb)->nr_frags; cnt++) { frag = &skb_shinfo(skb)->frags[cnt]; - buffer->element[element].addr = (char *) - page_to_phys(skb_frag_page(frag)) - + frag->page_offset; - buffer->element[element].length = frag->size; - buffer->element[element].eflags = SBAL_EFLAGS_MIDDLE_FRAG; - element++; + data = (char *)page_to_phys(skb_frag_page(frag)) + + frag->page_offset; + length = frag->size; + while (length > 0) { + length_here = PAGE_SIZE - + ((unsigned long) data % PAGE_SIZE); + if (length < length_here) + length_here = length; + + buffer->element[element].addr = data; + buffer->element[element].length = length_here; + buffer->element[element].eflags = + SBAL_EFLAGS_MIDDLE_FRAG; + length -= length_here; + data += length_here; + element++; + } } if (buffer->element[element - 1].eflags) @@ -4044,6 +4161,7 @@ static int qeth_setadpparms_set_access_ctrl_cb(struct qeth_card *card, { struct qeth_ipa_cmd *cmd; struct qeth_set_access_ctrl *access_ctrl_req; + int fallback = *(int *)reply->param; QETH_CARD_TEXT(card, 4, "setaccb"); @@ -4053,12 +4171,14 @@ static int qeth_setadpparms_set_access_ctrl_cb(struct qeth_card *card, QETH_DBF_TEXT_(SETUP, 2, "%s", card->gdev->dev.kobj.name); QETH_DBF_TEXT_(SETUP, 2, "rc=%d", cmd->data.setadapterparms.hdr.return_code); + if (cmd->data.setadapterparms.hdr.return_code != + SET_ACCESS_CTRL_RC_SUCCESS) + QETH_DBF_MESSAGE(3, "ERR:SET_ACCESS_CTRL(%s,%d)==%d\n", + card->gdev->dev.kobj.name, + access_ctrl_req->subcmd_code, + cmd->data.setadapterparms.hdr.return_code); switch (cmd->data.setadapterparms.hdr.return_code) { case SET_ACCESS_CTRL_RC_SUCCESS: - case SET_ACCESS_CTRL_RC_ALREADY_NOT_ISOLATED: - case SET_ACCESS_CTRL_RC_ALREADY_ISOLATED: - { - card->options.isolation = access_ctrl_req->subcmd_code; if (card->options.isolation == ISOLATION_MODE_NONE) { dev_info(&card->gdev->dev, "QDIO data connection isolation is deactivated\n"); @@ -4066,72 +4186,64 @@ static int qeth_setadpparms_set_access_ctrl_cb(struct qeth_card *card, dev_info(&card->gdev->dev, "QDIO data connection isolation is activated\n"); } - QETH_DBF_MESSAGE(3, "OK:SET_ACCESS_CTRL(%s, %d)==%d\n", - card->gdev->dev.kobj.name, - access_ctrl_req->subcmd_code, - cmd->data.setadapterparms.hdr.return_code); break; - } + case SET_ACCESS_CTRL_RC_ALREADY_NOT_ISOLATED: + QETH_DBF_MESSAGE(2, "%s QDIO data connection isolation already " + "deactivated\n", dev_name(&card->gdev->dev)); + if (fallback) + card->options.isolation = card->options.prev_isolation; + break; + case SET_ACCESS_CTRL_RC_ALREADY_ISOLATED: + QETH_DBF_MESSAGE(2, "%s QDIO data connection isolation already" + " activated\n", dev_name(&card->gdev->dev)); + if (fallback) + card->options.isolation = card->options.prev_isolation; + break; case SET_ACCESS_CTRL_RC_NOT_SUPPORTED: - { - QETH_DBF_MESSAGE(3, "ERR:SET_ACCESS_CTRL(%s,%d)==%d\n", - card->gdev->dev.kobj.name, - access_ctrl_req->subcmd_code, - cmd->data.setadapterparms.hdr.return_code); dev_err(&card->gdev->dev, "Adapter does not " "support QDIO data connection isolation\n"); - - /* ensure isolation mode is "none" */ - card->options.isolation = ISOLATION_MODE_NONE; break; - } case SET_ACCESS_CTRL_RC_NONE_SHARED_ADAPTER: - { - QETH_DBF_MESSAGE(3, "ERR:SET_ACCESS_MODE(%s,%d)==%d\n", - card->gdev->dev.kobj.name, - access_ctrl_req->subcmd_code, - cmd->data.setadapterparms.hdr.return_code); dev_err(&card->gdev->dev, "Adapter is dedicated. " "QDIO data connection isolation not supported\n"); - - /* ensure isolation mode is "none" */ - card->options.isolation = ISOLATION_MODE_NONE; + if (fallback) + card->options.isolation = card->options.prev_isolation; break; - } case SET_ACCESS_CTRL_RC_ACTIVE_CHECKSUM_OFF: - { - QETH_DBF_MESSAGE(3, "ERR:SET_ACCESS_MODE(%s,%d)==%d\n", - card->gdev->dev.kobj.name, - access_ctrl_req->subcmd_code, - cmd->data.setadapterparms.hdr.return_code); dev_err(&card->gdev->dev, "TSO does not permit QDIO data connection isolation\n"); - - /* ensure isolation mode is "none" */ - card->options.isolation = ISOLATION_MODE_NONE; + if (fallback) + card->options.isolation = card->options.prev_isolation; + break; + case SET_ACCESS_CTRL_RC_REFLREL_UNSUPPORTED: + dev_err(&card->gdev->dev, "The adjacent switch port does not " + "support reflective relay mode\n"); + if (fallback) + card->options.isolation = card->options.prev_isolation; + break; + case SET_ACCESS_CTRL_RC_REFLREL_FAILED: + dev_err(&card->gdev->dev, "The reflective relay mode cannot be " + "enabled at the adjacent switch port"); + if (fallback) + card->options.isolation = card->options.prev_isolation; + break; + case SET_ACCESS_CTRL_RC_REFLREL_DEACT_FAILED: + dev_warn(&card->gdev->dev, "Turning off reflective relay mode " + "at the adjacent switch failed\n"); break; - } default: - { /* this should never happen */ - QETH_DBF_MESSAGE(3, "ERR:SET_ACCESS_MODE(%s,%d)==%d" - "==UNKNOWN\n", - card->gdev->dev.kobj.name, - access_ctrl_req->subcmd_code, - cmd->data.setadapterparms.hdr.return_code); - - /* ensure isolation mode is "none" */ - card->options.isolation = ISOLATION_MODE_NONE; + if (fallback) + card->options.isolation = card->options.prev_isolation; break; } - } qeth_default_setadapterparms_cb(card, reply, (unsigned long) cmd); return 0; } static int qeth_setadpparms_set_access_ctrl(struct qeth_card *card, - enum qeth_ipa_isolation_modes isolation) + enum qeth_ipa_isolation_modes isolation, int fallback) { int rc; struct qeth_cmd_buffer *iob; @@ -4151,12 +4263,12 @@ static int qeth_setadpparms_set_access_ctrl(struct qeth_card *card, access_ctrl_req->subcmd_code = isolation; rc = qeth_send_ipa_cmd(card, iob, qeth_setadpparms_set_access_ctrl_cb, - NULL); + &fallback); QETH_DBF_TEXT_(SETUP, 2, "rc=%d", rc); return rc; } -int qeth_set_access_ctrl_online(struct qeth_card *card) +int qeth_set_access_ctrl_online(struct qeth_card *card, int fallback) { int rc = 0; @@ -4166,12 +4278,13 @@ int qeth_set_access_ctrl_online(struct qeth_card *card) card->info.type == QETH_CARD_TYPE_OSX) && qeth_adp_supported(card, IPA_SETADP_SET_ACCESS_CONTROL)) { rc = qeth_setadpparms_set_access_ctrl(card, - card->options.isolation); + card->options.isolation, fallback); if (rc) { QETH_DBF_MESSAGE(3, "IPA(SET_ACCESS_CTRL,%s,%d) sent failed\n", card->gdev->dev.kobj.name, rc); + rc = -EOPNOTSUPP; } } else if (card->options.isolation != ISOLATION_MODE_NONE) { card->options.isolation = ISOLATION_MODE_NONE; @@ -4319,7 +4432,7 @@ static int qeth_snmp_command_cb(struct qeth_card *card, /* check if there is enough room in userspace */ if ((qinfo->udata_len - qinfo->udata_offset) < data_len) { QETH_CARD_TEXT_(card, 4, "scer3%i", -ENOMEM); - cmd->hdr.return_code = -ENOMEM; + cmd->hdr.return_code = IPA_RC_ENOMEM; return 0; } QETH_CARD_TEXT_(card, 4, "snore%i", @@ -4353,7 +4466,7 @@ int qeth_snmp_command(struct qeth_card *card, char __user *udata) struct qeth_cmd_buffer *iob; struct qeth_ipa_cmd *cmd; struct qeth_snmp_ureq *ureq; - int req_len; + unsigned int req_len; struct qeth_arp_query_info qinfo = {0, }; int rc = 0; @@ -4369,6 +4482,10 @@ int qeth_snmp_command(struct qeth_card *card, char __user *udata) /* skip 4 bytes (data_len struct member) to get req_len */ if (copy_from_user(&req_len, udata + sizeof(int), sizeof(int))) return -EFAULT; + if (req_len > (QETH_BUFSIZE - IPA_PDU_HEADER_SIZE - + sizeof(struct qeth_ipacmd_hdr) - + sizeof(struct qeth_ipacmd_setadpparms_hdr))) + return -EINVAL; ureq = memdup_user(udata, req_len + sizeof(struct qeth_snmp_ureq_hdr)); if (IS_ERR(ureq)) { QETH_CARD_TEXT(card, 2, "snmpnome"); @@ -4402,6 +4519,140 @@ int qeth_snmp_command(struct qeth_card *card, char __user *udata) } EXPORT_SYMBOL_GPL(qeth_snmp_command); +static int qeth_setadpparms_query_oat_cb(struct qeth_card *card, + struct qeth_reply *reply, unsigned long data) +{ + struct qeth_ipa_cmd *cmd; + struct qeth_qoat_priv *priv; + char *resdata; + int resdatalen; + + QETH_CARD_TEXT(card, 3, "qoatcb"); + + cmd = (struct qeth_ipa_cmd *)data; + priv = (struct qeth_qoat_priv *)reply->param; + resdatalen = cmd->data.setadapterparms.hdr.cmdlength; + resdata = (char *)data + 28; + + if (resdatalen > (priv->buffer_len - priv->response_len)) { + cmd->hdr.return_code = IPA_RC_FFFF; + return 0; + } + + memcpy((priv->buffer + priv->response_len), resdata, + resdatalen); + priv->response_len += resdatalen; + + if (cmd->data.setadapterparms.hdr.seq_no < + cmd->data.setadapterparms.hdr.used_total) + return 1; + return 0; +} + +int qeth_query_oat_command(struct qeth_card *card, char __user *udata) +{ + int rc = 0; + struct qeth_cmd_buffer *iob; + struct qeth_ipa_cmd *cmd; + struct qeth_query_oat *oat_req; + struct qeth_query_oat_data oat_data; + struct qeth_qoat_priv priv; + void __user *tmp; + + QETH_CARD_TEXT(card, 3, "qoatcmd"); + + if (!qeth_adp_supported(card, IPA_SETADP_QUERY_OAT)) { + rc = -EOPNOTSUPP; + goto out; + } + + if (copy_from_user(&oat_data, udata, + sizeof(struct qeth_query_oat_data))) { + rc = -EFAULT; + goto out; + } + + priv.buffer_len = oat_data.buffer_len; + priv.response_len = 0; + priv.buffer = kzalloc(oat_data.buffer_len, GFP_KERNEL); + if (!priv.buffer) { + rc = -ENOMEM; + goto out; + } + + iob = qeth_get_adapter_cmd(card, IPA_SETADP_QUERY_OAT, + sizeof(struct qeth_ipacmd_setadpparms_hdr) + + sizeof(struct qeth_query_oat)); + cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); + oat_req = &cmd->data.setadapterparms.data.query_oat; + oat_req->subcmd_code = oat_data.command; + + rc = qeth_send_ipa_cmd(card, iob, qeth_setadpparms_query_oat_cb, + &priv); + if (!rc) { + if (is_compat_task()) + tmp = compat_ptr(oat_data.ptr); + else + tmp = (void __user *)(unsigned long)oat_data.ptr; + + if (copy_to_user(tmp, priv.buffer, + priv.response_len)) { + rc = -EFAULT; + goto out_free; + } + + oat_data.response_len = priv.response_len; + + if (copy_to_user(udata, &oat_data, + sizeof(struct qeth_query_oat_data))) + rc = -EFAULT; + } else + if (rc == IPA_RC_FFFF) + rc = -EFAULT; + +out_free: + kfree(priv.buffer); +out: + return rc; +} +EXPORT_SYMBOL_GPL(qeth_query_oat_command); + +static int qeth_query_card_info_cb(struct qeth_card *card, + struct qeth_reply *reply, unsigned long data) +{ + struct qeth_ipa_cmd *cmd; + struct qeth_query_card_info *card_info; + struct carrier_info *carrier_info; + + QETH_CARD_TEXT(card, 2, "qcrdincb"); + carrier_info = (struct carrier_info *)reply->param; + cmd = (struct qeth_ipa_cmd *)data; + card_info = &cmd->data.setadapterparms.data.card_info; + if (cmd->data.setadapterparms.hdr.return_code == 0) { + carrier_info->card_type = card_info->card_type; + carrier_info->port_mode = card_info->port_mode; + carrier_info->port_speed = card_info->port_speed; + } + + qeth_default_setadapterparms_cb(card, reply, (unsigned long) cmd); + return 0; +} + +int qeth_query_card_info(struct qeth_card *card, + struct carrier_info *carrier_info) +{ + struct qeth_cmd_buffer *iob; + + QETH_CARD_TEXT(card, 2, "qcrdinfo"); + if (!qeth_adp_supported(card, IPA_SETADP_QUERY_CARD_INFO)) + return -EOPNOTSUPP; + iob = qeth_get_adapter_cmd(card, IPA_SETADP_QUERY_CARD_INFO, + sizeof(struct qeth_ipacmd_setadpparms_hdr)); + return qeth_send_ipa_cmd(card, iob, qeth_query_card_info_cb, + (void *)carrier_info); +} +EXPORT_SYMBOL_GPL(qeth_query_card_info); + static inline int qeth_get_qdio_q_format(struct qeth_card *card) { switch (card->info.type) { @@ -4439,7 +4690,8 @@ static void qeth_determine_capabilities(struct qeth_card *card) goto out_offline; } qeth_configure_unitaddr(card, prcd); - qeth_configure_blkt_default(card, prcd); + if (ddev_offline) + qeth_configure_blkt_default(card, prcd); kfree(prcd); rc = qdio_get_ssqd_desc(ddev, &card->ssqd); @@ -4526,7 +4778,7 @@ static int qeth_qdio_establish(struct qeth_card *card) goto out_free_in_sbals; } for (i = 0; i < card->qdio.no_in_queues; ++i) - queue_start_poll[i] = card->discipline.start_poll; + queue_start_poll[i] = card->discipline->start_poll; qeth_qdio_establish_cq(card, in_sbal_ptrs, queue_start_poll); @@ -4550,15 +4802,15 @@ static int qeth_qdio_establish(struct qeth_card *card) init_data.qib_param_field = qib_param_field; init_data.no_input_qs = card->qdio.no_in_queues; init_data.no_output_qs = card->qdio.no_out_queues; - init_data.input_handler = card->discipline.input_handler; - init_data.output_handler = card->discipline.output_handler; + init_data.input_handler = card->discipline->input_handler; + init_data.output_handler = card->discipline->output_handler; init_data.queue_start_poll_array = queue_start_poll; init_data.int_parm = (unsigned long) card; init_data.input_sbal_addr_array = (void **) in_sbal_ptrs; init_data.output_sbal_addr_array = (void **) out_sbal_ptrs; init_data.output_sbal_state_array = card->qdio.out_bufstates; init_data.scan_threshold = - (card->info.type == QETH_CARD_TYPE_IQD) ? 8 : 32; + (card->info.type == QETH_CARD_TYPE_IQD) ? 1 : 32; if (atomic_cmpxchg(&card->qdio.state, QETH_QDIO_ALLOCATED, QETH_QDIO_ESTABLISHED) == QETH_QDIO_ALLOCATED) { @@ -4611,6 +4863,19 @@ static void qeth_core_free_card(struct qeth_card *card) kfree(card); } +void qeth_trace_features(struct qeth_card *card) +{ + QETH_CARD_TEXT(card, 2, "features"); + QETH_CARD_TEXT_(card, 2, "%x", card->options.ipa4.supported_funcs); + QETH_CARD_TEXT_(card, 2, "%x", card->options.ipa4.enabled_funcs); + QETH_CARD_TEXT_(card, 2, "%x", card->options.ipa6.supported_funcs); + QETH_CARD_TEXT_(card, 2, "%x", card->options.ipa6.enabled_funcs); + QETH_CARD_TEXT_(card, 2, "%x", card->options.adp.supported_funcs); + QETH_CARD_TEXT_(card, 2, "%x", card->options.adp.enabled_funcs); + QETH_CARD_TEXT_(card, 2, "%x", card->info.diagass_support); +} +EXPORT_SYMBOL_GPL(qeth_trace_features); + static struct ccw_device_id qeth_ids[] = { {CCW_DEVICE_DEVTYPE(0x1731, 0x01, 0x1732, 0x01), .driver_info = QETH_CARD_TYPE_OSD}, @@ -4636,28 +4901,23 @@ static struct ccw_driver qeth_ccw_driver = { .remove = ccwgroup_remove_ccwdev, }; -static int qeth_core_driver_group(const char *buf, struct device *root_dev, - unsigned long driver_id) -{ - return ccwgroup_create_from_string(root_dev, driver_id, - &qeth_ccw_driver, 3, buf); -} - int qeth_core_hardsetup_card(struct qeth_card *card) { - int retries = 0; + int retries = 3; int rc; QETH_DBF_TEXT(SETUP, 2, "hrdsetup"); atomic_set(&card->force_alloc_skb, 0); - qeth_get_channel_path_desc(card); + qeth_update_from_chp_desc(card); retry: - if (retries) + if (retries < 3) QETH_DBF_MESSAGE(2, "%s Retrying to do IDX activates.\n", dev_name(&card->gdev->dev)); + rc = qeth_qdio_clear_card(card, card->info.type != QETH_CARD_TYPE_IQD); ccw_device_set_offline(CARD_DDEV(card)); ccw_device_set_offline(CARD_WDEV(card)); ccw_device_set_offline(CARD_RDEV(card)); + qdio_free(CARD_DDEV(card)); rc = ccw_device_set_online(CARD_RDEV(card)); if (rc) goto retriable; @@ -4667,14 +4927,13 @@ retry: rc = ccw_device_set_online(CARD_DDEV(card)); if (rc) goto retriable; - rc = qeth_qdio_clear_card(card, card->info.type != QETH_CARD_TYPE_IQD); retriable: if (rc == -ERESTARTSYS) { QETH_DBF_TEXT(SETUP, 2, "break1"); return rc; } else if (rc) { QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc); - if (++retries > 3) + if (--retries < 0) goto out; else goto retry; @@ -4713,6 +4972,7 @@ retriable: card->options.ipa4.supported_funcs = 0; card->options.adp.supported_funcs = 0; + card->options.sbp.supported_funcs = 0; card->info.diagass_support = 0; qeth_query_ipassists(card, QETH_PROT_IPV4); if (qeth_is_supported(card, IPA_SETADAPTERPARMS)) @@ -4808,11 +5068,7 @@ struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card, break; case QETH_HEADER_TYPE_LAYER3: skb_len = (*hdr)->hdr.l3.length; - if ((card->info.link_type == QETH_LINK_TYPE_LANE_TR) || - (card->info.link_type == QETH_LINK_TYPE_HSTR)) - headroom = TR_HLEN; - else - headroom = ETH_HLEN; + headroom = ETH_HLEN; break; case QETH_HEADER_TYPE_OSN: skb_len = (*hdr)->hdr.osn.pdu_length; @@ -4897,7 +5153,7 @@ void qeth_dbf_longtext(debug_info_t *id, int level, char *fmt, ...) char dbf_txt_buf[32]; va_list args; - if (level > id->level) + if (!debug_level_enabled(id, level)) return; va_start(args, fmt); vsnprintf(dbf_txt_buf, sizeof(dbf_txt_buf), fmt, args); @@ -4940,33 +5196,110 @@ int qeth_core_load_discipline(struct qeth_card *card, enum qeth_discipline_id discipline) { int rc = 0; + mutex_lock(&qeth_mod_mutex); switch (discipline) { case QETH_DISCIPLINE_LAYER3: - card->discipline.ccwgdriver = try_then_request_module( - symbol_get(qeth_l3_ccwgroup_driver), - "qeth_l3"); + card->discipline = try_then_request_module( + symbol_get(qeth_l3_discipline), "qeth_l3"); break; case QETH_DISCIPLINE_LAYER2: - card->discipline.ccwgdriver = try_then_request_module( - symbol_get(qeth_l2_ccwgroup_driver), - "qeth_l2"); + card->discipline = try_then_request_module( + symbol_get(qeth_l2_discipline), "qeth_l2"); break; } - if (!card->discipline.ccwgdriver) { + if (!card->discipline) { dev_err(&card->gdev->dev, "There is no kernel module to " "support discipline %d\n", discipline); rc = -EINVAL; } + mutex_unlock(&qeth_mod_mutex); return rc; } void qeth_core_free_discipline(struct qeth_card *card) { if (card->options.layer2) - symbol_put(qeth_l2_ccwgroup_driver); + symbol_put(qeth_l2_discipline); else - symbol_put(qeth_l3_ccwgroup_driver); - card->discipline.ccwgdriver = NULL; + symbol_put(qeth_l3_discipline); + card->discipline = NULL; +} + +static const struct device_type qeth_generic_devtype = { + .name = "qeth_generic", + .groups = qeth_generic_attr_groups, +}; +static const struct device_type qeth_osn_devtype = { + .name = "qeth_osn", + .groups = qeth_osn_attr_groups, +}; + +#define DBF_NAME_LEN 20 + +struct qeth_dbf_entry { + char dbf_name[DBF_NAME_LEN]; + debug_info_t *dbf_info; + struct list_head dbf_list; +}; + +static LIST_HEAD(qeth_dbf_list); +static DEFINE_MUTEX(qeth_dbf_list_mutex); + +static debug_info_t *qeth_get_dbf_entry(char *name) +{ + struct qeth_dbf_entry *entry; + debug_info_t *rc = NULL; + + mutex_lock(&qeth_dbf_list_mutex); + list_for_each_entry(entry, &qeth_dbf_list, dbf_list) { + if (strcmp(entry->dbf_name, name) == 0) { + rc = entry->dbf_info; + break; + } + } + mutex_unlock(&qeth_dbf_list_mutex); + return rc; +} + +static int qeth_add_dbf_entry(struct qeth_card *card, char *name) +{ + struct qeth_dbf_entry *new_entry; + + card->debug = debug_register(name, 2, 1, 8); + if (!card->debug) { + QETH_DBF_TEXT_(SETUP, 2, "%s", "qcdbf"); + goto err; + } + if (debug_register_view(card->debug, &debug_hex_ascii_view)) + goto err_dbg; + new_entry = kzalloc(sizeof(struct qeth_dbf_entry), GFP_KERNEL); + if (!new_entry) + goto err_dbg; + strncpy(new_entry->dbf_name, name, DBF_NAME_LEN); + new_entry->dbf_info = card->debug; + mutex_lock(&qeth_dbf_list_mutex); + list_add(&new_entry->dbf_list, &qeth_dbf_list); + mutex_unlock(&qeth_dbf_list_mutex); + + return 0; + +err_dbg: + debug_unregister(card->debug); +err: + return -ENOMEM; +} + +static void qeth_clear_dbf_list(void) +{ + struct qeth_dbf_entry *entry, *tmp; + + mutex_lock(&qeth_dbf_list_mutex); + list_for_each_entry_safe(entry, tmp, &qeth_dbf_list, dbf_list) { + list_del(&entry->dbf_list); + debug_unregister(entry->dbf_info); + kfree(entry); + } + mutex_unlock(&qeth_dbf_list_mutex); } static int qeth_core_probe_device(struct ccwgroup_device *gdev) @@ -4975,7 +5308,7 @@ static int qeth_core_probe_device(struct ccwgroup_device *gdev) struct device *dev; int rc; unsigned long flags; - char dbf_name[20]; + char dbf_name[DBF_NAME_LEN]; QETH_DBF_TEXT(SETUP, 2, "probedev"); @@ -4994,13 +5327,12 @@ static int qeth_core_probe_device(struct ccwgroup_device *gdev) snprintf(dbf_name, sizeof(dbf_name), "qeth_card_%s", dev_name(&gdev->dev)); - card->debug = debug_register(dbf_name, 2, 1, 8); + card->debug = qeth_get_dbf_entry(dbf_name); if (!card->debug) { - QETH_DBF_TEXT_(SETUP, 2, "%s", "qcdbf"); - rc = -ENOMEM; - goto err_card; + rc = qeth_add_dbf_entry(card, dbf_name); + if (rc) + goto err_card; } - debug_register_view(card->debug, &debug_hex_ascii_view); card->read.ccwdev = gdev->cdev[0]; card->write.ccwdev = gdev->cdev[1]; @@ -5014,27 +5346,26 @@ static int qeth_core_probe_device(struct ccwgroup_device *gdev) rc = qeth_determine_card_type(card); if (rc) { QETH_DBF_TEXT_(SETUP, 2, "3err%d", rc); - goto err_dbf; + goto err_card; } rc = qeth_setup_card(card); if (rc) { QETH_DBF_TEXT_(SETUP, 2, "2err%d", rc); - goto err_dbf; + goto err_card; } if (card->info.type == QETH_CARD_TYPE_OSN) - rc = qeth_core_create_osn_attributes(dev); + gdev->dev.type = &qeth_osn_devtype; else - rc = qeth_core_create_device_attributes(dev); - if (rc) - goto err_dbf; + gdev->dev.type = &qeth_generic_devtype; + switch (card->info.type) { case QETH_CARD_TYPE_OSN: case QETH_CARD_TYPE_OSM: rc = qeth_core_load_discipline(card, QETH_DISCIPLINE_LAYER2); if (rc) - goto err_attr; - rc = card->discipline.ccwgdriver->probe(card->gdev); + goto err_card; + rc = card->discipline->setup(card->gdev); if (rc) goto err_disc; case QETH_CARD_TYPE_OSD: @@ -5052,13 +5383,6 @@ static int qeth_core_probe_device(struct ccwgroup_device *gdev) err_disc: qeth_core_free_discipline(card); -err_attr: - if (card->info.type == QETH_CARD_TYPE_OSN) - qeth_core_remove_osn_attributes(dev); - else - qeth_core_remove_device_attributes(dev); -err_dbf: - debug_unregister(card->debug); err_card: qeth_core_free_card(card); err_dev: @@ -5073,18 +5397,11 @@ static void qeth_core_remove_device(struct ccwgroup_device *gdev) QETH_DBF_TEXT(SETUP, 2, "removedv"); - if (card->info.type == QETH_CARD_TYPE_OSN) { - qeth_core_remove_osn_attributes(&gdev->dev); - } else { - qeth_core_remove_device_attributes(&gdev->dev); - } - - if (card->discipline.ccwgdriver) { - card->discipline.ccwgdriver->remove(gdev); + if (card->discipline) { + card->discipline->remove(gdev); qeth_core_free_discipline(card); } - debug_unregister(card->debug); write_lock_irqsave(&qeth_core_card_list.rwlock, flags); list_del(&card->list); write_unlock_irqrestore(&qeth_core_card_list.rwlock, flags); @@ -5100,7 +5417,7 @@ static int qeth_core_set_online(struct ccwgroup_device *gdev) int rc = 0; int def_discipline; - if (!card->discipline.ccwgdriver) { + if (!card->discipline) { if (card->info.type == QETH_CARD_TYPE_IQD) def_discipline = QETH_DISCIPLINE_LAYER3; else @@ -5108,11 +5425,11 @@ static int qeth_core_set_online(struct ccwgroup_device *gdev) rc = qeth_core_load_discipline(card, def_discipline); if (rc) goto err; - rc = card->discipline.ccwgdriver->probe(card->gdev); + rc = card->discipline->setup(card->gdev); if (rc) goto err; } - rc = card->discipline.ccwgdriver->set_online(gdev); + rc = card->discipline->set_online(gdev); err: return rc; } @@ -5120,58 +5437,52 @@ err: static int qeth_core_set_offline(struct ccwgroup_device *gdev) { struct qeth_card *card = dev_get_drvdata(&gdev->dev); - return card->discipline.ccwgdriver->set_offline(gdev); + return card->discipline->set_offline(gdev); } static void qeth_core_shutdown(struct ccwgroup_device *gdev) { struct qeth_card *card = dev_get_drvdata(&gdev->dev); - if (card->discipline.ccwgdriver && - card->discipline.ccwgdriver->shutdown) - card->discipline.ccwgdriver->shutdown(gdev); + if (card->discipline && card->discipline->shutdown) + card->discipline->shutdown(gdev); } static int qeth_core_prepare(struct ccwgroup_device *gdev) { struct qeth_card *card = dev_get_drvdata(&gdev->dev); - if (card->discipline.ccwgdriver && - card->discipline.ccwgdriver->prepare) - return card->discipline.ccwgdriver->prepare(gdev); + if (card->discipline && card->discipline->prepare) + return card->discipline->prepare(gdev); return 0; } static void qeth_core_complete(struct ccwgroup_device *gdev) { struct qeth_card *card = dev_get_drvdata(&gdev->dev); - if (card->discipline.ccwgdriver && - card->discipline.ccwgdriver->complete) - card->discipline.ccwgdriver->complete(gdev); + if (card->discipline && card->discipline->complete) + card->discipline->complete(gdev); } static int qeth_core_freeze(struct ccwgroup_device *gdev) { struct qeth_card *card = dev_get_drvdata(&gdev->dev); - if (card->discipline.ccwgdriver && - card->discipline.ccwgdriver->freeze) - return card->discipline.ccwgdriver->freeze(gdev); + if (card->discipline && card->discipline->freeze) + return card->discipline->freeze(gdev); return 0; } static int qeth_core_thaw(struct ccwgroup_device *gdev) { struct qeth_card *card = dev_get_drvdata(&gdev->dev); - if (card->discipline.ccwgdriver && - card->discipline.ccwgdriver->thaw) - return card->discipline.ccwgdriver->thaw(gdev); + if (card->discipline && card->discipline->thaw) + return card->discipline->thaw(gdev); return 0; } static int qeth_core_restore(struct ccwgroup_device *gdev) { struct qeth_card *card = dev_get_drvdata(&gdev->dev); - if (card->discipline.ccwgdriver && - card->discipline.ccwgdriver->restore) - return card->discipline.ccwgdriver->restore(gdev); + if (card->discipline && card->discipline->restore) + return card->discipline->restore(gdev); return 0; } @@ -5180,8 +5491,7 @@ static struct ccwgroup_driver qeth_core_ccwgroup_driver = { .owner = THIS_MODULE, .name = "qeth", }, - .driver_id = 0xD8C5E3C8, - .probe = qeth_core_probe_device, + .setup = qeth_core_probe_device, .remove = qeth_core_remove_device, .set_online = qeth_core_set_online, .set_offline = qeth_core_set_offline, @@ -5193,21 +5503,30 @@ static struct ccwgroup_driver qeth_core_ccwgroup_driver = { .restore = qeth_core_restore, }; -static ssize_t -qeth_core_driver_group_store(struct device_driver *ddrv, const char *buf, - size_t count) +static ssize_t qeth_core_driver_group_store(struct device_driver *ddrv, + const char *buf, size_t count) { int err; - err = qeth_core_driver_group(buf, qeth_core_root_dev, - qeth_core_ccwgroup_driver.driver_id); - if (err) - return err; - else - return count; -} + err = ccwgroup_create_dev(qeth_core_root_dev, + &qeth_core_ccwgroup_driver, 3, buf); + + return err ? err : count; +} static DRIVER_ATTR(group, 0200, NULL, qeth_core_driver_group_store); +static struct attribute *qeth_drv_attrs[] = { + &driver_attr_group.attr, + NULL, +}; +static struct attribute_group qeth_drv_attr_group = { + .attrs = qeth_drv_attrs, +}; +static const struct attribute_group *qeth_drv_attr_groups[] = { + &qeth_drv_attr_group, + NULL, +}; + static struct { const char str[ETH_GSTRING_LEN]; } qeth_ethtool_stats_keys[] = { @@ -5329,25 +5648,77 @@ void qeth_core_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { struct qeth_card *card = dev->ml_priv; - if (card->options.layer2) - strcpy(info->driver, "qeth_l2"); - else - strcpy(info->driver, "qeth_l3"); - strcpy(info->version, "1.0"); - strcpy(info->fw_version, card->info.mcl_level); - sprintf(info->bus_info, "%s/%s/%s", - CARD_RDEV_ID(card), - CARD_WDEV_ID(card), - CARD_DDEV_ID(card)); + strlcpy(info->driver, card->options.layer2 ? "qeth_l2" : "qeth_l3", + sizeof(info->driver)); + strlcpy(info->version, "1.0", sizeof(info->version)); + strlcpy(info->fw_version, card->info.mcl_level, + sizeof(info->fw_version)); + snprintf(info->bus_info, sizeof(info->bus_info), "%s/%s/%s", + CARD_RDEV_ID(card), CARD_WDEV_ID(card), CARD_DDEV_ID(card)); } EXPORT_SYMBOL_GPL(qeth_core_get_drvinfo); +/* Helper function to fill 'advertizing' and 'supported' which are the same. */ +/* Autoneg and full-duplex are supported and advertized uncondionally. */ +/* Always advertize and support all speeds up to specified, and only one */ +/* specified port type. */ +static void qeth_set_ecmd_adv_sup(struct ethtool_cmd *ecmd, + int maxspeed, int porttype) +{ + int port_sup, port_adv, spd_sup, spd_adv; + + switch (porttype) { + case PORT_TP: + port_sup = SUPPORTED_TP; + port_adv = ADVERTISED_TP; + break; + case PORT_FIBRE: + port_sup = SUPPORTED_FIBRE; + port_adv = ADVERTISED_FIBRE; + break; + default: + port_sup = SUPPORTED_TP; + port_adv = ADVERTISED_TP; + WARN_ON_ONCE(1); + } + + /* "Fallthrough" case'es ordered from high to low result in setting */ + /* flags cumulatively, starting from the specified speed and down to */ + /* the lowest possible. */ + spd_sup = 0; + spd_adv = 0; + switch (maxspeed) { + case SPEED_10000: + spd_sup |= SUPPORTED_10000baseT_Full; + spd_adv |= ADVERTISED_10000baseT_Full; + case SPEED_1000: + spd_sup |= SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full; + spd_adv |= ADVERTISED_1000baseT_Half | + ADVERTISED_1000baseT_Full; + case SPEED_100: + spd_sup |= SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full; + spd_adv |= ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full; + case SPEED_10: + spd_sup |= SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full; + spd_adv |= ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full; + break; + default: + spd_sup = SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full; + spd_adv = ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full; + WARN_ON_ONCE(1); + } + ecmd->advertising = ADVERTISED_Autoneg | port_adv | spd_adv; + ecmd->supported = SUPPORTED_Autoneg | port_sup | spd_sup; +} + int qeth_core_ethtool_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) { struct qeth_card *card = netdev->ml_priv; enum qeth_link_types link_type; + struct carrier_info carrier_info; + u32 speed; if ((card->info.type == QETH_CARD_TYPE_IQD) || (card->info.guestlan)) link_type = QETH_LINK_TYPE_10GBIT_ETH; @@ -5355,79 +5726,93 @@ int qeth_core_ethtool_get_settings(struct net_device *netdev, link_type = card->info.link_type; ecmd->transceiver = XCVR_INTERNAL; - ecmd->supported = SUPPORTED_Autoneg; - ecmd->advertising = ADVERTISED_Autoneg; ecmd->duplex = DUPLEX_FULL; ecmd->autoneg = AUTONEG_ENABLE; switch (link_type) { case QETH_LINK_TYPE_FAST_ETH: case QETH_LINK_TYPE_LANE_ETH100: - ecmd->supported |= SUPPORTED_10baseT_Half | - SUPPORTED_10baseT_Full | - SUPPORTED_100baseT_Half | - SUPPORTED_100baseT_Full | - SUPPORTED_TP; - ecmd->advertising |= ADVERTISED_10baseT_Half | - ADVERTISED_10baseT_Full | - ADVERTISED_100baseT_Half | - ADVERTISED_100baseT_Full | - ADVERTISED_TP; - ecmd->speed = SPEED_100; + qeth_set_ecmd_adv_sup(ecmd, SPEED_100, PORT_TP); + speed = SPEED_100; ecmd->port = PORT_TP; break; case QETH_LINK_TYPE_GBIT_ETH: case QETH_LINK_TYPE_LANE_ETH1000: - ecmd->supported |= SUPPORTED_10baseT_Half | - SUPPORTED_10baseT_Full | - SUPPORTED_100baseT_Half | - SUPPORTED_100baseT_Full | - SUPPORTED_1000baseT_Half | - SUPPORTED_1000baseT_Full | - SUPPORTED_FIBRE; - ecmd->advertising |= ADVERTISED_10baseT_Half | - ADVERTISED_10baseT_Full | - ADVERTISED_100baseT_Half | - ADVERTISED_100baseT_Full | - ADVERTISED_1000baseT_Half | - ADVERTISED_1000baseT_Full | - ADVERTISED_FIBRE; - ecmd->speed = SPEED_1000; + qeth_set_ecmd_adv_sup(ecmd, SPEED_1000, PORT_FIBRE); + speed = SPEED_1000; ecmd->port = PORT_FIBRE; break; case QETH_LINK_TYPE_10GBIT_ETH: - ecmd->supported |= SUPPORTED_10baseT_Half | - SUPPORTED_10baseT_Full | - SUPPORTED_100baseT_Half | - SUPPORTED_100baseT_Full | - SUPPORTED_1000baseT_Half | - SUPPORTED_1000baseT_Full | - SUPPORTED_10000baseT_Full | - SUPPORTED_FIBRE; - ecmd->advertising |= ADVERTISED_10baseT_Half | - ADVERTISED_10baseT_Full | - ADVERTISED_100baseT_Half | - ADVERTISED_100baseT_Full | - ADVERTISED_1000baseT_Half | - ADVERTISED_1000baseT_Full | - ADVERTISED_10000baseT_Full | - ADVERTISED_FIBRE; - ecmd->speed = SPEED_10000; + qeth_set_ecmd_adv_sup(ecmd, SPEED_10000, PORT_FIBRE); + speed = SPEED_10000; ecmd->port = PORT_FIBRE; break; default: - ecmd->supported |= SUPPORTED_10baseT_Half | - SUPPORTED_10baseT_Full | - SUPPORTED_TP; - ecmd->advertising |= ADVERTISED_10baseT_Half | - ADVERTISED_10baseT_Full | - ADVERTISED_TP; - ecmd->speed = SPEED_10; + qeth_set_ecmd_adv_sup(ecmd, SPEED_10, PORT_TP); + speed = SPEED_10; + ecmd->port = PORT_TP; + } + ethtool_cmd_speed_set(ecmd, speed); + + /* Check if we can obtain more accurate information. */ + /* If QUERY_CARD_INFO command is not supported or fails, */ + /* just return the heuristics that was filled above. */ + if (qeth_query_card_info(card, &carrier_info) != 0) + return 0; + + netdev_dbg(netdev, + "card info: card_type=0x%02x, port_mode=0x%04x, port_speed=0x%08x\n", + carrier_info.card_type, + carrier_info.port_mode, + carrier_info.port_speed); + + /* Update attributes for which we've obtained more authoritative */ + /* information, leave the rest the way they where filled above. */ + switch (carrier_info.card_type) { + case CARD_INFO_TYPE_1G_COPPER_A: + case CARD_INFO_TYPE_1G_COPPER_B: + qeth_set_ecmd_adv_sup(ecmd, SPEED_1000, PORT_TP); ecmd->port = PORT_TP; + break; + case CARD_INFO_TYPE_1G_FIBRE_A: + case CARD_INFO_TYPE_1G_FIBRE_B: + qeth_set_ecmd_adv_sup(ecmd, SPEED_1000, PORT_FIBRE); + ecmd->port = PORT_FIBRE; + break; + case CARD_INFO_TYPE_10G_FIBRE_A: + case CARD_INFO_TYPE_10G_FIBRE_B: + qeth_set_ecmd_adv_sup(ecmd, SPEED_10000, PORT_FIBRE); + ecmd->port = PORT_FIBRE; + break; + } + + switch (carrier_info.port_mode) { + case CARD_INFO_PORTM_FULLDUPLEX: + ecmd->duplex = DUPLEX_FULL; + break; + case CARD_INFO_PORTM_HALFDUPLEX: + ecmd->duplex = DUPLEX_HALF; + break; + } + + switch (carrier_info.port_speed) { + case CARD_INFO_PORTS_10M: + speed = SPEED_10; + break; + case CARD_INFO_PORTS_100M: + speed = SPEED_100; + break; + case CARD_INFO_PORTS_1G: + speed = SPEED_1000; + break; + case CARD_INFO_PORTS_10G: + speed = SPEED_10000; + break; } + ethtool_cmd_speed_set(ecmd, speed); return 0; } @@ -5439,54 +5824,50 @@ static int __init qeth_core_init(void) pr_info("loading core functions\n"); INIT_LIST_HEAD(&qeth_core_card_list.list); + INIT_LIST_HEAD(&qeth_dbf_list); rwlock_init(&qeth_core_card_list.rwlock); + mutex_init(&qeth_mod_mutex); + + qeth_wq = create_singlethread_workqueue("qeth_wq"); rc = qeth_register_dbf_views(); if (rc) goto out_err; - rc = ccw_driver_register(&qeth_ccw_driver); - if (rc) - goto ccw_err; - rc = ccwgroup_driver_register(&qeth_core_ccwgroup_driver); - if (rc) - goto ccwgroup_err; - rc = driver_create_file(&qeth_core_ccwgroup_driver.driver, - &driver_attr_group); - if (rc) - goto driver_err; qeth_core_root_dev = root_device_register("qeth"); - rc = IS_ERR(qeth_core_root_dev) ? PTR_ERR(qeth_core_root_dev) : 0; + rc = PTR_ERR_OR_ZERO(qeth_core_root_dev); if (rc) goto register_err; - qeth_core_header_cache = kmem_cache_create("qeth_hdr", sizeof(struct qeth_hdr) + ETH_HLEN, 64, 0, NULL); if (!qeth_core_header_cache) { rc = -ENOMEM; goto slab_err; } - qeth_qdio_outbuf_cache = kmem_cache_create("qeth_buf", sizeof(struct qeth_qdio_out_buffer), 0, 0, NULL); if (!qeth_qdio_outbuf_cache) { rc = -ENOMEM; goto cqslab_err; } + rc = ccw_driver_register(&qeth_ccw_driver); + if (rc) + goto ccw_err; + qeth_core_ccwgroup_driver.driver.groups = qeth_drv_attr_groups; + rc = ccwgroup_driver_register(&qeth_core_ccwgroup_driver); + if (rc) + goto ccwgroup_err; return 0; + +ccwgroup_err: + ccw_driver_unregister(&qeth_ccw_driver); +ccw_err: + kmem_cache_destroy(qeth_qdio_outbuf_cache); cqslab_err: kmem_cache_destroy(qeth_core_header_cache); slab_err: root_device_unregister(qeth_core_root_dev); register_err: - driver_remove_file(&qeth_core_ccwgroup_driver.driver, - &driver_attr_group); -driver_err: - ccwgroup_driver_unregister(&qeth_core_ccwgroup_driver); -ccwgroup_err: - ccw_driver_unregister(&qeth_ccw_driver); -ccw_err: - QETH_DBF_MESSAGE(2, "Initialization failed with code %d\n", rc); qeth_unregister_dbf_views(); out_err: pr_err("Initializing the qeth device driver failed\n"); @@ -5495,13 +5876,13 @@ out_err: static void __exit qeth_core_exit(void) { - root_device_unregister(qeth_core_root_dev); - driver_remove_file(&qeth_core_ccwgroup_driver.driver, - &driver_attr_group); + qeth_clear_dbf_list(); + destroy_workqueue(qeth_wq); ccwgroup_driver_unregister(&qeth_core_ccwgroup_driver); ccw_driver_unregister(&qeth_ccw_driver); kmem_cache_destroy(qeth_qdio_outbuf_cache); kmem_cache_destroy(qeth_core_header_cache); + root_device_unregister(qeth_core_root_dev); qeth_unregister_dbf_views(); pr_info("core functions removed\n"); } |
