1 /* $NetBSD: pv_map.c,v 1.1.1.1 2008/12/22 00:18:09 haad Exp $ */ 2 3 /* 4 * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. 5 * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved. 6 * 7 * This file is part of LVM2. 8 * 9 * This copyrighted material is made available to anyone wishing to use, 10 * modify, copy, or redistribute it subject to the terms and conditions 11 * of the GNU Lesser General Public License v.2.1. 12 * 13 * You should have received a copy of the GNU Lesser General Public License 14 * along with this program; if not, write to the Free Software Foundation, 15 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 */ 17 18 #include "lib.h" 19 #include "pv_map.h" 20 #include "pv_alloc.h" 21 22 #include <assert.h> 23 24 /* 25 * Areas are maintained in size order, largest first. 26 * 27 * FIXME Cope with overlap. 28 */ 29 static void _insert_area(struct dm_list *head, struct pv_area *a) 30 { 31 struct pv_area *pva; 32 33 dm_list_iterate_items(pva, head) { 34 if (a->count > pva->count) 35 break; 36 } 37 38 dm_list_add(&pva->list, &a->list); 39 a->map->pe_count += a->count; 40 } 41 42 static int _create_single_area(struct dm_pool *mem, struct pv_map *pvm, 43 uint32_t start, uint32_t length) 44 { 45 struct pv_area *pva; 46 47 if (!(pva = dm_pool_zalloc(mem, sizeof(*pva)))) 48 return_0; 49 50 log_debug("Allowing allocation on %s start PE %" PRIu32 " length %" 51 PRIu32, pv_dev_name(pvm->pv), start, length); 52 pva->map = pvm; 53 pva->start = start; 54 pva->count = length; 55 _insert_area(&pvm->areas, pva); 56 57 return 1; 58 } 59 60 static int _create_alloc_areas_for_pv(struct dm_pool *mem, struct pv_map *pvm, 61 uint32_t start, uint32_t count) 62 { 63 struct pv_segment *peg; 64 uint32_t pe, end, area_len; 65 66 /* Only select extents from start to end inclusive */ 67 end = start + count - 1; 68 if (end > pvm->pv->pe_count - 1) 69 end = pvm->pv->pe_count - 1; 70 71 pe = start; 72 73 /* Walk through complete ordered list of device segments */ 74 dm_list_iterate_items(peg, &pvm->pv->segments) { 75 /* pe holds the next extent we want to check */ 76 77 /* Beyond the range we're interested in? */ 78 if (pe > end) 79 break; 80 81 /* Skip if we haven't reached the first seg we want yet */ 82 if (pe > peg->pe + peg->len - 1) 83 continue; 84 85 /* Free? */ 86 if (peg->lvseg) 87 goto next; 88 89 /* How much of this peg do we need? */ 90 area_len = (end >= peg->pe + peg->len - 1) ? 91 peg->len - (pe - peg->pe) : end - pe + 1; 92 93 if (!_create_single_area(mem, pvm, pe, area_len)) 94 return_0; 95 96 next: 97 pe = peg->pe + peg->len; 98 } 99 100 return 1; 101 } 102 103 static int _create_all_areas_for_pv(struct dm_pool *mem, struct pv_map *pvm, 104 struct dm_list *pe_ranges) 105 { 106 struct pe_range *aa; 107 108 if (!pe_ranges) { 109 /* Use whole PV */ 110 if (!_create_alloc_areas_for_pv(mem, pvm, UINT32_C(0), 111 pvm->pv->pe_count)) 112 return_0; 113 114 return 1; 115 } 116 117 dm_list_iterate_items(aa, pe_ranges) { 118 if (!_create_alloc_areas_for_pv(mem, pvm, aa->start, 119 aa->count)) 120 return_0; 121 } 122 123 return 1; 124 } 125 126 static int _create_maps(struct dm_pool *mem, struct dm_list *pvs, struct dm_list *pvms) 127 { 128 struct pv_map *pvm, *pvm2; 129 struct pv_list *pvl; 130 131 dm_list_iterate_items(pvl, pvs) { 132 if (!(pvl->pv->status & ALLOCATABLE_PV)) 133 continue; 134 135 pvm = NULL; 136 137 dm_list_iterate_items(pvm2, pvms) 138 if (pvm2->pv->dev == pvl->pv->dev) { 139 pvm = pvm2; 140 break; 141 } 142 143 if (!pvm) { 144 if (!(pvm = dm_pool_zalloc(mem, sizeof(*pvm)))) 145 return_0; 146 147 pvm->pv = pvl->pv; 148 dm_list_init(&pvm->areas); 149 dm_list_add(pvms, &pvm->list); 150 } 151 152 if (!_create_all_areas_for_pv(mem, pvm, pvl->pe_ranges)) 153 return_0; 154 } 155 156 return 1; 157 } 158 159 /* 160 * Create list of PV areas available for this particular allocation 161 */ 162 struct dm_list *create_pv_maps(struct dm_pool *mem, struct volume_group *vg, 163 struct dm_list *allocatable_pvs) 164 { 165 struct dm_list *pvms; 166 167 if (!(pvms = dm_pool_zalloc(mem, sizeof(*pvms)))) { 168 log_error("create_pv_maps alloc failed"); 169 return NULL; 170 } 171 172 dm_list_init(pvms); 173 174 if (!_create_maps(mem, allocatable_pvs, pvms)) { 175 log_error("Couldn't create physical volume maps in %s", 176 vg->name); 177 dm_pool_free(mem, pvms); 178 return NULL; 179 } 180 181 return pvms; 182 } 183 184 void consume_pv_area(struct pv_area *pva, uint32_t to_go) 185 { 186 dm_list_del(&pva->list); 187 pva->map->pe_count -= pva->count; 188 189 assert(to_go <= pva->count); 190 191 if (to_go < pva->count) { 192 /* split the area */ 193 pva->start += to_go; 194 pva->count -= to_go; 195 _insert_area(&pva->map->areas, pva); 196 } 197 } 198 199 uint32_t pv_maps_size(struct dm_list *pvms) 200 { 201 struct pv_map *pvm; 202 uint32_t pe_count = 0; 203 204 dm_list_iterate_items(pvm, pvms) 205 pe_count += pvm->pe_count; 206 207 return pe_count; 208 } 209