lib.rs 6.8 KB

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