aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/block/cciss.c
diff options
context:
space:
mode:
authorMike Miller <mike.miller@hp.com>2008-02-21 02:54:03 -0500
committerJens Axboe <axboe@carl.home.kernel.dk>2008-03-04 05:14:39 -0500
commit89b6e743788516491846724d7ef89bcac7ac9c99 (patch)
treeead97b7f39a2961c9b5a44ed42259fa17268c3ab /drivers/block/cciss.c
parent02cf01aea5af7a4d1a38045712fe11bffcc206b0 (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.c253
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,
174static void fail_all_cmds(unsigned long ctlr); 175static void fail_all_cmds(unsigned long ctlr);
175 176
176#ifdef CONFIG_PROC_FS 177#ifdef CONFIG_PROC_FS
177static int cciss_proc_get_info(char *buffer, char **start, off_t offset,
178 int length, int *eof, void *data);
179static void cciss_procinit(int i); 178static void cciss_procinit(int i);
180#else 179#else
181static void cciss_procinit(int i) 180static 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"
243static const char *raid_label[] = { "0", "4", "1(1+0)", "5", "5+1", "ADG", 243static const char *raid_label[] = { "0", "4", "1(1+0)", "5", "5+1", "ADG",
244 "UNKNOWN" 244 "UNKNOWN"
245}; 245};
246 246
247static struct proc_dir_entry *proc_cciss; 247static struct proc_dir_entry *proc_cciss;
248 248
249static int cciss_proc_get_info(char *buffer, char **start, off_t offset, 249static 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
277static 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); 300static 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
328static 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
339static 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
327static int 351static struct seq_operations cciss_seq_ops = {
328cciss_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
358static 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
369static ssize_t
370cciss_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; 409out:
410 free_page((unsigned long)buffer);
411 return err;
357} 412}
358 413
359/* 414static 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
363static void __devinit cciss_procinit(int i) 423static 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