]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/ata/libata-eh.c
libata: blacklist NCQ on Seagate Barracuda ST380817AS
[linux-2.6-omap-h63xx.git] / drivers / ata / libata-eh.c
index 8219e2d7104505e4d4350f18e4052df1dda25cc0..0bd3898793a7fb439311b10df6ab765cdab47dd8 100644 (file)
@@ -74,7 +74,6 @@ static const unsigned long ata_eh_reset_timeouts[] = {
 };
 
 static void __ata_port_freeze(struct ata_port *ap);
-static void ata_eh_finish(struct ata_port *ap);
 #ifdef CONFIG_PM
 static void ata_eh_handle_port_suspend(struct ata_port *ap);
 static void ata_eh_handle_port_resume(struct ata_port *ap);
@@ -358,7 +357,7 @@ enum scsi_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd)
 void ata_scsi_error(struct Scsi_Host *host)
 {
        struct ata_port *ap = ata_shost_to_port(host);
-       int i, repeat_cnt = ATA_EH_MAX_REPEAT;
+       int i;
        unsigned long flags;
 
        DPRINTK("ENTER\n");
@@ -424,6 +423,9 @@ void ata_scsi_error(struct Scsi_Host *host)
                        __ata_port_freeze(ap);
 
                spin_unlock_irqrestore(ap->lock, flags);
+
+               /* initialize eh_tries */
+               ap->eh_tries = ATA_EH_MAX_TRIES;
        } else
                spin_unlock_wait(ap->lock);
 
@@ -449,6 +451,7 @@ void ata_scsi_error(struct Scsi_Host *host)
 
                ap->pflags |= ATA_PFLAG_EH_IN_PROGRESS;
                ap->pflags &= ~ATA_PFLAG_EH_PENDING;
+               ap->excl_link = NULL;   /* don't maintain exclusion over EH */
 
                spin_unlock_irqrestore(ap->lock, flags);
 
@@ -468,15 +471,12 @@ void ata_scsi_error(struct Scsi_Host *host)
                spin_lock_irqsave(ap->lock, flags);
 
                if (ap->pflags & ATA_PFLAG_EH_PENDING) {
-                       if (--repeat_cnt) {
-                               ata_port_printk(ap, KERN_INFO,
-                                       "EH pending after completion, "
-                                       "repeating EH (cnt=%d)\n", repeat_cnt);
+                       if (--ap->eh_tries) {
                                spin_unlock_irqrestore(ap->lock, flags);
                                goto repeat;
                        }
                        ata_port_printk(ap, KERN_ERR, "EH pending after %d "
-                                       "tries, giving up\n", ATA_EH_MAX_REPEAT);
+                                       "tries, giving up\n", ATA_EH_MAX_TRIES);
                        ap->pflags &= ~ATA_PFLAG_EH_PENDING;
                }
 
@@ -904,6 +904,79 @@ int ata_port_freeze(struct ata_port *ap)
        return nr_aborted;
 }
 
