Date: Mon, 3 Jan 2011 05:36:16 +0000 (UTC) From: Jeff Roberson <jeff@FreeBSD.org> To: src-committers@freebsd.org, svn-src-projects@freebsd.org Subject: svn commit: r216910 - in projects/ofed/base/sys/ofed/drivers/infiniband/hw: cxgb3 nes Message-ID: <201101030536.p035aGW2076019@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: jeff Date: Mon Jan 3 05:36:16 2011 New Revision: 216910 URL: http://svn.freebsd.org/changeset/base/216910 Log: - Add files missing from the last merge. Sponsored by: Isilon Systems, iX Systems, and Panasas. Added: projects/ofed/base/sys/ofed/drivers/infiniband/hw/cxgb3/genalloc.c projects/ofed/base/sys/ofed/drivers/infiniband/hw/nes/nes_ud.c projects/ofed/base/sys/ofed/drivers/infiniband/hw/nes/nes_ud.h Added: projects/ofed/base/sys/ofed/drivers/infiniband/hw/cxgb3/genalloc.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ projects/ofed/base/sys/ofed/drivers/infiniband/hw/cxgb3/genalloc.c Mon Jan 3 05:36:16 2011 (r216910) @@ -0,0 +1,209 @@ +/* + * Basic general purpose allocator for managing special purpose memory + * not managed by the regular kmalloc/kfree interface. + * Uses for this includes on-device special memory, uncached memory + * etc. + * + * Copyright 2005 (C) Jes Sorensen <jes@trained-monkey.org> + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +/* + * General purpose special memory pool descriptor. + */ +struct gen_pool { + rwlock_t lock; + struct list_head chunks; /* list of chunks in this pool */ + int min_alloc_order; /* minimum allocation order */ +}; + +/* + * General purpose special memory pool chunk descriptor. + */ +struct gen_pool_chunk { + spinlock_t lock; + struct list_head next_chunk; /* next chunk in pool */ + unsigned long start_addr; /* starting address of memory chunk */ + unsigned long end_addr; /* ending address of memory chunk */ + unsigned long bits[0]; /* bitmap for allocating memory chunk */ +}; + +/** + * gen_pool_create - create a new special memory pool + * @min_alloc_order: log base 2 of number of bytes each bitmap bit represents + * @nid: node id of the node the pool structure should be allocated on, or -1 + * + * Create a new special memory pool that can be used to manage special purpose + * memory not managed by the regular kmalloc/kfree interface. + */ +static struct gen_pool *gen_pool_create(int min_alloc_order, int nid) +{ + struct gen_pool *pool; + + pool = kmalloc_node(sizeof(struct gen_pool), GFP_KERNEL, nid); + if (pool != NULL) { + rwlock_init(&pool->lock); + INIT_LIST_HEAD(&pool->chunks); + pool->min_alloc_order = min_alloc_order; + } + return pool; +} + +/** + * gen_pool_add - add a new chunk of special memory to the pool + * @pool: pool to add new memory chunk to + * @addr: starting address of memory chunk to add to pool + * @size: size in bytes of the memory chunk to add to pool + * @nid: node id of the node the chunk structure and bitmap should be + * allocated on, or -1 + * + * Add a new chunk of special memory to the specified pool. + */ +static int gen_pool_add(struct gen_pool *pool, unsigned long addr, size_t size, + int nid) +{ + struct gen_pool_chunk *chunk; + int nbits = size >> pool->min_alloc_order; + int nbytes = sizeof(struct gen_pool_chunk) + + (nbits + BITS_PER_BYTE - 1) / BITS_PER_BYTE; + + chunk = kmalloc_node(nbytes, GFP_KERNEL, nid); + if (unlikely(chunk == NULL)) + return -1; + + memset(chunk, 0, nbytes); + spin_lock_init(&chunk->lock); + chunk->start_addr = addr; + chunk->end_addr = addr + size; + + write_lock(&pool->lock); + list_add(&chunk->next_chunk, &pool->chunks); + write_unlock(&pool->lock); + + return 0; +} + +/** + * gen_pool_destroy - destroy a special memory pool + * @pool: pool to destroy + * + * Destroy the specified special memory pool. Verifies that there are no + * outstanding allocations. + */ +static void gen_pool_destroy(struct gen_pool *pool) +{ + struct list_head *_chunk, *_next_chunk; + struct gen_pool_chunk *chunk; + int order = pool->min_alloc_order; + int bit, end_bit; + + + write_lock(&pool->lock); + list_for_each_safe(_chunk, _next_chunk, &pool->chunks) { + chunk = list_entry(_chunk, struct gen_pool_chunk, next_chunk); + list_del(&chunk->next_chunk); + + end_bit = (chunk->end_addr - chunk->start_addr) >> order; + bit = find_next_bit(chunk->bits, end_bit, 0); + BUG_ON(bit < end_bit); + + kfree(chunk); + } + kfree(pool); + return; +} + +/** + * gen_pool_alloc - allocate special memory from the pool + * @pool: pool to allocate from + * @size: number of bytes to allocate from the pool + * + * Allocate the requested number of bytes from the specified pool. + * Uses a first-fit algorithm. + */ +static unsigned long gen_pool_alloc(struct gen_pool *pool, size_t size) +{ + struct list_head *_chunk; + struct gen_pool_chunk *chunk; + unsigned long addr, flags; + int order = pool->min_alloc_order; + int nbits, bit, start_bit, end_bit; + + if (size == 0) + return 0; + + nbits = (size + (1UL << order) - 1) >> order; + + read_lock(&pool->lock); + list_for_each(_chunk, &pool->chunks) { + chunk = list_entry(_chunk, struct gen_pool_chunk, next_chunk); + + end_bit = (chunk->end_addr - chunk->start_addr) >> order; + end_bit -= nbits + 1; + + spin_lock_irqsave(&chunk->lock, flags); + bit = -1; + while (bit + 1 < end_bit) { + bit = find_next_zero_bit(chunk->bits, end_bit, bit + 1); + if (bit >= end_bit) + break; + + start_bit = bit; + if (nbits > 1) { + bit = find_next_bit(chunk->bits, bit + nbits, + bit + 1); + if (bit - start_bit < nbits) + continue; + } + + addr = chunk->start_addr + + ((unsigned long)start_bit << order); + while (nbits--) + __set_bit(start_bit++, chunk->bits); + spin_unlock_irqrestore(&chunk->lock, flags); + read_unlock(&pool->lock); + return addr; + } + spin_unlock_irqrestore(&chunk->lock, flags); + } + read_unlock(&pool->lock); + return 0; +} + +/** + * gen_pool_free - free allocated special memory back to the pool + * @pool: pool to free to + * @addr: starting address of memory to free back to pool + * @size: size in bytes of memory to free + * + * Free previously allocated special memory back to the specified pool. + */ +static void gen_pool_free(struct gen_pool *pool, unsigned long addr, size_t size) +{ + struct list_head *_chunk; + struct gen_pool_chunk *chunk; + unsigned long flags; + int order = pool->min_alloc_order; + int bit, nbits; + + nbits = (size + (1UL << order) - 1) >> order; + + read_lock(&pool->lock); + list_for_each(_chunk, &pool->chunks) { + chunk = list_entry(_chunk, struct gen_pool_chunk, next_chunk); + + if (addr >= chunk->start_addr && addr < chunk->end_addr) { + BUG_ON(addr + size > chunk->end_addr); + spin_lock_irqsave(&chunk->lock, flags); + bit = (addr - chunk->start_addr) >> order; + while (nbits--) + __clear_bit(bit++, chunk->bits); + spin_unlock_irqrestore(&chunk->lock, flags); + break; + } + } + BUG_ON(nbits > 0); + read_unlock(&pool->lock); +} Added: projects/ofed/base/sys/ofed/drivers/infiniband/hw/nes/nes_ud.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ projects/ofed/base/sys/ofed/drivers/infiniband/hw/nes/nes_ud.c Mon Jan 3 05:36:16 2011 (r216910) @@ -0,0 +1,2174 @@ +/* + * Copyright (c) 2009 - 2010 Intel Corporation. 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. + */ + +#include <linux/version.h> +#include <linux/vmalloc.h> +#include <linux/completion.h> +#include <linux/mutex.h> +#include <linux/poll.h> +#include <linux/idr.h> +#include <linux/in.h> +#include <linux/in6.h> +#include <linux/device.h> +#include <linux/netdevice.h> +#include <linux/list.h> +#include <linux/miscdevice.h> +#include <linux/device.h> + +#include <rdma/ib_umem.h> +#include <rdma/ib_user_verbs.h> + +#include "nes.h" +#include "nes_ud.h" + +#define NES_UD_BASE_XMIT_NIC_QPID 28 +#define NES_UD_BASE_RECV_NIC_IDX 12 +#define NES_UD_BASE_XMIT_NIC_IDX 8 +#define NES_UD_MAX_NIC_CNT 8 +#define NES_UD_CLEANUP_TIMEOUT (HZ) +#define NES_UD_MCAST_TBL_SZ 128 +#define NES_UD_SINGLE_HDR_SZ 64 +#define NES_UD_CQE_NUM NES_NIC_WQ_SIZE +#define NES_UD_SKSQ_WAIT_TIMEOUT 100000 +#define NES_UD_MAX_REG_CNT 128 +#define NES_UD_MAX_MCAST_PER_QP 40 + +#define NES_UD_MAX_ADAPTERS 4 /* number of supported interfaces for RAW ETH */ + +#define NES_UD_MAX_REG_HASH_CNT 256 /* last byte of the STAG is hash key */ + +/* + * the same multicast could be allocated up to 2 owners so there could be + * two differentmcast entries allocated for the same mcas address + */ +struct nes_ud_file; +struct nes_ud_mcast { + u8 addr[3]; + u8 in_use; + struct nes_ud_file *owner; + u8 nic_mask; +}; + +struct nes_ud_mem_region { + struct list_head list; + dma_addr_t *addrs; + u64 va; + u64 length; + u32 pg_cnt; + u32 in_use; + u32 stag; /* stag related this structure */ +}; + +struct nic_queue_info { + u32 qpn; + u32 nic_index; + u32 logical_port; + enum nes_ud_dev_priority prio; + enum nes_ud_queue_type queue_type; + struct nes_ud_file *file; + struct nes_ud_file file_body; +}; + +struct nes_ud_resources { + int num_logport_confed; + int num_allocated_nics; + u8 logport_2_map; + u8 logport_3_map; + u32 original_6000; + u32 original_60b8; + struct nic_queue_info nics[NES_UD_MAX_NIC_CNT]; + struct mutex mutex; + struct nes_ud_mcast mcast[NES_UD_MCAST_TBL_SZ]; + u32 adapter_no; /* the allocated adapter no */ + + /* the unique ID of the NE020 adapter */ + /*- it is allocated once per HW */ + struct nes_adapter *pAdap; +}; + +/* memory hash list entry */ +struct nes_ud_hash_mem { + struct list_head list; + int read_stats; +}; + + + +struct nes_ud_mem { + /* hash list of registered STAGs */ + struct nes_ud_hash_mem mrs[NES_UD_MAX_REG_HASH_CNT]; + struct mutex mutex; +}; + +/* the QP in format x.y.z where x is adapter no, */ +/* y is ud file idx in adapter, z is a qp no */ +static struct nes_ud_mem ud_mem; + +struct nes_ud_send_wr { + u32 wr_cnt; + u32 qpn; + u32 flags; + u32 resv[1]; + struct ib_sge sg_list[64]; +}; + +struct nes_ud_recv_wr { + u32 wr_cnt; + u32 qpn; + u32 resv[2]; + struct ib_sge sg_list[64]; +}; + +static struct nes_ud_resources nes_ud_rsc[NES_UD_MAX_ADAPTERS]; +static struct workqueue_struct *nes_ud_workqueue; + +/* + * locate_ud_adapter + * + * the function locates the UD adapter +* on base of the adapter unique ID (structure nes_adapter) + */ +static inline +struct nes_ud_resources *locate_ud_adapter(struct nes_adapter *pAdapt) +{ + int i; + struct nes_ud_resources *pRsc; + + for (i = 0; i < NES_UD_MAX_ADAPTERS; i++) { + pRsc = &nes_ud_rsc[i]; + + if (pRsc->pAdap == pAdapt) + return pRsc; + + } + return NULL; +} + +/* + * allocate_ud_adapter() + * + * function allocates a new adapter + */ +static inline +struct nes_ud_resources *allocate_ud_adapter(struct nes_adapter *pAdapt) +{ + int i; + struct nes_ud_resources *pRsc; + + for (i = 0; i < NES_UD_MAX_ADAPTERS; i++) { + pRsc = &nes_ud_rsc[i]; + if (pRsc->pAdap == NULL) { + pRsc->pAdap = pAdapt; + nes_debug(NES_DBG_UD, "new UD Adapter allocated %d" + " for adapter %p no =%d\n", i, pAdapt, pRsc->adapter_no); + return pRsc; + } + } + nes_debug(NES_DBG_UD, "Unable to allocate adapter\n"); + return NULL; +} + +static inline +struct nes_ud_file *allocate_nic_queue(struct nes_vnic *nesvnic, + enum nes_ud_queue_type queue_type) +{ + struct nes_ud_file *file = NULL; + int i = 0; + u8 select_log_port = 0xf; + struct nes_device *nesdev = nesvnic->nesdev; + int log_port_2_alloced = 0; + int log_port_3_alloced = 0; + int ret = 0; + struct nes_ud_resources *pRsc; + + /* the first thing that must be done is determine the adapter */ + /* number max the adapter could have up to 2 interfaces */ + if (nesvnic->nic_index != 0 && nesvnic->nic_index != 1) { + nes_debug(NES_DBG_UD, "nic queue allocation failed" + " nesvnic->nic_index = %d\n", nesvnic->nic_index); + return NULL; + } + + /* locate device on base of nesvnic */ + /* - when it is an unknown card a new one is allocated */ + pRsc = locate_ud_adapter(nesdev->nesadapter); + if (pRsc == NULL) + return NULL; + + for (i = 0; i < NES_UD_MAX_NIC_CNT; i++) { + if (pRsc->nics[i].file->active == 0) + continue; + if (pRsc->nics[i].logical_port == 2 && + queue_type == pRsc->nics[i].queue_type) + log_port_2_alloced++; + if (pRsc->nics[i].logical_port == 3 && + queue_type == pRsc->nics[i].queue_type) + log_port_3_alloced++; + } + + /* check dual/single card */ + if (pRsc->logport_2_map != pRsc->logport_3_map) { + /* a dual port card */ + /* allocation is NIC2, NIC2, NIC3, NIC3 */ + /*- no RX packat replication supported */ + if (log_port_2_alloced < 2 && + pRsc->logport_2_map == nesvnic->nic_index) + select_log_port = 2; + else if (log_port_3_alloced < 2 && + pRsc->logport_3_map == nesvnic->nic_index) + select_log_port = 3; + } else { + /* single port card */ + /* change allocation scheme to NIC2,NIC3,NIC2,NIC3 */ + switch (log_port_2_alloced + log_port_3_alloced) { + case 0: /* no QPs allocated - use NIC2 */ + if (pRsc->logport_2_map == nesvnic->nic_index) + select_log_port = 2; + + break; + case 1: /* NIC2 or NIC3 allocated */ + if (log_port_2_alloced > 0) { + /* if NIC2 allocated use NIC3 */ + if (pRsc->logport_3_map == nesvnic->nic_index) + select_log_port = 3; + + } else { + /* when NIC3 allocated use NIC2 */ + if (pRsc->logport_2_map == nesvnic->nic_index) + select_log_port = 2; + + } + break; + + case 2: + /* NIC2 and NIC3 allocated or both ports on NIC3 - use NIC2 */ + if ((log_port_2_alloced == 1) || + (log_port_3_alloced == 2)) { + if (pRsc->logport_2_map == nesvnic->nic_index) + select_log_port = 2; + + } else { + /* both ports allocated on NIC2 - use NIC3 */ + if (pRsc->logport_3_map == nesvnic->nic_index) + select_log_port = 3; + + } + break; + case 3: + /* when both NIC2 allocated use NIC3 */ + if (log_port_2_alloced == 2) { + if (pRsc->logport_3_map == nesvnic->nic_index) + select_log_port = 3; + + } else { + /* when both NIC3 alloced use NIC2 */ + if (pRsc->logport_2_map == nesvnic->nic_index) + select_log_port = 2; + } + break; + + default: + break; + } + } + if (select_log_port == 0xf) { + ret = -1; + nes_debug(NES_DBG_UD, "%s(%d) logport allocation failed " + "log_port_2_alloced=%d log_port_3_alloced=%d\n", + __func__, __LINE__, log_port_2_alloced, + log_port_3_alloced); + goto out; + } + + nes_debug(NES_DBG_UD, "%s(%d) log_port_2_alloced=%d " + "log_port_3_alloced=%d select_log_port=%d\n", + __func__, __LINE__, log_port_2_alloced, + log_port_3_alloced, select_log_port); + + for (i = 0; i < NES_UD_MAX_NIC_CNT; i++) { + if (pRsc->nics[i].file->active == 1) + continue; + if (pRsc->nics[i].logical_port == select_log_port && + queue_type == pRsc->nics[i].queue_type) { + + /* file is preallocated during initialization */ + file = pRsc->nics[i].file; + memset(file, 0, sizeof(*file)); + + file->nesvnic = nesvnic; + file->queue_type = queue_type; + + file->prio = pRsc->nics[i].prio; + file->qpn = pRsc->nics[i].qpn; + file->nes_ud_nic_index = pRsc->nics[i].nic_index; + file->rsc_idx = i; + file->adapter_no = pRsc->adapter_no; + goto out; + } + } + +out: + return file; +} + +static inline int del_rsc_list(struct nes_ud_file *file) +{ + int logport_2_cnt = 0; + int logport_3_cnt = 0; + struct nes_device *nesdev = file->nesvnic->nesdev; + int i = 0; + struct nes_ud_resources *pRsc; + + if (file == NULL) { + nes_debug(NES_DBG_UD, "%s(%d) file is NULL\n", + __func__, __LINE__); + return -EFAULT; + } + if (file->nesvnic == NULL) { + nes_debug(NES_DBG_UD, "%s(%d) file->nesvnic is NULL\n", + __func__, __LINE__); + return -EFAULT; + } + if (nesdev == NULL) { + nes_debug(NES_DBG_UD, "%s(%d) nesdev is NULL\n", + __func__, __LINE__); + return -EFAULT; + } + + /* locate device on base of nesvnic */ + /*- when it is an unknown card a new one is allocated */ + pRsc = locate_ud_adapter(nesdev->nesadapter); + if (pRsc == NULL) { + nes_debug(NES_DBG_UD, "%s(%d) cannot locate an allocated " + "adapter is NULL\n", __func__, __LINE__); + return -EFAULT; + } + if (pRsc->num_allocated_nics == 0) + return 0; + + if (--pRsc->num_allocated_nics == 0) { + nes_write_indexed(nesdev, 0x60b8, pRsc->original_60b8); + nes_write_indexed(nesdev, 0x6000, pRsc->original_6000); + pRsc->num_logport_confed = 0; + } + BUG_ON(file->rsc_idx >= NES_UD_MAX_NIC_CNT); + + for (i = 0; i < NES_UD_MAX_NIC_CNT; i++) { + if (pRsc->nics[i].file->active && + pRsc->nics[i].logical_port == 2) + logport_2_cnt++; + if (pRsc->nics[i].file->active && + pRsc->nics[i].logical_port == 3) + logport_3_cnt++; + } + + if (pRsc->num_logport_confed != 0x3 && logport_2_cnt == 0) + pRsc->logport_2_map = 0xf; + + if (pRsc->num_logport_confed != 0x3 && logport_3_cnt == 0) + pRsc->logport_3_map = 0xf; + return 0; +} + +/* +* the QPN contains now the number of the RAW ETH +* adapter and QPN number on the adapter +* the adapter number is located in the highier +* 8 bits so QPN is stored as [adapter:qpn] +*/ +static inline +struct nes_ud_file *get_file_by_qpn(struct nes_ud_resources *pRsc, int qpn) +{ + int i = 0; + + for (i = 0; i < NES_UD_MAX_NIC_CNT; i++) { + if (pRsc->nics[i].file->active && + pRsc->nics[i].qpn == (qpn & 0xff)) + return pRsc->nics[i].file; + + } + return NULL; +} + +/* function counts all ETH RAW entities that have */ +/* a specific type and relation to specific vnic */ +static inline +int count_files_by_nic(struct nes_vnic *nesvnic, + enum nes_ud_queue_type queue_type) +{ + int count = 0; + int i = 0; + struct nes_ud_resources *pRsc; + + pRsc = locate_ud_adapter(nesvnic->nesdev->nesadapter); + if (pRsc == NULL) + return 0; + + for (i = 0; i < NES_UD_MAX_NIC_CNT; i++) { + if (pRsc->nics[i].file->active && + pRsc->nics[i].file->nesvnic == nesvnic && + pRsc->nics[i].queue_type == queue_type) + count++; + } + return count; +} + +/* function counts all RAW ETH entities the have a specific type */ +static inline +int count_files(struct nes_vnic *nesvnic, enum nes_ud_queue_type queue_type) +{ + int count = 0; + int i = 0; + struct nes_ud_resources *pRsc; + + pRsc = locate_ud_adapter(nesvnic->nesdev->nesadapter); + if (pRsc == NULL) + return 0; + + for (i = 0; i < NES_UD_MAX_NIC_CNT; i++) { + if (pRsc->nics[i].file->active && + pRsc->nics[i].queue_type == queue_type) + count++; + } + return count; +} + +/* + * the function locates the entry allocated by IGMP and modifies the + * PFT entry with the list of the NICs allowed to receive that multicast + * the NIC0/NIC1 are removed due to performance issue so tcpdum + * like tools cannot receive the accelerated multicasts + */ +static void mcast_fix_filter_table_single(struct nes_ud_file *file, u8 *addr) +{ + struct nes_device *nesdev = file->nesvnic->nesdev; + int i = 0; + u32 macaddr_low; + u32 orig_low; + u32 macaddr_high; + u32 prev_high; + + for (i = 0; i < 48; i++) { + macaddr_low = nes_read_indexed(nesdev, + NES_IDX_PERFECT_FILTER_LOW + i*8); + orig_low = macaddr_low; + macaddr_high = nes_read_indexed(nesdev, + NES_IDX_PERFECT_FILTER_LOW + 4 + i*8); + if (!(macaddr_high & NES_MAC_ADDR_VALID)) + continue; + if ((macaddr_high & 0xffff) != 0x0100) + continue; + if ((macaddr_low & 0xff) != addr[2]) + continue; + macaddr_low >>= 8; + if ((macaddr_low & 0xff) != addr[1]) + continue; + macaddr_low >>= 8; + if ((macaddr_low & 0xff) != addr[0]) + continue; + macaddr_low >>= 8; + if ((macaddr_low & 0xff) != 0x5e) + continue; + /* hit - that means Linux or other UD set this bit earlier */ + prev_high = macaddr_high; + nes_write_indexed(nesdev, NES_IDX_PERFECT_FILTER_LOW + 4 + i*8, 0); + macaddr_high = (macaddr_high & 0xfffcffff) | + ((1<<file->nes_ud_nic_index) << 16); + + nes_debug(NES_DBG_UD, "%s(%d) found addr to fix, " + "i=%d, macaddr_high=0x%x macaddr_low=0x%x " + "nic_idx=%d prev_high=0x%x\n", + __func__, __LINE__, i, macaddr_high, orig_low, + file->nes_ud_nic_index, prev_high); + nes_write_indexed(nesdev, + NES_IDX_PERFECT_FILTER_LOW + 4 + i*8, macaddr_high); + break; + } +} + +/* this function is implemented that way because the Linux multicast API + use the multicast list approach. When a new multicast address is added + all PFT table is reinitialized by linux and all entries must be fixed + by this procedure +*/ +static void mcast_fix_filter_table(struct nes_ud_file *file) +{ + int i; + struct nes_ud_resources *pRsc; + + pRsc = locate_ud_adapter(file->nesvnic->nesdev->nesadapter); + if (pRsc == NULL) + return; + + for (i = 0; i < NES_UD_MCAST_TBL_SZ; i++) { + if (pRsc->mcast[i].in_use != 0) + mcast_fix_filter_table_single(pRsc->mcast[i].owner, + pRsc->mcast[i].addr); + } +} + +/* function invalidates the PFT entry */ +static void remove_mcast_from_pft(struct nes_ud_file *file, u8 *addr) +{ + struct nes_device *nesdev = file->nesvnic->nesdev; + int i = 0; + u32 macaddr_low; + u32 orig_low; + u32 macaddr_high; + u32 prev_high; + + for (i = 0; i < 48; i++) { + macaddr_low = nes_read_indexed(nesdev, + NES_IDX_PERFECT_FILTER_LOW + i*8); + orig_low = macaddr_low; + macaddr_high = nes_read_indexed(nesdev, + NES_IDX_PERFECT_FILTER_LOW + 4 + i*8); + if (!(macaddr_high & NES_MAC_ADDR_VALID)) + continue; + + if ((macaddr_high & 0xffff) != 0x0100) + continue; + if ((macaddr_low & 0xff) != addr[2]) + continue; + macaddr_low >>= 8; + if ((macaddr_low & 0xff) != addr[1]) + continue; + macaddr_low >>= 8; + if ((macaddr_low & 0xff) != addr[0]) + continue; + macaddr_low >>= 8; + if ((macaddr_low & 0xff) != 0x5e) + continue; + /* hit - that means Linux or other UD set this bit earlier */ + /* so remove the NIC from MAC address reception */ + prev_high = macaddr_high; + macaddr_high = (macaddr_high & 0xfffcffff) & + ~((1<<file->nes_ud_nic_index) << 16); + nes_debug(NES_DBG_UD, "%s(%d) found addr to mcast remove," + "i=%d, macaddr_high=0x%x macaddr_low=0x%x " + "nic_idx=%d prev_high=0x%x\n", __func__, __LINE__, i, + macaddr_high, orig_low, file->nes_ud_nic_index, prev_high); + nes_write_indexed(nesdev, NES_IDX_PERFECT_FILTER_LOW + 4 + i*8, + macaddr_high); + break; + } + +} + +/* +* the function returns a mask of the NICs +* assotiated with given multicast address +*/ +static int nes_ud_mcast_filter(struct nes_vnic *nesvnic, __u8 *dmi_addr) +{ + int i = 0; + int ret = 0; + int mask = 0; + struct nes_ud_resources *pRsc; + + pRsc = locate_ud_adapter(nesvnic->nesdev->nesadapter); + if (pRsc == NULL) + return 0; + + for (i = 0; i < NES_UD_MCAST_TBL_SZ; i++) { + if (pRsc->mcast[i].in_use && + pRsc->mcast[i].addr[0] == dmi_addr[3] && + pRsc->mcast[i].addr[1] == dmi_addr[4] && + pRsc->mcast[i].addr[2] == dmi_addr[5]) { + mask = (pRsc->mcast[i].owner->mcast_mode == + NES_UD_MCAST_PFT_MODE) ? + pRsc->mcast[i].owner->nes_ud_nic_index : 0; + + ret = ret | (1 << mask); + nes_debug(NES_DBG_UD, "mcast filter, " + "fpr=%02X%02X%02X ret=%d\n", + dmi_addr[3], dmi_addr[4], dmi_addr[5], ret); + } + } + if (ret == 0) + return -1; + else + return ret; + +} + +static __u32 mqueue_key[4] = { 0x0, 0x80, 0x0, 0x0 }; + +static inline __u8 nes_ud_calculate_hash(__u8 dest_addr_lsb) +{ + __u8 in[8]; + __u32 key_arr[4]; + int i; + __u32 result = 0; + int j, k; + __u8 shift_in, next_shift_in; + + in[0] = 0; + in[1] = 0; + in[2] = 0; + in[3] = 0; + + in[4] = 0; + + in[5] = 0; + in[6] = 0; + in[7] = dest_addr_lsb; + + + + for (i = 0; i < 4; i++) + key_arr[3-i] = mqueue_key[i]; + + + + for (i = 0; i < 8; i++) { + for (j = 7; j >= 0; j--) { + if (in[i] & (1 << j)) + result = result ^ key_arr[0]; + + shift_in = 0; + for (k = 3; k >= 0; k--) { + next_shift_in = key_arr[k] >> 31; + key_arr[k] = (key_arr[k] << 1) + shift_in; + shift_in = next_shift_in; + } + } + } + return result & 0x7f; +} + +static inline void nes_ud_enable_mqueue(struct nes_ud_file *file) +{ + struct nes_device *nesdev = file->nesvnic->nesdev; + int mqueue_config0; + int mqueue_config2; + int instance = file->nes_ud_nic_index & 0x1; + + mqueue_config0 = nes_read_indexed(nesdev, 0x6400); + mqueue_config0 |= (4 | (instance & 0x3)) << (file->nes_ud_nic_index*3); + nes_write_indexed(nesdev, 0x6400, mqueue_config0); + mqueue_config0 = nes_read_indexed(nesdev, 0x6400); + + mqueue_config2 = nes_read_indexed(nesdev, 0x6408); + mqueue_config2 |= (2 << (instance*2)) | (6 << (instance*3+8)); + nes_write_indexed(nesdev, 0x6408, mqueue_config2); + mqueue_config2 = nes_read_indexed(nesdev, 0x6408); + + nes_write_indexed(nesdev, 0x64a0+instance*0x100, mqueue_key[0]); + nes_write_indexed(nesdev, 0x64a4+instance*0x100, mqueue_key[1]); + nes_write_indexed(nesdev, 0x64a8+instance*0x100, mqueue_key[2]); + nes_write_indexed(nesdev, 0x64ac+instance*0x100, mqueue_key[3]); + + nes_debug(NES_DBG_UD, "mq_config0=0x%x mq_config2=0x%x nic_idx= %d\n", + mqueue_config0, mqueue_config2, file->nes_ud_nic_index); + +} + + + +static inline +void nes_ud_redirect_from_mqueue(struct nes_ud_file *file, int num_queues) +{ + struct nes_device *nesdev = file->nesvnic->nesdev; + int instance = file->nes_ud_nic_index & 0x1; + unsigned addr = 0x6420+instance*0x100; + unsigned value; + int i; + + value = (file->prio == NES_UD_DEV_PRIO_LOW || num_queues == 1) ? + 0x0 : 0x11111111; + for (i = 0; i < 16; i++) + nes_write_indexed(nesdev, addr+i*4, value); +} + + +static int nes_ud_create_nic(struct nes_ud_file *file) +{ + struct nes_vnic *nesvnic = file->nesvnic; + struct nes_device *nesdev = nesvnic->nesdev; + struct nes_hw_nic_qp_context *nic_context; + struct nes_hw_cqp_wqe *cqp_wqe; + struct nes_cqp_request *cqp_request; + unsigned long flags; + void *vmem; + dma_addr_t pmem; + u64 u64temp; + int ret = 0; + + BUG_ON(file->nic_vbase != NULL); + + file->nic_mem_size = 256 + + (NES_NIC_WQ_SIZE * sizeof(struct nes_hw_nic_sq_wqe)) + + sizeof(struct nes_hw_nic_qp_context); + + file->nic_vbase = pci_alloc_consistent(nesdev->pcidev, + file->nic_mem_size, + &file->nic_pbase); + if (!file->nic_vbase) { + nes_debug(NES_DBG_UD, "Unable to allocate memory for NIC host " + "descriptor rings\n"); + return -ENOMEM; + } + + memset(file->nic_vbase, 0, file->nic_mem_size); + + vmem = (void *)(((unsigned long long)file->nic_vbase + (256 - 1)) & + ~(unsigned long long)(256 - 1)); + pmem = (dma_addr_t)(((unsigned long long)file->nic_pbase + (256 - 1)) & + ~(unsigned long long)(256 - 1)); + + file->wq_vbase = vmem; + file->wq_pbase = pmem; + file->head = 0; + file->tail = 0; + + vmem += (NES_NIC_WQ_SIZE * sizeof(struct nes_hw_nic_sq_wqe)); + pmem += (NES_NIC_WQ_SIZE * sizeof(struct nes_hw_nic_sq_wqe)); + + cqp_request = nesvnic->get_cqp_request(nesdev); + if (cqp_request == NULL) { + nes_debug(NES_DBG_QP, "Failed to get a cqp_request.\n"); + goto fail_cqp_req_alloc; + } + cqp_request->waiting = 1; + cqp_wqe = &cqp_request->cqp_wqe; + + cqp_wqe->wqe_words[NES_CQP_WQE_OPCODE_IDX] = + cpu_to_le32(NES_CQP_CREATE_QP | NES_CQP_QP_TYPE_NIC); + cqp_wqe->wqe_words[NES_CQP_WQE_ID_IDX] = cpu_to_le32(file->qpn); + cqp_wqe->wqe_words[NES_CQP_WQE_COMP_CTX_LOW_IDX] = + cpu_to_le32((u32)((u64)(&nesdev->cqp))); + cqp_wqe->wqe_words[NES_CQP_WQE_COMP_CTX_HIGH_IDX] = + cpu_to_le32((u32)(((u64)(&nesdev->cqp))>>32)); + cqp_wqe->wqe_words[NES_CQP_WQE_COMP_SCRATCH_LOW_IDX] = 0; + cqp_wqe->wqe_words[NES_CQP_WQE_COMP_SCRATCH_HIGH_IDX] = 0; + + + nic_context = vmem; + + nic_context->context_words[NES_NIC_CTX_MISC_IDX] = + cpu_to_le32((u32)NES_NIC_CTX_SIZE | + ((u32)PCI_FUNC(nesdev->pcidev->devfn) << 12) | + (1 << 18)); + + nic_context->context_words[NES_NIC_CTX_SQ_LOW_IDX] = 0; + nic_context->context_words[NES_NIC_CTX_SQ_HIGH_IDX] = 0; + nic_context->context_words[NES_NIC_CTX_RQ_LOW_IDX] = 0; *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201101030536.p035aGW2076019>