qeth: HiperSockets Network Traffic Analyzer
authorUrsula Braun <ursula.braun@de.ibm.com>
Mon, 11 Jan 2010 02:50:50 +0000 (02:50 +0000)
committerDavid S. Miller <davem@davemloft.net>
Thu, 14 Jan 2010 04:34:55 +0000 (20:34 -0800)
New feature to trace HiperSockets network traffic for debugging
purposes.

Signed-off-by: Ursula Braun <ursula.braun@de.ibm.com>
Signed-off-by: Frank Blaschka <frank.blaschka@de.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/s390/net/qeth_core.h
drivers/s390/net/qeth_core_main.c
drivers/s390/net/qeth_core_mpc.h
drivers/s390/net/qeth_core_sys.c
drivers/s390/net/qeth_l2_main.c
drivers/s390/net/qeth_l3.h
drivers/s390/net/qeth_l3_main.c
drivers/s390/net/qeth_l3_sys.c

index b232693..a3ac445 100644 (file)
@@ -649,6 +649,7 @@ struct qeth_card_options {
        int performance_stats;
        int rx_sg_cb;
        enum qeth_ipa_isolation_modes isolation;
+       int sniffer;
 };
 
 /*
@@ -737,6 +738,7 @@ struct qeth_card {
        struct qeth_discipline discipline;
        atomic_t force_alloc_skb;
        struct service_level qeth_service_level;
+       struct qdio_ssqd_desc ssqd;
 };
 
 struct qeth_card_list_struct {
@@ -811,7 +813,8 @@ int qeth_send_ipa_cmd(struct qeth_card *, struct qeth_cmd_buffer *,
 struct qeth_cmd_buffer *qeth_get_ipacmd_buffer(struct qeth_card *,
                        enum qeth_ipa_cmds, enum qeth_prot_versions);
 int qeth_query_setadapterparms(struct qeth_card *);
-int qeth_check_qdio_errors(struct qdio_buffer *, unsigned int, const char *);
+int qeth_check_qdio_errors(struct qeth_card *, struct qdio_buffer *,
+               unsigned int, const char *);
 void qeth_queue_input_buffer(struct qeth_card *, int);
 struct sk_buff *qeth_core_get_next_skb(struct qeth_card *,
                struct qdio_buffer *, struct qdio_buffer_element **, int *,
index d34804d..2c8e9da 100644 (file)
@@ -269,6 +269,7 @@ int qeth_realloc_buffer_pool(struct qeth_card *card, int bufcnt)
        card->qdio.init_pool.buf_count = bufcnt;
        return qeth_alloc_buffer_pool(card);
 }
+EXPORT_SYMBOL_GPL(qeth_realloc_buffer_pool);
 
 static int qeth_issue_next_read(struct qeth_card *card)
 {
@@ -350,8 +351,10 @@ static struct qeth_ipa_cmd *qeth_check_ipa_data(struct qeth_card *card,
        if (IS_IPA(iob->data)) {
                cmd = (struct qeth_ipa_cmd *) PDU_ENCAPSULATION(iob->data);
                if (IS_IPA_REPLY(cmd)) {
-                       if (cmd->hdr.command < IPA_CMD_SETCCID ||
-                           cmd->hdr.command > IPA_CMD_MODCCID)
+                       if (cmd->hdr.command != IPA_CMD_SETCCID &&
+                           cmd->hdr.command != IPA_CMD_DELCCID &&
+                           cmd->hdr.command != IPA_CMD_MODCCID &&
+                           cmd->hdr.command != IPA_CMD_SET_DIAG_ASS)
                                qeth_issue_ipa_msg(cmd,
                                                cmd->hdr.return_code, card);
                        return cmd;
@@ -1100,11 +1103,6 @@ static int qeth_setup_card(struct qeth_card *card)
        card->thread_running_mask = 0;
        INIT_WORK(&card->kernel_thread_starter, qeth_start_kernel_thread);
        INIT_LIST_HEAD(&card->ip_list);
-       card->ip_tbd_list = kmalloc(sizeof(struct list_head), GFP_KERNEL);
-       if (!card->ip_tbd_list) {
-               QETH_DBF_TEXT(SETUP, 0, "iptbdnom");
-               return -ENOMEM;
-       }
        INIT_LIST_HEAD(card->ip_tbd_list);
        INIT_LIST_HEAD(&card->cmd_waiter_list);
        init_waitqueue_head(&card->wait_q);
@@ -1138,21 +1136,30 @@ static struct qeth_card *qeth_alloc_card(void)
        QETH_DBF_TEXT(SETUP, 2, "alloccrd");
        card = kzalloc(sizeof(struct qeth_card), GFP_DMA|GFP_KERNEL);
        if (!card)
-               return NULL;
+               goto out;
        QETH_DBF_HEX(SETUP, 2, &card, sizeof(void *));
-       if (qeth_setup_channel(&card->read)) {
-               kfree(card);
-               return NULL;
-       }
-       if (qeth_setup_channel(&card->write)) {
-               qeth_clean_channel(&card->read);
-               kfree(card);
-               return NULL;
+       card->ip_tbd_list = kmalloc(sizeof(struct list_head), GFP_KERNEL);
+       if (!card->ip_tbd_list) {
+               QETH_DBF_TEXT(SETUP, 0, "iptbdnom");
+               goto out_card;
        }
+       if (qeth_setup_channel(&card->read))
+               goto out_ip;
+       if (qeth_setup_channel(&card->write))
+               goto out_channel;
        card->options.layer2 = -1;
        card->qeth_service_level.seq_print = qeth_core_sl_print;
        register_service_level(&card->qeth_service_level);
        return card;
+
+out_channel:
+       qeth_clean_channel(&card->read);
+out_ip:
+       kfree(card->ip_tbd_list);
+out_card:
+       kfree(card);
+out:
+       return NULL;
 }
 
 static int qeth_determine_card_type(struct qeth_card *card)
@@ -2573,8 +2580,8 @@ int qeth_query_setadapterparms(struct qeth_card *card)
 }
 EXPORT_SYMBOL_GPL(qeth_query_setadapterparms);
 
-int qeth_check_qdio_errors(struct qdio_buffer *buf, unsigned int qdio_error,
-               const char *dbftext)
+int qeth_check_qdio_errors(struct qeth_card *card, struct qdio_buffer *buf,
+               unsigned int qdio_error, const char *dbftext)
 {
        if (qdio_error) {
                QETH_DBF_TEXT(TRACE, 2, dbftext);
@@ -2584,7 +2591,11 @@ int qeth_check_qdio_errors(struct qdio_buffer *buf, unsigned int qdio_error,
                QETH_DBF_TEXT_(QERR, 2, " F14=%02X",
                               buf->element[14].flags & 0xff);
                QETH_DBF_TEXT_(QERR, 2, " qerr=%X", qdio_error);
-               return 1;
+               if ((buf->element[15].flags & 0xff) == 0x12) {
+                       card->stats.rx_dropped++;
+                       return 0;
+               } else
+                       return 1;
        }
        return 0;
 }
@@ -2667,7 +2678,7 @@ static int qeth_handle_send_error(struct qeth_card *card,
                        qdio_err = 1;
                }
        }
-       qeth_check_qdio_errors(buffer->buffer, qdio_err, "qouterr");
+       qeth_check_qdio_errors(card, buffer->buffer, qdio_err, "qouterr");
 
        if (!qdio_err)
                return QETH_SEND_ERROR_NONE;
@@ -3509,6 +3520,7 @@ void qeth_tx_timeout(struct net_device *dev)
 {
        struct qeth_card *card;
 
+       QETH_DBF_TEXT(TRACE, 4, "txtimeo");
        card = dev->ml_priv;
        card->stats.tx_errors++;
        qeth_schedule_recovery(card);
@@ -3847,9 +3859,7 @@ static int qeth_core_driver_group(const char *buf, struct device *root_dev,
 
 int qeth_core_hardsetup_card(struct qeth_card *card)
 {
-       struct qdio_ssqd_desc *ssqd;
        int retries = 0;
-       int mpno = 0;
        int rc;
 
        QETH_DBF_TEXT(SETUP, 2, "hrdsetup");
@@ -3882,31 +3892,6 @@ retriable:
                else
                        goto retry;
        }
-
-       rc = qeth_get_unitaddr(card);
-       if (rc) {
-               QETH_DBF_TEXT_(SETUP, 2, "2err%d", rc);
-               return rc;
-       }
-
-       ssqd = kmalloc(sizeof(struct qdio_ssqd_desc), GFP_KERNEL);
-       if (!ssqd) {
-               rc = -ENOMEM;
-               goto out;
-       }
-       rc = qdio_get_ssqd_desc(CARD_DDEV(card), ssqd);
-       if (rc == 0)
-               mpno = ssqd->pcnt;
-       kfree(ssqd);
-
-       if (mpno)
-               mpno = min(mpno - 1, QETH_MAX_PORTNO);
-       if (card->info.portno > mpno) {
-               QETH_DBF_MESSAGE(2, "Device %s does not offer port number %d"
-                       "\n.", CARD_BUS_ID(card), card->info.portno);
-               rc = -ENODEV;
-               goto out;
-       }
        qeth_init_tokens(card);
        qeth_init_func_level(card);
        rc = qeth_idx_activate_channel(&card->read, qeth_idx_read_cb);
@@ -3990,7 +3975,7 @@ struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card,
        struct qdio_buffer_element *element = *__element;
        int offset = *__offset;
        struct sk_buff *skb = NULL;
-       int skb_len;
+       int skb_len = 0;
        void *data_ptr;
        int data_len;
        int headroom = 0;
@@ -4009,20 +3994,24 @@ struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card,
        *hdr = element->addr + offset;
 
        offset += sizeof(struct qeth_hdr);
-       if (card->options.layer2) {
-               if (card->info.type == QETH_CARD_TYPE_OSN) {
-                       skb_len = (*hdr)->hdr.osn.pdu_length;
-                       headroom = sizeof(struct qeth_hdr);
-               } else {
-                       skb_len = (*hdr)->hdr.l2.pkt_length;
-               }
-       } else {
+       switch ((*hdr)->hdr.l2.id) {
+       case QETH_HEADER_TYPE_LAYER2:
+               skb_len = (*hdr)->hdr.l2.pkt_length;
+               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;
+               break;
+       case QETH_HEADER_TYPE_OSN:
+               skb_len = (*hdr)->hdr.osn.pdu_length;
+               headroom = sizeof(struct qeth_hdr);
+               break;
+       default:
+               break;
        }
 
        if (!skb_len)
@@ -4177,6 +4166,33 @@ void qeth_core_free_discipline(struct qeth_card *card)
        card->discipline.ccwgdriver = NULL;
 }
 
+static void qeth_determine_capabilities(struct qeth_card *card)
+{
+       int rc;
+
+       QETH_DBF_TEXT(SETUP, 2, "detcapab");
+       rc = ccw_device_set_online(CARD_DDEV(card));
+       if (rc) {
+               QETH_DBF_TEXT_(SETUP, 2, "3err%d", rc);
+               goto out;
+       }
+
+       rc = qeth_get_unitaddr(card);
+       if (rc) {
+               QETH_DBF_TEXT_(SETUP, 2, "5err%d", rc);
+               goto out_offline;
+       }
+
+       rc = qdio_get_ssqd_desc(CARD_DDEV(card), &card->ssqd);
+       if (rc)
+               QETH_DBF_TEXT_(SETUP, 2, "6err%d", rc);
+
+out_offline:
+       ccw_device_set_offline(CARD_DDEV(card));
+out:
+       return;
+}
+
 static int qeth_core_probe_device(struct ccwgroup_device *gdev)
 {
        struct qeth_card *card;
@@ -4242,6 +4258,8 @@ static int qeth_core_probe_device(struct ccwgroup_device *gdev)
        write_lock_irqsave(&qeth_core_card_list.rwlock, flags);
        list_add_tail(&card->list, &qeth_core_card_list.list);
        write_unlock_irqrestore(&qeth_core_card_list.rwlock, flags);
+
+       qeth_determine_capabilities(card);
        return 0;
 
 err_card:
index 1ba5115..104a335 100644 (file)
@@ -156,6 +156,8 @@ enum qeth_ipa_return_codes {
        IPA_RC_IP_TABLE_FULL            = 0x0002,
        IPA_RC_UNKNOWN_ERROR            = 0x0003,
        IPA_RC_UNSUPPORTED_COMMAND      = 0x0004,
+       IPA_RC_TRACE_ALREADY_ACTIVE     = 0x0005,
+       IPA_RC_INVALID_FORMAT           = 0x0006,
        IPA_RC_DUP_IPV6_REMOTE          = 0x0008,
        IPA_RC_DUP_IPV6_HOME            = 0x0010,
        IPA_RC_UNREGISTERED_ADDR        = 0x0011,
@@ -196,6 +198,11 @@ enum qeth_ipa_return_codes {
        IPA_RC_INVALID_IP_VERSION2      = 0xf001,
        IPA_RC_FFFF                     = 0xffff
 };
+/* for DELIP */
+#define IPA_RC_IP_ADDRESS_NOT_DEFINED  IPA_RC_PRIMARY_ALREADY_DEFINED
+/* for SET_DIAGNOSTIC_ASSIST */
+#define IPA_RC_INVALID_SUBCMD          IPA_RC_IP_TABLE_FULL
+#define IPA_RC_HARDWARE_AUTH_ERROR     IPA_RC_UNKNOWN_ERROR
 
 /* IPA function flags; each flag marks availability of respective function */
 enum qeth_ipa_funcs {
@@ -246,6 +253,7 @@ enum qeth_ipa_setadp_cmd {
        IPA_SETADP_SET_SNMP_CONTROL             = 0x00000200L,
        IPA_SETADP_QUERY_CARD_INFO              = 0x00000400L,
        IPA_SETADP_SET_PROMISC_MODE             = 0x00000800L,
+       IPA_SETADP_SET_DIAG_ASSIST              = 0x00002000L,
        IPA_SETADP_SET_ACCESS_CONTROL           = 0x00010000L,
 };
 enum qeth_ipa_mac_ops {
@@ -424,6 +432,40 @@ struct qeth_create_destroy_address {
        __u8 unique_id[8];
 } __attribute__ ((packed));
 
+/* SET DIAGNOSTIC ASSIST IPA Command:   *************************************/
+
+enum qeth_diags_cmds {
+       QETH_DIAGS_CMD_QUERY    = 0x0001,
+       QETH_DIAGS_CMD_TRAP     = 0x0002,
+       QETH_DIAGS_CMD_TRACE    = 0x0004,
+       QETH_DIAGS_CMD_NOLOG    = 0x0008,
+       QETH_DIAGS_CMD_DUMP     = 0x0010,
+};
+
+enum qeth_diags_trace_types {
+       QETH_DIAGS_TYPE_HIPERSOCKET     = 0x02,
+};
+
+enum qeth_diags_trace_cmds {
+       QETH_DIAGS_CMD_TRACE_ENABLE     = 0x0001,
+       QETH_DIAGS_CMD_TRACE_DISABLE    = 0x0002,
+       QETH_DIAGS_CMD_TRACE_MODIFY     = 0x0004,
+       QETH_DIAGS_CMD_TRACE_REPLACE    = 0x0008,
+       QETH_DIAGS_CMD_TRACE_QUERY      = 0x0010,
+};
+
+struct qeth_ipacmd_diagass {
+       __u32  host_tod2;
+       __u32:32;
+       __u16  subcmd_len;
+       __u16:16;
+       __u32  subcmd;
+       __u8   type;
+       __u8   action;
+       __u16  options;
+       __u32:32;
+} __attribute__ ((packed));
+
 /* Header for each IPA command */
 struct qeth_ipacmd_hdr {
        __u8   command;
@@ -452,6 +494,7 @@ struct qeth_ipa_cmd {
                struct qeth_create_destroy_address      create_destroy_addr;
                struct qeth_ipacmd_setadpparms          setadapterparms;
                struct qeth_set_routing                 setrtg;
+               struct qeth_ipacmd_diagass              diagass;
        } data;
 } __attribute__ ((packed));
 
@@ -469,7 +512,6 @@ enum qeth_ipa_arp_return_codes {
        QETH_IPA_ARP_RC_Q_NO_DATA    = 0x0008,
 };
 
-
 extern char *qeth_get_ipa_msg(enum qeth_ipa_return_codes rc);
 extern char *qeth_get_ipa_cmd_name(enum qeth_ipa_cmds cmd);
 
index 9ff2b36..ac2239a 100644 (file)
@@ -118,7 +118,7 @@ static ssize_t qeth_dev_portno_store(struct device *dev,
 {
        struct qeth_card *card = dev_get_drvdata(dev);
        char *tmp;
-       unsigned int portno;
+       unsigned int portno, limit;
 
        if (!card)
                return -EINVAL;
@@ -128,9 +128,11 @@ static ssize_t qeth_dev_portno_store(struct device *dev,
                return -EPERM;
 
        portno = simple_strtoul(buf, &tmp, 16);
-       if (portno > QETH_MAX_PORTNO) {
+       if (portno > QETH_MAX_PORTNO)
+               return -EINVAL;
+       limit = (card->ssqd.pcnt ? card->ssqd.pcnt - 1 : card->ssqd.pcnt);
+       if (portno > limit)
                return -EINVAL;
-       }
 
        card->info.portno = portno;
        return count;
index 038299a..74ba388 100644 (file)
@@ -769,7 +769,8 @@ static void qeth_l2_qdio_input_handler(struct ccw_device *ccwdev,
                index = i % QDIO_MAX_BUFFERS_PER_Q;
                buffer = &card->qdio.in_q->bufs[index];
                if (!(qdio_err &&
-                     qeth_check_qdio_errors(buffer->buffer, qdio_err, "qinerr")))
+                     qeth_check_qdio_errors(card, buffer->buffer, qdio_err,
+                                            "qinerr")))
                        qeth_l2_process_inbound_buffer(card, buffer, index);
                /* clear buffer and give back to hardware */
                qeth_put_buffer_pool_entry(card, buffer->pool_entry);
index 321988f..8447d23 100644 (file)
@@ -13,6 +13,8 @@
 
 #include "qeth_core.h"
 
+#define QETH_SNIFF_AVAIL       0x0008
+
 struct qeth_ipaddr {
        struct list_head entry;
        enum qeth_ip_types type;
index fd1b6ed..337d03f 100644 (file)
@@ -242,6 +242,8 @@ static int __qeth_l3_insert_ip_todo(struct qeth_card *card,
        struct qeth_ipaddr *tmp, *t;
        int found = 0;
 
+       if (card->options.sniffer)
+               return 0;
        list_for_each_entry_safe(tmp, t, card->ip_tbd_list, entry) {
                if ((addr->type == QETH_IP_TYPE_DEL_ALL_MC) &&
                    (tmp->type == QETH_IP_TYPE_DEL_ALL_MC))
@@ -457,6 +459,8 @@ static void qeth_l3_set_ip_addr_list(struct qeth_card *card)
        QETH_DBF_TEXT(TRACE, 2, "sdiplist");
        QETH_DBF_HEX(TRACE, 2, &card, sizeof(void *));
 
+       if (card->options.sniffer)
+               return;
        spin_lock_irqsave(&card->ip_lock, flags);
        tbd_list = card->ip_tbd_list;
        card->ip_tbd_list = kmalloc(sizeof(struct list_head), GFP_ATOMIC);
@@ -495,7 +499,7 @@ static void qeth_l3_set_ip_addr_list(struct qeth_card *card)
                        spin_unlock_irqrestore(&card->ip_lock, flags);
                        rc = qeth_l3_deregister_addr_entry(card, addr);
                        spin_lock_irqsave(&card->ip_lock, flags);
-                       if (!rc || (rc == IPA_RC_PRIMARY_ALREADY_DEFINED))
+                       if (!rc || (rc == IPA_RC_IP_ADDRESS_NOT_DEFINED))
                                kfree(addr);
                        else
                                list_add_tail(&addr->entry, &card->ip_list);
@@ -513,6 +517,8 @@ static void qeth_l3_clear_ip_list(struct qeth_card *card, int clean,
        unsigned long flags;
 
        QETH_DBF_TEXT(TRACE, 4, "clearip");
+       if (recover && card->options.sniffer)
+               return;
        spin_lock_irqsave(&card->ip_lock, flags);
        /* clear todo list */
        list_for_each_entry_safe(addr, tmp, card->ip_tbd_list, entry) {
@@ -1674,6 +1680,76 @@ static int qeth_l3_get_unique_id(struct qeth_card *card)
        return rc;
 }
 
+static int
+qeth_diags_trace_cb(struct qeth_card *card, struct qeth_reply *reply,
+                           unsigned long data)
+{
+       struct qeth_ipa_cmd        *cmd;
+       __u16 rc;
+
+       QETH_DBF_TEXT(SETUP, 2, "diastrcb");
+
+       cmd = (struct qeth_ipa_cmd *)data;
+       rc = cmd->hdr.return_code;
+       if (rc) {
+               QETH_DBF_TEXT_(TRACE, 2, "dxter%x", rc);
+               if (cmd->data.diagass.action == QETH_DIAGS_CMD_TRACE_ENABLE) {
+                       switch (rc) {
+                       case IPA_RC_HARDWARE_AUTH_ERROR:
+                               dev_warn(&card->gdev->dev, "The device is not "
+                                       "authorized to run as a HiperSockets "
+                                       "network traffic analyzer\n");
+                               break;
+                       case IPA_RC_TRACE_ALREADY_ACTIVE:
+                               dev_warn(&card->gdev->dev, "A HiperSockets "
+                                       "network traffic analyzer is already "
+                                       "active in the HiperSockets LAN\n");
+                               break;
+                       default:
+                               break;
+                       }
+               }
+               return 0;
+       }
+
+       switch (cmd->data.diagass.action) {
+       case QETH_DIAGS_CMD_TRACE_QUERY:
+               break;
+       case QETH_DIAGS_CMD_TRACE_DISABLE:
+               card->info.promisc_mode = SET_PROMISC_MODE_OFF;
+               dev_info(&card->gdev->dev, "The HiperSockets network traffic "
+                       "analyzer is deactivated\n");
+               break;
+       case QETH_DIAGS_CMD_TRACE_ENABLE:
+               card->info.promisc_mode = SET_PROMISC_MODE_ON;
+               dev_info(&card->gdev->dev, "The HiperSockets network traffic "
+                       "analyzer is activated\n");
+               break;
+       default:
+               QETH_DBF_MESSAGE(2, "Unknown sniffer action (0x%04x) on %s\n",
+                       cmd->data.diagass.action, QETH_CARD_IFNAME(card));
+       }
+
+       return 0;
+}
+
+static int
+qeth_diags_trace(struct qeth_card *card, enum qeth_diags_trace_cmds diags_cmd)
+{
+       struct qeth_cmd_buffer *iob;
+       struct qeth_ipa_cmd    *cmd;
+
+       QETH_DBF_TEXT(SETUP, 2, "diagtrac");
+
+       iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SET_DIAG_ASS, 0);
+       cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+       cmd->data.diagass.subcmd_len = 16;
+       cmd->data.diagass.subcmd = QETH_DIAGS_CMD_TRACE;
+       cmd->data.diagass.type = QETH_DIAGS_TYPE_HIPERSOCKET;
+       cmd->data.diagass.action = diags_cmd;
+       return qeth_send_ipa_cmd(card, iob, qeth_diags_trace_cb, NULL);
+}
+
 static void qeth_l3_get_mac_for_ipm(__u32 ipm, char *mac,
                                struct net_device *dev)
 {
@@ -1951,7 +2027,10 @@ static inline __u16 qeth_l3_rebuild_skb(struct qeth_card *card,
                case QETH_CAST_ANYCAST:
                case QETH_CAST_NOCAST:
                default:
-                       skb->pkt_type = PACKET_HOST;
+                       if (card->options.sniffer)
+                               skb->pkt_type = PACKET_OTHERHOST;
+                       else
+                               skb->pkt_type = PACKET_HOST;
                        memcpy(tg_addr, card->dev->dev_addr,
                                card->dev->addr_len);
                }
@@ -2007,7 +2086,6 @@ static void qeth_l3_process_inbound_buffer(struct qeth_card *card,
        int offset;
        __u16 vlan_tag = 0;
        unsigned int len;
-
        /* get first element of current buffer */
        element = (struct qdio_buffer_element *)&buf->buffer->element[0];
        offset = 0;
@@ -2026,7 +2104,7 @@ static void qeth_l3_process_inbound_buffer(struct qeth_card *card,
                case QETH_HEADER_TYPE_LAYER3:
                        vlan_tag = qeth_l3_rebuild_skb(card, skb, hdr);
                        len = skb->len;
-                       if (vlan_tag)
+                       if (vlan_tag && !card->options.sniffer)
                                if (card->vlangrp)
                                        vlan_hwaccel_rx(skb, card->vlangrp,
                                                vlan_tag);
@@ -2037,6 +2115,16 @@ static void qeth_l3_process_inbound_buffer(struct qeth_card *card,
                        else
                                netif_rx(skb);
                        break;
+               case QETH_HEADER_TYPE_LAYER2: /* for HiperSockets sniffer */
+                       skb->pkt_type = PACKET_HOST;
+                       skb->protocol = eth_type_trans(skb, skb->dev);
+                       if (card->options.checksum_type == NO_CHECKSUMMING)
+                               skb->ip_summed = CHECKSUM_UNNECESSARY;
+                       else
+                               skb->ip_summed = CHECKSUM_NONE;
+                       len = skb->len;
+                       netif_receive_skb(skb);
+                       break;
                default:
                        dev_kfree_skb_any(skb);
                        QETH_DBF_TEXT(TRACE, 3, "inbunkno");
@@ -2118,6 +2206,9 @@ static int qeth_l3_stop_card(struct qeth_card *card, int recovery_mode)
        QETH_DBF_HEX(SETUP, 2, &card, sizeof(void *));
 
        qeth_set_allowed_threads(card, 0, 1);
+       if (card->options.sniffer &&
+           (card->info.promisc_mode == SET_PROMISC_MODE_ON))
+               qeth_diags_trace(card, QETH_DIAGS_CMD_TRACE_DISABLE);
        if (card->read.state == CH_STATE_UP &&
            card->write.state == CH_STATE_UP &&
            (card->state == CARD_STATE_UP)) {
@@ -2162,6 +2253,36 @@ static int qeth_l3_stop_card(struct qeth_card *card, int recovery_mode)
        return rc;
 }
 
+/*
+ * test for and Switch promiscuous mode (on or off)
+ *  either for guestlan or HiperSocket Sniffer
+ */
+static void
+qeth_l3_handle_promisc_mode(struct qeth_card *card)
+{
+       struct net_device *dev = card->dev;
+
+       if (((dev->flags & IFF_PROMISC) &&
+            (card->info.promisc_mode == SET_PROMISC_MODE_ON)) ||
+           (!(dev->flags & IFF_PROMISC) &&
+            (card->info.promisc_mode == SET_PROMISC_MODE_OFF)))
+               return;
+
+       if (card->info.guestlan) {              /* Guestlan trace */
+               if (qeth_adp_supported(card, IPA_SETADP_SET_PROMISC_MODE))
+                       qeth_setadp_promisc_mode(card);
+       } else if (card->options.sniffer &&     /* HiperSockets trace */
+                  qeth_adp_supported(card, IPA_SETADP_SET_DIAG_ASSIST)) {
+               if (dev->flags & IFF_PROMISC) {
+                       QETH_DBF_TEXT(TRACE, 3, "+promisc");
+                       qeth_diags_trace(card, QETH_DIAGS_CMD_TRACE_ENABLE);
+               } else {
+                       QETH_DBF_TEXT(TRACE, 3, "-promisc");
+                       qeth_diags_trace(card, QETH_DIAGS_CMD_TRACE_DISABLE);
+               }
+       }
+}
+
 static void qeth_l3_set_multicast_list(struct net_device *dev)
 {
        struct qeth_card *card = dev->ml_priv;
@@ -2170,15 +2291,17 @@ static void qeth_l3_set_multicast_list(struct net_device *dev)
        if (qeth_threads_running(card, QETH_RECOVER_THREAD) &&
            (card->state != CARD_STATE_UP))
                return;
-       qeth_l3_delete_mc_addresses(card);
-       qeth_l3_add_multicast_ipv4(card);
+       if (!card->options.sniffer) {
+               qeth_l3_delete_mc_addresses(card);
+               qeth_l3_add_multicast_ipv4(card);
 #ifdef CONFIG_QETH_IPV6
-       qeth_l3_add_multicast_ipv6(card);
+               qeth_l3_add_multicast_ipv6(card);
 #endif
-       qeth_l3_set_ip_addr_list(card);
-       if (!qeth_adp_supported(card, IPA_SETADP_SET_PROMISC_MODE))
-               return;
-       qeth_setadp_promisc_mode(card);
+               qeth_l3_set_ip_addr_list(card);
+               if (!qeth_adp_supported(card, IPA_SETADP_SET_PROMISC_MODE))
+                       return;
+       }
+       qeth_l3_handle_promisc_mode(card);
 }
 
 static const char *qeth_l3_arp_get_error_cause(int *rc)
@@ -2778,8 +2901,9 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
        int nr_frags;
 
        if ((card->info.type == QETH_CARD_TYPE_IQD) &&
-           (skb->protocol != htons(ETH_P_IPV6)) &&
-           (skb->protocol != htons(ETH_P_IP)))
+           (((skb->protocol != htons(ETH_P_IPV6)) &&
+             (skb->protocol != htons(ETH_P_IP))) ||
+            card->options.sniffer))
                        goto tx_drop;
 
        if ((card->state != CARD_STATE_UP) || !card->lan_online) {
@@ -3155,7 +3279,7 @@ static void qeth_l3_qdio_input_handler(struct ccw_device *ccwdev,
                index = i % QDIO_MAX_BUFFERS_PER_Q;
                buffer = &card->qdio.in_q->bufs[index];
                if (!(qdio_err &&
-                     qeth_check_qdio_errors(buffer->buffer,
+                     qeth_check_qdio_errors(card, buffer->buffer,
                                             qdio_err, "qinerr")))
                        qeth_l3_process_inbound_buffer(card, buffer, index);
                /* clear buffer and give back to hardware */
@@ -3250,20 +3374,22 @@ static int __qeth_l3_set_online(struct ccwgroup_device *gdev, int recovery_mode)
                goto out_remove;
        } else
                card->lan_online = 1;
-       qeth_l3_set_large_send(card, card->options.large_send);
 
        rc = qeth_l3_setadapter_parms(card);
        if (rc)
                QETH_DBF_TEXT_(SETUP, 2, "2err%d", rc);
-       rc = qeth_l3_start_ipassists(card);
-       if (rc)
-               QETH_DBF_TEXT_(SETUP, 2, "3err%d", rc);
-       rc = qeth_l3_setrouting_v4(card);
-       if (rc)
-               QETH_DBF_TEXT_(SETUP, 2, "4err%d", rc);
-       rc = qeth_l3_setrouting_v6(card);
-       if (rc)
-               QETH_DBF_TEXT_(SETUP, 2, "5err%d", rc);
+       if (!card->options.sniffer) {
+               rc = qeth_l3_start_ipassists(card);
+               if (rc)
+                       QETH_DBF_TEXT_(SETUP, 2, "3err%d", rc);
+               qeth_l3_set_large_send(card, card->options.large_send);
+               rc = qeth_l3_setrouting_v4(card);
+               if (rc)
+                       QETH_DBF_TEXT_(SETUP, 2, "4err%d", rc);
+               rc = qeth_l3_setrouting_v6(card);
+               if (rc)
+                       QETH_DBF_TEXT_(SETUP, 2, "5err%d", rc);
+       }
        netif_tx_disable(card->dev);
 
        rc = qeth_init_qdio_queues(card);
index 3360b09..3f08b11 100644 (file)
@@ -319,6 +319,61 @@ static ssize_t qeth_l3_dev_checksum_store(struct device *dev,
 static DEVICE_ATTR(checksumming, 0644, qeth_l3_dev_checksum_show,
                qeth_l3_dev_checksum_store);
 
+static ssize_t qeth_l3_dev_sniffer_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct qeth_card *card = dev_get_drvdata(dev);
+
+       if (!card)
+               return -EINVAL;
+
+       return sprintf(buf, "%i\n", card->options.sniffer ? 1 : 0);
+}
+
+static ssize_t qeth_l3_dev_sniffer_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct qeth_card *card = dev_get_drvdata(dev);
+       int ret;
+       unsigned long i;
+
+       if (!card)
+               return -EINVAL;
+
+       if (card->info.type != QETH_CARD_TYPE_IQD)
+               return -EPERM;
+
+       if ((card->state != CARD_STATE_DOWN) &&
+           (card->state != CARD_STATE_RECOVER))
+               return -EPERM;
+
+       ret = strict_strtoul(buf, 16, &i);
+       if (ret)
+               return -EINVAL;
+       switch (i) {
+       case 0:
+               card->options.sniffer = i;
+               break;
+       case 1:
+               ret = qdio_get_ssqd_desc(CARD_DDEV(card), &card->ssqd);
+               if (card->ssqd.qdioac2 & QETH_SNIFF_AVAIL) {
+                       card->options.sniffer = i;
+                       if (card->qdio.init_pool.buf_count !=
+                                       QETH_IN_BUF_COUNT_MAX)
+                               qeth_realloc_buffer_pool(card,
+                                       QETH_IN_BUF_COUNT_MAX);
+                       break;
+               } else
+                       return -EPERM;
+       default:   /* fall through */
+               return -EINVAL;
+       }
+       return count;
+}
+
+static DEVICE_ATTR(sniffer, 0644, qeth_l3_dev_sniffer_show,
+               qeth_l3_dev_sniffer_store);
+
 static ssize_t qeth_l3_dev_large_send_show(struct device *dev,
                                struct device_attribute *attr, char *buf)
 {
@@ -373,6 +428,7 @@ static struct attribute *qeth_l3_device_attrs[] = {
        &dev_attr_broadcast_mode.attr,
        &dev_attr_canonical_macaddr.attr,
        &dev_attr_checksumming.attr,
+       &dev_attr_sniffer.attr,
        &dev_attr_large_send.attr,
        NULL,
 };