[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [XEN PATCH v7 14/20] xen/arm: ffa: support guest FFA_PARTITION_INFO_GET
Hi, On Fri, Mar 3, 2023 at 2:50 PM Bertrand Marquis <Bertrand.Marquis@xxxxxxx> wrote: > > > > > On 3 Mar 2023, at 14:17, Jens Wiklander <jens.wiklander@xxxxxxxxxx> wrote: > > > > Hi Bertrand, > > > > On Fri, Mar 3, 2023 at 10:51 AM Bertrand Marquis > > <Bertrand.Marquis@xxxxxxx> wrote: > >> > >> Hi Jens, > >> > >>> On 22 Feb 2023, at 16:33, Jens Wiklander <jens.wiklander@xxxxxxxxxx> > >>> wrote: > >>> > >>> Adds support in the mediator to handle FFA_PARTITION_INFO_GET requests > >>> from a guest. The requests are forwarded to the SPMC and the response is > >>> translated according to the FF-A version in use by the guest. > >>> > >>> Using FFA_PARTITION_INFO_GET changes the owner of the RX buffer to the > >>> caller (the guest in this case), so once it is done with the buffer it > >>> must be released using FFA_RX_RELEASE before another call can be made. > >>> > >>> Signed-off-by: Jens Wiklander <jens.wiklander@xxxxxxxxxx> > >>> --- > >>> xen/arch/arm/tee/ffa.c | 126 ++++++++++++++++++++++++++++++++++++++++- > >>> 1 file changed, 124 insertions(+), 2 deletions(-) > >>> > >>> diff --git a/xen/arch/arm/tee/ffa.c b/xen/arch/arm/tee/ffa.c > >>> index 953b6dfd5eca..3571817c0bcd 100644 > >>> --- a/xen/arch/arm/tee/ffa.c > >>> +++ b/xen/arch/arm/tee/ffa.c > >>> @@ -141,6 +141,12 @@ > >>> #define FFA_MSG_POLL 0x8400006AU > >>> > >>> /* Partition information descriptor */ > >>> +struct ffa_partition_info_1_0 { > >>> + uint16_t id; > >>> + uint16_t execution_context; > >>> + uint32_t partition_properties; > >>> +}; > >>> + > >>> struct ffa_partition_info_1_1 { > >>> uint16_t id; > >>> uint16_t execution_context; > >>> @@ -157,9 +163,8 @@ struct ffa_ctx { > >>> uint32_t guest_vers; > >>> bool tx_is_mine; > >>> bool interrupted; > >>> + spinlock_t lock; > >>> }; > >>> - > >>> - > >> > >> This is removing 2 empty lines (previous patch was wrongly adding one) > >> but one empty line is required here. > > > > I'll fix it. > > > >> > >>> /* Negotiated FF-A version to use with the SPMC */ > >>> static uint32_t ffa_version __ro_after_init; > >>> > >>> @@ -173,10 +178,16 @@ static unsigned int subscr_vm_destroyed_count > >>> __read_mostly; > >>> * Our rx/tx buffers shared with the SPMC. > >>> * > >>> * ffa_page_count is the number of pages used in each of these buffers. > >>> + * > >>> + * The RX buffer is protected from concurrent usage with > >>> ffa_rx_buffer_lock. > >>> + * Note that the SPMC is also tracking the ownership of our RX buffer so > >>> + * for calls which uses our RX buffer to deliver a result we must call > >>> + * ffa_rx_release() to let the SPMC know that we're done with the buffer. > >>> */ > >>> static void *ffa_rx __read_mostly; > >>> static void *ffa_tx __read_mostly; > >>> static unsigned int ffa_page_count __read_mostly; > >>> +static DEFINE_SPINLOCK(ffa_rx_buffer_lock); > >>> > >>> static bool ffa_get_version(uint32_t *vers) > >>> { > >>> @@ -463,6 +474,98 @@ static uint32_t handle_rxtx_unmap(void) > >>> return FFA_RET_OK; > >>> } > >>> > >>> +static uint32_t handle_partition_info_get(uint32_t w1, uint32_t w2, > >>> uint32_t w3, > >>> + uint32_t w4, uint32_t w5, > >>> + uint32_t *count) > >>> +{ > >>> + bool query_count_only = w5 & FFA_PARTITION_INFO_GET_COUNT_FLAG; > >>> + uint32_t w5_mask = 0; > >>> + uint32_t ret = FFA_RET_DENIED; > >>> + struct domain *d = current->domain; > >>> + struct ffa_ctx *ctx = d->arch.tee; > >>> + > >>> + /* > >>> + * FF-A v1.0 has w5 MBZ while v1.1 allows > >>> + * FFA_PARTITION_INFO_GET_COUNT_FLAG to be non-zero. > >>> + */ > >>> + if ( ctx->guest_vers == FFA_VERSION_1_1 ) > >>> + w5_mask = FFA_PARTITION_INFO_GET_COUNT_FLAG; > >>> + if ( w5 & ~w5_mask ) > >>> + return FFA_RET_INVALID_PARAMETERS; > >>> + > >>> + if ( query_count_only ) > >>> + return ffa_partition_info_get(w1, w2, w3, w4, w5, count); > >> > >> This code seems a bit to complex. > >> I would suggest the following: > >> > >> if (w5 & FFA_PARTITION_INFO_GET_COUNT_FLAG) > >> { > >> if ( ctx->guest_vers == FFA_VERSION_1_1 ) > >> return ffa_partition_info_get(w1, w2, w3, w4, w5, count); > >> else > >> return FFA_RET_INVALID_PARAMETERS; > >> } > > > > OK, I can use that. I'll have to add a > > if (w5) > > return FFA_RET_INVALID_PARAMETERS; > > > > since the rest of the bits must be zero. > > ok > > > > >> > >>> + > >>> + if ( !ffa_page_count ) > >>> + return FFA_RET_DENIED; > >>> + > >>> + spin_lock(&ctx->lock); > >>> + spin_lock(&ffa_rx_buffer_lock); > >>> + if ( !ctx->page_count || !ctx->tx_is_mine ) > >> > >> If i understand correctly tx_is_mine is protecting the guest rx > >> buffer until rx_release is called by the guest so that we do not > >> write in it before the guest has retrieved the data from it. > >> > >> The name is very misleading, maybe rx_is_writeable or free would be better > >> ? > > > > The FF-A specification talks about ownership of the TX buffer (from > > the VMs point of view), hence the name. > > I'll change it to rx_is_free to be more intuitive without the spec. > > Yes but in the code it is quite unclear so better to rename it here. > > > > >> > >> Also it would be more optimal to test it before taking ffa_rx_buffer_lock. > > > > Yes, I'll change that. > > > >> > >> > >>> + goto out; > >>> + ret = ffa_partition_info_get(w1, w2, w3, w4, w5, count); > >>> + if ( ret ) > >>> + goto out; > >>> + > >>> + if ( ctx->guest_vers == FFA_VERSION_1_0 ) > >>> + { > >>> + size_t n; > >>> + struct ffa_partition_info_1_1 *src = ffa_rx; > >>> + struct ffa_partition_info_1_0 *dst = ctx->rx; > >>> + > >>> + if ( ctx->page_count * FFA_PAGE_SIZE < *count * sizeof(*dst) ) > >>> + { > >>> + ret = FFA_RET_NO_MEMORY; > >>> + goto out_rx_release; > >>> + } > >>> + > >>> + for ( n = 0; n < *count; n++ ) > >>> + { > >>> + dst[n].id = src[n].id; > >>> + dst[n].execution_context = src[n].execution_context; > >>> + dst[n].partition_properties = src[n].partition_properties; > >>> + } > >>> + } > >>> + else > >>> + { > >>> + size_t sz = *count * sizeof(struct ffa_partition_info_1_1); > >>> + > >>> + if ( ctx->page_count * FFA_PAGE_SIZE < sz ) > >>> + { > >>> + ret = FFA_RET_NO_MEMORY; > >>> + goto out_rx_release; > >>> + } > >>> + > >>> + > >>> + memcpy(ctx->rx, ffa_rx, sz); > >>> + } > >>> + ctx->tx_is_mine = false; > >> > >> at this point we have no reason to hold ctx->lock > > > > ctx->lock is special, we're never supposed to have contention on that > > lock. I believe that we in principle could use spin_trylock() instead > > and return FFA_RET_BUSY if it fails, but that might be a bit too much. > > The VM is supposed to synchronize calls that use the RXTX buffers. So > > unlocking ctx->lock early should give nothing for well-behaving > > guests, I'd prefer to keep things simple and unlock in reverse order > > if you don't mind. I'll add a comment. > > Please add a comment and a TODO as we have very big locked sections > here and xen is not preemptible so having someone blocked here by an > other core doing something is a concern. > > If it is expected that a VM should synchronize calls then we might want to > switch the ctx lock to use trylock and return busy. I'll try that. In fact, it should be possible to do the same with the ffa_rx_buffer_lock at least here in handle_partition_info_get(). In a later patch where share_shm() is introduced, it might only be possible with trylock if the guest uses the FFA_MEMORY_REGION_FLAG_TIME_SLICE flag. > > > > >> > >>> +out_rx_release: > >>> + ffa_rx_release(); > >> > >> There should be no case where do release without unlocking. > >> > >> It might be cleaner to have 2 functions ffa_rx_get and ffa_rx_release > >> handling both the lock and the rx_release message. > > > > I'd like to keep ffa_rx_release() as a dumb wrapper. ffa_rx_release() > > is also used in init_subscribers() where we don't use any locks. > > ffa_rx_release() is called after a successful call to > > ffa_partition_info_get() where we also gained ownership of our RX > > buffer. Things might be a bit too intertwined for further abstraction. > > I'll add a comment explaining the relationship between > > ffa_partition_info_get() and ffa_rx_release(). > > That would not create any problem to take the lock where it is not > done already and would make the implemenation a bit more robust. > > If at some point some FFA services are used in different cores during > the boot phase, it might make sense to take the lock even if we are in > situations where there should be no concurrency just to make the code > safer. > > Please tell me what you think here. I agree that some extra locking would be harmless, but there's a problem that ffa_rx_release() should only be called if we in fact have ownership of the RX buffer. We must if for instance ffa_partition_info_get() returns an error not call ffa_rx_release(). I think there is some value in only managing the locks in the handle_*() function since that makes it easier to see the locking order etc. That said, I wouldn't mind doing some locking in a helper function if it came naturally, but so far it hasn't in my opinion. Cheers, Jens > > > > >> > >>> +out: > >>> + spin_unlock(&ffa_rx_buffer_lock); > >> > >> this should stay with ffa_rx_release > > > > Depending on if you accept my explanation above. > > > Cheers > Bertrand > > > > > Thanks, > > Jens > > > >> > >> Cheers > >> Bertrand > >> > >>> + spin_unlock(&ctx->lock); > >>> + > >>> + return ret; > >>> +} > >>> + > >>> +static uint32_t handle_rx_release(void) > >>> +{ > >>> + uint32_t ret = FFA_RET_DENIED; > >>> + struct domain *d = current->domain; > >>> + struct ffa_ctx *ctx = d->arch.tee; > >>> + > >>> + spin_lock(&ctx->lock); > >>> + if ( !ctx->page_count || ctx->tx_is_mine ) > >>> + goto out; > >>> + ret = FFA_RET_OK; > >>> + ctx->tx_is_mine = true; > >>> +out: > >>> + spin_unlock(&ctx->lock); > >>> + > >>> + return ret; > >>> +} > >>> + > >>> static void handle_msg_send_direct_req(struct cpu_user_regs *regs, > >>> uint32_t fid) > >>> { > >>> struct arm_smccc_1_2_regs arg = { .a0 = fid, }; > >>> @@ -528,6 +631,7 @@ static bool ffa_handle_call(struct cpu_user_regs > >>> *regs) > >>> uint32_t fid = get_user_reg(regs, 0); > >>> struct domain *d = current->domain; > >>> struct ffa_ctx *ctx = d->arch.tee; > >>> + uint32_t count; > >>> int e; > >>> > >>> if ( !ctx ) > >>> @@ -559,6 +663,24 @@ static bool ffa_handle_call(struct cpu_user_regs > >>> *regs) > >>> else > >>> set_regs_success(regs, 0, 0); > >>> return true; > >>> + case FFA_PARTITION_INFO_GET: > >>> + e = handle_partition_info_get(get_user_reg(regs, 1), > >>> + get_user_reg(regs, 2), > >>> + get_user_reg(regs, 3), > >>> + get_user_reg(regs, 4), > >>> + get_user_reg(regs, 5), &count); > >>> + if ( e ) > >>> + set_regs_error(regs, e); > >>> + else > >>> + set_regs_success(regs, count, 0); > >>> + return true; > >>> + case FFA_RX_RELEASE: > >>> + e = handle_rx_release(); > >>> + if ( e ) > >>> + set_regs_error(regs, e); > >>> + else > >>> + set_regs_success(regs, 0, 0); > >>> + return true; > >>> case FFA_MSG_SEND_DIRECT_REQ_32: > >>> #ifdef CONFIG_ARM_64 > >>> case FFA_MSG_SEND_DIRECT_REQ_64: > >>> -- > >>> 2.34.1 > >
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |