Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

platform/surface: aggregator: Allow enabling of events without notifiers

We can already enable and disable SAM events via one of two ways: either
via a (non-observer) notifier tied to a specific event group, or a
generic event enable/disable request. In some instances, however,
neither method may be desirable.

The first method will tie the event enable request to a specific
notifier, however, when we want to receive notifications for multiple
event groups of the same target category and forward this to the same
notifier callback, we may receive duplicate events, i.e. one event per
registered notifier. The second method will bypass the internal
reference counting mechanism, meaning that a disable request will
disable the event regardless of any other client driver using it, which
may break the functionality of that driver.

To address this problem, add new functions that allow enabling and
disabling of events via the event reference counting mechanism built
into the controller, without needing to register a notifier.

This can then be used in combination with observer notifiers to process
multiple events of the same target category without duplication in the
same callback function.

Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>
Link: https://lore.kernel.org/r/20210604134755.535590-3-luzmaximilian@gmail.com
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>

authored by

Maximilian Luz and committed by
Hans de Goede
4b38a1dc 0e8512fa

+253 -48
+245 -48
drivers/platform/surface/aggregator/controller.c
··· 408 408 } 409 409 410 410 /** 411 + * ssam_nf_refcount_dec_free() - Decrement reference-/activation-count of the 412 + * given event and free its entry if the reference count reaches zero. 413 + * @nf: The notifier system reference. 414 + * @reg: The registry used to enable/disable the event. 415 + * @id: The event ID. 416 + * 417 + * Decrements the reference-/activation-count of the specified event, freeing 418 + * its entry if it reaches zero. 419 + * 420 + * Note: ``nf->lock`` must be held when calling this function. 421 + */ 422 + static void ssam_nf_refcount_dec_free(struct ssam_nf *nf, 423 + struct ssam_event_registry reg, 424 + struct ssam_event_id id) 425 + { 426 + struct ssam_nf_refcount_entry *entry; 427 + 428 + lockdep_assert_held(&nf->lock); 429 + 430 + entry = ssam_nf_refcount_dec(nf, reg, id); 431 + if (entry && entry->refcount == 0) 432 + kfree(entry); 433 + } 434 + 435 + /** 411 436 * ssam_nf_refcount_empty() - Test if the notification system has any 412 437 * enabled/active events. 413 438 * @nf: The notification system. ··· 2148 2123 /* -- Top-level event registry interface. ----------------------------------- */ 2149 2124 2150 2125 /** 2126 + * ssam_nf_refcount_enable() - Enable event for reference count entry if it has 2127 + * not already been enabled. 2128 + * @ctrl: The controller to enable the event on. 2129 + * @entry: The reference count entry for the event to be enabled. 2130 + * @flags: The flags used for enabling the event on the EC. 2131 + * 2132 + * Enable the event associated with the given reference count entry if the 2133 + * reference count equals one, i.e. the event has not previously been enabled. 2134 + * If the event has already been enabled (i.e. reference count not equal to 2135 + * one), check that the flags used for enabling match and warn about this if 2136 + * they do not. 2137 + * 2138 + * This does not modify the reference count itself, which is done with 2139 + * ssam_nf_refcount_inc() / ssam_nf_refcount_dec(). 2140 + * 2141 + * Note: ``nf->lock`` must be held when calling this function. 2142 + * 2143 + * Return: Returns zero on success. If the event is enabled by this call, 2144 + * returns the status of the event-enable EC command. 2145 + */ 2146 + static int ssam_nf_refcount_enable(struct ssam_controller *ctrl, 2147 + struct ssam_nf_refcount_entry *entry, u8 flags) 2148 + { 2149 + const struct ssam_event_registry reg = entry->key.reg; 2150 + const struct ssam_event_id id = entry->key.id; 2151 + struct ssam_nf *nf = &ctrl->cplt.event.notif; 2152 + int status; 2153 + 2154 + lockdep_assert_held(&nf->lock); 2155 + 2156 + ssam_dbg(ctrl, "enabling event (reg: %#04x, tc: %#04x, iid: %#04x, rc: %d)\n", 2157 + reg.target_category, id.target_category, id.instance, entry->refcount); 2158 + 2159 + if (entry->refcount == 1) { 2160 + status = ssam_ssh_event_enable(ctrl, reg, id, flags); 2161 + if (status) 2162 + return status; 2163 + 2164 + entry->flags = flags; 2165 + 2166 + } else if (entry->flags != flags) { 2167 + ssam_warn(ctrl, 2168 + "inconsistent flags when enabling event: got %#04x, expected %#04x (reg: %#04x, tc: %#04x, iid: %#04x)\n", 2169 + flags, entry->flags, reg.target_category, id.target_category, 2170 + id.instance); 2171 + } 2172 + 2173 + return 0; 2174 + } 2175 + 2176 + /** 2177 + * ssam_nf_refcount_disable_free() - Disable event for reference count entry if it is 2178 + * no longer in use and free the corresponding entry. 2179 + * @ctrl: The controller to disable the event on. 2180 + * @entry: The reference count entry for the event to be disabled. 2181 + * @flags: The flags used for enabling the event on the EC. 2182 + * 2183 + * If the reference count equals zero, i.e. the event is no longer requested by 2184 + * any client, the event will be disabled and the corresponding reference count 2185 + * entry freed. The reference count entry must not be used any more after a 2186 + * call to this function. 2187 + * 2188 + * Also checks if the flags used for disabling the event match the flags used 2189 + * for enabling the event and warns if they do not (regardless of reference 2190 + * count). 2191 + * 2192 + * This does not modify the reference count itself, which is done with 2193 + * ssam_nf_refcount_inc() / ssam_nf_refcount_dec(). 2194 + * 2195 + * Note: ``nf->lock`` must be held when calling this function. 2196 + * 2197 + * Return: Returns zero on success. If the event is disabled by this call, 2198 + * returns the status of the event-enable EC command. 2199 + */ 2200 + static int ssam_nf_refcount_disable_free(struct ssam_controller *ctrl, 2201 + struct ssam_nf_refcount_entry *entry, u8 flags) 2202 + { 2203 + const struct ssam_event_registry reg = entry->key.reg; 2204 + const struct ssam_event_id id = entry->key.id; 2205 + struct ssam_nf *nf = &ctrl->cplt.event.notif; 2206 + int status; 2207 + 2208 + lockdep_assert_held(&nf->lock); 2209 + 2210 + ssam_dbg(ctrl, "disabling event (reg: %#04x, tc: %#04x, iid: %#04x, rc: %d)\n", 2211 + reg.target_category, id.target_category, id.instance, entry->refcount); 2212 + 2213 + if (entry->flags != flags) { 2214 + ssam_warn(ctrl, 2215 + "inconsistent flags when disabling event: got %#04x, expected %#04x (reg: %#04x, tc: %#04x, iid: %#04x)\n", 2216 + flags, entry->flags, reg.target_category, id.target_category, 2217 + id.instance); 2218 + } 2219 + 2220 + if (entry->refcount == 0) { 2221 + status = ssam_ssh_event_disable(ctrl, reg, id, flags); 2222 + kfree(entry); 2223 + } 2224 + 2225 + return status; 2226 + } 2227 + 2228 + /** 2151 2229 * ssam_notifier_register() - Register an event notifier. 2152 2230 * @ctrl: The controller to register the notifier on. 2153 2231 * @n: The event notifier to register. ··· 2294 2166 mutex_unlock(&nf->lock); 2295 2167 return PTR_ERR(entry); 2296 2168 } 2297 - 2298 - ssam_dbg(ctrl, "enabling event (reg: %#04x, tc: %#04x, iid: %#04x, rc: %d)\n", 2299 - n->event.reg.target_category, n->event.id.target_category, 2300 - n->event.id.instance, entry->refcount); 2301 2169 } 2302 2170 2303 2171 status = ssam_nfblk_insert(nf_head, &n->base); 2304 2172 if (status) { 2305 - if (entry) { 2306 - entry = ssam_nf_refcount_dec(nf, n->event.reg, n->event.id); 2307 - if (entry->refcount == 0) 2308 - kfree(entry); 2309 - } 2173 + if (entry) 2174 + ssam_nf_refcount_dec_free(nf, n->event.reg, n->event.id); 2310 2175 2311 2176 mutex_unlock(&nf->lock); 2312 2177 return status; 2313 2178 } 2314 2179 2315 - if (entry && entry->refcount == 1) { 2316 - status = ssam_ssh_event_enable(ctrl, n->event.reg, n->event.id, n->event.flags); 2180 + if (entry) { 2181 + status = ssam_nf_refcount_enable(ctrl, entry, n->event.flags); 2317 2182 if (status) { 2318 2183 ssam_nfblk_remove(&n->base); 2319 - kfree(ssam_nf_refcount_dec(nf, n->event.reg, n->event.id)); 2184 + ssam_nf_refcount_dec_free(nf, n->event.reg, n->event.id); 2320 2185 mutex_unlock(&nf->lock); 2321 2186 synchronize_srcu(&nf_head->srcu); 2322 2187 return status; 2323 2188 } 2324 - 2325 - entry->flags = n->event.flags; 2326 - 2327 - } else if (entry && entry->flags != n->event.flags) { 2328 - ssam_warn(ctrl, 2329 - "inconsistent flags when enabling event: got %#04x, expected %#04x (reg: %#04x, tc: %#04x, iid: %#04x)\n", 2330 - n->event.flags, entry->flags, n->event.reg.target_category, 2331 - n->event.id.target_category, n->event.id.instance); 2332 2189 } 2333 2190 2334 2191 mutex_unlock(&nf->lock); ··· 2360 2247 * If this is an observer notifier, do not attempt to disable the 2361 2248 * event, just remove it. 2362 2249 */ 2363 - if (n->flags & SSAM_EVENT_NOTIFIER_OBSERVER) 2364 - goto remove; 2250 + if (!(n->flags & SSAM_EVENT_NOTIFIER_OBSERVER)) { 2251 + entry = ssam_nf_refcount_dec(nf, n->event.reg, n->event.id); 2252 + if (WARN_ON(!entry)) { 2253 + /* 2254 + * If this does not return an entry, there's a logic 2255 + * error somewhere: The notifier block is registered, 2256 + * but the event refcount entry is not there. Remove 2257 + * the notifier block anyways. 2258 + */ 2259 + status = -ENOENT; 2260 + goto remove; 2261 + } 2365 2262 2366 - entry = ssam_nf_refcount_dec(nf, n->event.reg, n->event.id); 2367 - if (WARN_ON(!entry)) { 2368 - /* 2369 - * If this does not return an entry, there's a logic error 2370 - * somewhere: The notifier block is registered, but the event 2371 - * refcount entry is not there. Remove the notifier block 2372 - * anyways. 2373 - */ 2374 - status = -ENOENT; 2375 - goto remove; 2376 - } 2377 - 2378 - ssam_dbg(ctrl, "disabling event (reg: %#04x, tc: %#04x, iid: %#04x, rc: %d)\n", 2379 - n->event.reg.target_category, n->event.id.target_category, 2380 - n->event.id.instance, entry->refcount); 2381 - 2382 - if (entry->flags != n->event.flags) { 2383 - ssam_warn(ctrl, 2384 - "inconsistent flags when disabling event: got %#04x, expected %#04x (reg: %#04x, tc: %#04x, iid: %#04x)\n", 2385 - n->event.flags, entry->flags, n->event.reg.target_category, 2386 - n->event.id.target_category, n->event.id.instance); 2387 - } 2388 - 2389 - if (entry->refcount == 0) { 2390 - status = ssam_ssh_event_disable(ctrl, n->event.reg, n->event.id, n->event.flags); 2391 - kfree(entry); 2263 + status = ssam_nf_refcount_disable_free(ctrl, entry, n->event.flags); 2392 2264 } 2393 2265 2394 2266 remove: ··· 2384 2286 return status; 2385 2287 } 2386 2288 EXPORT_SYMBOL_GPL(ssam_notifier_unregister); 2289 + 2290 + /** 2291 + * ssam_controller_event_enable() - Enable the specified event. 2292 + * @ctrl: The controller to enable the event for. 2293 + * @reg: The event registry to use for enabling the event. 2294 + * @id: The event ID specifying the event to be enabled. 2295 + * @flags: The SAM event flags used for enabling the event. 2296 + * 2297 + * Increment the event reference count of the specified event. If the event has 2298 + * not been enabled previously, it will be enabled by this call. 2299 + * 2300 + * Note: In general, ssam_notifier_register() with a non-observer notifier 2301 + * should be preferred for enabling/disabling events, as this will guarantee 2302 + * proper ordering and event forwarding in case of errors during event 2303 + * enabling/disabling. 2304 + * 2305 + * Return: Returns zero on success, %-ENOSPC if the reference count for the 2306 + * specified event has reached its maximum, %-ENOMEM if the corresponding event 2307 + * entry could not be allocated. If this is the first time that this event has 2308 + * been enabled (i.e. the reference count was incremented from zero to one by 2309 + * this call), returns the status of the event-enable EC-command. 2310 + */ 2311 + int ssam_controller_event_enable(struct ssam_controller *ctrl, 2312 + struct ssam_event_registry reg, 2313 + struct ssam_event_id id, u8 flags) 2314 + { 2315 + u16 rqid = ssh_tc_to_rqid(id.target_category); 2316 + struct ssam_nf *nf = &ctrl->cplt.event.notif; 2317 + struct ssam_nf_refcount_entry *entry; 2318 + int status; 2319 + 2320 + if (!ssh_rqid_is_event(rqid)) 2321 + return -EINVAL; 2322 + 2323 + mutex_lock(&nf->lock); 2324 + 2325 + entry = ssam_nf_refcount_inc(nf, reg, id); 2326 + if (IS_ERR(entry)) { 2327 + mutex_unlock(&nf->lock); 2328 + return PTR_ERR(entry); 2329 + } 2330 + 2331 + status = ssam_nf_refcount_enable(ctrl, entry, flags); 2332 + if (status) { 2333 + ssam_nf_refcount_dec_free(nf, reg, id); 2334 + mutex_unlock(&nf->lock); 2335 + return status; 2336 + } 2337 + 2338 + mutex_unlock(&nf->lock); 2339 + return 0; 2340 + } 2341 + EXPORT_SYMBOL_GPL(ssam_controller_event_enable); 2342 + 2343 + /** 2344 + * ssam_controller_event_disable() - Disable the specified event. 2345 + * @ctrl: The controller to disable the event for. 2346 + * @reg: The event registry to use for disabling the event. 2347 + * @id: The event ID specifying the event to be disabled. 2348 + * @flags: The flags used when enabling the event. 2349 + * 2350 + * Decrement the reference count of the specified event. If the reference count 2351 + * reaches zero, the event will be disabled. 2352 + * 2353 + * Note: In general, ssam_notifier_register()/ssam_notifier_unregister() with a 2354 + * non-observer notifier should be preferred for enabling/disabling events, as 2355 + * this will guarantee proper ordering and event forwarding in case of errors 2356 + * during event enabling/disabling. 2357 + * 2358 + * Return: Returns zero on success, %-ENOENT if the given event has not been 2359 + * enabled on the controller. If the reference count of the event reaches zero 2360 + * during this call, returns the status of the event-disable EC-command. 2361 + */ 2362 + int ssam_controller_event_disable(struct ssam_controller *ctrl, 2363 + struct ssam_event_registry reg, 2364 + struct ssam_event_id id, u8 flags) 2365 + { 2366 + u16 rqid = ssh_tc_to_rqid(id.target_category); 2367 + struct ssam_nf *nf = &ctrl->cplt.event.notif; 2368 + struct ssam_nf_refcount_entry *entry; 2369 + int status = 0; 2370 + 2371 + if (!ssh_rqid_is_event(rqid)) 2372 + return -EINVAL; 2373 + 2374 + mutex_lock(&nf->lock); 2375 + 2376 + entry = ssam_nf_refcount_dec(nf, reg, id); 2377 + if (!entry) { 2378 + mutex_unlock(&nf->lock); 2379 + return -ENOENT; 2380 + } 2381 + 2382 + status = ssam_nf_refcount_disable_free(ctrl, entry, flags); 2383 + 2384 + mutex_unlock(&nf->lock); 2385 + return status; 2386 + } 2387 + EXPORT_SYMBOL_GPL(ssam_controller_event_disable); 2387 2388 2388 2389 /** 2389 2390 * ssam_notifier_disable_registered() - Disable events for all registered
+8
include/linux/surface_aggregator/controller.h
··· 838 838 int ssam_notifier_unregister(struct ssam_controller *ctrl, 839 839 struct ssam_event_notifier *n); 840 840 841 + int ssam_controller_event_enable(struct ssam_controller *ctrl, 842 + struct ssam_event_registry reg, 843 + struct ssam_event_id id, u8 flags); 844 + 845 + int ssam_controller_event_disable(struct ssam_controller *ctrl, 846 + struct ssam_event_registry reg, 847 + struct ssam_event_id id, u8 flags); 848 + 841 849 #endif /* _LINUX_SURFACE_AGGREGATOR_CONTROLLER_H */