@@ -10,15 +10,19 @@ use crate::{
10
10
bindings,
11
11
device:: Device ,
12
12
error:: { Error , Result } ,
13
+ ffi:: c_void,
13
14
prelude:: * ,
14
15
revocable:: Revocable ,
15
16
sync:: Arc ,
17
+ types:: ARef ,
16
18
} ;
17
19
18
20
use core:: ops:: Deref ;
19
21
20
22
#[ pin_data]
21
23
struct DevresInner < T > {
24
+ dev : ARef < Device > ,
25
+ callback : unsafe extern "C" fn ( * mut c_void ) ,
22
26
#[ pin]
23
27
data : Revocable < T > ,
24
28
}
@@ -98,6 +102,8 @@ impl<T> DevresInner<T> {
98
102
fn new ( dev : & Device , data : T , flags : Flags ) -> Result < Arc < DevresInner < T > > > {
99
103
let inner = Arc :: pin_init (
100
104
pin_init ! ( DevresInner {
105
+ dev: dev. into( ) ,
106
+ callback: Self :: devres_callback,
101
107
data <- Revocable :: new( data) ,
102
108
} ) ,
103
109
flags,
@@ -109,9 +115,8 @@ impl<T> DevresInner<T> {
109
115
110
116
// SAFETY: `devm_add_action` guarantees to call `Self::devres_callback` once `dev` is
111
117
// detached.
112
- let ret = unsafe {
113
- bindings:: devm_add_action ( dev. as_raw ( ) , Some ( Self :: devres_callback) , data as _ )
114
- } ;
118
+ let ret =
119
+ unsafe { bindings:: devm_add_action ( dev. as_raw ( ) , Some ( inner. callback ) , data as _ ) } ;
115
120
116
121
if ret != 0 {
117
122
// SAFETY: We just created another reference to `inner` in order to pass it to
@@ -124,6 +129,32 @@ impl<T> DevresInner<T> {
124
129
Ok ( inner)
125
130
}
126
131
132
+ fn as_ptr ( & self ) -> * const Self {
133
+ self as _
134
+ }
135
+
136
+ fn remove_action ( this : & Arc < Self > ) {
137
+ // SAFETY:
138
+ // - `self.inner.dev` is a valid `Device`,
139
+ // - the `action` and `data` pointers are the exact same ones as given to devm_add_action()
140
+ // previously,
141
+ // - `self` is always valid, even if the action has been released already.
142
+ let ret = unsafe {
143
+ bindings:: devm_remove_action_nowarn (
144
+ this. dev . as_raw ( ) ,
145
+ Some ( this. callback ) ,
146
+ this. as_ptr ( ) as _ ,
147
+ )
148
+ } ;
149
+
150
+ if ret == 0 {
151
+ // SAFETY: We leaked an `Arc` reference to devm_add_action() in `DevresInner::new`; if
152
+ // devm_remove_action_nowarn() was successful we can (and have to) claim back ownership
153
+ // of this reference.
154
+ let _ = unsafe { Arc :: from_raw ( this. as_ptr ( ) ) } ;
155
+ }
156
+ }
157
+
127
158
#[ allow( clippy:: missing_safety_doc) ]
128
159
unsafe extern "C" fn devres_callback ( ptr : * mut kernel:: ffi:: c_void ) {
129
160
let ptr = ptr as * mut DevresInner < T > ;
@@ -165,14 +196,6 @@ impl<T> Deref for Devres<T> {
165
196
166
197
impl < T > Drop for Devres < T > {
167
198
fn drop ( & mut self ) {
168
- // Revoke the data, such that it gets dropped already and the actual resource is freed.
169
- //
170
- // `DevresInner` has to stay alive until the devres callback has been called. This is
171
- // necessary since we don't know when `Devres` is dropped and calling
172
- // `devm_remove_action()` instead could race with `devres_release_all()`.
173
- //
174
- // SAFETY: When `drop` runs, it's guaranteed that nobody is accessing the revocable data
175
- // anymore, hence it is safe not to wait for the grace period to finish.
176
- unsafe { self . revoke_nosync ( ) } ;
199
+ DevresInner :: remove_action ( & self . 0 ) ;
177
200
}
178
201
}
0 commit comments