Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[linux-2.6.git] / drivers / char / viocons.c
index df9d4de..4e53603 100644 (file)
@@ -26,7 +26,6 @@
  * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 #include <linux/config.h>
-#include <linux/version.h>
 #include <linux/kernel.h>
 #include <linux/proc_fs.h>
 #include <linux/errno.h>
 #include <linux/tty_flip.h>
 #include <linux/sysrq.h>
 
-#include <asm/iSeries/vio.h>
+#include <asm/iseries/vio.h>
 
-#include <asm/iSeries/HvLpEvent.h>
-#include <asm/iSeries/HvCallEvent.h>
-#include <asm/iSeries/HvLpConfig.h>
-#include <asm/iSeries/HvCall.h>
+#include <asm/iseries/hv_lp_event.h>
+#include <asm/iseries/hv_call_event.h>
+#include <asm/iseries/hv_lp_config.h>
+#include <asm/iseries/hv_call.h>
 
 #ifdef CONFIG_VT
 #error You must turn off CONFIG_VT to use CONFIG_VIOCONS
@@ -61,8 +60,8 @@
 #define VIOCONS_KERN_WARN      KERN_WARNING "viocons: "
 #define VIOCONS_KERN_INFO      KERN_INFO "viocons: "
 
-static spinlock_t consolelock = SPIN_LOCK_UNLOCKED;
-static spinlock_t consoleloglock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(consolelock);
+static DEFINE_SPINLOCK(consoleloglock);
 
 #ifdef CONFIG_MAGIC_SYSRQ
 static int vio_sysrq_pressed;
@@ -83,15 +82,6 @@ struct viocharlpevent {
        u8 data[VIOCHAR_MAX_DATA];
 };
 
-/*
- * This is a place where we handle the distribution of memory
- * for copy_from_user() calls.  The buffer_available array is to
- * help us determine which buffer to use.
- */
-#define VIOCHAR_NUM_CFU_BUFFERS        7
-static struct viocharlpevent viocons_cfu_buffer[VIOCHAR_NUM_CFU_BUFFERS];
-static atomic_t viocons_cfu_buffer_available[VIOCHAR_NUM_CFU_BUFFERS];
-
 #define VIOCHAR_WINDOW         10
 #define VIOCHAR_HIGHWATERMARK  3
 
@@ -141,7 +131,7 @@ static void initDataEvent(struct viocharlpevent *viochar, HvLpIndex lp);
 
 static struct tty_driver *viotty_driver;
 
-void hvlog(char *fmt, ...)
+static void hvlog(char *fmt, ...)
 {
        int i;
        unsigned long flags;
@@ -157,7 +147,7 @@ void hvlog(char *fmt, ...)
        spin_unlock_irqrestore(&consoleloglock, flags);
 }
 
