Commit 9a8dd213 authored by David S. Miller's avatar David S. Miller

Merge branch 'ncsi-next'

Gavin Shan says:

====================
net/ncsi: NCSI Improvment and bug fixes

This series of patches improves NCSI stack according to the comments
I received after the NCSI code was merged to 4.8.rc1:

  * PATCH[1/8] fixes the build warning caused by xchg() with ia64-linux-gcc.
    The atomic operations are removed. The NCSI's lock should be taken when
    reading or updating its state and chained state.
  * Channel ID (0x1f) is the reserved one and it cannot be valid channel ID.
    So we needn't try to probe channel whose ID is 0x1f. PATCH[2/8] and
    PATCH[3/8] are addressing this issue.
  * The request IDs are assigned in round-robin fashion, but it's broken.
    PATCH[4/8] make it work.
  * PATCH[5/8] and PATCH[6/8] reworks the channel monitoring to improve the
    code readability and its robustness.
  * PATCH[7/8] and PATCH[8/8] introduces ncsi_stop_dev() so that the network
    device can be closed and opened afterwards. No error will be seen.

Changelog
=========
v2:
  * The NCSI's lock is taken when reading or updating its state as the
    {READ,WRITE}_ONCE() isn't reliable.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 93409033 2c15f25b
...@@ -1190,6 +1190,8 @@ static int ftgmac100_stop(struct net_device *netdev) ...@@ -1190,6 +1190,8 @@ static int ftgmac100_stop(struct net_device *netdev)
napi_disable(&priv->napi); napi_disable(&priv->napi);
if (netdev->phydev) if (netdev->phydev)
phy_stop(netdev->phydev); phy_stop(netdev->phydev);
else if (priv->use_ncsi)
ncsi_stop_dev(priv->ndev);
ftgmac100_stop_hw(priv); ftgmac100_stop_hw(priv);
free_irq(priv->irq, netdev); free_irq(priv->irq, netdev);
......
...@@ -31,6 +31,7 @@ struct ncsi_dev { ...@@ -31,6 +31,7 @@ struct ncsi_dev {
struct ncsi_dev *ncsi_register_dev(struct net_device *dev, struct ncsi_dev *ncsi_register_dev(struct net_device *dev,
void (*notifier)(struct ncsi_dev *nd)); void (*notifier)(struct ncsi_dev *nd));
int ncsi_start_dev(struct ncsi_dev *nd); int ncsi_start_dev(struct ncsi_dev *nd);
void ncsi_stop_dev(struct ncsi_dev *nd);
void ncsi_unregister_dev(struct ncsi_dev *nd); void ncsi_unregister_dev(struct ncsi_dev *nd);
#else /* !CONFIG_NET_NCSI */ #else /* !CONFIG_NET_NCSI */
static inline struct ncsi_dev *ncsi_register_dev(struct net_device *dev, static inline struct ncsi_dev *ncsi_register_dev(struct net_device *dev,
...@@ -44,6 +45,10 @@ static inline int ncsi_start_dev(struct ncsi_dev *nd) ...@@ -44,6 +45,10 @@ static inline int ncsi_start_dev(struct ncsi_dev *nd)
return -ENOTTY; return -ENOTTY;
} }
static void ncsi_stop_dev(struct ncsi_dev *nd)
{
}
static inline void ncsi_unregister_dev(struct ncsi_dev *nd) static inline void ncsi_unregister_dev(struct ncsi_dev *nd)
{ {
} }
......
...@@ -170,6 +170,7 @@ struct ncsi_package; ...@@ -170,6 +170,7 @@ struct ncsi_package;
#define NCSI_PACKAGE_SHIFT 5 #define NCSI_PACKAGE_SHIFT 5
#define NCSI_PACKAGE_INDEX(c) (((c) >> NCSI_PACKAGE_SHIFT) & 0x7) #define NCSI_PACKAGE_INDEX(c) (((c) >> NCSI_PACKAGE_SHIFT) & 0x7)
#define NCSI_RESERVED_CHANNEL 0x1f
#define NCSI_CHANNEL_INDEX(c) ((c) & ((1 << NCSI_PACKAGE_SHIFT) - 1)) #define NCSI_CHANNEL_INDEX(c) ((c) & ((1 << NCSI_PACKAGE_SHIFT) - 1))
#define NCSI_TO_CHANNEL(p, c) (((p) << NCSI_PACKAGE_SHIFT) | (c)) #define NCSI_TO_CHANNEL(p, c) (((p) << NCSI_PACKAGE_SHIFT) | (c))
...@@ -186,9 +187,15 @@ struct ncsi_channel { ...@@ -186,9 +187,15 @@ struct ncsi_channel {
struct ncsi_channel_mode modes[NCSI_MODE_MAX]; struct ncsi_channel_mode modes[NCSI_MODE_MAX];
struct ncsi_channel_filter *filters[NCSI_FILTER_MAX]; struct ncsi_channel_filter *filters[NCSI_FILTER_MAX];
struct ncsi_channel_stats stats; struct ncsi_channel_stats stats;
struct timer_list timer; /* Link monitor timer */ struct {
bool enabled; /* Timer is enabled */ struct timer_list timer;
unsigned int timeout; /* Times of timeout */ bool enabled;
unsigned int state;
#define NCSI_CHANNEL_MONITOR_START 0
#define NCSI_CHANNEL_MONITOR_RETRY 1
#define NCSI_CHANNEL_MONITOR_WAIT 2
#define NCSI_CHANNEL_MONITOR_WAIT_MAX 5
} monitor;
struct list_head node; struct list_head node;
struct list_head link; struct list_head link;
}; };
...@@ -206,7 +213,8 @@ struct ncsi_package { ...@@ -206,7 +213,8 @@ struct ncsi_package {
struct ncsi_request { struct ncsi_request {
unsigned char id; /* Request ID - 0 to 255 */ unsigned char id; /* Request ID - 0 to 255 */
bool used; /* Request that has been assigned */ bool used; /* Request that has been assigned */
bool driven; /* Drive state machine */ unsigned int flags; /* NCSI request property */
#define NCSI_REQ_FLAG_EVENT_DRIVEN 1
struct ncsi_dev_priv *ndp; /* Associated NCSI device */ struct ncsi_dev_priv *ndp; /* Associated NCSI device */
struct sk_buff *cmd; /* Associated NCSI command packet */ struct sk_buff *cmd; /* Associated NCSI command packet */
struct sk_buff *rsp; /* Associated NCSI response packet */ struct sk_buff *rsp; /* Associated NCSI response packet */
...@@ -258,6 +266,7 @@ struct ncsi_dev_priv { ...@@ -258,6 +266,7 @@ struct ncsi_dev_priv {
struct list_head packages; /* List of packages */ struct list_head packages; /* List of packages */
struct ncsi_request requests[256]; /* Request table */ struct ncsi_request requests[256]; /* Request table */
unsigned int request_id; /* Last used request ID */ unsigned int request_id; /* Last used request ID */
#define NCSI_REQ_START_IDX 1
unsigned int pending_req_num; /* Number of pending requests */ unsigned int pending_req_num; /* Number of pending requests */
struct ncsi_package *active_package; /* Currently handled package */ struct ncsi_package *active_package; /* Currently handled package */
struct ncsi_channel *active_channel; /* Currently handled channel */ struct ncsi_channel *active_channel; /* Currently handled channel */
...@@ -274,7 +283,7 @@ struct ncsi_cmd_arg { ...@@ -274,7 +283,7 @@ struct ncsi_cmd_arg {
unsigned char package; /* Destination package ID */ unsigned char package; /* Destination package ID */
unsigned char channel; /* Detination channel ID or 0x1f */ unsigned char channel; /* Detination channel ID or 0x1f */
unsigned short payload; /* Command packet payload length */ unsigned short payload; /* Command packet payload length */
bool driven; /* Drive the state machine? */ unsigned int req_flags; /* NCSI request properties */
union { union {
unsigned char bytes[16]; /* Command packet specific data */ unsigned char bytes[16]; /* Command packet specific data */
unsigned short words[8]; unsigned short words[8];
...@@ -313,7 +322,8 @@ void ncsi_find_package_and_channel(struct ncsi_dev_priv *ndp, ...@@ -313,7 +322,8 @@ void ncsi_find_package_and_channel(struct ncsi_dev_priv *ndp,
unsigned char id, unsigned char id,
struct ncsi_package **np, struct ncsi_package **np,
struct ncsi_channel **nc); struct ncsi_channel **nc);
struct ncsi_request *ncsi_alloc_request(struct ncsi_dev_priv *ndp, bool driven); struct ncsi_request *ncsi_alloc_request(struct ncsi_dev_priv *ndp,
unsigned int req_flags);
void ncsi_free_request(struct ncsi_request *nr); void ncsi_free_request(struct ncsi_request *nr);
struct ncsi_dev *ncsi_find_dev(struct net_device *dev); struct ncsi_dev *ncsi_find_dev(struct net_device *dev);
int ncsi_process_next_channel(struct ncsi_dev_priv *ndp); int ncsi_process_next_channel(struct ncsi_dev_priv *ndp);
......
...@@ -53,7 +53,9 @@ static int ncsi_aen_handler_lsc(struct ncsi_dev_priv *ndp, ...@@ -53,7 +53,9 @@ static int ncsi_aen_handler_lsc(struct ncsi_dev_priv *ndp,
struct ncsi_aen_lsc_pkt *lsc; struct ncsi_aen_lsc_pkt *lsc;
struct ncsi_channel *nc; struct ncsi_channel *nc;
struct ncsi_channel_mode *ncm; struct ncsi_channel_mode *ncm;
unsigned long old_data; bool chained;
int state;
unsigned long old_data, data;
unsigned long flags; unsigned long flags;
/* Find the NCSI channel */ /* Find the NCSI channel */
...@@ -62,20 +64,27 @@ static int ncsi_aen_handler_lsc(struct ncsi_dev_priv *ndp, ...@@ -62,20 +64,27 @@ static int ncsi_aen_handler_lsc(struct ncsi_dev_priv *ndp,
return -ENODEV; return -ENODEV;
/* Update the link status */ /* Update the link status */
ncm = &nc->modes[NCSI_MODE_LINK];
lsc = (struct ncsi_aen_lsc_pkt *)h; lsc = (struct ncsi_aen_lsc_pkt *)h;
spin_lock_irqsave(&nc->lock, flags);
ncm = &nc->modes[NCSI_MODE_LINK];
old_data = ncm->data[2]; old_data = ncm->data[2];
ncm->data[2] = ntohl(lsc->status); data = ntohl(lsc->status);
ncm->data[2] = data;
ncm->data[4] = ntohl(lsc->oem_status); ncm->data[4] = ntohl(lsc->oem_status);
if (!((old_data ^ ncm->data[2]) & 0x1) ||
!list_empty(&nc->link)) chained = !list_empty(&nc->link);
state = nc->state;
spin_unlock_irqrestore(&nc->lock, flags);
if (!((old_data ^ data) & 0x1) || chained)
return 0; return 0;
if (!(nc->state == NCSI_CHANNEL_INACTIVE && (ncm->data[2] & 0x1)) && if (!(state == NCSI_CHANNEL_INACTIVE && (data & 0x1)) &&
!(nc->state == NCSI_CHANNEL_ACTIVE && !(ncm->data[2] & 0x1))) !(state == NCSI_CHANNEL_ACTIVE && !(data & 0x1)))
return 0; return 0;
if (!(ndp->flags & NCSI_DEV_HWA) && if (!(ndp->flags & NCSI_DEV_HWA) &&
nc->state == NCSI_CHANNEL_ACTIVE) state == NCSI_CHANNEL_ACTIVE)
ndp->flags |= NCSI_DEV_RESHUFFLE; ndp->flags |= NCSI_DEV_RESHUFFLE;
ncsi_stop_channel_monitor(nc); ncsi_stop_channel_monitor(nc);
...@@ -97,13 +106,21 @@ static int ncsi_aen_handler_cr(struct ncsi_dev_priv *ndp, ...@@ -97,13 +106,21 @@ static int ncsi_aen_handler_cr(struct ncsi_dev_priv *ndp,
if (!nc) if (!nc)
return -ENODEV; return -ENODEV;
spin_lock_irqsave(&nc->lock, flags);
if (!list_empty(&nc->link) || if (!list_empty(&nc->link) ||
nc->state != NCSI_CHANNEL_ACTIVE) nc->state != NCSI_CHANNEL_ACTIVE) {
spin_unlock_irqrestore(&nc->lock, flags);
return 0; return 0;
}
spin_unlock_irqrestore(&nc->lock, flags);
ncsi_stop_channel_monitor(nc); ncsi_stop_channel_monitor(nc);
spin_lock_irqsave(&nc->lock, flags);
nc->state = NCSI_CHANNEL_INVISIBLE;
spin_unlock_irqrestore(&nc->lock, flags);
spin_lock_irqsave(&ndp->lock, flags); spin_lock_irqsave(&ndp->lock, flags);
xchg(&nc->state, NCSI_CHANNEL_INACTIVE); nc->state = NCSI_CHANNEL_INACTIVE;
list_add_tail_rcu(&nc->link, &ndp->channel_queue); list_add_tail_rcu(&nc->link, &ndp->channel_queue);
spin_unlock_irqrestore(&ndp->lock, flags); spin_unlock_irqrestore(&ndp->lock, flags);
......
...@@ -272,7 +272,7 @@ static struct ncsi_request *ncsi_alloc_command(struct ncsi_cmd_arg *nca) ...@@ -272,7 +272,7 @@ static struct ncsi_request *ncsi_alloc_command(struct ncsi_cmd_arg *nca)
struct sk_buff *skb; struct sk_buff *skb;
struct ncsi_request *nr; struct ncsi_request *nr;
nr = ncsi_alloc_request(ndp, nca->driven); nr = ncsi_alloc_request(ndp, nca->req_flags);
if (!nr) if (!nr)
return NULL; return NULL;
......
...@@ -132,6 +132,7 @@ static void ncsi_report_link(struct ncsi_dev_priv *ndp, bool force_down) ...@@ -132,6 +132,7 @@ static void ncsi_report_link(struct ncsi_dev_priv *ndp, bool force_down)
struct ncsi_dev *nd = &ndp->ndev; struct ncsi_dev *nd = &ndp->ndev;
struct ncsi_package *np; struct ncsi_package *np;
struct ncsi_channel *nc; struct ncsi_channel *nc;
unsigned long flags;
nd->state = ncsi_dev_state_functional; nd->state = ncsi_dev_state_functional;
if (force_down) { if (force_down) {
...@@ -142,14 +143,21 @@ static void ncsi_report_link(struct ncsi_dev_priv *ndp, bool force_down) ...@@ -142,14 +143,21 @@ static void ncsi_report_link(struct ncsi_dev_priv *ndp, bool force_down)
nd->link_up = 0; nd->link_up = 0;
NCSI_FOR_EACH_PACKAGE(ndp, np) { NCSI_FOR_EACH_PACKAGE(ndp, np) {
NCSI_FOR_EACH_CHANNEL(np, nc) { NCSI_FOR_EACH_CHANNEL(np, nc) {
spin_lock_irqsave(&nc->lock, flags);
if (!list_empty(&nc->link) || if (!list_empty(&nc->link) ||
nc->state != NCSI_CHANNEL_ACTIVE) nc->state != NCSI_CHANNEL_ACTIVE) {
spin_unlock_irqrestore(&nc->lock, flags);
continue; continue;
}
if (nc->modes[NCSI_MODE_LINK].data[2] & 0x1) { if (nc->modes[NCSI_MODE_LINK].data[2] & 0x1) {
spin_unlock_irqrestore(&nc->lock, flags);
nd->link_up = 1; nd->link_up = 1;
goto report; goto report;
} }
spin_unlock_irqrestore(&nc->lock, flags);
} }
} }
...@@ -163,43 +171,55 @@ static void ncsi_channel_monitor(unsigned long data) ...@@ -163,43 +171,55 @@ static void ncsi_channel_monitor(unsigned long data)
struct ncsi_package *np = nc->package; struct ncsi_package *np = nc->package;
struct ncsi_dev_priv *ndp = np->ndp; struct ncsi_dev_priv *ndp = np->ndp;
struct ncsi_cmd_arg nca; struct ncsi_cmd_arg nca;
bool enabled; bool enabled, chained;
unsigned int timeout; unsigned int monitor_state;
unsigned long flags; unsigned long flags;
int ret; int state, ret;
spin_lock_irqsave(&nc->lock, flags); spin_lock_irqsave(&nc->lock, flags);
timeout = nc->timeout; state = nc->state;
enabled = nc->enabled; chained = !list_empty(&nc->link);
enabled = nc->monitor.enabled;
monitor_state = nc->monitor.state;
spin_unlock_irqrestore(&nc->lock, flags); spin_unlock_irqrestore(&nc->lock, flags);
if (!enabled || !list_empty(&nc->link)) if (!enabled || chained)
return; return;
if (nc->state != NCSI_CHANNEL_INACTIVE && if (state != NCSI_CHANNEL_INACTIVE &&
nc->state != NCSI_CHANNEL_ACTIVE) state != NCSI_CHANNEL_ACTIVE)
return; return;
if (!(timeout % 2)) { switch (monitor_state) {
case NCSI_CHANNEL_MONITOR_START:
case NCSI_CHANNEL_MONITOR_RETRY:
nca.ndp = ndp; nca.ndp = ndp;
nca.package = np->id; nca.package = np->id;
nca.channel = nc->id; nca.channel = nc->id;
nca.type = NCSI_PKT_CMD_GLS; nca.type = NCSI_PKT_CMD_GLS;
nca.driven = false; nca.req_flags = 0;
ret = ncsi_xmit_cmd(&nca); ret = ncsi_xmit_cmd(&nca);
if (ret) { if (ret) {
netdev_err(ndp->ndev.dev, "Error %d sending GLS\n", netdev_err(ndp->ndev.dev, "Error %d sending GLS\n",
ret); ret);
return; return;
} }
}
if (timeout + 1 >= 3) { break;
case NCSI_CHANNEL_MONITOR_WAIT ... NCSI_CHANNEL_MONITOR_WAIT_MAX:
break;
default:
if (!(ndp->flags & NCSI_DEV_HWA) && if (!(ndp->flags & NCSI_DEV_HWA) &&
nc->state == NCSI_CHANNEL_ACTIVE) state == NCSI_CHANNEL_ACTIVE) {
ncsi_report_link(ndp, true); ncsi_report_link(ndp, true);
ndp->flags |= NCSI_DEV_RESHUFFLE;
}
spin_lock_irqsave(&nc->lock, flags);
nc->state = NCSI_CHANNEL_INVISIBLE;
spin_unlock_irqrestore(&nc->lock, flags);
spin_lock_irqsave(&ndp->lock, flags); spin_lock_irqsave(&ndp->lock, flags);
xchg(&nc->state, NCSI_CHANNEL_INACTIVE); nc->state = NCSI_CHANNEL_INACTIVE;
list_add_tail_rcu(&nc->link, &ndp->channel_queue); list_add_tail_rcu(&nc->link, &ndp->channel_queue);
spin_unlock_irqrestore(&ndp->lock, flags); spin_unlock_irqrestore(&ndp->lock, flags);
ncsi_process_next_channel(ndp); ncsi_process_next_channel(ndp);
...@@ -207,10 +227,9 @@ static void ncsi_channel_monitor(unsigned long data) ...@@ -207,10 +227,9 @@ static void ncsi_channel_monitor(unsigned long data)
} }
spin_lock_irqsave(&nc->lock, flags); spin_lock_irqsave(&nc->lock, flags);
nc->timeout = timeout + 1; nc->monitor.state++;
nc->enabled = true;
spin_unlock_irqrestore(&nc->lock, flags); spin_unlock_irqrestore(&nc->lock, flags);
mod_timer(&nc->timer, jiffies + HZ * (1 << (nc->timeout / 2))); mod_timer(&nc->monitor.timer, jiffies + HZ);
} }
void ncsi_start_channel_monitor(struct ncsi_channel *nc) void ncsi_start_channel_monitor(struct ncsi_channel *nc)
...@@ -218,12 +237,12 @@ void ncsi_start_channel_monitor(struct ncsi_channel *nc) ...@@ -218,12 +237,12 @@ void ncsi_start_channel_monitor(struct ncsi_channel *nc)
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&nc->lock, flags); spin_lock_irqsave(&nc->lock, flags);
WARN_ON_ONCE(nc->enabled); WARN_ON_ONCE(nc->monitor.enabled);
nc->timeout = 0; nc->monitor.enabled = true;
nc->enabled = true; nc->monitor.state = NCSI_CHANNEL_MONITOR_START;
spin_unlock_irqrestore(&nc->lock, flags); spin_unlock_irqrestore(&nc->lock, flags);
mod_timer(&nc->timer, jiffies + HZ * (1 << (nc->timeout / 2))); mod_timer(&nc->monitor.timer, jiffies + HZ);
} }
void ncsi_stop_channel_monitor(struct ncsi_channel *nc) void ncsi_stop_channel_monitor(struct ncsi_channel *nc)
...@@ -231,14 +250,14 @@ void ncsi_stop_channel_monitor(struct ncsi_channel *nc) ...@@ -231,14 +250,14 @@ void ncsi_stop_channel_monitor(struct ncsi_channel *nc)
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&nc->lock, flags); spin_lock_irqsave(&nc->lock, flags);
if (!nc->enabled) { if (!nc->monitor.enabled) {
spin_unlock_irqrestore(&nc->lock, flags); spin_unlock_irqrestore(&nc->lock, flags);
return; return;
} }
nc->enabled = false; nc->monitor.enabled = false;
spin_unlock_irqrestore(&nc->lock, flags); spin_unlock_irqrestore(&nc->lock, flags);
del_timer_sync(&nc->timer); del_timer_sync(&nc->monitor.timer);
} }
struct ncsi_channel *ncsi_find_channel(struct ncsi_package *np, struct ncsi_channel *ncsi_find_channel(struct ncsi_package *np,
...@@ -267,8 +286,9 @@ struct ncsi_channel *ncsi_add_channel(struct ncsi_package *np, unsigned char id) ...@@ -267,8 +286,9 @@ struct ncsi_channel *ncsi_add_channel(struct ncsi_package *np, unsigned char id)
nc->id = id; nc->id = id;
nc->package = np; nc->package = np;
nc->state = NCSI_CHANNEL_INACTIVE; nc->state = NCSI_CHANNEL_INACTIVE;
nc->enabled = false; nc->monitor.enabled = false;
setup_timer(&nc->timer, ncsi_channel_monitor, (unsigned long)nc); setup_timer(&nc->monitor.timer,
ncsi_channel_monitor, (unsigned long)nc);
spin_lock_init(&nc->lock); spin_lock_init(&nc->lock);
INIT_LIST_HEAD(&nc->link); INIT_LIST_HEAD(&nc->link);
for (index = 0; index < NCSI_CAP_MAX; index++) for (index = 0; index < NCSI_CAP_MAX; index++)
...@@ -405,7 +425,8 @@ void ncsi_find_package_and_channel(struct ncsi_dev_priv *ndp, ...@@ -405,7 +425,8 @@ void ncsi_find_package_and_channel(struct ncsi_dev_priv *ndp,
* be same. Otherwise, the bogus response might be replied. So * be same. Otherwise, the bogus response might be replied. So
* the available IDs are allocated in round-robin fashion. * the available IDs are allocated in round-robin fashion.
*/ */
struct ncsi_request *ncsi_alloc_request(struct ncsi_dev_priv *ndp, bool driven) struct ncsi_request *ncsi_alloc_request(struct ncsi_dev_priv *ndp,
unsigned int req_flags)
{ {
struct ncsi_request *nr = NULL; struct ncsi_request *nr = NULL;
int i, limit = ARRAY_SIZE(ndp->requests); int i, limit = ARRAY_SIZE(ndp->requests);
...@@ -413,30 +434,31 @@ struct ncsi_request *ncsi_alloc_request(struct ncsi_dev_priv *ndp, bool driven) ...@@ -413,30 +434,31 @@ struct ncsi_request *ncsi_alloc_request(struct ncsi_dev_priv *ndp, bool driven)
/* Check if there is one available request until the ceiling */ /* Check if there is one available request until the ceiling */
spin_lock_irqsave(&ndp->lock, flags); spin_lock_irqsave(&ndp->lock, flags);
for (i = ndp->request_id; !nr && i < limit; i++) { for (i = ndp->request_id; i < limit; i++) {
if (ndp->requests[i].used) if (ndp->requests[i].used)
continue; continue;
nr = &ndp->requests[i]; nr = &ndp->requests[i];
nr->used = true; nr->used = true;
nr->driven = driven; nr->flags = req_flags;
if (++ndp->request_id >= limit) ndp->request_id = i + 1;
ndp->request_id = 0; goto found;
} }
/* Fail back to check from the starting cursor */ /* Fail back to check from the starting cursor */
for (i = 0; !nr && i < ndp->request_id; i++) { for (i = NCSI_REQ_START_IDX; i < ndp->request_id; i++) {
if (ndp->requests[i].used) if (ndp->requests[i].used)
continue; continue;
nr = &ndp->requests[i]; nr = &ndp->requests[i];
nr->used = true; nr->used = true;
nr->driven = driven; nr->flags = req_flags;
if (++ndp->request_id >= limit) ndp->request_id = i + 1;
ndp->request_id = 0; goto found;
} }
spin_unlock_irqrestore(&ndp->lock, flags);
found:
spin_unlock_irqrestore(&ndp->lock, flags);
return nr; return nr;
} }
...@@ -458,7 +480,7 @@ void ncsi_free_request(struct ncsi_request *nr) ...@@ -458,7 +480,7 @@ void ncsi_free_request(struct ncsi_request *nr)
nr->cmd = NULL; nr->cmd = NULL;
nr->rsp = NULL; nr->rsp = NULL;
nr->used = false; nr->used = false;
driven = nr->driven; driven = !!(nr->flags & NCSI_REQ_FLAG_EVENT_DRIVEN);
spin_unlock_irqrestore(&ndp->lock, flags); spin_unlock_irqrestore(&ndp->lock, flags);
if (driven && cmd && --ndp->pending_req_num == 0) if (driven && cmd && --ndp->pending_req_num == 0)
...@@ -508,10 +530,11 @@ static void ncsi_suspend_channel(struct ncsi_dev_priv *ndp) ...@@ -508,10 +530,11 @@ static void ncsi_suspend_channel(struct ncsi_dev_priv *ndp)
struct ncsi_package *np = ndp->active_package; struct ncsi_package *np = ndp->active_package;
struct ncsi_channel *nc = ndp->active_channel; struct ncsi_channel *nc = ndp->active_channel;
struct ncsi_cmd_arg nca; struct ncsi_cmd_arg nca;
unsigned long flags;
int ret; int ret;
nca.ndp = ndp; nca.ndp = ndp;
nca.driven = true; nca.req_flags = NCSI_REQ_FLAG_EVENT_DRIVEN;
switch (nd->state) { switch (nd->state) {
case ncsi_dev_state_suspend: case ncsi_dev_state_suspend:
nd->state = ncsi_dev_state_suspend_select; nd->state = ncsi_dev_state_suspend_select;
...@@ -527,7 +550,7 @@ static void ncsi_suspend_channel(struct ncsi_dev_priv *ndp) ...@@ -527,7 +550,7 @@ static void ncsi_suspend_channel(struct ncsi_dev_priv *ndp)
nca.package = np->id; nca.package = np->id;
if (nd->state == ncsi_dev_state_suspend_select) { if (nd->state == ncsi_dev_state_suspend_select) {
nca.type = NCSI_PKT_CMD_SP; nca.type = NCSI_PKT_CMD_SP;
nca.channel = 0x1f; nca.channel = NCSI_RESERVED_CHANNEL;
if (ndp->flags & NCSI_DEV_HWA) if (ndp->flags & NCSI_DEV_HWA)
nca.bytes[0] = 0; nca.bytes[0] = 0;
else else
...@@ -544,7 +567,7 @@ static void ncsi_suspend_channel(struct ncsi_dev_priv *ndp) ...@@ -544,7 +567,7 @@ static void ncsi_suspend_channel(struct ncsi_dev_priv *ndp)
nd->state = ncsi_dev_state_suspend_deselect; nd->state = ncsi_dev_state_suspend_deselect;
} else if (nd->state == ncsi_dev_state_suspend_deselect) { } else if (nd->state == ncsi_dev_state_suspend_deselect) {
nca.type = NCSI_PKT_CMD_DP; nca.type = NCSI_PKT_CMD_DP;
nca.channel = 0x1f; nca.channel = NCSI_RESERVED_CHANNEL;
nd->state = ncsi_dev_state_suspend_done; nd->state = ncsi_dev_state_suspend_done;
} }
...@@ -556,7 +579,9 @@ static void ncsi_suspend_channel(struct ncsi_dev_priv *ndp) ...@@ -556,7 +579,9 @@ static void ncsi_suspend_channel(struct ncsi_dev_priv *ndp)
break; break;
case ncsi_dev_state_suspend_done: case ncsi_dev_state_suspend_done:
xchg(&nc->state, NCSI_CHANNEL_INACTIVE); spin_lock_irqsave(&nc->lock, flags);
nc->state = NCSI_CHANNEL_INACTIVE;
spin_unlock_irqrestore(&nc->lock, flags);
ncsi_process_next_channel(ndp); ncsi_process_next_channel(ndp);
break; break;
...@@ -574,10 +599,11 @@ static void ncsi_configure_channel(struct ncsi_dev_priv *ndp) ...@@ -574,10 +599,11 @@ static void ncsi_configure_channel(struct ncsi_dev_priv *ndp)
struct ncsi_channel *nc = ndp->active_channel; struct ncsi_channel *nc = ndp->active_channel;
struct ncsi_cmd_arg nca; struct ncsi_cmd_arg nca;
unsigned char index; unsigned char index;
unsigned long flags;
int ret; int ret;
nca.ndp = ndp; nca.ndp = ndp;
nca.driven = true; nca.req_flags = NCSI_REQ_FLAG_EVENT_DRIVEN;
switch (nd->state) { switch (nd->state) {
case ncsi_dev_state_config: case ncsi_dev_state_config:
case ncsi_dev_state_config_sp: case ncsi_dev_state_config_sp:
...@@ -590,7 +616,7 @@ static void ncsi_configure_channel(struct ncsi_dev_priv *ndp) ...@@ -590,7 +616,7 @@ static void ncsi_configure_channel(struct ncsi_dev_priv *ndp)
else else
nca.bytes[0] = 1; nca.bytes[0] = 1;
nca.package = np->id; nca.package = np->id;
nca.channel = 0x1f; nca.channel = NCSI_RESERVED_CHANNEL;
ret = ncsi_xmit_cmd(&nca); ret = ncsi_xmit_cmd(&nca);
if (ret) if (ret)
goto error; goto error;
...@@ -675,10 +701,12 @@ static void ncsi_configure_channel(struct ncsi_dev_priv *ndp) ...@@ -675,10 +701,12 @@ static void ncsi_configure_channel(struct ncsi_dev_priv *ndp)
goto error; goto error;
break; break;
case ncsi_dev_state_config_done: case ncsi_dev_state_config_done:
spin_lock_irqsave(&nc->lock, flags);
if (nc->modes[NCSI_MODE_LINK].data[2] & 0x1) if (nc->modes[NCSI_MODE_LINK].data[2] & 0x1)
xchg(&nc->state, NCSI_CHANNEL_ACTIVE); nc->state = NCSI_CHANNEL_ACTIVE;
else else
xchg(&nc->state, NCSI_CHANNEL_INACTIVE); nc->state = NCSI_CHANNEL_INACTIVE;
spin_unlock_irqrestore(&nc->lock, flags);
ncsi_start_channel_monitor(nc); ncsi_start_channel_monitor(nc);
ncsi_process_next_channel(ndp); ncsi_process_next_channel(ndp);
...@@ -707,18 +735,25 @@ static int ncsi_choose_active_channel(struct ncsi_dev_priv *ndp) ...@@ -707,18 +735,25 @@ static int ncsi_choose_active_channel(struct ncsi_dev_priv *ndp)
found = NULL; found = NULL;
NCSI_FOR_EACH_PACKAGE(ndp, np) { NCSI_FOR_EACH_PACKAGE(ndp, np) {
NCSI_FOR_EACH_CHANNEL(np, nc) { NCSI_FOR_EACH_CHANNEL(np, nc) {
spin_lock_irqsave(&nc->lock, flags);
if (!list_empty(&nc->link) || if (!list_empty(&nc->link) ||
nc->state != NCSI_CHANNEL_INACTIVE) nc->state != NCSI_CHANNEL_INACTIVE) {
spin_unlock_irqrestore(&nc->lock, flags);
continue; continue;
}
if (!found) if (!found)
found = nc; found = nc;
ncm = &nc->modes[NCSI_MODE_LINK]; ncm = &nc->modes[NCSI_MODE_LINK];
if (ncm->data[2] & 0x1) { if (ncm->data[2] & 0x1) {
spin_unlock_irqrestore(&nc->lock, flags);
found = nc; found = nc;
goto out; goto out;
} }
spin_unlock_irqrestore(&nc->lock, flags);
} }
} }
...@@ -797,7 +832,7 @@ static void ncsi_probe_channel(struct ncsi_dev_priv *ndp) ...@@ -797,7 +832,7 @@ static void ncsi_probe_channel(struct ncsi_dev_priv *ndp)
int ret; int ret;
nca.ndp = ndp; nca.ndp = ndp;
nca.driven = true; nca.req_flags = NCSI_REQ_FLAG_EVENT_DRIVEN;
switch (nd->state) { switch (nd->state) {
case ncsi_dev_state_probe: case ncsi_dev_state_probe:
nd->state = ncsi_dev_state_probe_deselect; nd->state = ncsi_dev_state_probe_deselect;
...@@ -807,7 +842,7 @@ static void ncsi_probe_channel(struct ncsi_dev_priv *ndp) ...@@ -807,7 +842,7 @@ static void ncsi_probe_channel(struct ncsi_dev_priv *ndp)
/* Deselect all possible packages */ /* Deselect all possible packages */
nca.type = NCSI_PKT_CMD_DP; nca.type = NCSI_PKT_CMD_DP;
nca.channel = 0x1f; nca.channel = NCSI_RESERVED_CHANNEL;
for (index = 0; index < 8; index++) { for (index = 0; index < 8; index++) {
nca.package = index; nca.package = index;
ret = ncsi_xmit_cmd(&nca); ret = ncsi_xmit_cmd(&nca);
...@@ -823,7 +858,7 @@ static void ncsi_probe_channel(struct ncsi_dev_priv *ndp) ...@@ -823,7 +858,7 @@ static void ncsi_probe_channel(struct ncsi_dev_priv *ndp)
/* Select all possible packages */ /* Select all possible packages */
nca.type = NCSI_PKT_CMD_SP; nca.type = NCSI_PKT_CMD_SP;
nca.bytes[0] = 1; nca.bytes[0] = 1;
nca.channel = 0x1f; nca.channel = NCSI_RESERVED_CHANNEL;
for (index = 0; index < 8; index++) { for (index = 0; index < 8; index++) {
nca.package = index; nca.package = index;
ret = ncsi_xmit_cmd(&nca); ret = ncsi_xmit_cmd(&nca);
...@@ -876,7 +911,7 @@ static void ncsi_probe_channel(struct ncsi_dev_priv *ndp) ...@@ -876,7 +911,7 @@ static void ncsi_probe_channel(struct ncsi_dev_priv *ndp)
nca.type = NCSI_PKT_CMD_SP; nca.type = NCSI_PKT_CMD_SP;
nca.bytes[0] = 1; nca.bytes[0] = 1;
nca.package = ndp->active_package->id; nca.package = ndp->active_package->id;
nca.channel = 0x1f; nca.channel = NCSI_RESERVED_CHANNEL;
ret = ncsi_xmit_cmd(&nca); ret = ncsi_xmit_cmd(&nca);
if (ret) if (ret)
goto error; goto error;
...@@ -884,12 +919,12 @@ static void ncsi_probe_channel(struct ncsi_dev_priv *ndp) ...@@ -884,12 +919,12 @@ static void ncsi_probe_channel(struct ncsi_dev_priv *ndp)
nd->state = ncsi_dev_state_probe_cis; nd->state = ncsi_dev_state_probe_cis;
break; break;
case ncsi_dev_state_probe_cis: case ncsi_dev_state_probe_cis:
ndp->pending_req_num = 32; ndp->pending_req_num = NCSI_RESERVED_CHANNEL;
/* Clear initial state */ /* Clear initial state */
nca.type = NCSI_PKT_CMD_CIS; nca.type = NCSI_PKT_CMD_CIS;
nca.package = ndp->active_package->id; nca.package = ndp->active_package->id;
for (index = 0; index < 0x20; index++) { for (index = 0; index < NCSI_RESERVED_CHANNEL; index++) {
nca.channel = index; nca.channel = index;
ret = ncsi_xmit_cmd(&nca); ret = ncsi_xmit_cmd(&nca);
if (ret) if (ret)
...@@ -933,7 +968,7 @@ static void ncsi_probe_channel(struct ncsi_dev_priv *ndp) ...@@ -933,7 +968,7 @@ static void ncsi_probe_channel(struct ncsi_dev_priv *ndp)
/* Deselect the active package */ /* Deselect the active package */
nca.type = NCSI_PKT_CMD_DP; nca.type = NCSI_PKT_CMD_DP;
nca.package = ndp->active_package->id; nca.package = ndp->active_package->id;
nca.channel = 0x1f; nca.channel = NCSI_RESERVED_CHANNEL;
ret = ncsi_xmit_cmd(&nca); ret = ncsi_xmit_cmd(&nca);
if (ret) if (ret)
goto error; goto error;
...@@ -987,11 +1022,14 @@ int ncsi_process_next_channel(struct ncsi_dev_priv *ndp) ...@@ -987,11 +1022,14 @@ int ncsi_process_next_channel(struct ncsi_dev_priv *ndp)
goto out; goto out;
} }
old_state = xchg(&nc->state, NCSI_CHANNEL_INVISIBLE);
list_del_init(&nc->link); list_del_init(&nc->link);
spin_unlock_irqrestore(&ndp->lock, flags); spin_unlock_irqrestore(&ndp->lock, flags);
spin_lock_irqsave(&nc->lock, flags);
old_state = nc->state;
nc->state = NCSI_CHANNEL_INVISIBLE;
spin_unlock_irqrestore(&nc->lock, flags);
ndp->active_channel = nc; ndp->active_channel = nc;
ndp->active_package = nc->package; ndp->active_package = nc->package;
...@@ -1006,7 +1044,7 @@ int ncsi_process_next_channel(struct ncsi_dev_priv *ndp) ...@@ -1006,7 +1044,7 @@ int ncsi_process_next_channel(struct ncsi_dev_priv *ndp)
break; break;
default: default:
netdev_err(ndp->ndev.dev, "Invalid state 0x%x on %d:%d\n", netdev_err(ndp->ndev.dev, "Invalid state 0x%x on %d:%d\n",
nc->state, nc->package->id, nc->id); old_state, nc->package->id, nc->id);
ncsi_report_link(ndp, false); ncsi_report_link(ndp, false);
return -EINVAL; return -EINVAL;
} }
...@@ -1070,7 +1108,7 @@ static int ncsi_inet6addr_event(struct notifier_block *this, ...@@ -1070,7 +1108,7 @@ static int ncsi_inet6addr_event(struct notifier_block *this,
return NOTIFY_OK; return NOTIFY_OK;
nca.ndp = ndp; nca.ndp = ndp;
nca.driven = false; nca.req_flags = 0;
nca.package = np->id; nca.package = np->id;
nca.channel = nc->id; nca.channel = nc->id;
nca.dwords[0] = nc->caps[NCSI_CAP_MC].cap; nca.dwords[0] = nc->caps[NCSI_CAP_MC].cap;
...@@ -1118,7 +1156,7 @@ struct ncsi_dev *ncsi_register_dev(struct net_device *dev, ...@@ -1118,7 +1156,7 @@ struct ncsi_dev *ncsi_register_dev(struct net_device *dev,
/* Initialize private NCSI device */ /* Initialize private NCSI device */
spin_lock_init(&ndp->lock); spin_lock_init(&ndp->lock);
INIT_LIST_HEAD(&ndp->packages); INIT_LIST_HEAD(&ndp->packages);
ndp->request_id = 0; ndp->request_id = NCSI_REQ_START_IDX;
for (i = 0; i < ARRAY_SIZE(ndp->requests); i++) { for (i = 0; i < ARRAY_SIZE(ndp->requests); i++) {
ndp->requests[i].id = i; ndp->requests[i].id = i;
ndp->requests[i].ndp = ndp; ndp->requests[i].ndp = ndp;
...@@ -1149,9 +1187,7 @@ EXPORT_SYMBOL_GPL(ncsi_register_dev); ...@@ -1149,9 +1187,7 @@ EXPORT_SYMBOL_GPL(ncsi_register_dev);
int ncsi_start_dev(struct ncsi_dev *nd) int ncsi_start_dev(struct ncsi_dev *nd)
{ {
struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd); struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
struct ncsi_package *np; int ret;
struct ncsi_channel *nc;
int old_state, ret;
if (nd->state != ncsi_dev_state_registered && if (nd->state != ncsi_dev_state_registered &&
nd->state != ncsi_dev_state_functional) nd->state != ncsi_dev_state_functional)
...@@ -1163,15 +1199,6 @@ int ncsi_start_dev(struct ncsi_dev *nd) ...@@ -1163,15 +1199,6 @@ int ncsi_start_dev(struct ncsi_dev *nd)
return 0; return 0;
} }
/* Reset channel's state and start over */
NCSI_FOR_EACH_PACKAGE(ndp, np) {
NCSI_FOR_EACH_CHANNEL(np, nc) {
old_state = xchg(&nc->state, NCSI_CHANNEL_INACTIVE);
WARN_ON_ONCE(!list_empty(&nc->link) ||
old_state == NCSI_CHANNEL_INVISIBLE);
}
}
if (ndp->flags & NCSI_DEV_HWA) if (ndp->flags & NCSI_DEV_HWA)
ret = ncsi_enable_hwa(ndp); ret = ncsi_enable_hwa(ndp);
else else
...@@ -1181,6 +1208,35 @@ int ncsi_start_dev(struct ncsi_dev *nd) ...@@ -1181,6 +1208,35 @@ int ncsi_start_dev(struct ncsi_dev *nd)
} }
EXPORT_SYMBOL_GPL(ncsi_start_dev); EXPORT_SYMBOL_GPL(ncsi_start_dev);
void ncsi_stop_dev(struct ncsi_dev *nd)
{
struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
struct ncsi_package *np;
struct ncsi_channel *nc;
bool chained;
int old_state;
unsigned long flags;
/* Stop the channel monitor and reset channel's state */
NCSI_FOR_EACH_PACKAGE(ndp, np) {
NCSI_FOR_EACH_CHANNEL(np, nc) {
ncsi_stop_channel_monitor(nc);
spin_lock_irqsave(&nc->lock, flags);
chained = !list_empty(&nc->link);
old_state = nc->state;
nc->state = NCSI_CHANNEL_INACTIVE;
spin_unlock_irqrestore(&nc->lock, flags);
WARN_ON_ONCE(chained ||
old_state == NCSI_CHANNEL_INVISIBLE);
}
}
ncsi_report_link(ndp, true);
}
EXPORT_SYMBOL_GPL(ncsi_stop_dev);
void ncsi_unregister_dev(struct ncsi_dev *nd) void ncsi_unregister_dev(struct ncsi_dev *nd)
{ {
struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd); struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
......
...@@ -317,12 +317,12 @@ static int ncsi_rsp_handler_gls(struct ncsi_request *nr) ...@@ -317,12 +317,12 @@ static int ncsi_rsp_handler_gls(struct ncsi_request *nr)
ncm->data[3] = ntohl(rsp->other); ncm->data[3] = ntohl(rsp->other);
ncm->data[4] = ntohl(rsp->oem_status); ncm->data[4] = ntohl(rsp->oem_status);
if (nr->driven) if (nr->flags & NCSI_REQ_FLAG_EVENT_DRIVEN)
return 0; return 0;
/* Reset the channel monitor if it has been enabled */ /* Reset the channel monitor if it has been enabled */
spin_lock_irqsave(&nc->lock, flags); spin_lock_irqsave(&nc->lock, flags);
nc->timeout = 0; nc->monitor.state = NCSI_CHANNEL_MONITOR_START;
spin_unlock_irqrestore(&nc->lock, flags); spin_unlock_irqrestore(&nc->lock, flags);
return 0; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment