Skip to content

Commit

Permalink
resubmit: cciss: procfs updates to display info about many
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Mike Miller authored and Jens Axboe committed Mar 4, 2008
1 parent 02cf01a commit 89b6e74
Show file tree
Hide file tree
Showing 2 changed files with 160 additions and 103 deletions.
253 changes: 157 additions & 96 deletions drivers/block/cciss.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include <linux/blkpg.h>
#include <linux/timer.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/init.h>
#include <linux/hdreg.h>
#include <linux/spinlock.h>
Expand Down Expand Up @@ -174,8 +175,6 @@ static int sendcmd_withirq(__u8 cmd, int ctlr, void *buff, size_t size,
static void fail_all_cmds(unsigned long ctlr);

#ifdef CONFIG_PROC_FS
static int cciss_proc_get_info(char *buffer, char **start, off_t offset,
int length, int *eof, void *data);
static void cciss_procinit(int i);
#else
static void cciss_procinit(int i)
Expand Down Expand Up @@ -240,140 +239,202 @@ static inline CommandList_struct *removeQ(CommandList_struct **Qptr,
*/
#define ENG_GIG 1000000000
#define ENG_GIG_FACTOR (ENG_GIG/512)
#define ENGAGE_SCSI "engage scsi"
static const char *raid_label[] = { "0", "4", "1(1+0)", "5", "5+1", "ADG",
"UNKNOWN"
};

static struct proc_dir_entry *proc_cciss;

static int cciss_proc_get_info(char *buffer, char **start, off_t offset,
int length, int *eof, void *data)
static void cciss_seq_show_header(struct seq_file *seq)
{
off_t pos = 0;
off_t len = 0;
int size, i, ctlr;
ctlr_info_t *h = (ctlr_info_t *) data;
drive_info_struct *drv;
unsigned long flags;
sector_t vol_sz, vol_sz_frac;
ctlr_info_t *h = seq->private;

seq_printf(seq, "%s: HP %s Controller\n"
"Board ID: 0x%08lx\n"
"Firmware Version: %c%c%c%c\n"
"IRQ: %d\n"
"Logical drives: %d\n"
"Current Q depth: %d\n"
"Current # commands on controller: %d\n"
"Max Q depth since init: %d\n"
"Max # commands on controller since init: %d\n"
"Max SG entries since init: %d\n",
h->devname,
h->product_name,
(unsigned long)h->board_id,
h->firm_ver[0], h->firm_ver[1], h->firm_ver[2],
h->firm_ver[3], (unsigned int)h->intr[SIMPLE_MODE_INT],
h->num_luns,
h->Qdepth, h->commands_outstanding,
h->maxQsinceinit, h->max_outstanding, h->maxSG);

ctlr = h->ctlr;
#ifdef CONFIG_CISS_SCSI_TAPE
cciss_seq_tape_report(seq, h->ctlr);
#endif /* CONFIG_CISS_SCSI_TAPE */
}

static void *cciss_seq_start(struct seq_file *seq, loff_t *pos)
{
ctlr_info_t *h = seq->private;
unsigned ctlr = h->ctlr;
unsigned long flags;

/* prevent displaying bogus info during configuration
* or deconfiguration of a logical volume
*/
spin_lock_irqsave(CCISS_LOCK(ctlr), flags);
if (h->busy_configuring) {
spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags);
return -EBUSY;
return ERR_PTR(-EBUSY);
}
h->busy_configuring = 1;
spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags);

size = sprintf(buffer, "%s: HP %s Controller\n"
"Board ID: 0x%08lx\n"
"Firmware Version: %c%c%c%c\n"
"IRQ: %d\n"
"Logical drives: %d\n"
"Max sectors: %d\n"
"Current Q depth: %d\n"
"Current # commands on controller: %d\n"
"Max Q depth since init: %d\n"
"Max # commands on controller since init: %d\n"
"Max SG entries since init: %d\n\n",
h->devname,
h->product_name,
(unsigned long)h->board_id,
h->firm_ver[0], h->firm_ver[1], h->firm_ver[2],
h->firm_ver[3], (unsigned int)h->intr[SIMPLE_MODE_INT],
h->num_luns,
h->cciss_max_sectors,
h->Qdepth, h->commands_outstanding,
h->maxQsinceinit, h->max_outstanding, h->maxSG);

pos += size;
len += size;
cciss_proc_tape_report(ctlr, buffer, &pos, &len);
for (i = 0; i <= h->highest_lun; i++) {

drv = &h->drv[i];
if (drv->heads == 0)
continue;
if (*pos == 0)
cciss_seq_show_header(seq);

vol_sz = drv->nr_blocks;
vol_sz_frac = sector_div(vol_sz, ENG_GIG_FACTOR);
vol_sz_frac *= 100;
sector_div(vol_sz_frac, ENG_GIG_FACTOR);
return pos;
}

static int cciss_seq_show(struct seq_file *seq, void *v)
{
sector_t vol_sz, vol_sz_frac;
ctlr_info_t *h = seq->private;
unsigned ctlr = h->ctlr;
loff_t *pos = v;
drive_info_struct *drv = &h->drv[*pos];

if (*pos > h->highest_lun)
return 0;

if (drv->heads == 0)
return 0;

vol_sz = drv->nr_blocks;
vol_sz_frac = sector_div(vol_sz, ENG_GIG_FACTOR);
vol_sz_frac *= 100;
sector_div(vol_sz_frac, ENG_GIG_FACTOR);

if (drv->raid_level > 5)
drv->raid_level = RAID_UNKNOWN;
seq_printf(seq, "cciss/c%dd%d:"
"\t%4u.%02uGB\tRAID %s\n",
ctlr, (int) *pos, (int)vol_sz, (int)vol_sz_frac,
raid_label[drv->raid_level]);
return 0;
}

static void *cciss_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
ctlr_info_t *h = seq->private;

if (*pos > h->highest_lun)
return NULL;
*pos += 1;

return pos;
}

