[PATCH] iscsi-target: Fix initial login PDU asynchronous socket close OOPs

2017-05-25 Thread Nicholas A. Bellinger
From: Nicholas Bellinger 

This patch fixes a OOPs originally introduced by:

   commit bb048357dad6d604520c91586334c9c230366a14
   Author: Nicholas Bellinger 
   Date:   Thu Sep 5 14:54:04 2013 -0700

   iscsi-target: Add sk->sk_state_change to cleanup after TCP failure

which would trigger a NULL pointer dereference when a TCP connection
was closed asynchronously via iscsi_target_sk_state_change(), but only
when the initial PDU processing in iscsi_target_do_login() from iscsi_np
process context was blocked waiting for backend I/O to complete.

To address this issue, this patch makes the following changes.

First, it introduces some common helper functions used for checking
socket closing state, checking login_flags, and atomically checking
socket closing state + setting login_flags.

Second, it introduces a LOGIN_FLAGS_INITIAL_PDU bit to know when a TCP
connection has dropped via iscsi_target_sk_state_change(), but the
initial PDU processing within iscsi_target_do_login() in iscsi_np
context is still running.  For this case, it sets LOGIN_FLAGS_CLOSED,
but doesn't invoke schedule_delayed_work().

The original NULL pointer dereference case reported by MNC is now handled
by iscsi_target_do_login() doing a iscsi_target_sk_check_close() before
transitioning to FFP to determine when the socket has already closed,
or iscsi_target_start_negotiation() if the login needs to exchange
more PDUs (eg: iscsi_target_do_login returned 0) but the socket has
closed.  For both of these cases, the cleanup up of remaining connection
resources will occur in iscsi_target_start_negotiation() from iscsi_np
process context once the failure is detected.

Finally, to handle to case where iscsi_target_sk_state_change() is
called after the initial PDU procesing is complete, it now invokes
conn->login_work -> iscsi_target_do_login_rx() to perform cleanup once
existing iscsi_target_sk_check_close() checks detect connection failure.
For this case, the cleanup of remaining connection resources will occur
in iscsi_target_do_login_rx() from delayed workqueue process context
once the failure is detected.

Reported-by: Mike Christie 
Cc: Mike Christie 
Reported-by: Hannes Reinecke 
Cc: Hannes Reinecke 
Cc: Sagi Grimberg 
Cc: Varun Prakash 
Signed-off-by: Nicholas Bellinger 
---
 drivers/target/iscsi/iscsi_target_nego.c | 194 +--
 include/target/iscsi/iscsi_target_core.h |   1 +
 2 files changed, 133 insertions(+), 62 deletions(-)

diff --git a/drivers/target/iscsi/iscsi_target_nego.c 
b/drivers/target/iscsi/iscsi_target_nego.c
index 7ccc9c1..6f88b31 100644
--- a/drivers/target/iscsi/iscsi_target_nego.c
+++ b/drivers/target/iscsi/iscsi_target_nego.c
@@ -493,14 +493,60 @@ static void iscsi_target_restore_sock_callbacks(struct 
iscsi_conn *conn)
 
 static int iscsi_target_do_login(struct iscsi_conn *, struct iscsi_login *);
 
-static bool iscsi_target_sk_state_check(struct sock *sk)
+static bool __iscsi_target_sk_check_close(struct sock *sk)
 {
if (sk->sk_state == TCP_CLOSE_WAIT || sk->sk_state == TCP_CLOSE) {
-   pr_debug("iscsi_target_sk_state_check: 
TCP_CLOSE_WAIT|TCP_CLOSE,"
+   pr_debug("__iscsi_target_sk_check_close: 
TCP_CLOSE_WAIT|TCP_CLOSE,"
"returning FALSE\n");
-   return false;
+   return true;
}
-   return true;
+   return false;
+}
+
+static bool iscsi_target_sk_check_close(struct iscsi_conn *conn)
+{
+   bool state = false;
+
+   if (conn->sock) {
+   struct sock *sk = conn->sock->sk;
+
+   read_lock_bh(>sk_callback_lock);
+   state = (__iscsi_target_sk_check_close(sk) ||
+test_bit(LOGIN_FLAGS_CLOSED, >login_flags));
+   read_unlock_bh(>sk_callback_lock);
+   }
+   return state;
+}
+
+static bool iscsi_target_sk_check_flag(struct iscsi_conn *conn, unsigned int 
flag)
+{
+   bool state = false;
+
+   if (conn->sock) {
+   struct sock *sk = conn->sock->sk;
+
+   read_lock_bh(>sk_callback_lock);
+   state = test_bit(flag, >login_flags);
+   read_unlock_bh(>sk_callback_lock);
+   }
+   return state;
+}
+
+static bool iscsi_target_sk_check_and_clear(struct iscsi_conn *conn, unsigned 
int flag)
+{
+   bool state = false;
+
+   if (conn->sock) {
+   struct sock *sk = conn->sock->sk;
+
+   write_lock_bh(>sk_callback_lock);
+   state = (__iscsi_target_sk_check_close(sk) ||
+test_bit(LOGIN_FLAGS_CLOSED, >login_flags));
+   if (!state)
+   clear_bit(flag, >login_flags);
+   write_unlock_bh(>sk_callback_lock);
+   }
+   return state;
 }
 
 static void 

[PATCH 6/6] blk-mq: clarify dispatching won't be blocked by stopping queue

2017-05-25 Thread Ming Lei
BLK_MQ_S_STOPPED may be not observed in other concurrent I/O paths,
we can't guarantee that dispatching won't happen after queue
is stopped.

So clarify the fact and avoid potential misuse.

Signed-off-by: Ming Lei 
---
 block/blk-mq.c | 10 ++
 1 file changed, 10 insertions(+)

diff --git a/block/blk-mq.c b/block/blk-mq.c
index 900eb91e0ece..32bed5bac7da 100644
--- a/block/blk-mq.c
+++ b/block/blk-mq.c
@@ -1240,6 +1240,11 @@ static void __blk_mq_stop_hw_queue(struct blk_mq_hw_ctx 
*hctx, bool sync)
set_bit(BLK_MQ_S_STOPPED, >state);
 }
 
+/*
+ * Do not expect that dispatching or .queue_rq() can be blocked
+ * after blk_mq_stop_hw_queue() returns. Please use
+ * blk_mq_quiesce_queue() for that requirement.
+ */
 void blk_mq_stop_hw_queue(struct blk_mq_hw_ctx *hctx)
 {
__blk_mq_stop_hw_queue(hctx, false);
@@ -1255,6 +1260,11 @@ static void __blk_mq_stop_hw_queues(struct request_queue 
*q, bool sync)
__blk_mq_stop_hw_queue(hctx, sync);
 }
 
+/*
+ * Do not expect that dispatching or .queue_rq() can be blocked
+ * after blk_mq_stop_hw_queues() returns. Please use
+ * blk_mq_quiesce_queue() for that requirement.
+ */
 void blk_mq_stop_hw_queues(struct request_queue *q)
 {
__blk_mq_stop_hw_queues(q, false);
-- 
2.9.4



[PATCH 5/6] blk-mq: don't stop queue for quiescing

2017-05-25 Thread Ming Lei
Now we use the QUIESCED flag to drain & block dispatching,
not necessary to stop queue any more.

Also queue can be started by other blk-mq APIs, this limits
uses of blk_mq_quiesce_queue().

Now blk_mq_quiesce_queue() can be used safely and users won't
worry about queue restart during quiescing.

Signed-off-by: Ming Lei 
---
 block/blk-mq.c | 9 ++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/block/blk-mq.c b/block/blk-mq.c
index e1fc9ab50c87..900eb91e0ece 100644
--- a/block/blk-mq.c
+++ b/block/blk-mq.c
@@ -168,8 +168,6 @@ void blk_mq_quiesce_queue(struct request_queue *q)
unsigned int i;
bool rcu = false;
 
-   __blk_mq_stop_hw_queues(q, true);
-
spin_lock_irq(q->queue_lock);
queue_flag_set(QUEUE_FLAG_QUIESCED, q);
spin_unlock_irq(q->queue_lock);
@@ -198,7 +196,12 @@ void blk_mq_unquiesce_queue(struct request_queue *q)
queue_flag_clear(QUEUE_FLAG_QUIESCED, q);
spin_unlock_irq(q->queue_lock);
 
-   blk_mq_start_stopped_hw_queues(q, true);
+   /*
+* During quiescing, requests can be inserted
+* to scheduler queue or sw queue, so we run
+* queues for dispatching these requests.
+*/
+   blk_mq_start_hw_queues(q);
 }
 EXPORT_SYMBOL_GPL(blk_mq_unquiesce_queue);
 
-- 
2.9.4



[PATCH 4/6] blk-mq: update comments on blk_mq_quiesce_queue()

2017-05-25 Thread Ming Lei
Actually what we want to get from blk_mq_quiesce_queue()
isn't only to wait for completion of all ongooing .queue_rq().

In the typical context of canceling requests, we need to
make sure that the following is done in the dispatch path
before starting to cancel requests:
- failed dispatched request is freeed
- busy dispatched request is requeued, and the STARTED
flag is cleared

Signed-off-by: Ming Lei 
---
 block/blk-mq.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/block/blk-mq.c b/block/blk-mq.c
index 864709453c90..e1fc9ab50c87 100644
--- a/block/blk-mq.c
+++ b/block/blk-mq.c
@@ -155,12 +155,12 @@ void blk_mq_unfreeze_queue(struct request_queue *q)
 EXPORT_SYMBOL_GPL(blk_mq_unfreeze_queue);
 
 /**
- * blk_mq_quiesce_queue() - wait until all ongoing queue_rq calls have finished
+ * blk_mq_quiesce_queue() - wait until all ongoing dispatching have finished
  * @q: request queue.
  *
  * Note: this function does not prevent that the struct request end_io()
- * callback function is invoked. Additionally, it is not prevented that
- * new queue_rq() calls occur unless the queue has been stopped first.
+ * callback function is invoked. Once this function is returned, we make
+ * sure no dispatching can happen.
  */
 void blk_mq_quiesce_queue(struct request_queue *q)
 {
-- 
2.9.4



[PATCH 6/6] blk-mq: clarify dispatching may not be drained/blocked by stopping queue

2017-05-25 Thread Ming Lei
BLK_MQ_S_STOPPED may be not observed in other concurrent I/O paths,
we can't guarantee that dispatching won't happen after queue
is stopped.

So clarify the fact and avoid potential misuse.

Signed-off-by: Ming Lei 
---
 block/blk-mq.c | 10 ++
 1 file changed, 10 insertions(+)

diff --git a/block/blk-mq.c b/block/blk-mq.c
index 900eb91e0ece..32bed5bac7da 100644
--- a/block/blk-mq.c
+++ b/block/blk-mq.c
@@ -1240,6 +1240,11 @@ static void __blk_mq_stop_hw_queue(struct blk_mq_hw_ctx 
*hctx, bool sync)
set_bit(BLK_MQ_S_STOPPED, >state);
 }
 
+/*
+ * Do not expect that dispatching or .queue_rq() can be blocked
+ * after blk_mq_stop_hw_queue() returns. Please use
+ * blk_mq_quiesce_queue() for that requirement.
+ */
 void blk_mq_stop_hw_queue(struct blk_mq_hw_ctx *hctx)
 {
__blk_mq_stop_hw_queue(hctx, false);
@@ -1255,6 +1260,11 @@ static void __blk_mq_stop_hw_queues(struct request_queue 
*q, bool sync)
__blk_mq_stop_hw_queue(hctx, sync);
 }
 
+/*
+ * Do not expect that dispatching or .queue_rq() can be blocked
+ * after blk_mq_stop_hw_queues() returns. Please use
+ * blk_mq_quiesce_queue() for that requirement.
+ */
 void blk_mq_stop_hw_queues(struct request_queue *q)
 {
__blk_mq_stop_hw_queues(q, false);
-- 
2.9.4



[PATCH 3/6] blk-mq: fix blk_mq_quiesce_queue

2017-05-25 Thread Ming Lei
blk_mq_quiesce_queue() can not block dispatch in the following
two cases:

- direct issue or BLK_MQ_S_START_ON_RUN
- in theory, new RCU read-side critical sections may begin while
synchronize_rcu() was waiting, and end after returning of
synchronize_rcu().

so a new flag of QUEUE_FLAG_QUIESCED is introduced and evaluated
inside RCU read-side critical sections for fixing the above issues.

This patch fixes request use-after-free during canceling requets
of NVMe in nvme_dev_disable().

Signed-off-by: Ming Lei 
---
 block/blk-mq.c | 33 -
 include/linux/blkdev.h |  2 ++
 2 files changed, 30 insertions(+), 5 deletions(-)

diff --git a/block/blk-mq.c b/block/blk-mq.c
index a26fee3fb389..864709453c90 100644
--- a/block/blk-mq.c
+++ b/block/blk-mq.c
@@ -170,6 +170,10 @@ void blk_mq_quiesce_queue(struct request_queue *q)
 
__blk_mq_stop_hw_queues(q, true);
 
+   spin_lock_irq(q->queue_lock);
+   queue_flag_set(QUEUE_FLAG_QUIESCED, q);
+   spin_unlock_irq(q->queue_lock);
+
queue_for_each_hw_ctx(q, hctx, i) {
if (hctx->flags & BLK_MQ_F_BLOCKING)
synchronize_srcu(>queue_rq_srcu);
@@ -190,6 +194,10 @@ EXPORT_SYMBOL_GPL(blk_mq_quiesce_queue);
  */
 void blk_mq_unquiesce_queue(struct request_queue *q)
 {
+   spin_lock_irq(q->queue_lock);
+   queue_flag_clear(QUEUE_FLAG_QUIESCED, q);
+   spin_unlock_irq(q->queue_lock);
+
blk_mq_start_stopped_hw_queues(q, true);
 }
 EXPORT_SYMBOL_GPL(blk_mq_unquiesce_queue);
@@ -209,6 +217,9 @@ void blk_mq_wake_waiters(struct request_queue *q)
 * the queue are notified as well.
 */
wake_up_all(>mq_freeze_wq);
+
+   /* Forcibly unquiesce the queue to avoid having stuck requests */
+   blk_mq_unquiesce_queue(q);
 }
 
 bool blk_mq_can_queue(struct blk_mq_hw_ctx *hctx)
@@ -1108,13 +1119,15 @@ static void __blk_mq_run_hw_queue(struct blk_mq_hw_ctx 
*hctx)
 
if (!(hctx->flags & BLK_MQ_F_BLOCKING)) {
rcu_read_lock();
-   blk_mq_sched_dispatch_requests(hctx);
+   if (!blk_queue_quiesced(hctx->queue))
+   blk_mq_sched_dispatch_requests(hctx);
rcu_read_unlock();
} else {
might_sleep();
 
srcu_idx = srcu_read_lock(>queue_rq_srcu);
-   blk_mq_sched_dispatch_requests(hctx);
+   if (!blk_queue_quiesced(hctx->queue))
+   blk_mq_sched_dispatch_requests(hctx);
srcu_read_unlock(>queue_rq_srcu, srcu_idx);
}
 }