-void hvlogOutput(const char *buf, int count)
+static void hvlogOutput(const char *buf, int count)
 {
        unsigned long flags;
        int begin;
@@ -206,50 +196,6 @@ static inline int viotty_paranoia_check(struct port_info *pi,
        return 0;
 }
 
-/*
- * This function should ONLY be called once from viocons_init2
- */
-static void viocons_init_cfu_buffer(void)
-{
-       int i;
-
-       for (i = 1; i < VIOCHAR_NUM_CFU_BUFFERS; i++)
-               atomic_set(&viocons_cfu_buffer_available[i], 1);
-}
-
-static struct viocharlpevent *viocons_get_cfu_buffer(void)
-{
-       int i;
-
-       /*
-        * Grab the first available buffer.  It doesn't matter if we
-        * are interrupted during this array traversal as long as we
-        * get an available space.
-        */
-       for (i = 0; i < VIOCHAR_NUM_CFU_BUFFERS; i++)
-               if (atomic_dec_if_positive(&viocons_cfu_buffer_available[i])
-                               == 0 )
-                       return &viocons_cfu_buffer[i];
-       hvlog("\n\rviocons: viocons_get_cfu_buffer : no free buffers found");
-       return NULL;
-}
-
-static void viocons_free_cfu_buffer(struct viocharlpevent *buffer)
-{
-       int i;
-
-       i = buffer - &viocons_cfu_buffer[0];
-       if (i >= (sizeof(viocons_cfu_buffer) / sizeof(viocons_cfu_buffer[0]))) {
-               hvlog("\n\rviocons: viocons_free_cfu_buffer : buffer pointer not found in list.");
-               return;
-       }
-       if (atomic_read(&viocons_cfu_buffer_available[i]) != 0) {
-               hvlog("\n\rviocons: WARNING : returning unallocated cfu buffer.");
-               return;
-       }
-       atomic_set(&viocons_cfu_buffer_available[i], 1);
-}
-
 /*
  * Add data to our pending-send buffers.  
  *
@@ -422,10 +368,7 @@ static void send_buffers(struct port_info *pi)
                        pi->overflowMessage = 0;
 
                if (pi->tty) {
-                       if ((pi->tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
-                           (pi->tty->ldisc.write_wakeup))
-                               (pi->tty->ldisc.write_wakeup)(pi->tty);
-                       wake_up_interruptible(&pi->tty->write_wait);
+                       tty_wakeup(pi->tty);
                }
        }
 
@@ -441,15 +384,14 @@ static void send_buffers(struct port_info *pi)
  * NOTE: Don't use printk in here because it gets nastily recursive.  hvlog
  * can be used to log to the hypervisor buffer
  */
-static int internal_write(struct port_info *pi, const char *buf,
-                         size_t len, struct viocharlpevent *viochar)
+static int internal_write(struct port_info *pi, const char *buf, size_t len)
 {
        HvLpEvent_Rc hvrc;
        size_t bleft;
        size_t curlen;
        const char *curbuf;
        unsigned long flags;
-       int copy_needed = (viochar == NULL);
+       struct viocharlpevent *viochar;
 
        /*
         * Write to the hvlog of inbound data are now done prior to
@@ -465,25 +407,13 @@ static int internal_write(struct port_info *pi, const char *buf,
 
        spin_lock_irqsave(&consolelock, flags);
 
-       /*
-        * If the internal_write() was passed a pointer to a
-        * viocharlpevent then we don't need to allocate a new one
-        * (this is the case where we are internal_writing user space
-        * data).  If we aren't writing user space data then we need
-        * to get an event from viopath.
-        */
-       if (copy_needed) {
-               /* This one is fetched from the viopath data structure */
-               viochar = (struct viocharlpevent *)
-                       vio_get_event_buffer(viomajorsubtype_chario);
-               /* Make sure we got a buffer */
-               if (viochar == NULL) {
-                       spin_unlock_irqrestore(&consolelock, flags);
-                       hvlog("\n\rviocons: Can't get viochar buffer in internal_write().");
-                       return -EAGAIN;
-               }
-               initDataEvent(viochar, pi->lp);
+       viochar = vio_get_event_buffer(viomajorsubtype_chario);
+       if (viochar == NULL) {
+               spin_unlock_irqrestore(&consolelock, flags);
+               hvlog("\n\rviocons: Can't get vio buffer in internal_write().");
+               return -EAGAIN;
        }
+       initDataEvent(viochar, pi->lp);
 
        curbuf = buf;
        bleft = len;
@@ -496,25 +426,16 @@ static int internal_write(struct port_info *pi, const char *buf,
                        curlen = bleft;
 
                viochar->event.xCorrelationToken = pi->seq++;
-
-               if (copy_needed) {
-                       memcpy(viochar->data, curbuf, curlen);
-                       viochar->len = curlen;
-               }
-
+               memcpy(viochar->data, curbuf, curlen);
+               viochar->len = curlen;
                viochar->event.xSizeMinus1 =
                    offsetof(struct viocharlpevent, data) + curlen;
 
                hvrc = HvCallEvent_signalLpEvent(&viochar->event);
                if (hvrc) {
-                       spin_unlock_irqrestore(&consolelock, flags);
-                       if (copy_needed)
-                               vio_free_event_buffer(viomajorsubtype_chario, viochar);
-
                        hvlog("viocons: error sending event! %d\n", (int)hvrc);
-                       return len - bleft;
+                       goto out;
                }
-
                curbuf += curlen;
                bleft -= curlen;
        }
@@ -522,14 +443,9 @@ static int internal_write(struct port_info *pi, const char *buf,
        /* If we didn't send it all, buffer as much of it as we can. */
        if (bleft > 0)
                bleft -= buffer_add(pi, curbuf, bleft);
-       /*
-        * Since we grabbed it from the viopath data structure, return
-        * it to the data structure.
-        */
-       if (copy_needed)
-               vio_free_event_buffer(viomajorsubtype_chario, viochar);
+out:
+       vio_free_event_buffer(viomajorsubtype_chario, viochar);
        spin_unlock_irqrestore(&consolelock, flags);
-
        return len - bleft;
 }
 
@@ -560,19 +476,19 @@ static struct port_info *get_port_data(struct tty_struct *tty)
  */
 static void initDataEvent(struct viocharlpevent *viochar, HvLpIndex lp)
 {
+       struct HvLpEvent *hev = &viochar->event;
+
        memset(viochar, 0, sizeof(struct viocharlpevent));
 
-       viochar->event.xFlags.xValid = 1;
-       viochar->event.xFlags.xFunction = HvLpEvent_Function_Int;
-       viochar->event.xFlags.xAckInd = HvLpEvent_AckInd_NoAck;
-       viochar->event.xFlags.xAckType = HvLpEvent_AckType_DeferredAck;
-       viochar->event.xType = HvLpEvent_Type_VirtualIo;
-       viochar->event.xSubtype = viomajorsubtype_chario | viochardata;
-       viochar->event.xSourceLp = HvLpConfig_getLpIndex();
-       viochar->event.xTargetLp = lp;
-       viochar->event.xSizeMinus1 = sizeof(struct viocharlpevent);
-       viochar->event.xSourceInstanceId = viopath_sourceinst(lp);
-       viochar->event.xTargetInstanceId = viopath_targetinst(lp);
+       hev->flags = HV_LP_EVENT_VALID | HV_LP_EVENT_DEFERRED_ACK |
+               HV_LP_EVENT_INT;
+       hev->xType = HvLpEvent_Type_VirtualIo;
+       hev->xSubtype = viomajorsubtype_chario | viochardata;
+       hev->xSourceLp = HvLpConfig_getLpIndex();
+       hev->xTargetLp = lp;
+       hev->xSizeMinus1 = sizeof(struct viocharlpevent);
+       hev->xSourceInstanceId = viopath_sourceinst(lp);
+       hev->xTargetInstanceId = viopath_targetinst(lp);
 }
 
 /*
@@ -606,18 +522,8 @@ static void viocons_write(struct console *co, const char *s, unsigned count)
 
        hvlogOutput(s, count);
 
-       if (!viopath_isactive(pi->lp)) {
-               /*
-                * This is a VERY noisy trace message in the case where the
-                * path manager is not active or in the case where this
-                * function is called prior to viocons initialization.  It is
-                * being commented out for the sake of a clear trace buffer.
-                */
-#if 0
-                hvlog("\n\rviocons_write: path not active to lp %d", pi->lp);
-#endif
+       if (!viopath_isactive(pi->lp))
                return;
-       }
 
        /* 
         * Any newline character found will cause a
@@ -630,17 +536,16 @@ static void viocons_write(struct console *co, const char *s, unsigned count)
                         * Newline found. Print everything up to and 
                         * including the newline
                         */
-                       internal_write(pi, &s[begin], index - begin + 1,
-                                       NULL);
+                       internal_write(pi, &s[begin], index - begin + 1);
                        begin = index + 1;
                        /* Emit a carriage return as well */
-                       internal_write(pi, &cr, 1, NULL);
+                       internal_write(pi, &cr, 1);
                }
        }
 
        /* If any characters left to write, write them now */
        if ((index - begin) > 0)
