diff options
author | James Smart <James.Smart@Emulex.Com> | 2008-09-07 11:52:10 -0400 |
---|---|---|
committer | James Bottomley <James.Bottomley@HansenPartnership.com> | 2008-10-13 09:28:57 -0400 |
commit | ea2151b4e142fa2de0319d9dd80413a997bf435a (patch) | |
tree | 1daa395a0b8584431c1b739af2761a840d013985 /drivers/scsi/lpfc | |
parent | 977b5a0af6d22a1a0170057c19cde37eeac68acd (diff) |
[SCSI] lpfc 8.2.8 v2 : Add statistical reporting control and additional fc vendor events
Added support for new sysfs attributes: lpfc_stat_data_ctrl and
lpfc_max_scsicmpl_time. The attributes control statistical reporting
of io load.
Added support for new fc vendor events for error reporting.
Signed-off-by: James Smart <james.smart@emulex.com>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Diffstat (limited to 'drivers/scsi/lpfc')
-rw-r--r-- | drivers/scsi/lpfc/lpfc.h | 27 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_attr.c | 343 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_crtn.h | 6 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_ct.c | 1 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_debugfs.c | 1 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_disc.h | 20 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_els.c | 114 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_hbadisc.c | 143 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_init.c | 22 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_mbox.c | 1 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_mem.c | 1 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_nl.h | 163 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_nportdisc.c | 1 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_scsi.c | 279 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_scsi.h | 4 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_sli.c | 12 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_vport.c | 80 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_vport.h | 4 |
18 files changed, 1207 insertions, 15 deletions
diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h index 3a500d683065..60a9e6e9384b 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h | |||
@@ -40,6 +40,8 @@ struct lpfc_sli2_slim; | |||
40 | #define LPFC_MIN_TGT_QDEPTH 100 | 40 | #define LPFC_MIN_TGT_QDEPTH 100 |
41 | #define LPFC_MAX_TGT_QDEPTH 0xFFFF | 41 | #define LPFC_MAX_TGT_QDEPTH 0xFFFF |
42 | 42 | ||
43 | #define LPFC_MAX_BUCKET_COUNT 20 /* Maximum no. of buckets for stat data | ||
44 | collection. */ | ||
43 | /* | 45 | /* |
44 | * Following time intervals are used of adjusting SCSI device | 46 | * Following time intervals are used of adjusting SCSI device |
45 | * queue depths when there are driver resource error or Firmware | 47 | * queue depths when there are driver resource error or Firmware |
@@ -381,6 +383,8 @@ struct lpfc_vport { | |||
381 | struct lpfc_debugfs_trc *disc_trc; | 383 | struct lpfc_debugfs_trc *disc_trc; |
382 | atomic_t disc_trc_cnt; | 384 | atomic_t disc_trc_cnt; |
383 | #endif | 385 | #endif |
386 | uint8_t stat_data_enabled; | ||
387 | uint8_t stat_data_blocked; | ||
384 | }; | 388 | }; |
385 | 389 | ||
386 | struct hbq_s { | 390 | struct hbq_s { |
@@ -641,6 +645,17 @@ struct lpfc_hba { | |||
641 | uint32_t buffer_tag_count; | 645 | uint32_t buffer_tag_count; |
642 | int wait_4_mlo_maint_flg; | 646 | int wait_4_mlo_maint_flg; |
643 | wait_queue_head_t wait_4_mlo_m_q; | 647 | wait_queue_head_t wait_4_mlo_m_q; |
648 | /* data structure used for latency data collection */ | ||
649 | #define LPFC_NO_BUCKET 0 | ||
650 | #define LPFC_LINEAR_BUCKET 1 | ||
651 | #define LPFC_POWER2_BUCKET 2 | ||
652 | uint8_t bucket_type; | ||
653 | uint32_t bucket_base; | ||
654 | uint32_t bucket_step; | ||
655 | |||
656 | /* Maximum number of events that can be outstanding at any time*/ | ||
657 | #define LPFC_MAX_EVT_COUNT 512 | ||
658 | atomic_t fast_event_count; | ||
644 | }; | 659 | }; |
645 | 660 | ||
646 | static inline struct Scsi_Host * | 661 | static inline struct Scsi_Host * |
@@ -699,15 +714,3 @@ lpfc_sli_read_hs(struct lpfc_hba *phba) | |||
699 | return; | 714 | return; |
700 | } | 715 | } |
701 | 716 | ||
702 | #define FC_REG_DUMP_EVENT 0x10 /* Register for Dump events */ | ||
703 | #define FC_REG_TEMPERATURE_EVENT 0x20 /* Register for temperature | ||
704 | event */ | ||
705 | |||
706 | struct temp_event { | ||
707 | uint32_t event_type; | ||
708 | uint32_t event_code; | ||
709 | uint32_t data; | ||
710 | }; | ||
711 | #define LPFC_CRIT_TEMP 0x1 | ||
712 | #define LPFC_THRESHOLD_TEMP 0x2 | ||
713 | #define LPFC_NORMAL_TEMP 0x3 | ||
diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index 343b0b36ed22..aa3d6277581d 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c | |||
@@ -32,6 +32,7 @@ | |||
32 | 32 | ||
33 | #include "lpfc_hw.h" | 33 | #include "lpfc_hw.h" |
34 | #include "lpfc_sli.h" | 34 | #include "lpfc_sli.h" |
35 | #include "lpfc_nl.h" | ||
35 | #include "lpfc_disc.h" | 36 | #include "lpfc_disc.h" |
36 | #include "lpfc_scsi.h" | 37 | #include "lpfc_scsi.h" |
37 | #include "lpfc.h" | 38 | #include "lpfc.h" |
@@ -2183,6 +2184,335 @@ lpfc_param_store(topology) | |||
2183 | static DEVICE_ATTR(lpfc_topology, S_IRUGO | S_IWUSR, | 2184 | static DEVICE_ATTR(lpfc_topology, S_IRUGO | S_IWUSR, |
2184 | lpfc_topology_show, lpfc_topology_store); | 2185 | lpfc_topology_show, lpfc_topology_store); |
2185 | 2186 | ||
2187 | |||
2188 | /** | ||
2189 | * lpfc_stat_data_ctrl_store: write call back for lpfc_stat_data_ctrl | ||
2190 | * sysfs file. | ||
2191 | * @dev: Pointer to class device. | ||
2192 | * @buf: Data buffer. | ||
2193 | * @count: Size of the data buffer. | ||
2194 | * | ||
2195 | * This function get called when an user write to the lpfc_stat_data_ctrl | ||
2196 | * sysfs file. This function parse the command written to the sysfs file | ||
2197 | * and take appropriate action. These commands are used for controlling | ||
2198 | * driver statistical data collection. | ||
2199 | * Following are the command this function handles. | ||
2200 | * | ||
2201 | * setbucket <bucket_type> <base> <step> | ||
2202 | * = Set the latency buckets. | ||
2203 | * destroybucket = destroy all the buckets. | ||
2204 | * start = start data collection | ||
2205 | * stop = stop data collection | ||
2206 | * reset = reset the collected data | ||
2207 | **/ | ||
2208 | static ssize_t | ||
2209 | lpfc_stat_data_ctrl_store(struct device *dev, struct device_attribute *attr, | ||
2210 | const char *buf, size_t count) | ||
2211 | { | ||
2212 | struct Scsi_Host *shost = class_to_shost(dev); | ||
2213 | struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata; | ||
2214 | struct lpfc_hba *phba = vport->phba; | ||
2215 | #define LPFC_MAX_DATA_CTRL_LEN 1024 | ||
2216 | static char bucket_data[LPFC_MAX_DATA_CTRL_LEN]; | ||
2217 | unsigned long i; | ||
2218 | char *str_ptr, *token; | ||
2219 | struct lpfc_vport **vports; | ||
2220 | struct Scsi_Host *v_shost; | ||
2221 | char *bucket_type_str, *base_str, *step_str; | ||
2222 | unsigned long base, step, bucket_type; | ||
2223 | |||
2224 | if (!strncmp(buf, "setbucket", strlen("setbucket"))) { | ||
2225 | if (strlen(buf) > LPFC_MAX_DATA_CTRL_LEN) | ||
2226 | return -EINVAL; | ||
2227 | |||
2228 | strcpy(bucket_data, buf); | ||
2229 | str_ptr = &bucket_data[0]; | ||
2230 | /* Ignore this token - this is command token */ | ||
2231 | token = strsep(&str_ptr, "\t "); | ||
2232 | if (!token) | ||
2233 | return -EINVAL; | ||
2234 | |||
2235 | bucket_type_str = strsep(&str_ptr, "\t "); | ||
2236 | if (!bucket_type_str) | ||
2237 | return -EINVAL; | ||
2238 | |||
2239 | if (!strncmp(bucket_type_str, "linear", strlen("linear"))) | ||
2240 | bucket_type = LPFC_LINEAR_BUCKET; | ||
2241 | else if (!strncmp(bucket_type_str, "power2", strlen("power2"))) | ||
2242 | bucket_type = LPFC_POWER2_BUCKET; | ||
2243 | else | ||
2244 | return -EINVAL; | ||
2245 | |||
2246 | base_str = strsep(&str_ptr, "\t "); | ||
2247 | if (!base_str) | ||
2248 | return -EINVAL; | ||
2249 | base = simple_strtoul(base_str, NULL, 0); | ||
2250 | |||
2251 | step_str = strsep(&str_ptr, "\t "); | ||
2252 | if (!step_str) | ||
2253 | return -EINVAL; | ||
2254 | step = simple_strtoul(step_str, NULL, 0); | ||
2255 | if (!step) | ||
2256 | return -EINVAL; | ||
2257 | |||
2258 | /* Block the data collection for every vport */ | ||
2259 | vports = lpfc_create_vport_work_array(phba); | ||
2260 | if (vports == NULL) | ||
2261 | return -ENOMEM; | ||
2262 | |||
2263 | for (i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) { | ||
2264 | v_shost = lpfc_shost_from_vport(vports[i]); | ||
2265 | spin_lock_irq(v_shost->host_lock); | ||
2266 | /* Block and reset data collection */ | ||
2267 | vports[i]->stat_data_blocked = 1; | ||
2268 | if (vports[i]->stat_data_enabled) | ||
2269 | lpfc_vport_reset_stat_data(vports[i]); | ||
2270 | spin_unlock_irq(v_shost->host_lock); | ||
2271 | } | ||
2272 | |||
2273 | /* Set the bucket attributes */ | ||
2274 | phba->bucket_type = bucket_type; | ||
2275 | phba->bucket_base = base; | ||
2276 | phba->bucket_step = step; | ||
2277 | |||
2278 | for (i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) { | ||
2279 | v_shost = lpfc_shost_from_vport(vports[i]); | ||
2280 | |||
2281 | /* Unblock data collection */ | ||
2282 | spin_lock_irq(v_shost->host_lock); | ||
2283 | vports[i]->stat_data_blocked = 0; | ||
2284 | spin_unlock_irq(v_shost->host_lock); | ||
2285 | } | ||
2286 | lpfc_destroy_vport_work_array(phba, vports); | ||
2287 | return strlen(buf); | ||
2288 | } | ||
2289 | |||
2290 | if (!strncmp(buf, "destroybucket", strlen("destroybucket"))) { | ||
2291 | vports = lpfc_create_vport_work_array(phba); | ||
2292 | if (vports == NULL) | ||
2293 | return -ENOMEM; | ||
2294 | |||
2295 | for (i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) { | ||
2296 | v_shost = lpfc_shost_from_vport(vports[i]); | ||
2297 | spin_lock_irq(shost->host_lock); | ||
2298 | vports[i]->stat_data_blocked = 1; | ||
2299 | lpfc_free_bucket(vport); | ||
2300 | vport->stat_data_enabled = 0; | ||
2301 | vports[i]->stat_data_blocked = 0; | ||
2302 | spin_unlock_irq(shost->host_lock); | ||
2303 | } | ||
2304 | lpfc_destroy_vport_work_array(phba, vports); | ||
2305 | phba->bucket_type = LPFC_NO_BUCKET; | ||
2306 | phba->bucket_base = 0; | ||
2307 | phba->bucket_step = 0; | ||
2308 | return strlen(buf); | ||
2309 | } | ||
2310 | |||
2311 | if (!strncmp(buf, "start", strlen("start"))) { | ||
2312 | /* If no buckets configured return error */ | ||
2313 | if (phba->bucket_type == LPFC_NO_BUCKET) | ||
2314 | return -EINVAL; | ||
2315 | spin_lock_irq(shost->host_lock); | ||
2316 | if (vport->stat_data_enabled) { | ||
2317 | spin_unlock_irq(shost->host_lock); | ||
2318 | return strlen(buf); | ||
2319 | } | ||
2320 | lpfc_alloc_bucket(vport); | ||
2321 | vport->stat_data_enabled = 1; | ||
2322 | spin_unlock_irq(shost->host_lock); | ||
2323 | return strlen(buf); | ||
2324 | } | ||
2325 | |||
2326 | if (!strncmp(buf, "stop", strlen("stop"))) { | ||
2327 | spin_lock_irq(shost->host_lock); | ||
2328 | if (vport->stat_data_enabled == 0) { | ||
2329 | spin_unlock_irq(shost->host_lock); | ||
2330 | return strlen(buf); | ||
2331 | } | ||
2332 | lpfc_free_bucket(vport); | ||
2333 | vport->stat_data_enabled = 0; | ||
2334 | spin_unlock_irq(shost->host_lock); | ||
2335 | return strlen(buf); | ||
2336 | } | ||
2337 | |||
2338 | if (!strncmp(buf, "reset", strlen("reset"))) { | ||
2339 | if ((phba->bucket_type == LPFC_NO_BUCKET) | ||
2340 | || !vport->stat_data_enabled) | ||
2341 | return strlen(buf); | ||
2342 | spin_lock_irq(shost->host_lock); | ||
2343 | vport->stat_data_blocked = 1; | ||
2344 | lpfc_vport_reset_stat_data(vport); | ||
2345 | vport->stat_data_blocked = 0; | ||
2346 | spin_unlock_irq(shost->host_lock); | ||
2347 | return strlen(buf); | ||
2348 | } | ||
2349 | return -EINVAL; | ||
2350 | } | ||
2351 | |||
2352 | |||
2353 | /** | ||
2354 | * lpfc_stat_data_ctrl_show: Read callback function for | ||
2355 | * lpfc_stat_data_ctrl sysfs file. | ||
2356 | * @dev: Pointer to class device object. | ||
2357 | * @buf: Data buffer. | ||
2358 | * | ||
2359 | * This function is the read call back function for | ||
2360 | * lpfc_stat_data_ctrl sysfs file. This function report the | ||
2361 | * current statistical data collection state. | ||
2362 | **/ | ||
2363 | static ssize_t | ||
2364 | lpfc_stat_data_ctrl_show(struct device *dev, struct device_attribute *attr, | ||
2365 | char *buf) | ||
2366 | { | ||
2367 | struct Scsi_Host *shost = class_to_shost(dev); | ||
2368 | struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata; | ||
2369 | struct lpfc_hba *phba = vport->phba; | ||
2370 | int index = 0; | ||
2371 | int i; | ||
2372 | char *bucket_type; | ||
2373 | unsigned long bucket_value; | ||
2374 | |||
2375 | switch (phba->bucket_type) { | ||
2376 | case LPFC_LINEAR_BUCKET: | ||
2377 | bucket_type = "linear"; | ||
2378 | break; | ||
2379 | case LPFC_POWER2_BUCKET: | ||
2380 | bucket_type = "power2"; | ||
2381 | break; | ||
2382 | default: | ||
2383 | bucket_type = "No Bucket"; | ||
2384 | break; | ||
2385 | } | ||
2386 | |||
2387 | sprintf(&buf[index], "Statistical Data enabled :%d, " | ||
2388 | "blocked :%d, Bucket type :%s, Bucket base :%d," | ||
2389 | " Bucket step :%d\nLatency Ranges :", | ||
2390 | vport->stat_data_enabled, vport->stat_data_blocked, | ||
2391 | bucket_type, phba->bucket_base, phba->bucket_step); | ||
2392 | index = strlen(buf); | ||
2393 | if (phba->bucket_type != LPFC_NO_BUCKET) { | ||
2394 | for (i = 0; i < LPFC_MAX_BUCKET_COUNT; i++) { | ||
2395 | if (phba->bucket_type == LPFC_LINEAR_BUCKET) | ||
2396 | bucket_value = phba->bucket_base + | ||
2397 | phba->bucket_step * i; | ||
2398 | else | ||
2399 | bucket_value = phba->bucket_base + | ||
2400 | (1 << i) * phba->bucket_step; | ||
2401 | |||
2402 | if (index + 10 > PAGE_SIZE) | ||
2403 | break; | ||
2404 | sprintf(&buf[index], "%08ld ", bucket_value); | ||
2405 | index = strlen(buf); | ||
2406 | } | ||
2407 | } | ||
2408 | sprintf(&buf[index], "\n"); | ||
2409 | return strlen(buf); | ||
2410 | } | ||
2411 | |||
2412 | /* | ||
2413 | * Sysfs attribute to control the statistical data collection. | ||
2414 | */ | ||
2415 | static DEVICE_ATTR(lpfc_stat_data_ctrl, S_IRUGO | S_IWUSR, | ||
2416 | lpfc_stat_data_ctrl_show, lpfc_stat_data_ctrl_store); | ||
2417 | |||
2418 | /* | ||
2419 | * lpfc_drvr_stat_data: sysfs attr to get driver statistical data. | ||
2420 | */ | ||
2421 | |||
2422 | /* | ||
2423 | * Each Bucket takes 11 characters and 1 new line + 17 bytes WWN | ||
2424 | * for each target. | ||
2425 | */ | ||
2426 | #define STAT_DATA_SIZE_PER_TARGET(NUM_BUCKETS) ((NUM_BUCKETS) * 11 + 18) | ||
2427 | #define MAX_STAT_DATA_SIZE_PER_TARGET \ | ||
2428 | STAT_DATA_SIZE_PER_TARGET(LPFC_MAX_BUCKET_COUNT) | ||
2429 | |||
2430 | |||
2431 | /** | ||
2432 | * sysfs_drvr_stat_data_read: Read callback function for lpfc_drvr_stat_data | ||
2433 | * sysfs attribute. | ||
2434 | * @kobj: Pointer to the kernel object | ||
2435 | * @bin_attr: Attribute object | ||
2436 | * @buff: Buffer pointer | ||
2437 | * @off: File offset | ||
2438 | * @count: Buffer size | ||
2439 | * | ||
2440 | * This function is the read call back function for lpfc_drvr_stat_data | ||
2441 | * sysfs file. This function export the statistical data to user | ||
2442 | * applications. | ||
2443 | **/ | ||
2444 | static ssize_t | ||
2445 | sysfs_drvr_stat_data_read(struct kobject *kobj, struct bin_attribute *bin_attr, | ||
2446 | char *buf, loff_t off, size_t count) | ||
2447 | { | ||
2448 | struct device *dev = container_of(kobj, struct device, | ||
2449 | kobj); | ||
2450 | struct Scsi_Host *shost = class_to_shost(dev); | ||
2451 | struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata; | ||
2452 | struct lpfc_hba *phba = vport->phba; | ||
2453 | int i = 0, index = 0; | ||
2454 | unsigned long nport_index; | ||
2455 | struct lpfc_nodelist *ndlp = NULL; | ||
2456 | nport_index = (unsigned long)off / | ||
2457 | MAX_STAT_DATA_SIZE_PER_TARGET; | ||
2458 | |||
2459 | if (!vport->stat_data_enabled || vport->stat_data_blocked | ||
2460 | || (phba->bucket_type == LPFC_NO_BUCKET)) | ||
2461 | return 0; | ||
2462 | |||
2463 | spin_lock_irq(shost->host_lock); | ||
2464 | list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) { | ||
2465 | if (!NLP_CHK_NODE_ACT(ndlp) || !ndlp->lat_data) | ||
2466 | continue; | ||
2467 | |||
2468 | if (nport_index > 0) { | ||
2469 | nport_index--; | ||
2470 | continue; | ||
2471 | } | ||
2472 | |||
2473 | if ((index + MAX_STAT_DATA_SIZE_PER_TARGET) | ||
2474 | > count) | ||
2475 | break; | ||
2476 | |||
2477 | if (!ndlp->lat_data) | ||
2478 | continue; | ||
2479 | |||
2480 | /* Print the WWN */ | ||
2481 | sprintf(&buf[index], "%02x%02x%02x%02x%02x%02x%02x%02x:", | ||
2482 | ndlp->nlp_portname.u.wwn[0], | ||
2483 | ndlp->nlp_portname.u.wwn[1], | ||
2484 | ndlp->nlp_portname.u.wwn[2], | ||
2485 | ndlp->nlp_portname.u.wwn[3], | ||
2486 | ndlp->nlp_portname.u.wwn[4], | ||
2487 | ndlp->nlp_portname.u.wwn[5], | ||
2488 | ndlp->nlp_portname.u.wwn[6], | ||
2489 | ndlp->nlp_portname.u.wwn[7]); | ||
2490 | |||
2491 | index = strlen(buf); | ||
2492 | |||
2493 | for (i = 0; i < LPFC_MAX_BUCKET_COUNT; i++) { | ||
2494 | sprintf(&buf[index], "%010u,", | ||
2495 | ndlp->lat_data[i].cmd_count); | ||
2496 | index = strlen(buf); | ||
2497 | } | ||
2498 | sprintf(&buf[index], "\n"); | ||
2499 | index = strlen(buf); | ||
2500 | } | ||
2501 | spin_unlock_irq(shost->host_lock); | ||
2502 | return index; | ||
2503 | } | ||
2504 | |||
2505 | static struct bin_attribute sysfs_drvr_stat_data_attr = { | ||
2506 | .attr = { | ||
2507 | .name = "lpfc_drvr_stat_data", | ||
2508 | .mode = S_IRUSR, | ||
2509 | .owner = THIS_MODULE, | ||
2510 | }, | ||
2511 | .size = LPFC_MAX_TARGET * MAX_STAT_DATA_SIZE_PER_TARGET, | ||
2512 | .read = sysfs_drvr_stat_data_read, | ||
2513 | .write = NULL, | ||
2514 | }; | ||
2515 | |||
2186 | /* | 2516 | /* |
2187 | # lpfc_link_speed: Link speed selection for initializing the Fibre Channel | 2517 | # lpfc_link_speed: Link speed selection for initializing the Fibre Channel |
2188 | # connection. | 2518 | # connection. |
@@ -2502,6 +2832,7 @@ struct device_attribute *lpfc_hba_attrs[] = { | |||
2502 | &dev_attr_lpfc_enable_hba_heartbeat, | 2832 | &dev_attr_lpfc_enable_hba_heartbeat, |
2503 | &dev_attr_lpfc_sg_seg_cnt, | 2833 | &dev_attr_lpfc_sg_seg_cnt, |
2504 | &dev_attr_lpfc_max_scsicmpl_time, | 2834 | &dev_attr_lpfc_max_scsicmpl_time, |
2835 | &dev_attr_lpfc_stat_data_ctrl, | ||
2505 | NULL, | 2836 | NULL, |
2506 | }; | 2837 | }; |
2507 | 2838 | ||
@@ -2524,6 +2855,8 @@ struct device_attribute *lpfc_vport_attrs[] = { | |||
2524 | &dev_attr_nport_evt_cnt, | 2855 | &dev_attr_nport_evt_cnt, |
2525 | &dev_attr_npiv_info, | 2856 | &dev_attr_npiv_info, |
2526 | &dev_attr_lpfc_enable_da_id, | 2857 | &dev_attr_lpfc_enable_da_id, |
2858 | &dev_attr_lpfc_max_scsicmpl_time, | ||
2859 | &dev_attr_lpfc_stat_data_ctrl, | ||
2527 | NULL, | 2860 | NULL, |
2528 | }; | 2861 | }; |
2529 | 2862 | ||
@@ -2958,7 +3291,14 @@ lpfc_alloc_sysfs_attr(struct lpfc_vport *vport) | |||
2958 | if (error) | 3291 | if (error) |
2959 | goto out_remove_ctlreg_attr; | 3292 | goto out_remove_ctlreg_attr; |
2960 | 3293 | ||
3294 | error = sysfs_create_bin_file(&shost->shost_dev.kobj, | ||
3295 | &sysfs_drvr_stat_data_attr); | ||
3296 | if (error) | ||
3297 | goto out_remove_mbox_attr; | ||
3298 | |||
2961 | return 0; | 3299 | return 0; |
3300 | out_remove_mbox_attr: | ||
3301 | sysfs_remove_bin_file(&shost->shost_dev.kobj, &sysfs_mbox_attr); | ||
2962 | out_remove_ctlreg_attr: | 3302 | out_remove_ctlreg_attr: |
2963 | sysfs_remove_bin_file(&shost->shost_dev.kobj, &sysfs_ctlreg_attr); | 3303 | sysfs_remove_bin_file(&shost->shost_dev.kobj, &sysfs_ctlreg_attr); |
2964 | out: | 3304 | out: |
@@ -2973,7 +3313,8 @@ void | |||
2973 | lpfc_free_sysfs_attr(struct lpfc_vport *vport) | 3313 | lpfc_free_sysfs_attr(struct lpfc_vport *vport) |
2974 | { | 3314 | { |
2975 | struct Scsi_Host *shost = lpfc_shost_from_vport(vport); | 3315 | struct Scsi_Host *shost = lpfc_shost_from_vport(vport); |
2976 | 3316 | sysfs_remove_bin_file(&shost->shost_dev.kobj, | |
3317 | &sysfs_drvr_stat_data_attr); | ||
2977 | sysfs_remove_bin_file(&shost->shost_dev.kobj, &sysfs_mbox_attr); | 3318 | sysfs_remove_bin_file(&shost->shost_dev.kobj, &sysfs_mbox_attr); |
2978 | sysfs_remove_bin_file(&shost->shost_dev.kobj, &sysfs_ctlreg_attr); | 3319 | sysfs_remove_bin_file(&shost->shost_dev.kobj, &sysfs_ctlreg_attr); |
2979 | } | 3320 | } |
diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h index 7d173f4a37f5..044ef4057d28 100644 --- a/drivers/scsi/lpfc/lpfc_crtn.h +++ b/drivers/scsi/lpfc/lpfc_crtn.h | |||
@@ -294,6 +294,12 @@ void lpfc_ramp_down_queue_handler(struct lpfc_hba *); | |||
294 | void lpfc_ramp_up_queue_handler(struct lpfc_hba *); | 294 | void lpfc_ramp_up_queue_handler(struct lpfc_hba *); |
295 | void lpfc_scsi_dev_block(struct lpfc_hba *); | 295 | void lpfc_scsi_dev_block(struct lpfc_hba *); |
296 | 296 | ||
297 | void | ||
298 | lpfc_send_els_failure_event(struct lpfc_hba *, struct lpfc_iocbq *, | ||
299 | struct lpfc_iocbq *); | ||
300 | struct lpfc_fast_path_event *lpfc_alloc_fast_evt(struct lpfc_hba *); | ||
301 | void lpfc_free_fast_evt(struct lpfc_hba *, struct lpfc_fast_path_event *); | ||
302 | |||
297 | #define ScsiResult(host_code, scsi_code) (((host_code) << 16) | scsi_code) | 303 | #define ScsiResult(host_code, scsi_code) (((host_code) << 16) | scsi_code) |
298 | #define HBA_EVENT_RSCN 5 | 304 | #define HBA_EVENT_RSCN 5 |
299 | #define HBA_EVENT_LINK_UP 2 | 305 | #define HBA_EVENT_LINK_UP 2 |
diff --git a/drivers/scsi/lpfc/lpfc_ct.c b/drivers/scsi/lpfc/lpfc_ct.c index 71cfee884b8a..26dae8bae2d1 100644 --- a/drivers/scsi/lpfc/lpfc_ct.c +++ b/drivers/scsi/lpfc/lpfc_ct.c | |||
@@ -34,6 +34,7 @@ | |||
34 | 34 | ||
35 | #include "lpfc_hw.h" | 35 | #include "lpfc_hw.h" |
36 | #include "lpfc_sli.h" | 36 | #include "lpfc_sli.h" |
37 | #include "lpfc_nl.h" | ||
37 | #include "lpfc_disc.h" | 38 | #include "lpfc_disc.h" |
38 | #include "lpfc_scsi.h" | 39 | #include "lpfc_scsi.h" |
39 | #include "lpfc.h" | 40 | #include "lpfc.h" |
diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c index f85b99a7c43d..771920bdde44 100644 --- a/drivers/scsi/lpfc/lpfc_debugfs.c +++ b/drivers/scsi/lpfc/lpfc_debugfs.c | |||
@@ -35,6 +35,7 @@ | |||
35 | 35 | ||
36 | #include "lpfc_hw.h" | 36 | #include "lpfc_hw.h" |
37 | #include "lpfc_sli.h" | 37 | #include "lpfc_sli.h" |
38 | #include "lpfc_nl.h" | ||
38 | #include "lpfc_disc.h" | 39 | #include "lpfc_disc.h" |
39 | #include "lpfc_scsi.h" | 40 | #include "lpfc_scsi.h" |
40 | #include "lpfc.h" | 41 | #include "lpfc.h" |
diff --git a/drivers/scsi/lpfc/lpfc_disc.h b/drivers/scsi/lpfc/lpfc_disc.h index ccf8f41f345e..f29e548a90d1 100644 --- a/drivers/scsi/lpfc/lpfc_disc.h +++ b/drivers/scsi/lpfc/lpfc_disc.h | |||
@@ -37,6 +37,7 @@ enum lpfc_work_type { | |||
37 | LPFC_EVT_KILL, | 37 | LPFC_EVT_KILL, |
38 | LPFC_EVT_ELS_RETRY, | 38 | LPFC_EVT_ELS_RETRY, |
39 | LPFC_EVT_DEV_LOSS, | 39 | LPFC_EVT_DEV_LOSS, |
40 | LPFC_EVT_FASTPATH_MGMT_EVT, | ||
40 | }; | 41 | }; |
41 | 42 | ||
42 | /* structure used to queue event to the discovery tasklet */ | 43 | /* structure used to queue event to the discovery tasklet */ |
@@ -47,6 +48,24 @@ struct lpfc_work_evt { | |||
47 | enum lpfc_work_type evt; | 48 | enum lpfc_work_type evt; |
48 | }; | 49 | }; |
49 | 50 | ||
51 | struct lpfc_scsi_check_condition_event; | ||
52 | struct lpfc_scsi_varqueuedepth_event; | ||
53 | struct lpfc_scsi_event_header; | ||
54 | struct lpfc_fabric_event_header; | ||
55 | struct lpfc_fcprdchkerr_event; | ||
56 | |||
57 | /* structure used for sending events from fast path */ | ||
58 | struct lpfc_fast_path_event { | ||
59 | struct lpfc_work_evt work_evt; | ||
60 | struct lpfc_vport *vport; | ||
61 | union { | ||
62 | struct lpfc_scsi_check_condition_event check_cond_evt; | ||
63 | struct lpfc_scsi_varqueuedepth_event queue_depth_evt; | ||
64 | struct lpfc_scsi_event_header scsi_evt; | ||
65 | struct lpfc_fabric_event_header fabric_evt; | ||
66 | struct lpfc_fcprdchkerr_event read_check_error; | ||
67 | } un; | ||
68 | }; | ||
50 | 69 | ||
51 | struct lpfc_nodelist { | 70 | struct lpfc_nodelist { |
52 | struct list_head nlp_listp; | 71 | struct list_head nlp_listp; |
@@ -91,6 +110,7 @@ struct lpfc_nodelist { | |||
91 | atomic_t cmd_pending; | 110 | atomic_t cmd_pending; |
92 | uint32_t cmd_qdepth; | 111 | uint32_t cmd_qdepth; |
93 | unsigned long last_change_time; | 112 | unsigned long last_change_time; |
113 | struct lpfc_scsicmd_bkt *lat_data; /* Latency data */ | ||
94 | }; | 114 | }; |
95 | 115 | ||
96 | /* Defines for nlp_flag (uint32) */ | 116 | /* Defines for nlp_flag (uint32) */ |
diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c index d0730e79c1a7..630bd28fb997 100644 --- a/drivers/scsi/lpfc/lpfc_els.c +++ b/drivers/scsi/lpfc/lpfc_els.c | |||
@@ -30,6 +30,7 @@ | |||
30 | 30 | ||
31 | #include "lpfc_hw.h" | 31 | #include "lpfc_hw.h" |
32 | #include "lpfc_sli.h" | 32 | #include "lpfc_sli.h" |
33 | #include "lpfc_nl.h" | ||
33 | #include "lpfc_disc.h" | 34 | #include "lpfc_disc.h" |
34 | #include "lpfc_scsi.h" | 35 | #include "lpfc_scsi.h" |
35 | #include "lpfc.h" | 36 | #include "lpfc.h" |
@@ -5085,6 +5086,116 @@ lpfc_els_flush_all_cmd(struct lpfc_hba *phba) | |||
5085 | } | 5086 | } |
5086 | 5087 | ||
5087 | /** | 5088 | /** |
5089 | * lpfc_send_els_failure_event: Posts an ELS command failure event. | ||
5090 | * @phba: Pointer to hba context object. | ||
5091 | * @cmdiocbp: Pointer to command iocb which reported error. | ||
5092 | * @rspiocbp: Pointer to response iocb which reported error. | ||
5093 | * | ||
5094 | * This function sends an event when there is an ELS command | ||
5095 | * failure. | ||
5096 | **/ | ||
5097 | void | ||
5098 | lpfc_send_els_failure_event(struct lpfc_hba *phba, | ||
5099 | struct lpfc_iocbq *cmdiocbp, | ||
5100 | struct lpfc_iocbq *rspiocbp) | ||
5101 | { | ||
5102 | struct lpfc_vport *vport = cmdiocbp->vport; | ||
5103 | struct Scsi_Host *shost = lpfc_shost_from_vport(vport); | ||
5104 | struct lpfc_lsrjt_event lsrjt_event; | ||
5105 | struct lpfc_fabric_event_header fabric_event; | ||
5106 | struct ls_rjt stat; | ||
5107 | struct lpfc_nodelist *ndlp; | ||
5108 | uint32_t *pcmd; | ||
5109 | |||
5110 | ndlp = cmdiocbp->context1; | ||
5111 | if (!ndlp || !NLP_CHK_NODE_ACT(ndlp)) | ||
5112 | return; | ||
5113 | |||
5114 | if (rspiocbp->iocb.ulpStatus == IOSTAT_LS_RJT) { | ||
5115 | lsrjt_event.header.event_type = FC_REG_ELS_EVENT; | ||
5116 | lsrjt_event.header.subcategory = LPFC_EVENT_LSRJT_RCV; | ||
5117 | memcpy(lsrjt_event.header.wwpn, &ndlp->nlp_portname, | ||
5118 | sizeof(struct lpfc_name)); | ||
5119 | memcpy(lsrjt_event.header.wwnn, &ndlp->nlp_nodename, | ||
5120 | sizeof(struct lpfc_name)); | ||
5121 | pcmd = (uint32_t *) (((struct lpfc_dmabuf *) | ||
5122 | cmdiocbp->context2)->virt); | ||
5123 | lsrjt_event.command = *pcmd; | ||
5124 | stat.un.lsRjtError = be32_to_cpu(rspiocbp->iocb.un.ulpWord[4]); | ||
5125 | lsrjt_event.reason_code = stat.un.b.lsRjtRsnCode; | ||
5126 | lsrjt_event.explanation = stat.un.b.lsRjtRsnCodeExp; | ||
5127 | fc_host_post_vendor_event(shost, | ||
5128 | fc_get_event_number(), | ||
5129 | sizeof(lsrjt_event), | ||
5130 | (char *)&lsrjt_event, | ||
5131 | SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX); | ||
5132 | return; | ||
5133 | } | ||
5134 | if ((rspiocbp->iocb.ulpStatus == IOSTAT_NPORT_BSY) || | ||
5135 | (rspiocbp->iocb.ulpStatus == IOSTAT_FABRIC_BSY)) { | ||
5136 | fabric_event.event_type = FC_REG_FABRIC_EVENT; | ||
5137 | if (rspiocbp->iocb.ulpStatus == IOSTAT_NPORT_BSY) | ||
5138 | fabric_event.subcategory = LPFC_EVENT_PORT_BUSY; | ||
5139 | else | ||
5140 | fabric_event.subcategory = LPFC_EVENT_FABRIC_BUSY; | ||
5141 | memcpy(fabric_event.wwpn, &ndlp->nlp_portname, | ||
5142 | sizeof(struct lpfc_name)); | ||
5143 | memcpy(fabric_event.wwnn, &ndlp->nlp_nodename, | ||
5144 | sizeof(struct lpfc_name)); | ||
5145 | fc_host_post_vendor_event(shost, | ||
5146 | fc_get_event_number(), | ||
5147 | sizeof(fabric_event), | ||
5148 | (char *)&fabric_event, | ||
5149 | SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX); | ||
5150 | return; | ||
5151 | } | ||
5152 | |||
5153 | } | ||
5154 | |||
5155 | /** | ||
5156 | * lpfc_send_els_event: Posts unsolicited els event. | ||
5157 | * @vport: Pointer to vport object. | ||
5158 | * @ndlp: Pointer FC node object. | ||
5159 | * @cmd: ELS command code. | ||
5160 | * | ||
5161 | * This function posts an event when there is an incoming | ||
5162 | * unsolicited ELS command. | ||
5163 | **/ | ||
5164 | static void | ||
5165 | lpfc_send_els_event(struct lpfc_vport *vport, | ||
5166 | struct lpfc_nodelist *ndlp, | ||
5167 | uint32_t cmd) | ||
5168 | { | ||
5169 | struct lpfc_els_event_header els_data; | ||
5170 | struct Scsi_Host *shost = lpfc_shost_from_vport(vport); | ||
5171 | |||
5172 | els_data.event_type = FC_REG_ELS_EVENT; | ||
5173 | switch (cmd) { | ||
5174 | case ELS_CMD_PLOGI: | ||
5175 | els_data.subcategory = LPFC_EVENT_PLOGI_RCV; | ||
5176 | break; | ||
5177 | case ELS_CMD_PRLO: | ||
5178 | els_data.subcategory = LPFC_EVENT_PRLO_RCV; | ||
5179 | break; | ||
5180 | case ELS_CMD_ADISC: | ||
5181 | els_data.subcategory = LPFC_EVENT_ADISC_RCV; | ||
5182 | break; | ||
5183 | default: | ||
5184 | return; | ||
5185 | } | ||
5186 | memcpy(els_data.wwpn, &ndlp->nlp_portname, sizeof(struct lpfc_name)); | ||
5187 | memcpy(els_data.wwnn, &ndlp->nlp_nodename, sizeof(struct lpfc_name)); | ||
5188 | fc_host_post_vendor_event(shost, | ||
5189 | fc_get_event_number(), | ||
5190 | sizeof(els_data), | ||
5191 | (char *)&els_data, | ||
5192 | SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX); | ||
5193 | |||
5194 | return; | ||
5195 | } | ||
5196 | |||
5197 | |||
5198 | /** | ||
5088 | * lpfc_els_unsol_buffer: Process an unsolicited event data buffer. | 5199 | * lpfc_els_unsol_buffer: Process an unsolicited event data buffer. |
5089 | * @phba: pointer to lpfc hba data structure. | 5200 | * @phba: pointer to lpfc hba data structure. |
5090 | * @pring: pointer to a SLI ring. | 5201 | * @pring: pointer to a SLI ring. |
@@ -5185,6 +5296,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, | |||
5185 | phba->fc_stat.elsRcvPLOGI++; | 5296 | phba->fc_stat.elsRcvPLOGI++; |
5186 | ndlp = lpfc_plogi_confirm_nport(phba, payload, ndlp); | 5297 | ndlp = lpfc_plogi_confirm_nport(phba, payload, ndlp); |
5187 | 5298 | ||
5299 | lpfc_send_els_event(vport, ndlp, cmd); | ||
5188 | if (vport->port_state < LPFC_DISC_AUTH) { | 5300 | if (vport->port_state < LPFC_DISC_AUTH) { |
5189 | if (!(phba->pport->fc_flag & FC_PT2PT) || | 5301 | if (!(phba->pport->fc_flag & FC_PT2PT) || |
5190 | (phba->pport->fc_flag & FC_PT2PT_PLOGI)) { | 5302 | (phba->pport->fc_flag & FC_PT2PT_PLOGI)) { |
@@ -5234,6 +5346,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, | |||
5234 | did, vport->port_state, ndlp->nlp_flag); | 5346 | did, vport->port_state, ndlp->nlp_flag); |
5235 | 5347 | ||
5236 | phba->fc_stat.elsRcvPRLO++; | 5348 | phba->fc_stat.elsRcvPRLO++; |
5349 | lpfc_send_els_event(vport, ndlp, cmd); | ||
5237 | if (vport->port_state < LPFC_DISC_AUTH) { | 5350 | if (vport->port_state < LPFC_DISC_AUTH) { |
5238 | rjt_err = LSRJT_UNABLE_TPC; | 5351 | rjt_err = LSRJT_UNABLE_TPC; |
5239 | break; | 5352 | break; |
@@ -5251,6 +5364,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, | |||
5251 | "RCV ADISC: did:x%x/ste:x%x flg:x%x", | 5364 | "RCV ADISC: did:x%x/ste:x%x flg:x%x", |
5252 | did, vport->port_state, ndlp->nlp_flag); | 5365 | did, vport->port_state, ndlp->nlp_flag); |
5253 | 5366 | ||
5367 | lpfc_send_els_event(vport, ndlp, cmd); | ||
5254 | phba->fc_stat.elsRcvADISC++; | 5368 | phba->fc_stat.elsRcvADISC++; |
5255 | if (vport->port_state < LPFC_DISC_AUTH) { | 5369 | if (vport->port_state < LPFC_DISC_AUTH) { |
5256 | rjt_err = LSRJT_UNABLE_TPC; | 5370 | rjt_err = LSRJT_UNABLE_TPC; |
diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c index 887a5283605f..a1a70d9ffc2a 100644 --- a/drivers/scsi/lpfc/lpfc_hbadisc.c +++ b/drivers/scsi/lpfc/lpfc_hbadisc.c | |||
@@ -30,6 +30,7 @@ | |||
30 | #include <scsi/scsi_transport_fc.h> | 30 | #include <scsi/scsi_transport_fc.h> |
31 | 31 | ||
32 | #include "lpfc_hw.h" | 32 | #include "lpfc_hw.h" |
33 | #include "lpfc_nl.h" | ||
33 | #include "lpfc_disc.h" | 34 | #include "lpfc_disc.h" |
34 | #include "lpfc_sli.h" | 35 | #include "lpfc_sli.h" |
35 | #include "lpfc_scsi.h" | 36 | #include "lpfc_scsi.h" |
@@ -274,6 +275,124 @@ lpfc_dev_loss_tmo_handler(struct lpfc_nodelist *ndlp) | |||
274 | lpfc_disc_state_machine(vport, ndlp, NULL, NLP_EVT_DEVICE_RM); | 275 | lpfc_disc_state_machine(vport, ndlp, NULL, NLP_EVT_DEVICE_RM); |
275 | } | 276 | } |
276 | 277 | ||
278 | /** | ||
279 | * lpfc_alloc_fast_evt: Allocates data structure for posting event. | ||
280 | * @phba: Pointer to hba context object. | ||
281 | * | ||
282 | * This function is called from the functions which need to post | ||
283 | * events from interrupt context. This function allocates data | ||
284 | * structure required for posting event. It also keeps track of | ||
285 | * number of events pending and prevent event storm when there are | ||
286 | * too many events. | ||
287 | **/ | ||
288 | struct lpfc_fast_path_event * | ||
289 | lpfc_alloc_fast_evt(struct lpfc_hba *phba) { | ||
290 | struct lpfc_fast_path_event *ret; | ||
291 | |||
292 | /* If there are lot of fast event do not exhaust memory due to this */ | ||
293 | if (atomic_read(&phba->fast_event_count) > LPFC_MAX_EVT_COUNT) | ||
294 | return NULL; | ||
295 | |||
296 | ret = kzalloc(sizeof(struct lpfc_fast_path_event), | ||
297 | GFP_ATOMIC); | ||
298 | if (ret) | ||
299 | atomic_inc(&phba->fast_event_count); | ||
300 | INIT_LIST_HEAD(&ret->work_evt.evt_listp); | ||
301 | ret->work_evt.evt = LPFC_EVT_FASTPATH_MGMT_EVT; | ||
302 | return ret; | ||
303 | } | ||
304 | |||
305 | /** | ||
306 | * lpfc_free_fast_evt: Frees event data structure. | ||
307 | * @phba: Pointer to hba context object. | ||
308 | * @evt: Event object which need to be freed. | ||
309 | * | ||
310 | * This function frees the data structure required for posting | ||
311 | * events. | ||
312 | **/ | ||
313 | void | ||
314 | lpfc_free_fast_evt(struct lpfc_hba *phba, | ||
315 | struct lpfc_fast_path_event *evt) { | ||
316 | |||
317 | atomic_dec(&phba->fast_event_count); | ||
318 | kfree(evt); | ||
319 | } | ||
320 | |||
321 | /** | ||
322 | * lpfc_send_fastpath_evt: Posts events generated from fast path. | ||
323 | * @phba: Pointer to hba context object. | ||
324 | * @evtp: Event data structure. | ||
325 | * | ||
326 | * This function is called from worker thread, when the interrupt | ||
327 | * context need to post an event. This function posts the event | ||
328 | * to fc transport netlink interface. | ||
329 | **/ | ||
330 | static void | ||
331 | lpfc_send_fastpath_evt(struct lpfc_hba *phba, | ||
332 | struct lpfc_work_evt *evtp) | ||
333 | { | ||
334 | unsigned long evt_category, evt_sub_category; | ||
335 | struct lpfc_fast_path_event *fast_evt_data; | ||
336 | char *evt_data; | ||
337 | uint32_t evt_data_size; | ||
338 | struct Scsi_Host *shost; | ||
339 | |||
340 | fast_evt_data = container_of(evtp, struct lpfc_fast_path_event, | ||
341 | work_evt); | ||
342 | |||
343 | evt_category = (unsigned long) fast_evt_data->un.fabric_evt.event_type; | ||
344 | evt_sub_category = (unsigned long) fast_evt_data->un. | ||
345 | fabric_evt.subcategory; | ||
346 | shost = lpfc_shost_from_vport(fast_evt_data->vport); | ||
347 | if (evt_category == FC_REG_FABRIC_EVENT) { | ||
348 | if (evt_sub_category == LPFC_EVENT_FCPRDCHKERR) { | ||
349 | evt_data = (char *) &fast_evt_data->un.read_check_error; | ||
350 | evt_data_size = sizeof(fast_evt_data->un. | ||
351 | read_check_error); | ||
352 | } else if ((evt_sub_category == LPFC_EVENT_FABRIC_BUSY) || | ||
353 | (evt_sub_category == IOSTAT_NPORT_BSY)) { | ||
354 | evt_data = (char *) &fast_evt_data->un.fabric_evt; | ||
355 | evt_data_size = sizeof(fast_evt_data->un.fabric_evt); | ||
356 | } else { | ||
357 | lpfc_free_fast_evt(phba, fast_evt_data); | ||
358 | return; | ||
359 | } | ||
360 | } else if (evt_category == FC_REG_SCSI_EVENT) { | ||
361 | switch (evt_sub_category) { | ||
362 | case LPFC_EVENT_QFULL: | ||
363 | case LPFC_EVENT_DEVBSY: | ||
364 | evt_data = (char *) &fast_evt_data->un.scsi_evt; | ||
365 | evt_data_size = sizeof(fast_evt_data->un.scsi_evt); | ||
366 | break; | ||
367 | case LPFC_EVENT_CHECK_COND: | ||
368 | evt_data = (char *) &fast_evt_data->un.check_cond_evt; | ||
369 | evt_data_size = sizeof(fast_evt_data->un. | ||
370 | check_cond_evt); | ||
371 | break; | ||
372 | case LPFC_EVENT_VARQUEDEPTH: | ||
373 | evt_data = (char *) &fast_evt_data->un.queue_depth_evt; | ||
374 | evt_data_size = sizeof(fast_evt_data->un. | ||
375 | queue_depth_evt); | ||
376 | break; | ||
377 | default: | ||
378 | lpfc_free_fast_evt(phba, fast_evt_data); | ||
379 | return; | ||
380 | } | ||
381 | } else { | ||
382 | lpfc_free_fast_evt(phba, fast_evt_data); | ||
383 | return; | ||
384 | } | ||
385 | |||
386 | fc_host_post_vendor_event(shost, | ||
387 | fc_get_event_number(), | ||
388 | evt_data_size, | ||
389 | evt_data, | ||
390 | SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX); | ||
391 | |||
392 | lpfc_free_fast_evt(phba, fast_evt_data); | ||
393 | return; | ||
394 | } | ||
395 | |||
277 | static void | 396 | static void |
278 | lpfc_work_list_done(struct lpfc_hba *phba) | 397 | lpfc_work_list_done(struct lpfc_hba *phba) |
279 | { | 398 | { |
@@ -345,6 +464,10 @@ lpfc_work_list_done(struct lpfc_hba *phba) | |||
345 | lpfc_unblock_mgmt_io(phba); | 464 | lpfc_unblock_mgmt_io(phba); |
346 | complete((struct completion *)(evtp->evt_arg2)); | 465 | complete((struct completion *)(evtp->evt_arg2)); |
347 | break; | 466 | break; |
467 | case LPFC_EVT_FASTPATH_MGMT_EVT: | ||
468 | lpfc_send_fastpath_evt(phba, evtp); | ||
469 | free_evt = 0; | ||
470 | break; | ||
348 | } | 471 | } |
349 | if (free_evt) | 472 | if (free_evt) |
350 | kfree(evtp); | 473 | kfree(evtp); |
@@ -1601,6 +1724,22 @@ lpfc_nlp_state_cleanup(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, | |||
1601 | */ | 1724 | */ |
1602 | lpfc_register_remote_port(vport, ndlp); | 1725 | lpfc_register_remote_port(vport, ndlp); |
1603 | } | 1726 | } |
1727 | if ((new_state == NLP_STE_MAPPED_NODE) && | ||
1728 | (vport->stat_data_enabled)) { | ||
1729 | /* | ||
1730 | * A new target is discovered, if there is no buffer for | ||
1731 | * statistical data collection allocate buffer. | ||
1732 | */ | ||
1733 | ndlp->lat_data = kcalloc(LPFC_MAX_BUCKET_COUNT, | ||
1734 | sizeof(struct lpfc_scsicmd_bkt), | ||
1735 | GFP_KERNEL); | ||
1736 | |||
1737 | if (!ndlp->lat_data) | ||
1738 | lpfc_printf_vlog(vport, KERN_ERR, LOG_NODE, | ||
1739 | "0286 lpfc_nlp_state_cleanup failed to " | ||
1740 | "allocate statistical data buffer DID " | ||
1741 | "0x%x\n", ndlp->nlp_DID); | ||
1742 | } | ||
1604 | /* | 1743 | /* |
1605 | * if we added to Mapped list, but the remote port | 1744 | * if we added to Mapped list, but the remote port |
1606 | * registration failed or assigned a target id outside | 1745 | * registration failed or assigned a target id outside |
@@ -3029,8 +3168,10 @@ lpfc_nlp_release(struct kref *kref) | |||
3029 | spin_unlock_irqrestore(&phba->ndlp_lock, flags); | 3168 | spin_unlock_irqrestore(&phba->ndlp_lock, flags); |
3030 | 3169 | ||
3031 | /* free ndlp memory for final ndlp release */ | 3170 | /* free ndlp memory for final ndlp release */ |
3032 | if (NLP_CHK_FREE_REQ(ndlp)) | 3171 | if (NLP_CHK_FREE_REQ(ndlp)) { |
3172 | kfree(ndlp->lat_data); | ||
3033 | mempool_free(ndlp, ndlp->vport->phba->nlp_mem_pool); | 3173 | mempool_free(ndlp, ndlp->vport->phba->nlp_mem_pool); |
3174 | } | ||
3034 | } | 3175 | } |
3035 | 3176 | ||
3036 | /* This routine bumps the reference count for a ndlp structure to ensure | 3177 | /* This routine bumps the reference count for a ndlp structure to ensure |
diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 49577d5f130f..909be3301bba 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c | |||
@@ -36,6 +36,7 @@ | |||
36 | 36 | ||
37 | #include "lpfc_hw.h" | 37 | #include "lpfc_hw.h" |
38 | #include "lpfc_sli.h" | 38 | #include "lpfc_sli.h" |
39 | #include "lpfc_nl.h" | ||
39 | #include "lpfc_disc.h" | 40 | #include "lpfc_disc.h" |
40 | #include "lpfc_scsi.h" | 41 | #include "lpfc_scsi.h" |
41 | #include "lpfc.h" | 42 | #include "lpfc.h" |
@@ -815,6 +816,7 @@ lpfc_handle_eratt(struct lpfc_hba *phba) | |||
815 | unsigned long temperature; | 816 | unsigned long temperature; |
816 | struct temp_event temp_event_data; | 817 | struct temp_event temp_event_data; |
817 | struct Scsi_Host *shost; | 818 | struct Scsi_Host *shost; |
819 | struct lpfc_board_event_header board_event; | ||
818 | 820 | ||
819 | /* If the pci channel is offline, ignore possible errors, | 821 | /* If the pci channel is offline, ignore possible errors, |
820 | * since we cannot communicate with the pci card anyway. */ | 822 | * since we cannot communicate with the pci card anyway. */ |
@@ -824,6 +826,16 @@ lpfc_handle_eratt(struct lpfc_hba *phba) | |||
824 | if (!phba->cfg_enable_hba_reset) | 826 | if (!phba->cfg_enable_hba_reset) |
825 | return; | 827 | return; |
826 | 828 | ||
829 | /* Send an internal error event to mgmt application */ | ||
830 | board_event.event_type = FC_REG_BOARD_EVENT; | ||
831 | board_event.subcategory = LPFC_EVENT_PORTINTERR; | ||
832 | shost = lpfc_shost_from_vport(phba->pport); | ||
833 | fc_host_post_vendor_event(shost, fc_get_event_number(), | ||
834 | sizeof(board_event), | ||
835 | (char *) &board_event, | ||
836 | SCSI_NL_VID_TYPE_PCI | ||
837 | | PCI_VENDOR_ID_EMULEX); | ||
838 | |||
827 | if (phba->work_hs & HS_FFER6) { | 839 | if (phba->work_hs & HS_FFER6) { |
828 | /* Re-establishing Link */ | 840 | /* Re-establishing Link */ |
829 | lpfc_printf_log(phba, KERN_INFO, LOG_LINK_EVENT, | 841 | lpfc_printf_log(phba, KERN_INFO, LOG_LINK_EVENT, |
@@ -2345,6 +2357,7 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid) | |||
2345 | int i, hbq_count; | 2357 | int i, hbq_count; |
2346 | uint16_t iotag; | 2358 | uint16_t iotag; |
2347 | int bars = pci_select_bars(pdev, IORESOURCE_MEM); | 2359 | int bars = pci_select_bars(pdev, IORESOURCE_MEM); |
2360 | struct lpfc_adapter_event_header adapter_event; | ||
2348 | 2361 | ||
2349 | if (pci_enable_device_mem(pdev)) | 2362 | if (pci_enable_device_mem(pdev)) |
2350 | goto out; | 2363 | goto out; |
@@ -2355,6 +2368,7 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid) | |||
2355 | if (!phba) | 2368 | if (!phba) |
2356 | goto out_release_regions; | 2369 | goto out_release_regions; |
2357 | 2370 | ||
2371 | atomic_set(&phba->fast_event_count, 0); | ||
2358 | spin_lock_init(&phba->hbalock); | 2372 | spin_lock_init(&phba->hbalock); |
2359 | 2373 | ||
2360 | /* Initialize ndlp management spinlock */ | 2374 | /* Initialize ndlp management spinlock */ |
@@ -2626,6 +2640,14 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid) | |||
2626 | 2640 | ||
2627 | lpfc_printf_log(phba, KERN_INFO, LOG_INIT, | 2641 | lpfc_printf_log(phba, KERN_INFO, LOG_INIT, |
2628 | "0428 Perform SCSI scan\n"); | 2642 | "0428 Perform SCSI scan\n"); |
2643 | /* Send board arrival event to upper layer */ | ||
2644 | adapter_event.event_type = FC_REG_ADAPTER_EVENT; | ||
2645 | adapter_event.subcategory = LPFC_EVENT_ARRIVAL; | ||
2646 | fc_host_post_vendor_event(shost, fc_get_event_number(), | ||
2647 | sizeof(adapter_event), | ||
2648 | (char *) &adapter_event, | ||
2649 | SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX); | ||
2650 | |||
2629 | scsi_scan_host(shost); | 2651 | scsi_scan_host(shost); |
2630 | 2652 | ||
2631 | return 0; | 2653 | return 0; |
diff --git a/drivers/scsi/lpfc/lpfc_mbox.c b/drivers/scsi/lpfc/lpfc_mbox.c index 65bc8e1a5f7d..7465fe746fe9 100644 --- a/drivers/scsi/lpfc/lpfc_mbox.c +++ b/drivers/scsi/lpfc/lpfc_mbox.c | |||
@@ -30,6 +30,7 @@ | |||
30 | 30 | ||
31 | #include "lpfc_hw.h" | 31 | #include "lpfc_hw.h" |
32 | #include "lpfc_sli.h" | 32 | #include "lpfc_sli.h" |
33 | #include "lpfc_nl.h" | ||
33 | #include "lpfc_disc.h" | 34 | #include "lpfc_disc.h" |
34 | #include "lpfc_scsi.h" | 35 | #include "lpfc_scsi.h" |
35 | #include "lpfc.h" | 36 | #include "lpfc.h" |
diff --git a/drivers/scsi/lpfc/lpfc_mem.c b/drivers/scsi/lpfc/lpfc_mem.c index 50d9136a6e04..a4bba2069248 100644 --- a/drivers/scsi/lpfc/lpfc_mem.c +++ b/drivers/scsi/lpfc/lpfc_mem.c | |||
@@ -30,6 +30,7 @@ | |||
30 | 30 | ||
31 | #include "lpfc_hw.h" | 31 | #include "lpfc_hw.h" |
32 | #include "lpfc_sli.h" | 32 | #include "lpfc_sli.h" |
33 | #include "lpfc_nl.h" | ||
33 | #include "lpfc_disc.h" | 34 | #include "lpfc_disc.h" |
34 | #include "lpfc_scsi.h" | 35 | #include "lpfc_scsi.h" |
35 | #include "lpfc.h" | 36 | #include "lpfc.h" |
diff --git a/drivers/scsi/lpfc/lpfc_nl.h b/drivers/scsi/lpfc/lpfc_nl.h new file mode 100644 index 000000000000..1accb5a9f4e6 --- /dev/null +++ b/drivers/scsi/lpfc/lpfc_nl.h | |||
@@ -0,0 +1,163 @@ | |||
1 | /******************************************************************* | ||
2 | * This file is part of the Emulex Linux Device Driver for * | ||
3 | * Fibre Channel Host Bus Adapters. * | ||
4 | * Copyright (C) 2008 Emulex. All rights reserved. * | ||
5 | * EMULEX and SLI are trademarks of Emulex. * | ||
6 | * www.emulex.com * | ||
7 | * * | ||
8 | * This program is free software; you can redistribute it and/or * | ||
9 | * modify it under the terms of version 2 of the GNU General * | ||
10 | * Public License as published by the Free Software Foundation. * | ||
11 | * This program is distributed in the hope that it will be useful. * | ||
12 | * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND * | ||
13 | * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, * | ||
14 | * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE * | ||
15 | * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD * | ||
16 | * TO BE LEGALLY INVALID. See the GNU General Public License for * | ||
17 | * more details, a copy of which can be found in the file COPYING * | ||
18 | * included with this package. * | ||
19 | *******************************************************************/ | ||
20 | |||
21 | /* Event definitions for RegisterForEvent */ | ||
22 | #define FC_REG_LINK_EVENT 0x0001 /* link up / down events */ | ||
23 | #define FC_REG_RSCN_EVENT 0x0002 /* RSCN events */ | ||
24 | #define FC_REG_CT_EVENT 0x0004 /* CT request events */ | ||
25 | #define FC_REG_DUMP_EVENT 0x0008 /* Dump events */ | ||
26 | #define FC_REG_TEMPERATURE_EVENT 0x0010 /* temperature events */ | ||
27 | #define FC_REG_ELS_EVENT 0x0020 /* lpfc els events */ | ||
28 | #define FC_REG_FABRIC_EVENT 0x0040 /* lpfc fabric events */ | ||
29 | #define FC_REG_SCSI_EVENT 0x0080 /* lpfc scsi events */ | ||
30 | #define FC_REG_BOARD_EVENT 0x0100 /* lpfc board events */ | ||
31 | #define FC_REG_ADAPTER_EVENT 0x0200 /* lpfc adapter events */ | ||
32 | #define FC_REG_EVENT_MASK (FC_REG_LINK_EVENT | \ | ||
33 | FC_REG_RSCN_EVENT | \ | ||
34 | FC_REG_CT_EVENT | \ | ||
35 | FC_REG_DUMP_EVENT | \ | ||
36 | FC_REG_TEMPERATURE_EVENT | \ | ||
37 | FC_REG_ELS_EVENT | \ | ||
38 | FC_REG_FABRIC_EVENT | \ | ||
39 | FC_REG_SCSI_EVENT | \ | ||
40 | FC_REG_BOARD_EVENT | \ | ||
41 | FC_REG_ADAPTER_EVENT) | ||
42 | /* Temperature events */ | ||
43 | #define LPFC_CRIT_TEMP 0x1 | ||
44 | #define LPFC_THRESHOLD_TEMP 0x2 | ||
45 | #define LPFC_NORMAL_TEMP 0x3 | ||
46 | /* | ||
47 | * All net link event payloads will begin with and event type | ||
48 | * and subcategory. The event type must come first. | ||
49 | * The subcategory further defines the data that follows in the rest | ||
50 | * of the payload. Each category will have its own unique header plus | ||
51 | * any addtional data unique to the subcategory. | ||
52 | * The payload sent via the fc transport is one-way driver->application. | ||
53 | */ | ||
54 | |||
55 | /* els event header */ | ||
56 | struct lpfc_els_event_header { | ||
57 | uint32_t event_type; | ||
58 | uint32_t subcategory; | ||
59 | uint8_t wwpn[8]; | ||
60 | uint8_t wwnn[8]; | ||
61 | }; | ||
62 | |||
63 | /* subcategory codes for FC_REG_ELS_EVENT */ | ||
64 | #define LPFC_EVENT_PLOGI_RCV 0x01 | ||
65 | #define LPFC_EVENT_PRLO_RCV 0x02 | ||
66 | #define LPFC_EVENT_ADISC_RCV 0x04 | ||
67 | #define LPFC_EVENT_LSRJT_RCV 0x08 | ||
68 | |||
69 | /* special els lsrjt event */ | ||
70 | struct lpfc_lsrjt_event { | ||
71 | struct lpfc_els_event_header header; | ||
72 | uint32_t command; | ||
73 | uint32_t reason_code; | ||
74 | uint32_t explanation; | ||
75 | }; | ||
76 | |||
77 | |||
78 | /* fabric event header */ | ||
79 | struct lpfc_fabric_event_header { | ||
80 | uint32_t event_type; | ||
81 | uint32_t subcategory; | ||
82 | uint8_t wwpn[8]; | ||
83 | uint8_t wwnn[8]; | ||
84 | }; | ||
85 | |||
86 | /* subcategory codes for FC_REG_FABRIC_EVENT */ | ||
87 | #define LPFC_EVENT_FABRIC_BUSY 0x01 | ||
88 | #define LPFC_EVENT_PORT_BUSY 0x02 | ||
89 | #define LPFC_EVENT_FCPRDCHKERR 0x04 | ||
90 | |||
91 | /* special case fabric fcprdchkerr event */ | ||
92 | struct lpfc_fcprdchkerr_event { | ||
93 | struct lpfc_fabric_event_header header; | ||
94 | uint32_t lun; | ||
95 | uint32_t opcode; | ||
96 | uint32_t fcpiparam; | ||
97 | }; | ||
98 | |||
99 | |||
100 | /* scsi event header */ | ||
101 | struct lpfc_scsi_event_header { | ||
102 | uint32_t event_type; | ||
103 | uint32_t subcategory; | ||
104 | uint32_t lun; | ||
105 | uint8_t wwpn[8]; | ||
106 | uint8_t wwnn[8]; | ||
107 | }; | ||
108 | |||
109 | /* subcategory codes for FC_REG_SCSI_EVENT */ | ||
110 | #define LPFC_EVENT_QFULL 0x0001 | ||
111 | #define LPFC_EVENT_DEVBSY 0x0002 | ||
112 | #define LPFC_EVENT_CHECK_COND 0x0004 | ||
113 | #define LPFC_EVENT_LUNRESET 0x0008 | ||
114 | #define LPFC_EVENT_TGTRESET 0x0010 | ||
115 | #define LPFC_EVENT_BUSRESET 0x0020 | ||
116 | #define LPFC_EVENT_VARQUEDEPTH 0x0040 | ||
117 | |||
118 | /* special case scsi varqueuedepth event */ | ||
119 | struct lpfc_scsi_varqueuedepth_event { | ||
120 | struct lpfc_scsi_event_header scsi_event; | ||
121 | uint32_t oldval; | ||
122 | uint32_t newval; | ||
123 | }; | ||
124 | |||
125 | /* special case scsi check condition event */ | ||
126 | struct lpfc_scsi_check_condition_event { | ||
127 | struct lpfc_scsi_event_header scsi_event; | ||
128 | uint8_t sense_key; | ||
129 | uint8_t asc; | ||
130 | uint8_t ascq; | ||
131 | }; | ||
132 | |||
133 | /* event codes for FC_REG_BOARD_EVENT */ | ||
134 | #define LPFC_EVENT_PORTINTERR 0x01 | ||
135 | |||
136 | /* board event header */ | ||
137 | struct lpfc_board_event_header { | ||
138 | uint32_t event_type; | ||
139 | uint32_t subcategory; | ||
140 | }; | ||
141 | |||
142 | |||
143 | /* event codes for FC_REG_ADAPTER_EVENT */ | ||
144 | #define LPFC_EVENT_ARRIVAL 0x01 | ||
145 | |||
146 | /* adapter event header */ | ||
147 | struct lpfc_adapter_event_header { | ||
148 | uint32_t event_type; | ||
149 | uint32_t subcategory; | ||
150 | }; | ||
151 | |||
152 | |||
153 | /* event codes for temp_event */ | ||
154 | #define LPFC_CRIT_TEMP 0x1 | ||
155 | #define LPFC_THRESHOLD_TEMP 0x2 | ||
156 | #define LPFC_NORMAL_TEMP 0x3 | ||
157 | |||
158 | struct temp_event { | ||
159 | uint32_t event_type; | ||
160 | uint32_t event_code; | ||
161 | uint32_t data; | ||
162 | }; | ||
163 | |||
diff --git a/drivers/scsi/lpfc/lpfc_nportdisc.c b/drivers/scsi/lpfc/lpfc_nportdisc.c index 83ffa75378e5..0c25d97acb42 100644 --- a/drivers/scsi/lpfc/lpfc_nportdisc.c +++ b/drivers/scsi/lpfc/lpfc_nportdisc.c | |||
@@ -30,6 +30,7 @@ | |||
30 | 30 | ||
31 | #include "lpfc_hw.h" | 31 | #include "lpfc_hw.h" |
32 | #include "lpfc_sli.h" | 32 | #include "lpfc_sli.h" |
33 | #include "lpfc_nl.h" | ||
33 | #include "lpfc_disc.h" | 34 | #include "lpfc_disc.h" |
34 | #include "lpfc_scsi.h" | 35 | #include "lpfc_scsi.h" |
35 | #include "lpfc.h" | 36 | #include "lpfc.h" |
diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index 7b17f52660b4..bd1867411821 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c | |||
@@ -32,6 +32,7 @@ | |||
32 | #include "lpfc_version.h" | 32 | #include "lpfc_version.h" |
33 | #include "lpfc_hw.h" | 33 | #include "lpfc_hw.h" |
34 | #include "lpfc_sli.h" | 34 | #include "lpfc_sli.h" |
35 | #include "lpfc_nl.h" | ||
35 | #include "lpfc_disc.h" | 36 | #include "lpfc_disc.h" |
36 | #include "lpfc_scsi.h" | 37 | #include "lpfc_scsi.h" |
37 | #include "lpfc.h" | 38 | #include "lpfc.h" |
@@ -42,6 +43,111 @@ | |||
42 | #define LPFC_RESET_WAIT 2 | 43 | #define LPFC_RESET_WAIT 2 |
43 | #define LPFC_ABORT_WAIT 2 | 44 | #define LPFC_ABORT_WAIT 2 |
44 | 45 | ||
46 | /** | ||
47 | * lpfc_update_stats: Update statistical data for the command completion. | ||
48 | * @phba: Pointer to HBA object. | ||
49 | * @lpfc_cmd: lpfc scsi command object pointer. | ||
50 | * | ||
51 | * This function is called when there is a command completion and this | ||
52 | * function updates the statistical data for the command completion. | ||
53 | **/ | ||
54 | static void | ||
55 | lpfc_update_stats(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd) | ||
56 | { | ||
57 | struct lpfc_rport_data *rdata = lpfc_cmd->rdata; | ||
58 | struct lpfc_nodelist *pnode = rdata->pnode; | ||
59 | struct scsi_cmnd *cmd = lpfc_cmd->pCmd; | ||
60 | unsigned long flags; | ||
61 | struct Scsi_Host *shost = cmd->device->host; | ||
62 | struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata; | ||
63 | unsigned long latency; | ||
64 | int i; | ||
65 | |||
66 | if (cmd->result) | ||
67 | return; | ||
68 | |||
69 | spin_lock_irqsave(shost->host_lock, flags); | ||
70 | if (!vport->stat_data_enabled || | ||
71 | vport->stat_data_blocked || | ||
72 | !pnode->lat_data || | ||
73 | (phba->bucket_type == LPFC_NO_BUCKET)) { | ||
74 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
75 | return; | ||
76 | } | ||
77 | latency = jiffies_to_msecs(jiffies - lpfc_cmd->start_time); | ||
78 | |||
79 | if (phba->bucket_type == LPFC_LINEAR_BUCKET) { | ||
80 | i = (latency + phba->bucket_step - 1 - phba->bucket_base)/ | ||
81 | phba->bucket_step; | ||
82 | if (i >= LPFC_MAX_BUCKET_COUNT) | ||
83 | i = LPFC_MAX_BUCKET_COUNT; | ||
84 | } else { | ||
85 | for (i = 0; i < LPFC_MAX_BUCKET_COUNT-1; i++) | ||
86 | if (latency <= (phba->bucket_base + | ||
87 | ((1<<i)*phba->bucket_step))) | ||
88 | break; | ||
89 | } | ||
90 | |||
91 | pnode->lat_data[i].cmd_count++; | ||
92 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
93 | } | ||
94 | |||
95 | |||
96 | /** | ||
97 | * lpfc_send_sdev_queuedepth_change_event: Posts a queuedepth change | ||
98 | * event. | ||
99 | * @phba: Pointer to HBA context object. | ||
100 | * @vport: Pointer to vport object. | ||
101 | * @ndlp: Pointer to FC node associated with the target. | ||
102 | * @lun: Lun number of the scsi device. | ||
103 | * @old_val: Old value of the queue depth. | ||
104 | * @new_val: New value of the queue depth. | ||
105 | * | ||
106 | * This function sends an event to the mgmt application indicating | ||
107 | * there is a change in the scsi device queue depth. | ||
108 | **/ | ||
109 | static void | ||
110 | lpfc_send_sdev_queuedepth_change_event(struct lpfc_hba *phba, | ||
111 | struct lpfc_vport *vport, | ||
112 | struct lpfc_nodelist *ndlp, | ||
113 | uint32_t lun, | ||
114 | uint32_t old_val, | ||
115 | uint32_t new_val) | ||
116 | { | ||
117 | struct lpfc_fast_path_event *fast_path_evt; | ||
118 | unsigned long flags; | ||
119 | |||
120 | fast_path_evt = lpfc_alloc_fast_evt(phba); | ||
121 | if (!fast_path_evt) | ||
122 | return; | ||
123 | |||
124 | fast_path_evt->un.queue_depth_evt.scsi_event.event_type = | ||
125 | FC_REG_SCSI_EVENT; | ||
126 | fast_path_evt->un.queue_depth_evt.scsi_event.subcategory = | ||
127 | LPFC_EVENT_VARQUEDEPTH; | ||
128 | |||
129 | /* Report all luns with change in queue depth */ | ||
130 | fast_path_evt->un.queue_depth_evt.scsi_event.lun = lun; | ||
131 | if (ndlp && NLP_CHK_NODE_ACT(ndlp)) { | ||
132 | memcpy(&fast_path_evt->un.queue_depth_evt.scsi_event.wwpn, | ||
133 | &ndlp->nlp_portname, sizeof(struct lpfc_name)); | ||
134 | memcpy(&fast_path_evt->un.queue_depth_evt.scsi_event.wwnn, | ||
135 | &ndlp->nlp_nodename, sizeof(struct lpfc_name)); | ||
136 | } | ||
137 | |||
138 | fast_path_evt->un.queue_depth_evt.oldval = old_val; | ||
139 | fast_path_evt->un.queue_depth_evt.newval = new_val; | ||
140 | fast_path_evt->vport = vport; | ||
141 | |||
142 | fast_path_evt->work_evt.evt = LPFC_EVT_FASTPATH_MGMT_EVT; | ||
143 | spin_lock_irqsave(&phba->hbalock, flags); | ||
144 | list_add_tail(&fast_path_evt->work_evt.evt_listp, &phba->work_list); | ||
145 | spin_unlock_irqrestore(&phba->hbalock, flags); | ||
146 | lpfc_worker_wake_up(phba); | ||
147 | |||
148 | return; | ||
149 | } | ||
150 | |||
45 | /* | 151 | /* |
46 | * This function is called with no lock held when there is a resource | 152 | * This function is called with no lock held when there is a resource |
47 | * error in driver or in firmware. | 153 | * error in driver or in firmware. |
@@ -117,9 +223,10 @@ lpfc_ramp_down_queue_handler(struct lpfc_hba *phba) | |||
117 | struct lpfc_vport **vports; | 223 | struct lpfc_vport **vports; |
118 | struct Scsi_Host *shost; | 224 | struct Scsi_Host *shost; |
119 | struct scsi_device *sdev; | 225 | struct scsi_device *sdev; |
120 | unsigned long new_queue_depth; | 226 | unsigned long new_queue_depth, old_queue_depth; |
121 | unsigned long num_rsrc_err, num_cmd_success; | 227 | unsigned long num_rsrc_err, num_cmd_success; |
122 | int i; | 228 | int i; |
229 | struct lpfc_rport_data *rdata; | ||
123 | 230 | ||
124 | num_rsrc_err = atomic_read(&phba->num_rsrc_err); | 231 | num_rsrc_err = atomic_read(&phba->num_rsrc_err); |
125 | num_cmd_success = atomic_read(&phba->num_cmd_success); | 232 | num_cmd_success = atomic_read(&phba->num_cmd_success); |
@@ -137,6 +244,7 @@ lpfc_ramp_down_queue_handler(struct lpfc_hba *phba) | |||
137 | else | 244 | else |
138 | new_queue_depth = sdev->queue_depth - | 245 | new_queue_depth = sdev->queue_depth - |
139 | new_queue_depth; | 246 | new_queue_depth; |
247 | old_queue_depth = sdev->queue_depth; | ||
140 | if (sdev->ordered_tags) | 248 | if (sdev->ordered_tags) |
141 | scsi_adjust_queue_depth(sdev, | 249 | scsi_adjust_queue_depth(sdev, |
142 | MSG_ORDERED_TAG, | 250 | MSG_ORDERED_TAG, |
@@ -145,6 +253,13 @@ lpfc_ramp_down_queue_handler(struct lpfc_hba *phba) | |||
145 | scsi_adjust_queue_depth(sdev, | 253 | scsi_adjust_queue_depth(sdev, |
146 | MSG_SIMPLE_TAG, | 254 | MSG_SIMPLE_TAG, |
147 | new_queue_depth); | 255 | new_queue_depth); |
256 | rdata = sdev->hostdata; | ||
257 | if (rdata) | ||
258 | lpfc_send_sdev_queuedepth_change_event( | ||
259 | phba, vports[i], | ||
260 | rdata->pnode, | ||
261 | sdev->lun, old_queue_depth, | ||
262 | new_queue_depth); | ||
148 | } | 263 | } |
149 | } | 264 | } |
150 | lpfc_destroy_vport_work_array(phba, vports); | 265 | lpfc_destroy_vport_work_array(phba, vports); |
@@ -159,6 +274,7 @@ lpfc_ramp_up_queue_handler(struct lpfc_hba *phba) | |||
159 | struct Scsi_Host *shost; | 274 | struct Scsi_Host *shost; |
160 | struct scsi_device *sdev; | 275 | struct scsi_device *sdev; |
161 | int i; | 276 | int i; |
277 | struct lpfc_rport_data *rdata; | ||
162 | 278 | ||
163 | vports = lpfc_create_vport_work_array(phba); | 279 | vports = lpfc_create_vport_work_array(phba); |
164 | if (vports != NULL) | 280 | if (vports != NULL) |
@@ -176,6 +292,14 @@ lpfc_ramp_up_queue_handler(struct lpfc_hba *phba) | |||
176 | scsi_adjust_queue_depth(sdev, | 292 | scsi_adjust_queue_depth(sdev, |
177 | MSG_SIMPLE_TAG, | 293 | MSG_SIMPLE_TAG, |
178 | sdev->queue_depth+1); | 294 | sdev->queue_depth+1); |
295 | rdata = sdev->hostdata; | ||
296 | if (rdata) | ||
297 | lpfc_send_sdev_queuedepth_change_event( | ||
298 | phba, vports[i], | ||
299 | rdata->pnode, | ||
300 | sdev->lun, | ||
301 | sdev->queue_depth - 1, | ||
302 | sdev->queue_depth); | ||
179 | } | 303 | } |
180 | } | 304 | } |
181 | lpfc_destroy_vport_work_array(phba, vports); | 305 | lpfc_destroy_vport_work_array(phba, vports); |
@@ -466,6 +590,97 @@ lpfc_scsi_prep_dma_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd) | |||
466 | return 0; | 590 | return 0; |
467 | } | 591 | } |
468 | 592 | ||
593 | /** | ||
594 | * lpfc_send_scsi_error_event: Posts an event when there is SCSI error. | ||
595 | * @phba: Pointer to hba context object. | ||
596 | * @vport: Pointer to vport object. | ||
597 | * @lpfc_cmd: Pointer to lpfc scsi command which reported the error. | ||
598 | * @rsp_iocb: Pointer to response iocb object which reported error. | ||
599 | * | ||
600 | * This function posts an event when there is a SCSI command reporting | ||
601 | * error from the scsi device. | ||
602 | **/ | ||
603 | static void | ||
604 | lpfc_send_scsi_error_event(struct lpfc_hba *phba, struct lpfc_vport *vport, | ||
605 | struct lpfc_scsi_buf *lpfc_cmd, struct lpfc_iocbq *rsp_iocb) { | ||
606 | struct scsi_cmnd *cmnd = lpfc_cmd->pCmd; | ||
607 | struct fcp_rsp *fcprsp = lpfc_cmd->fcp_rsp; | ||
608 | uint32_t resp_info = fcprsp->rspStatus2; | ||
609 | uint32_t scsi_status = fcprsp->rspStatus3; | ||
610 | uint32_t fcpi_parm = rsp_iocb->iocb.un.fcpi.fcpi_parm; | ||
611 | struct lpfc_fast_path_event *fast_path_evt = NULL; | ||
612 | struct lpfc_nodelist *pnode = lpfc_cmd->rdata->pnode; | ||
613 | unsigned long flags; | ||
614 | |||
615 | /* If there is queuefull or busy condition send a scsi event */ | ||
616 | if ((cmnd->result == SAM_STAT_TASK_SET_FULL) || | ||
617 | (cmnd->result == SAM_STAT_BUSY)) { | ||
618 | fast_path_evt = lpfc_alloc_fast_evt(phba); | ||
619 | if (!fast_path_evt) | ||
620 | return; | ||
621 | fast_path_evt->un.scsi_evt.event_type = | ||
622 | FC_REG_SCSI_EVENT; | ||
623 | fast_path_evt->un.scsi_evt.subcategory = | ||
624 | (cmnd->result == SAM_STAT_TASK_SET_FULL) ? | ||
625 | LPFC_EVENT_QFULL : LPFC_EVENT_DEVBSY; | ||
626 | fast_path_evt->un.scsi_evt.lun = cmnd->device->lun; | ||
627 | memcpy(&fast_path_evt->un.scsi_evt.wwpn, | ||
628 | &pnode->nlp_portname, sizeof(struct lpfc_name)); | ||
629 | memcpy(&fast_path_evt->un.scsi_evt.wwnn, | ||
630 | &pnode->nlp_nodename, sizeof(struct lpfc_name)); | ||
631 | } else if ((resp_info & SNS_LEN_VALID) && fcprsp->rspSnsLen && | ||
632 | ((cmnd->cmnd[0] == READ_10) || (cmnd->cmnd[0] == WRITE_10))) { | ||
633 | fast_path_evt = lpfc_alloc_fast_evt(phba); | ||
634 | if (!fast_path_evt) | ||
635 | return; | ||
636 | fast_path_evt->un.check_cond_evt.scsi_event.event_type = | ||
637 | FC_REG_SCSI_EVENT; | ||
638 | fast_path_evt->un.check_cond_evt.scsi_event.subcategory = | ||
639 | LPFC_EVENT_CHECK_COND; | ||
640 | fast_path_evt->un.check_cond_evt.scsi_event.lun = | ||
641 | cmnd->device->lun; | ||
642 | memcpy(&fast_path_evt->un.check_cond_evt.scsi_event.wwpn, | ||
643 | &pnode->nlp_portname, sizeof(struct lpfc_name)); | ||
644 | memcpy(&fast_path_evt->un.check_cond_evt.scsi_event.wwnn, | ||
645 | &pnode->nlp_nodename, sizeof(struct lpfc_name)); | ||
646 | fast_path_evt->un.check_cond_evt.sense_key = | ||
647 | cmnd->sense_buffer[2] & 0xf; | ||
648 | fast_path_evt->un.check_cond_evt.asc = cmnd->sense_buffer[12]; | ||
649 | fast_path_evt->un.check_cond_evt.ascq = cmnd->sense_buffer[13]; | ||
650 | } else if ((cmnd->sc_data_direction == DMA_FROM_DEVICE) && | ||
651 | fcpi_parm && | ||
652 | ((be32_to_cpu(fcprsp->rspResId) != fcpi_parm) || | ||
653 | ((scsi_status == SAM_STAT_GOOD) && | ||
654 | !(resp_info & (RESID_UNDER | RESID_OVER))))) { | ||
655 | /* | ||
656 | * If status is good or resid does not match with fcp_param and | ||
657 | * there is valid fcpi_parm, then there is a read_check error | ||
658 | */ | ||
659 | fast_path_evt = lpfc_alloc_fast_evt(phba); | ||
660 | if (!fast_path_evt) | ||
661 | return; | ||
662 | fast_path_evt->un.read_check_error.header.event_type = | ||
663 | FC_REG_FABRIC_EVENT; | ||
664 | fast_path_evt->un.read_check_error.header.subcategory = | ||
665 | LPFC_EVENT_FCPRDCHKERR; | ||
666 | memcpy(&fast_path_evt->un.read_check_error.header.wwpn, | ||
667 | &pnode->nlp_portname, sizeof(struct lpfc_name)); | ||
668 | memcpy(&fast_path_evt->un.read_check_error.header.wwnn, | ||
669 | &pnode->nlp_nodename, sizeof(struct lpfc_name)); | ||
670 | fast_path_evt->un.read_check_error.lun = cmnd->device->lun; | ||
671 | fast_path_evt->un.read_check_error.opcode = cmnd->cmnd[0]; | ||
672 | fast_path_evt->un.read_check_error.fcpiparam = | ||
673 | fcpi_parm; | ||
674 | } else | ||
675 | return; | ||
676 | |||
677 | fast_path_evt->vport = vport; | ||
678 | spin_lock_irqsave(&phba->hbalock, flags); | ||
679 | list_add_tail(&fast_path_evt->work_evt.evt_listp, &phba->work_list); | ||
680 | spin_unlock_irqrestore(&phba->hbalock, flags); | ||
681 | lpfc_worker_wake_up(phba); | ||
682 | return; | ||
683 | } | ||
469 | static void | 684 | static void |
470 | lpfc_scsi_unprep_dma_buf(struct lpfc_hba * phba, struct lpfc_scsi_buf * psb) | 685 | lpfc_scsi_unprep_dma_buf(struct lpfc_hba * phba, struct lpfc_scsi_buf * psb) |
471 | { | 686 | { |
@@ -494,6 +709,7 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd, | |||
494 | uint32_t rsplen = 0; | 709 | uint32_t rsplen = 0; |
495 | uint32_t logit = LOG_FCP | LOG_FCP_ERROR; | 710 | uint32_t logit = LOG_FCP | LOG_FCP_ERROR; |
496 | 711 | ||
712 | |||
497 | /* | 713 | /* |
498 | * If this is a task management command, there is no | 714 | * If this is a task management command, there is no |
499 | * scsi packet associated with this lpfc_cmd. The driver | 715 | * scsi packet associated with this lpfc_cmd. The driver |
@@ -609,6 +825,7 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd, | |||
609 | 825 | ||
610 | out: | 826 | out: |
611 | cmnd->result = ScsiResult(host_status, scsi_status); | 827 | cmnd->result = ScsiResult(host_status, scsi_status); |
828 | lpfc_send_scsi_error_event(vport->phba, vport, lpfc_cmd, rsp_iocb); | ||
612 | } | 829 | } |
613 | 830 | ||
614 | static void | 831 | static void |
@@ -625,6 +842,7 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn, | |||
625 | struct scsi_device *sdev, *tmp_sdev; | 842 | struct scsi_device *sdev, *tmp_sdev; |
626 | int depth = 0; | 843 | int depth = 0; |
627 | unsigned long flags; | 844 | unsigned long flags; |
845 | struct lpfc_fast_path_event *fast_path_evt; | ||
628 | 846 | ||
629 | lpfc_cmd->result = pIocbOut->iocb.un.ulpWord[4]; | 847 | lpfc_cmd->result = pIocbOut->iocb.un.ulpWord[4]; |
630 | lpfc_cmd->status = pIocbOut->iocb.ulpStatus; | 848 | lpfc_cmd->status = pIocbOut->iocb.ulpStatus; |
@@ -655,6 +873,30 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn, | |||
655 | case IOSTAT_NPORT_BSY: | 873 | case IOSTAT_NPORT_BSY: |
656 | case IOSTAT_FABRIC_BSY: | 874 | case IOSTAT_FABRIC_BSY: |
657 | cmd->result = ScsiResult(DID_TRANSPORT_DISRUPTED, 0); | 875 | cmd->result = ScsiResult(DID_TRANSPORT_DISRUPTED, 0); |
876 | fast_path_evt = lpfc_alloc_fast_evt(phba); | ||
877 | if (!fast_path_evt) | ||
878 | break; | ||
879 | fast_path_evt->un.fabric_evt.event_type = | ||
880 | FC_REG_FABRIC_EVENT; | ||
881 | fast_path_evt->un.fabric_evt.subcategory = | ||
882 | (lpfc_cmd->status == IOSTAT_NPORT_BSY) ? | ||
883 | LPFC_EVENT_PORT_BUSY : LPFC_EVENT_FABRIC_BUSY; | ||
884 | if (pnode && NLP_CHK_NODE_ACT(pnode)) { | ||
885 | memcpy(&fast_path_evt->un.fabric_evt.wwpn, | ||
886 | &pnode->nlp_portname, | ||
887 | sizeof(struct lpfc_name)); | ||
888 | memcpy(&fast_path_evt->un.fabric_evt.wwnn, | ||
889 | &pnode->nlp_nodename, | ||
890 | sizeof(struct lpfc_name)); | ||
891 | } | ||
892 | fast_path_evt->vport = vport; | ||
893 | fast_path_evt->work_evt.evt = | ||
894 | LPFC_EVT_FASTPATH_MGMT_EVT; | ||
895 | spin_lock_irqsave(&phba->hbalock, flags); | ||
896 | list_add_tail(&fast_path_evt->work_evt.evt_listp, | ||
897 | &phba->work_list); | ||
898 | spin_unlock_irqrestore(&phba->hbalock, flags); | ||
899 | lpfc_worker_wake_up(phba); | ||
658 | break; | 900 | break; |
659 | case IOSTAT_LOCAL_REJECT: | 901 | case IOSTAT_LOCAL_REJECT: |
660 | if (lpfc_cmd->result == IOERR_INVALID_RPI || | 902 | if (lpfc_cmd->result == IOERR_INVALID_RPI || |
@@ -687,6 +929,7 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn, | |||
687 | scsi_get_resid(cmd)); | 929 | scsi_get_resid(cmd)); |
688 | } | 930 | } |
689 | 931 | ||
932 | lpfc_update_stats(phba, lpfc_cmd); | ||
690 | result = cmd->result; | 933 | result = cmd->result; |
691 | sdev = cmd->device; | 934 | sdev = cmd->device; |
692 | if (vport->cfg_max_scsicmpl_time && | 935 | if (vport->cfg_max_scsicmpl_time && |
@@ -755,6 +998,9 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn, | |||
755 | pnode->last_ramp_up_time = jiffies; | 998 | pnode->last_ramp_up_time = jiffies; |
756 | } | 999 | } |
757 | } | 1000 | } |
1001 | lpfc_send_sdev_queuedepth_change_event(phba, vport, pnode, | ||
1002 | 0xFFFFFFFF, | ||
1003 | sdev->queue_depth - 1, sdev->queue_depth); | ||
758 | } | 1004 | } |
759 | 1005 | ||
760 | /* | 1006 | /* |
@@ -784,6 +1030,9 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn, | |||
784 | lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP, | 1030 | lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP, |
785 | "0711 detected queue full - lun queue " | 1031 | "0711 detected queue full - lun queue " |
786 | "depth adjusted to %d.\n", depth); | 1032 | "depth adjusted to %d.\n", depth); |
1033 | lpfc_send_sdev_queuedepth_change_event(phba, vport, | ||
1034 | pnode, 0xFFFFFFFF, | ||
1035 | depth+1, depth); | ||
787 | } | 1036 | } |
788 | } | 1037 | } |
789 | 1038 | ||
@@ -1112,6 +1361,7 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *)) | |||
1112 | goto out_host_busy; | 1361 | goto out_host_busy; |
1113 | } | 1362 | } |
1114 | 1363 | ||
1364 | lpfc_cmd->start_time = jiffies; | ||
1115 | /* | 1365 | /* |
1116 | * Store the midlayer's command structure for the completion phase | 1366 | * Store the midlayer's command structure for the completion phase |
1117 | * and complete the command initialization. | 1367 | * and complete the command initialization. |
@@ -1280,6 +1530,7 @@ lpfc_device_reset_handler(struct scsi_cmnd *cmnd) | |||
1280 | int ret = SUCCESS; | 1530 | int ret = SUCCESS; |
1281 | int status; | 1531 | int status; |
1282 | int cnt; | 1532 | int cnt; |
1533 | struct lpfc_scsi_event_header scsi_event; | ||
1283 | 1534 | ||
1284 | lpfc_block_error_handler(cmnd); | 1535 | lpfc_block_error_handler(cmnd); |
1285 | /* | 1536 | /* |
@@ -1298,6 +1549,19 @@ lpfc_device_reset_handler(struct scsi_cmnd *cmnd) | |||
1298 | break; | 1549 | break; |
1299 | pnode = rdata->pnode; | 1550 | pnode = rdata->pnode; |
1300 | } | 1551 | } |
1552 | |||
1553 | scsi_event.event_type = FC_REG_SCSI_EVENT; | ||
1554 | scsi_event.subcategory = LPFC_EVENT_TGTRESET; | ||
1555 | scsi_event.lun = 0; | ||
1556 | memcpy(scsi_event.wwpn, &pnode->nlp_portname, sizeof(struct lpfc_name)); | ||
1557 | memcpy(scsi_event.wwnn, &pnode->nlp_nodename, sizeof(struct lpfc_name)); | ||
1558 | |||
1559 | fc_host_post_vendor_event(shost, | ||
1560 | fc_get_event_number(), | ||
1561 | sizeof(scsi_event), | ||
1562 | (char *)&scsi_event, | ||
1563 | SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX); | ||
1564 | |||
1301 | if (!rdata || pnode->nlp_state != NLP_STE_MAPPED_NODE) { | 1565 | if (!rdata || pnode->nlp_state != NLP_STE_MAPPED_NODE) { |
1302 | lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP, | 1566 | lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP, |
1303 | "0721 LUN Reset rport " | 1567 | "0721 LUN Reset rport " |
@@ -1381,6 +1645,19 @@ lpfc_bus_reset_handler(struct scsi_cmnd *cmnd) | |||
1381 | int cnt; | 1645 | int cnt; |
1382 | struct lpfc_scsi_buf * lpfc_cmd; | 1646 | struct lpfc_scsi_buf * lpfc_cmd; |
1383 | unsigned long later; | 1647 | unsigned long later; |
1648 | struct lpfc_scsi_event_header scsi_event; | ||
1649 | |||
1650 | scsi_event.event_type = FC_REG_SCSI_EVENT; | ||
1651 | scsi_event.subcategory = LPFC_EVENT_BUSRESET; | ||
1652 | scsi_event.lun = 0; | ||
1653 | memcpy(scsi_event.wwpn, &vport->fc_portname, sizeof(struct lpfc_name)); | ||
1654 | memcpy(scsi_event.wwnn, &vport->fc_nodename, sizeof(struct lpfc_name)); | ||
1655 | |||
1656 | fc_host_post_vendor_event(shost, | ||
1657 | fc_get_event_number(), | ||
1658 | sizeof(scsi_event), | ||
1659 | (char *)&scsi_event, | ||
1660 | SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX); | ||
1384 | 1661 | ||
1385 | lpfc_block_error_handler(cmnd); | 1662 | lpfc_block_error_handler(cmnd); |
1386 | /* | 1663 | /* |
diff --git a/drivers/scsi/lpfc/lpfc_scsi.h b/drivers/scsi/lpfc/lpfc_scsi.h index 6737cabe9a72..437f182e2322 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.h +++ b/drivers/scsi/lpfc/lpfc_scsi.h | |||
@@ -107,6 +107,10 @@ struct fcp_cmnd { | |||
107 | 107 | ||
108 | }; | 108 | }; |
109 | 109 | ||
110 | struct lpfc_scsicmd_bkt { | ||
111 | uint32_t cmd_count; | ||
112 | }; | ||
113 | |||
110 | struct lpfc_scsi_buf { | 114 | struct lpfc_scsi_buf { |
111 | struct list_head list; | 115 | struct list_head list; |
112 | struct scsi_cmnd *pCmd; | 116 | struct scsi_cmnd *pCmd; |
diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 2cca39e9b93d..8ab5babdeebc 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c | |||
@@ -32,6 +32,7 @@ | |||
32 | 32 | ||
33 | #include "lpfc_hw.h" | 33 | #include "lpfc_hw.h" |
34 | #include "lpfc_sli.h" | 34 | #include "lpfc_sli.h" |
35 | #include "lpfc_nl.h" | ||
35 | #include "lpfc_disc.h" | 36 | #include "lpfc_disc.h" |
36 | #include "lpfc_scsi.h" | 37 | #include "lpfc_scsi.h" |
37 | #include "lpfc.h" | 38 | #include "lpfc.h" |
@@ -1611,6 +1612,17 @@ lpfc_sli_process_sol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, | |||
1611 | if (cmdiocbp) { | 1612 | if (cmdiocbp) { |
1612 | if (cmdiocbp->iocb_cmpl) { | 1613 | if (cmdiocbp->iocb_cmpl) { |
1613 | /* | 1614 | /* |
1615 | * If an ELS command failed send an event to mgmt | ||
1616 | * application. | ||
1617 | */ | ||
1618 | if (saveq->iocb.ulpStatus && | ||
1619 | (pring->ringno == LPFC_ELS_RING) && | ||
1620 | (cmdiocbp->iocb.ulpCommand == | ||
1621 | CMD_ELS_REQUEST64_CR)) | ||
1622 | lpfc_send_els_failure_event(phba, | ||
1623 | cmdiocbp, saveq); | ||
1624 | |||
1625 | /* | ||
1614 | * Post all ELS completions to the worker thread. | 1626 | * Post all ELS completions to the worker thread. |
1615 | * All other are passed to the completion callback. | 1627 | * All other are passed to the completion callback. |
1616 | */ | 1628 | */ |
diff --git a/drivers/scsi/lpfc/lpfc_vport.c b/drivers/scsi/lpfc/lpfc_vport.c index 2578d5fd9537..a7de1cc02b40 100644 --- a/drivers/scsi/lpfc/lpfc_vport.c +++ b/drivers/scsi/lpfc/lpfc_vport.c | |||
@@ -34,6 +34,7 @@ | |||
34 | #include <scsi/scsi_transport_fc.h> | 34 | #include <scsi/scsi_transport_fc.h> |
35 | #include "lpfc_hw.h" | 35 | #include "lpfc_hw.h" |
36 | #include "lpfc_sli.h" | 36 | #include "lpfc_sli.h" |
37 | #include "lpfc_nl.h" | ||
37 | #include "lpfc_disc.h" | 38 | #include "lpfc_disc.h" |
38 | #include "lpfc_scsi.h" | 39 | #include "lpfc_scsi.h" |
39 | #include "lpfc.h" | 40 | #include "lpfc.h" |
@@ -745,3 +746,82 @@ lpfc_destroy_vport_work_array(struct lpfc_hba *phba, struct lpfc_vport **vports) | |||
745 | scsi_host_put(lpfc_shost_from_vport(vports[i])); | 746 | scsi_host_put(lpfc_shost_from_vport(vports[i])); |
746 | kfree(vports); | 747 | kfree(vports); |
747 | } | 748 | } |
749 | |||
750 | |||
751 | /** | ||
752 | * lpfc_vport_reset_stat_data: Reset the statistical data for the vport. | ||
753 | * @vport: Pointer to vport object. | ||
754 | * | ||
755 | * This function resets the statistical data for the vport. This function | ||
756 | * is called with the host_lock held | ||
757 | **/ | ||
758 | void | ||
759 | lpfc_vport_reset_stat_data(struct lpfc_vport *vport) | ||
760 | { | ||
761 | struct lpfc_nodelist *ndlp = NULL, *next_ndlp = NULL; | ||
762 | |||
763 | list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes, nlp_listp) { | ||
764 | if (!NLP_CHK_NODE_ACT(ndlp)) | ||
765 | continue; | ||
766 | if (ndlp->lat_data) | ||
767 | memset(ndlp->lat_data, 0, LPFC_MAX_BUCKET_COUNT * | ||
768 | sizeof(struct lpfc_scsicmd_bkt)); | ||
769 | } | ||
770 | } | ||
771 | |||
772 | |||
773 | /** | ||
774 | * lpfc_alloc_bucket: Allocate data buffer required for collecting | ||
775 | * statistical data. | ||
776 | * @vport: Pointer to vport object. | ||
777 | * | ||
778 | * This function allocates data buffer required for all the FC | ||
779 | * nodes of the vport to collect statistical data. | ||
780 | **/ | ||
781 | void | ||
782 | lpfc_alloc_bucket(struct lpfc_vport *vport) | ||
783 | { | ||
784 | struct lpfc_nodelist *ndlp = NULL, *next_ndlp = NULL; | ||
785 | |||
786 | list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes, nlp_listp) { | ||
787 | if (!NLP_CHK_NODE_ACT(ndlp)) | ||
788 | continue; | ||
789 | |||
790 | kfree(ndlp->lat_data); | ||
791 | ndlp->lat_data = NULL; | ||
792 | |||
793 | if (ndlp->nlp_state == NLP_STE_MAPPED_NODE) { | ||
794 | ndlp->lat_data = kcalloc(LPFC_MAX_BUCKET_COUNT, | ||
795 | sizeof(struct lpfc_scsicmd_bkt), | ||
796 | GFP_ATOMIC); | ||
797 | |||
798 | if (!ndlp->lat_data) | ||
799 | lpfc_printf_vlog(vport, KERN_ERR, LOG_NODE, | ||
800 | "0287 lpfc_alloc_bucket failed to " | ||
801 | "allocate statistical data buffer DID " | ||
802 | "0x%x\n", ndlp->nlp_DID); | ||
803 | } | ||
804 | } | ||
805 | } | ||
806 | |||
807 | /** | ||
808 | * lpfc_free_bucket: Free data buffer required for collecting | ||
809 | * statistical data. | ||
810 | * @vport: Pointer to vport object. | ||
811 | * | ||
812 | * Th function frees statistical data buffer of all the FC | ||
813 | * nodes of the vport. | ||
814 | **/ | ||
815 | void | ||
816 | lpfc_free_bucket(struct lpfc_vport *vport) | ||
817 | { | ||
818 | struct lpfc_nodelist *ndlp = NULL, *next_ndlp = NULL; | ||
819 | |||
820 | list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes, nlp_listp) { | ||
821 | if (!NLP_CHK_NODE_ACT(ndlp)) | ||
822 | continue; | ||
823 | |||
824 | kfree(ndlp->lat_data); | ||
825 | ndlp->lat_data = NULL; | ||
826 | } | ||
827 | } | ||
diff --git a/drivers/scsi/lpfc/lpfc_vport.h b/drivers/scsi/lpfc/lpfc_vport.h index 96c445333b69..90828340acea 100644 --- a/drivers/scsi/lpfc/lpfc_vport.h +++ b/drivers/scsi/lpfc/lpfc_vport.h | |||
@@ -112,4 +112,8 @@ struct vport_cmd_tag { | |||
112 | void lpfc_vport_set_state(struct lpfc_vport *vport, | 112 | void lpfc_vport_set_state(struct lpfc_vport *vport, |
113 | enum fc_vport_state new_state); | 113 | enum fc_vport_state new_state); |
114 | 114 | ||
115 | void lpfc_vport_reset_stat_data(struct lpfc_vport *); | ||
116 | void lpfc_alloc_bucket(struct lpfc_vport *); | ||
117 | void lpfc_free_bucket(struct lpfc_vport *); | ||
118 | |||
115 | #endif /* H_LPFC_VPORT */ | 119 | #endif /* H_LPFC_VPORT */ |