+/**
+ *     sata_async_notification - SATA async notification handler
+ *     @ap: ATA port where async notification is received
+ *
+ *     Handler to be called when async notification via SDB FIS is
+ *     received.  This function schedules EH if necessary.
+ *
+ *     LOCKING:
+ *     spin_lock_irqsave(host lock)
+ *
+ *     RETURNS:
+ *     1 if EH is scheduled, 0 otherwise.
+ */
+int sata_async_notification(struct ata_port *ap)
+{
+       u32 sntf;
+       int rc;
+
+       if (!(ap->flags & ATA_FLAG_AN))
+               return 0;
+
+       rc = sata_scr_read(&ap->link, SCR_NOTIFICATION, &sntf);
+       if (rc == 0)
+               sata_scr_write(&ap->link, SCR_NOTIFICATION, sntf);
+
+       if (!ap->nr_pmp_links || rc) {
+               /* PMP is not attached or SNTF is not available */
+               if (!ap->nr_pmp_links) {
+                       /* PMP is not attached.  Check whether ATAPI
+                        * AN is configured.  If so, notify media
+                        * change.
+                        */
+                       struct ata_device *dev = ap->link.device;
+
+                       if ((dev->class == ATA_DEV_ATAPI) &&
+                           (dev->flags & ATA_DFLAG_AN))
+                               ata_scsi_media_change_notify(dev);
+                       return 0;
+               } else {
+                       /* PMP is attached but SNTF is not available.
+                        * ATAPI async media change notification is
+                        * not used.  The PMP must be reporting PHY
+                        * status change, schedule EH.
+                        */
+                       ata_port_schedule_eh(ap);
+                       return 1;
+               }
+       } else {
+               /* PMP is attached and SNTF is available */
+               struct ata_link *link;
+
+               /* check and notify ATAPI AN */
+               ata_port_for_each_link(link, ap) {
+                       if (!(sntf & (1 << link->pmp)))
+                               continue;
+
+                       if ((link->device->class == ATA_DEV_ATAPI) &&
+                           (link->device->flags & ATA_DFLAG_AN))
+                               ata_scsi_media_change_notify(link->device);
+               }
+
+               /* If PMP is reporting that PHY status of some
+                * downstream ports has changed, schedule EH.
+                */
+               if (sntf & (1 << SATA_PMP_CTRL_PORT)) {
+                       ata_port_schedule_eh(ap);
+                       return 1;
+               }
+
+               return 0;
+       }
+}
+
 /**
  *     ata_eh_freeze_port - EH helper to freeze port
  *     @ap: ATA port to freeze
@@ -1015,7 +1088,7 @@ void ata_eh_qc_retry(struct ata_queued_cmd *qc)
  *     LOCKING:
  *     None.
  */
