ipath - PathScale InfiniPath driver
Subsystems (3)
PATCH 1/1 ipath - infiniband verbs support, part 1 of 3
In reply to Bryan O'Sullivandrivers/infiniband/hw/ipath/ipath_verbs.c new file +2307 -0
@@ -0,0 +1,2307 @@ +/* +* Copyright (c) 2005, 2006 PathScale, Inc. All rights reserved. +* +* This software is available to you under a choice of one of two +* licenses. You may choose to be licensed under the terms of the GNU +* General Public License (GPL) Version 2, available from the file +* COPYING in the main directory of this source tree, or the +* OpenIB.org BSD license below: +* +* 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. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +* +* Patent licenses, if any, provided herein do not apply to +* combinations of this program with other software, or any other +* product whatsoever. +*/ + +#include <linux/config.h> +#include <linux/version.h> +#include <linux/pci.h> +#include <linux/err.h> +#include <rdma/ib_pack.h> +#include <rdma/ib_smi.h> +#include <rdma/ib_mad.h> +#include <rdma/ib_user_verbs.h> + +#include <asm/uaccess.h> +#include <asm-generic/bug.h> + +#include "ips_common.h" +#include "ipath_layer.h" +#include "ipath_verbs.h" + +/* +* Compare the lower 24 bits of the two values. +* Returns an integer <, ==, or > than zero. +*/ +static inline int cmp24 (u32 a,u32 b) +{ +return (((int ) a) - ((int ) b)) <<8 ; +} + +#define MODNAME "ib_ipath" +#define DRIVER_LOAD_MSG "PathScale " MODNAME " loaded: " +#define PFX MODNAME ": " + + +#define BITS_PER_PAGE (PAGE_SIZE*BITS_PER_BYTE) +#define BITS_PER_PAGE_MASK (BITS_PER_PAGE-1) +#define mk_qpn (qpt, map, off) (((map) - (qpt)->map)*BITS_PER_PAGE + (off)) +#define find_next_offset (map, off) \ + find_next_zero_bit((map)->page, BITS_PER_PAGE, off) + +/* Not static, because we don't want the compiler removing it */ +const char ipath_verbs_version[] ="ipath_verbs " IPATH_IDSTR ; + +unsigned int ib_ipath_qp_table_size =251 ; +module_param (ib_ipath_qp_table_size, uint,0444 ); +MODULE_PARM_DESC (ib_ipath_qp_table_size,"QP table size" ); + +unsigned int ib_ipath_lkey_table_size =12 ; +module_param (ib_ipath_lkey_table_size, uint,0444 ); +MODULE_PARM_DESC (ib_ipath_lkey_table_size, +"LKEY table size in bits (2^n, 1 <= n <= 23)" ); + +unsigned int ib_ipath_debug;/* debug mask */ +module_param (ib_ipath_debug, uint,0644 ); +MODULE_PARM_DESC (ib_ipath_debug,"Verbs debug mask" ); + + +static void ipath_ud_loopback (struct ipath_qp *sqp,struct ipath_sge_state *ss, +u32 len,struct ib_send_wr *wr,struct ib_wc *wc); +static void ipath_ruc_loopback (struct ipath_qp *sqp,struct ib_wc *wc); +static int ipath_destroy_qp (struct ib_qp *ibqp); + +MODULE_LICENSE ("GPL" ); +MODULE_AUTHOR ("PathScale <[email protected]>" ); +MODULE_DESCRIPTION ("Pathscale InfiniPath driver" ); + +enum { +IPATH_FAULT_RC_DROP_SEND_F =1 , +IPATH_FAULT_RC_DROP_SEND_M , +IPATH_FAULT_RC_DROP_SEND_L , +IPATH_FAULT_RC_DROP_SEND_O , +IPATH_FAULT_RC_DROP_RDMA_WRITE_F , +IPATH_FAULT_RC_DROP_RDMA_WRITE_M , +IPATH_FAULT_RC_DROP_RDMA_WRITE_L , +IPATH_FAULT_RC_DROP_RDMA_WRITE_O , +IPATH_FAULT_RC_DROP_RDMA_READ_RESP_F , +IPATH_FAULT_RC_DROP_RDMA_READ_RESP_M , +IPATH_FAULT_RC_DROP_RDMA_READ_RESP_L , +IPATH_FAULT_RC_DROP_RDMA_READ_RESP_O , +IPATH_FAULT_RC_DROP_ACK , +}; + +enum { +IPATH_TRANS_INVALID =0 , +IPATH_TRANS_ANY2RST , +IPATH_TRANS_RST2INIT , +IPATH_TRANS_INIT2INIT , +IPATH_TRANS_INIT2RTR , +IPATH_TRANS_RTR2RTS , +IPATH_TRANS_RTS2RTS , +IPATH_TRANS_SQERR2RTS , +IPATH_TRANS_ANY2ERR , +IPATH_TRANS_RTS2SQD ,/* XXX Wait for expected ACKs & signal event */ +IPATH_TRANS_SQD2SQD ,/* error if not drained & parameter change */ +IPATH_TRANS_SQD2RTS ,/* error if not drained */ +}; + +enum { +IPATH_POST_SEND_OK =0x0001 , +IPATH_POST_RECV_OK =0x0002 , +IPATH_PROCESS_RECV_OK =0x0004 , +IPATH_PROCESS_SEND_OK =0x0008 , +}; + +static int state_ops[IB_QPS_ERR +1 ] = { + [IB_QPS_RESET ] =0 , + [IB_QPS_INIT ] =IPATH_POST_RECV_OK , + [IB_QPS_RTR ] =IPATH_POST_RECV_OK |IPATH_PROCESS_RECV_OK , + [IB_QPS_RTS ] =IPATH_POST_RECV_OK |IPATH_PROCESS_RECV_OK | +IPATH_POST_SEND_OK |IPATH_PROCESS_SEND_OK , + [IB_QPS_SQD ] =IPATH_POST_RECV_OK |IPATH_PROCESS_RECV_OK | +IPATH_POST_SEND_OK , + [IB_QPS_SQE ] =IPATH_POST_RECV_OK |IPATH_PROCESS_RECV_OK , + [IB_QPS_ERR ] =0 , +}; + +/* +* Convert the AETH credit code into the number of credits. +*/ +static u32 credit_table[31 ] = { +0 ,/* 0 */ +1 ,/* 1 */ +2 ,/* 2 */ +3 ,/* 3 */ +4 ,/* 4 */ +6 ,/* 5 */ +8 ,/* 6 */ +12 ,/* 7 */ +16 ,/* 8 */ +24 ,/* 9 */ +32 ,/* A */ +48 ,/* B */ +64 ,/* C */ +96 ,/* D */ +128 ,/* E */ +192 ,/* F */ +256 ,/* 10 */ +384 ,/* 11 */ +512 ,/* 12 */ +768 ,/* 13 */ +1024 ,/* 14 */ +1536 ,/* 15 */ +2048 ,/* 16 */ +3072 ,/* 17 */ +4096 ,/* 18 */ +6144 ,/* 19 */ +8192 ,/* 1A */ +12288 ,/* 1B */ +16384 ,/* 1C */ +24576 ,/* 1D */ +32768 /* 1E */ +}; + +/* +* Convert the AETH RNR timeout code into the number of milliseconds. +*/ +static u32 rnr_table[32 ] = { +656 ,/* 0 */ +1 ,/* 1 */ +1 ,/* 2 */ +1 ,/* 3 */ +1 ,/* 4 */ +1 ,/* 5 */ +1 ,/* 6 */ +1 ,/* 7 */ +1 ,/* 8 */ +1 ,/* 9 */ +1 ,/* A */ +1 ,/* B */ +1 ,/* C */ +1 ,/* D */ +2 ,/* E */ +2 ,/* F */ +3 ,/* 10 */ +4 ,/* 11 */ +6 ,/* 12 */ +8 ,/* 13 */ +11 ,/* 14 */ +16 ,/* 15 */ +21 ,/* 16 */ +31 ,/* 17 */ +41 ,/* 18 */ +62 ,/* 19 */ +82 ,/* 1A */ +123 ,/* 1B */ +164 ,/* 1C */ +246 ,/* 1D */ +328 ,/* 1E */ +492 /* 1F */ +}; + +/* +* Translate ib_wr_opcode into ib_wc_opcode. +*/ +static enum ib_wc_opcode wc_opcode[] = { + [IB_WR_RDMA_WRITE ] =IB_WC_RDMA_WRITE , + [IB_WR_RDMA_WRITE_WITH_IMM ] =IB_WC_RDMA_WRITE , + [IB_WR_SEND ] =IB_WC_SEND , + [IB_WR_SEND_WITH_IMM ] =IB_WC_SEND , + [IB_WR_RDMA_READ ] =IB_WC_RDMA_READ , + [IB_WR_ATOMIC_CMP_AND_SWP ] =IB_WC_COMP_SWAP , + [IB_WR_ATOMIC_FETCH_AND_ADD ] =IB_WC_FETCH_ADD +}; + +/* +* Array of device pointers. +*/ +static uint32_t number_of_devices; +static struct ipath_ibdev **ipath_devices; + +/* +* Global table of GID to attached QPs. +* The table is global to all ipath devices since a send from one QP/device +* needs to be locally routed to any locally attached QPs on the same +* or different device. +*/ +static struct rb_root mcast_tree; +static spinlock_t mcast_lock =SPIN_LOCK_UNLOCKED ; + +/* +* Allocate a structure to link a QP to the multicast GID structure. +*/ +static struct ipath_mcast_qp *ipath_mcast_qp_alloc (struct ipath_qp *qp) +{ +struct ipath_mcast_qp *mqp; + + mqp =kmalloc (sizeof (*mqp),GFP_KERNEL ); +if (!mqp) +return NULL ; + + mqp->qp = qp; +atomic_inc (&qp->refcount); + +return mqp; +} + +static void ipath_mcast_qp_free (struct ipath_mcast_qp *mqp) +{ +struct ipath_qp *qp = mqp->qp; + +/* Notify ipath_destroy_qp() if it is waiting. */ +if (atomic_dec_and_test (&qp->refcount)) +wake_up (&qp->wait); + +kfree (mqp); +} + +/* +* Allocate a structure for the multicast GID. +* A list of QPs will be attached to this structure. +*/ +static struct ipath_mcast *ipath_mcast_alloc (union ib_gid *mgid) +{ +struct ipath_mcast *mcast; + + mcast =kmalloc (sizeof (*mcast),GFP_KERNEL ); +if (!mcast) +return NULL ; + + mcast->mgid = *mgid; +INIT_LIST_HEAD (&mcast->qp_list); +init_waitqueue_head (&mcast->wait); +atomic_set (&mcast->refcount,0 ); + +return mcast; +} + +static void ipath_mcast_free (struct ipath_mcast *mcast) +{ +struct ipath_mcast_qp *p, *tmp; + +list_for_each_entry_safe (p, tmp, &mcast->qp_list, list) +ipath_mcast_qp_free (p); + +kfree (mcast); +} + +/* +* Search the global table for the given multicast GID. +* Return it or NULL if not found. +* The caller is responsible for decrementing the reference count if found. +*/ +static struct ipath_mcast *ipath_mcast_find (union ib_gid *mgid) +{ +struct rb_node *n; +unsigned long flags; + +spin_lock_irqsave (&mcast_lock, flags); + n = mcast_tree.rb_node; +while (n) { +struct ipath_mcast *mcast; +int ret; + + mcast = rb_entry(n, struct ipath_mcast, rb_node); + + ret =memcmp (mgid->raw, mcast->mgid.raw,sizeof (union ib_gid )); +if (ret <0 ) + n = n->rb_left; +else if (ret >0 ) + n = n->rb_right; +else { +atomic_inc (&mcast->refcount); +spin_unlock_irqrestore (&mcast_lock, flags); +return mcast; + } + } +spin_unlock_irqrestore (&mcast_lock, flags); + +return NULL ; +} + +/* +* Insert the multicast GID into the table and +* attach the QP structure. +* Return zero if both were added. +* Return EEXIST if the GID was already in the table but the QP was added. +* Return ESRCH if the QP was already attached and neither structure was added. +*/ +static int ipath_mcast_add (struct ipath_mcast *mcast, +struct ipath_mcast_qp *mqp) +{ +struct rb_node **n = &mcast_tree.rb_node; +struct rb_node *pn =NULL ; +unsigned long flags; + +spin_lock_irqsave (&mcast_lock, flags); + +while (*n) { +struct ipath_mcast *tmcast; +struct ipath_mcast_qp *p; +int ret; + + pn = *n; + tmcast = rb_entry(pn, struct ipath_mcast, rb_node); + + ret =memcmp (mcast->mgid.raw, tmcast->mgid.raw, +sizeof (union ib_gid )); +if (ret <0 ) { + n = &pn->rb_left; +continue ; + } +if (ret >0 ) { + n = &pn->rb_right; +continue ; + } + +/* Search the QP list to see if this is already there. */ +list_for_each_entry_rcu (p, &tmcast->qp_list, list) { +if (p->qp == mqp->qp) { +spin_unlock_irqrestore (&mcast_lock, flags); +return ESRCH ; + } + } +list_add_tail_rcu (&mqp->list, &tmcast->qp_list); +spin_unlock_irqrestore (&mcast_lock, flags); +return EEXIST ; + } + +list_add_tail_rcu (&mqp->list, &mcast->qp_list); + +atomic_inc (&mcast->refcount); +rb_link_node (&mcast->rb_node, pn, n); +rb_insert_color (&mcast->rb_node, &mcast_tree); + +spin_unlock_irqrestore (&mcast_lock, flags); + +return 0 ; +} + +static int ipath_multicast_attach (struct ib_qp *ibqp,union ib_gid *gid, +u16 lid) +{ +struct ipath_qp *qp =to_iqp (ibqp); +struct ipath_mcast *mcast; +struct ipath_mcast_qp *mqp; + +/* +* Allocate data structures since its better to do this outside of +* spin locks and it will most likely be needed. +*/ + mcast =ipath_mcast_alloc (gid); +if (mcast ==NULL ) +return -ENOMEM ; + mqp =ipath_mcast_qp_alloc (qp); +if (mqp ==NULL ) { +ipath_mcast_free (mcast); +return -ENOMEM ; + } +switch (ipath_mcast_add (mcast, mqp)) { +case ESRCH : +/* Neither was used: can't attach the same QP twice. */ +ipath_mcast_qp_free (mqp); +ipath_mcast_free (mcast); +return -EINVAL ; +case EEXIST :/* The mcast wasn't used */ +ipath_mcast_free (mcast); +break ; +default : +break ; + } +return 0 ; +} + +static int ipath_multicast_detach (struct ib_qp *ibqp,union ib_gid *gid, +u16 lid) +{ +struct ipath_qp *qp =to_iqp (ibqp); +struct ipath_mcast *mcast =NULL ; +struct ipath_mcast_qp *p, *tmp; +struct rb_node *n; +unsigned long flags; +int last =0 ; + +spin_lock_irqsave (&mcast_lock, flags); + +/* Find the GID in the mcast table. */ + n = mcast_tree.rb_node; +while (1 ) { +int ret; + +if (n ==NULL ) { +spin_unlock_irqrestore (&mcast_lock, flags); +return 0 ; + } + + mcast = rb_entry(n, struct ipath_mcast, rb_node); + ret =memcmp (gid->raw, mcast->mgid.raw,sizeof (union ib_gid )); +if (ret <0 ) + n = n->rb_left; +else if (ret >0 ) + n = n->rb_right; +else +break ; + } + +/* Search the QP list. */ +list_for_each_entry_safe (p, tmp, &mcast->qp_list, list) { +if (p->qp != qp) +continue ; +/* +* We found it, so remove it, but don't poison the forward link +* until we are sure there are no list walkers. +*/ +list_del_rcu (&p->list); + +/* If this was the last attached QP, remove the GID too. */ +if (list_empty (&mcast->qp_list)) { +rb_erase (&mcast->rb_node, &mcast_tree); + last =1 ; + } +break ; + } + +spin_unlock_irqrestore (&mcast_lock, flags); + +if (p) { +/* +* Wait for any list walkers to finish before freeing the +* list element. +*/ +wait_event (mcast->wait,atomic_read (&mcast->refcount) <=1 ); +ipath_mcast_qp_free (p); + } +if (last) { +atomic_dec (&mcast->refcount); +wait_event (mcast->wait, !atomic_read (&mcast->refcount)); +ipath_mcast_free (mcast); + } + +return 0 ; +} + +/* +* Copy data to SGE memory. +*/ +static void copy_sge (struct ipath_sge_state *ss,void *data,u32 length) +{ +struct ipath_sge *sge = &ss->sge; + +while (length) { +u32 len = sge->length; + +BUG_ON (len ==0 ); +if (len > length) + len = length; +memcpy (sge->vaddr, data, len); + sge->vaddr += len; + sge->length -= len; + sge->sge_length -= len; +if (sge->sge_length ==0 ) { +if (--ss->num_sge) + *sge = *ss->sg_list++; + }else if (sge->length ==0 && sge->mr !=NULL ) { +if (++sge->n >=IPATH_SEGSZ ) { +if (++sge->m >= sge->mr->mapsz) +break ; + sge->n =0 ; + } + sge->vaddr = sge->mr->map[sge->m]->segs[sge->n].vaddr; + sge->length = sge->mr->map[sge->m]->segs[sge->n].length; + } + data += len; + length -= len; + } +} + +/* +* Skip over length bytes of SGE memory. +*/ +static void skip_sge (struct ipath_sge_state *ss,u32 length) +{ +struct ipath_sge *sge = &ss->sge; + +while (length > sge->sge_length) { + length -= sge->sge_length; + ss->sge = *ss->sg_list++; + } +while (length) { +u32 len = sge->length; + +BUG_ON (len ==0 ); +if (len > length) + len = length; + sge->vaddr += len; + sge->length -= len; + sge->sge_length -= len; +if (sge->sge_length ==0 ) { +if (--ss->num_sge) + *sge = *ss->sg_list++; + }else if (sge->length ==0 && sge->mr !=NULL ) { +if (++sge->n >=IPATH_SEGSZ ) { +if (++sge->m >= sge->mr->mapsz) +break ; + sge->n =0 ; + } + sge->vaddr = sge->mr->map[sge->m]->segs[sge->n].vaddr; + sge->length = sge->mr->map[sge->m]->segs[sge->n].length; + } + length -= len; + } +} + +static inline u32 alloc_qpn (struct ipath_qp_table *qpt) +{ +u32 i, offset, max_scan, qpn; +struct qpn_map *map; + + qpn = qpt->last +1 ; +if (qpn >=QPN_MAX ) + qpn =2 ; + offset = qpn &BITS_PER_PAGE_MASK ; + map = &qpt->map[qpn /BITS_PER_PAGE ]; + max_scan = qpt->nmaps - !offset; +for (i =0 ;;) { +if (unlikely (!map->page)) { +unsigned long page =get_zeroed_page (GFP_KERNEL ); +unsigned long flags; + +/* +* Free the page if someone raced with us +* installing it: +*/ +spin_lock_irqsave (&qpt->lock, flags); +if (map->page) +free_page (page); +else + map->page = (void *)page; +spin_unlock_irqrestore (&qpt->lock, flags); +if (unlikely (!map->page)) +break ; + } +if (likely (atomic_read (&map->n_free))) { +do { +if (!test_and_set_bit (offset, map->page)) { +atomic_dec (&map->n_free); + qpt->last = qpn; +return qpn; + } + offset =find_next_offset (map, offset); + qpn =mk_qpn (qpt, map, offset); +/* +* This test differs from alloc_pidmap(). +* If find_next_offset() does find a zero bit, +* we don't need to check for QPN wrapping +* around past our starting QPN. We +* just need to be sure we don't loop forever. +*/ + }while (offset <BITS_PER_PAGE && qpn <QPN_MAX ); + } +/* +* In order to keep the number of pages allocated to a minimum, +* we scan the all existing pages before increasing the size +* of the bitmap table. +*/ +if (++i > max_scan) { +if (qpt->nmaps ==QPNMAP_ENTRIES ) +break ; + map = &qpt->map[qpt->nmaps++]; + offset =0 ; + }else if (map < &qpt->map[qpt->nmaps]) { + ++map; + offset =0 ; + }else { + map = &qpt->map[0 ]; + offset =2 ; + } + qpn =mk_qpn (qpt, map, offset); + } +return 0 ; +} + +static inline void free_qpn (struct ipath_qp_table *qpt,u32 qpn) +{ +struct qpn_map *map; + + map = qpt->map + qpn /BITS_PER_PAGE ; +if (map->page) +clear_bit (qpn &BITS_PER_PAGE_MASK , map->page); +atomic_inc (&map->n_free); +} + +/* +* Allocate the next available QPN and put the QP into the hash table. +* The hash table holds a reference to the QP. +*/ +static int ipath_alloc_qpn (struct ipath_qp_table *qpt,struct ipath_qp *qp, +enum ib_qp_type type) +{ +unsigned long flags; +u32 qpn; + +if (type ==IB_QPT_SMI ) + qpn =0 ; +else if (type ==IB_QPT_GSI ) + qpn =1 ; +else { +/* Allocate the next available QPN */ + qpn =alloc_qpn (qpt); +if (qpn ==0 ) { +return -ENOMEM ; + } + } + qp->ibqp.qp_num = qpn; + +/* Add the QP to the hash table. */ +spin_lock_irqsave (&qpt->lock, flags); + + qpn %= qpt->max; + qp->next = qpt->table[qpn]; + qpt->table[qpn] = qp; +atomic_inc (&qp->refcount); + +spin_unlock_irqrestore (&qpt->lock, flags); +return 0 ; +} + +/* +* Remove the QP from the table so it can't be found asynchronously by +* the receive interrupt routine. +*/ +static void ipath_free_qp (struct ipath_qp_table *qpt,struct ipath_qp *qp) +{ +struct ipath_qp *q, **qpp; +unsigned long flags; +int fnd =0 ; + +spin_lock_irqsave (&qpt->lock, flags); + +/* Remove QP from the hash table. */ + qpp = &qpt->table[qp->ibqp.qp_num % qpt->max]; +for (; (q = *qpp) !=NULL ; qpp = &q->next) { +if (q == qp) { + *qpp = qp->next; + qp->next =NULL ; +atomic_dec (&qp->refcount); + fnd =1 ; +break ; + } + } + +spin_unlock_irqrestore (&qpt->lock, flags); + +if (!fnd) +return ; + +/* If QPN is not reserved, mark QPN free in the bitmap. */ +if (qp->ibqp.qp_num >1 ) +free_qpn (qpt, qp->ibqp.qp_num); + +wait_event (qp->wait, !atomic_read (&qp->refcount)); +} + +/* +* Remove all QPs from the table. +*/ +static void ipath_free_all_qps (struct ipath_qp_table *qpt) +{ +unsigned long flags; +struct ipath_qp *qp, *nqp; +u32 n; + +for (n =0 ; n < qpt->max; n++) { +spin_lock_irqsave (&qpt->lock, flags); + qp = qpt->table[n]; + qpt->table[n] =NULL ; +spin_unlock_irqrestore (&qpt->lock, flags); + +while (qp) { + nqp = qp->next; +if (qp->ibqp.qp_num >1 ) +free_qpn (qpt, qp->ibqp.qp_num); +if (!atomic_dec_and_test (&qp->refcount) || + !ipath_destroy_qp (&qp->ibqp)) +_VERBS_INFO ("QP memory leak!\n" ); + qp = nqp; + } + } + +for (n =0 ; n <ARRAY_SIZE (qpt->map); n++) { +if (qpt->map[n].page) +free_page ((unsigned long )qpt->map[n].page); + } +} + +/* +* Return the QP with the given QPN. +* The caller is responsible for decrementing the QP reference count when done. +*/ +static struct ipath_qp *ipath_lookup_qpn (struct ipath_qp_table *qpt,u32 qpn) +{ +unsigned long flags; +struct ipath_qp *qp; + +spin_lock_irqsave (&qpt->lock, flags); + +for (qp = qpt->table[qpn % qpt->max]; qp; qp = qp->next) { +if (qp->ibqp.qp_num == qpn) { +atomic_inc (&qp->refcount); +break ; + } + } + +spin_unlock_irqrestore (&qpt->lock, flags); +return qp; +} + +static int ipath_alloc_lkey (struct ipath_lkey_table *rkt, +struct ipath_mregion *mr) +{ +unsigned long flags; +u32 r; +u32 n; + +spin_lock_irqsave (&rkt->lock, flags); + +/* Find the next available LKEY */ + r = n = rkt->next; +for (;;) { +if (rkt->table[r] ==NULL ) +break ; + r = (r +1 ) & (rkt->max -1 ); +if (r == n) { +spin_unlock_irqrestore (&rkt->lock, flags); +_VERBS_INFO ("LKEY table full\n" ); +return 0 ; + } + } + rkt->next = (r +1 ) & (rkt->max -1 ); +/* +* Make sure lkey is never zero which is reserved to indicate an +* unrestricted LKEY. +*/ + rkt->gen++; + mr->lkey = (r << (32 - ib_ipath_lkey_table_size)) | + ((((1 << (24 - ib_ipath_lkey_table_size)) -1 ) & rkt->gen) <<8 ); +if (mr->lkey ==0 ) { + mr->lkey |=1 <<8 ; + rkt->gen++; + } + rkt->table[r] = mr; +spin_unlock_irqrestore (&rkt->lock, flags); + +return 1 ; +} + +static void ipath_free_lkey (struct ipath_lkey_table *rkt,u32 lkey) +{ +unsigned long flags; +u32 r; + +if (lkey ==0 ) +return ; + r = lkey >> (32 - ib_ipath_lkey_table_size); +spin_lock_irqsave (&rkt->lock, flags); + rkt->table[r] =NULL ; +spin_unlock_irqrestore (&rkt->lock, flags); +} + +/* +* Check the IB SGE for validity and initialize our internal version of it. +* Return 1 if OK, else zero. +*/ +static int ipath_lkey_ok (struct ipath_lkey_table *rkt,struct ipath_sge *isge, +struct ib_sge *sge,int acc) +{ +struct ipath_mregion *mr; +size_t off; + +/* +* We use LKEY == zero to mean a physical kmalloc() address. +* This is a bit of a hack since we rely on dma_map_single() +* being reversible by calling bus_to_virt(). +*/ +if (sge->lkey ==0 ) { + isge->mr =NULL ; + isge->vaddr =bus_to_virt (sge->addr); + isge->length = sge->length; + isge->sge_length = sge->length; +return 1 ; + } +spin_lock (&rkt->lock); + mr = rkt->table[(sge->lkey >> (32 - ib_ipath_lkey_table_size))]; +spin_unlock (&rkt->lock); +if (unlikely (mr ==NULL || mr->lkey != sge->lkey)) +return 0 ; + + off = sge->addr - mr->user_base; +if (unlikely (sge->addr < mr->user_base || + off + sge->length > mr->length || + (mr->access_flags & acc) != acc)) +return 0 ; + + off += mr->offset; + isge->mr = mr; + isge->m =0 ; + isge->n =0 ; +while (off >= mr->map[isge->m]->segs[isge->n].length) { + off -= mr->map[isge->m]->segs[isge->n].length; +if (++isge->n >=IPATH_SEGSZ ) { + isge->m++; + isge->n =0 ; + } + } + isge->vaddr = mr->map[isge->m]->segs[isge->n].vaddr + off; + isge->length = mr->map[isge->m]->segs[isge->n].length - off; + isge->sge_length = sge->length; +return 1 ; +} + +/* +* Initialize the qp->s_sge after a restart. +* The QP s_lock should be held. +*/ +static void ipath_init_restart (struct ipath_qp *qp,struct ipath_swqe *wqe) +{ +struct ipath_ibdev *dev; +u32 len; + + len = ((qp->s_psn - wqe->psn) &0xFFFFFF ) * +ib_mtu_enum_to_int (qp->path_mtu); + qp->s_sge.sge = wqe->sg_list[0 ]; + qp->s_sge.sg_list = wqe->sg_list +1 ; + qp->s_sge.num_sge = wqe->wr.num_sge; +skip_sge (&qp->s_sge, len); + qp->s_len = wqe->length - len; + dev =to_idev (qp->ibqp.device); +spin_lock (&dev->pending_lock); +if (qp->timerwait.next ==LIST_POISON1 ) +list_add_tail (&qp->timerwait, + &dev->pending[dev->pending_index]); +spin_unlock (&dev->pending_lock); +} + +/* +* Check the IB virtual address, length, and RKEY. +* Return 1 if OK, else zero. +* The QP r_rq.lock should be held. +*/ +static int ipath_rkey_ok (struct ipath_ibdev *dev,struct ipath_sge_state *ss, +u32 len,u64 vaddr,u32 rkey,int acc) +{ +struct ipath_lkey_table *rkt = &dev->lk_table; +struct ipath_sge *sge = &ss->sge; +struct ipath_mregion *mr; +size_t off; + +spin_lock (&rkt->lock); + mr = rkt->table[(rkey >> (32 - ib_ipath_lkey_table_size))]; +spin_unlock (&rkt->lock); +if (unlikely (mr ==NULL || mr->lkey != rkey)) +return 0 ; + + off = vaddr - mr->iova; +if (unlikely (vaddr < mr->iova || off + len > mr->length || + (mr->access_flags & acc) ==0 )) +return 0 ; + + off += mr->offset; + sge->mr = mr; + sge->m =0 ; + sge->n =0 ; +while (off >= mr->map[sge->m]->segs[sge->n].length) { + off -= mr->map[sge->m]->segs[sge->n].length; +if (++sge->n >=IPATH_SEGSZ ) { + sge->m++; + sge->n =0 ; + } + } + sge->vaddr = mr->map[sge->m]->segs[sge->n].vaddr + off; + sge->length = mr->map[sge->m]->segs[sge->n].length - off; + sge->sge_length = len; + ss->sg_list =NULL ; + ss->num_sge =1 ; +return 1 ; +} + +/* +* Add a new entry to the completion queue. +* This may be called with one of the qp->s_lock or qp->r_rq.lock held. +*/ +static void ipath_cq_enter (struct ipath_cq *cq,struct ib_wc *entry,int sig) +{ +unsigned long flags; +u32 next; + +spin_lock_irqsave (&cq->lock, flags); + + cq->queue[cq->head] = *entry; + next = cq->head +1 ; +if (next == cq->ibcq.cqe) + next =0 ; +if (likely (next != cq->tail)) + cq->head = next; +else { +spin_unlock_irqrestore (&cq->lock, flags); +if (cq->ibcq.event_handler) { +struct ib_event ev; + + ev.device = cq->ibcq.device; + ev.element.cq = &cq->ibcq; + ev.event =IB_EVENT_CQ_ERR ; + cq->ibcq.event_handler (&ev, cq->ibcq.cq_context); + } +return ; + } + +if (cq->notify ==IB_CQ_NEXT_COMP || + (cq->notify ==IB_CQ_SOLICITED && sig)) { + cq->notify =IB_CQ_NONE ; + cq->triggered++; +/* +* This will cause send_complete() to be called in +* another thread. +*/ +tasklet_schedule (&cq->comptask); + } + +spin_unlock_irqrestore (&cq->lock, flags); + +if (entry->status !=IB_WC_SUCCESS ) +to_idev (cq->ibcq.device)->n_wqe_errs++; +} + +static void send_complete (unsigned long data) +{ +struct ipath_cq *cq = (struct ipath_cq *)data; + +/* +* The completion handler will most likely rearm the notification +* and poll for all pending entries. If a new completion entry +* is added while we are in this routine, tasklet_schedule() +* won't call us again until we return so we check triggered to +* see if we need to call the handler again. +*/ +for (;;) { +u8 triggered = cq->triggered; + + cq->ibcq.comp_handler (&cq->ibcq, cq->ibcq.cq_context); + +if (cq->triggered == triggered) +return ; + } +} + +/* +* This is the QP state transition table. +* See ipath_modify_qp() for details. +*/ +static const struct { +int trans; +u32 req_param[IB_QPT_RAW_IPV6 ]; +u32 opt_param[IB_QPT_RAW_IPV6 ]; +} qp_state_table[IB_QPS_ERR +1 ][IB_QPS_ERR +1 ] = { + [IB_QPS_RESET ] = { + [IB_QPS_RESET ] = { .trans =IPATH_TRANS_ANY2RST }, + [IB_QPS_ERR ] = { .trans =IPATH_TRANS_ANY2ERR }, + [IB_QPS_INIT ] = { + .trans =IPATH_TRANS_RST2INIT , + .req_param = { + [IB_QPT_SMI ] = (IB_QP_PKEY_INDEX | +IB_QP_QKEY ), + [IB_QPT_GSI ] = (IB_QP_PKEY_INDEX | +IB_QP_QKEY ), + [IB_QPT_UD ] = (IB_QP_PKEY_INDEX | +IB_QP_PORT | +IB_QP_QKEY ), + [IB_QPT_UC ] = (IB_QP_PKEY_INDEX | +IB_QP_PORT | +IB_QP_ACCESS_FLAGS ), + [IB_QPT_RC ] = (IB_QP_PKEY_INDEX | +IB_QP_PORT | +IB_QP_ACCESS_FLAGS ), + }, + }, + }, + [IB_QPS_INIT ] = { + [IB_QPS_RESET ] = { .trans =IPATH_TRANS_ANY2RST }, + [IB_QPS_ERR ] = { .trans =IPATH_TRANS_ANY2ERR }, + [IB_QPS_INIT ] = { + .trans =IPATH_TRANS_INIT2INIT , + .opt_param = { + [IB_QPT_SMI ] = (IB_QP_PKEY_INDEX | +IB_QP_QKEY ), + [IB_QPT_GSI ] = (IB_QP_PKEY_INDEX | +IB_QP_QKEY ), + [IB_QPT_UD ] = (IB_QP_PKEY_INDEX | +IB_QP_PORT | +IB_QP_QKEY ), + [IB_QPT_UC ] = (IB_QP_PKEY_INDEX | +IB_QP_PORT | +IB_QP_ACCESS_FLAGS ), + [IB_QPT_RC ] = (IB_QP_PKEY_INDEX | +IB_QP_PORT | +IB_QP_ACCESS_FLAGS ), + } + }, + [IB_QPS_RTR ] = { + .trans =IPATH_TRANS_INIT2RTR , + .req_param = { + [IB_QPT_UC ] = (IB_QP_AV | +IB_QP_PATH_MTU | +IB_QP_DEST_QPN | +IB_QP_RQ_PSN ), + [IB_QPT_RC ] = (IB_QP_AV | +IB_QP_PATH_MTU | +IB_QP_DEST_QPN | +IB_QP_RQ_PSN | +IB_QP_MAX_DEST_RD_ATOMIC | +IB_QP_MIN_RNR_TIMER ), + }, + .opt_param = { + [IB_QPT_SMI ] = (IB_QP_PKEY_INDEX | +IB_QP_QKEY ), + [IB_QPT_GSI ] = (IB_QP_PKEY_INDEX | +IB_QP_QKEY ), + [IB_QPT_UD ] = (IB_QP_PKEY_INDEX | +IB_QP_QKEY ), + [IB_QPT_UC ] = (IB_QP_ALT_PATH | +IB_QP_ACCESS_FLAGS | +IB_QP_PKEY_INDEX ), + [IB_QPT_RC ] = (IB_QP_ALT_PATH | +IB_QP_ACCESS_FLAGS | +IB_QP_PKEY_INDEX ), + } + } + }, + [IB_QPS_RTR ] = { + [IB_QPS_RESET ] = { .trans =IPATH_TRANS_ANY2RST }, + [IB_QPS_ERR ] = { .trans =IPATH_TRANS_ANY2ERR }, + [IB_QPS_RTS ] = { + .trans =IPATH_TRANS_RTR2RTS , + .req_param = { + [IB_QPT_SMI ] =IB_QP_SQ_PSN , + [IB_QPT_GSI ] =IB_QP_SQ_PSN , + [IB_QPT_UD ] =IB_QP_SQ_PSN , + [IB_QPT_UC ] =IB_QP_SQ_PSN , + [IB_QPT_RC ] = (IB_QP_TIMEOUT | +IB_QP_RETRY_CNT | +IB_QP_RNR_RETRY | +IB_QP_SQ_PSN | +IB_QP_MAX_QP_RD_ATOMIC ), + }, + .opt_param = { + [IB_QPT_SMI ] = (IB_QP_CUR_STATE |IB_QP_QKEY ), + [IB_QPT_GSI ] = (IB_QP_CUR_STATE |IB_QP_QKEY ), + [IB_QPT_UD ] = (IB_QP_CUR_STATE |IB_QP_QKEY ), + [IB_QPT_UC ] = (IB_QP_CUR_STATE | +IB_QP_ALT_PATH | +IB_QP_ACCESS_FLAGS | +IB_QP_PKEY_INDEX | +IB_QP_PATH_MIG_STATE ), + [IB_QPT_RC ] = (IB_QP_CUR_STATE | +IB_QP_ALT_PATH | +IB_QP_ACCESS_FLAGS | +IB_QP_PKEY_INDEX | +IB_QP_MIN_RNR_TIMER | +IB_QP_PATH_MIG_STATE ), + } + } + }, + [IB_QPS_RTS ] = { + [IB_QPS_RESET ] = { .trans =IPATH_TRANS_ANY2RST }, + [IB_QPS_ERR ] = { .trans =IPATH_TRANS_ANY2ERR }, + [IB_QPS_RTS ] = { + .trans =IPATH_TRANS_RTS2RTS , + .opt_param = { + [IB_QPT_SMI ] = (IB_QP_CUR_STATE |IB_QP_QKEY ), + [IB_QPT_GSI ] = (IB_QP_CUR_STATE |IB_QP_QKEY ), + [IB_QPT_UD ] = (IB_QP_CUR_STATE |IB_QP_QKEY ), + [IB_QPT_UC ] = (IB_QP_ACCESS_FLAGS | +IB_QP_ALT_PATH | +IB_QP_PATH_MIG_STATE ), + [IB_QPT_RC ] = (IB_QP_ACCESS_FLAGS | +IB_QP_ALT_PATH | +IB_QP_PATH_MIG_STATE | +IB_QP_MIN_RNR_TIMER ), + } + }, + [IB_QPS_SQD ] = { + .trans =IPATH_TRANS_RTS2SQD , + }, + }, + [IB_QPS_SQD ] = { + [IB_QPS_RESET ] = { .trans =IPATH_TRANS_ANY2RST }, + [IB_QPS_ERR ] = { .trans =IPATH_TRANS_ANY2ERR }, + [IB_QPS_RTS ] = { + .trans =IPATH_TRANS_SQD2RTS , + .opt_param = { + [IB_QPT_SMI ] = (IB_QP_CUR_STATE |IB_QP_QKEY ), + [IB_QPT_GSI ] = (IB_QP_CUR_STATE |IB_QP_QKEY ), + [IB_QPT_UD ] = (IB_QP_CUR_STATE |IB_QP_QKEY ), + [IB_QPT_UC ] = (IB_QP_CUR_STATE | +IB_QP_ALT_PATH | +IB_QP_ACCESS_FLAGS | +IB_QP_PATH_MIG_STATE ), + [IB_QPT_RC ] = (IB_QP_CUR_STATE | +IB_QP_ALT_PATH | +IB_QP_ACCESS_FLAGS | +IB_QP_MIN_RNR_TIMER | +IB_QP_PATH_MIG_STATE ), + } + }, + [IB_QPS_SQD ] = { + .trans =IPATH_TRANS_SQD2SQD , + .opt_param = { + [IB_QPT_SMI ] = (IB_QP_CUR_STATE |IB_QP_QKEY ), + [IB_QPT_GSI ] = (IB_QP_CUR_STATE |IB_QP_QKEY ), + [IB_QPT_UD ] = (IB_QP_PKEY_INDEX |IB_QP_QKEY ), + [IB_QPT_UC ] = (IB_QP_AV | +IB_QP_TIMEOUT | +IB_QP_CUR_STATE | +IB_QP_ALT_PATH | +IB_QP_ACCESS_FLAGS | +IB_QP_PKEY_INDEX | +IB_QP_PATH_MIG_STATE ), + [IB_QPT_RC ] = (IB_QP_AV | +IB_QP_TIMEOUT | +IB_QP_RETRY_CNT | +IB_QP_RNR_RETRY | +IB_QP_MAX_QP_RD_ATOMIC | +IB_QP_MAX_DEST_RD_ATOMIC | +IB_QP_CUR_STATE | +IB_QP_ALT_PATH | +IB_QP_ACCESS_FLAGS | +IB_QP_PKEY_INDEX | +IB_QP_MIN_RNR_TIMER | +IB_QP_PATH_MIG_STATE ), + } + } + }, + [IB_QPS_SQE ] = { + [IB_QPS_RESET ] = { .trans =IPATH_TRANS_ANY2RST }, + [IB_QPS_ERR ] = { .trans =IPATH_TRANS_ANY2ERR }, + [IB_QPS_RTS ] = { + .trans =IPATH_TRANS_SQERR2RTS , + .opt_param = { + [IB_QPT_SMI ] = (IB_QP_CUR_STATE |IB_QP_QKEY ), + [IB_QPT_GSI ] = (IB_QP_CUR_STATE |IB_QP_QKEY ), + [IB_QPT_UD ] = (IB_QP_CUR_STATE |IB_QP_QKEY ), + [IB_QPT_UC ] =IB_QP_CUR_STATE , + [IB_QPT_RC ] = (IB_QP_CUR_STATE | +IB_QP_MIN_RNR_TIMER ), + } + } + }, + [IB_QPS_ERR ] = { + [IB_QPS_RESET ] = { .trans =IPATH_TRANS_ANY2RST }, + [IB_QPS_ERR ] = { .trans =IPATH_TRANS_ANY2ERR } + } +}; + +/* +* Initialize the QP state to the reset state. +*/ +static void ipath_reset_qp (struct ipath_qp *qp) +{ + qp->remote_qpn =0 ; + qp->qkey =0 ; + qp->qp_access_flags =0 ; + qp->s_hdrwords =0 ; + qp->s_psn =0 ; + qp->r_psn =0 ; +atomic_set (&qp->msn,0 ); +if (qp->ibqp.qp_type ==IB_QPT_RC ) { + qp->s_state =IB_OPCODE_RC_SEND_LAST ; + qp->r_state =IB_OPCODE_RC_SEND_LAST ; + }else { + qp->s_state =IB_OPCODE_UC_SEND_LAST ; + qp->r_state =IB_OPCODE_UC_SEND_LAST ; + } + qp->s_ack_state =IB_OPCODE_RC_ACKNOWLEDGE ; + qp->s_nak_state =0 ; + qp->s_rnr_timeout =0 ; + qp->s_head =0 ; + qp->s_tail =0 ; + qp->s_cur =0 ; + qp->s_last =0 ; + qp->s_ssn =1 ; + qp->s_lsn =0 ; + qp->r_rq.head =0 ; + qp->r_rq.tail =0 ; + qp->r_reuse_sge =0 ; +} + +/* +* Flush send work queue. +* The QP s_lock should be held. +*/ +static void ipath_sqerror_qp (struct ipath_qp *qp,struct ib_wc *wc) +{ +struct ipath_ibdev *dev =to_idev (qp->ibqp.device); +struct ipath_swqe *wqe =get_swqe_ptr (qp, qp->s_last); + +_VERBS_INFO ("Send queue error on QP%d/%d: err: %d\n" , + qp->ibqp.qp_num, qp->remote_qpn, wc->status); + +spin_lock (&dev->pending_lock); +/* XXX What if its already removed by the timeout code? */ +if (qp->timerwait.next !=LIST_POISON1 ) +list_del (&qp->timerwait); +if (qp->piowait.next !=LIST_POISON1 ) +list_del (&qp->piowait); +spin_unlock (&dev->pending_lock); + +ipath_cq_enter (to_icq (qp->ibqp.send_cq), wc,1 ); +if (++qp->s_last >= qp->s_size) + qp->s_last =0 ; + + wc->status =IB_WC_WR_FLUSH_ERR ; + +while (qp->s_last != qp->s_head) { + wc->wr_id = wqe->wr.wr_id; + wc->opcode = wc_opcode[wqe->wr.opcode]; +ipath_cq_enter (to_icq (qp->ibqp.send_cq), wc,1 ); +if (++qp->s_last >= qp->s_size) + qp->s_last =0 ; + wqe =get_swqe_ptr (qp, qp->s_last); + } + qp->s_cur = qp->s_tail = qp->s_head; + qp->state =IB_QPS_SQE ; +} + +/* +* Flush both send and receive work queues. +* QP r_rq.lock and s_lock should be held. +*/ +static void ipath_error_qp (struct ipath_qp *qp) +{ +struct ipath_ibdev *dev =to_idev (qp->ibqp.device); +struct ib_wc wc; + +_VERBS_INFO ("QP%d/%d in error state\n" , + qp->ibqp.qp_num, qp->remote_qpn); + +spin_lock (&dev->pending_lock); +/* XXX What if its already removed by the timeout code? */ +if (qp->timerwait.next !=LIST_POISON1 ) +list_del (&qp->timerwait); +if (qp->piowait.next !=LIST_POISON1 ) +list_del (&qp->piowait); +spin_unlock (&dev->pending_lock); + + wc.status =IB_WC_WR_FLUSH_ERR ; + wc.vendor_err =0 ; + wc.byte_len =0 ; + wc.imm_data =0 ; + wc.qp_num = qp->ibqp.qp_num; + wc.src_qp =0 ; + wc.wc_flags =0 ; + wc.pkey_index =0 ; + wc.slid =0 ; + wc.sl =0 ; + wc.dlid_path_bits =0 ; + wc.port_num =0 ; + +while (qp->s_last != qp->s_head) { +struct ipath_swqe *wqe =get_swqe_ptr (qp, qp->s_last); + + wc.wr_id = wqe->wr.wr_id; + wc.opcode = wc_opcode[wqe->wr.opcode]; +if (++qp->s_last >= qp->s_size) + qp->s_last =0 ; +ipath_cq_enter (to_icq (qp->ibqp.send_cq), &wc,1 ); + } + qp->s_cur = qp->s_tail = qp->s_head; + qp->s_hdrwords =0 ; + qp->s_ack_state =IB_OPCODE_RC_ACKNOWLEDGE ; + + wc.opcode =IB_WC_RECV ; +while (qp->r_rq.tail != qp->r_rq.head) { + wc.wr_id =get_rwqe_ptr (&qp->r_rq, qp->r_rq.tail)->wr_id; +if (++qp->r_rq.tail >= qp->r_rq.size) + qp->r_rq.tail =0 ; +ipath_cq_enter (to_icq (qp->ibqp.recv_cq), &wc,1 ); + } +} + +static int ipath_modify_qp (struct ib_qp *ibqp,struct ib_qp_attr *attr, +int attr_mask) +{ +struct ipath_qp *qp =to_iqp (ibqp); +enum ib_qp_state cur_state, new_state; +u32 req_param, opt_param; +unsigned long flags; + +if (attr_mask &IB_QP_CUR_STATE ) { + cur_state = attr->cur_qp_state; +if (cur_state !=IB_QPS_RTR && + cur_state !=IB_QPS_RTS && + cur_state !=IB_QPS_SQD && cur_state !=IB_QPS_SQE ) +return -EINVAL ; +spin_lock_irqsave (&qp->r_rq.lock, flags); +spin_lock (&qp->s_lock); + }else { +spin_lock_irqsave (&qp->r_rq.lock, flags); +spin_lock (&qp->s_lock); + cur_state = qp->state; + } + +if (attr_mask &IB_QP_STATE ) { + new_state = attr->qp_state; +if (new_state <0 || new_state >IB_QPS_ERR ) + goto inval; + }else + new_state = cur_state; + +switch (qp_state_table[cur_state][new_state].trans) { +case IPATH_TRANS_INVALID : + goto inval; + +case IPATH_TRANS_ANY2RST : +ipath_reset_qp (qp); +break ; + +case IPATH_TRANS_ANY2ERR : +ipath_error_qp (qp); +break ; + + } + + req_param = + qp_state_table[cur_state][new_state].req_param[qp->ibqp.qp_type]; + opt_param = + qp_state_table[cur_state][new_state].opt_param[qp->ibqp.qp_type]; + +if ((req_param & attr_mask) != req_param) + goto inval; + +if (attr_mask & ~(req_param | opt_param |IB_QP_STATE )) + goto inval; + +if (attr_mask &IB_QP_PKEY_INDEX ) { +struct ipath_ibdev *dev =to_idev (ibqp->device); + +if (attr->pkey_index >=ipath_layer_get_npkeys (dev->ib_unit)) + goto inval; + qp->s_pkey_index = attr->pkey_index; + } + +if (attr_mask &IB_QP_DEST_QPN ) + qp->remote_qpn = attr->dest_qp_num; + +if (attr_mask &IB_QP_SQ_PSN ) { + qp->s_next_psn = attr->sq_psn; + qp->s_last_psn = qp->s_next_psn -1 ; + } + +if (attr_mask &IB_QP_RQ_PSN ) + qp->r_psn = attr->rq_psn; + +if (attr_mask &IB_QP_ACCESS_FLAGS ) + qp->qp_access_flags = attr->qp_access_flags; + +if (attr_mask &IB_QP_AV ) + qp->remote_ah_attr = attr->ah_attr; + +if (attr_mask &IB_QP_PATH_MTU ) + qp->path_mtu = attr->path_mtu; + +if (attr_mask &IB_QP_RETRY_CNT ) + qp->s_retry = qp->s_retry_cnt = attr->retry_cnt; + +if (attr_mask &IB_QP_RNR_RETRY ) { + qp->s_rnr_retry = attr->rnr_retry; +if (qp->s_rnr_retry >7 ) + qp->s_rnr_retry =7 ; + qp->s_rnr_retry_cnt = qp->s_rnr_retry; + } + +if (attr_mask &IB_QP_MIN_RNR_TIMER ) + qp->s_min_rnr_timer = attr->min_rnr_timer &0x1F ; + +if (attr_mask &IB_QP_QKEY ) + qp->qkey = attr->qkey; + +if (attr_mask &IB_QP_PKEY_INDEX ) + qp->s_pkey_index = attr->pkey_index; + + qp->state = new_state; +spin_unlock (&qp->s_lock); +spin_unlock_irqrestore (&qp->r_rq.lock, flags); + +/* +* Try to move to ARMED if QP1 changed to the RTS state. +*/ +if (qp->ibqp.qp_num ==1 && new_state ==IB_QPS_RTS ) { +struct ipath_ibdev *dev =to_idev (ibqp->device); + +/* +* Bounce the link even if it was active so the SM will +* reinitialize the SMA's state. +*/ +ipath_kset_linkstate ((dev->ib_unit <<16 ) |IPATH_IB_LINKDOWN ); +ipath_kset_linkstate ((dev->ib_unit <<16 ) |IPATH_IB_LINKARM ); + } +return 0 ; + +inval: +spin_unlock (&qp->s_lock); +spin_unlock_irqrestore (&qp->r_rq.lock, flags); +return -EINVAL ; +} + +/* +* Compute the AETH (syndrome + MSN). +* The QP s_lock should be held. +*/ +static u32 ipath_compute_aeth (struct ipath_qp *qp) +{ +u32 aeth =atomic_read (&qp->msn) &0xFFFFFF ; + +if (qp->s_nak_state) { + aeth |= qp->s_nak_state <<24 ; + }else if (qp->ibqp.srq) { +/* Shared receive queues don't generate credits. */ + aeth |=0x1F <<24 ; + }else { +u32 min, max, x; +u32 credits; + +/* +* Compute the number of credits available (RWQEs). +* XXX Not holding the r_rq.lock here so there is a small +* chance that the pair of reads are not atomic. +*/ + credits = qp->r_rq.head - qp->r_rq.tail; +if ((int )credits <0 ) + credits += qp->r_rq.size; +/* Binary search the credit table to find the code to use. */ + min =0 ; + max =31 ; +for (;;) { + x = (min + max) /2 ; +if (credit_table[x] == credits) +break ; +if (credit_table[x] > credits) + max = x; +else if (min == x) +break ; +else + min = x; + } + aeth |= x <<24 ; + } +return cpu_to_be32 (aeth); +} + + +static void no_bufs_available (struct ipath_qp *qp,struct ipath_ibdev *dev) +{ +unsigned long flags; + +spin_lock_irqsave (&dev->pending_lock, flags); +if (qp->piowait.next ==LIST_POISON1 ) +list_add_tail (&qp->piowait, &dev->piowait); +spin_unlock_irqrestore (&dev->pending_lock, flags); +/* +* Note that as soon as ipath_layer_want_buffer() is called and +* possibly before it returns, ipath_ib_piobufavail() +* could be called. If we are still in the tasklet function, +* tasklet_schedule() will not call us until the next time +* tasklet_schedule() is called. +* We clear the tasklet flag now since we are committing to return +* from the tasklet function. +*/ +tasklet_unlock (&qp->s_task); +ipath_layer_want_buffer (dev->ib_unit); + dev->n_piowait++; +} + +/* +* Process entries in the send work queue until the queue is exhausted. +* Only allow one CPU to send a packet per QP (tasklet). +* Otherwise, after we drop the QP lock, two threads could send +* packets out of order. +* This is similar to do_rc_send() below except we don't have timeouts or +* resends. +*/ +static void do_uc_send (unsigned long data) +{ +struct ipath_qp *qp = (struct ipath_qp *)data; +struct ipath_ibdev *dev =to_idev (qp->ibqp.device); +struct ipath_swqe *wqe; +unsigned long flags; +u16 lrh0; +u32 hwords; +u32 nwords; +u32 extra_bytes; +u32 bth0; +u32 bth2; +u32 pmtu =ib_mtu_enum_to_int (qp->path_mtu); +u32 len; +struct ipath_other_headers *ohdr; +struct ib_wc wc; + +if (test_and_set_bit (IPATH_S_BUSY , &qp->s_flags)) +return ; + +if (unlikely (qp->remote_ah_attr.dlid == +ipath_layer_get_lid (dev->ib_unit))) { +/* Pass in an uninitialized ib_wc to save stack space. */ +ipath_ruc_loopback (qp, &wc); +clear_bit (IPATH_S_BUSY , &qp->s_flags); +return ; + } + + ohdr = &qp->s_hdr.u.oth; +if (qp->remote_ah_attr.ah_flags &IB_AH_GRH ) + ohdr = &qp->s_hdr.u.l.oth; + +again: +/* Check for a constructed packet to be sent. */ +if (qp->s_hdrwords !=0 ) { +/* +* If no PIO bufs are available, return. +* An interrupt will call ipath_ib_piobufavail() +* when one is available. +*/ +if (ipath_verbs_send (dev->ib_unit, qp->s_hdrwords, + (uint32_t *) &qp->s_hdr, + qp->s_cur_size, qp->s_cur_sge)) { +no_bufs_available (qp, dev); +return ; + } + dev->n_unicast_xmit++; +/* Record that we sent the packet and s_hdr is empty. */ + qp->s_hdrwords =0 ; + } + + lrh0 =IPS_LRH_BTH ; +/* header size in 32-bit words LRH+BTH = (8+12)/4. */ + hwords =5 ; + +/* +* The lock is needed to synchronize between +* setting qp->s_ack_state and post_send(). +*/ +spin_lock_irqsave (&qp->s_lock, flags); + +if (!(state_ops[qp->state] &IPATH_PROCESS_SEND_OK )) + goto done; + + bth0 =ipath_layer_get_pkey (dev->ib_unit, qp->s_pkey_index); + +/* Send a request. */ + wqe =get_swqe_ptr (qp, qp->s_last); +switch (qp->s_state) { +default : +/* Signal the completion of the last send (if there is one). */ +if (qp->s_last != qp->s_tail) { +if (++qp->s_last == qp->s_size) + qp->s_last =0 ; +if (!test_bit (IPATH_S_SIGNAL_REQ_WR , &qp->s_flags) || + (wqe->wr.send_flags &IB_SEND_SIGNALED )) { + wc.wr_id = wqe->wr.wr_id; + wc.status =IB_WC_SUCCESS ; + wc.opcode = wc_opcode[wqe->wr.opcode]; + wc.vendor_err =0 ; + wc.byte_len = wqe->length; + wc.qp_num = qp->ibqp.qp_num; + wc.src_qp = qp->remote_qpn; + wc.pkey_index =0 ; + wc.slid = qp->remote_ah_attr.dlid; + wc.sl = qp->remote_ah_attr.sl; + wc.dlid_path_bits =0 ; + wc.port_num =0 ; +ipath_cq_enter (to_icq (qp->ibqp.send_cq), &wc, +0 ); + } + wqe =get_swqe_ptr (qp, qp->s_last); + } +/* Check if send work queue is empty. */ +if (qp->s_tail == qp->s_head) + goto done; +/* +* Start a new request. +*/ + qp->s_psn = wqe->psn = qp->s_next_psn; + qp->s_sge.sge = wqe->sg_list[0 ]; + qp->s_sge.sg_list = wqe->sg_list +1 ; + qp->s_sge.num_sge = wqe->wr.num_sge; + qp->s_len = len = wqe->length; +switch (wqe->wr.opcode) { +case IB_WR_SEND : +case IB_WR_SEND_WITH_IMM : +if (len > pmtu) { + qp->s_state =IB_OPCODE_UC_SEND_FIRST ; + len = pmtu; +break ; + } +if (wqe->wr.opcode ==IB_WR_SEND ) { + qp->s_state =IB_OPCODE_UC_SEND_ONLY ; + }else { + qp->s_state = +IB_OPCODE_UC_SEND_ONLY_WITH_IMMEDIATE ; +/* Immediate data comes after the BTH */ + ohdr->u.imm_data = wqe->wr.imm_data; + hwords +=1 ; + } +if (wqe->wr.send_flags &IB_SEND_SOLICITED ) + bth0 |=1 <<23 ; +break ; + +case IB_WR_RDMA_WRITE : +case IB_WR_RDMA_WRITE_WITH_IMM : + ohdr->u.rc.reth.vaddr = +cpu_to_be64 (wqe->wr.wr.rdma.remote_addr); + ohdr->u.rc.reth.rkey = +cpu_to_be32 (wqe->wr.wr.rdma.rkey); + ohdr->u.rc.reth.length =cpu_to_be32 (len); + hwords +=sizeof (struct ib_reth ) /4 ; +if (len > pmtu) { + qp->s_state =IB_OPCODE_UC_RDMA_WRITE_FIRST ; + len = pmtu; +break ; + } +if (wqe->wr.opcode ==IB_WR_RDMA_WRITE ) { + qp->s_state =IB_OPCODE_UC_RDMA_WRITE_ONLY ; + }else { + qp->s_state = +IB_OPCODE_UC_RDMA_WRITE_ONLY_WITH_IMMEDIATE ; +/* Immediate data comes after the RETH */ + ohdr->u.rc.imm_data = wqe->wr.imm_data; + hwords +=1 ; +if (wqe->wr.send_flags &IB_SEND_SOLICITED ) + bth0 |=1 <<23 ; + } +break ; + +default : + goto done; + } +if (++qp->s_tail >= qp->s_size) + qp->s_tail =0 ; +break ; + +case IB_OPCODE_UC_SEND_FIRST : + qp->s_state =IB_OPCODE_UC_SEND_MIDDLE ; +/* FALLTHROUGH */ +case IB_OPCODE_UC_SEND_MIDDLE : + len = qp->s_len; +if (len > pmtu) { + len = pmtu; +break ; + } +if (wqe->wr.opcode ==IB_WR_SEND ) + qp->s_state =IB_OPCODE_UC_SEND_LAST ; +else { + qp->s_state =IB_OPCODE_UC_SEND_LAST_WITH_IMMEDIATE ; +/* Immediate data comes after the BTH */ + ohdr->u.imm_data = wqe->wr.imm_data; + hwords +=1 ; + } +if (wqe->wr.send_flags &IB_SEND_SOLICITED ) + bth0 |=1 <<23 ; +break ; + +case IB_OPCODE_UC_RDMA_WRITE_FIRST : + qp->s_state =IB_OPCODE_UC_RDMA_WRITE_MIDDLE ; +/* FALLTHROUGH */ +case IB_OPCODE_UC_RDMA_WRITE_MIDDLE : + len = qp->s_len; +if (len > pmtu) { + len = pmtu; +break ; + } +if (wqe->wr.opcode ==IB_WR_RDMA_WRITE ) + qp->s_state =IB_OPCODE_UC_RDMA_WRITE_LAST ; +else { + qp->s_state = +IB_OPCODE_UC_RDMA_WRITE_LAST_WITH_IMMEDIATE ; +/* Immediate data comes after the BTH */ + ohdr->u.imm_data = wqe->wr.imm_data; + hwords +=1 ; +if (wqe->wr.send_flags &IB_SEND_SOLICITED ) + bth0 |=1 <<23 ; + } +break ; + } + bth2 = qp->s_next_psn++ &0xFFFFFF ; + qp->s_len -= len; + bth0 |= qp->s_state <<24 ; + +spin_unlock_irqrestore (&qp->s_lock, flags); + +/* Construct the header. */ + extra_bytes = (4 - len) &3 ; + nwords = (len + extra_bytes) >>2 ; +if (unlikely (qp->remote_ah_attr.ah_flags &IB_AH_GRH )) { +/* Header size in 32-bit words. */ + hwords +=10 ; + lrh0 =IPS_LRH_GRH ; + qp->s_hdr.u.l.grh.version_tclass_flow = +cpu_to_be32 ((6 <<28 ) | + (qp->remote_ah_attr.grh.traffic_class <<20 ) | + qp->remote_ah_attr.grh.flow_label); + qp->s_hdr.u.l.grh.paylen = +cpu_to_be16 (((hwords -12 ) + nwords +SIZE_OF_CRC ) <<2 ); + qp->s_hdr.u.l.grh.next_hdr =0x1B ; + qp->s_hdr.u.l.grh.hop_limit = qp->remote_ah_attr.grh.hop_limit; +/* The SGID is 32-bit aligned. */ + qp->s_hdr.u.l.grh.sgid.global.subnet_prefix = dev->gid_prefix; + qp->s_hdr.u.l.grh.sgid.global.interface_id = +ipath_layer_get_guid (dev->ib_unit); + qp->s_hdr.u.l.grh.dgid = qp->remote_ah_attr.grh.dgid; + } + qp->s_hdrwords = hwords; + qp->s_cur_sge = &qp->s_sge; + qp->s_cur_size = len; + lrh0 |= qp->remote_ah_attr.sl <<4 ; + qp->s_hdr.lrh[0 ] =cpu_to_be16 (lrh0); +/* DEST LID */ + qp->s_hdr.lrh[1 ] =cpu_to_be16 (qp->remote_ah_attr.dlid); + qp->s_hdr.lrh[2 ] =cpu_to_be16 (hwords + nwords +SIZE_OF_CRC ); + qp->s_hdr.lrh[3 ] =cpu_to_be16 (ipath_layer_get_lid (dev->ib_unit)); + bth0 |= extra_bytes <<20 ; + ohdr->bth[0 ] =cpu_to_be32 (bth0); + ohdr->bth[1 ] =cpu_to_be32 (qp->remote_qpn); + ohdr->bth[2 ] =cpu_to_be32 (bth2); + +/* Check for more work to do. */ + goto again; + +done: +spin_unlock_irqrestore (&qp->s_lock, flags); +clear_bit (IPATH_S_BUSY , &qp->s_flags); +} + +/* +* Process entries in the send work queue until credit or queue is exhausted. +* Only allow one CPU to send a packet per QP (tasklet). +* Otherwise, after we drop the QP s_lock, two threads could send +* packets out of order. +*/ +static void do_rc_send (unsigned long data) +{ +struct ipath_qp *qp = (struct ipath_qp *)data; +struct ipath_ibdev *dev =to_idev (qp->ibqp.device); +struct ipath_swqe *wqe; +struct ipath_sge_state *ss; +unsigned long flags; +u16 lrh0; +u32 hwords; +u32 nwords; +u32 extra_bytes; +u32 bth0; +u32 bth2; +u32 pmtu =ib_mtu_enum_to_int (qp->path_mtu); +u32 len; +struct ipath_other_headers *ohdr; +char newreq; + +if (test_and_set_bit (IPATH_S_BUSY , &qp->s_flags)) +return ; + +if (unlikely (qp->remote_ah_attr.dlid == +ipath_layer_get_lid (dev->ib_unit))) { +struct ib_wc wc; + +/* +* Pass in an uninitialized ib_wc to be consistent with +* other places where ipath_ruc_loopback() is called. +*/ +ipath_ruc_loopback (qp, &wc); +clear_bit (IPATH_S_BUSY , &qp->s_flags); +return ; + } + + ohdr = &qp->s_hdr.u.oth; +if (qp->remote_ah_attr.ah_flags &IB_AH_GRH ) + ohdr = &qp->s_hdr.u.l.oth; + +again: +/* Check for a constructed packet to be sent. */ +if (qp->s_hdrwords !=0 ) { +/* +* If no PIO bufs are available, return. +* An interrupt will call ipath_ib_piobufavail() +* when one is available. +*/ +if (ipath_verbs_send (dev->ib_unit, qp->s_hdrwords, + (uint32_t *) &qp->s_hdr, + qp->s_cur_size, qp->s_cur_sge)) { +no_bufs_available (qp, dev); +return ; + } + dev->n_unicast_xmit++; +/* Record that we sent the packet and s_hdr is empty. */ + qp->s_hdrwords =0 ; + } + + lrh0 =IPS_LRH_BTH ; +/* header size in 32-bit words LRH+BTH = (8+12)/4. */ + hwords =5 ; + +/* +* The lock is needed to synchronize between +* setting qp->s_ack_state, resend timer, and post_send(). +*/ +spin_lock_irqsave (&qp->s_lock, flags); + + bth0 =ipath_layer_get_pkey (dev->ib_unit, qp->s_pkey_index); + +/* Sending responses has higher priority over sending requests. */ +if (qp->s_ack_state !=IB_OPCODE_RC_ACKNOWLEDGE ) { +/* +* Send a response. +* Note that we are in the responder's side of the QP context. +*/ +switch (qp->s_ack_state) { +case IB_OPCODE_RC_RDMA_READ_REQUEST : + ss = &qp->s_rdma_sge; + len = qp->s_rdma_len; +if (len > pmtu) { + len = pmtu; + qp->s_ack_state = +IB_OPCODE_RC_RDMA_READ_RESPONSE_FIRST ; + }else { + qp->s_ack_state = +IB_OPCODE_RC_RDMA_READ_RESPONSE_ONLY ; + } + qp->s_rdma_len -= len; + bth0 |= qp->s_ack_state <<24 ; + ohdr->u.aeth =ipath_compute_aeth (qp); + hwords++; +break ; + +case IB_OPCODE_RC_RDMA_READ_RESPONSE_FIRST : + qp->s_ack_state = +IB_OPCODE_RC_RDMA_READ_RESPONSE_MIDDLE ; +/* FALLTHROUGH */ +case IB_OPCODE_RC_RDMA_READ_RESPONSE_MIDDLE : + ss = &qp->s_rdma_sge; + len = qp->s_rdma_len; +if (len > pmtu) { + len = pmtu; + }else { + ohdr->u.aeth =ipath_compute_aeth (qp); + hwords++; + qp->s_ack_state = +IB_OPCODE_RC_RDMA_READ_RESPONSE_LAST ; + } + qp->s_rdma_len -= len; + bth0 |= qp->s_ack_state <<24 ; +break ; + +case IB_OPCODE_RC_RDMA_READ_RESPONSE_LAST : +case IB_OPCODE_RC_RDMA_READ_RESPONSE_ONLY : +/* +* We have to prevent new requests from changing +* the r_sge state while a ipath_verbs_send() +* is in progress. +* Changing r_state allows the receiver +* to continue processing new packets. +* We do it here now instead of above so +* that we are sure the packet was sent before +* changing the state. +*/ + qp->r_state =IB_OPCODE_RC_RDMA_READ_RESPONSE_LAST ; + qp->s_ack_state =IB_OPCODE_RC_ACKNOWLEDGE ; + goto send_req; + +case IB_OPCODE_RC_COMPARE_SWAP : +case IB_OPCODE_RC_FETCH_ADD : + ss =NULL ; + len =0 ; + qp->r_state =IB_OPCODE_RC_SEND_LAST ; + qp->s_ack_state =IB_OPCODE_RC_ACKNOWLEDGE ; + bth0 |=IB_OPCODE_ATOMIC_ACKNOWLEDGE <<24 ; + ohdr->u.at.aeth =ipath_compute_aeth (qp); + ohdr->u.at.atomic_ack_eth = +cpu_to_be64 (qp->s_ack_atomic); + hwords +=sizeof (ohdr->u.at) /4 ; +break ; + +default : +/* Send a regular ACK. */ + ss =NULL ; + len =0 ; + qp->s_ack_state =IB_OPCODE_RC_ACKNOWLEDGE ; + bth0 |= qp->s_ack_state <<24 ; + ohdr->u.aeth =ipath_compute_aeth (qp); + hwords++; + } + bth2 = qp->s_ack_psn++ &0xFFFFFF ; + }else { + send_req: +if (!(state_ops[qp->state] &IPATH_PROCESS_SEND_OK ) || + qp->s_rnr_timeout) + goto done; + +/* Send a request. */ + wqe =get_swqe_ptr (qp, qp->s_cur); +switch (qp->s_state) { +default : +/* +* Resend an old request or start a new one. +* +* We keep track of the current SWQE so that +* we don't reset the "furthest progress" state +* if we need to back up. +*/ + newreq =0 ; +if (qp->s_cur == qp->s_tail) { +/* Check if send work queue is empty. */ +if (qp->s_tail == qp->s_head) + goto done; + qp->s_psn = wqe->psn = qp->s_next_psn; + newreq =1 ; + } +/* +* Note that we have to be careful not to modify the +* original work request since we may need to resend +* it. +*/ + qp->s_sge.sge = wqe->sg_list[0 ]; + qp->s_sge.sg_list = wqe->sg_list +1 ; + qp->s_sge.num_sge = wqe->wr.num_sge; + qp->s_len = len = wqe->length; + ss = &qp->s_sge; + bth2 =0 ; +switch (wqe->wr.opcode) { +case IB_WR_SEND : +case IB_WR_SEND_WITH_IMM : +/* If no credit, return. */ +if (qp->s_lsn != (u32) -1 && +cmp24 (wqe->ssn, qp->s_lsn +1 ) >0 ) { + goto done; + } + wqe->lpsn = wqe->psn; +if (len > pmtu) { + wqe->lpsn += (len -1 ) / pmtu; + qp->s_state =IB_OPCODE_RC_SEND_FIRST ; + len = pmtu; +break ; + } +if (wqe->wr.opcode ==IB_WR_SEND ) { + qp->s_state =IB_OPCODE_RC_SEND_ONLY ; + }else { + qp->s_state = +IB_OPCODE_RC_SEND_ONLY_WITH_IMMEDIATE ; +/* Immediate data comes after the BTH */ + ohdr->u.imm_data = wqe->wr.imm_data; + hwords +=1 ; + } +if (wqe->wr.send_flags &IB_SEND_SOLICITED ) + bth0 |=1 <<23 ; + bth2 =1 <<31 ;/* Request ACK. */ +if (++qp->s_cur == qp->s_size) + qp->s_cur =0 ; +break ; + +case IB_WR_RDMA_WRITE : +if (newreq) + qp->s_lsn++; +/* FALLTHROUGH */ +case IB_WR_RDMA_WRITE_WITH_IMM : +/* If no credit, return. */ +if (qp->s_lsn != (u32) -1 && +cmp24 (wqe->ssn, qp->s_lsn +1 ) >0 ) { + goto done; + } + ohdr->u.rc.reth.vaddr = +cpu_to_be64 (wqe->wr.wr.rdma.remote_addr); + ohdr->u.rc.reth.rkey = +cpu_to_be32 (wqe->wr.wr.rdma.rkey); + ohdr->u.rc.reth.length =cpu_to_be32 (len); + hwords +=sizeof (struct ib_reth ) /4 ; + wqe->lpsn = wqe->psn; +if (len > pmtu) { + wqe->lpsn += (len -1 ) / pmtu; + qp->s_state = +IB_OPCODE_RC_RDMA_WRITE_FIRST ; + len = pmtu; +break ; + } +if (wqe->wr.opcode ==IB_WR_RDMA_WRITE ) { + qp->s_state = +IB_OPCODE_RC_RDMA_WRITE_ONLY ; + }else { + qp->s_state = +IB_OPCODE_RC_RDMA_WRITE_ONLY_WITH_IMMEDIATE ; +/* Immediate data comes after RETH */ + ohdr->u.rc.imm_data = wqe->wr.imm_data; + hwords +=1 ; +if (wqe->wr. + send_flags &IB_SEND_SOLICITED ) + bth0 |=1 <<23 ; + } + bth2 =1 <<31 ;/* Request ACK. */ +if (++qp->s_cur == qp->s_size) + qp->s_cur =0 ; +break ; + +case IB_WR_RDMA_READ : + ohdr->u.rc.reth.vaddr = +cpu_to_be64 (wqe->wr.wr.rdma.remote_addr); + ohdr->u.rc.reth.rkey = +cpu_to_be32 (wqe->wr.wr.rdma.rkey); + ohdr->u.rc.reth.length =cpu_to_be32 (len); + qp->s_state =IB_OPCODE_RC_RDMA_READ_REQUEST ; + hwords +=sizeof (ohdr->u.rc.reth) /4 ; +if (newreq) { + qp->s_lsn++; +/* +* Adjust s_next_psn to count the +* expected number of responses. +*/ +if (len > pmtu) + qp->s_next_psn += + (len -1 ) / pmtu; + wqe->lpsn = qp->s_next_psn++; + } + ss =NULL ; + len =0 ; +if (++qp->s_cur == qp->s_size) + qp->s_cur =0 ; +break ; + +case IB_WR_ATOMIC_CMP_AND_SWP : +case IB_WR_ATOMIC_FETCH_AND_ADD : + qp->s_state = + wqe->wr.opcode ==IB_WR_ATOMIC_CMP_AND_SWP ? +IB_OPCODE_RC_COMPARE_SWAP : +IB_OPCODE_RC_FETCH_ADD ; + ohdr->u.atomic_eth.vaddr = +cpu_to_be64 (wqe->wr.wr.atomic.remote_addr); + ohdr->u.atomic_eth.rkey = +cpu_to_be32 (wqe->wr.wr.atomic.rkey); + ohdr->u.atomic_eth.swap_data = +cpu_to_be64 (wqe->wr.wr.atomic.swap); + ohdr->u.atomic_eth.compare_data = +cpu_to_be64 (wqe->wr.wr.atomic.compare_add); + hwords +=sizeof (struct ib_atomic_eth ) /4 ; +if (newreq) { + qp->s_lsn++; + wqe->lpsn = wqe->psn; + } +if (++qp->s_cur == qp->s_size) + qp->s_cur =0 ; + ss =NULL ; + len =0 ; +break ; + +default : + goto done; + } +if (newreq) { +if (++qp->s_tail >= qp->s_size) + qp->s_tail =0 ; + } + bth2 |= qp->s_psn++ &0xFFFFFF ; +if ((int )(qp->s_psn - qp->s_next_psn) >0 ) + qp->s_next_psn = qp->s_psn; +spin_lock (&dev->pending_lock); +if (qp->timerwait.next ==LIST_POISON1 ) { +list_add_tail (&qp->timerwait, + &dev->pending[dev-> + pending_index]); + } +spin_unlock (&dev->pending_lock); +break ; + +case IB_OPCODE_RC_RDMA_READ_RESPONSE_FIRST : +/* +* This case can only happen if a send is +* restarted. See ipath_restart_rc(). +*/ +ipath_init_restart (qp, wqe); +/* FALLTHROUGH */ +case IB_OPCODE_RC_SEND_FIRST : + qp->s_state =IB_OPCODE_RC_SEND_MIDDLE ; +/* FALLTHROUGH */ +case IB_OPCODE_RC_SEND_MIDDLE : + bth2 = qp->s_psn++ &0xFFFFFF ; +if ((int )(qp->s_psn - qp->s_next_psn) >0 ) + qp->s_next_psn = qp->s_psn; + ss = &qp->s_sge; + len = qp->s_len; +if (len > pmtu) { +/* +* Request an ACK every 1/2 MB to avoid +* retransmit timeouts. +*/ +if (((wqe->length - len) % (512 *1024 )) ==0 ) + bth2 |=1 <<31 ; + len = pmtu; +break ; + } +if (wqe->wr.opcode ==IB_WR_SEND ) + qp->s_state =IB_OPCODE_RC_SEND_LAST ; +else { + qp->s_state = +IB_OPCODE_RC_SEND_LAST_WITH_IMMEDIATE ; +/* Immediate data comes after the BTH */ + ohdr->u.imm_data = wqe->wr.imm_data; + hwords +=1 ; + } +if (wqe->wr.send_flags &IB_SEND_SOLICITED ) + bth0 |=1 <<23 ; + bth2 |=1 <<31 ;/* Request ACK. */ +if (++qp->s_cur >= qp->s_size) + qp->s_cur =0 ; +break ; + +case IB_OPCODE_RC_RDMA_READ_RESPONSE_LAST : +/* +* This case can only happen if a RDMA write is +* restarted. See ipath_restart_rc(). +*/ +ipath_init_restart (qp, wqe); +/* FALLTHROUGH */ +case IB_OPCODE_RC_RDMA_WRITE_FIRST : + qp->s_state =IB_OPCODE_RC_RDMA_WRITE_MIDDLE ; +/* FALLTHROUGH */ +case IB_OPCODE_RC_RDMA_WRITE_MIDDLE : + bth2 = qp->s_psn++ &0xFFFFFF ; +if ((int )(qp->s_psn - qp->s_next_psn) >0 ) + qp->s_next_psn = qp->s_psn; + ss = &qp->s_sge; + len = qp->s_len; +if (len > pmtu) { +/* +* Request an ACK every 1/2 MB to avoid +* retransmit timeouts. +*/ +if (((wqe->length - len) % (512 *1024 )) ==0 ) + bth2 |=1 <<31 ; + len = pmtu; +break ; + } +if (wqe->wr.opcode ==IB_WR_RDMA_WRITE ) + qp->s_state =IB_OPCODE_RC_RDMA_WRITE_LAST ; +else { + qp->s_state = +IB_OPCODE_RC_RDMA_WRITE_LAST_WITH_IMMEDIATE ; +/* Immediate data comes after the BTH */ + ohdr->u.imm_data = wqe->wr.imm_data; + hwords +=1 ; +if (wqe->wr.send_flags &IB_SEND_SOLICITED ) + bth0 |=1 <<23 ; + } + bth2 |=1 <<31 ;/* Request ACK. */ +if (++qp->s_cur >= qp->s_size) + qp->s_cur =0 ; +break ; + +case IB_OPCODE_RC_RDMA_READ_RESPONSE_MIDDLE : +/* +* This case can only happen if a RDMA read is +* restarted. See ipath_restart_rc(). +*/ +ipath_init_restart (qp, wqe); + len = ((qp->s_psn - wqe->psn) &0xFFFFFF ) * pmtu; + ohdr->u.rc.reth.vaddr = +cpu_to_be64 (wqe->wr.wr.rdma.remote_addr + len); + ohdr->u.rc.reth.rkey = +cpu_to_be32 (wqe->wr.wr.rdma.rkey); + ohdr->u.rc.reth.length =cpu_to_be32 (qp->s_len); + qp->s_state =IB_OPCODE_RC_RDMA_READ_REQUEST ; + hwords +=sizeof (ohdr->u.rc.reth) /4 ; + bth2 = qp->s_psn++ &0xFFFFFF ; +if ((int )(qp->s_psn - qp->s_next_psn) >0 ) + qp->s_next_psn = qp->s_psn; + ss =NULL ; + len =0 ; +if (++qp->s_cur == qp->s_size) + qp->s_cur =0 ; +break ; + +case IB_OPCODE_RC_RDMA_READ_REQUEST : +case IB_OPCODE_RC_COMPARE_SWAP : +case IB_OPCODE_RC_FETCH_ADD : +/* +* We shouldn't start anything new until this request +* is finished. The ACK will handle rescheduling us. +* XXX The number of outstanding ones is negotiated +* at connection setup time (see pg. 258,289)? +* XXX Also, if we support multiple outstanding +* requests, we need to check the WQE IB_SEND_FENCE +* flag and not send a new request if a RDMA read or +* atomic is pending. +*/ + goto done; + } + qp->s_len -= len; + bth0 |= qp->s_state <<24 ; +/* XXX queue resend timeout. */ + } +/* Make sure it is non-zero before dropping the lock. */ + qp->s_hdrwords = hwords; +spin_unlock_irqrestore (&qp->s_lock, flags); + +/* Construct the header. */ + extra_bytes = (4 - len) &3 ; + nwords = (len + extra_bytes) >>2 ; +if (unlikely (qp->remote_ah_attr.ah_flags &IB_AH_GRH )) { +/* Header size in 32-bit words. */ + hwords +=10 ; + lrh0 =IPS_LRH_GRH ; + qp->s_hdr.u.l.grh.version_tclass_flow = +cpu_to_be32 ((6 <<28 ) | + (qp->remote_ah_attr.grh.traffic_class <<20 ) | + qp->remote_ah_attr.grh.flow_label); + qp->s_hdr.u.l.grh.paylen = +cpu_to_be16 (((hwords -12 ) + nwords +SIZE_OF_CRC ) <<2 ); + qp->s_hdr.u.l.grh.next_hdr =0x1B ; + qp->s_hdr.u.l.grh.hop_limit = qp->remote_ah_attr.grh.hop_limit; +/* The SGID is 32-bit aligned. */ + qp->s_hdr.u.l.grh.sgid.global.subnet_prefix = dev->gid_prefix; + qp->s_hdr.u.l.grh.sgid.global.interface_id = +ipath_layer_get_guid (dev->ib_unit); + qp->s_hdr.u.l.grh.dgid = qp->remote_ah_attr.grh.dgid; + qp->s_hdrwords = hwords; + } + qp->s_cur_sge = ss; + qp->s_cur_size = len; + lrh0 |= qp->remote_ah_attr.sl <<4 ; + qp->s_hdr.lrh[0 ] =cpu_to_be16 (lrh0); +/* DEST LID */ + qp->s_hdr.lrh[1 ] =cpu_to_be16 (qp->remote_ah_attr.dlid); + qp->s_hdr.lrh[2 ] =cpu_to_be16 (hwords + nwords +SIZE_OF_CRC ); + qp->s_hdr.lrh[3 ] =cpu_to_be16 (ipath_layer_get_lid (dev->ib_unit)); + bth0 |= extra_bytes <<20 ; + ohdr->bth[0 ] =cpu_to_be32 (bth0); + ohdr->bth[1 ] =cpu_to_be32 (qp->remote_qpn); + ohdr->bth[2 ] =cpu_to_be32 (bth2); + +/* Check for more work to do. */ + goto again; + +done: +spin_unlock_irqrestore (&qp->s_lock, flags); +clear_bit (IPATH_S_BUSY , &qp->s_flags); +}
PATCH 1/1 Add memcpy_toio32 to each arch
In reply to Bryan O'SullivanMost arches use the generic __memcpy_toio32 routine, while x86_64 uses memcpy32, which is substantially faster.
include/asm-alpha/io.h +2 -0
@@ -504,6 +504,8 @@extern void memcpy_toio (volatile void __iomem *,const void *,long );extern void _memset_c_io (volatile void __iomem *,unsigned long ,long ); +#define memcpy_toio32 __memcpy_toio32 +static inline void memset_io (volatile void __iomem *addr,u8 c,long len) {_memset_c_io (addr,0x0101010101010101UL * c, len);
include/asm-arm/io.h +2 -0
@@ -184,6 +184,8 @@#define memset_io (c,v,l) _memset_io(__mem_pci(c),(v),(l))#define memcpy_fromio (a,c,l) _memcpy_fromio((a),__mem_pci(c),(l))#define memcpy_toio (c,a,l) _memcpy_toio(__mem_pci(c),(a),(l)) + +#define memcpy_toio32 __memcpy_toio32#define eth_io_copy_and_sum (s,c,l,b) \ eth_copy_and_sum((s),__mem_pci(c),(l),(b))
include/asm-cris/io.h +2 -0
@@ -121,6 +121,8 @@#define memcpy_fromio (a,b,c) memcpy((a),(void *)(b),(c))#define memcpy_toio (a,b,c) memcpy((void *)(a),(b),(c)) +#define memcpy_toio32 __memcpy_toio32 +/* * Again, CRIS does not require mem IO specific function. */
include/asm-frv/io.h +2 -0
@@ -124,6 +124,8 @@memcpy ((void __force *) dst, src, count); } +#define memcpy_toio32 __memcpy_toio32 +static inline uint8_t inb (unsigned long addr) {return __builtin_read8 ((void *)addr);
include/asm-h8300/io.h +2 -0
@@ -209,6 +209,8 @@#define memcpy_fromio (a,b,c) memcpy((a),(void *)(b),(c))#define memcpy_toio (a,b,c) memcpy((void *)(a),(b),(c)) +#define memcpy_toio32 __memcpy_toio32 +#define mmiowb ()#define inb (addr) ((h8300_buswidth(addr))?readw((addr) & ~1) & 0xff:readb(addr))
include/asm-i386/io.h +3 -0
@@ -203,6 +203,9 @@ {__memcpy ((void __force *) dst, src, count); } + +#define memcpy_toio32 __memcpy_toio32 +/* * ISA space is 'always mapped' on a typical x86 system, no need to
include/asm-ia64/io.h +2 -0
@@ -443,6 +443,8 @@extern void memcpy_toio (volatile void __iomem *dst,const void *src,long n);extern void memset_io (volatile void __iomem *s,int c,long n); +#define memcpy_toio32 __memcpy_toio32 +#define dma_cache_inv (_start,_size) do { } while (0)#define dma_cache_wback (_start,_size) do { } while (0)#define dma_cache_wback_inv (_start,_size) do { } while (0)
include/asm-m32r/io.h +2 -0
@@ -216,6 +216,8 @@memcpy ((void __force *) dst, src, count); } +#define memcpy_toio32 __memcpy_toio32 +/* * Convert a physical pointer to a virtual kernel pointer for /dev/mem * access
include/asm-m68knommu/io.h +2 -0
@@ -113,6 +113,8 @@#define memcpy_fromio (a,b,c) memcpy((a),(void *)(b),(c))#define memcpy_toio (a,b,c) memcpy((void *)(a),(b),(c)) +#define memcpy_toio32 __memcpy_toio32 +#define inb (addr) readb(addr)#define inw (addr) readw(addr)#define inl (addr) readl(addr)
include/asm-mips/io.h +2 -0
@@ -534,6 +534,8 @@memcpy ((void __force *) dst, src, count); } +#define memcpy_toio32 __memcpy_toio32 +/* * Memory Mapped I/O */
include/asm-parisc/io.h +2 -0
@@ -294,6 +294,8 @@void memcpy_fromio (void *dst,const volatile void __iomem *src,int count);void memcpy_toio (volatile void __iomem *dst,const void *src,int count); +#define memcpy_toio32 __memcpy_toio32 +/* Support old drivers which don't ioremap. * NB this interface is scheduled to disappear in 2.5 */
include/asm-powerpc/io.h +2 -0
@@ -63,6 +63,8 @@#define memcpy_fromio (a,b,c) iSeries_memcpy_fromio((a), (b), (c))#define memcpy_toio (a,b,c) iSeries_memcpy_toio((a), (b), (c)) +#define memcpy_toio32 __memcpy_toio32 +#define inb (addr) readb(((void __iomem *)(long)(addr)))#define inw (addr) readw(((void __iomem *)(long)(addr)))#define inl (addr) readl(((void __iomem *)(long)(addr)))
include/asm-ppc/io.h +2 -0
@@ -367,6 +367,8 @@ }#endif +#define memcpy_toio32 __memcpy_toio32 +#define eth_io_copy_and_sum (a,b,c,d) eth_copy_and_sum((a),(void __force *)(void __iomem *)(b),(c),(d))/*
include/asm-s390/io.h +2 -0
@@ -99,6 +99,8 @@#define memcpy_fromio (a,b,c) memcpy((a),__io_virt(b),(c))#define memcpy_toio (a,b,c) memcpy(__io_virt(a),(b),(c)) +#define memcpy_toio32 __memcpy_toio32 +#define inb_p (addr) readb(addr)#define inb (addr) readb(addr)
include/asm-sh/io.h +2 -0
@@ -177,6 +177,8 @@extern void memcpy_toio (unsigned long ,const void *,unsigned long );extern void memset_io (unsigned long ,int ,unsigned long ); +#define memcpy_toio32 __memcpy_toio32 +/* SuperH on-chip I/O functions */ static __inline__unsigned char ctrl_inb (unsigned long addr) {
include/asm-sh64/io.h +2 -0
@@ -125,6 +125,8 @@void memcpy_toio (void __iomem *to,const void *from,long count);void memcpy_fromio (void *to,void __iomem *from,long count); + +#define memcpy_toio32 __memcpy_toio32#define mmiowb ()
include/asm-sparc/io.h +2 -0
@@ -239,6 +239,8 @@#define memcpy_toio (d,s,sz) _memcpy_toio(d,s,sz) +#define memcpy_toio32 __memcpy_toio32 +#ifdef __KERNEL__/*
include/asm-sparc64/io.h +2 -0
@@ -440,6 +440,8 @@#define memcpy_toio (d,s,sz) _memcpy_toio(d,s,sz) +#define memcpy_toio32 __memcpy_toio32 +static inline int check_signature (void __iomem *io_addr,const unsigned char *signature,int length)
include/asm-v850/io.h +2 -0
@@ -130,6 +130,8 @@#define memcpy_fromio (dst, src, len) memcpy (dst, (void *)src, len)#define memcpy_toio (dst, src, len) memcpy ((void *)dst, src, len) +#define memcpy_toio32 __memcpy_toio32 +/* * Convert a physical pointer to a virtual kernel pointer for /dev/mem * access
include/asm-x86_64/io.h +7 -0
@@ -252,6 +252,13 @@__memcpy_toio ((unsigned long )to,from,len); } +#include <asm/string.h> + +static inline void memcpy_toio32 (void __iomem *dst,const void *src,size_t count) +{ +memcpy32 ((void __force *) dst, src, count); +} +void memset_io (volatile void __iomem *a,int b,size_t c);/*
include/asm-xtensa/io.h +2 -0
@@ -159,6 +159,8 @@#define memcpy_fromio (a,b,c) memcpy((a),(void *)(b),(c))#define memcpy_toio (a,b,c) memcpy((void *)(a),(b),(c)) +#define memcpy_toio32 __memcpy_toio32 +/* At this point the Xtensa doesn't provide byte swap instructions */ #ifdef __XTENSA_EB__
PATCH 1/1 ipath - driver core header files
In reply to Bryan O'Sullivandrivers/infiniband/hw/ipath/ipath_common.h new file +704 -0
@@ -0,0 +1,704 @@ +/* +* Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved. +* +* This software is available to you under a choice of one of two +* licenses. You may choose to be licensed under the terms of the GNU +* General Public License (GPL) Version 2, available from the file +* COPYING in the main directory of this source tree, or the +* OpenIB.org BSD license below: +* +* 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. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +* +* Patent licenses, if any, provided herein do not apply to +* combinations of this program with other software, or any other +* product whatsoever. +*/ + +#ifndef _IPATH_COMMON_H +#define _IPATH_COMMON_H + +/* +* This file contains defines, structures, etc. that are used +* to communicate between kernel and user code. +*/ + +#define IPATH_IDSTR "PathScale 1.1\n" + +typedef uint8_t ipath_type ; + +/* This is the IEEE-assigned OUI for PathScale, Inc. */ +#define IPATH_SRC_OUI_1 0x00 +#define IPATH_SRC_OUI_2 0x11 +#define IPATH_SRC_OUI_3 0x75 + +/* version of protocol header (known to chip also). In the long run, +* we should be able to generate and accept a range of version numbers; +* for now we only accept one, and it's compiled in. +*/ +#define IPS_PROTO_VERSION 2 + +/* +* These are compile time constants that you may want to enable or disable +* if you are trying to debug problems with code or performance. +* IPATH_VERBOSE_TRACING define as 1 if you want additional tracing in +* fastpath code +* IPATH_TRACE_REGWRITES define as 1 if you want register writes to be +* traced in faspath code +* _IPATH_TRACING define as 0 if you want to remove all tracing in a +* compilation unit +* _IPATH_DEBUGGING define as 0 if you want to remove debug prints +*/ + + +/* +* These tell the driver which ioctl's belong to the diags interface. +* As above, don't use them elsewhere. +*/ +#define _IPATH_DIAG_IOCTL_LOW 100 +#define _IPATH_DIAG_IOCTL_HIGH 109 + +struct ipath_eeprom_req { +long long addr; +uint16_t len; +uint16_t offset; +}; + +/* +* NOTE: We use compatible ioctls, the same ioctl code for both 32 and 64 +* bit user mode. For that reason, all structures, etc. used in these +* ioctls must have the same size and offsets, in both 32 and 64 bit mode. +* We normally use uint64_t to hold pointers for this reason, doing appropriate +* casts on both sides before using the data value. +*/ + +/* init; user params to kernel */ +#define IPATH_USERINIT _IOW('s', 16, struct ipath_user_info) +/* init; kernel/chip params to user */ +#define IPATH_BASEINFO _IOR('s', 17, struct ipath_base_info) +/* send a packet */ +#define IPATH_SENDPKT _IOW('s', 18, struct ipath_sendpkt) +/* +* if arg is 0, disable port, used when flushing after a hdrq overflow. +* If arg ia 1, re-enable, and return new value of head register +*/ +#define IPATH_RCVCTRL _IOR('s', 19, uint32_t) +#define IPATH_READ_EEPROM _IOWR('s', 20, struct ipath_eeprom_req) +/* set an accepted partition key; up to 4 pkeys can be active at once */ +#define IPATH_SET_PKEY _IOW('s', 21, uint16_t) +#define IPATH_WRITE_EEPROM _IOWR('s', 22, struct ipath_eeprom_req) +/* set LID for interface (SMA) */ +#define IPATH_SET_LID _IOW('s', 23, uint32_t) +/* set IB MTU for interface (SMA) */ +#define IPATH_SET_MTU _IOW('s', 24, uint32_t) +/* set IB link state for interface (SMA) */ +#define IPATH_SET_LINKSTATE _IOW('s', 25, uint32_t) +/* send an SMA packet, sps_flags contains "normal" SMA unit and minor number. */ +#define IPATH_SEND_SMA_PKT _IOW('s', 26, struct ipath_sendpkt) +/* receive an SMA packet */ +#define IPATH_RCV_SMA_PKT _IOW('s', 27, struct ipath_sendpkt) +/* get the portinfo data (SMA) +* takes array of 13, returns port info fields. Data is in host order, +* not network order; SMA-only fields are not filled in +*/ +#define IPATH_GET_PORTINFO _IOWR('s', 28, uint32_t *) +/* +* get the nodeinfo data (SMA) +* takes an array of 10, returns nodeinfo fields in host order +*/ +#define IPATH_GET_NODEINFO _IOWR('s', 29, uint32_t *) +/* set GUID on interface (SMA; GUID given in network order) */ +#define IPATH_SET_GUID _IOW('s', 30, struct ipath_setguid) +/* set MLID for interface (SMA) */ +#define IPATH_SET_MLID _IOW('s', 31, uint32_t) +#define IPATH_GET_MLID _IOWR('s', 32, uint32_t *)/* get the MLID (SMA) */ +/* update expected TID entries */ +#define IPATH_UPDM_TID _IOWR('s', 33, struct _tidupd) +/* free expected TID entries */ +#define IPATH_FREE_TID _IOW('s', 34, struct _tidupd) +/* return assigned unit:port */ +#define IPATH_GETPORT _IOR('s', 35, uint32_t) +/* wait for rcv pkt or pioavail */ +#define IPATH_WAIT _IOW('s', 36, uint32_t) +/* return LID for passed in unit */ +#define IPATH_GETLID _IOR('s', 37, uint16_t) +/* return # of units supported by driver */ +#define IPATH_GETUNITS _IO('s', 38) +/* get the device status */ +#define IPATH_GET_DEVSTATUS _IOWR('s', 39, uint64_t *) + +/* available for reuse ('s', 48) */ + +/* diagnostic read */ +#define IPATH_DIAGREAD _IOR('s', 100, struct ipath_diag_info) +/* diagnostic write */ +#define IPATH_DIAGWRITE _IOW('s', 101, struct ipath_diag_info) +/* HT Config read */ +#define IPATH_DIAG_HTREAD _IOR('s', 102, struct ipath_diag_info) +/* HT config write */ +#define IPATH_DIAG_HTWRITE _IOW('s', 103, struct ipath_diag_info) +#define IPATH_DIAGENTER _IO('s', 104)/* Enter diagnostic mode */ +#define IPATH_DIAGLEAVE _IO('s', 105)/* Leave diagnostic mode */ +/* send a packet, sps_flags contains unit and minor number. */ +#define IPATH_SEND_DIAG_PKT _IOW('s', 106, struct ipath_sendpkt) +/* +* read I2C FLASH +* NOTE: To read the I2C device, the _uaddress field should contain +* a pointer to struct ipath_eeprom_req, and _unit must be valid +*/ +#define IPATH_DIAG_RD_I2C _IOW('s', 107, struct ipath_diag_info) + +/* +* Monitoring ioctls. All of these work with the main device +* (/dev/ipath), if you don't mind using a port (e.g. you already have +* the device open.) IPATH_GETSTATS and IPATH_GETUNITCOUNTERS also +* work with the control device (/dev/ipath_ctrl), if you don't want to +* use a port. +*/ + +/* return chip counters for current unit. */ +#define IPATH_GETCOUNTERS _IOR('s', 40, struct infinipath_counters) +/* return chip stats */ +#define IPATH_GETSTATS _IOR('s', 41, struct infinipath_stats) +/* return chip counters for a particular unit. */ +#define IPATH_GETUNITCOUNTERS _IOR('s', 43, struct infinipath_getunitcounters) + +/* +* unit is incoming unit number. +* data is a pointer to the infinipath_counters structure. +*/ +struct infinipath_getunitcounters { +uint16_t unit; +uint16_t fill[3 ];/* required for same size struct 32/64 bit */ +uint64_t data; +}; + +/* +* The value in the BTH QP field that InfiniPath uses to differentiate +* an infinipath protocol IB packet vs standard IB transport +*/ +#define IPATH_KD_QP 0x656b79 + +/* +* valid states passed to ipath_set_linkstate() user call +* (IPATH_SET_LINKSTATE ioctl) +*/ +#define IPATH_IB_LINKDOWN 0 +#define IPATH_IB_LINKARM 1 +#define IPATH_IB_LINKACTIVE 2 +#define IPATH_IB_LINKINIT 3 +#define IPATH_IB_LINKDOWN_POLL 4 +#define IPATH_IB_LINKDOWN_DISABLE 5 + +/* +* stats maintained by the driver. For now, at least, this is global +* to all minor devices. +*/ +struct infinipath_stats { +uint64_t sps_ints;/* number of interrupts taken */ +uint64_t sps_errints;/* number of interrupts for errors */ +/* number of errors from chip (not including packet errors or CRC) */ +uint64_t sps_errs; +/* number of packet errors from chip other than CRC */ +uint64_t sps_pkterrs; +/* number of packets with CRC errors (ICRC and VCRC) */ +uint64_t sps_crcerrs; +/* number of hardware errors reported (parity, etc.) */ +uint64_t sps_hwerrs; +/* number of times IB link changed state unexpectedly */ +uint64_t sps_iblink; +uint64_t sps_unused3;/* no longer used; left for compatibility */ +uint64_t sps_port0pkts;/* number of kernel (port0) packets received */ +/* number of "ethernet" packets sent by driver */ +uint64_t sps_ether_spkts; +/* number of "ethernet" packets received by driver */ +uint64_t sps_ether_rpkts; +uint64_t sps_sma_spkts;/* number of SMA packets sent by driver */ +uint64_t sps_sma_rpkts;/* number of SMA packets received by driver */ +/* number of times all ports rcvhdrq was full and packet dropped */ +uint64_t sps_hdrqfull; +/* number of times all ports egrtid was full and packet dropped */ +uint64_t sps_etidfull; +/* +* number of times we tried to send from driver, but no pio +* buffers avail +*/ +uint64_t sps_nopiobufs; +uint64_t sps_ports;/* number of ports currently open */ +/* list of pkeys (other than default) accepted (0 means not set) */ +uint16_t sps_pkeys[4 ]; +/* lids for up to 4 infinipaths, indexed by infinipath # */ +uint16_t sps_lid[4 ]; +/* number of user ports per chip (not IB ports) */ +uint32_t sps_nports; +uint32_t sps_nullintr;/* not our interrupt, or already handled */ +uint32_t sps_maxpkts_call;/* max number of packets handled per receive call */ +uint32_t sps_avgpkts_call;/* avg number of packets handled per receive call */ +uint64_t sps_pagelocks;/* total number of pages locked */ +uint64_t sps_pageunlocks;/* total number of pages unlocked */ +/* +* Number of packets dropped in kernel other than errors +* (ether packets if ipath not configured, sma/mad, etc.) +*/ +uint64_t sps_krdrops; +/* mlids for up to 4 infinipaths, indexed by infinipath # */ +uint16_t sps_mlid[4 ]; +uint64_t __sps_pad[45 ];/* pad for future growth */ +}; + +/* +* These are the status bits returned (in ascii form, 64bit value) +* by the IPATH_GETSTATS ioctl. +*/ +#define IPATH_STATUS_INITTED 0x1/* basic driver initialization done */ +#define IPATH_STATUS_DISABLED 0x2/* hardware disabled */ +#define IPATH_STATUS_UNUSED 0x4/* available */ +#define IPATH_STATUS_OIB_SMA 0x8/* ipath_mad kernel SMA running */ +#define IPATH_STATUS_SMA 0x10/* user SMA running */ +/* Chip has been found and initted */ +#define IPATH_STATUS_CHIP_PRESENT 0x20 +#define IPATH_STATUS_IB_READY 0x40/* IB link is at ACTIVE, has LID, +* usable for all VL's */ +/* after link up, LID,MTU,etc. has been configured */ +#define IPATH_STATUS_IB_CONF 0x80 +/* no link established, probably no cable */ +#define IPATH_STATUS_IB_NOCABLE 0x100 +/* A Fatal hardware error has occurred. */ +#define IPATH_STATUS_HWERROR 0x200 + +/* The list of usermode accessible registers. Also see Reg_* later in file */ +typedef enum _ipath_ureg { + ur_rcvhdrtail =0 ,/* (RO) DMA RcvHdr to be used next. */ +/* (RW) RcvHdr entry to be processed next by host. */ + ur_rcvhdrhead =1 , + ur_rcvegrindextail =2 ,/* (RO) Index of next Eager index to use. */ + ur_rcvegrindexhead =3 ,/* (RW) Eager TID to be processed next */ +/* For internal use only; max register number. */ + _IPATH_UregMax +}ipath_ureg ; + +/* SMA minor# no portinfo, one for all instances */ +#define IPATH_SMA 128 + +/* Control minor# no portinfo, one for all instances */ +#define IPATH_CTRL 130 + +/* +* This structure is returned by ipath_userinit() immediately after open +* to get implementation-specific info, and info specific to this +* instance. +*/ +struct ipath_base_info { +/* version of hardware, for feature checking. */ +uint32_t spi_hw_version; +/* version of software, for feature checking. */ +uint32_t spi_sw_version; +/* InfiniPath port assigned, goes into sent packets */ +uint32_t spi_port; +/* +* IB MTU, packets IB data must be less than this. +* The MTU is in bytes, and will be a multiple of 4 bytes. +*/ +uint32_t spi_mtu; +/* +* size of a PIO buffer. Any given packet's total +* size must be less than this (in words). Included is the +* starting control word, so if 513 is returned, then total +* pkt size is 512 words or less. +*/ +uint32_t spi_piosize; +/* size of the TID cache in infinipath, in entries */ +uint32_t spi_tidcnt; +/* size of the TID Eager list in infinipath, in entries */ +uint32_t spi_tidegrcnt; +/* size of a single receive header queue entry. */ +uint32_t spi_rcvhdrent_size; +/* Count of receive header queue entries allocated. +* This may be less than the spu_rcvhdrcnt passed in!. +*/ +uint32_t spi_rcvhdr_cnt; + +uint32_t __32_bit_compatibility_pad;/* DO NOT MOVE OR REMOVE */ + +/* address where receive buffer queue is mapped into */ +uint64_t spi_rcvhdr_base; + +/* user program. */ + +/* base address of eager TID receive buffers. */ +uint64_t spi_rcv_egrbufs; + +/* Allocated by initialization code, not by protocol. */ + +/* size of each TID buffer in host memory, +* starting at spi_rcv_egrbufs. It includes spu_egrskip, and is +* at least spi_mtu bytes, and the buffers are virtually contiguous +*/ +uint32_t spi_rcv_egrbufsize; +/* +* The special QP (queue pair) value that identifies an infinipath +* protocol packet from standard IB packets. More, probably much +* more, to be added. +*/ +uint32_t spi_qpair; + +/* +* user register base for init code, not to be used directly by +* protocol or applications +*/ +uint64_t __spi_uregbase; +/* +* maximum buffer size in bytes that can be used in a +* single TID entry (assuming the buffer is aligned to this boundary). +* This is the minimum of what the hardware and software support +* Guaranteed to be a power of 2. +*/ +uint32_t spi_tid_maxsize; +/* +* alignment of each pio send buffer (byte count +* to add to spi_piobufbase to get to second buffer) +*/ +uint32_t spi_pioalign; +/* +* the index of the first pio buffer available +* to this process; needed to do lookup in spi_pioavailaddr; not added +* to spi_piobufbase +*/ +uint32_t spi_pioindex; +uint32_t spi_piocnt;/* number of buffers mapped for this process */ + +/* +* base address of writeonly pio buffers for this process. +* Each buffer has spi_piosize words, and is aligned on spi_pioalign +* boundaries. spi_piocnt buffers are mapped from this address +*/ +uint64_t spi_piobufbase; + +/* +* base address of readonly memory copy of the pioavail registers. +* There are 2 bits for each buffer. +*/ +uint64_t spi_pioavailaddr; + +/* +* Address where driver updates a copy +* of the interface and driver status (IPATH_STATUS_*) as a 64 bit value +* It's followed by a string indicating hardware error, if there was one +*/ +uint64_t spi_status; + +/* number of chip ports available to user processes */ +uint32_t spi_nports; +uint32_t spi_unit;/* unit number of chip we are using */ +uint32_t spi_rcv_egrperchunk;/* num bufs in each contiguous set */ +/* size in bytes of each contiguous set */ +uint32_t spi_rcv_egrchunksize; +/* total size of mmap to cover full rcvegrbuffers */ +uint32_t spi_rcv_egrbuftotlen; +/* +* ioctl cmd includes struct size, so pad out, and adjust down as +* new fields are added to keep size constant +*/ +uint32_t __spi_pad[19 ]; +} __attribute__ ((aligned (8 ))); + +#define IPATH_WAIT_RCV 0x1/* IPATH_WAIT, receive */ +#define IPATH_WAIT_PIO 0x2/* IPATH_WAIT, PIO */ + +/* +* This version number is given to the driver by the user code during +* initialization in the spu_userversion field of ipath_user_info, so +* the driver can check for compatibility with user code. +* +* The major version changes when data structures +* change in an incompatible way. The driver must be the same or higher +* for initialization to succeed. In some cases, a higher version +* driver will not interoperate with older software, and initialization +* will return an error. +*/ +#define IPATH_USER_SWMAJOR 1 + +/* +* Minor version differences are always compatible +* a within a major version, however if if user software is larger +* than driver software, some new features and/or structure fields +* may not be implemented; the user code must deal with this if it +* cares, or it must abort after initialization reports the difference +*/ +#define IPATH_USER_SWMINOR 2 + +#define IPATH_USER_SWVERSION ((IPATH_USER_SWMAJOR<<16) | IPATH_USER_SWMINOR) + +#define IPATH_KERN_TYPE 0 + +/* Similarly, this is the kernel version going back to the user. It's slightly +* different, in that we want to tell if the driver was built as part of a +* PathScale release, or from the driver from OpenIB, kernel.org, or a +* standard distribution, for support reasons. The high bit is 0 for +* non-PathScale, and 1 for PathScale-built/supplied. +* +* It's returned by the driver to the user code during initialization +* in the spi_sw_version field of ipath_base_info, so the user code can +* in turn check for compatibility with the kernel. +*/ +#define IPATH_KERN_SWVERSION ((IPATH_KERN_TYPE<<31) | IPATH_USER_SWVERSION) + +/* +* This structure is passed to ipath_userinit() to tell the driver where +* user code buffers are, sizes, etc. +*/ +struct ipath_user_info { +/* +* version of user software, to detect compatibility issues. +* Should be set to IPATH_USER_SWVERSION. +*/ +uint32_t spu_userversion; + +/* desired number of receive header queue entries */ +uint32_t spu_rcvhdrcnt; + +/* +* Leave this much unused space at the start of +* each eager buffer for software use. Similar in effect to +* setting K_Offset to this value. needs to be 'small', on the +* order of one or two cachelines +*/ +uint32_t spu_egrskip; + +/* +* number of words in KD protocol header +* This tells InfiniPath how many words to copy to rcvhdrq. If 0, +* kernel uses a default. Once set, attempts to set any other value +* are an error (EAGAIN) until driver is reloaded. +*/ +uint32_t spu_rcvhdrsize; + +/* +* cache line aligned (64 byte) user address to +* which the rcvhdrtail register will be written by infinipath +* whenever it changes, so that no chip registers are read in +* the performance path. +*/ +uint64_t spu_rcvhdraddr; + +/* +* ioctl cmd includes struct size, so pad out, +* and adjust down as new fields are added to keep size constant +*/ +uint32_t __spu_pad[6 ]; +} __attribute__ ((aligned (8 ))); + +struct ipath_iovec { +/* Pointer to data, but same size 32 and 64 bit */ +uint64_t iov_base; + +/* +* Length of data; don't need 64 bits, but want +* ipath_sendpkt to remain same size as before 32 bit changes, so... +*/ +uint64_t iov_len; +}; + +/* +* Describes a single packet for send. Each packet can have one or more +* buffers, but the total length (exclusive of IB headers) must be less +* than the MTU, and if using the PIO method, entire packet length, +* including IB headers, must be less than the ipath_piosize value (words). +* Use of this necessitates including sys/uio.h +*/ +struct ipath_sendpkt { +uint32_t sps_flags;/* flags for packet (TBD) */ +uint32_t sps_cnt;/* number of entries to use in sps_iov */ +/* array of iov's describing packet. TEMPORARY */ +struct ipath_iovec sps_iov[4 ]; +}; + +struct _tidupd {/* used only in inlined function for ioctl. */ +uint32_t tidcnt; +uint32_t tid__unused;/* make structure same size in 32 and 64 bit */ +uint64_t tidvaddr;/* virtual address of first page in transfer */ +/* pointer (same size 32/64 bit) to uint16_t tid array */ +uint64_t tidlist; + +/* +* pointer (same size 32/64 bit) to bitmap of TIDs used +* for this call; checked for being large enough at open +*/ +uint64_t tidmap; +}; + +struct ipath_setguid {/* set GUID for interface */ +uint64_t sguid;/* in network order */ +uint64_t sunit;/* unit number of interface */ +}; + +/* +* Structure used to send data to and receive data from a diags ioctl. +* +* NOTE: For HT reads and writes, we only support byte, word (16bits) and +* dword (32bits). All other sizes for HT are invalid. +*/ +struct ipath_diag_info { +uint64_t _base_offset;/* register to start reading from */ +uint64_t _num_bytes;/* number of bytes to read or write */ +/* +* address in user space. +* for reads, this is the address to store the read result(s). +* for writes, it the address to get the write data from. +* This memory better be valid in user space! +*/ +uint64_t _uaddress; +uint64_t _unit;/* Unit ID of chip we are accessing. */ +uint64_t _pad[15 ]; +}; + +/* +* Data layout in I2C flash (for GUID, etc.) +* All fields are little-endian binary unless otherwise stated +*/ +#define IPATH_FLASH_VERSION 1 +struct ipath_flash { +uint8_t if_fversion;/* flash layout version (IPATH_FLASH_VERSION) */ +uint8_t if_csum;/* checksum protecting if_length bytes */ +/* +* valid length (in use, protected by if_csum), including if_fversion +* and if_sum themselves) +*/ +uint8_t if_length; +uint8_t if_guid[8 ];/* the GUID, in network order */ +/* number of GUIDs to use, starting from if_guid */ +uint8_t if_numguid; +char if_serial[12 ];/* the board serial number, in ASCII */ +char if_mfgdate[8 ];/* board mfg date (YYYYMMDD ASCII) */ +/* last board rework/test date (YYYYMMDD ASCII) */ +char if_testdate[8 ]; +uint8_t if_errcntp[4 ];/* logging of error counts, TBD */ +/* powered on hours, updated at driver unload */ +uint8_t if_powerhour[2 ]; +char if_comment[32 ];/* ASCII free-form comment field */ +uint8_t if_future[50 ];/* 78 bytes used, min flash size is 128 bytes */ +}; + +uint8_t ipath_flash_csum (struct ipath_flash *,int ); + +/* +* These are the counters implemented in the chip, and are listed in order. +* They are returned in this order by the IPATH_GETCOUNTERS ioctl +*/ +struct infinipath_counters { +unsigned long long LBIntCnt; +unsigned long long LBFlowStallCnt; +unsigned long long Reserved1; +unsigned long long TxUnsupVLErrCnt; +unsigned long long TxDataPktCnt; +unsigned long long TxFlowPktCnt; +unsigned long long TxDwordCnt; +unsigned long long TxLenErrCnt; +unsigned long long TxMaxMinLenErrCnt; +unsigned long long TxUnderrunCnt; +unsigned long long TxFlowStallCnt; +unsigned long long TxDroppedPktCnt; +unsigned long long RxDroppedPktCnt; +unsigned long long RxDataPktCnt; +unsigned long long RxFlowPktCnt; +unsigned long long RxDwordCnt; +unsigned long long RxLenErrCnt; +unsigned long long RxMaxMinLenErrCnt; +unsigned long long RxICRCErrCnt; +unsigned long long RxVCRCErrCnt; +unsigned long long RxFlowCtrlErrCnt; +unsigned long long RxBadFormatCnt; +unsigned long long RxLinkProblemCnt; +unsigned long long RxEBPCnt; +unsigned long long RxLPCRCErrCnt; +unsigned long long RxBufOvflCnt; +unsigned long long RxTIDFullErrCnt; +unsigned long long RxTIDValidErrCnt; +unsigned long long RxPKeyMismatchCnt; +unsigned long long RxP0HdrEgrOvflCnt; +unsigned long long RxP1HdrEgrOvflCnt; +unsigned long long RxP2HdrEgrOvflCnt; +unsigned long long RxP3HdrEgrOvflCnt; +unsigned long long RxP4HdrEgrOvflCnt; +unsigned long long RxP5HdrEgrOvflCnt; +unsigned long long RxP6HdrEgrOvflCnt; +unsigned long long RxP7HdrEgrOvflCnt; +unsigned long long RxP8HdrEgrOvflCnt; +unsigned long long Reserved6; +unsigned long long Reserved7; +unsigned long long IBStatusChangeCnt; +unsigned long long IBLinkErrRecoveryCnt; +unsigned long long IBLinkDownedCnt; +unsigned long long IBSymbolErrCnt; +}; + +/* +* The next set of defines are for packet headers, and chip register +* and memory bits that are visible to and/or used by user-mode software +* The other bits that are used only by the driver or diags are in +* ipath_registers.h +*/ + +/* RcvHdrFlags bits */ +#define INFINIPATH_RHF_LENGTH_MASK 0x7FF +#define INFINIPATH_RHF_LENGTH_SHIFT 0 +#define INFINIPATH_RHF_RCVTYPE_MASK 0x7 +#define INFINIPATH_RHF_RCVTYPE_SHIFT 11 +#define INFINIPATH_RHF_EGRINDEX_MASK 0x7FF +#define INFINIPATH_RHF_EGRINDEX_SHIFT 16 +#define INFINIPATH_RHF_H_ICRCERR 0x80000000 +#define INFINIPATH_RHF_H_VCRCERR 0x40000000 +#define INFINIPATH_RHF_H_PARITYERR 0x20000000 +#define INFINIPATH_RHF_H_LENERR 0x10000000 +#define INFINIPATH_RHF_H_MTUERR 0x08000000 +#define INFINIPATH_RHF_H_IHDRERR 0x04000000 +#define INFINIPATH_RHF_H_TIDERR 0x02000000 +#define INFINIPATH_RHF_H_MKERR 0x01000000 +#define INFINIPATH_RHF_H_IBERR 0x00800000 +#define INFINIPATH_RHF_L_SWA 0x00008000 +#define INFINIPATH_RHF_L_SWB 0x00004000 + +/* infinipath header fields */ +#define INFINIPATH_I_VERS_MASK 0xF +#define INFINIPATH_I_VERS_SHIFT 28 +#define INFINIPATH_I_PORT_MASK 0xF +#define INFINIPATH_I_PORT_SHIFT 24 +#define INFINIPATH_I_TID_MASK 0x7FF +#define INFINIPATH_I_TID_SHIFT 13 +#define INFINIPATH_I_OFFSET_MASK 0x1FFF +#define INFINIPATH_I_OFFSET_SHIFT 0 + +/* K_PktFlags bits */ +#define INFINIPATH_KPF_INTR 0x1 + +/* SendPIO per-buffer control */ +#define INFINIPATH_SP_LENGTHP1_MASK 0x3FF +#define INFINIPATH_SP_LENGTHP1_SHIFT 0 +#define INFINIPATH_SP_INTR 0x80000000 +#define INFINIPATH_SP_TEST 0x40000000 +#define INFINIPATH_SP_TESTEBP 0x20000000 + +/* SendPIOAvail bits */ +#define INFINIPATH_SENDPIOAVAIL_BUSY_SHIFT 1 +#define INFINIPATH_SENDPIOAVAIL_CHECK_SHIFT 0 + +#endif /* _IPATH_COMMON_H */
drivers/infiniband/hw/ipath/ipath_kernel.h new file +697 -0
697 lines not on this page · raw message
drivers/infiniband/hw/ipath/ipath_layer.h new file +134 -0
134 lines not on this page · raw message
drivers/infiniband/hw/ipath/ipath_registers.h new file +355 -0
355 lines not on this page · raw message
drivers/infiniband/hw/ipath/ips_common.h new file +249 -0
249 lines not on this page · raw message
PATCH 1/1 ipath - core driver, part 2 of 4
In reply to Bryan O'Sullivandrivers/infiniband/hw/ipath/ipath_driver.c +2001 -0
@@ -1877,3 +1877,2004 @@return ret; } + +/* +* cancel a range of PIO buffers, used when they might be armed, but +* not triggered. Used at init to ensure buffer state, and also user +* process close, in case it died while writing to a PIO buffer +*/ + +static void ipath_disarm_piobufs (const ipath_type t,unsigned first, +unsigned cnt) +{ +unsigned i, last = first + cnt; +uint64_t sendctrl; +for (i = first; i < last; i++) { + sendctrl = devdata[t].ipath_sendctrl |INFINIPATH_S_DISARM | + (i <<INFINIPATH_S_DISARMPIOBUF_SHIFT ); +ipath_kput_kreg (t, kr_sendctrl, sendctrl); + } +} + +static void ipath_clean_partkey (struct ipath_portdata * pd, +struct ipath_devdata * dd) +{ +int i, j, pchanged =0 ; +uint64_t oldpkey; + +/* for debugging only */ + oldpkey = + (uint64_t ) dd->ipath_pkeys[0 ] | ((uint64_t ) dd-> + ipath_pkeys[1 ] <<16 ) + | ((uint64_t ) dd->ipath_pkeys[2 ] <<32 ) + | ((uint64_t ) dd->ipath_pkeys[3 ] <<48 ); + +for (i =0 ; i < (sizeof (pd->port_pkeys) /sizeof (pd->port_pkeys[0 ])); + i++) { +if (!pd->port_pkeys[i]) +continue ; +_IPATH_VDBG ("look for key[%d] %hx in pkeys\n" , i, + pd->port_pkeys[i]); +for (j =0 ; + j < (sizeof (dd->ipath_pkeys) /sizeof (dd->ipath_pkeys[0 ])); + j++) { +/* check for match independent of the global bit */ +if ((dd->ipath_pkeys[j] &0x7fff ) == + (pd->port_pkeys[i] &0x7fff )) { +if (atomic_dec_and_test (&dd->ipath_pkeyrefs[j])) { +_IPATH_VDBG + ("p%u clear key %x matches #%d\n" , + pd->port_port, pd->port_pkeys[i], + j); + ipath_stats.sps_pkeys[j] = + dd->ipath_pkeys[j] =0 ; + pchanged++; + }else +_IPATH_VDBG + ("p%u key %x matches #%d, but ref still %d\n" , + pd->port_port, pd->port_pkeys[i], + j, +atomic_read (&dd-> + ipath_pkeyrefs[j])); +break ; + } + } + pd->port_pkeys[i] =0 ; + } +if (pchanged) { +uint64_t pkey; + pkey = + (uint64_t ) dd->ipath_pkeys[0 ] | ((uint64_t ) dd-> + ipath_pkeys[1 ] <<16 ) + | ((uint64_t ) dd->ipath_pkeys[2 ] <<32 ) + | ((uint64_t ) dd->ipath_pkeys[3 ] <<48 ); +_IPATH_VDBG ("p%u old pkey reg %llx, new pkey reg %llx\n" , + pd->port_port, oldpkey, pkey); +ipath_kput_kreg (pd->port_unit, kr_partitionkey, pkey); + } +} + +static unsigned int ipath_poll (struct file *fp,struct poll_table_struct *pt) +{ +int ret; +struct ipath_portdata *pd; + + pd =port_fp (fp); +/* nothing for select/poll in this driver, at least for now */ + ret =0 ; + +return ret; +} + +/* +* wait up to msecs milliseconds for IB link state change to occur +* for now, take the easy polling route. Currently used only by +* the SMA ioctls. Returns 0 if state reached, otherwise -ETIMEDOUT +* state can have multiple states set, for any of several transitions. +*/ + +int ipath_wait_linkstate (const ipath_type t,uint32_t state,int msecs) +{ + devdata[t].ipath_sma_state_wanted = state; +wait_event_interruptible_timeout (ipath_sma_state_wait, + (devdata[t].ipath_flags & state), +msecs_to_jiffies (msecs)); + devdata[t].ipath_sma_state_wanted =0 ; + +if (!(devdata[t].ipath_flags & state)) +_IPATH_DBG + ("Didn't reach linkstate %s within %u ms (ibcc %llx %s)\n" , +/* test INIT ahead of DOWN, both can be set */ + (state &IPATH_LINKINIT ) ?"INIT" : + ((state &IPATH_LINKDOWN ) ?"DOWN" : + ((state &IPATH_LINKARMED ) ?"ARM" :"ACTIVE" )), + msecs,ipath_kget_kreg64 (t, kr_ibcctrl), + ipath_ibcstatus_str[ipath_kget_kreg64 (t, kr_ibcstatus) & +0xf ]); +return (devdata[t].ipath_flags & state) ?0 : -ETIMEDOUT ; +} + +/* unit number is already validated in ipath_ioctl() */ +static int ipath_kset_lid (uint32_t arg) +{ +unsigned unit = (arg >>16 ) &0xffff ; + +if (unit >= infinipath_max + || !(devdata[unit].ipath_flags &IPATH_INITTED )) { +_IPATH_DBG ("Invalid unit %u\n" , unit); +return -ENODEV ; + } + + arg &=0xffff ; +_IPATH_SMADBG ("Unit %u setting lid to 0x%x, was 0x%x\n" , unit, arg, + devdata[unit].ipath_lid); +ipath_set_sps_lid (unit, arg); +return 0 ; +} + +static int ipath_kset_mlid (uint32_t arg) +{ +unsigned unit = (arg >>16 ) &0xffff ; + +if (unit >= infinipath_max + || !(devdata[unit].ipath_flags &IPATH_INITTED )) { +_IPATH_DBG ("Invalid unit %u\n" , unit); +return -ENODEV ; + } + + arg &=0xffff ; +_IPATH_SMADBG ("Unit %u setting mlid to 0x%x, was 0x%x\n" , unit, arg, + devdata[unit].ipath_mlid); + ipath_stats.sps_mlid[unit] = devdata[unit].ipath_mlid = arg; +if (devdata[unit].ipath_layer.l_intr) + devdata[unit].ipath_layer.l_intr (unit,IPATH_LAYER_INT_BCAST ); +return 0 ; +} + +/* unit number is in incoming, overwritten on return with data */ + +static int ipath_get_devstatus (uint64_t __user *a) +{ +int ret; +uint64_t unit64; +uint32_t unit; +uint64_t devstatus; + +if ((ret =copy_from_user (&unit64, a,sizeof unit64))) { +_IPATH_DBG ("Failed to copy in unit: %d\n" , ret); +return -EFAULT ; + } + unit = unit64; +if (unit >= infinipath_max + || !(devdata[unit].ipath_flags &IPATH_INITTED )) { +_IPATH_DBG ("Invalid unit %u\n" , unit); +return -ENODEV ; + } + + devstatus = *devdata[unit].ipath_statusp; + +if ((ret =copy_to_user (a, &devstatus,sizeof devstatus))) { +_IPATH_DBG ("Failed to copy out device status: %d\n" , ret); + ret = -EFAULT ; + } +return ret; +} + +/* unit number is in incoming, overwritten on return with data */ + +static int ipath_get_mlid (uint32_t __user *a) +{ +int ret; +uint32_t unit; +uint32_t mlid; + +if ((ret =copy_from_user (&unit, a,sizeof unit))) { +_IPATH_DBG ("Failed to copy in mlid: %d\n" , ret); +return -EFAULT ; + } +if (unit >= infinipath_max + || !(devdata[unit].ipath_flags &IPATH_INITTED )) { +_IPATH_DBG ("Invalid unit %u\n" , unit); +return -ENODEV ; + } + + mlid = devdata[unit].ipath_mlid; + +if ((ret =copy_to_user (a, &mlid,sizeof mlid))) { +_IPATH_DBG ("Failed to copy out MLID: %d\n" , ret); + ret = -EFAULT ; + } +return ret; +} + +static int ipath_kset_guid (struct ipath_setguid __user *a) +{ +struct ipath_setguid setguid; +int ret; + +if ((ret =copy_from_user (&setguid, a,sizeof setguid))) { +_IPATH_DBG ("Failed to copy in guid info: %d\n" , ret); +return -EFAULT ; + } +if (setguid.sunit >= infinipath_max || + !(devdata[setguid.sunit].ipath_flags &IPATH_INITTED )) { +_IPATH_DBG ("Invalid unit %llu\n" , setguid.sunit); +return -ENODEV ; + } +if (setguid.sguid ==0ULL || setguid.sguid ==-1LL ) { +/* +* use INFO, not DBG, because ipath_mux doesn't yet +* complain about errors on this +*/ + +_IPATH_INFO ("Ignoring attempt to set invalid GUID %llx\n" , + setguid.sguid); +return -EINVAL ; + } + devdata[setguid.sunit].ipath_guid = setguid.sguid; + devdata[setguid.sunit].ipath_nguid =1 ; +_IPATH_DBG ("SMA set hardware GUID unit %llu to %llx (network order)\n" , + setguid.sunit, devdata[setguid.sunit].ipath_guid); +return 0 ; +} + +/* +* receive an IB packet with QP 0 or 1. For now, we have no timeout implemented +* We put the actual received count into the iov on return, and the unit we +* received from goes into the lower 16 bits of sps_flags. +* This receives from all/any of the active chips, and we currently do not +* allow specifying just one (we could, by filling in unit in the library +* before the syscall, and checking here). +*/ + +static int ipath_rcvsma_pkt (struct ipath_sendpkt __user *p) +{ +struct ipath_sendpkt rpkt; +int i, any, ret; +unsigned long flags; + +if ((ret =copy_from_user (&rpkt, p,sizeof rpkt))) { +_IPATH_DBG ("Failed to copy in pkt struct (%d)\n" , ret); +return -EFAULT ; + } +if (!ipath_sma_data_spare) { +_IPATH_DBG ("can't do receive, sma not initialized\n" ); +return -ENETDOWN ; + } + +for (any = i =0 ; i < infinipath_max; i++) +if (devdata[i].ipath_flags &IPATH_INITTED ) + any++; +if (!any) {/* no hardware, freeze, etc. */ +_IPATH_SMADBG ("Didn't find any initialized and usable chips\n" ); +return -ENODEV ; + } + +wait_event_interruptible (ipath_sma_wait, + ipath_sma_data[ipath_sma_first].len); + +spin_lock_irqsave (&ipath_sma_lock, flags); +if (ipath_sma_data[ipath_sma_first].len) { +int len; +uint32_t slen; +uint8_t *sdata; +struct _ipath_sma_rpkt *smpkt = + &ipath_sma_data[ipath_sma_first]; + +/* +* we swap out the buffer we are going to use with the +* spare buffer and set spare to that buffer. This code +* is the only code that ever manipulates spare, other +* than the initialization code. This code should never +* be entered by more than one process at a time, and +* if it is, the user code doing so deserves what it gets; +* it won't break anything in the driver by doing so. +* We do it this way to avoid holding a lock across the +* copy_to_user, which could fault, or delay a long time +* while paging occurs; ditto for printks +*/ + + slen = smpkt->len; + sdata = smpkt->buf; + rpkt.sps_flags = smpkt->unit; + smpkt->buf = ipath_sma_data_spare; + ipath_sma_data_spare = sdata; + smpkt->len =0 ;/* it's available again */ +if (++ipath_sma_first >=IPATH_NUM_SMAPKTS ) + ipath_sma_first =0 ; +spin_unlock_irqrestore (&ipath_sma_lock, flags); + + len =min ((uint32_t ) rpkt.sps_iov[0 ].iov_len, slen); + ret =copy_to_user ((void __user *) rpkt.sps_iov[0 ].iov_base, + sdata, len); +_IPATH_VDBG ("SMA packet (index=%d), len %d (actual %d) " +"buf %p, ubuf %llx\n" , ipath_sma_first, slen, + len, sdata, rpkt.sps_iov[0 ].iov_base); +if (!ret) { +/* actual length read. */ + rpkt.sps_iov[0 ].iov_len = len; + rpkt.sps_cnt =1 ;/* received one packet */ +if ((ret =copy_to_user (p, &rpkt,sizeof rpkt))) { +_IPATH_DBG ("Failed to copy out pkt struct " +"(%d)\n" , ret); + ret = -EFAULT ; + } + }else { +_IPATH_DBG ("copyout failed: %d\n" , ret); + ret = -EFAULT ; + } + }else { +/* usually means SMA process received a signal */ +spin_unlock_irqrestore (&ipath_sma_lock, flags); +return -EAGAIN ; + } + +return ret; +} + +/* unit number is in first word incoming, overwritten on return with data */ +static int ipath_get_portinfo (uint32_t __user *a) +{ +int ret; +uint32_t unit, tmp, tmp2; +struct ipath_devdata *dd; +uint32_t portinfo[13 ];/* just the data for Portinfo, in host horder */ + +if ((ret =copy_from_user (&unit, a,sizeof unit))) { +_IPATH_DBG ("Failed to copy in portinfo: %d\n" , ret); +return -EFAULT ; + } +if (unit >= infinipath_max + || !(devdata[unit].ipath_flags &IPATH_INITTED )) { +_IPATH_DBG ("Invalid unit %u\n" , unit); +return -ENODEV ; + } + dd = &devdata[unit]; +/* so we only initialize non-zero fields. */ +memset (portinfo,0 ,sizeof portinfo); + +/* +* Notimpl yet M_Key (64) +* Notimpl yet GID (64) +*/ + + portinfo[4 ] = (dd->ipath_lid <<16 ); + +/* +* Notimpl yet SMLID (should we store this in the driver, in +* case SMA dies?) +* CapabilityMask is 0, we don't support any of these +* DiagCode is 0; we don't store any diag info for now +* Notimpl yet M_KeyLeasePeriod (we don't support M_Key) +*/ + +/* LocalPortNum is whichever port number they ask for */ + portinfo[7 ] = (unit <<24 ) +/* LinkWidthEnabled */ + |(2 <<16 ) +/* LinkWidthSupported (really 2, but that's not IB valid...) */ + |(3 <<8 ) +/* LinkWidthActive */ + |(2 <<0 ); + tmp = dd->ipath_lastibcstat &0xff ; + tmp2 =5 ; +if (tmp ==0x11 ) + tmp =2 ; +else if (tmp ==0x21 ) + tmp =3 ; +else if (tmp ==0x31 ) + tmp =4 ; +else { + tmp =0 ;/* down */ + tmp2 = tmp &0xf ; + } + portinfo[8 ] = (1 <<28 )/* LinkSpeedSupported */ + |(tmp <<24 )/* PortState */ + |(tmp2 <<20 )/* PortPhysicalState */ + |(2 <<16 ) + +/* LinkDownDefaultState */ +/* M_KeyProtectBits == 0 */ +/* NotImpl yet LMC == 0 (we can support all values) */ + |(1 <<4 )/* LinkSpeedActive */ + |(1 <<0 );/* LinkSpeedEnabled */ +switch (dd->ipath_ibmtu) { +case 4096 : + tmp =5 ; +break ; +case 2048 : + tmp =4 ; +break ; +case 1024 : + tmp =3 ; +break ; +case 512 : + tmp =2 ; +break ; +case 256 : + tmp =1 ; +break ; +default :/* oops, something is wrong */ +_IPATH_DBG + ("Problem, ipath_ibmtu 0x%x not a valid IB MTU, treat as 2048\n" , + dd->ipath_ibmtu); + tmp =4 ; +break ; + } + portinfo[9 ] = (tmp <<28 ) +/* NeighborMTU */ +/* Notimpl MasterSMSL */ + |(1 <<20 ) + +/* VLCap */ +/* Notimpl InitType (actually, an SMA decision) */ +/* VLHighLimit is 0 (only one VL) */ + ;/* VLArbitrationHighCap is 0 (only one VL) */ + portinfo[10 ] =/* VLArbitrationLowCap is 0 (only one VL) */ +/* InitTypeReply is SMA decision */ + (5 <<16 )/* MTUCap 4096 */ + |(7 <<13 )/* VLStallCount */ + |(0x1f <<8 )/* HOQLife */ + |(1 <<4 )/* OperationalVLs 0 */ + +/* PartitionEnforcementInbound */ +/* PartitionEnforcementOutbound not enforced */ +/* FilterRawinbound not enforced */ + ;/* FilterRawOutbound not enforced */ +/* M_KeyViolations are not counted by hardware, SMA can count */ + tmp =ipath_kget_creg32 (unit, cr_errpkey); +/* P_KeyViolations are counted by hardware. */ + portinfo[11 ] = ((tmp &0xffff ) <<0 ); + portinfo[12 ] = +/* Q_KeyViolations are not counted by hardware */ + (1 <<8 ) + +/* GUIDCap */ +/* SubnetTimeOut handled by SMA */ +/* RespTimeValue handled by SMA */ + ; +/* LocalPhyErrors are programmed to max */ + portinfo[12 ] |= (0xf <<20 ) + |(0xf <<16 )/* OverRunErrors are programmed to max */ + ; + +if ((ret =copy_to_user (a, portinfo,sizeof portinfo))) { +_IPATH_DBG ("Failed to copy out portinfo: %d\n" , ret); + ret = -EFAULT ; + } +return ret; +} + +/* unit number is in first word incoming, overwritten on return with data */ +static int ipath_get_nodeinfo (uint32_t __user *a) +{ +int ret; +uint32_t unit;/*, tmp, tmp2; */ +struct ipath_devdata *dd; +uint32_t nodeinfo[10 ];/* just the data for Nodeinfo, in host horder */ + +if ((ret =copy_from_user (&unit, a,sizeof unit))) { +_IPATH_DBG ("Failed to copy in nodeinfo: %d\n" , ret); +return -EFAULT ; + } +if (unit >= infinipath_max + || !(devdata[unit].ipath_flags &IPATH_INITTED )) { +/* VDBG because sma normally probes for all possible units */ +_IPATH_VDBG ("Invalid unit %u\n" , unit); +return -ENODEV ; + } + dd = &devdata[unit]; + +/* so we only initialize non-zero fields. */ +memset (nodeinfo,0 ,sizeof nodeinfo); + + nodeinfo[0 ] =/* BaseVersion is SMA */ +/* ClassVersion is SMA */ + (1 <<8 )/* NodeType */ + |(1 <<0 );/* NumPorts */ + nodeinfo[1 ] = (uint32_t ) (dd->ipath_guid >>32 ); + nodeinfo[2 ] = (uint32_t ) (dd->ipath_guid &0xffffffff ); + nodeinfo[3 ] = nodeinfo[1 ];/* PortGUID == SystemImageGUID for us */ + nodeinfo[4 ] = nodeinfo[2 ];/* PortGUID == SystemImageGUID for us */ + nodeinfo[5 ] = nodeinfo[3 ];/* PortGUID == NodeGUID for us */ + nodeinfo[6 ] = nodeinfo[4 ];/* PortGUID == NodeGUID for us */ + nodeinfo[7 ] = (4 <<16 )/* we support 4 pkeys */ + |(dd->ipath_deviceid <<0 ); +/* our chip version as 16 bits major, 16 bits minor */ + nodeinfo[8 ] = dd->ipath_minrev | (dd->ipath_majrev <<16 ); + nodeinfo[9 ] = (unit <<24 ) | (dd->ipath_vendorid <<0 ); + +if ((ret =copy_to_user (a, nodeinfo,sizeof nodeinfo))) { +_IPATH_DBG ("Failed to copy out nodeinfo: %d\n" , ret); + ret = -EFAULT ; + } +return ret; +} + +static int ipath_sma_ioctl (struct file *fp,unsigned int cmd,unsigned long a) +{ +int ret =0 ; +switch (cmd) { +case IPATH_SEND_SMA_PKT :/* send SMA packet */ +if (!(ret =ipath_send_smapkt ((struct ipath_sendpkt __user *) a))) +/* another SMA packet sent */ + ipath_stats.sps_sma_spkts++; +break ; +case IPATH_RCV_SMA_PKT :/* recieve an SMA or MAD packet */ + ret =ipath_rcvsma_pkt ((struct ipath_sendpkt __user *) a); +break ; +case IPATH_SET_LID :/* set our lid, (SMA) */ + ret =ipath_kset_lid ((uint32_t ) a); +break ; +case IPATH_SET_MTU :/* set the IB mtu (not maxpktlen) (SMA) */ + ret =ipath_kset_mtu ((uint32_t ) a); +break ; +case IPATH_SET_LINKSTATE : +/* walk through the linkstate states (SMA) */ + ret =ipath_kset_linkstate ((uint32_t ) a); +break ; +case IPATH_GET_PORTINFO :/* get the SMA portinfo */ + ret =ipath_get_portinfo ((uint32_t __user *) a); +break ; +case IPATH_GET_NODEINFO :/* get the SMA nodeinfo */ + ret =ipath_get_nodeinfo ((uint32_t __user *) a); +break ; +case IPATH_SET_GUID : +/* +* set our guid, (SMA). This is not normally +* used, but provides a way to set the GUID when the i2c flash +* has a problem, or for special testing. +*/ + ret =ipath_kset_guid ((struct ipath_setguid __user *) a); +break ; +case IPATH_SET_MLID :/* set multicast LID for ipath broadcast */ + ret =ipath_kset_mlid ((uint32_t ) a); +break ; +case IPATH_GET_MLID :/* get multicast LID for ipath broadcast */ + ret =ipath_get_mlid ((uint32_t __user *) a); +break ; +case IPATH_GET_DEVSTATUS :/* get device status */ + ret =ipath_get_devstatus ((uint64_t __user *) a); +break ; +default : +_IPATH_DBG ("%x not a valid SMA ioctl for infinipath\n" , cmd); + ret = -EINVAL ; +break ; + } +return ret; +} + +static int ipath_get_unit_counters (struct infinipath_getunitcounters __user *a) +{ +struct infinipath_getunitcounters c; + +if (copy_from_user (&c, a,sizeof c)) +return -EFAULT ; + +if (c.unit >= infinipath_max || + !(devdata[c.unit].ipath_flags &IPATH_PRESENT )) +return -ENODEV ; + +return ipath_get_counters (c.unit, + (struct infinipath_counters __user *) c.data); +} + +/* +* ioctls for the control device, which is useful when you don't want +* to open the main device and use up a port. +*/ + +static int ipath_ctrl_ioctl (struct file *fp,unsigned int cmd,unsigned long a) +{ +int ret =0 ; + +switch (cmd) { +case IPATH_GETSTATS :/* return driver stats */ + ret =ipath_get_stats ((struct infinipath_stats __user *) a); +break ; +case IPATH_GETUNITCOUNTERS :/* return chip counters */ + ret =ipath_get_unit_counters ((struct infinipath_getunitcounters __user *) a); +break ; +default : +_IPATH_DBG ("%x not a valid CTRL ioctl for infinipath\n" , cmd); + ret = -EINVAL ; +break ; + } + +return ret; +} + +long ipath_ioctl (struct file *fp,unsigned int cmd,unsigned long a) +{ +int ret =0 ; +struct ipath_portdata *pd; +ipath_type unit; +uint32_t tmp, i, nactive =0 ; + +if (cmd ==IPATH_GETUNITS ) { +/* +* Return number of units supported. This is called +* here as this ioctl is needed via both the normal and +* diags interface, and it does not need the device to +* be opened. +*/ +return ipath_get_units (); + } + + pd =port_fp (fp); +if (!pd) { +if (IPATH_SMA == (unsigned long )fp->private_data) +/* sma separate; no pd */ +return (long )ipath_sma_ioctl (fp, cmd, a); +#ifdef IPATH_DIAG + else if (IPATH_DIAG == (unsigned long)fp->private_data) + /* diags separate; no pd */ + return (long)ipath_diags_ioctl(fp, cmd, a); +#endif + else if (IPATH_CTRL == (unsigned long)fp->private_data) +/* ctrl separate; no pd */ + return (long)ipath_ctrl_ioctl(fp, cmd, a); + else { +_IPATH_DBG ("NULL pd from fp (%p), cmd=%x\n" , fp, cmd); +return -ENODEV ;/* bad; shouldn't ever happen */ + } + } + + unit = pd->port_unit; + +if ((devdata[unit].ipath_flags &IPATH_PRESENT ) + && (cmd ==IPATH_GETCOUNTERS || cmd ==IPATH_GETSTATS + || cmd ==IPATH_READ_EEPROM || cmd ==IPATH_WRITE_EEPROM )) { +/* allowed to do these, as long as chip is accessible */ + }else if (!(devdata[unit].ipath_flags &IPATH_INITTED )) { +_IPATH_DBG + ("%s not initialized (flags=0x%x), failing ioctl #%u\n" , +ipath_get_unit_name (unit), devdata[unit].ipath_flags, +_IOC_NR (cmd)); + ret = -ENODEV ; + }else +if ((devdata[unit]. + ipath_flags & (IPATH_LINKDOWN |IPATH_LINKUNK ))) { +_IPATH_DBG ("%s link is down, failing ioctl #%u\n" , +ipath_get_unit_name (unit),_IOC_NR (cmd)); + ret = -ENETDOWN ; + } + +if (ret) +return ret; + +switch (cmd) { +case IPATH_USERINIT : +/* real application is starting on a port */ + ret =ipath_do_user_init (pd, (struct ipath_user_info __user *) a); +break ; +case IPATH_BASEINFO : +/* it's done the init, now return the info it needs */ + ret =ipath_get_baseinfo (pd, (struct ipath_base_info __user *) a); +break ; +case IPATH_GETPORT : +/* +* just return the unit:port that we were assigned, +* and the number of active chips. This is is used for +* doing sched_setaffinity() before initialization. +*/ +for (i =0 ; i < infinipath_max; i++) +if ((devdata[i].ipath_flags &IPATH_PRESENT ) + && devdata[i].ipath_kregbase + && devdata[i].ipath_lid + && !(devdata[i].ipath_flags & + (IPATH_LINKDOWN |IPATH_LINKUNK ))) + nactive++; + tmp = (nactive <<24 ) | (unit <<16 ) | pd->port_port; +if (copy_to_user ((void __user *) a, &tmp,sizeof (tmp))) + ret =EFAULT ; +break ; +case IPATH_GETLID : +/* get LID for given unit # */ + ret =ipath_layer_get_lid (a); +break ; +case IPATH_UPDM_TID :/* update expected TID entries */ + ret =ipath_tid_update (pd, (struct _tidupd __user *) a); +break ; +case IPATH_FREE_TID :/* free expected TID entries */ + ret =ipath_tid_free (pd, (struct _tidupd __user *) a); +break ; +case IPATH_GETCOUNTERS :/* return chip counters */ + ret =ipath_get_counters (unit, (struct infinipath_counters __user *) a); +break ; +case IPATH_GETSTATS :/* return driver stats */ + ret =ipath_get_stats ((struct infinipath_stats __user *) a); +break ; +case IPATH_GETUNITCOUNTERS :/* return chip counters */ + ret =ipath_get_unit_counters ( + (struct infinipath_getunitcounters __user *) a); +break ; +case IPATH_SET_PKEY :/* set a partition key */ + ret =ipath_set_partkey (pd, (uint16_t ) a); +break ; +case IPATH_RCVCTRL :/* error handling to manage the rcvq */ + ret =ipath_manage_rcvq (pd, (uint16_t ) a); +break ; +case IPATH_WRITE_EEPROM : +/* write the eeprom (for GUID) */ + ret =ipath_wr_eeprom (pd, + (struct ipath_eeprom_req __user *) a); +break ; +case IPATH_READ_EEPROM :/* read the eeprom (for GUID) */ + ret =ipath_rd_eeprom (pd->port_unit, + (struct ipath_eeprom_req __user *) a); +break ; +case IPATH_WAIT : +/* +* wait for a receive intr for this port, or PIO avail +*/ + ret =ipath_wait_intr (pd, (uint32_t ) a); +break ; + +default : +_IPATH_DBG ("cmd %x (%c,%u) not a valid ioctl\n" , cmd, +_IOC_TYPE (cmd),_IOC_NR (cmd)); + ret = -EINVAL ; +break ; + } + +return ret; +} + +static loff_t ipath_llseek (struct file *fp,loff_t off,int whence) +{ +loff_t ret; + +/* range checking is done where offset is used, not here. */ +down (&fp->f_dentry->d_inode->i_sem); +if (!whence) + ret = fp->f_pos = off; +else if (whence ==1 ) { + fp->f_pos += off; + ret = fp->f_pos; + }else + ret = -EINVAL ; +up (&fp->f_dentry->d_inode->i_sem); +_IPATH_DBG ("New offset %llx from seek %llx whence=%d\n" , fp->f_pos, off, + whence); + +return ret; +} + +/* +* We use this to have a shared buffer between the kernel and the user +* code for the rcvhdr queue, egr buffers, and the per-port user regs and pio +* buffers in the chip. We have the open and close entries so we can bump +* the ref count and keep the driver from being unloaded while still mapped. +*/ + +static struct vm_operations_struct ipath_vmops = { + .nopage = ipath_nopage, +}; + +static int ipath_mmap (struct file *fp,struct vm_area_struct *vm) +{ +int setlen =0 , ret = -EINVAL ; +struct ipath_portdata *pd; + +if (fp->private_data &&255UL < (unsigned long )fp->private_data) { + pd =port_fp (fp); + { +/* +* This is the ipath_do_user_init() code, +* mapping the shared buffers into the user +* process. The address referred to by vm_pgoff +* is the virtual, not physical, address; we only +* do one mmap for each space mapped. +*/ +uint64_t pgaddr, ureg; + + pgaddr = vm->vm_pgoff <<PAGE_SHIFT ; + +/* +* note that ureg does *NOT* have the kregvirt +* as part of it, to be sure that for 32 bit +* programs, we don't end up trying to map +* a > 44 address. Has to match ipath_get_baseinfo() +* code that sets __spi_uregbase +*/ + + ureg = devdata[pd->port_unit].ipath_uregbase + + devdata[pd->port_unit].ipath_palign * pd->port_port; + +_IPATH_MMDBG + ("ushare: pgaddr %llx vm_start=%lx, vmlen %lx\n" , + pgaddr, vm->vm_start, vm->vm_end - vm->vm_start); + +if (pgaddr == ureg) { +/* it's the real hardware, so io_remap works */ +unsigned long phys; +if ((vm->vm_end - vm->vm_start) >PAGE_SIZE ) { +_IPATH_INFO + ("FAIL mmap userreg: reqlen %lx > PAGE\n" , + vm->vm_end - vm->vm_start); + ret = -EFAULT ; + }else { + phys = + devdata[pd->port_unit]. + ipath_physaddr + ureg; + vm->vm_page_prot = +pgprot_noncached (vm->vm_page_prot); + + vm->vm_flags |= +VM_DONTCOPY |VM_DONTEXPAND |VM_IO + |VM_SHM |VM_LOCKED ; + ret = +io_remap_pfn_range (vm, vm->vm_start, phys >>PAGE_SHIFT , + vm->vm_end - vm->vm_start, + vm->vm_page_prot); + } + }else if (pgaddr == pd->port_piobufs) { +/* +* We use io_remap, so there is not a +* nopage handler for this case! +* when we map the PIO buffers, we want +* to map them as writeonly, no read possible. +*/ + +unsigned long phys; +if ((vm->vm_end - vm->vm_start) > + (devdata[pd->port_unit].ipath_pbufsport * + devdata[pd->port_unit].ipath_palign)) { +_IPATH_INFO + ("FAIL mmap piobufs: reqlen %lx > PAGE\n" , + vm->vm_end - vm->vm_start); + ret = -EFAULT ; + }else { + phys = + devdata[pd->port_unit]. + ipath_physaddr + pd->port_piobufs; +/* +* Do *NOT* mark this as +* non-cached (PWT bit), or we +* don't get the write combining +* behavior we want on the +* PIO buffers! +* vm->vm_page_prot = pgprot_noncached(vm->vm_page_prot); +*/ + +#if defined (pgprot_writecombine) && defined(_PAGE_MA_WC) +/* Enable WC */ + vm->vm_page_prot = +pgprot_writecombine (vm-> + vm_page_prot); +#endif + +if (vm->vm_flags &VM_READ ) { +_IPATH_INFO + ("Can't map piobufs as readable (flags=%lx)\n" , + vm->vm_flags); + ret = -EPERM ; + }else { +/* +* don't allow them to +* later change to readable +* with mprotect +*/ + + vm->vm_flags &= ~VM_MAYWRITE ; + + vm->vm_flags |= +VM_DONTCOPY |VM_DONTEXPAND + |VM_IO |VM_SHM | +VM_LOCKED ; + ret = +io_remap_pfn_range (vm, vm->vm_start, phys >>PAGE_SHIFT , + vm->vm_end - vm->vm_start, + vm->vm_page_prot); + } + } + }else if (pgaddr == (uint64_t ) pd->port_rcvegr_phys) { +if (!pd->port_rcvegrbuf_virt) +return -EFAULT ; +/* +* page_alloc'ed egr memory, not +* physically contiguous +* *BUT* to work around the 32 bit mmap64 +* only handling 44 bits, we have remapped +* the first page to kernel virtual, so +* we have to do the conversion here to +* get back to the original virtual +* address (not contig pages) so we have +* to mark this for special handling. +*/ + +/* +* not egrbufs * egrsize since they are +* no longer virtually contiguous. +*/ + setlen = pd->port_rcvegrbuf_chunks *PAGE_SIZE * + (1 << pd->port_rcvegrbuf_order); +if ((vm->vm_end - vm->vm_start) > setlen) { +_IPATH_INFO + ("FAIL on egr bufs: reqlen %lx > actual %x\n" , + vm->vm_end - vm->vm_start, setlen); + ret = -EFAULT ; + }else { + vm->vm_ops = &ipath_vmops; + vm->vm_private_data = + (void *)(3 | (uint64_t ) pd); +if (vm->vm_flags &VM_WRITE ) { +_IPATH_INFO + ("Can't map eager buffers as writable (flags=%lx)\n" , + vm->vm_flags); + ret = -EPERM ; + }else { +/* +* don't allow them to +* later change to writeable +* with mprotect +*/ + + vm->vm_flags &= ~VM_MAYWRITE ; +_IPATH_MMDBG + ("egrbufs, set private to %p, not %llx\n" , + vm->vm_private_data, + pgaddr); + ret =0 ; + } + } + }else if (pgaddr == (uint64_t ) pd->port_rcvhdrq_phys) { +/* +* kmalloc'ed memory, physically +* contiguous; this is from +* spi_rcvhdr_base; we allow user to +* map read-write so they can write +* hdrq entries to allow protocol code +* to directly poll whether a hdrq entry +* has been written. +*/ + setlen =ALIGN (devdata[pd->port_unit].ipath_rcvhdrcnt * devdata[pd->port_unit].ipath_rcvhdrentsize *sizeof (uint32_t ),PAGE_SIZE ); +if ((vm->vm_end - vm->vm_start) > setlen) { +_IPATH_INFO + ("FAIL on rcvhdrq: reqlen %lx > actual %x\n" , + vm->vm_end - vm->vm_start, setlen); + ret = -EFAULT ; + }else { + vm->vm_ops = &ipath_vmops; + vm->vm_private_data = + (void *)(pgaddr |1 ); + ret =0 ; + } + } +/* +* when we map the PIO bufferavail registers, +* we want to map them as readonly, no read +* possible. +*/ +else if (pgaddr == devdata[pd->port_unit].ipath_pioavailregs_phys) { +/* +* kmalloc'ed memory, physically +* contiguous, one page only, readonly +*/ + setlen =PAGE_SIZE ; +if ((vm->vm_end - vm->vm_start) > setlen) { +_IPATH_INFO + ("FAIL on pioavailregs_dma: reqlen %lx > actual %x\n" , + vm->vm_end - vm->vm_start, setlen); + ret = -EFAULT ; + }else if (vm->vm_flags &VM_WRITE ) { +_IPATH_INFO + ("Can't map pioavailregs as writable (flags=%lx)\n" , + vm->vm_flags); + ret = -EPERM ; + }else { +/* +* don't allow them to later +* change with mprotect +*/ + vm->vm_flags &= ~VM_MAYWRITE ; + vm->vm_ops = &ipath_vmops; + vm->vm_private_data = + (void *)(pgaddr |2 ); + ret =0 ; + } + } +if (!ret && setlen) { +/* keep page(s) from being swapped, etc. */ + vm->vm_flags |= +VM_DONTEXPAND |VM_DONTCOPY |VM_RESERVED | +VM_IO |VM_SHM ; + }else { +/* failure, or io_remap case */ + vm->vm_private_data =NULL ; +if (ret) +_IPATH_INFO + ("Failure %d, setlen %d, on addr %lx, off %lx\n" , + ret, setlen, vm->vm_start, + vm->vm_pgoff); + } + } + }else /* something very wrong */ +_IPATH_INFO ("fp_private wasn't set, no mmaping\n" ); + +return ret; +} + +/* page fault handler. For each page that is first faulted in from the +* mmap'ed shared address buffer, this routine is called. +* It's always for a single page. +* We use the low bits of the private_data field to tell us which case +* we are dealing with. +*/ + +static struct page *ipath_nopage (struct vm_area_struct *vma,unsigned long addr, +int *type) +{ +unsigned long avirt,/* the original [kv]malloc virtual address */ + paddr,/* physical address */ + off;/* calculated page offset */ +uint32_t which, chunk; +void *vaddr =NULL ; +struct ipath_portdata *pd; +struct page *vpage =NOPAGE_SIGBUS ; + +if (!(avirt = (unsigned long )vma->vm_private_data)) { +_IPATH_DBG ("NULL private_data, vm_pgoff %lx\n" , vma->vm_pgoff); + which =0 ;/* quiet incorrect gcc warning */ + goto done; + } + which = avirt &3 ; + avirt &= ~3ULL ; + +if (addr > vma->vm_end) { +_IPATH_DBG ("trying to fault in addr %lx past end\n" , addr); + goto done; + } + +/* +* most of our memory is vmalloc'ed, but rcvhdr Q is physically +* contiguous, either from kmalloc or alloc_pages() +* pgoff is virtual. +*/ +switch (which) { +case 1 :/* rcvhdrq_phys */ +/* should always be 0 */ + off = vma->vm_pgoff - (avirt >>PAGE_SHIFT ); + paddr = addr - vma->vm_start + (off <<PAGE_SHIFT ) + avirt; +_IPATH_MMDBG ("hdrq %lx (u=%lx)\n" , paddr, addr); + vpage =pfn_to_page (paddr >>PAGE_SHIFT ); +break ; +case 2 :/* PIO buffer avail regs */ +/* should always be 0 */ + off = vma->vm_pgoff - (avirt >>PAGE_SHIFT ); + paddr = (addr - vma->vm_start + (off <<PAGE_SHIFT ) + avirt); +_IPATH_MMDBG ("pioav %lx\n" , paddr); + vpage =pfn_to_page (paddr >>PAGE_SHIFT ); +break ; +case 3 : +/* +* rcvegrbufs; page_alloc()'ed like rcvhdrq, but we +* have to pick out which page_alloc()'ed chunk it is. +*/ + pd = (struct ipath_portdata *) avirt; +/* this should always be 0 */ + off = + vma->vm_pgoff - + ((unsigned long )pd->port_rcvegr_phys >>PAGE_SHIFT ); + off = (addr - vma->vm_start + (off <<PAGE_SHIFT )); + + chunk = off / (PAGE_SIZE * (1 << pd->port_rcvegrbuf_order)); +if (chunk > pd->port_rcvegrbuf_chunks) +_IPATH_DBG ("Bad egrbuf chunk %u (max %u); off = %lx\n" , + chunk, pd->port_rcvegrbuf_chunks, off); + vaddr = pd->port_rcvegrbuf_virt[chunk] + + off % (PAGE_SIZE * (1 << pd->port_rcvegrbuf_order)); + paddr =virt_to_phys (vaddr); + vpage =pfn_to_page (paddr >>PAGE_SHIFT ); +_IPATH_MMDBG ("egrb %p,%lx\n" , vaddr, paddr); +break ; +default : +_IPATH_DBG + ("trying to fault in mmap addr %lx (avirt %lx) that isn't known (case %u)\n" , + addr, avirt, which); + } + +done: +if (vpage !=NOPAGE_SIGBUS && vpage !=NOPAGE_OOM ) { +if (which ==2 ) +/* +* media/video/video-buf.c doesn't do get_page() for +* buffer from alloc_page(). Hmmm. +* +* keep it from being swapped, complaints if +* process exits before we [vf]free it, etc, +* and keep shared page counts correct, etc. +*/ +get_page (vpage); +mark_page_accessed (vpage); +if (type) + *type =VM_FAULT_MINOR ; + }else +_IPATH_DBG ("faultin of addr %lx vaddr %p avirt %lx failed\n" , + addr, vaddr, avirt); + +return vpage; +} + +/* this is separate to allow for better optimization of ipath_intr() */ + +static void ipath_bad_intr (const ipath_type t,uint32_t * unexpectp) +{ +struct ipath_devdata *dd = &devdata[t]; + +/* +* sometimes happen during driver init and unload, don't want +* to process any interrupts at that point +*/ + +/* this is just a bandaid, not a fix, if something goes badly wrong */ +if (++*unexpectp >100 ) { +if (++*unexpectp >105 ) { +/* +* ok, we must be taking somebody else's interrupts, +* due to a messed up mptable and/or PIRQ table, so +* unregister the interrupt. We've seen this +* during linuxbios development work, and it +* may happen in the future again. +*/ +if (dd->pcidev && dd->pcidev->irq) { +_IPATH_UNIT_ERROR (t, +"Now %u unexpected interrupts, unregistering interrupt handler\n" , + *unexpectp); +_IPATH_DBG ("free_irq of irq %x\n" , + dd->pcidev->irq); +free_irq (dd->pcidev->irq, dd); + dd->pcidev->irq =0 ; + } + } +if (ipath_kget_kreg32 (t, kr_intmask)) { +_IPATH_UNIT_ERROR (t, +"%u unexpected interrupts, disabling interrupts completely\n" , + *unexpectp); +/* disable all interrupts, something is very wrong */ +ipath_kput_kreg (t, kr_intmask,0ULL ); + } + }else if (*unexpectp >1 ) +_IPATH_DBG + ("Interrupt when not ready, should not happen, ignoring\n" ); +} + +/* separate routine, for better optimization of ipath_intr() */ + +static void ipath_bad_regread (const ipath_type t) +{ +static int allbits; +struct ipath_devdata *dd = &devdata[t]; + +/* +* We print the message and disable interrupts, in hope of +* having a better chance of debugging the problem. +*/ +_IPATH_UNIT_ERROR (t, +"Read of interrupt status failed (all bits set)\n" ); +if (allbits++) { +/* disable all interrupts, something is very wrong */ +ipath_kput_kreg (t, kr_intmask,0ULL ); +if (allbits ==2 ) { +_IPATH_UNIT_ERROR (t, +"Still bad interrupt status, unregistering interrupt\n" ); +free_irq (dd->pcidev->irq, dd); + dd->pcidev->irq =0 ; + }else if (allbits >2 ) { +if ((allbits %10000 ) ==0 ) +printk ("." ); + }else +_IPATH_UNIT_ERROR (t, +"Disabling interrupts, multiple errors\n" ); + } +} + +static irqreturn_t ipath_intr (int irq,void *data,struct pt_regs *regs) +{ +struct ipath_devdata *dd = data; +const ipath_type t =IPATH_UNIT (dd); +uint32_t istat =ipath_kget_kreg32 (t, kr_intstatus); +uint64_t estat =0 ; +static unsigned unexpected =0 ; + +if (unlikely (!istat)) { + ipath_stats.sps_nullintr++; +/* not our interrupt, or already handled */ +return IRQ_NONE ; + } +if (unlikely (istat ==-1 )) { +ipath_bad_regread (t); +/* don't know if it was our interrupt or not */ +return IRQ_NONE ; + } + + ipath_stats.sps_ints++; + +/* +* this needs to be flags&initted, not statusp, so we keep +* taking interrupts even after link goes down, etc. +* Also, we *must* clear the interrupt at some point, or we won't +* take it again, which can be real bad for errors, etc... +*/ + +if (!(dd->ipath_flags &IPATH_INITTED )) { +ipath_bad_intr (t, &unexpected); +return IRQ_NONE ; + } +if (unexpected) + unexpected =0 ; + +if (istat & ~infinipath_i_bitsextant) +_IPATH_UNIT_ERROR (t, +"interrupt with unknown interrupts %x set\n" , + istat & (uint32_t ) ~ infinipath_i_bitsextant); + +if (istat &INFINIPATH_I_ERROR ) { + ipath_stats.sps_errints++; + estat =ipath_kget_kreg64 (t, kr_errorstatus); +if (!estat) +_IPATH_INFO + ("error interrupt (%x), but no error bits set!\n" , + istat); +else if (estat ==-1LL ) +/* +* should we try clearing all, or hope next read +* works? +*/ +_IPATH_UNIT_ERROR (t, +"Read of error status failed (all bits set); ignoring\n" ); +else +ipath_handle_errors (t, estat); + } + +if (istat &INFINIPATH_I_GPIO ) { +/* Clear GPIO status bit 2 */ +ipath_kput_kreg (t, kr_gpio_clear, (uint64_t )(1 <<2 )); + +/* +* Packets are available in the port 0 receive queue. +* Eventually this needs to be generalized to check +* IPATH_GPIO_INTR, and the specific GPIO bit, when +* GPIO interrupts start being used for other things. +* We skip that now to improve performance. +*/ +ipath_kreceive (t); + } + +/* +* clear the ones we will deal with on this round +* We clear it early, mostly for receive interrupts, so we +* know the chip will have seen this by the time we process +* the queue, and will re-interrupt if necessary. The processor +* itself won't take the interrupt again until we return. +*/ +ipath_kput_kreg (t, kr_intclear, istat); + +if (istat &INFINIPATH_I_SPIOBUFAVAIL ) { +atomic_clear_mask (INFINIPATH_S_PIOINTBUFAVAIL , + &dd->ipath_sendctrl); +ipath_kput_kreg (t, kr_sendctrl, dd->ipath_sendctrl); + +if (dd->ipath_portpiowait) { +uint32_t i; +/* +* start from port 1, since for now port 0 is +* never using wait_event for PIO +*/ +for (i =1 ; + dd->ipath_portpiowait && i < dd->ipath_cfgports; + i++) { +if (dd->ipath_pd[i] + && dd->ipath_portpiowait & (1U << i)) { +atomic_clear_mask (1U << i, + &dd-> + ipath_portpiowait); +if (dd->ipath_pd[i]-> + port_flag &IPATH_PORT_WAITING_PIO ) + { + dd->ipath_pd[i]->port_flag &= + ~IPATH_PORT_WAITING_PIO ; +wake_up_interruptible (&dd-> + ipath_pd + [i]-> + port_wait); + } + } + } + } + +if (dd->ipath_layer.l_intr) { +if (dd->ipath_layer.l_intr (t, +IPATH_LAYER_INT_SEND_CONTINUE )) { +atomic_set_mask (INFINIPATH_S_PIOINTBUFAVAIL , + &dd->ipath_sendctrl); +ipath_kput_kreg (t, kr_sendctrl, + dd->ipath_sendctrl); + } + } + +if (dd->verbs_layer.l_piobufavail) { +if (!dd->verbs_layer.l_piobufavail (t)) { +atomic_set_mask (INFINIPATH_S_PIOINTBUFAVAIL , + &dd->ipath_sendctrl); +ipath_kput_kreg (t, kr_sendctrl, + dd->ipath_sendctrl); + } + } + } + +/* +* we check for both transition from empty to non-empty, and urgent +* packets (those with the interrupt bit set in the header) +*/ + +if (istat & ((infinipath_i_rcvavail_mask <<INFINIPATH_I_RCVAVAIL_SHIFT ) + | (infinipath_i_rcvurg_mask <<INFINIPATH_I_RCVURG_SHIFT ))) { +uint64_t portr; +int i; +uint32_t rcvdint =0 ; + + portr = ((istat >>INFINIPATH_I_RCVAVAIL_SHIFT ) & + infinipath_i_rcvavail_mask) + | ((istat >>INFINIPATH_I_RCVURG_SHIFT ) & + infinipath_i_rcvurg_mask); +for (i =0 ; i < dd->ipath_cfgports; i++) { +if (portr & (1 << i) && dd->ipath_pd[i]) { +if (i ==0 ) +ipath_kreceive (t); +else if (dd->ipath_pd[i]-> + port_flag &IPATH_PORT_WAITING_RCV ) { +atomic_clear_mask + (IPATH_PORT_WAITING_RCV , + &dd->ipath_pd[i]->port_flag); +wake_up_interruptible (&dd->ipath_pd[i]-> + port_wait); + rcvdint |=1U << i; + } + } + } +if (rcvdint) { +/* +* only want to take one interrupt, so turn off +* the rcv interrupt for all the ports that we +* did the wakeup on (but never for kernel port) +*/ +atomic_clear_mask (rcvdint << +INFINIPATH_R_INTRAVAIL_SHIFT , + &dd->ipath_rcvctrl); +ipath_kput_kreg (t, kr_rcvctrl, dd->ipath_rcvctrl); + } + } + +return IRQ_HANDLED ; +} + +static void ipath_decode_err (char *buf,size_t blen,uint64_t err) +{ + *buf ='\0' ; +if (err &INFINIPATH_E_RHDRLEN ) +strlcat (buf,"rhdrlen " , blen); +if (err &INFINIPATH_E_RBADTID ) +strlcat (buf,"rbadtid " , blen); +if (err &INFINIPATH_E_RBADVERSION ) +strlcat (buf,"rbadversion " , blen); +if (err &INFINIPATH_E_RHDR ) +strlcat (buf,"rhdr " , blen); +if (err &INFINIPATH_E_RLONGPKTLEN ) +strlcat (buf,"rlongpktlen " , blen); +if (err &INFINIPATH_E_RSHORTPKTLEN ) +strlcat (buf,"rshortpktlen " , blen); +if (err &INFINIPATH_E_RMAXPKTLEN ) +strlcat (buf,"rmaxpktlen " , blen); +if (err &INFINIPATH_E_RMINPKTLEN ) +strlcat (buf,"rminpktlen " , blen); +if (err &INFINIPATH_E_RFORMATERR ) +strlcat (buf,"rformaterr " , blen); +if (err &INFINIPATH_E_RUNSUPVL ) +strlcat (buf,"runsupvl " , blen); +if (err &INFINIPATH_E_RUNEXPCHAR ) +strlcat (buf,"runexpchar " , blen); +if (err &INFINIPATH_E_RIBFLOW ) +strlcat (buf,"ribflow " , blen); +if (err &INFINIPATH_E_REBP ) +strlcat (buf,"EBP " , blen); +if (err &INFINIPATH_E_SUNDERRUN ) +strlcat (buf,"sunderrun " , blen); +if (err &INFINIPATH_E_SPIOARMLAUNCH ) +strlcat (buf,"spioarmlaunch " , blen); +if (err &INFINIPATH_E_SUNEXPERRPKTNUM ) +strlcat (buf,"sunexperrpktnum " , blen); +if (err &INFINIPATH_E_SDROPPEDDATAPKT ) +strlcat (buf,"sdroppeddatapkt " , blen); +if (err &INFINIPATH_E_SDROPPEDSMPPKT ) +strlcat (buf,"sdroppedsmppkt " , blen); +if (err &INFINIPATH_E_SMAXPKTLEN ) +strlcat (buf,"smaxpktlen " , blen); +if (err &INFINIPATH_E_SMINPKTLEN ) +strlcat (buf,"sminpktlen " , blen); +if (err &INFINIPATH_E_SUNSUPVL ) +strlcat (buf,"sunsupVL " , blen); +if (err &INFINIPATH_E_SPKTLEN ) +strlcat (buf,"spktlen " , blen); +if (err &INFINIPATH_E_INVALIDADDR ) +strlcat (buf,"invalidaddr " , blen); +if (err &INFINIPATH_E_RICRC ) +strlcat (buf,"CRC " , blen); +if (err &INFINIPATH_E_RVCRC ) +strlcat (buf,"VCRC " , blen); +if (err &INFINIPATH_E_RRCVEGRFULL ) +strlcat (buf,"rcvegrfull " , blen); +if (err &INFINIPATH_E_RRCVHDRFULL ) +strlcat (buf,"rcvhdrfull " , blen); +if (err &INFINIPATH_E_IBSTATUSCHANGED ) +strlcat (buf,"ibcstatuschg " , blen); +if (err &INFINIPATH_E_RIBLOSTLINK ) +strlcat (buf,"riblostlink " , blen); +if (err &INFINIPATH_E_HARDWARE ) +strlcat (buf,"hardware " , blen); +if (err &INFINIPATH_E_RESET ) +strlcat (buf,"reset " , blen); +} + +/* decode RHF errors; only used one place now, may want more later */ +static void get_rhf_errstring (uint32_t err,char *msg,size_t len) +{ +/* if no errors, and so don't need to check what's first */ + *msg ='\0' ; + +if (err &INFINIPATH_RHF_H_ICRCERR ) +strlcat (msg,"icrcerr " , len); +if (err &INFINIPATH_RHF_H_VCRCERR ) +strlcat (msg,"vcrcerr " , len); +if (err &INFINIPATH_RHF_H_PARITYERR ) +strlcat (msg,"parityerr " , len); +if (err &INFINIPATH_RHF_H_LENERR ) +strlcat (msg,"lenerr " , len); +if (err &INFINIPATH_RHF_H_MTUERR ) +strlcat (msg,"mtuerr " , len); +if (err &INFINIPATH_RHF_H_IHDRERR ) +/* infinipath hdr checksum error */ +strlcat (msg,"ipathhdrerr " , len); +if (err &INFINIPATH_RHF_H_TIDERR ) +strlcat (msg,"tiderr " , len); +if (err &INFINIPATH_RHF_H_MKERR ) +/* bad port, offset, etc. */ +strlcat (msg,"invalid ipathhdr " , len); +if (err &INFINIPATH_RHF_H_IBERR ) +strlcat (msg,"iberr " , len); +if (err &INFINIPATH_RHF_L_SWA ) +strlcat (msg,"swA " , len); +if (err &INFINIPATH_RHF_L_SWB ) +strlcat (msg,"swB " , len); +} + +static void ipath_handle_errors (const ipath_type t,uint64_t errs) +{ +char msg[512 ]; +uint32_t piobcnt; +uint64_t sbuf[4 ], ignore_this_time =0 ; +int i; +int chkerrpkts =0 , noprint =0 ; +cycles_t nc; +static cycles_t nextmsg_time; +static unsigned nmsgs, supp_msgs; +struct ipath_devdata *dd = &devdata[t]; + +#define E_SUM_PKTERRS (INFINIPATH_E_RHDRLEN | INFINIPATH_E_RBADTID \ + | INFINIPATH_E_RBADVERSION \ + | INFINIPATH_E_RHDR | INFINIPATH_E_RLONGPKTLEN | INFINIPATH_E_RSHORTPKTLEN \ + | INFINIPATH_E_RMAXPKTLEN | INFINIPATH_E_RMINPKTLEN \ + | INFINIPATH_E_RFORMATERR | INFINIPATH_E_RUNSUPVL | INFINIPATH_E_RUNEXPCHAR \ + | INFINIPATH_E_REBP) + +#define E_SUM_ERRS ( INFINIPATH_E_SPIOARMLAUNCH \ + | INFINIPATH_E_SUNEXPERRPKTNUM | INFINIPATH_E_SDROPPEDDATAPKT \ + | INFINIPATH_E_SDROPPEDSMPPKT | INFINIPATH_E_SMAXPKTLEN \ + | INFINIPATH_E_SUNSUPVL | INFINIPATH_E_SMINPKTLEN | INFINIPATH_E_SPKTLEN \ + | INFINIPATH_E_INVALIDADDR) + +/* +* throttle back "fast" messages to no more than 10 per 5 seconds +* (1.4-2GHz clock). This isn't perfect, but it's a reasonable +* heuristic +* If we get more than 10, give a 5x longer delay +*/ + nc =get_cycles (); +if (nmsgs >10 ) { +if (nc < nextmsg_time) { + noprint =1 ; +if (!supp_msgs++) + nextmsg_time = nc +50000000000ULL ; + }else if (supp_msgs) { +/* +* Print the message unless it's ibc status +* change only, which happens so often we never +* want to count it. +*/ +if (dd->ipath_lasterror & ~INFINIPATH_E_IBSTATUSCHANGED ) { +ipath_decode_err (msg,sizeof msg, + dd-> + ipath_lasterror & + ~INFINIPATH_E_IBSTATUSCHANGED ); +if (dd-> + ipath_lasterror & ~(INFINIPATH_E_RRCVEGRFULL + | +INFINIPATH_E_RRCVHDRFULL )) +_IPATH_UNIT_ERROR (t, +"Suppressed %u messages for fast-repeating errors (%s) (%llx)\n" , + supp_msgs, msg, + dd->ipath_lasterror); +else { +/* +* rcvegrfull and rcvhdrqfull are +* "normal", for some types of +* processes (mostly benchmarks) +* that send huge numbers of +* messages, while not processing +* them. So only complain about +* these at debug level. +*/ +_IPATH_DBG + ("Suppressed %u messages for %s\n" , + supp_msgs, msg); + } + } + supp_msgs =0 ; + nmsgs =0 ; + } + }else if (!nmsgs++ || nc > nextmsg_time)/* start timer */ + nextmsg_time = nc +10000000000ULL ; + +/* +* don't report errors that are masked (includes those always +* ignored) +*/ + errs &= ~dd->ipath_maskederrs; + +/* do these first, they are most important */ +if (errs &INFINIPATH_E_HARDWARE ) { +/* reuse same msg buf */ +ipath_handle_hwerrors (t, msg,sizeof msg); + } + +if (!noprint && (errs & ~infinipath_e_bitsextant)) +_IPATH_UNIT_ERROR (t, +"error interrupt with unknown errors %llx set\n" , + errs & ~infinipath_e_bitsextant); + +if (errs &E_SUM_ERRS ) { +/* if possible that sendbuffererror could be valid */ + piobcnt = dd->ipath_piobcnt; +/* read these before writing errorclear */ + sbuf[0 ] =ipath_kget_kreg64 (t, kr_sendbuffererror); + sbuf[1 ] =ipath_kget_kreg64 (t, kr_sendbuffererror +1 ); +if (piobcnt >128 ) { + sbuf[2 ] =ipath_kget_kreg64 (t, kr_sendbuffererror +2 ); + sbuf[3 ] =ipath_kget_kreg64 (t, kr_sendbuffererror +3 ); + } + +if (sbuf[0 ] || sbuf[1 ] + || (piobcnt >128 && (sbuf[2 ] || sbuf[3 ]))) { +_IPATH_PDBG ("SendbufErrs %llx %llx " , sbuf[0 ], sbuf[1 ]); +if (infinipath_debug & __IPATH_PKTDBG && piobcnt >128 ) +printk ("%llx %llx " , sbuf[2 ], sbuf[3 ]); +for (i =0 ; i < piobcnt; i++) { +if (test_bit (i, sbuf)) { +uint32_t sendctrl; +if (infinipath_debug & __IPATH_PKTDBG) +printk ("%u " , i); + sendctrl = + dd-> + ipath_sendctrl |INFINIPATH_S_DISARM + | (i << +INFINIPATH_S_DISARMPIOBUF_SHIFT ); +ipath_kput_kreg (t, kr_sendctrl, + sendctrl); + } + } +if (infinipath_debug & __IPATH_PKTDBG) +printk ("\n" ); + } +if ((errs & + (INFINIPATH_E_SDROPPEDDATAPKT |INFINIPATH_E_SDROPPEDSMPPKT + |INFINIPATH_E_SMINPKTLEN )) + && !(dd->ipath_flags &IPATH_LINKACTIVE )) { +/* +* This can happen when SMA is trying to bring +* the link up, but the IB link changes state +* at the "wrong" time. The IB logic then +* complains that the packet isn't valid. +* We don't want to confuse people, so we just +* don't print them, except at debug +*/ +_IPATH_DBG + ("Ignoring pktsend errors %llx, because not yet active\n" , + errs); + ignore_this_time |= +INFINIPATH_E_SDROPPEDDATAPKT | +INFINIPATH_E_SDROPPEDSMPPKT | +INFINIPATH_E_SMINPKTLEN ; + } + } + +if (supp_msgs ==250000 ) { +/* +* It's not entirely reasonable assuming that the errors +* set in the last clear period are all responsible for +* the problem, but the alternative is to assume it's the only +* ones on this particular interrupt, which also isn't great +*/ + dd->ipath_maskederrs |= dd->ipath_lasterror | errs; +ipath_kput_kreg (t, kr_errormask, ~dd->ipath_maskederrs); +ipath_decode_err (msg,sizeof msg, + (dd->ipath_maskederrs & ~dd-> + ipath_ignorederrs)); + +if ((dd->ipath_maskederrs & ~dd->ipath_ignorederrs) + & ~(INFINIPATH_E_RRCVEGRFULL |INFINIPATH_E_RRCVHDRFULL )) +_IPATH_UNIT_ERROR (t, +"Disabling error(s) %llx because occuring too frequently (%s)\n" , + (dd->ipath_maskederrs & ~dd-> + ipath_ignorederrs), msg); +else { +/* +* rcvegrfull and rcvhdrqfull are "normal", +* for some types of processes (mostly benchmarks) +* that send huge numbers of messages, while not +* processing them. So only complain about +* these at debug level. +*/ +_IPATH_DBG + ("Disabling frequent queue full errors (%s)\n" , + msg); + } + +/* +* re-enable the masked errors after around 3 minutes. +* in ipath_get_faststats(). If we have a series of +* fast repeating but different errors, the interval will keep +* stretching out, but that's OK, as that's pretty catastrophic. +*/ + dd->ipath_unmasktime = nc +400000000000ULL ; + } + +ipath_kput_kreg (t, kr_errorclear, errs); +if (ignore_this_time) + errs &= ~ignore_this_time; +if (errs & ~dd->ipath_lasterror) { + errs &= ~dd->ipath_lasterror; +/* never suppress duplicate hwerrors or ibstatuschange */ + dd->ipath_lasterror |= errs & + ~(INFINIPATH_E_HARDWARE |INFINIPATH_E_IBSTATUSCHANGED ); + } +if (!errs) +return ; + +if (!noprint) +/* the ones we mask off are handled specially below or above */ +ipath_decode_err (msg,sizeof msg, + errs & ~(INFINIPATH_E_IBSTATUSCHANGED | +INFINIPATH_E_RRCVEGRFULL | +INFINIPATH_E_RRCVHDRFULL | +INFINIPATH_E_HARDWARE )); +else +/* so we don't need if (!noprint) at strlcat's below */ + *msg =0 ; + +if (errs &E_SUM_PKTERRS ) { + ipath_stats.sps_pkterrs++; + chkerrpkts =1 ; + } +if (errs &E_SUM_ERRS ) + ipath_stats.sps_errs++; + +if (errs & (INFINIPATH_E_RICRC |INFINIPATH_E_RVCRC )) { + ipath_stats.sps_crcerrs++; + chkerrpkts =1 ; + } + +/* +* We don't want to print these two as they happen, or we can make +* the situation even worse, because it takes so long to print messages. +* to serial consoles. kernel ports get printed from fast_stats, no +* more than every 5 seconds, user ports get printed on close +*/ +if (errs &INFINIPATH_E_RRCVHDRFULL ) { +int any; +uint32_t hd, tl; + ipath_stats.sps_hdrqfull++; +for (any = i =0 ; i < dd->ipath_cfgports; i++) { +if (i ==0 ) { + hd = dd->ipath_port0head; + tl = *dd->ipath_hdrqtailptr; + }else if (dd->ipath_pd[i] && + dd->ipath_pd[i]->port_rcvhdrtail_kvaddr) { +/* +* don't report same point multiple times, +* except kernel +*/ + tl = (uint32_t ) * + dd->ipath_pd[i]->port_rcvhdrtail_kvaddr; +if (tl == dd->ipath_lastrcvhdrqtails[i]) +continue ; + hd =ipath_kget_ureg32 (t, ur_rcvhdrhead, i); + }else +continue ; +if (hd == (tl +1 ) || (!hd && tl == dd->ipath_hdrqlast)) { + dd->ipath_lastrcvhdrqtails[i] = tl; + dd->ipath_pd[i]->port_hdrqfull++; +if (i ==0 ) + chkerrpkts =1 ; + } + } + } +if (errs &INFINIPATH_E_RRCVEGRFULL ) { +/* +* since this is of less importance and not likely to +* happen without also getting hdrfull, only count +* occurrences; don't check each port (or even the kernel +* vs user) +*/ + ipath_stats.sps_etidfull++; +if (dd->ipath_port0head != *dd->ipath_hdrqtailptr) + chkerrpkts =1 ; + } + +/* +* do this before IBSTATUSCHANGED, in case both bits set in a single +* interrupt; we want the STATUSCHANGE to "win", so we do our +* internal copy of state machine correctly +*/ +if (errs &INFINIPATH_E_RIBLOSTLINK ) { +/* force through block below */ + errs |=INFINIPATH_E_IBSTATUSCHANGED ; + ipath_stats.sps_iblink++; + dd->ipath_flags |=IPATH_LINKDOWN ; + dd->ipath_flags &= ~(IPATH_LINKUNK |IPATH_LINKINIT + |IPATH_LINKARMED |IPATH_LINKACTIVE ); +if (!noprint) +_IPATH_DBG ("Lost link, link now down (%s)\n" , + ipath_ibcstatus_str[ipath_kget_kreg64 + (t, + kr_ibcstatus) &0xf ]); + } + +if ((errs &INFINIPATH_E_IBSTATUSCHANGED ) && (!ipath_diags_enabled)) { +uint64_t val; +uint32_t ltstate; + + val =ipath_kget_kreg64 (t, kr_ibcstatus); + ltstate = val &0xff ; +if (ltstate ==0x11 || ltstate ==0x21 || ltstate ==0x31 ) +_IPATH_DBG ("Link state changed unit %u to 0x%x, last was 0x%llx\n" , + t, ltstate, dd->ipath_lastibcstat); +else { + ltstate = dd->ipath_lastibcstat &0xff ; +if (ltstate ==0x11 || ltstate ==0x21 || ltstate ==0x31 ) +_IPATH_DBG ("Link state unit %u changed to down state 0x%llx, last was 0x%llx\n" , + t, val, dd->ipath_lastibcstat); +else +_IPATH_VDBG ("Link state unit %u changed to 0x%llx from one of down states\n" , + t, val); + } + ltstate = (val >>INFINIPATH_IBCS_LINKTRAININGSTATE_SHIFT ) & +INFINIPATH_IBCS_LINKTRAININGSTATE_MASK ; + +if (ltstate ==2 || ltstate ==3 ) { +uint32_t last_ltstate; + +/* +* ignore cycling back and forth from states 2 to 3 +* while waiting for other end of link to come up +* except that if it keeps happening, we switch between +* linkinitstate SLEEP and POLL. While we cycle +* back and forth between them, we aren't seeing +* any other device, either no cable plugged in, +* other device powered off, other device is +* switch that hasn't yet polled us, etc. +*/ + last_ltstate = (dd->ipath_lastibcstat >> +INFINIPATH_IBCS_LINKTRAININGSTATE_SHIFT ) + &INFINIPATH_IBCS_LINKTRAININGSTATE_MASK ; +if (last_ltstate ==2 || last_ltstate ==3 ) { +if (++dd->ipath_ibpollcnt >4 ) { +uint64_t ibc; + dd->ipath_flags |= +IPATH_LINK_SLEEPING |IPATH_NOCABLE ; + *dd->ipath_statusp |= +IPATH_STATUS_IB_NOCABLE ; +_IPATH_VDBG + ("linkinitcmd POLL, move to SLEEP\n" ); + ibc = dd->ipath_ibcctrl; + ibc |=INFINIPATH_IBCC_LINKINITCMD_SLEEP + << +INFINIPATH_IBCC_LINKINITCMD_SHIFT ; +/* +* don't put linkinitcmd in +* ipath_ibcctrl, want that to +* stay a NOP +*/ +ipath_kput_kreg (t, kr_ibcctrl, ibc); + dd->ipath_ibpollcnt =0 ; + } + goto skip_ibchange; + } + } +/* some state other than 2 or 3 */ + dd->ipath_ibpollcnt =0 ; + ipath_stats.sps_iblink++; +/* +* Note: We try to match the Mellanox HCA LED behavior +* as best we can. That changed around Oct 2003. +* Green indicates link state (something is plugged in, +* and we can train). Amber indicates the link is +* logically up (ACTIVE). Mellanox further blinks the +* amber LED to indicate data packet activity, but we +* have no hardware support for that, so it would require +* waking up every 10-20 msecs and checking the counters +* on the chip, and then turning the LED off if +* appropriate. That's visible overhead, so not something +* we will do. +*/ +if (ltstate !=1 || ((dd->ipath_lastibcstat &0x30 ) ==0x30 && + (val &0x30 ) !=0x30 )) { + dd->ipath_flags |=IPATH_LINKDOWN ; + dd->ipath_flags &= ~(IPATH_LINKUNK |IPATH_LINKINIT + |IPATH_LINKACTIVE | +IPATH_LINKARMED ); + *dd->ipath_statusp &= ~IPATH_STATUS_IB_READY ; +if (!noprint) { +if ((dd->ipath_lastibcstat &0x30 ) ==0x30 ) +/* if from up to down be more vocal */ +_IPATH_DBG ("Link unit %u is now down (%s)\n" , + t, ipath_ibcstatus_str + [ltstate]); +else +_IPATH_VDBG ("Link unit %u is down (%s)\n" , + t, ipath_ibcstatus_str + [ltstate]); + } + +if (val &0x30 ) { +/* leave just green on, 0x11 and 0x21 */ + dd->ipath_extctrl &= + ~INFINIPATH_EXTC_LEDPRIPORTYELLOWON ; + dd->ipath_extctrl |= +INFINIPATH_EXTC_LEDPRIPORTGREENON ; + }else /* not up at all, so turn the leds off */ + dd->ipath_extctrl &= + ~(INFINIPATH_EXTC_LEDPRIPORTGREENON | +INFINIPATH_EXTC_LEDPRIPORTYELLOWON ); +ipath_kput_kreg (t, kr_extctrl, + (uint64_t ) dd->ipath_extctrl); +if (ltstate ==1 + && (dd-> + ipath_flags & (IPATH_LINK_TOARMED | +IPATH_LINK_TOACTIVE ))) { +ipath_set_ib_lstate (t, +INFINIPATH_IBCC_LINKCMD_INIT ); + } + }else if ((val &0x31 ) ==0x31 ) { +if (!noprint) +_IPATH_DBG ("Link unit %u is now in active state\n" , t); + dd->ipath_flags |=IPATH_LINKACTIVE ; + dd->ipath_flags &= + ~(IPATH_LINKUNK |IPATH_LINKINIT |IPATH_LINKDOWN | +IPATH_LINKARMED |IPATH_NOCABLE | +IPATH_LINK_TOACTIVE |IPATH_LINK_SLEEPING ); + *dd->ipath_statusp &= ~IPATH_STATUS_IB_NOCABLE ; + *dd->ipath_statusp |= +IPATH_STATUS_IB_READY |IPATH_STATUS_IB_CONF ; +/* set the externally visible LEDs to indicate state */ + dd->ipath_extctrl |=INFINIPATH_EXTC_LEDPRIPORTGREENON + |INFINIPATH_EXTC_LEDPRIPORTYELLOWON ; +ipath_kput_kreg (t, kr_extctrl, + (uint64_t ) dd->ipath_extctrl); + +/* +* since we are now active, set the linkinitcmd +* to NOP (0) it was probably either POLL or SLEEP +*/ + dd->ipath_ibcctrl &= + ~(INFINIPATH_IBCC_LINKINITCMD_MASK << +INFINIPATH_IBCC_LINKINITCMD_SHIFT ); +ipath_kput_kreg (t, kr_ibcctrl, dd->ipath_ibcctrl); + +if (devdata[t].ipath_layer.l_intr) + devdata[t].ipath_layer.l_intr (t, +IPATH_LAYER_INT_IF_UP ); + }else if ((val &0x31 ) ==0x11 ) { +/* +* set set INIT and DOWN. Down is checked by +* most of the other code, but INIT is useful +* to know in a few places. +*/ + dd->ipath_flags |=IPATH_LINKINIT |IPATH_LINKDOWN ; + dd->ipath_flags &= + ~(IPATH_LINKUNK |IPATH_LINKACTIVE |IPATH_LINKARMED + |IPATH_NOCABLE |IPATH_LINK_SLEEPING ); + *dd->ipath_statusp &= ~(IPATH_STATUS_IB_NOCABLE + |IPATH_STATUS_IB_READY ); + +/* set the externally visible LEDs to indicate state */ + dd->ipath_extctrl &= + ~INFINIPATH_EXTC_LEDPRIPORTYELLOWON ; + dd->ipath_extctrl |=INFINIPATH_EXTC_LEDPRIPORTGREENON ; +ipath_kput_kreg (t, kr_extctrl, + (uint64_t ) dd->ipath_extctrl); +if (dd-> + ipath_flags & (IPATH_LINK_TOARMED | +IPATH_LINK_TOACTIVE )) { +/* +* if we got here while trying to bring +* the link up, try again, but only once more! +*/ +ipath_set_ib_lstate (t, +INFINIPATH_IBCC_LINKCMD_ARMED ); + dd->ipath_flags &= + ~(IPATH_LINK_TOARMED |IPATH_LINK_TOACTIVE ); + } + }else if ((val &0x31 ) ==0x21 ) { + dd->ipath_flags |=IPATH_LINKARMED ; + dd->ipath_flags &= + ~(IPATH_LINKUNK |IPATH_LINKDOWN |IPATH_LINKINIT | +IPATH_LINKACTIVE |IPATH_NOCABLE | +IPATH_LINK_TOARMED |IPATH_LINK_SLEEPING ); + *dd->ipath_statusp &= ~(IPATH_STATUS_IB_NOCABLE + |IPATH_STATUS_IB_READY ); +/* +* set the externally visible LEDs to indicate +* state (same as 0x11) +*/ + dd->ipath_extctrl &= + ~INFINIPATH_EXTC_LEDPRIPORTYELLOWON ; + dd->ipath_extctrl |=INFINIPATH_EXTC_LEDPRIPORTGREENON ; +ipath_kput_kreg (t, kr_extctrl, + (uint64_t ) dd->ipath_extctrl); +if (dd->ipath_flags &IPATH_LINK_TOACTIVE ) { +/* +* if we got here while trying to bring +* the link up, try again, but only once more! +*/ +ipath_set_ib_lstate (t, +INFINIPATH_IBCC_LINKCMD_ACTIVE ); + dd->ipath_flags &= ~IPATH_LINK_TOACTIVE ; + } + }else { +if (dd-> + ipath_flags & (IPATH_LINK_TOARMED | +IPATH_LINK_TOACTIVE )) +ipath_set_ib_lstate (t, +INFINIPATH_IBCC_LINKCMD_INIT ); +else if (!noprint) +_IPATH_DBG ("IBstatuschange unit %u: %s\n" , + t, ipath_ibcstatus_str[ltstate]); + } + dd->ipath_lastibcstat = val; + } + +skip_ibchange: + +if (errs &INFINIPATH_E_RESET ) { +if (!noprint) +_IPATH_UNIT_ERROR (t, +"Got reset, requires re-initialization (unload and reload driver)\n" ); + dd->ipath_flags &= ~IPATH_INITTED ;/* needs re-init */ +/* mark as having had error */ + *dd->ipath_statusp |=IPATH_STATUS_HWERROR ; + *dd->ipath_statusp &= ~IPATH_STATUS_IB_CONF ; + } + +if (!noprint && *msg) +_IPATH_UNIT_ERROR (t,"%s error\n" , msg); +if (dd->ipath_sma_state_wanted & dd->ipath_flags) { +_IPATH_VDBG ("sma wanted state %x, iflags now %x, waking\n" , + dd->ipath_sma_state_wanted, dd->ipath_flags); +wake_up_interruptible (&ipath_sma_state_wait); + } + +if (chkerrpkts) +/* process possible error packets in hdrq */ +ipath_kreceive (t); +}
Re: [openib-general] [PATCH 11 of 20] ipath - core driver, part 4 of 4
In reply to Bryan O'SullivanI didn't notice this before:
Review
Quoted 5 lines
+ * This is volatile as it's the target of a DMA from the chip.
+ */(quoted diff) +3
-0+ +static volatile uint64_t ipath_port0_rcvhdrtail[512] + __attribute__ ((aligned(4096)));
... and then much later ...
drivers/infiniband/hw/ipath/ipath_driver.c line 6356
Quoted 11 lines
drivers/infiniband/hw/ipath/ipath_driver.c +11
-0+/* +* kernel modules loaded into vmalloc'ed memory, +* verify that when we assume that, map to phys, and back to virt, +* that we get the right contents, so we did the mapping right. +*/ + vpage =vmalloc_to_page ((void *)ipath_port0_rcvhdrtail); +if (vpage ==NOPAGE_SIGBUS || vpage ==NOPAGE_OOM ) { +_IPATH_UNIT_ERROR (t,"vmalloc_to_page for rcvhdrtail fails!\n" ); + ret = -ENOMEM ; + goto done; + }
This seems very wrong to me: there's no guarantee that a module will be loaded into memory that can be used as a DMA target. For example, on a non-cache-coherent architecture, I think this memory must be accessed through a non-cached mapping.
I think the correct solution is to allocate a buffer for each device with pci_alloc_consistent() (or maybe dma_alloc_coherent()).
(As a general comment, I'm still unhappy about how your driver has a static, fixed-size table of devices rather than allocating per-device data structures dynamically)
- R.
Re: [PATCH 5 of 20] ipath - driver core header files
In reply to Bryan O'SullivanHi Bryan,
drivers/infiniband/hw/ipath/ips_common.h line 247
Quoted 5 lines · Bryan O'Sullivan
drivers/infiniband/hw/ipath/ips_common.h new file +5
-0+/* +* Copy routine that is guaranteed to work in terms of aligned 32-bit +* quantities. +*/ +void ipath_dwordcpy (uint32_t *dest,uint32_t *src,uint32_t ndwords);
Wasn't this supposed to be killed?
Pekka
Re: [PATCH 14 of 20] ipath - infiniband verbs header
In reply to Bryan O'SullivanQuoted 9 lines · Bryan O'Sullivan
diff -r
f9bcd9de3548-r26993cb5faeedrivers/infiniband/hw/ipath/verbs_debug.hdrivers/infiniband/hw/ipath/verbs_debug.h Wed Dec 28 14:19:43 2005 -0800 renamed from dev/null Thu Jan 1 00:00:00 1970 +0000 +6
-0+#ifndef _VERBS_DEBUG_H +#define _VERBS_DEBUG_H + +/* + * This file contains tracing code for the ib_ipath kernel module. + */
drivers/infiniband/hw/ipath/verbs_debug.h Wed Dec 28 14:19:43 2005 -0800
Quoted 10 lines
drivers/infiniband/hw/ipath/verbs_debug.h Wed Dec 28 14:19:43 2005 -0800 renamed from dev/null Thu Jan 1 00:00:00 1970 +0000 +10
-0+#ifndef _VERBS_DEBUGGING /* tracing enabled or not */ +#define _VERBS_DEBUGGING 1 +#endif + +extern unsigned ib_ipath_debug; + +#define _VERBS_ERROR(fmt,...) \ + do { \ + printk(KERN_ERR "%s: " fmt, "ib_ipath", ##__VA_ARGS__); \ + } while(0)
[snip, snip]
Please consider using dev_dbg, dev_err, and friends from <linux/device.h>.
Pekka
Re: [PATCH 6 of 20] ipath - driver debugging headers
In reply to Bryan O'SullivanQuoted 20 lines · Bryan O'Sullivan
+#endif /*
_IPATH_DEBUG_H*/
diff -r2d9a3f27a10c-r9e8d017ed298drivers/infiniband/hw/ipath/ipath_kdebug.hdrivers/infiniband/hw/ipath/ipath_kdebug.h Wed Dec 28 14:19:42 2005 -0800 renamed from dev/null Thu Jan 1 00:00:00 1970 +0000 +15
-0@@ -0,0 +1,109 @@ +#ifndef _IPATH_KDEBUG_H +#define _IPATH_KDEBUG_H + +#include "ipath_debug.h" + +/* + * This file contains lightweight kernel tracing code. + */ + +extern unsigned infinipath_debug; +const char *ipath_get_unit_name(int unit); + +#if _IPATH_DEBUGGING + +#define _IPATH_UNIT_ERROR(unit,fmt,...) \
drivers/infiniband/hw/ipath/ipath_kdebug.h Wed Dec 28 14:19:42 2005 -0800 line 25
Quoted 10 lines
drivers/infiniband/hw/ipath/ipath_kdebug.h Wed Dec 28 14:19:42 2005 -0800 renamed from dev/null Thu Jan 1 00:00:00 1970 +0000 +10
-0+ printk(KERN_ERR "%s: " fmt, ipath_get_unit_name(unit), ##__VA_ARGS__) + +#define _IPATH_ERROR(fmt,...) printk(KERN_ERR "infinipath: " fmt, ##__VA_ARGS__) + +#define _IPATH_INFO(fmt,...) \ + do { \ + if(unlikely(infinipath_debug & __IPATH_INFO)) \ + printk(KERN_INFO "infinipath: " fmt, ##__VA_ARGS__); \ + } while(0) +
[snip, snip]
Please consider using dev_dbg, dev_err, et al from <linux/device.h>.
Pekka
Re: [PATCH 5 of 20] ipath - driver core header files
In reply to Pekka EnbergQuoted 3 lines · Pekka Enberg
Quoted 1 line
+void
ipath_dwordcpy(uint32_t *dest, uint32_t *src, uint32_t ndwords);Wasn't this supposed to be killed?
The routine itself is dead, but the prototype survived. Thanks for spotting that.
<b
Re: [openib-general] [PATCH 11 of 20] ipath - core driver, part 4 of 4
In reply to Roland DreierQuoted 2 lines · Roland Dreier
This seems very wrong to me: there's no guarantee that a module will be loaded into memory that can be used as a DMA target.
Right. I think we're just getting lucky on x86_64. I'll fix this.
Thanks,
<b
Signature · 1 line
Bryan O'Sullivan <[email protected]>
Re: [PATCH 14 of 20] ipath - infiniband verbs header
In reply to Pekka EnbergQuoted 1 line · Pekka Enberg
Please consider using
dev_dbg,dev_err, and friends from <linux/device.h>.
Will do, thanks.
<b
Quoted 3 lines · Bryan O'Sullivan
Following Roland's submission of our InfiniPath InfiniBand HCA driver earlier this month, we have responded to people's comments by making a large number of changes to the driver.
Many thanks!
Quoted 7 lines
Here is another set of driver patches for review. Roland is on vacation until January 4, so I'm posting these in his place. Once again, your comments are appreciated. We'd like to submit this driver for inclusion in 2.6.16, so we'll be responding quickly to all feedback.
A short summary of the changes we have made is as follows:
Some comments, just based on this:
[...]
Quoted 2 lines
- Renamed
_BITS_PER_BYTEtoBITS_PER_BYTE, and moved it intolinux/types.h
Haven't come across anything with this not 8 for a /long/ time now, and no Linux on that in sight.
[...]
Quoted 4 lines
There are a few requested changes we have chosen to omit for now:
- The driver still uses
EXPORT_SYMBOL, for consistency with other code indrivers/infiniband
I'd suppose that is your choice...
Quoted 3 lines
- Someone asked for the kernel's i2c infrastructure to be used, but our i2c usage is very specialised, and it would be more of a mess to use the kernel's
Problem with that is that if everybody and Aunt Tillie does the same, the kernel as a whole gets to be a mess.
Quoted 2 lines
- We're still using ioctls instead of sysfs or configfs in some cases, to maintain userspace compatibility
With what? You can very well ask people to upgrade to the latest userland utilities, and even make them run the old versions when they find that the new interface isn't there. Happened recently with modprobe/modutils.
Signature · 4 lines
Dr. Horst H. von Brand User #22616 counter.li.org Departamento de Informatica Fono: +56 32 654431 Universidad Tecnica Federico Santa Maria +56 32 654239 Casilla 110-V, Valparaiso, Chile Fax: +56 32 797513
Quoted 6 lines · Horst von Brand
Quoted 3 lines
- Someone asked for the kernel's i2c infrastructure to be used,but our i2c usage is very specialised, and it would be more of a mess to use the kernel's
Problem with that is that if everybody and Aunt Tillie does the same, the kernel as a whole gets to be a mess.
ALSA does the exact same thing for the exact same reason. Maybe an indication that the kernel's i2c layer is too heavy?
Lee
Re: [PATCH 17 of 20] ipath - infiniband verbs support, part 3 of 3
In reply to Bryan O'SullivanHi,
[Copy-paste reuse alert!]
Quoted 15 lines · Bryan O'Sullivan
drivers/infiniband/hw/ipath/ipath_verbs.c +15
-0+static struct ib_mr *ipath_reg_phys_mr (struct ib_pd *pd, +struct ib_phys_buf *buffer_list, +int num_phys_buf, +int acc,u64 *iova_start) +{ +struct ipath_mr *mr; +int n, m, i; + +/* Allocate struct plus pointers to first level page tables. */ + m = (num_phys_buf +IPATH_SEGSZ -1 ) /IPATH_SEGSZ ; + mr =kmalloc (sizeof *mr + m *sizeof mr->mr.map[0 ],GFP_KERNEL ); +if (!mr) +return ERR_PTR (-ENOMEM ); + +/* Allocate first level page tables. */
drivers/infiniband/hw/ipath/ipath_verbs.c line 5628
Quoted 10 lines
drivers/infiniband/hw/ipath/ipath_verbs.c +10
-0+for (i =0 ; i < m; i++) { + mr->mr.map[i] =kmalloc (sizeof *mr->mr.map[0 ],GFP_KERNEL ); +if (!mr->mr.map[i]) { +while (i) +kfree (mr->mr.map[--i]); +kfree (mr); +return ERR_PTR (-ENOMEM ); + } + } + mr->mr.mapsz = m;
[snip, snip]
Quoted 20 lines
drivers/infiniband/hw/ipath/ipath_verbs.c +20
-0+static struct ib_mr *ipath_reg_user_mr (struct ib_pd *pd, +struct ib_umem *region, +int mr_access_flags, +struct ib_udata *udata) +{ +struct ipath_mr *mr; +struct ib_umem_chunk *chunk; +int n, m, i; + + n =0 ; +list_for_each_entry (chunk, ®ion->chunk_list, list) + n += chunk->nents; + +/* Allocate struct plus pointers to first level page tables. */ + m = (n +IPATH_SEGSZ -1 ) /IPATH_SEGSZ ; + mr =kmalloc (sizeof *mr + m *sizeof mr->mr.map[0 ],GFP_KERNEL ); +if (!mr) +return ERR_PTR (-ENOMEM ); + +/* Allocate first level page tables. */
drivers/infiniband/hw/ipath/ipath_verbs.c line 5691
Quoted 10 lines
drivers/infiniband/hw/ipath/ipath_verbs.c +10
-0+for (i =0 ; i < m; i++) { + mr->mr.map[i] =kmalloc (sizeof *mr->mr.map[0 ],GFP_KERNEL ); +if (!mr->mr.map[i]) { +while (i) +kfree (mr->mr.map[--i]); +kfree (mr); +return ERR_PTR (-ENOMEM ); + } + } + mr->mr.mapsz = m;
[snip, more duplicate code]
The above fragment is repeated at least three times. Please factor out the common code into separate functions.
Pekka
Quoted 5 lines · Horst von Brand
Quoted 2 lines
- Renamed
_BITS_PER_BYTEtoBITS_PER_BYTE, and moved it intolinux/types.hHaven't come across anything with this not 8 for a /long/ time now, and no Linux on that in sight.
The point isn't that it might change, but that it makes code clearer to use BITS_PER_BYTE in arithmetic than to have the magic number 8 sprinkled around mysteriously.
<b
Re: [PATCH 17 of 20] ipath - infiniband verbs support, part 3 of 3
In reply to Pekka EnbergQuoted 1 line · Pekka Enberg
[Copy-paste reuse alert!]
Yep, thanks for pointing that out. The source file in question is about to go on a serious diet :-)
<b
Re: [PATCH 12 of 20] ipath - misc driver support code
In reply to Bryan O'SullivanQuoted 1 line · Bryan O'Sullivan
Signed-off-by Bryan O'Sullivan <[email protected]>
No description of what the patch does?
drivers/infiniband/hw/ipath/ipath_ht400.c line 60
Quoted 2 lines
drivers/infiniband/hw/ipath/ipath_ht400.c new file +2
-0+struct _infinipath_do_not_use_kernel_regs { +unsigned long long Revision;
u64?
drivers/infiniband/hw/ipath/ipath_ht400.c line 63
Quoted 3 lines
drivers/infiniband/hw/ipath/ipath_ht400.c new file +3
-0+unsigned long long Control; +unsigned long long PageAlign; +unsigned long long PortCnt;
And what's with the InterCapsNamingScheme of these variables?
drivers/infiniband/hw/ipath/ipath_i2c.c line 168
Quoted 6 lines
drivers/infiniband/hw/ipath/ipath_i2c.c new file +6
-0+/* +* would prefer to not inline this, to avoid code bloat, and simplify debugging +* But when compiling against 2.6.10 kernel tree, it gets an error, so +* not for now. +*/ +static void ipath_i2c_delay (ipath_type ,int );
You aren't compiling this for a 2.6.10 kernel anymore :)
Quoted 11 lines
drivers/infiniband/hw/ipath/ipath_i2c.c new file +11
-0+/* +* we use this instead of udelay directly, so we can make sure +* that previous register writes have been flushed all the way +* to the chip. Since we are delaying anyway, the cost doesn't +* hurt, and makes the bit twiddling more regular +* If delay is negative, we'll do the chip read, to be sure write made it +* to our chip, but won't do udelay() +*/ +static void ipath_i2c_delay (ipath_type dev,int dtime) +{ +/*
drivers/infiniband/hw/ipath/ipath_i2c.c line 190
Quoted 10 lines
drivers/infiniband/hw/ipath/ipath_i2c.c new file +10
-0+* This needs to be volatile, so that the compiler doesn't +* optimize away the read to the device's mapped memory. +*/ +volatile uint32_t read_val; +if (!dtime) +return ; + read_val =ipath_kget_kreg32 (dev, kr_scratch); +if (--dtime >0 )/* register read takes about .5 usec, itself */ +udelay (dtime); +}
Huh? After reading your comment, I still don't understand why you can't just use udelay(). Or are you counting on calling this function with only "1" being set for dtime?
Ah, in looking at your code, that is exactly what is happening. That's a mess, just delay and everything will work properly on the next rev of the hardware where the time to read that register will have dropped to 1/8 the time it does today...
Quoted 9 lines
drivers/infiniband/hw/ipath/ipath_i2c.c new file +9
-0+/* +* write a byte, one bit at a time. Returns 0 if we got the following +* ack, otherwise 1 +*/ +static int ipath_wr_byte (ipath_type dev,uint8_t data) +{ +int bit_cntr; +uint8_t bit; +
drivers/infiniband/hw/ipath/ipath_i2c.c line 250
Quoted 10 lines
drivers/infiniband/hw/ipath/ipath_i2c.c new file +10
-0+for (bit_cntr =7 ; bit_cntr >=0 ; bit_cntr--) { + bit = (data >> bit_cntr) &1 ; +ipath_sda_out (dev, bit,1 ); +ipath_scl_out (dev, i2c_line_high,1 ); +ipath_scl_out (dev, i2c_line_low,1 ); + } +if (!ipath_i2c_ackrcv (dev)) +return 1 ; +return 0 ; +}
Ah, isn't it fun to write bit-banging functions... And the in-kernel i2c code is messier than doing this by hand?
drivers/infiniband/hw/ipath/ipath_i2c.c line 342
Quoted 9 lines
drivers/infiniband/hw/ipath/ipath_i2c.c new file +9
-0+/* +* ipath_eeprom_read - Receives x # byte from the eeprom via I2C. +* +* eeprom: Atmel AT24C01 +* +*/ + +int ipath_eeprom_read (ipath_type dev,uint8_t eeprom_offset,void *buffer, +int len)
Odd function comment style. Please fix this to be in kerneldoc format.
Quoted 48 lines
diff -r
e8af3873b0d9-r5e9b0b7876e2drivers/infiniband/hw/ipath/ipath_lib.cdrivers/infiniband/hw/ipath/ipath_lib.c new file +44
-0@@ -0,0 +1,90 @@ +/* +* Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved. +* +* This software is available to you under a choice of one of two +* licenses. You may choose to be licensed under the terms of the GNU +* General Public License (GPL) Version 2, available from the file +* COPYING in the main directory of this source tree, or the +* OpenIB.org BSD license below: +* +* 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. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +* +* Patent licenses, if any, provided herein do not apply to +* combinations of this program with other software, or any other +* product whatsoever. +*/ + +/* +* This is library code for the driver, similar to what's in libinfinipath for +* usermode code. +*/ + +#include <linux/config.h> +#include <linux/version.h> +#include <linux/kernel.h>
drivers/infiniband/hw/ipath/ipath_lib.c line 54
Quoted 10 lines
drivers/infiniband/hw/ipath/ipath_lib.c new file +10
-0+#include <linux/errno.h> +#include <linux/types.h> +#include <linux/string.h> +#include <linux/delay.h> +#include <linux/timer.h> +#include <linux/fs.h> +#include <linux/poll.h> +#include <asm/io.h> +#include <asm/byteorder.h> +#include <asm/uaccess.h>
Are you _sure_ you need all of these for the one function in this file?
Quoted 26 lines
drivers/infiniband/hw/ipath/ipath_lib.c new file +26
-0+ +#include "ipath_kernel.h" + +unsigned infinipath_debug = __IPATH_INFO; + +uint32_t _ipath_pico_per_cycle;/* always present, for now */ + +/* +* This isn't perfect, but it's close enough for timing work. We want this +* to work on systems where the cycle counter isn't the same as the clock +* frequency. The one msec spin is OK, since we execute this only once +* when first loaded. We don't use CURRENT_TIME because on some systems +* it only has jiffy resolution; we just assume udelay is well calibrated +* and that we aren't likely to be rescheduled. Do it multiple times, +* with a yield in between, to try to make sure we get the "true minimum" +* value. +* _ipath_pico_per_cycle isn't going to lead to completely accurate +* conversions from timestamps to nanoseconds, but it's close enough +* for our purposes, which is mainly to allow people to show events with +* nsecs or usecs if desired, rather than cycles. +*/ +void ipath_init_picotime (void ) +{ +int i; +u_int64_t ts, te, delta =-1ULL ; +
drivers/infiniband/hw/ipath/ipath_lib.c line 90
Quoted 10 lines
drivers/infiniband/hw/ipath/ipath_lib.c new file +10
-0+for (i =0 ; i <5 ; i++) { + ts =get_cycles (); +udelay (250 ); + te =get_cycles (); +if ((te - ts) < delta) + delta = te - ts; +yield (); + } + _ipath_pico_per_cycle =250000000 / delta; +}
Ick. A whole file for one function and 2 public variables? And a horrible timing function too? Please just use the core kernel timing functions, which will work all the time on all arches...
Quoted 31 lines
diff -r
e8af3873b0d9-r5e9b0b7876e2drivers/infiniband/hw/ipath/ipath_upages.cdrivers/infiniband/hw/ipath/ipath_upages.c new file +27
-0@@ -0,0 +1,144 @@ +/* +* Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved. +* +* This software is available to you under a choice of one of two +* licenses. You may choose to be licensed under the terms of the GNU +* General Public License (GPL) Version 2, available from the file +* COPYING in the main directory of this source tree, or the +* OpenIB.org BSD license below: +* +* 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. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
drivers/infiniband/hw/ipath/ipath_upages.c line 37
Quoted 10 lines
drivers/infiniband/hw/ipath/ipath_upages.c new file +10
-0+* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +* +* Patent licenses, if any, provided herein do not apply to +* combinations of this program with other software, or any other +* product whatsoever. +*/ + +#include <stddef.h>
Where is this file being pulled in from?
Quoted 9 lines
drivers/infiniband/hw/ipath/ipath_upages.c new file +9
-0+ +#include <linux/config.h> +#include <linux/version.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/string.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/mm.h>
drivers/infiniband/hw/ipath/ipath_upages.c
Quoted 10 lines
drivers/infiniband/hw/ipath/ipath_upages.c new file +10
-0+#include <linux/spinlock.h> + +#include <asm/page.h> +#include <asm/io.h> + +#include "ipath_kernel.h" + +/* +* Our version of the kernel mlock function. This function is no longer +* exposed, so we need to do it ourselves.
Woah, um, don't you think that you should either export the main mlock function itself, or fix your code to not need it? Rolling it yourself isn't a good idea...
thanks,
greg k-h
Quoted 5 lines · Bryan O'Sullivan
There are a few requested changes we have chosen to omit for now:
- The driver still uses
EXPORT_SYMBOL, for consistency with other code indrivers/infiniband
Why would that matter?
Quoted 3 lines
- Someone asked for the kernel's i2c infrastructure to be used, but our i2c usage is very specialised, and it would be more of a mess to use the kernel's
Why is this? What is so messy about the in-kernel i2c interfaces? (yeah, I know that there are some oddities, just want to know what you specifically are not liking...)
Quoted 2 lines
- We're still using ioctls instead of sysfs or configfs in some cases, to maintain userspace compatibility
Compatibility with what? The driver isn't in the kernel tree yet, so there's no old kernel versions to remain compatibile with :)
I also noticed that you are still using the uint64_t type variable types, can you please switch to the proper kernel types instead (u64 in this specific example.)
thanks,
greg k-h
Re: [PATCH 12 of 20] ipath - misc driver support code
In reply to Bryan O'SullivanQuoted 14 lines
drivers/infiniband/hw/ipath/ipath_upages.c new file +14
-0+int ipath_get_upages_nocopy (unsigned long start_page,struct page **p) +{ +int n; +struct vm_area_struct *vm =NULL ; + +down_read (¤t->mm->mmap_sem); + n =get_user_pages (current, current->mm, start_page,1 ,1 ,1 , p, &vm); +up_read (¤t->mm->mmap_sem); +if (n !=1 ) { +_IPATH_INFO ("get_user_pages for 0x%lx failed with %d\n" , + start_page, n); +if (n <0 )/* it's an errno */ +return n; +/*
drivers/infiniband/hw/ipath/ipath_upages.c line 120
Quoted 10 lines
drivers/infiniband/hw/ipath/ipath_upages.c new file +10
-0+* If we ever ask for more than a single page, we will have to +* free the pages (if any) that we did get, via ipath_get_upages() +* or put_page() directly. +*/ +return -ENOMEM ;/* no way to know actual error */ + } + vm->vm_flags |=VM_SHM |VM_LOCKED ; + +return 0 ; +}
I hope you're not depending on the VM_LOCKED thing.. since the user can just undo that easily!
(this is also why all this "sys_mlock from the driver" is traditionally buggy to the point of being a roothole, things like some of the binary 3D drivers have had this security hole for a long time, as did some of the early infiniband drivers)
Re: [PATCH 10 of 20] ipath - core driver, part 3 of 4
In reply to Bryan O'SullivanAll your user page lookup/pinning code is terminally broken.
You can't do it that way. You have serveral major conceptual bugs, like keeping track of pages without incrementing their page count, and just expecting that they are magically "pinned" even you do nothing at all to pin them. The process exits or does an munmap, and the page will be used for something else, and you'll just corrupt totally random memory.
Similarly, you do page_address() on the page, which just can't work on highmem pages.
Crap like this must not be merged. Drivers aren't supposed to play VM tricks in the first place - even if they were to get it right (which they never do). Don't do it.
Linus
Re: [PATCH 11 of 20] ipath - core driver, part 4 of 4
In reply to Bryan O'Sullivandrivers/infiniband/hw/ipath/ipath_driver.c
Quoted 6 lines · Bryan O'Sullivan
Signed-off-by Bryan O'Sullivan <[email protected]>diff -r
c37b118ef806-re8af3873b0d9drivers/infiniband/hw/ipath/ipath_driver.cdrivers/infiniband/hw/ipath/ipath_driver.c +0
-0@@ -5408,3 +5408,1709 @@
Clever use of 4 patches to just add onto the same file. This has grown into a huge file, can't you split it up into smaller pieces?
Quoted 20 lines
drivers/infiniband/hw/ipath/ipath_driver.c +20
-0+int __init infinipath_init (void ) +{ +int r =0 , i; + + _IPATH_DBG(KERN_INFO DRIVER_LOAD_MSG "%s", ipath_core_version); + +ipath_init_picotime ();/* init cycles -> pico conversion */ + +/* +* initialize the statusp to temporary storage so we can use it +* everywhere without first checking. When we "really" assign it, +* we copy from _ipath_status +*/ +for (i =0 ; i < infinipath_max; i++) + devdata[i].ipath_statusp = &devdata[i]._ipath_status; + +/* +* init these early, in case we take an interrupt as soon as the irq +* is setup. Saw a spinlock panic once that appeared to be due to that +* problem, when they were initted later on.
drivers/infiniband/hw/ipath/ipath_driver.c line 6939
Quoted 10 lines
drivers/infiniband/hw/ipath/ipath_driver.c +10
-0+*/ +spin_lock_init (&ipath_pioavail_lock); +spin_lock_init (&ipath_sma_lock); + +pci_register_driver (&infinipath_driver); + +driver_create_file (&(infinipath_driver.driver), &driver_attr_version); + +if ((r =register_chrdev (ipath_major,MODNAME , &ipath_fops))) +_IPATH_ERROR ("Unable to register %s device\n" ,MODNAME );
Why even save off the return value if you don't do anything with it?
And please don't put assignments in the middle of if statements, that's just messy and harder to read (the fact that gcc made you put an extra () should be a hint that you were doing something wrong...)
And does your driver work with udev? I didn't see where you were exporting the major:minor number of your devices to sysfs, but I might have missed it.
drivers/infiniband/hw/ipath/ipath_driver.c line 6948
Quoted 7 lines
drivers/infiniband/hw/ipath/ipath_driver.c +7
-0+/* +* never return an error, since we could have stuff registered, +* resources used, etc., even if no hardware found. This way we +* can clean up through unload. +*/ +return 0 ; +}
Are you sure that's a good idea? Please do the proper thing and tear down your infrastructure if something fails, that's the correct thing to do. That way you can actually recover if something that you call in this function fails (like driver_create_file(), or pci_register_driver().) Functions don't return error values just so you can ignore them :)
drivers/infiniband/hw/ipath/ipath_driver.c line 6953
Quoted 4 lines
drivers/infiniband/hw/ipath/ipath_driver.c +4
-0+/* +* note: if for some reason the unload fails after this routine, and leaves +* the driver enterable by user code, we'll almost certainly crash and burn... +*/
See, you admit that what you are doing isn't the wisest thing, which should tell you something...
Quoted 122 lines
drivers/infiniband/hw/ipath/ipath_driver.c +122
-0+static void __exit infinipath_cleanup(void) +{ +int r, m, port; + +driver_remove_file (&(infinipath_driver.driver), &driver_attr_version); +if ((r =unregister_chrdev (ipath_major,MODNAME ))) +_IPATH_DBG ("unregister of device failed: %d\n" , r); + + +/* +* turn off rcv, send, and interrupts for all ports, all drivers +* should also hard reset the chip here? +* free up port 0 (kernel) rcvhdr, egr bufs, and eventually tid bufs +* for all versions of the driver, if they were allocated +*/ +for (m =0 ; m < infinipath_max; m++) { +uint64_t val; +struct ipath_devdata *dd = &devdata[m]; +if (dd->ipath_kregbase) { +/* in case unload fails, be consistent */ + dd->ipath_rcvctrl =0U ; +ipath_kput_kreg (m, kr_rcvctrl, dd->ipath_rcvctrl); + +/* +* gracefully stop all sends allowing any in +* progress to trickle out first. +*/ +ipath_kput_kreg (m, kr_sendctrl,0ULL ); + val =ipath_kget_kreg64 (m, kr_scratch);/* flush it */ +/* +* enough for anything that's going to trickle +* out to have actually done so. +*/ +udelay (5 ); + +/* +* abort any armed or launched PIO buffers that +* didn't go. (self clearing). Will cause any +* packet currently being transmitted to go out +* with an EBP, and may also cause a short packet +* error on the receiver. +*/ +ipath_kput_kreg (m, kr_sendctrl,INFINIPATH_S_ABORT ); + +/* mask interrupts, but not errors */ +ipath_kput_kreg (m, kr_intmask,0ULL ); +ipath_shutdown_link (m); + +/* +* clear all interrupts and errors. Next time +* driver is loaded, we know that whatever is +* set happened while we were unloaded +*/ +ipath_kput_kreg (m, kr_hwerrclear,-1LL ); +ipath_kput_kreg (m, kr_errorclear,-1LL ); +ipath_kput_kreg (m, kr_intclear,-1LL ); +if (dd->__ipath_pioavailregs_base) { +kfree ((void *)dd->__ipath_pioavailregs_base); + dd->__ipath_pioavailregs_base =NULL ; + dd->ipath_pioavailregs_dma =NULL ; + } + +if (dd->ipath_pageshadow) { +struct page **tmpp = dd->ipath_pageshadow; +int i, cnt =0 ; + +_IPATH_VDBG + ("Unlocking any expTID pages still locked\n" ); +for (port =0 ; port < dd->ipath_cfgports; + port++) { +int port_tidbase = + port * dd->ipath_rcvtidcnt; +int maxtid = + port_tidbase + dd->ipath_rcvtidcnt; +for (i = port_tidbase; i < maxtid; i++) { +if (tmpp[i]) { +ipath_putpages (1 , + &tmpp[i]); + tmpp[i] =NULL ; + cnt++; + } + } + } +if (cnt) { + ipath_stats.sps_pageunlocks += cnt; +_IPATH_VDBG + ("There were still %u expTID entries locked\n" , + cnt); + } +if (ipath_stats.sps_pagelocks + || ipath_stats.sps_pageunlocks) +_IPATH_VDBG + ("%llu pages locked, %llu unlocked via ipath_m{un}lock\n" , + ipath_stats.sps_pagelocks, + ipath_stats.sps_pageunlocks); + +_IPATH_VDBG + ("Free shadow page tid array at %p\n" , + dd->ipath_pageshadow); +vfree (dd->ipath_pageshadow); + dd->ipath_pageshadow =NULL ; + } + +/* +* free any resources still in use (usually just +* kernel ports) at unload +*/ +for (port =0 ; port < dd->ipath_cfgports; port++) +ipath_free_pddata (dd, port,1 ); +kfree (dd->ipath_pd); +/* +* debuggability, in case some cleanup path +* tries to use it after this +*/ + dd->ipath_pd =NULL ; + } + +if (dd->pcidev) { +if (dd->pcidev->irq) { +_IPATH_VDBG ("unit %u free_irq of irq %x\n" , m, + dd->pcidev->irq); +free_irq (dd->pcidev->irq, dd);
drivers/infiniband/hw/ipath/ipath_driver.c line 7085
Quoted 10 lines
drivers/infiniband/hw/ipath/ipath_driver.c +10
-0+ }else +_IPATH_DBG + ("irq is 0, not doing free_irq for unit %u\n" , + m); + dd->pcidev =NULL ; + } +if (dd->pci_registered) { +_IPATH_VDBG + ("Unregistering pci infrastructure unit %u\n" , m); +pci_unregister_driver (&infinipath_driver);
This is the call that should have cleaned up all of the memory and other stuff that you do above. If not, then your driver will not work in any hotplug pci systems, which would not be a good thing. Please do like Roland says and put your resources and stuff in the device specific structures, like the rest of the kernel drivers do. You know, we do things like this for a reason, not just because we like to be difficult :)
Quoted 14 lines
drivers/infiniband/hw/ipath/ipath_driver.c +14
-0+ dd->pci_registered =0 ; + }else +_IPATH_VDBG + ("unit %u: no pci unreg, wasn't registered\n" , m); +ipath_chip_cleanup (dd);/* clean up any per-chip chip-specific stuff */ + } +/* +* clean up any chip-specific stuff for now, only one type of chip +* for any given driver +*/ +ipath_chip_done (); + +/* cleanup all our locked pages private data structures */ +ipath_upages_cleanup (NULL );
drivers/infiniband/hw/ipath/ipath_driver.c line 7109
Quoted 10 lines
drivers/infiniband/hw/ipath/ipath_driver.c +10
-0+} + +/* This is a generic function here, so it can return device-specific +* info. This allows keeping in sync with the version that supports +* multiple chip types. +*/ +void ipath_get_boardname (const ipath_type t,char *name,size_t namelen) +{ +ipath_ht_get_boardname (t, name, namelen); +}
Why not just export ipath_ht_get_boardname instead?
drivers/infiniband/hw/ipath/ipath_driver.c line 7115
Quoted 5 lines
drivers/infiniband/hw/ipath/ipath_driver.c +5
-0+module_init (infinipath_init); +module_exit (infinipath_cleanup); + +EXPORT_SYMBOL (infinipath_debug); +EXPORT_SYMBOL (ipath_get_boardname);
EXPORT_SYMBOL_GPL() ?
And put them next to the functions themselves, it's easier to notice that way.
thanks,
greg k-h
Re: [PATCH 8 of 20] ipath - core driver, part 1 of 4
In reply to Bryan O'SullivanQuoted 192 lines · Bryan O'Sullivan
Signed-off-by Bryan O'Sullivan <[email protected]>diff -r
ffbd416f30d4-rddd21709e12cdrivers/infiniband/hw/ipath/ipath_driver.cdrivers/infiniband/hw/ipath/ipath_driver.c new file +186
-0@@ -0,0 +1,1879 @@ +/* +* Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved. +* +* This software is available to you under a choice of one of two +* licenses. You may choose to be licensed under the terms of the GNU +* General Public License (GPL) Version 2, available from the file +* COPYING in the main directory of this source tree, or the +* OpenIB.org BSD license below: +* +* 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. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +* +* Patent licenses, if any, provided herein do not apply to +* combinations of this program with other software, or any other +* product whatsoever. +*/ + +#include <linux/version.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/swap.h> +#include <asm/mtrr.h> +#include <linux/netdevice.h> + +#include <linux/crc32.h> /* we can generate our own crc's for testing */ + +#include "ipath_kernel.h" +#include "ips_common.h" +#include "ipath_layer.h" + +/* +* Our LSB-assigned major number, so scripts can figure +* out how to make entry in /dev. +*/ + +static int ipath_major =233 ; + +/* +* number of buffers reserved for driver (layered drivers and SMA send). +* Reserved at end of buffer list. +*/ + +static uint infinipath_kpiobufs =32 ; + +/* +* number of ports we are configured to use (to allow for more pio +* buffers per port, etc.) Zero means use chip value. +*/ + +static uint infinipath_cfgports; + +/* +* number of units we are configured to use (to allow for bringup on +* multi-chip systems) Zero means use only one for now, but eventually +* will mean to use infinipath_max +*/ + +static uint infinipath_cfgunits; + +uint64_t ipath_dummy_val_for_testing; + +static __kernel_pid_t ipath_sma_alive;/* PID of SMA, if it's running */ +static spinlock_t ipath_sma_lock;/* SMA receive */ + +/* max SM received packets we'll queue; we keep the most recent packets. */ + +#define IPATH_NUM_SMAPKTS 16 + +#define IPATH_SMA_HDRSZ (8+12+8)/* LRH+BTH+DETH */ + +static struct _ipath_sma_rpkt { +/* length of received packet; non-zero if queued */ +uint32_t len; +/* unit number of interface packet was received from */ +uint32_t unit; +uint8_t *buf; +} ipath_sma_data[IPATH_NUM_SMAPKTS ]; + +static unsigned ipath_sma_first;/* oldest sma packet index */ +static unsigned ipath_sma_next;/* next sma packet index to use */ + +/* +* ipath_sma_data_bufs has one extra, pointed to by ipath_sma_data_spare, +* so we can exchange buffers to do copy_to_user, and not hold the lock +* across the copy_to_user(). +*/ + +#define SMA_MAX_PKTSZ (IPATH_SMA_HDRSZ+256)/* max len of an SMA packet */ + +static uint8_t ipath_sma_data_bufs[IPATH_NUM_SMAPKTS +1 ][SMA_MAX_PKTSZ ]; +static uint8_t *ipath_sma_data_spare; +/* sma waits globally on all units */ +static wait_queue_head_t ipath_sma_wait; +static wait_queue_head_t ipath_sma_state_wait; + +struct infinipath_stats ipath_stats; + +/* +* this will only be used for diags, now that we have enabled the DMA +* of the sendpioavail regs to system memory. +*/ + +static inline uint64_t ipath_kget_sreg (const ipath_type stype, +ipath_sreg regno) +{ +uint64_t val; +uint64_t *sbase; + + sbase = (uint64_t *) (devdata[stype].ipath_sregbase + + (char *)devdata[stype].ipath_kregbase); + val = sbase ? sbase[regno] :0ULL ; +return val; +} + +static int ipath_do_user_init (struct ipath_portdata *, +struct ipath_user_info __user *); +static int ipath_get_baseinfo (struct ipath_portdata *, +struct ipath_base_info __user *); +static int ipath_get_units (void ); +static int ipath_wr_eeprom (struct ipath_portdata *, +struct ipath_eeprom_req __user *); +static int ipath_wait_intr (struct ipath_portdata *,uint32_t ); +static int ipath_tid_update (struct ipath_portdata *,struct _tidupd __user *); +static int ipath_tid_free (struct ipath_portdata *,struct _tidupd __user *); +static int ipath_get_counters (ipath_type ,struct infinipath_counters __user *); +static int ipath_get_unit_counters (struct infinipath_getunitcounters __user *a); +static int ipath_get_stats (struct infinipath_stats __user *); +static int ipath_set_partkey (struct ipath_portdata *,uint16_t ); +static int ipath_manage_rcvq (struct ipath_portdata *,uint16_t ); +static void ipath_clean_partkey (struct ipath_portdata *, +struct ipath_devdata *); +static void ipath_disarm_piobufs (const ipath_type ,unsigned ,unsigned ); +static int ipath_create_user_egr (struct ipath_portdata *); +static int ipath_create_port0_egr (struct ipath_portdata *); +static int ipath_create_rcvhdrq (struct ipath_portdata *); +static void ipath_handle_errors (const ipath_type ,uint64_t ); +static void ipath_update_pio_bufs (const ipath_type ); +static int ipath_shutdown_link (const ipath_type ); +static int ipath_bringup_link (const ipath_type ); +int ipath_bringup_serdes (const ipath_type ); +static void ipath_get_faststats (unsigned long ); +static int ipath_setup_htconfig (struct pci_dev *,uint64_t *,const ipath_type ); +static struct page *ipath_nopage (struct vm_area_struct *,unsigned long ,int *); +static irqreturn_t ipath_intr (int irq,void *devid,struct pt_regs *regs); +static void ipath_decode_err (char *,size_t ,uint64_t ); +void ipath_free_pddata (struct ipath_devdata *,uint32_t ,int ); +static void ipath_clear_tids (const ipath_type ,unsigned ); +static void ipath_get_guid (const ipath_type ); +static int ipath_sma_ioctl (struct file *,unsigned int ,unsigned long ); +static int ipath_rcvsma_pkt (struct ipath_sendpkt __user *); +static int ipath_kset_lid (uint32_t ); +static int ipath_kset_mlid (uint32_t ); +static int ipath_get_mlid (uint32_t __user *); +static int ipath_get_devstatus (uint64_t __user *); +static int ipath_kset_guid (struct ipath_setguid __user *); +static int ipath_get_portinfo (uint32_t __user *); +static int ipath_get_nodeinfo (uint32_t __user *); +#ifdef _IPATH_EXTRA_DEBUG +static void ipath_dump_allregs (char *,ipath_type ); +#endif + +static const char ipath_sma_name[] ="infinipath_SMA" ; + +/* +* is diags mode enabled? if it is, then things like auto bringup of +* links is disabled +*/
drivers/infiniband/hw/ipath/ipath_driver.c line 196
Quoted 10 lines
drivers/infiniband/hw/ipath/ipath_driver.c new file +10
-0+ +int ipath_diags_enabled =0 ; + +void ipath_chip_done (void ) +{ +} + +void ipath_chip_cleanup (struct ipath_devdata * dd) +{ +}
What are these two empty functions for?
Quoted 129 lines
drivers/infiniband/hw/ipath/ipath_driver.c new file +129
-0+/* +* cache aligned location +* +* where port 0 rcvhdrtail register is written back; also want +* nothing else sharing the cache line, so make it a cache line in size +* used for all units +* +* This is volatile as it's the target of a DMA from the chip. +*/ + +static volatile uint64_t ipath_port0_rcvhdrtail[512] + __attribute__ ((aligned(4096))); + +#define MODNAME "ipath_core" +#define DRIVER_LOAD_MSG "PathScale " MODNAME " loaded: " +#define PFX MODNAME ": " + +/* +* min buffers we want to have per port, after driver +*/ + +#define IPATH_MIN_USER_PORT_BUFCNT 8 + +/* The size has to be longer than this string, so we can +* append board/chip information to it in the init code. +*/ +static char ipath_core_version[192 ] =IPATH_IDSTR ; +static char *chip_driver_version; +static int chip_driver_size; + +/* mylid and lidbase are to deal with LIDs in "fabric", until SM is working */ + +module_param (infinipath_debug, uint,0644 ); +module_param (infinipath_kpiobufs, uint,0644 ); +module_param (infinipath_cfgports, uint,0644 ); +module_param (infinipath_cfgunits, uint,0644 ); + +MODULE_PARM_DESC (infinipath_debug,"mask for debug prints" ); +MODULE_PARM_DESC (infinipath_cfgports,"Set max number of ports to use" ); +MODULE_PARM_DESC (infinipath_cfgunits,"Set max number of devices to use" ); + +MODULE_LICENSE ("GPL" ); +MODULE_AUTHOR ("PathScale <[email protected]>" ); +MODULE_DESCRIPTION ("Pathscale InfiniPath driver" ); + +#ifdef IPATH_DIAG +static __kernel_pid_t ipath_diag_alive;/* PID of diags, if running */ +int ipath_diags_ioctl (struct file *,unsigned ,unsigned long ); +static int ipath_opendiag (struct inode *,struct file *); +#endif + +#if __IPATH_INFO || __IPATH_DBG +static const char *ipath_ibcstatus_str[] = { +"Disabled" , +"LinkUp" , +"PollActive" , +"PollQuiet" , +"SleepDelay" , +"SleepQuiet" , +"LState6" ,/* unused */ +"LState7" ,/* unused */ +"CfgDebounce" , +"CfgRcvfCfg" , +"CfgWaitRmt" , +"CfgIdle" , +"RecovRetrain" , +"LState0xD" ,/* unused */ +"RecovWaitRmt" , +"RecovIdle" , +}; +#endif + +static ssize_t show_version (struct device_driver *dev,char *buf) +{ +return snprintf (buf,PAGE_SIZE ,"%s" , ipath_core_version); +} + +static ssize_t show_status (struct device *dev, +struct device_attribute *attr, +char *buf) +{ +struct ipath_devdata *dd =dev_get_drvdata (dev); + +if (!dd) +return -EINVAL ; + +if (!dd->ipath_statusp) +return -EINVAL ; + +return snprintf (buf,PAGE_SIZE ,"%llx\n" , *(dd->ipath_statusp)); +} + +static const char *ipath_status_str[] = { +"Initted" , +"Disabled" , +"4" ,/* unused */ +"OIB_SMA" , +"SMA" , +"Present" , +"IB_link_up" , +"IB_configured" , +"NoIBcable" , +"Fatal_Hardware_Error" , +NULL , +}; + +static ssize_t show_status_str (struct device *dev, +struct device_attribute *attr, +char *buf) +{ +struct ipath_devdata *dd =dev_get_drvdata (dev); +int i, any; +uint64_t s; + +if (!dd) +return -EINVAL ; + +if (!dd->ipath_statusp) +return -EINVAL ; + + s = *(dd->ipath_statusp); + *buf ='\0' ; +for (any = i =0 ; s && ipath_status_str[i]; i++) { +if (s &1 ) { +if (any &&strlcat (buf," " ,PAGE_SIZE ) >=PAGE_SIZE ) +/* overflow */ +break ; +if (strlcat (buf, ipath_status_str[i], +PAGE_SIZE ) >=PAGE_SIZE )
drivers/infiniband/hw/ipath/ipath_driver.c line 336
Quoted 10 lines
drivers/infiniband/hw/ipath/ipath_driver.c new file +10
-0+break ; + any =1 ; + } + s >>=1 ; + } +if (any) +strlcat (buf,"\n" ,PAGE_SIZE ); + +return strlen (buf); +}
how big can this "status string" be? If it's even getting close to PAGE_SIZE, this doesn't need to be a sysfs attribute, but you should break it up into its individual pieces.
Based on the table above, this function can get much simpler...
Quoted 67 lines
drivers/infiniband/hw/ipath/ipath_driver.c new file +67
-0+ +static ssize_t show_lid (struct device *dev, +struct device_attribute *attr, +char *buf) +{ +struct ipath_devdata *dd =dev_get_drvdata (dev); + +if (!dd) +return -EINVAL ; + +return snprintf (buf,PAGE_SIZE ,"%x\n" , dd->ipath_lid); +} + +static ssize_t show_mlid (struct device *dev, +struct device_attribute *attr, +char *buf) +{ +struct ipath_devdata *dd =dev_get_drvdata (dev); + +if (!dd) +return -EINVAL ; + +return snprintf (buf,PAGE_SIZE ,"%x\n" , dd->ipath_mlid); +} + +static ssize_t show_guid (struct device *dev, +struct device_attribute *attr, +char *buf) +{ +struct ipath_devdata *dd =dev_get_drvdata (dev); +uint8_t *guid; + +if (!dd) +return -EINVAL ; + + guid = (uint8_t *)&(dd->ipath_guid); + +return snprintf (buf,PAGE_SIZE ,"%x:%x:%x:%x:%x:%x:%x:%x\n" , + guid[0 ], guid[1 ], guid[2 ], guid[3 ], guid[4 ], guid[5 ], + guid[6 ], guid[7 ]); +} + +static ssize_t show_nguid (struct device *dev, +struct device_attribute *attr, +char *buf) +{ +struct ipath_devdata *dd =dev_get_drvdata (dev); + +if (!dd) +return -EINVAL ; + +return snprintf (buf,PAGE_SIZE ,"%u\n" , dd->ipath_nguid); +} + +static ssize_t show_serial (struct device *dev, +struct device_attribute *attr, +char *buf) +{ +struct ipath_devdata *dd =dev_get_drvdata (dev); + +if (!dd) +return -EINVAL ; + + buf[sizeof dd->ipath_serial] ='\0' ; +memcpy (buf, dd->ipath_serial,sizeof dd->ipath_serial); +strcat (buf,"\n" ); +return strlen (buf);
drivers/infiniband/hw/ipath/ipath_driver.c line 413
Quoted 10 lines
drivers/infiniband/hw/ipath/ipath_driver.c new file +10
-0+} + +static ssize_t show_unit (struct device *dev, +struct device_attribute *attr, +char *buf) +{ +struct ipath_devdata *dd =dev_get_drvdata (dev); + +if (!dd) +return -EINVAL ;
Don't you mean -ENODEV?
drivers/infiniband/hw/ipath/ipath_driver.c line 416
Quoted 3 lines
drivers/infiniband/hw/ipath/ipath_driver.c new file +3
-0+ +snprintf (buf,PAGE_SIZE ,"%u\n" , dd->ipath_unit); +return strlen (buf);
return the snprintf() call instead of calling strlen() all the time please.
Quoted 251 lines
drivers/infiniband/hw/ipath/ipath_driver.c new file +251
-0+} + +static DRIVER_ATTR(version, S_IRUGO, show_version, NULL); +static DEVICE_ATTR(status, S_IRUGO, show_status, NULL); +static DEVICE_ATTR(status_str, S_IRUGO, show_status_str, NULL); +static DEVICE_ATTR(lid, S_IRUGO, show_lid, NULL); +static DEVICE_ATTR(mlid, S_IRUGO, show_mlid, NULL); +static DEVICE_ATTR(guid, S_IRUGO, show_guid, NULL); +static DEVICE_ATTR(nguid, S_IRUGO, show_nguid, NULL); +static DEVICE_ATTR(serial, S_IRUGO, show_serial, NULL); +static DEVICE_ATTR(unit, S_IRUGO, show_unit, NULL); + +/* +* called from add_timer and user counter read calls, to deal with +* counters that wrap in "human time". The words sent and received, and +* the packets sent and received are all that we worry about. For now, +* at least, we don't worry about error counters, because if they wrap +* that quickly, we probably don't care. We may eventually just make this +* handle all the counters. word counters can wrap in about 20 seconds +* of full bandwidth traffic, packet counters in a few hours. +*/ + +uint64_t ipath_snap_cntr (const ipath_type t,ipath_creg creg) +{ +uint32_t val; +uint64_t val64, t0, t1; +struct ipath_devdata *dd = &devdata[t]; +static uint64_t one_sec_in_cycles; +extern uint32_t _ipath_pico_per_cycle; + +if (!one_sec_in_cycles && _ipath_pico_per_cycle) + one_sec_in_cycles =1000000000000UL / _ipath_pico_per_cycle; + + t0 =get_cycles (); + val =ipath_kget_creg32 (t, creg); + t1 =get_cycles (); +if ((t1 - t0) > one_sec_in_cycles && val ==-1 ) { +/* +* This is just a way to detect things that are quite broken. +* Normally this should take just a few cycles (the check is +* for long enough that we don't care if we get pre-empted.) +* An Opteron HT O read timeout is 4 seconds with normal +* NB values +*/ + +_IPATH_UNIT_ERROR (t,"Error! Reading counter 0x%x timed out\n" , + creg); +return 0ULL ; + } + +if (creg == cr_wordsendcnt) { +if (val != dd->ipath_lastsword) { + dd->ipath_sword += val - dd->ipath_lastsword; + dd->ipath_lastsword = val; + } + val64 = dd->ipath_sword; + }else if (creg == cr_wordrcvcnt) { +if (val != dd->ipath_lastrword) { + dd->ipath_rword += val - dd->ipath_lastrword; + dd->ipath_lastrword = val; + } + val64 = dd->ipath_rword; + }else if (creg == cr_pktsendcnt) { +if (val != dd->ipath_lastspkts) { + dd->ipath_spkts += val - dd->ipath_lastspkts; + dd->ipath_lastspkts = val; + } + val64 = dd->ipath_spkts; + }else if (creg == cr_pktrcvcnt) { +if (val != dd->ipath_lastrpkts) { + dd->ipath_rpkts += val - dd->ipath_lastrpkts; + dd->ipath_lastrpkts = val; + } + val64 = dd->ipath_rpkts; + }else + val64 = (uint64_t ) val; + +return val64; +} + +/* +* print the delta of egrfull/hdrqfull errors for kernel ports no more +* than every 5 seconds. User processes are printed at close, but kernel +* doesn't close, so... Separate routine so may call from other places +* someday, and so function name when printed by _IPATH_INFO is meaningfull +*/ + +static void ipath_qcheck (const ipath_type t) +{ +static uint64_t last_tot_hdrqfull; +size_t blen =0 ; +struct ipath_devdata *dd = &devdata[t]; +char buf[128 ]; + + *buf =0 ; +if (dd->ipath_pd[0 ]->port_hdrqfull != dd->ipath_p0_hdrqfull) { + blen =snprintf (buf,sizeof buf,"port 0 hdrqfull %u" , + dd->ipath_pd[0 ]->port_hdrqfull - + dd->ipath_p0_hdrqfull); + dd->ipath_p0_hdrqfull = dd->ipath_pd[0 ]->port_hdrqfull; + } +if (ipath_stats.sps_etidfull != dd->ipath_last_tidfull) { + blen += +snprintf (buf + blen,sizeof buf - blen,"%srcvegrfull %llu" , + blen ?", " :"" , + ipath_stats.sps_etidfull - dd->ipath_last_tidfull); + dd->ipath_last_tidfull = ipath_stats.sps_etidfull; + } + +/* +* this is actually the number of hdrq full interrupts, not actual +* events, but at the moment that's mostly what I'm interested in. +* Actual count, etc. is in the counters, if needed. For production +* users this won't ordinarily be printed. +*/ + +if ((infinipath_debug & (__IPATH_PKTDBG | __IPATH_DBG)) && + ipath_stats.sps_hdrqfull != last_tot_hdrqfull) { + blen += +snprintf (buf + blen,sizeof buf - blen, +"%shdrqfull %llu (all ports)" , blen ?", " :"" , + ipath_stats.sps_hdrqfull - last_tot_hdrqfull); + last_tot_hdrqfull = ipath_stats.sps_hdrqfull; + } +if (blen) +_IPATH_DBG ("%s\n" , buf); + +if (*dd->ipath_hdrqtailptr != dd->ipath_port0head) { +if (dd->ipath_lastport0rcv_cnt == ipath_stats.sps_port0pkts) { +_IPATH_PDBG ("missing rcv interrupts? port0 hd=%llx tl=%x; port0pkts %llx\n" , + *dd->ipath_hdrqtailptr, dd->ipath_port0head,ipath_stats.sps_port0pkts); +ipath_kreceive (t); + } + dd->ipath_lastport0rcv_cnt = ipath_stats.sps_port0pkts; + } +} + +/* +* called from add_timer to get word counters from chip before they +* can overflow +*/ + +static void ipath_get_faststats (unsigned long t) +{ +uint32_t val; +struct ipath_devdata *dd = &devdata[t]; +static unsigned cnt; + +/* +* don't access the chip while running diags, or memory diags +* can fail +*/ +if (!dd->ipath_kregbase || !(dd->ipath_flags &IPATH_PRESENT ) || + ipath_diags_enabled) { +/* but re-arm the timer, for diags case; won't hurt other */ + goto done; + } + +ipath_snap_cntr ((ipath_type ) t, cr_wordsendcnt); +ipath_snap_cntr ((ipath_type ) t, cr_wordrcvcnt); +ipath_snap_cntr ((ipath_type ) t, cr_pktsendcnt); +ipath_snap_cntr ((ipath_type ) t, cr_pktrcvcnt); + +ipath_qcheck (t); + +/* +* deal with repeat error suppression. Doesn't really matter if +* last error was almost a full interval ago, or just a few usecs +* ago; still won't get more than 2 per interval. We may want +* longer intervals for this eventually, could do with mod, counter +* or separate timer. Also see code in ipath_handle_errors() and +* ipath_handle_hwerrors(). +*/ + +if (dd->ipath_lasterror) + dd->ipath_lasterror =0 ; +if (dd->ipath_lasthwerror) + dd->ipath_lasthwerror =0 ; +if ((devdata[t].ipath_maskederrs & ~devdata[t].ipath_ignorederrs) + &&get_cycles () > devdata[t].ipath_unmasktime) { +char ebuf[256 ]; +ipath_decode_err (ebuf,sizeof ebuf, + (devdata[t].ipath_maskederrs & ~devdata[t]. + ipath_ignorederrs)); +if ((devdata[t].ipath_maskederrs & ~devdata[t]. + ipath_ignorederrs) + & ~(INFINIPATH_E_RRCVEGRFULL |INFINIPATH_E_RRCVHDRFULL )) { +_IPATH_UNIT_ERROR (t,"Re-enabling masked errors (%s)\n" , + ebuf); + }else { +/* +* rcvegrfull and rcvhdrqfull are "normal", +* for some types of processes (mostly benchmarks) +* that send huge numbers of messages, while +* not processing them. So only complain about +* these at debug level. +*/ +_IPATH_DBG + ("Disabling frequent queue full errors (%s)\n" , + ebuf); + } + devdata[t].ipath_maskederrs = devdata[t].ipath_ignorederrs; +ipath_kput_kreg (t, kr_errormask, ~devdata[t].ipath_maskederrs); + } + +if (dd->ipath_flags &IPATH_LINK_SLEEPING ) { +uint64_t ibc; +_IPATH_VDBG ("linkinitcmd SLEEP, move to POLL\n" ); + dd->ipath_flags &= ~IPATH_LINK_SLEEPING ; + ibc = dd->ipath_ibcctrl; +/* +* don't put linkinitcmd in ipath_ibcctrl, want that to +* stay a NOP +*/ + ibc |= +INFINIPATH_IBCC_LINKINITCMD_POLL << +INFINIPATH_IBCC_LINKINITCMD_SHIFT ; +ipath_kput_kreg (t, kr_ibcctrl, ibc); + } + +/* limit qfull messages to ~one per minute per port */ +if ((++cnt &0x10 )) { +for (val = devdata[t].ipath_cfgports -1 ; ((int )val) >=0 ; + val--) { +if (dd->ipath_lastegrheads[val] !=-1 ) + dd->ipath_lastegrheads[val] =-1 ; +if (dd->ipath_lastrcvhdrqtails[val] !=-1 ) + dd->ipath_lastrcvhdrqtails[val] =-1 ; + } + } + +if (dd->ipath_nosma_bufs) { + dd->ipath_nosma_secs +=5 ; +if (dd->ipath_nosma_secs >=30 ) { +_IPATH_SMADBG ("No SMA bufs avail %u seconds; cancelling pending sends\n" , + dd->ipath_nosma_secs); +ipath_disarm_piobufs (t, dd->ipath_lastport_piobuf, + dd->ipath_piobcnt - dd->ipath_lastport_piobuf); + dd->ipath_nosma_secs =0 ;/* start again, if necessary */ + } +else +_IPATH_SMADBG ("No SMA bufs avail %u tries, after %u seconds\n" , + dd->ipath_nosma_bufs, dd->ipath_nosma_secs); + } + +done: +mod_timer (&dd->ipath_stats_timer, jiffies +HZ *5 ); +} + + +static void __devexit infinipath_remove_one(struct pci_dev *);
drivers/infiniband/hw/ipath/ipath_driver.c line 677
Quoted 10 lines
drivers/infiniband/hw/ipath/ipath_driver.c new file +10
-0+static int infinipath_init_one (struct pci_dev *,const struct pci_device_id *); + +/* Only needed for registration, nothing else needs this info */ +#define PCI_VENDOR_ID_PATHSCALE 0x1fc1 +#define PCI_DEVICE_ID_PATHSCALE_INFINIPATH_HT 0xd + +const struct pci_device_id infinipath_pci_tbl[] = { + { +PCI_VENDOR_ID_PATHSCALE ,PCI_DEVICE_ID_PATHSCALE_INFINIPATH_HT , +PCI_ANY_ID ,PCI_ANY_ID ,0 ,0 ,0 },
PCI_DEVICE() instead?
drivers/infiniband/hw/ipath/ipath_driver.c line 678
Quoted 1 line
drivers/infiniband/hw/ipath/ipath_driver.c new file +1
-0+ {0,}
{},
is all that is needed here.
drivers/infiniband/hw/ipath/ipath_driver.c line 685
Quoted 7 lines
drivers/infiniband/hw/ipath/ipath_driver.c new file +7
-0+}; + +MODULE_DEVICE_TABLE (pci, infinipath_pci_tbl); + +static struct pci_driver infinipath_driver = { + .name =MODNAME , + .driver.owner =THIS_MODULE ,
This line is not needed, you can remove it.
Quoted 33 lines
drivers/infiniband/hw/ipath/ipath_driver.c new file +33
-0+ .probe = infinipath_init_one, + .remove = __devexit_p(infinipath_remove_one), + .id_table = infinipath_pci_tbl, +}; + +#if defined (pgprot_writecombine) && defined(_PAGE_MA_WC) +int remap_area_pages (unsigned long address,unsigned long phys_addr, +unsigned long size,unsigned long flags); +#endif + +static int infinipath_init_one (struct pci_dev *pdev, +const struct pci_device_id *ent) +{ +int ret, len, j; +static int chip_idx =-1 ; +unsigned long addr; +uint64_t intconfig; +uint8_t rev; +ipath_type dev; + +/* +* XXX: Right now, we have a hardcoded array of devices. We'll +* change this in a future release, but not just yet. For the +* moment, we're limited to 4 infinipath devices per system. +*/ + + dev = ++chip_idx; + +_IPATH_VDBG ("initializing unit #%u\n" , dev); +if ((!infinipath_cfgunits && (dev >=1 )) || + (infinipath_cfgunits && (dev >= infinipath_cfgunits)) || + (dev >= infinipath_max)) { + _IPATH_ERROR("Trying to initialize unit %u, max is %u\n",
drivers/infiniband/hw/ipath/ipath_driver.c line 728
Quoted 10 lines
drivers/infiniband/hw/ipath/ipath_driver.c new file +10
-0+ dev, infinipath_max - 1); +return -EINVAL ; + } + + devdata[dev].pci_registered =1 ; + devdata[dev].ipath_unit = dev; + +if ((ret =pci_enable_device (pdev))) { +_IPATH_DBG ("pci_enable unit %u failed: %x\n" , dev, ret); + }
{} not needed here.
Quoted 31 lines
drivers/infiniband/hw/ipath/ipath_driver.c new file +31
-0+ +if ((ret =pci_request_regions (pdev,MODNAME ))) +_IPATH_INFO ("pci_request_regions unit %u fails: %d\n" , dev, + ret); + +if ((ret =pci_set_dma_mask (pdev,DMA_64BIT_MASK )) !=0 ) +_IPATH_INFO ("pci_set_dma_mask unit %u fails: %d\n" , dev, ret); + +pci_set_master (pdev);/* probably not be needed for HT */ + + addr =pci_resource_start (pdev,0 ); + len =pci_resource_len (pdev,0 ); +_IPATH_VDBG + ("regbase (0) %lx len %d irq %x, vend %x/%x driver_data %lx\n" , + addr, len, pdev->irq, ent->vendor, ent->device, ent->driver_data); + devdata[dev].ipath_deviceid = ent->device;/* save for later use */ + devdata[dev].ipath_vendorid = ent->vendor; +for (j =0 ; j <6 ; j++) { +if (!pdev->resource[j].start) +continue ; +_IPATH_VDBG ("BAR %d start %lx, end %lx, len %lx\n" , + j, pdev->resource[j].start, + pdev->resource[j].end,pci_resource_len (pdev, j)); + } + +if (!addr) { +_IPATH_UNIT_ERROR (dev,"No valid address in BAR 0!\n" ); +return -ENODEV ; + } + +if ((ret =pci_read_config_byte (pdev,PCI_REVISION_ID , &rev))) {
drivers/infiniband/hw/ipath/ipath_driver.c line 769
Quoted 10 lines
drivers/infiniband/hw/ipath/ipath_driver.c new file +10
-0+_IPATH_UNIT_ERROR (dev, +"Failed to read PCI revision ID unit %u: %d\n" , + dev, ret); +return ret;/* shouldn't ever happen */ + }else + devdata[dev].ipath_pcirev = rev; + + devdata[dev].ipath_kregbase =ioremap_nocache (addr, len); +#if defined (pgprot_writecombine) && defined(_PAGE_MA_WC) +printk ("Remapping pages WC\n" );
No KERN_ level?
Quoted 16 lines
drivers/infiniband/hw/ipath/ipath_driver.c new file +16
-0+remap_area_pages ((unsigned long ) devdata[dev].ipath_kregbase + +1024 *1024 , addr +1024 *1024 ,1024 *1024 , + _PAGE_MA_WC); +/* devdata[dev].ipath_kregbase = __ioremap(addr, len, _PAGE_MA_WC); */ +#endif + +if (!devdata[dev].ipath_kregbase) { +_IPATH_DBG ("Unable to map io addr %lx to kvirt, failing\n" , + addr); + ret = -ENOMEM ; + goto fail; + } + devdata[dev].ipath_kregend = (uint64_t __iomem *) + ((void __iomem *) devdata[dev].ipath_kregbase + len); + devdata[dev].ipath_physaddr = addr;/* used for io_remap, etc. */ +/* for user mmap */
drivers/infiniband/hw/ipath/ipath_driver.c line 795
Quoted 10 lines
drivers/infiniband/hw/ipath/ipath_driver.c new file +10
-0+ devdata[dev].ipath_kregvirt = (uint64_t __iomem *)phys_to_virt (addr); +_IPATH_VDBG ("mapped io addr %lx to kregbase %p kregvirt %p\n" , addr, + devdata[dev].ipath_kregbase, devdata[dev].ipath_kregvirt); + +/* +* set these up before registering the interrupt handler, just +* in case +*/ + devdata[dev].pcidev = pdev; +pci_set_drvdata (pdev, &(devdata[dev]));
It's not a "just in case" type thing, you have to do this before you register that interrupt handler, as you can be instantly called here.
Are you sure everything else is set up properly here before calling that function?
Quoted 120 lines
drivers/infiniband/hw/ipath/ipath_driver.c new file +120
-0+ +/* +* set up our interrupt handler; SA_SHIRQ probably not needed, +* but won't hurt for now. +*/ + +if (!pdev->irq) { +_IPATH_UNIT_ERROR (dev,"irq is 0, failing init\n" ); + ret = -EINVAL ; + goto fail; + } +if ((ret =request_irq (pdev->irq, ipath_intr, +SA_SHIRQ ,MODNAME , &devdata[dev]))) { +_IPATH_UNIT_ERROR (dev, +"Couldn't setup interrupt handler, irq=%u: %d\n" , + pdev->irq, ret); + goto fail; + } + +/* +* clear ipath_flags here instead of in ipath_init_chip as it is set +* by ipath_setup_htconfig. +*/ + devdata[dev].ipath_flags =0 ; +if (ipath_setup_htconfig (pdev, &intconfig, dev)) +_IPATH_DBG + ("Failed to setup HT config, continuing anyway for now\n" ); + + ret =ipath_init_chip (dev);/* do the chip-specific init */ +if (!ret) { +#ifdef CONFIG_MTRR +uint64_t pioaddr, piolen; +unsigned bits; +/* +* Set the PIO buffers to be WCCOMB, so we get HT bursts +* to the chip. Linux (possibly the hardware) requires +* it to be on a power of 2 address matching the length +* (which has to be a power of 2). For rev1, that means +* the base address, for rev2, it will be just the PIO +* buffers themselves. +*/ + pioaddr = addr + devdata[dev].ipath_piobufbase; + piolen = devdata[dev].ipath_piobcnt * +ALIGN (devdata[dev].ipath_piosize, + devdata[dev].ipath_palign); + +for (bits =0 ; !(piolen & (1ULL << bits)); bits++) +/* do nothing */ ; + +if (piolen != (1ULL << bits)) { +_IPATH_DBG ("piolen 0x%llx not power of 2, bits=%u\n" , + piolen, bits); + piolen >>= bits; +while (piolen >>=1 ) + bits++; + piolen =1ULL << (bits +1 ); +_IPATH_DBG ("Changed piolen to 0x%llx bits=%u\n" , piolen, + bits); + } +if (pioaddr & (piolen -1 )) { +uint64_t atmp; +_IPATH_DBG + ("pioaddr %llx not on right boundary for size %llx, fixing\n" , + pioaddr, piolen); + atmp = pioaddr & ~(piolen -1 ); +if (atmp < addr || (atmp + piolen) > (addr + len)) { +_IPATH_UNIT_ERROR (dev, +"No way to align address/size (%llx/%llx), no WC mtrr\n" , + atmp, piolen <<1 ); + ret = -ENODEV ; + }else { +_IPATH_DBG + ("changing WC base from %llx to %llx, len from %llx to %llx\n" , + pioaddr, atmp, piolen, piolen <<1 ); + pioaddr = atmp; + piolen <<=1 ; + } + } + +if (!ret) { +int cookie; +_IPATH_VDBG + ("Setting mtrr for chip to WC (addr %llx, len=0x%llx)\n" , + pioaddr, piolen); + cookie =mtrr_add (pioaddr, piolen,MTRR_TYPE_WRCOMB ,0 ); +if (cookie <0 ) { +_IPATH_INFO + ("mtrr_add(%llx,0x%llx,WC,0) failed (%d)\n" , + pioaddr, piolen, cookie); + ret = -EINVAL ; + }else { +_IPATH_VDBG + ("Set mtrr for chip to WC, cookie is %d\n" , + cookie); + devdata[dev].ipath_mtrr = (uint32_t ) cookie; + } + } +#endif /* CONFIG_MTRR */ + } + +if (!ret && devdata[dev].ipath_kregbase && (devdata[dev].ipath_flags + &IPATH_PRESENT )) { +/* +* for the hardware, enable interrupts only after +* kr_interruptconfig is written, if we could set it up +*/ +if (intconfig) { +/* interrupt address */ +ipath_kput_kreg (dev, kr_interruptconfig, intconfig); +/* enable all interrupts */ +ipath_kput_kreg (dev, kr_intmask,-1LL ); +/* force re-interrupt of any pending interrupts. */ +ipath_kput_kreg (dev, kr_intclear,0ULL ); +/* OK, the chip is usable, marked it as initialized */ + *devdata[dev].ipath_statusp |=IPATH_STATUS_INITTED ; + }else +_IPATH_UNIT_ERROR (dev, +"No interrupts enabled, couldn't setup interrupt address\n" ); + }else if (ret != -EPERM ) + _IPATH_INFO("Not configuring unit %u interrupts, init failed\n",
drivers/infiniband/hw/ipath/ipath_driver.c line 925
Quoted 10 lines
drivers/infiniband/hw/ipath/ipath_driver.c new file +10
-0+ dev); + +device_create_file (&(pdev->dev), &dev_attr_status); +device_create_file (&(pdev->dev), &dev_attr_status_str); +device_create_file (&(pdev->dev), &dev_attr_lid); +device_create_file (&(pdev->dev), &dev_attr_mlid); +device_create_file (&(pdev->dev), &dev_attr_guid); +device_create_file (&(pdev->dev), &dev_attr_nguid); +device_create_file (&(pdev->dev), &dev_attr_serial); +device_create_file (&(pdev->dev), &dev_attr_unit);
Why not use an attribute array? Makes for proper error handling if one of those calls does not work...
drivers/infiniband/hw/ipath/ipath_driver.c line 931
Quoted 5 lines
drivers/infiniband/hw/ipath/ipath_driver.c new file +5
-0+/* +* We used to cleanup here, with pci_release_regions, etc. but that +* can cause other problems if we want to run diags, etc., so instead +* defer that until driver unload. +*/
So memory leaks are acceptable?
drivers/infiniband/hw/ipath/ipath_driver.c line 936
Quoted 4 lines
drivers/infiniband/hw/ipath/ipath_driver.c new file +4
-0+fail:/* after we've done at least some of the pci setup */ +if (ret == -EPERM )/* disabled device, don't want module load error; +* just want to carry status through to this point */ + ret =0 ;
Module load error does not happen no matter what kind of return value you send back from this function. So the comment is wrong, and the fact that you failed initializing the device is also wrong, please don't do this.
thanks,
greg k-h
Re: [PATCH 12 of 20] ipath - misc driver support code
In reply to Greg KHQuoted 1 line · Greg KH
No description of what the patch does?
Ahem. Oops.
Quoted 4 lines
drivers/infiniband/hw/ipath/ipath_ht400.c line 60
Quoted 2 lines
drivers/infiniband/hw/ipath/ipath_ht400.c new file +2
-0+struct _infinipath_do_not_use_kernel_regs { +unsigned long long Revision;u64?
Right.
Quoted 5 lines
drivers/infiniband/hw/ipath/ipath_ht400.c line 63
Quoted 3 lines
drivers/infiniband/hw/ipath/ipath_ht400.c new file +3
-0+unsigned long long Control; +unsigned long long PageAlign; +unsigned long long PortCnt;And what's with the InterCapsNamingScheme of these variables?
They're taken straight from the register names in our chip spec. I can squish them to lowercase-only, if that seems important.
Quoted 8 lines
drivers/infiniband/hw/ipath/ipath_i2c.c line 168
Quoted 6 lines
drivers/infiniband/hw/ipath/ipath_i2c.c new file +6
-0+/* +* would prefer to not inline this, to avoid code bloat, and simplify debugging +* But when compiling against 2.6.10 kernel tree, it gets an error, so +* not for now. +*/ +static void ipath_i2c_delay (ipath_type ,int );You aren't compiling this for a 2.6.10 kernel anymore :)
Yes, that hunk is redundant. Thanks for spotting it.
Quoted 5 lines
Quoted 1 line
+static void
ipath_i2c_delay(ipath_type dev, int dtime)Huh? After reading your comment, I still don't understand why you can't just use
udelay(). Or are you counting on calling this function with only "1" being set for dtime?
It's usually called with a dtime of 1, but there's an added delay in one place.
I just rewrote that routine, so it's now a one-liner that does a read which waits for writes to the chip to complete. The sole caller that wanted an added wait calls udelay itself now.
Quoted 4 lines
Ah, isn't it fun to write bit-banging functions... And the in-kernel i2c code is messier than doing this by hand?
From looking at it, it will make the i2c part of the driver longer,
rather than shorter. There's nothing objectionable about the kernel i2c interfaces per se, but our bit-banging code is pretty small and specialised.
Quoted 1 line
Odd function comment style. Please fix this to be in kerneldoc format.
Sure.
Quoted 1 line
Are you _sure_ you need all of these for the one function in this file?
That file will be taken out and put to sleep.
Quoted 3 lines
Quoted 1 line
+#include <
stddef.h>Where is this file being pulled in from?
Ugh, braino.
Quoted 3 lines
Woah, um, don't you think that you should either export the main mlock function itself, or fix your code to not need it? Rolling it yourself isn't a good idea...
Other people have pointed out that our page-pinning code is horked. We'll find a saner alternative.
Thanks for the comments, Greg.
<b
Quoted 4 lines · Greg KH
Quoted 2 lines
- The driver still uses
EXPORT_SYMBOL, for consistency with other code indrivers/infinibandWhy would that matter?
I don't want to do something gratuitously different to the prevailing set of code in which it lives.
Quoted 5 lines
Quoted 2 lines
- We're still using ioctls instead of sysfs or configfs in some cases, to maintain userspace compatibility
Compatibility with what? The driver isn't in the kernel tree yet, so there's no old kernel versions to remain compatibile with :)
We already ship userspace code to customers that relies on the ioctl interfaces.
Quoted 3 lines
I also noticed that you are still using the
uint64_ttype variable types, can you please switch to the proper kernel types instead (u64 in this specific example.)
Yes, we'll use u64 for internal variables, and __u64 for stuff exported to userspace, etc.
<b
Re: [PATCH 11 of 20] ipath - core driver, part 4 of 4
In reply to Greg KHQuoted 2 lines · Greg KH
This has grown
into a huge file, can't you split it up into smaller pieces?
Absolutely.
Quoted 1 line
Why even save off the return value if you don't do anything with it?
I think that's just a throwback to an earlier rev of the driver.
Quoted 3 lines
And please don't put assignments in the middle of if statements, that's just messy and harder to read (the fact that gcc made you put an extra () should be a hint that you were doing something wrong...)
OK.
Quoted 3 lines
And does your driver work with udev? I didn't see where you were exporting the major:minor number of your devices to sysfs, but I might have missed it.
It was written in a pre-udev world, so it still uses a fixed major and minor number. How important is this to you? Is it "nice to have", or "blocker"? :-)
Quoted 6 lines
Are you sure that's a good idea? Please do the proper thing and tear down your infrastructure if something fails, that's the correct thing to do. That way you can actually recover if something that you call in this function fails (like
driver_create_file(), orpci_register_driver().) Functions don't return error values just so you can ignore them :)
This will take a bit of cleaning up, but it's a reasonable request.
Quoted 7 lines
Quoted 4 lines
+/*
+ * note: if for some reason the unload fails after this routine, and leaves
+ * the driver enterable by user code, we'll almost certainly crash and burn...
+ */See, you admit that what you are doing isn't the wisest thing, which should tell you something...
Indeed.
Quoted 5 lines
This is the call that should have cleaned up all of the memory and other stuff that you do above. If not, then your driver will not work in any hotplug pci systems, which would not be a good thing. Please do like Roland says and put your resources and stuff in the device specific structures, like the rest of the kernel drivers do.
I'm working on the appropriate hearts and minds as we speak :-)
Quoted 1 line
Why not just export
ipath_ht_get_boardnameinstead?
Because that's too specific to HT for my personal liking.
Quoted 7 lines
drivers/infiniband/hw/ipath/ipath_driver.c line 7115
Quoted 5 lines
drivers/infiniband/hw/ipath/ipath_driver.c +5
-0+module_init (infinipath_init); +module_exit (infinipath_cleanup); + +EXPORT_SYMBOL (infinipath_debug); +EXPORT_SYMBOL (ipath_get_boardname);
EXPORT_SYMBOL_GPL()?
I don't see a problem with that.
Quoted 2 lines
And put them next to the functions themselves, it's easier to notice that way.
OK.
Thanks again for the review,
<b
Re: [PATCH 8 of 20] ipath - core driver, part 1 of 4
In reply to Greg KHQuoted 9 lines · Greg KH
drivers/infiniband/hw/ipath/ipath_driver.c line 196
Quoted 7 lines
drivers/infiniband/hw/ipath/ipath_driver.c new file +7
-0+void ipath_chip_done (void ) +{ +} + +void ipath_chip_cleanup (struct ipath_devdata * dd) +{ +}What are these two empty functions for?
They're just as dead as they look.
Quoted 3 lines
Quoted 1 line
+static
ssize_tshow_status_str(struct device *dev,how big can this "status string" be?
Just a few dozen bytes.
Quoted 3 lines
If it's even getting close to
PAGE_SIZE, this doesn't need to be a sysfs attribute, but you should break it up into its individual pieces.
Do you think that's still warranted, given this?
Quoted 3 lines
Quoted 1 line
+static
ssize_tshow_unit(struct device *dev,Don't you mean
-ENODEV?
Yes, thanks.
Quoted 5 lines
drivers/infiniband/hw/ipath/ipath_driver.c line 416
Quoted 2 lines
drivers/infiniband/hw/ipath/ipath_driver.c new file +2
-0+snprintf (buf,PAGE_SIZE ,"%u\n" , dd->ipath_unit); +return strlen (buf);return the
snprintf()call instead of callingstrlen()all the time please.
OK.
Quoted 6 lines
drivers/infiniband/hw/ipath/ipath_driver.c line 677
Quoted 4 lines
drivers/infiniband/hw/ipath/ipath_driver.c new file +4
-0+const struct pci_device_id infinipath_pci_tbl[] = { + { +PCI_VENDOR_ID_PATHSCALE ,PCI_DEVICE_ID_PATHSCALE_INFINIPATH_HT , +PCI_ANY_ID ,PCI_ANY_ID ,0 ,0 ,0 },
PCI_DEVICE()instead?
OK.
Quoted 4 lines
drivers/infiniband/hw/ipath/ipath_driver.c line 678
Quoted 1 line
drivers/infiniband/hw/ipath/ipath_driver.c new file +1
-0+ {0,}{},
is all that is needed here.
OK.
Quoted 3 lines
drivers/infiniband/hw/ipath/ipath_driver.c line 685
Quoted 1 line
drivers/infiniband/hw/ipath/ipath_driver.c new file +1
-0+ .driver.owner = THIS_MODULE,This line is not needed, you can remove it.
OK.
Quoted 1 line
{} not needed here.
OK.
Quoted 4 lines
drivers/infiniband/hw/ipath/ipath_driver.c line 769
Quoted 2 lines
drivers/infiniband/hw/ipath/ipath_driver.c new file +2
-0+#if defined (pgprot_writecombine) && defined(_PAGE_MA_WC) +printk ("Remapping pages WC\n" );No
KERN_level?
That should just become a debug statement.
Quoted 9 lines
drivers/infiniband/hw/ipath/ipath_driver.c line 795
Quoted 6 lines
drivers/infiniband/hw/ipath/ipath_driver.c new file +6
-0+/* +* set these up before registering the interrupt handler, just +* in case +*/ + devdata[dev].pcidev = pdev; +pci_set_drvdata (pdev, &(devdata[dev]));It's not a "just in case" type thing, you have to do this before you register that interrupt handler, as you can be instantly called here.
OK, I'll remove the misleading comment.
Quoted 2 lines
Are you sure everything else is set up properly here before calling that function?
I believe so. I'll double check.
Quoted 11 lines
drivers/infiniband/hw/ipath/ipath_driver.c line 925
Quoted 8 lines
drivers/infiniband/hw/ipath/ipath_driver.c new file +8
-0+device_create_file (&(pdev->dev), &dev_attr_status); +device_create_file (&(pdev->dev), &dev_attr_status_str); +device_create_file (&(pdev->dev), &dev_attr_lid); +device_create_file (&(pdev->dev), &dev_attr_mlid); +device_create_file (&(pdev->dev), &dev_attr_guid); +device_create_file (&(pdev->dev), &dev_attr_nguid); +device_create_file (&(pdev->dev), &dev_attr_serial); +device_create_file (&(pdev->dev), &dev_attr_unit);Why not use an attribute array? Makes for proper error handling if one of those calls does not work...
OK, thanks.
Quoted 7 lines
drivers/infiniband/hw/ipath/ipath_driver.c line 931
Quoted 5 lines
drivers/infiniband/hw/ipath/ipath_driver.c new file +5
-0+/* +* We used to cleanup here, with pci_release_regions, etc. but that +* can cause other problems if we want to run diags, etc., so instead +* defer that until driver unload. +*/ So memory leaks are acceptable?
That clearly needs a bit of attention.
Quoted 9 lines
drivers/infiniband/hw/ipath/ipath_driver.c line 936
Quoted 4 lines
drivers/infiniband/hw/ipath/ipath_driver.c new file +4
-0+fail:/* after we've done at least some of the pci setup */ +if (ret == -EPERM )/* disabled device, don't want module load error; +* just want to carry status through to this point */ + ret =0 ;Module load error does not happen no matter what kind of return value you send back from this function. So the comment is wrong, and the fact that you failed initializing the device is also wrong, please don't do this.
OK.
Thanks for the extensive comments,
<b
Re: [PATCH 10 of 20] ipath - core driver, part 3 of 4
In reply to Linus TorvaldsQuoted 1 line · Linus Torvalds
All your user page lookup/pinning code is terminally broken.
Yes, this has been pointed out by a few others.
Quoted 1 line
Crap like this must not be merged.
I'm already busy decrappifying it...
<b
Re: [PATCH 12 of 20] ipath - misc driver support code
In reply to Bryan O'SullivanQuoted 9 lines · Bryan O'Sullivan
Quoted 5 lines · Greg KH
drivers/infiniband/hw/ipath/ipath_ht400.c line 63
Quoted 3 lines
drivers/infiniband/hw/ipath/ipath_ht400.c new file +3
-0+unsigned long long Control; +unsigned long long PageAlign; +unsigned long long PortCnt;And what's with the InterCapsNamingScheme of these variables?
They're taken straight from the register names in our chip spec. I can squish them to lowercase-only, if that seems important.
No, but document it that this is the reason for it (along with a pointer to your chip spec, if possible.)
thanks,
greg k-h
Re: [PATCH 8 of 20] ipath - core driver, part 1 of 4
In reply to Bryan O'SullivanQuoted 12 lines · Bryan O'Sullivan
Quoted 9 lines · Greg KH
drivers/infiniband/hw/ipath/ipath_driver.c line 196
Quoted 7 lines
drivers/infiniband/hw/ipath/ipath_driver.c new file +7
-0+void ipath_chip_done (void ) +{ +} + +void ipath_chip_cleanup (struct ipath_devdata * dd) +{ +}What are these two empty functions for?
Quoted 1 line
They're just as dead as they look.
Then you might want to remove them :)
Quoted 11 lines
Quoted 3 lines
Quoted 1 line
+static
ssize_tshow_status_str(struct device *dev,how big can this "status string" be?
Just a few dozen bytes.
Quoted 3 lines
If it's even getting close to
PAGE_SIZE, this doesn't need to be a sysfs attribute, but you should break it up into its individual pieces.Do you think that's still warranted, given this?
No I don't, unless you think that message will grow somehow...
thanks,
greg k-h
Quoted 9 lines · Bryan O'Sullivan
Quoted 4 lines · Greg KH
Quoted 2 lines
- The driver still uses
EXPORT_SYMBOL, for consistency with other code indrivers/infinibandWhy would that matter?
I don't want to do something gratuitously different to the prevailing set of code in which it lives.
Quoted 8 lines
Quoted 5 lines
Quoted 2 lines
- We're still using ioctls instead of sysfs or configfs in some cases, to maintain userspace compatibility
Compatibility with what? The driver isn't in the kernel tree yet, so there's no old kernel versions to remain compatibile with :)
We already ship userspace code to customers that relies on the ioctl interfaces.
But we (the kernel community), don't really accept that as a valid reason to accept this kind of code, sorry.
Why not just update your userspace code and ship that out to your customers, as you know exactly who they are due to the lack of the driver in the mainline kernel tree :)
thanks,
greg k-h
Re: [PATCH 11 of 20] ipath - core driver, part 4 of 4
In reply to Bryan O'SullivanQuoted 8 lines · Bryan O'Sullivan
Quoted 3 lines · Greg KH
And does your driver work with udev? I didn't see where you were exporting the major:minor number of your devices to sysfs, but I might have missed it.
It was written in a pre-udev world, so it still uses a fixed major and minor number. How important is this to you? Is it "nice to have", or "blocker"? :-)
Well, depends on if you want your driver to work with any of the major distros that rely on udev (RHEL, SLES, etc...) If not, fine, you don't need it :)
thanks,
greg k-h
Quoted 2 lines · Greg KH
But we (the kernel community), don't really accept that as a valid reason to accept this kind of code, sorry.
Fair enough. I'd like some guidance in that case. Some of our ioctls access the hardware more or less directly, while others do things like read or reset counters.
Which of these kinds of operations are appropriate to retain as ioctls, in your eyes, and which are best converted to sysfs or configfs alternatives?
As an example, take a look at ipath_sma_ioctl. It seems to me that receiving or sending subnet management packets ought to remain as ioctls, while getting port or node data could be turned into sysfs attributes. Lane identification could live in configfs. If you think otherwise, please let me know what's more appropriate.
The less blind I am in doing these conversions, the fewer rounds we'll have to go in reviewing humongous driver submission patches :-)
Thanks,
<b
Quoted 9 lines
Quoted 6 lines
Quoted 3 lines
- Someone asked for the kernel's i2c infrastructure to be used,but our i2c usage is very specialised, and it would be more of a mess to use the kernel's
Problem with that is that if everybody and Aunt Tillie does the same, the kernel as a whole gets to be a mess.
ALSA does the exact same thing for the exact same reason. Maybe an indication that the kernel's i2c layer is too heavy?
Sounds like a discussion a while back why jfs/xfs/reiser3/reiser4 all have their own journalling - compared to ext3-jbd.
Jan Engelhardt
Re: [PATCH 10 of 20] ipath - core driver, part 3 of 4
In reply to Bryan O'SullivanQuoted 9 lines · Bryan O'Sullivan
Quoted 1 line · Linus Torvalds
All your user page lookup/pinning code is terminally broken.
Yes, this has been pointed out by a few others.
Quoted 1 line
Crap like this must not be merged.
I'm already busy decrappifying it...
the point I think also was the fact that it exists is already wrong :)
makes it easier for you.. "rm" is a very powerful decrappify tool, as is "block delete" in just about any editor ;)
Quoted 10 lines · Lee Revell
Quoted 6 lines · Horst von Brand
Quoted 3 lines
- Someone asked for the kernel's i2c infrastructure to be used,but our i2c usage is very specialised, and it would be more of a mess to use the kernel's
Problem with that is that if everybody and Aunt Tillie does the same, the kernel as a whole gets to be a mess.
ALSA does the exact same thing for the exact same reason. Maybe an indication that the kernel's i2c layer is too heavy?
That would mean that the respective teams should put their heads together and (re)design it to their needs...
Signature · 4 lines
Dr. Horst H. von Brand User #22616 counter.li.org Departamento de Informatica Fono: +56 32 654431 Universidad Tecnica Federico Santa Maria +56 32 654239 Casilla 110-V, Valparaiso, Chile Fax: +56 32 797513
Quoted 8 lines · Horst von Brand
Quoted 5 lines
Quoted 2 lines
Problem with that is that if everybody and Aunt Tillie does the same, the kernel as a whole gets to be a mess.
ALSA does the exact same thing for the exact same reason. Maybe an indication that the kernel's i2c layer is too heavy?
That would mean that the respective teams should put their heads together and (re)design it to their needs...
Exactly. We got quite a few developers to help adjusting the i2c stack for their needs and improve it. The i2c stack started out beeing used only for hardware monitoring chips and then later multimedia devices. Help to make it more useful for other users is always appreciated.
Quoted 8 lines · Bryan O'Sullivan
Quoted 2 lines · Greg KH
But we (the kernel community), don't really accept that as a valid reason to accept this kind of code, sorry.
Fair enough. I'd like some guidance in that case. Some of our ioctls access the hardware more or less directly, while others do things like read or reset counters.
As a general rule a driver should push as much functionality to libraries and the infrastructure code as possible.
Quoted 9 lines
Which of these kinds of operations are appropriate to retain as ioctls, in your eyes, and which are best converted to sysfs or configfs alternatives?
As an example, take a look at
ipath_sma_ioctl. It seems to me that receiving or sending subnet management packets ought to remain as ioctls, while getting port or node data could be turned into sysfs attributes. Lane identification could live in configfs. If you think otherwise, please let me know what's more appropriate.
I haven't looked closely enough at the state of the openib tree but you should not need an additional interface to send/receive standard IB subnet management packets. That is something that should be provided the same way by all infiniband drivers.
The only case I can think of where this might not already exist is the code that responds to the subnet manager. If the current interfaces are not sufficient then the infiniband layer needs more work.
Quoted 2 lines
The less blind I am in doing these conversions, the fewer rounds we'll have to go in reviewing humongous driver submission patches :-)
Given Linus's comments and looking at where you are getting stuck I would recommend you split out support for the nonstandard ipath protocol from the rest of the driver. If the standard infiniband interfaces for kernel bypass are not sufficient for flinging packets then we need to re-examine them.
Eric
Quoted 4 lines · Eric W. Biederman
I haven't looked closely enough at the state of the openib tree but you should not need an additional interface to send/receive standard IB subnet management packets. That is something that should be provided the same way by all infiniband drivers.
We provide the standard OpenIB mechanisms for doing that, of course. However, our driver is layered. The OpenIB layer uses facilities provided by the main driver (via ipath_layer.c). The main driver can stand alone, without the OpenIB code compiled into the kernel or available as a module at all. In that case, a userland subnet management agent must still be able to send and receive management packets.
Quoted 3 lines
Given Linus's comments and looking at where you are getting stuck I would recommend you split out support for the nonstandard ipath protocol from the rest of the driver.
While we can split the main driver source file up along those lines, we are not planning to make the ipath protocol optional. We are planning to submit another non-OpenIB network driver that depends on the ipath protocol support.
<b
Quoted 5 lines · Bryan O'Sullivan
Quoted 2 lines · Greg KH
But we (the kernel community), don't really accept that as a valid reason to accept this kind of code, sorry.
Quoted 7 lines
Fair enough. I'd like some guidance in that case. Some of our ioctls access the hardware more or less directly, while others do things like read or reset counters.
Which of these kinds of operations are appropriate to retain as ioctls, in your eyes, and which are best converted to sysfs or configfs alternatives?
Idealy, nothing should be new ioctls. But in the end, it all depends on exactly what you are trying to do with each different one.
Quoted 5 lines
As an example, take a look at
ipath_sma_ioctl. It seems to me that receiving or sending subnet management packets ought to remain as ioctls, while getting port or node data could be turned into sysfs attributes. Lane identification could live in configfs. If you think otherwise, please let me know what's more appropriate.
I really don't know what the subnet management stuff involves, sorry. But doesn't the open-ib layer handle that all for you already?
thanks,
greg k-h
Quoted 2 lines · Greg KH
Idealy, nothing should be new ioctls. But in the end, it all depends on exactly what you are trying to do with each different one.
Fair enough.
Quoted 2 lines
I really don't know what the subnet management stuff involves, sorry. But doesn't the open-ib layer handle that all for you already?
It does when our OpenIB driver is being used. But our lower level driver is independent of OpenIB (and is often used without the infiniband stuff even configured into the kernel), and needs to provide some way for a userspace subnet management agent to send and receive packets.
<b
Quoted 7 lines · Bryan O'Sullivan
Quoted 2 lines · Greg KH
Idealy, nothing should be new ioctls. But in the end, it all depends on exactly what you are trying to do with each different one.
Fair enough.
Quoted 8 lines
Quoted 2 lines
I really don't know what the subnet management stuff involves, sorry. But doesn't the open-ib layer handle that all for you already?
It does when our OpenIB driver is being used. But our lower level driver is independent of OpenIB (and is often used without the infiniband stuff even configured into the kernel), and needs to provide some way for a userspace subnet management agent to send and receive packets.
that sounds like your driver should mimic the openIB userspace ABI for this *exactly* so that you can use the same management tools for either scenario...
Quoted 3 lines · Arjan van de Ven
that sounds like your driver should mimic the openIB userspace ABI for this *exactly* so that you can use the same management tools for either scenario...
The OpenIB userspace ABI is huge and complex, and the OpenIB subnet management agent (OpenSM) is even more so. Our low-level subnet management agent has vastly simpler needs, so it really is better served with 300 lines of specialised code (I don't care what the ABI actually is) than 15,000 lines introduced for the sake of unneeded compatibility.
Perhaps read/write on the character device file would be preferable to ioctls for sending and receiving these management packets? We don't implement those file methods at the moment, so it's not like we'd be displacing anything.
<b
Quoted 5 lines
Perhaps read/write on the character device file would be preferable to ioctls for sending and receiving these management packets? We don't implement those file methods at the moment, so it's not like we'd be displacing anything.
if it's just data packets.. you could implement a device that offers the SG_IO interface. Yes it's ioctls, but it's a preexisting ABI so I suspect that's not too big a deal (and maybe you can even leverage a lot of existing code for this)
Quoted 7 lines · Arjan van de Ven
Quoted 4 lines
Perhaps read/write on the character device file would be preferable to ioctls for sending and receiving these management packets? We don't implement those file methods at the moment, so it's not like we'd be displacing anything.
if it's just data packets.. you could implement a device that offers the
SG_IOinterface.
OK, thanks for the pointer. I'll take a look.
<b
Eric> Given Linus's comments and looking at where you are getting Eric> stuck I would recommend you split out support for the Eric> nonstandard ipath protocol from the rest of the driver. If Eric> the standard infiniband interfaces for kernel bypass are not Eric> sufficient for flinging packets then we need to re-examine Eric> them.
Yes, this might be a good idea. The "core" driver looks like it is suffering from really being several things stuck together. It would probably make things a lot cleaner and easier to maintain if the core driver just handled synchronizing access to the low-level hardware, with other stuff split into its own driver. It seems there might even be enough stuff to split "core" into three drivers: the real core, the ultra-high-performance MPI transport, and the management/diagnostitcs stuff.
Also, there are APIs in the "core" driver that are only exported for a single user outside the driver -- it would probably make sense to move that logic directly to where it's used. I'm thinking of things like ipath_verbs_send() and the whole ipath_copy.c file.
- R.
Bryan> It does when our OpenIB driver is being used. But our Bryan> lower level driver is independent of OpenIB (and is often Bryan> used without the infiniband stuff even configured into the Bryan> kernel), and needs to provide some way for a userspace Bryan> subnet management agent to send and receive packets.
Isn't there some way you can use the same SMA (subnet management agent) interface in all the cases? Can ipath_mad.c just go away in favor of your userspace SMA?
- R.
Quoted 2 lines · Roland Dreier
Yes, this might be a good idea. The "core" driver looks like it is suffering from really being several things stuck together.
Yes, this is undoubtedly the case; we developed it organically based on our evolving needs, and we're only now (maybe a bit belatedly) stepping back to take a breath and see how things should be logically split out.
Quoted 3 lines
Also, there are APIs in the "core" driver that are only exported for a single user outside the driver -- it would probably make sense to move that logic directly to where it's used.
Right. The purpose of the whole ipath_layer.c file has perhaps been unclear; we've been holding back a network driver that makes use of it, to keep the size of the review patches down. Some of the other verbs-related routines in the core driver are in the process of finding a new home, as you suggested.
As ever, thanks for the comments.
<b
Quoted 2 lines · Roland Dreier
Isn't there some way you can use the same SMA (subnet management agent) interface in all the cases?
I'll look into it, but I rather doubt it.
Quoted 2 lines
Can
ipath_mad.cjust go away in
favor of your userspace SMA?
Our userspace SMA is a tiny shrivelled thing that expects there to be a real subnet manager out there, so it only needs a very simple interface, and it's decoupled from OpenIB entirely. ipath_mad.c is part of our OpenIB layer, so it can't really go away.
<b