1 /*
2  * VMware VMCI Driver
3  *
4  * Copyright (C) 2012 VMware, Inc. All rights reserved.
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the
8  * Free Software Foundation version 2 and no later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
13  * for more details.
14  */
15 
16 #include <linux/slab.h>
17 #include "vmci_handle_array.h"
18 
handle_arr_calc_size(u32 capacity)19 static size_t handle_arr_calc_size(u32 capacity)
20 {
21 	return VMCI_HANDLE_ARRAY_HEADER_SIZE +
22 	    capacity * sizeof(struct vmci_handle);
23 }
24 
vmci_handle_arr_create(u32 capacity,u32 max_capacity)25 struct vmci_handle_arr *vmci_handle_arr_create(u32 capacity, u32 max_capacity)
26 {
27 	struct vmci_handle_arr *array;
28 
29 	if (max_capacity == 0 || capacity > max_capacity)
30 		return NULL;
31 
32 	if (capacity == 0)
33 		capacity = min((u32)VMCI_HANDLE_ARRAY_DEFAULT_CAPACITY,
34 			       max_capacity);
35 
36 	array = kmalloc(handle_arr_calc_size(capacity), GFP_ATOMIC);
37 	if (!array)
38 		return NULL;
39 
40 	array->capacity = capacity;
41 	array->max_capacity = max_capacity;
42 	array->size = 0;
43 
44 	return array;
45 }
46 
vmci_handle_arr_destroy(struct vmci_handle_arr * array)47 void vmci_handle_arr_destroy(struct vmci_handle_arr *array)
48 {
49 	kfree(array);
50 }
51 
vmci_handle_arr_append_entry(struct vmci_handle_arr ** array_ptr,struct vmci_handle handle)52 int vmci_handle_arr_append_entry(struct vmci_handle_arr **array_ptr,
53 				 struct vmci_handle handle)
54 {
55 	struct vmci_handle_arr *array = *array_ptr;
56 
57 	if (unlikely(array->size >= array->capacity)) {
58 		/* reallocate. */
59 		struct vmci_handle_arr *new_array;
60 		u32 capacity_bump = min(array->max_capacity - array->capacity,
61 					array->capacity);
62 		size_t new_size = handle_arr_calc_size(array->capacity +
63 						       capacity_bump);
64 
65 		if (array->size >= array->max_capacity)
66 			return VMCI_ERROR_NO_MEM;
67 
68 		new_array = krealloc(array, new_size, GFP_ATOMIC);
69 		if (!new_array)
70 			return VMCI_ERROR_NO_MEM;
71 
72 		new_array->capacity += capacity_bump;
73 		*array_ptr = array = new_array;
74 	}
75 
76 	array->entries[array->size] = handle;
77 	array->size++;
78 
79 	return VMCI_SUCCESS;
80 }
81 
82 /*
83  * Handle that was removed, VMCI_INVALID_HANDLE if entry not found.
84  */
vmci_handle_arr_remove_entry(struct vmci_handle_arr * array,struct vmci_handle entry_handle)85 struct vmci_handle vmci_handle_arr_remove_entry(struct vmci_handle_arr *array,
86 						struct vmci_handle entry_handle)
87 {
88 	struct vmci_handle handle = VMCI_INVALID_HANDLE;
89 	u32 i;
90 
91 	for (i = 0; i < array->size; i++) {
92 		if (vmci_handle_is_equal(array->entries[i], entry_handle)) {
93 			handle = array->entries[i];
94 			array->size--;
95 			array->entries[i] = array->entries[array->size];
96 			array->entries[array->size] = VMCI_INVALID_HANDLE;
97 			break;
98 		}
99 	}
100 
101 	return handle;
102 }
103 
104 /*
105  * Handle that was removed, VMCI_INVALID_HANDLE if array was empty.
106  */
vmci_handle_arr_remove_tail(struct vmci_handle_arr * array)107 struct vmci_handle vmci_handle_arr_remove_tail(struct vmci_handle_arr *array)
108 {
109 	struct vmci_handle handle = VMCI_INVALID_HANDLE;
110 
111 	if (array->size) {
112 		array->size--;
113 		handle = array->entries[array->size];
114 		array->entries[array->size] = VMCI_INVALID_HANDLE;
115 	}
116 
117 	return handle;
118 }
119 
120 /*
121  * Handle at given index, VMCI_INVALID_HANDLE if invalid index.
122  */
123 struct vmci_handle
vmci_handle_arr_get_entry(const struct vmci_handle_arr * array,u32 index)124 vmci_handle_arr_get_entry(const struct vmci_handle_arr *array, u32 index)
125 {
126 	if (unlikely(index >= array->size))
127 		return VMCI_INVALID_HANDLE;
128 
129 	return array->entries[index];
130 }
131 
vmci_handle_arr_has_entry(const struct vmci_handle_arr * array,struct vmci_handle entry_handle)132 bool vmci_handle_arr_has_entry(const struct vmci_handle_arr *array,
133 			       struct vmci_handle entry_handle)
134 {
135 	u32 i;
136 
137 	for (i = 0; i < array->size; i++)
138 		if (vmci_handle_is_equal(array->entries[i], entry_handle))
139 			return true;
140 
141 	return false;
142 }
143 
144 /*
145  * NULL if the array is empty. Otherwise, a pointer to the array
146  * of VMCI handles in the handle array.
147  */
vmci_handle_arr_get_handles(struct vmci_handle_arr * array)148 struct vmci_handle *vmci_handle_arr_get_handles(struct vmci_handle_arr *array)
149 {
150 	if (array->size)
151 		return array->entries;
152 
153 	return NULL;
154 }
155