/* * Copyright (C) 2011 * Boaz Harrosh * * This file is part of the objects raid engine (ore). * * It is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * You should have received a copy of the GNU General Public License * along with "ore". If not, write to the Free Software Foundation, Inc: * "Free Software Foundation " */ #include #include "ore_raid.h" struct page *_raid_page_alloc(void) { return alloc_page(GFP_KERNEL); } void _raid_page_free(struct page *p) { __free_page(p); } void _ore_add_sg_seg(struct ore_per_dev_state *per_dev, unsigned cur_len, bool not_last) { struct osd_sg_entry *sge; ORE_DBGMSG("dev=%d cur_len=0x%x not_last=%d cur_sg=%d " "offset=0x%llx length=0x%x last_sgs_total=0x%x\n", per_dev->dev, cur_len, not_last, per_dev->cur_sg, _LLU(per_dev->offset), per_dev->length, per_dev->last_sgs_total); if (!per_dev->cur_sg) { sge = per_dev->sglist; /* First time we prepare two entries */ if (per_dev->length) { ++per_dev->cur_sg; sge->offset = per_dev->offset; sge->len = per_dev->length; } else { /* Here the parity is the first unit of this object. * This happens every time we reach a parity device on * the same stripe as the per_dev->offset. We need to * just skip this unit. */ per_dev->offset += cur_len; return; } } else { /* finalize the last one */ sge = &per_dev->sglist[per_dev->cur_sg - 1]; sge->len = per_dev->length - per_dev->last_sgs_total; } if (not_last) { /* Partly prepare the next one */ struct osd_sg_entry *next_sge = sge + 1; ++per_dev->cur_sg; next_sge->offset = sge->offset + sge->len + cur_len; /* Save cur len so we know how mutch was added next time */ per_dev->last_sgs_total = per_dev->length; next_sge->len = 0; } else if (!sge->len) { /* Optimize for when the last unit is a parity */ --per_dev->cur_sg; } } /* In writes @cur_len means length left. .i.e cur_len==0 is the last parity U */ int _ore_add_parity_unit(struct ore_io_state *ios, struct ore_striping_info *si, struct ore_per_dev_state *per_dev, unsigned cur_len) { if (ios->reading) { BUG_ON(per_dev->cur_sg >= ios->sgs_per_dev); _ore_add_sg_seg(per_dev, cur_len, true); } else { struct page **pages = ios->parity_pages + ios->cur_par_page; unsigned num_pages = ios->layout->stripe_unit / PAGE_SIZE; unsigned array_start = 0; unsigned i; int ret; for (i = 0; i < num_pages; i++) { pages[i] = _raid_page_alloc(); if (unlikely(!pages[i])) return -ENOMEM; ++(ios->cur_par_page); /* TODO: only read support for now */ clear_highpage(pages[i]); } ORE_DBGMSG("writing dev=%d num_pages=%d cur_par_page=%d", per_dev->dev, num_pages, ios->cur_par_page); ret = _ore_add_stripe_unit(ios, &array_start, 0, pages, per_dev, num_pages * PAGE_SIZE); if (unlikely(ret)) return ret; } return 0; } int _ore_post_alloc_raid_stuff(struct ore_io_state *ios) { /*TODO: Only raid writes has stuff to add here */ return 0; } void _ore_free_raid_stuff(struct ore_io_state *ios) { if (ios->parity_pages) { /* writing and raid */ unsigned i; for (i = 0; i < ios->cur_par_page; i++) { struct page *page = ios->parity_pages[i]; if (page) _raid_page_free(page); } if (ios->extra_part_alloc) kfree(ios->parity_pages); } else { /* Will only be set if raid reading && sglist is big */ if (ios->extra_part_alloc) kfree(ios->per_dev[0].sglist); } }