Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sbin/camcontrol/depop.c
39475 views
1
/*-
2
* Copyright (c) 2021 Netflix, Inc.
3
*
4
* Redistribution and use in source and binary forms, with or without
5
* modification, are permitted provided that the following conditions
6
* are met:
7
* 1. Redistributions of source code must retain the above copyright
8
* notice, this list of conditions, and the following disclaimer,
9
* without modification.
10
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
11
* substantially similar to the "NO WARRANTY" disclaimer below
12
* ("Disclaimer") and any redistribution must be conditioned upon
13
* including a substantially similar Disclaimer requirement for further
14
* binary redistribution.
15
*
16
* NO WARRANTY
17
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
20
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
25
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
26
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
* POSSIBILITY OF SUCH DAMAGES.
28
*
29
*/
30
/*
31
* SCSI disk depop (head depopulation) support
32
*
33
* The standard defines 'storage elements' as the generic way of referring to a
34
* disk drive head. Each storage element has an identifier and an active status.
35
* The health of an element can be queried. Active elements may be removed from
36
* service with a REMOVE ELEMENT AND TRUNCATE (RET) command. Inactive element
37
* may be returned to service with a RESTORE ELEMENTS AND REBUILD (RER)
38
* command. GET PHYSICAL ELEMENT STATUS (GPES) will return a list of elements,
39
* their health, whether they are in service, how much capacity the element is
40
* used for, etc.
41
*
42
* When a depop operation starts, the drive becomes format corrupt. No normal
43
* I/O can be done to the drive and a limited number of CDBs will
44
* succeed. Status can be obtained by either a TEST UNIT READY or a GPES
45
* command. A drive reset will not stop a depop operation, but a power cycle
46
* will. A failed depop operation will be reported when the next TEST UNIT READY
47
* is sent to the drive. Drives that are format corrupt after an interrupted
48
* operation need to have that operation repeated.
49
*
50
* 'depop' provides a wrapper around all these functions.
51
*/
52
53
#include <sys/types.h>
54
55
#include <err.h>
56
#include <inttypes.h>
57
#include <stdio.h>
58
#include <stdlib.h>
59
#include <string.h>
60
#include <unistd.h>
61
62
#include <cam/cam.h>
63
#include <cam/cam_debug.h>
64
#include <cam/cam_ccb.h>
65
#include <cam/scsi/scsi_all.h>
66
#include <cam/scsi/scsi_message.h>
67
#include <camlib.h>
68
#include <scsi_wrap.h>
69
#include "camcontrol.h"
70
71
enum depop_action {
72
DEPOP_NONE,
73
DEPOP_LIST,
74
DEPOP_RESTORE,
75
DEPOP_REMOVE,
76
};
77
78
static int
79
depop_list(struct cam_device *device, int task_attr, int retry_count,
80
int timeout, int verbosemode __unused)
81
{
82
int error = 0;
83
uint32_t dtors;
84
struct scsi_get_physical_element_hdr *hdr;
85
struct scsi_get_physical_element_descriptor *dtor_ptr;
86
87
hdr = scsi_wrap_get_physical_element_status(device, task_attr, retry_count, timeout,
88
SCSI_GPES_FILTER_ALL | SCSI_GPES_REPORT_TYPE_PHYS, 1);
89
if (hdr == NULL)
90
errx(1, "scsi_wrap_get_physical_element_status returned an error");
91
92
/*
93
* OK, we have the data, not report it out.
94
*/
95
dtor_ptr = (struct scsi_get_physical_element_descriptor *)(hdr + 1);
96
dtors = scsi_4btoul(hdr->num_descriptors);
97
printf("Elem ID * Health Capacity\n");
98
for (uint32_t i = 0; i < dtors; i++) {
99
uint32_t id = scsi_4btoul(dtor_ptr[i].element_identifier);
100
uint8_t ralwd = dtor_ptr[i].ralwd;
101
uint8_t type = dtor_ptr[i].physical_element_type;
102
uint8_t health = dtor_ptr[i].physical_element_health;
103
uint64_t cap = scsi_8btou64(dtor_ptr[i].capacity);
104
if (type != GPED_TYPE_STORAGE)
105
printf("0x%08x -- type unknown %d\n", id, type);
106
else
107
printf("0x%08x %c 0x%02x %jd\n", id, ralwd ? '*' : ' ', health, cap);
108
}
109
printf("* -- Element can be restored\n");
110
111
free(hdr);
112
return (error);
113
}
114
115
static int
116
depop_remove(struct cam_device *device, int task_attr, int retry_count,
117
int timeout, int verbosemode __unused, uint32_t elem, uint64_t capacity)
118
{
119
union ccb *ccb;
120
int error = 0;
121
122
ccb = cam_getccb(device);
123
if (ccb == NULL) {
124
warnx("Can't allocate ccb");
125
return (1);
126
}
127
scsi_remove_element_and_truncate(&ccb->csio,
128
retry_count,
129
NULL,
130
task_attr,
131
capacity,
132
elem,
133
SSD_FULL_SIZE,
134
timeout);
135
/* Disable freezing the device queue */
136
ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
137
if (cam_send_ccb(device, ccb) < 0) {
138
warn("error sending GET PHYSICAL ELEMENT STATUS command");
139
error = 1;
140
goto out;
141
}
142
143
if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
144
cam_error_print(device, ccb, CAM_ESF_ALL,
145
CAM_EPF_ALL, stderr);
146
error = 1;
147
}
148
149
out:
150
cam_freeccb(ccb);
151
return (error);
152
}
153
154
static int
155
depop_restore(struct cam_device *device, int task_attr, int retry_count,
156
int timeout, int verbosemode __unused)
157
{
158
union ccb *ccb;
159
int error = 0;
160
161
ccb = cam_getccb(device);
162
if (ccb == NULL) {
163
warnx("Can't allocate ccb");
164
return (1);
165
}
166
scsi_restore_elements_and_rebuild(&ccb->csio,
167
retry_count,
168
NULL,
169
task_attr,
170
SSD_FULL_SIZE,
171
timeout);
172
173
/* Disable freezing the device queue */
174
ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
175
if (cam_send_ccb(device, ccb) < 0) {
176
warn("error sending GET PHYSICAL ELEMENT STATUS command");
177
error = 1;
178
goto out;
179
}
180
181
if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
182
cam_error_print(device, ccb, CAM_ESF_ALL,
183
CAM_EPF_ALL, stderr);
184
error = 1;
185
}
186
187
out:
188
cam_freeccb(ccb);
189
return (error);
190
}
191
192
#define MUST_BE_NONE() \
193
if (action != DEPOP_NONE) { \
194
warnx("Use only one of -d, -l, or -r"); \
195
error = 1; \
196
goto bailout; \
197
}
198
199
int
200
depop(struct cam_device *device, int argc, char **argv, char *combinedopt,
201
int task_attr, int retry_count, int timeout, int verbosemode)
202
{
203
int c;
204
int action = DEPOP_NONE;
205
char *endptr;
206
int error = 0;
207
uint32_t elem = 0;
208
uint64_t capacity = 0;
209
210
while ((c = getopt(argc, argv, combinedopt)) != -1) {
211
switch (c) {
212
case 'c':
213
capacity = strtoumax(optarg, &endptr, 0);
214
if (*endptr != '\0') {
215
warnx("Invalid capacity: %s", optarg);
216
error = 1;
217
goto bailout;
218
}
219
break;
220
case 'e':
221
elem = strtoul(optarg, &endptr, 0);
222
if (*endptr != '\0') {
223
warnx("Invalid element: %s", optarg);
224
error = 1;
225
goto bailout;
226
}
227
break;
228
case 'd':
229
MUST_BE_NONE();
230
action = DEPOP_REMOVE;
231
break;
232
case 'l':
233
MUST_BE_NONE();
234
action = DEPOP_LIST;
235
break;
236
case 'r':
237
MUST_BE_NONE();
238
action = DEPOP_RESTORE;
239
break;
240
default:
241
break;
242
}
243
}
244
245
/*
246
* Compute a sane timeout if none given. 5 seconds for the list command
247
* and whatever the block device characteristics VPD says for other
248
* depop commands. If there's no value in that field, default to 1
249
* day. Experience has shown that these operations take the better part
250
* of a day to complete, so a 1 day timeout default seems appropriate.
251
*/
252
if (timeout == 0 && action != DEPOP_NONE) {
253
if (action == DEPOP_LIST) {
254
timeout = 5 * 1000;
255
} else {
256
struct scsi_vpd_block_device_characteristics *bdc;
257
258
timeout = 24 * 60 * 60 * 1000; /* 1 day */
259
bdc = scsi_wrap_vpd_block_device_characteristics(device);
260
if (bdc != NULL) {
261
timeout = scsi_4btoul(bdc->depopulation_time);
262
}
263
free(bdc);
264
}
265
}
266
267
switch (action) {
268
case DEPOP_NONE:
269
warnx("Must specify one of -d, -l, or -r");
270
error = 1;
271
break;
272
case DEPOP_REMOVE:
273
if (elem == 0 && capacity == 0) {
274
warnx("Must specify at least one of -e and/or -c");
275
error = 1;
276
break;
277
}
278
error = depop_remove(device, task_attr, retry_count, timeout,
279
verbosemode, elem, capacity);
280
break;
281
case DEPOP_RESTORE:
282
error = depop_restore(device, task_attr, retry_count, timeout,
283
verbosemode);
284
break;
285
case DEPOP_LIST:
286
error = depop_list(device, task_attr, retry_count, timeout,
287
verbosemode);
288
break;
289
}
290
291
bailout:
292
293
return (error);
294
}
295
296