2012-04-04 16:58:27 -04:00
/*
* Copyright 2012 Tilera Corporation . All Rights Reserved .
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation , version 2.
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE , GOOD TITLE or
* NON INFRINGEMENT . See the GNU General Public License for
* more details .
*/
# ifndef _GXIO_DMA_QUEUE_H_
# define _GXIO_DMA_QUEUE_H_
/*
* DMA queue management APIs shared between TRIO and mPIPE .
*/
2012-10-02 18:01:25 +01:00
# include <gxio/common.h>
2012-04-04 16:58:27 -04:00
/* The credit counter lives in the high 32 bits. */
# define DMA_QUEUE_CREDIT_SHIFT 32
/*
* State object that tracks a DMA queue ' s head and tail indices , as
* well as the number of commands posted and completed . The
* structure is accessed via a thread - safe , lock - free algorithm .
*/
typedef struct {
/*
* Address of a MPIPE_EDMA_POST_REGION_VAL_t ,
* TRIO_PUSH_DMA_REGION_VAL_t , or TRIO_PULL_DMA_REGION_VAL_t
* register . These register have identical encodings and provide
* information about how many commands have been processed .
*/
void * post_region_addr ;
/*
* A lazily - updated count of how many edescs the hardware has
* completed .
*/
uint64_t hw_complete_count __attribute__ ( ( aligned ( 64 ) ) ) ;
/*
* High 32 bits are a count of available egress command credits ,
* low 24 bits are the next egress " slot " .
*/
int64_t credits_and_next_index ;
} __gxio_dma_queue_t ;
/* Initialize a dma queue. */
extern void __gxio_dma_queue_init ( __gxio_dma_queue_t * dma_queue ,
void * post_region_addr ,
unsigned int num_entries ) ;
/*
* Update the " credits_and_next_index " and " hw_complete_count " fields
* based on pending hardware completions . Note that some other thread
* may have already done this and , importantly , may still be in the
* process of updating " credits_and_next_index " .
*/
extern void __gxio_dma_queue_update_credits ( __gxio_dma_queue_t * dma_queue ) ;
/* Wait for credits to become available. */
extern int64_t __gxio_dma_queue_wait_for_credits ( __gxio_dma_queue_t * dma_queue ,
int64_t modifier ) ;
/* Reserve slots in the queue, optionally waiting for slots to become
* available , and optionally returning a " completion_slot " suitable for
* direct comparison to " hw_complete_count " .
*/
static inline int64_t __gxio_dma_queue_reserve ( __gxio_dma_queue_t * dma_queue ,
unsigned int num , bool wait ,
bool completion )
{
uint64_t slot ;
/*
* Try to reserve ' num ' egress command slots . We do this by
* constructing a constant that subtracts N credits and adds N to
* the index , and using fetchaddgez to only apply it if the credits
* count doesn ' t go negative .
*/
int64_t modifier = ( ( ( int64_t ) ( - num ) ) < < DMA_QUEUE_CREDIT_SHIFT ) | num ;
int64_t old =
__insn_fetchaddgez ( & dma_queue - > credits_and_next_index ,
modifier ) ;
if ( unlikely ( old + modifier < 0 ) ) {
/*
* We ' re out of credits . Try once to get more by checking for
* completed egress commands . If that fails , wait or fail .
*/
__gxio_dma_queue_update_credits ( dma_queue ) ;
old = __insn_fetchaddgez ( & dma_queue - > credits_and_next_index ,
modifier ) ;
if ( old + modifier < 0 ) {
if ( wait )
old = __gxio_dma_queue_wait_for_credits
( dma_queue , modifier ) ;
else
return GXIO_ERR_DMA_CREDITS ;
}
}
/* The bottom 24 bits of old encode the "slot". */
slot = ( old & 0xffffff ) ;
if ( completion ) {
/*
* A " completion_slot " is a " slot " which can be compared to
* " hw_complete_count " at any time in the future . To convert
* " slot " into a " completion_slot " , we access " hw_complete_count "
* once ( knowing that we have reserved a slot , and thus , it will
* be " basically " accurate ) , and combine its high 40 bits with
* the 24 bit " slot " , and handle " wrapping " by adding " 1 << 24 "
* if the result is LESS than " hw_complete_count " .
*/
uint64_t complete ;
locking/atomics: COCCINELLE/treewide: Convert trivial ACCESS_ONCE() patterns to READ_ONCE()/WRITE_ONCE()
Please do not apply this to mainline directly, instead please re-run the
coccinelle script shown below and apply its output.
For several reasons, it is desirable to use {READ,WRITE}_ONCE() in
preference to ACCESS_ONCE(), and new code is expected to use one of the
former. So far, there's been no reason to change most existing uses of
ACCESS_ONCE(), as these aren't harmful, and changing them results in
churn.
However, for some features, the read/write distinction is critical to
correct operation. To distinguish these cases, separate read/write
accessors must be used. This patch migrates (most) remaining
ACCESS_ONCE() instances to {READ,WRITE}_ONCE(), using the following
coccinelle script:
----
// Convert trivial ACCESS_ONCE() uses to equivalent READ_ONCE() and
// WRITE_ONCE()
// $ make coccicheck COCCI=/home/mark/once.cocci SPFLAGS="--include-headers" MODE=patch
virtual patch
@ depends on patch @
expression E1, E2;
@@
- ACCESS_ONCE(E1) = E2
+ WRITE_ONCE(E1, E2)
@ depends on patch @
expression E;
@@
- ACCESS_ONCE(E)
+ READ_ONCE(E)
----
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: davem@davemloft.net
Cc: linux-arch@vger.kernel.org
Cc: mpe@ellerman.id.au
Cc: shuah@kernel.org
Cc: snitzer@redhat.com
Cc: thor.thayer@linux.intel.com
Cc: tj@kernel.org
Cc: viro@zeniv.linux.org.uk
Cc: will.deacon@arm.com
Link: http://lkml.kernel.org/r/1508792849-3115-19-git-send-email-paulmck@linux.vnet.ibm.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
2017-10-23 14:07:29 -07:00
complete = READ_ONCE ( dma_queue - > hw_complete_count ) ;
2012-04-04 16:58:27 -04:00
slot | = ( complete & 0xffffffffff000000 ) ;
if ( slot < complete )
slot + = 0x1000000 ;
}
/*
* If any of our slots mod 256 were equivalent to 0 , go ahead and
* collect some egress credits , and update " hw_complete_count " , and
* make sure the index doesn ' t overflow into the credits .
*/
if ( unlikely ( ( ( old + num ) & 0xff ) < num ) ) {
__gxio_dma_queue_update_credits ( dma_queue ) ;
/* Make sure the index doesn't overflow into the credits. */
# ifdef __BIG_ENDIAN__
* ( ( ( uint8_t * ) & dma_queue - > credits_and_next_index ) + 4 ) = 0 ;
# else
* ( ( ( uint8_t * ) & dma_queue - > credits_and_next_index ) + 3 ) = 0 ;
# endif
}
return slot ;
}
/* Non-inlinable "__gxio_dma_queue_reserve(..., true)". */
extern int64_t __gxio_dma_queue_reserve_aux ( __gxio_dma_queue_t * dma_queue ,
unsigned int num , int wait ) ;
/* Check whether a particular "completion slot" has completed.
*
* Note that this function requires a " completion slot " , and thus
* cannot be used with the result of any " reserve_fast " function .
*/
extern int __gxio_dma_queue_is_complete ( __gxio_dma_queue_t * dma_queue ,
int64_t completion_slot , int update ) ;
# endif /* !_GXIO_DMA_QUEUE_H_ */