Skip to content

Commit 6fc4a2a

Browse files
scsi: target: Move I/O path stats to per CPU
JIRA: https://issues.redhat.com/browse/RHEL-111938 The atomic use in the main I/O path is causing perf issues when using higher performance backend devices and multiple queues. This moves the stats to per CPU. Combined with the next patch that moves the non_ordered/delayed_cmd_count to per CPU, IOPS by up to 33% for 8K IOS when using 4 or more queues. Signed-off-by: Mike Christie <michael.christie@oracle.com> Link: https://lore.kernel.org/r/20250424032741.16216-2-michael.christie@oracle.com Reviewed-by: Hannes Reinecke <hare@suse.de> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com> (cherry picked from commit 9cf2317) Signed-off-by: Maurizio Lombardi <mlombard@redhat.com>
1 parent baa33bc commit 6fc4a2a

File tree

3 files changed

+121
-37
lines changed

3 files changed

+121
-37
lines changed

drivers/target/target_core_device.c

Lines changed: 50 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -55,14 +55,14 @@ transport_lookup_cmd_lun(struct se_cmd *se_cmd)
5555
rcu_read_lock();
5656
deve = target_nacl_find_deve(nacl, se_cmd->orig_fe_lun);
5757
if (deve) {
58-
atomic_long_inc(&deve->total_cmds);
58+
this_cpu_inc(deve->stats->total_cmds);
5959

6060
if (se_cmd->data_direction == DMA_TO_DEVICE)
61-
atomic_long_add(se_cmd->data_length,
62-
&deve->write_bytes);
61+
this_cpu_add(deve->stats->write_bytes,
62+
se_cmd->data_length);
6363
else if (se_cmd->data_direction == DMA_FROM_DEVICE)
64-
atomic_long_add(se_cmd->data_length,
65-
&deve->read_bytes);
64+
this_cpu_add(deve->stats->read_bytes,
65+
se_cmd->data_length);
6666

6767
if ((se_cmd->data_direction == DMA_TO_DEVICE) &&
6868
deve->lun_access_ro) {
@@ -126,14 +126,14 @@ transport_lookup_cmd_lun(struct se_cmd *se_cmd)
126126
* target_core_fabric_configfs.c:target_fabric_port_release
127127
*/
128128
se_cmd->se_dev = rcu_dereference_raw(se_lun->lun_se_dev);
129-
atomic_long_inc(&se_cmd->se_dev->num_cmds);
129+
this_cpu_inc(se_cmd->se_dev->stats->total_cmds);
130130

131131
if (se_cmd->data_direction == DMA_TO_DEVICE)
132-
atomic_long_add(se_cmd->data_length,
133-
&se_cmd->se_dev->write_bytes);
132+
this_cpu_add(se_cmd->se_dev->stats->write_bytes,
133+
se_cmd->data_length);
134134
else if (se_cmd->data_direction == DMA_FROM_DEVICE)
135-
atomic_long_add(se_cmd->data_length,
136-
&se_cmd->se_dev->read_bytes);
135+
this_cpu_add(se_cmd->se_dev->stats->read_bytes,
136+
se_cmd->data_length);
137137

138138
return ret;
139139
}
@@ -322,13 +322,20 @@ int core_enable_device_list_for_node(
322322
struct se_portal_group *tpg)
323323
{
324324
struct se_dev_entry *orig, *new;
325+
int ret = 0;
325326

326327
new = kzalloc(sizeof(*new), GFP_KERNEL);
327328
if (!new) {
328329
pr_err("Unable to allocate se_dev_entry memory\n");
329330
return -ENOMEM;
330331
}
331332

333+
new->stats = alloc_percpu(struct se_dev_entry_io_stats);
334+
if (!new->stats) {
335+
ret = -ENOMEM;
336+
goto free_deve;
337+
}
338+
332339
spin_lock_init(&new->ua_lock);
333340
INIT_LIST_HEAD(&new->ua_list);
334341
INIT_LIST_HEAD(&new->lun_link);
@@ -351,17 +358,17 @@ int core_enable_device_list_for_node(
351358
" for dynamic -> explicit NodeACL conversion:"
352359
" %s\n", nacl->initiatorname);
353360
mutex_unlock(&nacl->lun_entry_mutex);
354-
kfree(new);
355-
return -EINVAL;
361+
ret = -EINVAL;
362+
goto free_stats;
356363
}
357364
if (orig->se_lun_acl != NULL) {
358365
pr_warn_ratelimited("Detected existing explicit"
359366
" se_lun_acl->se_lun_group reference for %s"
360367
" mapped_lun: %llu, failing\n",
361368
nacl->initiatorname, mapped_lun);
362369
mutex_unlock(&nacl->lun_entry_mutex);
363-
kfree(new);
364-
return -EINVAL;
370+
ret = -EINVAL;
371+
goto free_stats;
365372
}
366373

367374
new->se_lun = lun;
@@ -394,6 +401,20 @@ int core_enable_device_list_for_node(
394401

395402
target_luns_data_has_changed(nacl, new, true);
396403
return 0;
404+
405+
free_stats:
406+
free_percpu(new->stats);
407+
free_deve:
408+
kfree(new);
409+
return ret;
410+
}
411+
412+
static void target_free_dev_entry(struct rcu_head *head)
413+
{
414+
struct se_dev_entry *deve = container_of(head, struct se_dev_entry,
415+
rcu_head);
416+
free_percpu(deve->stats);
417+
kfree(deve);
397418
}
398419

399420
void core_disable_device_list_for_node(
@@ -443,7 +464,7 @@ void core_disable_device_list_for_node(
443464
kref_put(&orig->pr_kref, target_pr_kref_release);
444465
wait_for_completion(&orig->pr_comp);
445466

446-
kfree_rcu(orig, rcu_head);
467+
call_rcu(&orig->rcu_head, target_free_dev_entry);
447468

448469
core_scsi3_free_pr_reg_from_nacl(dev, nacl);
449470
target_luns_data_has_changed(nacl, NULL, false);
@@ -689,11 +710,13 @@ struct se_device *target_alloc_device(struct se_hba *hba, const char *name)
689710
if (!dev)
690711
return NULL;
691712

713+
dev->stats = alloc_percpu(struct se_dev_io_stats);
714+
if (!dev->stats)
715+
goto free_device;
716+
692717
dev->queues = kcalloc(nr_cpu_ids, sizeof(*dev->queues), GFP_KERNEL);
693-
if (!dev->queues) {
694-
hba->backend->ops->free_device(dev);
695-
return NULL;
696-
}
718+
if (!dev->queues)
719+
goto free_stats;
697720

698721
dev->queue_cnt = nr_cpu_ids;
699722
for (i = 0; i < dev->queue_cnt; i++) {
@@ -707,6 +730,7 @@ struct se_device *target_alloc_device(struct se_hba *hba, const char *name)
707730
INIT_WORK(&q->sq.work, target_queued_submit_work);
708731
}
709732

733+
710734
dev->se_hba = hba;
711735
dev->transport = hba->backend->ops;
712736
dev->transport_flags = dev->transport->transport_flags_default;
@@ -791,6 +815,12 @@ struct se_device *target_alloc_device(struct se_hba *hba, const char *name)
791815
sizeof(dev->t10_wwn.revision));
792816

793817
return dev;
818+
819+
free_stats:
820+
free_percpu(dev->stats);
821+
free_device:
822+
hba->backend->ops->free_device(dev);
823+
return NULL;
794824
}
795825

796826
/*
@@ -1001,6 +1031,7 @@ void target_free_device(struct se_device *dev)
10011031
dev->transport->free_prot(dev);
10021032

10031033
kfree(dev->queues);
1034+
free_percpu(dev->stats);
10041035
dev->transport->free_device(dev);
10051036
}
10061037

drivers/target/target_core_stat.c

Lines changed: 57 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -280,30 +280,51 @@ static ssize_t target_stat_lu_num_cmds_show(struct config_item *item,
280280
char *page)
281281
{
282282
struct se_device *dev = to_stat_lu_dev(item);
283+
struct se_dev_io_stats *stats;
284+
unsigned int cpu;
285+
u32 cmds = 0;
286+
287+
for_each_possible_cpu(cpu) {
288+
stats = per_cpu_ptr(dev->stats, cpu);
289+
cmds += stats->total_cmds;
290+
}
283291

284292
/* scsiLuNumCommands */
285-
return snprintf(page, PAGE_SIZE, "%lu\n",
286-
atomic_long_read(&dev->num_cmds));
293+
return snprintf(page, PAGE_SIZE, "%u\n", cmds);
287294
}
288295

