Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/patches/2230653/?format=api
{ "id": 2230653, "url": "http://patchwork.ozlabs.org/api/patches/2230653/?format=api", "web_url": "http://patchwork.ozlabs.org/project/linux-pci/patch/768409a2-0593-49bd-9065-e8b93c60d4ce@llnl.gov/", "project": { "id": 28, "url": "http://patchwork.ozlabs.org/api/projects/28/?format=api", "name": "Linux PCI development", "link_name": "linux-pci", "list_id": "linux-pci.vger.kernel.org", "list_email": "linux-pci@vger.kernel.org", "web_url": null, "scm_url": null, "webscm_url": null, "list_archive_url": "", "list_archive_url_format": "", "commit_url_format": "" }, "msgid": "<768409a2-0593-49bd-9065-e8b93c60d4ce@llnl.gov>", "list_archive_url": null, "date": "2026-04-29T23:22:55", "name": "[v8,RESEND] Introduce Cray ClusterStor E1000 NVMe slot LED driver", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "5a112287331b4ca4dd758c74661346257a2a3f27", "submitter": { "id": 89209, "url": "http://patchwork.ozlabs.org/api/people/89209/?format=api", "name": "Tony Hutter", "email": "hutter2@llnl.gov" }, "delegate": null, "mbox": "http://patchwork.ozlabs.org/project/linux-pci/patch/768409a2-0593-49bd-9065-e8b93c60d4ce@llnl.gov/mbox/", "series": [ { "id": 502166, "url": "http://patchwork.ozlabs.org/api/series/502166/?format=api", "web_url": "http://patchwork.ozlabs.org/project/linux-pci/list/?series=502166", "date": "2026-04-29T23:22:55", "name": "[v8,RESEND] Introduce Cray ClusterStor E1000 NVMe slot LED driver", "version": 8, "mbox": "http://patchwork.ozlabs.org/series/502166/mbox/" } ], "comments": "http://patchwork.ozlabs.org/api/patches/2230653/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/2230653/checks/", "tags": {}, "related": [], "headers": { "Return-Path": "\n <linux-pci+bounces-53433-incoming=patchwork.ozlabs.org@vger.kernel.org>", "X-Original-To": [ "incoming@patchwork.ozlabs.org", "linux-pci@vger.kernel.org" ], "Delivered-To": "patchwork-incoming@legolas.ozlabs.org", "Authentication-Results": [ "legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=llnl.gov header.i=@llnl.gov header.a=rsa-sha256\n header.s=02022021-podllnl header.b=ZLbmAoKP;\n\tdkim=pass (1024-bit key;\n unprotected) header.d=doellnl.onmicrosoft.com header.i=@doellnl.onmicrosoft.com\n header.a=rsa-sha256 header.s=selector1-doellnl-onmicrosoft-com\n header.b=C7vTFna5;\n\tdkim-atps=neutral", "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org\n (client-ip=2600:3c15:e001:75::12fc:5321; helo=sin.lore.kernel.org;\n envelope-from=linux-pci+bounces-53433-incoming=patchwork.ozlabs.org@vger.kernel.org;\n receiver=patchwork.ozlabs.org)", "smtp.subspace.kernel.org;\n\tdkim=pass (2048-bit key) header.d=llnl.gov header.i=@llnl.gov\n header.b=\"ZLbmAoKP\";\n\tdkim=pass (1024-bit key) header.d=doellnl.onmicrosoft.com\n header.i=@doellnl.onmicrosoft.com header.b=\"C7vTFna5\"", "smtp.subspace.kernel.org;\n arc=fail smtp.client-ip=67.231.155.129", "smtp.subspace.kernel.org;\n dmarc=pass (p=reject dis=none) header.from=llnl.gov", "smtp.subspace.kernel.org;\n spf=pass smtp.mailfrom=llnl.gov" ], "Received": [ "from sin.lore.kernel.org (sin.lore.kernel.org\n [IPv6:2600:3c15:e001:75::12fc:5321])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4g5YJy53fGz1yHZ\n\tfor <incoming@patchwork.ozlabs.org>; Thu, 30 Apr 2026 09:23:46 +1000 (AEST)", "from smtp.subspace.kernel.org (conduit.subspace.kernel.org\n [100.90.174.1])\n\tby sin.lore.kernel.org (Postfix) with ESMTP id 5DC3C3001046\n\tfor <incoming@patchwork.ozlabs.org>; Wed, 29 Apr 2026 23:23:43 +0000 (UTC)", "from localhost.localdomain (localhost.localdomain [127.0.0.1])\n\tby smtp.subspace.kernel.org (Postfix) with ESMTP id 34D4A39DBE1;\n\tWed, 29 Apr 2026 23:23:42 +0000 (UTC)", "from mx0f-00379502.gpphosted.com (mx0f-00379502.gpphosted.com\n [67.231.155.129])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))\n\t(No client certificate requested)\n\tby smtp.subspace.kernel.org (Postfix) with ESMTPS id D5C217478;\n\tWed, 29 Apr 2026 23:23:38 +0000 (UTC)", "from pps.filterd (m0422210.ppops.net [127.0.0.1])\n\tby mx0f-00379502.gpphosted.com (8.18.1.7/8.18.1.7) with ESMTP id\n 63TNMhBi001358;\n\tWed, 29 Apr 2026 16:22:59 -0700", "from bn8pr09cu001.outbound.protection.outlook.com\n (mail-eastus2azon11012036.outbound.protection.outlook.com [52.101.58.36])\n\tby mx0f-00379502.gpphosted.com (PPS) with ESMTPS id 4drvcq0ubn-1\n\t(version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT);\n\tWed, 29 Apr 2026 16:22:58 -0700 (PDT)", "from DS0PR09MB11477.namprd09.prod.outlook.com (2603:10b6:8:16c::14)\n by DM2PR09MB12297.namprd09.prod.outlook.com (2603:10b6:8:2cc::9) with\n Microsoft SMTP Server (version=TLS1_2,\n cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9870.18; Wed, 29 Apr\n 2026 23:22:57 +0000", "from DS0PR09MB11477.namprd09.prod.outlook.com\n ([fe80::e625:6b07:b409:f725]) by DS0PR09MB11477.namprd09.prod.outlook.com\n ([fe80::e625:6b07:b409:f725%3]) with mapi id 15.20.9870.020; Wed, 29 Apr 2026\n 23:22:56 +0000" ], "ARC-Seal": [ "i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116;\n\tt=1777505022; cv=fail;\n b=LgeOodTwgCKuRhzYY1kX01FS0ljQgX2Y0HmXngARI5XWai1PoA/mEo87UZO0JUxkgnnniYQ1yCw2idzuiUMKWpgtM7MaFWIkcAbaRk0bdSmmbkbGg/fG0u9113pAkAR8K+DPz4LDgiCEpDs2SspmO7VC9QHW4Mdl8zNeiMSt3r4=", "i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none;\n b=ML9E6e9LHBFpg4tEZeEVb1pgTdppOiDtHDnLfFH89I5Cx6VjyPSawfSeacKy5P6ciACIxAi/k5K9a6d3R+0AwM+jjlQCuGn9Vkmg8UfuR0DyIAQEO5PGeCrbg4kflOx+4hIhVLFMbtN+VmnwP1BowrDYURpyBLo8eBUZ0cghICxq5NF4BHG8VjhBqPVodl5mlvl4yrw/LJAvw75VfGGHmyPo+KK60h+kcVqqx2/NkTqyr7lgJPGkyRnbgxjxU2i3CHaLu9VLowODGydTTMpo0NZ8T286jiRDa33bn+JjBxwvHM92U5ybZ/Trra4fphMSSPlZqnW7XKTj4KltLh3B6g==" ], "ARC-Message-Signature": [ "i=2; a=rsa-sha256; d=subspace.kernel.org;\n\ts=arc-20240116; t=1777505022; c=relaxed/simple;\n\tbh=8exkbTQBBW11EBWOVkp9DAaxxSKtElP/7NbD7d5My4c=;\n\th=Message-ID:Date:To:Cc:From:Subject:Content-Type:MIME-Version;\n b=l7T5sbMdhYqfqNWODc0cymr9FJu287vURH3BXvGtWjRDnCbTRbA/v/tizWzuNIBBt9KMbH2JyUgAttrRRrBs1zNnXKKh5COON3Qv/R/L9rSnDkjMFK2uPF4pY4iPDf0m2omJe9pjLyHRSY/Awc87aTiLzOrDjfNinHidr9CFByk=", "i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com;\n s=arcselector10001;\n h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1;\n bh=yo12AYQLxcUe8+8I/YsR/5ii4LkhqWYwvuoFYRSWG1k=;\n b=BgKaof/ka9ISUZoI/SsR0j43lcqwaGvCPn1uABGUya3VdY+UJWsygdAWh4g78L77F49YbwyBwy4rJ2PS3o/+h9EiANa/kdR1D9kuzLc1ec3P5zRO2ctmd4mmZ499qg3LZGuqav4OMZV5LphsrbYMqSlpgXgsaIiuQ2NwRs3IRcdY1PF1qx2W7AmRW2kSoSXtlJUuhegsY0KDUIgs/4SnFF7ynlQ4FVoq59mMLkLmbmI+UQexybHU7RcKvB2SRyNRws1j2Gv5UTva13MU0SZV5bgQhvxFzCwy59pEejyHpLU/vZ76mdNBFBRujmf0HI9UpjExDVhELEaYVOl2uLisLw==" ], "ARC-Authentication-Results": [ "i=2; smtp.subspace.kernel.org;\n dmarc=pass (p=reject dis=none) header.from=llnl.gov;\n spf=pass smtp.mailfrom=llnl.gov;\n dkim=pass (2048-bit key) header.d=llnl.gov header.i=@llnl.gov\n header.b=ZLbmAoKP;\n dkim=pass (1024-bit key) header.d=doellnl.onmicrosoft.com header.i=@doellnl.onmicrosoft.com\n header.b=C7vTFna5; arc=fail smtp.client-ip=67.231.155.129", "i=1; mx.microsoft.com 1; spf=pass\n smtp.mailfrom=llnl.gov; dmarc=pass action=none header.from=llnl.gov;\n dkim=pass header.d=llnl.gov; arc=none" ], "DKIM-Signature": [ "v=1; a=rsa-sha256; c=relaxed/relaxed; d=llnl.gov; h=cc\n\t:content-transfer-encoding:content-type:date:from:message-id\n\t:mime-version:subject:to; s=02022021-podllnl; bh=yo12AYQLxcUe8+8\n\tI/YsR/5ii4LkhqWYwvuoFYRSWG1k=; b=ZLbmAoKPWTE4VaDs3hUyAdY8uqUC0OF\n\tFJTOM7zV5I9FTM+7773nvXEB4inNX+G5oQ2jvUjfx0yhWt2rvWcLMPEjneI/9pY1\n\t01QuL7eO3c2kOyJSw2tOz4cQSyoxcscEdYOIi135NrKHgHtxjEdqP8u+JNN3ws5M\n\tSoxhnoF78D8f1I3M3TtRDRyiP/4A5HMGKy7Bp9V7sFWBNXsinCdLGmehvb++fPvD\n\tXUslxcNlgUIa9nYmU42fAhQ8TzONQ5xtDfkJVbqyQM4fOWQBlz7XFcP5Nt73T4Rw\n\tq3uAYY6YZ4pDaDHPSCYbSEImRjmw9rWW15AdAxeJrTrua+Y5nsiheCw==", "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=doellnl.onmicrosoft.com; s=selector1-doellnl-onmicrosoft-com;\n h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck;\n bh=yo12AYQLxcUe8+8I/YsR/5ii4LkhqWYwvuoFYRSWG1k=;\n b=C7vTFna5T/saRRBjSDHJX0sjHI8LU6/KvU1agMXK93vAiBGieTDQeQBHWp7ZWuDi8ekngniaeTq9TbT3PAz9oPxn6Aqr1i9hfY673Grhr//yO9pBVddfCKCYxAlL6FBbACiBf7/NsMc9TrMNXiyWptSSHCrjaQ7Spj+1kKSZ9RY=" ], "Message-ID": "<768409a2-0593-49bd-9065-e8b93c60d4ce@llnl.gov>", "Date": "Wed, 29 Apr 2026 16:22:55 -0700", "User-Agent": "Mozilla Thunderbird", "Content-Language": "en-US", "To": "Lukas Wunner <lukas@wunner.de>, Bjorn Helgaas <helgaas@kernel.org>", "Cc": "corey@minyard.net, alok.a.tiwari@oracle.com,\n mariusz.tkaczyk@linux.intel.com, minyard@acm.org,\n linux-pci@vger.kernel.org, openipmi-developer@lists.sourceforge.net,\n Linux Kernel Mailing List <linux-kernel@vger.kernel.org>", "From": "Tony Hutter <hutter2@llnl.gov>", "Subject": "[PATCH v8 RESEND] Introduce Cray ClusterStor E1000 NVMe slot LED\n driver", "Content-Type": "text/plain; charset=UTF-8", "Content-Transfer-Encoding": "7bit", "X-ClientProxiedBy": "BY1P220CA0010.NAMP220.PROD.OUTLOOK.COM\n (2603:10b6:a03:59d::12) To DS0PR09MB11477.namprd09.prod.outlook.com\n (2603:10b6:8:16c::14)", "Precedence": "bulk", "X-Mailing-List": "linux-pci@vger.kernel.org", "List-Id": "<linux-pci.vger.kernel.org>", "List-Subscribe": "<mailto:linux-pci+subscribe@vger.kernel.org>", "List-Unsubscribe": "<mailto:linux-pci+unsubscribe@vger.kernel.org>", "MIME-Version": "1.0", "X-MS-PublicTrafficType": "Email", "X-MS-TrafficTypeDiagnostic": "DS0PR09MB11477:EE_|DM2PR09MB12297:EE_", "X-MS-Office365-Filtering-Correlation-Id": "2c0f6e8f-f172-4855-9655-08dea6463ee4", "X-MS-Exchange-SenderADCheck": "1", "X-MS-Exchange-AntiSpam-Relay": "0", "X-Microsoft-Antispam": "\n\tBCL:0;ARA:13230040|19092799006|1800799024|366016|56012099003|18002099003;", "X-Microsoft-Antispam-Message-Info": "\n\tj2H/Z0sbNZDMMjl/1ReTuQ1ltBH3kVJ0tM0XUORdVz3GWowLeMGC9y8dbb6OHPUSmtME8dpwL1u0iBo0wTEvZ6VZ2PyYrmTeGwEACtD3eJzmp5dbq8iRV4U0VEHb0cm6ZyY3AH+JX8/cf/5Tq52WirV+x8Z4B2rRH97uX9f4A7vDe+Jc8bewhSugTUDd7Ns6XUPOmTo52cbj2vAgDlokPVViSslOySwkrqXMCQ3Z1AR0bjj690gK0tq24IIS44z1l9L20og1K9EwUYfTmbLtpwaQWbTxOCLyoy/UD/GQjyoxf/lvw8+93bLZh3+0ilkRd/6UtX1Q7t8uSTaRum+rd43yDU9XJVu96/ydskRnXcADrRbt4ITeZ0Nz9cVlnSdLLSmdQ62kex1OsVaIlsoXI8QRhBewqQ1QXus8sPR5vqhI5/TNk5NBV0uitu46uDq7mVXhSJfg4eTqmc4QJ8sPfNNi1rEjVXYx8eYkh50geTspLLgz45PE4kjKuI34kEw/LnlmmdCEkV+liD5rlMQdSHnoqjrLwMz+484c/ahTXSGAJnHlLPdxI5p5aIZDwjsgdm+imhPPcz8D/2TI1cn6w5wJ6lEOCoRFVCL7WS1jsDk9oswESn5GAJC5rpVnHi6tujhM4aGm1uJdy7WKN9OLjev8U3JuKhWlrqYrUb02lGQ1I3hC71b90MVCfwJTf8YJ0mkA9P0DStTjvcfmxHUHcSK59PIMk8CFSRCOf725O0Y=", "X-Forefront-Antispam-Report": "\n\tCIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:DS0PR09MB11477.namprd09.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(19092799006)(1800799024)(366016)(56012099003)(18002099003);DIR:OUT;SFP:1101;", "X-MS-Exchange-AntiSpam-MessageData-ChunkCount": "1", "X-MS-Exchange-AntiSpam-MessageData-0": "=?utf-8?q?7sBDD8+hJfSnZL3kmtEZVSsIWRkj?=\n\t=?utf-8?q?NA34MT+nqxfzuJcC6BLDcbhTzjJkJ8P8P8XrSpJ/JjtMTVFvngqA1fR63wTLuWxQ1?=\n\t=?utf-8?q?pCZuH8iciy8EHZwj5vR69tMdGcJdTb6ri3DzrylgzzJpIhguJZlRM5bgT+1/UU5WY?=\n\t=?utf-8?q?uKW7KZ3enwhybpPQi7OgAQRsWCEj6dy/iEp+WKFyoU39Qie/Urey5pQXeBL/+2/94?=\n\t=?utf-8?q?Irr7xhMC923HNAs9C9vidoh7xAP6pliRWacdpGgVcMk7v5HUhhaO6JriOif+g5qZ/?=\n\t=?utf-8?q?//21+lC4NzVvGYjOn5sbwrwwz065fTTI+whkDb1YmtIdot6dJETymVcMYb2gfR30J?=\n\t=?utf-8?q?BQT87S3Zaa3VzgHKc+6HYrRXqH9V3M1hY3LcmNBHnyvPpeg6mXziKaHMW4ePjVe9z?=\n\t=?utf-8?q?xELGDtLuiogSgxi48Yj8betCTICxg/Q/ppXXIGgztCBqoH8ZIpR2dg60bA9ss+JYE?=\n\t=?utf-8?q?pUEj0XTY+t4HqRGYe7tkz1zWpvBa7IsppZN9VNf43PjfL64zidIuB8uCjY3QSCiOb?=\n\t=?utf-8?q?KZi8DzQNT4GMfZK0+JAkUJJV8CVh/9nzl6E0qN+SEFaOt21oLUwkGVs8hrDeE+Jdy?=\n\t=?utf-8?q?k2GpohrsKzNmR4TxFOMxkB3GRmfuHVHStpfyfPurgwy+NpQ85PmarB7q3OiC5ndOB?=\n\t=?utf-8?q?n9h8imvgzmFT/i+kZr1AZfCQF/Uc31cW3s/XY6G3H4Ci4v3PJaQOi8tYx2lR9dgPR?=\n\t=?utf-8?q?768kBx74bMeslsTS8aOLKHQITplqtun6mXFJcV53oQFQR+496puSVqryQubsGa2ym?=\n\t=?utf-8?q?3Ed+yIXWxnOhmDBfl14/70Tk7RqUl7woZMFUncb6a2ql/9dKbY3E15V20WVjVpuux?=\n\t=?utf-8?q?e8UNC4QpT/H994Hr1SxuV3D7n3OQDaIFWdJK7TJ2QmuqPT/YRclkD+2NrnbvclcYL?=\n\t=?utf-8?q?RAkx+aDi9JniXTIJVftun5s5Afky/vdUPAPzZh9RgqDApOHsi3KpjuXICwpGseh5V?=\n\t=?utf-8?q?tDNgLsnpnMYnSFYiX5/CRv1viM1LVKdX5Tzvjl3UuX6Lvz2Jfwg9gpJBhCWXrnnks?=\n\t=?utf-8?q?KJB/rL/G0PBZD2Ts8OKKQWssGZrhcCFDXthyiK9S20SD5AUEJQp+lUYoMeQZCv9NK?=\n\t=?utf-8?q?DFxhn413QdyBaF02/EnlU1DAsar/mUcatb5aKfRmCnjxdY6g65KTxapU6k4G3NLDG?=\n\t=?utf-8?q?+bC7YS9QJsgtTTt8pz1MSHynsYtPkUONt5OfIWnW44qii+NTGf8Ix+8dF2r9HMNnW?=\n\t=?utf-8?q?M/xUqo2Yzc6wgrKMzwohdqamLUPjx0D618KPwi1GUKZhjqv08rYR1JWJS3KsIlMpb?=\n\t=?utf-8?q?/JPZL5ThFq420qeEFkkTNJKRbnFgXIgO0zy2zBRgQDx3tabTRSJNkikMZouNxEraM?=\n\t=?utf-8?q?FN5npKfxLrjtrDuGPGSoRNuXqJHpvZ2TW2ZXMhdEhMhjFW/ts5Uk9I1ZwqkjVokbJ?=\n\t=?utf-8?q?BR0llwPpbaIoDTrt7x7cITQCeKbkE9vwgiVMIA0AizSFHRjPICGHYj7aw+FEH+cQ1?=\n\t=?utf-8?q?9vZ4XWVY8MN0+WCfEBScZnKGnc4HwAdOUvGG1z1kwZvPMWTPc/JhAY0FEUxkXl2LB?=\n\t=?utf-8?q?+Yxc1tOqIwCHVUZLMLWj5srSuyoR5UtyXMf2jHABmwIkTLrGuchhtk33eblU34ZMI?=\n\t=?utf-8?q?4P/uRAN3cAXd4+6lNMbLmiZpjjjbcc5uNiNzlPZg0LG2CGHBplZELP/t/rSr+r0PT?=\n\t=?utf-8?q?P/EBWaTUfAm0HaSkyKYvVLslMmYcAUDA=3D=3D?=", "X-Exchange-RoutingPolicyChecked": "\n\tnzDwZFsIiCqJsjU8EyN35QFSrBWYXu/pMMD8Uje0H63QMXugbNGrSL2UaI3EDeiouHJAtmGbtdTgSgNA7qxWEFxKJNIQb9hNk12KwIzUGZL4izVsCuwc1E/1kx+nrryjxqHW0bfpfEpfoinRs+g1xH1BI7hvW6s1ki4aTFmSjSzvp2odCQEgPJXIQ8Ic2eY3ECdDltobyZAus5L90HOqLU4eJiSasm2QRYZSxZ8B5rK8iCKSNy5506yRwt3LbiSROwwuqRXkmS04OpsKYDnVVObt3MbWVRINt8BkmahRKuTYLbdbnvI7IgPw9mDY8HUj6+JEUYcJ/iv8BSy5ghXiyQ==", "X-MS-Exchange-AntiSpam-ExternalHop-MessageData-ChunkCount": "1", "X-MS-Exchange-AntiSpam-ExternalHop-MessageData-0": "\n\tIz76MBQoF4sb/l2W4Dwwu5qorL5qLvGOfiQtYphT3jllottC+krstcWNrruG1e+HawvJRGJYcfYv+v2RQbcHNRNuApzrNrXb6SgsuWroafbDjJdQsx3WgEw8FdlgZA/KAh8gPe8DeIcjZnOdmMnJbyIxtgCZhmm4vS5Co3+ZJfQrWSaOWXuguB/EuINdbF7kD8+02PklyBjkhyIHnbrIZbkZra0G70RQ6zmfpYBl0F9snS3Rdxmn5Um589wu6sR3H3Q7bJJhv02s6k7AB3tAMhcikvQoUBFycsV/kRlPBYuE0S4cxfWD7fYCBf+wo15ZGPPuPMmUjME3wS5ZcaGE7xInyiVEmS+NiSFB7gk0dQ6ke2FRtW8PwrkUnYnD4zXFcEgIWSW7SDE04xJ57UEuHJ+fWob91MATWMerUKc8vdfee1tST5nu12n2Fr72pBuAEKEDaxxnhbYCr59V24J3tJ596sH+iOtc1InFjijvytpszEN36x25RsiBiNR+oehaWzSLAqKuGFbw92jzNI1HEjGZu9A5nw4Yx6mIi2hw3FWEeRbsyOSmT5MR24XuzNjgkhmpp4IBiqJbQ6n6qAXgkQ5hBrBf0PprHkgLSeAXY0+XHVomTiFwobYO5/wRNNY723C6VLUjssKR7JdJi48JWQ==", "X-OriginatorOrg": "llnl.gov", "X-MS-Exchange-CrossTenant-Network-Message-Id": "\n 2c0f6e8f-f172-4855-9655-08dea6463ee4", "X-MS-Exchange-CrossTenant-AuthSource": "DS0PR09MB11477.namprd09.prod.outlook.com", "X-MS-Exchange-CrossTenant-AuthAs": "Internal", "X-MS-Exchange-CrossTenant-OriginalArrivalTime": "29 Apr 2026 23:22:56.8408\n (UTC)", "X-MS-Exchange-CrossTenant-FromEntityHeader": "Hosted", "X-MS-Exchange-CrossTenant-Id": "a722dec9-ae4e-4ae3-9d75-fd66e2680a63", "X-MS-Exchange-Transport-CrossTenantHeadersStamped": "DM2PR09MB12297", "X-Proofpoint-Spam-Details-Enc": "AW1haW4tMjYwNDI5MDIzNSBTYWx0ZWRfXwtCgWCSyUutP\n aHQl3KX+X8hWCT4uY/+wxSNd1cW/rT/MyiK8OtvXL6wt3EgZGooqVKQgIeccDBBbEmXi6RyKMfK\n 9RLhglzUTNAeTiedmt4aAc06gTCgLqcenqFfRR9ykcFapVVoVDbt/JjZTDC84upFqoEMOCoI+oq\n OBBYY2glETbPJpq2u6Q2s3eVmbEklqXSgmEy3ZYc5HYZ8C21QCq5SKHSbOsIC7dj1VKcIQIHK29\n enc4BVH5RZg48rVylZrq386ImzEIdcoaNiHFENzXE5m/+trP/JkunkMS4e6YXggYkARvF11DiiJ\n hDWTSSWEb5diPY5bltFd9qc1l1wu9nefM6eQ1cwH4NLQksgq7uoJrmR5TgFxHp845NTTq9u5j/d\n jtqJIgVDplULu39PwYIGHt4aUfmTAcdqqFT22Fpj2rcL8E0W5+fbvcx/S305LkURJFVdxEfHsON\n t5rBrQBkI0jmaJIRRbw==", "X-Authority-Analysis": "v=2.4 cv=OcuVzxTY c=1 sm=1 tr=0 ts=69f292d3 cx=c_pps\n a=ir8HpCZramD4IlRBrlbw0Q==:117 a=6eWqkTHjU83fiwn7nKZWdM+Sl24=:19\n a=z/mQ4Ysz8XfWz/Q5cLBRGdckG28=:19 a=lCpzRmAYbLLaTzLvsPZ7Mbvzbb8=:19\n a=xqWC_Br6kY4A:10 a=IkcTkHD0fZMA:10 a=A5OVakUREuEA:10 a=Ul2ihcTNv6sA:10\n a=VkNPw1HP01LnGYTKEx00:22 a=zvN32FZK4hqnROEWhWPN:22 a=wlpGG4-9GgZF_kJ3c3mt:22\n a=VwQbUJbxAAAA:8 a=0QafeX8dEaX0NvCQ6LkA:9 a=QEXdDO2ut3YA:10", "X-Proofpoint-ORIG-GUID": "41nOigBU1byyBD5eR3CKV7YvaMI3Q5T2", "X-Proofpoint-GUID": "41nOigBU1byyBD5eR3CKV7YvaMI3Q5T2", "X-Proofpoint-Virus-Version": "vendor=baseguard\n engine=ICAP:2.0.293,Aquarius:18.0.1143,Hydra:6.1.51,FMLib:17.12.100.49\n definitions=2026-04-29_02,2026-04-28_01,2025-10-01_01", "X-Proofpoint-Spam-Details": "rule=outbound_notspam policy=outbound score=0\n spamscore=0 impostorscore=0 malwarescore=0 lowpriorityscore=0 adultscore=0\n suspectscore=0 bulkscore=0 phishscore=0 priorityscore=1501 clxscore=1011\n classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0\n reason=mlx scancount=1 engine=8.19.0-2604200000 definitions=main-2604290235" }, "content": "Add driver to control the NVMe slot LEDs on the Cray ClusterStor E1000.\nThe driver provides hotplug attention status callbacks for the 24 NVMe\nslots on the E1000. This allows users to access the E1000's locate and\nfault LEDs via the normal /sys/bus/pci/slots/<slot>/attention sysfs\nentries. This driver uses IPMI to communicate with the E1000 controller\nto toggle the LEDs.\n\nSigned-off-by: Tony Hutter <hutter2@llnl.gov>\n---\nChanges in v8:\n - Remove unused variable in craye1k_get_attention_status().\n\nChanges in v7:\n - Update sysfs-bus-pci text from feedback.\n - Add DMI dependency to Kconfig.\n - Refactor pciehp_core.c to remove CONFIG_HOTPLUG_PCI_PCIE_CRAY_E1000\n code blocks.\n - Move errno.h #include into correct alphabetical order.\n - Add comment describing the reasoning for the debugfs counters.\n - Move craye1k_init() call from pcie_hp_init() to init_slot().\n - Make craye1k mutex global rather than in craye1k->lock. This enables\n handling of craye1k_[get|set]_attention_status() calls before the craye1k\n driver is initialized.\n - Do driver cleanup on craye1k_smi_gone().\n\nChanges in v6:\n - Change some dev_info_ratelimited() calls to dev_info().\n - Don't call craye1k_init() if pcie_port_service_register() fails.\n - Fix stray indent in #define CRAYE1K_POST_CMD_WAIT_MS\n\nChanges in v5:\n - Removed unnecessary ipmi_smi.h #include.\n - Added WARN_ON() to craye1k_do_message() to sanity check that craye1k->lock\n is held.\n - Added additional comments for when craye1k->lock should be held.\n\nChanges in v4:\n - Fix typo in Kconfig: \"is it\" -> \"it is\"\n - Rename some #defines to CRAYE1K_SUBCMD_*\n - Use IS_ERR() check in craye1k_debugfs_init()\n - Return -EIO instead of -EINVAL when LED value check fails\n\nChanges in v3:\n - Add 'attention' values in Documentation/ABI/testing/sysfs-bus-pci.\n - Remove ACPI_PCI_SLOT dependency.\n - Cleanup craye1k_do_message() error checking.\n - Skip unneeded memcpy() on failure in __craye1k_do_command().\n - Merge craye1k_do_command_and_netfn() code into craye1k_do_command().\n - Make craye1k_is_primary() return boolean.\n - Return negative error code on failure in craye1k_set_primary().\n\nChanges in v2:\n - Integrated E1000 code into the pciehp driver as an built-in\n extention rather than as a standalone module.\n - Moved debug tunables and counters to debugfs.\n - Removed forward declarations.\n - Kept the /sys/bus/pci/slots/<slot>/attention interface rather\n than using NPEM/_DSM or led_classdev as suggested. The \"attention\"\n interface is more beneficial for our site, since it allows us to\n control the NVMe slot LEDs agnostically across different enclosure\n vendors and kernel versions using the same scripts. It is also\n nice to use the same /sys/bus/pci/slots/<slot>/ sysfs directory for\n both slot LED toggling (\"attention\") and slot power control\n (\"power\").\n---\n Documentation/ABI/testing/sysfs-bus-pci | 21 +\n MAINTAINERS | 5 +\n drivers/pci/hotplug/Kconfig | 10 +\n drivers/pci/hotplug/Makefile | 3 +\n drivers/pci/hotplug/pciehp.h | 20 +\n drivers/pci/hotplug/pciehp_core.c | 20 +-\n drivers/pci/hotplug/pciehp_craye1k.c | 687 ++++++++++++++++++++++++\n 7 files changed, 765 insertions(+), 1 deletion(-)\n create mode 100644 drivers/pci/hotplug/pciehp_craye1k.c", "diff": "diff --git a/Documentation/ABI/testing/sysfs-bus-pci b/Documentation/ABI/testing/sysfs-bus-pci\nindex 92debe879ffb..8536d2ff30d1 100644\n--- a/Documentation/ABI/testing/sysfs-bus-pci\n+++ b/Documentation/ABI/testing/sysfs-bus-pci\n@@ -231,6 +231,27 @@ Description:\n \t\t - scXX contains the device subclass;\n \t\t - iXX contains the device class programming interface.\n \n+What:\t\t/sys/bus/pci/slots/.../attention\n+Date:\t\tFebruary 2025\n+Contact:\tlinux-pci@vger.kernel.org\n+Description:\n+\t\tThe attention attribute is used to read or write the attention\n+\t\tstatus for an enclosure slot. This is often used to set the\n+\t\tslot LED value on a NVMe storage enclosure.\n+\n+\t\tCommon values:\n+\t\t0 = OFF\n+\t\t1 = ON\n+\t\t2 = blink\n+\n+\t\tUsing the Cray ClusterStor E1000 extensions:\n+\t\t0 = fault LED OFF, locate LED OFF\n+\t\t1 = fault LED ON, locate LED OFF\n+\t\t2 = fault LED OFF, locate LED ON\n+\t\t3 = fault LED ON, locate LED ON\n+\n+\t\tOther values are no-op, OFF, or ON depending on the driver.\n+\n What:\t\t/sys/bus/pci/slots/.../module\n Date:\t\tJune 2009\n Contact:\tlinux-pci@vger.kernel.org\ndiff --git a/MAINTAINERS b/MAINTAINERS\nindex 9ac254f4ec41..861576d60a36 100644\n--- a/MAINTAINERS\n+++ b/MAINTAINERS\n@@ -6543,6 +6543,11 @@ S:\tMaintained\n F:\tDocumentation/filesystems/cramfs.rst\n F:\tfs/cramfs/\n \n+CRAY CLUSTERSTOR E1000 NVME SLOT LED DRIVER EXTENSIONS\n+M:\tTony Hutter <hutter2@llnl.gov>\n+S:\tMaintained\n+F:\tdrivers/pci/hotplug/pciehp_craye1k.c\n+\n CRC LIBRARY\n M:\tEric Biggers <ebiggers@kernel.org>\n R:\tArd Biesheuvel <ardb@kernel.org>\ndiff --git a/drivers/pci/hotplug/Kconfig b/drivers/pci/hotplug/Kconfig\nindex 3207860b52e4..3cb84e5e13e9 100644\n--- a/drivers/pci/hotplug/Kconfig\n+++ b/drivers/pci/hotplug/Kconfig\n@@ -183,4 +183,14 @@ config HOTPLUG_PCI_S390\n \n \t When in doubt, say Y.\n \n+config HOTPLUG_PCI_PCIE_CRAY_E1000\n+\tbool \"PCIe Hotplug extensions for Cray ClusterStor E1000\"\n+\tdepends on DMI && HOTPLUG_PCI_PCIE && IPMI_HANDLER=y\n+\thelp\n+\t Say Y here if you have a Cray ClusterStor E1000 and want to control\n+\t your NVMe slot LEDs. Without this driver it is not possible\n+\t to control the fault and locate LEDs on the E1000's 24 NVMe slots.\n+\n+\t When in doubt, say N.\n+\n endif # HOTPLUG_PCI\ndiff --git a/drivers/pci/hotplug/Makefile b/drivers/pci/hotplug/Makefile\nindex 40aaf31fe338..82a1f0592d0a 100644\n--- a/drivers/pci/hotplug/Makefile\n+++ b/drivers/pci/hotplug/Makefile\n@@ -66,6 +66,9 @@ pciehp-objs\t\t:=\tpciehp_core.o\t\\\n \t\t\t\tpciehp_ctrl.o\t\\\n \t\t\t\tpciehp_pci.o\t\\\n \t\t\t\tpciehp_hpc.o\n+ifdef CONFIG_HOTPLUG_PCI_PCIE_CRAY_E1000\n+pciehp-objs\t\t+=\tpciehp_craye1k.o\n+endif\n \n shpchp-objs\t\t:=\tshpchp_core.o\t\\\n \t\t\t\tshpchp_ctrl.o\t\\\ndiff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h\nindex debc79b0adfb..3a8173f3e159 100644\n--- a/drivers/pci/hotplug/pciehp.h\n+++ b/drivers/pci/hotplug/pciehp.h\n@@ -199,6 +199,17 @@ int pciehp_get_raw_indicator_status(struct hotplug_slot *h_slot, u8 *status);\n \n int pciehp_slot_reset(struct pcie_device *dev);\n \n+#ifdef CONFIG_HOTPLUG_PCI_PCIE_CRAY_E1000\n+int craye1k_init(void);\n+bool is_craye1k_board(void);\n+int craye1k_get_attention_status(struct hotplug_slot *hotplug_slot, u8 *status);\n+int craye1k_set_attention_status(struct hotplug_slot *hotplug_slot, u8 status);\n+#else\n+#define craye1k_init() (0)\n+#define craye1k_get_attention_status NULL\n+#define craye1k_set_attention_status NULL\n+#endif\n+\n static inline const char *slot_name(struct controller *ctrl)\n {\n \treturn hotplug_slot_name(&ctrl->hotplug_slot);\n@@ -209,4 +220,13 @@ static inline struct controller *to_ctrl(struct hotplug_slot *hotplug_slot)\n \treturn container_of(hotplug_slot, struct controller, hotplug_slot);\n }\n \n+static inline bool is_craye1k_slot(struct controller *ctrl)\n+{\n+#ifdef CONFIG_HOTPLUG_PCI_PCIE_CRAY_E1000\n+\treturn (PSN(ctrl) >= 1 && PSN(ctrl) <= 24 && is_craye1k_board());\n+#else\n+\treturn false;\n+#endif\n+}\n+\n #endif\t\t\t\t/* _PCIEHP_H */\ndiff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c\nindex f59baa912970..3e8e2b3069bf 100644\n--- a/drivers/pci/hotplug/pciehp_core.c\n+++ b/drivers/pci/hotplug/pciehp_core.c\n@@ -72,6 +72,22 @@ static int init_slot(struct controller *ctrl)\n \t} else if (ctrl->pcie->port->hotplug_user_indicators) {\n \t\tops->get_attention_status = pciehp_get_raw_indicator_status;\n \t\tops->set_attention_status = pciehp_set_raw_indicator_status;\n+\t} else if (is_craye1k_slot(ctrl)) {\n+\t\t/*\n+\t\t * The Cray E1000 driver controls slots 1-24. Initialize the\n+\t\t * Cray E1000 driver when slot 1 is seen.\n+\t\t */\n+\t\tif (PSN(ctrl) == 1) {\n+\t\t\tretval = craye1k_init();\n+\t\t\tif (retval) {\n+\t\t\t\tctrl_err(ctrl,\n+\t\t\t\t\t \"Error loading Cray E1000 extensions\");\n+\t\t\t\tkfree(ops);\n+\t\t\t\treturn retval;\n+\t\t\t}\n+\t\t}\n+\t\tops->get_attention_status = craye1k_get_attention_status;\n+\t\tops->set_attention_status = craye1k_set_attention_status;\n \t}\n \n \t/* register this slot with the hotplug pci core */\n@@ -376,8 +392,10 @@ int __init pcie_hp_init(void)\n \n \tretval = pcie_port_service_register(&hpdriver_portdrv);\n \tpr_debug(\"pcie_port_service_register = %d\\n\", retval);\n-\tif (retval)\n+\tif (retval) {\n \t\tpr_debug(\"Failure to register service\\n\");\n+\t\treturn retval;\n+\t}\n \n \treturn retval;\n }\ndiff --git a/drivers/pci/hotplug/pciehp_craye1k.c b/drivers/pci/hotplug/pciehp_craye1k.c\nnew file mode 100644\nindex 000000000000..9c5bee81fdf8\n--- /dev/null\n+++ b/drivers/pci/hotplug/pciehp_craye1k.c\n@@ -0,0 +1,687 @@\n+// SPDX-License-Identifier: GPL-2.0\n+/*\n+ * Copyright 2022-2024 Lawrence Livermore National Security, LLC\n+ */\n+/*\n+ * Cray ClusterStor E1000 hotplug slot LED driver extensions\n+ *\n+ * This driver controls the NVMe slot LEDs on the Cray ClusterStore E1000.\n+ * It provides hotplug attention status callbacks for the 24 NVMe slots on\n+ * the E1000. This allows users to access the E1000's locate and fault\n+ * LEDs via the normal /sys/bus/pci/slots/<slot>/attention sysfs entries.\n+ * This driver uses IPMI to communicate with the E1000 controller to toggle\n+ * the LEDs.\n+ *\n+ * This driver is based off of ibmpex.c\n+ */\n+\n+#include <linux/debugfs.h>\n+#include <linux/delay.h>\n+#include <linux/dmi.h>\n+#include <linux/errno.h>\n+#include <linux/ipmi.h>\n+#include <linux/module.h>\n+#include <linux/pci.h>\n+#include <linux/pci_hotplug.h>\n+#include <linux/random.h>\n+#include \"pciehp.h\"\n+\n+/* Cray E1000 commands */\n+#define CRAYE1K_CMD_NETFN 0x3c\n+#define CRAYE1K_CMD_PRIMARY 0x33\n+#define CRAYE1K_CMD_FAULT_LED 0x39\n+#define CRAYE1K_CMD_LOCATE_LED 0x22\n+\n+/* Subcommands */\n+#define CRAYE1K_SUBCMD_GET_LED\t\t0x0\n+#define CRAYE1K_SUBCMD_SET_LED\t\t0x1\n+#define CRAYE1K_SUBCMD_SET_PRIMARY\t0x1\n+\n+/*\n+ * Milliseconds to wait after get/set LED command. 200ms value found though\n+ * experimentation\n+ */\n+#define CRAYE1K_POST_CMD_WAIT_MS\t200\n+\n+struct craye1k {\n+\tstruct device *dev; /* BMC device */\n+\tstruct mutex lock;\n+\tstruct completion read_complete;\n+\tstruct ipmi_addr address;\n+\tstruct ipmi_user *user;\n+\tint iface;\n+\n+\tlong tx_msg_id;\n+\tstruct kernel_ipmi_msg tx_msg;\n+\tunsigned char tx_msg_data[IPMI_MAX_MSG_LENGTH];\n+\tunsigned char rx_msg_data[IPMI_MAX_MSG_LENGTH];\n+\tunsigned long rx_msg_len;\n+\tunsigned char rx_result;\t/* IPMI completion code */\n+\n+\t/* Parent dir for all our debugfs entries */\n+\tstruct dentry *parent;\n+\n+\t/* debugfs stats */\n+\tu64 check_primary;\n+\tu64 check_primary_failed;\n+\tu64 was_already_primary;\n+\tu64 was_not_already_primary;\n+\tu64 set_primary;\n+\tu64 set_initial_primary_failed;\n+\tu64 set_primary_failed;\n+\tu64 set_led_locate_failed;\n+\tu64 set_led_fault_failed;\n+\tu64 set_led_readback_failed;\n+\tu64 set_led_failed;\n+\tu64 get_led_failed;\n+\tu64 completion_timeout;\n+\tu64 wrong_msgid;\n+\tu64 request_failed;\n+\n+\t/* debugfs configuration options */\n+\n+\t/* Print info on spurious IPMI messages */\n+\tbool print_errors;\n+\n+\t/* Retries for kernel IPMI layer */\n+\tu32 ipmi_retries;\n+\n+\t/* Timeout in ms for IPMI (0 = use IPMI default_retry_ms) */\n+\tu32 ipmi_timeout_ms;\n+\n+\t/* Timeout in ms to wait for E1000 message completion */\n+\tu32 completion_timeout_ms;\n+};\n+\n+/*\n+ * Make our craye1k a global so get/set_attention_status() can access it.\n+ * This is safe since there's only one node controller on the board, and so it's\n+ * impossible to instantiate more than one craye1k.\n+ */\n+static struct craye1k *craye1k_global;\n+static DEFINE_MUTEX(craye1k_lock);\n+\n+/*\n+ * The E1000 command timeout and retry values were found though experimentation\n+ * by looking at the error counters. Keep the counters around to troubleshoot\n+ * any issues with our current timeout/retry values.\n+ */\n+static struct dentry *\n+craye1k_debugfs_init(struct craye1k *craye1k)\n+{\n+\tumode_t mode = 0644;\n+\tstruct dentry *parent = debugfs_create_dir(\"pciehp_craye1k\", NULL);\n+\n+\tif (IS_ERR(parent))\n+\t\treturn NULL;\n+\n+\tdebugfs_create_x64(\"check_primary\", mode, parent,\n+\t\t\t &craye1k->check_primary);\n+\tdebugfs_create_x64(\"check_primary_failed\", mode, parent,\n+\t\t\t &craye1k->check_primary_failed);\n+\tdebugfs_create_x64(\"was_already_primary\", mode, parent,\n+\t\t\t &craye1k->was_already_primary);\n+\tdebugfs_create_x64(\"was_not_already_primary\", mode, parent,\n+\t\t\t &craye1k->was_not_already_primary);\n+\tdebugfs_create_x64(\"set_primary\", mode, parent,\n+\t\t\t &craye1k->set_primary);\n+\tdebugfs_create_x64(\"set_initial_primary_failed\", mode, parent,\n+\t\t\t &craye1k->set_initial_primary_failed);\n+\tdebugfs_create_x64(\"set_primary_failed\", mode, parent,\n+\t\t\t &craye1k->set_primary_failed);\n+\tdebugfs_create_x64(\"set_led_locate_failed\", mode, parent,\n+\t\t\t &craye1k->set_led_locate_failed);\n+\tdebugfs_create_x64(\"set_led_fault_failed\", mode, parent,\n+\t\t\t &craye1k->set_led_fault_failed);\n+\tdebugfs_create_x64(\"set_led_readback_failed\", mode, parent,\n+\t\t\t &craye1k->set_led_readback_failed);\n+\tdebugfs_create_x64(\"set_led_failed\", mode, parent,\n+\t\t\t &craye1k->set_led_failed);\n+\tdebugfs_create_x64(\"get_led_failed\", mode, parent,\n+\t\t\t &craye1k->get_led_failed);\n+\tdebugfs_create_x64(\"completion_timeout\", mode, parent,\n+\t\t\t &craye1k->completion_timeout);\n+\tdebugfs_create_x64(\"wrong_msgid\", mode, parent,\n+\t\t\t &craye1k->wrong_msgid);\n+\tdebugfs_create_x64(\"request_failed\", mode, parent,\n+\t\t\t &craye1k->request_failed);\n+\n+\tdebugfs_create_x32(\"ipmi_retries\", mode, parent,\n+\t\t\t &craye1k->ipmi_retries);\n+\tdebugfs_create_x32(\"ipmi_timeout_ms\", mode, parent,\n+\t\t\t &craye1k->ipmi_timeout_ms);\n+\tdebugfs_create_x32(\"completion_timeout_ms\", mode, parent,\n+\t\t\t &craye1k->completion_timeout_ms);\n+\tdebugfs_create_bool(\"print_errors\", mode, parent,\n+\t\t\t &craye1k->print_errors);\n+\n+\t/* Return parent dir dentry */\n+\treturn parent;\n+}\n+\n+/*\n+ * craye1k_msg_handler() - IPMI message response handler\n+ */\n+static void craye1k_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)\n+{\n+\tstruct craye1k *craye1k = user_msg_data;\n+\n+\tif (msg->msgid != craye1k->tx_msg_id) {\n+\t\tcraye1k->wrong_msgid++;\n+\t\tif (craye1k->print_errors) {\n+\t\t\tdev_warn_ratelimited(craye1k->dev,\n+\t\t\t\t\t \"rx msgid %ld != %ld\",\n+\t\t\t\t\t msg->msgid, craye1k->tx_msg_id);\n+\t\t}\n+\t\tipmi_free_recv_msg(msg);\n+\t\treturn;\n+\t}\n+\n+\t/* Set rx_result to the IPMI completion code */\n+\tif (msg->msg.data_len > 0)\n+\t\tcraye1k->rx_result = msg->msg.data[0];\n+\telse\n+\t\tcraye1k->rx_result = IPMI_UNKNOWN_ERR_COMPLETION_CODE;\n+\n+\tif (msg->msg.data_len > 1) {\n+\t\t/* Exclude completion code from data bytes */\n+\t\tcraye1k->rx_msg_len = msg->msg.data_len - 1;\n+\t\tmemcpy(craye1k->rx_msg_data, msg->msg.data + 1,\n+\t\t craye1k->rx_msg_len);\n+\t} else {\n+\t\tcraye1k->rx_msg_len = 0;\n+\t}\n+\n+\tipmi_free_recv_msg(msg);\n+\n+\tcomplete(&craye1k->read_complete);\n+}\n+\n+static const struct ipmi_user_hndl craye1k_user_hndl = {\n+\t.ipmi_recv_hndl = craye1k_msg_handler\n+};\n+\n+static void craye1k_new_smi(int iface, struct device *dev)\n+{\n+\tint rc;\n+\tstruct craye1k *craye1k;\n+\n+\tcraye1k = kzalloc(sizeof(*craye1k), GFP_KERNEL);\n+\tif (!craye1k)\n+\t\treturn;\n+\n+\tcraye1k->address.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;\n+\tcraye1k->address.channel = IPMI_BMC_CHANNEL;\n+\tcraye1k->iface = iface;\n+\tcraye1k->dev = dev;\n+\tcraye1k->tx_msg.data = craye1k->tx_msg_data;\n+\tcraye1k->ipmi_retries = 4;\n+\tcraye1k->ipmi_timeout_ms = 500;\n+\tcraye1k->completion_timeout_ms = 300;\n+\n+\tinit_completion(&craye1k->read_complete);\n+\n+\tdev_set_drvdata(dev, craye1k);\n+\n+\trc = ipmi_create_user(craye1k->iface, &craye1k_user_hndl, craye1k,\n+\t\t\t &craye1k->user);\n+\tif (rc < 0) {\n+\t\tdev_err(dev, \"Unable to register IPMI user, iface %d\\n\",\n+\t\t\tcraye1k->iface);\n+\t\tkfree(craye1k);\n+\t\tdev_set_drvdata(dev, NULL);\n+\t\treturn;\n+\t}\n+\n+\tmutex_lock(&craye1k_lock);\n+\n+\t/* There's only one node controller so driver data should not be set */\n+\tWARN_ON(craye1k_global);\n+\n+\tcraye1k_global = craye1k;\n+\tcraye1k->parent = craye1k_debugfs_init(craye1k);\n+\tmutex_unlock(&craye1k_lock);\n+\tif (!craye1k->parent)\n+\t\tdev_warn(dev, \"Cannot create debugfs\");\n+\n+\tdev_info(dev, \"Cray ClusterStor E1000 slot LEDs registered\");\n+}\n+\n+static void craye1k_smi_gone(int iface)\n+{\n+\tpr_warn(\"craye1k: Got unexpected smi_gone, iface=%d\", iface);\n+\n+\tmutex_lock(&craye1k_lock);\n+\tif (craye1k_global) {\n+\t\tdebugfs_remove_recursive(craye1k_global->parent);\n+\t\tkfree(craye1k_global);\n+\t\tcraye1k_global = NULL;\n+\t}\n+\tmutex_unlock(&craye1k_lock);\n+}\n+\n+static struct ipmi_smi_watcher craye1k_smi_watcher = {\n+\t.owner = THIS_MODULE,\n+\t.new_smi = craye1k_new_smi,\n+\t.smi_gone = craye1k_smi_gone\n+};\n+\n+/*\n+ * craye1k_send_message() - Send the message already setup in 'craye1k'\n+ *\n+ * Context: craye1k_lock is already held.\n+ * Return: 0 on success, non-zero on error.\n+ */\n+static int craye1k_send_message(struct craye1k *craye1k)\n+{\n+\tint rc;\n+\n+\trc = ipmi_validate_addr(&craye1k->address, sizeof(craye1k->address));\n+\tif (rc) {\n+\t\tdev_err_ratelimited(craye1k->dev, \"ipmi_validate_addr() = %d\\n\",\n+\t\t\t\t rc);\n+\t\treturn rc;\n+\t}\n+\n+\tcraye1k->tx_msg_id++;\n+\n+\trc = ipmi_request_settime(craye1k->user, &craye1k->address,\n+\t\t\t\t craye1k->tx_msg_id, &craye1k->tx_msg, craye1k,\n+\t\t\t\t 0, craye1k->ipmi_retries,\n+\t\t\t\t craye1k->ipmi_timeout_ms);\n+\n+\tif (rc) {\n+\t\tcraye1k->request_failed++;\n+\t\treturn rc;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * craye1k_do_message() - Send the message in 'craye1k' and wait for a response\n+ *\n+ * Context: craye1k_lock is already held.\n+ * Return: 0 on success, non-zero on error.\n+ */\n+static int craye1k_do_message(struct craye1k *craye1k)\n+{\n+\tint rc;\n+\tstruct completion *read_complete = &craye1k->read_complete;\n+\tunsigned long tout = msecs_to_jiffies(craye1k->completion_timeout_ms);\n+\n+\tWARN_ON(!mutex_is_locked(&craye1k_lock));\n+\n+\trc = craye1k_send_message(craye1k);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\trc = wait_for_completion_killable_timeout(read_complete, tout);\n+\tif (rc == 0) {\n+\t\t/* timed out */\n+\t\tcraye1k->completion_timeout++;\n+\t\treturn -ETIME;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * __craye1k_do_command() - Do an IPMI command\n+ *\n+ * Send a command with optional data bytes, and read back response bytes.\n+ *\n+ * Context: craye1k_lock is already held.\n+ * Returns: 0 on success, non-zero on error.\n+ */\n+static int __craye1k_do_command(struct craye1k *craye1k, u8 netfn, u8 cmd,\n+\t\t\t\tu8 *send_data, u8 send_data_len, u8 *recv_data,\n+\t\t\t\tu8 recv_data_len)\n+{\n+\tint rc;\n+\n+\tcraye1k->tx_msg.netfn = netfn;\n+\tcraye1k->tx_msg.cmd = cmd;\n+\n+\tif (send_data) {\n+\t\tmemcpy(&craye1k->tx_msg_data[0], send_data, send_data_len);\n+\t\tcraye1k->tx_msg.data_len = send_data_len;\n+\t} else {\n+\t\tcraye1k->tx_msg_data[0] = 0;\n+\t\tcraye1k->tx_msg.data_len = 0;\n+\t}\n+\n+\trc = craye1k_do_message(craye1k);\n+\tif (rc == 0)\n+\t\tmemcpy(recv_data, craye1k->rx_msg_data, recv_data_len);\n+\n+\treturn rc;\n+}\n+\n+/*\n+ * craye1k_do_command() - Do a Cray E1000 specific IPMI command.\n+ * @cmd: Cray E1000 specific command\n+ * @send_data: Data to send after the command\n+ * @send_data_len: Data length\n+ *\n+ * Context: craye1k_lock is already held.\n+ * Returns: the last byte from the response or 0 if response had no response\n+ * data bytes, else -1 on error.\n+ */\n+static int craye1k_do_command(struct craye1k *craye1k, u8 cmd, u8 *send_data,\n+\t\t\t u8 send_data_len)\n+{\n+\tint rc;\n+\n+\trc = __craye1k_do_command(craye1k, CRAYE1K_CMD_NETFN, cmd, send_data,\n+\t\t\t\t send_data_len, NULL, 0);\n+\tif (rc != 0) {\n+\t\t/* Error attempting command */\n+\t\treturn -1;\n+\t}\n+\n+\tif (craye1k->tx_msg.data_len == 0)\n+\t\treturn 0;\n+\n+\t/* Return last received byte value */\n+\treturn craye1k->rx_msg_data[craye1k->rx_msg_len - 1];\n+}\n+\n+/*\n+ * __craye1k_set_primary() - Tell the BMC we want to be the primary server\n+ *\n+ * An E1000 board has two physical servers on it. In order to set a slot\n+ * NVMe LED, this server needs to first tell the BMC that it's the primary\n+ * server.\n+ *\n+ * Context: craye1k_lock is already held.\n+ * Returns: 0 on success, non-zero on error.\n+ */\n+static int __craye1k_set_primary(struct craye1k *craye1k)\n+{\n+\tu8 bytes[2] = {CRAYE1K_SUBCMD_SET_PRIMARY, 1};\t/* set primary to 1 */\n+\n+\tcraye1k->set_primary++;\n+\treturn craye1k_do_command(craye1k, CRAYE1K_CMD_PRIMARY, bytes, 2);\n+}\n+\n+/*\n+ * craye1k_is_primary() - Are we the primary server?\n+ *\n+ * Context: craye1k_lock is already held.\n+ * Returns: true if we are the primary server, false otherwise.\n+ */\n+static bool craye1k_is_primary(struct craye1k *craye1k)\n+{\n+\tu8 byte = 0;\n+\tint rc;\n+\n+\t/* Response byte is 0x1 on success */\n+\trc = craye1k_do_command(craye1k, CRAYE1K_CMD_PRIMARY, &byte, 1);\n+\tcraye1k->check_primary++;\n+\tif (rc == 0x1)\n+\t\treturn true; /* success */\n+\n+\tcraye1k->check_primary_failed++;\n+\treturn false; /* We are not the primary server node */\n+}\n+\n+/*\n+ * craye1k_set_primary() - Attempt to set ourselves as the primary server\n+ *\n+ * Context: craye1k_lock is already held.\n+ * Returns: 0 on success, -1 otherwise.\n+ */\n+static int craye1k_set_primary(struct craye1k *craye1k)\n+{\n+\tint tries = 10;\n+\n+\tif (craye1k_is_primary(craye1k)) {\n+\t\tcraye1k->was_already_primary++;\n+\t\treturn 0;\n+\t}\n+\tcraye1k->was_not_already_primary++;\n+\n+\t/* delay found through experimentation */\n+\tmsleep(300);\n+\n+\tif (__craye1k_set_primary(craye1k) != 0) {\n+\t\tcraye1k->set_initial_primary_failed++;\n+\t\treturn -1;\t/* error */\n+\t}\n+\n+\t/*\n+\t * It can take 2 to 3 seconds after setting primary for the controller\n+\t * to report that it is the primary.\n+\t */\n+\twhile (tries--) {\n+\t\tmsleep(500);\n+\t\tif (craye1k_is_primary(craye1k))\n+\t\t\tbreak;\n+\t}\n+\n+\tif (tries == 0) {\n+\t\tcraye1k->set_primary_failed++;\n+\t\treturn -1;\t/* never reported that it's primary */\n+\t}\n+\n+\t/* Wait for primary switch to finish */\n+\tmsleep(1500);\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * craye1k_get_slot_led() - Get slot LED value\n+ * @slot: Slot number (1-24)\n+ * @is_locate_led: 0 = get fault LED value, 1 = get locate LED value\n+ *\n+ * Context: craye1k_lock is already held.\n+ * Returns: slot value on success, -1 on failure.\n+ */\n+static int craye1k_get_slot_led(struct craye1k *craye1k, unsigned char slot,\n+\t\t\t\tbool is_locate_led)\n+{\n+\tu8 bytes[2];\n+\tu8 cmd;\n+\n+\tbytes[0] = CRAYE1K_SUBCMD_GET_LED;\n+\tbytes[1] = slot;\n+\n+\tcmd = is_locate_led ? CRAYE1K_CMD_LOCATE_LED : CRAYE1K_CMD_FAULT_LED;\n+\n+\treturn craye1k_do_command(craye1k, cmd, bytes, 2);\n+}\n+\n+/*\n+ * craye1k_set_slot_led() - Attempt to set the locate/fault LED to a value\n+ * @slot: Slot number (1-24)\n+ * @is_locate_led: 0 = use fault LED, 1 = use locate LED\n+ * @value: Value to set (0 or 1)\n+ *\n+ * Check the LED value after calling this function to ensure it has been set\n+ * properly.\n+ *\n+ * Context: craye1k_lock is already held.\n+ * Returns: 0 on success, non-zero on failure.\n+ */\n+static int craye1k_set_slot_led(struct craye1k *craye1k, unsigned char slot,\n+\t\t\t\tunsigned char is_locate_led,\n+\t\t\t\tunsigned char value)\n+{\n+\tu8 bytes[3];\n+\tu8 cmd;\n+\n+\tbytes[0] = CRAYE1K_SUBCMD_SET_LED;\n+\tbytes[1] = slot;\n+\tbytes[2] = value;\n+\n+\tcmd = is_locate_led ? CRAYE1K_CMD_LOCATE_LED : CRAYE1K_CMD_FAULT_LED;\n+\n+\treturn craye1k_do_command(craye1k, cmd, bytes, 3);\n+}\n+\n+/*\n+ * __craye1k_get_attention_status() - Get LED value\n+ *\n+ * Context: craye1k_lock is already held.\n+ * Returns: 0 on success, -EIO on failure.\n+ */\n+static int __craye1k_get_attention_status(struct hotplug_slot *hotplug_slot,\n+\t\t\t\t\t u8 *status, bool set_primary)\n+{\n+\tunsigned char slot;\n+\tint locate, fault;\n+\tstruct craye1k *craye1k;\n+\n+\tcraye1k = craye1k_global;\n+\tslot = PSN(to_ctrl(hotplug_slot));\n+\n+\tif (set_primary) {\n+\t\tif (craye1k_set_primary(craye1k) != 0) {\n+\t\t\tcraye1k->get_led_failed++;\n+\t\t\treturn -EIO;\n+\t\t}\n+\t}\n+\n+\tlocate = craye1k_get_slot_led(craye1k, slot, true);\n+\tif (locate == -1) {\n+\t\tcraye1k->get_led_failed++;\n+\t\treturn -EIO;\n+\t}\n+\tmsleep(CRAYE1K_POST_CMD_WAIT_MS);\n+\n+\tfault = craye1k_get_slot_led(craye1k, slot, false);\n+\tif (fault == -1) {\n+\t\tcraye1k->get_led_failed++;\n+\t\treturn -EIO;\n+\t}\n+\tmsleep(CRAYE1K_POST_CMD_WAIT_MS);\n+\n+\t*status = locate << 1 | fault;\n+\n+\treturn 0;\n+}\n+\n+int craye1k_get_attention_status(struct hotplug_slot *hotplug_slot,\n+\t\t\t\t u8 *status)\n+{\n+\tint rc;\n+\n+\tif (mutex_lock_interruptible(&craye1k_lock) != 0)\n+\t\treturn -EINTR;\n+\n+\tif (!craye1k_global) {\n+\t\t/* Driver isn't initialized yet */\n+\t\tmutex_unlock(&craye1k_lock);\n+\t\treturn -EOPNOTSUPP;\n+\t}\n+\n+\trc = __craye1k_get_attention_status(hotplug_slot, status, true);\n+\n+\tmutex_unlock(&craye1k_lock);\n+\treturn rc;\n+}\n+\n+int craye1k_set_attention_status(struct hotplug_slot *hotplug_slot,\n+\t\t\t\t u8 status)\n+{\n+\tunsigned char slot;\n+\tint tries = 4;\n+\tint rc;\n+\tu8 new_status;\n+\tstruct craye1k *craye1k;\n+\tbool locate, fault;\n+\n+\tif (mutex_lock_interruptible(&craye1k_lock) != 0)\n+\t\treturn -EINTR;\n+\n+\tif (!craye1k_global) {\n+\t\t/* Driver isn't initialized yet */\n+\t\tmutex_unlock(&craye1k_lock);\n+\t\treturn -EOPNOTSUPP;\n+\t}\n+\n+\tcraye1k = craye1k_global;\n+\n+\tslot = PSN(to_ctrl(hotplug_slot));\n+\n+\t/* Retry to ensure all LEDs are set */\n+\twhile (tries--) {\n+\t\t/*\n+\t\t * The node must first set itself to be the primary node before\n+\t\t * setting the slot LEDs (each board has two nodes, or\n+\t\t * \"servers\" as they're called by the manufacturer). This can\n+\t\t * lead to contention if both nodes are trying to set the LEDs\n+\t\t * at the same time.\n+\t\t */\n+\t\trc = craye1k_set_primary(craye1k);\n+\t\tif (rc != 0) {\n+\t\t\t/* Could not set as primary node. Just retry again. */\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\t/* Write value twice to increase success rate */\n+\t\tlocate = (status & 0x2) >> 1;\n+\t\tcraye1k_set_slot_led(craye1k, slot, 1, locate);\n+\t\tif (craye1k_set_slot_led(craye1k, slot, 1, locate) != 0) {\n+\t\t\tcraye1k->set_led_locate_failed++;\n+\t\t\tcontinue;\t/* fail, retry */\n+\t\t}\n+\n+\t\tmsleep(CRAYE1K_POST_CMD_WAIT_MS);\n+\n+\t\tfault = status & 0x1;\n+\t\tcraye1k_set_slot_led(craye1k, slot, 0, fault);\n+\t\tif (craye1k_set_slot_led(craye1k, slot, 0, fault) != 0) {\n+\t\t\tcraye1k->set_led_fault_failed++;\n+\t\t\tcontinue;\t/* fail, retry */\n+\t\t}\n+\n+\t\tmsleep(CRAYE1K_POST_CMD_WAIT_MS);\n+\n+\t\trc = __craye1k_get_attention_status(hotplug_slot, &new_status,\n+\t\t\t\t\t\t false);\n+\n+\t\tmsleep(CRAYE1K_POST_CMD_WAIT_MS);\n+\n+\t\tif (rc == 0 && new_status == status)\n+\t\t\tbreak;\t/* success */\n+\n+\t\tcraye1k->set_led_readback_failed++;\n+\n+\t\t/*\n+\t\t * At this point we weren't successful in setting the LED and\n+\t\t * need to try again.\n+\t\t *\n+\t\t * Do a random back-off to reduce contention with other server\n+\t\t * node in the unlikely case that both server nodes are trying to\n+\t\t * trying to set a LED at the same time.\n+\t\t *\n+\t\t * The 500ms minimum in the back-off reduced the chance of this\n+\t\t * whole retry loop failing from 1 in 700 to none in 10000.\n+\t\t */\n+\t\tmsleep(500 + (get_random_long() % 500));\n+\t}\n+\tmutex_unlock(&craye1k_lock);\n+\tif (tries == 0) {\n+\t\tcraye1k->set_led_failed++;\n+\t\treturn -EIO;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+bool is_craye1k_board(void)\n+{\n+\treturn dmi_match(DMI_PRODUCT_NAME, \"VSSEP1EC\");\n+}\n+\n+int craye1k_init(void)\n+{\n+\treturn ipmi_smi_watcher_register(&craye1k_smi_watcher);\n+}\n+\n+MODULE_LICENSE(\"GPL\");\n+MODULE_AUTHOR(\"Tony Hutter <hutter2@llnl.gov>\");\n+MODULE_DESCRIPTION(\"Cray E1000 NVMe Slot LED driver\");\n", "prefixes": [ "v8", "RESEND" ] }