📄 mthca_mr.c
字号:
/* * Copyright (c) 2004 Topspin Communications. All rights reserved. * Copyright (c) 2005 Mellanox Technologies. 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. * * $Id: mthca_mr.c 1349 2004-12-16 21:09:43Z roland $ */#include <linux/slab.h>#include <linux/init.h>#include <linux/errno.h>#include "mthca_dev.h"#include "mthca_cmd.h"#include "mthca_memfree.h"struct mthca_mtt { struct mthca_buddy *buddy; int order; u32 first_seg;};/* * Must be packed because mtt_seg is 64 bits but only aligned to 32 bits. */struct mthca_mpt_entry { __be32 flags; __be32 page_size; __be32 key; __be32 pd; __be64 start; __be64 length; __be32 lkey; __be32 window_count; __be32 window_count_limit; __be64 mtt_seg; __be32 mtt_sz; /* Arbel only */ u32 reserved[2];} __attribute__((packed));#define MTHCA_MPT_FLAG_SW_OWNS (0xfUL << 28)#define MTHCA_MPT_FLAG_MIO (1 << 17)#define MTHCA_MPT_FLAG_BIND_ENABLE (1 << 15)#define MTHCA_MPT_FLAG_PHYSICAL (1 << 9)#define MTHCA_MPT_FLAG_REGION (1 << 8)#define MTHCA_MTT_FLAG_PRESENT 1#define MTHCA_MPT_STATUS_SW 0xF0#define MTHCA_MPT_STATUS_HW 0x00/* * Buddy allocator for MTT segments (currently not very efficient * since it doesn't keep a free list and just searches linearly * through the bitmaps) */static u32 mthca_buddy_alloc(struct mthca_buddy *buddy, int order){ int o; int m; u32 seg; spin_lock(&buddy->lock); for (o = order; o <= buddy->max_order; ++o) { m = 1 << (buddy->max_order - o); seg = find_first_bit(buddy->bits[o], m); if (seg < m) goto found; } spin_unlock(&buddy->lock); return -1; found: clear_bit(seg, buddy->bits[o]); while (o > order) { --o; seg <<= 1; set_bit(seg ^ 1, buddy->bits[o]); } spin_unlock(&buddy->lock); seg <<= order; return seg;}static void mthca_buddy_free(struct mthca_buddy *buddy, u32 seg, int order){ seg >>= order; spin_lock(&buddy->lock); while (test_bit(seg ^ 1, buddy->bits[order])) { clear_bit(seg ^ 1, buddy->bits[order]); seg >>= 1; ++order; } set_bit(seg, buddy->bits[order]); spin_unlock(&buddy->lock);}static int __devinit mthca_buddy_init(struct mthca_buddy *buddy, int max_order){ int i, s; buddy->max_order = max_order; spin_lock_init(&buddy->lock); buddy->bits = kzalloc((buddy->max_order + 1) * sizeof (long *), GFP_KERNEL); if (!buddy->bits) goto err_out; for (i = 0; i <= buddy->max_order; ++i) { s = BITS_TO_LONGS(1 << (buddy->max_order - i)); buddy->bits[i] = kmalloc(s * sizeof (long), GFP_KERNEL); if (!buddy->bits[i]) goto err_out_free; bitmap_zero(buddy->bits[i], 1 << (buddy->max_order - i)); } set_bit(0, buddy->bits[buddy->max_order]); return 0;err_out_free: for (i = 0; i <= buddy->max_order; ++i) kfree(buddy->bits[i]); kfree(buddy->bits);err_out: return -ENOMEM;}static void __devexit mthca_buddy_cleanup(struct mthca_buddy *buddy){ int i; for (i = 0; i <= buddy->max_order; ++i) kfree(buddy->bits[i]); kfree(buddy->bits);}static u32 mthca_alloc_mtt_range(struct mthca_dev *dev, int order, struct mthca_buddy *buddy){ u32 seg = mthca_buddy_alloc(buddy, order); if (seg == -1) return -1; if (mthca_is_memfree(dev)) if (mthca_table_get_range(dev, dev->mr_table.mtt_table, seg, seg + (1 << order) - 1)) { mthca_buddy_free(buddy, seg, order); seg = -1; } return seg;}static struct mthca_mtt *__mthca_alloc_mtt(struct mthca_dev *dev, int size, struct mthca_buddy *buddy){ struct mthca_mtt *mtt; int i; if (size <= 0) return ERR_PTR(-EINVAL); mtt = kmalloc(sizeof *mtt, GFP_KERNEL); if (!mtt) return ERR_PTR(-ENOMEM); mtt->buddy = buddy; mtt->order = 0; for (i = MTHCA_MTT_SEG_SIZE / 8; i < size; i <<= 1) ++mtt->order; mtt->first_seg = mthca_alloc_mtt_range(dev, mtt->order, buddy); if (mtt->first_seg == -1) { kfree(mtt); return ERR_PTR(-ENOMEM); } return mtt;}struct mthca_mtt *mthca_alloc_mtt(struct mthca_dev *dev, int size){ return __mthca_alloc_mtt(dev, size, &dev->mr_table.mtt_buddy);}void mthca_free_mtt(struct mthca_dev *dev, struct mthca_mtt *mtt){ if (!mtt) return; mthca_buddy_free(mtt->buddy, mtt->first_seg, mtt->order); mthca_table_put_range(dev, dev->mr_table.mtt_table, mtt->first_seg, mtt->first_seg + (1 << mtt->order) - 1); kfree(mtt);}int mthca_write_mtt(struct mthca_dev *dev, struct mthca_mtt *mtt, int start_index, u64 *buffer_list, int list_len){ struct mthca_mailbox *mailbox; __be64 *mtt_entry; int err = 0; u8 status; int i; mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL); if (IS_ERR(mailbox)) return PTR_ERR(mailbox); mtt_entry = mailbox->buf; while (list_len > 0) { mtt_entry[0] = cpu_to_be64(dev->mr_table.mtt_base + mtt->first_seg * MTHCA_MTT_SEG_SIZE + start_index * 8); mtt_entry[1] = 0; for (i = 0; i < list_len && i < MTHCA_MAILBOX_SIZE / 8 - 2; ++i) mtt_entry[i + 2] = cpu_to_be64(buffer_list[i] | MTHCA_MTT_FLAG_PRESENT); /* * If we have an odd number of entries to write, add * one more dummy entry for firmware efficiency. */ if (i & 1) mtt_entry[i + 2] = 0; err = mthca_WRITE_MTT(dev, mailbox, (i + 1) & ~1, &status); if (err) { mthca_warn(dev, "WRITE_MTT failed (%d)\n", err); goto out; } if (status) { mthca_warn(dev, "WRITE_MTT returned status 0x%02x\n", status); err = -EINVAL; goto out; } list_len -= i; start_index += i; buffer_list += i; }out: mthca_free_mailbox(dev, mailbox); return err;}static inline u32 tavor_hw_index_to_key(u32 ind){ return ind;}static inline u32 tavor_key_to_hw_index(u32 key){ return key;}static inline u32 arbel_hw_index_to_key(u32 ind){ return (ind >> 24) | (ind << 8);}static inline u32 arbel_key_to_hw_index(u32 key){ return (key << 24) | (key >> 8);}static inline u32 hw_index_to_key(struct mthca_dev *dev, u32 ind){ if (mthca_is_memfree(dev)) return arbel_hw_index_to_key(ind); else return tavor_hw_index_to_key(ind);}static inline u32 key_to_hw_index(struct mthca_dev *dev, u32 key){ if (mthca_is_memfree(dev)) return arbel_key_to_hw_index(key); else return tavor_key_to_hw_index(key);}int mthca_mr_alloc(struct mthca_dev *dev, u32 pd, int buffer_size_shift, u64 iova, u64 total_size, u32 access, struct mthca_mr *mr){ struct mthca_mailbox *mailbox; struct mthca_mpt_entry *mpt_entry; u32 key; int i; int err; u8 status; might_sleep(); WARN_ON(buffer_size_shift >= 32); key = mthca_alloc(&dev->mr_table.mpt_alloc); if (key == -1) return -ENOMEM; mr->ibmr.rkey = mr->ibmr.lkey = hw_index_to_key(dev, key); if (mthca_is_memfree(dev)) { err = mthca_table_get(dev, dev->mr_table.mpt_table, key); if (err) goto err_out_mpt_free; } mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL); if (IS_ERR(mailbox)) { err = PTR_ERR(mailbox); goto err_out_table; } mpt_entry = mailbox->buf; mpt_entry->flags = cpu_to_be32(MTHCA_MPT_FLAG_SW_OWNS | MTHCA_MPT_FLAG_MIO | MTHCA_MPT_FLAG_REGION | access); if (!mr->mtt) mpt_entry->flags |= cpu_to_be32(MTHCA_MPT_FLAG_PHYSICAL); mpt_entry->page_size = cpu_to_be32(buffer_size_shift - 12); mpt_entry->key = cpu_to_be32(key); mpt_entry->pd = cpu_to_be32(pd); mpt_entry->start = cpu_to_be64(iova); mpt_entry->length = cpu_to_be64(total_size); memset(&mpt_entry->lkey, 0, sizeof *mpt_entry - offsetof(struct mthca_mpt_entry, lkey)); if (mr->mtt) mpt_entry->mtt_seg = cpu_to_be64(dev->mr_table.mtt_base + mr->mtt->first_seg * MTHCA_MTT_SEG_SIZE); if (0) { mthca_dbg(dev, "Dumping MPT entry %08x:\n", mr->ibmr.lkey); for (i = 0; i < sizeof (struct mthca_mpt_entry) / 4; ++i) { if (i % 4 == 0) printk("[%02x] ", i * 4); printk(" %08x", be32_to_cpu(((__be32 *) mpt_entry)[i])); if ((i + 1) % 4 == 0) printk("\n"); } } err = mthca_SW2HW_MPT(dev, mailbox, key & (dev->limits.num_mpts - 1), &status); if (err) { mthca_warn(dev, "SW2HW_MPT failed (%d)\n", err); goto err_out_mailbox; } else if (status) { mthca_warn(dev, "SW2HW_MPT returned status 0x%02x\n", status); err = -EINVAL; goto err_out_mailbox; } mthca_free_mailbox(dev, mailbox); return err;err_out_mailbox: mthca_free_mailbox(dev, mailbox);err_out_table: mthca_table_put(dev, dev->mr_table.mpt_table, key);err_out_mpt_free: mthca_free(&dev->mr_table.mpt_alloc, key); return err;}int mthca_mr_alloc_notrans(struct mthca_dev *dev, u32 pd, u32 access, struct mthca_mr *mr){ mr->mtt = NULL; return mthca_mr_alloc(dev, pd, 12, 0, ~0ULL, access, mr);}int mthca_mr_alloc_phys(struct mthca_dev *dev, u32 pd, u64 *buffer_list, int buffer_size_shift, int list_len, u64 iova, u64 total_size, u32 access, struct mthca_mr *mr){ int err;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -