1 /* $NetBSD: pv_map.c,v 1.1.1.2 2009/12/02 00:26:41 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 if (pvl->pv->status & MISSING_PV) 135 continue; 136 assert(pvl->pv->dev); 137 138 pvm = NULL; 139 140 dm_list_iterate_items(pvm2, pvms) 141 if (pvm2->pv->dev == pvl->pv->dev) { 142 pvm = pvm2; 143 break; 144 } 145 146 if (!pvm) { 147 if (!(pvm = dm_pool_zalloc(mem, sizeof(*pvm)))) 148 return_0; 149 150 pvm->pv = pvl->pv; 151 dm_list_init(&pvm->areas); 152 dm_list_add(pvms, &pvm->list); 153 } 154 155 if (!_create_all_areas_for_pv(mem, pvm, pvl->pe_ranges)) 156 return_0; 157 } 158 159 return 1; 160 } 161 162 /* 163 * Create list of PV areas available for this particular allocation 164 */ 165 struct dm_list *create_pv_maps(struct dm_pool *mem, struct volume_group *vg, 166 struct dm_list *allocatable_pvs) 167 { 168 struct dm_list *pvms; 169 170 if (!(pvms = dm_pool_zalloc(mem, sizeof(*pvms)))) { 171 log_error("create_pv_maps alloc failed"); 172 return NULL; 173 } 174 175 dm_list_init(pvms); 176 177 if (!_create_maps(mem, allocatable_pvs, pvms)) { 178 log_error("Couldn't create physical volume maps in %s", 179 vg->name); 180 dm_pool_free(mem, pvms); 181 return NULL; 182 } 183 184 return pvms; 185 } 186 187 void consume_pv_area(struct pv_area *pva, uint32_t to_go) 188 { 189 dm_list_del(&pva->list); 190 pva->map->pe_count -= pva->count; 191 192 assert(to_go <= pva->count); 193 194 if (to_go < pva->count) { 195 /* split the area */ 196 pva->start += to_go; 197 pva->count -= to_go; 198 _insert_area(&pva->map->areas, pva); 199 } 200 } 201 202 uint32_t pv_maps_size(struct dm_list *pvms) 203 { 204 struct pv_map *pvm; 205 uint32_t pe_count = 0; 206 207 dm_list_iterate_items(pvm, pvms) 208 pe_count += pvm->pe_count; 209 210 return pe_count; 211 } 212