@@ -24,6 +24,9 @@ static void ionic_watchdog_cb(struct timer_list *t)
24
24
return ;
25
25
26
26
hb = ionic_heartbeat_check (ionic );
27
+ dev_dbg (ionic -> dev , "%s: hb %d running %d UP %d\n" ,
28
+ __func__ , hb , netif_running (lif -> netdev ),
29
+ test_bit (IONIC_LIF_F_UP , lif -> state ));
27
30
28
31
if (hb >= 0 &&
29
32
!test_bit (IONIC_LIF_F_FW_RESET , lif -> state ))
@@ -91,9 +94,17 @@ int ionic_dev_setup(struct ionic *ionic)
91
94
return - EFAULT ;
92
95
}
93
96
94
- idev -> last_fw_status = 0xff ;
95
97
timer_setup (& ionic -> watchdog_timer , ionic_watchdog_cb , 0 );
96
98
ionic -> watchdog_period = IONIC_WATCHDOG_SECS * HZ ;
99
+
100
+ /* set times to ensure the first check will proceed */
101
+ atomic_long_set (& idev -> last_check_time , jiffies - 2 * HZ );
102
+ idev -> last_hb_time = jiffies - 2 * ionic -> watchdog_period ;
103
+ /* init as ready, so no transition if the first check succeeds */
104
+ idev -> last_fw_hb = 0 ;
105
+ idev -> fw_hb_ready = true;
106
+ idev -> fw_status_ready = true;
107
+
97
108
mod_timer (& ionic -> watchdog_timer ,
98
109
round_jiffies (jiffies + ionic -> watchdog_period ));
99
110
@@ -107,29 +118,38 @@ int ionic_dev_setup(struct ionic *ionic)
107
118
int ionic_heartbeat_check (struct ionic * ionic )
108
119
{
109
120
struct ionic_dev * idev = & ionic -> idev ;
110
- unsigned long hb_time ;
121
+ unsigned long check_time , last_check_time ;
122
+ bool fw_status_ready , fw_hb_ready ;
111
123
u8 fw_status ;
112
- u32 hb ;
124
+ u32 fw_hb ;
113
125
114
- /* wait a little more than one second before testing again */
115
- hb_time = jiffies ;
116
- if (time_before (hb_time , (idev -> last_hb_time + ionic -> watchdog_period )))
126
+ /* wait a least one second before testing again */
127
+ check_time = jiffies ;
128
+ last_check_time = atomic_long_read (& idev -> last_check_time );
129
+ do_check_time :
130
+ if (time_before (check_time , last_check_time + HZ ))
117
131
return 0 ;
132
+ if (!atomic_long_try_cmpxchg_relaxed (& idev -> last_check_time ,
133
+ & last_check_time , check_time )) {
134
+ /* if called concurrently, only the first should proceed. */
135
+ dev_dbg (ionic -> dev , "%s: do_check_time again\n" , __func__ );
136
+ goto do_check_time ;
137
+ }
118
138
119
139
/* firmware is useful only if the running bit is set and
120
140
* fw_status != 0xff (bad PCI read)
121
141
*/
122
142
fw_status = ioread8 (& idev -> dev_info_regs -> fw_status );
123
- if (fw_status != 0xff )
124
- fw_status &= IONIC_FW_STS_F_RUNNING ; /* use only the run bit */
143
+ fw_status_ready = (fw_status != 0xff ) && (fw_status & IONIC_FW_STS_F_RUNNING );
125
144
126
145
/* is this a transition? */
127
- if (fw_status != idev -> last_fw_status &&
128
- idev -> last_fw_status != 0xff ) {
146
+ if (fw_status_ready != idev -> fw_status_ready ) {
129
147
struct ionic_lif * lif = ionic -> lif ;
130
148
bool trigger = false;
131
149
132
- if (!fw_status || fw_status == 0xff ) {
150
+ idev -> fw_status_ready = fw_status_ready ;
151
+
152
+ if (!fw_status_ready ) {
133
153
dev_info (ionic -> dev , "FW stopped %u\n" , fw_status );
134
154
if (lif && !test_bit (IONIC_LIF_F_FW_RESET , lif -> state ))
135
155
trigger = true;
@@ -143,44 +163,47 @@ int ionic_heartbeat_check(struct ionic *ionic)
143
163
struct ionic_deferred_work * work ;
144
164
145
165
work = kzalloc (sizeof (* work ), GFP_ATOMIC );
146
- if (!work ) {
147
- dev_err (ionic -> dev , "LIF reset trigger dropped\n" );
148
- } else {
166
+ if (work ) {
149
167
work -> type = IONIC_DW_TYPE_LIF_RESET ;
150
- if (fw_status & IONIC_FW_STS_F_RUNNING &&
151
- fw_status != 0xff )
152
- work -> fw_status = 1 ;
168
+ work -> fw_status = fw_status_ready ;
153
169
ionic_lif_deferred_enqueue (& lif -> deferred , work );
154
170
}
155
171
}
156
172
}
157
- idev -> last_fw_status = fw_status ;
158
173
159
- if (!fw_status || fw_status == 0xff )
174
+ if (!fw_status_ready )
160
175
return - ENXIO ;
161
176
162
- /* early FW has no heartbeat, else FW will return non-zero */
163
- hb = ioread32 ( & idev -> dev_info_regs -> fw_heartbeat ) ;
164
- if (! hb )
177
+ /* wait at least one watchdog period since the last heartbeat */
178
+ last_check_time = idev -> last_hb_time ;
179
+ if (time_before ( check_time , last_check_time + ionic -> watchdog_period ) )
165
180
return 0 ;
166
181
167
- /* are we stalled? */
168
- if (hb == idev -> last_hb ) {
169
- /* only complain once for each stall seen */
170
- if (idev -> last_hb_time != 1 ) {
171
- dev_info (ionic -> dev , "FW heartbeat stalled at %d\n" ,
172
- idev -> last_hb );
173
- idev -> last_hb_time = 1 ;
174
- }
182
+ fw_hb = ioread32 (& idev -> dev_info_regs -> fw_heartbeat );
183
+ fw_hb_ready = fw_hb != idev -> last_fw_hb ;
175
184
176
- return - ENXIO ;
185
+ /* early FW version had no heartbeat, so fake it */
186
+ if (!fw_hb_ready && !fw_hb )
187
+ fw_hb_ready = true;
188
+
189
+ dev_dbg (ionic -> dev , "%s: fw_hb %u last_fw_hb %u ready %u\n" ,
190
+ __func__ , fw_hb , idev -> last_fw_hb , fw_hb_ready );
191
+
192
+ idev -> last_fw_hb = fw_hb ;
193
+
194
+ /* log a transition */
195
+ if (fw_hb_ready != idev -> fw_hb_ready ) {
196
+ idev -> fw_hb_ready = fw_hb_ready ;
197
+ if (!fw_hb_ready )
198
+ dev_info (ionic -> dev , "FW heartbeat stalled at %d\n" , fw_hb );
199
+ else
200
+ dev_info (ionic -> dev , "FW heartbeat restored at %d\n" , fw_hb );
177
201
}
178
202
179
- if (idev -> last_hb_time == 1 )
180
- dev_info ( ionic -> dev , "FW heartbeat restored at %d\n" , hb ) ;
203
+ if (! fw_hb_ready )
204
+ return - ENXIO ;
181
205
182
- idev -> last_hb = hb ;
183
- idev -> last_hb_time = hb_time ;
206
+ idev -> last_hb_time = check_time ;
184
207
185
208
return 0 ;
186
209
}
0 commit comments