|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [XEN PATCH v4 5/5] xen/arm: ffa: support notification
Hi Julien,
On Fri, May 3, 2024 at 11:59 AM Julien Grall <julien@xxxxxxx> wrote:
>
> Hi Jens,
>
> On 02/05/2024 15:56, Jens Wiklander wrote:
> > -static bool ffa_probe(void)
> > +static int __init ffa_init(void)
> > {
> > uint32_t vers;
> > unsigned int major_vers;
> > @@ -460,16 +511,16 @@ static bool ffa_probe(void)
> > printk(XENLOG_ERR
> > "ffa: unsupported SMCCC version %#x (need at least %#x)\n",
> > smccc_ver, ARM_SMCCC_VERSION_1_2);
> > - return false;
> > + return 0;
> > }
> >
> > if ( !ffa_get_version(&vers) )
> > - return false;
> > + return 0;
> >
> > if ( vers < FFA_MIN_SPMC_VERSION || vers > FFA_MY_VERSION )
> > {
> > printk(XENLOG_ERR "ffa: Incompatible version %#x found\n", vers);
> > - return false;
> > + return 0;
> > }
> >
> > major_vers = (vers >> FFA_VERSION_MAJOR_SHIFT) &
> > FFA_VERSION_MAJOR_MASK;
> > @@ -492,26 +543,33 @@ static bool ffa_probe(void)
> > !check_mandatory_feature(FFA_MEM_SHARE_32) ||
> > !check_mandatory_feature(FFA_MEM_RECLAIM) ||
> > !check_mandatory_feature(FFA_MSG_SEND_DIRECT_REQ_32) )
> > - return false;
> > + return 0;
> >
> > if ( !ffa_rxtx_init() )
> > - return false;
> > + return 0;
> >
> > ffa_version = vers;
> >
> > if ( !ffa_partinfo_init() )
> > goto err_rxtx_destroy;
> >
> > + ffa_notif_init();
> > INIT_LIST_HEAD(&ffa_teardown_head);
> > init_timer(&ffa_teardown_timer, ffa_teardown_timer_callback, NULL, 0);
> >
> > - return true;
> > + return 0;
> >
> > err_rxtx_destroy:
> > ffa_rxtx_destroy();
> > ffa_version = 0;
> >
> > - return false;
> > + return 0;
> > +}
> > +presmp_initcall(ffa_init);
> I would rather prefer if tee_init() is called from presmp_initcall().
> This would avoid FFA to have to try to initialize itself before all the
> others.
OK, I'll update.
>
> This is what I had in mind with my original request, but I guess I
> wasn't clear enough. Sorry for that.
No worries.
>
> [...]
>
> > +static void notif_irq_handler(int irq, void *data)
> > +{
> > + const struct arm_smccc_1_2_regs arg = {
> > + .a0 = FFA_NOTIFICATION_INFO_GET_64,
> > + };
> > + struct arm_smccc_1_2_regs resp;
> > + unsigned int id_pos;
> > + unsigned int list_count;
> > + uint64_t ids_count;
> > + unsigned int n;
> > + int32_t res;
> > +
> > + do {
> > + arm_smccc_1_2_smc(&arg, &resp);
> > + res = ffa_get_ret_code(&resp);
> > + if ( res )
> > + {
> > + if ( res != FFA_RET_NO_DATA )
> > + printk(XENLOG_ERR "ffa: notification info get failed:
> > error %d\n",
> > + res);
> > + return;
> > + }
> > +
> > + ids_count = resp.a2 >> FFA_NOTIF_INFO_GET_ID_LIST_SHIFT;
> > + list_count = ( resp.a2 >> FFA_NOTIF_INFO_GET_ID_COUNT_SHIFT ) &
> > + FFA_NOTIF_INFO_GET_ID_COUNT_MASK;
> > +
> > + id_pos = 0;
> > + for ( n = 0; n < list_count; n++ )
> > + {
> > + unsigned int count = ((ids_count >> 2 * n) & 0x3) + 1;
> > + uint16_t vm_id = get_id_from_resp(&resp, id_pos);
> > + struct domain *d;
> > +
> > + /*
> > + * vm_id == 0 means a notifications pending for Xen itself, but
> > + * we don't support that yet.
> > + */
> > + if (vm_id)
> > + d = ffa_rcu_lock_domain_by_vm_id(vm_id);
>
> I am still unconvinced that this is sufficient. This will prevent
> "struct domain *" to be freed. But ...
>
> > + else
> > + d = NULL;
> > +
> > + if ( d )
> > + {
> > + struct ffa_ctx *ctx = d->arch.tee;
>
> ... I don't think it will protect d->arch.tee to be freed as this is
> happening during teardorwn. You mostly need some other sort of locking
> to avoid d->arch.tee been freed behind your back.
OK, I'll need to work more on this. The outcome of the [1] thread may
affect this too.
[1] https://lists.xenproject.org/archives/html/xen-devel/2024-05/msg00156.html
>
> > + struct vcpu *v;
> > +
> > + ACCESS_ONCE(ctx->notif.secure_pending) = true;
> > +
> > + /*
> > + * Since we're only delivering global notification, always
> > + * deliver to the first online vCPU. It doesn't matter
> > + * which we chose, as long as it's available.
> > + */
> > + for_each_vcpu(d, v)
> > + {
> > + if ( is_vcpu_online(v) )
> > + {
> > + vgic_inject_irq(d, v, GUEST_FFA_NOTIF_PEND_INTR_ID,
> > + true);
> > + break;
> > + }
> > + }
> > + if ( !v )
> > + printk(XENLOG_ERR "ffa: can't inject NPI, all vCPUs
> > offline\n");
> > +
> > + rcu_unlock_domain(d);
> > + }
> > +
> > + id_pos += count;
> > + }
> > +
> > + } while (resp.a2 & FFA_NOTIF_INFO_GET_MORE_FLAG);
> > +}
>
> [...]
>
> > +static int ffa_setup_irq_callback(struct notifier_block *nfb,
> > + unsigned long action, void *hcpu)
> > +{
> > + unsigned int cpu = (unsigned long)hcpu;
> > + struct notif_irq_info irq_info = { };
> > +
> > + switch ( action )
> > + {
> > + case CPU_ONLINE:
>
> Can't you execute the notifier in CPU_STARTING? This will be called on
> the CPU directly, so you should be able to use request_irq(...).
I tried that first but it failed with the ASSERT_ALLOC_CONTEXT() in _xmalloc().
I've also tested a three-step solution with CPU_UP_PREPARE,
CPU_STARTING, and CPU_UP_CANCELED.
My approach here is more direct, but it still suffers from a weakness
in error handling even if it seems quite unlikely to run out of heap
or for setup_irq() to fail at this stage.
Thanks,
Jens
>
> > + /*
> > + * The notifier call is done on the primary or initiating CPU when
> > + * the target CPU have come online, but the SGI must be setup on
> > + * the target CPU.
> > + *
> > + * We make an IPI call to the target CPU to setup the SGI. The call
> > + * is executed in interrupt context on the target CPU, so we can't
> > + * call request_irq() directly since it allocates memory.
> > + *
> > + * We preallocate the needed irqaction here and pass it via the
> > + * temporary struct notif_irq_info. The call is synchronous in the
> > + * sense that when on_selected_cpus() returns the callback
> > + * notif_irq_enable() has done the same on the target CPU.
> > + *
> > + * We deal with two errors, one where notif_irq_enable() hasn't
> > + * been called for some reason, that error is logged below. The
> > + * other where setup_irq() fails is logged in the callback. We must
> > + * free the irqaction in both cases since it has failed to become
> > + * registered.
> > + *
> > + * Failures leads to a problem notifications, the CPUs with failure
> > + * will trigger on the SGI indicating that there are notifications
> > + * pending, while the SPMC in the secure world will not notice that
> > + * the interrupt was lost.
> > + */
> > + irq_info.action = xmalloc(struct irqaction);
> > + if ( !irq_info.action )
> > + {
> > + printk(XENLOG_ERR "ffa: setup irq %u failed, out of memory\n",
> > + notif_sri_irq);
> > + return -ENOMEM;
> > + }
> > +
> > + *irq_info.action = (struct irqaction){
> > + .handler = notif_irq_handler,
> > + .name = "FF-A notif",
> > + .free_on_release = 1,
> > + };
> > +
> > + on_selected_cpus(cpumask_of(cpu), notif_irq_enable, &irq_info, 1);
> > + if (!irq_info.called)
> > + printk(XENLOG_ERR "ffa: on_selected_cpus(cpumask_of(%u))
> > failed\n",
> > + cpu);
> > + /*
> > + * The irqaction is unused and must be freed if irq_info.action is
> > + * non-NULL at this stage.
> > + */
> > + XFREE(irq_info.action);
> > + break;
> > + default:
> > + break;
> > + }
> > +
> > + return 0;
> > +}
>
> Cheers,
>
> --
> Julien Grall
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |