Path: blob/master/drivers/infiniband/hw/ehca/ipz_pt_fn.c
15112 views
/*1* IBM eServer eHCA Infiniband device driver for Linux on POWER2*3* internal queue handling4*5* Authors: Waleri Fomin <[email protected]>6* Reinhard Ernst <[email protected]>7* Christoph Raisch <[email protected]>8*9* Copyright (c) 2005 IBM Corporation10*11* This source code is distributed under a dual license of GPL v2.0 and OpenIB12* BSD.13*14* OpenIB BSD License15*16* Redistribution and use in source and binary forms, with or without17* modification, are permitted provided that the following conditions are met:18*19* Redistributions of source code must retain the above copyright notice, this20* list of conditions and the following disclaimer.21*22* Redistributions in binary form must reproduce the above copyright notice,23* this list of conditions and the following disclaimer in the documentation24* and/or other materials25* provided with the distribution.26*27* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"28* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE29* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE30* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE31* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR32* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF33* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR34* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER35* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)36* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE37* POSSIBILITY OF SUCH DAMAGE.38*/3940#include <linux/slab.h>4142#include "ehca_tools.h"43#include "ipz_pt_fn.h"44#include "ehca_classes.h"4546#define PAGES_PER_KPAGE (PAGE_SIZE >> EHCA_PAGESHIFT)4748struct kmem_cache *small_qp_cache;4950void *ipz_qpageit_get_inc(struct ipz_queue *queue)51{52void *ret = ipz_qeit_get(queue);53queue->current_q_offset += queue->pagesize;54if (queue->current_q_offset > queue->queue_length) {55queue->current_q_offset -= queue->pagesize;56ret = NULL;57}58if (((u64)ret) % queue->pagesize) {59ehca_gen_err("ERROR!! not at PAGE-Boundary");60return NULL;61}62return ret;63}6465void *ipz_qeit_eq_get_inc(struct ipz_queue *queue)66{67void *ret = ipz_qeit_get(queue);68u64 last_entry_in_q = queue->queue_length - queue->qe_size;6970queue->current_q_offset += queue->qe_size;71if (queue->current_q_offset > last_entry_in_q) {72queue->current_q_offset = 0;73queue->toggle_state = (~queue->toggle_state) & 1;74}7576return ret;77}7879int ipz_queue_abs_to_offset(struct ipz_queue *queue, u64 addr, u64 *q_offset)80{81int i;82for (i = 0; i < queue->queue_length / queue->pagesize; i++) {83u64 page = (u64)virt_to_abs(queue->queue_pages[i]);84if (addr >= page && addr < page + queue->pagesize) {85*q_offset = addr - page + i * queue->pagesize;86return 0;87}88}89return -EINVAL;90}9192#if PAGE_SHIFT < EHCA_PAGESHIFT93#error Kernel pages must be at least as large than eHCA pages (4K) !94#endif9596/*97* allocate pages for queue:98* outer loop allocates whole kernel pages (page aligned) and99* inner loop divides a kernel page into smaller hca queue pages100*/101static int alloc_queue_pages(struct ipz_queue *queue, const u32 nr_of_pages)102{103int k, f = 0;104u8 *kpage;105106while (f < nr_of_pages) {107kpage = (u8 *)get_zeroed_page(GFP_KERNEL);108if (!kpage)109goto out;110111for (k = 0; k < PAGES_PER_KPAGE && f < nr_of_pages; k++) {112queue->queue_pages[f] = (struct ipz_page *)kpage;113kpage += EHCA_PAGESIZE;114f++;115}116}117return 1;118119out:120for (f = 0; f < nr_of_pages && queue->queue_pages[f];121f += PAGES_PER_KPAGE)122free_page((unsigned long)(queue->queue_pages)[f]);123return 0;124}125126static int alloc_small_queue_page(struct ipz_queue *queue, struct ehca_pd *pd)127{128int order = ilog2(queue->pagesize) - 9;129struct ipz_small_queue_page *page;130unsigned long bit;131132mutex_lock(&pd->lock);133134if (!list_empty(&pd->free[order]))135page = list_entry(pd->free[order].next,136struct ipz_small_queue_page, list);137else {138page = kmem_cache_zalloc(small_qp_cache, GFP_KERNEL);139if (!page)140goto out;141142page->page = get_zeroed_page(GFP_KERNEL);143if (!page->page) {144kmem_cache_free(small_qp_cache, page);145goto out;146}147148list_add(&page->list, &pd->free[order]);149}150151bit = find_first_zero_bit(page->bitmap, IPZ_SPAGE_PER_KPAGE >> order);152__set_bit(bit, page->bitmap);153page->fill++;154155if (page->fill == IPZ_SPAGE_PER_KPAGE >> order)156list_move(&page->list, &pd->full[order]);157158mutex_unlock(&pd->lock);159160queue->queue_pages[0] = (void *)(page->page | (bit << (order + 9)));161queue->small_page = page;162queue->offset = bit << (order + 9);163return 1;164165out:166ehca_err(pd->ib_pd.device, "failed to allocate small queue page");167mutex_unlock(&pd->lock);168return 0;169}170171static void free_small_queue_page(struct ipz_queue *queue, struct ehca_pd *pd)172{173int order = ilog2(queue->pagesize) - 9;174struct ipz_small_queue_page *page = queue->small_page;175unsigned long bit;176int free_page = 0;177178bit = ((unsigned long)queue->queue_pages[0] & ~PAGE_MASK)179>> (order + 9);180181mutex_lock(&pd->lock);182183__clear_bit(bit, page->bitmap);184page->fill--;185186if (page->fill == 0) {187list_del(&page->list);188free_page = 1;189}190191if (page->fill == (IPZ_SPAGE_PER_KPAGE >> order) - 1)192/* the page was full until we freed the chunk */193list_move_tail(&page->list, &pd->free[order]);194195mutex_unlock(&pd->lock);196197if (free_page) {198free_page(page->page);199kmem_cache_free(small_qp_cache, page);200}201}202203int ipz_queue_ctor(struct ehca_pd *pd, struct ipz_queue *queue,204const u32 nr_of_pages, const u32 pagesize,205const u32 qe_size, const u32 nr_of_sg,206int is_small)207{208if (pagesize > PAGE_SIZE) {209ehca_gen_err("FATAL ERROR: pagesize=%x "210"is greater than kernel page size", pagesize);211return 0;212}213214/* init queue fields */215queue->queue_length = nr_of_pages * pagesize;216queue->pagesize = pagesize;217queue->qe_size = qe_size;218queue->act_nr_of_sg = nr_of_sg;219queue->current_q_offset = 0;220queue->toggle_state = 1;221queue->small_page = NULL;222223/* allocate queue page pointers */224queue->queue_pages = kzalloc(nr_of_pages * sizeof(void *), GFP_KERNEL);225if (!queue->queue_pages) {226queue->queue_pages = vzalloc(nr_of_pages * sizeof(void *));227if (!queue->queue_pages) {228ehca_gen_err("Couldn't allocate queue page list");229return 0;230}231}232233/* allocate actual queue pages */234if (is_small) {235if (!alloc_small_queue_page(queue, pd))236goto ipz_queue_ctor_exit0;237} else238if (!alloc_queue_pages(queue, nr_of_pages))239goto ipz_queue_ctor_exit0;240241return 1;242243ipz_queue_ctor_exit0:244ehca_gen_err("Couldn't alloc pages queue=%p "245"nr_of_pages=%x", queue, nr_of_pages);246if (is_vmalloc_addr(queue->queue_pages))247vfree(queue->queue_pages);248else249kfree(queue->queue_pages);250251return 0;252}253254int ipz_queue_dtor(struct ehca_pd *pd, struct ipz_queue *queue)255{256int i, nr_pages;257258if (!queue || !queue->queue_pages) {259ehca_gen_dbg("queue or queue_pages is NULL");260return 0;261}262263if (queue->small_page)264free_small_queue_page(queue, pd);265else {266nr_pages = queue->queue_length / queue->pagesize;267for (i = 0; i < nr_pages; i += PAGES_PER_KPAGE)268free_page((unsigned long)queue->queue_pages[i]);269}270271if (is_vmalloc_addr(queue->queue_pages))272vfree(queue->queue_pages);273else274kfree(queue->queue_pages);275276return 1;277}278279int ehca_init_small_qp_cache(void)280{281small_qp_cache = kmem_cache_create("ehca_cache_small_qp",282sizeof(struct ipz_small_queue_page),2830, SLAB_HWCACHE_ALIGN, NULL);284if (!small_qp_cache)285return -ENOMEM;286287return 0;288}289290void ehca_cleanup_small_qp_cache(void)291{292kmem_cache_destroy(small_qp_cache);293}294295296