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

[media] cec: fix race between configuring and unconfiguring

This race was discovered by running cec-compliance -A with the cec module debug
parameter set to 2: suddenly the test would fail.

It turns out that this happens when the test configures the adapter in
non-blocking mode, then it waits for the CEC_EVENT_STATE_CHANGE event and once
the event is received it unconfigures the adapter.

What happened was that the unconfigure was executed while the configure was
still transmitting the Report Features and Report Physical Address messages.
This messed up the internal state of the cec_adapter.

The fix is to transmit those messages with the adap->lock mutex held (this will
just queue them up in the internal transmit queue, and not actually transmit
anything yet). Only unlock the mutex once everything is done. The main thread
will dequeue the messages from the internal transmit queue and transmit them
one by one, unless an unconfigure was done, and in that case any messages are
just dropped.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>

authored by

Hans Verkuil and committed by
Mauro Carvalho Chehab
f60f3560 d3d64bc7

+13 -5
+13 -5
drivers/media/cec/cec-adap.c
··· 1256 1256 adap->is_configured = true; 1257 1257 adap->is_configuring = false; 1258 1258 cec_post_state_event(adap); 1259 - mutex_unlock(&adap->lock); 1260 1259 1260 + /* 1261 + * Now post the Report Features and Report Physical Address broadcast 1262 + * messages. Note that these are non-blocking transmits, meaning that 1263 + * they are just queued up and once adap->lock is unlocked the main 1264 + * thread will kick in and start transmitting these. 1265 + * 1266 + * If after this function is done (but before one or more of these 1267 + * messages are actually transmitted) the CEC adapter is unconfigured, 1268 + * then any remaining messages will be dropped by the main thread. 1269 + */ 1261 1270 for (i = 0; i < las->num_log_addrs; i++) { 1262 1271 struct cec_msg msg = {}; 1263 1272 ··· 1280 1271 if (las->log_addr[i] != CEC_LOG_ADDR_UNREGISTERED && 1281 1272 adap->log_addrs.cec_version >= CEC_OP_CEC_VERSION_2_0) { 1282 1273 cec_fill_msg_report_features(adap, &msg, i); 1283 - cec_transmit_msg(adap, &msg, false); 1274 + cec_transmit_msg_fh(adap, &msg, NULL, false); 1284 1275 } 1285 1276 1286 1277 /* Report Physical Address */ ··· 1289 1280 dprintk(2, "config: la %d pa %x.%x.%x.%x\n", 1290 1281 las->log_addr[i], 1291 1282 cec_phys_addr_exp(adap->phys_addr)); 1292 - cec_transmit_msg(adap, &msg, false); 1283 + cec_transmit_msg_fh(adap, &msg, NULL, false); 1293 1284 } 1294 - mutex_lock(&adap->lock); 1295 1285 adap->kthread_config = NULL; 1296 - mutex_unlock(&adap->lock); 1297 1286 complete(&adap->config_completion); 1287 + mutex_unlock(&adap->lock); 1298 1288 return 0; 1299 1289 1300 1290 unconfigure: