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/lpfc_attr.c | |
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/lpfc_attr.c')
-rw-r--r-- | drivers/scsi/lpfc/lpfc_attr.c | 343 |
1 files changed, 342 insertions, 1 deletions
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 | } |