lib.rs 6.2 KB

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