Path: blob/master/drivers/infiniband/hw/mthca/mthca_av.c
15112 views
/*1* Copyright (c) 2004 Topspin Communications. All rights reserved.2* Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.3*4* This software is available to you under a choice of one of two5* licenses. You may choose to be licensed under the terms of the GNU6* General Public License (GPL) Version 2, available from the file7* COPYING in the main directory of this source tree, or the8* OpenIB.org BSD license below:9*10* Redistribution and use in source and binary forms, with or11* without modification, are permitted provided that the following12* conditions are met:13*14* - Redistributions of source code must retain the above15* copyright notice, this list of conditions and the following16* disclaimer.17*18* - Redistributions in binary form must reproduce the above19* copyright notice, this list of conditions and the following20* disclaimer in the documentation and/or other materials21* provided with the distribution.22*23* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,24* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF25* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND26* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS27* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN28* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN29* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE30* SOFTWARE.31*/3233#include <linux/string.h>34#include <linux/slab.h>3536#include <rdma/ib_verbs.h>37#include <rdma/ib_cache.h>3839#include "mthca_dev.h"4041enum {42MTHCA_RATE_TAVOR_FULL = 0,43MTHCA_RATE_TAVOR_1X = 1,44MTHCA_RATE_TAVOR_4X = 2,45MTHCA_RATE_TAVOR_1X_DDR = 346};4748enum {49MTHCA_RATE_MEMFREE_FULL = 0,50MTHCA_RATE_MEMFREE_QUARTER = 1,51MTHCA_RATE_MEMFREE_EIGHTH = 2,52MTHCA_RATE_MEMFREE_HALF = 353};5455struct mthca_av {56__be32 port_pd;57u8 reserved1;58u8 g_slid;59__be16 dlid;60u8 reserved2;61u8 gid_index;62u8 msg_sr;63u8 hop_limit;64__be32 sl_tclass_flowlabel;65__be32 dgid[4];66};6768static enum ib_rate memfree_rate_to_ib(u8 mthca_rate, u8 port_rate)69{70switch (mthca_rate) {71case MTHCA_RATE_MEMFREE_EIGHTH:72return mult_to_ib_rate(port_rate >> 3);73case MTHCA_RATE_MEMFREE_QUARTER:74return mult_to_ib_rate(port_rate >> 2);75case MTHCA_RATE_MEMFREE_HALF:76return mult_to_ib_rate(port_rate >> 1);77case MTHCA_RATE_MEMFREE_FULL:78default:79return mult_to_ib_rate(port_rate);80}81}8283static enum ib_rate tavor_rate_to_ib(u8 mthca_rate, u8 port_rate)84{85switch (mthca_rate) {86case MTHCA_RATE_TAVOR_1X: return IB_RATE_2_5_GBPS;87case MTHCA_RATE_TAVOR_1X_DDR: return IB_RATE_5_GBPS;88case MTHCA_RATE_TAVOR_4X: return IB_RATE_10_GBPS;89default: return mult_to_ib_rate(port_rate);90}91}9293enum ib_rate mthca_rate_to_ib(struct mthca_dev *dev, u8 mthca_rate, u8 port)94{95if (mthca_is_memfree(dev)) {96/* Handle old Arbel FW */97if (dev->limits.stat_rate_support == 0x3 && mthca_rate)98return IB_RATE_2_5_GBPS;99100return memfree_rate_to_ib(mthca_rate, dev->rate[port - 1]);101} else102return tavor_rate_to_ib(mthca_rate, dev->rate[port - 1]);103}104105static u8 ib_rate_to_memfree(u8 req_rate, u8 cur_rate)106{107if (cur_rate <= req_rate)108return 0;109110/*111* Inter-packet delay (IPD) to get from rate X down to a rate112* no more than Y is (X - 1) / Y.113*/114switch ((cur_rate - 1) / req_rate) {115case 0: return MTHCA_RATE_MEMFREE_FULL;116case 1: return MTHCA_RATE_MEMFREE_HALF;117case 2: /* fall through */118case 3: return MTHCA_RATE_MEMFREE_QUARTER;119default: return MTHCA_RATE_MEMFREE_EIGHTH;120}121}122123static u8 ib_rate_to_tavor(u8 static_rate)124{125switch (static_rate) {126case IB_RATE_2_5_GBPS: return MTHCA_RATE_TAVOR_1X;127case IB_RATE_5_GBPS: return MTHCA_RATE_TAVOR_1X_DDR;128case IB_RATE_10_GBPS: return MTHCA_RATE_TAVOR_4X;129default: return MTHCA_RATE_TAVOR_FULL;130}131}132133u8 mthca_get_rate(struct mthca_dev *dev, int static_rate, u8 port)134{135u8 rate;136137if (!static_rate || ib_rate_to_mult(static_rate) >= dev->rate[port - 1])138return 0;139140if (mthca_is_memfree(dev))141rate = ib_rate_to_memfree(ib_rate_to_mult(static_rate),142dev->rate[port - 1]);143else144rate = ib_rate_to_tavor(static_rate);145146if (!(dev->limits.stat_rate_support & (1 << rate)))147rate = 1;148149return rate;150}151152int mthca_create_ah(struct mthca_dev *dev,153struct mthca_pd *pd,154struct ib_ah_attr *ah_attr,155struct mthca_ah *ah)156{157u32 index = -1;158struct mthca_av *av = NULL;159160ah->type = MTHCA_AH_PCI_POOL;161162if (mthca_is_memfree(dev)) {163ah->av = kmalloc(sizeof *ah->av, GFP_ATOMIC);164if (!ah->av)165return -ENOMEM;166167ah->type = MTHCA_AH_KMALLOC;168av = ah->av;169} else if (!atomic_read(&pd->sqp_count) &&170!(dev->mthca_flags & MTHCA_FLAG_DDR_HIDDEN)) {171index = mthca_alloc(&dev->av_table.alloc);172173/* fall back to allocate in host memory */174if (index == -1)175goto on_hca_fail;176177av = kmalloc(sizeof *av, GFP_ATOMIC);178if (!av)179goto on_hca_fail;180181ah->type = MTHCA_AH_ON_HCA;182ah->avdma = dev->av_table.ddr_av_base +183index * MTHCA_AV_SIZE;184}185186on_hca_fail:187if (ah->type == MTHCA_AH_PCI_POOL) {188ah->av = pci_pool_alloc(dev->av_table.pool,189GFP_ATOMIC, &ah->avdma);190if (!ah->av)191return -ENOMEM;192193av = ah->av;194}195196ah->key = pd->ntmr.ibmr.lkey;197198memset(av, 0, MTHCA_AV_SIZE);199200av->port_pd = cpu_to_be32(pd->pd_num | (ah_attr->port_num << 24));201av->g_slid = ah_attr->src_path_bits;202av->dlid = cpu_to_be16(ah_attr->dlid);203av->msg_sr = (3 << 4) | /* 2K message */204mthca_get_rate(dev, ah_attr->static_rate, ah_attr->port_num);205av->sl_tclass_flowlabel = cpu_to_be32(ah_attr->sl << 28);206if (ah_attr->ah_flags & IB_AH_GRH) {207av->g_slid |= 0x80;208av->gid_index = (ah_attr->port_num - 1) * dev->limits.gid_table_len +209ah_attr->grh.sgid_index;210av->hop_limit = ah_attr->grh.hop_limit;211av->sl_tclass_flowlabel |=212cpu_to_be32((ah_attr->grh.traffic_class << 20) |213ah_attr->grh.flow_label);214memcpy(av->dgid, ah_attr->grh.dgid.raw, 16);215} else {216/* Arbel workaround -- low byte of GID must be 2 */217av->dgid[3] = cpu_to_be32(2);218}219220if (0) {221int j;222223mthca_dbg(dev, "Created UDAV at %p/%08lx:\n",224av, (unsigned long) ah->avdma);225for (j = 0; j < 8; ++j)226printk(KERN_DEBUG " [%2x] %08x\n",227j * 4, be32_to_cpu(((__be32 *) av)[j]));228}229230if (ah->type == MTHCA_AH_ON_HCA) {231memcpy_toio(dev->av_table.av_map + index * MTHCA_AV_SIZE,232av, MTHCA_AV_SIZE);233kfree(av);234}235236return 0;237}238239int mthca_destroy_ah(struct mthca_dev *dev, struct mthca_ah *ah)240{241switch (ah->type) {242case MTHCA_AH_ON_HCA:243mthca_free(&dev->av_table.alloc,244(ah->avdma - dev->av_table.ddr_av_base) /245MTHCA_AV_SIZE);246break;247248case MTHCA_AH_PCI_POOL:249pci_pool_free(dev->av_table.pool, ah->av, ah->avdma);250break;251252case MTHCA_AH_KMALLOC:253kfree(ah->av);254break;255}256257return 0;258}259260int mthca_ah_grh_present(struct mthca_ah *ah)261{262return !!(ah->av->g_slid & 0x80);263}264265int mthca_read_ah(struct mthca_dev *dev, struct mthca_ah *ah,266struct ib_ud_header *header)267{268if (ah->type == MTHCA_AH_ON_HCA)269return -EINVAL;270271header->lrh.service_level = be32_to_cpu(ah->av->sl_tclass_flowlabel) >> 28;272header->lrh.destination_lid = ah->av->dlid;273header->lrh.source_lid = cpu_to_be16(ah->av->g_slid & 0x7f);274if (mthca_ah_grh_present(ah)) {275header->grh.traffic_class =276(be32_to_cpu(ah->av->sl_tclass_flowlabel) >> 20) & 0xff;277header->grh.flow_label =278ah->av->sl_tclass_flowlabel & cpu_to_be32(0xfffff);279header->grh.hop_limit = ah->av->hop_limit;280ib_get_cached_gid(&dev->ib_dev,281be32_to_cpu(ah->av->port_pd) >> 24,282ah->av->gid_index % dev->limits.gid_table_len,283&header->grh.source_gid);284memcpy(header->grh.destination_gid.raw,285ah->av->dgid, 16);286}287288return 0;289}290291int mthca_ah_query(struct ib_ah *ibah, struct ib_ah_attr *attr)292{293struct mthca_ah *ah = to_mah(ibah);294struct mthca_dev *dev = to_mdev(ibah->device);295296/* Only implement for MAD and memfree ah for now. */297if (ah->type == MTHCA_AH_ON_HCA)298return -ENOSYS;299300memset(attr, 0, sizeof *attr);301attr->dlid = be16_to_cpu(ah->av->dlid);302attr->sl = be32_to_cpu(ah->av->sl_tclass_flowlabel) >> 28;303attr->port_num = be32_to_cpu(ah->av->port_pd) >> 24;304attr->static_rate = mthca_rate_to_ib(dev, ah->av->msg_sr & 0x7,305attr->port_num);306attr->src_path_bits = ah->av->g_slid & 0x7F;307attr->ah_flags = mthca_ah_grh_present(ah) ? IB_AH_GRH : 0;308309if (attr->ah_flags) {310attr->grh.traffic_class =311be32_to_cpu(ah->av->sl_tclass_flowlabel) >> 20;312attr->grh.flow_label =313be32_to_cpu(ah->av->sl_tclass_flowlabel) & 0xfffff;314attr->grh.hop_limit = ah->av->hop_limit;315attr->grh.sgid_index = ah->av->gid_index &316(dev->limits.gid_table_len - 1);317memcpy(attr->grh.dgid.raw, ah->av->dgid, 16);318}319320return 0;321}322323int mthca_init_av_table(struct mthca_dev *dev)324{325int err;326327if (mthca_is_memfree(dev))328return 0;329330err = mthca_alloc_init(&dev->av_table.alloc,331dev->av_table.num_ddr_avs,332dev->av_table.num_ddr_avs - 1,3330);334if (err)335return err;336337dev->av_table.pool = pci_pool_create("mthca_av", dev->pdev,338MTHCA_AV_SIZE,339MTHCA_AV_SIZE, 0);340if (!dev->av_table.pool)341goto out_free_alloc;342343if (!(dev->mthca_flags & MTHCA_FLAG_DDR_HIDDEN)) {344dev->av_table.av_map = ioremap(pci_resource_start(dev->pdev, 4) +345dev->av_table.ddr_av_base -346dev->ddr_start,347dev->av_table.num_ddr_avs *348MTHCA_AV_SIZE);349if (!dev->av_table.av_map)350goto out_free_pool;351} else352dev->av_table.av_map = NULL;353354return 0;355356out_free_pool:357pci_pool_destroy(dev->av_table.pool);358359out_free_alloc:360mthca_alloc_cleanup(&dev->av_table.alloc);361return -ENOMEM;362}363364void mthca_cleanup_av_table(struct mthca_dev *dev)365{366if (mthca_is_memfree(dev))367return;368369if (dev->av_table.av_map)370iounmap(dev->av_table.av_map);371pci_pool_destroy(dev->av_table.pool);372mthca_alloc_cleanup(&dev->av_table.alloc);373}374375376