289296
static ssize_t target_stat_lu_read_mbytes_show(struct config_item *item,
290297
char *page)
291298
{
292299
struct se_device *dev = to_stat_lu_dev(item);
300+
struct se_dev_io_stats *stats;
301+
unsigned int cpu;
302+
u32 bytes = 0;
303+
304+
for_each_possible_cpu(cpu) {
305+
stats = per_cpu_ptr(dev->stats, cpu);
306+
bytes += stats->read_bytes;
307+
}
293308

294309
/* scsiLuReadMegaBytes */
295-
return snprintf(page, PAGE_SIZE, "%lu\n",
296-
atomic_long_read(&dev->read_bytes) >> 20);
310+
return snprintf(page, PAGE_SIZE, "%u\n", bytes >> 20);
297311
}
298312

299313
static ssize_t target_stat_lu_write_mbytes_show(struct config_item *item,
300314
char *page)
301315
{
302316
struct se_device *dev = to_stat_lu_dev(item);
317+
struct se_dev_io_stats *stats;
318+
unsigned int cpu;
319+
u32 bytes = 0;
320+
321+
for_each_possible_cpu(cpu) {
322+
stats = per_cpu_ptr(dev->stats, cpu);
323+
bytes += stats->write_bytes;
324+
}
303325

304326
/* scsiLuWrittenMegaBytes */
305-
return snprintf(page, PAGE_SIZE, "%lu\n",
306-
atomic_long_read(&dev->write_bytes) >> 20);
327+
return snprintf(page, PAGE_SIZE, "%u\n", bytes >> 20);
307328
}
308329

