lib.rs 5.5 KB

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