-static void ata_eh_detach_dev(struct ata_device *dev)
+void ata_eh_detach_dev(struct ata_device *dev)
 {
        struct ata_link *link = dev->link;
        struct ata_port *ap = link->ap;
@@ -1052,8 +1125,8 @@ static void ata_eh_detach_dev(struct ata_device *dev)
  *     LOCKING:
  *     None.
  */
-static void ata_eh_about_to_do(struct ata_link *link, struct ata_device *dev,
-                              unsigned int action)
+void ata_eh_about_to_do(struct ata_link *link, struct ata_device *dev,
+                       unsigned int action)
 {
        struct ata_port *ap = link->ap;
        struct ata_eh_info *ehi = &link->eh_info;
@@ -1095,8 +1168,8 @@ static void ata_eh_about_to_do(struct ata_link *link, struct ata_device *dev,
  *     LOCKING:
  *     None.
  */
-static void ata_eh_done(struct ata_link *link, struct ata_device *dev,
-                       unsigned int action)
+void ata_eh_done(struct ata_link *link, struct ata_device *dev,
+                unsigned int action)
 {
        struct ata_eh_context *ehc = &link->eh_context;
 
@@ -1308,6 +1381,7 @@ static void ata_eh_analyze_serror(struct ata_link *link)
        struct ata_eh_context *ehc = &link->eh_context;
        u32 serror = ehc->i.serror;
        unsigned int err_mask = 0, action = 0;
+       u32 hotplug_mask;
 
        if (serror & SERR_PERSISTENT) {
                err_mask |= AC_ERR_ATA_BUS;
@@ -1326,7 +1400,20 @@ static void ata_eh_analyze_serror(struct ata_link *link)
                err_mask |= AC_ERR_SYSTEM;
                action |= ATA_EH_HARDRESET;
        }
-       if (serror & (SERR_PHYRDY_CHG | SERR_DEV_XCHG))
+
+       /* Determine whether a hotplug event has occurred.  Both
+        * SError.N/X are considered hotplug events for enabled or
+        * host links.  For disabled PMP links, only N bit is
+        * considered as X bit is left at 1 for link plugging.
+        */
+       hotplug_mask = 0;
+
+       if (!(link->flags & ATA_LFLAG_DISABLED) || ata_is_host_link(link))
+               hotplug_mask = SERR_PHYRDY_CHG | SERR_DEV_XCHG;
+       else
+               hotplug_mask = SERR_PHYRDY_CHG;
+
+       if (serror & hotplug_mask)
                ata_ehi_hotplugged(&ehc->i);
 
        ehc->i.err_mask |= err_mask;
@@ -1756,7 +1843,7 @@ static void ata_eh_link_autopsy(struct ata_link *link)
  *     LOCKING:
  *     Kernel thread context (may sleep).
  */
-static void ata_eh_autopsy(struct ata_port *ap)
+void ata_eh_autopsy(struct ata_port *ap)
 {
        struct ata_link *link;
 
@@ -1778,6 +1865,7 @@ static void ata_eh_link_report(struct ata_link *link)
        struct ata_port *ap = link->ap;
        struct ata_eh_context *ehc = &link->eh_context;
        const char *frozen, *desc;
+       char tries_buf[6];
        int tag, nr_failed = 0;
 
        desc = NULL;
@@ -1802,22 +1890,48 @@ static void ata_eh_link_report(struct ata_link *link)
        if (ap->pflags & ATA_PFLAG_FROZEN)
                frozen = " frozen";
 
+       memset(tries_buf, 0, sizeof(tries_buf));
+       if (ap->eh_tries < ATA_EH_MAX_TRIES)
+               snprintf(tries_buf, sizeof(tries_buf) - 1, " t%d",
+                        ap->eh_tries);
+
        if (ehc->i.dev) {
                ata_dev_printk(ehc->i.dev, KERN_ERR, "exception Emask 0x%x "
-                              "SAct 0x%x SErr 0x%x action 0x%x%s\n",
-                              ehc->i.err_mask, link->sactive,
-                              ehc->i.serror, ehc->i.action, frozen);
+                              "SAct 0x%x SErr 0x%x action 0x%x%s%s\n",
+                              ehc->i.err_mask, link->sactive, ehc->i.serror,
+                              ehc->i.action, frozen, tries_buf);
                if (desc)
                        ata_dev_printk(ehc->i.dev, KERN_ERR, "%s\n", desc);
        } else {
                ata_link_printk(link, KERN_ERR, "exception Emask 0x%x "
-                               "SAct 0x%x SErr 0x%x action 0x%x%s\n",
-                               ehc->i.err_mask, link->sactive,
-                               ehc->i.serror, ehc->i.action, frozen);
+                               "SAct 0x%x SErr 0x%x action 0x%x%s%s\n",
+                               ehc->i.err_mask, link->sactive, ehc->i.serror,
+                               ehc->i.action, frozen, tries_buf);
                if (desc)
                        ata_link_printk(link, KERN_ERR, "%s\n", desc);
        }
 
+       if (ehc->i.serror)
+               ata_port_printk(ap, KERN_ERR,
+                 "SError: { %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s}\n",
+                 ehc->i.serror & SERR_DATA_RECOVERED ? "RecovData " : "",
+                 ehc->i.serror & SERR_COMM_RECOVERED ? "RecovComm " : "",
+                 ehc->i.serror & SERR_DATA ? "UnrecovData " : "",
+                 ehc->i.serror & SERR_PERSISTENT ? "Persist " : "",
+                 ehc->i.serror & SERR_PROTOCOL ? "Proto " : "",
+                 ehc->i.serror & SERR_INTERNAL ? "HostInt " : "",
+                 ehc->i.serror & SERR_PHYRDY_CHG ? "PHYRdyChg " : "",
+                 ehc->i.serror & SERR_PHY_INT_ERR ? "PHYInt " : "",
+                 ehc->i.serror & SERR_COMM_WAKE ? "CommWake " : "",
+                 ehc->i.serror & SERR_10B_8B_ERR ? "10B8B " : "",
+                 ehc->i.serror & SERR_DISPARITY ? "Dispar " : "",
+                 ehc->i.serror & SERR_CRC ? "BadCRC " : "",
+                 ehc->i.serror & SERR_HANDSHAKE ? "Handshk " : "",
+                 ehc->i.serror & SERR_LINK_SEQ_ERR ? "LinkSeq " : "",
+                 ehc->i.serror & SERR_TRANS_ST_ERROR ? "TrStaTrns " : "",
+                 ehc->i.serror & SERR_UNRECOG_FIS ? "UnrecFIS " : "",
+                 ehc->i.serror & SERR_DEV_XCHG ? "DevExch " : "" );
+
        for (tag = 0; tag < ATA_MAX_QUEUE; tag++) {
                static const char *dma_str[] = {
                        [DMA_BIDIRECTIONAL]     = "bidi",
@@ -1849,6 +1963,30 @@ static void ata_eh_link_report(struct ata_link *link)
                        res->hob_lbal, res->hob_lbam, res->hob_lbah,
                        res->device, qc->err_mask, ata_err_string(qc->err_mask),
                        qc->err_mask & AC_ERR_NCQ ? " <F>" : "");
+
+               if (res->command & (ATA_BUSY | ATA_DRDY | ATA_DF | ATA_DRQ |
+                                   ATA_ERR) ) {
+                       if (res->command & ATA_BUSY)
+                               ata_dev_printk(qc->dev, KERN_ERR,
+                                 "status: { Busy }\n" );
+                       else
+                               ata_dev_printk(qc->dev, KERN_ERR,
+                                 "status: { %s%s%s%s}\n",
+                                 res->command & ATA_DRDY ? "DRDY " : "",
+                                 res->command & ATA_DF ? "DF " : "",
+                                 res->command & ATA_DRQ ? "DRQ " : "",
+                                 res->command & ATA_ERR ? "ERR " : "" );
+               }
+
+               if (cmd->command != ATA_CMD_PACKET &&
+                   (res->feature & (ATA_ICRC | ATA_UNC | ATA_IDNF |
+                                    ATA_ABORTED)))
+                       ata_dev_printk(qc->dev, KERN_ERR,
+                         "error: { %s%s%s%s}\n",
+                         res->feature & ATA_ICRC ? "ICRC " : "",
+                         res->feature & ATA_UNC ? "UNC " : "",
+                         res->feature & ATA_IDNF ? "IDNF " : "",
+                         res->feature & ATA_ABORTED ? "ABRT " : "" );
        }
 }
 
@@ -1861,7 +1999,7 @@ static void ata_eh_link_report(struct ata_link *link)
  *     LOCKING:
  *     None.
  */
-static void ata_eh_report(struct ata_port *ap)
+void ata_eh_report(struct ata_port *ap)
 {
        struct ata_link *link;
 
@@ -1900,21 +2038,27 @@ static int ata_do_reset(struct ata_link *link, ata_reset_fn_t reset,
        return 0;
 }
 
-static int ata_eh_followup_srst_needed(int rc, int classify,
+static int ata_eh_followup_srst_needed(struct ata_link *link,
+                                      int rc, int classify,
                                       const unsigned int *classes)
 {
+       if (link->flags & ATA_LFLAG_NO_SRST)
+               return 0;
        if (rc == -EAGAIN)
                return 1;
        if (rc != 0)
                return 0;
-       if (classify && classes[0] == ATA_DEV_UNKNOWN)
+       if ((link->ap->flags & ATA_FLAG_PMP) && ata_is_host_link(link))
+               return 1;
+       if (classify && !(link->flags & ATA_LFLAG_ASSUME_CLASS) &&
+           classes[0] == ATA_DEV_UNKNOWN)
                return 1;
        return 0;
 }
 
-static int ata_eh_reset(struct ata_link *link, int classify,
-                       ata_prereset_fn_t prereset, ata_reset_fn_t softreset,
-                       ata_reset_fn_t hardreset, ata_postreset_fn_t postreset)
+int ata_eh_reset(struct ata_link *link, int classify,
+                ata_prereset_fn_t prereset, ata_reset_fn_t softreset,
+                ata_reset_fn_t hardreset, ata_postreset_fn_t postreset)
 {
        struct ata_eh_context *ehc = &link->eh_context;
        unsigned int *classes = ehc->classes;
@@ -1934,7 +2078,8 @@ static int ata_eh_reset(struct ata_link *link, int classify,
         */
        action = ehc->i.action;
        ehc->i.action &= ~ATA_EH_RESET_MASK;
-       if (softreset && (!hardreset || (!sata_set_spd_needed(link) &&
+       if (softreset && (!hardreset || (!(link->flags & ATA_LFLAG_NO_SRST) &&
+                                        !sata_set_spd_needed(link) &&
                                         !(action & ATA_EH_HARDRESET))))
                ehc->i.action |= ATA_EH_SOFTRESET;
        else
@@ -1997,7 +2142,7 @@ static int ata_eh_reset(struct ata_link *link, int classify,
        rc = ata_do_reset(link, reset, classes, deadline);
 
        if (reset == hardreset &&
-           ata_eh_followup_srst_needed(rc, classify, classes)) {
+           ata_eh_followup_srst_needed(link, rc, classify, classes)) {
                /* okay, let's do follow-up softreset */
                reset = softreset;
 
@@ -2012,8 +2157,8 @@ static int ata_eh_reset(struct ata_link *link, int classify,
                ata_eh_about_to_do(link, NULL, ATA_EH_RESET_MASK);
                rc = ata_do_reset(link, reset, classes, deadline);
 
-               if (rc == 0 && classify &&
-                   classes[0] == ATA_DEV_UNKNOWN) {
+               if (rc == 0 && classify && classes[0] == ATA_DEV_UNKNOWN &&
+                   !(link->flags & ATA_LFLAG_ASSUME_CLASS)) {
                        ata_link_printk(link, KERN_ERR,
                                        "classification failed\n");
                        rc = -EINVAL;
@@ -2021,7 +2166,11 @@ static int ata_eh_reset(struct ata_link *link, int classify,
                }
        }
 
-       if (rc && try < ARRAY_SIZE(ata_eh_reset_timeouts)) {
+       /* if we skipped follow-up srst, clear rc */
+       if (rc == -EAGAIN)
+               rc = 0;
+
+       if (rc && rc != -ERESTART && try < ARRAY_SIZE(ata_eh_reset_timeouts)) {
                unsigned long now = jiffies;
 
                if (time_before(now, deadline)) {
@@ -2045,12 +2194,25 @@ static int ata_eh_reset(struct ata_link *link, int classify,
        if (rc == 0) {
                u32 sstatus;
 
-               /* After the reset, the device state is PIO 0 and the
-                * controller state is undefined.  Record the mode.
-                */
-               ata_link_for_each_dev(dev, link)
+               ata_link_for_each_dev(dev, link) {
+                       /* After the reset, the device state is PIO 0
+                        * and the controller state is undefined.
+                        * Record the mode.
+                        */
                        dev->pio_mode = XFER_PIO_0;
 
+                       if (ata_link_offline(link))
+                               continue;
+
+                       /* apply class override and convert UNKNOWN to NONE */
+                       if (link->flags & ATA_LFLAG_ASSUME_ATA)
+                               classes[dev->devno] = ATA_DEV_ATA;
+                       else if (link->flags & ATA_LFLAG_ASSUME_SEMB)
+                               classes[dev->devno] = ATA_DEV_SEMB_UNSUP; /* not yet */
+                       else if (classes[dev->devno] == ATA_DEV_UNKNOWN)
+                               classes[dev->devno] = ATA_DEV_NONE;
+               }
+
                /* record current link speed */
                if (sata_scr_read(link, SCR_STATUS, &sstatus) == 0)
                        link->sata_spd = (sstatus >> 4) & 0xf;
@@ -2092,13 +2254,16 @@ static int ata_eh_revalidate_and_attach(struct ata_link *link,
                        readid_flags |= ATA_READID_POSTRESET;
 
                if ((action & ATA_EH_REVALIDATE) && ata_dev_enabled(dev)) {
+                       WARN_ON(dev->class == ATA_DEV_PMP);
+
                        if (ata_link_offline(link)) {
                                rc = -EIO;
                                goto err;
                        }
 
                        ata_eh_about_to_do(link, dev, ATA_EH_REVALIDATE);
-                       rc = ata_dev_revalidate(dev, readid_flags);
+                       rc = ata_dev_revalidate(dev, ehc->classes[dev->devno],
+                                               readid_flags);
                        if (rc)
                                goto err;
 
@@ -2116,8 +2281,11 @@ static int ata_eh_revalidate_and_attach(struct ata_link *link,
                           ata_class_enabled(ehc->classes[dev->devno])) {
                        dev->class = ehc->classes[dev->devno];
 
-                       rc = ata_dev_read_id(dev, &dev->class, readid_flags,
-                                            dev->id);
+                       if (dev->class == ATA_DEV_PMP)
+                               rc = sata_pmp_attach(dev);
+                       else
+                               rc = ata_dev_read_id(dev, &dev->class,
+                                                    readid_flags, dev->id);
                        switch (rc) {
                        case 0:
                                new_mask |= 1 << dev->devno;
@@ -2146,7 +2314,8 @@ static int ata_eh_revalidate_and_attach(struct ata_link *link,
         * device detection messages backwards.
         */
        ata_link_for_each_dev(dev, link) {
-               if (!(new_mask & (1 << dev->devno)))
+               if (!(new_mask & (1 << dev->devno)) ||
+                   dev->class == ATA_DEV_PMP)
                        continue;
 
                ehc->i.flags |= ATA_EHI_PRINTINFO;
@@ -2198,6 +2367,10 @@ static int ata_eh_skip_recovery(struct ata_link *link)
        struct ata_eh_context *ehc = &link->eh_context;
        struct ata_device *dev;
 
+       /* skip disabled links */
+       if (link->flags & ATA_LFLAG_DISABLED)
+               return 1;
+
        /* thaw frozen port, resume link and recover failed devices */
        if ((link->ap->pflags & ATA_PFLAG_FROZEN) ||
            (ehc->i.flags & ATA_EHI_RESUME_LINK) || ata_link_nr_enabled(link))
@@ -2289,15 +2462,16 @@ static int ata_eh_handle_dev_fail(struct ata_device *dev, int err)
  *     RETURNS:
  *     0 on success, -errno on failure.
  */
-static int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
-                         ata_reset_fn_t softreset, ata_reset_fn_t hardreset,
-                         ata_postreset_fn_t postreset,
-                         struct ata_link **r_failed_link)
+int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
+                  ata_reset_fn_t softreset, ata_reset_fn_t hardreset,
+                  ata_postreset_fn_t postreset,
+                  struct ata_link **r_failed_link)
 {
        struct ata_link *link;
        struct ata_device *dev;
        int nr_failed_devs, nr_disabled_devs;
        int reset, rc;
+       unsigned long flags;
 
        DPRINTK("ENTER\n");
 
@@ -2305,8 +2479,20 @@ static int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
        ata_port_for_each_link(link, ap) {
                struct ata_eh_context *ehc = &link->eh_context;
 
+               /* re-enable link? */
+               if (ehc->i.action & ATA_EH_ENABLE_LINK) {
+                       ata_eh_about_to_do(link, NULL, ATA_EH_ENABLE_LINK);
+                       spin_lock_irqsave(ap->lock, flags);
+                       link->flags &= ~ATA_LFLAG_DISABLED;
+                       spin_unlock_irqrestore(ap->lock, flags);
+                       ata_eh_done(link, NULL, ATA_EH_ENABLE_LINK);
+               }
+
                ata_link_for_each_dev(dev, link) {
-                       ehc->tries[dev->devno] = ATA_EH_DEV_TRIES;
+                       if (link->flags & ATA_LFLAG_NO_RETRY)
+                               ehc->tries[dev->devno] = 1;
+                       else
+                               ehc->tries[dev->devno] = ATA_EH_DEV_TRIES;
 
                        /* collect port action mask recorded in dev actions */
                        ehc->i.action |= ehc->i.dev_action[dev->devno] &
@@ -2386,6 +2572,12 @@ static int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
                if (rc)
                        goto dev_fail;
 
+               /* if PMP got attached, return, pmp EH will take care of it */
+               if (link->device->class == ATA_DEV_PMP) {
+                       ehc->i.action = 0;
+                       return 0;
+               }
+
                /* configure transfer mode if necessary */
                if (ehc->i.flags & ATA_EHI_SETMODE) {
                        rc = ata_set_mode(link, &dev);
@@ -2438,7 +2630,7 @@ static int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
  *     LOCKING:
  *     None.
  */
-static void ata_eh_finish(struct ata_port *ap)
+void ata_eh_finish(struct ata_port *ap)
 {
        int tag;
 
@@ -2468,6 +2660,10 @@ static void ata_eh_finish(struct ata_port *ap)
                        }
                }
        }
+
+       /* make sure nr_active_links is zero after EH */
+       WARN_ON(ap->nr_active_links);
+       ap->nr_active_links = 0;
 }
 
 /**