hvcs.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,667 行 · 第 1/4 页

C
1,667
字号
/* * IBM eServer Hypervisor Virtual Console Server Device Driver * Copyright (C) 2003, 2004 IBM Corp. *  Ryan S. Arnold (rsa@us.ibm.com) * *  This program is free software; you can redistribute it and/or modify *  it under the terms of the GNU General Public License as published by *  the Free Software Foundation; either version 2 of the License, or *  (at your option) any later version. * *  This program is distributed in the hope that it will be useful, *  but WITHOUT ANY WARRANTY; without even the implied warranty of *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the *  GNU General Public License for more details. * *  You should have received a copy of the GNU General Public License *  along with this program; if not, write to the Free Software *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA * * Author(s) :  Ryan S. Arnold <rsa@us.ibm.com> * * This is the device driver for the IBM Hypervisor Virtual Console Server, * "hvcs".  The IBM hvcs provides a tty driver interface to allow Linux * user space applications access to the system consoles of logically * partitioned operating systems, e.g. Linux, running on the same partitioned * Power5 ppc64 system.  Physical hardware consoles per partition are not * practical on this hardware so system consoles are accessed by this driver * using inter-partition firmware interfaces to virtual terminal devices. * * A vty is known to the HMC as a "virtual serial server adapter".  It is a * virtual terminal device that is created by firmware upon partition creation * to act as a partitioned OS's console device. * * Firmware dynamically (via hotplug) exposes vty-servers to a running ppc64 * Linux system upon their creation by the HMC or their exposure during boot. * The non-user interactive backend of this driver is implemented as a vio * device driver so that it can receive notification of vty-server lifetimes * after it registers with the vio bus to handle vty-server probe and remove * callbacks. * * Many vty-servers can be configured to connect to one vty, but a vty can * only be actively connected to by a single vty-server, in any manner, at one * time.  If the HMC is currently hosting the console for a target Linux * partition; attempts to open the tty device to the partition's console using * the hvcs on any partition will return -EBUSY with every open attempt until * the HMC frees the connection between its vty-server and the desired * partition's vty device.  Conversely, a vty-server may only be connected to * a single vty at one time even though it may have several configured vty * partner possibilities. * * Firmware does not provide notification of vty partner changes to this * driver.  This means that an HMC Super Admin may add or remove partner vtys * from a vty-server's partner list but the changes will not be signaled to * the vty-server.  Firmware only notifies the driver when a vty-server is * added or removed from the system.  To compensate for this deficiency, this * driver implements a sysfs update attribute which provides a method for * rescanning partner information upon a user's request. * * Each vty-server, prior to being exposed to this driver is reference counted * using the 2.6 Linux kernel kobject construct.  This kobject is also used by * the vio bus to provide a vio device sysfs entry that this driver attaches * device specific attributes to, including partner information.  The vio bus * framework also provides a sysfs entry for each vio driver.  The hvcs driver * provides driver attributes in this entry. * * For direction on installation and usage of this driver please reference * Documentation/powerpc/hvcs.txt. */#include <linux/device.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/kernel.h>#include <linux/kobject.h>#include <linux/kthread.h>#include <linux/list.h>#include <linux/major.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/sched.h>#include <linux/spinlock.h>#include <linux/stat.h>#include <linux/tty.h>#include <linux/tty_flip.h>#include <asm/hvconsole.h>#include <asm/hvcserver.h>#include <asm/uaccess.h>#include <asm/vio.h>/* * 1.3.0 -> 1.3.1 In hvcs_open memset(..,0x00,..) instead of memset(..,0x3F,00). * Removed braces around single statements following conditionals.  Removed '= * 0' after static int declarations since these default to zero.  Removed * list_for_each_safe() and replaced with list_for_each_entry() in * hvcs_get_by_index().  The 'safe' version is un-needed now that the driver is * using spinlocks.  Changed spin_lock_irqsave() to spin_lock() when locking * hvcs_structs_lock and hvcs_pi_lock since these are not touched in an int * handler.  Initialized hvcs_structs_lock and hvcs_pi_lock to * SPIN_LOCK_UNLOCKED at declaration time rather than in hvcs_module_init(). * Added spin_lock around list_del() in destroy_hvcs_struct() to protect the * list traversals from a deletion.  Removed '= NULL' from pointer declaration * statements since they are initialized NULL by default.  Removed wmb() * instances from hvcs_try_write().  They probably aren't needed with locking in * place.  Added check and cleanup for hvcs_pi_buff = kmalloc() in * hvcs_module_init().  Exposed hvcs_struct.index via a sysfs attribute so that * the coupling between /dev/hvcs* and a vty-server can be automatically * determined.  Moved kobject_put() in hvcs_open outside of the * spin_unlock_irqrestore(). * * 1.3.1 -> 1.3.2 Changed method for determining hvcs_struct->index and had it * align with how the tty layer always assigns the lowest index available.  This * change resulted in a list of ints that denotes which indexes are available. * Device additions and removals use the new hvcs_get_index() and * hvcs_return_index() helper functions.  The list is created with * hvsc_alloc_index_list() and it is destroyed with hvcs_free_index_list(). * Without these fixes hotplug vty-server adapter support goes crazy with this * driver if the user removes a vty-server adapter.  Moved free_irq() outside of * the hvcs_final_close() function in order to get it out of the spinlock. * Rearranged hvcs_close().  Cleaned up some printks and did some housekeeping * on the changelog.  Removed local CLC_LENGTH and used HVCS_CLC_LENGTH from * arch/ppc64/hvcserver.h. * * 1.3.2 -> 1.3.3 Replaced yield() in hvcs_close() with tty_wait_until_sent() to * prevent possible lockup with realtime scheduling as similarily pointed out by * akpm in hvc_console.  Changed resulted in the removal of hvcs_final_close() * to reorder cleanup operations and prevent discarding of pending data during * an hvcs_close().  Removed spinlock protection of hvcs_struct data members in * hvcs_write_room() and hvcs_chars_in_buffer() because they aren't needed. */#define HVCS_DRIVER_VERSION "1.3.3"MODULE_AUTHOR("Ryan S. Arnold <rsa@us.ibm.com>");MODULE_DESCRIPTION("IBM hvcs (Hypervisor Virtual Console Server) Driver");MODULE_LICENSE("GPL");MODULE_VERSION(HVCS_DRIVER_VERSION);/* * Wait this long per iteration while trying to push buffered data to the * hypervisor before allowing the tty to complete a close operation. */#define HVCS_CLOSE_WAIT (HZ/100) /* 1/10 of a second *//* * Since the Linux TTY code does not currently (2-04-2004) support dynamic * addition of tty derived devices and we shouldn't allocate thousands of * tty_device pointers when the number of vty-server & vty partner connections * will most often be much lower than this, we'll arbitrarily allocate * HVCS_DEFAULT_SERVER_ADAPTERS tty_structs and cdev's by default when we * register the tty_driver. This can be overridden using an insmod parameter. */#define HVCS_DEFAULT_SERVER_ADAPTERS	64/* * The user can't insmod with more than HVCS_MAX_SERVER_ADAPTERS hvcs device * nodes as a sanity check.  Theoretically there can be over 1 Billion * vty-server & vty partner connections. */#define HVCS_MAX_SERVER_ADAPTERS	1024/* * We let Linux assign us a major number and we start the minors at zero.  There * is no intuitive mapping between minor number and the target vty-server * adapter except that each new vty-server adapter is always assigned to the * smallest minor number available. */#define HVCS_MINOR_START	0/* * The hcall interface involves putting 8 chars into each of two registers. * We load up those 2 registers (in arch/ppc64/hvconsole.c) by casting char[16] * to long[2].  It would work without __ALIGNED__, but a little (tiny) bit * slower because an unaligned load is slower than aligned load. */#define __ALIGNED__	__attribute__((__aligned__(8)))/* * How much data can firmware send with each hvc_put_chars()?  Maybe this * should be moved into an architecture specific area. */#define HVCS_BUFF_LEN	16/* * This is the maximum amount of data we'll let the user send us (hvcs_write) at * once in a chunk as a sanity check. */#define HVCS_MAX_FROM_USER	4096/* * Be careful when adding flags to this line discipline.  Don't add anything * that will cause echoing or we'll go into recursive loop echoing chars back * and forth with the console drivers. */static struct termios hvcs_tty_termios = {	.c_iflag = IGNBRK | IGNPAR,	.c_oflag = OPOST,	.c_cflag = B38400 | CS8 | CREAD | HUPCL,	.c_cc = INIT_C_CC};/* * This value is used to take the place of a command line parameter when the * module is inserted.  It starts as -1 and stays as such if the user doesn't * specify a module insmod parameter.  If they DO specify one then it is set to * the value of the integer passed in. */static int hvcs_parm_num_devs = -1;module_param(hvcs_parm_num_devs, int, 0);char hvcs_driver_name[] = "hvcs";char hvcs_device_node[] = "hvcs";char hvcs_driver_string[]	= "IBM hvcs (Hypervisor Virtual Console Server) Driver";/* Status of partner info rescan triggered via sysfs. */static int hvcs_rescan_status;static struct tty_driver *hvcs_tty_driver;/* * In order to be somewhat sane this driver always associates the hvcs_struct * index element with the numerically equal tty->index.  This means that a * hotplugged vty-server adapter will always map to the lowest index valued * device node.  If vty-servers were hotplug removed from the system and then * new ones added the new vty-server may have the largest slot number of all * the vty-server adapters in the partition but it may have the lowest dev node * index of all the adapters due to the hole left by the hotplug removed * adapter.  There are a set of functions provided to get the lowest index for * a new device as well as return the index to the list.  This list is allocated * with a number of elements equal to the number of device nodes requested when * the module was inserted. */static int *hvcs_index_list;/* * How large is the list?  This is kept for traversal since the list is * dynamically created. */static int hvcs_index_count;/* * Used by the khvcsd to pick up I/O operations when the kernel_thread is * already awake but potentially shifted to TASK_INTERRUPTIBLE state. */static int hvcs_kicked;/* * Use by the kthread construct for task operations like waking the sleeping * thread and stopping the kthread. */static struct task_struct *hvcs_task;/* * We allocate this for the use of all of the hvcs_structs when they fetch * partner info. */static unsigned long *hvcs_pi_buff;/* Only allow one hvcs_struct to use the hvcs_pi_buff at a time. */static spinlock_t hvcs_pi_lock = SPIN_LOCK_UNLOCKED;/* One vty-server per hvcs_struct */struct hvcs_struct {	spinlock_t lock;	/*	 * This index identifies this hvcs device as the complement to a	 * specific tty index.	 */	unsigned int index;	struct tty_struct *tty;	unsigned int open_count;	/*	 * Used to tell the driver kernel_thread what operations need to take	 * place upon this hvcs_struct instance.	 */	int todo_mask;	/*	 * This buffer is required so that when hvcs_write_room() reports that	 * it can send HVCS_BUFF_LEN characters that it will buffer the full	 * HVCS_BUFF_LEN characters if need be.  This is essential for opost	 * writes since they do not do high level buffering and expect to be	 * able to send what the driver commits to sending buffering	 * [e.g. tab to space conversions in n_tty.c opost()].	 */	char buffer[HVCS_BUFF_LEN];	int chars_in_buffer;	/*	 * Any variable below the kobject is valid before a tty is connected and	 * stays valid after the tty is disconnected.  These shouldn't be	 * whacked until the koject refcount reaches zero though some entries	 * may be changed via sysfs initiatives.	 */	struct kobject kobj; /* ref count & hvcs_struct lifetime */	int connected; /* is the vty-server currently connected to a vty? */	uint32_t p_unit_address; /* partner unit address */	uint32_t p_partition_ID; /* partner partition ID */	char p_location_code[HVCS_CLC_LENGTH + 1]; /* CLC + Null Term */	struct list_head next; /* list management */	struct vio_dev *vdev;};/* Required to back map a kobject to its containing object */#define from_kobj(kobj) container_of(kobj, struct hvcs_struct, kobj)static struct list_head hvcs_structs = LIST_HEAD_INIT(hvcs_structs);static spinlock_t hvcs_structs_lock = SPIN_LOCK_UNLOCKED;static void hvcs_unthrottle(struct tty_struct *tty);static void hvcs_throttle(struct tty_struct *tty);static irqreturn_t hvcs_handle_interrupt(int irq, void *dev_instance,		struct pt_regs *regs);static int hvcs_write(struct tty_struct *tty, int from_user,		const unsigned char *buf, int count);static int hvcs_write_room(struct tty_struct *tty);static int hvcs_chars_in_buffer(struct tty_struct *tty);static int hvcs_has_pi(struct hvcs_struct *hvcsd);static void hvcs_set_pi(struct hvcs_partner_info *pi,		struct hvcs_struct *hvcsd);static int hvcs_get_pi(struct hvcs_struct *hvcsd);static int hvcs_rescan_devices_list(void);static int hvcs_partner_connect(struct hvcs_struct *hvcsd);static void hvcs_partner_free(struct hvcs_struct *hvcsd);static int hvcs_enable_device(struct hvcs_struct *hvcsd,		uint32_t unit_address, unsigned int irq, struct vio_dev *dev);static void destroy_hvcs_struct(struct kobject *kobj);static int hvcs_open(struct tty_struct *tty, struct file *filp);static void hvcs_close(struct tty_struct *tty, struct file *filp);static void hvcs_hangup(struct tty_struct * tty);static void hvcs_create_device_attrs(struct hvcs_struct *hvcsd);static void hvcs_remove_device_attrs(struct vio_dev *vdev);static void hvcs_create_driver_attrs(void);static void hvcs_remove_driver_attrs(void);static int __devinit hvcs_probe(struct vio_dev *dev,		const struct vio_device_id *id);static int __devexit hvcs_remove(struct vio_dev *dev);static int __init hvcs_module_init(void);static void __exit hvcs_module_exit(void);#define HVCS_SCHED_READ	0x00000001#define HVCS_QUICK_READ	0x00000002#define HVCS_TRY_WRITE	0x00000004#define HVCS_READ_MASK	(HVCS_SCHED_READ | HVCS_QUICK_READ)static void hvcs_kick(void){	hvcs_kicked = 1;	wmb();	wake_up_process(hvcs_task);}static void hvcs_unthrottle(struct tty_struct *tty){	struct hvcs_struct *hvcsd = tty->driver_data;	unsigned long flags;	spin_lock_irqsave(&hvcsd->lock, flags);	hvcsd->todo_mask |= HVCS_SCHED_READ;	spin_unlock_irqrestore(&hvcsd->lock, flags);	hvcs_kick();}static void hvcs_throttle(struct tty_struct *tty){	struct hvcs_struct *hvcsd = tty->driver_data;	unsigned long flags;	spin_lock_irqsave(&hvcsd->lock, flags);	vio_disable_interrupts(hvcsd->vdev);	spin_unlock_irqrestore(&hvcsd->lock, flags);}/* * If the device is being removed we don't have to worry about this interrupt * handler taking any further interrupts because they are disabled which means * the hvcs_struct will always be valid in this handler. */static irqreturn_t hvcs_handle_interrupt(int irq, void *dev_instance,		struct pt_regs *regs){	struct hvcs_struct *hvcsd = dev_instance;	spin_lock(&hvcsd->lock);	vio_disable_interrupts(hvcsd->vdev);	hvcsd->todo_mask |= HVCS_SCHED_READ;	spin_unlock(&hvcsd->lock);	hvcs_kick();	return IRQ_HANDLED;}/* This function must be called with the hvcsd->lock held */static void hvcs_try_write(struct hvcs_struct *hvcsd){	uint32_t unit_address = hvcsd->vdev->unit_address;	struct tty_struct *tty = hvcsd->tty;	int sent;	if (hvcsd->todo_mask & HVCS_TRY_WRITE) {		/* won't send partial writes */		sent = hvc_put_chars(unit_address,				&hvcsd->buffer[0],				hvcsd->chars_in_buffer );		if (sent > 0) {			hvcsd->chars_in_buffer = 0;			/* wmb(); */

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?