static void cciss_seq_stop(struct seq_file *seq, void *v)
{
ctlr_info_t *h = seq->private;

/* Only reset h->busy_configuring if we succeeded in setting
* it during cciss_seq_start. */
if (v == ERR_PTR(-EBUSY))
return;

if (drv->raid_level > 5)
drv->raid_level = RAID_UNKNOWN;
size = sprintf(buffer + len, "cciss/c%dd%d:"
"\t%4u.%02uGB\tRAID %s\n",
ctlr, i, (int)vol_sz, (int)vol_sz_frac,
raid_label[drv->raid_level]);
pos += size;
len += size;
}

*eof = 1;
*start = buffer + offset;
len -= offset;
if (len > length)
len = length;
h->busy_configuring = 0;
return len;
}

static int
cciss_proc_write(struct file *file, const char __user *buffer,
unsigned long count, void *data)
static struct seq_operations cciss_seq_ops = {
.start = cciss_seq_start,
.show = cciss_seq_show,
.next = cciss_seq_next,
.stop = cciss_seq_stop,
};

static int cciss_seq_open(struct inode *inode, struct file *file)
{
unsigned char cmd[80];
int len;
#ifdef CONFIG_CISS_SCSI_TAPE
ctlr_info_t *h = (ctlr_info_t *) data;
int rc;
int ret = seq_open(file, &cciss_seq_ops);
struct seq_file *seq = file->private_data;

if (!ret)
seq->private = PDE(inode)->data;

return ret;
}

