18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * VMware VMCI Driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2012 VMware, Inc. All rights reserved.
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/slab.h>
98c2ecf20Sopenharmony_ci#include "vmci_handle_array.h"
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_cistatic size_t handle_arr_calc_size(u32 capacity)
128c2ecf20Sopenharmony_ci{
138c2ecf20Sopenharmony_ci	return VMCI_HANDLE_ARRAY_HEADER_SIZE +
148c2ecf20Sopenharmony_ci	    capacity * sizeof(struct vmci_handle);
158c2ecf20Sopenharmony_ci}
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_cistruct vmci_handle_arr *vmci_handle_arr_create(u32 capacity, u32 max_capacity)
188c2ecf20Sopenharmony_ci{
198c2ecf20Sopenharmony_ci	struct vmci_handle_arr *array;
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci	if (max_capacity == 0 || capacity > max_capacity)
228c2ecf20Sopenharmony_ci		return NULL;
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci	if (capacity == 0)
258c2ecf20Sopenharmony_ci		capacity = min((u32)VMCI_HANDLE_ARRAY_DEFAULT_CAPACITY,
268c2ecf20Sopenharmony_ci			       max_capacity);
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci	array = kmalloc(handle_arr_calc_size(capacity), GFP_ATOMIC);
298c2ecf20Sopenharmony_ci	if (!array)
308c2ecf20Sopenharmony_ci		return NULL;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	array->capacity = capacity;
338c2ecf20Sopenharmony_ci	array->max_capacity = max_capacity;
348c2ecf20Sopenharmony_ci	array->size = 0;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	return array;
378c2ecf20Sopenharmony_ci}
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_civoid vmci_handle_arr_destroy(struct vmci_handle_arr *array)
408c2ecf20Sopenharmony_ci{
418c2ecf20Sopenharmony_ci	kfree(array);
428c2ecf20Sopenharmony_ci}
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ciint vmci_handle_arr_append_entry(struct vmci_handle_arr **array_ptr,
458c2ecf20Sopenharmony_ci				 struct vmci_handle handle)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	struct vmci_handle_arr *array = *array_ptr;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	if (unlikely(array->size >= array->capacity)) {
508c2ecf20Sopenharmony_ci		/* reallocate. */
518c2ecf20Sopenharmony_ci		struct vmci_handle_arr *new_array;
528c2ecf20Sopenharmony_ci		u32 capacity_bump = min(array->max_capacity - array->capacity,
538c2ecf20Sopenharmony_ci					array->capacity);
548c2ecf20Sopenharmony_ci		size_t new_size = handle_arr_calc_size(array->capacity +
558c2ecf20Sopenharmony_ci						       capacity_bump);
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci		if (array->size >= array->max_capacity)
588c2ecf20Sopenharmony_ci			return VMCI_ERROR_NO_MEM;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci		new_array = krealloc(array, new_size, GFP_ATOMIC);
618c2ecf20Sopenharmony_ci		if (!new_array)
628c2ecf20Sopenharmony_ci			return VMCI_ERROR_NO_MEM;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci		new_array->capacity += capacity_bump;
658c2ecf20Sopenharmony_ci		*array_ptr = array = new_array;
668c2ecf20Sopenharmony_ci	}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	array->entries[array->size] = handle;
698c2ecf20Sopenharmony_ci	array->size++;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	return VMCI_SUCCESS;
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci/*
758c2ecf20Sopenharmony_ci * Handle that was removed, VMCI_INVALID_HANDLE if entry not found.
768c2ecf20Sopenharmony_ci */
778c2ecf20Sopenharmony_cistruct vmci_handle vmci_handle_arr_remove_entry(struct vmci_handle_arr *array,
788c2ecf20Sopenharmony_ci						struct vmci_handle entry_handle)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	struct vmci_handle handle = VMCI_INVALID_HANDLE;
818c2ecf20Sopenharmony_ci	u32 i;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	for (i = 0; i < array->size; i++) {
848c2ecf20Sopenharmony_ci		if (vmci_handle_is_equal(array->entries[i], entry_handle)) {
858c2ecf20Sopenharmony_ci			handle = array->entries[i];
868c2ecf20Sopenharmony_ci			array->size--;
878c2ecf20Sopenharmony_ci			array->entries[i] = array->entries[array->size];
888c2ecf20Sopenharmony_ci			array->entries[array->size] = VMCI_INVALID_HANDLE;
898c2ecf20Sopenharmony_ci			break;
908c2ecf20Sopenharmony_ci		}
918c2ecf20Sopenharmony_ci	}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	return handle;
948c2ecf20Sopenharmony_ci}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci/*
978c2ecf20Sopenharmony_ci * Handle that was removed, VMCI_INVALID_HANDLE if array was empty.
988c2ecf20Sopenharmony_ci */
998c2ecf20Sopenharmony_cistruct vmci_handle vmci_handle_arr_remove_tail(struct vmci_handle_arr *array)
1008c2ecf20Sopenharmony_ci{
1018c2ecf20Sopenharmony_ci	struct vmci_handle handle = VMCI_INVALID_HANDLE;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	if (array->size) {
1048c2ecf20Sopenharmony_ci		array->size--;
1058c2ecf20Sopenharmony_ci		handle = array->entries[array->size];
1068c2ecf20Sopenharmony_ci		array->entries[array->size] = VMCI_INVALID_HANDLE;
1078c2ecf20Sopenharmony_ci	}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	return handle;
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci/*
1138c2ecf20Sopenharmony_ci * Handle at given index, VMCI_INVALID_HANDLE if invalid index.
1148c2ecf20Sopenharmony_ci */
1158c2ecf20Sopenharmony_cistruct vmci_handle
1168c2ecf20Sopenharmony_civmci_handle_arr_get_entry(const struct vmci_handle_arr *array, u32 index)
1178c2ecf20Sopenharmony_ci{
1188c2ecf20Sopenharmony_ci	if (unlikely(index >= array->size))
1198c2ecf20Sopenharmony_ci		return VMCI_INVALID_HANDLE;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	return array->entries[index];
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_cibool vmci_handle_arr_has_entry(const struct vmci_handle_arr *array,
1258c2ecf20Sopenharmony_ci			       struct vmci_handle entry_handle)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	u32 i;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	for (i = 0; i < array->size; i++)
1308c2ecf20Sopenharmony_ci		if (vmci_handle_is_equal(array->entries[i], entry_handle))
1318c2ecf20Sopenharmony_ci			return true;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	return false;
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci/*
1378c2ecf20Sopenharmony_ci * NULL if the array is empty. Otherwise, a pointer to the array
1388c2ecf20Sopenharmony_ci * of VMCI handles in the handle array.
1398c2ecf20Sopenharmony_ci */
1408c2ecf20Sopenharmony_cistruct vmci_handle *vmci_handle_arr_get_handles(struct vmci_handle_arr *array)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	if (array->size)
1438c2ecf20Sopenharmony_ci		return array->entries;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	return NULL;
1468c2ecf20Sopenharmony_ci}
147