diff options
author | Mike Miller <mike.miller@hp.com> | 2008-02-21 02:54:03 -0500 |
---|---|---|
committer | Jens Axboe <axboe@carl.home.kernel.dk> | 2008-03-04 05:14:39 -0500 |
commit | 89b6e743788516491846724d7ef89bcac7ac9c99 (patch) | |
tree | ead97b7f39a2961c9b5a44ed42259fa17268c3ab /drivers/block/cciss.c | |
parent | 02cf01aea5af7a4d1a38045712fe11bffcc206b0 (diff) |
resubmit: cciss: procfs updates to display info about many
volumes
This patch allows us to display information about all of the logical volumes
configured on a particular controller without stepping on memory even when
there are many volumes (128 or more) configured.
Please consider this for inclusion.
Signed-off-by: Mike Miller <mike.miller@hp.com>
Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
Diffstat (limited to 'drivers/block/cciss.c')
-rw-r--r-- | drivers/block/cciss.c | 253 |
1 files changed, 157 insertions, 96 deletions
diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index 9715be3f2487..f1e7390683fd 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c | |||
@@ -33,6 +33,7 @@ | |||
33 | #include <linux/blkpg.h> | 33 | #include <linux/blkpg.h> |
34 | #include <linux/timer.h> | 34 | #include <linux/timer.h> |
35 | #include <linux/proc_fs.h> | 35 | #include <linux/proc_fs.h> |
36 | #include <linux/seq_file.h> | ||
36 | #include <linux/init.h> | 37 | #include <linux/init.h> |
37 | #include <linux/hdreg.h> | 38 | #include <linux/hdreg.h> |
38 | #include <linux/spinlock.h> | 39 | #include <linux/spinlock.h> |
@@ -174,8 +175,6 @@ static int sendcmd_withirq(__u8 cmd, int ctlr, void *buff, size_t size, | |||
174 | static void fail_all_cmds(unsigned long ctlr); | 175 | static void fail_all_cmds(unsigned long ctlr); |
175 | 176 | ||
176 | #ifdef CONFIG_PROC_FS | 177 | #ifdef CONFIG_PROC_FS |
177 | static int cciss_proc_get_info(char *buffer, char **start, off_t offset, | ||
178 | int length, int *eof, void *data); | ||
179 | static void cciss_procinit(int i); | 178 | static void cciss_procinit(int i); |
180 | #else | 179 | #else |
181 | static void cciss_procinit(int i) | 180 | static void cciss_procinit(int i) |
@@ -240,24 +239,46 @@ static inline CommandList_struct *removeQ(CommandList_struct **Qptr, | |||
240 | */ | 239 | */ |
241 | #define ENG_GIG 1000000000 | 240 | #define ENG_GIG 1000000000 |
242 | #define ENG_GIG_FACTOR (ENG_GIG/512) | 241 | #define ENG_GIG_FACTOR (ENG_GIG/512) |
242 | #define ENGAGE_SCSI "engage scsi" | ||
243 | static const char *raid_label[] = { "0", "4", "1(1+0)", "5", "5+1", "ADG", | 243 | static const char *raid_label[] = { "0", "4", "1(1+0)", "5", "5+1", "ADG", |
244 | "UNKNOWN" | 244 | "UNKNOWN" |
245 | }; | 245 | }; |
246 | 246 | ||
247 | static struct proc_dir_entry *proc_cciss; | 247 | static struct proc_dir_entry *proc_cciss; |
248 | 248 | ||
249 | static int cciss_proc_get_info(char *buffer, char **start, off_t offset, | 249 | static void cciss_seq_show_header(struct seq_file *seq) |
250 | int length, int *eof, void *data) | ||
251 | { | 250 | { |
252 | off_t pos = 0; | 251 | ctlr_info_t *h = seq->private; |
253 | off_t len = 0; | 252 | |
254 | int size, i, ctlr; | 253 | seq_printf(seq, "%s: HP %s Controller\n" |
255 | ctlr_info_t *h = (ctlr_info_t *) data; | 254 | "Board ID: 0x%08lx\n" |
256 | drive_info_struct *drv; | 255 | "Firmware Version: %c%c%c%c\n" |
257 | unsigned long flags; | 256 | "IRQ: %d\n" |
258 | sector_t vol_sz, vol_sz_frac; | 257 | "Logical drives: %d\n" |
258 | "Current Q depth: %d\n" | ||
259 | "Current # commands on controller: %d\n" | ||
260 | "Max Q depth since init: %d\n" | ||
261 | "Max # commands on controller since init: %d\n" | ||
262 | "Max SG entries since init: %d\n", | ||
263 | h->devname, | ||
264 | h->product_name, | ||
265 | (unsigned long)h->board_id, | ||
266 | h->firm_ver[0], h->firm_ver[1], h->firm_ver[2], | ||
267 | h->firm_ver[3], (unsigned int)h->intr[SIMPLE_MODE_INT], | ||
268 | h->num_luns, | ||
269 | h->Qdepth, h->commands_outstanding, | ||
270 | h->maxQsinceinit, h->max_outstanding, h->maxSG); | ||
259 | 271 | ||
260 | ctlr = h->ctlr; | 272 | #ifdef CONFIG_CISS_SCSI_TAPE |
273 | cciss_seq_tape_report(seq, h->ctlr); | ||
274 | #endif /* CONFIG_CISS_SCSI_TAPE */ | ||
275 | } | ||
276 | |||
277 | static void *cciss_seq_start(struct seq_file *seq, loff_t *pos) | ||
278 | { | ||
279 | ctlr_info_t *h = seq->private; | ||
280 | unsigned ctlr = h->ctlr; | ||
281 | unsigned long flags; | ||
261 | 282 | ||
262 | /* prevent displaying bogus info during configuration | 283 | /* prevent displaying bogus info during configuration |
263 | * or deconfiguration of a logical volume | 284 | * or deconfiguration of a logical volume |
@@ -265,115 +286,155 @@ static int cciss_proc_get_info(char *buffer, char **start, off_t offset, | |||
265 | spin_lock_irqsave(CCISS_LOCK(ctlr), flags); | 286 | spin_lock_irqsave(CCISS_LOCK(ctlr), flags); |
266 | if (h->busy_configuring) { | 287 | if (h->busy_configuring) { |
267 | spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags); | 288 | spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags); |
268 | return -EBUSY; | 289 | return ERR_PTR(-EBUSY); |
269 | } | 290 | } |
270 | h->busy_configuring = 1; | 291 | h->busy_configuring = 1; |
271 | spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags); | 292 | spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags); |
272 | 293 | ||
273 | size = sprintf(buffer, "%s: HP %s Controller\n" | 294 | if (*pos == 0) |
274 | "Board ID: 0x%08lx\n" | 295 | cciss_seq_show_header(seq); |
275 | "Firmware Version: %c%c%c%c\n" | ||
276 | "IRQ: %d\n" | ||
277 | "Logical drives: %d\n" | ||
278 | "Max sectors: %d\n" | ||
279 | "Current Q depth: %d\n" | ||
280 | "Current # commands on controller: %d\n" | ||
281 | "Max Q depth since init: %d\n" | ||
282 | "Max # commands on controller since init: %d\n" | ||
283 | "Max SG entries since init: %d\n\n", | ||
284 | h->devname, | ||
285 | h->product_name, | ||
286 | (unsigned long)h->board_id, | ||
287 | h->firm_ver[0], h->firm_ver[1], h->firm_ver[2], | ||
288 | h->firm_ver[3], (unsigned int)h->intr[SIMPLE_MODE_INT], | ||
289 | h->num_luns, | ||
290 | h->cciss_max_sectors, | ||
291 | h->Qdepth, h->commands_outstanding, | ||
292 | h->maxQsinceinit, h->max_outstanding, h->maxSG); | ||
293 | |||
294 | pos += size; | ||
295 | len += size; | ||
296 | cciss_proc_tape_report(ctlr, buffer, &pos, &len); | ||
297 | for (i = 0; i <= h->highest_lun; i++) { | ||
298 | |||
299 | drv = &h->drv[i]; | ||
300 | if (drv->heads == 0) | ||
301 | continue; | ||
302 | 296 | ||
303 | vol_sz = drv->nr_blocks; | 297 | return pos; |
304 | vol_sz_frac = sector_div(vol_sz, ENG_GIG_FACTOR); | 298 | } |
305 | vol_sz_frac *= 100; | 299 | |
306 | sector_div(vol_sz_frac, ENG_GIG_FACTOR); | 300 | static int cciss_seq_show(struct seq_file *seq, void *v) |
301 | { | ||
302 | sector_t vol_sz, vol_sz_frac; | ||
303 | ctlr_info_t *h = seq->private; | ||
304 | unsigned ctlr = h->ctlr; | ||
305 | loff_t *pos = v; | ||
306 | drive_info_struct *drv = &h->drv[*pos]; | ||
307 | |||
308 | if (*pos > h->highest_lun) | ||
309 | return 0; | ||
310 | |||
311 | if (drv->heads == 0) | ||
312 | return 0; | ||
313 | |||
314 | vol_sz = drv->nr_blocks; | ||
315 | vol_sz_frac = sector_div(vol_sz, ENG_GIG_FACTOR); | ||
316 | vol_sz_frac *= 100; | ||
317 | sector_div(vol_sz_frac, ENG_GIG_FACTOR); | ||
318 | |||
319 | if (drv->raid_level > 5) | ||
320 | drv->raid_level = RAID_UNKNOWN; | ||
321 | seq_printf(seq, "cciss/c%dd%d:" | ||
322 | "\t%4u.%02uGB\tRAID %s\n", | ||
323 | ctlr, (int) *pos, (int)vol_sz, (int)vol_sz_frac, | ||
324 | raid_label[drv->raid_level]); | ||
325 | return 0; | ||
326 | } | ||
327 | |||
328 | static void *cciss_seq_next(struct seq_file *seq, void *v, loff_t *pos) | ||
329 | { | ||
330 | ctlr_info_t *h = seq->private; | ||
331 | |||
332 | if (*pos > h->highest_lun) | ||
333 | return NULL; | ||
334 | *pos += 1; | ||
335 | |||
336 | return pos; | ||
337 | } | ||
338 | |||
339 | static void cciss_seq_stop(struct seq_file *seq, void *v) | ||
340 | { | ||
341 | ctlr_info_t *h = seq->private; | ||
342 | |||
343 | /* Only reset h->busy_configuring if we succeeded in setting | ||
344 | * it during cciss_seq_start. */ | ||
345 | if (v == ERR_PTR(-EBUSY)) | ||
346 | return; | ||
307 | 347 | ||
308 | if (drv->raid_level > 5) | ||
309 | drv->raid_level = RAID_UNKNOWN; | ||
310 | size = sprintf(buffer + len, "cciss/c%dd%d:" | ||
311 | "\t%4u.%02uGB\tRAID %s\n", | ||
312 | ctlr, i, (int)vol_sz, (int)vol_sz_frac, | ||
313 | raid_label[drv->raid_level]); | ||
314 | pos += size; | ||
315 | len += size; | ||
316 | } | ||
317 | |||
318 | *eof = 1; | ||
319 | *start = buffer + offset; | ||
320 | len -= offset; | ||
321 | if (len > length) | ||
322 | len = length; | ||
323 | h->busy_configuring = 0; | 348 | h->busy_configuring = 0; |
324 | return len; | ||
325 | } | 349 | } |
326 | 350 | ||
327 | static int | 351 | static struct seq_operations cciss_seq_ops = { |
328 | cciss_proc_write(struct file *file, const char __user *buffer, | 352 | .start = cciss_seq_start, |
329 | unsigned long count, void *data) | 353 | .show = cciss_seq_show, |
354 | .next = cciss_seq_next, | ||
355 | .stop = cciss_seq_stop, | ||
356 | }; | ||
357 | |||
358 | static int cciss_seq_open(struct inode *inode, struct file *file) | ||
330 | { | 359 | { |
331 | unsigned char cmd[80]; | 360 | int ret = seq_open(file, &cciss_seq_ops); |
332 | int len; | 361 | struct seq_file *seq = file->private_data; |
333 | #ifdef CONFIG_CISS_SCSI_TAPE | 362 | |
334 | ctlr_info_t *h = (ctlr_info_t *) data; | 363 | if (!ret) |
335 | int rc; | 364 | seq->private = PDE(inode)->data; |
365 | |||
366 | return ret; | ||
367 | } | ||
368 | |||
369 | static ssize_t | ||
370 | cciss_proc_write(struct file *file, const char __user *buf, | ||
371 | size_t length, loff_t *ppos) | ||
372 | { | ||
373 | int err; | ||
374 | char *buffer; | ||
375 | |||
376 | #ifndef CONFIG_CISS_SCSI_TAPE | ||
377 | return -EINVAL; | ||
336 | #endif | 378 | #endif |
337 | 379 | ||
338 | if (count > sizeof(cmd) - 1) | 380 | if (!buf || length > PAGE_SIZE - 1) |
339 | return -EINVAL; | 381 | return -EINVAL; |
340 | if (copy_from_user(cmd, buffer, count)) | 382 | |
341 | return -EFAULT; | 383 | buffer = (char *)__get_free_page(GFP_KERNEL); |
342 | cmd[count] = '\0'; | 384 | if (!buffer) |
343 | len = strlen(cmd); // above 3 lines ensure safety | 385 | return -ENOMEM; |
344 | if (len && cmd[len - 1] == '\n') | 386 | |
345 | cmd[--len] = '\0'; | 387 | err = -EFAULT; |
346 | # ifdef CONFIG_CISS_SCSI_TAPE | 388 | if (copy_from_user(buffer, buf, length)) |
347 | if (strcmp("engage scsi", cmd) == 0) { | 389 | goto out; |
390 | buffer[length] = '\0'; | ||
391 | |||
392 | #ifdef CONFIG_CISS_SCSI_TAPE | ||
393 | if (strncmp(ENGAGE_SCSI, buffer, sizeof ENGAGE_SCSI - 1) == 0) { | ||
394 | struct seq_file *seq = file->private_data; | ||
395 | ctlr_info_t *h = seq->private; | ||
396 | int rc; | ||
397 | |||
348 | rc = cciss_engage_scsi(h->ctlr); | 398 | rc = cciss_engage_scsi(h->ctlr); |
349 | if (rc != 0) | 399 | if (rc != 0) |
350 | return -rc; | 400 | err = -rc; |
351 | return count; | 401 | else |
352 | } | 402 | err = length; |
403 | } else | ||
404 | #endif /* CONFIG_CISS_SCSI_TAPE */ | ||
405 | err = -EINVAL; | ||
353 | /* might be nice to have "disengage" too, but it's not | 406 | /* might be nice to have "disengage" too, but it's not |
354 | safely possible. (only 1 module use count, lock issues.) */ | 407 | safely possible. (only 1 module use count, lock issues.) */ |
355 | # endif | 408 | |
356 | return -EINVAL; | 409 | out: |
410 | free_page((unsigned long)buffer); | ||
411 | return err; | ||
357 | } | 412 | } |
358 | 413 | ||
359 | /* | 414 | static struct file_operations cciss_proc_fops = { |
360 | * Get us a file in /proc/cciss that says something about each controller. | 415 | .owner = THIS_MODULE, |
361 | * Create /proc/cciss if it doesn't exist yet. | 416 | .open = cciss_seq_open, |
362 | */ | 417 | .read = seq_read, |
418 | .llseek = seq_lseek, | ||
419 | .release = seq_release, | ||
420 | .write = cciss_proc_write, | ||
421 | }; | ||
422 | |||
363 | static void __devinit cciss_procinit(int i) | 423 | static void __devinit cciss_procinit(int i) |
364 | { | 424 | { |
365 | struct proc_dir_entry *pde; | 425 | struct proc_dir_entry *pde; |
366 | 426 | ||
367 | if (proc_cciss == NULL) { | 427 | if (proc_cciss == NULL) |
368 | proc_cciss = proc_mkdir("cciss", proc_root_driver); | 428 | proc_cciss = proc_mkdir("cciss", proc_root_driver); |
369 | if (!proc_cciss) | 429 | if (!proc_cciss) |
370 | return; | 430 | return; |
371 | } | 431 | pde = proc_create(hba[i]->devname, S_IWUSR | S_IRUSR | S_IRGRP | |
432 | S_IROTH, proc_cciss, | ||
433 | &cciss_proc_fops); | ||
434 | if (!pde) | ||
435 | return; | ||
372 | 436 | ||
373 | pde = create_proc_read_entry(hba[i]->devname, | 437 | pde->data = hba[i]; |
374 | S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH, | ||
375 | proc_cciss, cciss_proc_get_info, hba[i]); | ||
376 | pde->write_proc = cciss_proc_write; | ||
377 | } | 438 | } |
378 | #endif /* CONFIG_PROC_FS */ | 439 | #endif /* CONFIG_PROC_FS */ |
379 | 440 | ||