diff options
Diffstat (limited to 'drivers/net/ethernet/mellanox/mlx5')
20 files changed, 6126 insertions, 0 deletions
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Kconfig b/drivers/net/ethernet/mellanox/mlx5/core/Kconfig new file mode 100644 index 00000000000..8ff57e8e3e9 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/Kconfig @@ -0,0 +1,8 @@ +# +# Mellanox driver configuration +# + +config MLX5_CORE +	tristate +	depends on PCI +	default n diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Makefile b/drivers/net/ethernet/mellanox/mlx5/core/Makefile new file mode 100644 index 00000000000..105780bb980 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/Makefile @@ -0,0 +1,5 @@ +obj-$(CONFIG_MLX5_CORE)		+= mlx5_core.o + +mlx5_core-y :=	main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \ +		health.o mcg.o cq.o srq.o alloc.o qp.o port.o mr.o pd.o   \ +		mad.o diff --git a/drivers/net/ethernet/mellanox/mlx5/core/alloc.c b/drivers/net/ethernet/mellanox/mlx5/core/alloc.c new file mode 100644 index 00000000000..b215742b842 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/alloc.c @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2013, Mellanox Technologies 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. + */ + +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/mm.h> +#include <linux/export.h> +#include <linux/bitmap.h> +#include <linux/dma-mapping.h> +#include <linux/vmalloc.h> +#include <linux/mlx5/driver.h> + +#include "mlx5_core.h" + +/* Handling for queue buffers -- we allocate a bunch of memory and + * register it in a memory region at HCA virtual address 0.  If the + * requested size is > max_direct, we split the allocation into + * multiple pages, so we don't require too much contiguous memory. + */ + +int mlx5_buf_alloc(struct mlx5_core_dev *dev, int size, int max_direct, +		   struct mlx5_buf *buf) +{ +	dma_addr_t t; + +	buf->size = size; +	if (size <= max_direct) { +		buf->nbufs        = 1; +		buf->npages       = 1; +		buf->page_shift   = get_order(size) + PAGE_SHIFT; +		buf->direct.buf   = dma_zalloc_coherent(&dev->pdev->dev, +							size, &t, GFP_KERNEL); +		if (!buf->direct.buf) +			return -ENOMEM; + +		buf->direct.map = t; + +		while (t & ((1 << buf->page_shift) - 1)) { +			--buf->page_shift; +			buf->npages *= 2; +		} +	} else { +		int i; + +		buf->direct.buf  = NULL; +		buf->nbufs       = (size + PAGE_SIZE - 1) / PAGE_SIZE; +		buf->npages      = buf->nbufs; +		buf->page_shift  = PAGE_SHIFT; +		buf->page_list   = kcalloc(buf->nbufs, sizeof(*buf->page_list), +					   GFP_KERNEL); +		if (!buf->page_list) +			return -ENOMEM; + +		for (i = 0; i < buf->nbufs; i++) { +			buf->page_list[i].buf = +				dma_zalloc_coherent(&dev->pdev->dev, PAGE_SIZE, +						    &t, GFP_KERNEL); +			if (!buf->page_list[i].buf) +				goto err_free; + +			buf->page_list[i].map = t; +		} + +		if (BITS_PER_LONG == 64) { +			struct page **pages; +			pages = kmalloc(sizeof(*pages) * buf->nbufs, GFP_KERNEL); +			if (!pages) +				goto err_free; +			for (i = 0; i < buf->nbufs; i++) +				pages[i] = virt_to_page(buf->page_list[i].buf); +			buf->direct.buf = vmap(pages, buf->nbufs, VM_MAP, PAGE_KERNEL); +			kfree(pages); +			if (!buf->direct.buf) +				goto err_free; +		} +	} + +	return 0; + +err_free: +	mlx5_buf_free(dev, buf); + +	return -ENOMEM; +} +EXPORT_SYMBOL_GPL(mlx5_buf_alloc); + +void mlx5_buf_free(struct mlx5_core_dev *dev, struct mlx5_buf *buf) +{ +	int i; + +	if (buf->nbufs == 1) +		dma_free_coherent(&dev->pdev->dev, buf->size, buf->direct.buf, +				  buf->direct.map); +	else { +		if (BITS_PER_LONG == 64 && buf->direct.buf) +			vunmap(buf->direct.buf); + +		for (i = 0; i < buf->nbufs; i++) +			if (buf->page_list[i].buf) +				dma_free_coherent(&dev->pdev->dev, PAGE_SIZE, +						  buf->page_list[i].buf, +						  buf->page_list[i].map); +		kfree(buf->page_list); +	} +} +EXPORT_SYMBOL_GPL(mlx5_buf_free); + +static struct mlx5_db_pgdir *mlx5_alloc_db_pgdir(struct device *dma_device) +{ +	struct mlx5_db_pgdir *pgdir; + +	pgdir = kzalloc(sizeof(*pgdir), GFP_KERNEL); +	if (!pgdir) +		return NULL; + +	bitmap_fill(pgdir->bitmap, MLX5_DB_PER_PAGE); +	pgdir->db_page = dma_alloc_coherent(dma_device, PAGE_SIZE, +					    &pgdir->db_dma, GFP_KERNEL); +	if (!pgdir->db_page) { +		kfree(pgdir); +		return NULL; +	} + +	return pgdir; +} + +static int mlx5_alloc_db_from_pgdir(struct mlx5_db_pgdir *pgdir, +				    struct mlx5_db *db) +{ +	int offset; +	int i; + +	i = find_first_bit(pgdir->bitmap, MLX5_DB_PER_PAGE); +	if (i >= MLX5_DB_PER_PAGE) +		return -ENOMEM; + +	__clear_bit(i, pgdir->bitmap); + +	db->u.pgdir = pgdir; +	db->index   = i; +	offset = db->index * L1_CACHE_BYTES; +	db->db      = pgdir->db_page + offset / sizeof(*pgdir->db_page); +	db->dma     = pgdir->db_dma  + offset; + +	return 0; +} + +int mlx5_db_alloc(struct mlx5_core_dev *dev, struct mlx5_db *db) +{ +	struct mlx5_db_pgdir *pgdir; +	int ret = 0; + +	mutex_lock(&dev->priv.pgdir_mutex); + +	list_for_each_entry(pgdir, &dev->priv.pgdir_list, list) +		if (!mlx5_alloc_db_from_pgdir(pgdir, db)) +			goto out; + +	pgdir = mlx5_alloc_db_pgdir(&(dev->pdev->dev)); +	if (!pgdir) { +		ret = -ENOMEM; +		goto out; +	} + +	list_add(&pgdir->list, &dev->priv.pgdir_list); + +	/* This should never fail -- we just allocated an empty page: */ +	WARN_ON(mlx5_alloc_db_from_pgdir(pgdir, db)); + +out: +	mutex_unlock(&dev->priv.pgdir_mutex); + +	return ret; +} +EXPORT_SYMBOL_GPL(mlx5_db_alloc); + +void mlx5_db_free(struct mlx5_core_dev *dev, struct mlx5_db *db) +{ +	mutex_lock(&dev->priv.pgdir_mutex); + +	__set_bit(db->index, db->u.pgdir->bitmap); + +	if (bitmap_full(db->u.pgdir->bitmap, MLX5_DB_PER_PAGE)) { +		dma_free_coherent(&(dev->pdev->dev), PAGE_SIZE, +				  db->u.pgdir->db_page, db->u.pgdir->db_dma); +		list_del(&db->u.pgdir->list); +		kfree(db->u.pgdir); +	} + +	mutex_unlock(&dev->priv.pgdir_mutex); +} +EXPORT_SYMBOL_GPL(mlx5_db_free); + + +void mlx5_fill_page_array(struct mlx5_buf *buf, __be64 *pas) +{ +	u64 addr; +	int i; + +	for (i = 0; i < buf->npages; i++) { +		if (buf->nbufs == 1) +			addr = buf->direct.map + (i << buf->page_shift); +		else +			addr = buf->page_list[i].map; + +		pas[i] = cpu_to_be64(addr); +	} +} +EXPORT_SYMBOL_GPL(mlx5_fill_page_array); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c new file mode 100644 index 00000000000..87d1b018a9c --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c @@ -0,0 +1,1577 @@ +/* + * Copyright (c) 2013, Mellanox Technologies 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. + */ + +#include <asm-generic/kmap_types.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/pci.h> +#include <linux/dma-mapping.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/random.h> +#include <linux/io-mapping.h> +#include <linux/mlx5/driver.h> +#include <linux/debugfs.h> + +#include "mlx5_core.h" + +enum { +	CMD_IF_REV = 5, +}; + +enum { +	CMD_MODE_POLLING, +	CMD_MODE_EVENTS +}; + +enum { +	NUM_LONG_LISTS	  = 2, +	NUM_MED_LISTS	  = 64, +	LONG_LIST_SIZE	  = (2ULL * 1024 * 1024 * 1024 / PAGE_SIZE) * 8 + 16 + +				MLX5_CMD_DATA_BLOCK_SIZE, +	MED_LIST_SIZE	  = 16 + MLX5_CMD_DATA_BLOCK_SIZE, +}; + +enum { +	MLX5_CMD_DELIVERY_STAT_OK			= 0x0, +	MLX5_CMD_DELIVERY_STAT_SIGNAT_ERR		= 0x1, +	MLX5_CMD_DELIVERY_STAT_TOK_ERR			= 0x2, +	MLX5_CMD_DELIVERY_STAT_BAD_BLK_NUM_ERR		= 0x3, +	MLX5_CMD_DELIVERY_STAT_OUT_PTR_ALIGN_ERR	= 0x4, +	MLX5_CMD_DELIVERY_STAT_IN_PTR_ALIGN_ERR		= 0x5, +	MLX5_CMD_DELIVERY_STAT_FW_ERR			= 0x6, +	MLX5_CMD_DELIVERY_STAT_IN_LENGTH_ERR		= 0x7, +	MLX5_CMD_DELIVERY_STAT_OUT_LENGTH_ERR		= 0x8, +	MLX5_CMD_DELIVERY_STAT_RES_FLD_NOT_CLR_ERR	= 0x9, +	MLX5_CMD_DELIVERY_STAT_CMD_DESCR_ERR		= 0x10, +}; + +enum { +	MLX5_CMD_STAT_OK			= 0x0, +	MLX5_CMD_STAT_INT_ERR			= 0x1, +	MLX5_CMD_STAT_BAD_OP_ERR		= 0x2, +	MLX5_CMD_STAT_BAD_PARAM_ERR		= 0x3, +	MLX5_CMD_STAT_BAD_SYS_STATE_ERR		= 0x4, +	MLX5_CMD_STAT_BAD_RES_ERR		= 0x5, +	MLX5_CMD_STAT_RES_BUSY			= 0x6, +	MLX5_CMD_STAT_LIM_ERR			= 0x8, +	MLX5_CMD_STAT_BAD_RES_STATE_ERR		= 0x9, +	MLX5_CMD_STAT_IX_ERR			= 0xa, +	MLX5_CMD_STAT_NO_RES_ERR		= 0xf, +	MLX5_CMD_STAT_BAD_INP_LEN_ERR		= 0x50, +	MLX5_CMD_STAT_BAD_OUTP_LEN_ERR		= 0x51, +	MLX5_CMD_STAT_BAD_QP_STATE_ERR		= 0x10, +	MLX5_CMD_STAT_BAD_PKT_ERR		= 0x30, +	MLX5_CMD_STAT_BAD_SIZE_OUTS_CQES_ERR	= 0x40, +}; + +static struct mlx5_cmd_work_ent *alloc_cmd(struct mlx5_cmd *cmd, +					   struct mlx5_cmd_msg *in, +					   struct mlx5_cmd_msg *out, +					   void *uout, int uout_size, +					   mlx5_cmd_cbk_t cbk, +					   void *context, int page_queue) +{ +	gfp_t alloc_flags = cbk ? GFP_ATOMIC : GFP_KERNEL; +	struct mlx5_cmd_work_ent *ent; + +	ent = kzalloc(sizeof(*ent), alloc_flags); +	if (!ent) +		return ERR_PTR(-ENOMEM); + +	ent->in		= in; +	ent->out	= out; +	ent->uout	= uout; +	ent->uout_size	= uout_size; +	ent->callback	= cbk; +	ent->context	= context; +	ent->cmd	= cmd; +	ent->page_queue = page_queue; + +	return ent; +} + +static u8 alloc_token(struct mlx5_cmd *cmd) +{ +	u8 token; + +	spin_lock(&cmd->token_lock); +	token = cmd->token++ % 255 + 1; +	spin_unlock(&cmd->token_lock); + +	return token; +} + +static int alloc_ent(struct mlx5_cmd *cmd) +{ +	unsigned long flags; +	int ret; + +	spin_lock_irqsave(&cmd->alloc_lock, flags); +	ret = find_first_bit(&cmd->bitmask, cmd->max_reg_cmds); +	if (ret < cmd->max_reg_cmds) +		clear_bit(ret, &cmd->bitmask); +	spin_unlock_irqrestore(&cmd->alloc_lock, flags); + +	return ret < cmd->max_reg_cmds ? ret : -ENOMEM; +} + +static void free_ent(struct mlx5_cmd *cmd, int idx) +{ +	unsigned long flags; + +	spin_lock_irqsave(&cmd->alloc_lock, flags); +	set_bit(idx, &cmd->bitmask); +	spin_unlock_irqrestore(&cmd->alloc_lock, flags); +} + +static struct mlx5_cmd_layout *get_inst(struct mlx5_cmd *cmd, int idx) +{ +	return cmd->cmd_buf + (idx << cmd->log_stride); +} + +static u8 xor8_buf(void *buf, int len) +{ +	u8 *ptr = buf; +	u8 sum = 0; +	int i; + +	for (i = 0; i < len; i++) +		sum ^= ptr[i]; + +	return sum; +} + +static int verify_block_sig(struct mlx5_cmd_prot_block *block) +{ +	if (xor8_buf(block->rsvd0, sizeof(*block) - sizeof(block->data) - 1) != 0xff) +		return -EINVAL; + +	if (xor8_buf(block, sizeof(*block)) != 0xff) +		return -EINVAL; + +	return 0; +} + +static void calc_block_sig(struct mlx5_cmd_prot_block *block, u8 token, +			   int csum) +{ +	block->token = token; +	if (csum) { +		block->ctrl_sig = ~xor8_buf(block->rsvd0, sizeof(*block) - +					    sizeof(block->data) - 2); +		block->sig = ~xor8_buf(block, sizeof(*block) - 1); +	} +} + +static void calc_chain_sig(struct mlx5_cmd_msg *msg, u8 token, int csum) +{ +	struct mlx5_cmd_mailbox *next = msg->next; + +	while (next) { +		calc_block_sig(next->buf, token, csum); +		next = next->next; +	} +} + +static void set_signature(struct mlx5_cmd_work_ent *ent, int csum) +{ +	ent->lay->sig = ~xor8_buf(ent->lay, sizeof(*ent->lay)); +	calc_chain_sig(ent->in, ent->token, csum); +	calc_chain_sig(ent->out, ent->token, csum); +} + +static void poll_timeout(struct mlx5_cmd_work_ent *ent) +{ +	unsigned long poll_end = jiffies + msecs_to_jiffies(MLX5_CMD_TIMEOUT_MSEC + 1000); +	u8 own; + +	do { +		own = ent->lay->status_own; +		if (!(own & CMD_OWNER_HW)) { +			ent->ret = 0; +			return; +		} +		usleep_range(5000, 10000); +	} while (time_before(jiffies, poll_end)); + +	ent->ret = -ETIMEDOUT; +} + +static void free_cmd(struct mlx5_cmd_work_ent *ent) +{ +	kfree(ent); +} + + +static int verify_signature(struct mlx5_cmd_work_ent *ent) +{ +	struct mlx5_cmd_mailbox *next = ent->out->next; +	int err; +	u8 sig; + +	sig = xor8_buf(ent->lay, sizeof(*ent->lay)); +	if (sig != 0xff) +		return -EINVAL; + +	while (next) { +		err = verify_block_sig(next->buf); +		if (err) +			return err; + +		next = next->next; +	} + +	return 0; +} + +static void dump_buf(void *buf, int size, int data_only, int offset) +{ +	__be32 *p = buf; +	int i; + +	for (i = 0; i < size; i += 16) { +		pr_debug("%03x: %08x %08x %08x %08x\n", offset, be32_to_cpu(p[0]), +			 be32_to_cpu(p[1]), be32_to_cpu(p[2]), +			 be32_to_cpu(p[3])); +		p += 4; +		offset += 16; +	} +	if (!data_only) +		pr_debug("\n"); +} + +const char *mlx5_command_str(int command) +{ +	switch (command) { +	case MLX5_CMD_OP_QUERY_HCA_CAP: +		return "QUERY_HCA_CAP"; + +	case MLX5_CMD_OP_SET_HCA_CAP: +		return "SET_HCA_CAP"; + +	case MLX5_CMD_OP_QUERY_ADAPTER: +		return "QUERY_ADAPTER"; + +	case MLX5_CMD_OP_INIT_HCA: +		return "INIT_HCA"; + +	case MLX5_CMD_OP_TEARDOWN_HCA: +		return "TEARDOWN_HCA"; + +	case MLX5_CMD_OP_ENABLE_HCA: +		return "MLX5_CMD_OP_ENABLE_HCA"; + +	case MLX5_CMD_OP_DISABLE_HCA: +		return "MLX5_CMD_OP_DISABLE_HCA"; + +	case MLX5_CMD_OP_QUERY_PAGES: +		return "QUERY_PAGES"; + +	case MLX5_CMD_OP_MANAGE_PAGES: +		return "MANAGE_PAGES"; + +	case MLX5_CMD_OP_CREATE_MKEY: +		return "CREATE_MKEY"; + +	case MLX5_CMD_OP_QUERY_MKEY: +		return "QUERY_MKEY"; + +	case MLX5_CMD_OP_DESTROY_MKEY: +		return "DESTROY_MKEY"; + +	case MLX5_CMD_OP_QUERY_SPECIAL_CONTEXTS: +		return "QUERY_SPECIAL_CONTEXTS"; + +	case MLX5_CMD_OP_CREATE_EQ: +		return "CREATE_EQ"; + +	case MLX5_CMD_OP_DESTROY_EQ: +		return "DESTROY_EQ"; + +	case MLX5_CMD_OP_QUERY_EQ: +		return "QUERY_EQ"; + +	case MLX5_CMD_OP_CREATE_CQ: +		return "CREATE_CQ"; + +	case MLX5_CMD_OP_DESTROY_CQ: +		return "DESTROY_CQ"; + +	case MLX5_CMD_OP_QUERY_CQ: +		return "QUERY_CQ"; + +	case MLX5_CMD_OP_MODIFY_CQ: +		return "MODIFY_CQ"; + +	case MLX5_CMD_OP_CREATE_QP: +		return "CREATE_QP"; + +	case MLX5_CMD_OP_DESTROY_QP: +		return "DESTROY_QP"; + +	case MLX5_CMD_OP_RST2INIT_QP: +		return "RST2INIT_QP"; + +	case MLX5_CMD_OP_INIT2RTR_QP: +		return "INIT2RTR_QP"; + +	case MLX5_CMD_OP_RTR2RTS_QP: +		return "RTR2RTS_QP"; + +	case MLX5_CMD_OP_RTS2RTS_QP: +		return "RTS2RTS_QP"; + +	case MLX5_CMD_OP_SQERR2RTS_QP: +		return "SQERR2RTS_QP"; + +	case MLX5_CMD_OP_2ERR_QP: +		return "2ERR_QP"; + +	case MLX5_CMD_OP_RTS2SQD_QP: +		return "RTS2SQD_QP"; + +	case MLX5_CMD_OP_SQD2RTS_QP: +		return "SQD2RTS_QP"; + +	case MLX5_CMD_OP_2RST_QP: +		return "2RST_QP"; + +	case MLX5_CMD_OP_QUERY_QP: +		return "QUERY_QP"; + +	case MLX5_CMD_OP_CONF_SQP: +		return "CONF_SQP"; + +	case MLX5_CMD_OP_MAD_IFC: +		return "MAD_IFC"; + +	case MLX5_CMD_OP_INIT2INIT_QP: +		return "INIT2INIT_QP"; + +	case MLX5_CMD_OP_SUSPEND_QP: +		return "SUSPEND_QP"; + +	case MLX5_CMD_OP_UNSUSPEND_QP: +		return "UNSUSPEND_QP"; + +	case MLX5_CMD_OP_SQD2SQD_QP: +		return "SQD2SQD_QP"; + +	case MLX5_CMD_OP_ALLOC_QP_COUNTER_SET: +		return "ALLOC_QP_COUNTER_SET"; + +	case MLX5_CMD_OP_DEALLOC_QP_COUNTER_SET: +		return "DEALLOC_QP_COUNTER_SET"; + +	case MLX5_CMD_OP_QUERY_QP_COUNTER_SET: +		return "QUERY_QP_COUNTER_SET"; + +	case MLX5_CMD_OP_CREATE_PSV: +		return "CREATE_PSV"; + +	case MLX5_CMD_OP_DESTROY_PSV: +		return "DESTROY_PSV"; + +	case MLX5_CMD_OP_QUERY_PSV: +		return "QUERY_PSV"; + +	case MLX5_CMD_OP_QUERY_SIG_RULE_TABLE: +		return "QUERY_SIG_RULE_TABLE"; + +	case MLX5_CMD_OP_QUERY_BLOCK_SIZE_TABLE: +		return "QUERY_BLOCK_SIZE_TABLE"; + +	case MLX5_CMD_OP_CREATE_SRQ: +		return "CREATE_SRQ"; + +	case MLX5_CMD_OP_DESTROY_SRQ: +		return "DESTROY_SRQ"; + +	case MLX5_CMD_OP_QUERY_SRQ: +		return "QUERY_SRQ"; + +	case MLX5_CMD_OP_ARM_RQ: +		return "ARM_RQ"; + +	case MLX5_CMD_OP_RESIZE_SRQ: +		return "RESIZE_SRQ"; + +	case MLX5_CMD_OP_ALLOC_PD: +		return "ALLOC_PD"; + +	case MLX5_CMD_OP_DEALLOC_PD: +		return "DEALLOC_PD"; + +	case MLX5_CMD_OP_ALLOC_UAR: +		return "ALLOC_UAR"; + +	case MLX5_CMD_OP_DEALLOC_UAR: +		return "DEALLOC_UAR"; + +	case MLX5_CMD_OP_ATTACH_TO_MCG: +		return "ATTACH_TO_MCG"; + +	case MLX5_CMD_OP_DETACH_FROM_MCG: +		return "DETACH_FROM_MCG"; + +	case MLX5_CMD_OP_ALLOC_XRCD: +		return "ALLOC_XRCD"; + +	case MLX5_CMD_OP_DEALLOC_XRCD: +		return "DEALLOC_XRCD"; + +	case MLX5_CMD_OP_ACCESS_REG: +		return "MLX5_CMD_OP_ACCESS_REG"; + +	default: return "unknown command opcode"; +	} +} + +static void dump_command(struct mlx5_core_dev *dev, +			 struct mlx5_cmd_work_ent *ent, int input) +{ +	u16 op = be16_to_cpu(((struct mlx5_inbox_hdr *)(ent->lay->in))->opcode); +	struct mlx5_cmd_msg *msg = input ? ent->in : ent->out; +	struct mlx5_cmd_mailbox *next = msg->next; +	int data_only; +	int offset = 0; +	int dump_len; + +	data_only = !!(mlx5_core_debug_mask & (1 << MLX5_CMD_DATA)); + +	if (data_only) +		mlx5_core_dbg_mask(dev, 1 << MLX5_CMD_DATA, +				   "dump command data %s(0x%x) %s\n", +				   mlx5_command_str(op), op, +				   input ? "INPUT" : "OUTPUT"); +	else +		mlx5_core_dbg(dev, "dump command %s(0x%x) %s\n", +			      mlx5_command_str(op), op, +			      input ? "INPUT" : "OUTPUT"); + +	if (data_only) { +		if (input) { +			dump_buf(ent->lay->in, sizeof(ent->lay->in), 1, offset); +			offset += sizeof(ent->lay->in); +		} else { +			dump_buf(ent->lay->out, sizeof(ent->lay->out), 1, offset); +			offset += sizeof(ent->lay->out); +		} +	} else { +		dump_buf(ent->lay, sizeof(*ent->lay), 0, offset); +		offset += sizeof(*ent->lay); +	} + +	while (next && offset < msg->len) { +		if (data_only) { +			dump_len = min_t(int, MLX5_CMD_DATA_BLOCK_SIZE, msg->len - offset); +			dump_buf(next->buf, dump_len, 1, offset); +			offset += MLX5_CMD_DATA_BLOCK_SIZE; +		} else { +			mlx5_core_dbg(dev, "command block:\n"); +			dump_buf(next->buf, sizeof(struct mlx5_cmd_prot_block), 0, offset); +			offset += sizeof(struct mlx5_cmd_prot_block); +		} +		next = next->next; +	} + +	if (data_only) +		pr_debug("\n"); +} + +static void cmd_work_handler(struct work_struct *work) +{ +	struct mlx5_cmd_work_ent *ent = container_of(work, struct mlx5_cmd_work_ent, work); +	struct mlx5_cmd *cmd = ent->cmd; +	struct mlx5_core_dev *dev = container_of(cmd, struct mlx5_core_dev, cmd); +	struct mlx5_cmd_layout *lay; +	struct semaphore *sem; + +	sem = ent->page_queue ? &cmd->pages_sem : &cmd->sem; +	down(sem); +	if (!ent->page_queue) { +		ent->idx = alloc_ent(cmd); +		if (ent->idx < 0) { +			mlx5_core_err(dev, "failed to allocate command entry\n"); +			up(sem); +			return; +		} +	} else { +		ent->idx = cmd->max_reg_cmds; +	} + +	ent->token = alloc_token(cmd); +	cmd->ent_arr[ent->idx] = ent; +	lay = get_inst(cmd, ent->idx); +	ent->lay = lay; +	memset(lay, 0, sizeof(*lay)); +	memcpy(lay->in, ent->in->first.data, sizeof(lay->in)); +	ent->op = be32_to_cpu(lay->in[0]) >> 16; +	if (ent->in->next) +		lay->in_ptr = cpu_to_be64(ent->in->next->dma); +	lay->inlen = cpu_to_be32(ent->in->len); +	if (ent->out->next) +		lay->out_ptr = cpu_to_be64(ent->out->next->dma); +	lay->outlen = cpu_to_be32(ent->out->len); +	lay->type = MLX5_PCI_CMD_XPORT; +	lay->token = ent->token; +	lay->status_own = CMD_OWNER_HW; +	set_signature(ent, !cmd->checksum_disabled); +	dump_command(dev, ent, 1); +	ktime_get_ts(&ent->ts1); + +	/* ring doorbell after the descriptor is valid */ +	wmb(); +	iowrite32be(1 << ent->idx, &dev->iseg->cmd_dbell); +	mlx5_core_dbg(dev, "write 0x%x to command doorbell\n", 1 << ent->idx); +	mmiowb(); +	if (cmd->mode == CMD_MODE_POLLING) { +		poll_timeout(ent); +		/* make sure we read the descriptor after ownership is SW */ +		rmb(); +		mlx5_cmd_comp_handler(dev, 1UL << ent->idx); +	} +} + +static const char *deliv_status_to_str(u8 status) +{ +	switch (status) { +	case MLX5_CMD_DELIVERY_STAT_OK: +		return "no errors"; +	case MLX5_CMD_DELIVERY_STAT_SIGNAT_ERR: +		return "signature error"; +	case MLX5_CMD_DELIVERY_STAT_TOK_ERR: +		return "token error"; +	case MLX5_CMD_DELIVERY_STAT_BAD_BLK_NUM_ERR: +		return "bad block number"; +	case MLX5_CMD_DELIVERY_STAT_OUT_PTR_ALIGN_ERR: +		return "output pointer not aligned to block size"; +	case MLX5_CMD_DELIVERY_STAT_IN_PTR_ALIGN_ERR: +		return "input pointer not aligned to block size"; +	case MLX5_CMD_DELIVERY_STAT_FW_ERR: +		return "firmware internal error"; +	case MLX5_CMD_DELIVERY_STAT_IN_LENGTH_ERR: +		return "command input length error"; +	case MLX5_CMD_DELIVERY_STAT_OUT_LENGTH_ERR: +		return "command ouput length error"; +	case MLX5_CMD_DELIVERY_STAT_RES_FLD_NOT_CLR_ERR: +		return "reserved fields not cleared"; +	case MLX5_CMD_DELIVERY_STAT_CMD_DESCR_ERR: +		return "bad command descriptor type"; +	default: +		return "unknown status code"; +	} +} + +static u16 msg_to_opcode(struct mlx5_cmd_msg *in) +{ +	struct mlx5_inbox_hdr *hdr = (struct mlx5_inbox_hdr *)(in->first.data); + +	return be16_to_cpu(hdr->opcode); +} + +static int wait_func(struct mlx5_core_dev *dev, struct mlx5_cmd_work_ent *ent) +{ +	unsigned long timeout = msecs_to_jiffies(MLX5_CMD_TIMEOUT_MSEC); +	struct mlx5_cmd *cmd = &dev->cmd; +	int err; + +	if (cmd->mode == CMD_MODE_POLLING) { +		wait_for_completion(&ent->done); +		err = ent->ret; +	} else { +		if (!wait_for_completion_timeout(&ent->done, timeout)) +			err = -ETIMEDOUT; +		else +			err = 0; +	} +	if (err == -ETIMEDOUT) { +		mlx5_core_warn(dev, "%s(0x%x) timeout. Will cause a leak of a command resource\n", +			       mlx5_command_str(msg_to_opcode(ent->in)), +			       msg_to_opcode(ent->in)); +	} +	mlx5_core_dbg(dev, "err %d, delivery status %s(%d)\n", +		      err, deliv_status_to_str(ent->status), ent->status); + +	return err; +} + +/*  Notes: + *    1. Callback functions may not sleep + *    2. page queue commands do not support asynchrous completion + */ +static int mlx5_cmd_invoke(struct mlx5_core_dev *dev, struct mlx5_cmd_msg *in, +			   struct mlx5_cmd_msg *out, void *uout, int uout_size, +			   mlx5_cmd_cbk_t callback, +			   void *context, int page_queue, u8 *status) +{ +	struct mlx5_cmd *cmd = &dev->cmd; +	struct mlx5_cmd_work_ent *ent; +	ktime_t t1, t2, delta; +	struct mlx5_cmd_stats *stats; +	int err = 0; +	s64 ds; +	u16 op; + +	if (callback && page_queue) +		return -EINVAL; + +	ent = alloc_cmd(cmd, in, out, uout, uout_size, callback, context, +			page_queue); +	if (IS_ERR(ent)) +		return PTR_ERR(ent); + +	if (!callback) +		init_completion(&ent->done); + +	INIT_WORK(&ent->work, cmd_work_handler); +	if (page_queue) { +		cmd_work_handler(&ent->work); +	} else if (!queue_work(cmd->wq, &ent->work)) { +		mlx5_core_warn(dev, "failed to queue work\n"); +		err = -ENOMEM; +		goto out_free; +	} + +	if (!callback) { +		err = wait_func(dev, ent); +		if (err == -ETIMEDOUT) +			goto out; + +		t1 = timespec_to_ktime(ent->ts1); +		t2 = timespec_to_ktime(ent->ts2); +		delta = ktime_sub(t2, t1); +		ds = ktime_to_ns(delta); +		op = be16_to_cpu(((struct mlx5_inbox_hdr *)in->first.data)->opcode); +		if (op < ARRAY_SIZE(cmd->stats)) { +			stats = &cmd->stats[op]; +			spin_lock_irq(&stats->lock); +			stats->sum += ds; +			++stats->n; +			spin_unlock_irq(&stats->lock); +		} +		mlx5_core_dbg_mask(dev, 1 << MLX5_CMD_TIME, +				   "fw exec time for %s is %lld nsec\n", +				   mlx5_command_str(op), ds); +		*status = ent->status; +		free_cmd(ent); +	} + +	return err; + +out_free: +	free_cmd(ent); +out: +	return err; +} + +static ssize_t dbg_write(struct file *filp, const char __user *buf, +			 size_t count, loff_t *pos) +{ +	struct mlx5_core_dev *dev = filp->private_data; +	struct mlx5_cmd_debug *dbg = &dev->cmd.dbg; +	char lbuf[3]; +	int err; + +	if (!dbg->in_msg || !dbg->out_msg) +		return -ENOMEM; + +	if (copy_from_user(lbuf, buf, sizeof(lbuf))) +		return -EFAULT; + +	lbuf[sizeof(lbuf) - 1] = 0; + +	if (strcmp(lbuf, "go")) +		return -EINVAL; + +	err = mlx5_cmd_exec(dev, dbg->in_msg, dbg->inlen, dbg->out_msg, dbg->outlen); + +	return err ? err : count; +} + + +static const struct file_operations fops = { +	.owner	= THIS_MODULE, +	.open	= simple_open, +	.write	= dbg_write, +}; + +static int mlx5_copy_to_msg(struct mlx5_cmd_msg *to, void *from, int size) +{ +	struct mlx5_cmd_prot_block *block; +	struct mlx5_cmd_mailbox *next; +	int copy; + +	if (!to || !from) +		return -ENOMEM; + +	copy = min_t(int, size, sizeof(to->first.data)); +	memcpy(to->first.data, from, copy); +	size -= copy; +	from += copy; + +	next = to->next; +	while (size) { +		if (!next) { +			/* this is a BUG */ +			return -ENOMEM; +		} + +		copy = min_t(int, size, MLX5_CMD_DATA_BLOCK_SIZE); +		block = next->buf; +		memcpy(block->data, from, copy); +		from += copy; +		size -= copy; +		next = next->next; +	} + +	return 0; +} + +static int mlx5_copy_from_msg(void *to, struct mlx5_cmd_msg *from, int size) +{ +	struct mlx5_cmd_prot_block *block; +	struct mlx5_cmd_mailbox *next; +	int copy; + +	if (!to || !from) +		return -ENOMEM; + +	copy = min_t(int, size, sizeof(from->first.data)); +	memcpy(to, from->first.data, copy); +	size -= copy; +	to += copy; + +	next = from->next; +	while (size) { +		if (!next) { +			/* this is a BUG */ +			return -ENOMEM; +		} + +		copy = min_t(int, size, MLX5_CMD_DATA_BLOCK_SIZE); +		block = next->buf; + +		memcpy(to, block->data, copy); +		to += copy; +		size -= copy; +		next = next->next; +	} + +	return 0; +} + +static struct mlx5_cmd_mailbox *alloc_cmd_box(struct mlx5_core_dev *dev, +					      gfp_t flags) +{ +	struct mlx5_cmd_mailbox *mailbox; + +	mailbox = kmalloc(sizeof(*mailbox), flags); +	if (!mailbox) +		return ERR_PTR(-ENOMEM); + +	mailbox->buf = pci_pool_alloc(dev->cmd.pool, flags, +				      &mailbox->dma); +	if (!mailbox->buf) { +		mlx5_core_dbg(dev, "failed allocation\n"); +		kfree(mailbox); +		return ERR_PTR(-ENOMEM); +	} +	memset(mailbox->buf, 0, sizeof(struct mlx5_cmd_prot_block)); +	mailbox->next = NULL; + +	return mailbox; +} + +static void free_cmd_box(struct mlx5_core_dev *dev, +			 struct mlx5_cmd_mailbox *mailbox) +{ +	pci_pool_free(dev->cmd.pool, mailbox->buf, mailbox->dma); +	kfree(mailbox); +} + +static struct mlx5_cmd_msg *mlx5_alloc_cmd_msg(struct mlx5_core_dev *dev, +					       gfp_t flags, int size) +{ +	struct mlx5_cmd_mailbox *tmp, *head = NULL; +	struct mlx5_cmd_prot_block *block; +	struct mlx5_cmd_msg *msg; +	int blen; +	int err; +	int n; +	int i; + +	msg = kzalloc(sizeof(*msg), flags); +	if (!msg) +		return ERR_PTR(-ENOMEM); + +	blen = size - min_t(int, sizeof(msg->first.data), size); +	n = (blen + MLX5_CMD_DATA_BLOCK_SIZE - 1) / MLX5_CMD_DATA_BLOCK_SIZE; + +	for (i = 0; i < n; i++) { +		tmp = alloc_cmd_box(dev, flags); +		if (IS_ERR(tmp)) { +			mlx5_core_warn(dev, "failed allocating block\n"); +			err = PTR_ERR(tmp); +			goto err_alloc; +		} + +		block = tmp->buf; +		tmp->next = head; +		block->next = cpu_to_be64(tmp->next ? tmp->next->dma : 0); +		block->block_num = cpu_to_be32(n - i - 1); +		head = tmp; +	} +	msg->next = head; +	msg->len = size; +	return msg; + +err_alloc: +	while (head) { +		tmp = head->next; +		free_cmd_box(dev, head); +		head = tmp; +	} +	kfree(msg); + +	return ERR_PTR(err); +} + +static void mlx5_free_cmd_msg(struct mlx5_core_dev *dev, +				  struct mlx5_cmd_msg *msg) +{ +	struct mlx5_cmd_mailbox *head = msg->next; +	struct mlx5_cmd_mailbox *next; + +	while (head) { +		next = head->next; +		free_cmd_box(dev, head); +		head = next; +	} +	kfree(msg); +} + +static ssize_t data_write(struct file *filp, const char __user *buf, +			  size_t count, loff_t *pos) +{ +	struct mlx5_core_dev *dev = filp->private_data; +	struct mlx5_cmd_debug *dbg = &dev->cmd.dbg; +	void *ptr; +	int err; + +	if (*pos != 0) +		return -EINVAL; + +	kfree(dbg->in_msg); +	dbg->in_msg = NULL; +	dbg->inlen = 0; + +	ptr = kzalloc(count, GFP_KERNEL); +	if (!ptr) +		return -ENOMEM; + +	if (copy_from_user(ptr, buf, count)) { +		err = -EFAULT; +		goto out; +	} +	dbg->in_msg = ptr; +	dbg->inlen = count; + +	*pos = count; + +	return count; + +out: +	kfree(ptr); +	return err; +} + +static ssize_t data_read(struct file *filp, char __user *buf, size_t count, +			 loff_t *pos) +{ +	struct mlx5_core_dev *dev = filp->private_data; +	struct mlx5_cmd_debug *dbg = &dev->cmd.dbg; +	int copy; + +	if (*pos) +		return 0; + +	if (!dbg->out_msg) +		return -ENOMEM; + +	copy = min_t(int, count, dbg->outlen); +	if (copy_to_user(buf, dbg->out_msg, copy)) +		return -EFAULT; + +	*pos += copy; + +	return copy; +} + +static const struct file_operations dfops = { +	.owner	= THIS_MODULE, +	.open	= simple_open, +	.write	= data_write, +	.read	= data_read, +}; + +static ssize_t outlen_read(struct file *filp, char __user *buf, size_t count, +			   loff_t *pos) +{ +	struct mlx5_core_dev *dev = filp->private_data; +	struct mlx5_cmd_debug *dbg = &dev->cmd.dbg; +	char outlen[8]; +	int err; + +	if (*pos) +		return 0; + +	err = snprintf(outlen, sizeof(outlen), "%d", dbg->outlen); +	if (err < 0) +		return err; + +	if (copy_to_user(buf, &outlen, err)) +		return -EFAULT; + +	*pos += err; + +	return err; +} + +static ssize_t outlen_write(struct file *filp, const char __user *buf, +			    size_t count, loff_t *pos) +{ +	struct mlx5_core_dev *dev = filp->private_data; +	struct mlx5_cmd_debug *dbg = &dev->cmd.dbg; +	char outlen_str[8]; +	int outlen; +	void *ptr; +	int err; + +	if (*pos != 0 || count > 6) +		return -EINVAL; + +	kfree(dbg->out_msg); +	dbg->out_msg = NULL; +	dbg->outlen = 0; + +	if (copy_from_user(outlen_str, buf, count)) +		return -EFAULT; + +	outlen_str[7] = 0; + +	err = sscanf(outlen_str, "%d", &outlen); +	if (err < 0) +		return err; + +	ptr = kzalloc(outlen, GFP_KERNEL); +	if (!ptr) +		return -ENOMEM; + +	dbg->out_msg = ptr; +	dbg->outlen = outlen; + +	*pos = count; + +	return count; +} + +static const struct file_operations olfops = { +	.owner	= THIS_MODULE, +	.open	= simple_open, +	.write	= outlen_write, +	.read	= outlen_read, +}; + +static void set_wqname(struct mlx5_core_dev *dev) +{ +	struct mlx5_cmd *cmd = &dev->cmd; + +	snprintf(cmd->wq_name, sizeof(cmd->wq_name), "mlx5_cmd_%s", +		 dev_name(&dev->pdev->dev)); +} + +static void clean_debug_files(struct mlx5_core_dev *dev) +{ +	struct mlx5_cmd_debug *dbg = &dev->cmd.dbg; + +	if (!mlx5_debugfs_root) +		return; + +	mlx5_cmdif_debugfs_cleanup(dev); +	debugfs_remove_recursive(dbg->dbg_root); +} + +static int create_debugfs_files(struct mlx5_core_dev *dev) +{ +	struct mlx5_cmd_debug *dbg = &dev->cmd.dbg; +	int err = -ENOMEM; + +	if (!mlx5_debugfs_root) +		return 0; + +	dbg->dbg_root = debugfs_create_dir("cmd", dev->priv.dbg_root); +	if (!dbg->dbg_root) +		return err; + +	dbg->dbg_in = debugfs_create_file("in", 0400, dbg->dbg_root, +					  dev, &dfops); +	if (!dbg->dbg_in) +		goto err_dbg; + +	dbg->dbg_out = debugfs_create_file("out", 0200, dbg->dbg_root, +					   dev, &dfops); +	if (!dbg->dbg_out) +		goto err_dbg; + +	dbg->dbg_outlen = debugfs_create_file("out_len", 0600, dbg->dbg_root, +					      dev, &olfops); +	if (!dbg->dbg_outlen) +		goto err_dbg; + +	dbg->dbg_status = debugfs_create_u8("status", 0600, dbg->dbg_root, +					    &dbg->status); +	if (!dbg->dbg_status) +		goto err_dbg; + +	dbg->dbg_run = debugfs_create_file("run", 0200, dbg->dbg_root, dev, &fops); +	if (!dbg->dbg_run) +		goto err_dbg; + +	mlx5_cmdif_debugfs_init(dev); + +	return 0; + +err_dbg: +	clean_debug_files(dev); +	return err; +} + +void mlx5_cmd_use_events(struct mlx5_core_dev *dev) +{ +	struct mlx5_cmd *cmd = &dev->cmd; +	int i; + +	for (i = 0; i < cmd->max_reg_cmds; i++) +		down(&cmd->sem); + +	down(&cmd->pages_sem); + +	flush_workqueue(cmd->wq); + +	cmd->mode = CMD_MODE_EVENTS; + +	up(&cmd->pages_sem); +	for (i = 0; i < cmd->max_reg_cmds; i++) +		up(&cmd->sem); +} + +void mlx5_cmd_use_polling(struct mlx5_core_dev *dev) +{ +	struct mlx5_cmd *cmd = &dev->cmd; +	int i; + +	for (i = 0; i < cmd->max_reg_cmds; i++) +		down(&cmd->sem); + +	down(&cmd->pages_sem); + +	flush_workqueue(cmd->wq); +	cmd->mode = CMD_MODE_POLLING; + +	up(&cmd->pages_sem); +	for (i = 0; i < cmd->max_reg_cmds; i++) +		up(&cmd->sem); +} + +static void free_msg(struct mlx5_core_dev *dev, struct mlx5_cmd_msg *msg) +{ +	unsigned long flags; + +	if (msg->cache) { +		spin_lock_irqsave(&msg->cache->lock, flags); +		list_add_tail(&msg->list, &msg->cache->head); +		spin_unlock_irqrestore(&msg->cache->lock, flags); +	} else { +		mlx5_free_cmd_msg(dev, msg); +	} +} + +void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, unsigned long vector) +{ +	struct mlx5_cmd *cmd = &dev->cmd; +	struct mlx5_cmd_work_ent *ent; +	mlx5_cmd_cbk_t callback; +	void *context; +	int err; +	int i; +	ktime_t t1, t2, delta; +	s64 ds; +	struct mlx5_cmd_stats *stats; +	unsigned long flags; + +	for (i = 0; i < (1 << cmd->log_sz); i++) { +		if (test_bit(i, &vector)) { +			struct semaphore *sem; + +			ent = cmd->ent_arr[i]; +			if (ent->page_queue) +				sem = &cmd->pages_sem; +			else +				sem = &cmd->sem; +			ktime_get_ts(&ent->ts2); +			memcpy(ent->out->first.data, ent->lay->out, sizeof(ent->lay->out)); +			dump_command(dev, ent, 0); +			if (!ent->ret) { +				if (!cmd->checksum_disabled) +					ent->ret = verify_signature(ent); +				else +					ent->ret = 0; +				ent->status = ent->lay->status_own >> 1; +				mlx5_core_dbg(dev, "command completed. ret 0x%x, delivery status %s(0x%x)\n", +					      ent->ret, deliv_status_to_str(ent->status), ent->status); +			} +			free_ent(cmd, ent->idx); +			if (ent->callback) { +				t1 = timespec_to_ktime(ent->ts1); +				t2 = timespec_to_ktime(ent->ts2); +				delta = ktime_sub(t2, t1); +				ds = ktime_to_ns(delta); +				if (ent->op < ARRAY_SIZE(cmd->stats)) { +					stats = &cmd->stats[ent->op]; +					spin_lock_irqsave(&stats->lock, flags); +					stats->sum += ds; +					++stats->n; +					spin_unlock_irqrestore(&stats->lock, flags); +				} + +				callback = ent->callback; +				context = ent->context; +				err = ent->ret; +				if (!err) +					err = mlx5_copy_from_msg(ent->uout, +								 ent->out, +								 ent->uout_size); + +				mlx5_free_cmd_msg(dev, ent->out); +				free_msg(dev, ent->in); + +				free_cmd(ent); +				callback(err, context); +			} else { +				complete(&ent->done); +			} +			up(sem); +		} +	} +} +EXPORT_SYMBOL(mlx5_cmd_comp_handler); + +static int status_to_err(u8 status) +{ +	return status ? -1 : 0; /* TBD more meaningful codes */ +} + +static struct mlx5_cmd_msg *alloc_msg(struct mlx5_core_dev *dev, int in_size, +				      gfp_t gfp) +{ +	struct mlx5_cmd_msg *msg = ERR_PTR(-ENOMEM); +	struct mlx5_cmd *cmd = &dev->cmd; +	struct cache_ent *ent = NULL; + +	if (in_size > MED_LIST_SIZE && in_size <= LONG_LIST_SIZE) +		ent = &cmd->cache.large; +	else if (in_size > 16 && in_size <= MED_LIST_SIZE) +		ent = &cmd->cache.med; + +	if (ent) { +		spin_lock_irq(&ent->lock); +		if (!list_empty(&ent->head)) { +			msg = list_entry(ent->head.next, typeof(*msg), list); +			/* For cached lists, we must explicitly state what is +			 * the real size +			 */ +			msg->len = in_size; +			list_del(&msg->list); +		} +		spin_unlock_irq(&ent->lock); +	} + +	if (IS_ERR(msg)) +		msg = mlx5_alloc_cmd_msg(dev, gfp, in_size); + +	return msg; +} + +static int is_manage_pages(struct mlx5_inbox_hdr *in) +{ +	return be16_to_cpu(in->opcode) == MLX5_CMD_OP_MANAGE_PAGES; +} + +static int cmd_exec(struct mlx5_core_dev *dev, void *in, int in_size, void *out, +		    int out_size, mlx5_cmd_cbk_t callback, void *context) +{ +	struct mlx5_cmd_msg *inb; +	struct mlx5_cmd_msg *outb; +	int pages_queue; +	gfp_t gfp; +	int err; +	u8 status = 0; + +	pages_queue = is_manage_pages(in); +	gfp = callback ? GFP_ATOMIC : GFP_KERNEL; + +	inb = alloc_msg(dev, in_size, gfp); +	if (IS_ERR(inb)) { +		err = PTR_ERR(inb); +		return err; +	} + +	err = mlx5_copy_to_msg(inb, in, in_size); +	if (err) { +		mlx5_core_warn(dev, "err %d\n", err); +		goto out_in; +	} + +	outb = mlx5_alloc_cmd_msg(dev, gfp, out_size); +	if (IS_ERR(outb)) { +		err = PTR_ERR(outb); +		goto out_in; +	} + +	err = mlx5_cmd_invoke(dev, inb, outb, out, out_size, callback, context, +			      pages_queue, &status); +	if (err) +		goto out_out; + +	mlx5_core_dbg(dev, "err %d, status %d\n", err, status); +	if (status) { +		err = status_to_err(status); +		goto out_out; +	} + +	err = mlx5_copy_from_msg(out, outb, out_size); + +out_out: +	if (!callback) +		mlx5_free_cmd_msg(dev, outb); + +out_in: +	if (!callback) +		free_msg(dev, inb); +	return err; +} + +int mlx5_cmd_exec(struct mlx5_core_dev *dev, void *in, int in_size, void *out, +		  int out_size) +{ +	return cmd_exec(dev, in, in_size, out, out_size, NULL, NULL); +} +EXPORT_SYMBOL(mlx5_cmd_exec); + +int mlx5_cmd_exec_cb(struct mlx5_core_dev *dev, void *in, int in_size, +		     void *out, int out_size, mlx5_cmd_cbk_t callback, +		     void *context) +{ +	return cmd_exec(dev, in, in_size, out, out_size, callback, context); +} +EXPORT_SYMBOL(mlx5_cmd_exec_cb); + +static void destroy_msg_cache(struct mlx5_core_dev *dev) +{ +	struct mlx5_cmd *cmd = &dev->cmd; +	struct mlx5_cmd_msg *msg; +	struct mlx5_cmd_msg *n; + +	list_for_each_entry_safe(msg, n, &cmd->cache.large.head, list) { +		list_del(&msg->list); +		mlx5_free_cmd_msg(dev, msg); +	} + +	list_for_each_entry_safe(msg, n, &cmd->cache.med.head, list) { +		list_del(&msg->list); +		mlx5_free_cmd_msg(dev, msg); +	} +} + +static int create_msg_cache(struct mlx5_core_dev *dev) +{ +	struct mlx5_cmd *cmd = &dev->cmd; +	struct mlx5_cmd_msg *msg; +	int err; +	int i; + +	spin_lock_init(&cmd->cache.large.lock); +	INIT_LIST_HEAD(&cmd->cache.large.head); +	spin_lock_init(&cmd->cache.med.lock); +	INIT_LIST_HEAD(&cmd->cache.med.head); + +	for (i = 0; i < NUM_LONG_LISTS; i++) { +		msg = mlx5_alloc_cmd_msg(dev, GFP_KERNEL, LONG_LIST_SIZE); +		if (IS_ERR(msg)) { +			err = PTR_ERR(msg); +			goto ex_err; +		} +		msg->cache = &cmd->cache.large; +		list_add_tail(&msg->list, &cmd->cache.large.head); +	} + +	for (i = 0; i < NUM_MED_LISTS; i++) { +		msg = mlx5_alloc_cmd_msg(dev, GFP_KERNEL, MED_LIST_SIZE); +		if (IS_ERR(msg)) { +			err = PTR_ERR(msg); +			goto ex_err; +		} +		msg->cache = &cmd->cache.med; +		list_add_tail(&msg->list, &cmd->cache.med.head); +	} + +	return 0; + +ex_err: +	destroy_msg_cache(dev); +	return err; +} + +int mlx5_cmd_init(struct mlx5_core_dev *dev) +{ +	int size = sizeof(struct mlx5_cmd_prot_block); +	int align = roundup_pow_of_two(size); +	struct mlx5_cmd *cmd = &dev->cmd; +	u32 cmd_h, cmd_l; +	u16 cmd_if_rev; +	int err; +	int i; + +	cmd_if_rev = cmdif_rev(dev); +	if (cmd_if_rev != CMD_IF_REV) { +		dev_err(&dev->pdev->dev, +			"Driver cmdif rev(%d) differs from firmware's(%d)\n", +			CMD_IF_REV, cmd_if_rev); +		return -EINVAL; +	} + +	cmd->pool = pci_pool_create("mlx5_cmd", dev->pdev, size, align, 0); +	if (!cmd->pool) +		return -ENOMEM; + +	cmd->cmd_buf = (void *)__get_free_pages(GFP_ATOMIC, 0); +	if (!cmd->cmd_buf) { +		err = -ENOMEM; +		goto err_free_pool; +	} +	cmd->dma = dma_map_single(&dev->pdev->dev, cmd->cmd_buf, PAGE_SIZE, +				  DMA_BIDIRECTIONAL); +	if (dma_mapping_error(&dev->pdev->dev, cmd->dma)) { +		err = -ENOMEM; +		goto err_free; +	} + +	cmd_l = ioread32be(&dev->iseg->cmdq_addr_l_sz) & 0xff; +	cmd->log_sz = cmd_l >> 4 & 0xf; +	cmd->log_stride = cmd_l & 0xf; +	if (1 << cmd->log_sz > MLX5_MAX_COMMANDS) { +		dev_err(&dev->pdev->dev, "firmware reports too many outstanding commands %d\n", +			1 << cmd->log_sz); +		err = -EINVAL; +		goto err_map; +	} + +	if (cmd->log_sz + cmd->log_stride > PAGE_SHIFT) { +		dev_err(&dev->pdev->dev, "command queue size overflow\n"); +		err = -EINVAL; +		goto err_map; +	} + +	cmd->checksum_disabled = 1; +	cmd->max_reg_cmds = (1 << cmd->log_sz) - 1; +	cmd->bitmask = (1 << cmd->max_reg_cmds) - 1; + +	cmd->cmdif_rev = ioread32be(&dev->iseg->cmdif_rev_fw_sub) >> 16; +	if (cmd->cmdif_rev > CMD_IF_REV) { +		dev_err(&dev->pdev->dev, "driver does not support command interface version. driver %d, firmware %d\n", +			CMD_IF_REV, cmd->cmdif_rev); +		err = -ENOTSUPP; +		goto err_map; +	} + +	spin_lock_init(&cmd->alloc_lock); +	spin_lock_init(&cmd->token_lock); +	for (i = 0; i < ARRAY_SIZE(cmd->stats); i++) +		spin_lock_init(&cmd->stats[i].lock); + +	sema_init(&cmd->sem, cmd->max_reg_cmds); +	sema_init(&cmd->pages_sem, 1); + +	cmd_h = (u32)((u64)(cmd->dma) >> 32); +	cmd_l = (u32)(cmd->dma); +	if (cmd_l & 0xfff) { +		dev_err(&dev->pdev->dev, "invalid command queue address\n"); +		err = -ENOMEM; +		goto err_map; +	} + +	iowrite32be(cmd_h, &dev->iseg->cmdq_addr_h); +	iowrite32be(cmd_l, &dev->iseg->cmdq_addr_l_sz); + +	/* Make sure firmware sees the complete address before we proceed */ +	wmb(); + +	mlx5_core_dbg(dev, "descriptor at dma 0x%llx\n", (unsigned long long)(cmd->dma)); + +	cmd->mode = CMD_MODE_POLLING; + +	err = create_msg_cache(dev); +	if (err) { +		dev_err(&dev->pdev->dev, "failed to create command cache\n"); +		goto err_map; +	} + +	set_wqname(dev); +	cmd->wq = create_singlethread_workqueue(cmd->wq_name); +	if (!cmd->wq) { +		dev_err(&dev->pdev->dev, "failed to create command workqueue\n"); +		err = -ENOMEM; +		goto err_cache; +	} + +	err = create_debugfs_files(dev); +	if (err) { +		err = -ENOMEM; +		goto err_wq; +	} + +	return 0; + +err_wq: +	destroy_workqueue(cmd->wq); + +err_cache: +	destroy_msg_cache(dev); + +err_map: +	dma_unmap_single(&dev->pdev->dev, cmd->dma, PAGE_SIZE, +			 DMA_BIDIRECTIONAL); +err_free: +	free_pages((unsigned long)cmd->cmd_buf, 0); + +err_free_pool: +	pci_pool_destroy(cmd->pool); + +	return err; +} +EXPORT_SYMBOL(mlx5_cmd_init); + +void mlx5_cmd_cleanup(struct mlx5_core_dev *dev) +{ +	struct mlx5_cmd *cmd = &dev->cmd; + +	clean_debug_files(dev); +	destroy_workqueue(cmd->wq); +	destroy_msg_cache(dev); +	dma_unmap_single(&dev->pdev->dev, cmd->dma, PAGE_SIZE, +			 DMA_BIDIRECTIONAL); +	free_pages((unsigned long)cmd->cmd_buf, 0); +	pci_pool_destroy(cmd->pool); +} +EXPORT_SYMBOL(mlx5_cmd_cleanup); + +static const char *cmd_status_str(u8 status) +{ +	switch (status) { +	case MLX5_CMD_STAT_OK: +		return "OK"; +	case MLX5_CMD_STAT_INT_ERR: +		return "internal error"; +	case MLX5_CMD_STAT_BAD_OP_ERR: +		return "bad operation"; +	case MLX5_CMD_STAT_BAD_PARAM_ERR: +		return "bad parameter"; +	case MLX5_CMD_STAT_BAD_SYS_STATE_ERR: +		return "bad system state"; +	case MLX5_CMD_STAT_BAD_RES_ERR: +		return "bad resource"; +	case MLX5_CMD_STAT_RES_BUSY: +		return "resource busy"; +	case MLX5_CMD_STAT_LIM_ERR: +		return "limits exceeded"; +	case MLX5_CMD_STAT_BAD_RES_STATE_ERR: +		return "bad resource state"; +	case MLX5_CMD_STAT_IX_ERR: +		return "bad index"; +	case MLX5_CMD_STAT_NO_RES_ERR: +		return "no resources"; +	case MLX5_CMD_STAT_BAD_INP_LEN_ERR: +		return "bad input length"; +	case MLX5_CMD_STAT_BAD_OUTP_LEN_ERR: +		return "bad output length"; +	case MLX5_CMD_STAT_BAD_QP_STATE_ERR: +		return "bad QP state"; +	case MLX5_CMD_STAT_BAD_PKT_ERR: +		return "bad packet (discarded)"; +	case MLX5_CMD_STAT_BAD_SIZE_OUTS_CQES_ERR: +		return "bad size too many outstanding CQEs"; +	default: +		return "unknown status"; +	} +} + +int mlx5_cmd_status_to_err(struct mlx5_outbox_hdr *hdr) +{ +	if (!hdr->status) +		return 0; + +	pr_warn("command failed, status %s(0x%x), syndrome 0x%x\n", +		cmd_status_str(hdr->status), hdr->status, +		be32_to_cpu(hdr->syndrome)); + +	switch (hdr->status) { +	case MLX5_CMD_STAT_OK:				return 0; +	case MLX5_CMD_STAT_INT_ERR:			return -EIO; +	case MLX5_CMD_STAT_BAD_OP_ERR:			return -EINVAL; +	case MLX5_CMD_STAT_BAD_PARAM_ERR:		return -EINVAL; +	case MLX5_CMD_STAT_BAD_SYS_STATE_ERR:		return -EIO; +	case MLX5_CMD_STAT_BAD_RES_ERR:			return -EINVAL; +	case MLX5_CMD_STAT_RES_BUSY:			return -EBUSY; +	case MLX5_CMD_STAT_LIM_ERR:			return -ENOMEM; +	case MLX5_CMD_STAT_BAD_RES_STATE_ERR:		return -EINVAL; +	case MLX5_CMD_STAT_IX_ERR:			return -EINVAL; +	case MLX5_CMD_STAT_NO_RES_ERR:			return -EAGAIN; +	case MLX5_CMD_STAT_BAD_INP_LEN_ERR:		return -EIO; +	case MLX5_CMD_STAT_BAD_OUTP_LEN_ERR:		return -EIO; +	case MLX5_CMD_STAT_BAD_QP_STATE_ERR:		return -EINVAL; +	case MLX5_CMD_STAT_BAD_PKT_ERR:			return -EINVAL; +	case MLX5_CMD_STAT_BAD_SIZE_OUTS_CQES_ERR:	return -EINVAL; +	default:					return -EIO; +	} +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cq.c b/drivers/net/ethernet/mellanox/mlx5/core/cq.c new file mode 100644 index 00000000000..43c5f480952 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/cq.c @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2013, Mellanox Technologies 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. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/hardirq.h> +#include <linux/mlx5/driver.h> +#include <linux/mlx5/cmd.h> +#include <rdma/ib_verbs.h> +#include <linux/mlx5/cq.h> +#include "mlx5_core.h" + +void mlx5_cq_completion(struct mlx5_core_dev *dev, u32 cqn) +{ +	struct mlx5_core_cq *cq; +	struct mlx5_cq_table *table = &dev->priv.cq_table; + +	spin_lock(&table->lock); +	cq = radix_tree_lookup(&table->tree, cqn); +	if (likely(cq)) +		atomic_inc(&cq->refcount); +	spin_unlock(&table->lock); + +	if (!cq) { +		mlx5_core_warn(dev, "Completion event for bogus CQ 0x%x\n", cqn); +		return; +	} + +	++cq->arm_sn; + +	cq->comp(cq); + +	if (atomic_dec_and_test(&cq->refcount)) +		complete(&cq->free); +} + +void mlx5_cq_event(struct mlx5_core_dev *dev, u32 cqn, int event_type) +{ +	struct mlx5_cq_table *table = &dev->priv.cq_table; +	struct mlx5_core_cq *cq; + +	spin_lock(&table->lock); + +	cq = radix_tree_lookup(&table->tree, cqn); +	if (cq) +		atomic_inc(&cq->refcount); + +	spin_unlock(&table->lock); + +	if (!cq) { +		mlx5_core_warn(dev, "Async event for bogus CQ 0x%x\n", cqn); +		return; +	} + +	cq->event(cq, event_type); + +	if (atomic_dec_and_test(&cq->refcount)) +		complete(&cq->free); +} + + +int mlx5_core_create_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq, +			struct mlx5_create_cq_mbox_in *in, int inlen) +{ +	int err; +	struct mlx5_cq_table *table = &dev->priv.cq_table; +	struct mlx5_create_cq_mbox_out out; +	struct mlx5_destroy_cq_mbox_in din; +	struct mlx5_destroy_cq_mbox_out dout; + +	in->hdr.opcode = cpu_to_be16(MLX5_CMD_OP_CREATE_CQ); +	memset(&out, 0, sizeof(out)); +	err = mlx5_cmd_exec(dev, in, inlen, &out, sizeof(out)); +	if (err) +		return err; + +	if (out.hdr.status) +		return mlx5_cmd_status_to_err(&out.hdr); + +	cq->cqn = be32_to_cpu(out.cqn) & 0xffffff; +	cq->cons_index = 0; +	cq->arm_sn     = 0; +	atomic_set(&cq->refcount, 1); +	init_completion(&cq->free); + +	spin_lock_irq(&table->lock); +	err = radix_tree_insert(&table->tree, cq->cqn, cq); +	spin_unlock_irq(&table->lock); +	if (err) +		goto err_cmd; + +	cq->pid = current->pid; +	err = mlx5_debug_cq_add(dev, cq); +	if (err) +		mlx5_core_dbg(dev, "failed adding CP 0x%x to debug file system\n", +			      cq->cqn); + +	return 0; + +err_cmd: +	memset(&din, 0, sizeof(din)); +	memset(&dout, 0, sizeof(dout)); +	din.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_DESTROY_CQ); +	mlx5_cmd_exec(dev, &din, sizeof(din), &dout, sizeof(dout)); +	return err; +} +EXPORT_SYMBOL(mlx5_core_create_cq); + +int mlx5_core_destroy_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq) +{ +	struct mlx5_cq_table *table = &dev->priv.cq_table; +	struct mlx5_destroy_cq_mbox_in in; +	struct mlx5_destroy_cq_mbox_out out; +	struct mlx5_core_cq *tmp; +	int err; + +	spin_lock_irq(&table->lock); +	tmp = radix_tree_delete(&table->tree, cq->cqn); +	spin_unlock_irq(&table->lock); +	if (!tmp) { +		mlx5_core_warn(dev, "cq 0x%x not found in tree\n", cq->cqn); +		return -EINVAL; +	} +	if (tmp != cq) { +		mlx5_core_warn(dev, "corruption on srqn 0x%x\n", cq->cqn); +		return -EINVAL; +	} + +	memset(&in, 0, sizeof(in)); +	memset(&out, 0, sizeof(out)); +	in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_DESTROY_CQ); +	in.cqn = cpu_to_be32(cq->cqn); +	err = mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out)); +	if (err) +		return err; + +	if (out.hdr.status) +		return mlx5_cmd_status_to_err(&out.hdr); + +	synchronize_irq(cq->irqn); + +	mlx5_debug_cq_remove(dev, cq); +	if (atomic_dec_and_test(&cq->refcount)) +		complete(&cq->free); +	wait_for_completion(&cq->free); + +	return 0; +} +EXPORT_SYMBOL(mlx5_core_destroy_cq); + +int mlx5_core_query_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq, +		       struct mlx5_query_cq_mbox_out *out) +{ +	struct mlx5_query_cq_mbox_in in; +	int err; + +	memset(&in, 0, sizeof(in)); +	memset(out, 0, sizeof(*out)); + +	in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_QUERY_CQ); +	in.cqn = cpu_to_be32(cq->cqn); +	err = mlx5_cmd_exec(dev, &in, sizeof(in), out, sizeof(*out)); +	if (err) +		return err; + +	if (out->hdr.status) +		return mlx5_cmd_status_to_err(&out->hdr); + +	return err; +} +EXPORT_SYMBOL(mlx5_core_query_cq); + + +int mlx5_core_modify_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq, +			struct mlx5_modify_cq_mbox_in *in, int in_sz) +{ +	struct mlx5_modify_cq_mbox_out out; +	int err; + +	memset(&out, 0, sizeof(out)); +	in->hdr.opcode = cpu_to_be16(MLX5_CMD_OP_MODIFY_CQ); +	err = mlx5_cmd_exec(dev, in, in_sz, &out, sizeof(out)); +	if (err) +		return err; + +	if (out.hdr.status) +		return mlx5_cmd_status_to_err(&out.hdr); + +	return 0; +} +EXPORT_SYMBOL(mlx5_core_modify_cq); + +int mlx5_init_cq_table(struct mlx5_core_dev *dev) +{ +	struct mlx5_cq_table *table = &dev->priv.cq_table; +	int err; + +	spin_lock_init(&table->lock); +	INIT_RADIX_TREE(&table->tree, GFP_ATOMIC); +	err = mlx5_cq_debugfs_init(dev); + +	return err; +} + +void mlx5_cleanup_cq_table(struct mlx5_core_dev *dev) +{ +	mlx5_cq_debugfs_cleanup(dev); +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/debugfs.c b/drivers/net/ethernet/mellanox/mlx5/core/debugfs.c new file mode 100644 index 00000000000..10e1f1a1825 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/debugfs.c @@ -0,0 +1,610 @@ +/* + * Copyright (c) 2013, Mellanox Technologies 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. + */ + +#include <linux/module.h> +#include <linux/debugfs.h> +#include <linux/mlx5/qp.h> +#include <linux/mlx5/cq.h> +#include <linux/mlx5/driver.h> +#include "mlx5_core.h" + +enum { +	QP_PID, +	QP_STATE, +	QP_XPORT, +	QP_MTU, +	QP_N_RECV, +	QP_RECV_SZ, +	QP_N_SEND, +	QP_LOG_PG_SZ, +	QP_RQPN, +}; + +static char *qp_fields[] = { +	[QP_PID]	= "pid", +	[QP_STATE]	= "state", +	[QP_XPORT]	= "transport", +	[QP_MTU]	= "mtu", +	[QP_N_RECV]	= "num_recv", +	[QP_RECV_SZ]	= "rcv_wqe_sz", +	[QP_N_SEND]	= "num_send", +	[QP_LOG_PG_SZ]	= "log2_page_sz", +	[QP_RQPN]	= "remote_qpn", +}; + +enum { +	EQ_NUM_EQES, +	EQ_INTR, +	EQ_LOG_PG_SZ, +}; + +static char *eq_fields[] = { +	[EQ_NUM_EQES]	= "num_eqes", +	[EQ_INTR]	= "intr", +	[EQ_LOG_PG_SZ]	= "log_page_size", +}; + +enum { +	CQ_PID, +	CQ_NUM_CQES, +	CQ_LOG_PG_SZ, +}; + +static char *cq_fields[] = { +	[CQ_PID]	= "pid", +	[CQ_NUM_CQES]	= "num_cqes", +	[CQ_LOG_PG_SZ]	= "log_page_size", +}; + +struct dentry *mlx5_debugfs_root; +EXPORT_SYMBOL(mlx5_debugfs_root); + +void mlx5_register_debugfs(void) +{ +	mlx5_debugfs_root = debugfs_create_dir("mlx5", NULL); +	if (IS_ERR_OR_NULL(mlx5_debugfs_root)) +		mlx5_debugfs_root = NULL; +} + +void mlx5_unregister_debugfs(void) +{ +	debugfs_remove(mlx5_debugfs_root); +} + +int mlx5_qp_debugfs_init(struct mlx5_core_dev *dev) +{ +	if (!mlx5_debugfs_root) +		return 0; + +	atomic_set(&dev->num_qps, 0); + +	dev->priv.qp_debugfs = debugfs_create_dir("QPs",  dev->priv.dbg_root); +	if (!dev->priv.qp_debugfs) +		return -ENOMEM; + +	return 0; +} + +void mlx5_qp_debugfs_cleanup(struct mlx5_core_dev *dev) +{ +	if (!mlx5_debugfs_root) +		return; + +	debugfs_remove_recursive(dev->priv.qp_debugfs); +} + +int mlx5_eq_debugfs_init(struct mlx5_core_dev *dev) +{ +	if (!mlx5_debugfs_root) +		return 0; + +	dev->priv.eq_debugfs = debugfs_create_dir("EQs",  dev->priv.dbg_root); +	if (!dev->priv.eq_debugfs) +		return -ENOMEM; + +	return 0; +} + +void mlx5_eq_debugfs_cleanup(struct mlx5_core_dev *dev) +{ +	if (!mlx5_debugfs_root) +		return; + +	debugfs_remove_recursive(dev->priv.eq_debugfs); +} + +static ssize_t average_read(struct file *filp, char __user *buf, size_t count, +			    loff_t *pos) +{ +	struct mlx5_cmd_stats *stats; +	u64 field = 0; +	int ret; +	char tbuf[22]; + +	if (*pos) +		return 0; + +	stats = filp->private_data; +	spin_lock_irq(&stats->lock); +	if (stats->n) +		field = div64_u64(stats->sum, stats->n); +	spin_unlock_irq(&stats->lock); +	ret = snprintf(tbuf, sizeof(tbuf), "%llu\n", field); +	if (ret > 0) { +		if (copy_to_user(buf, tbuf, ret)) +			return -EFAULT; +	} + +	*pos += ret; +	return ret; +} + + +static ssize_t average_write(struct file *filp, const char __user *buf, +			     size_t count, loff_t *pos) +{ +	struct mlx5_cmd_stats *stats; + +	stats = filp->private_data; +	spin_lock_irq(&stats->lock); +	stats->sum = 0; +	stats->n = 0; +	spin_unlock_irq(&stats->lock); + +	*pos += count; + +	return count; +} + +static const struct file_operations stats_fops = { +	.owner	= THIS_MODULE, +	.open	= simple_open, +	.read	= average_read, +	.write	= average_write, +}; + +int mlx5_cmdif_debugfs_init(struct mlx5_core_dev *dev) +{ +	struct mlx5_cmd_stats *stats; +	struct dentry **cmd; +	const char *namep; +	int err; +	int i; + +	if (!mlx5_debugfs_root) +		return 0; + +	cmd = &dev->priv.cmdif_debugfs; +	*cmd = debugfs_create_dir("commands", dev->priv.dbg_root); +	if (!*cmd) +		return -ENOMEM; + +	for (i = 0; i < ARRAY_SIZE(dev->cmd.stats); i++) { +		stats = &dev->cmd.stats[i]; +		namep = mlx5_command_str(i); +		if (strcmp(namep, "unknown command opcode")) { +			stats->root = debugfs_create_dir(namep, *cmd); +			if (!stats->root) { +				mlx5_core_warn(dev, "failed adding command %d\n", +					       i); +				err = -ENOMEM; +				goto out; +			} + +			stats->avg = debugfs_create_file("average", 0400, +							 stats->root, stats, +							 &stats_fops); +			if (!stats->avg) { +				mlx5_core_warn(dev, "failed creating debugfs file\n"); +				err = -ENOMEM; +				goto out; +			} + +			stats->count = debugfs_create_u64("n", 0400, +							  stats->root, +							  &stats->n); +			if (!stats->count) { +				mlx5_core_warn(dev, "failed creating debugfs file\n"); +				err = -ENOMEM; +				goto out; +			} +		} +	} + +	return 0; +out: +	debugfs_remove_recursive(dev->priv.cmdif_debugfs); +	return err; +} + +void mlx5_cmdif_debugfs_cleanup(struct mlx5_core_dev *dev) +{ +	if (!mlx5_debugfs_root) +		return; + +	debugfs_remove_recursive(dev->priv.cmdif_debugfs); +} + +int mlx5_cq_debugfs_init(struct mlx5_core_dev *dev) +{ +	if (!mlx5_debugfs_root) +		return 0; + +	dev->priv.cq_debugfs = debugfs_create_dir("CQs",  dev->priv.dbg_root); +	if (!dev->priv.cq_debugfs) +		return -ENOMEM; + +	return 0; +} + +void mlx5_cq_debugfs_cleanup(struct mlx5_core_dev *dev) +{ +	if (!mlx5_debugfs_root) +		return; + +	debugfs_remove_recursive(dev->priv.cq_debugfs); +} + +static u64 qp_read_field(struct mlx5_core_dev *dev, struct mlx5_core_qp *qp, +			 int index, int *is_str) +{ +	struct mlx5_query_qp_mbox_out *out; +	struct mlx5_qp_context *ctx; +	u64 param = 0; +	int err; +	int no_sq; + +	out = kzalloc(sizeof(*out), GFP_KERNEL); +	if (!out) +		return param; + +	err = mlx5_core_qp_query(dev, qp, out, sizeof(*out)); +	if (err) { +		mlx5_core_warn(dev, "failed to query qp\n"); +		goto out; +	} + +	*is_str = 0; +	ctx = &out->ctx; +	switch (index) { +	case QP_PID: +		param = qp->pid; +		break; +	case QP_STATE: +		param = (u64)mlx5_qp_state_str(be32_to_cpu(ctx->flags) >> 28); +		*is_str = 1; +		break; +	case QP_XPORT: +		param = (u64)mlx5_qp_type_str((be32_to_cpu(ctx->flags) >> 16) & 0xff); +		*is_str = 1; +		break; +	case QP_MTU: +		switch (ctx->mtu_msgmax >> 5) { +		case IB_MTU_256: +			param = 256; +			break; +		case IB_MTU_512: +			param = 512; +			break; +		case IB_MTU_1024: +			param = 1024; +			break; +		case IB_MTU_2048: +			param = 2048; +			break; +		case IB_MTU_4096: +			param = 4096; +			break; +		default: +			param = 0; +		} +		break; +	case QP_N_RECV: +		param = 1 << ((ctx->rq_size_stride >> 3) & 0xf); +		break; +	case QP_RECV_SZ: +		param = 1 << ((ctx->rq_size_stride & 7) + 4); +		break; +	case QP_N_SEND: +		no_sq = be16_to_cpu(ctx->sq_crq_size) >> 15; +		if (!no_sq) +			param = 1 << (be16_to_cpu(ctx->sq_crq_size) >> 11); +		else +			param = 0; +		break; +	case QP_LOG_PG_SZ: +		param = (be32_to_cpu(ctx->log_pg_sz_remote_qpn) >> 24) & 0x1f; +		param += 12; +		break; +	case QP_RQPN: +		param = be32_to_cpu(ctx->log_pg_sz_remote_qpn) & 0xffffff; +		break; +	} + +out: +	kfree(out); +	return param; +} + +static u64 eq_read_field(struct mlx5_core_dev *dev, struct mlx5_eq *eq, +			 int index) +{ +	struct mlx5_query_eq_mbox_out *out; +	struct mlx5_eq_context *ctx; +	u64 param = 0; +	int err; + +	out = kzalloc(sizeof(*out), GFP_KERNEL); +	if (!out) +		return param; + +	ctx = &out->ctx; + +	err = mlx5_core_eq_query(dev, eq, out, sizeof(*out)); +	if (err) { +		mlx5_core_warn(dev, "failed to query eq\n"); +		goto out; +	} + +	switch (index) { +	case EQ_NUM_EQES: +		param = 1 << ((be32_to_cpu(ctx->log_sz_usr_page) >> 24) & 0x1f); +		break; +	case EQ_INTR: +		param = ctx->intr; +		break; +	case EQ_LOG_PG_SZ: +		param = (ctx->log_page_size & 0x1f) + 12; +		break; +	} + +out: +	kfree(out); +	return param; +} + +static u64 cq_read_field(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq, +			 int index) +{ +	struct mlx5_query_cq_mbox_out *out; +	struct mlx5_cq_context *ctx; +	u64 param = 0; +	int err; + +	out = kzalloc(sizeof(*out), GFP_KERNEL); +	if (!out) +		return param; + +	ctx = &out->ctx; + +	err = mlx5_core_query_cq(dev, cq, out); +	if (err) { +		mlx5_core_warn(dev, "failed to query cq\n"); +		goto out; +	} + +	switch (index) { +	case CQ_PID: +		param = cq->pid; +		break; +	case CQ_NUM_CQES: +		param = 1 << ((be32_to_cpu(ctx->log_sz_usr_page) >> 24) & 0x1f); +		break; +	case CQ_LOG_PG_SZ: +		param = (ctx->log_pg_sz & 0x1f) + 12; +		break; +	} + +out: +	kfree(out); +	return param; +} + +static ssize_t dbg_read(struct file *filp, char __user *buf, size_t count, +			loff_t *pos) +{ +	struct mlx5_field_desc *desc; +	struct mlx5_rsc_debug *d; +	char tbuf[18]; +	int is_str = 0; +	u64 field; +	int ret; + +	if (*pos) +		return 0; + +	desc = filp->private_data; +	d = (void *)(desc - desc->i) - sizeof(*d); +	switch (d->type) { +	case MLX5_DBG_RSC_QP: +		field = qp_read_field(d->dev, d->object, desc->i, &is_str); +		break; + +	case MLX5_DBG_RSC_EQ: +		field = eq_read_field(d->dev, d->object, desc->i); +		break; + +	case MLX5_DBG_RSC_CQ: +		field = cq_read_field(d->dev, d->object, desc->i); +		break; + +	default: +		mlx5_core_warn(d->dev, "invalid resource type %d\n", d->type); +		return -EINVAL; +	} + + +	if (is_str) +		ret = snprintf(tbuf, sizeof(tbuf), "%s\n", (const char *)field); +	else +		ret = snprintf(tbuf, sizeof(tbuf), "0x%llx\n", field); + +	if (ret > 0) { +		if (copy_to_user(buf, tbuf, ret)) +			return -EFAULT; +	} + +	*pos += ret; +	return ret; +} + +static const struct file_operations fops = { +	.owner	= THIS_MODULE, +	.open	= simple_open, +	.read	= dbg_read, +}; + +static int add_res_tree(struct mlx5_core_dev *dev, enum dbg_rsc_type type, +			struct dentry *root, struct mlx5_rsc_debug **dbg, +			int rsn, char **field, int nfile, void *data) +{ +	struct mlx5_rsc_debug *d; +	char resn[32]; +	int err; +	int i; + +	d = kzalloc(sizeof(*d) + nfile * sizeof(d->fields[0]), GFP_KERNEL); +	if (!d) +		return -ENOMEM; + +	d->dev = dev; +	d->object = data; +	d->type = type; +	sprintf(resn, "0x%x", rsn); +	d->root = debugfs_create_dir(resn,  root); +	if (!d->root) { +		err = -ENOMEM; +		goto out_free; +	} + +	for (i = 0; i < nfile; i++) { +		d->fields[i].i = i; +		d->fields[i].dent = debugfs_create_file(field[i], 0400, +							d->root, &d->fields[i], +							&fops); +		if (!d->fields[i].dent) { +			err = -ENOMEM; +			goto out_rem; +		} +	} +	*dbg = d; + +	return 0; +out_rem: +	debugfs_remove_recursive(d->root); + +out_free: +	kfree(d); +	return err; +} + +static void rem_res_tree(struct mlx5_rsc_debug *d) +{ +	debugfs_remove_recursive(d->root); +	kfree(d); +} + +int mlx5_debug_qp_add(struct mlx5_core_dev *dev, struct mlx5_core_qp *qp) +{ +	int err; + +	if (!mlx5_debugfs_root) +		return 0; + +	err = add_res_tree(dev, MLX5_DBG_RSC_QP, dev->priv.qp_debugfs, +			   &qp->dbg, qp->qpn, qp_fields, +			   ARRAY_SIZE(qp_fields), qp); +	if (err) +		qp->dbg = NULL; + +	return err; +} + +void mlx5_debug_qp_remove(struct mlx5_core_dev *dev, struct mlx5_core_qp *qp) +{ +	if (!mlx5_debugfs_root) +		return; + +	if (qp->dbg) +		rem_res_tree(qp->dbg); +} + + +int mlx5_debug_eq_add(struct mlx5_core_dev *dev, struct mlx5_eq *eq) +{ +	int err; + +	if (!mlx5_debugfs_root) +		return 0; + +	err = add_res_tree(dev, MLX5_DBG_RSC_EQ, dev->priv.eq_debugfs, +			   &eq->dbg, eq->eqn, eq_fields, +			   ARRAY_SIZE(eq_fields), eq); +	if (err) +		eq->dbg = NULL; + +	return err; +} + +void mlx5_debug_eq_remove(struct mlx5_core_dev *dev, struct mlx5_eq *eq) +{ +	if (!mlx5_debugfs_root) +		return; + +	if (eq->dbg) +		rem_res_tree(eq->dbg); +} + +int mlx5_debug_cq_add(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq) +{ +	int err; + +	if (!mlx5_debugfs_root) +		return 0; + +	err = add_res_tree(dev, MLX5_DBG_RSC_CQ, dev->priv.cq_debugfs, +			   &cq->dbg, cq->cqn, cq_fields, +			   ARRAY_SIZE(cq_fields), cq); +	if (err) +		cq->dbg = NULL; + +	return err; +} + +void mlx5_debug_cq_remove(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq) +{ +	if (!mlx5_debugfs_root) +		return; + +	if (cq->dbg) +		rem_res_tree(cq->dbg); +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eq.c b/drivers/net/ethernet/mellanox/mlx5/core/eq.c new file mode 100644 index 00000000000..7f39ebcd6ad --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/eq.c @@ -0,0 +1,526 @@ +/* + * Copyright (c) 2013, Mellanox Technologies 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. + */ + +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/mlx5/driver.h> +#include <linux/mlx5/cmd.h> +#include "mlx5_core.h" + +enum { +	MLX5_EQE_SIZE		= sizeof(struct mlx5_eqe), +	MLX5_EQE_OWNER_INIT_VAL	= 0x1, +}; + +enum { +	MLX5_EQ_STATE_ARMED		= 0x9, +	MLX5_EQ_STATE_FIRED		= 0xa, +	MLX5_EQ_STATE_ALWAYS_ARMED	= 0xb, +}; + +enum { +	MLX5_NUM_SPARE_EQE	= 0x80, +	MLX5_NUM_ASYNC_EQE	= 0x100, +	MLX5_NUM_CMD_EQE	= 32, +}; + +enum { +	MLX5_EQ_DOORBEL_OFFSET	= 0x40, +}; + +#define MLX5_ASYNC_EVENT_MASK ((1ull << MLX5_EVENT_TYPE_PATH_MIG)	    | \ +			       (1ull << MLX5_EVENT_TYPE_COMM_EST)	    | \ +			       (1ull << MLX5_EVENT_TYPE_SQ_DRAINED)	    | \ +			       (1ull << MLX5_EVENT_TYPE_CQ_ERROR)	    | \ +			       (1ull << MLX5_EVENT_TYPE_WQ_CATAS_ERROR)	    | \ +			       (1ull << MLX5_EVENT_TYPE_PATH_MIG_FAILED)    | \ +			       (1ull << MLX5_EVENT_TYPE_WQ_INVAL_REQ_ERROR) | \ +			       (1ull << MLX5_EVENT_TYPE_WQ_ACCESS_ERROR)    | \ +			       (1ull << MLX5_EVENT_TYPE_PORT_CHANGE)	    | \ +			       (1ull << MLX5_EVENT_TYPE_SRQ_CATAS_ERROR)    | \ +			       (1ull << MLX5_EVENT_TYPE_SRQ_LAST_WQE)	    | \ +			       (1ull << MLX5_EVENT_TYPE_SRQ_RQ_LIMIT)) + +struct map_eq_in { +	u64	mask; +	u32	reserved; +	u32	unmap_eqn; +}; + +struct cre_des_eq { +	u8	reserved[15]; +	u8	eqn; +}; + +static int mlx5_cmd_destroy_eq(struct mlx5_core_dev *dev, u8 eqn) +{ +	struct mlx5_destroy_eq_mbox_in in; +	struct mlx5_destroy_eq_mbox_out out; +	int err; + +	memset(&in, 0, sizeof(in)); +	memset(&out, 0, sizeof(out)); +	in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_DESTROY_EQ); +	in.eqn = eqn; +	err = mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out)); +	if (!err) +		goto ex; + +	if (out.hdr.status) +		err = mlx5_cmd_status_to_err(&out.hdr); + +ex: +	return err; +} + +static struct mlx5_eqe *get_eqe(struct mlx5_eq *eq, u32 entry) +{ +	return mlx5_buf_offset(&eq->buf, entry * MLX5_EQE_SIZE); +} + +static struct mlx5_eqe *next_eqe_sw(struct mlx5_eq *eq) +{ +	struct mlx5_eqe *eqe = get_eqe(eq, eq->cons_index & (eq->nent - 1)); + +	return ((eqe->owner & 1) ^ !!(eq->cons_index & eq->nent)) ? NULL : eqe; +} + +static const char *eqe_type_str(u8 type) +{ +	switch (type) { +	case MLX5_EVENT_TYPE_COMP: +		return "MLX5_EVENT_TYPE_COMP"; +	case MLX5_EVENT_TYPE_PATH_MIG: +		return "MLX5_EVENT_TYPE_PATH_MIG"; +	case MLX5_EVENT_TYPE_COMM_EST: +		return "MLX5_EVENT_TYPE_COMM_EST"; +	case MLX5_EVENT_TYPE_SQ_DRAINED: +		return "MLX5_EVENT_TYPE_SQ_DRAINED"; +	case MLX5_EVENT_TYPE_SRQ_LAST_WQE: +		return "MLX5_EVENT_TYPE_SRQ_LAST_WQE"; +	case MLX5_EVENT_TYPE_SRQ_RQ_LIMIT: +		return "MLX5_EVENT_TYPE_SRQ_RQ_LIMIT"; +	case MLX5_EVENT_TYPE_CQ_ERROR: +		return "MLX5_EVENT_TYPE_CQ_ERROR"; +	case MLX5_EVENT_TYPE_WQ_CATAS_ERROR: +		return "MLX5_EVENT_TYPE_WQ_CATAS_ERROR"; +	case MLX5_EVENT_TYPE_PATH_MIG_FAILED: +		return "MLX5_EVENT_TYPE_PATH_MIG_FAILED"; +	case MLX5_EVENT_TYPE_WQ_INVAL_REQ_ERROR: +		return "MLX5_EVENT_TYPE_WQ_INVAL_REQ_ERROR"; +	case MLX5_EVENT_TYPE_WQ_ACCESS_ERROR: +		return "MLX5_EVENT_TYPE_WQ_ACCESS_ERROR"; +	case MLX5_EVENT_TYPE_SRQ_CATAS_ERROR: +		return "MLX5_EVENT_TYPE_SRQ_CATAS_ERROR"; +	case MLX5_EVENT_TYPE_INTERNAL_ERROR: +		return "MLX5_EVENT_TYPE_INTERNAL_ERROR"; +	case MLX5_EVENT_TYPE_PORT_CHANGE: +		return "MLX5_EVENT_TYPE_PORT_CHANGE"; +	case MLX5_EVENT_TYPE_GPIO_EVENT: +		return "MLX5_EVENT_TYPE_GPIO_EVENT"; +	case MLX5_EVENT_TYPE_REMOTE_CONFIG: +		return "MLX5_EVENT_TYPE_REMOTE_CONFIG"; +	case MLX5_EVENT_TYPE_DB_BF_CONGESTION: +		return "MLX5_EVENT_TYPE_DB_BF_CONGESTION"; +	case MLX5_EVENT_TYPE_STALL_EVENT: +		return "MLX5_EVENT_TYPE_STALL_EVENT"; +	case MLX5_EVENT_TYPE_CMD: +		return "MLX5_EVENT_TYPE_CMD"; +	case MLX5_EVENT_TYPE_PAGE_REQUEST: +		return "MLX5_EVENT_TYPE_PAGE_REQUEST"; +	default: +		return "Unrecognized event"; +	} +} + +static enum mlx5_dev_event port_subtype_event(u8 subtype) +{ +	switch (subtype) { +	case MLX5_PORT_CHANGE_SUBTYPE_DOWN: +		return MLX5_DEV_EVENT_PORT_DOWN; +	case MLX5_PORT_CHANGE_SUBTYPE_ACTIVE: +		return MLX5_DEV_EVENT_PORT_UP; +	case MLX5_PORT_CHANGE_SUBTYPE_INITIALIZED: +		return MLX5_DEV_EVENT_PORT_INITIALIZED; +	case MLX5_PORT_CHANGE_SUBTYPE_LID: +		return MLX5_DEV_EVENT_LID_CHANGE; +	case MLX5_PORT_CHANGE_SUBTYPE_PKEY: +		return MLX5_DEV_EVENT_PKEY_CHANGE; +	case MLX5_PORT_CHANGE_SUBTYPE_GUID: +		return MLX5_DEV_EVENT_GUID_CHANGE; +	case MLX5_PORT_CHANGE_SUBTYPE_CLIENT_REREG: +		return MLX5_DEV_EVENT_CLIENT_REREG; +	} +	return -1; +} + +static void eq_update_ci(struct mlx5_eq *eq, int arm) +{ +	__be32 __iomem *addr = eq->doorbell + (arm ? 0 : 2); +	u32 val = (eq->cons_index & 0xffffff) | (eq->eqn << 24); +	__raw_writel((__force u32) cpu_to_be32(val), addr); +	/* We still want ordering, just not swabbing, so add a barrier */ +	mb(); +} + +static int mlx5_eq_int(struct mlx5_core_dev *dev, struct mlx5_eq *eq) +{ +	struct mlx5_eqe *eqe; +	int eqes_found = 0; +	int set_ci = 0; +	u32 cqn; +	u32 srqn; +	u8 port; + +	while ((eqe = next_eqe_sw(eq))) { +		/* +		 * Make sure we read EQ entry contents after we've +		 * checked the ownership bit. +		 */ +		rmb(); + +		mlx5_core_dbg(eq->dev, "eqn %d, eqe type %s\n", +			      eq->eqn, eqe_type_str(eqe->type)); +		switch (eqe->type) { +		case MLX5_EVENT_TYPE_COMP: +			cqn = be32_to_cpu(eqe->data.comp.cqn) & 0xffffff; +			mlx5_cq_completion(dev, cqn); +			break; + +		case MLX5_EVENT_TYPE_PATH_MIG: +		case MLX5_EVENT_TYPE_COMM_EST: +		case MLX5_EVENT_TYPE_SQ_DRAINED: +		case MLX5_EVENT_TYPE_SRQ_LAST_WQE: +		case MLX5_EVENT_TYPE_WQ_CATAS_ERROR: +		case MLX5_EVENT_TYPE_PATH_MIG_FAILED: +		case MLX5_EVENT_TYPE_WQ_INVAL_REQ_ERROR: +		case MLX5_EVENT_TYPE_WQ_ACCESS_ERROR: +			mlx5_core_dbg(dev, "event %s(%d) arrived\n", +				      eqe_type_str(eqe->type), eqe->type); +			mlx5_qp_event(dev, be32_to_cpu(eqe->data.qp_srq.qp_srq_n) & 0xffffff, +				      eqe->type); +			break; + +		case MLX5_EVENT_TYPE_SRQ_RQ_LIMIT: +		case MLX5_EVENT_TYPE_SRQ_CATAS_ERROR: +			srqn = be32_to_cpu(eqe->data.qp_srq.qp_srq_n) & 0xffffff; +			mlx5_core_dbg(dev, "SRQ event %s(%d): srqn 0x%x\n", +				      eqe_type_str(eqe->type), eqe->type, srqn); +			mlx5_srq_event(dev, srqn, eqe->type); +			break; + +		case MLX5_EVENT_TYPE_CMD: +			mlx5_cmd_comp_handler(dev, be32_to_cpu(eqe->data.cmd.vector)); +			break; + +		case MLX5_EVENT_TYPE_PORT_CHANGE: +			port = (eqe->data.port.port >> 4) & 0xf; +			switch (eqe->sub_type) { +			case MLX5_PORT_CHANGE_SUBTYPE_DOWN: +			case MLX5_PORT_CHANGE_SUBTYPE_ACTIVE: +			case MLX5_PORT_CHANGE_SUBTYPE_LID: +			case MLX5_PORT_CHANGE_SUBTYPE_PKEY: +			case MLX5_PORT_CHANGE_SUBTYPE_GUID: +			case MLX5_PORT_CHANGE_SUBTYPE_CLIENT_REREG: +			case MLX5_PORT_CHANGE_SUBTYPE_INITIALIZED: +				dev->event(dev, port_subtype_event(eqe->sub_type), &port); +				break; +			default: +				mlx5_core_warn(dev, "Port event with unrecognized subtype: port %d, sub_type %d\n", +					       port, eqe->sub_type); +			} +			break; +		case MLX5_EVENT_TYPE_CQ_ERROR: +			cqn = be32_to_cpu(eqe->data.cq_err.cqn) & 0xffffff; +			mlx5_core_warn(dev, "CQ error on CQN 0x%x, syndrom 0x%x\n", +				       cqn, eqe->data.cq_err.syndrome); +			mlx5_cq_event(dev, cqn, eqe->type); +			break; + +		case MLX5_EVENT_TYPE_PAGE_REQUEST: +			{ +				u16 func_id = be16_to_cpu(eqe->data.req_pages.func_id); +				s32 npages = be32_to_cpu(eqe->data.req_pages.num_pages); + +				mlx5_core_dbg(dev, "page request for func 0x%x, npages %d\n", +					      func_id, npages); +				mlx5_core_req_pages_handler(dev, func_id, npages); +			} +			break; + + +		default: +			mlx5_core_warn(dev, "Unhandled event 0x%x on EQ 0x%x\n", +				       eqe->type, eq->eqn); +			break; +		} + +		++eq->cons_index; +		eqes_found = 1; +		++set_ci; + +		/* The HCA will think the queue has overflowed if we +		 * don't tell it we've been processing events.  We +		 * create our EQs with MLX5_NUM_SPARE_EQE extra +		 * entries, so we must update our consumer index at +		 * least that often. +		 */ +		if (unlikely(set_ci >= MLX5_NUM_SPARE_EQE)) { +			eq_update_ci(eq, 0); +			set_ci = 0; +		} +	} + +	eq_update_ci(eq, 1); + +	return eqes_found; +} + +static irqreturn_t mlx5_msix_handler(int irq, void *eq_ptr) +{ +	struct mlx5_eq *eq = eq_ptr; +	struct mlx5_core_dev *dev = eq->dev; + +	mlx5_eq_int(dev, eq); + +	/* MSI-X vectors always belong to us */ +	return IRQ_HANDLED; +} + +static void init_eq_buf(struct mlx5_eq *eq) +{ +	struct mlx5_eqe *eqe; +	int i; + +	for (i = 0; i < eq->nent; i++) { +		eqe = get_eqe(eq, i); +		eqe->owner = MLX5_EQE_OWNER_INIT_VAL; +	} +} + +int mlx5_create_map_eq(struct mlx5_core_dev *dev, struct mlx5_eq *eq, u8 vecidx, +		       int nent, u64 mask, const char *name, struct mlx5_uar *uar) +{ +	struct mlx5_eq_table *table = &dev->priv.eq_table; +	struct mlx5_create_eq_mbox_in *in; +	struct mlx5_create_eq_mbox_out out; +	int err; +	int inlen; + +	eq->nent = roundup_pow_of_two(nent + MLX5_NUM_SPARE_EQE); +	err = mlx5_buf_alloc(dev, eq->nent * MLX5_EQE_SIZE, 2 * PAGE_SIZE, +			     &eq->buf); +	if (err) +		return err; + +	init_eq_buf(eq); + +	inlen = sizeof(*in) + sizeof(in->pas[0]) * eq->buf.npages; +	in = mlx5_vzalloc(inlen); +	if (!in) { +		err = -ENOMEM; +		goto err_buf; +	} +	memset(&out, 0, sizeof(out)); + +	mlx5_fill_page_array(&eq->buf, in->pas); + +	in->hdr.opcode = cpu_to_be16(MLX5_CMD_OP_CREATE_EQ); +	in->ctx.log_sz_usr_page = cpu_to_be32(ilog2(eq->nent) << 24 | uar->index); +	in->ctx.intr = vecidx; +	in->ctx.log_page_size = eq->buf.page_shift - MLX5_ADAPTER_PAGE_SHIFT; +	in->events_mask = cpu_to_be64(mask); + +	err = mlx5_cmd_exec(dev, in, inlen, &out, sizeof(out)); +	if (err) +		goto err_in; + +	if (out.hdr.status) { +		err = mlx5_cmd_status_to_err(&out.hdr); +		goto err_in; +	} + +	snprintf(eq->name, MLX5_MAX_EQ_NAME, "%s@pci:%s", +		 name, pci_name(dev->pdev)); +	eq->eqn = out.eq_number; +	err = request_irq(table->msix_arr[vecidx].vector, mlx5_msix_handler, 0, +			  eq->name, eq); +	if (err) +		goto err_eq; + +	eq->irqn = vecidx; +	eq->dev = dev; +	eq->doorbell = uar->map + MLX5_EQ_DOORBEL_OFFSET; + +	err = mlx5_debug_eq_add(dev, eq); +	if (err) +		goto err_irq; + +	/* EQs are created in ARMED state +	 */ +	eq_update_ci(eq, 1); + +	mlx5_vfree(in); +	return 0; + +err_irq: +	free_irq(table->msix_arr[vecidx].vector, eq); + +err_eq: +	mlx5_cmd_destroy_eq(dev, eq->eqn); + +err_in: +	mlx5_vfree(in); + +err_buf: +	mlx5_buf_free(dev, &eq->buf); +	return err; +} +EXPORT_SYMBOL_GPL(mlx5_create_map_eq); + +int mlx5_destroy_unmap_eq(struct mlx5_core_dev *dev, struct mlx5_eq *eq) +{ +	struct mlx5_eq_table *table = &dev->priv.eq_table; +	int err; + +	mlx5_debug_eq_remove(dev, eq); +	free_irq(table->msix_arr[eq->irqn].vector, eq); +	err = mlx5_cmd_destroy_eq(dev, eq->eqn); +	if (err) +		mlx5_core_warn(dev, "failed to destroy a previously created eq: eqn %d\n", +			       eq->eqn); +	mlx5_buf_free(dev, &eq->buf); + +	return err; +} +EXPORT_SYMBOL_GPL(mlx5_destroy_unmap_eq); + +int mlx5_eq_init(struct mlx5_core_dev *dev) +{ +	int err; + +	spin_lock_init(&dev->priv.eq_table.lock); + +	err = mlx5_eq_debugfs_init(dev); + +	return err; +} + + +void mlx5_eq_cleanup(struct mlx5_core_dev *dev) +{ +	mlx5_eq_debugfs_cleanup(dev); +} + +int mlx5_start_eqs(struct mlx5_core_dev *dev) +{ +	struct mlx5_eq_table *table = &dev->priv.eq_table; +	int err; + +	err = mlx5_create_map_eq(dev, &table->cmd_eq, MLX5_EQ_VEC_CMD, +				 MLX5_NUM_CMD_EQE, 1ull << MLX5_EVENT_TYPE_CMD, +				 "mlx5_cmd_eq", &dev->priv.uuari.uars[0]); +	if (err) { +		mlx5_core_warn(dev, "failed to create cmd EQ %d\n", err); +		return err; +	} + +	mlx5_cmd_use_events(dev); + +	err = mlx5_create_map_eq(dev, &table->async_eq, MLX5_EQ_VEC_ASYNC, +				 MLX5_NUM_ASYNC_EQE, MLX5_ASYNC_EVENT_MASK, +				 "mlx5_async_eq", &dev->priv.uuari.uars[0]); +	if (err) { +		mlx5_core_warn(dev, "failed to create async EQ %d\n", err); +		goto err1; +	} + +	err = mlx5_create_map_eq(dev, &table->pages_eq, +				 MLX5_EQ_VEC_PAGES, +				 dev->caps.max_vf + 1, +				 1 << MLX5_EVENT_TYPE_PAGE_REQUEST, "mlx5_pages_eq", +				 &dev->priv.uuari.uars[0]); +	if (err) { +		mlx5_core_warn(dev, "failed to create pages EQ %d\n", err); +		goto err2; +	} + +	return err; + +err2: +	mlx5_destroy_unmap_eq(dev, &table->async_eq); + +err1: +	mlx5_cmd_use_polling(dev); +	mlx5_destroy_unmap_eq(dev, &table->cmd_eq); +	return err; +} + +int mlx5_stop_eqs(struct mlx5_core_dev *dev) +{ +	struct mlx5_eq_table *table = &dev->priv.eq_table; +	int err; + +	err = mlx5_destroy_unmap_eq(dev, &table->pages_eq); +	if (err) +		return err; + +	mlx5_destroy_unmap_eq(dev, &table->async_eq); +	mlx5_cmd_use_polling(dev); + +	err = mlx5_destroy_unmap_eq(dev, &table->cmd_eq); +	if (err) +		mlx5_cmd_use_events(dev); + +	return err; +} + +int mlx5_core_eq_query(struct mlx5_core_dev *dev, struct mlx5_eq *eq, +		       struct mlx5_query_eq_mbox_out *out, int outlen) +{ +	struct mlx5_query_eq_mbox_in in; +	int err; + +	memset(&in, 0, sizeof(in)); +	memset(out, 0, outlen); +	in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_QUERY_EQ); +	in.eqn = eq->eqn; +	err = mlx5_cmd_exec(dev, &in, sizeof(in), out, outlen); +	if (err) +		return err; + +	if (out->hdr.status) +		err = mlx5_cmd_status_to_err(&out->hdr); + +	return err; +} +EXPORT_SYMBOL_GPL(mlx5_core_eq_query); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fw.c b/drivers/net/ethernet/mellanox/mlx5/core/fw.c new file mode 100644 index 00000000000..f012658b6a9 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/fw.c @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2013, Mellanox Technologies 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. + */ + +#include <linux/mlx5/driver.h> +#include <linux/mlx5/cmd.h> +#include <linux/module.h> +#include "mlx5_core.h" + +int mlx5_cmd_query_adapter(struct mlx5_core_dev *dev) +{ +	struct mlx5_cmd_query_adapter_mbox_out *out; +	struct mlx5_cmd_query_adapter_mbox_in in; +	int err; + +	out = kzalloc(sizeof(*out), GFP_KERNEL); +	if (!out) +		return -ENOMEM; + +	memset(&in, 0, sizeof(in)); +	in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_QUERY_ADAPTER); +	err = mlx5_cmd_exec(dev, &in, sizeof(in), out, sizeof(*out)); +	if (err) +		goto out_out; + +	if (out->hdr.status) { +		err = mlx5_cmd_status_to_err(&out->hdr); +		goto out_out; +	} + +	memcpy(dev->board_id, out->vsd_psid, sizeof(out->vsd_psid)); + +out_out: +	kfree(out); + +	return err; +} + +int mlx5_cmd_query_hca_cap(struct mlx5_core_dev *dev, +			   struct mlx5_caps *caps) +{ +	struct mlx5_cmd_query_hca_cap_mbox_out *out; +	struct mlx5_cmd_query_hca_cap_mbox_in in; +	struct mlx5_query_special_ctxs_mbox_out ctx_out; +	struct mlx5_query_special_ctxs_mbox_in ctx_in; +	int err; +	u16 t16; + +	out = kzalloc(sizeof(*out), GFP_KERNEL); +	if (!out) +		return -ENOMEM; + +	memset(&in, 0, sizeof(in)); +	in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_QUERY_HCA_CAP); +	in.hdr.opmod  = cpu_to_be16(0x1); +	err = mlx5_cmd_exec(dev, &in, sizeof(in), out, sizeof(*out)); +	if (err) +		goto out_out; + +	if (out->hdr.status) { +		err = mlx5_cmd_status_to_err(&out->hdr); +		goto out_out; +	} + + +	caps->log_max_eq = out->hca_cap.log_max_eq & 0xf; +	caps->max_cqes = 1 << out->hca_cap.log_max_cq_sz; +	caps->max_wqes = 1 << out->hca_cap.log_max_qp_sz; +	caps->max_sq_desc_sz = be16_to_cpu(out->hca_cap.max_desc_sz_sq); +	caps->max_rq_desc_sz = be16_to_cpu(out->hca_cap.max_desc_sz_rq); +	caps->flags = be64_to_cpu(out->hca_cap.flags); +	caps->stat_rate_support = be16_to_cpu(out->hca_cap.stat_rate_support); +	caps->log_max_msg = out->hca_cap.log_max_msg & 0x1f; +	caps->num_ports = out->hca_cap.num_ports & 0xf; +	caps->log_max_cq = out->hca_cap.log_max_cq & 0x1f; +	if (caps->num_ports > MLX5_MAX_PORTS) { +		mlx5_core_err(dev, "device has %d ports while the driver supports max %d ports\n", +			      caps->num_ports, MLX5_MAX_PORTS); +		err = -EINVAL; +		goto out_out; +	} +	caps->log_max_qp = out->hca_cap.log_max_qp & 0x1f; +	caps->log_max_mkey = out->hca_cap.log_max_mkey & 0x3f; +	caps->log_max_pd = out->hca_cap.log_max_pd & 0x1f; +	caps->log_max_srq = out->hca_cap.log_max_srqs & 0x1f; +	caps->local_ca_ack_delay = out->hca_cap.local_ca_ack_delay & 0x1f; +	caps->log_max_mcg = out->hca_cap.log_max_mcg; +	caps->max_qp_mcg = be32_to_cpu(out->hca_cap.max_qp_mcg) & 0xffffff; +	caps->max_ra_res_qp = 1 << (out->hca_cap.log_max_ra_res_qp & 0x3f); +	caps->max_ra_req_qp = 1 << (out->hca_cap.log_max_ra_req_qp & 0x3f); +	caps->max_srq_wqes = 1 << out->hca_cap.log_max_srq_sz; +	t16 = be16_to_cpu(out->hca_cap.bf_log_bf_reg_size); +	if (t16 & 0x8000) { +		caps->bf_reg_size = 1 << (t16 & 0x1f); +		caps->bf_regs_per_page = MLX5_BF_REGS_PER_PAGE; +	} else { +		caps->bf_reg_size = 0; +		caps->bf_regs_per_page = 0; +	} +	caps->min_page_sz = ~(u32)((1 << out->hca_cap.log_pg_sz) - 1); + +	memset(&ctx_in, 0, sizeof(ctx_in)); +	memset(&ctx_out, 0, sizeof(ctx_out)); +	ctx_in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_QUERY_SPECIAL_CONTEXTS); +	err = mlx5_cmd_exec(dev, &ctx_in, sizeof(ctx_in), +				 &ctx_out, sizeof(ctx_out)); +	if (err) +		goto out_out; + +	if (ctx_out.hdr.status) +		err = mlx5_cmd_status_to_err(&ctx_out.hdr); + +	caps->reserved_lkey = be32_to_cpu(ctx_out.reserved_lkey); + +out_out: +	kfree(out); + +	return err; +} + +int mlx5_cmd_init_hca(struct mlx5_core_dev *dev) +{ +	struct mlx5_cmd_init_hca_mbox_in in; +	struct mlx5_cmd_init_hca_mbox_out out; +	int err; + +	memset(&in, 0, sizeof(in)); +	memset(&out, 0, sizeof(out)); +	in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_INIT_HCA); +	err = mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out)); +	if (err) +		return err; + +	if (out.hdr.status) +		err = mlx5_cmd_status_to_err(&out.hdr); + +	return err; +} + +int mlx5_cmd_teardown_hca(struct mlx5_core_dev *dev) +{ +	struct mlx5_cmd_teardown_hca_mbox_in in; +	struct mlx5_cmd_teardown_hca_mbox_out out; +	int err; + +	memset(&in, 0, sizeof(in)); +	memset(&out, 0, sizeof(out)); +	in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_TEARDOWN_HCA); +	err = mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out)); +	if (err) +		return err; + +	if (out.hdr.status) +		err = mlx5_cmd_status_to_err(&out.hdr); + +	return err; +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/health.c b/drivers/net/ethernet/mellanox/mlx5/core/health.c new file mode 100644 index 00000000000..3e6670c4a7c --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/health.c @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2013, Mellanox Technologies 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. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/random.h> +#include <linux/vmalloc.h> +#include <linux/mlx5/driver.h> +#include <linux/mlx5/cmd.h> +#include "mlx5_core.h" + +enum { +	MLX5_HEALTH_POLL_INTERVAL	= 2 * HZ, +	MAX_MISSES			= 3, +}; + +enum { +	MLX5_HEALTH_SYNDR_FW_ERR		= 0x1, +	MLX5_HEALTH_SYNDR_IRISC_ERR		= 0x7, +	MLX5_HEALTH_SYNDR_CRC_ERR		= 0x9, +	MLX5_HEALTH_SYNDR_FETCH_PCI_ERR		= 0xa, +	MLX5_HEALTH_SYNDR_HW_FTL_ERR		= 0xb, +	MLX5_HEALTH_SYNDR_ASYNC_EQ_OVERRUN_ERR	= 0xc, +	MLX5_HEALTH_SYNDR_EQ_ERR		= 0xd, +	MLX5_HEALTH_SYNDR_FFSER_ERR		= 0xf, +}; + +static DEFINE_SPINLOCK(health_lock); +static LIST_HEAD(health_list); +static struct work_struct health_work; + +static void health_care(struct work_struct *work) +{ +	struct mlx5_core_health *health, *n; +	struct mlx5_core_dev *dev; +	struct mlx5_priv *priv; +	LIST_HEAD(tlist); + +	spin_lock_irq(&health_lock); +	list_splice_init(&health_list, &tlist); + +	spin_unlock_irq(&health_lock); + +	list_for_each_entry_safe(health, n, &tlist, list) { +		priv = container_of(health, struct mlx5_priv, health); +		dev = container_of(priv, struct mlx5_core_dev, priv); +		mlx5_core_warn(dev, "handling bad device here\n"); +		/* nothing yet */ +		spin_lock_irq(&health_lock); +		list_del_init(&health->list); +		spin_unlock_irq(&health_lock); +	} +} + +static const char *hsynd_str(u8 synd) +{ +	switch (synd) { +	case MLX5_HEALTH_SYNDR_FW_ERR: +		return "firmware internal error"; +	case MLX5_HEALTH_SYNDR_IRISC_ERR: +		return "irisc not responding"; +	case MLX5_HEALTH_SYNDR_CRC_ERR: +		return "firmware CRC error"; +	case MLX5_HEALTH_SYNDR_FETCH_PCI_ERR: +		return "ICM fetch PCI error"; +	case MLX5_HEALTH_SYNDR_HW_FTL_ERR: +		return "HW fatal error\n"; +	case MLX5_HEALTH_SYNDR_ASYNC_EQ_OVERRUN_ERR: +		return "async EQ buffer overrun"; +	case MLX5_HEALTH_SYNDR_EQ_ERR: +		return "EQ error"; +	case MLX5_HEALTH_SYNDR_FFSER_ERR: +		return "FFSER error"; +	default: +		return "unrecognized error"; +	} +} + +static u16 read_be16(__be16 __iomem *p) +{ +	return swab16(readl((__force u16 __iomem *) p)); +} + +static u32 read_be32(__be32 __iomem *p) +{ +	return swab32(readl((__force u32 __iomem *) p)); +} + +static void print_health_info(struct mlx5_core_dev *dev) +{ +	struct mlx5_core_health *health = &dev->priv.health; +	struct health_buffer __iomem *h = health->health; +	int i; + +	for (i = 0; i < ARRAY_SIZE(h->assert_var); i++) +		pr_info("assert_var[%d] 0x%08x\n", i, read_be32(h->assert_var + i)); + +	pr_info("assert_exit_ptr 0x%08x\n", read_be32(&h->assert_exit_ptr)); +	pr_info("assert_callra 0x%08x\n", read_be32(&h->assert_callra)); +	pr_info("fw_ver 0x%08x\n", read_be32(&h->fw_ver)); +	pr_info("hw_id 0x%08x\n", read_be32(&h->hw_id)); +	pr_info("irisc_index %d\n", readb(&h->irisc_index)); +	pr_info("synd 0x%x: %s\n", readb(&h->synd), hsynd_str(readb(&h->synd))); +	pr_info("ext_sync 0x%04x\n", read_be16(&h->ext_sync)); +} + +static void poll_health(unsigned long data) +{ +	struct mlx5_core_dev *dev = (struct mlx5_core_dev *)data; +	struct mlx5_core_health *health = &dev->priv.health; +	unsigned long next; +	u32 count; + +	count = ioread32be(health->health_counter); +	if (count == health->prev) +		++health->miss_counter; +	else +		health->miss_counter = 0; + +	health->prev = count; +	if (health->miss_counter == MAX_MISSES) { +		mlx5_core_err(dev, "device's health compromised\n"); +		print_health_info(dev); +		spin_lock_irq(&health_lock); +		list_add_tail(&health->list, &health_list); +		spin_unlock_irq(&health_lock); + +		queue_work(mlx5_core_wq, &health_work); +	} else { +		get_random_bytes(&next, sizeof(next)); +		next %= HZ; +		next += jiffies + MLX5_HEALTH_POLL_INTERVAL; +		mod_timer(&health->timer, next); +	} +} + +void mlx5_start_health_poll(struct mlx5_core_dev *dev) +{ +	struct mlx5_core_health *health = &dev->priv.health; + +	INIT_LIST_HEAD(&health->list); +	init_timer(&health->timer); +	health->health = &dev->iseg->health; +	health->health_counter = &dev->iseg->health_counter; + +	health->timer.data = (unsigned long)dev; +	health->timer.function = poll_health; +	health->timer.expires = round_jiffies(jiffies + MLX5_HEALTH_POLL_INTERVAL); +	add_timer(&health->timer); +} + +void mlx5_stop_health_poll(struct mlx5_core_dev *dev) +{ +	struct mlx5_core_health *health = &dev->priv.health; + +	del_timer_sync(&health->timer); + +	spin_lock_irq(&health_lock); +	if (!list_empty(&health->list)) +		list_del_init(&health->list); +	spin_unlock_irq(&health_lock); +} + +void mlx5_health_cleanup(void) +{ +} + +void  __init mlx5_health_init(void) +{ +	INIT_WORK(&health_work, health_care); +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mad.c b/drivers/net/ethernet/mellanox/mlx5/core/mad.c new file mode 100644 index 00000000000..18d6fd5dd90 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/mad.c @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2013, Mellanox Technologies 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. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mlx5/driver.h> +#include <linux/mlx5/cmd.h> +#include "mlx5_core.h" + +int mlx5_core_mad_ifc(struct mlx5_core_dev *dev, void *inb, void *outb, +		      u16 opmod, int port) +{ +	struct mlx5_mad_ifc_mbox_in *in = NULL; +	struct mlx5_mad_ifc_mbox_out *out = NULL; +	int err; + +	in = kzalloc(sizeof(*in), GFP_KERNEL); +	if (!in) +		return -ENOMEM; + +	out = kzalloc(sizeof(*out), GFP_KERNEL); +	if (!out) { +		err = -ENOMEM; +		goto out; +	} + +	in->hdr.opcode = cpu_to_be16(MLX5_CMD_OP_MAD_IFC); +	in->hdr.opmod = cpu_to_be16(opmod); +	in->port = port; + +	memcpy(in->data, inb, sizeof(in->data)); + +	err = mlx5_cmd_exec(dev, in, sizeof(*in), out, sizeof(*out)); +	if (err) +		goto out; + +	if (out->hdr.status) { +		err = mlx5_cmd_status_to_err(&out->hdr); +		goto out; +	} + +	memcpy(outb, out->data, sizeof(out->data)); + +out: +	kfree(out); +	kfree(in); +	return err; +} +EXPORT_SYMBOL_GPL(mlx5_core_mad_ifc); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c new file mode 100644 index 00000000000..ee24f132e31 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -0,0 +1,548 @@ +/* + * Copyright (c) 2013, Mellanox Technologies 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. + */ + +#include <asm-generic/kmap_types.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/pci.h> +#include <linux/dma-mapping.h> +#include <linux/slab.h> +#include <linux/io-mapping.h> +#include <linux/mlx5/driver.h> +#include <linux/mlx5/cq.h> +#include <linux/mlx5/qp.h> +#include <linux/mlx5/srq.h> +#include <linux/debugfs.h> +#include "mlx5_core.h" + +#define DRIVER_NAME "mlx5_core" +#define DRIVER_VERSION "2.2-1" +#define DRIVER_RELDATE	"Feb 2014" + +MODULE_AUTHOR("Eli Cohen <eli@mellanox.com>"); +MODULE_DESCRIPTION("Mellanox ConnectX-IB HCA core library"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_VERSION(DRIVER_VERSION); + +int mlx5_core_debug_mask; +module_param_named(debug_mask, mlx5_core_debug_mask, int, 0644); +MODULE_PARM_DESC(debug_mask, "debug mask: 1 = dump cmd data, 2 = dump cmd exec time, 3 = both. Default=0"); + +struct workqueue_struct *mlx5_core_wq; + +static int set_dma_caps(struct pci_dev *pdev) +{ +	int err; + +	err = pci_set_dma_mask(pdev, DMA_BIT_MASK(64)); +	if (err) { +		dev_warn(&pdev->dev, "Warning: couldn't set 64-bit PCI DMA mask\n"); +		err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); +		if (err) { +			dev_err(&pdev->dev, "Can't set PCI DMA mask, aborting\n"); +			return err; +		} +	} + +	err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)); +	if (err) { +		dev_warn(&pdev->dev, +			 "Warning: couldn't set 64-bit consistent PCI DMA mask\n"); +		err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); +		if (err) { +			dev_err(&pdev->dev, +				"Can't set consistent PCI DMA mask, aborting\n"); +			return err; +		} +	} + +	dma_set_max_seg_size(&pdev->dev, 2u * 1024 * 1024 * 1024); +	return err; +} + +static int request_bar(struct pci_dev *pdev) +{ +	int err = 0; + +	if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) { +		dev_err(&pdev->dev, "Missing registers BAR, aborting\n"); +		return -ENODEV; +	} + +	err = pci_request_regions(pdev, DRIVER_NAME); +	if (err) +		dev_err(&pdev->dev, "Couldn't get PCI resources, aborting\n"); + +	return err; +} + +static void release_bar(struct pci_dev *pdev) +{ +	pci_release_regions(pdev); +} + +static int mlx5_enable_msix(struct mlx5_core_dev *dev) +{ +	struct mlx5_eq_table *table = &dev->priv.eq_table; +	int num_eqs = 1 << dev->caps.log_max_eq; +	int nvec; +	int i; + +	nvec = dev->caps.num_ports * num_online_cpus() + MLX5_EQ_VEC_COMP_BASE; +	nvec = min_t(int, nvec, num_eqs); +	if (nvec <= MLX5_EQ_VEC_COMP_BASE) +		return -ENOMEM; + +	table->msix_arr = kzalloc(nvec * sizeof(*table->msix_arr), GFP_KERNEL); +	if (!table->msix_arr) +		return -ENOMEM; + +	for (i = 0; i < nvec; i++) +		table->msix_arr[i].entry = i; + +	nvec = pci_enable_msix_range(dev->pdev, table->msix_arr, +				     MLX5_EQ_VEC_COMP_BASE, nvec); +	if (nvec < 0) +		return nvec; + +	table->num_comp_vectors = nvec - MLX5_EQ_VEC_COMP_BASE; + +	return 0; +} + +static void mlx5_disable_msix(struct mlx5_core_dev *dev) +{ +	struct mlx5_eq_table *table = &dev->priv.eq_table; + +	pci_disable_msix(dev->pdev); +	kfree(table->msix_arr); +} + +struct mlx5_reg_host_endianess { +	u8	he; +	u8      rsvd[15]; +}; + + +#define CAP_MASK(pos, size) ((u64)((1 << (size)) - 1) << (pos)) + +enum { +	MLX5_CAP_BITS_RW_MASK	= CAP_MASK(MLX5_CAP_OFF_CMDIF_CSUM, 2) | +				  CAP_MASK(MLX5_CAP_OFF_DCT, 1), +}; + +/* selectively copy writable fields clearing any reserved area + */ +static void copy_rw_fields(struct mlx5_hca_cap *to, struct mlx5_hca_cap *from) +{ +	u64 v64; + +	to->log_max_qp = from->log_max_qp & 0x1f; +	to->log_max_ra_req_dc = from->log_max_ra_req_dc & 0x3f; +	to->log_max_ra_res_dc = from->log_max_ra_res_dc & 0x3f; +	to->log_max_ra_req_qp = from->log_max_ra_req_qp & 0x3f; +	to->log_max_ra_res_qp = from->log_max_ra_res_qp & 0x3f; +	to->log_max_atomic_size_qp = from->log_max_atomic_size_qp; +	to->log_max_atomic_size_dc = from->log_max_atomic_size_dc; +	v64 = be64_to_cpu(from->flags) & MLX5_CAP_BITS_RW_MASK; +	to->flags = cpu_to_be64(v64); +} + +enum { +	HCA_CAP_OPMOD_GET_MAX	= 0, +	HCA_CAP_OPMOD_GET_CUR	= 1, +}; + +static int handle_hca_cap(struct mlx5_core_dev *dev) +{ +	struct mlx5_cmd_query_hca_cap_mbox_out *query_out = NULL; +	struct mlx5_cmd_set_hca_cap_mbox_in *set_ctx = NULL; +	struct mlx5_cmd_query_hca_cap_mbox_in query_ctx; +	struct mlx5_cmd_set_hca_cap_mbox_out set_out; +	u64 flags; +	int err; + +	memset(&query_ctx, 0, sizeof(query_ctx)); +	query_out = kzalloc(sizeof(*query_out), GFP_KERNEL); +	if (!query_out) +		return -ENOMEM; + +	set_ctx = kzalloc(sizeof(*set_ctx), GFP_KERNEL); +	if (!set_ctx) { +		err = -ENOMEM; +		goto query_ex; +	} + +	query_ctx.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_QUERY_HCA_CAP); +	query_ctx.hdr.opmod  = cpu_to_be16(HCA_CAP_OPMOD_GET_CUR); +	err = mlx5_cmd_exec(dev, &query_ctx, sizeof(query_ctx), +				 query_out, sizeof(*query_out)); +	if (err) +		goto query_ex; + +	err = mlx5_cmd_status_to_err(&query_out->hdr); +	if (err) { +		mlx5_core_warn(dev, "query hca cap failed, %d\n", err); +		goto query_ex; +	} + +	copy_rw_fields(&set_ctx->hca_cap, &query_out->hca_cap); + +	if (dev->profile->mask & MLX5_PROF_MASK_QP_SIZE) +		set_ctx->hca_cap.log_max_qp = dev->profile->log_max_qp; + +	flags = be64_to_cpu(query_out->hca_cap.flags); +	/* disable checksum */ +	flags &= ~MLX5_DEV_CAP_FLAG_CMDIF_CSUM; + +	set_ctx->hca_cap.flags = cpu_to_be64(flags); +	memset(&set_out, 0, sizeof(set_out)); +	set_ctx->hca_cap.log_uar_page_sz = cpu_to_be16(PAGE_SHIFT - 12); +	set_ctx->hdr.opcode = cpu_to_be16(MLX5_CMD_OP_SET_HCA_CAP); +	err = mlx5_cmd_exec(dev, set_ctx, sizeof(*set_ctx), +				 &set_out, sizeof(set_out)); +	if (err) { +		mlx5_core_warn(dev, "set hca cap failed, %d\n", err); +		goto query_ex; +	} + +	err = mlx5_cmd_status_to_err(&set_out.hdr); +	if (err) +		goto query_ex; + +query_ex: +	kfree(query_out); +	kfree(set_ctx); + +	return err; +} + +static int set_hca_ctrl(struct mlx5_core_dev *dev) +{ +	struct mlx5_reg_host_endianess he_in; +	struct mlx5_reg_host_endianess he_out; +	int err; + +	memset(&he_in, 0, sizeof(he_in)); +	he_in.he = MLX5_SET_HOST_ENDIANNESS; +	err = mlx5_core_access_reg(dev, &he_in,  sizeof(he_in), +					&he_out, sizeof(he_out), +					MLX5_REG_HOST_ENDIANNESS, 0, 1); +	return err; +} + +static int mlx5_core_enable_hca(struct mlx5_core_dev *dev) +{ +	int err; +	struct mlx5_enable_hca_mbox_in in; +	struct mlx5_enable_hca_mbox_out out; + +	memset(&in, 0, sizeof(in)); +	memset(&out, 0, sizeof(out)); +	in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_ENABLE_HCA); +	err = mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out)); +	if (err) +		return err; + +	if (out.hdr.status) +		return mlx5_cmd_status_to_err(&out.hdr); + +	return 0; +} + +static int mlx5_core_disable_hca(struct mlx5_core_dev *dev) +{ +	int err; +	struct mlx5_disable_hca_mbox_in in; +	struct mlx5_disable_hca_mbox_out out; + +	memset(&in, 0, sizeof(in)); +	memset(&out, 0, sizeof(out)); +	in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_DISABLE_HCA); +	err = mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out)); +	if (err) +		return err; + +	if (out.hdr.status) +		return mlx5_cmd_status_to_err(&out.hdr); + +	return 0; +} + +int mlx5_dev_init(struct mlx5_core_dev *dev, struct pci_dev *pdev) +{ +	struct mlx5_priv *priv = &dev->priv; +	int err; + +	dev->pdev = pdev; +	pci_set_drvdata(dev->pdev, dev); +	strncpy(priv->name, dev_name(&pdev->dev), MLX5_MAX_NAME_LEN); +	priv->name[MLX5_MAX_NAME_LEN - 1] = 0; + +	mutex_init(&priv->pgdir_mutex); +	INIT_LIST_HEAD(&priv->pgdir_list); +	spin_lock_init(&priv->mkey_lock); + +	priv->dbg_root = debugfs_create_dir(dev_name(&pdev->dev), mlx5_debugfs_root); +	if (!priv->dbg_root) +		return -ENOMEM; + +	err = pci_enable_device(pdev); +	if (err) { +		dev_err(&pdev->dev, "Cannot enable PCI device, aborting\n"); +		goto err_dbg; +	} + +	err = request_bar(pdev); +	if (err) { +		dev_err(&pdev->dev, "error requesting BARs, aborting\n"); +		goto err_disable; +	} + +	pci_set_master(pdev); + +	err = set_dma_caps(pdev); +	if (err) { +		dev_err(&pdev->dev, "Failed setting DMA capabilities mask, aborting\n"); +		goto err_clr_master; +	} + +	dev->iseg_base = pci_resource_start(dev->pdev, 0); +	dev->iseg = ioremap(dev->iseg_base, sizeof(*dev->iseg)); +	if (!dev->iseg) { +		err = -ENOMEM; +		dev_err(&pdev->dev, "Failed mapping initialization segment, aborting\n"); +		goto err_clr_master; +	} +	dev_info(&pdev->dev, "firmware version: %d.%d.%d\n", fw_rev_maj(dev), +		 fw_rev_min(dev), fw_rev_sub(dev)); + +	err = mlx5_cmd_init(dev); +	if (err) { +		dev_err(&pdev->dev, "Failed initializing command interface, aborting\n"); +		goto err_unmap; +	} + +	mlx5_pagealloc_init(dev); + +	err = mlx5_core_enable_hca(dev); +	if (err) { +		dev_err(&pdev->dev, "enable hca failed\n"); +		goto err_pagealloc_cleanup; +	} + +	err = mlx5_satisfy_startup_pages(dev, 1); +	if (err) { +		dev_err(&pdev->dev, "failed to allocate boot pages\n"); +		goto err_disable_hca; +	} + +	err = set_hca_ctrl(dev); +	if (err) { +		dev_err(&pdev->dev, "set_hca_ctrl failed\n"); +		goto reclaim_boot_pages; +	} + +	err = handle_hca_cap(dev); +	if (err) { +		dev_err(&pdev->dev, "handle_hca_cap failed\n"); +		goto reclaim_boot_pages; +	} + +	err = mlx5_satisfy_startup_pages(dev, 0); +	if (err) { +		dev_err(&pdev->dev, "failed to allocate init pages\n"); +		goto reclaim_boot_pages; +	} + +	err = mlx5_pagealloc_start(dev); +	if (err) { +		dev_err(&pdev->dev, "mlx5_pagealloc_start failed\n"); +		goto reclaim_boot_pages; +	} + +	err = mlx5_cmd_init_hca(dev); +	if (err) { +		dev_err(&pdev->dev, "init hca failed\n"); +		goto err_pagealloc_stop; +	} + +	mlx5_start_health_poll(dev); + +	err = mlx5_cmd_query_hca_cap(dev, &dev->caps); +	if (err) { +		dev_err(&pdev->dev, "query hca failed\n"); +		goto err_stop_poll; +	} + +	err = mlx5_cmd_query_adapter(dev); +	if (err) { +		dev_err(&pdev->dev, "query adapter failed\n"); +		goto err_stop_poll; +	} + +	err = mlx5_enable_msix(dev); +	if (err) { +		dev_err(&pdev->dev, "enable msix failed\n"); +		goto err_stop_poll; +	} + +	err = mlx5_eq_init(dev); +	if (err) { +		dev_err(&pdev->dev, "failed to initialize eq\n"); +		goto disable_msix; +	} + +	err = mlx5_alloc_uuars(dev, &priv->uuari); +	if (err) { +		dev_err(&pdev->dev, "Failed allocating uar, aborting\n"); +		goto err_eq_cleanup; +	} + +	err = mlx5_start_eqs(dev); +	if (err) { +		dev_err(&pdev->dev, "Failed to start pages and async EQs\n"); +		goto err_free_uar; +	} + +	MLX5_INIT_DOORBELL_LOCK(&priv->cq_uar_lock); + +	mlx5_init_cq_table(dev); +	mlx5_init_qp_table(dev); +	mlx5_init_srq_table(dev); +	mlx5_init_mr_table(dev); + +	return 0; + +err_free_uar: +	mlx5_free_uuars(dev, &priv->uuari); + +err_eq_cleanup: +	mlx5_eq_cleanup(dev); + +disable_msix: +	mlx5_disable_msix(dev); + +err_stop_poll: +	mlx5_stop_health_poll(dev); +	if (mlx5_cmd_teardown_hca(dev)) { +		dev_err(&dev->pdev->dev, "tear_down_hca failed, skip cleanup\n"); +		return err; +	} + +err_pagealloc_stop: +	mlx5_pagealloc_stop(dev); + +reclaim_boot_pages: +	mlx5_reclaim_startup_pages(dev); + +err_disable_hca: +	mlx5_core_disable_hca(dev); + +err_pagealloc_cleanup: +	mlx5_pagealloc_cleanup(dev); +	mlx5_cmd_cleanup(dev); + +err_unmap: +	iounmap(dev->iseg); + +err_clr_master: +	pci_clear_master(dev->pdev); +	release_bar(dev->pdev); + +err_disable: +	pci_disable_device(dev->pdev); + +err_dbg: +	debugfs_remove(priv->dbg_root); +	return err; +} +EXPORT_SYMBOL(mlx5_dev_init); + +void mlx5_dev_cleanup(struct mlx5_core_dev *dev) +{ +	struct mlx5_priv *priv = &dev->priv; + +	mlx5_cleanup_srq_table(dev); +	mlx5_cleanup_qp_table(dev); +	mlx5_cleanup_cq_table(dev); +	mlx5_stop_eqs(dev); +	mlx5_free_uuars(dev, &priv->uuari); +	mlx5_eq_cleanup(dev); +	mlx5_disable_msix(dev); +	mlx5_stop_health_poll(dev); +	if (mlx5_cmd_teardown_hca(dev)) { +		dev_err(&dev->pdev->dev, "tear_down_hca failed, skip cleanup\n"); +		return; +	} +	mlx5_pagealloc_stop(dev); +	mlx5_reclaim_startup_pages(dev); +	mlx5_core_disable_hca(dev); +	mlx5_pagealloc_cleanup(dev); +	mlx5_cmd_cleanup(dev); +	iounmap(dev->iseg); +	pci_clear_master(dev->pdev); +	release_bar(dev->pdev); +	pci_disable_device(dev->pdev); +	debugfs_remove(priv->dbg_root); +} +EXPORT_SYMBOL(mlx5_dev_cleanup); + +static int __init init(void) +{ +	int err; + +	mlx5_register_debugfs(); +	mlx5_core_wq = create_singlethread_workqueue("mlx5_core_wq"); +	if (!mlx5_core_wq) { +		err = -ENOMEM; +		goto err_debug; +	} +	mlx5_health_init(); + +	return 0; + +err_debug: +	mlx5_unregister_debugfs(); +	return err; +} + +static void __exit cleanup(void) +{ +	mlx5_health_cleanup(); +	destroy_workqueue(mlx5_core_wq); +	mlx5_unregister_debugfs(); +} + +module_init(init); +module_exit(cleanup); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mcg.c b/drivers/net/ethernet/mellanox/mlx5/core/mcg.c new file mode 100644 index 00000000000..44837640bd7 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/mcg.c @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2013, Mellanox Technologies 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. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mlx5/driver.h> +#include <linux/mlx5/cmd.h> +#include <rdma/ib_verbs.h> +#include "mlx5_core.h" + +struct mlx5_attach_mcg_mbox_in { +	struct mlx5_inbox_hdr	hdr; +	__be32			qpn; +	__be32			rsvd; +	u8			gid[16]; +}; + +struct mlx5_attach_mcg_mbox_out { +	struct mlx5_outbox_hdr	hdr; +	u8			rsvf[8]; +}; + +struct mlx5_detach_mcg_mbox_in { +	struct mlx5_inbox_hdr	hdr; +	__be32			qpn; +	__be32			rsvd; +	u8			gid[16]; +}; + +struct mlx5_detach_mcg_mbox_out { +	struct mlx5_outbox_hdr	hdr; +	u8			rsvf[8]; +}; + +int mlx5_core_attach_mcg(struct mlx5_core_dev *dev, union ib_gid *mgid, u32 qpn) +{ +	struct mlx5_attach_mcg_mbox_in in; +	struct mlx5_attach_mcg_mbox_out out; +	int err; + +	memset(&in, 0, sizeof(in)); +	memset(&out, 0, sizeof(out)); +	in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_ATTACH_TO_MCG); +	memcpy(in.gid, mgid, sizeof(*mgid)); +	in.qpn = cpu_to_be32(qpn); +	err = mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out)); +	if (err) +		return err; + +	if (out.hdr.status) +		err = mlx5_cmd_status_to_err(&out.hdr); + +	return err; +} +EXPORT_SYMBOL(mlx5_core_attach_mcg); + +int mlx5_core_detach_mcg(struct mlx5_core_dev *dev, union ib_gid *mgid, u32 qpn) +{ +	struct mlx5_detach_mcg_mbox_in in; +	struct mlx5_detach_mcg_mbox_out out; +	int err; + +	memset(&in, 0, sizeof(in)); +	memset(&out, 0, sizeof(out)); +	in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_DETACH_FROM_MCG); +	memcpy(in.gid, mgid, sizeof(*mgid)); +	in.qpn = cpu_to_be32(qpn); +	err = mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out)); +	if (err) +		return err; + +	if (out.hdr.status) +		err = mlx5_cmd_status_to_err(&out.hdr); + +	return err; +} +EXPORT_SYMBOL(mlx5_core_detach_mcg); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h new file mode 100644 index 00000000000..f0c9f9a7a36 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2013, Mellanox Technologies 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. + */ + +#ifndef __MLX5_CORE_H__ +#define __MLX5_CORE_H__ + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/sched.h> + +extern int mlx5_core_debug_mask; + +#define mlx5_core_dbg(dev, format, ...)					\ +	pr_debug("%s:%s:%d:(pid %d): " format,				\ +		 (dev)->priv.name, __func__, __LINE__, current->pid,	\ +		 ##__VA_ARGS__) + +#define mlx5_core_dbg_mask(dev, mask, format, ...)			\ +do {									\ +	if ((mask) & mlx5_core_debug_mask)				\ +		mlx5_core_dbg(dev, format, ##__VA_ARGS__);		\ +} while (0) + +#define mlx5_core_err(dev, format, ...)					\ +	pr_err("%s:%s:%d:(pid %d): " format,				\ +	       (dev)->priv.name, __func__, __LINE__, current->pid,	\ +	       ##__VA_ARGS__) + +#define mlx5_core_warn(dev, format, ...)				\ +	pr_warn("%s:%s:%d:(pid %d): " format,				\ +		(dev)->priv.name, __func__, __LINE__, current->pid,	\ +		##__VA_ARGS__) + +enum { +	MLX5_CMD_DATA, /* print command payload only */ +	MLX5_CMD_TIME, /* print command execution time */ +}; + + +int mlx5_cmd_query_hca_cap(struct mlx5_core_dev *dev, +			   struct mlx5_caps *caps); +int mlx5_cmd_query_adapter(struct mlx5_core_dev *dev); +int mlx5_cmd_init_hca(struct mlx5_core_dev *dev); +int mlx5_cmd_teardown_hca(struct mlx5_core_dev *dev); + +#endif /* __MLX5_CORE_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mr.c b/drivers/net/ethernet/mellanox/mlx5/core/mr.c new file mode 100644 index 00000000000..184c3615f47 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/mr.c @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2013, Mellanox Technologies 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. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mlx5/driver.h> +#include <linux/mlx5/cmd.h> +#include "mlx5_core.h" + +void mlx5_init_mr_table(struct mlx5_core_dev *dev) +{ +	struct mlx5_mr_table *table = &dev->priv.mr_table; + +	rwlock_init(&table->lock); +	INIT_RADIX_TREE(&table->tree, GFP_ATOMIC); +} + +void mlx5_cleanup_mr_table(struct mlx5_core_dev *dev) +{ +} + +int mlx5_core_create_mkey(struct mlx5_core_dev *dev, struct mlx5_core_mr *mr, +			  struct mlx5_create_mkey_mbox_in *in, int inlen, +			  mlx5_cmd_cbk_t callback, void *context, +			  struct mlx5_create_mkey_mbox_out *out) +{ +	struct mlx5_mr_table *table = &dev->priv.mr_table; +	struct mlx5_create_mkey_mbox_out lout; +	int err; +	u8 key; + +	memset(&lout, 0, sizeof(lout)); +	spin_lock_irq(&dev->priv.mkey_lock); +	key = dev->priv.mkey_key++; +	spin_unlock_irq(&dev->priv.mkey_lock); +	in->seg.qpn_mkey7_0 |= cpu_to_be32(key); +	in->hdr.opcode = cpu_to_be16(MLX5_CMD_OP_CREATE_MKEY); +	if (callback) { +		err = mlx5_cmd_exec_cb(dev, in, inlen, out, sizeof(*out), +				       callback, context); +		return err; +	} else { +		err = mlx5_cmd_exec(dev, in, inlen, &lout, sizeof(lout)); +	} + +	if (err) { +		mlx5_core_dbg(dev, "cmd exec failed %d\n", err); +		return err; +	} + +	if (lout.hdr.status) { +		mlx5_core_dbg(dev, "status %d\n", lout.hdr.status); +		return mlx5_cmd_status_to_err(&lout.hdr); +	} + +	mr->iova = be64_to_cpu(in->seg.start_addr); +	mr->size = be64_to_cpu(in->seg.len); +	mr->key = mlx5_idx_to_mkey(be32_to_cpu(lout.mkey) & 0xffffff) | key; +	mr->pd = be32_to_cpu(in->seg.flags_pd) & 0xffffff; + +	mlx5_core_dbg(dev, "out 0x%x, key 0x%x, mkey 0x%x\n", +		      be32_to_cpu(lout.mkey), key, mr->key); + +	/* connect to MR tree */ +	write_lock_irq(&table->lock); +	err = radix_tree_insert(&table->tree, mlx5_base_mkey(mr->key), mr); +	write_unlock_irq(&table->lock); +	if (err) { +		mlx5_core_warn(dev, "failed radix tree insert of mr 0x%x, %d\n", +			       mlx5_base_mkey(mr->key), err); +		mlx5_core_destroy_mkey(dev, mr); +	} + +	return err; +} +EXPORT_SYMBOL(mlx5_core_create_mkey); + +int mlx5_core_destroy_mkey(struct mlx5_core_dev *dev, struct mlx5_core_mr *mr) +{ +	struct mlx5_mr_table *table = &dev->priv.mr_table; +	struct mlx5_destroy_mkey_mbox_in in; +	struct mlx5_destroy_mkey_mbox_out out; +	struct mlx5_core_mr *deleted_mr; +	unsigned long flags; +	int err; + +	memset(&in, 0, sizeof(in)); +	memset(&out, 0, sizeof(out)); + +	write_lock_irqsave(&table->lock, flags); +	deleted_mr = radix_tree_delete(&table->tree, mlx5_base_mkey(mr->key)); +	write_unlock_irqrestore(&table->lock, flags); +	if (!deleted_mr) { +		mlx5_core_warn(dev, "failed radix tree delete of mr 0x%x\n", +			       mlx5_base_mkey(mr->key)); +		return -ENOENT; +	} + +	in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_DESTROY_MKEY); +	in.mkey = cpu_to_be32(mlx5_mkey_to_idx(mr->key)); +	err = mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out)); +	if (err) +		return err; + +	if (out.hdr.status) +		return mlx5_cmd_status_to_err(&out.hdr); + +	return err; +} +EXPORT_SYMBOL(mlx5_core_destroy_mkey); + +int mlx5_core_query_mkey(struct mlx5_core_dev *dev, struct mlx5_core_mr *mr, +			 struct mlx5_query_mkey_mbox_out *out, int outlen) +{ +	struct mlx5_destroy_mkey_mbox_in in; +	int err; + +	memset(&in, 0, sizeof(in)); +	memset(out, 0, outlen); + +	in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_QUERY_MKEY); +	in.mkey = cpu_to_be32(mlx5_mkey_to_idx(mr->key)); +	err = mlx5_cmd_exec(dev, &in, sizeof(in), out, outlen); +	if (err) +		return err; + +	if (out->hdr.status) +		return mlx5_cmd_status_to_err(&out->hdr); + +	return err; +} +EXPORT_SYMBOL(mlx5_core_query_mkey); + +int mlx5_core_dump_fill_mkey(struct mlx5_core_dev *dev, struct mlx5_core_mr *mr, +			     u32 *mkey) +{ +	struct mlx5_query_special_ctxs_mbox_in in; +	struct mlx5_query_special_ctxs_mbox_out out; +	int err; + +	memset(&in, 0, sizeof(in)); +	memset(&out, 0, sizeof(out)); + +	in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_QUERY_SPECIAL_CONTEXTS); +	err = mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out)); +	if (err) +		return err; + +	if (out.hdr.status) +		return mlx5_cmd_status_to_err(&out.hdr); + +	*mkey = be32_to_cpu(out.dump_fill_mkey); + +	return err; +} +EXPORT_SYMBOL(mlx5_core_dump_fill_mkey); + +int mlx5_core_create_psv(struct mlx5_core_dev *dev, u32 pdn, +			 int npsvs, u32 *sig_index) +{ +	struct mlx5_allocate_psv_in in; +	struct mlx5_allocate_psv_out out; +	int i, err; + +	if (npsvs > MLX5_MAX_PSVS) +		return -EINVAL; + +	memset(&in, 0, sizeof(in)); +	memset(&out, 0, sizeof(out)); + +	in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_CREATE_PSV); +	in.npsv_pd = cpu_to_be32((npsvs << 28) | pdn); +	err = mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out)); +	if (err) { +		mlx5_core_err(dev, "cmd exec failed %d\n", err); +		return err; +	} + +	if (out.hdr.status) { +		mlx5_core_err(dev, "create_psv bad status %d\n", +			      out.hdr.status); +		return mlx5_cmd_status_to_err(&out.hdr); +	} + +	for (i = 0; i < npsvs; i++) +		sig_index[i] = be32_to_cpu(out.psv_idx[i]) & 0xffffff; + +	return err; +} +EXPORT_SYMBOL(mlx5_core_create_psv); + +int mlx5_core_destroy_psv(struct mlx5_core_dev *dev, int psv_num) +{ +	struct mlx5_destroy_psv_in in; +	struct mlx5_destroy_psv_out out; +	int err; + +	memset(&in, 0, sizeof(in)); +	memset(&out, 0, sizeof(out)); + +	in.psv_number = cpu_to_be32(psv_num); +	in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_DESTROY_PSV); +	err = mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out)); +	if (err) { +		mlx5_core_err(dev, "destroy_psv cmd exec failed %d\n", err); +		goto out; +	} + +	if (out.hdr.status) { +		mlx5_core_err(dev, "destroy_psv bad status %d\n", +			      out.hdr.status); +		err = mlx5_cmd_status_to_err(&out.hdr); +		goto out; +	} + +out: +	return err; +} +EXPORT_SYMBOL(mlx5_core_destroy_psv); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c b/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c new file mode 100644 index 00000000000..c2a953ef0e6 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c @@ -0,0 +1,531 @@ +/* + * Copyright (c) 2013, Mellanox Technologies 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. + */ + +#include <asm-generic/kmap_types.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mlx5/driver.h> +#include <linux/mlx5/cmd.h> +#include "mlx5_core.h" + +enum { +	MLX5_PAGES_CANT_GIVE	= 0, +	MLX5_PAGES_GIVE		= 1, +	MLX5_PAGES_TAKE		= 2 +}; + +enum { +	MLX5_BOOT_PAGES		= 1, +	MLX5_INIT_PAGES		= 2, +	MLX5_POST_INIT_PAGES	= 3 +}; + +struct mlx5_pages_req { +	struct mlx5_core_dev *dev; +	u32	func_id; +	s32	npages; +	struct work_struct work; +}; + +struct fw_page { +	struct rb_node		rb_node; +	u64			addr; +	struct page	       *page; +	u16			func_id; +	unsigned long		bitmask; +	struct list_head	list; +	unsigned		free_count; +}; + +struct mlx5_query_pages_inbox { +	struct mlx5_inbox_hdr	hdr; +	u8			rsvd[8]; +}; + +struct mlx5_query_pages_outbox { +	struct mlx5_outbox_hdr	hdr; +	__be16			rsvd; +	__be16			func_id; +	__be32			num_pages; +}; + +struct mlx5_manage_pages_inbox { +	struct mlx5_inbox_hdr	hdr; +	__be16			rsvd; +	__be16			func_id; +	__be32			num_entries; +	__be64			pas[0]; +}; + +struct mlx5_manage_pages_outbox { +	struct mlx5_outbox_hdr	hdr; +	__be32			num_entries; +	u8			rsvd[4]; +	__be64			pas[0]; +}; + +enum { +	MAX_RECLAIM_TIME_MSECS	= 5000, +}; + +enum { +	MLX5_MAX_RECLAIM_TIME_MILI	= 5000, +	MLX5_NUM_4K_IN_PAGE		= PAGE_SIZE / MLX5_ADAPTER_PAGE_SIZE, +}; + +static int insert_page(struct mlx5_core_dev *dev, u64 addr, struct page *page, u16 func_id) +{ +	struct rb_root *root = &dev->priv.page_root; +	struct rb_node **new = &root->rb_node; +	struct rb_node *parent = NULL; +	struct fw_page *nfp; +	struct fw_page *tfp; +	int i; + +	while (*new) { +		parent = *new; +		tfp = rb_entry(parent, struct fw_page, rb_node); +		if (tfp->addr < addr) +			new = &parent->rb_left; +		else if (tfp->addr > addr) +			new = &parent->rb_right; +		else +			return -EEXIST; +	} + +	nfp = kzalloc(sizeof(*nfp), GFP_KERNEL); +	if (!nfp) +		return -ENOMEM; + +	nfp->addr = addr; +	nfp->page = page; +	nfp->func_id = func_id; +	nfp->free_count = MLX5_NUM_4K_IN_PAGE; +	for (i = 0; i < MLX5_NUM_4K_IN_PAGE; i++) +		set_bit(i, &nfp->bitmask); + +	rb_link_node(&nfp->rb_node, parent, new); +	rb_insert_color(&nfp->rb_node, root); +	list_add(&nfp->list, &dev->priv.free_list); + +	return 0; +} + +static struct fw_page *find_fw_page(struct mlx5_core_dev *dev, u64 addr) +{ +	struct rb_root *root = &dev->priv.page_root; +	struct rb_node *tmp = root->rb_node; +	struct fw_page *result = NULL; +	struct fw_page *tfp; + +	while (tmp) { +		tfp = rb_entry(tmp, struct fw_page, rb_node); +		if (tfp->addr < addr) { +			tmp = tmp->rb_left; +		} else if (tfp->addr > addr) { +			tmp = tmp->rb_right; +		} else { +			result = tfp; +			break; +		} +	} + +	return result; +} + +static int mlx5_cmd_query_pages(struct mlx5_core_dev *dev, u16 *func_id, +				s32 *npages, int boot) +{ +	struct mlx5_query_pages_inbox	in; +	struct mlx5_query_pages_outbox	out; +	int err; + +	memset(&in, 0, sizeof(in)); +	memset(&out, 0, sizeof(out)); +	in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_QUERY_PAGES); +	in.hdr.opmod = boot ? cpu_to_be16(MLX5_BOOT_PAGES) : cpu_to_be16(MLX5_INIT_PAGES); + +	err = mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out)); +	if (err) +		return err; + +	if (out.hdr.status) +		return mlx5_cmd_status_to_err(&out.hdr); + +	*npages = be32_to_cpu(out.num_pages); +	*func_id = be16_to_cpu(out.func_id); + +	return err; +} + +static int alloc_4k(struct mlx5_core_dev *dev, u64 *addr) +{ +	struct fw_page *fp; +	unsigned n; + +	if (list_empty(&dev->priv.free_list)) +		return -ENOMEM; + +	fp = list_entry(dev->priv.free_list.next, struct fw_page, list); +	n = find_first_bit(&fp->bitmask, 8 * sizeof(fp->bitmask)); +	if (n >= MLX5_NUM_4K_IN_PAGE) { +		mlx5_core_warn(dev, "alloc 4k bug\n"); +		return -ENOENT; +	} +	clear_bit(n, &fp->bitmask); +	fp->free_count--; +	if (!fp->free_count) +		list_del(&fp->list); + +	*addr = fp->addr + n * MLX5_ADAPTER_PAGE_SIZE; + +	return 0; +} + +static void free_4k(struct mlx5_core_dev *dev, u64 addr) +{ +	struct fw_page *fwp; +	int n; + +	fwp = find_fw_page(dev, addr & PAGE_MASK); +	if (!fwp) { +		mlx5_core_warn(dev, "page not found\n"); +		return; +	} + +	n = (addr & ~PAGE_MASK) >> MLX5_ADAPTER_PAGE_SHIFT; +	fwp->free_count++; +	set_bit(n, &fwp->bitmask); +	if (fwp->free_count == MLX5_NUM_4K_IN_PAGE) { +		rb_erase(&fwp->rb_node, &dev->priv.page_root); +		if (fwp->free_count != 1) +			list_del(&fwp->list); +		dma_unmap_page(&dev->pdev->dev, addr & PAGE_MASK, PAGE_SIZE, +			       DMA_BIDIRECTIONAL); +		__free_page(fwp->page); +		kfree(fwp); +	} else if (fwp->free_count == 1) { +		list_add(&fwp->list, &dev->priv.free_list); +	} +} + +static int alloc_system_page(struct mlx5_core_dev *dev, u16 func_id) +{ +	struct page *page; +	u64 addr; +	int err; + +	page = alloc_page(GFP_HIGHUSER); +	if (!page) { +		mlx5_core_warn(dev, "failed to allocate page\n"); +		return -ENOMEM; +	} +	addr = dma_map_page(&dev->pdev->dev, page, 0, +			    PAGE_SIZE, DMA_BIDIRECTIONAL); +	if (dma_mapping_error(&dev->pdev->dev, addr)) { +		mlx5_core_warn(dev, "failed dma mapping page\n"); +		err = -ENOMEM; +		goto out_alloc; +	} +	err = insert_page(dev, addr, page, func_id); +	if (err) { +		mlx5_core_err(dev, "failed to track allocated page\n"); +		goto out_mapping; +	} + +	return 0; + +out_mapping: +	dma_unmap_page(&dev->pdev->dev, addr, PAGE_SIZE, DMA_BIDIRECTIONAL); + +out_alloc: +	__free_page(page); + +	return err; +} +static int give_pages(struct mlx5_core_dev *dev, u16 func_id, int npages, +		      int notify_fail) +{ +	struct mlx5_manage_pages_inbox *in; +	struct mlx5_manage_pages_outbox out; +	struct mlx5_manage_pages_inbox *nin; +	int inlen; +	u64 addr; +	int err; +	int i; + +	inlen = sizeof(*in) + npages * sizeof(in->pas[0]); +	in = mlx5_vzalloc(inlen); +	if (!in) { +		mlx5_core_warn(dev, "vzalloc failed %d\n", inlen); +		return -ENOMEM; +	} +	memset(&out, 0, sizeof(out)); + +	for (i = 0; i < npages; i++) { +retry: +		err = alloc_4k(dev, &addr); +		if (err) { +			if (err == -ENOMEM) +				err = alloc_system_page(dev, func_id); +			if (err) +				goto out_4k; + +			goto retry; +		} +		in->pas[i] = cpu_to_be64(addr); +	} + +	in->hdr.opcode = cpu_to_be16(MLX5_CMD_OP_MANAGE_PAGES); +	in->hdr.opmod = cpu_to_be16(MLX5_PAGES_GIVE); +	in->func_id = cpu_to_be16(func_id); +	in->num_entries = cpu_to_be32(npages); +	err = mlx5_cmd_exec(dev, in, inlen, &out, sizeof(out)); +	if (err) { +		mlx5_core_warn(dev, "func_id 0x%x, npages %d, err %d\n", +			       func_id, npages, err); +		goto out_alloc; +	} +	dev->priv.fw_pages += npages; + +	if (out.hdr.status) { +		err = mlx5_cmd_status_to_err(&out.hdr); +		if (err) { +			mlx5_core_warn(dev, "func_id 0x%x, npages %d, status %d\n", +				       func_id, npages, out.hdr.status); +			goto out_alloc; +		} +	} + +	mlx5_core_dbg(dev, "err %d\n", err); + +	goto out_free; + +out_alloc: +	if (notify_fail) { +		nin = kzalloc(sizeof(*nin), GFP_KERNEL); +		if (!nin) { +			mlx5_core_warn(dev, "allocation failed\n"); +			goto out_4k; +		} +		memset(&out, 0, sizeof(out)); +		nin->hdr.opcode = cpu_to_be16(MLX5_CMD_OP_MANAGE_PAGES); +		nin->hdr.opmod = cpu_to_be16(MLX5_PAGES_CANT_GIVE); +		if (mlx5_cmd_exec(dev, nin, sizeof(*nin), &out, sizeof(out))) +			mlx5_core_warn(dev, "page notify failed\n"); +		kfree(nin); +	} + +out_4k: +	for (i--; i >= 0; i--) +		free_4k(dev, be64_to_cpu(in->pas[i])); +out_free: +	mlx5_vfree(in); +	return err; +} + +static int reclaim_pages(struct mlx5_core_dev *dev, u32 func_id, int npages, +			 int *nclaimed) +{ +	struct mlx5_manage_pages_inbox   in; +	struct mlx5_manage_pages_outbox *out; +	int num_claimed; +	int outlen; +	u64 addr; +	int err; +	int i; + +	if (nclaimed) +		*nclaimed = 0; + +	memset(&in, 0, sizeof(in)); +	outlen = sizeof(*out) + npages * sizeof(out->pas[0]); +	out = mlx5_vzalloc(outlen); +	if (!out) +		return -ENOMEM; + +	in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_MANAGE_PAGES); +	in.hdr.opmod = cpu_to_be16(MLX5_PAGES_TAKE); +	in.func_id = cpu_to_be16(func_id); +	in.num_entries = cpu_to_be32(npages); +	mlx5_core_dbg(dev, "npages %d, outlen %d\n", npages, outlen); +	err = mlx5_cmd_exec(dev, &in, sizeof(in), out, outlen); +	if (err) { +		mlx5_core_err(dev, "failed reclaiming pages\n"); +		goto out_free; +	} +	dev->priv.fw_pages -= npages; + +	if (out->hdr.status) { +		err = mlx5_cmd_status_to_err(&out->hdr); +		goto out_free; +	} + +	num_claimed = be32_to_cpu(out->num_entries); +	if (nclaimed) +		*nclaimed = num_claimed; + +	for (i = 0; i < num_claimed; i++) { +		addr = be64_to_cpu(out->pas[i]); +		free_4k(dev, addr); +	} + +out_free: +	mlx5_vfree(out); +	return err; +} + +static void pages_work_handler(struct work_struct *work) +{ +	struct mlx5_pages_req *req = container_of(work, struct mlx5_pages_req, work); +	struct mlx5_core_dev *dev = req->dev; +	int err = 0; + +	if (req->npages < 0) +		err = reclaim_pages(dev, req->func_id, -1 * req->npages, NULL); +	else if (req->npages > 0) +		err = give_pages(dev, req->func_id, req->npages, 1); + +	if (err) +		mlx5_core_warn(dev, "%s fail %d\n", +			       req->npages < 0 ? "reclaim" : "give", err); + +	kfree(req); +} + +void mlx5_core_req_pages_handler(struct mlx5_core_dev *dev, u16 func_id, +				 s32 npages) +{ +	struct mlx5_pages_req *req; + +	req = kzalloc(sizeof(*req), GFP_ATOMIC); +	if (!req) { +		mlx5_core_warn(dev, "failed to allocate pages request\n"); +		return; +	} + +	req->dev = dev; +	req->func_id = func_id; +	req->npages = npages; +	INIT_WORK(&req->work, pages_work_handler); +	queue_work(dev->priv.pg_wq, &req->work); +} + +int mlx5_satisfy_startup_pages(struct mlx5_core_dev *dev, int boot) +{ +	u16 uninitialized_var(func_id); +	s32 uninitialized_var(npages); +	int err; + +	err = mlx5_cmd_query_pages(dev, &func_id, &npages, boot); +	if (err) +		return err; + +	mlx5_core_dbg(dev, "requested %d %s pages for func_id 0x%x\n", +		      npages, boot ? "boot" : "init", func_id); + +	return give_pages(dev, func_id, npages, 0); +} + +enum { +	MLX5_BLKS_FOR_RECLAIM_PAGES = 12 +}; + +static int optimal_reclaimed_pages(void) +{ +	struct mlx5_cmd_prot_block *block; +	struct mlx5_cmd_layout *lay; +	int ret; + +	ret = (sizeof(lay->out) + MLX5_BLKS_FOR_RECLAIM_PAGES * sizeof(block->data) - +	       sizeof(struct mlx5_manage_pages_outbox)) / +	       FIELD_SIZEOF(struct mlx5_manage_pages_outbox, pas[0]); + +	return ret; +} + +int mlx5_reclaim_startup_pages(struct mlx5_core_dev *dev) +{ +	unsigned long end = jiffies + msecs_to_jiffies(MAX_RECLAIM_TIME_MSECS); +	struct fw_page *fwp; +	struct rb_node *p; +	int nclaimed = 0; +	int err; + +	do { +		p = rb_first(&dev->priv.page_root); +		if (p) { +			fwp = rb_entry(p, struct fw_page, rb_node); +			err = reclaim_pages(dev, fwp->func_id, +					    optimal_reclaimed_pages(), +					    &nclaimed); +			if (err) { +				mlx5_core_warn(dev, "failed reclaiming pages (%d)\n", +					       err); +				return err; +			} +			if (nclaimed) +				end = jiffies + msecs_to_jiffies(MAX_RECLAIM_TIME_MSECS); +		} +		if (time_after(jiffies, end)) { +			mlx5_core_warn(dev, "FW did not return all pages. giving up...\n"); +			break; +		} +	} while (p); + +	return 0; +} + +void mlx5_pagealloc_init(struct mlx5_core_dev *dev) +{ +	dev->priv.page_root = RB_ROOT; +	INIT_LIST_HEAD(&dev->priv.free_list); +} + +void mlx5_pagealloc_cleanup(struct mlx5_core_dev *dev) +{ +	/* nothing */ +} + +int mlx5_pagealloc_start(struct mlx5_core_dev *dev) +{ +	dev->priv.pg_wq = create_singlethread_workqueue("mlx5_page_allocator"); +	if (!dev->priv.pg_wq) +		return -ENOMEM; + +	return 0; +} + +void mlx5_pagealloc_stop(struct mlx5_core_dev *dev) +{ +	destroy_workqueue(dev->priv.pg_wq); +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/pd.c b/drivers/net/ethernet/mellanox/mlx5/core/pd.c new file mode 100644 index 00000000000..790da5c4ca4 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/pd.c @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2013, Mellanox Technologies 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. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mlx5/driver.h> +#include <linux/mlx5/cmd.h> +#include "mlx5_core.h" + +struct mlx5_alloc_pd_mbox_in { +	struct mlx5_inbox_hdr	hdr; +	u8			rsvd[8]; +}; + +struct mlx5_alloc_pd_mbox_out { +	struct mlx5_outbox_hdr	hdr; +	__be32			pdn; +	u8			rsvd[4]; +}; + +struct mlx5_dealloc_pd_mbox_in { +	struct mlx5_inbox_hdr	hdr; +	__be32			pdn; +	u8			rsvd[4]; +}; + +struct mlx5_dealloc_pd_mbox_out { +	struct mlx5_outbox_hdr	hdr; +	u8			rsvd[8]; +}; + +int mlx5_core_alloc_pd(struct mlx5_core_dev *dev, u32 *pdn) +{ +	struct mlx5_alloc_pd_mbox_in	in; +	struct mlx5_alloc_pd_mbox_out	out; +	int err; + +	memset(&in, 0, sizeof(in)); +	memset(&out, 0, sizeof(out)); +	in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_ALLOC_PD); +	err = mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out)); +	if (err) +		return err; + +	if (out.hdr.status) +		return mlx5_cmd_status_to_err(&out.hdr); + +	*pdn = be32_to_cpu(out.pdn) & 0xffffff; +	return err; +} +EXPORT_SYMBOL(mlx5_core_alloc_pd); + +int mlx5_core_dealloc_pd(struct mlx5_core_dev *dev, u32 pdn) +{ +	struct mlx5_dealloc_pd_mbox_in	in; +	struct mlx5_dealloc_pd_mbox_out	out; +	int err; + +	memset(&in, 0, sizeof(in)); +	memset(&out, 0, sizeof(out)); +	in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_DEALLOC_PD); +	in.pdn = cpu_to_be32(pdn); +	err = mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out)); +	if (err) +		return err; + +	if (out.hdr.status) +		return mlx5_cmd_status_to_err(&out.hdr); + +	return err; +} +EXPORT_SYMBOL(mlx5_core_dealloc_pd); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/port.c b/drivers/net/ethernet/mellanox/mlx5/core/port.c new file mode 100644 index 00000000000..8c9ac870ecb --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/port.c @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2013, Mellanox Technologies 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. + */ + +#include <linux/module.h> +#include <linux/mlx5/driver.h> +#include <linux/mlx5/cmd.h> +#include "mlx5_core.h" + +int mlx5_core_access_reg(struct mlx5_core_dev *dev, void *data_in, +			 int size_in, void *data_out, int size_out, +			 u16 reg_num, int arg, int write) +{ +	struct mlx5_access_reg_mbox_in *in = NULL; +	struct mlx5_access_reg_mbox_out *out = NULL; +	int err = -ENOMEM; + +	in = mlx5_vzalloc(sizeof(*in) + size_in); +	if (!in) +		return -ENOMEM; + +	out = mlx5_vzalloc(sizeof(*out) + size_out); +	if (!out) +		goto ex1; + +	memcpy(in->data, data_in, size_in); +	in->hdr.opcode = cpu_to_be16(MLX5_CMD_OP_ACCESS_REG); +	in->hdr.opmod = cpu_to_be16(!write); +	in->arg = cpu_to_be32(arg); +	in->register_id = cpu_to_be16(reg_num); +	err = mlx5_cmd_exec(dev, in, sizeof(*in) + size_in, out, +			    sizeof(*out) + size_out); +	if (err) +		goto ex2; + +	if (out->hdr.status) +		err = mlx5_cmd_status_to_err(&out->hdr); + +	if (!err) +		memcpy(data_out, out->data, size_out); + +ex2: +	mlx5_vfree(out); +ex1: +	mlx5_vfree(in); +	return err; +} +EXPORT_SYMBOL_GPL(mlx5_core_access_reg); + + +struct mlx5_reg_pcap { +	u8			rsvd0; +	u8			port_num; +	u8			rsvd1[2]; +	__be32			caps_127_96; +	__be32			caps_95_64; +	__be32			caps_63_32; +	__be32			caps_31_0; +}; + +int mlx5_set_port_caps(struct mlx5_core_dev *dev, int port_num, u32 caps) +{ +	struct mlx5_reg_pcap in; +	struct mlx5_reg_pcap out; +	int err; + +	memset(&in, 0, sizeof(in)); +	in.caps_127_96 = cpu_to_be32(caps); +	in.port_num = port_num; + +	err = mlx5_core_access_reg(dev, &in, sizeof(in), &out, +				   sizeof(out), MLX5_REG_PCAP, 0, 1); + +	return err; +} +EXPORT_SYMBOL_GPL(mlx5_set_port_caps); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/qp.c b/drivers/net/ethernet/mellanox/mlx5/core/qp.c new file mode 100644 index 00000000000..8145b466822 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/qp.c @@ -0,0 +1,302 @@ +/* + * Copyright (c) 2013, Mellanox Technologies 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. + */ + + +#include <linux/gfp.h> +#include <linux/export.h> +#include <linux/mlx5/cmd.h> +#include <linux/mlx5/qp.h> +#include <linux/mlx5/driver.h> + +#include "mlx5_core.h" + +void mlx5_qp_event(struct mlx5_core_dev *dev, u32 qpn, int event_type) +{ +	struct mlx5_qp_table *table = &dev->priv.qp_table; +	struct mlx5_core_qp *qp; + +	spin_lock(&table->lock); + +	qp = radix_tree_lookup(&table->tree, qpn); +	if (qp) +		atomic_inc(&qp->refcount); + +	spin_unlock(&table->lock); + +	if (!qp) { +		mlx5_core_warn(dev, "Async event for bogus QP 0x%x\n", qpn); +		return; +	} + +	qp->event(qp, event_type); + +	if (atomic_dec_and_test(&qp->refcount)) +		complete(&qp->free); +} + +int mlx5_core_create_qp(struct mlx5_core_dev *dev, +			struct mlx5_core_qp *qp, +			struct mlx5_create_qp_mbox_in *in, +			int inlen) +{ +	struct mlx5_qp_table *table = &dev->priv.qp_table; +	struct mlx5_create_qp_mbox_out out; +	struct mlx5_destroy_qp_mbox_in din; +	struct mlx5_destroy_qp_mbox_out dout; +	int err; + +	memset(&out, 0, sizeof(out)); +	in->hdr.opcode = cpu_to_be16(MLX5_CMD_OP_CREATE_QP); + +	err = mlx5_cmd_exec(dev, in, inlen, &out, sizeof(out)); +	if (err) { +		mlx5_core_warn(dev, "ret %d\n", err); +		return err; +	} + +	if (out.hdr.status) { +		mlx5_core_warn(dev, "current num of QPs 0x%x\n", +			       atomic_read(&dev->num_qps)); +		return mlx5_cmd_status_to_err(&out.hdr); +	} + +	qp->qpn = be32_to_cpu(out.qpn) & 0xffffff; +	mlx5_core_dbg(dev, "qpn = 0x%x\n", qp->qpn); + +	spin_lock_irq(&table->lock); +	err = radix_tree_insert(&table->tree, qp->qpn, qp); +	spin_unlock_irq(&table->lock); +	if (err) { +		mlx5_core_warn(dev, "err %d\n", err); +		goto err_cmd; +	} + +	err = mlx5_debug_qp_add(dev, qp); +	if (err) +		mlx5_core_dbg(dev, "failed adding QP 0x%x to debug file system\n", +			      qp->qpn); + +	qp->pid = current->pid; +	atomic_set(&qp->refcount, 1); +	atomic_inc(&dev->num_qps); +	init_completion(&qp->free); + +	return 0; + +err_cmd: +	memset(&din, 0, sizeof(din)); +	memset(&dout, 0, sizeof(dout)); +	din.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_DESTROY_QP); +	din.qpn = cpu_to_be32(qp->qpn); +	mlx5_cmd_exec(dev, &din, sizeof(din), &out, sizeof(dout)); + +	return err; +} +EXPORT_SYMBOL_GPL(mlx5_core_create_qp); + +int mlx5_core_destroy_qp(struct mlx5_core_dev *dev, +			 struct mlx5_core_qp *qp) +{ +	struct mlx5_destroy_qp_mbox_in in; +	struct mlx5_destroy_qp_mbox_out out; +	struct mlx5_qp_table *table = &dev->priv.qp_table; +	unsigned long flags; +	int err; + +	mlx5_debug_qp_remove(dev, qp); + +	spin_lock_irqsave(&table->lock, flags); +	radix_tree_delete(&table->tree, qp->qpn); +	spin_unlock_irqrestore(&table->lock, flags); + +	if (atomic_dec_and_test(&qp->refcount)) +		complete(&qp->free); +	wait_for_completion(&qp->free); + +	memset(&in, 0, sizeof(in)); +	memset(&out, 0, sizeof(out)); +	in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_DESTROY_QP); +	in.qpn = cpu_to_be32(qp->qpn); +	err = mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out)); +	if (err) +		return err; + +	if (out.hdr.status) +		return mlx5_cmd_status_to_err(&out.hdr); + +	atomic_dec(&dev->num_qps); +	return 0; +} +EXPORT_SYMBOL_GPL(mlx5_core_destroy_qp); + +int mlx5_core_qp_modify(struct mlx5_core_dev *dev, enum mlx5_qp_state cur_state, +			enum mlx5_qp_state new_state, +			struct mlx5_modify_qp_mbox_in *in, int sqd_event, +			struct mlx5_core_qp *qp) +{ +	static const u16 optab[MLX5_QP_NUM_STATE][MLX5_QP_NUM_STATE] = { +		[MLX5_QP_STATE_RST] = { +			[MLX5_QP_STATE_RST]	= MLX5_CMD_OP_2RST_QP, +			[MLX5_QP_STATE_ERR]	= MLX5_CMD_OP_2ERR_QP, +			[MLX5_QP_STATE_INIT]	= MLX5_CMD_OP_RST2INIT_QP, +		}, +		[MLX5_QP_STATE_INIT]  = { +			[MLX5_QP_STATE_RST]	= MLX5_CMD_OP_2RST_QP, +			[MLX5_QP_STATE_ERR]	= MLX5_CMD_OP_2ERR_QP, +			[MLX5_QP_STATE_INIT]	= MLX5_CMD_OP_INIT2INIT_QP, +			[MLX5_QP_STATE_RTR]	= MLX5_CMD_OP_INIT2RTR_QP, +		}, +		[MLX5_QP_STATE_RTR]   = { +			[MLX5_QP_STATE_RST]	= MLX5_CMD_OP_2RST_QP, +			[MLX5_QP_STATE_ERR]	= MLX5_CMD_OP_2ERR_QP, +			[MLX5_QP_STATE_RTS]	= MLX5_CMD_OP_RTR2RTS_QP, +		}, +		[MLX5_QP_STATE_RTS]   = { +			[MLX5_QP_STATE_RST]	= MLX5_CMD_OP_2RST_QP, +			[MLX5_QP_STATE_ERR]	= MLX5_CMD_OP_2ERR_QP, +			[MLX5_QP_STATE_RTS]	= MLX5_CMD_OP_RTS2RTS_QP, +			[MLX5_QP_STATE_SQD]	= MLX5_CMD_OP_RTS2SQD_QP, +		}, +		[MLX5_QP_STATE_SQD] = { +			[MLX5_QP_STATE_RST]	= MLX5_CMD_OP_2RST_QP, +			[MLX5_QP_STATE_ERR]	= MLX5_CMD_OP_2ERR_QP, +			[MLX5_QP_STATE_RTS]	= MLX5_CMD_OP_SQD2RTS_QP, +			[MLX5_QP_STATE_SQD]	= MLX5_CMD_OP_SQD2SQD_QP, +		}, +		[MLX5_QP_STATE_SQER] = { +			[MLX5_QP_STATE_RST]	= MLX5_CMD_OP_2RST_QP, +			[MLX5_QP_STATE_ERR]	= MLX5_CMD_OP_2ERR_QP, +			[MLX5_QP_STATE_RTS]	= MLX5_CMD_OP_SQERR2RTS_QP, +		}, +		[MLX5_QP_STATE_ERR] = { +			[MLX5_QP_STATE_RST]	= MLX5_CMD_OP_2RST_QP, +			[MLX5_QP_STATE_ERR]	= MLX5_CMD_OP_2ERR_QP, +		} +	}; + +	struct mlx5_modify_qp_mbox_out out; +	int err = 0; +	u16 op; + +	if (cur_state >= MLX5_QP_NUM_STATE || new_state >= MLX5_QP_NUM_STATE || +	    !optab[cur_state][new_state]) +		return -EINVAL; + +	memset(&out, 0, sizeof(out)); +	op = optab[cur_state][new_state]; +	in->hdr.opcode = cpu_to_be16(op); +	in->qpn = cpu_to_be32(qp->qpn); +	err = mlx5_cmd_exec(dev, in, sizeof(*in), &out, sizeof(out)); +	if (err) +		return err; + +	return mlx5_cmd_status_to_err(&out.hdr); +} +EXPORT_SYMBOL_GPL(mlx5_core_qp_modify); + +void mlx5_init_qp_table(struct mlx5_core_dev *dev) +{ +	struct mlx5_qp_table *table = &dev->priv.qp_table; + +	spin_lock_init(&table->lock); +	INIT_RADIX_TREE(&table->tree, GFP_ATOMIC); +	mlx5_qp_debugfs_init(dev); +} + +void mlx5_cleanup_qp_table(struct mlx5_core_dev *dev) +{ +	mlx5_qp_debugfs_cleanup(dev); +} + +int mlx5_core_qp_query(struct mlx5_core_dev *dev, struct mlx5_core_qp *qp, +		       struct mlx5_query_qp_mbox_out *out, int outlen) +{ +	struct mlx5_query_qp_mbox_in in; +	int err; + +	memset(&in, 0, sizeof(in)); +	memset(out, 0, outlen); +	in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_QUERY_QP); +	in.qpn = cpu_to_be32(qp->qpn); +	err = mlx5_cmd_exec(dev, &in, sizeof(in), out, outlen); +	if (err) +		return err; + +	if (out->hdr.status) +		return mlx5_cmd_status_to_err(&out->hdr); + +	return err; +} +EXPORT_SYMBOL_GPL(mlx5_core_qp_query); + +int mlx5_core_xrcd_alloc(struct mlx5_core_dev *dev, u32 *xrcdn) +{ +	struct mlx5_alloc_xrcd_mbox_in in; +	struct mlx5_alloc_xrcd_mbox_out out; +	int err; + +	memset(&in, 0, sizeof(in)); +	memset(&out, 0, sizeof(out)); +	in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_ALLOC_XRCD); +	err = mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out)); +	if (err) +		return err; + +	if (out.hdr.status) +		err = mlx5_cmd_status_to_err(&out.hdr); +	else +		*xrcdn = be32_to_cpu(out.xrcdn); + +	return err; +} +EXPORT_SYMBOL_GPL(mlx5_core_xrcd_alloc); + +int mlx5_core_xrcd_dealloc(struct mlx5_core_dev *dev, u32 xrcdn) +{ +	struct mlx5_dealloc_xrcd_mbox_in in; +	struct mlx5_dealloc_xrcd_mbox_out out; +	int err; + +	memset(&in, 0, sizeof(in)); +	memset(&out, 0, sizeof(out)); +	in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_DEALLOC_XRCD); +	in.xrcdn = cpu_to_be32(xrcdn); +	err = mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out)); +	if (err) +		return err; + +	if (out.hdr.status) +		err = mlx5_cmd_status_to_err(&out.hdr); + +	return err; +} +EXPORT_SYMBOL_GPL(mlx5_core_xrcd_dealloc); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/srq.c b/drivers/net/ethernet/mellanox/mlx5/core/srq.c new file mode 100644 index 00000000000..38bce93f831 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/srq.c @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2013, Mellanox Technologies 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. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mlx5/driver.h> +#include <linux/mlx5/cmd.h> +#include <linux/mlx5/srq.h> +#include <rdma/ib_verbs.h> +#include "mlx5_core.h" + +void mlx5_srq_event(struct mlx5_core_dev *dev, u32 srqn, int event_type) +{ +	struct mlx5_srq_table *table = &dev->priv.srq_table; +	struct mlx5_core_srq *srq; + +	spin_lock(&table->lock); + +	srq = radix_tree_lookup(&table->tree, srqn); +	if (srq) +		atomic_inc(&srq->refcount); + +	spin_unlock(&table->lock); + +	if (!srq) { +		mlx5_core_warn(dev, "Async event for bogus SRQ 0x%08x\n", srqn); +		return; +	} + +	srq->event(srq, event_type); + +	if (atomic_dec_and_test(&srq->refcount)) +		complete(&srq->free); +} + +struct mlx5_core_srq *mlx5_core_get_srq(struct mlx5_core_dev *dev, u32 srqn) +{ +	struct mlx5_srq_table *table = &dev->priv.srq_table; +	struct mlx5_core_srq *srq; + +	spin_lock(&table->lock); + +	srq = radix_tree_lookup(&table->tree, srqn); +	if (srq) +		atomic_inc(&srq->refcount); + +	spin_unlock(&table->lock); + +	return srq; +} +EXPORT_SYMBOL(mlx5_core_get_srq); + +int mlx5_core_create_srq(struct mlx5_core_dev *dev, struct mlx5_core_srq *srq, +			 struct mlx5_create_srq_mbox_in *in, int inlen) +{ +	struct mlx5_create_srq_mbox_out out; +	struct mlx5_srq_table *table = &dev->priv.srq_table; +	struct mlx5_destroy_srq_mbox_in din; +	struct mlx5_destroy_srq_mbox_out dout; +	int err; + +	memset(&out, 0, sizeof(out)); +	in->hdr.opcode = cpu_to_be16(MLX5_CMD_OP_CREATE_SRQ); +	err = mlx5_cmd_exec(dev, in, inlen, &out, sizeof(out)); +	if (err) +		return err; + +	if (out.hdr.status) +		return mlx5_cmd_status_to_err(&out.hdr); + +	srq->srqn = be32_to_cpu(out.srqn) & 0xffffff; + +	atomic_set(&srq->refcount, 1); +	init_completion(&srq->free); + +	spin_lock_irq(&table->lock); +	err = radix_tree_insert(&table->tree, srq->srqn, srq); +	spin_unlock_irq(&table->lock); +	if (err) { +		mlx5_core_warn(dev, "err %d, srqn 0x%x\n", err, srq->srqn); +		goto err_cmd; +	} + +	return 0; + +err_cmd: +	memset(&din, 0, sizeof(din)); +	memset(&dout, 0, sizeof(dout)); +	din.srqn = cpu_to_be32(srq->srqn); +	din.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_DESTROY_SRQ); +	mlx5_cmd_exec(dev, &din, sizeof(din), &dout, sizeof(dout)); +	return err; +} +EXPORT_SYMBOL(mlx5_core_create_srq); + +int mlx5_core_destroy_srq(struct mlx5_core_dev *dev, struct mlx5_core_srq *srq) +{ +	struct mlx5_destroy_srq_mbox_in in; +	struct mlx5_destroy_srq_mbox_out out; +	struct mlx5_srq_table *table = &dev->priv.srq_table; +	struct mlx5_core_srq *tmp; +	int err; + +	spin_lock_irq(&table->lock); +	tmp = radix_tree_delete(&table->tree, srq->srqn); +	spin_unlock_irq(&table->lock); +	if (!tmp) { +		mlx5_core_warn(dev, "srq 0x%x not found in tree\n", srq->srqn); +		return -EINVAL; +	} +	if (tmp != srq) { +		mlx5_core_warn(dev, "corruption on srqn 0x%x\n", srq->srqn); +		return -EINVAL; +	} + +	memset(&in, 0, sizeof(in)); +	memset(&out, 0, sizeof(out)); +	in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_DESTROY_SRQ); +	in.srqn = cpu_to_be32(srq->srqn); +	err = mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out)); +	if (err) +		return err; + +	if (out.hdr.status) +		return mlx5_cmd_status_to_err(&out.hdr); + +	if (atomic_dec_and_test(&srq->refcount)) +		complete(&srq->free); +	wait_for_completion(&srq->free); + +	return 0; +} +EXPORT_SYMBOL(mlx5_core_destroy_srq); + +int mlx5_core_query_srq(struct mlx5_core_dev *dev, struct mlx5_core_srq *srq, +			struct mlx5_query_srq_mbox_out *out) +{ +	struct mlx5_query_srq_mbox_in in; +	int err; + +	memset(&in, 0, sizeof(in)); +	memset(out, 0, sizeof(*out)); + +	in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_QUERY_SRQ); +	in.srqn = cpu_to_be32(srq->srqn); +	err = mlx5_cmd_exec(dev, &in, sizeof(in), out, sizeof(*out)); +	if (err) +		return err; + +	if (out->hdr.status) +		return mlx5_cmd_status_to_err(&out->hdr); + +	return err; +} +EXPORT_SYMBOL(mlx5_core_query_srq); + +int mlx5_core_arm_srq(struct mlx5_core_dev *dev, struct mlx5_core_srq *srq, +		      u16 lwm, int is_srq) +{ +	struct mlx5_arm_srq_mbox_in	in; +	struct mlx5_arm_srq_mbox_out	out; +	int err; + +	memset(&in, 0, sizeof(in)); +	memset(&out, 0, sizeof(out)); + +	in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_ARM_RQ); +	in.hdr.opmod = cpu_to_be16(!!is_srq); +	in.srqn = cpu_to_be32(srq->srqn); +	in.lwm = cpu_to_be16(lwm); + +	err = mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out)); +	if (err) +		return err; + +	if (out.hdr.status) +		return mlx5_cmd_status_to_err(&out.hdr); + +	return err; +} +EXPORT_SYMBOL(mlx5_core_arm_srq); + +void mlx5_init_srq_table(struct mlx5_core_dev *dev) +{ +	struct mlx5_srq_table *table = &dev->priv.srq_table; + +	spin_lock_init(&table->lock); +	INIT_RADIX_TREE(&table->tree, GFP_ATOMIC); +} + +void mlx5_cleanup_srq_table(struct mlx5_core_dev *dev) +{ +	/* nothing */ +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/uar.c b/drivers/net/ethernet/mellanox/mlx5/core/uar.c new file mode 100644 index 00000000000..68f5d9c77c7 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/uar.c @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2013, Mellanox Technologies 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. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mlx5/driver.h> +#include <linux/mlx5/cmd.h> +#include "mlx5_core.h" + +enum { +	NUM_DRIVER_UARS		= 4, +	NUM_LOW_LAT_UUARS	= 4, +}; + + +struct mlx5_alloc_uar_mbox_in { +	struct mlx5_inbox_hdr	hdr; +	u8			rsvd[8]; +}; + +struct mlx5_alloc_uar_mbox_out { +	struct mlx5_outbox_hdr	hdr; +	__be32			uarn; +	u8			rsvd[4]; +}; + +struct mlx5_free_uar_mbox_in { +	struct mlx5_inbox_hdr	hdr; +	__be32			uarn; +	u8			rsvd[4]; +}; + +struct mlx5_free_uar_mbox_out { +	struct mlx5_outbox_hdr	hdr; +	u8			rsvd[8]; +}; + +int mlx5_cmd_alloc_uar(struct mlx5_core_dev *dev, u32 *uarn) +{ +	struct mlx5_alloc_uar_mbox_in	in; +	struct mlx5_alloc_uar_mbox_out	out; +	int err; + +	memset(&in, 0, sizeof(in)); +	memset(&out, 0, sizeof(out)); +	in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_ALLOC_UAR); +	err = mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out)); +	if (err) +		goto ex; + +	if (out.hdr.status) { +		err = mlx5_cmd_status_to_err(&out.hdr); +		goto ex; +	} + +	*uarn = be32_to_cpu(out.uarn) & 0xffffff; + +ex: +	return err; +} +EXPORT_SYMBOL(mlx5_cmd_alloc_uar); + +int mlx5_cmd_free_uar(struct mlx5_core_dev *dev, u32 uarn) +{ +	struct mlx5_free_uar_mbox_in	in; +	struct mlx5_free_uar_mbox_out	out; +	int err; + +	memset(&in, 0, sizeof(in)); +	in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_DEALLOC_UAR); +	in.uarn = cpu_to_be32(uarn); +	err = mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out)); +	if (err) +		goto ex; + +	if (out.hdr.status) +		err = mlx5_cmd_status_to_err(&out.hdr); + +ex: +	return err; +} +EXPORT_SYMBOL(mlx5_cmd_free_uar); + +static int need_uuar_lock(int uuarn) +{ +	int tot_uuars = NUM_DRIVER_UARS * MLX5_BF_REGS_PER_PAGE; + +	if (uuarn == 0 || tot_uuars - NUM_LOW_LAT_UUARS) +		return 0; + +	return 1; +} + +int mlx5_alloc_uuars(struct mlx5_core_dev *dev, struct mlx5_uuar_info *uuari) +{ +	int tot_uuars = NUM_DRIVER_UARS * MLX5_BF_REGS_PER_PAGE; +	struct mlx5_bf *bf; +	phys_addr_t addr; +	int err; +	int i; + +	uuari->num_uars = NUM_DRIVER_UARS; +	uuari->num_low_latency_uuars = NUM_LOW_LAT_UUARS; + +	mutex_init(&uuari->lock); +	uuari->uars = kcalloc(uuari->num_uars, sizeof(*uuari->uars), GFP_KERNEL); +	if (!uuari->uars) +		return -ENOMEM; + +	uuari->bfs = kcalloc(tot_uuars, sizeof(*uuari->bfs), GFP_KERNEL); +	if (!uuari->bfs) { +		err = -ENOMEM; +		goto out_uars; +	} + +	uuari->bitmap = kcalloc(BITS_TO_LONGS(tot_uuars), sizeof(*uuari->bitmap), +				GFP_KERNEL); +	if (!uuari->bitmap) { +		err = -ENOMEM; +		goto out_bfs; +	} + +	uuari->count = kcalloc(tot_uuars, sizeof(*uuari->count), GFP_KERNEL); +	if (!uuari->count) { +		err = -ENOMEM; +		goto out_bitmap; +	} + +	for (i = 0; i < uuari->num_uars; i++) { +		err = mlx5_cmd_alloc_uar(dev, &uuari->uars[i].index); +		if (err) +			goto out_count; + +		addr = dev->iseg_base + ((phys_addr_t)(uuari->uars[i].index) << PAGE_SHIFT); +		uuari->uars[i].map = ioremap(addr, PAGE_SIZE); +		if (!uuari->uars[i].map) { +			mlx5_cmd_free_uar(dev, uuari->uars[i].index); +			err = -ENOMEM; +			goto out_count; +		} +		mlx5_core_dbg(dev, "allocated uar index 0x%x, mmaped at %p\n", +			      uuari->uars[i].index, uuari->uars[i].map); +	} + +	for (i = 0; i < tot_uuars; i++) { +		bf = &uuari->bfs[i]; + +		bf->buf_size = dev->caps.bf_reg_size / 2; +		bf->uar = &uuari->uars[i / MLX5_BF_REGS_PER_PAGE]; +		bf->regreg = uuari->uars[i / MLX5_BF_REGS_PER_PAGE].map; +		bf->reg = NULL; /* Add WC support */ +		bf->offset = (i % MLX5_BF_REGS_PER_PAGE) * dev->caps.bf_reg_size + +			MLX5_BF_OFFSET; +		bf->need_lock = need_uuar_lock(i); +		spin_lock_init(&bf->lock); +		spin_lock_init(&bf->lock32); +		bf->uuarn = i; +	} + +	return 0; + +out_count: +	for (i--; i >= 0; i--) { +		iounmap(uuari->uars[i].map); +		mlx5_cmd_free_uar(dev, uuari->uars[i].index); +	} +	kfree(uuari->count); + +out_bitmap: +	kfree(uuari->bitmap); + +out_bfs: +	kfree(uuari->bfs); + +out_uars: +	kfree(uuari->uars); +	return err; +} + +int mlx5_free_uuars(struct mlx5_core_dev *dev, struct mlx5_uuar_info *uuari) +{ +	int i = uuari->num_uars; + +	for (i--; i >= 0; i--) { +		iounmap(uuari->uars[i].map); +		mlx5_cmd_free_uar(dev, uuari->uars[i].index); +	} + +	kfree(uuari->count); +	kfree(uuari->bitmap); +	kfree(uuari->bfs); +	kfree(uuari->uars); + +	return 0; +}  | 
