diff options
author | David Woodhouse <David.Woodhouse@intel.com> | 2008-07-11 14:36:25 +0100 |
---|---|---|
committer | David Woodhouse <David.Woodhouse@intel.com> | 2008-07-11 14:36:25 +0100 |
commit | a8931ef380c92d121ae74ecfb03b2d63f72eea6f (patch) | |
tree | 980fb6b019e11e6cb1ece55b7faff184721a8053 /drivers/block | |
parent | 90574d0a4d4b73308ae54a2a57a4f3f1fa98e984 (diff) | |
parent | e5a5816f7875207cb0a0a7032e39a4686c5e10a4 (diff) | |
download | talos-obmc-linux-a8931ef380c92d121ae74ecfb03b2d63f72eea6f.tar.gz talos-obmc-linux-a8931ef380c92d121ae74ecfb03b2d63f72eea6f.zip |
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
Diffstat (limited to 'drivers/block')
-rw-r--r-- | drivers/block/amiflop.c | 6 | ||||
-rw-r--r-- | drivers/block/aoe/aoecmd.c | 10 | ||||
-rw-r--r-- | drivers/block/brd.c | 2 | ||||
-rw-r--r-- | drivers/block/cciss.c | 81 | ||||
-rw-r--r-- | drivers/block/ub.c | 63 | ||||
-rw-r--r-- | drivers/block/viodasd.c | 2 | ||||
-rw-r--r-- | drivers/block/virtio_blk.c | 49 | ||||
-rw-r--r-- | drivers/block/z2ram.c | 2 |
8 files changed, 141 insertions, 74 deletions
diff --git a/drivers/block/amiflop.c b/drivers/block/amiflop.c index c9751b2b57e6..7516baff3bb9 100644 --- a/drivers/block/amiflop.c +++ b/drivers/block/amiflop.c @@ -1714,10 +1714,10 @@ static int __init amiga_floppy_init(void) int i, ret; if (!MACH_IS_AMIGA) - return -ENXIO; + return -ENODEV; if (!AMIGAHW_PRESENT(AMI_FLOPPY)) - return -ENXIO; + return -ENODEV; if (register_blkdev(FLOPPY_MAJOR,"fd")) return -EBUSY; @@ -1755,7 +1755,7 @@ static int __init amiga_floppy_init(void) if (!floppy_queue) goto out_queue; - ret = -ENXIO; + ret = -ENODEV; if (fd_probe_drives() < 1) /* No usable drives */ goto out_probe; diff --git a/drivers/block/aoe/aoecmd.c b/drivers/block/aoe/aoecmd.c index 8fc429cf82b6..41f818be2f7e 100644 --- a/drivers/block/aoe/aoecmd.c +++ b/drivers/block/aoe/aoecmd.c @@ -755,11 +755,13 @@ diskstats(struct gendisk *disk, struct bio *bio, ulong duration, sector_t sector { unsigned long n_sect = bio->bi_size >> 9; const int rw = bio_data_dir(bio); + struct hd_struct *part; - all_stat_inc(disk, ios[rw], sector); - all_stat_add(disk, ticks[rw], duration, sector); - all_stat_add(disk, sectors[rw], n_sect, sector); - all_stat_add(disk, io_ticks, duration, sector); + part = get_part(disk, sector); + all_stat_inc(disk, part, ios[rw], sector); + all_stat_add(disk, part, ticks[rw], duration, sector); + all_stat_add(disk, part, sectors[rw], n_sect, sector); + all_stat_add(disk, part, io_ticks, duration, sector); } void diff --git a/drivers/block/brd.c b/drivers/block/brd.c index a196ef7f147f..24b97b0bef99 100644 --- a/drivers/block/brd.c +++ b/drivers/block/brd.c @@ -397,6 +397,7 @@ module_param(max_part, int, 0); MODULE_PARM_DESC(max_part, "Maximum number of partitions per RAM disk"); MODULE_LICENSE("GPL"); MODULE_ALIAS_BLOCKDEV_MAJOR(RAMDISK_MAJOR); +MODULE_ALIAS("rd"); #ifndef MODULE /* Legacy boot options - nonmodular */ @@ -447,6 +448,7 @@ static struct brd_device *brd_alloc(int i) disk->fops = &brd_fops; disk->private_data = brd; disk->queue = brd->brd_queue; + disk->flags |= GENHD_FL_SUPPRESS_PARTITION_INFO; sprintf(disk->disk_name, "ram%d", i); set_capacity(disk, rd_size * 2); diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index e336b05fe4a7..d81632cd7d06 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -53,15 +53,16 @@ #include <linux/scatterlist.h> #define CCISS_DRIVER_VERSION(maj,min,submin) ((maj<<16)|(min<<8)|(submin)) -#define DRIVER_NAME "HP CISS Driver (v 3.6.14)" -#define DRIVER_VERSION CCISS_DRIVER_VERSION(3,6,14) +#define DRIVER_NAME "HP CISS Driver (v 3.6.20)" +#define DRIVER_VERSION CCISS_DRIVER_VERSION(3, 6, 20) /* Embedded module documentation macros - see modules.h */ MODULE_AUTHOR("Hewlett-Packard Company"); -MODULE_DESCRIPTION("Driver for HP Controller SA5xxx SA6xxx version 3.6.14"); +MODULE_DESCRIPTION("Driver for HP Smart Array Controllers"); MODULE_SUPPORTED_DEVICE("HP SA5i SA5i+ SA532 SA5300 SA5312 SA641 SA642 SA6400" - " SA6i P600 P800 P400 P400i E200 E200i E500"); -MODULE_VERSION("3.6.14"); + " SA6i P600 P800 P400 P400i E200 E200i E500 P700m" + " Smart Array G2 Series SAS/SATA Controllers"); +MODULE_VERSION("3.6.20"); MODULE_LICENSE("GPL"); #include "cciss_cmd.h" @@ -90,6 +91,11 @@ static const struct pci_device_id cciss_pci_device_id[] = { {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSD, 0x103C, 0x3215}, {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSC, 0x103C, 0x3237}, {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSC, 0x103C, 0x323D}, + {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x3241}, + {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x3243}, + {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x3245}, + {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x3247}, + {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x3249}, {PCI_VENDOR_ID_HP, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_RAID << 8, 0xffff << 8, 0}, {0,} @@ -100,30 +106,34 @@ MODULE_DEVICE_TABLE(pci, cciss_pci_device_id); /* board_id = Subsystem Device ID & Vendor ID * product = Marketing Name for the board * access = Address of the struct of function pointers - * nr_cmds = Number of commands supported by controller */ static struct board_type products[] = { - {0x40700E11, "Smart Array 5300", &SA5_access, 512}, - {0x40800E11, "Smart Array 5i", &SA5B_access, 512}, - {0x40820E11, "Smart Array 532", &SA5B_access, 512}, - {0x40830E11, "Smart Array 5312", &SA5B_access, 512}, - {0x409A0E11, "Smart Array 641", &SA5_access, 512}, - {0x409B0E11, "Smart Array 642", &SA5_access, 512}, - {0x409C0E11, "Smart Array 6400", &SA5_access, 512}, - {0x409D0E11, "Smart Array 6400 EM", &SA5_access, 512}, - {0x40910E11, "Smart Array 6i", &SA5_access, 512}, - {0x3225103C, "Smart Array P600", &SA5_access, 512}, - {0x3223103C, "Smart Array P800", &SA5_access, 512}, - {0x3234103C, "Smart Array P400", &SA5_access, 512}, - {0x3235103C, "Smart Array P400i", &SA5_access, 512}, - {0x3211103C, "Smart Array E200i", &SA5_access, 120}, - {0x3212103C, "Smart Array E200", &SA5_access, 120}, - {0x3213103C, "Smart Array E200i", &SA5_access, 120}, - {0x3214103C, "Smart Array E200i", &SA5_access, 120}, - {0x3215103C, "Smart Array E200i", &SA5_access, 120}, - {0x3237103C, "Smart Array E500", &SA5_access, 512}, - {0x323D103C, "Smart Array P700m", &SA5_access, 512}, - {0xFFFF103C, "Unknown Smart Array", &SA5_access, 120}, + {0x40700E11, "Smart Array 5300", &SA5_access}, + {0x40800E11, "Smart Array 5i", &SA5B_access}, + {0x40820E11, "Smart Array 532", &SA5B_access}, + {0x40830E11, "Smart Array 5312", &SA5B_access}, + {0x409A0E11, "Smart Array 641", &SA5_access}, + {0x409B0E11, "Smart Array 642", &SA5_access}, + {0x409C0E11, "Smart Array 6400", &SA5_access}, + {0x409D0E11, "Smart Array 6400 EM", &SA5_access}, + {0x40910E11, "Smart Array 6i", &SA5_access}, + {0x3225103C, "Smart Array P600", &SA5_access}, + {0x3223103C, "Smart Array P800", &SA5_access}, + {0x3234103C, "Smart Array P400", &SA5_access}, + {0x3235103C, "Smart Array P400i", &SA5_access}, + {0x3211103C, "Smart Array E200i", &SA5_access}, + {0x3212103C, "Smart Array E200", &SA5_access}, + {0x3213103C, "Smart Array E200i", &SA5_access}, + {0x3214103C, "Smart Array E200i", &SA5_access}, + {0x3215103C, "Smart Array E200i", &SA5_access}, + {0x3237103C, "Smart Array E500", &SA5_access}, + {0x323D103C, "Smart Array P700m", &SA5_access}, + {0x3241103C, "Smart Array P212", &SA5_access}, + {0x3243103C, "Smart Array P410", &SA5_access}, + {0x3245103C, "Smart Array P410i", &SA5_access}, + {0x3247103C, "Smart Array P411", &SA5_access}, + {0x3249103C, "Smart Array P812", &SA5_access}, + {0xFFFF103C, "Unknown Smart Array", &SA5_access}, }; /* How long to wait (in milliseconds) for board to go into simple mode */ @@ -3075,11 +3085,20 @@ static int __devinit cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev) print_cfg_table(c->cfgtable); #endif /* CCISS_DEBUG */ + /* Some controllers support Zero Memory Raid (ZMR). + * When configured in ZMR mode the number of supported + * commands drops to 64. So instead of just setting an + * arbitrary value we make the driver a little smarter. + * We read the config table to tell us how many commands + * are supported on the controller then subtract 4 to + * leave a little room for ioctl calls. + */ + c->max_commands = readl(&(c->cfgtable->CmdsOutMax)); for (i = 0; i < ARRAY_SIZE(products); i++) { if (board_id == products[i].board_id) { c->product_name = products[i].product_name; c->access = *(products[i].access); - c->nr_cmds = products[i].nr_cmds; + c->nr_cmds = c->max_commands - 4; break; } } @@ -3099,7 +3118,7 @@ static int __devinit cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev) if (subsystem_vendor_id == PCI_VENDOR_ID_HP) { c->product_name = products[i-1].product_name; c->access = *(products[i-1].access); - c->nr_cmds = products[i-1].nr_cmds; + c->nr_cmds = c->max_commands - 4; printk(KERN_WARNING "cciss: This is an unknown " "Smart Array controller.\n" "cciss: Please update to the latest driver " @@ -3535,6 +3554,10 @@ static int __devinit cciss_init_one(struct pci_dev *pdev, for (j = 0; j <= hba[i]->highest_lun; j++) add_disk(hba[i]->gendisk[j]); + /* we must register the controller even if no disks exist */ + if (hba[i]->highest_lun == -1) + add_disk(hba[i]->gendisk[0]); + return 1; clean4: diff --git a/drivers/block/ub.c b/drivers/block/ub.c index e322cce8c12d..3a281ef11ffa 100644 --- a/drivers/block/ub.c +++ b/drivers/block/ub.c @@ -205,6 +205,7 @@ struct ub_scsi_cmd { unsigned char key, asc, ascq; /* May be valid if error==-EIO */ int stat_count; /* Retries getting status. */ + unsigned int timeo; /* jiffies until rq->timeout changes */ unsigned int len; /* Requested length */ unsigned int current_sg; @@ -318,6 +319,7 @@ struct ub_dev { int openc; /* protected by ub_lock! */ /* kref is too implicit for our taste */ int reset; /* Reset is running */ + int bad_resid; unsigned int tagcnt; char name[12]; struct usb_device *dev; @@ -764,6 +766,12 @@ static void ub_cmd_build_packet(struct ub_dev *sc, struct ub_lun *lun, cmd->cdb_len = rq->cmd_len; cmd->len = rq->data_len; + + /* + * To reapply this to every URB is not as incorrect as it looks. + * In return, we avoid any complicated tracking calculations. + */ + cmd->timeo = rq->timeout; } static void ub_rw_cmd_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd) @@ -785,10 +793,6 @@ static void ub_rw_cmd_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd) scsi_status = 0; } else { if (cmd->act_len != cmd->len) { - if ((cmd->key == MEDIUM_ERROR || - cmd->key == UNIT_ATTENTION) && - ub_rw_cmd_retry(sc, lun, urq, cmd) == 0) - return; scsi_status = SAM_STAT_CHECK_CONDITION; } else { scsi_status = 0; @@ -804,7 +808,10 @@ static void ub_rw_cmd_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd) else scsi_status = DID_ERROR << 16; } else { - if (cmd->error == -EIO) { + if (cmd->error == -EIO && + (cmd->key == 0 || + cmd->key == MEDIUM_ERROR || + cmd->key == UNIT_ATTENTION)) { if (ub_rw_cmd_retry(sc, lun, urq, cmd) == 0) return; } @@ -1259,14 +1266,19 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd) return; } - len = le32_to_cpu(bcs->Residue); - if (len != cmd->len - cmd->act_len) { - /* - * It is all right to transfer less, the caller has - * to check. But it's not all right if the device - * counts disagree with our counts. - */ - goto Bad_End; + if (!sc->bad_resid) { + len = le32_to_cpu(bcs->Residue); + if (len != cmd->len - cmd->act_len) { + /* + * Only start ignoring if this cmd ended well. + */ + if (cmd->len == cmd->act_len) { + printk(KERN_NOTICE "%s: " + "bad residual %d of %d, ignoring\n", + sc->name, len, cmd->len); + sc->bad_resid = 1; + } + } } switch (bcs->Status) { @@ -1297,8 +1309,7 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd) ub_state_done(sc, cmd, -EIO); } else { - printk(KERN_WARNING "%s: " - "wrong command state %d\n", + printk(KERN_WARNING "%s: wrong command state %d\n", sc->name, cmd->state); ub_state_done(sc, cmd, -EINVAL); return; @@ -1336,7 +1347,10 @@ static void ub_data_start(struct ub_dev *sc, struct ub_scsi_cmd *cmd) return; } - sc->work_timer.expires = jiffies + UB_DATA_TIMEOUT; + if (cmd->timeo) + sc->work_timer.expires = jiffies + cmd->timeo; + else + sc->work_timer.expires = jiffies + UB_DATA_TIMEOUT; add_timer(&sc->work_timer); cmd->state = UB_CMDST_DATA; @@ -1376,7 +1390,10 @@ static int __ub_state_stat(struct ub_dev *sc, struct ub_scsi_cmd *cmd) return -1; } - sc->work_timer.expires = jiffies + UB_STAT_TIMEOUT; + if (cmd->timeo) + sc->work_timer.expires = jiffies + cmd->timeo; + else + sc->work_timer.expires = jiffies + UB_STAT_TIMEOUT; add_timer(&sc->work_timer); return 0; } @@ -1515,8 +1532,7 @@ static void ub_top_sense_done(struct ub_dev *sc, struct ub_scsi_cmd *scmd) return; } if (cmd->state != UB_CMDST_SENSE) { - printk(KERN_WARNING "%s: " - "sense done with bad cmd state %d\n", + printk(KERN_WARNING "%s: sense done with bad cmd state %d\n", sc->name, cmd->state); return; } @@ -1720,7 +1736,7 @@ static int ub_bd_ioctl(struct inode *inode, struct file *filp, } /* - * This is called once a new disk was seen by the block layer or by ub_probe(). + * This is called by check_disk_change if we reported a media change. * The main onjective here is to discover the features of the media such as * the capacity, read-only status, etc. USB storage generally does not * need to be spun up, but if we needed it, this would be the place. @@ -2136,8 +2152,7 @@ static int ub_get_pipes(struct ub_dev *sc, struct usb_device *dev, } if (ep_in == NULL || ep_out == NULL) { - printk(KERN_NOTICE "%s: failed endpoint check\n", - sc->name); + printk(KERN_NOTICE "%s: failed endpoint check\n", sc->name); return -ENODEV; } @@ -2354,7 +2369,7 @@ static void ub_disconnect(struct usb_interface *intf) spin_unlock_irqrestore(&ub_lock, flags); /* - * Fence stall clearnings, operations triggered by unlinkings and so on. + * Fence stall clearings, operations triggered by unlinkings and so on. * We do not attempt to unlink any URBs, because we do not trust the * unlink paths in HC drivers. Also, we get -84 upon disconnect anyway. */ @@ -2417,7 +2432,7 @@ static void ub_disconnect(struct usb_interface *intf) spin_unlock_irqrestore(sc->lock, flags); /* - * There is virtually no chance that other CPU runs times so long + * There is virtually no chance that other CPU runs a timeout so long * after ub_urb_complete should have called del_timer, but only if HCD * didn't forget to deliver a callback on unlink. */ diff --git a/drivers/block/viodasd.c b/drivers/block/viodasd.c index ebfe038d859e..f1c8feb5510b 100644 --- a/drivers/block/viodasd.c +++ b/drivers/block/viodasd.c @@ -3,7 +3,7 @@ * Authors: Dave Boutcher <boutcher@us.ibm.com> * Ryan Arnold <ryanarn@us.ibm.com> * Colin Devilbiss <devilbis@us.ibm.com> - * Stephen Rothwell <sfr@au1.ibm.com> + * Stephen Rothwell * * (C) Copyright 2000-2004 IBM Corporation * diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index 0cfbe8c594a5..dd7ea203f940 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c @@ -35,7 +35,7 @@ struct virtblk_req struct list_head list; struct request *req; struct virtio_blk_outhdr out_hdr; - struct virtio_blk_inhdr in_hdr; + u8 status; }; static void blk_done(struct virtqueue *vq) @@ -48,7 +48,7 @@ static void blk_done(struct virtqueue *vq) spin_lock_irqsave(&vblk->lock, flags); while ((vbr = vblk->vq->vq_ops->get_buf(vblk->vq, &len)) != NULL) { int uptodate; - switch (vbr->in_hdr.status) { + switch (vbr->status) { case VIRTIO_BLK_S_OK: uptodate = 1; break; @@ -101,7 +101,7 @@ static bool do_req(struct request_queue *q, struct virtio_blk *vblk, sg_init_table(vblk->sg, VIRTIO_MAX_SG); sg_set_buf(&vblk->sg[0], &vbr->out_hdr, sizeof(vbr->out_hdr)); num = blk_rq_map_sg(q, vbr->req, vblk->sg+1); - sg_set_buf(&vblk->sg[num+1], &vbr->in_hdr, sizeof(vbr->in_hdr)); + sg_set_buf(&vblk->sg[num+1], &vbr->status, sizeof(vbr->status)); if (rq_data_dir(vbr->req) == WRITE) { vbr->out_hdr.type |= VIRTIO_BLK_T_OUT; @@ -157,10 +157,25 @@ static int virtblk_ioctl(struct inode *inode, struct file *filp, /* We provide getgeo only to please some old bootloader/partitioning tools */ static int virtblk_getgeo(struct block_device *bd, struct hd_geometry *geo) { - /* some standard values, similar to sd */ - geo->heads = 1 << 6; - geo->sectors = 1 << 5; - geo->cylinders = get_capacity(bd->bd_disk) >> 11; + struct virtio_blk *vblk = bd->bd_disk->private_data; + struct virtio_blk_geometry vgeo; + int err; + + /* see if the host passed in geometry config */ + err = virtio_config_val(vblk->vdev, VIRTIO_BLK_F_GEOMETRY, + offsetof(struct virtio_blk_config, geometry), + &vgeo); + + if (!err) { + geo->heads = vgeo.heads; + geo->sectors = vgeo.sectors; + geo->cylinders = vgeo.cylinders; + } else { + /* some standard values, similar to sd */ + geo->heads = 1 << 6; + geo->sectors = 1 << 5; + geo->cylinders = get_capacity(bd->bd_disk) >> 11; + } return 0; } @@ -242,12 +257,16 @@ static int virtblk_probe(struct virtio_device *vdev) index++; /* If barriers are supported, tell block layer that queue is ordered */ - if (vdev->config->feature(vdev, VIRTIO_BLK_F_BARRIER)) + if (virtio_has_feature(vdev, VIRTIO_BLK_F_BARRIER)) blk_queue_ordered(vblk->disk->queue, QUEUE_ORDERED_TAG, NULL); + /* If disk is read-only in the host, the guest should obey */ + if (virtio_has_feature(vdev, VIRTIO_BLK_F_RO)) + set_disk_ro(vblk->disk, 1); + /* Host must always specify the capacity. */ - __virtio_config_val(vdev, offsetof(struct virtio_blk_config, capacity), - &cap); + vdev->config->get(vdev, offsetof(struct virtio_blk_config, capacity), + &cap, sizeof(cap)); /* If capacity is too big, truncate with warning. */ if ((sector_t)cap != cap) { @@ -289,7 +308,6 @@ out: static void virtblk_remove(struct virtio_device *vdev) { struct virtio_blk *vblk = vdev->priv; - int major = vblk->disk->major; /* Nothing should be pending. */ BUG_ON(!list_empty(&vblk->reqs)); @@ -297,9 +315,9 @@ static void virtblk_remove(struct virtio_device *vdev) /* Stop all the virtqueues. */ vdev->config->reset(vdev); + del_gendisk(vblk->disk); blk_cleanup_queue(vblk->disk->queue); put_disk(vblk->disk); - unregister_blkdev(major, "virtblk"); mempool_destroy(vblk->pool); vdev->config->del_vq(vblk->vq); kfree(vblk); @@ -310,7 +328,14 @@ static struct virtio_device_id id_table[] = { { 0 }, }; +static unsigned int features[] = { + VIRTIO_BLK_F_BARRIER, VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_F_SIZE_MAX, + VIRTIO_BLK_F_GEOMETRY, VIRTIO_BLK_F_RO, +}; + static struct virtio_driver virtio_blk = { + .feature_table = features, + .feature_table_size = ARRAY_SIZE(features), .driver.name = KBUILD_MODNAME, .driver.owner = THIS_MODULE, .id_table = id_table, diff --git a/drivers/block/z2ram.c b/drivers/block/z2ram.c index 2d5853cbd4b0..be20a67f1fa8 100644 --- a/drivers/block/z2ram.c +++ b/drivers/block/z2ram.c @@ -332,7 +332,7 @@ z2_init(void) int ret; if (!MACH_IS_AMIGA) - return -ENXIO; + return -ENODEV; ret = -EBUSY; if (register_blkdev(Z2RAM_MAJOR, DEVICE_NAME)) |