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

media: cec-adap.c: drop activate_cnt, use state info instead

Using an activation counter to decide when the enable or disable the
cec adapter is not the best approach and can lead to race conditions.

Change this to determining the current status of the adapter, and
enable or disable the adapter accordingly.

It now only needs to be called whenever there is a chance that the
state changes, and it can handle enabling/disabling monitoring as
well if needed.

This simplifies the code and it should be a more robust approach as well.

Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>

authored by

Hans Verkuil and committed by
Mauro Carvalho Chehab
f9222f8c e3891b36

+61 -95
+59 -93
drivers/media/cec/core/cec-adap.c
··· 1574 1574 } 1575 1575 1576 1576 /* 1577 - * Helper functions to enable/disable the CEC adapter. 1577 + * Helper function to enable/disable the CEC adapter. 1578 1578 * 1579 - * These functions are called with adap->lock held. 1579 + * This function is called with adap->lock held. 1580 1580 */ 1581 - static int cec_activate_cnt_inc(struct cec_adapter *adap) 1581 + static int cec_adap_enable(struct cec_adapter *adap) 1582 1582 { 1583 - int ret; 1583 + bool enable; 1584 + int ret = 0; 1584 1585 1585 - if (adap->activate_cnt++) 1586 + enable = adap->monitor_all_cnt || adap->monitor_pin_cnt || 1587 + adap->log_addrs.num_log_addrs; 1588 + if (adap->needs_hpd) 1589 + enable = enable && adap->phys_addr != CEC_PHYS_ADDR_INVALID; 1590 + 1591 + if (enable == adap->is_enabled) 1586 1592 return 0; 1587 1593 1588 1594 /* serialize adap_enable */ 1589 1595 mutex_lock(&adap->devnode.lock); 1590 - adap->last_initiator = 0xff; 1591 - adap->transmit_in_progress = false; 1592 - ret = call_op(adap, adap_enable, true); 1593 - if (ret) 1594 - adap->activate_cnt--; 1596 + if (enable) { 1597 + adap->last_initiator = 0xff; 1598 + adap->transmit_in_progress = false; 1599 + ret = adap->ops->adap_enable(adap, true); 1600 + if (!ret) { 1601 + /* 1602 + * Enable monitor-all/pin modes if needed. We warn, but 1603 + * continue if this fails as this is not a critical error. 1604 + */ 1605 + if (adap->monitor_all_cnt) 1606 + WARN_ON(call_op(adap, adap_monitor_all_enable, true)); 1607 + if (adap->monitor_pin_cnt) 1608 + WARN_ON(call_op(adap, adap_monitor_pin_enable, true)); 1609 + } 1610 + } else { 1611 + /* Disable monitor-all/pin modes if needed (needs_hpd == 1) */ 1612 + if (adap->monitor_all_cnt) 1613 + WARN_ON(call_op(adap, adap_monitor_all_enable, false)); 1614 + if (adap->monitor_pin_cnt) 1615 + WARN_ON(call_op(adap, adap_monitor_pin_enable, false)); 1616 + WARN_ON(adap->ops->adap_enable(adap, false)); 1617 + adap->last_initiator = 0xff; 1618 + adap->transmit_in_progress = false; 1619 + adap->transmit_in_progress_aborted = false; 1620 + if (adap->transmitting) 1621 + cec_data_cancel(adap->transmitting, CEC_TX_STATUS_ABORTED, 0); 1622 + } 1623 + if (!ret) 1624 + adap->is_enabled = enable; 1625 + wake_up_interruptible(&adap->kthread_waitq); 1595 1626 mutex_unlock(&adap->devnode.lock); 1596 1627 return ret; 1597 - } 1598 - 1599 - static void cec_activate_cnt_dec(struct cec_adapter *adap) 1600 - { 1601 - if (WARN_ON(!adap->activate_cnt)) 1602 - return; 1603 - 1604 - if (--adap->activate_cnt) 1605 - return; 1606 - 1607 - /* serialize adap_enable */ 1608 - mutex_lock(&adap->devnode.lock); 1609 - WARN_ON(call_op(adap, adap_enable, false)); 1610 - adap->last_initiator = 0xff; 1611 - adap->transmit_in_progress = false; 1612 - adap->transmit_in_progress_aborted = false; 1613 - if (adap->transmitting) 1614 - cec_data_cancel(adap->transmitting, CEC_TX_STATUS_ABORTED, 0); 1615 - mutex_unlock(&adap->devnode.lock); 1616 1628 } 1617 1629 1618 1630 /* Set a new physical address and send an event notifying userspace of this. ··· 1647 1635 adap->phys_addr = CEC_PHYS_ADDR_INVALID; 1648 1636 cec_post_state_event(adap); 1649 1637 cec_adap_unconfigure(adap); 1650 - if (becomes_invalid && adap->needs_hpd) { 1651 - /* Disable monitor-all/pin modes if needed */ 1652 - if (adap->monitor_all_cnt) 1653 - WARN_ON(call_op(adap, adap_monitor_all_enable, false)); 1654 - if (adap->monitor_pin_cnt) 1655 - WARN_ON(call_op(adap, adap_monitor_pin_enable, false)); 1656 - cec_activate_cnt_dec(adap); 1657 - wake_up_interruptible(&adap->kthread_waitq); 1638 + if (becomes_invalid) { 1639 + cec_adap_enable(adap); 1640 + return; 1658 1641 } 1659 - if (becomes_invalid) 1660 - return; 1661 - } 1662 - 1663 - if (is_invalid && adap->needs_hpd) { 1664 - if (cec_activate_cnt_inc(adap)) 1665 - return; 1666 - /* 1667 - * Re-enable monitor-all/pin modes if needed. We warn, but 1668 - * continue if this fails as this is not a critical error. 1669 - */ 1670 - if (adap->monitor_all_cnt) 1671 - WARN_ON(call_op(adap, adap_monitor_all_enable, true)); 1672 - if (adap->monitor_pin_cnt) 1673 - WARN_ON(call_op(adap, adap_monitor_pin_enable, true)); 1674 1642 } 1675 1643 1676 1644 adap->phys_addr = phys_addr; 1645 + if (is_invalid) 1646 + cec_adap_enable(adap); 1647 + 1677 1648 cec_post_state_event(adap); 1678 1649 if (!adap->log_addrs.num_log_addrs) 1679 1650 return; ··· 1717 1722 struct cec_log_addrs *log_addrs, bool block) 1718 1723 { 1719 1724 u16 type_mask = 0; 1725 + int err; 1720 1726 int i; 1721 1727 1722 1728 if (adap->devnode.unregistered) ··· 1734 1738 adap->log_addrs.osd_name[0] = '\0'; 1735 1739 adap->log_addrs.vendor_id = CEC_VENDOR_ID_NONE; 1736 1740 adap->log_addrs.cec_version = CEC_OP_CEC_VERSION_2_0; 1737 - if (!adap->needs_hpd) 1738 - cec_activate_cnt_dec(adap); 1741 + cec_adap_enable(adap); 1739 1742 return 0; 1740 1743 } 1741 1744 ··· 1868 1873 sizeof(log_addrs->features[i])); 1869 1874 } 1870 1875 1871 - if (!adap->needs_hpd && !adap->is_configuring && !adap->is_configured) { 1872 - int ret = cec_activate_cnt_inc(adap); 1873 - 1874 - if (ret) 1875 - return ret; 1876 - } 1877 1876 log_addrs->log_addr_mask = adap->log_addrs.log_addr_mask; 1878 1877 adap->log_addrs = *log_addrs; 1879 - if (adap->phys_addr != CEC_PHYS_ADDR_INVALID) 1878 + err = cec_adap_enable(adap); 1879 + if (!err && adap->phys_addr != CEC_PHYS_ADDR_INVALID) 1880 1880 cec_claim_log_addrs(adap, block); 1881 - return 0; 1881 + return err; 1882 1882 } 1883 1883 1884 1884 int cec_s_log_addrs(struct cec_adapter *adap, ··· 2176 2186 if (adap->monitor_all_cnt++) 2177 2187 return 0; 2178 2188 2179 - if (!adap->needs_hpd) { 2180 - ret = cec_activate_cnt_inc(adap); 2181 - if (ret) { 2182 - adap->monitor_all_cnt--; 2183 - return ret; 2184 - } 2185 - } 2186 - 2187 - ret = call_op(adap, adap_monitor_all_enable, true); 2188 - if (ret) { 2189 + ret = cec_adap_enable(adap); 2190 + if (ret) 2189 2191 adap->monitor_all_cnt--; 2190 - if (!adap->needs_hpd) 2191 - cec_activate_cnt_dec(adap); 2192 - } 2193 2192 return ret; 2194 2193 } 2195 2194 ··· 2189 2210 if (--adap->monitor_all_cnt) 2190 2211 return; 2191 2212 WARN_ON(call_op(adap, adap_monitor_all_enable, false)); 2192 - if (!adap->needs_hpd) 2193 - cec_activate_cnt_dec(adap); 2213 + cec_adap_enable(adap); 2194 2214 } 2195 2215 2196 2216 /* ··· 2204 2226 if (adap->monitor_pin_cnt++) 2205 2227 return 0; 2206 2228 2207 - if (!adap->needs_hpd) { 2208 - ret = cec_activate_cnt_inc(adap); 2209 - if (ret) { 2210 - adap->monitor_pin_cnt--; 2211 - return ret; 2212 - } 2213 - } 2214 - 2215 - ret = call_op(adap, adap_monitor_pin_enable, true); 2216 - if (ret) { 2229 + ret = cec_adap_enable(adap); 2230 + if (ret) 2217 2231 adap->monitor_pin_cnt--; 2218 - if (!adap->needs_hpd) 2219 - cec_activate_cnt_dec(adap); 2220 - } 2221 2232 return ret; 2222 2233 } 2223 2234 ··· 2217 2250 if (--adap->monitor_pin_cnt) 2218 2251 return; 2219 2252 WARN_ON(call_op(adap, adap_monitor_pin_enable, false)); 2220 - if (!adap->needs_hpd) 2221 - cec_activate_cnt_dec(adap); 2253 + cec_adap_enable(adap); 2222 2254 } 2223 2255 2224 2256 #ifdef CONFIG_DEBUG_FS ··· 2231 2265 struct cec_data *data; 2232 2266 2233 2267 mutex_lock(&adap->lock); 2234 - seq_printf(file, "activation count: %u\n", adap->activate_cnt); 2268 + seq_printf(file, "enabled: %d\n", adap->is_enabled); 2235 2269 seq_printf(file, "configured: %d\n", adap->is_configured); 2236 2270 seq_printf(file, "configuring: %d\n", adap->is_configuring); 2237 2271 seq_printf(file, "phys_addr: %x.%x.%x.%x\n",
+2 -2
include/media/cec.h
··· 183 183 * @needs_hpd: if true, then the HDMI HotPlug Detect pin must be high 184 184 * in order to transmit or receive CEC messages. This is usually a HW 185 185 * limitation. 186 + * @is_enabled: the CEC adapter is enabled 186 187 * @is_configuring: the CEC adapter is configuring (i.e. claiming LAs) 187 188 * @must_reconfigure: while configuring, the PA changed, so reclaim LAs 188 189 * @is_configured: the CEC adapter is configured (i.e. has claimed LAs) ··· 195 194 * Drivers that need this can set this field to true after the 196 195 * cec_allocate_adapter() call. 197 196 * @last_initiator: the initiator of the last transmitted message. 198 - * @activate_cnt: number of times that CEC is activated 199 197 * @monitor_all_cnt: number of filehandles monitoring all msgs 200 198 * @monitor_pin_cnt: number of filehandles monitoring pin changes 201 199 * @follower_cnt: number of filehandles in follower mode ··· 243 243 244 244 u16 phys_addr; 245 245 bool needs_hpd; 246 + bool is_enabled; 246 247 bool is_configuring; 247 248 bool must_reconfigure; 248 249 bool is_configured; 249 250 bool cec_pin_is_high; 250 251 bool adap_controls_phys_addr; 251 252 u8 last_initiator; 252 - u32 activate_cnt; 253 253 u32 monitor_all_cnt; 254 254 u32 monitor_pin_cnt; 255 255 u32 follower_cnt;