| /* |
| * |
| * This file is provided under a dual BSD/GPLv2 license. When using or |
| * redistributing this file, you may do so under either license. |
| * |
| * GPL LICENSE SUMMARY |
| * |
| * Copyright(c) 2015 Intel Corporation. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of version 2 of the GNU General Public License as |
| * published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, but |
| * WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * General Public License for more details. |
| * |
| * BSD LICENSE |
| * |
| * Copyright(c) 2015 Intel Corporation. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * - Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * - Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in |
| * the documentation and/or other materials provided with the |
| * distribution. |
| * - Neither the name of Intel Corporation nor the names of its |
| * contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| * |
| */ |
| |
| /* |
| * This file contains support for diagnostic functions. It is accessed by |
| * opening the hfi1_diag device, normally minor number 129. Diagnostic use |
| * of the chip may render the chip or board unusable until the driver |
| * is unloaded, or in some cases, until the system is rebooted. |
| * |
| * Accesses to the chip through this interface are not similar to going |
| * through the /sys/bus/pci resource mmap interface. |
| */ |
| |
| #include <linux/io.h> |
| #include <linux/pci.h> |
| #include <linux/poll.h> |
| #include <linux/vmalloc.h> |
| #include <linux/export.h> |
| #include <linux/fs.h> |
| #include <linux/uaccess.h> |
| #include <linux/module.h> |
| #include <rdma/ib_smi.h> |
| #include "hfi.h" |
| #include "device.h" |
| #include "common.h" |
| #include "trace.h" |
| |
| #undef pr_fmt |
| #define pr_fmt(fmt) DRIVER_NAME ": " fmt |
| #define snoop_dbg(fmt, ...) \ |
| hfi1_cdbg(SNOOP, fmt, ##__VA_ARGS__) |
| |
| /* Snoop option mask */ |
| #define SNOOP_DROP_SEND (1 << 0) |
| #define SNOOP_USE_METADATA (1 << 1) |
| |
| static u8 snoop_flags; |
| |
| /* |
| * Extract packet length from LRH header. |
| * Why & 0x7FF? Because len is only 11 bits in case it wasn't 0'd we throw the |
| * bogus bits away. This is in Dwords so multiply by 4 to get size in bytes |
| */ |
| #define HFI1_GET_PKT_LEN(x) (((be16_to_cpu((x)->lrh[2]) & 0x7FF)) << 2) |
| |
| enum hfi1_filter_status { |
| HFI1_FILTER_HIT, |
| HFI1_FILTER_ERR, |
| HFI1_FILTER_MISS |
| }; |
| |
| /* snoop processing functions */ |
| rhf_rcv_function_ptr snoop_rhf_rcv_functions[8] = { |
| [RHF_RCV_TYPE_EXPECTED] = snoop_recv_handler, |
| [RHF_RCV_TYPE_EAGER] = snoop_recv_handler, |
| [RHF_RCV_TYPE_IB] = snoop_recv_handler, |
| [RHF_RCV_TYPE_ERROR] = snoop_recv_handler, |
| [RHF_RCV_TYPE_BYPASS] = snoop_recv_handler, |
| [RHF_RCV_TYPE_INVALID5] = process_receive_invalid, |
| [RHF_RCV_TYPE_INVALID6] = process_receive_invalid, |
| [RHF_RCV_TYPE_INVALID7] = process_receive_invalid |
| }; |
| |
| /* Snoop packet structure */ |
| struct snoop_packet { |
| struct list_head list; |
| u32 total_len; |
| u8 data[]; |
| }; |
| |
| /* Do not make these an enum or it will blow up the capture_md */ |
| #define PKT_DIR_EGRESS 0x0 |
| #define PKT_DIR_INGRESS 0x1 |
| |
| /* Packet capture metadata returned to the user with the packet. */ |
| struct capture_md { |
| u8 port; |
| u8 dir; |
| u8 reserved[6]; |
| union { |
| u64 pbc; |
| u64 rhf; |
| } u; |
| }; |
| |
| static atomic_t diagpkt_count = ATOMIC_INIT(0); |
| static struct cdev diagpkt_cdev; |
| static struct device *diagpkt_device; |
| |
| static ssize_t diagpkt_write(struct file *fp, const char __user *data, |
| size_t count, loff_t *off); |
| |
| static const struct file_operations diagpkt_file_ops = { |
| .owner = THIS_MODULE, |
| .write = diagpkt_write, |
| .llseek = noop_llseek, |
| }; |
| |
| /* |
| * This is used for communication with user space for snoop extended IOCTLs |
| */ |
| struct hfi1_link_info { |
| __be64 node_guid; |
| u8 port_mode; |
| u8 port_state; |
| u16 link_speed_active; |
| u16 link_width_active; |
| u16 vl15_init; |
| u8 port_number; |
| /* |
| * Add padding to make this a full IB SMP payload. Note: changing the |
| * size of this structure will make the IOCTLs created with _IOWR |
| * change. |
| * Be sure to run tests on all IOCTLs when making changes to this |
| * structure. |
| */ |
| u8 res[47]; |
| }; |
| |
| /* |
| * This starts our ioctl sequence numbers *way* off from the ones |
| * defined in ib_core. |
| */ |
| #define SNOOP_CAPTURE_VERSION 0x1 |
| |
| #define IB_IOCTL_MAGIC 0x1b /* See Documentation/ioctl-number.txt */ |
| #define HFI1_SNOOP_IOC_MAGIC IB_IOCTL_MAGIC |
| #define HFI1_SNOOP_IOC_BASE_SEQ 0x80 |
| |
| #define HFI1_SNOOP_IOCGETLINKSTATE \ |
| _IO(HFI1_SNOOP_IOC_MAGIC, HFI1_SNOOP_IOC_BASE_SEQ) |
| #define HFI1_SNOOP_IOCSETLINKSTATE \ |
| _IO(HFI1_SNOOP_IOC_MAGIC, HFI1_SNOOP_IOC_BASE_SEQ+1) |
| #define HFI1_SNOOP_IOCCLEARQUEUE \ |
| _IO(HFI1_SNOOP_IOC_MAGIC, HFI1_SNOOP_IOC_BASE_SEQ+2) |
| #define HFI1_SNOOP_IOCCLEARFILTER \ |
| _IO(HFI1_SNOOP_IOC_MAGIC, HFI1_SNOOP_IOC_BASE_SEQ+3) |
| #define HFI1_SNOOP_IOCSETFILTER \ |
| _IO(HFI1_SNOOP_IOC_MAGIC, HFI1_SNOOP_IOC_BASE_SEQ+4) |
| #define HFI1_SNOOP_IOCGETVERSION \ |
| _IO(HFI1_SNOOP_IOC_MAGIC, HFI1_SNOOP_IOC_BASE_SEQ+5) |
| #define HFI1_SNOOP_IOCSET_OPTS \ |
| _IO(HFI1_SNOOP_IOC_MAGIC, HFI1_SNOOP_IOC_BASE_SEQ+6) |
| |
| /* |
| * These offsets +6/+7 could change, but these are already known and used |
| * IOCTL numbers so don't change them without a good reason. |
| */ |
| #define HFI1_SNOOP_IOCGETLINKSTATE_EXTRA \ |
| _IOWR(HFI1_SNOOP_IOC_MAGIC, HFI1_SNOOP_IOC_BASE_SEQ+6, \ |
| struct hfi1_link_info) |
| #define HFI1_SNOOP_IOCSETLINKSTATE_EXTRA \ |
| _IOWR(HFI1_SNOOP_IOC_MAGIC, HFI1_SNOOP_IOC_BASE_SEQ+7, \ |
| struct hfi1_link_info) |
| |
| static int hfi1_snoop_open(struct inode *in, struct file *fp); |
| static ssize_t hfi1_snoop_read(struct file *fp, char __user *data, |
| size_t pkt_len, loff_t *off); |
| static ssize_t hfi1_snoop_write(struct file *fp, const char __user *data, |
| size_t count, loff_t *off); |
| static long hfi1_ioctl(struct file *fp, unsigned int cmd, unsigned long arg); |
| static unsigned int hfi1_snoop_poll(struct file *fp, |
| struct poll_table_struct *wait); |
| static int hfi1_snoop_release(struct inode *in, struct file *fp); |
| |
| struct hfi1_packet_filter_command { |
| int opcode; |
| int length; |
| void *value_ptr; |
| }; |
| |
| /* Can't re-use PKT_DIR_*GRESS here because 0 means no packets for this */ |
| #define HFI1_SNOOP_INGRESS 0x1 |
| #define HFI1_SNOOP_EGRESS 0x2 |
| |
| enum hfi1_packet_filter_opcodes { |
| FILTER_BY_LID, |
| FILTER_BY_DLID, |
| FILTER_BY_MAD_MGMT_CLASS, |
| FILTER_BY_QP_NUMBER, |
| FILTER_BY_PKT_TYPE, |
| FILTER_BY_SERVICE_LEVEL, |
| FILTER_BY_PKEY, |
| FILTER_BY_DIRECTION, |
| }; |
| |
| static const struct file_operations snoop_file_ops = { |
| .owner = THIS_MODULE, |
| .open = hfi1_snoop_open, |
| .read = hfi1_snoop_read, |
| .unlocked_ioctl = hfi1_ioctl, |
| .poll = hfi1_snoop_poll, |
| .write = hfi1_snoop_write, |
| .release = hfi1_snoop_release |
| }; |
| |
| struct hfi1_filter_array { |
| int (*filter)(void *, void *, void *); |
| }; |
| |
| static int hfi1_filter_lid(void *ibhdr, void *packet_data, void *value); |
| static int hfi1_filter_dlid(void *ibhdr, void *packet_data, void *value); |
| static int hfi1_filter_mad_mgmt_class(void *ibhdr, void *packet_data, |
| void *value); |
| static int hfi1_filter_qp_number(void *ibhdr, void *packet_data, void *value); |
| static int hfi1_filter_ibpacket_type(void *ibhdr, void *packet_data, |
| void *value); |
| static int hfi1_filter_ib_service_level(void *ibhdr, void *packet_data, |
| void *value); |
| static int hfi1_filter_ib_pkey(void *ibhdr, void *packet_data, void *value); |
| static int hfi1_filter_direction(void *ibhdr, void *packet_data, void *value); |
| |
| static struct hfi1_filter_array hfi1_filters[] = { |
| { hfi1_filter_lid }, |
| { hfi1_filter_dlid }, |
| { hfi1_filter_mad_mgmt_class }, |
| { hfi1_filter_qp_number }, |
| { hfi1_filter_ibpacket_type }, |
| { hfi1_filter_ib_service_level }, |
| { hfi1_filter_ib_pkey }, |
| { hfi1_filter_direction }, |
| }; |
| |
| #define HFI1_MAX_FILTERS ARRAY_SIZE(hfi1_filters) |
| #define HFI1_DIAG_MINOR_BASE 129 |
| |
| static int hfi1_snoop_add(struct hfi1_devdata *dd, const char *name); |
| |
| int hfi1_diag_add(struct hfi1_devdata *dd) |
| { |
| char name[16]; |
| int ret = 0; |
| |
| snprintf(name, sizeof(name), "%s_diagpkt%d", class_name(), |
| dd->unit); |
| /* |
| * Do this for each device as opposed to the normal diagpkt |
| * interface which is one per host |
| */ |
| ret = hfi1_snoop_add(dd, name); |
| if (ret) |
| dd_dev_err(dd, "Unable to init snoop/capture device"); |
| |
| snprintf(name, sizeof(name), "%s_diagpkt", class_name()); |
| if (atomic_inc_return(&diagpkt_count) == 1) { |
| ret = hfi1_cdev_init(HFI1_DIAGPKT_MINOR, name, |
| &diagpkt_file_ops, &diagpkt_cdev, |
| &diagpkt_device, false); |
| } |
| |
| return ret; |
| } |
| |
| /* this must be called w/ dd->snoop_in_lock held */ |
| static void drain_snoop_list(struct list_head *queue) |
| { |
| struct list_head *pos, *q; |
| struct snoop_packet *packet; |
| |
| list_for_each_safe(pos, q, queue) { |
| packet = list_entry(pos, struct snoop_packet, list); |
| list_del(pos); |
| kfree(packet); |
| } |
| } |
| |
| static void hfi1_snoop_remove(struct hfi1_devdata *dd) |
| { |
| unsigned long flags = 0; |
| |
| spin_lock_irqsave(&dd->hfi1_snoop.snoop_lock, flags); |
| drain_snoop_list(&dd->hfi1_snoop.queue); |
| hfi1_cdev_cleanup(&dd->hfi1_snoop.cdev, &dd->hfi1_snoop.class_dev); |
| spin_unlock_irqrestore(&dd->hfi1_snoop.snoop_lock, flags); |
| } |
| |
| void hfi1_diag_remove(struct hfi1_devdata *dd) |
| { |
| |
| hfi1_snoop_remove(dd); |
| if (atomic_dec_and_test(&diagpkt_count)) |
| hfi1_cdev_cleanup(&diagpkt_cdev, &diagpkt_device); |
| hfi1_cdev_cleanup(&dd->diag_cdev, &dd->diag_device); |
| } |
| |
| |
| /* |
| * Allocated structure shared between the credit return mechanism and |
| * diagpkt_send(). |
| */ |
| struct diagpkt_wait { |
| struct completion credits_returned; |
| int code; |
| atomic_t count; |
| }; |
| |
| /* |
| * When each side is finished with the structure, they call this. |
| * The last user frees the structure. |
| */ |
| static void put_diagpkt_wait(struct diagpkt_wait *wait) |
| { |
| if (atomic_dec_and_test(&wait->count)) |
| kfree(wait); |
| } |
| |
| /* |
| * Callback from the credit return code. Set the complete, which |
| * will let diapkt_send() continue. |
| */ |
| static void diagpkt_complete(void *arg, int code) |
| { |
| struct diagpkt_wait *wait = (struct diagpkt_wait *)arg; |
| |
| wait->code = code; |
| complete(&wait->credits_returned); |
| put_diagpkt_wait(wait); /* finished with the structure */ |
| } |
| |
| /** |
| * diagpkt_send - send a packet |
| * @dp: diag packet descriptor |
| */ |
| static ssize_t diagpkt_send(struct diag_pkt *dp) |
| { |
| struct hfi1_devdata *dd; |
| struct send_context *sc; |
| struct pio_buf *pbuf; |
| u32 *tmpbuf = NULL; |
| ssize_t ret = 0; |
| u32 pkt_len, total_len; |
| pio_release_cb credit_cb = NULL; |
| void *credit_arg = NULL; |
| struct diagpkt_wait *wait = NULL; |
| |
| dd = hfi1_lookup(dp->unit); |
| if (!dd || !(dd->flags & HFI1_PRESENT) || !dd->kregbase) { |
| ret = -ENODEV; |
| goto bail; |
| } |
| if (!(dd->flags & HFI1_INITTED)) { |
| /* no hardware, freeze, etc. */ |
| ret = -ENODEV; |
| goto bail; |
| } |
| |
| if (dp->version != _DIAG_PKT_VERS) { |
| dd_dev_err(dd, "Invalid version %u for diagpkt_write\n", |
| dp->version); |
| ret = -EINVAL; |
| goto bail; |
| } |
| |
| /* send count must be an exact number of dwords */ |
| if (dp->len & 3) { |
| ret = -EINVAL; |
| goto bail; |
| } |
| |
| /* there is only port 1 */ |
| if (dp->port != 1) { |
| ret = -EINVAL; |
| goto bail; |
| } |
| |
| /* need a valid context */ |
| if (dp->sw_index >= dd->num_send_contexts) { |
| ret = -EINVAL; |
| goto bail; |
| } |
| /* can only use kernel contexts */ |
| if (dd->send_contexts[dp->sw_index].type != SC_KERNEL) { |
| ret = -EINVAL; |
| goto bail; |
| } |
| /* must be allocated */ |
| sc = dd->send_contexts[dp->sw_index].sc; |
| if (!sc) { |
| ret = -EINVAL; |
| goto bail; |
| } |
| /* must be enabled */ |
| if (!(sc->flags & SCF_ENABLED)) { |
| ret = -EINVAL; |
| goto bail; |
| } |
| |
| /* allocate a buffer and copy the data in */ |
| tmpbuf = vmalloc(dp->len); |
| if (!tmpbuf) { |
| ret = -ENOMEM; |
| goto bail; |
| } |
| |
| if (copy_from_user(tmpbuf, |
| (const void __user *) (unsigned long) dp->data, |
| dp->len)) { |
| ret = -EFAULT; |
| goto bail; |
| } |
| |
| /* |
| * pkt_len is how much data we have to write, includes header and data. |
| * total_len is length of the packet in Dwords plus the PBC should not |
| * include the CRC. |
| */ |
| pkt_len = dp->len >> 2; |
| total_len = pkt_len + 2; /* PBC + packet */ |
| |
| /* if 0, fill in a default */ |
| if (dp->pbc == 0) { |
| struct hfi1_pportdata *ppd = dd->pport; |
| |
| hfi1_cdbg(PKT, "Generating PBC"); |
| dp->pbc = create_pbc(ppd, 0, 0, 0, total_len); |
| } else { |
| hfi1_cdbg(PKT, "Using passed in PBC"); |
| } |
| |
| hfi1_cdbg(PKT, "Egress PBC content is 0x%llx", dp->pbc); |
| |
| /* |
| * The caller wants to wait until the packet is sent and to |
| * check for errors. The best we can do is wait until |
| * the buffer credits are returned and check if any packet |
| * error has occurred. If there are any late errors, this |
| * could miss it. If there are other senders who generate |
| * an error, this may find it. However, in general, it |
| * should catch most. |
| */ |
| if (dp->flags & F_DIAGPKT_WAIT) { |
| /* always force a credit return */ |
| dp->pbc |= PBC_CREDIT_RETURN; |
| /* turn on credit return interrupts */ |
| sc_add_credit_return_intr(sc); |
| wait = kmalloc(sizeof(*wait), GFP_KERNEL); |
| if (!wait) { |
| ret = -ENOMEM; |
| goto bail; |
| } |
| init_completion(&wait->credits_returned); |
| atomic_set(&wait->count, 2); |
| wait->code = PRC_OK; |
| |
| credit_cb = diagpkt_complete; |
| credit_arg = wait; |
| } |
| |
| pbuf = sc_buffer_alloc(sc, total_len, credit_cb, credit_arg); |
| if (!pbuf) { |
| /* |
| * No send buffer means no credit callback. Undo |
| * the wait set-up that was done above. We free wait |
| * because the callback will never be called. |
| */ |
| if (dp->flags & F_DIAGPKT_WAIT) { |
| sc_del_credit_return_intr(sc); |
| kfree(wait); |
| wait = NULL; |
| } |
| ret = -ENOSPC; |
| goto bail; |
| } |
| |
| pio_copy(dd, pbuf, dp->pbc, tmpbuf, pkt_len); |
| /* no flush needed as the HW knows the packet size */ |
| |
| ret = sizeof(*dp); |
| |
| if (dp->flags & F_DIAGPKT_WAIT) { |
| /* wait for credit return */ |
| ret = wait_for_completion_interruptible( |
| &wait->credits_returned); |
| /* |
| * If the wait returns an error, the wait was interrupted, |
| * e.g. with a ^C in the user program. The callback is |
| * still pending. This is OK as the wait structure is |
| * kmalloc'ed and the structure will free itself when |
| * all users are done with it. |
| * |
| * A context disable occurs on a send context restart, so |
| * include that in the list of errors below to check for. |
| * NOTE: PRC_FILL_ERR is at best informational and cannot |
| * be depended on. |
| */ |
| if (!ret && (((wait->code & PRC_STATUS_ERR) |
| || (wait->code & PRC_FILL_ERR) |
| || (wait->code & PRC_SC_DISABLE)))) |
| ret = -EIO; |
| |
| put_diagpkt_wait(wait); /* finished with the structure */ |
| sc_del_credit_return_intr(sc); |
| } |
| |
| bail: |
| vfree(tmpbuf); |
| return ret; |
| } |
| |
| static ssize_t diagpkt_write(struct file *fp, const char __user *data, |
| size_t count, loff_t *off) |
| { |
| struct hfi1_devdata *dd; |
| struct send_context *sc; |
| u8 vl; |
| |
| struct diag_pkt dp; |
| |
| if (count != sizeof(dp)) |
| return -EINVAL; |
| |
| if (copy_from_user(&dp, data, sizeof(dp))) |
| return -EFAULT; |
| |
| /* |
| * The Send Context is derived from the PbcVL value |
| * if PBC is populated |
| */ |
| if (dp.pbc) { |
| dd = hfi1_lookup(dp.unit); |
| if (dd == NULL) |
| return -ENODEV; |
| vl = (dp.pbc >> PBC_VL_SHIFT) & PBC_VL_MASK; |
| sc = dd->vld[vl].sc; |
| if (sc) { |
| dp.sw_index = sc->sw_index; |
| hfi1_cdbg( |
| PKT, |
| "Packet sent over VL %d via Send Context %u(%u)", |
| vl, sc->sw_index, sc->hw_context); |
| } |
| } |
| |
| return diagpkt_send(&dp); |
| } |
| |
| static int hfi1_snoop_add(struct hfi1_devdata *dd, const char *name) |
| { |
| int ret = 0; |
| |
| dd->hfi1_snoop.mode_flag = 0; |
| spin_lock_init(&dd->hfi1_snoop.snoop_lock); |
| INIT_LIST_HEAD(&dd->hfi1_snoop.queue); |
| init_waitqueue_head(&dd->hfi1_snoop.waitq); |
| |
| ret = hfi1_cdev_init(HFI1_SNOOP_CAPTURE_BASE + dd->unit, name, |
| &snoop_file_ops, |
| &dd->hfi1_snoop.cdev, &dd->hfi1_snoop.class_dev, |
| false); |
| |
| if (ret) { |
| dd_dev_err(dd, "Couldn't create %s device: %d", name, ret); |
| hfi1_cdev_cleanup(&dd->hfi1_snoop.cdev, |
| &dd->hfi1_snoop.class_dev); |
| } |
| |
| return ret; |
| } |
| |
| static struct hfi1_devdata *hfi1_dd_from_sc_inode(struct inode *in) |
| { |
| int unit = iminor(in) - HFI1_SNOOP_CAPTURE_BASE; |
| struct hfi1_devdata *dd; |
| |
| dd = hfi1_lookup(unit); |
| return dd; |
| |
| } |
| |
| /* clear or restore send context integrity checks */ |
| static void adjust_integrity_checks(struct hfi1_devdata *dd) |
| { |
| struct send_context *sc; |
| unsigned long sc_flags; |
| int i; |
| |
| spin_lock_irqsave(&dd->sc_lock, sc_flags); |
| for (i = 0; i < dd->num_send_contexts; i++) { |
| int enable; |
| |
| sc = dd->send_contexts[i].sc; |
| |
| if (!sc) |
| continue; /* not allocated */ |
| |
| enable = likely(!HFI1_CAP_IS_KSET(NO_INTEGRITY)) && |
| dd->hfi1_snoop.mode_flag != HFI1_PORT_SNOOP_MODE; |
| |
| set_pio_integrity(sc); |
| |
| if (enable) /* take HFI_CAP_* flags into account */ |
| hfi1_init_ctxt(sc); |
| } |
| spin_unlock_irqrestore(&dd->sc_lock, sc_flags); |
| } |
| |
| static int hfi1_snoop_open(struct inode *in, struct file *fp) |
| { |
| int ret; |
| int mode_flag = 0; |
| unsigned long flags = 0; |
| struct hfi1_devdata *dd; |
| struct list_head *queue; |
| |
| mutex_lock(&hfi1_mutex); |
| |
| dd = hfi1_dd_from_sc_inode(in); |
| if (dd == NULL) { |
| ret = -ENODEV; |
| goto bail; |
| } |
| |
| /* |
| * File mode determines snoop or capture. Some existing user |
| * applications expect the capture device to be able to be opened RDWR |
| * because they expect a dedicated capture device. For this reason we |
| * support a module param to force capture mode even if the file open |
| * mode matches snoop. |
| */ |
| if ((fp->f_flags & O_ACCMODE) == O_RDONLY) { |
| snoop_dbg("Capture Enabled"); |
| mode_flag = HFI1_PORT_CAPTURE_MODE; |
| } else if ((fp->f_flags & O_ACCMODE) == O_RDWR) { |
| snoop_dbg("Snoop Enabled"); |
| mode_flag = HFI1_PORT_SNOOP_MODE; |
| } else { |
| snoop_dbg("Invalid"); |
| ret = -EINVAL; |
| goto bail; |
| } |
| queue = &dd->hfi1_snoop.queue; |
| |
| /* |
| * We are not supporting snoop and capture at the same time. |
| */ |
| spin_lock_irqsave(&dd->hfi1_snoop.snoop_lock, flags); |
| if (dd->hfi1_snoop.mode_flag) { |
| ret = -EBUSY; |
| spin_unlock_irqrestore(&dd->hfi1_snoop.snoop_lock, flags); |
| goto bail; |
| } |
| |
| dd->hfi1_snoop.mode_flag = mode_flag; |
| drain_snoop_list(queue); |
| |
| dd->hfi1_snoop.filter_callback = NULL; |
| dd->hfi1_snoop.filter_value = NULL; |
| |
| /* |
| * Send side packet integrity checks are not helpful when snooping so |
| * disable and re-enable when we stop snooping. |
| */ |
| if (mode_flag == HFI1_PORT_SNOOP_MODE) { |
| /* clear after snoop mode is on */ |
| adjust_integrity_checks(dd); /* clear */ |
| |
| /* |
| * We also do not want to be doing the DLID LMC check for |
| * ingressed packets. |
| */ |
| dd->hfi1_snoop.dcc_cfg = read_csr(dd, DCC_CFG_PORT_CONFIG1); |
| write_csr(dd, DCC_CFG_PORT_CONFIG1, |
| (dd->hfi1_snoop.dcc_cfg >> 32) << 32); |
| } |
| |
| /* |
| * As soon as we set these function pointers the recv and send handlers |
| * are active. This is a race condition so we must make sure to drain |
| * the queue and init filter values above. Technically we should add |
| * locking here but all that will happen is on recv a packet will get |
| * allocated and get stuck on the snoop_lock before getting added to the |
| * queue. Same goes for send. |
| */ |
| dd->rhf_rcv_function_map = snoop_rhf_rcv_functions; |
| dd->process_pio_send = snoop_send_pio_handler; |
| dd->process_dma_send = snoop_send_pio_handler; |
| dd->pio_inline_send = snoop_inline_pio_send; |
| |
| spin_unlock_irqrestore(&dd->hfi1_snoop.snoop_lock, flags); |
| ret = 0; |
| |
| bail: |
| mutex_unlock(&hfi1_mutex); |
| |
| return ret; |
| } |
| |
| static int hfi1_snoop_release(struct inode *in, struct file *fp) |
| { |
| unsigned long flags = 0; |
| struct hfi1_devdata *dd; |
| int mode_flag; |
| |
| dd = hfi1_dd_from_sc_inode(in); |
| if (dd == NULL) |
| return -ENODEV; |
| |
| spin_lock_irqsave(&dd->hfi1_snoop.snoop_lock, flags); |
| |
| /* clear the snoop mode before re-adjusting send context CSRs */ |
| mode_flag = dd->hfi1_snoop.mode_flag; |
| dd->hfi1_snoop.mode_flag = 0; |
| |
| /* |
| * Drain the queue and clear the filters we are done with it. Don't |
| * forget to restore the packet integrity checks |
| */ |
| drain_snoop_list(&dd->hfi1_snoop.queue); |
| if (mode_flag == HFI1_PORT_SNOOP_MODE) { |
| /* restore after snoop mode is clear */ |
| adjust_integrity_checks(dd); /* restore */ |
| |
| /* |
| * Also should probably reset the DCC_CONFIG1 register for DLID |
| * checking on incoming packets again. Use the value saved when |
| * opening the snoop device. |
| */ |
| write_csr(dd, DCC_CFG_PORT_CONFIG1, dd->hfi1_snoop.dcc_cfg); |
| } |
| |
| dd->hfi1_snoop.filter_callback = NULL; |
| kfree(dd->hfi1_snoop.filter_value); |
| dd->hfi1_snoop.filter_value = NULL; |
| |
| /* |
| * User is done snooping and capturing, return control to the normal |
| * handler. Re-enable SDMA handling. |
| */ |
| dd->rhf_rcv_function_map = dd->normal_rhf_rcv_functions; |
| dd->process_pio_send = hfi1_verbs_send_pio; |
| dd->process_dma_send = hfi1_verbs_send_dma; |
| dd->pio_inline_send = pio_copy; |
| |
| spin_unlock_irqrestore(&dd->hfi1_snoop.snoop_lock, flags); |
| |
| snoop_dbg("snoop/capture device released"); |
| |
| return 0; |
| } |
| |
| static unsigned int hfi1_snoop_poll(struct file *fp, |
| struct poll_table_struct *wait) |
| { |
| int ret = 0; |
| unsigned long flags = 0; |
| |
| struct hfi1_devdata *dd; |
| |
| dd = hfi1_dd_from_sc_inode(fp->f_inode); |
| if (dd == NULL) |
| return -ENODEV; |
| |
| spin_lock_irqsave(&dd->hfi1_snoop.snoop_lock, flags); |
| |
| poll_wait(fp, &dd->hfi1_snoop.waitq, wait); |
| if (!list_empty(&dd->hfi1_snoop.queue)) |
| ret |= POLLIN | POLLRDNORM; |
| |
| spin_unlock_irqrestore(&dd->hfi1_snoop.snoop_lock, flags); |
| return ret; |
| |
| } |
| |
| static ssize_t hfi1_snoop_write(struct file *fp, const char __user *data, |
| size_t count, loff_t *off) |
| { |
| struct diag_pkt dpkt; |
| struct hfi1_devdata *dd; |
| size_t ret; |
| u8 byte_two, sl, sc5, sc4, vl, byte_one; |
| struct send_context *sc; |
| u32 len; |
| u64 pbc; |
| struct hfi1_ibport *ibp; |
| struct hfi1_pportdata *ppd; |
| |
| dd = hfi1_dd_from_sc_inode(fp->f_inode); |
| if (dd == NULL) |
| return -ENODEV; |
| |
| ppd = dd->pport; |
| snoop_dbg("received %lu bytes from user", count); |
| |
| memset(&dpkt, 0, sizeof(struct diag_pkt)); |
| dpkt.version = _DIAG_PKT_VERS; |
| dpkt.unit = dd->unit; |
| dpkt.port = 1; |
| |
| if (likely(!(snoop_flags & SNOOP_USE_METADATA))) { |
| /* |
| * We need to generate the PBC and not let diagpkt_send do it, |
| * to do this we need the VL and the length in dwords. |
| * The VL can be determined by using the SL and looking up the |
| * SC. Then the SC can be converted into VL. The exception to |
| * this is those packets which are from an SMI queue pair. |
| * Since we can't detect anything about the QP here we have to |
| * rely on the SC. If its 0xF then we assume its SMI and |
| * do not look at the SL. |
| */ |
| if (copy_from_user(&byte_one, data, 1)) |
| return -EINVAL; |
| |
| if (copy_from_user(&byte_two, data+1, 1)) |
| return -EINVAL; |
| |
| sc4 = (byte_one >> 4) & 0xf; |
| if (sc4 == 0xF) { |
| snoop_dbg("Detected VL15 packet ignoring SL in packet"); |
| vl = sc4; |
| } else { |
| sl = (byte_two >> 4) & 0xf; |
| ibp = to_iport(&dd->verbs_dev.ibdev, 1); |
| sc5 = ibp->sl_to_sc[sl]; |
| vl = sc_to_vlt(dd, sc5); |
| if (vl != sc4) { |
| snoop_dbg("VL %d does not match SC %d of packet", |
| vl, sc4); |
| return -EINVAL; |
| } |
| } |
| |
| sc = dd->vld[vl].sc; /* Look up the context based on VL */ |
| if (sc) { |
| dpkt.sw_index = sc->sw_index; |
| snoop_dbg("Sending on context %u(%u)", sc->sw_index, |
| sc->hw_context); |
| } else { |
| snoop_dbg("Could not find context for vl %d", vl); |
| return -EINVAL; |
| } |
| |
| len = (count >> 2) + 2; /* Add in PBC */ |
| pbc = create_pbc(ppd, 0, 0, vl, len); |
| } else { |
| if (copy_from_user(&pbc, data, sizeof(pbc))) |
| return -EINVAL; |
| vl = (pbc >> PBC_VL_SHIFT) & PBC_VL_MASK; |
| sc = dd->vld[vl].sc; /* Look up the context based on VL */ |
| if (sc) { |
| dpkt.sw_index = sc->sw_index; |
| } else { |
| snoop_dbg("Could not find context for vl %d", vl); |
| return -EINVAL; |
| } |
| data += sizeof(pbc); |
| count -= sizeof(pbc); |
| } |
| dpkt.len = count; |
| dpkt.data = (unsigned long)data; |
| |
| snoop_dbg("PBC: vl=0x%llx Length=0x%llx", |
| (pbc >> 12) & 0xf, |
| (pbc & 0xfff)); |
| |
| dpkt.pbc = pbc; |
| ret = diagpkt_send(&dpkt); |
| /* |
| * diagpkt_send only returns number of bytes in the diagpkt so patch |
| * that up here before returning. |
| */ |
| if (ret == sizeof(dpkt)) |
| return count; |
| |
| return ret; |
| } |
| |
| static ssize_t hfi1_snoop_read(struct file *fp, char __user *data, |
| size_t pkt_len, loff_t *off) |
| { |
| ssize_t ret = 0; |
| unsigned long flags = 0; |
| struct snoop_packet *packet = NULL; |
| struct hfi1_devdata *dd; |
| |
| dd = hfi1_dd_from_sc_inode(fp->f_inode); |
| if (dd == NULL) |
| return -ENODEV; |
| |
| spin_lock_irqsave(&dd->hfi1_snoop.snoop_lock, flags); |
| |
| while (list_empty(&dd->hfi1_snoop.queue)) { |
| spin_unlock_irqrestore(&dd->hfi1_snoop.snoop_lock, flags); |
| |
| if (fp->f_flags & O_NONBLOCK) |
| return -EAGAIN; |
| |
| if (wait_event_interruptible( |
| dd->hfi1_snoop.waitq, |
| !list_empty(&dd->hfi1_snoop.queue))) |
| return -EINTR; |
| |
| spin_lock_irqsave(&dd->hfi1_snoop.snoop_lock, flags); |
| } |
| |
| if (!list_empty(&dd->hfi1_snoop.queue)) { |
| packet = list_entry(dd->hfi1_snoop.queue.next, |
| struct snoop_packet, list); |
| list_del(&packet->list); |
| spin_unlock_irqrestore(&dd->hfi1_snoop.snoop_lock, flags); |
| if (pkt_len >= packet->total_len) { |
| if (copy_to_user(data, packet->data, |
| packet->total_len)) |
| ret = -EFAULT; |
| else |
| ret = packet->total_len; |
| } else |
| ret = -EINVAL; |
| |
| kfree(packet); |
| } else |
| spin_unlock_irqrestore(&dd->hfi1_snoop.snoop_lock, flags); |
| |
| return ret; |
| } |
| |
| static long hfi1_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) |
| { |
| struct hfi1_devdata *dd; |
| void *filter_value = NULL; |
| long ret = 0; |
| int value = 0; |
| u8 physState = 0; |
| u8 linkState = 0; |
| u16 devState = 0; |
| unsigned long flags = 0; |
| unsigned long *argp = NULL; |
| struct hfi1_packet_filter_command filter_cmd = {0}; |
| int mode_flag = 0; |
| struct hfi1_pportdata *ppd = NULL; |
| unsigned int index; |
| struct hfi1_link_info link_info; |
| |
| dd = hfi1_dd_from_sc_inode(fp->f_inode); |
| if (dd == NULL) |
| return -ENODEV; |
| |
| spin_lock_irqsave(&dd->hfi1_snoop.snoop_lock, flags); |
| |
| mode_flag = dd->hfi1_snoop.mode_flag; |
| |
| if (((_IOC_DIR(cmd) & _IOC_READ) |
| && !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd))) |
| || ((_IOC_DIR(cmd) & _IOC_WRITE) |
| && !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd)))) { |
| ret = -EFAULT; |
| } else if (!capable(CAP_SYS_ADMIN)) { |
| ret = -EPERM; |
| } else if ((mode_flag & HFI1_PORT_CAPTURE_MODE) && |
| (cmd != HFI1_SNOOP_IOCCLEARQUEUE) && |
| (cmd != HFI1_SNOOP_IOCCLEARFILTER) && |
| (cmd != HFI1_SNOOP_IOCSETFILTER)) { |
| /* Capture devices are allowed only 3 operations |
| * 1.Clear capture queue |
| * 2.Clear capture filter |
| * 3.Set capture filter |
| * Other are invalid. |
| */ |
| ret = -EINVAL; |
| } else { |
| switch (cmd) { |
| case HFI1_SNOOP_IOCSETLINKSTATE: |
| snoop_dbg("HFI1_SNOOP_IOCSETLINKSTATE is not valid"); |
| ret = -EINVAL; |
| break; |
| |
| case HFI1_SNOOP_IOCSETLINKSTATE_EXTRA: |
| memset(&link_info, 0, sizeof(link_info)); |
| |
| if (copy_from_user(&link_info, |
| (struct hfi1_link_info __user *)arg, |
| sizeof(link_info))) |
| ret = -EFAULT; |
| |
| value = link_info.port_state; |
| index = link_info.port_number; |
| if (index > dd->num_pports - 1) { |
| ret = -EINVAL; |
| break; |
| } |
| |
| ppd = &dd->pport[index]; |
| if (!ppd) { |
| ret = -EINVAL; |
| break; |
| } |
| |
| /* What we want to transition to */ |
| physState = (value >> 4) & 0xF; |
| linkState = value & 0xF; |
| snoop_dbg("Setting link state 0x%x", value); |
| |
| switch (linkState) { |
| case IB_PORT_NOP: |
| if (physState == 0) |
| break; |
| /* fall through */ |
| case IB_PORT_DOWN: |
| switch (physState) { |
| case 0: |
| devState = HLS_DN_DOWNDEF; |
| break; |
| case 2: |
| devState = HLS_DN_POLL; |
| break; |
| case 3: |
| devState = HLS_DN_DISABLE; |
| break; |
| default: |
| ret = -EINVAL; |
| goto done; |
| } |
| ret = set_link_state(ppd, devState); |
| break; |
| case IB_PORT_ARMED: |
| ret = set_link_state(ppd, HLS_UP_ARMED); |
| if (!ret) |
| send_idle_sma(dd, SMA_IDLE_ARM); |
| break; |
| case IB_PORT_ACTIVE: |
| ret = set_link_state(ppd, HLS_UP_ACTIVE); |
| if (!ret) |
| send_idle_sma(dd, SMA_IDLE_ACTIVE); |
| break; |
| default: |
| ret = -EINVAL; |
| break; |
| } |
| |
| if (ret) |
| break; |
| /* fall through */ |
| case HFI1_SNOOP_IOCGETLINKSTATE: |
| case HFI1_SNOOP_IOCGETLINKSTATE_EXTRA: |
| if (cmd == HFI1_SNOOP_IOCGETLINKSTATE_EXTRA) { |
| memset(&link_info, 0, sizeof(link_info)); |
| if (copy_from_user(&link_info, |
| (struct hfi1_link_info __user *)arg, |
| sizeof(link_info))) |
| ret = -EFAULT; |
| index = link_info.port_number; |
| } else { |
| ret = __get_user(index, (int __user *) arg); |
| if (ret != 0) |
| break; |
| } |
| |
| if (index > dd->num_pports - 1) { |
| ret = -EINVAL; |
| break; |
| } |
| |
| ppd = &dd->pport[index]; |
| if (!ppd) { |
| ret = -EINVAL; |
| break; |
| } |
| value = hfi1_ibphys_portstate(ppd); |
| value <<= 4; |
| value |= driver_lstate(ppd); |
| |
| snoop_dbg("Link port | Link State: %d", value); |
| |
| if ((cmd == HFI1_SNOOP_IOCGETLINKSTATE_EXTRA) || |
| (cmd == HFI1_SNOOP_IOCSETLINKSTATE_EXTRA)) { |
| link_info.port_state = value; |
| link_info.node_guid = cpu_to_be64(ppd->guid); |
| link_info.link_speed_active = |
| ppd->link_speed_active; |
| link_info.link_width_active = |
| ppd->link_width_active; |
| if (copy_to_user( |
| (struct hfi1_link_info __user *)arg, |
| &link_info, sizeof(link_info))) |
| ret = -EFAULT; |
| } else { |
| ret = __put_user(value, (int __user *)arg); |
| } |
| break; |
| |
| case HFI1_SNOOP_IOCCLEARQUEUE: |
| snoop_dbg("Clearing snoop queue"); |
| drain_snoop_list(&dd->hfi1_snoop.queue); |
| break; |
| |
| case HFI1_SNOOP_IOCCLEARFILTER: |
| snoop_dbg("Clearing filter"); |
| if (dd->hfi1_snoop.filter_callback) { |
| /* Drain packets first */ |
| drain_snoop_list(&dd->hfi1_snoop.queue); |
| dd->hfi1_snoop.filter_callback = NULL; |
| } |
| kfree(dd->hfi1_snoop.filter_value); |
| dd->hfi1_snoop.filter_value = NULL; |
| break; |
| |
| case HFI1_SNOOP_IOCSETFILTER: |
| snoop_dbg("Setting filter"); |
| /* just copy command structure */ |
| argp = (unsigned long *)arg; |
| if (copy_from_user(&filter_cmd, (void __user *)argp, |
| sizeof(filter_cmd))) { |
| ret = -EFAULT; |
| break; |
| } |
| if (filter_cmd.opcode >= HFI1_MAX_FILTERS) { |
| pr_alert("Invalid opcode in request\n"); |
| ret = -EINVAL; |
| break; |
| } |
| |
| snoop_dbg("Opcode %d Len %d Ptr %p", |
| filter_cmd.opcode, filter_cmd.length, |
| filter_cmd.value_ptr); |
| |
| filter_value = kcalloc(filter_cmd.length, sizeof(u8), |
| GFP_KERNEL); |
| if (!filter_value) { |
| pr_alert("Not enough memory\n"); |
| ret = -ENOMEM; |
| break; |
| } |
| /* copy remaining data from userspace */ |
| if (copy_from_user((u8 *)filter_value, |
| (void __user *)filter_cmd.value_ptr, |
| filter_cmd.length)) { |
| kfree(filter_value); |
| ret = -EFAULT; |
| break; |
| } |
| /* Drain packets first */ |
| drain_snoop_list(&dd->hfi1_snoop.queue); |
| dd->hfi1_snoop.filter_callback = |
| hfi1_filters[filter_cmd.opcode].filter; |
| /* just in case we see back to back sets */ |
| kfree(dd->hfi1_snoop.filter_value); |
| dd->hfi1_snoop.filter_value = filter_value; |
| |
| break; |
| case HFI1_SNOOP_IOCGETVERSION: |
| value = SNOOP_CAPTURE_VERSION; |
| snoop_dbg("Getting version: %d", value); |
| ret = __put_user(value, (int __user *)arg); |
| break; |
| case HFI1_SNOOP_IOCSET_OPTS: |
| snoop_flags = 0; |
| ret = __get_user(value, (int __user *) arg); |
| if (ret != 0) |
| break; |
| |
| snoop_dbg("Setting snoop option %d", value); |
| if (value & SNOOP_DROP_SEND) |
| snoop_flags |= SNOOP_DROP_SEND; |
| if (value & SNOOP_USE_METADATA) |
| snoop_flags |= SNOOP_USE_METADATA; |
| break; |
| default: |
| ret = -ENOTTY; |
| break; |
| } |
| } |
| done: |
| spin_unlock_irqrestore(&dd->hfi1_snoop.snoop_lock, flags); |
| return ret; |
| } |
| |
| static void snoop_list_add_tail(struct snoop_packet *packet, |
| struct hfi1_devdata *dd) |
| { |
| unsigned long flags = 0; |
| |
| spin_lock_irqsave(&dd->hfi1_snoop.snoop_lock, flags); |
| if (likely((dd->hfi1_snoop.mode_flag & HFI1_PORT_SNOOP_MODE) || |
| (dd->hfi1_snoop.mode_flag & HFI1_PORT_CAPTURE_MODE))) { |
| list_add_tail(&packet->list, &dd->hfi1_snoop.queue); |
| snoop_dbg("Added packet to list"); |
| } |
| |
| /* |
| * Technically we can could have closed the snoop device while waiting |
| * on the above lock and it is gone now. The snoop mode_flag will |
| * prevent us from adding the packet to the queue though. |
| */ |
| |
| spin_unlock_irqrestore(&dd->hfi1_snoop.snoop_lock, flags); |
| wake_up_interruptible(&dd->hfi1_snoop.waitq); |
| } |
| |
| static inline int hfi1_filter_check(void *val, const char *msg) |
| { |
| if (!val) { |
| snoop_dbg("Error invalid %s value for filter", msg); |
| return HFI1_FILTER_ERR; |
| } |
| return 0; |
| } |
| |
| static int hfi1_filter_lid(void *ibhdr, void *packet_data, void *value) |
| { |
| struct hfi1_ib_header *hdr; |
| int ret; |
| |
| ret = hfi1_filter_check(ibhdr, "header"); |
| if (ret) |
| return ret; |
| ret = hfi1_filter_check(value, "user"); |
| if (ret) |
| return ret; |
| hdr = (struct hfi1_ib_header *)ibhdr; |
| |
| if (*((u16 *)value) == be16_to_cpu(hdr->lrh[3])) /* matches slid */ |
| return HFI1_FILTER_HIT; /* matched */ |
| |
| return HFI1_FILTER_MISS; /* Not matched */ |
| } |
| |
| static int hfi1_filter_dlid(void *ibhdr, void *packet_data, void *value) |
| { |
| struct hfi1_ib_header *hdr; |
| int ret; |
| |
| ret = hfi1_filter_check(ibhdr, "header"); |
| if (ret) |
| return ret; |
| ret = hfi1_filter_check(value, "user"); |
| if (ret) |
| return ret; |
| |
| hdr = (struct hfi1_ib_header *)ibhdr; |
| |
| if (*((u16 *)value) == be16_to_cpu(hdr->lrh[1])) |
| return HFI1_FILTER_HIT; |
| |
| return HFI1_FILTER_MISS; |
| } |
| |
| /* Not valid for outgoing packets, send handler passes null for data*/ |
| static int hfi1_filter_mad_mgmt_class(void *ibhdr, void *packet_data, |
| void *value) |
| { |
| struct hfi1_ib_header *hdr; |
| struct hfi1_other_headers *ohdr = NULL; |
| struct ib_smp *smp = NULL; |
| u32 qpn = 0; |
| int ret; |
| |
| ret = hfi1_filter_check(ibhdr, "header"); |
| if (ret) |
| return ret; |
| ret = hfi1_filter_check(packet_data, "packet_data"); |
| if (ret) |
| return ret; |
| ret = hfi1_filter_check(value, "user"); |
| if (ret) |
| return ret; |
| |
| hdr = (struct hfi1_ib_header *)ibhdr; |
| |
| /* Check for GRH */ |
| if ((be16_to_cpu(hdr->lrh[0]) & 3) == HFI1_LRH_BTH) |
| ohdr = &hdr->u.oth; /* LRH + BTH + DETH */ |
| else |
| ohdr = &hdr->u.l.oth; /* LRH + GRH + BTH + DETH */ |
| |
| qpn = be32_to_cpu(ohdr->bth[1]) & 0x00FFFFFF; |
| if (qpn <= 1) { |
| smp = (struct ib_smp *)packet_data; |
| if (*((u8 *)value) == smp->mgmt_class) |
| return HFI1_FILTER_HIT; |
| else |
| return HFI1_FILTER_MISS; |
| } |
| return HFI1_FILTER_ERR; |
| } |
| |
| static int hfi1_filter_qp_number(void *ibhdr, void *packet_data, void *value) |
| { |
| |
| struct hfi1_ib_header *hdr; |
| struct hfi1_other_headers *ohdr = NULL; |
| int ret; |
| |
| ret = hfi1_filter_check(ibhdr, "header"); |
| if (ret) |
| return ret; |
| ret = hfi1_filter_check(value, "user"); |
| if (ret) |
| return ret; |
| |
| hdr = (struct hfi1_ib_header *)ibhdr; |
| |
| /* Check for GRH */ |
| if ((be16_to_cpu(hdr->lrh[0]) & 3) == HFI1_LRH_BTH) |
| ohdr = &hdr->u.oth; /* LRH + BTH + DETH */ |
| else |
| ohdr = &hdr->u.l.oth; /* LRH + GRH + BTH + DETH */ |
| if (*((u32 *)value) == (be32_to_cpu(ohdr->bth[1]) & 0x00FFFFFF)) |
| return HFI1_FILTER_HIT; |
| |
| return HFI1_FILTER_MISS; |
| } |
| |
| static int hfi1_filter_ibpacket_type(void *ibhdr, void *packet_data, |
| void *value) |
| { |
| u32 lnh = 0; |
| u8 opcode = 0; |
| struct hfi1_ib_header *hdr; |
| struct hfi1_other_headers *ohdr = NULL; |
| int ret; |
| |
| ret = hfi1_filter_check(ibhdr, "header"); |
| if (ret) |
| return ret; |
| ret = hfi1_filter_check(value, "user"); |
| if (ret) |
| return ret; |
| |
| hdr = (struct hfi1_ib_header *)ibhdr; |
| |
| lnh = (be16_to_cpu(hdr->lrh[0]) & 3); |
| |
| if (lnh == HFI1_LRH_BTH) |
| ohdr = &hdr->u.oth; |
| else if (lnh == HFI1_LRH_GRH) |
| ohdr = &hdr->u.l.oth; |
| else |
| return HFI1_FILTER_ERR; |
| |
| opcode = be32_to_cpu(ohdr->bth[0]) >> 24; |
| |
| if (*((u8 *)value) == ((opcode >> 5) & 0x7)) |
| return HFI1_FILTER_HIT; |
| |
| return HFI1_FILTER_MISS; |
| } |
| |
| static int hfi1_filter_ib_service_level(void *ibhdr, void *packet_data, |
| void *value) |
| { |
| struct hfi1_ib_header *hdr; |
| int ret; |
| |
| ret = hfi1_filter_check(ibhdr, "header"); |
| if (ret) |
| return ret; |
| ret = hfi1_filter_check(value, "user"); |
| if (ret) |
| return ret; |
| |
| hdr = (struct hfi1_ib_header *)ibhdr; |
| |
| if ((*((u8 *)value)) == ((be16_to_cpu(hdr->lrh[0]) >> 4) & 0xF)) |
| return HFI1_FILTER_HIT; |
| |
| return HFI1_FILTER_MISS; |
| } |
| |
| static int hfi1_filter_ib_pkey(void *ibhdr, void *packet_data, void *value) |
| { |
| |
| u32 lnh = 0; |
| struct hfi1_ib_header *hdr; |
| struct hfi1_other_headers *ohdr = NULL; |
| int ret; |
| |
| ret = hfi1_filter_check(ibhdr, "header"); |
| if (ret) |
| return ret; |
| ret = hfi1_filter_check(value, "user"); |
| if (ret) |
| return ret; |
| |
| hdr = (struct hfi1_ib_header *)ibhdr; |
| |
| lnh = (be16_to_cpu(hdr->lrh[0]) & 3); |
| if (lnh == HFI1_LRH_BTH) |
| ohdr = &hdr->u.oth; |
| else if (lnh == HFI1_LRH_GRH) |
| ohdr = &hdr->u.l.oth; |
| else |
| return HFI1_FILTER_ERR; |
| |
| /* P_key is 16-bit entity, however top most bit indicates |
| * type of membership. 0 for limited and 1 for Full. |
| * Limited members cannot accept information from other |
| * Limited members, but communication is allowed between |
| * every other combination of membership. |
| * Hence we'll omit comparing top-most bit while filtering |
| */ |
| |
| if ((*(u16 *)value & 0x7FFF) == |
| ((be32_to_cpu(ohdr->bth[0])) & 0x7FFF)) |
| return HFI1_FILTER_HIT; |
| |
| return HFI1_FILTER_MISS; |
| } |
| |
| /* |
| * If packet_data is NULL then this is coming from one of the send functions. |
| * Thus we know if its an ingressed or egressed packet. |
| */ |
| static int hfi1_filter_direction(void *ibhdr, void *packet_data, void *value) |
| { |
| u8 user_dir = *(u8 *)value; |
| int ret; |
| |
| ret = hfi1_filter_check(value, "user"); |
| if (ret) |
| return ret; |
| |
| if (packet_data) { |
| /* Incoming packet */ |
| if (user_dir & HFI1_SNOOP_INGRESS) |
| return HFI1_FILTER_HIT; |
| } else { |
| /* Outgoing packet */ |
| if (user_dir & HFI1_SNOOP_EGRESS) |
| return HFI1_FILTER_HIT; |
| } |
| |
| return HFI1_FILTER_MISS; |
| } |
| |
| /* |
| * Allocate a snoop packet. The structure that is stored in the ring buffer, not |
| * to be confused with an hfi packet type. |
| */ |
| static struct snoop_packet *allocate_snoop_packet(u32 hdr_len, |
| u32 data_len, |
| u32 md_len) |
| { |
| |
| struct snoop_packet *packet; |
| |
| packet = kzalloc(sizeof(struct snoop_packet) + hdr_len + data_len |
| + md_len, |
| GFP_ATOMIC | __GFP_NOWARN); |
| if (likely(packet)) |
| INIT_LIST_HEAD(&packet->list); |
| |
| |
| return packet; |
| } |
| |
| /* |
| * Instead of having snoop and capture code intermixed with the recv functions, |
| * both the interrupt handler and hfi1_ib_rcv() we are going to hijack the call |
| * and land in here for snoop/capture but if not enabled the call will go |
| * through as before. This gives us a single point to constrain all of the snoop |
| * snoop recv logic. There is nothing special that needs to happen for bypass |
| * packets. This routine should not try to look into the packet. It just copied |
| * it. There is no guarantee for filters when it comes to bypass packets as |
| * there is no specific support. Bottom line is this routine does now even know |
| * what a bypass packet is. |
| */ |
| int snoop_recv_handler(struct hfi1_packet *packet) |
| { |
| struct hfi1_pportdata *ppd = packet->rcd->ppd; |
| struct hfi1_ib_header *hdr = packet->hdr; |
| int header_size = packet->hlen; |
| void *data = packet->ebuf; |
| u32 tlen = packet->tlen; |
| struct snoop_packet *s_packet = NULL; |
| int ret; |
| int snoop_mode = 0; |
| u32 md_len = 0; |
| struct capture_md md; |
| |
| snoop_dbg("PACKET IN: hdr size %d tlen %d data %p", header_size, tlen, |
| data); |
| |
| trace_snoop_capture(ppd->dd, header_size, hdr, tlen - header_size, |
| data); |
| |
| if (!ppd->dd->hfi1_snoop.filter_callback) { |
| snoop_dbg("filter not set"); |
| ret = HFI1_FILTER_HIT; |
| } else { |
| ret = ppd->dd->hfi1_snoop.filter_callback(hdr, data, |
| ppd->dd->hfi1_snoop.filter_value); |
| } |
| |
| switch (ret) { |
| case HFI1_FILTER_ERR: |
| snoop_dbg("Error in filter call"); |
| break; |
| case HFI1_FILTER_MISS: |
| snoop_dbg("Filter Miss"); |
| break; |
| case HFI1_FILTER_HIT: |
| |
| if (ppd->dd->hfi1_snoop.mode_flag & HFI1_PORT_SNOOP_MODE) |
| snoop_mode = 1; |
| if ((snoop_mode == 0) || |
| unlikely(snoop_flags & SNOOP_USE_METADATA)) |
| md_len = sizeof(struct capture_md); |
| |
| |
| s_packet = allocate_snoop_packet(header_size, |
| tlen - header_size, |
| md_len); |
| |
| if (unlikely(s_packet == NULL)) { |
| dd_dev_warn_ratelimited(ppd->dd, "Unable to allocate snoop/capture packet\n"); |
| break; |
| } |
| |
| if (md_len > 0) { |
| memset(&md, 0, sizeof(struct capture_md)); |
| md.port = 1; |
| md.dir = PKT_DIR_INGRESS; |
| md.u.rhf = packet->rhf; |
| memcpy(s_packet->data, &md, md_len); |
| } |
| |
| /* We should always have a header */ |
| if (hdr) { |
| memcpy(s_packet->data + md_len, hdr, header_size); |
| } else { |
| dd_dev_err(ppd->dd, "Unable to copy header to snoop/capture packet\n"); |
| kfree(s_packet); |
| break; |
| } |
| |
| /* |
| * Packets with no data are possible. If there is no data needed |
| * to take care of the last 4 bytes which are normally included |
| * with data buffers and are included in tlen. Since we kzalloc |
| * the buffer we do not need to set any values but if we decide |
| * not to use kzalloc we should zero them. |
| */ |
| if (data) |
| memcpy(s_packet->data + header_size + md_len, data, |
| tlen - header_size); |
| |
| s_packet->total_len = tlen + md_len; |
| snoop_list_add_tail(s_packet, ppd->dd); |
| |
| /* |
| * If we are snooping the packet not capturing then throw away |
| * after adding to the list. |
| */ |
| snoop_dbg("Capturing packet"); |
| if (ppd->dd->hfi1_snoop.mode_flag & HFI1_PORT_SNOOP_MODE) { |
| snoop_dbg("Throwing packet away"); |
| /* |
| * If we are dropping the packet we still may need to |
| * handle the case where error flags are set, this is |
| * normally done by the type specific handler but that |
| * won't be called in this case. |
| */ |
| if (unlikely(rhf_err_flags(packet->rhf))) |
| handle_eflags(packet); |
| |
| /* throw the packet on the floor */ |
| return RHF_RCV_CONTINUE; |
| } |
| break; |
| default: |
| break; |
| } |
| |
| /* |
| * We do not care what type of packet came in here - just pass it off |
| * to the normal handler. |
| */ |
| return ppd->dd->normal_rhf_rcv_functions[rhf_rcv_type(packet->rhf)] |
| (packet); |
| } |
| |
| /* |
| * Handle snooping and capturing packets when sdma is being used. |
| */ |
| int snoop_send_dma_handler(struct hfi1_qp *qp, struct ahg_ib_header *ibhdr, |
| u32 hdrwords, struct hfi1_sge_state *ss, u32 len, |
| u32 plen, u32 dwords, u64 pbc) |
| { |
| pr_alert("Snooping/Capture of Send DMA Packets Is Not Supported!\n"); |
| snoop_dbg("Unsupported Operation"); |
| return hfi1_verbs_send_dma(qp, ibhdr, hdrwords, ss, len, plen, dwords, |
| 0); |
| } |
| |
| /* |
| * Handle snooping and capturing packets when pio is being used. Does not handle |
| * bypass packets. The only way to send a bypass packet currently is to use the |
| * diagpkt interface. When that interface is enable snoop/capture is not. |
| */ |
| int snoop_send_pio_handler(struct hfi1_qp *qp, struct ahg_ib_header *ahdr, |
| u32 hdrwords, struct hfi1_sge_state *ss, u32 len, |
| u32 plen, u32 dwords, u64 pbc) |
| { |
| struct hfi1_ibport *ibp = to_iport(qp->ibqp.device, qp->port_num); |
| struct hfi1_pportdata *ppd = ppd_from_ibp(ibp); |
| struct snoop_packet *s_packet = NULL; |
| u32 *hdr = (u32 *)&ahdr->ibh; |
| u32 length = 0; |
| struct hfi1_sge_state temp_ss; |
| void *data = NULL; |
| void *data_start = NULL; |
| int ret; |
| int snoop_mode = 0; |
| int md_len = 0; |
| struct capture_md md; |
| u32 vl; |
| u32 hdr_len = hdrwords << 2; |
| u32 tlen = HFI1_GET_PKT_LEN(&ahdr->ibh); |
| |
| md.u.pbc = 0; |
| |
| snoop_dbg("PACKET OUT: hdrword %u len %u plen %u dwords %u tlen %u", |
| hdrwords, len, plen, dwords, tlen); |
| if (ppd->dd->hfi1_snoop.mode_flag & HFI1_PORT_SNOOP_MODE) |
| snoop_mode = 1; |
| if ((snoop_mode == 0) || |
| unlikely(snoop_flags & SNOOP_USE_METADATA)) |
| md_len = sizeof(struct capture_md); |
| |
| /* not using ss->total_len as arg 2 b/c that does not count CRC */ |
| s_packet = allocate_snoop_packet(hdr_len, tlen - hdr_len, md_len); |
| |
| if (unlikely(s_packet == NULL)) { |
| dd_dev_warn_ratelimited(ppd->dd, "Unable to allocate snoop/capture packet\n"); |
| goto out; |
| } |
| |
| s_packet->total_len = tlen + md_len; |
| |
| if (md_len > 0) { |
| memset(&md, 0, sizeof(struct capture_md)); |
| md.port = 1; |
| md.dir = PKT_DIR_EGRESS; |
| if (likely(pbc == 0)) { |
| vl = be16_to_cpu(ahdr->ibh.lrh[0]) >> 12; |
| md.u.pbc = create_pbc(ppd, 0, qp->s_srate, vl, plen); |
| } else { |
| md.u.pbc = 0; |
| } |
| memcpy(s_packet->data, &md, md_len); |
| } else { |
| md.u.pbc = pbc; |
| } |
| |
| /* Copy header */ |
| if (likely(hdr)) { |
| memcpy(s_packet->data + md_len, hdr, hdr_len); |
| } else { |
| dd_dev_err(ppd->dd, |
| "Unable to copy header to snoop/capture packet\n"); |
| kfree(s_packet); |
| goto out; |
| } |
| |
| if (ss) { |
| data = s_packet->data + hdr_len + md_len; |
| data_start = data; |
| |
| /* |
| * Copy SGE State |
| * The update_sge() function below will not modify the |
| * individual SGEs in the array. It will make a copy each time |
| * and operate on that. So we only need to copy this instance |
| * and it won't impact PIO. |
| */ |
| temp_ss = *ss; |
| length = len; |
| |
| snoop_dbg("Need to copy %d bytes", length); |
| while (length) { |
| void *addr = temp_ss.sge.vaddr; |
| u32 slen = temp_ss.sge.length; |
| |
| if (slen > length) { |
| slen = length; |
| snoop_dbg("slen %d > len %d", slen, length); |
| } |
| snoop_dbg("copy %d to %p", slen, addr); |
| memcpy(data, addr, slen); |
| update_sge(&temp_ss, slen); |
| length -= slen; |
| data += slen; |
| snoop_dbg("data is now %p bytes left %d", data, length); |
| } |
| snoop_dbg("Completed SGE copy"); |
| } |
| |
| /* |
| * Why do the filter check down here? Because the event tracing has its |
| * own filtering and we need to have the walked the SGE list. |
| */ |
| if (!ppd->dd->hfi1_snoop.filter_callback) { |
| snoop_dbg("filter not set\n"); |
| ret = HFI1_FILTER_HIT; |
| } else { |
| ret = ppd->dd->hfi1_snoop.filter_callback( |
| &ahdr->ibh, |
| NULL, |
| ppd->dd->hfi1_snoop.filter_value); |
| } |
| |
| switch (ret) { |
| case HFI1_FILTER_ERR: |
| snoop_dbg("Error in filter call"); |
| /* fall through */ |
| case HFI1_FILTER_MISS: |
| snoop_dbg("Filter Miss"); |
| kfree(s_packet); |
| break; |
| case HFI1_FILTER_HIT: |
| snoop_dbg("Capturing packet"); |
| snoop_list_add_tail(s_packet, ppd->dd); |
| |
| if (unlikely((snoop_flags & SNOOP_DROP_SEND) && |
| (ppd->dd->hfi1_snoop.mode_flag & |
| HFI1_PORT_SNOOP_MODE))) { |
| unsigned long flags; |
| |
| snoop_dbg("Dropping packet"); |
| if (qp->s_wqe) { |
| spin_lock_irqsave(&qp->s_lock, flags); |
| hfi1_send_complete( |
| qp, |
| qp->s_wqe, |
| IB_WC_SUCCESS); |
| spin_unlock_irqrestore(&qp->s_lock, flags); |
| } else if (qp->ibqp.qp_type == IB_QPT_RC) { |
| spin_lock_irqsave(&qp->s_lock, flags); |
| hfi1_rc_send_complete(qp, &ahdr->ibh); |
| spin_unlock_irqrestore(&qp->s_lock, flags); |
| } |
| return 0; |
| } |
| break; |
| default: |
| kfree(s_packet); |
| break; |
| } |
| out: |
| return hfi1_verbs_send_pio(qp, ahdr, hdrwords, ss, len, plen, dwords, |
| md.u.pbc); |
| } |
| |
| /* |
| * Callers of this must pass a hfi1_ib_header type for the from ptr. Currently |
| * this can be used anywhere, but the intention is for inline ACKs for RC and |
| * CCA packets. We don't restrict this usage though. |
| */ |
| void snoop_inline_pio_send(struct hfi1_devdata *dd, struct pio_buf *pbuf, |
| u64 pbc, const void *from, size_t count) |
| { |
| int snoop_mode = 0; |
| int md_len = 0; |
| struct capture_md md; |
| struct snoop_packet *s_packet = NULL; |
| |
| /* |
| * count is in dwords so we need to convert to bytes. |
| * We also need to account for CRC which would be tacked on by hardware. |
| */ |
| int packet_len = (count << 2) + 4; |
| int ret; |
| |
| snoop_dbg("ACK OUT: len %d", packet_len); |
| |
| if (!dd->hfi1_snoop.filter_callback) { |
| snoop_dbg("filter not set"); |
| ret = HFI1_FILTER_HIT; |
| } else { |
| ret = dd->hfi1_snoop.filter_callback( |
| (struct hfi1_ib_header *)from, |
| NULL, |
| dd->hfi1_snoop.filter_value); |
| } |
| |
| switch (ret) { |
| case HFI1_FILTER_ERR: |
| snoop_dbg("Error in filter call"); |
| /* fall through */ |
| case HFI1_FILTER_MISS: |
| snoop_dbg("Filter Miss"); |
| break; |
| case HFI1_FILTER_HIT: |
| snoop_dbg("Capturing packet"); |
| if (dd->hfi1_snoop.mode_flag & HFI1_PORT_SNOOP_MODE) |
| snoop_mode = 1; |
| if ((snoop_mode == 0) || |
| unlikely(snoop_flags & SNOOP_USE_METADATA)) |
| md_len = sizeof(struct capture_md); |
| |
| s_packet = allocate_snoop_packet(packet_len, 0, md_len); |
| |
| if (unlikely(s_packet == NULL)) { |
| dd_dev_warn_ratelimited(dd, "Unable to allocate snoop/capture packet\n"); |
| goto inline_pio_out; |
| } |
| |
| s_packet->total_len = packet_len + md_len; |
| |
| /* Fill in the metadata for the packet */ |
| if (md_len > 0) { |
| memset(&md, 0, sizeof(struct capture_md)); |
| md.port = 1; |
| md.dir = PKT_DIR_EGRESS; |
| md.u.pbc = pbc; |
| memcpy(s_packet->data, &md, md_len); |
| } |
| |
| /* Add the packet data which is a single buffer */ |
| memcpy(s_packet->data + md_len, from, packet_len); |
| |
| snoop_list_add_tail(s_packet, dd); |
| |
| if (unlikely((snoop_flags & SNOOP_DROP_SEND) && snoop_mode)) { |
| snoop_dbg("Dropping packet"); |
| return; |
| } |
| break; |
| default: |
| break; |
| } |
| |
| inline_pio_out: |
| pio_copy(dd, pbuf, pbc, from, count); |
| |
| } |