V3: export opal_error_code() so that powernv_flash can be built=m
Hello,
Version one of this series ignored that OPAL may continue to use
buffers passed to it after Linux kfree()s the buffer. This version
addresses this, not in a particularly nice way - future work could
make this better. This version also includes a few cleanups and fixups
to powernv_flash driver one along the course of this work that I
thought I would just send.
The problem we're trying to solve here is that currently all users of
the opal-async calls must use wait_event(), this may be undesirable
when there is a userspace process behind the request for the opal
call, if OPAL takes too long to complete the call then hung task
warnings will appear.
In order to solve the problem callers should use
wait_event_interruptible(), due to the interruptible nature of this
call the opal-async infrastructure needs to track extra state
associated with each async token, this is prepared for in patch 6/10.
While I was working on the opal-async infrastructure improvements
Stewart fixed another problem and he relies on the corrected behaviour
of opal-async so I've sent it here.
Hello MTD folk, traditionally Michael Ellerman takes powernv_flash
driver patches through the powerpc tree, as always your feedback is
very welcome.
Thanks,
Cyril
Cyril Bur (9):
mtd: powernv_flash: Use WARN_ON_ONCE() rather than BUG_ON()
mtd: powernv_flash: Lock around concurrent access to OPAL
mtd: powernv_flash: Don't treat OPAL_SUCCESS as an error
mtd: powernv_flash: Remove pointless goto in driver init
powerpc/opal: Make __opal_async_{get,release}_token() static
powerpc/opal: Rework the opal-async interface
powerpc/opal: Add opal_async_wait_response_interruptible() to
opal-async
powerpc/powernv: Add OPAL_BUSY to opal_error_code()
mtd: powernv_flash: Use opal_async_wait_response_interruptible()
Stewart Smith (1):
powernv/opal-sensor: remove not needed lock
arch/powerpc/include/asm/opal.h | 4 +-
arch/powerpc/platforms/powernv/opal-async.c | 188 +++++++++++++++++++--------
arch/powerpc/platforms/powernv/opal-sensor.c | 17 +--
arch/powerpc/platforms/powernv/opal.c | 2 +
drivers/mtd/devices/powernv_flash.c | 66 +++++++---
5 files changed, 191 insertions(+), 86 deletions(-)
--
2.13.2
Also export opal_error_code() so that it can be used in modules
Signed-off-by: Cyril Bur <redacted>
---
arch/powerpc/platforms/powernv/opal.c | 2 ++
1 file changed, 2 insertions(+)
@@ -930,6 +930,7 @@ int opal_error_code(int rc)caseOPAL_PARAMETER:return-EINVAL;caseOPAL_ASYNC_COMPLETION:return-EINPROGRESS;+caseOPAL_BUSY:caseOPAL_BUSY_EVENT:return-EBUSY;caseOPAL_NO_MEM:return-ENOMEM;caseOPAL_PERMISSION:return-EPERM;
@@ -968,3 +969,4 @@ EXPORT_SYMBOL_GPL(opal_write_oppanel_async);/* Export this for KVM */EXPORT_SYMBOL_GPL(opal_int_set_mfrr);EXPORT_SYMBOL_GPL(opal_int_eoi);+EXPORT_SYMBOL_GPL(opal_error_code);
BUG_ON() should be reserved in situations where we can not longer
guarantee the integrity of the system. In the case where
powernv_flash_async_op() receives an impossible op, we can still
guarantee the integrity of the system.
Signed-off-by: Cyril Bur <redacted>
---
drivers/mtd/devices/powernv_flash.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
OPAL can only manage one flash access at a time and will return an
OPAL_BUSY error for each concurrent access to the flash. The simplest
way to prevent this from happening is with a mutex.
Signed-off-by: Cyril Bur <redacted>
---
drivers/mtd/devices/powernv_flash.c | 18 +++++++++++++++---
1 file changed, 15 insertions(+), 3 deletions(-)
@@ -59,12 +60,15 @@ static int powernv_flash_async_op(struct mtd_info *mtd, enum flash_op op,dev_dbg(dev,"%s(op=%d, offset=0x%llx, len=%zu)\n",__func__,op,offset,len);+mutex_lock(&info->lock);+token=opal_async_get_token_interruptible();if(token<0){if(token!=-ERESTARTSYS)dev_err(dev,"Failed to get an async token\n");-returntoken;+rc=token;+gotoout;}switch(op){
There are no callers of both __opal_async_get_token() and
__opal_async_release_token().
This patch also removes the possibility of "emergency through
synchronous call to __opal_async_get_token()" as such it makes more
sense to initialise opal_sync_sem for the maximum number of async
tokens.
Signed-off-by: Cyril Bur <redacted>
---
arch/powerpc/include/asm/opal.h | 2 --
arch/powerpc/platforms/powernv/opal-async.c | 10 +++-------
2 files changed, 3 insertions(+), 9 deletions(-)
@@ -73,7 +73,7 @@ int opal_async_get_token_interruptible(void)}EXPORT_SYMBOL_GPL(opal_async_get_token_interruptible);-int__opal_async_release_token(inttoken)+staticint__opal_async_release_token(inttoken){unsignedlongflags;
@@ -199,11 +199,7 @@ int __init opal_async_comp_init(void)gotoout_opal_node;}-/* Initialize to 1 less than the maximum tokens available, as we may-*requiretopoponeduringemergencythroughsynchronouscallto-*__opal_async_get_token()-*/-sema_init(&opal_async_sem,opal_max_async_tokens-1);+sema_init(&opal_async_sem,opal_max_async_tokens);out_opal_node:of_node_put(opal_node);
The OPAL calls performed in this driver shouldn't be using
opal_async_wait_response() as this performs a wait_event() which, on
long running OPAL calls could result in hung task warnings. wait_event()
prevents timely signal delivery which is also undesirable.
This patch also attempts to quieten down the use of dev_err() when
errors haven't actually occurred and also to return better information up
the stack rather than always -EIO.
Signed-off-by: Cyril Bur <redacted>
---
drivers/mtd/devices/powernv_flash.c | 28 +++++++++++++++++++++++-----
1 file changed, 23 insertions(+), 5 deletions(-)
Future work will add an opal_async_wait_response_interruptible()
which will call wait_event_interruptible(). This work requires extra
token state to be tracked as wait_event_interruptible() can return and
the caller could release the token before OPAL responds.
Currently token state is tracked with two bitfields which are 64 bits
big but may not need to be as OPAL informs Linux how many async tokens
there are. It also uses an array indexed by token to store response
messages for each token.
The bitfields make it difficult to add more state and also provide a
hard maximum as to how many tokens there can be - it is possible that
OPAL will inform Linux that there are more than 64 tokens.
Rather than add a bitfield to track the extra state, rework the
internals slightly.
Signed-off-by: Cyril Bur <redacted>
---
arch/powerpc/platforms/powernv/opal-async.c | 97 ++++++++++++++++-------------
1 file changed, 53 insertions(+), 44 deletions(-)
@@ -76,6 +82,7 @@ EXPORT_SYMBOL_GPL(opal_async_get_token_interruptible);staticint__opal_async_release_token(inttoken){unsignedlongflags;+intrc;if(token<0||token>=opal_max_async_tokens){pr_err("%s: Passed token is out of range, token %d\n",
@@ -84,11 +91,18 @@ static int __opal_async_release_token(int token)}spin_lock_irqsave(&opal_async_comp_lock,flags);-__set_bit(token,opal_async_complete_map);-__clear_bit(token,opal_async_token_map);+switch(opal_async_tokens[token].state){+caseASYNC_TOKEN_COMPLETED:+caseASYNC_TOKEN_ALLOCATED:+opal_async_tokens[token].state=ASYNC_TOKEN_FREE;+rc=0;+break;+default:+rc=1;+}spin_unlock_irqrestore(&opal_async_comp_lock,flags);-return0;+returnrc;}intopal_async_release_token(inttoken)
@@ -96,12 +110,10 @@ int opal_async_release_token(int token)intret;ret=__opal_async_release_token(token);-if(ret)-returnret;--up(&opal_async_sem);+if(!ret)+up(&opal_async_sem);-return0;+returnret;}EXPORT_SYMBOL_GPL(opal_async_release_token);
@@ -122,13 +134,15 @@ int opal_async_wait_response(uint64_t token, struct opal_msg *msg)*functional.*/opal_wake_poller();-wait_event(opal_async_wait,test_bit(token,opal_async_complete_map));-memcpy(msg,&opal_async_responses[token],sizeof(*msg));+wait_event(opal_async_wait,opal_async_tokens[token].state+==ASYNC_TOKEN_COMPLETED);+memcpy(msg,&opal_async_tokens[token].response,sizeof(*msg));return0;}EXPORT_SYMBOL_GPL(opal_async_wait_response);+/* Called from interrupt context */staticintopal_async_comp_event(structnotifier_block*nb,unsignedlongmsg_type,void*msg){
@@ -140,9 +154,9 @@ static int opal_async_comp_event(struct notifier_block *nb,return0;token=be64_to_cpu(comp_msg->params[0]);-memcpy(&opal_async_responses[token],comp_msg,sizeof(*comp_msg));+memcpy(&opal_async_tokens[token].response,comp_msg,sizeof(*comp_msg));spin_lock_irqsave(&opal_async_comp_lock,flags);-__set_bit(token,opal_async_complete_map);+opal_async_tokens[token].state=ASYNC_TOKEN_COMPLETED;spin_unlock_irqrestore(&opal_async_comp_lock,flags);wake_up(&opal_async_wait);
@@ -178,24 +192,19 @@ int __init opal_async_comp_init(void)}opal_max_async_tokens=be32_to_cpup(async);-if(opal_max_async_tokens>N_ASYNC_COMPLETIONS)-opal_max_async_tokens=N_ASYNC_COMPLETIONS;+opal_async_tokens=kcalloc(opal_max_async_tokens,+sizeof(*opal_async_tokens),GFP_KERNEL);+if(!opal_async_tokens){+err=-ENOMEM;+gotoout_opal_node;+}err=opal_message_notifier_register(OPAL_MSG_ASYNC_COMP,&opal_async_comp_nb);if(err){pr_err("%s: Can't register OPAL event notifier (%d)\n",__func__,err);-gotoout_opal_node;-}--opal_async_responses=kzalloc(-sizeof(*opal_async_responses)*opal_max_async_tokens,-GFP_KERNEL);-if(!opal_async_responses){-pr_err("%s: Out of memory, failed to do asynchronous "-"completion init\n",__func__);-err=-ENOMEM;+kfree(opal_async_tokens);gotoout_opal_node;}
This patch adds an _interruptible version of opal_async_wait_response().
This is useful when a long running OPAL call is performed on behalf of a
userspace thread, for example, the opal_flash_{read,write,erase}
functions performed by the powernv-flash MTD driver.
It is foreseeable that these functions would take upwards of two minutes
causing the wait_event() to block long enough to cause hung task
warnings. Furthermore, wait_event_interruptible() is preferable as
otherwise there is no way for signals to stop the process which is going
to be confusing in userspace.
Signed-off-by: Cyril Bur <redacted>
---
arch/powerpc/include/asm/opal.h | 2 +
arch/powerpc/platforms/powernv/opal-async.c | 87 +++++++++++++++++++++++++++--
2 files changed, 85 insertions(+), 4 deletions(-)
@@ -59,8 +61,10 @@ static int __opal_async_get_token(void)}/*-*Note:Ifthereturnedtokenisusedinanopalcallandopalreturns-*OPAL_ASYNC_COMPLETIONyouMUSTopal_async_wait_response()before+*Note:Ifthereturnedtokenisusedinanopalcallandopal+*returnsOPAL_ASYNC_COMPLETIONyouMUSToneof+*opal_async_wait_response()or+*opal_async_wait_response_interruptible()atleastoncebefore*callinganotherotheropal_async_*function*/intopal_async_get_token_interruptible(void)
@@ -97,6 +101,16 @@ static int __opal_async_release_token(int token)opal_async_tokens[token].state=ASYNC_TOKEN_FREE;rc=0;break;+/*+*DISPATCHEDandABANDONEDtokensmustwaitforOPALto+*respond.+*MarkaDISPATCHEDtokenasABANDONEDsothattheresponse+*responsehandlingcodeknowsnoonecaresandthatitcan+*freeitthen.+*/+caseASYNC_TOKEN_DISPATCHED:+opal_async_tokens[token].state=ASYNC_TOKEN_ABANDONED;+/* Fall through */default:rc=1;}
@@ -129,7 +143,11 @@ int opal_async_wait_response(uint64_t token, struct opal_msg *msg)return-EINVAL;}-/* Wakeup the poller before we wait for events to speed things+/*+*Thereisnoneedtomarkthetokenasdispatched,wait_event()+*willblockuntilthetokencompletes.+*+*Wakeupthepollerbeforewewaitforeventstospeedthings*uponplatformsorsimulatorswheretheinterruptsaren't*functional.*/
@@ -142,11 +160,66 @@ int opal_async_wait_response(uint64_t token, struct opal_msg *msg)}EXPORT_SYMBOL_GPL(opal_async_wait_response);+intopal_async_wait_response_interruptible(uint64_ttoken,structopal_msg*msg)+{+unsignedlongflags;+intret;++if(token>=opal_max_async_tokens){+pr_err("%s: Invalid token passed\n",__func__);+return-EINVAL;+}++if(!msg){+pr_err("%s: Invalid message pointer passed\n",__func__);+return-EINVAL;+}++/*+*ThefirsttimethisgetscalledwemarkthetokenasDISPATCHED+*sothatifwait_event_interruptible()returnsnotzeroandthe+*callerfreesthetoken,weknownottoactuallyfreethetoken+*untiltheresponsecomes.+*+*OnlychangeifthetokenisALLOCATED-itmayhavebeen+*completedevenbeforethecallergetsaroundtocallingthis+*thefirsttime.+*+*Thereisalsoadirtygreatcommentatthetokenallocation+*functionthatiftheopalcallreturnsOPAL_ASYNC_COMPLETIONto+*thecallerthenthecaller*must*callthisorthenot+*interruptibleversionbeforedoinganythingelsewiththe+*token.+*/+if(opal_async_tokens[token].state==ASYNC_TOKEN_ALLOCATED){+spin_lock_irqsave(&opal_async_comp_lock,flags);+if(opal_async_tokens[token].state==ASYNC_TOKEN_ALLOCATED)+opal_async_tokens[token].state=ASYNC_TOKEN_DISPATCHED;+spin_unlock_irqrestore(&opal_async_comp_lock,flags);+}++/*+*Wakeupthepollerbeforewewaitforeventstospeedthings+*uponplatformsorsimulatorswheretheinterruptsaren't+*functional.+*/+opal_wake_poller();+ret=wait_event_interruptible(opal_async_wait,+opal_async_tokens[token].state==+ASYNC_TOKEN_COMPLETED);+if(!ret)+memcpy(msg,&opal_async_tokens[token].response,sizeof(*msg));++returnret;+}+EXPORT_SYMBOL_GPL(opal_async_wait_response_interruptible);+/* Called from interrupt context */staticintopal_async_comp_event(structnotifier_block*nb,unsignedlongmsg_type,void*msg){structopal_msg*comp_msg=msg;+enumopal_async_token_statestate;unsignedlongflags;uint64_ttoken;
@@ -154,11 +227,17 @@ static int opal_async_comp_event(struct notifier_block *nb,return0;token=be64_to_cpu(comp_msg->params[0]);-memcpy(&opal_async_tokens[token].response,comp_msg,sizeof(*comp_msg));spin_lock_irqsave(&opal_async_comp_lock,flags);+state=opal_async_tokens[token].state;opal_async_tokens[token].state=ASYNC_TOKEN_COMPLETED;spin_unlock_irqrestore(&opal_async_comp_lock,flags);+if(state==ASYNC_TOKEN_ABANDONED){+/* Free the token, no one else will */+opal_async_release_token(token);+return0;+}+memcpy(&opal_async_tokens[token].response,comp_msg,sizeof(*comp_msg));wake_up(&opal_async_wait);return0;
From: Stewart Smith <redacted>
Parallel sensor reads could run out of async tokens due to
opal_get_sensor_data grabbing tokens but then doing the sensor
read behind a mutex, essentially serializing the (possibly
asynchronous and relatively slow) sensor read.
It turns out that the mutex isn't needed at all, not only
should the OPAL interface allow concurrent reads, the implementation
is certainly safe for that, and if any sensor we were reading
from somewhere isn't, doing the mutual exclusion in the kernel
is the wrong place to do it, OPAL should be doing it for the kernel.
So, remove the mutex.
Additionally, we shouldn't be printing out an error when we don't
get a token as the only way this should happen is if we've been
interrupted in down_interruptible() on the semaphore.
Reported-by: Robert Lippert <redacted>
Signed-off-by: Stewart Smith <redacted>
Signed-off-by: Cyril Bur <redacted>
---
arch/powerpc/platforms/powernv/opal-sensor.c | 17 ++++-------------
1 file changed, 4 insertions(+), 13 deletions(-)
@@ -38,13 +35,9 @@ int opal_get_sensor_data(u32 sensor_hndl, u32 *sensor_data)__be32data;token=opal_async_get_token_interruptible();-if(token<0){-pr_err("%s: Couldn't get the token, returning\n",__func__);-ret=token;-gotoout;-}+if(token<0)+returntoken;-mutex_lock(&opal_sensor_mutex);ret=opal_sensor_read(sensor_hndl,token,&data);switch(ret){caseOPAL_ASYNC_COMPLETION:
@@ -52,7 +45,7 @@ int opal_get_sensor_data(u32 sensor_hndl, u32 *sensor_data)if(ret){pr_err("%s: Failed to wait for the async response, %d\n",__func__,ret);-gotoout_token;+gotoout;}ret=opal_error_code(opal_get_async_rc(msg));
@@ -73,10 +66,8 @@ int opal_get_sensor_data(u32 sensor_hndl, u32 *sensor_data)break;}-out_token:-mutex_unlock(&opal_sensor_mutex);-opal_async_release_token(token);out:+opal_async_release_token(token);returnret;}EXPORT_SYMBOL_GPL(opal_get_sensor_data);
While this driver expects to interact asynchronously, OPAL is well
within its rights to return OPAL_SUCCESS to indicate that the operation
completed without the need for a callback. We shouldn't treat
OPAL_SUCCESS as an error rather we should wrap up and return promptly to
the caller.
Signed-off-by: Cyril Bur <redacted>
---
I'll note here that currently no OPAL exists that will return
OPAL_SUCCESS so there isn't the possibility of a bug today.
drivers/mtd/devices/powernv_flash.c | 17 +++++++++--------
1 file changed, 9 insertions(+), 8 deletions(-)
@@ -66,9 +66,8 @@ static int powernv_flash_async_op(struct mtd_info *mtd, enum flash_op op,if(token<0){if(token!=-ERESTARTSYS)dev_err(dev,"Failed to get an async token\n");--rc=token;-gotoout;+mutex_unlock(&info->lock);+returntoken;}switch(op){
On Wed, 2017-07-12 at 14:22 +1000, Cyril Bur wrote:
BUG_ON() should be reserved in situations where we can not longer
guarantee the integrity of the system. In the case where
powernv_flash_async_op() receives an impossible op, we can still
guarantee the integrity of the system.
Signed-off-by: Cyril Bur <redacted>
---
On Wed, 2017-07-12 at 14:22 +1000, Cyril Bur wrote:
OPAL can only manage one flash access at a time and will return an
OPAL_BUSY error for each concurrent access to the flash. The simplest
way to prevent this from happening is with a mutex.
Signed-off-by: Cyril Bur <redacted>
---
Should the mutex_lock() be mutex_lock_interruptible()? Are we OK waiting on
the mutex while other operations with the lock are busy?
Balbir Singh.
On Mon, 2017-07-17 at 17:34 +1000, Balbir Singh wrote:
On Wed, 2017-07-12 at 14:22 +1000, Cyril Bur wrote:
quoted
OPAL can only manage one flash access at a time and will return an
OPAL_BUSY error for each concurrent access to the flash. The simplest
way to prevent this from happening is with a mutex.
Signed-off-by: Cyril Bur <redacted>
---
Should the mutex_lock() be mutex_lock_interruptible()? Are we OK waiting on
the mutex while other operations with the lock are busy?
This is a good question. My best interpretation is that
_interruptible() should be used when you'll only be coming from a user
context. Which is mostly true for this driver, however, MTD does
provide kernel interfaces, so I was hesitant, there isn't a great deal
of use of _interruptible() in drivers/mtd.
Thoughts?
Cyril
On Wed, 2017-07-12 at 14:22 +1000, Cyril Bur wrote:
While this driver expects to interact asynchronously, OPAL is well
within its rights to return OPAL_SUCCESS to indicate that the operation
completed without the need for a callback. We shouldn't treat
OPAL_SUCCESS as an error rather we should wrap up and return promptly to
the caller.
Signed-off-by: Cyril Bur <redacted>
---
I'll note here that currently no OPAL exists that will return
OPAL_SUCCESS so there isn't the possibility of a bug today.
It would help if you mentioned OPAL_SUCCESS to the async call. So effectively
what we expected to be an asynchronous call with callback, but OPAL returned
immediately with success.
Balbir Singh.
On Mon, 2017-07-17 at 17:55 +1000, Cyril Bur wrote:
On Mon, 2017-07-17 at 17:34 +1000, Balbir Singh wrote:
quoted
On Wed, 2017-07-12 at 14:22 +1000, Cyril Bur wrote:
quoted
OPAL can only manage one flash access at a time and will return an
OPAL_BUSY error for each concurrent access to the flash. The simplest
way to prevent this from happening is with a mutex.
Signed-off-by: Cyril Bur <redacted>
---
Should the mutex_lock() be mutex_lock_interruptible()? Are we OK waiting on
the mutex while other operations with the lock are busy?
This is a good question. My best interpretation is that
_interruptible() should be used when you'll only be coming from a user
context. Which is mostly true for this driver, however, MTD does
provide kernel interfaces, so I was hesitant, there isn't a great deal
of use of _interruptible() in drivers/mtd.
Thoughts?
What are the kernel interfaces (I have not read through mtd in detail)?
I would still like to see us not blocked in mutex_lock() across threads
for parallel calls, one option is to use mutex_trylock() and return if
someone already holds the mutex with -EBUSY, but you'll need to evaluate
what that means for every call.
Balbir Singh.
On Wed, 2017-07-12 at 14:23 +1000, Cyril Bur wrote:
quoted hunk
Future work will add an opal_async_wait_response_interruptible()
which will call wait_event_interruptible(). This work requires extra
token state to be tracked as wait_event_interruptible() can return and
the caller could release the token before OPAL responds.
Currently token state is tracked with two bitfields which are 64 bits
big but may not need to be as OPAL informs Linux how many async tokens
there are. It also uses an array indexed by token to store response
messages for each token.
The bitfields make it difficult to add more state and also provide a
hard maximum as to how many tokens there can be - it is possible that
OPAL will inform Linux that there are more than 64 tokens.
Rather than add a bitfield to track the extra state, rework the
internals slightly.
Signed-off-by: Cyril Bur <redacted>
---
arch/powerpc/platforms/powernv/opal-async.c | 97 ++++++++++++++++-------------
1 file changed, 53 insertions(+), 44 deletions(-)
Are these states mutually exclusive? Does _COMPLETED imply that it is also
_ALLOCATED? ALLOCATED and FREE are confusing, I would use IN_USE and NOT_IN_USE
for tokens. If these are mutually exclusive then you can use IN_USE and !IN_USE
Why is the spin lock inside the for loop? If the last token is free, the
number of times we'll take and release a lock is extensive, why are we
doing it this way?
quoted hunk
+ if (opal_async_tokens[token].state == ASYNC_TOKEN_FREE) {
+ opal_async_tokens[token].state = ASYNC_TOKEN_ALLOCATED;
+ spin_unlock_irqrestore(&opal_async_comp_lock, flags);
+ return token;
+ }
+ spin_unlock_irqrestore(&opal_async_comp_lock, flags);
}
- __clear_bit(token, opal_async_complete_map);
-
-out:
- spin_unlock_irqrestore(&opal_async_comp_lock, flags);
- return token;
+ return -EBUSY;
}
+/*
+ * Note: If the returned token is used in an opal call and opal returns
+ * OPAL_ASYNC_COMPLETION you MUST opal_async_wait_response() before
+ * calling another other opal_async_* function
+ */
int opal_async_get_token_interruptible(void)
{
int token;
@@ -76,6 +82,7 @@ EXPORT_SYMBOL_GPL(opal_async_get_token_interruptible); static int __opal_async_release_token(int token) { unsigned long flags;+ int rc; if (token < 0 || token >= opal_max_async_tokens) { pr_err("%s: Passed token is out of range, token %d\n",
@@ -84,11 +91,18 @@ static int __opal_async_release_token(int token) } spin_lock_irqsave(&opal_async_comp_lock, flags);- __set_bit(token, opal_async_complete_map);- __clear_bit(token, opal_async_token_map);+ switch (opal_async_tokens[token].state) {+ case ASYNC_TOKEN_COMPLETED:+ case ASYNC_TOKEN_ALLOCATED:+ opal_async_tokens[token].state = ASYNC_TOKEN_FREE;
So we can go from
_COMPLETED | _ALLOCATED to _FREE on release_token, why would be release
an _ALLOCATED token, in the case the callback is not really called?
@@ -96,12 +110,10 @@ int opal_async_release_token(int token) int ret; ret = __opal_async_release_token(token);- if (ret)- return ret;-- up(&opal_async_sem);+ if (!ret)+ up(&opal_async_sem);
So we up the semaphore only if we made a transition and freed the token, right?
What happens otherwise?
Since wait_event is a macro, I'd recommend parenthesis around the second
argument. I think there is also an inbuilt assumption that the barriers
in schedule() called by wait_event() will make the write to the token
state visible.
quoted hunk
+ memcpy(msg, &opal_async_tokens[token].response, sizeof(*msg));
return 0;
}
EXPORT_SYMBOL_GPL(opal_async_wait_response);
+/* Called from interrupt context */
static int opal_async_comp_event(struct notifier_block *nb,
unsigned long msg_type, void *msg)
{
From: Frans Klaver <hidden> Date: 2017-07-17 11:33:08
On Wed, Jul 12, 2017 at 6:22 AM, Cyril Bur [off-list ref] wrote:
quoted hunk
BUG_ON() should be reserved in situations where we can not longer
guarantee the integrity of the system. In the case where
powernv_flash_async_op() receives an impossible op, we can still
guarantee the integrity of the system.
Signed-off-by: Cyril Bur <redacted>
---
drivers/mtd/devices/powernv_flash.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
Based on the fact that all three values in enum flash_op are handled,
I would go as far as stating that the default lemma adds no value and
can be removed.
Frans
On Mon, 2017-07-17 at 13:33 +0200, Frans Klaver wrote:
On Wed, Jul 12, 2017 at 6:22 AM, Cyril Bur [off-list ref] wrote:
quoted
BUG_ON() should be reserved in situations where we can not longer
guarantee the integrity of the system. In the case where
powernv_flash_async_op() receives an impossible op, we can still
guarantee the integrity of the system.
Signed-off-by: Cyril Bur <redacted>
---
drivers/mtd/devices/powernv_flash.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
Based on the fact that all three values in enum flash_op are handled,
I would go as far as stating that the default lemma adds no value and
can be removed.
The way I see it is that it isn't doing any harm being there and in
cases of future programmer error or during corruption events, that
WARN_ON might prove useful.
On Mon, 2017-07-17 at 21:30 +1000, Balbir Singh wrote:
On Wed, 2017-07-12 at 14:23 +1000, Cyril Bur wrote:
quoted
Future work will add an opal_async_wait_response_interruptible()
which will call wait_event_interruptible(). This work requires extra
token state to be tracked as wait_event_interruptible() can return and
the caller could release the token before OPAL responds.
Currently token state is tracked with two bitfields which are 64 bits
big but may not need to be as OPAL informs Linux how many async tokens
there are. It also uses an array indexed by token to store response
messages for each token.
The bitfields make it difficult to add more state and also provide a
hard maximum as to how many tokens there can be - it is possible that
OPAL will inform Linux that there are more than 64 tokens.
Rather than add a bitfield to track the extra state, rework the
internals slightly.
Signed-off-by: Cyril Bur <redacted>
---
arch/powerpc/platforms/powernv/opal-async.c | 97 ++++++++++++++++-------------
1 file changed, 53 insertions(+), 44 deletions(-)
Why is the spin lock inside the for loop? If the last token is free, the
number of times we'll take and release a lock is extensive, why are we
doing it this way?
Otherwise we might hold the lock for quite some time. At the moment I
think it isn't a bit deal since OPAL gives 8 but there is current work
to increase that number and while it seems the number might only grow
to 16, for a while it was looking like it might grow more.
In a previous iteration I had a check inside the loop but outside the
lock for if (token == ASYNC_TOKEN_FREE) which would then proceed to
take the lock, check again and mark it allocated...
Or I could put the lock around the loop, I'm not attached to any
particular approach.
quoted
+ if (opal_async_tokens[token].state == ASYNC_TOKEN_FREE) {
+ opal_async_tokens[token].state = ASYNC_TOKEN_ALLOCATED;
+ spin_unlock_irqrestore(&opal_async_comp_lock, flags);
+ return token;
+ }
+ spin_unlock_irqrestore(&opal_async_comp_lock, flags);
}
- __clear_bit(token, opal_async_complete_map);
-
-out:
- spin_unlock_irqrestore(&opal_async_comp_lock, flags);
- return token;
+ return -EBUSY;
}
+/*
+ * Note: If the returned token is used in an opal call and opal returns
+ * OPAL_ASYNC_COMPLETION you MUST opal_async_wait_response() before
+ * calling another other opal_async_* function
+ */
int opal_async_get_token_interruptible(void)
{
int token;
@@ -76,6 +82,7 @@ EXPORT_SYMBOL_GPL(opal_async_get_token_interruptible); static int __opal_async_release_token(int token) { unsigned long flags;+ int rc; if (token < 0 || token >= opal_max_async_tokens) { pr_err("%s: Passed token is out of range, token %d\n",
@@ -84,11 +91,18 @@ static int __opal_async_release_token(int token) } spin_lock_irqsave(&opal_async_comp_lock, flags);- __set_bit(token, opal_async_complete_map);- __clear_bit(token, opal_async_token_map);+ switch (opal_async_tokens[token].state) {+ case ASYNC_TOKEN_COMPLETED:+ case ASYNC_TOKEN_ALLOCATED:+ opal_async_tokens[token].state = ASYNC_TOKEN_FREE;
So we can go from
_COMPLETED | _ALLOCATED to _FREE on release_token, why would be release
an _ALLOCATED token, in the case the callback is not really called?
If the OPAL call fails, the caller can either choose to retry the OPAL
call or just abandon, if it abandons then it must free the token (which
will never complete since the OPAL call failed).
@@ -96,12 +110,10 @@ int opal_async_release_token(int token) int ret; ret = __opal_async_release_token(token);- if (ret)- return ret;-- up(&opal_async_sem);+ if (!ret)+ up(&opal_async_sem);
So we up the semaphore only if we made a transition and freed the token, right?
What happens otherwise?
Since wait_event is a macro, I'd recommend parenthesis around the second
argument. I think there is also an inbuilt assumption that the barriers
in schedule() called by wait_event() will make the write to the token
state visible.
Yes good point.
quoted
+ memcpy(msg, &opal_async_tokens[token].response, sizeof(*msg));
return 0;
}
EXPORT_SYMBOL_GPL(opal_async_wait_response);
+/* Called from interrupt context */
static int opal_async_comp_event(struct notifier_block *nb,
unsigned long msg_type, void *msg)
{
On Mon, 2017-07-17 at 18:50 +1000, Balbir Singh wrote:
On Wed, 2017-07-12 at 14:22 +1000, Cyril Bur wrote:
quoted
While this driver expects to interact asynchronously, OPAL is well
within its rights to return OPAL_SUCCESS to indicate that the operation
completed without the need for a callback. We shouldn't treat
OPAL_SUCCESS as an error rather we should wrap up and return promptly to
the caller.
Signed-off-by: Cyril Bur <redacted>
---
I'll note here that currently no OPAL exists that will return
OPAL_SUCCESS so there isn't the possibility of a bug today.
It would help if you mentioned OPAL_SUCCESS to the async call. So effectively
what we expected to be an asynchronous call with callback, but OPAL returned
immediately with success.
Ah my favourite problems, commit message.
Thanks,
Cyril
On Mon, 2017-07-17 at 19:29 +1000, Balbir Singh wrote:
On Mon, 2017-07-17 at 17:55 +1000, Cyril Bur wrote:
quoted
On Mon, 2017-07-17 at 17:34 +1000, Balbir Singh wrote:
quoted
On Wed, 2017-07-12 at 14:22 +1000, Cyril Bur wrote:
quoted
OPAL can only manage one flash access at a time and will return an
OPAL_BUSY error for each concurrent access to the flash. The simplest
way to prevent this from happening is with a mutex.
Signed-off-by: Cyril Bur <redacted>
---
Should the mutex_lock() be mutex_lock_interruptible()? Are we OK waiting on
the mutex while other operations with the lock are busy?
This is a good question. My best interpretation is that
_interruptible() should be used when you'll only be coming from a user
context. Which is mostly true for this driver, however, MTD does
provide kernel interfaces, so I was hesitant, there isn't a great deal
of use of _interruptible() in drivers/mtd.
Thoughts?
What are the kernel interfaces (I have not read through mtd in detail)?
I would still like to see us not blocked in mutex_lock() across threads
for parallel calls, one option is to use mutex_trylock() and return if
someone already holds the mutex with -EBUSY, but you'll need to evaluate
what that means for every call.
Yeah maybe mutex_trylock() is the way to go, thinking quickly, I don't
see how it could be a problem for userspace using powernv_flash. I'm
honestly not too sure about the depths of the mtd kernel interfaces but
I've seen a tonne of cool stuff you could do, hence my reluctance to go
with _interruptible()
Cyril
From: Michael Ellerman <mpe@ellerman.id.au> Date: 2017-07-18 03:13:00
Cyril Bur [off-list ref] writes:
On Mon, 2017-07-17 at 19:29 +1000, Balbir Singh wrote:
quoted
On Mon, 2017-07-17 at 17:55 +1000, Cyril Bur wrote:
quoted
On Mon, 2017-07-17 at 17:34 +1000, Balbir Singh wrote:
quoted
On Wed, 2017-07-12 at 14:22 +1000, Cyril Bur wrote:
quoted
OPAL can only manage one flash access at a time and will return an
OPAL_BUSY error for each concurrent access to the flash. The simplest
way to prevent this from happening is with a mutex.
Signed-off-by: Cyril Bur <redacted>
---
Should the mutex_lock() be mutex_lock_interruptible()? Are we OK waiting on
the mutex while other operations with the lock are busy?
This is a good question. My best interpretation is that
_interruptible() should be used when you'll only be coming from a user
context. Which is mostly true for this driver, however, MTD does
provide kernel interfaces, so I was hesitant, there isn't a great deal
of use of _interruptible() in drivers/mtd.
Thoughts?
What are the kernel interfaces (I have not read through mtd in detail)?
I would still like to see us not blocked in mutex_lock() across threads
for parallel calls, one option is to use mutex_trylock() and return if
someone already holds the mutex with -EBUSY, but you'll need to evaluate
what that means for every call.
Yeah maybe mutex_trylock() is the way to go, thinking quickly, I don't
see how it could be a problem for userspace using powernv_flash. I'm
honestly not too sure about the depths of the mtd kernel interfaces but
I've seen a tonne of cool stuff you could do, hence my reluctance to go
with _interruptible()
If you use trylock that means all your callers now need to handle EBUSY,
which I doubt they do. Which means it goes up to userspace, which most
users will just treat as a hard error.
So that sounds like a bad plan to me.
cheers
From: Michael Ellerman <mpe@ellerman.id.au> Date: 2017-07-18 03:20:08
Cyril Bur [off-list ref] writes:
On Mon, 2017-07-17 at 21:30 +1000, Balbir Singh wrote:
quoted
On Wed, 2017-07-12 at 14:23 +1000, Cyril Bur wrote:
quoted
static int __opal_async_get_token(void)
{
unsigned long flags;
int token;
- spin_lock_irqsave(&opal_async_comp_lock, flags);
- token = find_first_bit(opal_async_complete_map, opal_max_async_tokens);
- if (token >= opal_max_async_tokens) {
- token = -EBUSY;
- goto out;
- }
-
- if (__test_and_set_bit(token, opal_async_token_map)) {
- token = -EBUSY;
- goto out;
+ for (token = 0; token < opal_max_async_tokens; token++) {
+ spin_lock_irqsave(&opal_async_comp_lock, flags);
Why is the spin lock inside the for loop? If the last token is free, the
number of times we'll take and release a lock is extensive, why are we
doing it this way?
Otherwise we might hold the lock for quite some time.
No we won't. A loop over a few 10s or 100s of tokens is not going to
take long, compared to the overhead of taking the lock every iteration
through the loop.
If the linear search with the lock held is a bottle neck, then you can
keep a hint variable which tracks the last freed token and start
searching from there.
cheers