-               internal_write(pi, &s[begin], index - begin, NULL);
+               internal_write(pi, &s[begin], index - begin);
 }
 
 /*
@@ -724,11 +629,9 @@ static void viotty_close(struct tty_struct *tty, struct file *filp)
 /*
  * TTY Write method
  */
-static int viotty_write(struct tty_struct *tty, int from_user,
-                       const unsigned char *buf, int count)
+static int viotty_write(struct tty_struct *tty, const unsigned char *buf,
+               int count)
 {
-       int ret;
-       int total = 0;
        struct port_info *pi;
 
        pi = get_port_data(tty);
@@ -749,53 +652,10 @@ static int viotty_write(struct tty_struct *tty, int from_user,
         * viotty_write call and, since the viopath isn't active to this
         * partition, return count.
         */
-       if (!viopath_isactive(pi->lp)) {
-               /* Noisy trace.  Commented unless needed. */
-#if 0
-                hvlog("\n\rviotty_write: viopath NOT active for lp %d.",pi->lp);
-#endif
+       if (!viopath_isactive(pi->lp))
                return count;
-       }
 
-       /*
-        * If the viotty_write is invoked from user space we want to do the
-        * copy_from_user() into an event buffer from the cfu buffer before
-        * internal_write() is called because internal_write may need to buffer
-        * data which will need to grab a spin_lock and we shouldn't
-        * copy_from_user() while holding a spin_lock.  Should internal_write()
-        * not need to buffer data then it'll just use the event we created here
-        * rather than checking one out from vio_get_event_buffer().
-        */
-       if (from_user) {
-               struct viocharlpevent *viochar;
-               int curlen;
-               const char *curbuf = buf;
-
-               viochar = viocons_get_cfu_buffer();
-               if (viochar == NULL)
-                       return -EAGAIN;
-               initDataEvent(viochar, pi->lp);
-               while (count > 0) {
-                       if (count > VIOCHAR_MAX_DATA)
-                               curlen = VIOCHAR_MAX_DATA;
-                       else
-                               curlen = count;
-                       viochar->len = curlen;
-                       ret = copy_from_user(viochar->data, curbuf, curlen);
-                       if (ret)
-                               break;
-                       ret = internal_write(pi, viochar->data,
-                                       viochar->len, viochar);
-                       total += ret;
-                       if (ret != curlen)
-                               break;
-                       count -= curlen;
-                       curbuf += curlen;
-               }
-               viocons_free_cfu_buffer(viochar);
-       } else
-               total = internal_write(pi, buf, count, NULL);
-       return total;
+       return internal_write(pi, buf, count);
 }
 
 /*
@@ -814,7 +674,7 @@ static void viotty_put_char(struct tty_struct *tty, unsigned char ch)
                hvlogOutput(&ch, 1);
 
        if (viopath_isactive(pi->lp))
-               internal_write(pi, &ch, 1, NULL);
+               internal_write(pi, &ch, 1);
 }
 
 /*
@@ -892,7 +752,7 @@ static void vioHandleOpenEvent(struct HvLpEvent *event)
        struct port_info *pi;
        int reject = 0;
 
-       if (event->xFlags.xFunction == HvLpEvent_Function_Ack) {
+       if (hvlpevent_is_ack(event)) {
                if (port >= VTTY_PORTS)
                        return;
 
@@ -923,12 +783,12 @@ static void vioHandleOpenEvent(struct HvLpEvent *event)
                        atomic_set(aptr, 1);
                } else
                        printk(VIOCONS_KERN_WARN
-                              "wierd...got open ack without atomic\n");
+                              "weird...got open ack without atomic\n");
                return;
        }
 
        /* This had better require an ack, otherwise complain */
-       if (event->xFlags.xAckInd != HvLpEvent_AckInd_DoAck) {
+       if (!hvlpevent_need_ack(event)) {
                printk(VIOCONS_KERN_WARN "viocharopen without ack bit!\n");
                return;
        }
@@ -996,7 +856,7 @@ static void vioHandleCloseEvent(struct HvLpEvent *event)
        struct viocharlpevent *cevent = (struct viocharlpevent *)event;
        u8 port = cevent->virtual_device;
 
-       if (event->xFlags.xFunction == HvLpEvent_Function_Int) {
+       if (hvlpevent_is_int(event)) {
                if (port >= VTTY_PORTS) {
                        printk(VIOCONS_KERN_WARN
                                        "close message from invalid virtual device.\n");
@@ -1044,6 +904,7 @@ static void vioHandleData(struct HvLpEvent *event)
        struct viocharlpevent *cevent = (struct viocharlpevent *)event;
        struct port_info *pi;
        int index;
+       int num_pushed;
        u8 port = cevent->virtual_device;
 
        if (port >= VTTY_PORTS) {
@@ -1104,6 +965,7 @@ static void vioHandleData(struct HvLpEvent *event)
         * functionality will only work if built into the kernel and
         * then only if sysrq is enabled through the proc filesystem.
         */
+       num_pushed = 0;
        for (index = 0; index < cevent->len; index++) {
 #ifdef CONFIG_MAGIC_SYSRQ
                if (sysrq_enabled) {
@@ -1133,16 +995,14 @@ static void vioHandleData(struct HvLpEvent *event)
                 * Don't attempt to copy more data into the buffer than we
                 * have room for because it would fail without indication.
                 */
-               if ((tty->flip.count + 1) > TTY_FLIPBUF_SIZE) {
+               if(tty_insert_flip_char(tty, cevent->data[index], TTY_NORMAL) == 0) {
                        printk(VIOCONS_KERN_WARN "input buffer overflow!\n");
                        break;
                }
-               tty_insert_flip_char(tty, cevent->data[index], TTY_NORMAL);
+               num_pushed++;
        }
 
-       /* if cevent->len == 0 then no data was added to the buffer and flip.count == 0 */
-       if (tty->flip.count)
-               /* The next call resets flip.count when the data is flushed. */
+       if (num_pushed)
                tty_flip_buffer_push(tty);
 }
 
@@ -1196,8 +1056,7 @@ static void vioHandleCharEvent(struct HvLpEvent *event)
                vioHandleConfig(event);
                break;
        default:
-               if ((event->xFlags.xFunction == HvLpEvent_Function_Int) &&
-                   (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck)) {
+               if (hvlpevent_is_int(event) && hvlpevent_need_ack(event)) {
                        event->xRc = HvLpEvent_Rc_InvalidSubtype;
                        HvCallEvent_ackLpEvent(event);
                }
@@ -1310,8 +1169,6 @@ static int __init viocons_init2(void)
                viotty_driver = NULL;
        }
 
-       viocons_init_cfu_buffer();
-
        unregister_console(&viocons_early);
        register_console(&viocons);