1eb7935e4SPaul E. McKenney /* SPDX-License-Identifier: GPL-2.0+ */ 245753c5fSIngo Molnar /* 398059b98SPaul E. McKenney * RCU segmented callback lists, internal-to-rcu header file 445753c5fSIngo Molnar * 545753c5fSIngo Molnar * Copyright IBM Corporation, 2017 645753c5fSIngo Molnar * 7eb7935e4SPaul E. McKenney * Authors: Paul E. McKenney <paulmck@linux.ibm.com> 845753c5fSIngo Molnar */ 945753c5fSIngo Molnar 1045753c5fSIngo Molnar #include <linux/rcu_segcblist.h> 1145753c5fSIngo Molnar 12eda669a6SPaul E. McKenney /* Return number of callbacks in the specified callback list. */ 13eda669a6SPaul E. McKenney static inline long rcu_cblist_n_cbs(struct rcu_cblist *rclp) 14eda669a6SPaul E. McKenney { 15eda669a6SPaul E. McKenney return READ_ONCE(rclp->len); 16eda669a6SPaul E. McKenney } 17eda669a6SPaul E. McKenney 18b4e6039eSJoel Fernandes (Google) /* Return number of callbacks in segmented callback list by summing seglen. */ 19b4e6039eSJoel Fernandes (Google) long rcu_segcblist_n_segment_cbs(struct rcu_segcblist *rsclp); 20b4e6039eSJoel Fernandes (Google) 2198059b98SPaul E. McKenney void rcu_cblist_init(struct rcu_cblist *rclp); 22d1b222c6SPaul E. McKenney void rcu_cblist_enqueue(struct rcu_cblist *rclp, struct rcu_head *rhp); 23d1b222c6SPaul E. McKenney void rcu_cblist_flush_enqueue(struct rcu_cblist *drclp, 24d1b222c6SPaul E. McKenney struct rcu_cblist *srclp, 25d1b222c6SPaul E. McKenney struct rcu_head *rhp); 2698059b98SPaul E. McKenney struct rcu_head *rcu_cblist_dequeue(struct rcu_cblist *rclp); 2745753c5fSIngo Molnar 2845753c5fSIngo Molnar /* 2945753c5fSIngo Molnar * Is the specified rcu_segcblist structure empty? 3045753c5fSIngo Molnar * 3145753c5fSIngo Molnar * But careful! The fact that the ->head field is NULL does not 3245753c5fSIngo Molnar * necessarily imply that there are no callbacks associated with 3345753c5fSIngo Molnar * this structure. When callbacks are being invoked, they are 3445753c5fSIngo Molnar * removed as a group. If callback invocation must be preempted, 3545753c5fSIngo Molnar * the remaining callbacks will be added back to the list. Either 3645753c5fSIngo Molnar * way, the counts are updated later. 3745753c5fSIngo Molnar * 3845753c5fSIngo Molnar * So it is often the case that rcu_segcblist_n_cbs() should be used 3945753c5fSIngo Molnar * instead. 4045753c5fSIngo Molnar */ 4145753c5fSIngo Molnar static inline bool rcu_segcblist_empty(struct rcu_segcblist *rsclp) 4245753c5fSIngo Molnar { 43e6060b41SPaul E. McKenney return !READ_ONCE(rsclp->head); 4445753c5fSIngo Molnar } 4545753c5fSIngo Molnar 4645753c5fSIngo Molnar /* Return number of callbacks in segmented callback list. */ 4745753c5fSIngo Molnar static inline long rcu_segcblist_n_cbs(struct rcu_segcblist *rsclp) 4845753c5fSIngo Molnar { 49eda669a6SPaul E. McKenney #ifdef CONFIG_RCU_NOCB_CPU 50eda669a6SPaul E. McKenney return atomic_long_read(&rsclp->len); 51eda669a6SPaul E. McKenney #else 5245753c5fSIngo Molnar return READ_ONCE(rsclp->len); 53eda669a6SPaul E. McKenney #endif 5445753c5fSIngo Molnar } 5545753c5fSIngo Molnar 5665e56032SFrederic Weisbecker static inline void rcu_segcblist_set_flags(struct rcu_segcblist *rsclp, 5765e56032SFrederic Weisbecker int flags) 5865e56032SFrederic Weisbecker { 5965e56032SFrederic Weisbecker rsclp->flags |= flags; 6065e56032SFrederic Weisbecker } 6165e56032SFrederic Weisbecker 6265e56032SFrederic Weisbecker static inline void rcu_segcblist_clear_flags(struct rcu_segcblist *rsclp, 6365e56032SFrederic Weisbecker int flags) 6465e56032SFrederic Weisbecker { 6565e56032SFrederic Weisbecker rsclp->flags &= ~flags; 6665e56032SFrederic Weisbecker } 6765e56032SFrederic Weisbecker 6865e56032SFrederic Weisbecker static inline bool rcu_segcblist_test_flags(struct rcu_segcblist *rsclp, 6965e56032SFrederic Weisbecker int flags) 7065e56032SFrederic Weisbecker { 7165e56032SFrederic Weisbecker return READ_ONCE(rsclp->flags) & flags; 7265e56032SFrederic Weisbecker } 7365e56032SFrederic Weisbecker 7445753c5fSIngo Molnar /* 7545753c5fSIngo Molnar * Is the specified rcu_segcblist enabled, for example, not corresponding 76e83e73f5SPaul E. McKenney * to an offline CPU? 7745753c5fSIngo Molnar */ 7845753c5fSIngo Molnar static inline bool rcu_segcblist_is_enabled(struct rcu_segcblist *rsclp) 7945753c5fSIngo Molnar { 8065e56032SFrederic Weisbecker return rcu_segcblist_test_flags(rsclp, SEGCBLIST_ENABLED); 8145753c5fSIngo Molnar } 8245753c5fSIngo Molnar 83*f759081eSPaul E. McKenney /* Is the specified rcu_segcblist offloaded, or is SEGCBLIST_SOFTIRQ_ONLY set? */ 84ce5215c1SPaul E. McKenney static inline bool rcu_segcblist_is_offloaded(struct rcu_segcblist *rsclp) 85ce5215c1SPaul E. McKenney { 86*f759081eSPaul E. McKenney if (IS_ENABLED(CONFIG_RCU_NOCB_CPU) && 87*f759081eSPaul E. McKenney !rcu_segcblist_test_flags(rsclp, SEGCBLIST_SOFTIRQ_ONLY)) 888d346d43SFrederic Weisbecker return true; 898d346d43SFrederic Weisbecker 908d346d43SFrederic Weisbecker return false; 91ce5215c1SPaul E. McKenney } 92ce5215c1SPaul E. McKenney 9332aa2f41SFrederic Weisbecker static inline bool rcu_segcblist_completely_offloaded(struct rcu_segcblist *rsclp) 9432aa2f41SFrederic Weisbecker { 9532aa2f41SFrederic Weisbecker int flags = SEGCBLIST_KTHREAD_CB | SEGCBLIST_KTHREAD_GP | SEGCBLIST_OFFLOADED; 9632aa2f41SFrederic Weisbecker 97*f759081eSPaul E. McKenney if (IS_ENABLED(CONFIG_RCU_NOCB_CPU) && (rsclp->flags & flags) == flags) 9832aa2f41SFrederic Weisbecker return true; 9932aa2f41SFrederic Weisbecker 10032aa2f41SFrederic Weisbecker return false; 10132aa2f41SFrederic Weisbecker } 10232aa2f41SFrederic Weisbecker 10345753c5fSIngo Molnar /* 10445753c5fSIngo Molnar * Are all segments following the specified segment of the specified 10545753c5fSIngo Molnar * rcu_segcblist structure empty of callbacks? (The specified 10645753c5fSIngo Molnar * segment might well contain callbacks.) 10745753c5fSIngo Molnar */ 10845753c5fSIngo Molnar static inline bool rcu_segcblist_restempty(struct rcu_segcblist *rsclp, int seg) 10945753c5fSIngo Molnar { 11076c6927cSPaul E. McKenney return !READ_ONCE(*READ_ONCE(rsclp->tails[seg])); 11145753c5fSIngo Molnar } 11245753c5fSIngo Molnar 11334169061SPaul E. McKenney /* 11434169061SPaul E. McKenney * Is the specified segment of the specified rcu_segcblist structure 11534169061SPaul E. McKenney * empty of callbacks? 11634169061SPaul E. McKenney */ 11734169061SPaul E. McKenney static inline bool rcu_segcblist_segempty(struct rcu_segcblist *rsclp, int seg) 11834169061SPaul E. McKenney { 11934169061SPaul E. McKenney if (seg == RCU_DONE_TAIL) 12034169061SPaul E. McKenney return &rsclp->head == rsclp->tails[RCU_DONE_TAIL]; 12134169061SPaul E. McKenney return rsclp->tails[seg - 1] == rsclp->tails[seg]; 12234169061SPaul E. McKenney } 12334169061SPaul E. McKenney 124d1b222c6SPaul E. McKenney void rcu_segcblist_inc_len(struct rcu_segcblist *rsclp); 1256bc33582SJoel Fernandes (Google) void rcu_segcblist_add_len(struct rcu_segcblist *rsclp, long v); 12698059b98SPaul E. McKenney void rcu_segcblist_init(struct rcu_segcblist *rsclp); 12798059b98SPaul E. McKenney void rcu_segcblist_disable(struct rcu_segcblist *rsclp); 128d97b0781SFrederic Weisbecker void rcu_segcblist_offload(struct rcu_segcblist *rsclp, bool offload); 12998059b98SPaul E. McKenney bool rcu_segcblist_ready_cbs(struct rcu_segcblist *rsclp); 13098059b98SPaul E. McKenney bool rcu_segcblist_pend_cbs(struct rcu_segcblist *rsclp); 13198059b98SPaul E. McKenney struct rcu_head *rcu_segcblist_first_cb(struct rcu_segcblist *rsclp); 13298059b98SPaul E. McKenney struct rcu_head *rcu_segcblist_first_pend_cb(struct rcu_segcblist *rsclp); 1335d6742b3SPaul E. McKenney bool rcu_segcblist_nextgp(struct rcu_segcblist *rsclp, unsigned long *lp); 13498059b98SPaul E. McKenney void rcu_segcblist_enqueue(struct rcu_segcblist *rsclp, 13577a40f97SJoel Fernandes (Google) struct rcu_head *rhp); 13698059b98SPaul E. McKenney bool rcu_segcblist_entrain(struct rcu_segcblist *rsclp, 13777a40f97SJoel Fernandes (Google) struct rcu_head *rhp); 13898059b98SPaul E. McKenney void rcu_segcblist_extract_done_cbs(struct rcu_segcblist *rsclp, 13998059b98SPaul E. McKenney struct rcu_cblist *rclp); 14098059b98SPaul E. McKenney void rcu_segcblist_extract_pend_cbs(struct rcu_segcblist *rsclp, 14198059b98SPaul E. McKenney struct rcu_cblist *rclp); 14298059b98SPaul E. McKenney void rcu_segcblist_insert_count(struct rcu_segcblist *rsclp, 14398059b98SPaul E. McKenney struct rcu_cblist *rclp); 14498059b98SPaul E. McKenney void rcu_segcblist_insert_done_cbs(struct rcu_segcblist *rsclp, 14598059b98SPaul E. McKenney struct rcu_cblist *rclp); 14698059b98SPaul E. McKenney void rcu_segcblist_insert_pend_cbs(struct rcu_segcblist *rsclp, 14798059b98SPaul E. McKenney struct rcu_cblist *rclp); 14898059b98SPaul E. McKenney void rcu_segcblist_advance(struct rcu_segcblist *rsclp, unsigned long seq); 14998059b98SPaul E. McKenney bool rcu_segcblist_accelerate(struct rcu_segcblist *rsclp, unsigned long seq); 150f2dbe4a5SPaul E. McKenney void rcu_segcblist_merge(struct rcu_segcblist *dst_rsclp, 151f2dbe4a5SPaul E. McKenney struct rcu_segcblist *src_rsclp); 152