From f9fc1633c7327e38d44e2303034d88eda325717b Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 1 Jul 2002 00:41:38 -0700 Subject: Make in-kernel HZ be 1000 on x86, retaining user-level 100 HZ clock_t. Stop using "struct tms" internally - always use timer ticks (or one of the sane timeval/timespec types) instead. Explicitly convert to clock_t when copying to user space for the old broken interfaces that still use "clock_t". Clean up and unify jiffies<->timeval conversion. --- include/linux/sched.h | 3 +-- include/linux/time.h | 42 +++++++++++++++++++++++++++++++----------- include/linux/times.h | 4 ++++ 3 files changed, 36 insertions(+), 13 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index 390627c2f1f6..6a83711022f8 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -10,7 +10,6 @@ extern unsigned long event; #include #include #include -#include #include #include #include @@ -310,7 +309,7 @@ struct task_struct { unsigned long it_real_value, it_prof_value, it_virt_value; unsigned long it_real_incr, it_prof_incr, it_virt_incr; struct timer_list real_timer; - struct tms times; + unsigned long utime, stime, cutime, cstime; unsigned long start_time; long per_cpu_utime[NR_CPUS], per_cpu_stime[NR_CPUS]; /* mm fault and swap info: this can arguably be seen as either mm-specific or thread-specific */ diff --git a/include/linux/time.h b/include/linux/time.h index e7447958ece1..d9f9c6a340d8 100644 --- a/include/linux/time.h +++ b/include/linux/time.h @@ -12,6 +12,16 @@ struct timespec { }; #endif /* _STRUCT_TIMESPEC */ +struct timeval { + time_t tv_sec; /* seconds */ + suseconds_t tv_usec; /* microseconds */ +}; + +struct timezone { + int tz_minuteswest; /* minutes west of Greenwich */ + int tz_dsttime; /* type of dst correction */ +}; + #ifdef __KERNEL__ /* @@ -48,6 +58,27 @@ jiffies_to_timespec(unsigned long jiffies, struct timespec *value) value->tv_sec = jiffies / HZ; } +/* Same for "timeval" */ +static __inline__ unsigned long +timeval_to_jiffies(struct timeval *value) +{ + unsigned long sec = value->tv_sec; + long usec = value->tv_usec; + + if (sec >= (MAX_JIFFY_OFFSET / HZ)) + return MAX_JIFFY_OFFSET; + usec += 1000000L / HZ - 1; + usec /= 1000000L / HZ; + return HZ * sec + usec; +} + +static __inline__ void +jiffies_to_timeval(unsigned long jiffies, struct timeval *value) +{ + value->tv_usec = (jiffies % HZ) * (1000000L / HZ); + value->tv_sec = jiffies / HZ; +} + /* Converts Gregorian date to seconds since 1970-01-01 00:00:00. * Assumes input in normal date format, i.e. 1980-12-31 23:59:59 @@ -88,17 +119,6 @@ extern struct timeval xtime; #endif /* __KERNEL__ */ - -struct timeval { - time_t tv_sec; /* seconds */ - suseconds_t tv_usec; /* microseconds */ -}; - -struct timezone { - int tz_minuteswest; /* minutes west of Greenwich */ - int tz_dsttime; /* type of dst correction */ -}; - #define NFDBITS __NFDBITS #ifdef __KERNEL__ diff --git a/include/linux/times.h b/include/linux/times.h index 569349ef461e..1174e9f88ea2 100644 --- a/include/linux/times.h +++ b/include/linux/times.h @@ -1,6 +1,10 @@ #ifndef _LINUX_TIMES_H #define _LINUX_TIMES_H +#ifdef __KERNEL__ +# define jiffies_to_clock_t(x) ((x) / (HZ / USER_HZ)) +#endif + struct tms { clock_t tms_utime; clock_t tms_stime; -- cgit v1.2.3 From aa44f80a78db47189d15a67ffa329e3cefe22904 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Mon, 1 Jul 2002 04:36:58 -0400 Subject: [PATCH] softscsi patch Doug Gilbert and James Bottomley hassled me all through KernelSummit & OLS to explain about softirqs, tasklets and bottom halves. In the end, it was easier to write the code myself. Thanks to James for pointing out that the pointer handling in my original code was completely broken and helping me debug. I've booted this patch on a 4-way system at OSDL with two Adaptec SCSI cards. I haven't tried stressing it (not quite sure which discs I can use ;-), and I don't understand the locking in the scsi subsystem at all. The main effect of applying this patch is that scsi_softirq() [was scsi_tasklet_func, and before that scsi_bottom_half_handler()] can now be run on multiple CPUs at the same time. We _seem_ to do enough locking elsewhere in the SCSI stack that this is safe. But someone who really understands the SCSI stack should audit this. This work shows up a hole in the current softirq API -- there's no support for unregistering a softirq (close_softirq or similar). We should do this in scsi_exit -- make sure no softirqs are running while we unload. This probably isn't a problem in practice, but it'd be nice to fix it. --- drivers/scsi/scsi.c | 189 +++++++++++++++++++--------------------------- include/linux/interrupt.h | 1 + 2 files changed, 80 insertions(+), 110 deletions(-) (limited to 'include/linux') diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 0215c45cbd37..cdcf1e3c871a 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -130,8 +130,13 @@ const unsigned char scsi_command_size[8] = 16, 12, 10, 10 }; static unsigned long serial_number; -static Scsi_Cmnd *scsi_bh_queue_head; -static Scsi_Cmnd *scsi_bh_queue_tail; + +struct softscsi_data { + Scsi_Cmnd *head; + Scsi_Cmnd *tail; +}; + +static struct softscsi_data softscsi_data[NR_CPUS] __cacheline_aligned; /* * Note - the initial logging level can be set here to log events at boot time. @@ -270,12 +275,6 @@ static void scsi_wait_done(Scsi_Cmnd * SCpnt) */ static spinlock_t device_request_lock = SPIN_LOCK_UNLOCKED; -/* - * Used to protect insertion into and removal from the queue of - * commands to be processed by the bottom half handler. - */ -static spinlock_t scsi_bhqueue_lock = SPIN_LOCK_UNLOCKED; - /* * Function: scsi_allocate_request * @@ -1089,37 +1088,29 @@ void scsi_do_cmd(Scsi_Cmnd * SCpnt, const void *cmnd, SCSI_LOG_MLQUEUE(3, printk("Leaving scsi_do_cmd()\n")); } -void scsi_tasklet_func(unsigned long); -static DECLARE_TASKLET(scsi_tasklet, scsi_tasklet_func, 0); - -/* +/** + * scsi_done - Mark this command as done + * @SCpnt: The SCSI Command which we think we've completed. + * * This function is the mid-level interrupt routine, which decides how - * to handle error conditions. Each invocation of this function must - * do one and *only* one of the following: + * to handle error conditions. Each invocation of this function must + * do one and *only* one of the following: * * 1) Insert command in BH queue. * 2) Activate error handler for host. * - * FIXME(eric) - I am concerned about stack overflow (still). An - * interrupt could come while we are processing the bottom queue, - * which would cause another command to be stuffed onto the bottom - * queue, and it would in turn be processed as that interrupt handler - * is returning. Given a sufficiently steady rate of returning - * commands, this could cause the stack to overflow. I am not sure - * what is the most appropriate solution here - we should probably - * keep a depth count, and not process any commands while we still - * have a bottom handler active higher in the stack. - * - * There is currently code in the bottom half handler to monitor - * recursion in the bottom handler and report if it ever happens. If - * this becomes a problem, it won't be hard to engineer something to - * deal with it so that only the outer layer ever does any real - * processing. + * There is no longer a problem with stack overflow. Interrupts queue + * Scsi_Cmnd on a per-CPU queue and the softirq handler removes them + * from the queue one at a time. + * + * This function is sometimes called from interrupt context, but sometimes + * from task context. */ void scsi_done(Scsi_Cmnd * SCpnt) { unsigned long flags; - int tstatus; + int cpu, tstatus; + struct softscsi_data *queue; /* * We don't have to worry about this one timing out any more. @@ -1155,7 +1146,6 @@ void scsi_done(Scsi_Cmnd * SCpnt) SCSI_LOG_MLCOMPLETE(1, printk("Ignoring completion of %p due to timeout status", SCpnt)); return; } - spin_lock_irqsave(&scsi_bhqueue_lock, flags); SCpnt->serial_number_at_timeout = 0; SCpnt->state = SCSI_STATE_BHQUEUE; @@ -1163,75 +1153,49 @@ void scsi_done(Scsi_Cmnd * SCpnt) SCpnt->bh_next = NULL; /* - * Next, put this command in the BH queue. - * - * We need a spinlock here, or compare and exchange if we can reorder incoming - * Scsi_Cmnds, as it happens pretty often scsi_done is called multiple times - * before bh is serviced. -jj + * Next, put this command in the softirq queue. * - * We already have the io_request_lock here, since we are called from the - * interrupt handler or the error handler. (DB) - * - * This may be true at the moment, but I would like to wean all of the low - * level drivers away from using io_request_lock. Technically they should - * all use their own locking. I am adding a small spinlock to protect - * this datastructure to make it safe for that day. (ERY) + * This is a per-CPU queue, so we just disable local interrupts + * and need no spinlock. */ - if (!scsi_bh_queue_head) { - scsi_bh_queue_head = SCpnt; - scsi_bh_queue_tail = SCpnt; + + local_irq_save(flags); + + cpu = smp_processor_id(); + queue = &softscsi_data[cpu]; + + if (!queue->head) { + queue->head = SCpnt; + queue->tail = SCpnt; } else { - scsi_bh_queue_tail->bh_next = SCpnt; - scsi_bh_queue_tail = SCpnt; + queue->tail->bh_next = SCpnt; + queue->tail = SCpnt; } - spin_unlock_irqrestore(&scsi_bhqueue_lock, flags); - /* - * Mark the bottom half handler to be run. - */ - tasklet_hi_schedule(&scsi_tasklet); + cpu_raise_softirq(cpu, SCSI_SOFTIRQ); + + local_irq_restore(flags); } -/* - * Procedure: scsi_bottom_half_handler - * - * Purpose: Called after we have finished processing interrupts, it - * performs post-interrupt handling for commands that may - * have completed. - * - * Notes: This is called with all interrupts enabled. This should reduce - * interrupt latency, stack depth, and reentrancy of the low-level - * drivers. - * - * The io_request_lock is required in all the routine. There was a subtle - * race condition when scsi_done is called after a command has already - * timed out but before the time out is processed by the error handler. - * (DB) - * - * I believe I have corrected this. We simply monitor the return status of - * del_timer() - if this comes back as 0, it means that the timer has fired - * and that a timeout is in progress. I have modified scsi_done() such - * that in this instance the command is never inserted in the bottom - * half queue. Thus the only time we hold the lock here is when - * we wish to atomically remove the contents of the queue. +/** + * scsi_softirq - Perform post-interrupt handling for completed commands + * + * This is called with all interrupts enabled. This should reduce + * interrupt latency, stack depth, and reentrancy of the low-level + * drivers. */ -void scsi_tasklet_func(unsigned long ignore) +static void scsi_softirq(struct softirq_action *h) { - Scsi_Cmnd *SCpnt; - Scsi_Cmnd *SCnext; - unsigned long flags; - + int cpu = smp_processor_id(); + struct softscsi_data *queue = &softscsi_data[cpu]; - while (1 == 1) { - spin_lock_irqsave(&scsi_bhqueue_lock, flags); - SCpnt = scsi_bh_queue_head; - scsi_bh_queue_head = NULL; - spin_unlock_irqrestore(&scsi_bhqueue_lock, flags); + while (queue->head) { + Scsi_Cmnd *SCpnt, *SCnext; - if (SCpnt == NULL) { - return; - } - SCnext = SCpnt->bh_next; + local_irq_disable(); + SCpnt = queue->head; + queue->head = NULL; + local_irq_enable(); for (; SCpnt; SCpnt = SCnext) { SCnext = SCpnt->bh_next; @@ -1249,10 +1213,11 @@ void scsi_tasklet_func(unsigned long ignore) break; case NEEDS_RETRY: /* - * We only come in here if we want to retry a command. The - * test to see whether the command should be retried should be - * keeping track of the number of tries, so we don't end up looping, - * of course. + * We only come in here if we want to retry a + * command. The test to see whether the + * command should be retried should be keeping + * track of the number of tries, so we don't + * end up looping, of course. */ SCSI_LOG_MLCOMPLETE(3, printk("Command needs retry %d %d 0x%x\n", SCpnt->host->host_busy, SCpnt->host->host_failed, SCpnt->result)); @@ -1261,12 +1226,14 @@ void scsi_tasklet_func(unsigned long ignore) break; case ADD_TO_MLQUEUE: /* - * This typically happens for a QUEUE_FULL message - - * typically only when the queue depth is only - * approximate for a given device. Adding a command - * to the queue for the device will prevent further commands - * from being sent to the device, so we shouldn't end up - * with tons of things being sent down that shouldn't be. + * This typically happens for a QUEUE_FULL + * message - typically only when the queue + * depth is only approximate for a given + * device. Adding a command to the queue for + * the device will prevent further commands + * from being sent to the device, so we + * shouldn't end up with tons of things being + * sent down that shouldn't be. */ SCSI_LOG_MLCOMPLETE(3, printk("Command rejected as device queue full, put on ml queue %p\n", SCpnt)); @@ -1274,8 +1241,8 @@ void scsi_tasklet_func(unsigned long ignore) break; default: /* - * Here we have a fatal error of some sort. Turn it over to - * the error handler. + * Here we have a fatal error of some sort. + * Turn it over to the error handler. */ SCSI_LOG_MLCOMPLETE(3, printk("Command failed %p %x active=%d busy=%d failed=%d\n", SCpnt, SCpnt->result, @@ -1295,8 +1262,10 @@ void scsi_tasklet_func(unsigned long ignore) SCpnt->state = SCSI_STATE_FAILED; SCpnt->host->in_recovery = 1; /* - * If the host is having troubles, then look to see if this was the last - * command that might have failed. If so, wake up the error handler. + * If the host is having troubles, then + * look to see if this was the last + * command that might have failed. If + * so, wake up the error handler. */ if (SCpnt->host->host_busy == SCpnt->host->host_failed) { SCSI_LOG_ERROR_RECOVERY(5, printk("Waking error handler thread (%d)\n", @@ -1305,15 +1274,14 @@ void scsi_tasklet_func(unsigned long ignore) } } else { /* - * We only get here if the error recovery thread has died. + * We only get here if the error + * recovery thread has died. */ scsi_finish_command(SCpnt); } - } + } /* switch */ } /* for(; SCpnt...) */ - - } /* while(1==1) */ - + } /* while(queue->head) */ } /* @@ -2548,6 +2516,9 @@ static int __init init_scsi(void) printk(KERN_INFO "scsi: host order: %s\n", scsihosts); scsi_host_no_init (scsihosts); + /* Where we handle work queued by scsi_done */ + open_softirq(SCSI_SOFTIRQ, scsi_softirq, NULL); + return 0; } @@ -2556,8 +2527,6 @@ static void __exit exit_scsi(void) Scsi_Host_Name *shn, *shn2 = NULL; int i; - tasklet_kill(&scsi_tasklet); - devfs_unregister (scsi_devfs_handle); for (shn = scsi_host_no_list;shn;shn = shn->next) { if (shn->name) diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index fbc10eab16f4..3870d26066e9 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -57,6 +57,7 @@ enum HI_SOFTIRQ=0, NET_TX_SOFTIRQ, NET_RX_SOFTIRQ, + SCSI_SOFTIRQ, TASKLET_SOFTIRQ }; -- cgit v1.2.3 From cc236b9a01356d7a5586b38b0f1649dcd3f9cc2a Mon Sep 17 00:00:00 2001 From: Mike Sullivan Date: Tue, 2 Jul 2002 02:38:32 -0400 Subject: linux-2.5.22-driverfs.patch --- drivers/cdrom/cdrom.c | 5 + drivers/scsi/hosts.h | 7 + drivers/scsi/scsi.c | 39 +++++ drivers/scsi/scsi.h | 3 + drivers/scsi/scsi_scan.c | 413 +++++++++++++++++++++++++++++++++++++++++++++++ drivers/scsi/scsi_syms.c | 5 + drivers/scsi/sd.c | 22 ++- drivers/scsi/sg.c | 50 +++++- drivers/scsi/sr.c | 50 +++++- drivers/scsi/st.c | 73 ++++++++- drivers/scsi/st.h | 2 + fs/partitions/check.c | 138 ++++++++++++++++ include/linux/cdrom.h | 2 + include/linux/genhd.h | 4 + kernel/ksyms.c | 1 + 15 files changed, 807 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c index fc2fa884e1ed..5092049cdb57 100644 --- a/drivers/cdrom/cdrom.c +++ b/drivers/cdrom/cdrom.c @@ -431,6 +431,11 @@ int unregister_cdrom(struct cdrom_device_info *unreg) topCdromPtr = cdi->next; cdi->ops->n_minors--; devfs_unregister (cdi->de); + if (atomic_read (&cdi->cdrom_driverfs_dev.refcount)) { + device_remove_file (&cdi->cdrom_driverfs_dev, "name"); + device_remove_file (&cdi->cdrom_driverfs_dev, "kdev"); + put_device (&cdi->cdrom_driverfs_dev); + } devfs_dealloc_unique_number (&cdrom_numspace, cdi->number); cdinfo(CD_REG_UNREG, "drive \"/dev/%s\" unregistered\n", cdi->name); return 0; diff --git a/drivers/scsi/hosts.h b/drivers/scsi/hosts.h index 8604eb35140a..e1ff2b28ae3f 100644 --- a/drivers/scsi/hosts.h +++ b/drivers/scsi/hosts.h @@ -418,6 +418,11 @@ struct Scsi_Host */ struct pci_dev *pci_dev; + /* + * Support for driverfs filesystem + */ + struct device host_driverfs_dev; + /* * We should ensure that this is aligned, both for better performance * and also because some compilers (m68k) don't automatically force @@ -478,6 +483,7 @@ static inline void scsi_set_pci_device(struct Scsi_Host *SHpnt, struct pci_dev *pdev) { SHpnt->pci_dev = pdev; + SHpnt->host_driverfs_dev.parent=&pdev->dev; } @@ -516,6 +522,7 @@ struct Scsi_Device_Template void (*detach)(Scsi_Device *); int (*init_command)(Scsi_Cmnd *); /* Used by new queueing code. Selects command for blkdevs */ + struct device_driver scsi_driverfs_driver; }; void scsi_initialize_queue(Scsi_Device * SDpnt, struct Scsi_Host * SHpnt); diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index b83a0ec80e03..07c62b09a402 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -1939,6 +1939,11 @@ int scsi_register_host(Scsi_Host_Template * tpnt) } printk(KERN_INFO "scsi%d : %s\n", /* And print a little message */ shpnt->host_no, name); + strncpy(shpnt->host_driverfs_dev.name,name, + DEVICE_NAME_SIZE-1); + sprintf(shpnt->host_driverfs_dev.bus_id, + "scsi%d", + shpnt->host_no); } } @@ -1947,6 +1952,8 @@ int scsi_register_host(Scsi_Host_Template * tpnt) */ for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) { if (shpnt->hostt == tpnt) { + /* first register parent with driverfs */ + device_register(&shpnt->host_driverfs_dev); scan_scsis(shpnt, 0, 0, 0, 0); if (shpnt->select_queue_depths != NULL) { (shpnt->select_queue_depths) (shpnt, shpnt->host_queue); @@ -2101,6 +2108,7 @@ int scsi_unregister_host(Scsi_Host_Template * tpnt) goto err_out; } devfs_unregister (SDpnt->de); + put_device(&SDpnt->sdev_driverfs_dev); } } @@ -2151,6 +2159,7 @@ int scsi_unregister_host(Scsi_Host_Template * tpnt) /* Remove the /proc/scsi directory entry */ sprintf(name,"%d",shpnt->host_no); remove_proc_entry(name, tpnt->proc_dir); + put_device(&shpnt->host_driverfs_dev); if (tpnt->release) (*tpnt->release) (shpnt); else { @@ -2499,6 +2508,34 @@ void scsi_free_sgtable(struct scatterlist *sgl, int index) mempool_free(sgl, sgp->pool); } +static int scsi_bus_match(struct device *scsi_driverfs_dev, + struct device_driver *scsi_driverfs_drv) +{ + char *p=0; + + if (!strcmp("sd", scsi_driverfs_drv->name)) { + if ((p = strstr(scsi_driverfs_dev->bus_id, ":disc")) || + (p = strstr(scsi_driverfs_dev->bus_id, ":p"))) { + return 1; + } + } else if (!strcmp("sg", scsi_driverfs_drv->name)) { + if (strstr(scsi_driverfs_dev->bus_id, ":gen")) + return 1; + } else if (!strcmp("sr",scsi_driverfs_drv->name)) { + if (strstr(scsi_driverfs_dev->bus_id,":cd")) + return 1; + } else if (!strcmp("st",scsi_driverfs_drv->name)) { + if (strstr(scsi_driverfs_dev->bus_id,":mt")) + return 1; + } + return 0; +} + +struct bus_type scsi_driverfs_bus_type = { + name: "scsi", + match: scsi_bus_match, +}; + static int __init init_scsi(void) { struct proc_dir_entry *generic; @@ -2544,6 +2581,8 @@ static int __init init_scsi(void) if (scsihosts) printk(KERN_INFO "scsi: host order: %s\n", scsihosts); scsi_host_no_init (scsihosts); + + bus_register(&scsi_driverfs_bus_type); /* * This is where the processing takes place for most everything * when commands are completed. diff --git a/drivers/scsi/scsi.h b/drivers/scsi/scsi.h index cd1df44e19a6..32458a0acfa7 100644 --- a/drivers/scsi/scsi.h +++ b/drivers/scsi/scsi.h @@ -417,6 +417,8 @@ extern unsigned int scsi_need_isa_buffer; /* True if some devices need indirecti extern volatile int in_scan_scsis; extern const unsigned char scsi_command_size[8]; +extern struct bus_type scsi_driverfs_bus_type; + /* * These are the error handling functions defined in scsi_error.c @@ -622,6 +624,7 @@ struct scsi_device { // Flag to allow revalidate to succeed in sd_open int allow_revalidate; + struct device sdev_driverfs_dev; }; diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index bf046d01f433..3a2b1fcbf760 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -54,6 +54,7 @@ static void scan_scsis_target(unsigned int channel, unsigned int dev, char *scsi_result); static int find_lun0_scsi_level(unsigned int channel, unsigned int dev, struct Scsi_Host *shpnt); +static void scsi_load_identifier(Scsi_Device *SDpnt, Scsi_Request * SRpnt); struct dev_info { const char *vendor; @@ -288,6 +289,31 @@ static int scsilun_to_int(ScsiLun *scsilun_pnt) } #endif +/* Driverfs file content handlers */ +static ssize_t scsi_device_type_read(struct device *driverfs_dev, char *page, + size_t count, loff_t off) +{ + struct scsi_device *SDpnt = list_entry(driverfs_dev, + struct scsi_device, sdev_driverfs_dev); + + if ((SDpnt->type <= MAX_SCSI_DEVICE_CODE) && + (scsi_device_types[(int)SDpnt->type] != NULL)) + return off ? 0 : + sprintf(page, "%s\n", + scsi_device_types[(int)SDpnt->type]); + else + return off ? 0 : sprintf(page, "UNKNOWN\n"); + + return 0; +} + +static struct driver_file_entry scsi_device_type_file = { + name: "type", + mode: S_IRUGO, + show: scsi_device_type_read, +}; +/* end content handlers */ + static void print_inquiry(unsigned char *data) { int i; @@ -786,6 +812,22 @@ static int scan_scsis_single(unsigned int channel, unsigned int dev, print_inquiry(scsi_result); + /* interrogate scsi target to provide device identifier */ + scsi_load_identifier(SDpnt, SRpnt); + + /* create driverfs files */ + sprintf(SDpnt->sdev_driverfs_dev.bus_id,"%d:%d:%d:%d", + SDpnt->host->host_no, SDpnt->channel, SDpnt->id, SDpnt->lun); + + SDpnt->sdev_driverfs_dev.parent = &SDpnt->host->host_driverfs_dev; + SDpnt->sdev_driverfs_dev.bus = &scsi_driverfs_bus_type; + + device_register(&SDpnt->sdev_driverfs_dev); + + /* Create driverfs file entries */ + device_create_file(&SDpnt->sdev_driverfs_dev, + &scsi_device_type_file); + sprintf (devname, "host%d/bus%d/target%d/lun%d", SDpnt->host->host_no, SDpnt->channel, SDpnt->id, SDpnt->lun); if (SDpnt->de) printk ("DEBUG: dir: \"%s\" already exists\n", devname); @@ -1306,3 +1348,374 @@ static int find_lun0_scsi_level(unsigned int channel, unsigned int dev, /* haven't found lun0, should send INQUIRY but take easy route */ return res; } + +#define SCSI_UID_DEV_ID 'U' +#define SCSI_UID_SER_NUM 'S' +#define SCSI_UID_UNKNOWN 'Z' + +unsigned char *scsi_get_evpd_page(Scsi_Device *SDpnt, Scsi_Request * SRpnt) +{ + unsigned char *evpd_page = NULL; + unsigned char *scsi_cmd = NULL; + int lun = SDpnt->lun; + int scsi_level = SDpnt->scsi_level; + + evpd_page = kmalloc(255, + (SDpnt->host->unchecked_isa_dma ? GFP_DMA : GFP_ATOMIC)); + if (!evpd_page) + return NULL; + + scsi_cmd = kmalloc(MAX_COMMAND_SIZE, GFP_ATOMIC); + if (!scsi_cmd) { + kfree(evpd_page); + return NULL; + } + + /* Use vital product pages to determine serial number */ + /* Try Supported vital product data pages 0x00 first */ + scsi_cmd[0] = INQUIRY; + if ((lun > 0) && (scsi_level <= SCSI_2)) + scsi_cmd[1] = ((lun << 5) & 0xe0) | 0x01; + else + scsi_cmd[1] = 0x01; /* SCSI_3 and higher, don't touch */ + scsi_cmd[2] = 0x00; + scsi_cmd[3] = 0; + scsi_cmd[4] = 255; + scsi_cmd[5] = 0; + SRpnt->sr_cmd_len = 0; + SRpnt->sr_sense_buffer[0] = 0; + SRpnt->sr_sense_buffer[2] = 0; + SRpnt->sr_data_direction = SCSI_DATA_READ; + scsi_wait_req(SRpnt, (void *) scsi_cmd, (void *) evpd_page, + 255, SCSI_TIMEOUT+4*HZ, 3); + + if (SRpnt->sr_result) { + kfree(scsi_cmd); + kfree(evpd_page); + return NULL; + } + + /* check to see if response was truncated */ + if (evpd_page[3] > 255) { + int max_lgth = evpd_page[3] + 4; + + kfree(evpd_page); + evpd_page = kmalloc(max_lgth, (SDpnt->host->unchecked_isa_dma ? + GFP_DMA : GFP_ATOMIC)); + if (!evpd_page) { + kfree(scsi_cmd); + return NULL; + } + memset(scsi_cmd, 0, MAX_COMMAND_SIZE); + scsi_cmd[0] = INQUIRY; + if ((lun > 0) && (scsi_level <= SCSI_2)) + scsi_cmd[1] = ((lun << 5) & 0xe0) | 0x01; + else + scsi_cmd[1] = 0x01; /* SCSI_3 and higher, don't touch */ + scsi_cmd[2] = 0x00; + scsi_cmd[3] = 0; + scsi_cmd[4] = max_lgth; + scsi_cmd[5] = 0; + SRpnt->sr_cmd_len = 0; + SRpnt->sr_sense_buffer[0] = 0; + SRpnt->sr_sense_buffer[2] = 0; + SRpnt->sr_data_direction = SCSI_DATA_READ; + scsi_wait_req(SRpnt, (void *) scsi_cmd, (void *) evpd_page, + max_lgth, SCSI_TIMEOUT+4*HZ, 3); + if (SRpnt->sr_result) { + kfree(scsi_cmd); + kfree(evpd_page); + return NULL; + } + } + kfree(scsi_cmd); + /* some ill behaved devices return the std inquiry here rather than + the evpd data. snoop the data to verify */ + if (evpd_page[3] > 16) { + /* if vend id appears in the evpd page assume evpd is invalid */ + if (!strncmp(&evpd_page[8], SDpnt->vendor, 8)) { + kfree(evpd_page); + return NULL; + } + } + return evpd_page; +} + +int scsi_get_deviceid(Scsi_Device *SDpnt,Scsi_Request * SRpnt) +{ + unsigned char *id_page = NULL; + unsigned char *scsi_cmd = NULL; + int scnt, i, j, idtype; + char * id = SDpnt->sdev_driverfs_dev.name; + int lun = SDpnt->lun; + int scsi_level = SDpnt->scsi_level; + + id_page = kmalloc(255, + (SDpnt->host->unchecked_isa_dma ? GFP_DMA : GFP_ATOMIC)); + if (!id_page) + return 0; + + scsi_cmd = kmalloc(MAX_COMMAND_SIZE, GFP_ATOMIC); + if (!scsi_cmd) + goto leave; + + /* Use vital product pages to determine serial number */ + /* Try Supported vital product data pages 0x00 first */ + scsi_cmd[0] = INQUIRY; + if ((lun > 0) && (scsi_level <= SCSI_2)) + scsi_cmd[1] = ((lun << 5) & 0xe0) | 0x01; + else + scsi_cmd[1] = 0x01; /* SCSI_3 and higher, don't touch */ + scsi_cmd[2] = 0x83; + scsi_cmd[3] = 0; + scsi_cmd[4] = 255; + scsi_cmd[5] = 0; + SRpnt->sr_cmd_len = 0; + SRpnt->sr_sense_buffer[0] = 0; + SRpnt->sr_sense_buffer[2] = 0; + SRpnt->sr_data_direction = SCSI_DATA_READ; + scsi_wait_req(SRpnt, (void *) scsi_cmd, (void *) id_page, + 255, SCSI_TIMEOUT+4*HZ, 3); + if (SRpnt->sr_result) { + kfree(scsi_cmd); + goto leave; + } + + /* check to see if response was truncated */ + if (id_page[3] > 255) { + int max_lgth = id_page[3] + 4; + + kfree(id_page); + id_page = kmalloc(max_lgth, + (SDpnt->host->unchecked_isa_dma ? + GFP_DMA : GFP_ATOMIC)); + if (!id_page) { + kfree(scsi_cmd); + return 0; + } + memset(scsi_cmd, 0, MAX_COMMAND_SIZE); + scsi_cmd[0] = INQUIRY; + if ((lun > 0) && (scsi_level <= SCSI_2)) + scsi_cmd[1] = ((lun << 5) & 0xe0) | 0x01; + else + scsi_cmd[1] = 0x01; /* SCSI_3 and higher, don't touch */ + scsi_cmd[2] = 0x83; + scsi_cmd[3] = 0; + scsi_cmd[4] = max_lgth; + scsi_cmd[5] = 0; + SRpnt->sr_cmd_len = 0; + SRpnt->sr_sense_buffer[0] = 0; + SRpnt->sr_sense_buffer[2] = 0; + SRpnt->sr_data_direction = SCSI_DATA_READ; + scsi_wait_req(SRpnt, (void *) scsi_cmd, (void *) id_page, + max_lgth, SCSI_TIMEOUT+4*HZ, 3); + if (SRpnt->sr_result) { + kfree(scsi_cmd); + goto leave; + } + } + kfree(scsi_cmd); + + idtype = 3; + while (idtype > 0) { + for(scnt = 4; scnt <= id_page[3] + 3; + scnt += id_page[scnt + 3] + 4) { + if ((id_page[scnt + 1] & 0x0f) != idtype) { + continue; + } + if ((id_page[scnt] & 0x0f) == 2) { + for(i = scnt + 4, j = 1; + i < scnt + 4 + id_page[scnt + 3]; + i++) { + if (id_page[i] > 0x20) { + if (j == DEVICE_NAME_SIZE) { + memset(id, 0, + DEVICE_NAME_SIZE); + break; + } + id[j++] = id_page[i]; + } + } + } else if ((id_page[scnt] & 0x0f) == 1) { + static const char hex_str[]="0123456789abcdef"; + for(i = scnt + 4, j = 1; + i < scnt + 4 + id_page[scnt + 3]; + i++) { + if ((j + 1) == DEVICE_NAME_SIZE) { + memset(id, 0, DEVICE_NAME_SIZE); + break; + } + id[j++] = hex_str[(id_page[i] & 0xf0) >> + 4]; + id[j++] = hex_str[id_page[i] & 0x0f]; + } + } + if (id[1] != 0) goto leave; + } + idtype--; + } + leave: + kfree(id_page); + if (id[1] != 0) { + id[0] = SCSI_UID_DEV_ID; + return 1; + } + return 0; +} + +int scsi_get_serialnumber(Scsi_Device *SDpnt, Scsi_Request * SRpnt) +{ + unsigned char *serialnumber_page = NULL; + unsigned char *scsi_cmd = NULL; + int lun = SDpnt->lun; + int scsi_level = SDpnt->scsi_level; + int i, j; + + serialnumber_page = kmalloc(255, (SDpnt->host->unchecked_isa_dma ? + GFP_DMA : GFP_ATOMIC)); + if (!serialnumber_page) + return 0; + + scsi_cmd = kmalloc(MAX_COMMAND_SIZE, GFP_ATOMIC); + if (!scsi_cmd) + goto leave; + + /* Use vital product pages to determine serial number */ + /* Try Supported vital product data pages 0x00 first */ + scsi_cmd[0] = INQUIRY; + if ((lun > 0) && (scsi_level <= SCSI_2)) + scsi_cmd[1] = ((lun << 5) & 0xe0) | 0x01; + else + scsi_cmd[1] = 0x01; /* SCSI_3 and higher, don't touch */ + scsi_cmd[2] = 0x80; + scsi_cmd[3] = 0; + scsi_cmd[4] = 255; + scsi_cmd[5] = 0; + SRpnt->sr_cmd_len = 0; + SRpnt->sr_sense_buffer[0] = 0; + SRpnt->sr_sense_buffer[2] = 0; + SRpnt->sr_data_direction = SCSI_DATA_READ; + scsi_wait_req(SRpnt, (void *) scsi_cmd, (void *) serialnumber_page, + 255, SCSI_TIMEOUT+4*HZ, 3); + + if (SRpnt->sr_result) { + kfree(scsi_cmd); + goto leave; + } + /* check to see if response was truncated */ + if (serialnumber_page[3] > 255) { + int max_lgth = serialnumber_page[3] + 4; + + kfree(serialnumber_page); + serialnumber_page = kmalloc(max_lgth, + (SDpnt->host->unchecked_isa_dma ? + GFP_DMA : GFP_ATOMIC)); + if (!serialnumber_page) { + kfree(scsi_cmd); + return 0; + } + memset(scsi_cmd, 0, MAX_COMMAND_SIZE); + scsi_cmd[0] = INQUIRY; + if ((lun > 0) && (scsi_level <= SCSI_2)) + scsi_cmd[1] = ((lun << 5) & 0xe0) | 0x01; + else + scsi_cmd[1] = 0x01; /* SCSI_3 and higher, don't touch */ + scsi_cmd[2] = 0x80; + scsi_cmd[3] = 0; + scsi_cmd[4] = max_lgth; + scsi_cmd[5] = 0; + SRpnt->sr_cmd_len = 0; + SRpnt->sr_sense_buffer[0] = 0; + SRpnt->sr_sense_buffer[2] = 0; + SRpnt->sr_data_direction = SCSI_DATA_READ; + scsi_wait_req(SRpnt, (void *) scsi_cmd, + (void *) serialnumber_page, + max_lgth, SCSI_TIMEOUT+4*HZ, 3); + if (SRpnt->sr_result) { + kfree(scsi_cmd); + goto leave; + } + } + kfree(scsi_cmd); + + SDpnt->sdev_driverfs_dev.name[0] = SCSI_UID_SER_NUM; + for(i = 0, j = 1; i < serialnumber_page[3]; i++) { + if (serialnumber_page[4 + i] > 0x20) + SDpnt->sdev_driverfs_dev.name[j++] = + serialnumber_page[4 + i]; + } + for(i = 0, j = strlen(SDpnt->sdev_driverfs_dev.name); i < 8; i++) { + if (SDpnt->vendor[i] > 0x20) { + SDpnt->sdev_driverfs_dev.name[j++] = SDpnt->vendor[i]; + } + } + kfree(serialnumber_page); + return 1; + leave: + memset(SDpnt->sdev_driverfs_dev.name, 0, DEVICE_NAME_SIZE); + kfree(serialnumber_page); + return 0; +} + +int scsi_get_default_name(Scsi_Device *SDpnt) +{ + int i, j; + + SDpnt->sdev_driverfs_dev.name[0] = SCSI_UID_UNKNOWN; + for(i = 0, j = 1; i < 8; i++) { + if (SDpnt->vendor[i] > 0x20) { + SDpnt->sdev_driverfs_dev.name[j++] = + SDpnt->vendor[i]; + } + } + for(i = 0, j = strlen(SDpnt->sdev_driverfs_dev.name); + i < 16; i++) { + if (SDpnt->model[i] > 0x20) { + SDpnt->sdev_driverfs_dev.name[j++] = + SDpnt->model[i]; + } + } + for(i = 0, j = strlen(SDpnt->sdev_driverfs_dev.name); i < 4; i++) { + if (SDpnt->rev[i] > 0x20) { + SDpnt->sdev_driverfs_dev.name[j++] = SDpnt->rev[i]; + } + } + return 1; +} + + +static void scsi_load_identifier(Scsi_Device *SDpnt, Scsi_Request * SRpnt) +{ + unsigned char *evpd_page = NULL; + int cnt; + + memset(SDpnt->sdev_driverfs_dev.name, 0, DEVICE_NAME_SIZE); + evpd_page = scsi_get_evpd_page(SDpnt, SRpnt); + if (!evpd_page) { + /* try to obtain serial number anyway */ + if (!scsi_get_serialnumber(SDpnt, SRpnt)) + goto leave; + goto leave; + } + + for(cnt = 4; cnt <= evpd_page[3] + 3; cnt++) { + if (evpd_page[cnt] == 0x83) { + if (scsi_get_deviceid(SDpnt, SRpnt)) + goto leave; + } + } + + for(cnt = 4; cnt <= evpd_page[3] + 3; cnt++) { + if (evpd_page[cnt] == 0x80) { + if (scsi_get_serialnumber(SDpnt, SRpnt)) + goto leave; + } + } + +leave: + if (SDpnt->sdev_driverfs_dev.name[0] == 0) + scsi_get_default_name(SDpnt); + + if (evpd_page) kfree(evpd_page); + return; +} diff --git a/drivers/scsi/scsi_syms.c b/drivers/scsi/scsi_syms.c index fcc1dd92512c..20a06b1be288 100644 --- a/drivers/scsi/scsi_syms.c +++ b/drivers/scsi/scsi_syms.c @@ -101,3 +101,8 @@ extern void scsi_add_timer(Scsi_Cmnd *, int, void ((*) (Scsi_Cmnd *))); extern int scsi_delete_timer(Scsi_Cmnd *); EXPORT_SYMBOL(scsi_add_timer); EXPORT_SYMBOL(scsi_delete_timer); + +/* + * driverfs support for determining driver types + */ +EXPORT_SYMBOL(scsi_driverfs_bus_type); diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 382e04ceace2..8669b5e7f587 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -128,6 +128,7 @@ static void sd_rw_intr(Scsi_Cmnd * SCpnt); static Scsi_Disk * sd_get_sdisk(int index); +extern void driverfs_remove_partitions(struct gendisk *hd, int minor); #if defined(CONFIG_PPC32) /** @@ -1276,12 +1277,15 @@ static int sd_init() init_mem_lth(sd_gendisks[k].de_arr, N); init_mem_lth(sd_gendisks[k].flags, N); + init_mem_lth(sd_gendisks[k].driverfs_dev_arr, N); - if (!sd_gendisks[k].de_arr || !sd_gendisks[k].flags) + if (!sd_gendisks[k].de_arr || !sd_gendisks[k].flags || + !sd_gendisks[k].driverfs_dev_arr) goto cleanup_gendisks; zero_mem_lth(sd_gendisks[k].de_arr, N); zero_mem_lth(sd_gendisks[k].flags, N); + zero_mem_lth(sd_gendisks[k].driverfs_dev_arr, N); sd_gendisks[k].major = SD_MAJOR(k); sd_gendisks[k].major_name = "sd"; @@ -1290,7 +1294,6 @@ static int sd_init() sd_gendisks[k].sizes = sd_sizes + k * (N << 4); sd_gendisks[k].nr_real = 0; } - return 0; #undef init_mem_lth @@ -1301,6 +1304,7 @@ cleanup_gendisks: for (k = 0; k < N_USED_SD_MAJORS; k++) { vfree(sd_gendisks[k].de_arr); vfree(sd_gendisks[k].flags); + vfree(sd_gendisks[k].driverfs_dev_arr); } cleanup_mem: vfree(sd_gendisks); @@ -1435,6 +1439,8 @@ static int sd_attach(Scsi_Device * sdp) SD_GENDISK(dsk_nr).nr_real++; devnum = dsk_nr % SCSI_DISKS_PER_MAJOR; SD_GENDISK(dsk_nr).de_arr[devnum] = sdp->de; + SD_GENDISK(dsk_nr).driverfs_dev_arr[devnum] = + &sdp->sdev_driverfs_dev; if (sdp->removable) SD_GENDISK(dsk_nr).flags[devnum] |= GENHD_FL_REMOVABLE; sd_dskname(dsk_nr, diskname); @@ -1534,6 +1540,8 @@ static void sd_detach(Scsi_Device * sdp) max_p = 1 << sd_gendisk.minor_shift; start = dsk_nr << sd_gendisk.minor_shift; dev = MKDEV_SD_PARTITION(start); + driverfs_remove_partitions(&SD_GENDISK (dsk_nr), + SD_MINOR_NUMBER (start)); wipe_partitions(dev); for (j = max_p - 1; j >= 0; j--) sd_sizes[start + j] = 0; @@ -1555,9 +1563,16 @@ static void sd_detach(Scsi_Device * sdp) **/ static int __init init_sd(void) { + int rc; SCSI_LOG_HLQUEUE(3, printk("init_sd: sd driver entry point\n")); sd_template.module = THIS_MODULE; - return scsi_register_device(&sd_template); + rc = scsi_register_device(&sd_template); + if (!rc) { + sd_template.scsi_driverfs_driver.name = (char *)sd_template.tag; + sd_template.scsi_driverfs_driver.bus = &scsi_driverfs_bus_type; + driver_register(&sd_template.scsi_driverfs_driver); + } + return rc; } /** @@ -1590,6 +1605,7 @@ static void __exit exit_sd(void) sd_template.dev_max = 0; if (sd_gendisks != &sd_gendisk) vfree(sd_gendisks); + remove_driver(&sd_template.scsi_driverfs_driver); } static Scsi_Disk * sd_get_sdisk(int index) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 9b5e51229c09..595398dd1749 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -194,6 +194,7 @@ typedef struct sg_device /* holds the state of each scsi generic device */ volatile char detached; /* 0->attached, 1->detached pending removal */ volatile char exclude; /* opened for exclusive access */ char sgdebug; /* 0->off, 1->sense, 9->dump dev, 10-> all devs */ + struct device sg_driverfs_dev; } Sg_device; /* 36 bytes long on i386 */ @@ -1370,6 +1371,29 @@ static int __init sg_def_reserved_size_setup(char *str) __setup("sg_def_reserved_size=", sg_def_reserved_size_setup); #endif +/* Driverfs file support */ +static ssize_t sg_device_kdev_read(struct device *driverfs_dev, char *page, + size_t count, loff_t off) +{ + Sg_device * sdp=list_entry(driverfs_dev, Sg_device, sg_driverfs_dev); + return off ? 0 : sprintf(page, "%x\n",sdp->i_rdev.value); +} +static struct driver_file_entry sg_device_kdev_file = { + name: "kdev", + mode: S_IRUGO, + show: sg_device_kdev_read, +}; + +static ssize_t sg_device_type_read(struct device *driverfs_dev, char *page, + size_t count, loff_t off) +{ + return off ? 0 : sprintf (page, "CHR\n"); +} +static struct driver_file_entry sg_device_type_file = { + name: "type", + mode: S_IRUGO, + show: sg_device_type_read, +}; static int sg_attach(Scsi_Device * scsidp) { @@ -1428,6 +1452,18 @@ static int sg_attach(Scsi_Device * scsidp) sdp->detached = 0; sdp->sg_tablesize = scsidp->host ? scsidp->host->sg_tablesize : 0; sdp->i_rdev = mk_kdev(SCSI_GENERIC_MAJOR, k); + + memset(&sdp->sg_driverfs_dev, 0, sizeof(struct device)); + sprintf(sdp->sg_driverfs_dev.bus_id, "%s:gen", + scsidp->sdev_driverfs_dev.bus_id); + sprintf(sdp->sg_driverfs_dev.name, "%sgeneric", + scsidp->sdev_driverfs_dev.name); + sdp->sg_driverfs_dev.parent = &scsidp->sdev_driverfs_dev; + sdp->sg_driverfs_dev.bus = &scsi_driverfs_bus_type; + device_register(&sdp->sg_driverfs_dev); + device_create_file(&sdp->sg_driverfs_dev, &sg_device_type_file); + device_create_file(&sdp->sg_driverfs_dev, &sg_device_kdev_file); + sdp->de = devfs_register (scsidp->de, "generic", DEVFS_FL_DEFAULT, SCSI_GENERIC_MAJOR, k, S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, @@ -1496,6 +1532,9 @@ static void sg_detach(Scsi_Device * scsidp) } SCSI_LOG_TIMEOUT(3, printk("sg_detach: dev=%d, dirty\n", k)); devfs_unregister (sdp->de); + device_remove_file(&sdp->sg_driverfs_dev,sg_device_type_file.name); + device_remove_file(&sdp->sg_driverfs_dev,sg_device_kdev_file.name); + put_device(&sdp->sg_driverfs_dev); sdp->de = NULL; if (NULL == sdp->headfp) { kfree((char *)sdp); @@ -1505,6 +1544,7 @@ static void sg_detach(Scsi_Device * scsidp) else { /* nothing active, simple case */ SCSI_LOG_TIMEOUT(3, printk("sg_detach: dev=%d\n", k)); devfs_unregister (sdp->de); + put_device(&sdp->sg_driverfs_dev); kfree((char *)sdp); sg_dev_arr[k] = NULL; } @@ -1529,9 +1569,16 @@ MODULE_PARM(def_reserved_size, "i"); MODULE_PARM_DESC(def_reserved_size, "size of buffer reserved for each fd"); static int __init init_sg(void) { + int rc; if (def_reserved_size >= 0) sg_big_buff = def_reserved_size; - return scsi_register_device(&sg_template); + rc = scsi_register_device(&sg_template); + if (!rc) { + sg_template.scsi_driverfs_driver.name = (char *)sg_template.tag; + sg_template.scsi_driverfs_driver.bus = &scsi_driverfs_bus_type; + driver_register(&sg_template.scsi_driverfs_driver); + } + return rc; } static void __exit exit_sg( void) @@ -1546,6 +1593,7 @@ static void __exit exit_sg( void) sg_dev_arr = NULL; } sg_template.dev_max = 0; + remove_driver(&sg_template.scsi_driverfs_driver); } diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index d536f3bc94f6..f4ec0f98516b 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -730,6 +730,32 @@ cleanup_devfs: return 1; } +/* Driverfs file support */ +static ssize_t sr_device_kdev_read(struct device *driverfs_dev, + char *page, size_t count, loff_t off) +{ + kdev_t kdev; + kdev.value=(int)driverfs_dev->driver_data; + return off ? 0 : sprintf(page, "%x\n",kdev.value); +} +static struct driver_file_entry sr_device_kdev_file = { + name: "kdev", + mode: S_IRUGO, + show: sr_device_kdev_read, +}; + +static ssize_t sr_device_type_read(struct device *driverfs_dev, + char *page, size_t count, loff_t off) +{ + return off ? 0 : sprintf (page, "CHR\n"); +} +static struct driver_file_entry sr_device_type_file = { + name: "type", + mode: S_IRUGO, + show: sr_device_type_read, +}; + + void sr_finish() { int i; @@ -775,6 +801,20 @@ void sr_finish() sprintf(name, "sr%d", i); strcpy(SCp->cdi.name, name); + sprintf(SCp->cdi.cdrom_driverfs_dev.bus_id, "%s:cd", + SCp->device->sdev_driverfs_dev.bus_id); + sprintf(SCp->cdi.cdrom_driverfs_dev.name, "%scdrom", + SCp->device->sdev_driverfs_dev.name); + SCp->cdi.cdrom_driverfs_dev.parent = + &SCp->device->sdev_driverfs_dev; + SCp->cdi.cdrom_driverfs_dev.bus = &scsi_driverfs_bus_type; + SCp->cdi.cdrom_driverfs_dev.driver_data = + (void *)__mkdev(MAJOR_NR, i); + device_register(&SCp->cdi.cdrom_driverfs_dev); + device_create_file(&SCp->cdi.cdrom_driverfs_dev, + &sr_device_type_file); + device_create_file(&SCp->cdi.cdrom_driverfs_dev, + &sr_device_kdev_file); SCp->cdi.de = devfs_register(SCp->device->de, "cd", DEVFS_FL_DEFAULT, MAJOR_NR, i, S_IFBLK | S_IRUGO | S_IWUGO, @@ -815,7 +855,14 @@ static void sr_detach(Scsi_Device * SDp) static int __init init_sr(void) { - return scsi_register_device(&sr_template); + int rc; + rc = scsi_register_device(&sr_template); + if (!rc) { + sr_template.scsi_driverfs_driver.name = (char *)sr_template.tag; + sr_template.scsi_driverfs_driver.bus = &scsi_driverfs_bus_type; + driver_register(&sr_template.scsi_driverfs_driver); + } + return rc; } static void __exit exit_sr(void) @@ -832,6 +879,7 @@ static void __exit exit_sr(void) blk_clear(MAJOR_NR); sr_template.dev_max = 0; + remove_driver(&sr_template.scsi_driverfs_driver); } module_init(init_sr); diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index f48ac845bc08..4c387b729ea2 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c @@ -3677,6 +3677,31 @@ __setup("st=", st_setup); #endif +/* Driverfs file support */ +static ssize_t st_device_kdev_read(struct device *driverfs_dev, + char *page, size_t count, loff_t off) +{ + kdev_t kdev; + kdev.value=(int)driverfs_dev->driver_data; + return off ? 0 : sprintf(page, "%x\n",kdev.value); +} +static struct driver_file_entry st_device_kdev_file = { + name: "kdev", + mode: S_IRUGO, + show: st_device_kdev_read, +}; + +static ssize_t st_device_type_read(struct device *driverfs_dev, + char *page, size_t count, loff_t off) +{ + return off ? 0 : sprintf (page, "CHR\n"); +} +static struct driver_file_entry st_device_type_file = { + name: "type", + mode: S_IRUGO, + show: st_device_type_read, +}; + static struct file_operations st_fops = { @@ -3779,6 +3804,18 @@ static int st_attach(Scsi_Device * SDp) /* Rewind entry */ sprintf (name, "mt%s", formats[mode]); + sprintf(tpnt->driverfs_dev_r[mode].bus_id, "%s:%s", + SDp->sdev_driverfs_dev.name, name); + sprintf(tpnt->driverfs_dev_r[mode].name, "%s%s", + SDp->sdev_driverfs_dev.name, name); + tpnt->driverfs_dev_r[mode].parent = &SDp->sdev_driverfs_dev; + tpnt->driverfs_dev_r[mode].bus = &scsi_driverfs_bus_type; + tpnt->driverfs_dev_r[mode].driver_data = + (void *)__mkdev(MAJOR_NR, i + (mode << 5)); + device_register(&tpnt->driverfs_dev_r[mode]); + device_create_file(&tpnt->driverfs_dev_r[mode], + &st_device_type_file); + device_create_file(&tpnt->driverfs_dev_r[mode], &st_device_kdev_file); tpnt->de_r[mode] = devfs_register (SDp->de, name, DEVFS_FL_DEFAULT, MAJOR_NR, i + (mode << 5), @@ -3786,6 +3823,19 @@ static int st_attach(Scsi_Device * SDp) &st_fops, NULL); /* No-rewind entry */ sprintf (name, "mt%sn", formats[mode]); + sprintf(tpnt->driverfs_dev_n[mode].bus_id, "%s:%s", + SDp->sdev_driverfs_dev.name, name); + sprintf(tpnt->driverfs_dev_n[mode].name, "%s%s", + SDp->sdev_driverfs_dev.name, name); + tpnt->driverfs_dev_n[mode].parent= &SDp->sdev_driverfs_dev; + tpnt->driverfs_dev_n[mode].bus = &scsi_driverfs_bus_type; + tpnt->driverfs_dev_n[mode].driver_data = + (void *)__mkdev(MAJOR_NR, i + (mode << 5) + 128); + device_register(&tpnt->driverfs_dev_n[mode]); + device_create_file(&tpnt->driverfs_dev_n[mode], + &st_device_type_file); + device_create_file(&tpnt->driverfs_dev_n[mode], + &st_device_kdev_file); tpnt->de_n[mode] = devfs_register (SDp->de, name, DEVFS_FL_DEFAULT, MAJOR_NR, i + (mode << 5) + 128, @@ -3894,8 +3944,18 @@ static void st_detach(Scsi_Device * SDp) for (mode = 0; mode < ST_NBR_MODES; ++mode) { devfs_unregister (tpnt->de_r[mode]); tpnt->de_r[mode] = NULL; + device_remove_file(&tpnt->driverfs_dev_r[mode], + st_device_type_file.name); + device_remove_file(&tpnt->driverfs_dev_r[mode], + st_device_kdev_file.name); + put_device(&tpnt->driverfs_dev_r[mode]); devfs_unregister (tpnt->de_n[mode]); tpnt->de_n[mode] = NULL; + device_remove_file(&tpnt->driverfs_dev_n[mode], + st_device_type_file.name); + device_remove_file(&tpnt->driverfs_dev_n[mode], + st_device_kdev_file.name); + put_device(&tpnt->driverfs_dev_n[mode]); } kfree(tpnt); scsi_tapes[i] = 0; @@ -3921,8 +3981,16 @@ static int __init init_st(void) verstr, st_buffer_size, st_write_threshold, st_max_buffers, st_max_sg_segs); - if (devfs_register_chrdev(SCSI_TAPE_MAJOR, "st", &st_fops) >= 0) - return scsi_register_device(&st_template); + if (devfs_register_chrdev(SCSI_TAPE_MAJOR, "st", &st_fops) >= 0) { + if (scsi_register_device(&st_template) == 0) { + st_template.scsi_driverfs_driver.name = + (char *)st_template.tag; + st_template.scsi_driverfs_driver.bus = + &scsi_driverfs_bus_type; + driver_register(&st_template.scsi_driverfs_driver); + return 0; + } + } printk(KERN_ERR "Unable to get major %d for SCSI tapes\n", MAJOR_NR); return 1; @@ -3951,6 +4019,7 @@ static void __exit exit_st(void) } } st_template.dev_max = 0; + remove_driver(&st_template.scsi_driverfs_driver); printk(KERN_INFO "st: Unloaded.\n"); } diff --git a/drivers/scsi/st.h b/drivers/scsi/st.h index 7fcd055b5981..b5fcd14b9cfb 100644 --- a/drivers/scsi/st.h +++ b/drivers/scsi/st.h @@ -94,6 +94,8 @@ typedef struct { int current_mode; devfs_handle_t de_r[ST_NBR_MODES]; /* Rewind entries */ devfs_handle_t de_n[ST_NBR_MODES]; /* No-rewind entries */ + struct device driverfs_dev_r[ST_NBR_MODES]; + struct device driverfs_dev_n[ST_NBR_MODES]; /* Status variables */ int partition; diff --git a/fs/partitions/check.c b/fs/partitions/check.c index 87d202b99b2c..80f163259afa 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -22,6 +22,7 @@ #include #include #include /* for invalidate_bdev() */ +#include #include "check.h" @@ -221,6 +222,136 @@ void add_gd_partition(struct gendisk *hd, int minor, int start, int size) #endif } +/* Driverfs file support */ +static ssize_t partition_device_kdev_read(struct device *driverfs_dev, + char *page, size_t count, loff_t off) +{ + kdev_t kdev; + kdev.value=(int)driverfs_dev->driver_data; + return off ? 0 : sprintf (page, "%x\n",kdev.value); +} +static struct driver_file_entry partition_device_kdev_file = { + name: "kdev", + mode: S_IRUGO, + show: partition_device_kdev_read, +}; + +static ssize_t partition_device_type_read(struct device *driverfs_dev, + char *page, size_t count, loff_t off) +{ + return off ? 0 : sprintf (page, "BLK\n"); +} +static struct driver_file_entry partition_device_type_file = { + name: "type", + mode: S_IRUGO, + show: partition_device_type_read, +}; + +void driverfs_create_partitions(struct gendisk *hd, int minor) +{ + int pos = -1; + int devnum = minor >> hd->minor_shift; + char dirname[256]; + struct device *parent = 0; + int max_p; + int part; + devfs_handle_t dir = 0; + + /* get parent driverfs device structure */ + if (hd->driverfs_dev_arr) + parent = hd->driverfs_dev_arr[devnum]; + else /* if driverfs not supported by subsystem, skip partitions */ + return; + + /* get parent device node directory name */ + if (hd->de_arr) { + dir = hd->de_arr[devnum]; + if (dir) + pos = devfs_generate_path (dir, dirname, + sizeof dirname); + } + + if (pos < 0) { + disk_name(hd, minor, dirname); + pos = 0; + } + + max_p = (1 << hd->minor_shift); + + /* for all partitions setup parents and device node names */ + for(part=0; part < max_p; part++) { + if ((part == 0) || (hd->part[minor + part].nr_sects >= 1)) { + struct device * current_driverfs_dev = + &hd->part[minor+part].hd_driverfs_dev; + current_driverfs_dev->parent = parent; + /* handle disc case */ + current_driverfs_dev->driver_data = + (void *)__mkdev(hd->major, minor+part); + if (part == 0) { + if (parent) { + sprintf(current_driverfs_dev->name, + "%sdisc", parent->name); + sprintf(current_driverfs_dev->bus_id, + "%s:disc", parent->bus_id); + } else { + sprintf(current_driverfs_dev->name, + "disc"); + sprintf(current_driverfs_dev->bus_id, + "disc"); + } + } else { /* this is a partition */ + if (parent) { + sprintf(current_driverfs_dev->name, + "%spart%d", parent->name, part); + sprintf(current_driverfs_dev->bus_id, + "%s:p%d", parent->bus_id, part); + } else { + sprintf(current_driverfs_dev->name, + "part%d", part); + sprintf(current_driverfs_dev->bus_id, + "p%d" ,part); + } + } + if (parent) current_driverfs_dev->bus = parent->bus; + device_register(current_driverfs_dev); + device_create_file(current_driverfs_dev, + &partition_device_type_file); + device_create_file(current_driverfs_dev, + &partition_device_kdev_file); + } + } + return; +} + +void driverfs_remove_partitions(struct gendisk *hd, int minor) +{ + int max_p; + int part; + struct device * current_driverfs_dev; + + max_p=(1 << hd->minor_shift); + + /* for all parts setup parent relationships and device node names */ + for(part=1; part < max_p; part++) { + if ((hd->part[minor + part].nr_sects >= 1)) { + current_driverfs_dev = + &hd->part[minor + part].hd_driverfs_dev; + device_remove_file(current_driverfs_dev, + partition_device_type_file.name); + device_remove_file(current_driverfs_dev, + partition_device_kdev_file.name); + put_device(current_driverfs_dev); + } + } + current_driverfs_dev = &hd->part[minor].hd_driverfs_dev; + device_remove_file(current_driverfs_dev, + partition_device_type_file.name); + device_remove_file(current_driverfs_dev, + partition_device_kdev_file.name); + put_device(current_driverfs_dev); + return; +} + static void check_partition(struct gendisk *hd, kdev_t dev, int first_part_minor) { devfs_handle_t de = NULL; @@ -281,6 +412,13 @@ setup_devfs: truncate_inode_pages(bdev->bd_inode->i_mapping, 0); bdput(bdev); i = first_part_minor - 1; + + /* Setup driverfs tree */ + if (hd->sizes) + driverfs_create_partitions(hd, i); + else + driverfs_remove_partitions(hd, i); + devfs_register_partitions (hd, i, hd->sizes ? 0 : 1); } diff --git a/include/linux/cdrom.h b/include/linux/cdrom.h index b3a349fc341d..296ffe2cdfd4 100644 --- a/include/linux/cdrom.h +++ b/include/linux/cdrom.h @@ -716,6 +716,7 @@ struct request_sense { #ifdef __KERNEL__ #include +#include struct cdrom_write_settings { unsigned char fpacket; /* fixed/variable packets */ @@ -730,6 +731,7 @@ struct cdrom_device_info { struct cdrom_device_info *next; /* next device_info for this major */ void *handle; /* driver-dependent data */ devfs_handle_t de; /* real driver should create this */ + struct device cdrom_driverfs_dev; /* driverfs implementation */ int number; /* generic driver updates this */ /* specifications */ kdev_t dev; /* device number */ diff --git a/include/linux/genhd.h b/include/linux/genhd.h index 18c981dafbf3..44a954b2c370 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -12,6 +12,7 @@ #include #include #include +#include enum { /* These three have identical behaviour; use the second one if DOS fdisk gets @@ -62,6 +63,7 @@ struct hd_struct { unsigned long nr_sects; devfs_handle_t de; /* primary (master) devfs entry */ int number; /* stupid old code wastes space */ + struct device hd_driverfs_dev; /* support driverfs hiearchy */ }; #define GENHD_FL_REMOVABLE 1 @@ -80,6 +82,7 @@ struct gendisk { struct block_device_operations *fops; devfs_handle_t *de_arr; /* one per physical disc */ + struct device **driverfs_dev_arr;/* support driverfs hierarchy */ char *flags; /* one per physical disc */ }; @@ -241,6 +244,7 @@ char *disk_name (struct gendisk *hd, int minor, char *buf); extern void devfs_register_partitions (struct gendisk *dev, int minor, int unregister); +extern void driverfs_remove_partitions (struct gendisk *hd, int minor); static inline unsigned int disk_index (kdev_t dev) { diff --git a/kernel/ksyms.c b/kernel/ksyms.c index 9391bb0e933d..fae36d5da7a9 100644 --- a/kernel/ksyms.c +++ b/kernel/ksyms.c @@ -335,6 +335,7 @@ EXPORT_SYMBOL(bdev_read_only); EXPORT_SYMBOL(set_device_ro); EXPORT_SYMBOL(bmap); EXPORT_SYMBOL(devfs_register_partitions); +EXPORT_SYMBOL(driverfs_remove_partitions); EXPORT_SYMBOL(blkdev_open); EXPORT_SYMBOL(blkdev_get); EXPORT_SYMBOL(blkdev_put); -- cgit v1.2.3 From abd988c41bb537a6ab20fbe421d0bd280972c0f4 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 2 Jul 2002 01:37:41 -0700 Subject: Make ramfs/driverfs maintain directory nlink counts. Make dcache filesystems export directory entry types to readdir. --- fs/driverfs/inode.c | 26 ++++++++++++++++++-------- fs/libfs.c | 8 +++++++- fs/ramfs/inode.c | 32 +++++++++++++++++++++++--------- include/linux/fs.h | 3 +++ 4 files changed, 51 insertions(+), 18 deletions(-) (limited to 'include/linux') diff --git a/fs/driverfs/inode.c b/fs/driverfs/inode.c index 73ad5c1f6adb..2fd9126199b2 100644 --- a/fs/driverfs/inode.c +++ b/fs/driverfs/inode.c @@ -149,6 +149,10 @@ static int driverfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) lock_kernel(); dentry->d_op = &driverfs_dentry_dir_ops; res = driverfs_mknod(dir, dentry, mode | S_IFDIR, 0); + if (!res) { + dir->i_nlink++; + dentry->d_inode->i_nlink++; + } unlock_kernel(); return res; } @@ -205,23 +209,29 @@ static int driverfs_empty(struct dentry *dentry) } static int driverfs_unlink(struct inode *dir, struct dentry *dentry) +{ + struct inode *inode = dentry->d_inode; + + lock_kernel(); + inode->i_nlink--; + unlock_kernel(); + dput(dentry); + return 0; +} + +static int driverfs_rmdir(struct inode *dir, struct dentry *dentry) { int error = -ENOTEMPTY; if (driverfs_empty(dentry)) { - struct inode *inode = dentry->d_inode; - - lock_kernel(); - inode->i_nlink--; - unlock_kernel(); - dput(dentry); + dentry->d_inode->i_nlink--; + driverfs_unlink(dir, dentry); + dir->i_nlink--; error = 0; } return error; } -#define driverfs_rmdir driverfs_unlink - /** * driverfs_read_file - "read" data from a file. * @file: file pointer diff --git a/fs/libfs.c b/fs/libfs.c index f4b12699b37a..be9a8f7963e8 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -83,6 +83,12 @@ loff_t dcache_dir_lseek(struct file *file, loff_t offset, int origin) return offset; } +/* Relationship between i_mode and the DT_xxx types */ +static inline unsigned char dt_type(struct inode *inode) +{ + return (inode->i_mode >> 12) & 15; +} + /* * Directory is locked and all positive dentries in it are safe, since * for ramfs-type trees they can't go away without unlink() or rmdir(), @@ -125,7 +131,7 @@ int dcache_readdir(struct file * filp, void * dirent, filldir_t filldir) continue; spin_unlock(&dcache_lock); - if (filldir(dirent, next->d_name.name, next->d_name.len, filp->f_pos, next->d_inode->i_ino, DT_UNKNOWN) < 0) + if (filldir(dirent, next->d_name.name, next->d_name.len, filp->f_pos, next->d_inode->i_ino, dt_type(next->d_inode)) < 0) return 0; spin_lock(&dcache_lock); /* next is still alive */ diff --git a/fs/ramfs/inode.c b/fs/ramfs/inode.c index d3edc3128ec0..7d41fc6389f2 100644 --- a/fs/ramfs/inode.c +++ b/fs/ramfs/inode.c @@ -130,7 +130,12 @@ static int ramfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int d static int ramfs_mkdir(struct inode * dir, struct dentry * dentry, int mode) { - return ramfs_mknod(dir, dentry, mode | S_IFDIR, 0); + int retval = ramfs_mknod(dir, dentry, mode | S_IFDIR, 0); + if (!retval) { + dir->i_nlink++; + dentry->d_inode->i_nlink++; + } + return retval; } static int ramfs_create(struct inode *dir, struct dentry *dentry, int mode) @@ -186,25 +191,30 @@ static int ramfs_empty(struct dentry *dentry) } /* - * This works for both directories and regular files. - * (non-directories will always have empty subdirs) + * Unlink a ramfs entry */ static int ramfs_unlink(struct inode * dir, struct dentry *dentry) +{ + struct inode *inode = dentry->d_inode; + + inode->i_nlink--; + dput(dentry); /* Undo the count from "create" - this does all the work */ + return 0; +} + +static int ramfs_rmdir(struct inode * dir, struct dentry *dentry) { int retval = -ENOTEMPTY; if (ramfs_empty(dentry)) { - struct inode *inode = dentry->d_inode; - - inode->i_nlink--; - dput(dentry); /* Undo the count from "create" - this does all the work */ + dentry->d_inode->i_nlink--; + ramfs_unlink(dir, dentry); + dir->i_nlink--; retval = 0; } return retval; } -#define ramfs_rmdir ramfs_unlink - /* * The VFS layer already does all the dentry stuff for rename, * we just have to decrement the usage count for the target if @@ -221,6 +231,10 @@ static int ramfs_rename(struct inode * old_dir, struct dentry *old_dentry, struc inode->i_nlink--; dput(new_dentry); } + if (S_ISDIR(old_dentry->d_inode->i_mode)) { + old_dir->i_nlink--; + new_dir->i_nlink++; + } error = 0; } return error; diff --git a/include/linux/fs.h b/include/linux/fs.h index 322e644060cf..002e02289155 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -701,6 +701,9 @@ extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct de /* * File types + * + * NOTE! These match bits 12..15 of stat.st_mode + * (ie "(i_mode >> 12) & 15"). */ #define DT_UNKNOWN 0 #define DT_FIFO 1 -- cgit v1.2.3