// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2021 Broadcom. All Rights Reserved. The term
* “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
*/
/*
* domain_sm Domain State Machine: States
*/
#include "efc.h"
int
efc_domain_cb(void *arg, int event, void *data)
{
struct efc *efc = arg;
struct efc_domain *domain = NULL;
int rc = 0;
unsigned long flags = 0;
if (event != EFC_HW_DOMAIN_FOUND)
domain = data;
/* Accept domain callback events from the user driver */
spin_lock_irqsave(&efc->lock, flags);
switch (event) {
case EFC_HW_DOMAIN_FOUND: {
u64 fcf_wwn = 0;
struct efc_domain_record *drec = data;
/* extract the fcf_wwn */
fcf_wwn = be64_to_cpu(*((__be64 *)drec->wwn));
efc_log_debug(efc, "Domain found: wwn %016llX\n", fcf_wwn);
/* lookup domain, or allocate a new one */
domain = efc->domain;
if (!domain) {
domain = efc_domain_alloc(efc, fcf_wwn);
if (!domain) {
efc_log_err(efc, "efc_domain_alloc() failed\n");
rc = -1;
break;
}
efc_sm_transition(&domain->drvsm, __efc_domain_init,
NULL);
}
efc_domain_post_event(domain, EFC_EVT_DOMAIN_FOUND, drec);
break;
}
case EFC_HW_DOMAIN_LOST:
domain_trace(domain, "EFC_HW_DOMAIN_LOST:\n");
efc->hold_frames = true;
efc_domain_post_event(domain, EFC_EVT_DOMAIN_LOST, NULL);
break;
case EFC_HW_DOMAIN_ALLOC_OK:
domain_trace(domain, "EFC_HW_DOMAIN_ALLOC_OK:\n");
efc_domain_post_event(domain, EFC_EVT_DOMAIN_ALLOC_OK, NULL);
break;
case EFC_HW_DOMAIN_ALLOC_FAIL:
domain_trace(domain, "EFC_HW_DOMAIN_ALLOC_FAIL:\n");
efc_domain_post_event(domain, EFC_EVT_DOMAIN_ALLOC_FAIL,
NULL);
break;
case EFC_HW_DOMAIN_ATTACH_OK:
domain_trace(domain, "EFC_HW_DOMAIN_ATTACH_OK:\n");
efc_domain_post_event(domain, EFC_EVT_DOMAIN_ATTACH_OK, NULL);
break;
case EFC_HW_DOMAIN_ATTACH_FAIL:
domain_trace(domain, "EFC_HW_DOMAIN_ATTACH_FAIL:\n");
efc_domain_post_event(domain,
EFC_EVT_DOMAIN_ATTACH_FAIL, NULL);
break;
case EFC_HW_DOMAIN_FREE_OK:
domain_trace(domain, "EFC_HW_DOMAIN_FREE_OK:\n");
efc_domain_post_event(domain, EFC_EVT_DOMAIN_FREE_OK, NULL);
break;
case EFC_HW_DOMAIN_FREE_FAIL:
domain_trace(domain, "EFC_HW_DOMAIN_FREE_FAIL:\n");
efc_domain_post_event(domain, EFC_EVT_DOMAIN_FREE_FAIL, NULL);
break;
default:
efc_log_warn(efc, "unsupported event %#x\n", event);
}
spin_unlock_irqrestore(&efc->lock, flags);
if (efc->domain && domain->req_accept_frames) {
domain->req_accept_frames = false;
efc->hold_frames = false;
}
return rc;
}
static void
_efc_domain_free(struct kref *arg)
{
struct efc_domain *domain = container_of(arg, struct efc_domain, ref);
struct efc *efc = domain->efc;
if (efc->domain_free_cb)
(*efc->domain_free_cb)(efc, efc->domain_free_cb_arg);
kfree(domain);
}
void
efc_domain_free(struct efc_domain *domain)
{
struct efc *efc;
efc = domain->efc;
/* Hold frames to clear the domain pointer from the xport lookup */
efc->hold_frames = false;
efc_log_debug(efc, "Domain free: wwn %016llX\n", domain->fcf_wwn);
xa_destroy(&domain->lookup);
efc->domain = NULL;
kref_put(&domain->ref, domain->release);
}
struct efc_domain *
efc_domain_alloc(struct efc *efc, uint64_t fcf_wwn)
{
struct efc_domain *domain;
domain = kzalloc(sizeof(*domain), GFP_ATOMIC);
if (!domain)
return NULL;
domain->efc = efc;
domain->drvsm.app = domain;
/* initialize refcount */
kref_init(&domain->ref);
domain->release = _efc_domain_free;
xa_init(&domain->lookup);
INIT_LIST_HEAD(&domain->nport_list);
efc->domain = domain;
domain->fcf_wwn = fcf_wwn;
efc_log_debug(efc, "Domain allocated: wwn %016llX\n", domain->fcf_wwn);
return domain;
}
void
efc_register_domain_free_cb(struct efc *efc,
void (*callback)(struct efc *efc, void *arg),
void *arg)
{
/* Register a callback to be called when the domain is freed */
efc->domain_free_cb = callback;
efc->domain_free_cb_arg = arg;
if (!efc->domain && callback)
(*callback)(ef