⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 fmr_pool.c

📁 linux-2.6.15.6
💻 C
字号:
/* * Copyright (c) 2004 Topspin Communications.  All rights reserved. * Copyright (c) 2005 Sun Microsystems, 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. * * $Id: fmr_pool.c 2730 2005-06-28 16:43:03Z sean.hefty $ */#include <linux/errno.h>#include <linux/spinlock.h>#include <linux/slab.h>#include <linux/jhash.h>#include <linux/kthread.h>#include <rdma/ib_fmr_pool.h>#include "core_priv.h"enum {	IB_FMR_MAX_REMAPS = 32,	IB_FMR_HASH_BITS  = 8,	IB_FMR_HASH_SIZE  = 1 << IB_FMR_HASH_BITS,	IB_FMR_HASH_MASK  = IB_FMR_HASH_SIZE - 1};/* * If an FMR is not in use, then the list member will point to either * its pool's free_list (if the FMR can be mapped again; that is, * remap_count < IB_FMR_MAX_REMAPS) or its pool's dirty_list (if the * FMR needs to be unmapped before being remapped).  In either of * these cases it is a bug if the ref_count is not 0.  In other words, * if ref_count is > 0, then the list member must not be linked into * either free_list or dirty_list. * * The cache_node member is used to link the FMR into a cache bucket * (if caching is enabled).  This is independent of the reference * count of the FMR.  When a valid FMR is released, its ref_count is * decremented, and if ref_count reaches 0, the FMR is placed in * either free_list or dirty_list as appropriate.  However, it is not * removed from the cache and may be "revived" if a call to * ib_fmr_register_physical() occurs before the FMR is remapped.  In * this case we just increment the ref_count and remove the FMR from * free_list/dirty_list. * * Before we remap an FMR from free_list, we remove it from the cache * (to prevent another user from obtaining a stale FMR).  When an FMR * is released, we add it to the tail of the free list, so that our * cache eviction policy is "least recently used." * * All manipulation of ref_count, list and cache_node is protected by * pool_lock to maintain consistency. */struct ib_fmr_pool {	spinlock_t                pool_lock;	int                       pool_size;	int                       max_pages;	int                       dirty_watermark;	int                       dirty_len;	struct list_head          free_list;	struct list_head          dirty_list;	struct hlist_head        *cache_bucket;	void                     (*flush_function)(struct ib_fmr_pool *pool,						   void *              arg);	void                     *flush_arg;	struct task_struct       *thread;	atomic_t                  req_ser;	atomic_t                  flush_ser;	wait_queue_head_t         force_wait;};static inline u32 ib_fmr_hash(u64 first_page){	return jhash_2words((u32) first_page, (u32) (first_page >> 32), 0) &		(IB_FMR_HASH_SIZE - 1);}/* Caller must hold pool_lock */static inline struct ib_pool_fmr *ib_fmr_cache_lookup(struct ib_fmr_pool *pool,						      u64 *page_list,						      int  page_list_len,						      u64  io_virtual_address){	struct hlist_head *bucket;	struct ib_pool_fmr *fmr;	struct hlist_node *pos;	if (!pool->cache_bucket)		return NULL;	bucket = pool->cache_bucket + ib_fmr_hash(*page_list);	hlist_for_each_entry(fmr, pos, bucket, cache_node)		if (io_virtual_address == fmr->io_virtual_address &&		    page_list_len      == fmr->page_list_len      &&		    !memcmp(page_list, fmr->page_list,			    page_list_len * sizeof *page_list))			return fmr;	return NULL;}static void ib_fmr_batch_release(struct ib_fmr_pool *pool){	int                 ret;	struct ib_pool_fmr *fmr;	LIST_HEAD(unmap_list);	LIST_HEAD(fmr_list);	spin_lock_irq(&pool->pool_lock);	list_for_each_entry(fmr, &pool->dirty_list, list) {		hlist_del_init(&fmr->cache_node);		fmr->remap_count = 0;		list_add_tail(&fmr->fmr->list, &fmr_list);#ifdef DEBUG		if (fmr->ref_count !=0) {			printk(KERN_WARNING "Unmapping FMR 0x%08x with ref count %d",			       fmr, fmr->ref_count);		}#endif	}	list_splice(&pool->dirty_list, &unmap_list);	INIT_LIST_HEAD(&pool->dirty_list);	pool->dirty_len = 0;	spin_unlock_irq(&pool->pool_lock);	if (list_empty(&unmap_list)) {		return;	}	ret = ib_unmap_fmr(&fmr_list);	if (ret)		printk(KERN_WARNING "ib_unmap_fmr returned %d", ret);	spin_lock_irq(&pool->pool_lock);	list_splice(&unmap_list, &pool->free_list);	spin_unlock_irq(&pool->pool_lock);}static int ib_fmr_cleanup_thread(void *pool_ptr){	struct ib_fmr_pool *pool = pool_ptr;	do {		if (pool->dirty_len >= pool->dirty_watermark ||		    atomic_read(&pool->flush_ser) - atomic_read(&pool->req_ser) < 0) {			ib_fmr_batch_release(pool);			atomic_inc(&pool->flush_ser);			wake_up_interruptible(&pool->force_wait);			if (pool->flush_function)				pool->flush_function(pool, pool->flush_arg);		}		set_current_state(TASK_INTERRUPTIBLE);		if (pool->dirty_len < pool->dirty_watermark &&		    atomic_read(&pool->flush_ser) - atomic_read(&pool->req_ser) >= 0 &&		    !kthread_should_stop())			schedule();		__set_current_state(TASK_RUNNING);	} while (!kthread_should_stop());	return 0;}/** * ib_create_fmr_pool - Create an FMR pool * @pd:Protection domain for FMRs * @params:FMR pool parameters * * Create a pool of FMRs.  Return value is pointer to new pool or * error code if creation failed. */struct ib_fmr_pool *ib_create_fmr_pool(struct ib_pd             *pd,				       struct ib_fmr_pool_param *params){	struct ib_device   *device;	struct ib_fmr_pool *pool;	int i;	int ret;	if (!params)		return ERR_PTR(-EINVAL);	device = pd->device;	if (!device->alloc_fmr    || !device->dealloc_fmr  ||	    !device->map_phys_fmr || !device->unmap_fmr) {		printk(KERN_WARNING "Device %s does not support fast memory regions",		       device->name);		return ERR_PTR(-ENOSYS);	}	pool = kmalloc(sizeof *pool, GFP_KERNEL);	if (!pool) {		printk(KERN_WARNING "couldn't allocate pool struct");		return ERR_PTR(-ENOMEM);	}	pool->cache_bucket   = NULL;	pool->flush_function = params->flush_function;	pool->flush_arg      = params->flush_arg;	INIT_LIST_HEAD(&pool->free_list);	INIT_LIST_HEAD(&pool->dirty_list);	if (params->cache) {		pool->cache_bucket =			kmalloc(IB_FMR_HASH_SIZE * sizeof *pool->cache_bucket,				GFP_KERNEL);		if (!pool->cache_bucket) {			printk(KERN_WARNING "Failed to allocate cache in pool");			ret = -ENOMEM;			goto out_free_pool;		}		for (i = 0; i < IB_FMR_HASH_SIZE; ++i)			INIT_HLIST_HEAD(pool->cache_bucket + i);	}	pool->pool_size       = 0;	pool->max_pages       = params->max_pages_per_fmr;	pool->dirty_watermark = params->dirty_watermark;	pool->dirty_len       = 0;	spin_lock_init(&pool->pool_lock);	atomic_set(&pool->req_ser,   0);	atomic_set(&pool->flush_ser, 0);	init_waitqueue_head(&pool->force_wait);	pool->thread = kthread_create(ib_fmr_cleanup_thread,				      pool,				      "ib_fmr(%s)",				      device->name);	if (IS_ERR(pool->thread)) {		printk(KERN_WARNING "couldn't start cleanup thread");		ret = PTR_ERR(pool->thread);		goto out_free_pool;	}	{		struct ib_pool_fmr *fmr;		struct ib_fmr_attr attr = {			.max_pages = params->max_pages_per_fmr,			.max_maps  = IB_FMR_MAX_REMAPS,			.page_size = PAGE_SHIFT		};		for (i = 0; i < params->pool_size; ++i) {			fmr = kmalloc(sizeof *fmr + params->max_pages_per_fmr * sizeof (u64),				      GFP_KERNEL);			if (!fmr) {				printk(KERN_WARNING "failed to allocate fmr struct "				       "for FMR %d", i);				goto out_fail;			}			fmr->pool             = pool;			fmr->remap_count      = 0;			fmr->ref_count        = 0;			INIT_HLIST_NODE(&fmr->cache_node);			fmr->fmr = ib_alloc_fmr(pd, params->access, &attr);			if (IS_ERR(fmr->fmr)) {				printk(KERN_WARNING "fmr_create failed for FMR %d", i);				kfree(fmr);				goto out_fail;			}			list_add_tail(&fmr->list, &pool->free_list);			++pool->pool_size;		}	}	return pool; out_free_pool:	kfree(pool->cache_bucket);	kfree(pool);	return ERR_PTR(ret); out_fail:	ib_destroy_fmr_pool(pool);	return ERR_PTR(-ENOMEM);}EXPORT_SYMBOL(ib_create_fmr_pool);/** * ib_destroy_fmr_pool - Free FMR pool * @pool:FMR pool to free * * Destroy an FMR pool and free all associated resources. */void ib_destroy_fmr_pool(struct ib_fmr_pool *pool){	struct ib_pool_fmr *fmr;	struct ib_pool_fmr *tmp;	LIST_HEAD(fmr_list);	int                 i;	kthread_stop(pool->thread);	ib_fmr_batch_release(pool);	i = 0;	list_for_each_entry_safe(fmr, tmp, &pool->free_list, list) {		if (fmr->remap_count) {			INIT_LIST_HEAD(&fmr_list);			list_add_tail(&fmr->fmr->list, &fmr_list);			ib_unmap_fmr(&fmr_list);		}		ib_dealloc_fmr(fmr->fmr);		list_del(&fmr->list);		kfree(fmr);		++i;	}	if (i < pool->pool_size)		printk(KERN_WARNING "pool still has %d regions registered",		       pool->pool_size - i);	kfree(pool->cache_bucket);	kfree(pool);}EXPORT_SYMBOL(ib_destroy_fmr_pool);/** * ib_flush_fmr_pool - Invalidate all unmapped FMRs * @pool:FMR pool to flush * * Ensure that all unmapped FMRs are fully invalidated. */int ib_flush_fmr_pool(struct ib_fmr_pool *pool){	int serial;	atomic_inc(&pool->req_ser);	/*	 * It's OK if someone else bumps req_ser again here -- we'll	 * just wait a little longer.	 */	serial = atomic_read(&pool->req_ser);	wake_up_process(pool->thread);	if (wait_event_interruptible(pool->force_wait,				     atomic_read(&pool->flush_ser) -				     atomic_read(&pool->req_ser) >= 0))		return -EINTR;	return 0;}EXPORT_SYMBOL(ib_flush_fmr_pool);/** * ib_fmr_pool_map_phys - * @pool:FMR pool to allocate FMR from * @page_list:List of pages to map * @list_len:Number of pages in @page_list * @io_virtual_address:I/O virtual address for new FMR * * Map an FMR from an FMR pool. */struct ib_pool_fmr *ib_fmr_pool_map_phys(struct ib_fmr_pool *pool_handle,					 u64                *page_list,					 int                 list_len,					 u64                *io_virtual_address){	struct ib_fmr_pool *pool = pool_handle;	struct ib_pool_fmr *fmr;	unsigned long       flags;	int                 result;	if (list_len < 1 || list_len > pool->max_pages)		return ERR_PTR(-EINVAL);	spin_lock_irqsave(&pool->pool_lock, flags);	fmr = ib_fmr_cache_lookup(pool,				  page_list,				  list_len,				  *io_virtual_address);	if (fmr) {		/* found in cache */		++fmr->ref_count;		if (fmr->ref_count == 1) {			list_del(&fmr->list);		}		spin_unlock_irqrestore(&pool->pool_lock, flags);		return fmr;	}	if (list_empty(&pool->free_list)) {		spin_unlock_irqrestore(&pool->pool_lock, flags);		return ERR_PTR(-EAGAIN);	}	fmr = list_entry(pool->free_list.next, struct ib_pool_fmr, list);	list_del(&fmr->list);	hlist_del_init(&fmr->cache_node);	spin_unlock_irqrestore(&pool->pool_lock, flags);	result = ib_map_phys_fmr(fmr->fmr, page_list, list_len,				 *io_virtual_address);	if (result) {		spin_lock_irqsave(&pool->pool_lock, flags);		list_add(&fmr->list, &pool->free_list);		spin_unlock_irqrestore(&pool->pool_lock, flags);		printk(KERN_WARNING "fmr_map returns %d\n",		       result);		return ERR_PTR(result);	}	++fmr->remap_count;	fmr->ref_count = 1;	if (pool->cache_bucket) {		fmr->io_virtual_address = *io_virtual_address;		fmr->page_list_len      = list_len;		memcpy(fmr->page_list, page_list, list_len * sizeof(*page_list));		spin_lock_irqsave(&pool->pool_lock, flags);		hlist_add_head(&fmr->cache_node,			       pool->cache_bucket + ib_fmr_hash(fmr->page_list[0]));		spin_unlock_irqrestore(&pool->pool_lock, flags);	}	return fmr;}EXPORT_SYMBOL(ib_fmr_pool_map_phys);/** * ib_fmr_pool_unmap - Unmap FMR * @fmr:FMR to unmap * * Unmap an FMR.  The FMR mapping may remain valid until the FMR is * reused (or until ib_flush_fmr_pool() is called). */int ib_fmr_pool_unmap(struct ib_pool_fmr *fmr){	struct ib_fmr_pool *pool;	unsigned long flags;	pool = fmr->pool;	spin_lock_irqsave(&pool->pool_lock, flags);	--fmr->ref_count;	if (!fmr->ref_count) {		if (fmr->remap_count < IB_FMR_MAX_REMAPS) {			list_add_tail(&fmr->list, &pool->free_list);		} else {			list_add_tail(&fmr->list, &pool->dirty_list);			++pool->dirty_len;			wake_up_process(pool->thread);		}	}#ifdef DEBUG	if (fmr->ref_count < 0)		printk(KERN_WARNING "FMR %p has ref count %d < 0",		       fmr, fmr->ref_count);#endif	spin_unlock_irqrestore(&pool->pool_lock, flags);	return 0;}EXPORT_SYMBOL(ib_fmr_pool_unmap);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -