diff options
Diffstat (limited to 'drivers/pci/iova.c')
-rw-r--r-- | drivers/pci/iova.c | 50 |
1 files changed, 36 insertions, 14 deletions
diff --git a/drivers/pci/iova.c b/drivers/pci/iova.c index dbcdd6bfa63a..3ef4ac064315 100644 --- a/drivers/pci/iova.c +++ b/drivers/pci/iova.c | |||
@@ -73,10 +73,11 @@ iova_get_pad_size(int size, unsigned int limit_pfn) | |||
73 | return pad_size; | 73 | return pad_size; |
74 | } | 74 | } |
75 | 75 | ||
76 | static int __alloc_iova_range(struct iova_domain *iovad, unsigned long size, | 76 | static int __alloc_and_insert_iova_range(struct iova_domain *iovad, |
77 | unsigned long limit_pfn, struct iova *new, bool size_aligned) | 77 | unsigned long size, unsigned long limit_pfn, |
78 | struct iova *new, bool size_aligned) | ||
78 | { | 79 | { |
79 | struct rb_node *curr = NULL; | 80 | struct rb_node *prev, *curr = NULL; |
80 | unsigned long flags; | 81 | unsigned long flags; |
81 | unsigned long saved_pfn; | 82 | unsigned long saved_pfn; |
82 | unsigned int pad_size = 0; | 83 | unsigned int pad_size = 0; |
@@ -85,8 +86,10 @@ static int __alloc_iova_range(struct iova_domain *iovad, unsigned long size, | |||
85 | spin_lock_irqsave(&iovad->iova_rbtree_lock, flags); | 86 | spin_lock_irqsave(&iovad->iova_rbtree_lock, flags); |
86 | saved_pfn = limit_pfn; | 87 | saved_pfn = limit_pfn; |
87 | curr = __get_cached_rbnode(iovad, &limit_pfn); | 88 | curr = __get_cached_rbnode(iovad, &limit_pfn); |
89 | prev = curr; | ||
88 | while (curr) { | 90 | while (curr) { |
89 | struct iova *curr_iova = container_of(curr, struct iova, node); | 91 | struct iova *curr_iova = container_of(curr, struct iova, node); |
92 | |||
90 | if (limit_pfn < curr_iova->pfn_lo) | 93 | if (limit_pfn < curr_iova->pfn_lo) |
91 | goto move_left; | 94 | goto move_left; |
92 | else if (limit_pfn < curr_iova->pfn_hi) | 95 | else if (limit_pfn < curr_iova->pfn_hi) |
@@ -100,6 +103,7 @@ static int __alloc_iova_range(struct iova_domain *iovad, unsigned long size, | |||
100 | adjust_limit_pfn: | 103 | adjust_limit_pfn: |
101 | limit_pfn = curr_iova->pfn_lo - 1; | 104 | limit_pfn = curr_iova->pfn_lo - 1; |
102 | move_left: | 105 | move_left: |
106 | prev = curr; | ||
103 | curr = rb_prev(curr); | 107 | curr = rb_prev(curr); |
104 | } | 108 | } |
105 | 109 | ||
@@ -116,7 +120,33 @@ move_left: | |||
116 | new->pfn_lo = limit_pfn - (size + pad_size) + 1; | 120 | new->pfn_lo = limit_pfn - (size + pad_size) + 1; |
117 | new->pfn_hi = new->pfn_lo + size - 1; | 121 | new->pfn_hi = new->pfn_lo + size - 1; |
118 | 122 | ||
123 | /* Insert the new_iova into domain rbtree by holding writer lock */ | ||
124 | /* Add new node and rebalance tree. */ | ||
125 | { | ||
126 | struct rb_node **entry = &((prev)), *parent = NULL; | ||
127 | /* Figure out where to put new node */ | ||
128 | while (*entry) { | ||
129 | struct iova *this = container_of(*entry, | ||
130 | struct iova, node); | ||
131 | parent = *entry; | ||
132 | |||
133 | if (new->pfn_lo < this->pfn_lo) | ||
134 | entry = &((*entry)->rb_left); | ||
135 | else if (new->pfn_lo > this->pfn_lo) | ||
136 | entry = &((*entry)->rb_right); | ||
137 | else | ||
138 | BUG(); /* this should not happen */ | ||
139 | } | ||
140 | |||
141 | /* Add new node and rebalance tree. */ | ||
142 | rb_link_node(&new->node, parent, entry); | ||
143 | rb_insert_color(&new->node, &iovad->rbroot); | ||
144 | } | ||
145 | __cached_rbnode_insert_update(iovad, saved_pfn, new); | ||
146 | |||
119 | spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags); | 147 | spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags); |
148 | |||
149 | |||
120 | return 0; | 150 | return 0; |
121 | } | 151 | } |
122 | 152 | ||
@@ -172,23 +202,15 @@ alloc_iova(struct iova_domain *iovad, unsigned long size, | |||
172 | size = __roundup_pow_of_two(size); | 202 | size = __roundup_pow_of_two(size); |
173 | 203 | ||
174 | spin_lock_irqsave(&iovad->iova_alloc_lock, flags); | 204 | spin_lock_irqsave(&iovad->iova_alloc_lock, flags); |
175 | ret = __alloc_iova_range(iovad, size, limit_pfn, new_iova, | 205 | ret = __alloc_and_insert_iova_range(iovad, size, limit_pfn, |
176 | size_aligned); | 206 | new_iova, size_aligned); |
177 | 207 | ||
208 | spin_unlock_irqrestore(&iovad->iova_alloc_lock, flags); | ||
178 | if (ret) { | 209 | if (ret) { |
179 | spin_unlock_irqrestore(&iovad->iova_alloc_lock, flags); | ||
180 | free_iova_mem(new_iova); | 210 | free_iova_mem(new_iova); |
181 | return NULL; | 211 | return NULL; |
182 | } | 212 | } |
183 | 213 | ||
184 | /* Insert the new_iova into domain rbtree by holding writer lock */ | ||
185 | spin_lock(&iovad->iova_rbtree_lock); | ||
186 | iova_insert_rbtree(&iovad->rbroot, new_iova); | ||
187 | __cached_rbnode_insert_update(iovad, limit_pfn, new_iova); | ||
188 | spin_unlock(&iovad->iova_rbtree_lock); | ||
189 | |||
190 | spin_unlock_irqrestore(&iovad->iova_alloc_lock, flags); | ||
191 | |||
192 | return new_iova; | 214 | return new_iova; |
193 | } | 215 | } |
194 | 216 | ||