@@ -1519,9 +1532,14 @@ static void __blk_mq_try_issue_directly(struct request 
*rq, blk_qc_t *cookie,
 static void blk_mq_try_issue_directly(struct blk_mq_hw_ctx *hctx,
struct request *rq, blk_qc_t *cookie)
 {
-   if (!(hctx->flags & BLK_MQ_F_BLOCKING)) {
+   bool blocking = hctx->flags & BLK_MQ_F_BLOCKING;
+   bool quiesced;
+
+   if (!blocking) {
rcu_read_lock();
-   __blk_mq_try_issue_directly(rq, cookie, false);
+   quiesced = blk_queue_quiesced(rq->q);
+   if (!quiesced)
+   __blk_mq_try_issue_directly(rq, cookie, false);
rcu_read_unlock();
} else {
unsigned int srcu_idx;
@@ -1529,9 +1547,14 @@ static void blk_mq_try_issue_directly(struct 
blk_mq_hw_ctx *hctx,
might_sleep();
 
srcu_idx = srcu_read_lock(>queue_rq_srcu);
-   __blk_mq_try_issue_directly(rq, cookie, true);
+   quiesced = blk_queue_quiesced(rq->q);
+   if (!quiesced)
+   __blk_mq_try_issue_directly(rq, cookie, true);
srcu_read_unlock(>queue_rq_srcu, srcu_idx);
}
+
+   if (quiesced)
+   blk_mq_sched_insert_request(rq, false, false, false, blocking);
 }
 
 static blk_qc_t blk_mq_make_request(struct request_queue *q, struct bio *bio)
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index 41291be82ac4..60967797f4f6 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -618,6 +618,7 @@ struct request_queue {
 #define QUEUE_FLAG_STATS   27  /* track rq completion times */
 #define QUEUE_FLAG_POLL_STATS  28  /* collecting stats for hybrid polling 
*/
 #define QUEUE_FLAG_REGISTERED  29  /* queue has been registered to a disk 
*/
+#define QUEUE_FLAG_QUIESCED30  /* queue has been quiesced */
 
 #define QUEUE_FLAG_DEFAULT ((1 << QUEUE_FLAG_IO_STAT) |\
 (1 << QUEUE_FLAG_STACKABLE)|   \
@@ -712,6 +713,7 @@ static inline void queue_flag_clear(unsigned int flag, 
struct request_queue *q)
 #define blk_noretry_request(rq) \
((rq)->cmd_flags & (REQ_FAILFAST_DEV|REQ_FAILFAST_TRANSPORT| \
 REQ_FAILFAST_DRIVER))
+#define blk_queue_quiesced(q)  test_bit(QUEUE_FLAG_QUIESCED, 

[PATCH 1/6] blk-mq: introduce blk_mq_unquiesce_queue

2017-05-25 Thread Ming Lei
Now we use blk_mq_start_stopped_hw_queues() implictely
as pair of blk_mq_quiesce_queue(), now we introduce
blk_mq_unquiesce_queue() explictely.

Also this function is introduced for fixing
current quiescing mechanism, which will be done
in the following patches.

Signed-off-by: Ming Lei 
---
 block/blk-mq.c | 13 +
 include/linux/blkdev.h |  1 +
 2 files changed, 14 insertions(+)

diff --git a/block/blk-mq.c b/block/blk-mq.c
index f2224ffd225d..a26fee3fb389 100644
--- a/block/blk-mq.c
+++ b/block/blk-mq.c
@@ -181,6 +181,19 @@ void blk_mq_quiesce_queue(struct request_queue *q)
 }
 EXPORT_SYMBOL_GPL(blk_mq_quiesce_queue);
 
+/**
+ * blk_mq_unquiesce_queue() - pair of blk_mq_quiesce_queue()
+ * @q: request queue.
+ *
+ * This function recovers queue into the state before quiescing
+ * done by blk_mq_quiesce_queue
+ */
+void blk_mq_unquiesce_queue(struct request_queue *q)
+{
+   blk_mq_start_stopped_hw_queues(q, true);
+}
+EXPORT_SYMBOL_GPL(blk_mq_unquiesce_queue);
+
 void blk_mq_wake_waiters(struct request_queue *q)
 {
struct blk_mq_hw_ctx *hctx;
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index ab92c4ea138b..41291be82ac4 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -964,6 +964,7 @@ extern void __blk_run_queue_uncond(struct request_queue *q);
 extern void blk_run_queue(struct request_queue *);
 extern void blk_run_queue_async(struct request_queue *q);
 extern void blk_mq_quiesce_queue(struct request_queue *q);
+extern void blk_mq_unquiesce_queue(struct request_queue *q);
 extern int blk_rq_map_user(struct request_queue *, struct request *,
   struct rq_map_data *, void __user *, unsigned long,
   gfp_t);
-- 
2.9.4



[PATCH 0/6] blk-mq: fix & improve queue quiescing

2017-05-25 Thread Ming Lei
Hi,

There are some issues in current blk_mq_quiesce_queue():
- in case of direct issue or BLK_MQ_S_START_ON_RUN, dispatch won't
can't be prevented after blk_mq_quiesce_queue() is returned.
- in theory, new RCU read-side critical sections may begin while
synchronize_rcu() was waiting, and end after returning of
synchronize_rcu(), then dispatch still may be run after
synchronize_rcu() returns..

It is observed that request double-free/use-after-free
can be triggered easily when canceling NVMe requests via
blk_mq_tagset_busy_iter(...nvme_cancel_request) in nvme_dev_disable().
The reason is that blk_mq_quiesce_queue() can't prevent
dispatching from being run during the period.

Actually we have to quiesce queue for canceling dispatched
requests via blk_mq_tagset_busy_iter(), otherwise use-after-free
can be made easily. This way of canceling dispatched requests
has been used in several drivers, only NVMe uses blk_mq_quiesce_queue()
to avoid the issue, and others need to be fixed too. And it
is a common case for recovering dead controller.

blk_mq_quiesce_queue() is implemented via stopping queue, which
limits its uses, and easy to casue race, any start queue is other
paths may break blk_mq_quiesce_queue(). For example, we sometimes
stops queue when hw can't handle so many ongoing requests and restart
queues after srequests are completed. Meantime when we want to cancel
requests if hardware is dead, quiescing is run first, then the restarting
in complete path can break the quiescing. This patch improves this
interface via removing stopping queue, then it can be easier to use.
 
Thanks,
Ming

Ming Lei (6):
  blk-mq: introduce blk_mq_unquiesce_queue
  blk-mq: use the introduced blk_mq_unquiesce_queue()
  blk-mq: fix blk_mq_quiesce_queue
  blk-mq: update comments on blk_mq_quiesce_queue()
  blk-mq: don't stop queue for quiescing
  blk-mq: clarify dispatching won't be blocked by stopping queue

 block/blk-mq.c   | 67 +---
 drivers/md/dm-rq.c   |  2 +-
 drivers/nvme/host/core.c |  2 +-
 drivers/scsi/scsi_lib.c  |  5 +++-
 include/linux/blkdev.h   |  3 +++
 5 files changed, 67 insertions(+), 12 deletions(-)

-- 
2.9.4



[PATCH 2/6] blk-mq: use the introduced blk_mq_unquiesce_queue()

2017-05-25 Thread Ming Lei
blk_mq_unquiesce_queue() is used for unquiescing the queue.

Signed-off-by: Ming Lei 
---
 drivers/md/dm-rq.c   | 2 +-
 drivers/nvme/host/core.c | 2 +-
 drivers/scsi/scsi_lib.c  | 5 -
 3 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/drivers/md/dm-rq.c b/drivers/md/dm-rq.c
index 2af27026aa2e..673fcf075077 100644
--- a/drivers/md/dm-rq.c
+++ b/drivers/md/dm-rq.c
@@ -71,7 +71,7 @@ static void dm_old_start_queue(struct request_queue *q)
 
 static void dm_mq_start_queue(struct request_queue *q)
 {
-   blk_mq_start_stopped_hw_queues(q, true);
+   blk_mq_unquiesce_queue(q);
blk_mq_kick_requeue_list(q);
 }
 
diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
index 04e115834702..231d36028afc 100644
--- a/drivers/nvme/host/core.c
+++ b/drivers/nvme/host/core.c
@@ -2514,7 +2514,7 @@ void nvme_start_queues(struct nvme_ctrl *ctrl)
 
mutex_lock(>namespaces_mutex);
list_for_each_entry(ns, >namespaces, list) {
-   blk_mq_start_stopped_hw_queues(ns->queue, true);
+   blk_mq_unquiesce_queue(ns->queue);
blk_mq_kick_requeue_list(ns->queue);
}
mutex_unlock(>namespaces_mutex);
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index 814a4bd8405d..72b11f75719c 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -3030,7 +3030,10 @@ scsi_internal_device_unblock(struct scsi_device *sdev,
return -EINVAL;
 
if (q->mq_ops) {
-   blk_mq_start_stopped_hw_queues(q, false);
+   if (blk_queue_quiesced(q))
+   blk_mq_unquiesce_queue(q);
+   else
+   blk_mq_start_stopped_hw_queues(q, false);
} else {
spin_lock_irqsave(q->queue_lock, flags);
blk_start_queue(q);
-- 
2.9.4



Re: [PATCH v2 02/22] scsi: hisi_sas: optimise the usage of hisi_hba.lock

2017-05-25 Thread kbuild test robot
Hi Xiang,

[auto build test ERROR on mkp-scsi/for-next]
[also build test ERROR on v4.12-rc2 next-20170525]
[if your patch is applied to the wrong git tree, please drop us a note to help 
improve the system]

url:
https://github.com/0day-ci/linux/commits/John-Garry/hisi_sas-hip08-support/20170526-011153
base:   https://git.kernel.org/pub/scm/linux/kernel/git/mkp/scsi.git for-next
config: i386-allmodconfig (attached as .config)
compiler: gcc-6 (Debian 6.2.0-3) 6.2.0 20160901
reproduce:
# save the attached .config to linux build tree
make ARCH=i386 

All errors (new ones prefixed by >>):

>> ERROR: "__umoddi3" [drivers/scsi/hisi_sas/hisi_sas_main.ko] undefined!

---
0-DAY kernel test infrastructureOpen Source Technology Center
https://lists.01.org/pipermail/kbuild-all   Intel Corporation


.config.gz
Description: application/gzip


[PATCH] qla2xxx: Fix extraneous ref on sp's after adapter break

2017-05-25 Thread Bill Kuzeja
Hung task timeouts can result if a qlogic board breaks unexpectedly while
running I/O. These tasks become hung because command srb reference counts
are not going to zero, hence the affected srbs and commands do not get 
freed. This fix accounts for this extra reference in the srbs in the case 
of a board failure. 

Fixes: a465537ad1a4 ("qla2xxx: Disable the adapter and skip error recovery in 
case of register disconnect")
Signed-off-by: Bill Kuzeja 
---

I encountered this hang during by adapter break testing (on Stratus
hardware, we need to survive adapter breaks). I noticed that we wait
indefinitely for several commands which all have outstanding
references to them, but which have all followed this code path:

qla2x00_abort_all_cmds
   qla2xxx_eh_abort
   Exit early because qla2x00_isp_reg_stat is non-zero.

Note that before calling qla2xxx_eh_abort from this path, we do an extra
sp_get(sp), which takes out another reference on the current sp. If we
do not early exit immediately from qla2xxx_eh_abort, we'll get rid of this
extra reference through the abort - which is the normal case.

However, if we exit immediately, this extra sp_get is never dereferenced.
Each one of the commands flowing through this code will be stuck forever,
resulting in hung tasks.

This early exit in qla2xxx_eh_abort was introduced by this commit:

commit a465537ad1a4 ("qla2xxx: Disable the adapter and skip error recovery in 
case of register disconnect.")

The solution for this is somewhat tricky because qla2xxx_eh_abort can be
invoked from the scsi error handler as well as qla2x00_abort_all_cmds.
I originally thought of having qla2xxx_eh_abort remove the extra reference
before early exiting, but not knowing the context of its invocation,
this seemed dangerous.

Alternatively we could also just remove the early exit case from
qla2xxx_eh_abort, but the aforementioned commit was put in for a reason, so 
that doesn't seem correct either.

The right thing to do seems to be putting the fix in qla2x00_abort_all_cmds,
checking the conditions we where we have exited early from qla2xxx_eh_abort
before removing the extra reference.

---
 drivers/scsi/qla2xxx/qla_os.c | 7 +--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index 1c79579..1a93365 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -1632,7 +1632,7 @@ uint32_t qla2x00_isp_reg_stat(struct qla_hw_data *ha)
 void
 qla2x00_abort_all_cmds(scsi_qla_host_t *vha, int res)
 {
-   int que, cnt;
+   int que, cnt, status;
unsigned long flags;
srb_t *sp;
struct qla_hw_data *ha = vha->hw;
@@ -1662,8 +1662,12 @@ uint32_t qla2x00_isp_reg_stat(struct qla_hw_data *ha)
 */
sp_get(sp);

spin_unlock_irqrestore(>hardware_lock, flags);
-   qla2xxx_eh_abort(GET_CMD_SP(sp));
+   status = 
qla2xxx_eh_abort(GET_CMD_SP(sp));
spin_lock_irqsave(>hardware_lock, 
flags);
+   /* Get rid of extra reference if 
immediate exit
+* from ql2xxx_eh_abort */
+   if (status == FAILED && 
(qla2x00_isp_reg_stat(ha)))
+   atomic_dec(>ref_count);
}
req->outstanding_cmds[cnt] = NULL;
sp->done(sp, res);
-- 
1.8.3.1



Re: [PATCH 2/2] libsas: Enhance libsas hotplug

2017-05-25 Thread wangyijing
Hi John, thanks for your review and comments!

在 2017/5/25 17:04, John Garry 写道:
> Hi,
> 
> There are some comments, inline.
> 
> In general, if it works, it looks ok.
> 
> Other reviews would be greatly appreciated - Hannes, Christoph, Johannes, Dan 
> - please.
> 
>> Libsas complete a hotplug event notified by LLDD in several works,
>> for example, if libsas receive a PHYE_LOSS_OF_SIGNAL, we process it
>> in following steps:
>>
>> notify_phy_event[interrupt context]
>> sas_queue_event[queue work on shost->work_q]
>> sas_phye_loss_of_signal[running in shost->work_q]
>> sas_deform_port[remove sas port]
>> sas_unregister_dev
>> sas_discover_event[queue destruct work on 
>> shost->work_q tail]
>>
>> In above case, complete whole hotplug in two works, remove sas port first, 
>> then
>> put the destruction of device in another work and queue it on in the tail of
>> workqueue, since sas port is the parent of the children rphy device, so if 
>> remove
>> sas port first, the children rphy device would also be deleted, when the 
>> destruction
>> work coming, it would find the target has been removed already, and report a
>> sysfs warning calltrace.
>>
>> queue tail queue head
>> DISCE_DESTRUCT> PORTE_BYTES_DMAED event 
>> ->PHYE_LOSS_OF_SIGNAL[running]
>>
>> There are other hotplug issues in current framework, in above case, if there 
>> is
>> hotadd sas event queued between hotremove works, the hotplug order would be 
>> broken
>> and unexpected issues would happen.
>>
>> In this patch, we try to solve these issues in following steps:
>> 1. create a new workqueue used to run sas event work, instead of scsi host 
>> workqueue,
>>because we may block sas event work, we cannot block the normal scsi 
>> works.
> 
> What do we block the event work for?

When libsas receive a phy down event, sas_deform_port would be called, and now 
we block sas_deform_port
and wait for destruction work finish, in sas_destruct_devices, we may wait ata 
error handler, it would
take a long time, so if do all stuff in scsi host workq, libsas may block other 
scsi works too long.

> 
>> 2. create a new workqueue used to run sas discovery events work, instead of 
>> scsi host
>>workqueue, because in some cases, eg. in revalidate domain event, we may 
>> unregister
>>a sas device and discover new one, we must sync the execution, wait the 
>> remove process
>>finish, then start a new discovery. So we must put the probe and destruct 
>> discovery
>>events in a new workqueue to avoid deadlock.
>> 3. introudce a asd_sas_port level wait-complete and a sas_discovery level 
>> wait-complete
>>we use former wait-complete to achieve a sas event atomic process and use 
>> latter to
>>make a sas discovery sync.
>> 4. remove disco_mutex in sas_revalidate_domain, since now 
>> sas_revalidate_domain sync
>>the destruct discovery event execution, it's no need to lock disco mutex 
>> there.
>>
>> Signed-off-by: Yijing Wang 
>> ---
>>  drivers/scsi/libsas/sas_discover.c | 58 
>> --
>>  drivers/scsi/libsas/sas_event.c|  2 +-
>>  drivers/scsi/libsas/sas_expander.c |  9 +-
>>  drivers/scsi/libsas/sas_init.c | 31 +++-
>>  drivers/scsi/libsas/sas_internal.h | 50 
>>  drivers/scsi/libsas/sas_port.c |  4 +++
>>  include/scsi/libsas.h  | 11 +++-
>>  7 files changed, 146 insertions(+), 19 deletions(-)
>>
>> diff --git a/drivers/scsi/libsas/sas_discover.c 
>> b/drivers/scsi/libsas/sas_discover.c
>> index 60de662..43e8a1e 100644
>> --- a/drivers/scsi/libsas/sas_discover.c
>> +++ b/drivers/scsi/libsas/sas_discover.c
>> @@ -503,11 +503,10 @@ static void sas_revalidate_domain(struct work_struct 
>> *work)
>>  struct domain_device *ddev = port->port_dev;
>>
>>  /* prevent revalidation from finding sata links in recovery */
>> -mutex_lock(>disco_mutex);
>>  if (test_bit(SAS_HA_ATA_EH_ACTIVE, >state)) {
>>  SAS_DPRINTK("REVALIDATION DEFERRED on port %d, pid:%d\n",
>>  port->id, task_pid_nr(current));
>> -goto out;
>> +return;
>>  }
>>
>>  clear_bit(DISCE_REVALIDATE_DOMAIN, >disc.pending);
>> @@ -521,20 +520,57 @@ static void sas_revalidate_domain(struct work_struct 
>> *work)
>>
>>  SAS_DPRINTK("done REVALIDATING DOMAIN on port %d, pid:%d, res 0x%x\n",
>>  port->id, task_pid_nr(current), res);
>> - out:
>> -mutex_unlock(>disco_mutex);
>> +}
>> +
>> +static const work_func_t sas_event_fns[DISC_NUM_EVENTS] = {
>> +[DISCE_DISCOVER_DOMAIN] = sas_discover_domain,
>> +[DISCE_REVALIDATE_DOMAIN] = sas_revalidate_domain,
>> +[DISCE_PROBE] = sas_probe_devices,
>> +[DISCE_SUSPEND] = sas_suspend_devices,
>> +[DISCE_RESUME] = sas_resume_devices,
>> +[DISCE_DESTRUCT] = 

[PATCH v2 08/22] scsi: hisi_sas: add skeleton v3 hw driver

2017-05-25 Thread John Garry
Add skeleton driver for v3 hw in hisi_sas_v3_hw.c

File hisi_sas_v3_hw.c will serve 2 purposes:
- probing and initialisation of the controller based on pci device
- hw layer for v3-based controllers

The controller design is quite similar to v2 hw in hip07.

However key differences include:
-All v2 hw bugs are fixed (hopefully), so workarounds are not
required
-support for device deregistration
-some interrupt modifications
-configurable max device support

Signed-off-by: John Garry 
Signed-off-by: Xiang Chen 
---
 drivers/scsi/hisi_sas/Kconfig  | 10 +++-
 drivers/scsi/hisi_sas/Makefile |  1 +
 drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 47 ++
 3 files changed, 57 insertions(+), 1 deletion(-)
 create mode 100644 drivers/scsi/hisi_sas/hisi_sas_v3_hw.c

diff --git a/drivers/scsi/hisi_sas/Kconfig b/drivers/scsi/hisi_sas/Kconfig
index 374a329..d42f29a 100644
--- a/drivers/scsi/hisi_sas/Kconfig
+++ b/drivers/scsi/hisi_sas/Kconfig
@@ -6,4 +6,12 @@ config SCSI_HISI_SAS
select BLK_DEV_INTEGRITY
depends on ATA
help
-   This driver supports HiSilicon's SAS HBA
+   This driver supports HiSilicon's SAS HBA, including support 
based
+   on platform device
+
+config SCSI_HISI_SAS_PCI
+   tristate "HiSilicon SAS on PCI bus"
+   depends on SCSI_HISI_SAS
+   depends on PCI
+   help
+   This driver supports HiSilicon's SAS HBA based on PCI device
diff --git a/drivers/scsi/hisi_sas/Makefile b/drivers/scsi/hisi_sas/Makefile
index c6d3a1b..24623f2 100644
--- a/drivers/scsi/hisi_sas/Makefile
+++ b/drivers/scsi/hisi_sas/Makefile
@@ -1,2 +1,3 @@
 obj-$(CONFIG_SCSI_HISI_SAS)+= hisi_sas_main.o
 obj-$(CONFIG_SCSI_HISI_SAS)+= hisi_sas_v1_hw.o hisi_sas_v2_hw.o
+obj-$(CONFIG_SCSI_HISI_SAS_PCI)+= hisi_sas_v3_hw.o
diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c 
b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
new file mode 100644
index 000..cf72577
--- /dev/null
+++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2017 Hisilicon Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include "hisi_sas.h"
+#define DRV_NAME "hisi_sas_v3_hw"
+
+static int
+hisi_sas_v3_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+   return 0;
+}
+
+static void hisi_sas_v3_remove(struct pci_dev *pdev)
+{
+}
+
+enum {
+   /* instances of the controller */
+   hip08,
+};
+
+static const struct pci_device_id sas_v3_pci_table[] = {
+   { PCI_VDEVICE(HUAWEI, 0xa230), hip08 },
+   {}
+};
+
+static struct pci_driver sas_v3_pci_driver = {
+   .name   = DRV_NAME,
+   .id_table   = sas_v3_pci_table,
+   .probe  = hisi_sas_v3_probe,
+   .remove = hisi_sas_v3_remove,
+};
+
+module_pci_driver(sas_v3_pci_driver);
+
+MODULE_VERSION(DRV_VERSION);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("John Garry ");
+MODULE_DESCRIPTION("HISILICON SAS controller v3 hw driver based on pci 
device");
+MODULE_ALIAS("platform:" DRV_NAME);
-- 
1.9.1



[PATCH v2 11/22] scsi: hisi_sas: add v3 hw PHY init

2017-05-25 Thread John Garry
From: Xiang Chen 

Add code to configure PHYs for v3 hw.

Signed-off-by: John Garry 
Signed-off-by: Xiang Chen 
---
 drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 126 +
 1 file changed, 126 insertions(+)

diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c 
b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
index 316938a..e3e3734 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
@@ -19,6 +19,10 @@
 #define ITCT_BASE_ADDR_HI  0x14
 #define IO_BROKEN_MSG_ADDR_LO  0x18
 #define IO_BROKEN_MSG_ADDR_HI  0x1c
+#define PHY_CONTEXT0x20
+#define PHY_STATE  0x24
+#define PHY_PORT_NUM_MA0x28
+#define PHY_CONN_RATE  0x30
 #define AXI_AHB_CLK_CFG0x3c
 #define AXI_USER1  0x48
 #define AXI_USER2  0x4c
@@ -42,6 +46,7 @@
 #define CFG_SET_ABORTED_IPTT_OFF   0
 #define CFG_SET_ABORTED_IPTT_MSK   (0xfff << CFG_SET_ABORTED_IPTT_OFF)
 #define CFG_1US_TIMER_TRSH 0xcc
+#define CHNL_INT_STATUS0x148
 #define INT_COAL_EN0x19c
 #define OQ_INT_COAL_TIME   0x1a0
 #define OQ_INT_COAL_CNT0x1a4
@@ -68,9 +73,11 @@
 #define ENT_INT_SRC_MSK1   0x1c4
 #define ENT_INT_SRC_MSK2   0x1c8
 #define ENT_INT_SRC_MSK3   0x1cc
+#define ENT_INT_SRC_MSK3_ENT95_MSK_OFF 31
 #define CHNL_PHYUPDOWN_INT_MSK 0x1d0
 #define CHNL_ENT_INT_MSK   0x1d4
 #define HGC_COM_INT_MSK0x1d8
+#define ENT_INT_SRC_MSK3_ENT95_MSK_MSK (0x1 << ENT_INT_SRC_MSK3_ENT95_MSK_OFF)
 #define SAS_ECC_INTR   0x1e8
 #define SAS_ECC_INTR_MSK   0x1ec
 #define HGC_ERR_STAT_EN0x238
@@ -91,11 +98,33 @@
 
 /* phy registers requiring init */
 #define PORT_BASE  (0x2000)
+#define PHY_CFG(PORT_BASE + 0x0)
+#define HARD_PHY_LINKRATE  (PORT_BASE + 0x4)
+#define PHY_CFG_ENA_OFF0
+#define PHY_CFG_ENA_MSK(0x1 << PHY_CFG_ENA_OFF)
+#define PHY_CFG_DC_OPT_OFF 2
+#define PHY_CFG_DC_OPT_MSK (0x1 << PHY_CFG_DC_OPT_OFF)
 #define PROG_PHY_LINK_RATE (PORT_BASE + 0x8)
 #define PHY_CTRL   (PORT_BASE + 0x14)
 #define PHY_CTRL_RESET_OFF 0
 #define PHY_CTRL_RESET_MSK (0x1 << PHY_CTRL_RESET_OFF)
 #define SL_CFG (PORT_BASE + 0x84)
+#define SL_CONTROL (PORT_BASE + 0x94)
+#define SL_CONTROL_NOTIFY_EN_OFF   0
+#define SL_CONTROL_NOTIFY_EN_MSK   (0x1 << SL_CONTROL_NOTIFY_EN_OFF)
+#define SL_CTA_OFF 17
+#define SL_CTA_MSK (0x1 << SL_CTA_OFF)
+#define TX_ID_DWORD0   (PORT_BASE + 0x9c)
+#define TX_ID_DWORD1   (PORT_BASE + 0xa0)
+#define TX_ID_DWORD2   (PORT_BASE + 0xa4)
+#define TX_ID_DWORD3   (PORT_BASE + 0xa8)
+#define TX_ID_DWORD4   (PORT_BASE + 0xaC)
+#define TX_ID_DWORD5   (PORT_BASE + 0xb0)
+#define TX_ID_DWORD6   (PORT_BASE + 0xb4)
+#define TXID_AUTO  (PORT_BASE + 0xb8)
+#define CT3_OFF1
+#define CT3_MSK(0x1 << CT3_OFF)
+#define RX_IDAF_DWORD0 (PORT_BASE + 0xc4)
 #define RXOP_CHECK_CFG_H   (PORT_BASE + 0xfc)
 #define SAS_SSP_CON_TIMER_CFG  (PORT_BASE + 0x134)
 #define SAS_SMP_CON_TIMER_CFG  (PORT_BASE + 0x138)
@@ -136,6 +165,13 @@ struct hisi_sas_complete_v3_hdr {
 };
 
 #define HISI_SAS_COMMAND_ENTRIES_V3_HW 4096
+#define HISI_SAS_MSI_COUNT_V3_HW 32
+
+enum {
+   HISI_SAS_PHY_PHY_UPDOWN,
+   HISI_SAS_PHY_CHNL_INT,
+   HISI_SAS_PHY_INT_NR
+};
 
 static const struct hisi_sas_hw hisi_sas_v3_hw;
 
@@ -154,6 +190,14 @@ static void hisi_sas_phy_write32(struct hisi_hba 
*hisi_hba, int phy_no,
writel(val, regs);
 }
 
+static u32 hisi_sas_phy_read32(struct hisi_hba *hisi_hba,
+ int phy_no, u32 off)
+{
+   void __iomem *regs = hisi_hba->regs + (0x400 * phy_no) + off;
+
+   return readl(regs);
+}
+
 static void init_reg_v3_hw(struct hisi_hba *hisi_hba)
 {
int i;
@@ -268,6 +312,45 @@ static void init_reg_v3_hw(struct hisi_hba *hisi_hba)
 upper_32_bits(hisi_hba->initial_fis_dma));
 }
 
+static void config_phy_opt_mode_v3_hw(struct hisi_hba *hisi_hba, int phy_no)
+{
+   u32 cfg = hisi_sas_phy_read32(hisi_hba, phy_no, PHY_CFG);
+
+   cfg &= ~PHY_CFG_DC_OPT_MSK;
+   cfg |= 1 << PHY_CFG_DC_OPT_OFF;
+   hisi_sas_phy_write32(hisi_hba, phy_no, PHY_CFG, cfg);
+}
+
+static void config_id_frame_v3_hw(struct 

[PATCH v2 06/22] scsi: hisi_sas: add pci_dev in hisi_hba struct

2017-05-25 Thread John Garry
Since hip08 SAS controller is based on pci device, add hisi_hba.pci_dev
for hip08 (will be v3), and also rename hisi_hba.pdev to .platform_dev
for clarity.

In addition, for common code which wants to reference the controller
device struct, add hisi_hba.dev, and change the common code to use
it.

Signed-off-by: John Garry 
Signed-off-by: Xiang Chen 
---
 drivers/scsi/hisi_sas/hisi_sas.h   |  6 -
 drivers/scsi/hisi_sas/hisi_sas_main.c  | 36 ++--
 drivers/scsi/hisi_sas/hisi_sas_v1_hw.c | 28 +++---
 drivers/scsi/hisi_sas/hisi_sas_v2_hw.c | 44 +-
 4 files changed, 59 insertions(+), 55 deletions(-)

diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h
index 210daec..18044b7 100644
--- a/drivers/scsi/hisi_sas/hisi_sas.h
+++ b/drivers/scsi/hisi_sas/hisi_sas.h
@@ -18,6 +18,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -189,7 +190,10 @@ struct hisi_hba {
/* This must be the first element, used by SHOST_TO_SAS_HA */
struct sas_ha_struct *p;
 
-   struct platform_device *pdev;
+   struct platform_device *platform_dev;
+   struct pci_dev *pci_dev;
+   struct device *dev;
+
void __iomem *regs;
struct regmap *ctrl;
u32 ctrl_reset_reg;
diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c 
b/drivers/scsi/hisi_sas/hisi_sas_main.c
index aaa7f5d..d4809cf 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_main.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_main.c
@@ -174,7 +174,7 @@ void hisi_sas_slot_task_free(struct hisi_hba *hisi_hba, 
struct sas_task *task,
 {
 
if (task) {
-   struct device *dev = _hba->pdev->dev;
+   struct device *dev = hisi_hba->dev;
struct domain_device *device = task->dev;
struct hisi_sas_device *sas_dev = device->lldd_dev;
 
@@ -251,7 +251,7 @@ static void hisi_sas_slot_abort(struct work_struct *work)
struct scsi_cmnd *cmnd = task->uldd_task;
struct hisi_sas_tmf_task tmf_task;
struct scsi_lun lun;
-   struct device *dev = _hba->pdev->dev;
+   struct device *dev = hisi_hba->dev;
int tag = abort_slot->idx;
unsigned long flags;
 
@@ -285,7 +285,7 @@ static int hisi_sas_task_prep(struct sas_task *task, struct 
hisi_sas_dq
struct hisi_sas_slot *slot;
struct hisi_sas_cmd_hdr *cmd_hdr_base;
struct asd_sas_port *sas_port = device->port;
-   struct device *dev = _hba->pdev->dev;
+   struct device *dev = hisi_hba->dev;
int dlvry_queue_slot, dlvry_queue, n_elem = 0, rc, slot_idx;
unsigned long flags;
 
@@ -455,7 +455,7 @@ static int hisi_sas_task_exec(struct sas_task *task, gfp_t 
gfp_flags,
u32 pass = 0;
unsigned long flags;
struct hisi_hba *hisi_hba = dev_to_hisi_hba(task->dev);
-   struct device *dev = _hba->pdev->dev;
+   struct device *dev = hisi_hba->dev;
struct domain_device *device = task->dev;
struct hisi_sas_device *sas_dev = device->lldd_dev;
int queue_id = sas_dev->device_id % hisi_hba->queue_count;
@@ -547,7 +547,7 @@ static int hisi_sas_dev_found(struct domain_device *device)
struct hisi_hba *hisi_hba = dev_to_hisi_hba(device);
struct domain_device *parent_dev = device->parent;
struct hisi_sas_device *sas_dev;
-   struct device *dev = _hba->pdev->dev;
+   struct device *dev = hisi_hba->dev;
 
if (hisi_hba->hw->alloc_dev)
sas_dev = hisi_hba->hw->alloc_dev(device);
@@ -732,7 +732,7 @@ static void hisi_sas_dev_gone(struct domain_device *device)
 {
struct hisi_sas_device *sas_dev = device->lldd_dev;
struct hisi_hba *hisi_hba = dev_to_hisi_hba(device);
-   struct device *dev = _hba->pdev->dev;
+   struct device *dev = hisi_hba->dev;
u64 dev_id = sas_dev->device_id;
 
dev_info(dev, "found dev[%lld:%x] is gone\n",
@@ -815,7 +815,7 @@ static int hisi_sas_exec_internal_tmf_task(struct 
domain_device *device,
 {
struct hisi_sas_device *sas_dev = device->lldd_dev;
struct hisi_hba *hisi_hba = sas_dev->hisi_hba;
-   struct device *dev = _hba->pdev->dev;
+   struct device *dev = hisi_hba->dev;
struct sas_task *task;
int res, retry;
 
@@ -932,7 +932,7 @@ static int hisi_sas_softreset_ata_disk(struct domain_device 
*device)
struct ata_link *link;
int rc = TMF_RESP_FUNC_FAILED;
struct hisi_hba *hisi_hba = dev_to_hisi_hba(device);
-   struct device *dev = _hba->pdev->dev;
+   struct device *dev = hisi_hba->dev;
int s = sizeof(struct host_to_dev_fis);
unsigned long flags;
 
@@ -990,7 +990,7 @@ static int hisi_sas_controller_reset(struct hisi_hba 
*hisi_hba)
return -1;
 
if (!test_and_set_bit(HISI_SAS_RESET_BIT, _hba->flags)) {
-   struct device *dev = 

[PATCH v2 09/22] scsi: hisi_sas: add initialisation for v3 pci-based controller

2017-05-25 Thread John Garry
Add the code to initialise the controller which is based on pci
device in hisi_sas_v3_hw.c

The core controller routines are still in hisi_sas_main.c; some
common initialisation functions are also exported from
hisi_sas_main.c

For pci-based controller, the device properties, like
phy count and sas address are read from the firmware,
same as platform device-based controller.

Signed-off-by: John Garry 
Signed-off-by: Xiang Chen 
---
 drivers/scsi/hisi_sas/hisi_sas.h   |   6 ++
 drivers/scsi/hisi_sas/hisi_sas_main.c  |  18 ++--
 drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 157 +
 3 files changed, 175 insertions(+), 6 deletions(-)

diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h
index d029aba..ce2666b 100644
--- a/drivers/scsi/hisi_sas/hisi_sas.h
+++ b/drivers/scsi/hisi_sas/hisi_sas.h
@@ -359,6 +359,12 @@ struct hisi_sas_command_table_ssp {
struct hisi_sas_command_table_stp stp;
 };
 
+extern struct scsi_transport_template *hisi_sas_stt;
+extern struct scsi_host_template *hisi_sas_sht;
+
+extern void hisi_sas_init_add(struct hisi_hba *hisi_hba);
+extern int hisi_sas_alloc(struct hisi_hba *hisi_hba, struct Scsi_Host *shost);
+extern void hisi_sas_free(struct hisi_hba *hisi_hba);
 extern u8 hisi_sas_get_ata_protocol(u8 cmd, int direction);
 extern struct hisi_sas_port *to_hisi_sas_port(struct asd_sas_port *sas_port);
 extern void hisi_sas_sata_done(struct sas_task *task,
diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c 
b/drivers/scsi/hisi_sas/hisi_sas_main.c
index 58c6cf0..fba06fb 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_main.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_main.c
@@ -1476,9 +1476,10 @@ void hisi_sas_rescan_topology(struct hisi_hba *hisi_hba, 
u32 old_state,
 }
 EXPORT_SYMBOL_GPL(hisi_sas_rescan_topology);
 
-static struct scsi_transport_template *hisi_sas_stt;
+struct scsi_transport_template *hisi_sas_stt;
+EXPORT_SYMBOL_GPL(hisi_sas_stt);
 
-static struct scsi_host_template hisi_sas_sht = {
+static struct scsi_host_template _hisi_sas_sht = {
.module = THIS_MODULE,
.name   = DRV_NAME,
.queuecommand   = sas_queuecommand,
@@ -1498,6 +1499,8 @@ void hisi_sas_rescan_topology(struct hisi_hba *hisi_hba, 
u32 old_state,
.target_destroy = sas_target_destroy,
.ioctl  = sas_ioctl,
 };
+struct scsi_host_template *hisi_sas_sht = &_hisi_sas_sht;
+EXPORT_SYMBOL_GPL(hisi_sas_sht);
 
 static struct sas_domain_function_template hisi_sas_transport_ops = {
.lldd_dev_found = hisi_sas_dev_found,
@@ -1545,7 +1548,7 @@ void hisi_sas_init_mem(struct hisi_hba *hisi_hba)
 }
 EXPORT_SYMBOL_GPL(hisi_sas_init_mem);
 
-static int hisi_sas_alloc(struct hisi_hba *hisi_hba, struct Scsi_Host *shost)
+int hisi_sas_alloc(struct hisi_hba *hisi_hba, struct Scsi_Host *shost)
 {
struct device *dev = hisi_hba->dev;
int i, s, max_command_entries = hisi_hba->hw->max_command_entries;
@@ -1664,8 +1667,9 @@ static int hisi_sas_alloc(struct hisi_hba *hisi_hba, 
struct Scsi_Host *shost)
 err_out:
return -ENOMEM;
 }
+EXPORT_SYMBOL_GPL(hisi_sas_alloc);
 
-static void hisi_sas_free(struct hisi_hba *hisi_hba)
+void hisi_sas_free(struct hisi_hba *hisi_hba)
 {
struct device *dev = hisi_hba->dev;
int i, s, max_command_entries = hisi_hba->hw->max_command_entries;
@@ -1720,6 +1724,7 @@ static void hisi_sas_free(struct hisi_hba *hisi_hba)
if (hisi_hba->wq)
destroy_workqueue(hisi_hba->wq);
 }
+EXPORT_SYMBOL_GPL(hisi_sas_free);
 
 static void hisi_sas_rst_work_handler(struct work_struct *work)
 {
@@ -1801,7 +1806,7 @@ static struct Scsi_Host *hisi_sas_shost_alloc(struct 
platform_device *pdev,
struct hisi_hba *hisi_hba;
struct device *dev = >dev;
 
-   shost = scsi_host_alloc(_sas_sht, sizeof(*hisi_hba));
+   shost = scsi_host_alloc(hisi_sas_sht, sizeof(*hisi_hba));
if (!shost) {
dev_err(dev, "scsi host alloc failed\n");
return NULL;
@@ -1843,7 +1848,7 @@ static struct Scsi_Host *hisi_sas_shost_alloc(struct 
platform_device *pdev,
return NULL;
 }
 
-static void hisi_sas_init_add(struct hisi_hba *hisi_hba)
+void hisi_sas_init_add(struct hisi_hba *hisi_hba)
 {
int i;
 
@@ -1852,6 +1857,7 @@ static void hisi_sas_init_add(struct hisi_hba *hisi_hba)
   hisi_hba->sas_addr,
   SAS_ADDR_SIZE);
 }
+EXPORT_SYMBOL_GPL(hisi_sas_init_add);
 
 int hisi_sas_probe(struct platform_device *pdev,
 const struct hisi_sas_hw *hw)
diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c 
b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
index cf72577..05976db 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
@@ -11,16 +11,173 @@
 #include "hisi_sas.h"
 #define DRV_NAME "hisi_sas_v3_hw"
 
+static const 

[PATCH v2 02/22] scsi: hisi_sas: optimise the usage of hisi_hba.lock

2017-05-25 Thread John Garry
From: Xiang Chen 

Currently hisi_hba.lock is locked to deliver and receive a
command to/from any hw queue. This causes much
contention at high data-rates.

To boost performance, lock on a per queue basis for
sending and receiving commands to/from hw.

Certain critical regions still need to be locked in the delivery
and completion stages with hisi_hba.lock.

Signed-off-by: Xiang Chen 
Signed-off-by: John Garry 
---
 drivers/scsi/hisi_sas/hisi_sas.h   |  8 ++---
 drivers/scsi/hisi_sas/hisi_sas_main.c  | 59 ++
 drivers/scsi/hisi_sas/hisi_sas_v1_hw.c | 23 +
 drivers/scsi/hisi_sas/hisi_sas_v2_hw.c | 30 -
 4 files changed, 64 insertions(+), 56 deletions(-)

diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h
index 4e28f32..88f06be 100644
--- a/drivers/scsi/hisi_sas/hisi_sas.h
+++ b/drivers/scsi/hisi_sas/hisi_sas.h
@@ -102,8 +102,10 @@ struct hisi_sas_cq {
 
 struct hisi_sas_dq {
struct hisi_hba *hisi_hba;
+   spinlock_t lock;
int wr_point;
int id;
+   struct hisi_sas_slot*slot_prep;
 };
 
 struct hisi_sas_device {
@@ -154,9 +156,8 @@ struct hisi_sas_hw {
struct domain_device *device);
struct hisi_sas_device *(*alloc_dev)(struct domain_device *device);
void (*sl_notify)(struct hisi_hba *hisi_hba, int phy_no);
-   int (*get_free_slot)(struct hisi_hba *hisi_hba, u32 dev_id,
-   int *q, int *s);
-   void (*start_delivery)(struct hisi_hba *hisi_hba);
+   int (*get_free_slot)(struct hisi_hba *hisi_hba, struct hisi_sas_dq *dq);
+   void (*start_delivery)(struct hisi_sas_dq *dq);
int (*prep_ssp)(struct hisi_hba *hisi_hba,
struct hisi_sas_slot *slot, int is_tmf,
struct hisi_sas_tmf_task *tmf);
@@ -217,7 +218,6 @@ struct hisi_hba {
struct hisi_sas_port port[HISI_SAS_MAX_PHYS];
 
int queue_count;
-   struct hisi_sas_slot*slot_prep;
 
struct dma_pool *sge_page_pool;
struct hisi_sas_device  devices[HISI_SAS_MAX_DEVICES];
diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c 
b/drivers/scsi/hisi_sas/hisi_sas_main.c
index 3605d28..d1b7dbf 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_main.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_main.c
@@ -179,10 +179,11 @@ static void hisi_sas_slot_abort(struct work_struct *work)
task->task_done(task);
 }
 
-static int hisi_sas_task_prep(struct sas_task *task, struct hisi_hba *hisi_hba,
- int is_tmf, struct hisi_sas_tmf_task *tmf,
- int *pass)
+static int hisi_sas_task_prep(struct sas_task *task, struct hisi_sas_dq
+   *dq, int is_tmf, struct hisi_sas_tmf_task *tmf,
+   int *pass)
 {
+   struct hisi_hba *hisi_hba = dq->hisi_hba;
struct domain_device *device = task->dev;
struct hisi_sas_device *sas_dev = device->lldd_dev;
struct hisi_sas_port *port;
@@ -240,18 +241,24 @@ static int hisi_sas_task_prep(struct sas_task *task, 
struct hisi_hba *hisi_hba,
} else
n_elem = task->num_scatter;
 
+   spin_lock_irqsave(_hba->lock, flags);
if (hisi_hba->hw->slot_index_alloc)
rc = hisi_hba->hw->slot_index_alloc(hisi_hba, _idx,
device);
else
rc = hisi_sas_slot_index_alloc(hisi_hba, _idx);
-   if (rc)
+   if (rc) {
+   spin_unlock_irqrestore(_hba->lock, flags);
goto err_out;
-   rc = hisi_hba->hw->get_free_slot(hisi_hba, sas_dev->device_id,
-   _queue, _queue_slot);
+   }
+   spin_unlock_irqrestore(_hba->lock, flags);
+
+   rc = hisi_hba->hw->get_free_slot(hisi_hba, dq);
if (rc)
goto err_out_tag;
 
+   dlvry_queue = dq->id;
+   dlvry_queue_slot = dq->wr_point;
slot = _hba->slot_info[slot_idx];
memset(slot, 0, sizeof(struct hisi_sas_slot));
 
@@ -316,7 +323,7 @@ static int hisi_sas_task_prep(struct sas_task *task, struct 
hisi_hba *hisi_hba,
task->task_state_flags |= SAS_TASK_AT_INITIATOR;
spin_unlock_irqrestore(>task_state_lock, flags);
 
-   hisi_hba->slot_prep = slot;
+   dq->slot_prep = slot;
 
atomic64_inc(_dev->running_req);
++(*pass);
@@ -354,19 +361,23 @@ static int hisi_sas_task_exec(struct sas_task *task, 
gfp_t gfp_flags,
unsigned long flags;
struct hisi_hba *hisi_hba = dev_to_hisi_hba(task->dev);
struct device *dev = _hba->pdev->dev;
+   struct domain_device *device = task->dev;
+   struct hisi_sas_device *sas_dev = device->lldd_dev;
+   int queue_id = sas_dev->device_id % hisi_hba->queue_count;
+   struct hisi_sas_dq *dq = _hba->dq[queue_id];
 

[PATCH v2 12/22] scsi: hisi_sas: add phy up/down/bcast and channel ISR

2017-05-25 Thread John Garry
From: Xiang Chen 

Add code to initialise interrupts and add some interrupt handlers.

Signed-off-by: John Garry 
Signed-off-by: Xiang Chen 
---
 drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 282 +
 1 file changed, 282 insertions(+)

diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c 
b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
index e3e3734..e9fdedd 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
@@ -173,8 +173,16 @@ enum {
HISI_SAS_PHY_INT_NR
 };
 
+
 static const struct hisi_sas_hw hisi_sas_v3_hw;
 
+static u32 hisi_sas_read32(struct hisi_hba *hisi_hba, u32 off)
+{
+   void __iomem *regs = hisi_hba->regs + off;
+
+   return readl(regs);
+}
+
 static void hisi_sas_write32(struct hisi_hba *hisi_hba, u32 off, u32 val)
 {
void __iomem *regs = hisi_hba->regs + off;
@@ -399,6 +407,276 @@ static void sl_notify_v3_hw(struct hisi_hba *hisi_hba, 
int phy_no)
hisi_sas_phy_write32(hisi_hba, phy_no, SL_CONTROL, sl_control);
 }
 
+static int phy_up_v3_hw(int phy_no, struct hisi_hba *hisi_hba)
+{
+   int i, res = 0;
+   u32 context, port_id, link_rate, hard_phy_linkrate;
+   struct hisi_sas_phy *phy = _hba->phy[phy_no];
+   struct asd_sas_phy *sas_phy = >sas_phy;
+   struct device *dev = hisi_hba->dev;
+
+   hisi_sas_phy_write32(hisi_hba, phy_no, PHYCTRL_PHY_ENA_MSK, 1);
+
+   port_id = hisi_sas_read32(hisi_hba, PHY_PORT_NUM_MA);
+   port_id = (port_id >> (4 * phy_no)) & 0xf;
+   link_rate = hisi_sas_read32(hisi_hba, PHY_CONN_RATE);
+   link_rate = (link_rate >> (phy_no * 4)) & 0xf;
+
+   if (port_id == 0xf) {
+   dev_err(dev, "phyup: phy%d invalid portid\n", phy_no);
+   res = IRQ_NONE;
+   goto end;
+   }
+   sas_phy->linkrate = link_rate;
+   hard_phy_linkrate = hisi_sas_phy_read32(hisi_hba, phy_no,
+   HARD_PHY_LINKRATE);
+   phy->maximum_linkrate = hard_phy_linkrate & 0xf;
+   phy->minimum_linkrate = (hard_phy_linkrate >> 4) & 0xf;
+   phy->phy_type &= ~(PORT_TYPE_SAS | PORT_TYPE_SATA);
+
+   /* Check for SATA dev */
+   context = hisi_sas_read32(hisi_hba, PHY_CONTEXT);
+   if (context & (1 << phy_no)) {
+   struct hisi_sas_initial_fis *initial_fis;
+   struct dev_to_host_fis *fis;
+   u8 attached_sas_addr[SAS_ADDR_SIZE] = {0};
+
+   dev_info(dev, "phyup: phy%d link_rate=%d\n", phy_no, link_rate);
+   initial_fis = _hba->initial_fis[phy_no];
+   fis = _fis->fis;
+   sas_phy->oob_mode = SATA_OOB_MODE;
+   attached_sas_addr[0] = 0x50;
+   attached_sas_addr[7] = phy_no;
+   memcpy(sas_phy->attached_sas_addr,
+  attached_sas_addr,
+  SAS_ADDR_SIZE);
+   memcpy(sas_phy->frame_rcvd, fis,
+  sizeof(struct dev_to_host_fis));
+   phy->phy_type |= PORT_TYPE_SATA;
+   phy->identify.device_type = SAS_SATA_DEV;
+   phy->frame_rcvd_size = sizeof(struct dev_to_host_fis);
+   phy->identify.target_port_protocols = SAS_PROTOCOL_SATA;
+   } else {
+   u32 *frame_rcvd = (u32 *)sas_phy->frame_rcvd;
+   struct sas_identify_frame *id =
+   (struct sas_identify_frame *)frame_rcvd;
+
+   dev_info(dev, "phyup: phy%d link_rate=%d\n", phy_no, link_rate);
+   for (i = 0; i < 6; i++) {
+   u32 idaf = hisi_sas_phy_read32(hisi_hba, phy_no,
+  RX_IDAF_DWORD0 + (i * 4));
+   frame_rcvd[i] = __swab32(idaf);
+   }
+   sas_phy->oob_mode = SAS_OOB_MODE;
+   memcpy(sas_phy->attached_sas_addr,
+  >sas_addr,
+  SAS_ADDR_SIZE);
+   phy->phy_type |= PORT_TYPE_SAS;
+   phy->identify.device_type = id->dev_type;
+   phy->frame_rcvd_size = sizeof(struct sas_identify_frame);
+   if (phy->identify.device_type == SAS_END_DEVICE)
+   phy->identify.target_port_protocols =
+   SAS_PROTOCOL_SSP;
+   else if (phy->identify.device_type != SAS_PHY_UNUSED)
+   phy->identify.target_port_protocols =
+   SAS_PROTOCOL_SMP;
+   }
+
+   phy->port_id = port_id;
+   phy->phy_attached = 1;
+   queue_work(hisi_hba->wq, >phyup_ws);
+
+end:
+   hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT0,
+CHL_INT0_SL_PHY_ENABLE_MSK);
+   hisi_sas_phy_write32(hisi_hba, phy_no, PHYCTRL_PHY_ENA_MSK, 0);
+
+   return res;
+}
+
+static int phy_down_v3_hw(int phy_no, struct hisi_hba 

[PATCH v2 10/22] scsi: hisi_sas: add v3 hw init

2017-05-25 Thread John Garry
From: Xiang Chen 

Add code to initialise v3 hardware.

Signed-off-by: John Garry 
Signed-off-by: Xiang Chen 
---
 drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 276 +
 1 file changed, 276 insertions(+)

diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c 
b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
index 05976db..316938a 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
@@ -11,8 +11,281 @@
 #include "hisi_sas.h"
 #define DRV_NAME "hisi_sas_v3_hw"
 
+/* global registers need init*/
+#define DLVRY_QUEUE_ENABLE 0x0
+#define IOST_BASE_ADDR_LO  0x8
+#define IOST_BASE_ADDR_HI  0xc
+#define ITCT_BASE_ADDR_LO  0x10
+#define ITCT_BASE_ADDR_HI  0x14
+#define IO_BROKEN_MSG_ADDR_LO  0x18
+#define IO_BROKEN_MSG_ADDR_HI  0x1c
+#define AXI_AHB_CLK_CFG0x3c
+#define AXI_USER1  0x48
+#define AXI_USER2  0x4c
+#define IO_SATA_BROKEN_MSG_ADDR_LO 0x58
+#define IO_SATA_BROKEN_MSG_ADDR_HI 0x5c
+#define SATA_INITI_D2H_STORE_ADDR_LO   0x60
+#define SATA_INITI_D2H_STORE_ADDR_HI   0x64
+#define CFG_MAX_TAG0x68
+#define HGC_SAS_TX_OPEN_FAIL_RETRY_CTRL0x84
+#define HGC_SAS_TXFAIL_RETRY_CTRL  0x88
+#define HGC_GET_ITV_TIME   0x90
+#define DEVICE_MSG_WORK_MODE   0x94
+#define OPENA_WT_CONTI_TIME0x9c
+#define I_T_NEXUS_LOSS_TIME0xa0
+#define MAX_CON_TIME_LIMIT_TIME0xa4
+#define BUS_INACTIVE_LIMIT_TIME0xa8
+#define REJECT_TO_OPEN_LIMIT_TIME  0xac
+#define CFG_AGING_TIME 0xbc
+#define HGC_DFX_CFG2   0xc0
+#define CFG_ABT_SET_QUERY_IPTT 0xd4
+#define CFG_SET_ABORTED_IPTT_OFF   0
+#define CFG_SET_ABORTED_IPTT_MSK   (0xfff << CFG_SET_ABORTED_IPTT_OFF)
+#define CFG_1US_TIMER_TRSH 0xcc
+#define INT_COAL_EN0x19c
+#define OQ_INT_COAL_TIME   0x1a0
+#define OQ_INT_COAL_CNT0x1a4
+#define ENT_INT_COAL_TIME  0x1a8
+#define ENT_INT_COAL_CNT   0x1ac
+#define OQ_INT_SRC 0x1b0
+#define OQ_INT_SRC_MSK 0x1b4
+#define ENT_INT_SRC1   0x1b8
+#define ENT_INT_SRC1_D2H_FIS_CH0_OFF   0
+#define ENT_INT_SRC1_D2H_FIS_CH0_MSK   (0x1 << ENT_INT_SRC1_D2H_FIS_CH0_OFF)
+#define ENT_INT_SRC1_D2H_FIS_CH1_OFF   8
+#define ENT_INT_SRC1_D2H_FIS_CH1_MSK   (0x1 << ENT_INT_SRC1_D2H_FIS_CH1_OFF)
+#define ENT_INT_SRC2   0x1bc
+#define ENT_INT_SRC3   0x1c0
+#define ENT_INT_SRC3_WP_DEPTH_OFF  8
+#define ENT_INT_SRC3_IPTT_SLOT_NOMATCH_OFF 9
+#define ENT_INT_SRC3_RP_DEPTH_OFF  10
+#define ENT_INT_SRC3_AXI_OFF   11
+#define ENT_INT_SRC3_FIFO_OFF  12
+#define ENT_INT_SRC3_LM_OFF14
+#define ENT_INT_SRC3_ITC_INT_OFF   15
+#define ENT_INT_SRC3_ITC_INT_MSK   (0x1 << ENT_INT_SRC3_ITC_INT_OFF)
+#define ENT_INT_SRC3_ABT_OFF   16
+#define ENT_INT_SRC_MSK1   0x1c4
+#define ENT_INT_SRC_MSK2   0x1c8
+#define ENT_INT_SRC_MSK3   0x1cc
+#define CHNL_PHYUPDOWN_INT_MSK 0x1d0
+#define CHNL_ENT_INT_MSK   0x1d4
+#define HGC_COM_INT_MSK0x1d8
+#define SAS_ECC_INTR   0x1e8
+#define SAS_ECC_INTR_MSK   0x1ec
+#define HGC_ERR_STAT_EN0x238
+#define DLVRY_Q_0_BASE_ADDR_LO 0x260
+#define DLVRY_Q_0_BASE_ADDR_HI 0x264
+#define DLVRY_Q_0_DEPTH0x268
+#define DLVRY_Q_0_WR_PTR   0x26c
+#define DLVRY_Q_0_RD_PTR   0x270
+#define HYPER_STREAM_ID_EN_CFG 0xc80
+#define OQ0_INT_SRC_MSK0xc90
+#define COMPL_Q_0_BASE_ADDR_LO 0x4e0
+#define COMPL_Q_0_BASE_ADDR_HI 0x4e4
+#define COMPL_Q_0_DEPTH0x4e8
+#define COMPL_Q_0_WR_PTR   0x4ec
+#define COMPL_Q_0_RD_PTR   0x4f0
+#define AWQOS_AWCACHE_CFG  0xc84
+#define ARQOS_ARCACHE_CFG  0xc88
+
+/* phy registers requiring init */
+#define PORT_BASE  (0x2000)
+#define PROG_PHY_LINK_RATE (PORT_BASE + 0x8)
+#define PHY_CTRL   (PORT_BASE + 0x14)
+#define PHY_CTRL_RESET_OFF 0
+#define PHY_CTRL_RESET_MSK (0x1 << PHY_CTRL_RESET_OFF)
+#define SL_CFG (PORT_BASE + 0x84)
+#define RXOP_CHECK_CFG_H   (PORT_BASE + 0xfc)
+#define SAS_SSP_CON_TIMER_CFG  (PORT_BASE + 0x134)
+#define SAS_SMP_CON_TIMER_CFG  (PORT_BASE + 0x138)
+#define SAS_STP_CON_TIMER_CFG  (PORT_BASE + 0x13c)
+#define CHL_INT0   (PORT_BASE + 0x1b4)
+#define 

[PATCH v2 07/22] scsi: hisi_sas: create hisi_sas_get_fw_info()

2017-05-25 Thread John Garry
Move the functionality to retrieve the fw info into
a dedicated device type-agnostic function,
hisi_sas_get_fw_info().

The reasoning is that this function will be required
for future pci-based platforms.

Also add some debug prints for failure.

Signed-off-by: John Garry 
---
 drivers/scsi/hisi_sas/hisi_sas.h  |   1 +
 drivers/scsi/hisi_sas/hisi_sas_main.c | 103 ++
 2 files changed, 67 insertions(+), 37 deletions(-)

diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h
index 18044b7..d029aba 100644
--- a/drivers/scsi/hisi_sas/hisi_sas.h
+++ b/drivers/scsi/hisi_sas/hisi_sas.h
@@ -364,6 +364,7 @@ struct hisi_sas_command_table_ssp {
 extern void hisi_sas_sata_done(struct sas_task *task,
struct hisi_sas_slot *slot);
 extern int hisi_sas_get_ncq_tag(struct sas_task *task, u32 *tag);
+extern int hisi_sas_get_fw_info(struct hisi_hba *hisi_hba);
 extern int hisi_sas_probe(struct platform_device *pdev,
  const struct hisi_sas_hw *ops);
 extern int hisi_sas_remove(struct platform_device *pdev);
diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c 
b/drivers/scsi/hisi_sas/hisi_sas_main.c
index d4809cf..58c6cf0 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_main.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_main.c
@@ -1729,66 +1729,95 @@ static void hisi_sas_rst_work_handler(struct 
work_struct *work)
hisi_sas_controller_reset(hisi_hba);
 }
 
-static struct Scsi_Host *hisi_sas_shost_alloc(struct platform_device *pdev,
- const struct hisi_sas_hw *hw)
+int hisi_sas_get_fw_info(struct hisi_hba *hisi_hba)
 {
-   struct resource *res;
-   struct Scsi_Host *shost;
-   struct hisi_hba *hisi_hba;
-   struct device *dev = >dev;
-   struct device_node *np = pdev->dev.of_node;
+   struct device *dev = hisi_hba->dev;
+   struct platform_device *pdev = hisi_hba->platform_dev;
+   struct device_node *np = pdev ? pdev->dev.of_node : NULL;
struct clk *refclk;
 
-   shost = scsi_host_alloc(_sas_sht, sizeof(*hisi_hba));
-   if (!shost) {
-   dev_err(dev, "scsi host alloc failed\n");
-   return NULL;
-   }
-   hisi_hba = shost_priv(shost);
-
-   INIT_WORK(_hba->rst_work, hisi_sas_rst_work_handler);
-   hisi_hba->hw = hw;
-   hisi_hba->platform_dev = pdev;
-   hisi_hba->dev = dev;
-   hisi_hba->shost = shost;
-   SHOST_TO_SAS_HA(shost) = _hba->sha;
-
-   init_timer(_hba->timer);
-
if (device_property_read_u8_array(dev, "sas-addr", hisi_hba->sas_addr,
- SAS_ADDR_SIZE))
-   goto err_out;
+ SAS_ADDR_SIZE)) {
+   dev_err(dev, "could not get property sas-addr\n");
+   return -ENOENT;
+   }
 
if (np) {
hisi_hba->ctrl = syscon_regmap_lookup_by_phandle(np,
"hisilicon,sas-syscon");
-   if (IS_ERR(hisi_hba->ctrl))
-   goto err_out;
+   if (IS_ERR(hisi_hba->ctrl)) {
+   dev_err(dev, "could not get syscon\n");
+   return -ENOENT;
+   }
 
if (device_property_read_u32(dev, "ctrl-reset-reg",
-_hba->ctrl_reset_reg))
-   goto err_out;
+_hba->ctrl_reset_reg)) {
+   dev_err(dev,
+   "could not get property ctrl-reset-reg\n");
+   return -ENOENT;
+   }
 
if (device_property_read_u32(dev, "ctrl-reset-sts-reg",
-_hba->ctrl_reset_sts_reg))
-   goto err_out;
+_hba->ctrl_reset_sts_reg)) {
+   dev_err(dev,
+   "could not get property ctrl-reset-sts-reg\n");
+   return -ENOENT;
+   }
 
if (device_property_read_u32(dev, "ctrl-clock-ena-reg",
-_hba->ctrl_clock_ena_reg))
-   goto err_out;
+_hba->ctrl_clock_ena_reg)) {
+   dev_err(dev,
+   "could not get property ctrl-clock-ena-reg\n");
+   return -ENOENT;
+   }
}
 
-   refclk = devm_clk_get(>dev, NULL);
+   refclk = devm_clk_get(dev, NULL);
if (IS_ERR(refclk))
dev_dbg(dev, "no ref clk property\n");
else
hisi_hba->refclk_frequency_mhz = clk_get_rate(refclk) / 100;
 
-   if (device_property_read_u32(dev, "phy-count", _hba->n_phy))
-   goto err_out;
+   if 

[PATCH v2 05/22] scsi: hisi_sas: relocate get_ncq_tag_v2_hw()

2017-05-25 Thread John Garry
From: Xiang Chen 

Relocate get_ncq_tag_v2_hw() to a common location, as
future hw versions will require it.
Also rename with "hisi_sas_" prefix for consistency.

Signed-off-by: Xiang Chen 
Signed-off-by: John Garry 
---
 drivers/scsi/hisi_sas/hisi_sas.h   |  1 +
 drivers/scsi/hisi_sas/hisi_sas_main.c  | 15 +++
 drivers/scsi/hisi_sas/hisi_sas_v2_hw.c | 16 +---
 3 files changed, 17 insertions(+), 15 deletions(-)

diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h
index d0c0844..210daec 100644
--- a/drivers/scsi/hisi_sas/hisi_sas.h
+++ b/drivers/scsi/hisi_sas/hisi_sas.h
@@ -359,6 +359,7 @@ struct hisi_sas_command_table_ssp {
 extern struct hisi_sas_port *to_hisi_sas_port(struct asd_sas_port *sas_port);
 extern void hisi_sas_sata_done(struct sas_task *task,
struct hisi_sas_slot *slot);
+extern int hisi_sas_get_ncq_tag(struct sas_task *task, u32 *tag);
 extern int hisi_sas_probe(struct platform_device *pdev,
  const struct hisi_sas_hw *ops);
 extern int hisi_sas_remove(struct platform_device *pdev);
diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c 
b/drivers/scsi/hisi_sas/hisi_sas_main.c
index 3f2bb8e..aaa7f5d 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_main.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_main.c
@@ -103,6 +103,21 @@ void hisi_sas_sata_done(struct sas_task *task,
 }
 EXPORT_SYMBOL_GPL(hisi_sas_sata_done);
 
+int hisi_sas_get_ncq_tag(struct sas_task *task, u32 *tag)
+{
+   struct ata_queued_cmd *qc = task->uldd_task;
+
+   if (qc) {
+   if (qc->tf.command == ATA_CMD_FPDMA_WRITE ||
+   qc->tf.command == ATA_CMD_FPDMA_READ) {
+   *tag = qc->tag;
+   return 1;
+   }
+   }
+   return 0;
+}
+EXPORT_SYMBOL_GPL(hisi_sas_get_ncq_tag);
+
 static struct hisi_hba *dev_to_hisi_hba(struct domain_device *device)
 {
return device->port->ha->lldd_ha;
diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c 
b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c
index 51140dc..e64047c 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c
@@ -2328,20 +2328,6 @@ static void slot_err_v2_hw(struct hisi_hba *hisi_hba,
return sts;
 }
 
-static int get_ncq_tag_v2_hw(struct sas_task *task, u32 *tag)
-{
-   struct ata_queued_cmd *qc = task->uldd_task;
-
-   if (qc) {
-   if (qc->tf.command == ATA_CMD_FPDMA_WRITE ||
-   qc->tf.command == ATA_CMD_FPDMA_READ) {
-   *tag = qc->tag;
-   return 1;
-   }
-   }
-   return 0;
-}
-
 static int prep_ata_v2_hw(struct hisi_hba *hisi_hba,
  struct hisi_sas_slot *slot)
 {
@@ -2389,7 +2375,7 @@ static int prep_ata_v2_hw(struct hisi_hba *hisi_hba,
hdr->dw1 = cpu_to_le32(dw1);
 
/* dw2 */
-   if (task->ata_task.use_ncq && get_ncq_tag_v2_hw(task, _tag)) {
+   if (task->ata_task.use_ncq && hisi_sas_get_ncq_tag(task, _tag)) {
task->ata_task.fis.sector_count |= (u8) (hdr_tag << 3);
dw2 |= hdr_tag << CMD_HDR_NCQ_TAG_OFF;
}
-- 
1.9.1



[PATCH v2 15/22] scsi: hisi_sas: add v3 code to send SMP frame

2017-05-25 Thread John Garry
From: Xiang Chen 

Add code to prepare SMP frame.

Signed-off-by: John Garry 
Signed-off-by: Xiang Chen 
---
 drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 74 ++
 1 file changed, 74 insertions(+)

diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c 
b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
index 7dd4804..5de4738 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
@@ -186,6 +186,9 @@
 #define CMD_HDR_MRFL_MSK   (0x1ff << CMD_HDR_MRFL_OFF)
 #define CMD_HDR_SG_MOD_OFF 24
 #define CMD_HDR_SG_MOD_MSK (0x3 << CMD_HDR_SG_MOD_OFF)
+/* dw3 */
+#define CMD_HDR_IPTT_OFF   0
+#define CMD_HDR_IPTT_MSK   (0x << CMD_HDR_IPTT_OFF)
 /* dw6 */
 #define CMD_HDR_DIF_SGL_LEN_OFF0
 #define CMD_HDR_DIF_SGL_LEN_MSK(0x << 
CMD_HDR_DIF_SGL_LEN_OFF)
@@ -638,6 +641,76 @@ static int prep_ssp_v3_hw(struct hisi_hba *hisi_hba,
return 0;
 }
 
+static int prep_smp_v3_hw(struct hisi_hba *hisi_hba,
+ struct hisi_sas_slot *slot)
+{
+   struct sas_task *task = slot->task;
+   struct hisi_sas_cmd_hdr *hdr = slot->cmd_hdr;
+   struct domain_device *device = task->dev;
+   struct device *dev = hisi_hba->dev;
+   struct hisi_sas_port *port = slot->port;
+   struct scatterlist *sg_req, *sg_resp;
+   struct hisi_sas_device *sas_dev = device->lldd_dev;
+   dma_addr_t req_dma_addr;
+   unsigned int req_len, resp_len;
+   int elem, rc;
+
+   /*
+* DMA-map SMP request, response buffers
+*/
+   /* req */
+   sg_req = >smp_task.smp_req;
+   elem = dma_map_sg(dev, sg_req, 1, DMA_TO_DEVICE);
+   if (!elem)
+   return -ENOMEM;
+   req_len = sg_dma_len(sg_req);
+   req_dma_addr = sg_dma_address(sg_req);
+
+   /* resp */
+   sg_resp = >smp_task.smp_resp;
+   elem = dma_map_sg(dev, sg_resp, 1, DMA_FROM_DEVICE);
+   if (!elem) {
+   rc = -ENOMEM;
+   goto err_out_req;
+   }
+   resp_len = sg_dma_len(sg_resp);
+   if ((req_len & 0x3) || (resp_len & 0x3)) {
+   rc = -EINVAL;
+   goto err_out_resp;
+   }
+
+   /* create header */
+   /* dw0 */
+   hdr->dw0 = cpu_to_le32((port->id << CMD_HDR_PORT_OFF) |
+  (1 << CMD_HDR_PRIORITY_OFF) | /* high pri */
+  (2 << CMD_HDR_CMD_OFF)); /* smp */
+
+   /* map itct entry */
+   hdr->dw1 = cpu_to_le32((sas_dev->device_id << CMD_HDR_DEV_ID_OFF) |
+  (1 << CMD_HDR_FRAME_TYPE_OFF) |
+  (DIR_NO_DATA << CMD_HDR_DIR_OFF));
+
+   /* dw2 */
+   hdr->dw2 = cpu_to_le32req_len - 4) / 4) << CMD_HDR_CFL_OFF) |
+  (HISI_SAS_MAX_SMP_RESP_SZ / 4 <<
+  CMD_HDR_MRFL_OFF));
+
+   hdr->transfer_tags = cpu_to_le32(slot->idx << CMD_HDR_IPTT_OFF);
+
+   hdr->cmd_table_addr = cpu_to_le64(req_dma_addr);
+   hdr->sts_buffer_addr = cpu_to_le64(slot->status_buffer_dma);
+
+   return 0;
+
+err_out_resp:
+   dma_unmap_sg(dev, >task->smp_task.smp_resp, 1,
+DMA_FROM_DEVICE);
+err_out_req:
+   dma_unmap_sg(dev, >task->smp_task.smp_req, 1,
+DMA_TO_DEVICE);
+   return rc;
+}
+
 static int phy_up_v3_hw(int phy_no, struct hisi_hba *hisi_hba)
 {
int i, res = 0;
@@ -1394,6 +1467,7 @@ static void hisi_sas_v3_remove(struct pci_dev *pdev)
.sl_notify = sl_notify_v3_hw,
.complete_hdr_size = sizeof(struct hisi_sas_complete_v3_hdr),
.prep_ssp = prep_ssp_v3_hw,
+   .prep_smp = prep_smp_v3_hw,
.get_free_slot = get_free_slot_v3_hw,
.start_delivery = start_delivery_v3_hw,
.slot_complete = slot_complete_v3_hw,
-- 
1.9.1



[PATCH v2 01/22] scsi: hisi_sas: fix timeout check in hisi_sas_internal_task_abort()

2017-05-25 Thread John Garry
From: Xiang Chen 

We need to check for timeout before task status, or the task will be
mistook as completed internal abort command.
Also add protection for sas_task.task_state_flags in
hisi_sas_tmf_timedout().

Signed-off-by: Xiang Chen 
Signed-off-by: John Garry 
---
 drivers/scsi/hisi_sas/hisi_sas_main.c | 25 +
 1 file changed, 17 insertions(+), 8 deletions(-)

diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c 
b/drivers/scsi/hisi_sas/hisi_sas_main.c
index f720d3c..3605d28 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_main.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_main.c
@@ -691,8 +691,13 @@ static void hisi_sas_task_done(struct sas_task *task)
 static void hisi_sas_tmf_timedout(unsigned long data)
 {
struct sas_task *task = (struct sas_task *)data;
+   unsigned long flags;
+
+   spin_lock_irqsave(>task_state_lock, flags);
+   if (!(task->task_state_flags & SAS_TASK_STATE_DONE))
+   task->task_state_flags |= SAS_TASK_STATE_ABORTED;
+   spin_unlock_irqrestore(>task_state_lock, flags);
 
-   task->task_state_flags |= SAS_TASK_STATE_ABORTED;
complete(>slow_task->completion);
 }
 
@@ -1247,6 +1252,17 @@ static int hisi_sas_query_task(struct sas_task *task)
wait_for_completion(>slow_task->completion);
res = TMF_RESP_FUNC_FAILED;
 
+   /* Internal abort timed out */
+   if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) {
+   if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) {
+   struct hisi_sas_slot *slot = task->lldd_task;
+
+   if (slot)
+   slot->task = NULL;
+   dev_err(dev, "internal task abort: timeout.\n");
+   }
+   }
+
if (task->task_status.resp == SAS_TASK_COMPLETE &&
task->task_status.stat == TMF_RESP_FUNC_COMPLETE) {
res = TMF_RESP_FUNC_COMPLETE;
@@ -1259,13 +1275,6 @@ static int hisi_sas_query_task(struct sas_task *task)
goto exit;
}
 
-   /* Internal abort timed out */
-   if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) {
-   if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) {
-   dev_err(dev, "internal task abort: timeout.\n");
-   }
-   }
-
 exit:
dev_dbg(dev, "internal task abort: task to dev %016llx task=%p "
"resp: 0x%x sts 0x%x\n",
-- 
1.9.1



Re: [PATCH v9 06/15] mlx5: Replace PCI pool old API

2017-05-25 Thread Romain Perier
Hello,


Le 23/05/2017 à 17:26, David Miller a écrit :
> From: Romain Perier 
> Date: Tue, 23 May 2017 10:53:36 +0200
>
>> Hello,
>>
>>
>> Le 23/05/2017 à 09:27, Leon Romanovsky a écrit :
>>> On Mon, May 22, 2017 at 06:48:58PM +0200, Romain Perier wrote:
 The PCI pool API is deprecated. This commit replaces the PCI pool old
 API by the appropriate function with the DMA pool API.

 Signed-off-by: Romain Perier 
 Reviewed-by: Peter Senna Tschudin 
 Acked-by: Doug Ledford 
 Tested-by: Doug Ledford 
 ---
  drivers/net/ethernet/mellanox/mlx5/core/cmd.c | 11 ++-
  include/linux/mlx5/driver.h   |  2 +-
  2 files changed, 7 insertions(+), 6 deletions(-)

>>> Who is supposed to merge this patch series?
>>>
>>> Acked-by: Leon Romanovsky 
>> Each maintainer of the corresponding subsystem, can take a patch, I
>> guess. No ?
> It might be easier to accumulate acks and you submit them as a series,
> in my opinion.
That's what I did already, no ?

Romain


[PATCH v2 19/22] scsi: hisi_sas: add get_wideport_bitmap_v3_hw()

2017-05-25 Thread John Garry
From: Xiang Chen 

Add code for interface get_wide_port_bitmap_v3_hw().

Signed-off-by: John Garry 
Signed-off-by: Xiang Chen 
---
 drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 13 +
 1 file changed, 13 insertions(+)

diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c 
b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
index 6273368..4169e6a 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
@@ -616,6 +616,18 @@ static void sl_notify_v3_hw(struct hisi_hba *hisi_hba, int 
phy_no)
hisi_sas_phy_write32(hisi_hba, phy_no, SL_CONTROL, sl_control);
 }
 
+static int get_wideport_bitmap_v3_hw(struct hisi_hba *hisi_hba, int port_id)
+{
+   int i, bitmap = 0;
+   u32 phy_port_num_ma = hisi_sas_read32(hisi_hba, PHY_PORT_NUM_MA);
+
+   for (i = 0; i < hisi_hba->n_phy; i++)
+   if (((phy_port_num_ma >> (i * 4)) & 0xf) == port_id)
+   bitmap |= 1 << i;
+
+   return bitmap;
+}
+
 /**
  * The callpath to this function and upto writing the write
  * queue pointer should be safe from interruption.
@@ -1721,6 +1733,7 @@ static void hisi_sas_v3_remove(struct pci_dev *pdev)
.setup_itct = setup_itct_v3_hw,
.max_command_entries = HISI_SAS_COMMAND_ENTRIES_V3_HW,
.sl_notify = sl_notify_v3_hw,
+   .get_wideport_bitmap = get_wideport_bitmap_v3_hw,
.complete_hdr_size = sizeof(struct hisi_sas_complete_v3_hdr),
.free_device = free_device_v3_hw,
.prep_ssp = prep_ssp_v3_hw,
-- 
1.9.1



[PATCH v2 22/22] scsi: hisi_sas: modify internal abort dev flow for v3 hw

2017-05-25 Thread John Garry
From: Xiang Chen 

There is a change for abort dev for v3 hw: add registers to configure
unaborted iptt for a device, and then inform this to logic.

Signed-off-by: Xiang Chen 
Signed-off-by: John Garry 
---
 drivers/scsi/hisi_sas/hisi_sas.h   |  2 ++
 drivers/scsi/hisi_sas/hisi_sas_main.c  | 15 +++
 drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 28 
 3 files changed, 45 insertions(+)

diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h
index 2315102..c465aab 100644
--- a/drivers/scsi/hisi_sas/hisi_sas.h
+++ b/drivers/scsi/hisi_sas/hisi_sas.h
@@ -181,6 +181,8 @@ struct hisi_sas_hw {
void (*free_device)(struct hisi_hba *hisi_hba,
struct hisi_sas_device *dev);
int (*get_wideport_bitmap)(struct hisi_hba *hisi_hba, int port_id);
+   void (*dereg_device)(struct hisi_hba *hisi_hba,
+   struct domain_device *device);
int (*soft_reset)(struct hisi_hba *hisi_hba);
int max_command_entries;
int complete_hdr_size;
diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c 
b/drivers/scsi/hisi_sas/hisi_sas_main.c
index a6d0bf8..1803374 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_main.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_main.c
@@ -760,6 +760,13 @@ static void hisi_sas_release_tasks(struct hisi_hba 
*hisi_hba)
}
 }
 
+static void hisi_sas_dereg_device(struct hisi_hba *hisi_hba,
+   struct domain_device *device)
+{
+   if (hisi_hba->hw->dereg_device)
+   hisi_hba->hw->dereg_device(hisi_hba, device);
+}
+
 static void hisi_sas_dev_gone(struct domain_device *device)
 {
struct hisi_sas_device *sas_dev = device->lldd_dev;
@@ -773,6 +780,8 @@ static void hisi_sas_dev_gone(struct domain_device *device)
hisi_sas_internal_task_abort(hisi_hba, device,
 HISI_SAS_INT_ABT_DEV, 0);
 
+   hisi_sas_dereg_device(hisi_hba, device);
+
hisi_hba->hw->free_device(hisi_hba, sas_dev);
device->lldd_dev = NULL;
memset(sas_dev, 0, sizeof(*sas_dev));
@@ -1104,6 +1113,7 @@ static int hisi_sas_abort_task(struct sas_task *task)
if (task->dev->dev_type == SAS_SATA_DEV) {
hisi_sas_internal_task_abort(hisi_hba, device,
 HISI_SAS_INT_ABT_DEV, 0);
+   hisi_sas_dereg_device(hisi_hba, device);
rc = hisi_sas_softreset_ata_disk(device);
}
} else if (task->lldd_task && task->task_proto & SAS_PROTOCOL_SMP) {
@@ -1170,6 +1180,10 @@ static int hisi_sas_I_T_nexus_reset(struct domain_device 
*device)
return TMF_RESP_FUNC_FAILED;
sas_dev->dev_status = HISI_SAS_DEV_NORMAL;
 
+   hisi_sas_internal_task_abort(hisi_hba, device,
+   HISI_SAS_INT_ABT_DEV, 0);
+   hisi_sas_dereg_device(hisi_hba, device);
+
rc = hisi_sas_debug_I_T_nexus_reset(device);
 
if (rc == TMF_RESP_FUNC_COMPLETE) {
@@ -1197,6 +1211,7 @@ static int hisi_sas_lu_reset(struct domain_device 
*device, u8 *lun)
  HISI_SAS_INT_ABT_DEV, 0);
if (rc == TMF_RESP_FUNC_FAILED)
goto out;
+   hisi_sas_dereg_device(hisi_hba, device);
 
phy = sas_get_local_phy(device);
 
diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c 
b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
index 4a6f5c7..c1db7bc 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
@@ -50,6 +50,10 @@
 #define CFG_ABT_SET_QUERY_IPTT 0xd4
 #define CFG_SET_ABORTED_IPTT_OFF   0
 #define CFG_SET_ABORTED_IPTT_MSK   (0xfff << CFG_SET_ABORTED_IPTT_OFF)
+#define CFG_SET_ABORTED_EN_OFF 12
+#define CFG_ABT_SET_IPTT_DONE  0xd8
+#define CFG_ABT_SET_IPTT_DONE_OFF  0
+#define HGC_IOMB_PROC1_STATUS  0x104
 #define CFG_1US_TIMER_TRSH 0xcc
 #define HGC_LM_DFX_STATUS2 0x128
 #define HGC_LM_DFX_STATUS2_IOSTLIST_OFF0
@@ -637,6 +641,29 @@ static void free_device_v3_hw(struct hisi_hba *hisi_hba,
}
 }
 
+static void dereg_device_v3_hw(struct hisi_hba *hisi_hba,
+   struct domain_device *device)
+{
+   struct hisi_sas_slot *slot, *slot2;
+   struct hisi_sas_device *sas_dev = device->lldd_dev;
+   u32 cfg_abt_set_query_iptt;
+
+   cfg_abt_set_query_iptt = hisi_sas_read32(hisi_hba,
+   CFG_ABT_SET_QUERY_IPTT);
+   list_for_each_entry_safe(slot, slot2, _dev->list, entry) {
+   cfg_abt_set_query_iptt &= ~CFG_SET_ABORTED_IPTT_MSK;
+   cfg_abt_set_query_iptt |= (1 << CFG_SET_ABORTED_EN_OFF) |
+   (slot->idx << CFG_SET_ABORTED_IPTT_OFF);
+   

[PATCH v2 04/22] scsi: hisi_sas: relocate sata_done_v2_hw()

2017-05-25 Thread John Garry
From: Xiang Chen 

Relocate get_ata_protocol() to a common location, as future hw
versions will require it.
Also rename with "hisi_sas_" prefix for consistency.

Signed-off-by: Xiang Chen 
Signed-off-by: John Garry 
---
 drivers/scsi/hisi_sas/hisi_sas.h   |  2 ++
 drivers/scsi/hisi_sas/hisi_sas_main.c  | 15 +++
 drivers/scsi/hisi_sas/hisi_sas_v2_hw.c | 18 ++
 3 files changed, 19 insertions(+), 16 deletions(-)

diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h
index 02ec10b..d0c0844 100644
--- a/drivers/scsi/hisi_sas/hisi_sas.h
+++ b/drivers/scsi/hisi_sas/hisi_sas.h
@@ -357,6 +357,8 @@ struct hisi_sas_command_table_ssp {
 
 extern u8 hisi_sas_get_ata_protocol(u8 cmd, int direction);
 extern struct hisi_sas_port *to_hisi_sas_port(struct asd_sas_port *sas_port);
+extern void hisi_sas_sata_done(struct sas_task *task,
+   struct hisi_sas_slot *slot);
 extern int hisi_sas_probe(struct platform_device *pdev,
  const struct hisi_sas_hw *ops);
 extern int hisi_sas_remove(struct platform_device *pdev);
diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c 
b/drivers/scsi/hisi_sas/hisi_sas_main.c
index 8fce20b..3f2bb8e 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_main.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_main.c
@@ -88,6 +88,21 @@ u8 hisi_sas_get_ata_protocol(u8 cmd, int direction)
 }
 EXPORT_SYMBOL_GPL(hisi_sas_get_ata_protocol);
 
+void hisi_sas_sata_done(struct sas_task *task,
+   struct hisi_sas_slot *slot)
+{
+   struct task_status_struct *ts = >task_status;
+   struct ata_task_resp *resp = (struct ata_task_resp *)ts->buf;
+   struct dev_to_host_fis *d2h = slot->status_buffer +
+ sizeof(struct hisi_sas_err_record);
+
+   resp->frame_len = sizeof(struct dev_to_host_fis);
+   memcpy(>ending_fis[0], d2h, sizeof(struct dev_to_host_fis));
+
+   ts->buf_valid_size = sizeof(*resp);
+}
+EXPORT_SYMBOL_GPL(hisi_sas_sata_done);
+
 static struct hisi_hba *dev_to_hisi_hba(struct domain_device *device)
 {
return device->port->ha->lldd_ha;
diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c 
b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c
index 397f01e..51140dc 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c
@@ -1679,20 +1679,6 @@ static int prep_ssp_v2_hw(struct hisi_hba *hisi_hba,
return 0;
 }
 
-static void sata_done_v2_hw(struct hisi_hba *hisi_hba, struct sas_task *task,
-   struct hisi_sas_slot *slot)
-{
-   struct task_status_struct *ts = >task_status;
-   struct ata_task_resp *resp = (struct ata_task_resp *)ts->buf;
-   struct dev_to_host_fis *d2h = slot->status_buffer +
- sizeof(struct hisi_sas_err_record);
-
-   resp->frame_len = sizeof(struct dev_to_host_fis);
-   memcpy(>ending_fis[0], d2h, sizeof(struct dev_to_host_fis));
-
-   ts->buf_valid_size = sizeof(*resp);
-}
-
 #define TRANS_TX_ERR   0
 #define TRANS_RX_ERR   1
 #define DMA_TX_ERR 2
@@ -2185,7 +2171,7 @@ static void slot_err_v2_hw(struct hisi_hba *hisi_hba,
break;
}
}
-   sata_done_v2_hw(hisi_hba, task, slot);
+   hisi_sas_sata_done(task, slot);
}
break;
default:
@@ -2313,7 +2299,7 @@ static void slot_err_v2_hw(struct hisi_hba *hisi_hba,
case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP:
{
ts->stat = SAM_STAT_GOOD;
-   sata_done_v2_hw(hisi_hba, task, slot);
+   hisi_sas_sata_done(task, slot);
break;
}
default:
-- 
1.9.1



[PATCH v2 17/22] scsi: hisi_sas: add v3 code for itct setup and free

2017-05-25 Thread John Garry
From: Xiang Chen 

Add code to itct setup and free for v3 hw.

Signed-off-by: John Garry 
Signed-off-by: Xiang Chen 
---
 drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 115 +
 1 file changed, 115 insertions(+)

diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c 
b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
index e682ad7..c8b6997 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
@@ -24,6 +24,11 @@
 #define PHY_PORT_NUM_MA0x28
 #define PHY_CONN_RATE  0x30
 #define AXI_AHB_CLK_CFG0x3c
+#define ITCT_CLR   0x44
+#define ITCT_CLR_EN_OFF16
+#define ITCT_CLR_EN_MSK(0x1 << ITCT_CLR_EN_OFF)
+#define ITCT_DEV_OFF   0
+#define ITCT_DEV_MSK   (0x7ff << ITCT_DEV_OFF)
 #define AXI_USER1  0x48
 #define AXI_USER2  0x4c
 #define IO_SATA_BROKEN_MSG_ADDR_LO 0x58
@@ -226,6 +231,27 @@
 #define CMPLT_HDR_IO_IN_TARGET_OFF 17
 #define CMPLT_HDR_IO_IN_TARGET_MSK (0x1 << CMPLT_HDR_IO_IN_TARGET_OFF)
 
+/* ITCT header */
+/* qw0 */
+#define ITCT_HDR_DEV_TYPE_OFF  0
+#define ITCT_HDR_DEV_TYPE_MSK  (0x3 << ITCT_HDR_DEV_TYPE_OFF)
+#define ITCT_HDR_VALID_OFF 2
+#define ITCT_HDR_VALID_MSK (0x1 << ITCT_HDR_VALID_OFF)
+#define ITCT_HDR_MCR_OFF   5
+#define ITCT_HDR_MCR_MSK   (0xf << ITCT_HDR_MCR_OFF)
+#define ITCT_HDR_VLN_OFF   9
+#define ITCT_HDR_VLN_MSK   (0xf << ITCT_HDR_VLN_OFF)
+#define ITCT_HDR_SMP_TIMEOUT_OFF   16
+#define ITCT_HDR_AWT_CONTINUE_OFF  25
+#define ITCT_HDR_PORT_ID_OFF   28
+#define ITCT_HDR_PORT_ID_MSK   (0xf << ITCT_HDR_PORT_ID_OFF)
+/* qw2 */
+#define ITCT_HDR_INLT_OFF  0
+#define ITCT_HDR_INLT_MSK  (0xULL << ITCT_HDR_INLT_OFF)
+#define ITCT_HDR_RTOLT_OFF 48
+#define ITCT_HDR_RTOLT_MSK (0xULL << ITCT_HDR_RTOLT_OFF)
+
+
 struct hisi_sas_complete_v3_hdr {
__le32 dw0;
__le32 dw1;
@@ -446,6 +472,93 @@ static void config_id_frame_v3_hw(struct hisi_hba 
*hisi_hba, int phy_no)
__swab32(identify_buffer[5]));
 }
 
+static void setup_itct_v3_hw(struct hisi_hba *hisi_hba,
+struct hisi_sas_device *sas_dev)
+{
+   struct domain_device *device = sas_dev->sas_device;
+   struct device *dev = hisi_hba->dev;
+   u64 qw0, device_id = sas_dev->device_id;
+   struct hisi_sas_itct *itct = _hba->itct[device_id];
+   struct domain_device *parent_dev = device->parent;
+   struct asd_sas_port *sas_port = device->port;
+   struct hisi_sas_port *port = to_hisi_sas_port(sas_port);
+
+   memset(itct, 0, sizeof(*itct));
+
+   /* qw0 */
+   qw0 = 0;
+   switch (sas_dev->dev_type) {
+   case SAS_END_DEVICE:
+   case SAS_EDGE_EXPANDER_DEVICE:
+   case SAS_FANOUT_EXPANDER_DEVICE:
+   qw0 = HISI_SAS_DEV_TYPE_SSP << ITCT_HDR_DEV_TYPE_OFF;
+   break;
+   case SAS_SATA_DEV:
+   case SAS_SATA_PENDING:
+   if (parent_dev && DEV_IS_EXPANDER(parent_dev->dev_type))
+   qw0 = HISI_SAS_DEV_TYPE_STP << ITCT_HDR_DEV_TYPE_OFF;
+   else
+   qw0 = HISI_SAS_DEV_TYPE_SATA << ITCT_HDR_DEV_TYPE_OFF;
+   break;
+   default:
+   dev_warn(dev, "setup itct: unsupported dev type (%d)\n",
+sas_dev->dev_type);
+   }
+
+   qw0 |= ((1 << ITCT_HDR_VALID_OFF) |
+   (device->linkrate << ITCT_HDR_MCR_OFF) |
+   (1 << ITCT_HDR_VLN_OFF) |
+   (0xfa << ITCT_HDR_SMP_TIMEOUT_OFF) |
+   (1 << ITCT_HDR_AWT_CONTINUE_OFF) |
+   (port->id << ITCT_HDR_PORT_ID_OFF));
+   itct->qw0 = cpu_to_le64(qw0);
+
+   /* qw1 */
+   memcpy(>sas_addr, device->sas_addr, SAS_ADDR_SIZE);
+   itct->sas_addr = __swab64(itct->sas_addr);
+
+   /* qw2 */
+   if (!dev_is_sata(device))
+   itct->qw2 = cpu_to_le64((5000ULL << ITCT_HDR_INLT_OFF) |
+   (0x1ULL << ITCT_HDR_RTOLT_OFF));
+}
+
+static void free_device_v3_hw(struct hisi_hba *hisi_hba,
+ struct hisi_sas_device *sas_dev)
+{
+   u64 dev_id = sas_dev->device_id;
+   struct device *dev = hisi_hba->dev;
+   struct hisi_sas_itct *itct = _hba->itct[dev_id];
+   u32 reg_val = hisi_sas_read32(hisi_hba, ENT_INT_SRC3);
+
+   /* clear the itct interrupt state */
+   if (ENT_INT_SRC3_ITC_INT_MSK & reg_val)
+   hisi_sas_write32(hisi_hba, ENT_INT_SRC3,
+ENT_INT_SRC3_ITC_INT_MSK);
+
+   /* clear the itct table*/
+   reg_val = 

[PATCH v2 21/22] scsi: hisi_sas: add v3 code to fill some more hw function pointers

2017-05-25 Thread John Garry
From: Xiang Chen 

Add code to fill the interface of phy_hard_reset, phy_get_max_linkrate,
and phy enable/disable.

Signed-off-by: John Garry 
Signed-off-by: Xiang Chen 
---
 drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 39 ++
 1 file changed, 39 insertions(+)

diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c 
b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
index 9add347..4a6f5c7 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
@@ -195,6 +195,8 @@
 #define TXID_AUTO  (PORT_BASE + 0xb8)
 #define CT3_OFF1
 #define CT3_MSK(0x1 << CT3_OFF)
+#define TX_HARDRST_OFF  2
+#define TX_HARDRST_MSK  (0x1 << TX_HARDRST_OFF)
 #define RX_IDAF_DWORD0 (PORT_BASE + 0xc4)
 #define RXOP_CHECK_CFG_H   (PORT_BASE + 0xfc)
 #define SAS_SSP_CON_TIMER_CFG  (PORT_BASE + 0x134)
@@ -650,6 +652,14 @@ static void enable_phy_v3_hw(struct hisi_hba *hisi_hba, 
int phy_no)
hisi_sas_phy_write32(hisi_hba, phy_no, PHY_CFG, cfg);
 }
 
+static void disable_phy_v3_hw(struct hisi_hba *hisi_hba, int phy_no)
+{
+   u32 cfg = hisi_sas_phy_read32(hisi_hba, phy_no, PHY_CFG);
+
+   cfg &= ~PHY_CFG_ENA_MSK;
+   hisi_sas_phy_write32(hisi_hba, phy_no, PHY_CFG, cfg);
+}
+
 static void start_phy_v3_hw(struct hisi_hba *hisi_hba, int phy_no)
 {
config_id_frame_v3_hw(hisi_hba, phy_no);
@@ -657,6 +667,11 @@ static void start_phy_v3_hw(struct hisi_hba *hisi_hba, int 
phy_no)
enable_phy_v3_hw(hisi_hba, phy_no);
 }
 
+static void stop_phy_v3_hw(struct hisi_hba *hisi_hba, int phy_no)
+{
+   disable_phy_v3_hw(hisi_hba, phy_no);
+}
+
 static void start_phys_v3_hw(struct hisi_hba *hisi_hba)
 {
int i;
@@ -665,6 +680,26 @@ static void start_phys_v3_hw(struct hisi_hba *hisi_hba)
start_phy_v3_hw(hisi_hba, i);
 }
 
+static void phy_hard_reset_v3_hw(struct hisi_hba *hisi_hba, int phy_no)
+{
+   struct hisi_sas_phy *phy = _hba->phy[phy_no];
+   u32 txid_auto;
+
+   stop_phy_v3_hw(hisi_hba, phy_no);
+   if (phy->identify.device_type == SAS_END_DEVICE) {
+   txid_auto = hisi_sas_phy_read32(hisi_hba, phy_no, TXID_AUTO);
+   hisi_sas_phy_write32(hisi_hba, phy_no, TXID_AUTO,
+   txid_auto | TX_HARDRST_MSK);
+   }
+   msleep(100);
+   start_phy_v3_hw(hisi_hba, phy_no);
+}
+
+enum sas_linkrate phy_get_max_linkrate_v3_hw(void)
+{
+   return SAS_LINK_RATE_12_0_GBPS;
+}
+
 static void phys_init_v3_hw(struct hisi_hba *hisi_hba)
 {
start_phys_v3_hw(hisi_hba);
@@ -2141,6 +2176,10 @@ static void hisi_sas_v3_remove(struct pci_dev *pdev)
.start_delivery = start_delivery_v3_hw,
.slot_complete = slot_complete_v3_hw,
.phys_init = phys_init_v3_hw,
+   .phy_enable = enable_phy_v3_hw,
+   .phy_disable = disable_phy_v3_hw,
+   .phy_hard_reset = phy_hard_reset_v3_hw,
+   .phy_get_max_linkrate = phy_get_max_linkrate_v3_hw,
 };
 
 enum {
-- 
1.9.1



[PATCH v2 14/22] scsi: hisi_sas: add v3 code to send SSP frame

2017-05-25 Thread John Garry
From: Xiang Chen 

Add code to prepare SSP frame and deliver it to hardware.

Signed-off-by: John Garry 
Signed-off-by: Xiang Chen 
---
 drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 209 -
 1 file changed, 208 insertions(+), 1 deletion(-)

diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c 
b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
index f8ac239..7dd4804 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
@@ -157,6 +157,41 @@
 #define SL_RX_BCAST_CHK_MSK(PORT_BASE + 0x2c0)
 #define PHYCTRL_OOB_RESTART_MSK(PORT_BASE + 0x2c4)
 
+/* HW dma structures */
+/* Delivery queue header */
+/* dw0 */
+#define CMD_HDR_RESP_REPORT_OFF5
+#define CMD_HDR_RESP_REPORT_MSK(0x1 << CMD_HDR_RESP_REPORT_OFF)
+#define CMD_HDR_TLR_CTRL_OFF   6
+#define CMD_HDR_TLR_CTRL_MSK   (0x3 << CMD_HDR_TLR_CTRL_OFF)
+#define CMD_HDR_PORT_OFF   18
+#define CMD_HDR_PORT_MSK   (0xf << CMD_HDR_PORT_OFF)
+#define CMD_HDR_PRIORITY_OFF   27
+#define CMD_HDR_PRIORITY_MSK   (0x1 << CMD_HDR_PRIORITY_OFF)
+#define CMD_HDR_CMD_OFF29
+#define CMD_HDR_CMD_MSK(0x7 << CMD_HDR_CMD_OFF)
+/* dw1 */
+#define CMD_HDR_DIR_OFF5
+#define CMD_HDR_DIR_MSK(0x3 << CMD_HDR_DIR_OFF)
+#define CMD_HDR_VDTL_OFF   10
+#define CMD_HDR_VDTL_MSK   (0x1 << CMD_HDR_VDTL_OFF)
+#define CMD_HDR_FRAME_TYPE_OFF 11
+#define CMD_HDR_FRAME_TYPE_MSK (0x1f << CMD_HDR_FRAME_TYPE_OFF)
+#define CMD_HDR_DEV_ID_OFF 16
+#define CMD_HDR_DEV_ID_MSK (0x << CMD_HDR_DEV_ID_OFF)
+/* dw2 */
+#define CMD_HDR_CFL_OFF0
+#define CMD_HDR_CFL_MSK(0x1ff << CMD_HDR_CFL_OFF)
+#define CMD_HDR_MRFL_OFF   15
+#define CMD_HDR_MRFL_MSK   (0x1ff << CMD_HDR_MRFL_OFF)
+#define CMD_HDR_SG_MOD_OFF 24
+#define CMD_HDR_SG_MOD_MSK (0x3 << CMD_HDR_SG_MOD_OFF)
+/* dw6 */
+#define CMD_HDR_DIF_SGL_LEN_OFF0
+#define CMD_HDR_DIF_SGL_LEN_MSK(0x << 
CMD_HDR_DIF_SGL_LEN_OFF)
+#define CMD_HDR_DATA_SGL_LEN_OFF   16
+#define CMD_HDR_DATA_SGL_LEN_MSK   (0x << CMD_HDR_DATA_SGL_LEN_OFF)
+
 /* Completion header */
 /* dw0 */
 #define CMPLT_HDR_CMPLT_OFF0
@@ -201,9 +236,13 @@ enum {
HISI_SAS_PHY_INT_NR
 };
 
-
 static const struct hisi_sas_hw hisi_sas_v3_hw;
 
+#define DIR_NO_DATA 0
+#define DIR_TO_INI 1
+#define DIR_TO_DEVICE 2
+#define DIR_RESERVED 3
+
 static u32 hisi_sas_read32(struct hisi_hba *hisi_hba, u32 off)
 {
void __iomem *regs = hisi_hba->regs + off;
@@ -211,6 +250,13 @@ static u32 hisi_sas_read32(struct hisi_hba *hisi_hba, u32 
off)
return readl(regs);
 }
 
+static u32 hisi_sas_read32_relaxed(struct hisi_hba *hisi_hba, u32 off)
+{
+   void __iomem *regs = hisi_hba->regs + off;
+
+   return readl_relaxed(regs);
+}
+
 static void hisi_sas_write32(struct hisi_hba *hisi_hba, u32 off, u32 val)
 {
void __iomem *regs = hisi_hba->regs + off;
@@ -435,6 +481,163 @@ static void sl_notify_v3_hw(struct hisi_hba *hisi_hba, 
int phy_no)
hisi_sas_phy_write32(hisi_hba, phy_no, SL_CONTROL, sl_control);
 }
 
+/**
+ * The callpath to this function and upto writing the write
+ * queue pointer should be safe from interruption.
+ */
+static int
+get_free_slot_v3_hw(struct hisi_hba *hisi_hba, struct hisi_sas_dq *dq)
+{
+   struct device *dev = hisi_hba->dev;
+   int queue = dq->id;
+   u32 r, w;
+
+   w = dq->wr_point;
+   r = hisi_sas_read32_relaxed(hisi_hba,
+   DLVRY_Q_0_RD_PTR + (queue * 0x14));
+   if (r == (w+1) % HISI_SAS_QUEUE_SLOTS) {
+   dev_warn(dev, "full queue=%d r=%d w=%d\n\n",
+   queue, r, w);
+   return -EAGAIN;
+   }
+
+   return 0;
+}
+
+static void start_delivery_v3_hw(struct hisi_sas_dq *dq)
+{
+   struct hisi_hba *hisi_hba = dq->hisi_hba;
+   int dlvry_queue = dq->slot_prep->dlvry_queue;
+   int dlvry_queue_slot = dq->slot_prep->dlvry_queue_slot;
+
+   dq->wr_point = ++dlvry_queue_slot % HISI_SAS_QUEUE_SLOTS;
+   hisi_sas_write32(hisi_hba, DLVRY_Q_0_WR_PTR + (dlvry_queue * 0x14),
+dq->wr_point);
+}
+
+static int prep_prd_sge_v3_hw(struct hisi_hba *hisi_hba,
+ struct hisi_sas_slot *slot,
+ struct hisi_sas_cmd_hdr *hdr,
+ struct scatterlist *scatter,
+ int n_elem)
+{
+   struct device *dev = hisi_hba->dev;
+   struct scatterlist *sg;
+   int i;
+
+   if (n_elem > HISI_SAS_SGE_PAGE_CNT) {
+   dev_err(dev, "prd err: 

[PATCH v2 20/22] scsi: hisi_sas: Add v3 code to support ECC and AXI bus fatal error

2017-05-25 Thread John Garry
From: Xiang Chen 

For ECC 1bit error, logic can recover it, so we only print a warning.
For ECC multi-bit and AXI bus fatal error, we panic.

Note: once v3 hw controller reset support is added, the panic will
  be replaced by a controller reset, like v2 hw.

Signed-off-by: John Garry 
Signed-off-by: Xiang Chen 
---
 drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 401 -
 1 file changed, 399 insertions(+), 2 deletions(-)

diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c 
b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
index 4169e6a..9add347 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
@@ -51,7 +51,39 @@
 #define CFG_SET_ABORTED_IPTT_OFF   0
 #define CFG_SET_ABORTED_IPTT_MSK   (0xfff << CFG_SET_ABORTED_IPTT_OFF)
 #define CFG_1US_TIMER_TRSH 0xcc
+#define HGC_LM_DFX_STATUS2 0x128
+#define HGC_LM_DFX_STATUS2_IOSTLIST_OFF0
+#define HGC_LM_DFX_STATUS2_IOSTLIST_MSK(0xfff <<\
+   HGC_LM_DFX_STATUS2_IOSTLIST_OFF)
+#define HGC_LM_DFX_STATUS2_ITCTLIST_OFF12
+#define HGC_LM_DFX_STATUS2_ITCTLIST_MSK(0x7ff <<\
+   HGC_LM_DFX_STATUS2_ITCTLIST_OFF)
+#define HGC_CQE_ECC_ADDR   0x13c
+#define HGC_CQE_ECC_1B_ADDR_OFF0
+#define HGC_CQE_ECC_1B_ADDR_MSK(0x3f << 
HGC_CQE_ECC_1B_ADDR_OFF)
+#define HGC_CQE_ECC_MB_ADDR_OFF8
+#define HGC_CQE_ECC_MB_ADDR_MSK(0x3f << 
HGC_CQE_ECC_MB_ADDR_OFF)
+#define HGC_IOST_ECC_ADDR  0x140
+#define HGC_IOST_ECC_1B_ADDR_OFF   0
+#define HGC_IOST_ECC_1B_ADDR_MSK   (0x3ff << HGC_IOST_ECC_1B_ADDR_OFF)
+#define HGC_IOST_ECC_MB_ADDR_OFF   16
+#define HGC_IOST_ECC_MB_ADDR_MSK   (0x3ff << HGC_IOST_ECC_MB_ADDR_OFF)
+#define HGC_DQE_ECC_ADDR   0x144
+#define HGC_DQE_ECC_1B_ADDR_OFF0
+#define HGC_DQE_ECC_1B_ADDR_MSK(0xfff << 
HGC_DQE_ECC_1B_ADDR_OFF)
+#define HGC_DQE_ECC_MB_ADDR_OFF16
+#define HGC_DQE_ECC_MB_ADDR_MSK(0xfff << 
HGC_DQE_ECC_MB_ADDR_OFF)
 #define CHNL_INT_STATUS0x148
+#define HGC_ITCT_ECC_ADDR  0x150
+#define HGC_ITCT_ECC_1B_ADDR_OFF   0
+#define HGC_ITCT_ECC_1B_ADDR_MSK   (0x3ff << HGC_ITCT_ECC_1B_ADDR_OFF)
+#define HGC_ITCT_ECC_MB_ADDR_OFF   16
+#define HGC_ITCT_ECC_MB_ADDR_MSK   (0x3ff << HGC_ITCT_ECC_MB_ADDR_OFF)
+#define HGC_AXI_FIFO_ERR_INFO  0x154
+#define AXI_ERR_INFO_OFF   0
+#define AXI_ERR_INFO_MSK   (0xff << AXI_ERR_INFO_OFF)
+#define FIFO_ERR_INFO_OFF  8
+#define FIFO_ERR_INFO_MSK  (0xff << FIFO_ERR_INFO_OFF)
 #define INT_COAL_EN0x19c
 #define OQ_INT_COAL_TIME   0x1a0
 #define OQ_INT_COAL_CNT0x1a4
@@ -85,6 +117,26 @@
 #define ENT_INT_SRC_MSK3_ENT95_MSK_MSK (0x1 << ENT_INT_SRC_MSK3_ENT95_MSK_OFF)
 #define SAS_ECC_INTR   0x1e8
 #define SAS_ECC_INTR_MSK   0x1ec
+#define SAS_ECC_INTR_DQE_ECC_1B_OFF0
+#define SAS_ECC_INTR_DQE_ECC_MB_OFF1
+#define SAS_ECC_INTR_IOST_ECC_1B_OFF   2
+#define SAS_ECC_INTR_IOST_ECC_MB_OFF   3
+#define SAS_ECC_INTR_ITCT_ECC_MB_OFF   4
+#define SAS_ECC_INTR_ITCT_ECC_1B_OFF   5
+#define SAS_ECC_INTR_IOSTLIST_ECC_MB_OFF   6
+#define SAS_ECC_INTR_IOSTLIST_ECC_1B_OFF   7
+#define SAS_ECC_INTR_ITCTLIST_ECC_MB_OFF   8
+#define SAS_ECC_INTR_ITCTLIST_ECC_1B_OFF   9
+#define SAS_ECC_INTR_CQE_ECC_1B_OFF10
+#define SAS_ECC_INTR_CQE_ECC_MB_OFF11
+#define SAS_ECC_INTR_NCQ_MEM0_ECC_MB_OFF   12
+#define SAS_ECC_INTR_NCQ_MEM0_ECC_1B_OFF   13
+#define SAS_ECC_INTR_NCQ_MEM1_ECC_MB_OFF   14
+#define SAS_ECC_INTR_NCQ_MEM1_ECC_1B_OFF   15
+#define SAS_ECC_INTR_NCQ_MEM2_ECC_MB_OFF   16
+#define SAS_ECC_INTR_NCQ_MEM2_ECC_1B_OFF   17
+#define SAS_ECC_INTR_NCQ_MEM3_ECC_MB_OFF   18
+#define SAS_ECC_INTR_NCQ_MEM3_ECC_1B_OFF   19
 #define HGC_ERR_STAT_EN0x238
 #define DLVRY_Q_0_BASE_ADDR_LO 0x260
 #define DLVRY_Q_0_BASE_ADDR_HI 0x264
@@ -98,6 +150,20 @@
 #define COMPL_Q_0_DEPTH0x4e8
 #define COMPL_Q_0_WR_PTR   0x4ec
 #define COMPL_Q_0_RD_PTR   0x4f0
+#define HGC_RXM_DFX_STATUS14   0xae8
+#define HGC_RXM_DFX_STATUS14_MEM0_OFF  0
+#define HGC_RXM_DFX_STATUS14_MEM0_MSK  (0x1ff <<\
+   HGC_RXM_DFX_STATUS14_MEM0_OFF)
+#define HGC_RXM_DFX_STATUS14_MEM1_OFF  9
+#define HGC_RXM_DFX_STATUS14_MEM1_MSK  (0x1ff <<\
+   HGC_RXM_DFX_STATUS14_MEM1_OFF)
+#define HGC_RXM_DFX_STATUS14_MEM2_OFF  18
+#define HGC_RXM_DFX_STATUS14_MEM2_MSK  

[PATCH v2 03/22] scsi: hisi_sas: relocate get_ata_protocol()

2017-05-25 Thread John Garry
From: Xiang Chen 

Relocate get_ata_protocol() to a common location, as future
hw versions will require it.
Also rename with "hisi_sas_" prefix for consistency.

Signed-off-by: Xiang Chen 
Signed-off-by: John Garry 
---
 drivers/scsi/hisi_sas/hisi_sas.h   |  1 +
 drivers/scsi/hisi_sas/hisi_sas_main.c  | 65 +
 drivers/scsi/hisi_sas/hisi_sas_v2_hw.c | 67 +-
 3 files changed, 68 insertions(+), 65 deletions(-)

diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h
index 88f06be..02ec10b 100644
--- a/drivers/scsi/hisi_sas/hisi_sas.h
+++ b/drivers/scsi/hisi_sas/hisi_sas.h
@@ -355,6 +355,7 @@ struct hisi_sas_command_table_ssp {
struct hisi_sas_command_table_stp stp;
 };
 
+extern u8 hisi_sas_get_ata_protocol(u8 cmd, int direction);
 extern struct hisi_sas_port *to_hisi_sas_port(struct asd_sas_port *sas_port);
 extern int hisi_sas_probe(struct platform_device *pdev,
  const struct hisi_sas_hw *ops);
diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c 
b/drivers/scsi/hisi_sas/hisi_sas_main.c
index d1b7dbf..8fce20b 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_main.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_main.c
@@ -15,6 +15,12 @@
 #define DEV_IS_GONE(dev) \
((!dev) || (dev->dev_type == SAS_PHY_UNUSED))
 
+#define SATA_PROTOCOL_NONDATA  0x1
+#define SATA_PROTOCOL_PIO  0x2
+#define SATA_PROTOCOL_DMA  0x4
+#define SATA_PROTOCOL_FPDMA0x8
+#define SATA_PROTOCOL_ATAPI0x10
+
 static int hisi_sas_debug_issue_ssp_tmf(struct domain_device *device,
u8 *lun, struct hisi_sas_tmf_task *tmf);
 static int
@@ -23,6 +29,65 @@ static int hisi_sas_debug_issue_ssp_tmf(struct domain_device 
*device,
 int abort_flag, int tag);
 static int hisi_sas_softreset_ata_disk(struct domain_device *device);
 
+u8 hisi_sas_get_ata_protocol(u8 cmd, int direction)
+{
+   switch (cmd) {
+   case ATA_CMD_FPDMA_WRITE:
+   case ATA_CMD_FPDMA_READ:
+   case ATA_CMD_FPDMA_RECV:
+   case ATA_CMD_FPDMA_SEND:
+   case ATA_CMD_NCQ_NON_DATA:
+   return SATA_PROTOCOL_FPDMA;
+
+   case ATA_CMD_DOWNLOAD_MICRO:
+   case ATA_CMD_ID_ATA:
+   case ATA_CMD_PMP_READ:
+   case ATA_CMD_READ_LOG_EXT:
+   case ATA_CMD_PIO_READ:
+   case ATA_CMD_PIO_READ_EXT:
+   case ATA_CMD_PMP_WRITE:
+   case ATA_CMD_WRITE_LOG_EXT:
+   case ATA_CMD_PIO_WRITE:
+   case ATA_CMD_PIO_WRITE_EXT:
+   return SATA_PROTOCOL_PIO;
+
+   case ATA_CMD_DSM:
+   case ATA_CMD_DOWNLOAD_MICRO_DMA:
+   case ATA_CMD_PMP_READ_DMA:
+   case ATA_CMD_PMP_WRITE_DMA:
+   case ATA_CMD_READ:
+   case ATA_CMD_READ_EXT:
+   case ATA_CMD_READ_LOG_DMA_EXT:
+   case ATA_CMD_READ_STREAM_DMA_EXT:
+   case ATA_CMD_TRUSTED_RCV_DMA:
+   case ATA_CMD_TRUSTED_SND_DMA:
+   case ATA_CMD_WRITE:
+   case ATA_CMD_WRITE_EXT:
+   case ATA_CMD_WRITE_FUA_EXT:
+   case ATA_CMD_WRITE_QUEUED:
+   case ATA_CMD_WRITE_LOG_DMA_EXT:
+   case ATA_CMD_WRITE_STREAM_DMA_EXT:
+   return SATA_PROTOCOL_DMA;
+
+   case ATA_CMD_CHK_POWER:
+   case ATA_CMD_DEV_RESET:
+   case ATA_CMD_EDD:
+   case ATA_CMD_FLUSH:
+   case ATA_CMD_FLUSH_EXT:
+   case ATA_CMD_VERIFY:
+   case ATA_CMD_VERIFY_EXT:
+   case ATA_CMD_SET_FEATURES:
+   case ATA_CMD_STANDBY:
+   case ATA_CMD_STANDBYNOW1:
+   return SATA_PROTOCOL_NONDATA;
+   default:
+   if (direction == DMA_NONE)
+   return SATA_PROTOCOL_NONDATA;
+   return SATA_PROTOCOL_PIO;
+   }
+}
+EXPORT_SYMBOL_GPL(hisi_sas_get_ata_protocol);
+
 static struct hisi_hba *dev_to_hisi_hba(struct domain_device *device)
 {
return device->port->ha->lldd_ha;
diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c 
b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c
index 193cc43..397f01e 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c
@@ -554,12 +554,6 @@ enum {
 #define DIR_TO_DEVICE 2
 #define DIR_RESERVED 3
 
-#define SATA_PROTOCOL_NONDATA  0x1
-#define SATA_PROTOCOL_PIO  0x2
-#define SATA_PROTOCOL_DMA  0x4
-#define SATA_PROTOCOL_FPDMA0x8
-#define SATA_PROTOCOL_ATAPI0x10
-
 #define ERR_ON_TX_PHASE(err_phase) (err_phase == 0x2 || \
err_phase == 0x4 || err_phase == 0x8 ||\
err_phase == 0x6 || err_phase == 0xa)
@@ -2348,64 +2342,6 @@ static void slot_err_v2_hw(struct hisi_hba *hisi_hba,
return sts;
 }
 
-static u8 get_ata_protocol(u8 cmd, int direction)
-{
-   switch (cmd) {
-   case ATA_CMD_FPDMA_WRITE:
-   case ATA_CMD_FPDMA_READ:
-   case ATA_CMD_FPDMA_RECV:
-   case ATA_CMD_FPDMA_SEND:
-   case ATA_CMD_NCQ_NON_DATA:
- 

[PATCH v2 13/22] scsi: hisi_sas: add v3 cq interrupt handler

2017-05-25 Thread John Garry
From: Xiang Chen 

Add v3 cq interrupt handler slot_complete_v2_hw().

Signed-off-by: John Garry 
Signed-off-by: Xiang Chen 
---
 drivers/scsi/hisi_sas/hisi_sas.h   |   1 +
 drivers/scsi/hisi_sas/hisi_sas_main.c  |  32 
 drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 331 +
 3 files changed, 364 insertions(+)

diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h
index ce2666b..2315102 100644
--- a/drivers/scsi/hisi_sas/hisi_sas.h
+++ b/drivers/scsi/hisi_sas/hisi_sas.h
@@ -371,6 +371,7 @@ extern void hisi_sas_sata_done(struct sas_task *task,
struct hisi_sas_slot *slot);
 extern int hisi_sas_get_ncq_tag(struct sas_task *task, u32 *tag);
 extern int hisi_sas_get_fw_info(struct hisi_hba *hisi_hba);
+extern bool hisi_sas_is_rw_cmd(struct sas_task *task);
 extern int hisi_sas_probe(struct platform_device *pdev,
  const struct hisi_sas_hw *ops);
 extern int hisi_sas_remove(struct platform_device *pdev);
diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c 
b/drivers/scsi/hisi_sas/hisi_sas_main.c
index fba06fb..a6d0bf8 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_main.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_main.c
@@ -118,6 +118,38 @@ int hisi_sas_get_ncq_tag(struct sas_task *task, u32 *tag)
 }
 EXPORT_SYMBOL_GPL(hisi_sas_get_ncq_tag);
 
+bool hisi_sas_is_rw_cmd(struct sas_task *task)
+{
+   switch (task->task_proto) {
+   case SAS_PROTOCOL_SSP:
+   {
+   unsigned char op = task->ssp_task.cmd->cmnd[0];
+
+   if (op == READ_6 || op == WRITE_6 ||
+   op == READ_10 || op == WRITE_10 ||
+   op == READ_12 || op == WRITE_12 ||
+   op == READ_16 || op == WRITE_16)
+   return true;
+   break;
+   }
+   case SAS_PROTOCOL_SATA:
+   case SAS_PROTOCOL_STP:
+   case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP:
+   {
+   u32 cmd_proto = hisi_sas_get_ata_protocol(
+   task->ata_task.fis.command,
+   task->data_dir);
+   if (cmd_proto != SATA_PROTOCOL_NONDATA)
+   return true;
+   break;
+   }
+   default:
+   break;
+   }
+   return false;
+}
+EXPORT_SYMBOL_GPL(hisi_sas_is_rw_cmd);
+
 static struct hisi_hba *dev_to_hisi_hba(struct domain_device *device)
 {
return device->port->ha->lldd_ha;
diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c 
b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
index e9fdedd..f8ac239 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
@@ -157,6 +157,32 @@
 #define SL_RX_BCAST_CHK_MSK(PORT_BASE + 0x2c0)
 #define PHYCTRL_OOB_RESTART_MSK(PORT_BASE + 0x2c4)
 
+/* Completion header */
+/* dw0 */
+#define CMPLT_HDR_CMPLT_OFF0
+#define CMPLT_HDR_CMPLT_MSK(0x3 << CMPLT_HDR_CMPLT_OFF)
+#define CMPLT_HDR_ERROR_PHASE_OFF   2
+#define CMPLT_HDR_ERROR_PHASE_MSK   (0xff << CMPLT_HDR_ERROR_PHASE_OFF)
+#define CMPLT_HDR_RSPNS_XFRD_OFF   10
+#define CMPLT_HDR_RSPNS_XFRD_MSK   (0x1 << CMPLT_HDR_RSPNS_XFRD_OFF)
+#define CMPLT_HDR_ERX_OFF  12
+#define CMPLT_HDR_ERX_MSK  (0x1 << CMPLT_HDR_ERX_OFF)
+#define CMPLT_HDR_ABORT_STAT_OFF   13
+#define CMPLT_HDR_ABORT_STAT_MSK   (0x7 << CMPLT_HDR_ABORT_STAT_OFF)
+/* abort_stat */
+#define STAT_IO_NOT_VALID  0x1
+#define STAT_IO_NO_DEVICE  0x2
+#define STAT_IO_COMPLETE   0x3
+#define STAT_IO_ABORTED0x4
+/* dw1 */
+#define CMPLT_HDR_IPTT_OFF 0
+#define CMPLT_HDR_IPTT_MSK (0x << CMPLT_HDR_IPTT_OFF)
+#define CMPLT_HDR_DEV_ID_OFF   16
+#define CMPLT_HDR_DEV_ID_MSK   (0x << CMPLT_HDR_DEV_ID_OFF)
+/* dw3 */
+#define CMPLT_HDR_IO_IN_TARGET_OFF 17
+#define CMPLT_HDR_IO_IN_TARGET_MSK (0x1 << CMPLT_HDR_IO_IN_TARGET_OFF)
+
 struct hisi_sas_complete_v3_hdr {
__le32 dw0;
__le32 dw1;
@@ -164,6 +190,8 @@ struct hisi_sas_complete_v3_hdr {
__le32 dw3;
 };
 
+#define IO_RX_DATA_LEN_UNDERFLOW_OFF   6
+#define IO_RX_DATA_LEN_UNDERFLOW_MSK   (1 << IO_RX_DATA_LEN_UNDERFLOW_OFF)
 #define HISI_SAS_COMMAND_ENTRIES_V3_HW 4096
 #define HISI_SAS_MSI_COUNT_V3_HW 32
 
@@ -628,6 +656,286 @@ static irqreturn_t int_chnl_int_v3_hw(int irq_no, void *p)
return IRQ_HANDLED;
 }
 
+static void slot_err_v3_hw(struct hisi_hba *hisi_hba,
+   struct sas_task *task,
+   struct hisi_sas_slot *slot)
+{
+   struct task_status_struct *ts = >task_status;
+   struct hisi_sas_complete_v3_hdr *complete_queue =
+   hisi_hba->complete_hdr[slot->cmplt_queue];
+   struct hisi_sas_complete_v3_hdr *complete_hdr 

[PATCH v2 16/22] scsi: hisi_sas: add v3 code to send ATA frame

2017-05-25 Thread John Garry
From: Xiang Chen 

Add code to prepare ATA frame for v3 hw

Signed-off-by: John Garry 
Signed-off-by: Xiang Chen 
---
 drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 106 +
 1 file changed, 106 insertions(+)

diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c 
b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
index 5de4738..e682ad7 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
@@ -171,8 +171,11 @@
 #define CMD_HDR_CMD_OFF29
 #define CMD_HDR_CMD_MSK(0x7 << CMD_HDR_CMD_OFF)
 /* dw1 */
+#define CMD_HDR_UNCON_CMD_OFF  3
 #define CMD_HDR_DIR_OFF5
 #define CMD_HDR_DIR_MSK(0x3 << CMD_HDR_DIR_OFF)
+#define CMD_HDR_RESET_OFF  7
+#define CMD_HDR_RESET_MSK  (0x1 << CMD_HDR_RESET_OFF)
 #define CMD_HDR_VDTL_OFF   10
 #define CMD_HDR_VDTL_MSK   (0x1 << CMD_HDR_VDTL_OFF)
 #define CMD_HDR_FRAME_TYPE_OFF 11
@@ -182,6 +185,8 @@
 /* dw2 */
 #define CMD_HDR_CFL_OFF0
 #define CMD_HDR_CFL_MSK(0x1ff << CMD_HDR_CFL_OFF)
+#define CMD_HDR_NCQ_TAG_OFF10
+#define CMD_HDR_NCQ_TAG_MSK(0x1f << CMD_HDR_NCQ_TAG_OFF)
 #define CMD_HDR_MRFL_OFF   15
 #define CMD_HDR_MRFL_MSK   (0x1ff << CMD_HDR_MRFL_OFF)
 #define CMD_HDR_SG_MOD_OFF 24
@@ -246,6 +251,11 @@ enum {
 #define DIR_TO_DEVICE 2
 #define DIR_RESERVED 3
 
+#define CMD_IS_UNCONSTRAINT(cmd) \
+   ((cmd == ATA_CMD_READ_LOG_EXT) || \
+   (cmd == ATA_CMD_READ_LOG_DMA_EXT) || \
+   (cmd == ATA_CMD_DEV_RESET))
+
 static u32 hisi_sas_read32(struct hisi_hba *hisi_hba, u32 off)
 {
void __iomem *regs = hisi_hba->regs + off;
@@ -711,6 +721,101 @@ static int prep_smp_v3_hw(struct hisi_hba *hisi_hba,
return rc;
 }
 
+static int get_ncq_tag_v3_hw(struct sas_task *task, u32 *tag)
+{
+   struct ata_queued_cmd *qc = task->uldd_task;
+
+   if (qc) {
+   if (qc->tf.command == ATA_CMD_FPDMA_WRITE ||
+   qc->tf.command == ATA_CMD_FPDMA_READ) {
+   *tag = qc->tag;
+   return 1;
+   }
+   }
+   return 0;
+}
+
+static int prep_ata_v3_hw(struct hisi_hba *hisi_hba,
+ struct hisi_sas_slot *slot)
+{
+   struct sas_task *task = slot->task;
+   struct domain_device *device = task->dev;
+   struct domain_device *parent_dev = device->parent;
+   struct hisi_sas_device *sas_dev = device->lldd_dev;
+   struct hisi_sas_cmd_hdr *hdr = slot->cmd_hdr;
+   struct asd_sas_port *sas_port = device->port;
+   struct hisi_sas_port *port = to_hisi_sas_port(sas_port);
+   u8 *buf_cmd;
+   int has_data = 0, rc = 0, hdr_tag = 0;
+   u32 dw1 = 0, dw2 = 0;
+
+   hdr->dw0 = cpu_to_le32(port->id << CMD_HDR_PORT_OFF);
+   if (parent_dev && DEV_IS_EXPANDER(parent_dev->dev_type))
+   hdr->dw0 |= cpu_to_le32(3 << CMD_HDR_CMD_OFF);
+   else
+   hdr->dw0 |= cpu_to_le32(4 << CMD_HDR_CMD_OFF);
+
+   switch (task->data_dir) {
+   case DMA_TO_DEVICE:
+   has_data = 1;
+   dw1 |= DIR_TO_DEVICE << CMD_HDR_DIR_OFF;
+   break;
+   case DMA_FROM_DEVICE:
+   has_data = 1;
+   dw1 |= DIR_TO_INI << CMD_HDR_DIR_OFF;
+   break;
+   default:
+   dw1 &= ~CMD_HDR_DIR_MSK;
+   }
+
+   if ((task->ata_task.fis.command == ATA_CMD_DEV_RESET) &&
+   (task->ata_task.fis.control & ATA_SRST))
+   dw1 |= 1 << CMD_HDR_RESET_OFF;
+
+   dw1 |= (hisi_sas_get_ata_protocol(
+   task->ata_task.fis.command, task->data_dir))
+   << CMD_HDR_FRAME_TYPE_OFF;
+   dw1 |= sas_dev->device_id << CMD_HDR_DEV_ID_OFF;
+
+   if (CMD_IS_UNCONSTRAINT(task->ata_task.fis.command))
+   dw1 |= 1 << CMD_HDR_UNCON_CMD_OFF;
+
+   hdr->dw1 = cpu_to_le32(dw1);
+
+   /* dw2 */
+   if (task->ata_task.use_ncq && get_ncq_tag_v3_hw(task, _tag)) {
+   task->ata_task.fis.sector_count |= (u8) (hdr_tag << 3);
+   dw2 |= hdr_tag << CMD_HDR_NCQ_TAG_OFF;
+   }
+
+   dw2 |= (HISI_SAS_MAX_STP_RESP_SZ / 4) << CMD_HDR_CFL_OFF |
+   2 << CMD_HDR_SG_MOD_OFF;
+   hdr->dw2 = cpu_to_le32(dw2);
+
+   /* dw3 */
+   hdr->transfer_tags = cpu_to_le32(slot->idx);
+
+   if (has_data) {
+   rc = prep_prd_sge_v3_hw(hisi_hba, slot, hdr, task->scatter,
+   slot->n_elem);
+   if (rc)
+   return rc;
+   }
+
+   hdr->data_transfer_len = cpu_to_le32(task->total_xfer_len);
+   hdr->cmd_table_addr = cpu_to_le64(slot->command_table_dma);
+   

[PATCH v2 18/22] scsi: hisi_sas: add v3 code to send internal abort command

2017-05-25 Thread John Garry
From: Xiang Chen 

Add code to prepare internal abort command.

Signed-off-by: John Garry 
Signed-off-by: Xiang Chen 
---
 drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 38 ++
 1 file changed, 38 insertions(+)

diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c 
b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
index c8b6997..6273368 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
@@ -165,6 +165,10 @@
 /* HW dma structures */
 /* Delivery queue header */
 /* dw0 */
+#define CMD_HDR_ABORT_FLAG_OFF 0
+#define CMD_HDR_ABORT_FLAG_MSK (0x3 << CMD_HDR_ABORT_FLAG_OFF)
+#define CMD_HDR_ABORT_DEVICE_TYPE_OFF  2
+#define CMD_HDR_ABORT_DEVICE_TYPE_MSK  (0x1 << CMD_HDR_ABORT_DEVICE_TYPE_OFF)
 #define CMD_HDR_RESP_REPORT_OFF5
 #define CMD_HDR_RESP_REPORT_MSK(0x1 << CMD_HDR_RESP_REPORT_OFF)
 #define CMD_HDR_TLR_CTRL_OFF   6
@@ -204,6 +208,11 @@
 #define CMD_HDR_DIF_SGL_LEN_MSK(0x << 
CMD_HDR_DIF_SGL_LEN_OFF)
 #define CMD_HDR_DATA_SGL_LEN_OFF   16
 #define CMD_HDR_DATA_SGL_LEN_MSK   (0x << CMD_HDR_DATA_SGL_LEN_OFF)
+/* dw7 */
+#define CMD_HDR_ADDR_MODE_SEL_OFF  15
+#define CMD_HDR_ADDR_MODE_SEL_MSK  (1 << CMD_HDR_ADDR_MODE_SEL_OFF)
+#define CMD_HDR_ABORT_IPTT_OFF 16
+#define CMD_HDR_ABORT_IPTT_MSK (0x << CMD_HDR_ABORT_IPTT_OFF)
 
 /* Completion header */
 /* dw0 */
@@ -929,6 +938,34 @@ static int prep_ata_v3_hw(struct hisi_hba *hisi_hba,
return 0;
 }
 
+static int prep_abort_v3_hw(struct hisi_hba *hisi_hba,
+   struct hisi_sas_slot *slot,
+   int device_id, int abort_flag, int tag_to_abort)
+{
+   struct sas_task *task = slot->task;
+   struct domain_device *dev = task->dev;
+   struct hisi_sas_cmd_hdr *hdr = slot->cmd_hdr;
+   struct hisi_sas_port *port = slot->port;
+
+   /* dw0 */
+   hdr->dw0 = cpu_to_le32((5 << CMD_HDR_CMD_OFF) | /*abort*/
+  (port->id << CMD_HDR_PORT_OFF) |
+  ((dev_is_sata(dev) ? 1:0)
+   << CMD_HDR_ABORT_DEVICE_TYPE_OFF) |
+   (abort_flag
+<< CMD_HDR_ABORT_FLAG_OFF));
+
+   /* dw1 */
+   hdr->dw1 = cpu_to_le32(device_id
+   << CMD_HDR_DEV_ID_OFF);
+
+   /* dw7 */
+   hdr->dw7 = cpu_to_le32(tag_to_abort << CMD_HDR_ABORT_IPTT_OFF);
+   hdr->transfer_tags = cpu_to_le32(slot->idx);
+
+   return 0;
+}
+
 static int phy_up_v3_hw(int phy_no, struct hisi_hba *hisi_hba)
 {
int i, res = 0;
@@ -1689,6 +1726,7 @@ static void hisi_sas_v3_remove(struct pci_dev *pdev)
.prep_ssp = prep_ssp_v3_hw,
.prep_smp = prep_smp_v3_hw,
.prep_stp = prep_ata_v3_hw,
+   .prep_abort = prep_abort_v3_hw,
.get_free_slot = get_free_slot_v3_hw,
.start_delivery = start_delivery_v3_hw,
.slot_complete = slot_complete_v3_hw,
-- 
1.9.1



[PATCH v2 00/22] hisi_sas: hip08 support

2017-05-25 Thread John Garry
This patchset adds support for the HiSilicon SAS controller
in the hip08 chipset.

The key difference compared to earlier chipsets is that the
controller is an integrated PCI endpoint in hip08.
As such, the controller is a pci device (not a platform device,
like v2 hw in hip07).

The driver is refactored so it can support both platform and 
pci device-based controllers.

New hw layer file hisi_sas_v3_hw.c is added for v3 hw
support, which also includes pci device proving and
initialisation. 

Common functionality is still in hisi_sas_main.c, along with
platform device probing and initialization.

As for the patches, (ignoring #1 and #2) the first few
reorganise some functions from v2 hw.c into main.c, as they
are required for v3 hw. Then support is added for pci
device-based controller in subsequent patches.
And then hip08 support is added in the final patches.

Differences to v1 series:
- Addressed Arnd's comments, including:
 - read sas address from device node DSD under PCI host
bridge
 - add comment in fatal axi error patch commit log regarding
controller reset
 - eliminate hisi_sas_pci_init.c, and move functionality into
hisi_sas_v3_hw.c, eliminating one layer of indirection

John Garry (4):
  scsi: hisi_sas: add pci_dev in hisi_hba struct
  scsi: hisi_sas: create hisi_sas_get_fw_info()
  scsi: hisi_sas: add skeleton v3 hw driver
  scsi: hisi_sas: add initialisation for v3 pci-based controller

Xiang Chen (18):
  scsi: hisi_sas: fix timeout check in hisi_sas_internal_task_abort()
  scsi: hisi_sas: optimise the usage of hisi_hba.lock
  scsi: hisi_sas: relocate get_ata_protocol()
  scsi: hisi_sas: relocate sata_done_v2_hw()
  scsi: hisi_sas: relocate get_ncq_tag_v2_hw()
  scsi: hisi_sas: add v3 hw init
  scsi: hisi_sas: add v3 hw PHY init
  scsi: hisi_sas: add phy up/down/bcast and channel ISR
  scsi: hisi_sas: add v3 cq interrupt handler
  scsi: hisi_sas: add v3 code to send SSP frame
  scsi: hisi_sas: add v3 code to send SMP frame
  scsi: hisi_sas: add v3 code to send ATA frame
  scsi: hisi_sas: add v3 code for itct setup and free
  scsi: hisi_sas: add v3 code to send internal abort command
  scsi: hisi_sas: add get_wideport_bitmap_v3_hw()
  scsi: hisi_sas: Add v3 code to support ECC and AXI bus fatal error
  scsi: hisi_sas: add v3 code to fill some more hw function pointers
  scsi: hisi_sas: modify internal abort dev flow for v3 hw

 drivers/scsi/hisi_sas/Kconfig  |   10 +-
 drivers/scsi/hisi_sas/Makefile |1 +
 drivers/scsi/hisi_sas/hisi_sas.h   |   28 +-
 drivers/scsi/hisi_sas/hisi_sas_main.c  |  377 --
 drivers/scsi/hisi_sas/hisi_sas_v1_hw.c |   51 +-
 drivers/scsi/hisi_sas/hisi_sas_v2_hw.c |  175 +--
 drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 2236 
 7 files changed, 2622 insertions(+), 256 deletions(-)
 create mode 100644 drivers/scsi/hisi_sas/hisi_sas_v3_hw.c

-- 
1.9.1



Re: [PATCH 2/2] libsas: Enhance libsas hotplug

2017-05-25 Thread John Garry

Hi,

There are some comments, inline.

In general, if it works, it looks ok.

Other reviews would be greatly appreciated - Hannes, Christoph, 
Johannes, Dan - please.


> Libsas complete a hotplug event notified by LLDD in several works,
> for example, if libsas receive a PHYE_LOSS_OF_SIGNAL, we process it
> in following steps:
>
> notify_phy_event[interrupt context]
> sas_queue_event[queue work on shost->work_q]
> sas_phye_loss_of_signal[running in shost->work_q]
> sas_deform_port[remove sas port]
> sas_unregister_dev
> sas_discover_event[queue destruct work on 
shost->work_q tail]

>
> In above case, complete whole hotplug in two works, remove sas port 
first, then
> put the destruction of device in another work and queue it on in the 
tail of
> workqueue, since sas port is the parent of the children rphy device, 
so if remove
> sas port first, the children rphy device would also be deleted, when 
the destruction
> work coming, it would find the target has been removed already, and 
report a

> sysfs warning calltrace.
>
> queue tail queue head
> DISCE_DESTRUCT> PORTE_BYTES_DMAED event 
->PHYE_LOSS_OF_SIGNAL[running]

>
> There are other hotplug issues in current framework, in above case, 
if there is
> hotadd sas event queued between hotremove works, the hotplug order 
would be broken

> and unexpected issues would happen.
>
> In this patch, we try to solve these issues in following steps:
> 1. create a new workqueue used to run sas event work, instead of scsi 
host workqueue,
>because we may block sas event work, we cannot block the normal 
scsi works.


What do we block the event work for?

> 2. create a new workqueue used to run sas discovery events work, 
instead of scsi host
>workqueue, because in some cases, eg. in revalidate domain event, 
we may unregister
>a sas device and discover new one, we must sync the execution, 
wait the remove process
>finish, then start a new discovery. So we must put the probe and 
destruct discovery

>events in a new workqueue to avoid deadlock.
> 3. introudce a asd_sas_port level wait-complete and a sas_discovery 
level wait-complete
>we use former wait-complete to achieve a sas event atomic process 
and use latter to

>make a sas discovery sync.
> 4. remove disco_mutex in sas_revalidate_domain, since now 
sas_revalidate_domain sync
>the destruct discovery event execution, it's no need to lock disco 
mutex there.

>
> Signed-off-by: Yijing Wang 
> ---
>  drivers/scsi/libsas/sas_discover.c | 58 
--

>  drivers/scsi/libsas/sas_event.c|  2 +-
>  drivers/scsi/libsas/sas_expander.c |  9 +-
>  drivers/scsi/libsas/sas_init.c | 31 +++-
>  drivers/scsi/libsas/sas_internal.h | 50 
>  drivers/scsi/libsas/sas_port.c |  4 +++
>  include/scsi/libsas.h  | 11 +++-
>  7 files changed, 146 insertions(+), 19 deletions(-)
>
> diff --git a/drivers/scsi/libsas/sas_discover.c 
b/drivers/scsi/libsas/sas_discover.c

> index 60de662..43e8a1e 100644
> --- a/drivers/scsi/libsas/sas_discover.c
> +++ b/drivers/scsi/libsas/sas_discover.c
> @@ -503,11 +503,10 @@ static void sas_revalidate_domain(struct 
work_struct *work)

>  struct domain_device *ddev = port->port_dev;
>
>  /* prevent revalidation from finding sata links in recovery */
> -mutex_lock(>disco_mutex);
>  if (test_bit(SAS_HA_ATA_EH_ACTIVE, >state)) {
>  SAS_DPRINTK("REVALIDATION DEFERRED on port %d, pid:%d\n",
>  port->id, task_pid_nr(current));
> -goto out;
> +return;
>  }
>
>  clear_bit(DISCE_REVALIDATE_DOMAIN, >disc.pending);
> @@ -521,20 +520,57 @@ static void sas_revalidate_domain(struct 
work_struct *work)

>
>  SAS_DPRINTK("done REVALIDATING DOMAIN on port %d, pid:%d, res 
0x%x\n",

>  port->id, task_pid_nr(current), res);
> - out:
> -mutex_unlock(>disco_mutex);
> +}
> +
> +static const work_func_t sas_event_fns[DISC_NUM_EVENTS] = {
> +[DISCE_DISCOVER_DOMAIN] = sas_discover_domain,
> +[DISCE_REVALIDATE_DOMAIN] = sas_revalidate_domain,
> +[DISCE_PROBE] = sas_probe_devices,
> +[DISCE_SUSPEND] = sas_suspend_devices,
> +[DISCE_RESUME] = sas_resume_devices,
> +[DISCE_DESTRUCT] = sas_destruct_devices,
> +};
> +
> +/* a simple wrapper for sas discover event funtions */
> +static void sas_discover_common_fn(struct work_struct *work)
> +{
> +struct sas_discovery_event *ev = to_sas_discovery_event(work);
> +struct asd_sas_port *port = ev->port;
> +
> +sas_event_fns[ev->type](work);
> +sas_unbusy_port(port);
>  }
>
>  /* -- Events -- */
>
>  static void sas_chain_work(struct sas_ha_struct *ha, struct sas_work 
*sw)

>  {
> +int ret;
> +struct sas_discovery_event *ev =