static ssize_t
cciss_proc_write(struct file *file, const char __user *buf,
size_t length, loff_t *ppos)
{
int err;
char *buffer;

#ifndef CONFIG_CISS_SCSI_TAPE
return -EINVAL;
#endif

if (count > sizeof(cmd) - 1)
if (!buf || length > PAGE_SIZE - 1)
return -EINVAL;
if (copy_from_user(cmd, buffer, count))
return -EFAULT;
cmd[count] = '\0';
len = strlen(cmd); // above 3 lines ensure safety
if (len && cmd[len - 1] == '\n')
cmd[--len] = '\0';
# ifdef CONFIG_CISS_SCSI_TAPE
if (strcmp("engage scsi", cmd) == 0) {

buffer = (char *)__get_free_page(GFP_KERNEL);
if (!buffer)
return -ENOMEM;

err = -EFAULT;
if (copy_from_user(buffer, buf, length))
goto out;
buffer[length] = '\0';

#ifdef CONFIG_CISS_SCSI_TAPE
if (strncmp(ENGAGE_SCSI, buffer, sizeof ENGAGE_SCSI - 1) == 0) {
struct seq_file *seq = file->private_data;
ctlr_info_t *h = seq->private;
int rc;

rc = cciss_engage_scsi(h->ctlr);
if (rc != 0)
return -rc;
return count;
}
err = -rc;
else
err = length;
} else
#endif /* CONFIG_CISS_SCSI_TAPE */
err = -EINVAL;
/* might be nice to have "disengage" too, but it's not
safely possible. (only 1 module use count, lock issues.) */
# endif
return -EINVAL;

out:
free_page((unsigned long)buffer);
return err;
}

/*
* Get us a file in /proc/cciss that says something about each controller.
* Create /proc/cciss if it doesn't exist yet.
*/
static struct file_operations cciss_proc_fops = {
.owner = THIS_MODULE,
.open = cciss_seq_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
.write = cciss_proc_write,
};

static void __devinit cciss_procinit(int i)
{
struct proc_dir_entry *pde;

if (proc_cciss == NULL) {
if (proc_cciss == NULL)
proc_cciss = proc_mkdir("cciss", proc_root_driver);
if (!proc_cciss)
return;
}
if (!proc_cciss)
return;
pde = proc_create(hba[i]->devname, S_IWUSR | S_IRUSR | S_IRGRP |
S_IROTH, proc_cciss,
&cciss_proc_fops);
if (!pde)
return;

pde = create_proc_read_entry(hba[i]->devname,
S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH,
proc_cciss, cciss_proc_get_info, hba[i]);
pde->write_proc = cciss_proc_write;
pde->data = hba[i];
}
#endif /* CONFIG_PROC_FS */

Expand Down
10 changes: 3 additions & 7 deletions drivers/block/cciss_scsi.c
Original file line number Diff line number Diff line change
Expand Up @@ -1404,21 +1404,18 @@ cciss_engage_scsi(int ctlr)
}

static void
cciss_proc_tape_report(int ctlr, unsigned char *buffer, off_t *pos, off_t *len)
cciss_seq_tape_report(struct seq_file *seq, int ctlr)
{
unsigned long flags;
int size;

*pos = *pos -1; *len = *len - 1; // cut off the last trailing newline

CPQ_TAPE_LOCK(ctlr, flags);
size = sprintf(buffer + *len,
seq_printf(seq,
"Sequential access devices: %d\n\n",
ccissscsi[ctlr].ndevices);
CPQ_TAPE_UNLOCK(ctlr, flags);
*pos += size; *len += size;
}


/* Need at least one of these error handlers to keep ../scsi/hosts.c from
* complaining. Doing a host- or bus-reset can't do anything good here.
* Despite what it might say in scsi_error.c, there may well be commands
Expand Down Expand Up @@ -1498,6 +1495,5 @@ static int cciss_eh_abort_handler(struct scsi_cmnd *scsicmd)
#define cciss_scsi_setup(cntl_num)
#define cciss_unregister_scsi(ctlr)
#define cciss_register_scsi(ctlr)
#define cciss_proc_tape_report(ctlr, buffer, pos, len)

#endif /* CONFIG_CISS_SCSI_TAPE */

0 comments on commit 89b6e74

Please sign in to comment.