diff mbox

[v3,7/9] dax: report bytes remaining in dax_iomap_actor()

Message ID 152539240242.31796.4162676712193177396.stgit@dwillia2-desk3.amr.corp.intel.com (mailing list archive)
State New, archived
Headers show

Commit Message

Dan Williams May 4, 2018, 12:06 a.m. UTC
In preparation for protecting the dax read(2) path from media errors
with copy_to_iter_mcsafe() (via dax_copy_to_iter()), convert the
implementation to report the bytes successfully transferred.

Cc: <x86@kernel.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Tony Luck <tony.luck@intel.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
---
 fs/dax.c |   20 +++++++++++---------
 1 file changed, 11 insertions(+), 9 deletions(-)

Comments

Ross Zwisler May 23, 2018, 4:34 p.m. UTC | #1
On Thu, May 03, 2018 at 05:06:42PM -0700, Dan Williams wrote:
> In preparation for protecting the dax read(2) path from media errors
> with copy_to_iter_mcsafe() (via dax_copy_to_iter()), convert the
> implementation to report the bytes successfully transferred.
> 
> Cc: <x86@kernel.org>
> Cc: Ingo Molnar <mingo@redhat.com>
> Cc: Borislav Petkov <bp@alien8.de>
> Cc: Tony Luck <tony.luck@intel.com>
> Cc: Al Viro <viro@zeniv.linux.org.uk>
> Cc: Thomas Gleixner <tglx@linutronix.de>
> Cc: Andy Lutomirski <luto@amacapital.net>
> Cc: Peter Zijlstra <peterz@infradead.org>
> Cc: Andrew Morton <akpm@linux-foundation.org>
> Cc: Linus Torvalds <torvalds@linux-foundation.org>
> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
> ---
>  fs/dax.c |   20 +++++++++++---------
>  1 file changed, 11 insertions(+), 9 deletions(-)
> 
> diff --git a/fs/dax.c b/fs/dax.c
> index a64afdf7ec0d..34a2d435ae4b 100644
> --- a/fs/dax.c
> +++ b/fs/dax.c
> @@ -991,6 +991,7 @@ dax_iomap_actor(struct inode *inode, loff_t pos, loff_t length, void *data,
>  	struct iov_iter *iter = data;
>  	loff_t end = pos + length, done = 0;
>  	ssize_t ret = 0;
> +	size_t xfer;
>  	int id;
>  
>  	if (iov_iter_rw(iter) == READ) {
> @@ -1054,19 +1055,20 @@ dax_iomap_actor(struct inode *inode, loff_t pos, loff_t length, void *data,
>  		 * vfs_write(), depending on which operation we are doing.
>  		 */
>  		if (iov_iter_rw(iter) == WRITE)
> -			map_len = dax_copy_from_iter(dax_dev, pgoff, kaddr,
> +			xfer = dax_copy_from_iter(dax_dev, pgoff, kaddr,
>  					map_len, iter);
>  		else
> -			map_len = dax_copy_to_iter(dax_dev, pgoff, kaddr,
> +			xfer = dax_copy_to_iter(dax_dev, pgoff, kaddr,
>  					map_len, iter);
> -		if (map_len <= 0) {
> -			ret = map_len ? map_len : -EFAULT;
> -			break;
> -		}
>  
> -		pos += map_len;
> -		length -= map_len;
> -		done += map_len;
> +		pos += xfer;
> +		length -= xfer;
> +		done += xfer;
> +
> +		if (xfer == 0)
> +			ret = -EFAULT;
> +		if (xfer < map_len)
> +			break;

I'm confused by this error handling.  So if we hit an error on a given iov and
we don't transfer the expected number of bytes, we have two cases:

1) We transferred *something* on this iov but not everything - return success.
2) We didn't transfer anything on this iov - return -EFAULT.

Both of these are true regardless of data transferred on previous iovs.

Why the distinction?  If a given iov is interrupted, regardless of whether it
transferred 0 bytes or 1, shouldn't the error path be the same?

>  	}
>  	dax_read_unlock(id);
Dan Williams May 23, 2018, 4:39 p.m. UTC | #2
On Wed, May 23, 2018 at 9:34 AM, Ross Zwisler
<ross.zwisler@linux.intel.com> wrote:
> On Thu, May 03, 2018 at 05:06:42PM -0700, Dan Williams wrote:
>> In preparation for protecting the dax read(2) path from media errors
>> with copy_to_iter_mcsafe() (via dax_copy_to_iter()), convert the
>> implementation to report the bytes successfully transferred.
>>
>> Cc: <x86@kernel.org>
>> Cc: Ingo Molnar <mingo@redhat.com>
>> Cc: Borislav Petkov <bp@alien8.de>
>> Cc: Tony Luck <tony.luck@intel.com>
>> Cc: Al Viro <viro@zeniv.linux.org.uk>
>> Cc: Thomas Gleixner <tglx@linutronix.de>
>> Cc: Andy Lutomirski <luto@amacapital.net>
>> Cc: Peter Zijlstra <peterz@infradead.org>
>> Cc: Andrew Morton <akpm@linux-foundation.org>
>> Cc: Linus Torvalds <torvalds@linux-foundation.org>
>> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
>> ---
>>  fs/dax.c |   20 +++++++++++---------
>>  1 file changed, 11 insertions(+), 9 deletions(-)
>>
>> diff --git a/fs/dax.c b/fs/dax.c
>> index a64afdf7ec0d..34a2d435ae4b 100644
>> --- a/fs/dax.c
>> +++ b/fs/dax.c
>> @@ -991,6 +991,7 @@ dax_iomap_actor(struct inode *inode, loff_t pos, loff_t length, void *data,
>>       struct iov_iter *iter = data;
>>       loff_t end = pos + length, done = 0;
>>       ssize_t ret = 0;
>> +     size_t xfer;
>>       int id;
>>
>>       if (iov_iter_rw(iter) == READ) {
>> @@ -1054,19 +1055,20 @@ dax_iomap_actor(struct inode *inode, loff_t pos, loff_t length, void *data,
>>                * vfs_write(), depending on which operation we are doing.
>>                */
>>               if (iov_iter_rw(iter) == WRITE)
>> -                     map_len = dax_copy_from_iter(dax_dev, pgoff, kaddr,
>> +                     xfer = dax_copy_from_iter(dax_dev, pgoff, kaddr,
>>                                       map_len, iter);
>>               else
>> -                     map_len = dax_copy_to_iter(dax_dev, pgoff, kaddr,
>> +                     xfer = dax_copy_to_iter(dax_dev, pgoff, kaddr,
>>                                       map_len, iter);
>> -             if (map_len <= 0) {
>> -                     ret = map_len ? map_len : -EFAULT;
>> -                     break;
>> -             }
>>
>> -             pos += map_len;
>> -             length -= map_len;
>> -             done += map_len;
>> +             pos += xfer;
>> +             length -= xfer;
>> +             done += xfer;
>> +
>> +             if (xfer == 0)
>> +                     ret = -EFAULT;
>> +             if (xfer < map_len)
>> +                     break;
>
> I'm confused by this error handling.  So if we hit an error on a given iov and
> we don't transfer the expected number of bytes, we have two cases:
>
> 1) We transferred *something* on this iov but not everything - return success.
> 2) We didn't transfer anything on this iov - return -EFAULT.
>
> Both of these are true regardless of data transferred on previous iovs.
>
> Why the distinction?  If a given iov is interrupted, regardless of whether it
> transferred 0 bytes or 1, shouldn't the error path be the same?

This is is the semantics of read(2) / write(2). Quoting the pwrite man page:

       Note that is not an error for  a  successful  call  to
transfer  fewer  bytes  than
       requested (see read(2) and write(2)).
Ross Zwisler May 23, 2018, 4:47 p.m. UTC | #3
On Wed, May 23, 2018 at 09:39:04AM -0700, Dan Williams wrote:
> On Wed, May 23, 2018 at 9:34 AM, Ross Zwisler
> <ross.zwisler@linux.intel.com> wrote:
> > On Thu, May 03, 2018 at 05:06:42PM -0700, Dan Williams wrote:
> >> In preparation for protecting the dax read(2) path from media errors
> >> with copy_to_iter_mcsafe() (via dax_copy_to_iter()), convert the
> >> implementation to report the bytes successfully transferred.
> >>
> >> Cc: <x86@kernel.org>
> >> Cc: Ingo Molnar <mingo@redhat.com>
> >> Cc: Borislav Petkov <bp@alien8.de>
> >> Cc: Tony Luck <tony.luck@intel.com>
> >> Cc: Al Viro <viro@zeniv.linux.org.uk>
> >> Cc: Thomas Gleixner <tglx@linutronix.de>
> >> Cc: Andy Lutomirski <luto@amacapital.net>
> >> Cc: Peter Zijlstra <peterz@infradead.org>
> >> Cc: Andrew Morton <akpm@linux-foundation.org>
> >> Cc: Linus Torvalds <torvalds@linux-foundation.org>
> >> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
> >> ---
> >>  fs/dax.c |   20 +++++++++++---------
> >>  1 file changed, 11 insertions(+), 9 deletions(-)
> >>
> >> diff --git a/fs/dax.c b/fs/dax.c
> >> index a64afdf7ec0d..34a2d435ae4b 100644
> >> --- a/fs/dax.c
> >> +++ b/fs/dax.c
> >> @@ -991,6 +991,7 @@ dax_iomap_actor(struct inode *inode, loff_t pos, loff_t length, void *data,
> >>       struct iov_iter *iter = data;
> >>       loff_t end = pos + length, done = 0;
> >>       ssize_t ret = 0;
> >> +     size_t xfer;
> >>       int id;
> >>
> >>       if (iov_iter_rw(iter) == READ) {
> >> @@ -1054,19 +1055,20 @@ dax_iomap_actor(struct inode *inode, loff_t pos, loff_t length, void *data,
> >>                * vfs_write(), depending on which operation we are doing.
> >>                */
> >>               if (iov_iter_rw(iter) == WRITE)
> >> -                     map_len = dax_copy_from_iter(dax_dev, pgoff, kaddr,
> >> +                     xfer = dax_copy_from_iter(dax_dev, pgoff, kaddr,
> >>                                       map_len, iter);
> >>               else
> >> -                     map_len = dax_copy_to_iter(dax_dev, pgoff, kaddr,
> >> +                     xfer = dax_copy_to_iter(dax_dev, pgoff, kaddr,
> >>                                       map_len, iter);
> >> -             if (map_len <= 0) {
> >> -                     ret = map_len ? map_len : -EFAULT;
> >> -                     break;
> >> -             }
> >>
> >> -             pos += map_len;
> >> -             length -= map_len;
> >> -             done += map_len;
> >> +             pos += xfer;
> >> +             length -= xfer;
> >> +             done += xfer;
> >> +
> >> +             if (xfer == 0)
> >> +                     ret = -EFAULT;
> >> +             if (xfer < map_len)
> >> +                     break;
> >
> > I'm confused by this error handling.  So if we hit an error on a given iov and
> > we don't transfer the expected number of bytes, we have two cases:
> >
> > 1) We transferred *something* on this iov but not everything - return success.
> > 2) We didn't transfer anything on this iov - return -EFAULT.
> >
> > Both of these are true regardless of data transferred on previous iovs.
> >
> > Why the distinction?  If a given iov is interrupted, regardless of whether it
> > transferred 0 bytes or 1, shouldn't the error path be the same?
> 
> This is is the semantics of read(2) / write(2). Quoting the pwrite man page:
> 
>        Note that is not an error for  a  successful  call  to
> transfer  fewer  bytes  than
>        requested (see read(2) and write(2)).

Consider this case:

I have 4 IOVs, each of a full page.  The first three transfer their full page,
but on the third we hit an error.

If we transferred 0 bytes in the fourth page, we'll return -EFAULT.

If we transferred 1 byte in the fourth page, we'll return the total length
transferred, so 3 pages + 1 byte.

Why?  pwrite(2) says it returns the number of bytes written, which can be less
than the total requested.  Why not just return the length transferred in both
cases, instead of returning -EFAULT for one of them?
Dan Williams May 23, 2018, 4:53 p.m. UTC | #4
On Wed, May 23, 2018 at 9:47 AM, Ross Zwisler
<ross.zwisler@linux.intel.com> wrote:
> On Wed, May 23, 2018 at 09:39:04AM -0700, Dan Williams wrote:
>> On Wed, May 23, 2018 at 9:34 AM, Ross Zwisler
>> <ross.zwisler@linux.intel.com> wrote:
>> > On Thu, May 03, 2018 at 05:06:42PM -0700, Dan Williams wrote:
>> >> In preparation for protecting the dax read(2) path from media errors
>> >> with copy_to_iter_mcsafe() (via dax_copy_to_iter()), convert the
>> >> implementation to report the bytes successfully transferred.
>> >>
>> >> Cc: <x86@kernel.org>
>> >> Cc: Ingo Molnar <mingo@redhat.com>
>> >> Cc: Borislav Petkov <bp@alien8.de>
>> >> Cc: Tony Luck <tony.luck@intel.com>
>> >> Cc: Al Viro <viro@zeniv.linux.org.uk>
>> >> Cc: Thomas Gleixner <tglx@linutronix.de>
>> >> Cc: Andy Lutomirski <luto@amacapital.net>
>> >> Cc: Peter Zijlstra <peterz@infradead.org>
>> >> Cc: Andrew Morton <akpm@linux-foundation.org>
>> >> Cc: Linus Torvalds <torvalds@linux-foundation.org>
>> >> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
>> >> ---
>> >>  fs/dax.c |   20 +++++++++++---------
>> >>  1 file changed, 11 insertions(+), 9 deletions(-)
>> >>
>> >> diff --git a/fs/dax.c b/fs/dax.c
>> >> index a64afdf7ec0d..34a2d435ae4b 100644
>> >> --- a/fs/dax.c
>> >> +++ b/fs/dax.c
>> >> @@ -991,6 +991,7 @@ dax_iomap_actor(struct inode *inode, loff_t pos, loff_t length, void *data,
>> >>       struct iov_iter *iter = data;
>> >>       loff_t end = pos + length, done = 0;
>> >>       ssize_t ret = 0;
>> >> +     size_t xfer;
>> >>       int id;
>> >>
>> >>       if (iov_iter_rw(iter) == READ) {
>> >> @@ -1054,19 +1055,20 @@ dax_iomap_actor(struct inode *inode, loff_t pos, loff_t length, void *data,
>> >>                * vfs_write(), depending on which operation we are doing.
>> >>                */
>> >>               if (iov_iter_rw(iter) == WRITE)
>> >> -                     map_len = dax_copy_from_iter(dax_dev, pgoff, kaddr,
>> >> +                     xfer = dax_copy_from_iter(dax_dev, pgoff, kaddr,
>> >>                                       map_len, iter);
>> >>               else
>> >> -                     map_len = dax_copy_to_iter(dax_dev, pgoff, kaddr,
>> >> +                     xfer = dax_copy_to_iter(dax_dev, pgoff, kaddr,
>> >>                                       map_len, iter);
>> >> -             if (map_len <= 0) {
>> >> -                     ret = map_len ? map_len : -EFAULT;
>> >> -                     break;
>> >> -             }
>> >>
>> >> -             pos += map_len;
>> >> -             length -= map_len;
>> >> -             done += map_len;
>> >> +             pos += xfer;
>> >> +             length -= xfer;
>> >> +             done += xfer;
>> >> +
>> >> +             if (xfer == 0)
>> >> +                     ret = -EFAULT;
>> >> +             if (xfer < map_len)
>> >> +                     break;
>> >
>> > I'm confused by this error handling.  So if we hit an error on a given iov and
>> > we don't transfer the expected number of bytes, we have two cases:
>> >
>> > 1) We transferred *something* on this iov but not everything - return success.
>> > 2) We didn't transfer anything on this iov - return -EFAULT.
>> >
>> > Both of these are true regardless of data transferred on previous iovs.
>> >
>> > Why the distinction?  If a given iov is interrupted, regardless of whether it
>> > transferred 0 bytes or 1, shouldn't the error path be the same?
>>
>> This is is the semantics of read(2) / write(2). Quoting the pwrite man page:
>>
>>        Note that is not an error for  a  successful  call  to
>> transfer  fewer  bytes  than
>>        requested (see read(2) and write(2)).
>
> Consider this case:
>
> I have 4 IOVs, each of a full page.  The first three transfer their full page,
> but on the third we hit an error.
>
> If we transferred 0 bytes in the fourth page, we'll return -EFAULT.
>
> If we transferred 1 byte in the fourth page, we'll return the total length
> transferred, so 3 pages + 1 byte.
>
> Why?  pwrite(2) says it returns the number of bytes written, which can be less
> than the total requested.  Why not just return the length transferred in both
> cases, instead of returning -EFAULT for one of them?

Ah, now I see. Yes, that's a bug. Once we have successfully completed
any iovec we should be returning bytes transferred not -EFAULT.
Ross Zwisler May 23, 2018, 5:04 p.m. UTC | #5
On Wed, May 23, 2018 at 09:53:38AM -0700, Dan Williams wrote:
> On Wed, May 23, 2018 at 9:47 AM, Ross Zwisler
> <ross.zwisler@linux.intel.com> wrote:
> > On Wed, May 23, 2018 at 09:39:04AM -0700, Dan Williams wrote:
> >> On Wed, May 23, 2018 at 9:34 AM, Ross Zwisler
> >> <ross.zwisler@linux.intel.com> wrote:
> >> > On Thu, May 03, 2018 at 05:06:42PM -0700, Dan Williams wrote:
> >> >> In preparation for protecting the dax read(2) path from media errors
> >> >> with copy_to_iter_mcsafe() (via dax_copy_to_iter()), convert the
> >> >> implementation to report the bytes successfully transferred.
> >> >>
> >> >> Cc: <x86@kernel.org>
> >> >> Cc: Ingo Molnar <mingo@redhat.com>
> >> >> Cc: Borislav Petkov <bp@alien8.de>
> >> >> Cc: Tony Luck <tony.luck@intel.com>
> >> >> Cc: Al Viro <viro@zeniv.linux.org.uk>
> >> >> Cc: Thomas Gleixner <tglx@linutronix.de>
> >> >> Cc: Andy Lutomirski <luto@amacapital.net>
> >> >> Cc: Peter Zijlstra <peterz@infradead.org>
> >> >> Cc: Andrew Morton <akpm@linux-foundation.org>
> >> >> Cc: Linus Torvalds <torvalds@linux-foundation.org>
> >> >> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
> >> >> ---
> >> >>  fs/dax.c |   20 +++++++++++---------
> >> >>  1 file changed, 11 insertions(+), 9 deletions(-)
> >> >>
> >> >> diff --git a/fs/dax.c b/fs/dax.c
> >> >> index a64afdf7ec0d..34a2d435ae4b 100644
> >> >> --- a/fs/dax.c
> >> >> +++ b/fs/dax.c
> >> >> @@ -991,6 +991,7 @@ dax_iomap_actor(struct inode *inode, loff_t pos, loff_t length, void *data,
> >> >>       struct iov_iter *iter = data;
> >> >>       loff_t end = pos + length, done = 0;
> >> >>       ssize_t ret = 0;
> >> >> +     size_t xfer;
> >> >>       int id;
> >> >>
> >> >>       if (iov_iter_rw(iter) == READ) {
> >> >> @@ -1054,19 +1055,20 @@ dax_iomap_actor(struct inode *inode, loff_t pos, loff_t length, void *data,
> >> >>                * vfs_write(), depending on which operation we are doing.
> >> >>                */
> >> >>               if (iov_iter_rw(iter) == WRITE)
> >> >> -                     map_len = dax_copy_from_iter(dax_dev, pgoff, kaddr,
> >> >> +                     xfer = dax_copy_from_iter(dax_dev, pgoff, kaddr,
> >> >>                                       map_len, iter);
> >> >>               else
> >> >> -                     map_len = dax_copy_to_iter(dax_dev, pgoff, kaddr,
> >> >> +                     xfer = dax_copy_to_iter(dax_dev, pgoff, kaddr,
> >> >>                                       map_len, iter);
> >> >> -             if (map_len <= 0) {
> >> >> -                     ret = map_len ? map_len : -EFAULT;
> >> >> -                     break;
> >> >> -             }
> >> >>
> >> >> -             pos += map_len;
> >> >> -             length -= map_len;
> >> >> -             done += map_len;
> >> >> +             pos += xfer;
> >> >> +             length -= xfer;
> >> >> +             done += xfer;
> >> >> +
> >> >> +             if (xfer == 0)
> >> >> +                     ret = -EFAULT;
> >> >> +             if (xfer < map_len)
> >> >> +                     break;
> >> >
> >> > I'm confused by this error handling.  So if we hit an error on a given iov and
> >> > we don't transfer the expected number of bytes, we have two cases:
> >> >
> >> > 1) We transferred *something* on this iov but not everything - return success.
> >> > 2) We didn't transfer anything on this iov - return -EFAULT.
> >> >
> >> > Both of these are true regardless of data transferred on previous iovs.
> >> >
> >> > Why the distinction?  If a given iov is interrupted, regardless of whether it
> >> > transferred 0 bytes or 1, shouldn't the error path be the same?
> >>
> >> This is is the semantics of read(2) / write(2). Quoting the pwrite man page:
> >>
> >>        Note that is not an error for  a  successful  call  to
> >> transfer  fewer  bytes  than
> >>        requested (see read(2) and write(2)).
> >
> > Consider this case:
> >
> > I have 4 IOVs, each of a full page.  The first three transfer their full page,
> > but on the third we hit an error.
> >
> > If we transferred 0 bytes in the fourth page, we'll return -EFAULT.
> >
> > If we transferred 1 byte in the fourth page, we'll return the total length
> > transferred, so 3 pages + 1 byte.
> >
> > Why?  pwrite(2) says it returns the number of bytes written, which can be less
> > than the total requested.  Why not just return the length transferred in both
> > cases, instead of returning -EFAULT for one of them?
> 
> Ah, now I see. Yes, that's a bug. Once we have successfully completed
> any iovec we should be returning bytes transferred not -EFAULT.

Actually, your code is fine.  This is handled by the:

 return done ? done : ret;

at the end of the function.  So if we've transferred any data at all, we'll
return the number of bytes transferred, and if we didn't we'll return -EFAULT
because 0 is the special case which means EOF according to pread(2)/pwrite(2).

Looks good, then.  Thanks for answering my questions. 

Reviewed-by: Ross Zwisler <ross.zwisler@linux.intel.com>
diff mbox

Patch

diff --git a/fs/dax.c b/fs/dax.c
index a64afdf7ec0d..34a2d435ae4b 100644
--- a/fs/dax.c
+++ b/fs/dax.c
@@ -991,6 +991,7 @@  dax_iomap_actor(struct inode *inode, loff_t pos, loff_t length, void *data,
 	struct iov_iter *iter = data;
 	loff_t end = pos + length, done = 0;
 	ssize_t ret = 0;
+	size_t xfer;
 	int id;
 
 	if (iov_iter_rw(iter) == READ) {
@@ -1054,19 +1055,20 @@  dax_iomap_actor(struct inode *inode, loff_t pos, loff_t length, void *data,
 		 * vfs_write(), depending on which operation we are doing.
 		 */
 		if (iov_iter_rw(iter) == WRITE)
-			map_len = dax_copy_from_iter(dax_dev, pgoff, kaddr,
+			xfer = dax_copy_from_iter(dax_dev, pgoff, kaddr,
 					map_len, iter);
 		else
-			map_len = dax_copy_to_iter(dax_dev, pgoff, kaddr,
+			xfer = dax_copy_to_iter(dax_dev, pgoff, kaddr,
 					map_len, iter);
-		if (map_len <= 0) {
-			ret = map_len ? map_len : -EFAULT;
-			break;
-		}
 
-		pos += map_len;
-		length -= map_len;
-		done += map_len;
+		pos += xfer;
+		length -= xfer;
+		done += xfer;
+
+		if (xfer == 0)
+			ret = -EFAULT;
+		if (xfer < map_len)
+			break;
 	}
 	dax_read_unlock(id);