diff options
author | Linux Build Service Account <lnxbuild@localhost> | 2017-02-10 06:45:53 -0800 |
---|---|---|
committer | Gerrit - the friendly Code Review server <code-review@localhost> | 2017-02-10 06:45:52 -0800 |
commit | ddb5ca1fc605c0d11ca838a399fe880328dc4dcb (patch) | |
tree | 2f8bbd8a0d19dde11f99b21b659528c0edd74152 /drivers/net/ethernet | |
parent | 1dc780eb62849f2167ff353ca703e284fe16c511 (diff) | |
parent | 219214e306ca4c8c64dbb6c3d6ed4ee2c3fb2e27 (diff) |
Merge "msm: rndis_ipa: Fix to incorrect state transition"
Diffstat (limited to 'drivers/net/ethernet')
-rw-r--r-- | drivers/net/ethernet/msm/rndis_ipa.c | 63 |
1 files changed, 62 insertions, 1 deletions
diff --git a/drivers/net/ethernet/msm/rndis_ipa.c b/drivers/net/ethernet/msm/rndis_ipa.c index 78ec72dd3442..683f364ed27b 100644 --- a/drivers/net/ethernet/msm/rndis_ipa.c +++ b/drivers/net/ethernet/msm/rndis_ipa.c @@ -170,6 +170,7 @@ enum rndis_ipa_operation { * This callback shall be called by the Netdev once the Netdev internal * state is changed to RNDIS_IPA_CONNECTED_AND_UP * @xmit_error_delayed_work: work item for cases where IPA driver Tx fails + * @state_lock: used to protect the state variable. */ struct rndis_ipa_dev { struct net_device *net; @@ -197,6 +198,7 @@ struct rndis_ipa_dev { u8 device_ethaddr[ETH_ALEN]; void (*device_ready_notify)(void); struct delayed_work xmit_error_delayed_work; + spinlock_t state_lock; /* Spinlock for the state variable.*/ }; /** @@ -504,6 +506,8 @@ int rndis_ipa_init(struct ipa_usb_init_params *params) memset(rndis_ipa_ctx, 0, sizeof(*rndis_ipa_ctx)); RNDIS_IPA_DEBUG("rndis_ipa_ctx (private)=%p\n", rndis_ipa_ctx); + spin_lock_init(&rndis_ipa_ctx->state_lock); + rndis_ipa_ctx->net = net; rndis_ipa_ctx->tx_filter = false; rndis_ipa_ctx->rx_filter = false; @@ -643,6 +647,7 @@ int rndis_ipa_pipe_connect_notify(u32 usb_to_ipa_hdl, struct rndis_ipa_dev *rndis_ipa_ctx = private; int next_state; int result; + unsigned long flags; RNDIS_IPA_LOG_ENTRY(); @@ -656,12 +661,15 @@ int rndis_ipa_pipe_connect_notify(u32 usb_to_ipa_hdl, RNDIS_IPA_DEBUG("max_xfer_sz_to_host=%d\n", max_xfer_size_bytes_to_host); + spin_lock_irqsave(&rndis_ipa_ctx->state_lock, flags); next_state = rndis_ipa_next_state(rndis_ipa_ctx->state, RNDIS_IPA_CONNECT); if (next_state == RNDIS_IPA_INVALID) { + spin_unlock_irqrestore(&rndis_ipa_ctx->state_lock, flags); RNDIS_IPA_ERROR("use init()/disconnect() before connect()\n"); return -EPERM; } + spin_unlock_irqrestore(&rndis_ipa_ctx->state_lock, flags); if (usb_to_ipa_hdl >= IPA_CLIENT_MAX) { RNDIS_IPA_ERROR("usb_to_ipa_hdl(%d) - not valid ipa handle\n", @@ -710,7 +718,17 @@ int rndis_ipa_pipe_connect_notify(u32 usb_to_ipa_hdl, } RNDIS_IPA_DEBUG("netif_carrier_on() was called\n"); + spin_lock_irqsave(&rndis_ipa_ctx->state_lock, flags); + next_state = rndis_ipa_next_state(rndis_ipa_ctx->state, + RNDIS_IPA_CONNECT); + if (next_state == RNDIS_IPA_INVALID) { + spin_unlock_irqrestore(&rndis_ipa_ctx->state_lock, flags); + RNDIS_IPA_ERROR("use init()/disconnect() before connect()\n"); + return -EPERM; + } rndis_ipa_ctx->state = next_state; + spin_unlock_irqrestore(&rndis_ipa_ctx->state_lock, flags); + RNDIS_IPA_STATE_DEBUG(rndis_ipa_ctx); if (next_state == RNDIS_IPA_CONNECTED_AND_UP) @@ -747,18 +765,25 @@ static int rndis_ipa_open(struct net_device *net) { struct rndis_ipa_dev *rndis_ipa_ctx; int next_state; + unsigned long flags; RNDIS_IPA_LOG_ENTRY(); rndis_ipa_ctx = netdev_priv(net); + spin_lock_irqsave(&rndis_ipa_ctx->state_lock, flags); + next_state = rndis_ipa_next_state(rndis_ipa_ctx->state, RNDIS_IPA_OPEN); if (next_state == RNDIS_IPA_INVALID) { + spin_unlock_irqrestore(&rndis_ipa_ctx->state_lock, flags); RNDIS_IPA_ERROR("can't bring driver up before initialize\n"); return -EPERM; } rndis_ipa_ctx->state = next_state; + + spin_unlock_irqrestore(&rndis_ipa_ctx->state_lock, flags); + RNDIS_IPA_STATE_DEBUG(rndis_ipa_ctx); @@ -1091,19 +1116,26 @@ static int rndis_ipa_stop(struct net_device *net) { struct rndis_ipa_dev *rndis_ipa_ctx = netdev_priv(net); int next_state; + unsigned long flags; RNDIS_IPA_LOG_ENTRY(); + spin_lock_irqsave(&rndis_ipa_ctx->state_lock, flags); + next_state = rndis_ipa_next_state(rndis_ipa_ctx->state, RNDIS_IPA_STOP); if (next_state == RNDIS_IPA_INVALID) { + spin_unlock_irqrestore(&rndis_ipa_ctx->state_lock, flags); RNDIS_IPA_DEBUG("can't do network interface down without up\n"); return -EPERM; } + rndis_ipa_ctx->state = next_state; + + spin_unlock_irqrestore(&rndis_ipa_ctx->state_lock, flags); + netif_stop_queue(net); pr_info("RNDIS_IPA NetDev queue is stopped\n"); - rndis_ipa_ctx->state = next_state; RNDIS_IPA_STATE_DEBUG(rndis_ipa_ctx); RNDIS_IPA_LOG_EXIT(); @@ -1132,18 +1164,23 @@ int rndis_ipa_pipe_disconnect_notify(void *private) int next_state; int outstanding_dropped_pkts; int retval; + unsigned long flags; RNDIS_IPA_LOG_ENTRY(); NULL_CHECK_RETVAL(rndis_ipa_ctx); RNDIS_IPA_DEBUG("private=0x%p\n", private); + spin_lock_irqsave(&rndis_ipa_ctx->state_lock, flags); + next_state = rndis_ipa_next_state(rndis_ipa_ctx->state, RNDIS_IPA_DISCONNECT); if (next_state == RNDIS_IPA_INVALID) { + spin_unlock_irqrestore(&rndis_ipa_ctx->state_lock, flags); RNDIS_IPA_ERROR("can't disconnect before connect\n"); return -EPERM; } + spin_unlock_irqrestore(&rndis_ipa_ctx->state_lock, flags); if (rndis_ipa_ctx->during_xmit_error) { RNDIS_IPA_DEBUG("canceling xmit-error delayed work\n"); @@ -1171,7 +1208,17 @@ int rndis_ipa_pipe_disconnect_notify(void *private) } RNDIS_IPA_DEBUG("RM was successfully destroyed\n"); + spin_lock_irqsave(&rndis_ipa_ctx->state_lock, flags); + next_state = rndis_ipa_next_state(rndis_ipa_ctx->state, + RNDIS_IPA_DISCONNECT); + if (next_state == RNDIS_IPA_INVALID) { + spin_unlock_irqrestore(&rndis_ipa_ctx->state_lock, flags); + RNDIS_IPA_ERROR("can't disconnect before connect\n"); + return -EPERM; + } rndis_ipa_ctx->state = next_state; + spin_unlock_irqrestore(&rndis_ipa_ctx->state_lock, flags); + RNDIS_IPA_STATE_DEBUG(rndis_ipa_ctx); pr_info("RNDIS_IPA NetDev pipes disconnected (%d outstanding clr)\n", @@ -1210,6 +1257,7 @@ void rndis_ipa_cleanup(void *private) struct rndis_ipa_dev *rndis_ipa_ctx = private; int next_state; int retval; + unsigned long flags; RNDIS_IPA_LOG_ENTRY(); @@ -1220,12 +1268,16 @@ void rndis_ipa_cleanup(void *private) return; } + spin_lock_irqsave(&rndis_ipa_ctx->state_lock, flags); next_state = rndis_ipa_next_state(rndis_ipa_ctx->state, RNDIS_IPA_CLEANUP); if (next_state == RNDIS_IPA_INVALID) { + spin_unlock_irqrestore(&rndis_ipa_ctx->state_lock, flags); RNDIS_IPA_ERROR("use disconnect()before clean()\n"); return; } + spin_unlock_irqrestore(&rndis_ipa_ctx->state_lock, flags); + RNDIS_IPA_STATE_DEBUG(rndis_ipa_ctx); retval = rndis_ipa_deregister_properties(rndis_ipa_ctx->net->name); @@ -1248,7 +1300,16 @@ void rndis_ipa_cleanup(void *private) unregister_netdev(rndis_ipa_ctx->net); RNDIS_IPA_DEBUG("netdev unregistered\n"); + spin_lock_irqsave(&rndis_ipa_ctx->state_lock, flags); + next_state = rndis_ipa_next_state(rndis_ipa_ctx->state, + RNDIS_IPA_CLEANUP); + if (next_state == RNDIS_IPA_INVALID) { + spin_unlock_irqrestore(&rndis_ipa_ctx->state_lock, flags); + RNDIS_IPA_ERROR("use disconnect()before clean()\n"); + return; + } rndis_ipa_ctx->state = next_state; + spin_unlock_irqrestore(&rndis_ipa_ctx->state_lock, flags); free_netdev(rndis_ipa_ctx->net); pr_info("RNDIS_IPA NetDev was cleaned\n"); |