309330
static ssize_t target_stat_lu_resets_show(struct config_item *item, char *page)
@@ -1019,18 +1040,26 @@ static ssize_t target_stat_auth_num_cmds_show(struct config_item *item,
10191040
{
10201041
struct se_lun_acl *lacl = auth_to_lacl(item);
10211042
struct se_node_acl *nacl = lacl->se_lun_nacl;
1043+
struct se_dev_entry_io_stats *stats;
10221044
struct se_dev_entry *deve;
1045+
unsigned int cpu;
10231046
ssize_t ret;
1047+
u32 cmds = 0;
10241048

10251049
rcu_read_lock();
10261050
deve = target_nacl_find_deve(nacl, lacl->mapped_lun);
10271051
if (!deve) {
10281052
rcu_read_unlock();
10291053
return -ENODEV;
10301054
}
1055+
1056+
for_each_possible_cpu(cpu) {
1057+
stats = per_cpu_ptr(deve->stats, cpu);
1058+
cmds += stats->total_cmds;
1059+
}
1060+
10311061
/* scsiAuthIntrOutCommands */
1032-
ret = snprintf(page, PAGE_SIZE, "%lu\n",
1033-
atomic_long_read(&deve->total_cmds));
1062+
ret = snprintf(page, PAGE_SIZE, "%u\n", cmds);
10341063
rcu_read_unlock();
10351064
return ret;
10361065
}
@@ -1040,18 +1069,26 @@ static ssize_t target_stat_auth_read_mbytes_show(struct config_item *item,
10401069
{
10411070
struct se_lun_acl *lacl = auth_to_lacl(item);
10421071
struct se_node_acl *nacl = lacl->se_lun_nacl;
1072+
struct se_dev_entry_io_stats *stats;
10431073
struct se_dev_entry *deve;
1074+
unsigned int cpu;
10441075
ssize_t ret;
1076+
u32 bytes = 0;
10451077

10461078
rcu_read_lock();
10471079
deve = target_nacl_find_deve(nacl, lacl->mapped_lun);
10481080
if (!deve) {
10491081
rcu_read_unlock();
10501082
return -ENODEV;
10511083
}
1084+
1085+
for_each_possible_cpu(cpu) {
1086+
stats = per_cpu_ptr(deve->stats, cpu);
1087+
bytes += stats->read_bytes;
1088+
}
1089+
10521090
/* scsiAuthIntrReadMegaBytes */
1053-
ret = snprintf(page, PAGE_SIZE, "%u\n",
1054-
(u32)(atomic_long_read(&deve->read_bytes) >> 20));
1091+
ret = snprintf(page, PAGE_SIZE, "%u\n", bytes >> 20);
10551092
rcu_read_unlock();
10561093
return ret;
10571094
}
@@ -1061,18 +1098,26 @@ static ssize_t target_stat_auth_write_mbytes_show(struct config_item *item,
10611098
{
10621099
struct se_lun_acl *lacl = auth_to_lacl(item);
10631100
struct se_node_acl *nacl = lacl->se_lun_nacl;
1101+
struct se_dev_entry_io_stats *stats;
10641102
struct se_dev_entry *deve;
1103+
unsigned int cpu;
10651104
ssize_t ret;
1105+
u32 bytes = 0;
10661106

10671107
rcu_read_lock();
10681108
deve = target_nacl_find_deve(nacl, lacl->mapped_lun);
10691109
if (!deve) {
10701110
rcu_read_unlock();
10711111
return -ENODEV;
10721112
}
1113+
1114+
for_each_possible_cpu(cpu) {
1115+
stats = per_cpu_ptr(deve->stats, cpu);
1116+
bytes += stats->write_bytes;
1117+
}
1118+
10731119
/* scsiAuthIntrWrittenMegaBytes */
1074-
ret = snprintf(page, PAGE_SIZE, "%u\n",
1075-
(u32)(atomic_long_read(&deve->write_bytes) >> 20));
1120+
ret = snprintf(page, PAGE_SIZE, "%u\n", bytes >> 20);
10761121
rcu_read_unlock();
10771122
return ret;
10781123
}

include/target/target_core_base.h

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -669,15 +669,19 @@ struct se_lun_acl {
669669
struct se_ml_stat_grps ml_stat_grps;
670670
};
671671

672+
struct se_dev_entry_io_stats {
673+
u32 total_cmds;
674+
u32 read_bytes;
675+
u32 write_bytes;
676+
};
677+
672678
struct se_dev_entry {
673679
u64 mapped_lun;
674680
u64 pr_res_key;
675681
u64 creation_time;
676682
bool lun_access_ro;
677683
u32 attach_count;
678-
atomic_long_t total_cmds;
679-
atomic_long_t read_bytes;
680-
atomic_long_t write_bytes;
684+
struct se_dev_entry_io_stats __percpu *stats;
681685
/* Used for PR SPEC_I_PT=1 and REGISTER_AND_MOVE */
682686
struct kref pr_kref;
683687
struct completion pr_comp;
@@ -800,6 +804,12 @@ struct se_device_queue {
800804
struct se_cmd_queue sq;
801805
};
802806

807+
struct se_dev_io_stats {
808+
u32 total_cmds;
809+
u32 read_bytes;
810+
u32 write_bytes;
811+
};
812+
803813
struct se_device {
804814
/* Used for SAM Task Attribute ordering */
805815
u32 dev_cur_ordered_id;
@@ -821,9 +831,7 @@ struct se_device {
821831
atomic_long_t num_resets;
822832
atomic_long_t aborts_complete;
823833
atomic_long_t aborts_no_task;
824-
atomic_long_t num_cmds;
825-
atomic_long_t read_bytes;
826-
atomic_long_t write_bytes;
834+
struct se_dev_io_stats __percpu *stats;
827835
/* Active commands on this virtual SE device */
828836
atomic_t non_ordered;
829837
bool ordered_sync_in_progress;

0 commit comments

Comments
 (0)