diff options
author | Hannes Reinecke <hare@suse.de> | 2015-01-08 01:43:42 -0500 |
---|---|---|
committer | Christoph Hellwig <hch@lst.de> | 2015-01-09 09:44:28 -0500 |
commit | ded85c193a391a84076d5c6a7a5668fe164a490e (patch) | |
tree | a8f7796e5dcd0c1263155cb8fc62c4d68cc9ec49 | |
parent | b0a93d96b2814c725161f91a4e35d0c29ec0f95b (diff) |
scsi: Implement per-cpu logging buffer
Implement a per-cpu buffer for formatting messages to avoid line breaks
up under high load. This patch implements scmd_printk() and
sdev_prefix_printk() using the per-cpu buffer and makes sdev_printk() a
wrapper for sdev_prefix_printk().
Tested-by: Robert Elliott <elliott@hp.com>
Reviewed-by: Robert Elliott <elliott@hp.com>
Signed-off-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: Christoph Hellwig <hch@lst.de>
-rw-r--r-- | drivers/scsi/Makefile | 2 | ||||
-rw-r--r-- | drivers/scsi/scsi_logging.c | 124 | ||||
-rw-r--r-- | include/scsi/scsi_device.h | 21 |
3 files changed, 133 insertions, 14 deletions
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index 58158f11ed7b..447c2d24aafa 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile | |||
@@ -167,7 +167,7 @@ scsi_mod-y += scsi_scan.o scsi_sysfs.o scsi_devinfo.o | |||
167 | scsi_mod-$(CONFIG_SCSI_NETLINK) += scsi_netlink.o | 167 | scsi_mod-$(CONFIG_SCSI_NETLINK) += scsi_netlink.o |
168 | scsi_mod-$(CONFIG_SYSCTL) += scsi_sysctl.o | 168 | scsi_mod-$(CONFIG_SYSCTL) += scsi_sysctl.o |
169 | scsi_mod-$(CONFIG_SCSI_PROC_FS) += scsi_proc.o | 169 | scsi_mod-$(CONFIG_SCSI_PROC_FS) += scsi_proc.o |
170 | scsi_mod-y += scsi_trace.o | 170 | scsi_mod-y += scsi_trace.o scsi_logging.o |
171 | scsi_mod-$(CONFIG_PM) += scsi_pm.o | 171 | scsi_mod-$(CONFIG_PM) += scsi_pm.o |
172 | 172 | ||
173 | hv_storvsc-y := storvsc_drv.o | 173 | hv_storvsc-y := storvsc_drv.o |
diff --git a/drivers/scsi/scsi_logging.c b/drivers/scsi/scsi_logging.c new file mode 100644 index 000000000000..09d65dea9d56 --- /dev/null +++ b/drivers/scsi/scsi_logging.c | |||
@@ -0,0 +1,124 @@ | |||
1 | /* | ||
2 | * scsi_logging.c | ||
3 | * | ||
4 | * Copyright (C) 2014 SUSE Linux Products GmbH | ||
5 | * Copyright (C) 2014 Hannes Reinecke <hare@suse.de> | ||
6 | * | ||
7 | * This file is released under the GPLv2 | ||
8 | */ | ||
9 | |||
10 | #include <linux/kernel.h> | ||
11 | #include <linux/atomic.h> | ||
12 | |||
13 | #include <scsi/scsi.h> | ||
14 | #include <scsi/scsi_cmnd.h> | ||
15 | #include <scsi/scsi_device.h> | ||
16 | #include <scsi/scsi_dbg.h> | ||
17 | |||
18 | #define SCSI_LOG_SPOOLSIZE 4096 | ||
19 | #define SCSI_LOG_BUFSIZE 128 | ||
20 | |||
21 | #if (SCSI_LOG_SPOOLSIZE / SCSI_LOG_BUFSIZE) > BITS_PER_LONG | ||
22 | #warning SCSI logging bitmask too large | ||
23 | #endif | ||
24 | |||
25 | struct scsi_log_buf { | ||
26 | char buffer[SCSI_LOG_SPOOLSIZE]; | ||
27 | unsigned long map; | ||
28 | }; | ||
29 | |||
30 | static DEFINE_PER_CPU(struct scsi_log_buf, scsi_format_log); | ||
31 | |||
32 | static char *scsi_log_reserve_buffer(size_t *len) | ||
33 | { | ||
34 | struct scsi_log_buf *buf; | ||
35 | unsigned long map_bits = sizeof(buf->buffer) / SCSI_LOG_BUFSIZE; | ||
36 | unsigned long idx = 0; | ||
37 | |||
38 | preempt_disable(); | ||
39 | buf = this_cpu_ptr(&scsi_format_log); | ||
40 | idx = find_first_zero_bit(&buf->map, map_bits); | ||
41 | if (likely(idx < map_bits)) { | ||
42 | while (test_and_set_bit(idx, &buf->map)) { | ||
43 | idx = find_next_zero_bit(&buf->map, map_bits, idx); | ||
44 | if (idx >= map_bits) | ||
45 | break; | ||
46 | } | ||
47 | } | ||
48 | if (WARN_ON(idx >= map_bits)) { | ||
49 | preempt_enable(); | ||
50 | return NULL; | ||
51 | } | ||
52 | *len = SCSI_LOG_BUFSIZE; | ||
53 | return buf->buffer + idx * SCSI_LOG_BUFSIZE; | ||
54 | } | ||
55 | |||
56 | static void scsi_log_release_buffer(char *bufptr) | ||
57 | { | ||
58 | struct scsi_log_buf *buf; | ||
59 | unsigned long idx; | ||
60 | int ret; | ||
61 | |||
62 | buf = this_cpu_ptr(&scsi_format_log); | ||
63 | if (bufptr >= buf->buffer && | ||
64 | bufptr < buf->buffer + SCSI_LOG_SPOOLSIZE) { | ||
65 | idx = (bufptr - buf->buffer) / SCSI_LOG_BUFSIZE; | ||
66 | ret = test_and_clear_bit(idx, &buf->map); | ||
67 | WARN_ON(!ret); | ||
68 | } | ||
69 | preempt_enable(); | ||
70 | } | ||
71 | |||
72 | int sdev_prefix_printk(const char *level, const struct scsi_device *sdev, | ||
73 | const char *name, const char *fmt, ...) | ||
74 | { | ||
75 | va_list args; | ||
76 | char *logbuf; | ||
77 | size_t off = 0, logbuf_len; | ||
78 | int ret; | ||
79 | |||
80 | if (!sdev) | ||
81 | return 0; | ||
82 | |||
83 | logbuf = scsi_log_reserve_buffer(&logbuf_len); | ||
84 | if (!logbuf) | ||
85 | return 0; | ||
86 | |||
87 | if (name) | ||
88 | off += scnprintf(logbuf + off, logbuf_len - off, | ||
89 | "[%s] ", name); | ||
90 | va_start(args, fmt); | ||
91 | off += vscnprintf(logbuf + off, logbuf_len - off, fmt, args); | ||
92 | va_end(args); | ||
93 | ret = dev_printk(level, &sdev->sdev_gendev, "%s", logbuf); | ||
94 | scsi_log_release_buffer(logbuf); | ||
95 | return ret; | ||
96 | } | ||
97 | EXPORT_SYMBOL(sdev_prefix_printk); | ||
98 | |||
99 | int scmd_printk(const char *level, const struct scsi_cmnd *scmd, | ||
100 | const char *fmt, ...) | ||
101 | { | ||
102 | struct gendisk *disk = scmd->request->rq_disk; | ||
103 | va_list args; | ||
104 | char *logbuf; | ||
105 | size_t off = 0, logbuf_len; | ||
106 | int ret; | ||
107 | |||
108 | if (!scmd || !scmd->cmnd) | ||
109 | return 0; | ||
110 | |||
111 | logbuf = scsi_log_reserve_buffer(&logbuf_len); | ||
112 | if (!logbuf) | ||
113 | return 0; | ||
114 | if (disk) | ||
115 | off += scnprintf(logbuf + off, logbuf_len - off, | ||
116 | "[%s] ", disk->disk_name); | ||
117 | va_start(args, fmt); | ||
118 | off += vscnprintf(logbuf + off, logbuf_len - off, fmt, args); | ||
119 | va_end(args); | ||
120 | ret = dev_printk(level, &scmd->device->sdev_gendev, "%s", logbuf); | ||
121 | scsi_log_release_buffer(logbuf); | ||
122 | return ret; | ||
123 | } | ||
124 | EXPORT_SYMBOL(scmd_printk); | ||
diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index 3a4edd1f7dbb..d1aad4d04334 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h | |||
@@ -230,9 +230,6 @@ struct scsi_dh_data { | |||
230 | #define transport_class_to_sdev(class_dev) \ | 230 | #define transport_class_to_sdev(class_dev) \ |
231 | to_scsi_device(class_dev->parent) | 231 | to_scsi_device(class_dev->parent) |
232 | 232 | ||
233 | #define sdev_printk(prefix, sdev, fmt, a...) \ | ||
234 | dev_printk(prefix, &(sdev)->sdev_gendev, fmt, ##a) | ||
235 | |||
236 | #define sdev_dbg(sdev, fmt, a...) \ | 233 | #define sdev_dbg(sdev, fmt, a...) \ |
237 | dev_dbg(&(sdev)->sdev_gendev, fmt, ##a) | 234 | dev_dbg(&(sdev)->sdev_gendev, fmt, ##a) |
238 | 235 | ||
@@ -240,16 +237,14 @@ struct scsi_dh_data { | |||
240 | * like scmd_printk, but the device name is passed in | 237 | * like scmd_printk, but the device name is passed in |
241 | * as a string pointer | 238 | * as a string pointer |
242 | */ | 239 | */ |
243 | #define sdev_prefix_printk(l, sdev, p, fmt, a...) \ | 240 | extern int sdev_prefix_printk(const char *, const struct scsi_device *, |
244 | (p) ? \ | 241 | const char *, const char *, ...); |
245 | sdev_printk(l, sdev, "[%s] " fmt, p, ##a) : \ | 242 | |
246 | sdev_printk(l, sdev, fmt, ##a) | 243 | #define sdev_printk(l, sdev, fmt, a...) \ |
247 | 244 | sdev_prefix_printk(l, sdev, NULL, fmt, ##a) | |
248 | #define scmd_printk(prefix, scmd, fmt, a...) \ | 245 | |
249 | (scmd)->request->rq_disk ? \ | 246 | extern int scmd_printk(const char *, const struct scsi_cmnd *, |
250 | sdev_printk(prefix, (scmd)->device, "[%s] " fmt, \ | 247 | const char *, ...); |
251 | (scmd)->request->rq_disk->disk_name, ##a) : \ | ||
252 | sdev_printk(prefix, (scmd)->device, fmt, ##a) | ||
253 | 248 | ||
254 | #define scmd_dbg(scmd, fmt, a...) \ | 249 | #define scmd_dbg(scmd, fmt, a...) \ |
255 | do { \ | 250 | do { \ |