diff options
-rw-r--r-- | share/man/man4/cd.4 | 37 | ||||
-rw-r--r-- | share/man/man4/da.4 | 96 | ||||
-rw-r--r-- | share/man/man9/cd.9 | 18 | ||||
-rw-r--r-- | share/man/man9/taskqueue.9 | 37 | ||||
-rw-r--r-- | sys/cam/scsi/scsi_cd.c | 67 | ||||
-rw-r--r-- | sys/cam/scsi/scsi_da.c | 69 | ||||
-rw-r--r-- | sys/kern/subr_taskqueue.c | 32 | ||||
-rw-r--r-- | sys/sys/taskqueue.h | 11 |
8 files changed, 266 insertions, 101 deletions
diff --git a/share/man/man4/cd.4 b/share/man/man4/cd.4 index 579fcea..7c491f5 100644 --- a/share/man/man4/cd.4 +++ b/share/man/man4/cd.4 @@ -25,7 +25,7 @@ .\" .\" $FreeBSD$ .\" -.Dd October 10, 1998 +.Dd September 2, 2003 .Dt CD 4 .Os .Sh NAME @@ -370,6 +370,37 @@ Some work is planned to support some of the more common `broken' .Tn CD-ROM drives; however, this is not yet under way. +.Pp +The +.Nm +driver attempts to automatically determine whether the drive it is talking +to supports 6 byte or 10 byte MODE SENSE/MODE SELECT operations. Many +.Tn SCSI +drives only support 6 byte commands, and +.Tn ATAPI +drives only support 10 byte commands. +The +.Nm +driver first attempts to determine whether the protocol in use typically +supports 6 byte commands by issuing a CAM Path Inquiry CCB. +It will then default to 6 byte or 10 byte commands as appropriate. +After that, the +.Nm +driver defaults to using 6 byte commands (assuming the protocol the drive +speaks claims to support 6 byte commands), until one fails with a +.Tn SCSI +ILLEGAL REQUEST error. Then it tries the 10 byte version of the command to +see if that works instead. Users can change the default via per-drive +sysctl variables and loader tunables. The variable names are the same in +both instances: +.Pp +.Va kern.cam.cd.%d.minimum_cmd_size +.Pp +Where +.Dq %d +is the unit number of the drive in question. Valid minimum command sizes +are 6 and 10. Any value above 6 will be rounded to 10, and any value below +6 will be rounded to 6. .Sh CHANGER OPERATION This driver has built-in support for LUN-based CD changers. A LUN-based CD @@ -399,7 +430,7 @@ If there is no outstanding I/O for another LUN, the driver will allow indefinite access to a given LUN. .Pp The minimum and maximum time quanta are configurable via kernel options and -also via sysctl variables. +also via sysctl and kernel tunable variables. The kernel options are: .Pp .Bl -item -compact @@ -409,7 +440,7 @@ The kernel options are: .Cd "options ""CHANGER_MAX_BUSY_SECONDS=11""" .El .Pp -The sysctl variables are: +The sysctl/kernel tunable variables are: .Pp .Bl -item -compact .It diff --git a/share/man/man4/da.4 b/share/man/man4/da.4 index 835f4fd..c35b59f 100644 --- a/share/man/man4/da.4 +++ b/share/man/man4/da.4 @@ -25,7 +25,7 @@ .\" .\" $FreeBSD$ .\" -.Dd October 15, 1998 +.Dd September 2, 2003 .Dt DA 4 .Os .Sh NAME @@ -196,6 +196,59 @@ The driver .Em will write the new disklabel to the disk. .El +.Sh SYSCTL VARIABLES +The following variables are available as both +.Xr sysctl 8 +variables and +.Xr loader 8 +tunables: +.Bl -tag -width 12 +.It kern.cam.da.retry_count +.Pp +This variable determines how many times the +.Nm +driver will retry a READ or WRITE command. +This does not affect the number of retries used during probe time or for +the +.Nm +driver dump routine. +This value currently defaults to 4. +.It kern.cam.da.default_timeout +.Pp +This variable determines how long the +.Nm +driver will wait before timing out an outstanding command. +The units for this value are seconds, and the default is currently 60 +seconds. +.It kern.cam.da.%d.minimum_cmd_size +.Pp +This variable determines what the minimum READ/WRITE CDB size is for a +given +.Nm +unit. +(The %d above denotes the unit number of the +.Nm +driver instance, e.g. 1, 2, 4, 8, etc.) +Valid minimum command size values are 6, 10, 12 and 16 bytes. +The default is 6 bytes. +.Pp +The +.Nm +driver issues a CAM Path Inquiry CCB at probe time to determine whether the +protocol the device in question speaks (e.g. ATAPI) typically doesn't allow +6 byte commands. +If it doesn't, the +.Nm +driver will default to using at least 10 byte CDBs. +If a 6 byte READ or WRITE fails with an ILLEGAL REQUEST error, the +.Nm +driver will then increase the default CDB size for the device to 10 bytes and +retry the command. +CDB size is always +chosen as the smallest READ/WRITE CDB that will satisfy the specified minimum +command size, and the LBA and length of the READ or WRITE in question. +(e.g., a write to an LBA larger than 2^32 will require a 16 byte CDB.) +.El .Sh NOTES If a device becomes invalidated (media is removed, device becomes unresponsive) the disklabel and information held within the kernel about the device will @@ -206,25 +259,9 @@ the last file descriptor referencing the old device is closed. During this period, all new open attempts will be rejected. .Sh FILES .Bl -tag -width /dev/rsdXXXXX -compact -.It Pa /dev/rda Ns Ar u -raw mode -.Tn SCSI -disk unit -.Ar u , -accessed as an unpartitioned device .Sm off .It Pa /dev/da Ar u Pa s Ar n .Sm on -block mode -.Tn SCSI -disk unit -.Ar u , -slice -.Ar n , -accessed as an unpartitioned device -.Sm off -.It Pa /dev/rda Ar u Pa s Ar n -.Sm on raw mode .Tn SCSI disk unit @@ -233,15 +270,6 @@ slice .Ar n , accessed as an unpartitioned device .It Pa /dev/da Ns Ar u Ns Ar p -block mode -.Tn SCSI -disk unit -.Ar u , -first -.Fx -slice, partition -.Ar p -.It Pa /dev/rda Ns Ar u Ns Ar p raw mode .Tn SCSI disk unit @@ -259,22 +287,6 @@ slice, partition .Ar p .Xc .Sm on -block mode -.Tn SCSI -disk unit -.Ar u , -.Ar n Ns th -slice, partition -.Ar p -.Sm off -.It Xo -.Pa /dev/rda -.Ar u -.Pa s -.Ar n -.Ar p -.Xc -.Sm on raw mode .Tn SCSI disk unit diff --git a/share/man/man9/cd.9 b/share/man/man9/cd.9 index 036fc80..49c2ede 100644 --- a/share/man/man9/cd.9 +++ b/share/man/man9/cd.9 @@ -27,7 +27,7 @@ .\" .\" $FreeBSD$ .\" -.Dd October 10, 1998 +.Dd September 2, 2003 .Dt CD 9 .Os .Sh NAME @@ -74,6 +74,22 @@ In general, the driver will figure this out automatically when it sees a LUN greater than 0. Setting this flag only has the effect of telling the driver to run the initial read capacity command for LUN 0 of the changer through the changer scheduling code. +.It Dv CD_Q_10_BYTE_ONLY +This flag tells the driver that the given device only accepts 10 byte MODE +SENSE/MODE SELECT commands. In general these types of quirks should not be +added to the +.Xr cd 4 +driver. The reason is that the driver does several things to attempt to +determine whether the drive in question needs 10 byte commands. First, it +issues a CAM Path Inquiry command to determine whether the protocol that +the drive speaks typically only allows 10 byte commands. (ATAPI and USB +are two prominent examples of protocols where you generally only want to +send 10 byte commands.) Then, if it gets an ILLEGAL REQUEST error back +from a 6 byte MODE SENSE or MODE SELECT command, it attempts to send the 10 +byte version of the command instead. The only reason you would need a +quirk is if your drive uses a protocol (e.g. +.Tn SCSI ) +that typically doesn't have a problem with 6 byte commands. .El .Sh FILES .Bl -tag -width /sys/cam/scsi/scsi_cd.c -compact diff --git a/share/man/man9/taskqueue.9 b/share/man/man9/taskqueue.9 index 013f737..7d35458 100644 --- a/share/man/man9/taskqueue.9 +++ b/share/man/man9/taskqueue.9 @@ -174,26 +174,43 @@ argument to the macro is executed as a C statement, allowing any further initialisation to be performed (such as registering an interrupt handler etc.) .Pp -The system provides a global taskqueue, +The system provides three global taskqueues, .Va taskqueue_swi , -which is run via a software interrupt mechanism. -To use this queue, +.Va taskqueue_swi_giant , +and +.Va taskqueue_thread . +The swi taskqueues are run via a software interrupt mechanism. +The taskqueue_swi queue runs without the protection of the Giant kernel lock, +and the taskqueue_swi_giant queue runs with the protection of the Giant +kernel lock. +The thread taskqueue runs in a kernel thread context, and tasks run from +this thread do not run under the Giant kernel lock. +If the caller wants to run under Giant, he should explicitly acquire and +release Giant in his taskqueue handler routine. + +To use these queues, call .Fn taskqueue_enqueue -with the value of the global variable -.Va taskqueue_swi . -The queue will be run at -.\" XXX This should be a cross-reference (Xr), but there is no MANLINKS -.\" entry for splsofttq.9 yet. -.Fn splsofttq . +with the value of the global taskqueue variable for the queue you wish to +use ( +.Va taskqueue_swi , +.Va taskqueue_swi_giant , +or +.Va taskqueue_thread +). .Pp -This queue can be used, +This the software interrupt queues can be used, for instance, for implementing interrupt handlers which must perform a significant amount of processing in the handler. The hardware interrupt handler would perform minimal processing of the interrupt and then enqueue a task to finish the work. This reduces to a minimum the amount of time spent with interrupts disabled. +.Pp +The thread queue can be used, for instance, by interrupt level routines +that need to call kernel functions that do things that can only be done +from a thread context. +(e.g., call malloc with the M_WAITOK flag.) .Sh HISTORY This interface first appeared in .Fx 5.0 . diff --git a/sys/cam/scsi/scsi_cd.c b/sys/cam/scsi/scsi_cd.c index a887243..39c2562 100644 --- a/sys/cam/scsi/scsi_cd.c +++ b/sys/cam/scsi/scsi_cd.c @@ -62,6 +62,7 @@ __FBSDID("$FreeBSD$"); #include <sys/dvdio.h> #include <sys/devicestat.h> #include <sys/sysctl.h> +#include <sys/taskqueue.h> #include <cam/cam.h> #include <cam/cam_ccb.h> @@ -154,6 +155,7 @@ struct cd_softc { eventhandler_tag clonetag; int minimum_command_size; int outstanding_cmds; + struct task sysctl_task; struct sysctl_ctx_list sysctl_ctx; struct sysctl_oid *sysctl_tree; STAILQ_HEAD(, cd_mode_params) mode_queue; @@ -598,6 +600,43 @@ cdasync(void *callback_arg, u_int32_t code, } } +static void +cdsysctlinit(void *context, int pending) +{ + struct cam_periph *periph; + struct cd_softc *softc; + char tmpstr[80], tmpstr2[80]; + + periph = (struct cam_periph *)context; + softc = (struct cd_softc *)periph->softc; + + snprintf(tmpstr, sizeof(tmpstr), "CAM CD unit %d", periph->unit_number); + snprintf(tmpstr2, sizeof(tmpstr2), "%d", periph->unit_number); + + mtx_lock(&Giant); + + sysctl_ctx_init(&softc->sysctl_ctx); + softc->sysctl_tree = SYSCTL_ADD_NODE(&softc->sysctl_ctx, + SYSCTL_STATIC_CHILDREN(_kern_cam_cd), OID_AUTO, + tmpstr2, CTLFLAG_RD, 0, tmpstr); + + if (softc->sysctl_tree == NULL) { + printf("cdsysctlinit: unable to allocate sysctl tree\n"); + return; + } + + /* + * Now register the sysctl handler, so the user can the value on + * the fly. + */ + SYSCTL_ADD_PROC(&softc->sysctl_ctx,SYSCTL_CHILDREN(softc->sysctl_tree), + OID_AUTO, "minimum_cmd_size", CTLTYPE_INT | CTLFLAG_RW, + &softc->minimum_command_size, 0, cdcmdsizesysctl, "I", + "Minimum CDB size"); + + mtx_unlock(&Giant); +} + /* * We have a handler function for this so we can check the values when the * user sets them, instead of every time we look at them. @@ -642,7 +681,7 @@ cdregister(struct cam_periph *periph, void *arg) struct ccb_setasync csa; struct ccb_pathinq cpi; struct ccb_getdev *cgd; - char tmpstr[80], tmpstr2[80]; + char tmpstr[80]; caddr_t match; cgd = (struct ccb_getdev *)arg; @@ -696,17 +735,7 @@ cdregister(struct cam_periph *periph, void *arg) if (cpi.ccb_h.status == CAM_REQ_CMP && (cpi.hba_misc & PIM_NO_6_BYTE)) softc->quirks |= CD_Q_10_BYTE_ONLY; - snprintf(tmpstr, sizeof(tmpstr), "CAM CD unit %d", periph->unit_number); - snprintf(tmpstr2, sizeof(tmpstr2), "%d", periph->unit_number); - sysctl_ctx_init(&softc->sysctl_ctx); - softc->sysctl_tree = SYSCTL_ADD_NODE(&softc->sysctl_ctx, - SYSCTL_STATIC_CHILDREN(_kern_cam_cd), OID_AUTO, - tmpstr2, CTLFLAG_RD, 0, tmpstr); - if (softc->sysctl_tree == NULL) { - printf("cdregister: unable to allocate sysctl tree\n"); - free(softc, M_DEVBUF); - return (CAM_REQ_CMP_ERR); - } + TASK_INIT(&softc->sysctl_task, 0, cdsysctlinit, periph); /* The default is 6 byte commands, unless quirked otherwise */ if (softc->quirks & CD_Q_10_BYTE_ONLY) @@ -728,15 +757,6 @@ cdregister(struct cam_periph *periph, void *arg) softc->minimum_command_size = 10; /* - * Now register the sysctl handler, so the user can the value on - * the fly. - */ - SYSCTL_ADD_PROC(&softc->sysctl_ctx,SYSCTL_CHILDREN(softc->sysctl_tree), - OID_AUTO, "minimum_cmd_size", CTLTYPE_INT | CTLFLAG_RW, - &softc->minimum_command_size, 0, cdcmdsizesysctl, "I", - "Minimum CDB size"); - - /* * We need to register the statistics structure for this device, * but we don't have the blocksize yet for it. So, we register * the structure and indicate that we don't have the blocksize @@ -1847,6 +1867,11 @@ cddone(struct cam_periph *periph, union ccb *done_ccb) xpt_announce_periph(periph, announce_buf); if (softc->flags & CD_FLAG_CHANGER) cdchangerschedule(softc); + /* + * Create our sysctl variables, now that we know + * we have successfully attached. + */ + taskqueue_enqueue(taskqueue_thread,&softc->sysctl_task); } softc->state = CD_STATE_NORMAL; /* diff --git a/sys/cam/scsi/scsi_da.c b/sys/cam/scsi/scsi_da.c index e2edbcf..ce2bfff 100644 --- a/sys/cam/scsi/scsi_da.c +++ b/sys/cam/scsi/scsi_da.c @@ -48,6 +48,7 @@ __FBSDID("$FreeBSD$"); #include <sys/eventhandler.h> #include <sys/malloc.h> #include <sys/cons.h> +#include <sys/taskqueue.h> #include <machine/md_var.h> @@ -133,6 +134,7 @@ struct da_softc { struct disk_params params; struct disk disk; union ccb saved_ccb; + struct task sysctl_task; struct sysctl_ctx_list sysctl_ctx; struct sysctl_oid *sysctl_tree; }; @@ -388,6 +390,7 @@ static dumper_t dadump; static periph_init_t dainit; static void daasync(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg); +static void dasysctlinit(void *context, int pending); static int dacmdsizesysctl(SYSCTL_HANDLER_ARGS); static periph_ctor_t daregister; static periph_dtor_t dacleanup; @@ -915,6 +918,41 @@ daasync(void *callback_arg, u_int32_t code, } } +static void +dasysctlinit(void *context, int pending) +{ + struct cam_periph *periph; + struct da_softc *softc; + char tmpstr[80], tmpstr2[80]; + + periph = (struct cam_periph *)context; + softc = (struct da_softc *)periph->softc; + + snprintf(tmpstr, sizeof(tmpstr), "CAM DA unit %d", periph->unit_number); + snprintf(tmpstr2, sizeof(tmpstr2), "%d", periph->unit_number); + + mtx_lock(&Giant); + sysctl_ctx_init(&softc->sysctl_ctx); + softc->sysctl_tree = SYSCTL_ADD_NODE(&softc->sysctl_ctx, + SYSCTL_STATIC_CHILDREN(_kern_cam_da), OID_AUTO, tmpstr2, + CTLFLAG_RD, 0, tmpstr); + if (softc->sysctl_tree == NULL) { + printf("dasysctlinit: unable to allocate sysctl tree\n"); + return; + } + + /* + * Now register the sysctl handler, so the user can the value on + * the fly. + */ + SYSCTL_ADD_PROC(&softc->sysctl_ctx,SYSCTL_CHILDREN(softc->sysctl_tree), + OID_AUTO, "minimum_cmd_size", CTLTYPE_INT | CTLFLAG_RW, + &softc->minimum_cmd_size, 0, dacmdsizesysctl, "I", + "Minimum CDB size"); + + mtx_unlock(&Giant); +} + static int dacmdsizesysctl(SYSCTL_HANDLER_ARGS) { @@ -955,7 +993,7 @@ daregister(struct cam_periph *periph, void *arg) struct ccb_setasync csa; struct ccb_pathinq cpi; struct ccb_getdev *cgd; - char tmpstr[80], tmpstr2[80]; + char tmpstr[80]; caddr_t match; cgd = (struct ccb_getdev *)arg; @@ -1008,17 +1046,7 @@ daregister(struct cam_periph *periph, void *arg) if (cpi.ccb_h.status == CAM_REQ_CMP && (cpi.hba_misc & PIM_NO_6_BYTE)) softc->quirks |= DA_Q_NO_6_BYTE; - snprintf(tmpstr, sizeof(tmpstr), "CAM DA unit %d", periph->unit_number); - snprintf(tmpstr2, sizeof(tmpstr2), "%d", periph->unit_number); - sysctl_ctx_init(&softc->sysctl_ctx); - softc->sysctl_tree = SYSCTL_ADD_NODE(&softc->sysctl_ctx, - SYSCTL_STATIC_CHILDREN(_kern_cam_da), OID_AUTO, tmpstr2, - CTLFLAG_RD, 0, tmpstr); - if (softc->sysctl_tree == NULL) { - printf("daregister: unable to allocate sysctl tree\n"); - free(softc, M_DEVBUF); - return (CAM_REQ_CMP_ERR); - } + TASK_INIT(&softc->sysctl_task, 0, dasysctlinit, periph); /* * RBC devices don't have to support READ(6), only READ(10). @@ -1050,15 +1078,6 @@ daregister(struct cam_periph *periph, void *arg) softc->minimum_cmd_size = 16; /* - * Now register the sysctl handler, so the user can the value on - * the fly. - */ - SYSCTL_ADD_PROC(&softc->sysctl_ctx,SYSCTL_CHILDREN(softc->sysctl_tree), - OID_AUTO, "minimum_cmd_size", CTLTYPE_INT | CTLFLAG_RW, - &softc->minimum_cmd_size, 0, dacmdsizesysctl, "I", - "Minimum CDB size"); - - /* * Block our timeout handler while we * add this softc to the dev list. */ @@ -1539,8 +1558,14 @@ dadone(struct cam_periph *periph, union ccb *done_ccb) } } free(csio->data_ptr, M_TEMP); - if (announce_buf[0] != '\0') + if (announce_buf[0] != '\0') { xpt_announce_periph(periph, announce_buf); + /* + * Create our sysctl variables, now that we know + * we have successfully attached. + */ + taskqueue_enqueue(taskqueue_thread,&softc->sysctl_task); + } softc->state = DA_STATE_NORMAL; /* * Since our peripheral may be invalidated by an error diff --git a/sys/kern/subr_taskqueue.c b/sys/kern/subr_taskqueue.c index fa32be6..bb1f1b6 100644 --- a/sys/kern/subr_taskqueue.c +++ b/sys/kern/subr_taskqueue.c @@ -36,6 +36,8 @@ __FBSDID("$FreeBSD$"); #include <sys/malloc.h> #include <sys/mutex.h> #include <sys/taskqueue.h> +#include <sys/kthread.h> +#include <sys/unistd.h> static MALLOC_DEFINE(M_TASKQUEUE, "taskqueue", "Task Queues"); @@ -44,6 +46,7 @@ static STAILQ_HEAD(taskqueue_list, taskqueue) taskqueue_queues; static void *taskqueue_ih; static void *taskqueue_giant_ih; static struct mtx taskqueue_queues_mutex; +static struct proc *taskqueue_thread_proc; struct taskqueue { STAILQ_ENTRY(taskqueue) tq_link; @@ -233,6 +236,31 @@ taskqueue_swi_giant_run(void *dummy) taskqueue_run(taskqueue_swi_giant); } +static void +taskqueue_kthread(void *arg) +{ + struct mtx kthread_mutex; + + bzero(&kthread_mutex, sizeof(kthread_mutex)); + + mtx_init(&kthread_mutex, "taskqueue kthread", NULL, MTX_DEF); + + mtx_lock(&kthread_mutex); + + for (;;) { + mtx_unlock(&kthread_mutex); + taskqueue_run(taskqueue_thread); + mtx_lock(&kthread_mutex); + msleep(&taskqueue_thread, &kthread_mutex, PWAIT, "tqthr", 0); + } +} + +static void +taskqueue_thread_enqueue(void *context) +{ + wakeup(&taskqueue_thread); +} + TASKQUEUE_DEFINE(swi, taskqueue_swi_enqueue, 0, swi_add(NULL, "task queue", taskqueue_swi_run, NULL, SWI_TQ, INTR_MPSAFE, &taskqueue_ih)); @@ -240,3 +268,7 @@ TASKQUEUE_DEFINE(swi, taskqueue_swi_enqueue, 0, TASKQUEUE_DEFINE(swi_giant, taskqueue_swi_giant_enqueue, 0, swi_add(NULL, "Giant task queue", taskqueue_swi_giant_run, NULL, SWI_TQ_GIANT, 0, &taskqueue_giant_ih)); + +TASKQUEUE_DEFINE(thread, taskqueue_thread_enqueue, 0, + kthread_create(taskqueue_kthread, NULL, + &taskqueue_thread_proc, RFNOWAIT, 0, "taskqueue")); diff --git a/sys/sys/taskqueue.h b/sys/sys/taskqueue.h index 953bc28..d0cac11 100644 --- a/sys/sys/taskqueue.h +++ b/sys/sys/taskqueue.h @@ -107,10 +107,17 @@ SYSINIT(taskqueue_##name, SI_SUB_CONFIGURE, SI_ORDER_SECOND, \ struct __hack /* - * This queue is serviced by a software interrupt handler. To enqueue - * a task, call taskqueue_enqueue(taskqueue_swi, &task). + * These queues are serviced by software interrupt handlers. To enqueue + * a task, call taskqueue_enqueue(taskqueue_swi, &task) or + * taskqueue_enqueue(taskqueue_swi_giant, &task). */ TASKQUEUE_DECLARE(swi_giant); TASKQUEUE_DECLARE(swi); +/* + * This queue is serviced by a kernel thread. To enqueue a task, call + * taskqueue_enqueue(taskqueue_thread, &task). + */ +TASKQUEUE_DECLARE(thread); + #endif /* !_SYS_TASKQUEUE_H_ */ |