diff options
author | Stephen Rothwell <sfr@canb.auug.org.au> | 2010-05-13 14:18:37 +1000 |
---|---|---|
committer | Stephen Rothwell <sfr@canb.auug.org.au> | 2010-05-13 14:18:37 +1000 |
commit | 544ad1b87c04b7e185734c6a34a986e6434c3adb (patch) | |
tree | df4a127eb12cc397b0c3bc4a43c08e6037d7aa0e | |
parent | f717fd2969a49f53d929796c04bced564f5d21c0 (diff) | |
parent | 23ca86e2f77dbec6c50fee8d082d7bdefaec5418 (diff) |
Merge branch 'quilt/rr'
Conflicts:
drivers/net/virtio_net.c
drivers/net/wireless/libertas_tf/if_usb.c
include/linux/mod_devicetable.h
scripts/mod/file2alias.c
43 files changed, 1230 insertions, 677 deletions
diff --git a/Documentation/cpu-hotplug.txt b/Documentation/cpu-hotplug.txt index a99d7031cdf9..45d5a217484f 100644 --- a/Documentation/cpu-hotplug.txt +++ b/Documentation/cpu-hotplug.txt @@ -2,7 +2,7 @@ Maintainers: CPU Hotplug Core: - Rusty Russell <rusty@rustycorp.com.au> + Rusty Russell <rusty@rustcorp.com.au> Srivatsa Vaddagiri <vatsa@in.ibm.com> i386: Zwane Mwaikambo <zwane@arm.linux.org.uk> diff --git a/arch/um/drivers/hostaudio_kern.c b/arch/um/drivers/hostaudio_kern.c index 368219cc2366..3ceede02f426 100644 --- a/arch/um/drivers/hostaudio_kern.c +++ b/arch/um/drivers/hostaudio_kern.c @@ -186,7 +186,9 @@ static int hostaudio_open(struct inode *inode, struct file *file) int ret; #ifdef DEBUG + kparam_block_sysfs_write(dsp); printk(KERN_DEBUG "hostaudio: open called (host: %s)\n", dsp); + kparam_unblock_sysfs_write(dsp); #endif state = kmalloc(sizeof(struct hostaudio_state), GFP_KERNEL); @@ -198,7 +200,9 @@ static int hostaudio_open(struct inode *inode, struct file *file) if (file->f_mode & FMODE_WRITE) w = 1; + kparam_block_sysfs_write(dsp); ret = os_open_file(dsp, of_set_rw(OPENFLAGS(), r, w), 0); + kparam_unblock_sysfs_write(dsp); if (ret < 0) { kfree(state); return ret; @@ -254,11 +258,15 @@ static int hostmixer_open_mixdev(struct inode *inode, struct file *file) if (file->f_mode & FMODE_WRITE) w = 1; + kparam_block_sysfs_write(mixer); ret = os_open_file(mixer, of_set_rw(OPENFLAGS(), r, w), 0); + kparam_unblock_sysfs_write(mixer); if (ret < 0) { + kparam_block_sysfs_write(dsp); printk(KERN_ERR "hostaudio_open_mixdev failed to open '%s', " "err = %d\n", dsp, -ret); + kparam_unblock_sysfs_write(dsp); kfree(state); return ret; } @@ -314,8 +322,10 @@ MODULE_LICENSE("GPL"); static int __init hostaudio_init_module(void) { + __kernel_param_lock(); printk(KERN_INFO "UML Audio Relay (host dsp = %s, host mixer = %s)\n", dsp, mixer); + __kernel_param_unlock(); module_data.dev_audio = register_sound_dsp(&hostaudio_fops, -1); if (module_data.dev_audio < 0) { diff --git a/drivers/acpi/debug.c b/drivers/acpi/debug.c index 146135e7a6a1..295dbfa2db9c 100644 --- a/drivers/acpi/debug.c +++ b/drivers/acpi/debug.c @@ -96,7 +96,8 @@ static const struct acpi_dlevel acpi_debug_levels[] = { /* -------------------------------------------------------------------------- FS Interface (/sys) -------------------------------------------------------------------------- */ -static int param_get_debug_layer(char *buffer, struct kernel_param *kp) { +static int param_get_debug_layer(char *buffer, const struct kernel_param *kp) +{ int result = 0; int i; @@ -118,7 +119,8 @@ static int param_get_debug_layer(char *buffer, struct kernel_param *kp) { return result; } -static int param_get_debug_level(char *buffer, struct kernel_param *kp) { +static int param_get_debug_level(char *buffer, const struct kernel_param *kp) +{ int result = 0; int i; @@ -137,8 +139,18 @@ static int param_get_debug_level(char *buffer, struct kernel_param *kp) { return result; } -module_param_call(debug_layer, param_set_uint, param_get_debug_layer, &acpi_dbg_layer, 0644); -module_param_call(debug_level, param_set_uint, param_get_debug_level, &acpi_dbg_level, 0644); +static struct kernel_param_ops acpi_debug_layer_ops = { + .set = param_set_uint, + .get = param_get_debug_layer, +}; + +static struct kernel_param_ops acpi_debug_level_ops = { + .set = param_set_uint, + .get = param_get_debug_level, +}; + +module_param_cb(debug_layer, &acpi_debug_layer_ops, &acpi_dbg_layer, 0644); +module_param_cb(debug_level, &acpi_debug_level_ops, &acpi_dbg_level, 0644); static char trace_method_name[6]; module_param_string(trace_method_name, trace_method_name, 6, 0644); @@ -147,7 +159,7 @@ module_param(trace_debug_layer, uint, 0644); static unsigned int trace_debug_level; module_param(trace_debug_level, uint, 0644); -static int param_set_trace_state(const char *val, struct kernel_param *kp) +static int param_set_trace_state(const char *val, const struct kernel_param *kp) { int result = 0; @@ -181,7 +193,7 @@ exit: return result; } -static int param_get_trace_state(char *buffer, struct kernel_param *kp) +static int param_get_trace_state(char *buffer, const struct kernel_param *kp) { if (!acpi_gbl_trace_method_name) return sprintf(buffer, "disable"); @@ -194,8 +206,12 @@ static int param_get_trace_state(char *buffer, struct kernel_param *kp) return 0; } -module_param_call(trace_state, param_set_trace_state, param_get_trace_state, - NULL, 0644); +static struct kernel_param_ops param_ops_trace_state = { + .set = param_set_trace_state, + .get = param_get_trace_state, +}; + +module_param_cb(trace_state, ¶m_ops_trace_state, NULL, 0644); /* -------------------------------------------------------------------------- DebugFS Interface diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index 2138a7ae050c..135615607e71 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c @@ -50,7 +50,7 @@ static void blk_done(struct virtqueue *vq) unsigned long flags; spin_lock_irqsave(&vblk->lock, flags); - while ((vbr = vblk->vq->vq_ops->get_buf(vblk->vq, &len)) != NULL) { + while ((vbr = virtqueue_get_buf(vblk->vq, &len)) != NULL) { int error; switch (vbr->status) { @@ -70,6 +70,8 @@ static void blk_done(struct virtqueue *vq) vbr->req->sense_len = vbr->in_hdr.sense_len; vbr->req->errors = vbr->in_hdr.errors; } + if (blk_special_request(vbr->req)) + vbr->req->errors = (error != 0); __blk_end_request_all(vbr->req, error); list_del(&vbr->list); @@ -103,6 +105,11 @@ static bool do_req(struct request_queue *q, struct virtio_blk *vblk, vbr->out_hdr.sector = 0; vbr->out_hdr.ioprio = req_get_ioprio(vbr->req); break; + case REQ_TYPE_SPECIAL: + vbr->out_hdr.type = VIRTIO_BLK_T_GET_ID; + vbr->out_hdr.sector = 0; + vbr->out_hdr.ioprio = req_get_ioprio(vbr->req); + break; case REQ_TYPE_LINUX_BLOCK: if (req->cmd[0] == REQ_LB_OP_FLUSH) { vbr->out_hdr.type = VIRTIO_BLK_T_FLUSH; @@ -151,7 +158,7 @@ static bool do_req(struct request_queue *q, struct virtio_blk *vblk, } } - if (vblk->vq->vq_ops->add_buf(vblk->vq, vblk->sg, out, in, vbr) < 0) { + if (virtqueue_add_buf(vblk->vq, vblk->sg, out, in, vbr) < 0) { mempool_free(vbr, vblk->pool); return false; } @@ -180,7 +187,7 @@ static void do_virtblk_request(struct request_queue *q) } if (issued) - vblk->vq->vq_ops->kick(vblk->vq); + virtqueue_kick(vblk->vq); } static void virtblk_prepare_flush(struct request_queue *q, struct request *req) @@ -189,12 +196,45 @@ static void virtblk_prepare_flush(struct request_queue *q, struct request *req) req->cmd[0] = REQ_LB_OP_FLUSH; } +/* return id (s/n) string for *disk to *id_str + */ +static int virtblk_get_id(struct gendisk *disk, char *id_str) +{ + struct virtio_blk *vblk = disk->private_data; + struct request *req; + struct bio *bio; + + bio = bio_map_kern(vblk->disk->queue, id_str, VIRTIO_BLK_ID_BYTES, + GFP_KERNEL); + if (IS_ERR(bio)) + return PTR_ERR(bio); + + req = blk_make_request(vblk->disk->queue, bio, GFP_KERNEL); + if (IS_ERR(req)) { + bio_put(bio); + return PTR_ERR(req); + } + + req->cmd_type = REQ_TYPE_SPECIAL; + return blk_execute_rq(vblk->disk->queue, vblk->disk, req, false); +} + static int virtblk_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd, unsigned long data) { struct gendisk *disk = bdev->bd_disk; struct virtio_blk *vblk = disk->private_data; + if (cmd == 'VBID') { + void __user *usr_data = (void __user *)data; + char id_str[VIRTIO_BLK_ID_BYTES]; + int err; + + err = virtblk_get_id(disk, id_str); + if (!err && copy_to_user(usr_data, id_str, VIRTIO_BLK_ID_BYTES)) + err = -EFAULT; + return err; + } /* * Only allow the generic SCSI ioctls if the host can support it. */ diff --git a/drivers/char/hvc_iucv.c b/drivers/char/hvc_iucv.c index 5a80ad68ef22..7b01bc609de3 100644 --- a/drivers/char/hvc_iucv.c +++ b/drivers/char/hvc_iucv.c @@ -1149,7 +1149,7 @@ out_err: * Note: If it is called early in the boot process, @val is stored and * parsed later in hvc_iucv_init(). */ -static int param_set_vmidfilter(const char *val, struct kernel_param *kp) +static int param_set_vmidfilter(const char *val, const struct kernel_param *kp) { int rc; @@ -1176,7 +1176,7 @@ static int param_set_vmidfilter(const char *val, struct kernel_param *kp) * The function stores the filter as a comma-separated list of z/VM user IDs * in @buffer. Typically, sysfs routines call this function for attr show. */ -static int param_get_vmidfilter(char *buffer, struct kernel_param *kp) +static int param_get_vmidfilter(char *buffer, const struct kernel_param *kp) { int rc; size_t index, len; @@ -1203,6 +1203,11 @@ static int param_get_vmidfilter(char *buffer, struct kernel_param *kp) #define param_check_vmidfilter(name, p) __param_check(name, p, void) +static struct kernel_param_ops param_ops_vmidfilter = { + .set = param_set_vmidfilter, + .get = param_get_vmidfilter, +}; + /** * hvc_iucv_init() - z/VM IUCV HVC device driver initialization */ diff --git a/drivers/char/hw_random/virtio-rng.c b/drivers/char/hw_random/virtio-rng.c index 64fe0a793efd..75f1cbd61c17 100644 --- a/drivers/char/hw_random/virtio-rng.c +++ b/drivers/char/hw_random/virtio-rng.c @@ -32,7 +32,7 @@ static bool busy; static void random_recv_done(struct virtqueue *vq) { /* We can get spurious callbacks, e.g. shared IRQs + virtio_pci. */ - if (!vq->vq_ops->get_buf(vq, &data_avail)) + if (!virtqueue_get_buf(vq, &data_avail)) return; complete(&have_data); @@ -46,10 +46,10 @@ static void register_buffer(u8 *buf, size_t size) sg_init_one(&sg, buf, size); /* There should always be room for one buffer. */ - if (vq->vq_ops->add_buf(vq, &sg, 0, 1, buf) < 0) + if (virtqueue_add_buf(vq, &sg, 0, 1, buf) < 0) BUG(); - vq->vq_ops->kick(vq); + virtqueue_kick(vq); } static int virtio_read(struct hwrng *rng, void *buf, size_t size, bool wait) diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c index a4d57e31f713..45999d81004f 100644 --- a/drivers/char/ipmi/ipmi_watchdog.c +++ b/drivers/char/ipmi/ipmi_watchdog.c @@ -196,7 +196,7 @@ static void ipmi_unregister_watchdog(int ipmi_intf); */ static int start_now; -static int set_param_int(const char *val, struct kernel_param *kp) +static int set_param_timeout(const char *val, const struct kernel_param *kp) { char *endp; int l; @@ -215,10 +215,11 @@ static int set_param_int(const char *val, struct kernel_param *kp) return rv; } -static int get_param_int(char *buffer, struct kernel_param *kp) -{ - return sprintf(buffer, "%i", *((int *)kp->arg)); -} +static struct kernel_param_ops param_ops_timeout = { + .set = set_param_timeout, + .get = param_get_int, +}; +#define param_check_timeout param_check_int typedef int (*action_fn)(const char *intval, char *outval); @@ -227,7 +228,7 @@ static int preaction_op(const char *inval, char *outval); static int preop_op(const char *inval, char *outval); static void check_parms(void); -static int set_param_str(const char *val, struct kernel_param *kp) +static int set_param_str(const char *val, const struct kernel_param *kp) { action_fn fn = (action_fn) kp->arg; int rv = 0; @@ -251,7 +252,7 @@ static int set_param_str(const char *val, struct kernel_param *kp) return rv; } -static int get_param_str(char *buffer, struct kernel_param *kp) +static int get_param_str(char *buffer, const struct kernel_param *kp) { action_fn fn = (action_fn) kp->arg; int rv; @@ -263,7 +264,7 @@ static int get_param_str(char *buffer, struct kernel_param *kp) } -static int set_param_wdog_ifnum(const char *val, struct kernel_param *kp) +static int set_param_wdog_ifnum(const char *val, const struct kernel_param *kp) { int rv = param_set_int(val, kp); if (rv) @@ -276,27 +277,38 @@ static int set_param_wdog_ifnum(const char *val, struct kernel_param *kp) return 0; } -module_param_call(ifnum_to_use, set_param_wdog_ifnum, get_param_int, - &ifnum_to_use, 0644); +static struct kernel_param_ops param_ops_wdog_ifnum = { + .set = set_param_wdog_ifnum, + .get = param_get_int, +}; + +#define param_check_wdog_ifnum param_check_int + +static struct kernel_param_ops param_ops_str = { + .set = set_param_str, + .get = get_param_str, +}; + +module_param(ifnum_to_use, wdog_ifnum, 0644); MODULE_PARM_DESC(ifnum_to_use, "The interface number to use for the watchdog " "timer. Setting to -1 defaults to the first registered " "interface"); -module_param_call(timeout, set_param_int, get_param_int, &timeout, 0644); +module_param(timeout, timeout, 0644); MODULE_PARM_DESC(timeout, "Timeout value in seconds."); -module_param_call(pretimeout, set_param_int, get_param_int, &pretimeout, 0644); +module_param(pretimeout, timeout, 0644); MODULE_PARM_DESC(pretimeout, "Pretimeout value in seconds."); -module_param_call(action, set_param_str, get_param_str, action_op, 0644); +module_param_cb(action, ¶m_ops_str, action_op, 0644); MODULE_PARM_DESC(action, "Timeout action. One of: " "reset, none, power_cycle, power_off."); -module_param_call(preaction, set_param_str, get_param_str, preaction_op, 0644); +module_param_cb(preaction, ¶m_ops_str, preaction_op, 0644); MODULE_PARM_DESC(preaction, "Pretimeout action. One of: " "pre_none, pre_smi, pre_nmi, pre_int."); -module_param_call(preop, set_param_str, get_param_str, preop_op, 0644); +module_param_cb(preop, ¶m_ops_str, preop_op, 0644); MODULE_PARM_DESC(preop, "Pretimeout driver operation. One of: " "preop_none, preop_panic, preop_give_data."); diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 196428c2287a..458d907e3621 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -33,35 +33,6 @@ #include <linux/workqueue.h> #include "hvc_console.h" -/* Moved here from .h file in order to disable MULTIPORT. */ -#define VIRTIO_CONSOLE_F_MULTIPORT 1 /* Does host provide multiple ports? */ - -struct virtio_console_multiport_conf { - struct virtio_console_config config; - /* max. number of ports this device can hold */ - __u32 max_nr_ports; - /* number of ports added so far */ - __u32 nr_ports; -} __attribute__((packed)); - -/* - * A message that's passed between the Host and the Guest for a - * particular port. - */ -struct virtio_console_control { - __u32 id; /* Port number */ - __u16 event; /* The kind of control event (see below) */ - __u16 value; /* Extra information for the key */ -}; - -/* Some events for control messages */ -#define VIRTIO_CONSOLE_PORT_READY 0 -#define VIRTIO_CONSOLE_CONSOLE_PORT 1 -#define VIRTIO_CONSOLE_RESIZE 2 -#define VIRTIO_CONSOLE_PORT_OPEN 3 -#define VIRTIO_CONSOLE_PORT_NAME 4 -#define VIRTIO_CONSOLE_PORT_REMOVE 5 - /* * This is a global struct for storing common data for all the devices * this driver handles. @@ -107,6 +78,9 @@ struct console { /* The hvc device associated with this console port */ struct hvc_struct *hvc; + /* The size of the console */ + struct winsize ws; + /* * This number identifies the number that we used to register * with hvc in hvc_instantiate() and hvc_alloc(); this is the @@ -139,7 +113,6 @@ struct ports_device { * notification */ struct work_struct control_work; - struct work_struct config_work; struct list_head ports; @@ -150,7 +123,7 @@ struct ports_device { spinlock_t cvq_lock; /* The current config space is stored here */ - struct virtio_console_multiport_conf config; + struct virtio_console_config config; /* The virtio device we're associated with */ struct virtio_device *vdev; @@ -189,6 +162,9 @@ struct port { */ spinlock_t inbuf_lock; + /* Protect the operations on the out_vq. */ + spinlock_t outvq_lock; + /* The IO vqs for this port */ struct virtqueue *in_vq, *out_vq; @@ -214,6 +190,8 @@ struct port { /* The 'id' to identify the port with the Host */ u32 id; + bool outvq_full; + /* Is the host device open */ bool host_connected; @@ -328,7 +306,7 @@ static void *get_inbuf(struct port *port) unsigned int len; vq = port->in_vq; - buf = vq->vq_ops->get_buf(vq, &len); + buf = virtqueue_get_buf(vq, &len); if (buf) { buf->len = len; buf->offset = 0; @@ -349,8 +327,8 @@ static int add_inbuf(struct virtqueue *vq, struct port_buffer *buf) sg_init_one(sg, buf->buf, buf->size); - ret = vq->vq_ops->add_buf(vq, sg, 0, 1, buf); - vq->vq_ops->kick(vq); + ret = virtqueue_add_buf(vq, sg, 0, 1, buf); + virtqueue_kick(vq); return ret; } @@ -366,7 +344,7 @@ static void discard_port_data(struct port *port) if (port->inbuf) buf = port->inbuf; else - buf = vq->vq_ops->get_buf(vq, &len); + buf = virtqueue_get_buf(vq, &len); ret = 0; while (buf) { @@ -374,7 +352,7 @@ static void discard_port_data(struct port *port) ret++; free_buf(buf); } - buf = vq->vq_ops->get_buf(vq, &len); + buf = virtqueue_get_buf(vq, &len); } port->inbuf = NULL; if (ret) @@ -403,57 +381,96 @@ out: return ret; } -static ssize_t send_control_msg(struct port *port, unsigned int event, - unsigned int value) +static ssize_t __send_control_msg(struct ports_device *portdev, u32 port_id, + unsigned int event, unsigned int value) { struct scatterlist sg[1]; struct virtio_console_control cpkt; struct virtqueue *vq; unsigned int len; - if (!use_multiport(port->portdev)) + if (!use_multiport(portdev)) return 0; - cpkt.id = port->id; + cpkt.id = port_id; cpkt.event = event; cpkt.value = value; - vq = port->portdev->c_ovq; + vq = portdev->c_ovq; sg_init_one(sg, &cpkt, sizeof(cpkt)); - if (vq->vq_ops->add_buf(vq, sg, 1, 0, &cpkt) >= 0) { - vq->vq_ops->kick(vq); - while (!vq->vq_ops->get_buf(vq, &len)) + if (virtqueue_add_buf(vq, sg, 1, 0, &cpkt) >= 0) { + virtqueue_kick(vq); + while (!virtqueue_get_buf(vq, &len)) cpu_relax(); } return 0; } -static ssize_t send_buf(struct port *port, void *in_buf, size_t in_count) +static ssize_t send_control_msg(struct port *port, unsigned int event, + unsigned int value) +{ + return __send_control_msg(port->portdev, port->id, event, value); +} + +/* Callers must take the port->outvq_lock */ +static void reclaim_consumed_buffers(struct port *port) +{ + void *buf; + unsigned int len; + + while ((buf = virtqueue_get_buf(port->out_vq, &len))) { + kfree(buf); + port->outvq_full = false; + } +} + +static ssize_t send_buf(struct port *port, void *in_buf, size_t in_count, + bool nonblock) { struct scatterlist sg[1]; struct virtqueue *out_vq; ssize_t ret; + unsigned long flags; unsigned int len; out_vq = port->out_vq; + spin_lock_irqsave(&port->outvq_lock, flags); + + reclaim_consumed_buffers(port); + sg_init_one(sg, in_buf, in_count); - ret = out_vq->vq_ops->add_buf(out_vq, sg, 1, 0, in_buf); + ret = virtqueue_add_buf(out_vq, sg, 1, 0, in_buf); /* Tell Host to go! */ - out_vq->vq_ops->kick(out_vq); + virtqueue_kick(out_vq); if (ret < 0) { in_count = 0; - goto fail; + goto done; } - /* Wait till the host acknowledges it pushed out the data we sent. */ - while (!out_vq->vq_ops->get_buf(out_vq, &len)) + if (ret == 0) + port->outvq_full = true; + + if (nonblock) + goto done; + + /* + * Wait till the host acknowledges it pushed out the data we + * sent. This is done for ports in blocking mode or for data + * from the hvc_console; the tty operations are performed with + * spinlocks held so we can't sleep here. + */ + while (!virtqueue_get_buf(out_vq, &len)) cpu_relax(); -fail: - /* We're expected to return the amount of data we wrote */ +done: + spin_unlock_irqrestore(&port->outvq_lock, flags); + /* + * We're expected to return the amount of data we wrote -- all + * of it + */ return in_count; } @@ -503,9 +520,28 @@ static ssize_t fill_readbuf(struct port *port, char *out_buf, size_t out_count, } /* The condition that must be true for polling to end */ -static bool wait_is_over(struct port *port) +static bool will_read_block(struct port *port) +{ + return !port_has_data(port) && port->host_connected; +} + +static bool will_write_block(struct port *port) { - return port_has_data(port) || !port->host_connected; + bool ret; + + if (!port->host_connected) + return true; + + spin_lock_irq(&port->outvq_lock); + /* + * Check if the Host has consumed any buffers since we last + * sent data (this is only applicable for nonblocking ports). + */ + reclaim_consumed_buffers(port); + ret = port->outvq_full; + spin_unlock_irq(&port->outvq_lock); + + return ret; } static ssize_t port_fops_read(struct file *filp, char __user *ubuf, @@ -528,7 +564,7 @@ static ssize_t port_fops_read(struct file *filp, char __user *ubuf, return -EAGAIN; ret = wait_event_interruptible(port->waitqueue, - wait_is_over(port)); + !will_read_block(port)); if (ret < 0) return ret; } @@ -554,9 +590,22 @@ static ssize_t port_fops_write(struct file *filp, const char __user *ubuf, struct port *port; char *buf; ssize_t ret; + bool nonblock; port = filp->private_data; + nonblock = filp->f_flags & O_NONBLOCK; + + if (will_write_block(port)) { + if (nonblock) + return -EAGAIN; + + ret = wait_event_interruptible(port->waitqueue, + !will_write_block(port)); + if (ret < 0) + return ret; + } + count = min((size_t)(32 * 1024), count); buf = kmalloc(count, GFP_KERNEL); @@ -569,9 +618,14 @@ static ssize_t port_fops_write(struct file *filp, const char __user *ubuf, goto free_buf; } - ret = send_buf(port, buf, count); + ret = send_buf(port, buf, count, nonblock); + + if (nonblock && ret > 0) + goto out; + free_buf: kfree(buf); +out: return ret; } @@ -586,7 +640,7 @@ static unsigned int port_fops_poll(struct file *filp, poll_table *wait) ret = 0; if (port->inbuf) ret |= POLLIN | POLLRDNORM; - if (port->host_connected) + if (!will_write_block(port)) ret |= POLLOUT; if (!port->host_connected) ret |= POLLHUP; @@ -610,6 +664,10 @@ static int port_fops_release(struct inode *inode, struct file *filp) spin_unlock_irq(&port->inbuf_lock); + spin_lock_irq(&port->outvq_lock); + reclaim_consumed_buffers(port); + spin_unlock_irq(&port->outvq_lock); + return 0; } @@ -638,6 +696,15 @@ static int port_fops_open(struct inode *inode, struct file *filp) port->guest_connected = true; spin_unlock_irq(&port->inbuf_lock); + spin_lock_irq(&port->outvq_lock); + /* + * There might be a chance that we missed reclaiming a few + * buffers in the window of the port getting previously closed + * and opening now. + */ + reclaim_consumed_buffers(port); + spin_unlock_irq(&port->outvq_lock); + /* Notify host of port being opened */ send_control_msg(filp->private_data, VIRTIO_CONSOLE_PORT_OPEN, 1); @@ -676,9 +743,9 @@ static int put_chars(u32 vtermno, const char *buf, int count) port = find_port_by_vtermno(vtermno); if (!port) - return 0; + return -EPIPE; - return send_buf(port, (void *)buf, count); + return send_buf(port, (void *)buf, count, false); } /* @@ -692,9 +759,13 @@ static int get_chars(u32 vtermno, char *buf, int count) { struct port *port; + /* If we've not set up the port yet, we have no input to give. */ + if (unlikely(early_put_chars)) + return 0; + port = find_port_by_vtermno(vtermno); if (!port) - return 0; + return -EPIPE; /* If we don't have an input queue yet, we can't get input. */ BUG_ON(!port->in_vq); @@ -705,22 +776,14 @@ static int get_chars(u32 vtermno, char *buf, int count) static void resize_console(struct port *port) { struct virtio_device *vdev; - struct winsize ws; /* The port could have been hot-unplugged */ - if (!port) + if (!port || !is_console_port(port)) return; vdev = port->portdev->vdev; - if (virtio_has_feature(vdev, VIRTIO_CONSOLE_F_SIZE)) { - vdev->config->get(vdev, - offsetof(struct virtio_console_config, cols), - &ws.ws_col, sizeof(u16)); - vdev->config->get(vdev, - offsetof(struct virtio_console_config, rows), - &ws.ws_row, sizeof(u16)); - hvc_resize(port->cons.hvc, ws); - } + if (virtio_has_feature(vdev, VIRTIO_CONSOLE_F_SIZE)) + hvc_resize(port->cons.hvc, port->cons.ws); } /* We set the configuration at this point, since we now have a tty */ @@ -804,6 +867,13 @@ int init_port_console(struct port *port) spin_unlock_irq(&pdrvdata_lock); port->guest_connected = true; + /* + * Start using the new console output if this is the first + * console to come up. + */ + if (early_put_chars) + early_put_chars = NULL; + /* Notify host of port being opened */ send_control_msg(port, VIRTIO_CONSOLE_PORT_OPEN, 1); @@ -859,6 +929,8 @@ static ssize_t debugfs_read(struct file *filp, char __user *ubuf, out_offset += snprintf(buf + out_offset, out_count - out_offset, "host_connected: %d\n", port->host_connected); out_offset += snprintf(buf + out_offset, out_count - out_offset, + "outvq_full: %d\n", port->outvq_full); + out_offset += snprintf(buf + out_offset, out_count - out_offset, "is_console: %s\n", is_console_port(port) ? "yes" : "no"); out_offset += snprintf(buf + out_offset, out_count - out_offset, @@ -875,6 +947,153 @@ static const struct file_operations port_debugfs_ops = { .read = debugfs_read, }; +static void set_console_size(struct port *port, u16 rows, u16 cols) +{ + if (!port || !is_console_port(port)) + return; + + port->cons.ws.ws_row = rows; + port->cons.ws.ws_col = cols; +} + +static unsigned int fill_queue(struct virtqueue *vq, spinlock_t *lock) +{ + struct port_buffer *buf; + unsigned int nr_added_bufs; + int ret; + + nr_added_bufs = 0; + do { + buf = alloc_buf(PAGE_SIZE); + if (!buf) + break; + + spin_lock_irq(lock); + ret = add_inbuf(vq, buf); + if (ret < 0) { + spin_unlock_irq(lock); + free_buf(buf); + break; + } + nr_added_bufs++; + spin_unlock_irq(lock); + } while (ret > 0); + + return nr_added_bufs; +} + +static int add_port(struct ports_device *portdev, u32 id) +{ + char debugfs_name[16]; + struct port *port; + struct port_buffer *buf; + dev_t devt; + unsigned int nr_added_bufs; + int err; + + port = kmalloc(sizeof(*port), GFP_KERNEL); + if (!port) { + err = -ENOMEM; + goto fail; + } + + port->portdev = portdev; + port->id = id; + + port->name = NULL; + port->inbuf = NULL; + port->cons.hvc = NULL; + + port->cons.ws.ws_row = port->cons.ws.ws_col = 0; + + port->host_connected = port->guest_connected = false; + + port->outvq_full = false; + + port->in_vq = portdev->in_vqs[port->id]; + port->out_vq = portdev->out_vqs[port->id]; + + cdev_init(&port->cdev, &port_fops); + + devt = MKDEV(portdev->chr_major, id); + err = cdev_add(&port->cdev, devt, 1); + if (err < 0) { + dev_err(&port->portdev->vdev->dev, + "Error %d adding cdev for port %u\n", err, id); + goto free_port; + } + port->dev = device_create(pdrvdata.class, &port->portdev->vdev->dev, + devt, port, "vport%up%u", + port->portdev->drv_index, id); + if (IS_ERR(port->dev)) { + err = PTR_ERR(port->dev); + dev_err(&port->portdev->vdev->dev, + "Error %d creating device for port %u\n", + err, id); + goto free_cdev; + } + + spin_lock_init(&port->inbuf_lock); + spin_lock_init(&port->outvq_lock); + init_waitqueue_head(&port->waitqueue); + + /* Fill the in_vq with buffers so the host can send us data. */ + nr_added_bufs = fill_queue(port->in_vq, &port->inbuf_lock); + if (!nr_added_bufs) { + dev_err(port->dev, "Error allocating inbufs\n"); + err = -ENOMEM; + goto free_device; + } + + /* + * If we're not using multiport support, this has to be a console port + */ + if (!use_multiport(port->portdev)) { + err = init_port_console(port); + if (err) + goto free_inbufs; + } + + spin_lock_irq(&portdev->ports_lock); + list_add_tail(&port->list, &port->portdev->ports); + spin_unlock_irq(&portdev->ports_lock); + + /* + * Tell the Host we're set so that it can send us various + * configuration parameters for this port (eg, port name, + * caching, whether this is a console port, etc.) + */ + send_control_msg(port, VIRTIO_CONSOLE_PORT_READY, 1); + + if (pdrvdata.debugfs_dir) { + /* + * Finally, create the debugfs file that we can use to + * inspect a port's state at any time + */ + sprintf(debugfs_name, "vport%up%u", + port->portdev->drv_index, id); + port->debugfs_file = debugfs_create_file(debugfs_name, 0444, + pdrvdata.debugfs_dir, + port, + &port_debugfs_ops); + } + return 0; + +free_inbufs: + while ((buf = virtqueue_detach_unused_buf(port->in_vq))) + free_buf(buf); +free_device: + device_destroy(pdrvdata.class, port->dev->devt); +free_cdev: + cdev_del(&port->cdev); +free_port: + kfree(port); +fail: + /* The host might want to notify management sw about port add failure */ + send_control_msg(port, VIRTIO_CONSOLE_PORT_READY, 0); + return err; +} + /* Remove all port-specific data. */ static int remove_port(struct port *port) { @@ -888,7 +1107,18 @@ static int remove_port(struct port *port) spin_lock_irq(&pdrvdata_lock); list_del(&port->cons.list); spin_unlock_irq(&pdrvdata_lock); +#if 0 + /* + * hvc_remove() not called as removing one hvc port + * results in other hvc ports getting frozen. + * + * Once this is resolved in hvc, this functionality + * will be enabled. Till that is done, the -EPIPE + * return from get_chars() above will help + * hvc_console.c to clean up on ports we remove here. + */ hvc_remove(port->cons.hvc); +#endif } if (port->guest_connected) send_control_msg(port, VIRTIO_CONSOLE_PORT_OPEN, 0); @@ -900,8 +1130,10 @@ static int remove_port(struct port *port) /* Remove unused data this port might have received. */ discard_port_data(port); + reclaim_consumed_buffers(port); + /* Remove buffers we queued up for the Host to send us data in. */ - while ((buf = port->in_vq->vq_ops->detach_unused_buf(port->in_vq))) + while ((buf = virtqueue_detach_unused_buf(port->in_vq))) free_buf(buf); kfree(port->name); @@ -924,7 +1156,7 @@ static void handle_control_message(struct ports_device *portdev, cpkt = (struct virtio_console_control *)(buf->buf + buf->offset); port = find_port_by_id(portdev, cpkt->id); - if (!port) { + if (!port && cpkt->event != VIRTIO_CONSOLE_PORT_ADD) { /* No valid header at start of buffer. Drop it. */ dev_dbg(&portdev->vdev->dev, "Invalid index %u in control packet\n", cpkt->id); @@ -932,6 +1164,24 @@ static void handle_control_message(struct ports_device *portdev, } switch (cpkt->event) { + case VIRTIO_CONSOLE_PORT_ADD: + if (port) { + dev_dbg(&portdev->vdev->dev, + "Port %u already added\n", port->id); + send_control_msg(port, VIRTIO_CONSOLE_PORT_READY, 1); + break; + } + if (cpkt->id >= portdev->config.max_nr_ports) { + dev_warn(&portdev->vdev->dev, + "Request for adding port with out-of-bound id %u, max. supported id: %u\n", + cpkt->id, portdev->config.max_nr_ports - 1); + break; + } + add_port(portdev, cpkt->id); + break; + case VIRTIO_CONSOLE_PORT_REMOVE: + remove_port(port); + break; case VIRTIO_CONSOLE_CONSOLE_PORT: if (!cpkt->value) break; @@ -944,15 +1194,34 @@ static void handle_control_message(struct ports_device *portdev, * have to notify the host first. */ break; - case VIRTIO_CONSOLE_RESIZE: + case VIRTIO_CONSOLE_RESIZE: { + struct { + __u16 rows; + __u16 cols; + } size; + if (!is_console_port(port)) break; + + memcpy(&size, buf->buf + buf->offset + sizeof(*cpkt), + sizeof(size)); + set_console_size(port, size.rows, size.cols); + port->cons.hvc->irq_requested = 1; resize_console(port); break; + } case VIRTIO_CONSOLE_PORT_OPEN: port->host_connected = cpkt->value; wake_up_interruptible(&port->waitqueue); + /* + * If the host port got closed and the host had any + * unconsumed buffers, we'll be able to reclaim them + * now. + */ + spin_lock_irq(&port->outvq_lock); + reclaim_consumed_buffers(port); + spin_unlock_irq(&port->outvq_lock); break; case VIRTIO_CONSOLE_PORT_NAME: /* @@ -990,32 +1259,6 @@ static void handle_control_message(struct ports_device *portdev, kobject_uevent(&port->dev->kobj, KOBJ_CHANGE); } break; - case VIRTIO_CONSOLE_PORT_REMOVE: - /* - * Hot unplug the port. We don't decrement nr_ports - * since we don't want to deal with extra complexities - * of using the lowest-available port id: We can just - * pick up the nr_ports number as the id and not have - * userspace send it to us. This helps us in two - * ways: - * - * - We don't need to have a 'port_id' field in the - * config space when a port is hot-added. This is a - * good thing as we might queue up multiple hotplug - * requests issued in our workqueue. - * - * - Another way to deal with this would have been to - * use a bitmap of the active ports and select the - * lowest non-active port from that map. That - * bloats the already tight config space and we - * would end up artificially limiting the - * max. number of ports to sizeof(bitmap). Right - * now we can support 2^32 ports (as the port id is - * stored in a u32 type). - * - */ - remove_port(port); - break; } } @@ -1030,7 +1273,7 @@ static void control_work_handler(struct work_struct *work) vq = portdev->c_ivq; spin_lock(&portdev->cvq_lock); - while ((buf = vq->vq_ops->get_buf(vq, &len))) { + while ((buf = virtqueue_get_buf(vq, &len))) { spin_unlock(&portdev->cvq_lock); buf->len = len; @@ -1092,204 +1335,29 @@ static void config_intr(struct virtio_device *vdev) struct ports_device *portdev; portdev = vdev->priv; - if (use_multiport(portdev)) { - /* Handle port hot-add */ - schedule_work(&portdev->config_work); - } - /* - * We'll use this way of resizing only for legacy support. - * For newer userspace (VIRTIO_CONSOLE_F_MULTPORT+), use - * control messages to indicate console size changes so that - * it can be done per-port - */ - resize_console(find_port_by_id(portdev, 0)); -} - -static unsigned int fill_queue(struct virtqueue *vq, spinlock_t *lock) -{ - struct port_buffer *buf; - unsigned int nr_added_bufs; - int ret; - - nr_added_bufs = 0; - do { - buf = alloc_buf(PAGE_SIZE); - if (!buf) - break; - - spin_lock_irq(lock); - ret = add_inbuf(vq, buf); - if (ret < 0) { - spin_unlock_irq(lock); - free_buf(buf); - break; - } - nr_added_bufs++; - spin_unlock_irq(lock); - } while (ret > 0); - - return nr_added_bufs; -} - -static int add_port(struct ports_device *portdev, u32 id) -{ - char debugfs_name[16]; - struct port *port; - struct port_buffer *buf; - dev_t devt; - unsigned int nr_added_bufs; - int err; - - port = kmalloc(sizeof(*port), GFP_KERNEL); - if (!port) { - err = -ENOMEM; - goto fail; - } - - port->portdev = portdev; - port->id = id; - - port->name = NULL; - port->inbuf = NULL; - port->cons.hvc = NULL; - - port->host_connected = port->guest_connected = false; - - port->in_vq = portdev->in_vqs[port->id]; - port->out_vq = portdev->out_vqs[port->id]; - - cdev_init(&port->cdev, &port_fops); - - devt = MKDEV(portdev->chr_major, id); - err = cdev_add(&port->cdev, devt, 1); - if (err < 0) { - dev_err(&port->portdev->vdev->dev, - "Error %d adding cdev for port %u\n", err, id); - goto free_port; - } - port->dev = device_create(pdrvdata.class, &port->portdev->vdev->dev, - devt, port, "vport%up%u", - port->portdev->drv_index, id); - if (IS_ERR(port->dev)) { - err = PTR_ERR(port->dev); - dev_err(&port->portdev->vdev->dev, - "Error %d creating device for port %u\n", - err, id); - goto free_cdev; - } - - spin_lock_init(&port->inbuf_lock); - init_waitqueue_head(&port->waitqueue); - - /* Fill the in_vq with buffers so the host can send us data. */ - nr_added_bufs = fill_queue(port->in_vq, &port->inbuf_lock); - if (!nr_added_bufs) { - dev_err(port->dev, "Error allocating inbufs\n"); - err = -ENOMEM; - goto free_device; - } - - /* - * If we're not using multiport support, this has to be a console port - */ - if (!use_multiport(port->portdev)) { - err = init_port_console(port); - if (err) - goto free_inbufs; - } - - spin_lock_irq(&portdev->ports_lock); - list_add_tail(&port->list, &port->portdev->ports); - spin_unlock_irq(&portdev->ports_lock); - - /* - * Tell the Host we're set so that it can send us various - * configuration parameters for this port (eg, port name, - * caching, whether this is a console port, etc.) - */ - send_control_msg(port, VIRTIO_CONSOLE_PORT_READY, 1); - - if (pdrvdata.debugfs_dir) { - /* - * Finally, create the debugfs file that we can use to - * inspect a port's state at any time - */ - sprintf(debugfs_name, "vport%up%u", - port->portdev->drv_index, id); - port->debugfs_file = debugfs_create_file(debugfs_name, 0444, - pdrvdata.debugfs_dir, - port, - &port_debugfs_ops); - } - return 0; - -free_inbufs: - while ((buf = port->in_vq->vq_ops->detach_unused_buf(port->in_vq))) - free_buf(buf); -free_device: - device_destroy(pdrvdata.class, port->dev->devt); -free_cdev: - cdev_del(&port->cdev); -free_port: - kfree(port); -fail: - return err; -} -/* - * The workhandler for config-space updates. - * - * This is called when ports are hot-added. - */ -static void config_work_handler(struct work_struct *work) -{ - struct virtio_console_multiport_conf virtconconf; - struct ports_device *portdev; - struct virtio_device *vdev; - int err; + if (!use_multiport(portdev)) { + struct port *port; + u16 rows, cols; - portdev = container_of(work, struct ports_device, config_work); + vdev->config->get(vdev, + offsetof(struct virtio_console_config, cols), + &cols, sizeof(u16)); + vdev->config->get(vdev, + offsetof(struct virtio_console_config, rows), + &rows, sizeof(u16)); - vdev = portdev->vdev; - vdev->config->get(vdev, - offsetof(struct virtio_console_multiport_conf, - nr_ports), - &virtconconf.nr_ports, - sizeof(virtconconf.nr_ports)); + port = find_port_by_id(portdev, 0); + set_console_size(port, rows, cols); - if (portdev->config.nr_ports == virtconconf.nr_ports) { /* - * Port 0 got hot-added. Since we already did all the - * other initialisation for it, just tell the Host - * that the port is ready if we find the port. In - * case the port was hot-removed earlier, we call - * add_port to add the port. + * We'll use this way of resizing only for legacy + * support. For newer userspace + * (VIRTIO_CONSOLE_F_MULTPORT+), use control messages + * to indicate console size changes so that it can be + * done per-port. */ - struct port *port; - - port = find_port_by_id(portdev, 0); - if (!port) - add_port(portdev, 0); - else - send_control_msg(port, VIRTIO_CONSOLE_PORT_READY, 1); - return; - } - if (virtconconf.nr_ports > portdev->config.max_nr_ports) { - dev_warn(&vdev->dev, - "More ports specified (%u) than allowed (%u)", - portdev->config.nr_ports + 1, - portdev->config.max_nr_ports); - return; - } - if (virtconconf.nr_ports < portdev->config.nr_ports) - return; - - /* Hot-add ports */ - while (virtconconf.nr_ports - portdev->config.nr_ports) { - err = add_port(portdev, portdev->config.nr_ports); - if (err) - break; - portdev->config.nr_ports++; + resize_console(port); } } @@ -1414,7 +1482,6 @@ static const struct file_operations portdev_fops = { static int __devinit virtcons_probe(struct virtio_device *vdev) { struct ports_device *portdev; - u32 i; int err; bool multiport; @@ -1443,37 +1510,19 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) } multiport = false; - portdev->config.nr_ports = 1; portdev->config.max_nr_ports = 1; -#if 0 /* Multiport is not quite ready yet --RR */ if (virtio_has_feature(vdev, VIRTIO_CONSOLE_F_MULTIPORT)) { multiport = true; vdev->features[0] |= 1 << VIRTIO_CONSOLE_F_MULTIPORT; - vdev->config->get(vdev, - offsetof(struct virtio_console_multiport_conf, - nr_ports), - &portdev->config.nr_ports, - sizeof(portdev->config.nr_ports)); - vdev->config->get(vdev, - offsetof(struct virtio_console_multiport_conf, - max_nr_ports), + vdev->config->get(vdev, offsetof(struct virtio_console_config, + max_nr_ports), &portdev->config.max_nr_ports, sizeof(portdev->config.max_nr_ports)); - if (portdev->config.nr_ports > portdev->config.max_nr_ports) { - dev_warn(&vdev->dev, - "More ports (%u) specified than allowed (%u). Will init %u ports.", - portdev->config.nr_ports, - portdev->config.max_nr_ports, - portdev->config.max_nr_ports); - - portdev->config.nr_ports = portdev->config.max_nr_ports; - } } /* Let the Host know we support multiple ports.*/ vdev->config->finalize_features(vdev); -#endif err = init_vqs(portdev); if (err < 0) { @@ -1489,7 +1538,6 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) spin_lock_init(&portdev->cvq_lock); INIT_WORK(&portdev->control_work, &control_work_handler); - INIT_WORK(&portdev->config_work, &config_work_handler); nr_added_bufs = fill_queue(portdev->c_ivq, &portdev->cvq_lock); if (!nr_added_bufs) { @@ -1498,13 +1546,16 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) err = -ENOMEM; goto free_vqs; } + } else { + /* + * For backward compatibility: Create a console port + * if we're running on older host. + */ + add_port(portdev, 0); } - for (i = 0; i < portdev->config.nr_ports; i++) - add_port(portdev, i); - - /* Start using the new console output. */ - early_put_chars = NULL; + __send_control_msg(portdev, VIRTIO_CONSOLE_BAD_ID, + VIRTIO_CONSOLE_DEVICE_READY, 1); return 0; free_vqs: @@ -1516,6 +1567,9 @@ free_chrdev: free: kfree(portdev); fail: + /* The host might want to notify mgmt sw about device add failure */ + __send_control_msg(portdev, VIRTIO_CONSOLE_BAD_ID, + VIRTIO_CONSOLE_DEVICE_READY, 0); return err; } @@ -1529,17 +1583,16 @@ static void virtcons_remove(struct virtio_device *vdev) portdev = vdev->priv; cancel_work_sync(&portdev->control_work); - cancel_work_sync(&portdev->config_work); list_for_each_entry_safe(port, port2, &portdev->ports, list) remove_port(port); unregister_chrdev(portdev->chr_major, "virtio-portsdev"); - while ((buf = portdev->c_ivq->vq_ops->get_buf(portdev->c_ivq, &len))) + while ((buf = virtqueue_get_buf(portdev->c_ivq, &len))) free_buf(buf); - while ((buf = portdev->c_ivq->vq_ops->detach_unused_buf(portdev->c_ivq))) + while ((buf = virtqueue_detach_unused_buf(portdev->c_ivq))) free_buf(buf); vdev->config->del_vqs(vdev); @@ -1556,6 +1609,7 @@ static struct virtio_device_id id_table[] = { static unsigned int features[] = { VIRTIO_CONSOLE_F_SIZE, + VIRTIO_CONSOLE_F_MULTIPORT, }; static struct virtio_driver virtio_console = { diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index 3cb9c4e056ff..fa896210ed7b 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -177,7 +177,7 @@ EXPORT_SYMBOL_GPL(ide_pci_clk); module_param_named(pci_clock, ide_pci_clk, int, 0); MODULE_PARM_DESC(pci_clock, "PCI bus clock frequency (in MHz)"); -static int ide_set_dev_param_mask(const char *s, struct kernel_param *kp) +static int ide_set_dev_param_mask(const char *s, const struct kernel_param *kp) { int a, b, i, j = 1; unsigned int *dev_param_mask = (unsigned int *)kp->arg; @@ -200,34 +200,40 @@ static int ide_set_dev_param_mask(const char *s, struct kernel_param *kp) return 0; } +static struct kernel_param_ops param_ops_ide_dev_mask = { + .set = ide_set_dev_param_mask +}; + +#define param_check_ide_dev_mask(name, p) param_check_uint(name, p) + static unsigned int ide_nodma; -module_param_call(nodma, ide_set_dev_param_mask, NULL, &ide_nodma, 0); +module_param_named(nodma, ide_nodma, ide_dev_mask, 0); MODULE_PARM_DESC(nodma, "disallow DMA for a device"); static unsigned int ide_noflush; -module_param_call(noflush, ide_set_dev_param_mask, NULL, &ide_noflush, 0); +module_param_named(noflush, ide_noflush, ide_dev_mask, 0); MODULE_PARM_DESC(noflush, "disable flush requests for a device"); static unsigned int ide_nohpa; -module_param_call(nohpa, ide_set_dev_param_mask, NULL, &ide_nohpa, 0); +module_param_named(nohpa, ide_nohpa, ide_dev_mask, 0); MODULE_PARM_DESC(nohpa, "disable Host Protected Area for a device"); static unsigned int ide_noprobe; -module_param_call(noprobe, ide_set_dev_param_mask, NULL, &ide_noprobe, 0); +module_param_named(noprobe, ide_noprobe, ide_dev_mask, 0); MODULE_PARM_DESC(noprobe, "skip probing for a device"); static unsigned int ide_nowerr; -module_param_call(nowerr, ide_set_dev_param_mask, NULL, &ide_nowerr, 0); +module_param_named(nowerr, ide_nowerr, ide_dev_mask, 0); MODULE_PARM_DESC(nowerr, "ignore the ATA_DF bit for a device"); static unsigned int ide_cdroms; -module_param_call(cdrom, ide_set_dev_param_mask, NULL, &ide_cdroms, 0); +module_param_named(cdrom, ide_cdroms, ide_dev_mask, 0); MODULE_PARM_DESC(cdrom, "force device as a CD-ROM"); struct chs_geom { diff --git a/drivers/input/misc/ati_remote2.c b/drivers/input/misc/ati_remote2.c index 2124b99378bb..babef88ba1e0 100644 --- a/drivers/input/misc/ati_remote2.c +++ b/drivers/input/misc/ati_remote2.c @@ -38,7 +38,8 @@ enum { }; static int ati_remote2_set_mask(const char *val, - struct kernel_param *kp, unsigned int max) + const struct kernel_param *kp, + unsigned int max) { unsigned long mask; int ret; @@ -59,28 +60,31 @@ static int ati_remote2_set_mask(const char *val, } static int ati_remote2_set_channel_mask(const char *val, - struct kernel_param *kp) + const struct kernel_param *kp) { pr_debug("%s()\n", __func__); return ati_remote2_set_mask(val, kp, ATI_REMOTE2_MAX_CHANNEL_MASK); } -static int ati_remote2_get_channel_mask(char *buffer, struct kernel_param *kp) +static int ati_remote2_get_channel_mask(char *buffer, + const struct kernel_param *kp) { pr_debug("%s()\n", __func__); return sprintf(buffer, "0x%04x", *(unsigned int *)kp->arg); } -static int ati_remote2_set_mode_mask(const char *val, struct kernel_param *kp) +static int ati_remote2_set_mode_mask(const char *val, + const struct kernel_param *kp) { pr_debug("%s()\n", __func__); return ati_remote2_set_mask(val, kp, ATI_REMOTE2_MAX_MODE_MASK); } -static int ati_remote2_get_mode_mask(char *buffer, struct kernel_param *kp) +static int ati_remote2_get_mode_mask(char *buffer, + const struct kernel_param *kp) { pr_debug("%s()\n", __func__); @@ -89,15 +93,19 @@ static int ati_remote2_get_mode_mask(char *buffer, struct kernel_param *kp) static unsigned int channel_mask = ATI_REMOTE2_MAX_CHANNEL_MASK; #define param_check_channel_mask(name, p) __param_check(name, p, unsigned int) -#define param_set_channel_mask ati_remote2_set_channel_mask -#define param_get_channel_mask ati_remote2_get_channel_mask +static struct kernel_param_ops param_ops_channel_mask = { + .set = ati_remote2_set_channel_mask, + .get = ati_remote2_get_channel_mask, +}; module_param(channel_mask, channel_mask, 0644); MODULE_PARM_DESC(channel_mask, "Bitmask of channels to accept <15:Channel16>...<1:Channel2><0:Channel1>"); static unsigned int mode_mask = ATI_REMOTE2_MAX_MODE_MASK; #define param_check_mode_mask(name, p) __param_check(name, p, unsigned int) -#define param_set_mode_mask ati_remote2_set_mode_mask -#define param_get_mode_mask ati_remote2_get_mode_mask +static struct kernel_param_ops param_ops_mode_mask = { + .set = ati_remote2_set_mode_mask, + .get = ati_remote2_get_mode_mask, +}; module_param(mode_mask, mode_mask, 0644); MODULE_PARM_DESC(mode_mask, "Bitmask of modes to accept <4:PC><3:AUX4><2:AUX3><1:AUX2><0:AUX1>"); diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index cbc807264940..9fc4cd0b2056 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -39,11 +39,13 @@ MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); static unsigned int psmouse_max_proto = PSMOUSE_AUTO; -static int psmouse_set_maxproto(const char *val, struct kernel_param *kp); -static int psmouse_get_maxproto(char *buffer, struct kernel_param *kp); +static int psmouse_set_maxproto(const char *val, const struct kernel_param *); +static int psmouse_get_maxproto(char *buffer, const struct kernel_param *kp); +static struct kernel_param_ops param_ops_proto_abbrev = { + .set = psmouse_set_maxproto, + .get = psmouse_get_maxproto, +}; #define param_check_proto_abbrev(name, p) __param_check(name, p, unsigned int) -#define param_set_proto_abbrev psmouse_set_maxproto -#define param_get_proto_abbrev psmouse_get_maxproto module_param_named(proto, psmouse_max_proto, proto_abbrev, 0644); MODULE_PARM_DESC(proto, "Highest protocol extension to probe (bare, imps, exps, any). Useful for KVM switches."); @@ -1673,7 +1675,7 @@ static ssize_t psmouse_attr_set_resolution(struct psmouse *psmouse, void *data, } -static int psmouse_set_maxproto(const char *val, struct kernel_param *kp) +static int psmouse_set_maxproto(const char *val, const struct kernel_param *kp) { const struct psmouse_protocol *proto; @@ -1690,7 +1692,7 @@ static int psmouse_set_maxproto(const char *val, struct kernel_param *kp) return 0; } -static int psmouse_get_maxproto(char *buffer, struct kernel_param *kp) +static int psmouse_get_maxproto(char *buffer, const struct kernel_param *kp) { int type = *((unsigned int *)kp->arg); diff --git a/drivers/isdn/hisax/hisax_fcpcipnp.c b/drivers/isdn/hisax/hisax_fcpcipnp.c index 1925118122f8..8b0a7d86b30f 100644 --- a/drivers/isdn/hisax/hisax_fcpcipnp.c +++ b/drivers/isdn/hisax/hisax_fcpcipnp.c @@ -74,9 +74,10 @@ static struct pnp_device_id fcpnp_ids[] __devinitdata = { .id = "AVM0900", .driver_data = (unsigned long) "Fritz!Card PnP", }, + { .id = "" } }; -MODULE_DEVICE_TABLE(isapnp, fcpnp_ids); +MODULE_DEVICE_TABLE(pnp, fcpnp_ids); #endif static int protocol = 2; /* EURO-ISDN Default */ diff --git a/drivers/message/fusion/mptbase.c b/drivers/message/fusion/mptbase.c index a6a57011ba6c..b112f34d264b 100644 --- a/drivers/message/fusion/mptbase.c +++ b/drivers/message/fusion/mptbase.c @@ -109,8 +109,7 @@ MODULE_PARM_DESC(mpt_debug_level, " debug level - refer to mptdebug.h \ int mpt_fwfault_debug; EXPORT_SYMBOL(mpt_fwfault_debug); -module_param_call(mpt_fwfault_debug, param_set_int, param_get_int, - &mpt_fwfault_debug, 0600); +module_param(mpt_fwfault_debug, int, 0600); MODULE_PARM_DESC(mpt_fwfault_debug, "Enable detection of Firmware fault" " and halt Firmware on fault - (default=0)"); diff --git a/drivers/misc/lkdtm.c b/drivers/misc/lkdtm.c index 31a991161f0a..dcadb5edb234 100644 --- a/drivers/misc/lkdtm.c +++ b/drivers/misc/lkdtm.c @@ -118,9 +118,9 @@ static int count = DEFAULT_COUNT; module_param(recur_count, int, 0644); MODULE_PARM_DESC(recur_count, " Recursion level for the stack overflow test, "\ "default is 10"); -module_param(cpoint_name, charp, 0644); +module_param(cpoint_name, charp, 0444); MODULE_PARM_DESC(cpoint_name, " Crash Point, where kernel is to be crashed"); -module_param(cpoint_type, charp, 0644); +module_param(cpoint_type, charp, 0444); MODULE_PARM_DESC(cpoint_type, " Crash Point Type, action to be taken on "\ "hitting the crash point"); module_param(cpoint_count, int, 0644); diff --git a/drivers/net/myri10ge/myri10ge.c b/drivers/net/myri10ge/myri10ge.c index e0b47cc8a86e..5ae8a93fe4f5 100644 --- a/drivers/net/myri10ge/myri10ge.c +++ b/drivers/net/myri10ge/myri10ge.c @@ -239,6 +239,7 @@ struct myri10ge_priv { int watchdog_resets; int watchdog_pause; int pause; + bool fw_name_allocated; char *fw_name; char eeprom_strings[MYRI10GE_EEPROM_STRINGS_SIZE]; char *product_code_string; @@ -271,6 +272,7 @@ MODULE_FIRMWARE("myri10ge_eth_z8e.dat"); MODULE_FIRMWARE("myri10ge_rss_ethp_z8e.dat"); MODULE_FIRMWARE("myri10ge_rss_eth_z8e.dat"); +/* Careful: must be accessed under kparam_block_sysfs_write */ static char *myri10ge_fw_name = NULL; module_param(myri10ge_fw_name, charp, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(myri10ge_fw_name, "Firmware image name"); @@ -376,6 +378,14 @@ static inline void put_be32(__be32 val, __be32 __iomem * p) static struct net_device_stats *myri10ge_get_stats(struct net_device *dev); +static void set_fw_name(struct myri10ge_priv *mgp, char *name, bool allocated) +{ + if (mgp->fw_name_allocated) + kfree(mgp->fw_name); + mgp->fw_name = name; + mgp->fw_name_allocated = allocated; +} + static int myri10ge_send_cmd(struct myri10ge_priv *mgp, u32 cmd, struct myri10ge_cmd *data, int atomic) @@ -747,7 +757,7 @@ static int myri10ge_load_firmware(struct myri10ge_priv *mgp, int adopt) dev_warn(&mgp->pdev->dev, "via hotplug\n"); } - mgp->fw_name = "adopted"; + set_fw_name(mgp, "adopted", false); mgp->tx_boundary = 2048; myri10ge_dummy_rdma(mgp, 1); status = myri10ge_get_firmware_capabilities(mgp); @@ -3229,7 +3239,7 @@ static void myri10ge_firmware_probe(struct myri10ge_priv *mgp) * load the optimized firmware (which assumes aligned PCIe * completions) in order to see if it works on this host. */ - mgp->fw_name = myri10ge_fw_aligned; + set_fw_name(mgp, myri10ge_fw_aligned, false); status = myri10ge_load_firmware(mgp, 1); if (status != 0) { goto abort; @@ -3257,7 +3267,7 @@ static void myri10ge_firmware_probe(struct myri10ge_priv *mgp) abort: /* fall back to using the unaligned firmware */ mgp->tx_boundary = 2048; - mgp->fw_name = myri10ge_fw_unaligned; + set_fw_name(mgp, myri10ge_fw_unaligned, false); } @@ -3280,7 +3290,7 @@ static void myri10ge_select_firmware(struct myri10ge_priv *mgp) dev_info(&mgp->pdev->dev, "PCIE x%d Link\n", link_width); mgp->tx_boundary = 4096; - mgp->fw_name = myri10ge_fw_aligned; + set_fw_name(mgp, myri10ge_fw_aligned, false); } else { myri10ge_firmware_probe(mgp); } @@ -3289,22 +3299,29 @@ static void myri10ge_select_firmware(struct myri10ge_priv *mgp) dev_info(&mgp->pdev->dev, "Assuming aligned completions (forced)\n"); mgp->tx_boundary = 4096; - mgp->fw_name = myri10ge_fw_aligned; + set_fw_name(mgp, myri10ge_fw_aligned, false); } else { dev_info(&mgp->pdev->dev, "Assuming unaligned completions (forced)\n"); mgp->tx_boundary = 2048; - mgp->fw_name = myri10ge_fw_unaligned; + set_fw_name(mgp, myri10ge_fw_unaligned, false); } } + + kparam_block_sysfs_write(myri10ge_fw_name); if (myri10ge_fw_name != NULL) { - overridden = 1; - mgp->fw_name = myri10ge_fw_name; + char *fw_name = kstrdup(myri10ge_fw_name, GFP_KERNEL); + if (fw_name) { + overridden = 1; + set_fw_name(mgp, fw_name, true); + } } + kparam_unblock_sysfs_write(myri10ge_fw_name); + if (mgp->board_number < MYRI10GE_MAX_BOARDS && myri10ge_fw_names[mgp->board_number] != NULL && strlen(myri10ge_fw_names[mgp->board_number])) { - mgp->fw_name = myri10ge_fw_names[mgp->board_number]; + set_fw_name(mgp, myri10ge_fw_names[mgp->board_number], false); overridden = 1; } if (overridden) @@ -3656,6 +3673,7 @@ static void myri10ge_probe_slices(struct myri10ge_priv *mgp) struct myri10ge_cmd cmd; struct pci_dev *pdev = mgp->pdev; char *old_fw; + bool old_allocated; int i, status, ncpus, msix_cap; mgp->num_slices = 1; @@ -3668,17 +3686,23 @@ static void myri10ge_probe_slices(struct myri10ge_priv *mgp) /* try to load the slice aware rss firmware */ old_fw = mgp->fw_name; + old_allocated = mgp->fw_name_allocated; + /* don't free old_fw if we override it. */ + mgp->fw_name_allocated = false; + if (myri10ge_fw_name != NULL) { dev_info(&mgp->pdev->dev, "overriding rss firmware to %s\n", myri10ge_fw_name); - mgp->fw_name = myri10ge_fw_name; + set_fw_name(mgp, myri10ge_fw_name, false); } else if (old_fw == myri10ge_fw_aligned) - mgp->fw_name = myri10ge_fw_rss_aligned; + set_fw_name(mgp, myri10ge_fw_rss_aligned, false); else - mgp->fw_name = myri10ge_fw_rss_unaligned; + set_fw_name(mgp, myri10ge_fw_rss_unaligned, false); status = myri10ge_load_firmware(mgp, 0); if (status != 0) { dev_info(&pdev->dev, "Rss firmware not found\n"); + if (old_allocated) + kfree(old_fw); return; } @@ -3743,6 +3767,8 @@ static void myri10ge_probe_slices(struct myri10ge_priv *mgp) mgp->num_slices); if (status == 0) { pci_disable_msix(pdev); + if (old_allocated) + kfree(old_fw); return; } if (status > 0) @@ -3759,7 +3785,7 @@ disable_msix: abort_with_fw: mgp->num_slices = 1; - mgp->fw_name = old_fw; + set_fw_name(mgp, old_fw, old_allocated); myri10ge_load_firmware(mgp, 0); } @@ -3989,6 +4015,7 @@ abort_with_enabled: pci_disable_device(pdev); abort_with_netdev: + set_fw_name(mgp, NULL, false); free_netdev(netdev); return status; } @@ -4033,6 +4060,7 @@ static void myri10ge_remove(struct pci_dev *pdev) dma_free_coherent(&pdev->dev, sizeof(*mgp->cmd), mgp->cmd, mgp->cmd_bus); + set_fw_name(mgp, NULL, false); free_netdev(netdev); pci_disable_device(pdev); pci_set_drvdata(pdev, NULL); diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index b0a85d038796..890d45e90b25 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -122,7 +122,7 @@ static void skb_xmit_done(struct virtqueue *svq) struct virtnet_info *vi = svq->vdev->priv; /* Suppress further interrupts. */ - svq->vq_ops->disable_cb(svq); + virtqueue_disable_cb(svq); /* We were probably waiting for more output buffers. */ netif_wake_queue(vi->dev); @@ -210,7 +210,7 @@ static int receive_mergeable(struct virtnet_info *vi, struct sk_buff *skb) return -EINVAL; } - page = vi->rvq->vq_ops->get_buf(vi->rvq, &len); + page = virtqueue_get_buf(vi->rvq, &len); if (!page) { pr_debug("%s: rx error: %d buffers missing\n", skb->dev->name, hdr->mhdr.num_buffers); @@ -340,7 +340,7 @@ static int add_recvbuf_small(struct virtnet_info *vi, gfp_t gfp) skb_to_sgvec(skb, vi->rx_sg + 1, 0, skb->len); - err = vi->rvq->vq_ops->add_buf(vi->rvq, vi->rx_sg, 0, 2, skb); + err = virtqueue_add_buf_gfp(vi->rvq, vi->rx_sg, 0, 2, skb, gfp); if (err < 0) dev_kfree_skb(skb); @@ -385,8 +385,8 @@ static int add_recvbuf_big(struct virtnet_info *vi, gfp_t gfp) /* chain first in list head */ first->private = (unsigned long)list; - err = vi->rvq->vq_ops->add_buf(vi->rvq, vi->rx_sg, 0, MAX_SKB_FRAGS + 2, - first); + err = virtqueue_add_buf_gfp(vi->rvq, vi->rx_sg, 0, MAX_SKB_FRAGS + 2, + first, gfp); if (err < 0) give_pages(vi, first); @@ -404,7 +404,7 @@ static int add_recvbuf_mergeable(struct virtnet_info *vi, gfp_t gfp) sg_init_one(vi->rx_sg, page_address(page), PAGE_SIZE); - err = vi->rvq->vq_ops->add_buf(vi->rvq, vi->rx_sg, 0, 1, page); + err = virtqueue_add_buf_gfp(vi->rvq, vi->rx_sg, 0, 1, page, gfp); if (err < 0) give_pages(vi, page); @@ -433,7 +433,7 @@ static bool try_fill_recv(struct virtnet_info *vi, gfp_t gfp) } while (err > 0); if (unlikely(vi->num > vi->max)) vi->max = vi->num; - vi->rvq->vq_ops->kick(vi->rvq); + virtqueue_kick(vi->rvq); return !oom; } @@ -442,7 +442,7 @@ static void skb_recv_done(struct virtqueue *rvq) struct virtnet_info *vi = rvq->vdev->priv; /* Schedule NAPI, Suppress further interrupts if successful. */ if (napi_schedule_prep(&vi->napi)) { - rvq->vq_ops->disable_cb(rvq); + virtqueue_disable_cb(rvq); __napi_schedule(&vi->napi); } } @@ -471,7 +471,7 @@ static int virtnet_poll(struct napi_struct *napi, int budget) again: while (received < budget && - (buf = vi->rvq->vq_ops->get_buf(vi->rvq, &len)) != NULL) { + (buf = virtqueue_get_buf(vi->rvq, &len)) != NULL) { receive_buf(vi->dev, buf, len); --vi->num; received++; @@ -485,9 +485,9 @@ again: /* Out of packets? */ if (received < budget) { napi_complete(napi); - if (unlikely(!vi->rvq->vq_ops->enable_cb(vi->rvq)) && + if (unlikely(!virtqueue_enable_cb(vi->rvq)) && napi_schedule_prep(napi)) { - vi->rvq->vq_ops->disable_cb(vi->rvq); + virtqueue_disable_cb(vi->rvq); __napi_schedule(napi); goto again; } @@ -501,7 +501,7 @@ static unsigned int free_old_xmit_skbs(struct virtnet_info *vi) struct sk_buff *skb; unsigned int len, tot_sgs = 0; - while ((skb = vi->svq->vq_ops->get_buf(vi->svq, &len)) != NULL) { + while ((skb = virtqueue_get_buf(vi->svq, &len)) != NULL) { pr_debug("Sent skb %p\n", skb); vi->dev->stats.tx_bytes += skb->len; vi->dev->stats.tx_packets++; @@ -554,8 +554,7 @@ static int xmit_skb(struct virtnet_info *vi, struct sk_buff *skb) sg_set_buf(vi->tx_sg, &hdr->hdr, sizeof hdr->hdr); hdr->num_sg = skb_to_sgvec(skb, vi->tx_sg + 1, 0, skb->len) + 1; - return vi->svq->vq_ops->add_buf(vi->svq, vi->tx_sg, hdr->num_sg, - 0, skb); + return virtqueue_add_buf(vi->svq, vi->tx_sg, hdr->num_sg, 0, skb); } static netdev_tx_t start_xmit(struct sk_buff *skb, struct net_device *dev) @@ -574,14 +573,14 @@ again: if (unlikely(capacity < 0)) { netif_stop_queue(dev); dev_warn(&dev->dev, "Unexpected full queue\n"); - if (unlikely(!vi->svq->vq_ops->enable_cb(vi->svq))) { - vi->svq->vq_ops->disable_cb(vi->svq); + if (unlikely(!virtqueue_enable_cb(vi->svq))) { + virtqueue_disable_cb(vi->svq); netif_start_queue(dev); goto again; } return NETDEV_TX_BUSY; } - vi->svq->vq_ops->kick(vi->svq); + virtqueue_kick(vi->svq); /* Don't wait up for transmitted skbs to be freed. */ skb_orphan(skb); @@ -591,12 +590,12 @@ again: * before it gets out of hand. Naturally, this wastes entries. */ if (capacity < 2+MAX_SKB_FRAGS) { netif_stop_queue(dev); - if (unlikely(!vi->svq->vq_ops->enable_cb(vi->svq))) { + if (unlikely(!virtqueue_enable_cb(vi->svq))) { /* More just got used, free them then recheck. */ capacity += free_old_xmit_skbs(vi); if (capacity >= 2+MAX_SKB_FRAGS) { netif_start_queue(dev); - vi->svq->vq_ops->disable_cb(vi->svq); + virtqueue_disable_cb(vi->svq); } } } @@ -641,7 +640,7 @@ static int virtnet_open(struct net_device *dev) * now. virtnet_poll wants re-enable the queue, so we disable here. * We synchronize against interrupts via NAPI_STATE_SCHED */ if (napi_schedule_prep(&vi->napi)) { - vi->rvq->vq_ops->disable_cb(vi->rvq); + virtqueue_disable_cb(vi->rvq); __napi_schedule(&vi->napi); } return 0; @@ -678,15 +677,15 @@ static bool virtnet_send_command(struct virtnet_info *vi, u8 class, u8 cmd, sg_set_buf(&sg[i + 1], sg_virt(s), s->length); sg_set_buf(&sg[out + in - 1], &status, sizeof(status)); - BUG_ON(vi->cvq->vq_ops->add_buf(vi->cvq, sg, out, in, vi) < 0); + BUG_ON(virtqueue_add_buf(vi->cvq, sg, out, in, vi) < 0); - vi->cvq->vq_ops->kick(vi->cvq); + virtqueue_kick(vi->cvq); /* * Spin for a response, the kick causes an ioport write, trapping * into the hypervisor, so the request should be handled immediately. */ - while (!vi->cvq->vq_ops->get_buf(vi->cvq, &tmp)) + while (!virtqueue_get_buf(vi->cvq, &tmp)) cpu_relax(); return status == VIRTIO_NET_OK; @@ -1003,13 +1002,13 @@ static void free_unused_bufs(struct virtnet_info *vi) { void *buf; while (1) { - buf = vi->svq->vq_ops->detach_unused_buf(vi->svq); + buf = virtqueue_detach_unused_buf(vi->svq); if (!buf) break; dev_kfree_skb(buf); } while (1) { - buf = vi->rvq->vq_ops->detach_unused_buf(vi->rvq); + buf = virtqueue_detach_unused_buf(vi->rvq); if (!buf) break; if (vi->mergeable_rx_bufs || vi->big_packets) diff --git a/drivers/net/wireless/libertas/if_sdio.c b/drivers/net/wireless/libertas/if_sdio.c index 64dd345d30f5..7a59574cbb04 100644 --- a/drivers/net/wireless/libertas/if_sdio.c +++ b/drivers/net/wireless/libertas/if_sdio.c @@ -125,6 +125,8 @@ struct if_sdio_card { const char *helper; const char *firmware; + bool helper_allocated; + bool firmware_allocated; u8 buffer[65536]; @@ -984,16 +986,34 @@ static int if_sdio_probe(struct sdio_func *func, card->helper = if_sdio_models[i].helper; card->firmware = if_sdio_models[i].firmware; + kparam_block_sysfs_write(helper_name); if (lbs_helper_name) { + char *helper = kstrdup(lbs_helper_name, GFP_KERNEL); + if (!helper) { + kparam_unblock_sysfs_write(helper_name); + ret = -ENOMEM; + goto free; + } lbs_deb_sdio("overriding helper firmware: %s\n", lbs_helper_name); - card->helper = lbs_helper_name; + card->helper = helper; + card->helper_allocated = true; } + kparam_unblock_sysfs_write(helper_name); + kparam_block_sysfs_write(fw_name); if (lbs_fw_name) { + char *fw_name = kstrdup(lbs_fw_name, GFP_KERNEL); + if (!fw_name) { + kparam_unblock_sysfs_write(fw_name); + ret = -ENOMEM; + goto free; + } lbs_deb_sdio("overriding firmware: %s\n", lbs_fw_name); - card->firmware = lbs_fw_name; + card->firmware = fw_name; + card->firmware_allocated = true; } + kparam_unblock_sysfs_write(fw_name); sdio_claim_host(func); @@ -1127,6 +1147,10 @@ free: kfree(packet); } + if (card->helper_allocated) + kfree(card->helper); + if (card->firmware_allocated) + kfree(card->firmware); kfree(card); goto out; @@ -1177,6 +1201,10 @@ static void if_sdio_remove(struct sdio_func *func) kfree(packet); } + if (card->helper_allocated) + kfree(card->helper); + if (card->firmware_allocated) + kfree(card->firmware); kfree(card); lbs_deb_leave(LBS_DEB_SDIO); diff --git a/drivers/net/wireless/libertas/if_usb.c b/drivers/net/wireless/libertas/if_usb.c index fcea5741ba62..331e3b724874 100644 --- a/drivers/net/wireless/libertas/if_usb.c +++ b/drivers/net/wireless/libertas/if_usb.c @@ -291,10 +291,13 @@ static int if_usb_probe(struct usb_interface *intf, } /* Upload firmware */ + kparam_block_sysfs_write(fw_name); if (__if_usb_prog_firmware(cardp, lbs_fw_name, BOOT_CMD_FW_BY_USB)) { + kparam_unblock_sysfs_write(fw_name); lbs_deb_usbd(&udev->dev, "FW upload failed\n"); goto err_prog_firmware; } + kparam_unblock_sysfs_write(fw_name); if (!(priv = lbs_add_card(cardp, &udev->dev))) goto err_prog_firmware; diff --git a/drivers/net/wireless/libertas_tf/if_usb.c b/drivers/net/wireless/libertas_tf/if_usb.c index 4412c279ca94..c1c9e31e29a1 100644 --- a/drivers/net/wireless/libertas_tf/if_usb.c +++ b/drivers/net/wireless/libertas_tf/if_usb.c @@ -813,12 +813,15 @@ static int if_usb_prog_firmware(struct if_usb_card *cardp) lbtf_deb_enter(LBTF_DEB_USB); + kparam_block_sysfs_write(fw_name); ret = request_firmware(&cardp->fw, lbtf_fw_name, &cardp->udev->dev); if (ret < 0) { pr_err("request_firmware() failed with %#x\n", ret); pr_err("firmware %s not found\n", lbtf_fw_name); + kparam_unblock_sysfs_write(fw_name); goto done; } + kparam_unblock_sysfs_write(fw_name); if (check_fwfile_format(cardp->fw->data, cardp->fw->size)) goto release_fw; diff --git a/drivers/scsi/bfa/bfad.c b/drivers/scsi/bfa/bfad.c index d4fc4287ebd3..24d015241e5b 100644 --- a/drivers/scsi/bfa/bfad.c +++ b/drivers/scsi/bfa/bfad.c @@ -729,6 +729,7 @@ bfad_drv_init(struct bfad_s *bfad) memset(&driver_info, 0, sizeof(driver_info)); strncpy(driver_info.version, BFAD_DRIVER_VERSION, sizeof(driver_info.version) - 1); + __kernel_param_lock(); if (host_name) strncpy(driver_info.host_machine_name, host_name, sizeof(driver_info.host_machine_name) - 1); @@ -738,6 +739,7 @@ bfad_drv_init(struct bfad_s *bfad) if (os_patch) strncpy(driver_info.host_os_patch, os_patch, sizeof(driver_info.host_os_patch) - 1); + __kernel_param_unlock(); strncpy(driver_info.os_device_name, bfad->pci_name, sizeof(driver_info.os_device_name - 1)); diff --git a/drivers/staging/rtl8187se/r8180_core.c b/drivers/staging/rtl8187se/r8180_core.c index 55d12e3271de..b09ce4b517a4 100644 --- a/drivers/staging/rtl8187se/r8180_core.c +++ b/drivers/staging/rtl8187se/r8180_core.c @@ -61,7 +61,7 @@ static struct pci_device_id rtl8180_pci_id_tbl[] __devinitdata = { }; -static char* ifname = "wlan%d"; +static char ifname[IFNAMSIZ] = "wlan%d"; static int hwseqnum = 0; static int hwwep = 0; static int channels = 0x3fff; @@ -74,7 +74,7 @@ MODULE_AUTHOR("Andrea Merello <andreamrl@tiscali.it>"); MODULE_DESCRIPTION("Linux driver for Realtek RTL8180 / RTL8185 WiFi cards"); -module_param(ifname, charp, S_IRUGO|S_IWUSR ); +module_param_string(ifname, ifname, sizeof(ifname), S_IRUGO|S_IWUSR); module_param(hwseqnum,int, S_IRUGO|S_IWUSR); module_param(hwwep,int, S_IRUGO|S_IWUSR); module_param(channels,int, S_IRUGO|S_IWUSR); @@ -3613,7 +3613,7 @@ static int __devinit rtl8180_pci_probe(struct pci_dev *pdev, if (dev_alloc_name(dev, ifname) < 0){ DMESG("Oops: devname already taken! Trying wlan%%d...\n"); - ifname = "wlan%d"; + strcpy(ifname, "wlan%d"); dev_alloc_name(dev, ifname); } diff --git a/drivers/staging/rtl8192e/r8192E_core.c b/drivers/staging/rtl8192e/r8192E_core.c index bb7e1ef28d3b..dbb6fe3c9ddd 100644 --- a/drivers/staging/rtl8192e/r8192E_core.c +++ b/drivers/staging/rtl8192e/r8192E_core.c @@ -116,7 +116,7 @@ static struct pci_device_id rtl8192_pci_id_tbl[] __devinitdata = { {} }; -static char* ifname = "wlan%d"; +static char ifname[IFNAMSIZ] = "wlan%d"; static int hwwep = 1; //default use hw. set 0 to use software security static int channels = 0x3fff; @@ -127,7 +127,7 @@ MODULE_DEVICE_TABLE(pci, rtl8192_pci_id_tbl); MODULE_DESCRIPTION("Linux driver for Realtek RTL819x WiFi cards"); -module_param(ifname, charp, S_IRUGO|S_IWUSR ); +module_param_string(ifname, ifname, sizeof(ifname), S_IRUGO|S_IWUSR); //module_param(hwseqnum,int, S_IRUGO|S_IWUSR); module_param(hwwep,int, S_IRUGO|S_IWUSR); module_param(channels,int, S_IRUGO|S_IWUSR); @@ -6453,7 +6453,7 @@ static int __devinit rtl8192_pci_probe(struct pci_dev *pdev, if (dev_alloc_name(dev, ifname) < 0){ RT_TRACE(COMP_INIT, "Oops: devname already taken! Trying wlan%%d...\n"); - ifname = "wlan%d"; + strcpy(ifname, "wlan%d"); dev_alloc_name(dev, ifname); } diff --git a/drivers/staging/rtl8192su/r8192U_core.c b/drivers/staging/rtl8192su/r8192U_core.c index 04d9b85f3d4c..fcad2cdfeae7 100644 --- a/drivers/staging/rtl8192su/r8192U_core.c +++ b/drivers/staging/rtl8192su/r8192U_core.c @@ -142,13 +142,13 @@ MODULE_VERSION("V 1.1"); MODULE_DEVICE_TABLE(usb, rtl8192_usb_id_tbl); MODULE_DESCRIPTION("Linux driver for Realtek RTL8192 USB WiFi cards"); -static char* ifname = "wlan%d"; +static char ifname[IFNAMSIZ] = "wlan%d"; static int hwwep = 1; //default use hw. set 0 to use software security static int channels = 0x3fff; -module_param(ifname, charp, S_IRUGO|S_IWUSR ); +module_param_string(ifname, ifname, sizeof(ifname), S_IRUGO|S_IWUSR); //module_param(hwseqnum,int, S_IRUGO|S_IWUSR); module_param(hwwep,int, S_IRUGO|S_IWUSR); module_param(channels,int, S_IRUGO|S_IWUSR); @@ -7432,7 +7432,7 @@ static int __devinit rtl8192_usb_probe(struct usb_interface *intf, if (dev_alloc_name(dev, ifname) < 0){ RT_TRACE(COMP_INIT, "Oops: devname already taken! Trying wlan%%d...\n"); - ifname = "wlan%d"; + strcpy(ifname, "wlan%d"); dev_alloc_name(dev, ifname); } diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c index 25f01b536f67..bc62a2019b0d 100644 --- a/drivers/usb/atm/ueagle-atm.c +++ b/drivers/usb/atm/ueagle-atm.c @@ -1552,6 +1552,7 @@ static void cmvs_file_name(struct uea_softc *sc, char *const cmv_name, int ver) char file_arr[] = "CMVxy.bin"; char *file; + kparam_block_sysfs_write(cmv_file); /* set proper name corresponding modem version and line type */ if (cmv_file[sc->modem_index] == NULL) { if (UEA_CHIP_VERSION(sc) == ADI930) @@ -1570,6 +1571,7 @@ static void cmvs_file_name(struct uea_softc *sc, char *const cmv_name, int ver) strlcat(cmv_name, file, UEA_FW_NAME_MAX); if (ver == 2) strlcat(cmv_name, ".v2", UEA_FW_NAME_MAX); + kparam_unblock_sysfs_write(cmv_file); } static int request_cmvs_old(struct uea_softc *sc, diff --git a/drivers/video/uvesafb.c b/drivers/video/uvesafb.c index 7b8839ebf3c4..52ec0959d462 100644 --- a/drivers/video/uvesafb.c +++ b/drivers/video/uvesafb.c @@ -1977,8 +1977,7 @@ static void __devexit uvesafb_exit(void) module_exit(uvesafb_exit); -#define param_get_scroll NULL -static int param_set_scroll(const char *val, struct kernel_param *kp) +static int param_set_scroll(const char *val, const struct kernel_param *kp) { ypan = 0; @@ -1993,7 +1992,9 @@ static int param_set_scroll(const char *val, struct kernel_param *kp) return 0; } - +static struct kernel_param_ops param_ops_scroll = { + .set = param_set_scroll, +}; #define param_check_scroll(name, p) __param_check(name, p, void) module_param_named(scroll, ypan, scroll, 0); diff --git a/drivers/video/vt8623fb.c b/drivers/video/vt8623fb.c index d31dc96f838a..85d76ec4c63e 100644 --- a/drivers/video/vt8623fb.c +++ b/drivers/video/vt8623fb.c @@ -726,7 +726,9 @@ static int __devinit vt8623_pci_probe(struct pci_dev *dev, const struct pci_devi /* Prepare startup mode */ + kparam_block_sysfs_write(mode_option); rc = fb_find_mode(&(info->var), info, mode_option, NULL, 0, NULL, 8); + kparam_unblock_sysfs_write(mode_option); if (! ((rc == 1) || (rc == 2))) { rc = -EINVAL; dev_err(info->device, "mode %s not found\n", mode_option); diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c index bfec7c29486d..0f1da45ba47d 100644 --- a/drivers/virtio/virtio_balloon.c +++ b/drivers/virtio/virtio_balloon.c @@ -75,7 +75,7 @@ static void balloon_ack(struct virtqueue *vq) struct virtio_balloon *vb; unsigned int len; - vb = vq->vq_ops->get_buf(vq, &len); + vb = virtqueue_get_buf(vq, &len); if (vb) complete(&vb->acked); } @@ -89,9 +89,9 @@ static void tell_host(struct virtio_balloon *vb, struct virtqueue *vq) init_completion(&vb->acked); /* We should always be able to add one buffer to an empty queue. */ - if (vq->vq_ops->add_buf(vq, &sg, 1, 0, vb) < 0) + if (virtqueue_add_buf(vq, &sg, 1, 0, vb) < 0) BUG(); - vq->vq_ops->kick(vq); + virtqueue_kick(vq); /* When host has read buffer, this completes via balloon_ack */ wait_for_completion(&vb->acked); @@ -204,7 +204,7 @@ static void stats_request(struct virtqueue *vq) struct virtio_balloon *vb; unsigned int len; - vb = vq->vq_ops->get_buf(vq, &len); + vb = virtqueue_get_buf(vq, &len); if (!vb) return; vb->need_stats_update = 1; @@ -221,9 +221,9 @@ static void stats_handle_request(struct virtio_balloon *vb) vq = vb->stats_vq; sg_init_one(&sg, vb->stats, sizeof(vb->stats)); - if (vq->vq_ops->add_buf(vq, &sg, 1, 0, vb) < 0) + if (virtqueue_add_buf(vq, &sg, 1, 0, vb) < 0) BUG(); - vq->vq_ops->kick(vq); + virtqueue_kick(vq); } static void virtballoon_changed(struct virtio_device *vdev) @@ -314,10 +314,9 @@ static int virtballoon_probe(struct virtio_device *vdev) * use it to signal us later. */ sg_init_one(&sg, vb->stats, sizeof vb->stats); - if (vb->stats_vq->vq_ops->add_buf(vb->stats_vq, - &sg, 1, 0, vb) < 0) + if (virtqueue_add_buf(vb->stats_vq, &sg, 1, 0, vb) < 0) BUG(); - vb->stats_vq->vq_ops->kick(vb->stats_vq); + virtqueue_kick(vb->stats_vq); } vb->thread = kthread_run(balloon, vb, "vballoon"); diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c index 0f90634bcb85..1ca88908723b 100644 --- a/drivers/virtio/virtio_ring.c +++ b/drivers/virtio/virtio_ring.c @@ -110,13 +110,14 @@ struct vring_virtqueue static int vring_add_indirect(struct vring_virtqueue *vq, struct scatterlist sg[], unsigned int out, - unsigned int in) + unsigned int in, + gfp_t gfp) { struct vring_desc *desc; unsigned head; int i; - desc = kmalloc((out + in) * sizeof(struct vring_desc), GFP_ATOMIC); + desc = kmalloc((out + in) * sizeof(struct vring_desc), gfp); if (!desc) return vq->vring.num; @@ -155,11 +156,12 @@ static int vring_add_indirect(struct vring_virtqueue *vq, return head; } -static int vring_add_buf(struct virtqueue *_vq, - struct scatterlist sg[], - unsigned int out, - unsigned int in, - void *data) +int virtqueue_add_buf_gfp(struct virtqueue *_vq, + struct scatterlist sg[], + unsigned int out, + unsigned int in, + void *data, + gfp_t gfp) { struct vring_virtqueue *vq = to_vvq(_vq); unsigned int i, avail, head, uninitialized_var(prev); @@ -171,7 +173,7 @@ static int vring_add_buf(struct virtqueue *_vq, /* If the host supports indirect descriptor tables, and we have multiple * buffers, then go indirect. FIXME: tune this threshold */ if (vq->indirect && (out + in) > 1 && vq->num_free) { - head = vring_add_indirect(vq, sg, out, in); + head = vring_add_indirect(vq, sg, out, in, gfp); if (head != vq->vring.num) goto add_head; } @@ -232,8 +234,9 @@ add_head: return vq->num_free ? vq->vring.num : 0; return vq->num_free; } +EXPORT_SYMBOL_GPL(virtqueue_add_buf_gfp); -static void vring_kick(struct virtqueue *_vq) +void virtqueue_kick(struct virtqueue *_vq) { struct vring_virtqueue *vq = to_vvq(_vq); START_USE(vq); @@ -253,6 +256,7 @@ static void vring_kick(struct virtqueue *_vq) END_USE(vq); } +EXPORT_SYMBOL_GPL(virtqueue_kick); static void detach_buf(struct vring_virtqueue *vq, unsigned int head) { @@ -284,7 +288,7 @@ static inline bool more_used(const struct vring_virtqueue *vq) return vq->last_used_idx != vq->vring.used->idx; } -static void *vring_get_buf(struct virtqueue *_vq, unsigned int *len) +void *virtqueue_get_buf(struct virtqueue *_vq, unsigned int *len) { struct vring_virtqueue *vq = to_vvq(_vq); void *ret; @@ -325,15 +329,17 @@ static void *vring_get_buf(struct virtqueue *_vq, unsigned int *len) END_USE(vq); return ret; } +EXPORT_SYMBOL_GPL(virtqueue_get_buf); -static void vring_disable_cb(struct virtqueue *_vq) +void virtqueue_disable_cb(struct virtqueue *_vq) { struct vring_virtqueue *vq = to_vvq(_vq); vq->vring.avail->flags |= VRING_AVAIL_F_NO_INTERRUPT; } +EXPORT_SYMBOL_GPL(virtqueue_disable_cb); -static bool vring_enable_cb(struct virtqueue *_vq) +bool virtqueue_enable_cb(struct virtqueue *_vq) { struct vring_virtqueue *vq = to_vvq(_vq); @@ -351,8 +357,9 @@ static bool vring_enable_cb(struct virtqueue *_vq) END_USE(vq); return true; } +EXPORT_SYMBOL_GPL(virtqueue_enable_cb); -static void *vring_detach_unused_buf(struct virtqueue *_vq) +void *virtqueue_detach_unused_buf(struct virtqueue *_vq) { struct vring_virtqueue *vq = to_vvq(_vq); unsigned int i; @@ -375,6 +382,7 @@ static void *vring_detach_unused_buf(struct virtqueue *_vq) END_USE(vq); return NULL; } +EXPORT_SYMBOL_GPL(virtqueue_detach_unused_buf); irqreturn_t vring_interrupt(int irq, void *_vq) { @@ -396,15 +404,6 @@ irqreturn_t vring_interrupt(int irq, void *_vq) } EXPORT_SYMBOL_GPL(vring_interrupt); -static struct virtqueue_ops vring_vq_ops = { - .add_buf = vring_add_buf, - .get_buf = vring_get_buf, - .kick = vring_kick, - .disable_cb = vring_disable_cb, - .enable_cb = vring_enable_cb, - .detach_unused_buf = vring_detach_unused_buf, -}; - struct virtqueue *vring_new_virtqueue(unsigned int num, unsigned int vring_align, struct virtio_device *vdev, @@ -429,7 +428,6 @@ struct virtqueue *vring_new_virtqueue(unsigned int num, vring_init(&vq->vring, num, pages, vring_align); vq->vq.callback = callback; vq->vq.vdev = vdev; - vq->vq.vq_ops = &vring_vq_ops; vq->vq.name = name; vq->notify = notify; vq->broken = false; diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c index 36dfdae95123..e17b49e2eabd 100644 --- a/fs/nfs/callback.c +++ b/fs/nfs/callback.c @@ -45,7 +45,7 @@ unsigned short nfs_callback_tcpport; unsigned short nfs_callback_tcpport6; #define NFS_CALLBACK_MAXPORTNR (65535U) -static int param_set_portnr(const char *val, struct kernel_param *kp) +static int param_set_portnr(const char *val, const struct kernel_param *kp) { unsigned long num; int ret; @@ -58,11 +58,10 @@ static int param_set_portnr(const char *val, struct kernel_param *kp) *((unsigned int *)kp->arg) = num; return 0; } - -static int param_get_portnr(char *buffer, struct kernel_param *kp) -{ - return param_get_uint(buffer, kp); -} +static struct kernel_param_ops param_ops_portnr = { + .set = param_set_portnr, + .get = param_get_uint, +}; #define param_check_portnr(name, p) __param_check(name, p, unsigned int); module_param_named(callback_tcpport, nfs_callback_set_tcpport, portnr, 0644); diff --git a/include/linux/isapnp.h b/include/linux/isapnp.h index cd5a269fdb5e..e2d28b026a8c 100644 --- a/include/linux/isapnp.h +++ b/include/linux/isapnp.h @@ -43,10 +43,10 @@ */ #ifdef __KERNEL__ +#include <linux/mod_devicetable.h> #define DEVICE_COUNT_COMPATIBLE 4 -#define ISAPNP_ANY_ID 0xffff #define ISAPNP_CARD_DEVS 8 #define ISAPNP_CARD_ID(_va, _vb, _vc, _device) \ @@ -74,12 +74,6 @@ struct isapnp_card_id { #define ISAPNP_DEVICE_SINGLE_END \ .card_vendor = 0, .card_device = 0 -struct isapnp_device_id { - unsigned short card_vendor, card_device; - unsigned short vendor, function; - unsigned long driver_data; /* data private to the driver */ -}; - #if defined(CONFIG_ISAPNP) || (defined(CONFIG_ISAPNP_MODULE) && defined(MODULE)) #define __ISAPNP__ diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index 55f1f9c9506c..529e34b661af 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -500,4 +500,11 @@ struct mdio_device_id { __u32 phy_id_mask; }; +#define ISAPNP_ANY_ID 0xffff +struct isapnp_device_id { + unsigned short card_vendor, card_device; + unsigned short vendor, function; + kernel_ulong_t driver_data; /* data private to the driver */ +}; + #endif /* LINUX_MOD_DEVICETABLE_H */ diff --git a/include/linux/moduleparam.h b/include/linux/moduleparam.h index 82a9124f7d75..893549c04265 100644 --- a/include/linux/moduleparam.h +++ b/include/linux/moduleparam.h @@ -31,20 +31,23 @@ static const char __module_cat(name,__LINE__)[] \ struct kernel_param; -/* Returns 0, or -errno. arg is in kp->arg. */ -typedef int (*param_set_fn)(const char *val, struct kernel_param *kp); -/* Returns length written or -errno. Buffer is 4k (ie. be short!) */ -typedef int (*param_get_fn)(char *buffer, struct kernel_param *kp); +struct kernel_param_ops { + /* Returns 0, or -errno. arg is in kp->arg. */ + int (*set)(const char *val, const struct kernel_param *kp); + /* Returns length written or -errno. Buffer is 4k (ie. be short!) */ + int (*get)(char *buffer, const struct kernel_param *kp); + /* Optional function to free kp->arg when module unloaded. */ + void (*free)(void *arg); +}; /* Flag bits for kernel_param.flags */ #define KPARAM_ISBOOL 2 struct kernel_param { const char *name; + const struct kernel_param_ops *ops; u16 perm; u16 flags; - param_set_fn set; - param_get_fn get; union { void *arg; const struct kparam_string *str; @@ -63,12 +66,67 @@ struct kparam_array { unsigned int max; unsigned int *num; - param_set_fn set; - param_get_fn get; + const struct kernel_param_ops *ops; unsigned int elemsize; void *elem; }; +/** + * module_param - typesafe helper for a module/cmdline parameter + * @value: the variable to alter, and exposed parameter name. + * @type: the type of the parameter + * @perm: visibility in sysfs. + * + * @value becomes the module parameter, or (prefixed by KBUILD_MODNAME and a + * ".") the kernel commandline parameter. Note that - is changed to _, so + * the user can use "foo-bar=1" even for variable "foo_bar". + * + * @perm is 0 if the the variable is not to appear in sysfs, or 0444 + * for world-readable, 0644 for root-writable, etc. Note that if it + * is writable, you may need to use kparam_block_sysfs_write() around + * accesses (esp. charp, which can be kfreed when it changes). + * + * The @type is simply pasted to refer to a param_ops_##type and a + * param_check_##type: for convenience many standard types are provided but + * you can create your own by defining those variables. + * + * Standard types are: + * byte, short, ushort, int, uint, long, ulong + * charp: a character pointer + * bool: a bool, values 0/1, y/n, Y/N. + * invbool: the above, only sense-reversed (N = true). + */ +#define module_param(name, type, perm) \ + module_param_named(name, name, type, perm) + +/** + * module_param_named - typesafe helper for a renamed module/cmdline parameter + * @name: a valid C identifier which is the parameter name. + * @value: the actual lvalue to alter. + * @type: the type of the parameter + * @perm: visibility in sysfs. + * + * Usually it's a good idea to have variable names and user-exposed names the + * same, but that's harder if the variable must be non-static or is inside a + * structure. This allows exposure under a different name. + */ +#define module_param_named(name, value, type, perm) \ + param_check_##type(name, &(value)); \ + module_param_cb(name, ¶m_ops_##type, &value, perm); \ + __MODULE_PARM_TYPE(name, #type) + +/** + * module_param_cb - general callback for a module/cmdline parameter + * @name: a valid C identifier which is the parameter name. + * @ops: the set & get operations for this parameter. + * @perm: visibility in sysfs. + * + * The ops can have NULL set or get functions. + */ +#define module_param_cb(name, ops, arg, perm) \ + __module_param_call(MODULE_PARAM_PREFIX, \ + name, ops, arg, __same_type(*(arg), bool), perm) + /* On alpha, ia64 and ppc64 relocations to global data cannot go into read-only sections (which is part of respective UNIX ABI on these platforms). So 'const' makes no sense and even causes compile failures @@ -80,10 +138,8 @@ struct kparam_array #endif /* This is the fundamental function for registering boot/module - parameters. perm sets the visibility in sysfs: 000 means it's - not there, read bits mean it's readable, write bits mean it's - writable. */ -#define __module_param_call(prefix, name, set, get, arg, isbool, perm) \ + parameters. */ +#define __module_param_call(prefix, name, ops, arg, isbool, perm) \ /* Default value instead of permissions? */ \ static int __param_perm_check_##name __attribute__((unused)) = \ BUILD_BUG_ON_ZERO((perm) < 0 || (perm) > 0777 || ((perm) & 2)) \ @@ -92,31 +148,87 @@ struct kparam_array static struct kernel_param __moduleparam_const __param_##name \ __used \ __attribute__ ((unused,__section__ ("__param"),aligned(sizeof(void *)))) \ - = { __param_str_##name, perm, isbool ? KPARAM_ISBOOL : 0, \ - set, get, { arg } } + = { __param_str_##name, ops, perm, isbool ? KPARAM_ISBOOL : 0, \ + { arg } } + +/* Obsolete - use module_param_cb() */ +#define module_param_call(name, set, get, arg, perm) \ + static struct kernel_param_ops __param_ops_##name = \ + { (void *)set, (void *)get }; \ + __module_param_call(MODULE_PARAM_PREFIX, \ + name, &__param_ops_##name, arg, \ + __same_type(*(arg), bool), \ + (perm) + sizeof(__check_old_set_param(set))*0) + +/* We don't get oldget: it's often a new-style param_get_uint, etc. */ +static inline int +__check_old_set_param(int (*oldset)(const char *, struct kernel_param *)) +{ + return 0; +} -#define module_param_call(name, set, get, arg, perm) \ - __module_param_call(MODULE_PARAM_PREFIX, \ - name, set, get, arg, \ - __same_type(*(arg), bool), perm) +/** + * kparam_block_sysfs_write - make sure a parameter isn't written via sysfs. + * @name: the name of the parameter + * + * There's no point blocking write on a paramter that isn't writable via sysfs! + */ +#define kparam_block_sysfs_write(name) \ + do { \ + BUG_ON(!(__param_##name.perm & 0222)); \ + __kernel_param_lock(); \ + } while (0) -/* Helper functions: type is byte, short, ushort, int, uint, long, - ulong, charp, bool or invbool, or XXX if you define param_get_XXX, - param_set_XXX and param_check_XXX. */ -#define module_param_named(name, value, type, perm) \ - param_check_##type(name, &(value)); \ - module_param_call(name, param_set_##type, param_get_##type, &value, perm); \ - __MODULE_PARM_TYPE(name, #type) +/** + * kparam_unblock_sysfs_write - allows sysfs to write to a parameter again. + * @name: the name of the parameter + */ +#define kparam_unblock_sysfs_write(name) \ + do { \ + BUG_ON(!(__param_##name.perm & 0222)); \ + __kernel_param_unlock(); \ + } while (0) -#define module_param(name, type, perm) \ - module_param_named(name, name, type, perm) +/** + * kparam_block_sysfs_read - make sure a parameter isn't read via sysfs. + * @name: the name of the parameter + * + * This also blocks sysfs writes. + */ +#define kparam_block_sysfs_read(name) \ + do { \ + BUG_ON(!(__param_##name.perm & 0444)); \ + __kernel_param_lock(); \ + } while (0) + +/** + * kparam_unblock_sysfs_read - allows sysfs to read a parameter again. + * @name: the name of the parameter + */ +#define kparam_unblock_sysfs_read(name) \ + do { \ + BUG_ON(!(__param_##name.perm & 0444)); \ + __kernel_param_unlock(); \ + } while (0) + +#ifdef CONFIG_SYSFS +extern void __kernel_param_lock(void); +extern void __kernel_param_unlock(void); +#else +static inline void __kernel_param_lock(void) +{ +} +static inline void __kernel_param_unlock(void) +{ +} +#endif #ifndef MODULE /** * core_param - define a historical core kernel parameter. * @name: the name of the cmdline and sysfs parameter (often the same as var) * @var: the variable - * @type: the type (for param_set_##type and param_get_##type) + * @type: the type of the parameter * @perm: visibility in sysfs * * core_param is just like module_param(), but cannot be modular and @@ -126,23 +238,32 @@ struct kparam_array */ #define core_param(name, var, type, perm) \ param_check_##type(name, &(var)); \ - __module_param_call("", name, param_set_##type, param_get_##type, \ + __module_param_call("", name, ¶m_ops_##type, \ &var, __same_type(var, bool), perm) #endif /* !MODULE */ -/* Actually copy string: maxlen param is usually sizeof(string). */ +/** + * module_param_string - a char array parameter + * @name: the name of the parameter + * @string: the string variable + * @len: the maximum length of the string, incl. terminator + * @perm: visibility in sysfs. + * + * This actually copies the string when it's set (unlike type charp). + * @len is usually just sizeof(string). + */ #define module_param_string(name, string, len, perm) \ static const struct kparam_string __param_string_##name \ = { len, string }; \ __module_param_call(MODULE_PARAM_PREFIX, name, \ - param_set_copystring, param_get_string, \ + ¶m_ops_string, \ .str = &__param_string_##name, 0, perm); \ __MODULE_PARM_TYPE(name, "string") /* Called on module insert or kernel boot */ extern int parse_args(const char *name, char *args, - struct kernel_param *params, + const struct kernel_param *params, unsigned num, int (*unknown)(char *param, char *val)); @@ -162,41 +283,50 @@ static inline void destroy_params(const struct kernel_param *params, #define __param_check(name, p, type) \ static inline type *__check_##name(void) { return(p); } -extern int param_set_byte(const char *val, struct kernel_param *kp); -extern int param_get_byte(char *buffer, struct kernel_param *kp); +extern struct kernel_param_ops param_ops_byte; +extern int param_set_byte(const char *val, const struct kernel_param *kp); +extern int param_get_byte(char *buffer, const struct kernel_param *kp); #define param_check_byte(name, p) __param_check(name, p, unsigned char) -extern int param_set_short(const char *val, struct kernel_param *kp); -extern int param_get_short(char *buffer, struct kernel_param *kp); +extern struct kernel_param_ops param_ops_short; +extern int param_set_short(const char *val, const struct kernel_param *kp); +extern int param_get_short(char *buffer, const struct kernel_param *kp); #define param_check_short(name, p) __param_check(name, p, short) -extern int param_set_ushort(const char *val, struct kernel_param *kp); -extern int param_get_ushort(char *buffer, struct kernel_param *kp); +extern struct kernel_param_ops param_ops_ushort; +extern int param_set_ushort(const char *val, const struct kernel_param *kp); +extern int param_get_ushort(char *buffer, const struct kernel_param *kp); #define param_check_ushort(name, p) __param_check(name, p, unsigned short) -extern int param_set_int(const char *val, struct kernel_param *kp); -extern int param_get_int(char *buffer, struct kernel_param *kp); +extern struct kernel_param_ops param_ops_int; +extern int param_set_int(const char *val, const struct kernel_param *kp); +extern int param_get_int(char *buffer, const struct kernel_param *kp); #define param_check_int(name, p) __param_check(name, p, int) -extern int param_set_uint(const char *val, struct kernel_param *kp); -extern int param_get_uint(char *buffer, struct kernel_param *kp); +extern struct kernel_param_ops param_ops_uint; +extern int param_set_uint(const char *val, const struct kernel_param *kp); +extern int param_get_uint(char *buffer, const struct kernel_param *kp); #define param_check_uint(name, p) __param_check(name, p, unsigned int) -extern int param_set_long(const char *val, struct kernel_param *kp); -extern int param_get_long(char *buffer, struct kernel_param *kp); +extern struct kernel_param_ops param_ops_long; +extern int param_set_long(const char *val, const struct kernel_param *kp); +extern int param_get_long(char *buffer, const struct kernel_param *kp); #define param_check_long(name, p) __param_check(name, p, long) -extern int param_set_ulong(const char *val, struct kernel_param *kp); -extern int param_get_ulong(char *buffer, struct kernel_param *kp); +extern struct kernel_param_ops param_ops_ulong; +extern int param_set_ulong(const char *val, const struct kernel_param *kp); +extern int param_get_ulong(char *buffer, const struct kernel_param *kp); #define param_check_ulong(name, p) __param_check(name, p, unsigned long) -extern int param_set_charp(const char *val, struct kernel_param *kp); -extern int param_get_charp(char *buffer, struct kernel_param *kp); +extern struct kernel_param_ops param_ops_charp; +extern int param_set_charp(const char *val, const struct kernel_param *kp); +extern int param_get_charp(char *buffer, const struct kernel_param *kp); #define param_check_charp(name, p) __param_check(name, p, char *) /* For historical reasons "bool" parameters can be (unsigned) "int". */ -extern int param_set_bool(const char *val, struct kernel_param *kp); -extern int param_get_bool(char *buffer, struct kernel_param *kp); +extern struct kernel_param_ops param_ops_bool; +extern int param_set_bool(const char *val, const struct kernel_param *kp); +extern int param_get_bool(char *buffer, const struct kernel_param *kp); #define param_check_bool(name, p) \ static inline void __check_##name(void) \ { \ @@ -205,29 +335,53 @@ extern int param_get_bool(char *buffer, struct kernel_param *kp); !__same_type(*(p), int)); \ } -extern int param_set_invbool(const char *val, struct kernel_param *kp); -extern int param_get_invbool(char *buffer, struct kernel_param *kp); +extern struct kernel_param_ops param_ops_invbool; +extern int param_set_invbool(const char *val, const struct kernel_param *kp); +extern int param_get_invbool(char *buffer, const struct kernel_param *kp); #define param_check_invbool(name, p) __param_check(name, p, bool) -/* Comma-separated array: *nump is set to number they actually specified. */ +/** + * module_param_array - a parameter which is an array of some type + * @name: the name of the array variable + * @type: the type, as per module_param() + * @nump: optional pointer filled in with the number written + * @perm: visibility in sysfs + * + * Input and output are as comma-separated values. Commas inside values + * don't work properly (eg. an array of charp). + * + * ARRAY_SIZE(@name) is used to determine the number of elements in the + * array, so the definition must be visible. + */ +#define module_param_array(name, type, nump, perm) \ + module_param_array_named(name, name, type, nump, perm) + +/** + * module_param_array_named - renamed parameter which is an array of some type + * @name: a valid C identifier which is the parameter name + * @array: the name of the array variable + * @type: the type, as per module_param() + * @nump: optional pointer filled in with the number written + * @perm: visibility in sysfs + * + * This exposes a different name than the actual variable name. See + * module_param_named() for why this might be necessary. + */ #define module_param_array_named(name, array, type, nump, perm) \ static const struct kparam_array __param_arr_##name \ - = { ARRAY_SIZE(array), nump, param_set_##type, param_get_##type,\ + = { ARRAY_SIZE(array), nump, ¶m_ops_##type, \ sizeof(array[0]), array }; \ __module_param_call(MODULE_PARAM_PREFIX, name, \ - param_array_set, param_array_get, \ + ¶m_array_ops, \ .arr = &__param_arr_##name, \ __same_type(array[0], bool), perm); \ __MODULE_PARM_TYPE(name, "array of " #type) -#define module_param_array(name, type, nump, perm) \ - module_param_array_named(name, name, type, nump, perm) - -extern int param_array_set(const char *val, struct kernel_param *kp); -extern int param_array_get(char *buffer, struct kernel_param *kp); +extern struct kernel_param_ops param_array_ops; -extern int param_set_copystring(const char *val, struct kernel_param *kp); -extern int param_get_string(char *buffer, struct kernel_param *kp); +extern struct kernel_param_ops param_ops_string; +extern int param_set_copystring(const char *val, const struct kernel_param *); +extern int param_get_string(char *buffer, const struct kernel_param *kp); /* for exporting parameters in /sys/parameters */ @@ -235,13 +389,13 @@ struct module; #if defined(CONFIG_SYSFS) && defined(CONFIG_MODULES) extern int module_param_sysfs_setup(struct module *mod, - struct kernel_param *kparam, + const struct kernel_param *kparam, unsigned int num_params); extern void module_param_sysfs_remove(struct module *mod); #else static inline int module_param_sysfs_setup(struct module *mod, - struct kernel_param *kparam, + const struct kernel_param *kparam, unsigned int num_params) { return 0; diff --git a/include/linux/virtio.h b/include/linux/virtio.h index 40d1709bdbf4..aff5b4f74041 100644 --- a/include/linux/virtio.h +++ b/include/linux/virtio.h @@ -7,6 +7,7 @@ #include <linux/spinlock.h> #include <linux/device.h> #include <linux/mod_devicetable.h> +#include <linux/gfp.h> /** * virtqueue - a queue to register buffers for sending or receiving. @@ -14,7 +15,6 @@ * @callback: the function to call when buffers are consumed (can be NULL). * @name: the name of this virtqueue (mainly for debugging) * @vdev: the virtio device this queue was created for. - * @vq_ops: the operations for this virtqueue (see below). * @priv: a pointer for the virtqueue implementation to use. */ struct virtqueue { @@ -22,60 +22,71 @@ struct virtqueue { void (*callback)(struct virtqueue *vq); const char *name; struct virtio_device *vdev; - struct virtqueue_ops *vq_ops; void *priv; }; /** - * virtqueue_ops - operations for virtqueue abstraction layer - * @add_buf: expose buffer to other end + * operations for virtqueue + * virtqueue_add_buf: expose buffer to other end * vq: the struct virtqueue we're talking about. * sg: the description of the buffer(s). * out_num: the number of sg readable by other side * in_num: the number of sg which are writable (after readable ones) * data: the token identifying the buffer. + * gfp: how to do memory allocations (if necessary). * Returns remaining capacity of queue (sg segments) or a negative error. - * @kick: update after add_buf + * virtqueue_kick: update after add_buf * vq: the struct virtqueue * After one or more add_buf calls, invoke this to kick the other side. - * @get_buf: get the next used buffer + * virtqueue_get_buf: get the next used buffer * vq: the struct virtqueue we're talking about. * len: the length written into the buffer * Returns NULL or the "data" token handed to add_buf. - * @disable_cb: disable callbacks + * virtqueue_disable_cb: disable callbacks * vq: the struct virtqueue we're talking about. * Note that this is not necessarily synchronous, hence unreliable and only * useful as an optimization. - * @enable_cb: restart callbacks after disable_cb. + * virtqueue_enable_cb: restart callbacks after disable_cb. * vq: the struct virtqueue we're talking about. * This re-enables callbacks; it returns "false" if there are pending * buffers in the queue, to detect a possible race between the driver * checking for more work, and enabling callbacks. - * @detach_unused_buf: detach first unused buffer + * virtqueue_detach_unused_buf: detach first unused buffer * vq: the struct virtqueue we're talking about. * Returns NULL or the "data" token handed to add_buf * * Locking rules are straightforward: the driver is responsible for * locking. No two operations may be invoked simultaneously, with the exception - * of @disable_cb. + * of virtqueue_disable_cb. * * All operations can be called in any context. */ -struct virtqueue_ops { - int (*add_buf)(struct virtqueue *vq, - struct scatterlist sg[], - unsigned int out_num, - unsigned int in_num, - void *data); - void (*kick)(struct virtqueue *vq); +int virtqueue_add_buf_gfp(struct virtqueue *vq, + struct scatterlist sg[], + unsigned int out_num, + unsigned int in_num, + void *data, + gfp_t gfp); - void *(*get_buf)(struct virtqueue *vq, unsigned int *len); +static inline int virtqueue_add_buf(struct virtqueue *vq, + struct scatterlist sg[], + unsigned int out_num, + unsigned int in_num, + void *data) +{ + return virtqueue_add_buf_gfp(vq, sg, out_num, in_num, data, GFP_ATOMIC); +} - void (*disable_cb)(struct virtqueue *vq); - bool (*enable_cb)(struct virtqueue *vq); - void *(*detach_unused_buf)(struct virtqueue *vq); -}; +void virtqueue_kick(struct virtqueue *vq); + +void *virtqueue_get_buf(struct virtqueue *vq, unsigned int *len); + +void virtqueue_disable_cb(struct virtqueue *vq); + +bool virtqueue_enable_cb(struct virtqueue *vq); + +void *virtqueue_detach_unused_buf(struct virtqueue *vq); /** * virtio_device - representation of a device using virtio diff --git a/include/linux/virtio_blk.h b/include/linux/virtio_blk.h index e52029e98919..167720d695ed 100644 --- a/include/linux/virtio_blk.h +++ b/include/linux/virtio_blk.h @@ -17,6 +17,8 @@ #define VIRTIO_BLK_F_FLUSH 9 /* Cache flush command support */ #define VIRTIO_BLK_F_TOPOLOGY 10 /* Topology information is available */ +#define VIRTIO_BLK_ID_BYTES 20 /* ID string length */ + struct virtio_blk_config { /* The capacity (in 512-byte sectors). */ __u64 capacity; @@ -67,6 +69,9 @@ struct virtio_blk_config { /* Cache flush command */ #define VIRTIO_BLK_T_FLUSH 4 +/* Get device ID command */ +#define VIRTIO_BLK_T_GET_ID 8 + /* Barrier before this op. */ #define VIRTIO_BLK_T_BARRIER 0x80000000 diff --git a/include/linux/virtio_console.h b/include/linux/virtio_console.h index 92228a8fbcbc..a85064db8f94 100644 --- a/include/linux/virtio_console.h +++ b/include/linux/virtio_console.h @@ -12,14 +12,39 @@ /* Feature bits */ #define VIRTIO_CONSOLE_F_SIZE 0 /* Does host provide console size? */ +#define VIRTIO_CONSOLE_F_MULTIPORT 1 /* Does host provide multiple ports? */ + +#define VIRTIO_CONSOLE_BAD_ID (~(u32)0) struct virtio_console_config { /* colums of the screens */ __u16 cols; /* rows of the screens */ __u16 rows; + /* max. number of ports this device can hold */ + __u32 max_nr_ports; } __attribute__((packed)); +/* + * A message that's passed between the Host and the Guest for a + * particular port. + */ +struct virtio_console_control { + __u32 id; /* Port number */ + __u16 event; /* The kind of control event (see below) */ + __u16 value; /* Extra information for the key */ +}; + +/* Some events for control messages */ +#define VIRTIO_CONSOLE_DEVICE_READY 0 +#define VIRTIO_CONSOLE_PORT_ADD 1 +#define VIRTIO_CONSOLE_PORT_REMOVE 2 +#define VIRTIO_CONSOLE_PORT_READY 3 +#define VIRTIO_CONSOLE_CONSOLE_PORT 4 +#define VIRTIO_CONSOLE_RESIZE 5 +#define VIRTIO_CONSOLE_PORT_OPEN 6 +#define VIRTIO_CONSOLE_PORT_NAME 7 + #ifdef __KERNEL__ int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int)); #endif /* __KERNEL__ */ diff --git a/init/main.c b/init/main.c index 5c8540271529..f3d20418fc80 100644 --- a/init/main.c +++ b/init/main.c @@ -201,11 +201,11 @@ static char * argv_init[MAX_INIT_ARGS+2] = { "init", NULL, }; char * envp_init[MAX_INIT_ENVS+2] = { "HOME=/", "TERM=linux", NULL, }; static const char *panic_later, *panic_param; -extern struct obs_kernel_param __setup_start[], __setup_end[]; +extern const struct obs_kernel_param __setup_start[], __setup_end[]; static int __init obsolete_checksetup(char *line) { - struct obs_kernel_param *p; + const struct obs_kernel_param *p; int had_early_param = 0; p = __setup_start; @@ -451,7 +451,7 @@ static noinline void __init_refok rest_init(void) /* Check for early params. */ static int __init do_early_param(char *param, char *val) { - struct obs_kernel_param *p; + const struct obs_kernel_param *p; for (p = __setup_start; p < __setup_end; p++) { if ((p->early && strcmp(param, p->str) == 0) || @@ -528,7 +528,7 @@ static void __init mm_init(void) asmlinkage void __init start_kernel(void) { char * command_line; - extern struct kernel_param __start___param[], __stop___param[]; + extern const struct kernel_param __start___param[], __stop___param[]; smp_setup_processor_id(); diff --git a/kernel/module.c b/kernel/module.c index 738e2b3ed935..20e69c153fd0 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -560,33 +560,26 @@ int use_module(struct module *a, struct module *b) struct module_use *use; int no_warn, err; - if (b == NULL || already_uses(a, b)) return 1; - - /* If we're interrupted or time out, we fail. */ - if (wait_event_interruptible_timeout( - module_wq, (err = strong_try_module_get(b)) != -EBUSY, - 30 * HZ) <= 0) { - printk("%s: gave up waiting for init of module %s.\n", - a->name, b->name); + if (b == NULL || already_uses(a, b)) return 0; - } - /* If strong_try_module_get() returned a different error, we fail. */ + /* If we're interrupted or time out, we fail. */ + err = strong_try_module_get(b); if (err) - return 0; + return err; DEBUGP("Allocating new usage for %s.\n", a->name); use = kmalloc(sizeof(*use), GFP_ATOMIC); if (!use) { printk("%s: out of memory loading\n", a->name); module_put(b); - return 0; + return -ENOMEM; } use->module_which_uses = a; list_add(&use->list, &b->modules_which_use_me); no_warn = sysfs_create_link(b->holders_dir, &a->mkobj.kobj, a->name); - return 1; + return 0; } EXPORT_SYMBOL_GPL(use_module); @@ -890,7 +883,7 @@ static inline void module_unload_free(struct module *mod) int use_module(struct module *a, struct module *b) { - return strong_try_module_get(b) == 0; + return strong_try_module_get(b); } EXPORT_SYMBOL_GPL(use_module); @@ -1061,17 +1054,39 @@ static const struct kernel_symbol *resolve_symbol(Elf_Shdr *sechdrs, struct module *owner; const struct kernel_symbol *sym; const unsigned long *crc; + DEFINE_WAIT(wait); + int err; + long timeleft = 30 * HZ; +again: sym = find_symbol(name, &owner, &crc, !(mod->taints & (1 << TAINT_PROPRIETARY_MODULE)), true); - /* use_module can fail due to OOM, - or module initialization or unloading */ - if (sym) { - if (!check_version(sechdrs, versindex, name, mod, crc, owner) - || !use_module(mod, owner)) - sym = NULL; - } - return sym; + if (!sym) + return NULL; + + if (!check_version(sechdrs, versindex, name, mod, crc, owner)) + return NULL; + + prepare_to_wait(&module_wq, &wait, TASK_INTERRUPTIBLE); + err = use_module(mod, owner); + if (likely(!err) || err != -EBUSY || signal_pending(current)) { + finish_wait(&module_wq, &wait); + return err ? NULL : sym; + } + + /* Module is still loading. Drop lock and wait. */ + mutex_unlock(&module_mutex); + timeleft = schedule_timeout(timeleft); + mutex_lock(&module_mutex); + finish_wait(&module_wq, &wait); + + /* Module might be gone entirely, or replaced. Re-lookup. */ + if (timeleft) + goto again; + + printk(KERN_WARNING "%s: gave up waiting for init of module %s.\n", + mod->name, owner->name); + return NULL; } /* diff --git a/kernel/params.c b/kernel/params.c index 0b30ecd53a52..08107d181758 100644 --- a/kernel/params.c +++ b/kernel/params.c @@ -31,6 +31,42 @@ #define DEBUGP(fmt, a...) #endif +/* Protects all parameters, and incidentally kmalloced_param list. */ +static DEFINE_MUTEX(param_lock); + +/* This just allows us to keep track of which parameters are kmalloced. */ +struct kmalloced_param { + struct list_head list; + char val[]; +}; +static LIST_HEAD(kmalloced_params); + +static void *kmalloc_parameter(unsigned int size) +{ + struct kmalloced_param *p; + + p = kmalloc(sizeof(*p) + size, GFP_KERNEL); + if (!p) + return NULL; + + list_add(&p->list, &kmalloced_params); + return p->val; +} + +/* Does nothing if parameter wasn't kmalloced above. */ +static void maybe_kfree_parameter(void *param) +{ + struct kmalloced_param *p; + + list_for_each_entry(p, &kmalloced_params, list) { + if (p->val == param) { + list_del(&p->list); + kfree(p); + break; + } + } +} + static inline char dash2underscore(char c) { if (c == '-') @@ -49,18 +85,25 @@ static inline int parameq(const char *input, const char *paramname) static int parse_one(char *param, char *val, - struct kernel_param *params, + const struct kernel_param *params, unsigned num_params, int (*handle_unknown)(char *param, char *val)) { unsigned int i; + int err; /* Find parameter */ for (i = 0; i < num_params; i++) { if (parameq(param, params[i].name)) { + /* Noone handled NULL, so do it here. */ + if (!val && params[i].ops->set != param_set_bool) + return -EINVAL; DEBUGP("They are equal! Calling %p\n", - params[i].set); - return params[i].set(val, ¶ms[i]); + params[i].ops->set); + mutex_lock(¶m_lock); + err = params[i].ops->set(val, ¶ms[i]); + mutex_unlock(¶m_lock); + return err; } } @@ -128,7 +171,7 @@ static char *next_arg(char *args, char **param, char **val) /* Args looks like "foo=bar,bar2 baz=fuz wiz". */ int parse_args(const char *name, char *args, - struct kernel_param *params, + const struct kernel_param *params, unsigned num, int (*unknown)(char *param, char *val)) { @@ -176,22 +219,29 @@ int parse_args(const char *name, /* Lazy bastard, eh? */ #define STANDARD_PARAM_DEF(name, type, format, tmptype, strtolfn) \ - int param_set_##name(const char *val, struct kernel_param *kp) \ + int param_set_##name(const char *val, const struct kernel_param *kp) \ { \ tmptype l; \ int ret; \ \ - if (!val) return -EINVAL; \ ret = strtolfn(val, 0, &l); \ if (ret == -EINVAL || ((type)l != l)) \ return -EINVAL; \ *((type *)kp->arg) = l; \ return 0; \ } \ - int param_get_##name(char *buffer, struct kernel_param *kp) \ + int param_get_##name(char *buffer, const struct kernel_param *kp) \ { \ return sprintf(buffer, format, *((type *)kp->arg)); \ - } + } \ + struct kernel_param_ops param_ops_##name = { \ + .set = param_set_##name, \ + .get = param_get_##name, \ + }; \ + EXPORT_SYMBOL(param_set_##name); \ + EXPORT_SYMBOL(param_get_##name); \ + EXPORT_SYMBOL(param_ops_##name) + STANDARD_PARAM_DEF(byte, unsigned char, "%c", unsigned long, strict_strtoul); STANDARD_PARAM_DEF(short, short, "%hi", long, strict_strtol); @@ -201,39 +251,50 @@ STANDARD_PARAM_DEF(uint, unsigned int, "%u", unsigned long, strict_strtoul); STANDARD_PARAM_DEF(long, long, "%li", long, strict_strtol); STANDARD_PARAM_DEF(ulong, unsigned long, "%lu", unsigned long, strict_strtoul); -int param_set_charp(const char *val, struct kernel_param *kp) +int param_set_charp(const char *val, const struct kernel_param *kp) { - if (!val) { - printk(KERN_ERR "%s: string parameter expected\n", - kp->name); - return -EINVAL; - } - if (strlen(val) > 1024) { printk(KERN_ERR "%s: string parameter too long\n", kp->name); return -ENOSPC; } - /* This is a hack. We can't need to strdup in early boot, and we + maybe_kfree_parameter(*(char **)kp->arg); + + /* This is a hack. We can't kmalloc in early boot, and we * don't need to; this mangled commandline is preserved. */ if (slab_is_available()) { - *(char **)kp->arg = kstrdup(val, GFP_KERNEL); + *(char **)kp->arg = kmalloc_parameter(strlen(val)+1); if (!*(char **)kp->arg) return -ENOMEM; + strcpy(*(char **)kp->arg, val); } else *(const char **)kp->arg = val; return 0; } +EXPORT_SYMBOL(param_set_charp); -int param_get_charp(char *buffer, struct kernel_param *kp) +int param_get_charp(char *buffer, const struct kernel_param *kp) { return sprintf(buffer, "%s", *((char **)kp->arg)); } +EXPORT_SYMBOL(param_get_charp); + +static void param_free_charp(void *arg) +{ + maybe_kfree_parameter(*((char **)arg)); +} + +struct kernel_param_ops param_ops_charp = { + .set = param_set_charp, + .get = param_get_charp, + .free = param_free_charp, +}; +EXPORT_SYMBOL(param_ops_charp); /* Actually could be a bool or an int, for historical reasons. */ -int param_set_bool(const char *val, struct kernel_param *kp) +int param_set_bool(const char *val, const struct kernel_param *kp) { bool v; @@ -258,8 +319,9 @@ int param_set_bool(const char *val, struct kernel_param *kp) *(int *)kp->arg = v; return 0; } +EXPORT_SYMBOL(param_set_bool); -int param_get_bool(char *buffer, struct kernel_param *kp) +int param_get_bool(char *buffer, const struct kernel_param *kp) { bool val; if (kp->flags & KPARAM_ISBOOL) @@ -270,9 +332,16 @@ int param_get_bool(char *buffer, struct kernel_param *kp) /* Y and N chosen as being relatively non-coder friendly */ return sprintf(buffer, "%c", val ? 'Y' : 'N'); } +EXPORT_SYMBOL(param_get_bool); + +struct kernel_param_ops param_ops_bool = { + .set = param_set_bool, + .get = param_get_bool, +}; +EXPORT_SYMBOL(param_ops_bool); /* This one must be bool. */ -int param_set_invbool(const char *val, struct kernel_param *kp) +int param_set_invbool(const char *val, const struct kernel_param *kp) { int ret; bool boolval; @@ -285,18 +354,26 @@ int param_set_invbool(const char *val, struct kernel_param *kp) *(bool *)kp->arg = !boolval; return ret; } +EXPORT_SYMBOL(param_set_invbool); -int param_get_invbool(char *buffer, struct kernel_param *kp) +int param_get_invbool(char *buffer, const struct kernel_param *kp) { return sprintf(buffer, "%c", (*(bool *)kp->arg) ? 'N' : 'Y'); } +EXPORT_SYMBOL(param_get_invbool); + +struct kernel_param_ops param_ops_invbool = { + .set = param_set_invbool, + .get = param_get_invbool, +}; +EXPORT_SYMBOL(param_ops_invbool); /* We break the rule and mangle the string. */ static int param_array(const char *name, const char *val, unsigned int min, unsigned int max, void *elem, int elemsize, - int (*set)(const char *, struct kernel_param *kp), + int (*set)(const char *, const struct kernel_param *kp), u16 flags, unsigned int *num) { @@ -309,12 +386,6 @@ static int param_array(const char *name, kp.arg = elem; kp.flags = flags; - /* No equals sign? */ - if (!val) { - printk(KERN_ERR "%s: expects arguments\n", name); - return -EINVAL; - } - *num = 0; /* We expect a comma-separated list of values. */ do { @@ -330,6 +401,7 @@ static int param_array(const char *name, /* nul-terminate and parse */ save = val[len]; ((char *)val)[len] = '\0'; + BUG_ON(!mutex_is_locked(¶m_lock)); ret = set(val, &kp); if (ret != 0) @@ -347,17 +419,17 @@ static int param_array(const char *name, return 0; } -int param_array_set(const char *val, struct kernel_param *kp) +static int param_array_set(const char *val, const struct kernel_param *kp) { const struct kparam_array *arr = kp->arr; unsigned int temp_num; return param_array(kp->name, val, 1, arr->max, arr->elem, - arr->elemsize, arr->set, kp->flags, + arr->elemsize, arr->ops->set, kp->flags, arr->num ?: &temp_num); } -int param_array_get(char *buffer, struct kernel_param *kp) +static int param_array_get(char *buffer, const struct kernel_param *kp) { int i, off, ret; const struct kparam_array *arr = kp->arr; @@ -368,7 +440,8 @@ int param_array_get(char *buffer, struct kernel_param *kp) if (i) buffer[off++] = ','; p.arg = arr->elem + arr->elemsize * i; - ret = arr->get(buffer + off, &p); + BUG_ON(!mutex_is_locked(¶m_lock)); + ret = arr->ops->get(buffer + off, &p); if (ret < 0) return ret; off += ret; @@ -377,14 +450,27 @@ int param_array_get(char *buffer, struct kernel_param *kp) return off; } -int param_set_copystring(const char *val, struct kernel_param *kp) +static void param_array_free(void *arg) +{ + unsigned int i; + const struct kparam_array *arr = arg; + + if (arr->ops->free) + for (i = 0; i < (arr->num ? *arr->num : arr->max); i++) + arr->ops->free(arr->elem + arr->elemsize * i); +} + +struct kernel_param_ops param_array_ops = { + .set = param_array_set, + .get = param_array_get, + .free = param_array_free, +}; +EXPORT_SYMBOL(param_array_ops); + +int param_set_copystring(const char *val, const struct kernel_param *kp) { const struct kparam_string *kps = kp->str; - if (!val) { - printk(KERN_ERR "%s: missing param set value\n", kp->name); - return -EINVAL; - } if (strlen(val)+1 > kps->maxlen) { printk(KERN_ERR "%s: string doesn't fit in %u chars.\n", kp->name, kps->maxlen-1); @@ -393,12 +479,20 @@ int param_set_copystring(const char *val, struct kernel_param *kp) strcpy(kps->string, val); return 0; } +EXPORT_SYMBOL(param_set_copystring); -int param_get_string(char *buffer, struct kernel_param *kp) +int param_get_string(char *buffer, const struct kernel_param *kp) { const struct kparam_string *kps = kp->str; return strlcpy(buffer, kps->string, kps->maxlen); } +EXPORT_SYMBOL(param_get_string); + +struct kernel_param_ops param_ops_string = { + .set = param_set_copystring, + .get = param_get_string, +}; +EXPORT_SYMBOL(param_ops_string); /* sysfs output in /sys/modules/XYZ/parameters/ */ #define to_module_attr(n) container_of(n, struct module_attribute, attr) @@ -409,7 +503,7 @@ extern struct kernel_param __start___param[], __stop___param[]; struct param_attribute { struct module_attribute mattr; - struct kernel_param *param; + const struct kernel_param *param; }; struct module_param_attrs @@ -428,10 +522,12 @@ static ssize_t param_attr_show(struct module_attribute *mattr, int count; struct param_attribute *attribute = to_param_attr(mattr); - if (!attribute->param->get) + if (!attribute->param->ops->get) return -EPERM; - count = attribute->param->get(buf, attribute->param); + mutex_lock(¶m_lock); + count = attribute->param->ops->get(buf, attribute->param); + mutex_unlock(¶m_lock); if (count > 0) { strcat(buf, "\n"); ++count; @@ -447,10 +543,12 @@ static ssize_t param_attr_store(struct module_attribute *mattr, int err; struct param_attribute *attribute = to_param_attr(mattr); - if (!attribute->param->set) + if (!attribute->param->ops->set) return -EPERM; - err = attribute->param->set(buf, attribute->param); + mutex_lock(¶m_lock); + err = attribute->param->ops->set(buf, attribute->param); + mutex_unlock(¶m_lock); if (!err) return len; return err; @@ -464,6 +562,18 @@ static ssize_t param_attr_store(struct module_attribute *mattr, #endif #ifdef CONFIG_SYSFS +void __kernel_param_lock(void) +{ + mutex_lock(¶m_lock); +} +EXPORT_SYMBOL(__kernel_param_lock); + +void __kernel_param_unlock(void) +{ + mutex_unlock(¶m_lock); +} +EXPORT_SYMBOL(__kernel_param_unlock); + /* * add_sysfs_param - add a parameter to sysfs * @mk: struct module_kobject @@ -475,7 +585,7 @@ static ssize_t param_attr_store(struct module_attribute *mattr, * if there's an error. */ static __modinit int add_sysfs_param(struct module_kobject *mk, - struct kernel_param *kp, + const struct kernel_param *kp, const char *name) { struct module_param_attrs *new; @@ -557,7 +667,7 @@ static void free_module_param_attrs(struct module_kobject *mk) * /sys/module/[mod->name]/parameters/ */ int module_param_sysfs_setup(struct module *mod, - struct kernel_param *kparam, + const struct kernel_param *kparam, unsigned int num_params) { int i, err; @@ -602,7 +712,11 @@ void module_param_sysfs_remove(struct module *mod) void destroy_params(const struct kernel_param *params, unsigned num) { - /* FIXME: This should free kmalloced charp parameters. It doesn't. */ + unsigned int i; + + for (i = 0; i < num; i++) + if (params[i].ops->free) + params[i].ops->free(params[i].arg); } static void __init kernel_add_sysfs_param(const char *name, @@ -768,28 +882,3 @@ static int __init param_sysfs_init(void) subsys_initcall(param_sysfs_init); #endif /* CONFIG_SYSFS */ - -EXPORT_SYMBOL(param_set_byte); -EXPORT_SYMBOL(param_get_byte); -EXPORT_SYMBOL(param_set_short); -EXPORT_SYMBOL(param_get_short); -EXPORT_SYMBOL(param_set_ushort); -EXPORT_SYMBOL(param_get_ushort); -EXPORT_SYMBOL(param_set_int); -EXPORT_SYMBOL(param_get_int); -EXPORT_SYMBOL(param_set_uint); -EXPORT_SYMBOL(param_get_uint); -EXPORT_SYMBOL(param_set_long); -EXPORT_SYMBOL(param_get_long); -EXPORT_SYMBOL(param_set_ulong); -EXPORT_SYMBOL(param_get_ulong); -EXPORT_SYMBOL(param_set_charp); -EXPORT_SYMBOL(param_get_charp); -EXPORT_SYMBOL(param_set_bool); -EXPORT_SYMBOL(param_get_bool); -EXPORT_SYMBOL(param_set_invbool); -EXPORT_SYMBOL(param_get_invbool); -EXPORT_SYMBOL(param_array_set); -EXPORT_SYMBOL(param_array_get); -EXPORT_SYMBOL(param_set_copystring); -EXPORT_SYMBOL(param_get_string); diff --git a/net/9p/trans_virtio.c b/net/9p/trans_virtio.c index 7eb78ecc1618..dcfbe99ff81c 100644 --- a/net/9p/trans_virtio.c +++ b/net/9p/trans_virtio.c @@ -137,7 +137,7 @@ static void req_done(struct virtqueue *vq) P9_DPRINTK(P9_DEBUG_TRANS, ": request done\n"); - while ((rc = chan->vq->vq_ops->get_buf(chan->vq, &len)) != NULL) { + while ((rc = virtqueue_get_buf(chan->vq, &len)) != NULL) { P9_DPRINTK(P9_DEBUG_TRANS, ": rc %p\n", rc); P9_DPRINTK(P9_DEBUG_TRANS, ": lookup tag %d\n", rc->tag); req = p9_tag_lookup(chan->client, rc->tag); @@ -209,13 +209,13 @@ p9_virtio_request(struct p9_client *client, struct p9_req_t *req) req->status = REQ_STATUS_SENT; - if (chan->vq->vq_ops->add_buf(chan->vq, chan->sg, out, in, req->tc) < 0) { + if (virtqueue_add_buf(chan->vq, chan->sg, out, in, req->tc) < 0) { P9_DPRINTK(P9_DEBUG_TRANS, "9p debug: virtio rpc add_buf returned failure"); return -EIO; } - chan->vq->vq_ops->kick(chan->vq); + virtqueue_kick(chan->vq); P9_DPRINTK(P9_DEBUG_TRANS, "9p debug: virtio request kicked\n"); return 0; diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c index 6d0bd198af19..be04d46110fe 100644 --- a/net/mac80211/rate.c +++ b/net/mac80211/rate.c @@ -103,6 +103,7 @@ ieee80211_rate_control_ops_get(const char *name) struct rate_control_ops *ops; const char *alg_name; + kparam_block_sysfs_write(ieee80211_default_rc_algo); if (!name) alg_name = ieee80211_default_rc_algo; else @@ -120,6 +121,7 @@ ieee80211_rate_control_ops_get(const char *name) /* try built-in one if specific alg requested but not found */ if (!ops && strlen(CONFIG_MAC80211_RC_DEFAULT)) ops = ieee80211_try_rate_control_ops_get(CONFIG_MAC80211_RC_DEFAULT); + kparam_unblock_sysfs_write(ieee80211_default_rc_algo); return ops; } diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index beefa7a3a90e..756a836187e3 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -2568,7 +2568,8 @@ void cleanup_socket_xprt(void) xprt_unregister_transport(&xs_bc_tcp_transport); } -static int param_set_uint_minmax(const char *val, struct kernel_param *kp, +static int param_set_uint_minmax(const char *val, + const struct kernel_param *kp, unsigned int min, unsigned int max) { unsigned long num; @@ -2583,34 +2584,37 @@ static int param_set_uint_minmax(const char *val, struct kernel_param *kp, return 0; } -static int param_set_portnr(const char *val, struct kernel_param *kp) +static int param_set_portnr(const char *val, const struct kernel_param *kp) { return param_set_uint_minmax(val, kp, RPC_MIN_RESVPORT, RPC_MAX_RESVPORT); } -static int param_get_portnr(char *buffer, struct kernel_param *kp) -{ - return param_get_uint(buffer, kp); -} +static struct kernel_param_ops param_ops_portnr = { + .set = param_set_portnr, + .get = param_get_uint, +}; + #define param_check_portnr(name, p) \ __param_check(name, p, unsigned int); module_param_named(min_resvport, xprt_min_resvport, portnr, 0644); module_param_named(max_resvport, xprt_max_resvport, portnr, 0644); -static int param_set_slot_table_size(const char *val, struct kernel_param *kp) +static int param_set_slot_table_size(const char *val, + const struct kernel_param *kp) { return param_set_uint_minmax(val, kp, RPC_MIN_SLOT_TABLE, RPC_MAX_SLOT_TABLE); } -static int param_get_slot_table_size(char *buffer, struct kernel_param *kp) -{ - return param_get_uint(buffer, kp); -} +static struct kernel_param_ops param_ops_slot_table_size = { + .set = param_set_slot_table_size, + .get = param_get_uint, +}; + #define param_check_slot_table_size(name, p) \ __param_check(name, p, unsigned int); diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index 36a60a853173..003ccd72601d 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -818,6 +818,19 @@ static int do_mdio_entry(const char *filename, return 1; } +/* looks like: "pnp:dD" */ +static int do_isapnp_entry(const char *filename, + struct isapnp_device_id *id, char *alias) +{ + sprintf(alias, "pnp:d%c%c%c%x%x%x%x*", + 'A' + ((id->vendor >> 2) & 0x3f) - 1, + 'A' + (((id->vendor & 3) << 3) | ((id->vendor >> 13) & 7)) - 1, + 'A' + ((id->vendor >> 8) & 0x1f) - 1, + (id->function >> 4) & 0x0f, id->function & 0x0f, + (id->function >> 12) & 0x0f, (id->function >> 8) & 0x0f); + return 1; +} + /* Ignore any prefix, eg. some architectures prepend _ */ static inline int sym_is(const char *symbol, const char *name) { @@ -969,6 +982,10 @@ void handle_moddevtable(struct module *mod, struct elf_info *info, do_table(symval, sym->st_size, sizeof(struct mdio_device_id), "mdio", do_mdio_entry, mod); + else if (sym_is(symname, "__mod_isapnp_device_table")) + do_table(symval, sym->st_size, + sizeof(struct isapnp_device_id), "isa", + do_isapnp_entry, mod); free(zeros); } diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 3318692e4e76..a5d1457cf239 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -981,6 +981,13 @@ static const struct sectioncheck *section_mismatch( * fromsec = .data* * atsym =__param* * + * Pattern 1a: + * module_param_call() ops can refer to __init set function if permissions=0 + * The pattern is identified by: + * tosec = .init.text + * fromsec = .data* + * atsym = __param_ops_* + * * Pattern 2: * Many drivers utilise a *driver container with references to * add, remove, probe functions etc. @@ -1015,6 +1022,12 @@ static int secref_whitelist(const struct sectioncheck *mismatch, (strncmp(fromsym, "__param", strlen("__param")) == 0)) return 0; + /* Check for pattern 1a */ + if (strcmp(tosec, ".init.text") == 0 && + match(fromsec, data_sections) && + (strncmp(fromsym, "__param_ops_", strlen("__param_ops_")) == 0)) + return 0; + /* Check for pattern 2 */ if (match(tosec, init_exit_sections) && match(fromsec, data_sections) && |