blob: 5ef75463c2397aae3effc92f5401e4ec6d390e0e [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0-only
/*
* Interface for the array of abstracted fences.
*
* Copyright (C) 2023 Google LLC
*/
#include <linux/kref.h>
#include <linux/slab.h>
#include <gcip/gcip-fence-array.h>
#include <gcip/gcip-fence.h>
/*
* Holds the spin locks which protect the number of signalers of each fence in @fence_array.
*
* The caller must use the `gcip_fence_array_submitted_signalers_unlock` function to release the
* locks.
*/
static void gcip_fence_array_submitted_signalers_lock(struct gcip_fence_array *fence_array)
{
int i;
if (!fence_array || fence_array->size <= 0)
return;
for (i = 0; i < fence_array->size; i++)
gcip_fence_submitted_signalers_lock(fence_array->fences[i]);
}
/* Releases the spin locks held by the `gcip_fence_array_submitted_signalers_lock` function. */
static void gcip_fence_array_submitted_signalers_unlock(struct gcip_fence_array *fence_array)
{
int i;
if (!fence_array || fence_array->size <= 0)
return;
for (i = fence_array->size - 1; i >= 0; i--)
gcip_fence_submitted_signalers_unlock(fence_array->fences[i]);
}
struct gcip_fence_array *gcip_fence_array_create(int *fences, int num_fences, bool check_same_type)
{
int i, ret;
struct gcip_fence_array *fence_array;
struct gcip_fence *fence;
if ((!fences && num_fences) || num_fences < 0)
return ERR_PTR(-EINVAL);
fence_array = kzalloc(sizeof(*fence_array), GFP_KERNEL);
if (!fence_array)
return ERR_PTR(-ENOMEM);
fence_array->fences = kcalloc(num_fences, sizeof(*fence_array->fences), GFP_KERNEL);
if (!fence_array->fences) {
ret = -ENOMEM;
goto err_free_fence_array;
}
fence_array->same_type = true;
for (i = 0; i < num_fences; i++) {
fence = gcip_fence_fdget(fences[i]);
if (IS_ERR(fence)) {
ret = PTR_ERR(fence);
goto err_put_fences;
}
if (i && fence_array->same_type && fence->type != fence_array->fences[0]->type) {
/* Check whether all fences are the same type. */
if (check_same_type) {
ret = -EINVAL;
gcip_fence_put(fence);
goto err_put_fences;
}
fence_array->same_type = false;
}
fence_array->fences[i] = fence;
}
if (i && fence_array->same_type)
fence_array->type = fence_array->fences[0]->type;
fence_array->size = i;
kref_init(&fence_array->kref);
return fence_array;
err_put_fences:
while (i--)
gcip_fence_put(fence_array->fences[i]);
kfree(fence_array->fences);
err_free_fence_array:
kfree(fence_array);
return ERR_PTR(ret);
}
static void gcip_fence_array_release(struct kref *kref)
{
struct gcip_fence_array *fence_array = container_of(kref, struct gcip_fence_array, kref);
int i;
for (i = 0; i < fence_array->size; i++)
gcip_fence_put(fence_array->fences[i]);
kfree(fence_array->fences);
kfree(fence_array);
}
struct gcip_fence_array *gcip_fence_array_get(struct gcip_fence_array *fence_array)
{
if (!fence_array)
return NULL;
kref_get(&fence_array->kref);
return fence_array;
}
void gcip_fence_array_put(struct gcip_fence_array *fence_array)
{
if (fence_array)
kref_put(&fence_array->kref, gcip_fence_array_release);
}
void gcip_fence_array_signal(struct gcip_fence_array *fence_array, int errno)
{
int i;
if (!fence_array)
return;
for (i = 0; i < fence_array->size; i++)
gcip_fence_signal(fence_array->fences[i], errno);
}
void gcip_fence_array_waited(struct gcip_fence_array *fence_array)
{
int i;
if (!fence_array)
return;
for (i = 0; i < fence_array->size; i++)
gcip_fence_waited(fence_array->fences[i]);
}
void gcip_fence_array_submit_signaler(struct gcip_fence_array *fence_array)
{
int i;
if (!fence_array)
return;
for (i = 0; i < fence_array->size; i++)
gcip_fence_submit_signaler(fence_array->fences[i]);
}
void gcip_fence_array_submit_waiter(struct gcip_fence_array *fence_array)
{
int i;
if (!fence_array)
return;
for (i = 0; i < fence_array->size; i++)
gcip_fence_submit_waiter(fence_array->fences[i]);
}
int gcip_fence_array_submit_waiter_and_signaler(struct gcip_fence_array *in_fences,
struct gcip_fence_array *out_fences)
{
int i;
gcip_fence_array_submitted_signalers_lock(in_fences);
/* Checks whether we can submit a waiter to @in_fences. */
for (i = 0; in_fences && i < in_fences->size; i++) {
if (!gcip_fence_is_waiter_submittable_locked(in_fences->fences[i])) {
gcip_fence_array_submitted_signalers_unlock(in_fences);
return -EAGAIN;
}
}
/*
* We can release the lock of @in_fences because once they are able to submit a waiter, it
* means that all signalers have been submitted to @in_fences and the fact won't be changed.
* Will submit a waiter to @in_fences if @out_fences are able to submit a signaler.
*/
gcip_fence_array_submitted_signalers_unlock(in_fences);
gcip_fence_array_submitted_signalers_lock(out_fences);
/* Checks whether we can submit a signaler to @out_fences. */
for (i = 0; out_fences && i < out_fences->size; i++) {
if (!gcip_fence_is_signaler_submittable_locked(out_fences->fences[i])) {
gcip_fence_array_submitted_signalers_unlock(out_fences);
return -EPERM;
}
}
/* Submits a signaler to @out_fences. */
for (i = 0; out_fences && i < out_fences->size; i++)
gcip_fence_submit_signaler_locked(out_fences->fences[i]);
gcip_fence_array_submitted_signalers_unlock(out_fences);
/* Submits a waiter to @in_fences. */
for (i = 0; in_fences && i < in_fences->size; i++)
gcip_fence_submit_waiter(in_fences->fences[i]);
return 0;
}
uint16_t *gcip_fence_array_get_iif_id(struct gcip_fence_array *fence_array, int *num_iif,
bool out_fences, enum iif_ip_type signaler_ip)
{
uint16_t *iif_fences;
struct iif_fence *iif;
int i, j;
*num_iif = 0;
if (!fence_array)
return NULL;
for (i = 0; i < fence_array->size; i++) {
if (fence_array->fences[i]->type == GCIP_INTER_IP_FENCE) {
iif = fence_array->fences[i]->fence.iif;
if (out_fences && iif->signaler_ip != signaler_ip) {
*num_iif = 0;
return ERR_PTR(-EINVAL);
}
(*num_iif)++;
}
}
if (!(*num_iif))
return NULL;
iif_fences = kcalloc(*num_iif, sizeof(*iif_fences), GFP_KERNEL);
if (!iif_fences)
return ERR_PTR(-ENOMEM);
for (i = 0, j = 0; i < fence_array->size; i++) {
if (fence_array->fences[i]->type == GCIP_INTER_IP_FENCE)
iif_fences[j++] = gcip_fence_get_iif_id(fence_array->fences[i]);
}
return iif_fences;
}
int gcip_fence_array_wait_signaler_submission(struct gcip_fence_array *fence_array,
unsigned int eventfd, int *remaining_signalers)
{
return gcip_fence_wait_signaler_submission(fence_array->fences, fence_array->size, eventfd,
remaining_signalers);
}