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 + -
显示快捷键?