lib.rs 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. extern crate proc_macro;
  2. mod loongarch64;
  3. mod riscv64;
  4. mod x86_64;
  5. use proc_macro2::TokenStream;
  6. use quote::{format_ident, quote};
  7. use syn::{parse2, Ident, ItemStatic, Type};
  8. fn define_percpu_impl(
  9. attrs: TokenStream,
  10. item: TokenStream,
  11. get_percpu_pointer: fn(&Ident, &Type) -> TokenStream,
  12. ) -> TokenStream {
  13. if !attrs.is_empty() {
  14. panic!("`define_percpu` attribute does not take any arguments");
  15. }
  16. let item = parse2::<ItemStatic>(item).unwrap();
  17. let vis = &item.vis;
  18. let ident = &item.ident;
  19. let ty = &item.ty;
  20. let expr = &item.expr;
  21. let is_bool = quote!(#ty).to_string().as_str() == "bool";
  22. let is_integer =
  23. ["u8", "u16", "u32", "u64", "usize"].contains(&quote!(#ty).to_string().as_str());
  24. let is_atomic_like = is_bool || is_integer || quote!(#ty).to_string().contains("NonNull");
  25. let inner_ident = format_ident!("_percpu_inner_{}", ident);
  26. let access_ident = format_ident!("_access_{}", ident);
  27. let integer_methods = if is_integer {
  28. quote! {
  29. pub fn add(&self, value: #ty) {
  30. *unsafe { self.as_mut() } += value;
  31. }
  32. pub fn sub(&self, value: #ty) {
  33. *unsafe { self.as_mut() } -= value;
  34. }
  35. }
  36. } else {
  37. quote! {}
  38. };
  39. let preempt_disable = if !is_atomic_like {
  40. quote! { eonix_preempt::disable(); }
  41. } else {
  42. quote! {}
  43. };
  44. let preempt_enable = if !is_atomic_like {
  45. quote! { eonix_preempt::enable(); }
  46. } else {
  47. quote! {}
  48. };
  49. let as_ptr = get_percpu_pointer(&inner_ident, &ty);
  50. quote! {
  51. #[link_section = ".percpu"]
  52. #[allow(non_upper_case_globals)]
  53. static mut #inner_ident: #ty = #expr;
  54. #[allow(non_camel_case_types)]
  55. #vis struct #access_ident;
  56. #vis static #ident: #access_ident = #access_ident;
  57. impl #access_ident {
  58. /// # Safety
  59. /// This function is unsafe because it allows for mutable aliasing of the percpu
  60. /// variable.
  61. /// Make sure that preempt is disabled when calling this function.
  62. pub unsafe fn as_ptr(&self) -> *mut #ty {
  63. #as_ptr
  64. }
  65. pub fn get(&self) -> #ty {
  66. #preempt_disable
  67. let value = unsafe { self.as_ptr().read() };
  68. #preempt_enable
  69. value
  70. }
  71. pub fn set(&self, value: #ty) {
  72. #preempt_disable
  73. unsafe { self.as_ptr().write(value) }
  74. #preempt_enable
  75. }
  76. pub fn swap(&self, mut value: #ty) -> #ty {
  77. #preempt_disable
  78. unsafe { self.as_ptr().swap(&mut value) }
  79. #preempt_enable
  80. value
  81. }
  82. /// # Safety
  83. /// This function is unsafe because it allows for immutable aliasing of the percpu
  84. /// variable.
  85. /// Make sure that preempt is disabled when calling this function.
  86. pub unsafe fn as_ref(&self) -> & #ty {
  87. // SAFETY: This is safe because `as_ptr()` is guaranteed to be valid.
  88. self.as_ptr().as_ref().unwrap()
  89. }
  90. /// # Safety
  91. /// This function is unsafe because it allows for mutable aliasing of the percpu
  92. /// variable.
  93. /// Make sure that preempt is disabled when calling this function.
  94. pub unsafe fn as_mut(&self) -> &mut #ty {
  95. // SAFETY: This is safe because `as_ptr()` is guaranteed to be valid.
  96. self.as_ptr().as_mut().unwrap()
  97. }
  98. #integer_methods
  99. }
  100. }
  101. .into()
  102. }
  103. fn define_percpu_shared_impl(
  104. attrs: TokenStream,
  105. item: TokenStream,
  106. get_percpu_pointer: fn(&Ident, &Type) -> TokenStream,
  107. get_percpu_offset: fn(&Ident) -> TokenStream,
  108. ) -> TokenStream {
  109. if !attrs.is_empty() {
  110. panic!("`define_percpu_shared` attribute does not take any arguments");
  111. }
  112. let item = parse2::<ItemStatic>(item).unwrap();
  113. let vis = &item.vis;
  114. let ident = &item.ident;
  115. let ty = &item.ty;
  116. let expr = &item.expr;
  117. let inner_ident = format_ident!("_percpu_shared_inner_{}", ident);
  118. let access_ident = format_ident!("_access_shared_{}", ident);
  119. let as_ptr = get_percpu_pointer(&inner_ident, &ty);
  120. let get_offset = get_percpu_offset(&inner_ident);
  121. quote! {
  122. #[link_section = ".percpu"]
  123. #[allow(non_upper_case_globals)]
  124. static #inner_ident: #ty = #expr;
  125. #[allow(non_camel_case_types)]
  126. #vis struct #access_ident;
  127. #vis static #ident: #access_ident = #access_ident;
  128. impl #access_ident {
  129. fn as_ptr(&self) -> *const #ty {
  130. unsafe { ( #as_ptr ) }
  131. }
  132. pub fn get_ref(&self) -> & #ty {
  133. // SAFETY: This is safe because `as_ptr()` is guaranteed to be valid.
  134. unsafe { self.as_ptr().as_ref().unwrap() }
  135. }
  136. pub fn get_for_cpu(&self, cpuid: usize) -> Option<& #ty > {
  137. let offset = #get_offset;
  138. let base = ::eonix_percpu::PercpuArea::get_for(cpuid);
  139. base.map(|base| unsafe { base.byte_add(offset).cast().as_ref() })
  140. }
  141. }
  142. impl ::core::ops::Deref for #access_ident {
  143. type Target = #ty;
  144. fn deref(&self) -> &Self::Target {
  145. self.get_ref()
  146. }
  147. }
  148. impl<T> ::core::convert::AsRef<T> for #access_ident
  149. where
  150. <Self as ::core::ops::Deref>::Target: ::core::convert::AsRef<T>,
  151. {
  152. fn as_ref(&self) -> &T {
  153. use ::core::ops::Deref;
  154. self.deref().as_ref()
  155. }
  156. }
  157. }
  158. }
  159. #[proc_macro_attribute]
  160. pub fn define_percpu_x86_64(
  161. attrs: proc_macro::TokenStream,
  162. item: proc_macro::TokenStream,
  163. ) -> proc_macro::TokenStream {
  164. define_percpu_impl(attrs.into(), item.into(), x86_64::get_percpu_pointer).into()
  165. }
  166. #[proc_macro_attribute]
  167. pub fn define_percpu_shared_x86_64(
  168. attrs: proc_macro::TokenStream,
  169. item: proc_macro::TokenStream,
  170. ) -> proc_macro::TokenStream {
  171. define_percpu_shared_impl(
  172. attrs.into(),
  173. item.into(),
  174. x86_64::get_percpu_pointer,
  175. x86_64::get_percpu_offset,
  176. )
  177. .into()
  178. }
  179. #[proc_macro_attribute]
  180. pub fn define_percpu_riscv64(
  181. attrs: proc_macro::TokenStream,
  182. item: proc_macro::TokenStream,
  183. ) -> proc_macro::TokenStream {
  184. define_percpu_impl(attrs.into(), item.into(), riscv64::get_percpu_pointer).into()
  185. }
  186. #[proc_macro_attribute]
  187. pub fn define_percpu_shared_riscv64(
  188. attrs: proc_macro::TokenStream,
  189. item: proc_macro::TokenStream,
  190. ) -> proc_macro::TokenStream {
  191. define_percpu_shared_impl(
  192. attrs.into(),
  193. item.into(),
  194. riscv64::get_percpu_pointer,
  195. riscv64::get_percpu_offset,
  196. )
  197. .into()
  198. }
  199. #[proc_macro_attribute]
  200. pub fn define_percpu_loongarch64(
  201. attrs: proc_macro::TokenStream,
  202. item: proc_macro::TokenStream,
  203. ) -> proc_macro::TokenStream {
  204. define_percpu_impl(attrs.into(), item.into(), loongarch64::get_percpu_pointer).into()
  205. }
  206. #[proc_macro_attribute]
  207. pub fn define_percpu_shared_loongarch64(
  208. attrs: proc_macro::TokenStream,
  209. item: proc_macro::TokenStream,
  210. ) -> proc_macro::TokenStream {
  211. define_percpu_shared_impl(
  212. attrs.into(),
  213. item.into(),
  214. loongarch64::get_percpu_pointer,
  215. loongarch64::get_percpu_offset,
  216. )
  217. .into()
  218. }