From: "Darrick J. Wong" <darrick.wong@oracle.com>
To: Dave Chinner <david@fromorbit.com>
Cc: linux-xfs@vger.kernel.org
Subject: Re: [PATCH 15/27] libxfs: introduce userspace buftarg infrastructure
Date: Thu, 15 Oct 2020 10:16:25 -0700 [thread overview]
Message-ID: <20201015171625.GW9832@magnolia> (raw)
In-Reply-To: <20201015072155.1631135-16-david@fromorbit.com>
On Thu, Oct 15, 2020 at 06:21:43PM +1100, Dave Chinner wrote:
> From: Dave Chinner <dchinner@redhat.com>
>
> Move the uncached buffer IO API into the xfs_buftarg.h and the local
> buftarg implementation. The uncached buffer IO implementation is
> different between kernel and userspace, but the API is the same.
> Hence implement it via the buftarg abstraction.
>
> Pull the "alloc_write_buf()" function from mkfs up into the API as
> xfs_buf_get_uncached_daddr() so that it can be used in other places
> that need the same functionality.
But this doesn't actually remove alloc_write_buf from xfs_mkfs.c...
> The API movement still uses the existing raw buffer allocation
> and read IO implementation. This requires us to temporarily export
> the the prototypes for these functions in xfs_buftarg.h. They will
> go away once the buftarg has it's own buffer allocation and IO
> engine implementations.
>
> Signed-off-by: Dave Chinner <dchinner@redhat.com>
> ---
> libxfs/buftarg.c | 90 ++++++++++++++++++++++++++++++++++++++++++++
> libxfs/libxfs_io.h | 22 +----------
> libxfs/rdwr.c | 88 ++++++-------------------------------------
> libxfs/xfs_buftarg.h | 39 +++++++++++++++++++
> mkfs/xfs_mkfs.c | 29 ++++++++++----
> 5 files changed, 164 insertions(+), 104 deletions(-)
>
> diff --git a/libxfs/buftarg.c b/libxfs/buftarg.c
> index d4bcb2936f01..2a0aad2e0f8c 100644
> --- a/libxfs/buftarg.c
> +++ b/libxfs/buftarg.c
> @@ -97,3 +97,93 @@ xfs_buftarg_free(
> platform_flush_device(btp->bt_fd, btp->bt_bdev);
> free(btp);
> }
> +
> +/*
> + * Allocate an uncached buffer that points at daddr. The refcount will be 1,
> + * and the cache node hash list will be empty to indicate that it's uncached.
> + */
> +int
> +xfs_buf_get_uncached_daddr(
> + struct xfs_buftarg *target,
> + xfs_daddr_t daddr,
> + size_t bblen,
> + struct xfs_buf **bpp)
> +{
> + struct xfs_buf *bp;
> +
> + bp = libxfs_getbufr(target, daddr, bblen);
> + if (!bp)
> + return -ENOMEM;
> +
> + INIT_LIST_HEAD(&bp->b_node.cn_hash);
> + bp->b_node.cn_count = 1;
> + bp->b_bn = daddr;
> + bp->b_maps[0].bm_bn = daddr;
This function has all kinds of indent weirdness.
The general approach seems ok so far, but WIP indeed. :)
--D
> + *bpp = bp;
> + return 0;
> +}
> +
> +int
> +xfs_buf_read_uncached(
> + struct xfs_buftarg *target,
> + xfs_daddr_t daddr,
> + size_t bblen,
> + int flags,
> + struct xfs_buf **bpp,
> + const struct xfs_buf_ops *ops)
> +{
> + struct xfs_buf *bp;
> + int error;
> +
> +
> + error = xfs_buf_get_uncached(target, bblen, flags, &bp);
> + if (error)
> + return error;
> +
> + error = libxfs_readbufr(target, daddr, bp, bblen, flags);
> + if (error)
> + goto release_buf;
> +
> + error = libxfs_readbuf_verify(bp, ops);
> + if (error)
> + goto release_buf;
> +
> + *bpp = bp;
> + return 0;
> +
> +release_buf:
> + libxfs_buf_relse(bp);
> + return error;
> +}
> +
> +/*
> + * Return a buffer associated to external memory via xfs_buf_associate_memory()
> + * back to it's empty state.
> + */
> +void
> +xfs_buf_set_empty(
> + struct xfs_buf *bp,
> + size_t numblks)
> +{
> + bp->b_addr = NULL;
> + bp->b_length = numblks;
> +
> + ASSERT(bp->b_map_count == 1);
> + bp->b_bn = XFS_BUF_DADDR_NULL;
> + bp->b_maps[0].bm_bn = XFS_BUF_DADDR_NULL;
> + bp->b_maps[0].bm_len = bp->b_length;
> +}
> +
> +/*
> + * Associate external memory with an empty uncached buffer.
> + */
> +int
> +xfs_buf_associate_memory(
> + struct xfs_buf *bp,
> + void *mem,
> + size_t len)
> +{
> + bp->b_addr = mem;
> + bp->b_length = BTOBB(len);
> + return 0;
> +}
> diff --git a/libxfs/libxfs_io.h b/libxfs/libxfs_io.h
> index 0f9630e8e17a..7f8fd88f7de8 100644
> --- a/libxfs/libxfs_io.h
> +++ b/libxfs/libxfs_io.h
> @@ -61,7 +61,7 @@ struct xfs_buf {
> struct xfs_mount *b_mount;
> struct xfs_buf_map *b_maps;
> struct xfs_buf_map __b_map;
> - int b_nmaps;
> + int b_map_count;
> struct list_head b_list;
> };
>
> @@ -77,8 +77,6 @@ bool xfs_verify_magic16(struct xfs_buf *bp, __be16 dmagic);
>
> typedef unsigned int xfs_buf_flags_t;
>
> -#define XFS_BUF_DADDR_NULL ((xfs_daddr_t) (-1LL))
> -
> #define xfs_buf_offset(bp, offset) ((bp)->b_addr + (offset))
> #define XFS_BUF_ADDR(bp) ((bp)->b_bn)
>
> @@ -148,10 +146,6 @@ extern int libxfs_bcache_overflowed(void);
>
> /* Buffer (Raw) Interfaces */
> int libxfs_bwrite(struct xfs_buf *bp);
> -extern int libxfs_readbufr(struct xfs_buftarg *, xfs_daddr_t,
> - struct xfs_buf *, int, int);
> -extern int libxfs_readbufr_map(struct xfs_buftarg *, struct xfs_buf *, int);
> -
> extern int libxfs_device_zero(struct xfs_buftarg *, xfs_daddr_t, uint);
>
> extern int libxfs_bhash_size;
> @@ -170,26 +164,12 @@ xfs_buf_update_cksum(struct xfs_buf *bp, unsigned long cksum_offset)
> cksum_offset);
> }
>
> -static inline int
> -xfs_buf_associate_memory(struct xfs_buf *bp, void *mem, size_t len)
> -{
> - bp->b_addr = mem;
> - bp->b_length = BTOBB(len);
> - return 0;
> -}
> -
> static inline void
> xfs_buf_hold(struct xfs_buf *bp)
> {
> bp->b_node.cn_count++;
> }
>
> -int libxfs_buf_get_uncached(struct xfs_buftarg *targ, size_t bblen, int flags,
> - struct xfs_buf **bpp);
> -int libxfs_buf_read_uncached(struct xfs_buftarg *targ, xfs_daddr_t daddr,
> - size_t bblen, int flags, struct xfs_buf **bpp,
> - const struct xfs_buf_ops *ops);
> -
> /* Push a single buffer on a delwri queue. */
> static inline bool
> xfs_buf_delwri_queue(struct xfs_buf *bp, struct list_head *buffer_list)
> diff --git a/libxfs/rdwr.c b/libxfs/rdwr.c
> index 5ab1987eb0fe..3e755402b024 100644
> --- a/libxfs/rdwr.c
> +++ b/libxfs/rdwr.c
> @@ -249,7 +249,7 @@ __initbuf(struct xfs_buf *bp, struct xfs_buftarg *btp, xfs_daddr_t bno,
> INIT_LIST_HEAD(&bp->b_li_list);
>
> if (!bp->b_maps) {
> - bp->b_nmaps = 1;
> + bp->b_map_count = 1;
> bp->b_maps = &bp->__b_map;
> bp->b_maps[0].bm_bn = bp->b_bn;
> bp->b_maps[0].bm_len = bp->b_length;
> @@ -279,7 +279,7 @@ libxfs_initbuf_map(struct xfs_buf *bp, struct xfs_buftarg *btp,
> strerror(errno));
> exit(1);
> }
> - bp->b_nmaps = nmaps;
> + bp->b_map_count = nmaps;
>
> bytes = 0;
> for ( i = 0; i < nmaps; i++) {
> @@ -331,7 +331,7 @@ __libxfs_getbufr(int blen)
> return bp;
> }
>
> -static struct xfs_buf *
> +struct xfs_buf *
> libxfs_getbufr(struct xfs_buftarg *btp, xfs_daddr_t blkno, int bblen)
> {
> struct xfs_buf *bp;
> @@ -617,7 +617,7 @@ libxfs_readbufr_map(struct xfs_buftarg *btp, struct xfs_buf *bp, int flags)
>
> fd = libxfs_device_to_fd(btp->bt_bdev);
> buf = bp->b_addr;
> - for (i = 0; i < bp->b_nmaps; i++) {
> + for (i = 0; i < bp->b_map_count; i++) {
> off64_t offset = LIBXFS_BBTOOFF64(bp->b_maps[i].bm_bn);
> int len = BBTOB(bp->b_maps[i].bm_len);
>
> @@ -707,75 +707,6 @@ err:
> return error;
> }
>
> -/* Allocate a raw uncached buffer. */
> -static inline struct xfs_buf *
> -libxfs_getbufr_uncached(
> - struct xfs_buftarg *targ,
> - xfs_daddr_t daddr,
> - size_t bblen)
> -{
> - struct xfs_buf *bp;
> -
> - bp = libxfs_getbufr(targ, daddr, bblen);
> - if (!bp)
> - return NULL;
> -
> - INIT_LIST_HEAD(&bp->b_node.cn_hash);
> - bp->b_node.cn_count = 1;
> - return bp;
> -}
> -
> -/*
> - * Allocate an uncached buffer that points nowhere. The refcount will be 1,
> - * and the cache node hash list will be empty to indicate that it's uncached.
> - */
> -int
> -libxfs_buf_get_uncached(
> - struct xfs_buftarg *targ,
> - size_t bblen,
> - int flags,
> - struct xfs_buf **bpp)
> -{
> - *bpp = libxfs_getbufr_uncached(targ, XFS_BUF_DADDR_NULL, bblen);
> - return *bpp != NULL ? 0 : -ENOMEM;
> -}
> -
> -/*
> - * Allocate and read an uncached buffer. The refcount will be 1, and the cache
> - * node hash list will be empty to indicate that it's uncached.
> - */
> -int
> -libxfs_buf_read_uncached(
> - struct xfs_buftarg *targ,
> - xfs_daddr_t daddr,
> - size_t bblen,
> - int flags,
> - struct xfs_buf **bpp,
> - const struct xfs_buf_ops *ops)
> -{
> - struct xfs_buf *bp;
> - int error;
> -
> - *bpp = NULL;
> - bp = libxfs_getbufr_uncached(targ, daddr, bblen);
> - if (!bp)
> - return -ENOMEM;
> -
> - error = libxfs_readbufr(targ, daddr, bp, bblen, flags);
> - if (error)
> - goto err;
> -
> - error = libxfs_readbuf_verify(bp, ops);
> - if (error)
> - goto err;
> -
> - *bpp = bp;
> - return 0;
> -err:
> - libxfs_buf_relse(bp);
> - return error;
> -}
> -
> static int
> __write_buf(int fd, void *buf, int len, off64_t offset, int flags)
> {
> @@ -836,7 +767,7 @@ libxfs_bwrite(
> int i;
> void *buf = bp->b_addr;
>
> - for (i = 0; i < bp->b_nmaps; i++) {
> + for (i = 0; i < bp->b_map_count; i++) {
> off64_t offset = LIBXFS_BBTOOFF64(bp->b_maps[i].bm_bn);
> int len = BBTOB(bp->b_maps[i].bm_len);
>
> @@ -1207,6 +1138,7 @@ libxfs_log_clear(
> xfs_daddr_t blk;
> xfs_daddr_t end_blk;
> char *ptr;
> + int error;
>
> if (((btp && dptr) || (!btp && !dptr)) ||
> (btp && !btp->bt_bdev) || !fs_uuid)
> @@ -1236,7 +1168,9 @@ libxfs_log_clear(
> /* write out the first log record */
> ptr = dptr;
> if (btp) {
> - bp = libxfs_getbufr_uncached(btp, start, len);
> + error = xfs_buf_get_uncached_daddr(btp, start, len, &bp);
> + if (error)
> + return error;
> ptr = bp->b_addr;
> }
> libxfs_log_header(ptr, fs_uuid, version, sunit, fmt, lsn, tail_lsn,
> @@ -1284,7 +1218,9 @@ libxfs_log_clear(
>
> ptr = dptr;
> if (btp) {
> - bp = libxfs_getbufr_uncached(btp, blk, len);
> + error = xfs_buf_get_uncached_daddr(btp, blk, len, &bp);
> + if (error)
> + return error;
> ptr = bp->b_addr;
> }
> /*
> diff --git a/libxfs/xfs_buftarg.h b/libxfs/xfs_buftarg.h
> index 1bc3a4d0bc9c..5429c96c0547 100644
> --- a/libxfs/xfs_buftarg.h
> +++ b/libxfs/xfs_buftarg.h
> @@ -11,6 +11,8 @@ struct xfs_mount;
> struct xfs_buf;
> struct xfs_buf_ops;
>
> +#define XFS_BUF_DADDR_NULL ((xfs_daddr_t) (-1LL))
> +
> /*
> * The xfs_buftarg contains 2 notions of "sector size" -
> *
> @@ -52,4 +54,41 @@ int xfs_buftarg_setsize(struct xfs_buftarg *target, unsigned int size);
>
> #define xfs_getsize_buftarg(buftarg) block_size((buftarg)->bt_bdev)
>
> +/*
> + * Low level buftarg IO routines.
> + *
> + * This includes the uncached buffer IO API, as the memory management associated
> + * with uncached buffers is tightly tied to the kernel buffer implementation.
> + */
> +
> +void xfs_buf_set_empty(struct xfs_buf *bp, size_t numblks);
> +int xfs_buf_associate_memory(struct xfs_buf *bp, void *mem, size_t length);
> +
> +int xfs_buf_get_uncached_daddr(struct xfs_buftarg *target, xfs_daddr_t daddr,
> + size_t bblen, struct xfs_buf **bpp);
> +static inline int
> +xfs_buf_get_uncached(
> + struct xfs_buftarg *target,
> + size_t bblen,
> + int flags,
> + struct xfs_buf **bpp)
> +{
> + return xfs_buf_get_uncached_daddr(target, XFS_BUF_DADDR_NULL, bblen, bpp);
> +}
> +
> +int xfs_buf_read_uncached(struct xfs_buftarg *target, xfs_daddr_t daddr,
> + size_t bblen, int flags, struct xfs_buf **bpp,
> + const struct xfs_buf_ops *ops);
> +
> +/*
> + * Raw buffer access functions. These exist as temporary bridges for uncached IO
> + * that uses direct access to the buffers to submit IO. These will go away with
> + * the new buffer cache IO engine.
> + */
> +struct xfs_buf *libxfs_getbufr(struct xfs_buftarg *btp, xfs_daddr_t blkno,
> + int bblen);
> +int libxfs_readbufr(struct xfs_buftarg *, xfs_daddr_t, struct xfs_buf *, int,
> + int);
> +int libxfs_readbufr_map(struct xfs_buftarg *, struct xfs_buf *, int);
> +
> #endif /* __XFS_BUFTARG_H */
> diff --git a/mkfs/xfs_mkfs.c b/mkfs/xfs_mkfs.c
> index 794955a9624c..87e1881e3152 100644
> --- a/mkfs/xfs_mkfs.c
> +++ b/mkfs/xfs_mkfs.c
> @@ -3463,6 +3463,7 @@ prepare_devices(
> struct xfs_buf *buf;
> int whack_blks = BTOBB(WHACK_SIZE);
> int lsunit;
> + int error;
>
> /*
> * If there's an old XFS filesystem on the device with enough intact
> @@ -3496,8 +3497,10 @@ prepare_devices(
> * the end of the device. (MD sb is ~64k from the end, take out a wider
> * swath to be sure)
> */
> - buf = alloc_write_buf(mp->m_ddev_targp, (xi->dsize - whack_blks),
> - whack_blks);
> + error = xfs_buf_get_uncached_daddr(mp->m_ddev_targp,
> + (xi->dsize - whack_blks), whack_blks, &buf);
> + if (error)
> + goto out_error;
> memset(buf->b_addr, 0, WHACK_SIZE);
> libxfs_buf_mark_dirty(buf);
> libxfs_buf_relse(buf);
> @@ -3508,14 +3511,18 @@ prepare_devices(
> * swap (somewhere around the page size), jfs (32k),
> * ext[2,3] and reiserfs (64k) - and hopefully all else.
> */
> - buf = alloc_write_buf(mp->m_ddev_targp, 0, whack_blks);
> + error = xfs_buf_get_uncached_daddr(mp->m_ddev_targp, 0, whack_blks, &buf);
> + if (error)
> + goto out_error;
> memset(buf->b_addr, 0, WHACK_SIZE);
> libxfs_buf_mark_dirty(buf);
> libxfs_buf_relse(buf);
>
> /* OK, now write the superblock... */
> - buf = alloc_write_buf(mp->m_ddev_targp, XFS_SB_DADDR,
> - XFS_FSS_TO_BB(mp, 1));
> + error = xfs_buf_get_uncached_daddr(mp->m_ddev_targp, XFS_SB_DADDR,
> + XFS_FSS_TO_BB(mp, 1), &buf);
> + if (error)
> + goto out_error;
> buf->b_ops = &xfs_sb_buf_ops;
> memset(buf->b_addr, 0, cfg->sectorsize);
> libxfs_sb_to_disk(buf->b_addr, sbp);
> @@ -3536,14 +3543,22 @@ prepare_devices(
> /* finally, check we can write the last block in the realtime area */
> if (mp->m_rtdev_targp && mp->m_rtdev_targp->bt_bdev &&
> cfg->rtblocks > 0) {
> - buf = alloc_write_buf(mp->m_rtdev_targp,
> + error = xfs_buf_get_uncached_daddr(mp->m_rtdev_targp,
> XFS_FSB_TO_BB(mp, cfg->rtblocks - 1LL),
> - BTOBB(cfg->blocksize));
> + BTOBB(cfg->blocksize), &buf);
> + if (error)
> + goto out_error;
> memset(buf->b_addr, 0, cfg->blocksize);
> libxfs_buf_mark_dirty(buf);
> libxfs_buf_relse(buf);
> }
>
> + return;
> +
> +out_error:
> + fprintf(stderr, _("Could not get memory for buffer, err=%d\n"),
> + error);
> + exit(1);
> }
>
> static void
> --
> 2.28.0
>
next prev parent reply other threads:[~2020-10-15 17:16 UTC|newest]
Thread overview: 49+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-10-15 7:21 [PATCH 00/27] [RFC, WIP] xfsprogs: xfs_buf unification and AIO Dave Chinner
2020-10-15 7:21 ` [PATCH 01/27] xfsprogs: remove unused buffer tracing code Dave Chinner
2020-10-15 7:21 ` [PATCH 02/27] xfsprogs: remove unused IO_DEBUG functionality Dave Chinner
2020-11-16 2:31 ` Eric Sandeen
2020-10-15 7:21 ` [PATCH 03/27] libxfs: get rid of b_bcount from xfs_buf Dave Chinner
2020-11-23 19:53 ` Eric Sandeen
2020-10-15 7:21 ` [PATCH 04/27] libxfs: rename buftarg->dev to btdev Dave Chinner
2020-11-16 2:33 ` Eric Sandeen
2020-10-15 7:21 ` [PATCH 05/27] xfsprogs: get rid of ancient btree tracing fragments Dave Chinner
2020-11-16 2:35 ` Eric Sandeen
2020-10-15 7:21 ` [PATCH 06/27] xfsprogs: remove xfs_buf_t typedef Dave Chinner
2020-10-15 15:22 ` Darrick J. Wong
2020-10-15 20:54 ` Dave Chinner
2020-10-15 7:21 ` [PATCH 07/27] xfsprogs: introduce liburcu support Dave Chinner
2020-10-15 7:21 ` [PATCH 08/27] libxfs: add spinlock_t wrapper Dave Chinner
2020-10-15 7:21 ` [PATCH 09/27] atomic: convert to uatomic Dave Chinner
2020-10-15 7:21 ` [PATCH 10/27] libxfs: add kernel-compatible completion API Dave Chinner
2020-10-15 17:09 ` Darrick J. Wong
2020-10-19 22:21 ` Dave Chinner
2020-10-15 7:21 ` [PATCH 11/27] libxfs: add wrappers for kernel semaphores Dave Chinner
2020-10-15 7:21 ` [PATCH 12/27] xfsprogs: convert use-once buffer reads to uncached IO Dave Chinner
2020-10-15 17:12 ` Darrick J. Wong
2020-10-19 22:36 ` Dave Chinner
2020-10-15 7:21 ` [PATCH 13/27] libxfs: introduce userspace buftarg infrastructure Dave Chinner
2020-10-15 7:21 ` [PATCH 14/27] xfs: rename libxfs_buftarg_init to libxfs_open_devices() Dave Chinner
2020-10-15 7:21 ` [PATCH 15/27] libxfs: introduce userspace buftarg infrastructure Dave Chinner
2020-10-15 17:16 ` Darrick J. Wong [this message]
2020-10-15 7:21 ` [PATCH 16/27] libxfs: add a synchronous IO engine to the buftarg Dave Chinner
2020-10-15 7:21 ` [PATCH 17/27] xfsprogs: convert libxfs_readbufr to libxfs_buf_read_uncached Dave Chinner
2020-10-15 7:21 ` [PATCH 18/27] libxfs: convert libxfs_bwrite to buftarg IO Dave Chinner
2020-10-15 7:21 ` [PATCH 19/27] libxfs: add cache infrastructure to buftarg Dave Chinner
2020-10-15 7:21 ` [PATCH 20/27] libxfs: add internal lru to btcache Dave Chinner
2020-10-15 7:21 ` [PATCH 21/27] libxfs: Add kernel list_lru wrapper Dave Chinner
2020-10-15 7:21 ` [PATCH 22/27] libxfs: introduce new buffer cache infrastructure Dave Chinner
2020-10-15 17:46 ` Darrick J. Wong
2020-10-15 7:21 ` [PATCH 23/27] libxfs: use PSI information to detect memory pressure Dave Chinner
2020-10-15 17:56 ` Darrick J. Wong
2020-10-15 21:20 ` Dave Chinner
2020-10-15 7:21 ` [PATCH 24/27] libxfs: add a buftarg cache shrinker implementation Dave Chinner
2020-10-15 18:01 ` Darrick J. Wong
2020-10-15 21:33 ` Dave Chinner
2020-10-15 7:21 ` [PATCH 25/27] libxfs: switch buffer cache implementations Dave Chinner
2020-10-15 7:21 ` [PATCH 26/27] build: set platform_defs.h.in dependency correctly Dave Chinner
2020-10-15 7:21 ` [PATCH 27/27] libxfs: convert sync IO buftarg engine to AIO Dave Chinner
2020-10-15 18:26 ` Darrick J. Wong
2020-10-15 21:42 ` Dave Chinner
2020-10-15 7:29 ` [PATCH 00/27] [RFC, WIP] xfsprogs: xfs_buf unification and AIO Dave Chinner
2020-10-15 18:37 ` Darrick J. Wong
2020-10-15 22:35 ` Dave Chinner
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20201015171625.GW9832@magnolia \
--to=darrick.wong@oracle.com \
--cc=david@fromorbit.com \
--cc=linux-xfs@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).